fraim-framework 2.0.76 → 2.0.78

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.
@@ -23,6 +23,8 @@ const loadGlobalConfig = () => {
23
23
  return {
24
24
  fraimKey: config.apiKey,
25
25
  githubToken: config.tokens?.github || config.githubToken, // Support both old and new format
26
+ gitlabToken: config.tokens?.gitlab,
27
+ jiraToken: config.tokens?.jira,
26
28
  mode: config.mode
27
29
  };
28
30
  }
@@ -95,7 +97,7 @@ const saveGitHubTokenToConfig = (githubToken) => {
95
97
  }
96
98
  };
97
99
  exports.saveGitHubTokenToConfig = saveGitHubTokenToConfig;
98
- const configureIDEMCP = async (ide, fraimKey, githubToken) => {
100
+ const configureIDEMCP = async (ide, fraimKey, tokens) => {
99
101
  const configPath = (0, ide_detector_1.expandPath)(ide.configPath);
100
102
  console.log(chalk_1.default.blue(`šŸ”§ Configuring ${ide.name}...`));
101
103
  // Create backup if config exists
@@ -129,8 +131,8 @@ const configureIDEMCP = async (ide, fraimKey, githubToken) => {
129
131
  existingTomlContent = fs_1.default.readFileSync(configPath, 'utf8');
130
132
  console.log(chalk_1.default.gray(` šŸ“‹ Found existing TOML config`));
131
133
  }
132
- const newTomlContent = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, githubToken);
133
- const serversToAdd = ['fraim', 'git', 'github', 'playwright'];
134
+ const newTomlContent = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokens);
135
+ const serversToAdd = ['fraim', 'git', 'github', 'gitlab', 'jira', 'playwright'];
134
136
  const mergeResult = (0, mcp_config_generator_1.mergeTomlMCPServers)(existingTomlContent, newTomlContent, serversToAdd);
135
137
  fs_1.default.writeFileSync(configPath, mergeResult.content);
