fraim-framework 2.0.83 → 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 CHANGED
@@ -228,11 +228,24 @@ R - Retrospectives: Continuous learning from experience
228
228
  **Why Git Bash on Windows?** All FRAIM scripts use Unix-style paths and Bash commands. Git Bash ensures consistent behavior across platforms.
229
229
 
230
230
  ### **Install & Initialize**
231
+
232
+ **Recommended: Use npx (no installation needed)**
233
+ ```bash
234
+ npx fraim-framework@latest setup --key=<your-fraim-key>
235
+
236
+ # Optional: Create alias for convenience
237
+ echo 'alias fraim="npx fraim-framework"' >> ~/.bashrc
238
+ source ~/.bashrc
239
+ ```
240
+
241
+ **Alternative: Global install**
231
242
  ```bash
232
243
  npm install -g fraim-framework
233
- fraim setup # Complete setup with IDE configuration
244
+ fraim setup --key=<your-fraim-key>
234
245
  ```
235
246
 
247
+ > **šŸ’” Why npx?** Works with any Node version (16+), no conflicts when switching Node versions, always uses correct dependencies, and identical functionality to global install. Perfect for users with nvm, volta, or multiple Node versions.
248
+
236
249
  The setup command supports three modes:
237
250
 
238
251
  **Conversational Mode**: AI workflows only, no platform integration required
@@ -282,7 +295,7 @@ fraim setup --jira # Add Jira integration
282
295
  fraim init-project # Initialize FRAIM in current project
283
296
 
284
297
  # Testing and validation
285
- fraim test-mcp # Test MCP server connections
298
+ fraim doctor --test-mcp # Test MCP server connections
286
299
  fraim doctor # Diagnose configuration issues
287
300
 
288
301
  # Sync and maintenance
@@ -325,7 +338,7 @@ FRAIM uses the official Model Context Protocol (MCP) server for Jira integration
325
338
  **Troubleshooting**:
