@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.
- package/CHANGELOG.md +23 -0
- package/README.md +44 -0
- package/package.json +79 -73
- package/src/commands/flow/execute-v2.ts +39 -30
- package/src/commands/flow/index.ts +2 -4
- package/src/commands/flow/prompt.ts +5 -3
- package/src/commands/flow/types.ts +0 -9
- 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 +28 -15
- 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 +111 -3
- 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/execute.ts +0 -453
- package/src/commands/flow/setup.ts +0 -312
- 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/commands/run-command.ts +0 -126
- 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
|
@@ -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') {
|
package/src/config/ai-config.ts
CHANGED
|
@@ -10,12 +10,15 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import fs from 'node:fs/promises';
|
|
13
|
-
import path from 'node:path';
|
|
14
13
|
import os from 'node:os';
|
|
14
|
+
import path from 'node:path';
|
|
15
15
|
import { z } from 'zod';
|
|
16
|
-
import { type Result,
|
|
16
|
+
import { type Result, tryCatchAsync } from '../core/functional/result.js';
|
|
17
17
|
import { getAllProviders } from '../providers/index.js';
|
|
18
|
-
import type {
|
|
18
|
+
import type {
|
|
19
|
+
ProviderConfigValue as ProviderConfigValueType,
|
|
20
|
+
ProviderId,
|
|
21
|
+
} from '../types/provider.types.js';
|
|
19
22
|
|
|
20
23
|
// Re-export types for backward compatibility
|
|
21
24
|
export type { ProviderId } from '../types/provider.types.js';
|
|
@@ -39,14 +42,20 @@ export type ProviderConfigValue = ProviderConfigValueType;
|
|
|
39
42
|
* Uses generic Record for provider configs - validation happens at provider level
|
|
40
43
|
*/
|
|
41
44
|
const aiConfigSchema = z.object({
|
|
42
|
-
defaultProvider: z
|
|
45
|
+
defaultProvider: z
|
|
46
|
+
.enum(['anthropic', 'openai', 'google', 'openrouter', 'claude-code', 'zai'])
|
|
47
|
+
.optional(),
|
|
43
48
|
defaultModel: z.string().optional(),
|
|
44
|
-
providers: z
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
providers: z
|
|
50
|
+
.record(
|
|
51
|
+
z.string(),
|
|
52
|
+
z
|
|
53
|
+
.object({
|
|
54
|
+
defaultModel: z.string().optional(),
|
|
55
|
+
})
|
|
56
|
+
.passthrough() // Allow additional fields defined by provider
|
|
57
|
+
)
|
|
58
|
+
.optional(),
|
|
50
59
|
});
|
|
51
60
|
|
|
52
61
|
export type AIConfig = z.infer<typeof aiConfigSchema>;
|
|
@@ -61,7 +70,9 @@ const LOCAL_CONFIG_FILE = '.sylphx-flow/settings.local.json';
|
|
|
61
70
|
/**
|
|
62
71
|
* Get AI config file paths in priority order
|
|
63
72
|
*/
|
|
64
|
-
export const getAIConfigPaths = (
|
|
73
|
+
export const getAIConfigPaths = (
|
|
74
|
+
cwd: string = process.cwd()
|
|
75
|
+
): {
|
|
65
76
|
global: string;
|
|
66
77
|
project: string;
|
|
67
78
|
local: string;
|
|
@@ -79,8 +90,9 @@ const loadConfigFile = async (filePath: string): Promise<AIConfig | null> => {
|
|
|
79
90
|
const content = await fs.readFile(filePath, 'utf8');
|
|
80
91
|
const parsed = JSON.parse(content);
|
|
81
92
|
return aiConfigSchema.parse(parsed);
|
|
82
|
-
} catch (error:
|
|
83
|
-
|
|
93
|
+
} catch (error: unknown) {
|
|
94
|
+
const err = error as NodeJS.ErrnoException;
|
|
95
|
+
if (err.code === 'ENOENT') {
|
|
84
96
|
return null; // File doesn't exist
|
|
85
97
|
}
|
|
86
98
|
throw error; // Re-throw other errors
|
|
@@ -97,7 +109,7 @@ const mergeConfigs = (a: AIConfig, b: AIConfig): AIConfig => {
|
|
|
97
109
|
...Object.keys(b.providers || {}),
|
|
98
110
|
]);
|
|
99
111
|
|
|
100
|
-
const mergedProviders: Record<string,
|
|
112
|
+
const mergedProviders: Record<string, Record<string, unknown>> = {};
|
|
101
113
|
for (const providerId of allProviderIds) {
|
|
102
114
|
mergedProviders[providerId] = {
|
|
103
115
|
...a.providers?.[providerId],
|
|
@@ -117,30 +129,31 @@ const mergeConfigs = (a: AIConfig, b: AIConfig): AIConfig => {
|
|
|
117
129
|
*/
|
|
118
130
|
export const aiConfigExists = async (cwd: string = process.cwd()): Promise<boolean> => {
|
|
119
131
|
const paths = getAIConfigPaths(cwd);
|
|
120
|
-
try {
|
|
121
|
-
// Check any of the config files
|
|
122
|
-
await fs.access(paths.global).catch(() => {});
|
|
123
|
-
return true;
|
|
124
|
-
} catch {}
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
await fs.access(paths.project);
|
|
128
|
-
return true;
|
|
129
|
-
} catch {}
|
|
130
132
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
// Check if any config file exists
|
|
134
|
+
const checks = await Promise.all([
|
|
135
|
+
fs
|
|
136
|
+
.access(paths.global)
|
|
137
|
+
.then(() => true)
|
|
138
|
+
.catch(() => false),
|
|
139
|
+
fs
|
|
140
|
+
.access(paths.project)
|
|
141
|
+
.then(() => true)
|
|
142
|
+
.catch(() => false),
|
|
143
|
+
fs
|
|
144
|
+
.access(paths.local)
|
|
145
|
+
.then(() => true)
|
|
146
|
+
.catch(() => false),
|
|
147
|
+
]);
|
|
135
148
|
|
|
136
|
-
return
|
|
149
|
+
return checks.some(Boolean);
|
|
137
150
|
};
|
|
138
151
|
|
|
139
152
|
/**
|
|
140
153
|
* Load AI configuration
|
|
141
154
|
* Merges global, project, and local configs with priority: local > project > global
|
|
142
155
|
*/
|
|
143
|
-
export const loadAIConfig =
|
|
156
|
+
export const loadAIConfig = (cwd: string = process.cwd()): Promise<Result<AIConfig, Error>> => {
|
|
144
157
|
return tryCatchAsync(
|
|
145
158
|
async () => {
|
|
146
159
|
const paths = getAIConfigPaths(cwd);
|
|
@@ -156,13 +169,19 @@ export const loadAIConfig = async (cwd: string = process.cwd()): Promise<Result<
|
|
|
156
169
|
let merged: AIConfig = {};
|
|
157
170
|
|
|
158
171
|
// Merge in priority order: global < project < local
|
|
159
|
-
if (globalConfig)
|
|
160
|
-
|
|
161
|
-
|
|
172
|
+
if (globalConfig) {
|
|
173
|
+
merged = mergeConfigs(merged, globalConfig);
|
|
174
|
+
}
|
|
175
|
+
if (projectConfig) {
|
|
176
|
+
merged = mergeConfigs(merged, projectConfig);
|
|
177
|
+
}
|
|
178
|
+
if (localConfig) {
|
|
179
|
+
merged = mergeConfigs(merged, localConfig);
|
|
180
|
+
}
|
|
162
181
|
|
|
163
182
|
return merged;
|
|
164
183
|
},
|
|
165
|
-
(error:
|
|
184
|
+
(error: unknown) => new Error(`Failed to load AI config: ${(error as Error).message}`)
|
|
166
185
|
);
|
|
167
186
|
};
|
|
168
187
|
|
|
@@ -171,7 +190,7 @@ export const loadAIConfig = async (cwd: string = process.cwd()): Promise<Result<
|
|
|
171
190
|
* By default, all configuration (including API keys) goes to ~/.sylphx-flow/settings.json
|
|
172
191
|
* Automatically sets default provider if not set
|
|
173
192
|
*/
|
|
174
|
-
export const saveAIConfig =
|
|
193
|
+
export const saveAIConfig = (
|
|
175
194
|
config: AIConfig,
|
|
176
195
|
cwd: string = process.cwd()
|
|
177
196
|
): Promise<Result<void, Error>> => {
|
|
@@ -211,16 +230,16 @@ export const saveAIConfig = async (
|
|
|
211
230
|
const validated = aiConfigSchema.parse(configToSave);
|
|
212
231
|
|
|
213
232
|
// Write config
|
|
214
|
-
await fs.writeFile(configPath, JSON.stringify(validated, null, 2)
|
|
233
|
+
await fs.writeFile(configPath, `${JSON.stringify(validated, null, 2)}\n`, 'utf8');
|
|
215
234
|
},
|
|
216
|
-
(error:
|
|
235
|
+
(error: unknown) => new Error(`Failed to save AI config: ${(error as Error).message}`)
|
|
217
236
|
);
|
|
218
237
|
};
|
|
219
238
|
|
|
220
239
|
/**
|
|
221
240
|
* Save AI configuration to a specific location
|
|
222
241
|
*/
|
|
223
|
-
export const saveAIConfigTo =
|
|
242
|
+
export const saveAIConfigTo = (
|
|
224
243
|
config: AIConfig,
|
|
225
244
|
location: 'global' | 'project' | 'local',
|
|
226
245
|
cwd: string = process.cwd()
|
|
@@ -237,9 +256,10 @@ export const saveAIConfigTo = async (
|
|
|
237
256
|
const validated = aiConfigSchema.parse(config);
|
|
238
257
|
|
|
239
258
|
// Write config
|
|
240
|
-
await fs.writeFile(configPath, JSON.stringify(validated, null, 2)
|
|
259
|
+
await fs.writeFile(configPath, `${JSON.stringify(validated, null, 2)}\n`, 'utf8');
|
|
241
260
|
},
|
|
242
|
-
(error:
|
|
261
|
+
(error: unknown) =>
|
|
262
|
+
new Error(`Failed to save AI config to ${location}: ${(error as Error).message}`)
|
|
243
263
|
);
|
|
244
264
|
};
|
|
245
265
|
|
|
@@ -306,4 +326,3 @@ export const getConfiguredProviders = async (
|
|
|
306
326
|
|
|
307
327
|
return providers;
|
|
308
328
|
};
|
|
309
|
-
|
package/src/core/agent-loader.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Loads agent definitions from markdown files with front matter
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import { join, parse, relative, dirname } from 'node:path';
|
|
6
|
+
import { access, readdir, readFile } from 'node:fs/promises';
|
|
8
7
|
import { homedir } from 'node:os';
|
|
8
|
+
import { dirname, join, parse, relative } from 'node:path';
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
10
10
|
import matter from 'gray-matter';
|
|
11
11
|
import type { Agent, AgentMetadata } from '../types/agent.types.js';
|
|
@@ -52,7 +52,10 @@ export async function loadAgentFromFile(
|
|
|
52
52
|
/**
|
|
53
53
|
* Load all agents from a directory (recursively)
|
|
54
54
|
*/
|
|
55
|
-
export async function loadAgentsFromDirectory(
|
|
55
|
+
export async function loadAgentsFromDirectory(
|
|
56
|
+
dirPath: string,
|
|
57
|
+
isBuiltin: boolean = false
|
|
58
|
+
): Promise<Agent[]> {
|
|
56
59
|
try {
|
|
57
60
|
// Read directory recursively to support subdirectories
|
|
58
61
|
const files = await readdir(dirPath, { recursive: true, withFileTypes: true });
|
|
@@ -72,7 +75,7 @@ export async function loadAgentsFromDirectory(dirPath: string, isBuiltin: boolea
|
|
|
72
75
|
);
|
|
73
76
|
|
|
74
77
|
return agents.filter((agent): agent is Agent => agent !== null);
|
|
75
|
-
} catch (
|
|
78
|
+
} catch (_error) {
|
|
76
79
|
// Directory doesn't exist or can't be read
|
|
77
80
|
return [];
|
|
78
81
|
}
|
|
@@ -117,7 +120,7 @@ export async function loadAllAgents(cwd: string, targetAgentDir?: string): Promi
|
|
|
117
120
|
const systemPath = await getSystemAgentsPath();
|
|
118
121
|
const [globalPath, projectPath] = getAgentSearchPaths(cwd);
|
|
119
122
|
|
|
120
|
-
|
|
123
|
+
const allAgentPaths = [systemPath, globalPath, projectPath];
|
|
121
124
|
|
|
122
125
|
// If a target-specific agent directory is provided, add it with highest priority
|
|
123
126
|
if (targetAgentDir) {
|
|
@@ -126,7 +129,9 @@ export async function loadAllAgents(cwd: string, targetAgentDir?: string): Promi
|
|
|
126
129
|
}
|
|
127
130
|
|
|
128
131
|
// Load agents from all paths
|
|
129
|
-
const loadedAgentsPromises = allAgentPaths.map(path =>
|
|
132
|
+
const loadedAgentsPromises = allAgentPaths.map((path) =>
|
|
133
|
+
loadAgentsFromDirectory(path, path === systemPath)
|
|
134
|
+
);
|
|
130
135
|
const loadedAgentsArrays = await Promise.all(loadedAgentsPromises);
|
|
131
136
|
|
|
132
137
|
// Flatten and deduplicate
|