@sylphx/flow 1.0.1 â 1.0.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 +12 -0
- package/package.json +10 -9
- package/src/commands/codebase-command.ts +168 -0
- package/src/commands/flow-command.ts +1137 -0
- package/src/commands/flow-orchestrator.ts +296 -0
- package/src/commands/hook-command.ts +444 -0
- package/src/commands/init-command.ts +92 -0
- package/src/commands/init-core.ts +322 -0
- package/src/commands/knowledge-command.ts +161 -0
- package/src/commands/run-command.ts +120 -0
- package/src/components/benchmark-monitor.tsx +331 -0
- package/src/components/reindex-progress.tsx +261 -0
- package/src/composables/functional/index.ts +14 -0
- package/src/composables/functional/useEnvironment.ts +171 -0
- package/src/composables/functional/useFileSystem.ts +139 -0
- package/src/composables/index.ts +5 -0
- package/src/composables/useEnv.ts +13 -0
- package/src/composables/useRuntimeConfig.ts +27 -0
- package/src/composables/useTargetConfig.ts +45 -0
- package/src/config/ai-config.ts +376 -0
- package/src/config/constants.ts +35 -0
- package/src/config/index.ts +27 -0
- package/src/config/rules.ts +43 -0
- package/src/config/servers.ts +371 -0
- package/src/config/targets.ts +126 -0
- package/src/core/agent-loader.ts +141 -0
- package/src/core/agent-manager.ts +174 -0
- package/src/core/ai-sdk.ts +603 -0
- package/src/core/app-factory.ts +381 -0
- package/src/core/builtin-agents.ts +9 -0
- package/src/core/command-system.ts +550 -0
- package/src/core/config-system.ts +550 -0
- package/src/core/connection-pool.ts +390 -0
- package/src/core/di-container.ts +155 -0
- package/src/core/error-handling.ts +519 -0
- package/src/core/formatting/bytes.test.ts +115 -0
- package/src/core/formatting/bytes.ts +64 -0
- package/src/core/functional/async.ts +313 -0
- package/src/core/functional/either.ts +109 -0
- package/src/core/functional/error-handler.ts +135 -0
- package/src/core/functional/error-types.ts +311 -0
- package/src/core/functional/index.ts +19 -0
- package/src/core/functional/option.ts +142 -0
- package/src/core/functional/pipe.ts +189 -0
- package/src/core/functional/result.ts +204 -0
- package/src/core/functional/validation.ts +138 -0
- package/src/core/headless-display.ts +96 -0
- package/src/core/index.ts +6 -0
- package/src/core/installers/file-installer.ts +303 -0
- package/src/core/installers/mcp-installer.ts +213 -0
- package/src/core/interfaces/index.ts +22 -0
- package/src/core/interfaces/repository.interface.ts +91 -0
- package/src/core/interfaces/service.interface.ts +133 -0
- package/src/core/interfaces.ts +129 -0
- package/src/core/loop-controller.ts +200 -0
- package/src/core/result.ts +351 -0
- package/src/core/rule-loader.ts +147 -0
- package/src/core/rule-manager.ts +240 -0
- package/src/core/service-config.ts +252 -0
- package/src/core/session-service.ts +121 -0
- package/src/core/state-detector.ts +389 -0
- package/src/core/storage-factory.ts +115 -0
- package/src/core/stream-handler.ts +288 -0
- package/src/core/target-manager.ts +161 -0
- package/src/core/type-utils.ts +427 -0
- package/src/core/unified-storage.ts +456 -0
- package/src/core/upgrade-manager.ts +300 -0
- package/src/core/validation/limit.test.ts +155 -0
- package/src/core/validation/limit.ts +46 -0
- package/src/core/validation/query.test.ts +44 -0
- package/src/core/validation/query.ts +20 -0
- package/src/db/auto-migrate.ts +322 -0
- package/src/db/base-database-client.ts +144 -0
- package/src/db/cache-db.ts +218 -0
- package/src/db/cache-schema.ts +75 -0
- package/src/db/database.ts +70 -0
- package/src/db/index.ts +252 -0
- package/src/db/memory-db.ts +153 -0
- package/src/db/memory-schema.ts +29 -0
- package/src/db/schema.ts +289 -0
- package/src/db/session-repository.ts +733 -0
- package/src/domains/codebase/index.ts +5 -0
- package/src/domains/codebase/tools.ts +139 -0
- package/src/domains/index.ts +8 -0
- package/src/domains/knowledge/index.ts +10 -0
- package/src/domains/knowledge/resources.ts +537 -0
- package/src/domains/knowledge/tools.ts +174 -0
- package/src/domains/utilities/index.ts +6 -0
- package/src/domains/utilities/time/index.ts +5 -0
- package/src/domains/utilities/time/tools.ts +291 -0
- package/src/index.ts +211 -0
- package/src/services/agent-service.ts +273 -0
- package/src/services/claude-config-service.ts +252 -0
- package/src/services/config-service.ts +258 -0
- package/src/services/evaluation-service.ts +271 -0
- package/src/services/functional/evaluation-logic.ts +296 -0
- package/src/services/functional/file-processor.ts +273 -0
- package/src/services/functional/index.ts +12 -0
- package/src/services/index.ts +13 -0
- package/src/services/mcp-service.ts +432 -0
- package/src/services/memory.service.ts +476 -0
- package/src/services/search/base-indexer.ts +156 -0
- package/src/services/search/codebase-indexer-types.ts +38 -0
- package/src/services/search/codebase-indexer.ts +647 -0
- package/src/services/search/embeddings-provider.ts +455 -0
- package/src/services/search/embeddings.ts +316 -0
- package/src/services/search/functional-indexer.ts +323 -0
- package/src/services/search/index.ts +27 -0
- package/src/services/search/indexer.ts +380 -0
- package/src/services/search/knowledge-indexer.ts +422 -0
- package/src/services/search/semantic-search.ts +244 -0
- package/src/services/search/tfidf.ts +559 -0
- package/src/services/search/unified-search-service.ts +888 -0
- package/src/services/smart-config-service.ts +385 -0
- package/src/services/storage/cache-storage.ts +487 -0
- package/src/services/storage/drizzle-storage.ts +581 -0
- package/src/services/storage/index.ts +15 -0
- package/src/services/storage/lancedb-vector-storage.ts +494 -0
- package/src/services/storage/memory-storage.ts +268 -0
- package/src/services/storage/separated-storage.ts +467 -0
- package/src/services/storage/vector-storage.ts +13 -0
- package/src/shared/agents/index.ts +63 -0
- package/src/shared/files/index.ts +99 -0
- package/src/shared/index.ts +32 -0
- package/src/shared/logging/index.ts +24 -0
- package/src/shared/processing/index.ts +153 -0
- package/src/shared/types/index.ts +25 -0
- package/src/targets/claude-code.ts +574 -0
- package/src/targets/functional/claude-code-logic.ts +185 -0
- package/src/targets/functional/index.ts +6 -0
- package/src/targets/opencode.ts +529 -0
- package/src/types/agent.types.ts +32 -0
- package/src/types/api/batch.ts +108 -0
- package/src/types/api/errors.ts +118 -0
- package/src/types/api/index.ts +55 -0
- package/src/types/api/requests.ts +76 -0
- package/src/types/api/responses.ts +180 -0
- package/src/types/api/websockets.ts +85 -0
- package/src/types/api.types.ts +9 -0
- package/src/types/benchmark.ts +49 -0
- package/src/types/cli.types.ts +87 -0
- package/src/types/common.types.ts +35 -0
- package/src/types/database.types.ts +510 -0
- package/src/types/mcp-config.types.ts +448 -0
- package/src/types/mcp.types.ts +69 -0
- package/src/types/memory-types.ts +63 -0
- package/src/types/provider.types.ts +28 -0
- package/src/types/rule.types.ts +24 -0
- package/src/types/session.types.ts +214 -0
- package/src/types/target-config.types.ts +295 -0
- package/src/types/target.types.ts +140 -0
- package/src/types/todo.types.ts +25 -0
- package/src/types.ts +40 -0
- package/src/utils/advanced-tokenizer.ts +191 -0
- package/src/utils/agent-enhancer.ts +114 -0
- package/src/utils/ai-model-fetcher.ts +19 -0
- package/src/utils/async-file-operations.ts +516 -0
- package/src/utils/audio-player.ts +345 -0
- package/src/utils/cli-output.ts +266 -0
- package/src/utils/codebase-helpers.ts +211 -0
- package/src/utils/console-ui.ts +79 -0
- package/src/utils/database-errors.ts +140 -0
- package/src/utils/debug-logger.ts +49 -0
- package/src/utils/error-handler.ts +53 -0
- package/src/utils/file-operations.ts +310 -0
- package/src/utils/file-scanner.ts +259 -0
- package/src/utils/functional/array.ts +355 -0
- package/src/utils/functional/index.ts +15 -0
- package/src/utils/functional/object.ts +279 -0
- package/src/utils/functional/string.ts +281 -0
- package/src/utils/functional.ts +543 -0
- package/src/utils/help.ts +20 -0
- package/src/utils/immutable-cache.ts +106 -0
- package/src/utils/index.ts +78 -0
- package/src/utils/jsonc.ts +158 -0
- package/src/utils/logger.ts +396 -0
- package/src/utils/mcp-config.ts +249 -0
- package/src/utils/memory-tui.ts +414 -0
- package/src/utils/models-dev.ts +91 -0
- package/src/utils/notifications.ts +169 -0
- package/src/utils/object-utils.ts +51 -0
- package/src/utils/parallel-operations.ts +487 -0
- package/src/utils/paths.ts +143 -0
- package/src/utils/process-manager.ts +155 -0
- package/src/utils/prompts.ts +120 -0
- package/src/utils/search-tool-builder.ts +214 -0
- package/src/utils/secret-utils.ts +179 -0
- package/src/utils/security.ts +537 -0
- package/src/utils/session-manager.ts +168 -0
- package/src/utils/session-title.ts +87 -0
- package/src/utils/settings.ts +182 -0
- package/src/utils/simplified-errors.ts +410 -0
- package/src/utils/sync-utils.ts +159 -0
- package/src/utils/target-config.ts +570 -0
- package/src/utils/target-utils.ts +394 -0
- package/src/utils/template-engine.ts +94 -0
- package/src/utils/test-audio.ts +71 -0
- package/src/utils/todo-context.ts +46 -0
- package/src/utils/token-counter.ts +288 -0
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -59554
- package/dist/lancedb.linux-x64-gnu-b7f0jgsz.node +0 -0
- package/dist/lancedb.linux-x64-musl-tgcv22rx.node +0 -0
- package/dist/shared/chunk-25dwp0dp.js +0 -89
- package/dist/shared/chunk-3pjb6063.js +0 -208
- package/dist/shared/chunk-4d6ydpw7.js +0 -2854
- package/dist/shared/chunk-4wjcadjk.js +0 -225
- package/dist/shared/chunk-5j4w74t6.js +0 -30
- package/dist/shared/chunk-5j8m3dh3.js +0 -58
- package/dist/shared/chunk-5thh3qem.js +0 -91
- package/dist/shared/chunk-6g9xy73m.js +0 -252
- package/dist/shared/chunk-7eq34c42.js +0 -23
- package/dist/shared/chunk-c2gwgx3r.js +0 -115
- package/dist/shared/chunk-cjd3mk4c.js +0 -1320
- package/dist/shared/chunk-g5cv6703.js +0 -368
- package/dist/shared/chunk-hpkhykhq.js +0 -574
- package/dist/shared/chunk-m2322pdk.js +0 -122
- package/dist/shared/chunk-nd5fdvaq.js +0 -26
- package/dist/shared/chunk-pgd3m6zf.js +0 -108
- package/dist/shared/chunk-qk8n91hw.js +0 -494
- package/dist/shared/chunk-rkkn8szp.js +0 -16855
- package/dist/shared/chunk-t16rfxh0.js +0 -61
- package/dist/shared/chunk-t4fbfa5v.js +0 -19
- package/dist/shared/chunk-t77h86w6.js +0 -276
- package/dist/shared/chunk-v0ez4aef.js +0 -71
- package/dist/shared/chunk-v29j2r3s.js +0 -32051
- package/dist/shared/chunk-vfbc6ew5.js +0 -765
- package/dist/shared/chunk-vmeqwm1c.js +0 -204
- package/dist/shared/chunk-x66eh37x.js +0 -137
|
@@ -0,0 +1,1137 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import boxen from 'boxen';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import fs from 'node:fs/promises';
|
|
7
|
+
import { targetManager } from '../core/target-manager.js';
|
|
8
|
+
import { CLIError } from '../utils/error-handler.js';
|
|
9
|
+
import type { RunCommandOptions } from '../types.js';
|
|
10
|
+
import { StateDetector, type ProjectState } from '../core/state-detector.js';
|
|
11
|
+
import { UpgradeManager } from '../core/upgrade-manager.js';
|
|
12
|
+
import { loadAgentContent, extractAgentInstructions } from './run-command.js';
|
|
13
|
+
import { ClaudeConfigService } from '../services/claude-config-service.js';
|
|
14
|
+
import { ConfigService } from '../services/config-service.js';
|
|
15
|
+
import { projectSettings } from '../utils/settings.js';
|
|
16
|
+
|
|
17
|
+
export interface FlowOptions {
|
|
18
|
+
target?: string;
|
|
19
|
+
verbose?: boolean;
|
|
20
|
+
dryRun?: boolean;
|
|
21
|
+
sync?: boolean; // Sync mode - delete and re-install template files
|
|
22
|
+
initOnly?: boolean;
|
|
23
|
+
runOnly?: boolean;
|
|
24
|
+
repair?: boolean; // Repair mode - install missing components
|
|
25
|
+
upgrade?: boolean;
|
|
26
|
+
upgradeTarget?: boolean;
|
|
27
|
+
mcp?: boolean;
|
|
28
|
+
agents?: boolean;
|
|
29
|
+
rules?: boolean;
|
|
30
|
+
outputStyles?: boolean;
|
|
31
|
+
slashCommands?: boolean;
|
|
32
|
+
hooks?: boolean;
|
|
33
|
+
agent?: string;
|
|
34
|
+
agentFile?: string;
|
|
35
|
+
|
|
36
|
+
// Smart configuration options
|
|
37
|
+
selectProvider?: boolean;
|
|
38
|
+
selectAgent?: boolean;
|
|
39
|
+
useDefaults?: boolean;
|
|
40
|
+
provider?: string;
|
|
41
|
+
quick?: boolean;
|
|
42
|
+
|
|
43
|
+
// Execution modes
|
|
44
|
+
print?: boolean; // Headless print mode
|
|
45
|
+
continue?: boolean; // Continue previous conversation
|
|
46
|
+
|
|
47
|
+
// Loop mode (continuous execution)
|
|
48
|
+
loop?: number; // Loop every N seconds (--loop 60)
|
|
49
|
+
maxRuns?: number; // Optional max iterations (default: infinite)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Display welcome banner
|
|
54
|
+
*/
|
|
55
|
+
function showWelcome(): void {
|
|
56
|
+
console.log(
|
|
57
|
+
boxen(
|
|
58
|
+
`${chalk.cyan.bold('Sylphx Flow')} ${chalk.dim('- AI-Powered Development Framework')}\n` +
|
|
59
|
+
`${chalk.dim('Auto-initialization ⢠Smart upgrades ⢠One-click launch')}`,
|
|
60
|
+
{
|
|
61
|
+
padding: 1,
|
|
62
|
+
margin: { bottom: 1 },
|
|
63
|
+
borderStyle: 'round',
|
|
64
|
+
borderColor: 'cyan',
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Compare versions to check if one is outdated
|
|
72
|
+
*/
|
|
73
|
+
function isVersionOutdated(current: string, latest: string): boolean {
|
|
74
|
+
try {
|
|
75
|
+
return compareVersions(current, latest) < 0;
|
|
76
|
+
} catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Compare two version strings
|
|
83
|
+
*/
|
|
84
|
+
function compareVersions(v1: string, v2: string): number {
|
|
85
|
+
const parts1 = v1.split('.').map(Number);
|
|
86
|
+
const parts2 = v2.split('.').map(Number);
|
|
87
|
+
|
|
88
|
+
for (let i = 0; i < Math.min(parts1.length, parts2.length); i++) {
|
|
89
|
+
if (parts1[i] !== parts2[i]) {
|
|
90
|
+
return parts1[i] - parts2[i];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return parts1.length - parts2.length;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function showStatus(state: ProjectState): Promise<void> {
|
|
98
|
+
console.log(chalk.cyan.bold('ð Project Status\n'));
|
|
99
|
+
|
|
100
|
+
if (!state.initialized) {
|
|
101
|
+
console.log(' ' + chalk.yellow('â Not initialized'));
|
|
102
|
+
} else {
|
|
103
|
+
console.log(` ${chalk.green('â')} Initialized (Flow v${state.version || 'unknown'})`);
|
|
104
|
+
|
|
105
|
+
if (state.target) {
|
|
106
|
+
const versionStr = state.targetVersion ? ` (v${state.targetVersion})` : '';
|
|
107
|
+
console.log(` ${chalk.green('â')} Target platform: ${state.target}${versionStr}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Component status
|
|
111
|
+
const components = state.components;
|
|
112
|
+
console.log(`\n ${chalk.cyan('Components:')}`);
|
|
113
|
+
console.log(` Agents: ${components.agents.installed ? chalk.green(`â ${components.agents.count}`) : chalk.red('â')}`);
|
|
114
|
+
console.log(` Rules: ${components.rules.installed ? chalk.green(`â ${components.rules.count}`) : chalk.red('â')}`);
|
|
115
|
+
console.log(` Hooks: ${components.hooks.installed ? chalk.green('â') : chalk.red('â')}`);
|
|
116
|
+
console.log(` MCP: ${components.mcp.installed ? chalk.green(`â ${components.mcp.serverCount} servers`) : chalk.red('â')}`);
|
|
117
|
+
console.log(` Output styles: ${components.outputStyles.installed ? chalk.green('â') : chalk.red('â')}`);
|
|
118
|
+
console.log(` Slash commands: ${components.slashCommands.installed ? chalk.green(`â ${components.slashCommands.count}`) : chalk.red('â')}`);
|
|
119
|
+
|
|
120
|
+
// Outdated warnings
|
|
121
|
+
if (state.outdated) {
|
|
122
|
+
console.log(`\n ${chalk.yellow('â ')} Flow version outdated: ${state.version} â ${state.latestVersion}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (state.targetVersion && state.targetLatestVersion &&
|
|
126
|
+
isVersionOutdated(state.targetVersion, state.targetLatestVersion)) {
|
|
127
|
+
console.log(` ${chalk.yellow('â ')} ${state.target} update available: v${state.targetVersion} â v${state.targetLatestVersion}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (state.lastUpdated) {
|
|
131
|
+
const days = Math.floor((Date.now() - state.lastUpdated.getTime()) / (1000 * 60 * 60 * 24));
|
|
132
|
+
if (days > 7) {
|
|
133
|
+
console.log(`\n ${chalk.yellow('â ')} Last updated: ${days} days ago`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
console.log('');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get executable targets
|
|
143
|
+
*/
|
|
144
|
+
function getExecutableTargets(): string[] {
|
|
145
|
+
return targetManager.getImplementedTargetIDs().filter((targetId) => {
|
|
146
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
147
|
+
if (targetOption._tag === 'None') {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
return targetOption.value.executeCommand !== undefined;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Execute command using target's executeCommand method
|
|
156
|
+
*/
|
|
157
|
+
async function executeTargetCommand(
|
|
158
|
+
targetId: string,
|
|
159
|
+
systemPrompt: string,
|
|
160
|
+
userPrompt: string,
|
|
161
|
+
options: RunCommandOptions
|
|
162
|
+
): Promise<void> {
|
|
163
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
164
|
+
|
|
165
|
+
if (targetOption._tag === 'None') {
|
|
166
|
+
throw new CLIError(`Target not found: ${targetId}`, 'TARGET_NOT_FOUND');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const target = targetOption.value;
|
|
170
|
+
|
|
171
|
+
if (!target.isImplemented) {
|
|
172
|
+
throw new CLIError(
|
|
173
|
+
`Target '${targetId}' is not implemented. Supported targets: ${getExecutableTargets().join(', ')}`,
|
|
174
|
+
'TARGET_NOT_IMPLEMENTED'
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!target.executeCommand) {
|
|
179
|
+
throw new CLIError(
|
|
180
|
+
`Target '${targetId}' does not support command execution. Supported targets: ${getExecutableTargets().join(', ')}`,
|
|
181
|
+
'EXECUTION_NOT_SUPPORTED'
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return target.executeCommand(systemPrompt, userPrompt, options);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Compare versions
|
|
190
|
+
*/
|
|
191
|
+
function isVersionOutdated(current: string, latest: string): boolean {
|
|
192
|
+
try {
|
|
193
|
+
return compareVersions(current, latest) < 0;
|
|
194
|
+
} catch {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function compareVersions(v1: string, v2: string): number {
|
|
200
|
+
const parts1 = v1.split('.').map(Number);
|
|
201
|
+
const parts2 = v2.split('.').map(Number);
|
|
202
|
+
|
|
203
|
+
for (let i = 0; i < Math.min(parts1.length, parts2.length); i++) {
|
|
204
|
+
if (parts1[i] !== parts2[i]) {
|
|
205
|
+
return parts1[i] - parts2[i];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return parts1.length - parts2.length;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Resolve prompt - handle file input if needed
|
|
214
|
+
* Supports @filename syntax: @prompt.txt or @/path/to/prompt.txt
|
|
215
|
+
*/
|
|
216
|
+
async function resolvePrompt(prompt: string | undefined): Promise<string | undefined> {
|
|
217
|
+
if (!prompt) return prompt;
|
|
218
|
+
|
|
219
|
+
// Check for file input syntax: @filename
|
|
220
|
+
if (prompt.startsWith('@')) {
|
|
221
|
+
const filePath = prompt.slice(1); // Remove @ prefix
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const resolvedPath = path.isAbsolute(filePath)
|
|
225
|
+
? filePath
|
|
226
|
+
: path.resolve(process.cwd(), filePath);
|
|
227
|
+
|
|
228
|
+
const content = await fs.readFile(resolvedPath, 'utf-8');
|
|
229
|
+
console.log(chalk.dim(` â Loaded prompt from: ${filePath}\n`));
|
|
230
|
+
return content.trim();
|
|
231
|
+
} catch (error) {
|
|
232
|
+
throw new Error(`Failed to read prompt file: ${filePath}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return prompt;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Main flow execution logic - simplified with orchestrator
|
|
241
|
+
*/
|
|
242
|
+
export async function executeFlow(prompt: string | undefined, options: FlowOptions): Promise<void> {
|
|
243
|
+
// Resolve prompt (handle file input)
|
|
244
|
+
const resolvedPrompt = await resolvePrompt(prompt);
|
|
245
|
+
|
|
246
|
+
// Loop mode: Setup once, then loop only execution
|
|
247
|
+
if (options.loop !== undefined) {
|
|
248
|
+
const { LoopController } = await import('../core/loop-controller.js');
|
|
249
|
+
const controller = new LoopController();
|
|
250
|
+
|
|
251
|
+
// Default to 0s (no cooldown) if just --loop with no value
|
|
252
|
+
const interval = typeof options.loop === 'number' ? options.loop : 0;
|
|
253
|
+
|
|
254
|
+
// Auto-enable headless mode for loop
|
|
255
|
+
options.print = true;
|
|
256
|
+
|
|
257
|
+
// ONE-TIME SETUP: Do all initialization once before loop starts
|
|
258
|
+
const setupContext = await executeSetupPhase(resolvedPrompt, options);
|
|
259
|
+
|
|
260
|
+
// Save original continue flag
|
|
261
|
+
const originalContinue = options.continue || false;
|
|
262
|
+
|
|
263
|
+
// LOOP: Only execute the command repeatedly
|
|
264
|
+
await controller.run(
|
|
265
|
+
async () => {
|
|
266
|
+
const isFirstIteration = controller['state'].iteration === 1;
|
|
267
|
+
|
|
268
|
+
// Continue logic:
|
|
269
|
+
// - If user specified --continue, always use it (all iterations)
|
|
270
|
+
// - If user didn't specify, only use from 2nd iteration onwards
|
|
271
|
+
options.continue = originalContinue || !isFirstIteration;
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
await executeCommandOnly(setupContext, resolvedPrompt, options);
|
|
275
|
+
return { exitCode: 0 };
|
|
276
|
+
} catch (error) {
|
|
277
|
+
return { exitCode: 1, error: error as Error };
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
enabled: true,
|
|
282
|
+
interval,
|
|
283
|
+
maxRuns: options.maxRuns,
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Normal execution (non-loop)
|
|
291
|
+
await executeFlowOnce(resolvedPrompt, options);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Setup context for command execution
|
|
296
|
+
* Returns everything needed to execute the command repeatedly
|
|
297
|
+
*/
|
|
298
|
+
interface SetupContext {
|
|
299
|
+
resolvedTarget: string;
|
|
300
|
+
agent: string;
|
|
301
|
+
systemPrompt: string;
|
|
302
|
+
runOptions: RunCommandOptions;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Execute setup phase once (for loop mode)
|
|
307
|
+
* Returns context needed for repeated command execution
|
|
308
|
+
*/
|
|
309
|
+
async function executeSetupPhase(prompt: string | undefined, options: FlowOptions): Promise<SetupContext> {
|
|
310
|
+
// Quick mode: enable useDefaults and skip prompts
|
|
311
|
+
if (options.quick) {
|
|
312
|
+
options.useDefaults = true;
|
|
313
|
+
console.log(chalk.cyan('â¡ Quick mode enabled - using saved defaults\n'));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Import orchestrator functions
|
|
317
|
+
const {
|
|
318
|
+
checkUpgrades,
|
|
319
|
+
checkComponentIntegrity,
|
|
320
|
+
selectTarget,
|
|
321
|
+
initializeProject,
|
|
322
|
+
} = await import('./flow-orchestrator.js');
|
|
323
|
+
|
|
324
|
+
// Show welcome banner (only once)
|
|
325
|
+
showWelcome();
|
|
326
|
+
|
|
327
|
+
let selectedTarget: string | undefined;
|
|
328
|
+
let state: ProjectState | undefined;
|
|
329
|
+
|
|
330
|
+
// Determine target
|
|
331
|
+
const initialTarget = options.target || (await projectSettings.getDefaultTarget());
|
|
332
|
+
|
|
333
|
+
// Detect state if we have a target
|
|
334
|
+
if (initialTarget && !options.sync) {
|
|
335
|
+
const detector = new StateDetector();
|
|
336
|
+
|
|
337
|
+
if (options.verbose) {
|
|
338
|
+
console.log(chalk.dim('ð€ Checking project status...\n'));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
state = await detector.detect();
|
|
342
|
+
|
|
343
|
+
if (options.verbose) {
|
|
344
|
+
await showStatus(state);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Check for upgrades
|
|
348
|
+
if (!options.quick) {
|
|
349
|
+
await checkUpgrades(state, options);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Check component integrity
|
|
353
|
+
await checkComponentIntegrity(state, options);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Initialize if needed
|
|
357
|
+
const shouldInitialize =
|
|
358
|
+
!state?.initialized ||
|
|
359
|
+
options.sync ||
|
|
360
|
+
options.repair ||
|
|
361
|
+
options.initOnly;
|
|
362
|
+
|
|
363
|
+
if (shouldInitialize) {
|
|
364
|
+
try {
|
|
365
|
+
const { selectAndValidateTarget, previewDryRun, installComponents } =
|
|
366
|
+
await import('./init-core.js');
|
|
367
|
+
|
|
368
|
+
const initOptions = {
|
|
369
|
+
target: options.target,
|
|
370
|
+
verbose: options.verbose || false,
|
|
371
|
+
dryRun: options.dryRun || false,
|
|
372
|
+
clear: options.sync || false,
|
|
373
|
+
mcp: options.mcp !== false,
|
|
374
|
+
agents: options.agents !== false,
|
|
375
|
+
rules: options.rules !== false,
|
|
376
|
+
outputStyles: options.outputStyles !== false,
|
|
377
|
+
slashCommands: options.slashCommands !== false,
|
|
378
|
+
hooks: options.hooks !== false,
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
// Handle sync mode - delete template files first
|
|
382
|
+
if (options.sync && !options.dryRun) {
|
|
383
|
+
const { buildSyncManifest, showSyncPreview, confirmSync, executeSyncDelete } = await import('../utils/sync-utils.js');
|
|
384
|
+
|
|
385
|
+
// Need target to build manifest
|
|
386
|
+
const targetId = await selectAndValidateTarget(initOptions);
|
|
387
|
+
selectedTarget = targetId;
|
|
388
|
+
|
|
389
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
390
|
+
if (targetOption._tag === 'None') {
|
|
391
|
+
throw new Error(`Target not found: ${targetId}`);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const target = targetOption.value;
|
|
395
|
+
const manifest = await buildSyncManifest(process.cwd(), target);
|
|
396
|
+
|
|
397
|
+
console.log(chalk.cyan.bold('âââ ð Synchronizing Files\n'));
|
|
398
|
+
showSyncPreview(manifest, process.cwd());
|
|
399
|
+
|
|
400
|
+
const confirmed = await confirmSync();
|
|
401
|
+
if (!confirmed) {
|
|
402
|
+
console.log(chalk.yellow('\nâ Sync cancelled\n'));
|
|
403
|
+
process.exit(0);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const deletedCount = await executeSyncDelete(manifest);
|
|
407
|
+
console.log(chalk.green(`\nâ Deleted ${deletedCount} files\n`));
|
|
408
|
+
} else if (!options.sync) {
|
|
409
|
+
const targetId = await selectAndValidateTarget(initOptions);
|
|
410
|
+
selectedTarget = targetId;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (options.dryRun) {
|
|
414
|
+
// Ensure we have a target ID for dry run
|
|
415
|
+
if (!selectedTarget) {
|
|
416
|
+
const targetId = await selectAndValidateTarget(initOptions);
|
|
417
|
+
selectedTarget = targetId;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
console.log(
|
|
421
|
+
boxen(
|
|
422
|
+
chalk.yellow('â Dry Run Mode') + chalk.dim('\nNo changes will be made to your project'),
|
|
423
|
+
{
|
|
424
|
+
padding: 1,
|
|
425
|
+
margin: { top: 0, bottom: 1, left: 0, right: 0 },
|
|
426
|
+
borderStyle: 'round',
|
|
427
|
+
borderColor: 'yellow',
|
|
428
|
+
}
|
|
429
|
+
)
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
await previewDryRun(selectedTarget, initOptions);
|
|
433
|
+
|
|
434
|
+
console.log(
|
|
435
|
+
'\n' +
|
|
436
|
+
boxen(chalk.green.bold('â Dry run complete'), {
|
|
437
|
+
padding: { top: 0, bottom: 0, left: 2, right: 2 },
|
|
438
|
+
margin: 0,
|
|
439
|
+
borderStyle: 'round',
|
|
440
|
+
borderColor: 'green',
|
|
441
|
+
}) +
|
|
442
|
+
'\n'
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
console.log(chalk.dim('â Initialization dry run complete\n'));
|
|
446
|
+
} else {
|
|
447
|
+
// Ensure we have a target ID for installation
|
|
448
|
+
if (!selectedTarget) {
|
|
449
|
+
const targetId = await selectAndValidateTarget(initOptions);
|
|
450
|
+
selectedTarget = targetId;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
await installComponents(selectedTarget, initOptions);
|
|
454
|
+
console.log(chalk.green.bold('â Initialization complete\n'));
|
|
455
|
+
}
|
|
456
|
+
} catch (error) {
|
|
457
|
+
console.error(chalk.red.bold('â Initialization failed:'), error);
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Resolve target
|
|
463
|
+
let targetForResolution = options.target || state?.target || selectedTarget;
|
|
464
|
+
if (selectedTarget) {
|
|
465
|
+
targetForResolution = selectedTarget;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (!targetForResolution) {
|
|
469
|
+
console.error(chalk.red.bold('â No target selected. Use --target or run init first.'));
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const resolvedTarget = await targetManager.resolveTarget({
|
|
474
|
+
target: targetForResolution,
|
|
475
|
+
allowSelection: false,
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
console.log(chalk.cyan.bold(`âââ ð¯ Launching ${resolvedTarget}\n`));
|
|
479
|
+
|
|
480
|
+
// Check if target supports command execution
|
|
481
|
+
const { getTargetsWithCommandSupport } = await import('../config/targets.js');
|
|
482
|
+
const supportedTargets = getTargetsWithCommandSupport().map(t => t.id);
|
|
483
|
+
|
|
484
|
+
if (!supportedTargets.includes(resolvedTarget)) {
|
|
485
|
+
console.log(chalk.red.bold('â Unsupported target platform\n'));
|
|
486
|
+
console.log(chalk.yellow(`Target '${resolvedTarget}' does not support agent execution.`));
|
|
487
|
+
console.log(chalk.cyan(`Supported platforms: ${supportedTargets.join(', ')}\n`));
|
|
488
|
+
console.log(chalk.dim('Tip: Use --target claude-code to specify Claude Code platform'));
|
|
489
|
+
console.log(chalk.dim('Example: bun dev:flow --target claude-code\n'));
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Claude Code handling
|
|
494
|
+
if (resolvedTarget === 'claude-code') {
|
|
495
|
+
const { SmartConfigService } = await import('../services/smart-config-service.js');
|
|
496
|
+
const { ConfigService } = await import('../services/config-service.js');
|
|
497
|
+
|
|
498
|
+
if (!(await ConfigService.hasInitialSetup())) {
|
|
499
|
+
console.log(chalk.cyan('ð First-time setup for Claude Code\n'));
|
|
500
|
+
await SmartConfigService.initialSetup();
|
|
501
|
+
console.log(chalk.green('â Setup complete!\n'));
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const runtimeChoices = await SmartConfigService.selectRuntimeChoices({
|
|
505
|
+
selectProvider: options.selectProvider,
|
|
506
|
+
selectAgent: options.selectAgent,
|
|
507
|
+
useDefaults: options.useDefaults,
|
|
508
|
+
provider: options.provider,
|
|
509
|
+
agent: options.agent,
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
await SmartConfigService.setupEnvironment(runtimeChoices.provider!);
|
|
513
|
+
options.agent = runtimeChoices.agent;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const agent = options.agent || 'coder';
|
|
517
|
+
const verbose = options.verbose || false;
|
|
518
|
+
|
|
519
|
+
if (verbose || options.runOnly || !options.quick) {
|
|
520
|
+
console.log(` ð€ Agent: ${chalk.cyan(agent)}`);
|
|
521
|
+
console.log(` ð¯ Target: ${chalk.cyan(resolvedTarget)}`);
|
|
522
|
+
if (prompt) {
|
|
523
|
+
console.log(` ð¬ Prompt: ${chalk.dim(prompt)}\n`);
|
|
524
|
+
} else {
|
|
525
|
+
console.log(` ð¬ Mode: ${chalk.dim('Interactive')}\n`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Load agent and prepare prompts
|
|
530
|
+
const agentContent = await loadAgentContent(agent, options.agentFile);
|
|
531
|
+
const agentInstructions = extractAgentInstructions(agentContent);
|
|
532
|
+
const systemPrompt = `AGENT INSTRUCTIONS:\n${agentInstructions}`;
|
|
533
|
+
|
|
534
|
+
// Prepare run options
|
|
535
|
+
const runOptions: RunCommandOptions = {
|
|
536
|
+
target: resolvedTarget,
|
|
537
|
+
verbose,
|
|
538
|
+
dryRun: options.dryRun,
|
|
539
|
+
agent,
|
|
540
|
+
agentFile: options.agentFile,
|
|
541
|
+
prompt,
|
|
542
|
+
print: options.print,
|
|
543
|
+
continue: options.continue,
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
return {
|
|
547
|
+
resolvedTarget,
|
|
548
|
+
agent,
|
|
549
|
+
systemPrompt,
|
|
550
|
+
runOptions,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Execute command only (for loop mode iterations)
|
|
556
|
+
* Uses pre-setup context to execute command without re-doing setup
|
|
557
|
+
*/
|
|
558
|
+
async function executeCommandOnly(
|
|
559
|
+
context: SetupContext,
|
|
560
|
+
prompt: string | undefined,
|
|
561
|
+
options: FlowOptions
|
|
562
|
+
): Promise<void> {
|
|
563
|
+
const userPrompt = prompt?.trim() || '';
|
|
564
|
+
|
|
565
|
+
// Update continue flag in runOptions
|
|
566
|
+
const runOptions = {
|
|
567
|
+
...context.runOptions,
|
|
568
|
+
continue: options.continue,
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
try {
|
|
572
|
+
await executeTargetCommand(context.resolvedTarget, context.systemPrompt, userPrompt, runOptions);
|
|
573
|
+
} catch (error) {
|
|
574
|
+
console.error(chalk.red.bold('\nâ Launch failed:'), error);
|
|
575
|
+
throw error;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Single flow execution (used by both normal and loop mode)
|
|
581
|
+
*/
|
|
582
|
+
async function executeFlowOnce(prompt: string | undefined, options: FlowOptions): Promise<void> {
|
|
583
|
+
// Quick mode: enable useDefaults and skip prompts
|
|
584
|
+
if (options.quick) {
|
|
585
|
+
options.useDefaults = true;
|
|
586
|
+
console.log(chalk.cyan('â¡ Quick mode enabled - using saved defaults\n'));
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Continue mode always requires print mode
|
|
590
|
+
if (options.continue && !options.print) {
|
|
591
|
+
options.print = true;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Import orchestrator functions
|
|
595
|
+
const {
|
|
596
|
+
checkUpgrades,
|
|
597
|
+
checkComponentIntegrity,
|
|
598
|
+
selectTarget,
|
|
599
|
+
initializeProject,
|
|
600
|
+
launchTarget,
|
|
601
|
+
} = await import('./flow-orchestrator.js');
|
|
602
|
+
|
|
603
|
+
// Show welcome banner
|
|
604
|
+
showWelcome();
|
|
605
|
+
|
|
606
|
+
// Declare at function level to persist across steps
|
|
607
|
+
let selectedTarget: string | undefined;
|
|
608
|
+
let state: ProjectState | undefined;
|
|
609
|
+
|
|
610
|
+
// First: determine target (from options, saved settings, or init will prompt)
|
|
611
|
+
const initialTarget = options.target || (await projectSettings.getDefaultTarget());
|
|
612
|
+
|
|
613
|
+
// Only detect state if we have a target (can't check components without knowing target structure)
|
|
614
|
+
if (initialTarget && !options.sync) {
|
|
615
|
+
const detector = new StateDetector();
|
|
616
|
+
const upgradeManager = new UpgradeManager();
|
|
617
|
+
|
|
618
|
+
if (options.verbose) {
|
|
619
|
+
console.log(chalk.dim('ð€ Checking project status...\n'));
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
state = await detector.detect();
|
|
623
|
+
|
|
624
|
+
if (options.verbose) {
|
|
625
|
+
await showStatus(state);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Step 1: Check for upgrades
|
|
629
|
+
if (!options.quick) {
|
|
630
|
+
await checkUpgrades(state, options);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Step 1: Upgrade (if requested)
|
|
634
|
+
if (options.upgrade && state.outdated && state.latestVersion) {
|
|
635
|
+
console.log(chalk.cyan.bold('âââ ðŠ Upgrading Flow\n'));
|
|
636
|
+
await upgradeManager.upgradeFlow(state);
|
|
637
|
+
console.log(chalk.green('â Upgrade complete\n'));
|
|
638
|
+
// Re-detect after upgrade
|
|
639
|
+
state.version = state.latestVersion;
|
|
640
|
+
state.outdated = false;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Step 2: Upgrade target (if requested)
|
|
644
|
+
if (options.upgradeTarget && state.target) {
|
|
645
|
+
console.log(chalk.cyan.bold(`âââ ð¯ Upgrading ${state.target}\n`));
|
|
646
|
+
await upgradeManager.upgradeTarget(state);
|
|
647
|
+
console.log(chalk.green('â Target upgrade complete\n'));
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Step 2.5: Check component integrity (only if we have valid state)
|
|
651
|
+
await checkComponentIntegrity(state, options);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Step 3: Initialize (only if actually needed)
|
|
655
|
+
// Positive logic: should initialize when:
|
|
656
|
+
// - Not initialized yet (state?.initialized === false)
|
|
657
|
+
// - Sync mode (wipe and reinstall)
|
|
658
|
+
// - Repair mode (install missing components)
|
|
659
|
+
// - Init-only mode (user explicitly wants init)
|
|
660
|
+
const shouldInitialize =
|
|
661
|
+
!state?.initialized || // Not initialized yet
|
|
662
|
+
options.sync || // Sync reinstall
|
|
663
|
+
options.repair || // Repair missing components
|
|
664
|
+
options.initOnly; // Explicit init request
|
|
665
|
+
|
|
666
|
+
if (shouldInitialize) {
|
|
667
|
+
console.log(chalk.cyan.bold('âââ ð Initializing Project\n'));
|
|
668
|
+
|
|
669
|
+
// Import core init functions
|
|
670
|
+
const {
|
|
671
|
+
selectAndValidateTarget,
|
|
672
|
+
previewDryRun,
|
|
673
|
+
installComponents,
|
|
674
|
+
} = await import('./init-core.js');
|
|
675
|
+
|
|
676
|
+
try {
|
|
677
|
+
// In repair mode, use existing target from state
|
|
678
|
+
const targetForInit = options.repair && state?.target
|
|
679
|
+
? state.target
|
|
680
|
+
: options.target;
|
|
681
|
+
|
|
682
|
+
// Prepare init options
|
|
683
|
+
const initOptions = {
|
|
684
|
+
target: targetForInit, // Use existing target in repair mode
|
|
685
|
+
verbose: options.verbose,
|
|
686
|
+
dryRun: options.dryRun,
|
|
687
|
+
clear: options.sync || false,
|
|
688
|
+
mcp: options.mcp !== false,
|
|
689
|
+
agents: options.agents !== false,
|
|
690
|
+
rules: options.rules !== false,
|
|
691
|
+
outputStyles: options.outputStyles !== false,
|
|
692
|
+
slashCommands: options.slashCommands !== false,
|
|
693
|
+
hooks: options.hooks !== false,
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
// Handle sync mode - delete template files first
|
|
697
|
+
if (options.sync && !options.dryRun) {
|
|
698
|
+
const { buildSyncManifest, showSyncPreview, confirmSync, executeSyncDelete } = await import('../utils/sync-utils.js');
|
|
699
|
+
|
|
700
|
+
// Need target to build manifest
|
|
701
|
+
const targetId = await selectAndValidateTarget(initOptions);
|
|
702
|
+
selectedTarget = targetId;
|
|
703
|
+
|
|
704
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
705
|
+
if (targetOption._tag === 'None') {
|
|
706
|
+
throw new Error(`Target not found: ${targetId}`);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const target = targetOption.value;
|
|
710
|
+
const manifest = await buildSyncManifest(process.cwd(), target);
|
|
711
|
+
|
|
712
|
+
console.log(chalk.cyan.bold('âââ ð Synchronizing Files\n'));
|
|
713
|
+
showSyncPreview(manifest, process.cwd());
|
|
714
|
+
|
|
715
|
+
const confirmed = await confirmSync();
|
|
716
|
+
if (!confirmed) {
|
|
717
|
+
console.log(chalk.yellow('\nâ Sync cancelled\n'));
|
|
718
|
+
process.exit(0);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const deletedCount = await executeSyncDelete(manifest);
|
|
722
|
+
console.log(chalk.green(`\nâ Deleted ${deletedCount} files\n`));
|
|
723
|
+
} else {
|
|
724
|
+
// Select and validate target (will use existing in repair mode, or prompt if needed)
|
|
725
|
+
const targetId = await selectAndValidateTarget(initOptions);
|
|
726
|
+
selectedTarget = targetId; // Save for later use
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// Dry run preview
|
|
730
|
+
if (options.dryRun) {
|
|
731
|
+
// Ensure we have a target ID for dry run
|
|
732
|
+
if (!selectedTarget) {
|
|
733
|
+
const targetId = await selectAndValidateTarget(initOptions);
|
|
734
|
+
selectedTarget = targetId;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
console.log(
|
|
738
|
+
boxen(
|
|
739
|
+
chalk.yellow('â Dry Run Mode') + chalk.dim('\nNo changes will be made to your project'),
|
|
740
|
+
{
|
|
741
|
+
padding: 1,
|
|
742
|
+
margin: { top: 0, bottom: 1, left: 0, right: 0 },
|
|
743
|
+
borderStyle: 'round',
|
|
744
|
+
borderColor: 'yellow',
|
|
745
|
+
}
|
|
746
|
+
)
|
|
747
|
+
);
|
|
748
|
+
|
|
749
|
+
await previewDryRun(selectedTarget, initOptions);
|
|
750
|
+
|
|
751
|
+
console.log(
|
|
752
|
+
'\n' +
|
|
753
|
+
boxen(chalk.green.bold('â Dry run complete'), {
|
|
754
|
+
padding: { top: 0, bottom: 0, left: 2, right: 2 },
|
|
755
|
+
margin: 0,
|
|
756
|
+
borderStyle: 'round',
|
|
757
|
+
borderColor: 'green',
|
|
758
|
+
}) +
|
|
759
|
+
'\n'
|
|
760
|
+
);
|
|
761
|
+
|
|
762
|
+
console.log(chalk.dim('â Initialization dry run complete\n'));
|
|
763
|
+
// Don't return - continue to show execution command
|
|
764
|
+
} else {
|
|
765
|
+
// Actually install components
|
|
766
|
+
// Ensure we have a target ID for installation
|
|
767
|
+
if (!selectedTarget) {
|
|
768
|
+
const targetId = await selectAndValidateTarget(initOptions);
|
|
769
|
+
selectedTarget = targetId;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
const result = await installComponents(selectedTarget, initOptions);
|
|
773
|
+
|
|
774
|
+
console.log(chalk.green.bold('â Initialization complete\n'));
|
|
775
|
+
}
|
|
776
|
+
} catch (error) {
|
|
777
|
+
console.error(chalk.red.bold('â Initialization failed:'), error);
|
|
778
|
+
process.exit(1);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// Step 4: Launch target (if not init-only)
|
|
783
|
+
if (!options.initOnly) {
|
|
784
|
+
// Resolve target - use the target we just selected
|
|
785
|
+
let targetForResolution = options.target || state?.target || selectedTarget;
|
|
786
|
+
|
|
787
|
+
// If we just selected a target during init, use that
|
|
788
|
+
if (selectedTarget) {
|
|
789
|
+
targetForResolution = selectedTarget;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
if (!targetForResolution) {
|
|
793
|
+
console.error(chalk.red.bold('â No target selected. Use --target or run init first.'));
|
|
794
|
+
process.exit(1);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const resolvedTarget = await targetManager.resolveTarget({
|
|
798
|
+
target: targetForResolution,
|
|
799
|
+
allowSelection: false, // Target should already be selected during init
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
console.log(chalk.cyan.bold(`âââ ð¯ Launching ${resolvedTarget}\n`));
|
|
803
|
+
|
|
804
|
+
// Check if target supports command execution
|
|
805
|
+
const { getTargetsWithCommandSupport } = await import('../config/targets.js');
|
|
806
|
+
const supportedTargets = getTargetsWithCommandSupport().map(t => t.id);
|
|
807
|
+
|
|
808
|
+
if (!supportedTargets.includes(resolvedTarget)) {
|
|
809
|
+
console.log(chalk.red.bold('â Unsupported target platform\n'));
|
|
810
|
+
console.log(chalk.yellow(`Target '${resolvedTarget}' does not support agent execution.`));
|
|
811
|
+
console.log(chalk.cyan(`Supported platforms: ${supportedTargets.join(', ')}\n`));
|
|
812
|
+
console.log(chalk.dim('Tip: Use --target claude-code to specify Claude Code platform'));
|
|
813
|
+
console.log(chalk.dim('Example: bun dev:flow --target claude-code\n'));
|
|
814
|
+
process.exit(1);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// Claude Code handling - needs provider/agent setup
|
|
818
|
+
if (resolvedTarget === 'claude-code') {
|
|
819
|
+
// Handle provider and agent selection for Claude Code
|
|
820
|
+
const { SmartConfigService } = await import('../services/smart-config-service.js');
|
|
821
|
+
|
|
822
|
+
// Check if API keys are configured, if not, run initial setup
|
|
823
|
+
const { ConfigService } = await import('../services/config-service.js');
|
|
824
|
+
if (!(await ConfigService.hasInitialSetup())) {
|
|
825
|
+
console.log(chalk.cyan('ð First-time setup for Claude Code\n'));
|
|
826
|
+
await SmartConfigService.initialSetup();
|
|
827
|
+
console.log(chalk.green('â Setup complete!\n'));
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
const runtimeChoices = await SmartConfigService.selectRuntimeChoices({
|
|
831
|
+
selectProvider: options.selectProvider,
|
|
832
|
+
selectAgent: options.selectAgent,
|
|
833
|
+
useDefaults: options.useDefaults,
|
|
834
|
+
provider: options.provider,
|
|
835
|
+
agent: options.agent,
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
// Setup environment with selected provider
|
|
839
|
+
await SmartConfigService.setupEnvironment(runtimeChoices.provider!);
|
|
840
|
+
|
|
841
|
+
// Use selected agent
|
|
842
|
+
options.agent = runtimeChoices.agent;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
const agent = options.agent || 'coder';
|
|
846
|
+
const verbose = options.verbose || false;
|
|
847
|
+
|
|
848
|
+
if (verbose || options.runOnly || !options.quick) {
|
|
849
|
+
console.log(` ð€ Agent: ${chalk.cyan(agent)}`);
|
|
850
|
+
console.log(` ð¯ Target: ${chalk.cyan(resolvedTarget)}`);
|
|
851
|
+
if (prompt) {
|
|
852
|
+
console.log(` ð¬ Prompt: ${chalk.dim(prompt)}\n`);
|
|
853
|
+
} else {
|
|
854
|
+
console.log(` ð¬ Mode: ${chalk.dim('Interactive')}\n`);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// Load agent and prepare prompts
|
|
859
|
+
const agentContent = await loadAgentContent(agent, options.agentFile);
|
|
860
|
+
const agentInstructions = extractAgentInstructions(agentContent);
|
|
861
|
+
const systemPrompt = `AGENT INSTRUCTIONS:\n${agentInstructions}`;
|
|
862
|
+
|
|
863
|
+
const userPrompt = prompt?.trim() || '';
|
|
864
|
+
|
|
865
|
+
// Environment should already be set up by SmartConfigService in main flow
|
|
866
|
+
// No need to setup again here
|
|
867
|
+
|
|
868
|
+
// Run options
|
|
869
|
+
const runOptions: RunCommandOptions = {
|
|
870
|
+
target: resolvedTarget,
|
|
871
|
+
verbose,
|
|
872
|
+
dryRun: options.dryRun,
|
|
873
|
+
agent,
|
|
874
|
+
agentFile: options.agentFile,
|
|
875
|
+
prompt,
|
|
876
|
+
print: options.print,
|
|
877
|
+
continue: options.continue,
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
try {
|
|
881
|
+
await executeTargetCommand(resolvedTarget, systemPrompt, userPrompt, runOptions);
|
|
882
|
+
} catch (error) {
|
|
883
|
+
console.error(chalk.red.bold('\nâ Launch failed:'), error);
|
|
884
|
+
process.exit(1);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
if (!options.dryRun) {
|
|
888
|
+
console.log(chalk.dim('âââ\n'));
|
|
889
|
+
console.log(chalk.green('â Session complete\n'));
|
|
890
|
+
}
|
|
891
|
+
} else {
|
|
892
|
+
console.log(chalk.dim('â Init-only mode, skipping execution\n'));
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Smart flow command
|
|
898
|
+
*/
|
|
899
|
+
export const flowCommand = new Command('flow')
|
|
900
|
+
.description('Intelligent development flow (auto-detect state and act accordingly)')
|
|
901
|
+
|
|
902
|
+
// Smart options
|
|
903
|
+
.option('--init-only', 'Only initialize, do not run')
|
|
904
|
+
.option('--run-only', 'Only run, skip initialization')
|
|
905
|
+
.option('--sync', 'Synchronize with Flow templates (delete and re-install template files)')
|
|
906
|
+
.option('--upgrade', 'Upgrade Sylphx Flow to latest version')
|
|
907
|
+
.option('--upgrade-target', 'Upgrade target platform (Claude Code/OpenCode)')
|
|
908
|
+
|
|
909
|
+
// Smart configuration options
|
|
910
|
+
.option('--quick', 'Quick mode: use saved defaults and skip all prompts')
|
|
911
|
+
.option('--select-provider', 'Prompt to select provider each run')
|
|
912
|
+
.option('--select-agent', 'Prompt to select agent each run')
|
|
913
|
+
.option('--use-defaults', 'Skip prompts, use saved defaults')
|
|
914
|
+
.option('--provider <provider>', 'Override provider for this run (anthropic|z.ai|kimi)')
|
|
915
|
+
|
|
916
|
+
// Init options
|
|
917
|
+
.option('--target <type>', 'Target platform (opencode, claude-code, auto-detect)')
|
|
918
|
+
.option('--verbose', 'Show detailed output')
|
|
919
|
+
.option('--dry-run', 'Show what would be done without making changes')
|
|
920
|
+
.option('--no-mcp', 'Skip MCP installation')
|
|
921
|
+
.option('--no-agents', 'Skip agents installation')
|
|
922
|
+
.option('--no-rules', 'Skip rules installation')
|
|
923
|
+
.option('--no-output-styles', 'Skip output styles installation')
|
|
924
|
+
.option('--no-slash-commands', 'Skip slash commands installation')
|
|
925
|
+
.option('--no-hooks', 'Skip hooks setup')
|
|
926
|
+
|
|
927
|
+
// Run options
|
|
928
|
+
.option('--agent <name>', 'Agent to use (default: coder)', 'coder')
|
|
929
|
+
.option('--agent-file <path>', 'Load agent from specific file')
|
|
930
|
+
.option('-p, --print', 'Headless print mode (output only, no interactive)')
|
|
931
|
+
.option('-c, --continue', 'Continue previous conversation (requires print mode)')
|
|
932
|
+
|
|
933
|
+
// Prompt argument
|
|
934
|
+
.argument('[prompt]', 'Prompt to execute with agent (optional, supports @file.txt for file input)')
|
|
935
|
+
|
|
936
|
+
.action(async (prompt, options) => {
|
|
937
|
+
await executeFlow(prompt, options);
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* Setup command - alias for `flow --init-only`
|
|
942
|
+
* Kept for backward compatibility, but users should prefer `flow --init-only`
|
|
943
|
+
*/
|
|
944
|
+
export const setupCommand = new Command('setup')
|
|
945
|
+
.description('Initialize project configuration (alias for: flow --init-only)')
|
|
946
|
+
.action(async () => {
|
|
947
|
+
console.log(chalk.yellow('â¹ The "setup" command is deprecated.'));
|
|
948
|
+
console.log(chalk.yellow(' Please use: flow --init-only\n'));
|
|
949
|
+
|
|
950
|
+
showWelcome();
|
|
951
|
+
|
|
952
|
+
// Initialize project with default target
|
|
953
|
+
const { runInit } = await import('./init-command.js');
|
|
954
|
+
await runInit({
|
|
955
|
+
target: undefined, // Let user choose
|
|
956
|
+
verbose: false,
|
|
957
|
+
dryRun: false,
|
|
958
|
+
clear: false,
|
|
959
|
+
mcp: true,
|
|
960
|
+
agents: true,
|
|
961
|
+
rules: true,
|
|
962
|
+
outputStyles: true,
|
|
963
|
+
slashCommands: true,
|
|
964
|
+
hooks: true,
|
|
965
|
+
helpOption: () => {},
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
console.log(chalk.green('\nâ
Setup complete!'));
|
|
969
|
+
console.log(chalk.dim('\nNext time, use: flow --init-only'));
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* Status command - show project status
|
|
974
|
+
*/
|
|
975
|
+
export const statusCommand = new Command('status')
|
|
976
|
+
.description('Show project status and configuration')
|
|
977
|
+
.option('--verbose', 'Show detailed information')
|
|
978
|
+
.action(async (options) => {
|
|
979
|
+
const detector = new StateDetector();
|
|
980
|
+
const state = await detector.detect();
|
|
981
|
+
|
|
982
|
+
showWelcome();
|
|
983
|
+
await showStatus(state);
|
|
984
|
+
|
|
985
|
+
// Show detailed info if verbose
|
|
986
|
+
if (options.verbose) {
|
|
987
|
+
console.log(chalk.cyan.bold('\nð 诊ç»ä¿¡æ¯\n'));
|
|
988
|
+
|
|
989
|
+
// é
眮æä»¶å
容
|
|
990
|
+
try {
|
|
991
|
+
const { getProjectSettingsFile } = await import('../config/constants.js');
|
|
992
|
+
const configPath = path.join(process.cwd(), getProjectSettingsFile());
|
|
993
|
+
const config = JSON.parse(await fs.readFile(configPath, 'utf-8'));
|
|
994
|
+
console.log('é
眮æä»¶:', JSON.stringify(config, null, 2));
|
|
995
|
+
} catch {
|
|
996
|
+
console.log('é
眮æä»¶: äžååš');
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* Doctor command - diagnose and fix issues
|
|
1003
|
+
*/
|
|
1004
|
+
export const doctorCommand = new Command('doctor')
|
|
1005
|
+
.description('Diagnose and fix common issues')
|
|
1006
|
+
.option('--fix', 'Automatically fix issues')
|
|
1007
|
+
.option('--verbose', 'Show detailed diagnostics')
|
|
1008
|
+
.action(async (options) => {
|
|
1009
|
+
console.log(chalk.cyan.bold('ð è¯æé¡¹ç®\n'));
|
|
1010
|
+
|
|
1011
|
+
const detector = new StateDetector();
|
|
1012
|
+
const state = await detector.detect();
|
|
1013
|
+
|
|
1014
|
+
let issuesFound = false;
|
|
1015
|
+
|
|
1016
|
+
// Check 1: Claude Code installation
|
|
1017
|
+
console.log('æ£æ¥ Claude Code å®è£
...');
|
|
1018
|
+
try {
|
|
1019
|
+
const { exec } = await import('node:child_process');
|
|
1020
|
+
const { promisify } = await import('node:util');
|
|
1021
|
+
const execAsync = promisify(exec);
|
|
1022
|
+
await execAsync('which claude');
|
|
1023
|
+
console.log(chalk.green(' â Claude Code å·²å®è£
'));
|
|
1024
|
+
} catch {
|
|
1025
|
+
console.log(chalk.red(' â Claude Code æªå®è£
'));
|
|
1026
|
+
console.log(chalk.dim(' è¿è¡: npm install -g @anthropic-ai/claude-code'));
|
|
1027
|
+
issuesFound = true;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// Check 2: Configuration
|
|
1031
|
+
console.log('\næ£æ¥é
眮...');
|
|
1032
|
+
if (state.corrupted) {
|
|
1033
|
+
console.log(chalk.red(' â é
眮æå'));
|
|
1034
|
+
issuesFound = true;
|
|
1035
|
+
|
|
1036
|
+
if (options.fix) {
|
|
1037
|
+
console.log(chalk.yellow(' ð æ£åšä¿®å€...'));
|
|
1038
|
+
// Run flow with clean flag
|
|
1039
|
+
const { executeFlow } = await import('./flow-command.js');
|
|
1040
|
+
await executeFlow(undefined, { clean: true });
|
|
1041
|
+
console.log(chalk.green(' â 已修å€'));
|
|
1042
|
+
}
|
|
1043
|
+
} else if (!state.initialized) {
|
|
1044
|
+
console.log(chalk.yellow(' â é¡¹ç®æªåå§å'));
|
|
1045
|
+
issuesFound = true;
|
|
1046
|
+
} else {
|
|
1047
|
+
console.log(chalk.green(' â é
眮æ£åžž'));
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// Check 3: Components
|
|
1051
|
+
console.log('\næ£æ¥ç»ä»¶...');
|
|
1052
|
+
Object.entries(state.components).forEach(([name, component]) => {
|
|
1053
|
+
const status = component.installed ? chalk.green('â') : chalk.red('â');
|
|
1054
|
+
const count = ('count' in component && component.count) ? ` (${component.count})` : '';
|
|
1055
|
+
console.log(` ${status} ${name}${count}`);
|
|
1056
|
+
});
|
|
1057
|
+
|
|
1058
|
+
// Summary
|
|
1059
|
+
console.log('\n' + chalk.bold('ç»æ:'));
|
|
1060
|
+
if (!issuesFound) {
|
|
1061
|
+
console.log(chalk.green('â æææ£æ¥éè¿'));
|
|
1062
|
+
} else if (options.fix) {
|
|
1063
|
+
console.log(chalk.green('â ææé®é¢å·²ä¿®å€'));
|
|
1064
|
+
} else {
|
|
1065
|
+
console.log(chalk.yellow('â åç°é®é¢ïŒè¿è¡å --fix åæ°èªåšä¿®å€'));
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
/**
|
|
1070
|
+
* Upgrade command - upgrade components
|
|
1071
|
+
*/
|
|
1072
|
+
export const upgradeCommand = new Command('upgrade')
|
|
1073
|
+
.description('Upgrade Sylphx Flow and components')
|
|
1074
|
+
.option('--check', 'Only check for updates, do not upgrade')
|
|
1075
|
+
.option('--components', 'Upgrade components (agents, rules, etc)', true)
|
|
1076
|
+
.option('--target', 'Upgrade target platform (Claude Code/OpenCode)')
|
|
1077
|
+
.option('--verbose', 'Show detailed output')
|
|
1078
|
+
.action(async (options) => {
|
|
1079
|
+
console.log(chalk.cyan.bold('ðŠ æ£æ¥æŽæ°\n'));
|
|
1080
|
+
|
|
1081
|
+
const detector = new StateDetector();
|
|
1082
|
+
const upgradeManager = new UpgradeManager();
|
|
1083
|
+
|
|
1084
|
+
const updates = await upgradeManager.checkUpdates();
|
|
1085
|
+
|
|
1086
|
+
if (!updates.flowUpdate && !updates.targetUpdate) {
|
|
1087
|
+
console.log(chalk.green('â ææç»ä»¶å·²æ¯ææ°çæ¬\n'));
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
if (updates.flowVersion) {
|
|
1092
|
+
console.log(`Sylphx Flow: ${updates.flowVersion.current} â ${chalk.green(updates.flowVersion.latest)}`);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
if (updates.targetVersion) {
|
|
1096
|
+
console.log(`${updates.targetVersion.current ? 'claude-code' : 'target'}: ${updates.targetVersion.current} â ${chalk.green(updates.targetVersion.latest)}`);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// Check only
|
|
1100
|
+
if (options.check) {
|
|
1101
|
+
console.log('\n' + chalk.dim('äœ¿çš --no-check æçç¥åæ°è¿è¡å级'));
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// Confirm upgrade
|
|
1106
|
+
const { default: inquirer } = await import('inquirer');
|
|
1107
|
+
const { confirm } = await inquirer.prompt([
|
|
1108
|
+
{
|
|
1109
|
+
type: 'confirm',
|
|
1110
|
+
name: 'confirm',
|
|
1111
|
+
message: '确讀åçº§å°ææ°çæ¬?',
|
|
1112
|
+
default: true,
|
|
1113
|
+
},
|
|
1114
|
+
]);
|
|
1115
|
+
|
|
1116
|
+
if (!confirm) {
|
|
1117
|
+
console.log(chalk.dim('\nåçº§å·²åæ¶'));
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// Perform upgrade
|
|
1122
|
+
console.log('');
|
|
1123
|
+
|
|
1124
|
+
const state = await detector.detect();
|
|
1125
|
+
|
|
1126
|
+
if (updates.flowUpdate) {
|
|
1127
|
+
console.log(chalk.cyan.bold('\nâ å级 Sylphx Flow\n'));
|
|
1128
|
+
await upgradeManager.upgradeFlow(state);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
if (updates.targetUpdate && options.target) {
|
|
1132
|
+
console.log(chalk.cyan.bold('\nâ å级 Target\n'));
|
|
1133
|
+
await upgradeManager.upgradeTarget(state);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
console.log(chalk.green('\nâ åçº§å®æ\n'));
|
|
1137
|
+
});
|