context-mode 1.0.162 → 1.0.163

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.openclaw-plugin/openclaw.plugin.json +1 -1
  5. package/.openclaw-plugin/package.json +1 -1
  6. package/README.md +142 -28
  7. package/bin/statusline.mjs +24 -4
  8. package/build/adapters/antigravity/index.d.ts +1 -1
  9. package/build/adapters/antigravity-cli/index.d.ts +51 -0
  10. package/build/adapters/antigravity-cli/index.js +341 -0
  11. package/build/adapters/claude-code/hooks.d.ts +1 -0
  12. package/build/adapters/claude-code/hooks.js +3 -0
  13. package/build/adapters/claude-code/index.js +24 -5
  14. package/build/adapters/client-map.js +5 -0
  15. package/build/adapters/codex/hooks.d.ts +5 -1
  16. package/build/adapters/codex/hooks.js +5 -1
  17. package/build/adapters/codex/index.d.ts +9 -1
  18. package/build/adapters/codex/index.js +87 -5
  19. package/build/adapters/copilot-cli/hooks.d.ts +33 -0
  20. package/build/adapters/copilot-cli/hooks.js +64 -0
  21. package/build/adapters/copilot-cli/index.d.ts +48 -0
  22. package/build/adapters/copilot-cli/index.js +341 -0
  23. package/build/adapters/detect.d.ts +1 -1
  24. package/build/adapters/detect.js +71 -3
  25. package/build/adapters/openclaw/mcp-tools.js +1 -1
  26. package/build/adapters/opencode/index.js +31 -17
  27. package/build/adapters/opencode/zod3tov4.js +27 -6
  28. package/build/adapters/pi/extension.d.ts +2 -12
  29. package/build/adapters/pi/extension.js +114 -96
  30. package/build/adapters/types.d.ts +5 -4
  31. package/build/adapters/types.js +4 -3
  32. package/build/cache-heal.d.ts +48 -0
  33. package/build/cache-heal.js +150 -0
  34. package/build/cli.js +37 -97
  35. package/build/executor.d.ts +25 -0
  36. package/build/executor.js +143 -22
  37. package/build/opencode-plugin.js +5 -2
  38. package/build/routing-block.d.ts +8 -0
  39. package/build/routing-block.js +86 -0
  40. package/build/runtime.d.ts +0 -36
  41. package/build/runtime.js +107 -27
  42. package/build/search/flood-guard.d.ts +57 -0
  43. package/build/search/flood-guard.js +80 -0
  44. package/build/security.d.ts +8 -3
  45. package/build/security.js +155 -29
  46. package/build/server.d.ts +14 -0
  47. package/build/server.js +368 -350
  48. package/build/session/analytics.d.ts +1 -1
  49. package/build/session/analytics.js +5 -1
  50. package/build/session/db.js +23 -3
  51. package/build/session/extract.js +8 -0
  52. package/build/store.d.ts +1 -1
  53. package/build/store.js +139 -25
  54. package/build/tool-naming.d.ts +4 -0
  55. package/build/tool-naming.js +24 -0
  56. package/build/util/jsonc.d.ts +14 -0
  57. package/build/util/jsonc.js +104 -0
  58. package/cli.bundle.mjs +254 -252
  59. package/configs/antigravity/GEMINI.md +2 -2
  60. package/configs/antigravity-cli/hooks/hooks.json +37 -0
  61. package/configs/antigravity-cli/hooks.json +37 -0
  62. package/configs/antigravity-cli/mcp_config.json +10 -0
  63. package/configs/antigravity-cli/plugin.json +14 -0
  64. package/configs/antigravity-cli/rules/context-mode.md +77 -0
  65. package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
  66. package/configs/claude-code/CLAUDE.md +2 -2
  67. package/configs/codex/AGENTS.md +2 -2
  68. package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
  69. package/configs/copilot-cli/.mcp.json +12 -0
  70. package/configs/copilot-cli/README.md +47 -0
  71. package/configs/copilot-cli/hooks.json +41 -0
  72. package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
  73. package/configs/gemini-cli/GEMINI.md +2 -2
  74. package/configs/jetbrains-copilot/copilot-instructions.md +2 -2
  75. package/configs/kilo/AGENTS.md +2 -2
  76. package/configs/kiro/KIRO.md +2 -2
  77. package/configs/omp/SYSTEM.md +2 -2
  78. package/configs/openclaw/AGENTS.md +2 -2
  79. package/configs/opencode/AGENTS.md +2 -2
  80. package/configs/qwen-code/QWEN.md +2 -2
  81. package/configs/vscode-copilot/copilot-instructions.md +2 -2
  82. package/configs/zed/AGENTS.md +2 -2
  83. package/hooks/antigravity-cli/payload.mjs +98 -0
  84. package/hooks/antigravity-cli/posttooluse.mjs +138 -0
  85. package/hooks/antigravity-cli/pretooluse.mjs +78 -0
  86. package/hooks/antigravity-cli/stop.mjs +58 -0
  87. package/hooks/codex/pretooluse.mjs +14 -4
  88. package/hooks/codex/stop.mjs +12 -4
  89. package/hooks/copilot-cli/posttooluse.mjs +79 -0
  90. package/hooks/copilot-cli/precompact.mjs +66 -0
  91. package/hooks/copilot-cli/pretooluse.mjs +41 -0
  92. package/hooks/copilot-cli/sessionstart.mjs +121 -0
  93. package/hooks/copilot-cli/stop.mjs +59 -0
  94. package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
  95. package/hooks/core/codex-caps.mjs +112 -0
  96. package/hooks/core/formatters.mjs +158 -7
  97. package/hooks/core/mcp-ready.mjs +37 -8
  98. package/hooks/core/routing.mjs +94 -8
  99. package/hooks/core/tool-naming.mjs +3 -0
  100. package/hooks/hooks.json +12 -1
  101. package/hooks/pretooluse.mjs +6 -2
  102. package/hooks/routing-block.mjs +2 -2
  103. package/hooks/security.bundle.mjs +2 -1
  104. package/hooks/session-db.bundle.mjs +5 -5
  105. package/hooks/session-directive.mjs +88 -20
  106. package/hooks/session-extract.bundle.mjs +1 -1
  107. package/hooks/session-helpers.mjs +21 -0
  108. package/hooks/sessionstart.mjs +37 -5
  109. package/hooks/stop.mjs +49 -0
  110. package/openclaw.plugin.json +1 -1
  111. package/package.json +4 -10
  112. package/scripts/install-antigravity-cli-plugin.mjs +141 -0
  113. package/server.bundle.mjs +208 -203
  114. package/skills/ctx-insight/SKILL.md +12 -17
  115. package/build/util/db-lock.d.ts +0 -65
  116. package/build/util/db-lock.js +0 -166
  117. package/insight/index.html +0 -13
  118. package/insight/package.json +0 -55
  119. package/insight/server.mjs +0 -1265
  120. package/insight/src/components/analytics.tsx +0 -112
  121. package/insight/src/components/ui/badge.tsx +0 -52
  122. package/insight/src/components/ui/button.tsx +0 -58
  123. package/insight/src/components/ui/card.tsx +0 -103
  124. package/insight/src/components/ui/chart.tsx +0 -371
  125. package/insight/src/components/ui/collapsible.tsx +0 -19
  126. package/insight/src/components/ui/input.tsx +0 -20
  127. package/insight/src/components/ui/progress.tsx +0 -83
  128. package/insight/src/components/ui/scroll-area.tsx +0 -55
  129. package/insight/src/components/ui/separator.tsx +0 -23
  130. package/insight/src/components/ui/table.tsx +0 -114
  131. package/insight/src/components/ui/tabs.tsx +0 -82
  132. package/insight/src/components/ui/tooltip.tsx +0 -64
  133. package/insight/src/lib/api.ts +0 -144
  134. package/insight/src/lib/utils.ts +0 -6
  135. package/insight/src/main.tsx +0 -22
  136. package/insight/src/routeTree.gen.ts +0 -189
  137. package/insight/src/router.tsx +0 -19
  138. package/insight/src/routes/__root.tsx +0 -55
  139. package/insight/src/routes/enterprise.tsx +0 -316
  140. package/insight/src/routes/index.tsx +0 -1482
  141. package/insight/src/routes/knowledge.tsx +0 -221
  142. package/insight/src/routes/knowledge_.$dbHash.$sourceId.tsx +0 -137
  143. package/insight/src/routes/search.tsx +0 -97
  144. package/insight/src/routes/sessions.tsx +0 -179
  145. package/insight/src/routes/sessions_.$dbHash.$sessionId.tsx +0 -181
  146. package/insight/src/styles.css +0 -104
  147. package/insight/tsconfig.json +0 -29
  148. package/insight/vite.config.ts +0 -19
