fraim-framework 2.0.77 → 2.0.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -233,6 +233,34 @@ npm install -g fraim-framework
233
233
  fraim setup # Complete setup with IDE configuration
234
234
  ```
235
235
 
236
+ The setup command supports three modes:
237
+
238
+ **Conversational Mode**: AI workflows only, no platform integration required
239
+ ```bash
240
+ fraim setup --key=<your-fraim-key>
241
+ # Select "Conversational Mode" when prompted
242
+ ```
243
+
244
+ **Integrated Mode**: Single platform for both code hosting and issue tracking
245
+ ```bash
246
+ fraim setup --key=<your-fraim-key>
247
+ # Select "Integrated Mode" when prompted
248
+ # Choose platform: GitHub, Azure DevOps, or GitLab
249
+ ```
250
+
251
+ **Split Mode**: Separate platforms for code hosting and issue tracking
252
+ ```bash
253
+ fraim setup --key=<your-fraim-key>
254
+ # Select "Split Mode" when prompted
255
+ # Choose code repository platform: GitHub, Azure DevOps, or GitLab
256
+ # Choose issue tracking platform: GitHub, Azure DevOps, GitLab, or Jira
257
+ ```
258
+
259
+ Common Split mode combinations:
260
+ - GitHub (code) + Jira (issues)
261
+ - GitLab (code) + Jira (issues)
262
+ - Azure DevOps (code) + GitHub (issues)
263
+
236
264
  ### **🔧 Additional Commands**
237
265
 
238
266
  After initial setup, you can use these commands:
@@ -244,6 +272,12 @@ fraim add-ide --ide antigravity # Configure Gemini Antigravity
244
272
  fraim add-ide --all # Configure all detected IDEs
245
273
  fraim add-ide --list # List supported IDEs
246
274
 
275
+ # Add platform integrations to existing setup
276
+ fraim setup --github # Add GitHub integration
277
+ fraim setup --ado # Add Azure DevOps integration
278
+ fraim setup --gitlab # Add GitLab integration
279
+ fraim setup --jira # Add Jira integration
280
+
247
281
  # Project initialization
248
282
  fraim init-project # Initialize FRAIM in current project
249
283
 
@@ -255,7 +289,49 @@ fraim doctor # Diagnose configuration issues
255
289
  fraim sync # Sync latest workflows and rules
256
290
  ```
257
291
 
258
- **💡 Pro Tip**: Use `fraim add-ide` when you install a new IDE after initial setup. It reuses your existing FRAIM and GitHub keys, making it much faster than running full setup again.
292
+ **💡 Pro Tip**: Use `fraim add-ide` when you install a new IDE after initial setup. It reuses your existing FRAIM and platform tokens, making it much faster than running full setup again.
293
+
294
+ ### **🔧 Jira Integration Setup**
295
+
296
+ FRAIM uses the official Model Context Protocol (MCP) server for Jira integration. The setup command automatically configures the correct format.
297
+
298
+ **Jira API Token Requirements**:
299
+ 1. Go to https://id.atlassian.com/manage-profile/security/api-tokens
300
+ 2. Click "Create API token"
301
+ 3. Give it a name (e.g., "FRAIM Integration")
302
+ 4. Copy the token (starts with ATATT3...)
303
+ 5. Use this token during `fraim setup`
304
+
305
+ **Correct MCP Configuration** (automatically generated):
306
+ ```json
307
+ {
308
+ "jira": {
309
+ "command": "npx",
310
+ "args": ["-y", "@modelcontextprotocol/server-jira"],
311
+ "env": {
312
+ "JIRA_API_TOKEN": "your-token-here"
313
+ }
314
+ }
315
+ }
316
+ ```
317
+
318
+ **⚠️ Common Issues**:
319
+ - **Old configuration format**: If you see `https://mcp.atlassian.com/v1/sse` in your config, this is incorrect. Run `fraim setup --jira` to update.
320
+ - **Token format**: Jira API tokens typically start with `ATATT3`. If your token doesn't match this format, verify you created an API token (not a personal access token).
321
+ - **First run slow**: The first time `npx` runs the Jira MCP server, it downloads the package. This is normal and only happens once.
322
+
323
+ **Troubleshooting**:
324
+ ```bash
325
+ # Test Jira MCP connection
326
+ fraim test-mcp
327
+
328
+ # Reconfigure Jira integration
329
+ fraim setup --jira
330
+
331
+ # Check configuration
332
+ cat ~/.kiro/settings/mcp.json # For Kiro IDE
333
+ cat ~/Library/Application\ Support/Claude/claude_desktop_config.json # For Claude Desktop (macOS)
334
+ ```
259
335
 
