fraim 2.0.271 → 2.0.273

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.
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.saveProviderTokenToConfig = exports.loadGlobalConfig = exports.addIDECommand = exports.runAddIDE = void 0;
39
+ exports.loadGlobalConfig = exports.addIDECommand = exports.runAddIDE = void 0;
40
40
  const commander_1 = require("commander");
41
41
  const chalk_1 = __importDefault(require("chalk"));
42
42
  const prompts_1 = __importDefault(require("prompts"));
@@ -48,9 +48,6 @@ const mcp_config_generator_1 = require("../setup/mcp-config-generator");
48
48
  const claude_code_telemetry_1 = require("../setup/claude-code-telemetry");
49
49
  const script_sync_utils_1 = require("../utils/script-sync-utils");
50
50
  const mcp_server_registry_1 = require("../mcp/mcp-server-registry");
51
- const get_provider_client_1 = require("../api/get-provider-client");
52
- const provider_prompts_1 = require("../setup/provider-prompts");
53
- const provider_registry_1 = require("../providers/provider-registry");
54
51
  const user_config_1 = require("../utils/user-config");
55
52
  const resolveGlobalConfigPath = () => {
56
53
  const primary = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'config.json');
@@ -84,41 +81,9 @@ const loadGlobalConfig = async () => {
84
81
  const config = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
85
82
  if (!config.apiKey)
86
83
  return null;
87
- // Support both old and new token format
88
- const tokens = config.tokens || {};
89
- // Backward compatibility: map old format to new
90
- // Only try to fetch provider IDs if we have a valid FRAIM key
91
- if (config.apiKey) {
92
- try {
93
- const client = (0, get_provider_client_1.getProviderClient)();
94
- const providerIds = await client.getAllProviderIds();
95
- for (const id of providerIds) {
96
- const oldKey = `${id}Token`;
97
- if (config[oldKey] && !tokens[id]) {
98
- tokens[id] = config[oldKey];
99
- }
100
- }
101
- }
102
- catch (e) {
103
- // If provider client fails (network error, invalid key, etc.),
104
- // skip backward compatibility mapping and use local fallback
105
- // This is fine - the config will still work with the new format
106
- }
107
- }
108
- // Load all provider configs
109
- const providerConfigs = {};
110
- // New format: providerConfigs object
111
- if (config.providerConfigs) {
112
- Object.entries(config.providerConfigs).forEach(([key, value]) => {
113
- const providerId = key.replace('Config', '');
114
- providerConfigs[providerId] = value;
115
- });
116
- }
117
84
  return {
118
85
  fraimKey: config.apiKey,
119
- tokens,
120
86
  mode: config.mode,
121
- providerConfigs
122
87
  };
123
88
  }
124
89
  catch (e) {
@@ -126,65 +91,7 @@ const loadGlobalConfig = async () => {
126
91
  }
127
92
  };
128
93
  exports.loadGlobalConfig = loadGlobalConfig;
