@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.
Files changed (229) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +10 -9
  3. package/src/commands/codebase-command.ts +168 -0
  4. package/src/commands/flow-command.ts +1137 -0
  5. package/src/commands/flow-orchestrator.ts +296 -0
  6. package/src/commands/hook-command.ts +444 -0
  7. package/src/commands/init-command.ts +92 -0
  8. package/src/commands/init-core.ts +322 -0
  9. package/src/commands/knowledge-command.ts +161 -0
  10. package/src/commands/run-command.ts +120 -0
  11. package/src/components/benchmark-monitor.tsx +331 -0
  12. package/src/components/reindex-progress.tsx +261 -0
  13. package/src/composables/functional/index.ts +14 -0
  14. package/src/composables/functional/useEnvironment.ts +171 -0
  15. package/src/composables/functional/useFileSystem.ts +139 -0
  16. package/src/composables/index.ts +5 -0
  17. package/src/composables/useEnv.ts +13 -0
  18. package/src/composables/useRuntimeConfig.ts +27 -0
  19. package/src/composables/useTargetConfig.ts +45 -0
  20. package/src/config/ai-config.ts +376 -0
  21. package/src/config/constants.ts +35 -0
  22. package/src/config/index.ts +27 -0
  23. package/src/config/rules.ts +43 -0
  24. package/src/config/servers.ts +371 -0
  25. package/src/config/targets.ts +126 -0
  26. package/src/core/agent-loader.ts +141 -0
  27. package/src/core/agent-manager.ts +174 -0
  28. package/src/core/ai-sdk.ts +603 -0
  29. package/src/core/app-factory.ts +381 -0
  30. package/src/core/builtin-agents.ts +9 -0
  31. package/src/core/command-system.ts +550 -0
  32. package/src/core/config-system.ts +550 -0
  33. package/src/core/connection-pool.ts +390 -0
  34. package/src/core/di-container.ts +155 -0
  35. package/src/core/error-handling.ts +519 -0
  36. package/src/core/formatting/bytes.test.ts +115 -0
  37. package/src/core/formatting/bytes.ts +64 -0
  38. package/src/core/functional/async.ts +313 -0
  39. package/src/core/functional/either.ts +109 -0
  40. package/src/core/functional/error-handler.ts +135 -0
  41. package/src/core/functional/error-types.ts +311 -0
  42. package/src/core/functional/index.ts +19 -0
  43. package/src/core/functional/option.ts +142 -0
  44. package/src/core/functional/pipe.ts +189 -0
  45. package/src/core/functional/result.ts +204 -0
  46. package/src/core/functional/validation.ts +138 -0
  47. package/src/core/headless-display.ts +96 -0
  48. package/src/core/index.ts +6 -0
  49. package/src/core/installers/file-installer.ts +303 -0
  50. package/src/core/installers/mcp-installer.ts +213 -0
  51. package/src/core/interfaces/index.ts +22 -0
  52. package/src/core/interfaces/repository.interface.ts +91 -0
  53. package/src/core/interfaces/service.interface.ts +133 -0
  54. package/src/core/interfaces.ts +129 -0
  55. package/src/core/loop-controller.ts +200 -0
  56. package/src/core/result.ts +351 -0
  57. package/src/core/rule-loader.ts +147 -0
  58. package/src/core/rule-manager.ts +240 -0
  59. package/src/core/service-config.ts +252 -0
  60. package/src/core/session-service.ts +121 -0
  61. package/src/core/state-detector.ts +389 -0
  62. package/src/core/storage-factory.ts +115 -0
  63. package/src/core/stream-handler.ts +288 -0
  64. package/src/core/target-manager.ts +161 -0
  65. package/src/core/type-utils.ts +427 -0
  66. package/src/core/unified-storage.ts +456 -0
  67. package/src/core/upgrade-manager.ts +300 -0
  68. package/src/core/validation/limit.test.ts +155 -0
  69. package/src/core/validation/limit.ts +46 -0
  70. package/src/core/validation/query.test.ts +44 -0
  71. package/src/core/validation/query.ts +20 -0
  72. package/src/db/auto-migrate.ts +322 -0
  73. package/src/db/base-database-client.ts +144 -0
  74. package/src/db/cache-db.ts +218 -0
  75. package/src/db/cache-schema.ts +75 -0
  76. package/src/db/database.ts +70 -0
  77. package/src/db/index.ts +252 -0
  78. package/src/db/memory-db.ts +153 -0
  79. package/src/db/memory-schema.ts +29 -0
  80. package/src/db/schema.ts +289 -0
  81. package/src/db/session-repository.ts +733 -0
  82. package/src/domains/codebase/index.ts +5 -0
  83. package/src/domains/codebase/tools.ts +139 -0
  84. package/src/domains/index.ts +8 -0
  85. package/src/domains/knowledge/index.ts +10 -0
  86. package/src/domains/knowledge/resources.ts +537 -0
  87. package/src/domains/knowledge/tools.ts +174 -0
  88. package/src/domains/utilities/index.ts +6 -0
  89. package/src/domains/utilities/time/index.ts +5 -0
  90. package/src/domains/utilities/time/tools.ts +291 -0
  91. package/src/index.ts +211 -0
  92. package/src/services/agent-service.ts +273 -0
  93. package/src/services/claude-config-service.ts +252 -0
  94. package/src/services/config-service.ts +258 -0
  95. package/src/services/evaluation-service.ts +271 -0
  96. package/src/services/functional/evaluation-logic.ts +296 -0
  97. package/src/services/functional/file-processor.ts +273 -0
  98. package/src/services/functional/index.ts +12 -0
  99. package/src/services/index.ts +13 -0
  100. package/src/services/mcp-service.ts +432 -0
  101. package/src/services/memory.service.ts +476 -0
  102. package/src/services/search/base-indexer.ts +156 -0
  103. package/src/services/search/codebase-indexer-types.ts +38 -0
  104. package/src/services/search/codebase-indexer.ts +647 -0
  105. package/src/services/search/embeddings-provider.ts +455 -0
  106. package/src/services/search/embeddings.ts +316 -0
  107. package/src/services/search/functional-indexer.ts +323 -0
  108. package/src/services/search/index.ts +27 -0
  109. package/src/services/search/indexer.ts +380 -0
  110. package/src/services/search/knowledge-indexer.ts +422 -0
  111. package/src/services/search/semantic-search.ts +244 -0
  112. package/src/services/search/tfidf.ts +559 -0
  113. package/src/services/search/unified-search-service.ts +888 -0
  114. package/src/services/smart-config-service.ts +385 -0
  115. package/src/services/storage/cache-storage.ts +487 -0
  116. package/src/services/storage/drizzle-storage.ts +581 -0
  117. package/src/services/storage/index.ts +15 -0
  118. package/src/services/storage/lancedb-vector-storage.ts +494 -0
  119. package/src/services/storage/memory-storage.ts +268 -0
  120. package/src/services/storage/separated-storage.ts +467 -0
  121. package/src/services/storage/vector-storage.ts +13 -0
  122. package/src/shared/agents/index.ts +63 -0
  123. package/src/shared/files/index.ts +99 -0
  124. package/src/shared/index.ts +32 -0
  125. package/src/shared/logging/index.ts +24 -0
  126. package/src/shared/processing/index.ts +153 -0
  127. package/src/shared/types/index.ts +25 -0
  128. package/src/targets/claude-code.ts +574 -0
  129. package/src/targets/functional/claude-code-logic.ts +185 -0
  130. package/src/targets/functional/index.ts +6 -0
  131. package/src/targets/opencode.ts +529 -0
  132. package/src/types/agent.types.ts +32 -0
  133. package/src/types/api/batch.ts +108 -0
  134. package/src/types/api/errors.ts +118 -0
  135. package/src/types/api/index.ts +55 -0
  136. package/src/types/api/requests.ts +76 -0
  137. package/src/types/api/responses.ts +180 -0
  138. package/src/types/api/websockets.ts +85 -0
  139. package/src/types/api.types.ts +9 -0
  140. package/src/types/benchmark.ts +49 -0
  141. package/src/types/cli.types.ts +87 -0
  142. package/src/types/common.types.ts +35 -0
  143. package/src/types/database.types.ts +510 -0
  144. package/src/types/mcp-config.types.ts +448 -0
  145. package/src/types/mcp.types.ts +69 -0
  146. package/src/types/memory-types.ts +63 -0
  147. package/src/types/provider.types.ts +28 -0
  148. package/src/types/rule.types.ts +24 -0
  149. package/src/types/session.types.ts +214 -0
  150. package/src/types/target-config.types.ts +295 -0
  151. package/src/types/target.types.ts +140 -0
  152. package/src/types/todo.types.ts +25 -0
  153. package/src/types.ts +40 -0
  154. package/src/utils/advanced-tokenizer.ts +191 -0
  155. package/src/utils/agent-enhancer.ts +114 -0
  156. package/src/utils/ai-model-fetcher.ts +19 -0
  157. package/src/utils/async-file-operations.ts +516 -0
  158. package/src/utils/audio-player.ts +345 -0
  159. package/src/utils/cli-output.ts +266 -0
  160. package/src/utils/codebase-helpers.ts +211 -0
  161. package/src/utils/console-ui.ts +79 -0
  162. package/src/utils/database-errors.ts +140 -0
  163. package/src/utils/debug-logger.ts +49 -0
  164. package/src/utils/error-handler.ts +53 -0
  165. package/src/utils/file-operations.ts +310 -0
  166. package/src/utils/file-scanner.ts +259 -0
  167. package/src/utils/functional/array.ts +355 -0
  168. package/src/utils/functional/index.ts +15 -0
  169. package/src/utils/functional/object.ts +279 -0
  170. package/src/utils/functional/string.ts +281 -0
  171. package/src/utils/functional.ts +543 -0
  172. package/src/utils/help.ts +20 -0
  173. package/src/utils/immutable-cache.ts +106 -0
  174. package/src/utils/index.ts +78 -0
  175. package/src/utils/jsonc.ts +158 -0
  176. package/src/utils/logger.ts +396 -0
  177. package/src/utils/mcp-config.ts +249 -0
  178. package/src/utils/memory-tui.ts +414 -0
  179. package/src/utils/models-dev.ts +91 -0
  180. package/src/utils/notifications.ts +169 -0
  181. package/src/utils/object-utils.ts +51 -0
  182. package/src/utils/parallel-operations.ts +487 -0
  183. package/src/utils/paths.ts +143 -0
  184. package/src/utils/process-manager.ts +155 -0
  185. package/src/utils/prompts.ts +120 -0
  186. package/src/utils/search-tool-builder.ts +214 -0
  187. package/src/utils/secret-utils.ts +179 -0
  188. package/src/utils/security.ts +537 -0
  189. package/src/utils/session-manager.ts +168 -0
  190. package/src/utils/session-title.ts +87 -0
  191. package/src/utils/settings.ts +182 -0
  192. package/src/utils/simplified-errors.ts +410 -0
  193. package/src/utils/sync-utils.ts +159 -0
  194. package/src/utils/target-config.ts +570 -0
  195. package/src/utils/target-utils.ts +394 -0
  196. package/src/utils/template-engine.ts +94 -0
  197. package/src/utils/test-audio.ts +71 -0
  198. package/src/utils/todo-context.ts +46 -0
  199. package/src/utils/token-counter.ts +288 -0
  200. package/dist/index.d.ts +0 -10
  201. package/dist/index.js +0 -59554
  202. package/dist/lancedb.linux-x64-gnu-b7f0jgsz.node +0 -0
  203. package/dist/lancedb.linux-x64-musl-tgcv22rx.node +0 -0
  204. package/dist/shared/chunk-25dwp0dp.js +0 -89
  205. package/dist/shared/chunk-3pjb6063.js +0 -208
  206. package/dist/shared/chunk-4d6ydpw7.js +0 -2854
  207. package/dist/shared/chunk-4wjcadjk.js +0 -225
  208. package/dist/shared/chunk-5j4w74t6.js +0 -30
  209. package/dist/shared/chunk-5j8m3dh3.js +0 -58
  210. package/dist/shared/chunk-5thh3qem.js +0 -91
  211. package/dist/shared/chunk-6g9xy73m.js +0 -252
  212. package/dist/shared/chunk-7eq34c42.js +0 -23
  213. package/dist/shared/chunk-c2gwgx3r.js +0 -115
  214. package/dist/shared/chunk-cjd3mk4c.js +0 -1320
  215. package/dist/shared/chunk-g5cv6703.js +0 -368
  216. package/dist/shared/chunk-hpkhykhq.js +0 -574
  217. package/dist/shared/chunk-m2322pdk.js +0 -122
  218. package/dist/shared/chunk-nd5fdvaq.js +0 -26
  219. package/dist/shared/chunk-pgd3m6zf.js +0 -108
  220. package/dist/shared/chunk-qk8n91hw.js +0 -494
  221. package/dist/shared/chunk-rkkn8szp.js +0 -16855
  222. package/dist/shared/chunk-t16rfxh0.js +0 -61
  223. package/dist/shared/chunk-t4fbfa5v.js +0 -19
  224. package/dist/shared/chunk-t77h86w6.js +0 -276
  225. package/dist/shared/chunk-v0ez4aef.js +0 -71
  226. package/dist/shared/chunk-v29j2r3s.js +0 -32051
  227. package/dist/shared/chunk-vfbc6ew5.js +0 -765
  228. package/dist/shared/chunk-vmeqwm1c.js +0 -204
  229. package/dist/shared/chunk-x66eh37x.js +0 -137
