context-mode 1.0.106 → 1.0.107

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 (42) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.openclaw-plugin/openclaw.plugin.json +1 -1
  4. package/.openclaw-plugin/package.json +1 -1
  5. package/build/adapters/copilot-base.d.ts +3 -3
  6. package/build/adapters/cursor/hooks.js +8 -0
  7. package/build/adapters/cursor/index.js +4 -1
  8. package/build/adapters/gemini-cli/hooks.d.ts +6 -1
  9. package/build/adapters/gemini-cli/hooks.js +7 -1
  10. package/build/adapters/gemini-cli/index.js +12 -0
  11. package/build/adapters/kiro/hooks.js +4 -0
  12. package/build/adapters/kiro/index.d.ts +9 -2
  13. package/build/adapters/kiro/index.js +49 -27
  14. package/build/adapters/opencode/index.js +6 -0
  15. package/build/adapters/qwen-code/index.js +18 -0
  16. package/build/adapters/vscode-copilot/hooks.d.ts +0 -4
  17. package/build/adapters/vscode-copilot/hooks.js +6 -6
  18. package/build/cli.js +1 -0
  19. package/build/openclaw/mcp-tools.d.ts +54 -0
  20. package/build/openclaw/mcp-tools.js +198 -0
  21. package/build/openclaw-plugin.d.ts +9 -0
  22. package/build/openclaw-plugin.js +132 -16
  23. package/build/opencode-plugin.d.ts +29 -4
  24. package/build/opencode-plugin.js +154 -7
  25. package/build/pi-extension.js +123 -29
  26. package/build/server.d.ts +1 -0
  27. package/build/server.js +19 -1
  28. package/build/session/extract.d.ts +1 -1
  29. package/build/session/extract.js +46 -1
  30. package/cli.bundle.mjs +125 -125
  31. package/hooks/core/platform-detect.mjs +49 -0
  32. package/hooks/core/routing.mjs +13 -1
  33. package/hooks/cursor/afteragentresponse.mjs +74 -0
  34. package/hooks/gemini-cli/beforeagent.mjs +99 -0
  35. package/hooks/kiro/agentspawn.mjs +97 -0
  36. package/hooks/kiro/userpromptsubmit.mjs +88 -0
  37. package/hooks/session-extract.bundle.mjs +2 -2
  38. package/hooks/sessionstart.mjs +3 -1
  39. package/hooks/vscode-copilot/sessionstart.mjs +13 -14
  40. package/openclaw.plugin.json +1 -1
  41. package/package.json +1 -1
  42. package/server.bundle.mjs +68 -68
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Platform detection from process env vars.
3
+ *
4
+ * Each supported platform sets a distinctive env var when invoking
5
+ * hook scripts; we use those to pick the correct tool-namer prefix
6
+ * and routing block. Falls back to "claude-code" when nothing matches
7
+ * so existing CC behavior is preserved.
8
+ *
9
+ * SINGLE SOURCE OF TRUTH: this table mirrors `PLATFORM_ENV_VARS` in
10
+ * `src/adapters/detect.ts:33-77`. Every entry has been verified against
11
+ * the platform's own runtime source code (full audit May 2026, see
12
+ * git blame). DO NOT add platform env vars here that aren't also in
13
+ * detect.ts — the two MUST stay in lock-step or detection will diverge
14
+ * between MCP-server-side and hook-script-side.
15
+ *
16
+ * Order matters — same as detect.ts. Forks listed BEFORE the fork's
17
+ * parent so collision detection works (e.g. cursor BEFORE vscode-copilot
18
+ * because Cursor inherits VSCODE_PID as a fork; antigravity BEFORE
19
+ * vscode-copilot for the same reason).
20
+ */
21
+
22
+ // Mirror of `PLATFORM_ENV_VARS` in src/adapters/detect.ts:33-77.
23
+ // Keep in lock-step. If you change one, change the other.
24
+ const PLATFORM_ENV_VARS_MIRROR = [
25
+ ["claude-code", ["CLAUDE_PROJECT_DIR", "CLAUDE_SESSION_ID"]],
26
+ ["antigravity", ["ANTIGRAVITY_CLI_ALIAS"]],
27
+ ["cursor", ["CURSOR_TRACE_ID", "CURSOR_CLI"]],
28
+ ["kilo", ["KILO_PID"]],
29
+ ["opencode", ["OPENCODE", "OPENCODE_PID"]],
30
+ ["zed", ["ZED_SESSION_ID", "ZED_TERM"]],
31
+ ["codex", ["CODEX_THREAD_ID", "CODEX_CI"]],
32
+ ["gemini-cli", ["GEMINI_PROJECT_DIR", "GEMINI_CLI"]],
33
+ ["vscode-copilot", ["VSCODE_PID", "VSCODE_CWD"]],
34
+ ["jetbrains-copilot", ["IDEA_INITIAL_DIRECTORY"]],
35
+ ["qwen-code", ["QWEN_PROJECT_DIR"]],
36
+ ["pi", ["PI_PROJECT_DIR"]],
37
+ // openclaw — no auto-set process env vars; falls through to default
38
+ // kiro — no auto-set process env vars; falls through to default
39
+ ];
40
+
41
+ export function detectPlatformFromEnv(env = process.env) {
42
+ for (const [platform, vars] of PLATFORM_ENV_VARS_MIRROR) {
43
+ if (vars.some((v) => env[v])) return platform;
44
+ }
45
+ return "claude-code";
46
+ }
47
+
48
+ // Re-exported for tests so they can assert against the same canonical table.
49
+ export { PLATFORM_ENV_VARS_MIRROR };
@@ -133,13 +133,25 @@ export async function initSecurity(buildDir) {
133
133
  * - VS Code Copilot: run_in_terminal (command field), read_file, run_vs_code_task
134
134
  */
135
135
  const TOOL_ALIASES = {
136
- // Gemini CLI
136
+ // Gemini CLI / Qwen Code (share native tool names — Qwen is Gemini fork:
137
+ // refs/platforms/qwen-code/packages/core/src/tools/tool-names.ts)
137
138
  "run_shell_command": "Bash",
138
139
  "read_file": "Read",
139
140
  "read_many_files": "Read",
140
141
  "grep_search": "Grep",
141
142
  "search_file_content": "Grep",
142
143
  "web_fetch": "WebFetch",
144
+ // Qwen Code additional tool names (no routing branch yet but normalized
145
+ // so future routing logic works without per-platform fallback):
146
+ "write_file": "Write",
147
+ "edit": "Edit",
148
+ "glob": "Glob",
149
+ "todo_write": "TodoWrite",
150
+ "ask_user_question": "AskUserQuestion",
151
+ "list_directory": "LS",
152
+ "save_memory": "Memory",
153
+ "skill": "Skill",
154
+ "exit_plan_mode": "ExitPlanMode",
143
155
  // OpenCode
144
156
  "bash": "Bash",
145
157
  "view": "Read",
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ import "../suppress-stderr.mjs";
3
+ import "../ensure-deps.mjs";
4
+ /**
5
+ * Cursor afterAgentResponse hook — fires once Cursor finishes streaming
6
+ * the assistant turn back to the user (per Cursor v1 hook docs:
7
+ * https://cursor.com/docs/agent/hooks#afteragentresponse).
8
+ *
9
+ * Input: { "conversation_id": "...", "generation_id": "...", "text": "<final assistant message>" }
10
+ * Output: { "additional_context": "" } (Cursor rejects empty stdout, so emit a no-op payload)
11
+ *
12
+ * The hook records the agent_response event so session analytics + the
13
+ * resume directive can reconstruct the last assistant turn. Mirrors
14
+ * stop.mjs (turn lifecycle telemetry) — afterAgentResponse fires
15
+ * BEFORE stop, so we capture the produced text here while stop captures
16
+ * loop_count + final status.
17
+ */
18
+
19
+ import {
20
+ readStdin,
21
+ parseStdin,
22
+ getSessionId,
23
+ getSessionDBPath,
24
+ getInputProjectDir,
25
+ CURSOR_OPTS,
26
+ } from "../session-helpers.mjs";
27
+ import { dirname } from "node:path";
28
+ import { fileURLToPath } from "node:url";
29
+ import { createSessionLoaders } from "../session-loaders.mjs";
30
+
31
+ const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
32
+ const { loadSessionDB } = createSessionLoaders(HOOK_DIR);
33
+ const OPTS = CURSOR_OPTS;
34
+
35
+ // Truncate large assistant responses before persisting — keeps the
36
+ // session_events table from ballooning when an agent emits long output.
37
+ const MAX_TEXT_BYTES = 8 * 1024;
38
+
39
+ try {
40
+ const raw = await readStdin();
41
+ const input = parseStdin(raw);
42
+ const projectDir = getInputProjectDir(input, CURSOR_OPTS);
43
+
44
+ if (projectDir && !process.env.CURSOR_CWD) {
45
+ process.env.CURSOR_CWD = projectDir;
46
+ }
47
+
48
+ const text = typeof input.text === "string" ? input.text : "";
49
+ const truncated = text.length > MAX_TEXT_BYTES
50
+ ? text.slice(0, MAX_TEXT_BYTES)
51
+ : text;
52
+
53
+ const { SessionDB } = await loadSessionDB();
54
+ const dbPath = getSessionDBPath(OPTS);
55
+ const db = new SessionDB({ dbPath });
56
+ const sessionId = getSessionId(input, OPTS);
57
+
58
+ db.ensureSession(sessionId, projectDir);
59
+ db.insertEvent(sessionId, {
60
+ type: "agent_response",
61
+ generation_id: input.generation_id ?? null,
62
+ text_bytes: text.length,
63
+ truncated: text.length > MAX_TEXT_BYTES,
64
+ text: truncated,
65
+ }, "AfterAgentResponse");
66
+
67
+ db.close();
68
+ } catch {
69
+ // Cursor treats stderr as hook failure; swallow and continue.
70
+ }
71
+
72
+ // Cursor rejects empty stdout as "no valid response", so emit a no-op
73
+ // additional_context payload (same convention as sessionstart.mjs).
74
+ process.stdout.write(JSON.stringify({ additional_context: "" }) + "\n");
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ import "../suppress-stderr.mjs";
3
+ import "../ensure-deps.mjs";
4
+ /**
5
+ * Gemini CLI BeforeAgent hook — UserPromptSubmit equivalent.
6
+ *
7
+ * Captures every user prompt so the LLM can resume from the exact point
8
+ * the user left off after compact / resume. Mirrors hooks/userpromptsubmit.mjs
9
+ * but reads `input.prompt` (Gemini wire shape, types.ts:547-549) and emits
10
+ * `hookSpecificOutput.hookEventName: "BeforeAgent"` (types.ts:554-559) so
11
+ * Gemini's hookRunner appends additionalContext to the prompt
12
+ * (hookRunner.ts:183-197).
13
+ *
14
+ * Must be fast (<10ms). Single SQLite write.
15
+ */
16
+
17
+ import {
18
+ readStdin,
19
+ parseStdin,
20
+ getSessionId,
21
+ getSessionDBPath,
22
+ getInputProjectDir,
23
+ GEMINI_OPTS,
24
+ } from "../session-helpers.mjs";
25
+ import { createSessionLoaders, attributeAndInsertEvents } from "../session-loaders.mjs";
26
+ import { dirname } from "node:path";
27
+ import { fileURLToPath } from "node:url";
28
+
29
+ const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
30
+ const { loadSessionDB, loadExtract, loadProjectAttribution } = createSessionLoaders(HOOK_DIR);
31
+ const OPTS = GEMINI_OPTS;
32
+
33
+ try {
34
+ const raw = await readStdin();
35
+ const input = parseStdin(raw);
36
+ const projectDir = getInputProjectDir(input, OPTS);
37
+
38
+ const prompt = input.prompt ?? input.message ?? "";
39
+ const trimmed = (prompt || "").trim();
40
+
41
+ // Skip system-generated messages — only capture genuine user prompts.
42
+ const isSystemMessage = trimmed.startsWith("<task-notification>")
43
+ || trimmed.startsWith("<system-reminder>")
44
+ || trimmed.startsWith("<context_guidance>")
45
+ || trimmed.startsWith("<tool-result>");
46
+
47
+ if (trimmed.length > 0 && !isSystemMessage) {
48
+ const { SessionDB } = await loadSessionDB();
49
+ const { extractUserEvents } = await loadExtract();
50
+ const { resolveProjectAttributions } = await loadProjectAttribution();
51
+ const dbPath = getSessionDBPath(OPTS);
52
+ const db = new SessionDB({ dbPath });
53
+ const sessionId = getSessionId(input, OPTS);
54
+
55
+ db.ensureSession(sessionId, projectDir);
56
+
57
+ // 1. Always save the raw prompt.
58
+ const promptEvent = {
59
+ type: "user_prompt",
60
+ category: "user-prompt",
61
+ data: prompt,
62
+ priority: 1,
63
+ };
64
+ const promptAttributions = attributeAndInsertEvents(
65
+ db, sessionId, [promptEvent], input, projectDir, "BeforeAgent", resolveProjectAttributions,
66
+ );
67
+
68
+ // 2. Extract decision/role/intent/data from user message.
69
+ const userEvents = extractUserEvents(trimmed);
70
+ const savedLastKnown = promptAttributions[0]?.projectDir || null;
71
+ const sessionStats = db.getSessionStats(sessionId);
72
+ const lastKnownProjectDir = typeof db.getLatestAttributedProjectDir === "function"
73
+ ? db.getLatestAttributedProjectDir(sessionId)
74
+ : null;
75
+ const userAttributions = resolveProjectAttributions(userEvents, {
76
+ sessionOriginDir: sessionStats?.project_dir || projectDir,
77
+ inputProjectDir: projectDir,
78
+ workspaceRoots: Array.isArray(input.workspace_roots) ? input.workspace_roots : [],
79
+ lastKnownProjectDir: savedLastKnown || lastKnownProjectDir,
80
+ });
81
+ for (let i = 0; i < userEvents.length; i++) {
82
+ db.insertEvent(sessionId, userEvents[i], "BeforeAgent", userAttributions[i]);
83
+ }
84
+
85
+ db.close();
86
+ }
87
+ } catch {
88
+ // BeforeAgent must never block the session — silent fallback.
89
+ }
90
+
91
+ // Emit empty additionalContext so Gemini's hookRunner treats the response as
92
+ // metadata (no prompt mutation). Matches the SessionStart hook's structured-
93
+ // output convention (sessionstart.mjs:130-135).
94
+ console.log(JSON.stringify({
95
+ hookSpecificOutput: {
96
+ hookEventName: "BeforeAgent",
97
+ additionalContext: "",
98
+ },
99
+ }));
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ import "../suppress-stderr.mjs";
3
+ import "../ensure-deps.mjs";
4
+ /**
5
+ * Kiro CLI agentSpawn hook for context-mode.
6
+ *
7
+ * agentSpawn is the Kiro semantic equivalent of Claude Code's SessionStart:
8
+ * fires once when an agent loads. We use it to inject the routing block and
9
+ * (on resume/compact) the session-resume directive.
10
+ *
11
+ * Source: https://kiro.dev/docs/cli/hooks
12
+ */
13
+
14
+ import { createRoutingBlock } from "../routing-block.mjs";
15
+ import { createToolNamer } from "../core/tool-naming.mjs";
16
+
17
+ const toolNamer = createToolNamer("kiro");
18
+ const ROUTING_BLOCK = createRoutingBlock(toolNamer);
19
+ import {
20
+ writeSessionEventsFile,
21
+ buildSessionDirective,
22
+ getSessionEvents,
23
+ } from "../session-directive.mjs";
24
+ import {
25
+ readStdin,
26
+ parseStdin,
27
+ getSessionId,
28
+ getSessionDBPath,
29
+ getSessionEventsPath,
30
+ getCleanupFlagPath,
31
+ getInputProjectDir,
32
+ KIRO_OPTS,
33
+ } from "../session-helpers.mjs";
34
+ import { createSessionLoaders } from "../session-loaders.mjs";
35
+ import { unlinkSync } from "node:fs";
36
+ import { fileURLToPath } from "node:url";
37
+
38
+ const HOOK_DIR = fileURLToPath(new URL(".", import.meta.url));
39
+ const { loadSessionDB } = createSessionLoaders(HOOK_DIR);
40
+ const OPTS = KIRO_OPTS;
41
+
42
+ let additionalContext = ROUTING_BLOCK;
43
+
44
+ try {
45
+ const raw = await readStdin();
46
+ const input = parseStdin(raw);
47
+ // Kiro stdin shape mirrors codex/CC: { source, cwd, ... }. Default startup
48
+ // when no source provided (single-shot agent spawn).
49
+ const source = input.source ?? "startup";
50
+ const projectDir = getInputProjectDir(input, OPTS);
51
+
52
+ if (source === "compact" || source === "resume") {
53
+ const { SessionDB } = await loadSessionDB();
54
+ const dbPath = getSessionDBPath(OPTS);
55
+ const db = new SessionDB({ dbPath });
56
+
57
+ if (source === "compact") {
58
+ const sessionId = getSessionId(input, OPTS);
59
+ const resume = db.getResume(sessionId);
60
+ if (resume && !resume.consumed) {
61
+ db.markResumeConsumed(sessionId);
62
+ }
63
+ } else {
64
+ try { unlinkSync(getCleanupFlagPath(OPTS)); } catch { /* no flag */ }
65
+ }
66
+
67
+ const sessionId = getSessionId(input, OPTS);
68
+ const events = sessionId ? getSessionEvents(db, sessionId) : [];
69
+ if (events.length > 0) {
70
+ const eventMeta = writeSessionEventsFile(events, getSessionEventsPath(OPTS));
71
+ additionalContext += buildSessionDirective(source, eventMeta, toolNamer);
72
+ }
73
+
74
+ db.close();
75
+ } else if (source === "startup") {
76
+ const { SessionDB } = await loadSessionDB();
77
+ const dbPath = getSessionDBPath(OPTS);
78
+ const db = new SessionDB({ dbPath });
79
+ try { unlinkSync(getSessionEventsPath(OPTS)); } catch { /* no stale file */ }
80
+
81
+ db.cleanupOldSessions(7);
82
+ db.db.exec(`DELETE FROM session_events WHERE session_id NOT IN (SELECT session_id FROM session_meta)`);
83
+
84
+ const sessionId = getSessionId(input, OPTS);
85
+ db.ensureSession(sessionId, projectDir);
86
+
87
+ db.close();
88
+ }
89
+ // clear => routing block only
90
+ } catch {
91
+ // Swallow errors — hook must not fail
92
+ }
93
+
94
+ // Kiro CLI agentSpawn JSON output shape mirrors CC SessionStart.
95
+ process.stdout.write(JSON.stringify({
96
+ hookSpecificOutput: { hookEventName: "agentSpawn", additionalContext },
97
+ }) + "\n");
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+ import "../suppress-stderr.mjs";
3
+ import "../ensure-deps.mjs";
4
+ /**
5
+ * Kiro CLI UserPromptSubmit hook — capture user prompts for continuity.
6
+ *
7
+ * Mirrors hooks/codex/userpromptsubmit.mjs (same json-stdio paradigm).
8
+ * Kiro stdin: { hook_event_name: "userPromptSubmit", cwd, prompt }
9
+ *
10
+ * Source: https://kiro.dev/docs/cli/hooks
11
+ */
12
+
13
+ import {
14
+ readStdin,
15
+ parseStdin,
16
+ getSessionId,
17
+ getSessionDBPath,
18
+ getInputProjectDir,
19
+ KIRO_OPTS,
20
+ } from "../session-helpers.mjs";
21
+ import { createSessionLoaders, attributeAndInsertEvents } from "../session-loaders.mjs";
22
+ import { dirname } from "node:path";
23
+ import { fileURLToPath } from "node:url";
24
+
25
+ const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
26
+ const { loadSessionDB, loadExtract, loadProjectAttribution } = createSessionLoaders(HOOK_DIR);
27
+ const OPTS = KIRO_OPTS;
28
+
29
+ try {
30
+ const raw = await readStdin();
31
+ const input = parseStdin(raw);
32
+ const projectDir = getInputProjectDir(input, OPTS);
33
+
34
+ const prompt = input.prompt ?? input.message ?? "";
35
+ const trimmed = (prompt || "").trim();
36
+
37
+ const isSystemMessage = trimmed.startsWith("<task-notification>")
38
+ || trimmed.startsWith("<system-reminder>")
39
+ || trimmed.startsWith("<context_guidance>")
40
+ || trimmed.startsWith("<tool-result>");
41
+
42
+ if (trimmed.length > 0 && !isSystemMessage) {
43
+ const { SessionDB } = await loadSessionDB();
44
+ const { extractUserEvents } = await loadExtract();
45
+ const { resolveProjectAttributions } = await loadProjectAttribution();
46
+ const dbPath = getSessionDBPath(OPTS);
47
+ const db = new SessionDB({ dbPath });
48
+ const sessionId = getSessionId(input, OPTS);
49
+
50
+ db.ensureSession(sessionId, projectDir);
51
+
52
+ const promptEvent = {
53
+ type: "user_prompt",
54
+ category: "user-prompt",
55
+ data: prompt,
56
+ priority: 1,
57
+ };
58
+ const promptAttributions = attributeAndInsertEvents(
59
+ db, sessionId, [promptEvent], input, projectDir, "UserPromptSubmit", resolveProjectAttributions,
60
+ );
61
+
62
+ const userEvents = extractUserEvents(trimmed);
63
+ const savedLastKnown = promptAttributions[0]?.projectDir || null;
64
+ const sessionStats = db.getSessionStats(sessionId);
65
+ const lastKnownProjectDir = typeof db.getLatestAttributedProjectDir === "function"
66
+ ? db.getLatestAttributedProjectDir(sessionId)
67
+ : null;
68
+ const userAttributions = resolveProjectAttributions(userEvents, {
69
+ sessionOriginDir: sessionStats?.project_dir || projectDir,
70
+ inputProjectDir: projectDir,
71
+ workspaceRoots: Array.isArray(input.workspace_roots) ? input.workspace_roots : [],
72
+ lastKnownProjectDir: savedLastKnown || lastKnownProjectDir,
73
+ });
74
+ for (let i = 0; i < userEvents.length; i++) {
75
+ db.insertEvent(sessionId, userEvents[i], "UserPromptSubmit", userAttributions[i]);
76
+ }
77
+
78
+ db.close();
79
+ }
80
+ } catch {
81
+ // Kiro hooks must not block the session.
82
+ }
83
+
84
+ // Kiro CLI userPromptSubmit accepts JSON output for additionalContext (see docs).
85
+ // We don't inject context here — that's the agentSpawn hook's job.
86
+ process.stdout.write(JSON.stringify({
87
+ hookSpecificOutput: { hookEventName: "userPromptSubmit", additionalContext: "" },
88
+ }) + "\n");
@@ -1,2 +1,2 @@
1
- function s(t){return t==null?"":String(t)}function f(t){return t==null?"":typeof t=="string"?t:JSON.stringify(t)}function b(t){let{tool_name:e,tool_input:o,tool_response:i}=t,r=[];if(e==="Read"){let n=String(o.file_path??"");return(/(?:CLAUDE|AGENTS(?:\.override)?|GEMINI|QWEN|KIRO)\.md$/i.test(n)||/\/copilot-instructions\.md$/i.test(n)||/\/context-mode\.mdc$/i.test(n)||/\.claude[\\/]/i.test(n)||/[\\/]memor(?:y|ies)[\\/][^\\/]+\.md$/i.test(n))&&(r.push({type:"rule",category:"rule",data:s(n),priority:1}),i&&i.length>0&&r.push({type:"rule_content",category:"rule",data:s(i),priority:1})),r.push({type:"file_read",category:"file",data:s(n),priority:1}),r}if(e==="Edit"){let n=String(o.file_path??"");return r.push({type:"file_edit",category:"file",data:s(n),priority:1}),r}if(e==="NotebookEdit"){let n=String(o.notebook_path??"");return r.push({type:"file_edit",category:"file",data:s(n),priority:1}),r}if(e==="Write"){let n=String(o.file_path??"");return r.push({type:"file_write",category:"file",data:s(n),priority:1}),r}if(e==="Glob"){let n=String(o.pattern??"");return r.push({type:"file_glob",category:"file",data:s(n),priority:3}),r}if(e==="Grep"){let n=String(o.pattern??""),a=String(o.path??"");return r.push({type:"file_search",category:"file",data:s(`${n} in ${a}`),priority:3}),r}return r}function y(t){if(t.tool_name!=="Bash")return[];let o=String(t.tool_input.command??"").match(/\bcd\s+("([^"]+)"|'([^']+)'|(\S+))/);if(!o)return[];let i=o[2]??o[3]??o[4]??"";return[{type:"cwd",category:"cwd",data:s(i),priority:2}]}function h(t){let{tool_name:e,tool_input:o,tool_response:i,tool_output:r}=t,n=String(i??""),a=r?.isError===!0;return!(e==="Bash"&&/exit code [1-9]|error:|Error:|FAIL|failed/i.test(n))&&!a?[]:[{type:"error_tool",category:"error",data:s(n),priority:2}]}var _=[{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 m(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??""),o=_.find(i=>i.pattern.test(e));return o?[{type:"git",category:"git",data:s(o.operation),priority:2}]:[]}function S(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:s(JSON.stringify(t.tool_input)),priority:1}]:[]}function E(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=[],o=t.tool_input.allowedPrompts,i=Array.isArray(o)&&o.length>0?`exited plan mode (allowed: ${f(o.map(n=>typeof n=="object"&&n!==null&&"prompt"in n?String(n.prompt):String(n)).join(", "))})`:"exited plan mode";e.push({type:"plan_exit",category:"plan",data:s(i),priority:2});let r=String(t.tool_response??"").toLowerCase();return r.includes("approved")||r.includes("approve")?e.push({type:"plan_approved",category:"plan",data:"plan approved by user",priority:1}):(r.includes("rejected")||r.includes("decline")||r.includes("denied"))&&e.push({type:"plan_rejected",category:"plan",data:s(`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(/[/\\]\.claude[/\\]plans[/\\]/.test(e))return[{type:"plan_file_write",category:"plan",data:s(`plan file: ${e.split(/[/\\]/).pop()??e}`),priority:2}]}return[]}var k=[/\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 v(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??"");if(!k.some(r=>r.test(e)))return[];let i=e.replace(/\bexport\s+(\w+)=\S*/g,"export $1=***");return[{type:"env",category:"env",data:s(i),priority:2}]}function x(t){if(t.tool_name!=="Skill")return[];let e=String(t.tool_input.skill??"");return[{type:"skill",category:"skill",data:s(e),priority:2}]}function w(t){if(!t.tool_response?.includes("Error")&&!t.tool_output?.isError)return[];let e=String(t.tool_response||""),o=[/not supported/i,/cannot/i,/does not support/i,/FAIL/i,/refused/i,/permission denied/i,/incompatible/i];for(let i of o){let r=e.match(i);if(r){let n=e.toLowerCase().indexOf(r[0].toLowerCase()),a=e.slice(Math.max(0,n-50),Math.min(e.length,n+200)).trim();return[{type:"constraint_discovered",category:"constraint",data:s(a),priority:2}]}}return[]}function R(t){if(t.tool_name!=="Agent")return[];let e=s(String(t.tool_input.prompt??t.tool_input.description??"")),o=t.tool_response?s(String(t.tool_response)):"",i=o.length>0;return[{type:i?"subagent_completed":"subagent_launched",category:"subagent",data:s(i?`[completed] ${e} \u2192 ${o}`:`[launched] ${e}`),priority:i?2:3}]}function T(t){let{tool_name:e,tool_input:o,tool_response:i}=t;if(!e.startsWith("mcp__"))return[];let r=e.split("__"),n=r[r.length-1]||e,a=Object.values(o).find(d=>typeof d=="string"),p=a?`: ${s(String(a))}`:"",u=i&&i.length>0?`
2
- response: ${s(i)}`:"";return[{type:"mcp",category:"mcp",data:s(`${n}${p}${u}`),priority:3}]}var A=2048;function I(t,e){if(Buffer.byteLength(t,"utf8")<=e)return{value:t,truncated:!1};let o=Buffer.from(t,"utf8"),i=e;for(;i>0&&(o[i]&192)===128;)i--;return{value:o.subarray(0,i).toString("utf8"),truncated:!0}}var $=/(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,H="[REDACTED]";function g(t,e=new WeakSet){if(t==null||typeof t!="object")return t;if(e.has(t))return"[CIRCULAR]";e.add(t);let o;if(Array.isArray(t))o=t.map(i=>g(i,e));else{let i={};for(let[r,n]of Object.entries(t))$.test(r)?i[r]=H:i[r]=g(n,e);o=i}return e.delete(t),o}function N(t){let{tool_name:e,tool_input:o}=t;if(!e.startsWith("mcp__"))return[];let i=g(o??{}),r;try{r=JSON.stringify(i)}catch{r="{}"}let{value:n,truncated:a}=I(r,A),p=a?`{"tool_name":${JSON.stringify(e)},"params_raw":${JSON.stringify(n)},"truncated":true}`:`{"tool_name":${JSON.stringify(e)},"params":${n}}`;return[{type:"mcp_tool_call",category:"mcp_tool_call",data:s(p),priority:4}]}function C(t){if(t.tool_name!=="AskUserQuestion")return[];let e=t.tool_input.questions,o=Array.isArray(e)&&e.length>0?String(e[0].question??""):"",i=s(String(t.tool_response??"")),r=o?`Q: ${s(o)} \u2192 A: ${i}`:`answer: ${i}`;return[{type:"decision_question",category:"decision",data:s(r),priority:2}]}function P(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:s(e),priority:2}]}function O(t){let e=[f(t.tool_input),s(t.tool_response)].join(" ");if(e.length===0)return[];let o=new Set,i=e.match(/https?:\/\/[^\s)]+/g);if(i)for(let n of i)n=n.replace(/["'})\],;.]+$/,""),/localhost|127\.0\.0\.1/i.test(n)||o.add(n);let r=e.match(/(?<!\w)#(\d+)/g);if(r)for(let n of r)o.add(n);return o.size===0?[]:[{type:"external_ref",category:"external-ref",data:s(Array.from(o).join(", ")),priority:3}]}function B(t){if(t.tool_name!=="EnterWorktree")return[];let e=String(t.tool_input.name??"unnamed");return[{type:"worktree",category:"env",data:s(`entered worktree: ${e}`),priority:2}]}var L=[/\b(don'?t|do not|never|always|instead|rather|prefer)\b/i,/\b(use|switch to|go with|pick|choose)\s+\w+\s+(instead|over|not)\b/i,/\b(no,?\s+(use|do|try|make))\b/i,/\b(hayır|hayir|evet|böyle|boyle|degil|değil|yerine|kullan)\b/i];function j(t){return L.some(o=>o.test(t))?[{type:"decision",category:"decision",data:s(t),priority:2}]:[]}var D=[/\b(act as|you are|behave like|pretend|role of|persona)\b/i,/\b(senior|staff|principal|lead)\s+(engineer|developer|architect)\b/i,/\b(gibi davran|rolünde|olarak çalış)\b/i];function M(t){return D.some(o=>o.test(t))?[{type:"role",category:"role",data:s(t),priority:3}]:[]}var W=[{mode:"investigate",pattern:/\b(why|how does|explain|understand|what is|analyze|debug|look into)\b/i},{mode:"implement",pattern:/\b(create|add|build|implement|write|make|develop|fix)\b/i},{mode:"discuss",pattern:/\b(think about|consider|should we|what if|pros and cons|opinion)\b/i},{mode:"review",pattern:/\b(review|check|audit|verify|test|validate)\b/i}];function F(t){let e=W.find(({pattern:o})=>o.test(t));return e?[{type:"intent",category:"intent",data:s(e.mode),priority:4}]:[]}var U=[/\bblocked on\b/i,/\bwaiting for\b/i,/\bneed\s+\S+\s+before\b/i,/\bcan'?t proceed until\b/i,/\bdepends on\b/i,/\bblocked\b/i,/\bbekliyor\b/i,/\bbekliyorum\b/i],G=[/\bunblocked\b/i,/\bresolved\b/i,/\bgot the\s+\S+/i,/\bis ready now\b/i,/\bcan proceed\b/i];function J(t){let e=[];return G.some(r=>r.test(t))?(e.push({type:"blocker_resolved",category:"blocked-on",data:s(t),priority:2}),e):(U.some(r=>r.test(t))&&e.push({type:"blocker",category:"blocked-on",data:s(t),priority:2}),e)}function K(t){return t.length<=1024?[]:[{type:"data",category:"data",data:s(t),priority:4}]}var c=null;function q(t){let{tool_name:e,tool_response:o,tool_output:i}=t,r=String(o??""),n=i?.isError===!0;if(e==="Bash"&&/exit code [1-9]|error:|Error:|FAIL|failed/i.test(r)||n)return c={tool:e,error:r.slice(0,200),callsSince:0},[];if(!c)return[];if(c.callsSince++,c.callsSince>10)return c=null,[];let p=e===c.tool,u=c.tool==="Read"&&(e==="Edit"||e==="Write");if(p||u){let d={type:"error_resolved",category:"error-resolution",data:s(`Error in ${c.tool}: ${c.error} \u2192 Fixed`),priority:2};return c=null,[d]}return[]}function V(){c=null}var l=[];function z(t){return`${t.length}:${t.slice(0,20)}`}function Q(t){let{tool_name:e,tool_input:o}=t,i=z(JSON.stringify(o).slice(0,200));if(l.push({tool:e,inputHash:i}),l.length>50&&l.splice(0,l.length-50),l.length<3)return[];let r=0;for(let n=l.length-1;n>=0&&(l[n].tool===e&&l[n].inputHash===i);n--)r++;return r>=3?(l.splice(l.length-r),[{type:"retry_detected",category:"iteration-loop",data:s(`${e} called ${r} times with similar input`),priority:2}]):[]}function Y(){l.length=0}function X(t){try{let e=[];return e.push(...b(t)),e.push(...y(t)),e.push(...h(t)),e.push(...m(t)),e.push(...v(t)),e.push(...S(t)),e.push(...E(t)),e.push(...x(t)),e.push(...R(t)),e.push(...T(t)),e.push(...N(t)),e.push(...C(t)),e.push(...w(t)),e.push(...B(t)),e.push(...P(t)),e.push(...O(t)),e.push(...q(t)),e.push(...Q(t)),e}catch{return[]}}function Z(t){try{let e=[];return e.push(...j(t)),e.push(...M(t)),e.push(...F(t)),e.push(...J(t)),e.push(...K(t)),e}catch{return[]}}export{X as extractEvents,Z as extractUserEvents,V as resetErrorResolutionState,Y as resetIterationLoopState};
1
+ function s(t){return t==null?"":String(t)}function f(t){return t==null?"":typeof t=="string"?t:JSON.stringify(t)}function b(t){let{tool_name:e,tool_input:o,tool_response:i}=t,n=[];if(e==="Read"){let r=String(o.file_path??"");return(/(?:CLAUDE|AGENTS(?:\.override)?|GEMINI|QWEN|KIRO)\.md$/i.test(r)||/\/copilot-instructions\.md$/i.test(r)||/\/context-mode\.mdc$/i.test(r)||/\.claude[\\/]/i.test(r)||/[\\/]memor(?:y|ies)[\\/][^\\/]+\.md$/i.test(r))&&(n.push({type:"rule",category:"rule",data:s(r),priority:1}),i&&i.length>0&&n.push({type:"rule_content",category:"rule",data:s(i),priority:1})),n.push({type:"file_read",category:"file",data:s(r),priority:1}),n}if(e==="Edit"){let r=String(o.file_path??"");return n.push({type:"file_edit",category:"file",data:s(r),priority:1}),n}if(e==="NotebookEdit"){let r=String(o.notebook_path??"");return n.push({type:"file_edit",category:"file",data:s(r),priority:1}),n}if(e==="Write"){let r=String(o.file_path??"");return n.push({type:"file_write",category:"file",data:s(r),priority:1}),n}if(e==="Glob"){let r=String(o.pattern??"");return n.push({type:"file_glob",category:"file",data:s(r),priority:3}),n}if(e==="Grep"){let r=String(o.pattern??""),a=String(o.path??"");return n.push({type:"file_search",category:"file",data:s(`${r} in ${a}`),priority:3}),n}return n}function y(t){if(t.tool_name!=="Bash")return[];let o=String(t.tool_input.command??"").match(/\bcd\s+("([^"]+)"|'([^']+)'|(\S+))/);if(!o)return[];let i=o[2]??o[3]??o[4]??"";return[{type:"cwd",category:"cwd",data:s(i),priority:2}]}function h(t){let{tool_name:e,tool_input:o,tool_response:i,tool_output:n}=t,r=String(i??""),a=n?.isError===!0;return!(e==="Bash"&&/exit code [1-9]|error:|Error:|FAIL|failed/i.test(r))&&!a?[]:[{type:"error_tool",category:"error",data:s(r),priority:2}]}var _=[{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 m(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??""),o=_.find(i=>i.pattern.test(e));return o?[{type:"git",category:"git",data:s(o.operation),priority:2}]:[]}function S(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:s(JSON.stringify(t.tool_input)),priority:1}]:[]}function E(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=[],o=t.tool_input.allowedPrompts,i=Array.isArray(o)&&o.length>0?`exited plan mode (allowed: ${f(o.map(r=>typeof r=="object"&&r!==null&&"prompt"in r?String(r.prompt):String(r)).join(", "))})`:"exited plan mode";e.push({type:"plan_exit",category:"plan",data:s(i),priority:2});let n=String(t.tool_response??"").toLowerCase();return n.includes("approved")||n.includes("approve")?e.push({type:"plan_approved",category:"plan",data:"plan approved by user",priority:1}):(n.includes("rejected")||n.includes("decline")||n.includes("denied"))&&e.push({type:"plan_rejected",category:"plan",data:s(`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(/[/\\]\.claude[/\\]plans[/\\]/.test(e))return[{type:"plan_file_write",category:"plan",data:s(`plan file: ${e.split(/[/\\]/).pop()??e}`),priority:2}]}return[]}var k=[/\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 v(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??"");if(!k.some(n=>n.test(e)))return[];let i=e.replace(/\bexport\s+(\w+)=\S*/g,"export $1=***");return[{type:"env",category:"env",data:s(i),priority:2}]}function x(t){if(t.tool_name!=="Skill")return[];let e=String(t.tool_input.skill??"");return[{type:"skill",category:"skill",data:s(e),priority:2}]}function w(t){if(!t.tool_response?.includes("Error")&&!t.tool_output?.isError)return[];let e=String(t.tool_response||""),o=[/not supported/i,/cannot/i,/does not support/i,/FAIL/i,/refused/i,/permission denied/i,/incompatible/i];for(let i of o){let n=e.match(i);if(n){let r=e.toLowerCase().indexOf(n[0].toLowerCase()),a=e.slice(Math.max(0,r-50),Math.min(e.length,r+200)).trim();return[{type:"constraint_discovered",category:"constraint",data:s(a),priority:2}]}}return[]}function R(t){if(t.tool_name!=="Agent")return[];let e=s(String(t.tool_input.prompt??t.tool_input.description??"")),o=t.tool_response?s(String(t.tool_response)):"",i=o.length>0;return[{type:i?"subagent_completed":"subagent_launched",category:"subagent",data:s(i?`[completed] ${e} \u2192 ${o}`:`[launched] ${e}`),priority:i?2:3}]}function A(t){let{tool_name:e,tool_input:o,tool_response:i}=t;if(!e.startsWith("mcp__"))return[];let n=e.split("__"),r=n[n.length-1]||e,a=Object.values(o).find(d=>typeof d=="string"),p=a?`: ${s(String(a))}`:"",u=i&&i.length>0?`
2
+ response: ${s(i)}`:"";return[{type:"mcp",category:"mcp",data:s(`${r}${p}${u}`),priority:3}]}var T=2048;function I(t,e){if(Buffer.byteLength(t,"utf8")<=e)return{value:t,truncated:!1};let o=Buffer.from(t,"utf8"),i=e;for(;i>0&&(o[i]&192)===128;)i--;return{value:o.subarray(0,i).toString("utf8"),truncated:!0}}var $=/(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,H="[REDACTED]";function g(t,e=new WeakSet){if(t==null||typeof t!="object")return t;if(e.has(t))return"[CIRCULAR]";e.add(t);let o;if(Array.isArray(t))o=t.map(i=>g(i,e));else{let i={};for(let[n,r]of Object.entries(t))$.test(n)?i[n]=H:i[n]=g(r,e);o=i}return e.delete(t),o}function N(t){let{tool_name:e,tool_input:o}=t;if(!e.startsWith("mcp__"))return[];let i=g(o??{}),n;try{n=JSON.stringify(i)}catch{n="{}"}let{value:r,truncated:a}=I(n,T),p=a?`{"tool_name":${JSON.stringify(e)},"params_raw":${JSON.stringify(r)},"truncated":true}`:`{"tool_name":${JSON.stringify(e)},"params":${r}}`;return[{type:"mcp_tool_call",category:"mcp_tool_call",data:s(p),priority:4}]}function B(t){if(t.tool_name!=="AskUserQuestion")return[];let e=t.tool_input.questions,o=Array.isArray(e)&&e.length>0?String(e[0].question??""):"",i=s(String(t.tool_response??"")),n=o?`Q: ${s(o)} \u2192 A: ${i}`:`answer: ${i}`;return[{type:"decision_question",category:"decision",data:s(n),priority:2}]}function O(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:s(e),priority:2}]}function C(t){let e=[f(t.tool_input),s(t.tool_response)].join(" ");if(e.length===0)return[];let o=new Set,i=e.match(/https?:\/\/[^\s)]+/g);if(i)for(let r of i)r=r.replace(/["'})\],;.]+$/,""),/localhost|127\.0\.0\.1/i.test(r)||o.add(r);let n=e.match(/(?<!\w)#(\d+)/g);if(n)for(let r of n)o.add(r);return o.size===0?[]:[{type:"external_ref",category:"external-ref",data:s(Array.from(o).join(", ")),priority:3}]}function P(t){if(t.tool_name!=="EnterWorktree")return[];let e=String(t.tool_input.name??"unnamed");return[{type:"worktree",category:"env",data:s(`entered worktree: ${e}`),priority:2}]}var L=[/\b(don'?t|do not|never|always|instead|rather|prefer)\b/i,/\b(use|switch to|go with|pick|choose)\s+\w+\s+(instead|over|not)\b/i,/\b(no,?\s+(use|do|try|make))\b/i,/\b(hayır|hayir|evet|böyle|boyle|degil|değil|yerine|kullan)\b/i];function M(t){return L.some(o=>o.test(t))?[{type:"decision",category:"decision",data:s(t),priority:2}]:[]}var j=[/\b(act as|you are|behave like|pretend|role of|persona)\b/i,/\b(senior|staff|principal|lead)\s+(engineer|developer|architect)\b/i,/\b(gibi davran|rolünde|olarak çalış)\b/i];function W(t){return j.some(o=>o.test(t))?[{type:"role",category:"role",data:s(t),priority:3}]:[]}var D=[{mode:"investigate",pattern:/\b(why|how does|explain|understand|what is|analyze|debug|look into)\b/i},{mode:"implement",pattern:/\b(create|add|build|implement|write|make|develop|fix)\b/i},{mode:"discuss",pattern:/\b(think about|consider|should we|what if|pros and cons|opinion)\b/i},{mode:"review",pattern:/\b(review|check|audit|verify|test|validate)\b/i}];function F(t){let e=D.find(({pattern:o})=>o.test(t));return e?[{type:"intent",category:"intent",data:s(e.mode),priority:4}]:[]}var G=[/\bblocked on\b/i,/\bwaiting for\b/i,/\bneed\s+\S+\s+before\b/i,/\bcan'?t proceed until\b/i,/\bdepends on\b/i,/\bblocked\b/i,/\bbekliyor\b/i,/\bbekliyorum\b/i],U=[/\bunblocked\b/i,/\bresolved\b/i,/\bgot the\s+\S+/i,/\bis ready now\b/i,/\bcan proceed\b/i];function J(t){let e=[];return U.some(n=>n.test(t))?(e.push({type:"blocker_resolved",category:"blocked-on",data:s(t),priority:2}),e):(G.some(n=>n.test(t))&&e.push({type:"blocker",category:"blocked-on",data:s(t),priority:2}),e)}function q(t){return t.length<=1024?[]:[{type:"data",category:"data",data:s(t),priority:4}]}var c=null;function z(t){let{tool_name:e,tool_response:o,tool_output:i}=t,n=String(o??""),r=i?.isError===!0;if(e==="Bash"&&/exit code [1-9]|error:|Error:|FAIL|failed/i.test(n)||r)return c={tool:e,error:n.slice(0,200),callsSince:0},[];if(!c)return[];if(c.callsSince++,c.callsSince>10)return c=null,[];let p=e===c.tool,u=c.tool==="Read"&&(e==="Edit"||e==="Write");if(p||u){let d={type:"error_resolved",category:"error-resolution",data:s(`Error in ${c.tool}: ${c.error} \u2192 Fixed`),priority:2};return c=null,[d]}return[]}function Z(){c=null}var l=[];function K(t){return`${t.length}:${t.slice(0,20)}`}function Q(t){let{tool_name:e,tool_input:o}=t,i=K(JSON.stringify(o).slice(0,200));if(l.push({tool:e,inputHash:i}),l.length>50&&l.splice(0,l.length-50),l.length<3)return[];let n=0;for(let r=l.length-1;r>=0&&(l[r].tool===e&&l[r].inputHash===i);r--)n++;return n>=3?(l.splice(l.length-n),[{type:"retry_detected",category:"iteration-loop",data:s(`${e} called ${n} times with similar input`),priority:2}]):[]}function X(){l.length=0}var V={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 Y(t){let e=V[t.tool_name];return!e||e===t.tool_name?t:{...t,tool_name:e}}function tt(t){try{let e=Y(t),o=[];return o.push(...b(e)),o.push(...y(e)),o.push(...h(e)),o.push(...m(e)),o.push(...v(e)),o.push(...S(e)),o.push(...E(e)),o.push(...x(e)),o.push(...R(e)),o.push(...A(e)),o.push(...N(e)),o.push(...B(e)),o.push(...w(e)),o.push(...P(e)),o.push(...O(e)),o.push(...C(e)),o.push(...z(e)),o.push(...Q(e)),o}catch{return[]}}function et(t){try{let e=[];return e.push(...M(t)),e.push(...W(t)),e.push(...F(t)),e.push(...J(t)),e.push(...q(t)),e}catch{return[]}}export{tt as extractEvents,et as extractUserEvents,Z as resetErrorResolutionState,X as resetIterationLoopState};
@@ -17,9 +17,11 @@ import "./ensure-deps.mjs";
17
17
 
18
18
  import { createRoutingBlock } from "./routing-block.mjs";
19
19
  import { createToolNamer } from "./core/tool-naming.mjs";
20
+ import { detectPlatformFromEnv } from "./core/platform-detect.mjs";
20
21
  import { buildAutoInjection } from "./auto-injection.mjs";
21
22
 
22
- const toolNamer = createToolNamer("claude-code");
23
+ const detectedPlatform = detectPlatformFromEnv();
24
+ const toolNamer = createToolNamer(detectedPlatform);
23
25
  const ROUTING_BLOCK = createRoutingBlock(toolNamer);
24
26
  import { readStdin, parseStdin, getSessionId, getSessionDBPath, getSessionEventsPath, getCleanupFlagPath, resolveConfigDir } from "./session-helpers.mjs";
25
27
  import { writeSessionEventsFile, buildSessionDirective, getSessionEvents } from "./session-directive.mjs";
@@ -2,10 +2,16 @@
2
2
  import "../suppress-stderr.mjs";
3
3
  import "../ensure-deps.mjs";
4
4
  /**
5
- * VS Code Copilot SessionStart hook for context-mode
5
+ * VS Code Copilot SessionStart hook for context-mode (v1.0.107)
6
+ *
7
+ * Created to close the v1.0.107 audit-flagged path bug: hooks.ts:98
8
+ * was resolving SessionStart to the Claude-Code generic top-level
9
+ * `hooks/sessionstart.mjs`. With the fix, the path now resolves
10
+ * to this file. Mirrors the JetBrains Copilot hook (same shape,
11
+ * same Microsoft Copilot wire contract).
6
12
  *
7
13
  * Session lifecycle management:
8
- * - "startup" → Cleanup old sessions, capture instruction file rules
14
+ * - "startup" → Cleanup old sessions, capture .github/copilot-instructions.md as rule events
9
15
  * - "compact" → Write events file, inject session knowledge directive
10
16
  * - "resume" → Load previous session events, inject directive
11
17
  * - "clear" → No action needed
@@ -24,8 +30,7 @@ import {
24
30
  } from "../session-helpers.mjs";
25
31
  import { join } from "node:path";
26
32
  import { readFileSync, unlinkSync } from "node:fs";
27
- import { fileURLToPath, pathToFileURL } from "node:url";
28
- import { homedir } from "node:os";
33
+ import { fileURLToPath } from "node:url";
29
34
 
30
35
  const HOOK_DIR = fileURLToPath(new URL(".", import.meta.url));
31
36
  const { loadSessionDB } = createSessionLoaders(HOOK_DIR);
@@ -63,10 +68,7 @@ try {
63
68
  const dbPath = getSessionDBPath(OPTS);
64
69
  const db = new SessionDB({ dbPath });
65
70
 
66
- // Filter events to the session being resumed. Falling back to
67
- // getLatestSessionEvents(db) leaks events from any other session whose
68
- // session_meta.started_at is more recent — observed cross-session bleed
69
- // when a different session started after this one and before the resume.
71
+ // Filter events to the session being resumed (cross-session bleed guard).
70
72
  const sessionId = getSessionId(input, OPTS);
71
73
  const events = sessionId ? getSessionEvents(db, sessionId) : [];
72
74
  if (events.length > 0) {
@@ -88,12 +90,9 @@ try {
88
90
  const projectDir = getProjectDir(OPTS);
89
91
  db.ensureSession(sessionId, projectDir);
90
92
 
91
- // Auto-write copilot-instructions.md on first startup if not present
92
- try {
93
- const { VSCodeCopilotAdapter } = await import(pathToFileURL(join(HOOK_DIR, "..", "..", "build", "adapters", "vscode-copilot", "index.js")).href);
94
- new VSCodeCopilotAdapter().writeRoutingInstructions(projectDir, join(HOOK_DIR, "..", ".."));
95
- } catch { /* best effort — don't block session start */ }
96
-
93
+ // VSCode Copilot's canonical project-level instruction file.
94
+ // Captured as rule_content events so they survive compact and become
95
+ // searchable via ctx_search() same pattern as Claude Code captures CLAUDE.md.
97
96
  const ruleFilePaths = [
98
97
  join(projectDir, ".github", "copilot-instructions.md"),
99
98
  ];
@@ -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.106",
6
+ "version": "1.0.107",
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.106",
3
+ "version": "1.0.107",
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",