CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 7s
CI & Build / TypeScript typecheck (push) Successful in 10s
CI & Build / integration (push) Successful in 31s
CI & Build / Python tests (push) Successful in 1m5s
CI & Build / Build & push image (push) Successful in 38s
#3127 checklist 12, plus rule 27 — a capability with no surface the operator can touch is not shipped. The step was planned on the premise that nothing read `/api/version`. Two things did, and the state was worse than nothing: - `App.vue` fetched it, wrote `version` into a ref initialised to the literal `"dev"`, and swallowed the error. An instance that could not answer rendered EXACTLY what a healthy local build renders. That is checklist 12's named failure — a blank standing in for `unknown` — in the one readout whose whole job is to say what is running, and it would have made #3298's debugging session no cheaper. - `SettingsView.vue` fetched the same endpoint again on every mount and wrote the result into a local ref no template ever read. A duplicate request whose answer was discarded. So this is not "add a readout"; it is "make the existing one honest, and give it the three fields nobody could see." The readout — Settings → Config, first section, beside the other "what is this instance doing" facts. Three states kept apart, because collapsing any two of them is the defect: not asked yet (tab unopened) nothing answered the values, each ABSENT field as "unknown" the fetch itself failed its own message, with a retry `version` and `channel` prominent, `commit` in full with a copy button so it can be pasted into a `:sha` lookup (rule 145 — the registry's identity and the artifact's own must be checkable against each other), `build` kept because its ABSENCE is the diagnostic part: no ordering key means this build is not in any update order, which is what a local or hand-built image looks like. Absence, not falsiness. The payload omits what it does not know rather than sending `""` or `0` (see `build_version_payload`), so the renderer uses `??` throughout — `build` is a number and `0` is a legitimate ordering key, which `||` would report as unknown. `tests/test_version_readout.py` pins that operator specifically, along with the "no plausible default" property, because `||` is the form a person reaches for by habit. Rule 156 — the fetch carries a deadline. This readout is consulted when an instance is misbehaving, which is exactly when it may never answer; without one the surface sits on "still loading" forever, which is the same blank arrived at from the other direction. `apiGet` gains an OPT-IN `timeoutMs` rather than a default, so no existing call site's behaviour moves. Every other call in the client still has no deadline — reported separately, not fixed here. No frontend test runner exists, so verification is the typecheck lane plus four source-inspection guards in the unit lane, each pinning one property. Also folded in: `plugin/README.md` now leads with the mint script and offers `make` second, since `make` is not installed on every workstation. 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 `python3 scripts/mint_plugin_version.py` (or `make
|
|
mint-plugin`, where `make` is installed) 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.
|