260
336
 
261
337
  ## 🌟 **Why FRAIM is the Future**
package/bin/fraim-mcp.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * FRAIM MCP Server - Smart Entry Point
package/bin/fraim.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * FRAIM Framework CLI Entry Point
@@ -9,12 +9,12 @@ const chalk_1 = __importDefault(require("chalk"));
9
9
  const prompts_1 = __importDefault(require("prompts"));
10
10
  const fs_1 = __importDefault(require("fs"));
11
11
  const path_1 = __importDefault(require("path"));
12
- const os_1 = __importDefault(require("os"));
13
12
  const ide_detector_1 = require("../setup/ide-detector");
14
13
  const mcp_config_generator_1 = require("../setup/mcp-config-generator");
15
14
  const codex_local_config_1 = require("../setup/codex-local-config");
15
+ const script_sync_utils_1 = require("../utils/script-sync-utils");
16
16
  const loadGlobalConfig = () => {
17
- const globalConfigPath = path_1.default.join(os_1.default.homedir(), '.fraim', 'config.json');
17
+ const globalConfigPath = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'config.json');
18
18
  if (!fs_1.default.existsSync(globalConfigPath)) {
19
19
  return null;
20
20
  }
@@ -23,7 +23,10 @@ const loadGlobalConfig = () => {
23
23
  return {
24
24
  fraimKey: config.apiKey,
25
25
  githubToken: config.tokens?.github || config.githubToken, // Support both old and new format
26
- mode: config.mode
26
+ gitlabToken: config.tokens?.gitlab,
27
+ jiraToken: config.tokens?.jira,
28
+ mode: config.mode,
29
+ jiraConfig: config.jiraConfig
27
30
  };
28
31
  }
