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.
Files changed (132) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.openclaw-plugin/openclaw.plugin.json +1 -1
  4. package/.openclaw-plugin/package.json +1 -1
  5. package/README.md +184 -60
  6. package/build/adapters/antigravity/index.d.ts +3 -5
  7. package/build/adapters/antigravity/index.js +7 -35
  8. package/build/adapters/base.d.ts +27 -0
  9. package/build/adapters/base.js +59 -0
  10. package/build/adapters/claude-code/index.d.ts +9 -25
  11. package/build/adapters/claude-code/index.js +27 -141
  12. package/build/adapters/claude-code-base.d.ts +49 -0
  13. package/build/adapters/claude-code-base.js +113 -0
  14. package/build/adapters/client-map.js +5 -0
  15. package/build/adapters/codex/hooks.d.ts +21 -14
  16. package/build/adapters/codex/hooks.js +22 -15
  17. package/build/adapters/codex/index.d.ts +6 -10
  18. package/build/adapters/codex/index.js +13 -43
  19. package/build/adapters/copilot-base.d.ts +78 -0
  20. package/build/adapters/copilot-base.js +281 -0
  21. package/build/adapters/cursor/index.d.ts +3 -5
  22. package/build/adapters/cursor/index.js +6 -34
  23. package/build/adapters/detect.d.ts +7 -0
  24. package/build/adapters/detect.js +57 -56
  25. package/build/adapters/gemini-cli/index.d.ts +3 -5
  26. package/build/adapters/gemini-cli/index.js +7 -35
  27. package/build/adapters/jetbrains-copilot/config.d.ts +8 -0
  28. package/build/adapters/jetbrains-copilot/config.js +8 -0
  29. package/build/adapters/jetbrains-copilot/hooks.d.ts +51 -0
  30. package/build/adapters/jetbrains-copilot/hooks.js +82 -0
  31. package/build/adapters/jetbrains-copilot/index.d.ts +24 -0
  32. package/build/adapters/jetbrains-copilot/index.js +119 -0
  33. package/build/adapters/kiro/hooks.d.ts +14 -0
  34. package/build/adapters/kiro/hooks.js +23 -0
  35. package/build/adapters/kiro/index.d.ts +3 -5
  36. package/build/adapters/kiro/index.js +10 -38
  37. package/build/adapters/openclaw/index.d.ts +3 -4
  38. package/build/adapters/openclaw/index.js +6 -22
  39. package/build/adapters/opencode/index.d.ts +2 -3
  40. package/build/adapters/opencode/index.js +5 -16
  41. package/build/adapters/qwen-code/index.d.ts +39 -0
  42. package/build/adapters/qwen-code/index.js +199 -0
  43. package/build/adapters/types.d.ts +1 -1
  44. package/build/adapters/vscode-copilot/index.d.ts +16 -46
  45. package/build/adapters/vscode-copilot/index.js +29 -320
  46. package/build/adapters/zed/index.d.ts +3 -5
  47. package/build/adapters/zed/index.js +7 -35
  48. package/build/cli.js +113 -47
  49. package/build/lifecycle.d.ts +23 -0
  50. package/build/lifecycle.js +54 -13
  51. package/build/opencode-plugin.d.ts +19 -7
  52. package/build/opencode-plugin.js +19 -7
  53. package/build/pi-extension.js +24 -7
  54. package/build/runtime.js +24 -9
  55. package/build/security.d.ts +17 -1
  56. package/build/security.js +40 -6
  57. package/build/server.js +129 -21
  58. package/build/session/analytics.d.ts +8 -7
  59. package/build/session/analytics.js +95 -75
  60. package/build/session/db.d.ts +10 -1
  61. package/build/session/db.js +67 -8
  62. package/build/session/extract.js +10 -2
  63. package/build/session/project-attribution.d.ts +73 -0
  64. package/build/session/project-attribution.js +231 -0
  65. package/build/store.d.ts +7 -0
  66. package/build/store.js +117 -18
  67. package/build/truncate.d.ts +6 -0
  68. package/build/truncate.js +51 -29
  69. package/build/types.d.ts +8 -0
  70. package/cli.bundle.mjs +157 -136
  71. package/configs/antigravity/GEMINI.md +31 -36
  72. package/configs/claude-code/CLAUDE.md +31 -37
  73. package/configs/codex/AGENTS.md +35 -49
  74. package/configs/cursor/context-mode.mdc +24 -25
  75. package/configs/gemini-cli/GEMINI.md +30 -36
  76. package/configs/jetbrains-copilot/copilot-instructions.md +59 -0
  77. package/configs/jetbrains-copilot/hooks.json +16 -0
  78. package/configs/jetbrains-copilot/mcp.json +8 -0
  79. package/configs/kilo/AGENTS.md +30 -36
  80. package/configs/kiro/KIRO.md +30 -36
  81. package/configs/kiro/agent.json +1 -1
  82. package/configs/openclaw/AGENTS.md +30 -36
  83. package/configs/opencode/AGENTS.md +30 -36
  84. package/configs/pi/AGENTS.md +31 -36
  85. package/configs/qwen-code/QWEN.md +63 -0
  86. package/configs/vscode-copilot/copilot-instructions.md +30 -36
  87. package/configs/zed/AGENTS.md +31 -36
  88. package/hooks/codex/posttooluse.mjs +7 -7
  89. package/hooks/codex/pretooluse.mjs +3 -3
  90. package/hooks/codex/sessionstart.mjs +2 -1
  91. package/hooks/core/formatters.mjs +24 -0
  92. package/hooks/core/routing.mjs +40 -15
  93. package/hooks/core/tool-naming.mjs +2 -0
  94. package/hooks/cursor/posttooluse.mjs +7 -7
  95. package/hooks/cursor/pretooluse.mjs +3 -3
  96. package/hooks/cursor/sessionstart.mjs +2 -1
  97. package/hooks/cursor/stop.mjs +2 -2
  98. package/hooks/ensure-deps.mjs +22 -10
  99. package/hooks/gemini-cli/aftertool.mjs +8 -8
  100. package/hooks/gemini-cli/beforetool.mjs +3 -2
  101. package/hooks/gemini-cli/precompress.mjs +2 -2
  102. package/hooks/gemini-cli/sessionstart.mjs +12 -4
  103. package/hooks/jetbrains-copilot/posttooluse.mjs +61 -0
  104. package/hooks/jetbrains-copilot/precompact.mjs +54 -0
  105. package/hooks/jetbrains-copilot/pretooluse.mjs +27 -0
  106. package/hooks/jetbrains-copilot/sessionstart.mjs +119 -0
  107. package/hooks/kiro/posttooluse.mjs +6 -7
  108. package/hooks/kiro/pretooluse.mjs +3 -2
  109. package/hooks/posttooluse.mjs +8 -8
  110. package/hooks/precompact.mjs +3 -4
  111. package/hooks/pretooluse.mjs +43 -20
  112. package/hooks/routing-block.mjs +35 -33
  113. package/hooks/session-attribution.bundle.mjs +1 -0
  114. package/hooks/session-db.bundle.mjs +27 -8
  115. package/hooks/session-extract.bundle.mjs +2 -1
  116. package/hooks/session-helpers.mjs +44 -3
  117. package/hooks/session-loaders.mjs +37 -0
  118. package/hooks/session-snapshot.bundle.mjs +14 -14
  119. package/hooks/sessionstart.mjs +5 -5
  120. package/hooks/userpromptsubmit.mjs +26 -9
  121. package/hooks/vscode-copilot/posttooluse.mjs +8 -8
  122. package/hooks/vscode-copilot/precompact.mjs +2 -2
  123. package/hooks/vscode-copilot/pretooluse.mjs +3 -2
  124. package/hooks/vscode-copilot/sessionstart.mjs +2 -2
  125. package/insight/server.mjs +262 -32
  126. package/insight/src/lib/api.ts +2 -1
  127. package/insight/src/routes/index.tsx +16 -3
  128. package/insight/src/routes/search.tsx +1 -1
  129. package/openclaw.plugin.json +1 -1
  130. package/package.json +11 -2
  131. package/server.bundle.mjs +117 -99
  132. 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
