pi-loopflows 0.1.0 → 0.1.1
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 +8 -0
- package/README.md +181 -34
- package/loopflows/build-review.loopflow.json +48 -0
- package/loopflows/plan-review.loopflow.json +48 -0
- package/package.json +6 -4
- package/scripts/validate.mjs +71 -0
- package/skills/loopflows/SKILL.md +12 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.1 - 2026-06-22
|
|
4
|
+
|
|
5
|
+
- Polish product README and usage guidance for public release.
|
|
6
|
+
- Add bundled `build-review` loopflow for lightweight implementation feedback loops.
|
|
7
|
+
- Add bundled `plan-review` loopflow for plan quality gates before implementation.
|
|
8
|
+
- Expand loopflows skill instructions.
|
|
9
|
+
- Clarify adapter/backend direction for future compatible executors.
|
|
10
|
+
|
|
3
11
|
## 0.1.0 - 2026-06-22
|
|
4
12
|
|
|
5
13
|
- Initial release.
|
package/README.md
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
# pi-loopflows
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Build deterministic AI workflows out of Pi subagents.
|
|
4
4
|
|
|
5
|
-
A **loopflow**
|
|
5
|
+
A **loopflow** is a reusable process for agent work: steps, gates, feedback loops, stop rules, and evidence artifacts. Instead of asking one agent to “do the whole thing”, you describe how specialist agents should cooperate: one agent gathers context, another plans, another builds, another reviews, and a gate decides whether the work moves forward, loops back for fixes, or stops.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`pi-loopflows` ships with a production-ready **Launch Control** loopflow out of the box, and it is also a flexible constructor for your own workflows.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Why loopflows
|
|
10
|
+
|
|
11
|
+
Linear chains are useful, but real work is not always linear. A reviewer can request changes. A validator can reject missing evidence. A planner can reveal that the task is blocked. A builder may need several focused passes before the result is safe to accept.
|
|
12
|
+
|
|
13
|
+
Loopflows make that control flow explicit:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
step → step → gate
|
|
17
|
+
↓
|
|
18
|
+
approved → continue
|
|
19
|
+
changes_requested → loop back
|
|
20
|
+
blocked → stop
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The philosophy is simple: AI should work through a process, not just produce a confident answer. A loopflow defines who does the work, who checks it, what counts as success, how many attempts are allowed, where evidence is saved, and when the run must stop instead of guessing.
|
|
10
24
|
|
|
11
25
|
## Install
|
|
12
26
|
|
|
@@ -14,19 +28,19 @@ Normal chains are linear. Real work is not. A reviewer may request changes, a bu
|
|
|
14
28
|
pi install npm:pi-loopflows
|
|
15
29
|
```
|
|
16
30
|
|
|
17
|
-
|
|
31
|
+
`pi-loopflows` uses Pi subagent definitions as its first backend. Install `pi-subagents` if you have not already:
|
|
18
32
|
|
|
19
33
|
```bash
|
|
20
|
-
pi install
|
|
34
|
+
pi install npm:pi-subagents
|
|
21
35
|
```
|
|
22
36
|
|
|
23
|
-
|
|
37
|
+
Then reload Pi:
|
|
24
38
|
|
|
25
39
|
```text
|
|
26
40
|
/reload
|
|
27
41
|
```
|
|
28
42
|
|
|
29
|
-
## What
|
|
43
|
+
## What you get
|
|
30
44
|
|
|
31
45
|
### Tool
|
|
32
46
|
|
|
@@ -45,9 +59,15 @@ loopflow_run({
|
|
|
45
59
|
/loopflow launch-control -- Implement this approved backend plan
|
|
46
60
|
```
|
|
47
61
|
|
|
48
|
-
###
|
|
62
|
+
### Built-in loopflows
|
|
63
|
+
|
|
64
|
+
- `launch-control` — plan-as-contract implementation loop with builder/reviewer feedback and final audit.
|
|
65
|
+
- `build-review` — small generic build → review → fix loop for scoped implementation tasks.
|
|
66
|
+
- `plan-review` — planning loop that lets a reviewer reject vague or unsafe plans before implementation.
|
|
67
|
+
|
|
68
|
+
## Built-in: Launch Control
|
|
49
69
|
|
|
50
|
-
|
|
70
|
+
Launch Control is bundled because it is the clearest example of why loopflows exist. It turns a plan into a controlled implementation process:
|
|
51
71
|
|
|
52
72
|
```text
|
|
53
73
|
context-builder
|
|
@@ -61,19 +81,59 @@ context-builder
|
|
|
61
81
|
→ final audit
|
|
62
82
|
```
|
|
63
83
|
|
|
84
|
+
Use it when drift, skipped validation, PR correctness, or multi-step delivery risk matter.
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
/loopflow launch-control -- Implement the auth migration plan in docs/auth-plan.md
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Launch Control is not hard-coded. It is just a `.loopflow.json` file. You can copy it, change the agents, change the prompts, adjust max iterations, add stricter gates, or create a project-specific version in `.pi/loopflows/`.
|
|
93
|
+
|
|
94
|
+
## Loopflows as a constructor
|
|
95
|
+
|
|
96
|
+
Think of loopflows as LEGO for agent processes. A loopflow can use any available Pi subagent role:
|
|
97
|
+
|
|
98
|
+
- `context-builder`
|
|
99
|
+
- `scout`
|
|
100
|
+
- `researcher`
|
|
101
|
+
- `planner`
|
|
102
|
+
- `worker`
|
|
103
|
+
- `reviewer`
|
|
104
|
+
- `oracle`
|
|
105
|
+
- your own custom agents
|
|
106
|
+
|
|
107
|
+
You decide:
|
|
108
|
+
|
|
109
|
+
- which agent runs first;
|
|
110
|
+
- what each agent receives;
|
|
111
|
+
- which output is saved;
|
|
112
|
+
- which step is a gate;
|
|
113
|
+
- what statuses mean pass, retry, or stop;
|
|
114
|
+
- how many loop iterations are allowed;
|
|
115
|
+
- where artifacts go;
|
|
116
|
+
- what final audit should prove.
|
|
117
|
+
|
|
118
|
+
Today, the backend runs Pi-compatible subagents. The engine is intentionally built behind an adapter boundary, so future versions can add other compatible backends — Codex CLI, OpenCode, ACP workers, remote agents, or custom executors — without changing the loopflow concept.
|
|
119
|
+
|
|
64
120
|
## Loopflow files
|
|
65
121
|
|
|
66
|
-
Loopflows are JSON files named
|
|
122
|
+
Loopflows are JSON files named:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
*.loopflow.json
|
|
126
|
+
```
|
|
67
127
|
|
|
68
128
|
Discovery locations:
|
|
69
129
|
|
|
70
|
-
- bundled package
|
|
71
|
-
- user: `~/.pi/agent/loopflows
|
|
72
|
-
- project: `.pi/loopflows
|
|
130
|
+
- bundled package loopflows;
|
|
131
|
+
- user loopflows: `~/.pi/agent/loopflows/`;
|
|
132
|
+
- project loopflows: `.pi/loopflows/`.
|
|
73
133
|
|
|
74
|
-
Project loopflows
|
|
134
|
+
Project loopflows are the easiest way to customize behavior for one repo.
|
|
75
135
|
|
|
76
|
-
## Minimal
|
|
136
|
+
## Minimal example
|
|
77
137
|
|
|
78
138
|
```json
|
|
79
139
|
{
|
|
@@ -114,14 +174,39 @@ Project loopflows can override or add workflows for a repo.
|
|
|
114
174
|
|
|
115
175
|
## Template variables
|
|
116
176
|
|
|
117
|
-
- `{task}` — original user task
|
|
118
|
-
- `{previous}` — previous step output
|
|
119
|
-
- `{outputs.stepId}` — output from a named step
|
|
120
|
-
- `{outputs.stepId.
|
|
121
|
-
- `{outputs.stepId.
|
|
122
|
-
- `{
|
|
123
|
-
- `{
|
|
124
|
-
- `{
|
|
177
|
+
- `{task}` — original user task.
|
|
178
|
+
- `{previous}` — previous step output.
|
|
179
|
+
- `{outputs.stepId}` — output from a named step.
|
|
180
|
+
- `{outputs.stepId.output}` — same as above, explicit form.
|
|
181
|
+
- `{outputs.stepId.status}` — parsed gate status.
|
|
182
|
+
- `{outputs.stepId.json}` — parsed gate JSON.
|
|
183
|
+
- `{loop.iteration}` — current loop iteration.
|
|
184
|
+
- `{artifactsDir}` — current run artifact directory.
|
|
185
|
+
- `{params.name}` — runtime params passed to `loopflow_run`.
|
|
186
|
+
|
|
187
|
+
## Gate contract
|
|
188
|
+
|
|
189
|
+
Gate steps should return JSON. A typical reviewer gate returns:
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"status": "approved",
|
|
194
|
+
"summary": "The implementation satisfies the plan.",
|
|
195
|
+
"findings": [],
|
|
196
|
+
"validation_gaps": [],
|
|
197
|
+
"requires_user_decision": false
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Common statuses:
|
|
202
|
+
|
|
203
|
+
- `approved` — move forward.
|
|
204
|
+
- `changes_requested` — loop back for another pass.
|
|
205
|
+
- `blocked` — stop; user or environment action is required.
|
|
206
|
+
- `complete` — final audit passed.
|
|
207
|
+
- `incomplete` — final audit failed.
|
|
208
|
+
|
|
209
|
+
Each loopflow decides which statuses pass, retry, or stop.
|
|
125
210
|
|
|
126
211
|
## Artifacts
|
|
127
212
|
|
|
@@ -131,7 +216,7 @@ Every run writes evidence to:
|
|
|
131
216
|
<cwd>/.pi/loopflows/runs/<timestamp>-<workflow>/
|
|
132
217
|
```
|
|
133
218
|
|
|
134
|
-
Typical
|
|
219
|
+
Typical artifacts:
|
|
135
220
|
|
|
136
221
|
```text
|
|
137
222
|
task.md
|
|
@@ -144,30 +229,92 @@ final-audit.json
|
|
|
144
229
|
summary.md
|
|
145
230
|
```
|
|
146
231
|
|
|
232
|
+
This makes loopflows inspectable. You can see what each agent claimed, what the gate decided, and why the run stopped or passed.
|
|
233
|
+
|
|
234
|
+
## Customization patterns
|
|
235
|
+
|
|
236
|
+
### Make Launch Control stricter
|
|
237
|
+
|
|
238
|
+
Copy the bundled file:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
mkdir -p .pi/loopflows
|
|
242
|
+
cp ~/.pi/agent/npm/node_modules/pi-loopflows/loopflows/launch-control.loopflow.json \
|
|
243
|
+
.pi/loopflows/launch-control.loopflow.json
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Then edit the project copy. Common changes:
|
|
247
|
+
|
|
248
|
+
- increase `maxIterations`;
|
|
249
|
+
- change `reviewer` to a custom security reviewer;
|
|
250
|
+
- add stricter validation language;
|
|
251
|
+
- add a docs or migration audit step;
|
|
252
|
+
- change stop statuses;
|
|
253
|
+
- make the final audit require `complete` only.
|
|
254
|
+
|
|
255
|
+
### Create a lightweight workflow
|
|
256
|
+
|
|
257
|
+
Use `build-review` for small implementation tasks where full Launch Control is too formal.
|
|
258
|
+
|
|
259
|
+
```text
|
|
260
|
+
/loopflow build-review -- Add validation to the import endpoint
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Review a plan before coding
|
|
264
|
+
|
|
265
|
+
Use `plan-review` when you want a plan to be checked before a worker touches files.
|
|
266
|
+
|
|
267
|
+
```text
|
|
268
|
+
/loopflow plan-review -- Plan the database migration for workspace roles
|
|
269
|
+
```
|
|
270
|
+
|
|
147
271
|
## Backend design
|
|
148
272
|
|
|
149
|
-
The
|
|
273
|
+
The runtime uses an executor adapter boundary:
|
|
150
274
|
|
|
151
275
|
```ts
|
|
152
276
|
runAgent(agent, task, options) -> StepResult
|
|
153
277
|
```
|
|
154
278
|
|
|
155
|
-
Current backend:
|
|
279
|
+
Current backend:
|
|
156
280
|
|
|
157
|
-
|
|
281
|
+
- Pi subprocess agents compatible with `pi-subagents` agent definitions.
|
|
158
282
|
|
|
159
|
-
|
|
283
|
+
Future-compatible backend ideas:
|
|
160
284
|
|
|
161
|
-
-
|
|
162
|
-
-
|
|
285
|
+
- Codex CLI workers;
|
|
286
|
+
- OpenCode workers;
|
|
287
|
+
- ACP-compatible agents;
|
|
288
|
+
- remote worker pools;
|
|
289
|
+
- project-specific executors.
|
|
163
290
|
|
|
164
|
-
|
|
165
|
-
|
|
291
|
+
The point is that loopflows describe the process. The backend decides how each agent is actually executed.
|
|
292
|
+
|
|
293
|
+
## When to use loopflows vs chains
|
|
294
|
+
|
|
295
|
+
Use normal Pi subagent chains when the process is linear:
|
|
296
|
+
|
|
297
|
+
```text
|
|
298
|
+
scout → planner → worker
|
|
166
299
|
```
|
|
167
300
|
|
|
301
|
+
Use loopflows when a step can send work backward or stop the process:
|
|
302
|
+
|
|
303
|
+
```text
|
|
304
|
+
worker → reviewer → worker → reviewer
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
If you need a feedback loop, a quality gate, a max iteration limit, or saved evidence, use a loopflow.
|
|
308
|
+
|
|
168
309
|
## Status
|
|
169
310
|
|
|
170
|
-
|
|
311
|
+
`pi-loopflows` is early, but designed as a real product surface rather than a one-off script. The core model is intentionally small:
|
|
312
|
+
|
|
313
|
+
```text
|
|
314
|
+
steps + loops + gates + artifacts + adapters
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
That is enough to build useful workflows without turning the extension into a giant orchestration platform.
|
|
171
318
|
|
|
172
319
|
## License
|
|
173
320
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "build-review",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Generic scoped implementation loop: plan, build, independent review, repeat fixes until approved or blocked.",
|
|
5
|
+
"backend": "pi-subprocess",
|
|
6
|
+
"defaults": {
|
|
7
|
+
"agentScope": "both"
|
|
8
|
+
},
|
|
9
|
+
"steps": [
|
|
10
|
+
{
|
|
11
|
+
"id": "plan",
|
|
12
|
+
"agent": "planner",
|
|
13
|
+
"output": "plan.md",
|
|
14
|
+
"task": "Create a concise implementation plan for this task:\n\n{task}\n\nInclude acceptance criteria, likely files, validation commands, non-goals, and stop conditions. Do not edit files."
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"loop": {
|
|
18
|
+
"id": "build-review",
|
|
19
|
+
"maxIterations": 3,
|
|
20
|
+
"gateStep": "review",
|
|
21
|
+
"passStatuses": ["approved"],
|
|
22
|
+
"retryStatuses": ["changes_requested"],
|
|
23
|
+
"stopStatuses": ["blocked"],
|
|
24
|
+
"onExhausted": "stop",
|
|
25
|
+
"body": [
|
|
26
|
+
{
|
|
27
|
+
"id": "build",
|
|
28
|
+
"agent": "worker",
|
|
29
|
+
"output": "build-{loop.iteration}.md",
|
|
30
|
+
"task": "Implement or fix iteration {loop.iteration}.\n\nTask:\n{task}\n\nPlan:\n{outputs.plan}\n\nPrevious review, if any:\n{outputs.review.output}\n\nIf this is iteration 1, implement the planned change. If this is a later iteration, apply only required reviewer fixes. Use the real project stack. Do not add mocks, demos, fake validation, or hidden fallbacks unless explicitly requested. Run focused validation and report changed files, commands with exit codes, evidence, blockers, and remaining risks."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "review",
|
|
34
|
+
"agent": "reviewer",
|
|
35
|
+
"output": "review-{loop.iteration}.json",
|
|
36
|
+
"gate": {
|
|
37
|
+
"type": "json-status",
|
|
38
|
+
"passStatuses": ["approved"],
|
|
39
|
+
"retryStatuses": ["changes_requested"],
|
|
40
|
+
"stopStatuses": ["blocked"]
|
|
41
|
+
},
|
|
42
|
+
"task": "Review iteration {loop.iteration}. Do not edit project/source files.\n\nTask:\n{task}\n\nPlan:\n{outputs.plan}\n\nBuilder report:\n{outputs.build.output}\n\nReturn ONLY valid JSON:\n{\n \"status\": \"approved\" | \"changes_requested\" | \"blocked\",\n \"summary\": \"short verdict\",\n \"findings\": [{\"severity\": \"blocker|required|optional\", \"file\": \"path or null\", \"issue\": \"specific issue\", \"required_fix\": \"specific fix or null\"}],\n \"validation_gaps\": [\"gap\"],\n \"requires_user_decision\": false\n}\n\nApprove only if the implementation and evidence satisfy the plan. Request changes for required fixable issues. Block for missing credentials, missing decisions, unsafe scope, or stale plan."
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "plan-review",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Planning loop: gather context, draft a plan, review the plan, and revise until approved or blocked before implementation.",
|
|
5
|
+
"backend": "pi-subprocess",
|
|
6
|
+
"defaults": {
|
|
7
|
+
"agentScope": "both"
|
|
8
|
+
},
|
|
9
|
+
"steps": [
|
|
10
|
+
{
|
|
11
|
+
"id": "context",
|
|
12
|
+
"agent": "context-builder",
|
|
13
|
+
"output": "context.md",
|
|
14
|
+
"task": "Build planning context for this task:\n\n{task}\n\nInspect the repository enough to identify relevant files, constraints, validation commands, risks, and unknowns. Do not edit files."
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"loop": {
|
|
18
|
+
"id": "plan-review",
|
|
19
|
+
"maxIterations": 2,
|
|
20
|
+
"gateStep": "review",
|
|
21
|
+
"passStatuses": ["approved"],
|
|
22
|
+
"retryStatuses": ["changes_requested"],
|
|
23
|
+
"stopStatuses": ["blocked"],
|
|
24
|
+
"onExhausted": "stop",
|
|
25
|
+
"body": [
|
|
26
|
+
{
|
|
27
|
+
"id": "plan",
|
|
28
|
+
"agent": "planner",
|
|
29
|
+
"output": "plan-{loop.iteration}.md",
|
|
30
|
+
"task": "Draft or revise implementation plan iteration {loop.iteration}.\n\nTask:\n{task}\n\nContext:\n{outputs.context}\n\nPrevious review, if any:\n{outputs.review.output}\n\nReturn a practical implementation plan with scope, non-goals, acceptance criteria, likely files, validation commands, risks, and open questions. Do not edit files."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "review",
|
|
34
|
+
"agent": "reviewer",
|
|
35
|
+
"output": "plan-review-{loop.iteration}.json",
|
|
36
|
+
"gate": {
|
|
37
|
+
"type": "json-status",
|
|
38
|
+
"passStatuses": ["approved"],
|
|
39
|
+
"retryStatuses": ["changes_requested"],
|
|
40
|
+
"stopStatuses": ["blocked"]
|
|
41
|
+
},
|
|
42
|
+
"task": "Review this implementation plan before any code is written. Do not edit files.\n\nTask:\n{task}\n\nContext:\n{outputs.context}\n\nPlan:\n{outputs.plan}\n\nReturn ONLY valid JSON:\n{\n \"status\": \"approved\" | \"changes_requested\" | \"blocked\",\n \"summary\": \"short verdict\",\n \"findings\": [{\"severity\": \"blocker|required|optional\", \"issue\": \"specific issue\", \"required_fix\": \"specific plan change or null\"}],\n \"missing_acceptance_criteria\": [\"criterion\"],\n \"requires_user_decision\": false\n}\n\nApprove only if the plan is specific, scoped, testable, and safe to hand to a worker. Request changes for vague scope, missing validation, or unclear acceptance criteria. Block when user/product decisions or credentials are required."
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-loopflows",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Deterministic loop workflows for Pi subagents: steps, gates, feedback loops, and artifacts.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Nikita Nosov <20nik.nosov21@gmail.com>",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"extensions",
|
|
27
27
|
"loopflows",
|
|
28
28
|
"skills",
|
|
29
|
+
"scripts",
|
|
29
30
|
"README.md",
|
|
30
31
|
"LICENSE",
|
|
31
32
|
"CHANGELOG.md"
|
|
@@ -36,8 +37,7 @@
|
|
|
36
37
|
],
|
|
37
38
|
"skills": [
|
|
38
39
|
"./skills"
|
|
39
|
-
]
|
|
40
|
-
"image": "https://raw.githubusercontent.com/nik1t7n/pi-loopflows/main/assets/pi-loopflows.png"
|
|
40
|
+
]
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@earendil-works/pi-coding-agent": "*",
|
|
@@ -51,6 +51,8 @@
|
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"typecheck": "tsc --noEmit",
|
|
54
|
-
"pack:check": "npm pack --dry-run"
|
|
54
|
+
"pack:check": "npm pack --dry-run",
|
|
55
|
+
"validate": "node scripts/validate.mjs",
|
|
56
|
+
"prepublishOnly": "npm run validate && npm run typecheck && npm run pack:check"
|
|
55
57
|
}
|
|
56
58
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const root = path.resolve(new URL('..', import.meta.url).pathname);
|
|
5
|
+
const loopflowsDir = path.join(root, 'loopflows');
|
|
6
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
7
|
+
|
|
8
|
+
const errors = [];
|
|
9
|
+
|
|
10
|
+
function assert(condition, message) {
|
|
11
|
+
if (!condition) errors.push(message);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
assert(pkg.name === 'pi-loopflows', 'package name must be pi-loopflows');
|
|
15
|
+
assert(pkg.keywords?.includes('pi-package'), 'package must include pi-package keyword');
|
|
16
|
+
assert(pkg.pi?.extensions?.includes('./extensions'), 'package pi manifest must expose extensions');
|
|
17
|
+
assert(pkg.pi?.skills?.includes('./skills'), 'package pi manifest must expose skills');
|
|
18
|
+
|
|
19
|
+
const files = fs.readdirSync(loopflowsDir).filter((file) => file.endsWith('.loopflow.json'));
|
|
20
|
+
assert(files.length >= 3, 'expected bundled launch-control, build-review, and plan-review loopflows');
|
|
21
|
+
|
|
22
|
+
for (const file of files) {
|
|
23
|
+
const full = path.join(loopflowsDir, file);
|
|
24
|
+
let wf;
|
|
25
|
+
try {
|
|
26
|
+
wf = JSON.parse(fs.readFileSync(full, 'utf8'));
|
|
27
|
+
} catch (error) {
|
|
28
|
+
errors.push(`${file}: invalid JSON: ${error.message}`);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
assert(typeof wf.name === 'string' && wf.name.length > 0, `${file}: missing name`);
|
|
33
|
+
assert(typeof wf.description === 'string' && wf.description.length > 0, `${file}: missing description`);
|
|
34
|
+
assert(Array.isArray(wf.steps) && wf.steps.length > 0, `${file}: steps must be a non-empty array`);
|
|
35
|
+
|
|
36
|
+
const topLevelIds = new Set();
|
|
37
|
+
for (const [index, node] of wf.steps.entries()) {
|
|
38
|
+
if (node.loop) {
|
|
39
|
+
const loop = node.loop;
|
|
40
|
+
assert(typeof loop.id === 'string' && loop.id.length > 0, `${file}: loop ${index} missing id`);
|
|
41
|
+
assert(Number.isInteger(loop.maxIterations) && loop.maxIterations > 0, `${file}: loop ${loop.id} maxIterations must be positive integer`);
|
|
42
|
+
assert(Array.isArray(loop.body) && loop.body.length > 0, `${file}: loop ${loop.id} body must be non-empty`);
|
|
43
|
+
assert(typeof loop.gateStep === 'string' && loop.gateStep.length > 0, `${file}: loop ${loop.id} missing gateStep`);
|
|
44
|
+
const bodyIds = new Set(loop.body.map((step) => step.id));
|
|
45
|
+
assert(bodyIds.has(loop.gateStep), `${file}: loop ${loop.id} gateStep not present in body`);
|
|
46
|
+
for (const step of loop.body) validateStep(file, step, `loop ${loop.id}`);
|
|
47
|
+
} else {
|
|
48
|
+
validateStep(file, node, `step ${index}`);
|
|
49
|
+
if (node.id) {
|
|
50
|
+
assert(!topLevelIds.has(node.id), `${file}: duplicate top-level step id ${node.id}`);
|
|
51
|
+
topLevelIds.add(node.id);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function validateStep(file, step, label) {
|
|
58
|
+
assert(typeof step.id === 'string' && step.id.length > 0, `${file}: ${label} missing id`);
|
|
59
|
+
assert(typeof step.agent === 'string' && step.agent.length > 0, `${file}: ${label} missing agent`);
|
|
60
|
+
assert(typeof step.task === 'string' && step.task.length > 0, `${file}: ${label} missing task`);
|
|
61
|
+
if (step.gate) {
|
|
62
|
+
assert(step.task.includes('JSON') || step.task.includes('json'), `${file}: gate step ${step.id} should explicitly request JSON`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (errors.length) {
|
|
67
|
+
console.error(errors.map((e) => `- ${e}`).join('\n'));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(`Validated ${files.length} loopflows for ${pkg.name}@${pkg.version}`);
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: loopflows
|
|
3
|
-
description: Use when the user wants deterministic multi-agent workflows, feedback loops between builder/reviewer agents, launch-control style implementation gates,
|
|
3
|
+
description: Use when the user wants deterministic multi-agent workflows, feedback loops between builder/reviewer agents, launch-control style implementation gates, repeatable Pi subagent processes, or custom workflow construction with max iterations, gates, stop conditions, and artifacts.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Loopflows
|
|
7
7
|
|
|
8
|
-
Use `loopflow_run`
|
|
8
|
+
Use `loopflow_run` when a task needs process control, not just a linear chain.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
A loopflow is a reusable workflow made from subagent steps, gates, and loops. Use it when work should be checked, sent back for fixes, capped by max iterations, or stopped when evidence is missing.
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
12
|
+
## Built-in loopflows
|
|
13
|
+
|
|
14
|
+
- `launch-control` — strict plan-as-contract flow: context → plan → build/review loop → final audit.
|
|
15
|
+
- `build-review` — lightweight implementation loop for scoped changes.
|
|
16
|
+
- `plan-review` — review and revise a plan before implementation starts.
|
|
17
17
|
|
|
18
18
|
## Commands
|
|
19
19
|
|
|
@@ -38,3 +38,7 @@ loopflow_run({ workflow: "launch-control", task: "...", maxIterations: 3 })
|
|
|
38
38
|
## Rule of thumb
|
|
39
39
|
|
|
40
40
|
Use `pi-subagents` chains for simple linear handoffs. Use `pi-loopflows` when a gate can send work backward for fixes or stop the run.
|
|
41
|
+
|
|
42
|
+
## Customization
|
|
43
|
+
|
|
44
|
+
Loopflows are `.loopflow.json` files. Copy bundled workflows into `.pi/loopflows/` to customize them for a project: change agents, prompts, max iterations, pass/retry/stop statuses, or final audit rules.
|