@@ -0,0 +1,64 @@
1
+ /**
2
+ * adapters/copilot-cli/hooks — GitHub Copilot CLI hook definitions.
3
+ *
4
+ * GitHub Copilot CLI's native hook events are the camelCase names
5
+ * preToolUse / postToolUse / sessionStart / userPromptSubmitted / agentStop /
6
+ * preCompact. Per the copilot-cli changelog, PascalCase event names are ALSO
7
+ * accepted and fire — the CLI loads hook configs across VS Code, Claude Code,
8
+ * and the CLI by accepting PascalCase event names alongside camelCase
9
+ * (copilot-cli changelog.md:1065; see also :811 and :1081). We register the
10
+ * camelCase KEYS below because they are the CLI's native names, not because
11
+ * PascalCase would fail. The CLI dispatch token (the `.mjs` script base, e.g.
12
+ * `pretooluse`) is independent and stays lowercase via buildHookCommand, so
13
+ * changing the event casing does not change the dispatcher.
14
+ */
15
+ export const HOOK_TYPES = {
16
+ PRE_TOOL_USE: "preToolUse",
17
+ POST_TOOL_USE: "postToolUse",
18
+ PRE_COMPACT: "preCompact",
19
+ SESSION_START: "sessionStart",
20
+ // Copilot CLI 1.0.60 fires userPromptSubmitted + agentStop (verified against
21
+ // the @github/copilot binary). Used for user-prompt + session-end capture,
22
+ // same as the codex/kimi adapters.
23
+ USER_PROMPT_SUBMIT: "userPromptSubmitted",
24
+ STOP: "agentStop",
25
+ };
26
+ export const HOOK_SCRIPTS = {
27
+ [HOOK_TYPES.PRE_TOOL_USE]: "pretooluse.mjs",
28
+ [HOOK_TYPES.POST_TOOL_USE]: "posttooluse.mjs",
29
+ [HOOK_TYPES.PRE_COMPACT]: "precompact.mjs",
30
+ [HOOK_TYPES.SESSION_START]: "sessionstart.mjs",
31
+ [HOOK_TYPES.USER_PROMPT_SUBMIT]: "userpromptsubmit.mjs",
32
+ [HOOK_TYPES.STOP]: "stop.mjs",
33
+ };
34
+ export const REQUIRED_HOOKS = [
35
+ HOOK_TYPES.PRE_TOOL_USE,
36
+ HOOK_TYPES.SESSION_START,
37
+ ];
38
+ export const OPTIONAL_HOOKS = [
39
+ HOOK_TYPES.POST_TOOL_USE,
40
+ HOOK_TYPES.PRE_COMPACT,
41
+ HOOK_TYPES.USER_PROMPT_SUBMIT,
42
+ HOOK_TYPES.STOP,
43
+ ];
44
+ export function isContextModeHook(entry, hookType) {
45
+ const scriptName = HOOK_SCRIPTS[hookType];
46
+ if (!scriptName)
47
+ return false;
48
+ const cliCommand = buildHookCommand(hookType);
49
+ if (entry.command?.includes(cliCommand) || entry.command?.includes(scriptName)) {
50
+ return true;
51
+ }
52
+ return (entry.hooks?.some((h) => h.command?.includes(scriptName) || h.command?.includes(cliCommand)) ?? false);
53
+ }
54
+ export function buildHookCommand(hookType, _pluginRoot) {
55
+ const scriptName = HOOK_SCRIPTS[hookType];
56
+ if (!scriptName) {
57
+ throw new Error(`No script defined for hook type: ${hookType}`);
58
+ }
59
+ // The CLI dispatch token is the script base (pretooluse, posttooluse, …), NOT
60
+ // the camelCase host event name — keep them decoupled so the event KEY stays
61
+ // Copilot's camelCase while the dispatcher token (and the cli.ts hook handler)
62
+ // remain stable regardless of how the host names its events.
63
+ return `context-mode hook copilot-cli ${scriptName.replace(/\.mjs$/, "")}`;
64
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * adapters/copilot-cli — GitHub Copilot CLI adapter.
3
+ *
4
+ * Native config:
5
+ * - MCP: $COPILOT_HOME/mcp-config.json or ~/.copilot/mcp-config.json
6
+ * under root key `mcpServers`.
7
+ * - Hooks: $COPILOT_HOME/hooks/context-mode.json or
8
+ * ~/.copilot/hooks/context-mode.json.
9
+ *
10
+ * Hooks use Copilot CLI's native camelCase event keys (preToolUse /
11
+ * postToolUse / sessionStart / userPromptSubmitted / agentStop / preCompact)
12
+ * with flat `{ type, command }` entries. (The CLI also accepts PascalCase
13
+ * event names alongside camelCase — copilot-cli changelog.md:1065 — so the
14
+ * casing is not load-bearing; we use camelCase because it is the CLI's native
15
+ * naming.) Copilot CLI's command output contract is top-level
16
+ * (`permissionDecision`, `modifiedArgs`, `additionalContext`), so this adapter
17
+ * overrides the response formatter from CopilotBaseAdapter.
18
+ */
19
+ import { CopilotBaseAdapter } from "../copilot-base.js";
20
+ import type { CopilotHookInput, CopilotHookModule } from "../copilot-base.js";
21
+ import type { DiagnosticResult, HookRegistration, PostToolUseResponse, PreCompactResponse, PostToolUseEvent, PreToolUseEvent, PreToolUseResponse, SessionStartResponse } from "../types.js";
22
+ export declare function copilotCliHome(): string;
23
+ export declare function copilotCliMcpConfigPath(): string;
24
+ export declare class CopilotCliAdapter extends CopilotBaseAdapter {
25
+ constructor();
26
+ readonly name = "GitHub Copilot CLI";
27
+ protected readonly hookModule: CopilotHookModule;
28
+ protected readonly hookSubdir = "copilot-cli";
29
+ protected extractSessionId(input: CopilotHookInput): string;
30
+ protected getProjectDir(): string;
31
+ parsePreToolUseInput(raw: unknown): PreToolUseEvent;
32
+ parsePostToolUseInput(raw: unknown): PostToolUseEvent;
33
+ getSettingsPath(_projectDir?: string): string;
34
+ getConfigDir(_projectDir?: string): string;
35
+ getSessionDir(): string;
36
+ getInstructionFiles(): string[];
37
+ generateHookConfig(pluginRoot: string): HookRegistration;
38
+ writeSettings(settings: Record<string, unknown>): void;
39
+ configureAllHooks(pluginRoot: string): string[];
40
+ readSettings(): Record<string, unknown> | null;
41
+ formatPreToolUseResponse(response: PreToolUseResponse): unknown;
42
+ formatPostToolUseResponse(response: PostToolUseResponse): unknown;
43
+ formatPreCompactResponse(response: PreCompactResponse): unknown;
44
+ formatSessionStartResponse(response: SessionStartResponse): unknown;
45
+ validateHooks(pluginRoot: string): DiagnosticResult[];
46
+ checkPluginRegistration(): DiagnosticResult;
47
+ getInstalledVersion(): string;
48
+ }
@@ -0,0 +1,341 @@
1
+ /**
2
+ * adapters/copilot-cli — GitHub Copilot CLI adapter.
3
+ *
4
+ * Native config:
5
+ * - MCP: $COPILOT_HOME/mcp-config.json or ~/.copilot/mcp-config.json
6
+ * under root key `mcpServers`.
7
+ * - Hooks: $COPILOT_HOME/hooks/context-mode.json or
8
+ * ~/.copilot/hooks/context-mode.json.
9
+ *
10
+ * Hooks use Copilot CLI's native camelCase event keys (preToolUse /
11
+ * postToolUse / sessionStart / userPromptSubmitted / agentStop / preCompact)
12
+ * with flat `{ type, command }` entries. (The CLI also accepts PascalCase
13
+ * event names alongside camelCase — copilot-cli changelog.md:1065 — so the
14
+ * casing is not load-bearing; we use camelCase because it is the CLI's native
15
+ * naming.) Copilot CLI's command output contract is top-level
16
+ * (`permissionDecision`, `modifiedArgs`, `additionalContext`), so this adapter
17
+ * overrides the response formatter from CopilotBaseAdapter.
18
+ */
19
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
20
+ import { homedir } from "node:os";
21
+ import { dirname, join, resolve } from "node:path";
22
+ import { CopilotBaseAdapter } from "../copilot-base.js";
23
+ import { resolveContextModeDataRoot } from "../base.js";
24
+ import { parseJsonc } from "../../util/jsonc.js";
25
+ import { HOOK_TYPES as COPILOT_HOOK_NAMES, HOOK_SCRIPTS as COPILOT_HOOK_SCRIPTS, buildHookCommand as buildCopilotHookCommand, } from "./hooks.js";
26
+ const COPILOT_PLUGIN_ENV = "CONTEXT_MODE_COPILOT_PLUGIN";
27
+ function isCopilotPluginRuntime() {
28
+ return process.env[COPILOT_PLUGIN_ENV] === "1";
29
+ }
30
+ function copilotPluginHooksPath(pluginRoot) {
31
+ return resolve(pluginRoot, "configs", "copilot-cli", "hooks.json");
32
+ }
33
+ function readHookConfig(path) {
34
+ return parseJsonc(readFileSync(path, "utf-8")) ?? {};
35
+ }
36
+ function hasHook(hooks, hookName) {
37
+ return Array.isArray(hooks?.[hookName]) && (hooks?.[hookName]).length > 0;
38
+ }
39
+ export function copilotCliHome() {
40
+ const raw = process.env.COPILOT_HOME;
41
+ if (raw && raw.trim() !== "") {
42
+ if (raw.startsWith("~")) {
43
+ return join(homedir(), raw.replace(/^~[/\\]?/, ""));
44
+ }
45
+ return resolve(raw);
46
+ }
47
+ return join(homedir(), ".copilot");
48
+ }
49
+ export function copilotCliMcpConfigPath() {
50
+ return join(copilotCliHome(), "mcp-config.json");
51
+ }
52
+ export class CopilotCliAdapter extends CopilotBaseAdapter {
53
+ constructor() {
54
+ super([".copilot"]);
55
+ }
56
+ name = "GitHub Copilot CLI";
57
+ hookModule = {
58
+ HOOK_TYPES: COPILOT_HOOK_NAMES,
59
+ HOOK_SCRIPTS: COPILOT_HOOK_SCRIPTS,
60
+ buildHookCommand: buildCopilotHookCommand,
61
+ };
62
+ hookSubdir = "copilot-cli";
63
+ extractSessionId(input) {
64
+ const raw = input;
65
+ if (raw.transcript_path) {
66
+ const match = raw.transcript_path.match(/([a-f0-9-]{36})\.jsonl$/);
67
+ if (match)
68
+ return match[1];
69
+ }
70
+ if (raw.conversation_id)
71
+ return raw.conversation_id;
72
+ // `session_id` (snake_case) is the field Copilot CLI documents in its hook
73
+ // payloads (copilot-cli changelog.md:811). Prefer it; `sessionId` (camelCase)
74
+ // is kept only as a defensive fallback for non-standard payload shapes and is
75
+ // not a documented Copilot CLI field.
76
+ if (raw.session_id)
77
+ return raw.session_id;
78
+ if (input.sessionId)
79
+ return input.sessionId;
80
+ return `pid-${process.ppid}`;
81
+ }
82
+ getProjectDir() {
83
+ return process.cwd();
84
+ }
85
+ parsePreToolUseInput(raw) {
86
+ const input = raw;
87
+ return {
88
+ toolName: input.tool_name ?? input.toolName ?? "",
89
+ toolInput: input.tool_input ?? input.toolArgs ?? {},
90
+ sessionId: this.extractSessionId(input),
91
+ projectDir: typeof input.cwd === "string" && input.cwd ? input.cwd : process.cwd(),
92
+ raw,
93
+ };
94
+ }
95
+ parsePostToolUseInput(raw) {
96
+ const input = raw;
97
+ const output = input.tool_result?.text_result_for_llm ??
98
+ input.toolResult?.textResultForLlm ??
99
+ (typeof input.tool_response === "string" ? input.tool_response : undefined) ??
100
+ input.tool_output;
101
+ return {
102
+ toolName: input.tool_name ?? input.toolName ?? "",
103
+ toolInput: input.tool_input ?? input.toolArgs ?? {},
104
+ toolOutput: output,
105
+ isError: input.is_error,
106
+ sessionId: this.extractSessionId(input),
107
+ projectDir: typeof input.cwd === "string" && input.cwd ? input.cwd : process.cwd(),
108
+ raw,
109
+ };
110
+ }
111
+ getSettingsPath(_projectDir) {
112
+ return join(copilotCliHome(), "hooks", "context-mode.json");
113
+ }
114
+ getConfigDir(_projectDir) {
115
+ return copilotCliHome();
116
+ }
117
+ getSessionDir() {
118
+ // Parity with codex/kimi: honor CONTEXT_MODE_DATA_DIR first, else root
119
+ // session storage at getConfigDir() (= copilotCliHome(), COPILOT_HOME-aware)
120
+ // so the TS server reads sessions from the SAME place the hook runtime
121
+ // (COPILOT_OPTS configDirEnv: "COPILOT_HOME") writes them. Without this, a
122
+ // relocated COPILOT_HOME splits hook writes ($COPILOT_HOME/...) from server
123
+ // reads (~/.copilot/...) and sessions appear empty/orphaned.
124
+ const override = resolveContextModeDataRoot();
125
+ const dir = override
126
+ ? join(override, "context-mode", "sessions")
127
+ : join(this.getConfigDir(), "context-mode", "sessions");
128
+ mkdirSync(dir, { recursive: true });
129
+ return dir;
130
+ }
131
+ getInstructionFiles() {
132
+ return [".github/copilot-instructions.md", "AGENTS.md"];
133
+ }
134
+ // ── Hook-config writing (Copilot CLI shape) ────────────
135
+ //
136
+ // GitHub Copilot CLI's hooks schema differs from the VS Code / JetBrains
137
+ // Copilot extensions that share CopilotBaseAdapter:
138
+ // - hook entries are FLAT `{ type: "command", command }` — NOT the
139
+ // Claude-Code nested `{ matcher, hooks: [...] }` shape the base emits, and
140
+ // - we emit a top-level `"version": 1`. This is OPTIONAL, not mandatory:
141
+ // the Copilot CLI accepts hook config files that omit the version field
142
+ // (copilot-cli changelog.md:1109). We still write version:1 to pin the
143
+ // schema explicitly — it is harmless and self-documenting, NOT a
144
+ // hooks-never-fire requirement.
145
+ // The shared base also hardcodes `mkdir .github/hooks` and writes there, but
146
+ // this adapter's settings live at ~/.copilot/hooks/context-mode.json, so
147
+ // writeSettings is overridden to create the real parent directory. All three
148
+ // overrides are scoped to copilot-cli so vscode/jetbrains-copilot keep the
149
+ // shared base behavior unchanged.
150
+ generateHookConfig(pluginRoot) {
151
+ const { HOOK_TYPES, buildHookCommand } = this.hookModule;
152
+ const flat = (hookType) => [
153
+ { type: "command", command: buildHookCommand(hookType, pluginRoot) },
154
+ ];
155
+ // Emit an entry for every declared hook type (preToolUse, postToolUse,
156
+ // preCompact, sessionStart, userPromptSubmitted, agentStop).
157
+ const config = {};
158
+ for (const hookType of Object.values(HOOK_TYPES)) {
159
+ config[hookType] = flat(hookType);
160
+ }
161
+ return config;
162
+ }
163
+ writeSettings(settings) {
164
+ const configPath = this.getSettingsPath();
165
+ mkdirSync(dirname(configPath), { recursive: true });
166
+ writeFileSync(configPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
167
+ }
168
+ configureAllHooks(pluginRoot) {
169
+ const changes = [];
170
+ const settings = this.readSettings() ?? {};
171
+ const hooks = settings.hooks ?? {};
172
+ const { HOOK_TYPES, HOOK_SCRIPTS, buildHookCommand } = this.hookModule;
173
+ // Configure every hook type the module declares (preToolUse, postToolUse,
174
+ // preCompact, sessionStart, userPromptSubmitted, agentStop) — driven by
175
+ // HOOK_TYPES so new events are picked up automatically.
176
+ for (const hookType of Object.values(HOOK_TYPES)) {
177
+ if (!HOOK_SCRIPTS[hookType])
178
+ continue;
179
+ const desired = [
180
+ { type: "command", command: buildHookCommand(hookType, pluginRoot) },
181
+ ];
182
+ // Only treat a hook as drift when it differs from desired, so repeated
183
+ // `context-mode upgrade` runs stay idempotent.
184
+ if (JSON.stringify(hooks[hookType]) !== JSON.stringify(desired)) {
185
+ hooks[hookType] = desired;
186
+ changes.push(`Configured ${hookType} hook`);
187
+ }
188
+ }
189
+ // We pin version:1 explicitly (optional per copilot-cli changelog.md:1109,
190
+ // but self-documenting); treat a missing/mismatched value as drift to repair.
191
+ if (settings.version !== 1) {
192
+ changes.push("Set hooks schema version to 1");
193
+ }
194
+ if (changes.length > 0) {
195
+ settings.version = 1;
196
+ settings.hooks = hooks;
197
+ this.writeSettings(settings);
198
+ changes.push(`Wrote hook config to ${this.getSettingsPath()}`);
199
+ }
200
+ return changes;
201
+ }
202
+ readSettings() {
203
+ try {
204
+ const raw = readFileSync(this.getSettingsPath(), "utf-8");
205
+ return parseJsonc(raw) ?? null;
206
+ }
207
+ catch {
208
+ return null;
209
+ }
210
+ }
211
+ formatPreToolUseResponse(response) {
212
+ if (response.decision === "deny") {
213
+ return {
214
+ permissionDecision: "deny",
215
+ permissionDecisionReason: response.reason ?? "Blocked by context-mode hook",
216
+ };
217
+ }
218
+ if (response.decision === "ask") {
219
+ return {
220
+ permissionDecision: "ask",
221
+ permissionDecisionReason: response.reason ?? "Action requires user confirmation",
222
+ };
223
+ }
224
+ if (response.decision === "modify" && response.updatedInput) {
225
+ return { modifiedArgs: response.updatedInput };
226
+ }
227
+ if (response.decision === "context" && response.additionalContext) {
228
+ return { additionalContext: response.additionalContext };
229
+ }
230
+ return undefined;
231
+ }
232
+ formatPostToolUseResponse(response) {
233
+ if (response.updatedOutput) {
234
+ return {
235
+ modifiedResult: {
236
+ resultType: "success",
237
+ textResultForLlm: response.updatedOutput,
238
+ },
239
+ };
240
+ }
241
+ if (response.additionalContext) {
242
+ return { additionalContext: response.additionalContext };
243
+ }
244
+ return undefined;
245
+ }
246
+ formatPreCompactResponse(response) {
247
+ void response;
248
+ return undefined;
249
+ }
250
+ formatSessionStartResponse(response) {
251
+ return response.context ? { additionalContext: response.context } : undefined;
252
+ }
253
+ validateHooks(pluginRoot) {
254
+ const results = [];
255
+ const pluginRuntime = isCopilotPluginRuntime();
256
+ const settingsPath = pluginRuntime ? copilotPluginHooksPath(pluginRoot) : this.getSettingsPath();
257
+ const settingsLabel = pluginRuntime
258
+ ? `Copilot CLI plugin bundle hooks.json (${settingsPath})`
259
+ : settingsPath;
260
+ const fix = pluginRuntime
261
+ ? "copilot plugin install mksglu/context-mode:configs/copilot-cli"
262
+ : "context-mode upgrade";
263
+ try {
264
+ const config = readHookConfig(settingsPath);
265
+ const hooks = config.hooks;
266
+ results.push({
267
+ check: "Hooks schema version",
268
+ status: config.version === 1 ? "pass" : "fail",
269
+ message: config.version === 1
270
+ ? `${settingsLabel} declares the required "version": 1`
271
+ : `${settingsLabel} is missing top-level "version": 1`,
272
+ ...(config.version === 1 ? {} : { fix }),
273
+ });
274
+ for (const hookName of Object.values(COPILOT_HOOK_NAMES)) {
275
+ const configured = hasHook(hooks, hookName);
276
+ results.push({
277
+ check: `${hookName} hook`,
278
+ status: configured ? "pass" : "fail",
279
+ message: configured
280
+ ? `${hookName} hook configured in ${settingsLabel}`
281
+ : `${hookName} not found in ${settingsLabel}`,
282
+ ...(configured ? {} : { fix }),
283
+ });
284
+ }
285
+ }
286
+ catch {
287
+ results.push({
288
+ check: "Hook configuration",
289
+ status: "fail",
290
+ message: `Could not read ${settingsLabel}`,
291
+ fix,
292
+ });
293
+ }
294
+ return results;
295
+ }
296
+ checkPluginRegistration() {
297
+ if (isCopilotPluginRuntime()) {
298
+ return {
299
+ check: "MCP registration",
300
+ status: "pass",
301
+ message: "context-mode loaded from the Copilot CLI plugin bundle",
302
+ };
303
+ }
304
+ try {
305
+ const raw = readFileSync(copilotCliMcpConfigPath(), "utf-8");
306
+ const config = parseJsonc(raw) ?? {};
307
+ const mcpServers = config?.mcpServers ?? {};
308
+ if ("context-mode" in mcpServers) {
309
+ return {
310
+ check: "MCP registration",
311
+ status: "pass",
312
+ message: "context-mode found in Copilot CLI mcp-config.json",
313
+ };
314
+ }
315
+ return {
316
+ check: "MCP registration",
317
+ status: "fail",
318
+ message: "context-mode not found in Copilot CLI mcpServers",
319
+ // `context-mode upgrade` configures HOOKS only — never the MCP server.
320
+ // Copilot CLI's own command writes ~/.copilot/mcp-config.json for us.
321
+ fix: "copilot mcp add context-mode -- context-mode",
322
+ };
323
+ }
324
+ catch {
325
+ return {
326
+ check: "MCP registration",
327
+ status: "fail",
328
+ message: `Could not read ${copilotCliMcpConfigPath()}`,
329
+ fix: "copilot mcp add context-mode -- context-mode",
330
+ };
331
+ }
332
+ }
333
+ getInstalledVersion() {
334
+ // Copilot's user-level MCP config and local/plugin-dir bundle do not expose
335
+ // a durable installed plugin version to this process. The npm package
336
+ // version is already checked above, so avoid the false `vconfigured` WARN.
337
+ return existsSync(copilotCliMcpConfigPath()) || existsSync(this.getSettingsPath())
338
+ ? "standalone"
339
+ : "not installed";
340
+ }
341
+ }
@@ -42,7 +42,7 @@ export declare function __seedClaudeCodePluginCacheMissForTests(): void;
42
42
  *
43
43
  * Issue #545 — algorithmic env-leak fix. The split allows resolveProjectDir
44
44
  * to derive ALLOW (own workspace vars) and BAN (other platforms' workspace
45
- * vars) sets from a single registry, satisfying MUST-3 (15 adapters equal).
45
+ * vars) sets from a single registry, satisfying MUST-3 (17 adapters equal).
46
46
  *
