@sylphx/flow 2.1.2 → 2.1.4

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 (70) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +44 -0
  3. package/package.json +79 -73
  4. package/src/commands/flow/execute-v2.ts +39 -30
  5. package/src/commands/flow/index.ts +2 -4
  6. package/src/commands/flow/prompt.ts +5 -3
  7. package/src/commands/flow/types.ts +0 -9
  8. package/src/commands/flow-command.ts +20 -13
  9. package/src/commands/hook-command.ts +1 -3
  10. package/src/commands/settings-command.ts +36 -33
  11. package/src/config/ai-config.ts +60 -41
  12. package/src/core/agent-loader.ts +11 -6
  13. package/src/core/attach-manager.ts +92 -84
  14. package/src/core/backup-manager.ts +35 -29
  15. package/src/core/cleanup-handler.ts +11 -8
  16. package/src/core/error-handling.ts +23 -30
  17. package/src/core/flow-executor.ts +58 -76
  18. package/src/core/formatting/bytes.ts +2 -4
  19. package/src/core/functional/async.ts +5 -4
  20. package/src/core/functional/error-handler.ts +2 -2
  21. package/src/core/git-stash-manager.ts +21 -10
  22. package/src/core/installers/file-installer.ts +0 -1
  23. package/src/core/installers/mcp-installer.ts +0 -1
  24. package/src/core/project-manager.ts +24 -18
  25. package/src/core/secrets-manager.ts +54 -73
  26. package/src/core/session-manager.ts +20 -22
  27. package/src/core/state-detector.ts +139 -80
  28. package/src/core/template-loader.ts +13 -31
  29. package/src/core/upgrade-manager.ts +122 -69
  30. package/src/index.ts +8 -5
  31. package/src/services/auto-upgrade.ts +1 -1
  32. package/src/services/config-service.ts +41 -29
  33. package/src/services/global-config.ts +2 -2
  34. package/src/services/target-installer.ts +9 -7
  35. package/src/targets/claude-code.ts +28 -15
  36. package/src/targets/opencode.ts +17 -6
  37. package/src/types/cli.types.ts +2 -2
  38. package/src/types/provider.types.ts +1 -7
  39. package/src/types/session.types.ts +11 -11
  40. package/src/types/target.types.ts +3 -1
  41. package/src/types/todo.types.ts +1 -1
  42. package/src/types.ts +1 -1
  43. package/src/utils/__tests__/package-manager-detector.test.ts +6 -6
  44. package/src/utils/agent-enhancer.ts +111 -3
  45. package/src/utils/config/paths.ts +3 -1
  46. package/src/utils/config/target-utils.ts +2 -2
  47. package/src/utils/display/banner.ts +2 -2
  48. package/src/utils/display/notifications.ts +58 -45
  49. package/src/utils/display/status.ts +29 -12
  50. package/src/utils/files/file-operations.ts +1 -1
  51. package/src/utils/files/sync-utils.ts +38 -41
  52. package/src/utils/index.ts +19 -27
  53. package/src/utils/package-manager-detector.ts +15 -5
  54. package/src/utils/security/security.ts +8 -4
  55. package/src/utils/target-selection.ts +5 -2
  56. package/src/utils/version.ts +4 -2
  57. package/src/commands/flow/execute.ts +0 -453
  58. package/src/commands/flow/setup.ts +0 -312
  59. package/src/commands/flow-orchestrator.ts +0 -328
  60. package/src/commands/init-command.ts +0 -92
  61. package/src/commands/init-core.ts +0 -331
  62. package/src/commands/run-command.ts +0 -126
  63. package/src/core/agent-manager.ts +0 -174
  64. package/src/core/loop-controller.ts +0 -200
  65. package/src/core/rule-loader.ts +0 -147
  66. package/src/core/rule-manager.ts +0 -240
  67. package/src/services/claude-config-service.ts +0 -252
  68. package/src/services/first-run-setup.ts +0 -220
  69. package/src/services/smart-config-service.ts +0 -269
  70. package/src/types/api.types.ts +0 -9
