@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,92 +0,0 @@
1
- import boxen from 'boxen';
2
- import chalk from 'chalk';
3
- import gradient from 'gradient-string';
4
- import {
5
- selectAndValidateTarget,
6
- previewDryRun,
7
- installComponents,
8
- type InitOptions,
9
- } from './init-core.js';
10
-
11
- /**
12
- * Legacy init with full UI - used by setup command for backward compatibility
13
- * The flow command uses init-core functions directly for better integration
14
- */
15
- export async function runInit(options: InitOptions): Promise<void> {
16
- // Create ASCII art title
17
- const title = `
18
- ███████╗██╗ ██╗██╗ ██████╗ ██╗ ██╗██╗ ██╗ ███████╗██╗ ██████╗ ██╗ ██╗
19
- ██╔════╝╚██╗ ██╔╝██║ ██╔══██╗██║ ██║╚██╗██╔╝ ██╔════╝██║ ██╔═══██╗██║ ██║
20
- ███████╗ ╚████╔╝ ██║ ██████╔╝███████║ ╚███╔╝ █████╗ ██║ ██║ ██║██║ █╗ ██║
21
- ╚════██║ ╚██╔╝ ██║ ██╔═══╝ ██╔══██║ ██╔██╗ ██╔══╝ ██║ ██║ ██║██║███╗██║
22
- ███████║ ██║ ███████╗██║ ██║ ██║██╔╝ ██╗ ██║ ███████╗╚██████╔╝╚███╔███╔╝
23
- ╚══════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝
24
- `;
25
-
26
- console.log(gradient(['cyan', 'blue'])(title));
27
- console.log(chalk.dim.cyan(' Project Initialization\n'));
28
-
29
- // Select and validate target using core function
30
- const targetId = await selectAndValidateTarget(options);
31
-
32
- // Dry run preview
33
- if (options.dryRun) {
34
- console.log(
35
- boxen(
36
- chalk.yellow('⚠ Dry Run Mode') + chalk.dim('\nNo changes will be made to your project'),
37
- {
38
- padding: 1,
39
- margin: { top: 0, bottom: 1, left: 0, right: 0 },
40
- borderStyle: 'round',
41
- borderColor: 'yellow',
42
- }
43
- )
44
- );
45
-
46
- await previewDryRun(targetId, options);
47
-
48
- console.log(
49
- '\n' +
50
- boxen(chalk.green.bold('✓ Dry run complete'), {
51
- padding: { top: 0, bottom: 0, left: 2, right: 2 },
52
- margin: 0,
53
- borderStyle: 'round',
54
- borderColor: 'green',
55
- }) +
56
- '\n'
57
- );
58
- return;
59
- }
60
-
61
- console.log(chalk.cyan.bold('\n━━━ Installing Core Components ━━━\n'));
62
-
63
- // Install components using core function
64
- const result = await installComponents(targetId, options);
65
-
66
- // Success summary
67
- console.log(
68
- '\n' +
69
- boxen(
70
- chalk.green.bold('✓ Setup complete!') +
71
- '\n\n' +
72
- chalk.dim(`Target: ${result.targetName}`) +
73
- '\n\n' +
74
- chalk.cyan('Ready to code with Sylphx Flow'),
75
- {
76
- padding: 1,
77
- margin: 0,
78
- borderStyle: 'round',
79
- borderColor: 'green',
80
- }
81
- ) +
82
- '\n'
83
- );
84
- }
85
-
86
- /**
87
- * LEGACY: init command has been integrated into the flow command.
88
- * Use `flow --init-only` instead of standalone `init` command.
89
- *
90
- * This export is kept for backward compatibility but will be removed in future versions.
91
- * The runInit() function is the core implementation used by flow command.
92
- */
@@ -1,331 +0,0 @@
1
- /**
2
- * Core initialization logic - extracted for reuse without UI coupling
3
- * Used by both flow command (integrated) and legacy init command (standalone)
4
- */
5
-
6
- import chalk from 'chalk';
7
- import ora from 'ora';
8
- import { targetManager } from '../core/target-manager.js';
9
- import { CLIError } from '../utils/error-handler.js';
10
- import { projectSettings } from '../utils/config/settings.js';
11
- import { validateTarget } from '../utils/config/target-config.js';
12
- import { ConfigService } from '../services/config-service.js';
13
-
14
- export interface InitOptions {
15
- target?: string;
16
- verbose?: boolean;
17
- dryRun?: boolean;
18
- clear?: boolean;
19
- mcp?: boolean;
20
- agents?: boolean;
21
- rules?: boolean;
22
- outputStyles?: boolean;
23
- slashCommands?: boolean;
24
- hooks?: boolean;
25
- quiet?: boolean; // Suppress all output for programmatic use
26
- }
27
-
28
- export interface ComponentInstallResult {
29
- targetId: string;
30
- targetName: string;
31
- installed: {
32
- mcp?: number;
33
- agents?: number;
34
- outputStyles?: number;
35
- rules?: number;
36
- slashCommands?: number;
37
- hooks?: number;
38
- };
39
- }
40
-
41
- /**
42
- * Select and validate target - PURE LOGIC, no UI
43
- * @returns targetId
44
- */
45
- export async function selectAndValidateTarget(options: InitOptions): Promise<string> {
46
- let targetId = options.target;
47
-
48
- // Target selection (with UI prompt if needed)
49
- if (!targetId) {
50
- try {
51
- targetId = await targetManager.promptForTargetSelection();
52
- } catch (error) {
53
- // User cancelled with Ctrl+C - exit gracefully
54
- if (error instanceof Error && error.name === 'ExitPromptError') {
55
- console.log('\n');
56
- process.exit(0);
57
- }
58
- throw error;
59
- }
60
- }
61
-
62
- // Validate target
63
- if (targetId) {
64
- try {
65
- validateTarget(targetId);
66
- } catch (error) {
67
- if (error instanceof Error) {
68
- throw new CLIError(error.message, 'UNSUPPORTED_TARGET');
69
- }
70
- throw error;
71
- }
72
- }
73
-
74
- if (!targetId) {
75
- throw new Error('Target ID not set');
76
- }
77
-
78
- return targetId;
79
- }
80
-
81
- /**
82
- * Preview what will be installed in dry run mode
83
- */
84
- export async function previewDryRun(targetId: string, options: InitOptions): Promise<void> {
85
- const targetOption = targetManager.getTarget(targetId);
86
- if (targetOption._tag === 'None') {
87
- throw new Error(`Target not found: ${targetId}`);
88
- }
89
-
90
- const target = targetOption.value;
91
-
92
- if (options.mcp !== false && target.setupMCP) {
93
- console.log(chalk.cyan.bold('MCP Tools:'));
94
- console.log(chalk.dim(' ✓ MCP servers will be configured'));
95
- }
96
-
97
- if (options.agents !== false && target.setupAgents) {
98
- console.log(chalk.cyan.bold('\nAgents:'));
99
- console.log(chalk.dim(' ✓ Development agents will be installed'));
100
- }
101
-
102
- if (options.outputStyles !== false && target.setupOutputStyles) {
103
- console.log(chalk.cyan.bold('\nOutput Styles:'));
104
- console.log(chalk.dim(' ✓ Output styles will be installed'));
105
- }
106
-
107
- if (options.rules !== false && target.setupRules) {
108
- console.log(chalk.cyan.bold('\nRules:'));
109
- console.log(chalk.dim(' ✓ Custom rules will be installed'));
110
- }
111
-
112
- if (options.slashCommands !== false && target.setupSlashCommands) {
113
- console.log(chalk.cyan.bold('\nSlash Commands:'));
114
- console.log(chalk.dim(' ✓ Slash commands will be installed'));
115
- }
116
-
117
- if (options.hooks !== false && target.setupHooks) {
118
- console.log(chalk.cyan.bold('\nHooks:'));
119
- console.log(chalk.dim(' ✓ Hooks will be configured'));
120
- }
121
- }
122
-
123
- /**
124
- * Install all components - CORE LOGIC with minimal UI
125
- */
126
- export async function installComponents(
127
- targetId: string,
128
- options: InitOptions
129
- ): Promise<ComponentInstallResult> {
130
- const targetOption = targetManager.getTarget(targetId);
131
- if (targetOption._tag === 'None') {
132
- throw new Error(`Target not found: ${targetId}`);
133
- }
134
-
135
- const target = targetOption.value;
136
- const quiet = options.quiet || false;
137
- const result: ComponentInstallResult = {
138
- targetId,
139
- targetName: target.name,
140
- installed: {},
141
- };
142
-
143
- // Setup MCP servers if target supports it and MCP is enabled
144
- // Note: No spinner here because MCP setup is interactive (user prompts)
145
- if (target.setupMCP && options.mcp !== false) {
146
- try {
147
- const mcpResult = await target.setupMCP(process.cwd(), options);
148
- result.installed.mcp = mcpResult.count;
149
-
150
- if (!quiet) {
151
- if (mcpResult.count > 0) {
152
- console.log(
153
- chalk.green(
154
- `✔ Installed ${chalk.cyan(mcpResult.count)} MCP server${mcpResult.count !== 1 ? 's' : ''}`
155
- )
156
- );
157
- } else {
158
- console.log(chalk.dim('ℹ No MCP servers selected'));
159
- }
160
- }
161
- } catch (error) {
162
- // If user cancels MCP setup (Ctrl+C), continue with other components
163
- if (error instanceof Error && error.name === 'ExitPromptError') {
164
- if (!quiet) {
165
- console.log(chalk.yellow('\n⚠️ MCP setup cancelled, continuing with other components\n'));
166
- }
167
- } else {
168
- if (!quiet) {
169
- console.error(chalk.red('✖ Failed to setup MCP servers'));
170
- }
171
- throw error;
172
- }
173
- }
174
- }
175
-
176
- // Install agents if target supports it and agents are not skipped
177
- if (target.setupAgents && options.agents !== false) {
178
- const agentSpinner = quiet ? null : ora({ text: 'Installing agents', color: 'cyan' }).start();
179
- try {
180
- const agentResult = await target.setupAgents(process.cwd(), { ...options, quiet: true, force: options.clear });
181
- result.installed.agents = agentResult.count;
182
-
183
- if (agentSpinner) {
184
- agentSpinner.succeed(
185
- chalk.green(`Installed ${chalk.cyan(agentResult.count)} agent${agentResult.count !== 1 ? 's' : ''}`)
186
- );
187
- }
188
- } catch (error) {
189
- if (agentSpinner) {
190
- agentSpinner.fail(chalk.red('Failed to install agents'));
191
- }
192
- throw error;
193
- }
194
- }
195
-
196
- // Install output styles if target supports it and output styles are not skipped
197
- if (target.setupOutputStyles && options.outputStyles !== false) {
198
- const stylesSpinner = quiet ? null : ora({ text: 'Installing output styles', color: 'cyan' }).start();
199
- try {
200
- const stylesResult = await target.setupOutputStyles(process.cwd(), { ...options, quiet: true, force: options.clear });
201
- result.installed.outputStyles = stylesResult.count;
202
-
203
- if (stylesSpinner) {
204
- if (stylesResult.count > 0) {
205
- stylesSpinner.succeed(
206
- chalk.green(
207
- `Installed ${chalk.cyan(stylesResult.count)} output style${stylesResult.count !== 1 ? 's' : ''}`
208
- )
209
- );
210
- } else if (stylesResult.message) {
211
- stylesSpinner.info(chalk.dim(stylesResult.message));
212
- } else {
213
- stylesSpinner.info(chalk.dim('No output styles to install'));
214
- }
215
- }
216
- } catch (error) {
217
- if (stylesSpinner) {
218
- stylesSpinner.fail(chalk.red('Failed to install output styles'));
219
- }
220
- throw error;
221
- }
222
- }
223
-
224
- // Install rules if target supports it and rules are not skipped
225
- if (target.setupRules && options.rules !== false) {
226
- const rulesSpinner = quiet ? null : ora({ text: 'Installing rules', color: 'cyan' }).start();
227
- try {
228
- const rulesResult = await target.setupRules(process.cwd(), { ...options, quiet: true, force: options.clear });
229
- result.installed.rules = rulesResult.count;
230
-
231
- if (rulesSpinner) {
232
- if (rulesResult.count > 0) {
233
- rulesSpinner.succeed(
234
- chalk.green(
235
- `Installed ${chalk.cyan(rulesResult.count)} rule${rulesResult.count !== 1 ? 's' : ''}`
236
- )
237
- );
238
- } else if (rulesResult.message) {
239
- rulesSpinner.info(chalk.dim(rulesResult.message));
240
- } else {
241
- rulesSpinner.info(chalk.dim('No rules to install'));
242
- }
243
- }
244
- } catch (error) {
245
- if (rulesSpinner) {
246
- rulesSpinner.fail(chalk.red('Failed to install rules'));
247
- }
248
- throw error;
249
- }
250
- }
251
-
252
- // Install slash commands if target supports it and slash commands are not skipped
253
- if (target.setupSlashCommands && options.slashCommands !== false) {
254
- const commandsSpinner = quiet ? null : ora({
255
- text: 'Installing slash commands',
256
- color: 'cyan',
257
- }).start();
258
- try {
259
- const commandsResult = await target.setupSlashCommands(process.cwd(), { ...options, quiet: true, force: options.clear });
260
- result.installed.slashCommands = commandsResult.count;
261
-
262
- if (commandsSpinner) {
263
- if (commandsResult.count > 0) {
264
- commandsSpinner.succeed(
265
- chalk.green(
266
- `Installed ${chalk.cyan(commandsResult.count)} slash command${commandsResult.count !== 1 ? 's' : ''}`
267
- )
268
- );
269
- } else if (commandsResult.message) {
270
- commandsSpinner.info(chalk.dim(commandsResult.message));
271
- } else {
272
- commandsSpinner.info(chalk.dim('No slash commands to install'));
273
- }
274
- }
275
- } catch (error) {
276
- if (commandsSpinner) {
277
- commandsSpinner.fail(chalk.red('Failed to install slash commands'));
278
- }
279
- throw error;
280
- }
281
- }
282
-
283
- // Setup hooks if target supports it and hooks are not skipped
284
- if (target.setupHooks && options.hooks !== false) {
285
- const hooksSpinner = quiet ? null : ora({ text: 'Setting up hooks', color: 'cyan' }).start();
286
- try {
287
- const hooksResult = await target.setupHooks(process.cwd(), { ...options, quiet: true });
288
- result.installed.hooks = hooksResult.count;
289
-
290
- if (hooksSpinner) {
291
- if (hooksResult.count > 0) {
292
- const message = hooksResult.message
293
- ? `Configured ${chalk.cyan(hooksResult.count)} hook${hooksResult.count !== 1 ? 's' : ''} - ${hooksResult.message}`
294
- : `Configured ${chalk.cyan(hooksResult.count)} hook${hooksResult.count !== 1 ? 's' : ''}`;
295
- hooksSpinner.succeed(chalk.green(message));
296
- } else {
297
- hooksSpinner.info(chalk.dim(hooksResult.message || 'No hooks to configure'));
298
- }
299
- }
300
- } catch (error) {
301
- // Don't fail entire setup if hooks fail
302
- if (hooksSpinner) {
303
- hooksSpinner.warn(chalk.yellow('Could not setup hooks'));
304
- console.warn(chalk.dim(` ${error instanceof Error ? error.message : String(error)}`));
305
- }
306
- }
307
- }
308
-
309
- // Save the selected target as project default
310
- try {
311
- await projectSettings.setDefaultTarget(targetId);
312
-
313
- // Save to new ConfigService for proper layered configuration
314
- await ConfigService.saveProjectSettings({
315
- target: targetId,
316
- version: '1.0.0',
317
- lastUpdated: new Date().toISOString(),
318
- });
319
- } catch (error) {
320
- // Don't fail the entire setup if we can't save settings
321
- if (!quiet) {
322
- console.warn(
323
- chalk.yellow(
324
- `⚠ Warning: Could not save default target: ${error instanceof Error ? error.message : String(error)}`
325
- )
326
- );
327
- }
328
- }
329
-
330
- return result;
331
- }
@@ -1,126 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { Command } from 'commander';
4
- import { targetManager } from '../core/target-manager.js';
5
- import { CLIError } from '../utils/error-handler.js';
6
- import { getAgentsDir } from '../utils/config/paths.js';
7
-
8
- export async function loadAgentContent(
9
- agentName: string,
10
- agentFilePath?: string,
11
- enabledRules?: string[],
12
- enabledOutputStyles?: string[]
13
- ): Promise<string> {
14
- const { enhanceAgentContent } = await import('../utils/agent-enhancer.js');
15
-
16
- try {
17
- // If specific file path provided, load from there
18
- if (agentFilePath) {
19
- const content = await fs.readFile(path.resolve(agentFilePath), 'utf-8');
20
- // Enhance with enabled rules and styles
21
- return await enhanceAgentContent(content, enabledRules, enabledOutputStyles);
22
- }
23
-
24
- // First try to load from .claude/agents/ directory (processed agents with rules and styles)
25
- const claudeAgentPath = path.join(process.cwd(), '.claude', 'agents', `${agentName}.md`);
26
-
27
- try {
28
- const content = await fs.readFile(claudeAgentPath, 'utf-8');
29
- return content;
30
- } catch (_error) {
31
- // Try to load from local agents/ directory (user-defined agents)
32
- const localAgentPath = path.join(process.cwd(), 'agents', `${agentName}.md`);
33
-
34
- try {
35
- const content = await fs.readFile(localAgentPath, 'utf-8');
36
- // Enhance user-defined agents with enabled rules and styles
37
- return await enhanceAgentContent(content, enabledRules, enabledOutputStyles);
38
- } catch (_error2) {
39
- // Try to load from the package's agents directory
40
- const packageAgentsDir = getAgentsDir();
41
- const packageAgentPath = path.join(packageAgentsDir, `${agentName}.md`);
42
-
43
- const content = await fs.readFile(packageAgentPath, 'utf-8');
44
- // Enhance package agents with enabled rules and styles
45
- return await enhanceAgentContent(content, enabledRules, enabledOutputStyles);
46
- }
47
- }
48
- } catch (_error) {
49
- throw new CLIError(
50
- `Agent '${agentName}' not found${agentFilePath ? ` at ${agentFilePath}` : ''}`,
51
- 'AGENT_NOT_FOUND'
52
- );
53
- }
54
- }
55
-
56
- export function extractAgentInstructions(agentContent: string): string {
57
- // Extract content after YAML front matter
58
- const yamlFrontMatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/;
59
- const match = agentContent.match(yamlFrontMatterRegex);
60
-
61
- if (match) {
62
- return agentContent.substring(match[0].length).trim();
63
- }
64
-
65
- // If no front matter, return the full content
66
- return agentContent.trim();
67
- }
68
-
69
- function executeTargetCommand(
70
- targetId: string,
71
- systemPrompt: string,
72
- userPrompt: string,
73
- options: RunCommandOptions
74
- ): Promise<void> {
75
- // Get the target object
76
- const targetOption = targetManager.getTarget(targetId);
77
-
78
- if (targetOption._tag === 'None') {
79
- throw new CLIError(`Target not found: ${targetId}`, 'TARGET_NOT_FOUND');
80
- }
81
-
82
- const target = targetOption.value;
83
-
84
- // Check if the target is implemented
85
- if (!target.isImplemented) {
86
- throw new CLIError(
87
- `Target '${targetId}' is not implemented. Supported targets: ${getExecutableTargets().join(', ')}`,
88
- 'TARGET_NOT_IMPLEMENTED'
89
- );
90
- }
91
-
92
- // Check if the target supports command execution
93
- if (!target.executeCommand) {
94
- throw new CLIError(
95
- `Target '${targetId}' does not support command execution. Supported targets: ${getExecutableTargets().join(', ')}`,
96
- 'EXECUTION_NOT_SUPPORTED'
97
- );
98
- }
99
-
100
- // Use the target's executeCommand method
101
- return target.executeCommand(systemPrompt, userPrompt, options);
102
- }
103
-
104
- /**
105
- * Get list of targets that support command execution
106
- */
107
- function getExecutableTargets(): string[] {
108
- return targetManager.getImplementedTargetIDs().filter((targetId) => {
109
- const targetOption = targetManager.getTarget(targetId);
110
- if (targetOption._tag === 'None') {
111
- return false;
112
- }
113
- return targetOption.value.executeCommand !== undefined;
114
- });
115
- }
116
-
117
- /**
118
- * LEGACY: run command has been integrated into the flow command.
119
- * Use `flow [prompt]` instead of standalone `run` command.
120
- *
121
- * This file now only exports utility functions:
122
- * - loadAgentContent()
123
- * - extractAgentInstructions()
124
- *
125
- * These are used internally by the flow command.
126
- */