@sylphx/flow 2.0.0 → 2.1.1

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,122 @@
1
1
  # @sylphx/flow
2
2
 
3
+ ## 2.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 8ae48d6: Fix singleFiles location and improve settings cleanup
8
+
9
+ **Bug Fixes:**
10
+
11
+ 1. Fixed silent.md location bug - output style files were incorrectly written to project root instead of target config directory (.claude/ or .opencode/)
12
+
13
+ 2. Enhanced clearUserSettings to ensure complete cleanup in replace mode:
14
+ - Now clears ALL user configuration including hooks, complete MCP config, rules, and singleFiles
15
+ - Removes entire MCP section (not just servers) to properly clear user hooks
16
+ - Added legacy cleanup to remove incorrectly placed files from project root
17
+
18
+ This fixes the issue where user's hooks and MCP configs were still affecting execution even in replace mode (non-merge mode).
19
+
20
+ ## 2.1.0
21
+
22
+ ### Minor Changes
23
+
24
+ - 09608be: Auto-installation and auto-upgrade features
25
+
26
+ **New Features:**
27
+
28
+ - **Auto-detection**: Automatically detects installed AI CLIs (Claude Code, OpenCode, Cursor)
29
+ - **Auto-installation**: If no AI CLI is detected, prompts user to select and installs it automatically
30
+ - **Auto-upgrade**: Before each Flow execution, automatically checks and upgrades Flow and target CLI to latest versions
31
+ - **Zero-friction setup**: New users can install Flow and start using it immediately without manual setup
32
+
33
+ **Implementation:**
34
+
35
+ - Created `TargetInstaller` service for detecting and installing AI CLI tools
36
+ - Created `AutoUpgrade` service for automatic version checking and upgrading
37
+ - Integrated both services into execution flow (`execute-v2.ts`)
38
+ - Smart package manager detection (npm, bun, pnpm, yarn)
39
+
40
+ **User Experience:**
41
+
42
+ Flow 2.0 now delivers on the promise of "One CLI to rule them all":
43
+
44
+ 1. **First run**: User installs Flow → Flow detects no AI CLI → Prompts to select one → Installs it automatically
45
+ 2. **Every run**: Flow checks for updates → Upgrades Flow and AI CLI to latest → Runs user's task
46
+
47
+ Example flow:
48
+
49
+ ```
50
+ $ npm install -g @sylphx/flow
51
+ $ sylphx-flow "build my app"
52
+
53
+ 🔍 Detecting installed AI CLIs...
54
+ ⚠️ No AI CLI detected
55
+
56
+ ? No AI CLI detected. Which would you like to use?
57
+ ❯ Claude Code
58
+ OpenCode
59
+ Cursor
60
+
61
+ ✓ Claude Code installed successfully
62
+
63
+ 🔄 Checking for updates...
64
+ ✓ Flow is up to date
65
+ ✓ Claude Code is up to date
66
+
67
+ 🚀 Starting Flow session...
68
+ ```
69
+
70
+ **Philosophy:**
71
+
72
+ This implements Flow's core principle of "Transcendent Simplicity" - users don't need to know which AI CLI to use or how to install/upgrade it. Flow handles everything automatically.
73
+
74
+ ### Patch Changes
75
+
76
+ - cc065f2: Code cleanup and refactoring
77
+
78
+ **Removed:**
79
+
80
+ - All legacy config migration code (~70 lines)
81
+ - OpenCode old directory cleanup logic (~16 lines)
82
+ - Deprecated FileInstaller and MCPInstaller classes (~60 lines)
83
+ - Unused deprecated exports (ALL_TARGETS, IMPLEMENTED_TARGETS)
84
+
85
+ **Refactored:**
86
+
87
+ - Migrated from class-based installers to functional API
88
+ - opencode.ts: Direct function calls instead of class wrappers
89
+ - claude-code.ts: Direct function calls instead of class wrappers
90
+
91
+ **Improved:**
92
+
93
+ - Removed ~179 lines of dead code
94
+ - Cleaner functional API
95
+ - Better code organization and modularity
96
+ - Comprehensive JSDoc documentation
97
+ - Consistent error handling patterns
98
+
99
+ **Result:**
100
+
101
+ - Zero technical debt
102
+ - Zero deprecated code
103
+ - Modern, maintainable codebase
104
+
105
+ - edb043c: Fix target selection logic to properly distinguish between three cases
106
+
107
+ **Fixed:**
108
+
109
+ - Target selection now correctly handles three distinct scenarios:
110
+ 1. User explicitly set "ask-every-time" → always prompt
111
+ 2. User has no setting (undefined/null) → allow auto-detect
112
+ 3. User has specific target → use that target
113
+
114
+ **Improved:**
115
+
116
+ - Better code clarity with explicit case handling
117
+ - More predictable behavior for different user preferences
118
+ - Enhanced logic comments for maintainability
119
+
3
120
  ## 2.0.0
