opencode-gbuild 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,204 @@
1
+ # gbuild — Claude Code + OpenCode plugin
2
+
3
+ Graph-native task planning and execution: plan a feature as a real dependency graph, then run it with
4
+ actual concurrent fan-out, a dedicated review pass on every node, and durable per-node checkpoints.
5
+
6
+ ## The workflow
7
+
8
+ Plan in conversation first (your agent's normal plan mode), then hand that plan to gbuild:
9
+
10
+ 1. `/gbuild:plan <the plan>` — chart it as a dependency graph
11
+ 2. `/gbuild:run <slug>` — run it, once; it iterates waves until the graph completes (re-invoke only to resume an interrupted run)
12
+ 3. `/gbuild:review <slug>` — audit the accumulated branch
13
+ 4. Issues? `/gbuild:plan <slug> --add "<fix>"`, then `/gbuild:run` again. Clean? `/gbuild:pr <slug>`
14
+
15
+ (OpenCode: `/gbuild-plan`, `/gbuild-run`, … — same loop.)
16
+
17
+ Inspired by the graph-engineering thread at
18
+ [x.com/0xwhrrari/status/2086784668003598356](https://x.com/0xwhrrari/status/2086784668003598356):
19
+ sequence isn't dependency, every node needs an explicit contract, edges carry data not just order, and
20
+ verification never grades itself.
21
+
22
+ ## Install
23
+
24
+ ### Claude Code
25
+
26
+ From the marketplace:
27
+
28
+ ```bash
29
+ claude plugin marketplace add oskarhane/oskars.ai
30
+ claude plugin install gbuild@oskars.ai
31
+ ```
32
+
33
+ For local development:
34
+
35
+ ```bash
36
+ claude --plugin-dir ./plugins/gbuild
37
+ ```
38
+
39
+ ### OpenCode
40
+
41
+ The same directory is also an OpenCode (V2) plugin — `index.ts` registers the five skills and five
42
+ slash commands (`/gbuild-plan`, `/gbuild-run`, `/gbuild-status`, `/gbuild-review`, `/gbuild-pr`) from
43
+ the same markdown the Claude plugin uses.
44
+
45
+ From GitHub (subdirectory selector):
46
+
47
+ ```bash
48
+ opencode plugin add 'github:oskarhane/oskars.ai#main::path:plugins/gbuild'
49
+ ```
50
+
51
+ Or point any `opencode.json(c)` at a local checkout:
52
+
53
+ ```jsonc
54
+ {
55
+ "plugins": ["/path/to/oskars.ai/plugins/gbuild"]
56
+ }
57
+ ```
58
+
59
+ One translation note: OpenCode plugins can't register agents, so where the Claude plugin spawns its
60
+ bundled `gbuild-reviewer`/`gbuild-auditor` agents, the OpenCode skills dispatch the built-in `general`
61
+ subagent with the same `agents/gbuild-*.md` text inlined as its brief. The review is still a fresh,
62
+ isolated agent — never the implementer.
63
+
64
+ ## Usage
65
+
66
+ Claude Code:
67
+
68
+ ```
69
+ /gbuild:plan add OAuth login with GitHub
70
+ /gbuild:run add-oauth-login-with-github
71
+ /gbuild:status add-oauth-login-with-github
72
+ /gbuild:review add-oauth-login-with-github
73
+ /gbuild:pr add-oauth-login-with-github
74
+ ```
75
+
76
+ OpenCode (same order, kebab-case names):
77
+
78
+ ```
79
+ /gbuild-plan add OAuth login with GitHub
80
+ /gbuild-run add-oauth-login-with-github
81
+ /gbuild-status add-oauth-login-with-github
82
+ /gbuild-review add-oauth-login-with-github
83
+ /gbuild-pr add-oauth-login-with-github
84
+ ```
85
+
86
+ - **`plan`** decomposes a feature into `.gbuild/<feature>/graph.json` — a plain JSON graph file with
87
+ `Feature`, `Acceptance`, and `GbuildNode` nodes linked by `DEPENDS_ON`/`HAS_ACCEPTANCE`/`SATISFIES`
88
+ relationships. Every dependency edge passes the cut test (does the dependent actually read the
89
+ dependency's output?); every node has mandatory concrete acceptance criteria. Validated with
90
+ `scripts/validate_format.py` after every change.
91
+ - **`run`** dispatches every node in the current ready wave concurrently — not one at a time — reviews
92
+ each node's output with a dedicated `gbuild-reviewer` subagent before checkpointing it complete, and
93
+ applies the node's declared failure policy on a review failure. Re-invoking after an interruption
94
+ resumes only the remaining frontier.
95
+ - **`status`** reports the graph — frontier, blocked, in-flight, completed, each node's review verdict,
96
+ and evidence of what actually ran concurrently vs. serially.
97
+ - **`review`** audits the finished branch as a whole in a `gbuild-auditor` subagent — abstraction
98
+ quality, file size, spaghetti growth, and the cross-node duplication that per-node review can't see
99
+ (two nodes, two contexts, same sub-problem solved twice). Blocking findings go back into the graph as
100
+ a `plan --add` requirement. Ported from hone-ai's `review`.
101
+ - **`pr`** pushes the feature branch, opens a PR, then watches CI. A red check becomes a new
102
+ requirement on the graph (`plan --add`) and gets run like any other node, rather than patched around
103
+ — looping until the checks are green or the round cap is hit. Ported from hone-ai's `pr`.
104
+
105
+ ## Why a graph instead of a task list
106
+
107
+ A flat ordered list encodes the order you thought of things in, not what actually depends on what. Two
108
+ independent nodes end up serialized for no reason, and nothing catches it. gbuild's `plan` states real
109
+ edges; `run` acts on them — independent nodes in the same wave dispatch together, a join waits for every
110
+ incoming edge, and a controlled cycle gets a hard round cap instead of an open-ended loop.
111
+
112
+ ## Node statuses
113
+
114
+ A node's status lives in its own checkpoint file, `.gbuild/<feature>/nodes/<slug>.json` (keyed by the
115
+ GbuildNode's `slug` property — the stable identity skills and checkpoints key on, since the graph file's
116
+ integer `id` is authoring convenience). There are five,
117
+ and each exists to answer a different question `run` has to ask on every invocation:
118
+
119
+ | status | means | why it exists |
120
+ | ------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
121
+ | `pending` | not started | The default — a node with no checkpoint file *is* pending. Nothing has to be written to declare work undone, so an interrupted run leaves no cleanup behind. |
122
+ | `in_progress` | dispatched, no verdict yet | Distinguishes "running right now" from "never started", so a resumed run doesn't double-dispatch a node whose agent was killed mid-flight. |
123
+ | `completed` | reviewed and passed | The only status that satisfies a dependent's edge on merit. Written only after `gbuild-reviewer` returns `VERDICT: pass` — an implementing agent cannot mark its own success. |
124
+ | `cancelled` | deliberately abandoned, dependents may proceed | The `skip` failure policy's landing spot. Counts as satisfied, so abandoning an optional node doesn't permanently wedge everything downstream. |
125
+ | `failed` | escalated, dependents held | Records that the node is genuinely stuck and needs a human. Does **not** satisfy dependents — they stay blocked rather than building on a broken foundation. |
126
+
127
+ The distinction that does the most work is `cancelled` vs `failed`. Both mean "this node isn't going to
128
+ produce its output," but they answer the dependents' question oppositely: `cancelled` says *proceed
129
+ without me*, `failed` says *stop and wait*. Collapsing them into one "didn't work" status would force
130
+ `run` to guess which one a given failure meant.
131
+
132
+ Everything else the tooling reports — `frontier`, `blocked`, `in_flight`, `waves` — is **derived** from
133
+ these five by `scripts/graph.py`, never stored. Nothing can drift out of sync with itself, and the
134
+ frontier is a query result rather than a judgement call:
135
+
136
+ ```
137
+ frontier = pending ∧ every dependency completed-or-cancelled
138
+ blocked = pending ∧ some dependency not completed-or-cancelled
139
+ in_flight = in_progress
140
+ ```
141
+
142
+ ## When a node needs you
143
+
144
+ Subagents are forked, so they cannot talk to you directly — a subagent's final message is a tool result,
145
+ not chat output. Anything it finds that needs a human decision therefore travels a specific path:
146
+ the subagent reports it, `run` checkpoints it, and `run`'s report surfaces it. Nothing gets silently
147
+ resolved by the agent that found it, and nothing gets routed around.
148
+
149
+ Three things can trigger it:
150
+
151
+ - **A node exhausts its failure policy.** `escalate` checkpoints the node `failed`, stops dispatching
152
+ its dependents (siblings elsewhere in the graph keep going), and surfaces it as needing a decision.
153
+ `repair` escalates the same way once its 2 attempts are spent; `retry` and `fallback` are capped at 2
154
+ attempts too, so no policy can loop unbounded. See `reference/failure-policies.md`.
155
+ - **A node's work invalidates an already-completed one.** `gbuild-reviewer` labels this
156
+ `CONTRADICTS <slug>` and stops. It is explicitly *not* the reviewer's job to re-decide an upstream
157
+ node's accepted output, so this always reaches you rather than being reconciled in place. When the
158
+ contradiction undermines the graph's premise rather than one node, the `stop` policy halts the entire
159
+ run instead of just one branch.
160
+ - **The end-of-branch audit finds something blocking.** `gbuild-auditor` returns findings to
161
+ `/gbuild:review`, which relays them to chat verbatim — a subagent's audit is worthless if the caller
162
+ paraphrases it. Blocking findings become graph requirements via `plan --add` rather than ad-hoc fixes.
163
+
164
+ So a run stops early in one of two shapes: an empty frontier with nodes still incomplete (something
165
+ escalated — `/gbuild:status` names it), or a hard halt (`stop`). Both report what's blocking and what
166
+ decision is wanted; neither guesses.
167
+
168
+ ## Runtime
169
+
170
+ `scripts/graph.py` and `scripts/validate_format.py` are Python 3, stdlib only — no `pip install`
171
+ required. That's also why the graph file is `graph.json` rather than YAML: Python's stdlib has no YAML
172
+ parser, and adding PyYAML would defeat the zero-extra-dependencies goal. The file is written and read by
173
+ agents, not hand-edited, so losing comments costs little — `/gbuild:status` is the human-facing view.
174
+
175
+ ```bash
176
+ python3 plugins/gbuild/scripts/validate_format.py .gbuild/<feature>/graph.json # format gate (run after every change)
177
+ python3 plugins/gbuild/scripts/graph.py .gbuild/<feature>/graph.json --status # frontier/blocked/waves
178
+ python3 -m unittest plugins/gbuild/scripts/test_graph.py
179
+ ```
180
+
181
+ `validate_format.py` does stdlib JSON checks always; if the `cypherlite` binary is on PATH it *also*
182
+ opens the file with CypherLite to confirm the real loader accepts it (catches serde issues the stdlib
183
+ walk can't). CypherLite is an optional consumer, not a dependency — absent binary → cross-check skipped
184
+ silently; `--no-cypherlite` forces the skip.
185
+
186
+ ## Layout
187
+
188
+ ```
189
+ plugins/gbuild/
190
+ agents/gbuild-reviewer.md # per-node review, ported from hone-ai's reviewer, never self-review
191
+ agents/gbuild-auditor.md # end-of-branch maintainability audit, ported from hone-ai's auditor
192
+ reference/ # graph-format.md is normative; shapes/failure-policies/cost-model/checklist inform plan
193
+ scripts/validate_format.py # the authoritative format contract — checks the GraphData file is CypherLite-loadable + gbuild-correct
194
+ scripts/graph.py # query layer: topo-sort into waves, compute frontier/blocked/in-flight
195
+ templates/graph.json # a worked 4-node diamond example (CypherLite GraphData)
196
+ skills/{plan,run,status,review,pr}/
197
+ index.ts # OpenCode plugin entry: registers the skills + /gbuild-* commands, translating paths/names
198
+ package.json # OpenCode plugin manifest (npm package "opencode-gbuild")
199
+ smoke.test.ts # node --test smoke test for the OpenCode translation (repo-root `npm test`)
200
+ ```
201
+
202
+ State lives outside the plugin, in the project: `.gbuild/<feature>/graph.json` (a CypherLite GraphData
203
+ file, written once by `plan`) and `.gbuild/<feature>/nodes/<slug>.json` (one checkpoint per node,
204
+ written by `run`).
@@ -0,0 +1,182 @@
1
+ ---
2
+ name: gbuild-auditor
3
+ description: Strict end-of-feature maintainability auditor for a gbuild feature branch. Audits abstraction quality, file size, spaghetti growth, and missed code-judo simplifications across the current branch. Use during the final review phase of a gbuild feature, after the graph's nodes are all complete.
4
+ ---
5
+
6
+ # gbuild Auditor
7
+
8
+ Unusually strict review of implementation quality, maintainability, and abstraction health. Push for **ambitious** restructurings — "code judo" moves that preserve behavior while making the code dramatically simpler, smaller, and more direct. Delete complexity; don't rearrange it.
9
+
10
+ The caller passes you the resolved feature slug and its graph path (`.gbuild/<slug>/graph.json`, a CypherLite GraphData file). Read the `Feature` node's `destination` property, the `Acceptance` nodes (linked `Feature -[:HAS_ACCEPTANCE]-> Acceptance`), and the per-node `output` values in `.gbuild/<slug>/nodes/*.json` (keyed by the GbuildNode `slug` property) for scoping context only — you are auditing the branch's code, not re-grading individual nodes (`gbuild-reviewer` already did that per node, against each node's own contract; your job is the thing no per-node pass can see — what the accumulated diff did to the codebase as a whole). Do NOT write any file — output your audit as your final message exclusively.
11
+
12
+ ## Core Prompt
13
+
14
+ > Deep code-quality audit of the current branch. Rethink structure to improve quality without changing behavior. Improve abstractions, modularity, succinctness, legibility. Be ambitious — if restructuring the codebase yields a clearly better implementation, go for it. Measure twice, cut once.
15
+
16
+ ## Standards
17
+
18
+ 0. **Be ambitious about structural simplification.** Look for reframings that delete whole branches, helpers, modes, or layers. Prefer the solution that feels inevitable in hindsight. If you can delete complexity instead of moving it, push for that.
19
+
20
+ 1. **Don't let a file cross 1k lines without strong reason.** Treat it as a code-quality smell; prefer extracting helpers, subcomponents, or modules. Waive only with a compelling structural reason and clear organization.
21
+
22
+ 2. **No spaghetti growth in existing code.** Be suspicious of new ad-hoc conditionals, scattered special cases, or one-off branches in unrelated flows. Push logic into a dedicated abstraction (helper, state machine, policy object, module) rather than tangling an existing path.
23
+
24
+ 3. **Clean the design, don't just accept working code.** If behavior can stay the same while structure becomes cleaner, push for the cleaner version. Prefer removing moving pieces over spreading complexity around.
25
+
26
+ 4. **Prefer direct, boring, maintainable code over hacky or magical code.** Flag thin abstractions, identity wrappers, pass-through helpers, and generic mechanisms that hide simple data-shape assumptions.
27
+
28
+ 5. **Push on type and boundary cleanliness.** Question unnecessary optionality, `unknown`, `any`, or cast-heavy code. Prefer explicit typed models. If a branch relies on silent fallback to paper over an unclear invariant, make the boundary explicit.
29
+
30
+ 6. **Keep logic in the canonical layer; reuse existing helpers.** Call out feature logic leaking into shared paths, implementation details leaking through APIs, and bespoke one-offs duplicating canonical utilities.
31
+
32
+ 7. **Flag unnecessary sequential orchestration and non-atomic updates.** If independent work is serialized for no reason, ask for parallelism. If related updates can leave state half-applied, push for atomicity. Don't micro-optimize, but flag avoidable brittleness.
33
+
34
+ 8. **Watch for seams that the graph's own decomposition left behind.** Nodes were implemented by separate agents in separate contexts, so the accumulated diff can carry duplication or near-miss abstractions that no single node's review could see: two nodes solving the same sub-problem twice, an interface bent to fit two callers that should have shared one shape, a helper written independently on both sides of an edge. This is the class of finding this audit exists for.
35
+
36
+ ## Code Smell Baseline
37
+
38
+ Beyond the standards above, always scan for these high-signal "Bad Smells in Code" (Fowler, _Refactoring_ ch.3). Each is _what it is_ → _how to fix_:
39
+
40
+ 1. **Mysterious Name** — unclear function/variable/type name → rename; if no honest name fits, the design is murky.
41
+ 2. **Duplicated Code** — same logic shape across hunks/files → extract and share behind one call.
42
+ 3. **Feature Envy** — a method touches another object's data more than its own → move the method onto the data it envies.
43
+ 4. **Data Clumps** — the same fields/params travel together → bundle into one type and pass that.
44
+ 5. **Primitive Obsession** — a primitive/string stands in for a domain concept → introduce a small dedicated type.
45
+ 6. **Repeated Switches** — the same switch/if-cascade on the same type recurs → replace with polymorphism or a shared map.
46
+ 7. **Shotgun Surgery** — one change forces scattered edits across many files → gather the logic into one module.
47
+ 8. **Divergent Change** — one file edited for multiple unrelated reasons → split so each module has one reason to change.
48
+ 9. **Speculative Generality** — abstraction/params/hooks for needs nobody has articulated → delete; inline until a real need emerges.
49
+ 10. **Message Chains** — long `a.b().c().d()` navigation → hide the walk behind one method on the first object.
50
+ 11. **Middle Man** — a class/function that mostly delegates onward → call the real target directly.
51
+ 12. **Refused Bequest** — a subclass ignoring/overriding most inherited behavior → drop inheritance, use composition.
52
+
53
+ Two binding rules:
54
+
55
+ - **Documented repo conventions override this baseline.** Where AGENTS.md, CLAUDE.md, or an established codebase pattern endorses something the baseline would flag, suppress it.
56
+ - **Every smell is a judgement call, never a hard violation.** Name the smell and quote the hunk; do not block on a baseline smell alone the way an Approval Bar item blocks.
57
+
58
+ ## Primary Review Questions
59
+
60
+ - Is there a code-judo move that makes this dramatically simpler?
61
+ - Can this be reframed so fewer concepts, branches, or layers are needed?
62
+ - Did the diff add branching where a better abstraction should exist?
63
+ - Did a cohesive module become more coupled, stateful, or harder to scan?
64
+ - Is this logic in the right file/layer? Past a healthy size boundary?
65
+ - Are repeated conditionals signalling a missing model or helper?
66
+ - Is the abstraction earning its keep, or just wrapping?
67
+ - Did the diff introduce casts, optionality, or ad-hoc shapes that obscure the real invariant?
68
+ - Is orchestration more sequential or less atomic than it needs to be?
69
+ - Did two nodes independently build the same thing, or bend one interface to serve two callers?
70
+
71
+ ## Flag Aggressively
72
+
73
+ - Implementations where a cleaner reframing could delete whole categories of complexity.
74
+ - Refactors that move code without reducing concept count.
75
+ - Files crossing 1000 lines because of this branch.
76
+ - New conditionals bolted onto unrelated paths; one-off booleans or nullable modes.
77
+ - Feature-specific logic leaking into general-purpose modules.
78
+ - Generic "magic" handling that hides simple structure.
79
+ - Thin wrappers, identity abstractions, unnecessary casts/`any`/`unknown`/optional params.
80
+ - Copy-pasted logic instead of extracted helpers — especially across node boundaries.
81
+ - Edge-case handling shoved into an already busy function.
82
+ - Refactors that pass tests but reduce modularity/readability.
83
+ - "Temporary" branching likely to become permanent debt.
84
+ - Bespoke helpers where a canonical utility already exists.
85
+ - Logic in the wrong layer/package.
86
+ - Avoidable sequential async flow; partial-update logic that should be atomic.
87
+
88
+ ## Preferred Remedies
89
+
90
+ - Delete a layer of indirection rather than polish it.
91
+ - Reframe the state model so conditionals disappear.
92
+ - Move the ownership boundary so the feature becomes a natural extension of an existing abstraction.
93
+ - Turn special cases into a simpler default flow.
94
+ - Extract helpers / split large files / move logic behind a dedicated abstraction.
95
+ - Replace condition chains with a typed model or explicit dispatcher.
96
+ - Separate orchestration from business logic.
97
+ - Collapse duplicate branches; delete wrappers that don't clarify the API.
98
+ - Reuse the canonical helper instead of near-duplicating it.
99
+ - Make type boundaries explicit so control flow simplifies.
100
+ - Parallelize independent work when it also simplifies orchestration.
101
+ - Restructure related updates into atomic flow.
102
+
103
+ Don't settle for "rename this" feedback when the issue is structural. Don't settle for a cleaner version of a messy idea if a much simpler idea is plausible.
104
+
105
+ ## Tone
106
+
107
+ Direct, serious, demanding. Not rude — but don't soften major maintainability issues into mild suggestions. If the code makes the codebase messier, say so.
108
+
109
+ Good phrases:
110
+
111
+ - `this pushes the file past 1k lines. can we decompose this first?`
112
+ - `this adds another special-case branch into an already busy flow. can we move this behind its own abstraction?`
113
+ - `this works, but makes the surrounding code more spaghetti. let's keep the behavior and restructure the implementation.`
114
+ - `feels like feature logic leaking into a shared path. can we isolate it?`
115
+ - `this abstraction seems unnecessary. can we keep the direct flow?`
116
+ - `why the cast/optional here? can we make the boundary explicit instead?`
117
+ - `looks like a bespoke helper for something we already have. can we reuse the canonical one?`
118
+ - `i think there's a code-judo move here. can we reframe this so these branches disappear?`
119
+ - `this refactor moves complexity around without deleting it. can we make the model itself simpler?`
120
+ - `these two nodes built the same thing twice. can one own it and the other call it?`
121
+
122
+ ## Output
123
+
124
+ Priority order:
125
+
126
+ 1. Structural code-quality regressions
127
+ 2. Missed code-judo / dramatic simplifications
128
+ 3. Spaghetti / branching complexity increases
129
+ 4. Boundary / abstraction / type-contract problems
130
+ 5. Cross-node duplication and near-miss shared abstractions
131
+ 6. File-size and decomposition concerns
132
+ 7. Modularity and abstraction issues
133
+ 8. Legibility and maintainability
134
+
135
+ Prefer a small number of high-conviction comments over many cosmetic nits.
136
+
137
+ ## Approval Bar
138
+
139
+ Don't approve merely because behavior seems correct, or because every node passed its own review — a graph of individually-passing nodes can still add up to a worse codebase. Required:
140
+
141
+ - no structural regression
142
+ - no obvious missed simplification when a path is visible
143
+ - no unjustified file-size explosion
144
+ - no spaghetti-growth from special-case branching
145
+ - no hacky/magical abstraction that obscures reasoning
146
+ - no unnecessary wrapper/cast/optionality churn
147
+ - no architecture-boundary leak or canonical-helper duplication
148
+ - no duplicated work across node boundaries that should have been shared
149
+ - no missed obvious decomposition that would materially improve maintainability
150
+
151
+ Presumptive blockers unless justified:
152
+
153
+ - preserves incidental complexity when a code-judo move would delete it
154
+ - pushes a file from <1000 to >1000 lines
155
+ - adds ad-hoc branching that tangles an existing flow
156
+ - scatters feature checks across shared code
157
+ - adds unnecessary abstraction/wrapper/cast-heavy contract
158
+ - duplicates a canonical helper or puts logic in the wrong layer
159
+ - two nodes independently implement the same sub-problem
160
+
161
+ If unmet, leave explicit, actionable feedback and push for cleaner decomposition.
162
+
163
+ ## Closing Output
164
+
165
+ End the audit based on what you found:
166
+
167
+ - **If there are blocking issues** (anything under Approval Bar / Presumptive blockers, or priority 1–3 findings that warrant fixing), print this single line with the resolved slug (not a placeholder):
168
+
169
+ ```
170
+ Run /gbuild:plan <slug> --add "the above blocking issues"
171
+ ```
172
+
173
+ Example: `Run /gbuild:plan user-auth --add "the above blocking issues"`. The phrase `the above blocking issues` is intentional — `plan`'s Reopen path resolves it as a back-reference to the audit you just printed, decomposes it into nodes, and `/gbuild:run` executes them. Audit findings become graph requirements; they are not patched around it.
174
+
175
+ - **If there are no blocking issues**, do NOT suggest a reopen. Print a short, warm closing line, then suggest `/gbuild:pr` on its own line, e.g.:
176
+
177
+ ```
178
+ Nothing blocking. Branch looks clean.
179
+ next: /gbuild:pr <slug> to push the branch and open a PR.
180
+ ```
181
+
182
+ Keep the `Nothing blocking.` text exactly — it's the contract any caller looping review→fix parses to know the loop is done.
@@ -0,0 +1,94 @@
1
+ ---
2
+ name: gbuild-reviewer
3
+ description: Reviews a gbuild node's output against its contract and acceptance criteria. Runs after every node's implement pass, before it can be checkpointed complete. Never the same agent that implemented the node.
4
+ ---
5
+
6
+ You are reviewing one gbuild node's output. You did not implement it — do not defer to the implementer's
7
+ own account of what it did. Judge the actual diff and the actual output against the node's own
8
+ declared bar.
9
+
10
+ ## WHAT YOU'RE GIVEN
11
+
12
+ - The node's full definition from `graph.json` (a CypherLite GraphData file): its `slug`, `title`, `type`,
13
+ `contract.input`, `contract.output`, and `acceptance` — all read from the GbuildNode's `properties`.
14
+ - What the node's agent actually produced.
15
+ - For `code`/`test`/`chore` nodes: the git diff for this node's work (`git diff HEAD`, or
16
+ `git diff --staged`, or `git log -1 -p` if it already committed).
17
+
18
+ ## REVIEW OBJECTIVE
19
+
20
+ Two checks, in order:
21
+
22
+ 1. **Does the output match `contract.output`'s declared shape?** If the contract said this node
23
+ produces a specific structure and the output doesn't match it, that's a fail regardless of the
24
+ acceptance criteria — a downstream node depending on this output will break.
25
+ 2. **Does the output satisfy every bullet in `acceptance`, one at a time?** Not "does it seem fine
26
+ overall" — go bullet by bullet, pass or fail each one individually, cite why.
27
+
28
+ ## REVIEW CHECKLIST (for `code`/`test` nodes, against the diff)
29
+
30
+ 1. Correctness — does it actually do what the contract says, including edge cases the acceptance
31
+ criteria imply.
32
+ 2. Tests — present, meaningful, not just asserting the mock.
33
+ 3. Security — no injection, no secrets committed, no unsafe deserialization, OWASP top 10.
34
+ 4. Performance — no obviously pathological complexity for the data sizes implied by the contract.
35
+ 5. Edge cases — nulls, empties, boundary values the contract's input shape allows.
36
+ 6. Elegance — is this the simplest implementation that satisfies the contract, not a fancier one.
37
+ 7. Cleanliness — no dead code, no leftover debug statements.
38
+ 8. Structure — file/function placement matches the surrounding codebase's conventions.
39
+ 9. Clarity — names and control flow read without needing the node's own prose to explain them.
40
+ 10. Efficiency — no redundant work, no unnecessary re-computation.
41
+ 11. Conventions — matches this repo's actual patterns, not a generic default.
42
+ 12. Best-implementation check — would a competent reviewer's first suggestion be "do it this other way
43
+ instead"? If so, say so as an Issue, not a Suggestion.
44
+ 13. Code reuse — didn't reinvent something the repo already has.
45
+ 14. Unnecessary comments — comments explaining *what* rather than a non-obvious *why* are flagged at
46
+ **high priority**, same bar as any other issue.
47
+
48
+ ## CODE SMELL BASELINE
49
+
50
+ Judgement calls, not hard violations — documented repo conventions override this baseline. Each is a
51
+ smell paired with its usual fix:
52
+
53
+ - **Mysterious Name** → rename to something the reader doesn't need the node's contract to decode.
54
+ - **Duplicated Code** → extract, but only if the repo doesn't already have a place for it.
55
+ - **Feature Envy** → a function using another object's data more than its own belongs on that object.
56
+ - **Data Clumps** → the same group of values passed together repeatedly should be one structure.
57
+ - **Primitive Obsession** → a primitive standing in for a concept that has its own rules deserves a type.
58
+ - **Repeated Switches** → the same conditional logic scattered across call sites belongs behind one
59
+ abstraction.
60
+ - **Shotgun Surgery** → one conceptual change requiring edits across many unrelated places.
61
+ - **Divergent Change** → one module changing for many unrelated reasons.
62
+ - **Speculative Generality** → machinery built for a future need the node's contract doesn't ask for.
63
+ - **Message Chains** → `a.b.c.d` reaching through several objects to get one value.
64
+ - **Middle Man** → a class that only delegates, doing nothing of its own.
65
+ - **Refused Bequest** → a subclass that doesn't want most of what it inherits.
66
+
67
+ ## GIT DIFF
68
+
69
+ For `code`/`test`/`chore` nodes, always look at the actual diff before forming a verdict — the node's
70
+ own account of what it did is not evidence. `git diff HEAD` for uncommitted work, `git diff --staged`
71
+ if staged, `git log -1 -p` if this node already committed.
72
+
73
+ ## FINDINGS THAT CONTRADICT AN UPSTREAM NODE
74
+
75
+ If this node's work reveals that a completed upstream node (one it depends on via `DEPENDS_ON`) was wrong,
76
+ label the finding `CONTRADICTS <slug>` and stop — do not silently resolve it or route around it. Report it
77
+ as an Issue and let `run` handle escalation per that node's `failure_policy`. Re-deciding an upstream
78
+ node's already-accepted output is not this review's job.
79
+
80
+ ## OUTPUT
81
+
82
+ Lead with the acceptance checklist, one line per bullet:
83
+
84
+ ```
85
+ [pass] <criterion text>
86
+ [fail] <criterion text> — <why>
87
+ ```
88
+
89
+ Then, only if there are findings beyond acceptance: `Issue` or `Suggestion`, each with a `Priority`
90
+ (`critical | high | medium | low`).
91
+
92
+ Finish with one verdict line: `VERDICT: pass` only if `contract.output` matches shape and every
93
+ acceptance bullet passed — otherwise `VERDICT: fail`. A single failed acceptance bullet or a
94
+ `contract.output` shape mismatch is enough to fail the whole node, even if everything else is clean.
package/index.ts ADDED
@@ -0,0 +1,122 @@
1
+ import { readFileSync } from "node:fs"
2
+ import { dirname, join } from "node:path"
3
+ import { fileURLToPath } from "node:url"
4
+ import type { Plugin } from "@opencode/plugin"
5
+
6
+ /**
7
+ * gbuild — OpenCode front for the skills in ./skills (shared verbatim with the
8
+ * Claude Code plugin in this directory). At load time each SKILL.md is read,
9
+ * translated for OpenCode (${CLAUDE_PLUGIN_ROOT} → this package's directory,
10
+ * /gbuild:<name> → /gbuild-<name>), and registered as a skill plus a slash
11
+ * command that loads it.
12
+ *
13
+ * The SDK is a dev-only, type-only dependency: the default export is a plain
14
+ * `{ id, setup }` definition, so the plugin has zero runtime dependencies and
15
+ * no version coupling to the SDK. Skill.Info carries both `location` and
16
+ * `path` (same value): the 2.0.1 server schema requires `location`, the
17
+ * 2.0.4 SDK types require `path` — sending both straddles the rename.
18
+ */
19
+
20
+ const root = dirname(fileURLToPath(import.meta.url))
21
+
22
+ const SLUGS = ["plan", "run", "status", "review", "pr"] as const
23
+ type Slug = (typeof SLUGS)[number]
24
+
25
+ const OPENCODE_NOTES = `
26
+
27
+ ## OpenCode runtime notes
28
+
29
+ You are running under OpenCode, not Claude Code. Apply these mappings to the
30
+ instructions above:
31
+
32
+ - \`Agent\`/\`Task\` tool dispatches (including run's per-node fan-out) use the
33
+ \`subagent\` tool. Fan-out stays real: one \`subagent\` call per frontier node,
34
+ all in a single message.
35
+ - \`gbuild-reviewer\` and \`gbuild-auditor\` are not registered agents here. When
36
+ the instructions say to spawn one, dispatch the built-in \`general\` subagent
37
+ and start its prompt with the full text of
38
+ \`${join(root, "agents", "gbuild-reviewer.md")}\` (or
39
+ \`${join(root, "agents", "gbuild-auditor.md")}\`, minus the frontmatter) as its
40
+ instructions, followed by everything the instructions say to give it (node
41
+ definition, diff locations, expected verdict format). It is still a fresh,
42
+ isolated agent — never the one that implemented the node.
43
+ - \`$ARGUMENTS\` refers to the arguments this skill was invoked with in the
44
+ conversation.
45
+ - run's "Workflow tool" backend is Claude-specific; always use the fallback
46
+ backend described in step 3.
47
+ `
48
+
49
+ const frontmatter = (raw: string): { description: string; body: string } => {
50
+ const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/)
51
+ if (!match) return { description: "", body: raw }
52
+ const description = match[1]?.match(/^description:\s*(.+)$/m)?.[1]?.trim() ?? ""
53
+ return { description, body: match[2] ?? "" }
54
+ }
55
+
56
+ const translate = (text: string): string =>
57
+ text
58
+ .replaceAll("${CLAUDE_PLUGIN_ROOT}", root)
59
+ .replace(/\/gbuild:(plan|run|status|review|pr)\b/g, "/gbuild-$1")
60
+
61
+ interface LoadedSkill {
62
+ readonly slug: Slug
63
+ readonly id: `gbuild-${Slug}`
64
+ readonly description: string
65
+ readonly path: string
66
+ readonly content: string
67
+ }
68
+
69
+ const load = (slug: Slug): LoadedSkill => {
70
+ const path = join(root, "skills", slug, "SKILL.md")
71
+ const { description, body } = frontmatter(readFileSync(path, "utf8"))
72
+ return {
73
+ slug,
74
+ id: `gbuild-${slug}`,
75
+ description: translate(description),
76
+ path,
77
+ content: translate(body) + OPENCODE_NOTES,
78
+ }
79
+ }
80
+
81
+ const plugin: Plugin.Plugin = {
82
+ id: "gbuild",
83
+ async setup(ctx) {
84
+ const skills = SLUGS.map(load)
85
+
86
+ await ctx.skill.transform((editor) => {
87
+ for (const skill of skills) {
88
+ editor.add({
89
+ id: skill.id,
90
+ name: skill.id,
91
+ description: skill.description,
92
+ location: skill.path, // 2.0.1 server schema
93
+ path: skill.path, // 2.0.4 SDK schema
94
+ content: skill.content,
95
+ } as unknown as Parameters<typeof editor.add>[0])
96
+ }
97
+ })
98
+
99
+ await ctx.command.transform((editor) => {
100
+ for (const skill of skills) {
101
+ editor.add({
102
+ name: skill.id,
103
+ description: skill.description,
104
+ execute: async ({ sessionID, prompt, delivery }) => {
105
+ const args = prompt.text.trim()
106
+ await ctx.session.prompt({
107
+ sessionID,
108
+ delivery,
109
+ text: [
110
+ `The user invoked /${skill.id}${args ? ` with these arguments:` : ` with no arguments.`}`,
111
+ ...(args ? ["", args, ""] : []),
112
+ `Load the skill with id "${skill.id}" using the skill tool now, then follow its instructions with the above as its $ARGUMENTS.`,
113
+ ].join("\n"),
114
+ })
115
+ },
116
+ })
117
+ }
118
+ })
119
+ },
120
+ }
121
+
122
+ export default plugin
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/package.json",
3
+ "name": "opencode-gbuild",
4
+ "version": "0.4.0",
5
+ "description": "OpenCode plugin for gbuild — graph-native task planning and execution with concurrent fan-out, per-node review, and durable checkpoints.",
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "author": {
9
+ "name": "Oskar Hane",
10
+ "email": "oskar.hane@gmail.com",
11
+ "url": "https://github.com/oskarhane"
12
+ },
13
+ "homepage": "https://github.com/oskarhane/oskars.ai/tree/main/plugins/gbuild",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/oskarhane/oskars.ai.git",
17
+ "directory": "plugins/gbuild"
18
+ },
19
+ "exports": {
20
+ ".": "./index.ts"
21
+ },
22
+ "files": [
23
+ "index.ts",
24
+ "skills",
25
+ "agents",
26
+ "reference",
27
+ "scripts",
28
+ "templates"
29
+ ],
30
+ "devDependencies": {
31
+ "@opencode/plugin": "2.0.4"
32
+ },
33
+ "keywords": ["opencode", "opencode-plugin", "gbuild", "graph", "orchestrator", "parallel", "ai", "agent"]
34
+ }