@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 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.5",
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 { getRequiredEnvVars, MCP_SERVER_REGISTRY, type MCPServerID } from '../config/servers.js';
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, printHeader, printConfirmation } from './settings/index.js';
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 currentServers = mcpConfig.servers || {};
212
+ const savedServers = mcpConfig.servers || {};
208
213
 
209
- // Get all servers from registry
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: currentEnabled.includes(id) || server.defaultInInit,
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
- if (selectedServers.includes(id)) {
237
- if (currentServers[id]) {
238
- currentServers[id].enabled = true;
239
- } else {
240
- currentServers[id] = { enabled: true, env: {} };
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 server = currentServers[serverId];
254
+ const serverState = updatedServers[serverId];
254
255
 
255
256
  for (const envKey of requiredEnvVars) {
256
- const hasKey = server.env?.[envKey];
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
- if (!server.env) {
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 = currentServers;
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') {
@@ -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 enabledServers = await this.configService.getEnabledMCPServers();
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 [serverKey, serverConfig] of Object.entries(enabledServers)) {
92
- // Lookup server definition in registry
93
- const serverDef = MCP_SERVER_REGISTRY[serverKey];
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 global config
104
- if (serverConfig.env && Object.keys(serverConfig.env).length > 0) {
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, ...serverConfig.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(commands, commandsDir, 'command');
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
  /**
@@ -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, getSlashCommandsDir } from '../utils/config/paths.js';
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
- if (config.type === 'local') return localToStdio(config as LocalConfig);
117
- if (config.type === 'remote') return remoteToHttp(config as RemoteConfig);
118
- if (config.type === 'stdio') return normalizeStdio(config as StdioConfig);
119
- if (config.type === 'http') return normalizeHttp(config as HttpConfig);
120
- return config;
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
- if (config.type === 'stdio') return stdioToLocal(config as StdioConfig);
126
- if (config.type === 'http') return httpToRemote(config as HttpConfig);
127
- if (config.type === 'local' || config.type === 'remote') return config;
128
- return config;
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
- getAgentsDir(),
70
- targetDir,
71
- transformer,
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
- getSlashCommandsDir(),
88
- targetDir,
89
- transformer,
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) return config;
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
- ? path.join(cwd, config.slashCommandsDir)
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
  });