fraim-framework 2.0.52 → 2.0.55
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/registry/scripts/profile-server.js +2 -1
- package/dist/src/ai-manager/ai-manager.js +49 -1
- package/dist/src/ai-manager/phase-flow.js +68 -0
- package/dist/src/utils/digest-utils.js +18 -7
- package/dist/tests/test-debug-session.js +6 -2
- package/dist/tests/test-enhanced-session-init.js +6 -2
- package/dist/tests/test-mcp-lifecycle-methods.js +1 -2
- package/dist/tests/test-mcp-template-processing.js +6 -2
- package/dist/tests/test-modular-issue-tracking.js +6 -2
- package/dist/tests/test-node-compatibility.js +4 -2
- package/dist/tests/test-npm-install.js +4 -2
- package/dist/tests/test-productivity-integration.js +157 -0
- package/dist/tests/test-session-rehydration.js +1 -2
- package/dist/tests/test-telemetry.js +1 -2
- package/dist/tests/test-users-to-target-workflow.js +253 -0
- package/index.js +44 -55
- package/package.json +5 -5
- package/registry/agent-guardrails.md +62 -62
- package/registry/scripts/detect-tautological-tests.sh +38 -38
- package/registry/scripts/productivity/build-productivity-csv.mjs +242 -0
- package/registry/scripts/productivity/fetch-pr-details.mjs +144 -0
- package/registry/scripts/productivity/productivity-report.sh +147 -0
- package/registry/scripts/profile-server.ts +1 -1
- package/registry/scripts/validate-openapi-limits.ts +366 -366
- package/registry/scripts/validate-test-coverage.ts +280 -280
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase1-customer-profiling.md +11 -0
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase1-survey-scoping.md +11 -0
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase2-platform-discovery.md +11 -0
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase2-survey-build-linkedin.md +11 -0
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase3-prospect-qualification.md +11 -0
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase3-survey-build-reddit.md +11 -0
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase4-inventory-compilation.md +11 -0
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase4-survey-build-x.md +11 -0
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase5-survey-build-facebook.md +11 -0
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase6-survey-build-custom.md +11 -0
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase7-survey-dispatch.md +11 -0
- package/registry/stubs/workflows/customer-development/templates/customer-persona-template.md +11 -0
- package/registry/stubs/workflows/customer-development/templates/search-strategy-template.md +11 -0
- package/registry/stubs/workflows/customer-development/user-survey-dispatch.md +11 -0
- package/registry/stubs/workflows/customer-development/users-to-target.md +11 -0
- package/registry/stubs/workflows/productivity-report/productivity-report.md +11 -0
- package/bin/fraim.js +0 -8
- package/dist/registry/ai-manager-rules/design-phases/design.md +0 -108
- package/dist/registry/ai-manager-rules/design-phases/finalize.md +0 -60
- package/dist/registry/ai-manager-rules/design-phases/validate.md +0 -125
- package/dist/registry/ai-manager-rules/design.json +0 -97
- package/dist/registry/ai-manager-rules/implement-phases/code.md +0 -323
- package/dist/registry/ai-manager-rules/implement-phases/completeness-review.md +0 -94
- package/dist/registry/ai-manager-rules/implement-phases/finalize.md +0 -177
- package/dist/registry/ai-manager-rules/implement-phases/quality-review.md +0 -304
- package/dist/registry/ai-manager-rules/implement-phases/regression.md +0 -159
- package/dist/registry/ai-manager-rules/implement-phases/repro.md +0 -101
- package/dist/registry/ai-manager-rules/implement-phases/scoping.md +0 -93
- package/dist/registry/ai-manager-rules/implement-phases/smoke.md +0 -225
- package/dist/registry/ai-manager-rules/implement-phases/spike.md +0 -118
- package/dist/registry/ai-manager-rules/implement-phases/validate.md +0 -347
- package/dist/registry/ai-manager-rules/implement.json +0 -153
- package/dist/registry/ai-manager-rules/shared-phases/finalize.md +0 -169
- package/dist/registry/ai-manager-rules/spec-phases/finalize.md +0 -60
- package/dist/registry/ai-manager-rules/spec-phases/spec.md +0 -102
- package/dist/registry/ai-manager-rules/spec-phases/validate.md +0 -118
- package/dist/registry/ai-manager-rules/spec.json +0 -112
- package/dist/registry/ai-manager-rules/test.json +0 -98
- package/dist/registry/scripts/build-scripts-generator.js +0 -205
- package/dist/registry/scripts/fraim-config.js +0 -61
- package/dist/registry/scripts/generic-issues-api.js +0 -100
- package/dist/registry/scripts/openapi-generator.js +0 -664
- package/dist/registry/scripts/performance/profile-server.js +0 -390
- package/dist/src/ai-manager/evidence-validator.js +0 -309
- package/dist/src/fraim/issue-tracking/ado-provider.js +0 -304
- package/dist/src/fraim/issue-tracking/factory.js +0 -63
- package/dist/src/fraim/issue-tracking/github-provider.js +0 -200
- package/dist/src/fraim/issue-tracking/types.js +0 -7
- package/dist/src/fraim/issue-tracking-config.js +0 -83
- package/dist/src/static-website-middleware.js +0 -75
- package/dist/test-utils.js +0 -96
- package/dist/tests/esm-compat.js +0 -11
- package/dist/tests/test-ai-manager-phase-protocol.js +0 -147
- package/dist/tests/test-ai-manager.js +0 -118
- package/dist/tests/test-chalk-esm-issue.js +0 -159
- package/dist/tests/test-chalk-real-world.js +0 -265
- package/dist/tests/test-chalk-regression.js +0 -377
- package/dist/tests/test-chalk-resolution-issue.js +0 -304
- package/dist/tests/test-evidence-validation.js +0 -221
- package/dist/tests/test-first-run-interactive.js +0 -1
- package/dist/tests/test-fraim-install-chalk-issue.js +0 -254
- package/dist/tests/test-markdown-to-pdf.js +0 -454
- package/dist/tests/test-npm-resolution-diagnostic.js +0 -140
- package/dist/tests/test-pr-review-integration.js +0 -1
- package/dist/website/.nojekyll +0 -0
- package/dist/website/404.html +0 -101
- package/dist/website/CNAME +0 -1
- package/dist/website/README.md +0 -22
- package/dist/website/demo.html +0 -604
- package/dist/website/images/.gitkeep +0 -1
- package/dist/website/images/fraim-logo.png +0 -0
- package/dist/website/index.html +0 -290
- package/dist/website/pricing.html +0 -414
- package/dist/website/script.js +0 -55
- package/dist/website/styles.css +0 -2647
|
@@ -37,6 +37,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
37
37
|
const child_process_1 = require("child_process");
|
|
38
38
|
const fs_1 = require("fs");
|
|
39
39
|
const path_1 = require("path");
|
|
40
|
+
const path = __importStar(require("path"));
|
|
40
41
|
function loadClientConfig() {
|
|
41
42
|
const configPath = (0, path_1.join)(process.cwd(), '.fraim', 'config.json');
|
|
42
43
|
if (!(0, fs_1.existsSync)(configPath)) {
|
|
@@ -175,7 +176,7 @@ async function getSystemInformation(appName) {
|
|
|
175
176
|
try {
|
|
176
177
|
const response = await axios.post(`https://${appName}.scm.azurewebsites.net/api/command`, {
|
|
177
178
|
command: command.cmd,
|
|
178
|
-
dir: '/home'
|
|
179
|
+
dir: path.join('/', 'home')
|
|
179
180
|
}, {
|
|
180
181
|
headers: { Authorization: `Bearer ${token}` },
|
|
181
182
|
timeout: 5000
|
|
@@ -77,7 +77,7 @@ Please provide a valid issue number to get specific guidance for your workflow.
|
|
|
77
77
|
Example: \`seekCoachingOnNextStep({ workflowType: "${args.workflowType}", issueNumber: "123", currentPhase: "${args.currentPhase}", status: "${args.status}" })\``;
|
|
78
78
|
}
|
|
79
79
|
// Validate workflow type
|
|
80
|
-
const validWorkflowTypes = ['implement', 'spec', 'design', 'test'];
|
|
80
|
+
const validWorkflowTypes = ['implement', 'spec', 'design', 'test', 'customer-development', 'user-survey'];
|
|
81
81
|
if (!validWorkflowTypes.includes(args.workflowType)) {
|
|
82
82
|
console.log(`❌ AI Coach: Invalid workflow type: ${args.workflowType}`);
|
|
83
83
|
throw new Error(`Invalid workflow type: ${args.workflowType}. Valid types: ${validWorkflowTypes.join(', ')}`);
|
|
@@ -225,6 +225,14 @@ ${nextPhaseInstructions}
|
|
|
225
225
|
- ✅ **Validation**: Verified test coverage and quality
|
|
226
226
|
- ✅ **Submit PR**: Prepared tests for integration
|
|
227
227
|
- ✅ **PR Review**: Successfully completed test review process`;
|
|
228
|
+
case 'user-survey':
|
|
229
|
+
return `- ✅ **Scoping**: Defined research goals and target audience
|
|
230
|
+
- ✅ **LinkedIn Build**: Generated professional LinkedIn survey content
|
|
231
|
+
- ✅ **Reddit Build**: Targeted relevant communities with specific copy
|
|
232
|
+
- ✅ **X/Twitter Build**: Created high-engagement short-form content
|
|
233
|
+
- ✅ **Facebook Build**: Formulated community-focused content
|
|
234
|
+
- ✅ **Custom Platforms**: Addressed supplementary platform needs
|
|
235
|
+
- ✅ **Dispatch**: Successfully executed survey distribution via automation`;
|
|
228
236
|
default:
|
|
229
237
|
return `- ✅ **${workflowType}**: Completed all phases successfully
|
|
230
238
|
- ✅ **Validation**: Verified work quality and completeness
|
|
@@ -351,6 +359,42 @@ Remember: Take your time and follow each step carefully. The phase guidance cont
|
|
|
351
359
|
else if (workflowType === 'implement') {
|
|
352
360
|
phasePath = `ai-manager-rules/implement-phases/${phase}.md`;
|
|
353
361
|
}
|
|
362
|
+
else if (workflowType === 'customer-development') {
|
|
363
|
+
// Customer development phases are in workflows directory
|
|
364
|
+
// Map phase names to file names
|
|
365
|
+
const phaseFileMap = {
|
|
366
|
+
'customer-profiling': 'phase1-customer-profiling.md',
|
|
367
|
+
'platform-discovery': 'phase2-platform-discovery.md',
|
|
368
|
+
'prospect-qualification': 'phase3-prospect-qualification.md',
|
|
369
|
+
'inventory-compilation': 'phase4-inventory-compilation.md'
|
|
370
|
+
};
|
|
371
|
+
const fileName = phaseFileMap[phase];
|
|
372
|
+
if (fileName) {
|
|
373
|
+
phasePath = `workflows/customer-development/ai-coach-phases/${fileName}`;
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
phasePath = `workflows/customer-development/ai-coach-phases/${phase}.md`;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
else if (workflowType === 'user-survey') {
|
|
380
|
+
// User survey phases
|
|
381
|
+
const phaseFileMap = {
|
|
382
|
+
'survey-scoping': 'phase1-survey-scoping.md',
|
|
383
|
+
'survey-build-linkedin': 'phase2-survey-build-linkedin.md',
|
|
384
|
+
'survey-build-reddit': 'phase3-survey-build-reddit.md',
|
|
385
|
+
'survey-build-x': 'phase4-survey-build-x.md',
|
|
386
|
+
'survey-build-facebook': 'phase5-survey-build-facebook.md',
|
|
387
|
+
'survey-build-custom': 'phase6-survey-build-custom.md',
|
|
388
|
+
'survey-dispatch': 'phase7-survey-dispatch.md'
|
|
389
|
+
};
|
|
390
|
+
const fileName = phaseFileMap[phase];
|
|
391
|
+
if (fileName) {
|
|
392
|
+
phasePath = `workflows/customer-development/ai-coach-phases/${fileName}`;
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
phasePath = `workflows/customer-development/ai-coach-phases/${phase}.md`;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
354
398
|
else {
|
|
355
399
|
phasePath = `ai-manager-rules/${workflowType}-phases/${phase}.md`;
|
|
356
400
|
}
|
|
@@ -426,6 +470,10 @@ Available phases for ${workflowType} workflow: ${this.getAvailablePhasesForWorkf
|
|
|
426
470
|
return 'design-design, design-completeness-review, submit-pr, wait-for-pr-review';
|
|
427
471
|
case 'test':
|
|
428
472
|
return 'test-test, test-validate, submit-pr, wait-for-pr-review';
|
|
473
|
+
case 'customer-development':
|
|
474
|
+
return 'customer-profiling, platform-discovery, prospect-qualification, inventory-compilation';
|
|
475
|
+
case 'user-survey':
|
|
476
|
+
return 'survey-scoping, survey-build-linkedin, survey-build-reddit, survey-build-x, survey-build-facebook, survey-build-custom, survey-dispatch';
|
|
429
477
|
default:
|
|
430
478
|
return 'unknown';
|
|
431
479
|
}
|
|
@@ -59,6 +59,21 @@ const TEST_PHASE_FLOW = [
|
|
|
59
59
|
'wait-for-pr-review',
|
|
60
60
|
'retrospective',
|
|
61
61
|
];
|
|
62
|
+
const CUSTOMER_DEVELOPMENT_PHASE_FLOW = [
|
|
63
|
+
'customer-profiling',
|
|
64
|
+
'platform-discovery',
|
|
65
|
+
'prospect-qualification',
|
|
66
|
+
'inventory-compilation',
|
|
67
|
+
];
|
|
68
|
+
const USER_SURVEY_PHASE_FLOW = [
|
|
69
|
+
'survey-scoping',
|
|
70
|
+
'survey-build-linkedin',
|
|
71
|
+
'survey-build-reddit',
|
|
72
|
+
'survey-build-x',
|
|
73
|
+
'survey-build-facebook',
|
|
74
|
+
'survey-build-custom',
|
|
75
|
+
'survey-dispatch',
|
|
76
|
+
];
|
|
62
77
|
/**
|
|
63
78
|
* Get the next phase in the workflow
|
|
64
79
|
* @param currentPhase - Current phase
|
|
@@ -102,6 +117,12 @@ function getNextPhase(currentPhase, workflowType, issueType) {
|
|
|
102
117
|
else if (workflowType === 'test') {
|
|
103
118
|
flow = TEST_PHASE_FLOW;
|
|
104
119
|
}
|
|
120
|
+
else if (workflowType === 'customer-development') {
|
|
121
|
+
flow = CUSTOMER_DEVELOPMENT_PHASE_FLOW;
|
|
122
|
+
}
|
|
123
|
+
else if (workflowType === 'user-survey') {
|
|
124
|
+
flow = USER_SURVEY_PHASE_FLOW;
|
|
125
|
+
}
|
|
105
126
|
else {
|
|
106
127
|
throw new Error(`Unknown workflow type: ${workflowType}`);
|
|
107
128
|
}
|
|
@@ -137,9 +158,18 @@ function isPhaseValidForWorkflow(phase, workflowType, issueType) {
|
|
|
137
158
|
else if (workflowType === 'test') {
|
|
138
159
|
flow = TEST_PHASE_FLOW;
|
|
139
160
|
}
|
|
161
|
+
else if (workflowType === 'customer-development') {
|
|
162
|
+
flow = CUSTOMER_DEVELOPMENT_PHASE_FLOW;
|
|
163
|
+
}
|
|
164
|
+
else if (workflowType === 'user-survey') {
|
|
165
|
+
flow = USER_SURVEY_PHASE_FLOW;
|
|
166
|
+
}
|
|
140
167
|
else {
|
|
141
168
|
return false;
|
|
142
169
|
}
|
|
170
|
+
if (phase === 'address-pr-feedback') {
|
|
171
|
+
return ['implement', 'spec', 'design', 'test'].includes(workflowType);
|
|
172
|
+
}
|
|
143
173
|
return flow.includes(phase);
|
|
144
174
|
}
|
|
145
175
|
/**
|
|
@@ -244,6 +274,38 @@ function getPhaseOnFailure(failedPhase, workflowType, issueType // eslint-disabl
|
|
|
244
274
|
return 'test-test';
|
|
245
275
|
}
|
|
246
276
|
}
|
|
277
|
+
else if (workflowType === 'customer-development') {
|
|
278
|
+
// Customer development workflow failure handling
|
|
279
|
+
switch (failedPhase) {
|
|
280
|
+
case 'customer-profiling':
|
|
281
|
+
return 'customer-profiling'; // Start over at profiling
|
|
282
|
+
case 'platform-discovery':
|
|
283
|
+
return 'customer-profiling'; // Go back to refine customer profile
|
|
284
|
+
case 'prospect-qualification':
|
|
285
|
+
return 'platform-discovery'; // Go back to find more prospects
|
|
286
|
+
case 'inventory-compilation':
|
|
287
|
+
return 'prospect-qualification'; // Go back to improve qualification
|
|
288
|
+
default:
|
|
289
|
+
return 'customer-profiling';
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
else if (workflowType === 'user-survey') {
|
|
293
|
+
// User survey workflow failure handling
|
|
294
|
+
switch (failedPhase) {
|
|
295
|
+
case 'survey-scoping':
|
|
296
|
+
return 'survey-scoping';
|
|
297
|
+
case 'survey-build-linkedin':
|
|
298
|
+
case 'survey-build-reddit':
|
|
299
|
+
case 'survey-build-x':
|
|
300
|
+
case 'survey-build-facebook':
|
|
301
|
+
case 'survey-build-custom':
|
|
302
|
+
return 'survey-scoping'; // Go back to revise questions
|
|
303
|
+
case 'survey-dispatch':
|
|
304
|
+
return 'survey-build-linkedin'; // Go back to check builds
|
|
305
|
+
default:
|
|
306
|
+
return 'survey-scoping';
|
|
307
|
+
}
|
|
308
|
+
}
|
|
247
309
|
// Unknown workflow, return first phase of implement as fallback
|
|
248
310
|
return 'implement-scoping';
|
|
249
311
|
}
|
|
@@ -269,6 +331,12 @@ function getPhasesForWorkflow(workflowType, issueType) {
|
|
|
269
331
|
else if (workflowType === 'test') {
|
|
270
332
|
return [...TEST_PHASE_FLOW];
|
|
271
333
|
}
|
|
334
|
+
else if (workflowType === 'customer-development') {
|
|
335
|
+
return [...CUSTOMER_DEVELOPMENT_PHASE_FLOW];
|
|
336
|
+
}
|
|
337
|
+
else if (workflowType === 'user-survey') {
|
|
338
|
+
return [...USER_SURVEY_PHASE_FLOW];
|
|
339
|
+
}
|
|
272
340
|
else {
|
|
273
341
|
throw new Error(`Unknown workflow type: ${workflowType}`);
|
|
274
342
|
}
|
|
@@ -21,14 +21,25 @@ async function generateDigest(targetPath) {
|
|
|
21
21
|
return crypto_1.default.createHash('md5').update(content).digest('hex');
|
|
22
22
|
}
|
|
23
23
|
if (stats.isDirectory()) {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
const getAllFiles = (dir) => {
|
|
25
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
26
|
+
let files = [];
|
|
27
|
+
for (const entry of entries) {
|
|
28
|
+
const fullPath = path_1.default.join(dir, entry.name);
|
|
29
|
+
if (entry.isDirectory()) {
|
|
30
|
+
files = files.concat(getAllFiles(fullPath));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
files.push(fullPath);
|
|
34
|
+
}
|
|
31
35
|
}
|
|
36
|
+
return files;
|
|
37
|
+
};
|
|
38
|
+
const allFiles = getAllFiles(targetPath);
|
|
39
|
+
const hashes = [];
|
|
40
|
+
for (const file of allFiles.sort()) {
|
|
41
|
+
const content = fs_1.default.readFileSync(file);
|
|
42
|
+
hashes.push(crypto_1.default.createHash('md5').update(content).digest('hex'));
|
|
32
43
|
}
|
|
33
44
|
return crypto_1.default.createHash('md5').update(hashes.join('')).digest('hex');
|
|
34
45
|
}
|
|
@@ -122,9 +122,13 @@ async function testDebugSession() {
|
|
|
122
122
|
// Run if called directly
|
|
123
123
|
if (require.main === module) {
|
|
124
124
|
testDebugSession()
|
|
125
|
-
.then(success =>
|
|
125
|
+
.then(success => {
|
|
126
|
+
if (!success) {
|
|
127
|
+
throw new Error('Test failed');
|
|
128
|
+
}
|
|
129
|
+
})
|
|
126
130
|
.catch(err => {
|
|
127
131
|
console.error('Test runner error:', err);
|
|
128
|
-
|
|
132
|
+
throw err;
|
|
129
133
|
});
|
|
130
134
|
}
|
|
@@ -176,9 +176,13 @@ async function testEnhancedSessionInit() {
|
|
|
176
176
|
// Run if called directly
|
|
177
177
|
if (require.main === module) {
|
|
178
178
|
testEnhancedSessionInit()
|
|
179
|
-
.then(success =>
|
|
179
|
+
.then(success => {
|
|
180
|
+
if (!success) {
|
|
181
|
+
throw new Error('Test failed');
|
|
182
|
+
}
|
|
183
|
+
})
|
|
180
184
|
.catch(err => {
|
|
181
185
|
console.error('Test runner error:', err);
|
|
182
|
-
|
|
186
|
+
throw err;
|
|
183
187
|
});
|
|
184
188
|
}
|
|
@@ -234,8 +234,7 @@ const testCases = [
|
|
|
234
234
|
}
|
|
235
235
|
];
|
|
236
236
|
(0, test_utils_1.runTests)(testCases, async (t) => t.testFunction(), 'MCP Lifecycle Methods & Bootstrap Tools')
|
|
237
|
-
.then(() => process.exit(0))
|
|
238
237
|
.catch((err) => {
|
|
239
238
|
console.error('Test runner failed:', err);
|
|
240
|
-
|
|
239
|
+
throw err;
|
|
241
240
|
});
|
|
@@ -148,9 +148,13 @@ async function testMcpTemplateProcessing() {
|
|
|
148
148
|
// Run if called directly
|
|
149
149
|
if (require.main === module) {
|
|
150
150
|
testMcpTemplateProcessing()
|
|
151
|
-
.then(success =>
|
|
151
|
+
.then(success => {
|
|
152
|
+
if (!success) {
|
|
153
|
+
throw new Error('Test failed');
|
|
154
|
+
}
|
|
155
|
+
})
|
|
152
156
|
.catch(err => {
|
|
153
157
|
console.error('Test runner error:', err);
|
|
154
|
-
|
|
158
|
+
throw err;
|
|
155
159
|
});
|
|
156
160
|
}
|
|
@@ -153,9 +153,13 @@ async function testMultiProviderSupport() {
|
|
|
153
153
|
// Run if called directly
|
|
154
154
|
if (require.main === module) {
|
|
155
155
|
testMultiProviderSupport()
|
|
156
|
-
.then(success =>
|
|
156
|
+
.then(success => {
|
|
157
|
+
if (!success) {
|
|
158
|
+
throw new Error('Test failed');
|
|
159
|
+
}
|
|
160
|
+
})
|
|
157
161
|
.catch(err => {
|
|
158
162
|
console.error('Test runner error:', err);
|
|
159
|
-
|
|
163
|
+
throw err;
|
|
160
164
|
});
|
|
161
165
|
}
|
|
@@ -89,5 +89,7 @@ const testCases = nodeVersions.map(v => ({
|
|
|
89
89
|
tags: ['node', 'compatibility']
|
|
90
90
|
}));
|
|
91
91
|
(0, test_utils_1.runTests)(testCases, async (t) => await testInitOnNodeVersion(t.version), 'Node Compatibility Matrix')
|
|
92
|
-
.
|
|
93
|
-
.
|
|
92
|
+
.catch((err) => {
|
|
93
|
+
console.error('Test runner failed:', err);
|
|
94
|
+
throw err;
|
|
95
|
+
});
|
|
@@ -62,5 +62,7 @@ const testCases = nodeVersions.map(v => ({
|
|
|
62
62
|
tags: ['npm', 'compatibility']
|
|
63
63
|
}));
|
|
64
64
|
(0, test_utils_1.runTests)(testCases, async (t) => await testCleanNpmInstall(t.version), 'NPM Configuration Matrix')
|
|
65
|
-
.
|
|
66
|
-
.
|
|
65
|
+
.catch((err) => {
|
|
66
|
+
console.error('Test runner failed:', err);
|
|
67
|
+
throw err;
|
|
68
|
+
});
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Test suite for productivity report integration
|
|
4
|
+
* Tests the workflow and associated scripts integration
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
const node_test_1 = require("node:test");
|
|
11
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
// Test configuration
|
|
15
|
+
const WORKFLOW_PATH = 'registry/workflows/productivity-report/productivity-report.md';
|
|
16
|
+
const SCRIPTS_DIR = 'registry/scripts/productivity';
|
|
17
|
+
const EXPECTED_SCRIPTS = [
|
|
18
|
+
'productivity-report.sh',
|
|
19
|
+
'build-productivity-csv.mjs',
|
|
20
|
+
'fetch-pr-details.mjs'
|
|
21
|
+
];
|
|
22
|
+
(0, node_test_1.test)('Productivity Report Workflow Integration', async (t) => {
|
|
23
|
+
await t.test('workflow file exists and is accessible', () => {
|
|
24
|
+
node_assert_1.default.ok(fs_1.default.existsSync(WORKFLOW_PATH), `Workflow file should exist at ${WORKFLOW_PATH}`);
|
|
25
|
+
const content = fs_1.default.readFileSync(WORKFLOW_PATH, 'utf-8');
|
|
26
|
+
node_assert_1.default.ok(content.length > 0, 'Workflow file should not be empty');
|
|
27
|
+
node_assert_1.default.ok(content.includes('# Productivity Report Workflow'), 'Should contain proper workflow header');
|
|
28
|
+
node_assert_1.default.ok(content.includes('**Category:** Performance Analysis'), 'Should be categorized as Performance Analysis');
|
|
29
|
+
});
|
|
30
|
+
await t.test('workflow content is generalized', () => {
|
|
31
|
+
const content = fs_1.default.readFileSync(WORKFLOW_PATH, 'utf-8');
|
|
32
|
+
// Should not contain Ashley-Calendar-AI specific references
|
|
33
|
+
node_assert_1.default.ok(!content.includes('mathursrus/Ashley-Calendar-AI'), 'Should not contain hardcoded Ashley-Calendar-AI repository reference');
|
|
34
|
+
// Should contain generalized instructions
|
|
35
|
+
node_assert_1.default.ok(content.includes('any GitHub repository'), 'Should mention compatibility with any GitHub repository');
|
|
36
|
+
node_assert_1.default.ok(content.includes('automatically detects'), 'Should mention auto-detection capability');
|
|
37
|
+
node_assert_1.default.ok(content.includes('scripts/productivity/'), 'Should reference correct FRAIM script location');
|
|
38
|
+
});
|
|
39
|
+
await t.test('scripts directory exists with correct structure', () => {
|
|
40
|
+
node_assert_1.default.ok(fs_1.default.existsSync(SCRIPTS_DIR), `Scripts directory should exist at ${SCRIPTS_DIR}`);
|
|
41
|
+
const dirStats = fs_1.default.statSync(SCRIPTS_DIR);
|
|
42
|
+
node_assert_1.default.ok(dirStats.isDirectory(), 'Should be a directory');
|
|
43
|
+
});
|
|
44
|
+
await t.test('all expected scripts are present', () => {
|
|
45
|
+
for (const scriptName of EXPECTED_SCRIPTS) {
|
|
46
|
+
const scriptPath = path_1.default.join(SCRIPTS_DIR, scriptName);
|
|
47
|
+
node_assert_1.default.ok(fs_1.default.existsSync(scriptPath), `Script ${scriptName} should exist at ${scriptPath}`);
|
|
48
|
+
const content = fs_1.default.readFileSync(scriptPath, 'utf-8');
|
|
49
|
+
node_assert_1.default.ok(content.length > 0, `Script ${scriptName} should not be empty`);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
await t.test('main script is generalized and repository-agnostic', () => {
|
|
53
|
+
const scriptPath = path_1.default.join(SCRIPTS_DIR, 'productivity-report.sh');
|
|
54
|
+
const content = fs_1.default.readFileSync(scriptPath, 'utf-8');
|
|
55
|
+
// Should not contain hardcoded repository
|
|
56
|
+
node_assert_1.default.ok(!content.includes('mathursrus/Ashley-Calendar-AI'), 'Main script should not contain hardcoded repository reference');
|
|
57
|
+
// Should contain repository detection logic
|
|
58
|
+
node_assert_1.default.ok(content.includes('detect_repository()'), 'Should contain repository detection function');
|
|
59
|
+
node_assert_1.default.ok(content.includes('git remote get-url origin'), 'Should use git remote for auto-detection');
|
|
60
|
+
// Should handle environment variables
|
|
61
|
+
node_assert_1.default.ok(content.includes('REPO_FULL'), 'Should support REPO_FULL environment variable');
|
|
62
|
+
node_assert_1.default.ok(content.includes('REPO_OWNER') && content.includes('REPO_NAME'), 'Should support REPO_OWNER/REPO_NAME environment variables');
|
|
63
|
+
// Should have proper error handling
|
|
64
|
+
node_assert_1.default.ok(content.includes('validate_repo_format'), 'Should validate repository format');
|
|
65
|
+
node_assert_1.default.ok(content.includes('gh auth status'), 'Should check GitHub CLI authentication');
|
|
66
|
+
});
|
|
67
|
+
await t.test('fetch-pr-details script is generalized', () => {
|
|
68
|
+
const scriptPath = path_1.default.join(SCRIPTS_DIR, 'fetch-pr-details.mjs');
|
|
69
|
+
const content = fs_1.default.readFileSync(scriptPath, 'utf-8');
|
|
70
|
+
// Should not contain hardcoded repository
|
|
71
|
+
node_assert_1.default.ok(!content.includes('mathursrus/Ashley-Calendar-AI'), 'PR details script should not contain hardcoded repository');
|
|
72
|
+
// Should contain repository detection
|
|
73
|
+
node_assert_1.default.ok(content.includes('detectRepository()'), 'Should contain repository detection function');
|
|
74
|
+
node_assert_1.default.ok(content.includes('process.env.REPO_FULL'), 'Should check REPO_FULL environment variable');
|
|
75
|
+
// Should have proper error handling
|
|
76
|
+
node_assert_1.default.ok(content.includes('Invalid repository format'), 'Should validate repository format');
|
|
77
|
+
node_assert_1.default.ok(content.includes('timeout:'), 'Should have timeout handling for API calls');
|
|
78
|
+
});
|
|
79
|
+
await t.test('build-csv script is generalized', () => {
|
|
80
|
+
const scriptPath = path_1.default.join(SCRIPTS_DIR, 'build-productivity-csv.mjs');
|
|
81
|
+
const content = fs_1.default.readFileSync(scriptPath, 'utf-8');
|
|
82
|
+
// Should be repository-agnostic (no hardcoded repos)
|
|
83
|
+
node_assert_1.default.ok(!content.includes('mathursrus/Ashley-Calendar-AI'), 'CSV builder should not contain hardcoded repository');
|
|
84
|
+
// Should have proper error handling
|
|
85
|
+
node_assert_1.default.ok(content.includes('Data directory not found'), 'Should handle missing data directory');
|
|
86
|
+
node_assert_1.default.ok(content.includes('Warning:'), 'Should have warning messages for data issues');
|
|
87
|
+
// Should handle file locking
|
|
88
|
+
node_assert_1.default.ok(content.includes('productivity-report-new.csv'), 'Should handle locked files with alternative naming');
|
|
89
|
+
});
|
|
90
|
+
await t.test('scripts have proper Node.js shebang and are executable', () => {
|
|
91
|
+
const nodeScripts = ['build-productivity-csv.mjs', 'fetch-pr-details.mjs'];
|
|
92
|
+
for (const scriptName of nodeScripts) {
|
|
93
|
+
const scriptPath = path_1.default.join(SCRIPTS_DIR, scriptName);
|
|
94
|
+
const content = fs_1.default.readFileSync(scriptPath, 'utf-8');
|
|
95
|
+
node_assert_1.default.ok(content.startsWith('#!/usr/bin/env node'), `${scriptName} should have proper Node.js shebang`);
|
|
96
|
+
}
|
|
97
|
+
// Check bash script shebang
|
|
98
|
+
const bashScriptPath = path_1.default.join(SCRIPTS_DIR, 'productivity-report.sh');
|
|
99
|
+
const bashContent = fs_1.default.readFileSync(bashScriptPath, 'utf-8');
|
|
100
|
+
node_assert_1.default.ok(bashContent.startsWith('#!/bin/bash'), 'Bash script should have proper bash shebang');
|
|
101
|
+
});
|
|
102
|
+
await t.test('workflow references correct script locations', () => {
|
|
103
|
+
const content = fs_1.default.readFileSync(WORKFLOW_PATH, 'utf-8');
|
|
104
|
+
// Should reference FRAIM registry location
|
|
105
|
+
node_assert_1.default.ok(content.includes('~/.fraim/scripts/productivity/'), 'Should reference correct FRAIM script registry location');
|
|
106
|
+
// Should mention all three scripts
|
|
107
|
+
for (const scriptName of EXPECTED_SCRIPTS) {
|
|
108
|
+
node_assert_1.default.ok(content.includes(scriptName), `Should mention script ${scriptName}`);
|
|
109
|
+
}
|
|
110
|
+
// Should have proper usage instructions
|
|
111
|
+
node_assert_1.default.ok(content.includes('bash ~/.fraim/scripts/productivity/productivity-report.sh'), 'Should provide correct usage command');
|
|
112
|
+
});
|
|
113
|
+
await t.test('integration maintains FRAIM patterns', () => {
|
|
114
|
+
// Check that existing FRAIM structure is preserved
|
|
115
|
+
node_assert_1.default.ok(fs_1.default.existsSync('registry/scripts'), 'Existing scripts directory should be preserved');
|
|
116
|
+
node_assert_1.default.ok(fs_1.default.existsSync('registry/workflows'), 'Existing workflows directory should be preserved');
|
|
117
|
+
node_assert_1.default.ok(fs_1.default.existsSync('registry/workflows/productivity-report'), 'Productivity report workflows directory should exist');
|
|
118
|
+
// Check that new structure follows FRAIM patterns
|
|
119
|
+
const workflowContent = fs_1.default.readFileSync(WORKFLOW_PATH, 'utf-8');
|
|
120
|
+
node_assert_1.default.ok(workflowContent.includes('**Category:**'), 'Should follow FRAIM workflow format with category');
|
|
121
|
+
node_assert_1.default.ok(workflowContent.includes('## OVERVIEW'), 'Should follow FRAIM workflow format with OVERVIEW section');
|
|
122
|
+
node_assert_1.default.ok(workflowContent.includes('## WHEN TO USE'), 'Should follow FRAIM workflow format with WHEN TO USE section');
|
|
123
|
+
});
|
|
124
|
+
await t.test('scripts contain proper documentation and help', () => {
|
|
125
|
+
const mainScriptPath = path_1.default.join(SCRIPTS_DIR, 'productivity-report.sh');
|
|
126
|
+
const content = fs_1.default.readFileSync(mainScriptPath, 'utf-8');
|
|
127
|
+
// Should have usage information
|
|
128
|
+
node_assert_1.default.ok(content.includes('Usage:'), 'Main script should contain usage information');
|
|
129
|
+
node_assert_1.default.ok(content.includes('export REPO_FULL'), 'Should document environment variable usage');
|
|
130
|
+
// Should have dependency information
|
|
131
|
+
node_assert_1.default.ok(content.includes('gh CLI'), 'Should mention GitHub CLI dependency');
|
|
132
|
+
node_assert_1.default.ok(content.includes('node'), 'Should mention Node.js dependency');
|
|
133
|
+
node_assert_1.default.ok(content.includes('jq'), 'Should mention jq dependency');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
// Integration test with MCP server (if available)
|
|
137
|
+
(0, node_test_1.test)('MCP Server Integration', async (t) => {
|
|
138
|
+
await t.test('workflow is accessible via get_fraim_workflow pattern', () => {
|
|
139
|
+
// This test verifies the file is in the correct location for MCP access
|
|
140
|
+
const expectedPath = 'registry/workflows/productivity-report/productivity-report.md';
|
|
141
|
+
node_assert_1.default.ok(fs_1.default.existsSync(expectedPath), 'Workflow should be accessible at expected MCP path');
|
|
142
|
+
// Verify it follows the expected workflow format
|
|
143
|
+
const content = fs_1.default.readFileSync(expectedPath, 'utf-8');
|
|
144
|
+
node_assert_1.default.ok(content.includes('# Productivity Report Workflow'), 'Should have proper workflow header for MCP access');
|
|
145
|
+
});
|
|
146
|
+
await t.test('scripts are in registry location for FRAIM access', () => {
|
|
147
|
+
// Verify scripts are in the correct registry location
|
|
148
|
+
const registryPath = 'registry/scripts/productivity';
|
|
149
|
+
node_assert_1.default.ok(fs_1.default.existsSync(registryPath), 'Scripts should be in FRAIM registry location');
|
|
150
|
+
// Verify all scripts are accessible
|
|
151
|
+
for (const scriptName of EXPECTED_SCRIPTS) {
|
|
152
|
+
const scriptPath = path_1.default.join(registryPath, scriptName);
|
|
153
|
+
node_assert_1.default.ok(fs_1.default.existsSync(scriptPath), `Script ${scriptName} should be accessible in registry`);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
console.log('✅ Productivity integration tests completed');
|
|
@@ -142,8 +142,7 @@ const testCases = [
|
|
|
142
142
|
}
|
|
143
143
|
];
|
|
144
144
|
(0, test_utils_1.runTests)(testCases, async (t) => t.testFunction(), 'Fraim Session System')
|
|
145
|
-
.then(() => process.exit(0))
|
|
146
145
|
.catch((err) => {
|
|
147
146
|
console.error('Test runner failed:', err);
|
|
148
|
-
|
|
147
|
+
throw err;
|
|
149
148
|
});
|
|
@@ -187,8 +187,7 @@ const testCases = [
|
|
|
187
187
|
}
|
|
188
188
|
];
|
|
189
189
|
(0, test_utils_1.runTests)(testCases, async (t) => t.testFunction(), 'Fraim Telemetry System')
|
|
190
|
-
.then(() => process.exit(0))
|
|
191
190
|
.catch((err) => {
|
|
192
191
|
console.error('Test runner failed:', err);
|
|
193
|
-
|
|
192
|
+
throw err;
|
|
194
193
|
});
|