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:
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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):
|
||||
#
|
||||
# * `additionalContext` is NEVER read on PreCompact. The hook-output schema
|
||||
# has no PreCompact variant at all; the field is honoured for SessionStart,
|
||||
# SubagentStart, Stop and friends, and silently dropped here.
|
||||
#
|
||||
# * A PreCompact hook's STDOUT becomes the compaction's custom instructions.
|
||||
# The handler collects every hook that exited 0 with non-empty stdout and
|
||||
# returns it as `newCustomInstructions`, which is merged with whatever the
|
||||
# operator typed after `/compact` and passed into the prompt that writes
|
||||
# the summary. Every path does this — manual, auto and partial compaction.
|
||||
#
|
||||
# So the channel is the summary itself: we do not interrupt the compaction, we
|
||||
# steer the summary it produces. The
|
||||
# summary is what the next turn reads, so text that survives into it survives
|
||||
# the compaction.
|
||||
#
|
||||
# WHAT NOT TO DO HERE: never block. A PreCompact block (exit 2, or
|
||||
# `{"decision":"block"}`) does not pause for the model and cannot tell it
|
||||
# anything — the compaction is SKIPPED, a warning goes to the operator's
|
||||
# screen, and the session continues uncompacted toward its context limit with
|
||||
# no summary at all, silently from the model's side. This hook exits 0 on
|
||||
# every path.
|
||||
#
|
||||
# SCOPE: in a subagent the handler discards hook stdout and keeps only a block,
|
||||
# so this steers the main session's compaction and nothing else. That is the
|
||||
# one we care about — a subagent's summary does not outlive it.
|
||||
#
|
||||
# NO NETWORK, NO CONFIG. What must be preserved is already in the conversation
|
||||
# being summarized; this hook's job is to say which parts of it are load-bearing
|
||||
# so the summarizer keeps them literally instead of compressing them away.
|
||||
# Naming the in-flight task ids from the instance would need a server round-trip
|
||||
# inside the compaction path, which is a separate, measurable question.
|
||||
#
|
||||
# PROOF THAT IT FIRED: on a manual `/compact` the handler shows the operator
|
||||
# `PreCompact [<command>] completed successfully: <stdout>`, so the hook's own
|
||||
# text is the receipt. (Auto-compaction suppresses that notification.)
|
||||
set -uo pipefail
|
||||
|
||||
# shellcheck source=plugin/hooks/grimoire_defs.sh
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/grimoire_defs.sh"
|
||||
|
||||
# THE EVENT IS READ for `session_id`: the slippage readout below is specific
|
||||
# to THIS session, and the id is the key to the ledgers that hold it. The
|
||||
# instruction is still the same whether the operator typed
|
||||
# `/compact` or the session hit its limit — what varies is the measurement
|
||||
# appended to it.
|
||||
event=$(cat 2>/dev/null || true)
|
||||
session_id=""
|
||||
if [ -n "$event" ]; then
|
||||
event_flat=$(printf '%s' "$event" | grimoire_json_flat 2>/dev/null || true)
|
||||
session_id=$(grimoire_json_pick "$event_flat" '.session_id' 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
cat <<'EOF'
|
||||
Preserve the following literally in the summary — copied through, not
|
||||
paraphrased or counted:
|
||||
|
||||
- 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 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 Grimoire. These
|
||||
cannot: they are this session's only copy.
|
||||
EOF
|
||||
|
||||
# ── The slippage readout ─────────────────────────────────────────────────
|
||||
#
|
||||
# WHY IT BELONGS HERE RATHER THAN IN A REPLY. A rule read and left unresolved
|
||||
# is invisible by construction: it looks exactly like a rule that worked. The
|
||||
# compaction is where that invisibility becomes permanent — the turns holding
|
||||
# the evidence are summarised away, and an unjudged thing that survives as
|
||||
# nothing is how a decision quietly becomes nobody's.
|
||||
#
|
||||
# STDOUT HERE IS THE SUMMARISER'S INSTRUCTIONS, not a message to the model
|
||||
# (the header records how that was established). So this does not say "you
|
||||
# slipped"; it says WHICH ids must be carried through, which is the one thing
|
||||
# the summary can do about it. The next turn then reads them in the summary
|
||||
# with the work still attached.
|
||||
#
|
||||
# SILENT WHEN THERE IS NOTHING TO SAY. A session that opened everything it was
|
||||
# shown gets no extra lines. "0 rules unresolved" on every compaction is how a
|
||||
# readout teaches its reader to skip it.
|
||||
if [ -n "$session_id" ]; then
|
||||
safe_sid=$(printf '%s' "$session_id" | tr -c 'A-Za-z0-9._-' '_')
|
||||
slippage=$(grimoire_slippage_lines "${TMPDIR:-/tmp}/grimoire-priorart" "$safe_sid" 2>/dev/null || true)
|
||||
if [ -n "$slippage" ]; then
|
||||
printf '\n%s\n' "$slippage"
|
||||
fi
|
||||
fi
|
||||
exit 0
|
||||
Reference in New Issue
Block a user