wogiflow 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/.workflow/agents/reviewer.md +81 -0
- package/.workflow/agents/security.md +94 -0
- package/.workflow/agents/story-writer.md +58 -0
- package/.workflow/bridges/base-bridge.js +395 -0
- package/.workflow/bridges/claude-bridge.js +434 -0
- package/.workflow/bridges/index.js +130 -0
- package/.workflow/lib/assumption-detector.js +481 -0
- package/.workflow/lib/config-substitution.js +371 -0
- package/.workflow/lib/failure-categories.js +478 -0
- package/.workflow/state/app-map.md.template +15 -0
- package/.workflow/state/architecture.md.template +24 -0
- package/.workflow/state/component-index.json.template +5 -0
- package/.workflow/state/decisions.md.template +15 -0
- package/.workflow/state/feedback-patterns.md.template +9 -0
- package/.workflow/state/knowledge-sync.json.template +6 -0
- package/.workflow/state/progress.md.template +14 -0
- package/.workflow/state/ready.json.template +7 -0
- package/.workflow/state/request-log.md.template +14 -0
- package/.workflow/state/session-state.json.template +11 -0
- package/.workflow/state/stack.md.template +33 -0
- package/.workflow/state/testing.md.template +36 -0
- package/.workflow/templates/claude-md.hbs +257 -0
- package/.workflow/templates/correction-report.md +67 -0
- package/.workflow/templates/gemini-md.hbs +52 -0
- package/README.md +1802 -0
- package/bin/flow +205 -0
- package/lib/index.js +33 -0
- package/lib/installer.js +467 -0
- package/lib/release-channel.js +269 -0
- package/lib/skill-registry.js +526 -0
- package/lib/upgrader.js +401 -0
- package/lib/utils.js +305 -0
- package/package.json +64 -0
- package/scripts/flow +985 -0
- package/scripts/flow-adaptive-learning.js +1259 -0
- package/scripts/flow-aggregate.js +488 -0
- package/scripts/flow-archive +133 -0
- package/scripts/flow-auto-context.js +1015 -0
- package/scripts/flow-auto-learn.js +615 -0
- package/scripts/flow-bridge.js +223 -0
- package/scripts/flow-browser-suggest.js +316 -0
- package/scripts/flow-bug.js +247 -0
- package/scripts/flow-cascade.js +711 -0
- package/scripts/flow-changelog +85 -0
- package/scripts/flow-checkpoint.js +483 -0
- package/scripts/flow-cli.js +403 -0
- package/scripts/flow-code-intelligence.js +760 -0
- package/scripts/flow-complexity.js +502 -0
- package/scripts/flow-config-set.js +152 -0
- package/scripts/flow-constants.js +157 -0
- package/scripts/flow-context +152 -0
- package/scripts/flow-context-init.js +482 -0
- package/scripts/flow-context-monitor.js +384 -0
- package/scripts/flow-context-scoring.js +886 -0
- package/scripts/flow-correct.js +458 -0
- package/scripts/flow-damage-control.js +985 -0
- package/scripts/flow-deps +101 -0
- package/scripts/flow-diff.js +700 -0
- package/scripts/flow-done +151 -0
- package/scripts/flow-done.js +489 -0
- package/scripts/flow-durable-session.js +1541 -0
- package/scripts/flow-entropy-monitor.js +345 -0
- package/scripts/flow-export-profile +349 -0
- package/scripts/flow-export-scanner.js +1046 -0
- package/scripts/flow-figma-confirm.js +400 -0
- package/scripts/flow-figma-extract.js +496 -0
- package/scripts/flow-figma-generate.js +683 -0
- package/scripts/flow-figma-index.js +909 -0
- package/scripts/flow-figma-match.js +617 -0
- package/scripts/flow-figma-mcp-server.js +518 -0
- package/scripts/flow-figma-pipeline.js +414 -0
- package/scripts/flow-file-ops.js +301 -0
- package/scripts/flow-gate-confidence.js +825 -0
- package/scripts/flow-guided-edit.js +659 -0
- package/scripts/flow-health +185 -0
- package/scripts/flow-health.js +413 -0
- package/scripts/flow-hooks.js +556 -0
- package/scripts/flow-http-client.js +249 -0
- package/scripts/flow-hybrid-detect.js +167 -0
- package/scripts/flow-hybrid-interactive.js +591 -0
- package/scripts/flow-hybrid-test.js +152 -0
- package/scripts/flow-import-profile +439 -0
- package/scripts/flow-init +253 -0
- package/scripts/flow-instruction-richness.js +827 -0
- package/scripts/flow-jira-integration.js +579 -0
- package/scripts/flow-knowledge-router.js +522 -0
- package/scripts/flow-knowledge-sync.js +589 -0
- package/scripts/flow-linear-integration.js +631 -0
- package/scripts/flow-links.js +774 -0
- package/scripts/flow-log-manager.js +559 -0
- package/scripts/flow-loop-enforcer.js +1246 -0
- package/scripts/flow-loop-retry-learning.js +630 -0
- package/scripts/flow-lsp.js +923 -0
- package/scripts/flow-map-index +348 -0
- package/scripts/flow-map-sync +201 -0
- package/scripts/flow-memory-blocks.js +668 -0
- package/scripts/flow-memory-compactor.js +350 -0
- package/scripts/flow-memory-db.js +1110 -0
- package/scripts/flow-memory-sync.js +484 -0
- package/scripts/flow-metrics.js +353 -0
- package/scripts/flow-migrate-ids.js +370 -0
- package/scripts/flow-model-adapter.js +802 -0
- package/scripts/flow-model-router.js +884 -0
- package/scripts/flow-models.js +1231 -0
- package/scripts/flow-morning.js +517 -0
- package/scripts/flow-multi-approach.js +660 -0
- package/scripts/flow-new-feature +86 -0
- package/scripts/flow-onboard +1042 -0
- package/scripts/flow-orchestrate-llm.js +459 -0
- package/scripts/flow-orchestrate.js +3592 -0
- package/scripts/flow-output.js +123 -0
- package/scripts/flow-parallel-detector.js +399 -0
- package/scripts/flow-parallel-dispatch.js +987 -0
- package/scripts/flow-parallel.js +428 -0
- package/scripts/flow-pattern-enforcer.js +600 -0
- package/scripts/flow-prd-manager.js +282 -0
- package/scripts/flow-progress.js +323 -0
- package/scripts/flow-project-analyzer.js +975 -0
- package/scripts/flow-prompt-composer.js +487 -0
- package/scripts/flow-providers.js +1381 -0
- package/scripts/flow-queue.js +308 -0
- package/scripts/flow-ready +82 -0
- package/scripts/flow-ready.js +189 -0
- package/scripts/flow-regression.js +396 -0
- package/scripts/flow-response-parser.js +450 -0
- package/scripts/flow-resume.js +284 -0
- package/scripts/flow-rules-sync.js +439 -0
- package/scripts/flow-run-trace.js +718 -0
- package/scripts/flow-safety.js +587 -0
- package/scripts/flow-search +104 -0
- package/scripts/flow-security.js +481 -0
- package/scripts/flow-session-end +106 -0
- package/scripts/flow-session-end.js +437 -0
- package/scripts/flow-session-state.js +671 -0
- package/scripts/flow-setup-hooks +216 -0
- package/scripts/flow-setup-hooks.js +377 -0
- package/scripts/flow-skill-create.js +329 -0
- package/scripts/flow-skill-creator.js +572 -0
- package/scripts/flow-skill-generator.js +1046 -0
- package/scripts/flow-skill-learn.js +880 -0
- package/scripts/flow-skill-matcher.js +578 -0
- package/scripts/flow-spec-generator.js +820 -0
- package/scripts/flow-stack-wizard.js +895 -0
- package/scripts/flow-standup +162 -0
- package/scripts/flow-start +74 -0
- package/scripts/flow-start.js +235 -0
- package/scripts/flow-status +110 -0
- package/scripts/flow-status.js +301 -0
- package/scripts/flow-step-browser.js +83 -0
- package/scripts/flow-step-changelog.js +217 -0
- package/scripts/flow-step-comments.js +306 -0
- package/scripts/flow-step-complexity.js +234 -0
- package/scripts/flow-step-coverage.js +218 -0
- package/scripts/flow-step-knowledge.js +193 -0
- package/scripts/flow-step-pr-tests.js +364 -0
- package/scripts/flow-step-regression.js +89 -0
- package/scripts/flow-step-review.js +516 -0
- package/scripts/flow-step-security.js +162 -0
- package/scripts/flow-step-silent-failures.js +290 -0
- package/scripts/flow-step-simplifier.js +346 -0
- package/scripts/flow-story +105 -0
- package/scripts/flow-story.js +500 -0
- package/scripts/flow-suspend.js +252 -0
- package/scripts/flow-sync-daemon.js +654 -0
- package/scripts/flow-task-analyzer.js +606 -0
- package/scripts/flow-team-dashboard.js +748 -0
- package/scripts/flow-team-sync.js +752 -0
- package/scripts/flow-team.js +977 -0
- package/scripts/flow-tech-options.js +528 -0
- package/scripts/flow-templates.js +812 -0
- package/scripts/flow-tiered-learning.js +728 -0
- package/scripts/flow-trace +204 -0
- package/scripts/flow-transcript-chunking.js +1106 -0
- package/scripts/flow-transcript-digest.js +7918 -0
- package/scripts/flow-transcript-language.js +465 -0
- package/scripts/flow-transcript-parsing.js +1085 -0
- package/scripts/flow-transcript-stories.js +2194 -0
- package/scripts/flow-update-map +224 -0
- package/scripts/flow-utils.js +2242 -0
- package/scripts/flow-verification.js +644 -0
- package/scripts/flow-verify.js +1177 -0
- package/scripts/flow-voice-input.js +638 -0
- package/scripts/flow-watch +168 -0
- package/scripts/flow-workflow-steps.js +521 -0
- package/scripts/flow-workflow.js +1029 -0
- package/scripts/flow-worktree.js +489 -0
- package/scripts/hooks/adapters/base-adapter.js +102 -0
- package/scripts/hooks/adapters/claude-code.js +359 -0
- package/scripts/hooks/adapters/index.js +79 -0
- package/scripts/hooks/core/component-check.js +341 -0
- package/scripts/hooks/core/index.js +35 -0
- package/scripts/hooks/core/loop-check.js +241 -0
- package/scripts/hooks/core/session-context.js +294 -0
- package/scripts/hooks/core/task-gate.js +177 -0
- package/scripts/hooks/core/validation.js +230 -0
- package/scripts/hooks/entry/claude-code/post-tool-use.js +65 -0
- package/scripts/hooks/entry/claude-code/pre-tool-use.js +89 -0
- package/scripts/hooks/entry/claude-code/session-end.js +87 -0
- package/scripts/hooks/entry/claude-code/session-start.js +46 -0
- package/scripts/hooks/entry/claude-code/stop.js +43 -0
- package/scripts/postinstall.js +139 -0
- package/templates/browser-test-flow.json +56 -0
- package/templates/bug-report.md +43 -0
- package/templates/component-detail.md +42 -0
- package/templates/component.stories.tsx +49 -0
- package/templates/context/constraints.md +83 -0
- package/templates/context/conventions.md +177 -0
- package/templates/context/stack.md +60 -0
- package/templates/correction-report.md +90 -0
- package/templates/feature-proposal.md +35 -0
- package/templates/hybrid/_base.md +254 -0
- package/templates/hybrid/_patterns.md +45 -0
- package/templates/hybrid/create-component.md +127 -0
- package/templates/hybrid/create-file.md +56 -0
- package/templates/hybrid/create-hook.md +145 -0
- package/templates/hybrid/create-service.md +70 -0
- package/templates/hybrid/fix-bug.md +33 -0
- package/templates/hybrid/modify-file.md +55 -0
- package/templates/story.md +68 -0
- package/templates/task.json +56 -0
- package/templates/trace.md +69 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Regression Testing
|
|
5
|
+
*
|
|
6
|
+
* Tests random completed tasks to catch regressions early.
|
|
7
|
+
* Inspired by the "Long Running Agents" pattern where 3 random
|
|
8
|
+
* completed features are tested after each new task completion.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* flow regression # Test 3 random completed tasks
|
|
12
|
+
* flow regression --all # Test all completed tasks
|
|
13
|
+
* flow regression --task ID # Test specific task
|
|
14
|
+
* flow regression --count N # Test N random tasks
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const { execSync } = require('child_process');
|
|
20
|
+
const { getProjectRoot, colors, getConfig } = require('./flow-utils');
|
|
21
|
+
|
|
22
|
+
const PROJECT_ROOT = getProjectRoot();
|
|
23
|
+
const STATE_DIR = path.join(PROJECT_ROOT, '.workflow', 'state');
|
|
24
|
+
const READY_PATH = path.join(STATE_DIR, 'ready.json');
|
|
25
|
+
|
|
26
|
+
function log(color, ...args) {
|
|
27
|
+
console.log(colors[color] + args.join(' ') + colors.reset);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Load ready.json and get completed tasks
|
|
32
|
+
*/
|
|
33
|
+
function getCompletedTasks() {
|
|
34
|
+
if (!fs.existsSync(READY_PATH)) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const ready = JSON.parse(fs.readFileSync(READY_PATH, 'utf8'));
|
|
40
|
+
return ready.recentlyCompleted || [];
|
|
41
|
+
} catch (err) {
|
|
42
|
+
log('yellow', `Warning: Could not parse ready.json: ${err.message}`);
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Find test files associated with a task
|
|
49
|
+
*/
|
|
50
|
+
function findTestFiles(taskId, taskData) {
|
|
51
|
+
const testFiles = [];
|
|
52
|
+
|
|
53
|
+
// Check if task has explicit test files
|
|
54
|
+
if (taskData?.testFiles) {
|
|
55
|
+
testFiles.push(...taskData.testFiles);
|
|
56
|
+
return testFiles;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check if task has associated files
|
|
60
|
+
const files = taskData?.files || [];
|
|
61
|
+
|
|
62
|
+
for (const file of files) {
|
|
63
|
+
// Look for test files with common patterns
|
|
64
|
+
const ext = path.extname(file);
|
|
65
|
+
const base = file.slice(0, -ext.length);
|
|
66
|
+
const testPatterns = [
|
|
67
|
+
`${base}.test${ext}`,
|
|
68
|
+
`${base}.spec${ext}`,
|
|
69
|
+
file.replace(/src\//, 'src/__tests__/'),
|
|
70
|
+
file.replace(/src\/components\//, 'src/components/__tests__/'),
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
for (const pattern of testPatterns) {
|
|
74
|
+
if (fs.existsSync(path.join(PROJECT_ROOT, pattern))) {
|
|
75
|
+
testFiles.push(pattern);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Also look in request-log for files changed
|
|
81
|
+
const logPath = path.join(STATE_DIR, 'request-log.md');
|
|
82
|
+
if (fs.existsSync(logPath)) {
|
|
83
|
+
const logContent = fs.readFileSync(logPath, 'utf8');
|
|
84
|
+
// Escape special regex characters in taskId
|
|
85
|
+
const escapedTaskId = taskId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
86
|
+
const taskPattern = new RegExp(`### R-\\d+.*${escapedTaskId}[\\s\\S]*?Files:\\s*([^\\n]+)`, 'i');
|
|
87
|
+
const match = logContent.match(taskPattern);
|
|
88
|
+
if (match) {
|
|
89
|
+
const mentionedFiles = match[1].split(',').map(f => f.trim());
|
|
90
|
+
for (const file of mentionedFiles) {
|
|
91
|
+
const cleanFile = file.replace(/`/g, '');
|
|
92
|
+
const ext = path.extname(cleanFile);
|
|
93
|
+
if (ext) {
|
|
94
|
+
const base = cleanFile.slice(0, -ext.length);
|
|
95
|
+
const testPatterns = [
|
|
96
|
+
`${base}.test${ext}`,
|
|
97
|
+
`${base}.spec${ext}`,
|
|
98
|
+
];
|
|
99
|
+
for (const pattern of testPatterns) {
|
|
100
|
+
if (fs.existsSync(path.join(PROJECT_ROOT, pattern))) {
|
|
101
|
+
testFiles.push(pattern);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return [...new Set(testFiles)]; // Dedupe
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Run tests for a specific task
|
|
114
|
+
*/
|
|
115
|
+
function runTaskTests(taskId, taskData) {
|
|
116
|
+
const testFiles = findTestFiles(taskId, taskData);
|
|
117
|
+
|
|
118
|
+
if (testFiles.length === 0) {
|
|
119
|
+
return {
|
|
120
|
+
taskId,
|
|
121
|
+
skipped: true,
|
|
122
|
+
reason: 'No test files found'
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
log('white', ` Running tests: ${testFiles.join(', ')}`);
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
// Try to run tests with common test runners
|
|
130
|
+
const testCommand = detectTestRunner(testFiles);
|
|
131
|
+
execSync(testCommand, {
|
|
132
|
+
cwd: PROJECT_ROOT,
|
|
133
|
+
stdio: 'pipe',
|
|
134
|
+
timeout: 60000 // 1 minute timeout per task
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
taskId,
|
|
139
|
+
passed: true,
|
|
140
|
+
testFiles
|
|
141
|
+
};
|
|
142
|
+
} catch (err) {
|
|
143
|
+
return {
|
|
144
|
+
taskId,
|
|
145
|
+
passed: false,
|
|
146
|
+
error: err.message,
|
|
147
|
+
testFiles
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Detect which test runner to use
|
|
154
|
+
*/
|
|
155
|
+
function detectTestRunner(testFiles) {
|
|
156
|
+
const packageJson = path.join(PROJECT_ROOT, 'package.json');
|
|
157
|
+
|
|
158
|
+
if (fs.existsSync(packageJson)) {
|
|
159
|
+
try {
|
|
160
|
+
const pkg = JSON.parse(fs.readFileSync(packageJson, 'utf8'));
|
|
161
|
+
|
|
162
|
+
// Check scripts
|
|
163
|
+
if (pkg.scripts?.test) {
|
|
164
|
+
// If specific files, pass them
|
|
165
|
+
if (testFiles.length > 0) {
|
|
166
|
+
const fileArgs = testFiles.join(' ');
|
|
167
|
+
// Jest-style
|
|
168
|
+
if (pkg.devDependencies?.jest || pkg.dependencies?.jest) {
|
|
169
|
+
return `npx jest ${fileArgs} --passWithNoTests`;
|
|
170
|
+
}
|
|
171
|
+
// Vitest
|
|
172
|
+
if (pkg.devDependencies?.vitest || pkg.dependencies?.vitest) {
|
|
173
|
+
return `npx vitest run ${fileArgs}`;
|
|
174
|
+
}
|
|
175
|
+
// Mocha
|
|
176
|
+
if (pkg.devDependencies?.mocha || pkg.dependencies?.mocha) {
|
|
177
|
+
return `npx mocha ${fileArgs}`;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Fallback to npm test
|
|
181
|
+
return 'npm test -- --passWithNoTests';
|
|
182
|
+
}
|
|
183
|
+
} catch (err) {
|
|
184
|
+
// package.json is malformed, fall through to default
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Default to jest
|
|
189
|
+
return `npx jest ${testFiles.join(' ')} --passWithNoTests`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Pick random tasks from array using Fisher-Yates shuffle
|
|
194
|
+
*/
|
|
195
|
+
function pickRandom(arr, count) {
|
|
196
|
+
const shuffled = [...arr];
|
|
197
|
+
// Fisher-Yates shuffle (unbiased)
|
|
198
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
199
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
200
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
201
|
+
}
|
|
202
|
+
return shuffled.slice(0, Math.min(count, arr.length));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Main regression testing function
|
|
207
|
+
*/
|
|
208
|
+
async function runRegressionTests(options = {}) {
|
|
209
|
+
const config = getConfig();
|
|
210
|
+
const regressionConfig = config.regressionTesting || {};
|
|
211
|
+
|
|
212
|
+
// Check if enabled
|
|
213
|
+
if (!regressionConfig.enabled && !options.force) {
|
|
214
|
+
log('yellow', 'Regression testing is disabled. Enable in config.json or use --force');
|
|
215
|
+
return { skipped: true };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const sampleSize = options.count || regressionConfig.sampleSize || 3;
|
|
219
|
+
const onFailure = options.onFailure || regressionConfig.onFailure || 'warn';
|
|
220
|
+
|
|
221
|
+
// Get completed tasks
|
|
222
|
+
const completed = getCompletedTasks();
|
|
223
|
+
|
|
224
|
+
if (completed.length === 0) {
|
|
225
|
+
log('yellow', 'No completed tasks to test');
|
|
226
|
+
return { skipped: true, reason: 'No completed tasks' };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
log('cyan', '\n=== Regression Testing ===\n');
|
|
230
|
+
|
|
231
|
+
// Determine which tasks to test
|
|
232
|
+
let tasksToTest;
|
|
233
|
+
|
|
234
|
+
if (options.all) {
|
|
235
|
+
tasksToTest = completed;
|
|
236
|
+
log('white', `Testing all ${completed.length} completed tasks...`);
|
|
237
|
+
} else if (options.taskId) {
|
|
238
|
+
const task = completed.find(t => t.id === options.taskId);
|
|
239
|
+
if (!task) {
|
|
240
|
+
log('red', `Task ${options.taskId} not found in completed tasks`);
|
|
241
|
+
return { failed: true, reason: 'Task not found' };
|
|
242
|
+
}
|
|
243
|
+
tasksToTest = [task];
|
|
244
|
+
log('white', `Testing specific task: ${options.taskId}`);
|
|
245
|
+
} else {
|
|
246
|
+
tasksToTest = pickRandom(completed, sampleSize);
|
|
247
|
+
log('white', `Testing ${tasksToTest.length} random tasks (of ${completed.length} completed)...`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Run tests
|
|
251
|
+
const results = [];
|
|
252
|
+
|
|
253
|
+
for (const task of tasksToTest) {
|
|
254
|
+
log('white', `\nš Testing ${task.id}: ${task.title || task.name || 'Untitled'}`);
|
|
255
|
+
|
|
256
|
+
const result = runTaskTests(task.id, task);
|
|
257
|
+
results.push(result);
|
|
258
|
+
|
|
259
|
+
if (result.skipped) {
|
|
260
|
+
log('yellow', ` āļø Skipped: ${result.reason}`);
|
|
261
|
+
} else if (result.passed) {
|
|
262
|
+
log('green', ` ā
Passed`);
|
|
263
|
+
} else {
|
|
264
|
+
log('red', ` ā Failed: ${result.error?.substring(0, 100)}...`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Summary
|
|
269
|
+
const passed = results.filter(r => r.passed).length;
|
|
270
|
+
const failed = results.filter(r => r.passed === false).length;
|
|
271
|
+
const skipped = results.filter(r => r.skipped).length;
|
|
272
|
+
|
|
273
|
+
console.log('\n' + 'ā'.repeat(50));
|
|
274
|
+
log('white', '\nš Regression Test Summary');
|
|
275
|
+
log('green', ` ā
Passed: ${passed}`);
|
|
276
|
+
log('red', ` ā Failed: ${failed}`);
|
|
277
|
+
log('yellow', ` āļø Skipped: ${skipped}`);
|
|
278
|
+
console.log('');
|
|
279
|
+
|
|
280
|
+
// Handle failures
|
|
281
|
+
if (failed > 0) {
|
|
282
|
+
const failedTasks = results.filter(r => r.passed === false);
|
|
283
|
+
|
|
284
|
+
log('red', 'ā ļø Regression detected in:');
|
|
285
|
+
for (const f of failedTasks) {
|
|
286
|
+
log('red', ` - ${f.taskId}`);
|
|
287
|
+
}
|
|
288
|
+
console.log('');
|
|
289
|
+
|
|
290
|
+
if (onFailure === 'block') {
|
|
291
|
+
log('red', 'Blocking task completion due to regression failures');
|
|
292
|
+
process.exit(1);
|
|
293
|
+
} else if (onFailure === 'fix') {
|
|
294
|
+
log('yellow', 'Regressions need to be fixed before continuing');
|
|
295
|
+
// In the future, could integrate with auto-fix
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
success: false,
|
|
300
|
+
tested: tasksToTest.length,
|
|
301
|
+
passed,
|
|
302
|
+
failed,
|
|
303
|
+
skipped,
|
|
304
|
+
failures: failedTasks
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
log('green', 'ā
No regressions detected');
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
success: true,
|
|
312
|
+
tested: tasksToTest.length,
|
|
313
|
+
passed,
|
|
314
|
+
failed,
|
|
315
|
+
skipped
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// CLI handling
|
|
320
|
+
if (require.main === module) {
|
|
321
|
+
const args = process.argv.slice(2);
|
|
322
|
+
|
|
323
|
+
const options = {
|
|
324
|
+
all: args.includes('--all'),
|
|
325
|
+
force: args.includes('--force'),
|
|
326
|
+
taskId: null,
|
|
327
|
+
count: null,
|
|
328
|
+
onFailure: null
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
// Parse --task
|
|
332
|
+
const taskIdx = args.indexOf('--task');
|
|
333
|
+
if (taskIdx !== -1 && args[taskIdx + 1]) {
|
|
334
|
+
options.taskId = args[taskIdx + 1];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Parse --count
|
|
338
|
+
const countIdx = args.indexOf('--count');
|
|
339
|
+
if (countIdx !== -1 && args[countIdx + 1]) {
|
|
340
|
+
options.count = parseInt(args[countIdx + 1], 10);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Parse --on-failure
|
|
344
|
+
const failIdx = args.indexOf('--on-failure');
|
|
345
|
+
if (failIdx !== -1 && args[failIdx + 1]) {
|
|
346
|
+
options.onFailure = args[failIdx + 1];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Help
|
|
350
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
351
|
+
console.log(`
|
|
352
|
+
Wogi Flow - Regression Testing
|
|
353
|
+
|
|
354
|
+
Usage:
|
|
355
|
+
flow regression Test 3 random completed tasks (default)
|
|
356
|
+
flow regression --all Test all completed tasks
|
|
357
|
+
flow regression --task ID Test specific task
|
|
358
|
+
flow regression --count N Test N random tasks
|
|
359
|
+
flow regression --force Run even if disabled in config
|
|
360
|
+
flow regression --on-failure warn|block|fix Override failure behavior
|
|
361
|
+
|
|
362
|
+
Configuration (config.json):
|
|
363
|
+
"regressionTesting": {
|
|
364
|
+
"enabled": true,
|
|
365
|
+
"sampleSize": 3,
|
|
366
|
+
"runOnTaskComplete": true,
|
|
367
|
+
"onFailure": "warn"
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
Exit codes:
|
|
371
|
+
0 - All tests passed or skipped
|
|
372
|
+
1 - Tests failed and onFailure is "block"
|
|
373
|
+
`);
|
|
374
|
+
process.exit(0);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const onFailure = options.onFailure || getConfig().regressionTesting?.onFailure || 'warn';
|
|
378
|
+
|
|
379
|
+
runRegressionTests(options)
|
|
380
|
+
.then(result => {
|
|
381
|
+
if (!result.success && onFailure === 'block') {
|
|
382
|
+
process.exit(1);
|
|
383
|
+
}
|
|
384
|
+
})
|
|
385
|
+
.catch(err => {
|
|
386
|
+
log('red', `Error: ${err.message}`);
|
|
387
|
+
process.exit(1);
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Export for use by other modules
|
|
392
|
+
module.exports = {
|
|
393
|
+
runRegressionTests,
|
|
394
|
+
getCompletedTasks,
|
|
395
|
+
findTestFiles
|
|
396
|
+
};
|