@davidorex/pi-workflows 0.1.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/CHANGELOG.md +28 -0
- package/README.md +169 -0
- package/agents/audit-fixer.agent.yaml +18 -0
- package/agents/code-explorer.agent.yaml +11 -0
- package/agents/decomposer.agent.yaml +51 -0
- package/agents/investigator.agent.yaml +49 -0
- package/agents/pattern-analyzer.agent.yaml +21 -0
- package/agents/phase-author.agent.yaml +16 -0
- package/agents/plan-decomposer.agent.yaml +12 -0
- package/agents/quality-analyzer.agent.yaml +21 -0
- package/agents/researcher.agent.yaml +38 -0
- package/agents/spec-implementer.agent.yaml +12 -0
- package/agents/structure-analyzer.agent.yaml +21 -0
- package/agents/synthesizer.agent.yaml +23 -0
- package/agents/verifier.agent.yaml +12 -0
- package/package.json +40 -0
- package/schemas/decomposition-specs.schema.json +50 -0
- package/schemas/execution-results.schema.json +50 -0
- package/schemas/investigation-findings.schema.json +45 -0
- package/schemas/pattern-analysis.schema.json +43 -0
- package/schemas/plan-breakdown.schema.json +22 -0
- package/schemas/quality-analysis.schema.json +35 -0
- package/schemas/research-findings.schema.json +44 -0
- package/schemas/structure-analysis.schema.json +42 -0
- package/schemas/synthesis.schema.json +33 -0
- package/schemas/verifier-output.schema.json +96 -0
- package/src/agent-spec.test.ts +289 -0
- package/src/agent-spec.ts +122 -0
- package/src/block-validation.test.ts +350 -0
- package/src/checkpoint.test.ts +237 -0
- package/src/checkpoint.ts +139 -0
- package/src/completion.test.ts +143 -0
- package/src/completion.ts +68 -0
- package/src/dag.test.ts +350 -0
- package/src/dag.ts +219 -0
- package/src/dispatch.test.ts +473 -0
- package/src/dispatch.ts +353 -0
- package/src/expression.test.ts +353 -0
- package/src/expression.ts +332 -0
- package/src/format.test.ts +80 -0
- package/src/format.ts +41 -0
- package/src/graduated-failure.test.ts +556 -0
- package/src/index.test.ts +114 -0
- package/src/index.ts +488 -0
- package/src/loop.test.ts +549 -0
- package/src/output.test.ts +213 -0
- package/src/output.ts +70 -0
- package/src/parallel-integration.test.ts +175 -0
- package/src/resume.test.ts +192 -0
- package/src/state.test.ts +192 -0
- package/src/state.ts +253 -0
- package/src/step-agent.test.ts +327 -0
- package/src/step-agent.ts +178 -0
- package/src/step-command.test.ts +330 -0
- package/src/step-command.ts +132 -0
- package/src/step-foreach.test.ts +647 -0
- package/src/step-foreach.ts +148 -0
- package/src/step-gate.test.ts +185 -0
- package/src/step-gate.ts +116 -0
- package/src/step-loop.test.ts +626 -0
- package/src/step-loop.ts +323 -0
- package/src/step-parallel.test.ts +475 -0
- package/src/step-parallel.ts +168 -0
- package/src/step-pause.test.ts +30 -0
- package/src/step-pause.ts +27 -0
- package/src/step-shared.test.ts +355 -0
- package/src/step-shared.ts +157 -0
- package/src/step-transform.test.ts +155 -0
- package/src/step-transform.ts +47 -0
- package/src/template-integration.test.ts +72 -0
- package/src/template.test.ts +162 -0
- package/src/template.ts +92 -0
- package/src/test-helpers.ts +51 -0
- package/src/tui.test.ts +355 -0
- package/src/tui.ts +182 -0
- package/src/types.ts +195 -0
- package/src/verifier-schema.test.ts +226 -0
- package/src/workflow-discovery.test.ts +105 -0
- package/src/workflow-discovery.ts +113 -0
- package/src/workflow-executor.test.ts +1530 -0
- package/src/workflow-executor.ts +810 -0
- package/src/workflow-sdk.test.ts +238 -0
- package/src/workflow-sdk.ts +262 -0
- package/src/workflow-spec.test.ts +576 -0
- package/src/workflow-spec.ts +471 -0
- package/templates/analyzers/base-analyzer.md +9 -0
- package/templates/analyzers/macros.md +1 -0
- package/templates/analyzers/patterns-task.md +11 -0
- package/templates/analyzers/patterns.md +15 -0
- package/templates/analyzers/quality-task.md +11 -0
- package/templates/analyzers/quality.md +15 -0
- package/templates/analyzers/structure-task.md +17 -0
- package/templates/analyzers/structure.md +15 -0
- package/templates/audit-fixer/task.md +67 -0
- package/templates/decomposer/task.md +56 -0
- package/templates/explorer/system.md +3 -0
- package/templates/explorer/task.md +9 -0
- package/templates/investigator/task.md +30 -0
- package/templates/phase-author/task.md +74 -0
- package/templates/plan-decomposer/task.md +46 -0
- package/templates/researcher/task.md +26 -0
- package/templates/spec-implementer/task.md +53 -0
- package/templates/synthesizer/system.md +3 -0
- package/templates/synthesizer/task.md +38 -0
- package/templates/verifier/task.md +57 -0
- package/workflows/create-phase.workflow.yaml +67 -0
- package/workflows/do-gap.workflow.yaml +153 -0
- package/workflows/fix-audit.workflow.yaml +217 -0
- package/workflows/gap-to-phase.workflow.yaml +98 -0
- package/workflows/parallel-analysis.workflow.yaml +62 -0
- package/workflows/parallel-explicit.workflow.yaml +42 -0
- package/workflows/pausable-analysis.workflow.yaml +44 -0
- package/workflows/resumable-analysis.workflow.yaml +42 -0
- package/workflows/self-implement.workflow.yaml +63 -0
- package/workflows/typed-analysis.workflow.yaml +63 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
name: fix-audit
|
|
2
|
+
description: Fix audit findings grouped by related clusters
|
|
3
|
+
version: "1"
|
|
4
|
+
|
|
5
|
+
steps:
|
|
6
|
+
load:
|
|
7
|
+
command: |
|
|
8
|
+
node --experimental-strip-types -e "
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
const auditsDir = '.project/audits';
|
|
11
|
+
const files = fs.readdirSync(auditsDir).filter(f => f.endsWith('.json')).sort();
|
|
12
|
+
const latestFile = files[files.length - 1];
|
|
13
|
+
const auditPath = auditsDir + '/' + latestFile;
|
|
14
|
+
const audit = JSON.parse(fs.readFileSync(auditPath, 'utf8'));
|
|
15
|
+
let conformance_reference = null;
|
|
16
|
+
try { conformance_reference = JSON.parse(fs.readFileSync('.project/conformance-reference.json', 'utf8')); } catch {}
|
|
17
|
+
console.log(JSON.stringify({ audit, conformance_reference, audit_path: auditPath }));
|
|
18
|
+
"
|
|
19
|
+
output:
|
|
20
|
+
format: json
|
|
21
|
+
|
|
22
|
+
cluster:
|
|
23
|
+
command: |
|
|
24
|
+
node --experimental-strip-types -e "
|
|
25
|
+
import fs from 'fs';
|
|
26
|
+
import { execSync } from 'child_process';
|
|
27
|
+
const audit = JSON.parse(fs.readFileSync('.project/audits/' + fs.readdirSync('.project/audits').filter(f => f.endsWith('.json')).sort().pop(), 'utf8'));
|
|
28
|
+
|
|
29
|
+
// Filter to findings that still need fixing (code is source of truth)
|
|
30
|
+
const findings = audit.findings.filter(f => {
|
|
31
|
+
if (!f.fix) return false;
|
|
32
|
+
if (f.fix.verify_method === 'grep' && f.fix.verify_pattern) {
|
|
33
|
+
try {
|
|
34
|
+
execSync('grep -rn ' + JSON.stringify(f.fix.verify_pattern) + ' ' + f.locations.map(l => l.file).join(' '), { stdio: 'pipe' });
|
|
35
|
+
return true; // pattern still matches — needs fixing
|
|
36
|
+
} catch { return false; } // pattern gone — already fixed
|
|
37
|
+
}
|
|
38
|
+
// inspect findings: check resolution field, skip if already resolved
|
|
39
|
+
if (f.resolution && f.resolution.status === 'passed') return false;
|
|
40
|
+
return true; // no automated check — include it
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (findings.length === 0) {
|
|
44
|
+
console.log(JSON.stringify({ tasks: [], message: 'All findings already resolved' }));
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const visited = new Set();
|
|
49
|
+
const tasks = [];
|
|
50
|
+
for (const f of findings) {
|
|
51
|
+
if (visited.has(f.id)) continue;
|
|
52
|
+
const cluster = [f];
|
|
53
|
+
visited.add(f.id);
|
|
54
|
+
if (f.related) {
|
|
55
|
+
for (const rid of f.related) {
|
|
56
|
+
if (!visited.has(rid)) {
|
|
57
|
+
const r = findings.find(x => x.id === rid);
|
|
58
|
+
if (r) { cluster.push(r); visited.add(rid); }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const files = [...new Set(cluster.flatMap(c => c.locations).map(l => l.file))];
|
|
63
|
+
tasks.push({
|
|
64
|
+
name: cluster[0].id,
|
|
65
|
+
findings: cluster,
|
|
66
|
+
files,
|
|
67
|
+
principle: cluster[0].principle,
|
|
68
|
+
category: cluster[0].category,
|
|
69
|
+
severity: cluster.some(c => c.severity === 'error') ? 'error' : cluster[0].severity,
|
|
70
|
+
acceptance_criteria: cluster.map(c => {
|
|
71
|
+
if (c.fix && c.fix.verify_method === 'grep' && c.fix.verify_pattern) return 'grep pattern should not match: ' + c.fix.verify_pattern + ' in ' + c.locations.map(l=>l.file).join(', ');
|
|
72
|
+
return 'Fix: ' + (c.fix?.suggestion || c.description);
|
|
73
|
+
})
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
console.log(JSON.stringify({ tasks }));
|
|
77
|
+
"
|
|
78
|
+
output:
|
|
79
|
+
format: json
|
|
80
|
+
|
|
81
|
+
implement:
|
|
82
|
+
forEach: "${{ steps.cluster.output.tasks }}"
|
|
83
|
+
as: task
|
|
84
|
+
agent: audit-fixer
|
|
85
|
+
input:
|
|
86
|
+
task: "${{ task }}"
|
|
87
|
+
conformance_reference: "${{ steps.load.output.conformance_reference }}"
|
|
88
|
+
output:
|
|
89
|
+
format: json
|
|
90
|
+
schema: schemas/execution-results.schema.json
|
|
91
|
+
path: .project/fix-audit-results.json
|
|
92
|
+
|
|
93
|
+
route-results:
|
|
94
|
+
command: |
|
|
95
|
+
node --experimental-strip-types -e "
|
|
96
|
+
import fs from 'fs';
|
|
97
|
+
const raw = JSON.parse(fs.readFileSync('.project/fix-audit-results.json', 'utf8'));
|
|
98
|
+
const items = Array.isArray(raw) ? raw : [raw];
|
|
99
|
+
|
|
100
|
+
const decisionsPath = '.project/decisions.json';
|
|
101
|
+
if (fs.existsSync(decisionsPath)) {
|
|
102
|
+
const d = JSON.parse(fs.readFileSync(decisionsPath, 'utf8'));
|
|
103
|
+
const existingIds = new Set(d.decisions.map(x => x.id));
|
|
104
|
+
for (const r of items) {
|
|
105
|
+
for (const dec of r.decisions || []) {
|
|
106
|
+
if (!existingIds.has(dec.id)) { d.decisions.push(dec); existingIds.add(dec.id); }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
fs.writeFileSync(decisionsPath, JSON.stringify(d, null, 2) + '\n');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const gapsPath = '.project/gaps.json';
|
|
113
|
+
if (fs.existsSync(gapsPath)) {
|
|
114
|
+
const g = JSON.parse(fs.readFileSync(gapsPath, 'utf8'));
|
|
115
|
+
const existingIds = new Set(g.gaps.map(x => x.id));
|
|
116
|
+
for (const r of items) {
|
|
117
|
+
for (const issue of r.issues || []) {
|
|
118
|
+
const id = 'issue-' + issue.description.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 50);
|
|
119
|
+
if (!existingIds.has(id)) {
|
|
120
|
+
g.gaps.push({ id, description: issue.description, status: 'open', category: 'issue', priority: issue.severity === 'critical' ? 'high' : issue.severity === 'error' ? 'medium' : 'low', source: 'agent' });
|
|
121
|
+
existingIds.add(id);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
fs.writeFileSync(gapsPath, JSON.stringify(g, null, 2) + '\n');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const invPath = '.project/inventory.json';
|
|
129
|
+
if (fs.existsSync(invPath)) {
|
|
130
|
+
const inv = JSON.parse(fs.readFileSync(invPath, 'utf8'));
|
|
131
|
+
let maxTests = inv.test_count || 0;
|
|
132
|
+
for (const r of items) { if (r.test_count && r.test_count > maxTests) maxTests = r.test_count; }
|
|
133
|
+
if (maxTests > inv.test_count) { inv.test_count = maxTests; fs.writeFileSync(invPath, JSON.stringify(inv, null, 2) + '\n'); }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const statePath = '.project/state.json';
|
|
137
|
+
if (fs.existsSync(statePath)) {
|
|
138
|
+
const s = JSON.parse(fs.readFileSync(statePath, 'utf8'));
|
|
139
|
+
const lastCommit = items.filter(r => r.commit_hash).pop()?.commit_hash;
|
|
140
|
+
if (lastCommit) s.last_commit = lastCommit;
|
|
141
|
+
const maxTests = Math.max(...items.map(r => r.test_count || 0));
|
|
142
|
+
if (maxTests > 0) s.test_count = maxTests;
|
|
143
|
+
s.session.last_action = 'fix-audit workflow: ' + items.length + ' tasks completed';
|
|
144
|
+
fs.writeFileSync(statePath, JSON.stringify(s, null, 2) + '\n');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.log(JSON.stringify({ routed: items.length, decisions_added: items.reduce((s,r) => s + (r.decisions?.length || 0), 0), issues_added: items.reduce((s,r) => s + (r.issues?.length || 0), 0) }));
|
|
148
|
+
"
|
|
149
|
+
output:
|
|
150
|
+
format: json
|
|
151
|
+
|
|
152
|
+
verify:
|
|
153
|
+
command: |
|
|
154
|
+
node --experimental-strip-types -e "
|
|
155
|
+
import fs from 'fs';
|
|
156
|
+
import { execSync } from 'child_process';
|
|
157
|
+
const auditPath = '.project/audits/' + fs.readdirSync('.project/audits').filter(f => f.endsWith('.json')).sort().pop();
|
|
158
|
+
const audit = JSON.parse(fs.readFileSync(auditPath, 'utf8'));
|
|
159
|
+
const results = [];
|
|
160
|
+
for (const finding of audit.findings) {
|
|
161
|
+
const fix = finding.fix;
|
|
162
|
+
if (!fix) { results.push({ id: finding.id, status: 'skipped', reason: 'no fix defined' }); continue; }
|
|
163
|
+
if (fix.verify_method === 'grep' && fix.verify_pattern) {
|
|
164
|
+
try {
|
|
165
|
+
execSync('grep -rn ' + JSON.stringify(fix.verify_pattern) + ' ' + finding.locations.map(l => l.file).join(' '), { encoding: 'utf8' });
|
|
166
|
+
results.push({ id: finding.id, status: 'failed', reason: 'pattern still matches' });
|
|
167
|
+
} catch { results.push({ id: finding.id, status: 'passed' }); }
|
|
168
|
+
} else {
|
|
169
|
+
results.push({ id: finding.id, status: 'needs_inspect', reason: fix.verify_method });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const passed = results.filter(r => r.status === 'passed').length;
|
|
173
|
+
const failed = results.filter(r => r.status === 'failed').length;
|
|
174
|
+
const inspect = results.filter(r => r.status === 'needs_inspect').length;
|
|
175
|
+
const total = results.length;
|
|
176
|
+
const status = failed === 0 ? (inspect > 0 ? 'passed_with_inspect' : 'passed') : 'gaps_found';
|
|
177
|
+
console.log(JSON.stringify({ status, score: passed + '/' + total, passed, failed, needs_inspect: inspect, results }));
|
|
178
|
+
"
|
|
179
|
+
output:
|
|
180
|
+
format: json
|
|
181
|
+
path: .project/fix-audit-verify.json
|
|
182
|
+
|
|
183
|
+
update-audit:
|
|
184
|
+
command: |
|
|
185
|
+
node --experimental-strip-types -e "
|
|
186
|
+
import fs from 'fs';
|
|
187
|
+
const auditPath = '.project/audits/' + fs.readdirSync('.project/audits').filter(f => f.endsWith('.json')).sort().pop();
|
|
188
|
+
const verifyPath = '.project/fix-audit-verify.json';
|
|
189
|
+
let verifyResults;
|
|
190
|
+
try { verifyResults = JSON.parse(fs.readFileSync(verifyPath, 'utf8')); } catch { console.log(JSON.stringify({ updated: null, reason: 'cannot read verify output' })); process.exit(0); }
|
|
191
|
+
const audit = JSON.parse(fs.readFileSync(auditPath, 'utf8'));
|
|
192
|
+
for (const vr of verifyResults.results || []) {
|
|
193
|
+
const finding = audit.findings.find(f => f.id === vr.id);
|
|
194
|
+
if (finding) {
|
|
195
|
+
finding.resolution = { status: vr.status, verified_at: new Date().toISOString() };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const passed = (verifyResults.results || []).filter(r => r.status === 'passed').length;
|
|
199
|
+
audit.summary.resolved = passed;
|
|
200
|
+
fs.writeFileSync(auditPath, JSON.stringify(audit, null, 2) + '\n');
|
|
201
|
+
console.log(JSON.stringify({ updated: auditPath, resolved: passed, total: audit.findings.length }));
|
|
202
|
+
"
|
|
203
|
+
output:
|
|
204
|
+
format: json
|
|
205
|
+
|
|
206
|
+
check:
|
|
207
|
+
when: "${{ steps.update-audit.output.updated }}"
|
|
208
|
+
gate:
|
|
209
|
+
check: "test '${{ steps.verify.output.failed }}' = '0'"
|
|
210
|
+
onFail: fail
|
|
211
|
+
|
|
212
|
+
completion:
|
|
213
|
+
message: |
|
|
214
|
+
Audit fix workflow completed.
|
|
215
|
+
Verification: ${{ steps.verify.output.status }} (${{ steps.verify.output.score }})
|
|
216
|
+
include:
|
|
217
|
+
- steps.verify.output.results
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
name: gap-to-phase
|
|
2
|
+
description: Create a phase spec from a gap — loads gap context, enriches with project state, dispatches phase-author
|
|
3
|
+
version: "1"
|
|
4
|
+
|
|
5
|
+
input:
|
|
6
|
+
type: object
|
|
7
|
+
required: [gap_id]
|
|
8
|
+
properties:
|
|
9
|
+
gap_id:
|
|
10
|
+
type: string
|
|
11
|
+
description: ID of the gap to convert into a phase
|
|
12
|
+
source:
|
|
13
|
+
file: .project/gaps.json
|
|
14
|
+
array: gaps
|
|
15
|
+
filter: { status: open }
|
|
16
|
+
label: id
|
|
17
|
+
value: id
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
load-gap:
|
|
21
|
+
command: |
|
|
22
|
+
node --experimental-strip-types -e "
|
|
23
|
+
import { readBlock } from './src/block-api.ts';
|
|
24
|
+
const gapId = process.argv[1];
|
|
25
|
+
const data = readBlock('.', 'gaps');
|
|
26
|
+
const gap = data.gaps.find(g => g.id === gapId);
|
|
27
|
+
if (!gap) { console.error('Gap not found: ' + gapId); process.exit(1); }
|
|
28
|
+
if (gap.status === 'resolved') { console.error('Gap already resolved: ' + gapId); process.exit(1); }
|
|
29
|
+
console.log(JSON.stringify({ gap }));
|
|
30
|
+
" '${{ input.gap_id }}'
|
|
31
|
+
output:
|
|
32
|
+
format: json
|
|
33
|
+
|
|
34
|
+
load-context:
|
|
35
|
+
command: |
|
|
36
|
+
node --experimental-strip-types -e "
|
|
37
|
+
import fs from 'fs';
|
|
38
|
+
import path from 'path';
|
|
39
|
+
import { readBlock } from './src/block-api.ts';
|
|
40
|
+
|
|
41
|
+
const phases = [];
|
|
42
|
+
const phasesDir = '.project/phases';
|
|
43
|
+
if (fs.existsSync(phasesDir)) {
|
|
44
|
+
for (const f of fs.readdirSync(phasesDir).filter(f => f.endsWith('.json')).sort()) {
|
|
45
|
+
phases.push(JSON.parse(fs.readFileSync(path.join(phasesDir, f), 'utf8')));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let architecture = {};
|
|
50
|
+
try { architecture = readBlock('.', 'architecture'); } catch {}
|
|
51
|
+
let conventions = {};
|
|
52
|
+
try { conventions = readBlock('.', 'conventions'); } catch {}
|
|
53
|
+
let gaps = { gaps: [] };
|
|
54
|
+
try { gaps = readBlock('.', 'gaps'); } catch {}
|
|
55
|
+
let inventory = {};
|
|
56
|
+
try { inventory = readBlock('.', 'inventory'); } catch {}
|
|
57
|
+
|
|
58
|
+
console.log(JSON.stringify({ phases, architecture, conventions, gaps: gaps.gaps.filter(g => g.status === 'open'), inventory }));
|
|
59
|
+
"
|
|
60
|
+
output:
|
|
61
|
+
format: json
|
|
62
|
+
|
|
63
|
+
author:
|
|
64
|
+
agent: phase-author
|
|
65
|
+
input:
|
|
66
|
+
intent: "Implement gap ${{ steps.load-gap.output.gap.id }}: ${{ steps.load-gap.output.gap.description }}"
|
|
67
|
+
gap: "${{ steps.load-gap.output.gap }}"
|
|
68
|
+
phases: "${{ steps.load-context.output.phases }}"
|
|
69
|
+
architecture: "${{ steps.load-context.output.architecture }}"
|
|
70
|
+
conventions: "${{ steps.load-context.output.conventions }}"
|
|
71
|
+
gaps: "${{ steps.load-context.output.gaps }}"
|
|
72
|
+
inventory: "${{ steps.load-context.output.inventory }}"
|
|
73
|
+
output:
|
|
74
|
+
format: json
|
|
75
|
+
schema: ../../.project/schemas/phase.schema.json
|
|
76
|
+
|
|
77
|
+
write-phase:
|
|
78
|
+
command: |
|
|
79
|
+
node --experimental-strip-types -e "
|
|
80
|
+
import fs from 'fs';
|
|
81
|
+
const phase = JSON.parse(process.argv[1]);
|
|
82
|
+
const num = String(phase.number).padStart(2, '0');
|
|
83
|
+
const name = phase.name.toLowerCase().replace(/\s+/g, '-');
|
|
84
|
+
const filename = '.project/phases/' + num + '-' + name + '.json';
|
|
85
|
+
fs.mkdirSync('.project/phases', { recursive: true });
|
|
86
|
+
fs.writeFileSync(filename, JSON.stringify(phase, null, 2));
|
|
87
|
+
console.log(JSON.stringify({ path: filename, phase_number: phase.number, spec_count: (phase.specs || []).length }));
|
|
88
|
+
" '${{ steps.author.output | json }}'
|
|
89
|
+
output:
|
|
90
|
+
format: json
|
|
91
|
+
|
|
92
|
+
completion:
|
|
93
|
+
message: |
|
|
94
|
+
Phase ${{ steps.write-phase.output.phase_number }} created: ${{ steps.write-phase.output.path }}
|
|
95
|
+
Specs: ${{ steps.write-phase.output.spec_count }}
|
|
96
|
+
From gap: ${{ input.gap_id }}
|
|
97
|
+
include:
|
|
98
|
+
- steps.author.output
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: parallel-analysis
|
|
2
|
+
description: Analyze a codebase from multiple angles concurrently, then synthesize findings
|
|
3
|
+
version: "1"
|
|
4
|
+
|
|
5
|
+
input:
|
|
6
|
+
type: object
|
|
7
|
+
required: [path]
|
|
8
|
+
properties:
|
|
9
|
+
path:
|
|
10
|
+
type: string
|
|
11
|
+
description: Path to analyze (file or directory)
|
|
12
|
+
focus:
|
|
13
|
+
type: string
|
|
14
|
+
description: Optional area of focus for the analysis
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
explore:
|
|
18
|
+
agent: code-explorer
|
|
19
|
+
input:
|
|
20
|
+
path: ${{ input.path }}
|
|
21
|
+
|
|
22
|
+
# These three steps have no mutual dependencies — they all depend only on
|
|
23
|
+
# 'explore'. The DAG will place them in the same execution layer and run
|
|
24
|
+
# them concurrently.
|
|
25
|
+
analyze-structure:
|
|
26
|
+
agent: structure-analyzer
|
|
27
|
+
input:
|
|
28
|
+
exploration: ${{ steps.explore.textOutput }}
|
|
29
|
+
path: ${{ input.path }}
|
|
30
|
+
|
|
31
|
+
analyze-quality:
|
|
32
|
+
agent: quality-analyzer
|
|
33
|
+
input:
|
|
34
|
+
exploration: ${{ steps.explore.textOutput }}
|
|
35
|
+
path: ${{ input.path }}
|
|
36
|
+
|
|
37
|
+
analyze-patterns:
|
|
38
|
+
agent: pattern-analyzer
|
|
39
|
+
input:
|
|
40
|
+
exploration: ${{ steps.explore.textOutput }}
|
|
41
|
+
path: ${{ input.path }}
|
|
42
|
+
|
|
43
|
+
# This step depends on all three analyzers — it must wait for all to complete
|
|
44
|
+
synthesize:
|
|
45
|
+
agent: synthesizer
|
|
46
|
+
input:
|
|
47
|
+
structure: ${{ steps.analyze-structure.textOutput }}
|
|
48
|
+
quality: ${{ steps.analyze-quality.textOutput }}
|
|
49
|
+
patterns: ${{ steps.analyze-patterns.textOutput }}
|
|
50
|
+
focus: ${{ input.focus }}
|
|
51
|
+
|
|
52
|
+
artifacts:
|
|
53
|
+
report:
|
|
54
|
+
path: latest-analysis.md
|
|
55
|
+
from: steps.synthesize.textOutput
|
|
56
|
+
|
|
57
|
+
completion:
|
|
58
|
+
message: |
|
|
59
|
+
Present this code analysis report to the user. Highlight the most important
|
|
60
|
+
findings and any areas that need immediate attention.
|
|
61
|
+
include:
|
|
62
|
+
- steps.synthesize.textOutput
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: parallel-explicit
|
|
2
|
+
description: Demo of explicit parallel step syntax
|
|
3
|
+
version: "1"
|
|
4
|
+
|
|
5
|
+
input:
|
|
6
|
+
type: object
|
|
7
|
+
required: [path]
|
|
8
|
+
properties:
|
|
9
|
+
path:
|
|
10
|
+
type: string
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
explore:
|
|
14
|
+
agent: code-explorer
|
|
15
|
+
input:
|
|
16
|
+
path: ${{ input.path }}
|
|
17
|
+
|
|
18
|
+
analyzers:
|
|
19
|
+
parallel:
|
|
20
|
+
structure:
|
|
21
|
+
agent: structure-analyzer
|
|
22
|
+
input:
|
|
23
|
+
exploration: ${{ steps.explore.textOutput }}
|
|
24
|
+
path: ${{ input.path }}
|
|
25
|
+
quality:
|
|
26
|
+
agent: quality-analyzer
|
|
27
|
+
input:
|
|
28
|
+
exploration: ${{ steps.explore.textOutput }}
|
|
29
|
+
path: ${{ input.path }}
|
|
30
|
+
|
|
31
|
+
summary:
|
|
32
|
+
transform:
|
|
33
|
+
mapping:
|
|
34
|
+
structure: ${{ steps.analyzers.output.structure }}
|
|
35
|
+
quality: ${{ steps.analyzers.output.quality }}
|
|
36
|
+
|
|
37
|
+
completion:
|
|
38
|
+
template: |
|
|
39
|
+
Analysis complete.
|
|
40
|
+
|
|
41
|
+
Structure: ${{ steps.analyzers.output.structure | json }}
|
|
42
|
+
Quality: ${{ steps.analyzers.output.quality | json }}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: pausable-analysis
|
|
2
|
+
description: Multi-step analysis with deliberate pause point — demonstrates checkpoint/resume
|
|
3
|
+
version: "1"
|
|
4
|
+
|
|
5
|
+
input:
|
|
6
|
+
type: object
|
|
7
|
+
required: [path]
|
|
8
|
+
properties:
|
|
9
|
+
path:
|
|
10
|
+
type: string
|
|
11
|
+
description: Path to analyze
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
explore:
|
|
15
|
+
agent: code-explorer
|
|
16
|
+
input:
|
|
17
|
+
path: ${{ input.path }}
|
|
18
|
+
|
|
19
|
+
review-exploration:
|
|
20
|
+
pause: "Exploration complete. Review results, then press Ctrl+J or /workflow resume pausable-analysis to continue."
|
|
21
|
+
|
|
22
|
+
analyze-structure:
|
|
23
|
+
agent: structure-analyzer
|
|
24
|
+
input:
|
|
25
|
+
exploration: ${{ steps.explore.textOutput }}
|
|
26
|
+
path: ${{ input.path }}
|
|
27
|
+
|
|
28
|
+
analyze-quality:
|
|
29
|
+
agent: quality-analyzer
|
|
30
|
+
input:
|
|
31
|
+
exploration: ${{ steps.explore.textOutput }}
|
|
32
|
+
path: ${{ input.path }}
|
|
33
|
+
|
|
34
|
+
synthesize:
|
|
35
|
+
agent: synthesizer
|
|
36
|
+
input:
|
|
37
|
+
structure: ${{ steps.analyze-structure.textOutput }}
|
|
38
|
+
quality: ${{ steps.analyze-quality.textOutput }}
|
|
39
|
+
|
|
40
|
+
completion:
|
|
41
|
+
message: |
|
|
42
|
+
Present this analysis report to the user.
|
|
43
|
+
include:
|
|
44
|
+
- steps.synthesize.textOutput
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: resumable-analysis
|
|
2
|
+
description: A multi-step analysis workflow that demonstrates checkpoint/resume
|
|
3
|
+
version: "1"
|
|
4
|
+
|
|
5
|
+
input:
|
|
6
|
+
type: object
|
|
7
|
+
required: [path]
|
|
8
|
+
properties:
|
|
9
|
+
path:
|
|
10
|
+
type: string
|
|
11
|
+
description: Path to analyze
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
explore:
|
|
15
|
+
agent: code-explorer
|
|
16
|
+
input:
|
|
17
|
+
path: ${{ input.path }}
|
|
18
|
+
|
|
19
|
+
analyze-structure:
|
|
20
|
+
agent: structure-analyzer
|
|
21
|
+
input:
|
|
22
|
+
exploration: ${{ steps.explore.textOutput }}
|
|
23
|
+
path: ${{ input.path }}
|
|
24
|
+
|
|
25
|
+
analyze-quality:
|
|
26
|
+
agent: quality-analyzer
|
|
27
|
+
input:
|
|
28
|
+
exploration: ${{ steps.explore.textOutput }}
|
|
29
|
+
path: ${{ input.path }}
|
|
30
|
+
|
|
31
|
+
synthesize:
|
|
32
|
+
agent: synthesizer
|
|
33
|
+
input:
|
|
34
|
+
structure: ${{ steps.analyze-structure.textOutput }}
|
|
35
|
+
quality: ${{ steps.analyze-quality.textOutput }}
|
|
36
|
+
|
|
37
|
+
completion:
|
|
38
|
+
message: |
|
|
39
|
+
Present this analysis report. Note whether this was a fresh run
|
|
40
|
+
or resumed from a prior checkpoint.
|
|
41
|
+
include:
|
|
42
|
+
- steps.synthesize.textOutput
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: self-implement
|
|
2
|
+
description: Read project blocks, decompose into plans, implement, verify
|
|
3
|
+
version: "1"
|
|
4
|
+
|
|
5
|
+
input:
|
|
6
|
+
type: object
|
|
7
|
+
required: [phaseSpec, architecture, conventions]
|
|
8
|
+
properties:
|
|
9
|
+
phaseSpec:
|
|
10
|
+
type: object
|
|
11
|
+
description: Phase spec JSON block
|
|
12
|
+
architecture:
|
|
13
|
+
type: object
|
|
14
|
+
description: Architecture JSON block
|
|
15
|
+
conventions:
|
|
16
|
+
type: object
|
|
17
|
+
description: Conventions JSON block
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
plan:
|
|
21
|
+
agent: plan-decomposer
|
|
22
|
+
input:
|
|
23
|
+
phase: "${{ input.phaseSpec }}"
|
|
24
|
+
architecture: "${{ input.architecture }}"
|
|
25
|
+
conventions: "${{ input.conventions }}"
|
|
26
|
+
output:
|
|
27
|
+
format: json
|
|
28
|
+
schema: schemas/plan-breakdown.schema.json
|
|
29
|
+
|
|
30
|
+
implement:
|
|
31
|
+
forEach: "${{ steps.plan.output.plans }}"
|
|
32
|
+
as: plan
|
|
33
|
+
agent: spec-implementer
|
|
34
|
+
input:
|
|
35
|
+
plan: "${{ plan }}"
|
|
36
|
+
architecture: "${{ input.architecture }}"
|
|
37
|
+
conventions: "${{ input.conventions }}"
|
|
38
|
+
output:
|
|
39
|
+
format: json
|
|
40
|
+
schema: schemas/execution-results.schema.json
|
|
41
|
+
|
|
42
|
+
verify:
|
|
43
|
+
agent: verifier
|
|
44
|
+
input:
|
|
45
|
+
criteria: "${{ input.phaseSpec.success_criteria }}"
|
|
46
|
+
step_output: "${{ steps.implement.output }}"
|
|
47
|
+
output:
|
|
48
|
+
format: json
|
|
49
|
+
schema: schemas/verifier-output.schema.json
|
|
50
|
+
|
|
51
|
+
check:
|
|
52
|
+
gate:
|
|
53
|
+
check: "echo '${{ steps.verify.output.status }}' | grep -q passed"
|
|
54
|
+
onFail: fail
|
|
55
|
+
|
|
56
|
+
completion:
|
|
57
|
+
message: |
|
|
58
|
+
Self-implementation workflow completed.
|
|
59
|
+
Verification status: ${{ steps.verify.output.status }}
|
|
60
|
+
Score: ${{ steps.verify.output.score }}
|
|
61
|
+
include:
|
|
62
|
+
- steps.verify.output.gaps
|
|
63
|
+
- steps.verify.output.criteria_results
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: typed-analysis
|
|
2
|
+
description: >
|
|
3
|
+
Codebase analysis with typed JSON data flow. Analyzers produce
|
|
4
|
+
schema-bound JSON. Synthesizer renders from typed data via templates.
|
|
5
|
+
Proves: YAML spec → template composition → typed output → template task → agent.
|
|
6
|
+
version: "1"
|
|
7
|
+
|
|
8
|
+
input:
|
|
9
|
+
type: object
|
|
10
|
+
required: [path]
|
|
11
|
+
properties:
|
|
12
|
+
path:
|
|
13
|
+
type: string
|
|
14
|
+
description: Path to analyze
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
explore:
|
|
18
|
+
agent: code-explorer
|
|
19
|
+
input:
|
|
20
|
+
path: ${{ input.path }}
|
|
21
|
+
|
|
22
|
+
analyze-structure:
|
|
23
|
+
agent: structure-analyzer
|
|
24
|
+
input:
|
|
25
|
+
exploration: ${{ steps.explore.output }}
|
|
26
|
+
path: ${{ input.path }}
|
|
27
|
+
output:
|
|
28
|
+
format: json
|
|
29
|
+
schema: schemas/structure-analysis.schema.json
|
|
30
|
+
|
|
31
|
+
analyze-quality:
|
|
32
|
+
agent: quality-analyzer
|
|
33
|
+
input:
|
|
34
|
+
exploration: ${{ steps.explore.output }}
|
|
35
|
+
path: ${{ input.path }}
|
|
36
|
+
output:
|
|
37
|
+
format: json
|
|
38
|
+
schema: schemas/quality-analysis.schema.json
|
|
39
|
+
|
|
40
|
+
analyze-patterns:
|
|
41
|
+
agent: pattern-analyzer
|
|
42
|
+
input:
|
|
43
|
+
exploration: ${{ steps.explore.output }}
|
|
44
|
+
path: ${{ input.path }}
|
|
45
|
+
output:
|
|
46
|
+
format: json
|
|
47
|
+
schema: schemas/pattern-analysis.schema.json
|
|
48
|
+
|
|
49
|
+
synthesize:
|
|
50
|
+
agent: synthesizer
|
|
51
|
+
input:
|
|
52
|
+
structure: ${{ steps.analyze-structure.output }}
|
|
53
|
+
quality: ${{ steps.analyze-quality.output }}
|
|
54
|
+
patterns: ${{ steps.analyze-patterns.output }}
|
|
55
|
+
output:
|
|
56
|
+
format: json
|
|
57
|
+
schema: schemas/synthesis.schema.json
|
|
58
|
+
|
|
59
|
+
completion:
|
|
60
|
+
message: |
|
|
61
|
+
Present the analysis findings to the user.
|
|
62
|
+
include:
|
|
63
|
+
- steps.synthesize.output
|