context-mode 1.0.105 → 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 (45) 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 +185 -11
  25. package/build/pi-extension.js +123 -29
  26. package/build/server.d.ts +1 -0
  27. package/build/server.js +28 -2
  28. package/build/session/db.d.ts +12 -3
  29. package/build/session/db.js +19 -4
  30. package/build/session/extract.d.ts +1 -1
  31. package/build/session/extract.js +46 -1
  32. package/cli.bundle.mjs +128 -127
  33. package/hooks/core/platform-detect.mjs +49 -0
  34. package/hooks/core/routing.mjs +13 -1
  35. package/hooks/cursor/afteragentresponse.mjs +74 -0
  36. package/hooks/gemini-cli/beforeagent.mjs +99 -0
  37. package/hooks/kiro/agentspawn.mjs +97 -0
  38. package/hooks/kiro/userpromptsubmit.mjs +88 -0
  39. package/hooks/session-db.bundle.mjs +4 -3
  40. package/hooks/session-extract.bundle.mjs +2 -2
  41. package/hooks/sessionstart.mjs +3 -1
  42. package/hooks/vscode-copilot/sessionstart.mjs +13 -14
  43. package/openclaw.plugin.json +1 -1
  44. package/package.json +1 -1
  45. package/server.bundle.mjs +72 -71
