@cleocode/adapters 2.0.0

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 (217) hide show
  1. package/dist/index.d.ts +27 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +1927 -0
  4. package/dist/index.js.map +7 -0
  5. package/dist/providers/claude-code/adapter.d.ts +75 -0
  6. package/dist/providers/claude-code/adapter.d.ts.map +1 -0
  7. package/dist/providers/claude-code/adapter.js +154 -0
  8. package/dist/providers/claude-code/adapter.js.map +1 -0
  9. package/dist/providers/claude-code/context-monitor.d.ts +24 -0
  10. package/dist/providers/claude-code/context-monitor.d.ts.map +1 -0
  11. package/dist/providers/claude-code/context-monitor.js +148 -0
  12. package/dist/providers/claude-code/context-monitor.js.map +1 -0
  13. package/dist/providers/claude-code/hooks.d.ts +59 -0
  14. package/dist/providers/claude-code/hooks.d.ts.map +1 -0
  15. package/dist/providers/claude-code/hooks.js +77 -0
  16. package/dist/providers/claude-code/hooks.js.map +1 -0
  17. package/dist/providers/claude-code/index.d.ts +24 -0
  18. package/dist/providers/claude-code/index.d.ts.map +1 -0
  19. package/dist/providers/claude-code/index.js +26 -0
  20. package/dist/providers/claude-code/index.js.map +1 -0
  21. package/dist/providers/claude-code/install.d.ts +75 -0
  22. package/dist/providers/claude-code/install.d.ts.map +1 -0
  23. package/dist/providers/claude-code/install.js +237 -0
  24. package/dist/providers/claude-code/install.js.map +1 -0
  25. package/dist/providers/claude-code/paths.d.ts +24 -0
  26. package/dist/providers/claude-code/paths.d.ts.map +1 -0
  27. package/dist/providers/claude-code/paths.js +33 -0
  28. package/dist/providers/claude-code/paths.js.map +1 -0
  29. package/dist/providers/claude-code/spawn.d.ts +60 -0
  30. package/dist/providers/claude-code/spawn.d.ts.map +1 -0
  31. package/dist/providers/claude-code/spawn.js +160 -0
  32. package/dist/providers/claude-code/spawn.js.map +1 -0
  33. package/dist/providers/claude-code/statusline.d.ts +24 -0
  34. package/dist/providers/claude-code/statusline.d.ts.map +1 -0
  35. package/dist/providers/claude-code/statusline.js +85 -0
  36. package/dist/providers/claude-code/statusline.js.map +1 -0
  37. package/dist/providers/claude-code/task-sync.d.ts +27 -0
  38. package/dist/providers/claude-code/task-sync.d.ts.map +1 -0
  39. package/dist/providers/claude-code/task-sync.js +124 -0
  40. package/dist/providers/claude-code/task-sync.js.map +1 -0
  41. package/dist/providers/claude-code/transport.d.ts +14 -0
  42. package/dist/providers/claude-code/transport.d.ts.map +1 -0
  43. package/dist/providers/claude-code/transport.js +18 -0
  44. package/dist/providers/claude-code/transport.js.map +1 -0
  45. package/dist/providers/cursor/adapter.d.ts +62 -0
  46. package/dist/providers/cursor/adapter.d.ts.map +1 -0
  47. package/dist/providers/cursor/adapter.js +124 -0
  48. package/dist/providers/cursor/adapter.js.map +1 -0
  49. package/dist/providers/cursor/hooks.d.ts +48 -0
  50. package/dist/providers/cursor/hooks.d.ts.map +1 -0
  51. package/dist/providers/cursor/hooks.js +55 -0
  52. package/dist/providers/cursor/hooks.js.map +1 -0
  53. package/dist/providers/cursor/index.d.ts +19 -0
  54. package/dist/providers/cursor/index.d.ts.map +1 -0
  55. package/dist/providers/cursor/index.js +21 -0
  56. package/dist/providers/cursor/index.js.map +1 -0
  57. package/dist/providers/cursor/install.d.ts +94 -0
  58. package/dist/providers/cursor/install.d.ts.map +1 -0
  59. package/dist/providers/cursor/install.js +241 -0
  60. package/dist/providers/cursor/install.js.map +1 -0
  61. package/dist/providers/cursor/spawn.d.ts +50 -0
  62. package/dist/providers/cursor/spawn.d.ts.map +1 -0
  63. package/dist/providers/cursor/spawn.js +59 -0
  64. package/dist/providers/cursor/spawn.js.map +1 -0
  65. package/dist/providers/opencode/adapter.d.ts +67 -0
  66. package/dist/providers/opencode/adapter.d.ts.map +1 -0
  67. package/dist/providers/opencode/adapter.js +144 -0
  68. package/dist/providers/opencode/adapter.js.map +1 -0
  69. package/dist/providers/opencode/hooks.d.ts +66 -0
  70. package/dist/providers/opencode/hooks.d.ts.map +1 -0
  71. package/dist/providers/opencode/hooks.js +89 -0
  72. package/dist/providers/opencode/hooks.js.map +1 -0
  73. package/dist/providers/opencode/index.d.ts +20 -0
  74. package/dist/providers/opencode/index.d.ts.map +1 -0
  75. package/dist/providers/opencode/index.js +22 -0
  76. package/dist/providers/opencode/index.js.map +1 -0
  77. package/dist/providers/opencode/install.d.ts +65 -0
  78. package/dist/providers/opencode/install.d.ts.map +1 -0
  79. package/dist/providers/opencode/install.js +183 -0
  80. package/dist/providers/opencode/install.js.map +1 -0
  81. package/dist/providers/opencode/spawn.d.ts +72 -0
  82. package/dist/providers/opencode/spawn.d.ts.map +1 -0
  83. package/dist/providers/opencode/spawn.js +219 -0
  84. package/dist/providers/opencode/spawn.js.map +1 -0
  85. package/dist/registry.d.ts +36 -0
  86. package/dist/registry.d.ts.map +1 -0
  87. package/dist/registry.js +55 -0
  88. package/dist/registry.js.map +1 -0
  89. package/package.json +32 -0
  90. package/src/index.d.ts +27 -0
  91. package/src/index.d.ts.map +1 -0
  92. package/src/index.js +28 -0
  93. package/src/index.js.map +1 -0
  94. package/src/index.ts +37 -0
  95. package/src/providers/claude-code/__tests__/adapter.test.d.ts +7 -0
  96. package/src/providers/claude-code/__tests__/adapter.test.d.ts.map +1 -0
  97. package/src/providers/claude-code/__tests__/adapter.test.js +249 -0
  98. package/src/providers/claude-code/__tests__/adapter.test.js.map +1 -0
  99. package/src/providers/claude-code/__tests__/adapter.test.ts +291 -0
  100. package/src/providers/claude-code/adapter.d.ts +75 -0
  101. package/src/providers/claude-code/adapter.d.ts.map +1 -0
  102. package/src/providers/claude-code/adapter.js +154 -0
  103. package/src/providers/claude-code/adapter.js.map +1 -0
  104. package/src/providers/claude-code/adapter.ts +175 -0
  105. package/src/providers/claude-code/context-monitor.d.ts +24 -0
  106. package/src/providers/claude-code/context-monitor.d.ts.map +1 -0
  107. package/src/providers/claude-code/context-monitor.js +148 -0
  108. package/src/providers/claude-code/context-monitor.js.map +1 -0
  109. package/src/providers/claude-code/context-monitor.ts +175 -0
  110. package/src/providers/claude-code/hooks.d.ts +59 -0
  111. package/src/providers/claude-code/hooks.d.ts.map +1 -0
  112. package/src/providers/claude-code/hooks.js +77 -0
  113. package/src/providers/claude-code/hooks.js.map +1 -0
  114. package/src/providers/claude-code/hooks.ts +85 -0
  115. package/src/providers/claude-code/index.d.ts +24 -0
  116. package/src/providers/claude-code/index.d.ts.map +1 -0
  117. package/src/providers/claude-code/index.js +26 -0
  118. package/src/providers/claude-code/index.js.map +1 -0
  119. package/src/providers/claude-code/index.ts +33 -0
  120. package/src/providers/claude-code/install.d.ts +75 -0
  121. package/src/providers/claude-code/install.d.ts.map +1 -0
  122. package/src/providers/claude-code/install.js +237 -0
  123. package/src/providers/claude-code/install.js.map +1 -0
  124. package/src/providers/claude-code/install.ts +267 -0
  125. package/src/providers/claude-code/manifest.json +26 -0
  126. package/src/providers/claude-code/paths.d.ts +24 -0
  127. package/src/providers/claude-code/paths.d.ts.map +1 -0
  128. package/src/providers/claude-code/paths.js +33 -0
  129. package/src/providers/claude-code/paths.js.map +1 -0
  130. package/src/providers/claude-code/paths.ts +38 -0
  131. package/src/providers/claude-code/spawn.d.ts +60 -0
  132. package/src/providers/claude-code/spawn.d.ts.map +1 -0
  133. package/src/providers/claude-code/spawn.js +160 -0
  134. package/src/providers/claude-code/spawn.js.map +1 -0
  135. package/src/providers/claude-code/spawn.ts +178 -0
  136. package/src/providers/claude-code/statusline.d.ts +24 -0
  137. package/src/providers/claude-code/statusline.d.ts.map +1 -0
  138. package/src/providers/claude-code/statusline.js +85 -0
  139. package/src/providers/claude-code/statusline.js.map +1 -0
  140. package/src/providers/claude-code/statusline.ts +99 -0
  141. package/src/providers/claude-code/task-sync.d.ts +27 -0
  142. package/src/providers/claude-code/task-sync.d.ts.map +1 -0
  143. package/src/providers/claude-code/task-sync.js +124 -0
  144. package/src/providers/claude-code/task-sync.js.map +1 -0
  145. package/src/providers/claude-code/task-sync.ts +158 -0
  146. package/src/providers/claude-code/transport.d.ts +14 -0
  147. package/src/providers/claude-code/transport.d.ts.map +1 -0
  148. package/src/providers/claude-code/transport.js +18 -0
  149. package/src/providers/claude-code/transport.js.map +1 -0
  150. package/src/providers/claude-code/transport.ts +21 -0
  151. package/src/providers/cursor/__tests__/adapter.test.d.ts +7 -0
  152. package/src/providers/cursor/__tests__/adapter.test.d.ts.map +1 -0
  153. package/src/providers/cursor/__tests__/adapter.test.js +246 -0
  154. package/src/providers/cursor/__tests__/adapter.test.js.map +1 -0
  155. package/src/providers/cursor/__tests__/adapter.test.ts +291 -0
  156. package/src/providers/cursor/adapter.d.ts +62 -0
  157. package/src/providers/cursor/adapter.d.ts.map +1 -0
  158. package/src/providers/cursor/adapter.js +124 -0
  159. package/src/providers/cursor/adapter.js.map +1 -0
  160. package/src/providers/cursor/adapter.ts +145 -0
  161. package/src/providers/cursor/hooks.d.ts +48 -0
  162. package/src/providers/cursor/hooks.d.ts.map +1 -0
  163. package/src/providers/cursor/hooks.js +55 -0
  164. package/src/providers/cursor/hooks.js.map +1 -0
  165. package/src/providers/cursor/hooks.ts +61 -0
  166. package/src/providers/cursor/index.d.ts +19 -0
  167. package/src/providers/cursor/index.d.ts.map +1 -0
  168. package/src/providers/cursor/index.js +21 -0
  169. package/src/providers/cursor/index.js.map +1 -0
  170. package/src/providers/cursor/index.ts +24 -0
  171. package/src/providers/cursor/install.d.ts +94 -0
  172. package/src/providers/cursor/install.d.ts.map +1 -0
  173. package/src/providers/cursor/install.js +241 -0
  174. package/src/providers/cursor/install.js.map +1 -0
  175. package/src/providers/cursor/install.ts +271 -0
  176. package/src/providers/cursor/manifest.json +26 -0
  177. package/src/providers/cursor/spawn.d.ts +50 -0
  178. package/src/providers/cursor/spawn.d.ts.map +1 -0
  179. package/src/providers/cursor/spawn.js +59 -0
  180. package/src/providers/cursor/spawn.js.map +1 -0
  181. package/src/providers/cursor/spawn.ts +66 -0
  182. package/src/providers/opencode/__tests__/adapter.test.d.ts +7 -0
  183. package/src/providers/opencode/__tests__/adapter.test.d.ts.map +1 -0
  184. package/src/providers/opencode/__tests__/adapter.test.js +263 -0
  185. package/src/providers/opencode/__tests__/adapter.test.js.map +1 -0
  186. package/src/providers/opencode/__tests__/adapter.test.ts +309 -0
  187. package/src/providers/opencode/adapter.d.ts +67 -0
  188. package/src/providers/opencode/adapter.d.ts.map +1 -0
  189. package/src/providers/opencode/adapter.js +144 -0
  190. package/src/providers/opencode/adapter.js.map +1 -0
  191. package/src/providers/opencode/adapter.ts +165 -0
  192. package/src/providers/opencode/hooks.d.ts +66 -0
  193. package/src/providers/opencode/hooks.d.ts.map +1 -0
  194. package/src/providers/opencode/hooks.js +89 -0
  195. package/src/providers/opencode/hooks.js.map +1 -0
  196. package/src/providers/opencode/hooks.ts +97 -0
  197. package/src/providers/opencode/index.d.ts +20 -0
  198. package/src/providers/opencode/index.d.ts.map +1 -0
  199. package/src/providers/opencode/index.js +22 -0
  200. package/src/providers/opencode/index.js.map +1 -0
  201. package/src/providers/opencode/index.ts +25 -0
  202. package/src/providers/opencode/install.d.ts +65 -0
  203. package/src/providers/opencode/install.d.ts.map +1 -0
  204. package/src/providers/opencode/install.js +183 -0
  205. package/src/providers/opencode/install.js.map +1 -0
  206. package/src/providers/opencode/install.ts +206 -0
  207. package/src/providers/opencode/manifest.json +26 -0
  208. package/src/providers/opencode/spawn.d.ts +72 -0
  209. package/src/providers/opencode/spawn.d.ts.map +1 -0
  210. package/src/providers/opencode/spawn.js +219 -0
  211. package/src/providers/opencode/spawn.js.map +1 -0
  212. package/src/providers/opencode/spawn.ts +253 -0
  213. package/src/registry.d.ts +36 -0
  214. package/src/registry.d.ts.map +1 -0
  215. package/src/registry.js +55 -0
  216. package/src/registry.js.map +1 -0
  217. package/src/registry.ts +81 -0
