context-mode 1.0.21 → 1.0.23
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 +4 -2
- package/.openclaw-plugin/index.ts +11 -0
- package/.openclaw-plugin/openclaw.plugin.json +23 -0
- package/.openclaw-plugin/package.json +28 -0
- package/README.md +165 -26
- package/build/adapters/antigravity/index.d.ts +49 -0
- package/build/adapters/antigravity/index.js +217 -0
- package/build/adapters/client-map.d.ts +10 -0
- package/build/adapters/client-map.js +18 -0
- package/build/adapters/detect.d.ts +8 -1
- package/build/adapters/detect.js +58 -1
- package/build/adapters/kiro/hooks.d.ts +32 -0
- package/build/adapters/kiro/hooks.js +47 -0
- package/build/adapters/kiro/index.d.ts +50 -0
- package/build/adapters/kiro/index.js +325 -0
- package/build/adapters/openclaw/config.d.ts +8 -0
- package/build/adapters/openclaw/config.js +8 -0
- package/build/adapters/openclaw/hooks.d.ts +50 -0
- package/build/adapters/openclaw/hooks.js +61 -0
- package/build/adapters/openclaw/index.d.ts +51 -0
- package/build/adapters/openclaw/index.js +459 -0
- package/build/adapters/openclaw/session-db.d.ts +55 -0
- package/build/adapters/openclaw/session-db.js +88 -0
- package/build/adapters/types.d.ts +1 -1
- package/build/cli.js +5 -3
- package/build/executor.js +99 -112
- package/build/openclaw/workspace-router.d.ts +29 -0
- package/build/openclaw/workspace-router.js +64 -0
- package/build/openclaw-plugin.d.ts +121 -0
- package/build/openclaw-plugin.js +525 -0
- package/build/server.js +45 -10
- package/build/session/db.d.ts +9 -0
- package/build/session/db.js +38 -0
- package/cli.bundle.mjs +136 -124
- package/configs/antigravity/GEMINI.md +58 -0
- package/configs/antigravity/mcp_config.json +7 -0
- package/configs/kiro/mcp_config.json +7 -0
- package/configs/openclaw/AGENTS.md +58 -0
- package/configs/openclaw/openclaw.json +13 -0
- package/hooks/core/routing.mjs +16 -8
- package/hooks/kiro/posttooluse.mjs +58 -0
- package/hooks/kiro/pretooluse.mjs +63 -0
- package/hooks/posttooluse.mjs +6 -5
- package/hooks/precompact.mjs +5 -4
- package/hooks/session-db.bundle.mjs +57 -0
- package/hooks/session-extract.bundle.mjs +1 -0
- package/hooks/session-helpers.mjs +41 -3
- package/hooks/session-loaders.mjs +28 -0
- package/hooks/session-snapshot.bundle.mjs +14 -0
- package/hooks/sessionstart.mjs +6 -5
- package/hooks/userpromptsubmit.mjs +6 -5
- package/hooks/vscode-copilot/posttooluse.mjs +5 -4
- package/hooks/vscode-copilot/precompact.mjs +5 -4
- package/hooks/vscode-copilot/sessionstart.mjs +5 -4
- package/openclaw.plugin.json +23 -0
- package/package.json +13 -2
- package/server.bundle.mjs +94 -82
- package/start.mjs +1 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/client-map — MCP clientInfo.name → PlatformId mapping.
|
|
3
|
+
*
|
|
4
|
+
* Source: Apify MCP Client Capabilities Registry
|
|
5
|
+
* https://github.com/apify/mcp-client-capabilities
|
|
6
|
+
*
|
|
7
|
+
* Only includes platforms we have adapters for.
|
|
8
|
+
*/
|
|
9
|
+
export const CLIENT_NAME_TO_PLATFORM = {
|
|
10
|
+
"claude-code": "claude-code",
|
|
11
|
+
"gemini-cli-mcp-client": "gemini-cli",
|
|
12
|
+
"antigravity-client": "antigravity",
|
|
13
|
+
"cursor-vscode": "cursor",
|
|
14
|
+
"Visual-Studio-Code": "vscode-copilot",
|
|
15
|
+
"Codex": "codex",
|
|
16
|
+
"codex-mcp-client": "codex",
|
|
17
|
+
"Kiro CLI": "kiro",
|
|
18
|
+
};
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* - Claude Code: CLAUDE_PROJECT_DIR, CLAUDE_SESSION_ID | ~/.claude/
|
|
11
11
|
* - Gemini CLI: GEMINI_PROJECT_DIR (hooks), GEMINI_CLI (MCP) | ~/.gemini/
|
|
12
12
|
* - OpenCode: OPENCODE, OPENCODE_PID | ~/.config/opencode/
|
|
13
|
+
* - OpenClaw: OPENCLAW_HOME, OPENCLAW_PROJECT_DIR | ~/.openclaw/
|
|
13
14
|
* - Codex CLI: CODEX_CI, CODEX_THREAD_ID | ~/.codex/
|
|
14
15
|
* - Cursor: CURSOR_TRACE_ID (MCP), CURSOR_CLI (terminal) | ~/.cursor/
|
|
15
16
|
* - VS Code Copilot: VSCODE_PID, VSCODE_CWD | ~/.vscode/
|
|
@@ -17,8 +18,14 @@
|
|
|
17
18
|
import type { PlatformId, DetectionSignal, HookAdapter } from "./types.js";
|
|
18
19
|
/**
|
|
19
20
|
* Detect the current platform by checking env vars and config dirs.
|
|
21
|
+
*
|
|
22
|
+
* @param clientInfo - Optional MCP clientInfo from initialize handshake.
|
|
23
|
+
* When provided, takes highest priority (zero-config detection).
|
|
20
24
|
*/
|
|
21
|
-
export declare function detectPlatform(
|
|
25
|
+
export declare function detectPlatform(clientInfo?: {
|
|
26
|
+
name: string;
|
|
27
|
+
version?: string;
|
|
28
|
+
}): DetectionSignal;
|
|
22
29
|
/**
|
|
23
30
|
* Get the adapter instance for a given platform.
|
|
24
31
|
* Lazily imports platform-specific adapter modules.
|
package/build/adapters/detect.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* - Claude Code: CLAUDE_PROJECT_DIR, CLAUDE_SESSION_ID | ~/.claude/
|
|
11
11
|
* - Gemini CLI: GEMINI_PROJECT_DIR (hooks), GEMINI_CLI (MCP) | ~/.gemini/
|
|
12
12
|
* - OpenCode: OPENCODE, OPENCODE_PID | ~/.config/opencode/
|
|
13
|
+
* - OpenClaw: OPENCLAW_HOME, OPENCLAW_PROJECT_DIR | ~/.openclaw/
|
|
13
14
|
* - Codex CLI: CODEX_CI, CODEX_THREAD_ID | ~/.codex/
|
|
14
15
|
* - Cursor: CURSOR_TRACE_ID (MCP), CURSOR_CLI (terminal) | ~/.cursor/
|
|
15
16
|
* - VS Code Copilot: VSCODE_PID, VSCODE_CWD | ~/.vscode/
|
|
@@ -17,10 +18,40 @@
|
|
|
17
18
|
import { existsSync } from "node:fs";
|
|
18
19
|
import { resolve } from "node:path";
|
|
19
20
|
import { homedir } from "node:os";
|
|
21
|
+
import { CLIENT_NAME_TO_PLATFORM } from "./client-map.js";
|
|
20
22
|
/**
|
|
21
23
|
* Detect the current platform by checking env vars and config dirs.
|
|
24
|
+
*
|
|
25
|
+
* @param clientInfo - Optional MCP clientInfo from initialize handshake.
|
|
26
|
+
* When provided, takes highest priority (zero-config detection).
|
|
22
27
|
*/
|
|
23
|
-
export function detectPlatform() {
|
|
28
|
+
export function detectPlatform(clientInfo) {
|
|
29
|
+
// ── Highest priority: MCP clientInfo ──────────────────
|
|
30
|
+
if (clientInfo?.name) {
|
|
31
|
+
const platform = CLIENT_NAME_TO_PLATFORM[clientInfo.name];
|
|
32
|
+
if (platform) {
|
|
33
|
+
return {
|
|
34
|
+
platform,
|
|
35
|
+
confidence: "high",
|
|
36
|
+
reason: `MCP clientInfo.name="${clientInfo.name}"`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// ── Explicit platform override ────────────────────────
|
|
41
|
+
const platformOverride = process.env.CONTEXT_MODE_PLATFORM;
|
|
42
|
+
if (platformOverride) {
|
|
43
|
+
const validPlatforms = [
|
|
44
|
+
"claude-code", "gemini-cli", "opencode", "codex",
|
|
45
|
+
"vscode-copilot", "cursor", "antigravity", "kiro",
|
|
46
|
+
];
|
|
47
|
+
if (validPlatforms.includes(platformOverride)) {
|
|
48
|
+
return {
|
|
49
|
+
platform: platformOverride,
|
|
50
|
+
confidence: "high",
|
|
51
|
+
reason: `CONTEXT_MODE_PLATFORM=${platformOverride} override`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
24
55
|
// ── High confidence: environment variables ─────────────
|
|
25
56
|
if (process.env.CLAUDE_PROJECT_DIR || process.env.CLAUDE_SESSION_ID) {
|
|
26
57
|
return {
|
|
@@ -36,6 +67,13 @@ export function detectPlatform() {
|
|
|
36
67
|
reason: "GEMINI_PROJECT_DIR or GEMINI_CLI env var set",
|
|
37
68
|
};
|
|
38
69
|
}
|
|
70
|
+
if (process.env.OPENCLAW_HOME || process.env.OPENCLAW_PROJECT_DIR) {
|
|
71
|
+
return {
|
|
72
|
+
platform: "openclaw",
|
|
73
|
+
confidence: "high",
|
|
74
|
+
reason: "OPENCLAW_HOME or OPENCLAW_PROJECT_DIR env var set",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
39
77
|
if (process.env.OPENCODE || process.env.OPENCODE_PID) {
|
|
40
78
|
return {
|
|
41
79
|
platform: "opencode",
|
|
@@ -94,6 +132,13 @@ export function detectPlatform() {
|
|
|
94
132
|
reason: "~/.cursor/ directory exists",
|
|
95
133
|
};
|
|
96
134
|
}
|
|
135
|
+
if (existsSync(resolve(home, ".openclaw"))) {
|
|
136
|
+
return {
|
|
137
|
+
platform: "openclaw",
|
|
138
|
+
confidence: "medium",
|
|
139
|
+
reason: "~/.openclaw/ directory exists",
|
|
140
|
+
};
|
|
141
|
+
}
|
|
97
142
|
if (existsSync(resolve(home, ".config", "opencode"))) {
|
|
98
143
|
return {
|
|
99
144
|
platform: "opencode",
|
|
@@ -127,6 +172,10 @@ export async function getAdapter(platform) {
|
|
|
127
172
|
const { OpenCodeAdapter } = await import("./opencode/index.js");
|
|
128
173
|
return new OpenCodeAdapter();
|
|
129
174
|
}
|
|
175
|
+
case "openclaw": {
|
|
176
|
+
const { OpenClawAdapter } = await import("./openclaw/index.js");
|
|
177
|
+
return new OpenClawAdapter();
|
|
178
|
+
}
|
|
130
179
|
case "codex": {
|
|
131
180
|
const { CodexAdapter } = await import("./codex/index.js");
|
|
132
181
|
return new CodexAdapter();
|
|
@@ -139,6 +188,14 @@ export async function getAdapter(platform) {
|
|
|
139
188
|
const { CursorAdapter } = await import("./cursor/index.js");
|
|
140
189
|
return new CursorAdapter();
|
|
141
190
|
}
|
|
191
|
+
case "antigravity": {
|
|
192
|
+
const { AntigravityAdapter } = await import("./antigravity/index.js");
|
|
193
|
+
return new AntigravityAdapter();
|
|
194
|
+
}
|
|
195
|
+
case "kiro": {
|
|
196
|
+
const { KiroAdapter } = await import("./kiro/index.js");
|
|
197
|
+
return new KiroAdapter();
|
|
198
|
+
}
|
|
142
199
|
default: {
|
|
143
200
|
// Unsupported platform — fall back to Claude Code adapter
|
|
144
201
|
// (MCP server works everywhere, hooks may not)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/kiro/hooks — Kiro CLI hook definitions and matchers.
|
|
3
|
+
*
|
|
4
|
+
* Kiro CLI hook system reference:
|
|
5
|
+
* - Hooks are in agent config files (~/.kiro/agents/<name>.json) under "hooks" key
|
|
6
|
+
* - Each hook type maps to an array of { matcher, command } entries
|
|
7
|
+
* - Hook names: preToolUse, postToolUse, agentSpawn, userPromptSubmit
|
|
8
|
+
* - Input: JSON on stdin
|
|
9
|
+
* - Output: exit codes (0=allow, 2=block) + stdout/stderr
|
|
10
|
+
*
|
|
11
|
+
* Source: https://kiro.dev/docs/cli/custom-agents/configuration-reference#hooks-field
|
|
12
|
+
*/
|
|
13
|
+
export declare const HOOK_TYPES: {
|
|
14
|
+
readonly PRE_TOOL_USE: "preToolUse";
|
|
15
|
+
readonly POST_TOOL_USE: "postToolUse";
|
|
16
|
+
readonly AGENT_SPAWN: "agentSpawn";
|
|
17
|
+
readonly USER_PROMPT_SUBMIT: "userPromptSubmit";
|
|
18
|
+
};
|
|
19
|
+
export type HookType = (typeof HOOK_TYPES)[keyof typeof HOOK_TYPES];
|
|
20
|
+
export declare const HOOK_SCRIPTS: Record<string, string>;
|
|
21
|
+
export declare const REQUIRED_HOOKS: string[];
|
|
22
|
+
export declare const OPTIONAL_HOOKS: string[];
|
|
23
|
+
/**
|
|
24
|
+
* Check if a hook entry points to a context-mode hook script.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isContextModeHook(entry: {
|
|
27
|
+
command?: string;
|
|
28
|
+
}, hookType: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Build the hook command string for a given hook type.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildHookCommand(hookType: string, pluginRoot?: string): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/kiro/hooks — Kiro CLI hook definitions and matchers.
|
|
3
|
+
*
|
|
4
|
+
* Kiro CLI hook system reference:
|
|
5
|
+
* - Hooks are in agent config files (~/.kiro/agents/<name>.json) under "hooks" key
|
|
6
|
+
* - Each hook type maps to an array of { matcher, command } entries
|
|
7
|
+
* - Hook names: preToolUse, postToolUse, agentSpawn, userPromptSubmit
|
|
8
|
+
* - Input: JSON on stdin
|
|
9
|
+
* - Output: exit codes (0=allow, 2=block) + stdout/stderr
|
|
10
|
+
*
|
|
11
|
+
* Source: https://kiro.dev/docs/cli/custom-agents/configuration-reference#hooks-field
|
|
12
|
+
*/
|
|
13
|
+
export const HOOK_TYPES = {
|
|
14
|
+
PRE_TOOL_USE: "preToolUse",
|
|
15
|
+
POST_TOOL_USE: "postToolUse",
|
|
16
|
+
AGENT_SPAWN: "agentSpawn",
|
|
17
|
+
USER_PROMPT_SUBMIT: "userPromptSubmit",
|
|
18
|
+
};
|
|
19
|
+
export const HOOK_SCRIPTS = {
|
|
20
|
+
[HOOK_TYPES.PRE_TOOL_USE]: "pretooluse.mjs",
|
|
21
|
+
[HOOK_TYPES.POST_TOOL_USE]: "posttooluse.mjs",
|
|
22
|
+
};
|
|
23
|
+
export const REQUIRED_HOOKS = [
|
|
24
|
+
HOOK_TYPES.PRE_TOOL_USE,
|
|
25
|
+
];
|
|
26
|
+
export const OPTIONAL_HOOKS = [
|
|
27
|
+
HOOK_TYPES.POST_TOOL_USE,
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* Check if a hook entry points to a context-mode hook script.
|
|
31
|
+
*/
|
|
32
|
+
export function isContextModeHook(entry, hookType) {
|
|
33
|
+
const scriptName = HOOK_SCRIPTS[hookType];
|
|
34
|
+
if (!scriptName)
|
|
35
|
+
return false;
|
|
36
|
+
return entry.command?.includes(scriptName) || entry.command?.includes("context-mode hook kiro") || false;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build the hook command string for a given hook type.
|
|
40
|
+
*/
|
|
41
|
+
export function buildHookCommand(hookType, pluginRoot) {
|
|
42
|
+
const scriptName = HOOK_SCRIPTS[hookType];
|
|
43
|
+
if (pluginRoot && scriptName) {
|
|
44
|
+
return `node "${pluginRoot}/hooks/kiro/${scriptName}"`;
|
|
45
|
+
}
|
|
46
|
+
return `context-mode hook kiro ${hookType.toLowerCase()}`;
|
|
47
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/kiro — Kiro IDE/CLI platform adapter.
|
|
3
|
+
*
|
|
4
|
+
* Implements HookAdapter for Kiro's hooks-capable paradigm (json-stdio).
|
|
5
|
+
*
|
|
6
|
+
* Kiro specifics:
|
|
7
|
+
* - Hooks via agent config files (~/.kiro/agents/<name>.json)
|
|
8
|
+
* - Config: ~/.kiro/settings/mcp_config.json (JSON format)
|
|
9
|
+
* - MCP: full support via mcpServers in mcp_config.json
|
|
10
|
+
* - Hook exit codes: 0=allow, 2=block
|
|
11
|
+
* - Cannot modify tool input (exit codes only)
|
|
12
|
+
* - Session dir: ~/.kiro/context-mode/sessions/
|
|
13
|
+
* - Routing file: KIRO.md
|
|
14
|
+
*
|
|
15
|
+
* Sources:
|
|
16
|
+
* - MCP config: https://kiro.dev/docs/mcp/configuration/
|
|
17
|
+
* - clientInfo.name: https://github.com/kirodotdev/Kiro/issues/5205 ("Kiro CLI")
|
|
18
|
+
* - CLI hooks: https://kiro.dev/docs/cli/custom-agents/configuration-reference#hooks-field
|
|
19
|
+
*/
|
|
20
|
+
import type { HookAdapter, HookParadigm, PlatformCapabilities, DiagnosticResult, PreToolUseEvent, PostToolUseEvent, PreCompactEvent, SessionStartEvent, PreToolUseResponse, PostToolUseResponse, PreCompactResponse, SessionStartResponse, HookRegistration, RoutingInstructionsConfig } from "../types.js";
|
|
21
|
+
export declare class KiroAdapter implements HookAdapter {
|
|
22
|
+
readonly name = "Kiro";
|
|
23
|
+
readonly paradigm: HookParadigm;
|
|
24
|
+
readonly capabilities: PlatformCapabilities;
|
|
25
|
+
parsePreToolUseInput(raw: unknown): PreToolUseEvent;
|
|
26
|
+
parsePostToolUseInput(raw: unknown): PostToolUseEvent;
|
|
27
|
+
parsePreCompactInput(_raw: unknown): PreCompactEvent;
|
|
28
|
+
parseSessionStartInput(_raw: unknown): SessionStartEvent;
|
|
29
|
+
formatPreToolUseResponse(response: PreToolUseResponse): unknown;
|
|
30
|
+
formatPostToolUseResponse(_response: PostToolUseResponse): unknown;
|
|
31
|
+
formatPreCompactResponse(_response: PreCompactResponse): unknown;
|
|
32
|
+
formatSessionStartResponse(_response: SessionStartResponse): unknown;
|
|
33
|
+
getSettingsPath(): string;
|
|
34
|
+
getSessionDir(): string;
|
|
35
|
+
getSessionDBPath(projectDir: string): string;
|
|
36
|
+
getSessionEventsPath(projectDir: string): string;
|
|
37
|
+
generateHookConfig(pluginRoot: string): HookRegistration;
|
|
38
|
+
readSettings(): Record<string, unknown> | null;
|
|
39
|
+
writeSettings(settings: Record<string, unknown>): void;
|
|
40
|
+
validateHooks(pluginRoot: string): DiagnosticResult[];
|
|
41
|
+
checkPluginRegistration(): DiagnosticResult;
|
|
42
|
+
getInstalledVersion(): string;
|
|
43
|
+
configureAllHooks(pluginRoot: string): string[];
|
|
44
|
+
backupSettings(): string | null;
|
|
45
|
+
setHookPermissions(_pluginRoot: string): string[];
|
|
46
|
+
updatePluginRegistry(_pluginRoot: string, _version: string): void;
|
|
47
|
+
getRoutingInstructionsConfig(): RoutingInstructionsConfig;
|
|
48
|
+
writeRoutingInstructions(projectDir: string, pluginRoot: string): string | null;
|
|
49
|
+
getRoutingInstructions(): string;
|
|
50
|
+
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/kiro — Kiro IDE/CLI platform adapter.
|
|
3
|
+
*
|
|
4
|
+
* Implements HookAdapter for Kiro's hooks-capable paradigm (json-stdio).
|
|
5
|
+
*
|
|
6
|
+
* Kiro specifics:
|
|
7
|
+
* - Hooks via agent config files (~/.kiro/agents/<name>.json)
|
|
8
|
+
* - Config: ~/.kiro/settings/mcp_config.json (JSON format)
|
|
9
|
+
* - MCP: full support via mcpServers in mcp_config.json
|
|
10
|
+
* - Hook exit codes: 0=allow, 2=block
|
|
11
|
+
* - Cannot modify tool input (exit codes only)
|
|
12
|
+
* - Session dir: ~/.kiro/context-mode/sessions/
|
|
13
|
+
* - Routing file: KIRO.md
|
|
14
|
+
*
|
|
15
|
+
* Sources:
|
|
16
|
+
* - MCP config: https://kiro.dev/docs/mcp/configuration/
|
|
17
|
+
* - clientInfo.name: https://github.com/kirodotdev/Kiro/issues/5205 ("Kiro CLI")
|
|
18
|
+
* - CLI hooks: https://kiro.dev/docs/cli/custom-agents/configuration-reference#hooks-field
|
|
19
|
+
*/
|
|
20
|
+
import { createHash } from "node:crypto";
|
|
21
|
+
import { readFileSync, writeFileSync, mkdirSync, copyFileSync, accessSync, constants, } from "node:fs";
|
|
22
|
+
import { resolve, join, dirname } from "node:path";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
import { homedir } from "node:os";
|
|
25
|
+
import { HOOK_TYPES as KIRO_HOOK_TYPES, buildHookCommand as buildKiroHookCommand, isContextModeHook as isKiroContextModeHook, } from "./hooks.js";
|
|
26
|
+
// ─────────────────────────────────────────────────────────
|
|
27
|
+
// Adapter implementation
|
|
28
|
+
// ─────────────────────────────────────────────────────────
|
|
29
|
+
export class KiroAdapter {
|
|
30
|
+
name = "Kiro";
|
|
31
|
+
paradigm = "json-stdio";
|
|
32
|
+
capabilities = {
|
|
33
|
+
preToolUse: true,
|
|
34
|
+
postToolUse: true,
|
|
35
|
+
preCompact: false,
|
|
36
|
+
sessionStart: false,
|
|
37
|
+
canModifyArgs: false, // Kiro CLI uses exit codes, can't modify input
|
|
38
|
+
canModifyOutput: false,
|
|
39
|
+
canInjectSessionContext: false,
|
|
40
|
+
};
|
|
41
|
+
// ── Input parsing ──────────────────────────────────────
|
|
42
|
+
parsePreToolUseInput(raw) {
|
|
43
|
+
const input = raw;
|
|
44
|
+
return {
|
|
45
|
+
toolName: input.tool_name ?? "",
|
|
46
|
+
toolInput: input.tool_input ?? {},
|
|
47
|
+
sessionId: `pid-${process.ppid}`,
|
|
48
|
+
projectDir: input.cwd ?? process.cwd(),
|
|
49
|
+
raw,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
parsePostToolUseInput(raw) {
|
|
53
|
+
const input = raw;
|
|
54
|
+
const toolResponse = input.tool_response;
|
|
55
|
+
return {
|
|
56
|
+
toolName: input.tool_name ?? "",
|
|
57
|
+
toolInput: input.tool_input ?? {},
|
|
58
|
+
toolOutput: typeof toolResponse === "string"
|
|
59
|
+
? toolResponse
|
|
60
|
+
: JSON.stringify(toolResponse ?? ""),
|
|
61
|
+
sessionId: `pid-${process.ppid}`,
|
|
62
|
+
projectDir: input.cwd ?? process.cwd(),
|
|
63
|
+
raw,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
parsePreCompactInput(_raw) {
|
|
67
|
+
throw new Error("Kiro does not support PreCompact hooks");
|
|
68
|
+
}
|
|
69
|
+
parseSessionStartInput(_raw) {
|
|
70
|
+
throw new Error("Kiro does not support SessionStart hooks (yet)");
|
|
71
|
+
}
|
|
72
|
+
// ── Response formatting ────────────────────────────────
|
|
73
|
+
formatPreToolUseResponse(response) {
|
|
74
|
+
// Kiro CLI uses exit codes — this format is for adapter interface completeness.
|
|
75
|
+
// The actual hook script handles exit codes directly.
|
|
76
|
+
switch (response.decision) {
|
|
77
|
+
case "deny":
|
|
78
|
+
return { exitCode: 2, stderr: response.reason ?? "Blocked by context-mode" };
|
|
79
|
+
case "context":
|
|
80
|
+
return { exitCode: 0, stdout: response.additionalContext ?? "" };
|
|
81
|
+
default:
|
|
82
|
+
return undefined; // allow — no output needed
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
formatPostToolUseResponse(_response) {
|
|
86
|
+
return undefined; // PostToolUse is non-blocking
|
|
87
|
+
}
|
|
88
|
+
formatPreCompactResponse(_response) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
formatSessionStartResponse(_response) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
// ── Configuration ──────────────────────────────────────
|
|
95
|
+
getSettingsPath() {
|
|
96
|
+
return resolve(homedir(), ".kiro", "settings", "mcp_config.json");
|
|
97
|
+
}
|
|
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
|
+
generateHookConfig(pluginRoot) {
|
|
118
|
+
// Kiro CLI hook config format: { preToolUse: [{ matcher, command }] }
|
|
119
|
+
// Note: This generates the entries for agent config files
|
|
120
|
+
return {
|
|
121
|
+
[KIRO_HOOK_TYPES.PRE_TOOL_USE]: [{
|
|
122
|
+
matcher: "*",
|
|
123
|
+
hooks: [{ type: "command", command: buildKiroHookCommand(KIRO_HOOK_TYPES.PRE_TOOL_USE, pluginRoot) }],
|
|
124
|
+
}],
|
|
125
|
+
[KIRO_HOOK_TYPES.POST_TOOL_USE]: [{
|
|
126
|
+
matcher: "*",
|
|
127
|
+
hooks: [{ type: "command", command: buildKiroHookCommand(KIRO_HOOK_TYPES.POST_TOOL_USE, pluginRoot) }],
|
|
128
|
+
}],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
readSettings() {
|
|
132
|
+
try {
|
|
133
|
+
const raw = readFileSync(this.getSettingsPath(), "utf-8");
|
|
134
|
+
return JSON.parse(raw);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
writeSettings(settings) {
|
|
141
|
+
const settingsPath = this.getSettingsPath();
|
|
142
|
+
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
143
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
144
|
+
}
|
|
145
|
+
// ── Diagnostics (doctor) ─────────────────────────────────
|
|
146
|
+
validateHooks(pluginRoot) {
|
|
147
|
+
const results = [];
|
|
148
|
+
const defaultAgent = resolve(homedir(), ".kiro", "agents", "default.json");
|
|
149
|
+
try {
|
|
150
|
+
const config = JSON.parse(readFileSync(defaultAgent, "utf-8"));
|
|
151
|
+
const hooks = config.hooks ?? {};
|
|
152
|
+
// Check required hooks
|
|
153
|
+
for (const hookType of [KIRO_HOOK_TYPES.PRE_TOOL_USE]) {
|
|
154
|
+
const entries = hooks[hookType] ?? [];
|
|
155
|
+
const found = entries.some((e) => isKiroContextModeHook(e, hookType));
|
|
156
|
+
results.push({
|
|
157
|
+
check: `Hook: ${hookType}`,
|
|
158
|
+
status: found ? "pass" : "fail",
|
|
159
|
+
message: found
|
|
160
|
+
? `context-mode ${hookType} hook found`
|
|
161
|
+
: `context-mode ${hookType} hook not configured`,
|
|
162
|
+
...(found ? {} : { fix: `Run: context-mode upgrade` }),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
// Check optional hooks
|
|
166
|
+
for (const hookType of [KIRO_HOOK_TYPES.POST_TOOL_USE]) {
|
|
167
|
+
const entries = hooks[hookType] ?? [];
|
|
168
|
+
const found = entries.some((e) => isKiroContextModeHook(e, hookType));
|
|
169
|
+
results.push({
|
|
170
|
+
check: `Hook: ${hookType}`,
|
|
171
|
+
status: found ? "pass" : "warn",
|
|
172
|
+
message: found
|
|
173
|
+
? `context-mode ${hookType} hook found`
|
|
174
|
+
: `context-mode ${hookType} hook not configured (optional)`,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
results.push({
|
|
180
|
+
check: "Hook configuration",
|
|
181
|
+
status: "warn",
|
|
182
|
+
message: "Could not read ~/.kiro/agents/default.json",
|
|
183
|
+
fix: "Run: context-mode upgrade",
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return results;
|
|
187
|
+
}
|
|
188
|
+
checkPluginRegistration() {
|
|
189
|
+
try {
|
|
190
|
+
const raw = readFileSync(this.getSettingsPath(), "utf-8");
|
|
191
|
+
const config = JSON.parse(raw);
|
|
192
|
+
const mcpServers = config?.mcpServers ?? {};
|
|
193
|
+
if ("context-mode" in mcpServers) {
|
|
194
|
+
return {
|
|
195
|
+
check: "MCP registration",
|
|
196
|
+
status: "pass",
|
|
197
|
+
message: "context-mode found in mcpServers config",
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
check: "MCP registration",
|
|
202
|
+
status: "fail",
|
|
203
|
+
message: "context-mode not found in mcpServers",
|
|
204
|
+
fix: "Add context-mode to mcpServers in ~/.kiro/settings/mcp_config.json",
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
return {
|
|
209
|
+
check: "MCP registration",
|
|
210
|
+
status: "warn",
|
|
211
|
+
message: "Could not read ~/.kiro/settings/mcp_config.json",
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
getInstalledVersion() {
|
|
216
|
+
try {
|
|
217
|
+
const pkgPath = resolve(homedir(), ".kiro", "extensions", "context-mode", "package.json");
|
|
218
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
219
|
+
return pkg.version ?? "unknown";
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return "not installed";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// ── Upgrade ────────────────────────────────────────────
|
|
226
|
+
configureAllHooks(pluginRoot) {
|
|
227
|
+
const changes = [];
|
|
228
|
+
const configDir = resolve(homedir(), ".kiro", "agents");
|
|
229
|
+
const defaultAgent = resolve(configDir, "default.json");
|
|
230
|
+
try {
|
|
231
|
+
mkdirSync(configDir, { recursive: true });
|
|
232
|
+
let config = {};
|
|
233
|
+
try {
|
|
234
|
+
config = JSON.parse(readFileSync(defaultAgent, "utf-8"));
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
// No existing config — create new
|
|
238
|
+
}
|
|
239
|
+
const hooks = (config.hooks ?? {});
|
|
240
|
+
// Add preToolUse hook if not present
|
|
241
|
+
const preToolUseEntries = (hooks[KIRO_HOOK_TYPES.PRE_TOOL_USE] ?? []);
|
|
242
|
+
if (!preToolUseEntries.some(e => isKiroContextModeHook(e, KIRO_HOOK_TYPES.PRE_TOOL_USE))) {
|
|
243
|
+
preToolUseEntries.push({
|
|
244
|
+
matcher: "*",
|
|
245
|
+
command: buildKiroHookCommand(KIRO_HOOK_TYPES.PRE_TOOL_USE, pluginRoot),
|
|
246
|
+
});
|
|
247
|
+
hooks[KIRO_HOOK_TYPES.PRE_TOOL_USE] = preToolUseEntries;
|
|
248
|
+
changes.push(`Added ${KIRO_HOOK_TYPES.PRE_TOOL_USE} hook to ${defaultAgent}`);
|
|
249
|
+
}
|
|
250
|
+
// Add postToolUse hook if not present
|
|
251
|
+
const postToolUseEntries = (hooks[KIRO_HOOK_TYPES.POST_TOOL_USE] ?? []);
|
|
252
|
+
if (!postToolUseEntries.some(e => isKiroContextModeHook(e, KIRO_HOOK_TYPES.POST_TOOL_USE))) {
|
|
253
|
+
postToolUseEntries.push({
|
|
254
|
+
matcher: "*",
|
|
255
|
+
command: buildKiroHookCommand(KIRO_HOOK_TYPES.POST_TOOL_USE, pluginRoot),
|
|
256
|
+
});
|
|
257
|
+
hooks[KIRO_HOOK_TYPES.POST_TOOL_USE] = postToolUseEntries;
|
|
258
|
+
changes.push(`Added ${KIRO_HOOK_TYPES.POST_TOOL_USE} hook to ${defaultAgent}`);
|
|
259
|
+
}
|
|
260
|
+
config.hooks = hooks;
|
|
261
|
+
writeFileSync(defaultAgent, JSON.stringify(config, null, 2), "utf-8");
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
changes.push(`Failed to configure hooks: ${err.message}`);
|
|
265
|
+
}
|
|
266
|
+
return changes;
|
|
267
|
+
}
|
|
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
|
+
setHookPermissions(_pluginRoot) {
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
updatePluginRegistry(_pluginRoot, _version) {
|
|
284
|
+
// Kiro plugin registry is managed via mcp_config.json
|
|
285
|
+
}
|
|
286
|
+
// ── Routing Instructions (soft enforcement) ────────────
|
|
287
|
+
getRoutingInstructionsConfig() {
|
|
288
|
+
return {
|
|
289
|
+
fileName: "KIRO.md",
|
|
290
|
+
globalPath: resolve(homedir(), ".kiro", "KIRO.md"),
|
|
291
|
+
projectRelativePath: "KIRO.md",
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
writeRoutingInstructions(projectDir, pluginRoot) {
|
|
295
|
+
const config = this.getRoutingInstructionsConfig();
|
|
296
|
+
const targetPath = resolve(projectDir, config.projectRelativePath);
|
|
297
|
+
const sourcePath = resolve(pluginRoot, "configs", "kiro", config.fileName);
|
|
298
|
+
try {
|
|
299
|
+
const content = readFileSync(sourcePath, "utf-8");
|
|
300
|
+
try {
|
|
301
|
+
const existing = readFileSync(targetPath, "utf-8");
|
|
302
|
+
if (existing.includes("context-mode"))
|
|
303
|
+
return null;
|
|
304
|
+
writeFileSync(targetPath, existing.trimEnd() + "\n\n" + content, "utf-8");
|
|
305
|
+
return targetPath;
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
writeFileSync(targetPath, content, "utf-8");
|
|
309
|
+
return targetPath;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
getRoutingInstructions() {
|
|
317
|
+
const instructionsPath = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "configs", "kiro", "KIRO.md");
|
|
318
|
+
try {
|
|
319
|
+
return readFileSync(instructionsPath, "utf-8");
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
return "# context-mode\n\nUse context-mode MCP tools (execute, execute_file, batch_execute, fetch_and_index, search) instead of run_command/view_file for data-heavy operations.";
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/openclaw/config — Thin re-exports from OpenClawAdapter.
|
|
3
|
+
*
|
|
4
|
+
* This module exists for backward compatibility. All logic lives in the
|
|
5
|
+
* adapter class (index.ts). New code should use getAdapter() from detect.ts.
|
|
6
|
+
*/
|
|
7
|
+
export { OpenClawAdapter } from "./index.js";
|
|
8
|
+
export { HOOK_EVENTS, LIFECYCLE_HOOKS, REQUIRED_HOOKS, OPTIONAL_HOOKS } from "./hooks.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/openclaw/config — Thin re-exports from OpenClawAdapter.
|
|
3
|
+
*
|
|
4
|
+
* This module exists for backward compatibility. All logic lives in the
|
|
5
|
+
* adapter class (index.ts). New code should use getAdapter() from detect.ts.
|
|
6
|
+
*/
|
|
7
|
+
export { OpenClawAdapter } from "./index.js";
|
|
8
|
+
export { HOOK_EVENTS, LIFECYCLE_HOOKS, REQUIRED_HOOKS, OPTIONAL_HOOKS } from "./hooks.js";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/openclaw/hooks — OpenClaw hook definitions and validators.
|
|
3
|
+
*
|
|
4
|
+
* Defines the hook types and validation helpers specific to OpenClaw's
|
|
5
|
+
* TypeScript plugin paradigm. This module is used by:
|
|
6
|
+
* - CLI setup/upgrade commands (to configure plugin in openclaw.json)
|
|
7
|
+
* - Doctor command (to validate plugin configuration)
|
|
8
|
+
*
|
|
9
|
+
* OpenClaw hook system reference:
|
|
10
|
+
* - I/O: TS plugin functions via api.registerHook() and api.on()
|
|
11
|
+
* - Hook events: tool_call:before, tool_call:after, command:new, command:reset
|
|
12
|
+
* - Lifecycle: session_start, before_compaction, after_compaction, before_prompt_build, before_model_resolve
|
|
13
|
+
* - Context engine: api.registerContextEngine() with ownsCompaction
|
|
14
|
+
* - Blocking: return { block: true, blockReason } from tool_call:before
|
|
15
|
+
* - Config: openclaw.json plugins.entries, ~/.openclaw/extensions/
|
|
16
|
+
*/
|
|
17
|
+
/** OpenClaw hook event names (registered via api.registerHook). */
|
|
18
|
+
export declare const HOOK_EVENTS: {
|
|
19
|
+
readonly TOOL_CALL_BEFORE: "tool_call:before";
|
|
20
|
+
readonly TOOL_CALL_AFTER: "tool_call:after";
|
|
21
|
+
readonly COMMAND_NEW: "command:new";
|
|
22
|
+
readonly COMMAND_RESET: "command:reset";
|
|
23
|
+
readonly COMMAND_STOP: "command:stop";
|
|
24
|
+
};
|
|
25
|
+
/** OpenClaw lifecycle hook names (registered via api.on). */
|
|
26
|
+
export declare const LIFECYCLE_HOOKS: {
|
|
27
|
+
readonly SESSION_START: "session_start";
|
|
28
|
+
readonly BEFORE_COMPACTION: "before_compaction";
|
|
29
|
+
readonly AFTER_COMPACTION: "after_compaction";
|
|
30
|
+
readonly BEFORE_PROMPT_BUILD: "before_prompt_build";
|
|
31
|
+
readonly BEFORE_MODEL_RESOLVE: "before_model_resolve";
|
|
32
|
+
readonly BEFORE_AGENT_START: "before_agent_start";
|
|
33
|
+
};
|
|
34
|
+
export type HookEvent = (typeof HOOK_EVENTS)[keyof typeof HOOK_EVENTS];
|
|
35
|
+
export type LifecycleHook = (typeof LIFECYCLE_HOOKS)[keyof typeof LIFECYCLE_HOOKS];
|
|
36
|
+
/**
|
|
37
|
+
* Required hooks that must be active for context-mode to function.
|
|
38
|
+
* OpenClaw registers these via api.registerHook() in the plugin entry point.
|
|
39
|
+
*/
|
|
40
|
+
export declare const REQUIRED_HOOKS: HookEvent[];
|
|
41
|
+
/**
|
|
42
|
+
* Optional hooks that enhance functionality but aren't critical.
|
|
43
|
+
* command:new provides session cleanup; context engine handles compaction.
|
|
44
|
+
*/
|
|
45
|
+
export declare const OPTIONAL_HOOKS: HookEvent[];
|
|
46
|
+
/**
|
|
47
|
+
* Check if a plugin entry is the context-mode plugin.
|
|
48
|
+
* OpenClaw plugins are registered by id in plugins.entries.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isContextModePlugin(pluginId: string): boolean;
|