@sylphx/flow 1.0.1 → 1.0.3

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 (229) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +10 -9
  3. package/src/commands/codebase-command.ts +168 -0
  4. package/src/commands/flow-command.ts +1137 -0
  5. package/src/commands/flow-orchestrator.ts +296 -0
  6. package/src/commands/hook-command.ts +444 -0
  7. package/src/commands/init-command.ts +92 -0
  8. package/src/commands/init-core.ts +322 -0
  9. package/src/commands/knowledge-command.ts +161 -0
  10. package/src/commands/run-command.ts +120 -0
  11. package/src/components/benchmark-monitor.tsx +331 -0
  12. package/src/components/reindex-progress.tsx +261 -0
  13. package/src/composables/functional/index.ts +14 -0
  14. package/src/composables/functional/useEnvironment.ts +171 -0
  15. package/src/composables/functional/useFileSystem.ts +139 -0
  16. package/src/composables/index.ts +5 -0
  17. package/src/composables/useEnv.ts +13 -0
  18. package/src/composables/useRuntimeConfig.ts +27 -0
  19. package/src/composables/useTargetConfig.ts +45 -0
  20. package/src/config/ai-config.ts +376 -0
  21. package/src/config/constants.ts +35 -0
  22. package/src/config/index.ts +27 -0
  23. package/src/config/rules.ts +43 -0
  24. package/src/config/servers.ts +371 -0
  25. package/src/config/targets.ts +126 -0
  26. package/src/core/agent-loader.ts +141 -0
  27. package/src/core/agent-manager.ts +174 -0
  28. package/src/core/ai-sdk.ts +603 -0
  29. package/src/core/app-factory.ts +381 -0
  30. package/src/core/builtin-agents.ts +9 -0
  31. package/src/core/command-system.ts +550 -0
  32. package/src/core/config-system.ts +550 -0
  33. package/src/core/connection-pool.ts +390 -0
  34. package/src/core/di-container.ts +155 -0
  35. package/src/core/error-handling.ts +519 -0
  36. package/src/core/formatting/bytes.test.ts +115 -0
  37. package/src/core/formatting/bytes.ts +64 -0
  38. package/src/core/functional/async.ts +313 -0
  39. package/src/core/functional/either.ts +109 -0
  40. package/src/core/functional/error-handler.ts +135 -0
  41. package/src/core/functional/error-types.ts +311 -0
  42. package/src/core/functional/index.ts +19 -0
  43. package/src/core/functional/option.ts +142 -0
  44. package/src/core/functional/pipe.ts +189 -0
  45. package/src/core/functional/result.ts +204 -0
  46. package/src/core/functional/validation.ts +138 -0
  47. package/src/core/headless-display.ts +96 -0
  48. package/src/core/index.ts +6 -0
  49. package/src/core/installers/file-installer.ts +303 -0
  50. package/src/core/installers/mcp-installer.ts +213 -0
  51. package/src/core/interfaces/index.ts +22 -0
  52. package/src/core/interfaces/repository.interface.ts +91 -0
  53. package/src/core/interfaces/service.interface.ts +133 -0
  54. package/src/core/interfaces.ts +129 -0
  55. package/src/core/loop-controller.ts +200 -0
  56. package/src/core/result.ts +351 -0
  57. package/src/core/rule-loader.ts +147 -0
  58. package/src/core/rule-manager.ts +240 -0
  59. package/src/core/service-config.ts +252 -0
  60. package/src/core/session-service.ts +121 -0
  61. package/src/core/state-detector.ts +389 -0
  62. package/src/core/storage-factory.ts +115 -0
  63. package/src/core/stream-handler.ts +288 -0
  64. package/src/core/target-manager.ts +161 -0
  65. package/src/core/type-utils.ts +427 -0
  66. package/src/core/unified-storage.ts +456 -0
  67. package/src/core/upgrade-manager.ts +300 -0
  68. package/src/core/validation/limit.test.ts +155 -0
  69. package/src/core/validation/limit.ts +46 -0
  70. package/src/core/validation/query.test.ts +44 -0
  71. package/src/core/validation/query.ts +20 -0
  72. package/src/db/auto-migrate.ts +322 -0
  73. package/src/db/base-database-client.ts +144 -0
  74. package/src/db/cache-db.ts +218 -0
  75. package/src/db/cache-schema.ts +75 -0
  76. package/src/db/database.ts +70 -0
  77. package/src/db/index.ts +252 -0
  78. package/src/db/memory-db.ts +153 -0
  79. package/src/db/memory-schema.ts +29 -0
  80. package/src/db/schema.ts +289 -0
  81. package/src/db/session-repository.ts +733 -0
  82. package/src/domains/codebase/index.ts +5 -0
  83. package/src/domains/codebase/tools.ts +139 -0
  84. package/src/domains/index.ts +8 -0
  85. package/src/domains/knowledge/index.ts +10 -0
  86. package/src/domains/knowledge/resources.ts +537 -0
  87. package/src/domains/knowledge/tools.ts +174 -0
  88. package/src/domains/utilities/index.ts +6 -0
  89. package/src/domains/utilities/time/index.ts +5 -0
  90. package/src/domains/utilities/time/tools.ts +291 -0
  91. package/src/index.ts +211 -0
  92. package/src/services/agent-service.ts +273 -0
  93. package/src/services/claude-config-service.ts +252 -0
  94. package/src/services/config-service.ts +258 -0
  95. package/src/services/evaluation-service.ts +271 -0
  96. package/src/services/functional/evaluation-logic.ts +296 -0
  97. package/src/services/functional/file-processor.ts +273 -0
  98. package/src/services/functional/index.ts +12 -0
  99. package/src/services/index.ts +13 -0
  100. package/src/services/mcp-service.ts +432 -0
  101. package/src/services/memory.service.ts +476 -0
  102. package/src/services/search/base-indexer.ts +156 -0
  103. package/src/services/search/codebase-indexer-types.ts +38 -0
  104. package/src/services/search/codebase-indexer.ts +647 -0
  105. package/src/services/search/embeddings-provider.ts +455 -0
  106. package/src/services/search/embeddings.ts +316 -0
  107. package/src/services/search/functional-indexer.ts +323 -0
  108. package/src/services/search/index.ts +27 -0
  109. package/src/services/search/indexer.ts +380 -0
  110. package/src/services/search/knowledge-indexer.ts +422 -0
  111. package/src/services/search/semantic-search.ts +244 -0
  112. package/src/services/search/tfidf.ts +559 -0
  113. package/src/services/search/unified-search-service.ts +888 -0
  114. package/src/services/smart-config-service.ts +385 -0
  115. package/src/services/storage/cache-storage.ts +487 -0
  116. package/src/services/storage/drizzle-storage.ts +581 -0
  117. package/src/services/storage/index.ts +15 -0
  118. package/src/services/storage/lancedb-vector-storage.ts +494 -0
  119. package/src/services/storage/memory-storage.ts +268 -0
  120. package/src/services/storage/separated-storage.ts +467 -0
  121. package/src/services/storage/vector-storage.ts +13 -0
  122. package/src/shared/agents/index.ts +63 -0
  123. package/src/shared/files/index.ts +99 -0
  124. package/src/shared/index.ts +32 -0
  125. package/src/shared/logging/index.ts +24 -0
  126. package/src/shared/processing/index.ts +153 -0
  127. package/src/shared/types/index.ts +25 -0
  128. package/src/targets/claude-code.ts +574 -0
  129. package/src/targets/functional/claude-code-logic.ts +185 -0
  130. package/src/targets/functional/index.ts +6 -0
  131. package/src/targets/opencode.ts +529 -0
  132. package/src/types/agent.types.ts +32 -0
  133. package/src/types/api/batch.ts +108 -0
  134. package/src/types/api/errors.ts +118 -0
  135. package/src/types/api/index.ts +55 -0
  136. package/src/types/api/requests.ts +76 -0
  137. package/src/types/api/responses.ts +180 -0
  138. package/src/types/api/websockets.ts +85 -0
  139. package/src/types/api.types.ts +9 -0
  140. package/src/types/benchmark.ts +49 -0
  141. package/src/types/cli.types.ts +87 -0
  142. package/src/types/common.types.ts +35 -0
  143. package/src/types/database.types.ts +510 -0
  144. package/src/types/mcp-config.types.ts +448 -0
  145. package/src/types/mcp.types.ts +69 -0
  146. package/src/types/memory-types.ts +63 -0
  147. package/src/types/provider.types.ts +28 -0
  148. package/src/types/rule.types.ts +24 -0
  149. package/src/types/session.types.ts +214 -0
  150. package/src/types/target-config.types.ts +295 -0
  151. package/src/types/target.types.ts +140 -0
  152. package/src/types/todo.types.ts +25 -0
  153. package/src/types.ts +40 -0
  154. package/src/utils/advanced-tokenizer.ts +191 -0
  155. package/src/utils/agent-enhancer.ts +114 -0
  156. package/src/utils/ai-model-fetcher.ts +19 -0
  157. package/src/utils/async-file-operations.ts +516 -0
  158. package/src/utils/audio-player.ts +345 -0
  159. package/src/utils/cli-output.ts +266 -0
  160. package/src/utils/codebase-helpers.ts +211 -0
  161. package/src/utils/console-ui.ts +79 -0
  162. package/src/utils/database-errors.ts +140 -0
  163. package/src/utils/debug-logger.ts +49 -0
  164. package/src/utils/error-handler.ts +53 -0
  165. package/src/utils/file-operations.ts +310 -0
  166. package/src/utils/file-scanner.ts +259 -0
  167. package/src/utils/functional/array.ts +355 -0
  168. package/src/utils/functional/index.ts +15 -0
  169. package/src/utils/functional/object.ts +279 -0
  170. package/src/utils/functional/string.ts +281 -0
  171. package/src/utils/functional.ts +543 -0
  172. package/src/utils/help.ts +20 -0
  173. package/src/utils/immutable-cache.ts +106 -0
  174. package/src/utils/index.ts +78 -0
  175. package/src/utils/jsonc.ts +158 -0
  176. package/src/utils/logger.ts +396 -0
  177. package/src/utils/mcp-config.ts +249 -0
  178. package/src/utils/memory-tui.ts +414 -0
  179. package/src/utils/models-dev.ts +91 -0
  180. package/src/utils/notifications.ts +169 -0
  181. package/src/utils/object-utils.ts +51 -0
  182. package/src/utils/parallel-operations.ts +487 -0
  183. package/src/utils/paths.ts +143 -0
  184. package/src/utils/process-manager.ts +155 -0
  185. package/src/utils/prompts.ts +120 -0
  186. package/src/utils/search-tool-builder.ts +214 -0
  187. package/src/utils/secret-utils.ts +179 -0
  188. package/src/utils/security.ts +537 -0
  189. package/src/utils/session-manager.ts +168 -0
  190. package/src/utils/session-title.ts +87 -0
  191. package/src/utils/settings.ts +182 -0
  192. package/src/utils/simplified-errors.ts +410 -0
  193. package/src/utils/sync-utils.ts +159 -0
  194. package/src/utils/target-config.ts +570 -0
  195. package/src/utils/target-utils.ts +394 -0
  196. package/src/utils/template-engine.ts +94 -0
  197. package/src/utils/test-audio.ts +71 -0
  198. package/src/utils/todo-context.ts +46 -0
  199. package/src/utils/token-counter.ts +288 -0
  200. package/dist/index.d.ts +0 -10
  201. package/dist/index.js +0 -59554
  202. package/dist/lancedb.linux-x64-gnu-b7f0jgsz.node +0 -0
  203. package/dist/lancedb.linux-x64-musl-tgcv22rx.node +0 -0
  204. package/dist/shared/chunk-25dwp0dp.js +0 -89
  205. package/dist/shared/chunk-3pjb6063.js +0 -208
  206. package/dist/shared/chunk-4d6ydpw7.js +0 -2854
  207. package/dist/shared/chunk-4wjcadjk.js +0 -225
  208. package/dist/shared/chunk-5j4w74t6.js +0 -30
  209. package/dist/shared/chunk-5j8m3dh3.js +0 -58
  210. package/dist/shared/chunk-5thh3qem.js +0 -91
  211. package/dist/shared/chunk-6g9xy73m.js +0 -252
  212. package/dist/shared/chunk-7eq34c42.js +0 -23
  213. package/dist/shared/chunk-c2gwgx3r.js +0 -115
  214. package/dist/shared/chunk-cjd3mk4c.js +0 -1320
  215. package/dist/shared/chunk-g5cv6703.js +0 -368
  216. package/dist/shared/chunk-hpkhykhq.js +0 -574
  217. package/dist/shared/chunk-m2322pdk.js +0 -122
  218. package/dist/shared/chunk-nd5fdvaq.js +0 -26
  219. package/dist/shared/chunk-pgd3m6zf.js +0 -108
  220. package/dist/shared/chunk-qk8n91hw.js +0 -494
  221. package/dist/shared/chunk-rkkn8szp.js +0 -16855
  222. package/dist/shared/chunk-t16rfxh0.js +0 -61
  223. package/dist/shared/chunk-t4fbfa5v.js +0 -19
  224. package/dist/shared/chunk-t77h86w6.js +0 -276
  225. package/dist/shared/chunk-v0ez4aef.js +0 -71
  226. package/dist/shared/chunk-v29j2r3s.js +0 -32051
  227. package/dist/shared/chunk-vfbc6ew5.js +0 -765
  228. package/dist/shared/chunk-vmeqwm1c.js +0 -204
  229. package/dist/shared/chunk-x66eh37x.js +0 -137
