context-mode 1.0.161 → 1.0.163
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/README.md +142 -28
- package/bin/statusline.mjs +24 -4
- package/build/adapters/antigravity/index.d.ts +1 -1
- package/build/adapters/antigravity-cli/index.d.ts +51 -0
- package/build/adapters/antigravity-cli/index.js +341 -0
- package/build/adapters/claude-code/hooks.d.ts +1 -0
- package/build/adapters/claude-code/hooks.js +3 -0
- package/build/adapters/claude-code/index.js +24 -5
- package/build/adapters/client-map.js +5 -0
- package/build/adapters/codex/hooks.d.ts +5 -1
- package/build/adapters/codex/hooks.js +5 -1
- package/build/adapters/codex/index.d.ts +9 -1
- package/build/adapters/codex/index.js +87 -5
- package/build/adapters/copilot-cli/hooks.d.ts +33 -0
- package/build/adapters/copilot-cli/hooks.js +64 -0
- package/build/adapters/copilot-cli/index.d.ts +48 -0
- package/build/adapters/copilot-cli/index.js +341 -0
- package/build/adapters/detect.d.ts +1 -1
- package/build/adapters/detect.js +71 -3
- package/build/adapters/openclaw/mcp-tools.js +1 -1
- package/build/adapters/opencode/index.js +31 -17
- package/build/adapters/opencode/zod3tov4.js +27 -6
- package/build/adapters/pi/extension.d.ts +2 -12
- package/build/adapters/pi/extension.js +114 -96
- package/build/adapters/types.d.ts +5 -4
- package/build/adapters/types.js +4 -3
- package/build/cache-heal.d.ts +48 -0
- package/build/cache-heal.js +150 -0
- package/build/cli.js +37 -97
- package/build/executor.d.ts +25 -0
- package/build/executor.js +143 -22
- package/build/opencode-plugin.js +5 -2
- package/build/routing-block.d.ts +8 -0
- package/build/routing-block.js +86 -0
- package/build/runtime.d.ts +0 -36
- package/build/runtime.js +107 -27
- package/build/search/flood-guard.d.ts +57 -0
- package/build/search/flood-guard.js +80 -0
- package/build/security.d.ts +8 -3
- package/build/security.js +155 -29
- package/build/server.d.ts +14 -0
- package/build/server.js +368 -350
- package/build/session/analytics.d.ts +8 -8
- package/build/session/analytics.js +18 -13
- package/build/session/db.d.ts +1 -0
- package/build/session/db.js +37 -4
- package/build/session/extract.d.ts +46 -0
- package/build/session/extract.js +764 -13
- package/build/session/project-attribution.js +14 -0
- package/build/store.d.ts +1 -1
- package/build/store.js +139 -25
- package/build/tool-naming.d.ts +4 -0
- package/build/tool-naming.js +24 -0
- package/build/util/jsonc.d.ts +14 -0
- package/build/util/jsonc.js +104 -0
- package/cli.bundle.mjs +260 -254
- package/configs/antigravity/GEMINI.md +2 -2
- package/configs/antigravity-cli/hooks/hooks.json +37 -0
- package/configs/antigravity-cli/hooks.json +37 -0
- package/configs/antigravity-cli/mcp_config.json +10 -0
- package/configs/antigravity-cli/plugin.json +14 -0
- package/configs/antigravity-cli/rules/context-mode.md +77 -0
- package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
- package/configs/claude-code/CLAUDE.md +2 -2
- package/configs/codex/AGENTS.md +2 -2
- package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
- package/configs/copilot-cli/.mcp.json +12 -0
- package/configs/copilot-cli/README.md +47 -0
- package/configs/copilot-cli/hooks.json +41 -0
- package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
- package/configs/gemini-cli/GEMINI.md +2 -2
- package/configs/jetbrains-copilot/copilot-instructions.md +2 -2
- package/configs/kilo/AGENTS.md +2 -2
- package/configs/kiro/KIRO.md +2 -2
- package/configs/omp/SYSTEM.md +2 -2
- package/configs/openclaw/AGENTS.md +2 -2
- package/configs/opencode/AGENTS.md +2 -2
- package/configs/qwen-code/QWEN.md +2 -2
- package/configs/vscode-copilot/copilot-instructions.md +2 -2
- package/configs/zed/AGENTS.md +2 -2
- package/hooks/antigravity-cli/payload.mjs +98 -0
- package/hooks/antigravity-cli/posttooluse.mjs +138 -0
- package/hooks/antigravity-cli/pretooluse.mjs +78 -0
- package/hooks/antigravity-cli/stop.mjs +58 -0
- package/hooks/codex/pretooluse.mjs +14 -4
- package/hooks/codex/stop.mjs +12 -4
- package/hooks/copilot-cli/posttooluse.mjs +79 -0
- package/hooks/copilot-cli/precompact.mjs +66 -0
- package/hooks/copilot-cli/pretooluse.mjs +41 -0
- package/hooks/copilot-cli/sessionstart.mjs +121 -0
- package/hooks/copilot-cli/stop.mjs +59 -0
- package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
- package/hooks/core/codex-caps.mjs +112 -0
- package/hooks/core/formatters.mjs +158 -7
- package/hooks/core/mcp-ready.mjs +37 -8
- package/hooks/core/routing.mjs +94 -8
- package/hooks/core/tool-naming.mjs +3 -0
- package/hooks/hooks.json +12 -1
- package/hooks/pretooluse.mjs +6 -2
- package/hooks/routing-block.mjs +2 -2
- package/hooks/security.bundle.mjs +2 -1
- package/hooks/session-db.bundle.mjs +11 -7
- package/hooks/session-directive.mjs +88 -20
- package/hooks/session-extract.bundle.mjs +2 -2
- package/hooks/session-helpers.mjs +21 -0
- package/hooks/session-loaders.mjs +8 -5
- package/hooks/sessionstart.mjs +53 -7
- package/hooks/stop.mjs +49 -0
- package/hooks/userpromptsubmit.mjs +9 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +4 -10
- package/scripts/install-antigravity-cli-plugin.mjs +141 -0
- package/server.bundle.mjs +214 -205
- package/skills/ctx-insight/SKILL.md +12 -17
- package/build/util/db-lock.d.ts +0 -65
- package/build/util/db-lock.js +0 -166
- package/insight/index.html +0 -13
- package/insight/package.json +0 -55
- package/insight/server.mjs +0 -1265
- package/insight/src/components/analytics.tsx +0 -112
- package/insight/src/components/ui/badge.tsx +0 -52
- package/insight/src/components/ui/button.tsx +0 -58
- package/insight/src/components/ui/card.tsx +0 -103
- package/insight/src/components/ui/chart.tsx +0 -371
- package/insight/src/components/ui/collapsible.tsx +0 -19
- package/insight/src/components/ui/input.tsx +0 -20
- package/insight/src/components/ui/progress.tsx +0 -83
- package/insight/src/components/ui/scroll-area.tsx +0 -55
- package/insight/src/components/ui/separator.tsx +0 -23
- package/insight/src/components/ui/table.tsx +0 -114
- package/insight/src/components/ui/tabs.tsx +0 -82
- package/insight/src/components/ui/tooltip.tsx +0 -64
- package/insight/src/lib/api.ts +0 -144
- package/insight/src/lib/utils.ts +0 -6
- package/insight/src/main.tsx +0 -22
- package/insight/src/routeTree.gen.ts +0 -189
- package/insight/src/router.tsx +0 -19
- package/insight/src/routes/__root.tsx +0 -55
- package/insight/src/routes/enterprise.tsx +0 -316
- package/insight/src/routes/index.tsx +0 -1482
- package/insight/src/routes/knowledge.tsx +0 -221
- package/insight/src/routes/knowledge_.$dbHash.$sourceId.tsx +0 -137
- package/insight/src/routes/search.tsx +0 -97
- package/insight/src/routes/sessions.tsx +0 -179
- package/insight/src/routes/sessions_.$dbHash.$sessionId.tsx +0 -181
- package/insight/src/styles.css +0 -104
- package/insight/tsconfig.json +0 -29
- package/insight/vite.config.ts +0 -19
package/hooks/core/routing.mjs
CHANGED
|
@@ -25,7 +25,8 @@ import { existsSync, mkdirSync, rmSync, rmdirSync, readdirSync, unlinkSync, open
|
|
|
25
25
|
* redirect action — prevents agent from getting stuck when MCP tools
|
|
26
26
|
* are unavailable. Applies to deny and modify actions that mention MCP alternatives.
|
|
27
27
|
*/
|
|
28
|
-
function mcpRedirect(result) {
|
|
28
|
+
function mcpRedirect(result, mcpToolsAvailable = true) {
|
|
29
|
+
if (!mcpToolsAvailable) return null;
|
|
29
30
|
if (!isMCPReady()) return null;
|
|
30
31
|
return result;
|
|
31
32
|
}
|
|
@@ -75,6 +76,37 @@ function getExternalMcpNudgeEvery() {
|
|
|
75
76
|
return parsed;
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
// #817: size threshold so small Bash calls skip the routing nudge.
|
|
80
|
+
//
|
|
81
|
+
// PreToolUse fires BEFORE the command runs, so the actual output size is
|
|
82
|
+
// unknowable here. The only deterministic pre-execution signal is the command
|
|
83
|
+
// string itself. The Gemini CLI adapter solves the same over-interception
|
|
84
|
+
// problem with a matcher that only fires on large-output tools — "avoids
|
|
85
|
+
// unnecessary hook overhead on lightweight tools" (README). We mirror that at
|
|
86
|
+
// the routing layer: when CONTEXT_MODE_BASH_NUDGE_MIN_COMMAND_BYTES is set to
|
|
87
|
+
// N>0, an unbounded Bash command whose UTF-8 byte length is below N is treated
|
|
88
|
+
// as expected-lightweight and the generic routing nudge is suppressed.
|
|
89
|
+
//
|
|
90
|
+
// Default is 0 (unset) → CURRENT BEHAVIOR: every unbounded command is nudged.
|
|
91
|
+
// This preserves the context-saving guarantee for large outputs by default —
|
|
92
|
+
// the threshold is strictly opt-in. Bounds [0, 100000]; invalid/zero/negative
|
|
93
|
+
// values fall back to 0 (disabled). The threshold gates ONLY the generic Bash
|
|
94
|
+
// nudge — curl/wget, inline-HTTP, and build-tool redirects run earlier and are
|
|
95
|
+
// never relaxed, because those are deterministic floods regardless of command
|
|
96
|
+
// length.
|
|
97
|
+
const BASH_NUDGE_MIN_BYTES_ENV = "CONTEXT_MODE_BASH_NUDGE_MIN_COMMAND_BYTES";
|
|
98
|
+
const BASH_NUDGE_MIN_BYTES_MAX = 100_000;
|
|
99
|
+
|
|
100
|
+
function getBashNudgeMinCommandBytes() {
|
|
101
|
+
const raw = process.env[BASH_NUDGE_MIN_BYTES_ENV];
|
|
102
|
+
if (raw == null || raw === "") return 0;
|
|
103
|
+
const parsed = Number.parseInt(raw, 10);
|
|
104
|
+
if (!Number.isFinite(parsed) || parsed <= 0 || parsed > BASH_NUDGE_MIN_BYTES_MAX) {
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
return parsed;
|
|
108
|
+
}
|
|
109
|
+
|
|
78
110
|
function defaultGuidanceId() {
|
|
79
111
|
return process.env.VITEST_WORKER_ID
|
|
80
112
|
? `${process.ppid}-w${process.env.VITEST_WORKER_ID}`
|
|
@@ -476,6 +508,14 @@ const TOOL_ALIASES = {
|
|
|
476
508
|
"grep_search": "Grep",
|
|
477
509
|
"search_file_content": "Grep",
|
|
478
510
|
"web_fetch": "WebFetch",
|
|
511
|
+
"read_url_content": "WebFetch",
|
|
512
|
+
// Antigravity CLI (`agy`) native tool names. Keep in sync with the two other
|
|
513
|
+
// agy maps: hooks/antigravity-cli/payload.mjs (normalizeAgyToolName) and
|
|
514
|
+
// src/session/extract.ts (TOOL_NAME_NORMALIZE).
|
|
515
|
+
"run_command": "Bash",
|
|
516
|
+
"view_file": "Read",
|
|
517
|
+
"list_dir": "LS",
|
|
518
|
+
"search_web": "WebSearch",
|
|
479
519
|
// Qwen Code additional tool names (no routing branch yet but normalized
|
|
480
520
|
// so future routing logic works without per-platform fallback):
|
|
481
521
|
"write_file": "Write",
|
|
@@ -580,6 +620,24 @@ function getShellCommand(toolInput) {
|
|
|
580
620
|
if (!toolInput || typeof toolInput !== "object") return "";
|
|
581
621
|
if (typeof toolInput.command === "string") return toolInput.command;
|
|
582
622
|
if (typeof toolInput.cmd === "string") return toolInput.cmd;
|
|
623
|
+
if (typeof toolInput.CommandLine === "string") return toolInput.CommandLine;
|
|
624
|
+
return "";
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function getReadFilePath(toolInput) {
|
|
628
|
+
if (!toolInput || typeof toolInput !== "object") return "";
|
|
629
|
+
if (typeof toolInput.file_path === "string") return toolInput.file_path;
|
|
630
|
+
if (typeof toolInput.path === "string") return toolInput.path;
|
|
631
|
+
if (typeof toolInput.AbsolutePath === "string") return toolInput.AbsolutePath;
|
|
632
|
+
if (typeof toolInput.FilePath === "string") return toolInput.FilePath;
|
|
633
|
+
return "";
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function getWebFetchUrl(toolInput) {
|
|
637
|
+
if (!toolInput || typeof toolInput !== "object") return "";
|
|
638
|
+
if (typeof toolInput.url === "string") return toolInput.url;
|
|
639
|
+
if (typeof toolInput.URL === "string") return toolInput.URL;
|
|
640
|
+
if (typeof toolInput.Url === "string") return toolInput.Url;
|
|
583
641
|
return "";
|
|
584
642
|
}
|
|
585
643
|
|
|
@@ -604,8 +662,14 @@ function getPlatformSettingsPath(platform) {
|
|
|
604
662
|
* @param {string} [sessionId] - Stable session identifier from hook payload. When
|
|
605
663
|
* provided, the guidance throttle uses it to scope marker files across hook
|
|
606
664
|
* invocations even when process.ppid shifts (Windows/Git Bash — see #298).
|
|
665
|
+
* @param {object} [options] - Runtime routing context from the adapter.
|
|
666
|
+
* @param {boolean} [options.mcpToolsAvailable=true] - False when the current
|
|
667
|
+
* caller context cannot invoke ctx_* MCP tools even though an MCP server is
|
|
668
|
+
* live on the machine (Claude Code fixed-tool subagents — #794).
|
|
607
669
|
*/
|
|
608
|
-
export function routePreToolUse(toolName, toolInput, projectDir, platform, sessionId) {
|
|
670
|
+
export function routePreToolUse(toolName, toolInput, projectDir, platform, sessionId, options = {}) {
|
|
671
|
+
const mcpToolsAvailable = options.mcpToolsAvailable !== false;
|
|
672
|
+
|
|
609
673
|
// ─── Opt-in fail-closed gate (#468 follow-up) ───
|
|
610
674
|
// Default behavior on security-module load failure is fail-OPEN (a stderr
|
|
611
675
|
// warning is emitted but routing continues). Security-conscious users can
|
|
@@ -715,7 +779,7 @@ export function routePreToolUse(toolName, toolInput, projectDir, platform, sessi
|
|
|
715
779
|
bytesAvoided: 8192,
|
|
716
780
|
commandSummary: command.slice(0, 200),
|
|
717
781
|
},
|
|
718
|
-
});
|
|
782
|
+
}, mcpToolsAvailable);
|
|
719
783
|
}
|
|
720
784
|
// All segments safe → allow through
|
|
721
785
|
return null;
|
|
@@ -737,7 +801,7 @@ export function routePreToolUse(toolName, toolInput, projectDir, platform, sessi
|
|
|
737
801
|
updatedInput: {
|
|
738
802
|
command: `echo "context-mode: Inline HTTP redirected. Call ${t("ctx_execute")}(language, code) to fetch, derive your answer in code, and console.log() only the result — the raw response body stays in the sandbox instead of entering your conversation. Full network access. Retry the same call on a transient DNS error (EAI_AGAIN, ETIMEDOUT, ENETUNREACH)."`,
|
|
739
803
|
},
|
|
740
|
-
});
|
|
804
|
+
}, mcpToolsAvailable);
|
|
741
805
|
}
|
|
742
806
|
|
|
743
807
|
// Build tools (gradle, maven, sbt) → redirect to execute sandbox (Issue #38, #406).
|
|
@@ -750,7 +814,7 @@ export function routePreToolUse(toolName, toolInput, projectDir, platform, sessi
|
|
|
750
814
|
updatedInput: {
|
|
751
815
|
command: `echo "context-mode: Build tool redirected. Call ${t("ctx_execute")}(language: \\"shell\\", code: \\"${safeCmd} 2>&1 | tail -30\\") to run the build and print only the tail — the verbose build log stays in the sandbox instead of entering your conversation. For more targeted output, replace \\"tail -30\\" with \\"grep -E '(error|warning|FAIL|✗|×)'\\" or similar, so only the lines that matter come back."`,
|
|
752
816
|
},
|
|
753
|
-
});
|
|
817
|
+
}, mcpToolsAvailable);
|
|
754
818
|
}
|
|
755
819
|
|
|
756
820
|
// Skip the routing nudge for commands whose output is structurally
|
|
@@ -761,6 +825,17 @@ export function routePreToolUse(toolName, toolInput, projectDir, platform, sessi
|
|
|
761
825
|
return null;
|
|
762
826
|
}
|
|
763
827
|
|
|
828
|
+
// #817: opt-in size threshold. When the operator configures
|
|
829
|
+
// CONTEXT_MODE_BASH_NUDGE_MIN_COMMAND_BYTES, a short unbounded command is
|
|
830
|
+
// treated as expected-lightweight and passes through untouched — reserving
|
|
831
|
+
// the nudge for commands large/complex enough to plausibly flood context.
|
|
832
|
+
// Default (0) preserves current behavior, so large-output savings are not
|
|
833
|
+
// weakened unless the operator explicitly opts in.
|
|
834
|
+
const minCommandBytes = getBashNudgeMinCommandBytes();
|
|
835
|
+
if (minCommandBytes > 0 && Buffer.byteLength(command, "utf8") < minCommandBytes) {
|
|
836
|
+
return null;
|
|
837
|
+
}
|
|
838
|
+
|
|
764
839
|
// allow all other Bash commands, but inject routing nudge (once per session)
|
|
765
840
|
return guidanceOnce("bash", bashGuidance, sessionId);
|
|
766
841
|
}
|
|
@@ -771,7 +846,7 @@ export function routePreToolUse(toolName, toolInput, projectDir, platform, sessi
|
|
|
771
846
|
// event with the actual file size as bytes_avoided. Threshold = 50 000 bytes;
|
|
772
847
|
// smaller reads stay on the existing one-shot guidance nudge.
|
|
773
848
|
if (canonical === "Read") {
|
|
774
|
-
const filePath = toolInput
|
|
849
|
+
const filePath = getReadFilePath(toolInput);
|
|
775
850
|
if (filePath) {
|
|
776
851
|
try {
|
|
777
852
|
const st = statSync(filePath);
|
|
@@ -798,7 +873,7 @@ export function routePreToolUse(toolName, toolInput, projectDir, platform, sessi
|
|
|
798
873
|
|
|
799
874
|
// ─── WebFetch: deny + redirect to sandbox ───
|
|
800
875
|
if (canonical === "WebFetch") {
|
|
801
|
-
const url = toolInput
|
|
876
|
+
const url = getWebFetchUrl(toolInput);
|
|
802
877
|
return mcpRedirect({
|
|
803
878
|
action: "deny",
|
|
804
879
|
reason: `context-mode: WebFetch redirected. Call ${t("ctx_fetch_and_index")}(url: "${url}", source: "...") to fetch + index the page, then ${t("ctx_search")}(queries: [...]) to query the indexed content — the raw page bytes stay in storage instead of entering your conversation. Or call ${t("ctx_execute")}(language, code) when you want to derive your answer in one round trip (parse, extract, count) without persisting the response. Both have full network access. Retry the same call on a transient DNS error (EAI_AGAIN, ETIMEDOUT, ENETUNREACH).`,
|
|
@@ -811,7 +886,7 @@ export function routePreToolUse(toolName, toolInput, projectDir, platform, sessi
|
|
|
811
886
|
bytesAvoided: 16384,
|
|
812
887
|
commandSummary: String(url).slice(0, 200),
|
|
813
888
|
},
|
|
814
|
-
});
|
|
889
|
+
}, mcpToolsAvailable);
|
|
815
890
|
}
|
|
816
891
|
|
|
817
892
|
// ─── Agent: inject context-mode routing into subagent prompts ───
|
|
@@ -842,6 +917,11 @@ export function routePreToolUse(toolName, toolInput, projectDir, platform, sessi
|
|
|
842
917
|
|
|
843
918
|
// ─── MCP execute: security check for shell commands ───
|
|
844
919
|
// Match bare, generic MCP, and legacy context-mode execute tool names.
|
|
920
|
+
const shouldPinClaudeExecutorCwd =
|
|
921
|
+
platform === "claude-code" &&
|
|
922
|
+
typeof projectDir === "string" &&
|
|
923
|
+
projectDir.length > 0;
|
|
924
|
+
|
|
845
925
|
if (matchesContextModeTool(toolName, "ctx_execute", "execute")) {
|
|
846
926
|
if (security && toolInput.language === "shell") {
|
|
847
927
|
const code = toolInput.code ?? "";
|
|
@@ -856,6 +936,9 @@ export function routePreToolUse(toolName, toolInput, projectDir, platform, sessi
|
|
|
856
936
|
}
|
|
857
937
|
}
|
|
858
938
|
}
|
|
939
|
+
if (toolInput.language === "shell" && shouldPinClaudeExecutorCwd && typeof toolInput.cwd !== "string") {
|
|
940
|
+
return { action: "modify", updatedInput: { ...toolInput, cwd: projectDir } };
|
|
941
|
+
}
|
|
859
942
|
return null;
|
|
860
943
|
}
|
|
861
944
|
|
|
@@ -907,6 +990,9 @@ export function routePreToolUse(toolName, toolInput, projectDir, platform, sessi
|
|
|
907
990
|
}
|
|
908
991
|
}
|
|
909
992
|
}
|
|
993
|
+
if (shouldPinClaudeExecutorCwd && typeof toolInput.cwd !== "string") {
|
|
994
|
+
return { action: "modify", updatedInput: { ...toolInput, cwd: projectDir } };
|
|
995
|
+
}
|
|
910
996
|
return null;
|
|
911
997
|
}
|
|
912
998
|
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* | Claude Code | mcp__plugin_context-mode_context-mode__<tool> |
|
|
9
9
|
* | Gemini CLI | mcp__context-mode__<tool> |
|
|
10
10
|
* | Antigravity | mcp__context-mode__<tool> |
|
|
11
|
+
* | Antigravity CLI | context-mode/<tool> |
|
|
11
12
|
* | OpenCode | context-mode_<tool> |
|
|
12
13
|
* | VS Code Copilot | context-mode_<tool> |
|
|
13
14
|
* | Kiro | @context-mode/<tool> |
|
|
@@ -19,10 +20,12 @@ const TOOL_PREFIXES = {
|
|
|
19
20
|
"claude-code": (tool) => `mcp__plugin_context-mode_context-mode__${tool}`,
|
|
20
21
|
"gemini-cli": (tool) => `mcp__context-mode__${tool}`,
|
|
21
22
|
"antigravity": (tool) => `mcp__context-mode__${tool}`,
|
|
23
|
+
"antigravity-cli": (tool) => `context-mode/${tool}`,
|
|
22
24
|
"opencode": (tool) => `context-mode_${tool}`,
|
|
23
25
|
"kilo": (tool) => `context-mode_${tool}`,
|
|
24
26
|
"vscode-copilot": (tool) => `context-mode_${tool}`,
|
|
25
27
|
"jetbrains-copilot": (tool) => `context-mode_${tool}`,
|
|
28
|
+
"copilot-cli": (tool) => `context-mode_${tool}`,
|
|
26
29
|
"kiro": (tool) => `@context-mode/${tool}`,
|
|
27
30
|
"zed": (tool) => `mcp:context-mode:${tool}`,
|
|
28
31
|
"cursor": (tool) => tool,
|
package/hooks/hooks.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "Context-mode hooks — PreToolUse routing, PostToolUse session capture, PreCompact snapshot, SessionStart context injection",
|
|
2
|
+
"description": "Context-mode hooks — PreToolUse routing, PostToolUse session capture, UserPromptSubmit decisions, PreCompact snapshot, SessionStart context injection, Stop turn-end capture",
|
|
3
3
|
"hooks": {
|
|
4
4
|
"PostToolUse": [
|
|
5
5
|
{
|
|
@@ -127,6 +127,17 @@
|
|
|
127
127
|
}
|
|
128
128
|
]
|
|
129
129
|
}
|
|
130
|
+
],
|
|
131
|
+
"Stop": [
|
|
132
|
+
{
|
|
133
|
+
"matcher": "",
|
|
134
|
+
"hooks": [
|
|
135
|
+
{
|
|
136
|
+
"type": "command",
|
|
137
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/stop.mjs\""
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
}
|
|
130
141
|
]
|
|
131
142
|
}
|
|
132
143
|
}
|
package/hooks/pretooluse.mjs
CHANGED
|
@@ -28,7 +28,7 @@ await runHook(async () => {
|
|
|
28
28
|
const { readStdin } = await import("./core/stdin.mjs");
|
|
29
29
|
const { routePreToolUse, initSecurity } = await import("./core/routing.mjs");
|
|
30
30
|
const { formatDecision } = await import("./core/formatters.mjs");
|
|
31
|
-
const { parseStdin, getSessionId, resolveConfigDir } = await import("./session-helpers.mjs");
|
|
31
|
+
const { parseStdin, getInputProjectDir, getSessionId, resolveConfigDir } = await import("./session-helpers.mjs");
|
|
32
32
|
|
|
33
33
|
// ─── Manual recursive copy (avoids cpSync libuv crash on non-ASCII paths, Windows + Node 24) ───
|
|
34
34
|
function copyDirSync(src, dest) {
|
|
@@ -163,9 +163,13 @@ await runHook(async () => {
|
|
|
163
163
|
const input = parseStdin(raw);
|
|
164
164
|
const tool = input.tool_name ?? "";
|
|
165
165
|
const toolInput = input.tool_input ?? {};
|
|
166
|
+
const projectDir = getInputProjectDir(input);
|
|
167
|
+
const isSubagentContext = input.agent_id != null || input.agent_type != null;
|
|
166
168
|
|
|
167
169
|
// ─── Route and format response ───
|
|
168
|
-
const decision = routePreToolUse(tool, toolInput,
|
|
170
|
+
const decision = routePreToolUse(tool, toolInput, projectDir, "claude-code", getSessionId(input), {
|
|
171
|
+
mcpToolsAvailable: !isSubagentContext,
|
|
172
|
+
});
|
|
169
173
|
const response = formatDecision("claude-code", decision);
|
|
170
174
|
|
|
171
175
|
// ─── Write latency marker for cross-hook timing (Category 27) ───
|
package/hooks/routing-block.mjs
CHANGED
|
@@ -85,11 +85,11 @@ export function createReadGuidance(t) {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export function createGrepGuidance(t) {
|
|
88
|
-
return '<context_guidance>\n <tip>\n Grep results may be larger than you expect. When you intend to count, filter, or aggregate matches (not just spot-check one), run the search through ' + t("ctx_execute") + '(language: "
|
|
88
|
+
return '<context_guidance>\n <tip>\n Grep results may be larger than you expect. When you intend to count, filter, or aggregate matches (not just spot-check one), run the search through ' + t("ctx_execute") + '(language: "javascript", code: "...") — the raw match list stays in the sandbox and only your derived answer enters your conversation. Use language: "shell" only when the code matches the host shell (PowerShell on Windows, POSIX shell on Unix).\n </tip>\n</context_guidance>';
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
export function createBashGuidance(t) {
|
|
92
|
-
return '<context_guidance>\n <tip>\n When you intend to PROCESS the output (filter, count, parse, aggregate), use ' + t("ctx_batch_execute") + '(commands, queries) for multiple commands or ' + t("ctx_execute") + '(language: "
|
|
92
|
+
return '<context_guidance>\n <tip>\n When you intend to PROCESS the output (filter, count, parse, aggregate), use ' + t("ctx_batch_execute") + '(commands, queries) for multiple commands or ' + t("ctx_execute") + '(language: "javascript", code: "...") for one — the raw output stays in the sandbox and only what you print enters your conversation. Shell stays the right surface when you intend to OBSERVE a short fixed output or when you are mutating state (git, mkdir, rm, mv, navigation); if you use ' + t("ctx_execute") + '(language: "shell"), write syntax for the host shell.\n </tip>\n</context_guidance>';
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
export function createExternalMcpGuidance(t) {
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
var h=(n,t)=>()=>(n&&(t=n(n=0)),t);var _,I=h(()=>{"use strict";_={"claude-code":"claude-code","gemini-cli-mcp-client":"gemini-cli","antigravity-client":"antigravity","cursor-vscode":"cursor","Visual-Studio-Code":"vscode-copilot","JetBrains Client":"jetbrains-copilot","IntelliJ IDEA":"jetbrains-copilot",PyCharm:"jetbrains-copilot",Codex:"codex","codex-mcp-client":"codex","Kilo Code":"kilo","Kiro CLI":"kiro","Pi CLI":"pi","Pi Coding Agent":"pi","omp-coding-agent":"omp",Zed:"zed",zed:"zed","qwen-code":"qwen-code","qwen-cli-mcp-client":"qwen-code","kimi-code":"kimi",kimi:"kimi","Kimi Code":"kimi"}});import{existsSync as l,readFileSync as N}from"node:fs";import{resolve as a}from"node:path";import{homedir as k}from"node:os";function L(){if(f!==null)return f!=="miss"&&f.hasCM;try{let n=a(k(),".claude","plugins","installed_plugins.json"),t=N(n,"utf-8"),e=JSON.parse(t),r=[...Object.keys(e.plugins??{}),...Object.keys(e.enabledPlugins??{})].some(i=>i.includes("context-mode"));return f={hasCM:r},r}catch{return f="miss",!1}}function S(n){switch(n){case"claude-code":return[".claude"];case"gemini-cli":return[".gemini"];case"antigravity":return[".gemini"];case"openclaw":return[".openclaw"];case"codex":return[".codex"];case"cursor":return[".cursor"];case"vscode-copilot":return[".vscode"];case"kiro":return[".kiro"];case"pi":return[".pi"];case"omp":return[".omp"];case"qwen-code":return[".qwen"];case"kimi":return[".kimi-code"];case"kilo":return[".config","kilo"];case"opencode":return[".config","opencode"];case"zed":return[".config","zed"];case"jetbrains-copilot":return[".config","JetBrains"];default:return null}}function A(n){if(n?.name){let o=_[n.name];if(o)return{platform:o,confidence:"high",reason:`MCP clientInfo.name="${n.name}"`};if(n.name.startsWith("qwen-cli-mcp-client"))return{platform:"qwen-code",confidence:"high",reason:`MCP clientInfo.name="${n.name}" (qwen-cli pattern)`}}let t=process.env.CONTEXT_MODE_PLATFORM;if(t&&["claude-code","gemini-cli","kilo","opencode","codex","vscode-copilot","jetbrains-copilot","cursor","antigravity","kiro","pi","omp","zed","qwen-code","kimi"].includes(t))return{platform:t,confidence:"high",reason:`CONTEXT_MODE_PLATFORM=${t} override`};for(let[o,r]of F)if(r.some(i=>i.detect!==!1&&process.env[i.name]))return o==="vscode-copilot"&&L()?{platform:"claude-code",confidence:"high",reason:"VSCODE_PID set but ~/.claude/plugins/installed_plugins.json lists context-mode (issue #539 fallback)"}:{platform:o,confidence:"high",reason:`${r.filter(i=>i.detect!==!1).map(i=>i.name).join(" or ")} env var set`};let e=k();return l(a(e,".claude"))?{platform:"claude-code",confidence:"medium",reason:"~/.claude/ directory exists"}:l(a(e,".gemini"))?{platform:"gemini-cli",confidence:"medium",reason:"~/.gemini/ directory exists"}:l(a(e,".codex"))?{platform:"codex",confidence:"medium",reason:"~/.codex/ directory exists"}:l(a(e,".kiro"))?{platform:"kiro",confidence:"medium",reason:"~/.kiro/ directory exists"}:l(a(e,".omp"))?{platform:"omp",confidence:"medium",reason:"~/.omp/ directory exists"}:l(a(e,".pi"))?{platform:"pi",confidence:"medium",reason:"~/.pi/ directory exists"}:l(a(e,".qwen"))?{platform:"qwen-code",confidence:"medium",reason:"~/.qwen/ directory exists"}:l(a(e,".kimi-code"))?{platform:"kimi",confidence:"medium",reason:"~/.kimi-code/ directory exists"}:l(a(e,".openclaw"))?{platform:"openclaw",confidence:"medium",reason:"~/.openclaw/ directory exists"}:l(a(e,".cursor"))?{platform:"cursor",confidence:"medium",reason:"~/.cursor/ directory exists"}:l(a(e,".config","kilo"))?{platform:"kilo",confidence:"medium",reason:"~/.config/kilo/ directory exists"}:l(a(e,".config","JetBrains"))?{platform:"jetbrains-copilot",confidence:"medium",reason:"~/.config/JetBrains/ directory exists"}:l(a(e,".config","opencode"))?{platform:"opencode",confidence:"medium",reason:"~/.config/opencode/ directory exists"}:l(a(e,".config","zed"))?{platform:"zed",confidence:"medium",reason:"~/.config/zed/ directory exists"}:{platform:"claude-code",confidence:"low",reason:"No platform detected, defaulting to Claude Code"}}var f,M,F,O=h(()=>{"use strict";I();f=null;M=[["claude-code",[{name:"CLAUDE_CODE_ENTRYPOINT",role:"identification"},{name:"CLAUDE_PLUGIN_ROOT",role:"identification"},{name:"CLAUDE_PROJECT_DIR",role:"workspace"},{name:"CLAUDE_SESSION_ID",role:"identification"}]],["antigravity",[{name:"ANTIGRAVITY_CLI_ALIAS",role:"identification"}]],["cursor",[{name:"CURSOR_CWD",role:"workspace"},{name:"CURSOR_TRACE_ID",role:"identification"},{name:"CURSOR_CLI",role:"identification"}]],["kilo",[{name:"KILO",role:"identification"},{name:"KILO_PID",role:"identification"}]],["opencode",[{name:"OPENCODE_PROJECT_DIR",role:"workspace"},{name:"OPENCODE_CLIENT",role:"identification"},{name:"OPENCODE_TERMINAL",role:"identification"},{name:"OPENCODE",role:"identification"},{name:"OPENCODE_PID",role:"identification"}]],["zed",[{name:"ZED_SESSION_ID",role:"identification"},{name:"ZED_TERM",role:"identification"}]],["codex",[{name:"CODEX_THREAD_ID",role:"identification"},{name:"CODEX_CI",role:"identification"}]],["gemini-cli",[{name:"GEMINI_PROJECT_DIR",role:"workspace"},{name:"GEMINI_CLI",role:"identification"}]],["vscode-copilot",[{name:"VSCODE_CWD",role:"workspace"},{name:"VSCODE_PID",role:"identification"}]],["jetbrains-copilot",[{name:"IDEA_INITIAL_DIRECTORY",role:"workspace"}]],["qwen-code",[{name:"QWEN_PROJECT_DIR",role:"workspace"}]],["omp",[{name:"PI_CODING_AGENT_DIR",role:"workspace"}]],["pi",[{name:"PI_WORKSPACE_DIR",role:"workspace",detect:!1},{name:"PI_PROJECT_DIR",role:"workspace",detect:!1},{name:"PI_CONFIG_DIR",role:"identification"},{name:"PI_SESSION_FILE",role:"identification"},{name:"PI_COMPILED",role:"identification"}]]],F=new Map(M)});import{resolve as u}from"node:path";import{homedir as P}from"node:os";function j(n=process.env){let t=n.CLAUDE_CONFIG_DIR;return t&&t.trim()!==""?t.startsWith("~")?u(P(),t.replace(/^~[/\\]?/,"")):u(t):u(P(),".claude")}function G(n=process.env){return u(j(n),"settings.json")}function w(n=process.env){let t=[],e=A();if(e.platform!=="claude-code"){let r=S(e.platform);r&&r.length>0&&t.push(u(P(),...r,"settings.json"))}let o=G(n);return t.includes(o)||t.push(o),t}var R=h(()=>{"use strict";O()});R();import{readFileSync as D,realpathSync as $}from"node:fs";import{resolve as p}from"node:path";function b(n){let t=n.match(/^Bash\((.+)\)$/);return t?t[1]:null}function J(n){let t=n.match(/^(\w+)\((.+)\)$/);return t?{tool:t[1],glob:t[2]}:null}function q(n){return n.replace(/[.*+?^${}()|[\]\\\/\-]/g,"\\$&")}function v(n){return n.replace(/[.+?^${}()|[\]\\\/\-]/g,"\\$&").replace(/\*/g,".*")}function V(n,t=!1){let e,o=n.indexOf(":");if(o!==-1){let r=n.slice(0,o),i=n.slice(o+1),s=q(r),c=v(i);e=`^${s}(\\s${c})?$`}else e=`^${v(n)}$`;return new RegExp(e,t?"i":"")}function z(n,t=!1){let e="",o=0;for(;o<n.length;)n[o]==="*"&&n[o+1]==="*"?o+2<n.length&&n[o+2]==="/"?(e+="(.*/)?",o+=3):(e+=".*",o+=2):n[o]==="*"?(e+="[^/]*",o++):n[o]==="?"?(e+="[^/]",o++):(e+=n[o].replace(/[.+^${}()|[\]\\\/\-]/g,"\\$&"),o++);return new RegExp(`^${e}$`,t?"i":"")}function g(n,t,e=!1){for(let o of t){let r=b(o);if(r&&V(r,e).test(n))return o}return null}function T(n){let t=[],e="",o=!1,r=!1,i=!1;for(let s=0;s<n.length;s++){let c=n[s],d=s>0?n[s-1]:"";c==="'"&&!r&&!i&&d!=="\\"?(o=!o,e+=c):c==='"'&&!o&&!i&&d!=="\\"?(r=!r,e+=c):c==="`"&&!o&&!r&&d!=="\\"?(i=!i,e+=c):!o&&!r&&!i?c===";"?(t.push(e.trim()),e=""):c==="|"&&n[s+1]==="|"||c==="&"&&n[s+1]==="&"?(t.push(e.trim()),e="",s++):c==="|"?(t.push(e.trim()),e=""):e+=c:e+=c}return e.trim()&&t.push(e.trim()),t.filter(s=>s.length>0)}function C(n){let t;try{t=D(n,"utf-8")}catch{return null}let e;try{e=JSON.parse(t)}catch{return null}let o=e?.permissions;if(!o||typeof o!="object")return null;let r=i=>Array.isArray(i)?i.filter(s=>typeof s=="string"&&b(s)!==null):[];return{allow:r(o.allow),deny:r(o.deny),ask:r(o.ask)}}function ce(n,t){let e=[];if(n){let r=p(n,".claude","settings.local.json"),i=C(r);i&&e.push(i);let s=p(n,".claude","settings.json"),c=C(s);c&&e.push(c)}let o=t!==void 0?[t]:w();for(let r of o){let i=C(r);i&&e.push(i)}return e}function ae(n,t,e){let o=[],r=s=>{let c;try{c=D(s,"utf-8")}catch{return null}let d;try{d=JSON.parse(c)}catch{return null}let m=d?.permissions?.deny;if(!Array.isArray(m))return[];let x=[];for(let E of m){if(typeof E!="string")continue;let y=J(E);y&&y.tool===n&&x.push(y.glob)}return x};if(t){let s=r(p(t,".claude","settings.local.json"));s!==null&&o.push(s);let c=r(p(t,".claude","settings.json"));c!==null&&o.push(c)}let i=e!==void 0?[e]:w();for(let s of i){let c=r(s);c!==null&&o.push(c)}return o}function le(n,t,e=process.platform==="win32"){let o=T(n);for(let r of o)for(let i of t){let s=g(r,i.deny,e);if(s)return{decision:"deny",matchedPattern:s}}for(let r of t){let i=g(n,r.ask,e);if(i)return{decision:"ask",matchedPattern:i};let s=g(n,r.allow,e);if(s)return{decision:"allow",matchedPattern:s}}return{decision:"ask"}}function de(n,t,e=process.platform==="win32"){let o=T(n);for(let r of o)for(let i of t){let s=g(r,i.deny,e);if(s)return{decision:"deny",matchedPattern:s}}return{decision:"allow"}}function fe(n,t,e=process.platform==="win32",o){let r=s=>s.replace(/\\/g,"/"),i=new Set;if(i.add(r(n)),o){let s=p(o,n);i.add(r(s));try{i.add(r($(s)))}catch{}}for(let s of t)for(let c of s){let d=z(r(c),e);for(let m of i)if(d.test(m))return{denied:!0,matchedPattern:c}}return{denied:!1}}var B={python:[/os\.system\(\s*(['"])(.*?)\1\s*\)/g,/subprocess\.(?:run|call|Popen|check_output|check_call)\(\s*(['"])(.*?)\1/g],javascript:[/exec(?:Sync|File|FileSync)?\(\s*(['"`])(.*?)\1/g,/spawn(?:Sync)?\(\s*(['"`])(.*?)\1/g],typescript:[/exec(?:Sync|File|FileSync)?\(\s*(['"`])(.*?)\1/g,/spawn(?:Sync)?\(\s*(['"`])(.*?)\1/g],ruby:[/system\(\s*(['"])(.*?)\1/g,/`(.*?)`/g],go:[/exec\.Command\(\s*(['"`])(.*?)\1/g],php:[/shell_exec\(\s*(['"`])(.*?)\1/g,/(?:^|[^.])exec\(\s*(['"`])(.*?)\1/g,/(?:^|[^.])system\(\s*(['"`])(.*?)\1/g,/passthru\(\s*(['"`])(.*?)\1/g,/proc_open\(\s*(['"`])(.*?)\1/g],rust:[/Command::new\(\s*(['"`])(.*?)\1/g]};function U(n){let t=[],e=/subprocess\.(?:run|call|Popen|check_output|check_call)\(\s*\[([^\]]+)\]/g,o;for(;(o=e.exec(n))!==null;){let i=[...o[1].matchAll(/(['"])(.*?)\1/g)].map(s=>s[2]);i.length>0&&t.push(i.join(" "))}return t}function ue(n,t){let e=B[t];if(!e&&t!=="python")return[];let o=[];if(e)for(let r of e){r.lastIndex=0;let i;for(;(i=r.exec(n))!==null;){let s=i[i.length-1];s&&o.push(s)}}return t==="python"&&o.push(...U(n)),o}export{le as evaluateCommand,de as evaluateCommandDenyOnly,fe as evaluateFilePath,ue as extractShellCommands,z as fileGlobToRegex,V as globToRegex,g as matchesAnyPattern,b as parseBashPattern,J as parseToolPattern,ce as readBashPolicies,ae as readToolDenyPatterns,T as splitChainedCommands};
|
|
1
|
+
var P=(t,n)=>()=>(t&&(n=t(t=0)),n);var I,k=P(()=>{"use strict";I={"claude-code":"claude-code","gemini-cli-mcp-client":"gemini-cli","antigravity-client":"antigravity","antigravity-cli":"antigravity-cli",agy:"antigravity-cli","cursor-vscode":"cursor","Visual-Studio-Code":"vscode-copilot","copilot-cli":"copilot-cli","GitHub Copilot CLI":"copilot-cli","github-copilot-cli":"copilot-cli","JetBrains Client":"jetbrains-copilot","IntelliJ IDEA":"jetbrains-copilot",PyCharm:"jetbrains-copilot",Codex:"codex","codex-mcp-client":"codex","Kilo Code":"kilo","Kiro CLI":"kiro","Pi CLI":"pi","Pi Coding Agent":"pi","omp-coding-agent":"omp",Zed:"zed",zed:"zed","qwen-code":"qwen-code","qwen-cli-mcp-client":"qwen-code","kimi-code":"kimi",kimi:"kimi","Kimi Code":"kimi"}});import{existsSync as f,readFileSync as M}from"node:fs";import{resolve as d}from"node:path";import{homedir as O}from"node:os";function j(){if(g!==null)return g!=="miss"&&g.hasCM;try{let t=d(O(),".claude","plugins","installed_plugins.json"),n=M(t,"utf-8"),e=JSON.parse(n),i=[...Object.keys(e.plugins??{}),...Object.keys(e.enabledPlugins??{})].some(r=>r.includes("context-mode"));return g={hasCM:i},i}catch{return g="miss",!1}}function A(t){switch(t){case"claude-code":return[".claude"];case"gemini-cli":return[".gemini"];case"antigravity":return[".gemini"];case"antigravity-cli":return[".gemini"];case"openclaw":return[".openclaw"];case"codex":return[".codex"];case"cursor":return[".cursor"];case"vscode-copilot":return[".vscode"];case"copilot-cli":return[".copilot"];case"kiro":return[".kiro"];case"pi":return[".pi"];case"omp":return[".omp"];case"qwen-code":return[".qwen"];case"kimi":return[".kimi-code"];case"kilo":return[".config","kilo"];case"opencode":return[".config","opencode"];case"zed":return[".config","zed"];case"jetbrains-copilot":return[".config","JetBrains"];default:return null}}function v(t){if(t?.name){let r=I[t.name];if(r)return{platform:r,confidence:"high",reason:`MCP clientInfo.name="${t.name}"`};if(t.name.startsWith("qwen-cli-mcp-client"))return{platform:"qwen-code",confidence:"high",reason:`MCP clientInfo.name="${t.name}" (qwen-cli pattern)`}}let n=process.env.CONTEXT_MODE_PLATFORM;if(n&&["claude-code","gemini-cli","kilo","opencode","codex","vscode-copilot","jetbrains-copilot","copilot-cli","cursor","antigravity","antigravity-cli","kiro","pi","omp","zed","qwen-code","kimi"].includes(n))return{platform:n,confidence:"high",reason:`CONTEXT_MODE_PLATFORM=${n} override`};for(let[r,s]of $)if(s.some(c=>c.detect!==!1&&process.env[c.name]))return r==="vscode-copilot"&&j()?{platform:"claude-code",confidence:"high",reason:"VSCODE_PID set but ~/.claude/plugins/installed_plugins.json lists context-mode (issue #539 fallback)"}:{platform:r,confidence:"high",reason:`${s.filter(c=>c.detect!==!1).map(c=>c.name).join(" or ")} env var set`};let e=O(),o=(()=>{let r=process.env.COPILOT_HOME;return r&&r.trim()!==""?r.startsWith("~")?d(e,r.replace(/^~[/\\]?/,"")):d(r):d(e,".copilot")})(),i=f(d(o,"mcp-config.json"))||f(d(o,"hooks","context-mode.json"));return process.env.COPILOT_HOME?.trim()&&i?{platform:"copilot-cli",confidence:"medium",reason:"context-mode config in explicit COPILOT_HOME exists (mcp-config.json or hooks/context-mode.json)"}:f(d(e,".local","bin","agy"))||f(d(e,".gemini","antigravity-cli"))||f(d(e,".gemini","config","mcp_config.json"))?{platform:"antigravity-cli",confidence:"medium",reason:"Antigravity CLI marker exists (~/.local/bin/agy, ~/.gemini/antigravity-cli, or ~/.gemini/config/mcp_config.json)"}:i?{platform:"copilot-cli",confidence:"medium",reason:"context-mode config in Copilot CLI home exists (mcp-config.json or hooks/context-mode.json; honors COPILOT_HOME)"}:f(d(e,".claude"))?{platform:"claude-code",confidence:"medium",reason:"~/.claude/ directory exists"}:f(d(e,".gemini"))?{platform:"gemini-cli",confidence:"medium",reason:"~/.gemini/ directory exists"}:f(d(e,".codex"))?{platform:"codex",confidence:"medium",reason:"~/.codex/ directory exists"}:f(d(e,".kiro"))?{platform:"kiro",confidence:"medium",reason:"~/.kiro/ directory exists"}:f(d(e,".omp"))?{platform:"omp",confidence:"medium",reason:"~/.omp/ directory exists"}:f(d(e,".pi"))?{platform:"pi",confidence:"medium",reason:"~/.pi/ directory exists"}:f(d(e,".qwen"))?{platform:"qwen-code",confidence:"medium",reason:"~/.qwen/ directory exists"}:f(d(e,".kimi-code"))?{platform:"kimi",confidence:"medium",reason:"~/.kimi-code/ directory exists"}:f(d(e,".openclaw"))?{platform:"openclaw",confidence:"medium",reason:"~/.openclaw/ directory exists"}:f(d(e,".cursor"))?{platform:"cursor",confidence:"medium",reason:"~/.cursor/ directory exists"}:f(d(e,".config","kilo"))?{platform:"kilo",confidence:"medium",reason:"~/.config/kilo/ directory exists"}:f(d(e,".config","JetBrains"))?{platform:"jetbrains-copilot",confidence:"medium",reason:"~/.config/JetBrains/ directory exists"}:f(d(e,".config","opencode"))?{platform:"opencode",confidence:"medium",reason:"~/.config/opencode/ directory exists"}:f(d(e,".config","zed"))?{platform:"zed",confidence:"medium",reason:"~/.config/zed/ directory exists"}:{platform:"claude-code",confidence:"low",reason:"No platform detected, defaulting to Claude Code"}}var g,G,$,S=P(()=>{"use strict";k();g=null;G=[["claude-code",[{name:"CLAUDE_CODE_ENTRYPOINT",role:"identification"},{name:"CLAUDE_PLUGIN_ROOT",role:"identification"},{name:"CLAUDE_PROJECT_DIR",role:"workspace"},{name:"CLAUDE_SESSION_ID",role:"identification"}]],["antigravity",[{name:"ANTIGRAVITY_CLI_ALIAS",role:"identification"}]],["cursor",[{name:"CURSOR_CWD",role:"workspace"},{name:"CURSOR_TRACE_ID",role:"identification"},{name:"CURSOR_CLI",role:"identification"}]],["kilo",[{name:"KILO",role:"identification"},{name:"KILO_PID",role:"identification"}]],["opencode",[{name:"OPENCODE_PROJECT_DIR",role:"workspace"},{name:"OPENCODE_CLIENT",role:"identification"},{name:"OPENCODE_TERMINAL",role:"identification"},{name:"OPENCODE",role:"identification"},{name:"OPENCODE_PID",role:"identification"}]],["zed",[{name:"ZED_SESSION_ID",role:"identification"},{name:"ZED_TERM",role:"identification"}]],["codex",[{name:"CODEX_THREAD_ID",role:"identification"},{name:"CODEX_CI",role:"identification"}]],["gemini-cli",[{name:"GEMINI_PROJECT_DIR",role:"workspace"},{name:"GEMINI_CLI",role:"identification"}]],["vscode-copilot",[{name:"VSCODE_CWD",role:"workspace"},{name:"VSCODE_PID",role:"identification"}]],["jetbrains-copilot",[{name:"IDEA_INITIAL_DIRECTORY",role:"workspace"}]],["qwen-code",[{name:"QWEN_PROJECT_DIR",role:"workspace"}]],["omp",[{name:"PI_CODING_AGENT_DIR",role:"workspace"}]],["pi",[{name:"PI_WORKSPACE_DIR",role:"workspace",detect:!1},{name:"PI_PROJECT_DIR",role:"workspace",detect:!1},{name:"PI_CONFIG_DIR",role:"identification"},{name:"PI_SESSION_FILE",role:"identification"},{name:"PI_COMPILED",role:"identification"},{name:"PI_CODING_AGENT",role:"identification"}]]],$=new Map(G)});import{resolve as h}from"node:path";import{homedir as w}from"node:os";function F(t=process.env){let n=t.CLAUDE_CONFIG_DIR;return n&&n.trim()!==""?n.startsWith("~")?h(w(),n.replace(/^~[/\\]?/,"")):h(n):h(w(),".claude")}function J(t=process.env){return h(F(t),"settings.json")}function x(t=process.env){let n=[],e=v();if(e.platform!=="claude-code"){let i=A(e.platform);i&&i.length>0&&n.push(h(w(),...i,"settings.json"))}let o=J(t);return n.includes(o)||n.push(o),n}var b=P(()=>{"use strict";S()});b();import{readFileSync as R,realpathSync as q}from"node:fs";import{resolve as y}from"node:path";function T(t){let n=t.match(/^Bash\((.+)\)$/);return n?n[1]:null}function V(t){let n=t.match(/^(\w+)\((.+)\)$/);return n?{tool:n[1],glob:n[2]}:null}function z(t){return t.replace(/[.*+?^${}()|[\]\\\/\-]/g,"\\$&")}function D(t){return t.replace(/[.+?^${}()|[\]\\\/\-]/g,"\\$&").replace(/\*/g,".*")}function H(t,n=!1){let e,o=t.indexOf(":");if(o!==-1){let i=t.slice(0,o),r=t.slice(o+1),s=z(i),c=D(r);e=`^${s}(\\s${c})?$`}else e=`^${D(t)}$`;return new RegExp(e,n?"i":"")}function B(t,n=!1){let e="",o=0;for(;o<t.length;)t[o]==="*"&&t[o+1]==="*"?o+2<t.length&&t[o+2]==="/"?(e+="(.*/)?",o+=3):(e+=".*",o+=2):t[o]==="*"?(e+="[^/]*",o++):t[o]==="?"?(e+="[^/]",o++):(e+=t[o].replace(/[.+^${}()|[\]\\\/\-]/g,"\\$&"),o++);return new RegExp(`^${e}$`,n?"i":"")}function C(t,n,e=!1){for(let o of n){let i=T(o);if(i&&H(i,e).test(t))return o}return null}function N(t,n){let e=0;for(let o=n-1;o>=0&&t[o]==="\\";o--)e++;return e%2===1}function U(t){let n=[],e="",o=!1,i=!1,r=!1,s=0;for(let c=0;c<t.length;c++){let a=t[c],l=N(t,c);a==="'"&&!i&&!r&&!l?(o=!o,e+=a):a==='"'&&!o&&!r&&!l?(i=!i,e+=a):a==="`"&&!o&&!i&&!l?(r=!r,e+=a):!o&&!i&&!r?a==="$"&&t[c+1]==="("&&!l?(s++,e+=a+t[c+1],c++):s>0&&a==="("&&!l?(s++,e+=a):a===")"&&s>0&&!l?(s--,e+=a):s===0&&(a===";"||a===`
|
|
2
|
+
`||a==="\r")&&!l?(n.push(e.trim()),e=""):s===0&&a==="|"&&t[c+1]==="|"||s===0&&a==="&"&&t[c+1]==="&"?(n.push(e.trim()),e="",c++):s===0&&a==="&"&&!l||s===0&&a==="|"?(n.push(e.trim()),e=""):e+=a:e+=a}return e.trim()&&n.push(e.trim()),n.filter(c=>c.length>0)}function L(t){let n=[],e=!1,o=!1,i=-1,r=[],s=[],c=0;for(let a=0;a<t.length;a++){let l=t[a],p=N(t,a);if(l==="'"&&!o&&i===-1&&!p)e=!e;else if(l==='"'&&!e&&i===-1&&!p)o=!o;else if(l==="`"&&!e&&!o&&!p)if(i===-1)i=a+1;else{let u=t.slice(i,a);n.push(u),n.push(...L(u)),i=-1}else if(!e&&i===-1){if(l==="$"&&t[a+1]==="("&&!p)t[a+2]==="("?(c+=2,a+=2):(r.push(a+2),s.push(c),c++,a++);else if(l==="("&&!p)c++;else if(l===")"&&!p&&(c>0&&c--,s.length>0&&c===s[s.length-1])){s.pop();let u=r.pop(),m=t.slice(u,a);n.push(m)}}}return n}function _(t){let n=[],e=U(t);for(let o of e){n.push(o);for(let i of L(o))n.push(..._(i))}return n}function E(t){let n;try{n=R(t,"utf-8")}catch{return null}let e;try{e=JSON.parse(n)}catch{return null}let o=e?.permissions;if(!o||typeof o!="object")return null;let i=r=>Array.isArray(r)?r.filter(s=>typeof s=="string"&&T(s)!==null):[];return{allow:i(o.allow),deny:i(o.deny),ask:i(o.ask)}}function de(t,n){let e=[];if(t){let i=y(t,".claude","settings.local.json"),r=E(i);r&&e.push(r);let s=y(t,".claude","settings.json"),c=E(s);c&&e.push(c)}let o=n!==void 0?[n]:x();for(let i of o){let r=E(i);r&&e.push(r)}return e}function fe(t,n,e){let o=[],i=s=>{let c;try{c=R(s,"utf-8")}catch{return null}let a;try{a=JSON.parse(c)}catch{return null}let l=a?.permissions?.deny;if(!Array.isArray(l))return[];let p=[];for(let u of l){if(typeof u!="string")continue;let m=V(u);m&&m.tool===t&&p.push(m.glob)}return p};if(n){let s=i(y(n,".claude","settings.local.json"));s!==null&&o.push(s);let c=i(y(n,".claude","settings.json"));c!==null&&o.push(c)}let r=e!==void 0?[e]:x();for(let s of r){let c=i(s);c!==null&&o.push(c)}return o}function pe(t,n,e=process.platform==="win32"||process.platform==="darwin"){let o=_(t);for(let i of o)for(let r of n){let s=C(i,r.deny,e);if(s)return{decision:"deny",matchedPattern:s}}for(let i of n){let r=!0,s=!1,c,a;for(let l of o){let p=C(l,i.ask,e);if(p){s=!0,c=p;break}let u=C(l,i.allow,e);u?a=u:r=!1}if(s)return{decision:"ask",matchedPattern:c};if(r&&o.length>0)return{decision:"allow",matchedPattern:a}}return{decision:"ask"}}function ue(t,n,e=process.platform==="win32"||process.platform==="darwin"){let o=_(t);for(let i of o)for(let r of n){let s=C(i,r.deny,e);if(s)return{decision:"deny",matchedPattern:s}}return{decision:"allow"}}function me(t,n,e=process.platform==="win32"||process.platform==="darwin",o){let i=s=>s.replace(/\\/g,"/"),r=new Set;if(r.add(i(t)),o){let s=y(o,t);r.add(i(s));try{r.add(i(q(s)))}catch{}}for(let s of n)for(let c of s){let a=B(i(c),e);for(let l of r)if(a.test(l))return{denied:!0,matchedPattern:c}}return{denied:!1}}var W={python:[/os\.system\(\s*(['"])(.*?)\1\s*\)/g,/subprocess\.(?:run|call|Popen|check_output|check_call)\(\s*(['"])(.*?)\1/g],javascript:[/exec(?:Sync|File|FileSync)?\(\s*(['"`])(.*?)\1/g,/spawn(?:Sync)?\(\s*(['"`])(.*?)\1/g],typescript:[/exec(?:Sync|File|FileSync)?\(\s*(['"`])(.*?)\1/g,/spawn(?:Sync)?\(\s*(['"`])(.*?)\1/g],ruby:[/system\(\s*(['"])(.*?)\1/g,/`(.*?)`/g],go:[/exec\.Command\(\s*(['"`])(.*?)\1/g],php:[/shell_exec\(\s*(['"`])(.*?)\1/g,/(?:^|[^.])exec\(\s*(['"`])(.*?)\1/g,/(?:^|[^.])system\(\s*(['"`])(.*?)\1/g,/passthru\(\s*(['"`])(.*?)\1/g,/proc_open\(\s*(['"`])(.*?)\1/g],rust:[/Command::new\(\s*(['"`])(.*?)\1/g]};function K(t){let n=[],e=/subprocess\.(?:run|call|Popen|check_output|check_call)\(\s*\[([^\]]+)\]/g,o;for(;(o=e.exec(t))!==null;){let r=[...o[1].matchAll(/(['"])(.*?)\1/g)].map(s=>s[2]);r.length>0&&n.push(r.join(" "))}return n}function ge(t,n){let e=W[n];if(!e&&n!=="python")return[];let o=[];if(e)for(let i of e){i.lastIndex=0;let r;for(;(r=i.exec(t))!==null;){let s=r[r.length-1];s&&o.push(s)}}return n==="python"&&o.push(...K(t)),o}export{pe as evaluateCommand,ue as evaluateCommandDenyOnly,me as evaluateFilePath,ge as extractShellCommands,L as extractSubshellCommands,B as fileGlobToRegex,H as globToRegex,C as matchesAnyPattern,T as parseBashPattern,V as parseToolPattern,de as readBashPolicies,fe as readToolDenyPatterns,U as splitChainedCommands};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{createRequire as
|
|
2
|
-
`))}function K(n){let e=process.env[l];if(e===void 0)return{kind:"unset"};let t=e.trim();if(!t)return{kind:"ignored-empty",ignoredEnvVar:l,ignoredReason:"empty"};if(!G(t))throw
|
|
3
|
-
`)}function Oe(n){return n.ignoredEnvVar&&n.ignoredReason==="empty"?`Ignored empty ${n.ignoredEnvVar}; using adapter default.`:null}function J(){return`Set ${l} to a writable absolute path.`}function
|
|
1
|
+
import{createRequire as ie}from"node:module";import{existsSync as ae,unlinkSync as P,renameSync as ce}from"node:fs";import{tmpdir as ue}from"node:os";import{join as de}from"node:path";var A=class{#e;constructor(e){this.#e=e}pragma(e){let r=this.#e.prepare(`PRAGMA ${e}`).all();if(!r||r.length===0)return;if(r.length>1)return r;let s=Object.values(r[0]);return s.length===1?s[0]:r[0]}exec(e){let t="",r=null;for(let a=0;a<e.length;a++){let i=e[a];if(r)t+=i,i===r&&(r=null);else if(i==="'"||i==='"')t+=i,r=i;else if(i===";"){let c=t.trim();c&&this.#e.prepare(c).run(),t=""}else t+=i}let s=t.trim();return s&&this.#e.prepare(s).run(),this}prepare(e){let t=this.#e.prepare(e);return{run:(...r)=>t.run(...r),get:(...r)=>{let s=t.get(...r);return s===null?void 0:s},all:(...r)=>t.all(...r),iterate:(...r)=>t.iterate(...r)}}transaction(e){return this.#e.transaction(e)}close(){this.#e.close()}},w=class{#e;constructor(e){this.#e=e}pragma(e){let r=this.#e.prepare(`PRAGMA ${e}`).all();if(!r||r.length===0)return;if(r.length>1)return r;let s=Object.values(r[0]);return s.length===1?s[0]:r[0]}exec(e){return this.#e.exec(e),this}prepare(e){let t=this.#e.prepare(e);return{run:(...r)=>t.run(...r),get:(...r)=>t.get(...r),all:(...r)=>t.all(...r),iterate:(...r)=>typeof t.iterate=="function"?t.iterate(...r):t.all(...r)[Symbol.iterator]()}}transaction(e){return(...t)=>{this.#e.exec("BEGIN");try{let r=e(...t);return this.#e.exec("COMMIT"),r}catch(r){throw this.#e.exec("ROLLBACK"),r}}}close(){this.#e.close()}},m=null;function le(n){let e=null;try{return e=new n(":memory:"),e.exec("CREATE VIRTUAL TABLE __fts5_probe USING fts5(x)"),!0}catch{return!1}finally{try{e?.close()}catch{}}}function Ee(n,e){let t=e!==void 0?e:globalThis.Bun;if(typeof t<"u"&&t!==null)return!0;let r=n??process.versions,[s,a]=(r.node??"0.0.0").split("."),i=Number(s),c=Number(a);return!Number.isFinite(i)||!Number.isFinite(c)?!1:i>22||i===22&&c>=5}function ge(){if(!m){let n=ie(import.meta.url);if(globalThis.Bun){let e=n(["bun","sqlite"].join(":")).Database;m=function(r,s){let a=new e(r,{readonly:s?.readonly,create:!0}),i=new A(a);return s?.timeout&&i.pragma(`busy_timeout = ${s.timeout}`),i}}else if(Ee()){let e=null;try{({DatabaseSync:e}=n(["node","sqlite"].join(":")))}catch{e=null}e&&le(e)?m=function(r,s){let a=new e(r,{readOnly:s?.readonly??!1}),i=new w(a);return s?.timeout&&i.pragma(`busy_timeout = ${s.timeout}`),i}:m=n("better-sqlite3")}else m=n("better-sqlite3")}return m}function F(n){n.pragma("journal_mode = WAL"),n.pragma("synchronous = NORMAL");try{n.pragma("mmap_size = 268435456")}catch{}}function k(n){if(!ae(n))for(let e of["-wal","-shm"])try{P(n+e)}catch{}}function me(n){for(let e of["","-wal","-shm"])try{P(n+e)}catch{}}function x(n){try{n.pragma("wal_checkpoint(TRUNCATE)")}catch{}try{n.close()}catch{}}function B(n="context-mode"){return de(ue(),`${n}-${process.pid}.db`)}function _e(n,e=[100,500,2e3]){let t;for(let r=0;r<=e.length;r++)try{return n()}catch(s){let a=s instanceof Error?s.message:String(s);if(!a.includes("SQLITE_BUSY")&&!a.includes("database is locked"))throw s;if(t=s instanceof Error?s:new Error(a),r<e.length){let i=e[r],c=Date.now();for(;Date.now()-c<i;);}}throw new Error(`SQLITE_BUSY: database is locked after ${e.length} retries. Original error: ${t?.message}`)}function pe(n){return n.includes("SQLITE_CORRUPT")||n.includes("SQLITE_NOTADB")||n.includes("database disk image is malformed")||n.includes("file is not a database")}function ye(n){let e=Date.now();for(let t of["","-wal","-shm"])try{ce(n+t,`${n}${t}.corrupt-${e}`)}catch{}}var S=Symbol.for("__context_mode_live_dbs_v3__"),O=(()=>{let n=globalThis;return n[S]||(n[S]=new Set,process.on("exit",()=>{for(let e of n[S])x(e);n[S].clear()})),n[S]})(),R=class{#e;#t;constructor(e){let t=ge();this.#e=e,k(e);let r;try{r=new t(e,{timeout:3e4}),F(r)}catch(s){let a=s instanceof Error?s.message:String(s);if(pe(a)){ye(e),k(e);try{r=new t(e,{timeout:3e4}),F(r)}catch(i){throw new Error(`Failed to create fresh DB after renaming corrupt file: ${i instanceof Error?i.message:String(i)}`)}}else throw s}this.#t=r,O.add(this.#t),this.initSchema(),this.prepareStatements()}get db(){return this.#t}get dbPath(){return this.#e}close(){O.delete(this.#t),x(this.#t)}withRetry(e){return _e(e)}cleanup(){O.delete(this.#t),x(this.#t),me(this.#e)}};import{createHash as f}from"node:crypto";import{execFileSync as Se}from"node:child_process";import{accessSync as fe,constants as he,existsSync as L,mkdirSync as Te,realpathSync as ve,renameSync as I}from"node:fs";import{homedir as q}from"node:os";import{dirname as Re,isAbsolute as G,join as E,resolve as p}from"node:path";var l="CONTEXT_MODE_DIR",Y="sessions",j="content",h=class extends Error{kind;path;overrideEnvVar;ignoredEnvVar;ignoredReason;constructor(e,t,r=l,s,a,i={}){super(a??Ce(e,t,i),{cause:s}),this.name="StorageDirectoryError",this.kind=e,this.path=t,this.overrideEnvVar=r,this.ignoredEnvVar=i.ignoredEnvVar,this.ignoredReason=i.ignoredReason}},D=new Map;function Ge(n){let e=n.env??process.env,t=n.legacySessionDirEnv,r=t?e[t]?.trim():void 0;return r&&t?(n.onLegacySessionDir?.(t,r),r):E(be(n.configDir,n.configDirEnv,e),"context-mode","sessions")}function be(n,e,t){let r=e?t[e]:void 0;return r&&r.trim()!==""?V(r.trim()):V(n,q())}function V(n,e){return n.startsWith("~")?p(q(),n.replace(/^~[/\\]?/,"")):G(n)?p(n):e?p(e,n):p(n)}function De(n,e,t){return new h(n,e,l,void 0,[`Invalid ${l} for context-mode ${n} directory: ${t}`,J()].join(`
|
|
2
|
+
`))}function K(n){let e=process.env[l];if(e===void 0)return{kind:"unset"};let t=e.trim();if(!t)return{kind:"ignored-empty",ignoredEnvVar:l,ignoredReason:"empty"};if(!G(t))throw De(n,t,`${l} must be an absolute path.`);return{kind:"override",root:p(t)}}function Le(n){return n.kind==="ignored-empty"?{ignoredEnvVar:n.ignoredEnvVar,ignoredReason:n.ignoredReason}:{}}function z(n,e){let t=K(n);return t.kind!=="override"?null:{kind:n,path:E(t.root,e),envVar:l,source:"override"}}function Ne(n,e,t){return{kind:n,path:p(e()),envVar:null,source:"default",...t}}function Q(n){let e=K("session");return e.kind==="override"?{kind:"session",path:E(e.root,Y),envVar:l,source:"override"}:Ne("session",n,Le(e))}function Ye(n){let e=z("content",j);if(e)return e;let t=Q(n);return{kind:"content",path:E(Re(t.path),j),envVar:t.envVar,source:t.source,ignoredEnvVar:t.ignoredEnvVar,ignoredReason:t.ignoredReason}}function Ke(n){let e=z("stats",Y);if(e)return e;let t=Q(n);return{kind:"stats",path:t.path,envVar:t.envVar,source:t.source,ignoredEnvVar:t.ignoredEnvVar,ignoredReason:t.ignoredReason}}function ze(n){return n.message}function Qe(n){return n.source==="override"&&n.envVar?`via ${n.envVar}`:n.ignoredEnvVar&&n.ignoredReason==="empty"?`default; ignored empty ${n.ignoredEnvVar}`:"default"}function Je(){D.clear()}function Ze(n){let e=[n.kind,n.path,n.source,n.envVar??"",n.ignoredEnvVar??"",n.ignoredReason??""].join("\0"),t=D.get(e);if(t instanceof h)throw t;if(t===n.path)return t;try{return Te(n.path,{recursive:!0}),fe(n.path,he.W_OK),D.set(e,n.path),n.path}catch(r){let s=new h(n.kind,Ae(r)??n.path,l,r,void 0,{ignoredEnvVar:n.ignoredEnvVar,ignoredReason:n.ignoredReason});throw D.set(e,s),s}}function Ce(n,e,t={}){return[`context-mode ${n} directory is not writable: ${e}`,Oe(t),J()].filter(Boolean).join(`
|
|
3
|
+
`)}function Oe(n){return n.ignoredEnvVar&&n.ignoredReason==="empty"?`Ignored empty ${n.ignoredEnvVar}; using adapter default.`:null}function J(){return`Set ${l} to a writable absolute path.`}function Ae(n){if(!n||typeof n!="object")return null;let e=n.path;return typeof e=="string"&&e.length>0?e:null}var _;function g(n){let e=n.replace(/\\/g,"/");return/^\/+$/.test(e)?"/":/^[A-Za-z]:\/+$/.test(e)?`${e.slice(0,2)}/`:e.replace(/\/+$/,"")}function H(n){let e=n;try{e=ve.native(n)}catch{}let t=g(e);return process.platform==="win32"||process.platform==="darwin"?t.toLowerCase():t}function Z(n,e){return Se("git",["-C",n,...e],{encoding:"utf-8",timeout:2e3,stdio:["ignore","pipe","ignore"]}).trim()}function we(n){let e=Z(n,["rev-parse","--show-toplevel"]);return e.length>0?g(e):null}function xe(n){let e=Z(n,["worktree","list","--porcelain"]).split(/\r?\n/).find(t=>t.startsWith("worktree "))?.replace("worktree ","")?.trim();return e?g(e):null}function Ie(n=process.cwd()){let e=process.env.CONTEXT_MODE_SESSION_SUFFIX;if(_&&_.projectDir===n&&_.envSuffix===e)return _.suffix;let t="";if(e!==void 0)t=e?`__${e}`:"";else try{let r=we(n),s=xe(n);if(r&&s){let a=H(r),i=H(s);a!==i&&(t=`__${f("sha256").update(a).digest("hex").slice(0,8)}`)}}catch{}return _={projectDir:n,envSuffix:e,suffix:t},t}function et(){_=void 0}function ee(n){return f("sha256").update(g(n)).digest("hex").slice(0,16)}function te(n){let e=g(n),t=process.platform==="darwin"||process.platform==="win32"?e.toLowerCase():e;return f("sha256").update(t).digest("hex").slice(0,16)}function tt(n){let{projectDir:e,contentDir:t}=n,r=te(e),s=E(t,`${r}.db`);if(L(s))return s;let a=ee(e);if(a===r)return s;let i=E(t,`${a}.db`);if(L(i))try{I(i,s);for(let c of["-wal","-shm"])try{I(i+c,s+c)}catch{}}catch{}return s}function nt(n){return Me({...n,ext:".db"})}function Me(n){let{projectDir:e,sessionsDir:t,ext:r}=n,s=n.suffix??Ie(e),a=te(e),i=E(t,`${a}${s}${r}`);if(L(i))return i;let c=ee(e);if(c===a)return i;let d=E(t,`${c}${s}${r}`);if(L(d))try{I(d,i)}catch{}return i}var W=1e3,X=5;function b(n){let e=Number(n);return!Number.isFinite(e)||e<=0?0:Math.floor(e)}var o={insertEvent:"insertEvent",getEvents:"getEvents",getEventsByType:"getEventsByType",getEventsByPriority:"getEventsByPriority",getEventsByTypeAndPriority:"getEventsByTypeAndPriority",getEventCount:"getEventCount",getLatestAttributedProject:"getLatestAttributedProject",checkDuplicate:"checkDuplicate",evictLowestPriority:"evictLowestPriority",updateMetaLastEvent:"updateMetaLastEvent",ensureSession:"ensureSession",getSessionStats:"getSessionStats",getSessionRollup:"getSessionRollup",getMaxFileEdits:"getMaxFileEdits",getLatestCommitMessage:"getLatestCommitMessage",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",getEventBytesSummary:"getEventBytesSummary"},Ue=[["project_dir","TEXT NOT NULL DEFAULT ''"],["attribution_source","TEXT NOT NULL DEFAULT 'unknown'"],["attribution_confidence","REAL NOT NULL DEFAULT 0"],["bytes_avoided","INTEGER NOT NULL DEFAULT 0"],["bytes_returned","INTEGER NOT NULL DEFAULT 0"]];function ne(n){let e=n.pragma("table_xinfo(session_events)"),t=new Set(e.map(s=>s.name)),r=!1;for(let[s,a]of Ue)t.has(s)||(n.exec(`ALTER TABLE session_events ADD COLUMN ${s} ${a}`),r=!0);return r&&n.exec("CREATE INDEX IF NOT EXISTS idx_session_events_project ON session_events(session_id, project_dir)"),r}function rt(n,e){let t=null;try{t=new e(n),ne(t)}catch{}finally{try{t?.close()}catch{}}}var $=class extends R{constructor(e){super(e?.dbPath??B("session"))}stmt(e){return this.stmts.get(e)}initSchema(){try{let t=this.db.pragma("table_xinfo(session_events)").find(r=>r.name==="data_hash");t&&t.hidden!==0&&this.db.exec("DROP TABLE session_events")}catch{}this.db.exec(`
|
|
4
4
|
CREATE TABLE IF NOT EXISTS session_events (
|
|
5
5
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6
6
|
session_id TEXT NOT NULL,
|
|
@@ -92,7 +92,7 @@ import{createRequire as oe}from"node:module";import{existsSync as ie,unlinkSync
|
|
|
92
92
|
COALESCE(SUM(CASE WHEN category = 'error' THEN 1 ELSE 0 END), 0) AS errors,
|
|
93
93
|
COUNT(DISTINCT type) AS unique_tools,
|
|
94
94
|
COUNT(DISTINCT CASE WHEN category = 'file' THEN data END) AS unique_files,
|
|
95
|
-
CASE WHEN SUM(CASE WHEN
|
|
95
|
+
CASE WHEN SUM(CASE WHEN type = 'git_commit' THEN 1 ELSE 0 END) > 0 THEN 1 ELSE 0 END AS has_commit,
|
|
96
96
|
CAST(COALESCE((MAX(strftime('%s', created_at)) - MIN(strftime('%s', created_at))) / 60.0, 0) AS INTEGER) AS duration_min,
|
|
97
97
|
COALESCE(SUM(CASE WHEN type = 'external_ref' THEN 1 ELSE 0 END), 0) AS sources_indexed,
|
|
98
98
|
CAST(COALESCE(SUM(bytes_avoided) / 1024.0, 0) AS INTEGER) AS total_chunks,
|
|
@@ -104,7 +104,11 @@ import{createRequire as oe}from"node:module";import{existsSync as ie,unlinkSync
|
|
|
104
104
|
FROM session_events
|
|
105
105
|
WHERE session_id = ? AND category = 'file' AND type IN ('file_edit', 'file_write')
|
|
106
106
|
GROUP BY data
|
|
107
|
-
)`),e(o.
|
|
107
|
+
)`),e(o.getLatestCommitMessage,`SELECT data
|
|
108
|
+
FROM session_events
|
|
109
|
+
WHERE session_id = ? AND type = 'git_commit'
|
|
110
|
+
ORDER BY id DESC
|
|
111
|
+
LIMIT 1`),e(o.incrementCompactCount,"UPDATE session_meta SET compact_count = compact_count + 1 WHERE session_id = ?"),e(o.upsertResume,`INSERT INTO session_resume (session_id, snapshot, event_count)
|
|
108
112
|
VALUES (?, ?, ?)
|
|
109
113
|
ON CONFLICT(session_id) DO UPDATE SET
|
|
110
114
|
snapshot = excluded.snapshot,
|
|
@@ -135,6 +139,6 @@ import{createRequire as oe}from"node:module";import{existsSync as ie,unlinkSync
|
|
|
135
139
|
FROM tool_calls WHERE session_id = ?`),e(o.getToolCallByTool,`SELECT tool, calls, bytes_returned
|
|
136
140
|
FROM tool_calls WHERE session_id = ? ORDER BY calls DESC`),e(o.getEventBytesSummary,`SELECT COALESCE(SUM(bytes_avoided), 0) AS bytes_avoided,
|
|
137
141
|
COALESCE(SUM(bytes_returned), 0) AS bytes_returned
|
|
138
|
-
FROM session_events WHERE session_id = ?`)}insertEvent(e,t,r="PostToolUse",s,a){let i=
|
|
142
|
+
FROM session_events WHERE session_id = ?`)}insertEvent(e,t,r="PostToolUse",s,a){let i=f("sha256").update(t.data).digest("hex").slice(0,16).toUpperCase(),c=String(s?.projectDir??t.project_dir??this._getSessionProjectDir(e)).trim(),d=String(s?.source??t.attribution_source??"unknown"),u=Number(s?.confidence??t.attribution_confidence??0),T=Number.isFinite(u)?Math.max(0,Math.min(1,u)):0,y=b(a?.bytesAvoided),v=b(a?.bytesReturned),N=this.db.transaction(()=>{if(this.stmt(o.checkDuplicate).get(e,X,t.type,i))return;this.stmt(o.getEventCount).get(e).cnt>=W&&this.stmt(o.evictLowestPriority).run(e),this.stmt(o.insertEvent).run(e,t.type,t.category,t.priority,t.data,c,d,T,y,v,r,i),this.stmt(o.updateMetaLastEvent).run(e)});this.withRetry(()=>N())}bulkInsertEvents(e,t,r="PostToolUse",s,a){if(!t||t.length===0)return;if(t.length===1){this.insertEvent(e,t[0],r,s?.[0],a?.[0]);return}let i=t.map((d,u)=>{let T=f("sha256").update(d.data).digest("hex").slice(0,16).toUpperCase(),y=s?.[u],v=String(y?.projectDir??d.project_dir??this._getSessionProjectDir(e)??"").trim(),N=v===""?"":g(v),M=String(y?.source??d.attribution_source??"unknown"),C=Number(y?.confidence??d.attribution_confidence??0),re=Number.isFinite(C)?Math.max(0,Math.min(1,C)):0,U=a?.[u],se=b(U?.bytesAvoided),oe=b(U?.bytesReturned);return{event:d,dataHash:T,projectDir:N,attributionSource:M,attributionConfidence:re,bytesAvoided:se,bytesReturned:oe}}),c=this.db.transaction(()=>{let d=this.stmt(o.getEventCount).get(e).cnt;for(let u of i)this.stmt(o.checkDuplicate).get(e,X,u.event.type,u.dataHash)||(d>=W?this.stmt(o.evictLowestPriority).run(e):d++,this.stmt(o.insertEvent).run(e,u.event.type,u.event.category,u.event.priority,u.event.data,u.projectDir,u.attributionSource,u.attributionConfidence,u.bytesAvoided,u.bytesReturned,r,u.dataHash));this.stmt(o.updateMetaLastEvent).run(e)});this.withRetry(()=>c())}getEvents(e,t){let r=t?.limit??1e3,s=t?.type,a=t?.minPriority;return s&&a!==void 0?this.stmt(o.getEventsByTypeAndPriority).all(e,s,a,r):s?this.stmt(o.getEventsByType).all(e,s,r):a!==void 0?this.stmt(o.getEventsByPriority).all(e,a,r):this.stmt(o.getEvents).all(e,r)}getEventCount(e){return this.stmt(o.getEventCount).get(e).cnt}getEventBytesSummary(e){let t=this.stmt(o.getEventBytesSummary).get(e);return{bytesAvoided:Number(t?.bytes_avoided??0),bytesReturned:Number(t?.bytes_returned??0)}}getLatestAttributedProjectDir(e){return this.stmt(o.getLatestAttributedProject).get(e)?.project_dir||null}_getSessionProjectDir(e){try{return this.db.prepare("SELECT project_dir FROM session_meta WHERE session_id = ?").get(e)?.project_dir||""}catch{return""}}searchEvents(e,t,r,s){try{let a=e.replace(/[%_]/g,c=>"\\"+c),i=s??null;return this.stmt(o.searchEvents).all(r,a,a,i,i,t)}catch{return[]}}getSessionIdsForProject(e){try{let t=g(e);return this.db.prepare(`SELECT DISTINCT session_id
|
|
139
143
|
FROM session_events
|
|
140
|
-
WHERE project_dir = ?`).all(
|
|
144
|
+
WHERE RTRIM(REPLACE(project_dir, '\\', '/'), '/') = ?`).all(t).map(s=>s.session_id)}catch{return[]}}ensureSession(e,t){this.stmt(o.ensureSession).run(e,t)}getSessionStats(e){return this.stmt(o.getSessionStats).get(e)??null}getSessionRollup(e){let t=this.stmt(o.getSessionRollup).get(e),r=this.stmt(o.getMaxFileEdits).get(e),s=this.stmt(o.getLatestCommitMessage).get(e),a=this.getSessionStats(e),i=(t?.tool_calls??0)>0?t?.unique_files??0:0,c=t?.errors??0,d=Math.min(i,c);return{tool_calls:t?.tool_calls??0,errors:t?.errors??0,unique_tools:t?.unique_tools??0,unique_files:t?.unique_files??0,max_file_edits:r?.max_file_edits??0,has_commit:t?.has_commit??0,commit_message:s?.data??"",edit_test_cycles:d,duration_min:t?.duration_min??0,compact_count:a?.compact_count??0,sources_indexed:t?.sources_indexed??0,total_chunks:t?.total_chunks??0,search_queries:t?.search_queries??0}}incrementCompactCount(e){this.stmt(o.incrementCompactCount).run(e)}upsertResume(e,t,r){this.stmt(o.upsertResume).run(e,t,r??0)}getResume(e){return this.stmt(o.getResume).get(e)??null}markResumeConsumed(e){this.stmt(o.markResumeConsumed).run(e)}claimLatestUnconsumedResume(e){let t=this.stmt(o.claimLatestUnconsumedResume).get(e);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(e,t,r=0){let s=Number.isFinite(r)&&r>0?Math.round(r):0;try{this.stmt(o.incrementToolCall).run(e,t,s)}catch{}}getToolCallStats(e){try{let t=this.stmt(o.getToolCallTotals).get(e),r=this.stmt(o.getToolCallByTool).all(e),s={};for(let a of r)s[a.tool]={calls:a.calls,bytesReturned:a.bytes_returned};return{totalCalls:t?.calls??0,totalBytesReturned:t?.bytes_returned??0,byTool:s}}catch{return{totalCalls:0,totalBytesReturned:0,byTool:{}}}}deleteSession(e){this.db.transaction(()=>{this.stmt(o.deleteEvents).run(e),this.stmt(o.deleteResume).run(e),this.stmt(o.deleteMeta).run(e)})()}cleanupOldSessions(e=7){let t=`-${e}`,r=this.stmt(o.getOldSessions).all(t);for(let{session_id:s}of r)this.deleteSession(s);return r.length}pruneOrphanedEvents(){let e=this.db.prepare("DELETE FROM session_events WHERE session_id NOT IN (SELECT session_id FROM session_meta)").run();return Number(e.changes??0)}};export{$ as SessionDB,h as StorageDirectoryError,et as _resetWorktreeSuffixCacheForTests,ne as applyMissingSessionEventsColumns,Je as clearStorageDirectoryCheckCacheForTests,Qe as describeStorageDirectorySource,rt as ensureSessionEventsSchema,Ze as ensureWritableStorageDir,ze as formatStorageDirectoryError,Ie as getWorktreeSuffix,te as hashProjectDirCanonical,ee as hashProjectDirLegacy,g as normalizeWorktreePath,Ye as resolveContentStorageDir,tt as resolveContentStorePath,Ge as resolveDefaultSessionDir,nt as resolveSessionDbPath,Me as resolveSessionPath,Q as resolveSessionStorageDir,Ke as resolveStatsStorageDir};
|