fraim-framework 2.0.64 → 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 (107) 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 +23 -3
  18. package/dist/src/utils/remote-sync.js +130 -0
  19. package/package.json +2 -3
  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/azure/cost-optimization.md +0 -11
  49. package/registry/stubs/workflows/bootstrap/create-architecture.md +0 -11
  50. package/registry/stubs/workflows/bootstrap/detect-broken-windows.md +0 -11
  51. package/registry/stubs/workflows/bootstrap/evaluate-code-quality.md +0 -11
  52. package/registry/stubs/workflows/bootstrap/verify-test-coverage.md +0 -11
  53. package/registry/stubs/workflows/brainstorming/blue-sky-brainstorming.md +0 -11
  54. package/registry/stubs/workflows/brainstorming/codebase-brainstorming.md +0 -11
  55. package/registry/stubs/workflows/business-development/create-business-plan.md +0 -11
  56. package/registry/stubs/workflows/business-development/ideate-business-opportunity.md +0 -11
  57. package/registry/stubs/workflows/business-development/price-product.md +0 -18
  58. package/registry/stubs/workflows/compliance/detect-compliance-requirements.md +0 -11
  59. package/registry/stubs/workflows/compliance/generate-audit-evidence.md +0 -11
  60. package/registry/stubs/workflows/compliance/soc2-evidence-generator.md +0 -11
  61. package/registry/stubs/workflows/customer-development/insight-analysis.md +0 -11
  62. package/registry/stubs/workflows/customer-development/insight-triage.md +0 -11
  63. package/registry/stubs/workflows/customer-development/interview-preparation.md +0 -11
  64. package/registry/stubs/workflows/customer-development/linkedin-outreach.md +0 -11
  65. package/registry/stubs/workflows/customer-development/strategic-brainstorming.md +0 -11
  66. package/registry/stubs/workflows/customer-development/thank-customers.md +0 -11
  67. package/registry/stubs/workflows/customer-development/user-survey-dispatch.md +0 -11
  68. package/registry/stubs/workflows/customer-development/users-to-target.md +0 -11
  69. package/registry/stubs/workflows/customer-development/weekly-newsletter.md +0 -11
  70. package/registry/stubs/workflows/deploy/cloud-deployment.md +0 -11
  71. package/registry/stubs/workflows/improve-fraim/contribute.md +0 -11
  72. package/registry/stubs/workflows/improve-fraim/file-issue.md +0 -11
  73. package/registry/stubs/workflows/learning/build-skillset.md +0 -11
  74. package/registry/stubs/workflows/learning/synthesize-learnings.md +0 -11
  75. package/registry/stubs/workflows/legal/contract-review-analysis.md +0 -11
  76. package/registry/stubs/workflows/legal/nda.md +0 -11
  77. package/registry/stubs/workflows/legal/patent-filing.md +0 -11
  78. package/registry/stubs/workflows/legal/saas-contract-development.md +0 -11
  79. package/registry/stubs/workflows/legal/trademark-filing.md +0 -11
  80. package/registry/stubs/workflows/marketing/content-creation.md +0 -11
  81. package/registry/stubs/workflows/marketing/convert-to-pdf.md +0 -11
  82. package/registry/stubs/workflows/marketing/create-modern-website.md +0 -11
  83. package/registry/stubs/workflows/marketing/domain-registration.md +0 -11
  84. package/registry/stubs/workflows/marketing/hbr-article.md +0 -11
  85. package/registry/stubs/workflows/marketing/launch-checklist.md +0 -11
  86. package/registry/stubs/workflows/marketing/marketing-strategy.md +0 -11
  87. package/registry/stubs/workflows/marketing/storytelling.md +0 -11
  88. package/registry/stubs/workflows/performance/analyze-performance.md +0 -11
  89. package/registry/stubs/workflows/product-building/design.md +0 -11
  90. package/registry/stubs/workflows/product-building/implement.md +0 -11
  91. package/registry/stubs/workflows/product-building/iterate-on-pr-comments.md +0 -11
  92. package/registry/stubs/workflows/product-building/prep-issue.md +0 -11
  93. package/registry/stubs/workflows/product-building/prototype.md +0 -11
  94. package/registry/stubs/workflows/product-building/resolve.md +0 -11
  95. package/registry/stubs/workflows/product-building/retrospect.md +0 -11
  96. package/registry/stubs/workflows/product-building/spec.md +0 -11
  97. package/registry/stubs/workflows/product-building/test.md +0 -11
  98. package/registry/stubs/workflows/productivity-report/productivity-report.md +0 -11
  99. package/registry/stubs/workflows/quality-assurance/browser-validation.md +0 -11
  100. package/registry/stubs/workflows/quality-assurance/iterative-improvement-cycle.md +0 -11
  101. package/registry/stubs/workflows/replicate/replicate-discovery.md +0 -11
  102. package/registry/stubs/workflows/replicate/replicate-to-issues.md +0 -11
  103. package/registry/stubs/workflows/reviewer/review-implementation-vs-design-spec.md +0 -11
  104. package/registry/stubs/workflows/reviewer/review-implementation-vs-feature-spec.md +0 -11
  105. package/registry/stubs/workflows/startup-credits/aws-activate-application.md +0 -11
  106. package/registry/stubs/workflows/startup-credits/google-cloud-application.md +0 -11
  107. package/registry/stubs/workflows/startup-credits/microsoft-azure-application.md +0 -11
