labali-apple-notes-local
Deterministic local Apple Notes executor for macOS. It uses Notes.app automation through JXA (osascript -l JavaScript) and returns JSON for reliable downstream use.
Default operating mode is:
- Read the request.
- Translate it into one concrete action.
- If the action is destructive, produce a preview plan first.
- Execute only after explicit confirmation.
NEVER
- NEVER run on non-macOS. This skill is Notes.app automation, not a cross-platform notes client.
- NEVER auto-confirm destructive operations.
delete-note, delete-folder, move-note, and rename-folder must preview first unless --confirm true is passed explicitly.
- NEVER guess between multiple note matches. If title-based lookup is ambiguous, stop and return candidates.
- NEVER assume title and body are fully independent in Apple Notes. Notes can derive the visible title from body content. When both are updated, set body first and title second.
- NEVER delete a non-empty folder in automation flow. Empty it intentionally first or move notes elsewhere.
Required Constraints
- Runtime must be macOS with Apple Notes available.
- The host process must already have Automation permission to control Notes.app.
- Output is JSON only.
- Folder paths use
/ separators, for example iCloud/Work/Ideas.
- Prefer note
id over title-based matching whenever available.
Runtime Inputs
Required
action — one of:
list-notes
get-note
search-notes
create-note
update-note
append-note
delete-note
list-folders
create-folder
rename-folder
move-note
delete-folder
export-note
export-all-notes
Common optional inputs
note_id — exact Apple Notes note identifier.
title — note title for create or title-based lookup.
folder_path — source folder path like iCloud/Work.
target_folder_path — destination folder path for move or creation.
content — plain-text content.
body_html — explicit HTML body for create or update.
query — search text for search-notes.
limit — max returned notes for list/search.
output_dir — required for export-note and export-all-notes.
confirm — true to execute destructive actions after preview.
Execution
npx tsx skills/private/labali-apple-notes-local/scripts/run.ts \
--action list-notes \
[--folder_path "iCloud/Work"] \
[--limit 20]
Deterministic full-library export:
npx tsx skills/private/labali-apple-notes-local/scripts/run.ts \
--action export-all-notes \
--output_dir "/path/to/export-dir"
Destructive example:
npx tsx skills/private/labali-apple-notes-local/scripts/run.ts \
--action delete-note \
--note_id "x-coredata://..."
The first run returns a preview plan. Execute only after review:
npx tsx skills/private/labali-apple-notes-local/scripts/run.ts \
--action delete-note \
--note_id "x-coredata://..." \
--confirm true
Action Rules
Read operations
list-notes: return note summaries, optionally scoped to a folder.
get-note: return one note with body and plaintext.
search-notes: case-insensitive search across title and plaintext.
list-folders: return account and folder path inventory.
export-note: export one note into notes/<encoded-note-id>/note.json and note.md.
export-all-notes: full-library sync into a stable directory tree plus manifest.json and summary.json.
- Export actions exclude notes in
Recently Deleted.
Write operations
create-note: create note in target folder. Provide folder_path or target_folder_path.
update-note: replace note content. If both body_html and content are absent, fail.
update-note: replace note content and preserve the current title unless a new title is provided.
append-note: append plain text to the end of existing body.
create-folder: create a folder under account root or nested parent path.
Destructive or reclassification operations
delete-note: preview first, then delete on confirm=true.
move-note: preview first, then move on confirm=true.
rename-folder: preview first, then rename on confirm=true.
delete-folder: preview first, require empty folder, then delete on confirm=true.
Lookup Rules
- If
note_id is provided, use it directly.
- Otherwise require exact title match.
- If
folder_path is also provided, scope the match to that folder.
- If more than one note matches, return
AMBIGUOUS_NOTE.
Output Shape
Successful and preview responses both return JSON with:
ok
action
mode — execute or preview
data or plan
Errors return JSON with:
ok: false
action
error
details
Failure Modes and Remedies
| Symptom |
Likely Cause |
Remedy |
Automation permission denied |
Terminal process is not allowed to control Notes |
Grant Automation permission in System Settings and retry |
AMBIGUOUS_NOTE |
Multiple notes share the same title |
Re-run with note_id or folder_path |
FOLDER_NOT_EMPTY |
Folder still contains notes or child folders |
Move or delete contents first |
| Title changes unexpectedly after content update |
Apple Notes derived title from first body line |
Set body first, then set explicit title |
Stable Export Contract
- Stable source of truth is
notes/<encoded-note-id>/note.json.
- Human-readable cache is
notes/<encoded-note-id>/note.md.
encoded-note-id is a deterministic filesystem-safe encoding of the raw Apple Notes id.
manifest.json is deterministic and contains no export timestamp.
summary.json is deterministic and reports added / updated / removed counts and ids for the last sync.
- Re-running
export-all-notes with unchanged source notes must produce byte-identical outputs.
- Deleted notes are removed from the export tree on full-library sync.
- Notes currently in
Recently Deleted are treated as deleted and must not be exported.
Resources
| File |
Purpose |
references/architecture.md |
Execution model, lookup strategy, and Apple Notes behavior constraints |
references/plan.md |
Action-to-workflow map and safety gates |
scripts/apple-notes.py |
JSON CLI wrapper over Notes.app automation |
scripts/run.ts |
Runtime entry point and argument handling |
Load references/architecture.md when extending note identification, folder handling, or destructive safety rules.
Success Criteria
- The command validates required inputs before execution.
- Read operations return deterministic JSON.
- Destructive operations preview first by default.
- Confirmed destructive operations execute exactly once.
- Folder and note lookup failures return explicit machine-readable errors.
- Full export uses note id as the only filesystem identity key.
- Full export removes stale note directories and rewrites
manifest.json deterministically.
1---2name: labali-apple-notes-local3description: Use when you need to read, search, create, update, move, delete, or deterministically export local Apple Notes on macOS, including stable full-library sync into a filesystem directory for version control.4license: MIT5---67# labali-apple-notes-local89Deterministic local Apple Notes executor for macOS. It uses Notes.app automation through JXA (`osascript -l JavaScript`) and returns JSON for reliable downstream use.1011Default operating mode is:121. Read the request.132. Translate it into one concrete action.143. If the action is destructive, produce a preview plan first.154. Execute only after explicit confirmation.1617## NEVER1819- **NEVER run on non-macOS**. This skill is Notes.app automation, not a cross-platform notes client.20- **NEVER auto-confirm destructive operations**. `delete-note`, `delete-folder`, `move-note`, and `rename-folder` must preview first unless `--confirm true` is passed explicitly.21- **NEVER guess between multiple note matches**. If title-based lookup is ambiguous, stop and return candidates.22- **NEVER assume title and body are fully independent in Apple Notes**. Notes can derive the visible title from body content. When both are updated, set body first and title second.23- **NEVER delete a non-empty folder in automation flow**. Empty it intentionally first or move notes elsewhere.2425## Required Constraints2627- Runtime must be macOS with Apple Notes available.28- The host process must already have Automation permission to control Notes.app.29- Output is JSON only.30- Folder paths use `/` separators, for example `iCloud/Work/Ideas`.31- Prefer note `id` over title-based matching whenever available.3233## Runtime Inputs3435**Required**36- `action` — one of:37 - `list-notes`38 - `get-note`39 - `search-notes`40 - `create-note`41 - `update-note`42 - `append-note`43 - `delete-note`44 - `list-folders`45 - `create-folder`46 - `rename-folder`47 - `move-note`48 - `delete-folder`49 - `export-note`50 - `export-all-notes`5152**Common optional inputs**53- `note_id` — exact Apple Notes note identifier.54- `title` — note title for create or title-based lookup.55- `folder_path` — source folder path like `iCloud/Work`.56- `target_folder_path` — destination folder path for move or creation.57- `content` — plain-text content.58- `body_html` — explicit HTML body for create or update.59- `query` — search text for `search-notes`.60- `limit` — max returned notes for list/search.61- `output_dir` — required for `export-note` and `export-all-notes`.62- `confirm` — `true` to execute destructive actions after preview.6364## Execution6566```bash67npx tsx skills/private/labali-apple-notes-local/scripts/run.ts \68 --action list-notes \69 [--folder_path "iCloud/Work"] \70 [--limit 20]71```7273Deterministic full-library export:7475```bash76npx tsx skills/private/labali-apple-notes-local/scripts/run.ts \77 --action export-all-notes \78 --output_dir "/path/to/export-dir"79```8081Destructive example:8283```bash84npx tsx skills/private/labali-apple-notes-local/scripts/run.ts \85 --action delete-note \86 --note_id "x-coredata://..." 87```8889The first run returns a preview plan. Execute only after review:9091```bash92npx tsx skills/private/labali-apple-notes-local/scripts/run.ts \93 --action delete-note \94 --note_id "x-coredata://..." \95 --confirm true96```9798## Action Rules99100### Read operations101102- `list-notes`: return note summaries, optionally scoped to a folder.103- `get-note`: return one note with body and plaintext.104- `search-notes`: case-insensitive search across title and plaintext.105- `list-folders`: return account and folder path inventory.106- `export-note`: export one note into `notes/<encoded-note-id>/note.json` and `note.md`.107- `export-all-notes`: full-library sync into a stable directory tree plus `manifest.json` and `summary.json`.108- Export actions exclude notes in `Recently Deleted`.109110### Write operations111112- `create-note`: create note in target folder. Provide `folder_path` or `target_folder_path`.113- `update-note`: replace note content. If both `body_html` and `content` are absent, fail.114- `update-note`: replace note content and preserve the current title unless a new title is provided.115- `append-note`: append plain text to the end of existing body.116- `create-folder`: create a folder under account root or nested parent path.117118### Destructive or reclassification operations119120- `delete-note`: preview first, then delete on `confirm=true`.121- `move-note`: preview first, then move on `confirm=true`.122- `rename-folder`: preview first, then rename on `confirm=true`.123- `delete-folder`: preview first, require empty folder, then delete on `confirm=true`.124125## Lookup Rules1261271. If `note_id` is provided, use it directly.1282. Otherwise require exact title match.1293. If `folder_path` is also provided, scope the match to that folder.1304. If more than one note matches, return `AMBIGUOUS_NOTE`.131132## Output Shape133134Successful and preview responses both return JSON with:135136- `ok`137- `action`138- `mode` — `execute` or `preview`139- `data` or `plan`140141Errors return JSON with:142143- `ok: false`144- `action`145- `error`146- `details`147148## Failure Modes and Remedies149150| Symptom | Likely Cause | Remedy |151|---------|-------------|--------|152| `Automation permission denied` | Terminal process is not allowed to control Notes | Grant Automation permission in System Settings and retry |153| `AMBIGUOUS_NOTE` | Multiple notes share the same title | Re-run with `note_id` or `folder_path` |154| `FOLDER_NOT_EMPTY` | Folder still contains notes or child folders | Move or delete contents first |155| Title changes unexpectedly after content update | Apple Notes derived title from first body line | Set body first, then set explicit title |156157## Stable Export Contract158159- Stable source of truth is `notes/<encoded-note-id>/note.json`.160- Human-readable cache is `notes/<encoded-note-id>/note.md`.161- `encoded-note-id` is a deterministic filesystem-safe encoding of the raw Apple Notes id.162- `manifest.json` is deterministic and contains no export timestamp.163- `summary.json` is deterministic and reports `added / updated / removed` counts and ids for the last sync.164- Re-running `export-all-notes` with unchanged source notes must produce byte-identical outputs.165- Deleted notes are removed from the export tree on full-library sync.166- Notes currently in `Recently Deleted` are treated as deleted and must not be exported.167168## Resources169170| File | Purpose |171|------|---------|172| `references/architecture.md` | Execution model, lookup strategy, and Apple Notes behavior constraints |173| `references/plan.md` | Action-to-workflow map and safety gates |174| `scripts/apple-notes.py` | JSON CLI wrapper over Notes.app automation |175| `scripts/run.ts` | Runtime entry point and argument handling |176177Load `references/architecture.md` when extending note identification, folder handling, or destructive safety rules.178179## Success Criteria1801811. The command validates required inputs before execution.1822. Read operations return deterministic JSON.1833. Destructive operations preview first by default.1844. Confirmed destructive operations execute exactly once.1855. Folder and note lookup failures return explicit machine-readable errors.1866. Full export uses note id as the only filesystem identity key.1877. Full export removes stale note directories and rewrites `manifest.json` deterministically.