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,644 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - File-Based Verification (Priority 4)
|
|
5
|
+
*
|
|
6
|
+
* Every phase produces artifacts. Never trust chat output.
|
|
7
|
+
* Explicit success criteria with commands and pass/fail conditions.
|
|
8
|
+
*
|
|
9
|
+
* Uses file-based validation approach.
|
|
10
|
+
*
|
|
11
|
+
* Key principle: "Logs are state - next iteration depends on them"
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const { runVerification, saveVerificationResult } = require('./flow-verification');
|
|
15
|
+
* const result = await runVerification(taskId, 'implementation');
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const { execSync, spawn } = require('child_process');
|
|
21
|
+
const { getProjectRoot, getConfig, PATHS, colors } = require('./flow-utils');
|
|
22
|
+
|
|
23
|
+
const PROJECT_ROOT = getProjectRoot();
|
|
24
|
+
const VERIFICATIONS_DIR = path.join(PROJECT_ROOT, '.workflow', 'verifications');
|
|
25
|
+
|
|
26
|
+
// ============================================================
|
|
27
|
+
// Verification Execution
|
|
28
|
+
// ============================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Run verification commands for a task phase
|
|
32
|
+
*
|
|
33
|
+
* @param {string} taskId - Task ID
|
|
34
|
+
* @param {string} phase - Phase name (spec, test, implementation, final)
|
|
35
|
+
* @param {object} options - Verification options
|
|
36
|
+
* @param {Array} options.commands - Commands to run (overrides defaults)
|
|
37
|
+
* @param {boolean} options.failFast - Stop on first failure
|
|
38
|
+
* @param {number} options.timeout - Command timeout in ms
|
|
39
|
+
*/
|
|
40
|
+
async function runVerification(taskId, phase, options = {}) {
|
|
41
|
+
const config = getConfig();
|
|
42
|
+
const commands = options.commands || getDefaultCommands(phase, config);
|
|
43
|
+
const failFast = options.failFast !== false;
|
|
44
|
+
const timeout = options.timeout || 120000;
|
|
45
|
+
|
|
46
|
+
const result = {
|
|
47
|
+
taskId,
|
|
48
|
+
phase,
|
|
49
|
+
timestamp: new Date().toISOString(),
|
|
50
|
+
results: [],
|
|
51
|
+
allPassed: true,
|
|
52
|
+
duration: 0
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const startTime = Date.now();
|
|
56
|
+
|
|
57
|
+
for (const cmd of commands) {
|
|
58
|
+
const cmdResult = await runCommand(cmd, timeout);
|
|
59
|
+
result.results.push(cmdResult);
|
|
60
|
+
|
|
61
|
+
if (!cmdResult.passed) {
|
|
62
|
+
result.allPassed = false;
|
|
63
|
+
if (failFast) {
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
result.duration = Date.now() - startTime;
|
|
70
|
+
|
|
71
|
+
// Save verification result to file
|
|
72
|
+
saveVerificationResult(taskId, phase, result);
|
|
73
|
+
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Run a single command and capture result
|
|
79
|
+
*/
|
|
80
|
+
async function runCommand(cmd, timeout) {
|
|
81
|
+
const result = {
|
|
82
|
+
command: cmd.command,
|
|
83
|
+
description: cmd.description || cmd.command,
|
|
84
|
+
required: cmd.required !== false,
|
|
85
|
+
expectedExitCode: cmd.expectedExitCode || 0,
|
|
86
|
+
startTime: new Date().toISOString(),
|
|
87
|
+
passed: false,
|
|
88
|
+
exitCode: null,
|
|
89
|
+
stdout: '',
|
|
90
|
+
stderr: '',
|
|
91
|
+
duration: 0,
|
|
92
|
+
error: null
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const startTime = Date.now();
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const output = execSync(cmd.command, {
|
|
99
|
+
cwd: PROJECT_ROOT,
|
|
100
|
+
encoding: 'utf-8',
|
|
101
|
+
timeout,
|
|
102
|
+
stdio: 'pipe',
|
|
103
|
+
maxBuffer: 10 * 1024 * 1024 // 10MB
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
result.stdout = output;
|
|
107
|
+
result.exitCode = 0;
|
|
108
|
+
result.passed = result.exitCode === result.expectedExitCode;
|
|
109
|
+
} catch (err) {
|
|
110
|
+
result.exitCode = err.status || 1;
|
|
111
|
+
result.stdout = err.stdout || '';
|
|
112
|
+
result.stderr = err.stderr || '';
|
|
113
|
+
result.error = err.message;
|
|
114
|
+
result.passed = result.exitCode === result.expectedExitCode;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
result.duration = Date.now() - startTime;
|
|
118
|
+
result.endTime = new Date().toISOString();
|
|
119
|
+
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get default verification commands for a phase
|
|
125
|
+
*/
|
|
126
|
+
function getDefaultCommands(phase, config) {
|
|
127
|
+
const commands = [];
|
|
128
|
+
|
|
129
|
+
switch (phase) {
|
|
130
|
+
case 'spec':
|
|
131
|
+
// Spec phase - just validate spec exists
|
|
132
|
+
commands.push({
|
|
133
|
+
command: 'echo "Spec validation"',
|
|
134
|
+
description: 'Validate spec exists',
|
|
135
|
+
required: true
|
|
136
|
+
});
|
|
137
|
+
break;
|
|
138
|
+
|
|
139
|
+
case 'test':
|
|
140
|
+
// Test-first phase - run tests (expecting some to fail initially)
|
|
141
|
+
commands.push({
|
|
142
|
+
command: 'npm test -- --passWithNoTests 2>/dev/null || true',
|
|
143
|
+
description: 'Run tests (initial)',
|
|
144
|
+
required: false
|
|
145
|
+
});
|
|
146
|
+
break;
|
|
147
|
+
|
|
148
|
+
case 'implementation':
|
|
149
|
+
// After implementation - lint and typecheck
|
|
150
|
+
commands.push({
|
|
151
|
+
command: 'npm run lint 2>/dev/null || npx eslint . --ext .ts,.tsx,.js,.jsx --max-warnings 0 2>/dev/null || echo "lint skipped"',
|
|
152
|
+
description: 'Run linter',
|
|
153
|
+
required: false
|
|
154
|
+
});
|
|
155
|
+
commands.push({
|
|
156
|
+
command: 'npm run typecheck 2>/dev/null || npx tsc --noEmit 2>/dev/null || echo "typecheck skipped"',
|
|
157
|
+
description: 'Run type checker',
|
|
158
|
+
required: false
|
|
159
|
+
});
|
|
160
|
+
break;
|
|
161
|
+
|
|
162
|
+
case 'final':
|
|
163
|
+
// Final verification - all gates must pass
|
|
164
|
+
if (config.validation?.beforeCommit?.commands) {
|
|
165
|
+
for (const cmd of config.validation.beforeCommit.commands) {
|
|
166
|
+
commands.push({
|
|
167
|
+
command: cmd,
|
|
168
|
+
description: cmd,
|
|
169
|
+
required: true
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
commands.push({
|
|
174
|
+
command: 'npm run lint 2>/dev/null || echo "lint not configured"',
|
|
175
|
+
description: 'Run linter',
|
|
176
|
+
required: false
|
|
177
|
+
});
|
|
178
|
+
commands.push({
|
|
179
|
+
command: 'npm run typecheck 2>/dev/null || echo "typecheck not configured"',
|
|
180
|
+
description: 'Run type checker',
|
|
181
|
+
required: false
|
|
182
|
+
});
|
|
183
|
+
commands.push({
|
|
184
|
+
command: 'npm test 2>/dev/null || echo "tests not configured"',
|
|
185
|
+
description: 'Run tests',
|
|
186
|
+
required: false
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
break;
|
|
190
|
+
|
|
191
|
+
default:
|
|
192
|
+
// Generic verification
|
|
193
|
+
commands.push({
|
|
194
|
+
command: 'npm run lint 2>/dev/null || echo "lint not configured"',
|
|
195
|
+
description: 'Run linter',
|
|
196
|
+
required: false
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return commands;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ============================================================
|
|
204
|
+
// Verification Artifacts
|
|
205
|
+
// ============================================================
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Save verification result to file
|
|
209
|
+
*/
|
|
210
|
+
function saveVerificationResult(taskId, phase, result) {
|
|
211
|
+
// Ensure directory exists
|
|
212
|
+
if (!fs.existsSync(VERIFICATIONS_DIR)) {
|
|
213
|
+
fs.mkdirSync(VERIFICATIONS_DIR, { recursive: true });
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Save JSON result
|
|
217
|
+
const filename = `${taskId}-${phase}.json`;
|
|
218
|
+
const filePath = path.join(VERIFICATIONS_DIR, filename);
|
|
219
|
+
fs.writeFileSync(filePath, JSON.stringify(result, null, 2), 'utf-8');
|
|
220
|
+
|
|
221
|
+
// Also append to verification log
|
|
222
|
+
appendToVerificationLog(taskId, phase, result);
|
|
223
|
+
|
|
224
|
+
return filePath;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Load verification result for a task/phase
|
|
229
|
+
*/
|
|
230
|
+
function loadVerificationResult(taskId, phase) {
|
|
231
|
+
const filename = `${taskId}-${phase}.json`;
|
|
232
|
+
const filePath = path.join(VERIFICATIONS_DIR, filename);
|
|
233
|
+
|
|
234
|
+
if (!fs.existsSync(filePath)) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
240
|
+
} catch {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Get all verification results for a task
|
|
247
|
+
*/
|
|
248
|
+
function getAllVerificationResults(taskId) {
|
|
249
|
+
if (!fs.existsSync(VERIFICATIONS_DIR)) {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const files = fs.readdirSync(VERIFICATIONS_DIR)
|
|
254
|
+
.filter(f => f.startsWith(`${taskId}-`) && f.endsWith('.json'));
|
|
255
|
+
|
|
256
|
+
return files.map(f => {
|
|
257
|
+
try {
|
|
258
|
+
return JSON.parse(fs.readFileSync(path.join(VERIFICATIONS_DIR, f), 'utf-8'));
|
|
259
|
+
} catch {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
}).filter(Boolean);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Append to verification log (human-readable)
|
|
267
|
+
*/
|
|
268
|
+
function appendToVerificationLog(taskId, phase, result) {
|
|
269
|
+
const logPath = path.join(VERIFICATIONS_DIR, 'verification-log.md');
|
|
270
|
+
|
|
271
|
+
let content = '';
|
|
272
|
+
if (fs.existsSync(logPath)) {
|
|
273
|
+
content = fs.readFileSync(logPath, 'utf-8');
|
|
274
|
+
} else {
|
|
275
|
+
content = '# Verification Log\n\nFile-based verification results.\n\n';
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const status = result.allPassed ? '✓ PASSED' : '✗ FAILED';
|
|
279
|
+
const entry = `
|
|
280
|
+
## ${taskId} - ${phase} [${status}]
|
|
281
|
+
**Time:** ${result.timestamp}
|
|
282
|
+
**Duration:** ${result.duration}ms
|
|
283
|
+
|
|
284
|
+
| Command | Status | Exit | Duration |
|
|
285
|
+
|---------|--------|------|----------|
|
|
286
|
+
${result.results.map(r =>
|
|
287
|
+
`| \`${r.command.slice(0, 40)}${r.command.length > 40 ? '...' : ''}\` | ${r.passed ? '✓' : '✗'} | ${r.exitCode} | ${r.duration}ms |`
|
|
288
|
+
).join('\n')}
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
`;
|
|
292
|
+
|
|
293
|
+
content += entry;
|
|
294
|
+
fs.writeFileSync(logPath, content, 'utf-8');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// ============================================================
|
|
298
|
+
// Structured Execution Loop (Priority 3)
|
|
299
|
+
// ============================================================
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Execute structured loop: Spec → Test → Implement → Verify
|
|
303
|
+
* This is the core structured execution model
|
|
304
|
+
*
|
|
305
|
+
* @param {string} taskId - Task ID
|
|
306
|
+
* @param {object} callbacks - Phase callbacks
|
|
307
|
+
*/
|
|
308
|
+
async function executeStructuredLoop(taskId, callbacks = {}) {
|
|
309
|
+
const phases = ['spec', 'test', 'implementation', 'final'];
|
|
310
|
+
const loopState = {
|
|
311
|
+
taskId,
|
|
312
|
+
startTime: new Date().toISOString(),
|
|
313
|
+
phases: {},
|
|
314
|
+
currentPhase: null,
|
|
315
|
+
completed: false,
|
|
316
|
+
success: false
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
for (const phase of phases) {
|
|
320
|
+
loopState.currentPhase = phase;
|
|
321
|
+
loopState.phases[phase] = {
|
|
322
|
+
startTime: new Date().toISOString(),
|
|
323
|
+
status: 'running'
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// Call phase callback if provided
|
|
327
|
+
if (callbacks[phase]) {
|
|
328
|
+
try {
|
|
329
|
+
await callbacks[phase](loopState);
|
|
330
|
+
} catch (err) {
|
|
331
|
+
loopState.phases[phase].status = 'failed';
|
|
332
|
+
loopState.phases[phase].error = err.message;
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Run verification for this phase
|
|
338
|
+
const verification = await runVerification(taskId, phase);
|
|
339
|
+
|
|
340
|
+
loopState.phases[phase].verification = {
|
|
341
|
+
passed: verification.allPassed,
|
|
342
|
+
duration: verification.duration,
|
|
343
|
+
failedCommands: verification.results
|
|
344
|
+
.filter(r => !r.passed)
|
|
345
|
+
.map(r => r.command)
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
if (!verification.allPassed && phase === 'final') {
|
|
349
|
+
loopState.phases[phase].status = 'failed';
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
loopState.phases[phase].status = 'completed';
|
|
354
|
+
loopState.phases[phase].endTime = new Date().toISOString();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
loopState.completed = true;
|
|
358
|
+
loopState.success = Object.values(loopState.phases)
|
|
359
|
+
.every(p => p.status === 'completed');
|
|
360
|
+
loopState.endTime = new Date().toISOString();
|
|
361
|
+
|
|
362
|
+
// Save loop state
|
|
363
|
+
saveLoopState(taskId, loopState);
|
|
364
|
+
|
|
365
|
+
return loopState;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Save loop state to file
|
|
370
|
+
*/
|
|
371
|
+
function saveLoopState(taskId, state) {
|
|
372
|
+
const loopDir = path.join(VERIFICATIONS_DIR, 'loops');
|
|
373
|
+
if (!fs.existsSync(loopDir)) {
|
|
374
|
+
fs.mkdirSync(loopDir, { recursive: true });
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const filePath = path.join(loopDir, `${taskId}-loop.json`);
|
|
378
|
+
fs.writeFileSync(filePath, JSON.stringify(state, null, 2), 'utf-8');
|
|
379
|
+
|
|
380
|
+
return filePath;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Load loop state
|
|
385
|
+
*/
|
|
386
|
+
function loadLoopState(taskId) {
|
|
387
|
+
const filePath = path.join(VERIFICATIONS_DIR, 'loops', `${taskId}-loop.json`);
|
|
388
|
+
|
|
389
|
+
if (!fs.existsSync(filePath)) {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
try {
|
|
394
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
395
|
+
} catch {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ============================================================
|
|
401
|
+
// Self-Reflection Checkpoints (Priority 6)
|
|
402
|
+
// ============================================================
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Create a reflection checkpoint
|
|
406
|
+
* Pauses execution and records reflection data
|
|
407
|
+
*
|
|
408
|
+
* @param {string} taskId - Task ID
|
|
409
|
+
* @param {string} checkpoint - Checkpoint name
|
|
410
|
+
* @param {object} context - Context for reflection
|
|
411
|
+
*/
|
|
412
|
+
function createReflectionCheckpoint(taskId, checkpoint, context = {}) {
|
|
413
|
+
const reflectionsDir = path.join(VERIFICATIONS_DIR, 'reflections');
|
|
414
|
+
if (!fs.existsSync(reflectionsDir)) {
|
|
415
|
+
fs.mkdirSync(reflectionsDir, { recursive: true });
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const reflection = {
|
|
419
|
+
taskId,
|
|
420
|
+
checkpoint,
|
|
421
|
+
timestamp: new Date().toISOString(),
|
|
422
|
+
context,
|
|
423
|
+
questions: getReflectionQuestions(checkpoint),
|
|
424
|
+
answers: {}
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
const filePath = path.join(reflectionsDir, `${taskId}-${checkpoint}.json`);
|
|
428
|
+
fs.writeFileSync(filePath, JSON.stringify(reflection, null, 2), 'utf-8');
|
|
429
|
+
|
|
430
|
+
return reflection;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Get reflection questions for a checkpoint
|
|
435
|
+
*/
|
|
436
|
+
function getReflectionQuestions(checkpoint) {
|
|
437
|
+
const questions = {
|
|
438
|
+
'post-spec': [
|
|
439
|
+
'Does this spec fully address the requirements?',
|
|
440
|
+
'Are there any edge cases not covered?',
|
|
441
|
+
'Is the scope clear and achievable?'
|
|
442
|
+
],
|
|
443
|
+
'post-implementation': [
|
|
444
|
+
'Have I introduced any bugs or regressions?',
|
|
445
|
+
'Does the code follow project patterns?',
|
|
446
|
+
'Is there any code that could be simplified?'
|
|
447
|
+
],
|
|
448
|
+
'pre-completion': [
|
|
449
|
+
'Does this match what the user asked for?',
|
|
450
|
+
'Have all acceptance criteria been met?',
|
|
451
|
+
'Are there any loose ends to address?'
|
|
452
|
+
],
|
|
453
|
+
'post-failure': [
|
|
454
|
+
'What caused this failure?',
|
|
455
|
+
'What can be learned from this?',
|
|
456
|
+
'How can similar failures be prevented?'
|
|
457
|
+
]
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
return questions[checkpoint] || [
|
|
461
|
+
'Is the current approach correct?',
|
|
462
|
+
'Are there any concerns to address?'
|
|
463
|
+
];
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Record reflection answer
|
|
468
|
+
*/
|
|
469
|
+
function recordReflectionAnswer(taskId, checkpoint, questionIndex, answer) {
|
|
470
|
+
const filePath = path.join(VERIFICATIONS_DIR, 'reflections', `${taskId}-${checkpoint}.json`);
|
|
471
|
+
|
|
472
|
+
if (!fs.existsSync(filePath)) {
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
try {
|
|
477
|
+
const reflection = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
478
|
+
reflection.answers[questionIndex] = {
|
|
479
|
+
answer,
|
|
480
|
+
timestamp: new Date().toISOString()
|
|
481
|
+
};
|
|
482
|
+
fs.writeFileSync(filePath, JSON.stringify(reflection, null, 2), 'utf-8');
|
|
483
|
+
return reflection;
|
|
484
|
+
} catch {
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Get all reflections for a task
|
|
491
|
+
*/
|
|
492
|
+
function getTaskReflections(taskId) {
|
|
493
|
+
const reflectionsDir = path.join(VERIFICATIONS_DIR, 'reflections');
|
|
494
|
+
|
|
495
|
+
if (!fs.existsSync(reflectionsDir)) {
|
|
496
|
+
return [];
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const files = fs.readdirSync(reflectionsDir)
|
|
500
|
+
.filter(f => f.startsWith(`${taskId}-`) && f.endsWith('.json'));
|
|
501
|
+
|
|
502
|
+
return files.map(f => {
|
|
503
|
+
try {
|
|
504
|
+
return JSON.parse(fs.readFileSync(path.join(reflectionsDir, f), 'utf-8'));
|
|
505
|
+
} catch {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
}).filter(Boolean);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// ============================================================
|
|
512
|
+
// Formatting
|
|
513
|
+
// ============================================================
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Format verification result for display
|
|
517
|
+
*/
|
|
518
|
+
function formatVerificationResult(result) {
|
|
519
|
+
let output = '';
|
|
520
|
+
|
|
521
|
+
const status = result.allPassed
|
|
522
|
+
? `${colors.green}✓ VERIFICATION PASSED${colors.reset}`
|
|
523
|
+
: `${colors.red}✗ VERIFICATION FAILED${colors.reset}`;
|
|
524
|
+
|
|
525
|
+
output += `\n${status}\n`;
|
|
526
|
+
output += `Phase: ${result.phase} | Duration: ${result.duration}ms\n\n`;
|
|
527
|
+
|
|
528
|
+
for (const cmd of result.results) {
|
|
529
|
+
const icon = cmd.passed ? `${colors.green}✓${colors.reset}` : `${colors.red}✗${colors.reset}`;
|
|
530
|
+
output += `${icon} ${cmd.description}\n`;
|
|
531
|
+
if (!cmd.passed && cmd.stderr) {
|
|
532
|
+
output += ` ${colors.dim}${cmd.stderr.slice(0, 200)}${colors.reset}\n`;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return output;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// ============================================================
|
|
540
|
+
// CLI
|
|
541
|
+
// ============================================================
|
|
542
|
+
|
|
543
|
+
function showHelp() {
|
|
544
|
+
console.log(`
|
|
545
|
+
Wogi Flow - File-Based Verification
|
|
546
|
+
|
|
547
|
+
Every phase produces artifacts. Logs are state.
|
|
548
|
+
|
|
549
|
+
Usage:
|
|
550
|
+
flow verify <task-id> [phase]
|
|
551
|
+
flow verify <task-id> --all
|
|
552
|
+
flow verify <task-id> --loop
|
|
553
|
+
|
|
554
|
+
Phases:
|
|
555
|
+
spec Verify spec exists and is valid
|
|
556
|
+
test Run tests (initial)
|
|
557
|
+
implementation Run lint and typecheck
|
|
558
|
+
final Run all quality gates
|
|
559
|
+
|
|
560
|
+
Options:
|
|
561
|
+
--all Run all phase verifications
|
|
562
|
+
--loop Execute full structured loop
|
|
563
|
+
--json Output as JSON
|
|
564
|
+
--help, -h Show this help
|
|
565
|
+
|
|
566
|
+
Examples:
|
|
567
|
+
flow verify wf-abc123 implementation
|
|
568
|
+
flow verify wf-abc123 final
|
|
569
|
+
flow verify wf-abc123 --loop
|
|
570
|
+
`);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
async function main() {
|
|
574
|
+
const args = process.argv.slice(2);
|
|
575
|
+
|
|
576
|
+
if (args.includes('--help') || args.includes('-h') || args.length === 0) {
|
|
577
|
+
showHelp();
|
|
578
|
+
process.exit(0);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const taskId = args[0];
|
|
582
|
+
const phase = args[1] || 'implementation';
|
|
583
|
+
const jsonOutput = args.includes('--json');
|
|
584
|
+
const runAll = args.includes('--all');
|
|
585
|
+
const runLoop = args.includes('--loop');
|
|
586
|
+
|
|
587
|
+
if (runLoop) {
|
|
588
|
+
console.log(`${colors.cyan}Executing structured loop for ${taskId}...${colors.reset}\n`);
|
|
589
|
+
const loopState = await executeStructuredLoop(taskId);
|
|
590
|
+
|
|
591
|
+
if (jsonOutput) {
|
|
592
|
+
console.log(JSON.stringify(loopState, null, 2));
|
|
593
|
+
} else {
|
|
594
|
+
for (const [phaseName, phaseData] of Object.entries(loopState.phases)) {
|
|
595
|
+
const icon = phaseData.status === 'completed' ? '✓' : '✗';
|
|
596
|
+
console.log(`${icon} ${phaseName}: ${phaseData.status}`);
|
|
597
|
+
}
|
|
598
|
+
console.log(`\n${loopState.success ? 'Loop completed successfully' : 'Loop failed'}`);
|
|
599
|
+
}
|
|
600
|
+
} else if (runAll) {
|
|
601
|
+
const phases = ['spec', 'test', 'implementation', 'final'];
|
|
602
|
+
for (const p of phases) {
|
|
603
|
+
console.log(`\n${colors.cyan}Running ${p} verification...${colors.reset}`);
|
|
604
|
+
const result = await runVerification(taskId, p);
|
|
605
|
+
console.log(formatVerificationResult(result));
|
|
606
|
+
}
|
|
607
|
+
} else {
|
|
608
|
+
const result = await runVerification(taskId, phase);
|
|
609
|
+
|
|
610
|
+
if (jsonOutput) {
|
|
611
|
+
console.log(JSON.stringify(result, null, 2));
|
|
612
|
+
} else {
|
|
613
|
+
console.log(formatVerificationResult(result));
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// ============================================================
|
|
619
|
+
// Exports
|
|
620
|
+
// ============================================================
|
|
621
|
+
|
|
622
|
+
module.exports = {
|
|
623
|
+
runVerification,
|
|
624
|
+
runCommand,
|
|
625
|
+
getDefaultCommands,
|
|
626
|
+
saveVerificationResult,
|
|
627
|
+
loadVerificationResult,
|
|
628
|
+
getAllVerificationResults,
|
|
629
|
+
executeStructuredLoop,
|
|
630
|
+
saveLoopState,
|
|
631
|
+
loadLoopState,
|
|
632
|
+
createReflectionCheckpoint,
|
|
633
|
+
recordReflectionAnswer,
|
|
634
|
+
getTaskReflections,
|
|
635
|
+
getReflectionQuestions,
|
|
636
|
+
formatVerificationResult
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
if (require.main === module) {
|
|
640
|
+
main().catch(err => {
|
|
641
|
+
console.error(`Error: ${err.message}`);
|
|
642
|
+
process.exit(1);
|
|
643
|
+
});
|
|
644
|
+
}
|