From notes-commander
Park a draft message/text in macOS Notes for the operator to review and edit, then read it back before acting (e.g. before sending to a real person). Notes is the source of truth (AppleScript CRUD, iCloud-synced, provenance-stamped with the Claude Code session UUID); Stickies is a best-effort view-only desktop mirror. Use whenever you draft something a human should confirm/edit before it is sent or committed — messages, replies, announcements, anything outbound. TRIGGERS - hold this draft, park the message, let me edit first, draft for my approval, save to notes for review, read back the draft.
How this skill is triggered — by the user, by Claude, or both
Slash command
/notes-commander:draft-holdThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Self-Evolving skill** — if macOS Notes/Stickies behavior drifts from what's below, fix this SKILL.md and the shared engine `scripts/lib/notes-core.ts` (+ a case in `notes-core.test.ts`); see the Post-Execution Reflection at the bottom.
Self-Evolving skill — if macOS Notes/Stickies behavior drifts from what's below, fix this SKILL.md and the shared engine
scripts/lib/notes-core.ts(+ a case innotes-core.test.ts); see the Post-Execution Reflection at the bottom.
When you compose something a human should confirm or edit before it goes out (a message to a real person, an announcement, a commit body), don't keep it only in chat — park it in macOS Notes so the operator can edit it on any device, then read it back and act on the edited version.
DH="$CLAUDE_PLUGIN_ROOT/skills/draft-hold/draft-hold.sh" — a thin shim that execs the Bun/TypeScript engine scripts/draft-hold.ts, built on the plugin's shared notes-core engine (which also powers notes-inventory/notes-export/notes-organize).
The engine (formatter in scripts/lib/notes-core.ts, unit-tested in notes-core.test.ts) normalizes your input into Notes HTML, so you never hand-manage line breaks:
-, *, +, •, 1., 2), a. etc. each stay on their own line; a wrapped continuation line (indented, no marker) joins back to its item.``` fence. Every line inside is preserved exactly and rendered monospace with spaces held (via ), so columns and IDs line up in the Notes UI. (Note: get --body-only returns the sendable plain text and collapses inter-column runs to single spaces; if exact alignment must survive to the recipient, send an attachment/screenshot.)new asserts Notes returned a real note id (x-coredata://…); on recent macOS, osascript can exit 0 yet create nothing. If that happens you get a loud ✗ SILENT-FAILURE instead of a phantom "success".-600/-1712/"not running") retry with backoff; permission/syntax errors fail fast.new, the note is read back and checked for entity leaks and content presence. --no-verify skips (rarely needed).Park the draft (body on STDIN):
CLAUDE_SESSION_ID=<this-session-uuid> "$DH" new "<title>" --project "<repo-or-context>" <<'EOF'
Hi <name> — <your drafted message>...
EOF
Creates/replaces a note in the "Claude Drafts" folder with a provenance footer (session UUID + project + timestamp). Tell the operator: "Draft is in Notes → Claude Drafts → ; edit it there, then tell me to send."
(Optional) desktop mirror — "$DH" sticky "<title>" pops a view-only Stickies note (needs Accessibility permission). Notes stays authoritative.
Read it back before acting — ALWAYS re-read, since the operator may have edited it:
"$DH" get "<title>" # full note (heading + message + provenance footer)
"$DH" get "<title>" --body-only # JUST the sendable message (no heading, no footer)
Use --body-only to get exactly the text to send/paste — it strips the title heading and everything from the ------ provenance separator onward. Show the operator the exact current text, get explicit go-ahead, then send/commit.
"$DH" list enumerates held drafts.
body getter re-emits every " as the semicolon-less legacy entity " (verified 2026-06-29). We decode with textutil (a real HTML parser) instead of sed, so "/&/< etc. round-trip back to literal characters. Never hand-roll entity decoding here. The read-back verify now DETECTS a drift here automatically (✗ ENTITY-LEAK).textutil -format html assumes Latin-1 when the HTML has no charset declaration, mojibaking every non-ASCII character (关于 → 关于; verified 2026-07-02 with a Chinese draft). htmlToText therefore prepends <meta charset="utf-8"> before piping to textutil — keep that prefix.new therefore prepends the title as a bold first line so get/list/replace can find it by title. Pass the message body only on STDIN.``` fenced blocks are wrapped in <tt> (Notes' "Monostyled" face) with spaces held as . The mono face is fixed by Notes — it is not the global NSFixedPitchFont, which governs TextEdit-style apps, not Notes.Pass the current Claude Code session JSONL UUID via CLAUDE_SESSION_ID or --session so the note traces back to its session. If you don't know it, the statusline-tools:session-info skill reports it, or read the newest *.jsonl under ~/.claude/projects/<project-slug>/. If genuinely unavailable, omit it (the footer drops the token — never write a placeholder).
get the note first; the operator may have changed it.open x-coredata://… fails; applenotes: links are UI-only) — reference drafts by folder + title.notes-commander plugin as one of its skills; formatting + process wrappers moved to the shared scripts/lib/notes-core.ts engine. Added, per a web-researched audit of recent macOS AppleScript failure modes: silent-no-op detection (isNoteId on create — osascript can exit 0 yet create nothing on macOS 26), bounded retry on transient AppleEvent errors (-600/-1712), and a default-on read-back verify (entityLeaks + contentPresent). All pure helpers unit-tested (16 tests in notes-core.test.ts).new made each input line its own Notes paragraph (all wrapped in <tt>), the reader saw mid-sentence line breaks that did not reflow. First fix (insufficient): a "one line per paragraph" caller contract — but that only works if every caller remembers it. Real fix: reimplemented the engine as Bun/TypeScript, enforcing the formatting in code so the failure is impossible: prose blocks REFLOW (consecutive lines join; blank line = paragraph), list markers stay per-item, and only ``` fenced blocks are preserved verbatim/monospace (spaces held as ). Verified by unit tests + a live Notes round-trip.After holding or sending a draft, check before closing:
"), the decode path drifted; the verify step should have caught it — fix the textutil step in notes-core.ts, never hand-roll sed.get/list find the note by title? — if not, the name==title assumption broke; fix the title-prepend in new.Only update if the issue is real and reproducible — not speculative.
npx claudepluginhub terrylica/cc-skills --plugin notes-commanderGuides collaborative design exploration before implementation: explores context, asks clarifying questions, proposes approaches, and writes a design doc for user approval.
Creates structured, bite-sized implementation plans from specs or requirements before writing code. Useful for breaking down multi-step tasks into testable steps with file structure and task boundaries.
Synthesizes the current conversation into a structured spec (PRD) and publishes it to the project issue tracker with a ready-for-agent label, without interviewing the user.