CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m2s
CI & Build / Python tests (push) Failing after 1m26s
CI & Build / Build & push image (push) Skipped
Step 3 of the Scribe -> Grimoire rename. - Plugin `grimoire` in marketplace `grimoire-plugin`, MCP server key `grimoire`, so tools reach a session as mcp__plugin_grimoire_grimoire__*. - hooks/scribe_* -> hooks/grimoire_*, their shell functions and the TMPDIR ledgers with them; SCRIBE_URL / SCRIBE_TOKEN / SCRIBE_HOOK_DIR / SCRIBE_LEDGER_DIRS -> GRIMOIRE_*; the project marker is `.grimoire`. - skills/using-scribe -> using-grimoire, /grimoire:sync, stored processes arrive as grimoire-proc-<slug>; moment_actions names them the same way. - Migration 0124 rewrites stored skill names (rule_moments, rule_moment_judgments, moment_mappings) from scribe:/scribe-proc- to grimoire:/grimoire-proc-, so a rule mounted on a skill keeps firing. - Settings' install commands and MCP-only defaults, the docs and the tests follow. - scripts/check_plugin.py drops the smoke event for a report-check hook that has no file in plugin/hooks. Kept on purpose: the secret-sealing label, and the names that let backups written as Scribe restore and be counted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
7.9 KiB
Markdown
71 lines
7.9 KiB
Markdown
# Packaging Grimoire for an agent client
|
|
|
|
This is the maintainer contract for shipping Grimoire into an agent client:
|
|
what every client package shares, what each one adds, and where a new client's
|
|
files go. One principle governs it: **the server orients,
|
|
the skills hold the depth, and a client adapter only times delivery and says
|
|
what that one client needs said.**
|
|
|
|
Claude Code is the only client built today. The layout below is kept so adding
|
|
another one means adding files, not moving or rewriting any.
|
|
|
|
## Shared by every client package
|
|
|
|
| Piece | Where | What it is |
|
|
|---|---|---|
|
|
| **The skills** | `plugin/skills/*/SKILL.md` | Agent Skills (the open SKILL.md format). They state every Grimoire reflex in full and name no client. `tests/test_guidance_ownership.py` fails if a skill names a particular client, or references anything outside its own folder. Every client package ships this folder verbatim. |
|
|
| **The MCP server** | `<base URL>/mcp` | HTTP, `Authorization: Bearer <fmcp_ key>`. Its `_INSTRUCTIONS` is a client-neutral orientation — the workflow across tools (≤1,600 chars); each tool's description carries its contract; in-band responses (`placement`, `report_back`, `systems_hint`, the duplicate gate, the guessed-id refusal) fire in every client. |
|
|
| **The adapter API** | `<base URL>/api/plugin/*` | Endpoints any client's hooks can call with the same key. `GET`, so read scope is enough: `context` (live session state), `retrieve` (rules, preferences and notes for a message), `prior-art` (records and shape-ledger hints for code being written), `tool-rules` (rules for a command about to run), `moment-tools` (which tools reach a mounted moment), `shape-check` (definitions written this turn that nobody has judged), `claim-session` / `release-session` (task claims), `processes` (stored Processes to expose as skills). `POST`, so write scope: `moment` (rules mounted on the moments a tool call reaches), `rule-opened` (a rule was read), `reply-rules` (the finished reply, held for an unopened rule or — when the turn closed a task — for missing completion sections). |
|
|
| **The API key** | Grimoire → Settings → MCP Access | One `fmcp_` key per install. Write scope covers the MCP tools and every hook; a read key runs only the `GET` hooks. |
|
|
|
|
## Added by each client
|
|
|
|
| Piece | What it holds | Rule of thumb |
|
|
|---|---|---|
|
|
| **A manifest** | Name, version, how to reach the MCP server with the key, where the skills and hooks are | Only that client's format. |
|
|
| **Hooks** | Scripts in that client's hook format that call the adapter API at the right moment and print what comes back | Timing and transport only. A hook may print status lines (unconfigured, unreachable, running version) and pointers; guidance text comes from the server or the adapter's own static text. |
|
|
| **Commands** | That client's command wrappers, e.g. a process sync | Optional. |
|
|
| **Adapter static text** | What only that client needs said: its local memory files, its compaction command, its commands | Never a copy of a skill or of `_INSTRUCTIONS`. The loss guard and ownership registry in `tests/test_guidance_ownership.py` cover it. |
|
|
|
|
## The worked example: Claude Code
|
|
|
|
| File | Role |
|
|
|---|---|
|
|
| `.claude-plugin/plugin.json` | Manifest: `mcpServers.grimoire` (HTTP, `Authorization: Bearer ${user_config.api_token}`), `userConfig` for the base URL and key, version (minted, never hand-edited; see README). |
|
|
| `../.claude-plugin/marketplace.json` (repo root) | The marketplace entry pointing at `./plugin`. |
|
|
| `hooks/hooks.json` | Wires the scripts to Claude Code events. |
|
|
| `hooks/grimoire_session_context.sh` | SessionStart: adapter static text + running version + `GET /api/plugin/context`; a reload banner after compaction. |
|
|
| `hooks/grimoire_autoinject.sh` | UserPromptSubmit: `GET /api/plugin/retrieve` for the message. |
|
|
| `hooks/grimoire_prior_art.sh` | PreToolUse on editor writes: `GET /api/plugin/prior-art`. |
|
|
| `hooks/grimoire_after_write.sh` | PostToolUse on shell commands: the same check for code written through the shell. |
|
|
| `hooks/grimoire_tool_rules.sh` | PreToolUse on shell commands: `GET /api/plugin/tool-rules`. |
|
|
| `hooks/grimoire_moment.sh` | PreToolUse on every tool: the rules mounted on the moments the call reaches, `POST /api/plugin/moment`; skips tools `GET /api/plugin/moment-tools` says reach nothing mounted, and Grimoire's own (their responses carry `moment_rules`). |
|
|
| `hooks/grimoire_reply_check.sh` | Stop: sends the finished reply to `POST /api/plugin/reply-rules` — the rules mounted on the reply moments, and the reply against every rule's trigger, and — when the turn closed a task — the completion-section check; holds once per rule, with the reason the server returns, and never holds the rewrite. |
|
|
| `hooks/grimoire_shape_check.sh` | Stop: sends the definitions the turn wrote (the write hooks' `<sid>.written.ids` ledger) to `GET /api/plugin/shape-check`; blocks once, with the reason the server returns, so the agent judges what it built. |
|
|
| `hooks/grimoire_record_opened.sh` | PostToolUse on `get_rule`: notes the rule as read in the session ledger and `POST /api/plugin/rule-opened`. |
|
|
| `hooks/grimoire_record_outcome.sh` | PostToolUse on `rule_outcome`: notes the rule as acted on, locally, for the session-scoped readout. |
|
|
| `hooks/grimoire_claim_session.sh` | PostToolUse on `update_task` / `add_task_log`: `GET /api/plugin/claim-session`, so the task comes back after a compaction. |
|
|
| `hooks/grimoire_precompact_preserve.sh` | PreCompact: tells the summarizer what to keep literally. Local, and never blocks. |
|
|
| `hooks/grimoire_session_end.sh` | SessionEnd: `GET /api/plugin/release-session` releases the session's claims. |
|
|
| `hooks/grimoire_sync_processes.sh` + `commands/sync.md` | `GET /api/plugin/processes` → `~/.claude/skills/grimoire-proc-*` stubs; `/grimoire:sync` on demand. |
|
|
| `hooks/grimoire_defs.sh` | Shared shell helpers: config, dedup ledgers, outage line. |
|
|
| `hooks/grimoire_static_context.md` | The adapter static text. |
|
|
|
|
Hook config arrives as `CLAUDE_PLUGIN_OPTION_API_ENDPOINT` / `CLAUDE_PLUGIN_OPTION_API_TOKEN` (uppercased by Claude Code), with `GRIMOIRE_URL` / `GRIMOIRE_TOKEN` as an override.
|
|
|
|
## How other clients would map
|
|
|
|
**Researched, not tested.** Re-check each client's current docs before building.
|
|
|
|
- **Agent Plugins 1.0** (Codex, Cursor, GitHub Copilot / VS Code, Kiro, ChatGPT): a root `plugin.json`, a `skills/` folder, and an `mcp.json`. That is the same `plugin/skills/` plus two small files. Hooks, commands and rules are not part of v1; each client adds its own under a reverse-domain directory (e.g. `com.<client>/`) that other clients ignore.
|
|
- **Gemini CLI**: `gemini-extension.json` configuring the MCP server, a context file, and bundled skills.
|
|
- **Client-native formats also exist** (`.codex-plugin/`, `.cursor-plugin/`) where a client wants more than the shared standard carries.
|
|
- **Claude Code** does not read Agent Plugins: only `.claude-plugin/plugin.json`.
|
|
|
|
## Open questions for whoever builds the second package
|
|
|
|
1. **Passing the key to the MCP server.** Claude Code substitutes `${user_config.api_token}` into the header. Agent Plugins' `mcp.json` and Gemini's extension config have their own variable and secret handling. Decide per client, and keep the key out of files that get committed.
|
|
2. **Coexistence in one folder.** Whether a root `plugin.json` (Agent Plugins) beside `.claude-plugin/plugin.json` changes how Claude Code loads the plugin is untested. Test it before shipping both from `plugin/`, or give the second client its own package directory that reuses `plugin/skills/`.
|
|
3. **Hook parity.** Other clients' hook events differ and are partly implemented. Map each Grimoire hook to the nearest event, and where none exists, leave that timing to the in-band server responses rather than writing a copy of the guidance.
|
|
4. **Process skills.** The Claude Code sync writes `~/.claude/skills`. Another client needs its own skills location, or the MCP skills extension (SEP-2640) once a client supports it.
|