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
|
@@ -9,21 +9,22 @@
|
|
|
9
9
|
* - Config: ~/.codex/hooks.json + ~/.codex/config.toml (TOML for MCP/features)
|
|
10
10
|
* - Session dir: ~/.codex/context-mode/sessions/
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* Our adapter is ready; it will work once Codex enables dispatch.
|
|
16
|
-
* Track: https://github.com/openai/codex/issues/16685
|
|
12
|
+
* Hook dispatch is stable in Codex CLI. PreToolUse deny decisions work,
|
|
13
|
+
* while input rewriting remains blocked on upstream updatedInput support.
|
|
14
|
+
* Track: https://github.com/openai/codex/issues/18491
|
|
17
15
|
*/
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { resolve, join, dirname } from "node:path";
|
|
16
|
+
import { readFileSync, } from "node:fs";
|
|
17
|
+
import { resolve, dirname } from "node:path";
|
|
21
18
|
import { fileURLToPath } from "node:url";
|
|
22
19
|
import { homedir } from "node:os";
|
|
20
|
+
import { BaseAdapter } from "../base.js";
|
|
23
21
|
// ─────────────────────────────────────────────────────────
|
|
24
22
|
// Adapter implementation
|
|
25
23
|
// ─────────────────────────────────────────────────────────
|
|
26
|
-
export class CodexAdapter {
|
|
24
|
+
export class CodexAdapter extends BaseAdapter {
|
|
25
|
+
constructor() {
|
|
26
|
+
super([".codex"]);
|
|
27
|
+
}
|
|
27
28
|
name = "Codex CLI";
|
|
28
29
|
paradigm = "json-stdio";
|
|
29
30
|
capabilities = {
|
|
@@ -146,30 +147,11 @@ export class CodexAdapter {
|
|
|
146
147
|
getSettingsPath() {
|
|
147
148
|
return resolve(homedir(), ".codex", "config.toml");
|
|
148
149
|
}
|
|
149
|
-
getSessionDir() {
|
|
150
|
-
const dir = join(homedir(), ".codex", "context-mode", "sessions");
|
|
151
|
-
mkdirSync(dir, { recursive: true });
|
|
152
|
-
return dir;
|
|
153
|
-
}
|
|
154
|
-
getSessionDBPath(projectDir) {
|
|
155
|
-
const hash = createHash("sha256")
|
|
156
|
-
.update(projectDir)
|
|
157
|
-
.digest("hex")
|
|
158
|
-
.slice(0, 16);
|
|
159
|
-
return join(this.getSessionDir(), `${hash}.db`);
|
|
160
|
-
}
|
|
161
|
-
getSessionEventsPath(projectDir) {
|
|
162
|
-
const hash = createHash("sha256")
|
|
163
|
-
.update(projectDir)
|
|
164
|
-
.digest("hex")
|
|
165
|
-
.slice(0, 16);
|
|
166
|
-
return join(this.getSessionDir(), `${hash}-events.md`);
|
|
167
|
-
}
|
|
168
150
|
generateHookConfig(pluginRoot) {
|
|
169
151
|
return {
|
|
170
152
|
PreToolUse: [
|
|
171
153
|
{
|
|
172
|
-
matcher: "",
|
|
154
|
+
matcher: "local_shell|shell|shell_command|exec_command|container.exec|Bash|Shell|grep_files|mcp__plugin_context-mode_context-mode__ctx_execute|mcp__plugin_context-mode_context-mode__ctx_execute_file|mcp__plugin_context-mode_context-mode__ctx_batch_execute",
|
|
173
155
|
hooks: [
|
|
174
156
|
{
|
|
175
157
|
type: "command",
|
|
@@ -225,8 +207,8 @@ export class CodexAdapter {
|
|
|
225
207
|
return [
|
|
226
208
|
{
|
|
227
209
|
check: "Hook support",
|
|
228
|
-
status: "
|
|
229
|
-
message: "Codex CLI hooks are
|
|
210
|
+
status: "pass",
|
|
211
|
+
message: "Codex CLI hooks are stable. Configure ~/.codex/hooks.json for PreToolUse, PostToolUse, and SessionStart.",
|
|
230
212
|
},
|
|
231
213
|
];
|
|
232
214
|
}
|
|
@@ -275,18 +257,6 @@ export class CodexAdapter {
|
|
|
275
257
|
// Codex CLI hook configuration is done via hooks.json, not config.toml
|
|
276
258
|
return [];
|
|
277
259
|
}
|
|
278
|
-
backupSettings() {
|
|
279
|
-
const settingsPath = this.getSettingsPath();
|
|
280
|
-
try {
|
|
281
|
-
accessSync(settingsPath, constants.R_OK);
|
|
282
|
-
const backupPath = settingsPath + ".bak";
|
|
283
|
-
copyFileSync(settingsPath, backupPath);
|
|
284
|
-
return backupPath;
|
|
285
|
-
}
|
|
286
|
-
catch {
|
|
287
|
-
return null;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
260
|
setHookPermissions(_pluginRoot) {
|
|
291
261
|
// Hook permissions are set during plugin install
|
|
292
262
|
return [];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CopilotBaseAdapter — shared implementation for VS Code Copilot and JetBrains Copilot.
|
|
3
|
+
*
|
|
4
|
+
* Both platforms share the SAME Copilot agent runtime:
|
|
5
|
+
* - hookSpecificOutput wrapper with hookEventName
|
|
6
|
+
* - Same hook events (PreToolUse, PostToolUse, PreCompact, SessionStart)
|
|
7
|
+
* - Same .github/hooks/ config location
|
|
8
|
+
* - Same configureHooks logic
|
|
9
|
+
* - Same generateHookConfig format
|
|
10
|
+
* - Same parse/format methods
|
|
11
|
+
*
|
|
12
|
+
* Platform-specific differences handled by subclasses:
|
|
13
|
+
* - extractSessionId() — different env var fallbacks
|
|
14
|
+
* - getProjectDir() — different env vars for project root
|
|
15
|
+
* - getSessionDir() — different default session directories
|
|
16
|
+
* - checkPluginRegistration() — VS Code reads .vscode/mcp.json, JetBrains uses IDE UI
|
|
17
|
+
* - getInstalledVersion() — VS Code checks extensions dir, JetBrains checks hook config
|
|
18
|
+
* - validateHooks() — different warning messages
|
|
19
|
+
*/
|
|
20
|
+
import { BaseAdapter } from "./base.js";
|
|
21
|
+
import type { HookAdapter, HookParadigm, PlatformCapabilities, DiagnosticResult, PreToolUseEvent, PostToolUseEvent, PreCompactEvent, SessionStartEvent, PreToolUseResponse, PostToolUseResponse, PreCompactResponse, SessionStartResponse, HookRegistration } from "./types.js";
|
|
22
|
+
export interface CopilotHookInput {
|
|
23
|
+
tool_name?: string;
|
|
24
|
+
tool_input?: Record<string, unknown>;
|
|
25
|
+
tool_output?: string;
|
|
26
|
+
is_error?: boolean;
|
|
27
|
+
/** Copilot uses camelCase sessionId (NOT session_id). */
|
|
28
|
+
sessionId?: string;
|
|
29
|
+
source?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface CopilotHookModule {
|
|
32
|
+
HOOK_TYPES: {
|
|
33
|
+
readonly PRE_TOOL_USE: string;
|
|
34
|
+
readonly POST_TOOL_USE: string;
|
|
35
|
+
readonly PRE_COMPACT: string;
|
|
36
|
+
readonly SESSION_START: string;
|
|
37
|
+
readonly STOP: string;
|
|
38
|
+
readonly SUBAGENT_START: string;
|
|
39
|
+
readonly SUBAGENT_STOP: string;
|
|
40
|
+
};
|
|
41
|
+
HOOK_SCRIPTS: Record<string, string>;
|
|
42
|
+
buildHookCommand: (hookType: any, pluginRoot?: string) => string;
|
|
43
|
+
}
|
|
44
|
+
export declare abstract class CopilotBaseAdapter extends BaseAdapter implements HookAdapter {
|
|
45
|
+
readonly paradigm: HookParadigm;
|
|
46
|
+
readonly capabilities: PlatformCapabilities;
|
|
47
|
+
/** Subclasses must provide their platform name. */
|
|
48
|
+
abstract readonly name: string;
|
|
49
|
+
/** Subclasses must provide their hook module (HOOK_TYPES, HOOK_SCRIPTS, buildHookCommand). */
|
|
50
|
+
protected abstract readonly hookModule: CopilotHookModule;
|
|
51
|
+
/** Subclasses must provide the hook scripts subdirectory name (e.g., "vscode-copilot"). */
|
|
52
|
+
protected abstract readonly hookSubdir: string;
|
|
53
|
+
/** Extract session ID from Copilot hook input — env var fallbacks differ per platform. */
|
|
54
|
+
protected abstract extractSessionId(input: CopilotHookInput): string;
|
|
55
|
+
/** Get the project directory — env vars differ per platform. */
|
|
56
|
+
protected abstract getProjectDir(): string;
|
|
57
|
+
/** Validate that hooks are properly configured for this platform. */
|
|
58
|
+
abstract validateHooks(pluginRoot: string): DiagnosticResult[];
|
|
59
|
+
/** Check if the plugin is registered/enabled on this platform. */
|
|
60
|
+
abstract checkPluginRegistration(): DiagnosticResult;
|
|
61
|
+
/** Get the installed version from this platform's registry/marketplace. */
|
|
62
|
+
abstract getInstalledVersion(): string;
|
|
63
|
+
parsePreToolUseInput(raw: unknown): PreToolUseEvent;
|
|
64
|
+
parsePostToolUseInput(raw: unknown): PostToolUseEvent;
|
|
65
|
+
parsePreCompactInput(raw: unknown): PreCompactEvent;
|
|
66
|
+
parseSessionStartInput(raw: unknown): SessionStartEvent;
|
|
67
|
+
formatPreToolUseResponse(response: PreToolUseResponse): unknown;
|
|
68
|
+
formatPostToolUseResponse(response: PostToolUseResponse): unknown;
|
|
69
|
+
formatPreCompactResponse(response: PreCompactResponse): unknown;
|
|
70
|
+
formatSessionStartResponse(response: SessionStartResponse): unknown;
|
|
71
|
+
getSettingsPath(): string;
|
|
72
|
+
generateHookConfig(pluginRoot: string): HookRegistration;
|
|
73
|
+
readSettings(): Record<string, unknown> | null;
|
|
74
|
+
writeSettings(settings: Record<string, unknown>): void;
|
|
75
|
+
configureAllHooks(pluginRoot: string): string[];
|
|
76
|
+
setHookPermissions(pluginRoot: string): string[];
|
|
77
|
+
updatePluginRegistry(_pluginRoot: string, _version: string): void;
|
|
78
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CopilotBaseAdapter — shared implementation for VS Code Copilot and JetBrains Copilot.
|
|
3
|
+
*
|
|
4
|
+
* Both platforms share the SAME Copilot agent runtime:
|
|
5
|
+
* - hookSpecificOutput wrapper with hookEventName
|
|
6
|
+
* - Same hook events (PreToolUse, PostToolUse, PreCompact, SessionStart)
|
|
7
|
+
* - Same .github/hooks/ config location
|
|
8
|
+
* - Same configureHooks logic
|
|
9
|
+
* - Same generateHookConfig format
|
|
10
|
+
* - Same parse/format methods
|
|
11
|
+
*
|
|
12
|
+
* Platform-specific differences handled by subclasses:
|
|
13
|
+
* - extractSessionId() — different env var fallbacks
|
|
14
|
+
* - getProjectDir() — different env vars for project root
|
|
15
|
+
* - getSessionDir() — different default session directories
|
|
16
|
+
* - checkPluginRegistration() — VS Code reads .vscode/mcp.json, JetBrains uses IDE UI
|
|
17
|
+
* - getInstalledVersion() — VS Code checks extensions dir, JetBrains checks hook config
|
|
18
|
+
* - validateHooks() — different warning messages
|
|
19
|
+
*/
|
|
20
|
+
import { readFileSync, writeFileSync, mkdirSync, accessSync, chmodSync, constants, } from "node:fs";
|
|
21
|
+
import { resolve, join } from "node:path";
|
|
22
|
+
import { BaseAdapter } from "./base.js";
|
|
23
|
+
// ─────────────────────────────────────────────────────────
|
|
24
|
+
// Abstract base adapter for Copilot platforms
|
|
25
|
+
// ─────────────────────────────────────────────────────────
|
|
26
|
+
export class CopilotBaseAdapter extends BaseAdapter {
|
|
27
|
+
paradigm = "json-stdio";
|
|
28
|
+
capabilities = {
|
|
29
|
+
preToolUse: true,
|
|
30
|
+
postToolUse: true,
|
|
31
|
+
preCompact: true,
|
|
32
|
+
sessionStart: true,
|
|
33
|
+
canModifyArgs: true,
|
|
34
|
+
canModifyOutput: true,
|
|
35
|
+
canInjectSessionContext: true,
|
|
36
|
+
};
|
|
37
|
+
// ── Input parsing (shared) ─────────────────────────────
|
|
38
|
+
parsePreToolUseInput(raw) {
|
|
39
|
+
const input = raw;
|
|
40
|
+
return {
|
|
41
|
+
toolName: input.tool_name ?? "",
|
|
42
|
+
toolInput: input.tool_input ?? {},
|
|
43
|
+
sessionId: this.extractSessionId(input),
|
|
44
|
+
projectDir: this.getProjectDir(),
|
|
45
|
+
raw,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
parsePostToolUseInput(raw) {
|
|
49
|
+
const input = raw;
|
|
50
|
+
return {
|
|
51
|
+
toolName: input.tool_name ?? "",
|
|
52
|
+
toolInput: input.tool_input ?? {},
|
|
53
|
+
toolOutput: input.tool_output,
|
|
54
|
+
isError: input.is_error,
|
|
55
|
+
sessionId: this.extractSessionId(input),
|
|
56
|
+
projectDir: this.getProjectDir(),
|
|
57
|
+
raw,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
parsePreCompactInput(raw) {
|
|
61
|
+
const input = raw;
|
|
62
|
+
return {
|
|
63
|
+
sessionId: this.extractSessionId(input),
|
|
64
|
+
projectDir: this.getProjectDir(),
|
|
65
|
+
raw,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
parseSessionStartInput(raw) {
|
|
69
|
+
const input = raw;
|
|
70
|
+
const rawSource = input.source ?? "startup";
|
|
71
|
+
let source;
|
|
72
|
+
switch (rawSource) {
|
|
73
|
+
case "compact":
|
|
74
|
+
source = "compact";
|
|
75
|
+
break;
|
|
76
|
+
case "resume":
|
|
77
|
+
source = "resume";
|
|
78
|
+
break;
|
|
79
|
+
case "clear":
|
|
80
|
+
source = "clear";
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
source = "startup";
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
sessionId: this.extractSessionId(input),
|
|
87
|
+
source,
|
|
88
|
+
projectDir: this.getProjectDir(),
|
|
89
|
+
raw,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
// ── Response formatting (shared) ───────────────────────
|
|
93
|
+
formatPreToolUseResponse(response) {
|
|
94
|
+
if (response.decision === "deny") {
|
|
95
|
+
return {
|
|
96
|
+
permissionDecision: "deny",
|
|
97
|
+
reason: response.reason ?? "Blocked by context-mode hook",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (response.decision === "modify" && response.updatedInput) {
|
|
101
|
+
return {
|
|
102
|
+
hookSpecificOutput: {
|
|
103
|
+
hookEventName: this.hookModule.HOOK_TYPES.PRE_TOOL_USE,
|
|
104
|
+
updatedInput: response.updatedInput,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (response.decision === "context" && response.additionalContext) {
|
|
109
|
+
return {
|
|
110
|
+
hookSpecificOutput: {
|
|
111
|
+
hookEventName: this.hookModule.HOOK_TYPES.PRE_TOOL_USE,
|
|
112
|
+
additionalContext: response.additionalContext,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (response.decision === "ask") {
|
|
117
|
+
return {
|
|
118
|
+
permissionDecision: "deny",
|
|
119
|
+
reason: response.reason ?? "Action requires user confirmation (security policy)",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// "allow" — return undefined for passthrough
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
formatPostToolUseResponse(response) {
|
|
126
|
+
if (response.updatedOutput) {
|
|
127
|
+
return {
|
|
128
|
+
hookSpecificOutput: {
|
|
129
|
+
hookEventName: this.hookModule.HOOK_TYPES.POST_TOOL_USE,
|
|
130
|
+
decision: "block",
|
|
131
|
+
reason: response.updatedOutput,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (response.additionalContext) {
|
|
136
|
+
return {
|
|
137
|
+
hookSpecificOutput: {
|
|
138
|
+
hookEventName: this.hookModule.HOOK_TYPES.POST_TOOL_USE,
|
|
139
|
+
additionalContext: response.additionalContext,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
formatPreCompactResponse(response) {
|
|
146
|
+
return response.context ?? "";
|
|
147
|
+
}
|
|
148
|
+
formatSessionStartResponse(response) {
|
|
149
|
+
return response.context ?? "";
|
|
150
|
+
}
|
|
151
|
+
// ── Configuration (shared) ─────────────────────────────
|
|
152
|
+
getSettingsPath() {
|
|
153
|
+
return resolve(".github", "hooks", "context-mode.json");
|
|
154
|
+
}
|
|
155
|
+
generateHookConfig(pluginRoot) {
|
|
156
|
+
const { HOOK_TYPES, buildHookCommand } = this.hookModule;
|
|
157
|
+
return {
|
|
158
|
+
[HOOK_TYPES.PRE_TOOL_USE]: [
|
|
159
|
+
{
|
|
160
|
+
matcher: "",
|
|
161
|
+
hooks: [
|
|
162
|
+
{
|
|
163
|
+
type: "command",
|
|
164
|
+
command: buildHookCommand(HOOK_TYPES.PRE_TOOL_USE, pluginRoot),
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
[HOOK_TYPES.POST_TOOL_USE]: [
|
|
170
|
+
{
|
|
171
|
+
matcher: "",
|
|
172
|
+
hooks: [
|
|
173
|
+
{
|
|
174
|
+
type: "command",
|
|
175
|
+
command: buildHookCommand(HOOK_TYPES.POST_TOOL_USE, pluginRoot),
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
[HOOK_TYPES.PRE_COMPACT]: [
|
|
181
|
+
{
|
|
182
|
+
matcher: "",
|
|
183
|
+
hooks: [
|
|
184
|
+
{
|
|
185
|
+
type: "command",
|
|
186
|
+
command: buildHookCommand(HOOK_TYPES.PRE_COMPACT, pluginRoot),
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
[HOOK_TYPES.SESSION_START]: [
|
|
192
|
+
{
|
|
193
|
+
matcher: "",
|
|
194
|
+
hooks: [
|
|
195
|
+
{
|
|
196
|
+
type: "command",
|
|
197
|
+
command: buildHookCommand(HOOK_TYPES.SESSION_START, pluginRoot),
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
readSettings() {
|
|
205
|
+
// Primary: .github/hooks/context-mode.json
|
|
206
|
+
try {
|
|
207
|
+
const raw = readFileSync(this.getSettingsPath(), "utf-8");
|
|
208
|
+
return JSON.parse(raw);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
/* fall through */
|
|
212
|
+
}
|
|
213
|
+
// Fallback: .claude/settings.json
|
|
214
|
+
try {
|
|
215
|
+
const raw = readFileSync(resolve(".claude", "settings.json"), "utf-8");
|
|
216
|
+
return JSON.parse(raw);
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
writeSettings(settings) {
|
|
223
|
+
const configPath = this.getSettingsPath();
|
|
224
|
+
mkdirSync(resolve(".github", "hooks"), { recursive: true });
|
|
225
|
+
writeFileSync(configPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
226
|
+
}
|
|
227
|
+
// ── Upgrade (shared) ──────────────────────────────────
|
|
228
|
+
configureAllHooks(pluginRoot) {
|
|
229
|
+
const changes = [];
|
|
230
|
+
const settings = this.readSettings() ?? {};
|
|
231
|
+
const hooks = settings.hooks ?? {};
|
|
232
|
+
const { HOOK_TYPES, HOOK_SCRIPTS, buildHookCommand } = this.hookModule;
|
|
233
|
+
const hookTypes = [
|
|
234
|
+
HOOK_TYPES.PRE_TOOL_USE,
|
|
235
|
+
HOOK_TYPES.POST_TOOL_USE,
|
|
236
|
+
HOOK_TYPES.PRE_COMPACT,
|
|
237
|
+
HOOK_TYPES.SESSION_START,
|
|
238
|
+
];
|
|
239
|
+
for (const hookType of hookTypes) {
|
|
240
|
+
const script = HOOK_SCRIPTS[hookType];
|
|
241
|
+
if (!script)
|
|
242
|
+
continue;
|
|
243
|
+
hooks[hookType] = [
|
|
244
|
+
{
|
|
245
|
+
matcher: "",
|
|
246
|
+
hooks: [
|
|
247
|
+
{
|
|
248
|
+
type: "command",
|
|
249
|
+
command: buildHookCommand(hookType, pluginRoot),
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
];
|
|
254
|
+
changes.push(`Configured ${hookType} hook`);
|
|
255
|
+
}
|
|
256
|
+
settings.hooks = hooks;
|
|
257
|
+
this.writeSettings(settings);
|
|
258
|
+
changes.push(`Wrote hook config to ${this.getSettingsPath()}`);
|
|
259
|
+
return changes;
|
|
260
|
+
}
|
|
261
|
+
setHookPermissions(pluginRoot) {
|
|
262
|
+
const set = [];
|
|
263
|
+
const hooksDir = join(pluginRoot, "hooks", this.hookSubdir);
|
|
264
|
+
for (const scriptName of Object.values(this.hookModule.HOOK_SCRIPTS)) {
|
|
265
|
+
const scriptPath = resolve(hooksDir, scriptName);
|
|
266
|
+
try {
|
|
267
|
+
accessSync(scriptPath, constants.R_OK);
|
|
268
|
+
chmodSync(scriptPath, 0o755);
|
|
269
|
+
set.push(scriptPath);
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
/* skip missing scripts */
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return set;
|
|
276
|
+
}
|
|
277
|
+
updatePluginRegistry(_pluginRoot, _version) {
|
|
278
|
+
// Copilot platforms manage plugins through their own marketplaces.
|
|
279
|
+
// No manual registry update needed.
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Native Cursor hooks use lower-camel hook names and flat command entries in
|
|
5
5
|
* `.cursor/hooks.json` / `~/.cursor/hooks.json`.
|
|
6
6
|
*/
|
|
7
|
+
import { BaseAdapter } from "../base.js";
|
|
7
8
|
import type { HookAdapter, HookParadigm, PlatformCapabilities, DiagnosticResult, PreToolUseEvent, PostToolUseEvent, SessionStartEvent, PreToolUseResponse, PostToolUseResponse, SessionStartResponse, HookRegistration } from "../types.js";
|
|
8
9
|
interface StopEvent {
|
|
9
10
|
sessionId: string;
|
|
@@ -12,7 +13,8 @@ interface StopEvent {
|
|
|
12
13
|
generationId?: string;
|
|
13
14
|
transcriptPath?: string;
|
|
14
15
|
}
|
|
15
|
-
export declare class CursorAdapter implements HookAdapter {
|
|
16
|
+
export declare class CursorAdapter extends BaseAdapter implements HookAdapter {
|
|
17
|
+
constructor();
|
|
16
18
|
readonly name = "Cursor";
|
|
17
19
|
readonly paradigm: HookParadigm;
|
|
18
20
|
readonly capabilities: PlatformCapabilities;
|
|
@@ -30,9 +32,6 @@ export declare class CursorAdapter implements HookAdapter {
|
|
|
30
32
|
text: string;
|
|
31
33
|
};
|
|
32
34
|
getSettingsPath(): string;
|
|
33
|
-
getSessionDir(): string;
|
|
34
|
-
getSessionDBPath(projectDir: string): string;
|
|
35
|
-
getSessionEventsPath(projectDir: string): string;
|
|
36
35
|
generateHookConfig(_pluginRoot: string): HookRegistration;
|
|
37
36
|
readSettings(): Record<string, unknown> | null;
|
|
38
37
|
writeSettings(settings: Record<string, unknown>): void;
|
|
@@ -40,7 +39,6 @@ export declare class CursorAdapter implements HookAdapter {
|
|
|
40
39
|
checkPluginRegistration(): DiagnosticResult;
|
|
41
40
|
getInstalledVersion(): string;
|
|
42
41
|
configureAllHooks(_pluginRoot: string): string[];
|
|
43
|
-
backupSettings(): string | null;
|
|
44
42
|
setHookPermissions(pluginRoot: string): string[];
|
|
45
43
|
updatePluginRegistry(_pluginRoot: string, _version: string): void;
|
|
46
44
|
private getCandidateHookConfigPaths;
|
|
@@ -4,14 +4,17 @@
|
|
|
4
4
|
* Native Cursor hooks use lower-camel hook names and flat command entries in
|
|
5
5
|
* `.cursor/hooks.json` / `~/.cursor/hooks.json`.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import { readFileSync, writeFileSync, mkdirSync, copyFileSync, accessSync, chmodSync, constants, existsSync, } from "node:fs";
|
|
7
|
+
import { readFileSync, writeFileSync, mkdirSync, accessSync, chmodSync, constants, existsSync, } from "node:fs";
|
|
9
8
|
import { execSync } from "node:child_process";
|
|
10
9
|
import { resolve, join } from "node:path";
|
|
11
10
|
import { homedir } from "node:os";
|
|
11
|
+
import { BaseAdapter } from "../base.js";
|
|
12
12
|
import { HOOK_TYPES as CURSOR_HOOK_NAMES, HOOK_SCRIPTS as CURSOR_HOOK_SCRIPTS, PRE_TOOL_USE_MATCHER_PATTERN, REQUIRED_HOOKS, OPTIONAL_HOOKS, isContextModeHook, buildHookCommand, } from "./hooks.js";
|
|
13
13
|
const CURSOR_ENTERPRISE_HOOKS_PATH = "/Library/Application Support/Cursor/hooks.json";
|
|
14
|
-
export class CursorAdapter {
|
|
14
|
+
export class CursorAdapter extends BaseAdapter {
|
|
15
|
+
constructor() {
|
|
16
|
+
super([".cursor"]);
|
|
17
|
+
}
|
|
15
18
|
name = "Cursor";
|
|
16
19
|
paradigm = "json-stdio";
|
|
17
20
|
capabilities = {
|
|
@@ -125,25 +128,6 @@ export class CursorAdapter {
|
|
|
125
128
|
getSettingsPath() {
|
|
126
129
|
return resolve(".cursor", "hooks.json");
|
|
127
130
|
}
|
|
128
|
-
getSessionDir() {
|
|
129
|
-
const dir = join(homedir(), ".cursor", "context-mode", "sessions");
|
|
130
|
-
mkdirSync(dir, { recursive: true });
|
|
131
|
-
return dir;
|
|
132
|
-
}
|
|
133
|
-
getSessionDBPath(projectDir) {
|
|
134
|
-
const hash = createHash("sha256")
|
|
135
|
-
.update(projectDir)
|
|
136
|
-
.digest("hex")
|
|
137
|
-
.slice(0, 16);
|
|
138
|
-
return join(this.getSessionDir(), `${hash}.db`);
|
|
139
|
-
}
|
|
140
|
-
getSessionEventsPath(projectDir) {
|
|
141
|
-
const hash = createHash("sha256")
|
|
142
|
-
.update(projectDir)
|
|
143
|
-
.digest("hex")
|
|
144
|
-
.slice(0, 16);
|
|
145
|
-
return join(this.getSessionDir(), `${hash}-events.md`);
|
|
146
|
-
}
|
|
147
131
|
generateHookConfig(_pluginRoot) {
|
|
148
132
|
const hooks = {
|
|
149
133
|
[CURSOR_HOOK_NAMES.PRE_TOOL_USE]: [
|
|
@@ -355,18 +339,6 @@ export class CursorAdapter {
|
|
|
355
339
|
changes.push(`Wrote native Cursor hooks to ${this.getSettingsPath()}`);
|
|
356
340
|
return changes;
|
|
357
341
|
}
|
|
358
|
-
backupSettings() {
|
|
359
|
-
const settingsPath = this.getSettingsPath();
|
|
360
|
-
try {
|
|
361
|
-
accessSync(settingsPath, constants.R_OK);
|
|
362
|
-
const backupPath = settingsPath + ".bak";
|
|
363
|
-
copyFileSync(settingsPath, backupPath);
|
|
364
|
-
return backupPath;
|
|
365
|
-
}
|
|
366
|
-
catch {
|
|
367
|
-
return null;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
342
|
setHookPermissions(pluginRoot) {
|
|
371
343
|
const set = [];
|
|
372
344
|
const hooksDir = join(pluginRoot, "hooks", "cursor");
|
|
@@ -15,8 +15,15 @@
|
|
|
15
15
|
* - Codex CLI: CODEX_CI, CODEX_THREAD_ID | ~/.codex/
|
|
16
16
|
* - Cursor: CURSOR_TRACE_ID (MCP), CURSOR_CLI (terminal) | ~/.cursor/
|
|
17
17
|
* - VS Code Copilot: VSCODE_PID, VSCODE_CWD | ~/.vscode/
|
|
18
|
+
* - JetBrains Copilot: IDEA_INITIAL_DIRECTORY, IDEA_HOME, JETBRAINS_CLIENT_ID | ~/.config/JetBrains/
|
|
18
19
|
*/
|
|
19
20
|
import type { PlatformId, DetectionSignal, HookAdapter } from "./types.js";
|
|
21
|
+
/**
|
|
22
|
+
* High-confidence env vars per platform, checked in priority order.
|
|
23
|
+
* Single source of truth — consumed by detectPlatform() below and by
|
|
24
|
+
* tests that need to clear platform-related env vars deterministically.
|
|
25
|
+
*/
|
|
26
|
+
export declare const PLATFORM_ENV_VARS: readonly [readonly ["claude-code", readonly ["CLAUDE_PROJECT_DIR", "CLAUDE_SESSION_ID"]], readonly ["gemini-cli", readonly ["GEMINI_PROJECT_DIR", "GEMINI_CLI"]], readonly ["openclaw", readonly ["OPENCLAW_HOME", "OPENCLAW_CLI"]], readonly ["kilo", readonly ["KILO", "KILO_PID"]], readonly ["opencode", readonly ["OPENCODE", "OPENCODE_PID"]], readonly ["codex", readonly ["CODEX_CI", "CODEX_THREAD_ID"]], readonly ["cursor", readonly ["CURSOR_TRACE_ID", "CURSOR_CLI"]], readonly ["vscode-copilot", readonly ["VSCODE_PID", "VSCODE_CWD"]], readonly ["jetbrains-copilot", readonly ["IDEA_INITIAL_DIRECTORY", "IDEA_HOME", "JETBRAINS_CLIENT_ID"]], readonly ["qwen-code", readonly ["QWEN_PROJECT_DIR", "QWEN_SESSION_ID"]]];
|
|
20
27
|
/**
|
|
21
28
|
* Detect the current platform by checking env vars and config dirs.
|
|
22
29
|
*
|