- * Implements HookAdapter for VS Code Copilot's JSON stdin/stdout hook paradigm.
5
- *
6
- * VS Code Copilot hook specifics:
7
- * - I/O: JSON on stdin, JSON on stdout (same paradigm as Claude Code)
8
- * - Hook names: PreToolUse, PostToolUse, PreCompact, SessionStart (PascalCase)
9
- * - Additional hooks: Stop, SubagentStart, SubagentStop (unique to VS Code)
10
- * - Arg modification: `updatedInput` in hookSpecificOutput wrapper (NOT flat)
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 { createHash } from "node:crypto";
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
- paradigm = "json-stdio";
38
- capabilities = {
39
- preToolUse: true,
40
- postToolUse: true,
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
- // ── Input parsing ──────────────────────────────────────
48
- parsePreToolUseInput(raw) {
49
- const input = raw;
50
- return {
51
- toolName: input.tool_name ?? "",
52
- toolInput: input.tool_input ?? {},
53
- sessionId: this.extractSessionId(input),
54
- projectDir: process.env.CLAUDE_PROJECT_DIR || process.cwd(),
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
- // ── Configuration ──────────────────────────────────────
168
- getSettingsPath() {
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 { createHash } from "node:crypto";
14
- import { readFileSync, writeFileSync, mkdirSync, copyFileSync, accessSync, constants, } from "node:fs";
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
- const { execSync, spawn } = await import("node:child_process");
364
- const { statSync, mkdirSync, cpSync } = await import("node:fs");
365
- const insightSource = resolve(getPluginRoot(), "insight");
366
- // Adapter-agnostic cache: use ~/.claude/context-mode/insight-cache as default
367
- // (matches server.ts pattern but CLI doesn't have adapter detection)
368
- const cacheDir = join(homedir(), ".claude", "context-mode", "insight-cache");
369
- if (!existsSync(join(insightSource, "server.mjs"))) {
370
- console.error("Error: Insight source not found. Try upgrading context-mode.");
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