@@ -44,11 +44,21 @@ class FraimLocalMCPServer {
44
44
  this.log(`📡 Remote server: ${this.remoteUrl}`);
45
45
  this.log(`🔑 API key: ${this.apiKey.substring(0, 10)}...`);
46
46
  this.log(`Local MCP version: ${this.localVersion}`);
47
+ this.log(`🔍 DEBUG BUILD: Machine detection v2 active`);
47
48
  }
48
49
  log(message) {
49
50
  // Log to stderr (stdout is reserved for MCP protocol)
50
51
  const key = this.apiKey || 'MISSING_API_KEY';
51
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
+ }
52
62
  }
53
63
  logError(message) {
54
64
  const key = this.apiKey || 'MISSING_API_KEY';
@@ -89,6 +99,12 @@ class FraimLocalMCPServer {
89
99
  this.log(` INIT_CWD: ${process.env.INIT_CWD || '(not set)'}`);
90
100
  this.log(` HOME: ${process.env.HOME || '(not set)'}`);
91
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
+ });
92
108
  // Priority 1: Check for IDE-provided workspace environment variables
93
109
  const workspaceHints = [
94
110
  process.env.WORKSPACE_FOLDER_PATHS?.split(':')[0], // Cursor provides this (colon-separated for multi-root)
@@ -160,6 +176,7 @@ class FraimLocalMCPServer {
160
176
  */
161
177
  detectMachineInfo() {
162
178
  if (this.machineInfo) {
179
+ this.log(`🔄 Returning cached machine info: ${JSON.stringify(this.machineInfo)}`);
163
180
  return this.machineInfo;
164
181
  }
165
182
  try {
@@ -169,7 +186,7 @@ class FraimLocalMCPServer {
169
186
  memory: (0, os_1.totalmem)(),
170
187
  cpus: (0, os_1.cpus)().length
171
188
  };
172
- this.log(`✅ Detected machine info: ${this.machineInfo.hostname} (${this.machineInfo.platform})`);
189
+ this.log(`✅ Detected machine info: ${JSON.stringify(this.machineInfo)}`);
173
190
  return this.machineInfo;
174
191
  }
175
192
  catch (error) {
@@ -512,7 +529,10 @@ class FraimLocalMCPServer {
512
529
  ...args.machine, // Agent values as fallback
513
530
  ...detectedMachine // Detected values override (always win)
514
531
  };
515
- this.log(`[req:${requestId}] Auto-detected and injected machine info: ${args.machine.hostname} (${args.machine.platform}), ${Math.round(args.machine.memory / 1024 / 1024 / 1024)}GB RAM, ${args.machine.cpus} CPUs`);
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
+ }
516
536
  // REQUIRED: Auto-detect and inject repo info
517
537
  const detectedRepo = this.detectRepoInfo();
518
538
  if (detectedRepo) {
@@ -631,7 +651,7 @@ class FraimLocalMCPServer {
631
651
  // Proxy initialize to remote server first
632
652
  const response = await this.proxyToRemote(request);
633
653
  const processedResponse = this.processResponse(response);
634
- // After successful initialization, load config using fallback methods
654
+ // After successful initialization, load config
635
655
  if (!processedResponse.error) {
636
656
  // For now, don't request roots - just use env var + upward search
637
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.64",
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,6 +9,7 @@
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",
@@ -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
- }
@@ -1,101 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateAllWorkflows = validateAllWorkflows;
4
- const fs_1 = require("fs");
5
- const path_1 = require("path");
6
- const workflow_parser_js_1 = require("./workflow-parser.js");
7
- /**
8
- * Validate all workflows in the registry
9
- */
10
- function validateAllWorkflows(registryPath) {
11
- const errors = [];
12
- const workflowFiles = [];
13
- function findWorkflows(dir) {
14
- if (!(0, fs_1.existsSync)(dir))
15
- return;
16
- const entries = (0, fs_1.readdirSync)(dir, { withFileTypes: true });
17
- for (const entry of entries) {
18
- const fullPath = (0, path_1.join)(dir, entry.name);
19
- if (entry.isDirectory()) {
20
- findWorkflows(fullPath);
21
- }
22
- else if (entry.isFile() && entry.name.endsWith('.md')) {
23
- const content = (0, fs_1.readFileSync)(fullPath, 'utf-8');
24
- if (content.includes('"initialPhase"')) {
25
- workflowFiles.push(fullPath);
26
- }
27
- }
28
- }
29
- }
30
- findWorkflows(registryPath);
31
- console.log(`🔍 Validating ${workflowFiles.length} workflows...`);
32
- for (const filePath of workflowFiles) {
33
- const relativePath = filePath.replace(registryPath, '');
34
- try {
35
- const wf = workflow_parser_js_1.WorkflowParser.parse(filePath);
36
- if (!wf) {
37
- errors.push(`❌ ${relativePath}: Failed to parse. Ensure JSON metadata is present and valid.`);
38
- continue;
39
- }
40
- const { metadata, phases } = wf;
41
- // 1. Initial Phase check
42
- if (!phases.has(metadata.initialPhase)) {
43
- errors.push(`❌ ${relativePath}: initialPhase "${metadata.initialPhase}" does not exist in phases.`);
44
- }
45
- // 2. Validate Phase Flow
46
- for (const [phaseId, flow] of Object.entries(metadata.phases)) {
47
- // Check if phase exists in markdown
48
- if (!phases.has(phaseId)) {
49
- errors.push(`❌ ${relativePath}: Phase "${phaseId}" defined in metadata but missing in markdown.`);
50
- }
51
- // Check Success transition
52
- if (flow.onSuccess && flow.onSuccess !== 'null') {
53
- if (typeof flow.onSuccess === 'string') {
54
- if (!phases.has(flow.onSuccess)) {
55
- errors.push(`❌ ${relativePath}: Phase "${phaseId}" transition onSuccess -> "${flow.onSuccess}" target not found.`);
56
- }
57
- }
58
- else {
59
- // Validate each branch of conditional success
60
- for (const [branch, target] of Object.entries(flow.onSuccess)) {
61
- if (target && target !== 'null' && !phases.has(target)) {
62
- errors.push(`❌ ${relativePath}: Phase "${phaseId}" conditional transition onSuccess[${branch}] -> "${target}" target not found.`);
63
- }
64
- }
65
- }
66
- }
67
- // Check Failure transition
68
- if (flow.onFailure && !phases.has(flow.onFailure)) {
69
- errors.push(`❌ ${relativePath}: Phase "${phaseId}" transition onFailure -> "${flow.onFailure}" target not found.`);
70
- }
71
- }
72
- // 3. Check for orphaned phases in markdown
73
- for (const phaseId of phases.keys()) {
74
- if (!metadata.phases[phaseId]) {
75
- errors.push(`⚠️ ${relativePath}: Phase "${phaseId}" exists in markdown but is NOT defined in JSON metadata.`);
76
- }
77
- }
78
- }
79
- catch (e) {
80
- errors.push(`❌ ${relativePath}: Unexpected error: ${e.message}`);
81
- }
82
- }
83
- return {
84
- valid: errors.filter(e => e.startsWith('❌')).length === 0,
85
- errors
86
- };
87
- }
88
- // Run if called directly
89
- if (process.argv[1]?.includes('validate-workflows')) {
90
- const registryPath = process.argv[2] || (0, path_1.join)(process.cwd(), 'registry');
91
- const result = validateAllWorkflows(registryPath);
92
- result.errors.forEach(err => console.error(err));
93
- if (result.valid) {
94
- console.log('✅ All workflows validated successfully.');
95
- process.exit(0);
96
- }
97
- else {
98
- console.error('❌ Workflow validation failed.');
99
- process.exit(1);
100
- }
101
- }