claude-flow-novice 2.15.6 → 2.15.7
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/.claude/skills/cfn-loop-orchestration/helpers/gate-check.sh +39 -577
- package/.claude/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +49 -270
- package/.claude/skills/cfn-loop-orchestration/src/helpers/consensus.ts +87 -0
- package/.claude/skills/cfn-loop-orchestration/src/helpers/gate-check.ts +115 -0
- package/.claude/skills/cfn-loop-orchestration/src/helpers/parse-test-results.ts +372 -0
- package/.claude/skills/cfn-loop-orchestration/tests/consensus.test.ts +142 -0
- package/.claude/skills/cfn-loop-orchestration/tests/deliverable-verifier.test.ts +199 -0
- package/.claude/skills/cfn-loop-orchestration/tests/gate-check.test.ts +325 -0
- package/.claude/skills/cfn-loop-orchestration/tests/iteration-manager.test.ts +132 -0
- package/.claude/skills/cfn-loop-orchestration/tests/parse-test-results.test.ts +382 -0
- package/.claude/skills/cfn-loop-orchestration/tests/timeout-calculator.test.ts +118 -0
- package/.claude/skills/cfn-redis-coordination/coverage/clover.xml +1447 -0
- package/.claude/skills/cfn-redis-coordination/coverage/coverage-final.json +13 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/agent-logger.ts.html +1423 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/agent-recovery.ts.html +1447 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/base.css +224 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/block-navigation.js +87 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/completion-reporter.ts.html +1273 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/context-manager.ts.html +1066 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/favicon.png +0 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/index.html +281 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/mode-detector.ts.html +550 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/prettify.css +1 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/prettify.js +2 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/redis-client.ts.html +2047 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/result-collector.ts.html +1396 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/sorter.js +210 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/swarm-manager.ts.html +1567 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/task-analyzer.ts.html +1297 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/task-executor.ts.html +1354 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/types.ts.html +790 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov-report/waiting-coordinator.ts.html +1846 -0
- package/.claude/skills/cfn-redis-coordination/coverage/lcov.info +2650 -0
- package/.claude/skills/cfn-redis-coordination/dist/task-analyzer.js +1 -1
- package/.claude/skills/cfn-redis-coordination/src/task-analyzer.ts +1 -1
- package/.claude/skills/cfn-redis-coordination/tests/coordination.test.ts +18 -9
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +13 -72
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +5 -65
- package/claude-assets/agents/cfn-dev-team/coordinators/consensus-builder.md +465 -508
- package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +733 -743
- package/claude-assets/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +13 -79
- package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +13 -18
- package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +13 -18
- package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +13 -18
- package/claude-assets/agents/cfn-dev-team/developers/backend-developer.md +15 -17
- package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +15 -17
- package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +15 -14
- package/claude-assets/agents/cfn-dev-team/developers/frontend/mobile-dev.md +15 -17
- package/claude-assets/agents/cfn-dev-team/developers/frontend/react-frontend-engineer.md +15 -17
- package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +15 -17
- package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +23 -30
- package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +13 -18
- package/claude-assets/agents/cfn-dev-team/developers/rust-developer.md +13 -18
- package/claude-assets/agents/cfn-dev-team/reviewers/code-reviewer.md +312 -317
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +23 -20
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +23 -20
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +23 -20
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +23 -20
- package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +15 -20
- package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +15 -20
- package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +718 -737
- package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +817 -828
- package/claude-assets/agents/cfn-dev-team/testers/interaction-tester.md +15 -20
- package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +8 -9
- package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +668 -684
- package/claude-assets/agents/cfn-dev-team/testers/playwright-tester.md +15 -20
- package/claude-assets/agents/cfn-dev-team/testers/tester.md +248 -253
- package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +13 -18
- package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +13 -18
- package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +13 -18
- package/claude-assets/skills/cfn-loop-orchestration/helpers/gate-check.sh +39 -577
- package/claude-assets/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +49 -270
- package/claude-assets/skills/cfn-loop-orchestration/src/helpers/gate-check.ts +115 -0
- package/claude-assets/skills/cfn-loop-orchestration/src/helpers/parse-test-results.ts +372 -0
- package/claude-assets/skills/cfn-loop-orchestration/tests/consensus.test.ts +142 -0
- package/claude-assets/skills/cfn-loop-orchestration/tests/deliverable-verifier.test.ts +199 -0
- package/claude-assets/skills/cfn-loop-orchestration/tests/gate-check.test.ts +325 -0
- package/claude-assets/skills/cfn-loop-orchestration/tests/iteration-manager.test.ts +132 -0
- package/claude-assets/skills/cfn-loop-orchestration/tests/parse-test-results.test.ts +382 -0
- package/claude-assets/skills/cfn-loop-orchestration/tests/timeout-calculator.test.ts +118 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/clover.xml +1447 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/coverage-final.json +13 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/agent-logger.ts.html +1423 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/agent-recovery.ts.html +1447 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/base.css +224 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/block-navigation.js +87 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/completion-reporter.ts.html +1273 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/context-manager.ts.html +1066 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/favicon.png +0 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/index.html +281 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/mode-detector.ts.html +550 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/prettify.css +1 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/prettify.js +2 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/redis-client.ts.html +2047 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/result-collector.ts.html +1396 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/sorter.js +210 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/swarm-manager.ts.html +1567 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/task-analyzer.ts.html +1297 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/task-executor.ts.html +1354 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/types.ts.html +790 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov-report/waiting-coordinator.ts.html +1846 -0
- package/claude-assets/skills/cfn-redis-coordination/coverage/lcov.info +2650 -0
- package/claude-assets/skills/cfn-redis-coordination/dist/task-analyzer.js +1 -1
- package/claude-assets/skills/cfn-redis-coordination/src/task-analyzer.ts +1 -1
- package/claude-assets/skills/cfn-redis-coordination/tests/coordination.test.ts +18 -9
- package/dist/agents/agent-loader.js +165 -146
- package/dist/agents/agent-loader.js.map +1 -1
- package/dist/cli/config-manager.js +91 -109
- package/dist/cli/config-manager.js.map +1 -1
- package/package.json +1 -1
- package/scripts/clean-agent-profiles.sh +112 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Results Parser - TypeScript Implementation
|
|
3
|
+
* Parses test output from multiple testing frameworks
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type TestFramework = 'jest' | 'mocha' | 'pytest' | 'tap' | 'go' | 'junit' | 'unknown';
|
|
7
|
+
|
|
8
|
+
export interface TestResults {
|
|
9
|
+
framework: TestFramework;
|
|
10
|
+
total: number;
|
|
11
|
+
passed: number;
|
|
12
|
+
failed: number;
|
|
13
|
+
skipped: number;
|
|
14
|
+
passRate: number;
|
|
15
|
+
durationMs: number;
|
|
16
|
+
failedTestNames: string[];
|
|
17
|
+
raw: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Parse Jest test output
|
|
22
|
+
*/
|
|
23
|
+
function parseJestOutput(output: string): TestResults {
|
|
24
|
+
let total = 0;
|
|
25
|
+
let passed = 0;
|
|
26
|
+
let failed = 0;
|
|
27
|
+
let skipped = 0;
|
|
28
|
+
let duration = 0;
|
|
29
|
+
const failedNames: string[] = [];
|
|
30
|
+
|
|
31
|
+
// Extract test counts from "Tests:" line
|
|
32
|
+
const testsLine = output.match(/^\s*Tests:.*$/m)?.[0] || '';
|
|
33
|
+
|
|
34
|
+
if (testsLine) {
|
|
35
|
+
const passedMatch = testsLine.match(/(\d+)\s+passed/);
|
|
36
|
+
const failedMatch = testsLine.match(/(\d+)\s+failed/);
|
|
37
|
+
const skippedMatch = testsLine.match(/(\d+)\s+skipped/);
|
|
38
|
+
const totalMatch = testsLine.match(/(\d+)\s+total/);
|
|
39
|
+
|
|
40
|
+
if (passedMatch?.[1]) passed = parseInt(passedMatch[1], 10);
|
|
41
|
+
if (failedMatch?.[1]) failed = parseInt(failedMatch[1], 10);
|
|
42
|
+
if (skippedMatch?.[1]) skipped = parseInt(skippedMatch[1], 10);
|
|
43
|
+
if (totalMatch?.[1]) total = parseInt(totalMatch[1], 10);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Extract duration from "Time:" line
|
|
47
|
+
const timeMatch = output.match(/Time:\s+([0-9.]+)\s*s/);
|
|
48
|
+
if (timeMatch?.[1]) {
|
|
49
|
+
duration = Math.round(parseFloat(timeMatch[1]) * 1000);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Extract failed test names
|
|
53
|
+
const failedNameMatches = output.matchAll(/●\s+(.+)/g);
|
|
54
|
+
for (const match of failedNameMatches) {
|
|
55
|
+
if (match[1]) failedNames.push(match[1].trim());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const passRate = total > 0 ? passed / total : 0.0;
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
framework: 'jest',
|
|
62
|
+
total,
|
|
63
|
+
passed,
|
|
64
|
+
failed,
|
|
65
|
+
skipped,
|
|
66
|
+
passRate: parseFloat(passRate.toFixed(4)),
|
|
67
|
+
durationMs: duration,
|
|
68
|
+
failedTestNames: failedNames,
|
|
69
|
+
raw: output,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Parse Mocha test output
|
|
75
|
+
*/
|
|
76
|
+
function parseMochaOutput(output: string): TestResults {
|
|
77
|
+
let total = 0;
|
|
78
|
+
let passed = 0;
|
|
79
|
+
let failed = 0;
|
|
80
|
+
let skipped = 0;
|
|
81
|
+
let duration = 0;
|
|
82
|
+
const failedNames: string[] = [];
|
|
83
|
+
|
|
84
|
+
// Extract test counts
|
|
85
|
+
const passedMatch = output.match(/(\d+)\s+passing/);
|
|
86
|
+
const failedMatch = output.match(/(\d+)\s+failing/);
|
|
87
|
+
const pendingMatch = output.match(/(\d+)\s+pending/);
|
|
88
|
+
|
|
89
|
+
if (passedMatch?.[1]) passed = parseInt(passedMatch[1], 10);
|
|
90
|
+
if (failedMatch?.[1]) failed = parseInt(failedMatch[1], 10);
|
|
91
|
+
if (pendingMatch?.[1]) skipped = parseInt(pendingMatch[1], 10);
|
|
92
|
+
|
|
93
|
+
total = passed + failed + skipped;
|
|
94
|
+
|
|
95
|
+
// Extract duration - can be in ms or s
|
|
96
|
+
const durationMsMatch = output.match(/passing\s*\((\d+)ms\)/);
|
|
97
|
+
const durationSMatch = output.match(/passing\s*\(([0-9.]+)s\)/);
|
|
98
|
+
|
|
99
|
+
if (durationMsMatch?.[1]) {
|
|
100
|
+
duration = parseInt(durationMsMatch[1], 10);
|
|
101
|
+
} else if (durationSMatch?.[1]) {
|
|
102
|
+
duration = Math.round(parseFloat(durationSMatch[1]) * 1000);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Extract failed test names
|
|
106
|
+
const failedNameMatches = output.matchAll(/^\s*\d+\)\s*(.+):/gm);
|
|
107
|
+
for (const match of failedNameMatches) {
|
|
108
|
+
if (match[1]) failedNames.push(match[1].trim());
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const passRate = total > 0 ? passed / total : 0.0;
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
framework: 'mocha',
|
|
115
|
+
total,
|
|
116
|
+
passed,
|
|
117
|
+
failed,
|
|
118
|
+
skipped,
|
|
119
|
+
passRate: parseFloat(passRate.toFixed(4)),
|
|
120
|
+
durationMs: duration,
|
|
121
|
+
failedTestNames: failedNames,
|
|
122
|
+
raw: output,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Parse Pytest test output
|
|
128
|
+
*/
|
|
129
|
+
function parsePytestOutput(output: string): TestResults {
|
|
130
|
+
let total = 0;
|
|
131
|
+
let passed = 0;
|
|
132
|
+
let failed = 0;
|
|
133
|
+
let skipped = 0;
|
|
134
|
+
let duration = 0;
|
|
135
|
+
const failedNames: string[] = [];
|
|
136
|
+
|
|
137
|
+
// Extract test counts
|
|
138
|
+
const passedMatch = output.match(/(\d+)\s+passed/);
|
|
139
|
+
const failedMatch = output.match(/(\d+)\s+failed/);
|
|
140
|
+
const skippedMatch = output.match(/(\d+)\s+skipped/);
|
|
141
|
+
|
|
142
|
+
if (passedMatch?.[1]) passed = parseInt(passedMatch[1], 10);
|
|
143
|
+
if (failedMatch?.[1]) failed = parseInt(failedMatch[1], 10);
|
|
144
|
+
if (skippedMatch?.[1]) skipped = parseInt(skippedMatch[1], 10);
|
|
145
|
+
|
|
146
|
+
total = passed + failed + skipped;
|
|
147
|
+
|
|
148
|
+
// Extract duration
|
|
149
|
+
const durationMatch = output.match(/in\s+([0-9.]+)s/);
|
|
150
|
+
if (durationMatch?.[1]) {
|
|
151
|
+
duration = Math.round(parseFloat(durationMatch[1]) * 1000);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Extract failed test names
|
|
155
|
+
const failedNameMatches = output.matchAll(/^\s*FAILED\s+([^\s]+)/gm);
|
|
156
|
+
for (const match of failedNameMatches) {
|
|
157
|
+
if (match[1]) failedNames.push(match[1].trim());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const passRate = total > 0 ? passed / total : 0.0;
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
framework: 'pytest',
|
|
164
|
+
total,
|
|
165
|
+
passed,
|
|
166
|
+
failed,
|
|
167
|
+
skipped,
|
|
168
|
+
passRate: parseFloat(passRate.toFixed(4)),
|
|
169
|
+
durationMs: duration,
|
|
170
|
+
failedTestNames: failedNames,
|
|
171
|
+
raw: output,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Parse TAP test output
|
|
177
|
+
*/
|
|
178
|
+
function parseTapOutput(output: string): TestResults {
|
|
179
|
+
let total = 0;
|
|
180
|
+
let passed = 0;
|
|
181
|
+
let failed = 0;
|
|
182
|
+
let skipped = 0;
|
|
183
|
+
const failedNames: string[] = [];
|
|
184
|
+
|
|
185
|
+
// Extract total from plan line (e.g., "1..10")
|
|
186
|
+
const planMatch = output.match(/^\s*1\.\.(\d+)/m);
|
|
187
|
+
if (planMatch?.[1]) {
|
|
188
|
+
total = parseInt(planMatch[1], 10);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Count ok lines (excluding SKIP)
|
|
192
|
+
const okMatches = output.matchAll(/^\s*ok\s+\d+/gm);
|
|
193
|
+
let okCount = 0;
|
|
194
|
+
for (const _ of okMatches) {
|
|
195
|
+
okCount++;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Count skipped tests
|
|
199
|
+
const skipMatches = output.matchAll(/^\s*ok\s+\d+.*#\s*SKIP/gm);
|
|
200
|
+
for (const _ of skipMatches) {
|
|
201
|
+
skipped++;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
passed = okCount - skipped;
|
|
205
|
+
|
|
206
|
+
// Count not ok lines
|
|
207
|
+
const notOkMatches = output.matchAll(/^\s*not ok\s+\d+\s+(.+)/gm);
|
|
208
|
+
for (const match of notOkMatches) {
|
|
209
|
+
failed++;
|
|
210
|
+
if (match[1]) failedNames.push(match[1].trim());
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const passRate = total > 0 ? passed / total : 0.0;
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
framework: 'tap',
|
|
217
|
+
total,
|
|
218
|
+
passed,
|
|
219
|
+
failed,
|
|
220
|
+
skipped,
|
|
221
|
+
passRate: parseFloat(passRate.toFixed(4)),
|
|
222
|
+
durationMs: 0,
|
|
223
|
+
failedTestNames: failedNames,
|
|
224
|
+
raw: output,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Parse Go test output
|
|
230
|
+
*/
|
|
231
|
+
function parseGoTestOutput(output: string): TestResults {
|
|
232
|
+
let total = 0;
|
|
233
|
+
let passed = 0;
|
|
234
|
+
let failed = 0;
|
|
235
|
+
let skipped = 0;
|
|
236
|
+
let duration = 0;
|
|
237
|
+
const failedNames: string[] = [];
|
|
238
|
+
|
|
239
|
+
// Count PASS lines
|
|
240
|
+
const passMatches = output.matchAll(/^\s*---\s+PASS:\s+(.+)/gm);
|
|
241
|
+
for (const _ of passMatches) {
|
|
242
|
+
passed++;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Count FAIL lines
|
|
246
|
+
const failMatches = output.matchAll(/^\s*---\s+FAIL:\s+(.+)/gm);
|
|
247
|
+
for (const match of failMatches) {
|
|
248
|
+
failed++;
|
|
249
|
+
if (match[1]) failedNames.push(match[1].trim());
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Count SKIP lines
|
|
253
|
+
const skipMatches = output.matchAll(/^\s*---\s+SKIP:/gm);
|
|
254
|
+
for (const _ of skipMatches) {
|
|
255
|
+
skipped++;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
total = passed + failed + skipped;
|
|
259
|
+
|
|
260
|
+
// Extract duration
|
|
261
|
+
const durationMatch = output.match(/ok\s+[^\s]+\s+([0-9.]+)s/);
|
|
262
|
+
if (durationMatch?.[1]) {
|
|
263
|
+
duration = Math.round(parseFloat(durationMatch[1]) * 1000);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const passRate = total > 0 ? passed / total : 0.0;
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
framework: 'go',
|
|
270
|
+
total,
|
|
271
|
+
passed,
|
|
272
|
+
failed,
|
|
273
|
+
skipped,
|
|
274
|
+
passRate: parseFloat(passRate.toFixed(4)),
|
|
275
|
+
durationMs: duration,
|
|
276
|
+
failedTestNames: failedNames,
|
|
277
|
+
raw: output,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Auto-detect testing framework from output
|
|
283
|
+
*/
|
|
284
|
+
function autoDetectFramework(output: string): TestFramework {
|
|
285
|
+
// Jest
|
|
286
|
+
if (output.match(/Test Suites:/i) || output.match(/PASS\s+.*\.test\.(js|ts)/)) {
|
|
287
|
+
return 'jest';
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Mocha
|
|
291
|
+
if (output.match(/\d+\s+passing/) && output.match(/\d+\s+failing/)) {
|
|
292
|
+
return 'mocha';
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Pytest
|
|
296
|
+
if (output.match(/====.*passed.*====/) || output.match(/FAILED.*\.py::/)) {
|
|
297
|
+
return 'pytest';
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// TAP
|
|
301
|
+
if (output.match(/1\.\.\d+/) || output.match(/^ok\s+\d+/m) || output.match(/^not ok\s+\d+/m)) {
|
|
302
|
+
return 'tap';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Go
|
|
306
|
+
if (output.match(/---\s+PASS:/) || output.match(/---\s+FAIL:/)) {
|
|
307
|
+
return 'go';
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return 'unknown';
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Main entry point - parse test results from any framework
|
|
315
|
+
*/
|
|
316
|
+
export function parseTestResults(framework: string, output: string): TestResults {
|
|
317
|
+
let detectedFramework: TestFramework = framework as TestFramework;
|
|
318
|
+
|
|
319
|
+
// Auto-detect if requested
|
|
320
|
+
if (framework === 'auto') {
|
|
321
|
+
detectedFramework = autoDetectFramework(output);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Parse based on framework
|
|
325
|
+
switch (detectedFramework) {
|
|
326
|
+
case 'jest':
|
|
327
|
+
return parseJestOutput(output);
|
|
328
|
+
case 'mocha':
|
|
329
|
+
return parseMochaOutput(output);
|
|
330
|
+
case 'pytest':
|
|
331
|
+
return parsePytestOutput(output);
|
|
332
|
+
case 'tap':
|
|
333
|
+
return parseTapOutput(output);
|
|
334
|
+
case 'go':
|
|
335
|
+
return parseGoTestOutput(output);
|
|
336
|
+
default:
|
|
337
|
+
return {
|
|
338
|
+
framework: 'unknown',
|
|
339
|
+
total: 0,
|
|
340
|
+
passed: 0,
|
|
341
|
+
failed: 0,
|
|
342
|
+
skipped: 0,
|
|
343
|
+
passRate: 0.0,
|
|
344
|
+
durationMs: 0,
|
|
345
|
+
failedTestNames: [],
|
|
346
|
+
raw: output,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* CLI entry point for bash wrapper
|
|
353
|
+
*/
|
|
354
|
+
if (require.main === module) {
|
|
355
|
+
const args = process.argv.slice(2);
|
|
356
|
+
|
|
357
|
+
if (args.length < 2) {
|
|
358
|
+
console.error('Usage: parse-test-results.js <framework|auto> <output_file_or_string>');
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const framework = args[0];
|
|
363
|
+
const input = args[1];
|
|
364
|
+
|
|
365
|
+
if (!framework || !input) {
|
|
366
|
+
console.error('Error: framework and input are required');
|
|
367
|
+
process.exit(1);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const result = parseTestResults(framework, input);
|
|
371
|
+
console.log(JSON.stringify(result, null, 2));
|
|
372
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consensus Collection and Validation Tests
|
|
3
|
+
* Tests for collecting Loop 2 validator scores and checking thresholds
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { collectConsensus, validateConsensus, ConsensusResult, ConsensusValidation } from '../src/helpers/consensus';
|
|
7
|
+
|
|
8
|
+
describe('consensus', () => {
|
|
9
|
+
describe('collectConsensus', () => {
|
|
10
|
+
it('should collect scores from multiple validators', () => {
|
|
11
|
+
const scores = [0.92, 0.95, 0.88];
|
|
12
|
+
const result = collectConsensus(scores);
|
|
13
|
+
|
|
14
|
+
expect(result.scores).toEqual(scores);
|
|
15
|
+
expect(result.average).toBeCloseTo(0.917, 2);
|
|
16
|
+
expect(result.count).toBe(3);
|
|
17
|
+
expect(result.min).toBe(0.88);
|
|
18
|
+
expect(result.max).toBe(0.95);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should handle single validator', () => {
|
|
22
|
+
const scores = [0.85];
|
|
23
|
+
const result = collectConsensus(scores);
|
|
24
|
+
|
|
25
|
+
expect(result.scores).toEqual([0.85]);
|
|
26
|
+
expect(result.average).toBe(0.85);
|
|
27
|
+
expect(result.count).toBe(1);
|
|
28
|
+
expect(result.min).toBe(0.85);
|
|
29
|
+
expect(result.max).toBe(0.85);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should reject empty scores array', () => {
|
|
33
|
+
expect(() => collectConsensus([])).toThrow('No consensus scores provided');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should reject invalid scores (negative)', () => {
|
|
37
|
+
expect(() => collectConsensus([-0.5, 0.9])).toThrow('Invalid consensus score');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should reject invalid scores (>1.0)', () => {
|
|
41
|
+
expect(() => collectConsensus([0.9, 1.5])).toThrow('Invalid consensus score');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle edge case scores (0.0 and 1.0)', () => {
|
|
45
|
+
const scores = [0.0, 1.0, 0.5];
|
|
46
|
+
const result = collectConsensus(scores);
|
|
47
|
+
|
|
48
|
+
expect(result.average).toBeCloseTo(0.5, 2);
|
|
49
|
+
expect(result.min).toBe(0.0);
|
|
50
|
+
expect(result.max).toBe(1.0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should calculate correct average for many validators', () => {
|
|
54
|
+
const scores = [0.90, 0.92, 0.88, 0.95, 0.91];
|
|
55
|
+
const result = collectConsensus(scores);
|
|
56
|
+
|
|
57
|
+
expect(result.average).toBeCloseTo(0.912, 2);
|
|
58
|
+
expect(result.count).toBe(5);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('validateConsensus', () => {
|
|
63
|
+
it('should pass when average >= threshold', () => {
|
|
64
|
+
const result = validateConsensus({
|
|
65
|
+
average: 0.92,
|
|
66
|
+
threshold: 0.90,
|
|
67
|
+
mode: 'standard'
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(result.passed).toBe(true);
|
|
71
|
+
expect(result.average).toBe(0.92);
|
|
72
|
+
expect(result.threshold).toBe(0.90);
|
|
73
|
+
expect(result.gap).toBeCloseTo(0.02, 2);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should fail when average < threshold', () => {
|
|
77
|
+
const result = validateConsensus({
|
|
78
|
+
average: 0.85,
|
|
79
|
+
threshold: 0.90,
|
|
80
|
+
mode: 'standard'
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(result.passed).toBe(false);
|
|
84
|
+
expect(result.gap).toBeCloseTo(-0.05, 2);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should use mvp mode threshold (0.80)', () => {
|
|
88
|
+
const result = validateConsensus({
|
|
89
|
+
average: 0.82,
|
|
90
|
+
mode: 'mvp'
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
expect(result.passed).toBe(true);
|
|
94
|
+
expect(result.threshold).toBe(0.80);
|
|
95
|
+
expect(result.mode).toBe('mvp');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should use standard mode threshold (0.90)', () => {
|
|
99
|
+
const result = validateConsensus({
|
|
100
|
+
average: 0.89,
|
|
101
|
+
mode: 'standard'
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(result.passed).toBe(false);
|
|
105
|
+
expect(result.threshold).toBe(0.90);
|
|
106
|
+
expect(result.mode).toBe('standard');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should use enterprise mode threshold (0.95)', () => {
|
|
110
|
+
const result = validateConsensus({
|
|
111
|
+
average: 0.94,
|
|
112
|
+
mode: 'enterprise'
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
expect(result.passed).toBe(false);
|
|
116
|
+
expect(result.threshold).toBe(0.95);
|
|
117
|
+
expect(result.mode).toBe('enterprise');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should allow explicit threshold override', () => {
|
|
121
|
+
const result = validateConsensus({
|
|
122
|
+
average: 0.88,
|
|
123
|
+
threshold: 0.85,
|
|
124
|
+
mode: 'standard'
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
expect(result.passed).toBe(true);
|
|
128
|
+
expect(result.threshold).toBe(0.85); // Uses explicit, not mode default
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should handle exact threshold match', () => {
|
|
132
|
+
const result = validateConsensus({
|
|
133
|
+
average: 0.90,
|
|
134
|
+
threshold: 0.90,
|
|
135
|
+
mode: 'standard'
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
expect(result.passed).toBe(true);
|
|
139
|
+
expect(result.gap).toBe(0);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deliverable Verifier Tests
|
|
3
|
+
* Tests for verifying expected deliverables exist (prevents "consensus on vapor")
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { verifyDeliverables, VerificationResult } from '../src/helpers/deliverable-verifier';
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
|
|
10
|
+
describe('deliverable-verifier', () => {
|
|
11
|
+
describe('file existence verification', () => {
|
|
12
|
+
it('should verify existing files', () => {
|
|
13
|
+
const result = verifyDeliverables({
|
|
14
|
+
files: ['package.json', 'tsconfig.json']
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
expect(result.verified).toBe(true);
|
|
18
|
+
expect(result.missing).toEqual([]);
|
|
19
|
+
expect(result.found).toContain('package.json');
|
|
20
|
+
expect(result.found).toContain('tsconfig.json');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should detect missing files', () => {
|
|
24
|
+
const result = verifyDeliverables({
|
|
25
|
+
files: ['nonexistent-file-12345.txt']
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(result.verified).toBe(false);
|
|
29
|
+
expect(result.missing).toContain('nonexistent-file-12345.txt');
|
|
30
|
+
expect(result.found).toEqual([]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should detect partial missing files', () => {
|
|
34
|
+
const result = verifyDeliverables({
|
|
35
|
+
files: ['package.json', 'nonexistent.txt', 'tsconfig.json']
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
expect(result.verified).toBe(false);
|
|
39
|
+
expect(result.missing).toEqual(['nonexistent.txt']);
|
|
40
|
+
expect(result.found).toContain('package.json');
|
|
41
|
+
expect(result.found).toContain('tsconfig.json');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle empty file list', () => {
|
|
45
|
+
const result = verifyDeliverables({
|
|
46
|
+
files: []
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(result.verified).toBe(true);
|
|
50
|
+
expect(result.missing).toEqual([]);
|
|
51
|
+
expect(result.found).toEqual([]);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('file type validation', () => {
|
|
56
|
+
it('should verify TypeScript files', () => {
|
|
57
|
+
const result = verifyDeliverables({
|
|
58
|
+
files: ['src/helpers/gate-check.ts'],
|
|
59
|
+
expectedTypes: ['.ts']
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(result.verified).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should verify shell script files', () => {
|
|
66
|
+
const result = verifyDeliverables({
|
|
67
|
+
files: ['helpers/consensus.sh'],
|
|
68
|
+
expectedTypes: ['.sh']
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
expect(result.verified).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should reject wrong file types', () => {
|
|
75
|
+
const result = verifyDeliverables({
|
|
76
|
+
files: ['package.json'],
|
|
77
|
+
expectedTypes: ['.ts']
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(result.verified).toBe(false);
|
|
81
|
+
expect(result.typeErrors).toContain('package.json');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should allow multiple file types', () => {
|
|
85
|
+
const result = verifyDeliverables({
|
|
86
|
+
files: ['package.json', 'tsconfig.json'],
|
|
87
|
+
expectedTypes: ['.json', '.ts']
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(result.verified).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('git change detection', () => {
|
|
95
|
+
it('should detect when files are created', () => {
|
|
96
|
+
const result = verifyDeliverables({
|
|
97
|
+
files: [],
|
|
98
|
+
requireGitChanges: true
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// This test depends on actual git state, so we check structure
|
|
102
|
+
expect(result).toHaveProperty('gitChanges');
|
|
103
|
+
expect(typeof result.gitChanges).toBe('number');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should fail when no changes and changes required', () => {
|
|
107
|
+
// Mock scenario: implementation task with no files
|
|
108
|
+
const result = verifyDeliverables({
|
|
109
|
+
files: [],
|
|
110
|
+
requireGitChanges: true,
|
|
111
|
+
taskType: 'implementation'
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// If no files specified and git has no changes, should fail for implementation
|
|
115
|
+
if (result.gitChanges === 0) {
|
|
116
|
+
expect(result.verified).toBe(false);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('task type keyword detection', () => {
|
|
122
|
+
it('should detect implementation keywords', () => {
|
|
123
|
+
const keywords = ['create', 'build', 'implement', 'add', 'generate'];
|
|
124
|
+
|
|
125
|
+
keywords.forEach(keyword => {
|
|
126
|
+
const result = verifyDeliverables({
|
|
127
|
+
files: [],
|
|
128
|
+
taskType: keyword,
|
|
129
|
+
requireGitChanges: true
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Should require git changes for implementation keywords
|
|
133
|
+
expect(result).toHaveProperty('requiresChanges');
|
|
134
|
+
if (result.requiresChanges) {
|
|
135
|
+
expect(result.gitChanges).toBeGreaterThan(-1); // Check git changes were counted
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should not require changes for read-only tasks', () => {
|
|
141
|
+
const result = verifyDeliverables({
|
|
142
|
+
files: [],
|
|
143
|
+
taskType: 'analyze',
|
|
144
|
+
requireGitChanges: false
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
expect(result.verified).toBe(true);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('consensus on vapor prevention', () => {
|
|
152
|
+
it('should fail if implementation task has no deliverables', () => {
|
|
153
|
+
const result = verifyDeliverables({
|
|
154
|
+
files: [],
|
|
155
|
+
taskType: 'implement authentication',
|
|
156
|
+
requireGitChanges: true
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// If git has changes, this should pass; otherwise fail
|
|
160
|
+
if (result.gitChanges === 0) {
|
|
161
|
+
expect(result.verified).toBe(false);
|
|
162
|
+
expect(result.reason).toContain('vapor');
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should pass if implementation task has deliverables', () => {
|
|
167
|
+
const result = verifyDeliverables({
|
|
168
|
+
files: ['package.json'], // Existing file as proxy
|
|
169
|
+
taskType: 'implement feature'
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
expect(result.verified).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe('verification reporting', () => {
|
|
177
|
+
it('should provide detailed results', () => {
|
|
178
|
+
const result = verifyDeliverables({
|
|
179
|
+
files: ['package.json', 'missing.txt']
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
expect(result).toHaveProperty('verified');
|
|
183
|
+
expect(result).toHaveProperty('files');
|
|
184
|
+
expect(result).toHaveProperty('missing');
|
|
185
|
+
expect(result).toHaveProperty('found');
|
|
186
|
+
expect(result.files).toEqual(['package.json', 'missing.txt']);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should track all requested files', () => {
|
|
190
|
+
const requestedFiles = ['file1.ts', 'file2.ts', 'file3.ts'];
|
|
191
|
+
const result = verifyDeliverables({
|
|
192
|
+
files: requestedFiles
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(result.files).toEqual(requestedFiles);
|
|
196
|
+
expect(result.found.length + result.missing.length).toBe(requestedFiles.length);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|