fraim-framework 2.0.81 → 2.0.83

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 (33) hide show
  1. package/dist/src/cli/api/get-provider-client.js +41 -0
  2. package/dist/src/cli/api/provider-client.js +107 -0
  3. package/dist/src/cli/commands/add-ide.js +144 -77
  4. package/dist/src/cli/commands/add-provider.js +223 -0
  5. package/dist/src/cli/commands/doctor.js +131 -111
  6. package/dist/src/cli/commands/init-project.js +67 -31
  7. package/dist/src/cli/commands/setup.js +247 -563
  8. package/dist/src/cli/commands/sync.js +2 -2
  9. package/dist/src/cli/commands/test-mcp.js +35 -1
  10. package/dist/src/cli/doctor/check-runner.js +199 -0
  11. package/dist/src/cli/doctor/checks/global-setup-checks.js +220 -0
  12. package/dist/src/cli/doctor/checks/ide-config-checks.js +250 -0
  13. package/dist/src/cli/doctor/checks/mcp-connectivity-checks.js +381 -0
  14. package/dist/src/cli/doctor/checks/project-setup-checks.js +282 -0
  15. package/dist/src/cli/doctor/checks/scripts-checks.js +157 -0
  16. package/dist/src/cli/doctor/checks/workflow-checks.js +247 -0
  17. package/dist/src/cli/doctor/reporters/console-reporter.js +96 -0
  18. package/dist/src/cli/doctor/reporters/json-reporter.js +11 -0
  19. package/dist/src/cli/doctor/types.js +6 -0
  20. package/dist/src/cli/fraim.js +44 -3
  21. package/dist/src/cli/mcp/ide-formats.js +243 -0
  22. package/dist/src/cli/mcp/mcp-server-builder.js +48 -0
  23. package/dist/src/cli/mcp/mcp-server-registry.js +159 -0
  24. package/dist/src/cli/mcp/types.js +3 -0
  25. package/dist/src/cli/providers/local-provider-registry.js +145 -0
  26. package/dist/src/cli/providers/provider-registry.js +230 -0
  27. package/dist/src/cli/setup/auto-mcp-setup.js +56 -118
  28. package/dist/src/cli/setup/mcp-config-generator.js +64 -321
  29. package/dist/src/cli/setup/provider-prompts.js +300 -0
  30. package/dist/src/cli/utils/remote-sync.js +22 -2
  31. package/package.json +4 -2
  32. package/dist/src/cli/commands/install.js +0 -86
  33. package/dist/src/cli/setup/token-validator.js +0 -57
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getProviderClient = getProviderClient;
7
+ // Helper to get provider client with FRAIM key from config
8
+ const provider_client_1 = require("./provider-client");
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const os_1 = __importDefault(require("os"));
12
+ /**
13
+ * Get user FRAIM directory
14
+ */
15
+ function getUserFraimDir() {
16
+ return process.env.FRAIM_USER_DIR || path_1.default.join(os_1.default.homedir(), '.fraim');
17
+ }
18
+ /**
19
+ * Get provider client using FRAIM key from global config
20
+ * Throws error if no config found - caller should handle and use local fallback
21
+ */
22
+ function getProviderClient() {
23
+ const globalConfigPath = path_1.default.join(getUserFraimDir(), 'config.json');
24
+ if (!fs_1.default.existsSync(globalConfigPath)) {
25
+ throw new Error('No FRAIM configuration found');
26
+ }
27
+ try {
28
+ const config = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
29
+ const fraimKey = config.apiKey;
30
+ if (!fraimKey) {
31
+ throw new Error('FRAIM API key not found in config');
32
+ }
33
+ // Use FRAIM_REMOTE_URL if set (for tests or custom deployments)
34
+ // Otherwise ProviderClient will use its default
35
+ const serverUrl = process.env.FRAIM_REMOTE_URL || undefined;
36
+ return new provider_client_1.ProviderClient(fraimKey, serverUrl);
37
+ }
38
+ catch (error) {
39
+ throw new Error(`Failed to load FRAIM configuration: ${error.message}`);
40
+ }
41
+ }
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ProviderClient = void 0;
7
+ // CLI client for fetching provider metadata from server
8
+ const axios_1 = __importDefault(require("axios"));
9
+ class ProviderClient {
10
+ constructor(fraimKey, serverUrl) {
11
+ this.serverUrl = serverUrl || process.env.FRAIM_REMOTE_URL || 'https://fraim.wellnessatwork.me';
12
+ this.client = axios_1.default.create({
13
+ baseURL: this.serverUrl,
14
+ headers: {
15
+ 'x-api-key': fraimKey,
16
+ 'Content-Type': 'application/json'
17
+ },
18
+ timeout: 10000 // 10 second timeout
19
+ });
20
+ }
21
+ /**
22
+ * Get all providers from server
23
+ */
24
+ async getAllProviders() {
25
+ try {
26
+ const response = await this.client.get('/api/providers');
27
+ return response.data.providers;
28
+ }
29
+ catch (error) {
30
+ if (error.response) {
31
+ // Server responded with error status
32
+ throw new Error(`Failed to fetch providers: ${error.response.status} - ${JSON.stringify(error.response.data)}`);
33
+ }
34
+ else if (error.request) {
35
+ // Request was made but no response
36
+ throw new Error(`Failed to fetch providers: No response from server`);
37
+ }
38
+ else {
39
+ // Something else happened
40
+ throw new Error(`Failed to fetch providers: ${error.message}`);
41
+ }
42
+ }
43
+ }
44
+ /**
45
+ * Get a specific provider by ID
46
+ */
47
+ async getProvider(id) {
48
+ const providers = await this.getAllProviders();
49
+ return providers.find(p => p.id === id) || null;
50
+ }
51
+ /**
52
+ * Get provider config schema
53
+ */
54
+ async getProviderSchema(id) {
55
+ try {
56
+ const response = await this.client.get(`/api/providers/${id}/schema`);
57
+ return response.data;
58
+ }
59
+ catch (error) {
60
+ if (error.response?.status === 404) {
61
+ throw new Error(`Provider '${id}' not found`);
62
+ }
63
+ throw new Error(`Failed to fetch provider schema: ${error.message}`);
64
+ }
65
+ }
66
+ /**
67
+ * Validate provider config
68
+ */
69
+ async validateProviderConfig(id, config) {
70
+ try {
71
+ const response = await this.client.post(`/api/providers/${id}/validate`, config);
72
+ return response.data;
73
+ }
74
+ catch (error) {
75
+ throw new Error(`Failed to validate provider config: ${error.message}`);
76
+ }
77
+ }
78
+ /**
79
+ * Get all provider IDs
80
+ */
81
+ async getAllProviderIds() {
82
+ const providers = await this.getAllProviders();
83
+ return providers.map(p => p.id);
84
+ }
85
+ /**
86
+ * Get providers with a specific capability
87
+ */
88
+ async getProvidersWithCapability(capability) {
89
+ const providers = await this.getAllProviders();
90
+ return providers.filter(p => p.capabilities.includes(capability));
91
+ }
92
+ /**
93
+ * Check if a provider has a specific capability
94
+ */
95
+ async providerHasCapability(providerId, capability) {
96
+ const provider = await this.getProvider(providerId);
97
+ return provider ? provider.capabilities.includes(capability) : false;
98
+ }
99
+ /**
100
+ * Check if provider requires additional config
101
+ */
102
+ async requiresAdditionalConfig(providerId) {
103
+ const provider = await this.getProvider(providerId);
104
+ return provider?.hasAdditionalConfig || false;
105
+ }
106
+ }
107
+ exports.ProviderClient = ProviderClient;
@@ -1,9 +1,42 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
5
38
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.saveGitHubTokenToConfig = exports.loadGlobalConfig = exports.addIDECommand = exports.runAddIDE = void 0;
39
+ exports.saveProviderTokenToConfig = exports.loadGlobalConfig = exports.addIDECommand = exports.runAddIDE = void 0;
7
40
  const commander_1 = require("commander");
