@sylphx/flow 3.18.0 → 3.19.1

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 (57) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/package.json +1 -3
  3. package/src/commands/flow/execute-v2.ts +126 -128
  4. package/src/commands/flow-command.ts +52 -42
  5. package/src/config/index.ts +0 -20
  6. package/src/config/targets.ts +1 -1
  7. package/src/core/__tests__/backup-restore.test.ts +1 -1
  8. package/src/core/__tests__/cleanup-handler.test.ts +292 -0
  9. package/src/core/__tests__/git-stash-manager.test.ts +246 -0
  10. package/src/core/__tests__/secrets-manager.test.ts +126 -0
  11. package/src/core/__tests__/session-cleanup.test.ts +147 -0
  12. package/src/core/agent-loader.ts +2 -2
  13. package/src/core/attach-manager.ts +12 -78
  14. package/src/core/backup-manager.ts +8 -20
  15. package/src/core/cleanup-handler.ts +187 -11
  16. package/src/core/flow-executor.ts +139 -126
  17. package/src/core/functional/index.ts +0 -11
  18. package/src/core/git-stash-manager.ts +50 -68
  19. package/src/core/index.ts +1 -1
  20. package/src/core/project-manager.ts +26 -43
  21. package/src/core/secrets-manager.ts +15 -18
  22. package/src/core/session-manager.ts +32 -41
  23. package/src/core/state-detector.ts +4 -15
  24. package/src/core/target-manager.ts +6 -3
  25. package/src/core/target-resolver.ts +14 -9
  26. package/src/core/template-loader.ts +7 -33
  27. package/src/core/upgrade-manager.ts +5 -16
  28. package/src/index.ts +7 -36
  29. package/src/services/auto-upgrade.ts +6 -14
  30. package/src/services/config-service.ts +7 -23
  31. package/src/services/index.ts +1 -1
  32. package/src/targets/claude-code.ts +24 -109
  33. package/src/targets/functional/claude-code-logic.ts +47 -103
  34. package/src/targets/opencode.ts +63 -197
  35. package/src/targets/shared/mcp-transforms.ts +20 -43
  36. package/src/targets/shared/target-operations.ts +1 -54
  37. package/src/types/agent.types.ts +5 -3
  38. package/src/types/mcp.types.ts +38 -1
  39. package/src/types/target.types.ts +4 -24
  40. package/src/types.ts +4 -0
  41. package/src/utils/agent-enhancer.ts +1 -1
  42. package/src/utils/config/target-config.ts +8 -14
  43. package/src/utils/config/target-utils.ts +1 -50
  44. package/src/utils/errors.ts +13 -0
  45. package/src/utils/files/file-operations.ts +16 -0
  46. package/src/utils/files/sync-utils.ts +5 -5
  47. package/src/utils/index.ts +1 -1
  48. package/src/utils/object-utils.ts +10 -2
  49. package/src/utils/security/secret-utils.ts +2 -2
  50. package/src/core/error-handling.ts +0 -512
  51. package/src/core/functional/async.ts +0 -101
  52. package/src/core/functional/either.ts +0 -109
  53. package/src/core/functional/error-handler.ts +0 -135
  54. package/src/core/functional/pipe.ts +0 -189
  55. package/src/core/functional/validation.ts +0 -138
  56. package/src/types/mcp-config.types.ts +0 -448
  57. package/src/utils/error-handler.ts +0 -53
package/CHANGELOG.md CHANGED
@@ -1,5 +1,71 @@
1
1
  # @sylphx/flow
2
2
 
