@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,1137 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import boxen from 'boxen';
4
+ import ora from 'ora';
5
+ import path from 'node:path';
6
+ import fs from 'node:fs/promises';
7
+ import { targetManager } from '../core/target-manager.js';
8
+ import { CLIError } from '../utils/error-handler.js';
9
+ import type { RunCommandOptions } from '../types.js';
10
+ import { StateDetector, type ProjectState } from '../core/state-detector.js';
11
+ import { UpgradeManager } from '../core/upgrade-manager.js';
12
+ import { loadAgentContent, extractAgentInstructions } from './run-command.js';
13
+ import { ClaudeConfigService } from '../services/claude-config-service.js';
14
+ import { ConfigService } from '../services/config-service.js';
15
+ import { projectSettings } from '../utils/settings.js';
16
+
17
+ export interface FlowOptions {
18
+ target?: string;
19
+ verbose?: boolean;
20
+ dryRun?: boolean;
21
+ sync?: boolean; // Sync mode - delete and re-install template files
22
+ initOnly?: boolean;
23
+ runOnly?: boolean;
24
+ repair?: boolean; // Repair mode - install missing components
25
+ upgrade?: boolean;
26
+ upgradeTarget?: boolean;
27
+ mcp?: boolean;
28
+ agents?: boolean;
29
+ rules?: boolean;
30
+ outputStyles?: boolean;
31
+ slashCommands?: boolean;
32
+ hooks?: boolean;
33
+ agent?: string;
34
+ agentFile?: string;
35
+
36
+ // Smart configuration options
37
+ selectProvider?: boolean;
38
+ selectAgent?: boolean;
39
+ useDefaults?: boolean;
40
+ provider?: string;
41
+ quick?: boolean;
42
+
43
+ // Execution modes
44
+ print?: boolean; // Headless print mode
45
+ continue?: boolean; // Continue previous conversation
46
+
47
+ // Loop mode (continuous execution)
48
+ loop?: number; // Loop every N seconds (--loop 60)
49
+ maxRuns?: number; // Optional max iterations (default: infinite)
50
+ }
51
+
52
+ /**
53
+ * Display welcome banner
54
+ */
55
+ function showWelcome(): void {
56
+ console.log(
57
+ boxen(
58
+ `${chalk.cyan.bold('Sylphx Flow')} ${chalk.dim('- AI-Powered Development Framework')}\n` +
59
+ `${chalk.dim('Auto-initialization • Smart upgrades • One-click launch')}`,
60
+ {
61
+ padding: 1,
62
+ margin: { bottom: 1 },
63
+ borderStyle: 'round',
64
+ borderColor: 'cyan',
65
+ }
66
+ )
67
+ );
68
+ }
69
+
70
+ /**
71
+ * Compare versions to check if one is outdated
72
+ */
73
+ function isVersionOutdated(current: string, latest: string): boolean {
74
+ try {
75
+ return compareVersions(current, latest) < 0;
76
+ } catch {
77
+ return false;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Compare two version strings
83
+ */
84
+ function compareVersions(v1: string, v2: string): number {
85
+ const parts1 = v1.split('.').map(Number);
86
+ const parts2 = v2.split('.').map(Number);
87
+
88
+ for (let i = 0; i < Math.min(parts1.length, parts2.length); i++) {
89
+ if (parts1[i] !== parts2[i]) {
90
+ return parts1[i] - parts2[i];
91
+ }
92
+ }
93
+
94
+ return parts1.length - parts2.length;
95
+ }
96
+
97
+ async function showStatus(state: ProjectState): Promise<void> {
98
+ console.log(chalk.cyan.bold('📊 Project Status\n'));
99
+
100
+ if (!state.initialized) {
101
+ console.log(' ' + chalk.yellow('⚠ Not initialized'));
102
+ } else {
103
+ console.log(` ${chalk.green('✓')} Initialized (Flow v${state.version || 'unknown'})`);
104
+
105
+ if (state.target) {
106
+ const versionStr = state.targetVersion ? ` (v${state.targetVersion})` : '';
107
+ console.log(` ${chalk.green('✓')} Target platform: ${state.target}${versionStr}`);
108
+ }
109
+
110
+ // Component status
111
+ const components = state.components;
112
+ console.log(`\n ${chalk.cyan('Components:')}`);
113
+ console.log(` Agents: ${components.agents.installed ? chalk.green(`✓ ${components.agents.count}`) : chalk.red('✗')}`);
114
+ console.log(` Rules: ${components.rules.installed ? chalk.green(`✓ ${components.rules.count}`) : chalk.red('✗')}`);
115
+ console.log(` Hooks: ${components.hooks.installed ? chalk.green('✓') : chalk.red('✗')}`);
116
+ console.log(` MCP: ${components.mcp.installed ? chalk.green(`✓ ${components.mcp.serverCount} servers`) : chalk.red('✗')}`);
117
+ console.log(` Output styles: ${components.outputStyles.installed ? chalk.green('✓') : chalk.red('✗')}`);
118
+ console.log(` Slash commands: ${components.slashCommands.installed ? chalk.green(`✓ ${components.slashCommands.count}`) : chalk.red('✗')}`);
119
+
120
+ // Outdated warnings
121
+ if (state.outdated) {
122
+ console.log(`\n ${chalk.yellow('⚠')} Flow version outdated: ${state.version} → ${state.latestVersion}`);
123
+ }
124
+
125
+ if (state.targetVersion && state.targetLatestVersion &&
126
+ isVersionOutdated(state.targetVersion, state.targetLatestVersion)) {
127
+ console.log(` ${chalk.yellow('⚠')} ${state.target} update available: v${state.targetVersion} → v${state.targetLatestVersion}`);
128
+ }
129
+
130
+ if (state.lastUpdated) {
131
+ const days = Math.floor((Date.now() - state.lastUpdated.getTime()) / (1000 * 60 * 60 * 24));
132
+ if (days > 7) {
133
+ console.log(`\n ${chalk.yellow('⚠')} Last updated: ${days} days ago`);
134
+ }
135
+ }
136
+ }
137
+
138
+ console.log('');
139
+ }
140
+
141
+ /**
142
+ * Get executable targets
143
+ */
144
+ function getExecutableTargets(): string[] {
145
+ return targetManager.getImplementedTargetIDs().filter((targetId) => {
146
+ const targetOption = targetManager.getTarget(targetId);
147
+ if (targetOption._tag === 'None') {
148
+ return false;
149
+ }
150
+ return targetOption.value.executeCommand !== undefined;
151
+ });
152
+ }
153
+
154
+ /**
155
+ * Execute command using target's executeCommand method
156
+ */
157
+ async function executeTargetCommand(
158
+ targetId: string,
159
+ systemPrompt: string,
160
+ userPrompt: string,
161
+ options: RunCommandOptions
162
+ ): Promise<void> {
163
+ const targetOption = targetManager.getTarget(targetId);
164
+
165
+ if (targetOption._tag === 'None') {
166
+ throw new CLIError(`Target not found: ${targetId}`, 'TARGET_NOT_FOUND');
167
+ }
168
+
169
+ const target = targetOption.value;
170
+
171
+ if (!target.isImplemented) {
172
+ throw new CLIError(
173
+ `Target '${targetId}' is not implemented. Supported targets: ${getExecutableTargets().join(', ')}`,
174
+ 'TARGET_NOT_IMPLEMENTED'
175
+ );
176
+ }
177
+
178
+ if (!target.executeCommand) {
179
+ throw new CLIError(
180
+ `Target '${targetId}' does not support command execution. Supported targets: ${getExecutableTargets().join(', ')}`,
181
+ 'EXECUTION_NOT_SUPPORTED'
182
+ );
183
+ }
184
+
185
+ return target.executeCommand(systemPrompt, userPrompt, options);
186
+ }
187
+
188
+ /**
189
+ * Compare versions
190
+ */
191
+ function isVersionOutdated(current: string, latest: string): boolean {
192
+ try {
193
+ return compareVersions(current, latest) < 0;
194
+ } catch {
195
+ return false;
196
+ }
197
+ }
198
+
199
+ function compareVersions(v1: string, v2: string): number {
200
+ const parts1 = v1.split('.').map(Number);
201
+ const parts2 = v2.split('.').map(Number);
202
+
203
+ for (let i = 0; i < Math.min(parts1.length, parts2.length); i++) {
204
+ if (parts1[i] !== parts2[i]) {
205
+ return parts1[i] - parts2[i];
206
+ }
207
+ }
208
+
209
+ return parts1.length - parts2.length;
210
+ }
211
+
212
+ /**
213
+ * Resolve prompt - handle file input if needed
214
+ * Supports @filename syntax: @prompt.txt or @/path/to/prompt.txt
215
+ */
216
+ async function resolvePrompt(prompt: string | undefined): Promise<string | undefined> {
217
+ if (!prompt) return prompt;
218
+
219
+ // Check for file input syntax: @filename
220
+ if (prompt.startsWith('@')) {
221
+ const filePath = prompt.slice(1); // Remove @ prefix
222
+
223
+ try {
224
+ const resolvedPath = path.isAbsolute(filePath)
225
+ ? filePath
226
+ : path.resolve(process.cwd(), filePath);
227
+
228
+ const content = await fs.readFile(resolvedPath, 'utf-8');
229
+ console.log(chalk.dim(` ✓ Loaded prompt from: ${filePath}\n`));
230
+ return content.trim();
231
+ } catch (error) {
232
+ throw new Error(`Failed to read prompt file: ${filePath}`);
233
+ }
234
+ }
235
+
236
+ return prompt;
237
+ }
238
+
239
+ /**
240
+ * Main flow execution logic - simplified with orchestrator
241
+ */
242
+ export async function executeFlow(prompt: string | undefined, options: FlowOptions): Promise<void> {
243
+ // Resolve prompt (handle file input)
244
+ const resolvedPrompt = await resolvePrompt(prompt);
245
+
246
+ // Loop mode: Setup once, then loop only execution
247
+ if (options.loop !== undefined) {
248
+ const { LoopController } = await import('../core/loop-controller.js');
249
+ const controller = new LoopController();
250
+
251
+ // Default to 0s (no cooldown) if just --loop with no value
252
+ const interval = typeof options.loop === 'number' ? options.loop : 0;
253
+
254
+ // Auto-enable headless mode for loop
255
+ options.print = true;
256
+
257
+ // ONE-TIME SETUP: Do all initialization once before loop starts
258
+ const setupContext = await executeSetupPhase(resolvedPrompt, options);
259
+
260
+ // Save original continue flag
261
+ const originalContinue = options.continue || false;
262
+
263
+ // LOOP: Only execute the command repeatedly
264
+ await controller.run(
265
+ async () => {
266
+ const isFirstIteration = controller['state'].iteration === 1;
267
+
268
+ // Continue logic:
269
+ // - If user specified --continue, always use it (all iterations)
270
+ // - If user didn't specify, only use from 2nd iteration onwards
271
+ options.continue = originalContinue || !isFirstIteration;
272
+
273
+ try {
274
+ await executeCommandOnly(setupContext, resolvedPrompt, options);
275
+ return { exitCode: 0 };
276
+ } catch (error) {
277
+ return { exitCode: 1, error: error as Error };
278
+ }
279
+ },
280
+ {
281
+ enabled: true,
282
+ interval,
283
+ maxRuns: options.maxRuns,
284
+ }
285
+ );
286
+
287
+ return;
288
+ }
289
+
290
+ // Normal execution (non-loop)
291
+ await executeFlowOnce(resolvedPrompt, options);
292
+ }
293
+
294
+ /**
295
+ * Setup context for command execution
296
+ * Returns everything needed to execute the command repeatedly
297
+ */
298
+ interface SetupContext {
299
+ resolvedTarget: string;
300
+ agent: string;
301
+ systemPrompt: string;
302
+ runOptions: RunCommandOptions;
303
+ }
304
+
305
+ /**
306
+ * Execute setup phase once (for loop mode)
307
+ * Returns context needed for repeated command execution
308
+ */
309
+ async function executeSetupPhase(prompt: string | undefined, options: FlowOptions): Promise<SetupContext> {
310
+ // Quick mode: enable useDefaults and skip prompts
311
+ if (options.quick) {
312
+ options.useDefaults = true;
313
+ console.log(chalk.cyan('⚡ Quick mode enabled - using saved defaults\n'));
314
+ }
315
+
316
+ // Import orchestrator functions
317
+ const {
318
+ checkUpgrades,
319
+ checkComponentIntegrity,
320
+ selectTarget,
321
+ initializeProject,
322
+ } = await import('./flow-orchestrator.js');
323
+
324
+ // Show welcome banner (only once)
325
+ showWelcome();
326
+
327
+ let selectedTarget: string | undefined;
328
+ let state: ProjectState | undefined;
329
+
330
+ // Determine target
331
+ const initialTarget = options.target || (await projectSettings.getDefaultTarget());
332
+
333
+ // Detect state if we have a target
334
+ if (initialTarget && !options.sync) {
335
+ const detector = new StateDetector();
336
+
337
+ if (options.verbose) {
338
+ console.log(chalk.dim('🀔 Checking project status...\n'));
339
+ }
340
+
341
+ state = await detector.detect();
342
+
343
+ if (options.verbose) {
344
+ await showStatus(state);
345
+ }
346
+
347
+ // Check for upgrades
348
+ if (!options.quick) {
349
+ await checkUpgrades(state, options);
350
+ }
351
+
352
+ // Check component integrity
353
+ await checkComponentIntegrity(state, options);
354
+ }
355
+
356
+ // Initialize if needed
357
+ const shouldInitialize =
358
+ !state?.initialized ||
359
+ options.sync ||
360
+ options.repair ||
361
+ options.initOnly;
362
+
363
+ if (shouldInitialize) {
364
+ try {
365
+ const { selectAndValidateTarget, previewDryRun, installComponents } =
366
+ await import('./init-core.js');
367
+
368
+ const initOptions = {
369
+ target: options.target,
370
+ verbose: options.verbose || false,
371
+ dryRun: options.dryRun || false,
372
+ clear: options.sync || false,
373
+ mcp: options.mcp !== false,
374
+ agents: options.agents !== false,
375
+ rules: options.rules !== false,
376
+ outputStyles: options.outputStyles !== false,
377
+ slashCommands: options.slashCommands !== false,
378
+ hooks: options.hooks !== false,
379
+ };
380
+
381
+ // Handle sync mode - delete template files first
382
+ if (options.sync && !options.dryRun) {
383
+ const { buildSyncManifest, showSyncPreview, confirmSync, executeSyncDelete } = await import('../utils/sync-utils.js');
384
+
385
+ // Need target to build manifest
386
+ const targetId = await selectAndValidateTarget(initOptions);
387
+ selectedTarget = targetId;
388
+
389
+ const targetOption = targetManager.getTarget(targetId);
390
+ if (targetOption._tag === 'None') {
391
+ throw new Error(`Target not found: ${targetId}`);
392
+ }
393
+
394
+ const target = targetOption.value;
395
+ const manifest = await buildSyncManifest(process.cwd(), target);
396
+
397
+ console.log(chalk.cyan.bold('━━━ 🔄 Synchronizing Files\n'));
398
+ showSyncPreview(manifest, process.cwd());
399
+
400
+ const confirmed = await confirmSync();
401
+ if (!confirmed) {
402
+ console.log(chalk.yellow('\n✗ Sync cancelled\n'));
403
+ process.exit(0);
404
+ }
405
+
406
+ const deletedCount = await executeSyncDelete(manifest);
407
+ console.log(chalk.green(`\n✓ Deleted ${deletedCount} files\n`));
408
+ } else if (!options.sync) {
409
+ const targetId = await selectAndValidateTarget(initOptions);
410
+ selectedTarget = targetId;
411
+ }
412
+
413
+ if (options.dryRun) {
414
+ // Ensure we have a target ID for dry run
415
+ if (!selectedTarget) {
416
+ const targetId = await selectAndValidateTarget(initOptions);
417
+ selectedTarget = targetId;
418
+ }
419
+
420
+ console.log(
421
+ boxen(
422
+ chalk.yellow('⚠ Dry Run Mode') + chalk.dim('\nNo changes will be made to your project'),
423
+ {
424
+ padding: 1,
425
+ margin: { top: 0, bottom: 1, left: 0, right: 0 },
426
+ borderStyle: 'round',
427
+ borderColor: 'yellow',
428
+ }
429
+ )
430
+ );
431
+
432
+ await previewDryRun(selectedTarget, initOptions);
433
+
434
+ console.log(
435
+ '\n' +
436
+ boxen(chalk.green.bold('✓ Dry run complete'), {
437
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
438
+ margin: 0,
439
+ borderStyle: 'round',
440
+ borderColor: 'green',
441
+ }) +
442
+ '\n'
443
+ );
444
+
445
+ console.log(chalk.dim('✓ Initialization dry run complete\n'));
446
+ } else {
447
+ // Ensure we have a target ID for installation
448
+ if (!selectedTarget) {
449
+ const targetId = await selectAndValidateTarget(initOptions);
450
+ selectedTarget = targetId;
451
+ }
452
+
453
+ await installComponents(selectedTarget, initOptions);
454
+ console.log(chalk.green.bold('✓ Initialization complete\n'));
455
+ }
456
+ } catch (error) {
457
+ console.error(chalk.red.bold('✗ Initialization failed:'), error);
458
+ process.exit(1);
459
+ }
460
+ }
461
+
462
+ // Resolve target
463
+ let targetForResolution = options.target || state?.target || selectedTarget;
464
+ if (selectedTarget) {
465
+ targetForResolution = selectedTarget;
466
+ }
467
+
468
+ if (!targetForResolution) {
469
+ console.error(chalk.red.bold('✗ No target selected. Use --target or run init first.'));
470
+ process.exit(1);
471
+ }
472
+
473
+ const resolvedTarget = await targetManager.resolveTarget({
474
+ target: targetForResolution,
475
+ allowSelection: false,
476
+ });
477
+
478
+ console.log(chalk.cyan.bold(`━━━ 🎯 Launching ${resolvedTarget}\n`));
479
+
480
+ // Check if target supports command execution
481
+ const { getTargetsWithCommandSupport } = await import('../config/targets.js');
482
+ const supportedTargets = getTargetsWithCommandSupport().map(t => t.id);
483
+
484
+ if (!supportedTargets.includes(resolvedTarget)) {
485
+ console.log(chalk.red.bold('✗ Unsupported target platform\n'));
486
+ console.log(chalk.yellow(`Target '${resolvedTarget}' does not support agent execution.`));
487
+ console.log(chalk.cyan(`Supported platforms: ${supportedTargets.join(', ')}\n`));
488
+ console.log(chalk.dim('Tip: Use --target claude-code to specify Claude Code platform'));
489
+ console.log(chalk.dim('Example: bun dev:flow --target claude-code\n'));
490
+ process.exit(1);
491
+ }
492
+
493
+ // Claude Code handling
494
+ if (resolvedTarget === 'claude-code') {
495
+ const { SmartConfigService } = await import('../services/smart-config-service.js');
496
+ const { ConfigService } = await import('../services/config-service.js');
497
+
498
+ if (!(await ConfigService.hasInitialSetup())) {
499
+ console.log(chalk.cyan('🔑 First-time setup for Claude Code\n'));
500
+ await SmartConfigService.initialSetup();
501
+ console.log(chalk.green('✓ Setup complete!\n'));
502
+ }
503
+
504
+ const runtimeChoices = await SmartConfigService.selectRuntimeChoices({
505
+ selectProvider: options.selectProvider,
506
+ selectAgent: options.selectAgent,
507
+ useDefaults: options.useDefaults,
508
+ provider: options.provider,
509
+ agent: options.agent,
510
+ });
511
+
512
+ await SmartConfigService.setupEnvironment(runtimeChoices.provider!);
513
+ options.agent = runtimeChoices.agent;
514
+ }
515
+
516
+ const agent = options.agent || 'coder';
517
+ const verbose = options.verbose || false;
518
+
519
+ if (verbose || options.runOnly || !options.quick) {
520
+ console.log(` 🀖 Agent: ${chalk.cyan(agent)}`);
521
+ console.log(` 🎯 Target: ${chalk.cyan(resolvedTarget)}`);
522
+ if (prompt) {
523
+ console.log(` 💬 Prompt: ${chalk.dim(prompt)}\n`);
524
+ } else {
525
+ console.log(` 💬 Mode: ${chalk.dim('Interactive')}\n`);
526
+ }
527
+ }
528
+
529
+ // Load agent and prepare prompts
530
+ const agentContent = await loadAgentContent(agent, options.agentFile);
531
+ const agentInstructions = extractAgentInstructions(agentContent);
532
+ const systemPrompt = `AGENT INSTRUCTIONS:\n${agentInstructions}`;
533
+
534
+ // Prepare run options
535
+ const runOptions: RunCommandOptions = {
536
+ target: resolvedTarget,
537
+ verbose,
538
+ dryRun: options.dryRun,
539
+ agent,
540
+ agentFile: options.agentFile,
541
+ prompt,
542
+ print: options.print,
543
+ continue: options.continue,
544
+ };
545
+
546
+ return {
547
+ resolvedTarget,
548
+ agent,
549
+ systemPrompt,
550
+ runOptions,
551
+ };
552
+ }
553
+
554
+ /**
555
+ * Execute command only (for loop mode iterations)
556
+ * Uses pre-setup context to execute command without re-doing setup
557
+ */
558
+ async function executeCommandOnly(
559
+ context: SetupContext,
560
+ prompt: string | undefined,
561
+ options: FlowOptions
562
+ ): Promise<void> {
563
+ const userPrompt = prompt?.trim() || '';
564
+
565
+ // Update continue flag in runOptions
566
+ const runOptions = {
567
+ ...context.runOptions,
568
+ continue: options.continue,
569
+ };
570
+
571
+ try {
572
+ await executeTargetCommand(context.resolvedTarget, context.systemPrompt, userPrompt, runOptions);
573
+ } catch (error) {
574
+ console.error(chalk.red.bold('\n✗ Launch failed:'), error);
575
+ throw error;
576
+ }
577
+ }
578
+
579
+ /**
580
+ * Single flow execution (used by both normal and loop mode)
581
+ */
582
+ async function executeFlowOnce(prompt: string | undefined, options: FlowOptions): Promise<void> {
583
+ // Quick mode: enable useDefaults and skip prompts
584
+ if (options.quick) {
585
+ options.useDefaults = true;
586
+ console.log(chalk.cyan('⚡ Quick mode enabled - using saved defaults\n'));
587
+ }
588
+
589
+ // Continue mode always requires print mode
590
+ if (options.continue && !options.print) {
591
+ options.print = true;
592
+ }
593
+
594
+ // Import orchestrator functions
595
+ const {
596
+ checkUpgrades,
597
+ checkComponentIntegrity,
598
+ selectTarget,
599
+ initializeProject,
600
+ launchTarget,
601
+ } = await import('./flow-orchestrator.js');
602
+
603
+ // Show welcome banner
604
+ showWelcome();
605
+
606
+ // Declare at function level to persist across steps
607
+ let selectedTarget: string | undefined;
608
+ let state: ProjectState | undefined;
609
+
610
+ // First: determine target (from options, saved settings, or init will prompt)
611
+ const initialTarget = options.target || (await projectSettings.getDefaultTarget());
612
+
613
+ // Only detect state if we have a target (can't check components without knowing target structure)
614
+ if (initialTarget && !options.sync) {
615
+ const detector = new StateDetector();
616
+ const upgradeManager = new UpgradeManager();
617
+
618
+ if (options.verbose) {
619
+ console.log(chalk.dim('🀔 Checking project status...\n'));
620
+ }
621
+
622
+ state = await detector.detect();
623
+
624
+ if (options.verbose) {
625
+ await showStatus(state);
626
+ }
627
+
628
+ // Step 1: Check for upgrades
629
+ if (!options.quick) {
630
+ await checkUpgrades(state, options);
631
+ }
632
+
633
+ // Step 1: Upgrade (if requested)
634
+ if (options.upgrade && state.outdated && state.latestVersion) {
635
+ console.log(chalk.cyan.bold('━━━ 📊 Upgrading Flow\n'));
636
+ await upgradeManager.upgradeFlow(state);
637
+ console.log(chalk.green('✓ Upgrade complete\n'));
638
+ // Re-detect after upgrade
639
+ state.version = state.latestVersion;
640
+ state.outdated = false;
641
+ }
642
+
643
+ // Step 2: Upgrade target (if requested)
644
+ if (options.upgradeTarget && state.target) {
645
+ console.log(chalk.cyan.bold(`━━━ 🎯 Upgrading ${state.target}\n`));
646
+ await upgradeManager.upgradeTarget(state);
647
+ console.log(chalk.green('✓ Target upgrade complete\n'));
648
+ }
649
+
650
+ // Step 2.5: Check component integrity (only if we have valid state)
651
+ await checkComponentIntegrity(state, options);
652
+ }
653
+
654
+ // Step 3: Initialize (only if actually needed)
655
+ // Positive logic: should initialize when:
656
+ // - Not initialized yet (state?.initialized === false)
657
+ // - Sync mode (wipe and reinstall)
658
+ // - Repair mode (install missing components)
659
+ // - Init-only mode (user explicitly wants init)
660
+ const shouldInitialize =
661
+ !state?.initialized || // Not initialized yet
662
+ options.sync || // Sync reinstall
663
+ options.repair || // Repair missing components
664
+ options.initOnly; // Explicit init request
665
+
666
+ if (shouldInitialize) {
667
+ console.log(chalk.cyan.bold('━━━ 🚀 Initializing Project\n'));
668
+
669
+ // Import core init functions
670
+ const {
671
+ selectAndValidateTarget,
672
+ previewDryRun,
673
+ installComponents,
674
+ } = await import('./init-core.js');
675
+
676
+ try {
677
+ // In repair mode, use existing target from state
678
+ const targetForInit = options.repair && state?.target
679
+ ? state.target
680
+ : options.target;
681
+
682
+ // Prepare init options
683
+ const initOptions = {
684
+ target: targetForInit, // Use existing target in repair mode
685
+ verbose: options.verbose,
686
+ dryRun: options.dryRun,
687
+ clear: options.sync || false,
688
+ mcp: options.mcp !== false,
689
+ agents: options.agents !== false,
690
+ rules: options.rules !== false,
691
+ outputStyles: options.outputStyles !== false,
692
+ slashCommands: options.slashCommands !== false,
693
+ hooks: options.hooks !== false,
694
+ };
695
+
696
+ // Handle sync mode - delete template files first
697
+ if (options.sync && !options.dryRun) {
698
+ const { buildSyncManifest, showSyncPreview, confirmSync, executeSyncDelete } = await import('../utils/sync-utils.js');
699
+
700
+ // Need target to build manifest
701
+ const targetId = await selectAndValidateTarget(initOptions);
702
+ selectedTarget = targetId;
703
+
704
+ const targetOption = targetManager.getTarget(targetId);
705
+ if (targetOption._tag === 'None') {
706
+ throw new Error(`Target not found: ${targetId}`);
707
+ }
708
+
709
+ const target = targetOption.value;
710
+ const manifest = await buildSyncManifest(process.cwd(), target);
711
+
712
+ console.log(chalk.cyan.bold('━━━ 🔄 Synchronizing Files\n'));
713
+ showSyncPreview(manifest, process.cwd());
714
+
715
+ const confirmed = await confirmSync();
716
+ if (!confirmed) {
717
+ console.log(chalk.yellow('\n✗ Sync cancelled\n'));
718
+ process.exit(0);
719
+ }
720
+
721
+ const deletedCount = await executeSyncDelete(manifest);
722
+ console.log(chalk.green(`\n✓ Deleted ${deletedCount} files\n`));
723
+ } else {
724
+ // Select and validate target (will use existing in repair mode, or prompt if needed)
725
+ const targetId = await selectAndValidateTarget(initOptions);
726
+ selectedTarget = targetId; // Save for later use
727
+ }
728
+
729
+ // Dry run preview
730
+ if (options.dryRun) {
731
+ // Ensure we have a target ID for dry run
732
+ if (!selectedTarget) {
733
+ const targetId = await selectAndValidateTarget(initOptions);
734
+ selectedTarget = targetId;
735
+ }
736
+
737
+ console.log(
738
+ boxen(
739
+ chalk.yellow('⚠ Dry Run Mode') + chalk.dim('\nNo changes will be made to your project'),
740
+ {
741
+ padding: 1,
742
+ margin: { top: 0, bottom: 1, left: 0, right: 0 },
743
+ borderStyle: 'round',
744
+ borderColor: 'yellow',
745
+ }
746
+ )
747
+ );
748
+
749
+ await previewDryRun(selectedTarget, initOptions);
750
+
751
+ console.log(
752
+ '\n' +
753
+ boxen(chalk.green.bold('✓ Dry run complete'), {
754
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
755
+ margin: 0,
756
+ borderStyle: 'round',
757
+ borderColor: 'green',
758
+ }) +
759
+ '\n'
760
+ );
761
+
762
+ console.log(chalk.dim('✓ Initialization dry run complete\n'));
763
+ // Don't return - continue to show execution command
764
+ } else {
765
+ // Actually install components
766
+ // Ensure we have a target ID for installation
767
+ if (!selectedTarget) {
768
+ const targetId = await selectAndValidateTarget(initOptions);
769
+ selectedTarget = targetId;
770
+ }
771
+
772
+ const result = await installComponents(selectedTarget, initOptions);
773
+
774
+ console.log(chalk.green.bold('✓ Initialization complete\n'));
775
+ }
776
+ } catch (error) {
777
+ console.error(chalk.red.bold('✗ Initialization failed:'), error);
778
+ process.exit(1);
779
+ }
780
+ }
781
+
782
+ // Step 4: Launch target (if not init-only)
783
+ if (!options.initOnly) {
784
+ // Resolve target - use the target we just selected
785
+ let targetForResolution = options.target || state?.target || selectedTarget;
786
+
787
+ // If we just selected a target during init, use that
788
+ if (selectedTarget) {
789
+ targetForResolution = selectedTarget;
790
+ }
791
+
792
+ if (!targetForResolution) {
793
+ console.error(chalk.red.bold('✗ No target selected. Use --target or run init first.'));
794
+ process.exit(1);
795
+ }
796
+
797
+ const resolvedTarget = await targetManager.resolveTarget({
798
+ target: targetForResolution,
799
+ allowSelection: false, // Target should already be selected during init
800
+ });
801
+
802
+ console.log(chalk.cyan.bold(`━━━ 🎯 Launching ${resolvedTarget}\n`));
803
+
804
+ // Check if target supports command execution
805
+ const { getTargetsWithCommandSupport } = await import('../config/targets.js');
806
+ const supportedTargets = getTargetsWithCommandSupport().map(t => t.id);
807
+
808
+ if (!supportedTargets.includes(resolvedTarget)) {
809
+ console.log(chalk.red.bold('✗ Unsupported target platform\n'));
810
+ console.log(chalk.yellow(`Target '${resolvedTarget}' does not support agent execution.`));
811
+ console.log(chalk.cyan(`Supported platforms: ${supportedTargets.join(', ')}\n`));
812
+ console.log(chalk.dim('Tip: Use --target claude-code to specify Claude Code platform'));
813
+ console.log(chalk.dim('Example: bun dev:flow --target claude-code\n'));
814
+ process.exit(1);
815
+ }
816
+
817
+ // Claude Code handling - needs provider/agent setup
818
+ if (resolvedTarget === 'claude-code') {
819
+ // Handle provider and agent selection for Claude Code
820
+ const { SmartConfigService } = await import('../services/smart-config-service.js');
821
+
822
+ // Check if API keys are configured, if not, run initial setup
823
+ const { ConfigService } = await import('../services/config-service.js');
824
+ if (!(await ConfigService.hasInitialSetup())) {
825
+ console.log(chalk.cyan('🔑 First-time setup for Claude Code\n'));
826
+ await SmartConfigService.initialSetup();
827
+ console.log(chalk.green('✓ Setup complete!\n'));
828
+ }
829
+
830
+ const runtimeChoices = await SmartConfigService.selectRuntimeChoices({
831
+ selectProvider: options.selectProvider,
832
+ selectAgent: options.selectAgent,
833
+ useDefaults: options.useDefaults,
834
+ provider: options.provider,
835
+ agent: options.agent,
836
+ });
837
+
838
+ // Setup environment with selected provider
839
+ await SmartConfigService.setupEnvironment(runtimeChoices.provider!);
840
+
841
+ // Use selected agent
842
+ options.agent = runtimeChoices.agent;
843
+ }
844
+
845
+ const agent = options.agent || 'coder';
846
+ const verbose = options.verbose || false;
847
+
848
+ if (verbose || options.runOnly || !options.quick) {
849
+ console.log(` 🀖 Agent: ${chalk.cyan(agent)}`);
850
+ console.log(` 🎯 Target: ${chalk.cyan(resolvedTarget)}`);
851
+ if (prompt) {
852
+ console.log(` 💬 Prompt: ${chalk.dim(prompt)}\n`);
853
+ } else {
854
+ console.log(` 💬 Mode: ${chalk.dim('Interactive')}\n`);
855
+ }
856
+ }
857
+
858
+ // Load agent and prepare prompts
859
+ const agentContent = await loadAgentContent(agent, options.agentFile);
860
+ const agentInstructions = extractAgentInstructions(agentContent);
861
+ const systemPrompt = `AGENT INSTRUCTIONS:\n${agentInstructions}`;
862
+
863
+ const userPrompt = prompt?.trim() || '';
864
+
865
+ // Environment should already be set up by SmartConfigService in main flow
866
+ // No need to setup again here
867
+
868
+ // Run options
869
+ const runOptions: RunCommandOptions = {
870
+ target: resolvedTarget,
871
+ verbose,
872
+ dryRun: options.dryRun,
873
+ agent,
874
+ agentFile: options.agentFile,
875
+ prompt,
876
+ print: options.print,
877
+ continue: options.continue,
878
+ };
879
+
880
+ try {
881
+ await executeTargetCommand(resolvedTarget, systemPrompt, userPrompt, runOptions);
882
+ } catch (error) {
883
+ console.error(chalk.red.bold('\n✗ Launch failed:'), error);
884
+ process.exit(1);
885
+ }
886
+
887
+ if (!options.dryRun) {
888
+ console.log(chalk.dim('━━━\n'));
889
+ console.log(chalk.green('✓ Session complete\n'));
890
+ }
891
+ } else {
892
+ console.log(chalk.dim('✓ Init-only mode, skipping execution\n'));
893
+ }
894
+ }
895
+
896
+ /**
897
+ * Smart flow command
898
+ */
899
+ export const flowCommand = new Command('flow')
900
+ .description('Intelligent development flow (auto-detect state and act accordingly)')
901
+
902
+ // Smart options
903
+ .option('--init-only', 'Only initialize, do not run')
904
+ .option('--run-only', 'Only run, skip initialization')
905
+ .option('--sync', 'Synchronize with Flow templates (delete and re-install template files)')
906
+ .option('--upgrade', 'Upgrade Sylphx Flow to latest version')
907
+ .option('--upgrade-target', 'Upgrade target platform (Claude Code/OpenCode)')
908
+
909
+ // Smart configuration options
910
+ .option('--quick', 'Quick mode: use saved defaults and skip all prompts')
911
+ .option('--select-provider', 'Prompt to select provider each run')
912
+ .option('--select-agent', 'Prompt to select agent each run')
913
+ .option('--use-defaults', 'Skip prompts, use saved defaults')
914
+ .option('--provider <provider>', 'Override provider for this run (anthropic|z.ai|kimi)')
915
+
916
+ // Init options
917
+ .option('--target <type>', 'Target platform (opencode, claude-code, auto-detect)')
918
+ .option('--verbose', 'Show detailed output')
919
+ .option('--dry-run', 'Show what would be done without making changes')
920
+ .option('--no-mcp', 'Skip MCP installation')
921
+ .option('--no-agents', 'Skip agents installation')
922
+ .option('--no-rules', 'Skip rules installation')
923
+ .option('--no-output-styles', 'Skip output styles installation')
924
+ .option('--no-slash-commands', 'Skip slash commands installation')
925
+ .option('--no-hooks', 'Skip hooks setup')
926
+
927
+ // Run options
928
+ .option('--agent <name>', 'Agent to use (default: coder)', 'coder')
929
+ .option('--agent-file <path>', 'Load agent from specific file')
930
+ .option('-p, --print', 'Headless print mode (output only, no interactive)')
931
+ .option('-c, --continue', 'Continue previous conversation (requires print mode)')
932
+
933
+ // Prompt argument
934
+ .argument('[prompt]', 'Prompt to execute with agent (optional, supports @file.txt for file input)')
935
+
936
+ .action(async (prompt, options) => {
937
+ await executeFlow(prompt, options);
938
+ });
939
+
940
+ /**
941
+ * Setup command - alias for `flow --init-only`
942
+ * Kept for backward compatibility, but users should prefer `flow --init-only`
943
+ */
944
+ export const setupCommand = new Command('setup')
945
+ .description('Initialize project configuration (alias for: flow --init-only)')
946
+ .action(async () => {
947
+ console.log(chalk.yellow('ℹ The "setup" command is deprecated.'));
948
+ console.log(chalk.yellow(' Please use: flow --init-only\n'));
949
+
950
+ showWelcome();
951
+
952
+ // Initialize project with default target
953
+ const { runInit } = await import('./init-command.js');
954
+ await runInit({
955
+ target: undefined, // Let user choose
956
+ verbose: false,
957
+ dryRun: false,
958
+ clear: false,
959
+ mcp: true,
960
+ agents: true,
961
+ rules: true,
962
+ outputStyles: true,
963
+ slashCommands: true,
964
+ hooks: true,
965
+ helpOption: () => {},
966
+ });
967
+
968
+ console.log(chalk.green('\n✅ Setup complete!'));
969
+ console.log(chalk.dim('\nNext time, use: flow --init-only'));
970
+ });
971
+
972
+ /**
973
+ * Status command - show project status
974
+ */
975
+ export const statusCommand = new Command('status')
976
+ .description('Show project status and configuration')
977
+ .option('--verbose', 'Show detailed information')
978
+ .action(async (options) => {
979
+ const detector = new StateDetector();
980
+ const state = await detector.detect();
981
+
982
+ showWelcome();
983
+ await showStatus(state);
984
+
985
+ // Show detailed info if verbose
986
+ if (options.verbose) {
987
+ console.log(chalk.cyan.bold('\n📋 诊细信息\n'));
988
+
989
+ // 配眮文件内容
990
+ try {
991
+ const { getProjectSettingsFile } = await import('../config/constants.js');
992
+ const configPath = path.join(process.cwd(), getProjectSettingsFile());
993
+ const config = JSON.parse(await fs.readFile(configPath, 'utf-8'));
994
+ console.log('配眮文件:', JSON.stringify(config, null, 2));
995
+ } catch {
996
+ console.log('配眮文件: 䞍存圚');
997
+ }
998
+ }
999
+ });
1000
+
1001
+ /**
1002
+ * Doctor command - diagnose and fix issues
1003
+ */
1004
+ export const doctorCommand = new Command('doctor')
1005
+ .description('Diagnose and fix common issues')
1006
+ .option('--fix', 'Automatically fix issues')
1007
+ .option('--verbose', 'Show detailed diagnostics')
1008
+ .action(async (options) => {
1009
+ console.log(chalk.cyan.bold('🔍 诊断项目\n'));
1010
+
1011
+ const detector = new StateDetector();
1012
+ const state = await detector.detect();
1013
+
1014
+ let issuesFound = false;
1015
+
1016
+ // Check 1: Claude Code installation
1017
+ console.log('检查 Claude Code 安装...');
1018
+ try {
1019
+ const { exec } = await import('node:child_process');
1020
+ const { promisify } = await import('node:util');
1021
+ const execAsync = promisify(exec);
1022
+ await execAsync('which claude');
1023
+ console.log(chalk.green(' ✓ Claude Code 已安装'));
1024
+ } catch {
1025
+ console.log(chalk.red(' ✗ Claude Code 未安装'));
1026
+ console.log(chalk.dim(' 运行: npm install -g @anthropic-ai/claude-code'));
1027
+ issuesFound = true;
1028
+ }
1029
+
1030
+ // Check 2: Configuration
1031
+ console.log('\n检查配眮...');
1032
+ if (state.corrupted) {
1033
+ console.log(chalk.red(' ✗ 配眮损坏'));
1034
+ issuesFound = true;
1035
+
1036
+ if (options.fix) {
1037
+ console.log(chalk.yellow(' 🔄 正圚修倍...'));
1038
+ // Run flow with clean flag
1039
+ const { executeFlow } = await import('./flow-command.js');
1040
+ await executeFlow(undefined, { clean: true });
1041
+ console.log(chalk.green(' ✓ 已修倍'));
1042
+ }
1043
+ } else if (!state.initialized) {
1044
+ console.log(chalk.yellow(' ⚠ 项目未初始化'));
1045
+ issuesFound = true;
1046
+ } else {
1047
+ console.log(chalk.green(' ✓ 配眮正垞'));
1048
+ }
1049
+
1050
+ // Check 3: Components
1051
+ console.log('\n检查组件...');
1052
+ Object.entries(state.components).forEach(([name, component]) => {
1053
+ const status = component.installed ? chalk.green('✓') : chalk.red('✗');
1054
+ const count = ('count' in component && component.count) ? ` (${component.count})` : '';
1055
+ console.log(` ${status} ${name}${count}`);
1056
+ });
1057
+
1058
+ // Summary
1059
+ console.log('\n' + chalk.bold('结果:'));
1060
+ if (!issuesFound) {
1061
+ console.log(chalk.green('✓ 所有检查通过'));
1062
+ } else if (options.fix) {
1063
+ console.log(chalk.green('✓ 所有问题已修倍'));
1064
+ } else {
1065
+ console.log(chalk.yellow('⚠ 发现问题运行加 --fix 参数自劚修倍'));
1066
+ }
1067
+ });
1068
+
1069
+ /**
1070
+ * Upgrade command - upgrade components
1071
+ */
1072
+ export const upgradeCommand = new Command('upgrade')
1073
+ .description('Upgrade Sylphx Flow and components')
1074
+ .option('--check', 'Only check for updates, do not upgrade')
1075
+ .option('--components', 'Upgrade components (agents, rules, etc)', true)
1076
+ .option('--target', 'Upgrade target platform (Claude Code/OpenCode)')
1077
+ .option('--verbose', 'Show detailed output')
1078
+ .action(async (options) => {
1079
+ console.log(chalk.cyan.bold('📊 检查曎新\n'));
1080
+
1081
+ const detector = new StateDetector();
1082
+ const upgradeManager = new UpgradeManager();
1083
+
1084
+ const updates = await upgradeManager.checkUpdates();
1085
+
1086
+ if (!updates.flowUpdate && !updates.targetUpdate) {
1087
+ console.log(chalk.green('✓ 所有组件已是最新版本\n'));
1088
+ return;
1089
+ }
1090
+
1091
+ if (updates.flowVersion) {
1092
+ console.log(`Sylphx Flow: ${updates.flowVersion.current} → ${chalk.green(updates.flowVersion.latest)}`);
1093
+ }
1094
+
1095
+ if (updates.targetVersion) {
1096
+ console.log(`${updates.targetVersion.current ? 'claude-code' : 'target'}: ${updates.targetVersion.current} → ${chalk.green(updates.targetVersion.latest)}`);
1097
+ }
1098
+
1099
+ // Check only
1100
+ if (options.check) {
1101
+ console.log('\n' + chalk.dim('䜿甚 --no-check 或省略参数进行升级'));
1102
+ return;
1103
+ }
1104
+
1105
+ // Confirm upgrade
1106
+ const { default: inquirer } = await import('inquirer');
1107
+ const { confirm } = await inquirer.prompt([
1108
+ {
1109
+ type: 'confirm',
1110
+ name: 'confirm',
1111
+ message: '确讀升级到最新版本?',
1112
+ default: true,
1113
+ },
1114
+ ]);
1115
+
1116
+ if (!confirm) {
1117
+ console.log(chalk.dim('\n升级已取消'));
1118
+ return;
1119
+ }
1120
+
1121
+ // Perform upgrade
1122
+ console.log('');
1123
+
1124
+ const state = await detector.detect();
1125
+
1126
+ if (updates.flowUpdate) {
1127
+ console.log(chalk.cyan.bold('\n━ 升级 Sylphx Flow\n'));
1128
+ await upgradeManager.upgradeFlow(state);
1129
+ }
1130
+
1131
+ if (updates.targetUpdate && options.target) {
1132
+ console.log(chalk.cyan.bold('\n━ 升级 Target\n'));
1133
+ await upgradeManager.upgradeTarget(state);
1134
+ }
1135
+
1136
+ console.log(chalk.green('\n✓ 升级完成\n'));
1137
+ });