What's wrong
The PreCompact hook builds a resume snapshot that tells the model to retrieve prior context by calling ctx_search(...), but it never passes the platform-specific tool name, so on prefixed platforms the snapshot uses a name that does not match the real tool.
hooks/precompact.mjs:49 calls buildResumeSnapshot(events, { compactCount }) with no searchTool. buildResumeSnapshot (src/session/snapshot.ts) defaults searchTool to the bare string "ctx_search", so whenever the snapshot emits a retrieval hint it uses the bare ctx_search(queries: [...], source: "session-events"). The per-platform PreCompact hooks (hooks/codex/precompact.mjs:44, hooks/kimi/precompact.mjs:52, hooks/vscode-copilot/precompact.mjs:40, hooks/gemini-cli/precompress.mjs:40) repeat the same omission.
On 13 of the 17 known platforms the platform tool-namer produces a prefixed name, so the snapshot's bare ctx_search does not match (for example opencode context-mode_ctx_search, gemini-cli/kimi mcp__context-mode__ctx_search, claude-code mcp__plugin_context-mode_context-mode__ctx_search).
Concrete injection site
The opencode adapter pushes this snapshot straight into model context: src/adapters/opencode/plugin.ts:631-639 calls buildResumeSnapshot(events, { compactCount }) then output.context.push(snapshot), so on opencode the model receives the bare-name hint verbatim.
The plumbing already exists
The sibling sessionstart hooks wire the platform tool name correctly (for example hooks/gemini-cli/sessionstart.mjs passes toolNamer into the directive builder), and snapshot.ts documents searchTool?: string; // platform-specific tool name, default "ctx_search". The PreCompact path was never wired into it, so after a compact context-mode can hand the model two retrieval hints that disagree about the tool name.
Impact (kept narrow)
Recoverable, not a hard break. The separate sessionstart directive still carries the correct platform name as a fallback, and bare-name platforms (cursor/codex/openclaw/pi) are already correct. Where the snapshot is the hint actually surfaced (for example the opencode injection above), the model gets a retrieval cue for a tool name it cannot call.
Reproduce
Verified on main at e27c143. Drives the real exported buildResumeSnapshot plus createToolNamer/KNOWN_PLATFORMS from hooks/core/tool-naming.mjs:
import { buildResumeSnapshot } from "./src/session/snapshot.ts";
import { createToolNamer, KNOWN_PLATFORMS } from "./hooks/core/tool-naming.mjs";
// Real StoredEvent shape (the shape precompact.mjs passes from the DB).
const events = [
{ type: "file_edit", category: "file", data: "src/server.ts", priority: 1, created_at: "2026-07-27T00:00:00Z" },
{ type: "decision", category: "decision", data: "use FTS5 for retrieval", priority: 1, created_at: "2026-07-27T00:00:01Z" },
];
// Mirror hooks/precompact.mjs:49 (no searchTool).
const snap = buildResumeSnapshot(events, { compactCount: 1 });
console.log((snap.match(/\bctx_search\(/g) || []).length); // 2 bare-name calls
console.log(snap.includes("mcp__context-mode__")); // false (no prefix)
// 13 of 17 platform namers produce a name that differs from the bare hint.
const differ = KNOWN_PLATFORMS.filter(p => createToolNamer(p)("ctx_search") !== "ctx_search");
console.log(differ.length); // 13
Output: 2 bare ctx_search( calls, no platform prefix, 13/17 platform namers differ from the bare name. The one-line fix emits the platform-prefixed name instead.
Fix (one opt per hook, plus the adapter)
Pass the platform name into the snapshot, mirroring the sessionstart path, at hooks/precompact.mjs:49, the four sibling hooks, and the opencode adapter call at src/adapters/opencode/plugin.ts:631:
buildResumeSnapshot(events, { compactCount, searchTool: createToolNamer(platform)("ctx_search") })
Notes
What's wrong
The PreCompact hook builds a resume snapshot that tells the model to retrieve prior context by calling
ctx_search(...), but it never passes the platform-specific tool name, so on prefixed platforms the snapshot uses a name that does not match the real tool.hooks/precompact.mjs:49callsbuildResumeSnapshot(events, { compactCount })with nosearchTool.buildResumeSnapshot(src/session/snapshot.ts) defaultssearchToolto the bare string"ctx_search", so whenever the snapshot emits a retrieval hint it uses the barectx_search(queries: [...], source: "session-events"). The per-platform PreCompact hooks (hooks/codex/precompact.mjs:44,hooks/kimi/precompact.mjs:52,hooks/vscode-copilot/precompact.mjs:40,hooks/gemini-cli/precompress.mjs:40) repeat the same omission.On 13 of the 17 known platforms the platform tool-namer produces a prefixed name, so the snapshot's bare
ctx_searchdoes not match (for example opencodecontext-mode_ctx_search, gemini-cli/kimimcp__context-mode__ctx_search, claude-codemcp__plugin_context-mode_context-mode__ctx_search).Concrete injection site
The opencode adapter pushes this snapshot straight into model context:
src/adapters/opencode/plugin.ts:631-639callsbuildResumeSnapshot(events, { compactCount })thenoutput.context.push(snapshot), so on opencode the model receives the bare-name hint verbatim.The plumbing already exists
The sibling sessionstart hooks wire the platform tool name correctly (for example
hooks/gemini-cli/sessionstart.mjspassestoolNamerinto the directive builder), andsnapshot.tsdocumentssearchTool?: string; // platform-specific tool name, default "ctx_search". The PreCompact path was never wired into it, so after a compact context-mode can hand the model two retrieval hints that disagree about the tool name.Impact (kept narrow)
Recoverable, not a hard break. The separate sessionstart directive still carries the correct platform name as a fallback, and bare-name platforms (cursor/codex/openclaw/pi) are already correct. Where the snapshot is the hint actually surfaced (for example the opencode injection above), the model gets a retrieval cue for a tool name it cannot call.
Reproduce
Verified on
mainate27c143. Drives the real exportedbuildResumeSnapshotpluscreateToolNamer/KNOWN_PLATFORMSfromhooks/core/tool-naming.mjs:Output: 2 bare
ctx_search(calls, no platform prefix, 13/17 platform namers differ from the bare name. The one-line fix emits the platform-prefixed name instead.Fix (one opt per hook, plus the adapter)
Pass the platform name into the snapshot, mirroring the sessionstart path, at
hooks/precompact.mjs:49, the four sibling hooks, and the opencode adapter call atsrc/adapters/opencode/plugin.ts:631:Notes
gh issue create(the runtime form's debug-script/prompt/error fields do not fit a static contract bug), as [Bug]: claude-code tool naming assumes the plugin install, so standalone-MCP sessions get routing guidance naming nonexistent tools #1018 was. Happy to send this as a one-line-per-hook PR againstnextinstead.