fraim-framework 2.0.63 → 2.0.65

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.
Files changed (106) hide show
  1. package/bin/fraim-mcp.js +52 -19
  2. package/bin/fraim.js +23 -0
  3. package/dist/src/cli/commands/add-ide.js +53 -14
  4. package/dist/src/cli/commands/doctor.js +12 -24
  5. package/dist/src/cli/commands/init-project.js +0 -3
  6. package/dist/src/cli/commands/init.js +0 -2
  7. package/dist/src/cli/commands/mcp.js +65 -0
  8. package/dist/src/cli/commands/setup.js +17 -1
  9. package/dist/src/cli/commands/sync.js +173 -104
  10. package/dist/src/cli/setup/auto-mcp-setup.js +6 -4
  11. package/dist/src/cli/setup/mcp-config-generator.js +65 -41
  12. package/dist/src/fraim/issue-tracking/ado-provider.js +304 -0
  13. package/dist/src/fraim/issue-tracking/factory.js +63 -0
  14. package/dist/src/fraim/issue-tracking/github-provider.js +200 -0
  15. package/dist/src/fraim/issue-tracking/types.js +7 -0
  16. package/dist/src/fraim/issue-tracking-config.js +83 -0
  17. package/dist/src/local-mcp-server/stdio-server.js +91 -15
  18. package/dist/src/utils/remote-sync.js +130 -0
  19. package/package.json +3 -4
  20. package/dist/src/utils/enforcement-utils.js +0 -239
  21. package/dist/src/utils/validate-workflows.js +0 -101
  22. package/registry/scripts/cleanup-branch.ts +0 -341
  23. package/registry/scripts/code-quality-check.sh +0 -566
  24. package/registry/scripts/comprehensive-explorer.py +0 -297
  25. package/registry/scripts/create-git-labels.sh +0 -49
  26. package/registry/scripts/create-website-structure.js +0 -562
  27. package/registry/scripts/detect-tautological-tests.sh +0 -38
  28. package/registry/scripts/evaluate-code-quality.ts +0 -36
  29. package/registry/scripts/exec-with-timeout.ts +0 -122
  30. package/registry/scripts/generate-engagement-emails.ts +0 -830
  31. package/registry/scripts/interactive-explorer.py +0 -270
  32. package/registry/scripts/markdown-to-pdf.js +0 -395
  33. package/registry/scripts/newsletter-helpers.ts +0 -777
  34. package/registry/scripts/pdf-styles.css +0 -172
  35. package/registry/scripts/prep-issue.sh +0 -548
  36. package/registry/scripts/productivity/build-productivity-csv.mjs +0 -242
  37. package/registry/scripts/productivity/fetch-pr-details.mjs +0 -144
  38. package/registry/scripts/productivity/productivity-report.sh +0 -147
  39. package/registry/scripts/profile-server.ts +0 -426
  40. package/registry/scripts/run-thank-you-workflow.ts +0 -122
  41. package/registry/scripts/scrape-site.py +0 -302
  42. package/registry/scripts/send-newsletter-simple.ts +0 -102
  43. package/registry/scripts/send-thank-you-emails.ts +0 -57
  44. package/registry/scripts/validate-openapi-limits.ts +0 -366
  45. package/registry/scripts/validate-test-coverage.ts +0 -280
  46. package/registry/scripts/verify-pr-comments.sh +0 -74
  47. package/registry/scripts/verify-test-coverage.ts +0 -36
  48. package/registry/stubs/workflows/bootstrap/create-architecture.md +0 -11
  49. package/registry/stubs/workflows/bootstrap/detect-broken-windows.md +0 -11
  50. package/registry/stubs/workflows/bootstrap/evaluate-code-quality.md +0 -11
  51. package/registry/stubs/workflows/bootstrap/verify-test-coverage.md +0 -11
  52. package/registry/stubs/workflows/brainstorming/blue-sky-brainstorming.md +0 -11
  53. package/registry/stubs/workflows/brainstorming/codebase-brainstorming.md +0 -11
  54. package/registry/stubs/workflows/business-development/create-business-plan.md +0 -11
  55. package/registry/stubs/workflows/business-development/ideate-business-opportunity.md +0 -11
  56. package/registry/stubs/workflows/business-development/price-product.md +0 -18
  57. package/registry/stubs/workflows/compliance/detect-compliance-requirements.md +0 -11
  58. package/registry/stubs/workflows/compliance/generate-audit-evidence.md +0 -11
  59. package/registry/stubs/workflows/compliance/soc2-evidence-generator.md +0 -11
  60. package/registry/stubs/workflows/customer-development/insight-analysis.md +0 -11
  61. package/registry/stubs/workflows/customer-development/insight-triage.md +0 -11
  62. package/registry/stubs/workflows/customer-development/interview-preparation.md +0 -11
  63. package/registry/stubs/workflows/customer-development/linkedin-outreach.md +0 -11
  64. package/registry/stubs/workflows/customer-development/strategic-brainstorming.md +0 -11
  65. package/registry/stubs/workflows/customer-development/thank-customers.md +0 -11
  66. package/registry/stubs/workflows/customer-development/user-survey-dispatch.md +0 -11
  67. package/registry/stubs/workflows/customer-development/users-to-target.md +0 -11
  68. package/registry/stubs/workflows/customer-development/weekly-newsletter.md +0 -11
  69. package/registry/stubs/workflows/deploy/cloud-deployment.md +0 -11
  70. package/registry/stubs/workflows/improve-fraim/contribute.md +0 -11
  71. package/registry/stubs/workflows/improve-fraim/file-issue.md +0 -11
  72. package/registry/stubs/workflows/learning/build-skillset.md +0 -11
  73. package/registry/stubs/workflows/learning/synthesize-learnings.md +0 -11
  74. package/registry/stubs/workflows/legal/contract-review-analysis.md +0 -11
  75. package/registry/stubs/workflows/legal/nda.md +0 -11
  76. package/registry/stubs/workflows/legal/patent-filing.md +0 -11
  77. package/registry/stubs/workflows/legal/saas-contract-development.md +0 -11
  78. package/registry/stubs/workflows/legal/trademark-filing.md +0 -11
  79. package/registry/stubs/workflows/marketing/content-creation.md +0 -11
  80. package/registry/stubs/workflows/marketing/convert-to-pdf.md +0 -11
  81. package/registry/stubs/workflows/marketing/create-modern-website.md +0 -11
  82. package/registry/stubs/workflows/marketing/domain-registration.md +0 -11
  83. package/registry/stubs/workflows/marketing/hbr-article.md +0 -11
  84. package/registry/stubs/workflows/marketing/launch-checklist.md +0 -11
  85. package/registry/stubs/workflows/marketing/marketing-strategy.md +0 -11
  86. package/registry/stubs/workflows/marketing/storytelling.md +0 -11
  87. package/registry/stubs/workflows/performance/analyze-performance.md +0 -11
  88. package/registry/stubs/workflows/product-building/design.md +0 -11
  89. package/registry/stubs/workflows/product-building/implement.md +0 -11
  90. package/registry/stubs/workflows/product-building/iterate-on-pr-comments.md +0 -11
  91. package/registry/stubs/workflows/product-building/prep-issue.md +0 -11
  92. package/registry/stubs/workflows/product-building/prototype.md +0 -11
  93. package/registry/stubs/workflows/product-building/resolve.md +0 -11
  94. package/registry/stubs/workflows/product-building/retrospect.md +0 -11
  95. package/registry/stubs/workflows/product-building/spec.md +0 -11
  96. package/registry/stubs/workflows/product-building/test.md +0 -11
  97. package/registry/stubs/workflows/productivity-report/productivity-report.md +0 -11
  98. package/registry/stubs/workflows/quality-assurance/browser-validation.md +0 -11
  99. package/registry/stubs/workflows/quality-assurance/iterative-improvement-cycle.md +0 -11
  100. package/registry/stubs/workflows/replicate/replicate-discovery.md +0 -11
  101. package/registry/stubs/workflows/replicate/replicate-to-issues.md +0 -11
  102. package/registry/stubs/workflows/reviewer/review-implementation-vs-design-spec.md +0 -11
  103. package/registry/stubs/workflows/reviewer/review-implementation-vs-feature-spec.md +0 -11
  104. package/registry/stubs/workflows/startup-credits/aws-activate-application.md +0 -11
  105. package/registry/stubs/workflows/startup-credits/google-cloud-application.md +0 -11
  106. package/registry/stubs/workflows/startup-credits/microsoft-azure-application.md +0 -11