136
138
  mergeResult.addedServers.forEach(server => {
@@ -145,7 +147,7 @@ const configureIDEMCP = async (ide, fraimKey, githubToken) => {
145
147
  }
146
148
  else {
147
149
  // Handle JSON format
148
- const newConfig = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, githubToken);
150
+ const newConfig = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokens);
149
151
  const newMCPServers = newConfig.mcpServers || {};
150
152
  // Merge MCP servers intelligently
151
153
  const mergedMCPServers = { ...existingMCPServers };
@@ -197,7 +199,7 @@ const listSupportedIDEs = () => {
197
199
  console.log(chalk_1.default.yellow('šŸ’” Use "fraim add-ide --ide <name>" to configure a specific IDE'));
198
200
  console.log(chalk_1.default.yellow(' Example: fraim add-ide --ide claude'));
199
201
  };
200
- const promptForIDESelection = async (availableIDEs, githubToken) => {
202
+ const promptForIDESelection = async (availableIDEs, tokens) => {
201
203
  console.log(chalk_1.default.green(`āœ… Found ${availableIDEs.length} IDEs that can be configured:\n`));
202
204
  availableIDEs.forEach((ide, index) => {
203
205
  const configExists = fs_1.default.existsSync((0, ide_detector_1.expandPath)(ide.configPath));
@@ -208,10 +210,16 @@ const promptForIDESelection = async (availableIDEs, githubToken) => {
208
210
  console.log(chalk_1.default.blue('\nFRAIM will add these MCP servers:'));
209
211
  console.log(chalk_1.default.gray(' • fraim (workflows and AI management)'));
210
212
  console.log(chalk_1.default.gray(' • git (version control integration)'));
211
- if (githubToken) {
212
- console.log(chalk_1.default.gray(' • github (GitHub API access)'));
213
+ if (tokens?.github) {
214
+ console.log(chalk_1.default.gray(' - github (GitHub API access)'));
213
215
  }
214
- console.log(chalk_1.default.gray(' • playwright (browser automation)'));
216
+ if (tokens?.gitlab) {
217
+ console.log(chalk_1.default.gray(' - gitlab (GitLab API access)'));
218
+ }
219
+ if (tokens?.jira) {
220
+ console.log(chalk_1.default.gray(' - jira (Jira issue tracking)'));
221
+ }
222
+ console.log(chalk_1.default.gray(' - playwright (browser automation)'));
215
223
  const response = await (0, prompts_1.default)({
216
224
  type: 'text',
217
225
  name: 'selection',
@@ -250,12 +258,18 @@ const runAddIDE = async (options) => {
250
258
  process.exit(1);
251
259
  }
252
260
  let githubToken = globalConfig.githubToken;
261
+ const platformTokens = {
262
+ github: globalConfig.githubToken,
263
+ gitlab: globalConfig.gitlabToken,
264
+ jira: globalConfig.jiraToken
265
+ };
253
266
  const isConversationalMode = globalConfig.mode === 'conversational';
254
- if (!githubToken && !isConversationalMode) {
267
+ if (!githubToken && !platformTokens.gitlab && !platformTokens.jira && !isConversationalMode) {
255
268
  console.log(chalk_1.default.yellow('āš ļø No GitHub token found in configuration.'));
256
269
  githubToken = await promptForGitHubToken(false);
257
270
  if (githubToken) {
258
271
  saveGitHubTokenToConfig(githubToken);
272
+ platformTokens.github = githubToken;
259
273
  }
260
274
  }
261
275
  if (isConversationalMode && !githubToken) {
@@ -295,7 +309,7 @@ const runAddIDE = async (options) => {
295
309
  }
296
310
  else {
297
311
  // Interactive selection
298
- idesToConfigure = await promptForIDESelection(detectedIDEs, githubToken);
312
+ idesToConfigure = await promptForIDESelection(detectedIDEs, platformTokens);
299
313
  }
300
314
  if (idesToConfigure.length === 0) {
301
315
  console.log(chalk_1.default.yellow('āš ļø No IDEs selected for configuration.'));
@@ -308,7 +322,7 @@ const runAddIDE = async (options) => {
308
322
  };
309
323
  for (const ide of idesToConfigure) {
310
324
  try {
311
- await configureIDEMCP(ide, globalConfig.fraimKey, githubToken || '');
325
+ await configureIDEMCP(ide, globalConfig.fraimKey, platformTokens);
312
326
  results.successful.push(ide.name);
313
327
  }
314
328
  catch (error) {
@@ -24,42 +24,38 @@ const checkGlobalSetup = () => {
24
24
  const config = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
25
25
  return {
26
26
  exists: true,
27
- mode: config.mode || 'integrated', // Default to integrated for backward compatibility
27
+ mode: config.mode || 'integrated',
28
28
  tokens: config.tokens || {}
29
29
  };
30
30
  }
31
- catch (e) {
31
+ catch {
32
32
  return { exists: true, mode: 'integrated', tokens: {} };
33
33
  }
34
34
  };
35
35
  const runInitProject = async () => {
36
- console.log(chalk_1.default.blue('šŸš€ Initializing FRAIM project...'));
37
- // Check if global setup exists
36
+ console.log(chalk_1.default.blue('Initializing FRAIM project...'));
38
37
  const globalSetup = checkGlobalSetup();
39
38
  if (!globalSetup.exists) {
40
- console.log(chalk_1.default.red('āŒ Global FRAIM setup not found.'));
39
+ console.log(chalk_1.default.red('Global FRAIM setup not found.'));
41
40
  console.log(chalk_1.default.yellow('Please run global setup first:'));
42
41
  console.log(chalk_1.default.cyan(' fraim setup'));
43
42
  process.exit(1);
44
43
  }
45
- const mode = globalSetup.mode;
46
- const tokens = globalSetup.tokens || {};
47
- console.log(chalk_1.default.gray(` Mode: ${mode === 'conversational' ? 'Conversational' : 'Integrated'} (from global config)`));
48
44
  const projectRoot = process.cwd();
49
45
  const fraimDir = path_1.default.join(projectRoot, '.fraim');
50
46
  const configPath = path_1.default.join(fraimDir, 'config.json');
51
47
  if (!fs_1.default.existsSync(fraimDir)) {
52
48
  fs_1.default.mkdirSync(fraimDir, { recursive: true });
53
- console.log(chalk_1.default.green('āœ… Created .fraim directory'));
49
+ console.log(chalk_1.default.green('Created .fraim directory'));
54
50
  }
55
51
  else {
56
- console.log(chalk_1.default.yellow('ā„¹ļø .fraim directory already exists'));
52
+ console.log(chalk_1.default.yellow('.fraim directory already exists'));
57
53
  }
58
54
  if (!fs_1.default.existsSync(configPath)) {
55
+ const projectName = path_1.default.basename(projectRoot);
56
+ const preferredMode = globalSetup.mode || 'integrated';
59
57
  let config;
60
- if (mode === 'conversational') {
61
- // Conversational mode - no platform integration
62
- const projectName = path_1.default.basename(projectRoot);
58
+ if (preferredMode === 'conversational') {
63
59
  config = {
64
60
  version: (0, version_utils_1.getFraimVersion)(),
65
61
  project: {
@@ -67,77 +63,83 @@ const runInitProject = async () => {
67
63
  },
68
64
  customizations: {}
69
65
  };
70
- console.log(chalk_1.default.blue(' Conversational mode: No platform integration'));
66
+ console.log(chalk_1.default.blue(' conversational mode: no platform integration'));
71
67
  console.log(chalk_1.default.gray(` Project: ${projectName}`));
72
68
  }
73
69
  else {
74
- // Integrated mode - try to detect platform
75
70
  const detection = (0, platform_detection_1.detectPlatformFromGit)();
76
71
  if (detection.provider !== 'unknown' && detection.repository) {
77
- // Check if we have the appropriate token
78
- const hasToken = detection.provider === 'github' ? tokens.github : tokens.ado;
79
- if (!hasToken) {
80
- console.log(chalk_1.default.yellow(`\nāš ļø Warning: ${detection.provider.toUpperCase()} repository detected but no token configured`));
81
- console.log(chalk_1.default.gray(` Platform features will be limited.`));
82
- console.log(chalk_1.default.cyan(` Run: fraim setup --${detection.provider === 'github' ? 'github' : 'ado'}\n`));
83
- }
84
- // Use detected repository info
72
+ const issueTracking = detection.provider === 'github'
73
+ ? {
74
+ provider: 'github',
75
+ owner: detection.repository.owner,
76
+ name: detection.repository.name
77
+ }
78
+ : detection.provider === 'ado'
79
+ ? {
80
+ provider: 'ado',
81
+ organization: detection.repository.organization,
82
+ project: detection.repository.project,
83
+ name: detection.repository.name
84
+ }
85
+ : {
86
+ provider: 'gitlab',
87
+ namespace: detection.repository.namespace,
88
+ name: detection.repository.name,
89
+ projectPath: detection.repository.projectPath
90
+ };
85
91
  config = {
86
92
  version: (0, version_utils_1.getFraimVersion)(),
87
93
  project: {
88
- name: detection.repository.name
94
+ name: detection.repository.name || projectName
89
95
  },
90
96
  repository: detection.repository,
97
+ issueTracking,
91
98
  customizations: {}
92
99
  };
93
100
  console.log(chalk_1.default.blue(` Platform: ${detection.provider.toUpperCase()}`));
94
101
  if (detection.provider === 'github') {
95
102
  console.log(chalk_1.default.gray(` Repository: ${detection.repository.owner}/${detection.repository.name}`));
96
- console.log(chalk_1.default.gray(` Token: ${hasToken ? 'āœ“ Configured' : 'āœ— Missing'}`));
97
103
  }
98
104
  else if (detection.provider === 'ado') {
99
105
  console.log(chalk_1.default.gray(` Organization: ${detection.repository.organization}`));
100
106
  console.log(chalk_1.default.gray(` Project: ${detection.repository.project}`));
101
107
  console.log(chalk_1.default.gray(` Repository: ${detection.repository.name}`));
102
- console.log(chalk_1.default.gray(` Token: ${hasToken ? 'āœ“ Configured' : 'āœ— Missing'}`));
108
+ }
109
+ else if (detection.provider === 'gitlab') {
110
+ console.log(chalk_1.default.gray(` Namespace: ${detection.repository.namespace || '(none)'}`));
111
+ console.log(chalk_1.default.gray(` Repository: ${detection.repository.name}`));
103
112
  }
104
113
  }
105
114
  else {
106
- // No git remote detected - warn user and fallback to conversational-style config
107
- console.log(chalk_1.default.yellow(' āš ļø No git remote detected'));
108
- console.log(chalk_1.default.yellow(' Integrated mode requires a git remote for platform features.'));
109
- console.log(chalk_1.default.gray(' Falling back to conversational mode configuration...'));
110
- const repoName = path_1.default.basename(projectRoot);
111
115
  config = {
112
116
  version: (0, version_utils_1.getFraimVersion)(),
113
117
  project: {
114
- name: repoName
118
+ name: projectName
115
119
  },
116
120
  customizations: {}
117
121
  };
118
- console.log(chalk_1.default.gray(' Please update .fraim/config.json if you add a repository later.'));
122
+ console.log(chalk_1.default.yellow(' No git remote detected. Falling back to conversational mode.'));
119
123
  }
120
124
  }
121
125
  fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
122
- console.log(chalk_1.default.green('āœ… Created .fraim/config.json'));
126
+ console.log(chalk_1.default.green('Created .fraim/config.json'));
123
127
  }
124
- // Create subdirectories
125
- ['workflows'].forEach(dir => {
128
+ ['workflows'].forEach((dir) => {
126
129
  const dirPath = path_1.default.join(fraimDir, dir);
127
130
  if (!fs_1.default.existsSync(dirPath)) {
128
131
  fs_1.default.mkdirSync(dirPath, { recursive: true });
129
- console.log(chalk_1.default.green(`āœ… Created .fraim/${dir}`));
132
+ console.log(chalk_1.default.green(`Created .fraim/${dir}`));
130
133
  }
131
134
  });
132
- // Sync workflows from registry
133
135
  await (0, sync_1.runSync)({});
134
136
  const codexAvailable = (0, ide_detector_1.detectInstalledIDEs)().some((ide) => ide.configType === 'codex');
135
137
  if (codexAvailable) {
136
138
  const codexLocalResult = (0, codex_local_config_1.ensureCodexLocalConfig)(projectRoot);
137
139
  const status = codexLocalResult.created ? 'Created' : codexLocalResult.updated ? 'Updated' : 'Verified';
138
- console.log(chalk_1.default.green(`āœ… ${status} project Codex config at ${codexLocalResult.path}`));
140
+ console.log(chalk_1.default.green(`${status} project Codex config at ${codexLocalResult.path}`));
139
141
  }
140
- console.log(chalk_1.default.green('\nāœ… FRAIM project initialized!'));
142
+ console.log(chalk_1.default.green('\nFRAIM project initialized!'));
141
143
  console.log(chalk_1.default.cyan('Try: Ask your AI agent "list fraim workflows"'));
142
144
  };
143
145
  exports.runInitProject = runInitProject;
@@ -112,23 +112,31 @@ const promptForPlatforms = async () => {
112
112
  message: 'Select platforms (Space to select, Enter to confirm)',
113
113
  choices: [
114
114
  { title: 'GitHub', value: 'github', selected: true },
115
- { title: 'Azure DevOps', value: 'ado', selected: false }
115
+ { title: 'Azure DevOps', value: 'ado', selected: false },
116
+ { title: 'GitLab', value: 'gitlab', selected: false },
117
+ { title: 'Jira', value: 'jira', selected: false }
116
118
  ],
117
119
  min: 1
118
120
  });
119
121
  if (!response.platforms || response.platforms.length === 0) {
120
122
  console.log(chalk_1.default.yellow('\nā„¹ļø No platforms selected, defaulting to GitHub'));
121
- return { github: true, ado: false };
123
+ return { github: true, ado: false, gitlab: false, jira: false };
122
124
  }
123
125
  const platforms = {
124
126
  github: response.platforms.includes('github'),
125
- ado: response.platforms.includes('ado')
127
+ ado: response.platforms.includes('ado'),
128
+ gitlab: response.platforms.includes('gitlab'),
129
+ jira: response.platforms.includes('jira')
126
130
  };
127
131
  console.log(chalk_1.default.blue('\nāœ“ Selected platforms:'));
128
132
  if (platforms.github)
129
- console.log(chalk_1.default.gray(' • GitHub'));
133
+ console.log(chalk_1.default.gray(' - GitHub'));
130
134
  if (platforms.ado)
131
- console.log(chalk_1.default.gray(' • Azure DevOps'));
135
+ console.log(chalk_1.default.gray(' - Azure DevOps'));
136
+ if (platforms.gitlab)
137
+ console.log(chalk_1.default.gray(' - GitLab'));
138
+ if (platforms.jira)
139
+ console.log(chalk_1.default.gray(' - Jira'));
132
140
  console.log();
133
141
  return platforms;
134
142
  };
@@ -197,6 +205,48 @@ const promptForADOToken = async () => {
197
205
  }
198
206
  throw new Error('Failed to get Azure DevOps token');
199
207
  };
208
+ const promptForGitLabToken = async () => {
209
+ console.log(chalk_1.default.blue('\nGitLab Integration Setup'));
210
+ console.log('FRAIM requires a GitLab token for GitLab issue/repository MCP integration.\n');
211
+ const tokenResponse = await (0, prompts_1.default)({
212
+ type: 'password',
213
+ name: 'token',
214
+ message: 'Enter your GitLab token',
215
+ validate: (value) => {
216
+ if (!value)
217
+ return 'GitLab token is required';
218
+ if (!(0, token_validator_1.isValidTokenFormat)(value, 'gitlab'))
219
+ return 'Please enter a valid GitLab token (typically starts with glpat-)';
220
+ return true;
221
+ }
222
+ });
223
+ if (!tokenResponse.token) {
224
+ throw new Error('GitLab token is required');
225
+ }
226
+ console.log(chalk_1.default.green('GitLab token received\n'));
227
+ return tokenResponse.token;
228
+ };
229
+ const promptForJiraToken = async () => {
230
+ console.log(chalk_1.default.blue('\nJira Integration Setup'));
231
+ console.log('FRAIM requires a Jira API token for Jira MCP integration.\n');
232
+ const tokenResponse = await (0, prompts_1.default)({
233
+ type: 'password',
234
+ name: 'token',
235
+ message: 'Enter your Jira API token',
236
+ validate: (value) => {
237
+ if (!value)
238
+ return 'Jira token is required';
239
+ if (!(0, token_validator_1.isValidTokenFormat)(value, 'jira'))
240
+ return 'Please enter a valid Jira token';
241
+ return true;
242
+ }
243
+ });
244
+ if (!tokenResponse.token) {
245
+ throw new Error('Jira token is required');
246
+ }
247
+ console.log(chalk_1.default.green('Jira token received\n'));
248
+ return tokenResponse.token;
249
+ };
200
250
  const saveGlobalConfig = (fraimKey, mode, tokens) => {
201
251
  const globalConfigDir = path_1.default.join(os_1.default.homedir(), '.fraim');
202
252
  const globalConfigPath = path_1.default.join(globalConfigDir, 'config.json');
@@ -235,7 +285,7 @@ const runSetup = async (options) => {
235
285
  console.log(chalk_1.default.blue('šŸš€ Welcome to FRAIM! Let\'s get you set up.\n'));
236
286
  // Determine if this is an update (adding platforms to existing setup)
237
287
  const globalConfigPath = path_1.default.join(os_1.default.homedir(), '.fraim', 'config.json');
238
- const isUpdate = fs_1.default.existsSync(globalConfigPath) && (options.github || options.ado);
288
+ const isUpdate = fs_1.default.existsSync(globalConfigPath) && (options.github || options.ado || options.gitlab || options.jira);
239
289
  let fraimKey;
240
290
  let mode;
241
291
  const tokens = {};
@@ -252,9 +302,13 @@ const runSetup = async (options) => {
252
302
  tokens.github = existingConfig.tokens.github;
253
303
  if (existingConfig.tokens.ado)
254
304
  tokens.ado = existingConfig.tokens.ado;
305
+ if (existingConfig.tokens.gitlab)
306
+ tokens.gitlab = existingConfig.tokens.gitlab;
307
+ if (existingConfig.tokens.jira)
308
+ tokens.jira = existingConfig.tokens.jira;
255
309
  }
256
310
  console.log(chalk_1.default.gray(` Current mode: ${mode}`));
257
- console.log(chalk_1.default.gray(` Existing tokens: ${tokens.github ? 'GitHub āœ“' : ''} ${tokens.ado ? 'ADO āœ“' : ''}\n`));
311
+ console.log(chalk_1.default.gray(` Existing tokens: ${tokens.github ? 'GitHub yes' : ''} ${tokens.ado ? 'ADO yes' : ''} ${tokens.gitlab ? 'GitLab yes' : ''} ${tokens.jira ? 'Jira yes' : ''}\n`));
258
312
  }
259
313
  catch (e) {
260
314
  console.log(chalk_1.default.red('āŒ Failed to read existing configuration'));
@@ -288,11 +342,15 @@ const runSetup = async (options) => {
288
342
  if (mode === 'integrated') {
289
343
  let needGitHub = options.github || false;
290
344
  let needADO = options.ado || false;
345
+ let needGitLab = options.gitlab || false;
346
+ let needJira = options.jira || false;
291
347
  // If no specific platform flags and not an update, ask user
292
- if (!isUpdate && !needGitHub && !needADO) {
348
+ if (!isUpdate && !needGitHub && !needADO && !needGitLab && !needJira) {
293
349
  const platforms = await promptForPlatforms();
294
350
  needGitHub = platforms.github;
295
351
  needADO = platforms.ado;
352
+ needGitLab = platforms.gitlab;
353
+ needJira = platforms.jira;
296
354
  }
297
355
  // Get GitHub token if needed
298
356
  if (needGitHub && !tokens.github) {
@@ -336,11 +394,51 @@ const runSetup = async (options) => {
336
394
  }
337
395
  }
338
396
  }
339
- if (!tokens.github && !tokens.ado) {
397
+ // Get GitLab token if needed
398
+ if (needGitLab && !tokens.gitlab) {
399
+ if (options.gitlabToken) {
400
+ if (!(0, token_validator_1.isValidTokenFormat)(options.gitlabToken, 'gitlab')) {
401
+ console.log(chalk_1.default.red('Invalid GitLab token format'));
402
+ process.exit(1);
403
+ }
404
+ tokens.gitlab = options.gitlabToken;
405
+ }
406
+ else {
407
+ try {
408
+ tokens.gitlab = await promptForGitLabToken();
409
+ }
410
+ catch (e) {
411
+ console.log(chalk_1.default.red('Failed to get GitLab token'));
412
+ process.exit(1);
413
+ }
414
+ }
415
+ }
416
+ // Get Jira token if needed
417
+ if (needJira && !tokens.jira) {
418
+ if (options.jiraToken) {
419
+ if (!(0, token_validator_1.isValidTokenFormat)(options.jiraToken, 'jira')) {
420
+ console.log(chalk_1.default.red('Invalid Jira token format'));
421
+ process.exit(1);
422
+ }
423
+ tokens.jira = options.jiraToken;
424
+ }
425
+ else {
426
+ try {
427
+ tokens.jira = await promptForJiraToken();
428
+ }
429
+ catch (e) {
430
+ console.log(chalk_1.default.red('Failed to get Jira token'));
431
+ process.exit(1);
432
+ }
433
+ }
434
+ }
435
+ if (!tokens.github && !tokens.ado && !tokens.gitlab && !tokens.jira) {
340
436
  console.log(chalk_1.default.yellow('āš ļø No platform tokens configured.'));
341
437
  console.log(chalk_1.default.gray(' You can add them later with:'));
342
438
  console.log(chalk_1.default.cyan(' fraim setup --github'));
343
- console.log(chalk_1.default.cyan(' fraim setup --ado\n'));
439
+ console.log(chalk_1.default.cyan(' fraim setup --ado'));
440
+ console.log(chalk_1.default.cyan(' fraim setup --gitlab'));
441
+ console.log(chalk_1.default.cyan(' fraim setup --jira\n'));
344
442
  }
345
443
  }
346
444
  else {
@@ -354,13 +452,17 @@ const runSetup = async (options) => {
354
452
  console.log(chalk_1.default.blue('\nšŸ”Œ Configuring MCP servers...'));
355
453
  // For conversational mode, we still configure MCP but without GitHub token requirement
356
454
  // The FRAIM MCP server works without GitHub token, other servers can be optional
357
- const mcpGitHubToken = tokens.github || '';
358
- if (mode === 'conversational' && !mcpGitHubToken) {
455
+ const mcpTokens = {
456
+ github: tokens.github || '',
457
+ gitlab: tokens.gitlab || '',
458
+ jira: tokens.jira || ''
459
+ };
460
+ if (mode === 'conversational' && !mcpTokens.github && !mcpTokens.gitlab && !mcpTokens.jira) {
359
461
  console.log(chalk_1.default.yellow('ā„¹ļø Conversational mode: Configuring MCP servers without GitHub integration'));
360
462
  console.log(chalk_1.default.gray(' FRAIM workflows will work, but GitHub-specific features will be unavailable\n'));
361
463
  }
362
464
  try {
363
- await (0, auto_mcp_setup_1.autoConfigureMCP)(fraimKey, mcpGitHubToken, options.ide ? [options.ide] : undefined);
465
+ await (0, auto_mcp_setup_1.autoConfigureMCP)(fraimKey, mcpTokens, options.ide ? [options.ide] : undefined);
364
466
  }
365
467
  catch (e) {
366
468
  console.log(chalk_1.default.yellow('āš ļø MCP configuration encountered issues'));
@@ -380,18 +482,22 @@ const runSetup = async (options) => {
380
482
  console.log(chalk_1.default.green('\nšŸŽÆ Setup complete!'));
381
483
  console.log(chalk_1.default.gray(` Mode: ${mode}`));
382
484
  if (mode === 'integrated') {
383
- console.log(chalk_1.default.gray(` Platforms: ${tokens.github ? 'GitHub āœ“' : 'GitHub āœ—'} ${tokens.ado ? 'ADO āœ“' : 'ADO āœ—'}`));
485
+ console.log(chalk_1.default.gray(` Platforms: ${tokens.github ? 'GitHub yes' : 'GitHub no'} ${tokens.ado ? 'ADO yes' : 'ADO no'} ${tokens.gitlab ? 'GitLab yes' : 'GitLab no'} ${tokens.jira ? 'Jira yes' : 'Jira no'}`));
384
486
  }
385
487
  console.log(chalk_1.default.cyan('\nšŸ“ For future projects:'));
386
488
  console.log(chalk_1.default.cyan(' 1. cd into any project directory'));
387
489
  console.log(chalk_1.default.cyan(' 2. Run: fraim init-project'));
388
490
  console.log(chalk_1.default.cyan(' 3. Ask your AI agent (Claude Desktop / Cowork, Cursor, etc.) "list fraim workflows"'));
389
- if (mode === 'integrated' && (!tokens.github || !tokens.ado)) {
491
+ if (mode === 'integrated' && (!tokens.github || !tokens.ado || !tokens.gitlab || !tokens.jira)) {
390
492
  console.log(chalk_1.default.gray('\nšŸ’” To add more platforms later:'));
391
493
  if (!tokens.github)
392
494
  console.log(chalk_1.default.gray(' fraim setup --github'));
393
495
  if (!tokens.ado)
394
496
  console.log(chalk_1.default.gray(' fraim setup --ado'));
497
+ if (!tokens.gitlab)
498
+ console.log(chalk_1.default.gray(' fraim setup --gitlab'));
499
+ if (!tokens.jira)
500
+ console.log(chalk_1.default.gray(' fraim setup --jira'));
395
501
  }
396
502
  };
397
503
  exports.runSetup = runSetup;
@@ -400,8 +506,12 @@ exports.setupCommand = new commander_1.Command('setup')
400
506
  .option('--key <key>', 'FRAIM API key')
401
507
  .option('--github-token <token>', 'GitHub Personal Access Token')
402
508
  .option('--ado-token <token>', 'Azure DevOps Personal Access Token')
509
+ .option('--gitlab-token <token>', 'GitLab Personal Access Token')
510
+ .option('--jira-token <token>', 'Jira API token')
403
511
  .option('--github', 'Add/update GitHub integration')
404
512
  .option('--ado', 'Add/update Azure DevOps integration')
513
+ .option('--gitlab', 'Add/update GitLab integration')
514
+ .option('--jira', 'Add/update Jira integration')
405
515
  .option('--all', 'Configure all detected IDEs (deprecated)')
406
516
  .option('--ide <ides>', 'Configure specific IDEs (deprecated)')
407
517
  .action(exports.runSetup);
@@ -12,7 +12,16 @@ const ide_detector_1 = require("./ide-detector");
12
12
  const mcp_config_generator_1 = require("./mcp-config-generator");
13
13
  const token_validator_1 = require("./token-validator");
14
14
  const codex_local_config_1 = require("./codex-local-config");
15
- const promptForIDESelection = async (detectedIDEs, githubToken) => {
15
+ const normalizePlatformTokens = (tokenInput) => {
16
+ if (!tokenInput)
17
+ return {};
18
+ if (typeof tokenInput === 'string') {
19
+ return tokenInput ? { github: tokenInput } : {};
20
+ }
21
+ return tokenInput;
22
+ };
23
+ const promptForIDESelection = async (detectedIDEs, tokenInput) => {
24
+ const tokens = normalizePlatformTokens(tokenInput);
16
25
  console.log(chalk_1.default.green(`āœ… Found ${detectedIDEs.length} IDEs that can be configured with FRAIM:\n`));
17
26
  detectedIDEs.forEach((ide, index) => {
18
27
  const configExists = fs_1.default.existsSync((0, ide_detector_1.expandPath)(ide.configPath));
@@ -24,8 +33,14 @@ const promptForIDESelection = async (detectedIDEs, githubToken) => {
24
33
  console.log(chalk_1.default.blue('FRAIM will add these MCP servers to selected IDEs:'));
25
34
  console.log(chalk_1.default.gray(' • fraim (required for FRAIM workflows)'));
26
35
  console.log(chalk_1.default.gray(' • git (version control integration)'));
27
- if (githubToken) {
28
- console.log(chalk_1.default.gray(' • github (GitHub API access)'));
36
+ if (tokens.github) {
37
+ console.log(chalk_1.default.gray(' - github (GitHub API access)'));
38
+ }
39
+ if (tokens.gitlab) {
40
+ console.log(chalk_1.default.gray(' - gitlab (GitLab API access)'));
41
+ }
42
+ if (tokens.jira) {
43
+ console.log(chalk_1.default.gray(' - jira (Jira issue tracking)'));
29
44
  }
30
45
  console.log(chalk_1.default.gray(' • playwright (browser automation)'));
31
46
  console.log(chalk_1.default.yellow('\nšŸ’” Existing MCP servers will be preserved - only missing servers will be added.'));
@@ -197,7 +212,8 @@ const validateSetupResults = async (configuredIDEs) => {
197
212
  }
198
213
  };
199
214
  exports.validateSetupResults = validateSetupResults;
200
- const configureIDEMCP = async (ide, fraimKey, githubToken) => {
215
+ const configureIDEMCP = async (ide, fraimKey, tokenInput) => {
216
+ const tokens = normalizePlatformTokens(tokenInput);
201
217
  const configPath = (0, ide_detector_1.expandPath)(ide.configPath);
202
218
  console.log(chalk_1.default.blue(`šŸ”§ Configuring ${ide.name}...`));
203
219
  // Create backup
@@ -228,8 +244,8 @@ const configureIDEMCP = async (ide, fraimKey, githubToken) => {
228
244
  existingTomlContent = fs_1.default.readFileSync(configPath, 'utf8');
229
245
  console.log(chalk_1.default.gray(` šŸ“‹ Found existing TOML config`));
230
246
  }
231
- const newTomlContent = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, githubToken);
232
- const serversToAdd = ['fraim', 'git', 'github', 'playwright'];
247
+ const newTomlContent = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokens);
248
+ const serversToAdd = ['fraim', 'git', 'github', 'gitlab', 'jira', 'playwright'];
233
249
  const mergeResult = (0, mcp_config_generator_1.mergeTomlMCPServers)(existingTomlContent, newTomlContent, serversToAdd);
234
250
  fs_1.default.writeFileSync(configPath, mergeResult.content);
235
251
  mergeResult.addedServers.forEach(server => {
@@ -244,7 +260,7 @@ const configureIDEMCP = async (ide, fraimKey, githubToken) => {
244
260
  }
245
261
  else {
246
262
  // For JSON configs - intelligent merging
247
- const newConfig = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, githubToken);
263
+ const newConfig = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokens);
248
264
  const newMCPServers = newConfig[serversKey] || newConfig.mcpServers || {};
249
265
  // Merge MCP servers intelligently
250
266
  const mergedMCPServers = { ...existingMCPServers };
@@ -280,7 +296,8 @@ const configureIDEMCP = async (ide, fraimKey, githubToken) => {
280
296
  console.log(chalk_1.default.green(` āœ… ${status} local Codex config: ${localResult.path}`));
281
297
  }
282
298
  };
283
- const autoConfigureMCP = async (fraimKey, githubToken, selectedIDEs) => {
299
+ const autoConfigureMCP = async (fraimKey, tokenInput, selectedIDEs) => {
300
+ const tokens = normalizePlatformTokens(tokenInput);
284
301
  const detectedIDEs = (0, ide_detector_1.detectInstalledIDEs)();
285
302
  if (detectedIDEs.length === 0) {
286
303
  console.log(chalk_1.default.yellow('āš ļø No supported IDEs detected.'));
@@ -312,7 +329,7 @@ const autoConfigureMCP = async (fraimKey, githubToken, selectedIDEs) => {
312
329
  }
313
330
  else {
314
331
  // Interactive selection
315
- idesToConfigure = await (0, exports.promptForIDESelection)(detectedIDEs, githubToken);
332
+ idesToConfigure = await (0, exports.promptForIDESelection)(detectedIDEs, tokens);
316
333
  }
317
334
  if (idesToConfigure.length === 0) {
318
335
  console.log(chalk_1.default.yellow('āš ļø No IDEs selected for configuration.'));
@@ -326,7 +343,7 @@ const autoConfigureMCP = async (fraimKey, githubToken, selectedIDEs) => {
326
343
  };
327
344
  for (const ide of idesToConfigure) {
328
345
  try {
329
- await configureIDEMCP(ide, fraimKey, githubToken);
346
+ await configureIDEMCP(ide, fraimKey, tokens);
330
347
  results.successful.push(ide.name);
331
348
  }
332
349
  catch (error) {