@sylphx/flow 2.1.2 → 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.
Files changed (70) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +44 -0
  3. package/package.json +79 -73
  4. package/src/commands/flow/execute-v2.ts +39 -30
  5. package/src/commands/flow/index.ts +2 -4
  6. package/src/commands/flow/prompt.ts +5 -3
  7. package/src/commands/flow/types.ts +0 -9
  8. package/src/commands/flow-command.ts +20 -13
  9. package/src/commands/hook-command.ts +1 -3
  10. package/src/commands/settings-command.ts +36 -33
  11. package/src/config/ai-config.ts +60 -41
  12. package/src/core/agent-loader.ts +11 -6
  13. package/src/core/attach-manager.ts +92 -84
  14. package/src/core/backup-manager.ts +35 -29
  15. package/src/core/cleanup-handler.ts +11 -8
  16. package/src/core/error-handling.ts +23 -30
  17. package/src/core/flow-executor.ts +58 -76
  18. package/src/core/formatting/bytes.ts +2 -4
  19. package/src/core/functional/async.ts +5 -4
  20. package/src/core/functional/error-handler.ts +2 -2
  21. package/src/core/git-stash-manager.ts +21 -10
  22. package/src/core/installers/file-installer.ts +0 -1
  23. package/src/core/installers/mcp-installer.ts +0 -1
  24. package/src/core/project-manager.ts +24 -18
  25. package/src/core/secrets-manager.ts +54 -73
  26. package/src/core/session-manager.ts +20 -22
  27. package/src/core/state-detector.ts +139 -80
  28. package/src/core/template-loader.ts +13 -31
  29. package/src/core/upgrade-manager.ts +122 -69
  30. package/src/index.ts +8 -5
  31. package/src/services/auto-upgrade.ts +1 -1
  32. package/src/services/config-service.ts +41 -29
  33. package/src/services/global-config.ts +2 -2
  34. package/src/services/target-installer.ts +9 -7
  35. package/src/targets/claude-code.ts +28 -15
  36. package/src/targets/opencode.ts +17 -6
  37. package/src/types/cli.types.ts +2 -2
  38. package/src/types/provider.types.ts +1 -7
  39. package/src/types/session.types.ts +11 -11
  40. package/src/types/target.types.ts +3 -1
  41. package/src/types/todo.types.ts +1 -1
  42. package/src/types.ts +1 -1
  43. package/src/utils/__tests__/package-manager-detector.test.ts +6 -6
  44. package/src/utils/agent-enhancer.ts +111 -3
  45. package/src/utils/config/paths.ts +3 -1
  46. package/src/utils/config/target-utils.ts +2 -2
  47. package/src/utils/display/banner.ts +2 -2
  48. package/src/utils/display/notifications.ts +58 -45
  49. package/src/utils/display/status.ts +29 -12
  50. package/src/utils/files/file-operations.ts +1 -1
  51. package/src/utils/files/sync-utils.ts +38 -41
  52. package/src/utils/index.ts +19 -27
  53. package/src/utils/package-manager-detector.ts +15 -5
  54. package/src/utils/security/security.ts +8 -4
  55. package/src/utils/target-selection.ts +5 -2
  56. package/src/utils/version.ts +4 -2
  57. package/src/commands/flow/execute.ts +0 -453
  58. package/src/commands/flow/setup.ts +0 -312
  59. package/src/commands/flow-orchestrator.ts +0 -328
  60. package/src/commands/init-command.ts +0 -92
  61. package/src/commands/init-core.ts +0 -331
  62. package/src/commands/run-command.ts +0 -126
  63. package/src/core/agent-manager.ts +0 -174
  64. package/src/core/loop-controller.ts +0 -200
  65. package/src/core/rule-loader.ts +0 -147
  66. package/src/core/rule-manager.ts +0 -240
  67. package/src/services/claude-config-service.ts +0 -252
  68. package/src/services/first-run-setup.ts +0 -220
  69. package/src/services/smart-config-service.ts +0 -269
  70. package/src/types/api.types.ts +0 -9
@@ -6,10 +6,14 @@
6
6
  import { playNotificationSound } from './audio-player.js';
7
7
 
8
8
  // Terminal notification with sound
