context-mode 1.0.162 → 1.0.163
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/README.md +142 -28
- package/bin/statusline.mjs +24 -4
- package/build/adapters/antigravity/index.d.ts +1 -1
- package/build/adapters/antigravity-cli/index.d.ts +51 -0
- package/build/adapters/antigravity-cli/index.js +341 -0
- package/build/adapters/claude-code/hooks.d.ts +1 -0
- package/build/adapters/claude-code/hooks.js +3 -0
- package/build/adapters/claude-code/index.js +24 -5
- package/build/adapters/client-map.js +5 -0
- package/build/adapters/codex/hooks.d.ts +5 -1
- package/build/adapters/codex/hooks.js +5 -1
- package/build/adapters/codex/index.d.ts +9 -1
- package/build/adapters/codex/index.js +87 -5
- package/build/adapters/copilot-cli/hooks.d.ts +33 -0
- package/build/adapters/copilot-cli/hooks.js +64 -0
- package/build/adapters/copilot-cli/index.d.ts +48 -0
- package/build/adapters/copilot-cli/index.js +341 -0
- package/build/adapters/detect.d.ts +1 -1
- package/build/adapters/detect.js +71 -3
- package/build/adapters/openclaw/mcp-tools.js +1 -1
- package/build/adapters/opencode/index.js +31 -17
- package/build/adapters/opencode/zod3tov4.js +27 -6
- package/build/adapters/pi/extension.d.ts +2 -12
- package/build/adapters/pi/extension.js +114 -96
- package/build/adapters/types.d.ts +5 -4
- package/build/adapters/types.js +4 -3
- package/build/cache-heal.d.ts +48 -0
- package/build/cache-heal.js +150 -0
- package/build/cli.js +37 -97
- package/build/executor.d.ts +25 -0
- package/build/executor.js +143 -22
- package/build/opencode-plugin.js +5 -2
- package/build/routing-block.d.ts +8 -0
- package/build/routing-block.js +86 -0
- package/build/runtime.d.ts +0 -36
- package/build/runtime.js +107 -27
- package/build/search/flood-guard.d.ts +57 -0
- package/build/search/flood-guard.js +80 -0
- package/build/security.d.ts +8 -3
- package/build/security.js +155 -29
- package/build/server.d.ts +14 -0
- package/build/server.js +368 -350
- package/build/session/analytics.d.ts +1 -1
- package/build/session/analytics.js +5 -1
- package/build/session/db.js +23 -3
- package/build/session/extract.js +8 -0
- package/build/store.d.ts +1 -1
- package/build/store.js +139 -25
- package/build/tool-naming.d.ts +4 -0
- package/build/tool-naming.js +24 -0
- package/build/util/jsonc.d.ts +14 -0
- package/build/util/jsonc.js +104 -0
- package/cli.bundle.mjs +254 -252
- package/configs/antigravity/GEMINI.md +2 -2
- package/configs/antigravity-cli/hooks/hooks.json +37 -0
- package/configs/antigravity-cli/hooks.json +37 -0
- package/configs/antigravity-cli/mcp_config.json +10 -0
- package/configs/antigravity-cli/plugin.json +14 -0
- package/configs/antigravity-cli/rules/context-mode.md +77 -0
- package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
- package/configs/claude-code/CLAUDE.md +2 -2
- package/configs/codex/AGENTS.md +2 -2
- package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
- package/configs/copilot-cli/.mcp.json +12 -0
- package/configs/copilot-cli/README.md +47 -0
- package/configs/copilot-cli/hooks.json +41 -0
- package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
- package/configs/gemini-cli/GEMINI.md +2 -2
- package/configs/jetbrains-copilot/copilot-instructions.md +2 -2
- package/configs/kilo/AGENTS.md +2 -2
- package/configs/kiro/KIRO.md +2 -2
- package/configs/omp/SYSTEM.md +2 -2
- package/configs/openclaw/AGENTS.md +2 -2
- package/configs/opencode/AGENTS.md +2 -2
- package/configs/qwen-code/QWEN.md +2 -2
- package/configs/vscode-copilot/copilot-instructions.md +2 -2
- package/configs/zed/AGENTS.md +2 -2
- package/hooks/antigravity-cli/payload.mjs +98 -0
- package/hooks/antigravity-cli/posttooluse.mjs +138 -0
- package/hooks/antigravity-cli/pretooluse.mjs +78 -0
- package/hooks/antigravity-cli/stop.mjs +58 -0
- package/hooks/codex/pretooluse.mjs +14 -4
- package/hooks/codex/stop.mjs +12 -4
- package/hooks/copilot-cli/posttooluse.mjs +79 -0
- package/hooks/copilot-cli/precompact.mjs +66 -0
- package/hooks/copilot-cli/pretooluse.mjs +41 -0
- package/hooks/copilot-cli/sessionstart.mjs +121 -0
- package/hooks/copilot-cli/stop.mjs +59 -0
- package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
- package/hooks/core/codex-caps.mjs +112 -0
- package/hooks/core/formatters.mjs +158 -7
- package/hooks/core/mcp-ready.mjs +37 -8
- package/hooks/core/routing.mjs +94 -8
- package/hooks/core/tool-naming.mjs +3 -0
- package/hooks/hooks.json +12 -1
- package/hooks/pretooluse.mjs +6 -2
- package/hooks/routing-block.mjs +2 -2
- package/hooks/security.bundle.mjs +2 -1
- package/hooks/session-db.bundle.mjs +5 -5
- package/hooks/session-directive.mjs +88 -20
- package/hooks/session-extract.bundle.mjs +1 -1
- package/hooks/session-helpers.mjs +21 -0
- package/hooks/sessionstart.mjs +37 -5
- package/hooks/stop.mjs +49 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +4 -10
- package/scripts/install-antigravity-cli-plugin.mjs +141 -0
- package/server.bundle.mjs +208 -203
- package/skills/ctx-insight/SKILL.md +12 -17
- package/build/util/db-lock.d.ts +0 -65
- package/build/util/db-lock.js +0 -166
- package/insight/index.html +0 -13
- package/insight/package.json +0 -55
- package/insight/server.mjs +0 -1265
- package/insight/src/components/analytics.tsx +0 -112
- package/insight/src/components/ui/badge.tsx +0 -52
- package/insight/src/components/ui/button.tsx +0 -58
- package/insight/src/components/ui/card.tsx +0 -103
- package/insight/src/components/ui/chart.tsx +0 -371
- package/insight/src/components/ui/collapsible.tsx +0 -19
- package/insight/src/components/ui/input.tsx +0 -20
- package/insight/src/components/ui/progress.tsx +0 -83
- package/insight/src/components/ui/scroll-area.tsx +0 -55
- package/insight/src/components/ui/separator.tsx +0 -23
- package/insight/src/components/ui/table.tsx +0 -114
- package/insight/src/components/ui/tabs.tsx +0 -82
- package/insight/src/components/ui/tooltip.tsx +0 -64
- package/insight/src/lib/api.ts +0 -144
- package/insight/src/lib/utils.ts +0 -6
- package/insight/src/main.tsx +0 -22
- package/insight/src/routeTree.gen.ts +0 -189
- package/insight/src/router.tsx +0 -19
- package/insight/src/routes/__root.tsx +0 -55
- package/insight/src/routes/enterprise.tsx +0 -316
- package/insight/src/routes/index.tsx +0 -1482
- package/insight/src/routes/knowledge.tsx +0 -221
- package/insight/src/routes/knowledge_.$dbHash.$sourceId.tsx +0 -137
- package/insight/src/routes/search.tsx +0 -97
- package/insight/src/routes/sessions.tsx +0 -179
- package/insight/src/routes/sessions_.$dbHash.$sessionId.tsx +0 -181
- package/insight/src/styles.css +0 -104
- package/insight/tsconfig.json +0 -29
- package/insight/vite.config.ts +0 -19
|
@@ -7,10 +7,54 @@
|
|
|
7
7
|
|
|
8
8
|
import { writeFileSync } from "node:fs";
|
|
9
9
|
|
|
10
|
+
// ── Leg-boundary helpers (#780) ──
|
|
11
|
+
// The current Claude Code session_id persists across `--continue` legs, so the
|
|
12
|
+
// events array can carry rows written in a PRIOR leg. The last `session_start`
|
|
13
|
+
// lifecycle event (emitted by the prior leg's SessionStart, before this leg's
|
|
14
|
+
// directive is built — sessionstart.mjs:206/258, 277/293) is the boundary that
|
|
15
|
+
// opened the current leg. Rows with created_at < boundary are prior-leg.
|
|
16
|
+
function computeLegBoundary(events) {
|
|
17
|
+
let boundary = null;
|
|
18
|
+
for (const ev of events) {
|
|
19
|
+
if (ev.category === "session_start" && ev.created_at) boundary = ev.created_at;
|
|
20
|
+
}
|
|
21
|
+
return boundary;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// True when an event was written in a leg strictly before the current one.
|
|
25
|
+
// No boundary (first leg) → nothing is prior-leg, so everything stays current.
|
|
26
|
+
function isPriorLeg(ev, boundary) {
|
|
27
|
+
return boundary != null && ev.created_at != null && ev.created_at < boundary;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ── Data References sizing (#840) ──
|
|
31
|
+
// Inlining every captured tool-output verbatim defeats context-mode's own
|
|
32
|
+
// raw-bytes-stay-out principle. Inline only a small recent window; reference
|
|
33
|
+
// large blobs with a one-line pointer. The full payloads stay queryable in
|
|
34
|
+
// FTS5 via ctx_search(source: "session-events").
|
|
35
|
+
const DATA_REF_INLINE_MAX = 8; // most-recent captures rendered inline
|
|
36
|
+
const DATA_REF_ENTRY_MAX = 150; // per-entry char cap before it is referenced
|
|
37
|
+
|
|
38
|
+
function renderDataReferences(entries, push, searchHint) {
|
|
39
|
+
const recent = entries.slice(-DATA_REF_INLINE_MAX);
|
|
40
|
+
for (const ev of recent) {
|
|
41
|
+
const raw = ev.data ?? "";
|
|
42
|
+
const text = raw.length > DATA_REF_ENTRY_MAX
|
|
43
|
+
? `${raw.substring(0, DATA_REF_ENTRY_MAX - 3)}… (${raw.length} bytes — query ${searchHint})`
|
|
44
|
+
: raw;
|
|
45
|
+
push(`- ${text}`);
|
|
46
|
+
}
|
|
47
|
+
const older = entries.length - recent.length;
|
|
48
|
+
if (older > 0) {
|
|
49
|
+
push(`- … ${older} older captures kept in the sandbox — query ${searchHint}.`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
10
53
|
// ── Group events by category and extract metadata ──
|
|
11
54
|
export function groupEvents(events) {
|
|
12
55
|
const grouped = {};
|
|
13
56
|
let lastPrompt = "";
|
|
57
|
+
const legBoundary = computeLegBoundary(events);
|
|
14
58
|
for (const ev of events) {
|
|
15
59
|
if (ev.category === "prompt") {
|
|
16
60
|
lastPrompt = ev.data;
|
|
@@ -25,13 +69,13 @@ export function groupEvents(events) {
|
|
|
25
69
|
const base = path?.split(/[/\\]/).pop()?.trim();
|
|
26
70
|
if (base && !base.includes("*")) fileNames.add(base);
|
|
27
71
|
}
|
|
28
|
-
return { grouped, lastPrompt, fileNames };
|
|
72
|
+
return { grouped, lastPrompt, fileNames, legBoundary };
|
|
29
73
|
}
|
|
30
74
|
|
|
31
75
|
// ── Write session events as markdown for FTS5 auto-indexing ──
|
|
32
76
|
// Structured with H2 headings per category — optimal for FTS5 chunking.
|
|
33
77
|
export function writeSessionEventsFile(events, eventsPath) {
|
|
34
|
-
const { grouped, lastPrompt, fileNames } = groupEvents(events);
|
|
78
|
+
const { grouped, lastPrompt, fileNames, legBoundary } = groupEvents(events);
|
|
35
79
|
|
|
36
80
|
const lines = [];
|
|
37
81
|
lines.push("# Session Resume");
|
|
@@ -149,10 +193,23 @@ export function writeSessionEventsFile(events, eventsPath) {
|
|
|
149
193
|
}
|
|
150
194
|
|
|
151
195
|
if (grouped.subagent?.length > 0) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
196
|
+
// #780 — only current-leg subagent events are "current". Prior-leg ones
|
|
197
|
+
// are reframed as history so stale [completed]/[launched] labels are not
|
|
198
|
+
// re-injected as if the agent ran in this conversation.
|
|
199
|
+
const currentSub = grouped.subagent.filter(ev => !isPriorLeg(ev, legBoundary));
|
|
200
|
+
const priorSub = grouped.subagent.filter(ev => isPriorLeg(ev, legBoundary));
|
|
201
|
+
if (currentSub.length > 0) {
|
|
202
|
+
lines.push("## Subagent Tasks");
|
|
203
|
+
lines.push("");
|
|
204
|
+
for (const ev of currentSub) lines.push(`- ${ev.data}`);
|
|
205
|
+
lines.push("");
|
|
206
|
+
}
|
|
207
|
+
if (priorSub.length > 0) {
|
|
208
|
+
lines.push("## Subagent Tasks (earlier session — not current)");
|
|
209
|
+
lines.push("");
|
|
210
|
+
for (const ev of priorSub) lines.push(`- ${ev.data}`);
|
|
211
|
+
lines.push("");
|
|
212
|
+
}
|
|
156
213
|
}
|
|
157
214
|
|
|
158
215
|
if (grouped.skill?.length > 0) {
|
|
@@ -180,7 +237,11 @@ export function writeSessionEventsFile(events, eventsPath) {
|
|
|
180
237
|
if (grouped.data?.length > 0) {
|
|
181
238
|
lines.push("## Data References");
|
|
182
239
|
lines.push("");
|
|
183
|
-
|
|
240
|
+
renderDataReferences(
|
|
241
|
+
grouped.data,
|
|
242
|
+
(l) => lines.push(l),
|
|
243
|
+
'ctx_search(source: "session-events")',
|
|
244
|
+
);
|
|
184
245
|
lines.push("");
|
|
185
246
|
}
|
|
186
247
|
|
|
@@ -212,8 +273,10 @@ export function writeSessionEventsFile(events, eventsPath) {
|
|
|
212
273
|
|
|
213
274
|
// ── Build session guide — actionable narrative for LLM to continue from ──
|
|
214
275
|
export function buildSessionDirective(source, eventMeta, toolNamer) {
|
|
215
|
-
const { grouped, lastPrompt, fileNames } = eventMeta;
|
|
276
|
+
const { grouped, lastPrompt, fileNames, legBoundary } = eventMeta;
|
|
216
277
|
const isCompact = source === "compact";
|
|
278
|
+
const searchTool = toolNamer ? toolNamer("ctx_search") : "ctx_search";
|
|
279
|
+
const dataSearchHint = `${searchTool}(source: "session-events")`;
|
|
217
280
|
|
|
218
281
|
let block = `\n<session_knowledge source="${isCompact ? "compact" : "continue"}">`;
|
|
219
282
|
block += `\n<session_guide>`;
|
|
@@ -333,14 +396,23 @@ export function buildSessionDirective(source, eventMeta, toolNamer) {
|
|
|
333
396
|
block += `\n`;
|
|
334
397
|
}
|
|
335
398
|
|
|
336
|
-
// 9. Subagent tasks
|
|
399
|
+
// 9. Subagent tasks — #780: prior-leg events are NOT current work. Render
|
|
400
|
+
// only current-leg subagents under "Subagent Tasks"; reframe prior-leg
|
|
401
|
+
// ones so stale [completed]/[launched] labels don't read as this leg's.
|
|
337
402
|
if (grouped.subagent?.length > 0) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
403
|
+
const fmt = (ev) => ev.data.length > 120 ? ev.data.substring(0, 117) + "..." : ev.data;
|
|
404
|
+
const currentSub = grouped.subagent.filter(ev => !isPriorLeg(ev, legBoundary));
|
|
405
|
+
const priorSub = grouped.subagent.filter(ev => isPriorLeg(ev, legBoundary));
|
|
406
|
+
if (currentSub.length > 0) {
|
|
407
|
+
block += `\n## Subagent Tasks`;
|
|
408
|
+
for (const ev of currentSub) block += `\n- ${fmt(ev)}`;
|
|
409
|
+
block += `\n`;
|
|
410
|
+
}
|
|
411
|
+
if (priorSub.length > 0) {
|
|
412
|
+
block += `\n## Subagent Tasks (earlier session — not current)`;
|
|
413
|
+
for (const ev of priorSub) block += `\n- ${fmt(ev)}`;
|
|
414
|
+
block += `\n`;
|
|
342
415
|
}
|
|
343
|
-
block += `\n`;
|
|
344
416
|
}
|
|
345
417
|
|
|
346
418
|
// 10. Skills invoked
|
|
@@ -363,13 +435,10 @@ export function buildSessionDirective(source, eventMeta, toolNamer) {
|
|
|
363
435
|
block += `\n`;
|
|
364
436
|
}
|
|
365
437
|
|
|
366
|
-
// 12. Data references
|
|
438
|
+
// 12. Data references — #840: reference (don't inline) large tool-outputs.
|
|
367
439
|
if (grouped.data?.length > 0) {
|
|
368
440
|
block += `\n## Data References`;
|
|
369
|
-
|
|
370
|
-
const text = ev.data.length > 150 ? ev.data.substring(0, 147) + "..." : ev.data;
|
|
371
|
-
block += `\n- ${text}`;
|
|
372
|
-
}
|
|
441
|
+
renderDataReferences(grouped.data, (l) => { block += `\n${l}`; }, dataSearchHint);
|
|
373
442
|
block += `\n`;
|
|
374
443
|
}
|
|
375
444
|
|
|
@@ -417,7 +486,6 @@ export function buildSessionDirective(source, eventMeta, toolNamer) {
|
|
|
417
486
|
// Search on demand — detailed data lives in FTS5
|
|
418
487
|
block += `\n<session_search>`;
|
|
419
488
|
block += `\nDetailed session data is indexed in context-mode FTS5 (source: "session-events").`;
|
|
420
|
-
const searchTool = toolNamer ? toolNamer("ctx_search") : "ctx_search";
|
|
421
489
|
block += `\nUse ${searchTool}(queries: [...], source: "session-events") when you need specifics.`;
|
|
422
490
|
block += `\nDo NOT call ctx_index() — data is already indexed.`;
|
|
423
491
|
block += `\n</session_search>`;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
function i(t){return t==null?"":String(t)}function v(t){return t==null?"":typeof t=="string"?t:JSON.stringify(t)}function _(t){let e=String(t.tool_response??""),n=String(t.tool_input?.command??"");if(e.startsWith("context-mode:")||n.startsWith('echo "context-mode:')||n.startsWith("echo 'context-mode:"))return!1;let r=t.tool_output?.isError===!0||t.tool_output?.is_error===!0;return t.tool_name==="Bash"&&/exit code [1-9]|error:|Error:|FAIL|failed/i.test(e)||r}function R(t){if(!t)return[];let e=[];for(let r of t.split(/\r?\n/)){if(r.startsWith("*** Add File: ")){e.push({path:r.slice(14).trim(),type:"file_write"});continue}if(r.startsWith("*** Update File: ")){e.push({path:r.slice(17).trim(),type:"file_edit"});continue}if(r.startsWith("*** Delete File: ")){e.push({path:r.slice(17).trim(),type:"file_edit"});continue}r.startsWith("*** Move to: ")&&e.push({path:r.slice(13).trim(),type:"file_edit"})}let n=new Set;return e.filter(r=>{if(!r.path)return!1;let o=`${r.type}:${r.path}`;return n.has(o)?!1:(n.add(o),!0)})}function S(t){return/(?:^|[/\\])\.claude[/\\]plans[/\\]/.test(t)}function $(t){let{tool_name:e,tool_input:n,tool_response:r}=t,o=[];if(e==="Read"){let s=String(n.file_path??"");return(/(?:CLAUDE|AGENTS(?:\.override)?|GEMINI|QWEN|KIRO)\.md$/i.test(s)||/\/copilot-instructions\.md$/i.test(s)||/\/context-mode\.mdc$/i.test(s)||/\.claude[\\/]/i.test(s)||/[\\/]memor(?:y|ies)[\\/][^\\/]+\.md$/i.test(s))&&(o.push({type:"rule",category:"rule",data:i(s),priority:1}),r&&r.length>0&&o.push({type:"rule_content",category:"rule",data:i(r),priority:1})),o.push({type:"file_read",category:"file",data:i(s),priority:1}),o}if(e==="Edit"){let s=String(n.file_path??"");return o.push({type:"file_edit",category:"file",data:i(s),priority:1}),o}if(e==="NotebookEdit"){let s=String(n.notebook_path??"");return o.push({type:"file_edit",category:"file",data:i(s),priority:1}),o}if(e==="Write"){let s=String(n.file_path??"");return o.push({type:"file_write",category:"file",data:i(s),priority:1}),o}if(e==="apply_patch"){if(_(t))return[];let s=R(String(n.command??n.patch??""));for(let a of s)o.push({type:a.type,category:"file",data:i(a.path),priority:1});return o}if(e==="Glob"){let s=String(n.pattern??"");return o.push({type:"file_glob",category:"file",data:i(s),priority:3}),o}if(e==="Grep"){let s=String(n.pattern??""),a=String(n.path??"");return o.push({type:"file_search",category:"file",data:i(`${s} in ${a}`),priority:3}),o}return o}function I(t){if(t.tool_name!=="Bash")return[];let n=String(t.tool_input.command??"").match(/\bcd\s+("([^"]+)"|'([^']+)'|(\S+))/);if(!n)return[];let r=n[2]??n[3]??n[4]??"";return[{type:"cwd",category:"cwd",data:i(r),priority:2}]}function C(t){let{tool_response:e}=t,n=String(e??"");return _(t)?[{type:"error_tool",category:"error",data:i(n),priority:2}]:[]}var E=[{pattern:/\bgit\s+checkout\b/,operation:"branch"},{pattern:/\bgit\s+commit\b/,operation:"commit"},{pattern:/\bgit\s+merge\s+\S+/,operation:"merge"},{pattern:/\bgit\s+rebase\b/,operation:"rebase"},{pattern:/\bgit\s+stash\b/,operation:"stash"},{pattern:/\bgit\s+push\b/,operation:"push"},{pattern:/\bgit\s+pull\b/,operation:"pull"},{pattern:/\bgit\s+log\b/,operation:"log"},{pattern:/\bgit\s+diff\b/,operation:"diff"},{pattern:/\bgit\s+status\b/,operation:"status"},{pattern:/\bgit\s+branch\b/,operation:"branch"},{pattern:/\bgit\s+reset\b/,operation:"reset"},{pattern:/\bgit\s+add\b/,operation:"add"},{pattern:/\bgit\s+cherry-pick\b/,operation:"cherry-pick"},{pattern:/\bgit\s+tag\b/,operation:"tag"},{pattern:/\bgit\s+fetch\b/,operation:"fetch"},{pattern:/\bgit\s+clone\b/,operation:"clone"},{pattern:/\bgit\s+worktree\b/,operation:"worktree"}];function H(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??""),n=P(e),r;if(n&&n.operation&&(r=E.find(s=>s.operation===n.operation)),r||(r=E.find(s=>s.pattern.test(e))),!r)return[];let o=[];if(n?.scopedDir&&o.push({type:"cwd",category:"cwd",data:i(n.scopedDir),priority:2}),r.operation==="commit"){let s=N(e);if(s)return o.push({type:"git_commit",category:"git",data:i(s),priority:2}),o}return o.push({type:"git",category:"git",data:i(r.operation),priority:2}),o}function O(t){return typeof t!="string"||t.length===0?t:t==="~"?w():t.startsWith("~/")?w()+t.slice(1):t}function w(){try{return process.env.HOME||process.env.USERPROFILE||(process.env.HOMEDRIVE&&process.env.HOMEPATH?process.env.HOMEDRIVE+process.env.HOMEPATH:"")||"~"}catch{return"~"}}function P(t){let e=x(t),n=0;for(;n<e.length&&M(e[n]);)n++;for(;n<e.length&&e[n]!=="git"&&!e[n].endsWith("/git")&&L(e[n]);)n++;if(n>=e.length||e[n]!=="git"&&!e[n].endsWith("/git"))return null;n++;let r=null,o=null;for(;n<e.length;){let s=e[n];if(s==="-C"||s==="--directory"){r=e[n+1]??null,n+=2;continue}if(s.startsWith("--directory=")){r=s.slice(12),n++;continue}if(s.length>0&&s[0]==="-"){n++;continue}o=s;break}return r&&(r=O(r)),{scopedDir:r,operation:o}}function M(t){if(t.length===0)return!1;let e=!1;for(let n=0;n<t.length;n++){let r=t.charCodeAt(n);if(n===0){if(!(r>=65&&r<=90||r===95))return!1}else if(r===61){e=!0;break}else if(!(r>=65&&r<=90||r>=48&&r<=57||r===95))return!1}return e}function L(t){switch(t){case"sudo":case"doas":case"env":case"exec":case"time":return!0;default:return!1}}function x(t){let e=[],n=t.length,r=0;for(;r<n;){for(;r<n&&(t[r]===" "||t[r]===" ");)r++;if(r>=n)break;let o="";for(;r<n&&t[r]!==" "&&t[r]!==" ";){let s=t[r];if(s==='"'||s==="'"){let a=s;for(r++;r<n&&t[r]!==a;)t[r]==="\\"&&r+1<n?(o+=t[r+1],r+=2):(o+=t[r],r++);r<n&&r++}else s==="\\"&&r+1<n?(o+=t[r+1],r+=2):(o+=s,r++)}e.push(o)}return e}function N(t){let e=x(t),n="--message=";for(let r=0;r<e.length;r++){let o=e[r];if(o.length>n.length&&o.startsWith(n)){let s=o.slice(n.length);return s.length>0?s:null}if(o==="--message"){let s=e[r+1];return s&&s.length>0?s:null}if(o.length>=2&&o[0]==="-"&&o[1]!=="-"&&o[o.length-1]==="m"&&j(o,1)){let s=e[r+1];return s&&s.length>0?s:null}}return null}function j(t,e){if(e>=t.length)return!1;for(let n=e;n<t.length;n++){let r=t.charCodeAt(n);if(r<97||r>122)return!1}return!0}function W(t){return new Set(["TodoWrite","TaskCreate","TaskUpdate"]).has(t.tool_name)?[{type:t.tool_name==="TaskUpdate"?"task_update":t.tool_name==="TaskCreate"?"task_create":"task",category:"task",data:i(JSON.stringify(t.tool_input)),priority:1}]:[]}function D(t){let e=2166136261;for(let n=0;n<t.length;n++)e^=t.charCodeAt(n),e=Math.imul(e,16777619);return(e>>>0).toString(16).padStart(8,"0")}function B(t){let e=t.tool_input.plan;if(typeof e=="string"&&e.length>0)return e;let n=t.tool_response;if(typeof n=="string"&&n.length>0)try{let r=JSON.parse(n);if(r&&typeof r=="object"&&typeof r.plan=="string")return r.plan}catch{}return null}function F(t){if(t.tool_name==="EnterPlanMode")return[{type:"plan_enter",category:"plan",data:"entered plan mode",priority:2}];if(t.tool_name==="ExitPlanMode"){let e=[],n=t.tool_input.allowedPrompts,r=Array.isArray(n)&&n.length>0?`exited plan mode (allowed: ${v(n.map(a=>typeof a=="object"&&a!==null&&"prompt"in a?String(a.prompt):String(a)).join(", "))})`:"exited plan mode",o=B(t);typeof o=="string"&&o.length>0&&(r+=` plan_bytes:${o.length} plan_hash:${D(o)}`),e.push({type:"plan_exit",category:"plan",data:i(r),priority:2});let s=String(t.tool_response??"").toLowerCase();return s.includes("approved")||s.includes("approve")?e.push({type:"plan_approved",category:"plan",data:"plan approved by user",priority:1}):(s.includes("rejected")||s.includes("decline")||s.includes("denied"))&&e.push({type:"plan_rejected",category:"plan",data:i(`plan rejected: ${t.tool_response??""}`),priority:2}),e}if(t.tool_name==="Write"||t.tool_name==="Edit"){let e=String(t.tool_input.file_path??"");if(S(e))return[{type:"plan_file_write",category:"plan",data:i(`plan file: ${e.split(/[/\\]/).pop()??e}`),priority:2}]}return t.tool_name==="apply_patch"?_(t)?[]:R(String(t.tool_input.command??t.tool_input.patch??"")).filter(n=>S(n.path)).map(n=>({type:"plan_file_write",category:"plan",data:i(`plan file: ${n.path.split(/[/\\]/).pop()??n.path}`),priority:2})):[]}var U=[/\bsource\s+\S*activate\b/,/\bexport\s+\w+=/,/\bnvm\s+use\b/,/\bpyenv\s+(shell|local|global)\b/,/\bconda\s+activate\b/,/\brbenv\s+(shell|local|global)\b/,/\bnpm\s+install\b/,/\bnpm\s+ci\b/,/\bpip\s+install\b/,/\bbun\s+install\b/,/\byarn\s+(add|install)\b/,/\bpnpm\s+(add|install)\b/,/\bcargo\s+(install|add)\b/,/\bgo\s+(install|get)\b/,/\brustup\b/,/\basdf\b/,/\bvolta\b/,/\bdeno\s+install\b/];function G(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??"");if(!U.some(o=>o.test(e)))return[];let r=e.replace(/\bexport\s+(\w+)=\S*/g,"export $1=***");return[{type:"env",category:"env",data:i(r),priority:2}]}function K(t){if(t.tool_name!=="Skill")return[];let e=String(t.tool_input.skill??"");return[{type:"skill",category:"skill",data:i(e),priority:2}]}function J(t){if(!t.tool_response?.includes("Error")&&!t.tool_output?.isError)return[];let e=String(t.tool_response||""),n=[/not supported/i,/cannot/i,/does not support/i,/FAIL/i,/refused/i,/permission denied/i,/incompatible/i];for(let r of n){let o=e.match(r);if(o){let s=e.toLowerCase().indexOf(o[0].toLowerCase()),a=e.slice(Math.max(0,s-50),Math.min(e.length,s+200)).trim();return[{type:"constraint_discovered",category:"constraint",data:i(a),priority:2}]}}return[]}function q(t){if(t.tool_name!=="Agent")return[];let e=i(String(t.tool_input.prompt??t.tool_input.description??"")),n=t.tool_response?i(String(t.tool_response)):"",r=n.length>0;return[{type:r?"subagent_completed":"subagent_launched",category:"subagent",data:i(r?`[completed] ${e} \u2192 ${n}`:`[launched] ${e}`),priority:r?2:3}]}function z(t){let{tool_name:e,tool_input:n,tool_response:r}=t;if(!e.startsWith("mcp__"))return[];let o=e.split("__"),s=o[o.length-1]||e,a=Object.values(n).find(l=>typeof l=="string"),p=a?`: ${i(String(a))}`:"",c=r&&r.length>0?`
|
|
2
|
-
response: ${i(r)}`:"";return[{type:"mcp",category:"mcp",data:i(`${s}${p}${c}`),priority:3}]}var V=2048;function Q(t,e){if(Buffer.byteLength(t,"utf8")<=e)return{value:t,truncated:!1};let n=Buffer.from(t,"utf8"),r=e;for(;r>0&&(n[r]&192)===128;)r--;return{value:n.subarray(0,r).toString("utf8"),truncated:!0}}var X=/(authorization|auth_token|access_token|refresh_token|bearer|token|secret|password|passwd|pwd|api[-_]?key|apikey|cookie|set-cookie|signature|private[-_]?key|client[-_]?secret|x[-_]?api[-_]?key)/i,Y="[REDACTED]";function b(t,e=new WeakSet){if(t==null||typeof t!="object")return t;if(e.has(t))return"[CIRCULAR]";e.add(t);let n;if(Array.isArray(t))n=t.map(r=>b(r,e));else{let r={};for(let[o,s]of Object.entries(t))X.test(o)?r[o]=Y:r[o]=b(s,e);n=r}return e.delete(t),n}function Z(t){let{tool_name:e,tool_input:n}=t;if(!e.startsWith("mcp__"))return[];let r=b(n??{}),o;try{o=JSON.stringify(r)}catch{o="{}"}let{value:s,truncated:a}=Q(o,V),p=a?`{"tool_name":${JSON.stringify(e)},"params_raw":${JSON.stringify(s)},"truncated":true}`:`{"tool_name":${JSON.stringify(e)},"params":${s}}`;return[{type:"mcp_tool_call",category:"mcp_tool_call",data:i(p),priority:4}]}function tt(t){if(t.tool_name!=="AskUserQuestion")return[];let e=t.tool_input.questions,n=Array.isArray(e)&&e.length>0?String(e[0].question??""):"",r=String(t.tool_response??""),o="";try{let c=JSON.parse(r)?.answers;if(c&&typeof c=="object"){let l=g=>typeof g=="string"?g:Array.isArray(g)?g.filter(h=>typeof h=="string").join(" | "):"",u=n?l(c[n]):"";u?o=u:o=Object.values(c).map(l).filter(h=>h.length>0).join(" | ")}}catch{}let s=i(o),a=n?`Q: ${i(n)} \u2192 A: ${s}`:`answer: ${s}`;return[{type:"decision_question",category:"decision",data:i(a),priority:2}]}function et(t){if(t.tool_name!=="Agent")return[];if(!t.tool_response||t.tool_response.length===0)return[];let e=t.tool_response.length>500?t.tool_response.slice(0,500):t.tool_response;return[{type:"agent_finding",category:"agent-finding",data:i(e),priority:2}]}function nt(t){let e=[v(t.tool_input),i(t.tool_response)].join(" ");if(e.length===0)return[];let n=new Set,r=e.match(/https?:\/\/[^\s)]+/g);if(r)for(let c of r)c=c.replace(/["'})\],;.]+$/,""),/localhost|127\.0\.0\.1/i.test(c)||n.add(c);let o=e.match(/(?<!\w)#(\d+)/g);if(o)for(let c of o)n.add(c);if(n.size===0)return[];let s,a=i(t.tool_response).match(/Fetched and indexed[^\(]*\(([\d.]+)\s*KB\)/i);if(a){let c=Number(a[1]);Number.isFinite(c)&&c>0&&(s=Math.round(c*1024))}let p={type:"external_ref",category:"external-ref",data:i(Array.from(n).join(", ")),priority:3};return s!==void 0&&(p.bytes_avoided=s),[p]}function rt(t){if(t.tool_name==="EnterWorktree"){let e=String(t.tool_input.name??"unnamed");return[{type:"worktree",category:"env",data:i(`entered worktree: ${e}`),priority:2}]}if(t.tool_name==="ExitWorktree"){let e=!!t.tool_input.discard_changes;return[{type:"worktree_exit",category:"env",data:i(`exited worktree (discard_changes:${e})`),priority:2}]}return[]}function ot(t){if(typeof t!="string"||t.length===0)return null;let e=t.indexOf("://");if(e<0)return null;let n=e+3;if(n>=t.length)return null;let r=t.length;for(let s=n;s<t.length;s++){let a=t.charCodeAt(s);if(a===47||a===63||a===35){r=s;break}}let o=t.slice(n,r);return o.length>0?o:null}function st(t){if(t.tool_name!=="WebFetch")return[];let e=t.tool_response;if(typeof e!="string"||e.length===0)return[];let n;try{n=JSON.parse(e)}catch{return[]}if(!n||typeof n!="object")return[];let r=n,o=[];if(typeof r.code=="number"&&o.push(`code:${r.code}`),typeof r.bytes=="number"&&o.push(`bytes:${r.bytes}`),typeof r.durationMs=="number"&&o.push(`durMs:${r.durationMs}`),typeof r.url=="string"){let s=ot(r.url);s&&o.push(`host:${s}`)}return o.length===0?[]:[{type:"webfetch_metadata",category:"data",data:i(o.join(" ")),priority:3}]}function it(t){if(t.tool_name!=="Bash")return[];let e=t.tool_response;if(typeof e!="string"||e.length===0)return[];let n;try{n=JSON.parse(e)}catch{return[]}if(!n||typeof n!="object")return[];let r=n;if(!(typeof r.interrupted=="boolean"||typeof r.stderr=="string"||typeof r.returnCodeInterpretation=="string"))return[];let s=[];return typeof r.interrupted=="boolean"&&s.push(`interrupted:${r.interrupted}`),typeof r.returnCodeInterpretation=="string"&&s.push(`rcInterp:${r.returnCodeInterpretation.slice(0,80)}`),typeof r.stderr=="string"&&s.push(`stderrBytes:${r.stderr.length}`),[{type:"bash_outcome",category:"data",data:i(s.join(" ")),priority:3}]}function at(t){if(t.tool_name!=="Read")return[];let e=t.tool_response;if(typeof e!="string"||e.length===0)return[];let n;try{n=JSON.parse(e)}catch{return[]}if(!n||typeof n!="object")return[];let r=n,o=r.type;if(o!=="text"&&o!=="image")return[];let s=[`type:${o}`];if(o==="text")typeof r.numLines=="number"&&s.push(`lines:${r.numLines}`),typeof r.totalLines=="number"&&s.push(`totalLines:${r.totalLines}`),typeof r.startLine=="number"&&s.push(`start:${r.startLine}`);else{typeof r.originalSize=="number"&&s.push(`origSize:${r.originalSize}`);let a=r.dimensions;if(a&&typeof a=="object"){let p=a;typeof p.width=="number"&&typeof p.height=="number"&&s.push(`dims:${p.width}x${p.height}`)}}return[{type:"file_read_metadata",category:"data",data:i(s.join(" ")),priority:3}]}var y={"claude-opus-4-8":{input:5,output:25,cache_write:6.25,cache_read:.5},"claude-opus-4-7":{input:5,output:25,cache_write:6.25,cache_read:.5},"claude-sonnet-4-6":{input:3,output:15,cache_write:3.75,cache_read:.3},"claude-haiku-4-5":{input:1,output:5,cache_write:1.25,cache_read:.1},default:{input:3,output:15,cache_write:3.75,cache_read:.3}};function ct(t,e){let n=[t.tool_input?.model,t.model,e.model],r=Object.keys(y).filter(o=>o!=="default");for(let o of n)if(!(typeof o!="string"||o.length===0)){if(o in y)return o;for(let s of r)if(o.startsWith(s))return s}return"default"}function pt(t,e,n,r,o){let s=y[t]??y.default;return(e*s.input+n*s.output+r*s.cache_write+o*s.cache_read)/1e6}function lt(t){if(t.tool_name!=="Task")return[];let e=t.tool_response;if(typeof e!="string"||e.length===0)return[];let n;try{n=JSON.parse(e)}catch{return[]}if(!n||typeof n!="object")return[];let r=n,o=r.usage&&typeof r.usage=="object"?r.usage:{};if(!(typeof r.totalTokens=="number"||typeof r.totalDurationMs=="number"||typeof o.input_tokens=="number"||typeof o.output_tokens=="number"||typeof o.service_tier=="string"))return[];let a=[];typeof r.totalTokens=="number"&&a.push(`totalTokens:${r.totalTokens}`),typeof r.totalDurationMs=="number"&&a.push(`totalDurMs:${r.totalDurationMs}`),typeof o.input_tokens=="number"&&a.push(`tokens_in:${o.input_tokens}`),typeof o.output_tokens=="number"&&a.push(`tokens_out:${o.output_tokens}`),typeof o.cache_creation_input_tokens=="number"&&a.push(`cache_create:${o.cache_creation_input_tokens}`),typeof o.cache_read_input_tokens=="number"&&a.push(`cache_read:${o.cache_read_input_tokens}`),typeof o.service_tier=="string"&&a.push(`tier:${o.service_tier.slice(0,32)}`);let p=typeof o.input_tokens=="number"?o.input_tokens:0,c=typeof o.output_tokens=="number"?o.output_tokens:0,l=typeof o.cache_creation_input_tokens=="number"?o.cache_creation_input_tokens:0,u=typeof o.cache_read_input_tokens=="number"?o.cache_read_input_tokens:0;if(p>0||c>0||l>0||u>0){let h=ct(t,r),A=pt(h,p,c,l,u);a.push(`cost_usd:${A.toFixed(6).replace(/0+$/,"").replace(/\.$/,".0")}`)}return[{type:"agent_usage",category:"cost",data:i(a.join(" ")),priority:2}]}var T=/[,;,;、،]/u,ut=15,ft=500;function dt(t){if(m.test(t)||!k.test(t)||!T.test(t))return!1;let e=[...t].length;return e>=ut&&e<=ft}function gt(t){let e=t.trim();return dt(e)?[{type:"decision",category:"decision",data:i(t),priority:2}]:[]}var ht=8,_t=120,yt=new RegExp("\\p{L}+\\s+\\p{L}+","u"),mt=new RegExp("\\p{L}{6,}","u");function bt(t){let e=t.split(/[.!\n。!]/u)[0].trim();if(m.test(e)||T.test(e)||!k.test(e))return!1;let n=[...e].length;return n<ht||n>_t?!1:yt.test(e)||mt.test(e)}function kt(t){let e=t.trim();return bt(e)?[{type:"role",category:"role",data:i(t),priority:3}]:[]}var m=/[??؟¿]/u,k=new RegExp("\\p{L}","u"),St=60;function Et(t){if(m.test(t)||!k.test(t))return!1;let e=[...t].length;return e>0&&e<St}function wt(t){let e=t.trim();if(!e)return[];let n;return m.test(e)?n="investigate":Et(e)&&(n="implement"),n?[{type:"intent",category:"intent",data:i(n),priority:4}]:[]}var vt=/^(?:\/goal\s+|(?:goal|objective)\s*:\s*)(.+)$/is;function Rt(t){let e=t.trim();if(!e)return[];let n=e.match(vt);if(!n)return[];let r=n[1].trim();return r?[{type:"goal",category:"goal",data:i(r),priority:4}]:[]}var xt=/(?:\bError\s*:|\bException\s*:|\bTraceback\b|\bat\s+\S+\s*\([^)]*:\d+:\d+\))/u,Tt=/[✓✔✅☑🎉]/u,At=/^\s*(?:fixed|resolved)\s*:/iu;function $t(t){let e=[];return Tt.test(t)||At.test(t)?(e.push({type:"blocker_resolved",category:"blocked-on",data:i(t),priority:2}),e):(xt.test(t)&&e.push({type:"blocker",category:"blocked-on",data:i(t),priority:2}),e)}function It(t){return t.length<=1024?[]:[{type:"data",category:"data",data:i(t),priority:4}]}var f=null;function Ct(t){let{tool_name:e,tool_response:n}=t,r=String(n??"");if(_(t))return f={tool:e,error:r.slice(0,200),callsSince:0},[];if(!f)return[];if(f.callsSince++,f.callsSince>10)return f=null,[];if(!!_(t))return[];let s=e===f.tool,a=f.tool==="Read"&&(e==="Edit"||e==="Write"||e==="apply_patch");if(s||a){let p={type:"error_resolved",category:"error-resolution",data:i(`Error in ${f.tool}: ${f.error} \u2192 Fixed`),priority:2};return f=null,[p]}return[]}function Wt(){f=null}var d=[];function Ht(t){return`${t.length}:${t.slice(0,20)}`}function Ot(t){let{tool_name:e,tool_input:n}=t,r=Ht(JSON.stringify(n).slice(0,200));if(d.push({tool:e,inputHash:r}),d.length>50&&d.splice(0,d.length-50),d.length<3)return[];let o=0;for(let s=d.length-1;s>=0&&(d[s].tool===e&&d[s].inputHash===r);s--)o++;return o>=3?(d.splice(d.length-o),[{type:"retry_detected",category:"iteration-loop",data:i(`${e} called ${o} times with similar input`),priority:2}]):[]}function Dt(){d.length=0}var Pt={run_shell_command:"Bash",read_file:"Read",read_many_files:"Read",grep_search:"Grep",search_file_content:"Grep",web_fetch:"WebFetch",write_file:"Write",edit:"Edit",glob:"Glob",todo_write:"TodoWrite",ask_user_question:"AskUserQuestion",list_directory:"LS",save_memory:"Memory",skill:"Skill",exit_plan_mode:"ExitPlanMode",agent:"Agent",bash:"Bash",view:"Read",grep:"Grep",fetch:"WebFetch",shell:"Bash",shell_command:"Bash",exec_command:"Bash","container.exec":"Bash",local_shell:"Bash",grep_files:"Grep"};function Mt(t){let e=Pt[t.tool_name];return!e||e===t.tool_name?t:{...t,tool_name:e}}function Bt(t){try{let e=Mt(t),n=[];return n.push(...$(e)),n.push(...I(e)),n.push(...C(e)),n.push(...H(e)),n.push(...G(e)),n.push(...W(e)),n.push(...F(e)),n.push(...K(e)),n.push(...q(e)),n.push(...z(e)),n.push(...Z(e)),n.push(...tt(e)),n.push(...J(e)),n.push(...rt(e)),n.push(...st(e)),n.push(...it(e)),n.push(...at(e)),n.push(...lt(e)),n.push(...et(e)),n.push(...nt(e)),n.push(...Ct(e)),n.push(...Ot(e)),n}catch{return[]}}function Ft(t){try{let e=[];return e.push(...jt(t)),e.push(...gt(t)),e.push(...kt(t)),e.push(...wt(t)),e.push(...Rt(t)),e.push(...$t(t)),e.push(...It(t)),e}catch{return[]}}function Ut(t){if(!t||typeof t!="object")return[];let e=t,n=[],r=e.mcp_servers,o=null;return r&&typeof r=="object"&&!Array.isArray(r)&&(o=Object.keys(r),n.push(`mcp_count:${o.length}`),o.length>0&&n.push(`mcp_servers:${o.slice(0,8).join(",")}`)),typeof e.model=="string"&&n.push(`model:${e.model.slice(0,64)}`),typeof e.permission_mode=="string"&&n.push(`permission_mode:${e.permission_mode.slice(0,32)}`),n.length===0?[]:[{type:"session_settings_snapshot",category:"env",data:i(n.join(" ")),priority:2}]}var Lt=["Latin","Cyrillic","Arabic","Han","Hangul","Hiragana","Katakana","Devanagari","Hebrew","Thai","Greek"],Nt={prompt_length:0,prompt_word_count:0,prompt_uppercase_ratio:0,prompt_file_ref_count:0,prompt_path_ref_count:0,prompt_script_primary:null,prompt_script_count:0,prompt_question_glyph_count:0,prompt_code_block_count:0,prompt_url_count:0,prompt_word_tokens:[]};function Gt(t){if(typeof t!="string"||t.length===0)return{...Nt,prompt_word_tokens:[]};let e=t.match(new RegExp("\\p{L}+","gu"))??[],n=(t.match(new RegExp("\\p{Lu}","gu"))??[]).length,r=e.join("").length,o=(t.match(/```/g)??[]).length,s={};for(let l of Lt){let u=new RegExp(`\\p{Script=${l}}`,"gu"),g=(t.match(u)??[]).length;g>0&&(s[l]=g)}let a=Object.entries(s).sort((l,u)=>u[1]-l[1])[0]?.[0]??null,p=new Set,c=[];for(let l of e){if(l.length<3)continue;let u=l.toLowerCase();p.has(u)||(p.add(u),c.push(u))}return{prompt_length:t.length,prompt_word_count:e.length,prompt_uppercase_ratio:r===0?0:n/r,prompt_file_ref_count:(t.match(/(\w+\/)+\w+\.\w+/g)??[]).length,prompt_path_ref_count:(t.match(/\.{0,2}\/[\w\/.-]+/g)??[]).length,prompt_script_primary:a,prompt_script_count:Object.keys(s).length,prompt_question_glyph_count:(t.match(/[??؟]/gu)??[]).length,prompt_code_block_count:Math.floor(o/2),prompt_url_count:(t.match(/https?:\/\/[^\s]+/gu)??[]).length,prompt_word_tokens:c}}function jt(t){if(typeof t!="string"||t.length===0)return[];let e=0;for(;e<t.length;){let o=t.charCodeAt(e);if(o!==32&&o!==9)break;e++}if(e+5>t.length)return[];if(t.slice(e,e+5)!=="/plan")return[];if(e+5<t.length){let o=t.charCodeAt(e+5);if(!(o===32||o===9||o===10||o===13))return[]}let n=t.slice(e+5).trim(),r=n.length>0?`plan via /plan slash: ${n.slice(0,120)}`:"plan via /plan slash";return[{type:"plan_enter",category:"plan",data:i(r),priority:2}]}export{Bt as extractEvents,Ut as extractSessionSettings,Ft as extractUserEvents,Gt as extractUserPromptFeatures,Wt as resetErrorResolutionState,Dt as resetIterationLoopState};
|
|
2
|
+
response: ${i(r)}`:"";return[{type:"mcp",category:"mcp",data:i(`${s}${p}${c}`),priority:3}]}var V=2048;function Q(t,e){if(Buffer.byteLength(t,"utf8")<=e)return{value:t,truncated:!1};let n=Buffer.from(t,"utf8"),r=e;for(;r>0&&(n[r]&192)===128;)r--;return{value:n.subarray(0,r).toString("utf8"),truncated:!0}}var X=/(authorization|auth_token|access_token|refresh_token|bearer|token|secret|password|passwd|pwd|api[-_]?key|apikey|cookie|set-cookie|signature|private[-_]?key|client[-_]?secret|x[-_]?api[-_]?key)/i,Y="[REDACTED]";function b(t,e=new WeakSet){if(t==null||typeof t!="object")return t;if(e.has(t))return"[CIRCULAR]";e.add(t);let n;if(Array.isArray(t))n=t.map(r=>b(r,e));else{let r={};for(let[o,s]of Object.entries(t))X.test(o)?r[o]=Y:r[o]=b(s,e);n=r}return e.delete(t),n}function Z(t){let{tool_name:e,tool_input:n}=t;if(!e.startsWith("mcp__"))return[];let r=b(n??{}),o;try{o=JSON.stringify(r)}catch{o="{}"}let{value:s,truncated:a}=Q(o,V),p=a?`{"tool_name":${JSON.stringify(e)},"params_raw":${JSON.stringify(s)},"truncated":true}`:`{"tool_name":${JSON.stringify(e)},"params":${s}}`;return[{type:"mcp_tool_call",category:"mcp_tool_call",data:i(p),priority:4}]}function tt(t){if(t.tool_name!=="AskUserQuestion")return[];let e=t.tool_input.questions,n=Array.isArray(e)&&e.length>0?String(e[0].question??""):"",r=String(t.tool_response??""),o="";try{let c=JSON.parse(r)?.answers;if(c&&typeof c=="object"){let l=g=>typeof g=="string"?g:Array.isArray(g)?g.filter(h=>typeof h=="string").join(" | "):"",u=n?l(c[n]):"";u?o=u:o=Object.values(c).map(l).filter(h=>h.length>0).join(" | ")}}catch{}let s=i(o),a=n?`Q: ${i(n)} \u2192 A: ${s}`:`answer: ${s}`;return[{type:"decision_question",category:"decision",data:i(a),priority:2}]}function et(t){if(t.tool_name!=="Agent")return[];if(!t.tool_response||t.tool_response.length===0)return[];let e=t.tool_response.length>500?t.tool_response.slice(0,500):t.tool_response;return[{type:"agent_finding",category:"agent-finding",data:i(e),priority:2}]}function nt(t){let e=[v(t.tool_input),i(t.tool_response)].join(" ");if(e.length===0)return[];let n=new Set,r=e.match(/https?:\/\/[^\s)]+/g);if(r)for(let c of r)c=c.replace(/["'})\],;.]+$/,""),/localhost|127\.0\.0\.1/i.test(c)||n.add(c);let o=e.match(/(?<!\w)#(\d+)/g);if(o)for(let c of o)n.add(c);if(n.size===0)return[];let s,a=i(t.tool_response).match(/Fetched and indexed[^\(]*\(([\d.]+)\s*KB\)/i);if(a){let c=Number(a[1]);Number.isFinite(c)&&c>0&&(s=Math.round(c*1024))}let p={type:"external_ref",category:"external-ref",data:i(Array.from(n).join(", ")),priority:3};return s!==void 0&&(p.bytes_avoided=s),[p]}function rt(t){if(t.tool_name==="EnterWorktree"){let e=String(t.tool_input.name??"unnamed");return[{type:"worktree",category:"env",data:i(`entered worktree: ${e}`),priority:2}]}if(t.tool_name==="ExitWorktree"){let e=!!t.tool_input.discard_changes;return[{type:"worktree_exit",category:"env",data:i(`exited worktree (discard_changes:${e})`),priority:2}]}return[]}function ot(t){if(typeof t!="string"||t.length===0)return null;let e=t.indexOf("://");if(e<0)return null;let n=e+3;if(n>=t.length)return null;let r=t.length;for(let s=n;s<t.length;s++){let a=t.charCodeAt(s);if(a===47||a===63||a===35){r=s;break}}let o=t.slice(n,r);return o.length>0?o:null}function st(t){if(t.tool_name!=="WebFetch")return[];let e=t.tool_response;if(typeof e!="string"||e.length===0)return[];let n;try{n=JSON.parse(e)}catch{return[]}if(!n||typeof n!="object")return[];let r=n,o=[];if(typeof r.code=="number"&&o.push(`code:${r.code}`),typeof r.bytes=="number"&&o.push(`bytes:${r.bytes}`),typeof r.durationMs=="number"&&o.push(`durMs:${r.durationMs}`),typeof r.url=="string"){let s=ot(r.url);s&&o.push(`host:${s}`)}return o.length===0?[]:[{type:"webfetch_metadata",category:"data",data:i(o.join(" ")),priority:3}]}function it(t){if(t.tool_name!=="Bash")return[];let e=t.tool_response;if(typeof e!="string"||e.length===0)return[];let n;try{n=JSON.parse(e)}catch{return[]}if(!n||typeof n!="object")return[];let r=n;if(!(typeof r.interrupted=="boolean"||typeof r.stderr=="string"||typeof r.returnCodeInterpretation=="string"))return[];let s=[];return typeof r.interrupted=="boolean"&&s.push(`interrupted:${r.interrupted}`),typeof r.returnCodeInterpretation=="string"&&s.push(`rcInterp:${r.returnCodeInterpretation.slice(0,80)}`),typeof r.stderr=="string"&&s.push(`stderrBytes:${r.stderr.length}`),[{type:"bash_outcome",category:"data",data:i(s.join(" ")),priority:3}]}function at(t){if(t.tool_name!=="Read")return[];let e=t.tool_response;if(typeof e!="string"||e.length===0)return[];let n;try{n=JSON.parse(e)}catch{return[]}if(!n||typeof n!="object")return[];let r=n,o=r.type;if(o!=="text"&&o!=="image")return[];let s=[`type:${o}`];if(o==="text")typeof r.numLines=="number"&&s.push(`lines:${r.numLines}`),typeof r.totalLines=="number"&&s.push(`totalLines:${r.totalLines}`),typeof r.startLine=="number"&&s.push(`start:${r.startLine}`);else{typeof r.originalSize=="number"&&s.push(`origSize:${r.originalSize}`);let a=r.dimensions;if(a&&typeof a=="object"){let p=a;typeof p.width=="number"&&typeof p.height=="number"&&s.push(`dims:${p.width}x${p.height}`)}}return[{type:"file_read_metadata",category:"data",data:i(s.join(" ")),priority:3}]}var y={"claude-opus-4-8":{input:5,output:25,cache_write:6.25,cache_read:.5},"claude-opus-4-7":{input:5,output:25,cache_write:6.25,cache_read:.5},"claude-sonnet-4-6":{input:3,output:15,cache_write:3.75,cache_read:.3},"claude-haiku-4-5":{input:1,output:5,cache_write:1.25,cache_read:.1},default:{input:3,output:15,cache_write:3.75,cache_read:.3}};function ct(t,e){let n=[t.tool_input?.model,t.model,e.model],r=Object.keys(y).filter(o=>o!=="default");for(let o of n)if(!(typeof o!="string"||o.length===0)){if(o in y)return o;for(let s of r)if(o.startsWith(s))return s}return"default"}function pt(t,e,n,r,o){let s=y[t]??y.default;return(e*s.input+n*s.output+r*s.cache_write+o*s.cache_read)/1e6}function lt(t){if(t.tool_name!=="Task")return[];let e=t.tool_response;if(typeof e!="string"||e.length===0)return[];let n;try{n=JSON.parse(e)}catch{return[]}if(!n||typeof n!="object")return[];let r=n,o=r.usage&&typeof r.usage=="object"?r.usage:{};if(!(typeof r.totalTokens=="number"||typeof r.totalDurationMs=="number"||typeof o.input_tokens=="number"||typeof o.output_tokens=="number"||typeof o.service_tier=="string"))return[];let a=[];typeof r.totalTokens=="number"&&a.push(`totalTokens:${r.totalTokens}`),typeof r.totalDurationMs=="number"&&a.push(`totalDurMs:${r.totalDurationMs}`),typeof o.input_tokens=="number"&&a.push(`tokens_in:${o.input_tokens}`),typeof o.output_tokens=="number"&&a.push(`tokens_out:${o.output_tokens}`),typeof o.cache_creation_input_tokens=="number"&&a.push(`cache_create:${o.cache_creation_input_tokens}`),typeof o.cache_read_input_tokens=="number"&&a.push(`cache_read:${o.cache_read_input_tokens}`),typeof o.service_tier=="string"&&a.push(`tier:${o.service_tier.slice(0,32)}`);let p=typeof o.input_tokens=="number"?o.input_tokens:0,c=typeof o.output_tokens=="number"?o.output_tokens:0,l=typeof o.cache_creation_input_tokens=="number"?o.cache_creation_input_tokens:0,u=typeof o.cache_read_input_tokens=="number"?o.cache_read_input_tokens:0;if(p>0||c>0||l>0||u>0){let h=ct(t,r),A=pt(h,p,c,l,u);a.push(`cost_usd:${A.toFixed(6).replace(/0+$/,"").replace(/\.$/,".0")}`)}return[{type:"agent_usage",category:"cost",data:i(a.join(" ")),priority:2}]}var T=/[,;,;、،]/u,ut=15,ft=500;function dt(t){if(m.test(t)||!k.test(t)||!T.test(t))return!1;let e=[...t].length;return e>=ut&&e<=ft}function gt(t){let e=t.trim();return dt(e)?[{type:"decision",category:"decision",data:i(t),priority:2}]:[]}var ht=8,_t=120,yt=new RegExp("\\p{L}+\\s+\\p{L}+","u"),mt=new RegExp("\\p{L}{6,}","u");function bt(t){let e=t.split(/[.!\n。!]/u)[0].trim();if(m.test(e)||T.test(e)||!k.test(e))return!1;let n=[...e].length;return n<ht||n>_t?!1:yt.test(e)||mt.test(e)}function kt(t){let e=t.trim();return bt(e)?[{type:"role",category:"role",data:i(t),priority:3}]:[]}var m=/[??؟¿]/u,k=new RegExp("\\p{L}","u"),St=60;function Et(t){if(m.test(t)||!k.test(t))return!1;let e=[...t].length;return e>0&&e<St}function wt(t){let e=t.trim();if(!e)return[];let n;return m.test(e)?n="investigate":Et(e)&&(n="implement"),n?[{type:"intent",category:"intent",data:i(n),priority:4}]:[]}var vt=/^(?:\/goal\s+|(?:goal|objective)\s*:\s*)(.+)$/is;function Rt(t){let e=t.trim();if(!e)return[];let n=e.match(vt);if(!n)return[];let r=n[1].trim();return r?[{type:"goal",category:"goal",data:i(r),priority:4}]:[]}var xt=/(?:\bError\s*:|\bException\s*:|\bTraceback\b|\bat\s+\S+\s*\([^)]*:\d+:\d+\))/u,Tt=/[✓✔✅☑🎉]/u,At=/^\s*(?:fixed|resolved)\s*:/iu;function $t(t){let e=[];return Tt.test(t)||At.test(t)?(e.push({type:"blocker_resolved",category:"blocked-on",data:i(t),priority:2}),e):(xt.test(t)&&e.push({type:"blocker",category:"blocked-on",data:i(t),priority:2}),e)}function It(t){return t.length<=1024?[]:[{type:"data",category:"data",data:i(t),priority:4}]}var f=null;function Ct(t){let{tool_name:e,tool_response:n}=t,r=String(n??"");if(_(t))return f={tool:e,error:r.slice(0,200),callsSince:0},[];if(!f)return[];if(f.callsSince++,f.callsSince>10)return f=null,[];if(!!_(t))return[];let s=e===f.tool,a=f.tool==="Read"&&(e==="Edit"||e==="Write"||e==="apply_patch");if(s||a){let p={type:"error_resolved",category:"error-resolution",data:i(`Error in ${f.tool}: ${f.error} \u2192 Fixed`),priority:2};return f=null,[p]}return[]}function Wt(){f=null}var d=[];function Ht(t){return`${t.length}:${t.slice(0,20)}`}function Ot(t){let{tool_name:e,tool_input:n}=t,r=Ht(JSON.stringify(n).slice(0,200));if(d.push({tool:e,inputHash:r}),d.length>50&&d.splice(0,d.length-50),d.length<3)return[];let o=0;for(let s=d.length-1;s>=0&&(d[s].tool===e&&d[s].inputHash===r);s--)o++;return o>=3?(d.splice(d.length-o),[{type:"retry_detected",category:"iteration-loop",data:i(`${e} called ${o} times with similar input`),priority:2}]):[]}function Dt(){d.length=0}var Pt={run_shell_command:"Bash",read_file:"Read",read_many_files:"Read",grep_search:"Grep",search_file_content:"Grep",web_fetch:"WebFetch",write_file:"Write",edit:"Edit",glob:"Glob",todo_write:"TodoWrite",ask_user_question:"AskUserQuestion",list_directory:"LS",save_memory:"Memory",skill:"Skill",exit_plan_mode:"ExitPlanMode",agent:"Agent",bash:"Bash",view:"Read",grep:"Grep",fetch:"WebFetch",shell:"Bash",shell_command:"Bash",exec_command:"Bash","container.exec":"Bash",local_shell:"Bash",grep_files:"Grep",run_command:"Bash",view_file:"Read",read_url_content:"WebFetch",list_dir:"LS",search_web:"WebSearch"};function Mt(t){let e=Pt[t.tool_name];return!e||e===t.tool_name?t:{...t,tool_name:e}}function Bt(t){try{let e=Mt(t),n=[];return n.push(...$(e)),n.push(...I(e)),n.push(...C(e)),n.push(...H(e)),n.push(...G(e)),n.push(...W(e)),n.push(...F(e)),n.push(...K(e)),n.push(...q(e)),n.push(...z(e)),n.push(...Z(e)),n.push(...tt(e)),n.push(...J(e)),n.push(...rt(e)),n.push(...st(e)),n.push(...it(e)),n.push(...at(e)),n.push(...lt(e)),n.push(...et(e)),n.push(...nt(e)),n.push(...Ct(e)),n.push(...Ot(e)),n}catch{return[]}}function Ft(t){try{let e=[];return e.push(...jt(t)),e.push(...gt(t)),e.push(...kt(t)),e.push(...wt(t)),e.push(...Rt(t)),e.push(...$t(t)),e.push(...It(t)),e}catch{return[]}}function Ut(t){if(!t||typeof t!="object")return[];let e=t,n=[],r=e.mcp_servers,o=null;return r&&typeof r=="object"&&!Array.isArray(r)&&(o=Object.keys(r),n.push(`mcp_count:${o.length}`),o.length>0&&n.push(`mcp_servers:${o.slice(0,8).join(",")}`)),typeof e.model=="string"&&n.push(`model:${e.model.slice(0,64)}`),typeof e.permission_mode=="string"&&n.push(`permission_mode:${e.permission_mode.slice(0,32)}`),n.length===0?[]:[{type:"session_settings_snapshot",category:"env",data:i(n.join(" ")),priority:2}]}var Lt=["Latin","Cyrillic","Arabic","Han","Hangul","Hiragana","Katakana","Devanagari","Hebrew","Thai","Greek"],Nt={prompt_length:0,prompt_word_count:0,prompt_uppercase_ratio:0,prompt_file_ref_count:0,prompt_path_ref_count:0,prompt_script_primary:null,prompt_script_count:0,prompt_question_glyph_count:0,prompt_code_block_count:0,prompt_url_count:0,prompt_word_tokens:[]};function Gt(t){if(typeof t!="string"||t.length===0)return{...Nt,prompt_word_tokens:[]};let e=t.match(new RegExp("\\p{L}+","gu"))??[],n=(t.match(new RegExp("\\p{Lu}","gu"))??[]).length,r=e.join("").length,o=(t.match(/```/g)??[]).length,s={};for(let l of Lt){let u=new RegExp(`\\p{Script=${l}}`,"gu"),g=(t.match(u)??[]).length;g>0&&(s[l]=g)}let a=Object.entries(s).sort((l,u)=>u[1]-l[1])[0]?.[0]??null,p=new Set,c=[];for(let l of e){if(l.length<3)continue;let u=l.toLowerCase();p.has(u)||(p.add(u),c.push(u))}return{prompt_length:t.length,prompt_word_count:e.length,prompt_uppercase_ratio:r===0?0:n/r,prompt_file_ref_count:(t.match(/(\w+\/)+\w+\.\w+/g)??[]).length,prompt_path_ref_count:(t.match(/\.{0,2}\/[\w\/.-]+/g)??[]).length,prompt_script_primary:a,prompt_script_count:Object.keys(s).length,prompt_question_glyph_count:(t.match(/[??؟]/gu)??[]).length,prompt_code_block_count:Math.floor(o/2),prompt_url_count:(t.match(/https?:\/\/[^\s]+/gu)??[]).length,prompt_word_tokens:c}}function jt(t){if(typeof t!="string"||t.length===0)return[];let e=0;for(;e<t.length;){let o=t.charCodeAt(e);if(o!==32&&o!==9)break;e++}if(e+5>t.length)return[];if(t.slice(e,e+5)!=="/plan")return[];if(e+5<t.length){let o=t.charCodeAt(e+5);if(!(o===32||o===9||o===10||o===13))return[]}let n=t.slice(e+5).trim(),r=n.length>0?`plan via /plan slash: ${n.slice(0,120)}`:"plan via /plan slash";return[{type:"plan_enter",category:"plan",data:i(r),priority:2}]}export{Bt as extractEvents,Ut as extractSessionSettings,Ft as extractUserEvents,Gt as extractUserPromptFeatures,Wt as resetErrorResolutionState,Dt as resetIterationLoopState};
|
|
@@ -143,6 +143,19 @@ export const GEMINI_OPTS = {
|
|
|
143
143
|
sessionIdEnv: undefined,
|
|
144
144
|
};
|
|
145
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Antigravity CLI (`agy`) platform options. Shares the Gemini-family session
|
|
148
|
+
* root (~/.gemini/context-mode/sessions). agy supplies the conversation id and
|
|
149
|
+
* workspace path inside the hook payload (mapped to session_id/cwd before these
|
|
150
|
+
* opts are consulted), so no env-var fallbacks are needed.
|
|
151
|
+
*/
|
|
152
|
+
export const ANTIGRAVITY_CLI_OPTS = {
|
|
153
|
+
configDir: ".gemini",
|
|
154
|
+
configDirEnv: undefined,
|
|
155
|
+
projectDirEnv: undefined,
|
|
156
|
+
sessionIdEnv: undefined,
|
|
157
|
+
};
|
|
158
|
+
|
|
146
159
|
/** VS Code Copilot platform options. */
|
|
147
160
|
export const VSCODE_OPTS = {
|
|
148
161
|
configDir: ".vscode",
|
|
@@ -151,6 +164,14 @@ export const VSCODE_OPTS = {
|
|
|
151
164
|
sessionIdEnv: undefined,
|
|
152
165
|
};
|
|
153
166
|
|
|
167
|
+
/** GitHub Copilot CLI platform options. */
|
|
168
|
+
export const COPILOT_OPTS = {
|
|
169
|
+
configDir: ".copilot",
|
|
170
|
+
configDirEnv: "COPILOT_HOME",
|
|
171
|
+
projectDirEnv: undefined,
|
|
172
|
+
sessionIdEnv: undefined,
|
|
173
|
+
};
|
|
174
|
+
|
|
154
175
|
/** Cursor platform options. */
|
|
155
176
|
export const CURSOR_OPTS = {
|
|
156
177
|
configDir: ".cursor",
|
package/hooks/sessionstart.mjs
CHANGED
|
@@ -42,7 +42,7 @@ await runHook(async () => {
|
|
|
42
42
|
const { createSessionLoaders, attributeAndInsertEvents } = await import("./session-loaders.mjs");
|
|
43
43
|
const { join, dirname } = await import("node:path");
|
|
44
44
|
const { fileURLToPath } = await import("node:url");
|
|
45
|
-
const { readFileSync, unlinkSync, readdirSync, rmSync, lstatSync } = await import("node:fs");
|
|
45
|
+
const { readFileSync, unlinkSync, readdirSync, rmSync, lstatSync, realpathSync, symlinkSync } = await import("node:fs");
|
|
46
46
|
|
|
47
47
|
const detectedPlatform = detectPlatformFromEnv();
|
|
48
48
|
const toolNamer = createToolNamer(detectedPlatform);
|
|
@@ -398,12 +398,44 @@ await runHook(async () => {
|
|
|
398
398
|
const now = Date.now();
|
|
399
399
|
for (const d of readdirSync(cacheParent)) {
|
|
400
400
|
if (d === myDir) continue;
|
|
401
|
+
const oldDir = join(cacheParent, d);
|
|
401
402
|
try {
|
|
402
|
-
const st = lstatSync(
|
|
403
|
-
|
|
404
|
-
|
|
403
|
+
const st = lstatSync(oldDir);
|
|
404
|
+
let danglingBreadcrumb = false;
|
|
405
|
+
if (st.isSymbolicLink()) {
|
|
406
|
+
try {
|
|
407
|
+
realpathSync(oldDir);
|
|
408
|
+
} catch {
|
|
409
|
+
danglingBreadcrumb = true;
|
|
410
|
+
}
|
|
405
411
|
}
|
|
406
|
-
|
|
412
|
+
if (danglingBreadcrumb || now - st.mtimeMs > ONE_HOUR) {
|
|
413
|
+
rmSync(oldDir, { recursive: true, force: true });
|
|
414
|
+
// Leave a breadcrumb symlink (junction on Windows) in the
|
|
415
|
+
// removed version's place so sessions that loaded hooks
|
|
416
|
+
// from it before an auto-update keep resolving their
|
|
417
|
+
// scripts instead of erroring on every hook call until
|
|
418
|
+
// restart (#814, #807). Also fires when the entry was
|
|
419
|
+
// itself a stale breadcrumb, re-pointing it at the live
|
|
420
|
+
// root (a chain of updates would otherwise leave links
|
|
421
|
+
// targeting intermediate versions that no longer exist).
|
|
422
|
+
// The fresh mtime makes #644's lstat age gate protect it
|
|
423
|
+
// for the next hour, and the next sweep refreshes it
|
|
424
|
+
// again. Same pattern as healCacheMidSession (server.ts)
|
|
425
|
+
// and postinstall.mjs.
|
|
426
|
+
try {
|
|
427
|
+
symlinkSync(pluginRoot, oldDir, process.platform === "win32" ? "junction" : undefined);
|
|
428
|
+
} catch { /* best effort — plain delete is the pre-#814 behaviour */ }
|
|
429
|
+
}
|
|
430
|
+
} catch {
|
|
431
|
+
// On Windows, a dangling junction can fail before we can read
|
|
432
|
+
// its own mtime. Treat that as a stale breadcrumb and try to
|
|
433
|
+
// repoint it at the live root; failures remain best-effort.
|
|
434
|
+
try {
|
|
435
|
+
rmSync(oldDir, { recursive: true, force: true });
|
|
436
|
+
symlinkSync(pluginRoot, oldDir, process.platform === "win32" ? "junction" : undefined);
|
|
437
|
+
} catch { /* skip */ }
|
|
438
|
+
}
|
|
407
439
|
}
|
|
408
440
|
}
|
|
409
441
|
}
|
package/hooks/stop.mjs
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "./suppress-stderr.mjs";
|
|
3
|
+
import "./ensure-deps.mjs";
|
|
4
|
+
/**
|
|
5
|
+
* Claude Code Stop hook — record turn-end state for continuity.
|
|
6
|
+
*
|
|
7
|
+
* Stop fires when Claude is about to finish the current assistant turn. This is
|
|
8
|
+
* not a true session shutdown event, so record a turn_end marker and never ask
|
|
9
|
+
* Claude to continue.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readStdin, parseStdin, getSessionId, getSessionDBPath, getInputProjectDir } from "./session-helpers.mjs";
|
|
13
|
+
import { createSessionLoaders } from "./session-loaders.mjs";
|
|
14
|
+
import { dirname } from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
|
|
17
|
+
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const { loadSessionDB } = createSessionLoaders(HOOK_DIR);
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const raw = await readStdin();
|
|
22
|
+
const input = parseStdin(raw);
|
|
23
|
+
const projectDir = getInputProjectDir(input);
|
|
24
|
+
|
|
25
|
+
const { SessionDB } = await loadSessionDB();
|
|
26
|
+
const dbPath = getSessionDBPath(undefined, projectDir);
|
|
27
|
+
const db = new SessionDB({ dbPath });
|
|
28
|
+
const sessionId = getSessionId(input);
|
|
29
|
+
|
|
30
|
+
db.ensureSession(sessionId, projectDir);
|
|
31
|
+
const payload = {
|
|
32
|
+
stop_hook_active: input.stop_hook_active ?? false,
|
|
33
|
+
last_assistant_message: typeof input.last_assistant_message === "string"
|
|
34
|
+
? input.last_assistant_message.slice(0, 2000)
|
|
35
|
+
: null,
|
|
36
|
+
};
|
|
37
|
+
db.insertEvent(sessionId, {
|
|
38
|
+
type: "turn_end",
|
|
39
|
+
category: "session",
|
|
40
|
+
data: JSON.stringify(payload),
|
|
41
|
+
priority: 1,
|
|
42
|
+
}, "Stop");
|
|
43
|
+
|
|
44
|
+
db.close();
|
|
45
|
+
} catch {
|
|
46
|
+
// Claude Code hooks must not block the session.
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
process.stdout.write("{}\n");
|
package/openclaw.plugin.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Context Mode",
|
|
4
4
|
"kind": "tool",
|
|
5
5
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.163",
|
|
7
7
|
"sandbox": {
|
|
8
8
|
"mode": "permissive",
|
|
9
9
|
"filesystem_access": "full",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.163",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP plugin that saves 98% of your context window. Works with Claude Code, Gemini CLI, VS Code Copilot, OpenCode, and Codex CLI. Sandboxed code execution, FTS5 knowledge base, and intent-driven search.",
|
|
6
6
|
"author": "Mert Koseoğlu",
|
|
@@ -62,14 +62,6 @@
|
|
|
62
62
|
"build",
|
|
63
63
|
"hooks",
|
|
64
64
|
"configs",
|
|
65
|
-
"insight/server.mjs",
|
|
66
|
-
"insight/package.json",
|
|
67
|
-
"insight/tsconfig.json",
|
|
68
|
-
"insight/vite.config.ts",
|
|
69
|
-
"insight/tailwind.config.ts",
|
|
70
|
-
"insight/postcss.config.js",
|
|
71
|
-
"insight/index.html",
|
|
72
|
-
"insight/src",
|
|
73
65
|
"server.bundle.mjs",
|
|
74
66
|
"cli.bundle.mjs",
|
|
75
67
|
"bin",
|
|
@@ -80,6 +72,7 @@
|
|
|
80
72
|
"openclaw.plugin.json",
|
|
81
73
|
"start.mjs",
|
|
82
74
|
"scripts/postinstall.mjs",
|
|
75
|
+
"scripts/install-antigravity-cli-plugin.mjs",
|
|
83
76
|
"scripts/heal-better-sqlite3.mjs",
|
|
84
77
|
"scripts/heal-installed-plugins.mjs",
|
|
85
78
|
"scripts/plugin-cache-integrity.mjs",
|
|
@@ -92,7 +85,7 @@
|
|
|
92
85
|
"assert-asymmetric-drift": "node scripts/assert-asymmetric-drift.mjs",
|
|
93
86
|
"bundle": "esbuild src/server.ts --bundle --platform=node --target=node18 --format=esm --outfile=server.bundle.mjs --external:better-sqlite3 --external:turndown --external:turndown-plugin-gfm --external:@mixmark-io/domino --minify && esbuild src/cli.ts --bundle --platform=node --target=node18 --format=esm --outfile=cli.bundle.mjs --external:better-sqlite3 --minify && esbuild src/session/extract.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/session-extract.bundle.mjs --minify && esbuild src/session/snapshot.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/session-snapshot.bundle.mjs --minify && esbuild src/session/db.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/session-db.bundle.mjs --external:better-sqlite3 --minify && esbuild src/security.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/security.bundle.mjs --minify",
|
|
94
87
|
"version-sync": "node scripts/version-sync.mjs",
|
|
95
|
-
"version": "node scripts/version-sync.mjs && git add package.json .claude-plugin/plugin.json .claude-plugin/marketplace.json .cursor-plugin/plugin.json .codex-plugin/plugin.json .openclaw-plugin/openclaw.plugin.json .openclaw-plugin/package.json openclaw.plugin.json .pi/extensions/context-mode/package.json",
|
|
88
|
+
"version": "node scripts/version-sync.mjs && git add package.json .claude-plugin/plugin.json .claude-plugin/marketplace.json .cursor-plugin/plugin.json .codex-plugin/plugin.json .openclaw-plugin/openclaw.plugin.json .openclaw-plugin/package.json openclaw.plugin.json .pi/extensions/context-mode/package.json configs/antigravity-cli/plugin.json configs/copilot-cli/.github/plugin/plugin.json",
|
|
96
89
|
"prepublishOnly": "npm run build",
|
|
97
90
|
"dev": "npx tsx src/server.ts",
|
|
98
91
|
"setup": "npx tsx src/cli.ts setup",
|
|
@@ -106,6 +99,7 @@
|
|
|
106
99
|
"test:compare": "npx tsx tests/context-comparison.ts",
|
|
107
100
|
"test:ecosystem": "npx tsx tests/ecosystem-benchmark.ts",
|
|
108
101
|
"install:openclaw": "node -e \"if(process.platform==='win32'){console.error('OpenClaw install requires bash (Git Bash or WSL)');process.exit(1)}else{require('child_process').execSync('bash scripts/install-openclaw-plugin.sh',{stdio:'inherit'})}\"",
|
|
102
|
+
"install:agy": "node scripts/install-antigravity-cli-plugin.mjs",
|
|
109
103
|
"postinstall": "node scripts/postinstall.mjs"
|
|
110
104
|
},
|
|
111
105
|
"dependencies": {
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cross-platform installer: register the context-mode plugin into Antigravity CLI (agy).
|
|
4
|
+
*
|
|
5
|
+
* Replaces the former bash-only scripts/install-antigravity-cli-plugin.sh so
|
|
6
|
+
* `npm run install:agy` runs natively on Windows (PowerShell/cmd) as well as
|
|
7
|
+
* macOS/Linux. agy itself runs on Windows, so its installer must too — unlike
|
|
8
|
+
* the openclaw installer, which is genuinely POSIX-only (signals, pgrep, /tmp).
|
|
9
|
+
*
|
|
10
|
+
* The bundle (configs/antigravity-cli/) registers the context-mode MCP server,
|
|
11
|
+
* routing rule, routing skill, and bounded PreToolUse/PostToolUse/Stop hooks in
|
|
12
|
+
* one step.
|
|
13
|
+
*
|
|
14
|
+
* Usage: npm run install:agy (or: node scripts/install-antigravity-cli-plugin.mjs)
|
|
15
|
+
*/
|
|
16
|
+
import { spawnSync } from "node:child_process";
|
|
17
|
+
import { existsSync, rmSync } from "node:fs";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
import { dirname, join, resolve } from "node:path";
|
|
20
|
+
import { fileURLToPath } from "node:url";
|
|
21
|
+
|
|
22
|
+
const isWin = process.platform === "win32";
|
|
23
|
+
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
24
|
+
const bundle = resolve(repoRoot, "configs", "antigravity-cli");
|
|
25
|
+
|
|
26
|
+
// Double-quote a path so a clone dir with spaces survives the shell on both
|
|
27
|
+
// cmd.exe and /bin/sh (paths never contain literal quotes).
|
|
28
|
+
const q = (s) => `"${s}"`;
|
|
29
|
+
|
|
30
|
+
// Cross-platform "is <cmd> on PATH?" — `where` on Windows, `command -v` on POSIX.
|
|
31
|
+
// shell:true is required: `command -v` is a shell builtin and `where`/`agy` may
|
|
32
|
+
// resolve to a .cmd shim on Windows that only the shell can launch.
|
|
33
|
+
function onPath(cmd) {
|
|
34
|
+
const probe = isWin ? `where ${cmd}` : `command -v ${cmd}`;
|
|
35
|
+
return spawnSync(probe, { stdio: "ignore", shell: true }).status === 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// — preflight —
|
|
39
|
+
if (!onPath("agy")) {
|
|
40
|
+
console.error("✗ 'agy' (Antigravity CLI) not found in PATH. Install agy first, then re-run.");
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
if (!existsSync(bundle)) {
|
|
44
|
+
console.error(`✗ plugin bundle not found at ${bundle}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log("→ context-mode agy plugin installer");
|
|
49
|
+
console.log(` bundle : ${bundle}`);
|
|
50
|
+
|
|
51
|
+
// The plugin's MCP server runs the global `context-mode` binary (it needs the
|
|
52
|
+
// native better-sqlite3 dependency, which a bare clone does not have). Warn — do
|
|
53
|
+
// not silently global-install on the user's behalf.
|
|
54
|
+
const hasContextMode = onPath("context-mode");
|
|
55
|
+
if (!hasContextMode) {
|
|
56
|
+
console.error("⚠ 'context-mode' is not on PATH — the plugin's MCP server requires it.");
|
|
57
|
+
console.error(" Install it with: npm install -g context-mode");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Run `agy plugin install <bundle>`. String command + shell:true so cmd.exe can
|
|
61
|
+
// resolve agy's .cmd shim on Windows; the quoted bundle path handles spaces.
|
|
62
|
+
const install = spawnSync(`agy plugin install ${q(bundle)}`, { stdio: "inherit", shell: true });
|
|
63
|
+
if (install.status !== 0) {
|
|
64
|
+
console.error(`✗ 'agy plugin install' failed (exit ${install.status ?? "unknown"}).`);
|
|
65
|
+
process.exit(install.status ?? 1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// MCP is registered by `agy plugin install` above, straight from the bundle's
|
|
69
|
+
// native `mcp_config.json` (env-pinned CONTEXT_MODE_PLATFORM=antigravity-cli).
|
|
70
|
+
// Verified on agy 1.0.6: it logs "mcpServers : 1 processed" and writes the
|
|
71
|
+
// server into agy's plugin profile
|
|
72
|
+
// (~/.gemini/config/plugins/context-mode/mcp_config.json, env preserved).
|
|
73
|
+
// agy native validation/install does not read `.mcp.json`; keep the bundle on
|
|
74
|
+
// the single native `mcp_config.json` path to avoid manifest drift.
|
|
75
|
+
|
|
76
|
+
// agy CACHES each MCP server's tool schemas under
|
|
77
|
+
// ~/.gemini/antigravity-cli/mcp/<server>/ and does NOT refresh them on reconnect
|
|
78
|
+
// (verified on agy 1.0.6). A cache captured by an older context-mode holds the
|
|
79
|
+
// Gemini-incompatible schemas (`const` / `additionalProperties`) that make
|
|
80
|
+
// Antigravity CLI silently DROP the ctx_* tools from the model's function list —
|
|
81
|
+
// so even after upgrading, agy keeps hiding the tools and the agent works around
|
|
82
|
+
// them via shell scripts. Clear the cache so agy re-fetches the current
|
|
83
|
+
// (Gemini-safe) tools/list on its next launch.
|
|
84
|
+
const agyToolCache = join(homedir(), ".gemini", "antigravity-cli", "mcp", "context-mode");
|
|
85
|
+
let cacheCleared = false;
|
|
86
|
+
if (existsSync(agyToolCache)) {
|
|
87
|
+
try {
|
|
88
|
+
rmSync(agyToolCache, { recursive: true, force: true });
|
|
89
|
+
cacheCleared = true;
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error(`⚠ Could not clear agy's stale tool-schema cache at ${agyToolCache}: ${err.message}`);
|
|
92
|
+
console.error(" If ctx_* tools don't appear in agy, delete that folder manually and restart agy.");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Probe whether the global `context-mode` understands the antigravity-cli hooks.
|
|
97
|
+
// The shipped hook commands resolve the GLOBAL binary at runtime. A context-mode
|
|
98
|
+
// older than the release that added Antigravity CLI support may have no
|
|
99
|
+
// `antigravity-cli` HOOK_MAP entry, and the dispatcher suppresses stderr, so the
|
|
100
|
+
// hooks would be a SILENT no-op. Detect that here and tell the user instead.
|
|
101
|
+
let hooksOk = false;
|
|
102
|
+
if (hasContextMode) {
|
|
103
|
+
hooksOk = ["pretooluse", "posttooluse", "stop"].every((event) => {
|
|
104
|
+
const probe = spawnSync(`context-mode hook antigravity-cli ${event}`, {
|
|
105
|
+
input: "{}",
|
|
106
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
107
|
+
shell: true,
|
|
108
|
+
});
|
|
109
|
+
return probe.status === 0;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
console.log("");
|
|
114
|
+
// Confirm `agy plugin install` actually registered the MCP from the bundle's
|
|
115
|
+
// MCP config (it writes it into agy's plugin profile). If a future agy build
|
|
116
|
+
// skips it, fall back to a one-line manual instruction rather than silently
|
|
117
|
+
// leaving MCP unconfigured — never re-introduce a blind global-profile write.
|
|
118
|
+
const pluginMcp = join(homedir(), ".gemini", "config", "plugins", "context-mode", "mcp_config.json");
|
|
119
|
+
if (existsSync(pluginMcp)) {
|
|
120
|
+
console.log("✓ Installed the context-mode agy plugin: MCP server + routing rule + routing skill + hooks.");
|
|
121
|
+
console.log(` MCP registered from the bundle's mcp_config.json → ${pluginMcp}`);
|
|
122
|
+
} else {
|
|
123
|
+
console.log("✓ Installed the context-mode agy plugin: routing rule + routing skill + hooks.");
|
|
124
|
+
console.error("⚠ MCP server not found in agy's plugin profile. If ctx_* tools don't appear, add");
|
|
125
|
+
console.error(' { "mcpServers": { "context-mode": { "command": "context-mode" } } }');
|
|
126
|
+
console.error(" to ~/.gemini/config/mcp_config.json and restart agy.");
|
|
127
|
+
}
|
|
128
|
+
if (cacheCleared) {
|
|
129
|
+
console.log("✓ Cleared agy's stale tool-schema cache — agy re-fetches Gemini-safe schemas on next launch.");
|
|
130
|
+
}
|
|
131
|
+
if (hooksOk) {
|
|
132
|
+
console.log("✓ Antigravity CLI hooks are ACTIVE (this context-mode supports antigravity-cli).");
|
|
133
|
+
} else {
|
|
134
|
+
console.error("⚠ Antigravity CLI hooks may be INACTIVE: your global 'context-mode' is missing or too old");
|
|
135
|
+
console.error(" to handle 'context-mode hook antigravity-cli'. MCP tools + the routing rule + routing skill still work.");
|
|
136
|
+
console.error(" Enable hook enforcement/capture with: npm install -g context-mode@latest");
|
|
137
|
+
}
|
|
138
|
+
console.log("");
|
|
139
|
+
console.log(" Restart agy, then verify:");
|
|
140
|
+
console.log(' agy -p "Use the context-mode ctx_execute MCP tool to compute 7 + 5. Answer only the number." --dangerously-skip-permissions');
|
|
141
|
+
console.log(" Expected output: 12");
|