CI & Build / Python lint (push) Successful in 7s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 56s
CI & Build / Python tests (push) Successful in 1m26s
CI & Build / Build & push image (push) Successful in 16s
Step 5 of milestone 410 "One owner per piece of guidance". The operator wants any attempt to package Scribe for another agent client to find the repo already in the right shape. plugin/PACKAGING.md (linked from the README) states: - what every client package shares: plugin/skills/ (verbatim), the /mcp endpoint and its in-band responses, the /api/plugin/* adapter endpoints (context, retrieve, prior-art, tool-rules, processes), and one fmcp_ key - what each client adds: a manifest; hooks limited to timing and transport; optional commands; adapter static text that never copies a skill or the index - the Claude Code adapter file by file, as the worked example - how Agent Plugins 1.0 clients (Codex, Cursor, Copilot/VS Code, Kiro, ChatGPT) and Gemini CLI would map, marked researched-not-tested (#4023) - four open questions for the second package: passing the key to the MCP server, whether two manifests can share one folder, hook parity, and where process skills go Hook audit: every hook prints only server-provided text, status or outage lines, the running version, or the compaction reload pointer. No guidance copies, so nothing moved. Guard: test_the_skills_reference_nothing_outside_their_folder fails on a relative path upward or a reference to plugin/, hooks/, commands/ or a manifest from inside a skill, with a companion test showing it can fail. Plugin version minted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
63 lines
5.9 KiB
Markdown
63 lines
5.9 KiB
Markdown
# Packaging Scribe for an agent client
|
|
|
|
This is the maintainer contract for shipping Scribe into an agent client:
|
|
what every client package shares, what each one adds, and where a new client's
|
|
files go. It follows from decision #4027 (milestone 410): **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 Scribe 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 index (≤2,000 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/*` | Plain `GET` endpoints any client's hooks can call with the same key (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), `processes` (stored Processes to expose as skills). |
|
|
| **The API key** | Scribe → Settings → API Keys | One `fmcp_` key per install. Read scope for hooks; write scope for the MCP tools. |
|
|
|
|
## 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.scribe` (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/scribe_session_context.sh` | SessionStart: adapter static text + running version + `GET /api/plugin/context`; a reload banner after compaction. |
|
|
| `hooks/scribe_autoinject.sh` | UserPromptSubmit: `GET /api/plugin/retrieve` for the message. |
|
|
| `hooks/scribe_prior_art.sh` | PreToolUse on editor writes: `GET /api/plugin/prior-art`. |
|
|
| `hooks/scribe_after_write.sh` | PostToolUse on shell commands: the same check for code written through the shell. |
|
|
| `hooks/scribe_tool_rules.sh` | PreToolUse on shell commands: `GET /api/plugin/tool-rules`. |
|
|
| `hooks/scribe_sync_processes.sh` + `commands/sync.md` | `GET /api/plugin/processes` → `~/.claude/skills/scribe-proc-*` stubs; `/scribe:sync` on demand. |
|
|
| `hooks/scribe_defs.sh` | Shared shell helpers: config, dedup ledgers, outage line. |
|
|
| `hooks/scribe_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, #2198), with `SCRIBE_URL` / `SCRIBE_TOKEN` as an override.
|
|
|
|
## How other clients would map
|
|
|
|
**Researched, not tested** (spike #4023, September 2026). 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 Scribe 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.
|