Files
FabledScribe/plugin/README.md
T
bvandeusenandClaude Opus 5 f5a3643da8
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
refactor(plugin): retire what the hand-bump scheme left behind — the README that taught it, the floor test, the stale rationale (#3328)
#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
2026-09-02 00:38:50 -04:00

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.