@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.
Files changed (115) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +169 -0
  3. package/agents/audit-fixer.agent.yaml +18 -0
  4. package/agents/code-explorer.agent.yaml +11 -0
  5. package/agents/decomposer.agent.yaml +51 -0
  6. package/agents/investigator.agent.yaml +49 -0
  7. package/agents/pattern-analyzer.agent.yaml +21 -0
  8. package/agents/phase-author.agent.yaml +16 -0
  9. package/agents/plan-decomposer.agent.yaml +12 -0
  10. package/agents/quality-analyzer.agent.yaml +21 -0
  11. package/agents/researcher.agent.yaml +38 -0
  12. package/agents/spec-implementer.agent.yaml +12 -0
  13. package/agents/structure-analyzer.agent.yaml +21 -0
  14. package/agents/synthesizer.agent.yaml +23 -0
  15. package/agents/verifier.agent.yaml +12 -0
  16. package/package.json +40 -0
  17. package/schemas/decomposition-specs.schema.json +50 -0
  18. package/schemas/execution-results.schema.json +50 -0
  19. package/schemas/investigation-findings.schema.json +45 -0
  20. package/schemas/pattern-analysis.schema.json +43 -0
  21. package/schemas/plan-breakdown.schema.json +22 -0
  22. package/schemas/quality-analysis.schema.json +35 -0
  23. package/schemas/research-findings.schema.json +44 -0
  24. package/schemas/structure-analysis.schema.json +42 -0
  25. package/schemas/synthesis.schema.json +33 -0
  26. package/schemas/verifier-output.schema.json +96 -0
  27. package/src/agent-spec.test.ts +289 -0
  28. package/src/agent-spec.ts +122 -0
  29. package/src/block-validation.test.ts +350 -0
  30. package/src/checkpoint.test.ts +237 -0
  31. package/src/checkpoint.ts +139 -0
  32. package/src/completion.test.ts +143 -0
  33. package/src/completion.ts +68 -0
  34. package/src/dag.test.ts +350 -0
  35. package/src/dag.ts +219 -0
  36. package/src/dispatch.test.ts +473 -0
  37. package/src/dispatch.ts +353 -0
  38. package/src/expression.test.ts +353 -0
  39. package/src/expression.ts +332 -0
  40. package/src/format.test.ts +80 -0
  41. package/src/format.ts +41 -0
  42. package/src/graduated-failure.test.ts +556 -0
  43. package/src/index.test.ts +114 -0
  44. package/src/index.ts +488 -0
  45. package/src/loop.test.ts +549 -0
  46. package/src/output.test.ts +213 -0
  47. package/src/output.ts +70 -0
  48. package/src/parallel-integration.test.ts +175 -0
  49. package/src/resume.test.ts +192 -0
  50. package/src/state.test.ts +192 -0
  51. package/src/state.ts +253 -0
  52. package/src/step-agent.test.ts +327 -0
  53. package/src/step-agent.ts +178 -0
  54. package/src/step-command.test.ts +330 -0
  55. package/src/step-command.ts +132 -0
  56. package/src/step-foreach.test.ts +647 -0
  57. package/src/step-foreach.ts +148 -0
  58. package/src/step-gate.test.ts +185 -0
  59. package/src/step-gate.ts +116 -0
  60. package/src/step-loop.test.ts +626 -0
  61. package/src/step-loop.ts +323 -0
  62. package/src/step-parallel.test.ts +475 -0
  63. package/src/step-parallel.ts +168 -0
  64. package/src/step-pause.test.ts +30 -0
  65. package/src/step-pause.ts +27 -0
  66. package/src/step-shared.test.ts +355 -0
  67. package/src/step-shared.ts +157 -0
  68. package/src/step-transform.test.ts +155 -0
  69. package/src/step-transform.ts +47 -0
  70. package/src/template-integration.test.ts +72 -0
  71. package/src/template.test.ts +162 -0
  72. package/src/template.ts +92 -0
  73. package/src/test-helpers.ts +51 -0
  74. package/src/tui.test.ts +355 -0
  75. package/src/tui.ts +182 -0
  76. package/src/types.ts +195 -0
  77. package/src/verifier-schema.test.ts +226 -0
  78. package/src/workflow-discovery.test.ts +105 -0
  79. package/src/workflow-discovery.ts +113 -0
  80. package/src/workflow-executor.test.ts +1530 -0
  81. package/src/workflow-executor.ts +810 -0
  82. package/src/workflow-sdk.test.ts +238 -0
  83. package/src/workflow-sdk.ts +262 -0
  84. package/src/workflow-spec.test.ts +576 -0
  85. package/src/workflow-spec.ts +471 -0
  86. package/templates/analyzers/base-analyzer.md +9 -0
  87. package/templates/analyzers/macros.md +1 -0
  88. package/templates/analyzers/patterns-task.md +11 -0
  89. package/templates/analyzers/patterns.md +15 -0
  90. package/templates/analyzers/quality-task.md +11 -0
  91. package/templates/analyzers/quality.md +15 -0
  92. package/templates/analyzers/structure-task.md +17 -0
  93. package/templates/analyzers/structure.md +15 -0
  94. package/templates/audit-fixer/task.md +67 -0
  95. package/templates/decomposer/task.md +56 -0
  96. package/templates/explorer/system.md +3 -0
  97. package/templates/explorer/task.md +9 -0
  98. package/templates/investigator/task.md +30 -0
  99. package/templates/phase-author/task.md +74 -0
  100. package/templates/plan-decomposer/task.md +46 -0
  101. package/templates/researcher/task.md +26 -0
  102. package/templates/spec-implementer/task.md +53 -0
  103. package/templates/synthesizer/system.md +3 -0
  104. package/templates/synthesizer/task.md +38 -0
  105. package/templates/verifier/task.md +57 -0
  106. package/workflows/create-phase.workflow.yaml +67 -0
  107. package/workflows/do-gap.workflow.yaml +153 -0
  108. package/workflows/fix-audit.workflow.yaml +217 -0
  109. package/workflows/gap-to-phase.workflow.yaml +98 -0
  110. package/workflows/parallel-analysis.workflow.yaml +62 -0
  111. package/workflows/parallel-explicit.workflow.yaml +42 -0
  112. package/workflows/pausable-analysis.workflow.yaml +44 -0
  113. package/workflows/resumable-analysis.workflow.yaml +42 -0
  114. package/workflows/self-implement.workflow.yaml +63 -0
  115. 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