fraim-framework 2.0.82 → 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.
- package/dist/src/cli/api/get-provider-client.js +41 -0
- package/dist/src/cli/api/provider-client.js +107 -0
- package/dist/src/cli/commands/add-ide.js +144 -77
- package/dist/src/cli/commands/add-provider.js +61 -221
- package/dist/src/cli/commands/doctor.js +4 -1
- package/dist/src/cli/commands/init-project.js +24 -35
- package/dist/src/cli/commands/setup.js +247 -563
- package/dist/src/cli/commands/test-mcp.js +35 -1
- package/dist/src/cli/doctor/check-runner.js +6 -3
- package/dist/src/cli/doctor/checks/global-setup-checks.js +5 -6
- package/dist/src/cli/doctor/checks/ide-config-checks.js +44 -23
- package/dist/src/cli/doctor/checks/mcp-connectivity-checks.js +324 -146
- package/dist/src/cli/doctor/checks/scripts-checks.js +2 -2
- package/dist/src/cli/fraim.js +42 -1
- package/dist/src/cli/mcp/ide-formats.js +243 -0
- package/dist/src/cli/mcp/mcp-server-builder.js +48 -0
- package/dist/src/cli/mcp/mcp-server-registry.js +159 -0
- package/dist/src/cli/mcp/types.js +3 -0
- package/dist/src/cli/providers/local-provider-registry.js +145 -0
- package/dist/src/cli/providers/provider-registry.js +230 -0
- package/dist/src/cli/setup/auto-mcp-setup.js +56 -118
- package/dist/src/cli/setup/mcp-config-generator.js +64 -321
- package/dist/src/cli/setup/provider-prompts.js +300 -0
- package/package.json +2 -1
- package/dist/src/cli/commands/install.js +0 -86
- package/dist/src/cli/setup/token-validator.js +0 -57
|
@@ -1,18 +1,54 @@
|
|
|
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.setupCommand = exports.runSetup = void 0;
|
|
39
|
+
exports.setupCommandInitialization = exports.setupCommand = exports.runSetup = void 0;
|
|
40
|
+
// Refactored setup.ts - Generic provider system with zero hardcoded provider knowledge
|
|
7
41
|
const commander_1 = require("commander");
|
|
8
42
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
43
|
const prompts_1 = __importDefault(require("prompts"));
|
|
10
44
|
const fs_1 = __importDefault(require("fs"));
|
|
11
45
|
const path_1 = __importDefault(require("path"));
|
|
12
|
-
const token_validator_1 = require("../setup/token-validator");
|
|
13
46
|
const auto_mcp_setup_1 = require("../setup/auto-mcp-setup");
|
|
14
47
|
const version_utils_1 = require("../utils/version-utils");
|
|
15
48
|
const platform_detection_1 = require("../utils/platform-detection");
|
|
49
|
+
const get_provider_client_1 = require("../api/get-provider-client");
|
|
50
|
+
const provider_prompts_1 = require("../setup/provider-prompts");
|
|
51
|
+
const provider_registry_1 = require("../providers/provider-registry");
|
|
16
52
|
const init_project_1 = require("./init-project");
|
|
17
53
|
const script_sync_utils_1 = require("../utils/script-sync-utils");
|
|
18
54
|
const promptForFraimKey = async () => {
|
|
@@ -31,9 +67,7 @@ const promptForFraimKey = async () => {
|
|
|
31
67
|
validate: (value) => {
|
|
32
68
|
if (!value)
|
|
33
69
|
return 'FRAIM key is required';
|
|
34
|
-
|
|
35
|
-
return true;
|
|
36
|
-
return 'Please enter a valid FRAIM key (starts with fraim_)';
|
|
70
|
+
return true;
|
|
37
71
|
}
|
|
38
72
|
});
|
|
39
73
|
if (!keyResponse.key) {
|
|
@@ -52,16 +86,8 @@ const promptForFraimKey = async () => {
|
|
|
52
86
|
attempts++;
|
|
53
87
|
continue;
|
|
54
88
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (isValid) {
|
|
58
|
-
console.log(chalk_1.default.green('✅ FRAIM key validated\n'));
|
|
59
|
-
return keyResponse.key;
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
console.log(chalk_1.default.red('❌ Invalid FRAIM key\n'));
|
|
63
|
-
attempts++;
|
|
64
|
-
}
|
|
89
|
+
console.log(chalk_1.default.green('✅ FRAIM key accepted\n'));
|
|
90
|
+
return keyResponse.key;
|
|
65
91
|
}
|
|
66
92
|
console.log(chalk_1.default.red('\n❌ Maximum attempts reached. Setup cancelled.'));
|
|
67
93
|
console.log(chalk_1.default.gray('Please ensure you have a valid FRAIM key and try again.'));
|
|
@@ -83,7 +109,7 @@ const promptForMode = async () => {
|
|
|
83
109
|
{
|
|
84
110
|
title: 'Split Mode',
|
|
85
111
|
value: 'split',
|
|
86
|
-
description: 'Separate platforms for code hosting and issue tracking
|
|
112
|
+
description: 'Separate platforms for code hosting and issue tracking'
|
|
87
113
|
},
|
|
88
114
|
{
|
|
89
115
|
title: 'Conversational Mode',
|
|
@@ -104,8 +130,7 @@ const promptForMode = async () => {
|
|
|
104
130
|
}
|
|
105
131
|
else if (response.mode === 'split') {
|
|
106
132
|
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'));
|
|
133
|
+
console.log(chalk_1.default.gray(' You can use different platforms for code hosting and issue tracking.\n'));
|
|
109
134
|
}
|
|
110
135
|
else {
|
|
111
136
|
console.log(chalk_1.default.blue('\n✓ Integrated mode selected'));
|
|
@@ -113,242 +138,13 @@ const promptForMode = async () => {
|
|
|
113
138
|
}
|
|
114
139
|
return response.mode;
|
|
115
140
|
};
|
|
116
|
-
const
|
|
117
|
-
console.log(chalk_1.default.blue('\n🔧 Platform Selection'));
|
|
118
|
-
console.log(chalk_1.default.gray('Which platforms do you want to integrate with?\n'));
|
|
119
|
-
const response = await (0, prompts_1.default)({
|
|
120
|
-
type: 'multiselect',
|
|
121
|
-
name: 'platforms',
|
|
122
|
-
message: 'Select platforms (Space to select, Enter to confirm)',
|
|
123
|
-
choices: [
|
|
124
|
-
{ title: 'GitHub', value: 'github', selected: true },
|
|
125
|
-
{ title: 'Azure DevOps', value: 'ado', selected: false },
|
|
126
|
-
{ title: 'GitLab', value: 'gitlab', selected: false },
|
|
127
|
-
{ title: 'Jira', value: 'jira', selected: false }
|
|
128
|
-
],
|
|
129
|
-
min: 1
|
|
130
|
-
});
|
|
131
|
-
if (!response.platforms || response.platforms.length === 0) {
|
|
132
|
-
console.log(chalk_1.default.yellow('\nℹ️ No platforms selected, defaulting to GitHub'));
|
|
133
|
-
return { github: true, ado: false, gitlab: false, jira: false };
|
|
134
|
-
}
|
|
135
|
-
const platforms = {
|
|
136
|
-
github: response.platforms.includes('github'),
|
|
137
|
-
ado: response.platforms.includes('ado'),
|
|
138
|
-
gitlab: response.platforms.includes('gitlab'),
|
|
139
|
-
jira: response.platforms.includes('jira')
|
|
140
|
-
};
|
|
141
|
-
console.log(chalk_1.default.blue('\n✓ Selected platforms:'));
|
|
142
|
-
if (platforms.github)
|
|
143
|
-
console.log(chalk_1.default.gray(' - GitHub'));
|
|
144
|
-
if (platforms.ado)
|
|
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'));
|
|
150
|
-
console.log();
|
|
151
|
-
return platforms;
|
|
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
|
-
};
|
|
196
|
-
const promptForADOToken = async () => {
|
|
197
|
-
console.log(chalk_1.default.blue('\n🔧 Azure DevOps Integration Setup'));
|
|
198
|
-
console.log('FRAIM requires an Azure DevOps Personal Access Token for ADO integration.\n');
|
|
199
|
-
console.log(chalk_1.default.yellow('🔑 Why ADO token is required:'));
|
|
200
|
-
console.log(chalk_1.default.gray(' • Create and manage work items'));
|
|
201
|
-
console.log(chalk_1.default.gray(' • Access repository information'));
|
|
202
|
-
console.log(chalk_1.default.gray(' • Perform git operations'));
|
|
203
|
-
console.log(chalk_1.default.gray(' • Enable full development workflow automation\n'));
|
|
204
|
-
const hasToken = await (0, prompts_1.default)({
|
|
205
|
-
type: 'confirm',
|
|
206
|
-
name: 'hasToken',
|
|
207
|
-
message: 'Do you have an Azure DevOps Personal Access Token?',
|
|
208
|
-
initial: false
|
|
209
|
-
});
|
|
210
|
-
if (!hasToken.hasToken) {
|
|
211
|
-
console.log(chalk_1.default.yellow('\n📝 To create an Azure DevOps Personal Access Token:'));
|
|
212
|
-
console.log(chalk_1.default.gray(' 1. Go to https://dev.azure.com/{your-org}/_usersSettings/tokens'));
|
|
213
|
-
console.log(chalk_1.default.gray(' 2. Click "New Token"'));
|
|
214
|
-
console.log(chalk_1.default.gray(' 3. Select these scopes:'));
|
|
215
|
-
console.log(chalk_1.default.gray(' • Code (Read & Write)'));
|
|
216
|
-
console.log(chalk_1.default.gray(' • Work Items (Read & Write)'));
|
|
217
|
-
console.log(chalk_1.default.gray(' • Project and Team (Read)'));
|
|
218
|
-
console.log(chalk_1.default.gray(' 4. Set expiration (recommend 1 year)'));
|
|
219
|
-
console.log(chalk_1.default.gray(' 5. Click "Create" and copy the token immediately'));
|
|
220
|
-
console.log(chalk_1.default.yellow(' ⚠️ You won\'t be able to see the token again!\n'));
|
|
221
|
-
}
|
|
222
|
-
let token = null;
|
|
223
|
-
let attempts = 0;
|
|
224
|
-
const maxAttempts = 3;
|
|
225
|
-
while (!token && attempts < maxAttempts) {
|
|
226
|
-
const tokenResponse = await (0, prompts_1.default)({
|
|
227
|
-
type: 'password',
|
|
228
|
-
name: 'token',
|
|
229
|
-
message: attempts === 0
|
|
230
|
-
? 'Enter your Azure DevOps PAT'
|
|
231
|
-
: `Enter your Azure DevOps PAT (attempt ${attempts + 1}/${maxAttempts})`,
|
|
232
|
-
validate: (value) => {
|
|
233
|
-
if (!value)
|
|
234
|
-
return 'ADO token is required';
|
|
235
|
-
if (value.length < 20)
|
|
236
|
-
return 'Token seems too short';
|
|
237
|
-
return true;
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
if (!tokenResponse.token) {
|
|
241
|
-
console.log(chalk_1.default.red('\n❌ ADO token is required for Azure DevOps integration.'));
|
|
242
|
-
attempts++;
|
|
243
|
-
if (attempts < maxAttempts) {
|
|
244
|
-
const retry = await (0, prompts_1.default)({
|
|
245
|
-
type: 'confirm',
|
|
246
|
-
name: 'retry',
|
|
247
|
-
message: 'Would you like to try entering the token again?',
|
|
248
|
-
initial: true
|
|
249
|
-
});
|
|
250
|
-
if (!retry.retry)
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
token = tokenResponse.token;
|
|
256
|
-
console.log(chalk_1.default.green('✅ Azure DevOps token received\n'));
|
|
257
|
-
return token; // Token is definitely set here
|
|
258
|
-
}
|
|
259
|
-
throw new Error('Failed to get Azure DevOps token');
|
|
260
|
-
};
|
|
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) => {
|
|
141
|
+
const saveGlobalConfig = (fraimKey, mode, tokens, configs) => {
|
|
346
142
|
const globalConfigDir = (0, script_sync_utils_1.getUserFraimDir)();
|
|
347
143
|
const globalConfigPath = path_1.default.join(globalConfigDir, 'config.json');
|
|
348
144
|
if (!fs_1.default.existsSync(globalConfigDir)) {
|
|
349
145
|
fs_1.default.mkdirSync(globalConfigDir, { recursive: true });
|
|
350
146
|
}
|
|
351
|
-
// Read existing config to preserve any existing
|
|
147
|
+
// Read existing config to preserve any existing data
|
|
352
148
|
let existingConfig = {};
|
|
353
149
|
if (fs_1.default.existsSync(globalConfigPath)) {
|
|
354
150
|
try {
|
|
@@ -358,19 +154,23 @@ const saveGlobalConfig = (fraimKey, mode, tokens, jiraConfig) => {
|
|
|
358
154
|
// Ignore parse errors, will create new config
|
|
359
155
|
}
|
|
360
156
|
}
|
|
157
|
+
// Merge provider configs (e.g., jiraConfig)
|
|
158
|
+
const providerConfigs = { ...(existingConfig.providerConfigs || {}) };
|
|
159
|
+
Object.entries(configs).forEach(([providerId, config]) => {
|
|
160
|
+
providerConfigs[`${providerId}Config`] = {
|
|
161
|
+
...(providerConfigs[`${providerId}Config`] || {}),
|
|
162
|
+
...config
|
|
163
|
+
};
|
|
164
|
+
});
|
|
361
165
|
const config = {
|
|
362
166
|
version: (0, version_utils_1.getFraimVersion)(),
|
|
363
167
|
apiKey: fraimKey,
|
|
364
168
|
mode: mode,
|
|
365
169
|
tokens: {
|
|
366
|
-
// Preserve existing tokens and merge with new ones
|
|
367
170
|
...(existingConfig.tokens || {}),
|
|
368
171
|
...tokens
|
|
369
172
|
},
|
|
370
|
-
|
|
371
|
-
...(existingConfig.jiraConfig || {}),
|
|
372
|
-
...jiraConfig
|
|
373
|
-
} : existingConfig.jiraConfig,
|
|
173
|
+
providerConfigs,
|
|
374
174
|
configuredAt: new Date().toISOString(),
|
|
375
175
|
userPreferences: {
|
|
376
176
|
autoSync: true,
|
|
@@ -380,38 +180,102 @@ const saveGlobalConfig = (fraimKey, mode, tokens, jiraConfig) => {
|
|
|
380
180
|
fs_1.default.writeFileSync(globalConfigPath, JSON.stringify(config, null, 2));
|
|
381
181
|
console.log(chalk_1.default.green('✅ Global FRAIM configuration saved'));
|
|
382
182
|
};
|
|
183
|
+
// Parse CLI options into generic format using provider registry from server
|
|
184
|
+
// Tokens/config are optional - will prompt if not provided
|
|
185
|
+
const parseLegacyOptions = async (options, fraimKey) => {
|
|
186
|
+
const requestedProviders = [];
|
|
187
|
+
const providedTokens = {};
|
|
188
|
+
const providedConfigs = {};
|
|
189
|
+
// Use provider registry (with fallback) instead of direct client call
|
|
190
|
+
const { getAllProviderIds, getProviderConfigRequirements } = await Promise.resolve().then(() => __importStar(require('../providers/provider-registry')));
|
|
191
|
+
const providerIds = await getAllProviderIds();
|
|
192
|
+
// Dynamically check for provider flags from server registry
|
|
193
|
+
for (const providerId of providerIds) {
|
|
194
|
+
// Check for provider flag (e.g., --github)
|
|
195
|
+
if (options[providerId]) {
|
|
196
|
+
requestedProviders.push(providerId);
|
|
197
|
+
}
|
|
198
|
+
// Check for token option (e.g., --github-token)
|
|
199
|
+
const tokenKey = `${providerId}Token`;
|
|
200
|
+
if (options[tokenKey]) {
|
|
201
|
+
providedTokens[providerId] = options[tokenKey];
|
|
202
|
+
}
|
|
203
|
+
// Check for provider-specific config options
|
|
204
|
+
const configReqs = await getProviderConfigRequirements(providerId);
|
|
205
|
+
if (configReqs.length > 0) {
|
|
206
|
+
const config = {};
|
|
207
|
+
let hasAnyConfig = false;
|
|
208
|
+
configReqs.forEach(req => {
|
|
209
|
+
// Use custom CLI option name if provided, otherwise convert key to camelCase
|
|
210
|
+
const optionSuffix = req.cliOptionName
|
|
211
|
+
? req.cliOptionName.charAt(0).toUpperCase() + req.cliOptionName.slice(1)
|
|
212
|
+
: req.key.charAt(0).toUpperCase() + req.key.slice(1);
|
|
213
|
+
const optionKey = `${providerId}${optionSuffix}`;
|
|
214
|
+
if (options[optionKey]) {
|
|
215
|
+
config[req.key] = options[optionKey];
|
|
216
|
+
hasAnyConfig = true;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
if (hasAnyConfig) {
|
|
220
|
+
providedConfigs[providerId] = config;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return { requestedProviders, providedTokens, providedConfigs };
|
|
225
|
+
};
|
|
383
226
|
const runSetup = async (options) => {
|
|
384
227
|
console.log(chalk_1.default.blue('🚀 Welcome to FRAIM! Let\'s get you set up.\n'));
|
|
385
228
|
// Determine if this is an update (adding platforms to existing setup)
|
|
386
229
|
const globalConfigPath = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'config.json');
|
|
387
|
-
const isUpdate = fs_1.default.existsSync(globalConfigPath)
|
|
230
|
+
const isUpdate = fs_1.default.existsSync(globalConfigPath);
|
|
388
231
|
let fraimKey;
|
|
389
232
|
let mode;
|
|
390
233
|
const tokens = {};
|
|
391
|
-
|
|
234
|
+
const configs = {};
|
|
235
|
+
let requestedProviders = [];
|
|
236
|
+
let providedTokens = {};
|
|
237
|
+
let providedConfigs = {};
|
|
392
238
|
if (isUpdate) {
|
|
393
239
|
// Update existing setup - add platforms
|
|
394
240
|
console.log(chalk_1.default.blue('📝 Updating existing FRAIM configuration...\n'));
|
|
395
241
|
try {
|
|
396
242
|
const existingConfig = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
|
|
397
243
|
fraimKey = existingConfig.apiKey;
|
|
244
|
+
// Now we can parse options with the FRAIM key
|
|
245
|
+
const parsed = await parseLegacyOptions(options, fraimKey);
|
|
246
|
+
requestedProviders = parsed.requestedProviders;
|
|
247
|
+
providedTokens = parsed.providedTokens;
|
|
248
|
+
providedConfigs = parsed.providedConfigs;
|
|
249
|
+
// Only proceed with update if providers were requested
|
|
250
|
+
if (requestedProviders.length === 0) {
|
|
251
|
+
console.log(chalk_1.default.yellow('⚠️ No providers specified for update.'));
|
|
252
|
+
console.log(chalk_1.default.gray('Use --github, --gitlab, --jira, etc. to add providers.'));
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
398
255
|
mode = existingConfig.mode || 'integrated';
|
|
399
256
|
// Preserve existing tokens
|
|
400
257
|
if (existingConfig.tokens) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
258
|
+
Object.assign(tokens, existingConfig.tokens);
|
|
259
|
+
}
|
|
260
|
+
// Preserve existing configs
|
|
261
|
+
if (existingConfig.providerConfigs) {
|
|
262
|
+
Object.entries(existingConfig.providerConfigs).forEach(([key, value]) => {
|
|
263
|
+
const providerId = key.replace('Config', '');
|
|
264
|
+
configs[providerId] = value;
|
|
265
|
+
});
|
|
409
266
|
}
|
|
410
267
|
console.log(chalk_1.default.gray(` Current mode: ${mode}`));
|
|
411
|
-
|
|
268
|
+
// Show existing tokens
|
|
269
|
+
const { getProvider } = await Promise.resolve().then(() => __importStar(require('../providers/provider-registry')));
|
|
270
|
+
const providerNames = await Promise.all(Object.keys(tokens).map(async (id) => {
|
|
271
|
+
const provider = await getProvider(id);
|
|
272
|
+
return provider?.displayName || id;
|
|
273
|
+
}));
|
|
274
|
+
console.log(chalk_1.default.gray(` Existing tokens: ${providerNames.join(', ') || 'none'}\n`));
|
|
412
275
|
}
|
|
413
276
|
catch (e) {
|
|
414
277
|
console.log(chalk_1.default.red('❌ Failed to read existing configuration'));
|
|
278
|
+
console.error('Error details:', e);
|
|
415
279
|
process.exit(1);
|
|
416
280
|
}
|
|
417
281
|
}
|
|
@@ -424,136 +288,43 @@ const runSetup = async (options) => {
|
|
|
424
288
|
console.log(chalk_1.default.gray(' • Sync FRAIM scripts to your system\n'));
|
|
425
289
|
// Get FRAIM key
|
|
426
290
|
fraimKey = options.key || await promptForFraimKey();
|
|
427
|
-
|
|
428
|
-
console.log(chalk_1.default.red('❌ Invalid FRAIM key format. Key must start with fraim_'));
|
|
429
|
-
process.exit(1);
|
|
430
|
-
}
|
|
431
|
-
console.log(chalk_1.default.blue('🔑 Validating FRAIM key...'));
|
|
432
|
-
const isValid = await (0, token_validator_1.validateFraimKey)(fraimKey);
|
|
433
|
-
if (!isValid) {
|
|
434
|
-
console.log(chalk_1.default.red('❌ Invalid FRAIM key'));
|
|
435
|
-
process.exit(1);
|
|
436
|
-
}
|
|
437
|
-
console.log(chalk_1.default.green('✅ FRAIM key validated\n'));
|
|
291
|
+
console.log(chalk_1.default.green('✅ FRAIM key accepted\n'));
|
|
438
292
|
// Ask for mode preference
|
|
439
293
|
mode = await promptForMode();
|
|
440
294
|
}
|
|
441
295
|
// Handle platform tokens based on mode
|
|
442
296
|
if (mode === 'integrated') {
|
|
443
|
-
let
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
needADO = platforms.ado;
|
|
452
|
-
needGitLab = platforms.gitlab;
|
|
453
|
-
needJira = platforms.jira;
|
|
454
|
-
}
|
|
455
|
-
// Get GitHub token if needed
|
|
456
|
-
if (needGitHub && !tokens.github) {
|
|
457
|
-
if (options.githubToken) {
|
|
458
|
-
if (!(0, token_validator_1.isValidTokenFormat)(options.githubToken, 'github')) {
|
|
459
|
-
console.log(chalk_1.default.red('❌ Invalid GitHub token format'));
|
|
460
|
-
process.exit(1);
|
|
461
|
-
}
|
|
462
|
-
console.log(chalk_1.default.blue('🔍 Validating GitHub token...'));
|
|
463
|
-
const isValid = await (0, token_validator_1.validateGitHubToken)(options.githubToken);
|
|
464
|
-
if (!isValid) {
|
|
465
|
-
console.log(chalk_1.default.red('❌ Invalid GitHub token'));
|
|
466
|
-
process.exit(1);
|
|
467
|
-
}
|
|
468
|
-
tokens.github = options.githubToken;
|
|
469
|
-
console.log(chalk_1.default.green('✅ GitHub token validated\n'));
|
|
470
|
-
}
|
|
471
|
-
else {
|
|
472
|
-
try {
|
|
473
|
-
tokens.github = await (0, auto_mcp_setup_1.promptForGitHubToken)();
|
|
474
|
-
}
|
|
475
|
-
catch (e) {
|
|
476
|
-
// promptForGitHubToken calls process.exit on failure
|
|
477
|
-
process.exit(1);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
// Get ADO token if needed
|
|
482
|
-
if (needADO && !tokens.ado) {
|
|
483
|
-
if (options.adoToken) {
|
|
484
|
-
tokens.ado = options.adoToken;
|
|
485
|
-
console.log(chalk_1.default.green('✅ Azure DevOps token received\n'));
|
|
486
|
-
}
|
|
487
|
-
else {
|
|
297
|
+
let providersToSetup = requestedProviders;
|
|
298
|
+
// If no specific providers requested and not an update, ask user
|
|
299
|
+
if (!isUpdate && providersToSetup.length === 0) {
|
|
300
|
+
providersToSetup = await (0, provider_prompts_1.promptForProviders)((0, get_provider_client_1.getProviderClient)());
|
|
301
|
+
}
|
|
302
|
+
// Get credentials for each provider
|
|
303
|
+
for (const providerId of providersToSetup) {
|
|
304
|
+
if (!tokens[providerId]) {
|
|
488
305
|
try {
|
|
489
|
-
tokens
|
|
306
|
+
// Use provided tokens if available, otherwise prompt
|
|
307
|
+
const creds = await (0, provider_prompts_1.promptForProviderCredentials)((0, get_provider_client_1.getProviderClient)(), providerId, providedTokens[providerId], providedConfigs[providerId]);
|
|
308
|
+
tokens[providerId] = creds.token;
|
|
309
|
+
if (creds.config) {
|
|
310
|
+
configs[providerId] = creds.config;
|
|
311
|
+
}
|
|
490
312
|
}
|
|
491
313
|
catch (e) {
|
|
492
|
-
|
|
314
|
+
const providerName = await (0, provider_registry_1.getProviderDisplayName)(providerId);
|
|
315
|
+
console.log(chalk_1.default.red(`❌ Failed to get ${providerName} credentials`));
|
|
493
316
|
process.exit(1);
|
|
494
317
|
}
|
|
495
318
|
}
|
|
496
319
|
}
|
|
497
|
-
|
|
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) {
|
|
320
|
+
if (Object.keys(tokens).length === 0) {
|
|
551
321
|
console.log(chalk_1.default.yellow('⚠️ No platform tokens configured.'));
|
|
552
322
|
console.log(chalk_1.default.gray(' You can add them later with:'));
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
323
|
+
const allProviderIds = await (0, provider_registry_1.getAllProviderIds)();
|
|
324
|
+
allProviderIds.forEach(id => {
|
|
325
|
+
console.log(chalk_1.default.cyan(` fraim setup --${id}`));
|
|
326
|
+
});
|
|
327
|
+
console.log();
|
|
557
328
|
}
|
|
558
329
|
}
|
|
559
330
|
else if (mode === 'split') {
|
|
@@ -561,182 +332,70 @@ const runSetup = async (options) => {
|
|
|
561
332
|
console.log(chalk_1.default.blue('\n🔀 Split Mode Configuration'));
|
|
562
333
|
console.log(chalk_1.default.gray('Configure separate platforms for code hosting and issue tracking.\n'));
|
|
563
334
|
// Get code repository platform
|
|
564
|
-
const
|
|
565
|
-
// Get code repository
|
|
566
|
-
if (
|
|
567
|
-
|
|
568
|
-
if
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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);
|
|
335
|
+
const codeRepoProvider = await (0, provider_prompts_1.promptForSingleProvider)((0, get_provider_client_1.getProviderClient)(), 'code');
|
|
336
|
+
// Get code repository credentials
|
|
337
|
+
if (!tokens[codeRepoProvider]) {
|
|
338
|
+
try {
|
|
339
|
+
// Use provided tokens if available, otherwise prompt
|
|
340
|
+
const creds = await (0, provider_prompts_1.promptForProviderCredentials)((0, get_provider_client_1.getProviderClient)(), codeRepoProvider, providedTokens[codeRepoProvider], providedConfigs[codeRepoProvider]);
|
|
341
|
+
tokens[codeRepoProvider] = creds.token;
|
|
342
|
+
if (creds.config) {
|
|
343
|
+
configs[codeRepoProvider] = creds.config;
|
|
610
344
|
}
|
|
611
|
-
tokens.gitlab = options.gitlabToken;
|
|
612
345
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
catch (e) {
|
|
618
|
-
console.log(chalk_1.default.red('Failed to get GitLab token'));
|
|
619
|
-
process.exit(1);
|
|
620
|
-
}
|
|
346
|
+
catch (e) {
|
|
347
|
+
const providerName = await (0, provider_registry_1.getProviderDisplayName)(codeRepoProvider);
|
|
348
|
+
console.log(chalk_1.default.red(`❌ Failed to get ${providerName} credentials`));
|
|
349
|
+
process.exit(1);
|
|
621
350
|
}
|
|
622
351
|
}
|
|
623
352
|
// Get issue tracking platform
|
|
624
|
-
const
|
|
625
|
-
// Get issue tracking
|
|
626
|
-
if (
|
|
627
|
-
|
|
628
|
-
if
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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;
|
|
353
|
+
const issueProvider = await (0, provider_prompts_1.promptForSingleProvider)((0, get_provider_client_1.getProviderClient)(), 'issues');
|
|
354
|
+
// Get issue tracking credentials (if different from code repo)
|
|
355
|
+
if (!tokens[issueProvider]) {
|
|
356
|
+
try {
|
|
357
|
+
// Use provided tokens if available, otherwise prompt
|
|
358
|
+
const creds = await (0, provider_prompts_1.promptForProviderCredentials)((0, get_provider_client_1.getProviderClient)(), issueProvider, providedTokens[issueProvider], providedConfigs[issueProvider]);
|
|
359
|
+
tokens[issueProvider] = creds.token;
|
|
360
|
+
if (creds.config) {
|
|
361
|
+
configs[issueProvider] = creds.config;
|
|
701
362
|
}
|
|
702
363
|
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
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
|
-
}
|
|
364
|
+
catch (e) {
|
|
365
|
+
const providerName = await (0, provider_registry_1.getProviderDisplayName)(issueProvider);
|
|
366
|
+
console.log(chalk_1.default.red(`❌ Failed to get ${providerName} credentials`));
|
|
367
|
+
process.exit(1);
|
|
714
368
|
}
|
|
715
369
|
}
|
|
716
|
-
|
|
370
|
+
const codeRepoName = await (0, provider_registry_1.getProviderDisplayName)(codeRepoProvider);
|
|
371
|
+
const issueName = await (0, provider_registry_1.getProviderDisplayName)(issueProvider);
|
|
372
|
+
console.log(chalk_1.default.green(`\n✅ Split mode configured: ${codeRepoName} (code) + ${issueName} (issues)\n`));
|
|
717
373
|
}
|
|
718
374
|
else {
|
|
719
375
|
console.log(chalk_1.default.gray('ℹ️ Conversational mode: No platform tokens needed\n'));
|
|
720
376
|
}
|
|
721
377
|
// Save global configuration
|
|
722
378
|
console.log(chalk_1.default.blue('💾 Saving global configuration...'));
|
|
723
|
-
saveGlobalConfig(fraimKey, mode, tokens,
|
|
379
|
+
saveGlobalConfig(fraimKey, mode, tokens, configs);
|
|
724
380
|
// Configure MCP servers (only on initial setup)
|
|
725
381
|
if (!isUpdate) {
|
|
726
382
|
console.log(chalk_1.default.blue('\n🔌 Configuring MCP servers...'));
|
|
727
|
-
//
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
console.log(chalk_1.default.yellow('ℹ️ Conversational mode: Configuring MCP servers without GitHub integration'));
|
|
736
|
-
console.log(chalk_1.default.gray(' FRAIM workflows will work, but GitHub-specific features will be unavailable\n'));
|
|
383
|
+
// Convert to legacy format for MCP config generator
|
|
384
|
+
const mcpTokens = {};
|
|
385
|
+
Object.entries(tokens).forEach(([id, token]) => {
|
|
386
|
+
mcpTokens[id] = token;
|
|
387
|
+
});
|
|
388
|
+
if (mode === 'conversational' && Object.keys(mcpTokens).length === 0) {
|
|
389
|
+
console.log(chalk_1.default.yellow('ℹ️ Conversational mode: Configuring MCP servers without platform integration'));
|
|
390
|
+
console.log(chalk_1.default.gray(' FRAIM workflows will work, but platform-specific features will be unavailable\n'));
|
|
737
391
|
}
|
|
738
392
|
try {
|
|
739
|
-
|
|
393
|
+
// Build providerConfigs map from configs
|
|
394
|
+
const providerConfigsMap = {};
|
|
395
|
+
Object.entries(configs).forEach(([providerId, config]) => {
|
|
396
|
+
providerConfigsMap[providerId] = config;
|
|
397
|
+
});
|
|
398
|
+
await (0, auto_mcp_setup_1.autoConfigureMCP)(fraimKey, mcpTokens, options.ide ? [options.ide] : undefined, providerConfigsMap);
|
|
740
399
|
}
|
|
741
400
|
catch (e) {
|
|
742
401
|
console.log(chalk_1.default.yellow('⚠️ MCP configuration encountered issues'));
|
|
@@ -755,39 +414,64 @@ const runSetup = async (options) => {
|
|
|
755
414
|
// Show summary
|
|
756
415
|
console.log(chalk_1.default.green('\n🎯 Setup complete!'));
|
|
757
416
|
console.log(chalk_1.default.gray(` Mode: ${mode}`));
|
|
758
|
-
if (mode
|
|
759
|
-
|
|
417
|
+
if (mode !== 'conversational') {
|
|
418
|
+
const configuredProviders = await Promise.all(Object.keys(tokens).map(async (id) => await (0, provider_registry_1.getProviderDisplayName)(id)));
|
|
419
|
+
console.log(chalk_1.default.gray(` Platforms: ${configuredProviders.join(', ') || 'none'}`));
|
|
760
420
|
}
|
|
761
421
|
console.log(chalk_1.default.cyan('\n📝 For future projects:'));
|
|
762
422
|
console.log(chalk_1.default.cyan(' 1. cd into any project directory'));
|
|
763
423
|
console.log(chalk_1.default.cyan(' 2. Run: fraim init-project'));
|
|
764
|
-
console.log(chalk_1.default.cyan(' 3. Ask your AI agent
|
|
765
|
-
if (mode === 'integrated'
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
console.log(chalk_1.default.gray(' fraim setup --jira'));
|
|
424
|
+
console.log(chalk_1.default.cyan(' 3. Ask your AI agent "list fraim workflows"'));
|
|
425
|
+
if (mode === 'integrated') {
|
|
426
|
+
const allProviderIds = await (0, provider_registry_1.getAllProviderIds)();
|
|
427
|
+
const unconfiguredProviders = allProviderIds.filter(id => !tokens[id]);
|
|
428
|
+
if (unconfiguredProviders.length > 0) {
|
|
429
|
+
console.log(chalk_1.default.gray('\n💡 To add more platforms later:'));
|
|
430
|
+
unconfiguredProviders.forEach(id => {
|
|
431
|
+
console.log(chalk_1.default.gray(` fraim setup --${id}`));
|
|
432
|
+
});
|
|
433
|
+
}
|
|
775
434
|
}
|
|
776
435
|
};
|
|
777
436
|
exports.runSetup = runSetup;
|
|
778
437
|
exports.setupCommand = new commander_1.Command('setup')
|
|
779
438
|
.description('Complete global FRAIM setup with platform configuration')
|
|
780
439
|
.option('--key <key>', 'FRAIM API key')
|
|
781
|
-
.option('--
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
440
|
+
.option('--ide <ides>', 'Configure specific IDEs');
|
|
441
|
+
// Track initialization promise for CLI entry point
|
|
442
|
+
exports.setupCommandInitialization = null;
|
|
443
|
+
// Dynamically add provider options from registry (async initialization)
|
|
444
|
+
exports.setupCommandInitialization = (async () => {
|
|
445
|
+
try {
|
|
446
|
+
const allProviderIds = await (0, provider_registry_1.getAllProviderIds)();
|
|
447
|
+
for (const providerId of allProviderIds) {
|
|
448
|
+
const provider = await (0, provider_registry_1.getProvider)(providerId);
|
|
449
|
+
if (!provider)
|
|
450
|
+
continue;
|
|
451
|
+
// Add provider flag (e.g., --github)
|
|
452
|
+
exports.setupCommand.option(`--${providerId}`, `Add/update ${provider.displayName} integration`);
|
|
453
|
+
// Add token option (e.g., --github-token) - primarily for testing/automation
|
|
454
|
+
exports.setupCommand.option(`--${providerId}-token <token>`, `${provider.displayName} token (optional - will prompt if not provided)`);
|
|
455
|
+
// Add config options if provider requires them
|
|
456
|
+
const configReqs = await (0, provider_registry_1.getProviderConfigRequirements)(providerId);
|
|
457
|
+
configReqs.forEach(req => {
|
|
458
|
+
// Use custom CLI option name if provided, otherwise convert key to kebab-case
|
|
459
|
+
const optionSuffix = req.cliOptionName || req.key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
460
|
+
const optionName = `${providerId}-${optionSuffix}`;
|
|
461
|
+
exports.setupCommand.option(`--${optionName} <value>`, `${req.description} (optional - will prompt if not provided)`);
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
catch (error) {
|
|
466
|
+
// If we can't fetch providers (e.g., no config yet), that's okay
|
|
467
|
+
// The command will still work, just without dynamic options
|
|
468
|
+
}
|
|
469
|
+
})();
|
|
470
|
+
// Wrap the action to ensure initialization completes first
|
|
471
|
+
exports.setupCommand.action(async (options) => {
|
|
472
|
+
// Wait for dynamic options to be registered
|
|
473
|
+
if (exports.setupCommandInitialization) {
|
|
474
|
+
await exports.setupCommandInitialization;
|
|
475
|
+
}
|
|
476
|
+
return (0, exports.runSetup)(options);
|
|
477
|
+
});
|