@@ -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,4 +1,4 @@
1
- import{createRequire as I}from"node:module";import{existsSync as U,unlinkSync as v,renameSync as M}from"node:fs";import{tmpdir as x}from"node:os";import{join as F}from"node:path";var g=class{#t;constructor(t){this.#t=t}pragma(t){let e=this.#t.prepare(`PRAGMA ${t}`).all();if(!e||e.length===0)return;if(e.length>1)return e;let r=Object.values(e[0]);return r.length===1?r[0]:e[0]}exec(t){let s="",e=null;for(let o=0;o<t.length;o++){let a=t[o];if(e)s+=a,a===e&&(e=null);else if(a==="'"||a==='"')s+=a,e=a;else if(a===";"){let c=s.trim();c&&this.#t.prepare(c).run(),s=""}else s+=a}let r=s.trim();return r&&this.#t.prepare(r).run(),this}prepare(t){let s=this.#t.prepare(t);return{run:(...e)=>s.run(...e),get:(...e)=>{let r=s.get(...e);return r===null?void 0:r},all:(...e)=>s.all(...e),iterate:(...e)=>s.iterate(...e)}}transaction(t){return this.#t.transaction(t)}close(){this.#t.close()}},h=class{#t;constructor(t){this.#t=t}pragma(t){let e=this.#t.prepare(`PRAGMA ${t}`).all();if(!e||e.length===0)return;if(e.length>1)return e;let r=Object.values(e[0]);return r.length===1?r[0]:e[0]}exec(t){return this.#t.exec(t),this}prepare(t){let s=this.#t.prepare(t);return{run:(...e)=>s.run(...e),get:(...e)=>s.get(...e),all:(...e)=>s.all(...e),iterate:(...e)=>typeof s.iterate=="function"?s.iterate(...e):s.all(...e)[Symbol.iterator]()}}transaction(t){return(...s)=>{this.#t.exec("BEGIN");try{let e=t(...s);return this.#t.exec("COMMIT"),e}catch(e){throw this.#t.exec("ROLLBACK"),e}}}close(){this.#t.close()}},d=null;function B(){if(!d){let i=I(import.meta.url);if(globalThis.Bun){let t=i(["bun","sqlite"].join(":")).Database;d=function(e,r){let o=new t(e,{readonly:r?.readonly,create:!0}),a=new g(o);return r?.timeout&&a.pragma(`busy_timeout = ${r.timeout}`),a}}else if(process.platform==="linux")try{let{DatabaseSync:t}=i(["node","sqlite"].join(":"));d=function(e,r){let o=new t(e,{readOnly:r?.readonly??!1});return new h(o)}}catch{d=i("better-sqlite3")}else d=i("better-sqlite3")}return d}function b(i){i.pragma("journal_mode = WAL"),i.pragma("synchronous = NORMAL");try{i.pragma("mmap_size = 268435456")}catch{}}function N(i){if(!U(i))for(let t of["-wal","-shm"])try{v(i+t)}catch{}}function P(i){for(let t of["","-wal","-shm"])try{v(i+t)}catch{}}function y(i){try{i.pragma("wal_checkpoint(TRUNCATE)")}catch{}try{i.close()}catch{}}function C(i="context-mode"){return F(x(),`${i}-${process.pid}.db`)}function k(i,t=[100,500,2e3]){let s;for(let e=0;e<=t.length;e++)try{return i()}catch(r){let o=r instanceof Error?r.message:String(r);if(!o.includes("SQLITE_BUSY")&&!o.includes("database is locked"))throw r;if(s=r instanceof Error?r:new Error(o),e<t.length){let a=t[e],c=Date.now();for(;Date.now()-c<a;);}}throw new Error(`SQLITE_BUSY: database is locked after ${t.length} retries. Original error: ${s?.message}`)}function j(i){return i.includes("SQLITE_CORRUPT")||i.includes("SQLITE_NOTADB")||i.includes("database disk image is malformed")||i.includes("file is not a database")}function X(i){let t=Date.now();for(let s of["","-wal","-shm"])try{M(i+s,`${i}${s}.corrupt-${t}`)}catch{}}var m=Symbol.for("__context_mode_live_dbs__"),p=(()=>{let i=globalThis;return i[m]||(i[m]=new Set,process.on("exit",()=>{for(let t of i[m])y(t);i[m].clear()})),i[m]})(),T=class{#t;#e;constructor(t){let s=B();this.#t=t,N(t);let e;try{e=new s(t,{timeout:3e4}),b(e)}catch(r){let o=r instanceof Error?r.message:String(r);if(j(o)){X(t),N(t);try{e=new s(t,{timeout:3e4}),b(e)}catch(a){throw new Error(`Failed to create fresh DB after renaming corrupt file: ${a instanceof Error?a.message:String(a)}`)}}else throw r}this.#e=e,p.add(this.#e),this.initSchema(),this.prepareStatements()}get db(){return this.#e}get dbPath(){return this.#t}close(){p.delete(this.#e),y(this.#e)}withRetry(t){return k(t)}cleanup(){p.delete(this.#e),y(this.#e),P(this.#t)}};import{createHash as f}from"node:crypto";import{execFileSync as W}from"node:child_process";var l;function z(){let i=process.env.CONTEXT_MODE_SESSION_SUFFIX,t=process.cwd();if(l&&l.cwd===t&&l.envSuffix===i)return l.suffix;let s="";if(i!==void 0)s=i?`__${i}`:"";else try{let e=W("git",["worktree","list","--porcelain"],{encoding:"utf-8",timeout:2e3,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).find(r=>r.startsWith("worktree "))?.replace("worktree ","")?.trim();e&&t!==e&&(s=`__${f("sha256").update(t).digest("hex").slice(0,8)}`)}catch{}return l={cwd:t,envSuffix:i,suffix:s},s}function J(){l=void 0}var O=1e3,D=5,n={insertEvent:"insertEvent",getEvents:"getEvents",getEventsByType:"getEventsByType",getEventsByPriority:"getEventsByPriority",getEventsByTypeAndPriority:"getEventsByTypeAndPriority",getEventCount:"getEventCount",getLatestAttributedProject:"getLatestAttributedProject",checkDuplicate:"checkDuplicate",evictLowestPriority:"evictLowestPriority",updateMetaLastEvent:"updateMetaLastEvent",ensureSession:"ensureSession",getSessionStats:"getSessionStats",incrementCompactCount:"incrementCompactCount",upsertResume:"upsertResume",getResume:"getResume",markResumeConsumed:"markResumeConsumed",claimLatestUnconsumedResume:"claimLatestUnconsumedResume",deleteEvents:"deleteEvents",deleteMeta:"deleteMeta",deleteResume:"deleteResume",getOldSessions:"getOldSessions",searchEvents:"searchEvents",incrementToolCall:"incrementToolCall",getToolCallTotals:"getToolCallTotals",getToolCallByTool:"getToolCallByTool"},A=class extends T{constructor(t){super(t?.dbPath??C("session"))}stmt(t){return this.stmts.get(t)}initSchema(){try{let s=this.db.pragma("table_xinfo(session_events)").find(e=>e.name==="data_hash");s&&s.hidden!==0&&this.db.exec("DROP TABLE session_events")}catch{}this.db.exec(`
1
+ import{createRequire as I}from"node:module";import{existsSync as U,unlinkSync as v,renameSync as M}from"node:fs";import{tmpdir as x}from"node:os";import{join as F}from"node:path";var g=class{#t;constructor(t){this.#t=t}pragma(t){let s=this.#t.prepare(`PRAGMA ${t}`).all();if(!s||s.length===0)return;if(s.length>1)return s;let r=Object.values(s[0]);return r.length===1?r[0]:s[0]}exec(t){let e="",s=null;for(let o=0;o<t.length;o++){let a=t[o];if(s)e+=a,a===s&&(s=null);else if(a==="'"||a==='"')e+=a,s=a;else if(a===";"){let c=e.trim();c&&this.#t.prepare(c).run(),e=""}else e+=a}let r=e.trim();return r&&this.#t.prepare(r).run(),this}prepare(t){let e=this.#t.prepare(t);return{run:(...s)=>e.run(...s),get:(...s)=>{let r=e.get(...s);return r===null?void 0:r},all:(...s)=>e.all(...s),iterate:(...s)=>e.iterate(...s)}}transaction(t){return this.#t.transaction(t)}close(){this.#t.close()}},h=class{#t;constructor(t){this.#t=t}pragma(t){let s=this.#t.prepare(`PRAGMA ${t}`).all();if(!s||s.length===0)return;if(s.length>1)return s;let r=Object.values(s[0]);return r.length===1?r[0]:s[0]}exec(t){return this.#t.exec(t),this}prepare(t){let e=this.#t.prepare(t);return{run:(...s)=>e.run(...s),get:(...s)=>e.get(...s),all:(...s)=>e.all(...s),iterate:(...s)=>typeof e.iterate=="function"?e.iterate(...s):e.all(...s)[Symbol.iterator]()}}transaction(t){return(...e)=>{this.#t.exec("BEGIN");try{let s=t(...e);return this.#t.exec("COMMIT"),s}catch(s){throw this.#t.exec("ROLLBACK"),s}}}close(){this.#t.close()}},d=null;function B(){if(!d){let i=I(import.meta.url);if(globalThis.Bun){let t=i(["bun","sqlite"].join(":")).Database;d=function(s,r){let o=new t(s,{readonly:r?.readonly,create:!0}),a=new g(o);return r?.timeout&&a.pragma(`busy_timeout = ${r.timeout}`),a}}else if(process.platform==="linux")try{let{DatabaseSync:t}=i(["node","sqlite"].join(":"));d=function(s,r){let o=new t(s,{readOnly:r?.readonly??!1});return new h(o)}}catch{d=i("better-sqlite3")}else d=i("better-sqlite3")}return d}function b(i){i.pragma("journal_mode = WAL"),i.pragma("synchronous = NORMAL");try{i.pragma("mmap_size = 268435456")}catch{}}function N(i){if(!U(i))for(let t of["-wal","-shm"])try{v(i+t)}catch{}}function P(i){for(let t of["","-wal","-shm"])try{v(i+t)}catch{}}function y(i){try{i.pragma("wal_checkpoint(TRUNCATE)")}catch{}try{i.close()}catch{}}function C(i="context-mode"){return F(x(),`${i}-${process.pid}.db`)}function k(i,t=[100,500,2e3]){let e;for(let s=0;s<=t.length;s++)try{return i()}catch(r){let o=r instanceof Error?r.message:String(r);if(!o.includes("SQLITE_BUSY")&&!o.includes("database is locked"))throw r;if(e=r instanceof Error?r:new Error(o),s<t.length){let a=t[s],c=Date.now();for(;Date.now()-c<a;);}}throw new Error(`SQLITE_BUSY: database is locked after ${t.length} retries. Original error: ${e?.message}`)}function j(i){return i.includes("SQLITE_CORRUPT")||i.includes("SQLITE_NOTADB")||i.includes("database disk image is malformed")||i.includes("file is not a database")}function X(i){let t=Date.now();for(let e of["","-wal","-shm"])try{M(i+e,`${i}${e}.corrupt-${t}`)}catch{}}var m=Symbol.for("__context_mode_live_dbs__"),p=(()=>{let i=globalThis;return i[m]||(i[m]=new Set,process.on("exit",()=>{for(let t of i[m])y(t);i[m].clear()})),i[m]})(),T=class{#t;#e;constructor(t){let e=B();this.#t=t,N(t);let s;try{s=new e(t,{timeout:3e4}),b(s)}catch(r){let o=r instanceof Error?r.message:String(r);if(j(o)){X(t),N(t);try{s=new e(t,{timeout:3e4}),b(s)}catch(a){throw new Error(`Failed to create fresh DB after renaming corrupt file: ${a instanceof Error?a.message:String(a)}`)}}else throw r}this.#e=s,p.add(this.#e),this.initSchema(),this.prepareStatements()}get db(){return this.#e}get dbPath(){return this.#t}close(){p.delete(this.#e),y(this.#e)}withRetry(t){return k(t)}cleanup(){p.delete(this.#e),y(this.#e),P(this.#t)}};import{createHash as f}from"node:crypto";import{execFileSync as W}from"node:child_process";var l;function z(){let i=process.env.CONTEXT_MODE_SESSION_SUFFIX,t=process.cwd();if(l&&l.cwd===t&&l.envSuffix===i)return l.suffix;let e="";if(i!==void 0)e=i?`__${i}`:"";else try{let s=W("git",["worktree","list","--porcelain"],{encoding:"utf-8",timeout:2e3,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).find(r=>r.startsWith("worktree "))?.replace("worktree ","")?.trim();s&&t!==s&&(e=`__${f("sha256").update(t).digest("hex").slice(0,8)}`)}catch{}return l={cwd:t,envSuffix:i,suffix:e},e}function J(){l=void 0}var D=1e3,O=5,n={insertEvent:"insertEvent",getEvents:"getEvents",getEventsByType:"getEventsByType",getEventsByPriority:"getEventsByPriority",getEventsByTypeAndPriority:"getEventsByTypeAndPriority",getEventCount:"getEventCount",getLatestAttributedProject:"getLatestAttributedProject",checkDuplicate:"checkDuplicate",evictLowestPriority:"evictLowestPriority",updateMetaLastEvent:"updateMetaLastEvent",ensureSession:"ensureSession",getSessionStats:"getSessionStats",incrementCompactCount:"incrementCompactCount",upsertResume:"upsertResume",getResume:"getResume",markResumeConsumed:"markResumeConsumed",claimLatestUnconsumedResume:"claimLatestUnconsumedResume",deleteEvents:"deleteEvents",deleteMeta:"deleteMeta",deleteResume:"deleteResume",getOldSessions:"getOldSessions",searchEvents:"searchEvents",incrementToolCall:"incrementToolCall",getToolCallTotals:"getToolCallTotals",getToolCallByTool:"getToolCallByTool"},A=class extends T{constructor(t){super(t?.dbPath??C("session"))}stmt(t){return this.stmts.get(t)}initSchema(){try{let e=this.db.pragma("table_xinfo(session_events)").find(s=>s.name==="data_hash");e&&e.hidden!==0&&this.db.exec("DROP TABLE session_events")}catch{}this.db.exec(`
2
2
  CREATE TABLE IF NOT EXISTS session_events (
3
3
  id INTEGER PRIMARY KEY AUTOINCREMENT,
4
4
  session_id TEXT NOT NULL,
@@ -46,7 +46,7 @@ import{createRequire as I}from"node:module";import{existsSync as U,unlinkSync as
46
46
  );
47
47
 
48
48
  CREATE INDEX IF NOT EXISTS idx_tool_calls_session ON tool_calls(session_id);
49
- `);try{let t=this.db.pragma("table_xinfo(session_events)"),s=new Set(t.map(e=>e.name));s.has("project_dir")||this.db.exec("ALTER TABLE session_events ADD COLUMN project_dir TEXT NOT NULL DEFAULT ''"),s.has("attribution_source")||this.db.exec("ALTER TABLE session_events ADD COLUMN attribution_source TEXT NOT NULL DEFAULT 'unknown'"),s.has("attribution_confidence")||this.db.exec("ALTER TABLE session_events ADD COLUMN attribution_confidence REAL NOT NULL DEFAULT 0"),this.db.exec("CREATE INDEX IF NOT EXISTS idx_session_events_project ON session_events(session_id, project_dir)")}catch{}}prepareStatements(){this.stmts=new Map;let t=(s,e)=>{this.stmts.set(s,this.db.prepare(e))};t(n.insertEvent,`INSERT INTO session_events (
49
+ `);try{let t=this.db.pragma("table_xinfo(session_events)"),e=new Set(t.map(s=>s.name));e.has("project_dir")||this.db.exec("ALTER TABLE session_events ADD COLUMN project_dir TEXT NOT NULL DEFAULT ''"),e.has("attribution_source")||this.db.exec("ALTER TABLE session_events ADD COLUMN attribution_source TEXT NOT NULL DEFAULT 'unknown'"),e.has("attribution_confidence")||this.db.exec("ALTER TABLE session_events ADD COLUMN attribution_confidence REAL NOT NULL DEFAULT 0"),this.db.exec("CREATE INDEX IF NOT EXISTS idx_session_events_project ON session_events(session_id, project_dir)")}catch{}}prepareStatements(){this.stmts=new Map;let t=(e,s)=>{this.stmts.set(e,this.db.prepare(s))};t(n.insertEvent,`INSERT INTO session_events (
50
50
  session_id, type, category, priority, data,
51
51
  project_dir, attribution_source, attribution_confidence,
52
52
  source_hook, data_hash
@@ -89,6 +89,7 @@ import{createRequire as I}from"node:module";import{existsSync as U,unlinkSync as
89
89
  WHERE id = (
90
90
  SELECT id FROM session_resume
91
91
  WHERE consumed = 0
92
+ AND session_id != ?
92
93
  ORDER BY created_at DESC, id DESC
93
94
  LIMIT 1
94
95
  )
@@ -106,4 +107,4 @@ import{createRequire as I}from"node:module";import{existsSync as U,unlinkSync as
106
107
  updated_at = datetime('now')`),t(n.getToolCallTotals,`SELECT COALESCE(SUM(calls), 0) AS calls,
107
108
  COALESCE(SUM(bytes_returned), 0) AS bytes_returned
108
109
  FROM tool_calls WHERE session_id = ?`),t(n.getToolCallByTool,`SELECT tool, calls, bytes_returned
109
- FROM tool_calls WHERE session_id = ? ORDER BY calls DESC`)}insertEvent(t,s,e="PostToolUse",r){let o=f("sha256").update(s.data).digest("hex").slice(0,16).toUpperCase(),a=String(r?.projectDir??s.project_dir??"").trim(),c=String(r?.source??s.attribution_source??"unknown"),u=Number(r?.confidence??s.attribution_confidence??0),_=Number.isFinite(u)?Math.max(0,Math.min(1,u)):0,E=this.db.transaction(()=>{if(this.stmt(n.checkDuplicate).get(t,D,s.type,o))return;this.stmt(n.getEventCount).get(t).cnt>=O&&this.stmt(n.evictLowestPriority).run(t),this.stmt(n.insertEvent).run(t,s.type,s.category,s.priority,s.data,a,c,_,e,o),this.stmt(n.updateMetaLastEvent).run(t)});this.withRetry(()=>E())}bulkInsertEvents(t,s,e="PostToolUse",r){if(!s||s.length===0)return;if(s.length===1){this.insertEvent(t,s[0],e,r?.[0]);return}let o=s.map((c,u)=>{let _=f("sha256").update(c.data).digest("hex").slice(0,16).toUpperCase(),E=r?.[u],S=String(E?.projectDir??c.project_dir??"").trim(),L=String(E?.source??c.attribution_source??"unknown"),R=Number(E?.confidence??c.attribution_confidence??0),w=Number.isFinite(R)?Math.max(0,Math.min(1,R)):0;return{event:c,dataHash:_,projectDir:S,attributionSource:L,attributionConfidence:w}}),a=this.db.transaction(()=>{let c=this.stmt(n.getEventCount).get(t).cnt;for(let u of o)this.stmt(n.checkDuplicate).get(t,D,u.event.type,u.dataHash)||(c>=O?this.stmt(n.evictLowestPriority).run(t):c++,this.stmt(n.insertEvent).run(t,u.event.type,u.event.category,u.event.priority,u.event.data,u.projectDir,u.attributionSource,u.attributionConfidence,e,u.dataHash));this.stmt(n.updateMetaLastEvent).run(t)});this.withRetry(()=>a())}getEvents(t,s){let e=s?.limit??1e3,r=s?.type,o=s?.minPriority;return r&&o!==void 0?this.stmt(n.getEventsByTypeAndPriority).all(t,r,o,e):r?this.stmt(n.getEventsByType).all(t,r,e):o!==void 0?this.stmt(n.getEventsByPriority).all(t,o,e):this.stmt(n.getEvents).all(t,e)}getEventCount(t){return this.stmt(n.getEventCount).get(t).cnt}getLatestAttributedProjectDir(t){return this.stmt(n.getLatestAttributedProject).get(t)?.project_dir||null}searchEvents(t,s,e,r){try{let o=t.replace(/[%_]/g,c=>"\\"+c),a=r??null;return this.stmt(n.searchEvents).all(e,o,o,a,a,s)}catch{return[]}}ensureSession(t,s){this.stmt(n.ensureSession).run(t,s)}getSessionStats(t){return this.stmt(n.getSessionStats).get(t)??null}incrementCompactCount(t){this.stmt(n.incrementCompactCount).run(t)}upsertResume(t,s,e){this.stmt(n.upsertResume).run(t,s,e??0)}getResume(t){return this.stmt(n.getResume).get(t)??null}markResumeConsumed(t){this.stmt(n.markResumeConsumed).run(t)}claimLatestUnconsumedResume(){let t=this.stmt(n.claimLatestUnconsumedResume).get();return t?{sessionId:t.session_id,snapshot:t.snapshot}:null}getLatestSessionId(){try{return this.db.prepare("SELECT session_id FROM session_meta ORDER BY started_at DESC LIMIT 1").get()?.session_id??null}catch{return null}}incrementToolCall(t,s,e=0){let r=Number.isFinite(e)&&e>0?Math.round(e):0;try{this.stmt(n.incrementToolCall).run(t,s,r)}catch{}}getToolCallStats(t){try{let s=this.stmt(n.getToolCallTotals).get(t),e=this.stmt(n.getToolCallByTool).all(t),r={};for(let o of e)r[o.tool]={calls:o.calls,bytesReturned:o.bytes_returned};return{totalCalls:s?.calls??0,totalBytesReturned:s?.bytes_returned??0,byTool:r}}catch{return{totalCalls:0,totalBytesReturned:0,byTool:{}}}}deleteSession(t){this.db.transaction(()=>{this.stmt(n.deleteEvents).run(t),this.stmt(n.deleteResume).run(t),this.stmt(n.deleteMeta).run(t)})()}cleanupOldSessions(t=7){let s=`-${t}`,e=this.stmt(n.getOldSessions).all(s);for(let{session_id:r}of e)this.deleteSession(r);return e.length}};export{A as SessionDB,J as _resetWorktreeSuffixCacheForTests,z as getWorktreeSuffix};
110
+ FROM tool_calls WHERE session_id = ? ORDER BY calls DESC`)}insertEvent(t,e,s="PostToolUse",r){let o=f("sha256").update(e.data).digest("hex").slice(0,16).toUpperCase(),a=String(r?.projectDir??e.project_dir??"").trim(),c=String(r?.source??e.attribution_source??"unknown"),u=Number(r?.confidence??e.attribution_confidence??0),_=Number.isFinite(u)?Math.max(0,Math.min(1,u)):0,E=this.db.transaction(()=>{if(this.stmt(n.checkDuplicate).get(t,O,e.type,o))return;this.stmt(n.getEventCount).get(t).cnt>=D&&this.stmt(n.evictLowestPriority).run(t),this.stmt(n.insertEvent).run(t,e.type,e.category,e.priority,e.data,a,c,_,s,o),this.stmt(n.updateMetaLastEvent).run(t)});this.withRetry(()=>E())}bulkInsertEvents(t,e,s="PostToolUse",r){if(!e||e.length===0)return;if(e.length===1){this.insertEvent(t,e[0],s,r?.[0]);return}let o=e.map((c,u)=>{let _=f("sha256").update(c.data).digest("hex").slice(0,16).toUpperCase(),E=r?.[u],S=String(E?.projectDir??c.project_dir??"").trim(),L=String(E?.source??c.attribution_source??"unknown"),R=Number(E?.confidence??c.attribution_confidence??0),w=Number.isFinite(R)?Math.max(0,Math.min(1,R)):0;return{event:c,dataHash:_,projectDir:S,attributionSource:L,attributionConfidence:w}}),a=this.db.transaction(()=>{let c=this.stmt(n.getEventCount).get(t).cnt;for(let u of o)this.stmt(n.checkDuplicate).get(t,O,u.event.type,u.dataHash)||(c>=D?this.stmt(n.evictLowestPriority).run(t):c++,this.stmt(n.insertEvent).run(t,u.event.type,u.event.category,u.event.priority,u.event.data,u.projectDir,u.attributionSource,u.attributionConfidence,s,u.dataHash));this.stmt(n.updateMetaLastEvent).run(t)});this.withRetry(()=>a())}getEvents(t,e){let s=e?.limit??1e3,r=e?.type,o=e?.minPriority;return r&&o!==void 0?this.stmt(n.getEventsByTypeAndPriority).all(t,r,o,s):r?this.stmt(n.getEventsByType).all(t,r,s):o!==void 0?this.stmt(n.getEventsByPriority).all(t,o,s):this.stmt(n.getEvents).all(t,s)}getEventCount(t){return this.stmt(n.getEventCount).get(t).cnt}getLatestAttributedProjectDir(t){return this.stmt(n.getLatestAttributedProject).get(t)?.project_dir||null}searchEvents(t,e,s,r){try{let o=t.replace(/[%_]/g,c=>"\\"+c),a=r??null;return this.stmt(n.searchEvents).all(s,o,o,a,a,e)}catch{return[]}}ensureSession(t,e){this.stmt(n.ensureSession).run(t,e)}getSessionStats(t){return this.stmt(n.getSessionStats).get(t)??null}incrementCompactCount(t){this.stmt(n.incrementCompactCount).run(t)}upsertResume(t,e,s){this.stmt(n.upsertResume).run(t,e,s??0)}getResume(t){return this.stmt(n.getResume).get(t)??null}markResumeConsumed(t){this.stmt(n.markResumeConsumed).run(t)}claimLatestUnconsumedResume(t){let e=this.stmt(n.claimLatestUnconsumedResume).get(t);return e?{sessionId:e.session_id,snapshot:e.snapshot}:null}getLatestSessionId(){try{return this.db.prepare("SELECT session_id FROM session_meta ORDER BY started_at DESC LIMIT 1").get()?.session_id??null}catch{return null}}incrementToolCall(t,e,s=0){let r=Number.isFinite(s)&&s>0?Math.round(s):0;try{this.stmt(n.incrementToolCall).run(t,e,r)}catch{}}getToolCallStats(t){try{let e=this.stmt(n.getToolCallTotals).get(t),s=this.stmt(n.getToolCallByTool).all(t),r={};for(let o of s)r[o.tool]={calls:o.calls,bytesReturned:o.bytes_returned};return{totalCalls:e?.calls??0,totalBytesReturned:e?.bytes_returned??0,byTool:r}}catch{return{totalCalls:0,totalBytesReturned:0,byTool:{}}}}deleteSession(t){this.db.transaction(()=>{this.stmt(n.deleteEvents).run(t),this.stmt(n.deleteResume).run(t),this.stmt(n.deleteMeta).run(t)})()}cleanupOldSessions(t=7){let e=`-${t}`,s=this.stmt(n.getOldSessions).all(e);for(let{session_id:r}of s)this.deleteSession(r);return s.length}};export{A as SessionDB,J as _resetWorktreeSuffixCacheForTests,z as getWorktreeSuffix};
@@ -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";