rename(547): what people read says Grimoire
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 1m1s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 36s

Step 1 of the Scribe -> Grimoire rename: every capitalised "Scribe" in prose
becomes "Grimoire" ("Fabled Scribe" -> "Fabled Grimoire") across the UI,
the PWA manifest and page title, the MCP instructions and tool docstrings,
hook and skill text, docs and README, with the tests that assert on that
prose moving with it. Old migrations keep their wording.

Identifiers are untouched here: the package, plugin name, MCP server key,
hook file names, env vars, the .scribe marker and FabledScribe repo/image
names move in steps 2 and 3, along with prose that quotes them.

The sweep matched "Scribe" only as a whole word, so describe/subscribe and
friends are untouched. The longer name took the MCP instructions one
character over their 1,600 budget; two phrases are shortened to fit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 10:46:37 -04:00
co-authored by Claude Opus 5.5
parent 06be537a18
commit e99f1169f7
142 changed files with 460 additions and 460 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Scribe plugin — PostToolUse write-path trigger on Bash.
# Grimoire plugin — PostToolUse write-path trigger on Bash.
#
# scribe_prior_art.sh fires before a Write/Edit TOOL CALL. Code written any
# other way — sed, heredocs, python edit scripts, `cat > file` — never reaches
@@ -219,7 +219,7 @@ while IFS= read -r rel_path; do
if [ -n "$local_lines" ] && [ "$reached" != 0 ]; then
n_recorded=$(scribe_json_len "$body_flat" '.note_ids')
if [ "${n_recorded:-0}" = "0" ] || [ "$n_recorded" = "" ]; then
local_context="${local_context}"$'\n'"> None of those existing copies is recorded in Scribe. If the version just written is the canonical one — or this edit is consolidating the copies — record it now with create_snippet so the next session is offered it instead of writing another copy."
local_context="${local_context}"$'\n'"> None of those existing copies is recorded in Grimoire. If the version just written is the canonical one — or this edit is consolidating the copies — record it now with create_snippet so the next session is offered it instead of writing another copy."
fi
fi
+2 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# Scribe plugin — UserPromptSubmit push channel (knowledge auto-inject, Path A).
# Grimoire plugin — UserPromptSubmit push channel (knowledge auto-inject, Path A).
#
# On each user prompt, asks the operator's Scribe instance for a TITLE-FIRST
# On each user prompt, asks the operator's Grimoire instance for a TITLE-FIRST
# awareness hint: the few notes that clear the per-user auto-inject gates
# (high-confidence threshold, margin gate, session dedup, top-k). Titles + ids
# only — never bodies; the agent calls get_note(id) to pull anything it judges
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Scribe — say WHICH session holds a task's claim.
# Grimoire — say WHICH session holds a task's claim.
#
# The server stamps a claim on the write that is the work — a task reaching
# `in_progress`, a work log on an open task — so every MCP client gets a claim
+8 -8
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# shellcheck shell=bash
# Scribe plugin — the pieces the hooks share.
# Grimoire plugin — the pieces the hooks share.
#
# scribe_prior_art.sh fires BEFORE a Write/Edit tool call; scribe_after_write.sh
# fires AFTER a Bash tool call and diffs the working tree, so code written by
@@ -13,7 +13,7 @@
# scribe_defs stdin code → "kind<TAB>name" per definition
# scribe_local_dups ROOT REL "kind<TAB>name" lines on stdin → the by-name
# local-duplicate lines (ARM 1)
# scribe_unreached STATE SID SECS REL the "Scribe didn't answer" line, once
# scribe_unreached STATE SID SECS REL the "Grimoire didn't answer" line, once
# per outage — or nothing, if said lately
# scribe_reached STATE SID the server answered: the next outage speaks again
# scribe_config sets `url` + `token` from the env, returns 0
@@ -500,12 +500,12 @@ scribe_defs() {
# ---------------------------------------------------------------------------
# ARM 1 — BY NAME, LOCALLY. Does a definition of this already exist?
#
# The recorded arms ask Scribe what was RECORDED; the ledger arm asks what a
# The recorded arms ask Grimoire what was RECORDED; the ledger arm asks what a
# BOUND repo's ledger knows. A helper nobody recorded, in a repo nobody bound,
# is invisible to both, so it gets defined again in the next file. This arm asks the one question only the
# developer's machine can answer, inside the repo, holding the code about to
# be written: no index, no storage, no server — it runs even on an install
# that has never configured Scribe.
# that has never configured Grimoire.
#
# Definition-shaped patterns only. Grepping for bare occurrences would match
# every CALL site and drown the real finding — and a hint that is mostly noise
@@ -852,7 +852,7 @@ scribe_local_dups() {
# pass through a subshell's output, where it could land in a log or an `xtrace`
# line. Returns 0 only when both are usable, so a caller can either bail
# (`scribe_config || exit 0`) or carry on degraded — the session-context hook
# still owes its static floor when Scribe is unconfigured.
# still owes its static floor when Grimoire is unconfigured.
# Declared here, not just assigned inside the function: `scribe_defs.sh` owns
# these two names, and a sourcing hook should have them defined the moment it
# sources — before any code path that might reference them. It also lets
@@ -890,7 +890,7 @@ scribe_unreached() {
[ $((now - last)) -lt "$_SCRIBE_UNREACHED_QUIET" ] && return 0
fi
printf '%s' "$now" > "$marker" 2>/dev/null || true
printf '> Scribe did not answer the prior-art check for `%s` within %ss — this write went UNCHECKED against the record and the shape ledger (the local by-name arm, if it spoke above, needed no server). If the name matters, check it yourself: `search` for the concept, `list_shapes(project_id, path=…)` for the ledger. Said once per outage; if it keeps happening the instance is slow or down.' "$4" "$3"
printf '> Grimoire did not answer the prior-art check for `%s` within %ss — this write went UNCHECKED against the record and the shape ledger (the local by-name arm, if it spoke above, needed no server). If the name matters, check it yourself: `search` for the concept, `list_shapes(project_id, path=…)` for the ledger. Said once per outage; if it keeps happening the instance is slow or down.' "$4" "$3"
}
scribe_reached() {
@@ -1305,12 +1305,12 @@ scribe_turn_fact() {
# The second key is a `.scribe` file in the directory (or above it, the way
# git finds its root), naming the project the work belongs to. It is a
# POINTER, not a copy: an id and enough to check the id means what it says.
# Nothing Scribe should be holding goes in it.
# Nothing Grimoire should be holding goes in it.
#
# {"instance": "https://scribe.example.com", "project_id": 2,
# "project": "FabledScribe"}
#
# instance WHICH Scribe the id belongs to, and the reason this file is
# instance WHICH Grimoire the id belongs to, and the reason this file is
# not just a number. A project id means nothing on its own: id 2
# is a different project on every instance, so a marker that
# travels — a copied directory, a shared machine, a repo someone
+2 -2
View File
@@ -1,4 +1,4 @@
# Scribe plugin — JSON, read without jq.
# Grimoire plugin — JSON, read without jq.
#
# WHY THIS FILE EXISTS. Every hook here parsed JSON with `jq`, and every hook
# opened with `command -v jq >/dev/null 2>&1 || exit 0`. jq is not installed by
@@ -47,7 +47,7 @@
# ordering a turn is bounded by is unaffected by the dropped one.
#
# A KEY CONTAINING `.` OR `[` WOULD MAKE AN AMBIGUOUS PATH. Nothing in the two
# JSON dialects this reads — Claude Code hook events and Scribe's own API — has
# JSON dialects this reads — Claude Code hook events and Grimoire's own API — has
# one, and inventing an escaping scheme for a case neither producer can emit
# would cost every caller a decode for nothing. Stated so the next person hits
# a comment rather than a mystery.
+3 -3
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Scribe — PreToolUse moment arm: rules MOUNTED on a moment arrive when the
# Grimoire — PreToolUse moment arm: rules MOUNTED on a moment arrive when the
# moment happens.
#
# The other PreToolUse arms search: they turn the act into a query and hope a
@@ -67,7 +67,7 @@ now=$(date +%s 2>/dev/null) || now=0
# belongs on the moments they reached — which is how a rule nobody mounted
# gets proposed for one. Recorded whether or not anything is mounted, since
# an unmounted moment is exactly what the evidence is for, and including
# Scribe's own tools (closing a task is a moment too). The open itself is
# Grimoire's own tools (closing a task is a moment too). The open itself is
# not an act. A large event (a file write) is kept as its tool name alone.
if [ "$now" -gt 0 ] && [ "$tool_key" != "get_rule" ]; then
acts_file="$cache_dir/${safe_sid}.acts"
@@ -92,7 +92,7 @@ esac
# ── Which tools can reach a mounted rule: cached, first line a timestamp ──
#
# Five minutes. A mount or a mapping made in this session reaches the hook
# within that window; the MCP door delivers Scribe's own moments at once.
# within that window; the MCP door delivers Grimoire's own moments at once.
tools_file="$cache_dir/${safe_sid}.tools"
stamp=""
[ -f "$tools_file" ] && stamp=$(head -n 1 "$tools_file" 2>/dev/null)
+4 -4
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Scribe plugin — PreCompact: tell the summarizer what must survive.
# Grimoire plugin — PreCompact: tell the summarizer what must survive.
#
# HOW A PRECOMPACT HOOK REACHES THE MODEL (as read out of Claude Code 2.1.273):
#
@@ -59,19 +59,19 @@ cat <<'EOF'
Preserve the following literally in the summary — copied through, not
paraphrased or counted:
- Every Scribe record the conversation refers to, by id AND title: tasks,
- Every Grimoire record the conversation refers to, by id AND title: tasks,
issues, milestones, notes, rules, systems. A bare "#4061" is not enough; a
record whose name is lost has to be looked up again before it can be used.
- Which task is in progress and what its status was last set to, plus the
milestone it sits under.
- Work that was done but NOT yet recorded in Scribe — an edit with no work-log,
- Work that was done but NOT yet recorded in Grimoire — an edit with no work-log,
a fix not filed as an issue, a decision not written down. Carry these over as
outstanding; they exist nowhere else once this conversation is summarized.
- Any rule or preference that was retrieved and still governs the work, by id
and title.
- Anything the operator asked for that has not been done yet, in their words.
Everything else here can be recovered from the repository or from Scribe. These
Everything else here can be recovered from the repository or from Grimoire. These
cannot: they are this session's only copy.
EOF
+4 -4
View File
@@ -1,9 +1,9 @@
#!/usr/bin/env bash
# Scribe plugin — PreToolUse write-path trigger (prior-art recall).
# Grimoire plugin — PreToolUse write-path trigger (prior-art recall).
#
# Auto-inject (scribe_autoinject.sh) fires on the operator's prompt. The moment
# reuse is actually lost is later: when the AGENT decides mid-task to write a
# helper. This hook fires there — on Write/Edit — and asks the operator's Scribe
# helper. This hook fires there — on Write/Edit — and asks the operator's Grimoire
# instance what prior art is already recorded for the target file: a snippet at
# that path or in its directory, plus snippets resembling the code about to be
# written. Titles + ids only, never bodies.
@@ -278,7 +278,7 @@ if [ -n "$body" ]; then
fi
# ARM 1½ — the RECORD nudge. The local arm just proved the thing being
# written already exists elsewhere in this repo, and Scribe returned no record
# written already exists elsewhere in this repo, and Grimoire returned no record
# of anything for it. That is the one moment "record it" is earned rather than
# noise: the duplication is demonstrated, not guessed. Gated on BOTH sides so
# an ordinary new helper (no other copies) and an already-recorded one (the
@@ -289,7 +289,7 @@ fi
if [ -n "$local_lines" ] && [ "$reached" = 1 ]; then
n_recorded=$(scribe_json_len "$body_flat" '.note_ids')
if [ "${n_recorded:-0}" = "0" ] || [ "$n_recorded" = "" ]; then
local_context="${local_context}"$'\n'"> None of those existing copies is recorded in Scribe. If the version being written is the canonical one — or this edit is consolidating the copies — record it now with create_snippet (name, code, when-to-reach-for-it, location) so the next session is offered it instead of writing another copy."
local_context="${local_context}"$'\n'"> None of those existing copies is recorded in Grimoire. If the version being written is the canonical one — or this edit is consolidating the copies — record it now with create_snippet (name, code, when-to-reach-for-it, location) so the next session is offered it instead of writing another copy."
fi
fi
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Scribe — record that this session OPENED a rule, not merely saw it named.
# Grimoire — record that this session OPENED a rule, not merely saw it named.
#
# WHAT THIS ANSWERS
#
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Scribe — record that this session said what a rule DID, not merely that it
# Grimoire — record that this session said what a rule DID, not merely that it
# read one.
#
# THE THIRD LEDGER, AND WHY THE OTHER TWO ARE NOT ENOUGH.
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Scribe — Stop hook: the reply moment.
# Grimoire — Stop hook: the reply moment.
#
# A reply is the one act no tool call marks, and it is where "please check
# this on your end" or "it's done" gets said — so every arm that fires on a
+13 -13
View File
@@ -1,14 +1,14 @@
#!/usr/bin/env bash
# Scribe plugin — SessionStart push channel (two tiers + compaction re-grounding).
# Grimoire plugin — SessionStart push channel (two tiers + compaction re-grounding).
#
# Tier 1 (STATIC, always fires, no auth, no network): injects the Claude Code
# adapter's own guidance (scribe_static_context.md) — where Scribe's reflexes
# adapter's own guidance (scribe_static_context.md) — where Grimoire's reflexes
# are stated (the using-scribe skill), Claude Code's memory files, /compact,
# /scribe:sync, and what to do when Scribe is unavailable. It carries only what
# /scribe:sync, and what to do when Grimoire is unavailable. It carries only what
# this client needs said; the reflexes themselves are owned by the shared
# skills and the server's index.
#
# Tier 2 (DYNAMIC, best-effort enrichment): curls the operator's Scribe instance
# Tier 2 (DYNAMIC, best-effort enrichment): curls the operator's Grimoire instance
# for active-project context and appends it. Config comes from
# the plugin's userConfig, exported to hooks as:
# CLAUDE_PLUGIN_OPTION_API_ENDPOINT base URL, no trailing slash
@@ -24,7 +24,7 @@
# fires after a compaction (SessionStart input `source` == "compact"), when
# earlier turns have just been summarized and in-flight state is most at risk.
# On that source we lead with a banner telling the model to reload project +
# in-flight tasks from Scribe. This stays the durable path: record-as-you-go
# in-flight tasks from Grimoire. This stays the durable path: record-as-you-go
# plus a post-compaction reload from the instance.
#
# The other half: a host hook cannot make the model flush before a compaction,
@@ -153,7 +153,7 @@ manifest="$here/../.claude-plugin/plugin.json"
if [ -f "$manifest" ]; then
plugin_version=$(scribe_json_pick "$(scribe_json_flat < "$manifest")" '.version')
if [ -n "$plugin_version" ]; then
append "> Scribe plugin **v${plugin_version}** is executing in this session. A fix merged after this version has not reached it — the marketplace clone updates on its own, but the cache that runs only refreshes when the manifest version changes."
append "> Grimoire plugin **v${plugin_version}** is executing in this session. A fix merged after this version has not reached it — the marketplace clone updates on its own, but the cache that runs only refreshes when the manifest version changes."
fi
fi
@@ -235,16 +235,16 @@ if [ -n "$url" ] && [ -n "$token" ] && command -v curl >/dev/null 2>&1; then
else
first="Start by finding this repo's project with \`list_projects()\` and calling \`enter_project(<id>)\`"
fi
status="> ⚠️ Scribe: live project context was not loaded this session — ${why}. The tools may still answer. ${first} before any other work: it loads the recent milestones and open tasks this session would otherwise begin without, and prior work you cannot see is work you will redo. The using-scribe skill still applies."
status="> ⚠️ Grimoire: live project context was not loaded this session — ${why}. The tools may still answer. ${first} before any other work: it loads the recent milestones and open tasks this session would otherwise begin without, and prior work you cannot see is work you will redo. The using-scribe skill still applies."
fi
elif [ -n "$url" ] && [ -z "$token" ]; then
status="> ⚠️ Scribe: live context disabled this session — the API key is not configured (Scribe base URL is). Set it with \`/plugin\` → Scribe → configure, or export SCRIBE_TOKEN. Tools still work; ask for rules with \`search(content_type=\"rule\")\` and project context with \`enter_project()\`."
status="> ⚠️ Grimoire: live context disabled this session — the API key is not configured (Grimoire base URL is). Set it with \`/plugin\` → Grimoire → configure, or export SCRIBE_TOKEN. Tools still work; ask for rules with \`search(content_type=\"rule\")\` and project context with \`enter_project()\`."
elif [ -z "$url" ] && [ -z "$token" ]; then
# NEITHER value arrived. Not a benign "unconfigured install": the plugin
# prompts for both values at enable time, so if neither reached the hook,
# something is wrong (an env-var casing mismatch looks exactly like this).
# Say so.
status="> ⚠️ Scribe: live context disabled this session — neither the Scribe base URL nor the API key reached this hook. Configure the plugin (\`/plugin\` → Scribe), or export SCRIBE_URL + SCRIBE_TOKEN. Note this also disables prompt auto-inject and the write-path prior-art trigger. Tools still work; ask for rules with \`search(content_type=\"rule\")\` and project context with \`enter_project()\`."
status="> ⚠️ Grimoire: live context disabled this session — neither the Grimoire base URL nor the API key reached this hook. Configure the plugin (\`/plugin\` → Grimoire), or export SCRIBE_URL + SCRIBE_TOKEN. Note this also disables prompt auto-inject and the write-path prior-art trigger. Tools still work; ask for rules with \`search(content_type=\"rule\")\` and project context with \`enter_project()\`."
fi
[ -n "$dyn" ] && append "$dyn"
@@ -265,17 +265,17 @@ fi
if [ -n "$dyn" ] && [ -z "$(scribe_json_pick "$body_flat" '.project.id')" ]; then
host=$(scribe_url_host "$url")
if [ -n "$marker_why" ]; then
append "> ⚠️ Scribe: the marker file \`${marker}\` ${marker_why}, so no project context was loaded. Fix the file, or ignore it and bind this directory another way."
append "> ⚠️ Grimoire: the marker file \`${marker}\` ${marker_why}, so no project context was loaded. Fix the file, or ignore it and bind this directory another way."
elif [ -n "$marker_id" ]; then
append "> ⚠️ Scribe: \`${marker}\` names project ${marker_id}, which this account cannot read on ${host} — it may belong to a different Scribe instance, or the project may have been deleted. Check with \`list_projects()\` and correct the file."
append "> ⚠️ Grimoire: \`${marker}\` names project ${marker_id}, which this account cannot read on ${host} — it may belong to a different Grimoire instance, or the project may have been deleted. Check with \`list_projects()\` and correct the file."
elif [ -z "$repo" ]; then
append "> ℹ️ Scribe: this directory is not a git repository and has no \`.scribe\` marker, so no project context was loaded — every hook this session is unscoped. If this work belongs to a Scribe project, call \`list_projects()\` and write the marker: \`{\"instance\": \"${url%/}\", \"project_id\": <id>, \"project\": \"<title>\"}\` in \`${repo_dir}/.scribe\`. Future sessions here load that project on their own."
append "> ℹ️ Grimoire: this directory is not a git repository and has no \`.scribe\` marker, so no project context was loaded — every hook this session is unscoped. If this work belongs to a Grimoire project, call \`list_projects()\` and write the marker: \`{\"instance\": \"${url%/}\", \"project_id\": <id>, \"project\": \"<title>\"}\` in \`${repo_dir}/.scribe\`. Future sessions here load that project on their own."
fi
fi
# Compaction re-grounding: lead with a reload banner when this fire is a compact.
if [ "$source" = "compact" ]; then
prepend "> ⟳ This session was just COMPACTED — earlier turns are now a summary, so in-flight detail may be lost. Any rules that had been retrieved went into that summary with everything else, so treat yourself as holding none: before the next consequential act, ask again with \`search(content_type=\"rule\")\` rather than trusting a half-remembered one. Re-run \`enter_project()\` for the active project, check its recent milestones and open tasks, and reconcile what you are mid-way through against what Scribe records. Scribe is the record."
prepend "> ⟳ This session was just COMPACTED — earlier turns are now a summary, so in-flight detail may be lost. Any rules that had been retrieved went into that summary with everything else, so treat yourself as holding none: before the next consequential act, ask again with \`search(content_type=\"rule\")\` rather than trusting a half-remembered one. Re-run \`enter_project()\` for the active project, check its recent milestones and open tasks, and reconcile what you are mid-way through against what Grimoire records. Grimoire is the record."
fi
# Nothing at all to inject → stay silent.
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Scribe — release this session's task claims as it ends.
# Grimoire — release this session's task claims as it ends.
#
# A claim records a session's attention, and a session that ends has none left
# to give. This is the mechanical half of the hand-off: it needs nothing from
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Scribe plugin — Stop hook: the turn's new shapes are judged by the agent
# Grimoire plugin — Stop hook: the turn's new shapes are judged by the agent
# that wrote them.
#
# The write hooks (scribe_prior_art.sh, scribe_after_write.sh) append every
+10 -10
View File
@@ -1,28 +1,28 @@
# Scribe — Claude Code adapter
# Grimoire — Claude Code adapter
This session is connected to **Scribe**, the operator's system of record for
their work, through the `scribe` MCP tools. How to work with Scribe is stated
This session is connected to **Grimoire**, the operator's system of record for
their work, through the `scribe` MCP tools. How to work with Grimoire is stated
once, in the **`using-scribe`** skill: reach for it at the start of the session
and whenever you are unsure what Scribe expects. Each tool's contract is in its
and whenever you are unsure what Grimoire expects. Each tool's contract is in its
description, and the process skills (writing-plans, reporting-back,
reusing-code, systematic-debugging, verification, brainstorming,
shape-accounting, family-canon) carry their arcs.
What only Claude Code needs said:
- **Keep one copy — in Scribe, not Claude Code's local memory.** Claude
- **Keep one copy — in Grimoire, not Claude Code's local memory.** Claude
Code's local memory is `CLAUDE.md` and auto-memory: the operator's rules,
plans and project notes go to Scribe instead, and using-scribe says how
Scribe works alongside them.
plans and project notes go to Grimoire instead, and using-scribe says how
Grimoire works alongside them.
- **Lines injected beside your work are retrieval.** When the operator sends a
message, and before a write or a command, Scribe may add rules, preferences,
message, and before a write or a command, Grimoire may add rules, preferences,
notes and prior art that resemble what you are doing — and, when a tool call
or the reply ending a turn reaches a moment of work, the rules mounted on
it. Open the ones that apply; using-scribe says what a quiet turn means and
how to correct a moment that fired wrongly.
- **Compact at clean seams.** Because work is recorded as you go, a compaction
is safe once in-flight state is logged. After finishing a block of work in a
long session, log it to Scribe, then tell the operator it's a good moment to
long session, log it to Grimoire, then tell the operator it's a good moment to
`/compact` and name what you logged. You can't run it yourself; suggest it at
seams, not every turn.
- **When the operator wraps up, hand off first.** Log where each task you
@@ -31,5 +31,5 @@ What only Claude Code needs said:
- **Stored Processes arrive as skills** (`scribe-proc-*`), refreshed at session
start. After a Process is added or edited, `/scribe:sync` makes it available
straight away.
- **If the Scribe tools are unavailable, say so** rather than silently falling
- **If the Grimoire tools are unavailable, say so** rather than silently falling
back to local notes.
+5 -5
View File
@@ -1,11 +1,11 @@
#!/usr/bin/env bash
# Scribe plugin — sync stored Processes into auto-surfacing local skills.
# Grimoire plugin — sync stored Processes into auto-surfacing local skills.
#
# Fetches `GET /api/plugin/processes` and writes one
# ~/.claude/skills/scribe-proc-<slug>/SKILL.md
# per Process. The stub's frontmatter `description` is the auto-surface trigger;
# its body tells Claude to call get_process(<name>) and follow the LIVE procedure
# from Scribe (the DB stays the single source of truth — the stub is a pointer).
# from Grimoire (the DB stays the single source of truth — the stub is a pointer).
#
# WHY LOCAL ~/.claude/skills (not the plugin dir): the plugin is git-cloned and
# identical on every install, so instance-specific stubs can't live inside it.
@@ -65,9 +65,9 @@ while [ "$i" -lt "$count" ]; do
printf 'name: scribe-proc-%s\n' "$slug"
printf 'description: %s\n' "$desc"
printf -- '---\n\n'
printf '<!-- GENERATED by the Scribe plugin (scribe_sync_processes.sh). Do not edit here; edit the Process in Scribe and re-sync with /scribe:sync. -->\n\n'
printf 'This is a saved **Scribe Process**: `%s`.\n\n' "$name"
printf 'Do not improvise it. Call `get_process("%s")` via the Scribe MCP server to fetch the live procedure, then follow the returned body verbatim — including any "clarify first" steps it contains.\n' "$name"
printf '<!-- GENERATED by the Grimoire plugin (scribe_sync_processes.sh). Do not edit here; edit the Process in Grimoire and re-sync with /scribe:sync. -->\n\n'
printf 'This is a saved **Grimoire Process**: `%s`.\n\n' "$name"
printf 'Do not improvise it. Call `get_process("%s")` via the Grimoire MCP server to fetch the live procedure, then follow the returned body verbatim — including any "clarify first" steps it contains.\n' "$name"
} > "${dir}/SKILL.md" 2>/dev/null || continue
managed="${managed}${slug} "
done
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Scribe — PreToolUse rule arm for ACTIONS.
# Grimoire — PreToolUse rule arm for ACTIONS.
#
# The sibling of scribe_prior_art.sh. That hook is registered on Write|Edit and
# asks "what is recorded about the file being written". This one asks "does a
+1 -1
View File
@@ -1,4 +1,4 @@
# Scribe plugin — what happened in the LAST TURN of a transcript.
# Grimoire plugin — what happened in the LAST TURN of a transcript.
#
# Reads the flat `IDX<TAB>PATH<TAB>VALUE` stream that scribe_json.awk produces
# in `mode=lines` from a Claude Code transcript, and answers the four questions