9
- export function sendTerminalNotification(title: string, message: string, options?: {
10
- sound?: boolean;
11
- duration?: number;
12
- }) {
9
+ export function sendTerminalNotification(
10
+ _title: string,
11
+ _message: string,
12
+ options?: {
13
+ sound?: boolean;
14
+ duration?: number;
15
+ }
16
+ ) {
13
17
  const { sound = true, duration = 3000 } = options || {};
14
18
 
15
19
  // Play system sound using cross-platform audio player
@@ -30,40 +34,45 @@ export function sendTerminalNotification(title: string, message: string, options
30
34
  }
31
35
 
32
36
  // OS-level notification using system APIs
33
- export async function sendOSNotification(title: string, message: string, options?: {
34
- icon?: string;
35
- urgency?: 'low' | 'normal' | 'critical';
36
- sound?: boolean;
37
- timeout?: number;
38
- }) {
37
+ export async function sendOSNotification(
38
+ title: string,
39
+ message: string,
40
+ options?: {
41
+ icon?: string;
42
+ urgency?: 'low' | 'normal' | 'critical';
43
+ sound?: boolean;
44
+ timeout?: number;
45
+ }
46
+ ) {
39
47
  const {
40
48
  icon = '🌀', // Flow-themed spiral emoji for Sylphx Flow notifications
41
49
  urgency = 'normal',
42
50
  sound = true,
43
- timeout = 5000
51
+ timeout = 5000,
44
52
  } = options || {};
45
-
53
+
46
54
  try {
47
55
  if (process.platform === 'darwin') {
48
56
  // macOS: use osascript with simplified approach
49
- const { spawn } = require('child_process');
50
-
57
+ const { spawn } = require('node:child_process');
58
+
51
59
  await new Promise<void>((resolve, reject) => {
52
60
  // Simple notification without complex escaping
53
61
  const args = [
54
- '-e', `display notification "${message.replace(/"/g, '\\"')}" with title "${title.replace(/"/g, '\\"')}"`
62
+ '-e',
63
+ `display notification "${message.replace(/"/g, '\\"')}" with title "${title.replace(/"/g, '\\"')}"`,
55
64
  ];
56
-
57
- const proc = spawn('osascript', args, {
65
+
66
+ const proc = spawn('osascript', args, {
58
67
  stdio: 'pipe',
59
- env: { ...process.env, PATH: '/usr/bin:/bin:/usr/sbin:/sbin' }
68
+ env: { ...process.env, PATH: '/usr/bin:/bin:/usr/sbin:/sbin' },
60
69
  });
61
-
70
+
62
71
  let stderr = '';
63
72
  proc.stderr?.on('data', (data) => {
64
73
  stderr += data.toString();
65
74
  });
66
-
75
+
67
76
  proc.on('exit', (code) => {
68
77
  if (code === 0) {
69
78
  // Play sound separately if needed using cross-platform audio player
@@ -79,28 +88,32 @@ export async function sendOSNotification(title: string, message: string, options
79
88
  });
80
89
  proc.on('error', reject);
81
90
  });
82
-
83
91
  } else if (process.platform === 'linux') {
84
92
  // Linux: use notify-send
85
- const { spawn } = require('child_process');
93
+ const { spawn } = require('node:child_process');
86
94
  await new Promise<void>((resolve, reject) => {
87
95
  const proc = spawn('notify-send', [
88
- '--urgency', urgency,
89
- '--icon', icon,
90
- '--expire-time', timeout.toString(),
96
+ '--urgency',
97
+ urgency,
98
+ '--icon',
99
+ icon,
100
+ '--expire-time',
101
+ timeout.toString(),
91
102
  title,
92
- message
103
+ message,
93
104
  ]);
94
105
  proc.on('exit', (code) => {
95
- if (code === 0) resolve();
96
- else reject(new Error(`notify-send failed with code ${code}`));
106
+ if (code === 0) {
107
+ resolve();
108
+ } else {
109
+ reject(new Error(`notify-send failed with code ${code}`));
110
+ }
97
111
  });
98
112
  proc.on('error', reject);
99
113
  });
100
-
101
114
  } else if (process.platform === 'win32') {
102
115
  // Windows: use PowerShell toast notifications
103
- const { spawn } = require('child_process');
116
+ const { spawn } = require('node:child_process');
104
117
  const powershellScript = `
105
118
  Add-Type -AssemblyName System.Windows.Forms
106
119
  $notification = New-Object System.Windows.Forms.NotifyIcon
@@ -113,12 +126,15 @@ export async function sendOSNotification(title: string, message: string, options
113
126
  Start-Sleep -Milliseconds ${timeout + 1000}
114
127
  $notification.Dispose()
115
128
  `;
116
-
129
+
117
130
  await new Promise<void>((resolve, reject) => {
118
131
  const proc = spawn('powershell', ['-Command', powershellScript]);
119
132
  proc.on('exit', (code) => {
120
- if (code === 0) resolve();
121
- else reject(new Error(`PowerShell failed with code ${code}`));
133
+ if (code === 0) {
134
+ resolve();
135
+ } else {
136
+ reject(new Error(`PowerShell failed with code ${code}`));
137
+ }
122
138
  });
123
139
  proc.on('error', reject);
124
140
  });
@@ -132,24 +148,20 @@ export async function sendOSNotification(title: string, message: string, options
132
148
 
133
149
  // Combined notification - sends both terminal and OS notifications
134
150
  export function sendNotification(
135
- title: string,
136
- message: string,
151
+ title: string,
152
+ message: string,
137
153
  options?: {
138
154
  osNotification?: boolean;
139
155
  terminalNotification?: boolean;
140
156
  sound?: boolean;
141
157
  }
142
158
  ) {
143
- const {
144
- osNotification = true,
145
- terminalNotification = true,
146
- sound = true
147
- } = options || {};
148
-
159
+ const { osNotification = true, terminalNotification = true, sound = true } = options || {};
160
+
149
161
  if (terminalNotification) {
150
162
  sendTerminalNotification(title, message, { sound });
151
163
  }
152
-
164
+
153
165
  if (osNotification) {
154
166
  sendOSNotification(title, message, { sound });
155
167
  }
@@ -163,7 +175,8 @@ export async function checkNotificationSupport(): Promise<{
163
175
  }> {
164
176
  return {
165
177
  terminalSupported: true, // Always supported
166
- osSupported: process.platform === 'darwin' || process.platform === 'linux' || process.platform === 'win32',
167
- platform: process.platform
178
+ osSupported:
179
+ process.platform === 'darwin' || process.platform === 'linux' || process.platform === 'win32',
180
+ platform: process.platform,
168
181
  };
169
- }
182
+ }
@@ -13,9 +13,7 @@ import { isVersionOutdated } from '../version.js';
13
13
  export async function showStatus(state: ProjectState): Promise<void> {
14
14
  console.log(chalk.cyan.bold('📊 Project Status\n'));
15
15
 
16
- if (!state.initialized) {
17
- console.log(' ' + chalk.yellow('⚠ Not initialized'));
18
- } else {
16
+ if (state.initialized) {
19
17
  console.log(` ${chalk.green('✓')} Initialized (Flow v${state.version || 'unknown'})`);
20
18
 
21
19
  if (state.target) {
@@ -26,21 +24,38 @@ export async function showStatus(state: ProjectState): Promise<void> {
26
24
  // Component status
27
25
  const components = state.components;
28
26
  console.log(`\n ${chalk.cyan('Components:')}`);
29
- console.log(` Agents: ${components.agents.installed ? chalk.green(`✓ ${components.agents.count}`) : chalk.red('✗')}`);
30
- console.log(` Rules: ${components.rules.installed ? chalk.green(`✓ ${components.rules.count}`) : chalk.red('✗')}`);
27
+ console.log(
28
+ ` Agents: ${components.agents.installed ? chalk.green(`✓ ${components.agents.count}`) : chalk.red('✗')}`
29
+ );
30
+ console.log(
31
+ ` Rules: ${components.rules.installed ? chalk.green(`✓ ${components.rules.count}`) : chalk.red('✗')}`
32
+ );
31
33
  console.log(` Hooks: ${components.hooks.installed ? chalk.green('✓') : chalk.red('✗')}`);
32
- console.log(` MCP: ${components.mcp.installed ? chalk.green(`✓ ${components.mcp.serverCount} servers`) : chalk.red('✗')}`);
33
- console.log(` Output styles: ${components.outputStyles.installed ? chalk.green('✓') : chalk.red('✗')}`);
34
- console.log(` Slash commands: ${components.slashCommands.installed ? chalk.green(`✓ ${components.slashCommands.count}`) : chalk.red('✗')}`);
34
+ console.log(
35
+ ` MCP: ${components.mcp.installed ? chalk.green(`✓ ${components.mcp.serverCount} servers`) : chalk.red('✗')}`
36
+ );
37
+ console.log(
38
+ ` Output styles: ${components.outputStyles.installed ? chalk.green('✓') : chalk.red('✗')}`
39
+ );
40
+ console.log(
41
+ ` Slash commands: ${components.slashCommands.installed ? chalk.green(`✓ ${components.slashCommands.count}`) : chalk.red('✗')}`
42
+ );
35
43
 
36
44
  // Outdated warnings
37
45
  if (state.outdated) {
38
- console.log(`\n ${chalk.yellow('⚠')} Flow version outdated: ${state.version} → ${state.latestVersion}`);
46
+ console.log(
47
+ `\n ${chalk.yellow('⚠')} Flow version outdated: ${state.version} → ${state.latestVersion}`
48
+ );
39
49
  }
40
50
 
41
- if (state.targetVersion && state.targetLatestVersion &&
42
- isVersionOutdated(state.targetVersion, state.targetLatestVersion)) {
43
- console.log(` ${chalk.yellow('⚠')} ${state.target} update available: v${state.targetVersion} → v${state.targetLatestVersion}`);
51
+ if (
52
+ state.targetVersion &&
53
+ state.targetLatestVersion &&
54
+ isVersionOutdated(state.targetVersion, state.targetLatestVersion)
55
+ ) {
56
+ console.log(
57
+ ` ${chalk.yellow('⚠')} ${state.target} update available: v${state.targetVersion} → v${state.targetLatestVersion}`
58
+ );
44
59
  }
45
60
 
46
61
  if (state.lastUpdated) {
@@ -49,6 +64,8 @@ export async function showStatus(state: ProjectState): Promise<void> {
49
64
  console.log(`\n ${chalk.yellow('⚠')} Last updated: ${days} days ago`);
50
65
  }
51
66
  }
67
+ } else {
68
+ console.log(` ${chalk.yellow('⚠ Not initialized')}`);
52
69
  }
53
70
 
54
71
  console.log('');
@@ -5,8 +5,8 @@
5
5
 
6
6
  import fs from 'node:fs/promises';
7
7
  import path from 'node:path';
8
- import { pathSecurity } from '../security/security.js';
9
8
  import { formatFileSize as formatFileSizeCore } from '../../core/formatting/bytes.js';
9
+ import { pathSecurity } from '../security/security.js';
10
10
 
11
11
  export interface FileReadOptions {
12
12
  encoding?: BufferEncoding;
@@ -1,18 +1,21 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import chalk from 'chalk';
4
- import type { Target } from '../../types.js';
5
4
  import { MCP_SERVER_REGISTRY } from '../../config/servers.js';
6
- import { getAgentsDir, getSlashCommandsDir, getRulesDir } from '../config/paths.js';
5
+ import type { Target } from '../../types.js';
6
+ import { getAgentsDir, getRulesDir, getSlashCommandsDir } from '../config/paths.js';
7
7
 
8
8
  /**
9
9
  * Scan directory for .md files and return basenames
10
10
  */
11
11
  function scanTemplateDir(dir: string): string[] {
12
- if (!fs.existsSync(dir)) return [];
12
+ if (!fs.existsSync(dir)) {
13
+ return [];
14
+ }
13
15
 
14
16
  try {
15
- return fs.readdirSync(dir, { withFileTypes: true })
17
+ return fs
18
+ .readdirSync(dir, { withFileTypes: true })
16
19
  .filter((f) => f.isFile() && f.name.endsWith('.md'))
17
20
  .map((f) => f.name);
18
21
  } catch {
@@ -31,9 +34,9 @@ const FLOW_RULES = scanTemplateDir(getRulesDir());
31
34
  * Categorized files for sync
32
35
  */
33
36
  interface CategorizedFiles {
34
- inFlow: string[]; // Files that exist locally and in Flow templates (will reinstall)
35
- unknown: string[]; // Files not in Flow templates (custom or removed)
36
- missing: string[]; // Flow templates that don't exist locally (will install)
37
+ inFlow: string[]; // Files that exist locally and in Flow templates (will reinstall)
38
+ unknown: string[]; // Files not in Flow templates (custom or removed)
39
+ missing: string[]; // Flow templates that don't exist locally (will install)
37
40
  }
38
41
 
39
42
  /**
@@ -48,8 +51,8 @@ interface SyncManifest {
48
51
  notInRegistry: string[];
49
52
  };
50
53
  hooks: {
51
- inConfig: string[]; // Hooks from config (will be synced)
52
- orphaned: string[]; // Hooks that exist locally but not in config (ask user)
54
+ inConfig: string[]; // Hooks from config (will be synced)
55
+ orphaned: string[]; // Hooks that exist locally but not in config (ask user)
53
56
  };
54
57
  preserve: string[];
55
58
  }
@@ -151,14 +154,14 @@ export async function buildSyncManifest(cwd: string, target: Target): Promise<Sy
151
154
  const installedServers = Object.keys(mcpConfig.mcpServers);
152
155
  const registryServers = Object.keys(MCP_SERVER_REGISTRY);
153
156
 
154
- manifest.mcpServers.inRegistry = installedServers.filter(id =>
157
+ manifest.mcpServers.inRegistry = installedServers.filter((id) =>
155
158
  registryServers.includes(id)
156
159
  );
157
- manifest.mcpServers.notInRegistry = installedServers.filter(id =>
158
- !registryServers.includes(id)
160
+ manifest.mcpServers.notInRegistry = installedServers.filter(
161
+ (id) => !registryServers.includes(id)
159
162
  );
160
163
  }
161
- } catch (error) {
164
+ } catch (_error) {
162
165
  console.warn(chalk.yellow('⚠ Failed to read .mcp.json'));
163
166
  }
164
167
  }
@@ -178,25 +181,21 @@ export async function buildSyncManifest(cwd: string, target: Target): Promise<Sy
178
181
  // In the future, this could be read from Flow config
179
182
  const expectedHookTypes = ['Notification'];
180
183
 
181
- manifest.hooks.inConfig = existingHookTypes.filter(type =>
184
+ manifest.hooks.inConfig = existingHookTypes.filter((type) =>
182
185
  expectedHookTypes.includes(type)
183
186
  );
184
- manifest.hooks.orphaned = existingHookTypes.filter(type =>
185
- !expectedHookTypes.includes(type)
187
+ manifest.hooks.orphaned = existingHookTypes.filter(
188
+ (type) => !expectedHookTypes.includes(type)
186
189
  );
187
190
  }
188
- } catch (error) {
191
+ } catch (_error) {
189
192
  console.warn(chalk.yellow('⚠ Failed to read settings.json'));
190
193
  }
191
194
  }
192
195
  }
193
196
 
194
197
  // Files to preserve
195
- manifest.preserve = [
196
- '.sylphx-flow/',
197
- '.secrets/',
198
- target.config.configFile || '',
199
- ]
198
+ manifest.preserve = ['.sylphx-flow/', '.secrets/', target.config.configFile || '']
200
199
  .filter(Boolean)
201
200
  .map((p) => path.join(cwd, p));
202
201
 
@@ -379,7 +378,9 @@ export interface SelectedToRemove {
379
378
  /**
380
379
  * Select unknown files to remove
381
380
  */
382
- export async function selectUnknownFilesToRemove(manifest: SyncManifest): Promise<SelectedToRemove> {
381
+ export async function selectUnknownFilesToRemove(
382
+ manifest: SyncManifest
383
+ ): Promise<SelectedToRemove> {
383
384
  const unknownFiles: Array<{ name: string; value: string; type: string }> = [];
384
385
 
385
386
  // Collect all unknown files
@@ -463,10 +464,7 @@ export async function selectUnknownFilesToRemove(manifest: SyncManifest): Promis
463
464
  /**
464
465
  * Show final summary before execution
465
466
  */
466
- export function showFinalSummary(
467
- manifest: SyncManifest,
468
- selectedUnknowns: SelectedToRemove
469
- ): void {
467
+ export function showFinalSummary(manifest: SyncManifest, selectedUnknowns: SelectedToRemove): void {
470
468
  console.log(chalk.cyan.bold('\n━━━ 📋 Final Summary\n'));
471
469
 
472
470
  // Will delete + reinstall
@@ -490,7 +488,10 @@ export function showFinalSummary(
490
488
  }
491
489
 
492
490
  // Will remove (selected unknowns)
493
- const totalToRemove = selectedUnknowns.files.length + selectedUnknowns.mcpServers.length + selectedUnknowns.hooks.length;
491
+ const totalToRemove =
492
+ selectedUnknowns.files.length +
493
+ selectedUnknowns.mcpServers.length +
494
+ selectedUnknowns.hooks.length;
494
495
  if (totalToRemove > 0) {
495
496
  console.log(chalk.red('Remove (selected):\n'));
496
497
  selectedUnknowns.files.forEach((file) => {
@@ -506,7 +507,11 @@ export function showFinalSummary(
506
507
  }
507
508
 
508
509
  // Will preserve
509
- const allSelected = [...selectedUnknowns.files, ...selectedUnknowns.mcpServers, ...selectedUnknowns.hooks];
510
+ const allSelected = [
511
+ ...selectedUnknowns.files,
512
+ ...selectedUnknowns.mcpServers,
513
+ ...selectedUnknowns.hooks,
514
+ ];
510
515
  const preservedUnknowns = [
511
516
  ...manifest.agents.unknown,
512
517
  ...manifest.slashCommands.unknown,
@@ -618,14 +623,10 @@ export async function removeMCPServers(cwd: string, serversToRemove: string[]):
618
623
  }
619
624
 
620
625
  // Write back
621
- await fs.promises.writeFile(
622
- mcpPath,
623
- JSON.stringify(mcpConfig, null, 2) + '\n',
624
- 'utf-8'
625
- );
626
+ await fs.promises.writeFile(mcpPath, `${JSON.stringify(mcpConfig, null, 2)}\n`, 'utf-8');
626
627
 
627
628
  return removedCount;
628
- } catch (error) {
629
+ } catch (_error) {
629
630
  console.warn(chalk.yellow('⚠ Failed to update .mcp.json'));
630
631
  return 0;
631
632
  }
@@ -659,14 +660,10 @@ export async function removeHooks(cwd: string, hooksToRemove: string[]): Promise
659
660
  }
660
661
 
661
662
  // Write back
662
- await fs.promises.writeFile(
663
- settingsPath,
664
- JSON.stringify(settings, null, 2) + '\n',
665
- 'utf-8'
666
- );
663
+ await fs.promises.writeFile(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, 'utf-8');
667
664
 
668
665
  return removedCount;
669
- } catch (error) {
666
+ } catch (_error) {
670
667
  console.warn(chalk.yellow('⚠ Failed to update settings.json'));
671
668
  return 0;
672
669
  }
@@ -3,57 +3,49 @@
3
3
  * Feature-based organization for better modularity
4
4
  */
5
5
 
6
+ // ============================================================================
7
+ // SHARED UTILITIES
8
+ // ============================================================================
9
+ export * from '../shared/index.js';
10
+ // ============================================================================
11
+ // AGENTS
12
+ // ============================================================================
13
+ export * from './agent-enhancer.js';
14
+ export * from './config/mcp-config.js';
15
+ export * from './config/paths.js';
6
16
  // ============================================================================
7
17
  // CONFIG & SETTINGS
8
18
  // ============================================================================
9
19
  export * from './config/settings.js';
10
- export * from './config/mcp-config.js';
11
20
  export * from './config/target-config.js';
12
21
  export * from './config/target-utils.js';
13
- export * from './config/paths.js';
14
-
15
22
  // ============================================================================
16
23
  // DISPLAY & OUTPUT
17
24
  // ============================================================================
18
25
  export * from './display/banner.js';
19
- export * from './display/status.js';
20
26
  export * from './display/cli-output.js';
21
27
  export * from './display/logger.js';
22
28
  export * from './display/notifications.js';
23
-
29
+ export * from './display/status.js';
30
+ // ============================================================================
31
+ // ERROR HANDLING
32
+ // ============================================================================
33
+ export * from './error-handler.js';
24
34
  // ============================================================================
25
35
  // FILES & SYNC
26
36
  // ============================================================================
27
37
  export * from './files/file-operations.js';
28
38
  export * from './files/sync-utils.js';
29
-
30
39
  // ============================================================================
31
- // SECURITY
40
+ // FUNCTIONAL PROGRAMMING
32
41
  // ============================================================================
33
- export * from './security/security.js';
42
+ export * from './functional.js';
34
43
  export * from './security/secret-utils.js';
35
-
36
44
  // ============================================================================
37
- // ERROR HANDLING
45
+ // SECURITY
38
46
  // ============================================================================
39
- export * from './error-handler.js';
40
-
47
+ export * from './security/security.js';
41
48
  // ============================================================================
42
49
  // VERSIONING
43
50
  // ============================================================================
44
51
  export * from './version.js';
45
-
46
- // ============================================================================
47
- // AGENTS
48
- // ============================================================================
49
- export * from './agent-enhancer.js';
50
-
51
- // ============================================================================
52
- // FUNCTIONAL PROGRAMMING
53
- // ============================================================================
54
- export * from './functional.js';
55
-
56
- // ============================================================================
57
- // SHARED UTILITIES
58
- // ============================================================================
59
- export * from '../shared/index.js';
@@ -27,10 +27,18 @@ export function detectPackageManagerFromUserAgent(): PackageManager | null {
27
27
  return null;
28
28
  }
29
29
 
30
- if (userAgent.includes('bun')) return 'bun';
31
- if (userAgent.includes('pnpm')) return 'pnpm';
32
- if (userAgent.includes('yarn')) return 'yarn';
33
- if (userAgent.includes('npm')) return 'npm';
30
+ if (userAgent.includes('bun')) {
31
+ return 'bun';
32
+ }
33
+ if (userAgent.includes('pnpm')) {
34
+ return 'pnpm';
35
+ }
36
+ if (userAgent.includes('yarn')) {
37
+ return 'yarn';
38
+ }
39
+ if (userAgent.includes('npm')) {
40
+ return 'npm';
41
+ }
34
42
 
35
43
  return null;
36
44
  }
@@ -38,7 +46,9 @@ export function detectPackageManagerFromUserAgent(): PackageManager | null {
38
46
  /**
39
47
  * Detect package manager from lock files in directory
40
48
  */
41
- export function detectPackageManagerFromLockFiles(dir: string = process.cwd()): PackageManager | null {
49
+ export function detectPackageManagerFromLockFiles(
50
+ dir: string = process.cwd()
51
+ ): PackageManager | null {
42
52
  const lockFiles: Record<PackageManager, string[]> = {
43
53
  bun: ['bun.lockb', 'bun.lock'],
44
54
  pnpm: ['pnpm-lock.yaml'],
@@ -209,11 +209,15 @@ export const commandSecurity = {
209
209
 
210
210
  try {
211
211
  return await execFileAsync(command, validatedArgs, secureOptions);
212
- } catch (error: any) {
212
+ } catch (error: unknown) {
213
213
  // Sanitize error message to prevent information disclosure
214
- const sanitizedError = new Error(`Command execution failed: ${command}`);
215
- sanitizedError.code = error.code;
216
- sanitizedError.signal = error.signal;
214
+ const err = error as NodeJS.ErrnoException & { signal?: string };
215
+ const sanitizedError = new Error(`Command execution failed: ${command}`) as Error & {
216
+ code?: string;
217
+ signal?: string;
218
+ };
219
+ sanitizedError.code = err.code;
220
+ sanitizedError.signal = err.signal;
217
221
  throw sanitizedError;
218
222
  }
219
223
  },
@@ -5,7 +5,7 @@
5
5
 
6
6
  import chalk from 'chalk';
7
7
  import inquirer from 'inquirer';
8
- import { TargetInstaller } from '../services/target-installer.js';
8
+ import type { TargetInstaller } from '../services/target-installer.js';
9
9
  import { handlePromptError } from './prompt-helpers.js';
10
10
 
11
11
  /**
@@ -51,7 +51,10 @@ export function buildAvailableTargets(installedTargets: string[]): TargetChoice[
51
51
  * @param context - Context where choice is displayed (affects status message)
52
52
  * @returns Formatted string with target name and installation status
53
53
  */
54
- export function formatTargetChoice(target: TargetChoice, context: 'execution' | 'settings'): string {
54
+ export function formatTargetChoice(
55
+ target: TargetChoice,
56
+ context: 'execution' | 'settings'
57
+ ): string {
55
58
  const status = target.installed
56
59
  ? chalk.green(' ✓ installed')
57
60
  : context === 'execution'
@@ -34,9 +34,11 @@ export function isVersionOutdated(current: string, latest: string): boolean {
34
34
  /**
35
35
  * Parse version string into components
36
36
  */
37
- export function parseVersion(version: string): { major: number; minor: number; patch: number } | null {
37
+ export function parseVersion(
38
+ version: string
39
+ ): { major: number; minor: number; patch: number } | null {
38
40
  const parts = version.split('.').map(Number);
39
- if (parts.length < 3 || parts.some(isNaN)) {
41
+ if (parts.length < 3 || parts.some(Number.isNaN)) {
40
42
  return null;
41
43
  }
42
44
  return {