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
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:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "scribe",
|
||||
"description": "Scribe for Claude Code: connects the scribe MCP server, adds the hooks that deliver live project state and relevant records at the right moment, ships the shared client-neutral Scribe skills (using-scribe, writing-plans, reporting-back, systematic-debugging, verification, brainstorming, reusing-code, shape-accounting, family-canon), and syncs your saved Scribe Processes as skills (/scribe:sync).",
|
||||
"description": "Grimoire for Claude Code: connects the scribe MCP server, adds the hooks that deliver live project state and relevant records at the right moment, ships the shared client-neutral Grimoire skills (using-scribe, writing-plans, reporting-back, systematic-debugging, verification, brainstorming, reusing-code, shape-accounting, family-canon), and syncs your saved Grimoire Processes as skills (/scribe:sync).",
|
||||
"version": "2026.10.21.2344",
|
||||
"author": {
|
||||
"name": "Bryan Van Deusen"
|
||||
@@ -17,12 +17,12 @@
|
||||
"userConfig": {
|
||||
"api_endpoint": {
|
||||
"type": "string",
|
||||
"title": "Scribe base URL",
|
||||
"description": "Base URL of your Scribe instance, no trailing slash (e.g. https://scribe.example.com)"
|
||||
"title": "Grimoire base URL",
|
||||
"description": "Base URL of your Grimoire instance, no trailing slash (e.g. https://scribe.example.com)"
|
||||
},
|
||||
"api_token": {
|
||||
"type": "string",
|
||||
"title": "Scribe API key",
|
||||
"title": "Grimoire API key",
|
||||
"description": "An fmcp_ API key from Settings \u2192 API Keys (read scope is enough for the session-start hook; write scope to use the tools)",
|
||||
"sensitive": true
|
||||
}
|
||||
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
# Packaging Scribe for an agent client
|
||||
# Packaging Grimoire for an agent client
|
||||
|
||||
This is the maintainer contract for shipping Scribe into an agent client:
|
||||
This is the maintainer contract for shipping Grimoire into an agent client:
|
||||
what every client package shares, what each one adds, and where a new client's
|
||||
files go. One principle governs it: **the server orients,
|
||||
the skills hold the depth, and a client adapter only times delivery and says
|
||||
@@ -13,10 +13,10 @@ another one means adding files, not moving or rewriting any.
|
||||
|
||||
| Piece | Where | What it is |
|
||||
|---|---|---|
|
||||
| **The skills** | `plugin/skills/*/SKILL.md` | Agent Skills (the open SKILL.md format). They state every Scribe reflex in full and name no client. `tests/test_guidance_ownership.py` fails if a skill names a particular client, or references anything outside its own folder. Every client package ships this folder verbatim. |
|
||||
| **The skills** | `plugin/skills/*/SKILL.md` | Agent Skills (the open SKILL.md format). They state every Grimoire reflex in full and name no client. `tests/test_guidance_ownership.py` fails if a skill names a particular client, or references anything outside its own folder. Every client package ships this folder verbatim. |
|
||||
| **The MCP server** | `<base URL>/mcp` | HTTP, `Authorization: Bearer <fmcp_ key>`. Its `_INSTRUCTIONS` is a client-neutral orientation — the workflow across tools (≤1,600 chars); each tool's description carries its contract; in-band responses (`placement`, `report_back`, `systems_hint`, the duplicate gate, the guessed-id refusal) fire in every client. |
|
||||
| **The adapter API** | `<base URL>/api/plugin/*` | Endpoints any client's hooks can call with the same key. `GET`, so read scope is enough: `context` (live session state), `retrieve` (rules, preferences and notes for a message), `prior-art` (records and shape-ledger hints for code being written), `tool-rules` (rules for a command about to run), `moment-tools` (which tools reach a mounted moment), `shape-check` (definitions written this turn that nobody has judged), `claim-session` / `release-session` (task claims), `processes` (stored Processes to expose as skills). `POST`, so write scope: `moment` (rules mounted on the moments a tool call reaches), `rule-opened` (a rule was read), `reply-rules` (the finished reply, held for an unopened rule or — when the turn closed a task — for missing completion sections). |
|
||||
| **The API key** | Scribe → Settings → MCP Access | One `fmcp_` key per install. Write scope covers the MCP tools and every hook; a read key runs only the `GET` hooks. |
|
||||
| **The API key** | Grimoire → Settings → MCP Access | One `fmcp_` key per install. Write scope covers the MCP tools and every hook; a read key runs only the `GET` hooks. |
|
||||
|
||||
## Added by each client
|
||||
|
||||
@@ -39,7 +39,7 @@ another one means adding files, not moving or rewriting any.
|
||||
| `hooks/scribe_prior_art.sh` | PreToolUse on editor writes: `GET /api/plugin/prior-art`. |
|
||||
| `hooks/scribe_after_write.sh` | PostToolUse on shell commands: the same check for code written through the shell. |
|
||||
| `hooks/scribe_tool_rules.sh` | PreToolUse on shell commands: `GET /api/plugin/tool-rules`. |
|
||||
| `hooks/scribe_moment.sh` | PreToolUse on every tool: the rules mounted on the moments the call reaches, `POST /api/plugin/moment`; skips tools `GET /api/plugin/moment-tools` says reach nothing mounted, and Scribe's own (their responses carry `moment_rules`). |
|
||||
| `hooks/scribe_moment.sh` | PreToolUse on every tool: the rules mounted on the moments the call reaches, `POST /api/plugin/moment`; skips tools `GET /api/plugin/moment-tools` says reach nothing mounted, and Grimoire's own (their responses carry `moment_rules`). |
|
||||
| `hooks/scribe_reply_check.sh` | Stop: sends the finished reply to `POST /api/plugin/reply-rules` — the rules mounted on the reply moments, and the reply against every rule's trigger, and — when the turn closed a task — the completion-section check; holds once per rule, with the reason the server returns, and never holds the rewrite. |
|
||||
| `hooks/scribe_shape_check.sh` | Stop: sends the definitions the turn wrote (the write hooks' `<sid>.written.ids` ledger) to `GET /api/plugin/shape-check`; blocks once, with the reason the server returns, so the agent judges what it built. |
|
||||
| `hooks/scribe_record_opened.sh` | PostToolUse on `get_rule`: notes the rule as read in the session ledger and `POST /api/plugin/rule-opened`. |
|
||||
@@ -66,5 +66,5 @@ Hook config arrives as `CLAUDE_PLUGIN_OPTION_API_ENDPOINT` / `CLAUDE_PLUGIN_OPTI
|
||||
|
||||
1. **Passing the key to the MCP server.** Claude Code substitutes `${user_config.api_token}` into the header. Agent Plugins' `mcp.json` and Gemini's extension config have their own variable and secret handling. Decide per client, and keep the key out of files that get committed.
|
||||
2. **Coexistence in one folder.** Whether a root `plugin.json` (Agent Plugins) beside `.claude-plugin/plugin.json` changes how Claude Code loads the plugin is untested. Test it before shipping both from `plugin/`, or give the second client its own package directory that reuses `plugin/skills/`.
|
||||
3. **Hook parity.** Other clients' hook events differ and are partly implemented. Map each Scribe hook to the nearest event, and where none exists, leave that timing to the in-band server responses rather than writing a copy of the guidance.
|
||||
3. **Hook parity.** Other clients' hook events differ and are partly implemented. Map each Grimoire hook to the nearest event, and where none exists, leave that timing to the in-band server responses rather than writing a copy of the guidance.
|
||||
4. **Process skills.** The Claude Code sync writes `~/.claude/skills`. Another client needs its own skills location, or the MCP skills extension (SEP-2640) once a client supports it.
|
||||
|
||||
+20
-20
@@ -1,44 +1,44 @@
|
||||
# Scribe plugin for Claude Code
|
||||
# Grimoire plugin for Claude Code
|
||||
|
||||
Turns a self-hosted [Scribe](https://git.fabledsword.com/bvandeusen/FabledScribe)
|
||||
Turns a self-hosted [Grimoire](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 the
|
||||
active project's live state (from the server) and this adapter's short
|
||||
Claude Code guidance, so Scribe surfaces *without being asked*. Rules are
|
||||
Claude Code guidance, so Grimoire surfaces *without being asked*. Rules are
|
||||
never preloaded; they arrive by retrieval when your work matches one.
|
||||
- **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.
|
||||
- **The shared Scribe skills** — client-neutral Agent Skills, the same files
|
||||
- **The shared Grimoire skills** — client-neutral Agent Skills, the same files
|
||||
any client's package would ship: using-scribe, writing-plans, reporting-back
|
||||
(reply to the operator in a shape that says where the work stands),
|
||||
systematic-debugging, verification, brainstorming, reusing-code (record and
|
||||
recall reusable code as snippets), shape-accounting and family-canon.
|
||||
Replaces superpowers.
|
||||
- **Your Scribe Processes as skills** — saved Processes are synced into local
|
||||
- **Your Grimoire 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 depend on Scribe instead
|
||||
of auto-memory — leave auto-memory at its default; Scribe replaces its job by
|
||||
It is designed so you can uninstall `superpowers` and depend on Grimoire instead
|
||||
of auto-memory — leave auto-memory at its default; Grimoire replaces its job by
|
||||
holding the one copy, not by switching it off.
|
||||
|
||||
**How the pieces divide the work**: the Scribe server orients
|
||||
**How the pieces divide the work**: the Grimoire server orients
|
||||
every MCP client and serves live state; the skills in `skills/` state every
|
||||
reflex in full and name no client; this plugin is the Claude Code adapter —
|
||||
hooks that deliver at the right moment, `/scribe:sync`, and the few things only
|
||||
Claude Code needs said (`hooks/scribe_static_context.md`). Packaging Scribe for
|
||||
Claude Code needs said (`hooks/scribe_static_context.md`). Packaging Grimoire for
|
||||
another client: see [PACKAGING.md](PACKAGING.md).
|
||||
|
||||
## 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.
|
||||
The plugin ships inside the Grimoire app repo, so the marketplace *is* that repo —
|
||||
you always get the plugin version that matches your Grimoire instance.
|
||||
|
||||
```
|
||||
/plugin marketplace add https://git.fabledsword.com/bvandeusen/FabledScribe.git
|
||||
@@ -49,15 +49,15 @@ On install you'll be asked for:
|
||||
|
||||
| Setting | What |
|
||||
|---|---|
|
||||
| **Scribe base URL** | e.g. `https://scribe.example.com` (no trailing slash) |
|
||||
| **Scribe API key** | a write-scoped `fmcp_` key from **Settings → MCP Access** (stored in your OS keychain) |
|
||||
| **Grimoire base URL** | e.g. `https://scribe.example.com` (no trailing slash) |
|
||||
| **Grimoire API key** | a write-scoped `fmcp_` key from **Settings → MCP Access** (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
|
||||
**fail-open**: if Grimoire 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
|
||||
@@ -69,7 +69,7 @@ On install you'll be asked for:
|
||||
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
|
||||
in time is said, once per outage ("Grimoire 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 → General → Knowledge
|
||||
@@ -82,9 +82,9 @@ On install you'll be asked for:
|
||||
- `hooks/hooks.json` → PreToolUse hook on every tool (`hooks/scribe_moment.sh`)
|
||||
→ `POST /api/plugin/moment`: the rules mounted on the moments the call
|
||||
reaches. It skips tools that `GET /api/plugin/moment-tools` says reach
|
||||
nothing mounted, and Scribe's own tools, whose responses carry
|
||||
nothing mounted, and Grimoire's own tools, whose responses carry
|
||||
`moment_rules`.
|
||||
- `hooks/hooks.json` → PostToolUse hooks on Scribe's `get_rule`,
|
||||
- `hooks/hooks.json` → PostToolUse hooks on Grimoire's `get_rule`,
|
||||
`rule_outcome`, `update_task` and `add_task_log`: they keep the session's
|
||||
ledgers of rules opened and acted on (`get_rule` also reports to
|
||||
`POST /api/plugin/rule-opened`), and claim the task being worked
|
||||
@@ -102,7 +102,7 @@ On install you'll be asked for:
|
||||
- `hooks/hooks.json` → Stop hook (`hooks/scribe_reply_check.sh`): sends the
|
||||
reply that ends the turn to `POST /api/plugin/reply-rules`, which holds it
|
||||
once for an unopened rule mounted on the reply moments or matching the reply.
|
||||
When the turn closed a Scribe task (`update_task`/`create_task` with status
|
||||
When the turn closed a Grimoire task (`update_task`/`create_task` with status
|
||||
done), the same request has the server check the reply for the completion
|
||||
sections — where the work sits, what needs you, what comes next. If they are
|
||||
missing it blocks once with the reason the server returns, and records how
|
||||
@@ -120,9 +120,9 @@ On install you'll be asked for:
|
||||
land under category `plugin`, action `shape_check`.
|
||||
- `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
|
||||
command → generate `~/.claude/skills/scribe-proc-*` stubs from your Grimoire
|
||||
Processes (via `GET /api/plugin/processes`); also **fail-open**, and pruned to
|
||||
match what exists in Scribe.
|
||||
match what exists in Grimoire.
|
||||
- `hooks/hooks.json` → PreCompact hook (`hooks/scribe_precompact_preserve.sh`):
|
||||
tells the summarizer which parts of the conversation to keep literally. Local
|
||||
only, and it never blocks, because a blocked compaction is skipped outright.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
description: Sync your Scribe stored Processes into auto-surfacing local skills
|
||||
description: Sync your Grimoire stored Processes into auto-surfacing local skills
|
||||
allowed-tools: Bash(bash:*), Bash(ls:*)
|
||||
---
|
||||
|
||||
Regenerate the local skill stubs for your Scribe **Processes** so they auto-surface
|
||||
Regenerate the local skill stubs for your Grimoire **Processes** so they auto-surface
|
||||
this session. Each Process becomes a `~/.claude/skills/scribe-proc-<slug>/SKILL.md`
|
||||
whose body calls `get_process(<name>)` for the live procedure.
|
||||
|
||||
@@ -14,7 +14,7 @@ skill files within this session — no restart needed).
|
||||
Run the bundled sync script, then list what was synced:
|
||||
|
||||
```
|
||||
bash "${CLAUDE_PLUGIN_ROOT}/hooks/scribe_sync_processes.sh" && ls ~/.claude/skills 2>/dev/null | grep '^scribe-proc-' || echo "no Scribe process skills (no Processes, or Scribe unreachable)"
|
||||
bash "${CLAUDE_PLUGIN_ROOT}/hooks/scribe_sync_processes.sh" && ls ~/.claude/skills 2>/dev/null | grep '^scribe-proc-' || echo "no Grimoire process skills (no Processes, or Grimoire unreachable)"
|
||||
```
|
||||
|
||||
Report which `scribe-proc-*` skills are now present.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,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,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,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
|
||||
|
||||
@@ -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,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,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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,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,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
|
||||
|
||||
@@ -12,7 +12,7 @@ real options and a reasoned choice — not a single path defended after the fact
|
||||
|
||||
## Recall first
|
||||
|
||||
`search` Scribe before generating from scratch — a prior decision, note, or
|
||||
`search` Grimoire before generating from scratch — a prior decision, note, or
|
||||
brainstorm on this often already exists. Build on it instead of repeating it.
|
||||
|
||||
## Open up
|
||||
@@ -30,7 +30,7 @@ brainstorm on this often already exists. Build on it instead of repeating it.
|
||||
|
||||
## Capture the decision
|
||||
|
||||
When a direction is chosen, record it in Scribe (`create_note`, e.g. tag
|
||||
When a direction is chosen, record it in Grimoire (`create_note`, e.g. tag
|
||||
`decision`): the choice, the alternatives weighed, and the reason. That's what
|
||||
keeps the same question from being re-litigated later — and what a future
|
||||
session reads to understand *why*, not just *what*.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: reporting-back
|
||||
description: Use when you are about to write the reply the operator will read and the delivered reply shape is not enough — work finished, stopping on a blocker, asking them to decide or to do something, answering "where are we", or proposing an approach. The long-form reference behind the reply shapes Scribe delivers: why sections are chosen rather than filled, who decides what, taking placement from the record, and a completion report worked in full. Triggers on reporting completion, handing off, asking a question, or summarising progress.
|
||||
description: Use when you are about to write the reply the operator will read and the delivered reply shape is not enough — work finished, stopping on a blocker, asking them to decide or to do something, answering "where are we", or proposing an approach. The long-form reference behind the reply shapes Grimoire delivers: why sections are chosen rather than filled, who decides what, taking placement from the record, and a completion report worked in full. Triggers on reporting completion, handing off, asking a question, or summarising progress.
|
||||
metadata:
|
||||
moments: reply.report
|
||||
---
|
||||
@@ -14,7 +14,7 @@ still unreadable to them. Shape it around **where the work stands**.
|
||||
|
||||
## The shapes arrive on their own
|
||||
|
||||
Scribe delivers the default shape for each kind of reply as product, at the
|
||||
Grimoire delivers the default shape for each kind of reply as product, at the
|
||||
moment it is due: the core ("Reply shape · Every reply") with each turn, the
|
||||
completion report when a task closes, the asks when you put a question to the
|
||||
operator, the plan when you plan. A shape this session has already seen comes
|
||||
@@ -61,7 +61,7 @@ and it costs the operator the decision twice.
|
||||
## Take the placement from the record
|
||||
|
||||
A remembered milestone title or "next step" reads exactly like a real one when
|
||||
it is wrong. So take placement from Scribe:
|
||||
it is wrong. So take placement from Grimoire:
|
||||
|
||||
- `update_task` and `create_task` return a **`placement`** block — the project,
|
||||
the milestone, `position` (step N of M), `progress`, and `next` (the next open
|
||||
|
||||
@@ -41,7 +41,7 @@ through recall/auto-inject; this skill is the active reflex around that.
|
||||
turn's shapes — reuse from memory leaves no such evidence.
|
||||
- If auto-inject already surfaced a snippet title that looks relevant, that's
|
||||
your cue to `get_snippet` it rather than start from scratch.
|
||||
- **Prior art offered beside a write is not noise — read it.** When Scribe notes
|
||||
- **Prior art offered beside a write is not noise — read it.** When Grimoire notes
|
||||
that a snippet is already recorded for the file you just wrote or edited, open
|
||||
it before you go any further. Either it's the helper you were about to
|
||||
duplicate — reuse it and drop yours — or it isn't, and the record needs the new
|
||||
@@ -59,7 +59,7 @@ through recall/auto-inject; this skill is the active reflex around that.
|
||||
YOUR edit's responsibility: if the edit changes the recorded shape,
|
||||
`update_snippet(id, code=…)` with the new form as part of the same task; if
|
||||
it doesn't, `verify_snippet(id, status="ok", commit_sha=…)` costs one call
|
||||
and re-stamps the record as checked. Scribe never reads the repo — this
|
||||
and re-stamps the record as checked. Grimoire never reads the repo — this
|
||||
moment, in the session that has the context, is the only place the record
|
||||
gets kept true.
|
||||
|
||||
@@ -115,7 +115,7 @@ shape is asked about once.
|
||||
|
||||
## A shared snippet is a suggestion, not a standard
|
||||
|
||||
Scribe is multi-user, so a search can return snippets other people own. Those
|
||||
Grimoire is multi-user, so a search can return snippets other people own. Those
|
||||
come back marked `shared: true` with an `owner`.
|
||||
|
||||
- Read one as **that person's suggestion**, not as the way things are done here.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: systematic-debugging
|
||||
description: Use when diagnosing a bug, failure, or unexpected behavior — investigate methodically instead of guessing. Triggers on "why is this failing/breaking", a stack trace, a flaky test, or any "it should work but doesn't". On resolution, capture the issue in Scribe so it isn't re-debugged from scratch.
|
||||
description: Use when diagnosing a bug, failure, or unexpected behavior — investigate methodically instead of guessing. Triggers on "why is this failing/breaking", a stack trace, a flaky test, or any "it should work but doesn't". On resolution, capture the issue in Grimoire so it isn't re-debugged from scratch.
|
||||
metadata:
|
||||
moments: work.debug
|
||||
---
|
||||
@@ -12,7 +12,7 @@ guessed fix that "seems to work" often just moves the bug somewhere else.
|
||||
|
||||
## Recall first
|
||||
|
||||
Before digging in, `search` Scribe for the symptom — a prior issue
|
||||
Before digging in, `search` Grimoire for the symptom — a prior issue
|
||||
(`list_tasks(kind="issue")` or `search`) may already hold the cause and the fix.
|
||||
Don't re-debug what's already solved.
|
||||
|
||||
@@ -32,7 +32,7 @@ Don't re-debug what's already solved.
|
||||
|
||||
## Capture the issue (so it's findable)
|
||||
|
||||
When resolved, record it in Scribe as its own issue (`create_task(kind="issue")`):
|
||||
When resolved, record it in Grimoire as its own issue (`create_task(kind="issue")`):
|
||||
**symptom → root cause → fix → how it was verified** in the body, optionally
|
||||
linked to the task it arose from (`arose_from_id`) and the subsystem it touches
|
||||
(`system_ids`). Even a problem fixed in passing is worth two lines — that's how
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
name: using-scribe
|
||||
description: Use at the START of every session, and before answering anything about the operator's work or starting any task — establishes the Scribe-first reflex. You hold none of the operator's rules: they arrive by retrieval when your work matches one, and what_might_apply is how you ask before a consequential act — it returns the wide net of candidates with no bar. Call enter_project when a repo/project is in scope. Then recall before acting, update over duplicate, plan in Scribe not in files.
|
||||
description: Use at the START of every session, and before answering anything about the operator's work or starting any task — establishes the Grimoire-first reflex. You hold none of the operator's rules: they arrive by retrieval when your work matches one, and what_might_apply is how you ask before a consequential act — it returns the wide net of candidates with no bar. Call enter_project when a repo/project is in scope. Then recall before acting, update over duplicate, plan in Grimoire not in files.
|
||||
metadata:
|
||||
moments: session.start
|
||||
---
|
||||
|
||||
# Using Scribe
|
||||
# Using Grimoire
|
||||
|
||||
Scribe is the operator's self-hosted system of record (notes, tasks, issues,
|
||||
Grimoire is the operator's self-hosted system of record (notes, tasks, issues,
|
||||
projects, milestones, systems) and rulebook, reachable
|
||||
through the bundled `scribe` MCP server. Its value is mostly in what it
|
||||
**already holds** — so make reading it a reflex, not something you wait to be
|
||||
@@ -15,7 +15,7 @@ asked for.
|
||||
|
||||
## Do this first (every session)
|
||||
|
||||
If the working directory maps to a Scribe project, call `enter_project(id)` —
|
||||
If the working directory maps to a Grimoire project, call `enter_project(id)` —
|
||||
it returns the project's goal, the milestones and open tasks worked on most
|
||||
recently, its Systems and the titles of its own rules in one shot.
|
||||
|
||||
@@ -26,30 +26,30 @@ Then **ask before you act**: before anything hard to reverse or outward-facing,
|
||||
search the rules for what you are about to do. Reflex 2 below is why asking,
|
||||
not loading, is how the operator's rules reach you.
|
||||
|
||||
## Scribe holds these functions — keep one copy
|
||||
## Grimoire holds these functions — keep one copy
|
||||
|
||||
Scribe is the home for the operator's **rules, recall, and planning** — the
|
||||
Grimoire is the home for the operator's **rules, recall, and planning** — the
|
||||
jobs your client's own local memory files would otherwise do. Route those jobs
|
||||
to Scribe instead of also writing them locally: codify rules with
|
||||
`create_rule` / `create_project_rule`, capture durable knowledge as Scribe
|
||||
notes, and keep plans in Scribe milestones (via `start_planning`). One copy, in
|
||||
Scribe; let any existing local memory shrink as Scribe takes over.
|
||||
to Grimoire instead of also writing them locally: codify rules with
|
||||
`create_rule` / `create_project_rule`, capture durable knowledge as Grimoire
|
||||
notes, and keep plans in Grimoire milestones (via `start_planning`). One copy, in
|
||||
Grimoire; let any existing local memory shrink as Grimoire takes over.
|
||||
|
||||
Two constraints on *how* that's achieved:
|
||||
|
||||
- **Steer behaviour; leave the client's own settings as they are.** Scribe works
|
||||
- **Steer behaviour; leave the client's own settings as they are.** Grimoire works
|
||||
alongside a client's built-in memory at its defaults. You replace memory's
|
||||
functions by *doing the work in Scribe*, so there is no reason to ask the
|
||||
functions by *doing the work in Grimoire*, so there is no reason to ask the
|
||||
operator to switch a built-in feature off — a setting they didn't knowingly
|
||||
change is breakage they won't know to restore.
|
||||
- **A Scribe-shaped hole is acceptable.** If Scribe is later removed, the
|
||||
- **A Grimoire-shaped hole is acceptable.** If Grimoire is later removed, the
|
||||
operator recovers context over time — that's fine. Local memory doesn't need
|
||||
to be kept as a self-sufficient fallback.
|
||||
|
||||
## The reflex
|
||||
|
||||
1. **Recall before acting.** Before answering a question about the operator's
|
||||
work, or starting a task, `search` Scribe (and `list_tasks` / `list_notes`)
|
||||
work, or starting a task, `search` Grimoire (and `list_tasks` / `list_notes`)
|
||||
for related prior work — an existing task, decision, or note — instead of
|
||||
re-deriving it or opening a duplicate. When a project is in scope, pass its
|
||||
`project_id` so results stay scoped.
|
||||
@@ -129,7 +129,7 @@ Two constraints on *how* that's achieved:
|
||||
|
||||
**Preferences shape how work is done, never what gets recorded.** They
|
||||
govern your conduct — how you report, how carefully you pace, which form
|
||||
you reach for. What ends up in Scribe is decided by what the record is:
|
||||
you reach for. What ends up in Grimoire is decided by what the record is:
|
||||
a preference never makes a task into a note, downgrades a rule, or keeps
|
||||
something out of the corpus that belongs there.
|
||||
|
||||
@@ -151,7 +151,7 @@ Two constraints on *how* that's achieved:
|
||||
3. **Update over duplicate.** When recording, prefer updating an existing
|
||||
note/rule/task over creating a new one. Search first; revise what's there.
|
||||
|
||||
4. **When you plan, plan in Scribe.** Work with an *arc* — several steps toward
|
||||
4. **When you plan, plan in Grimoire.** Work with an *arc* — several steps toward
|
||||
one goal — gets a plan, and a plan is a milestone: `start_planning(project_id,
|
||||
title)` creates one whose `body` holds the design, each step is its own task
|
||||
under it (`create_task(milestone_id=...)`), progress goes in work-logs
|
||||
@@ -174,7 +174,7 @@ Two constraints on *how* that's achieved:
|
||||
`/clear`, the operator wrapping up for the day — each is the last moment the
|
||||
reasoning behind the work lives anywhere but here. Log on the task you were
|
||||
holding where it stands, what you tried and ruled out, and the next move:
|
||||
write down what the next session needs, because it arrives with Scribe's
|
||||
write down what the next session needs, because it arrives with Grimoire's
|
||||
record and nothing else. Moving a task to `in_progress` or logging on it also
|
||||
claims it for this session — that claim is what hands the work back to you
|
||||
after a compaction, and it ends on its own when you stop, so there is nothing
|
||||
@@ -211,14 +211,14 @@ Two constraints on *how* that's achieved:
|
||||
cover, and they plainly belong to that area, add them with `update_system`
|
||||
— which files are which area is your call, as naming the area was.
|
||||
|
||||
8. **Name the record, never just its number.** Whenever you refer to a Scribe
|
||||
8. **Name the record, never just its number.** Whenever you refer to a Grimoire
|
||||
record — in a message to the operator, a commit message, a task body, a
|
||||
work-log — write the id *and* its title: `#3244 "the staleness signal"`,
|
||||
`milestone 323 "rule versioning"`. Not `#3244`.
|
||||
|
||||
You have the record open; the operator does not. A bare id reads as
|
||||
complete to you and as homework to them — they have to look it up to know
|
||||
what their own conversation is about, or guess. Scribe's own duplicate gate
|
||||
what their own conversation is about, or guess. Grimoire's own duplicate gate
|
||||
already writes `id 412: "debounce helper"` for exactly this reason; match
|
||||
it everywhere else.
|
||||
|
||||
@@ -231,7 +231,7 @@ Two constraints on *how* that's achieved:
|
||||
|
||||
**An id exists only once a create call returns it.** Never write the id you
|
||||
expect a record to get — every session and user draws from one sequence,
|
||||
so the number goes to whoever creates next, and Scribe refuses a body that
|
||||
so the number goes to whoever creates next, and Grimoire refuses a body that
|
||||
cites an id not yet assigned. Records that must cite each other are created
|
||||
together — `create_records` or `start_planning(steps=...)` — with
|
||||
`{{ref:N}}` where the Nth record's id belongs.
|
||||
@@ -263,7 +263,7 @@ Two constraints on *how* that's achieved:
|
||||
order you did things in: which task or milestone it belongs to, what now
|
||||
works, what needs them, and what comes next. Take the placement from the
|
||||
`placement` block that `create_task` / `update_task` return — the milestone,
|
||||
step N of M, the next open step — rather than from memory. Scribe delivers
|
||||
step N of M, the next open step — rather than from memory. Grimoire delivers
|
||||
the shape for each kind of reply when it is due — the core with each turn,
|
||||
the completion report when a task closes, the asks when you put a question —
|
||||
and `list_reply_shapes` has them all; the `reporting-back` skill holds the
|
||||
@@ -302,7 +302,7 @@ moment rather than on every turn:
|
||||
|
||||
## You are the judge of what the record says
|
||||
|
||||
Scribe's shape exists to support how you work, so you are the judge of record
|
||||
Grimoire's shape exists to support how you work, so you are the judge of record
|
||||
for the work itself: what a shape is, which canon a thing belongs to, whether
|
||||
a finding holds, whether something is done. `classify_shapes`, `update_task`,
|
||||
`create_snippet` and the rest are where your judgment lands — under your own
|
||||
@@ -342,8 +342,8 @@ token: `resolve_design_system(id)` for the values, or
|
||||
stated outside the system, so it can never follow a palette change — and
|
||||
nothing will tell you it drifted.
|
||||
|
||||
## Other Scribe process-skills
|
||||
## Other Grimoire process-skills
|
||||
|
||||
Scribe also ships focused process-skills — writing-plans, reporting-back,
|
||||
Grimoire also ships focused process-skills — writing-plans, reporting-back,
|
||||
systematic debugging, verification, and brainstorming. Reach for the matching one when its
|
||||
situation arises, the same way you reach for this skill.
|
||||
|
||||
@@ -13,7 +13,7 @@ a `.scribe` file naming the project:
|
||||
{"instance": "https://scribe.example.com", "project_id": 2, "project": "Homelab"}
|
||||
|
||||
`instance` is what makes the id trustworthy. An id means nothing on its own —
|
||||
it is a different project on every Scribe — so a marker that has travelled to
|
||||
it is a different project on every Grimoire — so a marker that has travelled to
|
||||
another instance is ignored rather than followed to the wrong project. A bare
|
||||
`2` also works when writing the file by hand. When work plainly belongs to a
|
||||
project and the directory names none, offer to write the marker;
|
||||
|
||||
@@ -71,7 +71,7 @@ said twice at two strengths, which is worse than either alone: a session that
|
||||
retrieves the softer copy has been told that binding guidance is optional.
|
||||
|
||||
**First ask whether it's a rule at all.** A rule is prose you have to remember
|
||||
and apply; Scribe's other entities are structure a tool can resolve and check.
|
||||
and apply; Grimoire's other entities are structure a tool can resolve and check.
|
||||
Visual standards belong in a **design system**, not a rulebook — a token can be
|
||||
inherited, resolved per mode, rendered to a stylesheet and diffed against code,
|
||||
and none of that survives being written as a rule. A repeatable procedure is a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: writing-plans
|
||||
description: Use when a piece of work has an arc — several steps toward one goal, worth tracking as a unit — and you want the approach reviewable before you start. Triggers when the user asks you to plan, design an approach, or scope an effort, or when work is about to sprawl across several steps. Not for single-step work. The plan lives in a Scribe milestone (via start_planning), not a local file.
|
||||
description: Use when a piece of work has an arc — several steps toward one goal, worth tracking as a unit — and you want the approach reviewable before you start. Triggers when the user asks you to plan, design an approach, or scope an effort, or when work is about to sprawl across several steps. Not for single-step work. The plan lives in a Grimoire milestone (via start_planning), not a local file.
|
||||
metadata:
|
||||
moments: work.plan
|
||||
---
|
||||
@@ -56,7 +56,7 @@ the project has the same title or reads as the same plan, they return it as
|
||||
`existing_milestone` and create nothing. Add to that plan, or pass `force=true`
|
||||
only when you have read it and this really is separate work.
|
||||
|
||||
## When it does: start the plan in Scribe, not a file
|
||||
## When it does: start the plan in Grimoire, not a file
|
||||
|
||||
Call **`start_planning(project_id, title)`** before designing or implementing —
|
||||
so the milestone exists to write into, rather than being backfilled from work
|
||||
@@ -78,7 +78,7 @@ rules. The plan lives in that milestone:
|
||||
**Never write an id you have not been given.** A plan body that says "see
|
||||
#4012" before #4012 exists is a guess, and other sessions and users are
|
||||
creating records from the same sequence at the same time — the number goes to
|
||||
whoever creates next, and the plan then points at their record. Scribe refuses
|
||||
whoever creates next, and the plan then points at their record. Grimoire refuses
|
||||
a body citing an id that has not been assigned. Where the plan and its steps
|
||||
need to cite each other, write a placeholder instead: `{{ref:N}}` is the Nth
|
||||
step in the list, `{{ref:milestone}}` is the milestone. They are replaced with
|
||||
@@ -88,7 +88,7 @@ the real id and title as the records are created.
|
||||
record, not a file on disk. A plan is never a task: `kind="plan"` is refused,
|
||||
and `start_planning` makes a milestone.
|
||||
|
||||
Before designing from scratch, **recall**: `search` Scribe for a related prior
|
||||
Before designing from scratch, **recall**: `search` Grimoire for a related prior
|
||||
decision or note. Often the thinking (or half of it) already exists.
|
||||
|
||||
## What a good plan contains
|
||||
|
||||
Reference in New Issue
Block a user