@@ -0,0 +1,432 @@
1
+ import chalk from 'chalk';
2
+ import inquirer from 'inquirer';
3
+ import ora from 'ora';
4
+ import { MCP_SERVER_REGISTRY, type MCPServerID } from '../config/servers.js';
5
+ import type { TargetConfigurationData } from '../types/target-config.types.js';
6
+ import type { Target } from '../types.js';
7
+ import { isCLICommandConfig, type Resolvable } from '../types.js';
8
+
9
+ export interface ValidationResult {
10
+ isValid: boolean;
11
+ missingRequired: string[];
12
+ invalidValues: string[];
13
+ }
14
+
15
+ export interface InstallOptions {
16
+ skipValidation?: boolean;
17
+ dryRun?: boolean;
18
+ }
19
+
20
+ /**
21
+ * MCPService Interface
22
+ * Service for managing MCP server installation and configuration
23
+ */
24
+ export interface MCPService {
25
+ readonly getAllServerIds: () => MCPServerID[];
26
+ readonly getAvailableServers: () => Promise<MCPServerID[]>;
27
+ readonly getInstalledServerIds: () => Promise<MCPServerID[]>;
28
+ readonly getRequiringConfiguration: (serverIds: MCPServerID[]) => MCPServerID[];
29
+ readonly validateServer: (
30
+ serverId: MCPServerID,
31
+ envValues: Record<string, string>
32
+ ) => ValidationResult;
33
+ readonly configureServer: (
34
+ serverId: MCPServerID,
35
+ collectedEnv?: Record<string, string>
36
+ ) => Promise<Record<string, string>>;
37
+ readonly installServers: (
38
+ serverIds: MCPServerID[],
39
+ serverConfigs?: Record<MCPServerID, Record<string, string>>
40
+ ) => Promise<void>;
41
+ readonly readConfig: () => Promise<TargetConfigurationData>;
42
+ readonly writeConfig: (configData: TargetConfigurationData) => Promise<void>;
43
+ }
44
+
45
+ /**
46
+ * MCPService Dependencies
47
+ */
48
+ export interface MCPServiceDeps {
49
+ readonly target: Target;
50
+ }
51
+
52
+ /**
53
+ * Helper function to resolve static or dynamic configuration values
54
+ */
55
+ function resolveConfig<T>(config: Resolvable<T>): Promise<T> {
56
+ if (typeof config === 'function') {
57
+ const result = (config as () => T | Promise<T>)();
58
+ return Promise.resolve(result);
59
+ }
60
+ return Promise.resolve(config);
61
+ }
62
+
63
+ /**
64
+ * Helper function to resolve static or dynamic configuration values with parameters
65
+ */
66
+ export function resolveConfigWithParams<T, P>(
67
+ config: T | ((params: P) => Promise<T>) | ((params: P) => T),
68
+ params: P
69
+ ): Promise<T> {
70
+ if (typeof config === 'function') {
71
+ const result = (config as (params: P) => T | Promise<T>)(params);
72
+ return Promise.resolve(result);
73
+ }
74
+ return Promise.resolve(config);
75
+ }
76
+
77
+ export { resolveConfig };
78
+
79
+ /**
80
+ * Create MCP Service (Factory Function)
81
+ * Handles MCP server installation and configuration
82
+ */
83
+ export const createMCPService = (deps: MCPServiceDeps): MCPService => {
84
+ /**
85
+ * Get all available server IDs from registry
86
+ */
87
+ const getAllServerIds = (): MCPServerID[] => {
88
+ return Object.keys(MCP_SERVER_REGISTRY) as MCPServerID[];
89
+ };
90
+
91
+ /**
92
+ * Get servers that are not yet installed
93
+ */
94
+ const getAvailableServers = async (): Promise<MCPServerID[]> => {
95
+ const existingServerIds = await getInstalledServerIds();
96
+ return getAllServerIds().filter((id) => !existingServerIds.includes(id));
97
+ };
98
+
99
+ /**
100
+ * Get currently installed server IDs
101
+ */
102
+ const getInstalledServerIds = async (): Promise<MCPServerID[]> => {
103
+ try {
104
+ const configData = await deps.target.readConfig(process.cwd());
105
+ const mcpConfigPath = deps.target.config.mcpConfigPath;
106
+ const existingMcpSection = getNestedProperty(configData, mcpConfigPath) || {};
107
+ const existingServerNames = Object.keys(existingMcpSection);
108
+
109
+ return Object.values(MCP_SERVER_REGISTRY)
110
+ .filter((server) => existingServerNames.includes(server.name))
111
+ .map((server) => server.id);
112
+ } catch (_error) {
113
+ return [];
114
+ }
115
+ };
116
+
117
+ /**
118
+ * Get servers that require configuration
119
+ */
120
+ const getRequiringConfiguration = (serverIds: MCPServerID[]): MCPServerID[] => {
121
+ return serverIds.filter((id) => {
122
+ const server = MCP_SERVER_REGISTRY[id];
123
+ return !!server.envVars;
124
+ });
125
+ };
126
+
127
+ /**
128
+ * Validate server configuration
129
+ */
130
+ const validateServer = (
131
+ serverId: MCPServerID,
132
+ envValues: Record<string, string>
133
+ ): ValidationResult => {
134
+ const server = MCP_SERVER_REGISTRY[serverId];
135
+ if (!server?.envVars) {
136
+ return { isValid: true, missingRequired: [], invalidValues: [] };
137
+ }
138
+
139
+ const missingRequired: string[] = [];
140
+ const invalidValues: string[] = [];
141
+
142
+ for (const [key, config] of Object.entries(server.envVars)) {
143
+ const value = envValues[key] || process.env[key] || '';
144
+
145
+ if (config.required && (!value || value === '')) {
146
+ missingRequired.push(key);
147
+ }
148
+
149
+ // Don't validate dependencies here - configureServer already handles them
150
+ // by skipping fields with missing dependencies
151
+ }
152
+
153
+ return {
154
+ isValid: missingRequired.length === 0 && invalidValues.length === 0,
155
+ missingRequired,
156
+ invalidValues,
157
+ };
158
+ };
159
+
160
+ /**
161
+ * Configure server through interactive prompts
162
+ */
163
+ const configureServer = async (
164
+ serverId: MCPServerID,
165
+ collectedEnv: Record<string, string> = {}
166
+ ): Promise<Record<string, string>> => {
167
+ const server = MCP_SERVER_REGISTRY[serverId];
168
+ const values: Record<string, string> = {};
169
+
170
+ if (!server.envVars) {
171
+ return values;
172
+ }
173
+
174
+ console.log('');
175
+ console.log(chalk.cyan(`▸ ${server.name}`));
176
+ console.log(chalk.gray(` ${server.description}`));
177
+ console.log('');
178
+
179
+ for (const [key, config] of Object.entries(server.envVars)) {
180
+ if (config.dependsOn) {
181
+ const missingDeps = config.dependsOn.filter(
182
+ (dep) => !(collectedEnv[dep] || process.env[dep])
183
+ );
184
+ if (missingDeps.length > 0) {
185
+ continue;
186
+ }
187
+ }
188
+
189
+ // Check if value already exists in collectedEnv or process.env
190
+ const existingValue = collectedEnv[key] || process.env[key];
191
+ let value: string;
192
+
193
+ if (existingValue && existingValue.trim() !== '') {
194
+ value = existingValue;
195
+ } else if (config.fetchChoices) {
196
+ const spinner = ora('Fetching options...').start();
197
+
198
+ for (const [envKey, envValue] of Object.entries(collectedEnv)) {
199
+ if (envValue) {
200
+ process.env[envKey] = envValue;
201
+ }
202
+ }
203
+
204
+ try {
205
+ const choices = await config.fetchChoices();
206
+ spinner.stop();
207
+
208
+ const answer = await inquirer.prompt({
209
+ type: 'list',
210
+ name: 'value',
211
+ message: `${key}${config.required ? ' *' : ''}`,
212
+ choices,
213
+ default: config.default || choices[0],
214
+ });
215
+ value = answer.value;
216
+ } catch (error) {
217
+ const errorMsg = error instanceof Error ? error.message : String(error);
218
+ spinner.fail(chalk.red(`Failed to fetch options: ${errorMsg}`));
219
+
220
+ const answer = await inquirer.prompt({
221
+ type: 'input',
222
+ name: 'value',
223
+ message: `${key}${config.required ? ' *' : ''}`,
224
+ default: config.default,
225
+ });
226
+ value = answer.value;
227
+ }
228
+ } else if (key === 'GEMINI_MODEL') {
229
+ const answer = await inquirer.prompt({
230
+ type: 'list',
231
+ name: 'model',
232
+ message: `${key}${config.required ? ' *' : ''}`,
233
+ choices: ['gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-1.5-flash', 'gemini-1.5-pro'],
234
+ default: config.default || 'gemini-2.5-flash',
235
+ });
236
+ value = answer.model;
237
+ } else {
238
+ const answer = await inquirer.prompt({
239
+ type: config.secret ? 'password' : 'input',
240
+ name: 'value',
241
+ message: `${key}${config.required ? ' *' : ''}`,
242
+ default: config.default,
243
+ mask: config.secret ? '•' : undefined,
244
+ });
245
+ value = answer.value;
246
+ }
247
+
248
+ if (value) {
249
+ values[key] = value;
250
+ collectedEnv[key] = value;
251
+ }
252
+ }
253
+
254
+ console.log(chalk.green('✓ Configured'));
255
+ console.log('');
256
+
257
+ return values;
258
+ };
259
+
260
+ /**
261
+ * Install MCP servers to config file
262
+ * Note: Configuration should be done separately before calling this
263
+ */
264
+ const installServers = async (
265
+ serverIds: MCPServerID[],
266
+ serverConfigs: Record<MCPServerID, Record<string, string>> = {}
267
+ ): Promise<void> => {
268
+ if (serverIds.length === 0) {
269
+ return;
270
+ }
271
+
272
+ try {
273
+ // Read current config
274
+ const configData = await deps.target.readConfig(process.cwd());
275
+ const mcpConfigPath = deps.target.config.mcpConfigPath;
276
+ const existingMcpSection = getNestedProperty(configData, mcpConfigPath) || {};
277
+ const mcpSection = { ...existingMcpSection };
278
+
279
+ // Add/update each server
280
+ for (const serverId of serverIds) {
281
+ const server = MCP_SERVER_REGISTRY[serverId];
282
+ const configuredValues = serverConfigs[serverId] || {};
283
+
284
+ // Prepare config with environment variables and dynamic command
285
+ let configToTransform = { ...server.config };
286
+
287
+ // Resolve potentially dynamic command and args (only for CLI servers)
288
+ let resolvedCommand: unknown;
289
+ let resolvedArgs: unknown;
290
+
291
+ if (isCLICommandConfig(server.config)) {
292
+ resolvedCommand = server.config.command
293
+ ? await resolveConfig(server.config.command)
294
+ : undefined;
295
+ resolvedArgs = server.config.args ? await resolveConfig(server.config.args) : [];
296
+
297
+ // Apply target-specific flags for sylphx-flow server
298
+ if (serverId === 'sylphx-flow' && Array.isArray(resolvedArgs)) {
299
+ const targetConfig = deps.target.mcpServerConfig;
300
+ if (targetConfig) {
301
+ if (targetConfig.disableTime) {
302
+ resolvedArgs.push('--disable-time');
303
+ }
304
+ if (targetConfig.disableKnowledge) {
305
+ resolvedArgs.push('--disable-knowledge');
306
+ }
307
+ if (targetConfig.disableCodebase) {
308
+ resolvedArgs.push('--disable-codebase');
309
+ }
310
+ }
311
+ }
312
+ }
313
+
314
+ // Update the config with resolved values (only for CLI servers)
315
+ if (isCLICommandConfig(server.config)) {
316
+ configToTransform = {
317
+ ...server.config,
318
+ command: resolvedCommand,
319
+ args: resolvedArgs,
320
+ };
321
+ }
322
+
323
+ // If server has env vars and we have configured values, merge them
324
+ if (Object.keys(configuredValues).length > 0) {
325
+ const serverConfigEnv =
326
+ server.config.type === 'local'
327
+ ? server.config.environment
328
+ : server.config.type === 'stdio'
329
+ ? server.config.env
330
+ : {};
331
+ const updatedEnv = { ...serverConfigEnv };
332
+
333
+ for (const [key, value] of Object.entries(configuredValues)) {
334
+ if (value && value.trim() !== '') {
335
+ updatedEnv[key] = value;
336
+ }
337
+ }
338
+
339
+ if (configToTransform.type === 'local') {
340
+ configToTransform.environment = updatedEnv;
341
+ } else if (configToTransform.type === 'stdio') {
342
+ configToTransform.env = updatedEnv;
343
+ }
344
+ }
345
+
346
+ // Transform config for target-specific format
347
+ const transformedConfig = deps.target.transformMCPConfig(configToTransform, serverId);
348
+
349
+ mcpSection[server.name] = transformedConfig;
350
+ }
351
+
352
+ // Write updated config
353
+ setNestedProperty(configData, mcpConfigPath, mcpSection);
354
+ await deps.target.writeConfig(process.cwd(), configData);
355
+
356
+ // Approve MCP servers if the target supports it
357
+ if (deps.target.approveMCPServers) {
358
+ const serverNames = serverIds.map((id) => MCP_SERVER_REGISTRY[id].name);
359
+ await deps.target.approveMCPServers(process.cwd(), serverNames);
360
+ }
361
+ } catch (error) {
362
+ throw new Error(
363
+ `Failed to install MCP servers: ${error instanceof Error ? error.message : String(error)}`
364
+ );
365
+ }
366
+ };
367
+
368
+ /**
369
+ * Read target configuration
370
+ */
371
+ const readConfig = async (): Promise<TargetConfigurationData> => {
372
+ try {
373
+ return (await deps.target.readConfig(process.cwd())) as TargetConfigurationData;
374
+ } catch (_error) {
375
+ return { settings: {} };
376
+ }
377
+ };
378
+
379
+ /**
380
+ * Write target configuration
381
+ */
382
+ const writeConfig = async (configData: TargetConfigurationData): Promise<void> => {
383
+ await deps.target.writeConfig(process.cwd(), configData);
384
+ };
385
+
386
+ /**
387
+ * Get nested property from object using dot notation path
388
+ * Pure function - does not mutate input
389
+ */
390
+ const getNestedProperty = (obj: TargetConfigurationData, path: string): unknown => {
391
+ return path.split('.').reduce((current: unknown, key: string) => {
392
+ if (typeof current === 'object' && current !== null) {
393
+ return (current as Record<string, unknown>)[key];
394
+ }
395
+ return undefined;
396
+ }, obj);
397
+ };
398
+
399
+ /**
400
+ * Set nested property in object using dot notation path
401
+ * SIDE EFFECT: Mutates the input object
402
+ * NOTE: This is intentional for config file updates
403
+ */
404
+ const setNestedProperty = (obj: TargetConfigurationData, path: string, value: unknown): void => {
405
+ const keys = path.split('.');
406
+ const lastKey = keys.pop();
407
+ if (!lastKey) {
408
+ return;
409
+ }
410
+
411
+ const target = keys.reduce((current, key) => {
412
+ if (!current[key]) {
413
+ current[key] = {};
414
+ }
415
+ return current[key];
416
+ }, obj);
417
+ target[lastKey] = value;
418
+ };
419
+
420
+ // Return service interface
421
+ return {
422
+ getAllServerIds,
423
+ getAvailableServers,
424
+ getInstalledServerIds,
425
+ getRequiringConfiguration,
426
+ validateServer,
427
+ configureServer,
428
+ installServers,
429
+ readConfig,
430
+ writeConfig,
431
+ };
432
+ };