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 +16 -3
- package/dist/src/cli/commands/add-ide.js +1 -1
- package/dist/src/cli/commands/setup.js +44 -7
- package/dist/src/cli/fraim.js +0 -2
- package/dist/src/cli/mcp/mcp-server-registry.js +2 -0
- package/dist/src/cli/setup/auto-mcp-setup.js +12 -1
- package/dist/src/core/utils/workflow-parser.js +5 -1
- package/dist/src/local-mcp-server/stdio-server.js +30 -30
- package/package.json +5 -3
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
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
|
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
|
-
|
|
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)();
|
package/dist/src/cli/fraim.js
CHANGED
|
@@ -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(` āļø
|
|
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
|
-
|
|
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 =
|
|
230
|
+
this.apiKey = this.loadApiKey();
|
|
231
231
|
this.localVersion = this.detectLocalVersion();
|
|
232
232
|
if (!this.apiKey) {
|
|
233
|
-
this.logError('ā
|
|
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.
|
|
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",
|