diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index e54243e..68b2748 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -179,11 +179,11 @@ host-specific UI/activation): Skills: `review`, `brainstorm`, `memory`, `translate`, `translation-review`, `status`, `continuity`, `read-aloud`, `read-in`, `proof-read`, `plan-beats`, `character-setup`. -The **memory skill** uses `memory_list` → `memory_append` → `memory_compact` for session decisions and `note_list` / `note_get` / `note_create` / `note_append` for canonical story notes. Do not fall back to `get_text` + Edit tool for memory or note writes when a structured tool exists. +The **memory skill** uses `bindery_memory_list` → `bindery_memory_append` → `bindery_memory_compact` for session decisions and `bindery_note_list` / `bindery_note_get` / `bindery_note_create` / `bindery_note_append` for canonical story notes. Do not fall back to `bindery_get_text` + Edit tool for memory or note writes when a structured tool exists. -The split: durable story/project decisions → `memory_*` (`.bindery/memories/`); ephemeral working state (current focus, next actions, open questions, handoff) → `session_focus_*` (`SESSION.md`); durable working preferences → `PREFERENCES.md` (user-owned, never tool-written — propose changes instead); rough/unsorted/pasted material → `Notes/Inbox.md`, triaged with `inbox_process` / `inbox_resolve` (not memory). +The split: durable story/project decisions → `memory_*` (`.bindery/memories/`); ephemeral working state (current focus, next actions, open questions, handoff) → `session_focus_*` (`SESSION.md`); durable working preferences → `PREFERENCES.md` (user-owned, never tool-written — propose changes instead); rough/unsorted/pasted material → `Notes/Inbox.md`, triaged with `bindery_inbox_process` / `bindery_inbox_resolve` (not memory). -Current authoring-tool boundary: note, character, arc, memory, session-focus, and inbox-triage MCP/LM tools exist and have matching VS Code/Obsidian host command wrappers. `session_focus_update` only touches neutral SESSION.md sections; `inbox_process` only proposes and `inbox_resolve` only removes named items (route confirmed items with the destination tools first). +Current authoring-tool boundary: note, character, arc, memory, session-focus, and inbox-triage MCP/LM tools exist and have matching VS Code/Obsidian host command wrappers. `bindery_session_focus_update` only touches neutral SESSION.md sections; `bindery_inbox_process` only proposes and `bindery_inbox_resolve` only removes named items (route confirmed items with the destination tools first). ### AI setup versioning `FILE_VERSION_INFO` in `bindery-core/src/templates.ts` is a per-file version table/map (a Record keyed by output path) that controls staleness detection. @@ -199,9 +199,9 @@ Current authoring-tool boundary: note, character, arc, memory, session-focus, an Memory files live in `.bindery/memories/` inside the book root. - `global.md` — cross-chapter decisions - `chXX.md` — per-chapter notes (e.g. `ch10.md`) -- `archive/` — compacted originals (auto-created by `memory_compact`) +- `archive/` — compacted originals (auto-created by `bindery_memory_compact`) -Format of an appended entry (stamped by `memory_append`, not the caller): +Format of an appended entry (stamped by `bindery_memory_append`, not the caller): ``` ## Session YYYY-MM-DD — [title] [content lines] diff --git a/README.md b/README.md index 573845b..034f4b6 100644 --- a/README.md +++ b/README.md @@ -42,17 +42,17 @@ A [Model Context Protocol](https://modelcontextprotocol.io/) server that exposes - **BM25 full-text search** — fast lexical search across all chapters and notes via [MiniSearch](https://lucaong.github.io/minisearch/) - **Optional semantic search** — set `BINDERY_OLLAMA_URL` for semantic reranking, or enable a full semantic index for precomputed embedding search -- **Version tracking** — `get_review_text` returns a structured git diff **plus** any regions wrapped in ` ... ` markers (so committed work-in-progress can still be reviewed). `git_snapshot` saves progress as a git commit. Git is auto-initialized during workspace setup if available +- **Version tracking** — `bindery_get_review_text` returns a structured git diff **plus** any regions wrapped in ` ... ` markers (so committed work-in-progress can still be reviewed). `bindery_git_snapshot` saves progress as a git commit. Git is auto-initialized during workspace setup if available - **Translation & dialect management** — glossary entries and dialect substitution rules in `.bindery/translations.json`, queryable and updatable by agents -- **Opinionated authoring scaffold** — `init_workspace` creates `SESSION.md`, `PREFERENCES.md`, `Arc/index.md`, `Arc/Overall.md`, `Arc/Acts/`, `Notes/Inbox.md`, `Notes/Characters/index.md`, structured note folders, and `.bindery/memories/global.md` -- **Story note management** — agents can list, read, create, and append notes under the configured notes folder while `get_notes` remains compatible with older recursive note layouts +- **Opinionated authoring scaffold** — `bindery_init_workspace` creates `SESSION.md`, `PREFERENCES.md`, `Arc/index.md`, `Arc/Overall.md`, `Arc/Acts/`, `Notes/Inbox.md`, `Notes/Characters/index.md`, structured note folders, and `.bindery/memories/global.md` +- **Story note management** — agents can list, read, create, and append notes under the configured notes folder while `bindery_get_notes` remains compatible with older recursive note layouts - **Session memory** — persistent `.bindery/memories/` files for cross-session decisions, with append, list, and compact operations -- **Session focus** — `session_focus_*` tools maintain the ephemeral working-state file `SESSION.md` (current focus, next actions, open questions, handoff); durable preferences live in the user-owned `PREFERENCES.md` -- **Inbox triage** — `inbox_process` enumerates loose items in `Notes/Inbox.md` and proposes destinations (read-only); `inbox_resolve` clears items after they are routed and confirmed — rough/pasted material goes to the Inbox, not memory +- **Session focus** — `bindery_session_focus_*` tools maintain the ephemeral working-state file `SESSION.md` (current focus, next actions, open questions, handoff); durable preferences live in the user-owned `PREFERENCES.md` +- **Inbox triage** — `bindery_inbox_process` enumerates loose items in `Notes/Inbox.md` and proposes destinations (read-only); `bindery_inbox_resolve` clears items after they are routed and confirmed — rough/pasted material goes to the Inbox, not memory - **Structured arc & character management** — agents can create/update arc files and character profiles using structured tools that keep indexes in sync - **Host command parity** — VS Code and Obsidian expose command-palette actions for notes, characters, arcs, memory, and session focus, backed by the same structured tool functions agents use - **Multi-book support** — configure one or more books via `--book Name=path` CLI args or `BINDERY_BOOKS` env var; every tool call specifies which book to use by name (agents never see raw paths) -- **Container/mount aware** — agents in sandboxed environments (e.g. Cowork) can call `identify_book` with their working directory to discover their book name, even when mount paths differ from the configured paths +- **Container/mount aware** — agents in sandboxed environments (e.g. Cowork) can call `bindery_identify_book` with their working directory to discover their book name, even when mount paths differ from the configured paths See [mcpb/README.md](mcpb/README.md) for the full 40-tool reference and usage examples. @@ -110,7 +110,7 @@ Release assets now include two MCP package formats: 4. Optionally set the **Ollama URL** if you want semantic reranking 5. Optionally enable the semantic index and choose a default search mode if you want `full_semantic` search with rebuild warnings when the embedding index becomes stale. - **Note:** full embedding can be a heavy operation, depending on your hardware, when running a local Ollama instance. -6. Tools are now available — the agent calls `list_books` to discover book names +6. Tools are now available — the agent calls `bindery_list_books` to discover book names ### Standalone MCP Clients (ChatGPT Work, LM Studio, etc.) @@ -203,7 +203,7 @@ Shared logic in `bindery-core` and `bindery-merge` ensures both `vscode-ext` and - **One writing environment**: - **VS Code** 1.85+ - **Obsidian Desktop** with Community Plugins enabled -- **Git** (recommended) — needed for version tracking, `get_review_text`, and `git_snapshot`. Auto-initialized during workspace setup. +- **Git** (recommended) — needed for version tracking, `bindery_get_review_text`, and `bindery_git_snapshot`. Auto-initialized during workspace setup. - Install via package manager or from [https://git-scm.com](https://git-scm.com) - **Pandoc** (optional) — needed for DOCX/EPUB/PDF export. - Install via package manager or from [https://pandoc.org/installing.html](https://pandoc.org/installing.html) @@ -227,12 +227,12 @@ You usually do not need to configure anything — install Pandoc/LibreOffice nor ## Known limitations -- **Git** must be on `PATH` (or at a standard install location) for `get_review_text` and `git_snapshot`. If git isn't found, these tools fail with a clear error; all other tools still work. +- **Git** must be on `PATH` (or at a standard install location) for `bindery_get_review_text` and `bindery_git_snapshot`. If git isn't found, these tools fail with a clear error; all other tools still work. - **Pandoc** is required for DOCX, EPUB, and PDF export. Markdown-only export has no external dependencies. - **LibreOffice** is required only for PDF export. Bindery generates PDFs by producing a DOCX via Pandoc and then converting with LibreOffice headless. - **Semantic search** requires an optional [Ollama](https://ollama.com/) instance. Without it, lexical BM25 search still works offline. Configure with `BINDERY_OLLAMA_URL`; optional tuning via `BINDERY_OLLAMA_TIMEOUT_MS` (default 15000) and `BINDERY_OLLAMA_RETRIES` (default 1). - **Large books with semantic indexing** can take several minutes to embed on first build. Rebuilds are incremental when chapter content is unchanged. -- **Chapter numbering**: the tools sort chapters by filename but accept non-contiguous numbers. `get_overview` now flags gaps (e.g. chapters 1, 3 with no 2) as a warning. +- **Chapter numbering**: the tools sort chapters by filename but accept non-contiguous numbers. `bindery_get_overview` now flags gaps (e.g. chapters 1, 3 with no 2) as a warning. - **Search index format**: bumped automatically when the on-disk format changes. Older indexes are silently ignored and rebuilt on next use — no manual action required. ## Privacy diff --git a/bindery-core/src/templates/bindery-readme.ts b/bindery-core/src/templates/bindery-readme.ts index 7629982..141f18f 100644 --- a/bindery-core/src/templates/bindery-readme.ts +++ b/bindery-core/src/templates/bindery-readme.ts @@ -2,7 +2,7 @@ import type { TemplateContext, TemplateMeta } from './context'; export const meta: TemplateMeta = { file: '.bindery/README.md', - version: 11, + version: 12, label: 'bindery capabilities', }; @@ -11,7 +11,7 @@ export function render(ctx: TemplateContext): string { return `# Bindery — capabilities for this workspace This file is **the single source agents should consult to answer "What can Bindery do?"** -It is generated by Bindery itself (\`bindery init\` and \`setup_ai_files\`). Edits will be +It is generated by Bindery itself (\`bindery_init_workspace\` and \`bindery_setup_ai_files\`). Edits will be overwritten on the next setup. For richer narrative docs (install, contributing, release notes), see the project on @@ -40,14 +40,14 @@ work from the same map: | Path | Purpose | |---|---| -| \`${sessionFile}\` | Ephemeral working state: current focus, next actions, open questions, handoff notes. Maintained via \`session_focus_*\` tools and host commands. | +| \`${sessionFile}\` | Ephemeral working state: current focus, next actions, open questions, handoff notes. Maintained via \`bindery_session_focus_*\` tools and host commands. | | \`${preferencesFile}\` | Durable working preferences — "do it like this for me". User-owned: Bindery scaffolds it once and never edits it. | | \`${arcFolder}/index.md\` | Arc map and links to structure files. | | \`${arcFolder}/Overall.md\` | Whole-book arc, promise, turns, ending direction, and open questions. | | \`${arcFolder}/Acts/\` | Act-level arc files. Default arc granularity: \`${arcGranularity}\`. | | \`${charactersFolder}/index.md\` | Cast overview. | | \`${charactersFolder}/.md\` | One durable profile per character. | -| \`${notesFolder}/Inbox.md\` | Loose notes, pasted mobile chats, and unsorted ideas before triage. Triage with \`inbox_process\` / \`inbox_resolve\`. | +| \`${notesFolder}/Inbox.md\` | Loose notes, pasted mobile chats, and unsorted ideas before triage. Triage with \`bindery_inbox_process\` / \`bindery_inbox_resolve\`. | | \`${notesFolder}/World/\`, \`${notesFolder}/Scenes/\`, \`${notesFolder}/Research/\` | Structured supporting notes. | | \`${memoriesFolder}/global.md\` | Durable cross-session decisions and story rules. | | \`${storyFolder}/\` | Chapter source files by language. | @@ -93,43 +93,43 @@ tagged **(writes)** modify files or git state. | Tool | What it does | |---|---| -| \`health\` (reads) | Workspace status: settings, index, AI-file versions, embedding backend. | -| \`identify_book\` (reads) | Confirm the active book root and registry entry. | -| \`init_workspace\` (writes) | Create \`.bindery/settings.json\`, \`.bindery/translations.json\`, generated \`.bindery/README.md\`, and the opinionated Arc / Notes / Characters / SESSION / PREFERENCES / memory scaffold. | -| \`update_workspace\` (writes) | \`git fetch\` + pull, optional branch switch, optional auto-stash. | -| \`settings_update\` (writes) | Patch \`.bindery/settings.json\` from an agent. | -| \`setup_ai_files\` (writes) | (Re)generate AI instruction files + refresh this capabilities doc. | -| \`get_text\` / \`get_chapter\` / \`get_book_until\` / \`get_overview\` / \`get_notes\` (reads) | Read source files, chapters, ranges, and notes. | -| \`note_list\` / \`note_get\` / \`note_create\` / \`note_append\` (reads / writes) | First-class story note management under \`${notesFolder}/\`. | -| \`character_list\` / \`character_get\` / \`character_create\` / \`character_update\` (reads / writes) | Structured character profiles under \`${charactersFolder}/\`. | -| \`arc_list\` / \`arc_get\` / \`arc_create\` / \`arc_update\` (reads / writes) | Structured story-architecture files under \`${arcFolder}/\`. | -| \`search\` (reads) | BM25 / semantic search across the corpus (Ollama optional). | -| \`index_build\` / \`index_status\` (writes / reads) | Build or inspect the lexical/semantic index. | -| \`format\` (writes) | Apply typography to a file or folder (\`dryRun\` supported). | -| \`get_review_text\` (writes) | Returns the **git diff of uncommitted changes** *plus* any regions wrapped in \` ... \` markers. Marker regions surface even after a commit, so committed work-in-progress can still be reviewed. \`autoStage: true\` stages files **and** consumes (removes) the marker lines. | -| \`git_snapshot\` (writes) | Commit changes in bindery workspace, optional push. | -| \`get_translation\` / \`add_translation\` (reads / writes) | Glossary lookup and upsert per target language. | -| \`get_dialect\` / \`add_dialect\` (reads / writes) | Dialect substitution lookup and upsert (e.g. \`en-gb\`). | -| \`add_language\` (writes) | Scaffold a new language under the story folder. | -| \`memory_list\` / \`memory_append\` / \`memory_compact\` (reads / writes) | Manage \`.bindery/memories/\` files. | -| \`session_focus_get\` / \`session_focus_update\` (reads / writes) | Read or update the neutral working-state sections of \`${sessionFile}\` (Current Focus, Next Actions, Open Questions, Handoff Notes). Leaves \`${preferencesFile}\` and other content untouched. | -| \`inbox_process\` / \`inbox_resolve\` (reads / writes) | \`inbox_process\` enumerates \`${notesFolder}/Inbox.md\` items with stable numbers and proposes destinations (read-only); \`inbox_resolve\` removes already-routed items by number after confirmation. | +| \`bindery_health\` (reads) | Workspace status: settings, index, AI-file versions, embedding backend. | +| \`bindery_identify_book\` (reads) | Confirm the active book root and registry entry. | +| \`bindery_init_workspace\` (writes) | Create \`.bindery/settings.json\`, \`.bindery/translations.json\`, generated \`.bindery/README.md\`, and the opinionated Arc / Notes / Characters / SESSION / PREFERENCES / memory scaffold. | +| \`bindery_update_workspace\` (writes) | \`git fetch\` + pull, optional branch switch, optional auto-stash. | +| \`bindery_settings_update\` (writes) | Patch \`.bindery/settings.json\` from an agent. | +| \`bindery_setup_ai_files\` (writes) | (Re)generate AI instruction files + refresh this capabilities doc. | +| \`bindery_get_text\` / \`bindery_get_chapter\` / \`bindery_get_book_until\` / \`bindery_get_overview\` / \`bindery_get_notes\` (reads) | Read source files, chapters, ranges, and notes. | +| \`bindery_note_list\` / \`bindery_note_get\` / \`bindery_note_create\` / \`bindery_note_append\` (reads / writes) | First-class story note management under \`${notesFolder}/\`. | +| \`bindery_character_list\` / \`bindery_character_get\` / \`bindery_character_create\` / \`bindery_character_update\` (reads / writes) | Structured character profiles under \`${charactersFolder}/\`. | +| \`bindery_arc_list\` / \`bindery_arc_get\` / \`bindery_arc_create\` / \`bindery_arc_update\` (reads / writes) | Structured story-architecture files under \`${arcFolder}/\`. | +| \`bindery_search\` (reads) | BM25 / semantic search across the corpus (Ollama optional). | +| \`bindery_index_build\` / \`bindery_index_status\` (writes / reads) | Build or inspect the lexical/semantic index. | +| \`bindery_format\` (writes) | Apply typography to a file or folder (\`dryRun\` supported). | +| \`bindery_get_review_text\` (writes) | Returns the **git diff of uncommitted changes** *plus* any regions wrapped in \` ... \` markers. Marker regions surface even after a commit, so committed work-in-progress can still be reviewed. \`autoStage: true\` stages files **and** consumes (removes) the marker lines. | +| \`bindery_git_snapshot\` (writes) | Commit changes in bindery workspace, optional push. | +| \`bindery_get_translation\` / \`bindery_add_translation\` (reads / writes) | Glossary lookup and upsert per target language. | +| \`bindery_get_dialect\` / \`bindery_add_dialect\` (reads / writes) | Dialect substitution lookup and upsert (e.g. \`en-gb\`). | +| \`bindery_add_language\` (writes) | Scaffold a new language under the story folder. | +| \`bindery_memory_list\` / \`bindery_memory_append\` / \`bindery_memory_compact\` (reads / writes) | Manage \`.bindery/memories/\` files. | +| \`bindery_session_focus_get\` / \`bindery_session_focus_update\` (reads / writes) | Read or update the neutral working-state sections of \`${sessionFile}\` (Current Focus, Next Actions, Open Questions, Handoff Notes). Leaves \`${preferencesFile}\` and other content untouched. | +| \`bindery_inbox_process\` / \`bindery_inbox_resolve\` (reads / writes) | \`bindery_inbox_process\` enumerates \`${notesFolder}/Inbox.md\` items with stable numbers and proposes destinations (read-only); \`bindery_inbox_resolve\` removes already-routed items by number after confirmation. | ### Tool Workflow Shortcuts -- Use \`init_workspace\` first for a new book. It creates the settings, translation file, generated capability README, and the default authoring scaffold. -- Use \`setup_ai_files\` after init or when \`health\` reports outdated AI files. It refreshes CLAUDE.md, Copilot instructions, Cursor rules, AGENTS.md, generated skills, and this capability README. -- Use \`arc_*\` for story architecture under \`${arcFolder}/\`. -- Use \`character_*\` for cast profiles under \`${charactersFolder}/\`. -- Use \`note_*\` for canonical story notes under \`${notesFolder}/\`. -- Use \`get_notes\` for broad note lookup, \`memory_*\` for durable session decisions, and \`session_focus_*\` for ephemeral current-focus/handoff in \`${sessionFile}\`. -- Send rough, unsorted, or pasted material to \`${notesFolder}/Inbox.md\` (e.g. \`note_append\`), then triage it with \`inbox_process\` (propose) and \`inbox_resolve\` (clear routed items) — do not dump it into memory. +- Use \`bindery_init_workspace\` first for a new book. It creates the settings, translation file, generated capability README, and the default authoring scaffold. +- Use \`bindery_setup_ai_files\` after init or when \`bindery_health\` reports outdated AI files. It refreshes CLAUDE.md, Copilot instructions, Cursor rules, AGENTS.md, generated skills, and this capability README. +- Use \`bindery_arc_*\` for story architecture under \`${arcFolder}/\`. +- Use \`bindery_character_*\` for cast profiles under \`${charactersFolder}/\`. +- Use \`bindery_note_*\` for canonical story notes under \`${notesFolder}/\`. +- Use \`bindery_get_notes\` for broad note lookup, \`bindery_memory_*\` for durable session decisions, and \`bindery_session_focus_*\` for ephemeral current-focus/handoff in \`${sessionFile}\`. +- Send rough, unsorted, or pasted material to \`${notesFolder}/Inbox.md\` (e.g. \`bindery_note_append\`), then triage it with \`bindery_inbox_process\` (propose) and \`bindery_inbox_resolve\` (clear routed items) — do not dump it into memory. ### Current Tool Boundaries - Dedicated note, character, arc, memory, session-focus, and inbox-triage tools are available now. -- \`inbox_process\` only reads and proposes; \`inbox_resolve\` only removes items you name after they have been routed. Neither moves or categorizes content on its own — route confirmed items with the destination tools first. -- \`session_focus_*\` updates only the neutral working-state sections of \`${sessionFile}\`. \`${preferencesFile}\` is durable and user-owned — Bindery scaffolds it once and never edits it; propose preference changes for the author to apply. +- \`bindery_inbox_process\` only reads and proposes; \`bindery_inbox_resolve\` only removes items you name after they have been routed. Neither moves or categorizes content on its own — route confirmed items with the destination tools first. +- \`bindery_session_focus_*\` updates only the neutral working-state sections of \`${sessionFile}\`. \`${preferencesFile}\` is durable and user-owned — Bindery scaffolds it once and never edits it; propose preference changes for the author to apply. - VS Code and Obsidian host commands now mirror the structured note, character, arc, memory, and session-focus tools. Host prompts cover common fields; agents can still call the MCP tools directly for complete structured payloads. ## Review markers @@ -176,7 +176,7 @@ command or by paraphrasing the description. When an agent is asked what Bindery can do for this project, it should: 1. Read **this file** (\`.bindery/README.md\`) — it is intentionally complete. -2. Cross-check live state with \`health\` if the user asks about the index, +2. Cross-check live state with \`bindery_health\` if the user asks about the index, AI-file freshness, or the embedding backend. 3. For narrative / install / contribution guidance, point the user to (or fetch the README from diff --git a/bindery-core/src/templates/building-blocks.ts b/bindery-core/src/templates/building-blocks.ts index 551465b..91c54c4 100644 --- a/bindery-core/src/templates/building-blocks.ts +++ b/bindery-core/src/templates/building-blocks.ts @@ -28,18 +28,18 @@ export function pushCapabilitiesSource(lines: string[]): void { export function pushSessionStart(lines: string[], ctx: TemplateContext, agent: AgentTemplate): void { lines.push( '## Start of session', - '1. Run `health` from the Bindery MCP and check `ai_versions_outdated`.', - '2. If `ai_versions_outdated` has entries, run `setup_ai_files`.' + (agent.requiresSkillUpload ? ' If `skill_files.reupload_required` has entries, ask the user to re-upload those SKILL.md files to use them.' : ''), + '1. Run `bindery_health` from the Bindery MCP and check `ai_versions_outdated`.', + '2. If `ai_versions_outdated` has entries, run `bindery_setup_ai_files`.' + (agent.requiresSkillUpload ? ' If `skill_files.reupload_required` has entries, ask the user to re-upload those SKILL.md files to use them.' : ''), ); if (agent.hasSkills) { lines.push( '3. Use /read-in at the start of a session to load context and get your bearings.', - `4. If the skill is not available, read at least ${ctx.sessionFile} (if present, use \`session_focus_get\`) for current focus and handoff context, and ${ctx.preferencesFile} for the author's durable working preferences.`, + `4. If the skill is not available, read at least ${ctx.sessionFile} (if present, use \`bindery_session_focus_get\`) for current focus and handoff context, and ${ctx.preferencesFile} for the author's durable working preferences.`, ); } else { lines.push( `3. Read ${ctx.sessionFile} (if present) for current focus and handoff context, and ${ctx.preferencesFile} for the author's durable working preferences.`, - `4. If ${ctx.sessionFile} mentions a chapter, \`get_chapter(chapterNumber)\` to read that chapter and \`memory_list\` to check for any chapter-specific memory files.`, + `4. If ${ctx.sessionFile} mentions a chapter, \`bindery_get_chapter(chapterNumber)\` to read that chapter and \`bindery_memory_list\` to check for any chapter-specific memory files.`, ); } } @@ -57,9 +57,9 @@ export function pushMemorySystem(lines: string[], agent: AgentTemplate): void { ); } else { lines.push( - `1. When concluding a discussion, or after you give a meaningful, preservation-worthy response: use \`memory_append\` to store it.`, - `2. Also when the user asks or otherwise indicates the end of a session: use \`memory_append\` to save decisions.`, - `3. If a memory file grows too large, use \`memory_compact\` to condense it.`, + `1. When concluding a discussion, or after you give a meaningful, preservation-worthy response: use \`bindery_memory_append\` to store it.`, + `2. Also when the user asks or otherwise indicates the end of a session: use \`bindery_memory_append\` to save decisions.`, + `3. If a memory file grows too large, use \`bindery_memory_compact\` to condense it.`, ); } } @@ -74,7 +74,7 @@ export function pushRepoLayout(lines: string[], ctx: TemplateContext): void { ' Acts/ ← act-level arc files', `${ctx.notesFolder}/ ← story notes`, `${ctx.charactersFolder}/ ← character index and one profile per character`, - `${ctx.sessionFile} ← ephemeral working state (current focus / next actions / open questions / handoff) via session_focus_*`, + `${ctx.sessionFile} ← ephemeral working state (current focus / next actions / open questions / handoff) via bindery_session_focus_*`, `${ctx.preferencesFile} ← durable working preferences ("do it like this for me"); user-owned, never tool-written`, `${ctx.storyFolder}/`, ...ctx.languages.map(l => ` ${l.folderName}/ ← ${l.code} chapters (one .md per chapter)`), diff --git a/bindery-core/src/templates/skills/brainstorm.ts b/bindery-core/src/templates/skills/brainstorm.ts index 95e575a..7bf230f 100644 --- a/bindery-core/src/templates/skills/brainstorm.ts +++ b/bindery-core/src/templates/skills/brainstorm.ts @@ -2,7 +2,7 @@ import type { TemplateContext, TemplateMeta } from '../context'; export const meta: TemplateMeta = { file: '.claude/skills/brainstorm/SKILL.md', - version: 12, + version: 14, label: 'brainstorm skill', }; @@ -16,17 +16,17 @@ const CONTENT = [ "Help the author get unstuck through discussion — not an idea dump, but a guided conversation that surfaces options organically and only generates when direction is clear.", "", "## Prerequisites", - "This skill requires a Bindery workspace. If unsure, call `identify_book` to check. If no workspace is found, tell the user and stop.", + "This skill requires a Bindery workspace. If unsure, call `bindery_identify_book` to check. If no workspace is found, tell the user and stop.", "", "## Trigger", "User says `/brainstorm`, \"I'm stuck\", \"help me think of ideas\", or \"Am I stuck?\".", "", "## Tools", "Use these Bindery MCP tools to gather context:", - "- `get_text(path)` — read settings, arc files, or memory files", - "- `search(query, language)` — find thematic parallels and related moments across the book", - "- `get_notes(category, name)` — look up character profiles, world rules, or equipment details", - "- `get_chapter(chapterNumber, language)` — read a specific chapter for reference", + "- `bindery_get_text(path)` — read settings, arc files, or memory files", + "- `bindery_search(query, language)` — find thematic parallels and related moments across the book", + "- `bindery_get_notes(category, name)` — look up character profiles, world rules, or equipment details", + "- `bindery_get_chapter(chapterNumber, language)` — read a specific chapter for reference", "", "## Phases", "", @@ -46,10 +46,10 @@ const CONTENT = [ "### Phase 2 — Load context", "Load context appropriate to the block. Do this silently — don't list files to the user.", "", - "- Always: `get_text(\".bindery/settings.json\")`, `get_text(\".bindery/memories/global.md\")`, and the relevant arc file from `Arc/`", - "- If chapter-specific: `get_text(\".bindery/memories/chXX.md\")` if it exists", - "- If character-focused: `get_notes(category: \"Characters\")`", - "- Always: `search` for related moments or themes already in the book", + "- Always: `bindery_get_text(\".bindery/settings.json\")`, `bindery_get_text(\".bindery/memories/global.md\")`, and the relevant arc file from `Arc/`", + "- If chapter-specific: `bindery_get_text(\".bindery/memories/chXX.md\")` if it exists", + "- If character-focused: `bindery_get_notes(category: \"Characters\")`", + "- Always: `bindery_search` for related moments or themes already in the book", "", "### Phase 3 — Discuss", "This is the core of the skill. Probe the story problem before jumping to solutions.", diff --git a/bindery-core/src/templates/skills/character-setup.ts b/bindery-core/src/templates/skills/character-setup.ts index 84e77a2..a0fe13d 100644 --- a/bindery-core/src/templates/skills/character-setup.ts +++ b/bindery-core/src/templates/skills/character-setup.ts @@ -2,7 +2,7 @@ import type { TemplateContext, TemplateMeta } from '../context'; export const meta: TemplateMeta = { file: '.claude/skills/character-setup/SKILL.md', - version: 1, + version: 3, label: 'character-setup skill', }; @@ -17,7 +17,7 @@ const CONTENT = [ 'Build or refine character profiles using Bindery structured character tools.', '', '## Prerequisites', - 'This skill requires a Bindery workspace. If unsure, call `identify_book` to check. If no workspace is found, tell the user and stop.', + 'This skill requires a Bindery workspace. If unsure, call `bindery_identify_book` to check. If no workspace is found, tell the user and stop.', '', '## Trigger', 'User says `/character-setup`, "help me define this character", "make a character profile", "update this character", "describe the cast", or asks for durable character setup/description.', @@ -29,14 +29,14 @@ const CONTENT = [ '', '## Tools', 'Use these Bindery MCP tools:', - '- `character_list(name?)` — inspect existing profiles before creating duplicates', - '- `character_get(name)` — read an existing structured profile', - '- `character_create(...)` — create a profile and update the character index', - '- `character_update(...)` — update known profile fields and refresh the index row', - '- `get_notes(category: "Characters")` — broad lookup for older note layouts or unstructured character notes', - '- `search(query, language)` — find in-story mentions, first appearance, and continuity facts', - '- `get_chapter(chapterNumber, language)` / `get_book_until(...)` — inspect source text when the profile must reflect written canon', - '- `arc_list` / `arc_get` — check relevant arc movement before writing narrative-arc fields', + '- `bindery_character_list(name?)` — inspect existing profiles before creating duplicates', + '- `bindery_character_get(name)` — read an existing structured profile', + '- `bindery_character_create(...)` — create a profile and update the character index', + '- `bindery_character_update(...)` — update known profile fields and refresh the index row', + '- `bindery_get_notes(category: "Characters")` — broad lookup for older note layouts or unstructured character notes', + '- `bindery_search(query, language)` — find in-story mentions, first appearance, and continuity facts', + '- `bindery_get_chapter(chapterNumber, language)` / `bindery_get_book_until(...)` — inspect source text when the profile must reflect written canon', + '- `bindery_arc_list` / `bindery_arc_get` — check relevant arc movement before writing narrative-arc fields', '', '## Profile fields', 'Bindery character profiles use these durable fields:', @@ -59,21 +59,21 @@ const CONTENT = [ '## Steps', '', '### 1. Check for an existing profile', - 'Call `character_list` with the character name or likely spelling. If a plausible match exists, call `character_get` and update it instead of creating a duplicate.', + 'Call `bindery_character_list` with the character name or likely spelling. If a plausible match exists, call `bindery_character_get` and update it instead of creating a duplicate.', '', '### 2. Gather canon and planning context', - '- Use `search` for the character name and aliases.', - '- Use `get_notes(category: "Characters")` if older character notes may exist.', - '- Use `arc_list` / `arc_get` when narrative movement or act-level purpose matters.', + '- Use `bindery_search` for the character name and aliases.', + '- Use `bindery_get_notes(category: "Characters")` if older character notes may exist.', + '- Use `bindery_arc_list` / `bindery_arc_get` when narrative movement or act-level purpose matters.', '- Ask the user for missing intent before inventing durable facts.', '', '### 3. Draft the profile', 'Separate confirmed canon from open questions. Put uncertainties in `openQuestions`, not in factual fields. Keep descriptions useful for continuity and future writing rather than decorative.', '', '### 4. Write the profile', - '- For a new character, call `character_create` with the populated fields.', - '- For an existing character, call `character_update` with only fields that should change.', - '- Do not use generic file edits for character profiles when `character_create` or `character_update` can express the change.', + '- For a new character, call `bindery_character_create` with the populated fields.', + '- For an existing character, call `bindery_character_update` with only fields that should change.', + '- Do not use generic file edits for character profiles when `bindery_character_create` or `bindery_character_update` can express the change.', '', '### 5. Report back', 'Summarize what changed, list any open questions, and mention the profile path returned by the tool.', diff --git a/bindery-core/src/templates/skills/continuity.ts b/bindery-core/src/templates/skills/continuity.ts index fdc62f2..edf34da 100644 --- a/bindery-core/src/templates/skills/continuity.ts +++ b/bindery-core/src/templates/skills/continuity.ts @@ -2,7 +2,7 @@ import type { TemplateContext, TemplateMeta } from '../context'; export const meta: TemplateMeta = { file: '.claude/skills/continuity/SKILL.md', - version: 14, + version: 16, label: 'continuity skill', }; @@ -16,7 +16,7 @@ const CONTENT = [ "Cross-check a chapter for consistency errors.", "", "## Prerequisites", - "This skill requires a Bindery workspace. If unsure, call `identify_book` to check. If no workspace is found, tell the user and stop.", + "This skill requires a Bindery workspace. If unsure, call `bindery_identify_book` to check. If no workspace is found, tell the user and stop.", "", "## Trigger", "User says `/continuity`, \"check continuity\", or \"check chapter X for errors\".", @@ -28,30 +28,30 @@ const CONTENT = [ "", "## Tools", "Use these Bindery MCP tools:", - "- `get_text(path)` — read settings and memory files", - "- `session_focus_get(section?)` — read the current focus / working state from `SESSION.md`", - "- `arc_list` / `arc_get` — read structured arc files", - "- `character_list` / `character_get` — read structured character profiles", - "- `get_chapter(chapterNumber, language)` — read a specific chapter", - "- `get_book_until(chapterNumber, language, startChapter?)` — load all prior chapters in one call for timeline checks; fall back to `get_chapter` for nearby chapters if unavailable", - "- `get_notes(category, name)` — look up older character notes (`category: \"Characters\"`) or world rules (`category: \"World\"`)", - "- `search(query, language)` — find earlier mentions of a specific detail or event", - "- `memory_list` — check whether a chapter-specific memory file exists (`chXX.md`)", + "- `bindery_get_text(path)` — read settings and memory files", + "- `bindery_session_focus_get(section?)` — read the current focus / working state from `SESSION.md`", + "- `bindery_arc_list` / `bindery_arc_get` — read structured arc files", + "- `bindery_character_list` / `bindery_character_get` — read structured character profiles", + "- `bindery_get_chapter(chapterNumber, language)` — read a specific chapter", + "- `bindery_get_book_until(chapterNumber, language, startChapter?)` — load all prior chapters in one call for timeline checks; fall back to `bindery_get_chapter` for nearby chapters if unavailable", + "- `bindery_get_notes(category, name)` — look up older character notes (`category: \"Characters\"`) or world rules (`category: \"World\"`)", + "- `bindery_search(query, language)` — find earlier mentions of a specific detail or event", + "- `bindery_memory_list` — check whether a chapter-specific memory file exists (`chXX.md`)", "", "## Steps", "", "### Load context", - "1. `get_text(\".bindery/settings.json\")` — pick up the book's structure and conventions.", - "2. Use `arc_list` and `arc_get` to load the structural map. If the chapter's act is known, read the matching act file.", - "3. Use `character_list` to load the cast map.", - "4. `get_text(\".bindery/memories/global.md\")` — cross-chapter decisions and established facts. Use `memory_list` to check if a chapter-specific memory file exists; if so, read it too.", - "5. `get_chapter(chapterNumber, language)` — read the chapter to check.", + "1. `bindery_get_text(\".bindery/settings.json\")` — pick up the book's structure and conventions.", + "2. Use `bindery_arc_list` and `bindery_arc_get` to load the structural map. If the chapter's act is known, read the matching act file.", + "3. Use `bindery_character_list` to load the cast map.", + "4. `bindery_get_text(\".bindery/memories/global.md\")` — cross-chapter decisions and established facts. Use `bindery_memory_list` to check if a chapter-specific memory file exists; if so, read it too.", + "5. `bindery_get_chapter(chapterNumber, language)` — read the chapter to check.", "", "### Load reference material (by focus area)", - "6. **Characters**: `character_get` for relevant structured character profiles; use `get_notes(category: \"Characters\")` only for older notes.", - "7. **World rules**: `get_notes(category: \"World\")` for world and magic rules.", - "8. **Timeline**: `get_book_until` up to the focus chapter for continuity context.", - "9. For any focus: use `search` to verify specific details against earlier chapters.", + "6. **Characters**: `bindery_character_get` for relevant structured character profiles; use `bindery_get_notes(category: \"Characters\")` only for older notes.", + "7. **World rules**: `bindery_get_notes(category: \"World\")` for world and magic rules.", + "8. **Timeline**: `bindery_get_book_until` up to the focus chapter for continuity context.", + "9. For any focus: use `bindery_search` to verify specific details against earlier chapters.", "", "### Check and report", "10. Compare the chapter against the loaded reference material.", diff --git a/bindery-core/src/templates/skills/memory.ts b/bindery-core/src/templates/skills/memory.ts index 7d00696..20eccc7 100644 --- a/bindery-core/src/templates/skills/memory.ts +++ b/bindery-core/src/templates/skills/memory.ts @@ -2,7 +2,7 @@ import type { TemplateContext, TemplateMeta } from '../context'; export const meta: TemplateMeta = { file: '.claude/skills/memory/SKILL.md', - version: 14, + version: 16, label: 'memory skill', }; @@ -17,27 +17,27 @@ const CONTENT = [ "", "**What memory is:** A working log of decisions and preferences as they stood at the time — not canonical truth. Canonical sources live in `Notes/`, `Arc/`, and `PREFERENCES.md`; current working state lives in `SESSION.md`. If something in memory conflicts with those files, the canonical file wins.", "", - "**Three-way split:** durable story/project decisions → memory (`.bindery/memories/`); durable working preferences (\"do it like this for me\") → `PREFERENCES.md` (user-owned, never tool-written); ephemeral current focus, next actions, open questions, and handoff → `SESSION.md` via `session_focus_*`.", + "**Three-way split:** durable story/project decisions → memory (`.bindery/memories/`); durable working preferences (\"do it like this for me\") → `PREFERENCES.md` (user-owned, never tool-written); ephemeral current focus, next actions, open questions, and handoff → `SESSION.md` via `bindery_session_focus_*`.", "", - "**What memory is not:** A substitute for proper notes files. Character profiles, world rules, and writing conventions belong in their dedicated files, not accumulating in memory. Rough, unsorted, or pasted material (mobile chats, brain-dumps) goes to the notes Inbox (`Notes/Inbox.md`), not memory — triage it later with `inbox_process`.", + "**What memory is not:** A substitute for proper notes files. Character profiles, world rules, and writing conventions belong in their dedicated files, not accumulating in memory. Rough, unsorted, or pasted material (mobile chats, brain-dumps) goes to the notes Inbox (`Notes/Inbox.md`), not memory — triage it later with `bindery_inbox_process`.", "", "## Prerequisites", - "This skill requires a Bindery workspace. If unsure, call `identify_book` to check. If no workspace is found, tell the user and stop.", + "This skill requires a Bindery workspace. If unsure, call `bindery_identify_book` to check. If no workspace is found, tell the user and stop.", "", "## Trigger", "User says `/memory`, \"save this to memory\", \"update memories\", at meaningful points, or at session end.", "", "## Tools", "Use these Bindery MCP tools:", - "- `note_list(category?)` — list canonical story note files", - "- `note_get(path)` / `get_notes(category, name)` — read existing canonical notes before routing settled facts out of memory", - "- `note_create(path, title, content)` — create a new canonical story note when the user confirms settled reference material", - "- `note_append(path, content, heading?)` — append confirmed material to an existing canonical story note", - "- `memory_list` — discover which memory files exist and their line counts", - "- `memory_append(file, title, content)` — append a dated session entry; the tool stamps the date automatically", - "- `memory_compact(file, compacted_content)` — overwrite a file with a summary; backs up the original to `archive/` automatically", - "- `session_focus_get(section?)` / `session_focus_update(...)` — read/update `SESSION.md` working state (current focus, next actions, open questions, handoff)", - "- `git_snapshot(message)` — after updating memories, offer to save a snapshot", + "- `bindery_note_list(category?)` — list canonical story note files", + "- `bindery_note_get(path)` / `bindery_get_notes(category, name)` — read existing canonical notes before routing settled facts out of memory", + "- `bindery_note_create(path, title, content)` — create a new canonical story note when the user confirms settled reference material", + "- `bindery_note_append(path, content, heading?)` — append confirmed material to an existing canonical story note", + "- `bindery_memory_list` — discover which memory files exist and their line counts", + "- `bindery_memory_append(file, title, content)` — append a dated session entry; the tool stamps the date automatically", + "- `bindery_memory_compact(file, compacted_content)` — overwrite a file with a summary; backs up the original to `archive/` automatically", + "- `bindery_session_focus_get(section?)` / `bindery_session_focus_update(...)` — read/update `SESSION.md` working state (current focus, next actions, open questions, handoff)", + "- `bindery_git_snapshot(message)` — after updating memories, offer to save a snapshot", "", "## Steps", "", @@ -60,7 +60,7 @@ const CONTENT = [ "If no local/session memory exists, skip this step.", "", "### 1. Identify what to save and where it belongs", - "List the decisions, insights, or facts from the session worth preserving. For each item, classify it before writing anything. If an item looks like settled character or world reference material, use `note_list` and `note_get`/`get_notes` first to check whether the canonical note already exists or needs updating:", + "List the decisions, insights, or facts from the session worth preserving. For each item, classify it before writing anything. If an item looks like settled character or world reference material, use `bindery_note_list` and `bindery_note_get`/`bindery_get_notes` first to check whether the canonical note already exists or needs updating:", "", "| Destination | What goes here |", "|---|---|", @@ -68,21 +68,21 @@ const CONTENT = [ "| `chXX.md` | Chapter-specific decisions: confirmed plot choices, resolved open questions for that chapter |", "| **Notes / Arc files** (not memory) | Character profiles, world-building detail, magic rules, arc commitments — anything that has become settled enough to be a reference |", "| **`PREFERENCES.md`** (not memory) | Durable working preferences and writing conventions — \"do it like this for me\". User-owned; propose edits, do not write it with tools |", - "| **`SESSION.md`** via `session_focus_update` (not memory) | Current focus, next actions, open questions, handoff — ephemeral working state |", - "| **`Notes/Inbox.md`** (not memory) | Rough, unsorted, or pasted material not yet triaged. Append with `note_append`; triage later with `inbox_process` / `inbox_resolve` |", + "| **`SESSION.md`** via `bindery_session_focus_update` (not memory) | Current focus, next actions, open questions, handoff — ephemeral working state |", + "| **`Notes/Inbox.md`** (not memory) | Rough, unsorted, or pasted material not yet triaged. Append with `bindery_note_append`; triage later with `bindery_inbox_process` / `bindery_inbox_resolve` |", "| **Discard** | Session-local context, tool/workflow observations, things that were speculative and were not confirmed |", "", "> If an item is a preference or suggestion rather than a confirmed decision, either discard it or note it explicitly as tentative (e.g., `\"Tentative: consider X\"`) — do not write it as a fact.", "", - "Items routed to canonical story notes should be written with `note_create` or `note_append` after the user confirms the target file and wording. Do not duplicate them into memory.", + "Items routed to canonical story notes should be written with `bindery_note_create` or `bindery_note_append` after the user confirms the target file and wording. Do not duplicate them into memory.", "", - "> Tool boundary: Bindery has first-class note, character, arc, and session-focus write tools. Route current working state through `session_focus_update` (it touches only the neutral `SESSION.md` sections). `PREFERENCES.md` is user-owned — propose changes for the user instead of writing it.", + "> Tool boundary: Bindery has first-class note, character, arc, and session-focus write tools. Route current working state through `bindery_session_focus_update` (it touches only the neutral `SESSION.md` sections). `PREFERENCES.md` is user-owned — propose changes for the user instead of writing it.", "", "### 2. Check existing files", - "Use `memory_list` to see which memory files exist and how large they are.", + "Use `bindery_memory_list` to see which memory files exist and how large they are.", "", "### 3. Append the entry", - "Use `memory_append` to write to the right file:", + "Use `bindery_memory_append` to write to the right file:", "- `global.md` — cross-chapter decisions (character names, world rules, style choices)", "- `chXX.md` — chapter-specific decisions (e.g. `ch10.md`)", "", @@ -94,11 +94,11 @@ const CONTENT = [ "The tool stamps the current date. Do not add a date to the content.", "", "### 4. Compact if needed", - "If `memory_list` shows a file exceeding ~150 lines, offer to compact it:", + "If `bindery_memory_list` shows a file exceeding ~150 lines, offer to compact it:", "- Summarize the existing content into a concise replacement", - "- For each summarized entry, apply the routing table from Step 1: does it stay in memory, move to a canonical file, or get discarded? Use `note_list` and `note_get`/`get_notes` when checking whether routed note material already exists.", - "- Call `memory_compact(file, compacted_content)` — original is backed up automatically", - "- Move confirmed note material with `note_create` or `note_append`, character material with `character_create` or `character_update`, arc material with `arc_create` or `arc_update`, and current-focus material with `session_focus_update`; propose `PREFERENCES.md` edits for the user", + "- For each summarized entry, apply the routing table from Step 1: does it stay in memory, move to a canonical file, or get discarded? Use `bindery_note_list` and `bindery_note_get`/`bindery_get_notes` when checking whether routed note material already exists.", + "- Call `bindery_memory_compact(file, compacted_content)` — original is backed up automatically", + "- Move confirmed note material with `bindery_note_create` or `bindery_note_append`, character material with `bindery_character_create` or `bindery_character_update`, arc material with `bindery_arc_create` or `bindery_arc_update`, and current-focus material with `bindery_session_focus_update`; propose `PREFERENCES.md` edits for the user", "", "The compaction pass is also a good time to flag any entries that were written as facts but were originally preferences — surface these for the user to confirm or soften.", "", @@ -109,16 +109,16 @@ const CONTENT = [ "- Writing/style conventions → `PREFERENCES.md` (durable, user-owned) or a dedicated style file under `Notes/`", "", "### 5. Snapshot", - "Offer to save a snapshot with `git_snapshot`.", + "Offer to save a snapshot with `bindery_git_snapshot`.", "", "## Rules", - "- Always use `memory_append` — never use the Edit tool to write to memory files", + "- Always use `bindery_memory_append` — never use the Edit tool to write to memory files", "- Do not add dates to content — the tool stamps them automatically", "- Compaction is always opt-in", "- Memory is a log, not a spec — when memory conflicts with `Notes/`, `Arc/`, `PREFERENCES.md`, or `SESSION.md`, the canonical file is authoritative", "", "## Tool boundary", - "Bindery can create and append canonical note files with `note_create` and `note_append`, manage character profiles with `character_create` and `character_update`, manage arc files with `arc_create` and `arc_update`, and update current working state in `SESSION.md` with `session_focus_get`/`session_focus_update` (neutral sections only). `PREFERENCES.md` is user-owned and never tool-written — surface proposed preference changes for the user to apply.", + "Bindery can create and append canonical note files with `bindery_note_create` and `bindery_note_append`, manage character profiles with `bindery_character_create` and `bindery_character_update`, manage arc files with `bindery_arc_create` and `bindery_arc_update`, and update current working state in `SESSION.md` with `bindery_session_focus_get`/`bindery_session_focus_update` (neutral sections only). `PREFERENCES.md` is user-owned and never tool-written — surface proposed preference changes for the user to apply.", ].join('\n') + '\n'; export function render(_ctx: TemplateContext): string { diff --git a/bindery-core/src/templates/skills/plan-beats.ts b/bindery-core/src/templates/skills/plan-beats.ts index ec4555a..e80435e 100644 --- a/bindery-core/src/templates/skills/plan-beats.ts +++ b/bindery-core/src/templates/skills/plan-beats.ts @@ -2,7 +2,7 @@ import type { TemplateContext, TemplateMeta } from '../context'; export const meta: TemplateMeta = { file: '.claude/skills/plan-beats/SKILL.md', - version: 3, + version: 5, label: 'plan-beats skill', }; @@ -25,7 +25,7 @@ const CONTENT = [ "This skill works at levels 1–3. When working with existing beats, detect the current level and treat advancement to the next level as a valid refinement goal.", "", "## Prerequisites", - "This skill requires a Bindery workspace. If unsure, call `identify_book` to check. If no workspace is found, tell the user and stop.", + "This skill requires a Bindery workspace. If unsure, call `bindery_identify_book` to check. If no workspace is found, tell the user and stop.", "", "## Trigger", "User says `/plan-beats`, \"help me plan the beats\", \"let's work on the beatmap\", \"update the beatmap\", \"extend the beatmap\", \"refine chapter beats\", \"discuss the structure\", or any request that explicitly mentions beats, structure, or chapter planning.", @@ -47,12 +47,12 @@ const CONTENT = [ "", "## Tools", "Use these Bindery MCP tools:", - "- `get_chapter(chapterNumber, language)` — read an existing chapter and any embedded beatmap", - "- `get_text(path)` — read settings, `PREFERENCES.md`, `.bindery/README.md`, or memory files", - "- `arc_list` / `arc_get` / `arc_update` — read or update structured arc files that hold beatmaps", - "- `character_list` / `character_get` — load relevant structured character profiles", - "- `get_notes(category, name)` — look up characters, world rules, or equipment", - "- `search(query, language)` — find parallel moments or thematic echoes across the book", + "- `bindery_get_chapter(chapterNumber, language)` — read an existing chapter and any embedded beatmap", + "- `bindery_get_text(path)` — read settings, `PREFERENCES.md`, `.bindery/README.md`, or memory files", + "- `bindery_arc_list` / `bindery_arc_get` / `bindery_arc_update` — read or update structured arc files that hold beatmaps", + "- `bindery_character_list` / `bindery_character_get` — load relevant structured character profiles", + "- `bindery_get_notes(category, name)` — look up characters, world rules, or equipment", + "- `bindery_search(query, language)` — find parallel moments or thematic echoes across the book", "", "> If any tool call fails or returns no data, inform the user which file could not be read and continue without it.", "", @@ -62,7 +62,7 @@ const CONTENT = [ "1. Identify the **mode** and **target** from the user's message. Ask only if genuinely ambiguous. If the user requests a range of chapters (e.g. \"chapters 5–8\"), handle one chapter at a time and confirm before moving to the next.", "", "### Phase 2 — Load arc and settings context", - "2. Read `.bindery/settings.json` with `get_text` (genre, target audience, story structure).", + "2. Read `.bindery/settings.json` with `bindery_get_text` (genre, target audience, story structure).", "3. Read `.bindery/memories/global.md`, `Arc/index.md`, `Arc/Overall.md`, and the relevant file under `Arc/Acts/` for the chapter's act. If no `Arc/` directory exists, check for alternative planning directories (e.g. `Outline/`, `Planning/`); if none found, proceed without arc context and note this to the user.", "", "### Phase 3 — Locate existing beats", @@ -72,12 +72,12 @@ const CONTENT = [ " 3. If still not found, ask the user: *\"Where do you keep your chapter beats — in an arc file, in the chapter itself, or somewhere else?\"*", " 4. If no arc, outline, chapter, or beat file exists at all, tell the user there is no context to work from and ask them to describe the chapter's purpose and key events before proceeding.", "5. Read the relevant file(s) to extract any existing beats for the target chapter.", - "6. If the chapter is already written, read it with `get_chapter` for scene structure and prose context.", + "6. If the chapter is already written, read it with `bindery_get_chapter` for scene structure and prose context.", "7. Read any chapter-level memory or planning note if it exists (e.g. `.bindery/memories/chXX.md`).", "", "### Phase 4 — Load focused context (as needed)", - "8. If character-driven beats are involved: call `character_list`, then `character_get` for relevant profiles. Use `get_notes(category: \"Characters\")` only for older note layouts.", - "9. Call `search` to check for thematic echoes or parallel beats elsewhere in the book.", + "8. If character-driven beats are involved: call `bindery_character_list`, then `bindery_character_get` for relevant profiles. Use `bindery_get_notes(category: \"Characters\")` only for older note layouts.", + "9. Call `bindery_search` to check for thematic echoes or parallel beats elsewhere in the book.", "", "### Phase 5 — Discuss", "10. **all modes**: Align on the chapter's purpose before building beats — confirm or briefly discuss the key story beats and emotional arc. If the user has explicitly stated the chapter's purpose and key turning points, skip to Phase 6. (This is not a clarifying question about mode or target, but a contextual alignment step.)", diff --git a/bindery-core/src/templates/skills/proof-read.ts b/bindery-core/src/templates/skills/proof-read.ts index de70233..25e86f0 100644 --- a/bindery-core/src/templates/skills/proof-read.ts +++ b/bindery-core/src/templates/skills/proof-read.ts @@ -2,7 +2,7 @@ import type { TemplateContext, TemplateMeta } from '../context'; export const meta: TemplateMeta = { file: '.claude/skills/proof-read/SKILL.md', - version: 6, + version: 8, label: 'proof-read skill', }; @@ -18,7 +18,7 @@ const CONTENT = [ "The value is in the isolation. A reader doesn't know what the arc says should happen, what a character's backstory is, or what the chapter was *trying* to do. That's exactly the feedback you can't give yourself, and can't get from an agent that has been working on the book with you.", "", "## Prerequisites", - "This skill requires a Bindery workspace. If unsure, call `identify_book` to check. If no workspace is found, tell the user and stop.", + "This skill requires a Bindery workspace. If unsure, call `bindery_identify_book` to check. If no workspace is found, tell the user and stop.", "", "## Trigger", "User says `/proof-read`, \"proofread chapter X\", \"get reader feedback\", \"how does this land with readers\", \"simulate reader reactions\", or \"peer review\".", @@ -80,7 +80,7 @@ const CONTENT = [ "", "**Why not have subagents call MCP themselves?** Subagents with MCP access could accidentally pull notes, arc files, or overviews. Using a pre-written staging file and passing only that payload to subagents reduces that risk and is the best available way in this workflow to keep them focused on reader-visible text.", "", - "Use `get_book_until(chapterNumber: n, language)` to fetch all prior chapters in one call. If unavailable, loop `get_chapter(1)` through `get_chapter(n)` in the main agent. For a **whole-book** run, fetch all chapters.", + "Use `bindery_get_book_until(chapterNumber: n, language)` to fetch all prior chapters in one call. If unavailable, loop `bindery_get_chapter(1)` through `bindery_get_chapter(n)` in the main agent. For a **whole-book** run, fetch all chapters.", "", "Once the text is retrieved, **write it to a staging file**:", "`.bindery/proof-read-payload.md`", diff --git a/bindery-core/src/templates/skills/read-aloud.ts b/bindery-core/src/templates/skills/read-aloud.ts index d571952..5be93f5 100644 --- a/bindery-core/src/templates/skills/read-aloud.ts +++ b/bindery-core/src/templates/skills/read-aloud.ts @@ -2,7 +2,7 @@ import type { TemplateContext, TemplateMeta } from '../context'; export const meta: TemplateMeta = { file: '.claude/skills/read-aloud/SKILL.md', - version: 11, + version: 13, label: 'read-aloud skill', }; @@ -16,7 +16,7 @@ const CONTENT = [ "Test how a chapter sounds when read aloud — calibrated for books that may be read by an adult to a child, as well as for silent reading.", "", "## Prerequisites", - "This skill requires a Bindery workspace. If unsure, call `identify_book` to check. If no workspace is found, tell the user and stop.", + "This skill requires a Bindery workspace. If unsure, call `bindery_identify_book` to check. If no workspace is found, tell the user and stop.", "", "## Trigger", "User says `/read-aloud`, \"reading test\", or \"how does this sound\".", @@ -25,12 +25,12 @@ const CONTENT = [ "- Whole chapter or specific passage?", "", "## Runtime context", - "Before reviewing, read \".bindery/settings.json\" with `get_text` to pick up the current book's target audience and genre.", + "Before reviewing, read \".bindery/settings.json\" with `bindery_get_text` to pick up the current book's target audience and genre.", "", "## Tools", "Use these Bindery MCP tools:", - "- `get_chapter(chapterNumber, language)` — read the full chapter", - "- `get_text(identifier, startLine, endLine)` — read a specific passage by line range", + "- `bindery_get_chapter(chapterNumber, language)` — read the full chapter", + "- `bindery_get_text(identifier, startLine, endLine)` — read a specific passage by line range", "", "## What to check", "- Sentences over ~30 words", diff --git a/bindery-core/src/templates/skills/read-in.ts b/bindery-core/src/templates/skills/read-in.ts index 87f66b5..70009a5 100644 --- a/bindery-core/src/templates/skills/read-in.ts +++ b/bindery-core/src/templates/skills/read-in.ts @@ -2,7 +2,7 @@ import type { TemplateContext, TemplateMeta } from '../context'; export const meta: TemplateMeta = { file: '.claude/skills/read-in/SKILL.md', - version: 17, + version: 19, label: 'read-in skill', }; @@ -16,44 +16,44 @@ const CONTENT = [ "Load context and get your bearings before starting work.", "", "## Prerequisites", - "This skill requires a Bindery workspace. If unsure, call `identify_book` to check. If no workspace is found, tell the user and stop.", + "This skill requires a Bindery workspace. If unsure, call `bindery_identify_book` to check. If no workspace is found, tell the user and stop.", "", "## Trigger", "User says `/read-in`, \"get your bearings\", \"what were we working on\", or at the start of a session.", "", "## Tools", "Use these Bindery MCP tools:", - "- `update_workspace` — fetch and pull the workspace before loading context; also reports current branch versus the remote default branch", - "- `memory_list` — discover which memory files exist (`global.md`, `chXX.md` files)", - "- `session_focus_get(section?)` — read the current focus / working state from `SESSION.md`", - "- `get_text(identifier)` — read settings and memory files", - "- `arc_list` / `arc_get` — list and read structured arc files", - "- `character_list` / `character_get` — list and read structured character profiles", - "- `get_overview(language)` — list all acts and chapters", - "- `get_notes(category, name)` — look up key character or world notes if relevant to current focus", - "- `search(query, language)` — find relevant passages across the book based on current focus or open questions", - "- `get_chapter(chapterNumber, language)` — read a chapter if that's the current focus", + "- `bindery_update_workspace` — fetch and pull the workspace before loading context; also reports current branch versus the remote default branch", + "- `bindery_memory_list` — discover which memory files exist (`global.md`, `chXX.md` files)", + "- `bindery_session_focus_get(section?)` — read the current focus / working state from `SESSION.md`", + "- `bindery_get_text(identifier)` — read settings and memory files", + "- `bindery_arc_list` / `bindery_arc_get` — list and read structured arc files", + "- `bindery_character_list` / `bindery_character_get` — list and read structured character profiles", + "- `bindery_get_overview(language)` — list all acts and chapters", + "- `bindery_get_notes(category, name)` — look up key character or world notes if relevant to current focus", + "- `bindery_search(query, language)` — find relevant passages across the book based on current focus or open questions", + "- `bindery_get_chapter(chapterNumber, language)` — read a chapter if that's the current focus", "", "## Steps", "", "### 0. Sync repository", - "Call `update_workspace` before loading any context.", + "Call `bindery_update_workspace` before loading any context.", "- If the update fails (for example: no remote, merge issue, or upstream problem), flag it to the user and stop — do not proceed with stale context.", "- If the tool reports that the current branch differs from the remote default branch, mention that briefly so the user can decide whether to switch.", "- If the tool reports that the workspace is already up to date, say nothing unless the branch status matters.", "", "### 1. Check for current focus", - "Use `session_focus_get` to read the current working state from `SESSION.md` (ignore the empty-state message if it has not been created yet).", + "Use `bindery_session_focus_get` to read the current working state from `SESSION.md` (ignore the empty-state message if it has not been created yet).", "", "### 2. Load global memory", - "Use `get_text(\".bindery/settings.json\")` first to pick up the current book's structure and conventions.", - "Then use `memory_list` to discover available memory files, and `get_text(\".bindery/memories/global.md\")` to load cross-chapter decisions.", + "Use `bindery_get_text(\".bindery/settings.json\")` first to pick up the current book's structure and conventions.", + "Then use `bindery_memory_list` to discover available memory files, and `bindery_get_text(\".bindery/memories/global.md\")` to load cross-chapter decisions.", "", "### 3. Read the arc and character map", - "Use `arc_list` and `arc_get` to load the structural map (`index.md`, `Overall.md`, and any relevant act/thread/chapter arc file). Use `character_list` to load the cast map; call `character_get` only for characters relevant to the current focus.", + "Use `bindery_arc_list` and `bindery_arc_get` to load the structural map (`index.md`, `Overall.md`, and any relevant act/thread/chapter arc file). Use `bindery_character_list` to load the cast map; call `bindery_character_get` only for characters relevant to the current focus.", "", "### 4. Read chapter structure", - "Use `get_overview` to load the current chapter/act map for the active language.", + "Use `bindery_get_overview` to load the current chapter/act map for the active language.", "", "### 5. Determine working chapter", "If the current focus in `SESSION.md` names a chapter, use that.", @@ -61,14 +61,14 @@ const CONTENT = [ "Otherwise — **ask the user**: \"Which chapter do you want to work on?\"", "", "### 6. Load chapter memory", - "- Once the chapter is known (e.g. chapter 10), check `memory_list` output for a matching file (`ch10.md`). If it exists, read it with `get_text(\".bindery/memories/ch10.md\")`.", - "- Read the full chapter text with `get_chapter` to have it fresh in context.", + "- Once the chapter is known (e.g. chapter 10), check `bindery_memory_list` output for a matching file (`ch10.md`). If it exists, read it with `bindery_get_text(\".bindery/memories/ch10.md\")`.", + "- Read the full chapter text with `bindery_get_chapter` to have it fresh in context.", "- **Cross-check memory against chapter text:** if the chapter contradicts something in the memory file (a character name, a decision, a detail), flag it explicitly in the summary — do not silently treat memory as correct. Memory records decisions as they stood at the time; the chapter is what was actually written.", "", "### 7. Story / Arc focus", "Load additional context only if it is clearly relevant to the current chapter and focus:", - "- **Characters involved in the chapter**: call `character_get` for their profiles; use `get_notes(category: \"Characters\")` only for older note layouts", - "- **Open questions or unresolved details**: use `search` to find relevant earlier passages", + "- **Characters involved in the chapter**: call `bindery_character_get` for their profiles; use `bindery_get_notes(category: \"Characters\")` only for older note layouts", + "- **Open questions or unresolved details**: use `bindery_search` to find relevant earlier passages", "", "Do not load notes or search speculatively. If the current focus (`SESSION.md`) or the chapter memory names a specific open question, that is a good trigger; otherwise skip this step.", "", diff --git a/bindery-core/src/templates/skills/review.ts b/bindery-core/src/templates/skills/review.ts index ff03114..1b4a861 100644 --- a/bindery-core/src/templates/skills/review.ts +++ b/bindery-core/src/templates/skills/review.ts @@ -2,7 +2,7 @@ import type { TemplateContext, TemplateMeta } from '../context'; export const meta: TemplateMeta = { file: '.claude/skills/review/SKILL.md', - version: 14, + version: 16, label: 'review skill', }; @@ -16,7 +16,7 @@ const CONTENT = [ "Review a chapter and give structured feedback.", "", "## Prerequisites", - "This skill requires a Bindery workspace. If unsure, call `identify_book` to check. If no workspace is found, tell the user and stop.", + "This skill requires a Bindery workspace. If unsure, call `bindery_identify_book` to check. If no workspace is found, tell the user and stop.", "", "## Trigger", "User says `/review`, \"review chapter X\", \"quick review\", or \"review my changes\".", @@ -28,23 +28,23 @@ const CONTENT = [ "", "## Tools", "Use these Bindery MCP tools to gather context:", - "- `get_review_text(autoStage: true, contextLines: 3)` — returns the git diff of uncommitted changes **plus** any regions wrapped in `` / `` markers (works even on committed work). `autoStage: true` stages reviewed files **and** removes the marker lines from disk so the next call only shows new changes. Pass more contextLines when join points to existing prose need checking", - "- `get_chapter(chapterNumber, language)` — read the full chapter text", - "- `get_notes(category, name)` — look up character profiles (`category: \"Characters\"`) or world rules", - "- `search(query, language)` — find related passages across the book", - "- `git_snapshot(message)` — after a successful review, suggest saving a snapshot", + "- `bindery_get_review_text(autoStage: true, contextLines: 3)` — returns the git diff of uncommitted changes **plus** any regions wrapped in `` / `` markers (works even on committed work). `autoStage: true` stages reviewed files **and** removes the marker lines from disk so the next call only shows new changes. Pass more contextLines when join points to existing prose need checking", + "- `bindery_get_chapter(chapterNumber, language)` — read the full chapter text", + "- `bindery_get_notes(category, name)` — look up character profiles (`category: \"Characters\"`) or world rules", + "- `bindery_search(query, language)` — find related passages across the book", + "- `bindery_git_snapshot(message)` — after a successful review, suggest saving a snapshot", "", "## Steps", "", "### 1. Load settings and context", - "Start by reading \".bindery/settings.json\" with `get_text(\".bindery/settings.json\")` to pick up the current book's target audience, genre, and story structure.", + "Start by reading \".bindery/settings.json\" with `bindery_get_text(\".bindery/settings.json\")` to pick up the current book's target audience, genre, and story structure.", "", "Load the right context, pick any or all as needed:", "- Read `.bindery/memories/global.md`", "- Read `.bindery/memories/chXX.md` if it exists for chapter-specific context", - "- Use `get_chapter` to load the chapter", + "- Use `bindery_get_chapter` to load the chapter", "- For a Full review, read the relevant arc file from `Arc/`.", - "- For \"review my changes\", use `get_review_text` to get the diff", + "- For \"review my changes\", use `bindery_get_review_text` to get the diff", "- If the diff includes translated chapter files, flag that and offer `/translation-review` for source-vs-translation feedback", "", "### 2. Perform the review", @@ -54,7 +54,7 @@ const CONTENT = [ "**Full** — adds:", "- Arc consistency with the arc file", "- Age-appropriateness for the book's configured target audience", - "- Character consistency (`get_notes(category: \"Characters\")`)", + "- Character consistency (`bindery_get_notes(category: \"Characters\")`)", "- **Pacing** — are there sections that drag, rush, or repeat? Does the chapter breathe? Emotional beats and action beats should alternate", "- **Show vs tell** — is character emotion or intent over-explained rather than shown through action, dialogue, or physical detail?", "- **Scene purpose** — does the chapter earn its place? It should accomplish at least one of: advance the plot, reveal character, deepen the world. Flag if it does none of these", @@ -73,7 +73,7 @@ const CONTENT = [ "- End with a 2–3 sentence overall impression", "", "### 4. After review", - "If the review looks good, suggest: \"Want me to save a snapshot?\" (calls `git_snapshot`).", + "If the review looks good, suggest: \"Want me to save a snapshot?\" (calls `bindery_git_snapshot`).", "", "## Rules", "- Do not rewrite unless asked — suggest only", diff --git a/bindery-core/src/templates/skills/status.ts b/bindery-core/src/templates/skills/status.ts index c96de1d..8860c09 100644 --- a/bindery-core/src/templates/skills/status.ts +++ b/bindery-core/src/templates/skills/status.ts @@ -2,7 +2,7 @@ import type { TemplateContext, TemplateMeta } from '../context'; export const meta: TemplateMeta = { file: '.claude/skills/status/SKILL.md', - version: 15, + version: 17, label: 'status skill', }; @@ -18,26 +18,26 @@ const CONTENT = [ "Primary use case: returning to a project after time away and needing to quickly understand where things stand before starting work.", "", "## Prerequisites", - "This skill requires a Bindery workspace. If unsure, call `identify_book` to check. If no workspace is found, tell the user and stop.", + "This skill requires a Bindery workspace. If unsure, call `bindery_identify_book` to check. If no workspace is found, tell the user and stop.", "", "## Trigger", "User says `/status`, \"what's the book status\", or \"where are we\".", "", "## Tools", "Use these Bindery MCP tools:", - "- `get_overview(language)` — list all acts and chapters with titles", - "- `session_focus_get(section?)` — read the current focus / working state from `SESSION.md`", - "- `get_text(identifier)` — read settings.json and memory files", - "- `arc_list` / `arc_get` — inspect arc structure and relevant arc files", - "- `character_list` — inspect the cast index when character-profile coverage matters", - "- `memory_list` — discover which chapter memory files exist (`chXX.md`)", + "- `bindery_get_overview(language)` — list all acts and chapters with titles", + "- `bindery_session_focus_get(section?)` — read the current focus / working state from `SESSION.md`", + "- `bindery_get_text(identifier)` — read settings.json and memory files", + "- `bindery_arc_list` / `bindery_arc_get` — inspect arc structure and relevant arc files", + "- `bindery_character_list` — inspect the cast index when character-profile coverage matters", + "- `bindery_memory_list` — discover which chapter memory files exist (`chXX.md`)", "", "## Steps", "", - "1. Use `get_text(\".bindery/settings.json\")` to pick up the current book's structure and conventions.", - "2. Use `get_overview` for the full chapter listing and act structure.", - "3. Use `memory_list` to check available memory files. Use `session_focus_get` for current focus, and `get_text` to read `.bindery/memories/global.md` plus relevant `.bindery/memories/chXX.md` files.", - "4. Use `arc_list`/`arc_get` for arc context and `character_list` for cast coverage when needed.", + "1. Use `bindery_get_text(\".bindery/settings.json\")` to pick up the current book's structure and conventions.", + "2. Use `bindery_get_overview` for the full chapter listing and act structure.", + "3. Use `bindery_memory_list` to check available memory files. Use `bindery_session_focus_get` for current focus, and `bindery_get_text` to read `.bindery/memories/global.md` plus relevant `.bindery/memories/chXX.md` files.", + "4. Use `bindery_arc_list`/`bindery_arc_get` for arc context and `bindery_character_list` for cast coverage when needed.", "5. Check `Arc/` for what's planned vs written (`index.md`, `Overall.md`, and the relevant act/thread/chapter arc file).", "6. Output: overall chapter count / current focus / likely recently completed or active chapters / next 2-3 chapters / open questions.", "7. If status is ambiguous, say so explicitly and ask one clarifying question instead of guessing.", diff --git a/bindery-core/src/templates/skills/translate.ts b/bindery-core/src/templates/skills/translate.ts index 62ad0b4..045c0e8 100644 --- a/bindery-core/src/templates/skills/translate.ts +++ b/bindery-core/src/templates/skills/translate.ts @@ -2,7 +2,7 @@ import type { TemplateContext, TemplateMeta } from '../context'; export const meta: TemplateMeta = { file: '.claude/skills/translate/SKILL.md', - version: 10, + version: 12, label: 'translate skill', }; @@ -16,7 +16,7 @@ const CONTENT = [ "Translate a chapter or passage into the target language.", "", "## Prerequisites", - "This skill requires a Bindery workspace. If unsure, call `identify_book` to check. If no workspace is found, tell the user and stop.", + "This skill requires a Bindery workspace. If unsure, call `bindery_identify_book` to check. If no workspace is found, tell the user and stop.", "", "## Trigger", "User says `/translate`, \"translate chapter X\", or \"help me with the translation\".", @@ -27,20 +27,20 @@ const CONTENT = [ "", "## Tools", "Use these Bindery MCP tools:", - "- `get_chapter(chapterNumber, language)` — read a chapter in any language (source or existing translation)", - "- `get_translation(targetLanguage)` — list glossary entries for a target language (e.g. `\"nl\"`)", - "- `get_translation(targetLanguage, word)` — look up a specific term; forgiving: case-insensitive, handles plurals and inflected forms", - "- `search(query, targetLanguage)` — verify how a term was rendered in other translated chapters", - "- `add_translation(targetLanguage, from, to)` — save a new glossary term pair when the user confirms a translation choice", + "- `bindery_get_chapter(chapterNumber, language)` — read a chapter in any language (source or existing translation)", + "- `bindery_get_translation(targetLanguage)` — list glossary entries for a target language (e.g. `\"nl\"`)", + "- `bindery_get_translation(targetLanguage, word)` — look up a specific term; forgiving: case-insensitive, handles plurals and inflected forms", + "- `bindery_search(query, targetLanguage)` — verify how a term was rendered in other translated chapters", + "- `bindery_add_translation(targetLanguage, from, to)` — save a new glossary term pair when the user confirms a translation choice", "", "## Steps", "", "### 1. Load the translation table", - "Call `get_translation(targetLanguage)` to load all known glossary term mappings for the target language before translating anything.", + "Call `bindery_get_translation(targetLanguage)` to load all known glossary term mappings for the target language before translating anything.", "", "### 2. Load the chapter", - "Use `get_chapter(chapterNumber, sourceLanguage)` to read the source chapter.", - "For spot-check mode, also call `get_chapter(chapterNumber, targetLanguage)` to read the existing translation.", + "Use `bindery_get_chapter(chapterNumber, sourceLanguage)` to read the source chapter.", + "For spot-check mode, also call `bindery_get_chapter(chapterNumber, targetLanguage)` to read the existing translation.", "", "### 3. Translate or review", "**Full translation** — translate paragraph by paragraph, applying all terms from the glossary. Output the full result in a fenced ```markdown block for easy pasting.", @@ -67,7 +67,7 @@ const CONTENT = [ "|---|---|---|---|---|", "", "### 4. Save confirmed terms", - "When the user confirms a new or corrected term translation, call `add_translation` to persist it as a glossary entry. For spelling variant rules (dialect substitutions applied at export), use `add_dialect` instead.", + "When the user confirms a new or corrected term translation, call `bindery_add_translation` to persist it as a glossary entry. For spelling variant rules (dialect substitutions applied at export), use `bindery_add_dialect` instead.", "", "## Rules", "- Always load the translation table first — never invent translations for world-specific terms", diff --git a/bindery-core/src/templates/skills/translation-review.ts b/bindery-core/src/templates/skills/translation-review.ts index 6766b20..f9c5328 100644 --- a/bindery-core/src/templates/skills/translation-review.ts +++ b/bindery-core/src/templates/skills/translation-review.ts @@ -2,7 +2,7 @@ import type { TemplateContext, TemplateMeta } from '../context'; export const meta: TemplateMeta = { file: '.claude/skills/translation-review/SKILL.md', - version: 5, + version: 7, label: 'translation-review skill', }; @@ -18,7 +18,7 @@ const CONTENT = [ "Use this when the user has written or updated the target-language text and wants structured feedback.", "", "## Prerequisites", - "This skill requires a Bindery workspace. If unsure, call `identify_book` to check. If no workspace is found, tell the user and stop.", + "This skill requires a Bindery workspace. If unsure, call `bindery_identify_book` to check. If no workspace is found, tell the user and stop.", "", "## Trigger", "User says `/translation-review`, \"review my translation\", or \"what do you think\" when translation is the active focus.", @@ -29,27 +29,27 @@ const CONTENT = [ "", "## Tools", "Use these Bindery MCP tools:", - "- `get_review_text(autoStage: true, contextLines: 3)` — git diff of uncommitted changes **plus** any regions wrapped in `` / `` markers. Marker regions surface even after the author committed and continued elsewhere. `autoStage: true` stages files **and** consumes (removes) the marker lines so the next pass starts clean.", - "- `get_text(identifier, startLine?, endLine?)` — fetch matching source lines or focused ranges", - "- `get_translation(targetLanguage)` — load glossary terms for the target language before reviewing", - "- `get_chapter(chapterNumber, language)` — full chapter source/target pair for full spot-check mode", - "- `search(query, targetLanguage)` — verify how a term was used in previously translated chapters before flagging it", - "- `add_translation(targetLanguage, from, to)` — persist a confirmed glossary correction", + "- `bindery_get_review_text(autoStage: true, contextLines: 3)` — git diff of uncommitted changes **plus** any regions wrapped in `` / `` markers. Marker regions surface even after the author committed and continued elsewhere. `autoStage: true` stages files **and** consumes (removes) the marker lines so the next pass starts clean.", + "- `bindery_get_text(identifier, startLine?, endLine?)` — fetch matching source lines or focused ranges", + "- `bindery_get_translation(targetLanguage)` — load glossary terms for the target language before reviewing", + "- `bindery_get_chapter(chapterNumber, language)` — full chapter source/target pair for full spot-check mode", + "- `bindery_search(query, targetLanguage)` — verify how a term was used in previously translated chapters before flagging it", + "- `bindery_add_translation(targetLanguage, from, to)` — persist a confirmed glossary correction", "", "## Mode 1 - Scoped diff review (primary)", "", "### Steps", "", - "1. Call `get_review_text`. The response has two sections: a `# Git diff` block and a `# Review markers` block (one or both may be empty).", + "1. Call `bindery_get_review_text`. The response has two sections: a `# Git diff` block and a `# Review markers` block (one or both may be empty).", "2. If both sections are empty, report that nothing new has been translated yet.", "3. Identify changed files and determine source/target language from available context: the session file (`SESSION.md`), recent conversation, or ask the user if ambiguous.", "4. If the target-language file changed (or has marker regions), capture the changed/marked target line range.", "5. **Line parity matching** — attempt to fetch the corresponding source lines:", - " - First, assume line parity: call `get_text(sourceFile, startLine, endLine)` for the same range as the target.", - " - **If the content is a complete mismatch** (opening words differ significantly), the translation work may have added or removed lines. Search a window: fetch `get_text(sourceFile, startLine - 5, endLine + 5)` and scan for the target text within that range.", + " - First, assume line parity: call `bindery_get_text(sourceFile, startLine, endLine)` for the same range as the target.", + " - **If the content is a complete mismatch** (opening words differ significantly), the translation work may have added or removed lines. Search a window: fetch `bindery_get_text(sourceFile, startLine - 5, endLine + 5)` and scan for the target text within that range.", " - **If still not found**, ask the user: \"I couldn't locate these source lines. Can you point me to the starting line number in the source file for this translation?\"", - "6. Load glossary entries via `get_translation(targetLanguage)`.", - "7. Use `search(query, targetLanguage)` when a term may have an established translation elsewhere in the book.", + "6. Load glossary entries via `bindery_get_translation(targetLanguage)`.", + "7. Use `bindery_search(query, targetLanguage)` when a term may have an established translation elsewhere in the book.", "8. Compare source vs target and produce feedback using the table below. Also check:", " - **HTML comments** (``): these are writer notes and must appear unchanged in the translation file. If any are modified, moved, or removed in the diff, flag them", "9. If source-language lines also changed, flag that and suggest `/review` for source-quality feedback.", @@ -59,9 +59,9 @@ const CONTENT = [ "Use this when the user asks for a full chapter comparison.", "", "1. Determine source language, target language, and chapter number.", - "2. Load glossary with `get_translation(targetLanguage)`.", - "3. Use `search(query, targetLanguage)` as needed to verify recurring terminology in earlier translated chapters.", - "4. Load chapters with `get_chapter(chapterNumber, sourceLanguage)` and `get_chapter(chapterNumber, targetLanguage)`.", + "2. Load glossary with `bindery_get_translation(targetLanguage)`.", + "3. Use `bindery_search(query, targetLanguage)` as needed to verify recurring terminology in earlier translated chapters.", + "4. Load chapters with `bindery_get_chapter(chapterNumber, sourceLanguage)` and `bindery_get_chapter(chapterNumber, targetLanguage)`.", "5. Compare paragraph by paragraph and report findings with the same table. Check for:", " - Glossary mismatches — world-specific terms that differ from the stored glossary", " - Meaning drift — passages where translation changes or loses the source intent", @@ -95,7 +95,7 @@ const CONTENT = [ "- Suggest edits only; do not rewrite entire passages unless asked", "- Bold only changed words in Before/After rows", "- Mark uncertain calls as questions for user confirmation", - "- When the user confirms a corrected term, call `add_translation` before moving on", + "- When the user confirms a corrected term, call `bindery_add_translation` before moving on", "- Respond in the session language (usually source language)", ].join('\n') + '\n'; diff --git a/mcp-ts/src/index.ts b/mcp-ts/src/index.ts index 03142db..72b7cb1 100644 --- a/mcp-ts/src/index.ts +++ b/mcp-ts/src/index.ts @@ -6,7 +6,7 @@ * --book Name=path CLI flags (claude_desktop_config.json, .vscode/mcp.json) * BINDERY_BOOKS env var with semicolon-separated Name=path pairs (mcpb) * - * Every tool requires an explicit `book` argument. Use list_books to discover + * Every tool requires an explicit `book` argument. Use bindery_list_books to discover * available names. Agents never receive or provide raw filesystem paths. */ @@ -68,7 +68,7 @@ const server = new McpServer( // ─── Shared helpers ─────────────────────────────────────────────────────────── const bookSchema = z.string().describe( - 'Book name as configured via --book args (e.g. "MyNovel"). Call list_books to see available names.' + 'Book name as configured via --book args (e.g. "MyNovel"). Call bindery_list_books to see available names.' ); const characterFields = { @@ -106,7 +106,7 @@ function err(e: unknown) { return { content: [{ type: 'text' as const, text: ` // ─── Tools ──────────────────────────────────────────────────────────────────── -server.registerTool('list_books', { +server.registerTool('bindery_list_books', { title: 'List Books', description: 'List all books registered via --book args in the MCP server config. Call this first to discover available book names.', inputSchema: {}, @@ -123,7 +123,7 @@ server.registerTool('list_books', { return ok(books.map(b => `${b.name} → ${b.path}`).join('\n')); }); -server.registerTool('identify_book', { +server.registerTool('bindery_identify_book', { title: 'Identify Book', description: 'Identify which book matches the directory you are working in. ' + @@ -152,7 +152,7 @@ server.registerTool('identify_book', { } catch (e) { return err(e); } }); -server.registerTool('health', { +server.registerTool('bindery_health', { title: 'Health Check', description: 'Check server status: active book, settings, index, and embedding backend.', inputSchema: { book: bookSchema }, @@ -161,7 +161,7 @@ server.registerTool('health', { try { return ok(toolHealth(resolveBook(book).root)); } catch (e) { return err(e); } }); -server.registerTool('index_build', { +server.registerTool('bindery_index_build', { title: 'Build Index', description: 'Build or rebuild the lexical search index and, when enabled, the semantic embedding index for a book. Run after meaningful content changes.', inputSchema: { book: bookSchema }, @@ -170,7 +170,7 @@ server.registerTool('index_build', { try { return ok(await toolIndexBuild(resolveBook(book).root)); } catch (e) { return err(e); } }); -server.registerTool('index_status', { +server.registerTool('bindery_index_status', { title: 'Index Status', description: 'Show current index metadata: chunk count and build time.', inputSchema: { book: bookSchema }, @@ -179,7 +179,7 @@ server.registerTool('index_status', { try { return ok(toolIndexStatus(resolveBook(book).root)); } catch (e) { return err(e); } }); -server.registerTool('get_text', { +server.registerTool('bindery_get_text', { title: 'Get Text', description: 'Read a source file by relative path, optionally restricted to a line range.', inputSchema: { @@ -193,7 +193,7 @@ server.registerTool('get_text', { try { return ok(toolGetText(resolveBook(book).root, { identifier, startLine, endLine })); } catch (e) { return err(e); } }); -server.registerTool('get_chapter', { +server.registerTool('bindery_get_chapter', { title: 'Get Chapter', description: 'Fetch the full content of a chapter by number and language.', inputSchema: { @@ -206,7 +206,7 @@ server.registerTool('get_chapter', { try { return ok(toolGetChapter(resolveBook(book).root, { chapterNumber, language })); } catch (e) { return err(e); } }); -server.registerTool('get_book_until', { +server.registerTool('bindery_get_book_until', { title: 'Get Book Until', description: 'Fetch chapters from a starting chapter through a target chapter (inclusive), concatenated in reading order.', inputSchema: { @@ -220,7 +220,7 @@ server.registerTool('get_book_until', { try { return ok(toolGetBookUntil(resolveBook(book).root, { chapterNumber, language, startChapter })); } catch (e) { return err(e); } }); -server.registerTool('get_overview', { +server.registerTool('bindery_get_overview', { title: 'Get Overview', description: 'List the chapter structure (acts, chapters, titles) for one or all languages.', inputSchema: { @@ -233,7 +233,7 @@ server.registerTool('get_overview', { try { return ok(toolGetOverview(resolveBook(book).root, { language, act })); } catch (e) { return err(e); } }); -server.registerTool('get_notes', { +server.registerTool('bindery_get_notes', { title: 'Get Notes', description: 'Read from Notes/ files, optionally filtered by category name or character/place name.', inputSchema: { @@ -246,7 +246,7 @@ server.registerTool('get_notes', { try { return ok(toolGetNotes(resolveBook(book).root, { category, name })); } catch (e) { return err(e); } }); -server.registerTool('note_list', { +server.registerTool('bindery_note_list', { title: 'Note List', description: 'List markdown note files under the configured notes folder, optionally filtered to a category folder.', inputSchema: { @@ -258,7 +258,7 @@ server.registerTool('note_list', { try { return ok(toolNoteList(resolveBook(book).root, { category })); } catch (e) { return err(e); } }); -server.registerTool('note_get', { +server.registerTool('bindery_note_get', { title: 'Note Get', description: 'Read a single markdown note by path relative to the configured notes folder.', inputSchema: { @@ -270,7 +270,7 @@ server.registerTool('note_get', { try { return ok(toolNoteGet(resolveBook(book).root, { path })); } catch (e) { return err(e); } }); -server.registerTool('note_create', { +server.registerTool('bindery_note_create', { title: 'Note Create', description: 'Create a markdown note under the configured notes folder. Refuses to overwrite unless overwrite is true.', inputSchema: { @@ -285,7 +285,7 @@ server.registerTool('note_create', { try { return ok(toolNoteCreate(resolveBook(book).root, { path, title, content, overwrite })); } catch (e) { return err(e); } }); -server.registerTool('note_append', { +server.registerTool('bindery_note_append', { title: 'Note Append', description: 'Append markdown content to a note under the configured notes folder, creating the file if needed.', inputSchema: { @@ -299,7 +299,7 @@ server.registerTool('note_append', { try { return ok(toolNoteAppend(resolveBook(book).root, { path, content, heading })); } catch (e) { return err(e); } }); -server.registerTool('character_list', { +server.registerTool('bindery_character_list', { title: 'Character List', description: 'List structured character profile files under the configured characters folder.', inputSchema: { @@ -311,7 +311,7 @@ server.registerTool('character_list', { try { return ok(toolCharacterList(resolveBook(book).root, { name })); } catch (e) { return err(e); } }); -server.registerTool('character_get', { +server.registerTool('bindery_character_get', { title: 'Character Get', description: 'Read a structured character profile by character name.', inputSchema: { @@ -323,7 +323,7 @@ server.registerTool('character_get', { try { return ok(toolCharacterGet(resolveBook(book).root, { name })); } catch (e) { return err(e); } }); -server.registerTool('character_create', { +server.registerTool('bindery_character_create', { title: 'Character Create', description: 'Create a structured character profile and update Notes/Characters/index.md.', inputSchema: { @@ -337,7 +337,7 @@ server.registerTool('character_create', { try { return ok(toolCharacterCreate(resolveBook(book).root, args)); } catch (e) { return err(e); } }); -server.registerTool('character_update', { +server.registerTool('bindery_character_update', { title: 'Character Update', description: 'Update known fields in a structured character profile and refresh the character index row.', inputSchema: { @@ -350,7 +350,7 @@ server.registerTool('character_update', { try { return ok(toolCharacterUpdate(resolveBook(book).root, args)); } catch (e) { return err(e); } }); -server.registerTool('arc_list', { +server.registerTool('bindery_arc_list', { title: 'Arc List', description: 'List structured arc files under the configured arc folder.', inputSchema: { @@ -362,7 +362,7 @@ server.registerTool('arc_list', { try { return ok(toolArcList(resolveBook(book).root, { kind })); } catch (e) { return err(e); } }); -server.registerTool('arc_get', { +server.registerTool('bindery_arc_get', { title: 'Arc Get', description: 'Read a structured arc file by path relative to the configured arc folder.', inputSchema: { @@ -374,7 +374,7 @@ server.registerTool('arc_get', { try { return ok(toolArcGet(resolveBook(book).root, { path })); } catch (e) { return err(e); } }); -server.registerTool('arc_create', { +server.registerTool('bindery_arc_create', { title: 'Arc Create', description: 'Create a structured arc file under the configured arc folder and update Arc/index.md.', inputSchema: { @@ -388,7 +388,7 @@ server.registerTool('arc_create', { try { return ok(toolArcCreate(resolveBook(book).root, args)); } catch (e) { return err(e); } }); -server.registerTool('arc_update', { +server.registerTool('bindery_arc_update', { title: 'Arc Update', description: 'Update known fields in a structured arc file and refresh Arc/index.md.', inputSchema: { @@ -401,7 +401,7 @@ server.registerTool('arc_update', { try { return ok(toolArcUpdate(resolveBook(book).root, args)); } catch (e) { return err(e); } }); -server.registerTool('search', { +server.registerTool('bindery_search', { title: 'Search', description: 'Search the book corpus using lexical BM25, semantic reranking, or full semantic search. If Ollama or a semantic index is unavailable, semantic modes fall back to lexical results with a warning.', inputSchema: { @@ -416,7 +416,7 @@ server.registerTool('search', { try { return ok(await toolSearch(resolveBook(book).root, { query, language, maxResults, mode })); } catch (e) { return err(e); } }); -server.registerTool('format', { +server.registerTool('bindery_format', { title: 'Format Typography', description: 'Apply typography formatting (curly quotes, em-dashes, ellipses) to a file or folder.', inputSchema: { @@ -430,7 +430,7 @@ server.registerTool('format', { try { return ok(toolFormat(resolveBook(book).root, { filePath, dryRun, noRecurse })); } catch (e) { return err(e); } }); -server.registerTool('get_review_text', { +server.registerTool('bindery_get_review_text', { title: 'Review Text', description: 'Structured review payload combining (a) the git diff of uncommitted changes and (b) any regions wrapped in Bindery review markers ' + @@ -449,7 +449,7 @@ server.registerTool('get_review_text', { try { return ok(toolGetReviewText(resolveBook(book).root, { language, contextLines, autoStage })); } catch (e) { return err(e); } }); -server.registerTool('update_workspace', { +server.registerTool('bindery_update_workspace', { title: 'Update Workspace', description: 'Fetch and pull the current git branch for this workspace, report the current branch versus the remote default branch, ' + @@ -466,7 +466,7 @@ server.registerTool('update_workspace', { try { return ok(toolUpdateWorkspace(resolveBook(book).root, { remote, branch, switchBranch, autoStash })); } catch (e) { return err(e); } }); -server.registerTool('git_snapshot', { +server.registerTool('bindery_git_snapshot', { title: 'Git Snapshot', description: 'Save a snapshot (git commit) of all changes in the bindery workspace. ' + @@ -485,13 +485,13 @@ server.registerTool('git_snapshot', { try { return ok(toolGitSnapshot(resolveBook(book).root, { message, push, remote, branch, rememberPushDefaults })); } catch (e) { return err(e); } }); -server.registerTool('get_translation', { +server.registerTool('bindery_get_translation', { title: 'Get Translation', description: 'Look up glossary entries in .bindery/translations.json. ' + 'Without a word, lists all entries for the language. ' + 'With a word, does a forgiving case-insensitive lookup including plural and inflected forms. ' + - 'For dialect substitution rules, use get_dialect instead.', + 'For dialect substitution rules, use bindery_get_dialect instead.', inputSchema: { book: bookSchema, language: z.string().describe('Language code or label (e.g. "nl", "fr", "Dutch")'), @@ -503,12 +503,12 @@ server.registerTool('get_translation', { try { return ok(toolGetTranslation(resolveBook(book).root, { language, word, type })); } catch (e) { return err(e); } }); -server.registerTool('add_translation', { +server.registerTool('bindery_add_translation', { title: 'Add Translation', description: 'Add or update a glossary entry in .bindery/translations.json for agent reference. ' + 'Glossaries are cross-language term pairs (source → target language) used by agents for consistency, ' + - 'not auto-applied during export. For dialect substitution rules (e.g. US→UK spelling), use add_dialect.', + 'not auto-applied during export. For dialect substitution rules (e.g. US→UK spelling), use bindery_add_dialect.', inputSchema: { book: bookSchema, targetLangCode: z.string().describe('Target language code (e.g. "nl", "fr")'), @@ -520,12 +520,12 @@ server.registerTool('add_translation', { try { return ok(toolAddTranslation(resolveBook(book).root, { targetLangCode, from, to })); } catch (e) { return err(e); } }); -server.registerTool('add_dialect', { +server.registerTool('bindery_add_dialect', { title: 'Add Dialect Rule', description: 'Add or update a dialect substitution rule in .bindery/translations.json. ' + 'Substitution rules are auto-applied during export (e.g. US→UK spelling: color→colour). ' + - 'For cross-language glossary entries, use add_translation.', + 'For cross-language glossary entries, use bindery_add_translation.', inputSchema: { book: bookSchema, dialectCode: z.string().describe('Dialect code used as key, e.g. "en-gb"'), @@ -537,13 +537,13 @@ server.registerTool('add_dialect', { try { return ok(toolAddDialect(resolveBook(book).root, { dialectCode, from, to })); } catch (e) { return err(e); } }); -server.registerTool('get_dialect', { +server.registerTool('bindery_get_dialect', { title: 'Get Dialect Rules', description: 'Look up dialect substitution rules in .bindery/translations.json. ' + 'Without a word, lists all rules for the dialect. ' + 'With a word, does a forgiving case-insensitive lookup. ' + - 'For cross-language glossary entries, use get_translation.', + 'For cross-language glossary entries, use bindery_get_translation.', inputSchema: { book: bookSchema, dialectCode: z.string().describe('Dialect code, e.g. "en-gb"'), @@ -554,7 +554,7 @@ server.registerTool('get_dialect', { try { return ok(toolGetDialect(resolveBook(book).root, { dialectCode, word })); } catch (e) { return err(e); } }); -server.registerTool('add_language', { +server.registerTool('bindery_add_language', { title: 'Add Language', description: 'Add a new language to .bindery/settings.json and optionally scaffold ' + @@ -574,7 +574,7 @@ server.registerTool('add_language', { try { return ok(toolAddLanguage(resolveBook(book).root, { code, folderName, chapterWord, actPrefix, prologueLabel, epilogueLabel, createStubs })); } catch (e) { return err(e); } }); -server.registerTool('init_workspace', { +server.registerTool('bindery_init_workspace', { title: 'Init Workspace', description: 'Create or update .bindery/settings.json, .bindery/translations.json, .bindery/README.md, ' + @@ -596,7 +596,7 @@ server.registerTool('init_workspace', { try { return ok(toolInitWorkspace(resolveBook(book).root, { bookTitle, author, storyFolder, genre, description, targetAudience })); } catch (e) { return err(e); } }); -server.registerTool('settings_update', { +server.registerTool('bindery_settings_update', { title: 'Settings Update', description: 'Merge a partial patch into .bindery/settings.json without replacing unrelated keys. ' + @@ -610,12 +610,12 @@ server.registerTool('settings_update', { try { return ok(toolSettingsUpdate(resolveBook(book).root, { patch })); } catch (e) { return err(e); } }); -server.registerTool('setup_ai_files', { +server.registerTool('bindery_setup_ai_files', { title: 'Setup AI Files', description: 'Generate AI assistant instruction files (CLAUDE.md, .github/copilot-instructions.md, ' + '.cursor/rules, AGENTS.md), Claude skill templates, and the generated .bindery/README.md capability reference from .bindery/settings.json. ' + - 'Run init_workspace first. Safe to run multiple times — skips existing files unless overwrite is true.', + 'Run bindery_init_workspace first. Safe to run multiple times — skips existing files unless overwrite is true.', inputSchema: { book: bookSchema, targets: z.array(z.string()).optional().describe('Which files to generate: claude, copilot, cursor, agents. Default: all.'), @@ -627,7 +627,7 @@ server.registerTool('setup_ai_files', { try { return ok(toolSetupAiFiles(resolveBook(book).root, { targets, skills, overwrite })); } catch (e) { return err(e); } }); -server.registerTool('memory_list', { +server.registerTool('bindery_memory_list', { title: 'Memory List', description: 'List all session memory files in .bindery/memories/. Returns each filename and its line count.', inputSchema: { book: bookSchema }, @@ -636,7 +636,7 @@ server.registerTool('memory_list', { try { return ok(toolMemoryList(resolveBook(book).root)); } catch (e) { return err(e); } }); -server.registerTool('memory_append', { +server.registerTool('bindery_memory_append', { title: 'Memory Append', description: 'Append a dated session entry to a memory file in .bindery/memories/. ' + @@ -653,7 +653,7 @@ server.registerTool('memory_append', { try { return ok(toolMemoryAppend(resolveBook(book).root, { file, title, content })); } catch (e) { return err(e); } }); -server.registerTool('memory_compact', { +server.registerTool('bindery_memory_compact', { title: 'Memory Compact', description: 'Overwrite a memory file with a compacted version supplied by the model. ' + @@ -669,7 +669,7 @@ server.registerTool('memory_compact', { try { return ok(toolMemoryCompact(resolveBook(book).root, { file, compacted_content })); } catch (e) { return err(e); } }); -server.registerTool('session_focus_get', { +server.registerTool('bindery_session_focus_get', { title: 'Session Focus Get', description: 'Read the ephemeral session file (default SESSION.md) holding current working state. ' + @@ -685,14 +685,14 @@ server.registerTool('session_focus_get', { try { return ok(toolSessionFocusGet(resolveBook(book).root, { section })); } catch (e) { return err(e); } }); -server.registerTool('session_focus_update', { +server.registerTool('bindery_session_focus_update', { title: 'Session Focus Update', description: 'Update neutral sections of the ephemeral session file (default SESSION.md): Current Focus, Next Actions, Open Questions, Handoff Notes. ' + 'Only the sections you pass are changed; all other content (and the user-owned PREFERENCES.md) is preserved. ' + 'mode "replace" (default) overwrites a section body; mode "append" adds beneath existing content (natural for handoff notes). ' + 'Creates the session file from the standard scaffold if it does not exist. ' + - 'Use this for current working state, not durable preferences (PREFERENCES.md) or durable decisions (memory_append).', + 'Use this for current working state, not durable preferences (PREFERENCES.md) or durable decisions (bindery_memory_append).', inputSchema: { book: bookSchema, currentFocus: z.string().optional().describe('New content for the Current Focus section'), @@ -710,28 +710,28 @@ server.registerTool('session_focus_update', { } catch (e) { return err(e); } }); -server.registerTool('inbox_process', { +server.registerTool('bindery_inbox_process', { title: 'Inbox Process', description: 'Read the notes Inbox (Notes/Inbox.md) and return a structured triage proposal: each loose item enumerated with a stable number, ' + 'plus the destination tools to route them (note_*, character_*, arc_*, memory_*, session_focus_*). ' + 'This tool only reads and proposes — it never moves, deletes, or categorizes anything. ' + - 'After the user confirms and items are routed with the destination tools, call inbox_resolve with the item numbers to clear them.', + 'After the user confirms and items are routed with the destination tools, call bindery_inbox_resolve with the item numbers to clear them.', inputSchema: { book: bookSchema }, annotations: { readOnlyHint: true }, }, ({ book }) => { try { return ok(toolInboxProcess(resolveBook(book).root)); } catch (e) { return err(e); } }); -server.registerTool('inbox_resolve', { +server.registerTool('bindery_inbox_resolve', { title: 'Inbox Resolve', description: - 'Remove already-routed items from the notes Inbox (Notes/Inbox.md) by their item numbers, as enumerated by inbox_process. ' + + 'Remove already-routed items from the notes Inbox (Notes/Inbox.md) by their item numbers, as enumerated by bindery_inbox_process. ' + 'Use only after the items have been routed to their destinations and the user has confirmed. ' + - 'Item numbers are stable between inbox_process and inbox_resolve. Other items and the inbox heading/intro are preserved.', + 'Item numbers are stable between bindery_inbox_process and bindery_inbox_resolve. Other items and the inbox heading/intro are preserved.', inputSchema: { book: bookSchema, - items: z.array(z.number().int()).describe('Item numbers to remove, as shown by inbox_process (1-based)'), + items: z.array(z.number().int()).describe('Item numbers to remove, as shown by bindery_inbox_process (1-based)'), }, annotations: { destructiveHint: true }, }, ({ book, items }) => { diff --git a/mcp-ts/test/index-contract.test.ts b/mcp-ts/test/index-contract.test.ts index 7f3057c..f321031 100644 --- a/mcp-ts/test/index-contract.test.ts +++ b/mcp-ts/test/index-contract.test.ts @@ -25,10 +25,10 @@ describe('mcp index contract', () => { const source = fs.readFileSync(path.join(process.cwd(), 'src', 'index.ts'), 'utf-8'); // Find the annotation block for each tool that contacts an external service and verify openWorldHint: true - // index_build / search → Ollama reranking; update_workspace → git fetch/pull; git_snapshot → git push - const externalTools = ['index_build', 'search', 'update_workspace', 'git_snapshot']; + // bindery_index_build / bindery_search → Ollama reranking; bindery_update_workspace → git fetch/pull; bindery_git_snapshot → git push + const externalTools = ['bindery_index_build', 'bindery_search', 'bindery_update_workspace', 'bindery_git_snapshot']; for (const toolName of externalTools) { - // Match registerTool('search', { ... annotations: { ... } ... }) + // Match registerTool('bindery_search', { ... annotations: { ... } ... }) const toolBlockMatch = new RegExp( String.raw`server\.registerTool\('${toolName}',[\s\S]*?annotations:\s*\{([^}]*)\}`, ).exec(source); @@ -42,7 +42,7 @@ describe('mcp index contract', () => { const source = fs.readFileSync(path.join(process.cwd(), 'src', 'index.ts'), 'utf-8'); // Tools that legitimately contact external services (Ollama, git remotes, etc.) - const externalToolNames = new Set(['index_build', 'search', 'update_workspace', 'git_snapshot']); + const externalToolNames = new Set(['bindery_index_build', 'bindery_search', 'bindery_update_workspace', 'bindery_git_snapshot']); // Extract all registerTool blocks and check non-Ollama tools lack openWorldHint const toolBlockRegex = /server\.registerTool\('([^']+)'[\s\S]*?annotations:\s*\{([^}]*)\}/g; diff --git a/mcp-ts/test/integration-stdio.test.ts b/mcp-ts/test/integration-stdio.test.ts index da61d26..860a076 100644 --- a/mcp-ts/test/integration-stdio.test.ts +++ b/mcp-ts/test/integration-stdio.test.ts @@ -170,7 +170,7 @@ describe('MCP stdio integration', { timeout: 20_000 }, () => { ).toBeDefined(); }); - it('list_books returns the configured book', async () => { + it('bindery_list_books returns the configured book', async () => { const root = makeRoot(); const { send, readOne, kill: k } = spawnServer('TestBook', root); kill = k; @@ -181,7 +181,7 @@ describe('MCP stdio integration', { timeout: 20_000 }, () => { jsonrpc: '2.0', id: 2, method: 'tools/call', - params: { name: 'list_books', arguments: {} }, + params: { name: 'bindery_list_books', arguments: {} }, }); const resp = await readOne(); @@ -195,7 +195,7 @@ describe('MCP stdio integration', { timeout: 20_000 }, () => { expect(content[0]?.text).toContain('TestBook'); }); - it('health returns server status for a configured book', async () => { + it('bindery_health returns server status for a configured book', async () => { const root = makeRoot(); const { send, readOne, kill: k } = spawnServer('MyNovel', root); kill = k; @@ -206,7 +206,7 @@ describe('MCP stdio integration', { timeout: 20_000 }, () => { jsonrpc: '2.0', id: 3, method: 'tools/call', - params: { name: 'health', arguments: { book: 'MyNovel' } }, + params: { name: 'bindery_health', arguments: { book: 'MyNovel' } }, }); const resp = await readOne(); @@ -215,20 +215,20 @@ describe('MCP stdio integration', { timeout: 20_000 }, () => { expect(resp.error).toBeUndefined(); const content = (resp.result as { content: Array<{ text: string }> })?.content; - const health = JSON.parse(content[0]?.text ?? '{}') as { + const bindery_health = JSON.parse(content[0]?.text ?? '{}') as { root?: string; settings?: string; ai_version_outdated?: boolean; ai_versions_outdated?: unknown[]; }; - expect(health.root).toBe(root); - expect(typeof health.settings).toBe('string'); - expect(typeof health.ai_version_outdated).toBe('boolean'); - expect(Array.isArray(health.ai_versions_outdated)).toBe(true); + expect(bindery_health.root).toBe(root); + expect(typeof bindery_health.settings).toBe('string'); + expect(typeof bindery_health.ai_version_outdated).toBe('boolean'); + expect(Array.isArray(bindery_health.ai_versions_outdated)).toBe(true); }); - it('get_text blocks a path-traversal attack and returns an error result', async () => { + it('bindery_get_text blocks a path-traversal attack and returns an error result', async () => { const root = makeRoot(); // Create a sentinel file outside the book root to prove isolation const outsideRoot = path.dirname(root); @@ -248,7 +248,7 @@ describe('MCP stdio integration', { timeout: 20_000 }, () => { id: 4, method: 'tools/call', params: { - name: 'get_text', + name: 'bindery_get_text', arguments: { book: 'AttackBook', identifier: `../../${sentinelName}` }, }, }); @@ -278,7 +278,7 @@ describe('MCP stdio integration', { timeout: 20_000 }, () => { jsonrpc: '2.0', id: 5, method: 'tools/call', - params: { name: 'health', arguments: { book: 'NonExistentBook' } }, + params: { name: 'bindery_health', arguments: { book: 'NonExistentBook' } }, }); const resp = await readOne(); @@ -302,7 +302,7 @@ describe('MCP stdio integration', { timeout: 20_000 }, () => { jsonrpc: '2.0', id, method: 'tools/call', - params: { name: 'list_books', arguments: {} }, + params: { name: 'bindery_list_books', arguments: {} }, }); } diff --git a/mcpb/README.md b/mcpb/README.md index 6f8d14b..cff8778 100644 --- a/mcpb/README.md +++ b/mcpb/README.md @@ -12,7 +12,7 @@ Works with any Markdown book project structured with the Bindery VS Code extensi - **Opinionated authoring scaffold** — initialize Arc, Notes, Characters, SESSION, PREFERENCES, and memory files for agent-assisted writing - **Session memory** — append, list, and compact persistent cross-session notes in `.bindery/memories/` - **Session focus** — read and update the ephemeral working-state file `SESSION.md` (current focus, next actions, open questions, handoff); durable preferences stay user-owned in `PREFERENCES.md` -- **Inbox triage** — enumerate loose `Notes/Inbox.md` items and propose destinations (`inbox_process`), then clear routed items after confirmation (`inbox_resolve`) +- **Inbox triage** — enumerate loose `Notes/Inbox.md` items and propose destinations (`bindery_inbox_process`), then clear routed items after confirmation (`bindery_inbox_resolve`) - **Workspace setup** — create or update `.bindery/settings.json`, `.bindery/translations.json`, `.bindery/README.md`, the opinionated authoring scaffold, and AI instruction files - **Typography formatting** — curly quotes, em-dashes, ellipses - **Workspace sync** — fetch and pull the current branch before a session, with branch/default-branch reporting @@ -49,7 +49,7 @@ To install manually without using published Claude Connectors |---------|----------|-------------| | Books | Yes | Semicolon-separated `Name=path` pairs pointing to book projects | | Ollama URL | No | URL for Ollama instance (enables semantic reranking, see [https://docs.ollama.com/quickstart](https://docs.ollama.com/quickstart)) | -| Build full semantic index | No | `true` to let `index_build` precompute embeddings for `full_semantic` search | +| Build full semantic index | No | `true` to let `bindery_index_build` precompute embeddings for `full_semantic` search | | Default search mode | No | `lexical`, `semantic_rerank`, or `full_semantic` | **Books** example: `MyBook=C:\Users\Me\MyBook;MyNovel=D:\Writing\MyNovel` @@ -60,21 +60,21 @@ To install manually without using published Claude Connectors > "Show me the chapter list for my book" -Claude calls `list_books` to discover the book name, then `get_overview` to show +Claude calls `bindery_list_books` to discover the book name, then `bindery_get_overview` to show all acts and chapters with titles. ### Initialize an agent-ready book workspace > "Set this folder up as a Bindery book" -Claude calls `init_workspace`. The tool creates `.bindery/settings.json`, +Claude calls `bindery_init_workspace`. The tool creates `.bindery/settings.json`, `.bindery/translations.json`, the generated `.bindery/README.md` capability reference, and the default authoring scaffold: `SESSION.md`, `PREFERENCES.md`, `Arc/index.md`, `Arc/Overall.md`, `Arc/Acts/`, `Notes/Inbox.md`, `Notes/Characters/index.md`, structured note folders, `.bindery/memories/global.md`, and the remaining scaffold files. Existing files are preserved. -After that, Claude can call `setup_ai_files` to generate CLAUDE.md, +After that, Claude can call `bindery_setup_ai_files` to generate CLAUDE.md, Copilot instructions, Cursor rules, AGENTS.md, and Claude skill templates. ### Answer what Bindery can do @@ -89,15 +89,15 @@ skill workflows, and the current opinionated authoring layout. > "Where does Landa first meet the Keeper?" -Claude calls `search` with the query and returns the most relevant passages with file paths and line numbers, letting you jump straight to the scene. +Claude calls `bindery_search` with the query and returns the most relevant passages with file paths and line numbers, letting you jump straight to the scene. -If `full_semantic` is enabled and the semantic index is stale, Claude should call `index_status`, then recommend `index_build` before relying on semantic results. +If `full_semantic` is enabled and the semantic index is stale, Claude should call `bindery_index_status`, then recommend `bindery_index_build` before relying on semantic results. ### Add a dialect substitution rule > "Add a substitution: 'color' should become 'colour' in en-gb" -Claude calls `add_dialect` with `dialectCode: "en-gb"`, `from: "color"`, +Claude calls `bindery_add_dialect` with `dialectCode: "en-gb"`, `from: "color"`, `to: "colour"`. The rule is saved to `.bindery/translations.json` and applied automatically during future exports. @@ -105,37 +105,37 @@ automatically during future exports. > "Review my changes and save a snapshot if they look good" -Claude calls `get_review_text` to show the diff, reviews it, then calls -`git_snapshot` to commit the changes with a descriptive message. If you have configured a preferred push target, the same call can also push the snapshot. +Claude calls `bindery_get_review_text` to show the diff, reviews it, then calls +`bindery_git_snapshot` to commit the changes with a descriptive message. If you have configured a preferred push target, the same call can also push the snapshot. ### Sync the workspace before reading in > "Update this workspace before we start" -Claude calls `update_workspace` to fetch and pull the current branch. If the current branch differs from the remote default branch, the tool reports that so Claude can ask whether to switch before continuing. +Claude calls `bindery_update_workspace` to fetch and pull the current branch. If the current branch differs from the remote default branch, the tool reports that so Claude can ask whether to switch before continuing. ### Look up a glossary term > "How is 'flux' translated in the Dutch version?" -Claude calls `get_translation` with `language: "nl"` and `word: "flux"`. The +Claude calls `bindery_get_translation` with `language: "nl"` and `word: "flux"`. The lookup is forgiving — it matches case-insensitively and checks plural and inflected forms automatically. If no glossary entry exists yet, Claude can call -`add_translation` with `targetLangCode: "nl"` to create one. +`bindery_add_translation` with `targetLangCode: "nl"` to create one. ### List all dialect substitution rules > "Show me all the British English substitution rules" -Claude calls `get_dialect` with `dialectCode: "en-gb"` (omitting `word`) to dump every `from → to` rule in the `en-gb` entry of `.bindery/translations.json`. +Claude calls `bindery_get_dialect` with `dialectCode: "en-gb"` (omitting `word`) to dump every `from → to` rule in the `en-gb` entry of `.bindery/translations.json`. Useful before an export session to see what spelling substitutions are configured. ### Save session decisions to memory > "Save today's character decisions to memory" -Claude calls `memory_list` to check which files already exist and their sizes, -then calls `memory_append` with `file: "global.md"`, a short title, and the +Claude calls `bindery_memory_list` to check which files already exist and their sizes, +then calls `bindery_memory_append` with `file: "global.md"`, a short title, and the decisions to record. The tool stamps the current date automatically — no manual date formatting needed. @@ -143,13 +143,13 @@ date formatting needed. > "Add this world detail to the notes" -Claude calls `note_list` or `note_get` to find the right target under the configured notes folder, then uses `note_create` for a new note or `note_append` for an existing one. Older note layouts remain readable through `get_notes`. +Claude calls `bindery_note_list` or `bindery_note_get` to find the right target under the configured notes folder, then uses `bindery_note_create` for a new note or `bindery_note_append` for an existing one. Older note layouts remain readable through `bindery_get_notes`. ### Compact a memory file that has grown too large > "The global memory file is getting long — please compact it" -Claude reads the current content, summarizes it, then calls `memory_compact` +Claude reads the current content, summarizes it, then calls `bindery_memory_compact` with the compacted text. The original is automatically backed up to `.bindery/memories/archive/global_YYYY-MM-DD.md` before the file is overwritten. @@ -157,17 +157,17 @@ with the compacted text. The original is automatically backed up to > "Compare chapter 10 in EN and NL and flag any translation issues" -Claude calls `get_chapter` twice — once for EN, once for NL — then calls -`get_translation` with the target language to load the known term table. +Claude calls `bindery_get_chapter` twice — once for EN, once for NL — then calls +`bindery_get_translation` with the target language to load the known term table. Discrepancies are presented in a side-by-side table. Any confirmed corrections -are saved back with `add_translation` (glossary) or `add_dialect` (dialect substitutions). +are saved back with `bindery_add_translation` (glossary) or `bindery_add_dialect` (dialect substitutions). ### Check continuity across chapters > "Check chapter 8 for consistency errors — character descriptions and world rules" -Claude calls `get_book_until` to load prior chapters through the focus chapter, -uses `get_notes` to load character profiles and world rules, then uses `search` +Claude calls `bindery_get_book_until` to load prior chapters through the focus chapter, +uses `bindery_get_notes` to load character profiles and world rules, then uses `bindery_search` to verify specific details against earlier chapters. Results are presented in a table with issue type, location, and the reference that contradicts it. @@ -175,50 +175,50 @@ table with issue type, location, and the reference that contradicts it. | Tool | What it does | |---|---| -| `list_books` | List all configured book names | -| `identify_book` | Match a working directory to a book name | -| `health` | Server status: settings, index, embedding backend | -| `init_workspace` | Create or update `.bindery/settings.json`, `translations.json`, generated `.bindery/README.md`, and the opinionated Arc / Notes / Characters / SESSION / PREFERENCES / memory scaffold | -| `setup_ai_files` | Generate AI instruction files (CLAUDE.md, copilot-instructions.md, .cursor/rules, AGENTS.md), Claude skill templates, and refresh generated `.bindery/README.md` | -| `index_build` | Build or rebuild the lexical index and, when enabled, the semantic embedding index | -| `index_status` | Show lexical and semantic index status, build times, and stale hints | -| `get_text` | Read any file by relative path, with optional line range | -| `get_chapter` | Full chapter content by number and language | -| `get_book_until` | Chapters from a start chapter through N (inclusive), concatenated in order | -| `get_overview` | Chapter structure — acts, chapters, titles | -| `get_notes` | Notes/ files, filterable by category or name | -| `note_list` | List story note files under the configured notes folder | -| `note_get` | Read a single story note by path relative to the notes folder | -| `note_create` | Create a story note under the configured notes folder | -| `note_append` | Append markdown content to a story note, creating it if needed | -| `character_list` | List structured character profile files | -| `character_get` | Read a structured character profile by name | -| `character_create` | Create a character profile and update the character index | -| `character_update` | Update a character profile and refresh the character index row | -| `arc_list` | List structured arc files | -| `arc_get` | Read a structured arc file | -| `arc_create` | Create an arc file and update the arc index | -| `arc_update` | Update an arc file and refresh the arc index | -| `search` | Search in lexical, semantic-rerank, or full-semantic mode; semantic modes fall back to lexical with warnings | -| `format` | Apply typography formatting to a file or folder | -| `get_review_text` | Git diff of uncommitted changes plus any `` marker regions; optional auto-staging consumes the markers | -| `update_workspace` | Fetch and pull the current branch, with branch/default-branch reporting | -| `git_snapshot` | Commit changes in bindery workspace, with optional push | -| `get_translation` | List glossary entries for a language, or look up a specific term (forgiving) | -| `add_translation` | Add or update a cross-language glossary entry (agent reference) | -| `get_dialect` | List dialect substitution rules, or look up a specific word | -| `add_dialect` | Add or update a dialect substitution rule (auto-applied at export) | -| `add_language` | Add a language to settings.json and scaffold the story folder with stubs | -| `settings_update` | Deep-merge a partial patch into settings.json without replacing unrelated keys | -| `memory_list` | List memory files with line counts | -| `memory_append` | Append a dated session entry to a memory file | -| `memory_compact` | Overwrite a memory file with a summary (backs up original) | -| `session_focus_get` | Read working state from `SESSION.md` (optionally a single section: Current Focus, Next Actions, Open Questions, Handoff Notes) | -| `session_focus_update` | Update neutral `SESSION.md` sections (replace or append); leaves `PREFERENCES.md` and other content untouched | -| `inbox_process` | Enumerate `Notes/Inbox.md` items with stable numbers and propose destinations — read-only, never moves or categorizes | -| `inbox_resolve` | Remove already-routed inbox items by number after confirmation; preserves other items and the heading/intro | +| `bindery_list_books` | List all configured book names | +| `bindery_identify_book` | Match a working directory to a book name | +| `bindery_health` | Server status: settings, index, embedding backend | +| `bindery_init_workspace` | Create or update `.bindery/settings.json`, `translations.json`, generated `.bindery/README.md`, and the opinionated Arc / Notes / Characters / SESSION / PREFERENCES / memory scaffold | +| `bindery_setup_ai_files` | Generate AI instruction files (CLAUDE.md, copilot-instructions.md, .cursor/rules, AGENTS.md), Claude skill templates, and refresh generated `.bindery/README.md` | +| `bindery_index_build` | Build or rebuild the lexical index and, when enabled, the semantic embedding index | +| `bindery_index_status` | Show lexical and semantic index status, build times, and stale hints | +| `bindery_get_text` | Read any file by relative path, with optional line range | +| `bindery_get_chapter` | Full chapter content by number and language | +| `bindery_get_book_until` | Chapters from a start chapter through N (inclusive), concatenated in order | +| `bindery_get_overview` | Chapter structure — acts, chapters, titles | +| `bindery_get_notes` | Notes/ files, filterable by category or name | +| `bindery_note_list` | List story note files under the configured notes folder | +| `bindery_note_get` | Read a single story note by path relative to the notes folder | +| `bindery_note_create` | Create a story note under the configured notes folder | +| `bindery_note_append` | Append markdown content to a story note, creating it if needed | +| `bindery_character_list` | List structured character profile files | +| `bindery_character_get` | Read a structured character profile by name | +| `bindery_character_create` | Create a character profile and update the character index | +| `bindery_character_update` | Update a character profile and refresh the character index row | +| `bindery_arc_list` | List structured arc files | +| `bindery_arc_get` | Read a structured arc file | +| `bindery_arc_create` | Create an arc file and update the arc index | +| `bindery_arc_update` | Update an arc file and refresh the arc index | +| `bindery_search` | Search in lexical, semantic-rerank, or full-semantic mode; semantic modes fall back to lexical with warnings | +| `bindery_format` | Apply typography formatting to a file or folder | +| `bindery_get_review_text` | Git diff of uncommitted changes plus any `` marker regions; optional auto-staging consumes the markers | +| `bindery_update_workspace` | Fetch and pull the current branch, with branch/default-branch reporting | +| `bindery_git_snapshot` | Commit changes in bindery workspace, with optional push | +| `bindery_get_translation` | List glossary entries for a language, or look up a specific term (forgiving) | +| `bindery_add_translation` | Add or update a cross-language glossary entry (agent reference) | +| `bindery_get_dialect` | List dialect substitution rules, or look up a specific word | +| `bindery_add_dialect` | Add or update a dialect substitution rule (auto-applied at export) | +| `bindery_add_language` | Add a language to settings.json and scaffold the story folder with stubs | +| `bindery_settings_update` | Deep-merge a partial patch into settings.json without replacing unrelated keys | +| `bindery_memory_list` | List memory files with line counts | +| `bindery_memory_append` | Append a dated session entry to a memory file | +| `bindery_memory_compact` | Overwrite a memory file with a summary (backs up original) | +| `bindery_session_focus_get` | Read working state from `SESSION.md` (optionally a single section: Current Focus, Next Actions, Open Questions, Handoff Notes) | +| `bindery_session_focus_update` | Update neutral `SESSION.md` sections (replace or append); leaves `PREFERENCES.md` and other content untouched | +| `bindery_inbox_process` | Enumerate `Notes/Inbox.md` items with stable numbers and propose destinations — read-only, never moves or categorizes | +| `bindery_inbox_resolve` | Remove already-routed inbox items by number after confirmation; preserves other items and the heading/intro | -Current boundary: Arc, character, note, memory, session-focus, and inbox-triage workflows are available through MCP tools, with matching VS Code and Obsidian host command wrappers. `session_focus_update` touches only the neutral `SESSION.md` sections; `PREFERENCES.md` is user-owned and never tool-written. `inbox_process` only proposes and `inbox_resolve` only removes named items — route confirmed items with the destination tools first. +Current boundary: Arc, character, note, memory, session-focus, and inbox-triage workflows are available through MCP tools, with matching VS Code and Obsidian host command wrappers. `bindery_session_focus_update` touches only the neutral `SESSION.md` sections; `PREFERENCES.md` is user-owned and never tool-written. `bindery_inbox_process` only proposes and `bindery_inbox_resolve` only removes named items — route confirmed items with the destination tools first. ## Privacy Policy diff --git a/mcpb/manifest.json b/mcpb/manifest.json index 68a2dfe..54f2466 100644 --- a/mcpb/manifest.json +++ b/mcpb/manifest.json @@ -58,47 +58,47 @@ } }, "tools": [ - { "name": "list_books", "description": "List all books configured in the MCP server. Call this first to discover available book names." }, - { "name": "identify_book", "description": "Identify which book matches a working directory. Pass your cwd and the server matches by folder name or .bindery/settings.json." }, - { "name": "health", "description": "Check server status: active book, settings, search index, and embedding backend." }, - { "name": "init_workspace", "description": "Create or update .bindery/settings.json, translations.json, .bindery/README.md, and the opinionated Arc, Notes, Characters, SESSION, PREFERENCES, and memory scaffold. All arguments optional — smart defaults used if omitted. Detects language folders automatically. Safe to run on an existing workspace." }, - { "name": "setup_ai_files", "description": "Generate AI assistant instruction files (CLAUDE.md, copilot-instructions.md, .cursor/rules, AGENTS.md), Claude skill templates, and the generated .bindery/README.md capability reference from settings. Run init_workspace first. Skips existing files unless overwrite: true." }, - { "name": "index_build", "description": "Build or rebuild the lexical search index and, when enabled, the semantic embedding index for a book." }, - { "name": "index_status", "description": "Show lexical and semantic index metadata, including stale-status hints." }, - { "name": "search", "description": "Search using lexical BM25, semantic reranking, or full semantic mode. Semantic modes fall back to lexical with a warning if Ollama or the semantic index is unavailable." }, - { "name": "get_text", "description": "Read a source file by relative path, optionally restricted to a line range." }, - { "name": "get_chapter", "description": "Fetch the full content of a chapter by number and language code (e.g. EN or NL)." }, - { "name": "get_book_until", "description": "Fetch chapters from a starting chapter through a target chapter (inclusive), concatenated in reading order." }, - { "name": "get_overview", "description": "List the chapter structure (acts, chapters, titles) for one or all languages." }, - { "name": "get_notes", "description": "Read from Notes/, optionally filtered by category or character name." }, - { "name": "note_list", "description": "List markdown note files under the configured notes folder, optionally filtered to a category folder." }, - { "name": "note_get", "description": "Read a single markdown note by path relative to the configured notes folder." }, - { "name": "note_create", "description": "Create a markdown note under the configured notes folder. Refuses to overwrite unless overwrite is true." }, - { "name": "note_append", "description": "Append markdown content to a note under the configured notes folder, creating the file if needed." }, - { "name": "character_list", "description": "List structured character profile files under the configured characters folder." }, - { "name": "character_get", "description": "Read a structured character profile by character name." }, - { "name": "character_create", "description": "Create a structured character profile and update the character index." }, - { "name": "character_update", "description": "Update known fields in a structured character profile and refresh the character index row." }, - { "name": "arc_list", "description": "List structured arc files under the configured arc folder." }, - { "name": "arc_get", "description": "Read a structured arc file by path relative to the configured arc folder." }, - { "name": "arc_create", "description": "Create a structured arc file under the configured arc folder and update the arc index." }, - { "name": "arc_update", "description": "Update known fields in a structured arc file and refresh the arc index." }, - { "name": "format", "description": "Apply typography formatting (curly quotes, em-dashes, ellipses) to a file or folder." }, - { "name": "get_review_text", "description": "Structured review payload: git diff of uncommitted changes plus any regions wrapped in markers (works even on committed work). Filter by language (EN, NL, ALL). autoStage stages reviewed files and consumes review markers." }, - { "name": "update_workspace", "description": "Fetch and pull the current git branch, report the current branch versus the remote default branch, and optionally switch branches before pulling." }, - { "name": "git_snapshot", "description": "Save a snapshot (git commit) of all changes in the bindery workspace. Supports an optional commit message, optional push to a remote branch, and remembering push defaults in settings.json." }, - { "name": "add_translation", "description": "Add or update a cross-language glossary entry in .bindery/translations.json for agent reference (source → target term). For dialect substitution rules use add_dialect." }, - { "name": "get_translation", "description": "Look up cross-language glossary entries from .bindery/translations.json. List all entries for a language or do a forgiving word lookup. For dialect substitution rules use get_dialect." }, - { "name": "add_dialect", "description": "Add or update a dialect substitution rule in .bindery/translations.json (e.g. US→UK spelling: color→colour). Applied automatically during export." }, - { "name": "get_dialect", "description": "Look up dialect substitution rules from .bindery/translations.json. List all rules for a dialect or look up a specific word." }, - { "name": "add_language", "description": "Add a new language to .bindery/settings.json and scaffold its story folder with stub files mirroring the default language structure." }, - { "name": "settings_update", "description": "Merge a partial patch into .bindery/settings.json without replacing unrelated keys." }, - { "name": "memory_list", "description": "List all session memory files in .bindery/memories/ with their line counts." }, - { "name": "memory_append", "description": "Append a dated session entry to a memory file in .bindery/memories/. Supply a short title and content; the tool stamps the date." }, - { "name": "memory_compact", "description": "Overwrite a memory file with a compacted summary. The original is backed up to .bindery/memories/archive/ first." }, - { "name": "session_focus_get", "description": "Read the ephemeral session file (default SESSION.md) holding current working state. Optionally pass a section name (Current Focus, Next Actions, Open Questions, Handoff Notes) to read just that section. Durable preferences live in PREFERENCES.md and durable decisions in .bindery/memories/. Returns a clear empty-state message if the session file does not exist yet." }, - { "name": "session_focus_update", "description": "Update neutral sections of the ephemeral session file (default SESSION.md): Current Focus, Next Actions, Open Questions, Handoff Notes. Only the sections you pass change; other content and the user-owned PREFERENCES.md are preserved. mode 'replace' (default) overwrites a section body; 'append' adds beneath existing content. Creates the file from the standard scaffold if missing." }, - { "name": "inbox_process", "description": "Read the notes Inbox (Notes/Inbox.md) and return a structured triage proposal: each loose item enumerated with a stable number, plus destination tools (note_*, character_*, arc_*, memory_*, session_focus_*). Read-only — never moves, deletes, or categorizes. After items are routed and confirmed, call inbox_resolve to clear them." }, - { "name": "inbox_resolve", "description": "Remove already-routed items from the notes Inbox (Notes/Inbox.md) by their item numbers, as enumerated by inbox_process. Use only after items are routed and the user has confirmed. Item numbers are stable between inbox_process and inbox_resolve; other items and the heading/intro are preserved." } + { "name": "bindery_list_books", "description": "List all books configured in the MCP server. Call this first to discover available book names." }, + { "name": "bindery_identify_book", "description": "Identify which book matches a working directory. Pass your cwd and the server matches by folder name or .bindery/settings.json." }, + { "name": "bindery_health", "description": "Check server status: active book, settings, search index, and embedding backend." }, + { "name": "bindery_init_workspace", "description": "Create or update .bindery/settings.json, translations.json, .bindery/README.md, and the opinionated Arc, Notes, Characters, SESSION, PREFERENCES, and memory scaffold. All arguments optional — smart defaults used if omitted. Detects language folders automatically. Safe to run on an existing workspace." }, + { "name": "bindery_setup_ai_files", "description": "Generate AI assistant instruction files (CLAUDE.md, copilot-instructions.md, .cursor/rules, AGENTS.md), Claude skill templates, and the generated .bindery/README.md capability reference from settings. Run bindery_init_workspace first. Skips existing files unless overwrite: true." }, + { "name": "bindery_index_build", "description": "Build or rebuild the lexical search index and, when enabled, the semantic embedding index for a book." }, + { "name": "bindery_index_status", "description": "Show lexical and semantic index metadata, including stale-status hints." }, + { "name": "bindery_search", "description": "Search using lexical BM25, semantic reranking, or full semantic mode. Semantic modes fall back to lexical with a warning if Ollama or the semantic index is unavailable." }, + { "name": "bindery_get_text", "description": "Read a source file by relative path, optionally restricted to a line range." }, + { "name": "bindery_get_chapter", "description": "Fetch the full content of a chapter by number and language code (e.g. EN or NL)." }, + { "name": "bindery_get_book_until", "description": "Fetch chapters from a starting chapter through a target chapter (inclusive), concatenated in reading order." }, + { "name": "bindery_get_overview", "description": "List the chapter structure (acts, chapters, titles) for one or all languages." }, + { "name": "bindery_get_notes", "description": "Read from Notes/, optionally filtered by category or character name." }, + { "name": "bindery_note_list", "description": "List markdown note files under the configured notes folder, optionally filtered to a category folder." }, + { "name": "bindery_note_get", "description": "Read a single markdown note by path relative to the configured notes folder." }, + { "name": "bindery_note_create", "description": "Create a markdown note under the configured notes folder. Refuses to overwrite unless overwrite is true." }, + { "name": "bindery_note_append", "description": "Append markdown content to a note under the configured notes folder, creating the file if needed." }, + { "name": "bindery_character_list", "description": "List structured character profile files under the configured characters folder." }, + { "name": "bindery_character_get", "description": "Read a structured character profile by character name." }, + { "name": "bindery_character_create", "description": "Create a structured character profile and update the character index." }, + { "name": "bindery_character_update", "description": "Update known fields in a structured character profile and refresh the character index row." }, + { "name": "bindery_arc_list", "description": "List structured arc files under the configured arc folder." }, + { "name": "bindery_arc_get", "description": "Read a structured arc file by path relative to the configured arc folder." }, + { "name": "bindery_arc_create", "description": "Create a structured arc file under the configured arc folder and update the arc index." }, + { "name": "bindery_arc_update", "description": "Update known fields in a structured arc file and refresh the arc index." }, + { "name": "bindery_format", "description": "Apply typography formatting (curly quotes, em-dashes, ellipses) to a file or folder." }, + { "name": "bindery_get_review_text", "description": "Structured review payload: git diff of uncommitted changes plus any regions wrapped in markers (works even on committed work). Filter by language (EN, NL, ALL). autoStage stages reviewed files and consumes review markers." }, + { "name": "bindery_update_workspace", "description": "Fetch and pull the current git branch, report the current branch versus the remote default branch, and optionally switch branches before pulling." }, + { "name": "bindery_git_snapshot", "description": "Save a snapshot (git commit) of all changes in the bindery workspace. Supports an optional commit message, optional push to a remote branch, and remembering push defaults in settings.json." }, + { "name": "bindery_add_translation", "description": "Add or update a cross-language glossary entry in .bindery/translations.json for agent reference (source → target term). For dialect substitution rules use bindery_add_dialect." }, + { "name": "bindery_get_translation", "description": "Look up cross-language glossary entries from .bindery/translations.json. List all entries for a language or do a forgiving word lookup. For dialect substitution rules use bindery_get_dialect." }, + { "name": "bindery_add_dialect", "description": "Add or update a dialect substitution rule in .bindery/translations.json (e.g. US→UK spelling: color→colour). Applied automatically during export." }, + { "name": "bindery_get_dialect", "description": "Look up dialect substitution rules from .bindery/translations.json. List all rules for a dialect or look up a specific word." }, + { "name": "bindery_add_language", "description": "Add a new language to .bindery/settings.json and scaffold its story folder with stub files mirroring the default language structure." }, + { "name": "bindery_settings_update", "description": "Merge a partial patch into .bindery/settings.json without replacing unrelated keys." }, + { "name": "bindery_memory_list", "description": "List all session memory files in .bindery/memories/ with their line counts." }, + { "name": "bindery_memory_append", "description": "Append a dated session entry to a memory file in .bindery/memories/. Supply a short title and content; the tool stamps the date." }, + { "name": "bindery_memory_compact", "description": "Overwrite a memory file with a compacted summary. The original is backed up to .bindery/memories/archive/ first." }, + { "name": "bindery_session_focus_get", "description": "Read the ephemeral session file (default SESSION.md) holding current working state. Optionally pass a section name (Current Focus, Next Actions, Open Questions, Handoff Notes) to read just that section. Durable preferences live in PREFERENCES.md and durable decisions in .bindery/memories/. Returns a clear empty-state message if the session file does not exist yet." }, + { "name": "bindery_session_focus_update", "description": "Update neutral sections of the ephemeral session file (default SESSION.md): Current Focus, Next Actions, Open Questions, Handoff Notes. Only the sections you pass change; other content and the user-owned PREFERENCES.md are preserved. mode 'replace' (default) overwrites a section body; 'append' adds beneath existing content. Creates the file from the standard scaffold if missing." }, + { "name": "bindery_inbox_process", "description": "Read the notes Inbox (Notes/Inbox.md) and return a structured triage proposal: each loose item enumerated with a stable number, plus destination tools (note_*, character_*, arc_*, memory_*, session_focus_*). Read-only — never moves, deletes, or categorizes. After items are routed and confirmed, call bindery_inbox_resolve to clear them." }, + { "name": "bindery_inbox_resolve", "description": "Remove already-routed items from the notes Inbox (Notes/Inbox.md) by their item numbers, as enumerated by bindery_inbox_process. Use only after items are routed and the user has confirmed. Item numbers are stable between bindery_inbox_process and bindery_inbox_resolve; other items and the heading/intro are preserved." } ] } diff --git a/scripts/check-tool-parity.mjs b/scripts/check-tool-parity.mjs index 5d300f7..45082d4 100644 --- a/scripts/check-tool-parity.mjs +++ b/scripts/check-tool-parity.mjs @@ -4,10 +4,10 @@ * * Verifies every tool name appears in all 5 required surfaces: * 1. mcp-ts/src/tools.ts — implementation function `toolXxx` - * 2. mcp-ts/src/index.ts — server.registerTool('xxx', ...) + * 2. mcp-ts/src/index.ts — server.registerTool('bindery_xxx', ...) * 3. vscode-ext/src/mcp.ts — vscode.lm.registerTool('bindery_xxx', ...) * 4. vscode-ext/package.json — languageModelTools[].name = 'bindery_xxx' - * 5. mcpb/manifest.json — tools[].name = 'xxx' (no prefix) + * 5. mcpb/manifest.json — tools[].name = 'bindery_xxx' * * Source of truth: the set of tool names in mcp-ts/src/index.ts. * Exits with code 1 if any surface diverges. @@ -31,7 +31,7 @@ function extractMatches(source, regex) { return names; } -// ── Surface 1: mcp-ts/src/index.ts — registerTool('xxx', ...) (no prefix in server) +// ── Surface 1: mcp-ts/src/index.ts — registerTool('bindery_xxx', ...) const indexTs = read('mcp-ts/src/index.ts'); const sourceOfTruth = extractMatches(indexTs, /registerTool\s*\(\s*['"]([a-z0-9_]+)['"]/gi); @@ -41,22 +41,22 @@ const sourceOfTruth = extractMatches(indexTs, /registerTool\s*\(\s*['"]([a-z0-9_ * workspace), so these aren't exposed via vscode.lm.registerTool or listed * in languageModelTools[]. They must still appear in mcpb/manifest.json. */ -const serverOnlyTools = new Set(['list_books', 'identify_book']); +const serverOnlyTools = new Set(['bindery_list_books', 'bindery_identify_book']); const vscodeExpected = new Set([...sourceOfTruth].filter(t => !serverOnlyTools.has(t))); // ── Surface 2: vscode-ext/src/mcp.ts — vscode.lm.registerTool('bindery_xxx', ...) const mcpTs = read('vscode-ext/src/mcp.ts'); -const vscodeLmTools = extractMatches(mcpTs, /registerTool\s*(?:<[^>]+>)?\s*\(\s*['"]bindery_([a-z0-9_]+)['"]/gi); +const vscodeLmTools = extractMatches(mcpTs, /registerTool\s*(?:<[^>]+>)?\s*\(\s*['"](bindery_[a-z0-9_]+)['"]/gi); // ── Surface 3: vscode-ext/package.json — languageModelTools[].name const vsPkg = JSON.parse(read('vscode-ext/package.json')); const lmManifest = new Set( (vsPkg.contributes?.languageModelTools ?? []) - .map((t) => (t.name || '').replace(/^bindery_/, '')) + .map((t) => t.name || '') .filter(Boolean) ); -// ── Surface 4: mcpb/manifest.json — tools[].name (no prefix) +// ── Surface 4: mcpb/manifest.json — tools[].name (bindery_xxx) const mcpbManifest = JSON.parse(read('mcpb/manifest.json')); const mcpbTools = new Set((mcpbManifest.tools ?? []).map((t) => t.name).filter(Boolean)); diff --git a/vscode-ext/README.md b/vscode-ext/README.md index 51dfd1f..050af8e 100644 --- a/vscode-ext/README.md +++ b/vscode-ext/README.md @@ -60,7 +60,7 @@ Bindery includes a bundled MCP server that makes your book's chapters, arc files **Claude for VS Code / Codex** — run `Bindery: Register MCP Server` once per workspace. This writes `.vscode/mcp.json` pointing to the bundled server. Both extensions pick this up automatically on next reload. -#### Available MCP tools (38) +#### Available MCP tools | Tool | Description | |------|-------------| @@ -107,7 +107,7 @@ Bindery includes a bundled MCP server that makes your book's chapters, arc files `bindery_search` supports `lexical`, `semantic_rerank`, and `full_semantic` modes. Semantic modes require an Ollama instance and fall back to lexical results with a warning if unavailable. -For the standalone MCP server (Claude Desktop / Cowork), two additional tools are available: `list_books` and `identify_book` for multi-book discovery. See [mcpb/README.md](../mcpb/README.md) for full MCP documentation and usage examples. +For the standalone MCP server (Claude Desktop / Cowork), two additional tools are available: `bindery_list_books` and `bindery_identify_book` for multi-book discovery. See [mcpb/README.md](../mcpb/README.md) for full MCP documentation and usage examples. These are agent-facing MCP / language-model tools. The Command Palette also exposes host commands for the same structured note, character, arc, memory, and session-focus workflows. Host prompts cover common fields; agents can call the MCP/LM tools directly for complete structured payloads.