@sylphx/flow 1.0.1 → 1.0.3
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/package.json +10 -9
- package/src/commands/codebase-command.ts +168 -0
- package/src/commands/flow-command.ts +1137 -0
- package/src/commands/flow-orchestrator.ts +296 -0
- package/src/commands/hook-command.ts +444 -0
- package/src/commands/init-command.ts +92 -0
- package/src/commands/init-core.ts +322 -0
- package/src/commands/knowledge-command.ts +161 -0
- package/src/commands/run-command.ts +120 -0
- package/src/components/benchmark-monitor.tsx +331 -0
- package/src/components/reindex-progress.tsx +261 -0
- package/src/composables/functional/index.ts +14 -0
- package/src/composables/functional/useEnvironment.ts +171 -0
- package/src/composables/functional/useFileSystem.ts +139 -0
- package/src/composables/index.ts +5 -0
- package/src/composables/useEnv.ts +13 -0
- package/src/composables/useRuntimeConfig.ts +27 -0
- package/src/composables/useTargetConfig.ts +45 -0
- package/src/config/ai-config.ts +376 -0
- package/src/config/constants.ts +35 -0
- package/src/config/index.ts +27 -0
- package/src/config/rules.ts +43 -0
- package/src/config/servers.ts +371 -0
- package/src/config/targets.ts +126 -0
- package/src/core/agent-loader.ts +141 -0
- package/src/core/agent-manager.ts +174 -0
- package/src/core/ai-sdk.ts +603 -0
- package/src/core/app-factory.ts +381 -0
- package/src/core/builtin-agents.ts +9 -0
- package/src/core/command-system.ts +550 -0
- package/src/core/config-system.ts +550 -0
- package/src/core/connection-pool.ts +390 -0
- package/src/core/di-container.ts +155 -0
- package/src/core/error-handling.ts +519 -0
- package/src/core/formatting/bytes.test.ts +115 -0
- package/src/core/formatting/bytes.ts +64 -0
- package/src/core/functional/async.ts +313 -0
- package/src/core/functional/either.ts +109 -0
- package/src/core/functional/error-handler.ts +135 -0
- package/src/core/functional/error-types.ts +311 -0
- package/src/core/functional/index.ts +19 -0
- package/src/core/functional/option.ts +142 -0
- package/src/core/functional/pipe.ts +189 -0
- package/src/core/functional/result.ts +204 -0
- package/src/core/functional/validation.ts +138 -0
- package/src/core/headless-display.ts +96 -0
- package/src/core/index.ts +6 -0
- package/src/core/installers/file-installer.ts +303 -0
- package/src/core/installers/mcp-installer.ts +213 -0
- package/src/core/interfaces/index.ts +22 -0
- package/src/core/interfaces/repository.interface.ts +91 -0
- package/src/core/interfaces/service.interface.ts +133 -0
- package/src/core/interfaces.ts +129 -0
- package/src/core/loop-controller.ts +200 -0
- package/src/core/result.ts +351 -0
- package/src/core/rule-loader.ts +147 -0
- package/src/core/rule-manager.ts +240 -0
- package/src/core/service-config.ts +252 -0
- package/src/core/session-service.ts +121 -0
- package/src/core/state-detector.ts +389 -0
- package/src/core/storage-factory.ts +115 -0
- package/src/core/stream-handler.ts +288 -0
- package/src/core/target-manager.ts +161 -0
- package/src/core/type-utils.ts +427 -0
- package/src/core/unified-storage.ts +456 -0
- package/src/core/upgrade-manager.ts +300 -0
- package/src/core/validation/limit.test.ts +155 -0
- package/src/core/validation/limit.ts +46 -0
- package/src/core/validation/query.test.ts +44 -0
- package/src/core/validation/query.ts +20 -0
- package/src/db/auto-migrate.ts +322 -0
- package/src/db/base-database-client.ts +144 -0
- package/src/db/cache-db.ts +218 -0
- package/src/db/cache-schema.ts +75 -0
- package/src/db/database.ts +70 -0
- package/src/db/index.ts +252 -0
- package/src/db/memory-db.ts +153 -0
- package/src/db/memory-schema.ts +29 -0
- package/src/db/schema.ts +289 -0
- package/src/db/session-repository.ts +733 -0
- package/src/domains/codebase/index.ts +5 -0
- package/src/domains/codebase/tools.ts +139 -0
- package/src/domains/index.ts +8 -0
- package/src/domains/knowledge/index.ts +10 -0
- package/src/domains/knowledge/resources.ts +537 -0
- package/src/domains/knowledge/tools.ts +174 -0
- package/src/domains/utilities/index.ts +6 -0
- package/src/domains/utilities/time/index.ts +5 -0
- package/src/domains/utilities/time/tools.ts +291 -0
- package/src/index.ts +211 -0
- package/src/services/agent-service.ts +273 -0
- package/src/services/claude-config-service.ts +252 -0
- package/src/services/config-service.ts +258 -0
- package/src/services/evaluation-service.ts +271 -0
- package/src/services/functional/evaluation-logic.ts +296 -0
- package/src/services/functional/file-processor.ts +273 -0
- package/src/services/functional/index.ts +12 -0
- package/src/services/index.ts +13 -0
- package/src/services/mcp-service.ts +432 -0
- package/src/services/memory.service.ts +476 -0
- package/src/services/search/base-indexer.ts +156 -0
- package/src/services/search/codebase-indexer-types.ts +38 -0
- package/src/services/search/codebase-indexer.ts +647 -0
- package/src/services/search/embeddings-provider.ts +455 -0
- package/src/services/search/embeddings.ts +316 -0
- package/src/services/search/functional-indexer.ts +323 -0
- package/src/services/search/index.ts +27 -0
- package/src/services/search/indexer.ts +380 -0
- package/src/services/search/knowledge-indexer.ts +422 -0
- package/src/services/search/semantic-search.ts +244 -0
- package/src/services/search/tfidf.ts +559 -0
- package/src/services/search/unified-search-service.ts +888 -0
- package/src/services/smart-config-service.ts +385 -0
- package/src/services/storage/cache-storage.ts +487 -0
- package/src/services/storage/drizzle-storage.ts +581 -0
- package/src/services/storage/index.ts +15 -0
- package/src/services/storage/lancedb-vector-storage.ts +494 -0
- package/src/services/storage/memory-storage.ts +268 -0
- package/src/services/storage/separated-storage.ts +467 -0
- package/src/services/storage/vector-storage.ts +13 -0
- package/src/shared/agents/index.ts +63 -0
- package/src/shared/files/index.ts +99 -0
- package/src/shared/index.ts +32 -0
- package/src/shared/logging/index.ts +24 -0
- package/src/shared/processing/index.ts +153 -0
- package/src/shared/types/index.ts +25 -0
- package/src/targets/claude-code.ts +574 -0
- package/src/targets/functional/claude-code-logic.ts +185 -0
- package/src/targets/functional/index.ts +6 -0
- package/src/targets/opencode.ts +529 -0
- package/src/types/agent.types.ts +32 -0
- package/src/types/api/batch.ts +108 -0
- package/src/types/api/errors.ts +118 -0
- package/src/types/api/index.ts +55 -0
- package/src/types/api/requests.ts +76 -0
- package/src/types/api/responses.ts +180 -0
- package/src/types/api/websockets.ts +85 -0
- package/src/types/api.types.ts +9 -0
- package/src/types/benchmark.ts +49 -0
- package/src/types/cli.types.ts +87 -0
- package/src/types/common.types.ts +35 -0
- package/src/types/database.types.ts +510 -0
- package/src/types/mcp-config.types.ts +448 -0
- package/src/types/mcp.types.ts +69 -0
- package/src/types/memory-types.ts +63 -0
- package/src/types/provider.types.ts +28 -0
- package/src/types/rule.types.ts +24 -0
- package/src/types/session.types.ts +214 -0
- package/src/types/target-config.types.ts +295 -0
- package/src/types/target.types.ts +140 -0
- package/src/types/todo.types.ts +25 -0
- package/src/types.ts +40 -0
- package/src/utils/advanced-tokenizer.ts +191 -0
- package/src/utils/agent-enhancer.ts +114 -0
- package/src/utils/ai-model-fetcher.ts +19 -0
- package/src/utils/async-file-operations.ts +516 -0
- package/src/utils/audio-player.ts +345 -0
- package/src/utils/cli-output.ts +266 -0
- package/src/utils/codebase-helpers.ts +211 -0
- package/src/utils/console-ui.ts +79 -0
- package/src/utils/database-errors.ts +140 -0
- package/src/utils/debug-logger.ts +49 -0
- package/src/utils/error-handler.ts +53 -0
- package/src/utils/file-operations.ts +310 -0
- package/src/utils/file-scanner.ts +259 -0
- package/src/utils/functional/array.ts +355 -0
- package/src/utils/functional/index.ts +15 -0
- package/src/utils/functional/object.ts +279 -0
- package/src/utils/functional/string.ts +281 -0
- package/src/utils/functional.ts +543 -0
- package/src/utils/help.ts +20 -0
- package/src/utils/immutable-cache.ts +106 -0
- package/src/utils/index.ts +78 -0
- package/src/utils/jsonc.ts +158 -0
- package/src/utils/logger.ts +396 -0
- package/src/utils/mcp-config.ts +249 -0
- package/src/utils/memory-tui.ts +414 -0
- package/src/utils/models-dev.ts +91 -0
- package/src/utils/notifications.ts +169 -0
- package/src/utils/object-utils.ts +51 -0
- package/src/utils/parallel-operations.ts +487 -0
- package/src/utils/paths.ts +143 -0
- package/src/utils/process-manager.ts +155 -0
- package/src/utils/prompts.ts +120 -0
- package/src/utils/search-tool-builder.ts +214 -0
- package/src/utils/secret-utils.ts +179 -0
- package/src/utils/security.ts +537 -0
- package/src/utils/session-manager.ts +168 -0
- package/src/utils/session-title.ts +87 -0
- package/src/utils/settings.ts +182 -0
- package/src/utils/simplified-errors.ts +410 -0
- package/src/utils/sync-utils.ts +159 -0
- package/src/utils/target-config.ts +570 -0
- package/src/utils/target-utils.ts +394 -0
- package/src/utils/template-engine.ts +94 -0
- package/src/utils/test-audio.ts +71 -0
- package/src/utils/todo-context.ts +46 -0
- package/src/utils/token-counter.ts +288 -0
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -59554
- package/dist/lancedb.linux-x64-gnu-b7f0jgsz.node +0 -0
- package/dist/lancedb.linux-x64-musl-tgcv22rx.node +0 -0
- package/dist/shared/chunk-25dwp0dp.js +0 -89
- package/dist/shared/chunk-3pjb6063.js +0 -208
- package/dist/shared/chunk-4d6ydpw7.js +0 -2854
- package/dist/shared/chunk-4wjcadjk.js +0 -225
- package/dist/shared/chunk-5j4w74t6.js +0 -30
- package/dist/shared/chunk-5j8m3dh3.js +0 -58
- package/dist/shared/chunk-5thh3qem.js +0 -91
- package/dist/shared/chunk-6g9xy73m.js +0 -252
- package/dist/shared/chunk-7eq34c42.js +0 -23
- package/dist/shared/chunk-c2gwgx3r.js +0 -115
- package/dist/shared/chunk-cjd3mk4c.js +0 -1320
- package/dist/shared/chunk-g5cv6703.js +0 -368
- package/dist/shared/chunk-hpkhykhq.js +0 -574
- package/dist/shared/chunk-m2322pdk.js +0 -122
- package/dist/shared/chunk-nd5fdvaq.js +0 -26
- package/dist/shared/chunk-pgd3m6zf.js +0 -108
- package/dist/shared/chunk-qk8n91hw.js +0 -494
- package/dist/shared/chunk-rkkn8szp.js +0 -16855
- package/dist/shared/chunk-t16rfxh0.js +0 -61
- package/dist/shared/chunk-t4fbfa5v.js +0 -19
- package/dist/shared/chunk-t77h86w6.js +0 -276
- package/dist/shared/chunk-v0ez4aef.js +0 -71
- package/dist/shared/chunk-v29j2r3s.js +0 -32051
- package/dist/shared/chunk-vfbc6ew5.js +0 -765
- package/dist/shared/chunk-vmeqwm1c.js +0 -204
- package/dist/shared/chunk-x66eh37x.js +0 -137
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import type { MCPServerConfigUnion } from '../types.js';
|
|
2
|
+
import { envSecurity, securitySchemas } from '../utils/security.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Central MCP server registry for Sylphx Flow
|
|
6
|
+
* This is the single source of truth for all available MCP servers
|
|
7
|
+
*/
|
|
8
|
+
export interface EnvVarConfig {
|
|
9
|
+
/** Description of what this environment variable does */
|
|
10
|
+
description: string;
|
|
11
|
+
/** Whether this environment variable is required or optional */
|
|
12
|
+
required: boolean;
|
|
13
|
+
/** Default value (if any) */
|
|
14
|
+
default?: string;
|
|
15
|
+
/** Whether this environment variable contains sensitive data and should be stored as secret */
|
|
16
|
+
secret?: boolean;
|
|
17
|
+
/** Async function to fetch choices for list inputs */
|
|
18
|
+
fetchChoices?: () => Promise<string[]>;
|
|
19
|
+
/** Environment variables this field depends on */
|
|
20
|
+
dependsOn?: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface MCPServerDefinition {
|
|
24
|
+
/** Internal identifier used in CLI commands */
|
|
25
|
+
id: string;
|
|
26
|
+
/** Display name for the server */
|
|
27
|
+
name: string;
|
|
28
|
+
/** Human-readable description */
|
|
29
|
+
description: string;
|
|
30
|
+
/** MCP server configuration */
|
|
31
|
+
config: MCPServerConfigUnion;
|
|
32
|
+
/** Environment variables configuration */
|
|
33
|
+
envVars?: Record<string, EnvVarConfig>;
|
|
34
|
+
/** Server category for grouping */
|
|
35
|
+
category: 'core' | 'external' | 'ai';
|
|
36
|
+
/** Whether this server is included by default in init */
|
|
37
|
+
defaultInInit?: boolean;
|
|
38
|
+
/** Whether this server is required and cannot be uninstalled */
|
|
39
|
+
required?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Central registry of all available MCP servers
|
|
44
|
+
* This replaces all hardcoded server lists throughout the codebase
|
|
45
|
+
*/
|
|
46
|
+
export const MCP_SERVER_REGISTRY: Record<string, MCPServerDefinition> = {
|
|
47
|
+
'sylphx-flow': {
|
|
48
|
+
id: 'sylphx-flow',
|
|
49
|
+
name: 'sylphx-flow',
|
|
50
|
+
description: 'Sylphx Flow MCP server for agent coordination and memory management',
|
|
51
|
+
config: {
|
|
52
|
+
type: 'stdio' as const,
|
|
53
|
+
command: 'sylphx-flow',
|
|
54
|
+
args: ['mcp', 'start'],
|
|
55
|
+
env: {
|
|
56
|
+
OPENAI_API_KEY: '',
|
|
57
|
+
OPENAI_BASE_URL: 'https://api.openai.com/v1',
|
|
58
|
+
EMBEDDING_MODEL: 'text-embedding-3-small',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
envVars: {
|
|
62
|
+
OPENAI_API_KEY: {
|
|
63
|
+
description: 'OpenAI API key for vector search embeddings',
|
|
64
|
+
required: false,
|
|
65
|
+
secret: true,
|
|
66
|
+
},
|
|
67
|
+
OPENAI_BASE_URL: {
|
|
68
|
+
description: 'Base URL for OpenAI-compatible embedding API',
|
|
69
|
+
required: false,
|
|
70
|
+
default: 'https://api.openai.com/v1',
|
|
71
|
+
},
|
|
72
|
+
EMBEDDING_MODEL: {
|
|
73
|
+
description: 'Embedding model to use for vector search',
|
|
74
|
+
required: false,
|
|
75
|
+
default: 'text-embedding-3-small',
|
|
76
|
+
dependsOn: ['OPENAI_API_KEY', 'OPENAI_BASE_URL'],
|
|
77
|
+
fetchChoices: async () => {
|
|
78
|
+
// Validate environment variables
|
|
79
|
+
const validatedBaseUrl = envSecurity.getEnvVar(
|
|
80
|
+
'OPENAI_BASE_URL',
|
|
81
|
+
'https://api.openai.com/v1'
|
|
82
|
+
);
|
|
83
|
+
const validatedApiKey = envSecurity.getEnvVar('OPENAI_API_KEY');
|
|
84
|
+
|
|
85
|
+
if (!validatedApiKey) {
|
|
86
|
+
throw new Error('OPENAI_API_KEY is required to fetch embedding models');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Additional validation for API key format
|
|
90
|
+
try {
|
|
91
|
+
securitySchemas.apiKey.parse(validatedApiKey);
|
|
92
|
+
} catch (_error) {
|
|
93
|
+
throw new Error('Invalid OPENAI_API_KEY format');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const response = await fetch(`${validatedBaseUrl}/models`, {
|
|
97
|
+
headers: { Authorization: `Bearer ${validatedApiKey}` },
|
|
98
|
+
timeout: 10000, // Add timeout to prevent hanging
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
throw new Error(`Failed to fetch models: ${response.statusText}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const data = await response.json();
|
|
106
|
+
const embeddingModels = data.data
|
|
107
|
+
.filter((m: { id: string }) => m.id.includes('embedding'))
|
|
108
|
+
.map((m: { id: string }) => m.id)
|
|
109
|
+
.sort();
|
|
110
|
+
|
|
111
|
+
if (embeddingModels.length === 0) {
|
|
112
|
+
throw new Error('No embedding models found');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return embeddingModels;
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
category: 'core',
|
|
120
|
+
defaultInInit: true,
|
|
121
|
+
required: true,
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
'gpt-image': {
|
|
125
|
+
id: 'gpt-image',
|
|
126
|
+
name: 'gpt-image-1-mcp',
|
|
127
|
+
description: 'GPT Image generation MCP server',
|
|
128
|
+
config: {
|
|
129
|
+
type: 'stdio' as const,
|
|
130
|
+
command: 'npx',
|
|
131
|
+
args: ['@napolab/gpt-image-1-mcp'],
|
|
132
|
+
env: { OPENAI_API_KEY: '' },
|
|
133
|
+
},
|
|
134
|
+
envVars: {
|
|
135
|
+
OPENAI_API_KEY: {
|
|
136
|
+
description: 'OpenAI API key for image generation',
|
|
137
|
+
required: true,
|
|
138
|
+
secret: true,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
category: 'ai',
|
|
142
|
+
defaultInInit: false,
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
perplexity: {
|
|
146
|
+
id: 'perplexity',
|
|
147
|
+
name: 'perplexity-ask',
|
|
148
|
+
description: 'Perplexity Ask MCP server for search and queries',
|
|
149
|
+
config: {
|
|
150
|
+
type: 'stdio' as const,
|
|
151
|
+
command: 'npx',
|
|
152
|
+
args: ['-y', 'server-perplexity-ask'],
|
|
153
|
+
env: { PERPLEXITY_API_KEY: '' },
|
|
154
|
+
},
|
|
155
|
+
envVars: {
|
|
156
|
+
PERPLEXITY_API_KEY: {
|
|
157
|
+
description: 'Perplexity API key for search and queries',
|
|
158
|
+
required: true,
|
|
159
|
+
secret: true,
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
category: 'ai',
|
|
163
|
+
defaultInInit: false,
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
context7: {
|
|
167
|
+
id: 'context7',
|
|
168
|
+
name: 'context7',
|
|
169
|
+
description: 'Context7 HTTP MCP server for documentation retrieval',
|
|
170
|
+
config: {
|
|
171
|
+
type: 'http' as const,
|
|
172
|
+
url: 'https://mcp.context7.com/mcp',
|
|
173
|
+
},
|
|
174
|
+
envVars: {
|
|
175
|
+
CONTEXT7_API_KEY: {
|
|
176
|
+
description: 'Context7 API key for enhanced documentation access',
|
|
177
|
+
required: false,
|
|
178
|
+
secret: true,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
category: 'external',
|
|
182
|
+
defaultInInit: true,
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
'gemini-search': {
|
|
186
|
+
id: 'gemini-search',
|
|
187
|
+
name: 'gemini-google-search',
|
|
188
|
+
description: 'Gemini Google Search MCP server',
|
|
189
|
+
config: {
|
|
190
|
+
type: 'stdio' as const,
|
|
191
|
+
command: 'npx',
|
|
192
|
+
args: ['-y', 'mcp-gemini-google-search'],
|
|
193
|
+
env: { GEMINI_API_KEY: '', GEMINI_MODEL: 'gemini-2.5-flash' },
|
|
194
|
+
},
|
|
195
|
+
envVars: {
|
|
196
|
+
GEMINI_API_KEY: {
|
|
197
|
+
description: 'Google Gemini API key for search functionality',
|
|
198
|
+
required: true,
|
|
199
|
+
secret: true,
|
|
200
|
+
},
|
|
201
|
+
GEMINI_MODEL: {
|
|
202
|
+
description: 'Gemini model to use for search',
|
|
203
|
+
required: false,
|
|
204
|
+
default: 'gemini-2.5-flash',
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
category: 'ai',
|
|
208
|
+
defaultInInit: false,
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
grep: {
|
|
212
|
+
id: 'grep',
|
|
213
|
+
name: 'grep',
|
|
214
|
+
description: 'GitHub grep MCP server for searching GitHub repositories',
|
|
215
|
+
config: {
|
|
216
|
+
type: 'http' as const,
|
|
217
|
+
url: 'https://mcp.grep.app',
|
|
218
|
+
},
|
|
219
|
+
category: 'external',
|
|
220
|
+
defaultInInit: true,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Type for valid MCP server IDs
|
|
226
|
+
*/
|
|
227
|
+
export type MCPServerID = keyof typeof MCP_SERVER_REGISTRY;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get all available server IDs
|
|
231
|
+
*/
|
|
232
|
+
export function getAllServerIDs(): MCPServerID[] {
|
|
233
|
+
return Object.keys(MCP_SERVER_REGISTRY) as MCPServerID[];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get servers by category
|
|
238
|
+
*/
|
|
239
|
+
export function getServersByCategory(category: MCPServerDefinition['category']): MCPServerID[] {
|
|
240
|
+
return Object.entries(MCP_SERVER_REGISTRY)
|
|
241
|
+
.filter(([, server]) => server.category === category)
|
|
242
|
+
.map(([id]) => id as MCPServerID);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Get servers that are included by default in init
|
|
247
|
+
*/
|
|
248
|
+
export function getDefaultServers(): MCPServerID[] {
|
|
249
|
+
return Object.entries(MCP_SERVER_REGISTRY)
|
|
250
|
+
.filter(([, server]) => server.defaultInInit)
|
|
251
|
+
.map(([id]) => id as MCPServerID);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Get servers that require API keys
|
|
256
|
+
*/
|
|
257
|
+
export function getServersRequiringAPIKeys(): MCPServerID[] {
|
|
258
|
+
return Object.entries(MCP_SERVER_REGISTRY)
|
|
259
|
+
.filter(
|
|
260
|
+
([, server]) =>
|
|
261
|
+
server.envVars && Object.values(server.envVars).some((envVar) => envVar.required)
|
|
262
|
+
)
|
|
263
|
+
.map(([id]) => id as MCPServerID);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Get servers that have optional API keys
|
|
268
|
+
*/
|
|
269
|
+
export function getServersWithOptionalAPIKeys(): MCPServerID[] {
|
|
270
|
+
return Object.entries(MCP_SERVER_REGISTRY)
|
|
271
|
+
.filter(
|
|
272
|
+
([, server]) =>
|
|
273
|
+
server.envVars && Object.values(server.envVars).some((envVar) => !envVar.required)
|
|
274
|
+
)
|
|
275
|
+
.map(([id]) => id as MCPServerID);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Get all servers that have any API keys (required or optional)
|
|
280
|
+
*/
|
|
281
|
+
export function getServersWithAnyAPIKeys(): MCPServerID[] {
|
|
282
|
+
return Object.entries(MCP_SERVER_REGISTRY)
|
|
283
|
+
.filter(([, server]) => server.envVars && Object.keys(server.envVars).length > 0)
|
|
284
|
+
.map(([id]) => id as MCPServerID);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Get required environment variables for a server
|
|
289
|
+
*/
|
|
290
|
+
export function getRequiredEnvVars(serverId: MCPServerID): string[] {
|
|
291
|
+
const server = MCP_SERVER_REGISTRY[serverId];
|
|
292
|
+
if (!server?.envVars) {
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return Object.entries(server.envVars)
|
|
297
|
+
.filter(([, config]) => config.required)
|
|
298
|
+
.map(([name]) => name);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Get optional environment variables for a server
|
|
303
|
+
*/
|
|
304
|
+
export function getOptionalEnvVars(serverId: MCPServerID): string[] {
|
|
305
|
+
const server = MCP_SERVER_REGISTRY[serverId];
|
|
306
|
+
if (!server?.envVars) {
|
|
307
|
+
return [];
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return Object.entries(server.envVars)
|
|
311
|
+
.filter(([, config]) => !config.required)
|
|
312
|
+
.map(([name]) => name);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get all environment variables for a server
|
|
317
|
+
*/
|
|
318
|
+
export function getAllEnvVars(serverId: MCPServerID): string[] {
|
|
319
|
+
const server = MCP_SERVER_REGISTRY[serverId];
|
|
320
|
+
if (!server?.envVars) {
|
|
321
|
+
return [];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return Object.keys(server.envVars);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Get secret environment variables for a server
|
|
329
|
+
*/
|
|
330
|
+
export function getSecretEnvVars(serverId: MCPServerID): string[] {
|
|
331
|
+
const server = MCP_SERVER_REGISTRY[serverId];
|
|
332
|
+
if (!server?.envVars) {
|
|
333
|
+
return [];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return Object.entries(server.envVars)
|
|
337
|
+
.filter(([, config]) => config.secret)
|
|
338
|
+
.map(([name]) => name);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Get non-secret environment variables for a server
|
|
343
|
+
*/
|
|
344
|
+
export function getNonSecretEnvVars(serverId: MCPServerID): string[] {
|
|
345
|
+
const server = MCP_SERVER_REGISTRY[serverId];
|
|
346
|
+
if (!server?.envVars) {
|
|
347
|
+
return [];
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return Object.entries(server.envVars)
|
|
351
|
+
.filter(([, config]) => !config.secret)
|
|
352
|
+
.map(([name]) => name);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Validate server ID
|
|
357
|
+
*/
|
|
358
|
+
export function isValidServerID(id: string): id is MCPServerID {
|
|
359
|
+
return id in MCP_SERVER_REGISTRY;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Get server definition
|
|
364
|
+
*/
|
|
365
|
+
export function getServerDefinition(id: MCPServerID): MCPServerDefinition {
|
|
366
|
+
const server = MCP_SERVER_REGISTRY[id];
|
|
367
|
+
if (!server) {
|
|
368
|
+
throw new Error(`Unknown MCP server: ${id}`);
|
|
369
|
+
}
|
|
370
|
+
return server;
|
|
371
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Target registry - functional implementation with composition
|
|
3
|
+
* Pure functions operating on immutable data
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Option } from '../core/functional/option.js';
|
|
7
|
+
import { none, some } from '../core/functional/option.js';
|
|
8
|
+
import { claudeCodeTarget } from '../targets/claude-code.js';
|
|
9
|
+
import { opencodeTarget } from '../targets/opencode.js';
|
|
10
|
+
import type { Target } from '../types.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* All available targets
|
|
14
|
+
* Lazy-initialized to avoid circular dependencies
|
|
15
|
+
*/
|
|
16
|
+
let cachedTargets: readonly Target[] | null = null;
|
|
17
|
+
|
|
18
|
+
const initializeTargets = (): readonly Target[] => {
|
|
19
|
+
if (cachedTargets) {
|
|
20
|
+
return cachedTargets;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
cachedTargets = Object.freeze([opencodeTarget, claudeCodeTarget]);
|
|
24
|
+
|
|
25
|
+
return cachedTargets;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get all targets
|
|
30
|
+
*/
|
|
31
|
+
export const getAllTargets = (): readonly Target[] => initializeTargets();
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get implemented targets only
|
|
35
|
+
*/
|
|
36
|
+
export const getImplementedTargets = (): readonly Target[] =>
|
|
37
|
+
getAllTargets().filter((target) => target.isImplemented);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get all target IDs
|
|
41
|
+
*/
|
|
42
|
+
export const getAllTargetIDs = (): readonly string[] => getAllTargets().map((target) => target.id);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get implemented target IDs
|
|
46
|
+
*/
|
|
47
|
+
export const getImplementedTargetIDs = (): readonly string[] =>
|
|
48
|
+
getImplementedTargets().map((target) => target.id);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get target by ID
|
|
52
|
+
* Returns Option type for explicit null handling
|
|
53
|
+
*/
|
|
54
|
+
export const getTarget = (id: string): Option<Target> => {
|
|
55
|
+
const target = getAllTargets().find((t) => t.id === id);
|
|
56
|
+
return target ? some(target) : none;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get target by ID (unsafe - throws if not found)
|
|
61
|
+
* Use getTarget() for safer alternative with Option type
|
|
62
|
+
*/
|
|
63
|
+
export const getTargetUnsafe = (id: string): Target => {
|
|
64
|
+
const target = getAllTargets().find((t) => t.id === id);
|
|
65
|
+
if (!target) {
|
|
66
|
+
throw new Error(`Target not found: ${id}`);
|
|
67
|
+
}
|
|
68
|
+
return target;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get default target
|
|
73
|
+
* Returns Option type for explicit null handling
|
|
74
|
+
*/
|
|
75
|
+
export const getDefaultTarget = (): Option<Target> => {
|
|
76
|
+
const target = getAllTargets().find((t) => t.isDefault);
|
|
77
|
+
return target ? some(target) : none;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get default target (unsafe - throws if not found)
|
|
82
|
+
* Use getDefaultTarget() for safer alternative with Option type
|
|
83
|
+
*/
|
|
84
|
+
export const getDefaultTargetUnsafe = (): Target => {
|
|
85
|
+
const target = getAllTargets().find((t) => t.isDefault);
|
|
86
|
+
if (!target) {
|
|
87
|
+
throw new Error('No default target configured');
|
|
88
|
+
}
|
|
89
|
+
return target;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get targets that support MCP servers
|
|
94
|
+
*/
|
|
95
|
+
export const getTargetsWithMCPSupport = (): readonly Target[] =>
|
|
96
|
+
getImplementedTargets().filter((target) => !!target.setupMCP);
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get targets that support command execution (agent running)
|
|
100
|
+
*/
|
|
101
|
+
export const getTargetsWithCommandSupport = (): readonly Target[] =>
|
|
102
|
+
getImplementedTargets().filter((target) => !!target.executeCommand);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check if target is implemented
|
|
106
|
+
*/
|
|
107
|
+
export const isTargetImplemented = (id: string): boolean => {
|
|
108
|
+
const target = getAllTargets().find((t) => t.id === id);
|
|
109
|
+
return target?.isImplemented ?? false;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Utility type for target IDs
|
|
114
|
+
*/
|
|
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;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Loader
|
|
3
|
+
* Loads agent definitions from markdown files with front matter
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFile, readdir, access } from 'node:fs/promises';
|
|
7
|
+
import { join, parse, relative, dirname } from 'node:path';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import matter from 'gray-matter';
|
|
11
|
+
import type { Agent, AgentMetadata } from '../types/agent.types.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Load a single agent from a markdown file
|
|
15
|
+
*/
|
|
16
|
+
export async function loadAgentFromFile(
|
|
17
|
+
filePath: string,
|
|
18
|
+
isBuiltin: boolean = false,
|
|
19
|
+
agentId?: string
|
|
20
|
+
): Promise<Agent | null> {
|
|
21
|
+
try {
|
|
22
|
+
const content = await readFile(filePath, 'utf-8');
|
|
23
|
+
const { data, content: systemPrompt } = matter(content);
|
|
24
|
+
|
|
25
|
+
// Validate front matter
|
|
26
|
+
if (!data.name || typeof data.name !== 'string') {
|
|
27
|
+
console.error(`Agent file ${filePath} missing required 'name' field`);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const metadata: AgentMetadata = {
|
|
32
|
+
name: data.name,
|
|
33
|
+
description: data.description || '',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Get agent ID from parameter or filename
|
|
37
|
+
const id = agentId || parse(filePath).name;
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
id,
|
|
41
|
+
metadata,
|
|
42
|
+
systemPrompt: systemPrompt.trim(),
|
|
43
|
+
isBuiltin,
|
|
44
|
+
filePath,
|
|
45
|
+
};
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(`Failed to load agent from ${filePath}:`, error);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Load all agents from a directory (recursively)
|
|
54
|
+
*/
|
|
55
|
+
export async function loadAgentsFromDirectory(dirPath: string, isBuiltin: boolean = false): Promise<Agent[]> {
|
|
56
|
+
try {
|
|
57
|
+
// Read directory recursively to support subdirectories
|
|
58
|
+
const files = await readdir(dirPath, { recursive: true, withFileTypes: true });
|
|
59
|
+
|
|
60
|
+
// Filter for .md files and calculate agent IDs from relative paths
|
|
61
|
+
const agentFiles = files
|
|
62
|
+
.filter((f) => f.isFile() && f.name.endsWith('.md'))
|
|
63
|
+
.map((f) => {
|
|
64
|
+
const fullPath = join(f.parentPath || f.path, f.name);
|
|
65
|
+
// Calculate relative path from dirPath and remove .md extension
|
|
66
|
+
const relativePath = relative(dirPath, fullPath).replace(/\.md$/, '');
|
|
67
|
+
return { fullPath, agentId: relativePath };
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const agents = await Promise.all(
|
|
71
|
+
agentFiles.map(({ fullPath, agentId }) => loadAgentFromFile(fullPath, isBuiltin, agentId))
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return agents.filter((agent): agent is Agent => agent !== null);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
// Directory doesn't exist or can't be read
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get system agents path (bundled with the app)
|
|
83
|
+
*/
|
|
84
|
+
export async function getSystemAgentsPath(): Promise<string> {
|
|
85
|
+
// Get the directory of the current module (cross-platform)
|
|
86
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
87
|
+
const currentDir = dirname(currentFile);
|
|
88
|
+
|
|
89
|
+
// In production (dist), assets are at dist/assets/agents
|
|
90
|
+
// In development (src), go up to project root: src/core -> project root
|
|
91
|
+
const distPath = join(currentDir, '..', 'assets', 'agents');
|
|
92
|
+
const devPath = join(currentDir, '..', '..', 'assets', 'agents');
|
|
93
|
+
|
|
94
|
+
// Check which one exists (try dist first, then dev)
|
|
95
|
+
try {
|
|
96
|
+
await access(distPath);
|
|
97
|
+
return distPath;
|
|
98
|
+
} catch {
|
|
99
|
+
return devPath;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get all agent search paths
|
|
105
|
+
*/
|
|
106
|
+
export function getAgentSearchPaths(cwd: string): string[] {
|
|
107
|
+
const globalPath = join(homedir(), '.sylphx-flow', 'agents');
|
|
108
|
+
const projectPath = join(cwd, '.sylphx-flow', 'agents');
|
|
109
|
+
|
|
110
|
+
return [globalPath, projectPath];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Load all available agents from all sources
|
|
115
|
+
*/
|
|
116
|
+
export async function loadAllAgents(cwd: string, targetAgentDir?: string): Promise<Agent[]> {
|
|
117
|
+
const systemPath = await getSystemAgentsPath();
|
|
118
|
+
const [globalPath, projectPath] = getAgentSearchPaths(cwd);
|
|
119
|
+
|
|
120
|
+
let allAgentPaths = [systemPath, globalPath, projectPath];
|
|
121
|
+
|
|
122
|
+
// If a target-specific agent directory is provided, add it with highest priority
|
|
123
|
+
if (targetAgentDir) {
|
|
124
|
+
const targetPath = join(cwd, targetAgentDir); // Assuming targetAgentDir is relative to cwd
|
|
125
|
+
allAgentPaths.push(targetPath);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Load agents from all paths
|
|
129
|
+
const loadedAgentsPromises = allAgentPaths.map(path => loadAgentsFromDirectory(path, path === systemPath));
|
|
130
|
+
const loadedAgentsArrays = await Promise.all(loadedAgentsPromises);
|
|
131
|
+
|
|
132
|
+
// Flatten and deduplicate
|
|
133
|
+
const agentMap = new Map<string, Agent>();
|
|
134
|
+
for (const agentArray of loadedAgentsArrays) {
|
|
135
|
+
for (const agent of agentArray) {
|
|
136
|
+
agentMap.set(agent.id, agent);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return Array.from(agentMap.values());
|
|
141
|
+
}
|