context-mode 1.0.162 → 1.0.164
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 +149 -30
- 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 +342 -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 +128 -109
- 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/lifecycle.d.ts +48 -0
- package/build/lifecycle.js +111 -0
- 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 +73 -3
- package/build/security.js +293 -33
- package/build/server.d.ts +14 -0
- package/build/server.js +441 -354
- package/build/session/analytics.d.ts +1 -1
- package/build/session/analytics.js +5 -1
- package/build/session/db.js +23 -3
- package/build/session/extract.js +78 -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 +253 -250
- 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 +3 -4
- package/hooks/security.bundle.mjs +2 -1
- package/hooks/session-db.bundle.mjs +5 -5
- package/hooks/session-directive.mjs +88 -20
- package/hooks/session-extract.bundle.mjs +2 -2
- package/hooks/session-helpers.mjs +21 -0
- package/hooks/sessionstart.mjs +37 -5
- package/hooks/stop.mjs +49 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -10
- package/server.bundle.mjs +206 -200
- 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
|
@@ -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 (
|
|
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
|
package/build/adapters/detect.js
CHANGED
|
@@ -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),
|
|
173
|
-
//
|
|
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
|
|
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: {},
|