fraim-framework 2.0.30 → 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.
- package/dist/src/cli/commands/init.js +29 -2
- package/dist/src/cli/commands/sync.js +18 -1
- package/dist/src/utils/script-sync-utils.js +218 -0
- package/dist/tests/debug-tools.js +6 -5
- package/dist/tests/test-chalk-regression.js +58 -8
- package/dist/tests/test-cli.js +70 -5
- package/dist/tests/test-end-to-end-hybrid-validation.js +349 -0
- package/dist/tests/test-first-run-journey.js +43 -3
- package/dist/tests/test-hybrid-script-execution.js +369 -0
- package/dist/tests/test-mcp-connection.js +2 -2
- package/dist/tests/test-mcp-issue-integration.js +12 -4
- package/dist/tests/test-mcp-lifecycle-methods.js +4 -4
- package/dist/tests/test-node-compatibility.js +24 -2
- package/dist/tests/test-prep-issue.js +4 -1
- package/dist/tests/test-script-location-independence.js +173 -0
- package/dist/tests/test-script-sync.js +557 -0
- package/dist/tests/test-session-rehydration.js +2 -2
- package/dist/tests/test-standalone.js +3 -3
- package/dist/tests/test-sync-version-update.js +1 -1
- package/dist/tests/test-telemetry.js +2 -2
- package/dist/tests/test-user-journey.js +8 -4
- package/dist/tests/test-utils.js +13 -0
- package/dist/tests/test-wizard.js +2 -2
- package/package.json +3 -3
- package/registry/rules/agent-testing-guidelines.md +502 -502
- package/registry/rules/ephemeral-execution.md +37 -27
- package/registry/rules/local-development.md +253 -251
- package/registry/rules/successful-debugging-patterns.md +491 -482
- package/registry/scripts/prep-issue.sh +468 -468
- package/registry/workflows/bootstrap/evaluate-code-quality.md +8 -2
- package/registry/workflows/bootstrap/verify-test-coverage.md +8 -2
- package/registry/workflows/customer-development/thank-customers.md +203 -193
- package/registry/workflows/customer-development/weekly-newsletter.md +366 -362
- package/registry/workflows/performance/analyze-performance.md +65 -63
- package/registry/workflows/product-building/implement.md +6 -2
- package/registry/workflows/product-building/prep-issue.md +11 -24
- package/registry/workflows/product-building/resolve.md +5 -1
- package/registry/workflows/replicate/replicate-discovery.md +336 -0
- package/registry/workflows/replicate/replicate-to-issues.md +319 -0
- package/registry/workflows/reviewer/review-implementation-vs-design-spec.md +632 -632
- package/.windsurf/rules/windsurf-rules.md +0 -7
- package/.windsurf/workflows/resolve-issue.md +0 -6
- package/.windsurf/workflows/retrospect.md +0 -6
- package/.windsurf/workflows/start-design.md +0 -6
- package/.windsurf/workflows/start-impl.md +0 -6
- package/.windsurf/workflows/start-spec.md +0 -6
- package/.windsurf/workflows/start-tests.md +0 -6
- package/bin/fraim.js +0 -23
- package/registry/scripts/build-scripts-generator.ts +0 -216
- package/registry/scripts/cleanup-branch.ts +0 -303
- package/registry/scripts/fraim-config.ts +0 -63
- package/registry/scripts/generate-engagement-emails.ts +0 -744
- package/registry/scripts/generic-issues-api.ts +0 -110
- package/registry/scripts/newsletter-helpers.ts +0 -874
- package/registry/scripts/openapi-generator.ts +0 -695
- package/registry/scripts/performance/profile-server.ts +0 -370
- package/registry/scripts/run-thank-you-workflow.ts +0 -122
- package/registry/scripts/send-newsletter-simple.ts +0 -104
- package/registry/scripts/send-thank-you-emails.ts +0 -57
- package/registry/workflows/replicate/re-implementation-strategy.md +0 -226
- package/registry/workflows/replicate/use-case-extraction.md +0 -135
- package/registry/workflows/replicate/visual-analysis.md +0 -154
- package/registry/workflows/replicate/website-discovery-analysis.md +0 -231
- package/sample_package.json +0 -18
- /package/registry/scripts/{replicate/comprehensive-explorer.py → comprehensive-explorer.py} +0 -0
- /package/registry/scripts/{replicate/interactive-explorer.py → interactive-explorer.py} +0 -0
- /package/registry/scripts/{replicate/scrape-site.py → scrape-site.py} +0 -0
|
@@ -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
|
|
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, ['
|
|
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 });
|