326
339
  ```bash
327
340
  # Test Jira MCP connection
328
- fraim test-mcp
341
+ fraim doctor --test-mcp
329
342
 
330
343
  # Reconfigure Jira integration
331
344
  fraim setup --jira
@@ -418,7 +418,7 @@ const runAddIDE = async (options) => {
418
418
  console.log(chalk_1.default.blue('\nšŸ”„ Next steps:'));
419
419
  console.log(chalk_1.default.cyan(' 1. Restart your configured IDEs'));
420
420
  console.log(chalk_1.default.cyan(' 2. Ask your AI agent: "list fraim workflows"'));
421
- console.log(chalk_1.default.blue('\nšŸ’” Use "fraim test-mcp" to verify the configuration.'));
421
+ console.log(chalk_1.default.blue('\nšŸ’” Use "fraim doctor --test-mcp" to verify the configuration.'));
422
422
  }
423
423
  };
424
424
  exports.runAddIDE = runAddIDE;
@@ -240,16 +240,20 @@ const runSetup = async (options) => {
240
240
  console.log(chalk_1.default.blue('šŸ“ Updating existing FRAIM configuration...\n'));
241
241
  try {
242
242
  const existingConfig = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
243
- fraimKey = existingConfig.apiKey;
243
+ // Allow updating FRAIM key even without provider changes
244
+ fraimKey = options.key || existingConfig.apiKey;
244
245
  // Now we can parse options with the FRAIM key
245
246
  const parsed = await parseLegacyOptions(options, fraimKey);
246
247
  requestedProviders = parsed.requestedProviders;
247
248
  providedTokens = parsed.providedTokens;
248
249
  providedConfigs = parsed.providedConfigs;
249
- // Only proceed with update if providers were requested
250
- if (requestedProviders.length === 0) {
251
- console.log(chalk_1.default.yellow('āš ļø No providers specified for update.'));
252
- console.log(chalk_1.default.gray('Use --github, --gitlab, --jira, etc. to add providers.'));
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.'));
253
257
  return;
254
258
  }
255
259
  mode = existingConfig.mode || 'integrated';
@@ -377,8 +381,9 @@ const runSetup = async (options) => {
377
381
  // Save global configuration
378
382
  console.log(chalk_1.default.blue('šŸ’¾ Saving global configuration...'));
379
383
  saveGlobalConfig(fraimKey, mode, tokens, configs);
380
- // Configure MCP servers (only on initial setup)
384
+ // Configure MCP servers
381
385
  if (!isUpdate) {
386
+ // Initial setup - configure all detected IDEs
382
387
  console.log(chalk_1.default.blue('\nšŸ”Œ Configuring MCP servers...'));
383
388
  // Convert to legacy format for MCP config generator
384
389
  const mcpTokens = {};
@@ -401,7 +406,39 @@ const runSetup = async (options) => {
401
406
  console.log(chalk_1.default.yellow('āš ļø MCP configuration encountered issues'));
402
407
  console.log(chalk_1.default.gray(' You can configure MCP manually or run setup again later\n'));
403
408
  }
404
- // Auto-run project init if we're in a git repo
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) {
405
442
  if ((0, platform_detection_1.isGitRepository)()) {
406
443
  console.log(chalk_1.default.blue('\nšŸ“ Git repository detected — initializing project...'));
407
444
  await (0, init_project_1.runInitProject)();
@@ -43,7 +43,6 @@ const doctor_1 = require("./commands/doctor");
43
43
  const list_1 = require("./commands/list");
44
44
  const setup_1 = require("./commands/setup");
45
45
  const init_project_1 = require("./commands/init-project");
46
- const test_mcp_1 = require("./commands/test-mcp");
47
46
  const add_ide_1 = require("./commands/add-ide");
48
47
  const add_provider_1 = require("./commands/add-provider");
49
48
  const override_1 = require("./commands/override");
@@ -80,7 +79,6 @@ program.addCommand(doctor_1.doctorCommand);
80
79
  program.addCommand(list_1.listCommand);
81
80
  program.addCommand(setup_1.setupCommand);
82
81
  program.addCommand(init_project_1.initProjectCommand);
83
- program.addCommand(test_mcp_1.testMCPCommand);
84
82
  program.addCommand(add_ide_1.addIDECommand);
85
83
  program.addCommand(add_provider_1.addProviderCommand);
86
84
  program.addCommand(override_1.overrideCommand);
@@ -38,6 +38,8 @@ exports.BASE_MCP_SERVERS = [
38
38
  buildServer: (fraimKey) => ({
39
39
  command: 'fraim-mcp',
40
40
  env: {
41
+ // Include API key for IDE configs (Codex, VSCode, etc.)
42
+ // The stdio-server will use this if set, otherwise falls back to ~/.fraim/config.json
41
43
  FRAIM_API_KEY: fraimKey,
42
44
  FRAIM_REMOTE_URL: 'https://fraim.wellnessatwork.me'
43
45
  }
@@ -203,12 +203,20 @@ const configureIDEMCP = async (ide, fraimKey, tokenInput, providerConfigs) => {
203
203
  // Merge MCP servers intelligently
204
204
  const mergedMCPServers = { ...existingMCPServers };
205
205
  const addedServers = [];
206
+ const updatedServers = [];
206
207
  const skippedServers = [];
208
+ // Servers that should always be updated (not skipped)
209
+ const alwaysUpdateServers = new Set(['fraim', 'github', 'gitlab', 'jira', 'ado', 'linear']);
207
210
  for (const [serverName, serverConfig] of Object.entries(newMCPServers)) {
208
211
  if (!existingMCPServers[serverName]) {
209
212
  mergedMCPServers[serverName] = serverConfig;
210
213
  addedServers.push(serverName);
211
214
  }
215
+ else if (alwaysUpdateServers.has(serverName)) {
216
+ // Always update these servers to ensure keys/tokens are current
217
+ mergedMCPServers[serverName] = serverConfig;
218
+ updatedServers.push(serverName);
219
+ }
212
220
  else {
213
221
  skippedServers.push(serverName);
214
222
  }
@@ -223,8 +231,11 @@ const configureIDEMCP = async (ide, fraimKey, tokenInput, providerConfigs) => {
223
231
  addedServers.forEach(server => {
224
232
  console.log(chalk_1.default.green(` āœ… Added ${server} MCP server`));
225
233
  });
234
+ updatedServers.forEach(server => {
235
+ console.log(chalk_1.default.blue(` šŸ”„ Updated ${server} MCP server`));
236
+ });
226
237
  skippedServers.forEach(server => {
227
- console.log(chalk_1.default.gray(` ā­ļø Skipped ${server} (already exists)`));
238
+ console.log(chalk_1.default.gray(` ā­ļø Skipped ${server} (already exists)`));
228
239
  });
229
240
  }
230
241
  console.log(chalk_1.default.green(`āœ… Updated ${configPath}`));
@@ -13,7 +13,11 @@ class WorkflowParser {
13
13
  static parse(filePath) {
14
14
  if (!(0, fs_1.existsSync)(filePath))
15
15
  return null;
16
- const content = (0, fs_1.readFileSync)(filePath, 'utf-8');
16
+ let content = (0, fs_1.readFileSync)(filePath, 'utf-8');
17
+ // Strip optional UTF-8 BOM so frontmatter regex remains stable across editors.
18
+ if (content.charCodeAt(0) === 0xfeff) {
19
+ content = content.slice(1);
20
+ }
17
21
  // Try to extract JSON Metadata (frontmatter)
18
22
  const metadataMatch = content.match(/^---\r?\n([\s\S]+?)\r?\n---/);
19
23
  if (metadataMatch) {
@@ -227,10 +227,11 @@ class FraimLocalMCPServer {
227
227
  this.pendingFallbackEvents = [];
228
228
  this.fallbackSummaryEmitted = false;
229
229
  this.remoteUrl = process.env.FRAIM_REMOTE_URL || 'https://fraim.wellnessatwork.me';
230
- this.apiKey = process.env.FRAIM_API_KEY || '';
230
+ this.apiKey = this.loadApiKey();
231
231
  this.localVersion = this.detectLocalVersion();
232
232
  if (!this.apiKey) {
233
- this.logError('āŒ FRAIM_API_KEY environment variable is required');
233
+ this.logError('āŒ FRAIM API key is required');
234
+ this.logError(' Set FRAIM_API_KEY environment variable or add apiKey to ~/.fraim/config.json');
234
235
  process.exit(1);
235
236
  }
236
237
  this.log('šŸš€ FRAIM Local MCP Server starting... [DEBUG-PROXY-V3]');
@@ -239,6 +240,33 @@ class FraimLocalMCPServer {
239
240
  this.log(`Local MCP version: ${this.localVersion}`);
240
241
  this.log(`šŸ” DEBUG BUILD: Machine detection v2 active`);
241
242
  }
243
+ /**
244
+ * Load API key from environment variable or user config file
245
+ * Priority: FRAIM_API_KEY env var > ~/.fraim/config.json
246
+ */
247
+ loadApiKey() {
248
+ // First try environment variable (for IDE MCP configs)
249
+ if (process.env.FRAIM_API_KEY) {
250
+ return process.env.FRAIM_API_KEY;
251
+ }
252
+ // Fallback to user config file
253
+ try {
254
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '';
255
+ if (!homeDir)
256
+ return '';
257
+ const configPath = (0, path_1.join)(homeDir, '.fraim', 'config.json');
258
+ if ((0, fs_1.existsSync)(configPath)) {
259
+ const config = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf8'));
260
+ if (config.apiKey) {
261
+ return config.apiKey;
262
+ }
263
+ }
264
+ }
265
+ catch (error) {
266
+ // Ignore errors, will fail with clear message below
267
+ }
268
+ return '';
269
+ }
242
270
  log(message) {
243
271
  // Log to stderr (stdout is reserved for MCP protocol)
244
272
  const key = this.apiKey || 'MISSING_API_KEY';
@@ -736,38 +764,10 @@ class FraimLocalMCPServer {
736
764
  if (parsed) {
737
765
  engine.setProviderTemplates(provider, parsed);
738
766
  this.writeCachedTemplateFile(filename, parsed);
739
- continue;
740
767
  }
741
768
  }
742
- const bundled = this.readBundledTemplateFile(filename);
743
- if (bundled) {
744
- engine.setProviderTemplates(provider, bundled);
745
- }
746
769
  }
747
770
  }
748
- readBundledTemplateFile(filename) {
749
- const candidates = [
750
- (0, path_1.join)(__dirname, '..', '..', '..', 'registry', 'providers', filename),
751
- (0, path_1.join)(__dirname, '..', '..', 'registry', 'providers', filename),
752
- (0, path_1.join)(process.cwd(), 'registry', 'providers', filename)
753
- ];
754
- for (const candidate of candidates) {
755
- try {
756
- if (!(0, fs_1.existsSync)(candidate))
757
- continue;
758
- const content = (0, fs_1.readFileSync)(candidate, 'utf8');
759
- const parsed = JSON.parse(content);
760
- if (parsed && typeof parsed === 'object') {
761
- this.log(`āœ… Loaded bundled provider template: ${candidate}`);
762
- return parsed;
763
- }
764
- }
765
- catch (error) {
766
- this.log(`āš ļø Failed to load bundled template ${candidate}: ${error.message}`);
767
- }
768
- }
769
- return null;
770
- }
771
771
  async hydrateTemplateCachesForResponse(response, requestSessionId) {
772
772
  if (!response.result)
773
773
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim-framework",
3
- "version": "2.0.83",
3
+ "version": "2.0.84",
4
4
  "description": "FRAIM v2: Framework for Rigor-based AI Management - Transform from solo developer to AI manager orchestrating production-ready code with enterprise-grade discipline",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -29,14 +29,16 @@
29
29
  "postinstall": "fraim sync --skip-updates || echo 'FRAIM setup skipped.'",
30
30
  "prepublishOnly": "npm run build",
31
31
  "release": "npm version patch && npm publish",
32
- "validate:registry": "tsx scripts/verify-registry-paths.ts && npm run validate:workflows && npm run validate:skills && npm run validate:platform-agnostic && npm run validate:template-namespaces && npm run validate:config-fallbacks && npm run validate:bootstrap-config-coverage && npm run validate:provider-action-mappings",
32
+ "validate:registry": "tsx scripts/verify-registry-paths.ts && npm run validate:workflows && npm run validate:skills && npm run validate:platform-agnostic && npm run validate:template-namespaces && npm run validate:config-fallbacks && npm run validate:bootstrap-config-coverage && npm run validate:provider-action-mappings && npm run validate:fidelity && npm run validate:config-tokens",
33
33
  "validate:workflows": "tsx scripts/validate-workflows.ts",
34
34
  "validate:platform-agnostic": "tsx scripts/validate-platform-agnostic.ts",
35
35
  "validate:skills": "tsx scripts/validate-skills.ts",
36
36
  "validate:template-namespaces": "tsx scripts/validate-template-namespaces.ts",
37
37
  "validate:config-fallbacks": "tsx scripts/validate-config-fallbacks.ts",
38
38
  "validate:bootstrap-config-coverage": "tsx scripts/validate-bootstrap-config-coverage.ts",
39
- "validate:provider-action-mappings": "tsx scripts/validate-provider-action-mappings.ts"
39
+ "validate:provider-action-mappings": "tsx scripts/validate-provider-action-mappings.ts",
40
+ "validate:fidelity": "tsx scripts/validate-fidelity.ts",
41
+ "validate:config-tokens": "tsx scripts/validate-config-tokens.ts"
40
42
  },
41
43
  "repository": {
42
44
  "type": "git",