fraim-framework 2.0.27 → 2.0.33

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 (130) hide show
  1. package/.github/workflows/deploy-fraim.yml +1 -1
  2. package/dist/registry/scripts/build-scripts-generator.js +205 -0
  3. package/dist/registry/scripts/cleanup-branch.js +258 -0
  4. package/dist/registry/scripts/evaluate-code-quality.js +66 -0
  5. package/dist/registry/scripts/exec-with-timeout.js +142 -0
  6. package/dist/registry/scripts/fraim-config.js +61 -0
  7. package/dist/registry/scripts/generate-engagement-emails.js +630 -0
  8. package/dist/registry/scripts/generic-issues-api.js +100 -0
  9. package/dist/registry/scripts/newsletter-helpers.js +731 -0
  10. package/dist/registry/scripts/openapi-generator.js +664 -0
  11. package/{registry/scripts/performance/profile-server.ts → dist/registry/scripts/performance/profile-server.js} +390 -368
  12. package/dist/registry/scripts/run-thank-you-workflow.js +92 -0
  13. package/dist/registry/scripts/send-newsletter-simple.js +85 -0
  14. package/dist/registry/scripts/send-thank-you-emails.js +54 -0
  15. package/dist/registry/scripts/validate-openapi-limits.js +311 -0
  16. package/dist/registry/scripts/validate-test-coverage.js +262 -0
  17. package/dist/registry/scripts/verify-test-coverage.js +66 -0
  18. package/dist/src/cli/commands/init.js +42 -15
  19. package/dist/src/cli/commands/sync.js +22 -5
  20. package/dist/src/cli/fraim.js +24 -22
  21. package/dist/src/cli/setup/first-run.js +13 -6
  22. package/dist/src/fraim/db-service.js +26 -15
  23. package/dist/src/fraim/issues.js +67 -0
  24. package/dist/src/fraim-mcp-server.js +272 -18
  25. package/dist/src/utils/git-utils.js +1 -1
  26. package/dist/src/utils/script-sync-utils.js +218 -0
  27. package/dist/tests/debug-tools.js +80 -0
  28. package/dist/tests/esm-compat.js +11 -0
  29. package/dist/tests/test-chalk-esm-issue.js +159 -0
  30. package/dist/tests/test-chalk-real-world.js +265 -0
  31. package/dist/tests/test-chalk-regression.js +377 -0
  32. package/dist/tests/test-chalk-resolution-issue.js +304 -0
  33. package/dist/tests/test-cli.js +70 -5
  34. package/dist/tests/test-end-to-end-hybrid-validation.js +349 -0
  35. package/dist/tests/test-first-run-journey.js +43 -3
  36. package/dist/tests/test-fraim-install-chalk-issue.js +254 -0
  37. package/dist/tests/test-fraim-issues.js +59 -0
  38. package/dist/tests/test-genericization.js +1 -1
  39. package/dist/tests/test-hybrid-script-execution.js +369 -0
  40. package/dist/tests/test-mcp-connection.js +166 -0
  41. package/dist/tests/test-mcp-issue-integration.js +152 -0
  42. package/dist/tests/test-mcp-lifecycle-methods.js +312 -0
  43. package/dist/tests/test-node-compatibility.js +93 -0
  44. package/dist/tests/test-npm-install.js +66 -0
  45. package/dist/tests/test-npm-resolution-diagnostic.js +140 -0
  46. package/dist/tests/test-prep-issue.js +4 -1
  47. package/dist/tests/test-script-location-independence.js +173 -0
  48. package/dist/tests/test-script-sync.js +557 -0
  49. package/dist/tests/test-session-rehydration.js +145 -0
  50. package/dist/tests/test-standalone.js +5 -11
  51. package/dist/tests/test-sync-version-update.js +1 -1
  52. package/dist/tests/test-telemetry.js +190 -0
  53. package/dist/tests/test-user-journey.js +8 -4
  54. package/dist/tests/test-utils.js +13 -0
  55. package/dist/tests/test-wizard.js +2 -2
  56. package/package.json +11 -9
  57. package/registry/agent-guardrails.md +62 -54
  58. package/registry/rules/agent-success-criteria.md +52 -0
  59. package/registry/rules/agent-testing-guidelines.md +7 -7
  60. package/registry/rules/communication.md +121 -121
  61. package/registry/rules/continuous-learning.md +54 -54
  62. package/registry/rules/ephemeral-execution.md +39 -24
  63. package/registry/rules/hitl-ppe-record-analysis.md +302 -302
  64. package/registry/rules/local-development.md +7 -5
  65. package/registry/rules/software-development-lifecycle.md +104 -104
  66. package/registry/rules/successful-debugging-patterns.md +29 -16
  67. package/registry/rules/telemetry.md +67 -0
  68. package/registry/scripts/code-quality-check.sh +559 -559
  69. package/registry/scripts/detect-tautological-tests.sh +38 -38
  70. package/registry/scripts/evaluate-code-quality.ts +1 -1
  71. package/registry/scripts/prep-issue.sh +16 -3
  72. package/registry/scripts/validate-openapi-limits.ts +366 -365
  73. package/registry/scripts/validate-test-coverage.ts +280 -280
  74. package/registry/scripts/verify-pr-comments.sh +70 -70
  75. package/registry/scripts/verify-test-coverage.ts +1 -1
  76. package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +53 -53
  77. package/registry/templates/evidence/Implementation-BugEvidence.md +85 -85
  78. package/registry/templates/evidence/Implementation-FeatureEvidence.md +120 -120
  79. package/registry/templates/marketing/HBR-ARTICLE-TEMPLATE.md +66 -0
  80. package/registry/workflows/bootstrap/create-architecture.md +2 -2
  81. package/registry/workflows/bootstrap/evaluate-code-quality.md +10 -4
  82. package/registry/workflows/bootstrap/verify-test-coverage.md +9 -3
  83. package/registry/workflows/customer-development/insight-analysis.md +156 -156
  84. package/registry/workflows/customer-development/interview-preparation.md +421 -421
  85. package/registry/workflows/customer-development/strategic-brainstorming.md +146 -146
  86. package/registry/workflows/customer-development/thank-customers.md +14 -2
  87. package/registry/workflows/customer-development/weekly-newsletter.md +27 -13
  88. package/registry/workflows/improve-fraim/contribute.md +32 -0
  89. package/registry/workflows/improve-fraim/file-issue.md +32 -0
  90. package/registry/workflows/marketing/hbr-article.md +73 -0
  91. package/registry/workflows/performance/analyze-performance.md +10 -4
  92. package/registry/workflows/product-building/design.md +3 -2
  93. package/registry/workflows/product-building/implement.md +10 -5
  94. package/registry/workflows/product-building/prep-issue.md +16 -18
  95. package/registry/workflows/product-building/resolve.md +8 -3
  96. package/registry/workflows/product-building/retrospect.md +3 -2
  97. package/registry/workflows/product-building/spec.md +5 -4
  98. package/registry/workflows/product-building/test.md +3 -2
  99. package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +562 -562
  100. package/registry/workflows/replicate/replicate-discovery.md +336 -0
  101. package/registry/workflows/replicate/replicate-to-issues.md +319 -0
  102. package/registry/workflows/reviewer/review-implementation-vs-design-spec.md +3 -3
  103. package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +669 -669
  104. package/tsconfig.json +2 -1
  105. package/.windsurf/rules/windsurf-rules.md +0 -7
  106. package/.windsurf/workflows/resolve-issue.md +0 -6
  107. package/.windsurf/workflows/retrospect.md +0 -6
  108. package/.windsurf/workflows/start-design.md +0 -6
  109. package/.windsurf/workflows/start-impl.md +0 -6
  110. package/.windsurf/workflows/start-spec.md +0 -6
  111. package/.windsurf/workflows/start-tests.md +0 -6
  112. package/bin/fraim.js +0 -23
  113. package/registry/scripts/build-scripts-generator.ts +0 -215
  114. package/registry/scripts/cleanup-branch.ts +0 -284
  115. package/registry/scripts/fraim-config.ts +0 -63
  116. package/registry/scripts/generate-engagement-emails.ts +0 -744
  117. package/registry/scripts/generic-issues-api.ts +0 -150
  118. package/registry/scripts/newsletter-helpers.ts +0 -874
  119. package/registry/scripts/openapi-generator.ts +0 -693
  120. package/registry/scripts/run-thank-you-workflow.ts +0 -122
  121. package/registry/scripts/send-newsletter-simple.ts +0 -104
  122. package/registry/scripts/send-thank-you-emails.ts +0 -57
  123. package/registry/workflows/replicate/re-implementation-strategy.md +0 -226
  124. package/registry/workflows/replicate/use-case-extraction.md +0 -135
  125. package/registry/workflows/replicate/visual-analysis.md +0 -154
  126. package/registry/workflows/replicate/website-discovery-analysis.md +0 -231
  127. package/sample_package.json +0 -18
  128. /package/registry/scripts/{replicate/comprehensive-explorer.py → comprehensive-explorer.py} +0 -0
  129. /package/registry/scripts/{replicate/interactive-explorer.py → interactive-explorer.py} +0 -0
  130. /package/registry/scripts/{replicate/scrape-site.py → scrape-site.py} +0 -0
