fraim-framework 2.0.82 → 2.0.84
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 +16 -3
- 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 +145 -78
- 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 +287 -566
- 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 -3
- 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 +161 -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 +68 -119
- package/dist/src/cli/setup/mcp-config-generator.js +64 -321
- package/dist/src/cli/setup/provider-prompts.js +300 -0
- package/dist/src/core/utils/workflow-parser.js +5 -1
- package/dist/src/local-mcp-server/stdio-server.js +30 -30
- package/package.json +6 -3
- 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,106 @@ 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
|
+
// Allow updating FRAIM key even without provider changes
|
|
244
|
+
fraimKey = options.key || existingConfig.apiKey;
|
|
245
|
+
// Now we can parse options with the FRAIM key
|
|
246
|
+
const parsed = await parseLegacyOptions(options, fraimKey);
|
|
247
|
+
requestedProviders = parsed.requestedProviders;
|
|
248
|
+
providedTokens = parsed.providedTokens;
|
|
249
|
+
providedConfigs = parsed.providedConfigs;
|
|
250
|
+
// Check if this is just a key update or a provider update
|
|
251
|
+
const isKeyUpdate = options.key && options.key !== existingConfig.apiKey;
|
|
252
|
+
const isProviderUpdate = requestedProviders.length > 0;
|
|
253
|
+
// Only proceed if there's something to update
|
|
254
|
+
if (!isKeyUpdate && !isProviderUpdate) {
|
|
255
|
+
console.log(chalk_1.default.yellow('⚠️ No changes specified.'));
|
|
256
|
+
console.log(chalk_1.default.gray('Use --key to update FRAIM key, or --github, --gitlab, etc. to add providers.'));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
398
259
|
mode = existingConfig.mode || 'integrated';
|
|
399
260
|
// Preserve existing tokens
|
|
400
261
|
if (existingConfig.tokens) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
262
|
+
Object.assign(tokens, existingConfig.tokens);
|
|
263
|
+
}
|
|
264
|
+
// Preserve existing configs
|
|
265
|
+
if (existingConfig.providerConfigs) {
|
|
266
|
+
Object.entries(existingConfig.providerConfigs).forEach(([key, value]) => {
|
|
267
|
+
const providerId = key.replace('Config', '');
|
|
268
|
+
configs[providerId] = value;
|
|
269
|
+
});
|
|
409
270
|
}
|
|
410
271
|
console.log(chalk_1.default.gray(` Current mode: ${mode}`));
|
|
411
|
-
|
|
272
|
+
// Show existing tokens
|
|
273
|
+
const { getProvider } = await Promise.resolve().then(() => __importStar(require('../providers/provider-registry')));
|
|
274
|
+
const providerNames = await Promise.all(Object.keys(tokens).map(async (id) => {
|
|
275
|
+
const provider = await getProvider(id);
|
|
276
|
+
return provider?.displayName || id;
|
|
277
|
+
}));
|
|
278
|
+
console.log(chalk_1.default.gray(` Existing tokens: ${providerNames.join(', ') || 'none'}\n`));
|
|
412
279
|
}
|
|
413
280
|
catch (e) {
|
|
414
281
|
console.log(chalk_1.default.red('❌ Failed to read existing configuration'));
|
|
282
|
+
console.error('Error details:', e);
|
|
415
283
|
process.exit(1);
|
|
416
284
|
}
|
|
417
285
|
}
|
|
@@ -424,136 +292,43 @@ const runSetup = async (options) => {
|
|
|
424
292
|
console.log(chalk_1.default.gray(' • Sync FRAIM scripts to your system\n'));
|
|
425
293
|
// Get FRAIM key
|
|
426
294
|
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'));
|
|
295
|
+
console.log(chalk_1.default.green('✅ FRAIM key accepted\n'));
|
|
438
296
|
// Ask for mode preference
|
|
439
297
|
mode = await promptForMode();
|
|
440
298
|
}
|
|
441
299
|
// Handle platform tokens based on mode
|
|
442
300
|
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 {
|
|
488
|
-
try {
|
|
489
|
-
tokens.ado = await promptForADOToken();
|
|
490
|
-
}
|
|
491
|
-
catch (e) {
|
|
492
|
-
console.log(chalk_1.default.red('❌ Failed to get Azure DevOps token'));
|
|
493
|
-
process.exit(1);
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
}
|
|
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 {
|
|
301
|
+
let providersToSetup = requestedProviders;
|
|
302
|
+
// If no specific providers requested and not an update, ask user
|
|
303
|
+
if (!isUpdate && providersToSetup.length === 0) {
|
|
304
|
+
providersToSetup = await (0, provider_prompts_1.promptForProviders)((0, get_provider_client_1.getProviderClient)());
|
|
305
|
+
}
|
|
306
|
+
// Get credentials for each provider
|
|
307
|
+
for (const providerId of providersToSetup) {
|
|
308
|
+
if (!tokens[providerId]) {
|
|
507
309
|
try {
|
|
508
|
-
tokens
|
|
310
|
+
// Use provided tokens if available, otherwise prompt
|
|
311
|
+
const creds = await (0, provider_prompts_1.promptForProviderCredentials)((0, get_provider_client_1.getProviderClient)(), providerId, providedTokens[providerId], providedConfigs[providerId]);
|
|
312
|
+
tokens[providerId] = creds.token;
|
|
313
|
+
if (creds.config) {
|
|
314
|
+
configs[providerId] = creds.config;
|
|
315
|
+
}
|
|
509
316
|
}
|
|
510
317
|
catch (e) {
|
|
511
|
-
|
|
318
|
+
const providerName = await (0, provider_registry_1.getProviderDisplayName)(providerId);
|
|
319
|
+
console.log(chalk_1.default.red(`❌ Failed to get ${providerName} credentials`));
|
|
512
320
|
process.exit(1);
|
|
513
321
|
}
|
|
514
322
|
}
|
|
515
323
|
}
|
|
516
|
-
|
|
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) {
|
|
324
|
+
if (Object.keys(tokens).length === 0) {
|
|
551
325
|
console.log(chalk_1.default.yellow('⚠️ No platform tokens configured.'));
|
|
552
326
|
console.log(chalk_1.default.gray(' You can add them later with:'));
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
327
|
+
const allProviderIds = await (0, provider_registry_1.getAllProviderIds)();
|
|
328
|
+
allProviderIds.forEach(id => {
|
|
329
|
+
console.log(chalk_1.default.cyan(` fraim setup --${id}`));
|
|
330
|
+
});
|
|
331
|
+
console.log();
|
|
557
332
|
}
|
|
558
333
|
}
|
|
559
334
|
else if (mode === 'split') {
|
|
@@ -561,188 +336,109 @@ const runSetup = async (options) => {
|
|
|
561
336
|
console.log(chalk_1.default.blue('\n🔀 Split Mode Configuration'));
|
|
562
337
|
console.log(chalk_1.default.gray('Configure separate platforms for code hosting and issue tracking.\n'));
|
|
563
338
|
// 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);
|
|
339
|
+
const codeRepoProvider = await (0, provider_prompts_1.promptForSingleProvider)((0, get_provider_client_1.getProviderClient)(), 'code');
|
|
340
|
+
// Get code repository credentials
|
|
341
|
+
if (!tokens[codeRepoProvider]) {
|
|
342
|
+
try {
|
|
343
|
+
// Use provided tokens if available, otherwise prompt
|
|
344
|
+
const creds = await (0, provider_prompts_1.promptForProviderCredentials)((0, get_provider_client_1.getProviderClient)(), codeRepoProvider, providedTokens[codeRepoProvider], providedConfigs[codeRepoProvider]);
|
|
345
|
+
tokens[codeRepoProvider] = creds.token;
|
|
346
|
+
if (creds.config) {
|
|
347
|
+
configs[codeRepoProvider] = creds.config;
|
|
610
348
|
}
|
|
611
|
-
tokens.gitlab = options.gitlabToken;
|
|
612
349
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
catch (e) {
|
|
618
|
-
console.log(chalk_1.default.red('Failed to get GitLab token'));
|
|
619
|
-
process.exit(1);
|
|
620
|
-
}
|
|
350
|
+
catch (e) {
|
|
351
|
+
const providerName = await (0, provider_registry_1.getProviderDisplayName)(codeRepoProvider);
|
|
352
|
+
console.log(chalk_1.default.red(`❌ Failed to get ${providerName} credentials`));
|
|
353
|
+
process.exit(1);
|
|
621
354
|
}
|
|
622
355
|
}
|
|
623
356
|
// 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;
|
|
357
|
+
const issueProvider = await (0, provider_prompts_1.promptForSingleProvider)((0, get_provider_client_1.getProviderClient)(), 'issues');
|
|
358
|
+
// Get issue tracking credentials (if different from code repo)
|
|
359
|
+
if (!tokens[issueProvider]) {
|
|
360
|
+
try {
|
|
361
|
+
// Use provided tokens if available, otherwise prompt
|
|
362
|
+
const creds = await (0, provider_prompts_1.promptForProviderCredentials)((0, get_provider_client_1.getProviderClient)(), issueProvider, providedTokens[issueProvider], providedConfigs[issueProvider]);
|
|
363
|
+
tokens[issueProvider] = creds.token;
|
|
364
|
+
if (creds.config) {
|
|
365
|
+
configs[issueProvider] = creds.config;
|
|
701
366
|
}
|
|
702
367
|
}
|
|
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
|
-
}
|
|
368
|
+
catch (e) {
|
|
369
|
+
const providerName = await (0, provider_registry_1.getProviderDisplayName)(issueProvider);
|
|
370
|
+
console.log(chalk_1.default.red(`❌ Failed to get ${providerName} credentials`));
|
|
371
|
+
process.exit(1);
|
|
714
372
|
}
|
|
715
373
|
}
|
|
716
|
-
|
|
374
|
+
const codeRepoName = await (0, provider_registry_1.getProviderDisplayName)(codeRepoProvider);
|
|
375
|
+
const issueName = await (0, provider_registry_1.getProviderDisplayName)(issueProvider);
|
|
376
|
+
console.log(chalk_1.default.green(`\n✅ Split mode configured: ${codeRepoName} (code) + ${issueName} (issues)\n`));
|
|
717
377
|
}
|
|
718
378
|
else {
|
|
719
379
|
console.log(chalk_1.default.gray('ℹ️ Conversational mode: No platform tokens needed\n'));
|
|
720
380
|
}
|
|
721
381
|
// Save global configuration
|
|
722
382
|
console.log(chalk_1.default.blue('💾 Saving global configuration...'));
|
|
723
|
-
saveGlobalConfig(fraimKey, mode, tokens,
|
|
724
|
-
// Configure MCP servers
|
|
383
|
+
saveGlobalConfig(fraimKey, mode, tokens, configs);
|
|
384
|
+
// Configure MCP servers
|
|
725
385
|
if (!isUpdate) {
|
|
386
|
+
// Initial setup - configure all detected IDEs
|
|
726
387
|
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'));
|
|
388
|
+
// Convert to legacy format for MCP config generator
|
|
389
|
+
const mcpTokens = {};
|
|
390
|
+
Object.entries(tokens).forEach(([id, token]) => {
|
|
391
|
+
mcpTokens[id] = token;
|
|
392
|
+
});
|
|
393
|
+
if (mode === 'conversational' && Object.keys(mcpTokens).length === 0) {
|
|
394
|
+
console.log(chalk_1.default.yellow('ℹ️ Conversational mode: Configuring MCP servers without platform integration'));
|
|
395
|
+
console.log(chalk_1.default.gray(' FRAIM workflows will work, but platform-specific features will be unavailable\n'));
|
|
737
396
|
}
|
|
738
397
|
try {
|
|
739
|
-
|
|
398
|
+
// Build providerConfigs map from configs
|
|
399
|
+
const providerConfigsMap = {};
|
|
400
|
+
Object.entries(configs).forEach(([providerId, config]) => {
|
|
401
|
+
providerConfigsMap[providerId] = config;
|
|
402
|
+
});
|
|
403
|
+
await (0, auto_mcp_setup_1.autoConfigureMCP)(fraimKey, mcpTokens, options.ide ? [options.ide] : undefined, providerConfigsMap);
|
|
740
404
|
}
|
|
741
405
|
catch (e) {
|
|
742
406
|
console.log(chalk_1.default.yellow('⚠️ MCP configuration encountered issues'));
|
|
743
407
|
console.log(chalk_1.default.gray(' You can configure MCP manually or run setup again later\n'));
|
|
744
408
|
}
|
|
745
|
-
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
// Update existing setup - refresh all IDE MCP configs with new keys
|
|
412
|
+
console.log(chalk_1.default.blue('\n🔄 Updating IDE MCP configurations...'));
|
|
413
|
+
try {
|
|
414
|
+
const { detectInstalledIDEs } = await Promise.resolve().then(() => __importStar(require('../setup/ide-detector')));
|
|
415
|
+
const installedIDEs = detectInstalledIDEs();
|
|
416
|
+
if (installedIDEs.length === 0) {
|
|
417
|
+
console.log(chalk_1.default.gray(' No IDE configurations found to update'));
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
// Convert to legacy format for MCP config generator
|
|
421
|
+
const mcpTokens = {};
|
|
422
|
+
Object.entries(tokens).forEach(([id, token]) => {
|
|
423
|
+
mcpTokens[id] = token;
|
|
424
|
+
});
|
|
425
|
+
// Build providerConfigs map from configs
|
|
426
|
+
const providerConfigsMap = {};
|
|
427
|
+
Object.entries(configs).forEach(([providerId, config]) => {
|
|
428
|
+
providerConfigsMap[providerId] = config;
|
|
429
|
+
});
|
|
430
|
+
const ideNames = installedIDEs.map(ide => ide.name);
|
|
431
|
+
await (0, auto_mcp_setup_1.autoConfigureMCP)(fraimKey, mcpTokens, ideNames, providerConfigsMap);
|
|
432
|
+
console.log(chalk_1.default.green(`✅ Updated MCP configs for: ${ideNames.join(', ')}`));
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
catch (e) {
|
|
436
|
+
console.log(chalk_1.default.yellow('⚠️ Failed to update IDE MCP configurations'));
|
|
437
|
+
console.log(chalk_1.default.gray(' You can update them manually with: fraim add-ide <ide-name>\n'));
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
// Auto-run project init if we're in a git repo (only on initial setup)
|
|
441
|
+
if (!isUpdate) {
|
|
746
442
|
if ((0, platform_detection_1.isGitRepository)()) {
|
|
747
443
|
console.log(chalk_1.default.blue('\n📁 Git repository detected — initializing project...'));
|
|
748
444
|
await (0, init_project_1.runInitProject)();
|
|
@@ -755,39 +451,64 @@ const runSetup = async (options) => {
|
|
|
755
451
|
// Show summary
|
|
756
452
|
console.log(chalk_1.default.green('\n🎯 Setup complete!'));
|
|
757
453
|
console.log(chalk_1.default.gray(` Mode: ${mode}`));
|
|
758
|
-
if (mode
|
|
759
|
-
|
|
454
|
+
if (mode !== 'conversational') {
|
|
455
|
+
const configuredProviders = await Promise.all(Object.keys(tokens).map(async (id) => await (0, provider_registry_1.getProviderDisplayName)(id)));
|
|
456
|
+
console.log(chalk_1.default.gray(` Platforms: ${configuredProviders.join(', ') || 'none'}`));
|
|
760
457
|
}
|
|
761
458
|
console.log(chalk_1.default.cyan('\n📝 For future projects:'));
|
|
762
459
|
console.log(chalk_1.default.cyan(' 1. cd into any project directory'));
|
|
763
460
|
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'));
|
|
461
|
+
console.log(chalk_1.default.cyan(' 3. Ask your AI agent "list fraim workflows"'));
|
|
462
|
+
if (mode === 'integrated') {
|
|
463
|
+
const allProviderIds = await (0, provider_registry_1.getAllProviderIds)();
|
|
464
|
+
const unconfiguredProviders = allProviderIds.filter(id => !tokens[id]);
|
|
465
|
+
if (unconfiguredProviders.length > 0) {
|
|
466
|
+
console.log(chalk_1.default.gray('\n💡 To add more platforms later:'));
|
|
467
|
+
unconfiguredProviders.forEach(id => {
|
|
468
|
+
console.log(chalk_1.default.gray(` fraim setup --${id}`));
|
|
469
|
+
});
|
|
470
|
+
}
|
|
775
471
|
}
|
|
776
472
|
};
|
|
777
473
|
exports.runSetup = runSetup;
|
|
778
474
|
exports.setupCommand = new commander_1.Command('setup')
|
|
779
475
|
.description('Complete global FRAIM setup with platform configuration')
|
|
780
476
|
.option('--key <key>', 'FRAIM API key')
|
|
781
|
-
.option('--
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
477
|
+
.option('--ide <ides>', 'Configure specific IDEs');
|
|
478
|
+
// Track initialization promise for CLI entry point
|
|
479
|
+
exports.setupCommandInitialization = null;
|
|
480
|
+
// Dynamically add provider options from registry (async initialization)
|
|
481
|
+
exports.setupCommandInitialization = (async () => {
|
|
482
|
+
try {
|
|
483
|
+
const allProviderIds = await (0, provider_registry_1.getAllProviderIds)();
|
|
484
|
+
for (const providerId of allProviderIds) {
|
|
485
|
+
const provider = await (0, provider_registry_1.getProvider)(providerId);
|
|
486
|
+
if (!provider)
|
|
487
|
+
continue;
|
|
488
|
+
// Add provider flag (e.g., --github)
|
|
489
|
+
exports.setupCommand.option(`--${providerId}`, `Add/update ${provider.displayName} integration`);
|
|
490
|
+
// Add token option (e.g., --github-token) - primarily for testing/automation
|
|
491
|
+
exports.setupCommand.option(`--${providerId}-token <token>`, `${provider.displayName} token (optional - will prompt if not provided)`);
|
|
492
|
+
// Add config options if provider requires them
|
|
493
|
+
const configReqs = await (0, provider_registry_1.getProviderConfigRequirements)(providerId);
|
|
494
|
+
configReqs.forEach(req => {
|
|
495
|
+
// Use custom CLI option name if provided, otherwise convert key to kebab-case
|
|
496
|
+
const optionSuffix = req.cliOptionName || req.key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
497
|
+
const optionName = `${providerId}-${optionSuffix}`;
|
|
498
|
+
exports.setupCommand.option(`--${optionName} <value>`, `${req.description} (optional - will prompt if not provided)`);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
catch (error) {
|
|
503
|
+
// If we can't fetch providers (e.g., no config yet), that's okay
|
|
504
|
+
// The command will still work, just without dynamic options
|
|
505
|
+
}
|
|
506
|
+
})();
|
|
507
|
+
// Wrap the action to ensure initialization completes first
|
|
508
|
+
exports.setupCommand.action(async (options) => {
|
|
509
|
+
// Wait for dynamic options to be registered
|
|
510
|
+
if (exports.setupCommandInitialization) {
|
|
511
|
+
await exports.setupCommandInitialization;
|
|
512
|
+
}
|
|
513
|
+
return (0, exports.runSetup)(options);
|
|
514
|
+
});
|