4
121
 
5
122
  ### Major Changes
package/UPGRADE.md CHANGED
@@ -1,20 +1,28 @@
1
1
  # Upgrade Guide
2
2
 
3
- Sylphx Flow includes built-in upgrade detection and automatic update capabilities with **smart package manager detection**.
3
+ Flow 2.0 includes **automatic upgrade before every session** with smart package manager detection. No more manual version management—Flow keeps itself and your AI CLI tools up to date.
4
4
 
5
- ## Auto-Detection on Startup
5
+ ## Auto-Upgrade on Every Execution
6
6
 
7
- Every time you run Sylphx Flow, it automatically checks for available updates in the background. If an update is available, you'll see a notification like:
7
+ **Before each Flow session**, Flow automatically:
8
8
 
9
+ 1. **Checks for Flow updates**: Compares installed version with npm registry
10
+ 2. **Checks for AI CLI updates**: Verifies Claude Code, OpenCode, or Cursor versions
11
+ 3. **Upgrades automatically**: Installs latest versions if available
12
+ 4. **Detects package manager**: Uses npm, bun, pnpm, or yarn based on your environment
13
+
14
+ Example output:
9
15
  ```
10
- 📦 Sylphx Flow update available: 1.8.1 → 1.9.0
11
- Quick upgrade: sylphx-flow upgrade --auto
12
- Or run: bun install -g @sylphx/flow@latest
13
- ```
16
+ 🔄 Checking for updates...
17
+ Flow 2.0.0 2.1.0 (latest)
18
+ Claude Code 1.5.0 1.6.0 (latest)
19
+ 📦 Installing updates...
20
+ ✓ All tools upgraded
14
21
 
