CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / TypeScript typecheck (push) Successful in 10s
CI & Build / integration (push) Successful in 18s
CI & Build / Python tests (push) Successful in 46s
CI & Build / Build & push image (push) Successful in 25s
Step 4 of #278, product half. The audit that motivated it: one System in project 2, thirty records tagged, nothing since July 28 — three days after the feature landed. Not a discipline failure; retrieval was completely blind to the association (zero references in embeddings, knowledge, search, auto-inject, or enter_project), so tagging was a write-side label with no read-side payoff, and labels nobody reads don't get maintained. Three changes, ordered by what makes the others workable: 1. enter_project returns the project's Systems (id, name, first line of the charter). Load-bearing for the tagging instruction: you cannot ask an agent to check a record against a vocabulary it never sees. Trimmed because it rides on every session start; the full charter stays get_system's job. Present-and-empty rather than absent when a project has none — "no named areas yet" is information the create-the-System instruction acts on. 2. search accepts system_id, MCP and REST (#33). Implemented once in semantic_search_notes as an EXISTS against record_systems — an association filter deciding candidate-set membership before scoring, like project_id, not a ranking signal. The REST route's missing project filter stays #2463's: it carries a default-scope UI decision this change must not preempt. 3. The instructions (#119, _INSTRUCTIONS + using-scribe skill; plugin 0.1.25 for the cache): - Tag as you write, with an executable test — "would someone investigating that subsystem want this in the pile list_system_records returns?" — rather than "tag appropriately", which is what died. - Create the System when the area has no record: the two-or-more test snippets use, plus "don't wait to be asked to name an area that plainly exists", because the agent's default was leaving un-modelled areas un-modelled forever. - State vs chronicle: dev-logs are written once and never rewritten; durable findings live in the System's reference note, updated in place — safe because note versions are the changelog, which has existed since the feature shipped and was never named as one. list_system_records' docstring now sells it as the way to READ a subsystem, reference note first. No auto-inject boost by System — vocabulary and filter first, measure before adding ranking behaviour (the #2486 lesson). Refs #278, #2546
Scribe plugin for Claude Code
Turns a self-hosted Scribe instance into a first-class Claude Code extension:
- MCP tools over your notes, tasks, projects, milestones, systems, and
rulebook (the
scribeserver). - Session-start push channel — a
SessionStarthook injects your always-on rules + active-project context so Scribe surfaces without being asked. - Prior-art recall on writes — a
PreToolUsehook on Write/Edit checks the file about to be written against your recorded snippets (what's kept at that path, and what resembles the code) and offers them before the helper is rewritten. Titles only, never blocks the edit. - Universal process-skills — using-scribe, writing-plans, systematic-debugging, verification, brainstorming, reusing-code (record and recall reusable code as snippets). Replaces superpowers.
- Your Scribe Processes as skills — saved Processes are synced into local
~/.claude/skills/scribe-proc-*stubs that auto-surface by relevance; the stub fetches the live procedure viaget_process. Refreshed each session and on demand with/scribe:sync.
It is designed so you can uninstall superpowers and disable auto-memory and
depend on neither.
Install
The plugin ships inside the Scribe app repo, so the marketplace is that repo — you always get the plugin version that matches your Scribe instance.
/plugin marketplace add https://git.fabledsword.com/bvandeusen/FabledScribe.git
/plugin install scribe@scribe-plugin
On install you'll be asked for:
| Setting | What |
|---|---|
| Scribe base URL | e.g. https://scribe.example.com (no trailing slash) |
| Scribe API key | an fmcp_ key from Settings → API Keys (stored in your OS keychain) |
| Active project id | optional — numeric project id to scope the session-start context |
What gets wired
plugin.jsonmcpServers→ thescribeMCP server at<base URL>/mcp(Bearer auth).hooks/hooks.json→ SessionStart hook (hooks/scribe_session_context.sh), fail-open: if Scribe is unreachable it injects nothing and never blocks the session.hooks/hooks.json→ PreToolUse hook onWrite|Edit(hooks/scribe_prior_art.sh) →GET /api/plugin/prior-art. ReturnsadditionalContextwith no permission decision, so it can inform the write but never stop it; silent when nothing is recorded, which is most of the time. Toggle in Settings → Knowledge auto-inject.skills/→ the universal process-skills, surfaced by description match.hooks/scribe_sync_processes.sh(a 2nd SessionStart hook) + the/scribe:synccommand → generate~/.claude/skills/scribe-proc-*stubs from your Scribe Processes (viaGET /api/plugin/processes); also fail-open, and pruned to match what exists in Scribe.
Notes
- Set a
versionbump in.claude-plugin/plugin.jsonper release so clients pick up changes. - The session-start, auto-inject and prior-art hooks need only a read-scoped key; the MCP tools need write scope to create/update. Every hook is a GET for that reason — a read key cannot POST.