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
|
@@ -17,16 +17,19 @@
|
|
|
17
17
|
* - clientInfo.name: https://github.com/kirodotdev/Kiro/issues/5205 ("Kiro CLI")
|
|
18
18
|
* - CLI hooks: https://kiro.dev/docs/cli/custom-agents/configuration-reference#hooks-field
|
|
19
19
|
*/
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import { resolve, join, dirname } from "node:path";
|
|
20
|
+
import { readFileSync, writeFileSync, mkdirSync, } from "node:fs";
|
|
21
|
+
import { resolve, dirname } from "node:path";
|
|
23
22
|
import { fileURLToPath } from "node:url";
|
|
24
23
|
import { homedir } from "node:os";
|
|
25
|
-
import {
|
|
24
|
+
import { BaseAdapter } from "../base.js";
|
|
25
|
+
import { HOOK_TYPES as KIRO_HOOK_TYPES, PRE_TOOL_USE_MATCHER_PATTERN as KIRO_PRE_TOOL_USE_MATCHER_PATTERN, buildHookCommand as buildKiroHookCommand, isContextModeHook as isKiroContextModeHook, } from "./hooks.js";
|
|
26
26
|
// ─────────────────────────────────────────────────────────
|
|
27
27
|
// Adapter implementation
|
|
28
28
|
// ─────────────────────────────────────────────────────────
|
|
29
|
-
export class KiroAdapter {
|
|
29
|
+
export class KiroAdapter extends BaseAdapter {
|
|
30
|
+
constructor() {
|
|
31
|
+
super([".kiro"]);
|
|
32
|
+
}
|
|
30
33
|
name = "Kiro";
|
|
31
34
|
paradigm = "json-stdio";
|
|
32
35
|
capabilities = {
|
|
@@ -95,31 +98,12 @@ export class KiroAdapter {
|
|
|
95
98
|
getSettingsPath() {
|
|
96
99
|
return resolve(homedir(), ".kiro", "settings", "mcp.json");
|
|
97
100
|
}
|
|
98
|
-
getSessionDir() {
|
|
99
|
-
const dir = join(homedir(), ".kiro", "context-mode", "sessions");
|
|
100
|
-
mkdirSync(dir, { recursive: true });
|
|
101
|
-
return dir;
|
|
102
|
-
}
|
|
103
|
-
getSessionDBPath(projectDir) {
|
|
104
|
-
const hash = createHash("sha256")
|
|
105
|
-
.update(projectDir)
|
|
106
|
-
.digest("hex")
|
|
107
|
-
.slice(0, 16);
|
|
108
|
-
return join(this.getSessionDir(), `${hash}.db`);
|
|
109
|
-
}
|
|
110
|
-
getSessionEventsPath(projectDir) {
|
|
111
|
-
const hash = createHash("sha256")
|
|
112
|
-
.update(projectDir)
|
|
113
|
-
.digest("hex")
|
|
114
|
-
.slice(0, 16);
|
|
115
|
-
return join(this.getSessionDir(), `${hash}-events.md`);
|
|
116
|
-
}
|
|
117
101
|
generateHookConfig(pluginRoot) {
|
|
118
102
|
// Kiro CLI hook config format: { preToolUse: [{ matcher, command }] }
|
|
119
103
|
// Note: This generates the entries for agent config files
|
|
120
104
|
return {
|
|
121
105
|
[KIRO_HOOK_TYPES.PRE_TOOL_USE]: [{
|
|
122
|
-
matcher:
|
|
106
|
+
matcher: KIRO_PRE_TOOL_USE_MATCHER_PATTERN,
|
|
123
107
|
hooks: [{ type: "command", command: buildKiroHookCommand(KIRO_HOOK_TYPES.PRE_TOOL_USE, pluginRoot) }],
|
|
124
108
|
}],
|
|
125
109
|
[KIRO_HOOK_TYPES.POST_TOOL_USE]: [{
|
|
@@ -241,7 +225,7 @@ export class KiroAdapter {
|
|
|
241
225
|
const preToolUseEntries = (hooks[KIRO_HOOK_TYPES.PRE_TOOL_USE] ?? []);
|
|
242
226
|
if (!preToolUseEntries.some(e => isKiroContextModeHook(e, KIRO_HOOK_TYPES.PRE_TOOL_USE))) {
|
|
243
227
|
preToolUseEntries.push({
|
|
244
|
-
matcher:
|
|
228
|
+
matcher: KIRO_PRE_TOOL_USE_MATCHER_PATTERN,
|
|
245
229
|
command: buildKiroHookCommand(KIRO_HOOK_TYPES.PRE_TOOL_USE, pluginRoot),
|
|
246
230
|
});
|
|
247
231
|
hooks[KIRO_HOOK_TYPES.PRE_TOOL_USE] = preToolUseEntries;
|
|
@@ -265,18 +249,6 @@ export class KiroAdapter {
|
|
|
265
249
|
}
|
|
266
250
|
return changes;
|
|
267
251
|
}
|
|
268
|
-
backupSettings() {
|
|
269
|
-
const settingsPath = this.getSettingsPath();
|
|
270
|
-
try {
|
|
271
|
-
accessSync(settingsPath, constants.R_OK);
|
|
272
|
-
const backupPath = settingsPath + ".bak";
|
|
273
|
-
copyFileSync(settingsPath, backupPath);
|
|
274
|
-
return backupPath;
|
|
275
|
-
}
|
|
276
|
-
catch {
|
|
277
|
-
return null;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
252
|
setHookPermissions(_pluginRoot) {
|
|
281
253
|
return [];
|
|
282
254
|
}
|
|
@@ -15,8 +15,10 @@
|
|
|
15
15
|
* - Config: openclaw.json plugins.entries, ~/.openclaw/extensions/
|
|
16
16
|
* - Session dir: ~/.openclaw/context-mode/sessions/
|
|
17
17
|
*/
|
|
18
|
+
import { BaseAdapter } from "../base.js";
|
|
18
19
|
import type { HookAdapter, HookParadigm, PlatformCapabilities, DiagnosticResult, PreToolUseEvent, PostToolUseEvent, PreCompactEvent, SessionStartEvent, PreToolUseResponse, PostToolUseResponse, PreCompactResponse, SessionStartResponse, HookRegistration } from "../types.js";
|
|
19
|
-
export declare class OpenClawAdapter implements HookAdapter {
|
|
20
|
+
export declare class OpenClawAdapter extends BaseAdapter implements HookAdapter {
|
|
21
|
+
constructor();
|
|
20
22
|
readonly name = "OpenClaw";
|
|
21
23
|
readonly paradigm: HookParadigm;
|
|
22
24
|
readonly capabilities: PlatformCapabilities;
|
|
@@ -29,9 +31,6 @@ export declare class OpenClawAdapter implements HookAdapter {
|
|
|
29
31
|
formatPreCompactResponse(response: PreCompactResponse): unknown;
|
|
30
32
|
formatSessionStartResponse(response: SessionStartResponse): unknown;
|
|
31
33
|
getSettingsPath(): string;
|
|
32
|
-
getSessionDir(): string;
|
|
33
|
-
getSessionDBPath(projectDir: string): string;
|
|
34
|
-
getSessionEventsPath(projectDir: string): string;
|
|
35
34
|
generateHookConfig(_pluginRoot: string): HookRegistration;
|
|
36
35
|
readSettings(): Record<string, unknown> | null;
|
|
37
36
|
writeSettings(settings: Record<string, unknown>): void;
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
* - Config: openclaw.json plugins.entries, ~/.openclaw/extensions/
|
|
16
16
|
* - Session dir: ~/.openclaw/context-mode/sessions/
|
|
17
17
|
*/
|
|
18
|
-
import {
|
|
19
|
-
import { readFileSync, writeFileSync, mkdirSync, copyFileSync, accessSync, constants, } from "node:fs";
|
|
18
|
+
import { readFileSync, writeFileSync, copyFileSync, accessSync, constants, } from "node:fs";
|
|
20
19
|
import { resolve, join } from "node:path";
|
|
21
20
|
import { homedir } from "node:os";
|
|
21
|
+
import { BaseAdapter } from "../base.js";
|
|
22
22
|
// ─────────────────────────────────────────────────────────
|
|
23
23
|
// Hook constants (re-exported from hooks.ts)
|
|
24
24
|
// ─────────────────────────────────────────────────────────
|
|
@@ -26,7 +26,10 @@ import { HOOK_EVENTS as OPENCLAW_HOOK_EVENTS } from "./hooks.js";
|
|
|
26
26
|
// ─────────────────────────────────────────────────────────
|
|
27
27
|
// Adapter implementation
|
|
28
28
|
// ─────────────────────────────────────────────────────────
|
|
29
|
-
export class OpenClawAdapter {
|
|
29
|
+
export class OpenClawAdapter extends BaseAdapter {
|
|
30
|
+
constructor() {
|
|
31
|
+
super([".openclaw"]);
|
|
32
|
+
}
|
|
30
33
|
name = "OpenClaw";
|
|
31
34
|
paradigm = "ts-plugin";
|
|
32
35
|
capabilities = {
|
|
@@ -140,25 +143,6 @@ export class OpenClawAdapter {
|
|
|
140
143
|
// OpenClaw uses openclaw.json in the project root or ~/.openclaw/openclaw.json
|
|
141
144
|
return resolve("openclaw.json");
|
|
142
145
|
}
|
|
143
|
-
getSessionDir() {
|
|
144
|
-
const dir = join(homedir(), ".openclaw", "context-mode", "sessions");
|
|
145
|
-
mkdirSync(dir, { recursive: true });
|
|
146
|
-
return dir;
|
|
147
|
-
}
|
|
148
|
-
getSessionDBPath(projectDir) {
|
|
149
|
-
const hash = createHash("sha256")
|
|
150
|
-
.update(projectDir)
|
|
151
|
-
.digest("hex")
|
|
152
|
-
.slice(0, 16);
|
|
153
|
-
return join(this.getSessionDir(), `${hash}.db`);
|
|
154
|
-
}
|
|
155
|
-
getSessionEventsPath(projectDir) {
|
|
156
|
-
const hash = createHash("sha256")
|
|
157
|
-
.update(projectDir)
|
|
158
|
-
.digest("hex")
|
|
159
|
-
.slice(0, 16);
|
|
160
|
-
return join(this.getSessionDir(), `${hash}-events.md`);
|
|
161
|
-
}
|
|
162
146
|
generateHookConfig(_pluginRoot) {
|
|
163
147
|
// OpenClaw uses TS plugin paradigm — hooks are registered via
|
|
164
148
|
// api.registerHook() in the plugin entry point, not via config files.
|
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
* - Config: opencode.json plugin array, .opencode/plugins/*.ts
|
|
16
16
|
* - Session dir: ~/.config/opencode/context-mode/sessions/
|
|
17
17
|
*/
|
|
18
|
+
import { BaseAdapter } from "../base.js";
|
|
18
19
|
import type { HookAdapter, HookParadigm, PlatformCapabilities, DiagnosticResult, PreToolUseEvent, PostToolUseEvent, PreCompactEvent, SessionStartEvent, PreToolUseResponse, PostToolUseResponse, PreCompactResponse, SessionStartResponse, HookRegistration, PlatformId } from "../types.js";
|
|
19
20
|
export type AdapterPlatformType = Extract<PlatformId, "opencode" | "kilo">;
|
|
20
|
-
export declare class OpenCodeAdapter implements HookAdapter {
|
|
21
|
+
export declare class OpenCodeAdapter extends BaseAdapter implements HookAdapter {
|
|
21
22
|
get name(): string;
|
|
22
23
|
readonly paradigm: HookParadigm;
|
|
23
24
|
private settingsPath?;
|
|
@@ -35,8 +36,6 @@ export declare class OpenCodeAdapter implements HookAdapter {
|
|
|
35
36
|
getSettingsPath(): string;
|
|
36
37
|
private paths;
|
|
37
38
|
getSessionDir(): string;
|
|
38
|
-
getSessionDBPath(projectDir: string): string;
|
|
39
|
-
getSessionEventsPath(projectDir: string): string;
|
|
40
39
|
generateHookConfig(_pluginRoot: string): HookRegistration;
|
|
41
40
|
readSettings(): Record<string, unknown> | null;
|
|
42
41
|
writeSettings(settings: Record<string, unknown>): void;
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
* - Config: opencode.json plugin array, .opencode/plugins/*.ts
|
|
16
16
|
* - Session dir: ~/.config/opencode/context-mode/sessions/
|
|
17
17
|
*/
|
|
18
|
-
import { createHash } from "node:crypto";
|
|
19
18
|
/** Strip JSONC comments (// and /* */) and trailing commas for JSON.parse. */
|
|
20
19
|
function stripJsonComments(str) {
|
|
21
20
|
return str
|
|
@@ -26,11 +25,12 @@ function stripJsonComments(str) {
|
|
|
26
25
|
import { readFileSync, writeFileSync, mkdirSync, copyFileSync, accessSync, constants, } from "node:fs";
|
|
27
26
|
import { resolve, join } from "node:path";
|
|
28
27
|
import { homedir } from "node:os";
|
|
28
|
+
import { BaseAdapter } from "../base.js";
|
|
29
29
|
// ─────────────────────────────────────────────────────────
|
|
30
30
|
// Hook constants (re-exported from hooks.ts)
|
|
31
31
|
// ─────────────────────────────────────────────────────────
|
|
32
32
|
import { HOOK_TYPES as OPENCODE_HOOK_NAMES } from "./hooks.js";
|
|
33
|
-
export class OpenCodeAdapter {
|
|
33
|
+
export class OpenCodeAdapter extends BaseAdapter {
|
|
34
34
|
get name() {
|
|
35
35
|
return this.platform === "kilo" ? "KiloCode" : "OpenCode";
|
|
36
36
|
}
|
|
@@ -47,6 +47,9 @@ export class OpenCodeAdapter {
|
|
|
47
47
|
};
|
|
48
48
|
platform;
|
|
49
49
|
constructor(platform = "opencode") {
|
|
50
|
+
// sessionDirSegments unused — opencode overrides getSessionDir()
|
|
51
|
+
// with XDG_CONFIG_HOME / APPDATA logic
|
|
52
|
+
super([".config", platform]);
|
|
50
53
|
this.platform = platform;
|
|
51
54
|
}
|
|
52
55
|
// ── Input parsing ──────────────────────────────────────
|
|
@@ -179,20 +182,6 @@ export class OpenCodeAdapter {
|
|
|
179
182
|
mkdirSync(dir, { recursive: true });
|
|
180
183
|
return dir;
|
|
181
184
|
}
|
|
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
185
|
generateHookConfig(_pluginRoot) {
|
|
197
186
|
// OpenCode uses TS plugin paradigm — hooks are registered via plugin array
|
|
198
187
|
// in opencode.json, not via command-based hook entries.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/qwen-code — Qwen Code platform adapter.
|
|
3
|
+
*
|
|
4
|
+
* Extends ClaudeCodeBaseAdapter (shared wire-protocol parse/format methods)
|
|
5
|
+
* with Qwen Code-specific configuration, diagnostics, and session ID logic.
|
|
6
|
+
*
|
|
7
|
+
* Differences from Claude Code:
|
|
8
|
+
* - Config dir: ~/.qwen/ (not ~/.claude/)
|
|
9
|
+
* - Env vars: QWEN_PROJECT_DIR, QWEN_SESSION_ID (not CLAUDE_*)
|
|
10
|
+
* - Session ID priority: session_id field first (Claude: transcript_path first)
|
|
11
|
+
* - No plugin registry (Qwen uses settings.json directly)
|
|
12
|
+
* - MCP clientInfo: qwen-cli-mcp-client-* (pattern)
|
|
13
|
+
* - 12 hook events (superset of Claude's 5, but context-mode uses the shared 5)
|
|
14
|
+
*/
|
|
15
|
+
import { ClaudeCodeBaseAdapter, type ClaudeCodeWireInput } from "../claude-code-base.js";
|
|
16
|
+
import type { HookAdapter, HookParadigm, PlatformCapabilities, DiagnosticResult, HookRegistration } from "../types.js";
|
|
17
|
+
export declare class QwenCodeAdapter extends ClaudeCodeBaseAdapter implements HookAdapter {
|
|
18
|
+
constructor();
|
|
19
|
+
readonly name = "Qwen Code";
|
|
20
|
+
readonly paradigm: HookParadigm;
|
|
21
|
+
protected readonly projectDirEnvVar = "QWEN_PROJECT_DIR";
|
|
22
|
+
readonly capabilities: PlatformCapabilities;
|
|
23
|
+
getSettingsPath(): string;
|
|
24
|
+
generateHookConfig(pluginRoot: string): HookRegistration;
|
|
25
|
+
readSettings(): Record<string, unknown> | null;
|
|
26
|
+
writeSettings(settings: Record<string, unknown>): void;
|
|
27
|
+
validateHooks(_pluginRoot: string): DiagnosticResult[];
|
|
28
|
+
checkPluginRegistration(): DiagnosticResult;
|
|
29
|
+
getInstalledVersion(): string;
|
|
30
|
+
configureAllHooks(_pluginRoot: string): string[];
|
|
31
|
+
setHookPermissions(_pluginRoot: string): string[];
|
|
32
|
+
updatePluginRegistry(_pluginRoot: string, _version: string): void;
|
|
33
|
+
getRoutingInstructionsConfig(): {
|
|
34
|
+
instructionsPath: string;
|
|
35
|
+
targetPath: string;
|
|
36
|
+
platformName: string;
|
|
37
|
+
};
|
|
38
|
+
protected extractSessionId(input: ClaudeCodeWireInput): string;
|
|
39
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/qwen-code — Qwen Code platform adapter.
|
|
3
|
+
*
|
|
4
|
+
* Extends ClaudeCodeBaseAdapter (shared wire-protocol parse/format methods)
|
|
5
|
+
* with Qwen Code-specific configuration, diagnostics, and session ID logic.
|
|
6
|
+
*
|
|
7
|
+
* Differences from Claude Code:
|
|
8
|
+
* - Config dir: ~/.qwen/ (not ~/.claude/)
|
|
9
|
+
* - Env vars: QWEN_PROJECT_DIR, QWEN_SESSION_ID (not CLAUDE_*)
|
|
10
|
+
* - Session ID priority: session_id field first (Claude: transcript_path first)
|
|
11
|
+
* - No plugin registry (Qwen uses settings.json directly)
|
|
12
|
+
* - MCP clientInfo: qwen-cli-mcp-client-* (pattern)
|
|
13
|
+
* - 12 hook events (superset of Claude's 5, but context-mode uses the shared 5)
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync, } from "node:fs";
|
|
16
|
+
import { resolve, join } from "node:path";
|
|
17
|
+
import { homedir } from "node:os";
|
|
18
|
+
import { ClaudeCodeBaseAdapter } from "../claude-code-base.js";
|
|
19
|
+
// ─────────────────────────────────────────────────────────
|
|
20
|
+
// Adapter implementation
|
|
21
|
+
// ─────────────────────────────────────────────────────────
|
|
22
|
+
export class QwenCodeAdapter extends ClaudeCodeBaseAdapter {
|
|
23
|
+
constructor() {
|
|
24
|
+
super([".qwen"]);
|
|
25
|
+
}
|
|
26
|
+
name = "Qwen Code";
|
|
27
|
+
paradigm = "json-stdio";
|
|
28
|
+
projectDirEnvVar = "QWEN_PROJECT_DIR";
|
|
29
|
+
capabilities = {
|
|
30
|
+
preToolUse: true,
|
|
31
|
+
postToolUse: true,
|
|
32
|
+
preCompact: true,
|
|
33
|
+
sessionStart: true,
|
|
34
|
+
canModifyArgs: true,
|
|
35
|
+
canModifyOutput: true,
|
|
36
|
+
canInjectSessionContext: true,
|
|
37
|
+
};
|
|
38
|
+
// ── Configuration (differs from Claude Code) ───────────
|
|
39
|
+
getSettingsPath() {
|
|
40
|
+
return resolve(homedir(), ".qwen", "settings.json");
|
|
41
|
+
}
|
|
42
|
+
generateHookConfig(pluginRoot) {
|
|
43
|
+
// Qwen Code passes native tool names in hook stdin (verified from
|
|
44
|
+
// packages/core/src/tools/tool-names.ts). Claude-style names (Bash, Read)
|
|
45
|
+
// are only accepted in permission configs, NOT in hook tool_name payloads.
|
|
46
|
+
const preToolUseMatcher = [
|
|
47
|
+
// Qwen-native names (canonical tool_name in hook stdin)
|
|
48
|
+
"run_shell_command", "read_file", "read_many_files", "grep_search",
|
|
49
|
+
"web_fetch", "agent",
|
|
50
|
+
// MCP tools (same naming convention as Claude Code)
|
|
51
|
+
"mcp__plugin_context-mode_context-mode__ctx_execute",
|
|
52
|
+
"mcp__plugin_context-mode_context-mode__ctx_execute_file",
|
|
53
|
+
"mcp__plugin_context-mode_context-mode__ctx_batch_execute",
|
|
54
|
+
].join("|");
|
|
55
|
+
return {
|
|
56
|
+
PreToolUse: [
|
|
57
|
+
{
|
|
58
|
+
matcher: preToolUseMatcher,
|
|
59
|
+
hooks: [
|
|
60
|
+
{ type: "command", command: `node ${pluginRoot}/hooks/pretooluse.mjs` },
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
PostToolUse: [
|
|
65
|
+
{
|
|
66
|
+
matcher: "",
|
|
67
|
+
hooks: [
|
|
68
|
+
{ type: "command", command: `node ${pluginRoot}/hooks/posttooluse.mjs` },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
SessionStart: [
|
|
73
|
+
{
|
|
74
|
+
matcher: "",
|
|
75
|
+
hooks: [
|
|
76
|
+
{ type: "command", command: `node ${pluginRoot}/hooks/sessionstart.mjs` },
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
PreCompact: [
|
|
81
|
+
{
|
|
82
|
+
matcher: "",
|
|
83
|
+
hooks: [
|
|
84
|
+
{ type: "command", command: `node ${pluginRoot}/hooks/precompact.mjs` },
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
UserPromptSubmit: [
|
|
89
|
+
{
|
|
90
|
+
matcher: "",
|
|
91
|
+
hooks: [
|
|
92
|
+
{ type: "command", command: `node ${pluginRoot}/hooks/userpromptsubmit.mjs` },
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// ── Settings read/write ────────────────────────────────
|
|
99
|
+
readSettings() {
|
|
100
|
+
try {
|
|
101
|
+
const raw = readFileSync(this.getSettingsPath(), "utf-8");
|
|
102
|
+
return JSON.parse(raw);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
writeSettings(settings) {
|
|
109
|
+
const { writeFileSync } = require("node:fs");
|
|
110
|
+
writeFileSync(this.getSettingsPath(), JSON.stringify(settings, null, 2));
|
|
111
|
+
}
|
|
112
|
+
// ── Diagnostics (doctor) ───────────────────────────────
|
|
113
|
+
validateHooks(_pluginRoot) {
|
|
114
|
+
const results = [];
|
|
115
|
+
const settings = this.readSettings();
|
|
116
|
+
const hooks = (settings?.hooks ?? {});
|
|
117
|
+
for (const hookName of ["PreToolUse", "PostToolUse", "SessionStart", "PreCompact", "UserPromptSubmit"]) {
|
|
118
|
+
const configured = Array.isArray(hooks[hookName]) && hooks[hookName].length > 0;
|
|
119
|
+
results.push({
|
|
120
|
+
check: `${hookName} hook`,
|
|
121
|
+
status: configured ? "pass" : "fail",
|
|
122
|
+
message: configured
|
|
123
|
+
? `${hookName} hook configured in ~/.qwen/settings.json`
|
|
124
|
+
: `${hookName} hook not found in ~/.qwen/settings.json`,
|
|
125
|
+
...(configured ? {} : { fix: `Add ${hookName} hook to ~/.qwen/settings.json` }),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return results;
|
|
129
|
+
}
|
|
130
|
+
checkPluginRegistration() {
|
|
131
|
+
// Qwen Code has no plugin registry — check for MCP config instead
|
|
132
|
+
try {
|
|
133
|
+
const settings = this.readSettings();
|
|
134
|
+
if (settings?.mcpServers && typeof settings.mcpServers === "object") {
|
|
135
|
+
const servers = settings.mcpServers;
|
|
136
|
+
if (Object.keys(servers).some(k => k.includes("context-mode"))) {
|
|
137
|
+
return {
|
|
138
|
+
check: "Plugin registration",
|
|
139
|
+
status: "pass",
|
|
140
|
+
message: "context-mode found in mcpServers",
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
check: "Plugin registration",
|
|
145
|
+
status: "fail",
|
|
146
|
+
message: "mcpServers exists but context-mode not found",
|
|
147
|
+
fix: "Add context-mode to mcpServers in ~/.qwen/settings.json",
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
check: "Plugin registration",
|
|
152
|
+
status: "warn",
|
|
153
|
+
message: "No mcpServers in ~/.qwen/settings.json",
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return {
|
|
158
|
+
check: "Plugin registration",
|
|
159
|
+
status: "warn",
|
|
160
|
+
message: "Could not read ~/.qwen/settings.json",
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
getInstalledVersion() {
|
|
165
|
+
return "not installed";
|
|
166
|
+
}
|
|
167
|
+
configureAllHooks(_pluginRoot) {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
setHookPermissions(_pluginRoot) {
|
|
171
|
+
return [];
|
|
172
|
+
}
|
|
173
|
+
updatePluginRegistry(_pluginRoot, _version) {
|
|
174
|
+
// No plugin registry in Qwen Code
|
|
175
|
+
}
|
|
176
|
+
getRoutingInstructionsConfig() {
|
|
177
|
+
const instructionsPath = resolve(join(homedir(), ".qwen", "QWEN.md"));
|
|
178
|
+
return {
|
|
179
|
+
instructionsPath,
|
|
180
|
+
targetPath: "QWEN.md",
|
|
181
|
+
platformName: "Qwen Code",
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// ── Session ID extraction (differs from Claude Code) ───
|
|
185
|
+
// Qwen Code prioritizes session_id field, then QWEN_SESSION_ID env var.
|
|
186
|
+
// Claude Code prioritizes transcript_path UUID first.
|
|
187
|
+
extractSessionId(input) {
|
|
188
|
+
if (input.session_id)
|
|
189
|
+
return input.session_id;
|
|
190
|
+
if (input.transcript_path) {
|
|
191
|
+
const match = input.transcript_path.match(/([a-f0-9-]{36})\.jsonl$/);
|
|
192
|
+
if (match)
|
|
193
|
+
return match[1];
|
|
194
|
+
}
|
|
195
|
+
if (process.env.QWEN_SESSION_ID)
|
|
196
|
+
return process.env.QWEN_SESSION_ID;
|
|
197
|
+
return `pid-${process.ppid}`;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -193,7 +193,7 @@ export interface DiagnosticResult {
|
|
|
193
193
|
fix?: string;
|
|
194
194
|
}
|
|
195
195
|
/** Supported platform identifiers. */
|
|
196
|
-
export type PlatformId = "claude-code" | "gemini-cli" | "opencode" | "kilo" | "openclaw" | "codex" | "vscode-copilot" | "cursor" | "antigravity" | "kiro" | "pi" | "zed" | "unknown";
|
|
196
|
+
export type PlatformId = "claude-code" | "gemini-cli" | "opencode" | "kilo" | "openclaw" | "codex" | "vscode-copilot" | "jetbrains-copilot" | "cursor" | "antigravity" | "kiro" | "pi" | "zed" | "qwen-code" | "unknown";
|
|
197
197
|
/** Detection signal used to identify which platform is running. */
|
|
198
198
|
export interface DetectionSignal {
|
|
199
199
|
/** Platform identifier. */
|
|
@@ -1,56 +1,26 @@
|
|
|
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
|
-
|
|
12
|
+
import { CopilotBaseAdapter } from "../copilot-base.js";
|
|
13
|
+
import type { CopilotHookInput, CopilotHookModule } from "../copilot-base.js";
|
|
14
|
+
import type { DiagnosticResult } from "../types.js";
|
|
15
|
+
export declare class VSCodeCopilotAdapter extends CopilotBaseAdapter {
|
|
16
|
+
constructor();
|
|
26
17
|
readonly name = "VS Code Copilot";
|
|
27
|
-
readonly
|
|
28
|
-
readonly
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
parsePreCompactInput(raw: unknown): PreCompactEvent;
|
|
32
|
-
parseSessionStartInput(raw: unknown): SessionStartEvent;
|
|
33
|
-
formatPreToolUseResponse(response: PreToolUseResponse): unknown;
|
|
34
|
-
formatPostToolUseResponse(response: PostToolUseResponse): unknown;
|
|
35
|
-
formatPreCompactResponse(response: PreCompactResponse): unknown;
|
|
36
|
-
formatSessionStartResponse(response: SessionStartResponse): unknown;
|
|
37
|
-
getSettingsPath(): string;
|
|
18
|
+
protected readonly hookModule: CopilotHookModule;
|
|
19
|
+
protected readonly hookSubdir = "vscode-copilot";
|
|
20
|
+
protected extractSessionId(input: CopilotHookInput): string;
|
|
21
|
+
protected getProjectDir(): string;
|
|
38
22
|
getSessionDir(): string;
|
|
39
|
-
getSessionDBPath(projectDir: string): string;
|
|
40
|
-
getSessionEventsPath(projectDir: string): string;
|
|
41
|
-
generateHookConfig(pluginRoot: string): HookRegistration;
|
|
42
|
-
readSettings(): Record<string, unknown> | null;
|
|
43
|
-
writeSettings(settings: Record<string, unknown>): void;
|
|
44
23
|
validateHooks(pluginRoot: string): DiagnosticResult[];
|
|
45
24
|
checkPluginRegistration(): DiagnosticResult;
|
|
46
25
|
getInstalledVersion(): string;
|
|
47
|
-
configureAllHooks(pluginRoot: string): string[];
|
|
48
|
-
backupSettings(): string | null;
|
|
49
|
-
setHookPermissions(pluginRoot: string): string[];
|
|
50
|
-
updatePluginRegistry(_pluginRoot: string, _version: string): void;
|
|
51
|
-
/**
|
|
52
|
-
* Extract session ID from VS Code Copilot hook input.
|
|
53
|
-
* VS Code Copilot uses camelCase sessionId (NOT session_id).
|
|
54
|
-
*/
|
|
55
|
-
private extractSessionId;
|
|
56
26
|
}
|