15
- The upgrade command is automatically tailored to your detected package manager (npm, bun, pnpm, or yarn).
22
+ 🚀 Starting Flow session...
23
+ ```
16
24
 
17
- This check is non-intrusive and won't block your workflow. Use `--quick` mode to skip the check entirely.
25
+ This happens automatically. No flags needed, no configuration required.
18
26
 
19
27
  ## Manual Upgrade Check
20
28
 
@@ -94,13 +102,16 @@ Sylphx Flow automatically detects which package manager you're using:
94
102
  - Without `--auto`: Shows manual install command for your package manager
95
103
  - With `--auto`: Runs the appropriate install command automatically
96
104
 
97
- ## Disabling Auto-Check
105
+ ## Philosophy: Always Latest
98
106
 
99
- Use `--quick` mode to skip automatic update checks:
107
+ Flow 2.0 embraces the principle of **always running the latest stable versions**. This ensures:
100
108
 
101
- ```bash
102
- sylphx-flow --quick "your prompt here"
103
- ```
109
+ - Latest features and improvements
110
+ - Security patches applied immediately
111
+ - Compatibility with newest AI models
112
+ - Bug fixes without manual intervention
113
+
114
+ Auto-upgrade cannot be disabled—it's core to Flow's philosophy of zero-friction excellence.
104
115
 
105
116
  ## Troubleshooting
106
117
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sylphx/flow",
3
- "version": "2.0.0",
4
- "description": "AI-powered development workflow automation with autonomous loop mode and smart configuration",
3
+ "version": "2.1.1",
4
+ "description": "One CLI to rule them all. Unified orchestration layer for Claude Code, OpenCode, Cursor and all AI development tools. Auto-detection, auto-installation, auto-upgrade.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "sylphx-flow": "./src/index.ts"
@@ -50,12 +50,16 @@
50
50
  "ai",
51
51
  "automation",
52
52
  "workflow",
53
- "claude",
53
+ "claude-code",
54
54
  "opencode",
55
+ "cursor",
55
56
  "cli",
56
- "autonomous",
57
- "loop-mode",
58
- "developer-tools"
57
+ "orchestration",
58
+ "unified",
59
+ "meta-layer",
60
+ "developer-tools",
61
+ "auto-install",
62
+ "auto-upgrade"
59
63
  ],
60
64
  "repository": {
61
65
  "type": "git",
@@ -16,6 +16,32 @@ import type { FlowOptions } from './types.js';
16
16
  import { resolvePrompt } from './prompt.js';
17
17
  import { GlobalConfigService } from '../../services/global-config.js';
18
18
  import { UserCancelledError } from '../../utils/errors.js';
19
+ import { TargetInstaller } from '../../services/target-installer.js';
20
+ import { AutoUpgrade } from '../../services/auto-upgrade.js';
21
+ import { promptForTargetSelection, ensureTargetInstalled } from '../../utils/target-selection.js';
22
+
23
+ /**
24
+ * Configure provider environment variables
25
+ */
26
+ function configureProviderEnv(
27
+ provider: 'kimi' | 'zai',
28
+ apiKey: string
29
+ ): void {
30
+ const providerConfig = {
31
+ kimi: {
32
+ baseUrl: 'https://api.moonshot.cn/v1',
33
+ name: 'Kimi',
34
+ },
35
+ zai: {
36
+ baseUrl: 'https://api.z.ai/v1',
37
+ name: 'Z.ai',
38
+ },
39
+ };
40
+
41
+ const config = providerConfig[provider];
42
+ process.env.ANTHROPIC_BASE_URL = config.baseUrl;
43
+ process.env.ANTHROPIC_API_KEY = apiKey;
44
+ }
19
45
 
20
46
  /**
21
47
  * Select and configure provider for Claude Code
@@ -27,60 +53,48 @@ async function selectProvider(
27
53
  const providerConfig = await configService.loadProviderConfig();
28
54
  const defaultProvider = providerConfig.claudeCode.defaultProvider;
29
55
 
30
- // If not "ask-every-time", use the default provider
31
- if (defaultProvider !== 'ask-every-time') {
32
- // Configure environment variables for the selected provider
33
- if (defaultProvider === 'kimi' || defaultProvider === 'zai') {
34
- const provider = providerConfig.claudeCode.providers[defaultProvider];
35
- if (provider?.apiKey) {
36
- if (defaultProvider === 'kimi') {
37
- process.env.ANTHROPIC_BASE_URL = 'https://api.moonshot.cn/v1';
38
- process.env.ANTHROPIC_API_KEY = provider.apiKey;
39
- } else if (defaultProvider === 'zai') {
40
- process.env.ANTHROPIC_BASE_URL = 'https://api.z.ai/v1';
41
- process.env.ANTHROPIC_API_KEY = provider.apiKey;
56
+ // If not "ask-every-time", use the default provider
57
+ if (defaultProvider !== 'ask-every-time') {
58
+ if (defaultProvider === 'kimi' || defaultProvider === 'zai') {
59
+ const provider = providerConfig.claudeCode.providers[defaultProvider];
60
+ if (provider?.apiKey) {
61
+ configureProviderEnv(defaultProvider, provider.apiKey);
42
62
  }
43
63
  }
64
+ return;
44
65
  }
45
- return;
46
- }
47
-
48
- // Ask user which provider to use for this session
49
- const { selectedProvider } = await inquirer.prompt([
50
- {
51
- type: 'list',
52
- name: 'selectedProvider',
53
- message: 'Select provider for this session:',
54
- choices: [
55
- { name: 'Default (Claude Code built-in)', value: 'default' },
56
- { name: 'Kimi', value: 'kimi' },
57
- { name: 'Z.ai', value: 'zai' },
58
- ],
59
- default: 'default',
60
- },
61
- ]);
62
66
 
63
- // Configure environment variables based on selection
64
- if (selectedProvider === 'kimi' || selectedProvider === 'zai') {
65
- const provider = providerConfig.claudeCode.providers[selectedProvider];
67
+ // Ask user which provider to use for this session
68
+ const { selectedProvider } = await inquirer.prompt([
69
+ {
70
+ type: 'list',
71
+ name: 'selectedProvider',
72
+ message: 'Select provider for this session:',
73
+ choices: [
74
+ { name: 'Default (Claude Code built-in)', value: 'default' },
75
+ { name: 'Kimi', value: 'kimi' },
76
+ { name: 'Z.ai', value: 'zai' },
77
+ ],
78
+ default: 'default',
79
+ },
80
+ ]);
81
+
82
+ // Configure environment variables based on selection
83
+ if (selectedProvider === 'kimi' || selectedProvider === 'zai') {
84
+ const provider = providerConfig.claudeCode.providers[selectedProvider];
85
+
86
+ if (!provider?.apiKey) {
87
+ console.log(chalk.yellow('⚠ API key not configured. Use: sylphx-flow settings\n'));
88
+ return;
89
+ }
66
90
 
67
- if (!provider?.apiKey) {
68
- console.log(chalk.yellow('⚠ API key not configured. Use: sylphx-flow settings\n'));
69
- return;
70
- }
91
+ configureProviderEnv(selectedProvider, provider.apiKey);
71
92
 
72
- if (selectedProvider === 'kimi') {
73
- process.env.ANTHROPIC_BASE_URL = 'https://api.moonshot.cn/v1';
74
- process.env.ANTHROPIC_API_KEY = provider.apiKey;
75
- console.log(chalk.green('✓ Using Kimi provider\n'));
76
- } else if (selectedProvider === 'zai') {
77
- process.env.ANTHROPIC_BASE_URL = 'https://api.z.ai/v1';
78
- process.env.ANTHROPIC_API_KEY = provider.apiKey;
79
- console.log(chalk.green('✓ Using Z.ai provider\n'));
93
+ const providerName = selectedProvider === 'kimi' ? 'Kimi' : 'Z.ai';
94
+ console.log(chalk.green(`✓ Using ${providerName} provider\n`));
95
+ } else {
96
+ console.log(chalk.green('✓ Using default Claude Code provider\n'));
80
97
  }
81
- } else {
82
- console.log(chalk.green('✓ Using default Claude Code provider\n'));
83
- }
84
98
  } catch (error: any) {
85
99
  // Handle user cancellation (Ctrl+C)
86
100
  if (error.name === 'ExitPromptError' || error.message?.includes('force closed')) {
@@ -129,6 +143,103 @@ export async function executeFlowV2(
129
143
  // Show welcome banner
130
144
  showWelcome();
131
145
 
146
+ // Initialize config service early to check for saved preferences
147
+ const configService = new GlobalConfigService();
148
+ await configService.initialize();
149
+
150
+ // Step 1: Determine target
151
+ const targetInstaller = new TargetInstaller(projectPath);
152
+ const installedTargets = await targetInstaller.detectInstalledTargets();
153
+ const settings = await configService.loadSettings();
154
+
155
+ let selectedTargetId: string | null = null;
156
+
157
+ // Distinguish between three cases:
158
+ // 1. User explicitly set "ask-every-time" → always prompt
159
+ // 2. User has no setting (undefined/null) → allow auto-detect
160
+ // 3. User has specific target → use that target
161
+ const isAskEveryTime = settings.defaultTarget === 'ask-every-time';
162
+ const hasNoSetting = !settings.defaultTarget;
163
+ const hasSpecificTarget = settings.defaultTarget && settings.defaultTarget !== 'ask-every-time';
164
+
165
+ if (isAskEveryTime) {
166
+ // User explicitly wants to be asked every time - ALWAYS prompt, never auto-detect
167
+ console.log(chalk.cyan('🔍 Detecting installed AI CLIs...\n'));
168
+
169
+ selectedTargetId = await promptForTargetSelection(
170
+ installedTargets,
171
+ 'Select AI CLI to use:',
172
+ 'execution'
173
+ );
174
+
175
+ const installation = targetInstaller.getInstallationInfo(selectedTargetId);
176
+ const installed = await ensureTargetInstalled(selectedTargetId, targetInstaller, installedTargets);
177
+
178
+ if (!installed) {
179
+ process.exit(1);
180
+ }
181
+
182
+ if (installedTargets.includes(selectedTargetId)) {
183
+ console.log(chalk.green(`✓ Using ${installation?.name}\n`));
184
+ }
185
+ } else if (hasNoSetting) {
186
+ // No setting - use auto-detection (smart default behavior)
187
+ if (installedTargets.length === 1) {
188
+ // Exactly 1 target found - use it automatically
189
+ selectedTargetId = installedTargets[0];
190
+ const installation = targetInstaller.getInstallationInfo(selectedTargetId);
191
+ console.log(chalk.green(`✓ Using ${installation?.name} (auto-detected)\n`));
192
+ } else {
193
+ // 0 or multiple targets - prompt for selection
194
+ console.log(chalk.cyan('🔍 Detecting installed AI CLIs...\n'));
195
+
196
+ selectedTargetId = await promptForTargetSelection(
197
+ installedTargets,
198
+ 'Select AI CLI to use:',
199
+ 'execution'
200
+ );
201
+
202
+ const installation = targetInstaller.getInstallationInfo(selectedTargetId);
203
+ const installed = await ensureTargetInstalled(selectedTargetId, targetInstaller, installedTargets);
204
+
205
+ if (!installed) {
206
+ process.exit(1);
207
+ }
208
+
209
+ if (installedTargets.includes(selectedTargetId)) {
210
+ console.log(chalk.green(`✓ Using ${installation?.name}\n`));
211
+ }
212
+ }
213
+ } else if (hasSpecificTarget) {
214
+ // User has a specific target preference - ALWAYS use it
215
+ selectedTargetId = settings.defaultTarget;
216
+ const installation = targetInstaller.getInstallationInfo(selectedTargetId);
217
+
218
+ // Check if the preferred target is installed
219
+ if (installedTargets.includes(selectedTargetId)) {
220
+ console.log(chalk.green(`✓ Using ${installation?.name} (from settings)\n`));
221
+ } else {
222
+ // Preferred target not installed - try to install it
223
+ console.log(chalk.yellow(`⚠️ ${installation?.name} is set as default but not installed\n`));
224
+ const installed = await targetInstaller.install(selectedTargetId, true);
225
+
226
+ if (!installed) {
227
+ // Installation failed - show error and exit
228
+ console.log(chalk.red(`\n✗ Cannot proceed: ${installation?.name} is not installed and auto-install failed`));
229
+ console.log(chalk.yellow(' Please either:'));
230
+ console.log(chalk.cyan(' 1. Install manually (see instructions above)'));
231
+ console.log(chalk.cyan(' 2. Change default target: sylphx-flow settings\n'));
232
+ process.exit(1);
233
+ }
234
+
235
+ console.log();
236
+ }
237
+ }
238
+
239
+ // Step 2: Auto-upgrade Flow and target CLI
240
+ const autoUpgrade = new AutoUpgrade(projectPath);
241
+ await autoUpgrade.runAutoUpgrade(selectedTargetId);
242
+
132
243
  // Mode info
133
244
  if (options.merge) {
134
245
  console.log(chalk.cyan('🔗 Merge mode: Flow settings will be merged with your existing settings'));
@@ -138,24 +249,10 @@ export async function executeFlowV2(
138
249
  console.log(chalk.dim(' Use --merge to keep your existing settings\n'));
139
250
  }
140
251
 
141
- // Initialize config service
142
- const configService = new GlobalConfigService();
143
- await configService.initialize();
144
-
145
252
  // Create executor
146
253
  const executor = new FlowExecutor();
147
254
  const projectManager = executor.getProjectManager();
148
255
 
149
- // Step 1: Check for upgrades (non-intrusive)
150
- const upgradeManager = new UpgradeManager();
151
- const updates = await upgradeManager.checkUpdates();
152
-
153
- if (updates.flowUpdate || updates.targetUpdate) {
154
- console.log(
155
- chalk.yellow('📦 Updates available! Run: sylphx-flow upgrade --auto\n')
156
- );
157
- }
158
-
159
256
  // Step 2: Execute attach mode lifecycle
160
257
  try {
161
258
  // Attach Flow environment (backup → attach → register cleanup)
@@ -166,12 +263,9 @@ export async function executeFlowV2(
166
263
  merge: options.merge || false,
167
264
  });
168
265
 
169
- // Step 3: Detect target and load agent
170
- const projectHash = projectManager.getProjectHash(projectPath);
171
- const target = await projectManager.detectTarget(projectPath);
172
-
173
- // Map target to targetManager's target IDs
174
- const targetId = target === 'claude-code' ? 'claude-code' : 'opencode';
266
+ // Step 3: Use the target we already selected (don't re-detect)
267
+ // selectedTargetId was determined earlier based on settings/auto-detect/prompt
268
+ const targetId = selectedTargetId;
175
269
 
176
270
  // Step 3.5: Provider selection (Claude Code only)
177
271
  if (targetId === 'claude-code') {
@@ -8,6 +8,8 @@ import chalk from 'chalk';
8
8
  import inquirer from 'inquirer';
9
9
  import { GlobalConfigService } from '../services/global-config.js';
10
10
  import { UserCancelledError } from '../utils/errors.js';
11
+ import { TargetInstaller } from '../services/target-installer.js';
12
+ import { promptForDefaultTarget, buildAvailableTargets } from '../utils/target-selection.js';
11
13
 
12
14
  export const settingsCommand = new Command('settings')
13
15
  .description('Configure Sylphx Flow settings')
@@ -440,25 +442,30 @@ async function configureTarget(configService: GlobalConfigService): Promise<void
440
442
  console.log(chalk.cyan.bold('\n━━━ 🎯 Target Platform\n'));
441
443
 
442
444
  const settings = await configService.loadSettings();
445
+ const targetInstaller = new TargetInstaller();
443
446
 
444
- const { defaultTarget } = await inquirer.prompt([
445
- {
446
- type: 'list',
447
- name: 'defaultTarget',
448
- message: 'Select default target platform:',
449
- choices: [
450
- { name: 'Claude Code', value: 'claude-code' },
451
- { name: 'OpenCode', value: 'opencode' },
452
- ],
453
- default: settings.defaultTarget || 'claude-code',
454
- },
455
- ]);
447
+ // Detect which targets are installed
448
+ console.log(chalk.dim('Detecting installed AI CLIs...\n'));
449
+ const installedTargets = await targetInstaller.detectInstalledTargets();
456
450
 
457
- settings.defaultTarget = defaultTarget;
451
+ const defaultTarget = await promptForDefaultTarget(installedTargets, settings.defaultTarget);
452
+
453
+ settings.defaultTarget = defaultTarget as 'claude-code' | 'opencode' | 'cursor' | 'ask-every-time';
458
454
  await configService.saveSettings(settings);
459
455
 
460
- console.log(chalk.green('\n✓ Target platform saved'));
461
- console.log(chalk.dim(` Default: ${defaultTarget}`));
456
+ if (defaultTarget === 'ask-every-time') {
457
+ console.log(chalk.green('\n✓ Target platform saved'));
458
+ console.log(chalk.dim(' Default: Ask every time (auto-detect or prompt)'));
459
+ } else {
460
+ const availableTargets = buildAvailableTargets(installedTargets);
461
+ const selectedTarget = availableTargets.find((t) => t.value === defaultTarget);
462
+ const installStatus = selectedTarget?.installed
463
+ ? chalk.green('(installed)')
464
+ : chalk.yellow('(will be installed on first use)');
465
+
466
+ console.log(chalk.green('\n✓ Target platform saved'));
467
+ console.log(chalk.dim(` Default: ${defaultTarget} ${installStatus}`));
468
+ }
462
469
  }
463
470
 
464
471
  /**
@@ -58,11 +58,6 @@ const GLOBAL_CONFIG_FILE = path.join(os.homedir(), '.sylphx-flow', 'settings.jso
58
58
  const PROJECT_CONFIG_FILE = '.sylphx-flow/settings.json';
59
59
  const LOCAL_CONFIG_FILE = '.sylphx-flow/settings.local.json';
60
60
 
61
- /**
62
- * Deprecated config file (for migration)
63
- */
64
- const LEGACY_CONFIG_FILE = '.sylphx-flow/ai-config.json';
65
-
66
61
  /**
67
62
  * Get AI config file paths in priority order
68
63
  */
@@ -70,12 +65,10 @@ export const getAIConfigPaths = (cwd: string = process.cwd()): {
70
65
  global: string;
71
66
  project: string;
72
67
  local: string;
73
- legacy: string;
74
68
  } => ({
75
69
  global: GLOBAL_CONFIG_FILE,
76
70
  project: path.join(cwd, PROJECT_CONFIG_FILE),
77
71
  local: path.join(cwd, LOCAL_CONFIG_FILE),
78
- legacy: path.join(cwd, LEGACY_CONFIG_FILE),
79
72
  });
80
73
 
81
74
  /**
@@ -140,18 +133,12 @@ export const aiConfigExists = async (cwd: string = process.cwd()): Promise<boole
140
133
  return true;
141
134
  } catch {}
142
135
 
143
- try {
144
- await fs.access(paths.legacy);
145
- return true;
146
- } catch {}
147
-
148
136
  return false;
149
137
  };
150
138
 
151
139
  /**
152
140
  * Load AI configuration
153
141
  * Merges global, project, and local configs with priority: local > project > global
154
- * Automatically migrates legacy config on first load
155
142
  */
156
143
  export const loadAIConfig = async (cwd: string = process.cwd()): Promise<Result<AIConfig, Error>> => {
157
144
  return tryCatchAsync(
@@ -159,39 +146,19 @@ export const loadAIConfig = async (cwd: string = process.cwd()): Promise<Result<
159
146
  const paths = getAIConfigPaths(cwd);
160
147
 
161
148
  // Load all config files
162
- const [globalConfig, projectConfig, localConfig, legacyConfig] = await Promise.all([
149
+ const [globalConfig, projectConfig, localConfig] = await Promise.all([
163
150
  loadConfigFile(paths.global),
164
151
  loadConfigFile(paths.project),
165
152
  loadConfigFile(paths.local),
166
- loadConfigFile(paths.legacy),
167
153
  ]);
168
154
 
169
- // Auto-migrate legacy config if it exists and global doesn't
170
- if (legacyConfig && !globalConfig) {
171
- await migrateLegacyConfig(cwd);
172
- // Reload global config after migration
173
- const migratedGlobal = await loadConfigFile(paths.global);
174
- if (migratedGlobal) {
175
- // Start with empty config
176
- let merged: AIConfig = {};
177
-
178
- // Merge in priority order: global < project < local
179
- merged = mergeConfigs(merged, migratedGlobal);
180
- if (projectConfig) merged = mergeConfigs(merged, projectConfig);
181
- if (localConfig) merged = mergeConfigs(merged, localConfig);
182
-
183
- return merged;
184
- }
185
- }
186
-
187
155
  // Start with empty config
188
156
  let merged: AIConfig = {};
189
157
 
190
- // Merge in priority order: global < project < local < legacy (for backwards compat)
158
+ // Merge in priority order: global < project < local
191
159
  if (globalConfig) merged = mergeConfigs(merged, globalConfig);
192
160
  if (projectConfig) merged = mergeConfigs(merged, projectConfig);
193
161
  if (localConfig) merged = mergeConfigs(merged, localConfig);
194
- if (legacyConfig) merged = mergeConfigs(merged, legacyConfig);
195
162
 
196
163
  return merged;
197
164
  },
@@ -340,37 +307,3 @@ export const getConfiguredProviders = async (
340
307
  return providers;
341
308
  };
342
309
 
343
- /**
344
- * Migrate legacy ai-config.json to new settings system
345
- * Automatically called on first load if legacy config exists
346
- */
347
- export const migrateLegacyConfig = async (cwd: string = process.cwd()): Promise<Result<void, Error>> => {
348
- return tryCatchAsync(
349
- async () => {
350
- const paths = getAIConfigPaths(cwd);
351
-
352
- // Check if legacy config exists
353
- const legacyConfig = await loadConfigFile(paths.legacy);
354
- if (!legacyConfig) {
355
- return; // No legacy config to migrate
356
- }
357
-
358
- // Check if global config already exists
359
- const globalConfig = await loadConfigFile(paths.global);
360
- if (globalConfig) {
361
- // Global config exists, don't overwrite it
362
- console.log('Legacy config found but global config already exists. Skipping migration.');
363
- console.log(`You can manually delete ${paths.legacy} if migration is complete.`);
364
- return;
365
- }
366
-
367
- // Migrate to global config
368
- await fs.mkdir(path.dirname(paths.global), { recursive: true });
369
- await fs.writeFile(paths.global, JSON.stringify(legacyConfig, null, 2) + '\n', 'utf8');
370
-
371
- console.log(`✓ Migrated configuration from ${paths.legacy} to ${paths.global}`);
372
- console.log(` You can now safely delete the legacy file: ${paths.legacy}`);
373
- },
374
- (error: any) => new Error(`Failed to migrate legacy config: ${error.message}`)
375
- );
376
- };
@@ -113,14 +113,3 @@ export const isTargetImplemented = (id: string): boolean => {
113
113
  * Utility type for target IDs
114
114
  */
115
115
  export type TargetID = ReturnType<typeof getAllTargetIDs>[number];
116
-
117
- /**
118
- * Legacy aliases for backward compatibility
119
- * @deprecated Use getAllTargets() instead
120
- */
121
- export const ALL_TARGETS = getAllTargets;
122
-
123
- /**
124
- * @deprecated Use getImplementedTargets() instead
125
- */
126
- export const IMPLEMENTED_TARGETS = getImplementedTargets;