29
32
  catch (e) {
@@ -75,7 +78,7 @@ const promptForGitHubToken = async (isConversationalMode = false) => {
75
78
  return tokenResponse.token;
76
79
  };
77
80
  const saveGitHubTokenToConfig = (githubToken) => {
78
- const globalConfigPath = path_1.default.join(os_1.default.homedir(), '.fraim', 'config.json');
81
+ const globalConfigPath = path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'config.json');
79
82
  if (fs_1.default.existsSync(globalConfigPath)) {
80
83
  try {
81
84
  const config = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
@@ -95,7 +98,7 @@ const saveGitHubTokenToConfig = (githubToken) => {
95
98
  }
96
99
  };
97
100
  exports.saveGitHubTokenToConfig = saveGitHubTokenToConfig;
98
- const configureIDEMCP = async (ide, fraimKey, githubToken) => {
101
+ const configureIDEMCP = async (ide, fraimKey, tokens, jiraConfig) => {
99
102
  const configPath = (0, ide_detector_1.expandPath)(ide.configPath);
100
103
  console.log(chalk_1.default.blue(`🔧 Configuring ${ide.name}...`));
101
104
  // Create backup if config exists
@@ -112,10 +115,11 @@ const configureIDEMCP = async (ide, fraimKey, githubToken) => {
112
115
  }
113
116
  let existingConfig = {};
114
117
  let existingMCPServers = {};
118
+ const serversKey = ide.configType === 'vscode' ? 'servers' : 'mcpServers';
115
119
  if (fs_1.default.existsSync(configPath) && ide.configFormat === 'json') {
116
120
  try {
117
121
  existingConfig = JSON.parse(fs_1.default.readFileSync(configPath, 'utf8'));
118
- existingMCPServers = existingConfig.mcpServers || {};
122
+ existingMCPServers = existingConfig[serversKey] || {};
119
123
  console.log(chalk_1.default.gray(` 📋 Found existing config with ${Object.keys(existingMCPServers).length} MCP servers`));
120
124
  }
121
125
  catch (e) {
@@ -129,8 +133,8 @@ const configureIDEMCP = async (ide, fraimKey, githubToken) => {
129
133
  existingTomlContent = fs_1.default.readFileSync(configPath, 'utf8');
130
134
  console.log(chalk_1.default.gray(` 📋 Found existing TOML config`));
131
135
  }
132
- const newTomlContent = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, githubToken);
133
- const serversToAdd = ['fraim', 'git', 'github', 'playwright'];
136
+ const newTomlContent = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokens, jiraConfig);
137
+ const serversToAdd = ['fraim', 'git', 'github', 'gitlab', 'jira', 'playwright'];
134
138
  const mergeResult = (0, mcp_config_generator_1.mergeTomlMCPServers)(existingTomlContent, newTomlContent, serversToAdd);
135
139
  fs_1.default.writeFileSync(configPath, mergeResult.content);
136
140
  mergeResult.addedServers.forEach(server => {
@@ -144,9 +148,9 @@ const configureIDEMCP = async (ide, fraimKey, githubToken) => {
144
148
  });
145
149
  }
146
150
  else {
147
- // Handle JSON format
148
- const newConfig = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, githubToken);
149
- const newMCPServers = newConfig.mcpServers || {};
151
+ // For JSON configs - intelligent merging
152
+ const newConfig = (0, mcp_config_generator_1.generateMCPConfig)(ide.configType, fraimKey, tokens, jiraConfig);
153
+ const newMCPServers = newConfig[serversKey] || newConfig.mcpServers || {};
150
154
  // Merge MCP servers intelligently
151
155
  const mergedMCPServers = { ...existingMCPServers };
152
156
  const addedServers = [];
@@ -164,7 +168,7 @@ const configureIDEMCP = async (ide, fraimKey, githubToken) => {
164
168
  const mergedConfig = {
165
169
  ...existingConfig,
166
170
  ...newConfig,
167
- mcpServers: mergedMCPServers
171
+ [serversKey]: mergedMCPServers
168
172
  };
169
173
  // Write updated config
170
174
  fs_1.default.writeFileSync(configPath, JSON.stringify(mergedConfig, null, 2));
@@ -197,7 +201,7 @@ const listSupportedIDEs = () => {
197
201
  console.log(chalk_1.default.yellow('💡 Use "fraim add-ide --ide <name>" to configure a specific IDE'));
198
202
  console.log(chalk_1.default.yellow(' Example: fraim add-ide --ide claude'));
199
203
  };
200
- const promptForIDESelection = async (availableIDEs, githubToken) => {
204
+ const promptForIDESelection = async (availableIDEs, tokens) => {
201
205
  console.log(chalk_1.default.green(`✅ Found ${availableIDEs.length} IDEs that can be configured:\n`));
202
206
  availableIDEs.forEach((ide, index) => {
203
207
  const configExists = fs_1.default.existsSync((0, ide_detector_1.expandPath)(ide.configPath));
@@ -208,10 +212,16 @@ const promptForIDESelection = async (availableIDEs, githubToken) => {
208
212
  console.log(chalk_1.default.blue('\nFRAIM will add these MCP servers:'));
209
213
  console.log(chalk_1.default.gray(' • fraim (workflows and AI management)'));
210
214
  console.log(chalk_1.default.gray(' • git (version control integration)'));
211
- if (githubToken) {
212
- console.log(chalk_1.default.gray(' github (GitHub API access)'));
215
+ if (tokens?.github) {
216
+ console.log(chalk_1.default.gray(' - github (GitHub API access)'));
213
217
  }
214
- console.log(chalk_1.default.gray(' • playwright (browser automation)'));
218
+ if (tokens?.gitlab) {
219
+ console.log(chalk_1.default.gray(' - gitlab (GitLab API access)'));
220
+ }
221
+ if (tokens?.jira) {
222
+ console.log(chalk_1.default.gray(' - jira (Jira issue tracking)'));
223
+ }
224
+ console.log(chalk_1.default.gray(' - playwright (browser automation)'));
215
225
  const response = await (0, prompts_1.default)({
216
226
  type: 'text',
217
227
  name: 'selection',
@@ -250,12 +260,18 @@ const runAddIDE = async (options) => {
250
260
  process.exit(1);
251
261
  }
252
262
  let githubToken = globalConfig.githubToken;
263
+ const platformTokens = {
264
+ github: globalConfig.githubToken,
265
+ gitlab: globalConfig.gitlabToken,
266
+ jira: globalConfig.jiraToken
267
+ };
253
268
  const isConversationalMode = globalConfig.mode === 'conversational';
254
- if (!githubToken && !isConversationalMode) {
269
+ if (!githubToken && !platformTokens.gitlab && !platformTokens.jira && !isConversationalMode) {
255
270
  console.log(chalk_1.default.yellow('⚠️ No GitHub token found in configuration.'));
256
271
  githubToken = await promptForGitHubToken(false);
257
272
  if (githubToken) {
258
273
  saveGitHubTokenToConfig(githubToken);
274
+ platformTokens.github = githubToken;
259
275
  }
260
276
  }
261
277
  if (isConversationalMode && !githubToken) {
@@ -295,7 +311,7 @@ const runAddIDE = async (options) => {
295
311
  }
296
312
  else {
297
313
  // Interactive selection
298
- idesToConfigure = await promptForIDESelection(detectedIDEs, githubToken);
314
+ idesToConfigure = await promptForIDESelection(detectedIDEs, platformTokens);
299
315
  }
300
316
  if (idesToConfigure.length === 0) {
301
317
  console.log(chalk_1.default.yellow('⚠️ No IDEs selected for configuration.'));
@@ -308,7 +324,7 @@ const runAddIDE = async (options) => {
308
324
  };
309
325
  for (const ide of idesToConfigure) {
310
326
  try {
311
- await configureIDEMCP(ide, globalConfig.fraimKey, githubToken || '');
327
+ await configureIDEMCP(ide, globalConfig.fraimKey, platformTokens, globalConfig.jiraConfig);
312
328
  results.successful.push(ide.name);
313
329
  }
314
330
  catch (error) {
@@ -341,7 +357,7 @@ const runAddIDE = async (options) => {
341
357
  exports.runAddIDE = runAddIDE;
342
358
  exports.addIDECommand = new commander_1.Command('add-ide')
343
359
  .description('Add FRAIM configuration to additional IDEs')
344
- .option('--ide <name>', 'Configure specific IDE (claude, antigravity, kiro, cursor, vscode, codex, windsurf)')
360
+ .option('--ide <name>', 'Configure specific IDE (claude, claude-code, antigravity, kiro, cursor, vscode, codex, windsurf)')
345
361
  .option('--all', 'Configure all detected IDEs')
346
362
  .option('--list', 'List all supported IDEs and their detection status')
347
363
  .action(exports.runAddIDE);
@@ -75,12 +75,19 @@ const runInitProject = async () => {
75
75
  owner: detection.repository.owner,
76
76
  name: detection.repository.name
77
77
  }
78
- : {
79
- provider: 'ado',
80
- organization: detection.repository.organization,
81
- project: detection.repository.project,
82
- name: detection.repository.name
83
- };
78
+ : detection.provider === 'ado'
79
+ ? {
80
+ provider: 'ado',
81
+ organization: detection.repository.organization,
82
+ project: detection.repository.project,
83
+ name: detection.repository.name
84
+ }
85
+ : {
86
+ provider: 'gitlab',
87
+ namespace: detection.repository.namespace,
88
+ name: detection.repository.name,
89
+ projectPath: detection.repository.projectPath
90
+ };
84
91
  config = {
85
92
  version: (0, version_utils_1.getFraimVersion)(),
86
93
  project: {
@@ -99,6 +106,10 @@ const runInitProject = async () => {
99
106
  console.log(chalk_1.default.gray(` Project: ${detection.repository.project}`));
100
107
  console.log(chalk_1.default.gray(` Repository: ${detection.repository.name}`));
101
108
  }
109
+ else if (detection.provider === 'gitlab') {
110
+ console.log(chalk_1.default.gray(` Namespace: ${detection.repository.namespace || '(none)'}`));
111
+ console.log(chalk_1.default.gray(` Repository: ${detection.repository.name}`));
112
+ }
102
113
  }
103
114
  else {
104
115
  config = {