@@ -42,6 +42,64 @@ const node_assert_1 = __importDefault(require("node:assert"));
42
42
  const fs_1 = __importDefault(require("fs"));
43
43
  const path_1 = __importDefault(require("path"));
44
44
  const os_1 = __importDefault(require("os"));
45
+ async function testRepoNameRequirement() {
46
+ console.log(' 🚀 Testing Repo Name Requirement...');
47
+ // Create a temp directory for the test project
48
+ const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'fraim-repo-test-'));
49
+ console.log(` 📂 Created temp dir: ${tempDir}`);
50
+ try {
51
+ const platform = process.platform;
52
+ const npx = platform === 'win32' ? 'npx.cmd' : 'npx';
53
+ const cliScript = (0, test_utils_1.resolveProjectPath)('dist/src/cli/fraim.js');
54
+ // Helper to run CLI commands
55
+ const runFraim = (args, cwdOverride) => {
56
+ return new Promise((resolve) => {
57
+ const ps = (0, node_child_process_1.spawn)(npx, ['node', `"${cliScript}"`, ...args], {
58
+ cwd: cwdOverride || tempDir,
59
+ env: { ...process.env, TEST_MODE: 'true' },
60
+ shell: true
61
+ });
62
+ let stdout = '';
63
+ let stderr = '';
64
+ ps.stdout.on('data', d => stdout += d.toString());
65
+ ps.stderr.on('data', d => stderr += d.toString());
66
+ ps.on('close', (code) => {
67
+ resolve({ stdout, stderr, code });
68
+ });
69
+ });
70
+ };
71
+ // Test 1: Init without git should fail
72
+ console.log(' Testing init without git remote (should fail)...');
73
+ const initWithoutGit = await runFraim(['init'], tempDir);
74
+ node_assert_1.default.strictEqual(initWithoutGit.code, 1, 'Init without git remote should fail');
75
+ node_assert_1.default.ok(initWithoutGit.stdout.includes('No git remote found') || initWithoutGit.stderr.includes('No git remote found'), 'Should show git remote error');
76
+ // Test 2: Init with git remote should succeed and use repo name
77
+ console.log(' Testing init with git remote (should succeed)...');
78
+ const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
79
+ execSync('git init', { cwd: tempDir });
80
+ execSync('git remote add origin https://github.com/test-owner/my-awesome-project.git', { cwd: tempDir });
81
+ const initWithGit = await runFraim(['init'], tempDir);
82
+ node_assert_1.default.strictEqual(initWithGit.code, 0, 'Init with git remote should succeed');
83
+ const configPath = path_1.default.join(tempDir, '.fraim', 'config.json');
84
+ node_assert_1.default.ok(fs_1.default.existsSync(configPath), 'config.json should exist');
85
+ const config = JSON.parse(fs_1.default.readFileSync(configPath, 'utf-8'));
86
+ node_assert_1.default.strictEqual(config.project.name, 'my-awesome-project', 'project.name should use repo name');
87
+ node_assert_1.default.strictEqual(config.git.repoName, 'my-awesome-project', 'git.repoName should match');
88
+ node_assert_1.default.strictEqual(config.git.repoOwner, 'test-owner', 'git.repoOwner should be detected');
89
+ console.log(' ✅ Repo name requirement verified!');
90
+ return true;
91
+ }
92
+ catch (error) {
93
+ console.error(' ❌ Repo name test failed:', error);
94
+ return false;
95
+ }
96
+ finally {
97
+ try {
98
+ fs_1.default.rmSync(tempDir, { recursive: true, force: true });
99
+ }
100
+ catch (e) { }
101
+ }
102
+ }
45
103
  async function testCliLifecycle() {
46
104
  console.log(' 🚀 Testing Fraim CLI Lifecycle...');
47
105
  // Create a temp directory for the test project
@@ -50,11 +108,11 @@ async function testCliLifecycle() {
50
108
  try {
51
109
  const platform = process.platform;
52
110
  const npx = platform === 'win32' ? 'npx.cmd' : 'npx';
53
- const cliScript = path_1.default.resolve(__dirname, '../src/cli/fraim.ts');
111
+ const cliScript = (0, test_utils_1.resolveProjectPath)('dist/src/cli/fraim.js');
54
112
  // Helper to run CLI commands
55
113
  const runFraim = (args, cwdOverride) => {
56
- return new Promise((resolve, reject) => {
57
- const ps = (0, node_child_process_1.spawn)(npx, ['tsx', `"${cliScript}"`, ...args], {
114
+ return new Promise((resolve) => {
115
+ const ps = (0, node_child_process_1.spawn)(npx, ['node', `"${cliScript}"`, ...args], {
58
116
  cwd: cwdOverride || tempDir,
59
117
  env: { ...process.env, TEST_MODE: 'true' }, // Disable interactive first-run
60
118
  shell: true
@@ -102,11 +160,12 @@ async function testCliLifecycle() {
102
160
  if (hasGit) {
103
161
  node_assert_1.default.strictEqual(config.git.repoOwner, 'test-owner', 'repoOwner should be detected from git');
104
162
  node_assert_1.default.strictEqual(config.git.repoName, 'test-repo', 'repoName should be detected from git');
163
+ node_assert_1.default.strictEqual(config.project.name, 'test-repo', 'project.name should use repo name, not folder name');
105
164
  }
106
165
  else {
107
166
  console.warn(' ⚠️ No .git found in tempDir, owner/repo detection skipped');
108
- node_assert_1.default.strictEqual(config.git.repoOwner, '', 'repoOwner should be empty if no git');
109
- node_assert_1.default.strictEqual(config.git.repoName, '', 'repoName should be empty if no git');
167
+ // Without git remote, init should fail with our new logic
168
+ console.warn(' ⚠️ Init should have failed without git remote in new logic');
110
169
  }
111
170
  node_assert_1.default.ok(config.customizations, 'config.customizations should exist');
112
171
  node_assert_1.default.strictEqual(config.customizations.workflowsPath, '.fraim/workflows');
@@ -157,6 +216,12 @@ async function runCliTest(testCase) {
157
216
  return await testCase.testFunction();
158
217
  }
159
218
  const testCases = [
219
+ {
220
+ name: 'Repo Name Requirement',
221
+ description: 'Tests that init requires git remote and uses repo name for project.name',
222
+ testFunction: testRepoNameRequirement,
223
+ tags: ['cli', 'fraim', 'git']
224
+ },
160
225
  {
161
226
  name: 'Fraim CLI Lifecycle',
162
227
  description: 'Tests init, sync, list, and doctor commands',
@@ -0,0 +1,349 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const node_child_process_1 = require("node:child_process");
40
+ const test_utils_1 = require("./test-utils");
41
+ const node_assert_1 = __importDefault(require("node:assert"));
42
+ const fs_1 = __importDefault(require("fs"));
43
+ const path_1 = __importDefault(require("path"));
44
+ const os_1 = __importDefault(require("os"));
45
+ async function testCompleteHybridWorkflow() {
46
+ console.log(' 🚀 Testing Complete Hybrid Workflow...');
47
+ const tempProjectDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'fraim-e2e-hybrid-'));
48
+ const userFraimDir = path_1.default.join(os_1.default.homedir(), '.fraim-e2e-test');
49
+ try {
50
+ // Clean up any existing test user directory
51
+ if (fs_1.default.existsSync(userFraimDir)) {
52
+ fs_1.default.rmSync(userFraimDir, { recursive: true, force: true });
53
+ }
54
+ console.log(` 📂 Project dir: ${tempProjectDir}`);
55
+ console.log(` 🏠 User dir: ${userFraimDir}`);
56
+ // Set up git repository
57
+ const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
58
+ execSync('git init', { cwd: tempProjectDir });
59
+ execSync('git remote add origin https://github.com/test-owner/e2e-hybrid-test.git', { cwd: tempProjectDir });
60
+ // Run fraim init with custom user directory
61
+ const platform = process.platform;
62
+ const npx = platform === 'win32' ? 'npx.cmd' : 'npx';
63
+ const cliScript = (0, test_utils_1.resolveProjectPath)('dist/src/cli/fraim.js');
64
+ const runFraim = (args) => {
65
+ return new Promise((resolve) => {
66
+ const ps = (0, node_child_process_1.spawn)(npx, ['node', `"${cliScript}"`, ...args], {
67
+ cwd: tempProjectDir,
68
+ env: {
69
+ ...process.env,
70
+ TEST_MODE: 'true',
71
+ FRAIM_USER_DIR: userFraimDir
72
+ },
73
+ shell: true
74
+ });
75
+ let stdout = '';
76
+ let stderr = '';
77
+ ps.stdout.on('data', d => stdout += d.toString());
78
+ ps.stderr.on('data', d => stderr += d.toString());
79
+ ps.on('close', (code) => {
80
+ resolve({ stdout, stderr, code });
81
+ });
82
+ });
83
+ };
84
+ // Initialize FRAIM
85
+ console.log(' Initializing FRAIM...');
86
+ const initRes = await runFraim(['init']);
87
+ node_assert_1.default.strictEqual(initRes.code, 0, 'Init should succeed');
88
+ // Verify user scripts directory was created and populated
89
+ const userScriptsDir = path_1.default.join(userFraimDir, 'scripts');
90
+ node_assert_1.default.ok(fs_1.default.existsSync(userScriptsDir), 'User scripts directory should exist');
91
+ // Verify self-contained scripts were synced
92
+ const expectedSelfContainedScripts = [
93
+ 'prep-issue.sh',
94
+ 'code-quality-check.sh',
95
+ 'exec-with-timeout.ts',
96
+ 'evaluate-code-quality.ts'
97
+ ];
98
+ for (const scriptName of expectedSelfContainedScripts) {
99
+ const scriptPath = path_1.default.join(userScriptsDir, scriptName);
100
+ node_assert_1.default.ok(fs_1.default.existsSync(scriptPath), `Self-contained script ${scriptName} should be synced`);
101
+ }
102
+ // Verify dependent scripts were NOT synced
103
+ const expectedDependentScripts = [
104
+ 'fraim-config.ts',
105
+ 'build-scripts-generator.ts'
106
+ ];
107
+ for (const scriptName of expectedDependentScripts) {
108
+ const scriptPath = path_1.default.join(userScriptsDir, scriptName);
109
+ node_assert_1.default.ok(!fs_1.default.existsSync(scriptPath), `Dependent script ${scriptName} should NOT be synced`);
110
+ }
111
+ // Test that self-contained scripts can execute from user directory
112
+ console.log(' Testing self-contained script execution...');
113
+ // Create a simple test script to verify execution context
114
+ const testScript = `#!/usr/bin/env node
115
+ console.log('WORKING_DIR=' + process.cwd());
116
+ console.log('SCRIPT_DIR=' + __dirname);
117
+
118
+ // Verify we can read project config
119
+ const fs = require('fs');
120
+ const path = require('path');
121
+
122
+ try {
123
+ const configPath = path.join(process.cwd(), '.fraim', 'config.json');
124
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
125
+ console.log('PROJECT_NAME=' + config.project.name);
126
+ console.log('SUCCESS=true');
127
+ } catch (error) {
128
+ console.log('ERROR=' + error.message);
129
+ console.log('SUCCESS=false');
130
+ }
131
+ `;
132
+ const testScriptPath = path_1.default.join(userScriptsDir, 'test-execution.js');
133
+ fs_1.default.writeFileSync(testScriptPath, testScript);
134
+ // Execute the test script from project directory
135
+ let output;
136
+ try {
137
+ output = execSync(`node "${testScriptPath}"`, {
138
+ cwd: tempProjectDir,
139
+ encoding: 'utf-8'
140
+ });
141
+ }
142
+ catch (error) {
143
+ throw new Error(`Script execution failed: ${error.message}`);
144
+ }
145
+ // Validate execution results
146
+ node_assert_1.default.ok(output.includes('SUCCESS=true'), 'Script should execute successfully');
147
+ node_assert_1.default.ok(output.includes('PROJECT_NAME=e2e-hybrid-test'), 'Should read project config correctly');
148
+ // Verify working directory vs script directory are different
149
+ const workingDirMatch = output.match(/WORKING_DIR=(.+)/);
150
+ const scriptDirMatch = output.match(/SCRIPT_DIR=(.+)/);
151
+ if (workingDirMatch && scriptDirMatch) {
152
+ const workingDir = workingDirMatch[1].trim();
153
+ const scriptDir = scriptDirMatch[1].trim();
154
+ node_assert_1.default.notStrictEqual(workingDir, scriptDir, 'Working directory should be different from script directory');
155
+ }
156
+ // Test sync command updates
157
+ console.log(' Testing sync command...');
158
+ const syncRes = await runFraim(['sync']);
159
+ // Sync might warn but should not fail completely
160
+ if (syncRes.code !== 0) {
161
+ console.log(' ⚠️ Sync returned non-zero (environment dependent)');
162
+ }
163
+ // Verify scripts are still present after sync
164
+ for (const scriptName of expectedSelfContainedScripts) {
165
+ const scriptPath = path_1.default.join(userScriptsDir, scriptName);
166
+ node_assert_1.default.ok(fs_1.default.existsSync(scriptPath), `Script ${scriptName} should still exist after sync`);
167
+ }
168
+ console.log(' ✅ Complete hybrid workflow verified!');
169
+ return true;
170
+ }
171
+ catch (error) {
172
+ console.error(' ❌ End-to-end hybrid test failed:', error);
173
+ return false;
174
+ }
175
+ finally {
176
+ try {
177
+ fs_1.default.rmSync(tempProjectDir, { recursive: true, force: true });
178
+ if (fs_1.default.existsSync(userFraimDir)) {
179
+ fs_1.default.rmSync(userFraimDir, { recursive: true, force: true });
180
+ }
181
+ }
182
+ catch (e) { }
183
+ }
184
+ }
185
+ async function testScriptCategorizationAccuracy() {
186
+ console.log(' 🚀 Testing Script Categorization Accuracy...');
187
+ try {
188
+ const registryPath = path_1.default.resolve(__dirname, '../registry');
189
+ if (!fs_1.default.existsSync(registryPath)) {
190
+ console.log(' ⚠️ Registry not found, skipping categorization accuracy test');
191
+ return true;
192
+ }
193
+ const { getRegistryScripts, getDependentRegistryScripts, isScriptSelfContained } = await Promise.resolve().then(() => __importStar(require('../src/utils/script-sync-utils')));
194
+ const selfContainedScripts = getRegistryScripts(registryPath);
195
+ const dependentScripts = getDependentRegistryScripts(registryPath);
196
+ console.log(` 📊 Categorization results:`);
197
+ console.log(` Self-contained: ${selfContainedScripts.length} scripts`);
198
+ console.log(` Dependent: ${dependentScripts.length} scripts`);
199
+ // Verify specific known categorizations
200
+ const knownSelfContained = [
201
+ 'prep-issue.sh',
202
+ 'code-quality-check.sh',
203
+ 'exec-with-timeout.ts',
204
+ 'evaluate-code-quality.ts'
205
+ ];
206
+ const knownDependent = [
207
+ 'fraim-config.ts',
208
+ 'build-scripts-generator.ts'
209
+ ];
210
+ const selfContainedNames = selfContainedScripts.map(p => path_1.default.basename(p));
211
+ const dependentNames = dependentScripts.map(p => path_1.default.basename(p));
212
+ for (const scriptName of knownSelfContained) {
213
+ const scriptPath = path_1.default.join(registryPath, 'scripts', scriptName);
214
+ if (fs_1.default.existsSync(scriptPath)) {
215
+ node_assert_1.default.ok(selfContainedNames.includes(scriptName), `${scriptName} should be categorized as self-contained`);
216
+ node_assert_1.default.ok(isScriptSelfContained(scriptPath), `${scriptName} should be detected as self-contained`);
217
+ }
218
+ }
219
+ for (const scriptName of knownDependent) {
220
+ const scriptPath = path_1.default.join(registryPath, 'scripts', scriptName);
221
+ if (fs_1.default.existsSync(scriptPath)) {
222
+ node_assert_1.default.ok(dependentNames.includes(scriptName), `${scriptName} should be categorized as dependent`);
223
+ node_assert_1.default.ok(!isScriptSelfContained(scriptPath), `${scriptName} should be detected as dependent`);
224
+ }
225
+ }
226
+ // Verify no script is in both categories
227
+ const intersection = selfContainedNames.filter(name => dependentNames.includes(name));
228
+ node_assert_1.default.strictEqual(intersection.length, 0, 'No script should be in both categories');
229
+ // Verify all scripts are categorized
230
+ const allScripts = fs_1.default.readdirSync(path_1.default.join(registryPath, 'scripts'))
231
+ .filter(file => file.endsWith('.sh') || file.endsWith('.ts') || file.endsWith('.js'));
232
+ const categorizedScripts = [...selfContainedNames, ...dependentNames];
233
+ const uncategorized = allScripts.filter(script => !categorizedScripts.includes(script));
234
+ node_assert_1.default.strictEqual(uncategorized.length, 0, `All scripts should be categorized. Uncategorized: ${uncategorized.join(', ')}`);
235
+ console.log(' ✅ Script categorization accuracy verified!');
236
+ return true;
237
+ }
238
+ catch (error) {
239
+ console.error(' ❌ Script categorization accuracy test failed:', error);
240
+ return false;
241
+ }
242
+ }
243
+ async function testWorkflowInstructionConsistency() {
244
+ console.log(' 🚀 Testing Workflow Instruction Consistency...');
245
+ try {
246
+ const registryPath = path_1.default.resolve(__dirname, '../registry');
247
+ if (!fs_1.default.existsSync(registryPath)) {
248
+ console.log(' ⚠️ Registry not found, skipping workflow consistency test');
249
+ return true;
250
+ }
251
+ // Check that workflows use the correct execution patterns
252
+ const workflowsPath = path_1.default.join(registryPath, 'workflows');
253
+ const rulesPath = path_1.default.join(registryPath, 'rules');
254
+ // Get all markdown files
255
+ const getMarkdownFiles = (dir) => {
256
+ if (!fs_1.default.existsSync(dir))
257
+ return [];
258
+ const files = [];
259
+ const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
260
+ for (const entry of entries) {
261
+ const fullPath = path_1.default.join(dir, entry.name);
262
+ if (entry.isDirectory()) {
263
+ files.push(...getMarkdownFiles(fullPath));
264
+ }
265
+ else if (entry.name.endsWith('.md')) {
266
+ files.push(fullPath);
267
+ }
268
+ }
269
+ return files;
270
+ };
271
+ const allMarkdownFiles = [
272
+ ...getMarkdownFiles(workflowsPath),
273
+ ...getMarkdownFiles(rulesPath)
274
+ ];
275
+ let inconsistencies = 0;
276
+ for (const filePath of allMarkdownFiles) {
277
+ const content = fs_1.default.readFileSync(filePath, 'utf-8');
278
+ const relativePath = path_1.default.relative(registryPath, filePath);
279
+ // Check for old ephemeral execution patterns (but allow them for dependent scripts)
280
+ const oldPatterns = [
281
+ /tmp\/.*\.ts/g,
282
+ /tmp\/.*\.sh/g
283
+ ];
284
+ for (const pattern of oldPatterns) {
285
+ const matches = content.match(pattern);
286
+ if (matches) {
287
+ // Allow tmp/ usage if it's accompanied by get_fraim_file (ephemeral execution for dependent scripts)
288
+ const hasGetFramFile = /get_fraim_file.*scripts/.test(content);
289
+ if (!hasGetFramFile) {
290
+ console.log(` ⚠️ Found old pattern in ${relativePath}: ${matches.join(', ')}`);
291
+ inconsistencies++;
292
+ }
293
+ }
294
+ }
295
+ // Check for correct hybrid patterns
296
+ const hybridPatterns = [
297
+ /~\/\.fraim\/scripts\/.*\.ts/g,
298
+ /~\/\.fraim\/scripts\/.*\.sh/g,
299
+ /%USERPROFILE%\\\.fraim\\scripts\\/g
300
+ ];
301
+ let hasHybridPatterns = false;
302
+ for (const pattern of hybridPatterns) {
303
+ if (pattern.test(content)) {
304
+ hasHybridPatterns = true;
305
+ break;
306
+ }
307
+ }
308
+ // If file mentions scripts but doesn't use hybrid patterns, it might be an issue
309
+ if (content.includes('scripts/') && !hasHybridPatterns && inconsistencies === 0) {
310
+ // This is just informational, not necessarily an error
311
+ console.log(` ℹ️ File ${relativePath} mentions scripts but may not use hybrid patterns`);
312
+ }
313
+ }
314
+ if (inconsistencies > 0) {
315
+ console.log(` ❌ Found ${inconsistencies} workflow instruction inconsistencies`);
316
+ return false;
317
+ }
318
+ console.log(' ✅ Workflow instruction consistency verified!');
319
+ return true;
320
+ }
321
+ catch (error) {
322
+ console.error(' ❌ Workflow instruction consistency test failed:', error);
323
+ return false;
324
+ }
325
+ }
326
+ async function runEndToEndTest(testCase) {
327
+ return await testCase.testFunction();
328
+ }
329
+ const testCases = [
330
+ {
331
+ name: 'Complete Hybrid Workflow',
332
+ description: 'Tests the entire hybrid script execution workflow from init to execution',
333
+ testFunction: testCompleteHybridWorkflow,
334
+ tags: ['e2e', 'hybrid', 'workflow']
335
+ },
336
+ {
337
+ name: 'Script Categorization Accuracy',
338
+ description: 'Validates that all scripts are correctly categorized as self-contained or dependent',
339
+ testFunction: testScriptCategorizationAccuracy,
340
+ tags: ['e2e', 'categorization', 'validation']
341
+ },
342
+ {
343
+ name: 'Workflow Instruction Consistency',
344
+ description: 'Ensures all workflow files use the correct hybrid execution patterns',
345
+ testFunction: testWorkflowInstructionConsistency,
346
+ tags: ['e2e', 'consistency', 'workflows']
347
+ }
348
+ ];
349
+ (0, test_utils_1.runTests)(testCases, runEndToEndTest, 'End-to-End Hybrid Validation Tests');
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -13,11 +46,10 @@ const os_1 = __importDefault(require("os"));
13
46
  * Helper to run the interactive first-run experience within 'fraim init'
14
47
  */
15
48
  async function runInteractiveInit(cwd, interactions) {
16
- const projectRoot = path_1.default.resolve(__dirname, '..');
17
- const cliScript = path_1.default.resolve(projectRoot, 'src/cli/fraim.ts');
49
+ const cliScript = (0, test_utils_1.resolveProjectPath)('dist/src/cli/fraim.js');
18
50
  const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';
19
51
  return new Promise(resolve => {
20
- const ps = (0, node_child_process_1.spawn)(npx, ['tsx', `"${cliScript}"`, 'init'], {
52
+ const ps = (0, node_child_process_1.spawn)(npx, ['node', `"${cliScript}"`, 'init'], {
21
53
  cwd,
22
54
  env: { ...process.env, CI: 'false', TEST_MODE: 'false' },
23
55
  shell: true,
@@ -57,6 +89,10 @@ const testCases = [
57
89
  testFunction: async () => {
58
90
  const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'fraim-jour-1-'));
59
91
  try {
92
+ // Set up git repository with remote (required for new init logic)
93
+ const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
94
+ execSync('git init', { cwd: tempDir });
95
+ execSync('git remote add origin https://github.com/test-owner/test-repo.git', { cwd: tempDir });
60
96
  const res = await runInteractiveInit(tempDir, [
61
97
  { prompt: 'Do you have a FRAIM key?', response: 'key-123' },
62
98
  { prompt: 'Do you have an architecture document', response: 'n' }
@@ -83,6 +119,10 @@ const testCases = [
83
119
  const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'fraim-jour-2-'));
84
120
  const archPath = 'docs/my-architecture.md';
85
121
  try {
122
+ // Set up git repository with remote (required for new init logic)
123
+ const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
124
+ execSync('git init', { cwd: tempDir });
125
+ execSync('git remote add origin https://github.com/test-owner/test-repo.git', { cwd: tempDir });
86
126
  fs_1.default.mkdirSync(path_1.default.join(tempDir, '.fraim'), { recursive: true });
87
127
  fs_1.default.writeFileSync(path_1.default.join(tempDir, '.fraim', 'config.json'), JSON.stringify({ version: '1.0.0' }));
88
128
  fs_1.default.mkdirSync(path_1.default.join(tempDir, 'docs'), { recursive: true });