3
+ ## 3.19.1 (2026-02-07)
4
+
5
+ ### 🐛 Bug Fixes
6
+
7
+ - add missing .js extensions to barrel exports ([54c4bb1](https://github.com/SylphxAI/flow/commit/54c4bb1bba79eb5b2e0d8ba06a7bf1486be5302c))
8
+ - replace silent catch blocks with debug logging ([fe5a646](https://github.com/SylphxAI/flow/commit/fe5a646c180e87242034228c2472d1a0a1bd8dda))
9
+
10
+ ### ♻ Refactoring
11
+
12
+ - rename AgentMetadata to AgentDefinition in agent.types.ts ([01c16c9](https://github.com/SylphxAI/flow/commit/01c16c984b3e7ec7d318016e018d7db957ea140a))
13
+ - extract readJsonFileSafe for repeated JSON-read-with-fallback pattern ([22088ed](https://github.com/SylphxAI/flow/commit/22088ed4d24cbfda2dc70f77dd2ae3e6f1ac111e))
14
+ - remove unused functional modules and fix config barrel exports ([2f9f5b2](https://github.com/SylphxAI/flow/commit/2f9f5b242b9d9149630f8d933db788f862a8cebb))
15
+ - extract convertServerSecrets from opencode writeConfig ([af7eac7](https://github.com/SylphxAI/flow/commit/af7eac7211d9067580a476683610da7f73c962e0))
16
+ - extract health check functions from doctorCommand ([7debe50](https://github.com/SylphxAI/flow/commit/7debe50559c25c4e2ee2486eb1bfc329c32b178c))
17
+ - extract tryJoinExistingSession and createNewSession from flow-executor ([5e25da8](https://github.com/SylphxAI/flow/commit/5e25da895e9bc216865f1e9810c21effd0878322))
18
+ - extract selectTarget and loadAgent from executeFlowV2 ([9735cf0](https://github.com/SylphxAI/flow/commit/9735cf04c1ef43f6842b5ac5deafc3b0e04aecbb))
19
+
20
+ ### 💅 Styles
21
+
22
+ - **flow:** format package.json with biome (tabs → spaces) ([7aeac4c](https://github.com/SylphxAI/flow/commit/7aeac4cf1d946ec3af82ae20b6215482d989a38f))
23
+
24
+ ### 🔧 Chores
25
+
26
+ - remove 35 unused dependencies ([55318b0](https://github.com/SylphxAI/flow/commit/55318b0f3215bf47d1875bd07b9fcdad094620c4))
27
+
28
+ ## 3.19.0 (2026-02-07)
29
+
30
+ Performance, stability, and test coverage overhaul
31
+
32
+ **Performance:**
33
+ - Replace blocking `execSync('which')` with async `exec()` + `Promise.all()`
34
+ - Batch git operations (N shell spawns → 1 per operation)
35
+ - Parallelize I/O across startup path (`Promise.all` for mkdir, unlink, fs.cp)
36
+ - Skip heavy cleanup scans on startup when not needed (24h marker)
37
+
38
+ **Bug fixes:**
39
+ - Fix signal handler conflict causing storage accumulation (root cause: `process.exit(0)` preempted async cleanup)
40
+ - Fix skip-worktree flag leak on crash (read from git index instead of in-memory state)
41
+ - Fix secrets never cleared on session end
42
+ - Fix constructor ordering bug (`gitStashManager` used before initialized)
43
+ - Centralize all cleanup into CleanupHandler (signal, manual, crash recovery paths)
44
+
45
+ **Storage lifecycle:**
46
+ - Add orphaned project detection and cleanup
47
+ - Add session history pruning (keep last 50)
48
+ - Add periodic cleanup gating (at most once per 24h)
49
+
50
+ **Tests:**
51
+ - 30 → 81 tests (170% increase)
52
+ - New test suites: cleanup-handler, git-stash-manager, session-cleanup, secrets-manager
53
+
54
+ **Cleanup:**
55
+ - Remove 432 lines of dead code across 26 files
56
+
57
+ ### ✹ Features
58
+
59
+ - **flow:** performance, stability, and test coverage overhaul ([16c461f](https://github.com/SylphxAI/flow/commit/16c461f509d800d7456d933d3153078ded6d5669))
60
+
61
+ ### 🐛 Bug Fixes
62
+
63
+ - **flow:** inject env vars into spawned process instead of writing files ([51f037e](https://github.com/SylphxAI/flow/commit/51f037e0a5fed8b24271528efa9cc16f0409bb2a))
64
+
65
+ ### 💅 Styles
66
+
67
+ - **flow:** format package.json with biome (tabs → spaces) ([3206303](https://github.com/SylphxAI/flow/commit/320630301d8b85813f96b36e737b5d0b01b23793))
68
+
3
69
  ## 3.18.0 (2026-02-07)
4
70
 
5
71
  Enable agent teams by default for Claude Code
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylphx/flow",
3
- "version": "3.18.0",
3
+ "version": "3.19.1",
4
4
  "description": "One CLI to rule them all. Unified orchestration layer for AI coding assistants. Auto-detection, auto-installation, auto-upgrade.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,11 +26,9 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@clack/prompts": "^0.9.0",
29
- "boxen": "^8.0.1",
30
29
  "chalk": "^5.6.2",
31
30
  "commander": "^14.0.2",
32
31
  "debug": "^4.4.3",
33
- "gradient-string": "^3.0.0",
34
32
  "gray-matter": "^4.0.3",
35
33
  "pino": "^9.0.0",
36
34
  "pino-pretty": "^11.0.0",
@@ -3,11 +3,15 @@
3
3
  * Minimal, modern CLI output design
4
4
  */
5
5
 
6
- import fs from 'node:fs/promises';
7
6
  import path from 'node:path';
8
7
  import { fileURLToPath } from 'node:url';
9
8
  import chalk from 'chalk';
9
+ import createDebug from 'debug';
10
10
  import { FlowExecutor } from '../../core/flow-executor.js';
11
+ import { readJsonFileSafe } from '../../utils/files/file-operations.js';
12
+
13
+ const debug = createDebug('flow:execute');
14
+
11
15
  import { targetManager } from '../../core/target-manager.js';
12
16
  import { AutoUpgrade } from '../../services/auto-upgrade.js';
13
17
  import { GlobalConfigService } from '../../services/global-config.js';
@@ -15,8 +19,7 @@ import { TargetInstaller } from '../../services/target-installer.js';
15
19
  import type { RunCommandOptions } from '../../types.js';
16
20
  import { extractAgentInstructions, loadAgentContent } from '../../utils/agent-enhancer.js';
17
21
  import { showAttachSummary, showHeader } from '../../utils/display/banner.js';
18
- import { CLIError } from '../../utils/error-handler.js';
19
- import { UserCancelledError } from '../../utils/errors.js';
22
+ import { CLIError, UserCancelledError } from '../../utils/errors.js';
20
23
  import { log, promptConfirm, promptSelect } from '../../utils/prompts/index.js';
21
24
  import { ensureTargetInstalled, promptForTargetSelection } from '../../utils/target-selection.js';
22
25
  import { resolvePrompt } from './prompt.js';
@@ -29,13 +32,9 @@ const __dirname = path.dirname(__filename);
29
32
  * Get Flow version from package.json
30
33
  */
31
34
  async function getFlowVersion(): Promise<string> {
32
- try {
33
- const packageJsonPath = path.join(__dirname, '..', '..', '..', 'package.json');
34
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
35
- return packageJson.version;
36
- } catch {
37
- return 'unknown';
38
- }
35
+ const packageJsonPath = path.join(__dirname, '..', '..', '..', 'package.json');
36
+ const pkg = await readJsonFileSafe<{ version?: string }>(packageJsonPath, {});
37
+ return pkg.version ?? 'unknown';
39
38
  }
40
39
 
41
40
  /**
@@ -138,6 +137,106 @@ function executeTargetCommand(
138
137
  return target.executeCommand(systemPrompt, userPrompt, options);
139
138
  }
140
139
 
140
+ /**
141
+ * Select target based on user settings, installed targets, and prompts
142
+ */
143
+ async function selectTarget(
144
+ installedTargets: string[],
145
+ settings: import('../../services/global-config.js').GlobalSettings,
146
+ configService: GlobalConfigService,
147
+ targetInstaller: TargetInstaller
148
+ ): Promise<string | null> {
149
+ if (settings.defaultTarget === 'ask-every-time') {
150
+ const targetId = await promptForTargetSelection(
151
+ installedTargets,
152
+ 'Select AI CLI:',
153
+ 'execution'
154
+ );
155
+ const installed = await ensureTargetInstalled(targetId, targetInstaller, installedTargets);
156
+ if (!installed) {
157
+ process.exit(1);
158
+ }
159
+ return targetId;
160
+ }
161
+
162
+ if (!settings.defaultTarget) {
163
+ if (installedTargets.length === 1) {
164
+ const targetId = installedTargets[0];
165
+ settings.defaultTarget = targetId as 'claude-code' | 'opencode';
166
+ await configService.saveSettings(settings);
167
+ return targetId;
168
+ }
169
+
170
+ const targetId = await promptForTargetSelection(
171
+ installedTargets,
172
+ 'Select AI CLI:',
173
+ 'execution'
174
+ );
175
+ const installed = await ensureTargetInstalled(targetId, targetInstaller, installedTargets);
176
+ if (!installed) {
177
+ process.exit(1);
178
+ }
179
+
180
+ const rememberChoice = await promptConfirm({
181
+ message: 'Remember this choice?',
182
+ initialValue: true,
183
+ });
184
+ if (rememberChoice) {
185
+ settings.defaultTarget = targetId as 'claude-code' | 'opencode';
186
+ await configService.saveSettings(settings);
187
+ }
188
+ return targetId;
189
+ }
190
+
191
+ // User has a specific target preference
192
+ const targetId = settings.defaultTarget;
193
+ if (!installedTargets.includes(targetId)) {
194
+ const installation = targetInstaller.getInstallationInfo(targetId);
195
+ log.warn(`${installation?.name} not installed`);
196
+ const installed = await targetInstaller.install(targetId, true);
197
+ if (!installed) {
198
+ log.error('Cannot proceed: installation failed');
199
+ process.exit(1);
200
+ }
201
+ }
202
+ return targetId;
203
+ }
204
+
205
+ /**
206
+ * Load agent and build system prompt
207
+ */
208
+ async function loadAgent(
209
+ options: FlowOptions,
210
+ settings: import('../../services/global-config.js').GlobalSettings,
211
+ configService: GlobalConfigService
212
+ ): Promise<{ agent: string; systemPrompt: string }> {
213
+ const flowConfig = await configService.loadFlowConfig();
214
+ let agent = options.agent || settings.defaultAgent || 'builder';
215
+
216
+ if (!flowConfig.agents[agent]?.enabled) {
217
+ const enabledAgents = await configService.getEnabledAgents();
218
+ agent = enabledAgents.length > 0 ? enabledAgents[0] : 'builder';
219
+ }
220
+
221
+ const [enabledRules, enabledOutputStyles] = await Promise.all([
222
+ configService.getEnabledRules(),
223
+ configService.getEnabledOutputStyles(),
224
+ ]);
225
+
226
+ const agentContent = await loadAgentContent(
227
+ agent,
228
+ options.agentFile,
229
+ enabledRules,
230
+ enabledOutputStyles
231
+ );
232
+ const agentInstructions = extractAgentInstructions(agentContent);
233
+
234
+ return {
235
+ agent,
236
+ systemPrompt: `AGENT INSTRUCTIONS:\n${agentInstructions}`,
237
+ };
238
+ }
239
+
141
240
  /**
142
241
  * Main flow execution with attach mode (V2) - Minimal output design
143
242
  */
@@ -158,79 +257,12 @@ export async function executeFlowV2(
158
257
  getFlowVersion(),
159
258
  ]);
160
259
 
161
- let selectedTargetId: string | null = null;
162
-
163
- const isAskEveryTime = settings.defaultTarget === 'ask-every-time';
164
- const hasNoSetting = !settings.defaultTarget;
165
- const hasSpecificTarget = settings.defaultTarget && settings.defaultTarget !== 'ask-every-time';
166
-
167
- if (isAskEveryTime) {
168
- // User explicitly wants to be asked every time
169
- selectedTargetId = await promptForTargetSelection(
170
- installedTargets,
171
- 'Select AI CLI:',
172
- 'execution'
173
- );
174
-
175
- const installed = await ensureTargetInstalled(
176
- selectedTargetId,
177
- targetInstaller,
178
- installedTargets
179
- );
180
-
181
- if (!installed) {
182
- process.exit(1);
183
- }
184
- } else if (hasNoSetting) {
185
- // No setting - use auto-detection or prompt
186
- if (installedTargets.length === 1) {
187
- // Single target: auto-select and save silently
188
- selectedTargetId = installedTargets[0];
189
- settings.defaultTarget = selectedTargetId as 'claude-code' | 'opencode';
190
- await configService.saveSettings(settings);
191
- } else {
192
- // Multiple targets: prompt and ask to remember
193
- selectedTargetId = await promptForTargetSelection(
194
- installedTargets,
195
- 'Select AI CLI:',
196
- 'execution'
197
- );
198
-
199
- const installed = await ensureTargetInstalled(
200
- selectedTargetId,
201
- targetInstaller,
202
- installedTargets
203
- );
204
-
205
- if (!installed) {
206
- process.exit(1);
207
- }
208
-
209
- const rememberChoice = await promptConfirm({
210
- message: 'Remember this choice?',
211
- initialValue: true,
212
- });
213
-
214
- if (rememberChoice) {
215
- settings.defaultTarget = selectedTargetId as 'claude-code' | 'opencode';
216
- await configService.saveSettings(settings);
217
- }
218
- }
219
- } else if (hasSpecificTarget) {
220
- // User has a specific target preference
221
- selectedTargetId = settings.defaultTarget;
222
-
223
- if (!installedTargets.includes(selectedTargetId)) {
224
- const installation = targetInstaller.getInstallationInfo(selectedTargetId);
225
- log.warn(`${installation?.name} not installed`);
226
- const installed = await targetInstaller.install(selectedTargetId, true);
227
-
228
- if (!installed) {
229
- log.error('Cannot proceed: installation failed');
230
- process.exit(1);
231
- }
232
- }
233
- }
260
+ const selectedTargetId = await selectTarget(
261
+ installedTargets,
262
+ settings,
263
+ configService,
264
+ targetInstaller
265
+ );
234
266
 
235
267
  // Get target name for header
236
268
  const targetInstallation = targetInstaller.getInstallationInfo(selectedTargetId);
@@ -239,63 +271,34 @@ export async function executeFlowV2(
239
271
  // Show minimal header
240
272
  showHeader(version, targetName);
241
273
 
242
- // Step 2: Start background auto-upgrade (non-blocking)
274
+ // Start background auto-upgrade (non-blocking)
243
275
  new AutoUpgrade().start();
244
276
 
245
- // Create executor
246
277
  const executor = new FlowExecutor();
247
278
 
248
- // Step 3: Execute attach mode lifecycle
249
279
  try {
250
280
  // Attach Flow environment (backup → attach → register cleanup)
251
281
  const attachResult = await executor.execute(projectPath, {
252
282
  verbose: options.verbose,
253
283
  skipBackup: false,
254
284
  skipSecrets: false,
255
- skipProjectDocs: true, // Use /init command for project docs
285
+ skipProjectDocs: true,
256
286
  merge: options.merge || false,
257
287
  });
258
288
 
259
- // Show attach summary
260
289
  showAttachSummary(attachResult);
261
290
 
262
- const targetId = selectedTargetId;
263
-
264
291
  // Provider selection (Claude Code only, silent unless prompting)
265
- if (targetId === 'claude-code') {
292
+ if (selectedTargetId === 'claude-code') {
266
293
  await selectProvider(configService);
267
294
  }
268
295
 
269
- // Determine which agent to use
270
- const flowConfig = await configService.loadFlowConfig();
271
- let agent = options.agent || settings.defaultAgent || 'builder';
272
-
273
- // Check if agent is enabled (silent fallback)
274
- if (!flowConfig.agents[agent]?.enabled) {
275
- const enabledAgents = await configService.getEnabledAgents();
276
- agent = enabledAgents.length > 0 ? enabledAgents[0] : 'builder';
277
- }
278
-
279
- // Load agent content (parallel fetch rules and styles)
280
- const [enabledRules, enabledOutputStyles] = await Promise.all([
281
- configService.getEnabledRules(),
282
- configService.getEnabledOutputStyles(),
283
- ]);
284
-
285
- const agentContent = await loadAgentContent(
286
- agent,
287
- options.agentFile,
288
- enabledRules,
289
- enabledOutputStyles
290
- );
291
- const agentInstructions = extractAgentInstructions(agentContent);
292
-
293
- const systemPrompt = `AGENT INSTRUCTIONS:\n${agentInstructions}`;
296
+ // Load agent and build prompts
297
+ const { agent, systemPrompt } = await loadAgent(options, settings, configService);
294
298
  const userPrompt = prompt?.trim() || '';
295
299
 
296
- // Prepare run options
297
300
  const runOptions: RunCommandOptions = {
298
- target: targetId,
301
+ target: selectedTargetId,
299
302
  verbose: options.verbose || false,
300
303
  dryRun: options.dryRun || false,
301
304
  agent,
@@ -305,30 +308,25 @@ export async function executeFlowV2(
305
308
  continue: options.continue,
306
309
  };
307
310
 
308
- // Step 4: Execute command
309
- await executeTargetCommand(targetId, systemPrompt, userPrompt, runOptions);
310
-
311
- // Step 5: Cleanup (silent)
311
+ await executeTargetCommand(selectedTargetId, systemPrompt, userPrompt, runOptions);
312
312
  await executor.cleanup(projectPath);
313
313
  } catch (error) {
314
- // Handle user cancellation gracefully
315
314
  if (error instanceof UserCancelledError) {
316
315
  log.warn('Cancelled');
317
316
  try {
318
317
  await executor.cleanup(projectPath);
319
- } catch {
320
- // Silent cleanup failure
318
+ } catch (cleanupError) {
319
+ debug('cleanup after cancel failed:', cleanupError);
321
320
  }
322
321
  process.exit(0);
323
322
  }
324
323
 
325
324
  console.error(chalk.red('\n Error:'), error);
326
325
 
327
- // Ensure cleanup even on error
328
326
  try {
329
327
  await executor.cleanup(projectPath);
330
- } catch {
331
- // Silent cleanup failure
328
+ } catch (cleanupError) {
329
+ debug('cleanup after error failed:', cleanupError);
332
330
  }
333
331
 
334
332
  throw error;
@@ -81,6 +81,53 @@ export const statusCommand = new Command('status')
81
81
  }
82
82
  });
83
83
 
84
+ async function checkClaudeCodeInstalled(): Promise<boolean> {
85
+ console.log('检查 Claude Code 安装...');
86
+ try {
87
+ const { exec } = await import('node:child_process');
88
+ const { promisify } = await import('node:util');
89
+ const execAsync = promisify(exec);
90
+ await execAsync('which claude');
91
+ console.log(chalk.green(' ✓ Claude Code 已安装'));
92
+ return true;
93
+ } catch {
94
+ console.log(chalk.red(' ✗ Claude Code 未安装'));
95
+ console.log(chalk.dim(' 运行: npm install -g @anthropic-ai/claude-code'));
96
+ return false;
97
+ }
98
+ }
99
+
100
+ async function checkConfiguration(
101
+ state: Awaited<ReturnType<StateDetector['detect']>>,
102
+ fix: boolean
103
+ ): Promise<boolean> {
104
+ console.log('\n检查配眮...');
105
+ if (state.corrupted) {
106
+ console.log(chalk.red(' ✗ 配眮损坏'));
107
+ if (fix) {
108
+ console.log(chalk.yellow(' 🔄 正圚修倍...'));
109
+ await executeFlow(undefined, { sync: true });
110
+ console.log(chalk.green(' ✓ 已修倍'));
111
+ }
112
+ return false;
113
+ }
114
+ if (state.initialized) {
115
+ console.log(chalk.green(' ✓ 配眮正垞'));
116
+ return true;
117
+ }
118
+ console.log(chalk.yellow(' ⚠ 项目未初始化'));
119
+ return false;
120
+ }
121
+
122
+ function checkComponents(state: Awaited<ReturnType<StateDetector['detect']>>): void {
123
+ console.log('\n检查组件...');
124
+ for (const [name, component] of Object.entries(state.components)) {
125
+ const status = component.installed ? chalk.green('✓') : chalk.red('✗');
126
+ const count = 'count' in component && component.count ? ` (${component.count})` : '';
127
+ console.log(` ${status} ${name}${count}`);
128
+ }
129
+ }
130
+
84
131
  /**
85
132
  * Doctor command - diagnose and fix issues
86
133
  */
@@ -94,49 +141,12 @@ export const doctorCommand = new Command('doctor')
94
141
  const detector = new StateDetector();
95
142
  const state = await detector.detect();
96
143
 
97
- let issuesFound = false;
98
-
99
- // Check 1: Claude Code installation
100
- console.log('检查 Claude Code 安装...');
101
- try {
102
- const { exec } = await import('node:child_process');
103
- const { promisify } = await import('node:util');
104
- const execAsync = promisify(exec);
105
- await execAsync('which claude');
106
- console.log(chalk.green(' ✓ Claude Code 已安装'));
107
- } catch {
108
- console.log(chalk.red(' ✗ Claude Code 未安装'));
109
- console.log(chalk.dim(' 运行: npm install -g @anthropic-ai/claude-code'));
110
- issuesFound = true;
111
- }
112
-
113
- // Check 2: Configuration
114
- console.log('\n检查配眮...');
115
- if (state.corrupted) {
116
- console.log(chalk.red(' ✗ 配眮损坏'));
117
- issuesFound = true;
118
-
119
- if (options.fix) {
120
- console.log(chalk.yellow(' 🔄 正圚修倍...'));
121
- await executeFlow(undefined, { sync: true } as FlowOptions);
122
- console.log(chalk.green(' ✓ 已修倍'));
123
- }
124
- } else if (state.initialized) {
125
- console.log(chalk.green(' ✓ 配眮正垞'));
126
- } else {
127
- console.log(chalk.yellow(' ⚠ 项目未初始化'));
128
- issuesFound = true;
129
- }
144
+ const installOk = await checkClaudeCodeInstalled();
145
+ const configOk = await checkConfiguration(state, options.fix);
146
+ checkComponents(state);
130
147
 
131
- // Check 3: Components
132
- console.log('\n检查组件...');
133
- Object.entries(state.components).forEach(([name, component]) => {
134
- const status = component.installed ? chalk.green('✓') : chalk.red('✗');
135
- const count = 'count' in component && component.count ? ` (${component.count})` : '';
136
- console.log(` ${status} ${name}${count}`);
137
- });
148
+ const issuesFound = !installOk || !configOk;
138
149
 
139
- // Summary
140
150
  console.log(`\n${chalk.bold('结果:')}`);
141
151
  if (!issuesFound) {
142
152
  console.log(chalk.green('✓ 所有检查通过'));
@@ -241,7 +251,7 @@ export const quickstartCommand = new Command('quickstart')
241
251
 
242
252
  if (tryNow) {
243
253
  console.log(chalk.dim('\nLaunching Flow...\n'));
244
- await executeFlow('describe this codebase briefly', { agent: 'builder' } as FlowOptions);
254
+ await executeFlow('describe this codebase briefly', { agent: 'builder' });
245
255
  } else {
246
256
  console.log(chalk.green("\n✹ You're ready to go!\n"));
247
257
  console.log(chalk.dim(' Run: sylphx-flow "your first task"\n'));
@@ -1,27 +1,7 @@
1
1
  /**
2
2
  * Configuration modules barrel export
3
- * Centralized access to configuration-related functionality
4
3
  */
5
4
 
6
- // Rules configuration
7
5
  export * from './rules.js';
8
- export {
9
- getDefaultRules,
10
- loadRuleConfiguration,
11
- validateRuleConfiguration,
12
- } from './rules.js';
13
- // MCP server configurations
14
6
  export * from './servers.js';
15
- export {
16
- configureServer,
17
- getServerConfigurations,
18
- validateServerConfiguration,
19
- } from './servers.js';
20
- // Target configurations
21
7
  export * from './targets.js';
22
- // Re-export commonly used configuration functions with better naming
23
- export {
24
- configureTargetDefaults,
25
- getTargetDefaults,
26
- validateTargetConfiguration,
27
- } from './targets.js';
@@ -93,7 +93,7 @@ export const getDefaultTargetUnsafe = (): Target => {
93
93
  * Get targets that support MCP servers
94
94
  */
95
95
  export const getTargetsWithMCPSupport = (): readonly Target[] =>
96
- getImplementedTargets().filter((target) => !!target.setupMCP);
96
+ getImplementedTargets().filter((target) => !!target.config.supportsMCP);
97
97
 
98
98
  /**
99
99
  * Get targets that support command execution (agent running)
@@ -47,7 +47,7 @@ describe('Backup → Attach → Restore Lifecycle', () => {
47
47
  (projectManager as any).flowDataDir = flowDataDir;
48
48
 
49
49
  backupManager = new BackupManager(projectManager);
50
- attachManager = new AttachManager(projectManager);
50
+ attachManager = new AttachManager();
51
51
 
52
52
  // Get project hash
53
53
  projectHash = projectManager.getProjectHash(projectPath);