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.
Files changed (148) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.openclaw-plugin/openclaw.plugin.json +1 -1
  5. package/.openclaw-plugin/package.json +1 -1
  6. package/README.md +142 -28
  7. package/bin/statusline.mjs +24 -4
  8. package/build/adapters/antigravity/index.d.ts +1 -1
  9. package/build/adapters/antigravity-cli/index.d.ts +51 -0
  10. package/build/adapters/antigravity-cli/index.js +341 -0
  11. package/build/adapters/claude-code/hooks.d.ts +1 -0
  12. package/build/adapters/claude-code/hooks.js +3 -0
  13. package/build/adapters/claude-code/index.js +24 -5
  14. package/build/adapters/client-map.js +5 -0
  15. package/build/adapters/codex/hooks.d.ts +5 -1
  16. package/build/adapters/codex/hooks.js +5 -1
  17. package/build/adapters/codex/index.d.ts +9 -1
  18. package/build/adapters/codex/index.js +87 -5
  19. package/build/adapters/copilot-cli/hooks.d.ts +33 -0
  20. package/build/adapters/copilot-cli/hooks.js +64 -0
  21. package/build/adapters/copilot-cli/index.d.ts +48 -0
  22. package/build/adapters/copilot-cli/index.js +341 -0
  23. package/build/adapters/detect.d.ts +1 -1
  24. package/build/adapters/detect.js +71 -3
  25. package/build/adapters/openclaw/mcp-tools.js +1 -1
  26. package/build/adapters/opencode/index.js +31 -17
  27. package/build/adapters/opencode/zod3tov4.js +27 -6
  28. package/build/adapters/pi/extension.d.ts +2 -12
  29. package/build/adapters/pi/extension.js +114 -96
  30. package/build/adapters/types.d.ts +5 -4
  31. package/build/adapters/types.js +4 -3
  32. package/build/cache-heal.d.ts +48 -0
  33. package/build/cache-heal.js +150 -0
  34. package/build/cli.js +37 -97
  35. package/build/executor.d.ts +25 -0
  36. package/build/executor.js +143 -22
  37. package/build/opencode-plugin.js +5 -2
  38. package/build/routing-block.d.ts +8 -0
  39. package/build/routing-block.js +86 -0
  40. package/build/runtime.d.ts +0 -36
  41. package/build/runtime.js +107 -27
  42. package/build/search/flood-guard.d.ts +57 -0
  43. package/build/search/flood-guard.js +80 -0
  44. package/build/security.d.ts +8 -3
  45. package/build/security.js +155 -29
  46. package/build/server.d.ts +14 -0
  47. package/build/server.js +368 -350
  48. package/build/session/analytics.d.ts +1 -1
  49. package/build/session/analytics.js +5 -1
  50. package/build/session/db.js +23 -3
  51. package/build/session/extract.js +8 -0
  52. package/build/store.d.ts +1 -1
  53. package/build/store.js +139 -25
  54. package/build/tool-naming.d.ts +4 -0
  55. package/build/tool-naming.js +24 -0
  56. package/build/util/jsonc.d.ts +14 -0
  57. package/build/util/jsonc.js +104 -0
  58. package/cli.bundle.mjs +254 -252
  59. package/configs/antigravity/GEMINI.md +2 -2
  60. package/configs/antigravity-cli/hooks/hooks.json +37 -0
  61. package/configs/antigravity-cli/hooks.json +37 -0
  62. package/configs/antigravity-cli/mcp_config.json +10 -0
  63. package/configs/antigravity-cli/plugin.json +14 -0
  64. package/configs/antigravity-cli/rules/context-mode.md +77 -0
  65. package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
  66. package/configs/claude-code/CLAUDE.md +2 -2
  67. package/configs/codex/AGENTS.md +2 -2
  68. package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
  69. package/configs/copilot-cli/.mcp.json +12 -0
  70. package/configs/copilot-cli/README.md +47 -0
  71. package/configs/copilot-cli/hooks.json +41 -0
  72. package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
  73. package/configs/gemini-cli/GEMINI.md +2 -2
  74. package/configs/jetbrains-copilot/copilot-instructions.md +2 -2
  75. package/configs/kilo/AGENTS.md +2 -2
  76. package/configs/kiro/KIRO.md +2 -2
  77. package/configs/omp/SYSTEM.md +2 -2
  78. package/configs/openclaw/AGENTS.md +2 -2
  79. package/configs/opencode/AGENTS.md +2 -2
  80. package/configs/qwen-code/QWEN.md +2 -2
  81. package/configs/vscode-copilot/copilot-instructions.md +2 -2
  82. package/configs/zed/AGENTS.md +2 -2
  83. package/hooks/antigravity-cli/payload.mjs +98 -0
  84. package/hooks/antigravity-cli/posttooluse.mjs +138 -0
  85. package/hooks/antigravity-cli/pretooluse.mjs +78 -0
  86. package/hooks/antigravity-cli/stop.mjs +58 -0
  87. package/hooks/codex/pretooluse.mjs +14 -4
  88. package/hooks/codex/stop.mjs +12 -4
  89. package/hooks/copilot-cli/posttooluse.mjs +79 -0
  90. package/hooks/copilot-cli/precompact.mjs +66 -0
  91. package/hooks/copilot-cli/pretooluse.mjs +41 -0
  92. package/hooks/copilot-cli/sessionstart.mjs +121 -0
  93. package/hooks/copilot-cli/stop.mjs +59 -0
  94. package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
  95. package/hooks/core/codex-caps.mjs +112 -0
  96. package/hooks/core/formatters.mjs +158 -7
  97. package/hooks/core/mcp-ready.mjs +37 -8
  98. package/hooks/core/routing.mjs +94 -8
  99. package/hooks/core/tool-naming.mjs +3 -0
  100. package/hooks/hooks.json +12 -1
  101. package/hooks/pretooluse.mjs +6 -2
  102. package/hooks/routing-block.mjs +2 -2
  103. package/hooks/security.bundle.mjs +2 -1
  104. package/hooks/session-db.bundle.mjs +5 -5
  105. package/hooks/session-directive.mjs +88 -20
  106. package/hooks/session-extract.bundle.mjs +1 -1
  107. package/hooks/session-helpers.mjs +21 -0
  108. package/hooks/sessionstart.mjs +37 -5
  109. package/hooks/stop.mjs +49 -0
  110. package/openclaw.plugin.json +1 -1
  111. package/package.json +4 -10
  112. package/scripts/install-antigravity-cli-plugin.mjs +141 -0
  113. package/server.bundle.mjs +208 -203
  114. package/skills/ctx-insight/SKILL.md +12 -17
  115. package/build/util/db-lock.d.ts +0 -65
  116. package/build/util/db-lock.js +0 -166
  117. package/insight/index.html +0 -13
  118. package/insight/package.json +0 -55
  119. package/insight/server.mjs +0 -1265
  120. package/insight/src/components/analytics.tsx +0 -112
  121. package/insight/src/components/ui/badge.tsx +0 -52
  122. package/insight/src/components/ui/button.tsx +0 -58
  123. package/insight/src/components/ui/card.tsx +0 -103
  124. package/insight/src/components/ui/chart.tsx +0 -371
  125. package/insight/src/components/ui/collapsible.tsx +0 -19
  126. package/insight/src/components/ui/input.tsx +0 -20
  127. package/insight/src/components/ui/progress.tsx +0 -83
  128. package/insight/src/components/ui/scroll-area.tsx +0 -55
  129. package/insight/src/components/ui/separator.tsx +0 -23
  130. package/insight/src/components/ui/table.tsx +0 -114
  131. package/insight/src/components/ui/tabs.tsx +0 -82
  132. package/insight/src/components/ui/tooltip.tsx +0 -64
  133. package/insight/src/lib/api.ts +0 -144
  134. package/insight/src/lib/utils.ts +0 -6
  135. package/insight/src/main.tsx +0 -22
  136. package/insight/src/routeTree.gen.ts +0 -189
  137. package/insight/src/router.tsx +0 -19
  138. package/insight/src/routes/__root.tsx +0 -55
  139. package/insight/src/routes/enterprise.tsx +0 -316
  140. package/insight/src/routes/index.tsx +0 -1482
  141. package/insight/src/routes/knowledge.tsx +0 -221
  142. package/insight/src/routes/knowledge_.$dbHash.$sourceId.tsx +0 -137
  143. package/insight/src/routes/search.tsx +0 -97
  144. package/insight/src/routes/sessions.tsx +0 -179
  145. package/insight/src/routes/sessions_.$dbHash.$sessionId.tsx +0 -181
  146. package/insight/src/styles.css +0 -104
  147. package/insight/tsconfig.json +0 -29
  148. 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
- lines.push("## Subagent Tasks");
153
- lines.push("");
154
- for (const ev of grouped.subagent) lines.push(`- ${ev.data}`);
155
- lines.push("");
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
- for (const ev of grouped.data) lines.push(`- ${ev.data}`);
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
- block += `\n## Subagent Tasks`;
339
- for (const ev of grouped.subagent) {
340
- const text = ev.data.length > 120 ? ev.data.substring(0, 117) + "..." : ev.data;
341
- block += `\n- ${text}`;
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
- for (const ev of grouped.data) {
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",
@@ -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(join(cacheParent, d));
403
- if (now - st.mtimeMs > ONE_HOUR) {
404
- rmSync(join(cacheParent, d), { recursive: true, force: true });
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
- } catch { /* skip */ }
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");
@@ -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.162",
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.162",
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");