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.
@@ -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, githubToken) => {
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
- // Only add GitHub server if token is provided
104
- if (githubToken) {
115
+ if (tokens.github) {
105
116
  servers.github = {
106
- serverUrl: "https://api.githubcopilot.com/mcp/",
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 ${githubToken}`
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, githubToken) => {
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
- // Only add GitHub server if token is provided
134
- if (githubToken) {
161
+ if (tokens.github) {
135
162
  servers.github = {
136
163
  type: "http",
137
- url: "https://api.githubcopilot.com/mcp/",
164
+ url: GITHUB_MCP_URL,
138
165
  headers: {
139
- Authorization: `Bearer ${githubToken}`
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, githubToken) => {
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
- // Only add GitHub server if token is provided
165
- if (githubToken) {
210
+ if (tokens.github) {
166
211
  servers.github = {
167
- url: "https://api.githubcopilot.com/mcp/",
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 ${githubToken}`
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, githubToken) => {
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
- // Only add GitHub server if token is provided
185
- if (githubToken) {
245
+ if (tokens.github) {
246
+ const escapedGithubToken = escapeTomlString(tokens.github);
186
247
  config += `
187
248
  [mcp_servers.github]
188
- url = "https://api.githubcopilot.com/mcp/"
249
+ url = "${GITHUB_MCP_URL}"
189
250
  http_headers = { Authorization = "Bearer ${escapedGithubToken}" }
190
251
  `;
191
252
  }
192
- config += `
193
- [mcp_servers.playwright]
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, githubToken) => {
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 (githubToken) {
307
+ if (tokens.github) {
230
308
  servers.github = {
231
309
  type: 'http',
232
- url: 'https://api.githubcopilot.com/mcp/',
310
+ url: GITHUB_MCP_URL,
233
311
  headers: {
234
- Authorization: `Bearer ${githubToken}`
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, githubToken) => {
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
- // Only add GitHub server if token is provided
260
- if (githubToken) {
356
+ if (tokens.github) {
261
357
  servers.github = {
262
358
  command: "npx",
263
- args: ["-y", "@modelcontextprotocol/server-fetch", "https://api.githubcopilot.com/mcp/"],
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
- GITHUB_TOKEN: githubToken
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, githubToken) => {
386
+ const generateMCPConfig = (configType, fraimKey, tokenInput) => {
273
387
  switch (configType) {
274
388
  case 'standard':
275
- return (0, exports.generateStandardMCPServers)(fraimKey, githubToken);
389
+ return (0, exports.generateStandardMCPServers)(fraimKey, tokenInput);
276
390
  case 'claude':
277
- return (0, exports.generateClaudeMCPServers)(fraimKey, githubToken);
391
+ return (0, exports.generateClaudeMCPServers)(fraimKey, tokenInput);
278
392
  case 'kiro':
279
- return (0, exports.generateKiroMCPServers)(fraimKey, githubToken);
393
+ return (0, exports.generateKiroMCPServers)(fraimKey, tokenInput);
280
394
  case 'vscode':
281
- return (0, exports.generateVSCodeMCPServers)(fraimKey, githubToken);
395
+ return (0, exports.generateVSCodeMCPServers)(fraimKey, tokenInput);
282
396
  case 'codex':
283
- return (0, exports.generateCodexMCPServers)(fraimKey, githubToken);
397
+ return (0, exports.generateCodexMCPServers)(fraimKey, tokenInput);
284
398
  case 'windsurf':
285
- return (0, exports.generateWindsurfMCPServers)(fraimKey, githubToken);
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
- if (url.includes('dev.azure.com') || url.includes('visualstudio.com')) {
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';