opencode-plugin-flow 4.0.1 → 4.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/dist/index.js CHANGED
@@ -1,39 +1,181 @@
1
- import{createHash as St}from"node:crypto";import{mkdir as _t,readdir as nn,readFile as It,rm as rn,writeFile as M}from"node:fs/promises";import{createRequire as Ct}from"node:module";import{dirname as At,join as I,normalize as Pt,sep as Tt}from"node:path";var ae=`# Parallel orchestration
1
+ import{createHash as Pt}from"node:crypto";import{mkdir as Tt,readdir as fn,readFile as Et,rm as mn,writeFile as M}from"node:fs/promises";import{createRequire as Ut}from"node:module";import{dirname as jt,join as I,normalize as Ot,sep as qt}from"node:path";var ae=`# Flow worker handoff contract
2
2
 
3
- Use fan-out for every worker category whose output can be merged as evidence,
4
- review, validation support, audit findings, or an isolated candidate patch. Flow
5
- execution still has one active feature, and the manager owns every
6
- state-changing Flow tool call.
3
+ Flow managers merge only the worker's final response. Treat that response as the
4
+ worker report of record: it must include the assigned scope, what was actually
5
+ covered, the evidence for each useful claim, and the remaining gaps. End worker
6
+ prompts with "Return only this Flow handoff."
7
+
8
+ ## Evidence, review, validation, or audit worker report
9
+
10
+ Use this for \`flow-evidence-worker\`, \`flow-reviewer\`,
11
+ \`flow-validation-worker\`, and \`flow-audit-worker\`.
12
+
13
+ \`\`\`markdown
14
+ ## Status
15
+ success | partial | blocked
16
+
17
+ ## Scope
18
+ <owned slice: path set, module, command, risk lens, route, data range, or question set>
19
+
20
+ ## Coverage
21
+ - Expected: <files, ranges, questions, commands, or findings assigned>
22
+ - Checked: <actual coverage, for example "12/12 files" or "command not run">
23
+ - Not checked: <items skipped with reason, or "none">
24
+
25
+ ## Findings or facts
26
+ - [high|med|low] <claim>; evidence: <file:line | command summary | screenshot path | URL | metric>; corroboration: <N sources or "single source">
27
+ - [high|med|low] <claim>; evidence: <...>; corroboration: <...>
28
+
29
+ ## Sources
30
+ - <paths read, commands run, docs fetched, data ranges covered, screenshots inspected>
31
+
32
+ ## Confidence and verification
33
+ - Verified: <claims directly re-run, recounted, traced, or cross-checked>
34
+ - Single-source: <claims with exactly one supporting source>
35
+ - Inferred: <claims derived from surrounding evidence rather than directly observed>
36
+ - Unsettled: <claims, sources, or citations that could not be resolved>
37
+ - Falsifier or missing input: <what would overturn or materially change the result>
38
+
39
+ ## Open questions / gaps
40
+ - <ambiguity, missing source, contradiction, skipped item, or out-of-scope dependency>
41
+
42
+ ## Manager follow-ups
43
+ - <concrete next tasks, verifier claims, validation commands, or Flow plan targets>
44
+ \`\`\`
45
+
46
+ Validation workers must include exact command names and raw outcome summaries
47
+ for commands they actually ran. Audit workers must include guards checked for
48
+ any blocking-severity candidate. Review workers must separate blocking findings
49
+ from advisory notes.
50
+
51
+ ## Verifier worker report
52
+
53
+ Use this for \`flow-verifier-worker\`. Give it atomic claims and the cited sources
54
+ or commands. Do not include the generator's reasoning unless that reasoning is
55
+ the thing being verified.
56
+
57
+ \`\`\`markdown
58
+ ## Status
59
+ success | partial | blocked
60
+
61
+ ## Scope
62
+ <claim ids, sources or commands checked, and the acceptance question>
63
+
64
+ ## Verdict per claim
65
+ - <claim id>: verdict=<supported | partly-supported | unsupported | source-not-found>
66
+ - claim: <claim text>
67
+ - evidence: <supporting snippet, path plus line, measured value, command result, or "none">
68
+ - source resolution: <URL, path, or command plus whether it resolved>
69
+ - confidence level: high | med | low
70
+ - recommended action: <keep, narrow, rewrite, or remove>
71
+
72
+ ## Overall
73
+ <accept | revise | reject> because <brief reason>
74
+
75
+ ## Gaps
76
+ - <unavailable source, ambiguous claim wording, missing oracle, or check not run>
77
+
78
+ ## Manager follow-ups
79
+ - <narrow recheck, plan adjustment, review finding, or none>
80
+ \`\`\`
81
+
82
+ ## Candidate implementation worker report
83
+
84
+ Use this only for isolated worktrees or exact non-overlapping path ownership
85
+ authorized by the manager.
86
+
87
+ \`\`\`markdown
88
+ ## Status
89
+ success | partial | blocked
90
+
91
+ ## Scope
92
+ <isolated worktree or exact path-owned slice>
93
+
94
+ ## Changed or proposed patch
95
+ - <path>: <what changed and why>
96
+
97
+ ## Coverage
98
+ - Assigned: <owned files/modules>
99
+ - Touched: <files changed or proposed>
100
+ - Skipped: <anything assigned but not changed and why, or "none">
101
+
102
+ ## Verification
103
+ live-verified | test-verified | type-check-only | not-verified
104
+ - <command, observed outcome, pass/fail counts, or manual check>
105
+
106
+ ## Confidence and risk
107
+ - Checked directly: <behavior, files, or commands verified by the worker>
108
+ - Still open: <tests, review paths, or integration points the manager must cover>
109
+ - Risk: low | medium | high -- <why>
110
+
111
+ ## Merge notes
112
+ - <conflicts, nearby user changes, assumptions, or deviations>
113
+
114
+ ## Manager follow-ups
115
+ - <merge, reject, rerun check, verifier pass, or replan task>
116
+ \`\`\`
117
+
118
+ The manager must inspect and validate any candidate patch before recording Flow
119
+ completion.
120
+ `;var ne=`# Parallel orchestration
121
+
122
+ Use fan-out when Flow work is broad enough that independent workers can gather
123
+ evidence faster than one linear pass. The manager still owns the Flow session:
124
+ only the manager calls state-changing Flow tools, approves plans, completes
125
+ features, records reviews, or closes sessions.
126
+
127
+ Read these companion references before a broad wave:
128
+
129
+ - \`handoff-format.md\` for the exact worker response shapes.
130
+ - \`verification-gates.md\` for coverage checks, handoff acceptance, verifier
131
+ triggers, and synthesis rules.
7
132
 
8
133
  ## Manager sequence
9
134
 
10
- 1. Call \`flow_status\` if a session may exist.
11
- 2. Do a brief serial orientation pass so slices are real and non-overlapping.
12
- 3. Name the manager's immediate local task; do not delegate the next blocker.
13
- 4. Define slices by module, risk, route, command, artifact type, or worktree.
14
- 5. Send workers narrow prompts with exact ownership and expected output shape.
135
+ 1. Call \`flow_status\` if a Flow session may already exist.
136
+ 2. Do a serial orientation pass. Read enough files, schemas, docs, tests,
137
+ commands, or artifacts to identify real slices.
138
+ 3. Define the local manager task. Do not delegate the immediate blocker that
139
+ determines whether fan-out is even valid.
140
+ 4. Build a pre-fan-out coverage gate:
141
+ - total files, modules, routes, commands, findings, rows, or claims in scope.
142
+ - one line per slice with path/range/lens and expected count.
143
+ - partition check showing slices add back to the total when the work is
144
+ countable.
145
+ - overlap/gap check showing no duplicate ownership, empty slices, or missing
146
+ target areas.
147
+ 5. Spawn only named Flow workers. Use exact slices and the required handoff
148
+ shape. Keep each prompt self-contained.
15
149
  6. Continue non-overlapping manager work while workers run.
16
- 7. Reconcile handoffs yourself; dedupe, refute, and decide what belongs in the
17
- plan, completion payload, review payload, or follow-up feature.
18
- 8. Run second waves only for material gaps, conflicts, or missing coverage.
150
+ 7. Read every handoff. Keep only claims that have evidence, match the assigned
151
+ scope, and carry confidence labels.
152
+ 8. Send important low-confidence, single-source, contested, or citation-heavy
153
+ claims to \`flow-verifier-worker\`.
154
+ 9. Run second waves only for material gaps, conflicts, narrowed scope, or
155
+ verification needs.
156
+ 10. Synthesize one Flow artifact: plan fields, completion evidence, review
157
+ payload, audit report, or candidate patch decision. Do not paste worker
158
+ handoffs as the user-facing result.
19
159
 
20
160
  ## Modes
21
161
 
22
162
  When fanning out Flow work, select the matching hidden Flow agent by name. Do
23
- not use generic subagents for Flow slices because the Flow workers carry the
163
+ not use generic subagents for Flow slices because Flow workers carry the
24
164
  permission boundaries for each mode.
25
165
 
26
166
  | Mode | Use agent | Worker output | Write access | Flow tools |
27
167
  | --- | --- | --- | --- | --- |
28
- | \`evidence\` | \`flow-evidence-worker\` | Facts, files inspected, gaps, suggested plan targets | No | \`flow_status\` only if needed |
29
- | \`review\` | \`flow-reviewer\` | Candidate findings or review slice summary | No | \`flow_status\` only if needed |
30
- | \`validation\` | \`flow-validation-worker\` | Command options, raw output summaries, coverage gaps | No code edits; commands only when explicitly allowed | \`flow_status\` only if needed |
31
- | \`audit\` | \`flow-audit-worker\` | Refuted or surviving finding candidates | No | \`flow_status\` only if needed |
168
+ | \`evidence\` | \`flow-evidence-worker\` | Coverage, facts, files inspected, confidence, gaps, suggested plan targets | No | \`flow_status\` only if needed |
169
+ | \`review\` | \`flow-reviewer\` | Coverage, candidate findings or review slice summary, confidence, gaps | No | \`flow_status\` only if needed |
170
+ | \`validation\` | \`flow-validation-worker\` | Command options or manager-authorized raw output, coverage, confidence, gaps | No code edits; commands only when explicitly allowed | \`flow_status\` only if needed |
171
+ | \`audit\` | \`flow-audit-worker\` | Refuted or surviving finding candidates, guards checked, confidence, gaps | No | \`flow_status\` only if needed |
172
+ | \`verifier\` | \`flow-verifier-worker\` | Per-claim verdicts against cited evidence or commands | No | \`flow_status\` only if needed |
32
173
  | \`candidate-implementation\` | \`flow-candidate-worker\` | Candidate patch summary from an isolated worktree or exact path-owned slice | Only with explicit user authorization plus isolation or exact non-overlapping path ownership | No state-changing Flow tools |
33
174
 
34
- Do not fan out parallel \`flow_run_start\`, \`flow_feature_complete\`,
35
- \`flow_feature_reset\`, or \`flow_session_close\` calls. Runtime locking serializes
36
- file writes, but the Flow model accepts only one active feature result at a time.
175
+ Do not fan out parallel \`flow_plan_save\`, \`flow_plan_approve\`,
176
+ \`flow_run_start\`, \`flow_feature_complete\`, \`flow_feature_reset\`, or
177
+ \`flow_session_close\` calls. Runtime locking protects files, but Flow accepts only
178
+ one active feature result at a time.
37
179
 
38
180
  ## Worker rules
39
181
 
@@ -51,9 +193,9 @@ Workers must not edit \`.flow/**\` and must not call:
51
193
  - \`flow_feature_reset\`
52
194
  - \`flow_session_close\`
53
195
 
54
- Workers also must not approve work, close sessions, or claim validation they did
55
- not run. A worker may report raw validation output it actually ran; the manager
56
- decides whether it is strong enough to record.
196
+ Workers also must not approve work, close sessions, record Flow validation, or
197
+ claim validation they did not run. A worker may report raw validation output it
198
+ actually ran; the manager decides whether it is strong enough to record.
57
199
 
58
200
  ## Prompt contract
59
201
 
@@ -61,31 +203,17 @@ Every worker prompt includes:
61
203
 
62
204
  \`\`\`text
63
205
  Overall goal, context only: <goal>
64
- Mode: evidence | review | validation | audit | candidate-implementation
65
- Your exact slice: <paths, modules, command, risk lens, or worktree>
206
+ Mode: evidence | review | validation | audit | verifier | candidate-implementation
207
+ Your exact slice: <paths, modules, command, claim ids, risk lens, or worktree>
208
+ Expected coverage: <count, paths, range, or complete question set>
66
209
  Do: <bounded actions>
67
- Do not: call Flow state tools, edit .flow/**, own sibling slices, or make the final verdict.
68
- Return exactly the handoff shape below.
69
- \`\`\`
70
-
71
- Ask workers for:
72
-
73
- \`\`\`text
74
- Scope
75
- Evidence inspected
76
- Findings or facts
77
- Open questions / gaps
78
- Suggested Flow follow-ups
210
+ Do not: call Flow state tools, edit .flow/**, own sibling slices, or make the final Flow verdict.
211
+ Return exactly the matching handoff shape from handoff-format.md.
79
212
  \`\`\`
80
213
 
81
- For candidate implementation workers, ask for:
82
-
83
- \`\`\`text
84
- Changed or proposed patch
85
- Verification run
86
- Merge risks
87
- Manager follow-ups
88
- \`\`\`
214
+ For research or current-doc slices, require source checks for versioned or
215
+ time-sensitive facts. For implementation candidates, remind workers that other
216
+ work may be active and that they must not revert unrelated changes.
89
217
 
90
218
  ## Where handoffs go
91
219
 
@@ -97,12 +225,117 @@ Manager follow-ups
97
225
  and raw outcome are concrete enough to trust.
98
226
  - Review evidence informs \`featureReview\` or \`finalReview\`, but the manager owns
99
227
  the pass/fail verdict.
228
+ - Audit evidence becomes findings only after refutation and verification rules
229
+ in \`verification-gates.md\`.
100
230
  - Candidate patches are inspected, merged, and validated by the manager before
101
231
  any Flow completion call.
102
232
 
103
- If worker results conflict, prefer the directly inspected source artifact over
104
- summaries and rerun the narrowest missing check.
105
- `;var ne='# Recovery playbook\n\nUse this when a Flow tool returns `status: "error"`, a blocker, or a `nextAction` that conflicts with memory.\n\n## First response\n\n1. Re-anchor with `flow_status`.\n2. Read the returned `summary`, `recovery`, `lastError`, and active feature.\n3. Fix the cause, then retry the smallest valid Flow action.\n\n## Common cases\n\n- `missing_session`: start with `flow_plan_save` using the user\'s goal.\n- `missing_goal`: ask for a concrete goal before planning.\n- `Approved plans cannot be changed`: use `flow_feature_reset` when only affected features need another pass; otherwise close and start a new goal.\n- `No feature is currently running`: call `flow_run_start` before completing.\n- `already in progress`: finish, reset, or block the active feature before starting another.\n- `Completion requires recorded validation evidence`: run real validation and include at least one passing `validationRun`.\n- `Completion requires all recorded validation to pass`: fix failures and rerun. Do not relabel failed checks as passed.\n- `Non-final feature completion requires targeted validation`: use `validationScope: "targeted"` for ordinary features.\n- `Final feature completion requires broad validation`: run the project-level gate and use `validationScope: "broad"`.\n- `Completion requires a passing featureReview`: run or request a real review and include a passing `featureReview` only when there are no blocking findings.\n- `Final feature completion requires a finalReview`: perform final review and include `finalReview`.\n- `Final review depth must match the plan policy`: use `reviewDepth` equal to the approved plan\'s `finalReviewPolicy`.\n- `Cannot close ... unfinished features`: complete, reset, defer, or abandon honestly. Do not mark completed while work remains.\n\n## Reset guidance\n\nUse `flow_feature_reset` when the active or completed work was built on the wrong assumption, validation revealed a design issue, dependencies need to be rerun, or dependent features must be invalidated. Resetting a feature also resets its dependents.\n\n## Closure guidance\n\nUse `flow_session_close`:\n\n- `completed`: only after all planned features are complete.\n- `deferred`: the user intentionally postpones unfinished work.\n- `abandoned`: the session should be archived without claiming delivery.\n\nAfter closure, the active `.flow/session.json` is removed and the archived JSON is stored under `.flow/history/`.\n';var re="---\nname: flow\ndescription: Main Flow loop for skills-first OpenCode work. Use when a user asks for Flow-guided planning, autonomous execution, resumable implementation, session status, or end-to-end delivery with validation and review gates.\n---\n\n# Flow\n\nUse Flow as a minimal state ledger, not as a framework. Skills provide judgment; the runtime only records the approved plan, active feature, validation evidence, review evidence, and closure.\n\n## Loop\n\n1. Call `flow_status` first. Trust its active session and next action over conversation memory.\n2. If there is no active session and the user gave a goal, load `flow-plan`, save a plan with `flow_plan_save`, then approve it with `flow_plan_approve`. If there is no goal, ask for one.\n3. Load `flow-run`, call `flow_run_start`, implement exactly one feature, validate it, and prepare a `flow_feature_complete` payload.\n4. Load `flow-review` for the required feature review. The reviewer reports a `featureReview` payload; the manager records it inside `flow_feature_complete`.\n5. On the final feature, run broad validation and include `finalReview` in the same `flow_feature_complete` call. Its `reviewDepth` must match the plan's `finalReviewPolicy`.\n6. After all features are complete, archive the session with `flow_session_close` using `kind: \"completed\"`.\n\nUse `references/parallel-orchestration.md` for broad read-only discovery, audit, or review. The manager owns every `flow_*` state change.\n\n## Runtime Surface\n\n- `flow_status`: read the active session.\n- `flow_plan_save`: create a session and/or save a draft plan.\n- `flow_plan_approve`: lock the draft plan.\n- `flow_run_start`: start one runnable feature.\n- `flow_feature_complete`: record completion or a real blocker with validation and review evidence.\n- `flow_feature_reset`: reset one feature and its dependents.\n- `flow_session_close`: archive the active session as `completed`, `deferred`, or `abandoned`.\n\nThere is no `flow_context`, no separate review-record tool, and no multi-session activation surface. The single active source of truth is `.flow/session.json`; closed sessions are archived under `.flow/history/`.\n\n## Hard Gates\n\n- Approved plans are immutable. To change direction, reset affected features or close the session and start a new goal.\n- Only one feature can be active at a time.\n- Completion requires at least one passing `validationRun` entry.\n- Non-final completion requires `validationScope: \"targeted\"`.\n- Final completion requires `validationScope: \"broad\"` and a passing `finalReview`.\n- Every completed feature requires a passing `featureReview` with no blocking findings.\n- `flow_session_close` accepts `kind: \"completed\"` only after an approved plan has passed final completion.\n\n## Recovery\n\n- Confused state: call `flow_status` and follow `nextAction`.\n- Wrong assumption or failed implementation path: use `flow_feature_reset` for the feature and dependents, then rerun from the corrected plan.\n- Missing validation or review evidence: gather real evidence, then call `flow_feature_complete`.\n- Approved plan is materially wrong: reset the affected features, save a revised plan if the session is back in planning; otherwise close and start a new goal.\n- Unknown runtime error: read `summary` and `recovery`; see `references/recovery-playbook.md` for common cases.\n\nNever fabricate validation output, backfill review approval you did not perform, or close as `deferred`/`abandoned` merely to avoid an unfinished-work blocker.\n";var oe=`# Safe refactor workflow
233
+ When worker results conflict, inspect the underlying artifact directly and rerun
234
+ the smallest check that can settle the disagreement.
235
+
236
+ ## Second waves
237
+
238
+ Start a follow-up wave when first-wave handoffs reveal:
239
+
240
+ - missing coverage in the original slice map.
241
+ - conflicting findings that matter to the Flow decision.
242
+ - a specialized follow-up that was intentionally out of scope.
243
+ - high-stakes, low-confidence, or single-source claims needing verification.
244
+ - bounded implementation candidates after research converges.
245
+
246
+ Do not recurse by default. If a worker says it needs another worker, the manager
247
+ decides whether that is a second wave and writes the next bounded prompt.
248
+ `;var re='# Recovery playbook\n\nUse this when a Flow tool returns `status: "error"`, a blocker, or a `nextAction` that conflicts with memory.\n\n## First response\n\n1. Re-anchor with `flow_status`.\n2. Read the returned `summary`, `recovery`, `lastError`, and active feature.\n3. Fix the cause, then retry the smallest valid Flow action.\n\n## Common cases\n\n- `missing_session`: start with `flow_plan_save` using the user\'s goal.\n- `missing_goal`: ask for a concrete goal before planning.\n- `Approved plans cannot be changed`: use `flow_feature_reset` when only affected features need another pass; otherwise close and start a new goal.\n- `No feature is currently running`: call `flow_run_start` before completing.\n- `already in progress`: finish, reset, or block the active feature before starting another.\n- `Completion requires recorded validation evidence`: run real validation and include at least one passing `validationRun`.\n- `Completion requires all recorded validation to pass`: fix failures and rerun. Do not relabel failed checks as passed.\n- `Non-final feature completion requires targeted validation`: use `validationScope: "targeted"` for ordinary features.\n- `Final feature completion requires broad validation`: run the project-level gate and use `validationScope: "broad"`.\n- `Completion requires a passing featureReview`: run or request a real review and include a passing `featureReview` only when there are no blocking findings.\n- `Final feature completion requires a finalReview`: perform final review and include `finalReview`.\n- `Final review depth must match the plan policy`: use `reviewDepth` equal to the approved plan\'s `finalReviewPolicy`.\n- `Cannot close ... unfinished features`: complete, reset, defer, or abandon honestly. Do not mark completed while work remains.\n\n## Reset guidance\n\nUse `flow_feature_reset` when the active or completed work was built on the wrong assumption, validation revealed a design issue, dependencies need to be rerun, or dependent features must be invalidated. Resetting a feature also resets its dependents.\n\n## Closure guidance\n\nUse `flow_session_close`:\n\n- `completed`: only after all planned features are complete.\n- `deferred`: the user intentionally postpones unfinished work.\n- `abandoned`: the session should be archived without claiming delivery.\n\nAfter closure, the active `.flow/session.json` is removed and the archived JSON is stored under `.flow/history/`.\n';var oe=`# Verification gates
249
+
250
+ Verification is how Flow keeps parallel work from turning into parallel
251
+ guesswork. Worker handoffs are candidate evidence; the manager decides what can
252
+ enter the plan, validation record, review payload, audit report, or final
253
+ answer.
254
+
255
+ ## Before fan-out
256
+
257
+ Run a pre-fan-out gate after serial orientation and before spawning workers:
258
+
259
+ - Count the total work items when countable: files, modules, routes, commands,
260
+ rows, findings, screenshots, or claims.
261
+ - List every slice with path/range/lens and expected count.
262
+ - Check that countable slices add back to the total.
263
+ - Check for overlapping ownership, missing areas, empty slices, and ambiguous
264
+ shared contracts.
265
+ - Fix the slice map centrally before spawning if the gate does not reconcile.
266
+
267
+ If the scope cannot be counted, state the completeness rule instead, such as
268
+ "all changed files plus callers" or "all public commands plus release docs."
269
+
270
+ ## Every handoff
271
+
272
+ Accept a handoff only after a cheap manager-side pass:
273
+
274
+ - \`Status\` is terminal: \`success\`, \`partial\`, or \`blocked\`.
275
+ - Coverage matches the assigned slice, or skips are explicit.
276
+ - Important claims have concrete evidence and confidence tags.
277
+ - Cited paths, commands, screenshots, URLs, or metrics resolve.
278
+ - The evidence supports the claim, not just the topic.
279
+ - Findings stay inside the worker's slice.
280
+ - Headline counts can be recounted or traced.
281
+ - Contradictions between workers are either resolved or explicitly marked as
282
+ contested.
283
+
284
+ Demote, drop, re-task, or verify claims that fail this pass.
285
+
286
+ ## Verifier triggers
287
+
288
+ Use \`flow-verifier-worker\` when a claim is:
289
+
290
+ - blocking or release-sensitive.
291
+ - high-stakes for user data, security, persistence, permissions, or public API
292
+ behavior.
293
+ - low-confidence, inferred, surprising, or single-sourced.
294
+ - citation-heavy enough that source drift would change the conclusion.
295
+ - contradicted by another worker or by manager inspection.
296
+ - a count, benchmark, command result, or pass/fail claim that a Flow payload will
297
+ rely on.
298
+
299
+ Give the verifier atomic claims, cited sources or commands, and the acceptance
300
+ question. Do not ask it to redesign the work or review the whole feature.
301
+
302
+ ## Flow payload acceptance
303
+
304
+ Planning fields may use worker evidence only when the source and scope are clear.
305
+ Unverified broad findings should become a review-first feature, not a fix plan.
306
+
307
+ \`validationRun\` entries may use worker-reported commands only when the worker
308
+ was explicitly authorized to run the command and reported the exact command,
309
+ status, and raw outcome summary.
310
+
311
+ \`featureReview\` and \`finalReview\` may use worker review slices, but the manager
312
+ owns the pass/fail decision and must resolve blockers, contradictions, and
313
+ coverage gaps before returning the payload.
314
+
315
+ Audit reports may include only findings that survived refutation. Blocking audit
316
+ findings need guards checked, deployment context, and evidence that the current
317
+ code exhibits the behavior.
318
+
319
+ Candidate implementation patches are not Flow evidence until the manager
320
+ inspects, merges or rejects them, and runs suitable validation in the main
321
+ Flow-managed workspace.
322
+
323
+ ## Final synthesis
324
+
325
+ Before presenting or recording the result:
326
+
327
+ - Preserve confidence: verified, single-sourced, inferred, and unresolved claims
328
+ stay distinct when it matters.
329
+ - When workers disagree, inspect the cited artifact or rerun the cited command
330
+ instead of arbitrating from summaries.
331
+ - Run the strongest practical local check for the deliverable.
332
+ - Re-read critical files or docs that will be cited in the final decision.
333
+ - Record gaps honestly instead of converting missing evidence into success
334
+ language.
335
+
336
+ \`Status: success\` only says the worker believes its slice is done. The manager
337
+ still checks coverage and evidence before trusting the result.
338
+ `;var ie="---\nname: flow\ndescription: Main Flow loop for skills-first OpenCode work. Use when a user asks for Flow-guided planning, autonomous execution, resumable implementation, session status, or end-to-end delivery with validation and review gates.\n---\n\n# Flow\n\nUse Flow as a minimal state ledger, not as a framework. Skills provide judgment; the runtime only records the approved plan, active feature, validation evidence, review evidence, and closure.\n\n## Loop\n\n1. Call `flow_status` first. Trust its active session and next action over conversation memory.\n2. If there is no active session and the user gave a goal, load `flow-plan`, save a plan with `flow_plan_save`, then approve it with `flow_plan_approve`. If there is no goal, ask for one.\n3. Load `flow-run`, call `flow_run_start`, implement exactly one feature, validate it, and prepare a `flow_feature_complete` payload.\n4. Load `flow-review` for the required feature review. The reviewer reports a `featureReview` payload; the manager records it inside `flow_feature_complete`.\n5. On the final feature, run broad validation and include `finalReview` in the same `flow_feature_complete` call. Its `reviewDepth` must match the plan's `finalReviewPolicy`.\n6. After all features are complete, archive the session with `flow_session_close` using `kind: \"completed\"`.\n\nUse `references/parallel-orchestration.md` for broad read-only discovery, audit, validation, review, verification, or candidate implementation waves. Its `references/handoff-format.md` and `references/verification-gates.md` companions define the worker contracts. The manager owns every `flow_*` state change.\n\n## Runtime Surface\n\n- `flow_status`: read the active session.\n- `flow_plan_save`: create a session and/or save a draft plan.\n- `flow_plan_approve`: lock the draft plan.\n- `flow_run_start`: start one runnable feature.\n- `flow_feature_complete`: record completion or a real blocker with validation and review evidence.\n- `flow_feature_reset`: reset one feature and its dependents.\n- `flow_session_close`: archive the active session as `completed`, `deferred`, or `abandoned`.\n\nThere is no `flow_context`, no separate review-record tool, and no multi-session activation surface. The single active source of truth is `.flow/session.json`; closed sessions are archived under `.flow/history/`.\n\n## Hard Gates\n\n- Approved plans are immutable. To change direction, reset affected features or close the session and start a new goal.\n- Only one feature can be active at a time.\n- Completion requires at least one passing `validationRun` entry.\n- Non-final completion requires `validationScope: \"targeted\"`.\n- Final completion requires `validationScope: \"broad\"` and a passing `finalReview`.\n- Every completed feature requires a passing `featureReview` with no blocking findings.\n- `flow_session_close` accepts `kind: \"completed\"` only after an approved plan has passed final completion.\n\n## Recovery\n\n- Confused state: call `flow_status` and follow `nextAction`.\n- Wrong assumption or failed implementation path: use `flow_feature_reset` for the feature and dependents, then rerun from the corrected plan.\n- Missing validation or review evidence: gather real evidence, then call `flow_feature_complete`.\n- Approved plan is materially wrong: reset the affected features, save a revised plan if the session is back in planning; otherwise close and start a new goal.\n- Unknown runtime error: read `summary` and `recovery`; see `references/recovery-playbook.md` for common cases.\n\nNever fabricate validation output, backfill review approval you did not perform, or close as `deferred`/`abandoned` merely to avoid an unfinished-work blocker.\n";var se=`# Safe refactor workflow
106
339
 
107
340
  Refactoring is a behavior-preserving sequence of small changes. This workflow keeps cleanup from becoming an unreviewable rewrite.
108
341
 
@@ -144,7 +377,7 @@ Weak evidence includes:
144
377
  - Public contracts and compatibility shims remain intact or were explicitly planned.
145
378
  - Deleted code is actually unreachable or obsolete.
146
379
  - Validation can catch a realistic mistake in the refactor.
147
- `;var ie=`# Deslop smell rubric
380
+ `;var le=`# Deslop smell rubric
148
381
 
149
382
  Use this rubric to turn vague cleanup instincts into reviewable findings.
150
383
 
@@ -178,7 +411,7 @@ class; severity; location; evidence read; refutation checked; why it matters; sa
178
411
  \`\`\`
179
412
 
180
413
  Rate as blocking only when the smell materially raises defect risk, blocks planned work, hides behavior, or makes the success claim unverifiable. Style-only cleanup is advisory.
181
- `;var se=`---
414
+ `;var ce=`---
182
415
  name: flow-deslop
183
416
  description: Flow guidance for evidence-backed code smell cleanup, AI-slop removal, overengineering reduction, maintainability refactors, and behavior-preserving cleanup. Use when planning, executing, or reviewing broad cleanup/refactor work, code smell findings, duplicated or bloated code, speculative abstractions, dead code, or agent-introduced mess.
184
417
  ---
@@ -220,7 +453,73 @@ For each claimed smell removal, verify:
220
453
  - **blast radius** — public contracts and downstream callers still work.
221
454
 
222
455
  Never approve cleanup because it "looks cleaner" without evidence. Tests passing is necessary but not sufficient when the refactor changes structure across files.
223
- `;var le="# Parallel discovery\n\nUse this only after a serial orientation pass has identified the repo shape and the likely slices. Workers are read-only evidence gatherers; the planner owns the plan.\n\n## Good slices\n\n- Independent modules or packages.\n- Frontend route and backend endpoint pairs.\n- Test, CI, and release surfaces.\n- Risk lenses such as security, persistence, accessibility, migration, or performance.\n- Documentation and operator-contract checks.\n\n## Flow repo default slices\n\nFor this repository, good first-wave slices are:\n\n- Runtime gates: `src/runtime/schema.ts`, `src/runtime/transitions.ts`,\n `src/runtime/api.ts`, and `tests/runtime-gates.test.ts`.\n- Workspace persistence: `src/runtime/workspace.ts`,\n `src/runtime/json/strict-object.ts`, and\n `tests/workspace-persistence.test.ts`.\n- OpenCode adapter surface: `src/adapters/opencode/**`, `src/config-shared.ts`,\n `src/config.ts`, `src/index.ts`, and surface tests.\n- Distribution and synced skills: `src/distribution/**`, `src/cli.ts`,\n `skills/**`, and distribution tests.\n- CI, package, and release contract: `.github/workflows/**`, `package.json`,\n `bun.lock`, `README.md`, and `CHANGELOG.md`.\n- Docs and operator contract: `docs/**`, `README.md`, and skill references.\n\n## Worker prompt\n\n```text\nInspect <slice> for <goal>. Read-only. Do not edit files or call Flow tools.\nReturn: scope inspected; files/commands checked; evidence-backed facts or findings; gaps; suggested feature targets and validation.\n```\n\nFor validation-oriented discovery:\n\n```text\nInspect <slice> for validation risk. Read-only. Do not edit files or call Flow\ntools. You may report commands that should be run, and include raw output only\nfor commands you actually ran. Return: scope inspected; validation-relevant\nfacts; suggested targeted checks; broad-gate implications; gaps.\n```\n\n## Synthesis\n\nConvert only evidence-backed work into plan fields:\n\n- `requirements`: user promises and externally visible acceptance criteria.\n- `decisions`: architecture boundaries, rejected approaches, and scope cuts.\n- feature `targets`: files, modules, routes, commands, docs, or workflows the feature owns.\n- feature `validation`: checks expected to prove the feature.\n\nIf workers disagree, inspect the source artifact yourself. If a candidate finding lacks a concrete citation or refutation pass, make it a review-first deliverable rather than a fix feature.\n";var ce=`# Planning examples
456
+ `;var de=`# Parallel discovery
457
+
458
+ Use this only after a serial orientation pass has identified the repo shape and the likely slices. Workers are read-only evidence gatherers; the planner owns the plan.
459
+
460
+ For broad waves, also load \`../../flow/references/parallel-orchestration.md\`.
461
+ Use its pre-fan-out coverage gate and
462
+ \`../../flow/references/handoff-format.md\` response shapes.
463
+
464
+ ## Good slices
465
+
466
+ - Independent modules or packages.
467
+ - Frontend route and backend endpoint pairs.
468
+ - Test, CI, and release surfaces.
469
+ - Risk lenses such as security, persistence, accessibility, migration, or performance.
470
+ - Documentation and operator-contract checks.
471
+
472
+ ## Flow repo default slices
473
+
474
+ For this repository, good first-wave slices are:
475
+
476
+ - Runtime gates: \`src/runtime/schema.ts\`, \`src/runtime/transitions.ts\`,
477
+ \`src/runtime/api.ts\`, and \`tests/runtime-gates.test.ts\`.
478
+ - Workspace persistence: \`src/runtime/workspace.ts\`,
479
+ \`src/runtime/json/strict-object.ts\`, and
480
+ \`tests/workspace-persistence.test.ts\`.
481
+ - OpenCode adapter surface: \`src/adapters/opencode/**\`, \`src/config-shared.ts\`,
482
+ \`src/config.ts\`, \`src/index.ts\`, and surface tests.
483
+ - Distribution and synced skills: \`src/distribution/**\`, \`src/cli.ts\`,
484
+ \`skills/**\`, and distribution tests.
485
+ - CI, package, and release contract: \`.github/workflows/**\`, \`package.json\`,
486
+ \`bun.lock\`, \`README.md\`, and \`CHANGELOG.md\`.
487
+ - Docs and operator contract: \`docs/**\`, \`README.md\`, and skill references.
488
+
489
+ ## Coverage gate
490
+
491
+ Before spawning workers, state the total discovery scope and one line per slice.
492
+ For countable scopes, confirm that slice counts add back to the total and that
493
+ there are no overlaps, gaps, or empty slices. If the scope is not countable,
494
+ state the completeness rule, such as "all changed files plus callers."
495
+
496
+ ## Worker prompt
497
+
498
+ \`\`\`text
499
+ Inspect <slice> for <goal>. Read-only. Do not edit files or call Flow tools.
500
+ Return the evidence/review/validation/audit handoff shape from ../../flow/references/handoff-format.md.
501
+ \`\`\`
502
+
503
+ For validation-oriented discovery:
504
+
505
+ \`\`\`text
506
+ Inspect <slice> for validation risk. Read-only. Do not edit files or call Flow
507
+ tools. You may report commands that should be run, and include raw output only
508
+ for commands you actually ran. Return the evidence/review/validation/audit
509
+ handoff shape from ../../flow/references/handoff-format.md.
510
+ \`\`\`
511
+
512
+ ## Synthesis
513
+
514
+ Convert only evidence-backed work into plan fields:
515
+
516
+ - \`requirements\`: user promises and externally visible acceptance criteria.
517
+ - \`decisions\`: architecture boundaries, rejected approaches, and scope cuts.
518
+ - feature \`targets\`: files, modules, routes, commands, docs, or workflows the feature owns.
519
+ - feature \`validation\`: checks expected to prove the feature.
520
+
521
+ If workers disagree, inspect the source artifact yourself. If a candidate finding lacks a concrete citation or refutation pass, make it a review-first deliverable rather than a fix feature.
522
+ `;var ue=`# Planning examples
224
523
 
225
524
  ## Rate limiting feature set
226
525
 
@@ -305,7 +604,7 @@ Better plan:
305
604
  - Validation that only says "manual testing".
306
605
  - Targets that name the entire repo.
307
606
  - Features with hidden dependencies instead of \`dependsOn\`.
308
- `;var de=`---
607
+ `;var pe=`---
309
608
  name: flow-plan
310
609
  description: Plan Flow work for the v4 skills-first runtime: inspect the repo, decompose a user goal into right-sized features, save a draft with flow_plan_save, and approve it with flow_plan_approve.
311
610
  ---
@@ -319,7 +618,7 @@ If \`flow_plan_save\` is unavailable, stop and tell the user to check that \`ope
319
618
  ## Inspect first
320
619
 
321
620
  - Read the files, docs, tests, package scripts, and local conventions that determine the work.
322
- - For broad discovery, read \`references/parallel-discovery.md\` after a serial orientation pass.
621
+ - For broad discovery, read \`references/parallel-discovery.md\` after a serial orientation pass. Use \`../flow/references/parallel-orchestration.md\` when discovery needs multiple workers, and apply its coverage gate before fan-out.
323
622
  - For cleanup/refactor goals, load \`flow-deslop\`.
324
623
  - For UI/frontend goals, load \`flow-ui-quality\`.
325
624
  - Do not invent findings. Broad "review and fix" goals start with a review-first feature whose deliverable is evidence-backed findings.
@@ -366,7 +665,7 @@ Use \`finalReviewPolicy: "broad"\` only for low-risk, narrow work. Use \`"detail
366
665
  After saving, summarize the plan to the user or proceed if they already authorized implementation. Then call \`flow_plan_approve\`. Approved plans are immutable; changing them later requires reset/closure rather than silent edits.
367
666
 
368
667
  See \`references/planning-examples.md\` for payload examples and decomposition anti-patterns.
369
- `;var ue=`# Review rubric
668
+ `;var fe=`# Review rubric
370
669
 
371
670
  Use this to decide whether a \`featureReview\` or \`finalReview\` payload may pass.
372
671
 
@@ -454,7 +753,7 @@ When reviewing a findings report, verify findings adversarially:
454
753
  - Downgrade or reject findings that do not survive refutation.
455
754
 
456
755
  Approve only on evidence actually inspected. A review is a claim of coverage, not a courtesy stamp.
457
- `;var pe=`---
756
+ `;var me=`---
458
757
  name: flow-review
459
758
  description: Review Flow work in the v4 runtime: inspect feature or final-session changes, classify findings, and return featureReview or finalReview payloads for flow_feature_complete.
460
759
  ---
@@ -514,11 +813,12 @@ Use \`status: "failed"\` when any blocking finding remains. Advisory findings ma
514
813
  - Large reviews: use \`../flow/references/parallel-orchestration.md\` for
515
814
  read-only slices by changed-file group, risk lens, or validation surface.
516
815
  Use the named review, audit, evidence, or validation agents from that
517
- reference instead of generic subagents. Synthesize the handoffs yourself; only
518
- the manager returns the final \`featureReview\` or \`finalReview\` payload.
816
+ reference instead of generic subagents. Apply its handoff format and
817
+ verification gates; only the manager returns the final \`featureReview\` or
818
+ \`finalReview\` payload.
519
819
 
520
820
  Never approve to unblock completion, fix findings in the review pass, or vouch for validation you did not inspect.
521
- `;var fe=`# Audit findings rubric
821
+ `;var he=`# Audit findings rubric
522
822
 
523
823
  What counts as a valid finding when the feature's deliverable is a findings report: a codebase audit, a review-first feature, or any report whose findings a later feature will fix. The commands you run are still governed by \`validation-rubric.md\`; this rubric governs the findings themselves.
524
824
 
@@ -538,8 +838,9 @@ A finding that survives this pass is worth reporting. A finding you did not try
538
838
 
539
839
  For broad audits, use \`../../flow/references/parallel-orchestration.md\` to split
540
840
  read-only slices by module, data flow, or risk lens. Workers surface candidates;
541
- the audit author owns the report. Before blocking severity, dedupe, trace
542
- guards, fill cross-layer checks, and downgrade missing context.
841
+ the audit author owns the report. Apply its handoff format and verification
842
+ gates. Before blocking severity, dedupe, trace guards, fill cross-layer checks,
843
+ verify contested or high-stakes claims, and downgrade missing context.
543
844
 
544
845
  ## Every blocking finding records "guards checked"
545
846
 
@@ -570,7 +871,7 @@ follow-up order — correctness and persisted/user-input surfaces first
570
871
  \`\`\`
571
872
 
572
873
  Never: promote a hypothesis to blocking severity; cite a line you did not read in context; rate severity against a deployment model the product does not have; pad the report to look thorough — six verified findings outrank nine where three die on first contact.
573
- `;var me=`# Validation evidence rubric
874
+ `;var ge=`# Validation evidence rubric
574
875
 
575
876
  Use this before recording \`flow_feature_complete\`.
576
877
 
@@ -588,6 +889,7 @@ Use the strongest practical tier. For risky work, combine tiers.
588
889
  - Each \`validationRun\` entry has \`command\`, \`status\`, and \`summary\`.
589
890
  - Completion accepts only passing entries. Failed or skipped checks belong in the summary/notes and must be resolved or explained as blockers.
590
891
  - Do not claim a command was run unless it was run in this session or directly reported by a trusted worker with raw output.
892
+ - Worker-reported command output must satisfy \`../../flow/references/verification-gates.md\`: exact command, status, raw outcome summary, coverage, and manager acceptance.
591
893
  - Include scope in the summary: what behavior, files, routes, or states the check covered.
592
894
  - UI work should include browser or screenshot evidence when the app can run locally.
593
895
  - Cleanup/refactor work should show behavior preservation, not only formatting success.
@@ -626,7 +928,7 @@ Broad validation usually means the repo's full check command, full relevant test
626
928
  - If validation needs external access, missing credentials, or ambiguous user input, record \`status: "needs_input"\` with an honest \`outcome\`.
627
929
 
628
930
  Never trim failing output, relabel a failed command as passed, or use "not run" as completion evidence.
629
- `;var he="---\nname: flow-run\ndescription: Execute one approved Flow feature in the v4 runtime: start a feature with flow_run_start, make scoped changes, gather real validation evidence, obtain review payloads, and complete with flow_feature_complete.\n---\n\n# Flow Run\n\nUse this skill for implementation after a Flow plan is approved. Work one feature at a time.\n\nIf `flow_run_start` is unavailable, stop and tell the user to check that `opencode-plugin-flow` is loaded in OpenCode.\n\n## Start\n\n- Call `flow_status`.\n- Call `flow_run_start` with no `featureId` unless the user or plan requires a specific runnable feature.\n- Treat the returned feature as the sole scope until it is completed, blocked, or reset.\n- Load `flow-deslop` for cleanup/refactor features.\n- Load `flow-ui-quality` for frontend, UX, responsive, accessibility, or visual work.\n\n## Implement\n\n- Read the feature `targets`, `summary`, `validation`, dependencies, and plan `requirements`/`decisions`.\n- Keep edits scoped to the active feature. If new scope appears, stop and replan or defer it to another feature.\n- Preserve unrelated user changes in the worktree.\n- When a wrong assumption invalidates the feature, use `flow_feature_reset`; do not pile patches onto a bad path.\n\n## Validate\n\n- Read `references/validation-rubric.md` before completing.\n- Run the strongest practical checks for the changed behavior.\n- Record concrete command names, status, and observed results. \"Tests pass\" is not evidence.\n- Non-final features complete with `validationScope: \"targeted\"`.\n- The final feature must run a broad project-level gate and use `validationScope: \"broad\"`.\n\nFor broad validation research, risky changes, or unclear coverage, use\n`../flow/references/parallel-orchestration.md` to fan out named Flow workers.\nUse the mode-to-agent mapping in that reference instead of generic subagents.\nWorkers may report command output they actually ran or propose focused checks;\nthe manager decides what is strong enough to record.\n\nFor independent implementation attempts, use candidate workers only with\nexplicit user authorization plus isolated worktrees or exact non-overlapping\npath ownership. Treat their output as candidate patches. The manager inspects,\nmerges, validates, and records Flow state serially.\n\n## Review and complete\n\nBefore `flow_feature_complete`, obtain a `featureReview` payload. Load `flow-review`; for read-only subagent reviews, the manager receives the payload and records it.\n\nFor the final feature, also obtain a `finalReview` payload whose `reviewDepth` equals the approved plan's `finalReviewPolicy`.\n\nComplete with:\n\n```json\n{\n \"status\": \"ok\",\n \"featureId\": \"active-feature-id\",\n \"summary\": \"what changed\",\n \"artifactsChanged\": [{ \"path\": \"src/file.ts\" }],\n \"validationRun\": [\n { \"command\": \"bun test tests/foo.test.ts\", \"status\": \"passed\", \"summary\": \"3 pass, exercised foo behavior\" }\n ],\n \"validationScope\": \"targeted\",\n \"featureReview\": { \"status\": \"passed\", \"summary\": \"review summary\", \"blockingFindings\": [] }\n}\n```\n\nIf genuinely blocked, call `flow_feature_complete` with `status: \"needs_input\"` and an `outcome` that explains the blocker and next step. Never fabricate validation or review evidence to force progress.\n";var ge=`# UI quality rubric
931
+ `;var we="---\nname: flow-run\ndescription: Execute one approved Flow feature in the v4 runtime: start a feature with flow_run_start, make scoped changes, gather real validation evidence, obtain review payloads, and complete with flow_feature_complete.\n---\n\n# Flow Run\n\nUse this skill for implementation after a Flow plan is approved. Work one feature at a time.\n\nIf `flow_run_start` is unavailable, stop and tell the user to check that `opencode-plugin-flow` is loaded in OpenCode.\n\n## Start\n\n- Call `flow_status`.\n- Call `flow_run_start` with no `featureId` unless the user or plan requires a specific runnable feature.\n- Treat the returned feature as the sole scope until it is completed, blocked, or reset.\n- Load `flow-deslop` for cleanup/refactor features.\n- Load `flow-ui-quality` for frontend, UX, responsive, accessibility, or visual work.\n\n## Implement\n\n- Read the feature `targets`, `summary`, `validation`, dependencies, and plan `requirements`/`decisions`.\n- Keep edits scoped to the active feature. If new scope appears, stop and replan or defer it to another feature.\n- Preserve unrelated user changes in the worktree.\n- When a wrong assumption invalidates the feature, use `flow_feature_reset`; do not pile patches onto a bad path.\n\n## Validate\n\n- Read `references/validation-rubric.md` before completing.\n- Run the strongest practical checks for the changed behavior.\n- Record concrete command names, status, and observed results. \"Tests pass\" is not evidence.\n- Non-final features complete with `validationScope: \"targeted\"`.\n- The final feature must run a broad project-level gate and use `validationScope: \"broad\"`.\n\nFor broad validation research, risky changes, or unclear coverage, use\n`../flow/references/parallel-orchestration.md` to fan out named Flow workers.\nUse the mode-to-agent mapping in that reference instead of generic subagents.\nWorkers must use the handoff format and verification gates from that reference.\nThey may report command output they actually ran or propose focused checks; the\nmanager decides what is strong enough to record.\n\nFor independent implementation attempts, use candidate workers only with\nexplicit user authorization plus isolated worktrees or exact non-overlapping\npath ownership. Treat their output as candidate patches. The manager inspects,\nmerges, validates, and records Flow state serially.\n\n## Review and complete\n\nBefore `flow_feature_complete`, obtain a `featureReview` payload. Load `flow-review`; for read-only subagent reviews, the manager receives the payload and records it.\n\nFor the final feature, also obtain a `finalReview` payload whose `reviewDepth` equals the approved plan's `finalReviewPolicy`.\n\nComplete with:\n\n```json\n{\n \"status\": \"ok\",\n \"featureId\": \"active-feature-id\",\n \"summary\": \"what changed\",\n \"artifactsChanged\": [{ \"path\": \"src/file.ts\" }],\n \"validationRun\": [\n { \"command\": \"bun test tests/foo.test.ts\", \"status\": \"passed\", \"summary\": \"3 pass, exercised foo behavior\" }\n ],\n \"validationScope\": \"targeted\",\n \"featureReview\": { \"status\": \"passed\", \"summary\": \"review summary\", \"blockingFindings\": [] }\n}\n```\n\nIf genuinely blocked, call `flow_feature_complete` with `status: \"needs_input\"` and an `outcome` that explains the blocker and next step. Never fabricate validation or review evidence to force progress.\n";var ve=`# UI quality rubric
630
932
 
631
933
  Use this rubric for frontend planning, implementation, and review.
632
934
 
@@ -670,7 +972,7 @@ class; severity; location or screenshot area; evidence inspected; user impact; f
670
972
  \`\`\`
671
973
 
672
974
  Blocking UI findings are issues that prevent task completion, hide required information, break accessibility basics, create incoherent layout at supported sizes, or make the visual success claim unverifiable.
673
- `;var ve=`# Visual verification workflow
975
+ `;var ye=`# Visual verification workflow
674
976
 
675
977
  Use this workflow when UI changes can be run locally. Flow execution may create visual evidence; Flow review usually assesses recorded evidence because the reviewer is read-only.
676
978
 
@@ -710,7 +1012,7 @@ Record the reason and use the strongest available substitute:
710
1012
  - code inspection against existing component patterns.
711
1013
 
712
1014
  Do not claim visual polish was verified if no visual artifact was inspected.
713
- `;var we=`---
1015
+ `;var be=`---
714
1016
  name: flow-ui-quality
715
1017
  description: Flow guidance for UX/UI design, frontend polish, visual quality review, responsive and accessible interfaces, and avoiding generic AI-generated UI. Use when planning, executing, or reviewing frontend pages, components, dashboards, visual redesigns, interaction states, screenshots, or browser-verified UI work.
716
1018
  ---
@@ -755,13 +1057,11 @@ Approve only when the interface is both useful and inspectable:
755
1057
  - Screenshot/browser evidence supports the claim whenever feasible.
756
1058
 
757
1059
  Never approve a UI change based only on code shape. If users will judge it visually, Flow evidence should include visual inspection.
758
- `;var ye=[{name:"flow",files:[{relativePath:"SKILL.md",content:re},{relativePath:"references/recovery-playbook.md",content:ne},{relativePath:"references/parallel-orchestration.md",content:ae}]},{name:"flow-plan",files:[{relativePath:"SKILL.md",content:de},{relativePath:"references/planning-examples.md",content:ce},{relativePath:"references/parallel-discovery.md",content:le}]},{name:"flow-run",files:[{relativePath:"SKILL.md",content:he},{relativePath:"references/validation-rubric.md",content:me},{relativePath:"references/audit-rubric.md",content:fe}]},{name:"flow-review",files:[{relativePath:"SKILL.md",content:pe},{relativePath:"references/review-rubric.md",content:ue}]},{name:"flow-deslop",files:[{relativePath:"SKILL.md",content:se},{relativePath:"references/smell-rubric.md",content:ie},{relativePath:"references/refactor-workflow.md",content:oe}]},{name:"flow-ui-quality",files:[{relativePath:"SKILL.md",content:we},{relativePath:"references/ui-rubric.md",content:ge},{relativePath:"references/visual-verification.md",content:ve}]}];var Et=".flow-skill-version";function xe(){return process.env.HOME??process.env.USERPROFILE??""}function Ot(e=xe()){return I(e,".config","opencode","skills")}function Fe(e){return St("sha256").update(e).digest("hex")}function be(e,a){return[`version=${a}`,...e.files.map((t)=>`file=${t.relativePath} sha256=${Fe(t.content)}`),""].join(`
759
- `)}async function V(e){try{return await It(e,"utf8")}catch(a){if(a.code==="ENOENT")return null;throw a}}function jt(e){let a=new Map;if(!e)return a;for(let t of e.split(/\r?\n/)){let n=/^file=(.+) sha256=([a-f0-9]{64})$/.exec(t)??/^file=(.+)=sha256:([a-f0-9]{64})$/.exec(t);if(n?.[1]&&n[2])a.set(n[1],n[2]);let r=/^hash=sha256:([a-f0-9]{64})$/.exec(t);if(r?.[1]&&!a.has("SKILL.md"))a.set("SKILL.md",r[1])}return a}function ke(e,a){let t=Pt(I(e,...a.split("/")));if(t!==e&&t.startsWith(`${e}${Tt}`))return t;throw Error(`Unsafe skill file path '${a}'.`)}async function Ut(e,a,t){let n=I(t,e.name),r=I(n,Et),i=await V(r),s=jt(i);if(await V(I(n,"SKILL.md"))!==null&&i===null)return{name:e.name,action:"skipped_foreign"};let m=!1,ee=!1;for(let x of e.files){let _=ke(n,x.relativePath),P=await V(_);if(P===x.content)continue;m=!0;let te=s.get(x.relativePath);if(P!==null&&(te?Fe(P)!==te:i!==null))await M(`${_}.backup`,P,"utf8"),ee=!0}if(!m&&i===be(e,a))return{name:e.name,action:"unchanged"};let st=i!==null;for(let x of e.files){let _=ke(n,x.relativePath);await _t(At(_),{recursive:!0}),await M(_,x.content,"utf8")}return await M(r,be(e,a),"utf8"),{name:e.name,action:ee?"updated_with_backup":st?"updated":"installed"}}function Re(){if(process.env.npm_package_version)return process.env.npm_package_version;try{let e=Ct(import.meta.url);for(let a of["../package.json","../../package.json"])try{let t=e(a);if(t.version)return t.version}catch{}}catch{}return"0.0.0"}async function Nt(e,a=xe()){let t=Ot(a);return Promise.all(ye.map((n)=>Ut(n,e,t)))}async function Se(e,a){try{let n=(await Nt(e)).filter((r)=>r.action!=="unchanged"&&r.action!=="skipped_foreign");if(n.length>0)a("info",`Flow synced skills (${n.map((r)=>`${r.name}:${r.action}`).join(", ")}). Restart OpenCode if skills were just installed.`)}catch(t){a("warn",`Flow skill sync failed: ${t instanceof Error?t.message:String(t)}`)}}import{randomUUID as Vt}from"node:crypto";import{mkdir as N,open as Te,readFile as je,rename as Jt,rm as q,writeFile as Ee}from"node:fs/promises";import{homedir as Bt}from"node:os";import{dirname as Oe,isAbsolute as vn,join as F,parse as Gt,resolve as Ue}from"node:path";import{setTimeout as Kt}from"node:timers/promises";function qt(e){let a=[],t=0;while(t<e.length){let n=e[t];if(n==="{"){a.push({isObject:!0,keys:new Set,awaitingKey:!0}),t+=1;continue}if(n==="["){a.push({isObject:!1,keys:new Set,awaitingKey:!1}),t+=1;continue}if(n==="}"||n==="]"){a.pop(),t+=1;continue}if(n===","){let r=a.at(-1);if(r?.isObject)r.awaitingKey=!0;t+=1;continue}if(n===":"){let r=a.at(-1);if(r?.isObject)r.awaitingKey=!1;t+=1;continue}if(n==='"'){let r=t+1;while(r<e.length){if(e[r]==="\\"){r+=2;continue}if(e[r]==='"')break;r+=1}let i=a.at(-1);if(i?.isObject&&i.awaitingKey){let s=JSON.parse(e.slice(t,r+1));if(i.keys.has(s))return s;i.keys.add(s)}t=r+1;continue}t+=1}return null}function _e(e,a){if(e.trim().length===0)return{ok:!1,error:`${a} is empty.`};let t;try{t=JSON.parse(e)}catch(r){return{ok:!1,error:r instanceof Error?`${a} is not valid JSON: ${r.message}`:`${a} is not valid JSON.`}}if(t===null||typeof t!=="object"||Array.isArray(t))return{ok:!1,error:`${a} must be a JSON object.`};let n=qt(e);if(n)return{ok:!1,error:`${a} has duplicate key '${n}'.`};return{ok:!0,value:t}}import{z as o}from"zod";var v=/^[a-z0-9]+(?:-[a-z0-9]+)*$/,C="Feature ids must be lowercase kebab-case",Ie=o.enum(["pending","in_progress","completed","blocked"]),Dt=o.enum(["planning","ready","running","blocked","completed"]),$t=o.enum(["passed","failed"]),Wt=o.enum(["passed","failed"]),J=o.enum(["targeted","broad"]),H=o.enum(["broad","detailed"]),zt=o.object({summary:o.string().min(1),severity:o.enum(["blocking","advisory"]).default("blocking")}).strict(),T=o.object({status:$t,summary:o.string().min(1),blockingFindings:o.array(zt).default([])}).strict(),B=T.extend({reviewDepth:H}).strict(),G=o.object({command:o.string().min(1),status:Wt,summary:o.string().min(1)}).strict(),K=o.object({path:o.string().min(1)}).strict(),Ce=o.object({id:o.string().regex(v,C),title:o.string().min(1),summary:o.string().min(1),status:Ie.default("pending"),targets:o.array(o.string().min(1)).default([]),validation:o.array(o.string().min(1)).default([]),dependsOn:o.array(o.string().regex(v)).default([])}).strict(),Ae=o.object({summary:o.string().min(1),overview:o.string().min(1),requirements:o.array(o.string().min(1)).default([]),decisions:o.array(o.string().min(1)).default([]),finalReviewPolicy:H.default("detailed"),features:o.array(Ce).min(1)}).strict(),E=Ae.omit({features:!0}).extend({finalReviewPolicy:H.optional(),features:o.array(Ce.omit({status:!0}).extend({status:Ie.optional(),targets:o.array(o.string().min(1)).optional(),validation:o.array(o.string().min(1)).optional(),dependsOn:o.array(o.string().regex(v)).optional()}).strict()).min(1)}),Pe=o.object({kind:o.enum(["completed","blocked","needs_input","replan_required"]).default("completed"),summary:o.string().min(1).optional(),resolutionHint:o.string().min(1).optional()}).strict(),Lt=o.object({kind:o.enum(["blocked","needs_input","replan_required"]).default("needs_input"),summary:o.string().min(1),resolutionHint:o.string().min(1).optional()}).strict(),O=o.discriminatedUnion("status",[o.object({status:o.literal("ok"),featureId:o.string().regex(v,C),summary:o.string().min(1),artifactsChanged:o.array(K).default([]),validationRun:o.array(G).default([]),validationScope:J,featureReview:T,finalReview:B.optional(),outcome:Pe.optional()}).strict(),o.object({status:o.literal("needs_input"),featureId:o.string().regex(v,C),summary:o.string().min(1),artifactsChanged:o.array(K).default([]),validationRun:o.array(G).default([]),validationScope:J.optional(),featureReview:T.optional(),finalReview:B.optional(),outcome:Lt}).strict()]).superRefine((e,a)=>{if(e.status==="ok"&&e.outcome?.kind&&e.outcome.kind!=="completed")a.addIssue({code:"custom",path:["outcome","kind"],message:'ok worker results must use outcome.kind "completed".'})}),Mt=o.object({featureId:o.string().regex(v,C),status:o.enum(["completed","blocked","needs_input"]),summary:o.string().min(1),recordedAt:o.string().min(1),artifactsChanged:o.array(K).default([]),validationRun:o.array(G).default([]),validationScope:J.optional(),featureReview:T.optional(),finalReview:B.optional(),outcome:Pe.optional()}).strict(),j=o.object({version:o.literal(2),id:o.string().min(1),goal:o.string().min(1),status:Dt,approval:o.enum(["pending","approved"]),plan:Ae.nullable(),activeFeatureId:o.string().regex(v,C).nullable(),history:o.array(Mt).default([]),closure:o.object({kind:o.enum(["completed","deferred","abandoned"]),summary:o.string().min(1),recordedAt:o.string().min(1)}).strict().nullable(),lastError:o.object({tool:o.string().min(1),summary:o.string().min(1),recovery:o.string().min(1).optional(),recordedAt:o.string().min(1)}).strict().nullable().default(null),timestamps:o.object({createdAt:o.string().min(1),updatedAt:o.string().min(1),completedAt:o.string().min(1).nullable()}).strict()}).strict();class D extends Error{code="INVALID_FLOW_WORKSPACE_ROOT";constructor(e){super(e);this.name="InvalidFlowWorkspaceRootError"}}function X(e){let a=e?.trim();if(!a)return null;let t=Ue(a);return Gt(t).root===t?null:t}function $(e){let a=X(e);if(!a)throw new D("Flow requires a non-root workspace path.");if(a===Ue(process.env.HOME??Bt()))throw new D("Flow refuses to use $HOME itself as a mutable workspace root.");return a}function Ne(e){let a=X(e.worktree)??X(e.directory);if(!a)throw new D("Flow could not resolve a workspace root from tool context.");return $(a)}function W(e){return F(e,".flow")}function Y(e){return F(W(e),"session.json")}function qe(e){return F(W(e),"history")}function Ht(e,a){if(!/^[a-zA-Z0-9_-]+$/.test(a))throw Error("Invalid session id.");return F(qe(e),`${a}.json`)}async function De(e,a){await N(Oe(e),{recursive:!0});let t=`${e}.${process.pid}.${Vt()}.tmp`,n=await Te(t,"w");try{await n.writeFile(a,"utf8"),await n.sync()}catch(i){throw await n.close(),await q(t,{force:!0}),i}await n.close();try{await Jt(t,e)}catch(i){throw await q(t,{force:!0}),i}let r=await Te(Oe(e),"r");try{await r.sync()}finally{await r.close()}}var U=new Map,Xt=30000,Yt=25;async function Zt(e){let a=W(e),t=F(a,"session.lock"),n=Date.now();while(!0)try{return await N(t,{recursive:!1}),async()=>{await q(t,{recursive:!0,force:!0})}}catch(r){let i=r.code;if(i==="ENOENT"){await N(a,{recursive:!0});continue}if(i!=="EEXIST")throw r;if(Date.now()-n>Xt)throw Error(`Timed out waiting for Flow session lock at ${t}.`);await Kt(Yt)}}async function $e(e,a){let t=U.get(e)??Promise.resolve(),n=()=>{},r=new Promise((d)=>{n=d}),i=t.catch(()=>{return}).then(()=>r);U.set(e,i);let s=null;try{return await t.catch(()=>{return}),s=await Zt(e),await a()}finally{try{await s?.()}finally{if(n(),U.get(e)===i)U.delete(e)}}}async function A(e){let a=$(e),t;try{t=await je(Y(a),"utf8")}catch(r){if(r.code==="ENOENT")return null;throw r}let n=_e(t,"Flow session file");if(!n.ok)throw Error(n.error);return j.parse(n.value)}async function w(e,a){let t=$(e),n=j.parse(a);return await De(Y(t),`${JSON.stringify(n,null,2)}
760
- `),await We(t),n}async function Z(e,a){let t=$(e);await N(qe(t),{recursive:!0}),await De(Ht(t,a.id),`${JSON.stringify(j.parse(a),null,2)}
761
- `),await q(Y(t),{force:!0}),await We(t)}async function We(e){let a=F(W(e),".gitignore");try{let t=await je(a,"utf8");if(t.includes("session.lock"))return;await Ee(a,`${t.trimEnd()}
762
- session.lock/
763
- `,"utf8")}catch(t){if(t.code!=="ENOENT")throw t;await Ee(a,`session.lock/
764
- `,"utf8")}}var Qt={"flow-reviewer":{mode:"subagent",hidden:!0,description:"Internal read-only reviewer for Flow-guided work.",prompt:"Load `flow-review`. Inspect the current work read-only, report findings, and do not mutate Flow state.",permission:{edit:"deny",bash:"deny",task:{"*":"deny"},"flow_*":"deny",flow_status:"allow"}},"flow-evidence-worker":{mode:"subagent",hidden:!0,description:"Internal read-only evidence worker for Flow planning and execution support.",prompt:"Use Flow evidence mode. Inspect only the assigned slice, do not edit files, do not call state-changing Flow tools, and return scope, evidence inspected, findings or facts, gaps, and suggested Flow follow-ups.",permission:{edit:"deny",bash:"deny",task:{"*":"deny"},"flow_*":"deny",flow_status:"allow"}},"flow-validation-worker":{mode:"subagent",hidden:!0,description:"Internal validation worker for Flow check selection and command evidence.",prompt:"Use Flow validation mode. Run only manager-specified commands or propose focused checks, do not edit files, do not call state-changing Flow tools, and report exact command, status, raw outcome summary, coverage, and gaps.",permission:{edit:"deny",bash:"ask",task:{"*":"deny"},"flow_*":"deny",flow_status:"allow"}},"flow-audit-worker":{mode:"subagent",hidden:!0,description:"Internal read-only audit worker for refuted or surviving finding candidates.",prompt:"Use Flow audit mode. Inspect only the assigned slice, actively refute candidate findings before reporting them, do not edit files, do not call state-changing Flow tools, and return evidence, guards checked, gaps, and follow-ups.",permission:{edit:"deny",bash:"ask",task:{"*":"deny"},"flow_*":"deny",flow_status:"allow"}},"flow-candidate-worker":{mode:"subagent",hidden:!0,description:"Internal candidate implementation worker for isolated Flow worktrees or exact non-overlapping path ownership.",prompt:"Use Flow candidate-implementation mode only when the manager assigned an isolated worktree or exact non-overlapping path ownership. Do not edit .flow/**, do not call state-changing Flow tools, do not complete Flow state, and return changed or proposed patch, verification run, merge risks, and manager follow-ups.",permission:{edit:"ask",bash:"ask",task:{"*":"deny"},"flow_*":"deny",flow_status:"allow"}}},ea={"flow-auto":{description:"Drive Flow skills against the minimal runtime ledger",template:"Load the `flow` skill and drive the Flow loop until completion or a real blocker: $ARGUMENTS"},"flow-plan":{description:"Create or approve a Flow plan",template:"Load the `flow-plan` skill and plan: $ARGUMENTS"},"flow-run":{description:"Run one approved Flow feature",template:"Load the `flow-run` skill and execute the next approved feature. $ARGUMENTS"},"flow-review":{description:"Run a read-only Flow review",agent:"flow-reviewer",subtask:!0,template:"Load the `flow-review` skill and review: $ARGUMENTS"},"flow-status":{description:"Inspect the active Flow session",template:"Call flow_status and report the session state and next action."}};function ta(){return{agent:Object.fromEntries(Object.entries(Qt).map(([e,a])=>{let t=a.permission?{...a.permission,...a.permission.task?{task:{...a.permission.task}}:{}}:void 0;return[e,{...a,...t?{permission:t}:{}}]})),command:Object.fromEntries(Object.entries(ea).map(([e,a])=>[e,{...a}]))}}function ze(e){let a=ta();e.agent={...e.agent??{},...a.agent},e.command={...e.command??{},...a.command}}function Le(e){return async(a)=>{ze(a)}}function z(e){let a=e?.client,t=a?.app?.log;return(n,r)=>{if(typeof t!=="function")return;try{t.call(a?.app,{body:{service:"opencode-plugin-flow",level:n,message:r}})}catch{}}}import{z as p}from"zod";import{randomUUID as na}from"node:crypto";var aa=null;function h(){return aa?.()??new Date().toISOString()}function u(e){return{ok:!0,value:e}}function l(e,a,t){return{ok:!1,message:e,...a?{recovery:a}:{},...t?{session:t}:{}}}function ra(e){let a=E.parse(e);return{summary:a.summary,overview:a.overview,requirements:a.requirements??[],decisions:a.decisions??[],finalReviewPolicy:a.finalReviewPolicy??"detailed",features:a.features.map((t)=>({id:t.id,title:t.title,summary:t.summary,status:"pending",targets:t.targets??[],validation:t.validation??[],dependsOn:t.dependsOn??[]}))}}function oa(e){let a=new Set;for(let s of e.features){if(a.has(s.id))return`Duplicate feature id '${s.id}'.`;a.add(s.id)}for(let s of e.features)for(let d of s.dependsOn){if(!a.has(d))return`Feature '${s.id}' depends on unknown feature '${d}'.`;if(d===s.id)return`Feature '${s.id}' cannot depend on itself.`}let t=new Set,n=new Set,r=new Map(e.features.map((s)=>[s.id,s]));function i(s){if(n.has(s))return!1;if(t.has(s))return!0;t.add(s);for(let d of r.get(s)?.dependsOn??[])if(i(d))return!0;return t.delete(s),n.add(s),!1}return e.features.some((s)=>i(s.id))?"Feature dependencies contain a cycle.":null}function L(e){let a=h();return{version:2,id:na(),goal:e,status:"planning",approval:"pending",plan:null,activeFeatureId:null,history:[],closure:null,lastError:null,timestamps:{createdAt:a,updatedAt:a,completedAt:null}}}function y(e){return{...e,timestamps:{...e.timestamps,updatedAt:h()}}}function Je(e,a){if(e.approval==="approved"||e.status!=="planning")return l("Approved plans cannot be changed. Reset or start a new session.");let t=ra(a),n=oa(t);if(n)return l(n);return u(y({...e,status:"planning",approval:"pending",plan:t,activeFeatureId:null,history:[],closure:null,lastError:null,timestamps:{...e.timestamps,completedAt:null}}))}function Be(e){if(!e.plan)return l("There is no draft plan to approve.");if(e.approval==="approved"&&e.status==="ready")return u(e);if(e.status!=="planning")return l("Only planning sessions can be approved.");return u(y({...e,approval:"approved",status:"ready"}))}function Me(e,a){return e.status==="pending"&&e.dependsOn.every((t)=>a.has(t))}function ia(e,a){let t=new Set(e.filter((i)=>i.status==="completed").map((i)=>i.id)),n=new Map(e.map((i)=>[i.id,i]));if(a){let i=n.get(a);if(!i)return l(`Feature '${a}' is not in the plan.`);if(i.status==="completed")return l(`Feature '${a}' is already completed.`);if(i.status!=="pending")return l(`Feature '${a}' is ${i.status} and must be reset before it can run.`);if(!Me(i,t))return l(`Feature '${a}' has incomplete dependencies.`);return u(i)}let r=e.find((i)=>Me(i,t));return r?u(r):l("No runnable feature is available.")}function Q(e,a,t){return e.map((n)=>n.id===a?{...n,status:t}:n.status==="in_progress"&&t==="in_progress"?{...n,status:"pending"}:n)}function Ge(e,a){if(e.status==="completed")return l("This Flow session is already completed.");if(!e.plan||e.approval!=="approved")return l("There is no approved plan to run.");if(e.status==="blocked")return l("Blocked features must be reset before rerun.","Call flow_feature_reset for the blocked feature, then start it again.");if(e.activeFeatureId){if(!a||a===e.activeFeatureId){let i=e.plan.features.find((s)=>s.id===e.activeFeatureId);if(i)return u({session:e,feature:i})}return l(`Feature '${e.activeFeatureId}' is already in progress.`)}let t=ia(e.plan.features,a);if(!t.ok)return t;let n={...e.plan,features:Q(e.plan.features,t.value.id,"in_progress")},r=y({...e,status:"running",plan:n,activeFeatureId:t.value.id,lastError:null});return u({session:r,feature:r.plan?.features.find((i)=>i.id===t.value.id)??t.value})}function Ve(e){return e.status==="passed"&&e.blockingFindings.length===0}function sa(e,a){if(!e.plan)return!1;return e.plan.features.every((t)=>t.id===a||t.status==="completed")}function g(e,a,t,n){return l(t,n,{...e,lastError:{tool:a,summary:t,recovery:n,recordedAt:h()}})}function la(e,a){let t=sa(e,a.featureId);if(a.validationRun.length===0)return g(e,"flow_feature_complete","Completion requires recorded validation evidence.","Run the targeted or broad validation command and record the result.");if(!a.validationRun.every((n)=>n.status==="passed"))return g(e,"flow_feature_complete","Completion requires all recorded validation to pass.","Fix failures, rerun validation, then complete the feature.");if(!t&&a.validationScope!=="targeted")return g(e,"flow_feature_complete","Non-final feature completion requires targeted validation.","Record validationScope: targeted for ordinary feature completion.");if(t&&a.validationScope!=="broad")return g(e,"flow_feature_complete","Final feature completion requires broad validation.","Run the project-level gate and record validationScope: broad.");if(!Ve(a.featureReview))return g(e,"flow_feature_complete","Completion requires a passing featureReview with no blocking findings.","Fix or acknowledge the review findings before completing.");if(t){if(!a.finalReview)return g(e,"flow_feature_complete","Final feature completion requires a finalReview.","Run final review and include the finalReview payload.");if(!Ve(a.finalReview))return g(e,"flow_feature_complete","Final completion requires a passing finalReview.","Resolve final review findings before completing the session.");let n=e.plan?.finalReviewPolicy??"detailed";if(a.finalReview.reviewDepth!==n)return g(e,"flow_feature_complete",`Final review depth must match the plan policy '${n}'.`,"Record a finalReview whose reviewDepth matches the approved plan.")}return u(void 0)}function Ke(e,a){if(!e.plan||e.status!=="running"||!e.activeFeatureId)return l("No feature is currently running.");let t=O.parse(a);if(t.featureId!==e.activeFeatureId)return l(`Worker result feature '${t.featureId}' does not match active feature '${e.activeFeatureId}'.`);if(t.status==="needs_input"){let m={featureId:t.featureId,status:"needs_input",summary:t.summary,recordedAt:h(),artifactsChanged:t.artifactsChanged,validationRun:t.validationRun,validationScope:t.validationScope,featureReview:t.featureReview,finalReview:t.finalReview,outcome:t.outcome};return u(y({...e,status:"blocked",activeFeatureId:null,plan:{...e.plan,features:Q(e.plan.features,t.featureId,"blocked")},history:[...e.history,m]}))}let n=la(e,t);if(!n.ok)return n;let r={featureId:t.featureId,status:"completed",summary:t.summary,recordedAt:h(),artifactsChanged:t.artifactsChanged,validationRun:t.validationRun,validationScope:t.validationScope,featureReview:t.featureReview,finalReview:t.finalReview,outcome:t.outcome},i=Q(e.plan.features,t.featureId,"completed"),s=i.every((m)=>m.status==="completed"),d=h();return u(y({...e,status:s?"completed":"ready",activeFeatureId:null,plan:{...e.plan,features:i},history:[...e.history,r],closure:s?{kind:"completed",summary:t.summary,recordedAt:d}:null,lastError:null,timestamps:{...e.timestamps,completedAt:s?d:e.timestamps.completedAt}}))}function ca(e,a){let t=new Set([a]),n=!0;while(n){n=!1;for(let r of e){if(t.has(r.id))continue;if(r.dependsOn.some((i)=>t.has(i)))t.add(r.id),n=!0}}return t}function He(e,a){if(!e.plan)return l("There is no active plan to reset.");if(!e.plan.features.some((s)=>s.id===a))return l(`Feature '${a}' is not in the plan.`);let t=ca(e.plan.features,a),n=e.activeFeatureId&&t.has(e.activeFeatureId)?null:e.activeFeatureId,r=e.plan.features.map((s)=>t.has(s.id)?{...s,status:"pending"}:s),i=e.approval!=="approved"?"planning":n?"running":r.some((s)=>s.status==="blocked")?"blocked":"ready";return u(y({...e,status:i,activeFeatureId:n,plan:{...e.plan,features:r},closure:null,lastError:null,timestamps:{...e.timestamps,completedAt:null}}))}function Xe(e,a,t){if(a==="completed"){if(!e.plan||e.approval!=="approved")return l("Cannot close a Flow session as completed without an approved plan.");let r=e.plan.features.filter((i)=>i.status!=="completed");if(r.length>0)return l("Cannot close a Flow session as completed with unfinished features.",`Unfinished features: ${r.map((i)=>i.id).join(", ")}`);if(e.status!=="completed")return l("Cannot close a Flow session as completed before final completion gates pass.")}let n=h();return u(y({...e,status:a==="completed"?"completed":e.status,activeFeatureId:null,closure:{kind:a,summary:t??`Session closed as ${a}.`,recordedAt:n},timestamps:{...e.timestamps,completedAt:a==="completed"?n:e.timestamps.completedAt}}))}function b(e){if(!e)return{status:"missing_session",summary:"No active Flow session exists.",nextAction:"Start with /flow-plan <goal>."};let a=e.plan?.features??[],t=a.filter((s)=>s.status==="completed"),n=e.history.at(-1)??null,r=e.status==="blocked"?n:null,i=e.activeFeatureId?a.find((s)=>s.id===e.activeFeatureId):null;return{status:e.status,summary:e.closure?.summary??e.lastError?.summary??r?.summary??e.plan?.summary??"Flow session is active.",nextAction:da(e),session:{id:e.id,goal:e.goal,status:e.status,approval:e.approval,activeFeature:i??null,progress:{completed:t.length,total:a.length},features:a,closure:e.closure,lastError:e.lastError,latestHistoryEntry:n,historyCount:e.history.length,timestamps:e.timestamps}}}function da(e){if(!e.plan)return"Save a plan with flow_plan_save.";if(e.approval!=="approved")return"Approve the plan.";if(e.status==="ready")return"Start the next feature.";if(e.status==="running")return"Complete or reset the active feature.";if(e.status==="blocked")return"Reset the blocked feature or close the session.";if(e.status==="completed")return"Close/archive the session or start a new goal.";return"Inspect session state."}var ua=p.object({goal:p.string().trim().min(1).optional(),plan:E.optional()}).strict(),pa=p.object({featureId:p.string().min(1).optional()}).strict(),fa=p.object({featureId:p.string().min(1)}).strict(),ma=p.object({kind:p.enum(["completed","deferred","abandoned"]),summary:p.string().trim().min(1).optional()}).strict();function R(e){return{status:"error",summary:e.message,...e.recovery?{recovery:e.recovery}:{}}}async function S(e,a){return $e(e,async()=>a(await A(e)))}async function Ye(e){return b(await A(e))}async function Ze(e,a){let t=ua.parse(a??{});return S(e,async(n)=>{let r=t.goal??n?.goal;if(!r)return{status:"missing_goal",summary:"Provide a goal before saving a Flow plan.",nextAction:"/flow-plan <goal>"};if(n?.status==="completed")await Z(e,n);let i=n?.status==="completed"?L(r):n??L(r);if(i.goal!==r){if(i.approval==="approved")return{status:"error",summary:"An approved Flow session already exists for a different goal. Close it before starting a new one."}}let s=i.goal===r?i:L(r),d=t.plan?Je(s,t.plan):{ok:!0,value:s};if(!d.ok)return R(d);let m=await w(e,d.value);return{...b(m),status:"ok",summary:t.plan?"Flow plan saved.":"Flow session ready."}})}async function Qe(e){return S(e,async(a)=>{if(!a)return{status:"missing_session",summary:"No active Flow session exists.",nextAction:"/flow-plan <goal>"};let t=Be(a);if(!t.ok)return R(t);let n=await w(e,t.value);return{...b(n),status:"ok",summary:"Flow plan approved."}})}async function et(e,a){let t=pa.parse(a??{});return S(e,async(n)=>{if(!n)return{status:"missing_session",summary:"No active Flow session exists.",nextAction:"/flow-plan <goal>"};let r=Ge(n,t.featureId);if(!r.ok)return R(r);let i=await w(e,r.value.session);return{...b(i),status:"ok",summary:`Started feature '${r.value.feature.id}'.`,feature:r.value.feature}})}async function tt(e,a){let t=O.parse(a??{});return S(e,async(n)=>{if(!n)return{status:"missing_session",summary:"No active Flow session exists.",nextAction:"/flow-plan <goal>"};let r=Ke(n,t);if(!r.ok){if(r.session)await w(e,r.session);return R(r)}let i=await w(e,r.value);return{...b(i),status:"ok",summary:"Feature result recorded."}})}async function at(e,a){let t=fa.parse(a??{});return S(e,async(n)=>{if(!n)return{status:"missing_session",summary:"No active Flow session exists.",nextAction:"/flow-plan <goal>"};let r=He(n,t.featureId);if(!r.ok)return R(r);let i=await w(e,r.value);return{...b(i),status:"ok",summary:`Feature '${t.featureId}' reset.`}})}async function nt(e,a){let t=ma.parse(a??{});return S(e,async(n)=>{if(!n)return{status:"missing_session",summary:"No active Flow session exists.",nextAction:"/flow-plan <goal>"};let r=Xe(n,t.kind,t.summary);if(!r.ok)return R(r);return await Z(e,r.value),{status:"ok",summary:`Flow session closed as ${t.kind}.`,archivedSessionId:r.value.id,closure:r.value.closure}})}import{tool as f}from"@opencode-ai/plugin";var c=f.schema;function rt(e){return JSON.stringify(e,null,2)}function ha(e){return rt({status:"error",summary:e instanceof Error?e.message:String(e)})}async function k(e,a){try{return rt(await a(Ne(e)))}catch(t){return ha(t)}}function ot(e){return z(e)("info","Creating minimal Flow v4 tool surface."),{flow_status:f({description:"Show the active Flow session and next action",args:{},execute:(a,t)=>k(t,Ye)}),flow_plan_save:f({description:"Create or update a draft Flow plan for the active goal",args:{goal:c.string().optional(),plan:c.any().optional()},execute:(a,t)=>k(t,(n)=>Ze(n,a))}),flow_plan_approve:f({description:"Approve the current draft Flow plan",args:{},execute:(a,t)=>k(t,Qe)}),flow_run_start:f({description:"Start the next runnable approved Flow feature",args:{featureId:c.string().optional()},execute:(a,t)=>k(t,(n)=>et(n,a))}),flow_feature_complete:f({description:"Record a completed or blocked active feature with validation and review evidence",args:{status:c.enum(["ok","needs_input"]),featureId:c.string(),summary:c.string(),artifactsChanged:c.array(c.object({path:c.string()})).optional(),validationRun:c.array(c.object({command:c.string(),status:c.enum(["passed","failed"]),summary:c.string()})).optional(),validationScope:c.enum(["targeted","broad"]).optional(),featureReview:c.any().optional(),finalReview:c.any().optional(),outcome:c.any().optional()},execute:(a,t)=>k(t,(n)=>tt(n,a))}),flow_feature_reset:f({description:"Reset one feature and its dependents to pending",args:{featureId:c.string()},execute:(a,t)=>k(t,(n)=>at(n,a))}),flow_session_close:f({description:"Close and archive the active Flow session",args:{kind:c.enum(["completed","deferred","abandoned"]),summary:c.string().optional()},execute:(a,t)=>k(t,(n)=>nt(n,a))})}}async function it(e){let a=e.worktree??e.directory;if(!a)return null;try{let t=await A(a);if(!t)return null;return["Flow session facts:",`- goal: ${t.goal}`,`- status: ${t.status}`,`- approval: ${t.approval}`,`- active feature: ${t.activeFeatureId??"none"}`,`- progress: ${t.plan?.features.filter((n)=>n.status==="completed").length??0}/${t.plan?.features.length??0}`,"Call flow_status before any Flow action."].join(`
765
- `)}catch{return null}}function ga(e){return async(a,t)=>{let n=await it(e);if(n)t.system.push(n)}}var va=async(e)=>{let a=z(e);return a("info","Flow v4 plugin initialized."),await Se(Re(),a),{config:Le(e),tool:ot(e),"experimental.chat.system.transform":ga(e),"experimental.session.compacting":async(t,n)=>{let r=await it(e);if(!r)return;n.context=[...n.context??[],r]}}},wa=va;export{wa as default};
766
-
767
- //# debugId=7C8AC5AB436E3BC964756E2164756E21
1060
+ `;var ke=[{name:"flow",files:[{relativePath:"SKILL.md",content:ie},{relativePath:"references/recovery-playbook.md",content:re},{relativePath:"references/parallel-orchestration.md",content:ne},{relativePath:"references/handoff-format.md",content:ae},{relativePath:"references/verification-gates.md",content:oe}]},{name:"flow-plan",files:[{relativePath:"SKILL.md",content:pe},{relativePath:"references/planning-examples.md",content:ue},{relativePath:"references/parallel-discovery.md",content:de}]},{name:"flow-run",files:[{relativePath:"SKILL.md",content:we},{relativePath:"references/validation-rubric.md",content:ge},{relativePath:"references/audit-rubric.md",content:he}]},{name:"flow-review",files:[{relativePath:"SKILL.md",content:me},{relativePath:"references/review-rubric.md",content:fe}]},{name:"flow-deslop",files:[{relativePath:"SKILL.md",content:ce},{relativePath:"references/smell-rubric.md",content:le},{relativePath:"references/refactor-workflow.md",content:se}]},{name:"flow-ui-quality",files:[{relativePath:"SKILL.md",content:be},{relativePath:"references/ui-rubric.md",content:ve},{relativePath:"references/visual-verification.md",content:ye}]}];var Nt=".flow-skill-version";function Re(){return process.env.HOME??process.env.USERPROFILE??""}function Dt(e=Re()){return I(e,".config","opencode","skills")}function Se(e){return Pt("sha256").update(e).digest("hex")}function xe(e,a){return[`version=${a}`,...e.files.map((t)=>`file=${t.relativePath} sha256=${Se(t.content)}`),""].join(`
1061
+ `)}async function V(e){try{return await Et(e,"utf8")}catch(a){if(a.code==="ENOENT")return null;throw a}}function $t(e){let a=new Map;if(!e)return a;for(let t of e.split(/\r?\n/)){let n=/^file=(.+) sha256=([a-f0-9]{64})$/.exec(t)??/^file=(.+)=sha256:([a-f0-9]{64})$/.exec(t);if(n?.[1]&&n[2])a.set(n[1],n[2]);let r=/^hash=sha256:([a-f0-9]{64})$/.exec(t);if(r?.[1]&&!a.has("SKILL.md"))a.set("SKILL.md",r[1])}return a}function Fe(e,a){let t=Ot(I(e,...a.split("/")));if(t!==e&&t.startsWith(`${e}${qt}`))return t;throw Error(`Unsafe skill file path '${a}'.`)}async function Wt(e,a,t){let n=I(t,e.name),r=I(n,Nt),i=await V(r),s=$t(i);if(await V(I(n,"SKILL.md"))!==null&&i===null)return{name:e.name,action:"skipped_foreign"};let m=!1,ee=!1;for(let x of e.files){let _=Fe(n,x.relativePath),P=await V(_);if(P===x.content)continue;m=!0;let te=s.get(x.relativePath);if(P!==null&&(te?Se(P)!==te:i!==null))await M(`${_}.backup`,P,"utf8"),ee=!0}if(!m&&i===xe(e,a))return{name:e.name,action:"unchanged"};let dt=i!==null;for(let x of e.files){let _=Fe(n,x.relativePath);await Tt(jt(_),{recursive:!0}),await M(_,x.content,"utf8")}return await M(r,xe(e,a),"utf8"),{name:e.name,action:ee?"updated_with_backup":dt?"updated":"installed"}}function _e(){if(process.env.npm_package_version)return process.env.npm_package_version;try{let e=Ut(import.meta.url);for(let a of["../package.json","../../package.json"])try{let t=e(a);if(t.version)return t.version}catch{}}catch{}return"0.0.0"}async function zt(e,a=Re()){let t=Dt(a);return Promise.all(ke.map((n)=>Wt(n,e,t)))}async function Ie(e,a){try{let n=(await zt(e)).filter((r)=>r.action!=="unchanged"&&r.action!=="skipped_foreign");if(n.length>0)a("info",`Flow synced skills (${n.map((r)=>`${r.name}:${r.action}`).join(", ")}). Restart OpenCode if skills were just installed.`)}catch(t){a("warn",`Flow skill sync failed: ${t instanceof Error?t.message:String(t)}`)}}import{randomUUID as Ht}from"node:crypto";import{mkdir as q,open as Ue,readFile as Ne,rename as Xt,rm as N,writeFile as je}from"node:fs/promises";import{homedir as Yt}from"node:os";import{dirname as Oe,isAbsolute as _n,join as F,parse as Zt,resolve as De}from"node:path";import{setTimeout as Qt}from"node:timers/promises";function Lt(e){let a=[],t=0;while(t<e.length){let n=e[t];if(n==="{"){a.push({isObject:!0,keys:new Set,awaitingKey:!0}),t+=1;continue}if(n==="["){a.push({isObject:!1,keys:new Set,awaitingKey:!1}),t+=1;continue}if(n==="}"||n==="]"){a.pop(),t+=1;continue}if(n===","){let r=a.at(-1);if(r?.isObject)r.awaitingKey=!0;t+=1;continue}if(n===":"){let r=a.at(-1);if(r?.isObject)r.awaitingKey=!1;t+=1;continue}if(n==='"'){let r=t+1;while(r<e.length){if(e[r]==="\\"){r+=2;continue}if(e[r]==='"')break;r+=1}let i=a.at(-1);if(i?.isObject&&i.awaitingKey){let s=JSON.parse(e.slice(t,r+1));if(i.keys.has(s))return s;i.keys.add(s)}t=r+1;continue}t+=1}return null}function Ce(e,a){if(e.trim().length===0)return{ok:!1,error:`${a} is empty.`};let t;try{t=JSON.parse(e)}catch(r){return{ok:!1,error:r instanceof Error?`${a} is not valid JSON: ${r.message}`:`${a} is not valid JSON.`}}if(t===null||typeof t!=="object"||Array.isArray(t))return{ok:!1,error:`${a} must be a JSON object.`};let n=Lt(e);if(n)return{ok:!1,error:`${a} has duplicate key '${n}'.`};return{ok:!0,value:t}}import{z as o}from"zod";var w=/^[a-z0-9]+(?:-[a-z0-9]+)*$/,C="Feature ids must be lowercase kebab-case",Ae=o.enum(["pending","in_progress","completed","blocked"]),Mt=o.enum(["planning","ready","running","blocked","completed"]),Vt=o.enum(["passed","failed"]),Bt=o.enum(["passed","failed"]),B=o.enum(["targeted","broad"]),H=o.enum(["broad","detailed"]),Gt=o.object({summary:o.string().min(1),severity:o.enum(["blocking","advisory"]).default("blocking")}).strict(),T=o.object({status:Vt,summary:o.string().min(1),blockingFindings:o.array(Gt).default([])}).strict(),G=T.extend({reviewDepth:H}).strict(),J=o.object({command:o.string().min(1),status:Bt,summary:o.string().min(1)}).strict(),K=o.object({path:o.string().min(1)}).strict(),Pe=o.object({id:o.string().regex(w,C),title:o.string().min(1),summary:o.string().min(1),status:Ae.default("pending"),targets:o.array(o.string().min(1)).default([]),validation:o.array(o.string().min(1)).default([]),dependsOn:o.array(o.string().regex(w)).default([])}).strict(),Te=o.object({summary:o.string().min(1),overview:o.string().min(1),requirements:o.array(o.string().min(1)).default([]),decisions:o.array(o.string().min(1)).default([]),finalReviewPolicy:H.default("detailed"),features:o.array(Pe).min(1)}).strict(),E=Te.omit({features:!0}).extend({finalReviewPolicy:H.optional(),features:o.array(Pe.omit({status:!0}).extend({status:Ae.optional(),targets:o.array(o.string().min(1)).optional(),validation:o.array(o.string().min(1)).optional(),dependsOn:o.array(o.string().regex(w)).optional()}).strict()).min(1)}),Ee=o.object({kind:o.enum(["completed","blocked","needs_input","replan_required"]).default("completed"),summary:o.string().min(1).optional(),resolutionHint:o.string().min(1).optional()}).strict(),Jt=o.object({kind:o.enum(["blocked","needs_input","replan_required"]).default("needs_input"),summary:o.string().min(1),resolutionHint:o.string().min(1).optional()}).strict(),U=o.discriminatedUnion("status",[o.object({status:o.literal("ok"),featureId:o.string().regex(w,C),summary:o.string().min(1),artifactsChanged:o.array(K).default([]),validationRun:o.array(J).default([]),validationScope:B,featureReview:T,finalReview:G.optional(),outcome:Ee.optional()}).strict(),o.object({status:o.literal("needs_input"),featureId:o.string().regex(w,C),summary:o.string().min(1),artifactsChanged:o.array(K).default([]),validationRun:o.array(J).default([]),validationScope:B.optional(),featureReview:T.optional(),finalReview:G.optional(),outcome:Jt}).strict()]).superRefine((e,a)=>{if(e.status==="ok"&&e.outcome?.kind&&e.outcome.kind!=="completed")a.addIssue({code:"custom",path:["outcome","kind"],message:'ok worker results must use outcome.kind "completed".'})}),Kt=o.object({featureId:o.string().regex(w,C),status:o.enum(["completed","blocked","needs_input"]),summary:o.string().min(1),recordedAt:o.string().min(1),artifactsChanged:o.array(K).default([]),validationRun:o.array(J).default([]),validationScope:B.optional(),featureReview:T.optional(),finalReview:G.optional(),outcome:Ee.optional()}).strict(),j=o.object({version:o.literal(2),id:o.string().min(1),goal:o.string().min(1),status:Mt,approval:o.enum(["pending","approved"]),plan:Te.nullable(),activeFeatureId:o.string().regex(w,C).nullable(),history:o.array(Kt).default([]),closure:o.object({kind:o.enum(["completed","deferred","abandoned"]),summary:o.string().min(1),recordedAt:o.string().min(1)}).strict().nullable(),lastError:o.object({tool:o.string().min(1),summary:o.string().min(1),recovery:o.string().min(1).optional(),recordedAt:o.string().min(1)}).strict().nullable().default(null),timestamps:o.object({createdAt:o.string().min(1),updatedAt:o.string().min(1),completedAt:o.string().min(1).nullable()}).strict()}).strict();class D extends Error{code="INVALID_FLOW_WORKSPACE_ROOT";constructor(e){super(e);this.name="InvalidFlowWorkspaceRootError"}}function X(e){let a=e?.trim();if(!a)return null;let t=De(a);return Zt(t).root===t?null:t}function $(e){let a=X(e);if(!a)throw new D("Flow requires a non-root workspace path.");if(a===De(process.env.HOME??Yt()))throw new D("Flow refuses to use $HOME itself as a mutable workspace root.");return a}function $e(e){let a=X(e.worktree)??X(e.directory);if(!a)throw new D("Flow could not resolve a workspace root from tool context.");return $(a)}function W(e){return F(e,".flow")}function Y(e){return F(W(e),"session.json")}function We(e){return F(W(e),"history")}function ea(e,a){if(!/^[a-zA-Z0-9_-]+$/.test(a))throw Error("Invalid session id.");return F(We(e),`${a}.json`)}async function ze(e,a){await q(Oe(e),{recursive:!0});let t=`${e}.${process.pid}.${Ht()}.tmp`,n=await Ue(t,"w");try{await n.writeFile(a,"utf8"),await n.sync()}catch(i){throw await n.close(),await N(t,{force:!0}),i}await n.close();try{await Xt(t,e)}catch(i){throw await N(t,{force:!0}),i}let r=await Ue(Oe(e),"r");try{await r.sync()}finally{await r.close()}}var O=new Map,ta=30000,aa=25;async function na(e){let a=W(e),t=F(a,"session.lock"),n=Date.now();while(!0)try{return await q(t,{recursive:!1}),async()=>{await N(t,{recursive:!0,force:!0})}}catch(r){let i=r.code;if(i==="ENOENT"){await q(a,{recursive:!0});continue}if(i!=="EEXIST")throw r;if(Date.now()-n>ta)throw Error(`Timed out waiting for Flow session lock at ${t}.`);await Qt(aa)}}async function Le(e,a){let t=O.get(e)??Promise.resolve(),n=()=>{},r=new Promise((d)=>{n=d}),i=t.catch(()=>{return}).then(()=>r);O.set(e,i);let s=null;try{return await t.catch(()=>{return}),s=await na(e),await a()}finally{try{await s?.()}finally{if(n(),O.get(e)===i)O.delete(e)}}}async function A(e){let a=$(e),t;try{t=await Ne(Y(a),"utf8")}catch(r){if(r.code==="ENOENT")return null;throw r}let n=Ce(t,"Flow session file");if(!n.ok)throw Error(n.error);return j.parse(n.value)}async function v(e,a){let t=$(e),n=j.parse(a);return await ze(Y(t),`${JSON.stringify(n,null,2)}
1062
+ `),await Me(t),n}async function Z(e,a){let t=$(e);await q(We(t),{recursive:!0}),await ze(ea(t,a.id),`${JSON.stringify(j.parse(a),null,2)}
1063
+ `),await N(Y(t),{force:!0}),await Me(t)}var qe=["session.json","history/","session.lock/",".gitignore",""].join(`
1064
+ `);async function Me(e){let a=F(W(e),".gitignore");try{if((await Ne(a,"utf8")).trim()==="session.lock/")await je(a,qe,"utf8")}catch(t){if(t.code!=="ENOENT")throw t;await je(a,qe,"utf8")}}var ra={"flow-reviewer":{mode:"subagent",hidden:!0,description:"Internal read-only reviewer for Flow-guided work.",prompt:"Load `flow-review`. Inspect the current work read-only, report coverage, evidence-backed findings, confidence, gaps, and manager follow-ups, and do not mutate Flow state.",permission:{edit:"deny",bash:"deny",task:{"*":"deny"},"flow_*":"deny",flow_status:"allow"}},"flow-evidence-worker":{mode:"subagent",hidden:!0,description:"Internal read-only evidence worker for Flow planning and execution support.",prompt:"Use Flow evidence mode. Inspect only the assigned slice, do not edit files, do not call state-changing Flow tools, and return coverage, evidence inspected, confidence-tagged findings or facts, gaps, and manager follow-ups.",permission:{edit:"deny",bash:"deny",task:{"*":"deny"},"flow_*":"deny",flow_status:"allow"}},"flow-validation-worker":{mode:"subagent",hidden:!0,description:"Internal validation worker for Flow check selection and command evidence.",prompt:"Use Flow validation mode. Run only manager-specified commands or propose focused checks, do not edit files, do not call state-changing Flow tools, and report exact command, status, raw outcome summary, coverage, confidence, gaps, and manager follow-ups.",permission:{edit:"deny",bash:"ask",task:{"*":"deny"},"flow_*":"deny",flow_status:"allow"}},"flow-audit-worker":{mode:"subagent",hidden:!0,description:"Internal read-only audit worker for refuted or surviving finding candidates.",prompt:"Use Flow audit mode. Inspect only the assigned slice, actively refute candidate findings before reporting them, do not edit files, do not call state-changing Flow tools, and return coverage, evidence, guards checked, confidence, gaps, and manager follow-ups.",permission:{edit:"deny",bash:"ask",task:{"*":"deny"},"flow_*":"deny",flow_status:"allow"}},"flow-candidate-worker":{mode:"subagent",hidden:!0,description:"Internal candidate implementation worker for isolated Flow worktrees or exact non-overlapping path ownership.",prompt:"Use Flow candidate-implementation mode only when the manager assigned an isolated worktree or exact non-overlapping path ownership. Do not edit .flow/**, do not call state-changing Flow tools, do not complete Flow state, and return changed or proposed patch, verification run, coverage, confidence, merge risks, and manager follow-ups.",permission:{edit:"ask",bash:"ask",task:{"*":"deny"},"flow_*":"deny",flow_status:"allow"}},"flow-verifier-worker":{mode:"subagent",hidden:!0,description:"Internal verifier worker for checking Flow worker claims against cited evidence.",prompt:"Use Flow verifier mode. Verify only the assigned claims against the provided sources, commands, counts, or current docs. Do not generate new scope, do not edit files, do not call state-changing Flow tools, and return supported, partly-supported, unsupported, or source-not-found per claim with evidence, confidence, gaps, and manager follow-ups.",permission:{edit:"deny",bash:"ask",task:{"*":"deny"},"flow_*":"deny",flow_status:"allow"}}},oa={"flow-auto":{description:"Drive Flow skills against the minimal runtime ledger",template:"Load the `flow` skill and drive the Flow loop until completion or a real blocker: $ARGUMENTS"},"flow-plan":{description:"Create or approve a Flow plan",template:"Load the `flow-plan` skill and plan: $ARGUMENTS"},"flow-run":{description:"Run one approved Flow feature",template:"Load the `flow-run` skill and execute the next approved feature. $ARGUMENTS"},"flow-review":{description:"Run a read-only Flow review",agent:"flow-reviewer",subtask:!0,template:"Load the `flow-review` skill and review: $ARGUMENTS"},"flow-status":{description:"Inspect the active Flow session",template:"Call flow_status and report the session state and next action."}};function ia(){return{agent:Object.fromEntries(Object.entries(ra).map(([e,a])=>{let t=a.permission?{...a.permission,...a.permission.task?{task:{...a.permission.task}}:{}}:void 0;return[e,{...a,...t?{permission:t}:{}}]})),command:Object.fromEntries(Object.entries(oa).map(([e,a])=>[e,{...a}]))}}function Ve(e){let a=ia();e.agent={...e.agent??{},...a.agent},e.command={...e.command??{},...a.command}}function Be(e){return async(a)=>{Ve(a)}}function z(e){let a=e?.client,t=a?.app?.log;return(n,r)=>{if(typeof t!=="function")return;try{t.call(a?.app,{body:{service:"opencode-plugin-flow",level:n,message:r}})}catch{}}}import{z as p}from"zod";import{randomUUID as la}from"node:crypto";var sa=null;function h(){return sa?.()??new Date().toISOString()}function u(e){return{ok:!0,value:e}}function l(e,a,t){return{ok:!1,message:e,...a?{recovery:a}:{},...t?{session:t}:{}}}function ca(e){let a=E.parse(e);return{summary:a.summary,overview:a.overview,requirements:a.requirements??[],decisions:a.decisions??[],finalReviewPolicy:a.finalReviewPolicy??"detailed",features:a.features.map((t)=>({id:t.id,title:t.title,summary:t.summary,status:"pending",targets:t.targets??[],validation:t.validation??[],dependsOn:t.dependsOn??[]}))}}function da(e){let a=new Set;for(let s of e.features){if(a.has(s.id))return`Duplicate feature id '${s.id}'.`;a.add(s.id)}for(let s of e.features)for(let d of s.dependsOn){if(!a.has(d))return`Feature '${s.id}' depends on unknown feature '${d}'.`;if(d===s.id)return`Feature '${s.id}' cannot depend on itself.`}let t=new Set,n=new Set,r=new Map(e.features.map((s)=>[s.id,s]));function i(s){if(n.has(s))return!1;if(t.has(s))return!0;t.add(s);for(let d of r.get(s)?.dependsOn??[])if(i(d))return!0;return t.delete(s),n.add(s),!1}return e.features.some((s)=>i(s.id))?"Feature dependencies contain a cycle.":null}function L(e){let a=h();return{version:2,id:la(),goal:e,status:"planning",approval:"pending",plan:null,activeFeatureId:null,history:[],closure:null,lastError:null,timestamps:{createdAt:a,updatedAt:a,completedAt:null}}}function y(e){return{...e,timestamps:{...e.timestamps,updatedAt:h()}}}function Ke(e,a){if(e.approval==="approved"||e.status!=="planning")return l("Approved plans cannot be changed. Reset or start a new session.");let t=ca(a),n=da(t);if(n)return l(n);return u(y({...e,status:"planning",approval:"pending",plan:t,activeFeatureId:null,history:[],closure:null,lastError:null,timestamps:{...e.timestamps,completedAt:null}}))}function He(e){if(!e.plan)return l("There is no draft plan to approve.");if(e.approval==="approved"&&e.status==="ready")return u(e);if(e.status!=="planning")return l("Only planning sessions can be approved.");return u(y({...e,approval:"approved",status:"ready"}))}function Ge(e,a){return e.status==="pending"&&e.dependsOn.every((t)=>a.has(t))}function ua(e,a){let t=new Set(e.filter((i)=>i.status==="completed").map((i)=>i.id)),n=new Map(e.map((i)=>[i.id,i]));if(a){let i=n.get(a);if(!i)return l(`Feature '${a}' is not in the plan.`);if(i.status==="completed")return l(`Feature '${a}' is already completed.`);if(i.status!=="pending")return l(`Feature '${a}' is ${i.status} and must be reset before it can run.`);if(!Ge(i,t))return l(`Feature '${a}' has incomplete dependencies.`);return u(i)}let r=e.find((i)=>Ge(i,t));return r?u(r):l("No runnable feature is available.")}function Q(e,a,t){return e.map((n)=>n.id===a?{...n,status:t}:n.status==="in_progress"&&t==="in_progress"?{...n,status:"pending"}:n)}function Xe(e,a){if(e.status==="completed")return l("This Flow session is already completed.");if(!e.plan||e.approval!=="approved")return l("There is no approved plan to run.");if(e.status==="blocked")return l("Blocked features must be reset before rerun.","Call flow_feature_reset for the blocked feature, then start it again.");if(e.activeFeatureId){if(!a||a===e.activeFeatureId){let i=e.plan.features.find((s)=>s.id===e.activeFeatureId);if(i)return u({session:e,feature:i})}return l(`Feature '${e.activeFeatureId}' is already in progress.`)}let t=ua(e.plan.features,a);if(!t.ok)return t;let n={...e.plan,features:Q(e.plan.features,t.value.id,"in_progress")},r=y({...e,status:"running",plan:n,activeFeatureId:t.value.id,lastError:null});return u({session:r,feature:r.plan?.features.find((i)=>i.id===t.value.id)??t.value})}function Je(e){return e.status==="passed"&&e.blockingFindings.length===0}function pa(e,a){if(!e.plan)return!1;return e.plan.features.every((t)=>t.id===a||t.status==="completed")}function g(e,a,t,n){return l(t,n,{...e,lastError:{tool:a,summary:t,recovery:n,recordedAt:h()}})}function fa(e,a){let t=pa(e,a.featureId);if(a.validationRun.length===0)return g(e,"flow_feature_complete","Completion requires recorded validation evidence.","Run the targeted or broad validation command and record the result.");if(!a.validationRun.every((n)=>n.status==="passed"))return g(e,"flow_feature_complete","Completion requires all recorded validation to pass.","Fix failures, rerun validation, then complete the feature.");if(!t&&a.validationScope!=="targeted")return g(e,"flow_feature_complete","Non-final feature completion requires targeted validation.","Record validationScope: targeted for ordinary feature completion.");if(t&&a.validationScope!=="broad")return g(e,"flow_feature_complete","Final feature completion requires broad validation.","Run the project-level gate and record validationScope: broad.");if(!Je(a.featureReview))return g(e,"flow_feature_complete","Completion requires a passing featureReview with no blocking findings.","Fix or acknowledge the review findings before completing.");if(t){if(!a.finalReview)return g(e,"flow_feature_complete","Final feature completion requires a finalReview.","Run final review and include the finalReview payload.");if(!Je(a.finalReview))return g(e,"flow_feature_complete","Final completion requires a passing finalReview.","Resolve final review findings before completing the session.");let n=e.plan?.finalReviewPolicy??"detailed";if(a.finalReview.reviewDepth!==n)return g(e,"flow_feature_complete",`Final review depth must match the plan policy '${n}'.`,"Record a finalReview whose reviewDepth matches the approved plan.")}return u(void 0)}function Ye(e,a){if(!e.plan||e.status!=="running"||!e.activeFeatureId)return l("No feature is currently running.");let t=U.parse(a);if(t.featureId!==e.activeFeatureId)return l(`Worker result feature '${t.featureId}' does not match active feature '${e.activeFeatureId}'.`);if(t.status==="needs_input"){let m={featureId:t.featureId,status:"needs_input",summary:t.summary,recordedAt:h(),artifactsChanged:t.artifactsChanged,validationRun:t.validationRun,validationScope:t.validationScope,featureReview:t.featureReview,finalReview:t.finalReview,outcome:t.outcome};return u(y({...e,status:"blocked",activeFeatureId:null,plan:{...e.plan,features:Q(e.plan.features,t.featureId,"blocked")},history:[...e.history,m]}))}let n=fa(e,t);if(!n.ok)return n;let r={featureId:t.featureId,status:"completed",summary:t.summary,recordedAt:h(),artifactsChanged:t.artifactsChanged,validationRun:t.validationRun,validationScope:t.validationScope,featureReview:t.featureReview,finalReview:t.finalReview,outcome:t.outcome},i=Q(e.plan.features,t.featureId,"completed"),s=i.every((m)=>m.status==="completed"),d=h();return u(y({...e,status:s?"completed":"ready",activeFeatureId:null,plan:{...e.plan,features:i},history:[...e.history,r],closure:s?{kind:"completed",summary:t.summary,recordedAt:d}:null,lastError:null,timestamps:{...e.timestamps,completedAt:s?d:e.timestamps.completedAt}}))}function ma(e,a){let t=new Set([a]),n=!0;while(n){n=!1;for(let r of e){if(t.has(r.id))continue;if(r.dependsOn.some((i)=>t.has(i)))t.add(r.id),n=!0}}return t}function Ze(e,a){if(!e.plan)return l("There is no active plan to reset.");if(!e.plan.features.some((s)=>s.id===a))return l(`Feature '${a}' is not in the plan.`);let t=ma(e.plan.features,a),n=e.activeFeatureId&&t.has(e.activeFeatureId)?null:e.activeFeatureId,r=e.plan.features.map((s)=>t.has(s.id)?{...s,status:"pending"}:s),i=e.approval!=="approved"?"planning":n?"running":r.some((s)=>s.status==="blocked")?"blocked":"ready";return u(y({...e,status:i,activeFeatureId:n,plan:{...e.plan,features:r},closure:null,lastError:null,timestamps:{...e.timestamps,completedAt:null}}))}function Qe(e,a,t){if(a==="completed"){if(!e.plan||e.approval!=="approved")return l("Cannot close a Flow session as completed without an approved plan.");let r=e.plan.features.filter((i)=>i.status!=="completed");if(r.length>0)return l("Cannot close a Flow session as completed with unfinished features.",`Unfinished features: ${r.map((i)=>i.id).join(", ")}`);if(e.status!=="completed")return l("Cannot close a Flow session as completed before final completion gates pass.")}let n=h();return u(y({...e,status:a==="completed"?"completed":e.status,activeFeatureId:null,closure:{kind:a,summary:t??`Session closed as ${a}.`,recordedAt:n},timestamps:{...e.timestamps,completedAt:a==="completed"?n:e.timestamps.completedAt}}))}function b(e){if(!e)return{status:"missing_session",summary:"No active Flow session exists.",nextAction:"Start with /flow-plan <goal>."};let a=e.plan?.features??[],t=a.filter((s)=>s.status==="completed"),n=e.history.at(-1)??null,r=e.status==="blocked"?n:null,i=e.activeFeatureId?a.find((s)=>s.id===e.activeFeatureId):null;return{status:e.status,summary:e.closure?.summary??e.lastError?.summary??r?.summary??e.plan?.summary??"Flow session is active.",nextAction:ha(e),session:{id:e.id,goal:e.goal,status:e.status,approval:e.approval,activeFeature:i??null,progress:{completed:t.length,total:a.length},features:a,closure:e.closure,lastError:e.lastError,latestHistoryEntry:n,historyCount:e.history.length,timestamps:e.timestamps}}}function ha(e){if(!e.plan)return"Save a plan with flow_plan_save.";if(e.approval!=="approved")return"Approve the plan.";if(e.status==="ready")return"Start the next feature.";if(e.status==="running")return"Complete or reset the active feature.";if(e.status==="blocked")return"Reset the blocked feature or close the session.";if(e.status==="completed")return"Close/archive the session or start a new goal.";return"Inspect session state."}var ga=p.object({goal:p.string().trim().min(1).optional(),plan:E.optional()}).strict(),wa=p.object({featureId:p.string().min(1).optional()}).strict(),va=p.object({featureId:p.string().min(1)}).strict(),ya=p.object({kind:p.enum(["completed","deferred","abandoned"]),summary:p.string().trim().min(1).optional()}).strict();function R(e){return{status:"error",summary:e.message,...e.recovery?{recovery:e.recovery}:{}}}async function S(e,a){return Le(e,async()=>a(await A(e)))}async function et(e){return b(await A(e))}async function tt(e,a){let t=ga.parse(a??{});return S(e,async(n)=>{let r=t.goal??n?.goal;if(!r)return{status:"missing_goal",summary:"Provide a goal before saving a Flow plan.",nextAction:"/flow-plan <goal>"};if(n?.status==="completed")await Z(e,n);let i=n?.status==="completed"?L(r):n??L(r);if(i.goal!==r){if(i.approval==="approved")return{status:"error",summary:"An approved Flow session already exists for a different goal. Close it before starting a new one."}}let s=i.goal===r?i:L(r),d=t.plan?Ke(s,t.plan):{ok:!0,value:s};if(!d.ok)return R(d);let m=await v(e,d.value);return{...b(m),status:"ok",summary:t.plan?"Flow plan saved.":"Flow session ready."}})}async function at(e){return S(e,async(a)=>{if(!a)return{status:"missing_session",summary:"No active Flow session exists.",nextAction:"/flow-plan <goal>"};let t=He(a);if(!t.ok)return R(t);let n=await v(e,t.value);return{...b(n),status:"ok",summary:"Flow plan approved."}})}async function nt(e,a){let t=wa.parse(a??{});return S(e,async(n)=>{if(!n)return{status:"missing_session",summary:"No active Flow session exists.",nextAction:"/flow-plan <goal>"};let r=Xe(n,t.featureId);if(!r.ok)return R(r);let i=await v(e,r.value.session);return{...b(i),status:"ok",summary:`Started feature '${r.value.feature.id}'.`,feature:r.value.feature}})}async function rt(e,a){let t=U.parse(a??{});return S(e,async(n)=>{if(!n)return{status:"missing_session",summary:"No active Flow session exists.",nextAction:"/flow-plan <goal>"};let r=Ye(n,t);if(!r.ok){if(r.session)await v(e,r.session);return R(r)}let i=await v(e,r.value);return{...b(i),status:"ok",summary:"Feature result recorded."}})}async function ot(e,a){let t=va.parse(a??{});return S(e,async(n)=>{if(!n)return{status:"missing_session",summary:"No active Flow session exists.",nextAction:"/flow-plan <goal>"};let r=Ze(n,t.featureId);if(!r.ok)return R(r);let i=await v(e,r.value);return{...b(i),status:"ok",summary:`Feature '${t.featureId}' reset.`}})}async function it(e,a){let t=ya.parse(a??{});return S(e,async(n)=>{if(!n)return{status:"missing_session",summary:"No active Flow session exists.",nextAction:"/flow-plan <goal>"};let r=Qe(n,t.kind,t.summary);if(!r.ok)return R(r);return await Z(e,r.value),{status:"ok",summary:`Flow session closed as ${t.kind}.`,archivedSessionId:r.value.id,closure:r.value.closure}})}import{tool as f}from"@opencode-ai/plugin";var c=f.schema;function st(e){return JSON.stringify(e,null,2)}function ba(e){return st({status:"error",summary:e instanceof Error?e.message:String(e)})}async function k(e,a){try{return st(await a($e(e)))}catch(t){return ba(t)}}function lt(e){return z(e)("info","Creating minimal Flow v4 tool surface."),{flow_status:f({description:"Show the active Flow session and next action",args:{},execute:(a,t)=>k(t,et)}),flow_plan_save:f({description:"Create or update a draft Flow plan for the active goal",args:{goal:c.string().optional(),plan:c.any().optional()},execute:(a,t)=>k(t,(n)=>tt(n,a))}),flow_plan_approve:f({description:"Approve the current draft Flow plan",args:{},execute:(a,t)=>k(t,at)}),flow_run_start:f({description:"Start the next runnable approved Flow feature",args:{featureId:c.string().optional()},execute:(a,t)=>k(t,(n)=>nt(n,a))}),flow_feature_complete:f({description:"Record a completed or blocked active feature with validation and review evidence",args:{status:c.enum(["ok","needs_input"]),featureId:c.string(),summary:c.string(),artifactsChanged:c.array(c.object({path:c.string()})).optional(),validationRun:c.array(c.object({command:c.string(),status:c.enum(["passed","failed"]),summary:c.string()})).optional(),validationScope:c.enum(["targeted","broad"]).optional(),featureReview:c.any().optional(),finalReview:c.any().optional(),outcome:c.any().optional()},execute:(a,t)=>k(t,(n)=>rt(n,a))}),flow_feature_reset:f({description:"Reset one feature and its dependents to pending",args:{featureId:c.string()},execute:(a,t)=>k(t,(n)=>ot(n,a))}),flow_session_close:f({description:"Close and archive the active Flow session",args:{kind:c.enum(["completed","deferred","abandoned"]),summary:c.string().optional()},execute:(a,t)=>k(t,(n)=>it(n,a))})}}async function ct(e){let a=e.worktree??e.directory;if(!a)return null;try{let t=await A(a);if(!t)return null;return["Flow session facts:",`- goal: ${t.goal}`,`- status: ${t.status}`,`- approval: ${t.approval}`,`- active feature: ${t.activeFeatureId??"none"}`,`- progress: ${t.plan?.features.filter((n)=>n.status==="completed").length??0}/${t.plan?.features.length??0}`,"Call flow_status before any Flow action."].join(`
1065
+ `)}catch{return null}}function ka(e){return async(a,t)=>{let n=await ct(e);if(n)t.system.push(n)}}var xa=async(e)=>{let a=z(e);return a("info","Flow v4 plugin initialized."),await Ie(_e(),a),{config:Be(e),tool:lt(e),"experimental.chat.system.transform":ka(e),"experimental.session.compacting":async(t,n)=>{let r=await ct(e);if(!r)return;n.context=[...n.context??[],r]}}},Fa=xa;export{Fa as default};
1066
+
1067
+ //# debugId=77F331211D1168FA64756E2164756E21