@sylphx/flow 2.1.3 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +44 -0
- package/package.json +79 -73
- package/src/commands/flow/execute-v2.ts +37 -29
- package/src/commands/flow/prompt.ts +5 -3
- package/src/commands/flow/types.ts +0 -2
- package/src/commands/flow-command.ts +20 -13
- package/src/commands/hook-command.ts +1 -3
- package/src/commands/settings-command.ts +36 -33
- package/src/config/ai-config.ts +60 -41
- package/src/core/agent-loader.ts +11 -6
- package/src/core/attach-manager.ts +92 -84
- package/src/core/backup-manager.ts +35 -29
- package/src/core/cleanup-handler.ts +11 -8
- package/src/core/error-handling.ts +23 -30
- package/src/core/flow-executor.ts +58 -76
- package/src/core/formatting/bytes.ts +2 -4
- package/src/core/functional/async.ts +5 -4
- package/src/core/functional/error-handler.ts +2 -2
- package/src/core/git-stash-manager.ts +21 -10
- package/src/core/installers/file-installer.ts +0 -1
- package/src/core/installers/mcp-installer.ts +0 -1
- package/src/core/project-manager.ts +24 -18
- package/src/core/secrets-manager.ts +54 -73
- package/src/core/session-manager.ts +20 -22
- package/src/core/state-detector.ts +139 -80
- package/src/core/template-loader.ts +13 -31
- package/src/core/upgrade-manager.ts +122 -69
- package/src/index.ts +8 -5
- package/src/services/auto-upgrade.ts +1 -1
- package/src/services/config-service.ts +41 -29
- package/src/services/global-config.ts +2 -2
- package/src/services/target-installer.ts +9 -7
- package/src/targets/claude-code.ts +24 -12
- package/src/targets/opencode.ts +17 -6
- package/src/types/cli.types.ts +2 -2
- package/src/types/provider.types.ts +1 -7
- package/src/types/session.types.ts +11 -11
- package/src/types/target.types.ts +3 -1
- package/src/types/todo.types.ts +1 -1
- package/src/types.ts +1 -1
- package/src/utils/__tests__/package-manager-detector.test.ts +6 -6
- package/src/utils/agent-enhancer.ts +4 -4
- package/src/utils/config/paths.ts +3 -1
- package/src/utils/config/target-utils.ts +2 -2
- package/src/utils/display/banner.ts +2 -2
- package/src/utils/display/notifications.ts +58 -45
- package/src/utils/display/status.ts +29 -12
- package/src/utils/files/file-operations.ts +1 -1
- package/src/utils/files/sync-utils.ts +38 -41
- package/src/utils/index.ts +19 -27
- package/src/utils/package-manager-detector.ts +15 -5
- package/src/utils/security/security.ts +8 -4
- package/src/utils/target-selection.ts +5 -2
- package/src/utils/version.ts +4 -2
- package/src/commands/flow-orchestrator.ts +0 -328
- package/src/commands/init-command.ts +0 -92
- package/src/commands/init-core.ts +0 -331
- package/src/core/agent-manager.ts +0 -174
- package/src/core/loop-controller.ts +0 -200
- package/src/core/rule-loader.ts +0 -147
- package/src/core/rule-manager.ts +0 -240
- package/src/services/claude-config-service.ts +0 -252
- package/src/services/first-run-setup.ts +0 -220
- package/src/services/smart-config-service.ts +0 -269
- package/src/types/api.types.ts +0 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @sylphx/flow
|
|
2
2
|
|
|
3
|
+
## 2.1.4 (2025-11-28)
|
|
4
|
+
|
|
5
|
+
### ♻️ Refactoring
|
|
6
|
+
|
|
7
|
+
- **flow:** eliminate hardcoded target checks with Target interface ([1dc75f9](https://github.com/SylphxAI/flow/commit/1dc75f9d4936b51554b1d09bf8576f832ce131e9))
|
|
8
|
+
|
|
9
|
+
### 🔧 Chores
|
|
10
|
+
|
|
11
|
+
- apply @sylphx/doctor fixes for 100% health score ([ae55969](https://github.com/SylphxAI/flow/commit/ae5596924dab48675ff3100b40f67651e7ebe26f))
|
|
12
|
+
- remove unused api.types.ts re-export file ([ad8f6a6](https://github.com/SylphxAI/flow/commit/ad8f6a6b8dcad75d2c0201f2286e52adccb728c7))
|
|
13
|
+
- remove dead code and unused modules ([6eaa904](https://github.com/SylphxAI/flow/commit/6eaa90438dcb40f9508953e874bf8c04204ae017))
|
|
14
|
+
|
|
3
15
|
## 2.1.3
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# @sylphx/flow
|
|
2
|
+
|
|
3
|
+
**One CLI to rule them all.**
|
|
4
|
+
|
|
5
|
+
Unified orchestration layer for Claude Code, OpenCode, Cursor and all AI development tools. Auto-detection, auto-installation, auto-upgrade.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @sylphx/flow
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Run with a prompt
|
|
17
|
+
sylphx-flow "your prompt here"
|
|
18
|
+
|
|
19
|
+
# Configure settings
|
|
20
|
+
sylphx-flow settings
|
|
21
|
+
|
|
22
|
+
# Check status
|
|
23
|
+
sylphx-flow status
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- **Auto-detect** - Automatically detects installed AI CLIs
|
|
29
|
+
- **Auto-install** - Installs missing dependencies on demand
|
|
30
|
+
- **Auto-upgrade** - Upgrades before each session
|
|
31
|
+
- **Unified settings** - One configuration for all tools
|
|
32
|
+
- **MEP system** - Minimal Effective Prompts for optimal context
|
|
33
|
+
|
|
34
|
+
## Documentation
|
|
35
|
+
|
|
36
|
+
See the [main repository](https://github.com/sylphxltd/flow) for full documentation.
|
|
37
|
+
|
|
38
|
+
## License
|
|
39
|
+
|
|
40
|
+
MIT License - see [LICENSE](../../LICENSE) file for details.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
Powered by [@sylphx](https://github.com/SylphxAI)
|
package/package.json
CHANGED
|
@@ -1,75 +1,81 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
2
|
+
"name": "@sylphx/flow",
|
|
3
|
+
"version": "2.1.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
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"sylphx-flow": "./src/index.ts"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./src/index.ts",
|
|
12
|
+
"types": "./src/index.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18.0.0"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "bun src/index.ts",
|
|
20
|
+
"start": "bun src/index.ts",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:watch": "vitest",
|
|
23
|
+
"type-check": "tsc --noEmit",
|
|
24
|
+
"prepublishOnly": "echo 'Using assets from packages/flow/assets'"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"commander": "^14.0.2",
|
|
28
|
+
"chalk": "^5.6.2",
|
|
29
|
+
"boxen": "^8.0.1",
|
|
30
|
+
"gradient-string": "^3.0.0",
|
|
31
|
+
"ora": "^9.0.0",
|
|
32
|
+
"inquirer": "^12.10.0",
|
|
33
|
+
"gray-matter": "^4.0.3",
|
|
34
|
+
"yaml": "^2.8.1",
|
|
35
|
+
"zod": "^4.1.12",
|
|
36
|
+
"debug": "^4.4.3"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^24.9.2",
|
|
40
|
+
"typescript": "^5.9.3",
|
|
41
|
+
"vitest": "^4.0.6"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"src",
|
|
48
|
+
"assets",
|
|
49
|
+
"README.md",
|
|
50
|
+
"CHANGELOG.md",
|
|
51
|
+
"LOOP_MODE.md",
|
|
52
|
+
"UPGRADE.md",
|
|
53
|
+
"package.json"
|
|
54
|
+
],
|
|
55
|
+
"keywords": [
|
|
56
|
+
"ai",
|
|
57
|
+
"automation",
|
|
58
|
+
"workflow",
|
|
59
|
+
"claude-code",
|
|
60
|
+
"opencode",
|
|
61
|
+
"cursor",
|
|
62
|
+
"cli",
|
|
63
|
+
"orchestration",
|
|
64
|
+
"unified",
|
|
65
|
+
"meta-layer",
|
|
66
|
+
"developer-tools",
|
|
67
|
+
"auto-install",
|
|
68
|
+
"auto-upgrade"
|
|
69
|
+
],
|
|
70
|
+
"repository": {
|
|
71
|
+
"type": "git",
|
|
72
|
+
"url": "https://github.com/sylphxltd/flow.git",
|
|
73
|
+
"directory": "packages/flow"
|
|
74
|
+
},
|
|
75
|
+
"bugs": {
|
|
76
|
+
"url": "https://github.com/sylphxltd/flow/issues"
|
|
77
|
+
},
|
|
78
|
+
"homepage": "https://github.com/sylphxltd/flow#readme",
|
|
79
|
+
"license": "MIT",
|
|
80
|
+
"author": "sylphxltd"
|
|
75
81
|
}
|
|
@@ -7,26 +7,22 @@ import chalk from 'chalk';
|
|
|
7
7
|
import inquirer from 'inquirer';
|
|
8
8
|
import { FlowExecutor } from '../../core/flow-executor.js';
|
|
9
9
|
import { targetManager } from '../../core/target-manager.js';
|
|
10
|
-
import {
|
|
10
|
+
import { AutoUpgrade } from '../../services/auto-upgrade.js';
|
|
11
|
+
import { GlobalConfigService } from '../../services/global-config.js';
|
|
12
|
+
import { TargetInstaller } from '../../services/target-installer.js';
|
|
13
|
+
import type { RunCommandOptions } from '../../types.js';
|
|
14
|
+
import { extractAgentInstructions, loadAgentContent } from '../../utils/agent-enhancer.js';
|
|
11
15
|
import { showWelcome } from '../../utils/display/banner.js';
|
|
12
|
-
import { loadAgentContent, extractAgentInstructions } from '../../utils/agent-enhancer.js';
|
|
13
16
|
import { CLIError } from '../../utils/error-handler.js';
|
|
14
|
-
import type { RunCommandOptions } from '../../types.js';
|
|
15
|
-
import type { FlowOptions } from './types.js';
|
|
16
|
-
import { resolvePrompt } from './prompt.js';
|
|
17
|
-
import { GlobalConfigService } from '../../services/global-config.js';
|
|
18
17
|
import { UserCancelledError } from '../../utils/errors.js';
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
18
|
+
import { ensureTargetInstalled, promptForTargetSelection } from '../../utils/target-selection.js';
|
|
19
|
+
import { resolvePrompt } from './prompt.js';
|
|
20
|
+
import type { FlowOptions } from './types.js';
|
|
22
21
|
|
|
23
22
|
/**
|
|
24
23
|
* Configure provider environment variables
|
|
25
24
|
*/
|
|
26
|
-
function configureProviderEnv(
|
|
27
|
-
provider: 'kimi' | 'zai',
|
|
28
|
-
apiKey: string
|
|
29
|
-
): void {
|
|
25
|
+
function configureProviderEnv(provider: 'kimi' | 'zai', apiKey: string): void {
|
|
30
26
|
const providerConfig = {
|
|
31
27
|
kimi: {
|
|
32
28
|
baseUrl: 'https://api.moonshot.cn/v1',
|
|
@@ -46,9 +42,7 @@ function configureProviderEnv(
|
|
|
46
42
|
/**
|
|
47
43
|
* Select and configure provider for Claude Code
|
|
48
44
|
*/
|
|
49
|
-
async function selectProvider(
|
|
50
|
-
configService: GlobalConfigService
|
|
51
|
-
): Promise<void> {
|
|
45
|
+
async function selectProvider(configService: GlobalConfigService): Promise<void> {
|
|
52
46
|
try {
|
|
53
47
|
const providerConfig = await configService.loadProviderConfig();
|
|
54
48
|
const defaultProvider = providerConfig.claudeCode.defaultProvider;
|
|
@@ -95,9 +89,10 @@ async function selectProvider(
|
|
|
95
89
|
} else {
|
|
96
90
|
console.log(chalk.green('✓ Using default Claude Code provider\n'));
|
|
97
91
|
}
|
|
98
|
-
} catch (error:
|
|
92
|
+
} catch (error: unknown) {
|
|
99
93
|
// Handle user cancellation (Ctrl+C)
|
|
100
|
-
|
|
94
|
+
const err = error as Error & { name?: string };
|
|
95
|
+
if (err.name === 'ExitPromptError' || err.message?.includes('force closed')) {
|
|
101
96
|
throw new UserCancelledError('Provider selection cancelled');
|
|
102
97
|
}
|
|
103
98
|
throw error;
|
|
@@ -107,7 +102,7 @@ async function selectProvider(
|
|
|
107
102
|
/**
|
|
108
103
|
* Execute command using target's executeCommand method
|
|
109
104
|
*/
|
|
110
|
-
|
|
105
|
+
function executeTargetCommand(
|
|
111
106
|
targetId: string,
|
|
112
107
|
systemPrompt: string,
|
|
113
108
|
userPrompt: string,
|
|
@@ -173,7 +168,11 @@ export async function executeFlowV2(
|
|
|
173
168
|
);
|
|
174
169
|
|
|
175
170
|
const installation = targetInstaller.getInstallationInfo(selectedTargetId);
|
|
176
|
-
const installed = await ensureTargetInstalled(
|
|
171
|
+
const installed = await ensureTargetInstalled(
|
|
172
|
+
selectedTargetId,
|
|
173
|
+
targetInstaller,
|
|
174
|
+
installedTargets
|
|
175
|
+
);
|
|
177
176
|
|
|
178
177
|
if (!installed) {
|
|
179
178
|
process.exit(1);
|
|
@@ -200,7 +199,11 @@ export async function executeFlowV2(
|
|
|
200
199
|
);
|
|
201
200
|
|
|
202
201
|
const installation = targetInstaller.getInstallationInfo(selectedTargetId);
|
|
203
|
-
const installed = await ensureTargetInstalled(
|
|
202
|
+
const installed = await ensureTargetInstalled(
|
|
203
|
+
selectedTargetId,
|
|
204
|
+
targetInstaller,
|
|
205
|
+
installedTargets
|
|
206
|
+
);
|
|
204
207
|
|
|
205
208
|
if (!installed) {
|
|
206
209
|
process.exit(1);
|
|
@@ -225,7 +228,11 @@ export async function executeFlowV2(
|
|
|
225
228
|
|
|
226
229
|
if (!installed) {
|
|
227
230
|
// Installation failed - show error and exit
|
|
228
|
-
console.log(
|
|
231
|
+
console.log(
|
|
232
|
+
chalk.red(
|
|
233
|
+
`\n✗ Cannot proceed: ${installation?.name} is not installed and auto-install failed`
|
|
234
|
+
)
|
|
235
|
+
);
|
|
229
236
|
console.log(chalk.yellow(' Please either:'));
|
|
230
237
|
console.log(chalk.cyan(' 1. Install manually (see instructions above)'));
|
|
231
238
|
console.log(chalk.cyan(' 2. Change default target: sylphx-flow settings\n'));
|
|
@@ -242,16 +249,20 @@ export async function executeFlowV2(
|
|
|
242
249
|
|
|
243
250
|
// Mode info
|
|
244
251
|
if (options.merge) {
|
|
245
|
-
console.log(
|
|
252
|
+
console.log(
|
|
253
|
+
chalk.cyan('🔗 Merge mode: Flow settings will be merged with your existing settings')
|
|
254
|
+
);
|
|
246
255
|
console.log(chalk.dim(' Settings will be restored after execution\n'));
|
|
247
256
|
} else {
|
|
248
|
-
console.log(
|
|
257
|
+
console.log(
|
|
258
|
+
chalk.yellow('🔄 Replace mode (default): All settings will use Flow configuration')
|
|
259
|
+
);
|
|
249
260
|
console.log(chalk.dim(' Use --merge to keep your existing settings\n'));
|
|
250
261
|
}
|
|
251
262
|
|
|
252
263
|
// Create executor
|
|
253
264
|
const executor = new FlowExecutor();
|
|
254
|
-
const
|
|
265
|
+
const _projectManager = executor.getProjectManager();
|
|
255
266
|
|
|
256
267
|
// Step 2: Execute attach mode lifecycle
|
|
257
268
|
try {
|
|
@@ -361,10 +372,7 @@ export async function executeFlowV2(
|
|
|
361
372
|
/**
|
|
362
373
|
* Main flow execution entry point
|
|
363
374
|
*/
|
|
364
|
-
export async function executeFlow(
|
|
365
|
-
prompt: string | undefined,
|
|
366
|
-
options: FlowOptions
|
|
367
|
-
): Promise<void> {
|
|
375
|
+
export async function executeFlow(prompt: string | undefined, options: FlowOptions): Promise<void> {
|
|
368
376
|
// Resolve prompt (handle file input)
|
|
369
377
|
const resolvedPrompt = await resolvePrompt(prompt);
|
|
370
378
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Handle file input (@file.txt) and prompt loading
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import path from 'node:path';
|
|
7
6
|
import fs from 'node:fs/promises';
|
|
7
|
+
import path from 'node:path';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -12,7 +12,9 @@ import chalk from 'chalk';
|
|
|
12
12
|
* Supports @filename syntax: @prompt.txt or @/path/to/prompt.txt
|
|
13
13
|
*/
|
|
14
14
|
export async function resolvePrompt(prompt: string | undefined): Promise<string | undefined> {
|
|
15
|
-
if (!prompt)
|
|
15
|
+
if (!prompt) {
|
|
16
|
+
return prompt;
|
|
17
|
+
}
|
|
16
18
|
|
|
17
19
|
// Check for file input syntax: @filename
|
|
18
20
|
if (prompt.startsWith('@')) {
|
|
@@ -26,7 +28,7 @@ export async function resolvePrompt(prompt: string | undefined): Promise<string
|
|
|
26
28
|
const content = await fs.readFile(resolvedPath, 'utf-8');
|
|
27
29
|
console.log(chalk.dim(` ✓ Loaded prompt from: ${filePath}\n`));
|
|
28
30
|
return content.trim();
|
|
29
|
-
} catch (
|
|
31
|
+
} catch (_error) {
|
|
30
32
|
throw new Error(`Failed to read prompt file: ${filePath}`);
|
|
31
33
|
}
|
|
32
34
|
}
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* Entry point for all flow-related CLI commands
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Command } from 'commander';
|
|
7
|
-
import chalk from 'chalk';
|
|
8
|
-
import path from 'node:path';
|
|
9
6
|
import fs from 'node:fs/promises';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { Command } from 'commander';
|
|
10
10
|
import { StateDetector } from '../core/state-detector.js';
|
|
11
11
|
import { UpgradeManager } from '../core/upgrade-manager.js';
|
|
12
12
|
import { showWelcome } from '../utils/display/banner.js';
|
|
@@ -30,7 +30,10 @@ export const flowCommand = new Command('flow')
|
|
|
30
30
|
.option('--merge', 'Merge Flow settings with existing settings (default: replace all)')
|
|
31
31
|
|
|
32
32
|
// Prompt argument
|
|
33
|
-
.argument(
|
|
33
|
+
.argument(
|
|
34
|
+
'[prompt]',
|
|
35
|
+
'Prompt to execute with agent (optional, supports @file.txt for file input)'
|
|
36
|
+
)
|
|
34
37
|
|
|
35
38
|
.action(async (prompt, options: FlowOptions) => {
|
|
36
39
|
await executeFlow(prompt, options);
|
|
@@ -41,7 +44,7 @@ export const flowCommand = new Command('flow')
|
|
|
41
44
|
*/
|
|
42
45
|
export const setupCommand = new Command('setup')
|
|
43
46
|
.description('[DEPRECATED] No longer needed - Flow uses automatic attach mode')
|
|
44
|
-
.action(
|
|
47
|
+
.action(() => {
|
|
45
48
|
console.log(chalk.yellow('⚠️ The "setup" command is deprecated.\n'));
|
|
46
49
|
console.log(chalk.cyan('Flow now uses automatic attach mode:'));
|
|
47
50
|
console.log(chalk.dim(' • No installation needed'));
|
|
@@ -118,23 +121,23 @@ export const doctorCommand = new Command('doctor')
|
|
|
118
121
|
await executeFlow(undefined, { sync: true } as FlowOptions);
|
|
119
122
|
console.log(chalk.green(' ✓ 已修复'));
|
|
120
123
|
}
|
|
121
|
-
} else if (
|
|
124
|
+
} else if (state.initialized) {
|
|
125
|
+
console.log(chalk.green(' ✓ 配置正常'));
|
|
126
|
+
} else {
|
|
122
127
|
console.log(chalk.yellow(' ⚠ 项目未初始化'));
|
|
123
128
|
issuesFound = true;
|
|
124
|
-
} else {
|
|
125
|
-
console.log(chalk.green(' ✓ 配置正常'));
|
|
126
129
|
}
|
|
127
130
|
|
|
128
131
|
// Check 3: Components
|
|
129
132
|
console.log('\n检查组件...');
|
|
130
133
|
Object.entries(state.components).forEach(([name, component]) => {
|
|
131
134
|
const status = component.installed ? chalk.green('✓') : chalk.red('✗');
|
|
132
|
-
const count =
|
|
135
|
+
const count = 'count' in component && component.count ? ` (${component.count})` : '';
|
|
133
136
|
console.log(` ${status} ${name}${count}`);
|
|
134
137
|
});
|
|
135
138
|
|
|
136
139
|
// Summary
|
|
137
|
-
console.log(
|
|
140
|
+
console.log(`\n${chalk.bold('结果:')}`);
|
|
138
141
|
if (!issuesFound) {
|
|
139
142
|
console.log(chalk.green('✓ 所有检查通过'));
|
|
140
143
|
} else if (options.fix) {
|
|
@@ -168,16 +171,20 @@ export const upgradeCommand = new Command('upgrade')
|
|
|
168
171
|
}
|
|
169
172
|
|
|
170
173
|
if (updates.flowVersion) {
|
|
171
|
-
console.log(
|
|
174
|
+
console.log(
|
|
175
|
+
`Sylphx Flow: ${updates.flowVersion.current} → ${chalk.green(updates.flowVersion.latest)}`
|
|
176
|
+
);
|
|
172
177
|
}
|
|
173
178
|
|
|
174
179
|
if (updates.targetVersion) {
|
|
175
|
-
console.log(
|
|
180
|
+
console.log(
|
|
181
|
+
`${updates.targetVersion.current ? 'claude-code' : 'target'}: ${updates.targetVersion.current} → ${chalk.green(updates.targetVersion.latest)}`
|
|
182
|
+
);
|
|
176
183
|
}
|
|
177
184
|
|
|
178
185
|
// Check only
|
|
179
186
|
if (options.check) {
|
|
180
|
-
console.log(
|
|
187
|
+
console.log(`\n${chalk.dim('Run without --check to upgrade')}`);
|
|
181
188
|
return;
|
|
182
189
|
}
|
|
183
190
|
|
|
@@ -153,9 +153,7 @@ async function sendLinuxNotification(title: string, message: string): Promise<vo
|
|
|
153
153
|
try {
|
|
154
154
|
await execAsync('which notify-send');
|
|
155
155
|
// Use Flow-themed spiral emoji as icon for Sylphx Flow
|
|
156
|
-
await execAsync(
|
|
157
|
-
`notify-send -i "🌀" "${escapeForShell(title)}" "${escapeForShell(message)}"`
|
|
158
|
-
);
|
|
156
|
+
await execAsync(`notify-send -i "🌀" "${escapeForShell(title)}" "${escapeForShell(message)}"`);
|
|
159
157
|
} catch {
|
|
160
158
|
// notify-send not available, skip notification silently
|
|
161
159
|
}
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* Interactive configuration for Sylphx Flow
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Command } from 'commander';
|
|
7
6
|
import chalk from 'chalk';
|
|
7
|
+
import { Command } from 'commander';
|
|
8
8
|
import inquirer from 'inquirer';
|
|
9
9
|
import { GlobalConfigService } from '../services/global-config.js';
|
|
10
|
-
import { UserCancelledError } from '../utils/errors.js';
|
|
11
10
|
import { TargetInstaller } from '../services/target-installer.js';
|
|
12
|
-
import {
|
|
11
|
+
import { UserCancelledError } from '../utils/errors.js';
|
|
12
|
+
import { buildAvailableTargets, promptForDefaultTarget } from '../utils/target-selection.js';
|
|
13
13
|
|
|
14
14
|
export const settingsCommand = new Command('settings')
|
|
15
15
|
.description('Configure Sylphx Flow settings')
|
|
@@ -30,9 +30,10 @@ export const settingsCommand = new Command('settings')
|
|
|
30
30
|
} else {
|
|
31
31
|
await showMainMenu(configService);
|
|
32
32
|
}
|
|
33
|
-
} catch (error:
|
|
33
|
+
} catch (error: unknown) {
|
|
34
34
|
// Handle user cancellation (Ctrl+C)
|
|
35
|
-
|
|
35
|
+
const err = error as Error & { name?: string };
|
|
36
|
+
if (err.name === 'ExitPromptError' || err.message?.includes('force closed')) {
|
|
36
37
|
throw new UserCancelledError('Settings cancelled by user');
|
|
37
38
|
}
|
|
38
39
|
throw error;
|
|
@@ -123,9 +124,7 @@ async function configureAgents(configService: GlobalConfigService): Promise<void
|
|
|
123
124
|
};
|
|
124
125
|
|
|
125
126
|
// Get current enabled agents
|
|
126
|
-
const currentEnabled = Object.keys(currentAgents).filter(
|
|
127
|
-
(key) => currentAgents[key].enabled
|
|
128
|
-
);
|
|
127
|
+
const currentEnabled = Object.keys(currentAgents).filter((key) => currentAgents[key].enabled);
|
|
129
128
|
|
|
130
129
|
const { selectedAgents } = await inquirer.prompt([
|
|
131
130
|
{
|
|
@@ -191,9 +190,7 @@ async function configureRules(configService: GlobalConfigService): Promise<void>
|
|
|
191
190
|
};
|
|
192
191
|
|
|
193
192
|
// Get current enabled rules
|
|
194
|
-
const currentEnabled = Object.keys(currentRules).filter(
|
|
195
|
-
(key) => currentRules[key].enabled
|
|
196
|
-
);
|
|
193
|
+
const currentEnabled = Object.keys(currentRules).filter((key) => currentRules[key].enabled);
|
|
197
194
|
|
|
198
195
|
const { selectedRules } = await inquirer.prompt([
|
|
199
196
|
{
|
|
@@ -239,9 +236,7 @@ async function configureOutputStyles(configService: GlobalConfigService): Promis
|
|
|
239
236
|
};
|
|
240
237
|
|
|
241
238
|
// Get current enabled styles
|
|
242
|
-
const currentEnabled = Object.keys(currentStyles).filter(
|
|
243
|
-
(key) => currentStyles[key].enabled
|
|
244
|
-
);
|
|
239
|
+
const currentEnabled = Object.keys(currentStyles).filter((key) => currentStyles[key].enabled);
|
|
245
240
|
|
|
246
241
|
const { selectedStyles } = await inquirer.prompt([
|
|
247
242
|
{
|
|
@@ -283,17 +278,15 @@ async function configureMCP(configService: GlobalConfigService): Promise<void> {
|
|
|
283
278
|
|
|
284
279
|
// Available MCP servers (from MCP_SERVER_REGISTRY)
|
|
285
280
|
const availableServers = {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
281
|
+
grep: { name: 'GitHub Code Search (grep.app)', requiresEnv: [] },
|
|
282
|
+
context7: { name: 'Context7 Docs', requiresEnv: [] },
|
|
283
|
+
playwright: { name: 'Playwright Browser Control', requiresEnv: [] },
|
|
284
|
+
github: { name: 'GitHub', requiresEnv: ['GITHUB_TOKEN'] },
|
|
285
|
+
notion: { name: 'Notion', requiresEnv: ['NOTION_API_KEY'] },
|
|
291
286
|
};
|
|
292
287
|
|
|
293
288
|
// Get current enabled servers
|
|
294
|
-
const currentEnabled = Object.keys(currentServers).filter(
|
|
295
|
-
(key) => currentServers[key].enabled
|
|
296
|
-
);
|
|
289
|
+
const currentEnabled = Object.keys(currentServers).filter((key) => currentServers[key].enabled);
|
|
297
290
|
|
|
298
291
|
const { selectedServers } = await inquirer.prompt([
|
|
299
292
|
{
|
|
@@ -301,9 +294,10 @@ async function configureMCP(configService: GlobalConfigService): Promise<void> {
|
|
|
301
294
|
name: 'selectedServers',
|
|
302
295
|
message: 'Select MCP servers to enable:',
|
|
303
296
|
choices: Object.entries(availableServers).map(([key, info]) => {
|
|
304
|
-
const requiresText =
|
|
305
|
-
|
|
306
|
-
|
|
297
|
+
const requiresText =
|
|
298
|
+
info.requiresEnv.length > 0
|
|
299
|
+
? chalk.dim(` (requires ${info.requiresEnv.join(', ')})`)
|
|
300
|
+
: '';
|
|
307
301
|
return {
|
|
308
302
|
name: `${info.name}${requiresText}`,
|
|
309
303
|
value: key,
|
|
@@ -316,10 +310,10 @@ async function configureMCP(configService: GlobalConfigService): Promise<void> {
|
|
|
316
310
|
// Update servers
|
|
317
311
|
for (const key of Object.keys(availableServers)) {
|
|
318
312
|
if (selectedServers.includes(key)) {
|
|
319
|
-
if (
|
|
320
|
-
currentServers[key] = { enabled: true, env: {} };
|
|
321
|
-
} else {
|
|
313
|
+
if (currentServers[key]) {
|
|
322
314
|
currentServers[key].enabled = true;
|
|
315
|
+
} else {
|
|
316
|
+
currentServers[key] = { enabled: true, env: {} };
|
|
323
317
|
}
|
|
324
318
|
} else if (currentServers[key]) {
|
|
325
319
|
currentServers[key].enabled = false;
|
|
@@ -333,7 +327,7 @@ async function configureMCP(configService: GlobalConfigService): Promise<void> {
|
|
|
333
327
|
const server = currentServers[serverKey];
|
|
334
328
|
|
|
335
329
|
for (const envKey of serverInfo.requiresEnv) {
|
|
336
|
-
const hasKey = server.env
|
|
330
|
+
const hasKey = server.env?.[envKey];
|
|
337
331
|
|
|
338
332
|
const { shouldConfigure } = await inquirer.prompt([
|
|
339
333
|
{
|
|
@@ -406,7 +400,9 @@ async function configureProvider(configService: GlobalConfigService): Promise<vo
|
|
|
406
400
|
{
|
|
407
401
|
type: 'confirm',
|
|
408
402
|
name: 'shouldConfigure',
|
|
409
|
-
message: currentKey
|
|
403
|
+
message: currentKey
|
|
404
|
+
? `Update ${defaultProvider} API key?`
|
|
405
|
+
: `Configure ${defaultProvider} API key?`,
|
|
410
406
|
default: !currentKey,
|
|
411
407
|
},
|
|
412
408
|
]);
|
|
@@ -424,8 +420,11 @@ async function configureProvider(configService: GlobalConfigService): Promise<vo
|
|
|
424
420
|
if (!providerConfig.claudeCode.providers[defaultProvider]) {
|
|
425
421
|
providerConfig.claudeCode.providers[defaultProvider] = { enabled: true };
|
|
426
422
|
}
|
|
427
|
-
providerConfig.claudeCode.providers[defaultProvider]
|
|
428
|
-
|
|
423
|
+
const provider = providerConfig.claudeCode.providers[defaultProvider];
|
|
424
|
+
if (provider) {
|
|
425
|
+
provider.apiKey = apiKey;
|
|
426
|
+
provider.enabled = true;
|
|
427
|
+
}
|
|
429
428
|
}
|
|
430
429
|
}
|
|
431
430
|
|
|
@@ -450,7 +449,11 @@ async function configureTarget(configService: GlobalConfigService): Promise<void
|
|
|
450
449
|
|
|
451
450
|
const defaultTarget = await promptForDefaultTarget(installedTargets, settings.defaultTarget);
|
|
452
451
|
|
|
453
|
-
settings.defaultTarget = defaultTarget as
|
|
452
|
+
settings.defaultTarget = defaultTarget as
|
|
453
|
+
| 'claude-code'
|
|
454
|
+
| 'opencode'
|
|
455
|
+
| 'cursor'
|
|
456
|
+
| 'ask-every-time';
|
|
454
457
|
await configService.saveSettings(settings);
|
|
455
458
|
|
|
456
459
|
if (defaultTarget === 'ask-every-time') {
|