@cleocode/adapters 2026.4.13 → 2026.4.14

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