8
41
  const chalk_1 = __importDefault(require("chalk"));
9
42
  const prompts_1 = __importDefault(require("prompts"));
@@ -13,20 +46,52 @@ const ide_detector_1 = require("../setup/ide-detector");
13
46
  const mcp_config_generator_1 = require("../setup/mcp-config-generator");
14
47
  const codex_local_config_1 = require("../setup/codex-local-config");
15
48
  const script_sync_utils_1 = require("../utils/script-sync-utils");
16
- const loadGlobalConfig = () => {
49
+ const mcp_server_registry_1 = require("../mcp/mcp-server-registry");
50
+ const get_provider_client_1 = require("../api/get-provider-client");
51
+ const provider_prompts_1 = require("../setup/provider-prompts");
52
+ const provider_registry_1 = require("../providers/provider-registry");
53
+ const loadGlobalConfig = async () => {
17
54
  const globalConfigPath = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'config.json');
18
55
  if (!fs_1.default.existsSync(globalConfigPath)) {
19
56
  return null;
20
57
  }
21
58
  try {
22
59
  const config = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
60
+ // Support both old and new token format
61
+ const tokens = config.tokens || {};
62
+ // Backward compatibility: map old format to new
63
+ // Only try to fetch provider IDs if we have a valid FRAIM key
64
+ if (config.apiKey) {
65
+ try {
66
+ const client = (0, get_provider_client_1.getProviderClient)();
67
+ const providerIds = await client.getAllProviderIds();
68
+ for (const id of providerIds) {
69
+ const oldKey = `${id}Token`;
70
+ if (config[oldKey] && !tokens[id]) {
71
+ tokens[id] = config[oldKey];
72
+ }
73
+ }
74
+ }
75
+ catch (e) {
76
+ // If provider client fails (network error, invalid key, etc.),
77
+ // skip backward compatibility mapping and use local fallback
78
+ // This is fine - the config will still work with the new format
79
+ }
80
+ }
81
+ // Load all provider configs
82
+ const providerConfigs = {};
83
+ // New format: providerConfigs object
84
+ if (config.providerConfigs) {
85
+ Object.entries(config.providerConfigs).forEach(([key, value]) => {
86
+ const providerId = key.replace('Config', '');
87
+ providerConfigs[providerId] = value;
88
+ });
89
+ }
23
90
  return {
24
91
  fraimKey: config.apiKey,
25
- githubToken: config.tokens?.github || config.githubToken, // Support both old and new format
26
- gitlabToken: config.tokens?.gitlab,
27
- jiraToken: config.tokens?.jira,
92
+ tokens,
28
93
  mode: config.mode,
29
- jiraConfig: config.jiraConfig
94
+ providerConfigs
30
95
  };
31
96
  }
32
97
  catch (e) {
@@ -34,71 +99,65 @@ const loadGlobalConfig = () => {
34
99
  }
35
100
  };
36
101
  exports.loadGlobalConfig = loadGlobalConfig;
37
- const promptForGitHubToken = async (isConversationalMode = false) => {
38
- if (isConversationalMode) {
39
- console.log(chalk_1.default.yellow('\n🔑 GitHub token (optional for conversational mode)'));
40
- console.log(chalk_1.default.gray('GitHub token enables GitHub-specific MCP features.\n'));
102
+ const promptForProviderTokenIfNeeded = async (providerId, isOptional = false) => {
103
+ const client = (0, get_provider_client_1.getProviderClient)();
104
+ const provider = await client.getProvider(providerId);
105
+ if (!provider)
106
+ return '';
107
+ if (isOptional) {
108
+ console.log(chalk_1.default.yellow(`\n🔑 ${provider.displayName} token (optional for conversational mode)`));
109
+ console.log(chalk_1.default.gray(`${provider.displayName} token enables ${provider.displayName}-specific MCP features.\n`));
41
110
  const wantsToken = await (0, prompts_1.default)({
42
111
  type: 'confirm',
43
112
  name: 'addToken',
44
- message: 'Do you want to add a GitHub token?',
113
+ message: `Do you want to add a ${provider.displayName} token?`,
45
114
  initial: false
46
115
  });
47
116
  if (!wantsToken.addToken) {
48
- console.log(chalk_1.default.blue('ℹ️ Skipping GitHub token - GitHub MCP server will not be configured'));
117
+ console.log(chalk_1.default.blue(`ℹ️ Skipping ${provider.displayName} token - ${provider.displayName} MCP server will not be configured`));
49
118
  return '';
50
119
  }
51
120
  }
52
121
  else {
53
- console.log(chalk_1.default.yellow('\n🔑 GitHub token needed for MCP configuration'));
54
- console.log(chalk_1.default.gray('This is required for git and GitHub MCP servers to function properly.\n'));
122
+ console.log(chalk_1.default.yellow(`\n🔑 ${provider.displayName} token needed for MCP configuration`));
123
+ console.log(chalk_1.default.gray(`This is required for ${provider.displayName} MCP servers to function properly.\n`));
55
124
  }
56
- const tokenResponse = await (0, prompts_1.default)({
57
- type: 'password',
58
- name: 'token',
59
- message: 'Enter your GitHub token',
60
- validate: (value) => {
61
- if (!value && !isConversationalMode)
62
- return 'GitHub token is required';
63
- if (!value && isConversationalMode)
64
- return true; // Allow empty in conversational mode
65
- if (value.startsWith('ghp_') || value.startsWith('github_pat_'))
66
- return true;
67
- return 'Please enter a valid GitHub token (starts with ghp_ or github_pat_)';
68
- }
69
- });
70
- if (!tokenResponse.token) {
71
- if (isConversationalMode) {
72
- console.log(chalk_1.default.blue('ℹ️ No GitHub token provided - GitHub MCP server will not be configured'));
125
+ try {
126
+ return await (0, provider_prompts_1.promptForProviderToken)(client, providerId);
127
+ }
128
+ catch (error) {
129
+ if (isOptional) {
130
+ console.log(chalk_1.default.blue(`ℹ️ No ${provider.displayName} token provided - ${provider.displayName} MCP server will not be configured`));
73
131
  return '';
74
132
  }
75
- console.log(chalk_1.default.red('GitHub token is required. Exiting.'));
133
+ console.log(chalk_1.default.red(`${provider.displayName} token is required. Exiting.`));
76
134
  process.exit(1);
77
135
  }
78
- return tokenResponse.token;
79
136
  };
80
- const saveGitHubTokenToConfig = (githubToken) => {
137
+ const saveProviderTokenToConfig = async (providerId, token) => {
81
138
  const globalConfigPath = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'config.json');
82
139
  if (fs_1.default.existsSync(globalConfigPath)) {
83
140
  try {
84
141
  const config = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
85
- // Use new tokens structure if it exists, otherwise fall back to old format
86
- if (config.tokens) {
87
- config.tokens.github = githubToken;
88
- }
89
- else {
90
- config.githubToken = githubToken;
142
+ // Use new tokens structure
143
+ if (!config.tokens) {
144
+ config.tokens = {};
91
145
  }
146
+ config.tokens[providerId] = token;
92
147
  fs_1.default.writeFileSync(globalConfigPath, JSON.stringify(config, null, 2));
93
- console.log(chalk_1.default.green('✅ GitHub token saved to global config'));
148
+ const { getProvider } = await Promise.resolve().then(() => __importStar(require('../providers/provider-registry')));
149
+ const provider = await getProvider(providerId);
150
+ console.log(chalk_1.default.green(`✅ ${provider?.displayName || providerId} token saved to global config`));
94
151
  }
95
152
  catch (e) {
96
- console.log(chalk_1.default.yellow('⚠️ Could not save GitHub token to config'));
153
+ const { getProvider } = await Promise.resolve().then(() => __importStar(require('../providers/provider-registry')));
154
+ const provider = await getProvider(providerId);
155
+ console.log(chalk_1.default.yellow(`⚠️ Could not save ${provider?.displayName || providerId} token to config`));
97
156
  }
98
157
  }
99
158
  };
100
- exports.saveGitHubTokenToConfig = saveGitHubTokenToConfig;
101
- const configureIDEMCP = async (ide, fraimKey, tokens, jiraConfig) => {
159
+ exports.saveProviderTokenToConfig = saveProviderTokenToConfig;
160
+ const configureIDEMCP = async (ide, fraimKey, tokens, providerConfigs) => {
102
161
  const configPath = (0, ide_detector_1.expandPath)(ide.configPath);
103
162
  console.log(chalk_1.default.blue(`🔧 Configuring ${ide.name}...`));
104
163
  // Create backup if config exists
@@ -127,14 +186,18 @@ const configureIDEMCP = async (ide, fraimKey, tokens, jiraConfig) => {
127
186
  }
128
187
  }
129
188
  if (ide.configFormat === 'toml') {
130
- // Handle TOML format (Codex)
189
+ // Handle TOML format (e.g., Codex, Zed)
131
190
  let existingTomlContent = '';
132
191
  if (fs_1.default.existsSync(configPath)) {
133
192
  existingTomlContent = fs_1.default.readFileSync(configPath, 'utf8');
134
193
  console.log(chalk_1.default.gray(` 📋 Found existing TOML config`));
135
194
  }
136
- const newTomlContent = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokens, jiraConfig);
137
- const serversToAdd = ['fraim', 'git', 'github', 'gitlab', 'jira', 'playwright'];
195
+ const newTomlContent = await (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokens, providerConfigs);
196
+ const { getAllMCPServerIds } = await Promise.resolve().then(() => __importStar(require('../mcp/mcp-server-registry')));
197
+ const baseServerIds = getAllMCPServerIds();
198
+ // Add provider server IDs from tokens
199
+ const providerServerIds = Object.keys(tokens).filter(id => tokens[id]);
200
+ const serversToAdd = [...baseServerIds, ...providerServerIds];
138
201
  const mergeResult = (0, mcp_config_generator_1.mergeTomlMCPServers)(existingTomlContent, newTomlContent, serversToAdd);
139
202
  fs_1.default.writeFileSync(configPath, mergeResult.content);
140
203
  mergeResult.addedServers.forEach(server => {
@@ -149,7 +212,7 @@ const configureIDEMCP = async (ide, fraimKey, tokens, jiraConfig) => {
149
212
  }
150
213
  else {
151
214
  // For JSON configs - intelligent merging
152
- const newConfig = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokens, jiraConfig);
215
+ const newConfig = await (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokens, providerConfigs);
153
216
  const newMCPServers = newConfig[serversKey] || newConfig.mcpServers || {};
154
217
  // Merge MCP servers intelligently
155
218
  const mergedMCPServers = { ...existingMCPServers };
@@ -180,10 +243,11 @@ const configureIDEMCP = async (ide, fraimKey, tokens, jiraConfig) => {
180
243
  });
181
244
  }
182
245
  console.log(chalk_1.default.green(`✅ Updated ${configPath}`));
246
+ // Handle IDE-specific local config (e.g., Codex needs project-level config)
183
247
  if (ide.configType === 'codex') {
184
248
  const localResult = (0, codex_local_config_1.ensureCodexLocalConfig)(process.cwd());
185
249
  const status = localResult.created ? 'Created' : localResult.updated ? 'Updated' : 'Verified';
186
- console.log(chalk_1.default.green(` ✅ ${status} local Codex config: ${localResult.path}`));
250
+ console.log(chalk_1.default.green(` ✅ ${status} local ${ide.name} config: ${localResult.path}`));
187
251
  }
188
252
  };
189
253
  const listSupportedIDEs = () => {
@@ -210,18 +274,18 @@ const promptForIDESelection = async (availableIDEs, tokens) => {
210
274
  console.log(chalk_1.default.white(` ${index + 1}. ${ide.name} ${statusIcon} (${statusText})`));
211
275
  });
212
276
  console.log(chalk_1.default.blue('\nFRAIM will add these MCP servers:'));
213
- console.log(chalk_1.default.gray(' • fraim (workflows and AI management)'));
214
- console.log(chalk_1.default.gray(' • git (version control integration)'));
215
- if (tokens?.github) {
216
- console.log(chalk_1.default.gray(' - github (GitHub API access)'));
217
- }
218
- if (tokens?.gitlab) {
219
- console.log(chalk_1.default.gray(' - gitlab (GitLab API access)'));
220
- }
221
- if (tokens?.jira) {
222
- console.log(chalk_1.default.gray(' - jira (Jira issue tracking)'));
277
+ // Show base servers
278
+ for (const server of mcp_server_registry_1.BASE_MCP_SERVERS) {
279
+ console.log(chalk_1.default.gray(` • ${server.id} (${server.description})`));
280
+ }
281
+ // Show provider servers (only if tokens exist)
282
+ const allProviders = await (0, provider_registry_1.getAllProviders)();
283
+ for (const provider of allProviders) {
284
+ const hasToken = tokens?.[provider.id];
285
+ if (hasToken && provider.mcpServer) {
286
+ console.log(chalk_1.default.gray(` - ${provider.id} (${provider.description})`));
287
+ }
223
288
  }
224
- console.log(chalk_1.default.gray(' - playwright (browser automation)'));
225
289
  const response = await (0, prompts_1.default)({
226
290
  type: 'text',
227
291
  name: 'selection',
@@ -253,29 +317,32 @@ const runAddIDE = async (options) => {
253
317
  }
254
318
  console.log(chalk_1.default.blue('🔧 FRAIM IDE Configuration\n'));
255
319
  // Load existing configuration
256
- const globalConfig = loadGlobalConfig();
320
+ const globalConfig = await loadGlobalConfig();
257
321
  if (!globalConfig || !globalConfig.fraimKey) {
258
322
  console.log(chalk_1.default.red('❌ No FRAIM configuration found.'));
259
- console.log(chalk_1.default.yellow('💡 Please run "fraim setup" first to configure your FRAIM and GitHub keys.'));
323
+ console.log(chalk_1.default.yellow('💡 Please run "fraim setup" first to configure your FRAIM keys.'));
260
324
  process.exit(1);
261
325
  }
262
- let githubToken = globalConfig.githubToken;
263
- const platformTokens = {
264
- github: globalConfig.githubToken,
265
- gitlab: globalConfig.gitlabToken,
266
- jira: globalConfig.jiraToken
267
- };
326
+ const platformTokens = globalConfig.tokens || {};
268
327
  const isConversationalMode = globalConfig.mode === 'conversational';
269
- if (!githubToken && !platformTokens.gitlab && !platformTokens.jira && !isConversationalMode) {
270
- console.log(chalk_1.default.yellow('⚠️ No GitHub token found in configuration.'));
271
- githubToken = await promptForGitHubToken(false);
272
- if (githubToken) {
273
- saveGitHubTokenToConfig(githubToken);
274
- platformTokens.github = githubToken;
328
+ // Check if any provider tokens exist
329
+ const allProviderIds = await (0, provider_registry_1.getAllProviderIds)();
330
+ const hasAnyToken = allProviderIds.some(id => platformTokens[id]);
331
+ if (!hasAnyToken && !isConversationalMode) {
332
+ console.log(chalk_1.default.yellow('⚠️ No provider tokens found in configuration.'));
333
+ // Prompt for first integrated provider as default
334
+ const integratedProviders = await (0, provider_registry_1.getProvidersWithCapability)('integrated');
335
+ const defaultProviderId = integratedProviders[0]?.id;
336
+ if (defaultProviderId) {
337
+ const token = await promptForProviderTokenIfNeeded(defaultProviderId, false);
338
+ if (token) {
339
+ await saveProviderTokenToConfig(defaultProviderId, token);
340
+ platformTokens[defaultProviderId] = token;
341
+ }
275
342
  }
276
343
  }
277
- if (isConversationalMode && !githubToken) {
278
- console.log(chalk_1.default.blue('ℹ️ Conversational mode: Configuring MCP without GitHub integration\n'));
344
+ if (isConversationalMode && !hasAnyToken) {
345
+ console.log(chalk_1.default.blue('ℹ️ Conversational mode: Configuring MCP without platform integration\n'));
279
346
  }
280
347
  else {
281
348
  console.log(chalk_1.default.green('✅ Using existing FRAIM configuration\n'));
@@ -324,7 +391,7 @@ const runAddIDE = async (options) => {
324
391
  };
325
392
  for (const ide of idesToConfigure) {
326
393
  try {
327
- await configureIDEMCP(ide, globalConfig.fraimKey, platformTokens, globalConfig.jiraConfig);
394
+ await configureIDEMCP(ide, globalConfig.fraimKey, platformTokens, globalConfig.providerConfigs);
328
395
  results.successful.push(ide.name);
329
396
  }
330
397
  catch (error) {