context-mode 1.0.88 → 1.0.90
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/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/README.md +184 -60
- package/build/adapters/antigravity/index.d.ts +3 -5
- package/build/adapters/antigravity/index.js +7 -35
- package/build/adapters/base.d.ts +27 -0
- package/build/adapters/base.js +59 -0
- package/build/adapters/claude-code/index.d.ts +9 -25
- package/build/adapters/claude-code/index.js +27 -141
- package/build/adapters/claude-code-base.d.ts +49 -0
- package/build/adapters/claude-code-base.js +113 -0
- package/build/adapters/client-map.js +5 -0
- package/build/adapters/codex/hooks.d.ts +21 -14
- package/build/adapters/codex/hooks.js +22 -15
- package/build/adapters/codex/index.d.ts +6 -10
- package/build/adapters/codex/index.js +13 -43
- package/build/adapters/copilot-base.d.ts +78 -0
- package/build/adapters/copilot-base.js +281 -0
- package/build/adapters/cursor/index.d.ts +3 -5
- package/build/adapters/cursor/index.js +6 -34
- package/build/adapters/detect.d.ts +7 -0
- package/build/adapters/detect.js +57 -56
- package/build/adapters/gemini-cli/index.d.ts +3 -5
- package/build/adapters/gemini-cli/index.js +7 -35
- package/build/adapters/jetbrains-copilot/config.d.ts +8 -0
- package/build/adapters/jetbrains-copilot/config.js +8 -0
- package/build/adapters/jetbrains-copilot/hooks.d.ts +51 -0
- package/build/adapters/jetbrains-copilot/hooks.js +82 -0
- package/build/adapters/jetbrains-copilot/index.d.ts +24 -0
- package/build/adapters/jetbrains-copilot/index.js +119 -0
- package/build/adapters/kiro/hooks.d.ts +14 -0
- package/build/adapters/kiro/hooks.js +23 -0
- package/build/adapters/kiro/index.d.ts +3 -5
- package/build/adapters/kiro/index.js +10 -38
- package/build/adapters/openclaw/index.d.ts +3 -4
- package/build/adapters/openclaw/index.js +6 -22
- package/build/adapters/opencode/index.d.ts +2 -3
- package/build/adapters/opencode/index.js +5 -16
- package/build/adapters/qwen-code/index.d.ts +39 -0
- package/build/adapters/qwen-code/index.js +199 -0
- package/build/adapters/types.d.ts +1 -1
- package/build/adapters/vscode-copilot/index.d.ts +16 -46
- package/build/adapters/vscode-copilot/index.js +29 -320
- package/build/adapters/zed/index.d.ts +3 -5
- package/build/adapters/zed/index.js +7 -35
- package/build/cli.js +113 -47
- package/build/lifecycle.d.ts +23 -0
- package/build/lifecycle.js +54 -13
- package/build/opencode-plugin.d.ts +19 -7
- package/build/opencode-plugin.js +19 -7
- package/build/pi-extension.js +24 -7
- package/build/runtime.js +24 -9
- package/build/security.d.ts +17 -1
- package/build/security.js +40 -6
- package/build/server.js +129 -21
- package/build/session/analytics.d.ts +8 -7
- package/build/session/analytics.js +95 -75
- package/build/session/db.d.ts +10 -1
- package/build/session/db.js +67 -8
- package/build/session/extract.js +10 -2
- package/build/session/project-attribution.d.ts +73 -0
- package/build/session/project-attribution.js +231 -0
- package/build/store.d.ts +7 -0
- package/build/store.js +117 -18
- package/build/truncate.d.ts +6 -0
- package/build/truncate.js +51 -29
- package/build/types.d.ts +8 -0
- package/cli.bundle.mjs +157 -136
- package/configs/antigravity/GEMINI.md +31 -36
- package/configs/claude-code/CLAUDE.md +31 -37
- package/configs/codex/AGENTS.md +35 -49
- package/configs/cursor/context-mode.mdc +24 -25
- package/configs/gemini-cli/GEMINI.md +30 -36
- package/configs/jetbrains-copilot/copilot-instructions.md +59 -0
- package/configs/jetbrains-copilot/hooks.json +16 -0
- package/configs/jetbrains-copilot/mcp.json +8 -0
- package/configs/kilo/AGENTS.md +30 -36
- package/configs/kiro/KIRO.md +30 -36
- package/configs/kiro/agent.json +1 -1
- package/configs/openclaw/AGENTS.md +30 -36
- package/configs/opencode/AGENTS.md +30 -36
- package/configs/pi/AGENTS.md +31 -36
- package/configs/qwen-code/QWEN.md +63 -0
- package/configs/vscode-copilot/copilot-instructions.md +30 -36
- package/configs/zed/AGENTS.md +31 -36
- package/hooks/codex/posttooluse.mjs +7 -7
- package/hooks/codex/pretooluse.mjs +3 -3
- package/hooks/codex/sessionstart.mjs +2 -1
- package/hooks/core/formatters.mjs +24 -0
- package/hooks/core/routing.mjs +40 -15
- package/hooks/core/tool-naming.mjs +2 -0
- package/hooks/cursor/posttooluse.mjs +7 -7
- package/hooks/cursor/pretooluse.mjs +3 -3
- package/hooks/cursor/sessionstart.mjs +2 -1
- package/hooks/cursor/stop.mjs +2 -2
- package/hooks/ensure-deps.mjs +22 -10
- package/hooks/gemini-cli/aftertool.mjs +8 -8
- package/hooks/gemini-cli/beforetool.mjs +3 -2
- package/hooks/gemini-cli/precompress.mjs +2 -2
- package/hooks/gemini-cli/sessionstart.mjs +12 -4
- package/hooks/jetbrains-copilot/posttooluse.mjs +61 -0
- package/hooks/jetbrains-copilot/precompact.mjs +54 -0
- package/hooks/jetbrains-copilot/pretooluse.mjs +27 -0
- package/hooks/jetbrains-copilot/sessionstart.mjs +119 -0
- package/hooks/kiro/posttooluse.mjs +6 -7
- package/hooks/kiro/pretooluse.mjs +3 -2
- package/hooks/posttooluse.mjs +8 -8
- package/hooks/precompact.mjs +3 -4
- package/hooks/pretooluse.mjs +43 -20
- package/hooks/routing-block.mjs +35 -33
- package/hooks/session-attribution.bundle.mjs +1 -0
- package/hooks/session-db.bundle.mjs +27 -8
- package/hooks/session-extract.bundle.mjs +2 -1
- package/hooks/session-helpers.mjs +44 -3
- package/hooks/session-loaders.mjs +37 -0
- package/hooks/session-snapshot.bundle.mjs +14 -14
- package/hooks/sessionstart.mjs +5 -5
- package/hooks/userpromptsubmit.mjs +26 -9
- package/hooks/vscode-copilot/posttooluse.mjs +8 -8
- package/hooks/vscode-copilot/precompact.mjs +2 -2
- package/hooks/vscode-copilot/pretooluse.mjs +3 -2
- package/hooks/vscode-copilot/sessionstart.mjs +2 -2
- package/insight/server.mjs +262 -32
- package/insight/src/lib/api.ts +2 -1
- package/insight/src/routes/index.tsx +16 -3
- package/insight/src/routes/search.tsx +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +11 -2
- package/server.bundle.mjs +117 -99
- package/skills/ctx-insight/SKILL.md +1 -1
|
@@ -1,30 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* adapters/vscode-copilot — VS Code Copilot platform adapter.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* - Blocking: `permissionDecision: "deny"` (same as Claude Code)
|
|
12
|
-
* - Output modification: `additionalContext` in hookSpecificOutput,
|
|
13
|
-
* `decision: "block"` + reason
|
|
14
|
-
* - Tool input fields: tool_name, tool_input (snake_case, same as Claude Code)
|
|
15
|
-
* - But tool input PROPERTY names are camelCase (filePath not file_path)
|
|
16
|
-
* - Session ID: sessionId (camelCase, NOT session_id)
|
|
17
|
-
* - MCP tool prefix: f1e_ (not mcp__server__tool)
|
|
18
|
-
* - CRITICAL: matchers are parsed but IGNORED (all hooks fire on all tools)
|
|
19
|
-
* - Config: .github/hooks/*.json (primary), also reads .claude/settings.json
|
|
20
|
-
* - Env detection: VSCODE_PID, TERM_PROGRAM=vscode
|
|
21
|
-
* - Session dir: ~/.vscode/context-mode/sessions/ (fallback)
|
|
22
|
-
* - Preview status — API may change
|
|
4
|
+
* Extends CopilotBaseAdapter with VS Code-specific logic:
|
|
5
|
+
* - extractSessionId: VSCODE_PID fallback
|
|
6
|
+
* - getProjectDir: CLAUDE_PROJECT_DIR
|
|
7
|
+
* - getSessionDir: .github/ detection with ~/.vscode/ fallback
|
|
8
|
+
* - checkPluginRegistration: reads .vscode/mcp.json
|
|
9
|
+
* - getInstalledVersion: scans VS Code extensions dir
|
|
10
|
+
* - validateHooks: preview status + matcher warnings
|
|
23
11
|
*/
|
|
24
|
-
import {
|
|
25
|
-
import { readFileSync, writeFileSync, mkdirSync, copyFileSync, accessSync, existsSync, chmodSync, constants, } from "node:fs";
|
|
12
|
+
import { readFileSync, mkdirSync, accessSync, existsSync, constants, } from "node:fs";
|
|
26
13
|
import { resolve, join } from "node:path";
|
|
27
14
|
import { homedir } from "node:os";
|
|
15
|
+
import { CopilotBaseAdapter } from "../copilot-base.js";
|
|
28
16
|
// ─────────────────────────────────────────────────────────
|
|
29
17
|
// Hook constants (re-exported from hooks.ts)
|
|
30
18
|
// ─────────────────────────────────────────────────────────
|
|
@@ -32,143 +20,29 @@ import { HOOK_TYPES as VSCODE_HOOK_NAMES, HOOK_SCRIPTS as VSCODE_HOOK_SCRIPTS, b
|
|
|
32
20
|
// ─────────────────────────────────────────────────────────
|
|
33
21
|
// Adapter implementation
|
|
34
22
|
// ─────────────────────────────────────────────────────────
|
|
35
|
-
export class VSCodeCopilotAdapter {
|
|
23
|
+
export class VSCodeCopilotAdapter extends CopilotBaseAdapter {
|
|
24
|
+
constructor() {
|
|
25
|
+
// sessionDirSegments unused — vscode-copilot overrides getSessionDir()
|
|
26
|
+
// with .github directory detection fallback logic
|
|
27
|
+
super([".vscode"]);
|
|
28
|
+
}
|
|
36
29
|
name = "VS Code Copilot";
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
preCompact: true,
|
|
42
|
-
sessionStart: true,
|
|
43
|
-
canModifyArgs: true,
|
|
44
|
-
canModifyOutput: true,
|
|
45
|
-
canInjectSessionContext: true,
|
|
30
|
+
hookModule = {
|
|
31
|
+
HOOK_TYPES: VSCODE_HOOK_NAMES,
|
|
32
|
+
HOOK_SCRIPTS: VSCODE_HOOK_SCRIPTS,
|
|
33
|
+
buildHookCommand: buildVSCodeHookCommand,
|
|
46
34
|
};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
raw,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
parsePostToolUseInput(raw) {
|
|
59
|
-
const input = raw;
|
|
60
|
-
return {
|
|
61
|
-
toolName: input.tool_name ?? "",
|
|
62
|
-
toolInput: input.tool_input ?? {},
|
|
63
|
-
toolOutput: input.tool_output,
|
|
64
|
-
isError: input.is_error,
|
|
65
|
-
sessionId: this.extractSessionId(input),
|
|
66
|
-
projectDir: process.env.CLAUDE_PROJECT_DIR || process.cwd(),
|
|
67
|
-
raw,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
parsePreCompactInput(raw) {
|
|
71
|
-
const input = raw;
|
|
72
|
-
return {
|
|
73
|
-
sessionId: this.extractSessionId(input),
|
|
74
|
-
projectDir: process.env.CLAUDE_PROJECT_DIR || process.cwd(),
|
|
75
|
-
raw,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
parseSessionStartInput(raw) {
|
|
79
|
-
const input = raw;
|
|
80
|
-
const rawSource = input.source ?? "startup";
|
|
81
|
-
let source;
|
|
82
|
-
switch (rawSource) {
|
|
83
|
-
case "compact":
|
|
84
|
-
source = "compact";
|
|
85
|
-
break;
|
|
86
|
-
case "resume":
|
|
87
|
-
source = "resume";
|
|
88
|
-
break;
|
|
89
|
-
case "clear":
|
|
90
|
-
source = "clear";
|
|
91
|
-
break;
|
|
92
|
-
default:
|
|
93
|
-
source = "startup";
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
sessionId: this.extractSessionId(input),
|
|
97
|
-
source,
|
|
98
|
-
projectDir: process.env.CLAUDE_PROJECT_DIR || process.cwd(),
|
|
99
|
-
raw,
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
// ── Response formatting ────────────────────────────────
|
|
103
|
-
formatPreToolUseResponse(response) {
|
|
104
|
-
if (response.decision === "deny") {
|
|
105
|
-
return {
|
|
106
|
-
permissionDecision: "deny",
|
|
107
|
-
reason: response.reason ?? "Blocked by context-mode hook",
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
if (response.decision === "modify" && response.updatedInput) {
|
|
111
|
-
// VS Code Copilot: updatedInput is wrapped in hookSpecificOutput
|
|
112
|
-
return {
|
|
113
|
-
hookSpecificOutput: {
|
|
114
|
-
hookEventName: VSCODE_HOOK_NAMES.PRE_TOOL_USE,
|
|
115
|
-
updatedInput: response.updatedInput,
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
if (response.decision === "context" && response.additionalContext) {
|
|
120
|
-
// VS Code Copilot: inject additionalContext via hookSpecificOutput
|
|
121
|
-
return {
|
|
122
|
-
hookSpecificOutput: {
|
|
123
|
-
hookEventName: VSCODE_HOOK_NAMES.PRE_TOOL_USE,
|
|
124
|
-
additionalContext: response.additionalContext,
|
|
125
|
-
},
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
if (response.decision === "ask") {
|
|
129
|
-
// VS Code Copilot: use deny to force user attention (no native "ask")
|
|
130
|
-
return {
|
|
131
|
-
permissionDecision: "deny",
|
|
132
|
-
reason: response.reason ?? "Action requires user confirmation (security policy)",
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
// "allow" — return undefined for passthrough
|
|
136
|
-
return undefined;
|
|
137
|
-
}
|
|
138
|
-
formatPostToolUseResponse(response) {
|
|
139
|
-
if (response.updatedOutput) {
|
|
140
|
-
// VS Code Copilot: decision "block" + reason for output replacement
|
|
141
|
-
return {
|
|
142
|
-
hookSpecificOutput: {
|
|
143
|
-
hookEventName: VSCODE_HOOK_NAMES.POST_TOOL_USE,
|
|
144
|
-
decision: "block",
|
|
145
|
-
reason: response.updatedOutput,
|
|
146
|
-
},
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
if (response.additionalContext) {
|
|
150
|
-
return {
|
|
151
|
-
hookSpecificOutput: {
|
|
152
|
-
hookEventName: VSCODE_HOOK_NAMES.POST_TOOL_USE,
|
|
153
|
-
additionalContext: response.additionalContext,
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
return undefined;
|
|
158
|
-
}
|
|
159
|
-
formatPreCompactResponse(response) {
|
|
160
|
-
// VS Code Copilot: stdout content on exit 0 is injected as context
|
|
161
|
-
return response.context ?? "";
|
|
162
|
-
}
|
|
163
|
-
formatSessionStartResponse(response) {
|
|
164
|
-
// VS Code Copilot: stdout content is injected as additional context
|
|
165
|
-
return response.context ?? "";
|
|
35
|
+
hookSubdir = "vscode-copilot";
|
|
36
|
+
// ── Platform-specific overrides ────────────────────────
|
|
37
|
+
extractSessionId(input) {
|
|
38
|
+
if (input.sessionId)
|
|
39
|
+
return input.sessionId;
|
|
40
|
+
if (process.env.VSCODE_PID)
|
|
41
|
+
return `vscode-${process.env.VSCODE_PID}`;
|
|
42
|
+
return `pid-${process.ppid}`;
|
|
166
43
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// VS Code Copilot primarily uses .github/hooks/*.json
|
|
170
|
-
// but also reads .claude/settings.json
|
|
171
|
-
return resolve(".github", "hooks", "context-mode.json");
|
|
44
|
+
getProjectDir() {
|
|
45
|
+
return process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
172
46
|
}
|
|
173
47
|
getSessionDir() {
|
|
174
48
|
// Prefer .github/context-mode/sessions/ if .github exists,
|
|
@@ -179,91 +53,6 @@ export class VSCodeCopilotAdapter {
|
|
|
179
53
|
mkdirSync(dir, { recursive: true });
|
|
180
54
|
return dir;
|
|
181
55
|
}
|
|
182
|
-
getSessionDBPath(projectDir) {
|
|
183
|
-
const hash = createHash("sha256")
|
|
184
|
-
.update(projectDir)
|
|
185
|
-
.digest("hex")
|
|
186
|
-
.slice(0, 16);
|
|
187
|
-
return join(this.getSessionDir(), `${hash}.db`);
|
|
188
|
-
}
|
|
189
|
-
getSessionEventsPath(projectDir) {
|
|
190
|
-
const hash = createHash("sha256")
|
|
191
|
-
.update(projectDir)
|
|
192
|
-
.digest("hex")
|
|
193
|
-
.slice(0, 16);
|
|
194
|
-
return join(this.getSessionDir(), `${hash}-events.md`);
|
|
195
|
-
}
|
|
196
|
-
generateHookConfig(pluginRoot) {
|
|
197
|
-
return {
|
|
198
|
-
[VSCODE_HOOK_NAMES.PRE_TOOL_USE]: [
|
|
199
|
-
{
|
|
200
|
-
matcher: "",
|
|
201
|
-
hooks: [
|
|
202
|
-
{
|
|
203
|
-
type: "command",
|
|
204
|
-
command: buildVSCodeHookCommand(VSCODE_HOOK_NAMES.PRE_TOOL_USE, pluginRoot),
|
|
205
|
-
},
|
|
206
|
-
],
|
|
207
|
-
},
|
|
208
|
-
],
|
|
209
|
-
[VSCODE_HOOK_NAMES.POST_TOOL_USE]: [
|
|
210
|
-
{
|
|
211
|
-
matcher: "",
|
|
212
|
-
hooks: [
|
|
213
|
-
{
|
|
214
|
-
type: "command",
|
|
215
|
-
command: buildVSCodeHookCommand(VSCODE_HOOK_NAMES.POST_TOOL_USE, pluginRoot),
|
|
216
|
-
},
|
|
217
|
-
],
|
|
218
|
-
},
|
|
219
|
-
],
|
|
220
|
-
[VSCODE_HOOK_NAMES.PRE_COMPACT]: [
|
|
221
|
-
{
|
|
222
|
-
matcher: "",
|
|
223
|
-
hooks: [
|
|
224
|
-
{
|
|
225
|
-
type: "command",
|
|
226
|
-
command: buildVSCodeHookCommand(VSCODE_HOOK_NAMES.PRE_COMPACT, pluginRoot),
|
|
227
|
-
},
|
|
228
|
-
],
|
|
229
|
-
},
|
|
230
|
-
],
|
|
231
|
-
[VSCODE_HOOK_NAMES.SESSION_START]: [
|
|
232
|
-
{
|
|
233
|
-
matcher: "",
|
|
234
|
-
hooks: [
|
|
235
|
-
{
|
|
236
|
-
type: "command",
|
|
237
|
-
command: buildVSCodeHookCommand(VSCODE_HOOK_NAMES.SESSION_START, pluginRoot),
|
|
238
|
-
},
|
|
239
|
-
],
|
|
240
|
-
},
|
|
241
|
-
],
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
readSettings() {
|
|
245
|
-
// Try .github/hooks/context-mode.json first, then .claude/settings.json
|
|
246
|
-
const paths = [
|
|
247
|
-
this.getSettingsPath(),
|
|
248
|
-
resolve(".claude", "settings.json"),
|
|
249
|
-
];
|
|
250
|
-
for (const configPath of paths) {
|
|
251
|
-
try {
|
|
252
|
-
const raw = readFileSync(configPath, "utf-8");
|
|
253
|
-
return JSON.parse(raw);
|
|
254
|
-
}
|
|
255
|
-
catch {
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return null;
|
|
260
|
-
}
|
|
261
|
-
writeSettings(settings) {
|
|
262
|
-
const configPath = this.getSettingsPath();
|
|
263
|
-
const dir = resolve(".github", "hooks");
|
|
264
|
-
mkdirSync(dir, { recursive: true });
|
|
265
|
-
writeFileSync(configPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
266
|
-
}
|
|
267
56
|
// ── Diagnostics (doctor) ─────────────────────────────────
|
|
268
57
|
validateHooks(pluginRoot) {
|
|
269
58
|
const results = [];
|
|
@@ -397,84 +186,4 @@ export class VSCodeCopilotAdapter {
|
|
|
397
186
|
}
|
|
398
187
|
return "not installed";
|
|
399
188
|
}
|
|
400
|
-
// ── Upgrade ────────────────────────────────────────────
|
|
401
|
-
configureAllHooks(pluginRoot) {
|
|
402
|
-
const changes = [];
|
|
403
|
-
const hookConfig = { hooks: {} };
|
|
404
|
-
const hooks = hookConfig.hooks;
|
|
405
|
-
const hookTypes = [
|
|
406
|
-
VSCODE_HOOK_NAMES.PRE_TOOL_USE,
|
|
407
|
-
VSCODE_HOOK_NAMES.POST_TOOL_USE,
|
|
408
|
-
VSCODE_HOOK_NAMES.PRE_COMPACT,
|
|
409
|
-
VSCODE_HOOK_NAMES.SESSION_START,
|
|
410
|
-
];
|
|
411
|
-
for (const hookType of hookTypes) {
|
|
412
|
-
const script = VSCODE_HOOK_SCRIPTS[hookType];
|
|
413
|
-
if (!script)
|
|
414
|
-
continue;
|
|
415
|
-
hooks[hookType] = [
|
|
416
|
-
{
|
|
417
|
-
matcher: "",
|
|
418
|
-
hooks: [
|
|
419
|
-
{
|
|
420
|
-
type: "command",
|
|
421
|
-
command: buildVSCodeHookCommand(hookType, pluginRoot),
|
|
422
|
-
},
|
|
423
|
-
],
|
|
424
|
-
},
|
|
425
|
-
];
|
|
426
|
-
changes.push(`Configured ${hookType} hook`);
|
|
427
|
-
}
|
|
428
|
-
// Write to .github/hooks/context-mode.json
|
|
429
|
-
const outputDir = resolve(".github", "hooks");
|
|
430
|
-
mkdirSync(outputDir, { recursive: true });
|
|
431
|
-
const outputPath = resolve(outputDir, "context-mode.json");
|
|
432
|
-
writeFileSync(outputPath, JSON.stringify(hookConfig, null, 2) + "\n", "utf-8");
|
|
433
|
-
changes.push(`Wrote hook config to ${outputPath}`);
|
|
434
|
-
return changes;
|
|
435
|
-
}
|
|
436
|
-
backupSettings() {
|
|
437
|
-
const settingsPath = this.getSettingsPath();
|
|
438
|
-
try {
|
|
439
|
-
accessSync(settingsPath, constants.R_OK);
|
|
440
|
-
const backupPath = settingsPath + ".bak";
|
|
441
|
-
copyFileSync(settingsPath, backupPath);
|
|
442
|
-
return backupPath;
|
|
443
|
-
}
|
|
444
|
-
catch {
|
|
445
|
-
return null;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
setHookPermissions(pluginRoot) {
|
|
449
|
-
const set = [];
|
|
450
|
-
const hooksDir = join(pluginRoot, "hooks", "vscode-copilot");
|
|
451
|
-
for (const scriptName of Object.values(VSCODE_HOOK_SCRIPTS)) {
|
|
452
|
-
const scriptPath = resolve(hooksDir, scriptName);
|
|
453
|
-
try {
|
|
454
|
-
accessSync(scriptPath, constants.R_OK);
|
|
455
|
-
chmodSync(scriptPath, 0o755);
|
|
456
|
-
set.push(scriptPath);
|
|
457
|
-
}
|
|
458
|
-
catch {
|
|
459
|
-
/* skip missing scripts */
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
return set;
|
|
463
|
-
}
|
|
464
|
-
updatePluginRegistry(_pluginRoot, _version) {
|
|
465
|
-
// VS Code manages extensions through its own marketplace/extension system.
|
|
466
|
-
// No manual registry update needed.
|
|
467
|
-
}
|
|
468
|
-
// ── Internal helpers ───────────────────────────────────
|
|
469
|
-
/**
|
|
470
|
-
* Extract session ID from VS Code Copilot hook input.
|
|
471
|
-
* VS Code Copilot uses camelCase sessionId (NOT session_id).
|
|
472
|
-
*/
|
|
473
|
-
extractSessionId(input) {
|
|
474
|
-
if (input.sessionId)
|
|
475
|
-
return input.sessionId;
|
|
476
|
-
if (process.env.VSCODE_PID)
|
|
477
|
-
return `vscode-${process.env.VSCODE_PID}`;
|
|
478
|
-
return `pid-${process.ppid}`;
|
|
479
|
-
}
|
|
480
189
|
}
|
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
* - All capabilities are false — MCP is the only integration path
|
|
11
11
|
* - Session dir: ~/.config/zed/context-mode/sessions/
|
|
12
12
|
*/
|
|
13
|
+
import { BaseAdapter } from "../base.js";
|
|
13
14
|
import type { HookAdapter, HookParadigm, PlatformCapabilities, DiagnosticResult, PreToolUseEvent, PostToolUseEvent, PreCompactEvent, SessionStartEvent, PreToolUseResponse, PostToolUseResponse, PreCompactResponse, SessionStartResponse, HookRegistration } from "../types.js";
|
|
14
|
-
export declare class ZedAdapter implements HookAdapter {
|
|
15
|
+
export declare class ZedAdapter extends BaseAdapter implements HookAdapter {
|
|
16
|
+
constructor();
|
|
15
17
|
readonly name = "Zed";
|
|
16
18
|
readonly paradigm: HookParadigm;
|
|
17
19
|
readonly capabilities: PlatformCapabilities;
|
|
@@ -24,9 +26,6 @@ export declare class ZedAdapter implements HookAdapter {
|
|
|
24
26
|
formatPreCompactResponse(_response: PreCompactResponse): unknown;
|
|
25
27
|
formatSessionStartResponse(_response: SessionStartResponse): unknown;
|
|
26
28
|
getSettingsPath(): string;
|
|
27
|
-
getSessionDir(): string;
|
|
28
|
-
getSessionDBPath(projectDir: string): string;
|
|
29
|
-
getSessionEventsPath(projectDir: string): string;
|
|
30
29
|
generateHookConfig(_pluginRoot: string): HookRegistration;
|
|
31
30
|
readSettings(): Record<string, unknown> | null;
|
|
32
31
|
writeSettings(settings: Record<string, unknown>): void;
|
|
@@ -34,7 +33,6 @@ export declare class ZedAdapter implements HookAdapter {
|
|
|
34
33
|
checkPluginRegistration(): DiagnosticResult;
|
|
35
34
|
getInstalledVersion(): string;
|
|
36
35
|
configureAllHooks(_pluginRoot: string): string[];
|
|
37
|
-
backupSettings(): string | null;
|
|
38
36
|
setHookPermissions(_pluginRoot: string): string[];
|
|
39
37
|
updatePluginRegistry(_pluginRoot: string, _version: string): void;
|
|
40
38
|
getRoutingInstructions(): string;
|
|
@@ -10,15 +10,18 @@
|
|
|
10
10
|
* - All capabilities are false — MCP is the only integration path
|
|
11
11
|
* - Session dir: ~/.config/zed/context-mode/sessions/
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { resolve, join, dirname } from "node:path";
|
|
13
|
+
import { readFileSync, writeFileSync, mkdirSync, } from "node:fs";
|
|
14
|
+
import { resolve, dirname } from "node:path";
|
|
16
15
|
import { fileURLToPath } from "node:url";
|
|
17
16
|
import { homedir } from "node:os";
|
|
17
|
+
import { BaseAdapter } from "../base.js";
|
|
18
18
|
// ─────────────────────────────────────────────────────────
|
|
19
19
|
// Adapter implementation
|
|
20
20
|
// ─────────────────────────────────────────────────────────
|
|
21
|
-
export class ZedAdapter {
|
|
21
|
+
export class ZedAdapter extends BaseAdapter {
|
|
22
|
+
constructor() {
|
|
23
|
+
super([".config", "zed"]);
|
|
24
|
+
}
|
|
22
25
|
name = "Zed";
|
|
23
26
|
paradigm = "mcp-only";
|
|
24
27
|
capabilities = {
|
|
@@ -63,25 +66,6 @@ export class ZedAdapter {
|
|
|
63
66
|
getSettingsPath() {
|
|
64
67
|
return resolve(homedir(), ".config", "zed", "settings.json");
|
|
65
68
|
}
|
|
66
|
-
getSessionDir() {
|
|
67
|
-
const dir = join(homedir(), ".config", "zed", "context-mode", "sessions");
|
|
68
|
-
mkdirSync(dir, { recursive: true });
|
|
69
|
-
return dir;
|
|
70
|
-
}
|
|
71
|
-
getSessionDBPath(projectDir) {
|
|
72
|
-
const hash = createHash("sha256")
|
|
73
|
-
.update(projectDir)
|
|
74
|
-
.digest("hex")
|
|
75
|
-
.slice(0, 16);
|
|
76
|
-
return join(this.getSessionDir(), `${hash}.db`);
|
|
77
|
-
}
|
|
78
|
-
getSessionEventsPath(projectDir) {
|
|
79
|
-
const hash = createHash("sha256")
|
|
80
|
-
.update(projectDir)
|
|
81
|
-
.digest("hex")
|
|
82
|
-
.slice(0, 16);
|
|
83
|
-
return join(this.getSessionDir(), `${hash}-events.md`);
|
|
84
|
-
}
|
|
85
69
|
generateHookConfig(_pluginRoot) {
|
|
86
70
|
// Zed does not support hooks — return empty registration
|
|
87
71
|
return {};
|
|
@@ -156,18 +140,6 @@ export class ZedAdapter {
|
|
|
156
140
|
// Zed does not support hooks — nothing to configure
|
|
157
141
|
return [];
|
|
158
142
|
}
|
|
159
|
-
backupSettings() {
|
|
160
|
-
const settingsPath = this.getSettingsPath();
|
|
161
|
-
try {
|
|
162
|
-
accessSync(settingsPath, constants.R_OK);
|
|
163
|
-
const backupPath = settingsPath + ".bak";
|
|
164
|
-
copyFileSync(settingsPath, backupPath);
|
|
165
|
-
return backupPath;
|
|
166
|
-
}
|
|
167
|
-
catch {
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
143
|
setHookPermissions(_pluginRoot) {
|
|
172
144
|
// No hook scripts for Zed
|
|
173
145
|
return [];
|
package/build/cli.js
CHANGED
|
@@ -60,6 +60,19 @@ const HOOK_MAP = {
|
|
|
60
60
|
pretooluse: "hooks/kiro/pretooluse.mjs",
|
|
61
61
|
posttooluse: "hooks/kiro/posttooluse.mjs",
|
|
62
62
|
},
|
|
63
|
+
"jetbrains-copilot": {
|
|
64
|
+
pretooluse: "hooks/jetbrains-copilot/pretooluse.mjs",
|
|
65
|
+
posttooluse: "hooks/jetbrains-copilot/posttooluse.mjs",
|
|
66
|
+
precompact: "hooks/jetbrains-copilot/precompact.mjs",
|
|
67
|
+
sessionstart: "hooks/jetbrains-copilot/sessionstart.mjs",
|
|
68
|
+
},
|
|
69
|
+
"qwen-code": {
|
|
70
|
+
pretooluse: "hooks/pretooluse.mjs",
|
|
71
|
+
posttooluse: "hooks/posttooluse.mjs",
|
|
72
|
+
precompact: "hooks/precompact.mjs",
|
|
73
|
+
sessionstart: "hooks/sessionstart.mjs",
|
|
74
|
+
userpromptsubmit: "hooks/userpromptsubmit.mjs",
|
|
75
|
+
},
|
|
63
76
|
};
|
|
64
77
|
async function hookDispatch(platform, event) {
|
|
65
78
|
// Suppress stderr at OS fd level — native C++ modules (better-sqlite3) write
|
|
@@ -360,55 +373,108 @@ async function doctor() {
|
|
|
360
373
|
* Insight — analytics dashboard
|
|
361
374
|
* ------------------------------------------------------- */
|
|
362
375
|
async function insight(port) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
376
|
+
try {
|
|
377
|
+
const { execSync, spawn } = await import("node:child_process");
|
|
378
|
+
const { statSync, mkdirSync, cpSync } = await import("node:fs");
|
|
379
|
+
const insightSource = resolve(getPluginRoot(), "insight");
|
|
380
|
+
// Detect platform + adapter for correct session/content paths
|
|
381
|
+
const detection = detectPlatform();
|
|
382
|
+
const adapter = await getAdapter(detection.platform);
|
|
383
|
+
const sessDir = adapter.getSessionDir();
|
|
384
|
+
const contentDir = join(dirname(sessDir), "content");
|
|
385
|
+
const cacheDir = join(dirname(sessDir), "insight-cache");
|
|
386
|
+
if (!existsSync(join(insightSource, "server.mjs"))) {
|
|
387
|
+
console.error("Error: Insight source not found. Try upgrading context-mode.");
|
|
388
|
+
process.exit(1);
|
|
389
|
+
}
|
|
390
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
391
|
+
// Copy source if newer
|
|
392
|
+
const srcMtime = statSync(join(insightSource, "server.mjs")).mtimeMs;
|
|
393
|
+
const cacheMtime = existsSync(join(cacheDir, "server.mjs"))
|
|
394
|
+
? statSync(join(cacheDir, "server.mjs")).mtimeMs : 0;
|
|
395
|
+
if (srcMtime > cacheMtime) {
|
|
396
|
+
console.log("Copying Insight source...");
|
|
397
|
+
cpSync(insightSource, cacheDir, { recursive: true, force: true });
|
|
398
|
+
}
|
|
399
|
+
// Install deps
|
|
400
|
+
if (!existsSync(join(cacheDir, "node_modules"))) {
|
|
401
|
+
console.log("Installing dependencies (first run)...");
|
|
402
|
+
try {
|
|
403
|
+
execSync("npm install --production=false", { cwd: cacheDir, stdio: "inherit", timeout: 300000 });
|
|
404
|
+
}
|
|
405
|
+
catch {
|
|
406
|
+
// Clean up partial install so next run retries fresh
|
|
407
|
+
try {
|
|
408
|
+
rmSync(join(cacheDir, "node_modules"), { recursive: true, force: true });
|
|
409
|
+
}
|
|
410
|
+
catch { }
|
|
411
|
+
throw new Error("npm install failed — please retry");
|
|
412
|
+
}
|
|
413
|
+
// Sentinel check: verify install completed (cold cache can timeout leaving partial node_modules)
|
|
414
|
+
if (!existsSync(join(cacheDir, "node_modules", "vite")) || !existsSync(join(cacheDir, "node_modules", "better-sqlite3"))) {
|
|
415
|
+
rmSync(join(cacheDir, "node_modules"), { recursive: true, force: true });
|
|
416
|
+
throw new Error("npm install incomplete — please retry");
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
// Build
|
|
420
|
+
console.log("Building dashboard...");
|
|
421
|
+
execSync("npx vite build", { cwd: cacheDir, stdio: "pipe", timeout: 60000 });
|
|
422
|
+
// Start server
|
|
423
|
+
const url = `http://localhost:${port}`;
|
|
424
|
+
console.log(`\n context-mode Insight\n ${url}\n`);
|
|
425
|
+
const child = spawn("node", [join(cacheDir, "server.mjs")], {
|
|
426
|
+
cwd: cacheDir,
|
|
427
|
+
env: {
|
|
428
|
+
...process.env,
|
|
429
|
+
PORT: String(port),
|
|
430
|
+
INSIGHT_SESSION_DIR: sessDir,
|
|
431
|
+
INSIGHT_CONTENT_DIR: contentDir,
|
|
432
|
+
},
|
|
433
|
+
stdio: "inherit",
|
|
434
|
+
});
|
|
435
|
+
child.on("error", () => { }); // prevent unhandled error crash
|
|
436
|
+
// Wait for server to be ready, then verify it started
|
|
437
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
438
|
+
try {
|
|
439
|
+
const { request } = await import("node:http");
|
|
440
|
+
await new Promise((resolve, reject) => {
|
|
441
|
+
const req = request(`http://127.0.0.1:${port}/api/overview`, { timeout: 3000 }, (res) => {
|
|
442
|
+
resolve();
|
|
443
|
+
res.resume();
|
|
444
|
+
});
|
|
445
|
+
req.on("error", reject);
|
|
446
|
+
req.on("timeout", () => { req.destroy(); reject(new Error("timeout")); });
|
|
447
|
+
req.end();
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
catch {
|
|
451
|
+
console.error(`\nError: Port ${port} appears to be in use. Either a previous dashboard is still running, or another service is using this port.`);
|
|
452
|
+
console.error(`\nTo fix:`);
|
|
453
|
+
console.error(` Kill the existing process: ${process.platform === "win32" ? `netstat -ano | findstr :${port}` : `lsof -ti:${port} | xargs kill`}`);
|
|
454
|
+
console.error(` Or use a different port: context-mode insight ${port + 1}`);
|
|
455
|
+
child.kill();
|
|
456
|
+
process.exit(1);
|
|
457
|
+
}
|
|
458
|
+
// Open browser
|
|
459
|
+
const platform = process.platform;
|
|
460
|
+
try {
|
|
461
|
+
if (platform === "darwin")
|
|
462
|
+
execSync(`open "${url}"`, { stdio: "pipe" });
|
|
463
|
+
else if (platform === "win32")
|
|
464
|
+
execSync(`start "" "${url}"`, { stdio: "pipe" });
|
|
465
|
+
else
|
|
466
|
+
execSync(`xdg-open "${url}" 2>/dev/null || sensible-browser "${url}" 2>/dev/null`, { stdio: "pipe" });
|
|
467
|
+
}
|
|
468
|
+
catch { /* best effort */ }
|
|
469
|
+
// Keep alive until Ctrl+C
|
|
470
|
+
process.on("SIGINT", () => { child.kill(); process.exit(0); });
|
|
471
|
+
process.on("SIGTERM", () => { child.kill(); process.exit(0); });
|
|
472
|
+
}
|
|
473
|
+
catch (err) {
|
|
474
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
475
|
+
console.error(`\nInsight error: ${msg}`);
|
|
371
476
|
process.exit(1);
|
|
372
477
|
}
|
|
373
|
-
mkdirSync(cacheDir, { recursive: true });
|
|
374
|
-
// Copy source if newer
|
|
375
|
-
const srcMtime = statSync(join(insightSource, "server.mjs")).mtimeMs;
|
|
376
|
-
const cacheMtime = existsSync(join(cacheDir, "server.mjs"))
|
|
377
|
-
? statSync(join(cacheDir, "server.mjs")).mtimeMs : 0;
|
|
378
|
-
if (srcMtime > cacheMtime) {
|
|
379
|
-
console.log("Copying Insight source...");
|
|
380
|
-
cpSync(insightSource, cacheDir, { recursive: true, force: true });
|
|
381
|
-
}
|
|
382
|
-
// Install deps
|
|
383
|
-
if (!existsSync(join(cacheDir, "node_modules"))) {
|
|
384
|
-
console.log("Installing dependencies (first run)...");
|
|
385
|
-
execSync("npm install --production=false", { cwd: cacheDir, stdio: "inherit", timeout: 120000 });
|
|
386
|
-
}
|
|
387
|
-
// Build
|
|
388
|
-
console.log("Building dashboard...");
|
|
389
|
-
execSync("npx vite build", { cwd: cacheDir, stdio: "pipe", timeout: 30000 });
|
|
390
|
-
// Start server
|
|
391
|
-
const url = `http://localhost:${port}`;
|
|
392
|
-
console.log(`\n context-mode Insight\n ${url}\n`);
|
|
393
|
-
const child = spawn("node", [join(cacheDir, "server.mjs")], {
|
|
394
|
-
cwd: cacheDir,
|
|
395
|
-
env: { ...process.env, PORT: String(port) },
|
|
396
|
-
stdio: "inherit",
|
|
397
|
-
});
|
|
398
|
-
// Open browser
|
|
399
|
-
const platform = process.platform;
|
|
400
|
-
try {
|
|
401
|
-
if (platform === "darwin")
|
|
402
|
-
execSync(`open "${url}"`, { stdio: "pipe" });
|
|
403
|
-
else if (platform === "win32")
|
|
404
|
-
execSync(`start "" "${url}"`, { stdio: "pipe" });
|
|
405
|
-
else
|
|
406
|
-
execSync(`xdg-open "${url}" 2>/dev/null || sensible-browser "${url}" 2>/dev/null`, { stdio: "pipe" });
|
|
407
|
-
}
|
|
408
|
-
catch { /* best effort */ }
|
|
409
|
-
// Keep alive until Ctrl+C
|
|
410
|
-
process.on("SIGINT", () => { child.kill(); process.exit(0); });
|
|
411
|
-
process.on("SIGTERM", () => { child.kill(); process.exit(0); });
|
|
412
478
|
}
|
|
413
479
|
/* -------------------------------------------------------
|
|
414
480
|
* Upgrade — adapter-aware hook configuration
|