47
47
  * Issue #561 — FOREIGN identification vars MUST be scrubbed when spawning a
48
48
  * child under a different host (e.g. Pi spawning context-mode child must
@@ -169,8 +169,9 @@ const _PLATFORM_ENV_VARS_RAW = [
169
169
  // refs/platforms/oh-my-pi/packages/coding-agent/src/mcp/transports/stdio.ts:55-63
170
170
  // (env passthrough only, no synthesis). The Pi runtime DOES set
171
171
  // PI_CONFIG_DIR (config dir override), PI_SESSION_FILE (active session
172
- // path), and PI_COMPILED (binary build marker). PI_CODING_AGENT_DIR is
173
- // owned by OMP above; keep it there.
172
+ // path), PI_COMPILED (binary build marker), and PI_CODING_AGENT=true
173
+ // in package-spawned MCP children (#760). PI_CODING_AGENT_DIR is owned
174
+ // by OMP above; keep it there.
174
175
  //
175
176
  // Issue #545 — PI_WORKSPACE_DIR / PI_PROJECT_DIR are workspace vars set
176
177
  // by Pi's bridge so the resolver picks them up under strict mode.
@@ -185,6 +186,7 @@ const _PLATFORM_ENV_VARS_RAW = [
185
186
  { name: "PI_CONFIG_DIR", role: "identification" },
186
187
  { name: "PI_SESSION_FILE", role: "identification" },
187
188
  { name: "PI_COMPILED", role: "identification" },
189
+ { name: "PI_CODING_AGENT", role: "identification" },
188
190
  ]],
189
191
  // openclaw — removed (runtime never sets OPENCLAW_HOME or OPENCLAW_CLI;
190
192
  // detection falls through to ~/.openclaw/ config-dir tier below).
@@ -273,10 +275,12 @@ export function getSessionDirSegments(platform) {
273
275
  case "claude-code": return [".claude"];
274
276
  case "gemini-cli": return [".gemini"];
275
277
  case "antigravity": return [".gemini"];
278
+ case "antigravity-cli": return [".gemini"];
276
279
  case "openclaw": return [".openclaw"];
277
280
  case "codex": return [".codex"];
278
281
  case "cursor": return [".cursor"];
279
282
  case "vscode-copilot": return [".vscode"];
283
+ case "copilot-cli": return [".copilot"];
280
284
  case "kiro": return [".kiro"];
281
285
  case "pi": return [".pi"];
282
286
  case "omp": return [".omp"];
@@ -320,7 +324,7 @@ export function detectPlatform(clientInfo) {
320
324
  if (platformOverride) {
321
325
  const validPlatforms = [
322
326
  "claude-code", "gemini-cli", "kilo", "opencode", "codex",
323
- "vscode-copilot", "jetbrains-copilot", "cursor", "antigravity", "kiro", "pi", "omp", "zed", "qwen-code", "kimi",
327
+ "vscode-copilot", "jetbrains-copilot", "copilot-cli", "cursor", "antigravity", "antigravity-cli", "kiro", "pi", "omp", "zed", "qwen-code", "kimi",
324
328
  ];
325
329
  if (validPlatforms.includes(platformOverride)) {
326
330
  return {
@@ -358,6 +362,62 @@ export function detectPlatform(clientInfo) {
358
362
  }
359
363
  // ── Medium confidence: config directory existence ──────
360
364
  const home = homedir();
365
+ // Issue #774 — dedicated CLI agents (Antigravity CLI `agy`, GitHub Copilot
366
+ // CLI) MUST be probed BEFORE the generic ~/.claude and ~/.gemini fallbacks.
367
+ // A user migrating from gemini-cli to `agy` keeps ~/.claude AND ~/.gemini, so
368
+ // the ~/.claude check below otherwise wins and `context-mode doctor`
369
+ // mis-detected `agy` as Claude Code — pointing storage at ~/.claude and
370
+ // reporting the wrong platform (reproduced in #774).
371
+ //
372
+ // Regression guard (whole-branch detection-ordering review): these markers
373
+ // are deliberately narrow so they cannot relocate an existing Claude Code
374
+ // user's storage from a bare shell. The antigravity-cli markers are either
375
+ // agy-exclusive (`~/.local/bin/agy`, `~/.gemini/antigravity-cli`) or map to
376
+ // the SAME `~/.gemini` root as gemini-cli (so a mis-detect is storage-neutral
377
+ // and never collides with `~/.gemini/settings.json` (gemini-cli) or
378
+ // `~/.gemini/antigravity/` (Antigravity IDE)). The copilot-cli marker is
379
+ // gated on a context-mode-written file (NOT a bare `~/.copilot/` directory),
380
+ // so a Claude Code user who merely co-installed GitHub Copilot CLI — but has
381
+ // not configured context-mode for it — is NOT pulled away from ~/.claude.
382
+ // GitHub Copilot CLI's config root is relocatable via COPILOT_HOME (the
383
+ // documented relocation env, incl. on Windows), so the marker must honor it —
384
+ // not just ~/.copilot. Mirrors copilotCliHome() in copilot-cli/index.ts.
385
+ const copilotHome = (() => {
386
+ const raw = process.env.COPILOT_HOME;
387
+ if (raw && raw.trim() !== "") {
388
+ return raw.startsWith("~") ? resolve(home, raw.replace(/^~[/\\]?/, "")) : resolve(raw);
389
+ }
390
+ return resolve(home, ".copilot");
391
+ })();
392
+ const copilotConfigured = existsSync(resolve(copilotHome, "mcp-config.json")) ||
393
+ existsSync(resolve(copilotHome, "hooks", "context-mode.json"));
394
+ // A non-empty COPILOT_HOME is an explicit user/session selection, not a
395
+ // passive co-install marker. Respect it before agy's global markers so a
396
+ // Copilot doctor run in an isolated COPILOT_HOME is not stolen by an
397
+ // unrelated ~/.local/bin/agy or ~/.gemini/config/mcp_config.json.
398
+ if (process.env.COPILOT_HOME?.trim() && copilotConfigured) {
399
+ return {
400
+ platform: "copilot-cli",
401
+ confidence: "medium",
402
+ reason: "context-mode config in explicit COPILOT_HOME exists (mcp-config.json or hooks/context-mode.json)",
403
+ };
404
+ }
405
+ if (existsSync(resolve(home, ".local", "bin", "agy")) ||
406
+ existsSync(resolve(home, ".gemini", "antigravity-cli")) ||
407
+ existsSync(resolve(home, ".gemini", "config", "mcp_config.json"))) {
408
+ return {
409
+ platform: "antigravity-cli",
410
+ confidence: "medium",
411
+ reason: "Antigravity CLI marker exists (~/.local/bin/agy, ~/.gemini/antigravity-cli, or ~/.gemini/config/mcp_config.json)",
412
+ };
413
+ }
414
+ if (copilotConfigured) {
415
+ return {
416
+ platform: "copilot-cli",
417
+ confidence: "medium",
418
+ reason: "context-mode config in Copilot CLI home exists (mcp-config.json or hooks/context-mode.json; honors COPILOT_HOME)",
419
+ };
420
+ }
361
421
  if (existsSync(resolve(home, ".claude"))) {
362
422
  return {
363
423
  platform: "claude-code",
@@ -510,6 +570,10 @@ export async function getAdapter(platform) {
510
570
  const { JetBrainsCopilotAdapter } = await import("./jetbrains-copilot/index.js");
511
571
  return new JetBrainsCopilotAdapter();
512
572
  }
573
+ case "copilot-cli": {
574
+ const { CopilotCliAdapter } = await import("./copilot-cli/index.js");
575
+ return new CopilotCliAdapter();
576
+ }
513
577
  case "cursor": {
514
578
  const { CursorAdapter } = await import("./cursor/index.js");
515
579
  return new CursorAdapter();
@@ -518,6 +582,10 @@ export async function getAdapter(platform) {
518
582
  const { AntigravityAdapter } = await import("./antigravity/index.js");
519
583
  return new AntigravityAdapter();
520
584
  }
585
+ case "antigravity-cli": {
586
+ const { AntigravityCliAdapter } = await import("./antigravity-cli/index.js");
587
+ return new AntigravityCliAdapter();
588
+ }
521
589
  case "kiro": {
522
590
  const { KiroAdapter } = await import("./kiro/index.js");
523
591
  return new KiroAdapter();
@@ -194,7 +194,7 @@ export const OPENCLAW_TOOL_DEFS = [
194
194
  },
195
195
  {
196
196
  name: "ctx_insight",
197
- description: "Open the context-mode Insight analytics dashboard in the browser.",
197
+ description: "Open the hosted context-mode Insight dashboard (context-mode.com/insight) in the browser.",
198
198
  parameters: {
199
199
  type: "object",
200
200
  properties: {},