fraim-framework 2.0.35 → 2.0.37

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 (159) hide show
  1. package/dist/registry/scripts/cleanup-branch.js +62 -33
  2. package/dist/registry/scripts/generate-engagement-emails.js +119 -44
  3. package/dist/registry/scripts/newsletter-helpers.js +208 -268
  4. package/dist/registry/scripts/profile-server.js +387 -0
  5. package/dist/scripts/build-stub-registry.js +108 -0
  6. package/dist/src/cli/commands/doctor.js +5 -5
  7. package/dist/src/cli/commands/sync.js +33 -19
  8. package/dist/tests/test-client-scripts-validation.js +133 -0
  9. package/dist/tests/test-package-size.js +88 -0
  10. package/dist/tests/test-script-location-independence.js +76 -28
  11. package/dist/tests/test-stub-registry.js +120 -0
  12. package/dist/tests/test-sync-stubs.js +143 -0
  13. package/package.json +7 -9
  14. package/registry/scripts/cleanup-branch.ts +341 -0
  15. package/registry/scripts/generate-engagement-emails.ts +830 -0
  16. package/registry/scripts/markdown-to-pdf.js +7 -3
  17. package/registry/scripts/newsletter-helpers.ts +777 -0
  18. package/registry/scripts/profile-server.ts +424 -0
  19. package/registry/scripts/run-thank-you-workflow.ts +122 -0
  20. package/registry/scripts/send-newsletter-simple.ts +102 -0
  21. package/registry/scripts/send-thank-you-emails.ts +57 -0
  22. package/registry/stubs/workflows/bootstrap/create-architecture.md +11 -0
  23. package/registry/stubs/workflows/bootstrap/detect-broken-windows.md +11 -0
  24. package/registry/stubs/workflows/bootstrap/evaluate-code-quality.md +11 -0
  25. package/registry/stubs/workflows/bootstrap/verify-test-coverage.md +11 -0
  26. package/registry/stubs/workflows/business-development/create-business-plan.md +11 -0
  27. package/registry/stubs/workflows/business-development/ideate-business-opportunity.md +11 -0
  28. package/registry/stubs/workflows/business-development/price-product.md +18 -0
  29. package/registry/stubs/workflows/convert-to-pdf.md +11 -0
  30. package/registry/stubs/workflows/customer-development/insight-analysis.md +11 -0
  31. package/registry/stubs/workflows/customer-development/insight-triage.md +11 -0
  32. package/registry/stubs/workflows/customer-development/interview-preparation.md +11 -0
  33. package/registry/stubs/workflows/customer-development/linkedin-outreach.md +11 -0
  34. package/registry/stubs/workflows/customer-development/strategic-brainstorming.md +11 -0
  35. package/registry/stubs/workflows/customer-development/thank-customers.md +11 -0
  36. package/registry/stubs/workflows/customer-development/weekly-newsletter.md +11 -0
  37. package/registry/stubs/workflows/deploy/cloud-deployment.md +11 -0
  38. package/registry/stubs/workflows/improve-fraim/contribute.md +11 -0
  39. package/registry/stubs/workflows/improve-fraim/file-issue.md +11 -0
  40. package/registry/stubs/workflows/marketing/content-creation.md +11 -0
  41. package/registry/stubs/workflows/marketing/hbr-article.md +11 -0
  42. package/registry/stubs/workflows/marketing/launch-checklist.md +11 -0
  43. package/registry/stubs/workflows/marketing/marketing-strategy.md +11 -0
  44. package/registry/stubs/workflows/marketing/storytelling.md +11 -0
  45. package/registry/stubs/workflows/performance/analyze-performance.md +11 -0
  46. package/registry/stubs/workflows/product-building/design.md +11 -0
  47. package/registry/stubs/workflows/product-building/implement.md +12 -0
  48. package/registry/stubs/workflows/product-building/iterate-on-pr-comments.md +11 -0
  49. package/registry/stubs/workflows/product-building/prep-issue.md +11 -0
  50. package/registry/stubs/workflows/product-building/prototype.md +11 -0
  51. package/registry/stubs/workflows/product-building/resolve.md +11 -0
  52. package/registry/stubs/workflows/product-building/retrospect.md +11 -0
  53. package/registry/stubs/workflows/product-building/spec.md +11 -0
  54. package/registry/stubs/workflows/product-building/test.md +11 -0
  55. package/registry/stubs/workflows/quality-assurance/browser-validation.md +11 -0
  56. package/registry/stubs/workflows/quality-assurance/iterative-improvement-cycle.md +11 -0
  57. package/registry/stubs/workflows/replicate/replicate-discovery.md +11 -0
  58. package/registry/stubs/workflows/replicate/replicate-to-issues.md +11 -0
  59. package/registry/stubs/workflows/reviewer/review-implementation-vs-design-spec.md +11 -0
  60. package/registry/stubs/workflows/reviewer/review-implementation-vs-feature-spec.md +11 -0
  61. package/registry/stubs/workflows/startup-credits/aws-activate-application.md +11 -0
  62. package/registry/stubs/workflows/startup-credits/google-cloud-application.md +11 -0
  63. package/registry/stubs/workflows/startup-credits/microsoft-azure-application.md +11 -0
  64. package/.github/workflows/ci.yml +0 -65
  65. package/.github/workflows/deploy-fraim.yml +0 -87
  66. package/.github/workflows/phase-change.yml +0 -251
  67. package/.github/workflows/status-change.yml +0 -68
  68. package/.github/workflows/sync-on-pr-review.yml +0 -66
  69. package/examples/simple-webapp/TESTING.md +0 -62
  70. package/examples/simple-webapp/example-test.ts +0 -186
  71. package/registry/github/workflows/ci.yml +0 -51
  72. package/registry/github/workflows/phase-change.yml +0 -251
  73. package/registry/github/workflows/status-change.yml +0 -68
  74. package/registry/github/workflows/sync-on-pr-review.yml +0 -66
  75. package/registry/mcp-template.jsonc +0 -29
  76. package/registry/rules/agent-success-criteria.md +0 -52
  77. package/registry/rules/agent-testing-guidelines.md +0 -502
  78. package/registry/rules/architecture.md +0 -52
  79. package/registry/rules/communication.md +0 -122
  80. package/registry/rules/continuous-learning.md +0 -55
  81. package/registry/rules/debugging-multitenancy-issues.md +0 -85
  82. package/registry/rules/ephemeral-execution.md +0 -57
  83. package/registry/rules/git-safe-commands.md +0 -34
  84. package/registry/rules/hitl-ppe-record-analysis.md +0 -302
  85. package/registry/rules/integrity-and-test-ethics.md +0 -275
  86. package/registry/rules/local-development.md +0 -254
  87. package/registry/rules/merge-requirements.md +0 -231
  88. package/registry/rules/simplicity.md +0 -118
  89. package/registry/rules/software-development-lifecycle.md +0 -105
  90. package/registry/rules/spike-first-development.md +0 -205
  91. package/registry/rules/successful-debugging-patterns.md +0 -491
  92. package/registry/rules/telemetry.md +0 -67
  93. package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +0 -53
  94. package/registry/templates/bootstrap/CODE-QUALITY-REPORT-TEMPLATE.md +0 -37
  95. package/registry/templates/bootstrap/TEST-COVERAGE-REPORT-TEMPLATE.md +0 -35
  96. package/registry/templates/business-development/IDEATION-REPORT-TEMPLATE.md +0 -29
  97. package/registry/templates/business-development/PRICING-STRATEGY-TEMPLATE.md +0 -126
  98. package/registry/templates/customer-development/customer-interview-template.md +0 -99
  99. package/registry/templates/customer-development/follow-up-email-templates.md +0 -132
  100. package/registry/templates/customer-development/insight-analysis-template.md +0 -74
  101. package/registry/templates/customer-development/strategic-recommendations-template.md +0 -53
  102. package/registry/templates/customer-development/thank-you-email-template.html +0 -124
  103. package/registry/templates/customer-development/thank-you-note-template.md +0 -16
  104. package/registry/templates/customer-development/triage-log-template.md +0 -278
  105. package/registry/templates/customer-development/weekly-newsletter-template.html +0 -204
  106. package/registry/templates/evidence/Design-Evidence.md +0 -30
  107. package/registry/templates/evidence/Implementation-BugEvidence.md +0 -86
  108. package/registry/templates/evidence/Implementation-FeatureEvidence.md +0 -121
  109. package/registry/templates/evidence/Spec-Evidence.md +0 -19
  110. package/registry/templates/help/HelpNeeded.md +0 -14
  111. package/registry/templates/marketing/HBR-ARTICLE-TEMPLATE.md +0 -66
  112. package/registry/templates/marketing/STORYTELLING-TEMPLATE.md +0 -130
  113. package/registry/templates/replicate/implementation-checklist.md +0 -39
  114. package/registry/templates/replicate/use-cases-template.md +0 -88
  115. package/registry/templates/retrospective/RETROSPECTIVE-TEMPLATE.md +0 -55
  116. package/registry/templates/specs/BUGSPEC-TEMPLATE.md +0 -37
  117. package/registry/templates/specs/FEATURESPEC-TEMPLATE.md +0 -29
  118. package/registry/templates/specs/TECHSPEC-TEMPLATE.md +0 -39
  119. package/registry/workflows/bootstrap/create-architecture.md +0 -38
  120. package/registry/workflows/bootstrap/evaluate-code-quality.md +0 -36
  121. package/registry/workflows/bootstrap/verify-test-coverage.md +0 -37
  122. package/registry/workflows/business-development/create-business-plan.md +0 -737
  123. package/registry/workflows/business-development/ideate-business-opportunity.md +0 -55
  124. package/registry/workflows/business-development/price-product.md +0 -325
  125. package/registry/workflows/convert-to-pdf.md +0 -235
  126. package/registry/workflows/customer-development/insight-analysis.md +0 -156
  127. package/registry/workflows/customer-development/insight-triage.md +0 -933
  128. package/registry/workflows/customer-development/interview-preparation.md +0 -421
  129. package/registry/workflows/customer-development/linkedin-outreach.md +0 -593
  130. package/registry/workflows/customer-development/strategic-brainstorming.md +0 -146
  131. package/registry/workflows/customer-development/thank-customers.md +0 -203
  132. package/registry/workflows/customer-development/weekly-newsletter.md +0 -366
  133. package/registry/workflows/deploy/cloud-deployment.md +0 -310
  134. package/registry/workflows/improve-fraim/contribute.md +0 -32
  135. package/registry/workflows/improve-fraim/file-issue.md +0 -32
  136. package/registry/workflows/marketing/content-creation.md +0 -37
  137. package/registry/workflows/marketing/hbr-article.md +0 -73
  138. package/registry/workflows/marketing/launch-checklist.md +0 -37
  139. package/registry/workflows/marketing/marketing-strategy.md +0 -45
  140. package/registry/workflows/marketing/storytelling.md +0 -65
  141. package/registry/workflows/performance/analyze-performance.md +0 -65
  142. package/registry/workflows/product-building/design.md +0 -130
  143. package/registry/workflows/product-building/implement.md +0 -315
  144. package/registry/workflows/product-building/iterate-on-pr-comments.md +0 -70
  145. package/registry/workflows/product-building/prep-issue.md +0 -43
  146. package/registry/workflows/product-building/prototype.md +0 -60
  147. package/registry/workflows/product-building/resolve.md +0 -164
  148. package/registry/workflows/product-building/retrospect.md +0 -86
  149. package/registry/workflows/product-building/spec.md +0 -117
  150. package/registry/workflows/product-building/test.md +0 -120
  151. package/registry/workflows/quality-assurance/browser-validation.md +0 -221
  152. package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +0 -562
  153. package/registry/workflows/replicate/replicate-discovery.md +0 -336
  154. package/registry/workflows/replicate/replicate-to-issues.md +0 -319
  155. package/registry/workflows/reviewer/review-implementation-vs-design-spec.md +0 -632
  156. package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +0 -669
  157. package/registry/workflows/startup-credits/aws-activate-application.md +0 -535
  158. package/registry/workflows/startup-credits/google-cloud-application.md +0 -647
  159. package/registry/workflows/startup-credits/microsoft-azure-application.md +0 -538
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const test_utils_1 = require("./test-utils");
7
+ const child_process_1 = require("child_process");
8
+ const fs_1 = require("fs");
9
+ const path_1 = require("path");
10
+ const node_assert_1 = __importDefault(require("node:assert"));
11
+ async function testNoFraimInternalImports() {
12
+ console.log(' 🧪 Testing registry scripts do not import FRAIM internals...');
13
+ try {
14
+ const scriptsDir = (0, path_1.join)(process.cwd(), 'registry', 'scripts');
15
+ const scriptFiles = ['cleanup-branch.ts', 'generate-engagement-emails.ts', 'newsletter-helpers.ts'];
16
+ for (const scriptFile of scriptFiles) {
17
+ const scriptPath = (0, path_1.join)(scriptsDir, scriptFile);
18
+ if ((0, fs_1.existsSync)(scriptPath)) {
19
+ const content = (0, fs_1.readFileSync)(scriptPath, 'utf-8');
20
+ // Should not import from ../../src/
21
+ (0, node_assert_1.default)(!content.includes('../../src/'), `${scriptFile} should not import from ../../src/`);
22
+ // Should not import fraim-config
23
+ (0, node_assert_1.default)(!content.includes('./fraim-config'), `${scriptFile} should not import fraim-config`);
24
+ // Should have self-contained config loading
25
+ (0, node_assert_1.default)(content.includes('.fraim/config.json') || content.includes('loadClientConfig'), `${scriptFile} should read config directly`);
26
+ }
27
+ }
28
+ return true;
29
+ }
30
+ catch (e) {
31
+ console.error(' ❌ FRAIM internal imports check failed:', e);
32
+ return false;
33
+ }
34
+ }
35
+ async function testInlineUtilityFunctions() {
36
+ console.log(' 🧪 Testing scripts have inline utility functions...');
37
+ try {
38
+ const scriptsDir = (0, path_1.join)(process.cwd(), 'registry', 'scripts');
39
+ const scriptPath = (0, path_1.join)(scriptsDir, 'cleanup-branch.ts');
40
+ if ((0, fs_1.existsSync)(scriptPath)) {
41
+ const content = (0, fs_1.readFileSync)(scriptPath, 'utf-8');
42
+ // Should have inline extractIssueNumber function
43
+ (0, node_assert_1.default)(content.includes('function extractIssueNumber'), 'cleanup-branch.ts should have inline extractIssueNumber function');
44
+ // Should have inline getCurrentGitBranch function
45
+ (0, node_assert_1.default)(content.includes('function getCurrentGitBranch'), 'cleanup-branch.ts should have inline getCurrentGitBranch function');
46
+ }
47
+ return true;
48
+ }
49
+ catch (e) {
50
+ console.error(' ❌ Inline utility functions check failed:', e);
51
+ return false;
52
+ }
53
+ }
54
+ async function testConfigLoadingFromJson() {
55
+ console.log(' 🧪 Testing scripts load config from .fraim/config.json...');
56
+ try {
57
+ const scriptsDir = (0, path_1.join)(process.cwd(), 'registry', 'scripts');
58
+ const scriptPath = (0, path_1.join)(scriptsDir, 'generate-engagement-emails.ts');
59
+ if ((0, fs_1.existsSync)(scriptPath)) {
60
+ const content = (0, fs_1.readFileSync)(scriptPath, 'utf-8');
61
+ // Should have loadClientConfig function
62
+ (0, node_assert_1.default)(content.includes('function loadClientConfig'), 'generate-engagement-emails.ts should have loadClientConfig function');
63
+ // Should read .fraim/config.json
64
+ (0, node_assert_1.default)(content.includes('.fraim/config.json'), 'generate-engagement-emails.ts should read .fraim/config.json');
65
+ }
66
+ return true;
67
+ }
68
+ catch (e) {
69
+ console.error(' ❌ Config loading check failed:', e);
70
+ return false;
71
+ }
72
+ }
73
+ async function testRegistryPathValidator() {
74
+ console.log(' 🧪 Testing registry path validator passes...');
75
+ try {
76
+ (0, child_process_1.execSync)('npm run validate:registry', { stdio: 'pipe' });
77
+ return true;
78
+ }
79
+ catch (error) {
80
+ console.error(' ❌ Registry validation failed:', error.message);
81
+ return false;
82
+ }
83
+ }
84
+ async function testScriptExecutability() {
85
+ console.log(' 🧪 Testing scripts are executable without import errors...');
86
+ try {
87
+ const scriptsDir = (0, path_1.join)(process.cwd(), 'registry', 'scripts');
88
+ const scriptPath = (0, path_1.join)(scriptsDir, 'cleanup-branch.ts');
89
+ if ((0, fs_1.existsSync)(scriptPath)) {
90
+ // Test that the script can be parsed without import errors
91
+ const result = (0, child_process_1.execSync)(`npx tsx ${scriptPath} --help`, {
92
+ stdio: 'pipe',
93
+ encoding: 'utf-8'
94
+ });
95
+ // Should show help without errors
96
+ (0, node_assert_1.default)(result.includes('Branch Cleanup Script'), 'cleanup-branch.ts should show help without import errors');
97
+ }
98
+ return true;
99
+ }
100
+ catch (error) {
101
+ // Check if it's an import error
102
+ if (error.message.includes('Cannot find module') || error.message.includes('../../src/')) {
103
+ console.error(' ❌ Script has import errors:', error.message);
104
+ return false;
105
+ }
106
+ // Other errors (like missing git repo) are acceptable for this test
107
+ return true;
108
+ }
109
+ }
110
+ const testCases = [
111
+ {
112
+ name: 'Registry scripts should not import FRAIM internals',
113
+ testFunction: testNoFraimInternalImports
114
+ },
115
+ {
116
+ name: 'Scripts should have inline utility functions',
117
+ testFunction: testInlineUtilityFunctions
118
+ },
119
+ {
120
+ name: 'Scripts should load config from .fraim/config.json',
121
+ testFunction: testConfigLoadingFromJson
122
+ },
123
+ {
124
+ name: 'Registry path validator should pass',
125
+ testFunction: testRegistryPathValidator
126
+ },
127
+ {
128
+ name: 'Scripts should be executable without import errors',
129
+ testFunction: testScriptExecutability
130
+ }
131
+ ];
132
+ // Run the tests using the standard test runner
133
+ (0, test_utils_1.runTests)(testCases, async (testCase) => testCase.testFunction(), 'Client-Side Script Validation');
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env tsx
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const node_test_1 = require("node:test");
8
+ const node_assert_1 = __importDefault(require("node:assert"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ /**
12
+ * Test to verify package size reduction from stub-only registry
13
+ */
14
+ (0, node_test_1.test)('Stub registry is significantly smaller than full registry', () => {
15
+ const fullWorkflowsPath = path_1.default.join(__dirname, '..', 'registry', 'workflows');
16
+ const stubWorkflowsPath = path_1.default.join(__dirname, '..', 'registry', 'stubs', 'workflows');
17
+ (0, node_assert_1.default)(fs_1.default.existsSync(fullWorkflowsPath), 'Full workflows directory should exist');
18
+ (0, node_assert_1.default)(fs_1.default.existsSync(stubWorkflowsPath), 'Stub workflows directory should exist');
19
+ // Calculate sizes
20
+ const fullSize = getDirectorySize(fullWorkflowsPath);
21
+ const stubSize = getDirectorySize(stubWorkflowsPath);
22
+ console.log(`Full workflows size: ${fullSize} bytes`);
23
+ console.log(`Stub workflows size: ${stubSize} bytes`);
24
+ console.log(`Size reduction: ${Math.round((1 - stubSize / fullSize) * 100)}%`);
25
+ // Stubs should be significantly smaller
26
+ (0, node_assert_1.default)(stubSize < fullSize * 0.3, `Stubs should be <30% of full size (actual: ${Math.round(stubSize / fullSize * 100)}%)`);
27
+ // Should have same number of files
28
+ const fullFiles = getAllFiles(fullWorkflowsPath, '.md');
29
+ const stubFiles = getAllFiles(stubWorkflowsPath, '.md');
30
+ node_assert_1.default.strictEqual(stubFiles.length, fullFiles.length, 'Should have same number of workflow files');
31
+ console.log(`✅ Package size reduced by ${Math.round((1 - stubSize / fullSize) * 100)}% while maintaining all ${stubFiles.length} workflows`);
32
+ });
33
+ (0, node_test_1.test)('Package excludes full workflows and templates', () => {
34
+ const packageJsonPath = path_1.default.join(__dirname, '..', 'package.json');
35
+ const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
36
+ const files = packageJson.files || [];
37
+ // Calculate what would be excluded
38
+ const fullWorkflowsSize = getDirectorySize(path_1.default.join(__dirname, '..', 'registry', 'workflows'));
39
+ const templatesSize = getDirectorySize(path_1.default.join(__dirname, '..', 'registry', 'templates'));
40
+ const rulesSize = getDirectorySize(path_1.default.join(__dirname, '..', 'registry', 'rules'));
41
+ const excludedSize = fullWorkflowsSize + templatesSize + rulesSize;
42
+ // What's included
43
+ const stubsSize = getDirectorySize(path_1.default.join(__dirname, '..', 'registry', 'stubs'));
44
+ const scriptsSize = getDirectorySize(path_1.default.join(__dirname, '..', 'registry', 'scripts'));
45
+ const includedSize = stubsSize + scriptsSize;
46
+ console.log(`Excluded from package: ${excludedSize} bytes (workflows + templates + rules)`);
47
+ console.log(`Included in package: ${includedSize} bytes (stubs + scripts)`);
48
+ console.log(`Total registry size reduction: ${Math.round((1 - includedSize / (includedSize + excludedSize)) * 100)}%`);
49
+ // Verify significant size reduction
50
+ (0, node_assert_1.default)(includedSize < excludedSize, 'Package should exclude more content than it includes');
51
+ console.log('✅ Package correctly excludes heavy content while keeping essentials');
52
+ });
53
+ function getDirectorySize(dirPath) {
54
+ if (!fs_1.default.existsSync(dirPath)) {
55
+ return 0;
56
+ }
57
+ let totalSize = 0;
58
+ const files = getAllFiles(dirPath);
59
+ for (const file of files) {
60
+ try {
61
+ const stats = fs_1.default.statSync(file);
62
+ totalSize += stats.size;
63
+ }
64
+ catch (e) {
65
+ // Skip files that can't be read
66
+ }
67
+ }
68
+ return totalSize;
69
+ }
70
+ function getAllFiles(dir, extension) {
71
+ const files = [];
72
+ if (!fs_1.default.existsSync(dir)) {
73
+ return files;
74
+ }
75
+ const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
76
+ for (const entry of entries) {
77
+ const fullPath = path_1.default.join(dir, entry.name);
78
+ if (entry.isDirectory()) {
79
+ files.push(...getAllFiles(fullPath, extension));
80
+ }
81
+ else if (entry.isFile()) {
82
+ if (!extension || entry.name.endsWith(extension)) {
83
+ files.push(fullPath);
84
+ }
85
+ }
86
+ }
87
+ return files;
88
+ }
@@ -89,24 +89,47 @@ async function testScriptLocationIndependence() {
89
89
  if (fs_1.default.existsSync(prepIssueScript)) {
90
90
  try {
91
91
  const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
92
- // Use Git Bash on Windows, regular bash on Unix
93
- const bashCommand = process.platform === 'win32'
94
- ? `"C:\\Program Files\\Git\\bin\\bash.exe" "${prepIssueScript}" --help`
95
- : `"${prepIssueScript}" --help`;
96
- const output = execSync(bashCommand, {
97
- cwd: tempProjectDir,
98
- encoding: 'utf-8',
99
- timeout: 10000
100
- });
92
+ // On Windows, we need to check if Git Bash is available
93
+ if (process.platform === 'win32') {
94
+ const gitBashPaths = [
95
+ 'C:\\Program Files\\Git\\bin\\bash.exe',
96
+ 'C:\\Program Files (x86)\\Git\\bin\\bash.exe'
97
+ ];
98
+ let bashPath = null;
99
+ for (const path of gitBashPaths) {
100
+ if (fs_1.default.existsSync(path)) {
101
+ bashPath = path;
102
+ break;
103
+ }
104
+ }
105
+ if (!bashPath) {
106
+ console.log(' ⚠️ Git Bash not found on Windows, skipping bash script tests');
107
+ console.log(' ✅ Script location independence test completed (Windows limitation)');
108
+ return true;
109
+ }
110
+ const bashCommand = `"${bashPath}" "${prepIssueScript}" --help`;
111
+ const output = execSync(bashCommand, {
112
+ cwd: tempProjectDir,
113
+ encoding: 'utf-8',
114
+ timeout: 10000
115
+ });
116
+ }
117
+ else {
118
+ // Unix systems
119
+ const output = execSync(`"${prepIssueScript}" --help`, {
120
+ cwd: tempProjectDir,
121
+ encoding: 'utf-8',
122
+ timeout: 10000
123
+ });
124
+ }
101
125
  // If we get here, the script at least started successfully
102
126
  console.log(' ✅ prep-issue.sh executed successfully from user directory');
103
127
  }
104
128
  catch (error) {
105
129
  // The script may fail due to missing repo, but as long as it started, that's OK
106
- if (process.platform === 'win32' && error.message.includes('not recognized')) {
107
- console.log(' ⚠️ Git Bash not found on Windows, skipping prep-issue test');
108
- }
109
- else if (error.message.includes('Repository not found') || error.message.includes('fatal: repository')) {
130
+ if (error.message.includes('Repository not found') ||
131
+ error.message.includes('fatal: repository') ||
132
+ error.message.includes('not a git repository')) {
110
133
  // This is expected - the test repo doesn't exist, but the script ran
111
134
  console.log(' ✅ prep-issue.sh executed from user directory (repo not found is expected)');
112
135
  }
@@ -116,31 +139,56 @@ async function testScriptLocationIndependence() {
116
139
  }
117
140
  }
118
141
  }
142
+ else {
143
+ console.log(' ⚠️ prep-issue.sh not found, skipping test');
144
+ }
119
145
  // Test 2: Try to execute code-quality-check.sh (this will likely fail due to dependencies)
120
146
  console.log(' Testing code-quality-check.sh execution from user directory...');
121
147
  const qualityCheckScript = path_1.default.join(userScriptsDir, 'code-quality-check.sh');
122
148
  if (fs_1.default.existsSync(qualityCheckScript)) {
123
149
  try {
124
150
  const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
125
- const bashCommand = process.platform === 'win32'
126
- ? `"C:\\Program Files\\Git\\bin\\bash.exe" "${qualityCheckScript}" --help`
127
- : `"${qualityCheckScript}" --help`;
128
- const output = execSync(bashCommand, {
129
- cwd: tempProjectDir,
130
- encoding: 'utf-8',
131
- timeout: 10000
132
- });
133
- console.log(' ✅ code-quality-check.sh executed successfully from user directory');
134
- }
135
- catch (error) {
136
- if (process.platform === 'win32' && error.message.includes('not recognized')) {
137
- console.log(' ⚠️ Git Bash not found on Windows, skipping quality check test');
151
+ if (process.platform === 'win32') {
152
+ const gitBashPaths = [
153
+ 'C:\\Program Files\\Git\\bin\\bash.exe',
154
+ 'C:\\Program Files (x86)\\Git\\bin\\bash.exe'
155
+ ];
156
+ let bashPath = null;
157
+ for (const path of gitBashPaths) {
158
+ if (fs_1.default.existsSync(path)) {
159
+ bashPath = path;
160
+ break;
161
+ }
162
+ }
163
+ if (!bashPath) {
164
+ console.log(' ⚠️ Git Bash not found on Windows, skipping quality check test');
165
+ }
166
+ else {
167
+ const bashCommand = `"${bashPath}" "${qualityCheckScript}" --help`;
168
+ const output = execSync(bashCommand, {
169
+ cwd: tempProjectDir,
170
+ encoding: 'utf-8',
171
+ timeout: 10000
172
+ });
173
+ console.log(' ✅ code-quality-check.sh executed successfully from user directory');
174
+ }
138
175
  }
139
176
  else {
140
- console.log(' ❌ code-quality-check.sh failed (expected due to dependencies):', error.message);
141
- // This is expected to fail - we'll document this as a known issue
177
+ const output = execSync(`"${qualityCheckScript}" --help`, {
178
+ cwd: tempProjectDir,
179
+ encoding: 'utf-8',
180
+ timeout: 10000
181
+ });
182
+ console.log(' ✅ code-quality-check.sh executed successfully from user directory');
142
183
  }
143
184
  }
185
+ catch (error) {
186
+ console.log(' ❌ code-quality-check.sh failed (expected due to dependencies):', error.message);
187
+ // This is expected to fail - we'll document this as a known issue
188
+ }
189
+ }
190
+ else {
191
+ console.log(' ⚠️ code-quality-check.sh not found, skipping test');
144
192
  }
145
193
  console.log(' ✅ Script location independence test completed');
146
194
  return true;
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env tsx
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const node_test_1 = require("node:test");
8
+ const node_assert_1 = __importDefault(require("node:assert"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const build_stub_registry_1 = require("../scripts/build-stub-registry");
12
+ /**
13
+ * Tests for stub registry build process
14
+ * Ensures that the npm package contains only stubs and scripts, not full workflows
15
+ */
16
+ (0, node_test_1.test)('Stub registry build generates correct structure', async () => {
17
+ // Clean up any existing stubs
18
+ const stubsPath = path_1.default.join(__dirname, '..', 'registry', 'stubs');
19
+ if (fs_1.default.existsSync(stubsPath)) {
20
+ fs_1.default.rmSync(stubsPath, { recursive: true, force: true });
21
+ }
22
+ // Build stub registry
23
+ await (0, build_stub_registry_1.buildStubRegistry)();
24
+ // Verify stubs directory exists
25
+ (0, node_assert_1.default)(fs_1.default.existsSync(stubsPath), 'Stubs directory should exist');
26
+ // Verify workflows stubs directory exists
27
+ const workflowsStubsPath = path_1.default.join(stubsPath, 'workflows');
28
+ (0, node_assert_1.default)(fs_1.default.existsSync(workflowsStubsPath), 'Workflows stubs directory should exist');
29
+ // Verify at least some stubs were generated
30
+ const stubFiles = getAllFiles(workflowsStubsPath, '.md');
31
+ (0, node_assert_1.default)(stubFiles.length > 0, 'Should generate at least one workflow stub');
32
+ console.log(`✅ Generated ${stubFiles.length} workflow stubs`);
33
+ });
34
+ (0, node_test_1.test)('Generated stubs are lightweight and contain correct format', async () => {
35
+ const stubsPath = path_1.default.join(__dirname, '..', 'registry', 'stubs', 'workflows');
36
+ // Find a stub file to test
37
+ const stubFiles = getAllFiles(stubsPath, '.md');
38
+ (0, node_assert_1.default)(stubFiles.length > 0, 'Should have stub files to test');
39
+ const testStubPath = stubFiles[0];
40
+ const stubContent = fs_1.default.readFileSync(testStubPath, 'utf8');
41
+ // Verify stub format
42
+ (0, node_assert_1.default)(stubContent.includes('# FRAIM Workflow:'), 'Stub should have workflow title');
43
+ (0, node_assert_1.default)(stubContent.includes('FRAIM-managed workflow stub'), 'Stub should identify itself as a stub');
44
+ (0, node_assert_1.default)(stubContent.includes('get_fraim_workflow('), 'Stub should contain MCP call instruction');
45
+ (0, node_assert_1.default)(stubContent.includes('## Intent'), 'Stub should contain intent section');
46
+ (0, node_assert_1.default)(stubContent.includes('DO NOT EXECUTE'), 'Stub should contain execution warning');
47
+ // Verify stub is lightweight (should be much smaller than full workflow)
48
+ const stubLines = stubContent.split('\n').length;
49
+ (0, node_assert_1.default)(stubLines < 20, `Stub should be lightweight (${stubLines} lines), not a full workflow`);
50
+ console.log(`✅ Stub format verified (${stubLines} lines)`);
51
+ });
52
+ (0, node_test_1.test)('Stubs do not contain full workflow content', async () => {
53
+ const stubsPath = path_1.default.join(__dirname, '..', 'registry', 'stubs', 'workflows');
54
+ const fullWorkflowsPath = path_1.default.join(__dirname, '..', 'registry', 'workflows');
55
+ const stubFiles = getAllFiles(stubsPath, '.md');
56
+ (0, node_assert_1.default)(stubFiles.length > 0, 'Should have stub files to test');
57
+ for (const stubFile of stubFiles.slice(0, 3)) { // Test first 3 stubs
58
+ const stubContent = fs_1.default.readFileSync(stubFile, 'utf8');
59
+ // Find corresponding full workflow
60
+ const relativePath = path_1.default.relative(stubsPath, stubFile);
61
+ const fullWorkflowPath = path_1.default.join(fullWorkflowsPath, relativePath);
62
+ if (fs_1.default.existsSync(fullWorkflowPath)) {
63
+ const fullContent = fs_1.default.readFileSync(fullWorkflowPath, 'utf8');
64
+ // Stub should be much smaller than full workflow
65
+ (0, node_assert_1.default)(stubContent.length < fullContent.length * 0.5, `Stub should be <50% size of full workflow (stub: ${stubContent.length}, full: ${fullContent.length})`);
66
+ // More importantly, stub should not contain full workflow sections
67
+ (0, node_assert_1.default)(!stubContent.includes('## WORKFLOW TRIGGER'), 'Stub should not contain WORKFLOW TRIGGER section');
68
+ (0, node_assert_1.default)(!stubContent.includes('## AI AGENT PROCESS'), 'Stub should not contain AI AGENT PROCESS section');
69
+ (0, node_assert_1.default)(!stubContent.includes('## EXAMPLES'), 'Stub should not contain EXAMPLES section');
70
+ }
71
+ }
72
+ console.log('✅ Stubs are properly lightweight');
73
+ });
74
+ (0, node_test_1.test)('Package.json files array excludes full workflows', () => {
75
+ const packageJsonPath = path_1.default.join(__dirname, '..', 'package.json');
76
+ const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
77
+ const files = packageJson.files || [];
78
+ // Should include stubs
79
+ (0, node_assert_1.default)(files.includes('registry/stubs/'), 'Should include registry/stubs/');
80
+ // Should include scripts
81
+ (0, node_assert_1.default)(files.includes('registry/scripts/'), 'Should include registry/scripts/');
82
+ // Should NOT include full registry
83
+ (0, node_assert_1.default)(!files.includes('registry/'), 'Should NOT include full registry/');
84
+ (0, node_assert_1.default)(!files.includes('registry/workflows/'), 'Should NOT include registry/workflows/');
85
+ (0, node_assert_1.default)(!files.includes('registry/templates/'), 'Should NOT include registry/templates/');
86
+ (0, node_assert_1.default)(!files.includes('registry/rules/'), 'Should NOT include registry/rules/');
87
+ console.log('✅ Package.json correctly excludes full workflows');
88
+ });
89
+ (0, node_test_1.test)('Scripts are still included in package', () => {
90
+ const scriptsPath = path_1.default.join(__dirname, '..', 'registry', 'scripts');
91
+ (0, node_assert_1.default)(fs_1.default.existsSync(scriptsPath), 'Scripts directory should exist');
92
+ const scriptFiles = getAllFiles(scriptsPath);
93
+ (0, node_assert_1.default)(scriptFiles.length > 0, 'Should have script files');
94
+ // Verify package.json includes scripts
95
+ const packageJsonPath = path_1.default.join(__dirname, '..', 'package.json');
96
+ const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
97
+ const files = packageJson.files || [];
98
+ (0, node_assert_1.default)(files.includes('registry/scripts/'), 'Package should include scripts');
99
+ console.log(`✅ ${scriptFiles.length} scripts are included in package`);
100
+ });
101
+ // Helper function to get all files recursively
102
+ function getAllFiles(dir, extension) {
103
+ const files = [];
104
+ if (!fs_1.default.existsSync(dir)) {
105
+ return files;
106
+ }
107
+ const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
108
+ for (const entry of entries) {
109
+ const fullPath = path_1.default.join(dir, entry.name);
110
+ if (entry.isDirectory()) {
111
+ files.push(...getAllFiles(fullPath, extension));
112
+ }
113
+ else if (entry.isFile()) {
114
+ if (!extension || entry.name.endsWith(extension)) {
115
+ files.push(fullPath);
116
+ }
117
+ }
118
+ }
119
+ return files;
120
+ }
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env tsx
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const node_test_1 = require("node:test");
8
+ const node_assert_1 = __importDefault(require("node:assert"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const os_1 = __importDefault(require("os"));
12
+ const sync_1 = require("../src/cli/commands/sync");
13
+ /**
14
+ * Tests for sync command with stub registry
15
+ * Ensures sync works correctly with pre-generated stubs
16
+ */
17
+ // Use a temporary directory for testing
18
+ const TEST_PROJECT_DIR = path_1.default.join(os_1.default.tmpdir(), 'fraim-test-sync-' + Date.now());
19
+ const TEST_FRAIM_DIR = path_1.default.join(TEST_PROJECT_DIR, '.fraim');
20
+ const TEST_WORKFLOWS_DIR = path_1.default.join(TEST_FRAIM_DIR, 'workflows');
21
+ (0, node_test_1.test)('Sync command works with stub registry', async () => {
22
+ // Setup test environment
23
+ setupTestProject();
24
+ // Change to test directory
25
+ const originalCwd = process.cwd();
26
+ process.chdir(TEST_PROJECT_DIR);
27
+ try {
28
+ // Run sync
29
+ await (0, sync_1.runSync)({ force: true });
30
+ // Verify workflows directory was created
31
+ (0, node_assert_1.default)(fs_1.default.existsSync(TEST_WORKFLOWS_DIR), 'Workflows directory should be created');
32
+ // Verify stubs were copied
33
+ const stubFiles = getAllFiles(TEST_WORKFLOWS_DIR, '.md');
34
+ (0, node_assert_1.default)(stubFiles.length > 0, 'Should copy workflow stubs');
35
+ // Verify stubs have correct format
36
+ const testStub = fs_1.default.readFileSync(stubFiles[0], 'utf8');
37
+ (0, node_assert_1.default)(testStub.includes('FRAIM-managed workflow stub'), 'Copied files should be stubs');
38
+ (0, node_assert_1.default)(testStub.includes('get_fraim_workflow('), 'Stubs should contain MCP call instruction');
39
+ console.log(`✅ Sync copied ${stubFiles.length} workflow stubs`);
40
+ }
41
+ finally {
42
+ // Restore original directory
43
+ process.chdir(originalCwd);
44
+ // Cleanup test directory
45
+ if (fs_1.default.existsSync(TEST_PROJECT_DIR)) {
46
+ fs_1.default.rmSync(TEST_PROJECT_DIR, { recursive: true, force: true });
47
+ }
48
+ }
49
+ });
50
+ (0, node_test_1.test)('Sync uses stub registry, not full workflows', async () => {
51
+ // Setup test environment
52
+ setupTestProject();
53
+ const originalCwd = process.cwd();
54
+ process.chdir(TEST_PROJECT_DIR);
55
+ try {
56
+ // Run sync
57
+ await (0, sync_1.runSync)({ force: true });
58
+ // Verify synced files are stubs, not full workflows
59
+ const stubFiles = getAllFiles(TEST_WORKFLOWS_DIR, '.md');
60
+ (0, node_assert_1.default)(stubFiles.length > 0, 'Should have synced files');
61
+ for (const stubFile of stubFiles.slice(0, 3)) { // Test first 3 files
62
+ const content = fs_1.default.readFileSync(stubFile, 'utf8');
63
+ const lines = content.split('\n').length;
64
+ // Should be lightweight stubs
65
+ (0, node_assert_1.default)(lines < 20, `File should be a stub (${lines} lines), not a full workflow`);
66
+ (0, node_assert_1.default)(content.includes('DO NOT EXECUTE'), 'Should contain stub warning');
67
+ (0, node_assert_1.default)(!content.includes('## WORKFLOW TRIGGER'), 'Should not contain full workflow sections');
68
+ (0, node_assert_1.default)(!content.includes('## AI AGENT PROCESS'), 'Should not contain full workflow sections');
69
+ }
70
+ console.log('✅ Sync correctly uses stubs, not full workflows');
71
+ }
72
+ finally {
73
+ process.chdir(originalCwd);
74
+ if (fs_1.default.existsSync(TEST_PROJECT_DIR)) {
75
+ fs_1.default.rmSync(TEST_PROJECT_DIR, { recursive: true, force: true });
76
+ }
77
+ }
78
+ });
79
+ (0, node_test_1.test)('Sync preserves directory structure from stub registry', async () => {
80
+ setupTestProject();
81
+ const originalCwd = process.cwd();
82
+ process.chdir(TEST_PROJECT_DIR);
83
+ try {
84
+ await (0, sync_1.runSync)({ force: true });
85
+ // Verify directory structure is preserved
86
+ const expectedDirs = [
87
+ 'bootstrap',
88
+ 'product-building',
89
+ 'customer-development',
90
+ 'marketing'
91
+ ];
92
+ for (const expectedDir of expectedDirs) {
93
+ const dirPath = path_1.default.join(TEST_WORKFLOWS_DIR, expectedDir);
94
+ if (fs_1.default.existsSync(dirPath)) {
95
+ (0, node_assert_1.default)(fs_1.default.statSync(dirPath).isDirectory(), `${expectedDir} should be a directory`);
96
+ // Should contain stub files
97
+ const files = fs_1.default.readdirSync(dirPath).filter(f => f.endsWith('.md'));
98
+ (0, node_assert_1.default)(files.length > 0, `${expectedDir} should contain workflow stubs`);
99
+ }
100
+ }
101
+ console.log('✅ Directory structure preserved correctly');
102
+ }
103
+ finally {
104
+ process.chdir(originalCwd);
105
+ if (fs_1.default.existsSync(TEST_PROJECT_DIR)) {
106
+ fs_1.default.rmSync(TEST_PROJECT_DIR, { recursive: true, force: true });
107
+ }
108
+ }
109
+ });
110
+ function setupTestProject() {
111
+ // Create test project directory
112
+ if (fs_1.default.existsSync(TEST_PROJECT_DIR)) {
113
+ fs_1.default.rmSync(TEST_PROJECT_DIR, { recursive: true, force: true });
114
+ }
115
+ fs_1.default.mkdirSync(TEST_PROJECT_DIR, { recursive: true });
116
+ // Create .fraim directory
117
+ fs_1.default.mkdirSync(TEST_FRAIM_DIR, { recursive: true });
118
+ // Create basic config.json
119
+ const config = {
120
+ version: "2.0.36",
121
+ customizations: {}
122
+ };
123
+ fs_1.default.writeFileSync(path_1.default.join(TEST_FRAIM_DIR, 'config.json'), JSON.stringify(config, null, 2));
124
+ }
125
+ function getAllFiles(dir, extension) {
126
+ const files = [];
127
+ if (!fs_1.default.existsSync(dir)) {
128
+ return files;
129
+ }
130
+ const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
131
+ for (const entry of entries) {
132
+ const fullPath = path_1.default.join(dir, entry.name);
133
+ if (entry.isDirectory()) {
134
+ files.push(...getAllFiles(fullPath, extension));
135
+ }
136
+ else if (entry.isFile()) {
137
+ if (!extension || entry.name.endsWith(extension)) {
138
+ files.push(fullPath);
139
+ }
140
+ }
141
+ }
142
+ return files;
143
+ }