@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
@@ -0,0 +1,278 @@
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 { evaluateCondition, resolveExpressions } from "./expression.js";
7
+ import { persistStepOutput } from "./output.js";
8
+ import { executeGate } from "./step-gate.js";
9
+ import { addUsage, buildPrompt, compileAgentSpec, DEFAULT_MAX_ATTEMPTS, zeroUsage } from "./step-shared.js";
10
+ import { executeTransform } from "./step-transform.js";
11
+ /**
12
+ * Execute a loop step: runs sub-steps repeatedly until a gate breaks,
13
+ * max attempts is reached, or a step fails.
14
+ *
15
+ * Loop sub-steps can be agent, gate, or transform steps.
16
+ * Gates inside loops support onPass: "break" (stop looping on success)
17
+ * and onFail: "continue" (retry on failure, the default inside loops).
18
+ *
19
+ * The loop scope provides ${{ loop.iteration }}, ${{ loop.maxAttempts }},
20
+ * and ${{ loop.priorAttempts }} for expression resolution inside sub-steps.
21
+ */
22
+ export async function executeLoop(loopSpec, stepName, state, options) {
23
+ const { ctx, signal, loadAgent, dispatchAgent, runDir } = options;
24
+ const startTime = Date.now();
25
+ // Resolve maxAttempts (may be a ${{ }} expression)
26
+ const scope = { input: state.input, steps: state.steps };
27
+ let maxAttempts;
28
+ if (loopSpec.attempts) {
29
+ const resolved = resolveExpressions(loopSpec.attempts, scope);
30
+ maxAttempts = Number(resolved);
31
+ if (isNaN(maxAttempts) || maxAttempts < 1)
32
+ maxAttempts = loopSpec.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
33
+ }
34
+ else {
35
+ maxAttempts = loopSpec.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
36
+ }
37
+ const allAttempts = [];
38
+ let loopStatus = "failed"; // assume failure until break
39
+ let lastIterationSteps = {};
40
+ for (let iteration = 0; iteration < maxAttempts; iteration++) {
41
+ // Check cancellation
42
+ if (signal?.aborted)
43
+ break;
44
+ const iterationSteps = {};
45
+ let shouldBreak = false;
46
+ let iterationFailed = false;
47
+ // Execute sub-steps sequentially
48
+ const subStepEntries = Object.entries(loopSpec.steps);
49
+ for (const [subName, subSpec] of subStepEntries) {
50
+ if (signal?.aborted)
51
+ break;
52
+ // Build scope with loop context
53
+ const subScope = {
54
+ input: state.input,
55
+ steps: { ...state.steps, ...iterationSteps }, // see prior sub-steps in this iteration
56
+ loop: {
57
+ iteration,
58
+ maxAttempts,
59
+ priorAttempts: allAttempts,
60
+ },
61
+ };
62
+ // Handle `when` conditional
63
+ if (subSpec.when) {
64
+ const condExpr = subSpec.when.replace(/^\$\{\{\s*/, "").replace(/\s*\}\}$/, "");
65
+ if (!evaluateCondition(condExpr, subScope)) {
66
+ iterationSteps[subName] = {
67
+ step: subName,
68
+ agent: subSpec.agent ?? "skipped",
69
+ status: "skipped",
70
+ usage: zeroUsage(),
71
+ durationMs: 0,
72
+ };
73
+ continue;
74
+ }
75
+ }
76
+ // Execute sub-step based on type
77
+ let result;
78
+ if (subSpec.gate) {
79
+ const resolvedCheck = String(resolveExpressions(subSpec.gate.check, subScope));
80
+ result = await executeGate({ ...subSpec.gate, check: resolvedCheck }, subName, { cwd: ctx.cwd, signal });
81
+ // Handle gate pass/fail with break/continue
82
+ const passed = result.output?.passed;
83
+ if (passed && subSpec.gate.onPass === "break") {
84
+ iterationSteps[subName] = result;
85
+ shouldBreak = true;
86
+ loopStatus = "completed";
87
+ break;
88
+ }
89
+ if (!passed) {
90
+ if (subSpec.gate.onFail === "break") {
91
+ iterationSteps[subName] = result;
92
+ shouldBreak = true;
93
+ break;
94
+ }
95
+ if (subSpec.gate.onFail === "fail") {
96
+ // Explicitly set to "fail" — stop the loop
97
+ result.status = "failed";
98
+ iterationSteps[subName] = result;
99
+ iterationFailed = true;
100
+ break;
101
+ }
102
+ // Default inside loops is "continue" — proceed to next sub-step
103
+ // (or end iteration, triggering retry)
104
+ }
105
+ }
106
+ else if (subSpec.transform) {
107
+ result = executeTransform(subSpec.transform, subName, subScope);
108
+ if (result.status === "failed") {
109
+ iterationSteps[subName] = result;
110
+ iterationFailed = true;
111
+ break;
112
+ }
113
+ }
114
+ else if (subSpec.agent) {
115
+ // Resolve input, load agent, dispatch
116
+ let resolvedInput;
117
+ try {
118
+ resolvedInput = resolveExpressions(subSpec.input ?? {}, subScope);
119
+ }
120
+ catch (err) {
121
+ iterationSteps[subName] = {
122
+ step: subName,
123
+ agent: subSpec.agent,
124
+ status: "failed",
125
+ usage: zeroUsage(),
126
+ durationMs: 0,
127
+ error: err instanceof Error ? err.message : String(err),
128
+ };
129
+ iterationFailed = true;
130
+ break;
131
+ }
132
+ let agentSpec;
133
+ try {
134
+ agentSpec = loadAgent(subSpec.agent);
135
+ }
136
+ catch (err) {
137
+ iterationSteps[subName] = {
138
+ step: subName,
139
+ agent: subSpec.agent,
140
+ status: "failed",
141
+ usage: zeroUsage(),
142
+ durationMs: 0,
143
+ error: err instanceof Error ? err.message : String(err),
144
+ };
145
+ iterationFailed = true;
146
+ break;
147
+ }
148
+ agentSpec = compileAgentSpec(agentSpec, resolvedInput, options.templateEnv);
149
+ const prompt = buildPrompt(subSpec, agentSpec, resolvedInput, runDir, `${stepName}-${iteration}-${subName}`);
150
+ result = await dispatchAgent(subSpec, agentSpec, prompt, {
151
+ cwd: ctx.cwd,
152
+ sessionLogDir: path.join(runDir, "sessions"),
153
+ stepName: `${stepName}-${iteration}-${subName}`,
154
+ signal,
155
+ });
156
+ if (result.status === "failed") {
157
+ iterationSteps[subName] = result;
158
+ iterationFailed = true;
159
+ break;
160
+ }
161
+ }
162
+ else {
163
+ continue; // unknown step type, skip
164
+ }
165
+ iterationSteps[subName] = result;
166
+ }
167
+ // Record this iteration
168
+ lastIterationSteps = iterationSteps;
169
+ allAttempts.push({ iteration, steps: iterationSteps });
170
+ if (shouldBreak)
171
+ break;
172
+ if (iterationFailed) {
173
+ // Agent step failed within loop — stop the loop (not just the iteration)
174
+ loopStatus = "failed";
175
+ break;
176
+ }
177
+ // End of iteration without break — loop continues to next iteration
178
+ }
179
+ // If loop exhausted without break or failure, run onExhausted
180
+ if (loopStatus !== "completed" && loopSpec.onExhausted) {
181
+ const exhaustedScope = {
182
+ input: state.input,
183
+ steps: state.steps,
184
+ loop: {
185
+ iteration: allAttempts.length,
186
+ maxAttempts,
187
+ priorAttempts: allAttempts,
188
+ allAttempts,
189
+ },
190
+ };
191
+ let resolvedInput;
192
+ let exhaustedError;
193
+ try {
194
+ resolvedInput = resolveExpressions(loopSpec.onExhausted.input ?? {}, exhaustedScope);
195
+ }
196
+ catch (err) {
197
+ exhaustedError = err instanceof Error ? err.message : String(err);
198
+ // Still proceed with onExhausted — let the agent run with empty input
199
+ // but record the expression error so it's visible in the result
200
+ }
201
+ if (loopSpec.onExhausted.agent) {
202
+ let agentSpec;
203
+ try {
204
+ agentSpec = loadAgent(loopSpec.onExhausted.agent);
205
+ }
206
+ catch (err) {
207
+ lastIterationSteps["_exhausted"] = {
208
+ step: `${stepName}-exhausted`,
209
+ agent: loopSpec.onExhausted.agent,
210
+ status: "failed",
211
+ usage: zeroUsage(),
212
+ durationMs: 0,
213
+ error: err instanceof Error ? err.message : String(err),
214
+ };
215
+ agentSpec = null;
216
+ }
217
+ if (agentSpec) {
218
+ const prompt = buildPrompt(loopSpec.onExhausted, agentSpec, resolvedInput, runDir, `${stepName}-exhausted`);
219
+ const exhaustedResult = await dispatchAgent(loopSpec.onExhausted, agentSpec, prompt, {
220
+ cwd: ctx.cwd,
221
+ sessionLogDir: path.join(runDir, "sessions"),
222
+ stepName: `${stepName}-exhausted`,
223
+ signal,
224
+ });
225
+ if (exhaustedResult && exhaustedError) {
226
+ exhaustedResult.error = `Expression error in onExhausted input: ${exhaustedError}. Agent ran with empty input.`;
227
+ }
228
+ // Include exhausted result in the loop's output
229
+ lastIterationSteps["_exhausted"] = exhaustedResult;
230
+ }
231
+ }
232
+ if (!loopSpec.onExhausted.agent && exhaustedError) {
233
+ lastIterationSteps["_exhausted"] = {
234
+ step: `${stepName}-exhausted`,
235
+ agent: "exhausted",
236
+ status: "failed",
237
+ usage: zeroUsage(),
238
+ durationMs: 0,
239
+ error: `Expression error in onExhausted input: ${exhaustedError}`,
240
+ };
241
+ }
242
+ }
243
+ // Aggregate usage across all iterations
244
+ const totalUsage = zeroUsage();
245
+ for (const attempt of allAttempts) {
246
+ for (const result of Object.values(attempt.steps)) {
247
+ addUsage(totalUsage, result.usage);
248
+ }
249
+ }
250
+ const loopOutput = {
251
+ iterations: allAttempts.length,
252
+ maxAttempts,
253
+ attempts: allAttempts,
254
+ lastIteration: lastIterationSteps,
255
+ };
256
+ const loopTextOutput = `Loop '${stepName}': ${allAttempts.length}/${maxAttempts} iterations, status: ${loopStatus}`;
257
+ const result = {
258
+ step: stepName,
259
+ agent: "loop",
260
+ status: loopStatus,
261
+ output: loopOutput,
262
+ textOutput: loopTextOutput,
263
+ usage: totalUsage,
264
+ durationMs: Date.now() - startTime,
265
+ };
266
+ // Propagate truncation from any sub-step
267
+ const anyTruncated = allAttempts.some((a) => Object.values(a.steps).some((s) => s.truncated));
268
+ if (anyTruncated) {
269
+ result.truncated = true;
270
+ result.warnings = [
271
+ ...(result.warnings ?? []),
272
+ "One or more loop sub-steps had truncated stdout — usage totals may undercount.",
273
+ ];
274
+ }
275
+ result.outputPath = persistStepOutput(runDir, stepName, loopOutput, loopTextOutput, options.outputPath);
276
+ return result;
277
+ }
278
+ //# sourceMappingURL=step-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-loop.js","sourceRoot":"","sources":["../src/step-loop.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC5G,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AA8BvD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAChC,QAAkB,EAClB,QAAgB,EAChB,KAAqB,EACrB,OAA2B;IAE3B,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAClE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,mDAAmD;IACnD,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IACzD,IAAI,WAAmB,CAAC;IACxB,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC9D,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,CAAC;YAAE,WAAW,GAAG,QAAQ,CAAC,WAAW,IAAI,oBAAoB,CAAC;IACvG,CAAC;SAAM,CAAC;QACP,WAAW,GAAG,QAAQ,CAAC,WAAW,IAAI,oBAAoB,CAAC;IAC5D,CAAC;IAED,MAAM,WAAW,GAAkB,EAAE,CAAC;IACtC,IAAI,UAAU,GAA2B,QAAQ,CAAC,CAAC,6BAA6B;IAChF,IAAI,kBAAkB,GAA+B,EAAE,CAAC;IAExD,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,WAAW,EAAE,SAAS,EAAE,EAAE,CAAC;QAC9D,qBAAqB;QACrB,IAAI,MAAM,EAAE,OAAO;YAAE,MAAM;QAE3B,MAAM,cAAc,GAA+B,EAAE,CAAC;QACtD,IAAI,WAAW,GAAG,KAAK,CAAC;QACxB,IAAI,eAAe,GAAG,KAAK,CAAC;QAE5B,iCAAiC;QACjC,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACtD,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,cAAc,EAAE,CAAC;YACjD,IAAI,MAAM,EAAE,OAAO;gBAAE,MAAM;YAE3B,gCAAgC;YAChC,MAAM,QAAQ,GAA4B;gBACzC,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,KAAK,EAAE,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,cAAc,EAAE,EAAE,wCAAwC;gBACtF,IAAI,EAAE;oBACL,SAAS;oBACT,WAAW;oBACX,aAAa,EAAE,WAAW;iBAC1B;aACD,CAAC;YAEF,4BAA4B;YAC5B,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBAClB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;gBAChF,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;oBAC5C,cAAc,CAAC,OAAO,CAAC,GAAG;wBACzB,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS;wBACjC,MAAM,EAAE,SAAS;wBACjB,KAAK,EAAE,SAAS,EAAE;wBAClB,UAAU,EAAE,CAAC;qBACb,CAAC;oBACF,SAAS;gBACV,CAAC;YACF,CAAC;YAED,iCAAiC;YACjC,IAAI,MAAkB,CAAC;YAEvB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBAClB,MAAM,aAAa,GAAG,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;gBAC/E,MAAM,GAAG,MAAM,WAAW,CAAC,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;gBAEzG,4CAA4C;gBAC5C,MAAM,MAAM,GAAI,MAAM,CAAC,MAA8C,EAAE,MAAM,CAAC;gBAC9E,IAAI,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;oBAC/C,cAAc,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;oBACjC,WAAW,GAAG,IAAI,CAAC;oBACnB,UAAU,GAAG,WAAW,CAAC;oBACzB,MAAM;gBACP,CAAC;gBACD,IAAI,CAAC,MAAM,EAAE,CAAC;oBACb,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;wBACrC,cAAc,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;wBACjC,WAAW,GAAG,IAAI,CAAC;wBACnB,MAAM;oBACP,CAAC;oBACD,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;wBACpC,2CAA2C;wBAC3C,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC;wBACzB,cAAc,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;wBACjC,eAAe,GAAG,IAAI,CAAC;wBACvB,MAAM;oBACP,CAAC;oBACD,gEAAgE;oBAChE,uCAAuC;gBACxC,CAAC;YACF,CAAC;iBAAM,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBAC9B,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;gBAChE,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;oBAChC,cAAc,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;oBACjC,eAAe,GAAG,IAAI,CAAC;oBACvB,MAAM;gBACP,CAAC;YACF,CAAC;iBAAM,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;gBAC1B,sCAAsC;gBACtC,IAAI,aAAsB,CAAC;gBAC3B,IAAI,CAAC;oBACJ,aAAa,GAAG,kBAAkB,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC;gBACnE,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACd,cAAc,CAAC,OAAO,CAAC,GAAG;wBACzB,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,OAAO,CAAC,KAAK;wBACpB,MAAM,EAAE,QAAQ;wBAChB,KAAK,EAAE,SAAS,EAAE;wBAClB,UAAU,EAAE,CAAC;wBACb,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;qBACvD,CAAC;oBACF,eAAe,GAAG,IAAI,CAAC;oBACvB,MAAM;gBACP,CAAC;gBAED,IAAI,SAAoB,CAAC;gBACzB,IAAI,CAAC;oBACJ,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBACtC,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACd,cAAc,CAAC,OAAO,CAAC,GAAG;wBACzB,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,OAAO,CAAC,KAAK;wBACpB,MAAM,EAAE,QAAQ;wBAChB,KAAK,EAAE,SAAS,EAAE;wBAClB,UAAU,EAAE,CAAC;wBACb,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;qBACvD,CAAC;oBACF,eAAe,GAAG,IAAI,CAAC;oBACvB,MAAM;gBACP,CAAC;gBACD,SAAS,GAAG,gBAAgB,CAAC,SAAS,EAAE,aAAa,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;gBAE5E,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,QAAQ,IAAI,SAAS,IAAI,OAAO,EAAE,CAAC,CAAC;gBAE7G,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE;oBACxD,GAAG,EAAE,GAAG,CAAC,GAAG;oBACZ,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC;oBAC5C,QAAQ,EAAE,GAAG,QAAQ,IAAI,SAAS,IAAI,OAAO,EAAE;oBAC/C,MAAM;iBACN,CAAC,CAAC;gBAEH,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;oBAChC,cAAc,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;oBACjC,eAAe,GAAG,IAAI,CAAC;oBACvB,MAAM;gBACP,CAAC;YACF,CAAC;iBAAM,CAAC;gBACP,SAAS,CAAC,0BAA0B;YACrC,CAAC;YAED,cAAc,CAAC,OAAO,CAAC,GAAG,MAAO,CAAC;QACnC,CAAC;QAED,wBAAwB;QACxB,kBAAkB,GAAG,cAAc,CAAC;QACpC,WAAW,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;QAEvD,IAAI,WAAW;YAAE,MAAM;QACvB,IAAI,eAAe,EAAE,CAAC;YACrB,yEAAyE;YACzE,UAAU,GAAG,QAAQ,CAAC;YACtB,MAAM;QACP,CAAC;QAED,oEAAoE;IACrE,CAAC;IAED,8DAA8D;IAC9D,IAAI,UAAU,KAAK,WAAW,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;QACxD,MAAM,cAAc,GAA4B;YAC/C,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,IAAI,EAAE;gBACL,SAAS,EAAE,WAAW,CAAC,MAAM;gBAC7B,WAAW;gBACX,aAAa,EAAE,WAAW;gBAC1B,WAAW;aACX;SACD,CAAC;QAEF,IAAI,aAAsB,CAAC;QAC3B,IAAI,cAAkC,CAAC;QACvC,IAAI,CAAC;YACJ,aAAa,GAAG,kBAAkB,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,IAAI,EAAE,EAAE,cAAc,CAAC,CAAC;QACtF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,cAAc,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAClE,sEAAsE;YACtE,gEAAgE;QACjE,CAAC;QAED,IAAI,QAAQ,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;YAChC,IAAI,SAAoB,CAAC;YACzB,IAAI,CAAC;gBACJ,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACnD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,kBAAkB,CAAC,YAAY,CAAC,GAAG;oBAClC,IAAI,EAAE,GAAG,QAAQ,YAAY;oBAC7B,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC,KAAK;oBACjC,MAAM,EAAE,QAAQ;oBAChB,KAAK,EAAE,SAAS,EAAE;oBAClB,UAAU,EAAE,CAAC;oBACb,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;iBACvD,CAAC;gBACF,SAAS,GAAG,IAAW,CAAC;YACzB,CAAC;YAED,IAAI,SAAS,EAAE,CAAC;gBACf,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,QAAQ,YAAY,CAAC,CAAC;gBAE5G,MAAM,eAAe,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE;oBACpF,GAAG,EAAE,GAAG,CAAC,GAAG;oBACZ,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC;oBAC5C,QAAQ,EAAE,GAAG,QAAQ,YAAY;oBACjC,MAAM;iBACN,CAAC,CAAC;gBAEH,IAAI,eAAe,IAAI,cAAc,EAAE,CAAC;oBACvC,eAAe,CAAC,KAAK,GAAG,0CAA0C,cAAc,+BAA+B,CAAC;gBACjH,CAAC;gBACD,gDAAgD;gBAChD,kBAAkB,CAAC,YAAY,CAAC,GAAG,eAAe,CAAC;YACpD,CAAC;QACF,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,IAAI,cAAc,EAAE,CAAC;YACnD,kBAAkB,CAAC,YAAY,CAAC,GAAG;gBAClC,IAAI,EAAE,GAAG,QAAQ,YAAY;gBAC7B,KAAK,EAAE,WAAW;gBAClB,MAAM,EAAE,QAAQ;gBAChB,KAAK,EAAE,SAAS,EAAE;gBAClB,UAAU,EAAE,CAAC;gBACb,KAAK,EAAE,0CAA0C,cAAc,EAAE;aACjE,CAAC;QACH,CAAC;IACF,CAAC;IAED,wCAAwC;IACxC,MAAM,UAAU,GAAG,SAAS,EAAE,CAAC;IAC/B,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;QACnC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACnD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;IACF,CAAC;IAED,MAAM,UAAU,GAAG;QAClB,UAAU,EAAE,WAAW,CAAC,MAAM;QAC9B,WAAW;QACX,QAAQ,EAAE,WAAW;QACrB,aAAa,EAAE,kBAAkB;KACjC,CAAC;IACF,MAAM,cAAc,GAAG,SAAS,QAAQ,MAAM,WAAW,CAAC,MAAM,IAAI,WAAW,wBAAwB,UAAU,EAAE,CAAC;IAEpH,MAAM,MAAM,GAAe;QAC1B,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,UAAU;QAClB,MAAM,EAAE,UAAU;QAClB,UAAU,EAAE,cAAc;QAC1B,KAAK,EAAE,UAAU;QACjB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;KAClC,CAAC;IAEF,yCAAyC;IACzC,MAAM,YAAY,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC9F,IAAI,YAAY,EAAE,CAAC;QAClB,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,MAAM,CAAC,QAAQ,GAAG;YACjB,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;YAC1B,gFAAgF;SAChF,CAAC;IACH,CAAC;IAED,MAAM,CAAC,UAAU,GAAG,iBAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACxG,OAAO,MAAM,CAAC;AACf,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Monitor step executor — runs a monitor's classification as a workflow
3
+ * verification gate. Lightweight: reads monitor spec, loads patterns,
4
+ * renders prompt (Nunjucks or inline), calls LLM, parses verdict.
5
+ *
6
+ * Does NOT depend on pi-behavior-monitors — implements just enough to
7
+ * classify. No event handling, steering, ceiling, or escalation.
8
+ */
9
+ import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
10
+ import type { StepResult } from "./types.js";
11
+ interface MonitorSpec {
12
+ name: string;
13
+ classify: {
14
+ model: string;
15
+ context: string[];
16
+ prompt: string;
17
+ promptTemplate?: string;
18
+ };
19
+ patterns: {
20
+ path: string;
21
+ };
22
+ instructions?: {
23
+ path: string;
24
+ };
25
+ }
26
+ /**
27
+ * Find a monitor spec by name. Searches:
28
+ * 1. .pi/monitors/ (project)
29
+ * 2. ~/.pi/agent/monitors/ (user)
30
+ * 3. pi-behavior-monitors examples (if installed as peer)
31
+ */
32
+ export declare function findMonitorSpec(monitorName: string, cwd: string): {
33
+ spec: MonitorSpec;
34
+ dir: string;
35
+ } | null;
36
+ export interface MonitorStepOptions {
37
+ cwd: string;
38
+ ctx: ExtensionContext;
39
+ signal?: AbortSignal;
40
+ runDir?: string;
41
+ outputPath?: string;
42
+ }
43
+ /**
44
+ * Execute a monitor step: discover monitor, render prompt, classify, return verdict.
45
+ *
46
+ * - CLEAN → status: "completed", output: { verdict: "clean" }
47
+ * - FLAG → status: "failed", output: { verdict: "flag", description }
48
+ * - NEW → status: "failed", output: { verdict: "new", pattern, description }
49
+ */
50
+ export declare function executeMonitor(monitorName: string, stepName: string, input: Record<string, unknown>, options: MonitorStepOptions): Promise<StepResult>;
51
+ /**
52
+ * List all discoverable monitor names for validation.
53
+ */
54
+ export declare function availableMonitors(cwd: string): string[];
55
+ export {};
56
+ //# sourceMappingURL=step-monitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-monitor.d.ts","sourceRoot":"","sources":["../src/step-monitor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAItE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,UAAU,WAAW;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE;QACT,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,QAAQ,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;KACb,CAAC;IACF,YAAY,CAAC,EAAE;QACd,IAAI,EAAE,MAAM,CAAC;KACb,CAAC;CACF;AAoBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAwC3G;AAoHD,MAAM,WAAW,kBAAkB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,gBAAgB,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CACnC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,EAAE,kBAAkB,GACzB,OAAO,CAAC,UAAU,CAAC,CAuHrB;AAYD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CA0BvD"}
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Monitor step executor — runs a monitor's classification as a workflow
3
+ * verification gate. Lightweight: reads monitor spec, loads patterns,
4
+ * renders prompt (Nunjucks or inline), calls LLM, parses verdict.
5
+ *
6
+ * Does NOT depend on pi-behavior-monitors — implements just enough to
7
+ * classify. No event handling, steering, ceiling, or escalation.
8
+ */
9
+ import * as fs from "node:fs";
10
+ import * as os from "node:os";
11
+ import * as path from "node:path";
12
+ import { complete } from "@mariozechner/pi-ai";
13
+ import nunjucks from "nunjucks";
14
+ import { persistStepOutput } from "./output.js";
15
+ import { zeroUsage } from "./step-shared.js";
16
+ // ── Discovery ────────────────────────────────────────────────────────────────
17
+ /**
18
+ * Find a monitor spec by name. Searches:
19
+ * 1. .pi/monitors/ (project)
20
+ * 2. ~/.pi/agent/monitors/ (user)
21
+ * 3. pi-behavior-monitors examples (if installed as peer)
22
+ */
23
+ export function findMonitorSpec(monitorName, cwd) {
24
+ const searchDirs = [
25
+ path.join(cwd, ".pi", "monitors"),
26
+ path.join(os.homedir(), ".pi", "agent", "monitors"),
27
+ ];
28
+ // Also check pi-behavior-monitors examples via node_modules
29
+ try {
30
+ const bmPath = require.resolve("@davidorex/pi-behavior-monitors/package.json", { paths: [cwd] });
31
+ searchDirs.push(path.join(path.dirname(bmPath), "examples"));
32
+ }
33
+ catch {
34
+ // pi-behavior-monitors not installed — skip
35
+ }
36
+ for (const dir of searchDirs) {
37
+ const filePath = path.join(dir, `${monitorName}.monitor.json`);
38
+ try {
39
+ const raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
40
+ if (raw.name && raw.classify && raw.patterns?.path) {
41
+ return {
42
+ spec: {
43
+ name: raw.name,
44
+ classify: {
45
+ model: raw.classify.model ?? "claude-sonnet-4-20250514",
46
+ context: Array.isArray(raw.classify.context) ? raw.classify.context : [],
47
+ prompt: raw.classify.prompt ?? "",
48
+ promptTemplate: typeof raw.classify.promptTemplate === "string" ? raw.classify.promptTemplate : undefined,
49
+ },
50
+ patterns: { path: raw.patterns.path },
51
+ instructions: raw.instructions?.path ? { path: raw.instructions.path } : undefined,
52
+ },
53
+ dir,
54
+ };
55
+ }
56
+ }
57
+ catch {
58
+ // file doesn't exist or is invalid — try next
59
+ }
60
+ }
61
+ return null;
62
+ }
63
+ // ── Pattern & instruction loading ────────────────────────────────────────────
64
+ function loadPatterns(dir, patternsPath) {
65
+ try {
66
+ const resolved = path.resolve(dir, patternsPath);
67
+ return JSON.parse(fs.readFileSync(resolved, "utf-8"));
68
+ }
69
+ catch {
70
+ return [];
71
+ }
72
+ }
73
+ function loadInstructions(dir, instructionsPath) {
74
+ if (!instructionsPath)
75
+ return [];
76
+ try {
77
+ const resolved = path.resolve(dir, instructionsPath);
78
+ return JSON.parse(fs.readFileSync(resolved, "utf-8"));
79
+ }
80
+ catch {
81
+ return [];
82
+ }
83
+ }
84
+ function formatPatterns(patterns) {
85
+ return patterns.map((p, i) => `${i + 1}. [${p.severity ?? "warning"}] ${p.description}`).join("\n");
86
+ }
87
+ function formatInstructions(instructions) {
88
+ if (instructions.length === 0)
89
+ return "";
90
+ const lines = instructions.map((i) => `- ${i.text}`).join("\n");
91
+ return `\nOperating instructions from the user (follow these strictly):\n${lines}\n`;
92
+ }
93
+ // ── Template rendering ───────────────────────────────────────────────────────
94
+ function createTemplateEnv(cwd) {
95
+ const searchPaths = [];
96
+ const projectDir = path.join(cwd, ".pi", "monitors");
97
+ const userDir = path.join(os.homedir(), ".pi", "agent", "monitors");
98
+ if (fs.existsSync(projectDir))
99
+ searchPaths.push(projectDir);
100
+ if (fs.existsSync(userDir))
101
+ searchPaths.push(userDir);
102
+ // pi-behavior-monitors examples
103
+ try {
104
+ const bmPath = require.resolve("@davidorex/pi-behavior-monitors/package.json", { paths: [cwd] });
105
+ const exDir = path.join(path.dirname(bmPath), "examples");
106
+ if (fs.existsSync(exDir))
107
+ searchPaths.push(exDir);
108
+ }
109
+ catch {
110
+ /* not installed */
111
+ }
112
+ const loader = searchPaths.length > 0 ? new nunjucks.FileSystemLoader(searchPaths) : undefined;
113
+ return new nunjucks.Environment(loader, { autoescape: false, throwOnUndefined: false });
114
+ }
115
+ function renderPrompt(spec, dir, input, templateEnv) {
116
+ const patterns = loadPatterns(dir, spec.patterns.path);
117
+ if (patterns.length === 0)
118
+ return null;
119
+ const instructions = loadInstructions(dir, spec.instructions?.path);
120
+ // Build context: patterns, instructions, plus any input fields
121
+ const context = {
122
+ patterns: formatPatterns(patterns),
123
+ instructions: formatInstructions(instructions),
124
+ iteration: 0,
125
+ ...input,
126
+ };
127
+ if (spec.classify.promptTemplate) {
128
+ try {
129
+ return templateEnv.render(spec.classify.promptTemplate, context);
130
+ }
131
+ catch {
132
+ // Fall through to inline prompt
133
+ if (!spec.classify.prompt)
134
+ return null;
135
+ }
136
+ }
137
+ if (!spec.classify.prompt)
138
+ return null;
139
+ return spec.classify.prompt.replace(/\{(\w+)\}/g, (match, key) => {
140
+ return String(context[key] ?? match);
141
+ });
142
+ }
143
+ // ── Verdict parsing ──────────────────────────────────────────────────────────
144
+ function parseVerdict(raw) {
145
+ const text = raw.trim();
146
+ if (text.startsWith("CLEAN"))
147
+ return { verdict: "clean" };
148
+ if (text.startsWith("NEW:")) {
149
+ const rest = text.slice(4);
150
+ const pipe = rest.indexOf("|");
151
+ if (pipe !== -1) {
152
+ return { verdict: "new", pattern: rest.slice(0, pipe).trim(), description: rest.slice(pipe + 1).trim() };
153
+ }
154
+ return { verdict: "new", pattern: rest.trim(), description: rest.trim() };
155
+ }
156
+ if (text.startsWith("FLAG:"))
157
+ return { verdict: "flag", description: text.slice(5).trim() };
158
+ return { verdict: "clean" };
159
+ }
160
+ function extractText(parts) {
161
+ return parts
162
+ .filter((b) => b.type === "text")
163
+ .map((b) => b.text)
164
+ .join("");
165
+ }
166
+ /**
167
+ * Execute a monitor step: discover monitor, render prompt, classify, return verdict.
168
+ *
169
+ * - CLEAN → status: "completed", output: { verdict: "clean" }
170
+ * - FLAG → status: "failed", output: { verdict: "flag", description }
171
+ * - NEW → status: "failed", output: { verdict: "new", pattern, description }
172
+ */
173
+ export async function executeMonitor(monitorName, stepName, input, options) {
174
+ const startTime = Date.now();
175
+ // 1. Discover monitor
176
+ const found = findMonitorSpec(monitorName, options.cwd);
177
+ if (!found) {
178
+ return {
179
+ step: stepName,
180
+ agent: `monitor:${monitorName}`,
181
+ status: "failed",
182
+ usage: zeroUsage(),
183
+ durationMs: Date.now() - startTime,
184
+ error: `Monitor '${monitorName}' not found`,
185
+ };
186
+ }
187
+ const { spec, dir } = found;
188
+ // 2. Render prompt
189
+ const templateEnv = createTemplateEnv(options.cwd);
190
+ const prompt = renderPrompt(spec, dir, input, templateEnv);
191
+ if (!prompt) {
192
+ return {
193
+ step: stepName,
194
+ agent: `monitor:${monitorName}`,
195
+ status: "completed",
196
+ output: { verdict: "clean", note: "No patterns loaded — nothing to classify" },
197
+ textOutput: "CLEAN (no patterns)",
198
+ usage: zeroUsage(),
199
+ durationMs: Date.now() - startTime,
200
+ };
201
+ }
202
+ // 3. Resolve model
203
+ const { provider, modelId } = parseModelSpec(spec.classify.model);
204
+ const model = options.ctx.modelRegistry.find(provider, modelId);
205
+ if (!model) {
206
+ return {
207
+ step: stepName,
208
+ agent: `monitor:${monitorName}`,
209
+ status: "failed",
210
+ usage: zeroUsage(),
211
+ durationMs: Date.now() - startTime,
212
+ error: `Model '${spec.classify.model}' not found in registry`,
213
+ };
214
+ }
215
+ const apiKey = await options.ctx.modelRegistry.getApiKey(model);
216
+ if (!apiKey) {
217
+ return {
218
+ step: stepName,
219
+ agent: `monitor:${monitorName}`,
220
+ status: "failed",
221
+ usage: zeroUsage(),
222
+ durationMs: Date.now() - startTime,
223
+ error: `No API key for model '${spec.classify.model}'`,
224
+ };
225
+ }
226
+ // 4. Classify
227
+ let response;
228
+ try {
229
+ response = await complete(model, { messages: [{ role: "user", content: [{ type: "text", text: prompt }], timestamp: Date.now() }] }, { apiKey, maxTokens: 150, signal: options.signal });
230
+ }
231
+ catch (err) {
232
+ if (options.signal?.aborted) {
233
+ return {
234
+ step: stepName,
235
+ agent: `monitor:${monitorName}`,
236
+ status: "failed",
237
+ usage: zeroUsage(),
238
+ durationMs: Date.now() - startTime,
239
+ error: "Monitor classification cancelled",
240
+ };
241
+ }
242
+ const msg = err instanceof Error ? err.message : String(err);
243
+ return {
244
+ step: stepName,
245
+ agent: `monitor:${monitorName}`,
246
+ status: "failed",
247
+ usage: zeroUsage(),
248
+ durationMs: Date.now() - startTime,
249
+ error: `Classification failed: ${msg}`,
250
+ };
251
+ }
252
+ // 5. Parse verdict
253
+ const rawText = extractText(response.content);
254
+ const verdict = parseVerdict(rawText);
255
+ const durationMs = Date.now() - startTime;
256
+ // Estimate usage from response
257
+ const usage = zeroUsage();
258
+ // Token counts not directly available from complete() — leave as zero
259
+ const output = {
260
+ verdict: verdict.verdict,
261
+ ...(verdict.description ? { description: verdict.description } : {}),
262
+ ...(verdict.pattern ? { pattern: verdict.pattern } : {}),
263
+ };
264
+ const result = {
265
+ step: stepName,
266
+ agent: `monitor:${monitorName}`,
267
+ status: verdict.verdict === "clean" ? "completed" : "failed",
268
+ output,
269
+ textOutput: rawText.trim(),
270
+ usage,
271
+ durationMs,
272
+ ...(verdict.verdict !== "clean" ? { error: verdict.description ?? "Monitor flagged an issue" } : {}),
273
+ };
274
+ if (options.runDir) {
275
+ result.outputPath = persistStepOutput(options.runDir, stepName, output, rawText.trim(), options.outputPath);
276
+ }
277
+ return result;
278
+ }
279
+ // ── Utility ──────────────────────────────────────────────────────────────────
280
+ function parseModelSpec(spec) {
281
+ const slashIndex = spec.indexOf("/");
282
+ if (slashIndex !== -1) {
283
+ return { provider: spec.slice(0, slashIndex), modelId: spec.slice(slashIndex + 1) };
284
+ }
285
+ return { provider: "anthropic", modelId: spec };
286
+ }
287
+ /**
288
+ * List all discoverable monitor names for validation.
289
+ */
290
+ export function availableMonitors(cwd) {
291
+ const searchDirs = [
292
+ path.join(cwd, ".pi", "monitors"),
293
+ path.join(os.homedir(), ".pi", "agent", "monitors"),
294
+ ];
295
+ try {
296
+ const bmPath = require.resolve("@davidorex/pi-behavior-monitors/package.json", { paths: [cwd] });
297
+ searchDirs.push(path.join(path.dirname(bmPath), "examples"));
298
+ }
299
+ catch {
300
+ /* not installed */
301
+ }
302
+ const seen = new Set();
303
+ for (const dir of searchDirs) {
304
+ try {
305
+ for (const file of fs.readdirSync(dir)) {
306
+ if (file.endsWith(".monitor.json")) {
307
+ seen.add(file.replace(".monitor.json", ""));
308
+ }
309
+ }
310
+ }
311
+ catch {
312
+ /* dir doesn't exist */
313
+ }
314
+ }
315
+ return Array.from(seen).sort();
316
+ }
317
+ //# sourceMappingURL=step-monitor.js.map