context-mode 1.0.103 → 1.0.105
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 +39 -7
- package/bin/statusline.mjs +321 -0
- package/build/adapters/antigravity/index.d.ts +6 -0
- package/build/adapters/antigravity/index.js +10 -0
- package/build/adapters/base.d.ts +23 -0
- package/build/adapters/base.js +29 -0
- package/build/adapters/codex/index.d.ts +10 -0
- package/build/adapters/codex/index.js +22 -4
- package/build/adapters/cursor/index.d.ts +7 -0
- package/build/adapters/cursor/index.js +11 -0
- package/build/adapters/detect.d.ts +12 -1
- package/build/adapters/detect.js +69 -7
- package/build/adapters/gemini-cli/index.d.ts +8 -1
- package/build/adapters/gemini-cli/index.js +19 -7
- package/build/adapters/jetbrains-copilot/index.d.ts +7 -0
- package/build/adapters/jetbrains-copilot/index.js +12 -0
- package/build/adapters/kiro/index.d.ts +8 -0
- package/build/adapters/kiro/index.js +12 -0
- package/build/adapters/openclaw/index.d.ts +17 -0
- package/build/adapters/openclaw/index.js +29 -4
- package/build/adapters/opencode/index.d.ts +8 -0
- package/build/adapters/opencode/index.js +18 -6
- package/build/adapters/qwen-code/index.d.ts +1 -0
- package/build/adapters/qwen-code/index.js +3 -0
- package/build/adapters/types.d.ts +33 -0
- package/build/adapters/vscode-copilot/index.d.ts +6 -0
- package/build/adapters/vscode-copilot/index.js +10 -0
- package/build/adapters/zed/index.d.ts +1 -0
- package/build/adapters/zed/index.js +3 -0
- package/build/cli.d.ts +15 -0
- package/build/cli.js +62 -16
- package/build/concurrency/runPool.d.ts +36 -0
- package/build/concurrency/runPool.js +51 -0
- package/build/executor.d.ts +11 -1
- package/build/executor.js +77 -21
- package/build/fetch-cache.d.ts +13 -0
- package/build/fetch-cache.js +15 -0
- package/build/lifecycle.d.ts +6 -2
- package/build/lifecycle.js +29 -2
- package/build/opencode-plugin.d.ts +23 -0
- package/build/opencode-plugin.js +80 -6
- package/build/routing-block.d.ts +8 -0
- package/build/routing-block.js +86 -0
- package/build/runtime.d.ts +1 -0
- package/build/runtime.js +54 -3
- package/build/search/auto-memory.d.ts +23 -10
- package/build/search/auto-memory.js +64 -26
- package/build/search/unified.d.ts +3 -0
- package/build/search/unified.js +2 -2
- package/build/server.d.ts +47 -0
- package/build/server.js +736 -188
- package/build/session/analytics.d.ts +49 -1
- package/build/session/analytics.js +278 -16
- package/build/session/db.d.ts +53 -8
- package/build/session/db.js +200 -19
- package/build/session/extract.js +124 -2
- package/build/tool-naming.d.ts +4 -0
- package/build/tool-naming.js +24 -0
- package/cli.bundle.mjs +208 -158
- package/configs/antigravity/GEMINI.md +11 -0
- package/configs/claude-code/CLAUDE.md +11 -0
- package/configs/codex/AGENTS.md +11 -0
- package/configs/cursor/context-mode.mdc +11 -0
- package/configs/gemini-cli/GEMINI.md +11 -0
- package/configs/jetbrains-copilot/copilot-instructions.md +3 -0
- package/configs/kilo/AGENTS.md +11 -0
- package/configs/kiro/KIRO.md +11 -0
- package/configs/openclaw/AGENTS.md +11 -0
- package/configs/opencode/AGENTS.md +11 -0
- package/configs/pi/AGENTS.md +11 -0
- package/configs/qwen-code/QWEN.md +11 -0
- package/configs/vscode-copilot/copilot-instructions.md +3 -0
- package/configs/zed/AGENTS.md +11 -0
- package/hooks/auto-injection.mjs +36 -10
- package/hooks/cache-heal-utils.mjs +231 -0
- package/hooks/codex/sessionstart.mjs +7 -4
- package/hooks/core/routing.mjs +8 -2
- package/hooks/cursor/sessionstart.mjs +7 -4
- package/hooks/formatters/claude-code.mjs +20 -0
- package/hooks/gemini-cli/sessionstart.mjs +7 -2
- package/hooks/jetbrains-copilot/sessionstart.mjs +7 -2
- package/hooks/normalize-hooks.mjs +184 -0
- package/hooks/session-db.bundle.mjs +41 -14
- package/hooks/session-extract.bundle.mjs +2 -2
- package/hooks/session-helpers.mjs +68 -20
- package/hooks/session-loaders.mjs +8 -2
- package/hooks/sessionstart.mjs +8 -2
- package/hooks/vscode-copilot/sessionstart.mjs +7 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -1
- package/server.bundle.mjs +181 -134
- package/skills/ctx-doctor/SKILL.md +3 -3
- package/skills/ctx-insight/SKILL.md +1 -1
- package/start.mjs +63 -3
|
@@ -32,6 +32,13 @@ export declare class CursorAdapter extends BaseAdapter implements HookAdapter {
|
|
|
32
32
|
text: string;
|
|
33
33
|
};
|
|
34
34
|
getSettingsPath(): string;
|
|
35
|
+
/**
|
|
36
|
+
* Cursor stores conventions per project under .cursor/. Always returned
|
|
37
|
+
* as an absolute path resolved against `projectDir` (or `process.cwd()`
|
|
38
|
+
* when omitted) per the HookAdapter.getConfigDir contract.
|
|
39
|
+
*/
|
|
40
|
+
getConfigDir(projectDir?: string): string;
|
|
41
|
+
getInstructionFiles(): string[];
|
|
35
42
|
generateHookConfig(_pluginRoot: string): HookRegistration;
|
|
36
43
|
readSettings(): Record<string, unknown> | null;
|
|
37
44
|
writeSettings(settings: Record<string, unknown>): void;
|
|
@@ -128,6 +128,17 @@ export class CursorAdapter extends BaseAdapter {
|
|
|
128
128
|
getSettingsPath() {
|
|
129
129
|
return resolve(".cursor", "hooks.json");
|
|
130
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Cursor stores conventions per project under .cursor/. Always returned
|
|
133
|
+
* as an absolute path resolved against `projectDir` (or `process.cwd()`
|
|
134
|
+
* when omitted) per the HookAdapter.getConfigDir contract.
|
|
135
|
+
*/
|
|
136
|
+
getConfigDir(projectDir) {
|
|
137
|
+
return resolve(projectDir ?? process.cwd(), ".cursor");
|
|
138
|
+
}
|
|
139
|
+
getInstructionFiles() {
|
|
140
|
+
return ["context-mode.mdc"];
|
|
141
|
+
}
|
|
131
142
|
generateHookConfig(_pluginRoot) {
|
|
132
143
|
const hooks = {
|
|
133
144
|
[CURSOR_HOOK_NAMES.PRE_TOOL_USE]: [
|
|
@@ -23,7 +23,18 @@ import type { PlatformId, DetectionSignal, HookAdapter } from "./types.js";
|
|
|
23
23
|
* Single source of truth — consumed by detectPlatform() below and by
|
|
24
24
|
* tests that need to clear platform-related env vars deterministically.
|
|
25
25
|
*/
|
|
26
|
-
export declare const PLATFORM_ENV_VARS: readonly [readonly ["claude-code", readonly ["CLAUDE_PROJECT_DIR", "CLAUDE_SESSION_ID"]], readonly ["
|
|
26
|
+
export declare const PLATFORM_ENV_VARS: readonly [readonly ["claude-code", readonly ["CLAUDE_PROJECT_DIR", "CLAUDE_SESSION_ID"]], readonly ["antigravity", readonly ["ANTIGRAVITY_CLI_ALIAS"]], readonly ["cursor", readonly ["CURSOR_TRACE_ID", "CURSOR_CLI"]], readonly ["kilo", readonly ["KILO_PID"]], readonly ["opencode", readonly ["OPENCODE", "OPENCODE_PID"]], readonly ["zed", readonly ["ZED_SESSION_ID", "ZED_TERM"]], readonly ["codex", readonly ["CODEX_THREAD_ID", "CODEX_CI"]], readonly ["gemini-cli", readonly ["GEMINI_PROJECT_DIR", "GEMINI_CLI"]], readonly ["vscode-copilot", readonly ["VSCODE_PID", "VSCODE_CWD"]], readonly ["jetbrains-copilot", readonly ["IDEA_INITIAL_DIRECTORY"]], readonly ["qwen-code", readonly ["QWEN_PROJECT_DIR"]], readonly ["pi", readonly ["PI_PROJECT_DIR"]]];
|
|
27
|
+
/**
|
|
28
|
+
* Sync map from platform identifier → home-relative path segments where that
|
|
29
|
+
* platform stores its config. Mirrors the `super([...])` argument passed by
|
|
30
|
+
* each adapter — kept in sync as the single source of truth used when we need
|
|
31
|
+
* a session dir BEFORE an adapter has been instantiated (race window between
|
|
32
|
+
* MCP server start and `initialize` handshake completion).
|
|
33
|
+
*
|
|
34
|
+
* Returns `null` for "unknown" or any string outside the supported set so the
|
|
35
|
+
* caller can decide on a safe fallback.
|
|
36
|
+
*/
|
|
37
|
+
export declare function getSessionDirSegments(platform: string): string[] | null;
|
|
27
38
|
/**
|
|
28
39
|
* Detect the current platform by checking env vars and config dirs.
|
|
29
40
|
*
|
package/build/adapters/detect.js
CHANGED
|
@@ -27,17 +27,79 @@ import { CLIENT_NAME_TO_PLATFORM } from "./client-map.js";
|
|
|
27
27
|
* tests that need to clear platform-related env vars deterministically.
|
|
28
28
|
*/
|
|
29
29
|
export const PLATFORM_ENV_VARS = [
|
|
30
|
+
// Order matters: forks listed BEFORE the fork's parent so collision
|
|
31
|
+
// detection works. Every entry verified against platform's own runtime
|
|
32
|
+
// source code (PR #376 follow-up: full audit, May 2026 — see git blame).
|
|
30
33
|
["claude-code", ["CLAUDE_PROJECT_DIR", "CLAUDE_SESSION_ID"]],
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
["
|
|
35
|
-
|
|
34
|
+
// antigravity (Electron/VSCode fork) — google-gemini/gemini-cli
|
|
35
|
+
// packages/core/src/ide/detect-ide.ts checks ANTIGRAVITY_CLI_ALIAS as the
|
|
36
|
+
// canonical Antigravity marker. Listed before vscode-copilot.
|
|
37
|
+
["antigravity", ["ANTIGRAVITY_CLI_ALIAS"]],
|
|
38
|
+
// cursor (VSCode fork) — listed before vscode-copilot. CURSOR_TRACE_ID has
|
|
39
|
+
// 800+ hits in major OSS detection libs (Vercel Next.js, Bun, Google
|
|
40
|
+
// gemini-cli, Nx, CrewAI).
|
|
36
41
|
["cursor", ["CURSOR_TRACE_ID", "CURSOR_CLI"]],
|
|
42
|
+
// kilo (OpenCode fork) — Kilo-Org/kilocode packages/opencode/src/index.ts:140
|
|
43
|
+
// sets `process.env.KILO_PID = String(process.pid)`. Bare KILO is NEVER set
|
|
44
|
+
// (verified). Kilo also sets OPENCODE=1 (fork) — listed before opencode.
|
|
45
|
+
["kilo", ["KILO_PID"]],
|
|
46
|
+
// opencode — sst/opencode packages/opencode/src/index.ts:108-109 sets
|
|
47
|
+
// OPENCODE=1 + OPENCODE_PID=<pid> on every CLI invocation.
|
|
48
|
+
["opencode", ["OPENCODE", "OPENCODE_PID"]],
|
|
49
|
+
// zed — zed-industries/zed crates/terminal/src/terminal.rs sets ZED_TERM=true
|
|
50
|
+
// in `insert_zed_terminal_env()`. Google's gemini-cli uses ZED_SESSION_ID.
|
|
51
|
+
["zed", ["ZED_SESSION_ID", "ZED_TERM"]],
|
|
52
|
+
// codex — openai/codex codex-rs/core/src/exec_env.rs sets CODEX_THREAD_ID
|
|
53
|
+
// per exec; unified_exec/process_manager.rs sets CODEX_CI in CI mode.
|
|
54
|
+
["codex", ["CODEX_THREAD_ID", "CODEX_CI"]],
|
|
55
|
+
// gemini-cli — GEMINI_PROJECT_DIR per google-gemini/gemini-cli
|
|
56
|
+
// docs/hooks/index.md; GEMINI_CLI is the MCP-server sentinel.
|
|
57
|
+
["gemini-cli", ["GEMINI_PROJECT_DIR", "GEMINI_CLI"]],
|
|
58
|
+
// vscode-copilot — VSCODE_PID + VSCODE_CWD set by microsoft/vscode bootstrap.
|
|
59
|
+
// Listed AFTER cursor and antigravity since they inherit these vars as forks.
|
|
37
60
|
["vscode-copilot", ["VSCODE_PID", "VSCODE_CWD"]],
|
|
38
|
-
|
|
39
|
-
|
|
61
|
+
// jetbrains-copilot — IDEA_INITIAL_DIRECTORY set by JetBrains launcher.
|
|
62
|
+
// (IDEA_HOME and JETBRAINS_CLIENT_ID removed — no source-line evidence.)
|
|
63
|
+
["jetbrains-copilot", ["IDEA_INITIAL_DIRECTORY"]],
|
|
64
|
+
// qwen-code — QWEN_PROJECT_DIR per QwenLM/qwen-code docs/users/features/hooks.md.
|
|
65
|
+
// (QWEN_SESSION_ID removed — 0 hits in qwen-code repository.)
|
|
66
|
+
["qwen-code", ["QWEN_PROJECT_DIR"]],
|
|
67
|
+
// pi — PI_PROJECT_DIR consumed by src/pi-extension.ts:154 + src/server.ts:153
|
|
68
|
+
// — implies the Pi runtime sets it before invoking the extension.
|
|
69
|
+
["pi", ["PI_PROJECT_DIR"]],
|
|
70
|
+
// openclaw — removed (runtime never sets OPENCLAW_HOME or OPENCLAW_CLI;
|
|
71
|
+
// detection falls through to ~/.openclaw/ config-dir tier below).
|
|
72
|
+
// kiro — not listed (no auto-set process env vars; ~/.kiro/ config-dir tier).
|
|
40
73
|
];
|
|
74
|
+
/**
|
|
75
|
+
* Sync map from platform identifier → home-relative path segments where that
|
|
76
|
+
* platform stores its config. Mirrors the `super([...])` argument passed by
|
|
77
|
+
* each adapter — kept in sync as the single source of truth used when we need
|
|
78
|
+
* a session dir BEFORE an adapter has been instantiated (race window between
|
|
79
|
+
* MCP server start and `initialize` handshake completion).
|
|
80
|
+
*
|
|
81
|
+
* Returns `null` for "unknown" or any string outside the supported set so the
|
|
82
|
+
* caller can decide on a safe fallback.
|
|
83
|
+
*/
|
|
84
|
+
export function getSessionDirSegments(platform) {
|
|
85
|
+
switch (platform) {
|
|
86
|
+
case "claude-code": return [".claude"];
|
|
87
|
+
case "gemini-cli": return [".gemini"];
|
|
88
|
+
case "antigravity": return [".gemini"];
|
|
89
|
+
case "openclaw": return [".openclaw"];
|
|
90
|
+
case "codex": return [".codex"];
|
|
91
|
+
case "cursor": return [".cursor"];
|
|
92
|
+
case "vscode-copilot": return [".vscode"];
|
|
93
|
+
case "kiro": return [".kiro"];
|
|
94
|
+
case "pi": return [".pi"];
|
|
95
|
+
case "qwen-code": return [".qwen"];
|
|
96
|
+
case "kilo": return [".config", "kilo"];
|
|
97
|
+
case "opencode": return [".config", "opencode"];
|
|
98
|
+
case "zed": return [".config", "zed"];
|
|
99
|
+
case "jetbrains-copilot": return [".config", "JetBrains"];
|
|
100
|
+
default: return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
41
103
|
/**
|
|
42
104
|
* Detect the current platform by checking env vars and config dirs.
|
|
43
105
|
*
|
|
@@ -34,6 +34,7 @@ export declare class GeminiCLIAdapter extends BaseAdapter implements HookAdapter
|
|
|
34
34
|
formatPreCompactResponse(response: PreCompactResponse): unknown;
|
|
35
35
|
formatSessionStartResponse(response: SessionStartResponse): unknown;
|
|
36
36
|
getSettingsPath(): string;
|
|
37
|
+
getInstructionFiles(): string[];
|
|
37
38
|
generateHookConfig(pluginRoot: string): HookRegistration;
|
|
38
39
|
readSettings(): Record<string, unknown> | null;
|
|
39
40
|
writeSettings(settings: Record<string, unknown>): void;
|
|
@@ -43,7 +44,13 @@ export declare class GeminiCLIAdapter extends BaseAdapter implements HookAdapter
|
|
|
43
44
|
configureAllHooks(pluginRoot: string): string[];
|
|
44
45
|
setHookPermissions(pluginRoot: string): string[];
|
|
45
46
|
updatePluginRegistry(pluginRoot: string, version: string): void;
|
|
46
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the project directory for a Gemini CLI hook input.
|
|
49
|
+
* Priority: input.cwd > GEMINI_PROJECT_DIR > CLAUDE_PROJECT_DIR > process.cwd().
|
|
50
|
+
* Mirrors the cursor / opencode pattern so downstream hooks always
|
|
51
|
+
* receive a defined projectDir even when the platform omits cwd
|
|
52
|
+
* from the wire payload (e.g. under worktrees).
|
|
53
|
+
*/
|
|
47
54
|
private getProjectDir;
|
|
48
55
|
/**
|
|
49
56
|
* Extract session ID from Gemini CLI hook input.
|
|
@@ -51,7 +51,7 @@ export class GeminiCLIAdapter extends BaseAdapter {
|
|
|
51
51
|
toolName: input.tool_name ?? "",
|
|
52
52
|
toolInput: input.tool_input ?? {},
|
|
53
53
|
sessionId: this.extractSessionId(input),
|
|
54
|
-
projectDir: this.getProjectDir(),
|
|
54
|
+
projectDir: this.getProjectDir(input),
|
|
55
55
|
raw,
|
|
56
56
|
};
|
|
57
57
|
}
|
|
@@ -63,7 +63,7 @@ export class GeminiCLIAdapter extends BaseAdapter {
|
|
|
63
63
|
toolOutput: input.tool_output,
|
|
64
64
|
isError: input.is_error,
|
|
65
65
|
sessionId: this.extractSessionId(input),
|
|
66
|
-
projectDir: this.getProjectDir(),
|
|
66
|
+
projectDir: this.getProjectDir(input),
|
|
67
67
|
raw,
|
|
68
68
|
};
|
|
69
69
|
}
|
|
@@ -71,7 +71,7 @@ export class GeminiCLIAdapter extends BaseAdapter {
|
|
|
71
71
|
const input = raw;
|
|
72
72
|
return {
|
|
73
73
|
sessionId: this.extractSessionId(input),
|
|
74
|
-
projectDir: this.getProjectDir(),
|
|
74
|
+
projectDir: this.getProjectDir(input),
|
|
75
75
|
raw,
|
|
76
76
|
};
|
|
77
77
|
}
|
|
@@ -95,7 +95,7 @@ export class GeminiCLIAdapter extends BaseAdapter {
|
|
|
95
95
|
return {
|
|
96
96
|
sessionId: this.extractSessionId(input),
|
|
97
97
|
source,
|
|
98
|
-
projectDir: this.getProjectDir(),
|
|
98
|
+
projectDir: this.getProjectDir(input),
|
|
99
99
|
raw,
|
|
100
100
|
};
|
|
101
101
|
}
|
|
@@ -160,6 +160,9 @@ export class GeminiCLIAdapter extends BaseAdapter {
|
|
|
160
160
|
getSettingsPath() {
|
|
161
161
|
return resolve(homedir(), ".gemini", "settings.json");
|
|
162
162
|
}
|
|
163
|
+
getInstructionFiles() {
|
|
164
|
+
return ["GEMINI.md"];
|
|
165
|
+
}
|
|
163
166
|
generateHookConfig(pluginRoot) {
|
|
164
167
|
return {
|
|
165
168
|
[GEMINI_HOOK_NAMES.BEFORE_TOOL]: [
|
|
@@ -394,9 +397,18 @@ export class GeminiCLIAdapter extends BaseAdapter {
|
|
|
394
397
|
}
|
|
395
398
|
}
|
|
396
399
|
// ── Internal helpers ───────────────────────────────────
|
|
397
|
-
/**
|
|
398
|
-
|
|
399
|
-
|
|
400
|
+
/**
|
|
401
|
+
* Resolve the project directory for a Gemini CLI hook input.
|
|
402
|
+
* Priority: input.cwd > GEMINI_PROJECT_DIR > CLAUDE_PROJECT_DIR > process.cwd().
|
|
403
|
+
* Mirrors the cursor / opencode pattern so downstream hooks always
|
|
404
|
+
* receive a defined projectDir even when the platform omits cwd
|
|
405
|
+
* from the wire payload (e.g. under worktrees).
|
|
406
|
+
*/
|
|
407
|
+
getProjectDir(input) {
|
|
408
|
+
return (input.cwd
|
|
409
|
+
?? process.env.GEMINI_PROJECT_DIR
|
|
410
|
+
?? process.env.CLAUDE_PROJECT_DIR
|
|
411
|
+
?? process.cwd());
|
|
400
412
|
}
|
|
401
413
|
/**
|
|
402
414
|
* Extract session ID from Gemini CLI hook input.
|
|
@@ -18,6 +18,13 @@ export declare class JetBrainsCopilotAdapter extends CopilotBaseAdapter {
|
|
|
18
18
|
protected readonly hookSubdir = "jetbrains-copilot";
|
|
19
19
|
protected extractSessionId(input: CopilotHookInput): string;
|
|
20
20
|
protected getProjectDir(): string;
|
|
21
|
+
/**
|
|
22
|
+
* JetBrains Copilot honors .github/copilot-instructions.md per project.
|
|
23
|
+
* Always returned absolute, resolved against the supplied `projectDir`,
|
|
24
|
+
* the JetBrains-specific project env vars, or `process.cwd()`.
|
|
25
|
+
*/
|
|
26
|
+
getConfigDir(projectDir?: string): string;
|
|
27
|
+
getInstructionFiles(): string[];
|
|
21
28
|
validateHooks(pluginRoot: string): DiagnosticResult[];
|
|
22
29
|
checkPluginRegistration(): DiagnosticResult;
|
|
23
30
|
getInstalledVersion(): string;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - validateHooks: JetBrains-specific warnings
|
|
10
10
|
*/
|
|
11
11
|
import { readFileSync, } from "node:fs";
|
|
12
|
+
import { resolve } from "node:path";
|
|
12
13
|
import { CopilotBaseAdapter } from "../copilot-base.js";
|
|
13
14
|
// ─────────────────────────────────────────────────────────
|
|
14
15
|
// Hook constants (re-exported from hooks.ts)
|
|
@@ -42,6 +43,17 @@ export class JetBrainsCopilotAdapter extends CopilotBaseAdapter {
|
|
|
42
43
|
getProjectDir() {
|
|
43
44
|
return process.env.IDEA_INITIAL_DIRECTORY || process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
44
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* JetBrains Copilot honors .github/copilot-instructions.md per project.
|
|
48
|
+
* Always returned absolute, resolved against the supplied `projectDir`,
|
|
49
|
+
* the JetBrains-specific project env vars, or `process.cwd()`.
|
|
50
|
+
*/
|
|
51
|
+
getConfigDir(projectDir) {
|
|
52
|
+
return resolve(projectDir ?? this.getProjectDir(), ".github");
|
|
53
|
+
}
|
|
54
|
+
getInstructionFiles() {
|
|
55
|
+
return ["copilot-instructions.md"];
|
|
56
|
+
}
|
|
45
57
|
// ── Diagnostics (doctor) ─────────────────────────────────
|
|
46
58
|
validateHooks(pluginRoot) {
|
|
47
59
|
const results = [];
|
|
@@ -33,6 +33,14 @@ export declare class KiroAdapter extends BaseAdapter implements HookAdapter {
|
|
|
33
33
|
formatPreCompactResponse(_response: PreCompactResponse): unknown;
|
|
34
34
|
formatSessionStartResponse(_response: SessionStartResponse): unknown;
|
|
35
35
|
getSettingsPath(): string;
|
|
36
|
+
/**
|
|
37
|
+
* Kiro stores per-project context under .kiro/ (steering files, etc).
|
|
38
|
+
* Auto-memory + rule detection use this project-relative dir, returned as
|
|
39
|
+
* an absolute path resolved against `projectDir` (or `process.cwd()`).
|
|
40
|
+
* (Settings/MCP config still live under ~/.kiro/.)
|
|
41
|
+
*/
|
|
42
|
+
getConfigDir(projectDir?: string): string;
|
|
43
|
+
getInstructionFiles(): string[];
|
|
36
44
|
generateHookConfig(pluginRoot: string): HookRegistration;
|
|
37
45
|
readSettings(): Record<string, unknown> | null;
|
|
38
46
|
writeSettings(settings: Record<string, unknown>): void;
|
|
@@ -98,6 +98,18 @@ export class KiroAdapter extends BaseAdapter {
|
|
|
98
98
|
getSettingsPath() {
|
|
99
99
|
return resolve(homedir(), ".kiro", "settings", "mcp.json");
|
|
100
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Kiro stores per-project context under .kiro/ (steering files, etc).
|
|
103
|
+
* Auto-memory + rule detection use this project-relative dir, returned as
|
|
104
|
+
* an absolute path resolved against `projectDir` (or `process.cwd()`).
|
|
105
|
+
* (Settings/MCP config still live under ~/.kiro/.)
|
|
106
|
+
*/
|
|
107
|
+
getConfigDir(projectDir) {
|
|
108
|
+
return resolve(projectDir ?? process.cwd(), ".kiro");
|
|
109
|
+
}
|
|
110
|
+
getInstructionFiles() {
|
|
111
|
+
return ["KIRO.md"];
|
|
112
|
+
}
|
|
101
113
|
generateHookConfig(pluginRoot) {
|
|
102
114
|
// Kiro CLI hook config format: { preToolUse: [{ matcher, command }] }
|
|
103
115
|
// Note: This generates the entries for agent config files
|
|
@@ -31,6 +31,15 @@ export declare class OpenClawAdapter extends BaseAdapter implements HookAdapter
|
|
|
31
31
|
formatPreCompactResponse(response: PreCompactResponse): unknown;
|
|
32
32
|
formatSessionStartResponse(response: SessionStartResponse): unknown;
|
|
33
33
|
getSettingsPath(): string;
|
|
34
|
+
/**
|
|
35
|
+
* OpenClaw stores everything in the project root — no separate config
|
|
36
|
+
* dir. Returned as the absolute project directory itself per the
|
|
37
|
+
* HookAdapter.getConfigDir contract (always-absolute).
|
|
38
|
+
*/
|
|
39
|
+
getConfigDir(projectDir?: string): string;
|
|
40
|
+
getInstructionFiles(): string[];
|
|
41
|
+
/** Absolute <projectRoot>/memory directory. */
|
|
42
|
+
getMemoryDir(): string;
|
|
34
43
|
generateHookConfig(_pluginRoot: string): HookRegistration;
|
|
35
44
|
readSettings(): Record<string, unknown> | null;
|
|
36
45
|
writeSettings(settings: Record<string, unknown>): void;
|
|
@@ -41,6 +50,14 @@ export declare class OpenClawAdapter extends BaseAdapter implements HookAdapter
|
|
|
41
50
|
backupSettings(): string | null;
|
|
42
51
|
setHookPermissions(_pluginRoot: string): string[];
|
|
43
52
|
updatePluginRegistry(_pluginRoot: string, _version: string): void;
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the project directory for an OpenClaw hook input.
|
|
55
|
+
* Priority: input.cwd > OPENCLAW_PROJECT_DIR env > process.cwd().
|
|
56
|
+
* Mirrors the cursor / opencode pattern so downstream hooks always
|
|
57
|
+
* receive a defined projectDir even under worktrees or when the
|
|
58
|
+
* platform omits cwd from the wire payload.
|
|
59
|
+
*/
|
|
60
|
+
private getProjectDir;
|
|
44
61
|
/**
|
|
45
62
|
* Extract session ID from OpenClaw hook input.
|
|
46
63
|
*/
|
|
@@ -48,7 +48,7 @@ export class OpenClawAdapter extends BaseAdapter {
|
|
|
48
48
|
toolName: input.toolName ?? input.tool_name ?? "",
|
|
49
49
|
toolInput: input.params ?? input.tool_input ?? {},
|
|
50
50
|
sessionId: this.extractSessionId(input),
|
|
51
|
-
projectDir:
|
|
51
|
+
projectDir: this.getProjectDir(input),
|
|
52
52
|
raw,
|
|
53
53
|
};
|
|
54
54
|
}
|
|
@@ -60,7 +60,7 @@ export class OpenClawAdapter extends BaseAdapter {
|
|
|
60
60
|
toolOutput: input.output ?? input.tool_output,
|
|
61
61
|
isError: input.isError ?? input.is_error,
|
|
62
62
|
sessionId: this.extractSessionId(input),
|
|
63
|
-
projectDir:
|
|
63
|
+
projectDir: this.getProjectDir(input),
|
|
64
64
|
raw,
|
|
65
65
|
};
|
|
66
66
|
}
|
|
@@ -68,7 +68,7 @@ export class OpenClawAdapter extends BaseAdapter {
|
|
|
68
68
|
const input = raw;
|
|
69
69
|
return {
|
|
70
70
|
sessionId: this.extractSessionId(input),
|
|
71
|
-
projectDir:
|
|
71
|
+
projectDir: this.getProjectDir(input),
|
|
72
72
|
raw,
|
|
73
73
|
};
|
|
74
74
|
}
|
|
@@ -92,7 +92,7 @@ export class OpenClawAdapter extends BaseAdapter {
|
|
|
92
92
|
return {
|
|
93
93
|
sessionId: this.extractSessionId(input),
|
|
94
94
|
source,
|
|
95
|
-
projectDir:
|
|
95
|
+
projectDir: this.getProjectDir(input),
|
|
96
96
|
raw,
|
|
97
97
|
};
|
|
98
98
|
}
|
|
@@ -143,6 +143,21 @@ export class OpenClawAdapter extends BaseAdapter {
|
|
|
143
143
|
// OpenClaw uses openclaw.json in the project root or ~/.openclaw/openclaw.json
|
|
144
144
|
return resolve("openclaw.json");
|
|
145
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* OpenClaw stores everything in the project root — no separate config
|
|
148
|
+
* dir. Returned as the absolute project directory itself per the
|
|
149
|
+
* HookAdapter.getConfigDir contract (always-absolute).
|
|
150
|
+
*/
|
|
151
|
+
getConfigDir(projectDir) {
|
|
152
|
+
return resolve(projectDir ?? process.cwd());
|
|
153
|
+
}
|
|
154
|
+
getInstructionFiles() {
|
|
155
|
+
return ["AGENTS.md"];
|
|
156
|
+
}
|
|
157
|
+
/** Absolute <projectRoot>/memory directory. */
|
|
158
|
+
getMemoryDir() {
|
|
159
|
+
return join(this.getConfigDir(), "memory");
|
|
160
|
+
}
|
|
146
161
|
generateHookConfig(_pluginRoot) {
|
|
147
162
|
// OpenClaw uses TS plugin paradigm — hooks are registered via
|
|
148
163
|
// api.registerHook() in the plugin entry point, not via config files.
|
|
@@ -394,6 +409,16 @@ export class OpenClawAdapter extends BaseAdapter {
|
|
|
394
409
|
// OpenClaw manages plugins through npm/openclaw.json — no separate registry
|
|
395
410
|
}
|
|
396
411
|
// ── Internal helpers ───────────────────────────────────
|
|
412
|
+
/**
|
|
413
|
+
* Resolve the project directory for an OpenClaw hook input.
|
|
414
|
+
* Priority: input.cwd > OPENCLAW_PROJECT_DIR env > process.cwd().
|
|
415
|
+
* Mirrors the cursor / opencode pattern so downstream hooks always
|
|
416
|
+
* receive a defined projectDir even under worktrees or when the
|
|
417
|
+
* platform omits cwd from the wire payload.
|
|
418
|
+
*/
|
|
419
|
+
getProjectDir(input) {
|
|
420
|
+
return input.cwd ?? process.env.OPENCLAW_PROJECT_DIR ?? process.cwd();
|
|
421
|
+
}
|
|
397
422
|
/**
|
|
398
423
|
* Extract session ID from OpenClaw hook input.
|
|
399
424
|
*/
|
|
@@ -36,6 +36,14 @@ export declare class OpenCodeAdapter extends BaseAdapter implements HookAdapter
|
|
|
36
36
|
getSettingsPath(): string;
|
|
37
37
|
private paths;
|
|
38
38
|
getSessionDir(): string;
|
|
39
|
+
/**
|
|
40
|
+
* OpenCode/KiloCode honor XDG_CONFIG_HOME on POSIX and APPDATA on Windows.
|
|
41
|
+
* Falls back to ~/.config/<platform> (or %APPDATA%\<platform>).
|
|
42
|
+
* Always absolute. `_projectDir` is accepted for interface symmetry but
|
|
43
|
+
* unused — config is home/XDG-rooted, never project-scoped.
|
|
44
|
+
*/
|
|
45
|
+
getConfigDir(_projectDir?: string): string;
|
|
46
|
+
getInstructionFiles(): string[];
|
|
39
47
|
generateHookConfig(_pluginRoot: string): HookRegistration;
|
|
40
48
|
readSettings(): Record<string, unknown> | null;
|
|
41
49
|
writeSettings(settings: Record<string, unknown>): void;
|
|
@@ -171,16 +171,28 @@ export class OpenCodeAdapter extends BaseAdapter {
|
|
|
171
171
|
];
|
|
172
172
|
}
|
|
173
173
|
getSessionDir() {
|
|
174
|
-
|
|
174
|
+
const dir = join(this.getConfigDir(), "context-mode", "sessions");
|
|
175
|
+
mkdirSync(dir, { recursive: true });
|
|
176
|
+
return dir;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* OpenCode/KiloCode honor XDG_CONFIG_HOME on POSIX and APPDATA on Windows.
|
|
180
|
+
* Falls back to ~/.config/<platform> (or %APPDATA%\<platform>).
|
|
181
|
+
* Always absolute. `_projectDir` is accepted for interface symmetry but
|
|
182
|
+
* unused — config is home/XDG-rooted, never project-scoped.
|
|
183
|
+
*/
|
|
184
|
+
getConfigDir(_projectDir) {
|
|
185
|
+
let root;
|
|
175
186
|
if (process.platform === "win32") {
|
|
176
|
-
|
|
187
|
+
root = process.env.APPDATA || join(homedir(), "AppData", "Roaming");
|
|
177
188
|
}
|
|
178
189
|
else {
|
|
179
|
-
|
|
190
|
+
root = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
180
191
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
192
|
+
return join(root, this.platform);
|
|
193
|
+
}
|
|
194
|
+
getInstructionFiles() {
|
|
195
|
+
return ["AGENTS.md"];
|
|
184
196
|
}
|
|
185
197
|
generateHookConfig(_pluginRoot) {
|
|
186
198
|
// OpenCode uses TS plugin paradigm — hooks are registered via plugin array
|
|
@@ -21,6 +21,7 @@ export declare class QwenCodeAdapter extends ClaudeCodeBaseAdapter implements Ho
|
|
|
21
21
|
protected readonly projectDirEnvVar = "QWEN_PROJECT_DIR";
|
|
22
22
|
readonly capabilities: PlatformCapabilities;
|
|
23
23
|
getSettingsPath(): string;
|
|
24
|
+
getInstructionFiles(): string[];
|
|
24
25
|
generateHookConfig(pluginRoot: string): HookRegistration;
|
|
25
26
|
readSettings(): Record<string, unknown> | null;
|
|
26
27
|
writeSettings(settings: Record<string, unknown>): void;
|
|
@@ -40,6 +40,9 @@ export class QwenCodeAdapter extends ClaudeCodeBaseAdapter {
|
|
|
40
40
|
getSettingsPath() {
|
|
41
41
|
return resolve(homedir(), ".qwen", "settings.json");
|
|
42
42
|
}
|
|
43
|
+
getInstructionFiles() {
|
|
44
|
+
return ["QWEN.md"];
|
|
45
|
+
}
|
|
43
46
|
generateHookConfig(pluginRoot) {
|
|
44
47
|
// Qwen Code passes native tool names in hook stdin (verified from
|
|
45
48
|
// packages/core/src/tools/tool-names.ts). Claude-style names (Bash, Read)
|
|
@@ -160,6 +160,39 @@ export interface HookAdapter {
|
|
|
160
160
|
getSessionDBPath(projectDir: string): string;
|
|
161
161
|
/** Compute per-project session events file path. */
|
|
162
162
|
getSessionEventsPath(projectDir: string): string;
|
|
163
|
+
/**
|
|
164
|
+
* Platform config directory.
|
|
165
|
+
*
|
|
166
|
+
* Contract: ALWAYS returns an absolute path. Never returns a relative
|
|
167
|
+
* segment, never returns an empty string. This eliminates the leaky-seam
|
|
168
|
+
* where callers could not tell whether the return needed further resolution.
|
|
169
|
+
*
|
|
170
|
+
* Resolution rules:
|
|
171
|
+
* - Home-rooted platforms (claude-code, codex, qwen, gemini, antigravity,
|
|
172
|
+
* zed, opencode, …) return paths under `homedir()` / XDG / APPDATA.
|
|
173
|
+
* - Project-scoped platforms (cursor → `.cursor`, vscode-copilot &
|
|
174
|
+
* jetbrains-copilot → `.github`, kiro → `.kiro`, openclaw → project root)
|
|
175
|
+
* resolve their segment against the supplied `projectDir`. When
|
|
176
|
+
* `projectDir` is omitted, `process.cwd()` is used as the fallback.
|
|
177
|
+
*
|
|
178
|
+
* @param projectDir Optional project root used to resolve project-scoped
|
|
179
|
+
* adapters. Ignored by home-rooted adapters.
|
|
180
|
+
*/
|
|
181
|
+
getConfigDir(projectDir?: string): string;
|
|
182
|
+
/**
|
|
183
|
+
* Names of platform-native instruction/rule files that act as the
|
|
184
|
+
* project's "user CLAUDE.md equivalent" (e.g., ["CLAUDE.md"],
|
|
185
|
+
* ["AGENTS.md"], ["GEMINI.md"]). Auto-memory scans for these in the
|
|
186
|
+
* project root and config dir, and rule-detection emits "rule" events
|
|
187
|
+
* when they are read.
|
|
188
|
+
*/
|
|
189
|
+
getInstructionFiles(): string[];
|
|
190
|
+
/**
|
|
191
|
+
* Directory where persistent per-user memory is stored
|
|
192
|
+
* (e.g., ~/.claude/memory, ~/.codex/memories). Auto-memory scans
|
|
193
|
+
* *.md files in this directory.
|
|
194
|
+
*/
|
|
195
|
+
getMemoryDir(): string;
|
|
163
196
|
/** Generate hook registration config for this platform. */
|
|
164
197
|
generateHookConfig(pluginRoot: string): HookRegistration;
|
|
165
198
|
/** Read current platform settings. */
|
|
@@ -20,6 +20,12 @@ export declare class VSCodeCopilotAdapter extends CopilotBaseAdapter {
|
|
|
20
20
|
protected extractSessionId(input: CopilotHookInput): string;
|
|
21
21
|
protected getProjectDir(): string;
|
|
22
22
|
getSessionDir(): string;
|
|
23
|
+
/**
|
|
24
|
+
* VS Code Copilot honors .github/copilot-instructions.md per project.
|
|
25
|
+
* Always returned absolute, resolved against `projectDir` (or `cwd`).
|
|
26
|
+
*/
|
|
27
|
+
getConfigDir(projectDir?: string): string;
|
|
28
|
+
getInstructionFiles(): string[];
|
|
23
29
|
validateHooks(pluginRoot: string): DiagnosticResult[];
|
|
24
30
|
checkPluginRegistration(): DiagnosticResult;
|
|
25
31
|
getInstalledVersion(): string;
|
|
@@ -53,6 +53,16 @@ export class VSCodeCopilotAdapter extends CopilotBaseAdapter {
|
|
|
53
53
|
mkdirSync(dir, { recursive: true });
|
|
54
54
|
return dir;
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* VS Code Copilot honors .github/copilot-instructions.md per project.
|
|
58
|
+
* Always returned absolute, resolved against `projectDir` (or `cwd`).
|
|
59
|
+
*/
|
|
60
|
+
getConfigDir(projectDir) {
|
|
61
|
+
return resolve(projectDir ?? process.cwd(), ".github");
|
|
62
|
+
}
|
|
63
|
+
getInstructionFiles() {
|
|
64
|
+
return ["copilot-instructions.md"];
|
|
65
|
+
}
|
|
56
66
|
// ── Diagnostics (doctor) ─────────────────────────────────
|
|
57
67
|
validateHooks(pluginRoot) {
|
|
58
68
|
const results = [];
|
|
@@ -26,6 +26,7 @@ export declare class ZedAdapter extends BaseAdapter implements HookAdapter {
|
|
|
26
26
|
formatPreCompactResponse(_response: PreCompactResponse): unknown;
|
|
27
27
|
formatSessionStartResponse(_response: SessionStartResponse): unknown;
|
|
28
28
|
getSettingsPath(): string;
|
|
29
|
+
getInstructionFiles(): string[];
|
|
29
30
|
generateHookConfig(_pluginRoot: string): HookRegistration;
|
|
30
31
|
readSettings(): Record<string, unknown> | null;
|
|
31
32
|
writeSettings(settings: Record<string, unknown>): void;
|
|
@@ -66,6 +66,9 @@ export class ZedAdapter extends BaseAdapter {
|
|
|
66
66
|
getSettingsPath() {
|
|
67
67
|
return resolve(homedir(), ".config", "zed", "settings.json");
|
|
68
68
|
}
|
|
69
|
+
getInstructionFiles() {
|
|
70
|
+
return ["AGENTS.md"];
|
|
71
|
+
}
|
|
69
72
|
generateHookConfig(_pluginRoot) {
|
|
70
73
|
// Zed does not support hooks — return empty registration
|
|
71
74
|
return {};
|
package/build/cli.d.ts
CHANGED
|
@@ -15,3 +15,18 @@
|
|
|
15
15
|
export declare function toUnixPath(p: string): string;
|
|
16
16
|
export declare function npmExecFile(args: string[], opts?: Record<string, unknown>): void;
|
|
17
17
|
export declare function npmExec(command: string, opts?: Record<string, unknown>): void;
|
|
18
|
+
/**
|
|
19
|
+
* Open a URL in the user's default browser without invoking a shell.
|
|
20
|
+
*
|
|
21
|
+
* Uses `execFile` with an arg array so the URL cannot be interpreted as
|
|
22
|
+
* shell metacharacters. Original code used `execSync(`open "${url}"`)`
|
|
23
|
+
* which would shell-interpolate the URL — fragile if the URL ever
|
|
24
|
+
* becomes attacker-controlled (remote, weak port-validation, etc).
|
|
25
|
+
*
|
|
26
|
+
* Best-effort: if the OS opener is missing the function logs a copyable
|
|
27
|
+
* URL hint and returns; it never throws. `runner` is injectable for
|
|
28
|
+
* tests; default is `child_process.execFile` (callback form, fire-and-
|
|
29
|
+
* forget).
|
|
30
|
+
*/
|
|
31
|
+
export type ExecFileFn = (file: string, args: readonly string[], opts?: Record<string, unknown>) => unknown;
|
|
32
|
+
export declare function openInBrowser(url: string, platform?: NodeJS.Platform, runner?: ExecFileFn): void;
|