fe-harness 1.0.0
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/README.md +55 -0
- package/agents/fe-codebase-mapper.md +945 -0
- package/agents/fe-design-scanner.md +47 -0
- package/agents/fe-executor.md +221 -0
- package/agents/fe-fix-loop.md +310 -0
- package/agents/fe-fixer.md +153 -0
- package/agents/fe-project-scanner.md +95 -0
- package/agents/fe-reviewer.md +141 -0
- package/agents/fe-verifier.md +231 -0
- package/agents/fe-wave-runner.md +477 -0
- package/bin/install.js +292 -0
- package/commands/fe/complete.md +35 -0
- package/commands/fe/execute.md +46 -0
- package/commands/fe/help.md +17 -0
- package/commands/fe/map-codebase.md +60 -0
- package/commands/fe/plan.md +36 -0
- package/commands/fe/status.md +39 -0
- package/fe-harness/bin/browser.cjs +271 -0
- package/fe-harness/bin/fe-tools.cjs +317 -0
- package/fe-harness/bin/lib/__tests__/browser.test.cjs +422 -0
- package/fe-harness/bin/lib/__tests__/config.test.cjs +93 -0
- package/fe-harness/bin/lib/__tests__/core.test.cjs +127 -0
- package/fe-harness/bin/lib/__tests__/scoring.test.cjs +130 -0
- package/fe-harness/bin/lib/__tests__/tasks.test.cjs +698 -0
- package/fe-harness/bin/lib/browser-core.cjs +365 -0
- package/fe-harness/bin/lib/config.cjs +34 -0
- package/fe-harness/bin/lib/core.cjs +135 -0
- package/fe-harness/bin/lib/logger.cjs +93 -0
- package/fe-harness/bin/lib/scoring.cjs +219 -0
- package/fe-harness/bin/lib/tasks.cjs +632 -0
- package/fe-harness/references/model-profiles.md +44 -0
- package/fe-harness/templates/config.jsonc +31 -0
- package/fe-harness/vendor/.gitkeep +0 -0
- package/fe-harness/vendor/puppeteer-core.cjs +445 -0
- package/fe-harness/workflows/complete.md +143 -0
- package/fe-harness/workflows/execute.md +227 -0
- package/fe-harness/workflows/help.md +89 -0
- package/fe-harness/workflows/map-codebase.md +331 -0
- package/fe-harness/workflows/plan.md +244 -0
- package/package.json +35 -0
- package/scripts/bundle-puppeteer.js +38 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { describe, it } = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
const { DESIGN_WEIGHTS, LOGIC_WEIGHTS, calculateScore, checkRegression } = require('../scoring.cjs');
|
|
6
|
+
|
|
7
|
+
const defaultThresholds = {
|
|
8
|
+
verifyThreshold: 80,
|
|
9
|
+
reviewThreshold: 80,
|
|
10
|
+
dimensionThreshold: 6,
|
|
11
|
+
scoreDropTolerance: 3,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// --- calculateScore ---
|
|
15
|
+
|
|
16
|
+
describe('calculateScore', () => {
|
|
17
|
+
describe('design type', () => {
|
|
18
|
+
it('should return 100 when all dimensions score 10', () => {
|
|
19
|
+
const scores = {};
|
|
20
|
+
for (const dim of Object.keys(DESIGN_WEIGHTS)) scores[dim] = 10;
|
|
21
|
+
const result = calculateScore(scores, 'design', defaultThresholds);
|
|
22
|
+
assert.equal(result.total_score, 100);
|
|
23
|
+
assert.equal(result.passed, true);
|
|
24
|
+
assert.deepEqual(result.failed_dimensions, []);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should return 0 when all dimensions score 0', () => {
|
|
28
|
+
const scores = {};
|
|
29
|
+
for (const dim of Object.keys(DESIGN_WEIGHTS)) scores[dim] = 0;
|
|
30
|
+
const result = calculateScore(scores, 'design', defaultThresholds);
|
|
31
|
+
assert.equal(result.total_score, 0);
|
|
32
|
+
assert.equal(result.passed, false);
|
|
33
|
+
assert.deepEqual(result.failed_dimensions, Object.keys(DESIGN_WEIGHTS));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should fail when total score is below verifyThreshold', () => {
|
|
37
|
+
const scores = {};
|
|
38
|
+
for (const dim of Object.keys(DESIGN_WEIGHTS)) scores[dim] = 7;
|
|
39
|
+
const result = calculateScore(scores, 'design', defaultThresholds);
|
|
40
|
+
assert.equal(result.total_score, 70);
|
|
41
|
+
assert.equal(result.passed, false);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should fail when any dimension is below dimensionThreshold', () => {
|
|
45
|
+
const scores = {};
|
|
46
|
+
for (const dim of Object.keys(DESIGN_WEIGHTS)) scores[dim] = 10;
|
|
47
|
+
scores.layout = 5; // below 6
|
|
48
|
+
const result = calculateScore(scores, 'design', defaultThresholds);
|
|
49
|
+
assert.equal(result.passed, false);
|
|
50
|
+
assert.deepEqual(result.failed_dimensions, ['layout']);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should treat missing dimensions as 0', () => {
|
|
54
|
+
const result = calculateScore({}, 'design', defaultThresholds);
|
|
55
|
+
assert.equal(result.total_score, 0);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should calculate weighted scores correctly', () => {
|
|
59
|
+
const scores = { layout: 8, spacing: 6, colors: 6, typography: 10, borders: 10, shadows: 10, icons_images: 10, completeness: 8 };
|
|
60
|
+
const result = calculateScore(scores, 'design', defaultThresholds);
|
|
61
|
+
// weighted sum = 8*2 + 6*1.5 + 6*1.5 + 10*1 + 10*0.5 + 10*0.5 + 10*1 + 8*2 = 16+9+9+10+5+5+10+16 = 80
|
|
62
|
+
// total = round(80 / 100 * 100) = 80
|
|
63
|
+
assert.equal(result.total_score, 80);
|
|
64
|
+
assert.equal(result.passed, true);
|
|
65
|
+
assert.equal(result.weighted_scores.layout, 16);
|
|
66
|
+
assert.equal(result.weighted_scores.spacing, 9);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('logic type', () => {
|
|
71
|
+
it('should return 100 when all dimensions score 10', () => {
|
|
72
|
+
const scores = {};
|
|
73
|
+
for (const dim of Object.keys(LOGIC_WEIGHTS)) scores[dim] = 10;
|
|
74
|
+
const result = calculateScore(scores, 'logic', defaultThresholds);
|
|
75
|
+
assert.equal(result.total_score, 100);
|
|
76
|
+
assert.equal(result.passed, true);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should use reviewThreshold for logic type', () => {
|
|
80
|
+
const thresholds = { ...defaultThresholds, reviewThreshold: 90 };
|
|
81
|
+
const scores = {};
|
|
82
|
+
for (const dim of Object.keys(LOGIC_WEIGHTS)) scores[dim] = 8;
|
|
83
|
+
const result = calculateScore(scores, 'logic', thresholds);
|
|
84
|
+
assert.equal(result.total_score, 80);
|
|
85
|
+
assert.equal(result.passed, false); // 80 < 90
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// --- checkRegression ---
|
|
91
|
+
|
|
92
|
+
describe('checkRegression', () => {
|
|
93
|
+
it('should detect total score regression exceeding tolerance', () => {
|
|
94
|
+
const current = { total_score: 75, scores: {} };
|
|
95
|
+
const best = { total_score: 80, scores: {} };
|
|
96
|
+
const result = checkRegression(current, best, defaultThresholds);
|
|
97
|
+
assert.equal(result.regressed, true);
|
|
98
|
+
assert.equal(result.action, 'rollback');
|
|
99
|
+
assert.ok(result.reason.includes('dropped 5'));
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should allow score drop within tolerance', () => {
|
|
103
|
+
const current = { total_score: 78, scores: {} };
|
|
104
|
+
const best = { total_score: 80, scores: {} };
|
|
105
|
+
const result = checkRegression(current, best, defaultThresholds);
|
|
106
|
+
assert.equal(result.regressed, false);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should detect dimension regression (previously passed, now failed)', () => {
|
|
110
|
+
const current = { total_score: 80, scores: { layout: 5 } };
|
|
111
|
+
const best = { total_score: 80, scores: { layout: 8 } };
|
|
112
|
+
const result = checkRegression(current, best, defaultThresholds);
|
|
113
|
+
assert.equal(result.regressed, true);
|
|
114
|
+
assert.ok(result.reason.includes('layout'));
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should not regress if dimension was already below threshold', () => {
|
|
118
|
+
const current = { total_score: 80, scores: { layout: 4 } };
|
|
119
|
+
const best = { total_score: 80, scores: { layout: 5 } };
|
|
120
|
+
const result = checkRegression(current, best, defaultThresholds);
|
|
121
|
+
assert.equal(result.regressed, false);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should handle missing scores gracefully', () => {
|
|
125
|
+
const current = { total_score: 80 };
|
|
126
|
+
const best = { total_score: 80 };
|
|
127
|
+
const result = checkRegression(current, best, defaultThresholds);
|
|
128
|
+
assert.equal(result.regressed, false);
|
|
129
|
+
});
|
|
130
|
+
});
|