fraim-framework 2.0.76 → 2.0.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli/commands/add-ide.js +25 -11
- package/dist/src/cli/commands/init-project.js +42 -40
- package/dist/src/cli/commands/setup.js +125 -15
- package/dist/src/cli/setup/auto-mcp-setup.js +27 -10
- package/dist/src/cli/setup/mcp-config-generator.js +153 -39
- package/dist/src/cli/setup/token-validator.js +8 -0
- package/dist/src/cli/utils/platform-detection.js +45 -0
- package/dist/src/core/config-loader.js +4 -1
- package/dist/src/core/utils/provider-utils.js +5 -1
- package/dist/src/local-mcp-server/stdio-server.js +237 -59
- package/package.json +1 -1
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateMCPConfig = exports.generateWindsurfMCPServers = exports.generateVSCodeMCPServers = exports.generateCodexMCPServers = exports.generateKiroMCPServers = exports.generateClaudeMCPServers = exports.generateStandardMCPServers = exports.mergeTomlMCPServers = exports.extractTomlMcpServerBlock = void 0;
|
|
4
|
+
const GITHUB_MCP_URL = 'https://api.githubcopilot.com/mcp/';
|
|
5
|
+
const GITLAB_MCP_URL = 'https://gitlab.com/api/v4/mcp';
|
|
6
|
+
const JIRA_MCP_URL = 'https://mcp.atlassian.com/v1/sse';
|
|
7
|
+
const normalizeTokens = (tokenInput) => {
|
|
8
|
+
if (!tokenInput)
|
|
9
|
+
return {};
|
|
10
|
+
if (typeof tokenInput === 'string') {
|
|
11
|
+
return tokenInput ? { github: tokenInput } : {};
|
|
12
|
+
}
|
|
13
|
+
return tokenInput;
|
|
14
|
+
};
|
|
4
15
|
const escapeTomlString = (value) => value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
5
16
|
const findTomlServerBlockRange = (content, server) => {
|
|
6
17
|
const lines = content.split(/\r?\n/);
|
|
@@ -82,7 +93,8 @@ const mergeTomlMCPServers = (existingContent, generatedContent, servers) => {
|
|
|
82
93
|
};
|
|
83
94
|
};
|
|
84
95
|
exports.mergeTomlMCPServers = mergeTomlMCPServers;
|
|
85
|
-
const generateStandardMCPServers = (fraimKey,
|
|
96
|
+
const generateStandardMCPServers = (fraimKey, tokenInput) => {
|
|
97
|
+
const tokens = normalizeTokens(tokenInput);
|
|
86
98
|
const servers = {
|
|
87
99
|
git: {
|
|
88
100
|
command: "npx",
|
|
@@ -100,19 +112,35 @@ const generateStandardMCPServers = (fraimKey, githubToken) => {
|
|
|
100
112
|
}
|
|
101
113
|
}
|
|
102
114
|
};
|
|
103
|
-
|
|
104
|
-
if (githubToken) {
|
|
115
|
+
if (tokens.github) {
|
|
105
116
|
servers.github = {
|
|
106
|
-
serverUrl:
|
|
117
|
+
serverUrl: GITHUB_MCP_URL,
|
|
118
|
+
headers: {
|
|
119
|
+
Authorization: `Bearer ${tokens.github}`
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (tokens.gitlab) {
|
|
124
|
+
servers.gitlab = {
|
|
125
|
+
serverUrl: GITLAB_MCP_URL,
|
|
126
|
+
headers: {
|
|
127
|
+
Authorization: `Bearer ${tokens.gitlab}`
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
if (tokens.jira) {
|
|
132
|
+
servers.jira = {
|
|
133
|
+
serverUrl: JIRA_MCP_URL,
|
|
107
134
|
headers: {
|
|
108
|
-
Authorization: `Bearer ${
|
|
135
|
+
Authorization: `Bearer ${tokens.jira}`
|
|
109
136
|
}
|
|
110
137
|
};
|
|
111
138
|
}
|
|
112
139
|
return { mcpServers: servers };
|
|
113
140
|
};
|
|
114
141
|
exports.generateStandardMCPServers = generateStandardMCPServers;
|
|
115
|
-
const generateClaudeMCPServers = (fraimKey,
|
|
142
|
+
const generateClaudeMCPServers = (fraimKey, tokenInput) => {
|
|
143
|
+
const tokens = normalizeTokens(tokenInput);
|
|
116
144
|
const servers = {
|
|
117
145
|
git: {
|
|
118
146
|
command: "npx",
|
|
@@ -130,20 +158,38 @@ const generateClaudeMCPServers = (fraimKey, githubToken) => {
|
|
|
130
158
|
}
|
|
131
159
|
}
|
|
132
160
|
};
|
|
133
|
-
|
|
134
|
-
if (githubToken) {
|
|
161
|
+
if (tokens.github) {
|
|
135
162
|
servers.github = {
|
|
136
163
|
type: "http",
|
|
137
|
-
url:
|
|
164
|
+
url: GITHUB_MCP_URL,
|
|
138
165
|
headers: {
|
|
139
|
-
Authorization: `Bearer ${
|
|
166
|
+
Authorization: `Bearer ${tokens.github}`
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
if (tokens.gitlab) {
|
|
171
|
+
servers.gitlab = {
|
|
172
|
+
type: 'http',
|
|
173
|
+
url: GITLAB_MCP_URL,
|
|
174
|
+
headers: {
|
|
175
|
+
Authorization: `Bearer ${tokens.gitlab}`
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (tokens.jira) {
|
|
180
|
+
servers.jira = {
|
|
181
|
+
type: 'http',
|
|
182
|
+
url: JIRA_MCP_URL,
|
|
183
|
+
headers: {
|
|
184
|
+
Authorization: `Bearer ${tokens.jira}`
|
|
140
185
|
}
|
|
141
186
|
};
|
|
142
187
|
}
|
|
143
188
|
return { mcpServers: servers };
|
|
144
189
|
};
|
|
145
190
|
exports.generateClaudeMCPServers = generateClaudeMCPServers;
|
|
146
|
-
const generateKiroMCPServers = (fraimKey,
|
|
191
|
+
const generateKiroMCPServers = (fraimKey, tokenInput) => {
|
|
192
|
+
const tokens = normalizeTokens(tokenInput);
|
|
147
193
|
const servers = {
|
|
148
194
|
git: {
|
|
149
195
|
command: "npx",
|
|
@@ -161,36 +207,67 @@ const generateKiroMCPServers = (fraimKey, githubToken) => {
|
|
|
161
207
|
}
|
|
162
208
|
}
|
|
163
209
|
};
|
|
164
|
-
|
|
165
|
-
if (githubToken) {
|
|
210
|
+
if (tokens.github) {
|
|
166
211
|
servers.github = {
|
|
167
|
-
url:
|
|
212
|
+
url: GITHUB_MCP_URL,
|
|
213
|
+
headers: {
|
|
214
|
+
Authorization: `Bearer ${tokens.github}`
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (tokens.gitlab) {
|
|
219
|
+
servers.gitlab = {
|
|
220
|
+
url: GITLAB_MCP_URL,
|
|
221
|
+
headers: {
|
|
222
|
+
Authorization: `Bearer ${tokens.gitlab}`
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
if (tokens.jira) {
|
|
227
|
+
servers.jira = {
|
|
228
|
+
url: JIRA_MCP_URL,
|
|
168
229
|
headers: {
|
|
169
|
-
Authorization: `Bearer ${
|
|
230
|
+
Authorization: `Bearer ${tokens.jira}`
|
|
170
231
|
}
|
|
171
232
|
};
|
|
172
233
|
}
|
|
173
234
|
return { mcpServers: servers };
|
|
174
235
|
};
|
|
175
236
|
exports.generateKiroMCPServers = generateKiroMCPServers;
|
|
176
|
-
const generateCodexMCPServers = (fraimKey,
|
|
237
|
+
const generateCodexMCPServers = (fraimKey, tokenInput) => {
|
|
238
|
+
const tokens = normalizeTokens(tokenInput);
|
|
177
239
|
const escapedFraimKey = escapeTomlString(fraimKey);
|
|
178
|
-
const escapedGithubToken = escapeTomlString(githubToken);
|
|
179
240
|
let config = `
|
|
180
241
|
[mcp_servers.git]
|
|
181
242
|
command = "npx"
|
|
182
|
-
args = ["-y", "@cyanheads/git-mcp-server"]
|
|
243
|
+
args = ["-y", "@cyanheads/git-mcp-server"]
|
|
183
244
|
`;
|
|
184
|
-
|
|
185
|
-
|
|
245
|
+
if (tokens.github) {
|
|
246
|
+
const escapedGithubToken = escapeTomlString(tokens.github);
|
|
186
247
|
config += `
|
|
187
248
|
[mcp_servers.github]
|
|
188
|
-
url = "
|
|
249
|
+
url = "${GITHUB_MCP_URL}"
|
|
189
250
|
http_headers = { Authorization = "Bearer ${escapedGithubToken}" }
|
|
190
251
|
`;
|
|
191
252
|
}
|
|
192
|
-
|
|
193
|
-
|
|
253
|
+
if (tokens.gitlab) {
|
|
254
|
+
const escapedGitLabToken = escapeTomlString(tokens.gitlab);
|
|
255
|
+
config += `
|
|
256
|
+
[mcp_servers.gitlab]
|
|
257
|
+
url = "${GITLAB_MCP_URL}"
|
|
258
|
+
http_headers = { Authorization = "Bearer ${escapedGitLabToken}" }
|
|
259
|
+
`;
|
|
260
|
+
}
|
|
261
|
+
if (tokens.jira) {
|
|
262
|
+
const escapedJiraToken = escapeTomlString(tokens.jira);
|
|
263
|
+
config += `
|
|
264
|
+
[mcp_servers.jira]
|
|
265
|
+
url = "${JIRA_MCP_URL}"
|
|
266
|
+
http_headers = { Authorization = "Bearer ${escapedJiraToken}" }
|
|
267
|
+
`;
|
|
268
|
+
}
|
|
269
|
+
config += `
|
|
270
|
+
[mcp_servers.playwright]
|
|
194
271
|
command = "npx"
|
|
195
272
|
args = ["-y", "@playwright/mcp"]
|
|
196
273
|
|
|
@@ -205,7 +282,8 @@ FRAIM_REMOTE_URL = "https://fraim.wellnessatwork.me"
|
|
|
205
282
|
};
|
|
206
283
|
exports.generateCodexMCPServers = generateCodexMCPServers;
|
|
207
284
|
/** VS Code uses "servers" key and requires type: "stdio" for stdio servers */
|
|
208
|
-
const generateVSCodeMCPServers = (fraimKey,
|
|
285
|
+
const generateVSCodeMCPServers = (fraimKey, tokenInput) => {
|
|
286
|
+
const tokens = normalizeTokens(tokenInput);
|
|
209
287
|
const servers = {
|
|
210
288
|
git: {
|
|
211
289
|
type: 'stdio',
|
|
@@ -226,19 +304,38 @@ const generateVSCodeMCPServers = (fraimKey, githubToken) => {
|
|
|
226
304
|
}
|
|
227
305
|
}
|
|
228
306
|
};
|
|
229
|
-
if (
|
|
307
|
+
if (tokens.github) {
|
|
230
308
|
servers.github = {
|
|
231
309
|
type: 'http',
|
|
232
|
-
url:
|
|
310
|
+
url: GITHUB_MCP_URL,
|
|
233
311
|
headers: {
|
|
234
|
-
Authorization: `Bearer ${
|
|
312
|
+
Authorization: `Bearer ${tokens.github}`
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
if (tokens.gitlab) {
|
|
317
|
+
servers.gitlab = {
|
|
318
|
+
type: 'http',
|
|
319
|
+
url: GITLAB_MCP_URL,
|
|
320
|
+
headers: {
|
|
321
|
+
Authorization: `Bearer ${tokens.gitlab}`
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
if (tokens.jira) {
|
|
326
|
+
servers.jira = {
|
|
327
|
+
type: 'http',
|
|
328
|
+
url: JIRA_MCP_URL,
|
|
329
|
+
headers: {
|
|
330
|
+
Authorization: `Bearer ${tokens.jira}`
|
|
235
331
|
}
|
|
236
332
|
};
|
|
237
333
|
}
|
|
238
334
|
return { servers };
|
|
239
335
|
};
|
|
240
336
|
exports.generateVSCodeMCPServers = generateVSCodeMCPServers;
|
|
241
|
-
const generateWindsurfMCPServers = (fraimKey,
|
|
337
|
+
const generateWindsurfMCPServers = (fraimKey, tokenInput) => {
|
|
338
|
+
const tokens = normalizeTokens(tokenInput);
|
|
242
339
|
const servers = {
|
|
243
340
|
git: {
|
|
244
341
|
command: "npx",
|
|
@@ -256,33 +353,50 @@ const generateWindsurfMCPServers = (fraimKey, githubToken) => {
|
|
|
256
353
|
}
|
|
257
354
|
}
|
|
258
355
|
};
|
|
259
|
-
|
|
260
|
-
if (githubToken) {
|
|
356
|
+
if (tokens.github) {
|
|
261
357
|
servers.github = {
|
|
262
358
|
command: "npx",
|
|
263
|
-
args: ["-y", "@modelcontextprotocol/server-fetch",
|
|
359
|
+
args: ["-y", "@modelcontextprotocol/server-fetch", GITHUB_MCP_URL],
|
|
360
|
+
env: {
|
|
361
|
+
GITHUB_TOKEN: tokens.github
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
if (tokens.gitlab) {
|
|
366
|
+
servers.gitlab = {
|
|
367
|
+
command: 'npx',
|
|
368
|
+
args: ['-y', '@modelcontextprotocol/server-fetch', GITLAB_MCP_URL],
|
|
369
|
+
env: {
|
|
370
|
+
GITLAB_TOKEN: tokens.gitlab
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
if (tokens.jira) {
|
|
375
|
+
servers.jira = {
|
|
376
|
+
command: 'npx',
|
|
377
|
+
args: ['-y', '@modelcontextprotocol/server-fetch', JIRA_MCP_URL],
|
|
264
378
|
env: {
|
|
265
|
-
|
|
379
|
+
JIRA_TOKEN: tokens.jira
|
|
266
380
|
}
|
|
267
381
|
};
|
|
268
382
|
}
|
|
269
383
|
return { mcpServers: servers };
|
|
270
384
|
};
|
|
271
385
|
exports.generateWindsurfMCPServers = generateWindsurfMCPServers;
|
|
272
|
-
const generateMCPConfig = (configType, fraimKey,
|
|
386
|
+
const generateMCPConfig = (configType, fraimKey, tokenInput) => {
|
|
273
387
|
switch (configType) {
|
|
274
388
|
case 'standard':
|
|
275
|
-
return (0, exports.generateStandardMCPServers)(fraimKey,
|
|
389
|
+
return (0, exports.generateStandardMCPServers)(fraimKey, tokenInput);
|
|
276
390
|
case 'claude':
|
|
277
|
-
return (0, exports.generateClaudeMCPServers)(fraimKey,
|
|
391
|
+
return (0, exports.generateClaudeMCPServers)(fraimKey, tokenInput);
|
|
278
392
|
case 'kiro':
|
|
279
|
-
return (0, exports.generateKiroMCPServers)(fraimKey,
|
|
393
|
+
return (0, exports.generateKiroMCPServers)(fraimKey, tokenInput);
|
|
280
394
|
case 'vscode':
|
|
281
|
-
return (0, exports.generateVSCodeMCPServers)(fraimKey,
|
|
395
|
+
return (0, exports.generateVSCodeMCPServers)(fraimKey, tokenInput);
|
|
282
396
|
case 'codex':
|
|
283
|
-
return (0, exports.generateCodexMCPServers)(fraimKey,
|
|
397
|
+
return (0, exports.generateCodexMCPServers)(fraimKey, tokenInput);
|
|
284
398
|
case 'windsurf':
|
|
285
|
-
return (0, exports.generateWindsurfMCPServers)(fraimKey,
|
|
399
|
+
return (0, exports.generateWindsurfMCPServers)(fraimKey, tokenInput);
|
|
286
400
|
default:
|
|
287
401
|
throw new Error(`Unsupported config type: ${configType}`);
|
|
288
402
|
}
|
|
@@ -44,6 +44,14 @@ const isValidTokenFormat = (token, type) => {
|
|
|
44
44
|
if (type === 'github') {
|
|
45
45
|
return token.startsWith('ghp_') || token.startsWith('github_pat_');
|
|
46
46
|
}
|
|
47
|
+
if (type === 'gitlab') {
|
|
48
|
+
// GitLab PATs commonly use glpat- prefix, but keep a permissive fallback for self-managed/token variants.
|
|
49
|
+
return token.startsWith('glpat-') || token.length >= 20;
|
|
50
|
+
}
|
|
51
|
+
if (type === 'jira') {
|
|
52
|
+
// Jira API tokens do not have a stable prefix; enforce a minimum length.
|
|
53
|
+
return token.length >= 20;
|
|
54
|
+
}
|
|
47
55
|
return false;
|
|
48
56
|
};
|
|
49
57
|
exports.isValidTokenFormat = isValidTokenFormat;
|
|
@@ -78,6 +78,15 @@ function detectPlatformFromUrl(url) {
|
|
|
78
78
|
confidence: 'high'
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
|
+
// GitLab detection
|
|
82
|
+
if (normalizedUrl.includes('gitlab.com') || normalizedUrl.includes('gitlab.')) {
|
|
83
|
+
const repository = extractGitLabInfo(url);
|
|
84
|
+
return {
|
|
85
|
+
provider: 'gitlab',
|
|
86
|
+
repository,
|
|
87
|
+
confidence: 'high'
|
|
88
|
+
};
|
|
89
|
+
}
|
|
81
90
|
// ADO detection
|
|
82
91
|
if (normalizedUrl.includes('dev.azure.com') ||
|
|
83
92
|
normalizedUrl.includes('visualstudio.com') ||
|
|
@@ -117,6 +126,35 @@ function extractGitHubInfo(url) {
|
|
|
117
126
|
defaultBranch: 'main'
|
|
118
127
|
};
|
|
119
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Extract GitLab repository information from URL
|
|
131
|
+
*/
|
|
132
|
+
function extractGitLabInfo(url) {
|
|
133
|
+
// GitLab URL formats:
|
|
134
|
+
// HTTPS: https://gitlab.com/group/subgroup/repo.git
|
|
135
|
+
// SSH: git@gitlab.com:group/subgroup/repo.git
|
|
136
|
+
const match = url.match(/gitlab[^\/:]*[\/:]([^?\s#]+?)(?:\.git)?$/i);
|
|
137
|
+
if (match) {
|
|
138
|
+
const projectPath = match[1].replace(/^\/+/, '');
|
|
139
|
+
const segments = projectPath.split('/').filter(Boolean);
|
|
140
|
+
const name = segments[segments.length - 1];
|
|
141
|
+
const namespace = segments.slice(0, -1).join('/');
|
|
142
|
+
return {
|
|
143
|
+
provider: 'gitlab',
|
|
144
|
+
namespace: namespace || undefined,
|
|
145
|
+
name,
|
|
146
|
+
projectPath,
|
|
147
|
+
url,
|
|
148
|
+
defaultBranch: 'main'
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// Fallback - just mark as GitLab
|
|
152
|
+
return {
|
|
153
|
+
provider: 'gitlab',
|
|
154
|
+
url,
|
|
155
|
+
defaultBranch: 'main'
|
|
156
|
+
};
|
|
157
|
+
}
|
|
120
158
|
/**
|
|
121
159
|
* Extract ADO repository information from URL
|
|
122
160
|
*/
|
|
@@ -178,6 +216,13 @@ function validateRepositoryConfig(config) {
|
|
|
178
216
|
if (!config.name)
|
|
179
217
|
errors.push('ADO repository name is required');
|
|
180
218
|
}
|
|
219
|
+
if (config.provider === 'gitlab') {
|
|
220
|
+
const hasProjectPath = typeof config.projectPath === 'string' && config.projectPath.length > 0;
|
|
221
|
+
const hasNamespaceAndName = typeof config.namespace === 'string' && config.namespace.length > 0 && typeof config.name === 'string' && config.name.length > 0;
|
|
222
|
+
if (!hasProjectPath && !hasNamespaceAndName) {
|
|
223
|
+
errors.push('GitLab repository requires projectPath or namespace + name');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
181
226
|
return {
|
|
182
227
|
valid: errors.length === 0,
|
|
183
228
|
errors
|
|
@@ -46,6 +46,9 @@ function loadFraimConfig() {
|
|
|
46
46
|
...(config.customizations || {})
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
|
+
if (config.issueTracking && typeof config.issueTracking === 'object') {
|
|
50
|
+
mergedConfig.issueTracking = config.issueTracking;
|
|
51
|
+
}
|
|
49
52
|
// Add optional workflow-driven fields only if they exist in the config
|
|
50
53
|
if (config.compliance) {
|
|
51
54
|
mergedConfig.compliance = config.compliance;
|
|
@@ -92,7 +95,7 @@ function getRepositoryInfo(config) {
|
|
|
92
95
|
// Prefer new repository config
|
|
93
96
|
if (config.repository) {
|
|
94
97
|
return {
|
|
95
|
-
owner: config.repository.owner || config.repository.organization,
|
|
98
|
+
owner: config.repository.owner || config.repository.organization || config.repository.namespace,
|
|
96
99
|
name: config.repository.name,
|
|
97
100
|
provider: config.repository.provider,
|
|
98
101
|
defaultBranch: config.repository.defaultBranch
|
|
@@ -7,7 +7,11 @@ exports.detectProvider = detectProvider;
|
|
|
7
7
|
function detectProvider(url) {
|
|
8
8
|
if (!url)
|
|
9
9
|
return 'github';
|
|
10
|
-
|
|
10
|
+
const normalized = url.toLowerCase();
|
|
11
|
+
if (normalized.includes('gitlab.com') || normalized.includes('gitlab.')) {
|
|
12
|
+
return 'gitlab';
|
|
13
|
+
}
|
|
14
|
+
if (normalized.includes('dev.azure.com') || normalized.includes('visualstudio.com')) {
|
|
11
15
|
return 'ado';
|
|
12
16
|
}
|
|
13
17
|
return 'github';
|