@sylphx/flow 2.1.2 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/package.json +1 -1
- package/src/commands/flow/execute-v2.ts +3 -2
- package/src/commands/flow/index.ts +2 -4
- package/src/commands/flow/types.ts +0 -7
- package/src/targets/claude-code.ts +4 -3
- package/src/utils/agent-enhancer.ts +109 -1
- package/src/commands/flow/execute.ts +0 -453
- package/src/commands/flow/setup.ts +0 -312
- package/src/commands/run-command.ts +0 -126
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @sylphx/flow
|
|
2
2
|
|
|
3
|
+
## 2.1.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f571de3: Fix rules and output styles loading logic
|
|
8
|
+
|
|
9
|
+
- Fix output styles duplication: Only attach during runtime, not during attach phase
|
|
10
|
+
- Fix rules loading: Use intersection of agent frontmatter rules and globally enabled rules
|
|
11
|
+
- Remove deprecated command files (execute.ts, setup.ts, run-command.ts)
|
|
12
|
+
- Move loadAgentContent and extractAgentInstructions to agent-enhancer.ts
|
|
13
|
+
|
|
3
14
|
## 2.1.2
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sylphx/flow",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "One CLI to rule them all. Unified orchestration layer for Claude Code, OpenCode, Cursor and all AI development tools. Auto-detection, auto-installation, auto-upgrade.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,7 +9,7 @@ import { FlowExecutor } from '../../core/flow-executor.js';
|
|
|
9
9
|
import { targetManager } from '../../core/target-manager.js';
|
|
10
10
|
import { UpgradeManager } from '../../core/upgrade-manager.js';
|
|
11
11
|
import { showWelcome } from '../../utils/display/banner.js';
|
|
12
|
-
import { loadAgentContent, extractAgentInstructions } from '
|
|
12
|
+
import { loadAgentContent, extractAgentInstructions } from '../../utils/agent-enhancer.js';
|
|
13
13
|
import { CLIError } from '../../utils/error-handler.js';
|
|
14
14
|
import type { RunCommandOptions } from '../../types.js';
|
|
15
15
|
import type { FlowOptions } from './types.js';
|
|
@@ -299,7 +299,8 @@ export async function executeFlowV2(
|
|
|
299
299
|
console.log(chalk.dim(` Enabled rules: ${enabledRules.join(', ')}`));
|
|
300
300
|
console.log(chalk.dim(` Enabled output styles: ${enabledOutputStyles.join(', ')}\n`));
|
|
301
301
|
|
|
302
|
-
// Load agent content with
|
|
302
|
+
// Load agent content with enabled rules and output styles
|
|
303
|
+
// Rules are filtered: intersection of agent's frontmatter rules and globally enabled rules
|
|
303
304
|
const agentContent = await loadAgentContent(
|
|
304
305
|
agent,
|
|
305
306
|
options.agentFile,
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
* Centralized exports for all flow command components
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export { executeFlow } from './execute.js';
|
|
7
|
-
export { executeSetupPhase } from './setup.js';
|
|
8
|
-
export { executeTargetCommand, executeCommandOnly, executeFlowOnce } from './execute.js';
|
|
6
|
+
export { executeFlow } from './execute-v2.js';
|
|
9
7
|
export { resolvePrompt } from './prompt.js';
|
|
10
8
|
export { getExecutableTargets } from './targets.js';
|
|
11
|
-
export type { FlowOptions
|
|
9
|
+
export type { FlowOptions } from './types.js';
|
|
@@ -428,7 +428,8 @@ Please begin your response with a comprehensive summary of all the instructions
|
|
|
428
428
|
|
|
429
429
|
/**
|
|
430
430
|
* Setup agents for Claude Code
|
|
431
|
-
* Install agents to .claude/agents/ directory with rules
|
|
431
|
+
* Install agents to .claude/agents/ directory with rules appended
|
|
432
|
+
* Output styles are applied dynamically at runtime based on user settings
|
|
432
433
|
*/
|
|
433
434
|
async setupAgents(cwd: string, options: CommonOptions): Promise<SetupResult> {
|
|
434
435
|
const { enhanceAgentContent } = await import('../utils/agent-enhancer.js');
|
|
@@ -445,8 +446,8 @@ Please begin your response with a comprehensive summary of all the instructions
|
|
|
445
446
|
// Transform agent content (converts to Claude Code format, strips unsupported fields)
|
|
446
447
|
const transformed = await this.transformAgentContent(content, undefined, sourcePath);
|
|
447
448
|
|
|
448
|
-
// Enhance with rules
|
|
449
|
-
const enhanced = await enhanceAgentContent(transformed, rules);
|
|
449
|
+
// Enhance with rules only (output styles are applied dynamically at runtime)
|
|
450
|
+
const enhanced = await enhanceAgentContent(transformed, rules, []);
|
|
450
451
|
|
|
451
452
|
return enhanced;
|
|
452
453
|
},
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
|
|
13
13
|
import fs from 'node:fs/promises';
|
|
14
14
|
import path from 'node:path';
|
|
15
|
-
import { getOutputStylesDir, getRulesDir } from './config/paths.js';
|
|
15
|
+
import { getOutputStylesDir, getRulesDir, getAgentsDir } from './config/paths.js';
|
|
16
16
|
import { yamlUtils } from './config/target-utils.js';
|
|
17
|
+
import { CLIError } from './error-handler.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Load and combine rules and output styles
|
|
@@ -130,3 +131,110 @@ export async function enhanceAgentContent(
|
|
|
130
131
|
|
|
131
132
|
return `${agentContent}\n\n---\n\n# Rules and Output Styles\n\n${rulesAndStyles}`;
|
|
132
133
|
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Filter rules: intersection of agent's required rules and globally enabled rules
|
|
137
|
+
* @param agentRules - Rules defined in agent frontmatter
|
|
138
|
+
* @param enabledRules - Globally enabled rules from user settings
|
|
139
|
+
* @returns Intersection of both arrays (rules that are both required by agent AND enabled globally)
|
|
140
|
+
*/
|
|
141
|
+
function filterRules(agentRules?: string[], enabledRules?: string[]): string[] | undefined {
|
|
142
|
+
// If agent doesn't define rules, return undefined (will use default)
|
|
143
|
+
if (!agentRules || agentRules.length === 0) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// If no global filter, use all agent rules
|
|
148
|
+
if (!enabledRules || enabledRules.length === 0) {
|
|
149
|
+
return agentRules;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Return intersection: rules that are both in agent's list AND globally enabled
|
|
153
|
+
const filtered = agentRules.filter(rule => enabledRules.includes(rule));
|
|
154
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Load agent content from various locations
|
|
159
|
+
* @param agentName - Name of the agent (without .md extension)
|
|
160
|
+
* @param agentFilePath - Optional specific file path to load from
|
|
161
|
+
* @param enabledRules - Globally enabled rules (filtered with agent's frontmatter rules)
|
|
162
|
+
* @param enabledOutputStyles - Optional array of enabled output style names
|
|
163
|
+
*/
|
|
164
|
+
export async function loadAgentContent(
|
|
165
|
+
agentName: string,
|
|
166
|
+
agentFilePath?: string,
|
|
167
|
+
enabledRules?: string[],
|
|
168
|
+
enabledOutputStyles?: string[]
|
|
169
|
+
): Promise<string> {
|
|
170
|
+
try {
|
|
171
|
+
// If specific file path provided, load from there
|
|
172
|
+
if (agentFilePath) {
|
|
173
|
+
const content = await fs.readFile(path.resolve(agentFilePath), 'utf-8');
|
|
174
|
+
// Extract rules from agent frontmatter
|
|
175
|
+
const { metadata } = await yamlUtils.extractFrontMatter(content);
|
|
176
|
+
const agentRules = metadata.rules as string[] | undefined;
|
|
177
|
+
// Filter: intersection of agent's rules and globally enabled rules
|
|
178
|
+
const rulesToLoad = filterRules(agentRules, enabledRules);
|
|
179
|
+
// Enhance with filtered rules and enabled output styles
|
|
180
|
+
return await enhanceAgentContent(content, rulesToLoad, enabledOutputStyles);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// First try to load from .claude/agents/ directory (processed agents with rules already included)
|
|
184
|
+
const claudeAgentPath = path.join(process.cwd(), '.claude', 'agents', `${agentName}.md`);
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const content = await fs.readFile(claudeAgentPath, 'utf-8');
|
|
188
|
+
// Enhance with enabled output styles (rules are already included in the file)
|
|
189
|
+
return await enhanceAgentContent(content, [], enabledOutputStyles);
|
|
190
|
+
} catch (_error) {
|
|
191
|
+
// Try to load from local agents/ directory (user-defined agents)
|
|
192
|
+
const localAgentPath = path.join(process.cwd(), 'agents', `${agentName}.md`);
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const content = await fs.readFile(localAgentPath, 'utf-8');
|
|
196
|
+
// Extract rules from agent frontmatter
|
|
197
|
+
const { metadata } = await yamlUtils.extractFrontMatter(content);
|
|
198
|
+
const agentRules = metadata.rules as string[] | undefined;
|
|
199
|
+
// Filter: intersection of agent's rules and globally enabled rules
|
|
200
|
+
const rulesToLoad = filterRules(agentRules, enabledRules);
|
|
201
|
+
// Enhance with filtered rules and enabled output styles
|
|
202
|
+
return await enhanceAgentContent(content, rulesToLoad, enabledOutputStyles);
|
|
203
|
+
} catch (_error2) {
|
|
204
|
+
// Try to load from the package's agents directory
|
|
205
|
+
const packageAgentsDir = getAgentsDir();
|
|
206
|
+
const packageAgentPath = path.join(packageAgentsDir, `${agentName}.md`);
|
|
207
|
+
|
|
208
|
+
const content = await fs.readFile(packageAgentPath, 'utf-8');
|
|
209
|
+
// Extract rules from agent frontmatter
|
|
210
|
+
const { metadata } = await yamlUtils.extractFrontMatter(content);
|
|
211
|
+
const agentRules = metadata.rules as string[] | undefined;
|
|
212
|
+
// Filter: intersection of agent's rules and globally enabled rules
|
|
213
|
+
const rulesToLoad = filterRules(agentRules, enabledRules);
|
|
214
|
+
// Enhance with filtered rules and enabled output styles
|
|
215
|
+
return await enhanceAgentContent(content, rulesToLoad, enabledOutputStyles);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
} catch (_error) {
|
|
219
|
+
throw new CLIError(
|
|
220
|
+
`Agent '${agentName}' not found${agentFilePath ? ` at ${agentFilePath}` : ''}`,
|
|
221
|
+
'AGENT_NOT_FOUND'
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Extract agent instructions from agent content (strip YAML front matter)
|
|
228
|
+
*/
|
|
229
|
+
export function extractAgentInstructions(agentContent: string): string {
|
|
230
|
+
// Extract content after YAML front matter
|
|
231
|
+
const yamlFrontMatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/;
|
|
232
|
+
const match = agentContent.match(yamlFrontMatterRegex);
|
|
233
|
+
|
|
234
|
+
if (match) {
|
|
235
|
+
return agentContent.substring(match[0].length).trim();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// If no front matter, return the full content
|
|
239
|
+
return agentContent.trim();
|
|
240
|
+
}
|
|
@@ -1,453 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Execution Logic for Flow Command
|
|
3
|
-
* Command execution for single-run and loop modes
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import chalk from 'chalk';
|
|
7
|
-
import { targetManager } from '../../core/target-manager.js';
|
|
8
|
-
import { StateDetector } from '../../core/state-detector.js';
|
|
9
|
-
import { UpgradeManager } from '../../core/upgrade-manager.js';
|
|
10
|
-
import { projectSettings } from '../../utils/config/settings.js';
|
|
11
|
-
import { showWelcome } from '../../utils/display/banner.js';
|
|
12
|
-
import { showStatus } from '../../utils/display/status.js';
|
|
13
|
-
import { loadAgentContent, extractAgentInstructions } from '../run-command.js';
|
|
14
|
-
import { CLIError } from '../../utils/error-handler.js';
|
|
15
|
-
import type { RunCommandOptions } from '../../types.js';
|
|
16
|
-
import type { FlowOptions, SetupContext } from './types.js';
|
|
17
|
-
import { resolvePrompt } from './prompt.js';
|
|
18
|
-
import { executeSetupPhase } from './setup.js';
|
|
19
|
-
import { getExecutableTargets } from './targets.js';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Execute command using target's executeCommand method
|
|
23
|
-
*/
|
|
24
|
-
export async function executeTargetCommand(
|
|
25
|
-
targetId: string,
|
|
26
|
-
systemPrompt: string,
|
|
27
|
-
userPrompt: string,
|
|
28
|
-
options: RunCommandOptions
|
|
29
|
-
): Promise<void> {
|
|
30
|
-
const targetOption = targetManager.getTarget(targetId);
|
|
31
|
-
|
|
32
|
-
if (targetOption._tag === 'None') {
|
|
33
|
-
throw new CLIError(`Target not found: ${targetId}`, 'TARGET_NOT_FOUND');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const target = targetOption.value;
|
|
37
|
-
|
|
38
|
-
if (!target.isImplemented) {
|
|
39
|
-
throw new CLIError(
|
|
40
|
-
`Target '${targetId}' is not implemented. Supported targets: ${getExecutableTargets().join(', ')}`,
|
|
41
|
-
'TARGET_NOT_IMPLEMENTED'
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (!target.executeCommand) {
|
|
46
|
-
throw new CLIError(
|
|
47
|
-
`Target '${targetId}' does not support command execution. Supported targets: ${getExecutableTargets().join(', ')}`,
|
|
48
|
-
'EXECUTION_NOT_SUPPORTED'
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return target.executeCommand(systemPrompt, userPrompt, options);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Execute command only (for loop mode iterations)
|
|
57
|
-
* Uses pre-setup context to execute command without re-doing setup
|
|
58
|
-
*/
|
|
59
|
-
export async function executeCommandOnly(
|
|
60
|
-
context: SetupContext,
|
|
61
|
-
prompt: string | undefined,
|
|
62
|
-
options: FlowOptions
|
|
63
|
-
): Promise<void> {
|
|
64
|
-
const userPrompt = prompt?.trim() || '';
|
|
65
|
-
|
|
66
|
-
// Update continue flag in runOptions
|
|
67
|
-
const runOptions = {
|
|
68
|
-
...context.runOptions,
|
|
69
|
-
continue: options.continue,
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
await executeTargetCommand(context.resolvedTarget, context.systemPrompt!, userPrompt, runOptions);
|
|
74
|
-
} catch (error) {
|
|
75
|
-
console.error(chalk.red.bold('\n✗ Launch failed:'), error);
|
|
76
|
-
throw error;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Single flow execution (used by both normal and loop mode)
|
|
82
|
-
*/
|
|
83
|
-
export async function executeFlowOnce(prompt: string | undefined, options: FlowOptions): Promise<void> {
|
|
84
|
-
// Quick mode: enable useDefaults and skip prompts
|
|
85
|
-
if (options.quick) {
|
|
86
|
-
options.useDefaults = true;
|
|
87
|
-
console.log(chalk.cyan('⚡ Quick mode enabled - using saved defaults\n'));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Continue mode always requires print mode
|
|
91
|
-
if (options.continue && !options.print) {
|
|
92
|
-
options.print = true;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Import orchestrator functions
|
|
96
|
-
const {
|
|
97
|
-
checkUpgrades,
|
|
98
|
-
checkComponentIntegrity,
|
|
99
|
-
checkSyncStatus,
|
|
100
|
-
} = await import('../flow-orchestrator.js');
|
|
101
|
-
|
|
102
|
-
// Show welcome banner
|
|
103
|
-
showWelcome();
|
|
104
|
-
|
|
105
|
-
// Declare at function level to persist across steps
|
|
106
|
-
let selectedTarget: string | undefined;
|
|
107
|
-
let state = undefined;
|
|
108
|
-
|
|
109
|
-
// First: determine target (from options, saved settings, or init will prompt)
|
|
110
|
-
const initialTarget = options.target || (await projectSettings.getDefaultTarget());
|
|
111
|
-
|
|
112
|
-
// Only detect state if we have a target (can't check components without knowing target structure)
|
|
113
|
-
if (initialTarget && !options.sync) {
|
|
114
|
-
const detector = new StateDetector();
|
|
115
|
-
const upgradeManager = new UpgradeManager();
|
|
116
|
-
|
|
117
|
-
if (options.verbose) {
|
|
118
|
-
console.log(chalk.dim('🤔 Checking project status...\n'));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
state = await detector.detect();
|
|
122
|
-
|
|
123
|
-
if (options.verbose) {
|
|
124
|
-
await showStatus(state);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Step 1: Check for upgrades (non-intrusive notification)
|
|
128
|
-
if (!options.quick) {
|
|
129
|
-
await checkUpgrades(state, options);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Step 2.5: Check component integrity (only if we have valid state)
|
|
133
|
-
await checkComponentIntegrity(state, options);
|
|
134
|
-
|
|
135
|
-
// Step 2.6: Check sync status (new templates available)
|
|
136
|
-
await checkSyncStatus(state, options);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Step 3: Initialize (only if actually needed)
|
|
140
|
-
const shouldInitialize =
|
|
141
|
-
!state?.initialized ||
|
|
142
|
-
options.sync ||
|
|
143
|
-
options.repair ||
|
|
144
|
-
options.initOnly;
|
|
145
|
-
|
|
146
|
-
if (shouldInitialize) {
|
|
147
|
-
console.log(chalk.cyan.bold('━━━ 🚀 Initializing Project\n'));
|
|
148
|
-
selectedTarget = await initializeProject(options, state);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Step 4: Launch target (if not init-only)
|
|
152
|
-
if (!options.initOnly) {
|
|
153
|
-
await launchTarget(prompt, options, state, selectedTarget);
|
|
154
|
-
} else {
|
|
155
|
-
console.log(chalk.dim('✓ Init-only mode, skipping execution\n'));
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Initialize project with components
|
|
161
|
-
*/
|
|
162
|
-
async function initializeProject(options: FlowOptions, state: any): Promise<string | undefined> {
|
|
163
|
-
const {
|
|
164
|
-
selectAndValidateTarget,
|
|
165
|
-
previewDryRun,
|
|
166
|
-
installComponents,
|
|
167
|
-
} = await import('../init-core.js');
|
|
168
|
-
|
|
169
|
-
let selectedTarget: string | undefined;
|
|
170
|
-
|
|
171
|
-
try {
|
|
172
|
-
// In repair mode, use existing target from state
|
|
173
|
-
const targetForInit = options.repair && state?.target
|
|
174
|
-
? state.target
|
|
175
|
-
: options.target;
|
|
176
|
-
|
|
177
|
-
// Prepare init options
|
|
178
|
-
const initOptions = {
|
|
179
|
-
target: targetForInit,
|
|
180
|
-
verbose: options.verbose,
|
|
181
|
-
dryRun: options.dryRun,
|
|
182
|
-
clear: options.sync || false,
|
|
183
|
-
mcp: options.mcp !== false,
|
|
184
|
-
agents: options.agents !== false,
|
|
185
|
-
rules: options.rules !== false,
|
|
186
|
-
outputStyles: options.outputStyles !== false,
|
|
187
|
-
slashCommands: options.slashCommands !== false,
|
|
188
|
-
hooks: options.hooks !== false,
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
// Handle sync mode - delete template files first
|
|
192
|
-
if (options.sync && !options.dryRun) {
|
|
193
|
-
selectedTarget = await handleSyncMode(initOptions);
|
|
194
|
-
} else {
|
|
195
|
-
// Select and validate target
|
|
196
|
-
const targetId = await selectAndValidateTarget(initOptions);
|
|
197
|
-
selectedTarget = targetId;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Dry run preview
|
|
201
|
-
if (options.dryRun) {
|
|
202
|
-
if (!selectedTarget) {
|
|
203
|
-
const targetId = await selectAndValidateTarget(initOptions);
|
|
204
|
-
selectedTarget = targetId;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
await previewDryRun(selectedTarget, initOptions);
|
|
208
|
-
console.log(chalk.dim('✓ Initialization dry run complete\n'));
|
|
209
|
-
} else {
|
|
210
|
-
// Actually install components
|
|
211
|
-
if (!selectedTarget) {
|
|
212
|
-
const targetId = await selectAndValidateTarget(initOptions);
|
|
213
|
-
selectedTarget = targetId;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
await installComponents(selectedTarget, initOptions);
|
|
217
|
-
console.log(chalk.green.bold('✓ Initialization complete\n'));
|
|
218
|
-
}
|
|
219
|
-
} catch (error) {
|
|
220
|
-
console.error(chalk.red.bold('✗ Initialization failed:'), error);
|
|
221
|
-
process.exit(1);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return selectedTarget;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Handle sync mode
|
|
229
|
-
*/
|
|
230
|
-
async function handleSyncMode(initOptions: any): Promise<string> {
|
|
231
|
-
const { buildSyncManifest, showSyncPreview, selectUnknownFilesToRemove, showFinalSummary, confirmSync, executeSyncDelete, removeMCPServers, removeHooks } = await import('../../utils/files/sync-utils.js');
|
|
232
|
-
const { selectAndValidateTarget } = await import('../init-core.js');
|
|
233
|
-
|
|
234
|
-
const targetId = await selectAndValidateTarget(initOptions);
|
|
235
|
-
|
|
236
|
-
const targetOption = targetManager.getTarget(targetId);
|
|
237
|
-
if (targetOption._tag === 'None') {
|
|
238
|
-
throw new Error(`Target not found: ${targetId}`);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const target = targetOption.value;
|
|
242
|
-
const manifest = await buildSyncManifest(process.cwd(), target);
|
|
243
|
-
|
|
244
|
-
console.log(chalk.cyan.bold('━━━ 🔄 Synchronizing Files\n'));
|
|
245
|
-
showSyncPreview(manifest, process.cwd(), target);
|
|
246
|
-
|
|
247
|
-
const selectedUnknowns = await selectUnknownFilesToRemove(manifest);
|
|
248
|
-
showFinalSummary(manifest, selectedUnknowns);
|
|
249
|
-
|
|
250
|
-
const confirmed = await confirmSync();
|
|
251
|
-
if (!confirmed) {
|
|
252
|
-
console.log(chalk.yellow('\n✗ Sync cancelled\n'));
|
|
253
|
-
process.exit(0);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const { templates, unknowns } = await executeSyncDelete(manifest, selectedUnknowns);
|
|
257
|
-
|
|
258
|
-
let mcpRemoved = 0;
|
|
259
|
-
if (selectedUnknowns.mcpServers.length > 0) {
|
|
260
|
-
mcpRemoved = await removeMCPServers(process.cwd(), selectedUnknowns.mcpServers);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
let hooksRemoved = 0;
|
|
264
|
-
if (selectedUnknowns.hooks.length > 0) {
|
|
265
|
-
hooksRemoved = await removeHooks(process.cwd(), selectedUnknowns.hooks);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
console.log(chalk.green(`\n✓ Synced ${templates} templates`));
|
|
269
|
-
const totalRemoved = unknowns + mcpRemoved + hooksRemoved;
|
|
270
|
-
if (totalRemoved > 0) {
|
|
271
|
-
console.log(chalk.green(`✓ Removed ${totalRemoved} items`));
|
|
272
|
-
}
|
|
273
|
-
const totalSelected = selectedUnknowns.files.length + selectedUnknowns.mcpServers.length + selectedUnknowns.hooks.length;
|
|
274
|
-
const preserved = manifest.agents.unknown.length + manifest.slashCommands.unknown.length + manifest.rules.unknown.length + manifest.mcpServers.notInRegistry.length + manifest.hooks.orphaned.length - totalSelected;
|
|
275
|
-
if (preserved > 0) {
|
|
276
|
-
console.log(chalk.green(`✓ Preserved ${preserved} custom items`));
|
|
277
|
-
}
|
|
278
|
-
console.log('');
|
|
279
|
-
|
|
280
|
-
return targetId;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Launch target with agent
|
|
285
|
-
*/
|
|
286
|
-
async function launchTarget(
|
|
287
|
-
prompt: string | undefined,
|
|
288
|
-
options: FlowOptions,
|
|
289
|
-
state: any,
|
|
290
|
-
selectedTarget: string | undefined
|
|
291
|
-
): Promise<void> {
|
|
292
|
-
// Resolve target - use the target we just selected
|
|
293
|
-
let targetForResolution = options.target || state?.target || selectedTarget;
|
|
294
|
-
|
|
295
|
-
// If we just selected a target during init, use that
|
|
296
|
-
if (selectedTarget) {
|
|
297
|
-
targetForResolution = selectedTarget;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
if (!targetForResolution) {
|
|
301
|
-
console.error(chalk.red.bold('✗ No target selected. Use --target or run init first.'));
|
|
302
|
-
process.exit(1);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const resolvedTarget = await targetManager.resolveTarget({
|
|
306
|
-
target: targetForResolution,
|
|
307
|
-
allowSelection: false,
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
console.log(chalk.cyan.bold(`━━━ 🎯 Launching ${resolvedTarget}\n`));
|
|
311
|
-
|
|
312
|
-
// Check if target supports command execution
|
|
313
|
-
const { getTargetsWithCommandSupport } = await import('../../config/targets.js');
|
|
314
|
-
const supportedTargets = getTargetsWithCommandSupport().map(t => t.id);
|
|
315
|
-
|
|
316
|
-
if (!supportedTargets.includes(resolvedTarget)) {
|
|
317
|
-
console.log(chalk.red.bold('✗ Unsupported target platform\n'));
|
|
318
|
-
console.log(chalk.yellow(`Target '${resolvedTarget}' does not support agent execution.`));
|
|
319
|
-
console.log(chalk.cyan(`Supported platforms: ${supportedTargets.join(', ')}\n`));
|
|
320
|
-
console.log(chalk.dim('Tip: Use --target claude-code to specify Claude Code platform'));
|
|
321
|
-
console.log(chalk.dim('Example: bun dev:flow --target claude-code\n'));
|
|
322
|
-
process.exit(1);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// Claude Code handling - needs provider/agent setup
|
|
326
|
-
if (resolvedTarget === 'claude-code') {
|
|
327
|
-
await setupClaudeCode(options);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
const agent = options.agent || 'coder';
|
|
331
|
-
const verbose = options.verbose || false;
|
|
332
|
-
|
|
333
|
-
if (verbose || options.runOnly || !options.quick) {
|
|
334
|
-
console.log(` 🤖 Agent: ${chalk.cyan(agent)}`);
|
|
335
|
-
console.log(` 🎯 Target: ${chalk.cyan(resolvedTarget)}`);
|
|
336
|
-
if (prompt) {
|
|
337
|
-
console.log(` 💬 Prompt: ${chalk.dim(prompt)}\n`);
|
|
338
|
-
} else {
|
|
339
|
-
console.log(` 💬 Mode: ${chalk.dim('Interactive')}\n`);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Load agent and prepare prompts
|
|
344
|
-
const agentContent = await loadAgentContent(agent, options.agentFile);
|
|
345
|
-
const agentInstructions = extractAgentInstructions(agentContent);
|
|
346
|
-
const systemPrompt = `AGENT INSTRUCTIONS:\n${agentInstructions}`;
|
|
347
|
-
|
|
348
|
-
const userPrompt = prompt?.trim() || '';
|
|
349
|
-
|
|
350
|
-
// Run options
|
|
351
|
-
const runOptions: RunCommandOptions = {
|
|
352
|
-
target: resolvedTarget,
|
|
353
|
-
verbose,
|
|
354
|
-
dryRun: options.dryRun,
|
|
355
|
-
agent,
|
|
356
|
-
agentFile: options.agentFile,
|
|
357
|
-
prompt,
|
|
358
|
-
print: options.print,
|
|
359
|
-
continue: options.continue,
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
try {
|
|
363
|
-
await executeTargetCommand(resolvedTarget, systemPrompt, userPrompt, runOptions);
|
|
364
|
-
} catch (error) {
|
|
365
|
-
console.error(chalk.red.bold('\n✗ Launch failed:'), error);
|
|
366
|
-
process.exit(1);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
if (!options.dryRun) {
|
|
370
|
-
console.log(chalk.dim('━━━\n'));
|
|
371
|
-
console.log(chalk.green('✓ Session complete\n'));
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Setup Claude Code provider and agent
|
|
377
|
-
*/
|
|
378
|
-
async function setupClaudeCode(options: FlowOptions): Promise<void> {
|
|
379
|
-
const { SmartConfigService } = await import('../../services/smart-config-service.js');
|
|
380
|
-
const { ConfigService } = await import('../../services/config-service.js');
|
|
381
|
-
|
|
382
|
-
if (!(await ConfigService.hasInitialSetup())) {
|
|
383
|
-
console.log(chalk.cyan('🔑 First-time setup for Claude Code\n'));
|
|
384
|
-
await SmartConfigService.initialSetup();
|
|
385
|
-
console.log(chalk.green('✓ Setup complete!\n'));
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
const runtimeChoices = await SmartConfigService.selectRuntimeChoices({
|
|
389
|
-
selectProvider: options.selectProvider,
|
|
390
|
-
selectAgent: options.selectAgent,
|
|
391
|
-
useDefaults: options.useDefaults,
|
|
392
|
-
provider: options.provider,
|
|
393
|
-
agent: options.agent,
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
await SmartConfigService.setupEnvironment(runtimeChoices.provider!);
|
|
397
|
-
options.agent = runtimeChoices.agent;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* Main flow execution logic - simplified with orchestrator
|
|
402
|
-
*/
|
|
403
|
-
export async function executeFlow(prompt: string | undefined, options: FlowOptions): Promise<void> {
|
|
404
|
-
// Resolve prompt (handle file input)
|
|
405
|
-
const resolvedPrompt = await resolvePrompt(prompt);
|
|
406
|
-
|
|
407
|
-
// Loop mode: Setup once, then loop only execution
|
|
408
|
-
if (options.loop !== undefined) {
|
|
409
|
-
const { LoopController } = await import('../../core/loop-controller.js');
|
|
410
|
-
const controller = new LoopController();
|
|
411
|
-
|
|
412
|
-
// Default to 0s (no cooldown) if just --loop with no value
|
|
413
|
-
const interval = typeof options.loop === 'number' ? options.loop : 0;
|
|
414
|
-
|
|
415
|
-
// Auto-enable headless mode for loop
|
|
416
|
-
options.print = true;
|
|
417
|
-
|
|
418
|
-
// ONE-TIME SETUP: Do all initialization once before loop starts
|
|
419
|
-
const setupContext = await executeSetupPhase(resolvedPrompt, options);
|
|
420
|
-
|
|
421
|
-
// Save original continue flag
|
|
422
|
-
const originalContinue = options.continue || false;
|
|
423
|
-
|
|
424
|
-
// LOOP: Only execute the command repeatedly
|
|
425
|
-
await controller.run(
|
|
426
|
-
async () => {
|
|
427
|
-
const isFirstIteration = controller['state'].iteration === 1;
|
|
428
|
-
|
|
429
|
-
// Continue logic:
|
|
430
|
-
// - If user specified --continue, always use it (all iterations)
|
|
431
|
-
// - If user didn't specify, only use from 2nd iteration onwards
|
|
432
|
-
options.continue = originalContinue || !isFirstIteration;
|
|
433
|
-
|
|
434
|
-
try {
|
|
435
|
-
await executeCommandOnly(setupContext, resolvedPrompt, options);
|
|
436
|
-
return { exitCode: 0 };
|
|
437
|
-
} catch (error) {
|
|
438
|
-
return { exitCode: 1, error: error as Error };
|
|
439
|
-
}
|
|
440
|
-
},
|
|
441
|
-
{
|
|
442
|
-
enabled: true,
|
|
443
|
-
interval,
|
|
444
|
-
maxRuns: options.maxRuns,
|
|
445
|
-
}
|
|
446
|
-
);
|
|
447
|
-
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// Normal execution (non-loop)
|
|
452
|
-
await executeFlowOnce(resolvedPrompt, options);
|
|
453
|
-
}
|
|
@@ -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,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
|
-
*/
|