129
- const promptForProviderTokenIfNeeded = async (providerId, isOptional = false) => {
130
- const client = (0, get_provider_client_1.getProviderClient)();
131
- const provider = await client.getProvider(providerId);
132
- if (!provider)
133
- return '';
134
- if (isOptional) {
135
- console.log(chalk_1.default.yellow(`\nšŸ”‘ ${provider.displayName} token (optional for conversational mode)`));
136
- console.log(chalk_1.default.gray(`${provider.displayName} token enables ${provider.displayName}-specific MCP features.\n`));
137
- const wantsToken = await (0, prompts_1.default)({
138
- type: 'confirm',
139
- name: 'addToken',
140
- message: `Do you want to add a ${provider.displayName} token?`,
141
- initial: false
142
- });
143
- if (!wantsToken.addToken) {
144
- console.log(chalk_1.default.blue(`ā„¹ļø Skipping ${provider.displayName} token - ${provider.displayName} MCP server will not be configured`));
145
- return '';
146
- }
147
- }
148
- else {
149
- console.log(chalk_1.default.yellow(`\nšŸ”‘ ${provider.displayName} token needed for MCP configuration`));
150
- console.log(chalk_1.default.gray(`This is required for ${provider.displayName} MCP servers to function properly.\n`));
151
- }
152
- try {
153
- return await (0, provider_prompts_1.promptForProviderToken)(client, providerId);
154
- }
155
- catch (error) {
156
- if (isOptional) {
157
- console.log(chalk_1.default.blue(`ā„¹ļø No ${provider.displayName} token provided - ${provider.displayName} MCP server will not be configured`));
158
- return '';
159
- }
160
- console.log(chalk_1.default.red(`${provider.displayName} token is required. Exiting.`));
161
- process.exit(1);
162
- }
163
- };
164
- const saveProviderTokenToConfig = async (providerId, token) => {
165
- const globalConfigPath = resolveGlobalConfigPath();
166
- if (fs_1.default.existsSync(globalConfigPath)) {
167
- try {
168
- const config = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
169
- // Use new tokens structure
170
- if (!config.tokens) {
171
- config.tokens = {};
172
- }
173
- config.tokens[providerId] = token;
174
- fs_1.default.writeFileSync(globalConfigPath, JSON.stringify(config, null, 2));
175
- const { getProvider } = await Promise.resolve().then(() => __importStar(require('../providers/provider-registry')));
176
- const provider = await getProvider(providerId);
177
- console.log(chalk_1.default.green(`āœ… ${provider?.displayName || providerId} token saved to global config`));
178
- }
179
- catch (e) {
180
- const { getProvider } = await Promise.resolve().then(() => __importStar(require('../providers/provider-registry')));
181
- const provider = await getProvider(providerId);
182
- console.log(chalk_1.default.yellow(`āš ļø Could not save ${provider?.displayName || providerId} token to config`));
183
- }
184
- }
185
- };
186
- exports.saveProviderTokenToConfig = saveProviderTokenToConfig;
187
- const configureIDEMCP = async (ide, fraimKey, tokens, providerConfigs) => {
94
+ const configureIDEMCP = async (ide, fraimKey) => {
188
95
  const configPath = (0, ide_detector_1.expandPath)(ide.configPath);
189
96
  console.log(chalk_1.default.blue(`šŸ”§ Configuring ${ide.name}...`));
190
97
  // Create backup if config exists
@@ -219,13 +126,10 @@ const configureIDEMCP = async (ide, fraimKey, tokens, providerConfigs) => {
219
126
  existingTomlContent = fs_1.default.readFileSync(configPath, 'utf8');
220
127
  console.log(chalk_1.default.gray(` šŸ“‹ Found existing TOML config`));
221
128
  }
222
- const newTomlContent = await (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokens, providerConfigs);
129
+ const newTomlContent = await (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, {});
223
130
  const { getAllMCPServerIds } = await Promise.resolve().then(() => __importStar(require('../mcp/mcp-server-registry')));
224
131
  const baseServerIds = getAllMCPServerIds();
225
- // Add provider server IDs from tokens
226
- const providerServerIds = Object.keys(tokens).filter(id => tokens[id]);
227
- const serversToAdd = [...baseServerIds, ...providerServerIds];
228
- const mergeResult = (0, mcp_config_generator_1.mergeTomlMCPServers)(existingTomlContent, newTomlContent, serversToAdd);
132
+ const mergeResult = (0, mcp_config_generator_1.mergeTomlMCPServers)(existingTomlContent, newTomlContent, baseServerIds);
229
133
  fs_1.default.writeFileSync(configPath, mergeResult.content);
230
134
  mergeResult.addedServers.forEach(server => {
231
135
  console.log(chalk_1.default.green(` āœ… Added ${server} MCP server`));
@@ -239,14 +143,14 @@ const configureIDEMCP = async (ide, fraimKey, tokens, providerConfigs) => {
239
143
  }
240
144
  else {
241
145
  // For JSON configs - intelligent merging
242
- const newConfig = await (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokens, providerConfigs);
146
+ const newConfig = await (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, {});
243
147
  const newMCPServers = newConfig[serversKey] || newConfig.mcpServers || {};
244
148
  // Merge MCP servers intelligently
245
149
  const mergedMCPServers = { ...existingMCPServers };
246
150
  const addedServers = [];
247
151
  const updatedServers = [];
248
152
  const skippedServers = [];
249
- const alwaysUpdateServers = new Set(['fraim', 'github', 'gitlab', 'jira', 'ado', 'linear']);
153
+ const alwaysUpdateServers = new Set(['fraim']);
250
154
  for (const [serverName, serverConfig] of Object.entries(newMCPServers)) {
251
155
  if (!existingMCPServers[serverName]) {
252
156
  mergedMCPServers[serverName] = serverConfig;
@@ -302,7 +206,7 @@ const listSupportedIDEs = () => {
302
206
  console.log(chalk_1.default.yellow(' Gemini aliases: gemini, gemini-cli, gemini cli'));
303
207
  console.log(chalk_1.default.yellow(' GitHub Copilot CLI aliases: copilot, copilot-cli, github copilot cli'));
304
208
  };
305
- const promptForIDESelection = async (availableIDEs, tokens) => {
209
+ const promptForIDESelection = async (availableIDEs) => {
306
210
  console.log(chalk_1.default.green(`āœ… Found ${availableIDEs.length} IDEs that can be configured:\n`));
307
211
  availableIDEs.forEach((ide, index) => {
308
212
  const configExists = fs_1.default.existsSync((0, ide_detector_1.expandPath)(ide.configPath));
@@ -315,14 +219,6 @@ const promptForIDESelection = async (availableIDEs, tokens) => {
315
219
  for (const server of mcp_server_registry_1.BASE_MCP_SERVERS) {
316
220
  console.log(chalk_1.default.gray(` • ${server.id} (${server.description})`));
317
221
  }
318
- // Show provider servers (only if tokens exist)
319
- const allProviders = await (0, provider_registry_1.getAllProviders)();
320
- for (const provider of allProviders) {
321
- const hasToken = tokens?.[provider.id];
322
- if (hasToken && provider.mcpServer) {
323
- console.log(chalk_1.default.gray(` - ${provider.id} (${provider.description})`));
324
- }
325
- }
326
222
  const response = await (0, prompts_1.default)({
327
223
  type: 'text',
328
224
  name: 'selection',
@@ -360,25 +256,8 @@ const runAddIDE = async (options) => {
360
256
  console.log(chalk_1.default.yellow('šŸ’” Please run "fraim setup" first to configure your FRAIM keys.'));
361
257
  process.exit(1);
362
258
  }
363
- const platformTokens = globalConfig.tokens || {};
364
259
  const isConversationalMode = globalConfig.mode === 'conversational';
365
- // Check if any provider tokens exist
366
- const allProviderIds = await (0, provider_registry_1.getAllProviderIds)();
367
- const hasAnyToken = allProviderIds.some(id => platformTokens[id]);
368
- if (!hasAnyToken && !isConversationalMode && !options.skipTokenPrompts) {
369
- console.log(chalk_1.default.yellow('āš ļø No provider tokens found in configuration.'));
370
- // Prompt for first integrated provider as default
371
- const integratedProviders = await (0, provider_registry_1.getProvidersWithCapability)('integrated');
372
- const defaultProviderId = integratedProviders[0]?.id;
373
- if (defaultProviderId) {
374
- const token = await promptForProviderTokenIfNeeded(defaultProviderId, false);
375
- if (token) {
376
- await saveProviderTokenToConfig(defaultProviderId, token);
377
- platformTokens[defaultProviderId] = token;
378
- }
379
- }
380
- }
381
- if (isConversationalMode && !hasAnyToken) {
260
+ if (isConversationalMode) {
382
261
  console.log(chalk_1.default.blue('ā„¹ļø Conversational mode: Configuring MCP without platform integration\n'));
383
262
  }
384
263
  else {
@@ -415,7 +294,7 @@ const runAddIDE = async (options) => {
415
294
  }
416
295
  else {
417
296
  // Interactive selection
418
- idesToConfigure = await promptForIDESelection(detectedIDEs, platformTokens);
297
+ idesToConfigure = await promptForIDESelection(detectedIDEs);
419
298
  }
420
299
  }
421
300
  if (idesToConfigure.length === 0) {
@@ -429,7 +308,7 @@ const runAddIDE = async (options) => {
429
308
  };
430
309
  for (const ide of idesToConfigure) {
431
310
  try {
432
- await configureIDEMCP(ide, globalConfig.fraimKey, platformTokens, globalConfig.providerConfigs);
311
+ await configureIDEMCP(ide, globalConfig.fraimKey);
433
312
  results.successful.push(ide.name);
434
313
  (0, user_config_1.addInstalledIde)((0, ide_detector_1.getAdapterConfigType)(ide));
435
314
  }
@@ -1,275 +1,39 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.addProviderCommand = exports.runAddProvider = void 0;
3
+ exports.addProviderCommand = exports.runAddProvider = exports.explainAgentManagedProviderSetup = void 0;
7
4
  const commander_1 = require("commander");
8
- const chalk_1 = __importDefault(require("chalk"));
9
- const prompts_1 = __importDefault(require("prompts"));
10
- const fs_1 = __importDefault(require("fs"));
11
- const path_1 = __importDefault(require("path"));
12
- const child_process_1 = require("child_process");
13
- const mcp_config_generator_1 = require("../setup/mcp-config-generator");
14
- const ide_detector_1 = require("../setup/ide-detector");
15
- const script_sync_utils_1 = require("../utils/script-sync-utils");
16
- const mcp_config_generator_2 = require("../setup/mcp-config-generator");
17
- const provider_registry_1 = require("../providers/provider-registry");
18
- const provider_prompts_1 = require("../setup/provider-prompts");
19
- const get_provider_client_1 = require("../api/get-provider-client");
20
- const loadGlobalConfig = () => {
21
- const globalConfigPath = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'config.json');
22
- if (!fs_1.default.existsSync(globalConfigPath)) {
23
- return null;
24
- }
25
- try {
26
- return JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
27
- }
28
- catch (e) {
29
- return null;
30
- }
31
- };
32
- const saveGlobalConfig = (config) => {
33
- const globalConfigPath = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'config.json');
34
- fs_1.default.writeFileSync(globalConfigPath, JSON.stringify(config, null, 2));
35
- };
36
- const forEachInstalledIDE = async (callback) => {
37
- const detectedIDEs = (0, ide_detector_1.detectInstalledIDEs)();
38
- if (detectedIDEs.length === 0) {
39
- console.log(chalk_1.default.yellow('āš ļø No IDEs detected. MCP configs not updated.'));
40
- return;
41
- }
42
- console.log(chalk_1.default.blue(`\nšŸ”§ Updating ${detectedIDEs.length} IDE configuration(s)...\n`));
43
- for (const ide of detectedIDEs) {
44
- try {
45
- await callback(ide, (0, ide_detector_1.expandPath)(ide.configPath));
46
- }
47
- catch (error) {
48
- console.log(chalk_1.default.red(`āŒ Failed to update ${ide.name}: ${error instanceof Error ? error.message : 'Unknown error'}`));
49
- }
50
- }
51
- };
52
- const writeOAuthProviderToIDEConfigs = async (provider, mcpUrl) => {
53
- const urlOnlyEntry = { type: 'http', url: mcpUrl };
54
- await forEachInstalledIDE(async (ide, configPath) => {
55
- if (provider === 'github' && ide.configType === 'copilot-cli') {
56
- console.log(chalk_1.default.gray(`Skipped ${ide.name} GitHub MCP entry (built into Copilot CLI)`));
57
- return;
58
- }
59
- if (ide.configFormat === 'json') {
60
- let ideConfig = {};
61
- if (fs_1.default.existsSync(configPath)) {
62
- ideConfig = JSON.parse(fs_1.default.readFileSync(configPath, 'utf8'));
63
- }
64
- const serversKey = ide.configType === 'vscode' ? 'servers' : 'mcpServers';
65
- if (!ideConfig[serversKey]) {
66
- ideConfig[serversKey] = {};
67
- }
68
- ideConfig[serversKey][provider] = ide.configType === 'copilot-cli'
69
- ? { ...urlOnlyEntry, tools: ['*'] }
70
- : urlOnlyEntry;
71
- fs_1.default.writeFileSync(configPath, JSON.stringify(ideConfig, null, 2));
72
- console.log(chalk_1.default.green(`āœ… Updated ${ide.name} config`));
73
- }
74
- else if (ide.configFormat === 'toml') {
75
- console.log(chalk_1.default.yellow(`āš ļø ${ide.name} uses TOML format — update manually: add [tools.${provider}] url = "${mcpUrl}"`));
76
- }
77
- });
78
- };
79
- const updateIDEConfigs = async (provider, fraimKey, token, providerConfig) => {
80
- const tokenConfig = { [provider]: token };
81
- const providerConfigs = providerConfig ? { [provider]: providerConfig } : {};
82
- await forEachInstalledIDE(async (ide, configPath) => {
83
- if (ide.configFormat === 'json') {
84
- let ideConfig = {};
85
- if (fs_1.default.existsSync(configPath)) {
86
- ideConfig = JSON.parse(fs_1.default.readFileSync(configPath, 'utf8'));
87
- }
88
- const mcpConfig = await (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokenConfig, providerConfigs);
89
- const serversKey = ide.configType === 'vscode' ? 'servers' : 'mcpServers';
90
- if (!ideConfig[serversKey]) {
91
- ideConfig[serversKey] = {};
92
- }
93
- const providerServerKey = provider === 'ado' ? 'ado' : provider;
94
- if (mcpConfig[serversKey] && mcpConfig[serversKey][providerServerKey]) {
95
- ideConfig[serversKey][providerServerKey] = mcpConfig[serversKey][providerServerKey];
96
- fs_1.default.writeFileSync(configPath, JSON.stringify(ideConfig, null, 2));
97
- console.log(chalk_1.default.green(`āœ… Updated ${ide.name} config`));
98
- }
99
- }
100
- else if (ide.configFormat === 'toml') {
101
- const mcpConfigToml = await (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokenConfig, providerConfigs);
102
- if (fs_1.default.existsSync(configPath)) {
103
- const existingContent = fs_1.default.readFileSync(configPath, 'utf8');
104
- const serversToMerge = [provider === 'ado' ? 'ado' : provider];
105
- const mergeResult = (0, mcp_config_generator_2.mergeTomlMCPServers)(existingContent, mcpConfigToml, serversToMerge);
106
- fs_1.default.writeFileSync(configPath, mergeResult.content);
107
- console.log(chalk_1.default.green(`āœ… Updated ${ide.name} config`));
108
- }
109
- else {
110
- console.log(chalk_1.default.yellow(`āš ļø ${ide.name} config not found, skipping`));
111
- }
112
- }
113
- });
114
- };
115
- const offerModeSwitch = async (config) => {
116
- if (config.mode === 'split') {
117
- console.log(chalk_1.default.gray('\nAlready in split mode.'));
118
- return;
119
- }
120
- console.log(chalk_1.default.blue('\nšŸ”„ Mode Switch Available'));
121
- console.log(chalk_1.default.gray('You can now use split mode (separate platforms for code and issues).'));
122
- console.log(chalk_1.default.gray('Example: GitHub for code, Jira for issue tracking\n'));
123
- // Skip prompt if FRAIM_AUTO_SWITCH_MODE is set (for testing)
124
- const shouldSwitch = process.env.FRAIM_AUTO_SWITCH_MODE === 'true';
125
- if (!shouldSwitch && !process.env.FRAIM_AUTO_SWITCH_MODE) {
126
- const response = await (0, prompts_1.default)({
127
- type: 'confirm',
128
- name: 'switchMode',
129
- message: 'Switch to split mode?',
130
- initial: false
131
- });
132
- if (response.switchMode) {
133
- config.mode = 'split';
134
- saveGlobalConfig(config);
135
- console.log(chalk_1.default.green('āœ… Switched to split mode'));
136
- console.log(chalk_1.default.gray('Run "fraim init-project" in your projects to use split mode'));
137
- }
138
- }
139
- else if (shouldSwitch) {
140
- config.mode = 'split';
141
- saveGlobalConfig(config);
142
- console.log(chalk_1.default.green('āœ… Switched to split mode'));
143
- console.log(chalk_1.default.gray('Run "fraim init-project" in your projects to use split mode'));
144
- }
145
- };
146
- const runAddProvider = async (provider, options) => {
147
- console.log(chalk_1.default.blue(`\nšŸ”§ Adding ${provider.toUpperCase()} provider...\n`));
148
- // Check global setup exists
149
- const config = loadGlobalConfig();
150
- if (!config) {
151
- console.log(chalk_1.default.red('āŒ Global FRAIM setup not found.'));
152
- console.log(chalk_1.default.yellow('Please run: fraim setup --key=<your-fraim-key>'));
153
- process.exit(1);
154
- }
155
- // Initialize tokens object if it doesn't exist
156
- if (!config.tokens) {
157
- config.tokens = {};
158
- }
159
- // Check if provider already configured
160
- const providerName = await (0, provider_registry_1.getProviderDisplayName)(provider);
161
- const hasExistingConfig = config.tokens[provider] ||
162
- (provider === 'jira' && config.providerConfigs?.jiraConfig?.baseUrl) ||
163
- (provider === 'ado' && config.providerConfigs?.adoConfig?.organization);
164
- if (hasExistingConfig) {
165
- const displayInfo = provider === 'jira' && config.providerConfigs?.jiraConfig?.baseUrl
166
- ? ` (${config.providerConfigs.jiraConfig.baseUrl})`
167
- : provider === 'ado' && config.providerConfigs?.adoConfig?.organization
168
- ? ` (${config.providerConfigs.adoConfig.organization})`
169
- : '';
170
- console.log(chalk_1.default.yellow(`āš ļø ${providerName} is already configured${displayInfo}`));
171
- // Skip prompt if FRAIM_FORCE_OVERWRITE is set (for testing)
172
- if (!process.env.FRAIM_FORCE_OVERWRITE) {
173
- const response = await (0, prompts_1.default)({
174
- type: 'confirm',
175
- name: 'overwrite',
176
- message: `Overwrite existing ${providerName} configuration?`,
177
- initial: false
178
- });
179
- if (!response.overwrite) {
180
- console.log(chalk_1.default.gray('Cancelled.'));
181
- process.exit(0);
182
- }
183
- }
184
- }
185
- // Check prerequisites for the provider's MCP server command
186
- const providerDefCheck = await (0, provider_registry_1.getProvider)(provider);
187
- if (providerDefCheck?.mcpServer?.type === 'stdio' && providerDefCheck.mcpServer.command && !process.env.FRAIM_SKIP_PREREQ_CHECK) {
188
- const cmd = providerDefCheck.mcpServer.command;
189
- // Only check non-standard commands (npx/node are assumed to be available)
190
- if (!['npx', 'node', 'npm'].includes(cmd)) {
191
- try {
192
- (0, child_process_1.execSync)(`${cmd} --version`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
193
- }
194
- catch {
195
- const installInstructions = {
196
- uvx: 'Install uv first: https://docs.astral.sh/uv/getting-started/installation/\n' +
197
- ' macOS/Linux: curl -LsSf https://astral.sh/uv/install.sh | sh\n' +
198
- ' Windows: powershell -c "irm https://astral.sh/uv/install.ps1 | iex"',
199
- };
200
- console.log(chalk_1.default.red(`\nāŒ "${cmd}" is required for ${providerName} but was not found on your system.\n`));
201
- console.log(chalk_1.default.yellow(installInstructions[cmd] || `Please install "${cmd}" and try again.`));
202
- console.log(chalk_1.default.gray('\nAfter installing, restart your terminal and run this command again.'));
203
- process.exit(1);
204
- }
205
- }
206
- }
207
- // Detect OAuth-first providers: HTTP MCP with no authHeaderTemplate means the IDE
208
- // handles OAuth natively. Skip credential collection and write a URL-only IDE entry.
209
- const providerDefOAuth = await (0, provider_registry_1.getProvider)(provider);
210
- if (providerDefOAuth?.mcpServer?.type === 'http' &&
211
- !providerDefOAuth.mcpServer.authHeaderTemplate) {
212
- const mcpUrl = providerDefOAuth.mcpServer.url;
213
- await writeOAuthProviderToIDEConfigs(provider, mcpUrl);
214
- console.log(chalk_1.default.green(`\nāœ… ${providerName} configured for IDE-native OAuth.`));
215
- console.log(chalk_1.default.cyan('\nšŸ’” Next steps:'));
216
- console.log(chalk_1.default.gray(' 1. Restart your IDE(s) to load the new MCP server'));
217
- console.log(chalk_1.default.gray(' 2. On first use, your IDE will prompt you to sign in to ' + providerName));
218
- return;
219
- }
220
- // Get credentials using generic prompt system
221
- try {
222
- // Build provided tokens/configs from CLI options
223
- const providedTokens = {};
224
- const providedConfigs = {};
225
- if (options.token) {
226
- providedTokens[provider] = options.token;
227
- }
228
- // Handle provider-specific config options (e.g., Jira URL/email or ADO organization)
229
- if (options.url || options.email || options.organization) {
230
- providedConfigs[provider] = {
231
- ...(options.url && { baseUrl: options.url.replace(/^https?:\/\//, '').replace(/\/$/, '') }),
232
- ...(options.email && { email: options.email }),
233
- ...(options.organization && { organization: options.organization })
234
- };
235
- }
236
- // Get credentials using generic prompt system
237
- const client = (0, get_provider_client_1.getProviderClient)();
238
- const creds = await (0, provider_prompts_1.promptForProviderCredentials)(client, provider, providedTokens[provider], providedConfigs[provider]);
239
- // Write provider MCP entry directly to IDE configs (token not stored in global config)
240
- await updateIDEConfigs(provider, config.apiKey, creds.token, creds.config);
241
- // Offer mode switch if adding a provider with issues capability
242
- const providerDef = await (0, provider_registry_1.getProvider)(provider);
243
- if (providerDef?.capabilities.includes('issues') && provider === 'jira') {
244
- await offerModeSwitch(config);
245
- }
246
- console.log(chalk_1.default.green('\nšŸŽ‰ Provider added successfully!'));
247
- console.log(chalk_1.default.cyan('\nšŸ’” Next steps:'));
248
- console.log(chalk_1.default.gray(' 1. Restart your IDE(s) to load new MCP servers'));
249
- if (provider === 'jira' && config.mode === 'split') {
250
- console.log(chalk_1.default.gray(' 2. Run "fraim init-project" in your projects to use split mode'));
251
- }
252
- }
253
- catch (error) {
254
- console.log(chalk_1.default.red(`\nāŒ Failed to add provider: ${error instanceof Error ? error.message : 'Unknown error'}`));
255
- process.exit(1);
256
- }
5
+ const MAX_PROVIDER_LABEL_LENGTH = 80;
6
+ const safeProviderLabel = (provider) => {
7
+ const sanitized = provider
8
+ .replace(/\p{C}/gu, '')
9
+ .trim()
10
+ .slice(0, MAX_PROVIDER_LABEL_LENGTH);
11
+ return sanitized || 'this provider';
12
+ };
13
+ const explainAgentManagedProviderSetup = (provider) => {
14
+ const safeProvider = safeProviderLabel(provider);
15
+ return {
16
+ provider: safeProvider,
17
+ changed: false,
18
+ exitCode: 1,
19
+ message: [
20
+ 'Provider setup is managed by your FRAIM agent.',
21
+ `Ask your agent: Use the FRAIM connect-mcp skill to connect ${safeProvider}.`,
22
+ 'The legacy fraim add-provider command made no changes.',
23
+ ].join('\n'),
24
+ };
25
+ };
26
+ exports.explainAgentManagedProviderSetup = explainAgentManagedProviderSetup;
27
+ const runAddProvider = (provider) => {
28
+ const explanation = (0, exports.explainAgentManagedProviderSetup)(provider);
29
+ console.error(explanation.message);
30
+ return explanation;
257
31
  };
258
32
  exports.runAddProvider = runAddProvider;
259
33
  exports.addProviderCommand = new commander_1.Command('add-provider')
260
- .description(`Add or update a provider after initial setup`)
261
- .argument('<provider>', `Provider to add`)
262
- .option('--token <token>', 'Provider token (will prompt if not provided)')
263
- .option('--organization <organization>', 'Organization (for providers that require it)')
264
- .option('--email <email>', 'Email (for providers that require it)')
265
- .option('--url <url>', 'Instance URL (for providers that require it)')
266
- .option('--no-validate', 'Skip token validation')
267
- .action(async (provider, options) => {
268
- if (!(await (0, provider_registry_1.isValidProviderId)(provider))) {
269
- const allProviderIds = await (0, provider_registry_1.getAllProviderIds)();
270
- console.log(chalk_1.default.red(`āŒ Invalid provider: ${provider}`));
271
- console.log(chalk_1.default.yellow(`Valid providers: ${allProviderIds.join(', ')}`));
272
- process.exit(1);
273
- }
274
- await (0, exports.runAddProvider)(provider, options);
34
+ .description('Deprecated: provider setup is agent-managed through the FRAIM connect-mcp skill')
35
+ .argument('<provider>', 'Provider to connect through your FRAIM agent')
36
+ .action((provider) => {
37
+ const result = (0, exports.runAddProvider)(provider);
38
+ process.exitCode = result.exitCode;
275
39
  });
@@ -10,10 +10,10 @@ exports.loginCommand = new commander_1.Command('login')
10
10
  .description('Login to platforms (GitHub, etc.)');
11
11
  exports.loginCommand
12
12
  .command('github')
13
- .description('Configure GitHub MCP access (redirects to add-provider)')
13
+ .description('Explain agent-managed GitHub MCP setup')
14
14
  .action(async () => {
15
- console.log(chalk_1.default.blue('\nšŸ’” GitHub authentication is now handled natively by your IDE.'));
16
- console.log(chalk_1.default.gray('To configure GitHub MCP access, run:'));
17
- console.log(chalk_1.default.cyan('\n fraim add-provider github\n'));
18
- console.log(chalk_1.default.gray('Your IDE will prompt for GitHub sign-in automatically on first use.'));
15
+ console.log(chalk_1.default.blue('\nšŸ’” GitHub MCP setup is managed by your FRAIM agent.'));
16
+ console.log(chalk_1.default.gray('Ask your agent:'));
17
+ console.log(chalk_1.default.cyan('\n Use the FRAIM connect-mcp skill to connect github.\n'));
18
+ console.log(chalk_1.default.gray('The agent will follow current GitHub and host guidance and verify the connection.'));
19
19
  });