fraim-framework 2.0.77 ā 2.0.80
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/README.md +77 -1
- package/bin/fraim-mcp.js +1 -1
- package/bin/fraim.js +1 -1
- package/dist/src/cli/commands/add-ide.js +36 -20
- package/dist/src/cli/commands/init-project.js +17 -6
- package/dist/src/cli/commands/setup.js +407 -21
- package/dist/src/cli/commands/test-mcp.js +2 -2
- package/dist/src/cli/setup/auto-mcp-setup.js +27 -10
- package/dist/src/cli/setup/codex-local-config.js +3 -3
- package/dist/src/cli/setup/first-run.js +2 -1
- package/dist/src/cli/setup/ide-detector.js +1 -1
- package/dist/src/cli/setup/mcp-config-generator.js +206 -49
- package/dist/src/cli/setup/token-validator.js +8 -0
- package/dist/src/cli/utils/platform-detection.js +45 -0
- package/dist/src/core/config-loader.js +1 -1
- package/dist/src/core/utils/provider-utils.js +5 -1
- package/dist/src/local-mcp-server/stdio-server.js +84 -15
- package/index.js +1 -1
- package/package.json +116 -116
- package/dist/src/cli/commands/init.js +0 -148
- package/dist/src/cli/commands/mcp.js +0 -65
- package/dist/src/cli/commands/wizard.js +0 -35
|
@@ -9,12 +9,12 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
9
9
|
const prompts_1 = __importDefault(require("prompts"));
|
|
10
10
|
const fs_1 = __importDefault(require("fs"));
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
|
-
const os_1 = __importDefault(require("os"));
|
|
13
12
|
const token_validator_1 = require("../setup/token-validator");
|
|
14
13
|
const auto_mcp_setup_1 = require("../setup/auto-mcp-setup");
|
|
15
14
|
const version_utils_1 = require("../utils/version-utils");
|
|
16
15
|
const platform_detection_1 = require("../utils/platform-detection");
|
|
17
16
|
const init_project_1 = require("./init-project");
|
|
17
|
+
const script_sync_utils_1 = require("../utils/script-sync-utils");
|
|
18
18
|
const promptForFraimKey = async () => {
|
|
19
19
|
console.log(chalk_1.default.blue('š FRAIM Key Setup'));
|
|
20
20
|
console.log('FRAIM requires a valid API key to access workflows and features.\n');
|
|
@@ -78,7 +78,12 @@ const promptForMode = async () => {
|
|
|
78
78
|
{
|
|
79
79
|
title: 'Integrated Mode',
|
|
80
80
|
value: 'integrated',
|
|
81
|
-
description: '
|
|
81
|
+
description: 'Single platform for both code hosting and issue tracking'
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
title: 'Split Mode',
|
|
85
|
+
value: 'split',
|
|
86
|
+
description: 'Separate platforms for code hosting and issue tracking (e.g., GitHub + Jira)'
|
|
82
87
|
},
|
|
83
88
|
{
|
|
84
89
|
title: 'Conversational Mode',
|
|
@@ -97,6 +102,11 @@ const promptForMode = async () => {
|
|
|
97
102
|
console.log(chalk_1.default.gray(' You can use FRAIM workflows without platform credentials.'));
|
|
98
103
|
console.log(chalk_1.default.gray(' Platform features (issues, PRs) will be unavailable.\n'));
|
|
99
104
|
}
|
|
105
|
+
else if (response.mode === 'split') {
|
|
106
|
+
console.log(chalk_1.default.blue('\nā Split mode selected'));
|
|
107
|
+
console.log(chalk_1.default.gray(' You can use different platforms for code hosting and issue tracking.'));
|
|
108
|
+
console.log(chalk_1.default.gray(' For example: GitHub for code + Jira for issues.\n'));
|
|
109
|
+
}
|
|
100
110
|
else {
|
|
101
111
|
console.log(chalk_1.default.blue('\nā Integrated mode selected'));
|
|
102
112
|
console.log(chalk_1.default.gray(' Full platform integration will be available.\n'));
|
|
@@ -112,26 +122,77 @@ const promptForPlatforms = async () => {
|
|
|
112
122
|
message: 'Select platforms (Space to select, Enter to confirm)',
|
|
113
123
|
choices: [
|
|
114
124
|
{ title: 'GitHub', value: 'github', selected: true },
|
|
115
|
-
{ title: 'Azure DevOps', value: 'ado', selected: false }
|
|
125
|
+
{ title: 'Azure DevOps', value: 'ado', selected: false },
|
|
126
|
+
{ title: 'GitLab', value: 'gitlab', selected: false },
|
|
127
|
+
{ title: 'Jira', value: 'jira', selected: false }
|
|
116
128
|
],
|
|
117
129
|
min: 1
|
|
118
130
|
});
|
|
119
131
|
if (!response.platforms || response.platforms.length === 0) {
|
|
120
132
|
console.log(chalk_1.default.yellow('\nā¹ļø No platforms selected, defaulting to GitHub'));
|
|
121
|
-
return { github: true, ado: false };
|
|
133
|
+
return { github: true, ado: false, gitlab: false, jira: false };
|
|
122
134
|
}
|
|
123
135
|
const platforms = {
|
|
124
136
|
github: response.platforms.includes('github'),
|
|
125
|
-
ado: response.platforms.includes('ado')
|
|
137
|
+
ado: response.platforms.includes('ado'),
|
|
138
|
+
gitlab: response.platforms.includes('gitlab'),
|
|
139
|
+
jira: response.platforms.includes('jira')
|
|
126
140
|
};
|
|
127
141
|
console.log(chalk_1.default.blue('\nā Selected platforms:'));
|
|
128
142
|
if (platforms.github)
|
|
129
|
-
console.log(chalk_1.default.gray('
|
|
143
|
+
console.log(chalk_1.default.gray(' - GitHub'));
|
|
130
144
|
if (platforms.ado)
|
|
131
|
-
console.log(chalk_1.default.gray('
|
|
145
|
+
console.log(chalk_1.default.gray(' - Azure DevOps'));
|
|
146
|
+
if (platforms.gitlab)
|
|
147
|
+
console.log(chalk_1.default.gray(' - GitLab'));
|
|
148
|
+
if (platforms.jira)
|
|
149
|
+
console.log(chalk_1.default.gray(' - Jira'));
|
|
132
150
|
console.log();
|
|
133
151
|
return platforms;
|
|
134
152
|
};
|
|
153
|
+
const promptForCodeRepository = async () => {
|
|
154
|
+
console.log(chalk_1.default.blue('\nš¦ Code Repository Platform'));
|
|
155
|
+
console.log(chalk_1.default.gray('Select the platform for code hosting:\n'));
|
|
156
|
+
const response = await (0, prompts_1.default)({
|
|
157
|
+
type: 'select',
|
|
158
|
+
name: 'platform',
|
|
159
|
+
message: 'Select code repository platform',
|
|
160
|
+
choices: [
|
|
161
|
+
{ title: 'GitHub', value: 'github' },
|
|
162
|
+
{ title: 'Azure DevOps', value: 'ado' },
|
|
163
|
+
{ title: 'GitLab', value: 'gitlab' }
|
|
164
|
+
],
|
|
165
|
+
initial: 0
|
|
166
|
+
});
|
|
167
|
+
if (!response.platform) {
|
|
168
|
+
console.log(chalk_1.default.yellow('\nā¹ļø No platform selected, defaulting to GitHub'));
|
|
169
|
+
return 'github';
|
|
170
|
+
}
|
|
171
|
+
console.log(chalk_1.default.blue(`\nā Code repository: ${response.platform === 'github' ? 'GitHub' : response.platform === 'ado' ? 'Azure DevOps' : 'GitLab'}\n`));
|
|
172
|
+
return response.platform;
|
|
173
|
+
};
|
|
174
|
+
const promptForIssueTracking = async () => {
|
|
175
|
+
console.log(chalk_1.default.blue('\nš« Issue Tracking Platform'));
|
|
176
|
+
console.log(chalk_1.default.gray('Select the platform for issue tracking:\n'));
|
|
177
|
+
const response = await (0, prompts_1.default)({
|
|
178
|
+
type: 'select',
|
|
179
|
+
name: 'platform',
|
|
180
|
+
message: 'Select issue tracking platform',
|
|
181
|
+
choices: [
|
|
182
|
+
{ title: 'GitHub', value: 'github' },
|
|
183
|
+
{ title: 'Azure DevOps', value: 'ado' },
|
|
184
|
+
{ title: 'GitLab', value: 'gitlab' },
|
|
185
|
+
{ title: 'Jira', value: 'jira' }
|
|
186
|
+
],
|
|
187
|
+
initial: 0
|
|
188
|
+
});
|
|
189
|
+
if (!response.platform) {
|
|
190
|
+
console.log(chalk_1.default.yellow('\nā¹ļø No platform selected, defaulting to GitHub'));
|
|
191
|
+
return 'github';
|
|
192
|
+
}
|
|
193
|
+
console.log(chalk_1.default.blue(`\nā Issue tracking: ${response.platform === 'github' ? 'GitHub' : response.platform === 'ado' ? 'Azure DevOps' : response.platform === 'gitlab' ? 'GitLab' : 'Jira'}\n`));
|
|
194
|
+
return response.platform;
|
|
195
|
+
};
|
|
135
196
|
const promptForADOToken = async () => {
|
|
136
197
|
console.log(chalk_1.default.blue('\nš§ Azure DevOps Integration Setup'));
|
|
137
198
|
console.log('FRAIM requires an Azure DevOps Personal Access Token for ADO integration.\n');
|
|
@@ -197,8 +258,92 @@ const promptForADOToken = async () => {
|
|
|
197
258
|
}
|
|
198
259
|
throw new Error('Failed to get Azure DevOps token');
|
|
199
260
|
};
|
|
200
|
-
const
|
|
201
|
-
|
|
261
|
+
const promptForGitLabToken = async () => {
|
|
262
|
+
console.log(chalk_1.default.blue('\nGitLab Integration Setup'));
|
|
263
|
+
console.log('FRAIM requires a GitLab token for GitLab issue/repository MCP integration.\n');
|
|
264
|
+
const tokenResponse = await (0, prompts_1.default)({
|
|
265
|
+
type: 'password',
|
|
266
|
+
name: 'token',
|
|
267
|
+
message: 'Enter your GitLab token',
|
|
268
|
+
validate: (value) => {
|
|
269
|
+
if (!value)
|
|
270
|
+
return 'GitLab token is required';
|
|
271
|
+
if (!(0, token_validator_1.isValidTokenFormat)(value, 'gitlab'))
|
|
272
|
+
return 'Please enter a valid GitLab token (typically starts with glpat-)';
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
if (!tokenResponse.token) {
|
|
277
|
+
throw new Error('GitLab token is required');
|
|
278
|
+
}
|
|
279
|
+
console.log(chalk_1.default.green('GitLab token received\n'));
|
|
280
|
+
return tokenResponse.token;
|
|
281
|
+
};
|
|
282
|
+
const promptForJiraCredentials = async () => {
|
|
283
|
+
console.log(chalk_1.default.blue('\nJira Integration Setup'));
|
|
284
|
+
console.log('FRAIM requires Jira credentials for MCP integration.\n');
|
|
285
|
+
// Prompt for base URL
|
|
286
|
+
const urlResponse = await (0, prompts_1.default)({
|
|
287
|
+
type: 'text',
|
|
288
|
+
name: 'baseUrl',
|
|
289
|
+
message: 'Enter your Jira base URL (e.g., https://company.atlassian.net)',
|
|
290
|
+
validate: (value) => {
|
|
291
|
+
if (!value)
|
|
292
|
+
return 'Jira base URL is required';
|
|
293
|
+
// Basic URL validation
|
|
294
|
+
try {
|
|
295
|
+
new URL(value.startsWith('http') ? value : `https://${value}`);
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
return 'Please enter a valid URL';
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
if (!urlResponse.baseUrl) {
|
|
304
|
+
throw new Error('Jira base URL is required');
|
|
305
|
+
}
|
|
306
|
+
// Prompt for email
|
|
307
|
+
const emailResponse = await (0, prompts_1.default)({
|
|
308
|
+
type: 'text',
|
|
309
|
+
name: 'email',
|
|
310
|
+
message: 'Enter your Jira account email',
|
|
311
|
+
validate: (value) => {
|
|
312
|
+
if (!value)
|
|
313
|
+
return 'Jira email is required';
|
|
314
|
+
if (!value.includes('@'))
|
|
315
|
+
return 'Please enter a valid email address';
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
if (!emailResponse.email) {
|
|
320
|
+
throw new Error('Jira email is required');
|
|
321
|
+
}
|
|
322
|
+
// Prompt for API token
|
|
323
|
+
const tokenResponse = await (0, prompts_1.default)({
|
|
324
|
+
type: 'password',
|
|
325
|
+
name: 'token',
|
|
326
|
+
message: 'Enter your Jira API token',
|
|
327
|
+
validate: (value) => {
|
|
328
|
+
if (!value)
|
|
329
|
+
return 'Jira API token is required';
|
|
330
|
+
if (!(0, token_validator_1.isValidTokenFormat)(value, 'jira'))
|
|
331
|
+
return 'Please enter a valid Jira token';
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
if (!tokenResponse.token) {
|
|
336
|
+
throw new Error('Jira API token is required');
|
|
337
|
+
}
|
|
338
|
+
console.log(chalk_1.default.green('Jira credentials received\n'));
|
|
339
|
+
return {
|
|
340
|
+
baseUrl: urlResponse.baseUrl,
|
|
341
|
+
email: emailResponse.email,
|
|
342
|
+
token: tokenResponse.token
|
|
343
|
+
};
|
|
344
|
+
};
|
|
345
|
+
const saveGlobalConfig = (fraimKey, mode, tokens, jiraConfig) => {
|
|
346
|
+
const globalConfigDir = (0, script_sync_utils_1.getUserFraimDir)();
|
|
202
347
|
const globalConfigPath = path_1.default.join(globalConfigDir, 'config.json');
|
|
203
348
|
if (!fs_1.default.existsSync(globalConfigDir)) {
|
|
204
349
|
fs_1.default.mkdirSync(globalConfigDir, { recursive: true });
|
|
@@ -222,6 +367,10 @@ const saveGlobalConfig = (fraimKey, mode, tokens) => {
|
|
|
222
367
|
...(existingConfig.tokens || {}),
|
|
223
368
|
...tokens
|
|
224
369
|
},
|
|
370
|
+
jiraConfig: jiraConfig && (jiraConfig.baseUrl || jiraConfig.email) ? {
|
|
371
|
+
...(existingConfig.jiraConfig || {}),
|
|
372
|
+
...jiraConfig
|
|
373
|
+
} : existingConfig.jiraConfig,
|
|
225
374
|
configuredAt: new Date().toISOString(),
|
|
226
375
|
userPreferences: {
|
|
227
376
|
autoSync: true,
|
|
@@ -234,11 +383,12 @@ const saveGlobalConfig = (fraimKey, mode, tokens) => {
|
|
|
234
383
|
const runSetup = async (options) => {
|
|
235
384
|
console.log(chalk_1.default.blue('š Welcome to FRAIM! Let\'s get you set up.\n'));
|
|
236
385
|
// Determine if this is an update (adding platforms to existing setup)
|
|
237
|
-
const globalConfigPath = path_1.default.join(
|
|
238
|
-
const isUpdate = fs_1.default.existsSync(globalConfigPath) && (options.github || options.ado);
|
|
386
|
+
const globalConfigPath = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'config.json');
|
|
387
|
+
const isUpdate = fs_1.default.existsSync(globalConfigPath) && (options.github || options.ado || options.gitlab || options.jira);
|
|
239
388
|
let fraimKey;
|
|
240
389
|
let mode;
|
|
241
390
|
const tokens = {};
|
|
391
|
+
let jiraConfig = {};
|
|
242
392
|
if (isUpdate) {
|
|
243
393
|
// Update existing setup - add platforms
|
|
244
394
|
console.log(chalk_1.default.blue('š Updating existing FRAIM configuration...\n'));
|
|
@@ -252,9 +402,13 @@ const runSetup = async (options) => {
|
|
|
252
402
|
tokens.github = existingConfig.tokens.github;
|
|
253
403
|
if (existingConfig.tokens.ado)
|
|
254
404
|
tokens.ado = existingConfig.tokens.ado;
|
|
405
|
+
if (existingConfig.tokens.gitlab)
|
|
406
|
+
tokens.gitlab = existingConfig.tokens.gitlab;
|
|
407
|
+
if (existingConfig.tokens.jira)
|
|
408
|
+
tokens.jira = existingConfig.tokens.jira;
|
|
255
409
|
}
|
|
256
410
|
console.log(chalk_1.default.gray(` Current mode: ${mode}`));
|
|
257
|
-
console.log(chalk_1.default.gray(` Existing tokens: ${tokens.github ? 'GitHub
|
|
411
|
+
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
412
|
}
|
|
259
413
|
catch (e) {
|
|
260
414
|
console.log(chalk_1.default.red('ā Failed to read existing configuration'));
|
|
@@ -288,11 +442,15 @@ const runSetup = async (options) => {
|
|
|
288
442
|
if (mode === 'integrated') {
|
|
289
443
|
let needGitHub = options.github || false;
|
|
290
444
|
let needADO = options.ado || false;
|
|
445
|
+
let needGitLab = options.gitlab || false;
|
|
446
|
+
let needJira = options.jira || false;
|
|
291
447
|
// If no specific platform flags and not an update, ask user
|
|
292
|
-
if (!isUpdate && !needGitHub && !needADO) {
|
|
448
|
+
if (!isUpdate && !needGitHub && !needADO && !needGitLab && !needJira) {
|
|
293
449
|
const platforms = await promptForPlatforms();
|
|
294
450
|
needGitHub = platforms.github;
|
|
295
451
|
needADO = platforms.ado;
|
|
452
|
+
needGitLab = platforms.gitlab;
|
|
453
|
+
needJira = platforms.jira;
|
|
296
454
|
}
|
|
297
455
|
// Get GitHub token if needed
|
|
298
456
|
if (needGitHub && !tokens.github) {
|
|
@@ -336,31 +494,249 @@ const runSetup = async (options) => {
|
|
|
336
494
|
}
|
|
337
495
|
}
|
|
338
496
|
}
|
|
339
|
-
|
|
497
|
+
// Get GitLab token if needed
|
|
498
|
+
if (needGitLab && !tokens.gitlab) {
|
|
499
|
+
if (options.gitlabToken) {
|
|
500
|
+
if (!(0, token_validator_1.isValidTokenFormat)(options.gitlabToken, 'gitlab')) {
|
|
501
|
+
console.log(chalk_1.default.red('Invalid GitLab token format'));
|
|
502
|
+
process.exit(1);
|
|
503
|
+
}
|
|
504
|
+
tokens.gitlab = options.gitlabToken;
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
try {
|
|
508
|
+
tokens.gitlab = await promptForGitLabToken();
|
|
509
|
+
}
|
|
510
|
+
catch (e) {
|
|
511
|
+
console.log(chalk_1.default.red('Failed to get GitLab token'));
|
|
512
|
+
process.exit(1);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
// Get Jira credentials if needed
|
|
517
|
+
if (needJira && !tokens.jira) {
|
|
518
|
+
if (options.jiraToken) {
|
|
519
|
+
if (!(0, token_validator_1.isValidTokenFormat)(options.jiraToken, 'jira')) {
|
|
520
|
+
console.log(chalk_1.default.red('Invalid Jira token format'));
|
|
521
|
+
process.exit(1);
|
|
522
|
+
}
|
|
523
|
+
tokens.jira = options.jiraToken;
|
|
524
|
+
// Use provided URL and email if available, otherwise prompt
|
|
525
|
+
if (options.jiraUrl && options.jiraEmail) {
|
|
526
|
+
jiraConfig.baseUrl = options.jiraUrl;
|
|
527
|
+
jiraConfig.email = options.jiraEmail;
|
|
528
|
+
console.log(chalk_1.default.green('ā
Jira credentials received\n'));
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
console.log(chalk_1.default.yellow('ā ļø Jira token provided but missing URL or email'));
|
|
532
|
+
const jiraCredentials = await promptForJiraCredentials();
|
|
533
|
+
jiraConfig.baseUrl = jiraCredentials.baseUrl;
|
|
534
|
+
jiraConfig.email = jiraCredentials.email;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
try {
|
|
539
|
+
const jiraCredentials = await promptForJiraCredentials();
|
|
540
|
+
tokens.jira = jiraCredentials.token;
|
|
541
|
+
jiraConfig.baseUrl = jiraCredentials.baseUrl;
|
|
542
|
+
jiraConfig.email = jiraCredentials.email;
|
|
543
|
+
}
|
|
544
|
+
catch (e) {
|
|
545
|
+
console.log(chalk_1.default.red('Failed to get Jira credentials'));
|
|
546
|
+
process.exit(1);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
if (!tokens.github && !tokens.ado && !tokens.gitlab && !tokens.jira) {
|
|
340
551
|
console.log(chalk_1.default.yellow('ā ļø No platform tokens configured.'));
|
|
341
552
|
console.log(chalk_1.default.gray(' You can add them later with:'));
|
|
342
553
|
console.log(chalk_1.default.cyan(' fraim setup --github'));
|
|
343
|
-
console.log(chalk_1.default.cyan(' fraim setup --ado
|
|
554
|
+
console.log(chalk_1.default.cyan(' fraim setup --ado'));
|
|
555
|
+
console.log(chalk_1.default.cyan(' fraim setup --gitlab'));
|
|
556
|
+
console.log(chalk_1.default.cyan(' fraim setup --jira\n'));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
else if (mode === 'split') {
|
|
560
|
+
// Split mode: separate platforms for code repository and issue tracking
|
|
561
|
+
console.log(chalk_1.default.blue('\nš Split Mode Configuration'));
|
|
562
|
+
console.log(chalk_1.default.gray('Configure separate platforms for code hosting and issue tracking.\n'));
|
|
563
|
+
// Get code repository platform
|
|
564
|
+
const codeRepoPlatform = await promptForCodeRepository();
|
|
565
|
+
// Get code repository token
|
|
566
|
+
if (codeRepoPlatform === 'github' && !tokens.github) {
|
|
567
|
+
if (options.githubToken) {
|
|
568
|
+
if (!(0, token_validator_1.isValidTokenFormat)(options.githubToken, 'github')) {
|
|
569
|
+
console.log(chalk_1.default.red('ā Invalid GitHub token format'));
|
|
570
|
+
process.exit(1);
|
|
571
|
+
}
|
|
572
|
+
console.log(chalk_1.default.blue('š Validating GitHub token...'));
|
|
573
|
+
const isValid = await (0, token_validator_1.validateGitHubToken)(options.githubToken);
|
|
574
|
+
if (!isValid) {
|
|
575
|
+
console.log(chalk_1.default.red('ā Invalid GitHub token'));
|
|
576
|
+
process.exit(1);
|
|
577
|
+
}
|
|
578
|
+
tokens.github = options.githubToken;
|
|
579
|
+
console.log(chalk_1.default.green('ā
GitHub token validated\n'));
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
try {
|
|
583
|
+
tokens.github = await (0, auto_mcp_setup_1.promptForGitHubToken)();
|
|
584
|
+
}
|
|
585
|
+
catch (e) {
|
|
586
|
+
process.exit(1);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
else if (codeRepoPlatform === 'ado' && !tokens.ado) {
|
|
591
|
+
if (options.adoToken) {
|
|
592
|
+
tokens.ado = options.adoToken;
|
|
593
|
+
console.log(chalk_1.default.green('ā
Azure DevOps token received\n'));
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
try {
|
|
597
|
+
tokens.ado = await promptForADOToken();
|
|
598
|
+
}
|
|
599
|
+
catch (e) {
|
|
600
|
+
console.log(chalk_1.default.red('ā Failed to get Azure DevOps token'));
|
|
601
|
+
process.exit(1);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
else if (codeRepoPlatform === 'gitlab' && !tokens.gitlab) {
|
|
606
|
+
if (options.gitlabToken) {
|
|
607
|
+
if (!(0, token_validator_1.isValidTokenFormat)(options.gitlabToken, 'gitlab')) {
|
|
608
|
+
console.log(chalk_1.default.red('Invalid GitLab token format'));
|
|
609
|
+
process.exit(1);
|
|
610
|
+
}
|
|
611
|
+
tokens.gitlab = options.gitlabToken;
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
try {
|
|
615
|
+
tokens.gitlab = await promptForGitLabToken();
|
|
616
|
+
}
|
|
617
|
+
catch (e) {
|
|
618
|
+
console.log(chalk_1.default.red('Failed to get GitLab token'));
|
|
619
|
+
process.exit(1);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
// Get issue tracking platform
|
|
624
|
+
const issueTrackingPlatform = await promptForIssueTracking();
|
|
625
|
+
// Get issue tracking token (if different from code repo)
|
|
626
|
+
if (issueTrackingPlatform === 'github' && !tokens.github) {
|
|
627
|
+
if (options.githubToken) {
|
|
628
|
+
if (!(0, token_validator_1.isValidTokenFormat)(options.githubToken, 'github')) {
|
|
629
|
+
console.log(chalk_1.default.red('ā Invalid GitHub token format'));
|
|
630
|
+
process.exit(1);
|
|
631
|
+
}
|
|
632
|
+
console.log(chalk_1.default.blue('š Validating GitHub token...'));
|
|
633
|
+
const isValid = await (0, token_validator_1.validateGitHubToken)(options.githubToken);
|
|
634
|
+
if (!isValid) {
|
|
635
|
+
console.log(chalk_1.default.red('ā Invalid GitHub token'));
|
|
636
|
+
process.exit(1);
|
|
637
|
+
}
|
|
638
|
+
tokens.github = options.githubToken;
|
|
639
|
+
console.log(chalk_1.default.green('ā
GitHub token validated\n'));
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
try {
|
|
643
|
+
tokens.github = await (0, auto_mcp_setup_1.promptForGitHubToken)();
|
|
644
|
+
}
|
|
645
|
+
catch (e) {
|
|
646
|
+
process.exit(1);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
else if (issueTrackingPlatform === 'ado' && !tokens.ado) {
|
|
651
|
+
if (options.adoToken) {
|
|
652
|
+
tokens.ado = options.adoToken;
|
|
653
|
+
console.log(chalk_1.default.green('ā
Azure DevOps token received\n'));
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
try {
|
|
657
|
+
tokens.ado = await promptForADOToken();
|
|
658
|
+
}
|
|
659
|
+
catch (e) {
|
|
660
|
+
console.log(chalk_1.default.red('ā Failed to get Azure DevOps token'));
|
|
661
|
+
process.exit(1);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
else if (issueTrackingPlatform === 'gitlab' && !tokens.gitlab) {
|
|
666
|
+
if (options.gitlabToken) {
|
|
667
|
+
if (!(0, token_validator_1.isValidTokenFormat)(options.gitlabToken, 'gitlab')) {
|
|
668
|
+
console.log(chalk_1.default.red('Invalid GitLab token format'));
|
|
669
|
+
process.exit(1);
|
|
670
|
+
}
|
|
671
|
+
tokens.gitlab = options.gitlabToken;
|
|
672
|
+
}
|
|
673
|
+
else {
|
|
674
|
+
try {
|
|
675
|
+
tokens.gitlab = await promptForGitLabToken();
|
|
676
|
+
}
|
|
677
|
+
catch (e) {
|
|
678
|
+
console.log(chalk_1.default.red('Failed to get GitLab token'));
|
|
679
|
+
process.exit(1);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
else if (issueTrackingPlatform === 'jira' && !tokens.jira) {
|
|
684
|
+
if (options.jiraToken) {
|
|
685
|
+
if (!(0, token_validator_1.isValidTokenFormat)(options.jiraToken, 'jira')) {
|
|
686
|
+
console.log(chalk_1.default.red('Invalid Jira token format'));
|
|
687
|
+
process.exit(1);
|
|
688
|
+
}
|
|
689
|
+
tokens.jira = options.jiraToken;
|
|
690
|
+
// Use provided URL and email if available, otherwise prompt
|
|
691
|
+
if (options.jiraUrl && options.jiraEmail) {
|
|
692
|
+
jiraConfig.baseUrl = options.jiraUrl;
|
|
693
|
+
jiraConfig.email = options.jiraEmail;
|
|
694
|
+
console.log(chalk_1.default.green('ā
Jira credentials received\n'));
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
console.log(chalk_1.default.yellow('ā ļø Jira token provided but missing URL or email'));
|
|
698
|
+
const jiraCredentials = await promptForJiraCredentials();
|
|
699
|
+
jiraConfig.baseUrl = jiraCredentials.baseUrl;
|
|
700
|
+
jiraConfig.email = jiraCredentials.email;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
try {
|
|
705
|
+
const jiraCredentials = await promptForJiraCredentials();
|
|
706
|
+
tokens.jira = jiraCredentials.token;
|
|
707
|
+
jiraConfig.baseUrl = jiraCredentials.baseUrl;
|
|
708
|
+
jiraConfig.email = jiraCredentials.email;
|
|
709
|
+
}
|
|
710
|
+
catch (e) {
|
|
711
|
+
console.log(chalk_1.default.red('Failed to get Jira credentials'));
|
|
712
|
+
process.exit(1);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
344
715
|
}
|
|
716
|
+
console.log(chalk_1.default.green(`\nā
Split mode configured: ${codeRepoPlatform} (code) + ${issueTrackingPlatform} (issues)\n`));
|
|
345
717
|
}
|
|
346
718
|
else {
|
|
347
719
|
console.log(chalk_1.default.gray('ā¹ļø Conversational mode: No platform tokens needed\n'));
|
|
348
720
|
}
|
|
349
721
|
// Save global configuration
|
|
350
722
|
console.log(chalk_1.default.blue('š¾ Saving global configuration...'));
|
|
351
|
-
saveGlobalConfig(fraimKey, mode, tokens);
|
|
723
|
+
saveGlobalConfig(fraimKey, mode, tokens, jiraConfig);
|
|
352
724
|
// Configure MCP servers (only on initial setup)
|
|
353
725
|
if (!isUpdate) {
|
|
354
726
|
console.log(chalk_1.default.blue('\nš Configuring MCP servers...'));
|
|
355
727
|
// For conversational mode, we still configure MCP but without GitHub token requirement
|
|
356
728
|
// The FRAIM MCP server works without GitHub token, other servers can be optional
|
|
357
|
-
const
|
|
358
|
-
|
|
729
|
+
const mcpTokens = {
|
|
730
|
+
github: tokens.github || '',
|
|
731
|
+
gitlab: tokens.gitlab || '',
|
|
732
|
+
jira: tokens.jira || ''
|
|
733
|
+
};
|
|
734
|
+
if (mode === 'conversational' && !mcpTokens.github && !mcpTokens.gitlab && !mcpTokens.jira) {
|
|
359
735
|
console.log(chalk_1.default.yellow('ā¹ļø Conversational mode: Configuring MCP servers without GitHub integration'));
|
|
360
736
|
console.log(chalk_1.default.gray(' FRAIM workflows will work, but GitHub-specific features will be unavailable\n'));
|
|
361
737
|
}
|
|
362
738
|
try {
|
|
363
|
-
await (0, auto_mcp_setup_1.autoConfigureMCP)(fraimKey,
|
|
739
|
+
await (0, auto_mcp_setup_1.autoConfigureMCP)(fraimKey, mcpTokens, options.ide ? [options.ide] : undefined, jiraConfig);
|
|
364
740
|
}
|
|
365
741
|
catch (e) {
|
|
366
742
|
console.log(chalk_1.default.yellow('ā ļø MCP configuration encountered issues'));
|
|
@@ -380,18 +756,22 @@ const runSetup = async (options) => {
|
|
|
380
756
|
console.log(chalk_1.default.green('\nšÆ Setup complete!'));
|
|
381
757
|
console.log(chalk_1.default.gray(` Mode: ${mode}`));
|
|
382
758
|
if (mode === 'integrated') {
|
|
383
|
-
console.log(chalk_1.default.gray(` Platforms: ${tokens.github ? 'GitHub
|
|
759
|
+
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
760
|
}
|
|
385
761
|
console.log(chalk_1.default.cyan('\nš For future projects:'));
|
|
386
762
|
console.log(chalk_1.default.cyan(' 1. cd into any project directory'));
|
|
387
763
|
console.log(chalk_1.default.cyan(' 2. Run: fraim init-project'));
|
|
388
764
|
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)) {
|
|
765
|
+
if (mode === 'integrated' && (!tokens.github || !tokens.ado || !tokens.gitlab || !tokens.jira)) {
|
|
390
766
|
console.log(chalk_1.default.gray('\nš” To add more platforms later:'));
|
|
391
767
|
if (!tokens.github)
|
|
392
768
|
console.log(chalk_1.default.gray(' fraim setup --github'));
|
|
393
769
|
if (!tokens.ado)
|
|
394
770
|
console.log(chalk_1.default.gray(' fraim setup --ado'));
|
|
771
|
+
if (!tokens.gitlab)
|
|
772
|
+
console.log(chalk_1.default.gray(' fraim setup --gitlab'));
|
|
773
|
+
if (!tokens.jira)
|
|
774
|
+
console.log(chalk_1.default.gray(' fraim setup --jira'));
|
|
395
775
|
}
|
|
396
776
|
};
|
|
397
777
|
exports.runSetup = runSetup;
|
|
@@ -400,8 +780,14 @@ exports.setupCommand = new commander_1.Command('setup')
|
|
|
400
780
|
.option('--key <key>', 'FRAIM API key')
|
|
401
781
|
.option('--github-token <token>', 'GitHub Personal Access Token')
|
|
402
782
|
.option('--ado-token <token>', 'Azure DevOps Personal Access Token')
|
|
783
|
+
.option('--gitlab-token <token>', 'GitLab Personal Access Token')
|
|
784
|
+
.option('--jira-token <token>', 'Jira API token')
|
|
785
|
+
.option('--jira-url <url>', 'Jira base URL (e.g., https://company.atlassian.net)')
|
|
786
|
+
.option('--jira-email <email>', 'Jira account email')
|
|
403
787
|
.option('--github', 'Add/update GitHub integration')
|
|
404
788
|
.option('--ado', 'Add/update Azure DevOps integration')
|
|
789
|
+
.option('--gitlab', 'Add/update GitLab integration')
|
|
790
|
+
.option('--jira', 'Add/update Jira integration')
|
|
405
791
|
.option('--all', 'Configure all detected IDEs (deprecated)')
|
|
406
792
|
.option('--ide <ides>', 'Configure specific IDEs (deprecated)')
|
|
407
793
|
.action(exports.runSetup);
|
|
@@ -8,8 +8,8 @@ const commander_1 = require("commander");
|
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
9
|
const fs_1 = __importDefault(require("fs"));
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
|
-
const os_1 = __importDefault(require("os"));
|
|
12
11
|
const ide_detector_1 = require("../setup/ide-detector");
|
|
12
|
+
const script_sync_utils_1 = require("../utils/script-sync-utils");
|
|
13
13
|
const testIDEConfig = async (ide) => {
|
|
14
14
|
const result = {
|
|
15
15
|
ide: ide.name,
|
|
@@ -57,7 +57,7 @@ const testIDEConfig = async (ide) => {
|
|
|
57
57
|
return result;
|
|
58
58
|
};
|
|
59
59
|
const checkGlobalSetup = () => {
|
|
60
|
-
const globalConfigPath = path_1.default.join(
|
|
60
|
+
const globalConfigPath = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'config.json');
|
|
61
61
|
return fs_1.default.existsSync(globalConfigPath);
|
|
62
62
|
};
|
|
63
63
|
const runTestMCP = async () => {
|