@davidorex/pi-workflows 0.2.0 → 0.3.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 (215) hide show
  1. package/CHANGELOG.md +7 -4
  2. package/README.md +12 -2
  3. package/agents/architecture-designer.agent.yaml +58 -0
  4. package/agents/architecture-inferrer.agent.yaml +60 -0
  5. package/agents/gap-identifier.agent.yaml +66 -0
  6. package/agents/handoff-writer.agent.yaml +64 -0
  7. package/agents/plan-creator.agent.yaml +63 -0
  8. package/agents/project-definer.agent.yaml +53 -0
  9. package/agents/project-inferrer.agent.yaml +63 -0
  10. package/agents/requirements-gatherer.agent.yaml +58 -0
  11. package/dist/agent-spec.d.ts +27 -0
  12. package/dist/agent-spec.d.ts.map +1 -0
  13. package/dist/agent-spec.js +112 -0
  14. package/dist/agent-spec.js.map +1 -0
  15. package/dist/checkpoint.d.ts +50 -0
  16. package/dist/checkpoint.d.ts.map +1 -0
  17. package/dist/checkpoint.js +109 -0
  18. package/dist/checkpoint.js.map +1 -0
  19. package/dist/completion.d.ts +18 -0
  20. package/dist/completion.d.ts.map +1 -0
  21. package/dist/completion.js +58 -0
  22. package/dist/completion.js.map +1 -0
  23. package/dist/dag.d.ts +56 -0
  24. package/dist/dag.d.ts.map +1 -0
  25. package/dist/dag.js +172 -0
  26. package/dist/dag.js.map +1 -0
  27. package/dist/dispatch.d.ts +35 -0
  28. package/dist/dispatch.d.ts.map +1 -0
  29. package/dist/dispatch.js +311 -0
  30. package/dist/dispatch.js.map +1 -0
  31. package/dist/expression.d.ts +64 -0
  32. package/dist/expression.d.ts.map +1 -0
  33. package/dist/expression.js +294 -0
  34. package/dist/expression.js.map +1 -0
  35. package/dist/format.d.ts +19 -0
  36. package/dist/format.d.ts.map +1 -0
  37. package/dist/format.js +42 -0
  38. package/dist/format.js.map +1 -0
  39. package/dist/index.d.ts +8 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +675 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/output.d.ts +12 -0
  44. package/dist/output.d.ts.map +1 -0
  45. package/dist/output.js +59 -0
  46. package/dist/output.js.map +1 -0
  47. package/dist/state.d.ts +66 -0
  48. package/dist/state.d.ts.map +1 -0
  49. package/dist/state.js +227 -0
  50. package/dist/state.js.map +1 -0
  51. package/dist/step-agent.d.ts +35 -0
  52. package/dist/step-agent.d.ts.map +1 -0
  53. package/dist/step-agent.js +156 -0
  54. package/dist/step-agent.js.map +1 -0
  55. package/dist/step-command.d.ts +21 -0
  56. package/dist/step-command.d.ts.map +1 -0
  57. package/dist/step-command.js +150 -0
  58. package/dist/step-command.js.map +1 -0
  59. package/dist/step-foreach.d.ts +33 -0
  60. package/dist/step-foreach.d.ts.map +1 -0
  61. package/dist/step-foreach.js +112 -0
  62. package/dist/step-foreach.js.map +1 -0
  63. package/dist/step-gate.d.ts +18 -0
  64. package/dist/step-gate.d.ts.map +1 -0
  65. package/dist/step-gate.js +137 -0
  66. package/dist/step-gate.js.map +1 -0
  67. package/dist/step-loop.d.ts +32 -0
  68. package/dist/step-loop.d.ts.map +1 -0
  69. package/dist/step-loop.js +278 -0
  70. package/dist/step-loop.js.map +1 -0
  71. package/dist/step-monitor.d.ts +56 -0
  72. package/dist/step-monitor.d.ts.map +1 -0
  73. package/dist/step-monitor.js +317 -0
  74. package/dist/step-monitor.js.map +1 -0
  75. package/dist/step-parallel.d.ts +43 -0
  76. package/dist/step-parallel.d.ts.map +1 -0
  77. package/dist/step-parallel.js +122 -0
  78. package/dist/step-parallel.js.map +1 -0
  79. package/{src/step-pause.ts → dist/step-pause.d.ts} +3 -13
  80. package/dist/step-pause.d.ts.map +1 -0
  81. package/dist/step-pause.js +22 -0
  82. package/dist/step-pause.js.map +1 -0
  83. package/dist/step-shared.d.ts +57 -0
  84. package/dist/step-shared.d.ts.map +1 -0
  85. package/dist/step-shared.js +125 -0
  86. package/dist/step-shared.js.map +1 -0
  87. package/dist/step-transform.d.ts +11 -0
  88. package/dist/step-transform.d.ts.map +1 -0
  89. package/dist/step-transform.js +41 -0
  90. package/dist/step-transform.js.map +1 -0
  91. package/dist/template.d.ts +51 -0
  92. package/dist/template.d.ts.map +1 -0
  93. package/{src/template.ts → dist/template.js} +32 -38
  94. package/dist/template.js.map +1 -0
  95. package/dist/test-helpers.d.ts +17 -0
  96. package/dist/test-helpers.d.ts.map +1 -0
  97. package/dist/test-helpers.js +46 -0
  98. package/dist/test-helpers.js.map +1 -0
  99. package/dist/tui.d.ts +47 -0
  100. package/dist/tui.d.ts.map +1 -0
  101. package/dist/tui.js +143 -0
  102. package/dist/tui.js.map +1 -0
  103. package/dist/types.d.ts +164 -0
  104. package/dist/types.d.ts.map +1 -0
  105. package/dist/types.js +3 -0
  106. package/dist/types.js.map +1 -0
  107. package/dist/workflow-discovery.d.ts +23 -0
  108. package/dist/workflow-discovery.d.ts.map +1 -0
  109. package/dist/workflow-discovery.js +103 -0
  110. package/dist/workflow-discovery.js.map +1 -0
  111. package/dist/workflow-executor.d.ts +50 -0
  112. package/dist/workflow-executor.d.ts.map +1 -0
  113. package/dist/workflow-executor.js +726 -0
  114. package/dist/workflow-executor.js.map +1 -0
  115. package/dist/workflow-sdk.d.ts +44 -0
  116. package/dist/workflow-sdk.d.ts.map +1 -0
  117. package/dist/workflow-sdk.js +438 -0
  118. package/dist/workflow-sdk.js.map +1 -0
  119. package/dist/workflow-spec.d.ts +33 -0
  120. package/dist/workflow-spec.d.ts.map +1 -0
  121. package/dist/workflow-spec.js +464 -0
  122. package/dist/workflow-spec.js.map +1 -0
  123. package/dist/workflows-dir.d.ts +3 -0
  124. package/dist/workflows-dir.d.ts.map +1 -0
  125. package/{src/workflows-dir.ts → dist/workflows-dir.js} +1 -0
  126. package/dist/workflows-dir.js.map +1 -0
  127. package/package.json +53 -38
  128. package/schemas/decomposition-specs.schema.json +48 -48
  129. package/schemas/execution-results.schema.json +48 -48
  130. package/schemas/investigation-findings.schema.json +43 -43
  131. package/schemas/pattern-analysis.schema.json +41 -41
  132. package/schemas/phase.schema.json +48 -0
  133. package/schemas/plan-breakdown.schema.json +20 -20
  134. package/schemas/quality-analysis.schema.json +33 -33
  135. package/schemas/research-findings.schema.json +42 -42
  136. package/schemas/structure-analysis.schema.json +40 -40
  137. package/schemas/synthesis.schema.json +31 -31
  138. package/schemas/verifier-output.schema.json +94 -94
  139. package/skill-narrative.md +84 -0
  140. package/skills/pi-workflows/SKILL.md +247 -0
  141. package/templates/architecture-designer/task.md +117 -0
  142. package/templates/architecture-inferrer/task.md +61 -0
  143. package/templates/gap-identifier/task.md +103 -0
  144. package/templates/handoff-writer/task.md +91 -0
  145. package/templates/plan-creator/task.md +143 -0
  146. package/templates/project-definer/task.md +67 -0
  147. package/templates/project-inferrer/task.md +56 -0
  148. package/templates/requirements-gatherer/task.md +90 -0
  149. package/workflows/analyze-existing-project.workflow.yaml +74 -0
  150. package/workflows/create-handoff.workflow.yaml +58 -0
  151. package/workflows/create-phase.workflow.yaml +1 -1
  152. package/workflows/do-gap.workflow.yaml +4 -4
  153. package/workflows/fix-audit.workflow.yaml +1 -1
  154. package/workflows/gap-to-phase.workflow.yaml +1 -1
  155. package/workflows/init-new-project.workflow.yaml +55 -0
  156. package/workflows/plan-from-requirements.workflow.yaml +51 -0
  157. package/workflows/self-implement.workflow.yaml +3 -3
  158. package/workflows/typed-analysis.workflow.yaml +4 -4
  159. package/src/agent-spec.test.ts +0 -289
  160. package/src/agent-spec.ts +0 -122
  161. package/src/block-validation.test.ts +0 -350
  162. package/src/checkpoint.test.ts +0 -237
  163. package/src/checkpoint.ts +0 -140
  164. package/src/completion.test.ts +0 -143
  165. package/src/completion.ts +0 -68
  166. package/src/dag.test.ts +0 -350
  167. package/src/dag.ts +0 -219
  168. package/src/dispatch.test.ts +0 -475
  169. package/src/dispatch.ts +0 -353
  170. package/src/expression.test.ts +0 -353
  171. package/src/expression.ts +0 -332
  172. package/src/format.test.ts +0 -80
  173. package/src/format.ts +0 -41
  174. package/src/graduated-failure.test.ts +0 -556
  175. package/src/index.test.ts +0 -114
  176. package/src/index.ts +0 -516
  177. package/src/loop.test.ts +0 -551
  178. package/src/output.test.ts +0 -213
  179. package/src/output.ts +0 -70
  180. package/src/parallel-integration.test.ts +0 -175
  181. package/src/resume.test.ts +0 -192
  182. package/src/state.test.ts +0 -192
  183. package/src/state.ts +0 -254
  184. package/src/step-agent.test.ts +0 -327
  185. package/src/step-agent.ts +0 -178
  186. package/src/step-command.test.ts +0 -330
  187. package/src/step-command.ts +0 -132
  188. package/src/step-foreach.test.ts +0 -647
  189. package/src/step-foreach.ts +0 -148
  190. package/src/step-gate.test.ts +0 -185
  191. package/src/step-gate.ts +0 -116
  192. package/src/step-loop.test.ts +0 -626
  193. package/src/step-loop.ts +0 -323
  194. package/src/step-parallel.test.ts +0 -475
  195. package/src/step-parallel.ts +0 -168
  196. package/src/step-pause.test.ts +0 -30
  197. package/src/step-shared.test.ts +0 -355
  198. package/src/step-shared.ts +0 -157
  199. package/src/step-transform.test.ts +0 -155
  200. package/src/step-transform.ts +0 -47
  201. package/src/template-integration.test.ts +0 -72
  202. package/src/template.test.ts +0 -162
  203. package/src/test-helpers.ts +0 -51
  204. package/src/tui.test.ts +0 -355
  205. package/src/tui.ts +0 -182
  206. package/src/types.ts +0 -195
  207. package/src/verifier-schema.test.ts +0 -226
  208. package/src/workflow-discovery.test.ts +0 -105
  209. package/src/workflow-discovery.ts +0 -114
  210. package/src/workflow-executor.test.ts +0 -1532
  211. package/src/workflow-executor.ts +0 -810
  212. package/src/workflow-sdk.test.ts +0 -238
  213. package/src/workflow-sdk.ts +0 -262
  214. package/src/workflow-spec.test.ts +0 -576
  215. package/src/workflow-spec.ts +0 -471
