@cleocode/adapters 2026.4.11 → 2026.4.13
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/package.json +3 -3
- package/src/__tests__/claude-code-adapter.test.ts +1 -3
- package/src/__tests__/cursor-adapter.test.ts +1 -3
- package/src/__tests__/opencode-adapter.test.ts +1 -3
- package/src/providers/claude-code/__tests__/adapter.test.ts +0 -12
- package/src/providers/claude-code/adapter.ts +0 -1
- package/src/providers/claude-code/install.ts +0 -1
- package/src/providers/codex/adapter.ts +0 -1
- package/src/providers/codex/install.ts +0 -1
- package/src/providers/cursor/__tests__/adapter.test.ts +0 -12
- package/src/providers/cursor/adapter.ts +0 -1
- package/src/providers/cursor/install.ts +0 -1
- package/src/providers/gemini-cli/adapter.ts +0 -1
- package/src/providers/gemini-cli/install.ts +0 -1
- package/src/providers/kimi/adapter.ts +0 -1
- package/src/providers/kimi/install.ts +0 -1
- package/src/providers/opencode/__tests__/adapter.test.ts +0 -12
- package/src/providers/opencode/adapter.ts +0 -1
- package/src/providers/opencode/install.ts +0 -1
- package/dist/index.d.ts +0 -23
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -3258
- package/dist/index.js.map +0 -7
- package/dist/providers/claude-code/adapter.d.ts +0 -95
- package/dist/providers/claude-code/adapter.d.ts.map +0 -1
- package/dist/providers/claude-code/adapter.js +0 -185
- package/dist/providers/claude-code/adapter.js.map +0 -1
- package/dist/providers/claude-code/context-monitor.d.ts +0 -35
- package/dist/providers/claude-code/context-monitor.d.ts.map +0 -1
- package/dist/providers/claude-code/context-monitor.js +0 -159
- package/dist/providers/claude-code/context-monitor.js.map +0 -1
- package/dist/providers/claude-code/hooks.d.ts +0 -146
- package/dist/providers/claude-code/hooks.d.ts.map +0 -1
- package/dist/providers/claude-code/hooks.js +0 -286
- package/dist/providers/claude-code/hooks.js.map +0 -1
- package/dist/providers/claude-code/index.d.ts +0 -39
- package/dist/providers/claude-code/index.d.ts.map +0 -1
- package/dist/providers/claude-code/index.js +0 -41
- package/dist/providers/claude-code/index.js.map +0 -1
- package/dist/providers/claude-code/install.d.ts +0 -67
- package/dist/providers/claude-code/install.d.ts.map +0 -1
- package/dist/providers/claude-code/install.js +0 -161
- package/dist/providers/claude-code/install.js.map +0 -1
- package/dist/providers/claude-code/paths.d.ts +0 -32
- package/dist/providers/claude-code/paths.d.ts.map +0 -1
- package/dist/providers/claude-code/paths.js +0 -41
- package/dist/providers/claude-code/paths.js.map +0 -1
- package/dist/providers/claude-code/spawn.d.ts +0 -67
- package/dist/providers/claude-code/spawn.d.ts.map +0 -1
- package/dist/providers/claude-code/spawn.js +0 -171
- package/dist/providers/claude-code/spawn.js.map +0 -1
- package/dist/providers/claude-code/statusline.d.ts +0 -68
- package/dist/providers/claude-code/statusline.d.ts.map +0 -1
- package/dist/providers/claude-code/statusline.js +0 -130
- package/dist/providers/claude-code/statusline.js.map +0 -1
- package/dist/providers/claude-code/task-sync.d.ts +0 -32
- package/dist/providers/claude-code/task-sync.d.ts.map +0 -1
- package/dist/providers/claude-code/task-sync.js +0 -119
- package/dist/providers/claude-code/task-sync.js.map +0 -1
- package/dist/providers/claude-code/transport.d.ts +0 -25
- package/dist/providers/claude-code/transport.d.ts.map +0 -1
- package/dist/providers/claude-code/transport.js +0 -29
- package/dist/providers/claude-code/transport.js.map +0 -1
- package/dist/providers/codex/adapter.d.ts +0 -83
- package/dist/providers/codex/adapter.d.ts.map +0 -1
- package/dist/providers/codex/adapter.js +0 -147
- package/dist/providers/codex/adapter.js.map +0 -1
- package/dist/providers/codex/hooks.d.ts +0 -91
- package/dist/providers/codex/hooks.d.ts.map +0 -1
- package/dist/providers/codex/hooks.js +0 -113
- package/dist/providers/codex/hooks.js.map +0 -1
- package/dist/providers/codex/index.d.ts +0 -37
- package/dist/providers/codex/index.d.ts.map +0 -1
- package/dist/providers/codex/index.js +0 -39
- package/dist/providers/codex/index.js.map +0 -1
- package/dist/providers/codex/install.d.ts +0 -65
- package/dist/providers/codex/install.d.ts.map +0 -1
- package/dist/providers/codex/install.js +0 -125
- package/dist/providers/codex/install.js.map +0 -1
- package/dist/providers/cursor/adapter.d.ts +0 -76
- package/dist/providers/cursor/adapter.d.ts.map +0 -1
- package/dist/providers/cursor/adapter.js +0 -152
- package/dist/providers/cursor/adapter.js.map +0 -1
- package/dist/providers/cursor/hooks.d.ts +0 -140
- package/dist/providers/cursor/hooks.d.ts.map +0 -1
- package/dist/providers/cursor/hooks.js +0 -208
- package/dist/providers/cursor/hooks.js.map +0 -1
- package/dist/providers/cursor/index.d.ts +0 -34
- package/dist/providers/cursor/index.d.ts.map +0 -1
- package/dist/providers/cursor/index.js +0 -36
- package/dist/providers/cursor/index.js.map +0 -1
- package/dist/providers/cursor/install.d.ts +0 -87
- package/dist/providers/cursor/install.d.ts.map +0 -1
- package/dist/providers/cursor/install.js +0 -181
- package/dist/providers/cursor/install.js.map +0 -1
- package/dist/providers/cursor/spawn.d.ts +0 -50
- package/dist/providers/cursor/spawn.d.ts.map +0 -1
- package/dist/providers/cursor/spawn.js +0 -59
- package/dist/providers/cursor/spawn.js.map +0 -1
- package/dist/providers/gemini-cli/adapter.d.ts +0 -84
- package/dist/providers/gemini-cli/adapter.d.ts.map +0 -1
- package/dist/providers/gemini-cli/adapter.js +0 -159
- package/dist/providers/gemini-cli/adapter.js.map +0 -1
- package/dist/providers/gemini-cli/hooks.d.ts +0 -99
- package/dist/providers/gemini-cli/hooks.d.ts.map +0 -1
- package/dist/providers/gemini-cli/hooks.js +0 -128
- package/dist/providers/gemini-cli/hooks.js.map +0 -1
- package/dist/providers/gemini-cli/index.d.ts +0 -37
- package/dist/providers/gemini-cli/index.d.ts.map +0 -1
- package/dist/providers/gemini-cli/index.js +0 -39
- package/dist/providers/gemini-cli/index.js.map +0 -1
- package/dist/providers/gemini-cli/install.d.ts +0 -65
- package/dist/providers/gemini-cli/install.d.ts.map +0 -1
- package/dist/providers/gemini-cli/install.js +0 -125
- package/dist/providers/gemini-cli/install.js.map +0 -1
- package/dist/providers/kimi/adapter.d.ts +0 -85
- package/dist/providers/kimi/adapter.d.ts.map +0 -1
- package/dist/providers/kimi/adapter.js +0 -146
- package/dist/providers/kimi/adapter.js.map +0 -1
- package/dist/providers/kimi/hooks.d.ts +0 -70
- package/dist/providers/kimi/hooks.d.ts.map +0 -1
- package/dist/providers/kimi/hooks.js +0 -79
- package/dist/providers/kimi/hooks.js.map +0 -1
- package/dist/providers/kimi/index.d.ts +0 -37
- package/dist/providers/kimi/index.d.ts.map +0 -1
- package/dist/providers/kimi/index.js +0 -39
- package/dist/providers/kimi/index.js.map +0 -1
- package/dist/providers/kimi/install.d.ts +0 -65
- package/dist/providers/kimi/install.d.ts.map +0 -1
- package/dist/providers/kimi/install.js +0 -125
- package/dist/providers/kimi/install.js.map +0 -1
- package/dist/providers/opencode/adapter.d.ts +0 -83
- package/dist/providers/opencode/adapter.d.ts.map +0 -1
- package/dist/providers/opencode/adapter.js +0 -167
- package/dist/providers/opencode/adapter.js.map +0 -1
- package/dist/providers/opencode/hooks.d.ts +0 -136
- package/dist/providers/opencode/hooks.d.ts.map +0 -1
- package/dist/providers/opencode/hooks.js +0 -206
- package/dist/providers/opencode/hooks.js.map +0 -1
- package/dist/providers/opencode/index.d.ts +0 -35
- package/dist/providers/opencode/index.d.ts.map +0 -1
- package/dist/providers/opencode/index.js +0 -37
- package/dist/providers/opencode/index.js.map +0 -1
- package/dist/providers/opencode/install.d.ts +0 -56
- package/dist/providers/opencode/install.d.ts.map +0 -1
- package/dist/providers/opencode/install.js +0 -116
- package/dist/providers/opencode/install.js.map +0 -1
- package/dist/providers/opencode/spawn.d.ts +0 -94
- package/dist/providers/opencode/spawn.d.ts.map +0 -1
- package/dist/providers/opencode/spawn.js +0 -241
- package/dist/providers/opencode/spawn.js.map +0 -1
- package/dist/providers/shared/transcript-reader.d.ts +0 -58
- package/dist/providers/shared/transcript-reader.d.ts.map +0 -1
- package/dist/providers/shared/transcript-reader.js +0 -124
- package/dist/providers/shared/transcript-reader.js.map +0 -1
- package/dist/registry.d.ts +0 -88
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js +0 -88
- package/dist/registry.js.map +0 -1
package/dist/index.js
DELETED
|
@@ -1,3258 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
-
var __esm = (fn, res) => function __init() {
|
|
4
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
-
};
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
// packages/adapters/src/providers/claude-code/paths.ts
|
|
12
|
-
import { homedir } from "node:os";
|
|
13
|
-
import { join } from "node:path";
|
|
14
|
-
var ClaudeCodePathProvider;
|
|
15
|
-
var init_paths = __esm({
|
|
16
|
-
"packages/adapters/src/providers/claude-code/paths.ts"() {
|
|
17
|
-
"use strict";
|
|
18
|
-
ClaudeCodePathProvider = class {
|
|
19
|
-
/** Get the provider's root configuration directory. */
|
|
20
|
-
getProviderDir() {
|
|
21
|
-
return process.env["CLAUDE_HOME"] ?? join(homedir(), ".claude");
|
|
22
|
-
}
|
|
23
|
-
/** Get the path to the provider's settings file, or null if unavailable. */
|
|
24
|
-
getSettingsPath() {
|
|
25
|
-
return process.env["CLAUDE_SETTINGS"] ?? join(this.getProviderDir(), "settings.json");
|
|
26
|
-
}
|
|
27
|
-
/** Get the directory where agents are installed, or null if unsupported. */
|
|
28
|
-
getAgentInstallDir() {
|
|
29
|
-
return join(this.getProviderDir(), "agents");
|
|
30
|
-
}
|
|
31
|
-
/** Get the path to the provider's memory database, or null if unsupported. */
|
|
32
|
-
getMemoryDbPath() {
|
|
33
|
-
return process.env["CLAUDE_MEM_DB"] ?? join(homedir(), ".claude-mem", "claude-mem.db");
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// packages/adapters/src/providers/claude-code/context-monitor.ts
|
|
40
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
41
|
-
import { mkdir } from "node:fs/promises";
|
|
42
|
-
import { homedir as homedir2 } from "node:os";
|
|
43
|
-
import { dirname, join as join2 } from "node:path";
|
|
44
|
-
function getContextStatusFromPercentage(percentage) {
|
|
45
|
-
if (percentage >= THRESHOLDS.EMERGENCY) return "emergency";
|
|
46
|
-
if (percentage >= THRESHOLDS.CRITICAL) return "critical";
|
|
47
|
-
if (percentage >= THRESHOLDS.CAUTION) return "caution";
|
|
48
|
-
if (percentage >= THRESHOLDS.WARNING) return "warning";
|
|
49
|
-
return "ok";
|
|
50
|
-
}
|
|
51
|
-
var THRESHOLDS, ClaudeCodeContextMonitorProvider;
|
|
52
|
-
var init_context_monitor = __esm({
|
|
53
|
-
"packages/adapters/src/providers/claude-code/context-monitor.ts"() {
|
|
54
|
-
"use strict";
|
|
55
|
-
init_paths();
|
|
56
|
-
THRESHOLDS = {
|
|
57
|
-
WARNING: 50,
|
|
58
|
-
CAUTION: 70,
|
|
59
|
-
CRITICAL: 85,
|
|
60
|
-
EMERGENCY: 95
|
|
61
|
-
};
|
|
62
|
-
ClaudeCodeContextMonitorProvider = class {
|
|
63
|
-
/** Path provider for resolving Claude Code directory locations. */
|
|
64
|
-
pathProvider = new ClaudeCodePathProvider();
|
|
65
|
-
/** Process raw context window JSON and return a formatted summary string. */
|
|
66
|
-
async processContextInput(input, cwd) {
|
|
67
|
-
const typed = input;
|
|
68
|
-
const contextSize = typed.context_window?.context_window_size ?? 2e5;
|
|
69
|
-
const usage = typed.context_window?.current_usage;
|
|
70
|
-
if (!usage) return "-- no data";
|
|
71
|
-
const inputTokens = usage.input_tokens ?? 0;
|
|
72
|
-
const outputTokens = usage.output_tokens ?? 0;
|
|
73
|
-
const cacheCreate = usage.cache_creation_input_tokens ?? 0;
|
|
74
|
-
const totalTokens = inputTokens + outputTokens + cacheCreate;
|
|
75
|
-
const percentage = Math.floor(totalTokens * 100 / contextSize);
|
|
76
|
-
const status = getContextStatusFromPercentage(percentage);
|
|
77
|
-
const cleoDir = cwd ? join2(cwd, ".cleo") : ".cleo";
|
|
78
|
-
if (existsSync(cleoDir)) {
|
|
79
|
-
const stateDir = join2(cleoDir, "context-states");
|
|
80
|
-
const statePath = join2(stateDir, ".context-state.json");
|
|
81
|
-
const state = {
|
|
82
|
-
$schema: "https://cleo-dev.com/schemas/v1/context-state.schema.json",
|
|
83
|
-
version: "1.0.0",
|
|
84
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
85
|
-
staleAfterMs: 5e3,
|
|
86
|
-
contextWindow: {
|
|
87
|
-
maxTokens: contextSize,
|
|
88
|
-
currentTokens: totalTokens,
|
|
89
|
-
percentage,
|
|
90
|
-
breakdown: {
|
|
91
|
-
inputTokens,
|
|
92
|
-
outputTokens,
|
|
93
|
-
cacheCreationTokens: cacheCreate,
|
|
94
|
-
cacheReadTokens: usage.cache_read_input_tokens ?? 0
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
thresholds: {
|
|
98
|
-
warning: THRESHOLDS.WARNING,
|
|
99
|
-
caution: THRESHOLDS.CAUTION,
|
|
100
|
-
critical: THRESHOLDS.CRITICAL,
|
|
101
|
-
emergency: THRESHOLDS.EMERGENCY
|
|
102
|
-
},
|
|
103
|
-
status,
|
|
104
|
-
cleoSessionId: ""
|
|
105
|
-
};
|
|
106
|
-
try {
|
|
107
|
-
await mkdir(dirname(statePath), { recursive: true });
|
|
108
|
-
writeFileSync(statePath, JSON.stringify(state, null, 2));
|
|
109
|
-
} catch {
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return `${percentage}% | ${totalTokens}/${contextSize}`;
|
|
113
|
-
}
|
|
114
|
-
/** Check the current statusline integration status in Claude Code settings. */
|
|
115
|
-
checkStatuslineIntegration() {
|
|
116
|
-
const settingsPath = this.pathProvider.getSettingsPath();
|
|
117
|
-
if (!settingsPath || !existsSync(settingsPath)) return "no_settings";
|
|
118
|
-
try {
|
|
119
|
-
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
120
|
-
const statusLine = settings.statusLine;
|
|
121
|
-
if (!statusLine?.type) return "not_configured";
|
|
122
|
-
if (statusLine.type !== "command") return "custom_no_cleo";
|
|
123
|
-
const cmd = statusLine.command ?? "";
|
|
124
|
-
if (cmd.includes("context-monitor.sh") || cmd.includes("cleo-statusline") || cmd.includes(".context-state.json") || cmd.includes("context-states")) {
|
|
125
|
-
return "configured";
|
|
126
|
-
}
|
|
127
|
-
const scriptPath = cmd.startsWith("~") ? cmd.replace("~", homedir2()) : cmd;
|
|
128
|
-
if (existsSync(scriptPath)) {
|
|
129
|
-
try {
|
|
130
|
-
const content = readFileSync(scriptPath, "utf-8");
|
|
131
|
-
if (content.includes("context-state.json")) return "configured";
|
|
132
|
-
} catch {
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return "custom_no_cleo";
|
|
136
|
-
} catch {
|
|
137
|
-
return "no_settings";
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
/** Get the recommended statusline configuration object for Claude Code settings. */
|
|
141
|
-
getStatuslineConfig() {
|
|
142
|
-
return {
|
|
143
|
-
statusLine: {
|
|
144
|
-
type: "command",
|
|
145
|
-
command: join2(homedir2(), ".cleo", "lib", "session", "context-monitor.sh")
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
/** Get human-readable setup instructions for enabling context monitoring. */
|
|
150
|
-
getSetupInstructions() {
|
|
151
|
-
const settingsPath = this.pathProvider.getSettingsPath() ?? "~/.claude/settings.json";
|
|
152
|
-
return [
|
|
153
|
-
"To enable context monitoring, add to your Claude Code settings:",
|
|
154
|
-
`File: ${settingsPath}`,
|
|
155
|
-
"",
|
|
156
|
-
JSON.stringify(this.getStatuslineConfig(), null, 2),
|
|
157
|
-
"",
|
|
158
|
-
"This enables real-time context window tracking in the CLI."
|
|
159
|
-
].join("\n");
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
// packages/adapters/src/providers/claude-code/hooks.ts
|
|
166
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
167
|
-
import { join as join3 } from "node:path";
|
|
168
|
-
var PROVIDER_ID, CLAUDE_CODE_EVENT_MAP, ClaudeCodeHookProvider;
|
|
169
|
-
var init_hooks = __esm({
|
|
170
|
-
"packages/adapters/src/providers/claude-code/hooks.ts"() {
|
|
171
|
-
"use strict";
|
|
172
|
-
PROVIDER_ID = "claude-code";
|
|
173
|
-
CLAUDE_CODE_EVENT_MAP = {
|
|
174
|
-
// CAAMP: toNative('SessionStart', 'claude-code') = 'SessionStart'
|
|
175
|
-
SessionStart: "SessionStart",
|
|
176
|
-
// CAAMP: toNative('SessionEnd', 'claude-code') = 'SessionEnd'
|
|
177
|
-
SessionEnd: "SessionEnd",
|
|
178
|
-
// CAAMP: toNative('PromptSubmit', 'claude-code') = 'UserPromptSubmit'
|
|
179
|
-
UserPromptSubmit: "PromptSubmit",
|
|
180
|
-
// CAAMP: toNative('ResponseComplete', 'claude-code') = 'Stop'
|
|
181
|
-
Stop: "ResponseComplete",
|
|
182
|
-
// CAAMP: toNative('PreToolUse', 'claude-code') = 'PreToolUse'
|
|
183
|
-
PreToolUse: "PreToolUse",
|
|
184
|
-
// CAAMP: toNative('PostToolUse', 'claude-code') = 'PostToolUse'
|
|
185
|
-
PostToolUse: "PostToolUse",
|
|
186
|
-
// CAAMP: toNative('PostToolUseFailure','claude-code') = 'PostToolUseFailure'
|
|
187
|
-
PostToolUseFailure: "PostToolUseFailure",
|
|
188
|
-
// CAAMP: toNative('PermissionRequest', 'claude-code') = 'PermissionRequest'
|
|
189
|
-
PermissionRequest: "PermissionRequest",
|
|
190
|
-
// CAAMP: toNative('SubagentStart', 'claude-code') = 'SubagentStart'
|
|
191
|
-
SubagentStart: "SubagentStart",
|
|
192
|
-
// CAAMP: toNative('SubagentStop', 'claude-code') = 'SubagentStop'
|
|
193
|
-
SubagentStop: "SubagentStop",
|
|
194
|
-
// CAAMP: toNative('PreCompact', 'claude-code') = 'PreCompact'
|
|
195
|
-
PreCompact: "PreCompact",
|
|
196
|
-
// CAAMP: toNative('PostCompact', 'claude-code') = 'PostCompact'
|
|
197
|
-
PostCompact: "PostCompact",
|
|
198
|
-
// CAAMP: toNative('Notification', 'claude-code') = 'Notification'
|
|
199
|
-
Notification: "Notification",
|
|
200
|
-
// CAAMP: toNative('ConfigChange', 'claude-code') = 'ConfigChange'
|
|
201
|
-
ConfigChange: "ConfigChange"
|
|
202
|
-
};
|
|
203
|
-
ClaudeCodeHookProvider = class {
|
|
204
|
-
/** Whether hooks have been registered for the current session. */
|
|
205
|
-
registered = false;
|
|
206
|
-
/**
|
|
207
|
-
* Map a Claude Code native event name to a CAAMP canonical hook event name.
|
|
208
|
-
*
|
|
209
|
-
* Looks up the native event name in the map derived from
|
|
210
|
-
* `getProviderHookProfile('claude-code').mappings` (CAAMP 1.9.1).
|
|
211
|
-
* Returns null for unrecognised events (e.g. PreModel, PostModel which
|
|
212
|
-
* Claude Code does not support).
|
|
213
|
-
*
|
|
214
|
-
* @param providerEvent - Claude Code native event (e.g. "UserPromptSubmit", "Stop")
|
|
215
|
-
* @returns CAAMP canonical event name, or null if unmapped
|
|
216
|
-
* @task T164
|
|
217
|
-
*/
|
|
218
|
-
mapProviderEvent(providerEvent) {
|
|
219
|
-
return CLAUDE_CODE_EVENT_MAP[providerEvent] ?? null;
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Register native hooks for a project.
|
|
223
|
-
*
|
|
224
|
-
* For Claude Code, hooks are registered via the config system
|
|
225
|
-
* (`~/.claude/settings.json`), managed by the install provider.
|
|
226
|
-
* This method marks hooks as registered without performing filesystem operations.
|
|
227
|
-
*
|
|
228
|
-
* Iterating supported events is handled at install time using
|
|
229
|
-
* `getSupportedCanonicalEvents()` to enumerate all 14 supported hooks.
|
|
230
|
-
*
|
|
231
|
-
* @param _projectDir - Project directory (unused; Claude Code uses global config)
|
|
232
|
-
* @task T164
|
|
233
|
-
*/
|
|
234
|
-
async registerNativeHooks(_projectDir) {
|
|
235
|
-
this.registered = true;
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Unregister native hooks.
|
|
239
|
-
*
|
|
240
|
-
* For Claude Code, this is a no-op since hooks are managed through the config
|
|
241
|
-
* system. Unregistration happens via the install provider's uninstall method.
|
|
242
|
-
*
|
|
243
|
-
* @task T164
|
|
244
|
-
*/
|
|
245
|
-
async unregisterNativeHooks() {
|
|
246
|
-
this.registered = false;
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Check whether hooks have been registered via `registerNativeHooks`.
|
|
250
|
-
*/
|
|
251
|
-
isRegistered() {
|
|
252
|
-
return this.registered;
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Get the native→canonical event mapping for introspection and debugging.
|
|
256
|
-
*
|
|
257
|
-
* Returns the map derived from `getProviderHookProfile('claude-code').mappings`
|
|
258
|
-
* (CAAMP 1.9.1). Use `getSupportedCanonicalEvents()` to enumerate canonical
|
|
259
|
-
* names via live CAAMP APIs.
|
|
260
|
-
*
|
|
261
|
-
* @returns Immutable record of native event name → canonical event name
|
|
262
|
-
*/
|
|
263
|
-
getEventMap() {
|
|
264
|
-
return { ...CLAUDE_CODE_EVENT_MAP };
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Enumerate supported canonical events via CAAMP's `getSupportedEvents()`.
|
|
268
|
-
*
|
|
269
|
-
* Calls `getSupportedEvents('claude-code')` from the CAAMP normalizer to
|
|
270
|
-
* get the authoritative list. Claude Code supports 14 of 16 canonical events
|
|
271
|
-
* (PreModel and PostModel are not supported). Falls back to the values of
|
|
272
|
-
* the static event map when CAAMP is unavailable at runtime.
|
|
273
|
-
*
|
|
274
|
-
* @returns Array of CAAMP canonical event names supported by Claude Code
|
|
275
|
-
* @task T164
|
|
276
|
-
*/
|
|
277
|
-
async getSupportedCanonicalEvents() {
|
|
278
|
-
try {
|
|
279
|
-
const { getSupportedEvents } = await import("@cleocode/caamp");
|
|
280
|
-
return getSupportedEvents(PROVIDER_ID);
|
|
281
|
-
} catch {
|
|
282
|
-
return [...new Set(Object.values(CLAUDE_CODE_EVENT_MAP))];
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Retrieve the full provider hook profile from CAAMP.
|
|
287
|
-
*
|
|
288
|
-
* Calls `getProviderHookProfile('claude-code')` from the CAAMP normalizer to
|
|
289
|
-
* get the complete profile: hook system type (`config`), config path
|
|
290
|
-
* (`~/.claude/settings.json`), handler types, and all event mappings.
|
|
291
|
-
* Returns null when CAAMP is unavailable at runtime.
|
|
292
|
-
*
|
|
293
|
-
* @returns Provider hook profile or null if CAAMP is unavailable
|
|
294
|
-
* @task T164
|
|
295
|
-
*/
|
|
296
|
-
async getProviderProfile() {
|
|
297
|
-
try {
|
|
298
|
-
const { getProviderHookProfile } = await import("@cleocode/caamp");
|
|
299
|
-
return getProviderHookProfile(PROVIDER_ID) ?? null;
|
|
300
|
-
} catch {
|
|
301
|
-
return null;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* Translate a CAAMP canonical event to its Claude Code native name via CAAMP.
|
|
306
|
-
*
|
|
307
|
-
* Calls `toNative(canonical, 'claude-code')` from the CAAMP normalizer.
|
|
308
|
-
* Returns null for unsupported events (PreModel, PostModel) or when
|
|
309
|
-
* CAAMP is unavailable.
|
|
310
|
-
*
|
|
311
|
-
* @param canonical - CAAMP canonical event name (e.g. "PromptSubmit")
|
|
312
|
-
* @returns Claude Code native event name or null
|
|
313
|
-
* @task T164
|
|
314
|
-
*/
|
|
315
|
-
async toNativeEvent(canonical) {
|
|
316
|
-
try {
|
|
317
|
-
const { toNative } = await import("@cleocode/caamp");
|
|
318
|
-
return toNative(canonical, PROVIDER_ID);
|
|
319
|
-
} catch {
|
|
320
|
-
const entry = Object.entries(CLAUDE_CODE_EVENT_MAP).find(([, v]) => v === canonical);
|
|
321
|
-
return entry?.[0] ?? null;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Extract a plain-text transcript from Claude Code session JSONL files.
|
|
326
|
-
*
|
|
327
|
-
* Reads the most recent .jsonl file under `~/.claude/projects/` and
|
|
328
|
-
* extracts user/assistant turn text into a flat string for brain
|
|
329
|
-
* observation extraction.
|
|
330
|
-
*
|
|
331
|
-
* Returns null when no session data is found or on any read error.
|
|
332
|
-
*
|
|
333
|
-
* @param _sessionId - CLEO session ID (unused; reads the most recent file)
|
|
334
|
-
* @param _projectDir - Project directory (unused; Claude Code uses global paths)
|
|
335
|
-
* @task T144 @epic T134
|
|
336
|
-
*/
|
|
337
|
-
async getTranscript(_sessionId, _projectDir) {
|
|
338
|
-
try {
|
|
339
|
-
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "/root";
|
|
340
|
-
const projectsDir = join3(homeDir, ".claude", "projects");
|
|
341
|
-
let allFiles = [];
|
|
342
|
-
try {
|
|
343
|
-
const projectDirs = await readdir(projectsDir, { withFileTypes: true });
|
|
344
|
-
for (const entry of projectDirs) {
|
|
345
|
-
if (!entry.isDirectory()) continue;
|
|
346
|
-
const subDir = join3(projectsDir, entry.name);
|
|
347
|
-
try {
|
|
348
|
-
const files = await readdir(subDir);
|
|
349
|
-
for (const file of files) {
|
|
350
|
-
if (!file.endsWith(".jsonl")) continue;
|
|
351
|
-
const filePath = join3(subDir, file);
|
|
352
|
-
allFiles.push({ path: filePath, mtime: 0 });
|
|
353
|
-
}
|
|
354
|
-
} catch {
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
} catch {
|
|
358
|
-
return null;
|
|
359
|
-
}
|
|
360
|
-
if (allFiles.length === 0) return null;
|
|
361
|
-
allFiles = allFiles.sort((a, b) => b.path.localeCompare(a.path));
|
|
362
|
-
const mostRecent = allFiles[0];
|
|
363
|
-
if (!mostRecent) return null;
|
|
364
|
-
const raw = await readFile(mostRecent.path, "utf-8");
|
|
365
|
-
const lines = raw.split("\n").filter((l) => l.trim());
|
|
366
|
-
const turns = [];
|
|
367
|
-
for (const line of lines) {
|
|
368
|
-
try {
|
|
369
|
-
const entry = JSON.parse(line);
|
|
370
|
-
const role = entry.role;
|
|
371
|
-
const content = entry.content;
|
|
372
|
-
if (role === "assistant" && typeof content === "string") {
|
|
373
|
-
turns.push(`assistant: ${content}`);
|
|
374
|
-
} else if (role === "user" && typeof content === "string") {
|
|
375
|
-
turns.push(`user: ${content}`);
|
|
376
|
-
}
|
|
377
|
-
} catch {
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
return turns.length > 0 ? turns.join("\n") : null;
|
|
381
|
-
} catch {
|
|
382
|
-
return null;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
// packages/adapters/src/providers/claude-code/install.ts
|
|
390
|
-
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
391
|
-
import { homedir as homedir3 } from "node:os";
|
|
392
|
-
import { join as join4 } from "node:path";
|
|
393
|
-
var INSTRUCTION_REFERENCES, ClaudeCodeInstallProvider;
|
|
394
|
-
var init_install = __esm({
|
|
395
|
-
"packages/adapters/src/providers/claude-code/install.ts"() {
|
|
396
|
-
"use strict";
|
|
397
|
-
INSTRUCTION_REFERENCES = ["@~/.cleo/templates/CLEO-INJECTION.md", "@.cleo/memory-bridge.md"];
|
|
398
|
-
ClaudeCodeInstallProvider = class {
|
|
399
|
-
/**
|
|
400
|
-
* Install CLEO into a Claude Code project.
|
|
401
|
-
*
|
|
402
|
-
* @param options - Installation options including project directory
|
|
403
|
-
* @returns Result describing what was installed
|
|
404
|
-
*/
|
|
405
|
-
async install(options) {
|
|
406
|
-
const { projectDir } = options;
|
|
407
|
-
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
408
|
-
let instructionFileUpdated = false;
|
|
409
|
-
const details = {};
|
|
410
|
-
instructionFileUpdated = this.updateInstructionFile(projectDir);
|
|
411
|
-
if (instructionFileUpdated) {
|
|
412
|
-
details.instructionFile = join4(projectDir, "CLAUDE.md");
|
|
413
|
-
}
|
|
414
|
-
const pluginResult = this.registerPlugin();
|
|
415
|
-
if (pluginResult) {
|
|
416
|
-
details.plugin = pluginResult;
|
|
417
|
-
}
|
|
418
|
-
return {
|
|
419
|
-
success: true,
|
|
420
|
-
installedAt,
|
|
421
|
-
instructionFileUpdated,
|
|
422
|
-
mcpRegistered: false,
|
|
423
|
-
details
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Uninstall CLEO from the current Claude Code project.
|
|
428
|
-
*
|
|
429
|
-
* Does not remove CLAUDE.md references (they are harmless if CLEO is not present).
|
|
430
|
-
*/
|
|
431
|
-
async uninstall() {
|
|
432
|
-
}
|
|
433
|
-
/**
|
|
434
|
-
* Check whether CLEO is installed in the current environment.
|
|
435
|
-
*
|
|
436
|
-
* Checks for plugin enabled in ~/.claude/settings.json.
|
|
437
|
-
*/
|
|
438
|
-
async isInstalled() {
|
|
439
|
-
const settingsPath = join4(homedir3(), ".claude", "settings.json");
|
|
440
|
-
if (existsSync2(settingsPath)) {
|
|
441
|
-
try {
|
|
442
|
-
const settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
|
|
443
|
-
const plugins = settings.enabledPlugins;
|
|
444
|
-
if (plugins && plugins["cleo@cleocode"] === true) {
|
|
445
|
-
return true;
|
|
446
|
-
}
|
|
447
|
-
} catch {
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
return false;
|
|
451
|
-
}
|
|
452
|
-
/**
|
|
453
|
-
* Ensure CLAUDE.md contains @-references to CLEO instruction files.
|
|
454
|
-
*
|
|
455
|
-
* Creates CLAUDE.md if it does not exist. Appends any missing references.
|
|
456
|
-
*
|
|
457
|
-
* @param projectDir - Project root directory
|
|
458
|
-
*/
|
|
459
|
-
async ensureInstructionReferences(projectDir) {
|
|
460
|
-
this.updateInstructionFile(projectDir);
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* Update CLAUDE.md with CLEO @-references.
|
|
464
|
-
*
|
|
465
|
-
* @returns true if the file was created or modified
|
|
466
|
-
*/
|
|
467
|
-
updateInstructionFile(projectDir) {
|
|
468
|
-
const claudeMdPath = join4(projectDir, "CLAUDE.md");
|
|
469
|
-
let content = "";
|
|
470
|
-
let existed = false;
|
|
471
|
-
if (existsSync2(claudeMdPath)) {
|
|
472
|
-
content = readFileSync2(claudeMdPath, "utf-8");
|
|
473
|
-
existed = true;
|
|
474
|
-
}
|
|
475
|
-
const missingRefs = INSTRUCTION_REFERENCES.filter((ref) => !content.includes(ref));
|
|
476
|
-
if (missingRefs.length === 0) {
|
|
477
|
-
return false;
|
|
478
|
-
}
|
|
479
|
-
const refsBlock = missingRefs.join("\n");
|
|
480
|
-
if (existed) {
|
|
481
|
-
const separator = content.endsWith("\n") ? "" : "\n";
|
|
482
|
-
content = content + separator + refsBlock + "\n";
|
|
483
|
-
} else {
|
|
484
|
-
content = refsBlock + "\n";
|
|
485
|
-
}
|
|
486
|
-
writeFileSync2(claudeMdPath, content, "utf-8");
|
|
487
|
-
return true;
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* Register the CLEO brain plugin in ~/.claude/settings.json.
|
|
491
|
-
*
|
|
492
|
-
* @returns Description of what was registered, or null if no change needed
|
|
493
|
-
*/
|
|
494
|
-
registerPlugin() {
|
|
495
|
-
const home = homedir3();
|
|
496
|
-
const settingsPath = join4(home, ".claude", "settings.json");
|
|
497
|
-
let settings = {};
|
|
498
|
-
if (existsSync2(settingsPath)) {
|
|
499
|
-
try {
|
|
500
|
-
settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
|
|
501
|
-
} catch {
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
const enabledPlugins = settings.enabledPlugins ?? {};
|
|
505
|
-
const pluginKey = "cleo@cleocode";
|
|
506
|
-
if (enabledPlugins[pluginKey] === true) {
|
|
507
|
-
return null;
|
|
508
|
-
}
|
|
509
|
-
if (enabledPlugins["claude-mem@thedotmack"] === true) {
|
|
510
|
-
enabledPlugins["claude-mem@thedotmack"] = false;
|
|
511
|
-
}
|
|
512
|
-
enabledPlugins[pluginKey] = true;
|
|
513
|
-
settings.enabledPlugins = enabledPlugins;
|
|
514
|
-
mkdirSync(join4(home, ".claude"), { recursive: true });
|
|
515
|
-
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
516
|
-
return `Enabled ${pluginKey} in ~/.claude/settings.json`;
|
|
517
|
-
}
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
// packages/contracts/src/errors.ts
|
|
523
|
-
function getErrorMessage(error, fallback = "Unknown error") {
|
|
524
|
-
if (error instanceof Error) {
|
|
525
|
-
return error.message;
|
|
526
|
-
}
|
|
527
|
-
if (typeof error === "string") {
|
|
528
|
-
return error;
|
|
529
|
-
}
|
|
530
|
-
if (error !== null && typeof error === "object" && "message" in error && typeof error.message === "string") {
|
|
531
|
-
return error.message;
|
|
532
|
-
}
|
|
533
|
-
return fallback;
|
|
534
|
-
}
|
|
535
|
-
var init_errors = __esm({
|
|
536
|
-
"packages/contracts/src/errors.ts"() {
|
|
537
|
-
"use strict";
|
|
538
|
-
}
|
|
539
|
-
});
|
|
540
|
-
|
|
541
|
-
// packages/contracts/src/exit-codes.ts
|
|
542
|
-
var init_exit_codes = __esm({
|
|
543
|
-
"packages/contracts/src/exit-codes.ts"() {
|
|
544
|
-
"use strict";
|
|
545
|
-
}
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
// packages/contracts/src/facade.ts
|
|
549
|
-
var init_facade = __esm({
|
|
550
|
-
"packages/contracts/src/facade.ts"() {
|
|
551
|
-
"use strict";
|
|
552
|
-
}
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
// packages/contracts/src/lafs.ts
|
|
556
|
-
var init_lafs = __esm({
|
|
557
|
-
"packages/contracts/src/lafs.ts"() {
|
|
558
|
-
"use strict";
|
|
559
|
-
}
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
// packages/contracts/src/operations/issues.ts
|
|
563
|
-
var init_issues = __esm({
|
|
564
|
-
"packages/contracts/src/operations/issues.ts"() {
|
|
565
|
-
"use strict";
|
|
566
|
-
}
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
// packages/contracts/src/operations/lifecycle.ts
|
|
570
|
-
var init_lifecycle = __esm({
|
|
571
|
-
"packages/contracts/src/operations/lifecycle.ts"() {
|
|
572
|
-
"use strict";
|
|
573
|
-
}
|
|
574
|
-
});
|
|
575
|
-
|
|
576
|
-
// packages/contracts/src/operations/orchestrate.ts
|
|
577
|
-
var init_orchestrate = __esm({
|
|
578
|
-
"packages/contracts/src/operations/orchestrate.ts"() {
|
|
579
|
-
"use strict";
|
|
580
|
-
}
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
// packages/contracts/src/operations/release.ts
|
|
584
|
-
var init_release = __esm({
|
|
585
|
-
"packages/contracts/src/operations/release.ts"() {
|
|
586
|
-
"use strict";
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
// packages/contracts/src/operations/research.ts
|
|
591
|
-
var init_research = __esm({
|
|
592
|
-
"packages/contracts/src/operations/research.ts"() {
|
|
593
|
-
"use strict";
|
|
594
|
-
}
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
// packages/contracts/src/operations/session.ts
|
|
598
|
-
var init_session = __esm({
|
|
599
|
-
"packages/contracts/src/operations/session.ts"() {
|
|
600
|
-
"use strict";
|
|
601
|
-
}
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
// packages/contracts/src/operations/skills.ts
|
|
605
|
-
var init_skills = __esm({
|
|
606
|
-
"packages/contracts/src/operations/skills.ts"() {
|
|
607
|
-
"use strict";
|
|
608
|
-
}
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
// packages/contracts/src/operations/system.ts
|
|
612
|
-
var init_system = __esm({
|
|
613
|
-
"packages/contracts/src/operations/system.ts"() {
|
|
614
|
-
"use strict";
|
|
615
|
-
}
|
|
616
|
-
});
|
|
617
|
-
|
|
618
|
-
// packages/contracts/src/operations/tasks.ts
|
|
619
|
-
var init_tasks = __esm({
|
|
620
|
-
"packages/contracts/src/operations/tasks.ts"() {
|
|
621
|
-
"use strict";
|
|
622
|
-
}
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
// packages/contracts/src/operations/validate.ts
|
|
626
|
-
var init_validate = __esm({
|
|
627
|
-
"packages/contracts/src/operations/validate.ts"() {
|
|
628
|
-
"use strict";
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
// packages/contracts/src/operations/index.ts
|
|
633
|
-
var init_operations = __esm({
|
|
634
|
-
"packages/contracts/src/operations/index.ts"() {
|
|
635
|
-
"use strict";
|
|
636
|
-
init_issues();
|
|
637
|
-
init_lifecycle();
|
|
638
|
-
init_orchestrate();
|
|
639
|
-
init_release();
|
|
640
|
-
init_research();
|
|
641
|
-
init_session();
|
|
642
|
-
init_skills();
|
|
643
|
-
init_system();
|
|
644
|
-
init_tasks();
|
|
645
|
-
init_validate();
|
|
646
|
-
}
|
|
647
|
-
});
|
|
648
|
-
|
|
649
|
-
// packages/contracts/src/orchestration-hierarchy.ts
|
|
650
|
-
var init_orchestration_hierarchy = __esm({
|
|
651
|
-
"packages/contracts/src/orchestration-hierarchy.ts"() {
|
|
652
|
-
"use strict";
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
// packages/contracts/src/session.ts
|
|
657
|
-
var init_session2 = __esm({
|
|
658
|
-
"packages/contracts/src/session.ts"() {
|
|
659
|
-
"use strict";
|
|
660
|
-
}
|
|
661
|
-
});
|
|
662
|
-
|
|
663
|
-
// packages/contracts/src/status-registry.ts
|
|
664
|
-
var init_status_registry = __esm({
|
|
665
|
-
"packages/contracts/src/status-registry.ts"() {
|
|
666
|
-
"use strict";
|
|
667
|
-
}
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
// packages/contracts/src/index.ts
|
|
671
|
-
var init_src = __esm({
|
|
672
|
-
"packages/contracts/src/index.ts"() {
|
|
673
|
-
init_errors();
|
|
674
|
-
init_exit_codes();
|
|
675
|
-
init_facade();
|
|
676
|
-
init_lafs();
|
|
677
|
-
init_operations();
|
|
678
|
-
init_orchestration_hierarchy();
|
|
679
|
-
init_session2();
|
|
680
|
-
init_status_registry();
|
|
681
|
-
}
|
|
682
|
-
});
|
|
683
|
-
|
|
684
|
-
// packages/adapters/src/providers/claude-code/spawn.ts
|
|
685
|
-
import { exec, spawn as nodeSpawn } from "node:child_process";
|
|
686
|
-
import { unlink, writeFile } from "node:fs/promises";
|
|
687
|
-
import { promisify } from "node:util";
|
|
688
|
-
var execAsync, ClaudeCodeSpawnProvider;
|
|
689
|
-
var init_spawn = __esm({
|
|
690
|
-
"packages/adapters/src/providers/claude-code/spawn.ts"() {
|
|
691
|
-
"use strict";
|
|
692
|
-
init_src();
|
|
693
|
-
execAsync = promisify(exec);
|
|
694
|
-
ClaudeCodeSpawnProvider = class {
|
|
695
|
-
/** Map of instance IDs to tracked process info. */
|
|
696
|
-
processMap = /* @__PURE__ */ new Map();
|
|
697
|
-
/**
|
|
698
|
-
* Check if the Claude CLI is available in PATH.
|
|
699
|
-
*
|
|
700
|
-
* @returns true if `claude` is found via `which`
|
|
701
|
-
*/
|
|
702
|
-
async canSpawn() {
|
|
703
|
-
try {
|
|
704
|
-
await execAsync("which claude");
|
|
705
|
-
return true;
|
|
706
|
-
} catch {
|
|
707
|
-
return false;
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
/**
|
|
711
|
-
* Spawn a subagent via Claude CLI.
|
|
712
|
-
*
|
|
713
|
-
* Writes the prompt to a temporary file and spawns a detached Claude
|
|
714
|
-
* process. The process runs independently of the parent.
|
|
715
|
-
*
|
|
716
|
-
* @param context - Spawn context with taskId, prompt, and options
|
|
717
|
-
* @returns Spawn result with instance ID and status
|
|
718
|
-
*/
|
|
719
|
-
async spawn(context) {
|
|
720
|
-
const instanceId = `claude-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
721
|
-
const startTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
722
|
-
let tmpFile;
|
|
723
|
-
try {
|
|
724
|
-
tmpFile = `/tmp/claude-spawn-${instanceId}.txt`;
|
|
725
|
-
await writeFile(tmpFile, context.prompt, "utf-8");
|
|
726
|
-
const args = ["--allow-insecure", "--no-upgrade-check", tmpFile];
|
|
727
|
-
const spawnOpts = {
|
|
728
|
-
detached: true,
|
|
729
|
-
stdio: "ignore"
|
|
730
|
-
};
|
|
731
|
-
if (context.workingDirectory) {
|
|
732
|
-
spawnOpts.cwd = context.workingDirectory;
|
|
733
|
-
}
|
|
734
|
-
const child = nodeSpawn("claude", args, spawnOpts);
|
|
735
|
-
child.unref();
|
|
736
|
-
if (child.pid) {
|
|
737
|
-
this.processMap.set(instanceId, {
|
|
738
|
-
pid: child.pid,
|
|
739
|
-
taskId: context.taskId,
|
|
740
|
-
startTime
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
const capturedTmpFile = tmpFile;
|
|
744
|
-
child.on("exit", async () => {
|
|
745
|
-
this.processMap.delete(instanceId);
|
|
746
|
-
try {
|
|
747
|
-
await unlink(capturedTmpFile);
|
|
748
|
-
} catch {
|
|
749
|
-
}
|
|
750
|
-
});
|
|
751
|
-
return {
|
|
752
|
-
instanceId,
|
|
753
|
-
taskId: context.taskId,
|
|
754
|
-
providerId: "claude-code",
|
|
755
|
-
status: "running",
|
|
756
|
-
startTime
|
|
757
|
-
};
|
|
758
|
-
} catch (error) {
|
|
759
|
-
console.error(`[ClaudeCodeSpawnProvider] Failed to spawn: ${getErrorMessage(error)}`);
|
|
760
|
-
if (tmpFile) {
|
|
761
|
-
try {
|
|
762
|
-
await unlink(tmpFile);
|
|
763
|
-
} catch {
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
return {
|
|
767
|
-
instanceId,
|
|
768
|
-
taskId: context.taskId,
|
|
769
|
-
providerId: "claude-code",
|
|
770
|
-
status: "failed",
|
|
771
|
-
startTime,
|
|
772
|
-
endTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
773
|
-
error: getErrorMessage(error)
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
/**
|
|
778
|
-
* List currently running Claude subagent processes.
|
|
779
|
-
*
|
|
780
|
-
* Checks each tracked process via kill(pid, 0) to verify it is still alive.
|
|
781
|
-
* Dead processes are automatically cleaned from the tracking map.
|
|
782
|
-
*
|
|
783
|
-
* @returns Array of spawn results for running processes
|
|
784
|
-
*/
|
|
785
|
-
async listRunning() {
|
|
786
|
-
const running = [];
|
|
787
|
-
for (const [instanceId, tracked] of this.processMap.entries()) {
|
|
788
|
-
try {
|
|
789
|
-
process.kill(tracked.pid, 0);
|
|
790
|
-
running.push({
|
|
791
|
-
instanceId,
|
|
792
|
-
taskId: tracked.taskId,
|
|
793
|
-
providerId: "claude-code",
|
|
794
|
-
status: "running",
|
|
795
|
-
startTime: tracked.startTime
|
|
796
|
-
});
|
|
797
|
-
} catch {
|
|
798
|
-
this.processMap.delete(instanceId);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
return running;
|
|
802
|
-
}
|
|
803
|
-
/**
|
|
804
|
-
* Terminate a running spawn by instance ID.
|
|
805
|
-
*
|
|
806
|
-
* Sends SIGTERM to the tracked process. If the process is not found
|
|
807
|
-
* or has already exited, this is a no-op.
|
|
808
|
-
*
|
|
809
|
-
* @param instanceId - ID of the spawn instance to terminate
|
|
810
|
-
*/
|
|
811
|
-
async terminate(instanceId) {
|
|
812
|
-
const tracked = this.processMap.get(instanceId);
|
|
813
|
-
if (!tracked) return;
|
|
814
|
-
try {
|
|
815
|
-
process.kill(tracked.pid, "SIGTERM");
|
|
816
|
-
} catch {
|
|
817
|
-
}
|
|
818
|
-
this.processMap.delete(instanceId);
|
|
819
|
-
}
|
|
820
|
-
};
|
|
821
|
-
}
|
|
822
|
-
});
|
|
823
|
-
|
|
824
|
-
// packages/adapters/src/providers/claude-code/task-sync.ts
|
|
825
|
-
import { readFile as readFile2, stat } from "node:fs/promises";
|
|
826
|
-
import { join as join5 } from "node:path";
|
|
827
|
-
function parseTaskId(content) {
|
|
828
|
-
const match = content.match(/^\[T(\d+)\]/);
|
|
829
|
-
return match ? `T${match[1]}` : null;
|
|
830
|
-
}
|
|
831
|
-
function stripPrefixes(content) {
|
|
832
|
-
return content.replace(/^\[T\d+\]\s*/, "").replace(/^\[!\]\s*/, "").replace(/^\[BLOCKED\]\s*/, "");
|
|
833
|
-
}
|
|
834
|
-
function mapStatus(twStatus) {
|
|
835
|
-
switch (twStatus) {
|
|
836
|
-
case "completed":
|
|
837
|
-
return "completed";
|
|
838
|
-
case "in_progress":
|
|
839
|
-
return "active";
|
|
840
|
-
case "pending":
|
|
841
|
-
return "pending";
|
|
842
|
-
default:
|
|
843
|
-
return "pending";
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
function getTodoWriteFilePath(projectDir) {
|
|
847
|
-
return join5(projectDir, ".cleo", "sync", "todowrite-state.json");
|
|
848
|
-
}
|
|
849
|
-
var ClaudeCodeTaskSyncProvider;
|
|
850
|
-
var init_task_sync = __esm({
|
|
851
|
-
"packages/adapters/src/providers/claude-code/task-sync.ts"() {
|
|
852
|
-
"use strict";
|
|
853
|
-
ClaudeCodeTaskSyncProvider = class {
|
|
854
|
-
/** Optional override path for the TodoWrite state file (used in tests). */
|
|
855
|
-
customFilePath;
|
|
856
|
-
constructor(options) {
|
|
857
|
-
this.customFilePath = options?.filePath;
|
|
858
|
-
}
|
|
859
|
-
/** Retrieve external tasks from Claude's TodoWrite state file. */
|
|
860
|
-
async getExternalTasks(projectDir) {
|
|
861
|
-
const filePath = this.customFilePath ?? getTodoWriteFilePath(projectDir);
|
|
862
|
-
try {
|
|
863
|
-
await stat(filePath);
|
|
864
|
-
} catch {
|
|
865
|
-
return [];
|
|
866
|
-
}
|
|
867
|
-
const raw = await readFile2(filePath, "utf-8");
|
|
868
|
-
let state;
|
|
869
|
-
try {
|
|
870
|
-
state = JSON.parse(raw);
|
|
871
|
-
} catch {
|
|
872
|
-
return [];
|
|
873
|
-
}
|
|
874
|
-
if (!state.todos || !Array.isArray(state.todos)) {
|
|
875
|
-
return [];
|
|
876
|
-
}
|
|
877
|
-
const tasks = [];
|
|
878
|
-
let syntheticIndex = 0;
|
|
879
|
-
for (const item of state.todos) {
|
|
880
|
-
const cleoTaskId = parseTaskId(item.content);
|
|
881
|
-
const title = cleoTaskId ? stripPrefixes(item.content).trim() : item.content.trim();
|
|
882
|
-
if (!title) continue;
|
|
883
|
-
tasks.push({
|
|
884
|
-
externalId: cleoTaskId ?? `tw-new-${syntheticIndex++}`,
|
|
885
|
-
title,
|
|
886
|
-
status: mapStatus(item.status),
|
|
887
|
-
providerMeta: {
|
|
888
|
-
source: "todowrite",
|
|
889
|
-
cleoTaskId,
|
|
890
|
-
activeForm: item.activeForm,
|
|
891
|
-
rawContent: item.content
|
|
892
|
-
}
|
|
893
|
-
});
|
|
894
|
-
}
|
|
895
|
-
return tasks;
|
|
896
|
-
}
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
});
|
|
900
|
-
|
|
901
|
-
// packages/adapters/src/providers/claude-code/transport.ts
|
|
902
|
-
var ClaudeCodeTransportProvider;
|
|
903
|
-
var init_transport = __esm({
|
|
904
|
-
"packages/adapters/src/providers/claude-code/transport.ts"() {
|
|
905
|
-
"use strict";
|
|
906
|
-
ClaudeCodeTransportProvider = class {
|
|
907
|
-
/** Provider-specific transport name used for capability negotiation. */
|
|
908
|
-
transportName = "claude-code";
|
|
909
|
-
/** Create a transport instance for inter-agent messaging. */
|
|
910
|
-
createTransport() {
|
|
911
|
-
return null;
|
|
912
|
-
}
|
|
913
|
-
};
|
|
914
|
-
}
|
|
915
|
-
});
|
|
916
|
-
|
|
917
|
-
// packages/adapters/src/providers/claude-code/adapter.ts
|
|
918
|
-
import { exec as exec2 } from "node:child_process";
|
|
919
|
-
import { existsSync as existsSync3 } from "node:fs";
|
|
920
|
-
import { homedir as homedir4 } from "node:os";
|
|
921
|
-
import { join as join6 } from "node:path";
|
|
922
|
-
import { promisify as promisify2 } from "node:util";
|
|
923
|
-
var execAsync2, ClaudeCodeAdapter;
|
|
924
|
-
var init_adapter = __esm({
|
|
925
|
-
"packages/adapters/src/providers/claude-code/adapter.ts"() {
|
|
926
|
-
"use strict";
|
|
927
|
-
init_context_monitor();
|
|
928
|
-
init_hooks();
|
|
929
|
-
init_install();
|
|
930
|
-
init_paths();
|
|
931
|
-
init_spawn();
|
|
932
|
-
init_task_sync();
|
|
933
|
-
init_transport();
|
|
934
|
-
execAsync2 = promisify2(exec2);
|
|
935
|
-
ClaudeCodeAdapter = class {
|
|
936
|
-
/** Unique provider identifier. */
|
|
937
|
-
id = "claude-code";
|
|
938
|
-
/** Human-readable provider name. */
|
|
939
|
-
name = "Claude Code";
|
|
940
|
-
/** Adapter version string. */
|
|
941
|
-
version = "1.0.0";
|
|
942
|
-
/** Declared capabilities for this provider. */
|
|
943
|
-
capabilities = {
|
|
944
|
-
supportsHooks: true,
|
|
945
|
-
// 14/16 canonical events — derived from getProviderHookProfile('claude-code') in CAAMP 1.9.1.
|
|
946
|
-
// PreModel and PostModel are not supported by Claude Code.
|
|
947
|
-
supportedHookEvents: [
|
|
948
|
-
"SessionStart",
|
|
949
|
-
"SessionEnd",
|
|
950
|
-
"PromptSubmit",
|
|
951
|
-
"ResponseComplete",
|
|
952
|
-
"PreToolUse",
|
|
953
|
-
"PostToolUse",
|
|
954
|
-
"PostToolUseFailure",
|
|
955
|
-
"PermissionRequest",
|
|
956
|
-
"SubagentStart",
|
|
957
|
-
"SubagentStop",
|
|
958
|
-
"PreCompact",
|
|
959
|
-
"PostCompact",
|
|
960
|
-
"Notification",
|
|
961
|
-
"ConfigChange"
|
|
962
|
-
],
|
|
963
|
-
supportsSpawn: true,
|
|
964
|
-
supportsInstall: true,
|
|
965
|
-
supportsMcp: false,
|
|
966
|
-
supportsInstructionFiles: true,
|
|
967
|
-
instructionFilePattern: "CLAUDE.md",
|
|
968
|
-
supportsContextMonitor: true,
|
|
969
|
-
supportsStatusline: true,
|
|
970
|
-
supportsProviderPaths: true,
|
|
971
|
-
supportsTransport: true,
|
|
972
|
-
supportsTaskSync: true
|
|
973
|
-
};
|
|
974
|
-
/** Hook provider for CAAMP event mapping and registration. */
|
|
975
|
-
hooks;
|
|
976
|
-
/** Spawn provider for launching subagent processes via `claude` CLI. */
|
|
977
|
-
spawn;
|
|
978
|
-
/** Install provider for managing instruction files and plugin registration. */
|
|
979
|
-
install;
|
|
980
|
-
/** Path provider for resolving Claude Code directory locations. */
|
|
981
|
-
paths;
|
|
982
|
-
/** Context monitor for tracking context window usage and statusline output. */
|
|
983
|
-
contextMonitor;
|
|
984
|
-
/** Transport provider for inter-agent communication. */
|
|
985
|
-
transport;
|
|
986
|
-
/** Task sync provider bridging Claude's TodoWrite format to CLEO tasks. */
|
|
987
|
-
taskSync;
|
|
988
|
-
/** Project directory this adapter was initialized with, or null. */
|
|
989
|
-
projectDir = null;
|
|
990
|
-
/** Whether {@link initialize} has been called. */
|
|
991
|
-
initialized = false;
|
|
992
|
-
constructor() {
|
|
993
|
-
this.hooks = new ClaudeCodeHookProvider();
|
|
994
|
-
this.spawn = new ClaudeCodeSpawnProvider();
|
|
995
|
-
this.install = new ClaudeCodeInstallProvider();
|
|
996
|
-
this.paths = new ClaudeCodePathProvider();
|
|
997
|
-
this.contextMonitor = new ClaudeCodeContextMonitorProvider();
|
|
998
|
-
this.transport = new ClaudeCodeTransportProvider();
|
|
999
|
-
this.taskSync = new ClaudeCodeTaskSyncProvider();
|
|
1000
|
-
}
|
|
1001
|
-
/**
|
|
1002
|
-
* Initialize the adapter for a given project directory.
|
|
1003
|
-
*
|
|
1004
|
-
* Validates the environment by checking for the Claude CLI
|
|
1005
|
-
* and Claude Code configuration directory.
|
|
1006
|
-
*
|
|
1007
|
-
* @param projectDir - Root directory of the project
|
|
1008
|
-
*/
|
|
1009
|
-
async initialize(projectDir) {
|
|
1010
|
-
this.projectDir = projectDir;
|
|
1011
|
-
this.initialized = true;
|
|
1012
|
-
}
|
|
1013
|
-
/**
|
|
1014
|
-
* Dispose the adapter and clean up resources.
|
|
1015
|
-
*
|
|
1016
|
-
* Unregisters hooks and releases any tracked state.
|
|
1017
|
-
*/
|
|
1018
|
-
async dispose() {
|
|
1019
|
-
if (this.hooks.isRegistered()) {
|
|
1020
|
-
await this.hooks.unregisterNativeHooks();
|
|
1021
|
-
}
|
|
1022
|
-
this.initialized = false;
|
|
1023
|
-
this.projectDir = null;
|
|
1024
|
-
}
|
|
1025
|
-
/**
|
|
1026
|
-
* Run a health check to verify Claude Code is accessible.
|
|
1027
|
-
*
|
|
1028
|
-
* Checks:
|
|
1029
|
-
* 1. Adapter has been initialized
|
|
1030
|
-
* 2. Claude CLI is available in PATH
|
|
1031
|
-
* 3. ~/.claude/ configuration directory exists
|
|
1032
|
-
*
|
|
1033
|
-
* @returns Health status with details about each check
|
|
1034
|
-
*/
|
|
1035
|
-
async healthCheck() {
|
|
1036
|
-
const details = {};
|
|
1037
|
-
if (!this.initialized) {
|
|
1038
|
-
return {
|
|
1039
|
-
healthy: false,
|
|
1040
|
-
provider: this.id,
|
|
1041
|
-
details: { error: "Adapter not initialized" }
|
|
1042
|
-
};
|
|
1043
|
-
}
|
|
1044
|
-
let cliAvailable = false;
|
|
1045
|
-
try {
|
|
1046
|
-
const { stdout } = await execAsync2("which claude");
|
|
1047
|
-
cliAvailable = stdout.trim().length > 0;
|
|
1048
|
-
details.cliPath = stdout.trim();
|
|
1049
|
-
} catch {
|
|
1050
|
-
details.cliAvailable = false;
|
|
1051
|
-
}
|
|
1052
|
-
const claudeConfigDir = join6(homedir4(), ".claude");
|
|
1053
|
-
const configExists = existsSync3(claudeConfigDir);
|
|
1054
|
-
details.configDirExists = configExists;
|
|
1055
|
-
const entrypointSet = process.env.CLAUDE_CODE_ENTRYPOINT !== void 0;
|
|
1056
|
-
details.entrypointEnvSet = entrypointSet;
|
|
1057
|
-
const healthy = cliAvailable;
|
|
1058
|
-
details.cliAvailable = cliAvailable;
|
|
1059
|
-
return {
|
|
1060
|
-
healthy,
|
|
1061
|
-
provider: this.id,
|
|
1062
|
-
details
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
/**
|
|
1066
|
-
* Check whether the adapter has been initialized.
|
|
1067
|
-
*/
|
|
1068
|
-
isInitialized() {
|
|
1069
|
-
return this.initialized;
|
|
1070
|
-
}
|
|
1071
|
-
/**
|
|
1072
|
-
* Get the project directory this adapter was initialized with.
|
|
1073
|
-
*/
|
|
1074
|
-
getProjectDir() {
|
|
1075
|
-
return this.projectDir;
|
|
1076
|
-
}
|
|
1077
|
-
};
|
|
1078
|
-
}
|
|
1079
|
-
});
|
|
1080
|
-
|
|
1081
|
-
// packages/adapters/src/providers/claude-code/statusline.ts
|
|
1082
|
-
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
|
|
1083
|
-
import { homedir as homedir5 } from "node:os";
|
|
1084
|
-
import { join as join7 } from "node:path";
|
|
1085
|
-
function getClaudeSettingsPath() {
|
|
1086
|
-
return process.env["CLAUDE_SETTINGS"] ?? join7(process.env["CLAUDE_HOME"] ?? join7(homedir5(), ".claude"), "settings.json");
|
|
1087
|
-
}
|
|
1088
|
-
function checkStatuslineIntegration() {
|
|
1089
|
-
const settingsPath = getClaudeSettingsPath();
|
|
1090
|
-
if (!existsSync4(settingsPath)) return "no_settings";
|
|
1091
|
-
try {
|
|
1092
|
-
const settings = JSON.parse(readFileSync3(settingsPath, "utf-8"));
|
|
1093
|
-
const statusLine = settings.statusLine;
|
|
1094
|
-
if (!statusLine?.type) return "not_configured";
|
|
1095
|
-
if (statusLine.type !== "command") return "custom_no_cleo";
|
|
1096
|
-
const cmd = statusLine.command ?? "";
|
|
1097
|
-
if (cmd.includes("context-monitor.sh") || cmd.includes("cleo-statusline") || cmd.includes(".context-state.json") || cmd.includes("context-states")) {
|
|
1098
|
-
return "configured";
|
|
1099
|
-
}
|
|
1100
|
-
const scriptPath = cmd.startsWith("~") ? cmd.replace("~", homedir5()) : cmd;
|
|
1101
|
-
if (existsSync4(scriptPath)) {
|
|
1102
|
-
try {
|
|
1103
|
-
const content = readFileSync3(scriptPath, "utf-8");
|
|
1104
|
-
if (content.includes("context-state.json")) return "configured";
|
|
1105
|
-
} catch {
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
return "custom_no_cleo";
|
|
1109
|
-
} catch {
|
|
1110
|
-
return "no_settings";
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
function getStatuslineConfig(cleoHome) {
|
|
1114
|
-
return {
|
|
1115
|
-
statusLine: {
|
|
1116
|
-
type: "command",
|
|
1117
|
-
command: join7(cleoHome, "lib", "session", "context-monitor.sh")
|
|
1118
|
-
}
|
|
1119
|
-
};
|
|
1120
|
-
}
|
|
1121
|
-
function getSetupInstructions(cleoHome) {
|
|
1122
|
-
const settingsPath = getClaudeSettingsPath();
|
|
1123
|
-
return [
|
|
1124
|
-
"To enable context monitoring, add to your Claude Code settings:",
|
|
1125
|
-
`File: ${settingsPath}`,
|
|
1126
|
-
"",
|
|
1127
|
-
JSON.stringify(getStatuslineConfig(cleoHome), null, 2),
|
|
1128
|
-
"",
|
|
1129
|
-
"This enables real-time context window tracking in the CLI."
|
|
1130
|
-
].join("\n");
|
|
1131
|
-
}
|
|
1132
|
-
var init_statusline = __esm({
|
|
1133
|
-
"packages/adapters/src/providers/claude-code/statusline.ts"() {
|
|
1134
|
-
"use strict";
|
|
1135
|
-
}
|
|
1136
|
-
});
|
|
1137
|
-
|
|
1138
|
-
// packages/adapters/src/providers/claude-code/index.ts
|
|
1139
|
-
var claude_code_exports = {};
|
|
1140
|
-
__export(claude_code_exports, {
|
|
1141
|
-
ClaudeCodeAdapter: () => ClaudeCodeAdapter,
|
|
1142
|
-
ClaudeCodeContextMonitorProvider: () => ClaudeCodeContextMonitorProvider,
|
|
1143
|
-
ClaudeCodeHookProvider: () => ClaudeCodeHookProvider,
|
|
1144
|
-
ClaudeCodeInstallProvider: () => ClaudeCodeInstallProvider,
|
|
1145
|
-
ClaudeCodePathProvider: () => ClaudeCodePathProvider,
|
|
1146
|
-
ClaudeCodeSpawnProvider: () => ClaudeCodeSpawnProvider,
|
|
1147
|
-
ClaudeCodeTransportProvider: () => ClaudeCodeTransportProvider,
|
|
1148
|
-
checkStatuslineIntegration: () => checkStatuslineIntegration,
|
|
1149
|
-
createAdapter: () => createAdapter,
|
|
1150
|
-
default: () => claude_code_default,
|
|
1151
|
-
getSetupInstructions: () => getSetupInstructions,
|
|
1152
|
-
getStatuslineConfig: () => getStatuslineConfig
|
|
1153
|
-
});
|
|
1154
|
-
function createAdapter() {
|
|
1155
|
-
return new ClaudeCodeAdapter();
|
|
1156
|
-
}
|
|
1157
|
-
var claude_code_default;
|
|
1158
|
-
var init_claude_code = __esm({
|
|
1159
|
-
"packages/adapters/src/providers/claude-code/index.ts"() {
|
|
1160
|
-
"use strict";
|
|
1161
|
-
init_adapter();
|
|
1162
|
-
init_adapter();
|
|
1163
|
-
init_context_monitor();
|
|
1164
|
-
init_hooks();
|
|
1165
|
-
init_install();
|
|
1166
|
-
init_paths();
|
|
1167
|
-
init_spawn();
|
|
1168
|
-
init_statusline();
|
|
1169
|
-
init_transport();
|
|
1170
|
-
claude_code_default = ClaudeCodeAdapter;
|
|
1171
|
-
}
|
|
1172
|
-
});
|
|
1173
|
-
|
|
1174
|
-
// packages/adapters/src/providers/cursor/hooks.ts
|
|
1175
|
-
var PROVIDER_ID2, CURSOR_EVENT_MAP, CursorHookProvider;
|
|
1176
|
-
var init_hooks2 = __esm({
|
|
1177
|
-
"packages/adapters/src/providers/cursor/hooks.ts"() {
|
|
1178
|
-
"use strict";
|
|
1179
|
-
PROVIDER_ID2 = "cursor";
|
|
1180
|
-
CURSOR_EVENT_MAP = {
|
|
1181
|
-
// CAAMP: toNative('SessionStart', 'cursor') = 'sessionStart'
|
|
1182
|
-
sessionStart: "SessionStart",
|
|
1183
|
-
// CAAMP: toNative('SessionEnd', 'cursor') = 'sessionEnd'
|
|
1184
|
-
sessionEnd: "SessionEnd",
|
|
1185
|
-
// CAAMP: toNative('PromptSubmit', 'cursor') = 'beforeSubmitPrompt'
|
|
1186
|
-
beforeSubmitPrompt: "PromptSubmit",
|
|
1187
|
-
// CAAMP: toNative('ResponseComplete', 'cursor') = 'stop'
|
|
1188
|
-
stop: "ResponseComplete",
|
|
1189
|
-
// CAAMP: toNative('PreToolUse', 'cursor') = 'preToolUse'
|
|
1190
|
-
preToolUse: "PreToolUse",
|
|
1191
|
-
// CAAMP: toNative('PostToolUse', 'cursor') = 'postToolUse'
|
|
1192
|
-
postToolUse: "PostToolUse",
|
|
1193
|
-
// CAAMP: toNative('PostToolUseFailure', 'cursor') = 'postToolUseFailure'
|
|
1194
|
-
postToolUseFailure: "PostToolUseFailure",
|
|
1195
|
-
// CAAMP: toNative('SubagentStart', 'cursor') = 'subagentStart'
|
|
1196
|
-
subagentStart: "SubagentStart",
|
|
1197
|
-
// CAAMP: toNative('SubagentStop', 'cursor') = 'subagentStop'
|
|
1198
|
-
subagentStop: "SubagentStop",
|
|
1199
|
-
// CAAMP: toNative('PreCompact', 'cursor') = 'preCompact'
|
|
1200
|
-
preCompact: "PreCompact"
|
|
1201
|
-
};
|
|
1202
|
-
CursorHookProvider = class {
|
|
1203
|
-
/** Whether hooks have been registered for the current session. */
|
|
1204
|
-
registered = false;
|
|
1205
|
-
/**
|
|
1206
|
-
* Map a Cursor native event name to a CAAMP canonical hook event name.
|
|
1207
|
-
*
|
|
1208
|
-
* Looks up the native event name in the map derived from
|
|
1209
|
-
* `getProviderHookProfile('cursor').mappings` (CAAMP 1.9.1). Cursor uses
|
|
1210
|
-
* camelCase names (e.g. "preToolUse", "sessionStart").
|
|
1211
|
-
*
|
|
1212
|
-
* Returns null for unsupported events (PermissionRequest, PreModel,
|
|
1213
|
-
* PostModel, PostCompact, Notification, ConfigChange).
|
|
1214
|
-
*
|
|
1215
|
-
* @param providerEvent - Cursor native event name (e.g. "preToolUse", "sessionStart")
|
|
1216
|
-
* @returns CAAMP canonical event name, or null if unmapped
|
|
1217
|
-
* @task T165
|
|
1218
|
-
*/
|
|
1219
|
-
mapProviderEvent(providerEvent) {
|
|
1220
|
-
return CURSOR_EVENT_MAP[providerEvent] ?? null;
|
|
1221
|
-
}
|
|
1222
|
-
/**
|
|
1223
|
-
* Register native hooks for a project.
|
|
1224
|
-
*
|
|
1225
|
-
* For Cursor, hooks are registered via the config system
|
|
1226
|
-
* (`.cursor/hooks.json`), managed by the install provider.
|
|
1227
|
-
* This method marks hooks as registered without performing filesystem operations.
|
|
1228
|
-
*
|
|
1229
|
-
* Iterating supported events is handled at install time using
|
|
1230
|
-
* `getSupportedCanonicalEvents()` to enumerate all 10 supported hooks.
|
|
1231
|
-
*
|
|
1232
|
-
* @param _projectDir - Project directory (unused; Cursor config manages registration)
|
|
1233
|
-
* @task T165
|
|
1234
|
-
*/
|
|
1235
|
-
async registerNativeHooks(_projectDir) {
|
|
1236
|
-
this.registered = true;
|
|
1237
|
-
}
|
|
1238
|
-
/**
|
|
1239
|
-
* Unregister native hooks.
|
|
1240
|
-
*
|
|
1241
|
-
* For Cursor, this is a no-op since hooks are managed through the config
|
|
1242
|
-
* system. Unregistration happens via the install provider's uninstall method.
|
|
1243
|
-
*
|
|
1244
|
-
* @task T165
|
|
1245
|
-
*/
|
|
1246
|
-
async unregisterNativeHooks() {
|
|
1247
|
-
this.registered = false;
|
|
1248
|
-
}
|
|
1249
|
-
/**
|
|
1250
|
-
* Check whether hooks have been registered via `registerNativeHooks`.
|
|
1251
|
-
*/
|
|
1252
|
-
isRegistered() {
|
|
1253
|
-
return this.registered;
|
|
1254
|
-
}
|
|
1255
|
-
/**
|
|
1256
|
-
* Get the native→canonical event mapping for introspection and debugging.
|
|
1257
|
-
*
|
|
1258
|
-
* Returns the map derived from `getProviderHookProfile('cursor').mappings`
|
|
1259
|
-
* (CAAMP 1.9.1). Use `getSupportedCanonicalEvents()` to enumerate canonical
|
|
1260
|
-
* names via live CAAMP APIs.
|
|
1261
|
-
*
|
|
1262
|
-
* @returns Immutable record of native event name → canonical event name
|
|
1263
|
-
*/
|
|
1264
|
-
getEventMap() {
|
|
1265
|
-
return { ...CURSOR_EVENT_MAP };
|
|
1266
|
-
}
|
|
1267
|
-
/**
|
|
1268
|
-
* Enumerate supported canonical events via CAAMP's `getSupportedEvents()`.
|
|
1269
|
-
*
|
|
1270
|
-
* Calls `getSupportedEvents('cursor')` from the CAAMP normalizer to get the
|
|
1271
|
-
* authoritative list. Cursor supports 10 of 16 canonical events. Falls back
|
|
1272
|
-
* to the values of the static event map when CAAMP is unavailable at runtime.
|
|
1273
|
-
*
|
|
1274
|
-
* @returns Array of CAAMP canonical event names supported by Cursor
|
|
1275
|
-
* @task T165
|
|
1276
|
-
*/
|
|
1277
|
-
async getSupportedCanonicalEvents() {
|
|
1278
|
-
try {
|
|
1279
|
-
const { getSupportedEvents } = await import("@cleocode/caamp");
|
|
1280
|
-
return getSupportedEvents(PROVIDER_ID2);
|
|
1281
|
-
} catch {
|
|
1282
|
-
return [...new Set(Object.values(CURSOR_EVENT_MAP))];
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
/**
|
|
1286
|
-
* Retrieve the full provider hook profile from CAAMP.
|
|
1287
|
-
*
|
|
1288
|
-
* Calls `getProviderHookProfile('cursor')` from the CAAMP normalizer to
|
|
1289
|
-
* get the complete profile: hook system type (`config`), config path
|
|
1290
|
-
* (`.cursor/hooks.json`), handler types (command, prompt), and all event
|
|
1291
|
-
* mappings. Returns null when CAAMP is unavailable at runtime.
|
|
1292
|
-
*
|
|
1293
|
-
* @returns Provider hook profile or null if CAAMP is unavailable
|
|
1294
|
-
* @task T165
|
|
1295
|
-
*/
|
|
1296
|
-
async getProviderProfile() {
|
|
1297
|
-
try {
|
|
1298
|
-
const { getProviderHookProfile } = await import("@cleocode/caamp");
|
|
1299
|
-
return getProviderHookProfile(PROVIDER_ID2) ?? null;
|
|
1300
|
-
} catch {
|
|
1301
|
-
return null;
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
/**
|
|
1305
|
-
* Translate a CAAMP canonical event to its Cursor native name via CAAMP.
|
|
1306
|
-
*
|
|
1307
|
-
* Calls `toNative(canonical, 'cursor')` from the CAAMP normalizer.
|
|
1308
|
-
* Returns null for unsupported events or when CAAMP is unavailable.
|
|
1309
|
-
*
|
|
1310
|
-
* @param canonical - CAAMP canonical event name (e.g. "PreToolUse")
|
|
1311
|
-
* @returns Cursor native event name (e.g. "preToolUse") or null
|
|
1312
|
-
* @task T165
|
|
1313
|
-
*/
|
|
1314
|
-
async toNativeEvent(canonical) {
|
|
1315
|
-
try {
|
|
1316
|
-
const { toNative } = await import("@cleocode/caamp");
|
|
1317
|
-
return toNative(canonical, PROVIDER_ID2);
|
|
1318
|
-
} catch {
|
|
1319
|
-
const entry = Object.entries(CURSOR_EVENT_MAP).find(([, v]) => v === canonical);
|
|
1320
|
-
return entry?.[0] ?? null;
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
};
|
|
1324
|
-
}
|
|
1325
|
-
});
|
|
1326
|
-
|
|
1327
|
-
// packages/adapters/src/providers/cursor/install.ts
|
|
1328
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1329
|
-
import { join as join12 } from "node:path";
|
|
1330
|
-
var INSTRUCTION_REFERENCES3, CursorInstallProvider;
|
|
1331
|
-
var init_install2 = __esm({
|
|
1332
|
-
"packages/adapters/src/providers/cursor/install.ts"() {
|
|
1333
|
-
"use strict";
|
|
1334
|
-
INSTRUCTION_REFERENCES3 = ["@~/.cleo/templates/CLEO-INJECTION.md", "@.cleo/memory-bridge.md"];
|
|
1335
|
-
CursorInstallProvider = class {
|
|
1336
|
-
/**
|
|
1337
|
-
* Install CLEO into a Cursor project.
|
|
1338
|
-
*
|
|
1339
|
-
* @param options - Installation options including project directory
|
|
1340
|
-
* @returns Result describing what was installed
|
|
1341
|
-
*/
|
|
1342
|
-
async install(options) {
|
|
1343
|
-
const { projectDir } = options;
|
|
1344
|
-
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1345
|
-
let instructionFileUpdated = false;
|
|
1346
|
-
const details = {};
|
|
1347
|
-
instructionFileUpdated = this.updateInstructionFiles(projectDir);
|
|
1348
|
-
if (instructionFileUpdated) {
|
|
1349
|
-
details.instructionFiles = this.getUpdatedFileList(projectDir);
|
|
1350
|
-
}
|
|
1351
|
-
return {
|
|
1352
|
-
success: true,
|
|
1353
|
-
installedAt,
|
|
1354
|
-
instructionFileUpdated,
|
|
1355
|
-
mcpRegistered: false,
|
|
1356
|
-
details
|
|
1357
|
-
};
|
|
1358
|
-
}
|
|
1359
|
-
/**
|
|
1360
|
-
* Uninstall CLEO from the current Cursor project.
|
|
1361
|
-
*
|
|
1362
|
-
* Does not remove instruction file references (they are harmless if CLEO is not present).
|
|
1363
|
-
*/
|
|
1364
|
-
async uninstall() {
|
|
1365
|
-
}
|
|
1366
|
-
/**
|
|
1367
|
-
* Check whether CLEO is installed in the current environment.
|
|
1368
|
-
*
|
|
1369
|
-
* Checks for .cursor/rules/cleo.mdc or .cursorrules with CLEO references.
|
|
1370
|
-
*/
|
|
1371
|
-
async isInstalled() {
|
|
1372
|
-
const mdcPath = join12(process.cwd(), ".cursor", "rules", "cleo.mdc");
|
|
1373
|
-
if (existsSync7(mdcPath)) {
|
|
1374
|
-
return true;
|
|
1375
|
-
}
|
|
1376
|
-
const rulesPath = join12(process.cwd(), ".cursorrules");
|
|
1377
|
-
if (existsSync7(rulesPath)) {
|
|
1378
|
-
try {
|
|
1379
|
-
const content = readFileSync5(rulesPath, "utf-8");
|
|
1380
|
-
if (INSTRUCTION_REFERENCES3.some((ref) => content.includes(ref))) {
|
|
1381
|
-
return true;
|
|
1382
|
-
}
|
|
1383
|
-
} catch {
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
return false;
|
|
1387
|
-
}
|
|
1388
|
-
/**
|
|
1389
|
-
* Ensure instruction files contain @-references to CLEO.
|
|
1390
|
-
*
|
|
1391
|
-
* Updates .cursorrules (legacy) and creates .cursor/rules/cleo.mdc (modern).
|
|
1392
|
-
*
|
|
1393
|
-
* @param projectDir - Project root directory
|
|
1394
|
-
*/
|
|
1395
|
-
async ensureInstructionReferences(projectDir) {
|
|
1396
|
-
this.updateInstructionFiles(projectDir);
|
|
1397
|
-
}
|
|
1398
|
-
/**
|
|
1399
|
-
* Update instruction files with CLEO @-references.
|
|
1400
|
-
*
|
|
1401
|
-
* Handles both legacy (.cursorrules) and modern (.cursor/rules/cleo.mdc) formats.
|
|
1402
|
-
*
|
|
1403
|
-
* @returns true if any file was created or modified
|
|
1404
|
-
*/
|
|
1405
|
-
updateInstructionFiles(projectDir) {
|
|
1406
|
-
let updated = false;
|
|
1407
|
-
if (this.updateLegacyRules(projectDir)) {
|
|
1408
|
-
updated = true;
|
|
1409
|
-
}
|
|
1410
|
-
if (this.updateModernRules(projectDir)) {
|
|
1411
|
-
updated = true;
|
|
1412
|
-
}
|
|
1413
|
-
return updated;
|
|
1414
|
-
}
|
|
1415
|
-
/**
|
|
1416
|
-
* Update legacy .cursorrules file with @-references.
|
|
1417
|
-
* Only modifies the file if it already exists (does not create it).
|
|
1418
|
-
*
|
|
1419
|
-
* @returns true if the file was modified
|
|
1420
|
-
*/
|
|
1421
|
-
updateLegacyRules(projectDir) {
|
|
1422
|
-
const rulesPath = join12(projectDir, ".cursorrules");
|
|
1423
|
-
if (!existsSync7(rulesPath)) {
|
|
1424
|
-
return false;
|
|
1425
|
-
}
|
|
1426
|
-
let content = readFileSync5(rulesPath, "utf-8");
|
|
1427
|
-
const missingRefs = INSTRUCTION_REFERENCES3.filter((ref) => !content.includes(ref));
|
|
1428
|
-
if (missingRefs.length === 0) {
|
|
1429
|
-
return false;
|
|
1430
|
-
}
|
|
1431
|
-
const separator = content.endsWith("\n") ? "" : "\n";
|
|
1432
|
-
content = content + separator + missingRefs.join("\n") + "\n";
|
|
1433
|
-
writeFileSync4(rulesPath, content, "utf-8");
|
|
1434
|
-
return true;
|
|
1435
|
-
}
|
|
1436
|
-
/**
|
|
1437
|
-
* Create or update .cursor/rules/cleo.mdc with CLEO references.
|
|
1438
|
-
*
|
|
1439
|
-
* MDC (Markdown Component) format is Cursor's modern rule file format.
|
|
1440
|
-
* Each .mdc file in .cursor/rules/ is loaded as a rule set.
|
|
1441
|
-
*
|
|
1442
|
-
* @returns true if the file was created or modified
|
|
1443
|
-
*/
|
|
1444
|
-
updateModernRules(projectDir) {
|
|
1445
|
-
const rulesDir = join12(projectDir, ".cursor", "rules");
|
|
1446
|
-
const mdcPath = join12(rulesDir, "cleo.mdc");
|
|
1447
|
-
const expectedContent = [
|
|
1448
|
-
"---",
|
|
1449
|
-
"description: CLEO task management protocol references",
|
|
1450
|
-
'globs: "**/*"',
|
|
1451
|
-
"alwaysApply: true",
|
|
1452
|
-
"---",
|
|
1453
|
-
"",
|
|
1454
|
-
...INSTRUCTION_REFERENCES3,
|
|
1455
|
-
""
|
|
1456
|
-
].join("\n");
|
|
1457
|
-
if (existsSync7(mdcPath)) {
|
|
1458
|
-
const existing = readFileSync5(mdcPath, "utf-8");
|
|
1459
|
-
if (existing === expectedContent) {
|
|
1460
|
-
return false;
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
mkdirSync2(rulesDir, { recursive: true });
|
|
1464
|
-
writeFileSync4(mdcPath, expectedContent, "utf-8");
|
|
1465
|
-
return true;
|
|
1466
|
-
}
|
|
1467
|
-
/**
|
|
1468
|
-
* Get list of instruction files that were updated.
|
|
1469
|
-
*/
|
|
1470
|
-
getUpdatedFileList(projectDir) {
|
|
1471
|
-
const files = [];
|
|
1472
|
-
if (existsSync7(join12(projectDir, ".cursorrules"))) {
|
|
1473
|
-
files.push(join12(projectDir, ".cursorrules"));
|
|
1474
|
-
}
|
|
1475
|
-
files.push(join12(projectDir, ".cursor", "rules", "cleo.mdc"));
|
|
1476
|
-
return files;
|
|
1477
|
-
}
|
|
1478
|
-
};
|
|
1479
|
-
}
|
|
1480
|
-
});
|
|
1481
|
-
|
|
1482
|
-
// packages/adapters/src/providers/cursor/adapter.ts
|
|
1483
|
-
import { existsSync as existsSync8 } from "node:fs";
|
|
1484
|
-
import { join as join13 } from "node:path";
|
|
1485
|
-
var CursorAdapter;
|
|
1486
|
-
var init_adapter2 = __esm({
|
|
1487
|
-
"packages/adapters/src/providers/cursor/adapter.ts"() {
|
|
1488
|
-
"use strict";
|
|
1489
|
-
init_hooks2();
|
|
1490
|
-
init_install2();
|
|
1491
|
-
CursorAdapter = class {
|
|
1492
|
-
/** Unique provider identifier. */
|
|
1493
|
-
id = "cursor";
|
|
1494
|
-
/** Human-readable provider name. */
|
|
1495
|
-
name = "Cursor";
|
|
1496
|
-
/** Adapter version string. */
|
|
1497
|
-
version = "1.0.0";
|
|
1498
|
-
/** Declared capabilities for this provider. */
|
|
1499
|
-
capabilities = {
|
|
1500
|
-
supportsHooks: true,
|
|
1501
|
-
// 10/16 canonical events — derived from getProviderHookProfile('cursor') in CAAMP 1.9.1.
|
|
1502
|
-
// PermissionRequest, PreModel, PostModel, PostCompact, Notification, ConfigChange are
|
|
1503
|
-
// not supported by Cursor's hook system.
|
|
1504
|
-
supportedHookEvents: [
|
|
1505
|
-
"SessionStart",
|
|
1506
|
-
"SessionEnd",
|
|
1507
|
-
"PromptSubmit",
|
|
1508
|
-
"ResponseComplete",
|
|
1509
|
-
"PreToolUse",
|
|
1510
|
-
"PostToolUse",
|
|
1511
|
-
"PostToolUseFailure",
|
|
1512
|
-
"SubagentStart",
|
|
1513
|
-
"SubagentStop",
|
|
1514
|
-
"PreCompact"
|
|
1515
|
-
],
|
|
1516
|
-
supportsSpawn: false,
|
|
1517
|
-
supportsInstall: true,
|
|
1518
|
-
supportsMcp: false,
|
|
1519
|
-
supportsInstructionFiles: true,
|
|
1520
|
-
instructionFilePattern: ".cursor/rules/*.mdc",
|
|
1521
|
-
supportsContextMonitor: false,
|
|
1522
|
-
supportsStatusline: false,
|
|
1523
|
-
supportsProviderPaths: true,
|
|
1524
|
-
supportsTransport: false,
|
|
1525
|
-
supportsTaskSync: false
|
|
1526
|
-
};
|
|
1527
|
-
/** Hook provider for CAAMP event mapping. */
|
|
1528
|
-
hooks;
|
|
1529
|
-
/** Install provider for managing rule files. */
|
|
1530
|
-
install;
|
|
1531
|
-
/** Project directory this adapter was initialized with, or null. */
|
|
1532
|
-
projectDir = null;
|
|
1533
|
-
/** Whether {@link initialize} has been called. */
|
|
1534
|
-
initialized = false;
|
|
1535
|
-
constructor() {
|
|
1536
|
-
this.hooks = new CursorHookProvider();
|
|
1537
|
-
this.install = new CursorInstallProvider();
|
|
1538
|
-
}
|
|
1539
|
-
/**
|
|
1540
|
-
* Initialize the adapter for a given project directory.
|
|
1541
|
-
*
|
|
1542
|
-
* @param projectDir - Root directory of the project
|
|
1543
|
-
*/
|
|
1544
|
-
async initialize(projectDir) {
|
|
1545
|
-
this.projectDir = projectDir;
|
|
1546
|
-
this.initialized = true;
|
|
1547
|
-
}
|
|
1548
|
-
/**
|
|
1549
|
-
* Dispose the adapter and clean up resources.
|
|
1550
|
-
*/
|
|
1551
|
-
async dispose() {
|
|
1552
|
-
if (this.hooks.isRegistered()) {
|
|
1553
|
-
await this.hooks.unregisterNativeHooks();
|
|
1554
|
-
}
|
|
1555
|
-
this.initialized = false;
|
|
1556
|
-
this.projectDir = null;
|
|
1557
|
-
}
|
|
1558
|
-
/**
|
|
1559
|
-
* Run a health check to verify Cursor is accessible.
|
|
1560
|
-
*
|
|
1561
|
-
* Checks:
|
|
1562
|
-
* 1. Adapter has been initialized
|
|
1563
|
-
* 2. .cursor/ configuration directory exists in the project
|
|
1564
|
-
* 3. CURSOR_EDITOR env var is set
|
|
1565
|
-
*
|
|
1566
|
-
* @returns Health status with details about each check
|
|
1567
|
-
*/
|
|
1568
|
-
async healthCheck() {
|
|
1569
|
-
const details = {};
|
|
1570
|
-
if (!this.initialized) {
|
|
1571
|
-
return {
|
|
1572
|
-
healthy: false,
|
|
1573
|
-
provider: this.id,
|
|
1574
|
-
details: { error: "Adapter not initialized" }
|
|
1575
|
-
};
|
|
1576
|
-
}
|
|
1577
|
-
let configExists = false;
|
|
1578
|
-
if (this.projectDir) {
|
|
1579
|
-
const cursorConfigDir = join13(this.projectDir, ".cursor");
|
|
1580
|
-
configExists = existsSync8(cursorConfigDir);
|
|
1581
|
-
details.configDirExists = configExists;
|
|
1582
|
-
}
|
|
1583
|
-
const editorEnvSet = process.env.CURSOR_EDITOR !== void 0;
|
|
1584
|
-
details.editorEnvSet = editorEnvSet;
|
|
1585
|
-
if (this.projectDir) {
|
|
1586
|
-
const legacyRulesExist = existsSync8(join13(this.projectDir, ".cursorrules"));
|
|
1587
|
-
details.legacyRulesExist = legacyRulesExist;
|
|
1588
|
-
}
|
|
1589
|
-
const healthy = configExists || editorEnvSet;
|
|
1590
|
-
details.detected = healthy;
|
|
1591
|
-
return {
|
|
1592
|
-
healthy,
|
|
1593
|
-
provider: this.id,
|
|
1594
|
-
details
|
|
1595
|
-
};
|
|
1596
|
-
}
|
|
1597
|
-
/**
|
|
1598
|
-
* Check whether the adapter has been initialized.
|
|
1599
|
-
*/
|
|
1600
|
-
isInitialized() {
|
|
1601
|
-
return this.initialized;
|
|
1602
|
-
}
|
|
1603
|
-
/**
|
|
1604
|
-
* Get the project directory this adapter was initialized with.
|
|
1605
|
-
*/
|
|
1606
|
-
getProjectDir() {
|
|
1607
|
-
return this.projectDir;
|
|
1608
|
-
}
|
|
1609
|
-
};
|
|
1610
|
-
}
|
|
1611
|
-
});
|
|
1612
|
-
|
|
1613
|
-
// packages/adapters/src/providers/cursor/index.ts
|
|
1614
|
-
var cursor_exports = {};
|
|
1615
|
-
__export(cursor_exports, {
|
|
1616
|
-
CursorAdapter: () => CursorAdapter,
|
|
1617
|
-
CursorHookProvider: () => CursorHookProvider,
|
|
1618
|
-
CursorInstallProvider: () => CursorInstallProvider,
|
|
1619
|
-
createAdapter: () => createAdapter3,
|
|
1620
|
-
default: () => cursor_default
|
|
1621
|
-
});
|
|
1622
|
-
function createAdapter3() {
|
|
1623
|
-
return new CursorAdapter();
|
|
1624
|
-
}
|
|
1625
|
-
var cursor_default;
|
|
1626
|
-
var init_cursor = __esm({
|
|
1627
|
-
"packages/adapters/src/providers/cursor/index.ts"() {
|
|
1628
|
-
"use strict";
|
|
1629
|
-
init_adapter2();
|
|
1630
|
-
init_adapter2();
|
|
1631
|
-
init_hooks2();
|
|
1632
|
-
init_install2();
|
|
1633
|
-
cursor_default = CursorAdapter;
|
|
1634
|
-
}
|
|
1635
|
-
});
|
|
1636
|
-
|
|
1637
|
-
// packages/adapters/src/providers/opencode/hooks.ts
|
|
1638
|
-
var PROVIDER_ID3, OPENCODE_EVENT_MAP, OpenCodeHookProvider;
|
|
1639
|
-
var init_hooks3 = __esm({
|
|
1640
|
-
"packages/adapters/src/providers/opencode/hooks.ts"() {
|
|
1641
|
-
"use strict";
|
|
1642
|
-
PROVIDER_ID3 = "opencode";
|
|
1643
|
-
OPENCODE_EVENT_MAP = {
|
|
1644
|
-
// CAAMP: toNative('SessionStart', 'opencode') = 'event:session.created'
|
|
1645
|
-
"event:session.created": "SessionStart",
|
|
1646
|
-
// CAAMP: toNative('SessionEnd', 'opencode') = 'event:session.deleted'
|
|
1647
|
-
"event:session.deleted": "SessionEnd",
|
|
1648
|
-
// CAAMP: toNative('PromptSubmit', 'opencode') = 'chat.message'
|
|
1649
|
-
"chat.message": "PromptSubmit",
|
|
1650
|
-
// CAAMP: toNative('ResponseComplete', 'opencode') = 'event:session.idle'
|
|
1651
|
-
"event:session.idle": "ResponseComplete",
|
|
1652
|
-
// CAAMP: toNative('PreToolUse', 'opencode') = 'tool.execute.before'
|
|
1653
|
-
"tool.execute.before": "PreToolUse",
|
|
1654
|
-
// CAAMP: toNative('PostToolUse', 'opencode') = 'tool.execute.after'
|
|
1655
|
-
"tool.execute.after": "PostToolUse",
|
|
1656
|
-
// CAAMP: toNative('PermissionRequest', 'opencode') = 'permission.ask'
|
|
1657
|
-
"permission.ask": "PermissionRequest",
|
|
1658
|
-
// CAAMP: toNative('PreModel', 'opencode') = 'chat.params'
|
|
1659
|
-
"chat.params": "PreModel",
|
|
1660
|
-
// CAAMP: toNative('PreCompact', 'opencode') = 'experimental.session.compacting'
|
|
1661
|
-
"experimental.session.compacting": "PreCompact",
|
|
1662
|
-
// CAAMP: toNative('PostCompact', 'opencode') = 'event:session.compacted'
|
|
1663
|
-
"event:session.compacted": "PostCompact"
|
|
1664
|
-
};
|
|
1665
|
-
OpenCodeHookProvider = class {
|
|
1666
|
-
/** Whether hooks have been registered for the current session. */
|
|
1667
|
-
registered = false;
|
|
1668
|
-
/**
|
|
1669
|
-
* Map an OpenCode native event name to a CAAMP canonical hook event name.
|
|
1670
|
-
*
|
|
1671
|
-
* Looks up the native event name in the map derived from
|
|
1672
|
-
* `getProviderHookProfile('opencode').mappings` (CAAMP 1.9.1).
|
|
1673
|
-
* Returns null for unsupported events (PostToolUseFailure, SubagentStart,
|
|
1674
|
-
* SubagentStop, Notification, ConfigChange).
|
|
1675
|
-
*
|
|
1676
|
-
* @param providerEvent - OpenCode native event (e.g. "event:session.created", "tool.execute.before")
|
|
1677
|
-
* @returns CAAMP canonical event name, or null if unmapped
|
|
1678
|
-
* @task T164
|
|
1679
|
-
*/
|
|
1680
|
-
mapProviderEvent(providerEvent) {
|
|
1681
|
-
return OPENCODE_EVENT_MAP[providerEvent] ?? null;
|
|
1682
|
-
}
|
|
1683
|
-
/**
|
|
1684
|
-
* Register native hooks for a project.
|
|
1685
|
-
*
|
|
1686
|
-
* For OpenCode, hooks are registered via the plugin system
|
|
1687
|
-
* (`.opencode/plugins/`), managed by the install provider.
|
|
1688
|
-
* This method marks hooks as registered without performing filesystem operations.
|
|
1689
|
-
*
|
|
1690
|
-
* Iterating supported events is handled at install time using
|
|
1691
|
-
* `getSupportedCanonicalEvents()` to enumerate all 10 supported hooks.
|
|
1692
|
-
*
|
|
1693
|
-
* @param _projectDir - Project directory (unused; config manages registration)
|
|
1694
|
-
* @task T164
|
|
1695
|
-
*/
|
|
1696
|
-
async registerNativeHooks(_projectDir) {
|
|
1697
|
-
this.registered = true;
|
|
1698
|
-
}
|
|
1699
|
-
/**
|
|
1700
|
-
* Unregister native hooks.
|
|
1701
|
-
*
|
|
1702
|
-
* For OpenCode, this is a no-op since hooks are managed through the plugin
|
|
1703
|
-
* system. Unregistration happens via the install provider's uninstall method.
|
|
1704
|
-
*
|
|
1705
|
-
* @task T164
|
|
1706
|
-
*/
|
|
1707
|
-
async unregisterNativeHooks() {
|
|
1708
|
-
this.registered = false;
|
|
1709
|
-
}
|
|
1710
|
-
/**
|
|
1711
|
-
* Check whether hooks have been registered via `registerNativeHooks`.
|
|
1712
|
-
*/
|
|
1713
|
-
isRegistered() {
|
|
1714
|
-
return this.registered;
|
|
1715
|
-
}
|
|
1716
|
-
/**
|
|
1717
|
-
* Get the native→canonical event mapping for introspection and debugging.
|
|
1718
|
-
*
|
|
1719
|
-
* Returns the map derived from `getProviderHookProfile('opencode').mappings`
|
|
1720
|
-
* (CAAMP 1.9.1). Use `getSupportedCanonicalEvents()` to enumerate canonical
|
|
1721
|
-
* names via live CAAMP APIs.
|
|
1722
|
-
*
|
|
1723
|
-
* @returns Immutable record of native event name → canonical event name
|
|
1724
|
-
*/
|
|
1725
|
-
getEventMap() {
|
|
1726
|
-
return { ...OPENCODE_EVENT_MAP };
|
|
1727
|
-
}
|
|
1728
|
-
/**
|
|
1729
|
-
* Enumerate supported canonical events via CAAMP's `getSupportedEvents()`.
|
|
1730
|
-
*
|
|
1731
|
-
* Calls `getSupportedEvents('opencode')` from the CAAMP normalizer to get the
|
|
1732
|
-
* authoritative list. OpenCode supports 10 of 16 canonical events via its
|
|
1733
|
-
* plugin system. Falls back to the values of the static event map when
|
|
1734
|
-
* CAAMP is unavailable at runtime.
|
|
1735
|
-
*
|
|
1736
|
-
* @returns Array of CAAMP canonical event names supported by OpenCode
|
|
1737
|
-
* @task T164
|
|
1738
|
-
*/
|
|
1739
|
-
async getSupportedCanonicalEvents() {
|
|
1740
|
-
try {
|
|
1741
|
-
const { getSupportedEvents } = await import("@cleocode/caamp");
|
|
1742
|
-
return getSupportedEvents(PROVIDER_ID3);
|
|
1743
|
-
} catch {
|
|
1744
|
-
return [...new Set(Object.values(OPENCODE_EVENT_MAP))];
|
|
1745
|
-
}
|
|
1746
|
-
}
|
|
1747
|
-
/**
|
|
1748
|
-
* Retrieve the full provider hook profile from CAAMP.
|
|
1749
|
-
*
|
|
1750
|
-
* Calls `getProviderHookProfile('opencode')` from the CAAMP normalizer to
|
|
1751
|
-
* get the complete profile: hook system type (`plugin`), config path
|
|
1752
|
-
* (`.opencode/plugins/`), handler types, and all event mappings.
|
|
1753
|
-
* Returns null when CAAMP is unavailable at runtime.
|
|
1754
|
-
*
|
|
1755
|
-
* @returns Provider hook profile or null if CAAMP is unavailable
|
|
1756
|
-
* @task T164
|
|
1757
|
-
*/
|
|
1758
|
-
async getProviderProfile() {
|
|
1759
|
-
try {
|
|
1760
|
-
const { getProviderHookProfile } = await import("@cleocode/caamp");
|
|
1761
|
-
return getProviderHookProfile(PROVIDER_ID3) ?? null;
|
|
1762
|
-
} catch {
|
|
1763
|
-
return null;
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
/**
|
|
1767
|
-
* Translate a CAAMP canonical event to its OpenCode native name via CAAMP.
|
|
1768
|
-
*
|
|
1769
|
-
* Calls `toNative(canonical, 'opencode')` from the CAAMP normalizer.
|
|
1770
|
-
* Returns null for unsupported events or when CAAMP is unavailable.
|
|
1771
|
-
*
|
|
1772
|
-
* @param canonical - CAAMP canonical event name (e.g. "PreToolUse")
|
|
1773
|
-
* @returns OpenCode native event name or null
|
|
1774
|
-
* @task T164
|
|
1775
|
-
*/
|
|
1776
|
-
async toNativeEvent(canonical) {
|
|
1777
|
-
try {
|
|
1778
|
-
const { toNative } = await import("@cleocode/caamp");
|
|
1779
|
-
return toNative(canonical, PROVIDER_ID3);
|
|
1780
|
-
} catch {
|
|
1781
|
-
const entry = Object.entries(OPENCODE_EVENT_MAP).find(([, v]) => v === canonical);
|
|
1782
|
-
return entry?.[0] ?? null;
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
};
|
|
1786
|
-
}
|
|
1787
|
-
});
|
|
1788
|
-
|
|
1789
|
-
// packages/adapters/src/providers/opencode/install.ts
|
|
1790
|
-
import { existsSync as existsSync13, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "node:fs";
|
|
1791
|
-
import { join as join19 } from "node:path";
|
|
1792
|
-
var INSTRUCTION_REFERENCES6, OpenCodeInstallProvider;
|
|
1793
|
-
var init_install3 = __esm({
|
|
1794
|
-
"packages/adapters/src/providers/opencode/install.ts"() {
|
|
1795
|
-
"use strict";
|
|
1796
|
-
INSTRUCTION_REFERENCES6 = ["@~/.cleo/templates/CLEO-INJECTION.md", "@.cleo/memory-bridge.md"];
|
|
1797
|
-
OpenCodeInstallProvider = class {
|
|
1798
|
-
/**
|
|
1799
|
-
* Install CLEO into an OpenCode project.
|
|
1800
|
-
*
|
|
1801
|
-
* @param options - Installation options including project directory
|
|
1802
|
-
* @returns Result describing what was installed
|
|
1803
|
-
*/
|
|
1804
|
-
async install(options) {
|
|
1805
|
-
const { projectDir } = options;
|
|
1806
|
-
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1807
|
-
let instructionFileUpdated = false;
|
|
1808
|
-
const details = {};
|
|
1809
|
-
instructionFileUpdated = this.updateInstructionFile(projectDir);
|
|
1810
|
-
if (instructionFileUpdated) {
|
|
1811
|
-
details.instructionFile = join19(projectDir, "AGENTS.md");
|
|
1812
|
-
}
|
|
1813
|
-
return {
|
|
1814
|
-
success: true,
|
|
1815
|
-
installedAt,
|
|
1816
|
-
instructionFileUpdated,
|
|
1817
|
-
mcpRegistered: false,
|
|
1818
|
-
details
|
|
1819
|
-
};
|
|
1820
|
-
}
|
|
1821
|
-
/**
|
|
1822
|
-
* Uninstall CLEO from the current OpenCode project.
|
|
1823
|
-
*
|
|
1824
|
-
* Does not remove AGENTS.md references (they are harmless if CLEO is not present).
|
|
1825
|
-
*/
|
|
1826
|
-
async uninstall() {
|
|
1827
|
-
}
|
|
1828
|
-
/**
|
|
1829
|
-
* Check whether CLEO is installed in the current environment.
|
|
1830
|
-
*
|
|
1831
|
-
* Checks for CLEO references in AGENTS.md.
|
|
1832
|
-
*/
|
|
1833
|
-
async isInstalled() {
|
|
1834
|
-
const agentsMdPath = join19(process.cwd(), "AGENTS.md");
|
|
1835
|
-
if (existsSync13(agentsMdPath)) {
|
|
1836
|
-
try {
|
|
1837
|
-
const content = readFileSync8(agentsMdPath, "utf-8");
|
|
1838
|
-
if (INSTRUCTION_REFERENCES6.some((ref) => content.includes(ref))) {
|
|
1839
|
-
return true;
|
|
1840
|
-
}
|
|
1841
|
-
} catch {
|
|
1842
|
-
}
|
|
1843
|
-
}
|
|
1844
|
-
return false;
|
|
1845
|
-
}
|
|
1846
|
-
/**
|
|
1847
|
-
* Ensure AGENTS.md contains @-references to CLEO instruction files.
|
|
1848
|
-
*
|
|
1849
|
-
* Creates AGENTS.md if it does not exist. Appends any missing references.
|
|
1850
|
-
*
|
|
1851
|
-
* @param projectDir - Project root directory
|
|
1852
|
-
*/
|
|
1853
|
-
async ensureInstructionReferences(projectDir) {
|
|
1854
|
-
this.updateInstructionFile(projectDir);
|
|
1855
|
-
}
|
|
1856
|
-
/**
|
|
1857
|
-
* Update AGENTS.md with CLEO @-references.
|
|
1858
|
-
*
|
|
1859
|
-
* @returns true if the file was created or modified
|
|
1860
|
-
*/
|
|
1861
|
-
updateInstructionFile(projectDir) {
|
|
1862
|
-
const agentsMdPath = join19(projectDir, "AGENTS.md");
|
|
1863
|
-
let content = "";
|
|
1864
|
-
let existed = false;
|
|
1865
|
-
if (existsSync13(agentsMdPath)) {
|
|
1866
|
-
content = readFileSync8(agentsMdPath, "utf-8");
|
|
1867
|
-
existed = true;
|
|
1868
|
-
}
|
|
1869
|
-
const missingRefs = INSTRUCTION_REFERENCES6.filter((ref) => !content.includes(ref));
|
|
1870
|
-
if (missingRefs.length === 0) {
|
|
1871
|
-
return false;
|
|
1872
|
-
}
|
|
1873
|
-
const refsBlock = missingRefs.join("\n");
|
|
1874
|
-
if (existed) {
|
|
1875
|
-
const separator = content.endsWith("\n") ? "" : "\n";
|
|
1876
|
-
content = content + separator + refsBlock + "\n";
|
|
1877
|
-
} else {
|
|
1878
|
-
content = refsBlock + "\n";
|
|
1879
|
-
}
|
|
1880
|
-
writeFileSync7(agentsMdPath, content, "utf-8");
|
|
1881
|
-
return true;
|
|
1882
|
-
}
|
|
1883
|
-
};
|
|
1884
|
-
}
|
|
1885
|
-
});
|
|
1886
|
-
|
|
1887
|
-
// packages/adapters/src/providers/opencode/spawn.ts
|
|
1888
|
-
import { exec as exec6, spawn as nodeSpawn2 } from "node:child_process";
|
|
1889
|
-
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
|
|
1890
|
-
import { join as join20 } from "node:path";
|
|
1891
|
-
import { promisify as promisify6 } from "node:util";
|
|
1892
|
-
function buildOpenCodeAgentMarkdown(description, instructions) {
|
|
1893
|
-
const normalizedDesc = description.replace(/\s+/g, " ").trim();
|
|
1894
|
-
return [
|
|
1895
|
-
"---",
|
|
1896
|
-
`description: ${JSON.stringify(normalizedDesc)}`,
|
|
1897
|
-
"mode: subagent",
|
|
1898
|
-
"hidden: true",
|
|
1899
|
-
"---",
|
|
1900
|
-
"",
|
|
1901
|
-
instructions.trim(),
|
|
1902
|
-
""
|
|
1903
|
-
].join("\n");
|
|
1904
|
-
}
|
|
1905
|
-
async function ensureSubagentDefinition(workingDirectory) {
|
|
1906
|
-
const agentDir = join20(workingDirectory, ".opencode", "agent");
|
|
1907
|
-
const agentPath = join20(agentDir, `${OPENCODE_SUBAGENT_NAME}.md`);
|
|
1908
|
-
const description = "CLEO task executor with protocol compliance.";
|
|
1909
|
-
const instructions = [
|
|
1910
|
-
"# CLEO Subagent",
|
|
1911
|
-
"",
|
|
1912
|
-
"You are a CLEO subagent executing a delegated task.",
|
|
1913
|
-
"Follow the CLEO protocol and complete the assigned work.",
|
|
1914
|
-
"",
|
|
1915
|
-
"@~/.cleo/templates/CLEO-INJECTION.md"
|
|
1916
|
-
].join("\n");
|
|
1917
|
-
const content = buildOpenCodeAgentMarkdown(description, instructions);
|
|
1918
|
-
await mkdir2(agentDir, { recursive: true });
|
|
1919
|
-
let existing = null;
|
|
1920
|
-
try {
|
|
1921
|
-
existing = await readFile4(agentPath, "utf-8");
|
|
1922
|
-
} catch {
|
|
1923
|
-
existing = null;
|
|
1924
|
-
}
|
|
1925
|
-
if (existing !== content) {
|
|
1926
|
-
await writeFile2(agentPath, content, "utf-8");
|
|
1927
|
-
}
|
|
1928
|
-
return OPENCODE_SUBAGENT_NAME;
|
|
1929
|
-
}
|
|
1930
|
-
var execAsync6, OPENCODE_SUBAGENT_NAME, OPENCODE_FALLBACK_AGENT, OpenCodeSpawnProvider;
|
|
1931
|
-
var init_spawn2 = __esm({
|
|
1932
|
-
"packages/adapters/src/providers/opencode/spawn.ts"() {
|
|
1933
|
-
"use strict";
|
|
1934
|
-
execAsync6 = promisify6(exec6);
|
|
1935
|
-
OPENCODE_SUBAGENT_NAME = "cleo-subagent";
|
|
1936
|
-
OPENCODE_FALLBACK_AGENT = "general";
|
|
1937
|
-
OpenCodeSpawnProvider = class {
|
|
1938
|
-
/** Map of instance IDs to tracked process info. */
|
|
1939
|
-
processMap = /* @__PURE__ */ new Map();
|
|
1940
|
-
/**
|
|
1941
|
-
* Check if the OpenCode CLI is available in PATH.
|
|
1942
|
-
*
|
|
1943
|
-
* @returns true if `opencode` is found via `which`
|
|
1944
|
-
*/
|
|
1945
|
-
async canSpawn() {
|
|
1946
|
-
try {
|
|
1947
|
-
await execAsync6("which opencode");
|
|
1948
|
-
return true;
|
|
1949
|
-
} catch {
|
|
1950
|
-
return false;
|
|
1951
|
-
}
|
|
1952
|
-
}
|
|
1953
|
-
/**
|
|
1954
|
-
* Spawn a subagent via OpenCode CLI.
|
|
1955
|
-
*
|
|
1956
|
-
* Ensures the CLEO subagent definition exists in the project's
|
|
1957
|
-
* .opencode/agent/ directory, then spawns a detached OpenCode
|
|
1958
|
-
* process. The process runs independently of the parent.
|
|
1959
|
-
*
|
|
1960
|
-
* @param context - Spawn context with taskId, prompt, and options
|
|
1961
|
-
* @returns Spawn result with instance ID and status
|
|
1962
|
-
*/
|
|
1963
|
-
async spawn(context) {
|
|
1964
|
-
const instanceId = `opencode-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
1965
|
-
const startTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
1966
|
-
const workingDirectory = context.workingDirectory ?? process.cwd();
|
|
1967
|
-
try {
|
|
1968
|
-
let agentName;
|
|
1969
|
-
try {
|
|
1970
|
-
agentName = await ensureSubagentDefinition(workingDirectory);
|
|
1971
|
-
} catch {
|
|
1972
|
-
agentName = OPENCODE_FALLBACK_AGENT;
|
|
1973
|
-
}
|
|
1974
|
-
const child = nodeSpawn2(
|
|
1975
|
-
"opencode",
|
|
1976
|
-
[
|
|
1977
|
-
"run",
|
|
1978
|
-
"--format",
|
|
1979
|
-
"json",
|
|
1980
|
-
"--agent",
|
|
1981
|
-
agentName,
|
|
1982
|
-
"--title",
|
|
1983
|
-
`CLEO ${context.taskId}`,
|
|
1984
|
-
context.prompt
|
|
1985
|
-
],
|
|
1986
|
-
{
|
|
1987
|
-
cwd: workingDirectory,
|
|
1988
|
-
detached: true,
|
|
1989
|
-
stdio: "ignore"
|
|
1990
|
-
}
|
|
1991
|
-
);
|
|
1992
|
-
child.unref();
|
|
1993
|
-
if (child.pid) {
|
|
1994
|
-
this.processMap.set(instanceId, {
|
|
1995
|
-
pid: child.pid,
|
|
1996
|
-
taskId: context.taskId,
|
|
1997
|
-
startTime
|
|
1998
|
-
});
|
|
1999
|
-
}
|
|
2000
|
-
child.on("exit", () => {
|
|
2001
|
-
this.processMap.delete(instanceId);
|
|
2002
|
-
});
|
|
2003
|
-
return {
|
|
2004
|
-
instanceId,
|
|
2005
|
-
taskId: context.taskId,
|
|
2006
|
-
providerId: "opencode",
|
|
2007
|
-
status: "running",
|
|
2008
|
-
startTime
|
|
2009
|
-
};
|
|
2010
|
-
} catch {
|
|
2011
|
-
return {
|
|
2012
|
-
instanceId,
|
|
2013
|
-
taskId: context.taskId,
|
|
2014
|
-
providerId: "opencode",
|
|
2015
|
-
status: "failed",
|
|
2016
|
-
startTime,
|
|
2017
|
-
endTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
2018
|
-
};
|
|
2019
|
-
}
|
|
2020
|
-
}
|
|
2021
|
-
/**
|
|
2022
|
-
* List currently running OpenCode subagent processes.
|
|
2023
|
-
*
|
|
2024
|
-
* Checks each tracked process via kill(pid, 0) to verify it is still alive.
|
|
2025
|
-
* Dead processes are automatically cleaned from the tracking map.
|
|
2026
|
-
*
|
|
2027
|
-
* @returns Array of spawn results for running processes
|
|
2028
|
-
*/
|
|
2029
|
-
async listRunning() {
|
|
2030
|
-
const running = [];
|
|
2031
|
-
for (const [instanceId, tracked] of this.processMap.entries()) {
|
|
2032
|
-
try {
|
|
2033
|
-
process.kill(tracked.pid, 0);
|
|
2034
|
-
running.push({
|
|
2035
|
-
instanceId,
|
|
2036
|
-
taskId: tracked.taskId,
|
|
2037
|
-
providerId: "opencode",
|
|
2038
|
-
status: "running",
|
|
2039
|
-
startTime: tracked.startTime
|
|
2040
|
-
});
|
|
2041
|
-
} catch {
|
|
2042
|
-
this.processMap.delete(instanceId);
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
return running;
|
|
2046
|
-
}
|
|
2047
|
-
/**
|
|
2048
|
-
* Terminate a running spawn by instance ID.
|
|
2049
|
-
*
|
|
2050
|
-
* Sends SIGTERM to the tracked process. If the process is not found
|
|
2051
|
-
* or has already exited, this is a no-op.
|
|
2052
|
-
*
|
|
2053
|
-
* @param instanceId - ID of the spawn instance to terminate
|
|
2054
|
-
*/
|
|
2055
|
-
async terminate(instanceId) {
|
|
2056
|
-
const tracked = this.processMap.get(instanceId);
|
|
2057
|
-
if (!tracked) return;
|
|
2058
|
-
try {
|
|
2059
|
-
process.kill(tracked.pid, "SIGTERM");
|
|
2060
|
-
} catch {
|
|
2061
|
-
}
|
|
2062
|
-
this.processMap.delete(instanceId);
|
|
2063
|
-
}
|
|
2064
|
-
};
|
|
2065
|
-
}
|
|
2066
|
-
});
|
|
2067
|
-
|
|
2068
|
-
// packages/adapters/src/providers/opencode/adapter.ts
|
|
2069
|
-
import { exec as exec7 } from "node:child_process";
|
|
2070
|
-
import { existsSync as existsSync14 } from "node:fs";
|
|
2071
|
-
import { join as join21 } from "node:path";
|
|
2072
|
-
import { promisify as promisify7 } from "node:util";
|
|
2073
|
-
var execAsync7, OpenCodeAdapter;
|
|
2074
|
-
var init_adapter3 = __esm({
|
|
2075
|
-
"packages/adapters/src/providers/opencode/adapter.ts"() {
|
|
2076
|
-
"use strict";
|
|
2077
|
-
init_hooks3();
|
|
2078
|
-
init_install3();
|
|
2079
|
-
init_spawn2();
|
|
2080
|
-
execAsync7 = promisify7(exec7);
|
|
2081
|
-
OpenCodeAdapter = class {
|
|
2082
|
-
/** Unique provider identifier. */
|
|
2083
|
-
id = "opencode";
|
|
2084
|
-
/** Human-readable provider name. */
|
|
2085
|
-
name = "OpenCode";
|
|
2086
|
-
/** Adapter version string. */
|
|
2087
|
-
version = "1.0.0";
|
|
2088
|
-
/** Declared capabilities for this provider. */
|
|
2089
|
-
capabilities = {
|
|
2090
|
-
supportsHooks: true,
|
|
2091
|
-
// 10/16 canonical events — derived from getProviderHookProfile('opencode') in CAAMP 1.9.1.
|
|
2092
|
-
// PostToolUseFailure, SubagentStart, SubagentStop, Notification, ConfigChange are
|
|
2093
|
-
// not supported by OpenCode's plugin system.
|
|
2094
|
-
supportedHookEvents: [
|
|
2095
|
-
"SessionStart",
|
|
2096
|
-
"SessionEnd",
|
|
2097
|
-
"PromptSubmit",
|
|
2098
|
-
"ResponseComplete",
|
|
2099
|
-
"PreToolUse",
|
|
2100
|
-
"PostToolUse",
|
|
2101
|
-
"PermissionRequest",
|
|
2102
|
-
"PreModel",
|
|
2103
|
-
"PreCompact",
|
|
2104
|
-
"PostCompact"
|
|
2105
|
-
],
|
|
2106
|
-
supportsSpawn: true,
|
|
2107
|
-
supportsInstall: true,
|
|
2108
|
-
supportsMcp: false,
|
|
2109
|
-
supportsInstructionFiles: true,
|
|
2110
|
-
instructionFilePattern: "AGENTS.md",
|
|
2111
|
-
supportsContextMonitor: false,
|
|
2112
|
-
supportsStatusline: false,
|
|
2113
|
-
supportsProviderPaths: true,
|
|
2114
|
-
supportsTransport: false,
|
|
2115
|
-
supportsTaskSync: false
|
|
2116
|
-
};
|
|
2117
|
-
/** Hook provider for CAAMP event mapping via OpenCode's plugin system. */
|
|
2118
|
-
hooks;
|
|
2119
|
-
/** Spawn provider for launching subagent processes via `opencode run`. */
|
|
2120
|
-
spawn;
|
|
2121
|
-
/** Install provider for managing AGENTS.md instruction files. */
|
|
2122
|
-
install;
|
|
2123
|
-
/** Project directory this adapter was initialized with, or null. */
|
|
2124
|
-
projectDir = null;
|
|
2125
|
-
/** Whether {@link initialize} has been called. */
|
|
2126
|
-
initialized = false;
|
|
2127
|
-
constructor() {
|
|
2128
|
-
this.hooks = new OpenCodeHookProvider();
|
|
2129
|
-
this.spawn = new OpenCodeSpawnProvider();
|
|
2130
|
-
this.install = new OpenCodeInstallProvider();
|
|
2131
|
-
}
|
|
2132
|
-
/**
|
|
2133
|
-
* Initialize the adapter for a given project directory.
|
|
2134
|
-
*
|
|
2135
|
-
* Validates the environment by checking for the OpenCode CLI
|
|
2136
|
-
* and OpenCode configuration directory.
|
|
2137
|
-
*
|
|
2138
|
-
* @param projectDir - Root directory of the project
|
|
2139
|
-
*/
|
|
2140
|
-
async initialize(projectDir) {
|
|
2141
|
-
this.projectDir = projectDir;
|
|
2142
|
-
this.initialized = true;
|
|
2143
|
-
}
|
|
2144
|
-
/**
|
|
2145
|
-
* Dispose the adapter and clean up resources.
|
|
2146
|
-
*
|
|
2147
|
-
* Unregisters hooks and releases any tracked state.
|
|
2148
|
-
*/
|
|
2149
|
-
async dispose() {
|
|
2150
|
-
if (this.hooks.isRegistered()) {
|
|
2151
|
-
await this.hooks.unregisterNativeHooks();
|
|
2152
|
-
}
|
|
2153
|
-
this.initialized = false;
|
|
2154
|
-
this.projectDir = null;
|
|
2155
|
-
}
|
|
2156
|
-
/**
|
|
2157
|
-
* Run a health check to verify OpenCode is accessible.
|
|
2158
|
-
*
|
|
2159
|
-
* Checks:
|
|
2160
|
-
* 1. Adapter has been initialized
|
|
2161
|
-
* 2. OpenCode CLI is available in PATH
|
|
2162
|
-
* 3. .opencode/ configuration directory exists in the project
|
|
2163
|
-
*
|
|
2164
|
-
* @returns Health status with details about each check
|
|
2165
|
-
*/
|
|
2166
|
-
async healthCheck() {
|
|
2167
|
-
const details = {};
|
|
2168
|
-
if (!this.initialized) {
|
|
2169
|
-
return {
|
|
2170
|
-
healthy: false,
|
|
2171
|
-
provider: this.id,
|
|
2172
|
-
details: { error: "Adapter not initialized" }
|
|
2173
|
-
};
|
|
2174
|
-
}
|
|
2175
|
-
let cliAvailable = false;
|
|
2176
|
-
try {
|
|
2177
|
-
const { stdout } = await execAsync7("which opencode");
|
|
2178
|
-
cliAvailable = stdout.trim().length > 0;
|
|
2179
|
-
details.cliPath = stdout.trim();
|
|
2180
|
-
} catch {
|
|
2181
|
-
details.cliAvailable = false;
|
|
2182
|
-
}
|
|
2183
|
-
if (this.projectDir) {
|
|
2184
|
-
const openCodeConfigDir = join21(this.projectDir, ".opencode");
|
|
2185
|
-
const configExists = existsSync14(openCodeConfigDir);
|
|
2186
|
-
details.configDirExists = configExists;
|
|
2187
|
-
}
|
|
2188
|
-
const versionEnvSet = process.env.OPENCODE_VERSION !== void 0;
|
|
2189
|
-
details.versionEnvSet = versionEnvSet;
|
|
2190
|
-
const healthy = cliAvailable;
|
|
2191
|
-
details.cliAvailable = cliAvailable;
|
|
2192
|
-
return {
|
|
2193
|
-
healthy,
|
|
2194
|
-
provider: this.id,
|
|
2195
|
-
details
|
|
2196
|
-
};
|
|
2197
|
-
}
|
|
2198
|
-
/**
|
|
2199
|
-
* Check whether the adapter has been initialized.
|
|
2200
|
-
*/
|
|
2201
|
-
isInitialized() {
|
|
2202
|
-
return this.initialized;
|
|
2203
|
-
}
|
|
2204
|
-
/**
|
|
2205
|
-
* Get the project directory this adapter was initialized with.
|
|
2206
|
-
*/
|
|
2207
|
-
getProjectDir() {
|
|
2208
|
-
return this.projectDir;
|
|
2209
|
-
}
|
|
2210
|
-
};
|
|
2211
|
-
}
|
|
2212
|
-
});
|
|
2213
|
-
|
|
2214
|
-
// packages/adapters/src/providers/opencode/index.ts
|
|
2215
|
-
var opencode_exports = {};
|
|
2216
|
-
__export(opencode_exports, {
|
|
2217
|
-
OpenCodeAdapter: () => OpenCodeAdapter,
|
|
2218
|
-
OpenCodeHookProvider: () => OpenCodeHookProvider,
|
|
2219
|
-
OpenCodeInstallProvider: () => OpenCodeInstallProvider,
|
|
2220
|
-
OpenCodeSpawnProvider: () => OpenCodeSpawnProvider,
|
|
2221
|
-
createAdapter: () => createAdapter6,
|
|
2222
|
-
default: () => opencode_default
|
|
2223
|
-
});
|
|
2224
|
-
function createAdapter6() {
|
|
2225
|
-
return new OpenCodeAdapter();
|
|
2226
|
-
}
|
|
2227
|
-
var opencode_default;
|
|
2228
|
-
var init_opencode = __esm({
|
|
2229
|
-
"packages/adapters/src/providers/opencode/index.ts"() {
|
|
2230
|
-
"use strict";
|
|
2231
|
-
init_adapter3();
|
|
2232
|
-
init_adapter3();
|
|
2233
|
-
init_hooks3();
|
|
2234
|
-
init_install3();
|
|
2235
|
-
init_spawn2();
|
|
2236
|
-
opencode_default = OpenCodeAdapter;
|
|
2237
|
-
}
|
|
2238
|
-
});
|
|
2239
|
-
|
|
2240
|
-
// packages/adapters/src/index.ts
|
|
2241
|
-
init_claude_code();
|
|
2242
|
-
|
|
2243
|
-
// packages/adapters/src/providers/codex/adapter.ts
|
|
2244
|
-
import { exec as exec3 } from "node:child_process";
|
|
2245
|
-
import { existsSync as existsSync6 } from "node:fs";
|
|
2246
|
-
import { homedir as homedir7 } from "node:os";
|
|
2247
|
-
import { join as join11 } from "node:path";
|
|
2248
|
-
import { promisify as promisify3 } from "node:util";
|
|
2249
|
-
|
|
2250
|
-
// packages/adapters/src/providers/codex/hooks.ts
|
|
2251
|
-
import { homedir as homedir6 } from "node:os";
|
|
2252
|
-
import { join as join9 } from "node:path";
|
|
2253
|
-
|
|
2254
|
-
// packages/adapters/src/providers/shared/transcript-reader.ts
|
|
2255
|
-
import { readdir as readdir2, readFile as readFile3 } from "node:fs/promises";
|
|
2256
|
-
import { join as join8 } from "node:path";
|
|
2257
|
-
function parseTranscriptLines(raw) {
|
|
2258
|
-
const turns = [];
|
|
2259
|
-
const lines = raw.split("\n").filter((l) => l.trim());
|
|
2260
|
-
for (const line of lines) {
|
|
2261
|
-
try {
|
|
2262
|
-
const entry = JSON.parse(line);
|
|
2263
|
-
const role = entry.role;
|
|
2264
|
-
const content = entry.content;
|
|
2265
|
-
if (typeof role === "string" && typeof content === "string") {
|
|
2266
|
-
turns.push({ role, content });
|
|
2267
|
-
}
|
|
2268
|
-
} catch {
|
|
2269
|
-
}
|
|
2270
|
-
}
|
|
2271
|
-
return turns;
|
|
2272
|
-
}
|
|
2273
|
-
async function readLatestTranscript(providerDir) {
|
|
2274
|
-
let allFiles = [];
|
|
2275
|
-
try {
|
|
2276
|
-
const entries = await readdir2(providerDir, { withFileTypes: true });
|
|
2277
|
-
for (const entry of entries) {
|
|
2278
|
-
if (!entry.isFile()) continue;
|
|
2279
|
-
const name = entry.name;
|
|
2280
|
-
if (name.endsWith(".json") || name.endsWith(".jsonl")) {
|
|
2281
|
-
allFiles.push(join8(providerDir, name));
|
|
2282
|
-
}
|
|
2283
|
-
}
|
|
2284
|
-
} catch {
|
|
2285
|
-
return null;
|
|
2286
|
-
}
|
|
2287
|
-
if (allFiles.length === 0) return null;
|
|
2288
|
-
allFiles = allFiles.sort((a, b) => b.localeCompare(a));
|
|
2289
|
-
const mostRecent = allFiles[0];
|
|
2290
|
-
if (!mostRecent) return null;
|
|
2291
|
-
try {
|
|
2292
|
-
const raw = await readFile3(mostRecent, "utf-8");
|
|
2293
|
-
const turns = parseTranscriptLines(raw);
|
|
2294
|
-
return turns.length > 0 ? turns.map((t) => `${t.role}: ${t.content}`).join("\n") : null;
|
|
2295
|
-
} catch {
|
|
2296
|
-
return null;
|
|
2297
|
-
}
|
|
2298
|
-
}
|
|
2299
|
-
|
|
2300
|
-
// packages/adapters/src/providers/codex/hooks.ts
|
|
2301
|
-
var CODEX_EVENT_MAP = {
|
|
2302
|
-
SessionStart: "SessionStart",
|
|
2303
|
-
PromptSubmit: "UserPromptSubmit",
|
|
2304
|
-
ResponseComplete: "Stop"
|
|
2305
|
-
};
|
|
2306
|
-
var CodexHookProvider = class {
|
|
2307
|
-
/** Whether hooks have been registered for the current session. */
|
|
2308
|
-
registered = false;
|
|
2309
|
-
/**
|
|
2310
|
-
* Map a Codex CLI native event name to a CAAMP hook event name.
|
|
2311
|
-
*
|
|
2312
|
-
* @param providerEvent - Codex CLI event name (e.g. "SessionStart", "PromptSubmit")
|
|
2313
|
-
* @returns CAAMP event name or null if unmapped
|
|
2314
|
-
* @task T162
|
|
2315
|
-
*/
|
|
2316
|
-
mapProviderEvent(providerEvent) {
|
|
2317
|
-
return CODEX_EVENT_MAP[providerEvent] ?? null;
|
|
2318
|
-
}
|
|
2319
|
-
/**
|
|
2320
|
-
* Register native hooks for a project.
|
|
2321
|
-
*
|
|
2322
|
-
* For Codex CLI, hooks are registered via the config system
|
|
2323
|
-
* (~/.codex/), which is handled by the install provider.
|
|
2324
|
-
* This method marks hooks as registered without performing
|
|
2325
|
-
* filesystem operations.
|
|
2326
|
-
*
|
|
2327
|
-
* @param _projectDir - Project directory (unused; hooks are global)
|
|
2328
|
-
* @task T162
|
|
2329
|
-
*/
|
|
2330
|
-
async registerNativeHooks(_projectDir) {
|
|
2331
|
-
this.registered = true;
|
|
2332
|
-
}
|
|
2333
|
-
/**
|
|
2334
|
-
* Unregister native hooks.
|
|
2335
|
-
*
|
|
2336
|
-
* For Codex CLI, this is a no-op since hooks are managed through
|
|
2337
|
-
* the config system. Unregistration happens via the install
|
|
2338
|
-
* provider's uninstall method.
|
|
2339
|
-
* @task T162
|
|
2340
|
-
*/
|
|
2341
|
-
async unregisterNativeHooks() {
|
|
2342
|
-
this.registered = false;
|
|
2343
|
-
}
|
|
2344
|
-
/**
|
|
2345
|
-
* Check whether hooks have been registered via registerNativeHooks.
|
|
2346
|
-
* @task T162
|
|
2347
|
-
*/
|
|
2348
|
-
isRegistered() {
|
|
2349
|
-
return this.registered;
|
|
2350
|
-
}
|
|
2351
|
-
/**
|
|
2352
|
-
* Get the full event mapping for introspection/debugging.
|
|
2353
|
-
* @task T162
|
|
2354
|
-
*/
|
|
2355
|
-
getEventMap() {
|
|
2356
|
-
return { ...CODEX_EVENT_MAP };
|
|
2357
|
-
}
|
|
2358
|
-
/**
|
|
2359
|
-
* Extract a plain-text transcript from Codex CLI session data.
|
|
2360
|
-
*
|
|
2361
|
-
* Reads the most recent JSON/JSONL session file under `~/.codex/`
|
|
2362
|
-
* and returns its turns as a flat string for brain observation extraction.
|
|
2363
|
-
*
|
|
2364
|
-
* Returns null when no session data is found or on any read error.
|
|
2365
|
-
*
|
|
2366
|
-
* @param _sessionId - CLEO session ID (unused; reads the most recent file)
|
|
2367
|
-
* @param _projectDir - Project directory (unused; Codex CLI uses global paths)
|
|
2368
|
-
* @task T162 @epic T134
|
|
2369
|
-
*/
|
|
2370
|
-
async getTranscript(_sessionId, _projectDir) {
|
|
2371
|
-
return readLatestTranscript(join9(homedir6(), ".codex"));
|
|
2372
|
-
}
|
|
2373
|
-
};
|
|
2374
|
-
|
|
2375
|
-
// packages/adapters/src/providers/codex/install.ts
|
|
2376
|
-
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2377
|
-
import { join as join10 } from "node:path";
|
|
2378
|
-
var INSTRUCTION_REFERENCES2 = ["@~/.cleo/templates/CLEO-INJECTION.md", "@.cleo/memory-bridge.md"];
|
|
2379
|
-
var CodexInstallProvider = class {
|
|
2380
|
-
/**
|
|
2381
|
-
* Install CLEO into a Codex CLI environment.
|
|
2382
|
-
*
|
|
2383
|
-
* @param options - Installation options including project directory
|
|
2384
|
-
* @returns Result describing what was installed
|
|
2385
|
-
* @task T162
|
|
2386
|
-
*/
|
|
2387
|
-
async install(options) {
|
|
2388
|
-
const { projectDir } = options;
|
|
2389
|
-
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2390
|
-
let instructionFileUpdated = false;
|
|
2391
|
-
const details = {};
|
|
2392
|
-
instructionFileUpdated = this.updateInstructionFile(projectDir);
|
|
2393
|
-
if (instructionFileUpdated) {
|
|
2394
|
-
details.instructionFile = join10(projectDir, "AGENTS.md");
|
|
2395
|
-
}
|
|
2396
|
-
return {
|
|
2397
|
-
success: true,
|
|
2398
|
-
installedAt,
|
|
2399
|
-
instructionFileUpdated,
|
|
2400
|
-
mcpRegistered: false,
|
|
2401
|
-
details
|
|
2402
|
-
};
|
|
2403
|
-
}
|
|
2404
|
-
/**
|
|
2405
|
-
* Uninstall CLEO from the Codex CLI environment.
|
|
2406
|
-
*
|
|
2407
|
-
* Does not remove AGENTS.md references (they are harmless if CLEO is not present).
|
|
2408
|
-
* @task T162
|
|
2409
|
-
*/
|
|
2410
|
-
async uninstall() {
|
|
2411
|
-
}
|
|
2412
|
-
/**
|
|
2413
|
-
* Check whether CLEO is installed in the Codex CLI environment.
|
|
2414
|
-
*
|
|
2415
|
-
* Checks for CLEO references in AGENTS.md.
|
|
2416
|
-
* @task T162
|
|
2417
|
-
*/
|
|
2418
|
-
async isInstalled() {
|
|
2419
|
-
const agentsMdPath = join10(process.cwd(), "AGENTS.md");
|
|
2420
|
-
if (existsSync5(agentsMdPath)) {
|
|
2421
|
-
try {
|
|
2422
|
-
const content = readFileSync4(agentsMdPath, "utf-8");
|
|
2423
|
-
if (INSTRUCTION_REFERENCES2.some((ref) => content.includes(ref))) {
|
|
2424
|
-
return true;
|
|
2425
|
-
}
|
|
2426
|
-
} catch {
|
|
2427
|
-
}
|
|
2428
|
-
}
|
|
2429
|
-
return false;
|
|
2430
|
-
}
|
|
2431
|
-
/**
|
|
2432
|
-
* Ensure AGENTS.md contains @-references to CLEO instruction files.
|
|
2433
|
-
*
|
|
2434
|
-
* Creates AGENTS.md if it does not exist. Appends any missing references.
|
|
2435
|
-
*
|
|
2436
|
-
* @param projectDir - Project root directory
|
|
2437
|
-
* @task T162
|
|
2438
|
-
*/
|
|
2439
|
-
async ensureInstructionReferences(projectDir) {
|
|
2440
|
-
this.updateInstructionFile(projectDir);
|
|
2441
|
-
}
|
|
2442
|
-
/**
|
|
2443
|
-
* Update AGENTS.md with CLEO @-references.
|
|
2444
|
-
*
|
|
2445
|
-
* @param projectDir - Project root directory
|
|
2446
|
-
* @returns true if the file was created or modified
|
|
2447
|
-
*/
|
|
2448
|
-
updateInstructionFile(projectDir) {
|
|
2449
|
-
const agentsMdPath = join10(projectDir, "AGENTS.md");
|
|
2450
|
-
let content = "";
|
|
2451
|
-
let existed = false;
|
|
2452
|
-
if (existsSync5(agentsMdPath)) {
|
|
2453
|
-
content = readFileSync4(agentsMdPath, "utf-8");
|
|
2454
|
-
existed = true;
|
|
2455
|
-
}
|
|
2456
|
-
const missingRefs = INSTRUCTION_REFERENCES2.filter((ref) => !content.includes(ref));
|
|
2457
|
-
if (missingRefs.length === 0) {
|
|
2458
|
-
return false;
|
|
2459
|
-
}
|
|
2460
|
-
const refsBlock = missingRefs.join("\n");
|
|
2461
|
-
if (existed) {
|
|
2462
|
-
const separator = content.endsWith("\n") ? "" : "\n";
|
|
2463
|
-
content = content + separator + refsBlock + "\n";
|
|
2464
|
-
} else {
|
|
2465
|
-
content = refsBlock + "\n";
|
|
2466
|
-
}
|
|
2467
|
-
writeFileSync3(agentsMdPath, content, "utf-8");
|
|
2468
|
-
return true;
|
|
2469
|
-
}
|
|
2470
|
-
};
|
|
2471
|
-
|
|
2472
|
-
// packages/adapters/src/providers/codex/adapter.ts
|
|
2473
|
-
var execAsync3 = promisify3(exec3);
|
|
2474
|
-
var CodexAdapter = class {
|
|
2475
|
-
/** Unique provider identifier. */
|
|
2476
|
-
id = "codex";
|
|
2477
|
-
/** Human-readable provider name. */
|
|
2478
|
-
name = "Codex";
|
|
2479
|
-
/** Adapter version string. */
|
|
2480
|
-
version = "1.0.0";
|
|
2481
|
-
/** Declared capabilities for this provider. */
|
|
2482
|
-
capabilities = {
|
|
2483
|
-
supportsHooks: true,
|
|
2484
|
-
supportedHookEvents: ["SessionStart", "UserPromptSubmit", "Stop"],
|
|
2485
|
-
supportsSpawn: false,
|
|
2486
|
-
supportsInstall: true,
|
|
2487
|
-
supportsMcp: false,
|
|
2488
|
-
supportsInstructionFiles: false,
|
|
2489
|
-
supportsContextMonitor: false,
|
|
2490
|
-
supportsStatusline: false,
|
|
2491
|
-
supportsProviderPaths: false,
|
|
2492
|
-
supportsTransport: false,
|
|
2493
|
-
supportsTaskSync: false
|
|
2494
|
-
};
|
|
2495
|
-
/** Hook provider for CAAMP event mapping. */
|
|
2496
|
-
hooks;
|
|
2497
|
-
/** Install provider for managing instruction files. */
|
|
2498
|
-
install;
|
|
2499
|
-
/** Project directory this adapter was initialized with, or null. */
|
|
2500
|
-
projectDir = null;
|
|
2501
|
-
/** Whether {@link initialize} has been called. */
|
|
2502
|
-
initialized = false;
|
|
2503
|
-
constructor() {
|
|
2504
|
-
this.hooks = new CodexHookProvider();
|
|
2505
|
-
this.install = new CodexInstallProvider();
|
|
2506
|
-
}
|
|
2507
|
-
/**
|
|
2508
|
-
* Initialize the adapter for a given project directory.
|
|
2509
|
-
*
|
|
2510
|
-
* @param projectDir - Root directory of the project
|
|
2511
|
-
* @task T162
|
|
2512
|
-
*/
|
|
2513
|
-
async initialize(projectDir) {
|
|
2514
|
-
this.projectDir = projectDir;
|
|
2515
|
-
this.initialized = true;
|
|
2516
|
-
}
|
|
2517
|
-
/**
|
|
2518
|
-
* Dispose the adapter and clean up resources.
|
|
2519
|
-
*
|
|
2520
|
-
* Unregisters hooks and releases any tracked state.
|
|
2521
|
-
* @task T162
|
|
2522
|
-
*/
|
|
2523
|
-
async dispose() {
|
|
2524
|
-
if (this.hooks.isRegistered()) {
|
|
2525
|
-
await this.hooks.unregisterNativeHooks();
|
|
2526
|
-
}
|
|
2527
|
-
this.initialized = false;
|
|
2528
|
-
this.projectDir = null;
|
|
2529
|
-
}
|
|
2530
|
-
/**
|
|
2531
|
-
* Run a health check to verify Codex CLI is accessible.
|
|
2532
|
-
*
|
|
2533
|
-
* Checks:
|
|
2534
|
-
* 1. Adapter has been initialized
|
|
2535
|
-
* 2. Codex CLI binary is available in PATH
|
|
2536
|
-
* 3. ~/.codex/ configuration directory exists
|
|
2537
|
-
*
|
|
2538
|
-
* @returns Health status with details about each check
|
|
2539
|
-
* @task T162
|
|
2540
|
-
*/
|
|
2541
|
-
async healthCheck() {
|
|
2542
|
-
const details = {};
|
|
2543
|
-
if (!this.initialized) {
|
|
2544
|
-
return {
|
|
2545
|
-
healthy: false,
|
|
2546
|
-
provider: this.id,
|
|
2547
|
-
details: { error: "Adapter not initialized" }
|
|
2548
|
-
};
|
|
2549
|
-
}
|
|
2550
|
-
let cliAvailable = false;
|
|
2551
|
-
try {
|
|
2552
|
-
const { stdout } = await execAsync3("which codex");
|
|
2553
|
-
cliAvailable = stdout.trim().length > 0;
|
|
2554
|
-
details.cliPath = stdout.trim();
|
|
2555
|
-
} catch {
|
|
2556
|
-
details.cliAvailable = false;
|
|
2557
|
-
}
|
|
2558
|
-
const codexConfigDir = join11(homedir7(), ".codex");
|
|
2559
|
-
const configExists = existsSync6(codexConfigDir);
|
|
2560
|
-
details.configDirExists = configExists;
|
|
2561
|
-
const healthy = cliAvailable;
|
|
2562
|
-
details.cliAvailable = cliAvailable;
|
|
2563
|
-
return {
|
|
2564
|
-
healthy,
|
|
2565
|
-
provider: this.id,
|
|
2566
|
-
details
|
|
2567
|
-
};
|
|
2568
|
-
}
|
|
2569
|
-
/**
|
|
2570
|
-
* Check whether the adapter has been initialized.
|
|
2571
|
-
* @task T162
|
|
2572
|
-
*/
|
|
2573
|
-
isInitialized() {
|
|
2574
|
-
return this.initialized;
|
|
2575
|
-
}
|
|
2576
|
-
/**
|
|
2577
|
-
* Get the project directory this adapter was initialized with.
|
|
2578
|
-
* @task T162
|
|
2579
|
-
*/
|
|
2580
|
-
getProjectDir() {
|
|
2581
|
-
return this.projectDir;
|
|
2582
|
-
}
|
|
2583
|
-
};
|
|
2584
|
-
|
|
2585
|
-
// packages/adapters/src/providers/codex/index.ts
|
|
2586
|
-
function createAdapter2() {
|
|
2587
|
-
return new CodexAdapter();
|
|
2588
|
-
}
|
|
2589
|
-
|
|
2590
|
-
// packages/adapters/src/index.ts
|
|
2591
|
-
init_cursor();
|
|
2592
|
-
|
|
2593
|
-
// packages/adapters/src/providers/gemini-cli/adapter.ts
|
|
2594
|
-
import { exec as exec4 } from "node:child_process";
|
|
2595
|
-
import { existsSync as existsSync10 } from "node:fs";
|
|
2596
|
-
import { homedir as homedir9 } from "node:os";
|
|
2597
|
-
import { join as join16 } from "node:path";
|
|
2598
|
-
import { promisify as promisify4 } from "node:util";
|
|
2599
|
-
|
|
2600
|
-
// packages/adapters/src/providers/gemini-cli/hooks.ts
|
|
2601
|
-
import { homedir as homedir8 } from "node:os";
|
|
2602
|
-
import { join as join14 } from "node:path";
|
|
2603
|
-
var GEMINI_CLI_EVENT_MAP = {
|
|
2604
|
-
SessionStart: "SessionStart",
|
|
2605
|
-
SessionEnd: "SessionEnd",
|
|
2606
|
-
PromptSubmit: "BeforeAgent",
|
|
2607
|
-
ResponseComplete: "AfterAgent",
|
|
2608
|
-
PreToolUse: "BeforeTool",
|
|
2609
|
-
PostToolUse: "AfterTool",
|
|
2610
|
-
PreModel: "BeforeModel",
|
|
2611
|
-
PostModel: "AfterModel",
|
|
2612
|
-
PreCompact: "PreCompress",
|
|
2613
|
-
Notification: "Notification"
|
|
2614
|
-
};
|
|
2615
|
-
var GeminiCliHookProvider = class {
|
|
2616
|
-
/** Whether hooks have been registered for the current session. */
|
|
2617
|
-
registered = false;
|
|
2618
|
-
/**
|
|
2619
|
-
* Map a Gemini CLI native event name to a CAAMP hook event name.
|
|
2620
|
-
*
|
|
2621
|
-
* @param providerEvent - Gemini CLI event name (e.g. "SessionStart", "PreToolUse")
|
|
2622
|
-
* @returns CAAMP event name or null if unmapped
|
|
2623
|
-
* @task T161
|
|
2624
|
-
*/
|
|
2625
|
-
mapProviderEvent(providerEvent) {
|
|
2626
|
-
return GEMINI_CLI_EVENT_MAP[providerEvent] ?? null;
|
|
2627
|
-
}
|
|
2628
|
-
/**
|
|
2629
|
-
* Register native hooks for a project.
|
|
2630
|
-
*
|
|
2631
|
-
* For Gemini CLI, hooks are registered via the config system
|
|
2632
|
-
* (~/.gemini/), which is handled by the install provider.
|
|
2633
|
-
* This method marks hooks as registered without performing
|
|
2634
|
-
* filesystem operations.
|
|
2635
|
-
*
|
|
2636
|
-
* @param _projectDir - Project directory (unused; hooks are global)
|
|
2637
|
-
* @task T161
|
|
2638
|
-
*/
|
|
2639
|
-
async registerNativeHooks(_projectDir) {
|
|
2640
|
-
this.registered = true;
|
|
2641
|
-
}
|
|
2642
|
-
/**
|
|
2643
|
-
* Unregister native hooks.
|
|
2644
|
-
*
|
|
2645
|
-
* For Gemini CLI, this is a no-op since hooks are managed through
|
|
2646
|
-
* the config system. Unregistration happens via the install
|
|
2647
|
-
* provider's uninstall method.
|
|
2648
|
-
* @task T161
|
|
2649
|
-
*/
|
|
2650
|
-
async unregisterNativeHooks() {
|
|
2651
|
-
this.registered = false;
|
|
2652
|
-
}
|
|
2653
|
-
/**
|
|
2654
|
-
* Check whether hooks have been registered via registerNativeHooks.
|
|
2655
|
-
* @task T161
|
|
2656
|
-
*/
|
|
2657
|
-
isRegistered() {
|
|
2658
|
-
return this.registered;
|
|
2659
|
-
}
|
|
2660
|
-
/**
|
|
2661
|
-
* Get the full event mapping for introspection/debugging.
|
|
2662
|
-
* @task T161
|
|
2663
|
-
*/
|
|
2664
|
-
getEventMap() {
|
|
2665
|
-
return { ...GEMINI_CLI_EVENT_MAP };
|
|
2666
|
-
}
|
|
2667
|
-
/**
|
|
2668
|
-
* Extract a plain-text transcript from Gemini CLI session data.
|
|
2669
|
-
*
|
|
2670
|
-
* Reads the most recent JSON/JSONL session file under `~/.gemini/`
|
|
2671
|
-
* and returns its turns as a flat string for brain observation extraction.
|
|
2672
|
-
*
|
|
2673
|
-
* Returns null when no session data is found or on any read error.
|
|
2674
|
-
*
|
|
2675
|
-
* @param _sessionId - CLEO session ID (unused; reads the most recent file)
|
|
2676
|
-
* @param _projectDir - Project directory (unused; Gemini CLI uses global paths)
|
|
2677
|
-
* @task T161 @epic T134
|
|
2678
|
-
*/
|
|
2679
|
-
async getTranscript(_sessionId, _projectDir) {
|
|
2680
|
-
return readLatestTranscript(join14(homedir8(), ".gemini"));
|
|
2681
|
-
}
|
|
2682
|
-
};
|
|
2683
|
-
|
|
2684
|
-
// packages/adapters/src/providers/gemini-cli/install.ts
|
|
2685
|
-
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2686
|
-
import { join as join15 } from "node:path";
|
|
2687
|
-
var INSTRUCTION_REFERENCES4 = ["@~/.cleo/templates/CLEO-INJECTION.md", "@.cleo/memory-bridge.md"];
|
|
2688
|
-
var GeminiCliInstallProvider = class {
|
|
2689
|
-
/**
|
|
2690
|
-
* Install CLEO into a Gemini CLI environment.
|
|
2691
|
-
*
|
|
2692
|
-
* @param options - Installation options including project directory
|
|
2693
|
-
* @returns Result describing what was installed
|
|
2694
|
-
* @task T161
|
|
2695
|
-
*/
|
|
2696
|
-
async install(options) {
|
|
2697
|
-
const { projectDir } = options;
|
|
2698
|
-
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2699
|
-
let instructionFileUpdated = false;
|
|
2700
|
-
const details = {};
|
|
2701
|
-
instructionFileUpdated = this.updateInstructionFile(projectDir);
|
|
2702
|
-
if (instructionFileUpdated) {
|
|
2703
|
-
details.instructionFile = join15(projectDir, "AGENTS.md");
|
|
2704
|
-
}
|
|
2705
|
-
return {
|
|
2706
|
-
success: true,
|
|
2707
|
-
installedAt,
|
|
2708
|
-
instructionFileUpdated,
|
|
2709
|
-
mcpRegistered: false,
|
|
2710
|
-
details
|
|
2711
|
-
};
|
|
2712
|
-
}
|
|
2713
|
-
/**
|
|
2714
|
-
* Uninstall CLEO from the Gemini CLI environment.
|
|
2715
|
-
*
|
|
2716
|
-
* Does not remove AGENTS.md references (they are harmless if CLEO is not present).
|
|
2717
|
-
* @task T161
|
|
2718
|
-
*/
|
|
2719
|
-
async uninstall() {
|
|
2720
|
-
}
|
|
2721
|
-
/**
|
|
2722
|
-
* Check whether CLEO is installed in the Gemini CLI environment.
|
|
2723
|
-
*
|
|
2724
|
-
* Checks for CLEO references in AGENTS.md.
|
|
2725
|
-
* @task T161
|
|
2726
|
-
*/
|
|
2727
|
-
async isInstalled() {
|
|
2728
|
-
const agentsMdPath = join15(process.cwd(), "AGENTS.md");
|
|
2729
|
-
if (existsSync9(agentsMdPath)) {
|
|
2730
|
-
try {
|
|
2731
|
-
const content = readFileSync6(agentsMdPath, "utf-8");
|
|
2732
|
-
if (INSTRUCTION_REFERENCES4.some((ref) => content.includes(ref))) {
|
|
2733
|
-
return true;
|
|
2734
|
-
}
|
|
2735
|
-
} catch {
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
return false;
|
|
2739
|
-
}
|
|
2740
|
-
/**
|
|
2741
|
-
* Ensure AGENTS.md contains @-references to CLEO instruction files.
|
|
2742
|
-
*
|
|
2743
|
-
* Creates AGENTS.md if it does not exist. Appends any missing references.
|
|
2744
|
-
*
|
|
2745
|
-
* @param projectDir - Project root directory
|
|
2746
|
-
* @task T161
|
|
2747
|
-
*/
|
|
2748
|
-
async ensureInstructionReferences(projectDir) {
|
|
2749
|
-
this.updateInstructionFile(projectDir);
|
|
2750
|
-
}
|
|
2751
|
-
/**
|
|
2752
|
-
* Update AGENTS.md with CLEO @-references.
|
|
2753
|
-
*
|
|
2754
|
-
* @param projectDir - Project root directory
|
|
2755
|
-
* @returns true if the file was created or modified
|
|
2756
|
-
*/
|
|
2757
|
-
updateInstructionFile(projectDir) {
|
|
2758
|
-
const agentsMdPath = join15(projectDir, "AGENTS.md");
|
|
2759
|
-
let content = "";
|
|
2760
|
-
let existed = false;
|
|
2761
|
-
if (existsSync9(agentsMdPath)) {
|
|
2762
|
-
content = readFileSync6(agentsMdPath, "utf-8");
|
|
2763
|
-
existed = true;
|
|
2764
|
-
}
|
|
2765
|
-
const missingRefs = INSTRUCTION_REFERENCES4.filter((ref) => !content.includes(ref));
|
|
2766
|
-
if (missingRefs.length === 0) {
|
|
2767
|
-
return false;
|
|
2768
|
-
}
|
|
2769
|
-
const refsBlock = missingRefs.join("\n");
|
|
2770
|
-
if (existed) {
|
|
2771
|
-
const separator = content.endsWith("\n") ? "" : "\n";
|
|
2772
|
-
content = content + separator + refsBlock + "\n";
|
|
2773
|
-
} else {
|
|
2774
|
-
content = refsBlock + "\n";
|
|
2775
|
-
}
|
|
2776
|
-
writeFileSync5(agentsMdPath, content, "utf-8");
|
|
2777
|
-
return true;
|
|
2778
|
-
}
|
|
2779
|
-
};
|
|
2780
|
-
|
|
2781
|
-
// packages/adapters/src/providers/gemini-cli/adapter.ts
|
|
2782
|
-
var execAsync4 = promisify4(exec4);
|
|
2783
|
-
var GeminiCliAdapter = class {
|
|
2784
|
-
/** Unique provider identifier. */
|
|
2785
|
-
id = "gemini-cli";
|
|
2786
|
-
/** Human-readable provider name. */
|
|
2787
|
-
name = "Gemini CLI";
|
|
2788
|
-
/** Adapter version string. */
|
|
2789
|
-
version = "1.0.0";
|
|
2790
|
-
/** Declared capabilities for this provider. */
|
|
2791
|
-
capabilities = {
|
|
2792
|
-
supportsHooks: true,
|
|
2793
|
-
supportedHookEvents: [
|
|
2794
|
-
"SessionStart",
|
|
2795
|
-
"SessionEnd",
|
|
2796
|
-
"BeforeAgent",
|
|
2797
|
-
"AfterAgent",
|
|
2798
|
-
"BeforeTool",
|
|
2799
|
-
"AfterTool",
|
|
2800
|
-
"BeforeModel",
|
|
2801
|
-
"AfterModel",
|
|
2802
|
-
"PreCompress",
|
|
2803
|
-
"Notification"
|
|
2804
|
-
],
|
|
2805
|
-
supportsSpawn: false,
|
|
2806
|
-
supportsInstall: true,
|
|
2807
|
-
supportsMcp: false,
|
|
2808
|
-
supportsInstructionFiles: false,
|
|
2809
|
-
supportsContextMonitor: false,
|
|
2810
|
-
supportsStatusline: false,
|
|
2811
|
-
supportsProviderPaths: false,
|
|
2812
|
-
supportsTransport: false,
|
|
2813
|
-
supportsTaskSync: false
|
|
2814
|
-
};
|
|
2815
|
-
/** Hook provider for CAAMP event mapping. */
|
|
2816
|
-
hooks;
|
|
2817
|
-
/** Install provider for managing instruction files. */
|
|
2818
|
-
install;
|
|
2819
|
-
/** Project directory this adapter was initialized with, or null. */
|
|
2820
|
-
projectDir = null;
|
|
2821
|
-
/** Whether {@link initialize} has been called. */
|
|
2822
|
-
initialized = false;
|
|
2823
|
-
constructor() {
|
|
2824
|
-
this.hooks = new GeminiCliHookProvider();
|
|
2825
|
-
this.install = new GeminiCliInstallProvider();
|
|
2826
|
-
}
|
|
2827
|
-
/**
|
|
2828
|
-
* Initialize the adapter for a given project directory.
|
|
2829
|
-
*
|
|
2830
|
-
* @param projectDir - Root directory of the project
|
|
2831
|
-
* @task T161
|
|
2832
|
-
*/
|
|
2833
|
-
async initialize(projectDir) {
|
|
2834
|
-
this.projectDir = projectDir;
|
|
2835
|
-
this.initialized = true;
|
|
2836
|
-
}
|
|
2837
|
-
/**
|
|
2838
|
-
* Dispose the adapter and clean up resources.
|
|
2839
|
-
*
|
|
2840
|
-
* Unregisters hooks and releases any tracked state.
|
|
2841
|
-
* @task T161
|
|
2842
|
-
*/
|
|
2843
|
-
async dispose() {
|
|
2844
|
-
if (this.hooks.isRegistered()) {
|
|
2845
|
-
await this.hooks.unregisterNativeHooks();
|
|
2846
|
-
}
|
|
2847
|
-
this.initialized = false;
|
|
2848
|
-
this.projectDir = null;
|
|
2849
|
-
}
|
|
2850
|
-
/**
|
|
2851
|
-
* Run a health check to verify Gemini CLI is accessible.
|
|
2852
|
-
*
|
|
2853
|
-
* Checks:
|
|
2854
|
-
* 1. Adapter has been initialized
|
|
2855
|
-
* 2. Gemini CLI binary is available in PATH
|
|
2856
|
-
* 3. ~/.gemini/ configuration directory exists
|
|
2857
|
-
*
|
|
2858
|
-
* @returns Health status with details about each check
|
|
2859
|
-
* @task T161
|
|
2860
|
-
*/
|
|
2861
|
-
async healthCheck() {
|
|
2862
|
-
const details = {};
|
|
2863
|
-
if (!this.initialized) {
|
|
2864
|
-
return {
|
|
2865
|
-
healthy: false,
|
|
2866
|
-
provider: this.id,
|
|
2867
|
-
details: { error: "Adapter not initialized" }
|
|
2868
|
-
};
|
|
2869
|
-
}
|
|
2870
|
-
let cliAvailable = false;
|
|
2871
|
-
try {
|
|
2872
|
-
const { stdout } = await execAsync4("which gemini");
|
|
2873
|
-
cliAvailable = stdout.trim().length > 0;
|
|
2874
|
-
details.cliPath = stdout.trim();
|
|
2875
|
-
} catch {
|
|
2876
|
-
details.cliAvailable = false;
|
|
2877
|
-
}
|
|
2878
|
-
const geminiConfigDir = join16(homedir9(), ".gemini");
|
|
2879
|
-
const configExists = existsSync10(geminiConfigDir);
|
|
2880
|
-
details.configDirExists = configExists;
|
|
2881
|
-
const healthy = cliAvailable;
|
|
2882
|
-
details.cliAvailable = cliAvailable;
|
|
2883
|
-
return {
|
|
2884
|
-
healthy,
|
|
2885
|
-
provider: this.id,
|
|
2886
|
-
details
|
|
2887
|
-
};
|
|
2888
|
-
}
|
|
2889
|
-
/**
|
|
2890
|
-
* Check whether the adapter has been initialized.
|
|
2891
|
-
* @task T161
|
|
2892
|
-
*/
|
|
2893
|
-
isInitialized() {
|
|
2894
|
-
return this.initialized;
|
|
2895
|
-
}
|
|
2896
|
-
/**
|
|
2897
|
-
* Get the project directory this adapter was initialized with.
|
|
2898
|
-
* @task T161
|
|
2899
|
-
*/
|
|
2900
|
-
getProjectDir() {
|
|
2901
|
-
return this.projectDir;
|
|
2902
|
-
}
|
|
2903
|
-
};
|
|
2904
|
-
|
|
2905
|
-
// packages/adapters/src/providers/gemini-cli/index.ts
|
|
2906
|
-
function createAdapter4() {
|
|
2907
|
-
return new GeminiCliAdapter();
|
|
2908
|
-
}
|
|
2909
|
-
|
|
2910
|
-
// packages/adapters/src/providers/kimi/adapter.ts
|
|
2911
|
-
import { exec as exec5 } from "node:child_process";
|
|
2912
|
-
import { existsSync as existsSync12 } from "node:fs";
|
|
2913
|
-
import { homedir as homedir10 } from "node:os";
|
|
2914
|
-
import { join as join18 } from "node:path";
|
|
2915
|
-
import { promisify as promisify5 } from "node:util";
|
|
2916
|
-
|
|
2917
|
-
// packages/adapters/src/providers/kimi/hooks.ts
|
|
2918
|
-
var KimiHookProvider = class {
|
|
2919
|
-
/** Whether hooks have been registered (always a no-op for Kimi). */
|
|
2920
|
-
registered = false;
|
|
2921
|
-
/**
|
|
2922
|
-
* Map a Kimi native event name to a CAAMP hook event name.
|
|
2923
|
-
*
|
|
2924
|
-
* Kimi has no hook system, so this always returns null.
|
|
2925
|
-
*
|
|
2926
|
-
* @param _providerEvent - Unused; Kimi emits no hookable events
|
|
2927
|
-
* @returns Always null
|
|
2928
|
-
* @task T163
|
|
2929
|
-
*/
|
|
2930
|
-
mapProviderEvent(_providerEvent) {
|
|
2931
|
-
return null;
|
|
2932
|
-
}
|
|
2933
|
-
/**
|
|
2934
|
-
* Register native hooks for a project.
|
|
2935
|
-
*
|
|
2936
|
-
* Kimi has no hook system. This method is a no-op and only
|
|
2937
|
-
* tracks registration state for interface compliance.
|
|
2938
|
-
*
|
|
2939
|
-
* @param _projectDir - Project directory (unused)
|
|
2940
|
-
* @task T163
|
|
2941
|
-
*/
|
|
2942
|
-
async registerNativeHooks(_projectDir) {
|
|
2943
|
-
this.registered = true;
|
|
2944
|
-
}
|
|
2945
|
-
/**
|
|
2946
|
-
* Unregister native hooks.
|
|
2947
|
-
*
|
|
2948
|
-
* Kimi has no hook system. This method is a no-op.
|
|
2949
|
-
* @task T163
|
|
2950
|
-
*/
|
|
2951
|
-
async unregisterNativeHooks() {
|
|
2952
|
-
this.registered = false;
|
|
2953
|
-
}
|
|
2954
|
-
/**
|
|
2955
|
-
* Check whether hooks have been registered via registerNativeHooks.
|
|
2956
|
-
* @task T163
|
|
2957
|
-
*/
|
|
2958
|
-
isRegistered() {
|
|
2959
|
-
return this.registered;
|
|
2960
|
-
}
|
|
2961
|
-
/**
|
|
2962
|
-
* Get the full event mapping for introspection/debugging.
|
|
2963
|
-
*
|
|
2964
|
-
* Returns an empty map since Kimi has no hookable events.
|
|
2965
|
-
* @task T163
|
|
2966
|
-
*/
|
|
2967
|
-
getEventMap() {
|
|
2968
|
-
return {};
|
|
2969
|
-
}
|
|
2970
|
-
};
|
|
2971
|
-
|
|
2972
|
-
// packages/adapters/src/providers/kimi/install.ts
|
|
2973
|
-
import { existsSync as existsSync11, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
2974
|
-
import { join as join17 } from "node:path";
|
|
2975
|
-
var INSTRUCTION_REFERENCES5 = ["@~/.cleo/templates/CLEO-INJECTION.md", "@.cleo/memory-bridge.md"];
|
|
2976
|
-
var KimiInstallProvider = class {
|
|
2977
|
-
/**
|
|
2978
|
-
* Install CLEO into a Kimi environment.
|
|
2979
|
-
*
|
|
2980
|
-
* @param options - Installation options including project directory
|
|
2981
|
-
* @returns Result describing what was installed
|
|
2982
|
-
* @task T163
|
|
2983
|
-
*/
|
|
2984
|
-
async install(options) {
|
|
2985
|
-
const { projectDir } = options;
|
|
2986
|
-
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2987
|
-
let instructionFileUpdated = false;
|
|
2988
|
-
const details = {};
|
|
2989
|
-
instructionFileUpdated = this.updateInstructionFile(projectDir);
|
|
2990
|
-
if (instructionFileUpdated) {
|
|
2991
|
-
details.instructionFile = join17(projectDir, "AGENTS.md");
|
|
2992
|
-
}
|
|
2993
|
-
return {
|
|
2994
|
-
success: true,
|
|
2995
|
-
installedAt,
|
|
2996
|
-
instructionFileUpdated,
|
|
2997
|
-
mcpRegistered: false,
|
|
2998
|
-
details
|
|
2999
|
-
};
|
|
3000
|
-
}
|
|
3001
|
-
/**
|
|
3002
|
-
* Uninstall CLEO from the Kimi environment.
|
|
3003
|
-
*
|
|
3004
|
-
* Does not remove AGENTS.md references (they are harmless if CLEO is not present).
|
|
3005
|
-
* @task T163
|
|
3006
|
-
*/
|
|
3007
|
-
async uninstall() {
|
|
3008
|
-
}
|
|
3009
|
-
/**
|
|
3010
|
-
* Check whether CLEO is installed in the Kimi environment.
|
|
3011
|
-
*
|
|
3012
|
-
* Checks for CLEO references in AGENTS.md.
|
|
3013
|
-
* @task T163
|
|
3014
|
-
*/
|
|
3015
|
-
async isInstalled() {
|
|
3016
|
-
const agentsMdPath = join17(process.cwd(), "AGENTS.md");
|
|
3017
|
-
if (existsSync11(agentsMdPath)) {
|
|
3018
|
-
try {
|
|
3019
|
-
const content = readFileSync7(agentsMdPath, "utf-8");
|
|
3020
|
-
if (INSTRUCTION_REFERENCES5.some((ref) => content.includes(ref))) {
|
|
3021
|
-
return true;
|
|
3022
|
-
}
|
|
3023
|
-
} catch {
|
|
3024
|
-
}
|
|
3025
|
-
}
|
|
3026
|
-
return false;
|
|
3027
|
-
}
|
|
3028
|
-
/**
|
|
3029
|
-
* Ensure AGENTS.md contains @-references to CLEO instruction files.
|
|
3030
|
-
*
|
|
3031
|
-
* Creates AGENTS.md if it does not exist. Appends any missing references.
|
|
3032
|
-
*
|
|
3033
|
-
* @param projectDir - Project root directory
|
|
3034
|
-
* @task T163
|
|
3035
|
-
*/
|
|
3036
|
-
async ensureInstructionReferences(projectDir) {
|
|
3037
|
-
this.updateInstructionFile(projectDir);
|
|
3038
|
-
}
|
|
3039
|
-
/**
|
|
3040
|
-
* Update AGENTS.md with CLEO @-references.
|
|
3041
|
-
*
|
|
3042
|
-
* @param projectDir - Project root directory
|
|
3043
|
-
* @returns true if the file was created or modified
|
|
3044
|
-
*/
|
|
3045
|
-
updateInstructionFile(projectDir) {
|
|
3046
|
-
const agentsMdPath = join17(projectDir, "AGENTS.md");
|
|
3047
|
-
let content = "";
|
|
3048
|
-
let existed = false;
|
|
3049
|
-
if (existsSync11(agentsMdPath)) {
|
|
3050
|
-
content = readFileSync7(agentsMdPath, "utf-8");
|
|
3051
|
-
existed = true;
|
|
3052
|
-
}
|
|
3053
|
-
const missingRefs = INSTRUCTION_REFERENCES5.filter((ref) => !content.includes(ref));
|
|
3054
|
-
if (missingRefs.length === 0) {
|
|
3055
|
-
return false;
|
|
3056
|
-
}
|
|
3057
|
-
const refsBlock = missingRefs.join("\n");
|
|
3058
|
-
if (existed) {
|
|
3059
|
-
const separator = content.endsWith("\n") ? "" : "\n";
|
|
3060
|
-
content = content + separator + refsBlock + "\n";
|
|
3061
|
-
} else {
|
|
3062
|
-
content = refsBlock + "\n";
|
|
3063
|
-
}
|
|
3064
|
-
writeFileSync6(agentsMdPath, content, "utf-8");
|
|
3065
|
-
return true;
|
|
3066
|
-
}
|
|
3067
|
-
};
|
|
3068
|
-
|
|
3069
|
-
// packages/adapters/src/providers/kimi/adapter.ts
|
|
3070
|
-
var execAsync5 = promisify5(exec5);
|
|
3071
|
-
var KimiAdapter = class {
|
|
3072
|
-
/** Unique provider identifier. */
|
|
3073
|
-
id = "kimi";
|
|
3074
|
-
/** Human-readable provider name. */
|
|
3075
|
-
name = "Kimi";
|
|
3076
|
-
/** Adapter version string. */
|
|
3077
|
-
version = "1.0.0";
|
|
3078
|
-
/** Declared capabilities for this provider. */
|
|
3079
|
-
capabilities = {
|
|
3080
|
-
supportsHooks: false,
|
|
3081
|
-
supportedHookEvents: [],
|
|
3082
|
-
supportsSpawn: false,
|
|
3083
|
-
supportsInstall: true,
|
|
3084
|
-
supportsMcp: false,
|
|
3085
|
-
supportsInstructionFiles: false,
|
|
3086
|
-
supportsContextMonitor: false,
|
|
3087
|
-
supportsStatusline: false,
|
|
3088
|
-
supportsProviderPaths: false,
|
|
3089
|
-
supportsTransport: false,
|
|
3090
|
-
supportsTaskSync: false
|
|
3091
|
-
};
|
|
3092
|
-
/** Hook provider (no-op since Kimi has no event system). */
|
|
3093
|
-
hooks;
|
|
3094
|
-
/** Install provider for managing instruction files. */
|
|
3095
|
-
install;
|
|
3096
|
-
/** Project directory this adapter was initialized with, or null. */
|
|
3097
|
-
projectDir = null;
|
|
3098
|
-
/** Whether {@link initialize} has been called. */
|
|
3099
|
-
initialized = false;
|
|
3100
|
-
constructor() {
|
|
3101
|
-
this.hooks = new KimiHookProvider();
|
|
3102
|
-
this.install = new KimiInstallProvider();
|
|
3103
|
-
}
|
|
3104
|
-
/**
|
|
3105
|
-
* Initialize the adapter for a given project directory.
|
|
3106
|
-
*
|
|
3107
|
-
* @param projectDir - Root directory of the project
|
|
3108
|
-
* @task T163
|
|
3109
|
-
*/
|
|
3110
|
-
async initialize(projectDir) {
|
|
3111
|
-
this.projectDir = projectDir;
|
|
3112
|
-
this.initialized = true;
|
|
3113
|
-
}
|
|
3114
|
-
/**
|
|
3115
|
-
* Dispose the adapter and clean up resources.
|
|
3116
|
-
*
|
|
3117
|
-
* Releases tracked state. No hooks to unregister since Kimi
|
|
3118
|
-
* has no native hook system.
|
|
3119
|
-
* @task T163
|
|
3120
|
-
*/
|
|
3121
|
-
async dispose() {
|
|
3122
|
-
this.initialized = false;
|
|
3123
|
-
this.projectDir = null;
|
|
3124
|
-
}
|
|
3125
|
-
/**
|
|
3126
|
-
* Run a health check to verify Kimi is accessible.
|
|
3127
|
-
*
|
|
3128
|
-
* Checks:
|
|
3129
|
-
* 1. Adapter has been initialized
|
|
3130
|
-
* 2. Kimi CLI binary is available in PATH
|
|
3131
|
-
* 3. ~/.kimi/ configuration directory exists
|
|
3132
|
-
*
|
|
3133
|
-
* @returns Health status with details about each check
|
|
3134
|
-
* @task T163
|
|
3135
|
-
*/
|
|
3136
|
-
async healthCheck() {
|
|
3137
|
-
const details = {};
|
|
3138
|
-
if (!this.initialized) {
|
|
3139
|
-
return {
|
|
3140
|
-
healthy: false,
|
|
3141
|
-
provider: this.id,
|
|
3142
|
-
details: { error: "Adapter not initialized" }
|
|
3143
|
-
};
|
|
3144
|
-
}
|
|
3145
|
-
let cliAvailable = false;
|
|
3146
|
-
try {
|
|
3147
|
-
const { stdout } = await execAsync5("which kimi");
|
|
3148
|
-
cliAvailable = stdout.trim().length > 0;
|
|
3149
|
-
details.cliPath = stdout.trim();
|
|
3150
|
-
} catch {
|
|
3151
|
-
details.cliAvailable = false;
|
|
3152
|
-
}
|
|
3153
|
-
const kimiConfigDir = join18(homedir10(), ".kimi");
|
|
3154
|
-
const configExists = existsSync12(kimiConfigDir);
|
|
3155
|
-
details.configDirExists = configExists;
|
|
3156
|
-
const healthy = cliAvailable;
|
|
3157
|
-
details.cliAvailable = cliAvailable;
|
|
3158
|
-
return {
|
|
3159
|
-
healthy,
|
|
3160
|
-
provider: this.id,
|
|
3161
|
-
details
|
|
3162
|
-
};
|
|
3163
|
-
}
|
|
3164
|
-
/**
|
|
3165
|
-
* Check whether the adapter has been initialized.
|
|
3166
|
-
* @task T163
|
|
3167
|
-
*/
|
|
3168
|
-
isInitialized() {
|
|
3169
|
-
return this.initialized;
|
|
3170
|
-
}
|
|
3171
|
-
/**
|
|
3172
|
-
* Get the project directory this adapter was initialized with.
|
|
3173
|
-
* @task T163
|
|
3174
|
-
*/
|
|
3175
|
-
getProjectDir() {
|
|
3176
|
-
return this.projectDir;
|
|
3177
|
-
}
|
|
3178
|
-
};
|
|
3179
|
-
|
|
3180
|
-
// packages/adapters/src/providers/kimi/index.ts
|
|
3181
|
-
function createAdapter5() {
|
|
3182
|
-
return new KimiAdapter();
|
|
3183
|
-
}
|
|
3184
|
-
|
|
3185
|
-
// packages/adapters/src/index.ts
|
|
3186
|
-
init_opencode();
|
|
3187
|
-
|
|
3188
|
-
// packages/adapters/src/registry.ts
|
|
3189
|
-
import { readFileSync as readFileSync9 } from "node:fs";
|
|
3190
|
-
import { dirname as dirname2, join as join22, resolve } from "node:path";
|
|
3191
|
-
import { fileURLToPath } from "node:url";
|
|
3192
|
-
var PROVIDER_IDS = ["claude-code", "opencode", "cursor"];
|
|
3193
|
-
function getProviderManifests() {
|
|
3194
|
-
const manifests = [];
|
|
3195
|
-
const baseDir = resolve(dirname2(fileURLToPath(import.meta.url)), "providers");
|
|
3196
|
-
for (const providerId of PROVIDER_IDS) {
|
|
3197
|
-
try {
|
|
3198
|
-
const manifestPath = join22(baseDir, providerId, "manifest.json");
|
|
3199
|
-
const raw = readFileSync9(manifestPath, "utf-8");
|
|
3200
|
-
manifests.push(JSON.parse(raw));
|
|
3201
|
-
} catch {
|
|
3202
|
-
}
|
|
3203
|
-
}
|
|
3204
|
-
return manifests;
|
|
3205
|
-
}
|
|
3206
|
-
async function discoverProviders() {
|
|
3207
|
-
const providers = /* @__PURE__ */ new Map();
|
|
3208
|
-
providers.set("claude-code", async () => {
|
|
3209
|
-
const { ClaudeCodeAdapter: ClaudeCodeAdapter2 } = await Promise.resolve().then(() => (init_claude_code(), claude_code_exports));
|
|
3210
|
-
return new ClaudeCodeAdapter2();
|
|
3211
|
-
});
|
|
3212
|
-
providers.set("opencode", async () => {
|
|
3213
|
-
const { OpenCodeAdapter: OpenCodeAdapter2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
|
|
3214
|
-
return new OpenCodeAdapter2();
|
|
3215
|
-
});
|
|
3216
|
-
providers.set("cursor", async () => {
|
|
3217
|
-
const { CursorAdapter: CursorAdapter2 } = await Promise.resolve().then(() => (init_cursor(), cursor_exports));
|
|
3218
|
-
return new CursorAdapter2();
|
|
3219
|
-
});
|
|
3220
|
-
return providers;
|
|
3221
|
-
}
|
|
3222
|
-
export {
|
|
3223
|
-
ClaudeCodeAdapter,
|
|
3224
|
-
ClaudeCodeContextMonitorProvider,
|
|
3225
|
-
ClaudeCodeHookProvider,
|
|
3226
|
-
ClaudeCodeInstallProvider,
|
|
3227
|
-
ClaudeCodePathProvider,
|
|
3228
|
-
ClaudeCodeSpawnProvider,
|
|
3229
|
-
ClaudeCodeTransportProvider,
|
|
3230
|
-
CodexAdapter,
|
|
3231
|
-
CodexHookProvider,
|
|
3232
|
-
CodexInstallProvider,
|
|
3233
|
-
CursorAdapter,
|
|
3234
|
-
CursorHookProvider,
|
|
3235
|
-
CursorInstallProvider,
|
|
3236
|
-
GeminiCliAdapter,
|
|
3237
|
-
GeminiCliHookProvider,
|
|
3238
|
-
GeminiCliInstallProvider,
|
|
3239
|
-
KimiAdapter,
|
|
3240
|
-
KimiHookProvider,
|
|
3241
|
-
KimiInstallProvider,
|
|
3242
|
-
OpenCodeAdapter,
|
|
3243
|
-
OpenCodeHookProvider,
|
|
3244
|
-
OpenCodeInstallProvider,
|
|
3245
|
-
OpenCodeSpawnProvider,
|
|
3246
|
-
checkStatuslineIntegration,
|
|
3247
|
-
createAdapter as createClaudeCodeAdapter,
|
|
3248
|
-
createAdapter2 as createCodexAdapter,
|
|
3249
|
-
createAdapter3 as createCursorAdapter,
|
|
3250
|
-
createAdapter4 as createGeminiCliAdapter,
|
|
3251
|
-
createAdapter5 as createKimiAdapter,
|
|
3252
|
-
createAdapter6 as createOpenCodeAdapter,
|
|
3253
|
-
discoverProviders,
|
|
3254
|
-
getProviderManifests,
|
|
3255
|
-
getSetupInstructions,
|
|
3256
|
-
getStatuslineConfig
|
|
3257
|
-
};
|
|
3258
|
-
//# sourceMappingURL=index.js.map
|