package/dist/index.js ADDED
@@ -0,0 +1,1927 @@
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.js
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.js"() {
17
+ "use strict";
18
+ ClaudeCodePathProvider = class {
19
+ getProviderDir() {
20
+ return process.env["CLAUDE_HOME"] ?? join(homedir(), ".claude");
21
+ }
22
+ getSettingsPath() {
23
+ return process.env["CLAUDE_SETTINGS"] ?? join(this.getProviderDir(), "settings.json");
24
+ }
25
+ getAgentInstallDir() {
26
+ return join(this.getProviderDir(), "agents");
27
+ }
28
+ getMemoryDbPath() {
29
+ return process.env["CLAUDE_MEM_DB"] ?? join(homedir(), ".claude-mem", "claude-mem.db");
30
+ }
31
+ };
32
+ }
33
+ });
34
+
35
+ // packages/adapters/src/providers/claude-code/context-monitor.js
36
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
37
+ import { mkdir } from "node:fs/promises";
38
+ import { homedir as homedir2 } from "node:os";
39
+ import { dirname, join as join2 } from "node:path";
40
+ function getContextStatusFromPercentage(percentage) {
41
+ if (percentage >= THRESHOLDS.EMERGENCY)
42
+ return "emergency";
43
+ if (percentage >= THRESHOLDS.CRITICAL)
44
+ return "critical";
45
+ if (percentage >= THRESHOLDS.CAUTION)
46
+ return "caution";
47
+ if (percentage >= THRESHOLDS.WARNING)
48
+ 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.js"() {
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
+ pathProvider = new ClaudeCodePathProvider();
64
+ async processContextInput(input, cwd) {
65
+ const typed = input;
66
+ const contextSize = typed.context_window?.context_window_size ?? 2e5;
67
+ const usage = typed.context_window?.current_usage;
68
+ if (!usage)
69
+ return "-- no data";
70
+ const inputTokens = usage.input_tokens ?? 0;
71
+ const outputTokens = usage.output_tokens ?? 0;
72
+ const cacheCreate = usage.cache_creation_input_tokens ?? 0;
73
+ const totalTokens = inputTokens + outputTokens + cacheCreate;
74
+ const percentage = Math.floor(totalTokens * 100 / contextSize);
75
+ const status = getContextStatusFromPercentage(percentage);
76
+ const cleoDir = cwd ? join2(cwd, ".cleo") : ".cleo";
77
+ if (existsSync(cleoDir)) {
78
+ const stateDir = join2(cleoDir, "context-states");
79
+ const statePath = join2(stateDir, ".context-state.json");
80
+ const state = {
81
+ $schema: "https://cleo-dev.com/schemas/v1/context-state.schema.json",
82
+ version: "1.0.0",
83
+ timestamp: (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z"),
84
+ staleAfterMs: 5e3,
85
+ contextWindow: {
86
+ maxTokens: contextSize,
87
+ currentTokens: totalTokens,
88
+ percentage,
89
+ breakdown: {
90
+ inputTokens,
91
+ outputTokens,
92
+ cacheCreationTokens: cacheCreate,
93
+ cacheReadTokens: usage.cache_read_input_tokens ?? 0
94
+ }
95
+ },
96
+ thresholds: {
97
+ warning: THRESHOLDS.WARNING,
98
+ caution: THRESHOLDS.CAUTION,
99
+ critical: THRESHOLDS.CRITICAL,
100
+ emergency: THRESHOLDS.EMERGENCY
101
+ },
102
+ status,
103
+ cleoSessionId: ""
104
+ };
105
+ try {
106
+ await mkdir(dirname(statePath), { recursive: true });
107
+ writeFileSync(statePath, JSON.stringify(state, null, 2));
108
+ } catch {
109
+ }
110
+ }
111
+ return `${percentage}% | ${totalTokens}/${contextSize}`;
112
+ }
113
+ checkStatuslineIntegration() {
114
+ const settingsPath = this.pathProvider.getSettingsPath();
115
+ if (!settingsPath || !existsSync(settingsPath))
116
+ return "no_settings";
117
+ try {
118
+ const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
119
+ const statusLine = settings.statusLine;
120
+ if (!statusLine?.type)
121
+ return "not_configured";
122
+ if (statusLine.type !== "command")
123
+ return "custom_no_cleo";
124
+ const cmd = statusLine.command ?? "";
125
+ if (cmd.includes("context-monitor.sh") || cmd.includes("cleo-statusline") || cmd.includes(".context-state.json") || cmd.includes("context-states")) {
126
+ return "configured";
127
+ }
128
+ const scriptPath = cmd.startsWith("~") ? cmd.replace("~", homedir2()) : cmd;
129
+ if (existsSync(scriptPath)) {
130
+ try {
131
+ const content = readFileSync(scriptPath, "utf-8");
132
+ if (content.includes("context-state.json"))
133
+ return "configured";
134
+ } catch {
135
+ }
136
+ }
137
+ return "custom_no_cleo";
138
+ } catch {
139
+ return "no_settings";
140
+ }
141
+ }
142
+ getStatuslineConfig() {
143
+ return {
144
+ statusLine: {
145
+ type: "command",
146
+ command: join2(homedir2(), ".cleo", "lib", "session", "context-monitor.sh")
147
+ }
148
+ };
149
+ }
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.js
166
+ var CLAUDE_CODE_EVENT_MAP, ClaudeCodeHookProvider;
167
+ var init_hooks = __esm({
168
+ "packages/adapters/src/providers/claude-code/hooks.js"() {
169
+ "use strict";
170
+ CLAUDE_CODE_EVENT_MAP = {
171
+ SessionStart: "onSessionStart",
172
+ PostToolUse: "onToolComplete",
173
+ UserPromptSubmit: "onPromptSubmit",
174
+ Stop: "onSessionEnd"
175
+ };
176
+ ClaudeCodeHookProvider = class {
177
+ registered = false;
178
+ /**
179
+ * Map a Claude Code native event name to a CAAMP hook event name.
180
+ *
181
+ * @param providerEvent - Claude Code event name (e.g. "SessionStart", "PostToolUse")
182
+ * @returns CAAMP event name or null if unmapped
183
+ */
184
+ mapProviderEvent(providerEvent) {
185
+ return CLAUDE_CODE_EVENT_MAP[providerEvent] ?? null;
186
+ }
187
+ /**
188
+ * Register native hooks for a project.
189
+ *
190
+ * For Claude Code, hooks are registered via the plugin system
191
+ * (hooks.json descriptor), which is handled by the
192
+ * install provider. This method is a no-op since registration
193
+ * is managed through the plugin install lifecycle.
194
+ *
195
+ * @param _projectDir - Project directory (unused; hooks are global)
196
+ */
197
+ async registerNativeHooks(_projectDir) {
198
+ this.registered = true;
199
+ }
200
+ /**
201
+ * Unregister native hooks.
202
+ *
203
+ * For Claude Code, this is a no-op since hooks are managed through
204
+ * the plugin system. Unregistration happens via the install provider's
205
+ * uninstall method.
206
+ */
207
+ async unregisterNativeHooks() {
208
+ this.registered = false;
209
+ }
210
+ /**
211
+ * Check whether hooks have been registered via registerNativeHooks.
212
+ */
213
+ isRegistered() {
214
+ return this.registered;
215
+ }
216
+ /**
217
+ * Get the full event mapping for introspection/debugging.
218
+ */
219
+ getEventMap() {
220
+ return { ...CLAUDE_CODE_EVENT_MAP };
221
+ }
222
+ };
223
+ }
224
+ });
225
+
226
+ // packages/adapters/src/providers/claude-code/install.js
227
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
228
+ import { homedir as homedir3 } from "node:os";
229
+ import { join as join3 } from "node:path";
230
+ var INSTRUCTION_REFERENCES, MCP_SERVER_KEY, ClaudeCodeInstallProvider;
231
+ var init_install = __esm({
232
+ "packages/adapters/src/providers/claude-code/install.js"() {
233
+ "use strict";
234
+ INSTRUCTION_REFERENCES = [
235
+ "@~/.cleo/templates/CLEO-INJECTION.md",
236
+ "@.cleo/memory-bridge.md"
237
+ ];
238
+ MCP_SERVER_KEY = "cleo";
239
+ ClaudeCodeInstallProvider = class {
240
+ installedProjectDir = null;
241
+ /**
242
+ * Install CLEO into a Claude Code project.
243
+ *
244
+ * @param options - Installation options including project directory and MCP server path
245
+ * @returns Result describing what was installed
246
+ */
247
+ async install(options) {
248
+ const { projectDir, mcpServerPath } = options;
249
+ const installedAt = (/* @__PURE__ */ new Date()).toISOString();
250
+ let instructionFileUpdated = false;
251
+ let mcpRegistered = false;
252
+ const details = {};
253
+ if (mcpServerPath) {
254
+ mcpRegistered = this.registerMcpServer(projectDir, mcpServerPath);
255
+ if (mcpRegistered) {
256
+ details.mcpConfigPath = join3(projectDir, ".mcp.json");
257
+ }
258
+ }
259
+ instructionFileUpdated = this.updateInstructionFile(projectDir);
260
+ if (instructionFileUpdated) {
261
+ details.instructionFile = join3(projectDir, "CLAUDE.md");
262
+ }
263
+ const pluginResult = this.registerPlugin();
264
+ if (pluginResult) {
265
+ details.plugin = pluginResult;
266
+ }
267
+ this.installedProjectDir = projectDir;
268
+ return {
269
+ success: true,
270
+ installedAt,
271
+ instructionFileUpdated,
272
+ mcpRegistered,
273
+ details
274
+ };
275
+ }
276
+ /**
277
+ * Uninstall CLEO from the current Claude Code project.
278
+ *
279
+ * Removes the MCP server registration from .mcp.json.
280
+ * Does not remove CLAUDE.md references (they are harmless if CLEO is not present).
281
+ */
282
+ async uninstall() {
283
+ if (!this.installedProjectDir)
284
+ return;
285
+ const mcpPath = join3(this.installedProjectDir, ".mcp.json");
286
+ if (existsSync2(mcpPath)) {
287
+ try {
288
+ const raw = readFileSync2(mcpPath, "utf-8");
289
+ const config = JSON.parse(raw);
290
+ const mcpServers = config.mcpServers;
291
+ if (mcpServers && MCP_SERVER_KEY in mcpServers) {
292
+ delete mcpServers[MCP_SERVER_KEY];
293
+ writeFileSync2(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
294
+ }
295
+ } catch {
296
+ }
297
+ }
298
+ this.installedProjectDir = null;
299
+ }
300
+ /**
301
+ * Check whether CLEO is installed in the current environment.
302
+ *
303
+ * Checks for:
304
+ * 1. MCP server registered in .mcp.json
305
+ * 2. Plugin enabled in ~/.claude/settings.json
306
+ *
307
+ * Returns true if either condition is met (partial install counts).
308
+ */
309
+ async isInstalled() {
310
+ const settingsPath = join3(homedir3(), ".claude", "settings.json");
311
+ if (existsSync2(settingsPath)) {
312
+ try {
313
+ const settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
314
+ const plugins = settings.enabledPlugins;
315
+ if (plugins && plugins["cleo@cleocode"] === true) {
316
+ return true;
317
+ }
318
+ } catch {
319
+ }
320
+ }
321
+ const mcpPath = join3(process.cwd(), ".mcp.json");
322
+ if (existsSync2(mcpPath)) {
323
+ try {
324
+ const config = JSON.parse(readFileSync2(mcpPath, "utf-8"));
325
+ const mcpServers = config.mcpServers;
326
+ if (mcpServers && MCP_SERVER_KEY in mcpServers) {
327
+ return true;
328
+ }
329
+ } catch {
330
+ }
331
+ }
332
+ return false;
333
+ }
334
+ /**
335
+ * Ensure CLAUDE.md contains @-references to CLEO instruction files.
336
+ *
337
+ * Creates CLAUDE.md if it does not exist. Appends any missing references.
338
+ *
339
+ * @param projectDir - Project root directory
340
+ */
341
+ async ensureInstructionReferences(projectDir) {
342
+ this.updateInstructionFile(projectDir);
343
+ }
344
+ /**
345
+ * Register the CLEO MCP server in .mcp.json.
346
+ *
347
+ * @returns true if registration was performed or updated
348
+ */
349
+ registerMcpServer(projectDir, mcpServerPath) {
350
+ const mcpPath = join3(projectDir, ".mcp.json");
351
+ let config = {};
352
+ if (existsSync2(mcpPath)) {
353
+ try {
354
+ config = JSON.parse(readFileSync2(mcpPath, "utf-8"));
355
+ } catch {
356
+ }
357
+ }
358
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
359
+ config.mcpServers = {};
360
+ }
361
+ const mcpServers = config.mcpServers;
362
+ mcpServers[MCP_SERVER_KEY] = {
363
+ command: "node",
364
+ args: [mcpServerPath]
365
+ };
366
+ writeFileSync2(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
367
+ return true;
368
+ }
369
+ /**
370
+ * Update CLAUDE.md with CLEO @-references.
371
+ *
372
+ * @returns true if the file was created or modified
373
+ */
374
+ updateInstructionFile(projectDir) {
375
+ const claudeMdPath = join3(projectDir, "CLAUDE.md");
376
+ let content = "";
377
+ let existed = false;
378
+ if (existsSync2(claudeMdPath)) {
379
+ content = readFileSync2(claudeMdPath, "utf-8");
380
+ existed = true;
381
+ }
382
+ const missingRefs = INSTRUCTION_REFERENCES.filter((ref) => !content.includes(ref));
383
+ if (missingRefs.length === 0) {
384
+ return false;
385
+ }
386
+ const refsBlock = missingRefs.join("\n");
387
+ if (existed) {
388
+ const separator = content.endsWith("\n") ? "" : "\n";
389
+ content = content + separator + refsBlock + "\n";
390
+ } else {
391
+ content = refsBlock + "\n";
392
+ }
393
+ writeFileSync2(claudeMdPath, content, "utf-8");
394
+ return true;
395
+ }
396
+ /**
397
+ * Register the CLEO brain plugin in ~/.claude/settings.json.
398
+ *
399
+ * @returns Description of what was registered, or null if no change needed
400
+ */
401
+ registerPlugin() {
402
+ const home = homedir3();
403
+ const settingsPath = join3(home, ".claude", "settings.json");
404
+ let settings = {};
405
+ if (existsSync2(settingsPath)) {
406
+ try {
407
+ settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
408
+ } catch {
409
+ }
410
+ }
411
+ const enabledPlugins = settings.enabledPlugins ?? {};
412
+ const pluginKey = "cleo@cleocode";
413
+ if (enabledPlugins[pluginKey] === true) {
414
+ return null;
415
+ }
416
+ if (enabledPlugins["claude-mem@thedotmack"] === true) {
417
+ enabledPlugins["claude-mem@thedotmack"] = false;
418
+ }
419
+ enabledPlugins[pluginKey] = true;
420
+ settings.enabledPlugins = enabledPlugins;
421
+ mkdirSync(join3(home, ".claude"), { recursive: true });
422
+ writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
423
+ return `Enabled ${pluginKey} in ~/.claude/settings.json`;
424
+ }
425
+ };
426
+ }
427
+ });
428
+
429
+ // packages/adapters/src/providers/claude-code/spawn.js
430
+ import { exec, spawn as nodeSpawn } from "node:child_process";
431
+ import { unlink, writeFile } from "node:fs/promises";
432
+ import { promisify } from "node:util";
433
+ var execAsync, ClaudeCodeSpawnProvider;
434
+ var init_spawn = __esm({
435
+ "packages/adapters/src/providers/claude-code/spawn.js"() {
436
+ "use strict";
437
+ execAsync = promisify(exec);
438
+ ClaudeCodeSpawnProvider = class {
439
+ /** Map of instance IDs to tracked process info. */
440
+ processMap = /* @__PURE__ */ new Map();
441
+ /**
442
+ * Check if the Claude CLI is available in PATH.
443
+ *
444
+ * @returns true if `claude` is found via `which`
445
+ */
446
+ async canSpawn() {
447
+ try {
448
+ await execAsync("which claude");
449
+ return true;
450
+ } catch {
451
+ return false;
452
+ }
453
+ }
454
+ /**
455
+ * Spawn a subagent via Claude CLI.
456
+ *
457
+ * Writes the prompt to a temporary file and spawns a detached Claude
458
+ * process. The process runs independently of the parent.
459
+ *
460
+ * @param context - Spawn context with taskId, prompt, and options
461
+ * @returns Spawn result with instance ID and status
462
+ */
463
+ async spawn(context) {
464
+ const instanceId = `claude-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
465
+ const startTime = (/* @__PURE__ */ new Date()).toISOString();
466
+ let tmpFile;
467
+ try {
468
+ tmpFile = `/tmp/claude-spawn-${instanceId}.txt`;
469
+ await writeFile(tmpFile, context.prompt, "utf-8");
470
+ const args = ["--allow-insecure", "--no-upgrade-check", tmpFile];
471
+ const spawnOpts = {
472
+ detached: true,
473
+ stdio: "ignore"
474
+ };
475
+ if (context.workingDirectory) {
476
+ spawnOpts.cwd = context.workingDirectory;
477
+ }
478
+ const child = nodeSpawn("claude", args, spawnOpts);
479
+ child.unref();
480
+ if (child.pid) {
481
+ this.processMap.set(instanceId, {
482
+ pid: child.pid,
483
+ taskId: context.taskId,
484
+ startTime
485
+ });
486
+ }
487
+ const capturedTmpFile = tmpFile;
488
+ child.on("exit", async () => {
489
+ this.processMap.delete(instanceId);
490
+ try {
491
+ await unlink(capturedTmpFile);
492
+ } catch {
493
+ }
494
+ });
495
+ return {
496
+ instanceId,
497
+ taskId: context.taskId,
498
+ providerId: "claude-code",
499
+ status: "running",
500
+ startTime
501
+ };
502
+ } catch (error) {
503
+ if (tmpFile) {
504
+ try {
505
+ await unlink(tmpFile);
506
+ } catch {
507
+ }
508
+ }
509
+ return {
510
+ instanceId,
511
+ taskId: context.taskId,
512
+ providerId: "claude-code",
513
+ status: "failed",
514
+ startTime,
515
+ endTime: (/* @__PURE__ */ new Date()).toISOString()
516
+ };
517
+ }
518
+ }
519
+ /**
520
+ * List currently running Claude subagent processes.
521
+ *
522
+ * Checks each tracked process via kill(pid, 0) to verify it is still alive.
523
+ * Dead processes are automatically cleaned from the tracking map.
524
+ *
525
+ * @returns Array of spawn results for running processes
526
+ */
527
+ async listRunning() {
528
+ const running = [];
529
+ for (const [instanceId, tracked] of this.processMap.entries()) {
530
+ try {
531
+ process.kill(tracked.pid, 0);
532
+ running.push({
533
+ instanceId,
534
+ taskId: tracked.taskId,
535
+ providerId: "claude-code",
536
+ status: "running",
537
+ startTime: tracked.startTime
538
+ });
539
+ } catch {
540
+ this.processMap.delete(instanceId);
541
+ }
542
+ }
543
+ return running;
544
+ }
545
+ /**
546
+ * Terminate a running spawn by instance ID.
547
+ *
548
+ * Sends SIGTERM to the tracked process. If the process is not found
549
+ * or has already exited, this is a no-op.
550
+ *
551
+ * @param instanceId - ID of the spawn instance to terminate
552
+ */
553
+ async terminate(instanceId) {
554
+ const tracked = this.processMap.get(instanceId);
555
+ if (!tracked)
556
+ return;
557
+ try {
558
+ process.kill(tracked.pid, "SIGTERM");
559
+ } catch {
560
+ }
561
+ this.processMap.delete(instanceId);
562
+ }
563
+ };
564
+ }
565
+ });
566
+
567
+ // packages/adapters/src/providers/claude-code/task-sync.js
568
+ import { readFile, rm, stat } from "node:fs/promises";
569
+ import { join as join4 } from "node:path";
570
+ function parseTaskId(content) {
571
+ const match = content.match(/^\[T(\d+)\]/);
572
+ return match ? `T${match[1]}` : null;
573
+ }
574
+ function stripPrefixes(content) {
575
+ return content.replace(/^\[T\d+\]\s*/, "").replace(/^\[!\]\s*/, "").replace(/^\[BLOCKED\]\s*/, "");
576
+ }
577
+ function mapStatus(twStatus) {
578
+ switch (twStatus) {
579
+ case "completed":
580
+ return "completed";
581
+ case "in_progress":
582
+ return "active";
583
+ case "pending":
584
+ return "pending";
585
+ default:
586
+ return "pending";
587
+ }
588
+ }
589
+ function getTodoWriteFilePath(projectDir) {
590
+ return join4(projectDir, ".cleo", "sync", "todowrite-state.json");
591
+ }
592
+ var ClaudeCodeTaskSyncProvider;
593
+ var init_task_sync = __esm({
594
+ "packages/adapters/src/providers/claude-code/task-sync.js"() {
595
+ "use strict";
596
+ ClaudeCodeTaskSyncProvider = class {
597
+ customFilePath;
598
+ constructor(options) {
599
+ this.customFilePath = options?.filePath;
600
+ }
601
+ async getExternalTasks(projectDir) {
602
+ const filePath = this.customFilePath ?? getTodoWriteFilePath(projectDir);
603
+ try {
604
+ await stat(filePath);
605
+ } catch {
606
+ return [];
607
+ }
608
+ const raw = await readFile(filePath, "utf-8");
609
+ let state;
610
+ try {
611
+ state = JSON.parse(raw);
612
+ } catch {
613
+ return [];
614
+ }
615
+ if (!state.todos || !Array.isArray(state.todos)) {
616
+ return [];
617
+ }
618
+ const tasks = [];
619
+ let syntheticIndex = 0;
620
+ for (const item of state.todos) {
621
+ const cleoTaskId = parseTaskId(item.content);
622
+ const title = cleoTaskId ? stripPrefixes(item.content).trim() : item.content.trim();
623
+ if (!title)
624
+ continue;
625
+ tasks.push({
626
+ externalId: cleoTaskId ?? `tw-new-${syntheticIndex++}`,
627
+ cleoTaskId,
628
+ title,
629
+ status: mapStatus(item.status),
630
+ providerMeta: {
631
+ source: "todowrite",
632
+ activeForm: item.activeForm,
633
+ rawContent: item.content
634
+ }
635
+ });
636
+ }
637
+ return tasks;
638
+ }
639
+ async cleanup(projectDir) {
640
+ const filePath = this.customFilePath ?? getTodoWriteFilePath(projectDir);
641
+ try {
642
+ await rm(filePath);
643
+ } catch {
644
+ }
645
+ }
646
+ };
647
+ }
648
+ });
649
+
650
+ // packages/adapters/src/providers/claude-code/transport.js
651
+ var ClaudeCodeTransportProvider;
652
+ var init_transport = __esm({
653
+ "packages/adapters/src/providers/claude-code/transport.js"() {
654
+ "use strict";
655
+ ClaudeCodeTransportProvider = class {
656
+ transportName = "claude-code";
657
+ createTransport() {
658
+ return null;
659
+ }
660
+ };
661
+ }
662
+ });
663
+
664
+ // packages/adapters/src/providers/claude-code/adapter.js
665
+ import { exec as exec2 } from "node:child_process";
666
+ import { existsSync as existsSync3 } from "node:fs";
667
+ import { join as join5 } from "node:path";
668
+ import { homedir as homedir4 } from "node:os";
669
+ import { promisify as promisify2 } from "node:util";
670
+ var execAsync2, ClaudeCodeAdapter;
671
+ var init_adapter = __esm({
672
+ "packages/adapters/src/providers/claude-code/adapter.js"() {
673
+ "use strict";
674
+ init_context_monitor();
675
+ init_hooks();
676
+ init_install();
677
+ init_paths();
678
+ init_spawn();
679
+ init_task_sync();
680
+ init_transport();
681
+ execAsync2 = promisify2(exec2);
682
+ ClaudeCodeAdapter = class {
683
+ id = "claude-code";
684
+ name = "Claude Code";
685
+ version = "1.0.0";
686
+ capabilities = {
687
+ supportsHooks: true,
688
+ supportedHookEvents: [
689
+ "onSessionStart",
690
+ "onSessionEnd",
691
+ "onToolStart",
692
+ "onToolComplete",
693
+ "onError"
694
+ ],
695
+ supportsSpawn: true,
696
+ supportsInstall: true,
697
+ supportsMcp: true,
698
+ supportsInstructionFiles: true,
699
+ instructionFilePattern: "CLAUDE.md",
700
+ supportsContextMonitor: true,
701
+ supportsStatusline: true,
702
+ supportsProviderPaths: true,
703
+ supportsTransport: true,
704
+ supportsTaskSync: true
705
+ };
706
+ hooks;
707
+ spawn;
708
+ install;
709
+ paths;
710
+ contextMonitor;
711
+ transport;
712
+ taskSync;
713
+ projectDir = null;
714
+ initialized = false;
715
+ constructor() {
716
+ this.hooks = new ClaudeCodeHookProvider();
717
+ this.spawn = new ClaudeCodeSpawnProvider();
718
+ this.install = new ClaudeCodeInstallProvider();
719
+ this.paths = new ClaudeCodePathProvider();
720
+ this.contextMonitor = new ClaudeCodeContextMonitorProvider();
721
+ this.transport = new ClaudeCodeTransportProvider();
722
+ this.taskSync = new ClaudeCodeTaskSyncProvider();
723
+ }
724
+ /**
725
+ * Initialize the adapter for a given project directory.
726
+ *
727
+ * Validates the environment by checking for the Claude CLI
728
+ * and Claude Code configuration directory.
729
+ *
730
+ * @param projectDir - Root directory of the project
731
+ */
732
+ async initialize(projectDir) {
733
+ this.projectDir = projectDir;
734
+ this.initialized = true;
735
+ }
736
+ /**
737
+ * Dispose the adapter and clean up resources.
738
+ *
739
+ * Unregisters hooks and releases any tracked state.
740
+ */
741
+ async dispose() {
742
+ if (this.hooks.isRegistered()) {
743
+ await this.hooks.unregisterNativeHooks();
744
+ }
745
+ this.initialized = false;
746
+ this.projectDir = null;
747
+ }
748
+ /**
749
+ * Run a health check to verify Claude Code is accessible.
750
+ *
751
+ * Checks:
752
+ * 1. Adapter has been initialized
753
+ * 2. Claude CLI is available in PATH
754
+ * 3. ~/.claude/ configuration directory exists
755
+ *
756
+ * @returns Health status with details about each check
757
+ */
758
+ async healthCheck() {
759
+ const details = {};
760
+ if (!this.initialized) {
761
+ return {
762
+ healthy: false,
763
+ provider: this.id,
764
+ details: { error: "Adapter not initialized" }
765
+ };
766
+ }
767
+ let cliAvailable = false;
768
+ try {
769
+ const { stdout } = await execAsync2("which claude");
770
+ cliAvailable = stdout.trim().length > 0;
771
+ details.cliPath = stdout.trim();
772
+ } catch {
773
+ details.cliAvailable = false;
774
+ }
775
+ const claudeConfigDir = join5(homedir4(), ".claude");
776
+ const configExists = existsSync3(claudeConfigDir);
777
+ details.configDirExists = configExists;
778
+ const entrypointSet = process.env.CLAUDE_CODE_ENTRYPOINT !== void 0;
779
+ details.entrypointEnvSet = entrypointSet;
780
+ const healthy = cliAvailable;
781
+ details.cliAvailable = cliAvailable;
782
+ return {
783
+ healthy,
784
+ provider: this.id,
785
+ details
786
+ };
787
+ }
788
+ /**
789
+ * Check whether the adapter has been initialized.
790
+ */
791
+ isInitialized() {
792
+ return this.initialized;
793
+ }
794
+ /**
795
+ * Get the project directory this adapter was initialized with.
796
+ */
797
+ getProjectDir() {
798
+ return this.projectDir;
799
+ }
800
+ };
801
+ }
802
+ });
803
+
804
+ // packages/adapters/src/providers/claude-code/statusline.js
805
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
806
+ import { homedir as homedir5 } from "node:os";
807
+ import { join as join6 } from "node:path";
808
+ function getClaudeSettingsPath() {
809
+ return process.env["CLAUDE_SETTINGS"] ?? join6(process.env["CLAUDE_HOME"] ?? join6(homedir5(), ".claude"), "settings.json");
810
+ }
811
+ function checkStatuslineIntegration() {
812
+ const settingsPath = getClaudeSettingsPath();
813
+ if (!existsSync4(settingsPath))
814
+ return "no_settings";
815
+ try {
816
+ const settings = JSON.parse(readFileSync3(settingsPath, "utf-8"));
817
+ const statusLine = settings.statusLine;
818
+ if (!statusLine?.type)
819
+ return "not_configured";
820
+ if (statusLine.type !== "command")
821
+ return "custom_no_cleo";
822
+ const cmd = statusLine.command ?? "";
823
+ if (cmd.includes("context-monitor.sh") || cmd.includes("cleo-statusline") || cmd.includes(".context-state.json") || cmd.includes("context-states")) {
824
+ return "configured";
825
+ }
826
+ const scriptPath = cmd.startsWith("~") ? cmd.replace("~", homedir5()) : cmd;
827
+ if (existsSync4(scriptPath)) {
828
+ try {
829
+ const content = readFileSync3(scriptPath, "utf-8");
830
+ if (content.includes("context-state.json"))
831
+ return "configured";
832
+ } catch {
833
+ }
834
+ }
835
+ return "custom_no_cleo";
836
+ } catch {
837
+ return "no_settings";
838
+ }
839
+ }
840
+ function getStatuslineConfig(cleoHome) {
841
+ return {
842
+ statusLine: {
843
+ type: "command",
844
+ command: join6(cleoHome, "lib", "session", "context-monitor.sh")
845
+ }
846
+ };
847
+ }
848
+ function getSetupInstructions(cleoHome) {
849
+ const settingsPath = getClaudeSettingsPath();
850
+ return [
851
+ "To enable context monitoring, add to your Claude Code settings:",
852
+ `File: ${settingsPath}`,
853
+ "",
854
+ JSON.stringify(getStatuslineConfig(cleoHome), null, 2),
855
+ "",
856
+ "This enables real-time context window tracking in the CLI."
857
+ ].join("\n");
858
+ }
859
+ var init_statusline = __esm({
860
+ "packages/adapters/src/providers/claude-code/statusline.js"() {
861
+ "use strict";
862
+ }
863
+ });
864
+
865
+ // packages/adapters/src/providers/claude-code/index.js
866
+ var claude_code_exports = {};
867
+ __export(claude_code_exports, {
868
+ ClaudeCodeAdapter: () => ClaudeCodeAdapter,
869
+ ClaudeCodeContextMonitorProvider: () => ClaudeCodeContextMonitorProvider,
870
+ ClaudeCodeHookProvider: () => ClaudeCodeHookProvider,
871
+ ClaudeCodeInstallProvider: () => ClaudeCodeInstallProvider,
872
+ ClaudeCodePathProvider: () => ClaudeCodePathProvider,
873
+ ClaudeCodeSpawnProvider: () => ClaudeCodeSpawnProvider,
874
+ ClaudeCodeTransportProvider: () => ClaudeCodeTransportProvider,
875
+ checkStatuslineIntegration: () => checkStatuslineIntegration,
876
+ createAdapter: () => createAdapter,
877
+ default: () => claude_code_default,
878
+ getSetupInstructions: () => getSetupInstructions,
879
+ getStatuslineConfig: () => getStatuslineConfig
880
+ });
881
+ function createAdapter() {
882
+ return new ClaudeCodeAdapter();
883
+ }
884
+ var claude_code_default;
885
+ var init_claude_code = __esm({
886
+ "packages/adapters/src/providers/claude-code/index.js"() {
887
+ "use strict";
888
+ init_adapter();
889
+ init_adapter();
890
+ init_context_monitor();
891
+ init_hooks();
892
+ init_install();
893
+ init_paths();
894
+ init_spawn();
895
+ init_transport();
896
+ init_statusline();
897
+ claude_code_default = ClaudeCodeAdapter;
898
+ }
899
+ });
900
+
901
+ // packages/adapters/src/providers/opencode/hooks.js
902
+ var OPENCODE_EVENT_MAP, OpenCodeHookProvider;
903
+ var init_hooks2 = __esm({
904
+ "packages/adapters/src/providers/opencode/hooks.js"() {
905
+ "use strict";
906
+ OPENCODE_EVENT_MAP = {
907
+ "session.start": "onSessionStart",
908
+ "session.end": "onSessionEnd",
909
+ "tool.start": "onToolStart",
910
+ "tool.complete": "onToolComplete",
911
+ "error": "onError",
912
+ "prompt.submit": "onPromptSubmit"
913
+ };
914
+ OpenCodeHookProvider = class {
915
+ registered = false;
916
+ /**
917
+ * Map an OpenCode native event name to a CAAMP hook event name.
918
+ *
919
+ * @param providerEvent - OpenCode event name (e.g. "session.start", "tool.complete")
920
+ * @returns CAAMP event name or null if unmapped
921
+ */
922
+ mapProviderEvent(providerEvent) {
923
+ return OPENCODE_EVENT_MAP[providerEvent] ?? null;
924
+ }
925
+ /**
926
+ * Register native hooks for a project.
927
+ *
928
+ * For OpenCode, hooks are registered via the config system
929
+ * (.opencode/config.json), which is handled by the install provider.
930
+ * This method marks hooks as registered without performing
931
+ * filesystem operations.
932
+ *
933
+ * @param _projectDir - Project directory (unused; config manages registration)
934
+ */
935
+ async registerNativeHooks(_projectDir) {
936
+ this.registered = true;
937
+ }
938
+ /**
939
+ * Unregister native hooks.
940
+ *
941
+ * For OpenCode, this is a no-op since hooks are managed through
942
+ * the config system. Unregistration happens via the install
943
+ * provider's uninstall method.
944
+ */
945
+ async unregisterNativeHooks() {
946
+ this.registered = false;
947
+ }
948
+ /**
949
+ * Check whether hooks have been registered via registerNativeHooks.
950
+ */
951
+ isRegistered() {
952
+ return this.registered;
953
+ }
954
+ /**
955
+ * Get the full event mapping for introspection/debugging.
956
+ */
957
+ getEventMap() {
958
+ return { ...OPENCODE_EVENT_MAP };
959
+ }
960
+ };
961
+ }
962
+ });
963
+
964
+ // packages/adapters/src/providers/opencode/install.js
965
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
966
+ import { join as join7 } from "node:path";
967
+ var INSTRUCTION_REFERENCES2, MCP_SERVER_KEY2, OpenCodeInstallProvider;
968
+ var init_install2 = __esm({
969
+ "packages/adapters/src/providers/opencode/install.js"() {
970
+ "use strict";
971
+ INSTRUCTION_REFERENCES2 = [
972
+ "@~/.cleo/templates/CLEO-INJECTION.md",
973
+ "@.cleo/memory-bridge.md"
974
+ ];
975
+ MCP_SERVER_KEY2 = "cleo";
976
+ OpenCodeInstallProvider = class {
977
+ installedProjectDir = null;
978
+ /**
979
+ * Install CLEO into an OpenCode project.
980
+ *
981
+ * @param options - Installation options including project directory and MCP server path
982
+ * @returns Result describing what was installed
983
+ */
984
+ async install(options) {
985
+ const { projectDir, mcpServerPath } = options;
986
+ const installedAt = (/* @__PURE__ */ new Date()).toISOString();
987
+ let instructionFileUpdated = false;
988
+ let mcpRegistered = false;
989
+ const details = {};
990
+ if (mcpServerPath) {
991
+ mcpRegistered = this.registerMcpServer(projectDir, mcpServerPath);
992
+ if (mcpRegistered) {
993
+ details.mcpConfigPath = join7(projectDir, ".opencode", "config.json");
994
+ }
995
+ }
996
+ instructionFileUpdated = this.updateInstructionFile(projectDir);
997
+ if (instructionFileUpdated) {
998
+ details.instructionFile = join7(projectDir, "AGENTS.md");
999
+ }
1000
+ this.installedProjectDir = projectDir;
1001
+ return {
1002
+ success: true,
1003
+ installedAt,
1004
+ instructionFileUpdated,
1005
+ mcpRegistered,
1006
+ details
1007
+ };
1008
+ }
1009
+ /**
1010
+ * Uninstall CLEO from the current OpenCode project.
1011
+ *
1012
+ * Removes the MCP server registration from .opencode/config.json.
1013
+ * Does not remove AGENTS.md references (they are harmless if CLEO is not present).
1014
+ */
1015
+ async uninstall() {
1016
+ if (!this.installedProjectDir)
1017
+ return;
1018
+ const configPath = join7(this.installedProjectDir, ".opencode", "config.json");
1019
+ if (existsSync5(configPath)) {
1020
+ try {
1021
+ const raw = readFileSync4(configPath, "utf-8");
1022
+ const config = JSON.parse(raw);
1023
+ const mcpServers = config.mcpServers;
1024
+ if (mcpServers && MCP_SERVER_KEY2 in mcpServers) {
1025
+ delete mcpServers[MCP_SERVER_KEY2];
1026
+ writeFileSync3(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
1027
+ }
1028
+ } catch {
1029
+ }
1030
+ }
1031
+ this.installedProjectDir = null;
1032
+ }
1033
+ /**
1034
+ * Check whether CLEO is installed in the current environment.
1035
+ *
1036
+ * Checks for MCP server registered in .opencode/config.json.
1037
+ * Returns true if the CLEO MCP server entry is found.
1038
+ */
1039
+ async isInstalled() {
1040
+ const configPath = join7(process.cwd(), ".opencode", "config.json");
1041
+ if (existsSync5(configPath)) {
1042
+ try {
1043
+ const config = JSON.parse(readFileSync4(configPath, "utf-8"));
1044
+ const mcpServers = config.mcpServers;
1045
+ if (mcpServers && MCP_SERVER_KEY2 in mcpServers) {
1046
+ return true;
1047
+ }
1048
+ } catch {
1049
+ }
1050
+ }
1051
+ return false;
1052
+ }
1053
+ /**
1054
+ * Ensure AGENTS.md contains @-references to CLEO instruction files.
1055
+ *
1056
+ * Creates AGENTS.md if it does not exist. Appends any missing references.
1057
+ *
1058
+ * @param projectDir - Project root directory
1059
+ */
1060
+ async ensureInstructionReferences(projectDir) {
1061
+ this.updateInstructionFile(projectDir);
1062
+ }
1063
+ /**
1064
+ * Register the CLEO MCP server in .opencode/config.json.
1065
+ *
1066
+ * OpenCode stores its MCP server configuration in .opencode/config.json
1067
+ * under the mcpServers key.
1068
+ *
1069
+ * @returns true if registration was performed or updated
1070
+ */
1071
+ registerMcpServer(projectDir, mcpServerPath) {
1072
+ const openCodeDir = join7(projectDir, ".opencode");
1073
+ const configPath = join7(openCodeDir, "config.json");
1074
+ let config = {};
1075
+ mkdirSync2(openCodeDir, { recursive: true });
1076
+ if (existsSync5(configPath)) {
1077
+ try {
1078
+ config = JSON.parse(readFileSync4(configPath, "utf-8"));
1079
+ } catch {
1080
+ }
1081
+ }
1082
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
1083
+ config.mcpServers = {};
1084
+ }
1085
+ const mcpServers = config.mcpServers;
1086
+ mcpServers[MCP_SERVER_KEY2] = {
1087
+ command: "node",
1088
+ args: [mcpServerPath]
1089
+ };
1090
+ writeFileSync3(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
1091
+ return true;
1092
+ }
1093
+ /**
1094
+ * Update AGENTS.md with CLEO @-references.
1095
+ *
1096
+ * @returns true if the file was created or modified
1097
+ */
1098
+ updateInstructionFile(projectDir) {
1099
+ const agentsMdPath = join7(projectDir, "AGENTS.md");
1100
+ let content = "";
1101
+ let existed = false;
1102
+ if (existsSync5(agentsMdPath)) {
1103
+ content = readFileSync4(agentsMdPath, "utf-8");
1104
+ existed = true;
1105
+ }
1106
+ const missingRefs = INSTRUCTION_REFERENCES2.filter((ref) => !content.includes(ref));
1107
+ if (missingRefs.length === 0) {
1108
+ return false;
1109
+ }
1110
+ const refsBlock = missingRefs.join("\n");
1111
+ if (existed) {
1112
+ const separator = content.endsWith("\n") ? "" : "\n";
1113
+ content = content + separator + refsBlock + "\n";
1114
+ } else {
1115
+ content = refsBlock + "\n";
1116
+ }
1117
+ writeFileSync3(agentsMdPath, content, "utf-8");
1118
+ return true;
1119
+ }
1120
+ };
1121
+ }
1122
+ });
1123
+
1124
+ // packages/adapters/src/providers/opencode/spawn.js
1125
+ import { exec as exec3, spawn as nodeSpawn2 } from "node:child_process";
1126
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
1127
+ import { join as join8 } from "node:path";
1128
+ import { promisify as promisify3 } from "node:util";
1129
+ function buildOpenCodeAgentMarkdown(description, instructions) {
1130
+ const normalizedDesc = description.replace(/\s+/g, " ").trim();
1131
+ return [
1132
+ "---",
1133
+ `description: ${JSON.stringify(normalizedDesc)}`,
1134
+ "mode: subagent",
1135
+ "hidden: true",
1136
+ "---",
1137
+ "",
1138
+ instructions.trim(),
1139
+ ""
1140
+ ].join("\n");
1141
+ }
1142
+ async function ensureSubagentDefinition(workingDirectory) {
1143
+ const agentDir = join8(workingDirectory, ".opencode", "agent");
1144
+ const agentPath = join8(agentDir, `${OPENCODE_SUBAGENT_NAME}.md`);
1145
+ const description = "CLEO task executor with protocol compliance.";
1146
+ const instructions = [
1147
+ "# CLEO Subagent",
1148
+ "",
1149
+ "You are a CLEO subagent executing a delegated task.",
1150
+ "Follow the CLEO protocol and complete the assigned work.",
1151
+ "",
1152
+ "@~/.cleo/templates/CLEO-INJECTION.md"
1153
+ ].join("\n");
1154
+ const content = buildOpenCodeAgentMarkdown(description, instructions);
1155
+ await mkdir2(agentDir, { recursive: true });
1156
+ let existing = null;
1157
+ try {
1158
+ existing = await readFile2(agentPath, "utf-8");
1159
+ } catch {
1160
+ existing = null;
1161
+ }
1162
+ if (existing !== content) {
1163
+ await writeFile2(agentPath, content, "utf-8");
1164
+ }
1165
+ return OPENCODE_SUBAGENT_NAME;
1166
+ }
1167
+ var execAsync3, OPENCODE_SUBAGENT_NAME, OPENCODE_FALLBACK_AGENT, OpenCodeSpawnProvider;
1168
+ var init_spawn2 = __esm({
1169
+ "packages/adapters/src/providers/opencode/spawn.js"() {
1170
+ "use strict";
1171
+ execAsync3 = promisify3(exec3);
1172
+ OPENCODE_SUBAGENT_NAME = "cleo-subagent";
1173
+ OPENCODE_FALLBACK_AGENT = "general";
1174
+ OpenCodeSpawnProvider = class {
1175
+ /** Map of instance IDs to tracked process info. */
1176
+ processMap = /* @__PURE__ */ new Map();
1177
+ /**
1178
+ * Check if the OpenCode CLI is available in PATH.
1179
+ *
1180
+ * @returns true if `opencode` is found via `which`
1181
+ */
1182
+ async canSpawn() {
1183
+ try {
1184
+ await execAsync3("which opencode");
1185
+ return true;
1186
+ } catch {
1187
+ return false;
1188
+ }
1189
+ }
1190
+ /**
1191
+ * Spawn a subagent via OpenCode CLI.
1192
+ *
1193
+ * Ensures the CLEO subagent definition exists in the project's
1194
+ * .opencode/agent/ directory, then spawns a detached OpenCode
1195
+ * process. The process runs independently of the parent.
1196
+ *
1197
+ * @param context - Spawn context with taskId, prompt, and options
1198
+ * @returns Spawn result with instance ID and status
1199
+ */
1200
+ async spawn(context) {
1201
+ const instanceId = `opencode-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
1202
+ const startTime = (/* @__PURE__ */ new Date()).toISOString();
1203
+ const workingDirectory = context.workingDirectory ?? process.cwd();
1204
+ try {
1205
+ let agentName;
1206
+ try {
1207
+ agentName = await ensureSubagentDefinition(workingDirectory);
1208
+ } catch {
1209
+ agentName = OPENCODE_FALLBACK_AGENT;
1210
+ }
1211
+ const child = nodeSpawn2("opencode", [
1212
+ "run",
1213
+ "--format",
1214
+ "json",
1215
+ "--agent",
1216
+ agentName,
1217
+ "--title",
1218
+ `CLEO ${context.taskId}`,
1219
+ context.prompt
1220
+ ], {
1221
+ cwd: workingDirectory,
1222
+ detached: true,
1223
+ stdio: "ignore"
1224
+ });
1225
+ child.unref();
1226
+ if (child.pid) {
1227
+ this.processMap.set(instanceId, {
1228
+ pid: child.pid,
1229
+ taskId: context.taskId,
1230
+ startTime
1231
+ });
1232
+ }
1233
+ child.on("exit", () => {
1234
+ this.processMap.delete(instanceId);
1235
+ });
1236
+ return {
1237
+ instanceId,
1238
+ taskId: context.taskId,
1239
+ providerId: "opencode",
1240
+ status: "running",
1241
+ startTime
1242
+ };
1243
+ } catch {
1244
+ return {
1245
+ instanceId,
1246
+ taskId: context.taskId,
1247
+ providerId: "opencode",
1248
+ status: "failed",
1249
+ startTime,
1250
+ endTime: (/* @__PURE__ */ new Date()).toISOString()
1251
+ };
1252
+ }
1253
+ }
1254
+ /**
1255
+ * List currently running OpenCode subagent processes.
1256
+ *
1257
+ * Checks each tracked process via kill(pid, 0) to verify it is still alive.
1258
+ * Dead processes are automatically cleaned from the tracking map.
1259
+ *
1260
+ * @returns Array of spawn results for running processes
1261
+ */
1262
+ async listRunning() {
1263
+ const running = [];
1264
+ for (const [instanceId, tracked] of this.processMap.entries()) {
1265
+ try {
1266
+ process.kill(tracked.pid, 0);
1267
+ running.push({
1268
+ instanceId,
1269
+ taskId: tracked.taskId,
1270
+ providerId: "opencode",
1271
+ status: "running",
1272
+ startTime: tracked.startTime
1273
+ });
1274
+ } catch {
1275
+ this.processMap.delete(instanceId);
1276
+ }
1277
+ }
1278
+ return running;
1279
+ }
1280
+ /**
1281
+ * Terminate a running spawn by instance ID.
1282
+ *
1283
+ * Sends SIGTERM to the tracked process. If the process is not found
1284
+ * or has already exited, this is a no-op.
1285
+ *
1286
+ * @param instanceId - ID of the spawn instance to terminate
1287
+ */
1288
+ async terminate(instanceId) {
1289
+ const tracked = this.processMap.get(instanceId);
1290
+ if (!tracked)
1291
+ return;
1292
+ try {
1293
+ process.kill(tracked.pid, "SIGTERM");
1294
+ } catch {
1295
+ }
1296
+ this.processMap.delete(instanceId);
1297
+ }
1298
+ };
1299
+ }
1300
+ });
1301
+
1302
+ // packages/adapters/src/providers/opencode/adapter.js
1303
+ import { exec as exec4 } from "node:child_process";
1304
+ import { existsSync as existsSync6 } from "node:fs";
1305
+ import { join as join9 } from "node:path";
1306
+ import { promisify as promisify4 } from "node:util";
1307
+ var execAsync4, OpenCodeAdapter;
1308
+ var init_adapter2 = __esm({
1309
+ "packages/adapters/src/providers/opencode/adapter.js"() {
1310
+ "use strict";
1311
+ init_hooks2();
1312
+ init_install2();
1313
+ init_spawn2();
1314
+ execAsync4 = promisify4(exec4);
1315
+ OpenCodeAdapter = class {
1316
+ id = "opencode";
1317
+ name = "OpenCode";
1318
+ version = "1.0.0";
1319
+ capabilities = {
1320
+ supportsHooks: true,
1321
+ supportedHookEvents: [
1322
+ "onSessionStart",
1323
+ "onSessionEnd",
1324
+ "onToolStart",
1325
+ "onToolComplete",
1326
+ "onError",
1327
+ "onPromptSubmit"
1328
+ ],
1329
+ supportsSpawn: true,
1330
+ supportsInstall: true,
1331
+ supportsMcp: true,
1332
+ supportsInstructionFiles: true,
1333
+ instructionFilePattern: "AGENTS.md",
1334
+ supportsContextMonitor: false,
1335
+ supportsStatusline: false,
1336
+ supportsProviderPaths: true,
1337
+ supportsTransport: false,
1338
+ supportsTaskSync: false
1339
+ };
1340
+ hooks;
1341
+ spawn;
1342
+ install;
1343
+ projectDir = null;
1344
+ initialized = false;
1345
+ constructor() {
1346
+ this.hooks = new OpenCodeHookProvider();
1347
+ this.spawn = new OpenCodeSpawnProvider();
1348
+ this.install = new OpenCodeInstallProvider();
1349
+ }
1350
+ /**
1351
+ * Initialize the adapter for a given project directory.
1352
+ *
1353
+ * Validates the environment by checking for the OpenCode CLI
1354
+ * and OpenCode configuration directory.
1355
+ *
1356
+ * @param projectDir - Root directory of the project
1357
+ */
1358
+ async initialize(projectDir) {
1359
+ this.projectDir = projectDir;
1360
+ this.initialized = true;
1361
+ }
1362
+ /**
1363
+ * Dispose the adapter and clean up resources.
1364
+ *
1365
+ * Unregisters hooks and releases any tracked state.
1366
+ */
1367
+ async dispose() {
1368
+ if (this.hooks.isRegistered()) {
1369
+ await this.hooks.unregisterNativeHooks();
1370
+ }
1371
+ this.initialized = false;
1372
+ this.projectDir = null;
1373
+ }
1374
+ /**
1375
+ * Run a health check to verify OpenCode is accessible.
1376
+ *
1377
+ * Checks:
1378
+ * 1. Adapter has been initialized
1379
+ * 2. OpenCode CLI is available in PATH
1380
+ * 3. .opencode/ configuration directory exists in the project
1381
+ *
1382
+ * @returns Health status with details about each check
1383
+ */
1384
+ async healthCheck() {
1385
+ const details = {};
1386
+ if (!this.initialized) {
1387
+ return {
1388
+ healthy: false,
1389
+ provider: this.id,
1390
+ details: { error: "Adapter not initialized" }
1391
+ };
1392
+ }
1393
+ let cliAvailable = false;
1394
+ try {
1395
+ const { stdout } = await execAsync4("which opencode");
1396
+ cliAvailable = stdout.trim().length > 0;
1397
+ details.cliPath = stdout.trim();
1398
+ } catch {
1399
+ details.cliAvailable = false;
1400
+ }
1401
+ if (this.projectDir) {
1402
+ const openCodeConfigDir = join9(this.projectDir, ".opencode");
1403
+ const configExists = existsSync6(openCodeConfigDir);
1404
+ details.configDirExists = configExists;
1405
+ }
1406
+ const versionEnvSet = process.env.OPENCODE_VERSION !== void 0;
1407
+ details.versionEnvSet = versionEnvSet;
1408
+ const healthy = cliAvailable;
1409
+ details.cliAvailable = cliAvailable;
1410
+ return {
1411
+ healthy,
1412
+ provider: this.id,
1413
+ details
1414
+ };
1415
+ }
1416
+ /**
1417
+ * Check whether the adapter has been initialized.
1418
+ */
1419
+ isInitialized() {
1420
+ return this.initialized;
1421
+ }
1422
+ /**
1423
+ * Get the project directory this adapter was initialized with.
1424
+ */
1425
+ getProjectDir() {
1426
+ return this.projectDir;
1427
+ }
1428
+ };
1429
+ }
1430
+ });
1431
+
1432
+ // packages/adapters/src/providers/opencode/index.js
1433
+ var opencode_exports = {};
1434
+ __export(opencode_exports, {
1435
+ OpenCodeAdapter: () => OpenCodeAdapter,
1436
+ OpenCodeHookProvider: () => OpenCodeHookProvider,
1437
+ OpenCodeInstallProvider: () => OpenCodeInstallProvider,
1438
+ OpenCodeSpawnProvider: () => OpenCodeSpawnProvider,
1439
+ createAdapter: () => createAdapter2,
1440
+ default: () => opencode_default
1441
+ });
1442
+ function createAdapter2() {
1443
+ return new OpenCodeAdapter();
1444
+ }
1445
+ var opencode_default;
1446
+ var init_opencode = __esm({
1447
+ "packages/adapters/src/providers/opencode/index.js"() {
1448
+ "use strict";
1449
+ init_adapter2();
1450
+ init_adapter2();
1451
+ init_hooks2();
1452
+ init_spawn2();
1453
+ init_install2();
1454
+ opencode_default = OpenCodeAdapter;
1455
+ }
1456
+ });
1457
+
1458
+ // packages/adapters/src/providers/cursor/hooks.js
1459
+ var CursorHookProvider;
1460
+ var init_hooks3 = __esm({
1461
+ "packages/adapters/src/providers/cursor/hooks.js"() {
1462
+ "use strict";
1463
+ CursorHookProvider = class {
1464
+ registered = false;
1465
+ /**
1466
+ * Map a provider event name to a CAAMP hook event name.
1467
+ *
1468
+ * Always returns null since Cursor does not emit hook events.
1469
+ *
1470
+ * @param _providerEvent - Ignored; Cursor has no hook events
1471
+ * @returns null (no mapping available)
1472
+ */
1473
+ mapProviderEvent(_providerEvent) {
1474
+ return null;
1475
+ }
1476
+ /**
1477
+ * Register native hooks for a project.
1478
+ *
1479
+ * No-op for Cursor since it has no hook system.
1480
+ *
1481
+ * @param _projectDir - Ignored
1482
+ */
1483
+ async registerNativeHooks(_projectDir) {
1484
+ this.registered = true;
1485
+ }
1486
+ /**
1487
+ * Unregister native hooks.
1488
+ *
1489
+ * No-op for Cursor since it has no hook system.
1490
+ */
1491
+ async unregisterNativeHooks() {
1492
+ this.registered = false;
1493
+ }
1494
+ /**
1495
+ * Check whether hooks have been registered.
1496
+ */
1497
+ isRegistered() {
1498
+ return this.registered;
1499
+ }
1500
+ };
1501
+ }
1502
+ });
1503
+
1504
+ // packages/adapters/src/providers/cursor/install.js
1505
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
1506
+ import { join as join10 } from "node:path";
1507
+ var INSTRUCTION_REFERENCES3, MCP_SERVER_KEY3, CursorInstallProvider;
1508
+ var init_install3 = __esm({
1509
+ "packages/adapters/src/providers/cursor/install.js"() {
1510
+ "use strict";
1511
+ INSTRUCTION_REFERENCES3 = [
1512
+ "@~/.cleo/templates/CLEO-INJECTION.md",
1513
+ "@.cleo/memory-bridge.md"
1514
+ ];
1515
+ MCP_SERVER_KEY3 = "cleo";
1516
+ CursorInstallProvider = class {
1517
+ installedProjectDir = null;
1518
+ /**
1519
+ * Install CLEO into a Cursor project.
1520
+ *
1521
+ * @param options - Installation options including project directory and MCP server path
1522
+ * @returns Result describing what was installed
1523
+ */
1524
+ async install(options) {
1525
+ const { projectDir, mcpServerPath } = options;
1526
+ const installedAt = (/* @__PURE__ */ new Date()).toISOString();
1527
+ let instructionFileUpdated = false;
1528
+ let mcpRegistered = false;
1529
+ const details = {};
1530
+ if (mcpServerPath) {
1531
+ mcpRegistered = this.registerMcpServer(projectDir, mcpServerPath);
1532
+ if (mcpRegistered) {
1533
+ details.mcpConfigPath = join10(projectDir, ".cursor", "mcp.json");
1534
+ }
1535
+ }
1536
+ instructionFileUpdated = this.updateInstructionFiles(projectDir);
1537
+ if (instructionFileUpdated) {
1538
+ details.instructionFiles = this.getUpdatedFileList(projectDir);
1539
+ }
1540
+ this.installedProjectDir = projectDir;
1541
+ return {
1542
+ success: true,
1543
+ installedAt,
1544
+ instructionFileUpdated,
1545
+ mcpRegistered,
1546
+ details
1547
+ };
1548
+ }
1549
+ /**
1550
+ * Uninstall CLEO from the current Cursor project.
1551
+ *
1552
+ * Removes the MCP server registration from .cursor/mcp.json.
1553
+ * Does not remove instruction file references (they are harmless if CLEO is not present).
1554
+ */
1555
+ async uninstall() {
1556
+ if (!this.installedProjectDir)
1557
+ return;
1558
+ const mcpPath = join10(this.installedProjectDir, ".cursor", "mcp.json");
1559
+ if (existsSync7(mcpPath)) {
1560
+ try {
1561
+ const raw = readFileSync5(mcpPath, "utf-8");
1562
+ const config = JSON.parse(raw);
1563
+ const mcpServers = config.mcpServers;
1564
+ if (mcpServers && MCP_SERVER_KEY3 in mcpServers) {
1565
+ delete mcpServers[MCP_SERVER_KEY3];
1566
+ writeFileSync4(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
1567
+ }
1568
+ } catch {
1569
+ }
1570
+ }
1571
+ this.installedProjectDir = null;
1572
+ }
1573
+ /**
1574
+ * Check whether CLEO is installed in the current environment.
1575
+ *
1576
+ * Checks for MCP server registered in .cursor/mcp.json.
1577
+ */
1578
+ async isInstalled() {
1579
+ const mcpPath = join10(process.cwd(), ".cursor", "mcp.json");
1580
+ if (existsSync7(mcpPath)) {
1581
+ try {
1582
+ const config = JSON.parse(readFileSync5(mcpPath, "utf-8"));
1583
+ const mcpServers = config.mcpServers;
1584
+ if (mcpServers && MCP_SERVER_KEY3 in mcpServers) {
1585
+ return true;
1586
+ }
1587
+ } catch {
1588
+ }
1589
+ }
1590
+ return false;
1591
+ }
1592
+ /**
1593
+ * Ensure instruction files contain @-references to CLEO.
1594
+ *
1595
+ * Updates .cursorrules (legacy) and creates .cursor/rules/cleo.mdc (modern).
1596
+ *
1597
+ * @param projectDir - Project root directory
1598
+ */
1599
+ async ensureInstructionReferences(projectDir) {
1600
+ this.updateInstructionFiles(projectDir);
1601
+ }
1602
+ /**
1603
+ * Register the CLEO MCP server in .cursor/mcp.json.
1604
+ *
1605
+ * Cursor stores MCP server configuration in .cursor/mcp.json
1606
+ * under the mcpServers key.
1607
+ *
1608
+ * @returns true if registration was performed or updated
1609
+ */
1610
+ registerMcpServer(projectDir, mcpServerPath) {
1611
+ const cursorDir = join10(projectDir, ".cursor");
1612
+ const mcpPath = join10(cursorDir, "mcp.json");
1613
+ let config = {};
1614
+ mkdirSync3(cursorDir, { recursive: true });
1615
+ if (existsSync7(mcpPath)) {
1616
+ try {
1617
+ config = JSON.parse(readFileSync5(mcpPath, "utf-8"));
1618
+ } catch {
1619
+ }
1620
+ }
1621
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
1622
+ config.mcpServers = {};
1623
+ }
1624
+ const mcpServers = config.mcpServers;
1625
+ mcpServers[MCP_SERVER_KEY3] = {
1626
+ command: "node",
1627
+ args: [mcpServerPath]
1628
+ };
1629
+ writeFileSync4(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
1630
+ return true;
1631
+ }
1632
+ /**
1633
+ * Update instruction files with CLEO @-references.
1634
+ *
1635
+ * Handles both legacy (.cursorrules) and modern (.cursor/rules/cleo.mdc) formats.
1636
+ *
1637
+ * @returns true if any file was created or modified
1638
+ */
1639
+ updateInstructionFiles(projectDir) {
1640
+ let updated = false;
1641
+ if (this.updateLegacyRules(projectDir)) {
1642
+ updated = true;
1643
+ }
1644
+ if (this.updateModernRules(projectDir)) {
1645
+ updated = true;
1646
+ }
1647
+ return updated;
1648
+ }
1649
+ /**
1650
+ * Update legacy .cursorrules file with @-references.
1651
+ * Only modifies the file if it already exists (does not create it).
1652
+ *
1653
+ * @returns true if the file was modified
1654
+ */
1655
+ updateLegacyRules(projectDir) {
1656
+ const rulesPath = join10(projectDir, ".cursorrules");
1657
+ if (!existsSync7(rulesPath)) {
1658
+ return false;
1659
+ }
1660
+ let content = readFileSync5(rulesPath, "utf-8");
1661
+ const missingRefs = INSTRUCTION_REFERENCES3.filter((ref) => !content.includes(ref));
1662
+ if (missingRefs.length === 0) {
1663
+ return false;
1664
+ }
1665
+ const separator = content.endsWith("\n") ? "" : "\n";
1666
+ content = content + separator + missingRefs.join("\n") + "\n";
1667
+ writeFileSync4(rulesPath, content, "utf-8");
1668
+ return true;
1669
+ }
1670
+ /**
1671
+ * Create or update .cursor/rules/cleo.mdc with CLEO references.
1672
+ *
1673
+ * MDC (Markdown Component) format is Cursor's modern rule file format.
1674
+ * Each .mdc file in .cursor/rules/ is loaded as a rule set.
1675
+ *
1676
+ * @returns true if the file was created or modified
1677
+ */
1678
+ updateModernRules(projectDir) {
1679
+ const rulesDir = join10(projectDir, ".cursor", "rules");
1680
+ const mdcPath = join10(rulesDir, "cleo.mdc");
1681
+ const expectedContent = [
1682
+ "---",
1683
+ "description: CLEO task management protocol references",
1684
+ 'globs: "**/*"',
1685
+ "alwaysApply: true",
1686
+ "---",
1687
+ "",
1688
+ ...INSTRUCTION_REFERENCES3,
1689
+ ""
1690
+ ].join("\n");
1691
+ if (existsSync7(mdcPath)) {
1692
+ const existing = readFileSync5(mdcPath, "utf-8");
1693
+ if (existing === expectedContent) {
1694
+ return false;
1695
+ }
1696
+ }
1697
+ mkdirSync3(rulesDir, { recursive: true });
1698
+ writeFileSync4(mdcPath, expectedContent, "utf-8");
1699
+ return true;
1700
+ }
1701
+ /**
1702
+ * Get list of instruction files that were updated.
1703
+ */
1704
+ getUpdatedFileList(projectDir) {
1705
+ const files = [];
1706
+ if (existsSync7(join10(projectDir, ".cursorrules"))) {
1707
+ files.push(join10(projectDir, ".cursorrules"));
1708
+ }
1709
+ files.push(join10(projectDir, ".cursor", "rules", "cleo.mdc"));
1710
+ return files;
1711
+ }
1712
+ };
1713
+ }
1714
+ });
1715
+
1716
+ // packages/adapters/src/providers/cursor/adapter.js
1717
+ import { existsSync as existsSync8 } from "node:fs";
1718
+ import { join as join11 } from "node:path";
1719
+ var CursorAdapter;
1720
+ var init_adapter3 = __esm({
1721
+ "packages/adapters/src/providers/cursor/adapter.js"() {
1722
+ "use strict";
1723
+ init_hooks3();
1724
+ init_install3();
1725
+ CursorAdapter = class {
1726
+ id = "cursor";
1727
+ name = "Cursor";
1728
+ version = "1.0.0";
1729
+ capabilities = {
1730
+ supportsHooks: false,
1731
+ supportedHookEvents: [],
1732
+ supportsSpawn: false,
1733
+ supportsInstall: true,
1734
+ supportsMcp: true,
1735
+ supportsInstructionFiles: true,
1736
+ instructionFilePattern: ".cursor/rules/*.mdc",
1737
+ supportsContextMonitor: false,
1738
+ supportsStatusline: false,
1739
+ supportsProviderPaths: true,
1740
+ supportsTransport: false,
1741
+ supportsTaskSync: false
1742
+ };
1743
+ hooks;
1744
+ install;
1745
+ projectDir = null;
1746
+ initialized = false;
1747
+ constructor() {
1748
+ this.hooks = new CursorHookProvider();
1749
+ this.install = new CursorInstallProvider();
1750
+ }
1751
+ /**
1752
+ * Initialize the adapter for a given project directory.
1753
+ *
1754
+ * @param projectDir - Root directory of the project
1755
+ */
1756
+ async initialize(projectDir) {
1757
+ this.projectDir = projectDir;
1758
+ this.initialized = true;
1759
+ }
1760
+ /**
1761
+ * Dispose the adapter and clean up resources.
1762
+ */
1763
+ async dispose() {
1764
+ if (this.hooks.isRegistered()) {
1765
+ await this.hooks.unregisterNativeHooks();
1766
+ }
1767
+ this.initialized = false;
1768
+ this.projectDir = null;
1769
+ }
1770
+ /**
1771
+ * Run a health check to verify Cursor is accessible.
1772
+ *
1773
+ * Checks:
1774
+ * 1. Adapter has been initialized
1775
+ * 2. .cursor/ configuration directory exists in the project
1776
+ * 3. CURSOR_EDITOR env var is set
1777
+ *
1778
+ * @returns Health status with details about each check
1779
+ */
1780
+ async healthCheck() {
1781
+ const details = {};
1782
+ if (!this.initialized) {
1783
+ return {
1784
+ healthy: false,
1785
+ provider: this.id,
1786
+ details: { error: "Adapter not initialized" }
1787
+ };
1788
+ }
1789
+ let configExists = false;
1790
+ if (this.projectDir) {
1791
+ const cursorConfigDir = join11(this.projectDir, ".cursor");
1792
+ configExists = existsSync8(cursorConfigDir);
1793
+ details.configDirExists = configExists;
1794
+ }
1795
+ const editorEnvSet = process.env.CURSOR_EDITOR !== void 0;
1796
+ details.editorEnvSet = editorEnvSet;
1797
+ if (this.projectDir) {
1798
+ const legacyRulesExist = existsSync8(join11(this.projectDir, ".cursorrules"));
1799
+ details.legacyRulesExist = legacyRulesExist;
1800
+ }
1801
+ const healthy = configExists || editorEnvSet;
1802
+ details.detected = healthy;
1803
+ return {
1804
+ healthy,
1805
+ provider: this.id,
1806
+ details
1807
+ };
1808
+ }
1809
+ /**
1810
+ * Check whether the adapter has been initialized.
1811
+ */
1812
+ isInitialized() {
1813
+ return this.initialized;
1814
+ }
1815
+ /**
1816
+ * Get the project directory this adapter was initialized with.
1817
+ */
1818
+ getProjectDir() {
1819
+ return this.projectDir;
1820
+ }
1821
+ };
1822
+ }
1823
+ });
1824
+
1825
+ // packages/adapters/src/providers/cursor/index.js
1826
+ var cursor_exports = {};
1827
+ __export(cursor_exports, {
1828
+ CursorAdapter: () => CursorAdapter,
1829
+ CursorHookProvider: () => CursorHookProvider,
1830
+ CursorInstallProvider: () => CursorInstallProvider,
1831
+ createAdapter: () => createAdapter3,
1832
+ default: () => cursor_default
1833
+ });
1834
+ function createAdapter3() {
1835
+ return new CursorAdapter();
1836
+ }
1837
+ var cursor_default;
1838
+ var init_cursor = __esm({
1839
+ "packages/adapters/src/providers/cursor/index.js"() {
1840
+ "use strict";
1841
+ init_adapter3();
1842
+ init_adapter3();
1843
+ init_hooks3();
1844
+ init_install3();
1845
+ cursor_default = CursorAdapter;
1846
+ }
1847
+ });
1848
+
1849
+ // packages/adapters/src/registry.js
1850
+ import { readFileSync as readFileSync6 } from "node:fs";
1851
+ import { dirname as dirname2, join as join12, resolve } from "node:path";
1852
+ import { fileURLToPath } from "node:url";
1853
+ var PROVIDER_IDS = ["claude-code", "opencode", "cursor"];
1854
+ function getProviderManifests() {
1855
+ const manifests = [];
1856
+ const baseDir = resolve(dirname2(fileURLToPath(import.meta.url)), "providers");
1857
+ for (const providerId of PROVIDER_IDS) {
1858
+ try {
1859
+ const manifestPath = join12(baseDir, providerId, "manifest.json");
1860
+ const raw = readFileSync6(manifestPath, "utf-8");
1861
+ manifests.push(JSON.parse(raw));
1862
+ } catch {
1863
+ }
1864
+ }
1865
+ return manifests;
1866
+ }
1867
+ async function discoverProviders() {
1868
+ const providers = /* @__PURE__ */ new Map();
1869
+ providers.set("claude-code", async () => {
1870
+ const { ClaudeCodeAdapter: ClaudeCodeAdapter2 } = await Promise.resolve().then(() => (init_claude_code(), claude_code_exports));
1871
+ return new ClaudeCodeAdapter2();
1872
+ });
1873
+ providers.set("opencode", async () => {
1874
+ const { OpenCodeAdapter: OpenCodeAdapter2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
1875
+ return new OpenCodeAdapter2();
1876
+ });
1877
+ providers.set("cursor", async () => {
1878
+ const { CursorAdapter: CursorAdapter2 } = await Promise.resolve().then(() => (init_cursor(), cursor_exports));
1879
+ return new CursorAdapter2();
1880
+ });
1881
+ return providers;
1882
+ }
1883
+
1884
+ // packages/adapters/src/index.ts
1885
+ init_claude_code();
1886
+ init_claude_code();
1887
+ init_claude_code();
1888
+ init_claude_code();
1889
+ init_claude_code();
1890
+ init_claude_code();
1891
+ init_claude_code();
1892
+ init_claude_code();
1893
+ init_opencode();
1894
+ init_opencode();
1895
+ init_opencode();
1896
+ init_opencode();
1897
+ init_cursor();
1898
+ init_cursor();
1899
+ init_cursor();
1900
+ init_claude_code();
1901
+ init_opencode();
1902
+ init_cursor();
1903
+ export {
1904
+ ClaudeCodeAdapter,
1905
+ ClaudeCodeContextMonitorProvider,
1906
+ ClaudeCodeHookProvider,
1907
+ ClaudeCodeInstallProvider,
1908
+ ClaudeCodePathProvider,
1909
+ ClaudeCodeSpawnProvider,
1910
+ ClaudeCodeTransportProvider,
1911
+ CursorAdapter,
1912
+ CursorHookProvider,
1913
+ CursorInstallProvider,
1914
+ OpenCodeAdapter,
1915
+ OpenCodeHookProvider,
1916
+ OpenCodeInstallProvider,
1917
+ OpenCodeSpawnProvider,
1918
+ checkStatuslineIntegration,
1919
+ createAdapter as createClaudeCodeAdapter,
1920
+ createAdapter3 as createCursorAdapter,
1921
+ createAdapter2 as createOpenCodeAdapter,
1922
+ discoverProviders,
1923
+ getProviderManifests,
1924
+ getSetupInstructions,
1925
+ getStatuslineConfig
1926
+ };
1927
+ //# sourceMappingURL=index.js.map