package/src/step-loop.ts DELETED
@@ -1,323 +0,0 @@
1
- /**
2
- * Loop step executor — runs sub-steps repeatedly until a gate breaks,
3
- * max attempts is reached, or a step fails.
4
- */
5
- import path from "node:path";
6
- import type { LoopSpec, StepResult, StepUsage, ExecutionState, AgentSpec, StepSpec, WorkflowSpec, LoopAttempt } from "./types.ts";
7
- import { resolveExpressions, evaluateCondition } from "./expression.ts";
8
- import { executeGate } from "./step-gate.ts";
9
- import { executeTransform } from "./step-transform.ts";
10
- import { zeroUsage, addUsage, buildPrompt, DEFAULT_MAX_ATTEMPTS, compileAgentSpec } from "./step-shared.ts";
11
- import { persistStepOutput } from "./output.ts";
12
- import type nunjucks from "nunjucks";
13
-
14
- /** Options for executeLoop, including callback-injected dispatch to avoid circular imports. */
15
- export interface LoopExecuteOptions {
16
- ctx: any;
17
- pi: any;
18
- signal?: AbortSignal;
19
- loadAgent: (name: string) => AgentSpec;
20
- dispatchAgent: (
21
- stepSpec: StepSpec,
22
- agentSpec: AgentSpec,
23
- prompt: string,
24
- opts: { cwd: string; sessionLogDir: string; stepName: string; signal?: AbortSignal },
25
- ) => Promise<StepResult>;
26
- runDir: string;
27
- spec: WorkflowSpec;
28
- templateEnv?: nunjucks.Environment;
29
- outputPath?: string; // author-declared output path (resolved ${{ }} expressions)
30
- }
31
-
32
- /**
33
- * Execute a loop step: runs sub-steps repeatedly until a gate breaks,
34
- * max attempts is reached, or a step fails.
35
- *
36
- * Loop sub-steps can be agent, gate, or transform steps.
37
- * Gates inside loops support onPass: "break" (stop looping on success)
38
- * and onFail: "continue" (retry on failure, the default inside loops).
39
- *
40
- * The loop scope provides ${{ loop.iteration }}, ${{ loop.maxAttempts }},
41
- * and ${{ loop.priorAttempts }} for expression resolution inside sub-steps.
42
- */
43
- export async function executeLoop(
44
- loopSpec: LoopSpec,
45
- stepName: string,
46
- state: ExecutionState,
47
- options: LoopExecuteOptions,
48
- ): Promise<StepResult> {
49
- const { ctx, signal, loadAgent, dispatchAgent, runDir } = options;
50
- const startTime = Date.now();
51
-
52
- // Resolve maxAttempts (may be a ${{ }} expression)
53
- const scope = { input: state.input, steps: state.steps };
54
- let maxAttempts: number;
55
- if (loopSpec.attempts) {
56
- const resolved = resolveExpressions(loopSpec.attempts, scope);
57
- maxAttempts = Number(resolved);
58
- if (isNaN(maxAttempts) || maxAttempts < 1) maxAttempts = loopSpec.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
59
- } else {
60
- maxAttempts = loopSpec.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
61
- }
62
-
63
- const allAttempts: LoopAttempt[] = [];
64
- let loopStatus: "completed" | "failed" = "failed"; // assume failure until break
65
- let lastIterationSteps: Record<string, StepResult> = {};
66
-
67
- for (let iteration = 0; iteration < maxAttempts; iteration++) {
68
- // Check cancellation
69
- if (signal?.aborted) break;
70
-
71
- const iterationSteps: Record<string, StepResult> = {};
72
- let shouldBreak = false;
73
- let iterationFailed = false;
74
-
75
- // Execute sub-steps sequentially
76
- const subStepEntries = Object.entries(loopSpec.steps);
77
- for (const [subName, subSpec] of subStepEntries) {
78
- if (signal?.aborted) break;
79
-
80
- // Build scope with loop context
81
- const subScope: Record<string, unknown> = {
82
- input: state.input,
83
- steps: { ...state.steps, ...iterationSteps }, // see prior sub-steps in this iteration
84
- loop: {
85
- iteration,
86
- maxAttempts,
87
- priorAttempts: allAttempts,
88
- },
89
- };
90
-
91
- // Handle `when` conditional
92
- if (subSpec.when) {
93
- const condExpr = subSpec.when.replace(/^\$\{\{\s*/, "").replace(/\s*\}\}$/, "");
94
- if (!evaluateCondition(condExpr, subScope)) {
95
- iterationSteps[subName] = {
96
- step: subName,
97
- agent: subSpec.agent ?? "skipped",
98
- status: "skipped",
99
- usage: zeroUsage(),
100
- durationMs: 0,
101
- };
102
- continue;
103
- }
104
- }
105
-
106
- // Execute sub-step based on type
107
- let result: StepResult;
108
-
109
- if (subSpec.gate) {
110
- const resolvedCheck = String(resolveExpressions(subSpec.gate.check, subScope));
111
- result = await executeGate({ ...subSpec.gate, check: resolvedCheck }, subName, { cwd: ctx.cwd, signal });
112
-
113
- // Handle gate pass/fail with break/continue
114
- const passed = result.output?.passed;
115
- if (passed && subSpec.gate.onPass === "break") {
116
- iterationSteps[subName] = result;
117
- shouldBreak = true;
118
- loopStatus = "completed";
119
- break;
120
- }
121
- if (!passed) {
122
- if (subSpec.gate.onFail === "break") {
123
- iterationSteps[subName] = result;
124
- shouldBreak = true;
125
- break;
126
- }
127
- if (subSpec.gate.onFail === "fail") {
128
- // Explicitly set to "fail" — stop the loop
129
- result.status = "failed";
130
- iterationSteps[subName] = result;
131
- iterationFailed = true;
132
- break;
133
- }
134
- // Default inside loops is "continue" — proceed to next sub-step
135
- // (or end iteration, triggering retry)
136
- }
137
- } else if (subSpec.transform) {
138
- result = executeTransform(subSpec.transform, subName, subScope);
139
- if (result.status === "failed") {
140
- iterationSteps[subName] = result;
141
- iterationFailed = true;
142
- break;
143
- }
144
- } else if (subSpec.agent) {
145
- // Resolve input, load agent, dispatch
146
- let resolvedInput: unknown;
147
- try {
148
- resolvedInput = resolveExpressions(subSpec.input ?? {}, subScope);
149
- } catch (err) {
150
- iterationSteps[subName] = {
151
- step: subName,
152
- agent: subSpec.agent,
153
- status: "failed",
154
- usage: zeroUsage(),
155
- durationMs: 0,
156
- error: err instanceof Error ? err.message : String(err),
157
- };
158
- iterationFailed = true;
159
- break;
160
- }
161
-
162
- let agentSpec: AgentSpec;
163
- try {
164
- agentSpec = loadAgent(subSpec.agent);
165
- } catch (err) {
166
- iterationSteps[subName] = {
167
- step: subName,
168
- agent: subSpec.agent,
169
- status: "failed",
170
- usage: zeroUsage(),
171
- durationMs: 0,
172
- error: err instanceof Error ? err.message : String(err),
173
- };
174
- iterationFailed = true;
175
- break;
176
- }
177
- agentSpec = compileAgentSpec(agentSpec, resolvedInput, options.templateEnv);
178
-
179
- const prompt = buildPrompt(subSpec, agentSpec, resolvedInput, runDir, `${stepName}-${iteration}-${subName}`);
180
-
181
- result = await dispatchAgent(subSpec, agentSpec, prompt, {
182
- cwd: ctx.cwd,
183
- sessionLogDir: path.join(runDir, "sessions"),
184
- stepName: `${stepName}-${iteration}-${subName}`,
185
- signal,
186
- });
187
-
188
- if (result.status === "failed") {
189
- iterationSteps[subName] = result;
190
- iterationFailed = true;
191
- break;
192
- }
193
- } else {
194
- continue; // unknown step type, skip
195
- }
196
-
197
- iterationSteps[subName] = result!;
198
- }
199
-
200
- // Record this iteration
201
- lastIterationSteps = iterationSteps;
202
- allAttempts.push({ iteration, steps: iterationSteps });
203
-
204
- if (shouldBreak) break;
205
- if (iterationFailed) {
206
- // Agent step failed within loop — stop the loop (not just the iteration)
207
- loopStatus = "failed";
208
- break;
209
- }
210
-
211
- // End of iteration without break — loop continues to next iteration
212
- }
213
-
214
- // If loop exhausted without break or failure, run onExhausted
215
- if (loopStatus !== "completed" && loopSpec.onExhausted) {
216
- const exhaustedScope: Record<string, unknown> = {
217
- input: state.input,
218
- steps: state.steps,
219
- loop: {
220
- iteration: allAttempts.length,
221
- maxAttempts,
222
- priorAttempts: allAttempts,
223
- allAttempts,
224
- },
225
- };
226
-
227
- let resolvedInput: unknown;
228
- let exhaustedError: string | undefined;
229
- try {
230
- resolvedInput = resolveExpressions(loopSpec.onExhausted.input ?? {}, exhaustedScope);
231
- } catch (err) {
232
- exhaustedError = err instanceof Error ? err.message : String(err);
233
- // Still proceed with onExhausted — let the agent run with empty input
234
- // but record the expression error so it's visible in the result
235
- }
236
-
237
- if (loopSpec.onExhausted.agent) {
238
- let agentSpec: AgentSpec;
239
- try {
240
- agentSpec = loadAgent(loopSpec.onExhausted.agent);
241
- } catch (err) {
242
- lastIterationSteps["_exhausted"] = {
243
- step: `${stepName}-exhausted`,
244
- agent: loopSpec.onExhausted.agent,
245
- status: "failed",
246
- usage: zeroUsage(),
247
- durationMs: 0,
248
- error: err instanceof Error ? err.message : String(err),
249
- };
250
- agentSpec = null as any;
251
- }
252
-
253
- if (agentSpec) {
254
- const prompt = buildPrompt(loopSpec.onExhausted, agentSpec, resolvedInput, runDir, `${stepName}-exhausted`);
255
-
256
- const exhaustedResult = await dispatchAgent(loopSpec.onExhausted, agentSpec, prompt, {
257
- cwd: ctx.cwd,
258
- sessionLogDir: path.join(runDir, "sessions"),
259
- stepName: `${stepName}-exhausted`,
260
- signal,
261
- });
262
-
263
- if (exhaustedResult && exhaustedError) {
264
- exhaustedResult.error = `Expression error in onExhausted input: ${exhaustedError}. Agent ran with empty input.`;
265
- }
266
- // Include exhausted result in the loop's output
267
- lastIterationSteps["_exhausted"] = exhaustedResult;
268
- }
269
- }
270
-
271
- if (!loopSpec.onExhausted.agent && exhaustedError) {
272
- lastIterationSteps["_exhausted"] = {
273
- step: `${stepName}-exhausted`,
274
- agent: "exhausted",
275
- status: "failed",
276
- usage: zeroUsage(),
277
- durationMs: 0,
278
- error: `Expression error in onExhausted input: ${exhaustedError}`,
279
- };
280
- }
281
- }
282
-
283
- // Aggregate usage across all iterations
284
- const totalUsage = zeroUsage();
285
- for (const attempt of allAttempts) {
286
- for (const result of Object.values(attempt.steps)) {
287
- addUsage(totalUsage, result.usage);
288
- }
289
- }
290
-
291
- const loopOutput = {
292
- iterations: allAttempts.length,
293
- maxAttempts,
294
- attempts: allAttempts,
295
- lastIteration: lastIterationSteps,
296
- };
297
- const loopTextOutput = `Loop '${stepName}': ${allAttempts.length}/${maxAttempts} iterations, status: ${loopStatus}`;
298
-
299
- const result: StepResult = {
300
- step: stepName,
301
- agent: "loop",
302
- status: loopStatus,
303
- output: loopOutput,
304
- textOutput: loopTextOutput,
305
- usage: totalUsage,
306
- durationMs: Date.now() - startTime,
307
- };
308
-
309
- // Propagate truncation from any sub-step
310
- const anyTruncated = allAttempts.some(a =>
311
- Object.values(a.steps).some(s => s.truncated)
312
- );
313
- if (anyTruncated) {
314
- result.truncated = true;
315
- result.warnings = [
316
- ...(result.warnings ?? []),
317
- "One or more loop sub-steps had truncated stdout — usage totals may undercount.",
318
- ];
319
- }
320
-
321
- result.outputPath = persistStepOutput(runDir, stepName, loopOutput, loopTextOutput, options.outputPath);
322
- return result;
323
- }