@sylphx/flow 1.8.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +159 -0
- package/UPGRADE.md +151 -0
- package/package.json +11 -6
- package/src/commands/flow/execute-v2.ts +372 -0
- package/src/commands/flow/execute.ts +1 -18
- package/src/commands/flow/types.ts +3 -2
- package/src/commands/flow-command.ts +32 -69
- package/src/commands/flow-orchestrator.ts +18 -55
- package/src/commands/run-command.ts +12 -6
- package/src/commands/settings-command.ts +536 -0
- package/src/config/ai-config.ts +2 -69
- package/src/config/targets.ts +0 -11
- package/src/core/attach-manager.ts +495 -0
- package/src/core/backup-manager.ts +308 -0
- package/src/core/cleanup-handler.ts +166 -0
- package/src/core/flow-executor.ts +323 -0
- package/src/core/git-stash-manager.ts +133 -0
- package/src/core/installers/file-installer.ts +0 -57
- package/src/core/installers/mcp-installer.ts +0 -33
- package/src/core/project-manager.ts +274 -0
- package/src/core/secrets-manager.ts +229 -0
- package/src/core/session-manager.ts +268 -0
- package/src/core/template-loader.ts +189 -0
- package/src/core/upgrade-manager.ts +79 -47
- package/src/index.ts +15 -29
- package/src/services/auto-upgrade.ts +248 -0
- package/src/services/first-run-setup.ts +220 -0
- package/src/services/global-config.ts +337 -0
- package/src/services/target-installer.ts +254 -0
- package/src/targets/claude-code.ts +5 -7
- package/src/targets/opencode.ts +6 -26
- package/src/utils/__tests__/package-manager-detector.test.ts +163 -0
- package/src/utils/agent-enhancer.ts +40 -22
- package/src/utils/errors.ts +9 -0
- package/src/utils/package-manager-detector.ts +139 -0
- package/src/utils/prompt-helpers.ts +48 -0
- package/src/utils/target-selection.ts +169 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution Logic for Flow Command (V2 - Attach Mode)
|
|
3
|
+
* New execution flow with attach-mode lifecycle
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
|
+
import { FlowExecutor } from '../../core/flow-executor.js';
|
|
9
|
+
import { targetManager } from '../../core/target-manager.js';
|
|
10
|
+
import { UpgradeManager } from '../../core/upgrade-manager.js';
|
|
11
|
+
import { showWelcome } from '../../utils/display/banner.js';
|
|
12
|
+
import { loadAgentContent, extractAgentInstructions } from '../run-command.js';
|
|
13
|
+
import { CLIError } from '../../utils/error-handler.js';
|
|
14
|
+
import type { RunCommandOptions } from '../../types.js';
|
|
15
|
+
import type { FlowOptions } from './types.js';
|
|
16
|
+
import { resolvePrompt } from './prompt.js';
|
|
17
|
+
import { GlobalConfigService } from '../../services/global-config.js';
|
|
18
|
+
import { UserCancelledError } from '../../utils/errors.js';
|
|
19
|
+
import { TargetInstaller } from '../../services/target-installer.js';
|
|
20
|
+
import { AutoUpgrade } from '../../services/auto-upgrade.js';
|
|
21
|
+
import { promptForTargetSelection, ensureTargetInstalled } from '../../utils/target-selection.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Configure provider environment variables
|
|
25
|
+
*/
|
|
26
|
+
function configureProviderEnv(
|
|
27
|
+
provider: 'kimi' | 'zai',
|
|
28
|
+
apiKey: string
|
|
29
|
+
): void {
|
|
30
|
+
const providerConfig = {
|
|
31
|
+
kimi: {
|
|
32
|
+
baseUrl: 'https://api.moonshot.cn/v1',
|
|
33
|
+
name: 'Kimi',
|
|
34
|
+
},
|
|
35
|
+
zai: {
|
|
36
|
+
baseUrl: 'https://api.z.ai/v1',
|
|
37
|
+
name: 'Z.ai',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const config = providerConfig[provider];
|
|
42
|
+
process.env.ANTHROPIC_BASE_URL = config.baseUrl;
|
|
43
|
+
process.env.ANTHROPIC_API_KEY = apiKey;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Select and configure provider for Claude Code
|
|
48
|
+
*/
|
|
49
|
+
async function selectProvider(
|
|
50
|
+
configService: GlobalConfigService
|
|
51
|
+
): Promise<void> {
|
|
52
|
+
try {
|
|
53
|
+
const providerConfig = await configService.loadProviderConfig();
|
|
54
|
+
const defaultProvider = providerConfig.claudeCode.defaultProvider;
|
|
55
|
+
|
|
56
|
+
// If not "ask-every-time", use the default provider
|
|
57
|
+
if (defaultProvider !== 'ask-every-time') {
|
|
58
|
+
if (defaultProvider === 'kimi' || defaultProvider === 'zai') {
|
|
59
|
+
const provider = providerConfig.claudeCode.providers[defaultProvider];
|
|
60
|
+
if (provider?.apiKey) {
|
|
61
|
+
configureProviderEnv(defaultProvider, provider.apiKey);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Ask user which provider to use for this session
|
|
68
|
+
const { selectedProvider } = await inquirer.prompt([
|
|
69
|
+
{
|
|
70
|
+
type: 'list',
|
|
71
|
+
name: 'selectedProvider',
|
|
72
|
+
message: 'Select provider for this session:',
|
|
73
|
+
choices: [
|
|
74
|
+
{ name: 'Default (Claude Code built-in)', value: 'default' },
|
|
75
|
+
{ name: 'Kimi', value: 'kimi' },
|
|
76
|
+
{ name: 'Z.ai', value: 'zai' },
|
|
77
|
+
],
|
|
78
|
+
default: 'default',
|
|
79
|
+
},
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
// Configure environment variables based on selection
|
|
83
|
+
if (selectedProvider === 'kimi' || selectedProvider === 'zai') {
|
|
84
|
+
const provider = providerConfig.claudeCode.providers[selectedProvider];
|
|
85
|
+
|
|
86
|
+
if (!provider?.apiKey) {
|
|
87
|
+
console.log(chalk.yellow('⚠ API key not configured. Use: sylphx-flow settings\n'));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
configureProviderEnv(selectedProvider, provider.apiKey);
|
|
92
|
+
|
|
93
|
+
const providerName = selectedProvider === 'kimi' ? 'Kimi' : 'Z.ai';
|
|
94
|
+
console.log(chalk.green(`✓ Using ${providerName} provider\n`));
|
|
95
|
+
} else {
|
|
96
|
+
console.log(chalk.green('✓ Using default Claude Code provider\n'));
|
|
97
|
+
}
|
|
98
|
+
} catch (error: any) {
|
|
99
|
+
// Handle user cancellation (Ctrl+C)
|
|
100
|
+
if (error.name === 'ExitPromptError' || error.message?.includes('force closed')) {
|
|
101
|
+
throw new UserCancelledError('Provider selection cancelled');
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Execute command using target's executeCommand method
|
|
109
|
+
*/
|
|
110
|
+
async function executeTargetCommand(
|
|
111
|
+
targetId: string,
|
|
112
|
+
systemPrompt: string,
|
|
113
|
+
userPrompt: string,
|
|
114
|
+
options: RunCommandOptions
|
|
115
|
+
): Promise<void> {
|
|
116
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
117
|
+
|
|
118
|
+
if (targetOption._tag === 'None') {
|
|
119
|
+
throw new CLIError(`Target not found: ${targetId}`, 'TARGET_NOT_FOUND');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const target = targetOption.value;
|
|
123
|
+
|
|
124
|
+
if (!target.isImplemented || !target.executeCommand) {
|
|
125
|
+
throw new CLIError(
|
|
126
|
+
`Target '${targetId}' does not support command execution`,
|
|
127
|
+
'EXECUTION_NOT_SUPPORTED'
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return target.executeCommand(systemPrompt, userPrompt, options);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Main flow execution with attach mode (V2)
|
|
136
|
+
*/
|
|
137
|
+
export async function executeFlowV2(
|
|
138
|
+
prompt: string | undefined,
|
|
139
|
+
options: FlowOptions
|
|
140
|
+
): Promise<void> {
|
|
141
|
+
const projectPath = process.cwd();
|
|
142
|
+
|
|
143
|
+
// Show welcome banner
|
|
144
|
+
showWelcome();
|
|
145
|
+
|
|
146
|
+
// Initialize config service early to check for saved preferences
|
|
147
|
+
const configService = new GlobalConfigService();
|
|
148
|
+
await configService.initialize();
|
|
149
|
+
|
|
150
|
+
// Step 1: Determine target
|
|
151
|
+
const targetInstaller = new TargetInstaller(projectPath);
|
|
152
|
+
const installedTargets = await targetInstaller.detectInstalledTargets();
|
|
153
|
+
const settings = await configService.loadSettings();
|
|
154
|
+
|
|
155
|
+
let selectedTargetId: string | null = null;
|
|
156
|
+
|
|
157
|
+
// Distinguish between three cases:
|
|
158
|
+
// 1. User explicitly set "ask-every-time" → always prompt
|
|
159
|
+
// 2. User has no setting (undefined/null) → allow auto-detect
|
|
160
|
+
// 3. User has specific target → use that target
|
|
161
|
+
const isAskEveryTime = settings.defaultTarget === 'ask-every-time';
|
|
162
|
+
const hasNoSetting = !settings.defaultTarget;
|
|
163
|
+
const hasSpecificTarget = settings.defaultTarget && settings.defaultTarget !== 'ask-every-time';
|
|
164
|
+
|
|
165
|
+
if (isAskEveryTime) {
|
|
166
|
+
// User explicitly wants to be asked every time - ALWAYS prompt, never auto-detect
|
|
167
|
+
console.log(chalk.cyan('🔍 Detecting installed AI CLIs...\n'));
|
|
168
|
+
|
|
169
|
+
selectedTargetId = await promptForTargetSelection(
|
|
170
|
+
installedTargets,
|
|
171
|
+
'Select AI CLI to use:',
|
|
172
|
+
'execution'
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const installation = targetInstaller.getInstallationInfo(selectedTargetId);
|
|
176
|
+
const installed = await ensureTargetInstalled(selectedTargetId, targetInstaller, installedTargets);
|
|
177
|
+
|
|
178
|
+
if (!installed) {
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (installedTargets.includes(selectedTargetId)) {
|
|
183
|
+
console.log(chalk.green(`✓ Using ${installation?.name}\n`));
|
|
184
|
+
}
|
|
185
|
+
} else if (hasNoSetting) {
|
|
186
|
+
// No setting - use auto-detection (smart default behavior)
|
|
187
|
+
if (installedTargets.length === 1) {
|
|
188
|
+
// Exactly 1 target found - use it automatically
|
|
189
|
+
selectedTargetId = installedTargets[0];
|
|
190
|
+
const installation = targetInstaller.getInstallationInfo(selectedTargetId);
|
|
191
|
+
console.log(chalk.green(`✓ Using ${installation?.name} (auto-detected)\n`));
|
|
192
|
+
} else {
|
|
193
|
+
// 0 or multiple targets - prompt for selection
|
|
194
|
+
console.log(chalk.cyan('🔍 Detecting installed AI CLIs...\n'));
|
|
195
|
+
|
|
196
|
+
selectedTargetId = await promptForTargetSelection(
|
|
197
|
+
installedTargets,
|
|
198
|
+
'Select AI CLI to use:',
|
|
199
|
+
'execution'
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const installation = targetInstaller.getInstallationInfo(selectedTargetId);
|
|
203
|
+
const installed = await ensureTargetInstalled(selectedTargetId, targetInstaller, installedTargets);
|
|
204
|
+
|
|
205
|
+
if (!installed) {
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (installedTargets.includes(selectedTargetId)) {
|
|
210
|
+
console.log(chalk.green(`✓ Using ${installation?.name}\n`));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
} else if (hasSpecificTarget) {
|
|
214
|
+
// User has a specific target preference - ALWAYS use it
|
|
215
|
+
selectedTargetId = settings.defaultTarget;
|
|
216
|
+
const installation = targetInstaller.getInstallationInfo(selectedTargetId);
|
|
217
|
+
|
|
218
|
+
// Check if the preferred target is installed
|
|
219
|
+
if (installedTargets.includes(selectedTargetId)) {
|
|
220
|
+
console.log(chalk.green(`✓ Using ${installation?.name} (from settings)\n`));
|
|
221
|
+
} else {
|
|
222
|
+
// Preferred target not installed - try to install it
|
|
223
|
+
console.log(chalk.yellow(`⚠️ ${installation?.name} is set as default but not installed\n`));
|
|
224
|
+
const installed = await targetInstaller.install(selectedTargetId, true);
|
|
225
|
+
|
|
226
|
+
if (!installed) {
|
|
227
|
+
// Installation failed - show error and exit
|
|
228
|
+
console.log(chalk.red(`\n✗ Cannot proceed: ${installation?.name} is not installed and auto-install failed`));
|
|
229
|
+
console.log(chalk.yellow(' Please either:'));
|
|
230
|
+
console.log(chalk.cyan(' 1. Install manually (see instructions above)'));
|
|
231
|
+
console.log(chalk.cyan(' 2. Change default target: sylphx-flow settings\n'));
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
console.log();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Step 2: Auto-upgrade Flow and target CLI
|
|
240
|
+
const autoUpgrade = new AutoUpgrade(projectPath);
|
|
241
|
+
await autoUpgrade.runAutoUpgrade(selectedTargetId);
|
|
242
|
+
|
|
243
|
+
// Mode info
|
|
244
|
+
if (options.merge) {
|
|
245
|
+
console.log(chalk.cyan('🔗 Merge mode: Flow settings will be merged with your existing settings'));
|
|
246
|
+
console.log(chalk.dim(' Settings will be restored after execution\n'));
|
|
247
|
+
} else {
|
|
248
|
+
console.log(chalk.yellow('🔄 Replace mode (default): All settings will use Flow configuration'));
|
|
249
|
+
console.log(chalk.dim(' Use --merge to keep your existing settings\n'));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Create executor
|
|
253
|
+
const executor = new FlowExecutor();
|
|
254
|
+
const projectManager = executor.getProjectManager();
|
|
255
|
+
|
|
256
|
+
// Step 2: Execute attach mode lifecycle
|
|
257
|
+
try {
|
|
258
|
+
// Attach Flow environment (backup → attach → register cleanup)
|
|
259
|
+
await executor.execute(projectPath, {
|
|
260
|
+
verbose: options.verbose,
|
|
261
|
+
skipBackup: false,
|
|
262
|
+
skipSecrets: false,
|
|
263
|
+
merge: options.merge || false,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Step 3: Use the target we already selected (don't re-detect)
|
|
267
|
+
// selectedTargetId was determined earlier based on settings/auto-detect/prompt
|
|
268
|
+
const targetId = selectedTargetId;
|
|
269
|
+
|
|
270
|
+
// Step 3.5: Provider selection (Claude Code only)
|
|
271
|
+
if (targetId === 'claude-code') {
|
|
272
|
+
await selectProvider(configService);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Step 3.6: Load Flow settings and determine agent to use
|
|
276
|
+
const settings = await configService.loadSettings();
|
|
277
|
+
const flowConfig = await configService.loadFlowConfig();
|
|
278
|
+
|
|
279
|
+
// Determine which agent to use (CLI option > settings default > 'coder')
|
|
280
|
+
const agent = options.agent || settings.defaultAgent || 'coder';
|
|
281
|
+
|
|
282
|
+
// Check if agent is enabled
|
|
283
|
+
if (!flowConfig.agents[agent]?.enabled) {
|
|
284
|
+
console.log(chalk.yellow(`⚠️ Agent '${agent}' is not enabled in settings`));
|
|
285
|
+
console.log(chalk.yellow(` Enable it with: sylphx-flow settings`));
|
|
286
|
+
console.log(chalk.yellow(` Using 'coder' agent instead\n`));
|
|
287
|
+
// Fallback to first enabled agent or coder
|
|
288
|
+
const enabledAgents = await configService.getEnabledAgents();
|
|
289
|
+
const fallbackAgent = enabledAgents.length > 0 ? enabledAgents[0] : 'coder';
|
|
290
|
+
options.agent = fallbackAgent;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
console.log(chalk.cyan(`🤖 Running agent: ${agent}\n`));
|
|
294
|
+
|
|
295
|
+
// Load enabled rules and output styles from config
|
|
296
|
+
const enabledRules = await configService.getEnabledRules();
|
|
297
|
+
const enabledOutputStyles = await configService.getEnabledOutputStyles();
|
|
298
|
+
|
|
299
|
+
console.log(chalk.dim(` Enabled rules: ${enabledRules.join(', ')}`));
|
|
300
|
+
console.log(chalk.dim(` Enabled output styles: ${enabledOutputStyles.join(', ')}\n`));
|
|
301
|
+
|
|
302
|
+
// Load agent content with only enabled rules and styles
|
|
303
|
+
const agentContent = await loadAgentContent(
|
|
304
|
+
agent,
|
|
305
|
+
options.agentFile,
|
|
306
|
+
enabledRules,
|
|
307
|
+
enabledOutputStyles
|
|
308
|
+
);
|
|
309
|
+
const agentInstructions = extractAgentInstructions(agentContent);
|
|
310
|
+
|
|
311
|
+
const systemPrompt = `AGENT INSTRUCTIONS:\n${agentInstructions}`;
|
|
312
|
+
|
|
313
|
+
const userPrompt = prompt?.trim() || '';
|
|
314
|
+
|
|
315
|
+
// Prepare run options
|
|
316
|
+
const runOptions: RunCommandOptions = {
|
|
317
|
+
target: targetId,
|
|
318
|
+
verbose: options.verbose || false,
|
|
319
|
+
dryRun: options.dryRun || false,
|
|
320
|
+
agent,
|
|
321
|
+
agentFile: options.agentFile,
|
|
322
|
+
prompt,
|
|
323
|
+
print: options.print,
|
|
324
|
+
continue: options.continue,
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// Step 4: Execute command
|
|
328
|
+
await executeTargetCommand(targetId, systemPrompt, userPrompt, runOptions);
|
|
329
|
+
|
|
330
|
+
// Step 5: Cleanup (restore environment)
|
|
331
|
+
await executor.cleanup(projectPath);
|
|
332
|
+
|
|
333
|
+
console.log(chalk.green('✓ Session complete\n'));
|
|
334
|
+
} catch (error) {
|
|
335
|
+
// Handle user cancellation gracefully
|
|
336
|
+
if (error instanceof UserCancelledError) {
|
|
337
|
+
console.log(chalk.yellow('\n⚠️ Operation cancelled by user'));
|
|
338
|
+
try {
|
|
339
|
+
await executor.cleanup(projectPath);
|
|
340
|
+
console.log(chalk.green(' ✓ Settings restored\n'));
|
|
341
|
+
} catch (cleanupError) {
|
|
342
|
+
console.error(chalk.red(' ✗ Cleanup failed:'), cleanupError);
|
|
343
|
+
}
|
|
344
|
+
process.exit(0);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
console.error(chalk.red.bold('\n✗ Execution failed:'), error);
|
|
348
|
+
|
|
349
|
+
// Ensure cleanup even on error
|
|
350
|
+
try {
|
|
351
|
+
await executor.cleanup(projectPath);
|
|
352
|
+
} catch (cleanupError) {
|
|
353
|
+
console.error(chalk.red('✗ Cleanup failed:'), cleanupError);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
throw error;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Main flow execution entry point
|
|
362
|
+
*/
|
|
363
|
+
export async function executeFlow(
|
|
364
|
+
prompt: string | undefined,
|
|
365
|
+
options: FlowOptions
|
|
366
|
+
): Promise<void> {
|
|
367
|
+
// Resolve prompt (handle file input)
|
|
368
|
+
const resolvedPrompt = await resolvePrompt(prompt);
|
|
369
|
+
|
|
370
|
+
// Use V2 attach mode execution
|
|
371
|
+
await executeFlowV2(resolvedPrompt, options);
|
|
372
|
+
}
|
|
@@ -124,28 +124,11 @@ export async function executeFlowOnce(prompt: string | undefined, options: FlowO
|
|
|
124
124
|
await showStatus(state);
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
// Step 1: Check for upgrades
|
|
127
|
+
// Step 1: Check for upgrades (non-intrusive notification)
|
|
128
128
|
if (!options.quick) {
|
|
129
129
|
await checkUpgrades(state, options);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
// Step 1: Upgrade (if requested)
|
|
133
|
-
if (options.upgrade && state.outdated && state.latestVersion) {
|
|
134
|
-
console.log(chalk.cyan.bold('━━━ 📦 Upgrading Flow\n'));
|
|
135
|
-
await upgradeManager.upgradeFlow(state);
|
|
136
|
-
console.log(chalk.green('✓ Upgrade complete\n'));
|
|
137
|
-
// Re-detect after upgrade
|
|
138
|
-
state.version = state.latestVersion;
|
|
139
|
-
state.outdated = false;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Step 2: Upgrade target (if requested)
|
|
143
|
-
if (options.upgradeTarget && state.target) {
|
|
144
|
-
console.log(chalk.cyan.bold(`━━━ 🎯 Upgrading ${state.target}\n`));
|
|
145
|
-
await upgradeManager.upgradeTarget(state);
|
|
146
|
-
console.log(chalk.green('✓ Target upgrade complete\n'));
|
|
147
|
-
}
|
|
148
|
-
|
|
149
132
|
// Step 2.5: Check component integrity (only if we have valid state)
|
|
150
133
|
await checkComponentIntegrity(state, options);
|
|
151
134
|
|
|
@@ -26,14 +26,15 @@ export interface FlowOptions {
|
|
|
26
26
|
// Smart configuration options
|
|
27
27
|
selectProvider?: boolean;
|
|
28
28
|
selectAgent?: boolean;
|
|
29
|
-
useDefaults?: boolean;
|
|
30
29
|
provider?: string;
|
|
31
|
-
quick?: boolean;
|
|
32
30
|
|
|
33
31
|
// Execution modes
|
|
34
32
|
print?: boolean;
|
|
35
33
|
continue?: boolean;
|
|
36
34
|
|
|
35
|
+
// Attach strategy
|
|
36
|
+
merge?: boolean; // Merge with user settings instead of replacing (default: replace)
|
|
37
|
+
|
|
37
38
|
// Loop mode
|
|
38
39
|
loop?: number;
|
|
39
40
|
maxRuns?: number;
|
|
@@ -11,49 +11,23 @@ import { StateDetector } from '../core/state-detector.js';
|
|
|
11
11
|
import { UpgradeManager } from '../core/upgrade-manager.js';
|
|
12
12
|
import { showWelcome } from '../utils/display/banner.js';
|
|
13
13
|
import { showStatus } from '../utils/display/status.js';
|
|
14
|
-
import { executeFlow } from './flow/execute.js';
|
|
14
|
+
import { executeFlow } from './flow/execute-v2.js';
|
|
15
15
|
import type { FlowOptions } from './flow/types.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* Flow command (simplified for attach mode)
|
|
19
19
|
*/
|
|
20
20
|
export const flowCommand = new Command('flow')
|
|
21
|
-
.description('
|
|
22
|
-
|
|
23
|
-
//
|
|
24
|
-
.option('--init-only', 'Only initialize, do not run')
|
|
25
|
-
.option('--run-only', 'Only run, skip initialization')
|
|
26
|
-
.option('--sync', 'Synchronize with Flow templates (delete and re-install template files)')
|
|
27
|
-
.option('--upgrade', 'Upgrade Sylphx Flow to latest version')
|
|
28
|
-
.option('--upgrade-target', 'Upgrade target platform (Claude Code/OpenCode)')
|
|
29
|
-
|
|
30
|
-
// Smart configuration options
|
|
31
|
-
.option('--quick', 'Quick mode: use saved defaults and skip all prompts')
|
|
32
|
-
.option('--select-provider', 'Prompt to select provider each run')
|
|
33
|
-
.option('--select-agent', 'Prompt to select agent each run')
|
|
34
|
-
.option('--use-defaults', 'Skip prompts, use saved defaults')
|
|
35
|
-
.option('--provider <provider>', 'Override provider for this run (anthropic|z.ai|kimi)')
|
|
36
|
-
|
|
37
|
-
// Init options
|
|
38
|
-
.option('--target <type>', 'Target platform (opencode, claude-code, auto-detect)')
|
|
39
|
-
.option('--verbose', 'Show detailed output')
|
|
40
|
-
.option('--dry-run', 'Show what would be done without making changes')
|
|
41
|
-
.option('--no-mcp', 'Skip MCP installation')
|
|
42
|
-
.option('--no-agents', 'Skip agents installation')
|
|
43
|
-
.option('--no-rules', 'Skip rules installation')
|
|
44
|
-
.option('--no-output-styles', 'Skip output styles installation')
|
|
45
|
-
.option('--no-slash-commands', 'Skip slash commands installation')
|
|
46
|
-
.option('--no-hooks', 'Skip hooks setup')
|
|
47
|
-
|
|
48
|
-
// Run options
|
|
21
|
+
.description('Run Flow with automatic environment attach')
|
|
22
|
+
|
|
23
|
+
// Core options
|
|
49
24
|
.option('--agent <name>', 'Agent to use (default: coder)', 'coder')
|
|
50
25
|
.option('--agent-file <path>', 'Load agent from specific file')
|
|
26
|
+
.option('--verbose', 'Show detailed output')
|
|
27
|
+
.option('--dry-run', 'Show what would be done without making changes')
|
|
51
28
|
.option('-p, --print', 'Headless print mode (output only, no interactive)')
|
|
52
29
|
.option('-c, --continue', 'Continue previous conversation (requires print mode)')
|
|
53
|
-
|
|
54
|
-
// Loop options
|
|
55
|
-
.option('--loop [interval]', 'Loop mode: run repeatedly (optional cooldown in seconds)')
|
|
56
|
-
.option('--max-runs <number>', 'Maximum loop iterations (default: infinite)', parseInt)
|
|
30
|
+
.option('--merge', 'Merge Flow settings with existing settings (default: replace all)')
|
|
57
31
|
|
|
58
32
|
// Prompt argument
|
|
59
33
|
.argument('[prompt]', 'Prompt to execute with agent (optional, supports @file.txt for file input)')
|
|
@@ -63,34 +37,17 @@ export const flowCommand = new Command('flow')
|
|
|
63
37
|
});
|
|
64
38
|
|
|
65
39
|
/**
|
|
66
|
-
* Setup command -
|
|
67
|
-
* Kept for backward compatibility
|
|
40
|
+
* Setup command - deprecated (attach mode is automatic)
|
|
68
41
|
*/
|
|
69
42
|
export const setupCommand = new Command('setup')
|
|
70
|
-
.description('
|
|
43
|
+
.description('[DEPRECATED] No longer needed - Flow uses automatic attach mode')
|
|
71
44
|
.action(async () => {
|
|
72
|
-
console.log(chalk.yellow('
|
|
73
|
-
console.log(chalk.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
await runInit({
|
|
79
|
-
target: undefined,
|
|
80
|
-
verbose: false,
|
|
81
|
-
dryRun: false,
|
|
82
|
-
clear: false,
|
|
83
|
-
mcp: true,
|
|
84
|
-
agents: true,
|
|
85
|
-
rules: true,
|
|
86
|
-
outputStyles: true,
|
|
87
|
-
slashCommands: true,
|
|
88
|
-
hooks: true,
|
|
89
|
-
helpOption: () => {},
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
console.log(chalk.green('\n✅ Setup complete!'));
|
|
93
|
-
console.log(chalk.dim('\nNext time, use: flow --init-only'));
|
|
45
|
+
console.log(chalk.yellow('⚠️ The "setup" command is deprecated.\n'));
|
|
46
|
+
console.log(chalk.cyan('Flow now uses automatic attach mode:'));
|
|
47
|
+
console.log(chalk.dim(' • No installation needed'));
|
|
48
|
+
console.log(chalk.dim(' • Environment attached automatically'));
|
|
49
|
+
console.log(chalk.dim(' • Restored on exit\n'));
|
|
50
|
+
console.log(chalk.green('✨ Just run: sylphx-flow "your prompt"\n'));
|
|
94
51
|
});
|
|
95
52
|
|
|
96
53
|
/**
|
|
@@ -193,11 +150,12 @@ export const doctorCommand = new Command('doctor')
|
|
|
193
150
|
export const upgradeCommand = new Command('upgrade')
|
|
194
151
|
.description('Upgrade Sylphx Flow and components')
|
|
195
152
|
.option('--check', 'Only check for updates, do not upgrade')
|
|
153
|
+
.option('--auto', 'Automatically install updates via npm')
|
|
196
154
|
.option('--components', 'Upgrade components (agents, rules, etc)', true)
|
|
197
155
|
.option('--target', 'Upgrade target platform (Claude Code/OpenCode)')
|
|
198
156
|
.option('--verbose', 'Show detailed output')
|
|
199
157
|
.action(async (options) => {
|
|
200
|
-
console.log(chalk.cyan.bold('📦
|
|
158
|
+
console.log(chalk.cyan.bold('📦 Checking for updates\n'));
|
|
201
159
|
|
|
202
160
|
const detector = new StateDetector();
|
|
203
161
|
const upgradeManager = new UpgradeManager();
|
|
@@ -205,7 +163,7 @@ export const upgradeCommand = new Command('upgrade')
|
|
|
205
163
|
const updates = await upgradeManager.checkUpdates();
|
|
206
164
|
|
|
207
165
|
if (!updates.flowUpdate && !updates.targetUpdate) {
|
|
208
|
-
console.log(chalk.green('✓
|
|
166
|
+
console.log(chalk.green('✓ All components are up to date\n'));
|
|
209
167
|
return;
|
|
210
168
|
}
|
|
211
169
|
|
|
@@ -219,7 +177,7 @@ export const upgradeCommand = new Command('upgrade')
|
|
|
219
177
|
|
|
220
178
|
// Check only
|
|
221
179
|
if (options.check) {
|
|
222
|
-
console.log('\n' + chalk.dim('
|
|
180
|
+
console.log('\n' + chalk.dim('Run without --check to upgrade'));
|
|
223
181
|
return;
|
|
224
182
|
}
|
|
225
183
|
|
|
@@ -229,13 +187,13 @@ export const upgradeCommand = new Command('upgrade')
|
|
|
229
187
|
{
|
|
230
188
|
type: 'confirm',
|
|
231
189
|
name: 'confirm',
|
|
232
|
-
message: '
|
|
190
|
+
message: 'Upgrade to latest version?',
|
|
233
191
|
default: true,
|
|
234
192
|
},
|
|
235
193
|
]);
|
|
236
194
|
|
|
237
195
|
if (!confirm) {
|
|
238
|
-
console.log(chalk.dim('\
|
|
196
|
+
console.log(chalk.dim('\nUpgrade cancelled'));
|
|
239
197
|
return;
|
|
240
198
|
}
|
|
241
199
|
|
|
@@ -243,16 +201,21 @@ export const upgradeCommand = new Command('upgrade')
|
|
|
243
201
|
console.log('');
|
|
244
202
|
|
|
245
203
|
const state = await detector.detect();
|
|
204
|
+
const autoInstall = options.auto || false;
|
|
246
205
|
|
|
247
206
|
if (updates.flowUpdate) {
|
|
248
|
-
console.log(chalk.cyan.bold('\n━
|
|
249
|
-
await upgradeManager.upgradeFlow(state);
|
|
207
|
+
console.log(chalk.cyan.bold('\n━ Upgrading Sylphx Flow\n'));
|
|
208
|
+
await upgradeManager.upgradeFlow(state, autoInstall);
|
|
250
209
|
}
|
|
251
210
|
|
|
252
211
|
if (updates.targetUpdate && options.target) {
|
|
253
|
-
console.log(chalk.cyan.bold('\n━
|
|
254
|
-
await upgradeManager.upgradeTarget(state);
|
|
212
|
+
console.log(chalk.cyan.bold('\n━ Upgrading Target\n'));
|
|
213
|
+
await upgradeManager.upgradeTarget(state, autoInstall);
|
|
255
214
|
}
|
|
256
215
|
|
|
257
|
-
console.log(chalk.green('\n✓
|
|
216
|
+
console.log(chalk.green('\n✓ Upgrade complete\n'));
|
|
217
|
+
|
|
218
|
+
if (!autoInstall) {
|
|
219
|
+
console.log(chalk.dim('💡 Tip: Use --auto flag to automatically install updates via npm\n'));
|
|
220
|
+
}
|
|
258
221
|
});
|