CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / integration (push) Successful in 32s
CI & Build / Python tests (push) Successful in 1m6s
CI & Build / Build & push image (push) Successful in 16s
#3127 checklist 19. The step's own deletion list turned out to be largely spent: `check_version_bump()` came out with #3327, and the machinery the step expected to delete alongside it is load-bearing for its replacement. `manifest_version(ref=…)`, `--base`, `--no-version` and the `origin/main` resolve path all STAY. Derivation makes the value right; it does not make the comparison unnecessary. `check_version_is_minted` still has to ask "did the version move when the shipped content did?", and that is a base-branch question no matter who chose the number. The step was planned before #3327 landed, when the assumption was that these died with the guard. What was actually still standing, all of it teaching or asserting the retired scheme: - `plugin/README.md` told the reader to "set a `version` bump per release." A shipped file, instructing the exact act the mint replaced — this is how a deleted control gets re-added by someone following the docs. Now says not to hand-edit the field, names `make mint-plugin`, and says what a forgotten mint costs. (`make` is not installed on every workstation, so the direct script invocation is given too.) - `test_plugin_version_bumped_with_the_hook` asserted `version >= (0,1,31)` as a tuple of ints. Under a minted value it passes vacuously — every date clears a floor of 0.1.31 — and `int("0415")` silently eats the padding the format exists to keep. Superseded by `test_the_shipped_manifest_carries_a_minted_version`, which asserts the canonical shape instead of an ordering the comparator does not perform. Removed whole (rule 22). - The module preamble still ended on "a written rule that depends on being remembered is not a control; this is" — true of the bump guard, and read as a stronger claim than the mint can support. Replaced with what the change did and did not remove: choosing a number is gone, running the mint is not, and the difference is that forgetting is now loud rather than silent. - An orphaned `# --- the version bump ---` section header with nothing under it, and a test docstring still naming `check_version_bump`. `--no-version` keeps its one legitimate case — on `main` the version is measured against itself — and now says so in both the usage block and its `--help`, so it does not read as an escape hatch. `check_session_context_ reports_its_version` stays untouched: a different check with a different job, and the only thing that makes step 6 readable from a transcript (#2220). Version minted 2026.09.02.0415 -> 2026.09.02.0438 for the README change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcCs1CcQ1ormdnzSshKqvN
91 lines
4.9 KiB
Markdown
91 lines
4.9 KiB
Markdown
# Scribe plugin for Claude Code
|
|
|
|
Turns a self-hosted [Scribe](https://git.fabledsword.com/bvandeusen/FabledScribe)
|
|
instance into a first-class Claude Code extension:
|
|
|
|
- **MCP tools** over your notes, tasks, projects, milestones, systems, and
|
|
rulebook (the `scribe` server).
|
|
- **Session-start push channel** — a `SessionStart` hook injects your always-on
|
|
rules + active-project context so Scribe surfaces *without being asked*.
|
|
- **Prior-art recall on writes** — a `PreToolUse` hook 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 via `get_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.json` `mcpServers` → the `scribe` MCP 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 on `Write|Edit`
|
|
(`hooks/scribe_prior_art.sh`) → `GET /api/plugin/prior-art`. Returns
|
|
`additionalContext` with **no** permission decision, so it can inform the write
|
|
but never stop it; silent when nothing is recorded, which is most of the time.
|
|
Two framings: a REUSE menu (similar/nearby records), and a SYNC nudge when a
|
|
snippet records the exact file being edited — "updating the record is part of
|
|
the edit" — each with its own once-per-session dedup. A third, ledger-fed
|
|
line names a duplicate family (no canon) or a canon recorded elsewhere for
|
|
the names being written (its own dedup channel, `exclude_derive`).
|
|
Fail-open but not fail-silent: a configured instance that does not answer
|
|
in time is said, once per outage ("Scribe did not answer … this write went
|
|
UNCHECKED"), so a session can tell "checked, nothing there" from "never
|
|
checked"; an answer clears the marker. The local by-name arm needs no
|
|
server and always runs. Toggle in **Settings → Knowledge auto-inject**.
|
|
- `hooks/hooks.json` → PostToolUse hook on `Bash`
|
|
(`hooks/scribe_after_write.sh`): code written through sed/heredocs/scripts
|
|
never reaches the PreToolUse hook, so this one diffs the working tree after
|
|
every Bash call (per-session path+blob snapshot; one `git status` when
|
|
nothing changed) and runs the same arms on the definitions just written,
|
|
through the same endpoint and the same dedup channels. `additionalContext`
|
|
only; never blocks, and shares the pre-write hook's once-per-outage "did not
|
|
answer" line (8 s budget here — it runs after the tool, so it gates
|
|
nothing). The extractor, the prose/data skip list, the local by-name
|
|
duplicate arm and the outage line are shared in `hooks/scribe_defs.sh`.
|
|
- `skills/` → the universal process-skills, surfaced by description match.
|
|
- `hooks/scribe_sync_processes.sh` (a 2nd SessionStart hook) + the `/scribe:sync`
|
|
command → generate `~/.claude/skills/scribe-proc-*` stubs from your Scribe
|
|
Processes (via `GET /api/plugin/processes`); also **fail-open**, and pruned to
|
|
match what exists in Scribe.
|
|
|
|
## Notes
|
|
|
|
- **Do not hand-edit `version` in `.claude-plugin/plugin.json`.** It is minted
|
|
from the clock — run `make mint-plugin` (or `python3
|
|
scripts/mint_plugin_version.py`) after changing anything under `plugin/`, and
|
|
commit the result. The installer decides whether to refresh the cache it
|
|
executes from by comparing that string, so content that ships without a new
|
|
version reaches the repo and stops there (#2209). CI fails the lane if you
|
|
forget.
|
|
- 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.
|