claude-flow-novice 2.15.6 → 2.15.8
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/cfn-extras/agents/google-sheets/google-sheets-coordinator.md +1 -1
- package/.claude/cfn-extras/docs/GOOGLE_SHEETS_CFN_LOOP.md +13 -11
- 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/deliverable-verifier.ts +103 -0
- package/.claude/skills/cfn-loop-orchestration/src/helpers/gate-check.ts +115 -0
- package/.claude/skills/cfn-loop-orchestration/src/helpers/iteration-manager.ts +45 -0
- package/.claude/skills/cfn-loop-orchestration/src/helpers/parse-test-results.ts +372 -0
- package/.claude/skills/cfn-loop-orchestration/src/helpers/timeout-calculator.ts +41 -0
- package/.claude/skills/cfn-loop-orchestration/tests/consensus.test.ts +142 -0
- package/.claude/skills/cfn-loop-orchestration/tests/deliverable-verifier.test.ts +197 -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/cfn-extras/agents/google-sheets/google-sheets-coordinator.md +1 -1
- package/claude-assets/cfn-extras/docs/GOOGLE_SHEETS_CFN_LOOP.md +13 -11
- 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/consensus.ts +87 -0
- package/claude-assets/skills/cfn-loop-orchestration/src/helpers/deliverable-verifier.ts +103 -0
- package/claude-assets/skills/cfn-loop-orchestration/src/helpers/gate-check.ts +115 -0
- package/claude-assets/skills/cfn-loop-orchestration/src/helpers/iteration-manager.ts +45 -0
- package/claude-assets/skills/cfn-loop-orchestration/src/helpers/parse-test-results.ts +372 -0
- package/claude-assets/skills/cfn-loop-orchestration/src/helpers/timeout-calculator.ts +41 -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 +197 -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/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,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeout Calculator
|
|
3
|
+
* Calculates mode and phase-specific timeouts for agent execution
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type Mode = 'mvp' | 'standard' | 'enterprise';
|
|
7
|
+
|
|
8
|
+
const BASE_TIMEOUTS: Record<Mode, number> = {
|
|
9
|
+
mvp: 1800, // 30 minutes
|
|
10
|
+
standard: 3600, // 60 minutes
|
|
11
|
+
enterprise: 7200 // 120 minutes
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const PHASE_MULTIPLIERS: Record<string, number> = {
|
|
15
|
+
'phase-1': 1.0, // Backend work
|
|
16
|
+
'phase-2': 1.5, // React components
|
|
17
|
+
'phase-3': 2.0, // Advanced components
|
|
18
|
+
'phase-4': 1.0 // Testing/integration
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Calculates timeout based on mode and optional phase
|
|
23
|
+
* @param params Mode and optional phase identifier
|
|
24
|
+
* @returns Timeout in seconds
|
|
25
|
+
*/
|
|
26
|
+
export function calculateTimeout(params: {
|
|
27
|
+
mode: Mode;
|
|
28
|
+
phase?: string;
|
|
29
|
+
}): number {
|
|
30
|
+
const baseTimeout = BASE_TIMEOUTS[params.mode] || BASE_TIMEOUTS.standard;
|
|
31
|
+
|
|
32
|
+
if (!params.phase) {
|
|
33
|
+
return baseTimeout;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Normalize phase to lowercase for case-insensitive matching
|
|
37
|
+
const normalizedPhase = params.phase.toLowerCase();
|
|
38
|
+
const multiplier = PHASE_MULTIPLIERS[normalizedPhase] || 1.0;
|
|
39
|
+
|
|
40
|
+
return Math.floor(baseTimeout * multiplier);
|
|
41
|
+
}
|
|
@@ -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 } 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
|
+
});
|