@sylphx/flow 2.1.5 ā 2.1.7
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 +21 -0
- package/package.json +1 -1
- package/src/commands/settings-command.ts +24 -29
- package/src/config/servers.ts +67 -0
- package/src/core/attach/file-attacher.ts +2 -4
- package/src/core/attach-manager.ts +25 -16
- package/src/services/global-config.ts +23 -5
- package/src/targets/claude-code.ts +3 -3
- package/src/targets/opencode.ts +3 -3
- package/src/targets/shared/mcp-transforms.ts +23 -9
- package/src/targets/shared/target-operations.ts +15 -22
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @sylphx/flow
|
|
2
2
|
|
|
3
|
+
## 2.1.7 (2025-11-28)
|
|
4
|
+
|
|
5
|
+
### š Bug Fixes
|
|
6
|
+
|
|
7
|
+
- **mcp:** approve MCP servers for Claude Code during attach ([19cdc3b](https://github.com/SylphxAI/flow/commit/19cdc3bea9df58bc9c1fe55242a8e2858c1303c1))
|
|
8
|
+
|
|
9
|
+
## 2.1.6 (2025-11-28)
|
|
10
|
+
|
|
11
|
+
### š Bug Fixes
|
|
12
|
+
|
|
13
|
+
- **settings:** respect saved MCP server enabled state ([8447cea](https://github.com/SylphxAI/flow/commit/8447cea1b2f46e49cfc1bd7e57e557307d072163))
|
|
14
|
+
- **mcp:** return default servers when no config exists ([bd6c588](https://github.com/SylphxAI/flow/commit/bd6c58819cdde8e31bd18cdc2f05c2c45e4f3d39))
|
|
15
|
+
|
|
16
|
+
### ā»ļø Refactoring
|
|
17
|
+
|
|
18
|
+
- **mcp:** implement SSOT for server configuration ([e0b5ee0](https://github.com/SylphxAI/flow/commit/e0b5ee01d4952e825d81005465147ce39963bbd0))
|
|
19
|
+
|
|
20
|
+
### š§ Chores
|
|
21
|
+
|
|
22
|
+
- format package.json (tabs to spaces) ([305096a](https://github.com/SylphxAI/flow/commit/305096a9e276a3626415d76b8f313e95dc6daeff))
|
|
23
|
+
|
|
3
24
|
## 2.1.5 (2025-11-28)
|
|
4
25
|
|
|
5
26
|
### š Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sylphx/flow",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.7",
|
|
4
4
|
"description": "One CLI to rule them all. Unified orchestration layer for Claude Code, OpenCode, Cursor and all AI development tools. Auto-detection, auto-installation, auto-upgrade.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -6,12 +6,17 @@
|
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import { Command } from 'commander';
|
|
8
8
|
import inquirer from 'inquirer';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
computeEffectiveServers,
|
|
11
|
+
getRequiredEnvVars,
|
|
12
|
+
MCP_SERVER_REGISTRY,
|
|
13
|
+
type MCPServerID,
|
|
14
|
+
} from '../config/servers.js';
|
|
10
15
|
import { GlobalConfigService } from '../services/global-config.js';
|
|
11
16
|
import { TargetInstaller } from '../services/target-installer.js';
|
|
12
17
|
import { UserCancelledError } from '../utils/errors.js';
|
|
13
18
|
import { buildAvailableTargets, promptForDefaultTarget } from '../utils/target-selection.js';
|
|
14
|
-
import { handleCheckboxConfig
|
|
19
|
+
import { handleCheckboxConfig } from './settings/index.js';
|
|
15
20
|
|
|
16
21
|
export const settingsCommand = new Command('settings')
|
|
17
22
|
.description('Configure Sylphx Flow settings')
|
|
@@ -204,14 +209,12 @@ async function configureMCP(configService: GlobalConfigService): Promise<void> {
|
|
|
204
209
|
console.log(chalk.cyan.bold('\nāāā š” MCP Server Configuration\n'));
|
|
205
210
|
|
|
206
211
|
const mcpConfig = await configService.loadMCPConfig();
|
|
207
|
-
const
|
|
212
|
+
const savedServers = mcpConfig.servers || {};
|
|
208
213
|
|
|
209
|
-
//
|
|
214
|
+
// SSOT: compute effective state from saved config + defaults
|
|
215
|
+
const effectiveServers = computeEffectiveServers(savedServers);
|
|
210
216
|
const allServerIds = Object.keys(MCP_SERVER_REGISTRY) as MCPServerID[];
|
|
211
217
|
|
|
212
|
-
// Get current enabled servers
|
|
213
|
-
const currentEnabled = Object.keys(currentServers).filter((key) => currentServers[key].enabled);
|
|
214
|
-
|
|
215
218
|
const { selectedServers } = await inquirer.prompt([
|
|
216
219
|
{
|
|
217
220
|
type: 'checkbox',
|
|
@@ -219,29 +222,27 @@ async function configureMCP(configService: GlobalConfigService): Promise<void> {
|
|
|
219
222
|
message: 'Select MCP servers to enable:',
|
|
220
223
|
choices: allServerIds.map((id) => {
|
|
221
224
|
const server = MCP_SERVER_REGISTRY[id];
|
|
225
|
+
const effective = effectiveServers[id];
|
|
222
226
|
const requiredEnvVars = getRequiredEnvVars(id);
|
|
223
227
|
const requiresText =
|
|
224
228
|
requiredEnvVars.length > 0 ? chalk.dim(` (requires ${requiredEnvVars.join(', ')})`) : '';
|
|
225
229
|
return {
|
|
226
230
|
name: `${server.name} - ${server.description}${requiresText}`,
|
|
227
231
|
value: id,
|
|
228
|
-
checked:
|
|
232
|
+
checked: effective.enabled, // Use SSOT effective state
|
|
229
233
|
};
|
|
230
234
|
}),
|
|
231
235
|
},
|
|
232
236
|
]);
|
|
233
237
|
|
|
234
|
-
// Update servers
|
|
238
|
+
// Update servers - save ALL servers with explicit enabled state
|
|
239
|
+
const updatedServers: Record<string, { enabled: boolean; env: Record<string, string> }> = {};
|
|
235
240
|
for (const id of allServerIds) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
} else if (currentServers[id]) {
|
|
243
|
-
currentServers[id].enabled = false;
|
|
244
|
-
}
|
|
241
|
+
const effective = effectiveServers[id];
|
|
242
|
+
updatedServers[id] = {
|
|
243
|
+
enabled: selectedServers.includes(id),
|
|
244
|
+
env: effective.env, // Preserve existing env vars
|
|
245
|
+
};
|
|
245
246
|
}
|
|
246
247
|
|
|
247
248
|
// Ask for API keys for newly enabled servers
|
|
@@ -250,10 +251,10 @@ async function configureMCP(configService: GlobalConfigService): Promise<void> {
|
|
|
250
251
|
const requiredEnvVars = getRequiredEnvVars(serverId);
|
|
251
252
|
|
|
252
253
|
if (requiredEnvVars.length > 0) {
|
|
253
|
-
const
|
|
254
|
+
const serverState = updatedServers[serverId];
|
|
254
255
|
|
|
255
256
|
for (const envKey of requiredEnvVars) {
|
|
256
|
-
const hasKey =
|
|
257
|
+
const hasKey = serverState.env?.[envKey];
|
|
257
258
|
|
|
258
259
|
const { shouldConfigure } = await inquirer.prompt([
|
|
259
260
|
{
|
|
@@ -276,16 +277,13 @@ async function configureMCP(configService: GlobalConfigService): Promise<void> {
|
|
|
276
277
|
},
|
|
277
278
|
]);
|
|
278
279
|
|
|
279
|
-
|
|
280
|
-
server.env = {};
|
|
281
|
-
}
|
|
282
|
-
server.env[envKey] = apiKey;
|
|
280
|
+
serverState.env[envKey] = apiKey;
|
|
283
281
|
}
|
|
284
282
|
}
|
|
285
283
|
}
|
|
286
284
|
}
|
|
287
285
|
|
|
288
|
-
mcpConfig.servers =
|
|
286
|
+
mcpConfig.servers = updatedServers;
|
|
289
287
|
await configService.saveMCPConfig(mcpConfig);
|
|
290
288
|
|
|
291
289
|
console.log(chalk.green(`\nā MCP configuration saved`));
|
|
@@ -375,10 +373,7 @@ async function configureTarget(configService: GlobalConfigService): Promise<void
|
|
|
375
373
|
|
|
376
374
|
const defaultTarget = await promptForDefaultTarget(installedTargets, settings.defaultTarget);
|
|
377
375
|
|
|
378
|
-
settings.defaultTarget = defaultTarget as
|
|
379
|
-
| 'claude-code'
|
|
380
|
-
| 'opencode'
|
|
381
|
-
| 'ask-every-time';
|
|
376
|
+
settings.defaultTarget = defaultTarget as 'claude-code' | 'opencode' | 'ask-every-time';
|
|
382
377
|
await configService.saveSettings(settings);
|
|
383
378
|
|
|
384
379
|
if (defaultTarget === 'ask-every-time') {
|
package/src/config/servers.ts
CHANGED
|
@@ -339,3 +339,70 @@ export function getServerDefinition(id: MCPServerID): MCPServerDefinition {
|
|
|
339
339
|
}
|
|
340
340
|
return server;
|
|
341
341
|
}
|
|
342
|
+
|
|
343
|
+
// ============================================================================
|
|
344
|
+
// SSOT: Effective Server Config
|
|
345
|
+
// ============================================================================
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Saved server state (from user config file)
|
|
349
|
+
*/
|
|
350
|
+
export interface SavedServerState {
|
|
351
|
+
enabled: boolean;
|
|
352
|
+
env?: Record<string, string>;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Effective server state (computed from saved + defaults)
|
|
357
|
+
*/
|
|
358
|
+
export interface EffectiveServerState {
|
|
359
|
+
id: MCPServerID;
|
|
360
|
+
enabled: boolean;
|
|
361
|
+
env: Record<string, string>;
|
|
362
|
+
/** Whether this server was in saved config or using default */
|
|
363
|
+
isDefault: boolean;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Compute effective server states from saved config
|
|
368
|
+
* This is the SSOT for determining which servers are enabled
|
|
369
|
+
*
|
|
370
|
+
* Logic:
|
|
371
|
+
* - If server is in savedConfig, use its enabled state
|
|
372
|
+
* - If server is NOT in savedConfig, use defaultInInit from registry
|
|
373
|
+
*
|
|
374
|
+
* @param savedServers - Servers from user's config file (may be empty/undefined)
|
|
375
|
+
* @returns Map of server ID to effective state
|
|
376
|
+
*/
|
|
377
|
+
export function computeEffectiveServers(
|
|
378
|
+
savedServers: Record<string, SavedServerState> | undefined
|
|
379
|
+
): Record<MCPServerID, EffectiveServerState> {
|
|
380
|
+
const result: Record<string, EffectiveServerState> = {};
|
|
381
|
+
const saved = savedServers || {};
|
|
382
|
+
|
|
383
|
+
for (const [id, def] of Object.entries(MCP_SERVER_REGISTRY)) {
|
|
384
|
+
const serverId = id as MCPServerID;
|
|
385
|
+
const savedState = saved[id];
|
|
386
|
+
const isInSaved = id in saved;
|
|
387
|
+
|
|
388
|
+
result[serverId] = {
|
|
389
|
+
id: serverId,
|
|
390
|
+
enabled: isInSaved ? (savedState?.enabled ?? false) : (def.defaultInInit ?? false),
|
|
391
|
+
env: savedState?.env || {},
|
|
392
|
+
isDefault: !isInSaved,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return result as Record<MCPServerID, EffectiveServerState>;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Get only enabled servers from effective config
|
|
401
|
+
*/
|
|
402
|
+
export function getEnabledServersFromEffective(
|
|
403
|
+
effective: Record<MCPServerID, EffectiveServerState>
|
|
404
|
+
): MCPServerID[] {
|
|
405
|
+
return Object.entries(effective)
|
|
406
|
+
.filter(([, state]) => state.enabled)
|
|
407
|
+
.map(([id]) => id as MCPServerID);
|
|
408
|
+
}
|
|
@@ -72,8 +72,7 @@ export const writeFile = (filePath: string, content: string): Promise<void> =>
|
|
|
72
72
|
/**
|
|
73
73
|
* Read file content
|
|
74
74
|
*/
|
|
75
|
-
export const readFile = (filePath: string): Promise<string> =>
|
|
76
|
-
fs.readFile(filePath, 'utf-8');
|
|
75
|
+
export const readFile = (filePath: string): Promise<string> => fs.readFile(filePath, 'utf-8');
|
|
77
76
|
|
|
78
77
|
// ============================================================================
|
|
79
78
|
// Generic Attach Function
|
|
@@ -131,8 +130,7 @@ const FLOW_RULES_MARKER = '<!-- Sylphx Flow Rules -->';
|
|
|
131
130
|
/**
|
|
132
131
|
* Check if content already has Flow rules appended
|
|
133
132
|
*/
|
|
134
|
-
export const hasFlowRules = (content: string): boolean =>
|
|
135
|
-
content.includes(FLOW_RULES_MARKER);
|
|
133
|
+
export const hasFlowRules = (content: string): boolean => content.includes(FLOW_RULES_MARKER);
|
|
136
134
|
|
|
137
135
|
/**
|
|
138
136
|
* Wrap rules content with markers
|
|
@@ -9,7 +9,7 @@ import { existsSync } from 'node:fs';
|
|
|
9
9
|
import fs from 'node:fs/promises';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import chalk from 'chalk';
|
|
12
|
-
import { MCP_SERVER_REGISTRY } from '../config/servers.js';
|
|
12
|
+
import { MCP_SERVER_REGISTRY, type MCPServerID } from '../config/servers.js';
|
|
13
13
|
import { GlobalConfigService } from '../services/global-config.js';
|
|
14
14
|
import type { Target } from '../types/target.types.js';
|
|
15
15
|
import { attachItemsToDir, attachRulesFile } from './attach/index.js';
|
|
@@ -80,30 +80,33 @@ export class AttachManager {
|
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
82
|
* Load global MCP servers from ~/.sylphx-flow/mcp-config.json
|
|
83
|
+
* Uses SSOT: computeEffectiveServers for determining enabled servers
|
|
83
84
|
*/
|
|
84
85
|
private async loadGlobalMCPServers(
|
|
85
86
|
_target: Target
|
|
86
87
|
): Promise<Array<{ name: string; config: Record<string, unknown> }>> {
|
|
87
88
|
try {
|
|
88
|
-
const
|
|
89
|
+
const mcpConfig = await this.configService.loadMCPConfig();
|
|
90
|
+
const enabledServerIds = this.configService.getEnabledServerIds(mcpConfig.servers);
|
|
91
|
+
const effectiveServers = this.configService.getEffectiveMCPServers(mcpConfig.servers);
|
|
92
|
+
|
|
89
93
|
const servers: Array<{ name: string; config: Record<string, unknown> }> = [];
|
|
90
94
|
|
|
91
|
-
for (const
|
|
92
|
-
|
|
93
|
-
const
|
|
95
|
+
for (const serverId of enabledServerIds) {
|
|
96
|
+
const serverDef = MCP_SERVER_REGISTRY[serverId as MCPServerID];
|
|
97
|
+
const effective = effectiveServers[serverId as MCPServerID];
|
|
94
98
|
|
|
95
99
|
if (!serverDef) {
|
|
96
|
-
console.warn(`MCP server '${serverKey}' not found in registry, skipping`);
|
|
97
100
|
continue;
|
|
98
101
|
}
|
|
99
102
|
|
|
100
103
|
// Clone the server config from registry
|
|
101
104
|
const config: Record<string, unknown> = { ...serverDef.config };
|
|
102
105
|
|
|
103
|
-
// Merge environment variables from
|
|
104
|
-
if (
|
|
106
|
+
// Merge environment variables from effective config (SSOT)
|
|
107
|
+
if (effective.env && Object.keys(effective.env).length > 0) {
|
|
105
108
|
if (config.type === 'stdio' || config.type === 'local') {
|
|
106
|
-
config.env = { ...config.env, ...
|
|
109
|
+
config.env = { ...config.env, ...effective.env };
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
112
|
|
|
@@ -204,9 +207,7 @@ export class AttachManager {
|
|
|
204
207
|
// Update result
|
|
205
208
|
result.agentsAdded.push(...stats.added);
|
|
206
209
|
result.agentsOverridden.push(...stats.overridden);
|
|
207
|
-
result.conflicts.push(
|
|
208
|
-
...stats.conflicts.map((c) => ({ ...c, type: 'agent' as const }))
|
|
209
|
-
);
|
|
210
|
+
result.conflicts.push(...stats.conflicts.map((c) => ({ ...c, type: 'agent' as const })));
|
|
210
211
|
|
|
211
212
|
// Update manifest
|
|
212
213
|
manifest.backup.agents.user.push(...itemManifest.user);
|
|
@@ -225,14 +226,16 @@ export class AttachManager {
|
|
|
225
226
|
manifest: BackupManifest
|
|
226
227
|
): Promise<void> {
|
|
227
228
|
const commandsDir = path.join(projectPath, target.config.slashCommandsDir);
|
|
228
|
-
const { stats, manifest: itemManifest } = await attachItemsToDir(
|
|
229
|
+
const { stats, manifest: itemManifest } = await attachItemsToDir(
|
|
230
|
+
commands,
|
|
231
|
+
commandsDir,
|
|
232
|
+
'command'
|
|
233
|
+
);
|
|
229
234
|
|
|
230
235
|
// Update result
|
|
231
236
|
result.commandsAdded.push(...stats.added);
|
|
232
237
|
result.commandsOverridden.push(...stats.overridden);
|
|
233
|
-
result.conflicts.push(
|
|
234
|
-
...stats.conflicts.map((c) => ({ ...c, type: 'command' as const }))
|
|
235
|
-
);
|
|
238
|
+
result.conflicts.push(...stats.conflicts.map((c) => ({ ...c, type: 'command' as const })));
|
|
236
239
|
|
|
237
240
|
// Update manifest
|
|
238
241
|
manifest.backup.commands.user.push(...itemManifest.user);
|
|
@@ -324,6 +327,12 @@ export class AttachManager {
|
|
|
324
327
|
// Write updated config
|
|
325
328
|
await fs.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
326
329
|
|
|
330
|
+
// Approve MCP servers if target supports it (e.g., Claude Code needs enabledMcpjsonServers)
|
|
331
|
+
if (target.approveMCPServers) {
|
|
332
|
+
const serverNames = mcpServers.map((s) => s.name);
|
|
333
|
+
await target.approveMCPServers(projectPath, serverNames);
|
|
334
|
+
}
|
|
335
|
+
|
|
327
336
|
// Track in manifest
|
|
328
337
|
manifest.backup.config = {
|
|
329
338
|
path: configPath,
|
|
@@ -7,6 +7,11 @@ import { existsSync } from 'node:fs';
|
|
|
7
7
|
import fs from 'node:fs/promises';
|
|
8
8
|
import os from 'node:os';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
+
import {
|
|
11
|
+
computeEffectiveServers,
|
|
12
|
+
getEnabledServersFromEffective,
|
|
13
|
+
type MCPServerID,
|
|
14
|
+
} from '../config/servers.js';
|
|
10
15
|
|
|
11
16
|
export interface GlobalSettings {
|
|
12
17
|
version: string;
|
|
@@ -198,22 +203,35 @@ export class GlobalConfigService {
|
|
|
198
203
|
}
|
|
199
204
|
|
|
200
205
|
/**
|
|
201
|
-
* Load MCP config
|
|
206
|
+
* Load raw MCP config from file (may be empty if no file)
|
|
202
207
|
*/
|
|
203
208
|
async loadMCPConfig(): Promise<MCPConfig> {
|
|
204
209
|
const configPath = this.getMCPConfigPath();
|
|
205
210
|
|
|
206
211
|
if (!existsSync(configPath)) {
|
|
207
|
-
return {
|
|
208
|
-
version: '1.0.0',
|
|
209
|
-
servers: {},
|
|
210
|
-
};
|
|
212
|
+
return { version: '1.0.0', servers: {} };
|
|
211
213
|
}
|
|
212
214
|
|
|
213
215
|
const data = await fs.readFile(configPath, 'utf-8');
|
|
214
216
|
return JSON.parse(data);
|
|
215
217
|
}
|
|
216
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Get effective MCP servers using SSOT computation
|
|
221
|
+
* Merges saved config with defaults from registry
|
|
222
|
+
*/
|
|
223
|
+
getEffectiveMCPServers(savedServers: Record<string, MCPServerConfig> | undefined) {
|
|
224
|
+
return computeEffectiveServers(savedServers);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Get enabled server IDs using SSOT
|
|
229
|
+
*/
|
|
230
|
+
getEnabledServerIds(savedServers: Record<string, MCPServerConfig> | undefined): MCPServerID[] {
|
|
231
|
+
const effective = computeEffectiveServers(savedServers);
|
|
232
|
+
return getEnabledServersFromEffective(effective);
|
|
233
|
+
}
|
|
234
|
+
|
|
217
235
|
/**
|
|
218
236
|
* Save MCP config
|
|
219
237
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
2
|
import fsPromises from 'node:fs/promises';
|
|
4
3
|
import path from 'node:path';
|
|
5
4
|
import chalk from 'chalk';
|
|
5
|
+
import { installToDirectory } from '../core/installers/file-installer.js';
|
|
6
6
|
import { createMCPInstaller } from '../core/installers/mcp-installer.js';
|
|
7
7
|
import type { AgentMetadata } from '../types/target-config.types.js';
|
|
8
8
|
import type { CommonOptions, MCPServerConfigUnion, SetupResult, Target } from '../types.js';
|
|
@@ -11,10 +11,10 @@ import { fileUtils, generateHelpText, pathUtils, yamlUtils } from '../utils/conf
|
|
|
11
11
|
import { CLIError } from '../utils/error-handler.js';
|
|
12
12
|
import { sanitize } from '../utils/security/security.js';
|
|
13
13
|
import {
|
|
14
|
-
transformMCPConfig as transformMCP,
|
|
15
14
|
detectTargetConfig,
|
|
16
|
-
stripFrontMatter,
|
|
17
15
|
setupSlashCommandsTo,
|
|
16
|
+
stripFrontMatter,
|
|
17
|
+
transformMCPConfig as transformMCP,
|
|
18
18
|
} from './shared/index.js';
|
|
19
19
|
|
|
20
20
|
/**
|
package/src/targets/opencode.ts
CHANGED
|
@@ -7,15 +7,15 @@ import { installFile, installToDirectory } from '../core/installers/file-install
|
|
|
7
7
|
import { createMCPInstaller } from '../core/installers/mcp-installer.js';
|
|
8
8
|
import type { AgentMetadata } from '../types/target-config.types.js';
|
|
9
9
|
import type { CommonOptions, MCPServerConfigUnion, SetupResult, Target } from '../types.js';
|
|
10
|
-
import { getAgentsDir, getOutputStylesDir
|
|
10
|
+
import { getAgentsDir, getOutputStylesDir } from '../utils/config/paths.js';
|
|
11
11
|
import { fileUtils, generateHelpText, yamlUtils } from '../utils/config/target-utils.js';
|
|
12
12
|
import { CLIError } from '../utils/error-handler.js';
|
|
13
13
|
import { secretUtils } from '../utils/security/secret-utils.js';
|
|
14
14
|
import {
|
|
15
|
-
transformMCPConfig as transformMCP,
|
|
16
15
|
detectTargetConfig,
|
|
17
|
-
stripFrontMatter,
|
|
18
16
|
setupSlashCommandsTo,
|
|
17
|
+
stripFrontMatter,
|
|
18
|
+
transformMCPConfig as transformMCP,
|
|
19
19
|
} from './shared/index.js';
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -113,19 +113,33 @@ export const transformMCPConfig = (
|
|
|
113
113
|
): Record<string, unknown> => {
|
|
114
114
|
// Claude Code format (stdio/http)
|
|
115
115
|
if (targetFormat === 'claude-code') {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
switch (config.type) {
|
|
117
|
+
case 'local':
|
|
118
|
+
return localToStdio(config as LocalConfig);
|
|
119
|
+
case 'remote':
|
|
120
|
+
return remoteToHttp(config as RemoteConfig);
|
|
121
|
+
case 'stdio':
|
|
122
|
+
return normalizeStdio(config as StdioConfig);
|
|
123
|
+
case 'http':
|
|
124
|
+
return normalizeHttp(config as HttpConfig);
|
|
125
|
+
default:
|
|
126
|
+
return config;
|
|
127
|
+
}
|
|
121
128
|
}
|
|
122
129
|
|
|
123
130
|
// OpenCode format (local/remote)
|
|
124
131
|
if (targetFormat === 'opencode') {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
switch (config.type) {
|
|
133
|
+
case 'stdio':
|
|
134
|
+
return stdioToLocal(config as StdioConfig);
|
|
135
|
+
case 'http':
|
|
136
|
+
return httpToRemote(config as HttpConfig);
|
|
137
|
+
case 'local':
|
|
138
|
+
case 'remote':
|
|
139
|
+
return config;
|
|
140
|
+
default:
|
|
141
|
+
return config;
|
|
142
|
+
}
|
|
129
143
|
}
|
|
130
144
|
|
|
131
145
|
return config;
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import fs from 'node:fs';
|
|
7
7
|
import path from 'node:path';
|
|
8
|
-
import type { CommonOptions, SetupResult, TargetConfig } from '../../types.js';
|
|
9
8
|
import { installToDirectory } from '../../core/installers/file-installer.js';
|
|
9
|
+
import type { CommonOptions, SetupResult, TargetConfig } from '../../types.js';
|
|
10
10
|
import { getAgentsDir, getSlashCommandsDir } from '../../utils/config/paths.js';
|
|
11
11
|
import { yamlUtils } from '../../utils/config/target-utils.js';
|
|
12
12
|
|
|
@@ -49,8 +49,7 @@ export const stripFrontMatter = (content: string): Promise<string> =>
|
|
|
49
49
|
/**
|
|
50
50
|
* Identity transformer - returns content unchanged
|
|
51
51
|
*/
|
|
52
|
-
export const identityTransform: ContentTransformer = (content: string) =>
|
|
53
|
-
Promise.resolve(content);
|
|
52
|
+
export const identityTransform: ContentTransformer = (content: string) => Promise.resolve(content);
|
|
54
53
|
|
|
55
54
|
// ============================================================================
|
|
56
55
|
// Pure Functions - Setup Operations
|
|
@@ -65,12 +64,10 @@ export const setupAgentsTo = async (
|
|
|
65
64
|
transformer: ContentTransformer,
|
|
66
65
|
options: SetupOptions = {}
|
|
67
66
|
): Promise<SetupResult> => {
|
|
68
|
-
const results = await installToDirectory(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
{ ...options, showProgress: false }
|
|
73
|
-
);
|
|
67
|
+
const results = await installToDirectory(getAgentsDir(), targetDir, transformer, {
|
|
68
|
+
...options,
|
|
69
|
+
showProgress: false,
|
|
70
|
+
});
|
|
74
71
|
return { count: results.length };
|
|
75
72
|
};
|
|
76
73
|
|
|
@@ -83,12 +80,10 @@ export const setupSlashCommandsTo = async (
|
|
|
83
80
|
transformer: ContentTransformer = identityTransform,
|
|
84
81
|
options: SetupOptions = {}
|
|
85
82
|
): Promise<SetupResult> => {
|
|
86
|
-
const results = await installToDirectory(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
{ ...options, showProgress: false }
|
|
91
|
-
);
|
|
83
|
+
const results = await installToDirectory(getSlashCommandsDir(), targetDir, transformer, {
|
|
84
|
+
...options,
|
|
85
|
+
showProgress: false,
|
|
86
|
+
});
|
|
92
87
|
return { count: results.length };
|
|
93
88
|
};
|
|
94
89
|
|
|
@@ -105,7 +100,9 @@ export const ensureConfigStructure = <T extends Record<string, unknown>>(
|
|
|
105
100
|
key: string,
|
|
106
101
|
defaultValue: unknown = {}
|
|
107
102
|
): T => {
|
|
108
|
-
if (config[key] !== undefined)
|
|
103
|
+
if (config[key] !== undefined) {
|
|
104
|
+
return config;
|
|
105
|
+
}
|
|
109
106
|
return { ...config, [key]: defaultValue };
|
|
110
107
|
};
|
|
111
108
|
|
|
@@ -126,10 +123,6 @@ export const resolveTargetPaths = (cwd: string, config: TargetConfig) => ({
|
|
|
126
123
|
configDir: path.join(cwd, config.configDir),
|
|
127
124
|
agentDir: path.join(cwd, config.agentDir),
|
|
128
125
|
configFile: path.join(cwd, config.configFile),
|
|
129
|
-
slashCommandsDir: config.slashCommandsDir
|
|
130
|
-
|
|
131
|
-
: undefined,
|
|
132
|
-
rulesFile: config.rulesFile
|
|
133
|
-
? path.join(cwd, config.rulesFile)
|
|
134
|
-
: undefined,
|
|
126
|
+
slashCommandsDir: config.slashCommandsDir ? path.join(cwd, config.slashCommandsDir) : undefined,
|
|
127
|
+
rulesFile: config.rulesFile ? path.join(cwd, config.rulesFile) : undefined,
|
|
135
128
|
});
|