@sylphx/flow 3.15.0 → 3.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # @sylphx/flow
2
2
 
3
+ ## 3.17.0 (2026-02-05)
4
+
5
+ ### ♻️ Refactoring
6
+
7
+ - **flow:** migrate CLI prompts to Clack and logging to Pino ([08aceb7](https://github.com/SylphxAI/flow/commit/08aceb7d))
8
+
9
+ ### 🐛 Bug Fixes
10
+
11
+ - **flow:** remove unsafe separator pattern from Clack prompts ([aaf17a7](https://github.com/SylphxAI/flow/commit/aaf17a7d))
12
+
13
+ ### 📝 Documentation
14
+
15
+ - **builder:** add Zod v4 to tech stack ([2b37327](https://github.com/SylphxAI/flow/commit/2b373279))
16
+ - **builder:** restructure tech stack - Zod as standalone category ([90875ba](https://github.com/SylphxAI/flow/commit/90875baa))
17
+
18
+ ## 3.16.0 (2026-02-05)
19
+
20
+ ### ✨ Features
21
+
22
+ - **builder:** add Ink and Clack for CLI apps ([e9c3f15](https://github.com/SylphxAI/flow/commit/e9c3f1569f61bf9523c51afee31092584edf0db3))
23
+ - **builder:** add Pino logging to tech stack ([76c9893](https://github.com/SylphxAI/flow/commit/76c98934c0926676e01705bae5ac719cc8bdd3d8))
24
+ - **builder:** expand tech stack with modern React libraries ([af758b0](https://github.com/SylphxAI/flow/commit/af758b0847f450349a192385ccc2d466e0ce1a7f))
25
+
26
+ ### 🐛 Bug Fixes
27
+
28
+ - **builder:** use Pragmatic Drag and Drop over dnd-kit ([75db518](https://github.com/SylphxAI/flow/commit/75db518f392ebebce65fa59739be03a983a0ed95))
29
+
30
+ ### ♻️ Refactoring
31
+
32
+ - **builder:** consolidate tech stack to single best choice per category ([b149175](https://github.com/SylphxAI/flow/commit/b149175889c7838bf8f0a56459c0bd9ca811d01f))
33
+ - **builder:** replace Radix UI with Base UI in tech stack ([a4e81df](https://github.com/SylphxAI/flow/commit/a4e81dfcd382030e99c49fe60082aafe033e3981))
34
+
3
35
  ## 3.15.0 (2026-02-05)
4
36
 
5
37
  ### ✨ Features
@@ -46,13 +46,15 @@ State-of-the-art industrial standard. Every time. Would you stake your reputatio
46
46
 
47
47
  **Framework & Runtime:** Next.js 16+, React, Bun
48
48
 
49
+ **Schema & Validation:** Zod v4
50
+
49
51
  **Data & API:** Hono + @hono/zod-openapi + hc (type-safe client), React Query, Drizzle ORM
50
52
 
51
53
  **Database & Infrastructure:** Neon PostgreSQL, Upstash Workflow, Vercel, Vercel Blob, Modal (serverless long-running)
52
54
 
53
55
  **UI & Styling:** Base UI, Tailwind CSS v4 (CSS-first), Motion v12 (animation)
54
56
 
55
- **Forms:** React Hook Form + Zod
57
+ **Forms:** React Hook Form + @hookform/resolvers
56
58
 
57
59
  **Tables & Lists:** TanStack Table, TanStack Virtual
58
60
 
@@ -62,6 +64,8 @@ State-of-the-art industrial standard. Every time. Would you stake your reputatio
62
64
 
63
65
  **Logging:** Pino
64
66
 
67
+ **CLI Apps:** Ink (React CLI), Clack (prompts)
68
+
65
69
  **AI:** AI SDK v6+
66
70
 
67
71
  **Auth & Services:** Better Auth, Resend (email)
package/package.json CHANGED
@@ -1,81 +1,82 @@
1
1
  {
2
- "name": "@sylphx/flow",
3
- "version": "3.15.0",
4
- "description": "One CLI to rule them all. Unified orchestration layer for AI coding assistants. Auto-detection, auto-installation, auto-upgrade.",
5
- "type": "module",
6
- "bin": {
7
- "sylphx-flow": "./src/index.ts",
8
- "flow": "./src/index.ts"
9
- },
10
- "exports": {
11
- ".": {
12
- "import": "./src/index.ts",
13
- "types": "./src/index.ts"
14
- }
15
- },
16
- "engines": {
17
- "node": ">=18.0.0"
18
- },
19
- "scripts": {
20
- "dev": "bun src/index.ts",
21
- "start": "bun src/index.ts",
22
- "test": "bun test",
23
- "test:watch": "bun test --watch",
24
- "type-check": "tsc --noEmit",
25
- "prepublishOnly": "echo 'Using assets from packages/flow/assets'"
26
- },
27
- "dependencies": {
28
- "commander": "^14.0.2",
29
- "chalk": "^5.6.2",
30
- "boxen": "^8.0.1",
31
- "gradient-string": "^3.0.0",
32
- "ora": "^9.0.0",
33
- "inquirer": "^12.10.0",
34
- "gray-matter": "^4.0.3",
35
- "yaml": "^2.8.1",
36
- "zod": "^4.1.12",
37
- "debug": "^4.4.3"
38
- },
39
- "devDependencies": {
40
- "@types/node": "^24.9.2",
41
- "typescript": "^5.9.3"
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"
2
+ "name": "@sylphx/flow",
3
+ "version": "3.17.0",
4
+ "description": "One CLI to rule them all. Unified orchestration layer for AI coding assistants. Auto-detection, auto-installation, auto-upgrade.",
5
+ "type": "module",
6
+ "bin": {
7
+ "sylphx-flow": "./src/index.ts",
8
+ "flow": "./src/index.ts"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "import": "./src/index.ts",
13
+ "types": "./src/index.ts"
14
+ }
15
+ },
16
+ "engines": {
17
+ "node": ">=18.0.0"
18
+ },
19
+ "scripts": {
20
+ "dev": "bun src/index.ts",
21
+ "start": "bun src/index.ts",
22
+ "test": "bun test",
23
+ "test:watch": "bun test --watch",
24
+ "type-check": "tsc --noEmit",
25
+ "prepublishOnly": "echo 'Using assets from packages/flow/assets'"
26
+ },
27
+ "dependencies": {
28
+ "@clack/prompts": "^0.9.0",
29
+ "boxen": "^8.0.1",
30
+ "chalk": "^5.6.2",
31
+ "commander": "^14.0.2",
32
+ "debug": "^4.4.3",
33
+ "gradient-string": "^3.0.0",
34
+ "gray-matter": "^4.0.3",
35
+ "pino": "^9.0.0",
36
+ "pino-pretty": "^11.0.0",
37
+ "yaml": "^2.8.1",
38
+ "zod": "^4.1.12"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^24.9.2",
42
+ "typescript": "^5.9.3"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "files": [
48
+ "src",
49
+ "assets",
50
+ "README.md",
51
+ "CHANGELOG.md",
52
+ "LOOP_MODE.md",
53
+ "UPGRADE.md",
54
+ "package.json"
55
+ ],
56
+ "keywords": [
57
+ "ai",
58
+ "automation",
59
+ "workflow",
60
+ "claude-code",
61
+ "opencode",
62
+ "cursor",
63
+ "cli",
64
+ "orchestration",
65
+ "unified",
66
+ "meta-layer",
67
+ "developer-tools",
68
+ "auto-install",
69
+ "auto-upgrade"
70
+ ],
71
+ "repository": {
72
+ "type": "git",
73
+ "url": "https://github.com/sylphxltd/flow.git",
74
+ "directory": "packages/flow"
75
+ },
76
+ "bugs": {
77
+ "url": "https://github.com/sylphxltd/flow/issues"
78
+ },
79
+ "homepage": "https://github.com/sylphxltd/flow#readme",
80
+ "license": "MIT",
81
+ "author": "sylphxltd"
81
82
  }
@@ -7,7 +7,6 @@ import fs from 'node:fs/promises';
7
7
  import path from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import chalk from 'chalk';
10
- import inquirer from 'inquirer';
11
10
  import { FlowExecutor } from '../../core/flow-executor.js';
12
11
  import { targetManager } from '../../core/target-manager.js';
13
12
  import { AutoUpgrade } from '../../services/auto-upgrade.js';
@@ -18,6 +17,7 @@ import { extractAgentInstructions, loadAgentContent } from '../../utils/agent-en
18
17
  import { showAttachSummary, showHeader } from '../../utils/display/banner.js';
19
18
  import { CLIError } from '../../utils/error-handler.js';
20
19
  import { UserCancelledError } from '../../utils/errors.js';
20
+ import { log, promptConfirm, promptSelect } from '../../utils/prompts/index.js';
21
21
  import { ensureTargetInstalled, promptForTargetSelection } from '../../utils/target-selection.js';
22
22
  import { resolvePrompt } from './prompt.js';
23
23
  import type { FlowOptions } from './types.js';
@@ -62,66 +62,52 @@ function configureProviderEnv(provider: 'kimi' | 'zai', apiKey: string): void {
62
62
  * Select and configure provider for Claude Code (silent unless prompting)
63
63
  */
64
64
  async function selectProvider(configService: GlobalConfigService): Promise<void> {
65
- try {
66
- const providerConfig = await configService.loadProviderConfig();
67
- const defaultProvider = providerConfig.claudeCode.defaultProvider;
68
-
69
- // If not "ask-every-time", use the default provider silently
70
- if (defaultProvider !== 'ask-every-time') {
71
- if (defaultProvider === 'kimi' || defaultProvider === 'zai') {
72
- const provider = providerConfig.claudeCode.providers[defaultProvider];
73
- if (provider?.apiKey) {
74
- configureProviderEnv(defaultProvider, provider.apiKey);
75
- }
65
+ const providerConfig = await configService.loadProviderConfig();
66
+ const defaultProvider = providerConfig.claudeCode.defaultProvider;
67
+
68
+ // If not "ask-every-time", use the default provider silently
69
+ if (defaultProvider !== 'ask-every-time') {
70
+ if (defaultProvider === 'kimi' || defaultProvider === 'zai') {
71
+ const provider = providerConfig.claudeCode.providers[defaultProvider];
72
+ if (provider?.apiKey) {
73
+ configureProviderEnv(defaultProvider, provider.apiKey);
76
74
  }
77
- return;
78
- }
79
-
80
- // Ask user which provider to use for this session
81
- const { selectedProvider, rememberChoice } = await inquirer.prompt([
82
- {
83
- type: 'list',
84
- name: 'selectedProvider',
85
- message: 'Select provider:',
86
- choices: [
87
- { name: 'Default (Claude Code built-in)', value: 'default' },
88
- { name: 'Kimi', value: 'kimi' },
89
- { name: 'Z.ai', value: 'zai' },
90
- ],
91
- default: 'default',
92
- },
93
- {
94
- type: 'confirm',
95
- name: 'rememberChoice',
96
- message: 'Remember this choice?',
97
- default: true,
98
- },
99
- ]);
100
-
101
- // Save choice if user wants to remember
102
- if (rememberChoice) {
103
- providerConfig.claudeCode.defaultProvider = selectedProvider;
104
- await configService.saveProviderConfig(providerConfig);
105
75
  }
76
+ return;
77
+ }
106
78
 
107
- // Configure environment variables based on selection
108
- if (selectedProvider === 'kimi' || selectedProvider === 'zai') {
109
- const provider = providerConfig.claudeCode.providers[selectedProvider];
79
+ // Ask user which provider to use for this session
80
+ const selectedProvider = await promptSelect({
81
+ message: 'Select provider:',
82
+ options: [
83
+ { label: 'Default (Claude Code built-in)', value: 'default' },
84
+ { label: 'Kimi', value: 'kimi' },
85
+ { label: 'Z.ai', value: 'zai' },
86
+ ],
87
+ initialValue: 'default',
88
+ });
89
+
90
+ const rememberChoice = await promptConfirm({
91
+ message: 'Remember this choice?',
92
+ initialValue: true,
93
+ });
94
+
95
+ // Save choice if user wants to remember
96
+ if (rememberChoice) {
97
+ providerConfig.claudeCode.defaultProvider = selectedProvider;
98
+ await configService.saveProviderConfig(providerConfig);
99
+ }
110
100
 
111
- if (!provider?.apiKey) {
112
- console.log(chalk.yellow(' API key not configured. Use: sylphx-flow settings'));
113
- return;
114
- }
101
+ // Configure environment variables based on selection
102
+ if (selectedProvider === 'kimi' || selectedProvider === 'zai') {
103
+ const provider = providerConfig.claudeCode.providers[selectedProvider];
115
104
 
116
- configureProviderEnv(selectedProvider, provider.apiKey);
117
- }
118
- } catch (error: unknown) {
119
- // Handle user cancellation (Ctrl+C)
120
- const err = error as Error & { name?: string };
121
- if (err.name === 'ExitPromptError' || err.message?.includes('force closed')) {
122
- throw new UserCancelledError('Provider selection cancelled');
105
+ if (!provider?.apiKey) {
106
+ log.warn('API key not configured. Use: sylphx-flow settings');
107
+ return;
123
108
  }
124
- throw error;
109
+
110
+ configureProviderEnv(selectedProvider, provider.apiKey);
125
111
  }
126
112
  }
127
113
 
@@ -221,11 +207,11 @@ export async function executeFlowV2(
221
207
 
222
208
  if (!installedTargets.includes(selectedTargetId)) {
223
209
  const installation = targetInstaller.getInstallationInfo(selectedTargetId);
224
- console.log(chalk.yellow(`\n ${installation?.name} not installed`));
210
+ log.warn(`${installation?.name} not installed`);
225
211
  const installed = await targetInstaller.install(selectedTargetId, true);
226
212
 
227
213
  if (!installed) {
228
- console.log(chalk.red(` Cannot proceed: installation failed\n`));
214
+ log.error('Cannot proceed: installation failed');
229
215
  process.exit(1);
230
216
  }
231
217
  }
@@ -313,7 +299,7 @@ export async function executeFlowV2(
313
299
  } catch (error) {
314
300
  // Handle user cancellation gracefully
315
301
  if (error instanceof UserCancelledError) {
316
- console.log(chalk.yellow('\n Cancelled'));
302
+ log.warn('Cancelled');
317
303
  try {
318
304
  await executor.cleanup(projectPath);
319
305
  } catch {
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import chalk from 'chalk';
7
- import inquirer from 'inquirer';
7
+ import { log, type MultiselectOption, promptMultiselect } from '../../utils/prompts/index.js';
8
8
 
9
9
  // ============================================================================
10
10
  // Types
@@ -47,16 +47,16 @@ export const getEnabledKeys = (config: ConfigMap): string[] =>
47
47
  Object.keys(config).filter((key) => config[key]?.enabled);
48
48
 
49
49
  /**
50
- * Build checkbox choices from available items
50
+ * Build multiselect options from available items
51
51
  */
52
- export const buildChoices = <T extends string>(
52
+ export const buildOptions = <T extends string>(
53
53
  available: Record<T, string>,
54
54
  enabledKeys: string[]
55
- ): Array<{ name: string; value: T; checked: boolean }> =>
55
+ ): MultiselectOption<T>[] =>
56
56
  Object.entries(available).map(([key, name]) => ({
57
- name: name as string,
57
+ label: name as string,
58
58
  value: key as T,
59
- checked: enabledKeys.includes(key),
59
+ hint: enabledKeys.includes(key) ? 'enabled' : undefined,
60
60
  }));
61
61
 
62
62
  /**
@@ -82,11 +82,11 @@ export const printHeader = (icon: string, title: string): void => {
82
82
  };
83
83
 
84
84
  /**
85
- * Print confirmation message
85
+ * Print confirmation message using Clack log
86
86
  */
87
87
  export const printConfirmation = (itemType: string, count: number): void => {
88
- console.log(chalk.green(`\n✓ ${itemType} configuration saved`));
89
- console.log(chalk.dim(` Enabled ${itemType.toLowerCase()}: ${count}`));
88
+ log.success(`${itemType} configuration saved`);
89
+ log.info(`Enabled ${itemType.toLowerCase()}: ${count}`);
90
90
  };
91
91
 
92
92
  // ============================================================================
@@ -108,15 +108,15 @@ export const handleCheckboxConfig = async <T extends string>(
108
108
  // Get current enabled items
109
109
  const enabledKeys = getEnabledKeys(current);
110
110
 
111
- // Show checkbox prompt
112
- const { selected } = await inquirer.prompt([
113
- {
114
- type: 'checkbox',
115
- name: 'selected',
116
- message,
117
- choices: buildChoices(available, enabledKeys),
118
- },
119
- ]);
111
+ // Build options for multiselect
112
+ const multiselectOptions = buildOptions(available, enabledKeys);
113
+
114
+ // Show multiselect prompt
115
+ const selected = await promptMultiselect<T>({
116
+ message,
117
+ options: multiselectOptions,
118
+ initialValues: enabledKeys as T[],
119
+ });
120
120
 
121
121
  // Update config
122
122
  const updated = updateConfig(available, selected);