rename(547): the plugin is grimoire — manifest, MCP key, hooks, env, skills
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m2s
CI & Build / Python tests (push) Failing after 1m26s
CI & Build / Build & push image (push) Skipped
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m2s
CI & Build / Python tests (push) Failing after 1m26s
CI & Build / Build & push image (push) Skipped
Step 3 of the Scribe -> Grimoire rename. - Plugin `grimoire` in marketplace `grimoire-plugin`, MCP server key `grimoire`, so tools reach a session as mcp__plugin_grimoire_grimoire__*. - hooks/scribe_* -> hooks/grimoire_*, their shell functions and the TMPDIR ledgers with them; SCRIBE_URL / SCRIBE_TOKEN / SCRIBE_HOOK_DIR / SCRIBE_LEDGER_DIRS -> GRIMOIRE_*; the project marker is `.grimoire`. - skills/using-scribe -> using-grimoire, /grimoire:sync, stored processes arrive as grimoire-proc-<slug>; moment_actions names them the same way. - Migration 0124 rewrites stored skill names (rule_moments, rule_moment_judgments, moment_mappings) from scribe:/scribe-proc- to grimoire:/grimoire-proc-, so a rule mounted on a skill keeps firing. - Settings' install commands and MCP-only defaults, the docs and the tests follow. - scripts/check_plugin.py drops the smoke event for a report-check hook that has no file in plugin/hooks. Kept on purpose: the secret-sealing label, and the names that let backups written as Scribe restore and be counted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,7 @@ from quart import Quart
|
||||
# the duplicate gate, the guessed-id refusal, `systems_hint`,
|
||||
# `placement` and `report_back` — at the moment it applies.
|
||||
# - The bundled skills (Agent Skills, client-neutral): every reflex in full.
|
||||
# `using-scribe` owns the working reflexes; the process skills own arcs.
|
||||
# `using-grimoire` owns the working reflexes; the process skills own arcs.
|
||||
# - Client adapters (the Claude Code plugin today): timing and that
|
||||
# client's own conventions, never a copy of the above.
|
||||
# tests/test_guidance_ownership.py holds the topic registry that enforces it.
|
||||
@@ -353,7 +353,7 @@ def build_mcp_server() -> MCPServer:
|
||||
Transport settings (statelessness, DNS-rebinding protection) are not
|
||||
here: since SDK v2 they belong to the ASGI app, built in `mount_mcp`.
|
||||
"""
|
||||
mcp = StrictArgsMCPServer("scribe", instructions=_INSTRUCTIONS.strip())
|
||||
mcp = StrictArgsMCPServer("grimoire", instructions=_INSTRUCTIONS.strip())
|
||||
from grimoire.mcp.tools import register_all
|
||||
register_all(mcp)
|
||||
return mcp
|
||||
|
||||
@@ -84,7 +84,7 @@ async def create_process(
|
||||
moments: The moments from list_moments this procedure is FOR — a
|
||||
release procedure is a deliver, a review procedure a verify.
|
||||
Loading the process then reaches each one, as well as its own
|
||||
`skill.scribe-proc-<slug>`, so a rule mounted on work.deliver
|
||||
`skill.grimoire-proc-<slug>`, so a rule mounted on work.deliver
|
||||
arrives when the release procedure is loaded, before any of its
|
||||
steps run.
|
||||
force: Bypass the near-duplicate gate. By default, if a title- or
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Restore a Grimoire backup into an empty instance, from the command line.
|
||||
|
||||
python -m grimoire.restore /backups/scribe-20261015T020000Z.tar
|
||||
python -m grimoire.restore /backups/grimoire-20261015T020000Z.tar
|
||||
|
||||
This is how a fresh instance is brought back after losing the old one. Run it
|
||||
inside the Grimoire container, which already has the database settings and has
|
||||
|
||||
@@ -73,7 +73,7 @@ async def session_context():
|
||||
active project is determined — the plugin never pins a project id.
|
||||
project_id (optional int) — the project named directly, and the only
|
||||
key a caller outside a git repo has: the plugin's hooks
|
||||
send it when a `.scribe` marker file names a project. Takes
|
||||
send it when a `.grimoire` marker file names a project. Takes
|
||||
precedence over `repo`. Access-checked like any other read — an id
|
||||
this account cannot read loads no project rather than failing.
|
||||
source (optional str) — the host's SessionStart source (startup,
|
||||
@@ -467,7 +467,7 @@ async def write_path_prior_art():
|
||||
are the ones shown in full on this call.
|
||||
(Returns a `checkpoint` block on the same contract as /tool-rules —
|
||||
see that endpoint. The write-path HOOK deliberately does not act on
|
||||
it: scribe_prior_art.sh carries a tested property that it never
|
||||
it: grimoire_prior_art.sh carries a tested property that it never
|
||||
returns a permissionDecision, because a recall aid must not stand in
|
||||
the way of a write. The block is computed and
|
||||
returned so that decision can be revisited with evidence rather than
|
||||
@@ -602,7 +602,7 @@ async def shape_check():
|
||||
async def claim_session():
|
||||
"""Bind the harness's session id to the caller's live claim on a task.
|
||||
|
||||
Called by `scribe_claim_session.sh` after `update_task` / `add_task_log`.
|
||||
Called by `grimoire_claim_session.sh` after `update_task` / `add_task_log`.
|
||||
The server has already stamped the claim on that write; this only says
|
||||
WHICH session made it — an id the harness reported, not one the model
|
||||
asserted. A GET for the reason every plugin endpoint is one: a read-scoped
|
||||
@@ -631,7 +631,7 @@ async def claim_session():
|
||||
async def release_session():
|
||||
"""Release the claims a session held, as it ends.
|
||||
|
||||
Called by `scribe_session_end.sh`. Best-effort by design: SessionEnd does
|
||||
Called by `grimoire_session_end.sh`. Best-effort by design: SessionEnd does
|
||||
not fire on a crash, so the claim's lease — not this call — is what makes a
|
||||
dead session's claim read as dead. This only makes the common case tidy.
|
||||
|
||||
@@ -650,8 +650,8 @@ async def release_session():
|
||||
async def process_manifest():
|
||||
"""Stored Processes as skill-stub specs for the plugin's sync script.
|
||||
|
||||
The plugin's `scribe_sync_processes.sh` (run at SessionStart and via the
|
||||
`/scribe:sync` command) curls this and writes one auto-surfacing local skill
|
||||
The plugin's `grimoire_sync_processes.sh` (run at SessionStart and via the
|
||||
`/grimoire:sync` command) curls this and writes one auto-surfacing local skill
|
||||
per Process into ~/.claude/skills/. See services/plugin_context.
|
||||
build_process_manifest. A read-scoped API key suffices.
|
||||
"""
|
||||
|
||||
@@ -8,7 +8,7 @@ compares them against recorded snippet locations, so "record everything"
|
||||
becomes a watched number instead of an aspiration.
|
||||
|
||||
The definition extractor MIRRORS the write-path hook's awk rules
|
||||
(plugin/hooks/scribe_prior_art.sh, ARM 1) — one shared notion of "a
|
||||
(plugin/hooks/grimoire_prior_art.sh, ARM 1) — one shared notion of "a
|
||||
definition" between the hook and the server, so the metric and the backstop
|
||||
agree on what counts. The two are pinned together by shared test vectors in
|
||||
tests/test_pattern_coverage.py; change one, change both.
|
||||
@@ -69,7 +69,7 @@ _SKIP_DIRS = frozenset({
|
||||
_MAX_FILE_BYTES = 1_000_000
|
||||
|
||||
|
||||
# --- the definition extractor (mirror of scribe_prior_art.sh ARM 1) ----------
|
||||
# --- the definition extractor (mirror of grimoire_prior_art.sh ARM 1) ----------
|
||||
|
||||
_CSS_RE = re.compile(r"^\s*\.([A-Za-z][A-Za-z0-9_-]*)\s*[,{]")
|
||||
# Leading declaration modifiers, so the definition keyword is the first word
|
||||
@@ -105,7 +105,7 @@ _ARROW_RE = re.compile(
|
||||
#
|
||||
# The honest tool for .py would be `ast`, which cannot be fooled by prose at
|
||||
# all. It is not what this uses, because this extractor is mirrored rule for
|
||||
# rule by an awk program in plugin/hooks/scribe_defs.sh, awk cannot parse
|
||||
# rule by an awk program in plugin/hooks/grimoire_defs.sh, awk cannot parse
|
||||
# Python, and a fix only one of the pair can run is the drift the mirror
|
||||
# exists to prevent. What both can do is blank the SPANS: a triple-quoted
|
||||
# string or a /* … */ comment is replaced by its own newlines before any
|
||||
|
||||
@@ -70,9 +70,9 @@ SKILL_TOOL, SKILL_FIELD = "skill", "skill"
|
||||
# (`metadata: moments:`), and the declaration ships HERE as defaults, because
|
||||
# the server never sees the plugin's files. `test_skill_moments` holds the two
|
||||
# together. The plugin's name qualifies the skill, as the harness names it.
|
||||
BUNDLED_SKILL_PLUGIN = "scribe"
|
||||
BUNDLED_SKILL_PLUGIN = "grimoire"
|
||||
BUNDLED_SKILL_MOMENTS: dict[str, tuple[str, ...]] = {
|
||||
"using-scribe": ("session.start",),
|
||||
"using-grimoire": ("session.start",),
|
||||
"brainstorming": ("work.plan",),
|
||||
"writing-plans": ("work.plan",),
|
||||
"reusing-code": ("work.change",),
|
||||
@@ -83,11 +83,11 @@ BUNDLED_SKILL_MOMENTS: dict[str, tuple[str, ...]] = {
|
||||
"family-canon": ("work.record",),
|
||||
}
|
||||
|
||||
# A stored process arrives as the skill `scribe-proc-<slug>`
|
||||
# (scribe_sync_processes.sh). Its moments are the process's own field, read
|
||||
# A stored process arrives as the skill `grimoire-proc-<slug>`
|
||||
# (grimoire_sync_processes.sh). Its moments are the process's own field, read
|
||||
# when it loads — not copied into the stub, which is written once a session
|
||||
# and would go on firing a moment its process no longer declares.
|
||||
PROCESS_SKILL_PREFIX = "scribe-proc-"
|
||||
PROCESS_SKILL_PREFIX = "grimoire-proc-"
|
||||
PROCESS_MOMENTS_FIELD = "moments"
|
||||
PROCESS = "process"
|
||||
|
||||
@@ -353,7 +353,7 @@ async def moments_for(user_id: int, tool: str, tool_input: dict | None) -> list[
|
||||
|
||||
|
||||
async def process_moments(user_id: int, skill: str) -> list[str]:
|
||||
"""The moments the stored process behind `scribe-proc-<slug>` declares.
|
||||
"""The moments the stored process behind `grimoire-proc-<slug>` declares.
|
||||
|
||||
The slug is resolved through the same manifest the sync script wrote the
|
||||
skill from, so the two agree on which process a slug names. Fails open to
|
||||
|
||||
@@ -508,8 +508,8 @@ def _slugify(text: str) -> str:
|
||||
async def build_process_manifest(user_id: int) -> dict:
|
||||
"""List the user's stored Processes as auto-surfacing skill-stub specs.
|
||||
|
||||
The plugin's sync script (scribe_sync_processes.sh) writes one
|
||||
~/.claude/skills/scribe-proc-<slug>/SKILL.md per entry — `description` is the
|
||||
The plugin's sync script (grimoire_sync_processes.sh) writes one
|
||||
~/.claude/skills/grimoire-proc-<slug>/SKILL.md per entry — `description` is the
|
||||
auto-surface trigger, and the stub body calls get_process(name) for the live
|
||||
procedure (single source of truth in the DB). Reuses the list_processes query
|
||||
(note_type='process'). Instance-agnostic: derived from whatever Processes the
|
||||
@@ -2052,7 +2052,7 @@ async def build_session_context(
|
||||
Args:
|
||||
user_id: the operator.
|
||||
project_id: the resolved active project (0 = none). The endpoint
|
||||
resolves this from the working repo's remote, or from a `.scribe`
|
||||
resolves this from the working repo's remote, or from a `.grimoire`
|
||||
marker file naming the project directly — the only key a session
|
||||
outside a git repo has. A non-zero id that does not
|
||||
resolve is reported rather than silently dropped.
|
||||
|
||||
@@ -223,7 +223,7 @@ POINTS: dict[str, Point] = dict([
|
||||
# out would not fail here. What catches that is `assert_registered`, which the
|
||||
# recorders call as the row is written.
|
||||
# Keyed by the EXACT string the extractor produces, matched by equality rather
|
||||
# than by prefix. The paths are relative to `src/` and so begin `scribe/`; a
|
||||
# than by prefix. The paths are relative to `src/` and so begin `grimoire/`; a
|
||||
# prefix spelled any other way silently matches nothing, and every site sails
|
||||
# through a check that looks like it is running.
|
||||
FAN_OUT_SITES: dict[str, tuple[str, ...]] = {
|
||||
|
||||
@@ -953,7 +953,7 @@ async def snippet_consumers(user_id: int, note_id: int) -> dict:
|
||||
|
||||
# --- write-path stamping: hook evidence lands as rows -----------------------
|
||||
#
|
||||
# The write-path hook (plugin/hooks/scribe_prior_art.sh) fires on every
|
||||
# The write-path hook (plugin/hooks/grimoire_prior_art.sh) fires on every
|
||||
# Write/Edit and already carries the two halves of a consumer-map row: the
|
||||
# file being written and the definitions in (or enclosing) the payload. What
|
||||
# it could not say on its own is WHICH canon the session is instantiating.
|
||||
|
||||
Reference in New Issue
Block a user