@@ -0,0 +1,574 @@
1
+ import { spawn } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import fsPromises from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { FileInstaller } from '../core/installers/file-installer.js';
6
+ import { MCPInstaller } from '../core/installers/mcp-installer.js';
7
+ import type { AgentMetadata } from '../types/target-config.types.js';
8
+ import type { CommonOptions, MCPServerConfigUnion, SetupResult, Target } from '../types.js';
9
+ import { CLIError } from '../utils/error-handler.js';
10
+ import { getAgentsDir, getSlashCommandsDir } from '../utils/paths.js';
11
+ import { sanitize } from '../utils/security.js';
12
+ import { fileUtils, generateHelpText, pathUtils, yamlUtils } from '../utils/target-utils.js';
13
+
14
+ /**
15
+ * Claude Code target - composition approach with all original functionality
16
+ */
17
+ export const claudeCodeTarget: Target = {
18
+ id: 'claude-code',
19
+ name: 'Claude Code',
20
+ description: 'Claude Code CLI with YAML front matter agents (.claude/agents/*.md)',
21
+ category: 'cli',
22
+ isImplemented: true,
23
+ isDefault: false,
24
+
25
+ mcpServerConfig: {
26
+ disableTime: true,
27
+ disableKnowledge: false,
28
+ disableCodebase: true,
29
+ },
30
+
31
+ config: {
32
+ agentDir: '.claude/agents',
33
+ agentExtension: '.md',
34
+ agentFormat: 'yaml-frontmatter',
35
+ stripYaml: false,
36
+ flatten: false,
37
+ configFile: '.mcp.json',
38
+ configSchema: null,
39
+ mcpConfigPath: 'mcpServers',
40
+ rulesFile: undefined, // Rules are included in agent files
41
+ outputStylesDir: undefined, // Output styles are included in agent files
42
+ slashCommandsDir: '.claude/commands',
43
+ installation: {
44
+ createAgentDir: true,
45
+ createConfigFile: true,
46
+ useSecretFiles: false,
47
+ },
48
+ },
49
+
50
+ /**
51
+ * Transform agent content for Claude Code
52
+ * Convert OpenCode format to Claude Code format with proper name/description extraction
53
+ */
54
+ async transformAgentContent(
55
+ content: string,
56
+ metadata?: AgentMetadata,
57
+ sourcePath?: string
58
+ ): Promise<string> {
59
+ const { metadata: existingMetadata, content: baseContent } =
60
+ await yamlUtils.extractFrontMatter(content);
61
+
62
+ // Convert OpenCode format to Claude Code format
63
+ const claudeCodeMetadata = convertToClaudeCodeFormat(existingMetadata, baseContent, sourcePath);
64
+
65
+ // If additional metadata is provided, merge it
66
+ if (metadata) {
67
+ Object.assign(claudeCodeMetadata, metadata);
68
+ }
69
+
70
+ return yamlUtils.addFrontMatter(baseContent, claudeCodeMetadata);
71
+ },
72
+
73
+ /**
74
+ * Transform MCP server configuration for Claude Code
75
+ * Convert from various formats to Claude Code's optimal format
76
+ */
77
+ transformMCPConfig(config: MCPServerConfigUnion, _serverId?: string): Record<string, unknown> {
78
+ // Handle legacy OpenCode 'local' type
79
+ if (config.type === 'local') {
80
+ // Convert OpenCode 'local' array command to Claude Code format
81
+ const [command, ...args] = config.command;
82
+ return {
83
+ type: 'stdio',
84
+ command,
85
+ ...(args && args.length > 0 && { args }),
86
+ ...(config.environment && { env: config.environment }),
87
+ };
88
+ }
89
+
90
+ // Handle new stdio format (already optimized for Claude Code)
91
+ if (config.type === 'stdio') {
92
+ return {
93
+ type: 'stdio',
94
+ command: config.command,
95
+ ...(config.args && config.args.length > 0 && { args: config.args }),
96
+ ...(config.env && { env: config.env }),
97
+ };
98
+ }
99
+
100
+ // Handle legacy OpenCode 'remote' type
101
+ if (config.type === 'remote') {
102
+ return {
103
+ type: 'http',
104
+ url: config.url,
105
+ ...(config.headers && { headers: config.headers }),
106
+ };
107
+ }
108
+
109
+ // Handle new http format (already optimized for Claude Code)
110
+ if (config.type === 'http') {
111
+ return {
112
+ type: 'http',
113
+ url: config.url,
114
+ ...(config.headers && { headers: config.headers }),
115
+ };
116
+ }
117
+
118
+ return config;
119
+ },
120
+
121
+ getConfigPath: (cwd: string) =>
122
+ Promise.resolve(fileUtils.getConfigPath(claudeCodeTarget.config, cwd)),
123
+
124
+ /**
125
+ * Read Claude Code configuration with structure normalization
126
+ */
127
+ async readConfig(cwd: string): Promise<any> {
128
+ const config = await fileUtils.readConfig(claudeCodeTarget.config, cwd);
129
+
130
+ // Ensure the config has the expected structure for Claude Code
131
+ if (!config.mcpServers) {
132
+ config.mcpServers = {};
133
+ }
134
+
135
+ return config;
136
+ },
137
+
138
+ /**
139
+ * Write Claude Code configuration with structure normalization
140
+ */
141
+ async writeConfig(cwd: string, config: Record<string, unknown>): Promise<void> {
142
+ // Ensure the config has the expected structure for Claude Code
143
+ if (!config.mcpServers) {
144
+ config.mcpServers = {};
145
+ }
146
+
147
+ await fileUtils.writeConfig(claudeCodeTarget.config, cwd, config);
148
+ },
149
+
150
+ validateRequirements: (cwd: string) =>
151
+ fileUtils.validateRequirements(claudeCodeTarget.config, cwd),
152
+
153
+ /**
154
+ * Get detailed Claude Code-specific help text
155
+ */
156
+ getHelpText(): string {
157
+ let help = generateHelpText(claudeCodeTarget.config);
158
+
159
+ help += 'Claude Code-Specific Information:\n';
160
+ help += ' Configuration File: .mcp.json\n';
161
+ help += ' Agent Format: Markdown with YAML front matter\n';
162
+ help += ' MCP Integration: Full server support\n\n';
163
+
164
+ help += 'Example Agent Structure:\n';
165
+ help += ' ---\n';
166
+ help += ` name: "code-reviewer"\n`;
167
+ help += ` description: "Expert code review specialist"\n`;
168
+ help += ' ---\n\n';
169
+ help += ' Agent content here...\n\n';
170
+ help += `Note: Only 'name' and 'description' fields are supported.\n`;
171
+ help += 'Tools field omitted to allow all tools by default (whitelist model limitation).\n';
172
+ help += 'Unsupported fields (mode, temperature, etc.) are automatically removed.\n\n';
173
+
174
+ help += 'Example MCP Configuration:\n';
175
+ help += ' {\n';
176
+ help += ` "mcpServers": {\n`;
177
+ help += ` "filesystem": {\n`;
178
+ help += ` "type": "stdio",\n`;
179
+ help += ` "command": "npx",\n`;
180
+ help += ` "args": ["@modelcontextprotocol/server-filesystem", "/path/to/allowed/dir"]\n`;
181
+ help += ' },\n';
182
+ help += ` "git": {\n`;
183
+ help += ` "type": "stdio",\n`;
184
+ help += ` "command": "npx",\n`;
185
+ help += ` "args": ["@modelcontextprotocol/server-git", "."],\n`;
186
+ help += ` "env": {\n`;
187
+ help += ` "GIT_TRACE": "1"\n`;
188
+ help += ' }\n';
189
+ help += ' },\n';
190
+ help += ` "api-server": {\n`;
191
+ help += ` "type": "http",\n`;
192
+ help += ` "url": "https://api.example.com/mcp",\n`;
193
+ help += ` "headers": {\n`;
194
+ help += ` "Authorization": "Bearer $API_KEY"\n`;
195
+ help += ' }\n';
196
+ help += ' }\n';
197
+ help += ' }\n';
198
+ help += ' }\n\n';
199
+ help +=
200
+ 'Note: Environment variables can be expanded in command, args, env, url, and headers.\n\n';
201
+
202
+ return help;
203
+ },
204
+
205
+ /**
206
+ * Execute command using Claude Code with system prompt and user prompt
207
+ */
208
+ async executeCommand(
209
+ systemPrompt: string,
210
+ userPrompt: string,
211
+ options: { verbose?: boolean; dryRun?: boolean; print?: boolean; continue?: boolean } = {}
212
+ ): Promise<void> {
213
+ // Sanitize and validate inputs
214
+ const sanitizedSystemPrompt = sanitize.yamlContent(systemPrompt);
215
+ // Remove dangerous control characters but don't limit length for user prompts
216
+ const sanitizedUserPrompt = userPrompt.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
217
+
218
+ // Add summary request to system prompt
219
+ const enhancedSystemPrompt = `${sanitizedSystemPrompt}
220
+
221
+ Please begin your response with a comprehensive summary of all the instructions and context provided above.`;
222
+
223
+ if (options.dryRun) {
224
+ // Build the command for display
225
+ const dryRunArgs = ['claude', '--dangerously-skip-permissions'];
226
+ if (options.print) dryRunArgs.push('-p');
227
+ if (options.continue) dryRunArgs.push('-c');
228
+ dryRunArgs.push('--system-prompt', '"<agent content>"');
229
+ if (sanitizedUserPrompt.trim() !== '') {
230
+ dryRunArgs.push(`"${sanitizedUserPrompt}"`);
231
+ }
232
+
233
+ console.log(chalk.cyan('Dry run - Would execute:'));
234
+ console.log(chalk.bold(dryRunArgs.join(' ')));
235
+ console.log(chalk.dim(`System prompt: ${enhancedSystemPrompt.length} characters`));
236
+ console.log(chalk.dim(`User prompt: ${sanitizedUserPrompt.length} characters`));
237
+ console.log('✓ Dry run completed successfully');
238
+ return;
239
+ }
240
+
241
+ try {
242
+ // Build arguments
243
+ const args = ['--dangerously-skip-permissions'];
244
+
245
+ // Add print and continue flags
246
+ if (options.print) {
247
+ args.push('-p');
248
+ }
249
+ if (options.continue) {
250
+ args.push('-c');
251
+ }
252
+
253
+ args.push('--system-prompt', enhancedSystemPrompt);
254
+ if (options.verbose) {
255
+ console.log('🚀 Executing Claude Code');
256
+ console.log(`📝 System prompt length: ${enhancedSystemPrompt.length} characters`);
257
+ }
258
+
259
+ if (sanitizedUserPrompt.trim() !== '') {
260
+ args.push(sanitizedUserPrompt);
261
+ }
262
+
263
+ if (options.verbose) {
264
+ console.log(`📝 User prompt length: ${sanitizedUserPrompt.length} characters`);
265
+ }
266
+
267
+ // Use child_process directly to bypass security validation for this specific case
268
+ // This is safe because we're controlling the command and just passing the prompt as an argument
269
+
270
+ if (options.verbose) {
271
+ console.log(`🚀 Executing: claude ${args.join(' ')}`);
272
+ console.log('');
273
+ }
274
+
275
+ await new Promise<void>((resolve, reject) => {
276
+ const child = spawn('claude', args, {
277
+ stdio: 'inherit',
278
+ shell: false,
279
+ env: process.env, // Pass environment variables including ANTHROPIC_BASE_URL and ANTHROPIC_API_KEY
280
+ });
281
+
282
+ child.on('spawn', () => {
283
+ if (options.verbose) {
284
+ console.log('✓ Claude Code started');
285
+ }
286
+ });
287
+
288
+ child.on('close', (code) => {
289
+ if (code === 0) {
290
+ resolve();
291
+ } else {
292
+ const error = new Error(`Claude Code exited with code ${code}`) as any;
293
+ error.code = code;
294
+ reject(error);
295
+ }
296
+ });
297
+
298
+ child.on('error', (error) => {
299
+ if (options.verbose) {
300
+ console.error('✗ Error spawning Claude:', error);
301
+ }
302
+ reject(error);
303
+ });
304
+ });
305
+ } catch (error: any) {
306
+ if (error.code === 'ENOENT') {
307
+ throw new CLIError('Claude Code not found. Please install it first.', 'CLAUDE_NOT_FOUND');
308
+ }
309
+ if (error.code) {
310
+ throw new CLIError(`Claude Code exited with code ${error.code}`, 'CLAUDE_ERROR');
311
+ }
312
+ throw new CLIError(`Failed to execute Claude Code: ${error.message}`, 'CLAUDE_ERROR');
313
+ }
314
+ },
315
+
316
+ /**
317
+ * Detect if this target is being used in the current environment
318
+ */
319
+ detectFromEnvironment(): boolean {
320
+ try {
321
+ const cwd = process.cwd();
322
+ return fs.existsSync(path.join(cwd, '.mcp.json'));
323
+ } catch {
324
+ return false;
325
+ }
326
+ },
327
+
328
+ /**
329
+ * Update .claude/settings.local.json to approve MCP servers
330
+ */
331
+ async approveMCPServers(cwd: string, serverNames: string[]): Promise<void> {
332
+ const settingsPath = path.join(cwd, '.claude', 'settings.local.json');
333
+
334
+ try {
335
+ // Read existing settings or create new
336
+ let settings: Record<string, unknown> = {};
337
+
338
+ try {
339
+ const content = await fsPromises.readFile(settingsPath, 'utf8');
340
+ settings = JSON.parse(content);
341
+ } catch (error: any) {
342
+ if (error.code !== 'ENOENT') {
343
+ throw error;
344
+ }
345
+ // File doesn't exist, will create new
346
+ }
347
+
348
+ // Get existing approved servers
349
+ const existingServers = Array.isArray(settings.enabledMcpjsonServers)
350
+ ? settings.enabledMcpjsonServers
351
+ : [];
352
+
353
+ // Merge with new servers (deduplicate)
354
+ const allServers = [...new Set([...existingServers, ...serverNames])];
355
+
356
+ // Update settings
357
+ settings.enabledMcpjsonServers = allServers;
358
+
359
+ // Ensure .claude directory exists
360
+ await fsPromises.mkdir(path.dirname(settingsPath), { recursive: true });
361
+
362
+ // Write updated settings
363
+ await fsPromises.writeFile(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, 'utf8');
364
+ } catch (error) {
365
+ throw new Error(
366
+ `Failed to approve MCP servers: ${error instanceof Error ? error.message : String(error)}`
367
+ );
368
+ }
369
+ },
370
+
371
+ /**
372
+ * Transform rules content for Claude Code
373
+ * Claude Code doesn't need front matter in rules files (CLAUDE.md)
374
+ */
375
+ async transformRulesContent(content: string): Promise<string> {
376
+ return yamlUtils.stripFrontMatter(content);
377
+ },
378
+
379
+ /**
380
+ * Setup hooks for Claude Code
381
+ * Configure session and prompt hooks for system information display
382
+ */
383
+ async setupHooks(cwd: string, _options: CommonOptions): Promise<SetupResult> {
384
+ const { processSettings, generateHookCommands } = await import('./functional/claude-code-logic.js');
385
+ const { pathExists, createDirectory, readFile, writeFile } = await import(
386
+ '../composables/functional/useFileSystem.js'
387
+ );
388
+
389
+ const claudeConfigDir = path.join(cwd, '.claude');
390
+ const settingsPath = path.join(claudeConfigDir, 'settings.json');
391
+
392
+ // Ensure .claude directory exists
393
+ const dirExistsResult = await pathExists(claudeConfigDir);
394
+ if (dirExistsResult._tag === 'Success' && !dirExistsResult.value) {
395
+ const createResult = await createDirectory(claudeConfigDir, { recursive: true });
396
+ if (createResult._tag === 'Failure') {
397
+ throw new Error(`Failed to create .claude directory: ${createResult.error.message}`);
398
+ }
399
+ }
400
+
401
+ // Read existing settings or null if doesn't exist
402
+ let existingContent: string | null = null;
403
+ const fileExistsResult = await pathExists(settingsPath);
404
+ if (fileExistsResult._tag === 'Success' && fileExistsResult.value) {
405
+ const readResult = await readFile(settingsPath);
406
+ if (readResult._tag === 'Success') {
407
+ existingContent = readResult.value;
408
+ }
409
+ }
410
+
411
+ // Generate hooks based on how CLI was invoked
412
+ const hookCommands = await generateHookCommands('claude-code');
413
+
414
+ // Process settings using pure functions
415
+ const settingsResult = processSettings(existingContent, hookCommands);
416
+
417
+ if (settingsResult._tag === 'Failure') {
418
+ throw new Error(`Failed to process settings: ${settingsResult.error.message}`);
419
+ }
420
+
421
+ // Write updated settings
422
+ const writeResult = await writeFile(settingsPath, settingsResult.value);
423
+ if (writeResult._tag === 'Failure') {
424
+ throw new Error(`Failed to write settings: ${writeResult.error.message}`);
425
+ }
426
+
427
+ // Return 3 hooks configured (SessionStart + UserPromptSubmit + Notification)
428
+ return {
429
+ count: 3,
430
+ message: 'Configured session, message, and notification hooks',
431
+ };
432
+ },
433
+
434
+ /**
435
+ * Setup agents for Claude Code
436
+ * Install agents to .claude/agents/ directory with rules and output styles appended
437
+ */
438
+ async setupAgents(cwd: string, options: CommonOptions): Promise<SetupResult> {
439
+ const { enhanceAgentContent } = await import('../utils/agent-enhancer.js');
440
+ const installer = new FileInstaller();
441
+ const agentsDir = path.join(cwd, this.config.agentDir);
442
+
443
+ const results = await installer.installToDirectory(
444
+ getAgentsDir(),
445
+ agentsDir,
446
+ async (content, sourcePath) => {
447
+ // Transform agent content (add YAML front matter, etc.)
448
+ const transformed = await this.transformAgentContent(content, undefined, sourcePath);
449
+
450
+ // Extract rules from frontmatter to pass to enhancer
451
+ const { metadata } = await yamlUtils.extractFrontMatter(transformed);
452
+ const rules = metadata.rules as string[] | undefined;
453
+
454
+ // Enhance with rules and output styles
455
+ const enhanced = await enhanceAgentContent(transformed, rules);
456
+
457
+ return enhanced;
458
+ },
459
+ {
460
+ ...options,
461
+ showProgress: false, // UI handled by init-command
462
+ }
463
+ );
464
+
465
+ return { count: results.length };
466
+ },
467
+
468
+ /**
469
+ * Setup output styles for Claude Code
470
+ * Output styles are appended to each agent file
471
+ */
472
+ async setupOutputStyles(_cwd: string, _options: CommonOptions): Promise<SetupResult> {
473
+ // Output styles are appended to each agent file during setupAgents
474
+ // No separate installation needed
475
+ return {
476
+ count: 0,
477
+ message: 'Output styles included in agent files',
478
+ };
479
+ },
480
+
481
+ /**
482
+ * Setup rules for Claude Code
483
+ * Rules are appended to each agent file
484
+ */
485
+ async setupRules(_cwd: string, _options: CommonOptions): Promise<SetupResult> {
486
+ // Rules are appended to each agent file during setupAgents
487
+ // No separate CLAUDE.md file needed
488
+ return {
489
+ count: 0,
490
+ message: 'Rules included in agent files',
491
+ };
492
+ },
493
+
494
+ /**
495
+ * Setup MCP servers for Claude Code
496
+ * Select, configure, install, and approve MCP servers
497
+ */
498
+ async setupMCP(cwd: string, options: CommonOptions): Promise<SetupResult> {
499
+ const installer = new MCPInstaller(this);
500
+ const result = await installer.setupMCP({ ...options, quiet: true });
501
+
502
+ // Approve servers in Claude Code settings
503
+ if (result.selectedServers.length > 0 && !options.dryRun) {
504
+ if (this.approveMCPServers) {
505
+ await this.approveMCPServers(cwd, result.selectedServers);
506
+ }
507
+ }
508
+
509
+ return { count: result.selectedServers.length };
510
+ },
511
+
512
+ /**
513
+ * Setup slash commands for Claude Code
514
+ * Install slash command templates to .claude/commands/ directory
515
+ */
516
+ async setupSlashCommands(cwd: string, options: CommonOptions): Promise<SetupResult> {
517
+ if (!this.config.slashCommandsDir) {
518
+ return { count: 0 };
519
+ }
520
+
521
+ const installer = new FileInstaller();
522
+ const slashCommandsDir = path.join(cwd, this.config.slashCommandsDir);
523
+
524
+ const results = await installer.installToDirectory(
525
+ getSlashCommandsDir(),
526
+ slashCommandsDir,
527
+ async (content) => {
528
+ // Slash commands are plain markdown with front matter - no transformation needed
529
+ return content;
530
+ },
531
+ {
532
+ ...options,
533
+ showProgress: false, // UI handled by init-command
534
+ }
535
+ );
536
+
537
+ return { count: results.length };
538
+ },
539
+ };
540
+
541
+ /**
542
+ * Convert OpenCode frontmatter to Claude Code format
543
+ */
544
+ function convertToClaudeCodeFormat(
545
+ openCodeMetadata: any,
546
+ content: string,
547
+ sourcePath?: string
548
+ ): any {
549
+ // Use explicit name from metadata if available, otherwise extract from content or path
550
+ const agentName =
551
+ openCodeMetadata.name || pathUtils.extractAgentName(content, openCodeMetadata, sourcePath);
552
+
553
+ // Extract description from metadata or content
554
+ const description = openCodeMetadata.description || pathUtils.extractDescription(content);
555
+
556
+ // Only keep supported fields for Claude Code
557
+ const result: any = {
558
+ name: agentName,
559
+ description: description,
560
+ };
561
+
562
+ // Only add model if it exists and is not 'inherit' (default)
563
+ if (openCodeMetadata.model && openCodeMetadata.model !== 'inherit') {
564
+ result.model = openCodeMetadata.model;
565
+ }
566
+
567
+ // Remove unsupported fields that might cause issues
568
+ // - tools: removed to allow all tools by default
569
+ // - mode: not supported by Claude Code
570
+ // - temperature: not supported by Claude Code
571
+ // - Other custom fields should also be removed for compatibility
572
+
573
+ return result;
574
+ }