@@ -23,6 +23,7 @@ const fs_1 = require("fs");
23
23
  const path_1 = require("path");
24
24
  const os_1 = require("os");
25
25
  const child_process_1 = require("child_process");
26
+ const crypto_1 = require("crypto");
26
27
  const axios_1 = __importDefault(require("axios"));
27
28
  class FraimLocalMCPServer {
28
29
  constructor() {
@@ -34,6 +35,7 @@ class FraimLocalMCPServer {
34
35
  this.repoInfo = null;
35
36
  this.remoteUrl = process.env.FRAIM_REMOTE_URL || 'https://fraim.wellnessatwork.me';
36
37
  this.apiKey = process.env.FRAIM_API_KEY || '';
38
+ this.localVersion = this.detectLocalVersion();
37
39
  if (!this.apiKey) {
38
40
  this.logError('❌ FRAIM_API_KEY environment variable is required');
39
41
  process.exit(1);
@@ -41,13 +43,46 @@ class FraimLocalMCPServer {
41
43
  this.log('🚀 FRAIM Local MCP Server starting...');
42
44
  this.log(`📡 Remote server: ${this.remoteUrl}`);
43
45
  this.log(`🔑 API key: ${this.apiKey.substring(0, 10)}...`);
46
+ this.log(`Local MCP version: ${this.localVersion}`);
47
+ this.log(`🔍 DEBUG BUILD: Machine detection v2 active`);
44
48
  }
45
49
  log(message) {
46
50
  // Log to stderr (stdout is reserved for MCP protocol)
47
- console.error(`[FRAIM] ${message}`);
51
+ const key = this.apiKey || 'MISSING_API_KEY';
52
+ console.error(`[FRAIM key:${key}] ${message}`);
53
+ // Also log to file for debugging
54
+ try {
55
+ const fs = require('fs');
56
+ const logFile = require('path').join(require('os').tmpdir(), 'fraim-mcp-proxy.log');
57
+ fs.appendFileSync(logFile, `${new Date().toISOString()} [${key}] ${message}\n`);
58
+ }
59
+ catch (e) {
60
+ // Ignore file logging errors
61
+ }
48
62
  }
49
63
  logError(message) {
50
- console.error(`[FRAIM ERROR] ${message}`);
64
+ const key = this.apiKey || 'MISSING_API_KEY';
65
+ console.error(`[FRAIM ERROR key:${key}] ${message}`);
66
+ }
67
+ detectLocalVersion() {
68
+ const candidates = [
69
+ (0, path_1.join)(__dirname, '..', '..', '..', 'package.json'),
70
+ (0, path_1.join)(__dirname, '..', '..', 'package.json')
71
+ ];
72
+ for (const pkgPath of candidates) {
73
+ try {
74
+ if (!(0, fs_1.existsSync)(pkgPath))
75
+ continue;
76
+ const pkg = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf8'));
77
+ if (typeof pkg.version === 'string' && pkg.version.trim().length > 0) {
78
+ return pkg.version;
79
+ }
80
+ }
81
+ catch {
82
+ // Ignore and try the next candidate
83
+ }
84
+ }
85
+ return 'unknown';
51
86
  }
52
87
  findProjectRoot() {
53
88
  // If we already have workspace root from MCP roots, use it
@@ -64,6 +99,12 @@ class FraimLocalMCPServer {
64
99
  this.log(` INIT_CWD: ${process.env.INIT_CWD || '(not set)'}`);
65
100
  this.log(` HOME: ${process.env.HOME || '(not set)'}`);
66
101
  this.log(` USERPROFILE: ${process.env.USERPROFILE || '(not set)'}`);
102
+ this.log(` PWD: ${process.env.PWD || '(not set)'}`);
103
+ this.log(` OLDPWD: ${process.env.OLDPWD || '(not set)'}`);
104
+ // Log ALL env vars that might contain workspace info
105
+ Object.keys(process.env).filter(k => k.toLowerCase().includes('workspace') || k.toLowerCase().includes('project') || k.toLowerCase().includes('kiro')).forEach(k => {
106
+ this.log(` ${k}: ${process.env[k]}`);
107
+ });
67
108
  // Priority 1: Check for IDE-provided workspace environment variables
68
109
  const workspaceHints = [
69
110
  process.env.WORKSPACE_FOLDER_PATHS?.split(':')[0], // Cursor provides this (colon-separated for multi-root)
@@ -135,6 +176,7 @@ class FraimLocalMCPServer {
135
176
  */
136
177
  detectMachineInfo() {
137
178
  if (this.machineInfo) {
179
+ this.log(`🔄 Returning cached machine info: ${JSON.stringify(this.machineInfo)}`);
138
180
  return this.machineInfo;
139
181
  }
140
182
  try {
@@ -144,7 +186,7 @@ class FraimLocalMCPServer {
144
186
  memory: (0, os_1.totalmem)(),
145
187
  cpus: (0, os_1.cpus)().length
146
188
  };
147
- this.log(`✅ Detected machine info: ${this.machineInfo.hostname} (${this.machineInfo.platform})`);
189
+ this.log(`✅ Detected machine info: ${JSON.stringify(this.machineInfo)}`);
148
190
  return this.machineInfo;
149
191
  }
150
192
  catch (error) {
@@ -475,30 +517,34 @@ class FraimLocalMCPServer {
475
517
  * Proxy request to remote FRAIM server
476
518
  */
477
519
  async proxyToRemote(request) {
520
+ const requestId = (0, crypto_1.randomUUID)();
478
521
  try {
479
522
  // Special handling for fraim_connect - automatically inject machine and repo info
480
523
  if (request.method === 'tools/call' && request.params?.name === 'fraim_connect') {
481
- this.log('🔧 Intercepting fraim_connect to inject machine/repo info');
524
+ this.log(`[req:${requestId}] Intercepting fraim_connect to inject machine/repo info`);
482
525
  const args = request.params.arguments || {};
483
526
  // REQUIRED: Auto-detect and inject machine info
484
527
  const detectedMachine = this.detectMachineInfo();
485
528
  args.machine = {
486
- ...detectedMachine,
487
- ...args.machine // Allow agent to override specific fields if needed
529
+ ...args.machine, // Agent values as fallback
530
+ ...detectedMachine // Detected values override (always win)
488
531
  };
489
- this.log(`✅ Injected machine info: ${args.machine.hostname} (${args.machine.platform})`);
532
+ this.log(`[req:${requestId}] Auto-detected and injected machine info: ${JSON.stringify(args.machine)}`);
533
+ if (!args.machine.memory || !args.machine.cpus) {
534
+ this.logError(`[req:${requestId}] WARNING: Machine info missing memory or cpus! Detected: ${JSON.stringify(detectedMachine)}, Final: ${JSON.stringify(args.machine)}`);
535
+ }
490
536
  // REQUIRED: Auto-detect and inject repo info
491
537
  const detectedRepo = this.detectRepoInfo();
492
538
  if (detectedRepo) {
493
539
  args.repo = {
494
- ...detectedRepo,
495
- ...args.repo // Allow agent to override specific fields if needed
540
+ ...args.repo, // Agent values as fallback
541
+ ...detectedRepo // Detected values override (always win)
496
542
  };
497
- this.log(`✅ Injected repo info: ${args.repo.owner}/${args.repo.name}`);
543
+ this.log(`[req:${requestId}] Auto-detected and injected repo info: ${args.repo.owner}/${args.repo.name}`);
498
544
  }
499
545
  else {
500
546
  // If detection fails completely, return error instead of sending garbage
501
- this.logError('⚠️ Could not detect repo info and no config available');
547
+ this.logError(`[req:${requestId}] Could not detect repo info and no config available`);
502
548
  return {
503
549
  jsonrpc: '2.0',
504
550
  id: request.id,
@@ -511,23 +557,53 @@ class FraimLocalMCPServer {
511
557
  // Update the request with injected info
512
558
  request.params.arguments = args;
513
559
  }
560
+ this.log(`[req:${requestId}] Proxying ${request.method} to ${this.remoteUrl}/mcp`);
514
561
  const response = await axios_1.default.post(`${this.remoteUrl}/mcp`, request, {
515
562
  headers: {
516
563
  'Content-Type': 'application/json',
517
- 'x-api-key': this.apiKey
564
+ 'x-api-key': this.apiKey,
565
+ 'x-fraim-request-id': requestId,
566
+ 'x-fraim-local-version': this.localVersion
518
567
  },
519
568
  timeout: 30000
520
569
  });
521
570
  return response.data;
522
571
  }
523
572
  catch (error) {
524
- this.logError(`Remote request failed: ${error.message}`);
573
+ const status = error?.response?.status;
574
+ const remoteData = error?.response?.data;
575
+ this.logError(`[req:${requestId}] Remote request failed (${status || 'no-status'}): ${error.message}`);
576
+ if (remoteData && typeof remoteData === 'object') {
577
+ const forwarded = {
578
+ jsonrpc: typeof remoteData.jsonrpc === 'string' ? remoteData.jsonrpc : '2.0',
579
+ id: remoteData.id ?? request.id,
580
+ error: remoteData.error ?? {
581
+ code: -32603,
582
+ message: `Remote server error (${status || 'unknown status'}): ${error.message}`
583
+ }
584
+ };
585
+ if (forwarded.error && typeof forwarded.error === 'object') {
586
+ const existingData = forwarded.error.data;
587
+ forwarded.error.data = {
588
+ ...(existingData && typeof existingData === 'object' ? existingData : {}),
589
+ fraimRequestId: requestId,
590
+ remoteStatus: status ?? null,
591
+ localMcpVersion: this.localVersion
592
+ };
593
+ }
594
+ return forwarded;
595
+ }
525
596
  return {
526
597
  jsonrpc: '2.0',
527
598
  id: request.id,
528
599
  error: {
529
600
  code: -32603,
530
- message: `Remote server error: ${error.message}`
601
+ message: `Remote server error: ${error.message}`,
602
+ data: {
603
+ fraimRequestId: requestId,
604
+ remoteStatus: status ?? null,
605
+ localMcpVersion: this.localVersion
606
+ }
531
607
  }
532
608
  };
533
609
  }
@@ -575,7 +651,7 @@ class FraimLocalMCPServer {
575
651
  // Proxy initialize to remote server first
576
652
  const response = await this.proxyToRemote(request);
577
653
  const processedResponse = this.processResponse(response);
578
- // After successful initialization, load config using fallback methods
654
+ // After successful initialization, load config
579
655
  if (!processedResponse.error) {
580
656
  // For now, don't request roots - just use env var + upward search
581
657
  // TODO: Implement roots/list properly after initialization is complete
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ /**
3
+ * Remote Registry Sync
4
+ *
5
+ * Fetches workflows and scripts from the remote FRAIM server
6
+ * instead of bundling them in the npm package.
7
+ *
8
+ * Issue: #83 - Minimize client package by fetching registry remotely
9
+ */
10
+ var __importDefault = (this && this.__importDefault) || function (mod) {
11
+ return (mod && mod.__esModule) ? mod : { "default": mod };
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.syncFromRemote = syncFromRemote;
15
+ const axios_1 = __importDefault(require("axios"));
16
+ const fs_1 = require("fs");
17
+ const path_1 = require("path");
18
+ const chalk_1 = __importDefault(require("chalk"));
19
+ const script_sync_utils_1 = require("./script-sync-utils");
20
+ /**
21
+ * Sync workflows and scripts from remote FRAIM server
22
+ */
23
+ async function syncFromRemote(options) {
24
+ const remoteUrl = options.remoteUrl || process.env.FRAIM_REMOTE_URL || 'https://fraim.wellnessatwork.me';
25
+ const apiKey = options.apiKey || process.env.FRAIM_API_KEY || '';
26
+ if (!apiKey) {
27
+ return {
28
+ success: false,
29
+ workflowsSynced: 0,
30
+ scriptsSynced: 0,
31
+ error: 'FRAIM_API_KEY not set'
32
+ };
33
+ }
34
+ try {
35
+ console.log(chalk_1.default.blue('🔄 Syncing from remote FRAIM server...'));
36
+ console.log(chalk_1.default.gray(` Remote: ${remoteUrl}`));
37
+ // Fetch registry files from remote server
38
+ const response = await axios_1.default.get(`${remoteUrl}/api/registry/sync`, {
39
+ headers: {
40
+ 'x-api-key': apiKey
41
+ },
42
+ timeout: 30000
43
+ });
44
+ const files = response.data.files || [];
45
+ if (!files || files.length === 0) {
46
+ console.log(chalk_1.default.yellow('⚠️ No files received from remote server'));
47
+ return {
48
+ success: false,
49
+ workflowsSynced: 0,
50
+ scriptsSynced: 0,
51
+ error: 'No files received'
52
+ };
53
+ }
54
+ // Sync workflows
55
+ const workflowFiles = files.filter(f => f.type === 'workflow');
56
+ const workflowsDir = (0, path_1.join)(options.projectRoot, '.fraim', 'workflows');
57
+ if (!(0, fs_1.existsSync)(workflowsDir)) {
58
+ (0, fs_1.mkdirSync)(workflowsDir, { recursive: true });
59
+ }
60
+ // Clean existing workflows
61
+ cleanDirectory(workflowsDir);
62
+ // Write workflow files
63
+ for (const file of workflowFiles) {
64
+ const filePath = (0, path_1.join)(workflowsDir, file.path);
65
+ const fileDir = (0, path_1.dirname)(filePath);
66
+ if (!(0, fs_1.existsSync)(fileDir)) {
67
+ (0, fs_1.mkdirSync)(fileDir, { recursive: true });
68
+ }
69
+ (0, fs_1.writeFileSync)(filePath, file.content, 'utf8');
70
+ console.log(chalk_1.default.gray(` + ${file.path}`));
71
+ }
72
+ // Sync scripts to user directory
73
+ const scriptFiles = files.filter(f => f.type === 'script');
74
+ const userDir = (0, script_sync_utils_1.getUserFraimDir)();
75
+ const scriptsDir = (0, path_1.join)(userDir, 'scripts');
76
+ if (!(0, fs_1.existsSync)(scriptsDir)) {
77
+ (0, fs_1.mkdirSync)(scriptsDir, { recursive: true });
78
+ }
79
+ // Clean existing scripts
80
+ cleanDirectory(scriptsDir);
81
+ // Write script files
82
+ for (const file of scriptFiles) {
83
+ const filePath = (0, path_1.join)(scriptsDir, file.path);
84
+ const fileDir = (0, path_1.dirname)(filePath);
85
+ if (!(0, fs_1.existsSync)(fileDir)) {
86
+ (0, fs_1.mkdirSync)(fileDir, { recursive: true });
87
+ }
88
+ (0, fs_1.writeFileSync)(filePath, file.content, 'utf8');
89
+ console.log(chalk_1.default.gray(` + ${file.path}`));
90
+ }
91
+ console.log(chalk_1.default.green(`\n✅ Synced ${workflowFiles.length} workflows and ${scriptFiles.length} scripts from remote`));
92
+ return {
93
+ success: true,
94
+ workflowsSynced: workflowFiles.length,
95
+ scriptsSynced: scriptFiles.length
96
+ };
97
+ }
98
+ catch (error) {
99
+ console.error(chalk_1.default.red(`❌ Remote sync failed: ${error.message}`));
100
+ return {
101
+ success: false,
102
+ workflowsSynced: 0,
103
+ scriptsSynced: 0,
104
+ error: error.message
105
+ };
106
+ }
107
+ }
108
+ /**
109
+ * Clean directory contents (but keep the directory itself)
110
+ */
111
+ function cleanDirectory(dirPath) {
112
+ if (!(0, fs_1.existsSync)(dirPath))
113
+ return;
114
+ const entries = (0, fs_1.readdirSync)(dirPath, { withFileTypes: true });
115
+ for (const entry of entries) {
116
+ const fullPath = (0, path_1.join)(dirPath, entry.name);
117
+ if (entry.isDirectory()) {
118
+ cleanDirectory(fullPath);
119
+ try {
120
+ (0, fs_1.rmdirSync)(fullPath);
121
+ }
122
+ catch (e) {
123
+ // Directory not empty, skip
124
+ }
125
+ }
126
+ else {
127
+ (0, fs_1.unlinkSync)(fullPath);
128
+ }
129
+ }
130
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim-framework",
3
- "version": "2.0.63",
3
+ "version": "2.0.65",
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": {
@@ -9,13 +9,14 @@
9
9
  },
10
10
  "scripts": {
11
11
  "dev": "tsx --watch src/fraim-mcp-server.ts > server.log 2>&1",
12
+ "dev:prod": "npm run build && node dist/src/fraim-mcp-server.js > server.log 2>&1",
12
13
  "build": "tsc && node scripts/copy-ai-manager-rules.js && npm run build:stubs && npm run validate:registry",
13
14
  "build:stubs": "tsx scripts/build-stub-registry.ts",
14
15
  "test": "node scripts/test-with-server.js",
15
16
  "start:fraim": "tsx src/fraim-mcp-server.ts",
16
17
  "dev:fraim": "tsx --watch src/fraim-mcp-server.ts",
17
18
  "serve:website": "node fraim-pro/serve.js",
18
- "watch:fraimlogs": "tsx scripts/watch-fraim-logs.ts",
19
+ "watch:fraimlogs": "tsx scripts/watch-fraim-logs.ts > prodlogs.log 2>&1",
19
20
  "manage-keys": "tsx scripts/fraim/manage-keys.ts",
20
21
  "view-signups": "tsx scripts/view-signups.ts",
21
22
  "fraim:init": "npm run build && node index.js init",
@@ -74,8 +75,6 @@
74
75
  "dist/src/cli/",
75
76
  "dist/src/fraim/",
76
77
  "dist/src/utils/",
77
- "registry/stubs/",
78
- "registry/scripts/",
79
78
  "bin/fraim.js",
80
79
  "bin/fraim-mcp.js",
81
80
  "index.js",
@@ -1,239 +0,0 @@
1
- "use strict";
2
- /**
3
- * Enforcement Utilities for FRAIM
4
- *
5
- * Provides deterministic enforcement of:
6
- * - Working style (PR vs Conversation)
7
- * - Feedback tracking requirements
8
- * - User config initialization
9
- */
10
- Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.initializeUserConfig = initializeUserConfig;
12
- exports.loadUserConfig = loadUserConfig;
13
- exports.getEnforcementContext = getEnforcementContext;
14
- exports.isCommitAllowed = isCommitAllowed;
15
- exports.checkUnaddressedFeedback = checkUnaddressedFeedback;
16
- exports.generateCommitInstructions = generateCommitInstructions;
17
- exports.generateFeedbackInstructions = generateFeedbackInstructions;
18
- const fs_1 = require("fs");
19
- const path_1 = require("path");
20
- const child_process_1 = require("child_process");
21
- const script_sync_utils_js_1 = require("./script-sync-utils.js");
22
- const git_utils_js_1 = require("./git-utils.js");
23
- /**
24
- * Initialize user config with default values if it doesn't exist
25
- */
26
- function initializeUserConfig() {
27
- try {
28
- const userConfigPath = (0, path_1.join)((0, script_sync_utils_js_1.getUserFraimDir)(), 'config.json');
29
- if (!(0, fs_1.existsSync)(userConfigPath)) {
30
- const defaultConfig = {
31
- workingStyle: 'PR' // Default to PR mode
32
- };
33
- // Ensure directory exists
34
- const userFraimDir = (0, script_sync_utils_js_1.getUserFraimDir)();
35
- if (!(0, fs_1.existsSync)(userFraimDir)) {
36
- const { mkdirSync } = require('fs');
37
- mkdirSync(userFraimDir, { recursive: true });
38
- }
39
- (0, fs_1.writeFileSync)(userConfigPath, JSON.stringify(defaultConfig, null, 2));
40
- }
41
- }
42
- catch (error) {
43
- console.warn('⚠️ Failed to initialize user config:', error);
44
- }
45
- }
46
- /**
47
- * Load user config from ~/.fraim/config.json
48
- */
49
- function loadUserConfig() {
50
- try {
51
- const userConfigPath = (0, path_1.join)((0, script_sync_utils_js_1.getUserFraimDir)(), 'config.json');
52
- if ((0, fs_1.existsSync)(userConfigPath)) {
53
- const content = (0, fs_1.readFileSync)(userConfigPath, 'utf8');
54
- return JSON.parse(content);
55
- }
56
- }
57
- catch (error) {
58
- console.warn('⚠️ Failed to load user config:', error);
59
- }
60
- return null;
61
- }
62
- /**
63
- * Get enforcement context (working style, branch info)
64
- */
65
- function getEnforcementContext(issueNumber, projectRoot) {
66
- const userConfig = loadUserConfig();
67
- const workingStyle = userConfig?.workingStyle || 'PR'; // Default to PR
68
- let currentBranch = null;
69
- let defaultBranch = null;
70
- try {
71
- currentBranch = (0, git_utils_js_1.getCurrentGitBranch)();
72
- // Try to get default branch
73
- try {
74
- const remoteHead = (0, child_process_1.execSync)('git symbolic-ref refs/remotes/origin/HEAD', {
75
- timeout: 2000,
76
- stdio: 'pipe'
77
- }).toString().trim();
78
- const match = remoteHead.match(/refs\/remotes\/origin\/(.+)$/);
79
- if (match) {
80
- defaultBranch = match[1];
81
- }
82
- }
83
- catch (e) {
84
- defaultBranch = 'main'; // Fallback
85
- }
86
- }
87
- catch (e) {
88
- // Not a git repo or git command failed
89
- }
90
- return {
91
- workingStyle,
92
- currentBranch,
93
- defaultBranch,
94
- issueNumber,
95
- projectRoot
96
- };
97
- }
98
- /**
99
- * Check if agent commit is allowed based on working style
100
- * Note: In Conversation mode, agent NEVER commits (user commits manually)
101
- */
102
- function isCommitAllowed(context) {
103
- // If Conversation style, agent NEVER commits (user commits manually)
104
- if (context.workingStyle === 'Conversation') {
105
- return {
106
- allowed: false,
107
- reason: 'Working style is "Conversation" - agent does not commit. User commits manually.'
108
- };
109
- }
110
- // PR style: commits only allowed on feature branches, not main/master
111
- if (!context.currentBranch) {
112
- return {
113
- allowed: false,
114
- reason: 'Not in a git repository or unable to determine current branch'
115
- };
116
- }
117
- const defaultBranch = context.defaultBranch || 'main';
118
- const isDefaultBranch = context.currentBranch === defaultBranch ||
119
- context.currentBranch === 'master' ||
120
- context.currentBranch === 'main';
121
- if (isDefaultBranch) {
122
- return {
123
- allowed: false,
124
- reason: `Working style is "PR" but you are on ${context.currentBranch} branch. Create a feature branch first.`
125
- };
126
- }
127
- return { allowed: true };
128
- }
129
- /**
130
- * Check for unaddressed feedback files
131
- */
132
- function checkUnaddressedFeedback(issueNumber, projectRoot) {
133
- const feedbackFiles = [];
134
- if (!projectRoot) {
135
- return { hasUnaddressed: false, feedbackFiles: [] };
136
- }
137
- try {
138
- const evidenceDir = (0, path_1.join)(projectRoot, 'docs', 'evidence');
139
- if (!(0, fs_1.existsSync)(evidenceDir)) {
140
- return { hasUnaddressed: false, feedbackFiles: [] };
141
- }
142
- const files = (0, fs_1.readdirSync)(evidenceDir);
143
- const issueFeedbackPattern = new RegExp(`^${issueNumber}-.*-feedback\\.md$`);
144
- for (const file of files) {
145
- if (issueFeedbackPattern.test(file)) {
146
- const filePath = (0, path_1.join)(evidenceDir, file);
147
- const content = (0, fs_1.readFileSync)(filePath, 'utf8');
148
- // Check if feedback is addressed (look for "Status: Addressed" or similar markers)
149
- // Simple heuristic: if file contains "Status: Addressed" or "✅ Addressed", consider it addressed
150
- // If it contains "Status: Unaddressed", it's definitely unaddressed
151
- const hasUnaddressed = /status:\s*unaddressed/i.test(content);
152
- const isAddressed = /status:\s*addressed|✅\s*addressed/i.test(content);
153
- // If explicitly unaddressed, or if no addressed markers found at all, consider unaddressed
154
- // Note: If file has both, "Unaddressed" takes precedence
155
- if (hasUnaddressed || (!hasUnaddressed && !isAddressed)) {
156
- feedbackFiles.push(file);
157
- }
158
- }
159
- }
160
- }
161
- catch (error) {
162
- console.warn('⚠️ Failed to check feedback files:', error);
163
- }
164
- return {
165
- hasUnaddressed: feedbackFiles.length > 0,
166
- feedbackFiles
167
- };
168
- }
169
- /**
170
- * Generate commit behavior instructions based on working style
171
- */
172
- function generateCommitInstructions(context) {
173
- // Check commit permission first (handles both Conversation and PR modes)
174
- const commitCheck = isCommitAllowed(context);
175
- if (context.workingStyle === 'Conversation') {
176
- // Conversation mode: Agent never commits, user commits manually
177
- return `\n\n**⚠️ Working Style: Conversation**\n\n` +
178
- `You are in "Conversation" mode. **DO NOT commit changes**. The user will commit manually.\n\n` +
179
- `**Branch Strategy**:\n` +
180
- `- Work directly on ${context.defaultBranch || 'main/master'} branch\n` +
181
- `- **Skip branch creation**: No need to run prep-issue.sh or create feature branches\n` +
182
- `- **Skip PR creation**: Work is committed directly to ${context.defaultBranch || 'main/master'}\n\n` +
183
- `**Commit Behavior**:\n` +
184
- `- Make changes as requested\n` +
185
- `- Do not use git commit tools\n` +
186
- `- Show diff to user: "I've made these changes: [show diff]"\n` +
187
- `- Suggest commit message if helpful\n` +
188
- `- Wait for user to review and commit manually\n\n` +
189
- `**Outcome Statements**: When documenting completion, use:\n` +
190
- `- "All work ready for user review and commit" (instead of "committed to feature branch")\n` +
191
- `- "Changes prepared on ${context.defaultBranch || 'main/master'} branch"\n`;
192
- }
193
- // PR style: Check if commits are allowed
194
- if (!commitCheck.allowed) {
195
- // Blocked: either on main/master or not in git repo
196
- return `\n\n**⚠️ Working Style: PR**\n\n` +
197
- `**BLOCKED**: ${commitCheck.reason}\n\n` +
198
- `**Action Required**:\n` +
199
- `1. Run prep-issue script: \`~/.fraim/scripts/prep-issue.sh ${context.issueNumber || 'ISSUE_NUMBER'}\`\n` +
200
- ` OR create feature branch manually: \`git checkout -b feature/${context.issueNumber || 'issue'}-description\`\n` +
201
- `2. Push branch: \`git push -u origin feature/${context.issueNumber || 'issue'}-description\`\n` +
202
- `3. Then proceed with your work\n` +
203
- `**DO NOT commit to ${context.currentBranch || 'main/master'} branch.**\n`;
204
- }
205
- // PR mode on feature branch: Commits allowed
206
- return `\n\n**✅ Working Style: PR**\n\n` +
207
- `You are in "PR" mode. Commits are allowed on feature branch: **${context.currentBranch}**\n\n` +
208
- `**Branch Setup**:\n` +
209
- `- If branch doesn't exist, run: \`~/.fraim/scripts/prep-issue.sh ${context.issueNumber || 'ISSUE_NUMBER'}\`\n` +
210
- `- Confirm you're on feature branch: \`git branch --show-current\`\n\n` +
211
- `**Commit Behavior**:\n` +
212
- `- Create commits on this branch as you complete work\n` +
213
- `- Commit message format: \`{workflow_type}({issue_number}): {description}\`\n` +
214
- `- Push branch: \`git push origin ${context.currentBranch}\`\n\n` +
215
- `**PR Management**:\n` +
216
- `- Create PR when ready for review\n` +
217
- `- **Verify PR exists** before marking work complete\n` +
218
- `- Link PR to issue and add evidence document link\n\n` +
219
- `**Outcome Statements**: When documenting completion, use:\n` +
220
- `- "All work committed and pushed to feature branch: ${context.currentBranch}"\n`;
221
- }
222
- /**
223
- * Generate feedback check instructions
224
- */
225
- function generateFeedbackInstructions(issueNumber, projectRoot) {
226
- const feedbackCheck = checkUnaddressedFeedback(issueNumber, projectRoot);
227
- if (!feedbackCheck.hasUnaddressed) {
228
- return '';
229
- }
230
- return `\n\n**⚠️ Unaddressed Feedback Detected**\n\n` +
231
- `The following feedback files need to be addressed before proceeding:\n` +
232
- feedbackCheck.feedbackFiles.map(f => `- \`docs/evidence/${f}\``).join('\n') +
233
- `\n\n**Action Required**:\n` +
234
- `1. Review each feedback file\n` +
235
- `2. Address all feedback items\n` +
236
- `3. Mark feedback as addressed in the file\n` +
237
- `4. Then continue with this phase\n` +
238
- `\n**DO NOT proceed until all feedback is addressed.**\n`;
239
- }