@@ -1,312 +0,0 @@
1
- /**
2
- * Setup Phase for Flow Command
3
- * One-time initialization and preparation for command execution
4
- */
5
-
6
- import chalk from 'chalk';
7
- import { targetManager } from '../../core/target-manager.js';
8
- import { StateDetector, type ProjectState } from '../../core/state-detector.js';
9
- import { projectSettings } from '../../utils/config/settings.js';
10
- import { showWelcome } from '../../utils/display/banner.js';
11
- import { showStatus } from '../../utils/display/status.js';
12
- import { loadAgentContent, extractAgentInstructions } from '../run-command.js';
13
- import type { RunCommandOptions } from '../../types.js';
14
- import type { FlowOptions, SetupContext } from './types.js';
15
-
16
- /**
17
- * Execute setup phase once (for loop mode)
18
- * Returns context needed for repeated command execution
19
- */
20
- export async function executeSetupPhase(prompt: string | undefined, options: FlowOptions): Promise<SetupContext> {
21
- // Quick mode: enable useDefaults and skip prompts
22
- if (options.quick) {
23
- options.useDefaults = true;
24
- console.log(chalk.cyan('⚡ Quick mode enabled - using saved defaults\n'));
25
- }
26
-
27
- // Import orchestrator functions
28
- const {
29
- checkUpgrades,
30
- checkComponentIntegrity,
31
- } = await import('../flow-orchestrator.js');
32
-
33
- // Show welcome banner (only once)
34
- showWelcome();
35
-
36
- let selectedTarget: string | undefined;
37
- let state: ProjectState | undefined;
38
-
39
- // Determine target
40
- const initialTarget = options.target || (await projectSettings.getDefaultTarget());
41
-
42
- // Detect state if we have a target
43
- if (initialTarget && !options.sync) {
44
- const detector = new StateDetector();
45
-
46
- if (options.verbose) {
47
- console.log(chalk.dim('🤔 Checking project status...\n'));
48
- }
49
-
50
- state = await detector.detect();
51
-
52
- if (options.verbose) {
53
- await showStatus(state);
54
- }
55
-
56
- // Check for upgrades
57
- if (!options.quick) {
58
- await checkUpgrades(state, options);
59
- }
60
-
61
- // Check component integrity
62
- await checkComponentIntegrity(state, options);
63
- }
64
-
65
- // Initialize if needed
66
- const shouldInitialize =
67
- !state?.initialized ||
68
- options.sync ||
69
- options.repair ||
70
- options.initOnly;
71
-
72
- if (shouldInitialize) {
73
- selectedTarget = await initializeWithTarget(options);
74
- }
75
-
76
- // Resolve target
77
- const targetForResolution = resolveTargetPriority(options.target, state?.target, selectedTarget);
78
-
79
- if (!targetForResolution) {
80
- console.error(chalk.red.bold('✗ No target selected. Use --target or run init first.'));
81
- process.exit(1);
82
- }
83
-
84
- const resolvedTarget = await targetManager.resolveTarget({
85
- target: targetForResolution,
86
- allowSelection: false,
87
- });
88
-
89
- console.log(chalk.cyan.bold(`━━━ 🎯 Launching ${resolvedTarget}\n`));
90
-
91
- // Validate target support
92
- await validateTargetSupport(resolvedTarget);
93
-
94
- // Handle Claude Code specific setup
95
- if (resolvedTarget === 'claude-code') {
96
- await setupClaudeCode(options);
97
- }
98
-
99
- const agent = options.agent || 'coder';
100
- const verbose = options.verbose || false;
101
-
102
- if (verbose || options.runOnly || !options.quick) {
103
- console.log(` 🤖 Agent: ${chalk.cyan(agent)}`);
104
- console.log(` 🎯 Target: ${chalk.cyan(resolvedTarget)}`);
105
- if (prompt) {
106
- console.log(` 💬 Prompt: ${chalk.dim(prompt)}\n`);
107
- } else {
108
- console.log(` 💬 Mode: ${chalk.dim('Interactive')}\n`);
109
- }
110
- }
111
-
112
- // Load agent and prepare prompts
113
- const agentContent = await loadAgentContent(agent, options.agentFile);
114
- const agentInstructions = extractAgentInstructions(agentContent);
115
- const systemPrompt = `AGENT INSTRUCTIONS:\n${agentInstructions}`;
116
-
117
- // Prepare run options
118
- const runOptions: RunCommandOptions = {
119
- target: resolvedTarget,
120
- verbose,
121
- dryRun: options.dryRun,
122
- agent,
123
- agentFile: options.agentFile,
124
- prompt,
125
- print: options.print,
126
- continue: options.continue,
127
- };
128
-
129
- return {
130
- resolvedTarget,
131
- initializedSuccessfully: true,
132
- systemPrompt,
133
- runOptions,
134
- };
135
- }
136
-
137
- /**
138
- * Initialize project with target selection and component installation
139
- */
140
- async function initializeWithTarget(options: FlowOptions): Promise<string | undefined> {
141
- let selectedTarget: string | undefined;
142
-
143
- try {
144
- const { selectAndValidateTarget, previewDryRun, installComponents } =
145
- await import('../init-core.js');
146
-
147
- const initOptions = {
148
- target: options.target,
149
- verbose: options.verbose || false,
150
- dryRun: options.dryRun || false,
151
- clear: options.sync || false,
152
- mcp: options.mcp !== false,
153
- agents: options.agents !== false,
154
- rules: options.rules !== false,
155
- outputStyles: options.outputStyles !== false,
156
- slashCommands: options.slashCommands !== false,
157
- hooks: options.hooks !== false,
158
- };
159
-
160
- // Handle sync mode - delete template files first
161
- if (options.sync && !options.dryRun) {
162
- selectedTarget = await handleSyncMode(initOptions);
163
- } else if (!options.sync) {
164
- const targetId = await selectAndValidateTarget(initOptions);
165
- selectedTarget = targetId;
166
- }
167
-
168
- if (options.dryRun) {
169
- // Ensure we have a target ID for dry run
170
- if (!selectedTarget) {
171
- const targetId = await selectAndValidateTarget(initOptions);
172
- selectedTarget = targetId;
173
- }
174
-
175
- await previewDryRun(selectedTarget, initOptions);
176
- console.log(chalk.dim('✓ Initialization dry run complete\n'));
177
- } else {
178
- // Ensure we have a target ID for installation
179
- if (!selectedTarget) {
180
- const targetId = await selectAndValidateTarget(initOptions);
181
- selectedTarget = targetId;
182
- }
183
-
184
- await installComponents(selectedTarget, initOptions);
185
- console.log(chalk.green.bold('✓ Initialization complete\n'));
186
- }
187
- } catch (error) {
188
- console.error(chalk.red.bold('✗ Initialization failed:'), error);
189
- process.exit(1);
190
- }
191
-
192
- return selectedTarget;
193
- }
194
-
195
- /**
196
- * Handle sync mode: delete templates then reinstall
197
- */
198
- async function handleSyncMode(initOptions: any): Promise<string> {
199
- const { buildSyncManifest, showSyncPreview, selectUnknownFilesToRemove, showFinalSummary, confirmSync, executeSyncDelete, removeMCPServers, removeHooks } = await import('../../utils/files/sync-utils.js');
200
- const { selectAndValidateTarget } = await import('../init-core.js');
201
-
202
- // Need target to build manifest
203
- const targetId = await selectAndValidateTarget(initOptions);
204
-
205
- const targetOption = targetManager.getTarget(targetId);
206
- if (targetOption._tag === 'None') {
207
- throw new Error(`Target not found: ${targetId}`);
208
- }
209
-
210
- const target = targetOption.value;
211
- const manifest = await buildSyncManifest(process.cwd(), target);
212
-
213
- // Show preview
214
- console.log(chalk.cyan.bold('━━━ 🔄 Synchronizing Files\n'));
215
- showSyncPreview(manifest, process.cwd(), target);
216
-
217
- // Select unknown files to remove
218
- const selectedUnknowns = await selectUnknownFilesToRemove(manifest);
219
-
220
- // Show final summary
221
- showFinalSummary(manifest, selectedUnknowns);
222
-
223
- // Confirm
224
- const confirmed = await confirmSync();
225
- if (!confirmed) {
226
- console.log(chalk.yellow('\n✗ Sync cancelled\n'));
227
- process.exit(0);
228
- }
229
-
230
- // Execute deletion
231
- const { templates, unknowns } = await executeSyncDelete(manifest, selectedUnknowns);
232
-
233
- // Remove MCP servers
234
- let mcpRemoved = 0;
235
- if (selectedUnknowns.mcpServers.length > 0) {
236
- mcpRemoved = await removeMCPServers(process.cwd(), selectedUnknowns.mcpServers);
237
- }
238
-
239
- // Remove hooks
240
- let hooksRemoved = 0;
241
- if (selectedUnknowns.hooks.length > 0) {
242
- hooksRemoved = await removeHooks(process.cwd(), selectedUnknowns.hooks);
243
- }
244
-
245
- // Summary
246
- console.log(chalk.green(`\n✓ Synced ${templates} templates`));
247
- const totalRemoved = unknowns + mcpRemoved + hooksRemoved;
248
- if (totalRemoved > 0) {
249
- console.log(chalk.green(`✓ Removed ${totalRemoved} items`));
250
- }
251
- const totalSelected = selectedUnknowns.files.length + selectedUnknowns.mcpServers.length + selectedUnknowns.hooks.length;
252
- const preserved = manifest.agents.unknown.length + manifest.slashCommands.unknown.length + manifest.rules.unknown.length + manifest.mcpServers.notInRegistry.length + manifest.hooks.orphaned.length - totalSelected;
253
- if (preserved > 0) {
254
- console.log(chalk.green(`✓ Preserved ${preserved} custom items`));
255
- }
256
- console.log('');
257
-
258
- return targetId;
259
- }
260
-
261
- /**
262
- * Resolve target priority: options > state > selected
263
- */
264
- function resolveTargetPriority(
265
- optionsTarget: string | undefined,
266
- stateTarget: string | undefined,
267
- selectedTarget: string | undefined
268
- ): string | undefined {
269
- return selectedTarget || optionsTarget || stateTarget;
270
- }
271
-
272
- /**
273
- * Validate that target supports command execution
274
- */
275
- async function validateTargetSupport(resolvedTarget: string): Promise<void> {
276
- const { getTargetsWithCommandSupport } = await import('../../config/targets.js');
277
- const supportedTargets = getTargetsWithCommandSupport().map(t => t.id);
278
-
279
- if (!supportedTargets.includes(resolvedTarget)) {
280
- console.log(chalk.red.bold('✗ Unsupported target platform\n'));
281
- console.log(chalk.yellow(`Target '${resolvedTarget}' does not support agent execution.`));
282
- console.log(chalk.cyan(`Supported platforms: ${supportedTargets.join(', ')}\n`));
283
- console.log(chalk.dim('Tip: Use --target claude-code to specify Claude Code platform'));
284
- console.log(chalk.dim('Example: bun dev:flow --target claude-code\n'));
285
- process.exit(1);
286
- }
287
- }
288
-
289
- /**
290
- * Setup Claude Code provider and agent
291
- */
292
- async function setupClaudeCode(options: FlowOptions): Promise<void> {
293
- const { SmartConfigService } = await import('../../services/smart-config-service.js');
294
- const { ConfigService } = await import('../../services/config-service.js');
295
-
296
- if (!(await ConfigService.hasInitialSetup())) {
297
- console.log(chalk.cyan('🔑 First-time setup for Claude Code\n'));
298
- await SmartConfigService.initialSetup();
299
- console.log(chalk.green('✓ Setup complete!\n'));
300
- }
301
-
302
- const runtimeChoices = await SmartConfigService.selectRuntimeChoices({
303
- selectProvider: options.selectProvider,
304
- selectAgent: options.selectAgent,
305
- useDefaults: options.useDefaults,
306
- provider: options.provider,
307
- agent: options.agent,
308
- });
309
-
310
- await SmartConfigService.setupEnvironment(runtimeChoices.provider!);
311
- options.agent = runtimeChoices.agent;
312
- }
@@ -1,328 +0,0 @@
1
- /**
2
- * Flow Orchestrator - Simplified flow management
3
- * Separates concerns and reduces complexity
4
- */
5
-
6
- import chalk from 'chalk';
7
- import type { FlowOptions } from './flow/types.js';
8
- import { StateDetector, type ProjectState } from '../core/state-detector.js';
9
- import { UpgradeManager } from '../core/upgrade-manager.js';
10
- import { targetManager } from '../core/target-manager.js';
11
- import { detectPackageManager, getUpgradeCommand } from '../utils/package-manager-detector.js';
12
-
13
- /**
14
- * Step 1: Check for available upgrades
15
- */
16
- export async function checkUpgrades(
17
- state: ProjectState,
18
- options: FlowOptions
19
- ): Promise<void> {
20
- if (options.initOnly || options.runOnly) return;
21
-
22
- const upgradeManager = new UpgradeManager();
23
- const updates = await upgradeManager.checkUpdates();
24
- const packageManager = detectPackageManager();
25
-
26
- // Check Flow upgrade
27
- if (updates.flowUpdate && updates.flowVersion) {
28
- const upgradeCmd = getUpgradeCommand('@sylphx/flow', packageManager);
29
- console.log(
30
- chalk.yellow(
31
- `📦 Sylphx Flow update available: ${updates.flowVersion.current} → ${updates.flowVersion.latest}`
32
- )
33
- );
34
- console.log(chalk.dim(` Quick upgrade: ${chalk.cyan('sylphx-flow upgrade --auto')}`));
35
- console.log(chalk.dim(` Or run: ${chalk.cyan(upgradeCmd)}\n`));
36
- }
37
-
38
- // Check target upgrade
39
- if (updates.targetUpdate && updates.targetVersion) {
40
- console.log(
41
- chalk.yellow(
42
- `📦 Target update available: ${updates.targetVersion.current} → ${updates.targetVersion.latest}`
43
- )
44
- );
45
- console.log(chalk.dim(` Run: ${chalk.cyan('sylphx-flow upgrade --target --auto')}\n`));
46
- }
47
- }
48
-
49
-
50
- /**
51
- * Step 2: Check component integrity and prompt for repair
52
- */
53
- export async function checkComponentIntegrity(
54
- state: ProjectState,
55
- options: FlowOptions
56
- ): Promise<void> {
57
- // Skip if not initialized or cleaning or init-only
58
- if (!state.initialized || options.clean || options.initOnly) return;
59
-
60
- // Skip in quick mode
61
- if (options.quick) return;
62
-
63
- // Find missing components (target-aware)
64
- const missing: string[] = [];
65
-
66
- // Agents are always required
67
- if (!state.components.agents.installed) missing.push('agents');
68
-
69
- // For OpenCode: check rules (separate AGENTS.md file)
70
- // For Claude Code: rules are included in agent files, so skip this check
71
- if (state.target === 'opencode' && !state.components.rules.installed) {
72
- missing.push('rules');
73
- }
74
-
75
- // Hooks are optional - don't check
76
- // Claude Code can have hooks in .claude/hooks/*.js but they're optional
77
- // OpenCode doesn't have separate hooks
78
-
79
- // MCP is optional now - many users don't use MCP
80
- // if (!state.components.mcp.installed) missing.push('mcp');
81
-
82
- // Output styles:
83
- // - Claude Code: included in agent files, so skip check
84
- // - OpenCode: included in AGENTS.md, so skip check
85
-
86
- // Slash commands are optional
87
- // if (!state.components.slashCommands.installed) missing.push('slash commands');
88
-
89
- // If no missing components, we're good
90
- if (missing.length === 0) return;
91
-
92
- // Prompt user to repair
93
- console.log(chalk.yellow(`\n⚠️ Missing components detected: ${missing.join(', ')}\n`));
94
- const { default: inquirer } = await import('inquirer');
95
- const { repair } = await inquirer.prompt([
96
- {
97
- type: 'confirm',
98
- name: 'repair',
99
- message: 'Install missing components now?',
100
- default: true,
101
- },
102
- ]);
103
-
104
- if (repair) {
105
- // Set repair mode - will trigger component installation without full re-init
106
- options.repair = true;
107
- console.log(chalk.cyan('\n🔧 Repairing components...\n'));
108
- } else {
109
- console.log(chalk.dim('Skipping repair. Components may not work correctly.\n'));
110
- }
111
- }
112
-
113
- /**
114
- * Step 2.5: Check sync status (new templates available)
115
- * Only checks for missing templates, ignores unknown files
116
- */
117
- export async function checkSyncStatus(
118
- state: ProjectState,
119
- options: FlowOptions
120
- ): Promise<void> {
121
- // Skip if not initialized, syncing, or init-only
122
- if (!state.initialized || options.sync || options.initOnly) return;
123
-
124
- // Skip in quick mode
125
- if (options.quick) return;
126
-
127
- // Need target to check sync status
128
- if (!state.target) return;
129
-
130
- try {
131
- const { buildSyncManifest } = await import('../utils/files/sync-utils.js');
132
- const target = targetManager.getTarget(state.target);
133
-
134
- if (target._tag === 'None') return;
135
-
136
- const manifest = await buildSyncManifest(process.cwd(), target.value);
137
-
138
- // Count missing templates (new templates not installed locally)
139
- const missingCount =
140
- manifest.agents.missing.length +
141
- manifest.slashCommands.missing.length +
142
- manifest.rules.missing.length;
143
-
144
- // Only prompt if there are missing templates
145
- if (missingCount > 0) {
146
- const missing: string[] = [];
147
-
148
- if (manifest.agents.missing.length > 0) {
149
- missing.push(`${manifest.agents.missing.length} agent${manifest.agents.missing.length > 1 ? 's' : ''}`);
150
- }
151
- if (manifest.slashCommands.missing.length > 0) {
152
- missing.push(`${manifest.slashCommands.missing.length} command${manifest.slashCommands.missing.length > 1 ? 's' : ''}`);
153
- }
154
- if (manifest.rules.missing.length > 0) {
155
- missing.push(`${manifest.rules.missing.length} rule${manifest.rules.missing.length > 1 ? 's' : ''}`);
156
- }
157
-
158
- console.log(chalk.yellow(`\n📦 New templates available: ${missing.join(', ')}\n`));
159
- console.log(chalk.dim(` Run ${chalk.cyan('sylphx-flow --sync')} to install new templates\n`));
160
- }
161
- } catch (error) {
162
- // Silently ignore sync check errors - don't block execution
163
- }
164
- }
165
-
166
- /**
167
- * Step 3: Handle target selection
168
- * Returns the selected target ID
169
- */
170
- export async function selectTarget(
171
- state: ProjectState,
172
- options: FlowOptions
173
- ): Promise<string | undefined> {
174
- // Force target selection when cleaning
175
- if (options.clean) {
176
- try {
177
- const targetId = await targetManager.promptForTargetSelection();
178
- console.log(chalk.green(`✅ Selected target: ${targetId}`));
179
- return targetId;
180
- } catch (error) {
181
- // User cancelled with Ctrl+C - exit gracefully
182
- if (error instanceof Error && error.name === 'ExitPromptError') {
183
- console.log('\n');
184
- process.exit(0);
185
- }
186
- throw error;
187
- }
188
- }
189
-
190
- // Use existing target or option
191
- return options.target || state.target;
192
- }
193
-
194
- /**
195
- * Step 3: Initialize project
196
- */
197
- export async function initializeProject(
198
- targetId: string | undefined,
199
- options: FlowOptions
200
- ): Promise<void> {
201
- if (options.runOnly && !options.clean) return;
202
-
203
- console.log(chalk.cyan.bold('━ Initializing Project\n'));
204
-
205
- const { runInit } = await import('./init-command.js');
206
-
207
- const initOptions = {
208
- target: targetId,
209
- verbose: options.verbose,
210
- dryRun: options.dryRun,
211
- clear: options.clean || false,
212
- mcp: options.mcp !== false,
213
- agents: options.agents !== false,
214
- rules: options.rules !== false,
215
- outputStyles: options.outputStyles !== false,
216
- slashCommands: options.slashCommands !== false,
217
- hooks: options.hooks !== false,
218
- helpOption: () => {},
219
- };
220
-
221
- try {
222
- await runInit(initOptions);
223
-
224
- if (!options.dryRun) {
225
- console.log(chalk.green.bold('\n✓ Initialization complete\n'));
226
- } else {
227
- console.log(chalk.dim('\n✓ Dry run complete - skipping execution\n'));
228
- }
229
- } catch (error) {
230
- console.error(chalk.red.bold('\n✗ Initialization failed:'), error);
231
- throw error;
232
- }
233
- }
234
-
235
- /**
236
- * Step 4: Launch target
237
- */
238
- export async function launchTarget(
239
- targetId: string | undefined,
240
- prompt: string | undefined,
241
- options: FlowOptions,
242
- state: ProjectState
243
- ): Promise<void> {
244
- if (options.initOnly) return;
245
-
246
- // Resolve target
247
- const resolvedTarget = await targetManager.resolveTarget({
248
- target: targetId || state.target,
249
- allowSelection: false,
250
- });
251
-
252
- console.log(chalk.cyan.bold(`━ Launching ${resolvedTarget}\n`));
253
-
254
- // Check if target supports command execution
255
- const { getTargetsWithCommandSupport } = await import('../config/targets.js');
256
- const supportedTargets = getTargetsWithCommandSupport().map(t => t.id);
257
-
258
- if (!supportedTargets.includes(resolvedTarget)) {
259
- console.log(chalk.red.bold('✗ Unsupported target platform\n'));
260
- console.log(
261
- chalk.yellow(`Target '${resolvedTarget}' does not support agent execution.`)
262
- );
263
- console.log(chalk.cyan(`Supported platforms: ${supportedTargets.join(', ')}\n`));
264
- throw new Error(`Unsupported target: ${resolvedTarget}`);
265
- }
266
-
267
- // Handle Claude Code specific setup
268
- if (resolvedTarget === 'claude-code') {
269
- await setupClaudeCode(options);
270
- }
271
-
272
- // Execute command
273
- await executeCommand(resolvedTarget, prompt, options);
274
- }
275
-
276
- /**
277
- * Setup Claude Code (provider + agent selection)
278
- */
279
- async function setupClaudeCode(options: FlowOptions): Promise<void> {
280
- const { SmartConfigService } = await import('../services/smart-config-service.js');
281
- const { ConfigService } = await import('../services/config-service.js');
282
-
283
- // Check if API keys are configured
284
- if (!(await ConfigService.hasInitialSetup())) {
285
- console.log(chalk.cyan('\n🔑 First-time setup for Claude Code:\n'));
286
- await SmartConfigService.initialSetup();
287
- console.log(chalk.green('\n✅ Claude Code setup complete!\n'));
288
- }
289
-
290
- // Select provider and agent
291
- const runtimeChoices = await SmartConfigService.selectRuntimeChoices({
292
- selectProvider: options.selectProvider,
293
- selectAgent: options.selectAgent,
294
- useDefaults: options.useDefaults,
295
- provider: options.provider,
296
- agent: options.agent,
297
- });
298
-
299
- // Setup environment
300
- await SmartConfigService.setupEnvironment(runtimeChoices.provider!);
301
-
302
- // Store selected agent
303
- options.agent = runtimeChoices.agent;
304
- }
305
-
306
- /**
307
- * Execute the target command
308
- */
309
- async function executeCommand(
310
- targetId: string,
311
- prompt: string | undefined,
312
- options: FlowOptions
313
- ): Promise<void> {
314
- const agent = options.agent || 'coder';
315
- const verbose = options.verbose || false;
316
-
317
- if (verbose || options.runOnly) {
318
- console.log(`🤖 Agent: ${agent}`);
319
- console.log(`🎯 Target: ${targetId}`);
320
- if (prompt) {
321
- console.log(`💬 Prompt: ${prompt}\n`);
322
- }
323
- }
324
-
325
- // Run the command
326
- const { runCommand } = await import('./run-command.js');
327
- await runCommand({ target: targetId, agent, prompt, verbose });
328
- }