opencode-resolve 0.1.1 → 0.1.3
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 +410 -112
- package/dist/index.js +103 -24
- package/opencode-resolve.example.json +14 -11
- package/opencode-resolve.reference.jsonc +169 -0
- package/package.json +2 -1
- package/scripts/postinstall.mjs +53 -0
package/README.md
CHANGED
|
@@ -3,28 +3,59 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/opencode-resolve)
|
|
4
4
|
[](./LICENSE)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Small OpenCode plugin that turns a single instruction into a finished, verified change.
|
|
7
7
|
|
|
8
|
-
`opencode-resolve`
|
|
8
|
+
`opencode-resolve` ships three roles by default — **resolver** (orchestrator), **coder** (implementer), and **reviewer** (read-only auditor) — and runs them with auto-approved permissions so a task drives to completion without prompting at every step. It defines roles, not model providers: agents inherit your OpenCode default model unless you pin them.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
---
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
- Injects only `coder` and `reviewer` by default.
|
|
14
|
-
- Uses GLM-style defaults for coding and GPT-style defaults for review.
|
|
15
|
-
- Adds a Context7 MCP preset only when one is not already configured.
|
|
16
|
-
- Validates config strictly so typos fail fast.
|
|
17
|
-
- Keeps Telegram notifications and remote control as companion-plugin concerns.
|
|
12
|
+
## Table of Contents
|
|
18
13
|
|
|
19
|
-
|
|
14
|
+
- [Roles at a glance](#roles-at-a-glance)
|
|
15
|
+
- [Quick Start](#quick-start)
|
|
16
|
+
- [Drop-in setup (give to an LLM)](#drop-in-setup-give-to-an-llm)
|
|
17
|
+
- [Default Behavior](#default-behavior)
|
|
18
|
+
- [Configuration](#configuration)
|
|
19
|
+
- [Configuration Reference](#configuration-reference)
|
|
20
|
+
- [Auto Approval](#auto-approval)
|
|
21
|
+
- [Parallel Subagent Limit](#parallel-subagent-limit)
|
|
22
|
+
- [Upgrade & Migration](#upgrade--migration)
|
|
23
|
+
- [Model Setup](#model-setup)
|
|
24
|
+
- [Agent Reference](#agent-reference)
|
|
25
|
+
- [Optional Commands](#optional-commands)
|
|
26
|
+
- [Local Development](#local-development)
|
|
27
|
+
- [Verification](#verification)
|
|
28
|
+
- [Release](#release)
|
|
29
|
+
- [Design Rules](#design-rules)
|
|
20
30
|
|
|
21
|
-
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Roles at a glance
|
|
34
|
+
|
|
35
|
+
| Role | Default | Can edit? | Can run shell? | Purpose |
|
|
36
|
+
|---|:---:|:---:|:---:|---|
|
|
37
|
+
| `resolver` | Yes | Yes (auto) | Yes (auto) | Primary orchestrator. Plans, dispatches `coder` (one at a time), verifies, iterates. |
|
|
38
|
+
| `coder` | Yes | Yes (auto) | Yes (auto) | Focused implementer. Makes the smallest correct change and verifies. |
|
|
39
|
+
| `reviewer` | Yes | **Never** | **Never** | Read-only auditor. Inspects only — recommends fixes for `coder` or `resolver` to apply. |
|
|
40
|
+
|
|
41
|
+
Hard rule: **the reviewer cannot modify the project by any means.** Both `edit` and `bash` are denied for the reviewer regardless of `autoApprove`. Any required fix is routed back through `coder` or `resolver`.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
Install from npm:
|
|
22
48
|
|
|
23
49
|
```sh
|
|
24
50
|
npm install -g opencode-resolve
|
|
25
51
|
```
|
|
26
52
|
|
|
27
|
-
The package
|
|
53
|
+
The package `postinstall` step:
|
|
54
|
+
|
|
55
|
+
1. Adds `opencode-resolve` to `~/.config/opencode/opencode.json` `plugin` array.
|
|
56
|
+
2. Creates `~/.config/opencode/resolve.json` from [`opencode-resolve.example.json`](./opencode-resolve.example.json) when missing.
|
|
57
|
+
|
|
58
|
+
Restart OpenCode after installation.
|
|
28
59
|
|
|
29
60
|
To skip automatic registration:
|
|
30
61
|
|
|
@@ -32,7 +63,7 @@ To skip automatic registration:
|
|
|
32
63
|
OPENCODE_RESOLVE_SKIP_POSTINSTALL=1 npm install -g opencode-resolve
|
|
33
64
|
```
|
|
34
65
|
|
|
35
|
-
Manual config
|
|
66
|
+
Manual fallback: add the plugin to `~/.config/opencode/opencode.json` yourself:
|
|
36
67
|
|
|
37
68
|
```json
|
|
38
69
|
{
|
|
@@ -40,150 +71,433 @@ Manual config fallback:
|
|
|
40
71
|
}
|
|
41
72
|
```
|
|
42
73
|
|
|
43
|
-
|
|
74
|
+
---
|
|
44
75
|
|
|
45
|
-
##
|
|
76
|
+
## Drop-in setup (give to an LLM)
|
|
46
77
|
|
|
47
|
-
|
|
78
|
+
Hand the block below to any reasonably capable LLM along with the instruction _"set up opencode-resolve on this machine"_ and it can finish the install end-to-end. Everything below is the entire required configuration — no other MCP servers, environment variables, or files are needed.
|
|
48
79
|
|
|
49
|
-
|
|
50
|
-
- `reviewer`: GPT-based Oracle-style review agent for requirements fit, correctness, security, tests, and maintainability.
|
|
80
|
+
> **Important — OpenCode caches plugins itself.** OpenCode does NOT use globally-installed npm packages. It maintains its own cache at `~/.cache/opencode/packages/`. After the very first install (or any future upgrade), refresh the cache with `opencode plugin opencode-resolve --global --force` and restart OpenCode. `npm install -g` alone is not enough.
|
|
51
81
|
|
|
52
|
-
|
|
82
|
+
**Step 1 — install via OpenCode (also primes the cache):**
|
|
53
83
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
- `researcher`: codebase and documentation research.
|
|
84
|
+
```sh
|
|
85
|
+
opencode plugin opencode-resolve --global --force
|
|
86
|
+
```
|
|
58
87
|
|
|
59
|
-
|
|
88
|
+
**Step 2 — `~/.config/opencode/opencode.json`** (merge into the existing file; do not replace it):
|
|
60
89
|
|
|
61
|
-
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"$schema": "https://opencode.ai/config.json",
|
|
93
|
+
"model": "<your-default-model-id>",
|
|
94
|
+
"plugin": ["opencode-resolve"]
|
|
95
|
+
}
|
|
96
|
+
```
|
|
62
97
|
|
|
63
|
-
|
|
98
|
+
**Step 3 — `~/.config/opencode/resolve.json`** (auto-created by `postinstall`; the block below is the recommended canonical setup — copy it verbatim then edit `models` to match your provider):
|
|
64
99
|
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"enabled": ["coder", "reviewer", "resolver"],
|
|
103
|
+
"preserveNative": true,
|
|
104
|
+
"context7": true,
|
|
105
|
+
"commands": false,
|
|
106
|
+
"autoApprove": true,
|
|
107
|
+
"maxParallelSubagents": 2,
|
|
108
|
+
"models": {
|
|
109
|
+
"glm": "zai-coding-plan/glm-5",
|
|
110
|
+
"gpt": "openai/gpt-5.5",
|
|
111
|
+
"coder": "glm",
|
|
112
|
+
"reviewer": "openai/gpt-4o-mini",
|
|
113
|
+
"resolver": "gpt"
|
|
114
|
+
},
|
|
115
|
+
"agents": {
|
|
116
|
+
"coder": { "mode": "all" },
|
|
117
|
+
"reviewer": { "mode": "all" },
|
|
118
|
+
"resolver": { "enabled": true },
|
|
119
|
+
"architect": { "enabled": false },
|
|
120
|
+
"gpt-coder": { "enabled": false },
|
|
121
|
+
"debugger": { "enabled": false },
|
|
122
|
+
"researcher": { "enabled": false }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
69
125
|
```
|
|
70
126
|
|
|
71
|
-
|
|
127
|
+
**Step 4 — restart OpenCode.**
|
|
72
128
|
|
|
73
|
-
|
|
129
|
+
### Why this template
|
|
74
130
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
131
|
+
- **`enabled`** activates the three default roles: `coder` implements, `reviewer` audits read-only, `resolver` orchestrates.
|
|
132
|
+
- **`autoApprove: true`** lets `coder` and `resolver` work without per-action approval prompts. `reviewer` stays locked to deny — it cannot modify by any means.
|
|
133
|
+
- **`maxParallelSubagents: 2`** is a per-role cap: up to two coders and up to two reviewers may run concurrently when working on genuinely independent things. Drop to `1` for strict per-role serialization, raise above `2` for more aggressive fan-out.
|
|
134
|
+
- **`agents.coder.mode = "all"` and `agents.reviewer.mode = "all"`** make both visible in the primary agent picker, not just as subagents the resolver dispatches. You can still call them directly when you don't need orchestration.
|
|
135
|
+
- **`agents.resolver.enabled: true`** is explicit (default is also true); ships with `mode: "all"` so it appears as both a primary agent and a subagent.
|
|
136
|
+
- **`models`** uses two aliases (`glm` for fast/cheap implementation, `gpt` for stronger orchestration) and pins reviewer to `gpt-4o-mini` for cheap reads. Replace these with model IDs your provider actually exposes.
|
|
137
|
+
- **`context7`** is the only MCP this plugin auto-registers — no other MCP servers are required.
|
|
138
|
+
- All other resolve agents (`architect`, `gpt-coder`, `debugger`, `researcher`) ship disabled. Flip `enabled: true` only if you want to use them.
|
|
139
|
+
|
|
140
|
+
### Resolver workflow (what happens when you call it)
|
|
141
|
+
|
|
142
|
+
1. Resolver reads the request, inspects relevant files.
|
|
143
|
+
2. Plans the smallest correct change.
|
|
144
|
+
3. Dispatches `coder` to implement (one at a time).
|
|
145
|
+
4. Verifies (tests, type checks, targeted checks).
|
|
146
|
+
5. If issues remain, dispatches `coder` again with a focused fix.
|
|
147
|
+
6. For risky changes, optionally consults `reviewer` for a read-only audit; routes any required fixes back through `coder`.
|
|
148
|
+
7. Repeats until the task is resolved or clearly blocked, then returns a concise summary.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Default Behavior
|
|
153
|
+
|
|
154
|
+
| Item | Default |
|
|
155
|
+
|---|---|
|
|
156
|
+
| Enabled agents | `coder`, `reviewer`, `resolver` |
|
|
157
|
+
| Primary agent for new tasks | `resolver` (`mode: "all"`) |
|
|
158
|
+
| Agent model | Inherits top-level OpenCode `model` |
|
|
159
|
+
| Native `plan` / `build` | Preserved untouched |
|
|
160
|
+
| Context7 MCP preset | Added if absent |
|
|
161
|
+
| Optional commands | Disabled |
|
|
162
|
+
| `autoApprove` | `true` (no per-action prompts on coder/resolver) |
|
|
163
|
+
| Reviewer modification | Denied (cannot be auto-approved) |
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Configuration
|
|
168
|
+
|
|
169
|
+
The plugin reads the first config file it finds:
|
|
170
|
+
|
|
171
|
+
| Priority | Path |
|
|
172
|
+
|---:|---|
|
|
173
|
+
| 1 | `.opencode/resolve.json` (project) |
|
|
174
|
+
| 2 | `opencode-resolve.json` (project) |
|
|
175
|
+
| 3 | `~/.config/opencode/resolve.json` |
|
|
176
|
+
| 4 | `~/.config/opencode/opencode-resolve.json` |
|
|
177
|
+
|
|
178
|
+
Inline plugin options in `opencode.json` override file config.
|
|
179
|
+
|
|
180
|
+
Config precedence:
|
|
181
|
+
|
|
182
|
+
```text
|
|
183
|
+
built-in defaults -> first config file found -> inline plugin options
|
|
79
184
|
```
|
|
80
185
|
|
|
81
|
-
|
|
186
|
+
Minimal config (matches defaults):
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"enabled": ["coder", "reviewer", "resolver"],
|
|
191
|
+
"autoApprove": true,
|
|
192
|
+
"context7": true,
|
|
193
|
+
"commands": false
|
|
194
|
+
}
|
|
195
|
+
```
|
|
82
196
|
|
|
83
|
-
|
|
197
|
+
Inline form inside `opencode.json`:
|
|
84
198
|
|
|
85
199
|
```json
|
|
86
200
|
{
|
|
87
|
-
"plugin": [
|
|
201
|
+
"plugin": [
|
|
202
|
+
[
|
|
203
|
+
"opencode-resolve",
|
|
204
|
+
{
|
|
205
|
+
"enabled": ["coder", "reviewer", "resolver"],
|
|
206
|
+
"autoApprove": true,
|
|
207
|
+
"context7": true,
|
|
208
|
+
"commands": false
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
]
|
|
88
212
|
}
|
|
89
213
|
```
|
|
90
214
|
|
|
91
|
-
|
|
215
|
+
Strict validation rejects unknown agent names, misspelled keys, invalid modes, invalid permission values, and wrong value types — typos fail fast.
|
|
92
216
|
|
|
93
|
-
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Configuration Reference
|
|
220
|
+
|
|
221
|
+
Every accepted top-level option:
|
|
222
|
+
|
|
223
|
+
| Key | Type | Default | Purpose |
|
|
224
|
+
|---|---|---|---|
|
|
225
|
+
| `enabled` | `string[]` | `["coder", "reviewer", "resolver"]` | Which resolve agents to inject. Per-agent `agents.<name>.enabled` overrides this. |
|
|
226
|
+
| `preserveNative` | `boolean` | `true` | Native `plan`/`build` are always preserved. Accepted for readability. |
|
|
227
|
+
| `context7` | `boolean` | `true` | When true, registers the Context7 MCP server unless already configured. |
|
|
228
|
+
| `commands` | `boolean` | `false` | When true, adds `resolve`, `resolve-code`, `resolve-review` commands. |
|
|
229
|
+
| `autoApprove` | `boolean` | `true` | Flips default `"ask"` permissions to `"allow"` on enabled agents. Never touches `"deny"` or user-set keys. |
|
|
230
|
+
| `maxParallelSubagents` | `positive integer` | `1` | Cap on simultaneous subagents the resolver dispatches across coder, reviewer, etc. |
|
|
231
|
+
| `models` | `object` | `{}` | Alias map. Keys are agent names or `glm`/`gpt`. Values are model ids or other aliases. |
|
|
232
|
+
| `agents` | `object` | `{}` | Per-agent overrides (see below). |
|
|
233
|
+
| `config` | `string` | _none_ | Custom path to a config file (relative to the project or absolute). |
|
|
234
|
+
|
|
235
|
+
Per-agent options inside `agents.<name>`:
|
|
236
|
+
|
|
237
|
+
| Key | Type | Notes |
|
|
238
|
+
|---|---|---|
|
|
239
|
+
| `enabled` | `boolean` | Force-enable or force-disable this agent regardless of top-level `enabled`. |
|
|
240
|
+
| `model` | `string` | Model id or alias. Resolved against the top-level `models` map. |
|
|
241
|
+
| `mode` | `"subagent" \| "primary" \| "all"` | OpenCode agent mode. |
|
|
242
|
+
| `description` | `string` | Override the default description shown to other agents. |
|
|
243
|
+
| `prompt` | `string` | Override the default system prompt. (For `resolver`, this also disables the templated parallel-rule prompt.) |
|
|
244
|
+
| `color` | `string` | UI color. |
|
|
245
|
+
| `maxSteps` | `positive integer` | Per-invocation step budget. |
|
|
246
|
+
| `tools` | `Record<string, boolean>` | Toggle individual OpenCode tools. |
|
|
247
|
+
| `permission` | `object` | Permission overrides — see below. |
|
|
248
|
+
|
|
249
|
+
Permission keys (each takes `"ask"`, `"allow"`, or `"deny"`):
|
|
250
|
+
|
|
251
|
+
`edit`, `bash`, `webfetch`, `doom_loop`, `external_directory`.
|
|
252
|
+
|
|
253
|
+
`permission.bash` may also be a per-command map:
|
|
254
|
+
|
|
255
|
+
```json
|
|
256
|
+
{
|
|
257
|
+
"permission": {
|
|
258
|
+
"bash": { "npm test": "allow", "rm -rf": "deny" }
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
A fully-annotated reference config ships with the package as
|
|
264
|
+
[`opencode-resolve.reference.jsonc`](./opencode-resolve.reference.jsonc) — copy
|
|
265
|
+
the keys you need into your `resolve.json` (without the comments).
|
|
266
|
+
|
|
267
|
+
---
|
|
94
268
|
|
|
95
|
-
|
|
96
|
-
- `opencode-resolve.json`
|
|
97
|
-
- `~/.config/opencode/resolve.json`
|
|
98
|
-
- `~/.config/opencode/opencode-resolve.json`
|
|
269
|
+
## Auto Approval
|
|
99
270
|
|
|
100
|
-
|
|
271
|
+
`autoApprove` (default `true`) flips every `"ask"` permission on the **enabled** agents to `"allow"`, so coder and resolver work continuously without per-action prompts. It never touches `"deny"` and never overrides a permission key the user explicitly set.
|
|
272
|
+
|
|
273
|
+
| Permission state | autoApprove: true | autoApprove: false |
|
|
274
|
+
|---|---|---|
|
|
275
|
+
| Default `"ask"` | becomes `"allow"` | stays `"ask"` |
|
|
276
|
+
| Default `"deny"` | stays `"deny"` | stays `"deny"` |
|
|
277
|
+
| User explicit `"ask"` | stays `"ask"` | stays `"ask"` |
|
|
278
|
+
| User explicit `"allow"` | stays `"allow"` | stays `"allow"` |
|
|
279
|
+
| User explicit `"deny"` | stays `"deny"` | stays `"deny"` |
|
|
280
|
+
|
|
281
|
+
Reviewer permissions for `edit` and `bash` are `"deny"` in the defaults, so `autoApprove` cannot grant the reviewer modification rights.
|
|
282
|
+
|
|
283
|
+
Turn it off when you want the conservative ask-every-time behavior:
|
|
101
284
|
|
|
102
285
|
```json
|
|
103
286
|
{
|
|
104
|
-
"
|
|
287
|
+
"autoApprove": false
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Trust note: `autoApprove: true` assumes you trust the workspace and the model you have configured. Use a sandbox or VM for untrusted code, and keep `autoApprove: false` if you want to inspect every action.
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Parallel Subagent Limit
|
|
296
|
+
|
|
297
|
+
`maxParallelSubagents` (default `2`) caps how many subagents the **resolver** may dispatch concurrently **per role**. The default of `2` lets up to two coders run in parallel AND up to two reviewers run in parallel — total up to four subagents in flight when both roles are active. Subagents of different roles may always run concurrently (e.g. coder implementing while reviewer audits the previous step).
|
|
298
|
+
|
|
299
|
+
| Value | Behavior |
|
|
300
|
+
|---|---|
|
|
301
|
+
| `1` | Strictly one of each role at a time. Coder may run while reviewer runs, but never two coders or two reviewers in parallel. |
|
|
302
|
+
| `2` (default) | Up to two of each role concurrently. Total in flight up to (per-role limit × number of active roles). |
|
|
303
|
+
| `N > 2` | Up to N of each role concurrently. Useful when fanning out genuinely independent work. |
|
|
304
|
+
|
|
305
|
+
Override per project or per user:
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{ "maxParallelSubagents": 1 } // strictest
|
|
309
|
+
{ "maxParallelSubagents": 2 } // default
|
|
310
|
+
{ "maxParallelSubagents": 4 } // aggressive parallelism
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
> **Important — soft limit, not a hard cap.** The limit is woven into the resolver's system prompt only. There is no runtime interceptor that blocks excess dispatches. Modern models (GPT-5.x, GLM-5, Claude 4.x) generally respect the directive, but if a model misbehaves, dispatches above the limit will go through. Pair this with `maxSteps` to bound total iterations if you want a stricter ceiling.
|
|
314
|
+
|
|
315
|
+
The limit is templated into the prompt at config-load time, so restart OpenCode to pick up the new value. If you provide a custom `agents.resolver.prompt`, the templated rule is skipped and your prompt wins entirely.
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Upgrade & Migration
|
|
320
|
+
|
|
321
|
+
When you upgrade to a newer version of `opencode-resolve`, the `postinstall` script runs an **additive migration** on your existing `~/.config/opencode/resolve.json`:
|
|
322
|
+
|
|
323
|
+
- Adds new top-level keys (e.g. `autoApprove`, `maxParallelSubagents`) with their defaults if they are absent.
|
|
324
|
+
- **Never** modifies keys you have already set.
|
|
325
|
+
- **Never** rewrites your `enabled` list, `models` map, or `agents` overrides.
|
|
326
|
+
- If `enabled` is set and does not include `"resolver"`, prints a one-line tip suggesting you add it. Your file is left untouched.
|
|
327
|
+
|
|
328
|
+
Skip the migration entirely with:
|
|
329
|
+
|
|
330
|
+
```sh
|
|
331
|
+
OPENCODE_RESOLVE_SKIP_POSTINSTALL=1 npm install -g opencode-resolve
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Model Setup
|
|
337
|
+
|
|
338
|
+
`opencode-resolve` does not ship a provider-specific role default.
|
|
339
|
+
|
|
340
|
+
Model resolution order for each resolve agent:
|
|
341
|
+
|
|
342
|
+
1. `agents.<name>.model`
|
|
343
|
+
2. `models.<name>` alias mapping
|
|
344
|
+
3. top-level OpenCode `model`
|
|
345
|
+
4. OpenCode's own fallback when no model is configured
|
|
346
|
+
|
|
347
|
+
Use the default config when all resolve roles should follow your current OpenCode model.
|
|
348
|
+
|
|
349
|
+
Pin models only when you intentionally want fixed role behavior, such as fast coding on one model and deeper review on another.
|
|
350
|
+
|
|
351
|
+
Role-specific aliases:
|
|
352
|
+
|
|
353
|
+
```json
|
|
354
|
+
{
|
|
355
|
+
"enabled": ["coder", "reviewer", "resolver"],
|
|
105
356
|
"models": {
|
|
106
|
-
"glm": "zai-coding-plan/glm-5",
|
|
107
|
-
"gpt": "openai/gpt-5",
|
|
357
|
+
"glm": "zai-coding-plan/glm-5.1",
|
|
358
|
+
"gpt": "openai/gpt-5.5",
|
|
108
359
|
"coder": "glm",
|
|
360
|
+
"resolver": "gpt",
|
|
109
361
|
"reviewer": "gpt"
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Pin one role directly:
|
|
367
|
+
|
|
368
|
+
```json
|
|
369
|
+
{
|
|
114
370
|
"agents": {
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
},
|
|
118
|
-
"gpt-coder": {
|
|
119
|
-
"enabled": false,
|
|
120
|
-
"model": "gpt"
|
|
371
|
+
"reviewer": {
|
|
372
|
+
"model": "openai/gpt-5.5"
|
|
121
373
|
}
|
|
122
374
|
}
|
|
123
375
|
}
|
|
124
376
|
```
|
|
125
377
|
|
|
126
|
-
|
|
378
|
+
Native OpenCode agents such as `plan` and `build` are configured through the top-level OpenCode `agent`, not through `opencode-resolve`.
|
|
379
|
+
|
|
380
|
+
Mixed setup example:
|
|
127
381
|
|
|
128
382
|
```json
|
|
129
383
|
{
|
|
384
|
+
"model": "zai-coding-plan/glm-5.1",
|
|
385
|
+
"agent": {
|
|
386
|
+
"plan": {
|
|
387
|
+
"model": "openai/gpt-5.5"
|
|
388
|
+
}
|
|
389
|
+
},
|
|
130
390
|
"plugin": [
|
|
131
391
|
[
|
|
132
392
|
"opencode-resolve",
|
|
133
393
|
{
|
|
134
|
-
"enabled": ["coder", "reviewer", "
|
|
135
|
-
"
|
|
394
|
+
"enabled": ["coder", "reviewer", "resolver"],
|
|
395
|
+
"models": {
|
|
396
|
+
"glm": "zai-coding-plan/glm-5.1",
|
|
397
|
+
"gpt": "openai/gpt-5.5",
|
|
398
|
+
"coder": "glm",
|
|
399
|
+
"resolver": "gpt",
|
|
400
|
+
"reviewer": "gpt"
|
|
401
|
+
}
|
|
136
402
|
}
|
|
137
403
|
]
|
|
138
404
|
]
|
|
139
405
|
}
|
|
140
406
|
```
|
|
141
407
|
|
|
142
|
-
|
|
408
|
+
In this setup, `plan`, `resolver`, and `reviewer` use `openai/gpt-5.5`; native `build` and resolve `coder` use `zai-coding-plan/glm-5.1`.
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## Agent Reference
|
|
413
|
+
|
|
414
|
+
| Agent | Default | Mode | Edit | Bash | WebFetch | Purpose |
|
|
415
|
+
|---|:---:|---|---|---|---|---|
|
|
416
|
+
| `resolver` | Yes | `all` | ask → allow | ask → allow | ask → allow | Primary orchestrator. Plans, dispatches `coder` (one at a time), verifies, iterates to completion. |
|
|
417
|
+
| `coder` | Yes | `subagent` | ask → allow | ask → allow | ask → allow | Focused implementer. Smallest correct change. |
|
|
418
|
+
| `reviewer` | Yes | `subagent` | **deny** | **deny** | ask → allow | Read-only auditor. Cannot modify by any means. |
|
|
419
|
+
| `architect` | No | `subagent` | deny | ask → allow | ask → allow | Design and task decomposition. |
|
|
420
|
+
| `gpt-coder` | No | `subagent` | ask → allow | ask → allow | ask → allow | Stronger-reasoning implementation fallback. |
|
|
421
|
+
| `debugger` | No | `subagent` | ask → allow | ask → allow | ask → allow | Reproduction and root-cause analysis. |
|
|
422
|
+
| `researcher` | No | `subagent` | deny | ask → allow | ask → allow | Codebase and documentation research. |
|
|
423
|
+
|
|
424
|
+
`ask → allow` means the default is `"ask"` and `autoApprove` (default on) flips it to `"allow"`. Set `autoApprove: false` to keep them as `"ask"`.
|
|
425
|
+
|
|
426
|
+
Supported modes:
|
|
427
|
+
|
|
428
|
+
| Mode | Meaning |
|
|
429
|
+
|---|---|
|
|
430
|
+
| `subagent` | Available only as a subagent |
|
|
431
|
+
| `primary` | Available as a primary agent |
|
|
432
|
+
| `all` | Available as both primary and subagent |
|
|
433
|
+
|
|
434
|
+
Supported permission values: `ask`, `allow`, `deny`.
|
|
435
|
+
|
|
436
|
+
Supported model alias keys: `glm`, `gpt`, and every supported agent name. Aliases only resolve when defined in `models`.
|
|
437
|
+
|
|
438
|
+
`preserveNative` is accepted for readability, but native `plan` and `build` are always preserved. The plugin never rewrites built-in OpenCode agents.
|
|
143
439
|
|
|
144
|
-
|
|
440
|
+
### Resolver orchestration rules
|
|
145
441
|
|
|
146
|
-
The
|
|
442
|
+
The resolver's prompt enforces the following behavior:
|
|
147
443
|
|
|
148
|
-
|
|
444
|
+
- Plan the smallest correct change before dispatching.
|
|
445
|
+
- Dispatch **only one `coder` subagent at a time**. Never call coders in parallel.
|
|
446
|
+
- After each coder run, verify (tests, type checks, targeted checks) when practical.
|
|
447
|
+
- Optionally consult `reviewer` for an independent read-only audit on risky changes; route any required fixes back through `coder`.
|
|
448
|
+
- Iterate until the task is resolved or clearly blocked, then return a concise summary.
|
|
149
449
|
|
|
150
|
-
|
|
151
|
-
- `reviewer`
|
|
152
|
-
- `architect`
|
|
153
|
-
- `gpt-coder`
|
|
154
|
-
- `debugger`
|
|
155
|
-
- `researcher`
|
|
450
|
+
---
|
|
156
451
|
|
|
157
|
-
|
|
452
|
+
## Optional Commands
|
|
158
453
|
|
|
159
|
-
|
|
160
|
-
- `gpt`
|
|
161
|
-
- every supported agent name
|
|
454
|
+
Set `commands: true` to add helper subtask commands:
|
|
162
455
|
|
|
163
|
-
|
|
456
|
+
| Command | Description |
|
|
457
|
+
|---|---|
|
|
458
|
+
| `resolve` | Run the `resolver` agent end-to-end on the current task |
|
|
459
|
+
| `resolve-code` | Run the `coder` agent for focused implementation |
|
|
460
|
+
| `resolve-review` | Run the `reviewer` agent for a read-only audit |
|
|
164
461
|
|
|
165
|
-
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
## Local Development
|
|
465
|
+
|
|
466
|
+
From this repository:
|
|
467
|
+
|
|
468
|
+
```sh
|
|
469
|
+
npm install
|
|
470
|
+
npm test
|
|
471
|
+
npm run install:local
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
`install:local` builds the plugin, links it into the OpenCode global plugin directory, and creates `~/.config/opencode/resolve.json` if it does not exist.
|
|
166
475
|
|
|
167
|
-
|
|
476
|
+
Manual local install:
|
|
168
477
|
|
|
169
|
-
|
|
478
|
+
```sh
|
|
479
|
+
npm run build
|
|
480
|
+
mkdir -p ~/.config/opencode/plugins
|
|
481
|
+
ln -sf "$PWD/dist/index.js" ~/.config/opencode/plugins/opencode-resolve.js
|
|
482
|
+
```
|
|
170
483
|
|
|
171
|
-
|
|
484
|
+
Local plugin files are loaded automatically by OpenCode.
|
|
172
485
|
|
|
173
|
-
|
|
174
|
-
- `resolve-review`: run the `reviewer` agent for requirement/risk review.
|
|
486
|
+
---
|
|
175
487
|
|
|
176
488
|
## Verification
|
|
177
489
|
|
|
490
|
+
Run the normal checks:
|
|
491
|
+
|
|
178
492
|
```sh
|
|
179
493
|
npm run typecheck
|
|
180
494
|
npm test
|
|
181
495
|
npm run build
|
|
182
496
|
```
|
|
183
497
|
|
|
184
|
-
The test suite executes the built plugin and verifies default agent injection, model aliases, file config, plugin option overrides, optional commands, Context7 preservation, and native `plan`/`build` preservation.
|
|
498
|
+
The test suite executes the built plugin and verifies default agent injection, `autoApprove` behavior, model aliases, file config, plugin option overrides, optional commands, Context7 preservation, and native `plan`/`build` preservation.
|
|
185
499
|
|
|
186
|
-
Before publishing
|
|
500
|
+
Before publishing:
|
|
187
501
|
|
|
188
502
|
```sh
|
|
189
503
|
npm run typecheck
|
|
@@ -194,13 +508,17 @@ npm publish --dry-run
|
|
|
194
508
|
|
|
195
509
|
`npm pack` and `npm publish` run `npm test` first through the `prepack` script.
|
|
196
510
|
|
|
511
|
+
---
|
|
512
|
+
|
|
197
513
|
## Release
|
|
198
514
|
|
|
199
515
|
Releases are published by GitHub Actions when a version tag is pushed.
|
|
200
516
|
|
|
201
517
|
Required repository secret:
|
|
202
518
|
|
|
203
|
-
|
|
519
|
+
| Secret | Description |
|
|
520
|
+
|---|---|
|
|
521
|
+
| `NPM_TOKEN` | npm automation token with publish access |
|
|
204
522
|
|
|
205
523
|
Tag release flow:
|
|
206
524
|
|
|
@@ -213,32 +531,12 @@ You can also run the `Publish to npm` workflow manually from GitHub Actions and
|
|
|
213
531
|
|
|
214
532
|
The release workflow runs `npm ci`, `npm run typecheck`, `npm test`, and `npm publish --access public --provenance`.
|
|
215
533
|
|
|
534
|
+
---
|
|
535
|
+
|
|
216
536
|
## Design Rules
|
|
217
537
|
|
|
218
538
|
- Do not overwrite native `plan` or `build` agents.
|
|
219
|
-
- Keep default
|
|
220
|
-
-
|
|
221
|
-
-
|
|
222
|
-
- Verify when practical.
|
|
223
|
-
- Use reviewer for final requirement/risk checks instead of running many agents by default.
|
|
224
|
-
|
|
225
|
-
## Companion Plugins
|
|
226
|
-
|
|
227
|
-
`opencode-resolve` intentionally stays focused on agent injection and task resolution. Use notification or remote-control plugins alongside it instead of baking those concerns into this package.
|
|
228
|
-
|
|
229
|
-
Recommended companion:
|
|
230
|
-
|
|
231
|
-
- [`@jshsakura/opencode-telegram-bot-plugin`](https://github.com/jshsakura/opencode-telegram-bot-plugin): Telegram notifications and permission-response support for OpenCode sessions.
|
|
232
|
-
|
|
233
|
-
Install and configure the Telegram plugin separately. It needs its own bot token/chat setup; see that plugin's README for details.
|
|
234
|
-
|
|
235
|
-
Example:
|
|
236
|
-
|
|
237
|
-
```json
|
|
238
|
-
{
|
|
239
|
-
"plugin": [
|
|
240
|
-
"opencode-resolve",
|
|
241
|
-
"@jshsakura/opencode-telegram-bot-plugin"
|
|
242
|
-
]
|
|
243
|
-
}
|
|
244
|
-
```
|
|
539
|
+
- Keep the default agent set small and role-clear: `resolver` orchestrates, `coder` modifies, `reviewer` only reads.
|
|
540
|
+
- The reviewer never modifies anything — fixes always go through `coder` or `resolver`.
|
|
541
|
+
- The resolver dispatches at most one coder at a time.
|
|
542
|
+
- Search and inspect before editing. Make the smallest correct change. Verify when practical.
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import { access, readFile } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { basename, isAbsolute, join, resolve } from "node:path";
|
|
4
|
-
const DEFAULT_MODELS = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
coder: "zai-coding-plan/glm-5",
|
|
8
|
-
reviewer: "openai/gpt-5",
|
|
9
|
-
architect: "openai/gpt-5",
|
|
10
|
-
"gpt-coder": "openai/gpt-5",
|
|
11
|
-
debugger: "zai-coding-plan/glm-5",
|
|
12
|
-
researcher: "zai-coding-plan/glm-5",
|
|
13
|
-
};
|
|
14
|
-
const DEFAULT_ENABLED = ["coder", "reviewer"];
|
|
15
|
-
const VALID_AGENT_NAMES = ["coder", "reviewer", "architect", "gpt-coder", "debugger", "researcher"];
|
|
4
|
+
const DEFAULT_MODELS = {};
|
|
5
|
+
const DEFAULT_ENABLED = ["coder", "reviewer", "resolver"];
|
|
6
|
+
const VALID_AGENT_NAMES = ["coder", "reviewer", "resolver", "architect", "gpt-coder", "debugger", "researcher"];
|
|
16
7
|
const VALID_AGENT_NAME_SET = new Set(VALID_AGENT_NAMES);
|
|
17
8
|
const VALID_MODEL_ALIASES = [...VALID_AGENT_NAMES, "glm", "gpt"];
|
|
18
9
|
const VALID_MODEL_ALIAS_SET = new Set(VALID_MODEL_ALIASES);
|
|
@@ -25,8 +16,11 @@ const VALID_TOP_LEVEL_KEYS = new Set([
|
|
|
25
16
|
"preserveNative",
|
|
26
17
|
"context7",
|
|
27
18
|
"commands",
|
|
19
|
+
"autoApprove",
|
|
20
|
+
"maxParallelSubagents",
|
|
28
21
|
"config",
|
|
29
22
|
]);
|
|
23
|
+
const DEFAULT_MAX_PARALLEL_SUBAGENTS = 2;
|
|
30
24
|
const VALID_AGENT_KEYS = new Set([
|
|
31
25
|
"enabled",
|
|
32
26
|
"model",
|
|
@@ -38,6 +32,27 @@ const VALID_AGENT_KEYS = new Set([
|
|
|
38
32
|
"tools",
|
|
39
33
|
"permission",
|
|
40
34
|
]);
|
|
35
|
+
function buildResolverPrompt(maxParallelSubagents) {
|
|
36
|
+
const limit = Math.max(1, Math.trunc(maxParallelSubagents));
|
|
37
|
+
const parallelRule = limit === 1
|
|
38
|
+
? "CRITICAL: Dispatch at most ONE subagent of each role concurrently. Never run two coders in parallel, and never run two reviewers in parallel. A coder and a reviewer MAY run concurrently when they are doing genuinely independent work (e.g. coder is implementing the next change while reviewer audits the previous one). Wait for an in-flight subagent of a given role to finish before dispatching another of the same role."
|
|
39
|
+
: `CRITICAL: Dispatch at most ${limit} subagents of the same role concurrently. Never exceed ${limit} coders in parallel, and never exceed ${limit} reviewers in parallel. Subagents of different roles may run concurrently when they are doing genuinely independent work. Wait for in-flight subagents of a given role to finish before dispatching more of that role.`;
|
|
40
|
+
return [
|
|
41
|
+
"You are Resolver, the primary orchestrator agent for OpenCode Resolve.",
|
|
42
|
+
"Your job is to drive the user's task to a verified resolution end-to-end without unnecessary stops.",
|
|
43
|
+
"Workflow:",
|
|
44
|
+
"1. Understand the requirement. Inspect the relevant files briefly before planning.",
|
|
45
|
+
"2. Plan the smallest correct change.",
|
|
46
|
+
"3. Dispatch the coder subagent to implement the change. Pass a focused instruction with the exact files and behavior.",
|
|
47
|
+
`4. ${parallelRule}`,
|
|
48
|
+
"5. After implementation, verify when practical (run tests, type checks, or targeted checks).",
|
|
49
|
+
"6. If issues remain, dispatch the coder again with a focused fix, or apply a small direct edit yourself when it is clearly trivial.",
|
|
50
|
+
"7. Optionally consult the reviewer subagent for an independent read-only review on risky changes. The reviewer cannot modify anything; treat its output as advice and route any required fixes back through the coder. The same per-role parallel limit applies to the reviewer.",
|
|
51
|
+
"8. Repeat until the task is resolved or clearly blocked.",
|
|
52
|
+
"Return a concise summary of what changed, verification results, and any remaining blockers.",
|
|
53
|
+
"Note: this parallel rule is enforced via prompt only — there is no runtime cap on subagent dispatches. Honor it strictly to avoid file conflicts and wasted work.",
|
|
54
|
+
].join("\n");
|
|
55
|
+
}
|
|
41
56
|
const DEFAULT_AGENT_CONFIG = {
|
|
42
57
|
coder: {
|
|
43
58
|
mode: "subagent",
|
|
@@ -61,16 +76,29 @@ const DEFAULT_AGENT_CONFIG = {
|
|
|
61
76
|
mode: "subagent",
|
|
62
77
|
color: "#8A7CFF",
|
|
63
78
|
maxSteps: 8,
|
|
64
|
-
description: "
|
|
79
|
+
description: "Read-only Oracle-style reviewer. Inspects code for requirements fit, correctness, security, tests, and maintainability risks. Never modifies anything.",
|
|
65
80
|
prompt: [
|
|
66
|
-
"You are Reviewer,
|
|
67
|
-
"
|
|
81
|
+
"You are Reviewer, a strictly read-only review subagent for OpenCode Resolve.",
|
|
82
|
+
"You MUST NOT modify the project by any means: no file edits, no writes, no shell commands that change state, no git commits, no package installs.",
|
|
83
|
+
"Use read-only tools (read, grep, glob, list, web fetch for documentation) to inspect the work against the user's requirements and the repository's existing patterns.",
|
|
68
84
|
"Prioritize concrete bugs, behavioral regressions, security risks, missing tests, and maintainability issues.",
|
|
69
|
-
"
|
|
70
|
-
"If
|
|
85
|
+
"Return findings ordered by severity with file and line references when available. If there are no findings, say so and mention residual risks or verification gaps.",
|
|
86
|
+
"If a fix is needed, describe it precisely and recommend dispatching the coder or resolver agent. Never apply fixes yourself.",
|
|
71
87
|
].join("\n"),
|
|
72
88
|
permission: {
|
|
73
89
|
edit: "deny",
|
|
90
|
+
bash: "deny",
|
|
91
|
+
webfetch: "ask",
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
resolver: {
|
|
95
|
+
mode: "all",
|
|
96
|
+
color: "#FF7AC6",
|
|
97
|
+
maxSteps: 30,
|
|
98
|
+
description: "Primary orchestrator. Drives a task to completion by planning, dispatching subagents (one at a time by default), and verifying results. Iterates until the task is resolved or clearly blocked.",
|
|
99
|
+
prompt: buildResolverPrompt(DEFAULT_MAX_PARALLEL_SUBAGENTS),
|
|
100
|
+
permission: {
|
|
101
|
+
edit: "ask",
|
|
74
102
|
bash: "ask",
|
|
75
103
|
webfetch: "ask",
|
|
76
104
|
},
|
|
@@ -166,6 +194,9 @@ async function loadResolveConfig(directory, opencodeConfig, options) {
|
|
|
166
194
|
function applyResolveConfig(config, resolveConfig) {
|
|
167
195
|
const enabled = new Set(resolveConfig.enabled ?? DEFAULT_ENABLED);
|
|
168
196
|
const models = { ...DEFAULT_MODELS, ...resolveConfig.models };
|
|
197
|
+
const defaultModel = typeof config.model === "string" ? config.model : undefined;
|
|
198
|
+
const autoApprove = resolveConfig.autoApprove !== false;
|
|
199
|
+
const maxParallelSubagents = resolveConfig.maxParallelSubagents ?? DEFAULT_MAX_PARALLEL_SUBAGENTS;
|
|
169
200
|
config.agent ??= {};
|
|
170
201
|
for (const name of Object.keys(DEFAULT_AGENT_CONFIG)) {
|
|
171
202
|
const override = resolveConfig.agents?.[name];
|
|
@@ -173,13 +204,21 @@ function applyResolveConfig(config, resolveConfig) {
|
|
|
173
204
|
if (!isEnabled)
|
|
174
205
|
continue;
|
|
175
206
|
const base = DEFAULT_AGENT_CONFIG[name];
|
|
176
|
-
const { enabled: _enabled, ...agentOverride } = override ?? {};
|
|
177
|
-
const model = resolveModel(
|
|
178
|
-
|
|
207
|
+
const { enabled: _enabled, model: requestedModel, permission: userPermission, ...agentOverride } = override ?? {};
|
|
208
|
+
const model = resolveModel(requestedModel ?? models[name] ?? defaultModel, models);
|
|
209
|
+
const permission = buildPermission(base.permission, userPermission, autoApprove);
|
|
210
|
+
const agentConfig = {
|
|
179
211
|
...base,
|
|
180
212
|
...agentOverride,
|
|
181
|
-
model,
|
|
182
213
|
};
|
|
214
|
+
if (name === "resolver" && agentOverride.prompt === undefined) {
|
|
215
|
+
agentConfig.prompt = buildResolverPrompt(maxParallelSubagents);
|
|
216
|
+
}
|
|
217
|
+
if (permission)
|
|
218
|
+
agentConfig.permission = permission;
|
|
219
|
+
if (model)
|
|
220
|
+
agentConfig.model = model;
|
|
221
|
+
config.agent[name] = agentConfig;
|
|
183
222
|
}
|
|
184
223
|
if (resolveConfig.context7 !== false) {
|
|
185
224
|
config.mcp ??= {};
|
|
@@ -190,9 +229,15 @@ function applyResolveConfig(config, resolveConfig) {
|
|
|
190
229
|
}
|
|
191
230
|
if (resolveConfig.commands) {
|
|
192
231
|
config.command ??= {};
|
|
232
|
+
config.command["resolve"] ??= {
|
|
233
|
+
template: "Drive this task to a verified resolution end-to-end. Plan, dispatch one coder at a time, verify, and iterate. $ARGUMENTS",
|
|
234
|
+
description: "Run the OpenCode Resolve resolver agent end-to-end",
|
|
235
|
+
agent: "resolver",
|
|
236
|
+
subtask: true,
|
|
237
|
+
};
|
|
193
238
|
config.command["resolve-review"] ??= {
|
|
194
|
-
template: "Review the current implementation against the user's requirements. Focus on correctness, tests, security, and maintainability.",
|
|
195
|
-
description: "Run the OpenCode Resolve reviewer agent",
|
|
239
|
+
template: "Review the current implementation against the user's requirements. Focus on correctness, tests, security, and maintainability. Do not modify anything.",
|
|
240
|
+
description: "Run the OpenCode Resolve reviewer agent (read-only)",
|
|
196
241
|
agent: "reviewer",
|
|
197
242
|
subtask: true,
|
|
198
243
|
};
|
|
@@ -207,11 +252,13 @@ function applyResolveConfig(config, resolveConfig) {
|
|
|
207
252
|
function defaultResolveConfig() {
|
|
208
253
|
return {
|
|
209
254
|
enabled: DEFAULT_ENABLED,
|
|
210
|
-
models:
|
|
255
|
+
models: {},
|
|
211
256
|
agents: {},
|
|
212
257
|
preserveNative: true,
|
|
213
258
|
context7: true,
|
|
214
259
|
commands: false,
|
|
260
|
+
autoApprove: true,
|
|
261
|
+
maxParallelSubagents: DEFAULT_MAX_PARALLEL_SUBAGENTS,
|
|
215
262
|
};
|
|
216
263
|
}
|
|
217
264
|
function mergeResolveConfig(...configs) {
|
|
@@ -223,6 +270,8 @@ function mergeResolveConfig(...configs) {
|
|
|
223
270
|
result.preserveNative = config.preserveNative ?? result.preserveNative;
|
|
224
271
|
result.context7 = config.context7 ?? result.context7;
|
|
225
272
|
result.commands = config.commands ?? result.commands;
|
|
273
|
+
result.autoApprove = config.autoApprove ?? result.autoApprove;
|
|
274
|
+
result.maxParallelSubagents = config.maxParallelSubagents ?? result.maxParallelSubagents;
|
|
226
275
|
result.models = { ...result.models, ...config.models };
|
|
227
276
|
result.agents = mergeAgents(result.agents, config.agents);
|
|
228
277
|
}
|
|
@@ -240,6 +289,27 @@ function resolveModel(model, models) {
|
|
|
240
289
|
return undefined;
|
|
241
290
|
return models[model] ?? model;
|
|
242
291
|
}
|
|
292
|
+
function buildPermission(basePermission, userPermission, autoApprove) {
|
|
293
|
+
const merged = {
|
|
294
|
+
...(basePermission ?? {}),
|
|
295
|
+
...(userPermission ?? {}),
|
|
296
|
+
};
|
|
297
|
+
if (Object.keys(merged).length === 0)
|
|
298
|
+
return undefined;
|
|
299
|
+
if (!autoApprove)
|
|
300
|
+
return merged;
|
|
301
|
+
const userKeys = new Set(Object.keys(userPermission ?? {}));
|
|
302
|
+
const result = { ...merged };
|
|
303
|
+
for (const key of Object.keys(result)) {
|
|
304
|
+
if (userKeys.has(key))
|
|
305
|
+
continue;
|
|
306
|
+
const value = result[key];
|
|
307
|
+
if (value === "ask") {
|
|
308
|
+
result[key] = "allow";
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return result;
|
|
312
|
+
}
|
|
243
313
|
function getPluginOptions(config) {
|
|
244
314
|
for (const entry of config.plugin ?? []) {
|
|
245
315
|
if (Array.isArray(entry) && isResolvePluginEntry(entry[0])) {
|
|
@@ -310,6 +380,15 @@ function normalizeResolveConfig(value, source) {
|
|
|
310
380
|
result.context7 = expectBoolean(config.context7, `${source}.context7`);
|
|
311
381
|
if (config.commands !== undefined)
|
|
312
382
|
result.commands = expectBoolean(config.commands, `${source}.commands`);
|
|
383
|
+
if (config.autoApprove !== undefined)
|
|
384
|
+
result.autoApprove = expectBoolean(config.autoApprove, `${source}.autoApprove`);
|
|
385
|
+
if (config.maxParallelSubagents !== undefined) {
|
|
386
|
+
const limit = expectNumber(config.maxParallelSubagents, `${source}.maxParallelSubagents`);
|
|
387
|
+
if (!Number.isInteger(limit) || limit < 1) {
|
|
388
|
+
throw new Error(`${source}.maxParallelSubagents must be a positive integer`);
|
|
389
|
+
}
|
|
390
|
+
result.maxParallelSubagents = limit;
|
|
391
|
+
}
|
|
313
392
|
if (config.config !== undefined)
|
|
314
393
|
result.config = expectString(config.config, `${source}.config`);
|
|
315
394
|
return result;
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
{
|
|
2
|
-
"enabled": ["coder", "reviewer"],
|
|
3
|
-
"models": {
|
|
4
|
-
"glm": "zai-coding-plan/glm-5",
|
|
5
|
-
"gpt": "openai/gpt-5",
|
|
6
|
-
"coder": "glm",
|
|
7
|
-
"reviewer": "gpt",
|
|
8
|
-
"architect": "gpt",
|
|
9
|
-
"gpt-coder": "gpt",
|
|
10
|
-
"debugger": "glm",
|
|
11
|
-
"researcher": "glm"
|
|
12
|
-
},
|
|
2
|
+
"enabled": ["coder", "reviewer", "resolver"],
|
|
13
3
|
"preserveNative": true,
|
|
14
4
|
"context7": true,
|
|
15
5
|
"commands": false,
|
|
6
|
+
"autoApprove": true,
|
|
7
|
+
"maxParallelSubagents": 2,
|
|
8
|
+
"models": {},
|
|
16
9
|
"agents": {
|
|
10
|
+
"coder": {
|
|
11
|
+
"mode": "all"
|
|
12
|
+
},
|
|
13
|
+
"reviewer": {
|
|
14
|
+
"mode": "all"
|
|
15
|
+
},
|
|
16
|
+
"resolver": {
|
|
17
|
+
"enabled": true
|
|
18
|
+
},
|
|
17
19
|
"architect": {
|
|
18
20
|
"enabled": false
|
|
19
21
|
},
|
|
@@ -28,3 +30,4 @@
|
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
32
|
}
|
|
33
|
+
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// opencode-resolve.reference.jsonc
|
|
2
|
+
//
|
|
3
|
+
// This file documents EVERY option opencode-resolve accepts. It is NOT loaded
|
|
4
|
+
// by the plugin (the plugin only reads .json). Copy what you need into
|
|
5
|
+
// `~/.config/opencode/resolve.json`, `.opencode/resolve.json`, or
|
|
6
|
+
// `opencode-resolve.json` (without the comments).
|
|
7
|
+
//
|
|
8
|
+
// Config file lookup order (first one found wins):
|
|
9
|
+
// 1. <project>/.opencode/resolve.json
|
|
10
|
+
// 2. <project>/opencode-resolve.json
|
|
11
|
+
// 3. ~/.config/opencode/resolve.json
|
|
12
|
+
// 4. ~/.config/opencode/opencode-resolve.json
|
|
13
|
+
//
|
|
14
|
+
// Inline plugin options inside opencode.json override the file config.
|
|
15
|
+
//
|
|
16
|
+
// Validation is strict: unknown keys, misspellings, invalid modes, invalid
|
|
17
|
+
// permission values, and wrong types throw an error at load time.
|
|
18
|
+
|
|
19
|
+
{
|
|
20
|
+
// -----------------------------------------------------------------------
|
|
21
|
+
// enabled (string[])
|
|
22
|
+
// Which resolve agents to inject. Default: ["coder", "reviewer", "resolver"].
|
|
23
|
+
// Native OpenCode agents (plan, build) are always preserved and never
|
|
24
|
+
// touched by this list. Per-agent `agents.<name>.enabled` overrides this.
|
|
25
|
+
// -----------------------------------------------------------------------
|
|
26
|
+
"enabled": ["coder", "reviewer", "resolver"],
|
|
27
|
+
|
|
28
|
+
// -----------------------------------------------------------------------
|
|
29
|
+
// preserveNative (boolean)
|
|
30
|
+
// Always true in practice — plan/build are never rewritten. Accepted for
|
|
31
|
+
// readability. Default: true.
|
|
32
|
+
// -----------------------------------------------------------------------
|
|
33
|
+
"preserveNative": true,
|
|
34
|
+
|
|
35
|
+
// -----------------------------------------------------------------------
|
|
36
|
+
// context7 (boolean)
|
|
37
|
+
// When true, registers the Context7 MCP server unless `mcp.context7` is
|
|
38
|
+
// already configured. Default: true.
|
|
39
|
+
// -----------------------------------------------------------------------
|
|
40
|
+
"context7": true,
|
|
41
|
+
|
|
42
|
+
// -----------------------------------------------------------------------
|
|
43
|
+
// commands (boolean)
|
|
44
|
+
// When true, adds three optional commands: `resolve`, `resolve-code`,
|
|
45
|
+
// `resolve-review`. Default: false.
|
|
46
|
+
// -----------------------------------------------------------------------
|
|
47
|
+
"commands": false,
|
|
48
|
+
|
|
49
|
+
// -----------------------------------------------------------------------
|
|
50
|
+
// autoApprove (boolean)
|
|
51
|
+
// When true, every default `"ask"` permission on enabled agents is flipped
|
|
52
|
+
// to `"allow"`. Never touches `"deny"`. Never overrides a permission key
|
|
53
|
+
// you set explicitly under `agents.<name>.permission`. Default: true.
|
|
54
|
+
//
|
|
55
|
+
// Set to false for the conservative ask-every-time behavior.
|
|
56
|
+
// -----------------------------------------------------------------------
|
|
57
|
+
"autoApprove": true,
|
|
58
|
+
|
|
59
|
+
// -----------------------------------------------------------------------
|
|
60
|
+
// maxParallelSubagents (positive integer)
|
|
61
|
+
// Maximum number of subagents of the SAME ROLE the resolver may dispatch
|
|
62
|
+
// concurrently. Default: 2 — at most two coders in parallel and at most
|
|
63
|
+
// two reviewers in parallel. Subagents of different roles may always run
|
|
64
|
+
// concurrently. This is a soft prompt-level limit, not a runtime cap —
|
|
65
|
+
// pair with `maxSteps` if you need a stricter ceiling.
|
|
66
|
+
// 1 = strict per-role serial (one of each role at a time)
|
|
67
|
+
// 2 = default
|
|
68
|
+
// N = up to N of each role
|
|
69
|
+
// -----------------------------------------------------------------------
|
|
70
|
+
"maxParallelSubagents": 2,
|
|
71
|
+
|
|
72
|
+
// -----------------------------------------------------------------------
|
|
73
|
+
// models (object)
|
|
74
|
+
// Alias map. Keys are either an agent name (coder, reviewer, resolver,
|
|
75
|
+
// architect, gpt-coder, debugger, researcher) OR one of the well-known
|
|
76
|
+
// aliases (`glm`, `gpt`). Values are model identifiers OR another alias.
|
|
77
|
+
//
|
|
78
|
+
// Resolution order for a given agent:
|
|
79
|
+
// 1. agents.<name>.model
|
|
80
|
+
// 2. models.<name> (resolved through the alias map)
|
|
81
|
+
// 3. top-level OpenCode `model`
|
|
82
|
+
// 4. OpenCode's own fallback
|
|
83
|
+
// -----------------------------------------------------------------------
|
|
84
|
+
"models": {
|
|
85
|
+
"glm": "zai-coding-plan/glm-5.1",
|
|
86
|
+
"gpt": "openai/gpt-5.5",
|
|
87
|
+
"coder": "glm",
|
|
88
|
+
"resolver": "gpt",
|
|
89
|
+
"reviewer": "gpt"
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// -----------------------------------------------------------------------
|
|
93
|
+
// agents (object)
|
|
94
|
+
// Per-agent overrides. Each key MUST be a valid agent name. Each entry
|
|
95
|
+
// accepts: enabled, model, mode, description, prompt, color, maxSteps,
|
|
96
|
+
// tools, permission. Unknown keys throw.
|
|
97
|
+
//
|
|
98
|
+
// mode: "subagent" | "primary" | "all"
|
|
99
|
+
// permission.edit | bash | webfetch | doom_loop | external_directory:
|
|
100
|
+
// "ask" | "allow" | "deny"
|
|
101
|
+
// permission.bash may also be a per-command map:
|
|
102
|
+
// { "npm test": "allow", "rm -rf": "deny" }
|
|
103
|
+
// -----------------------------------------------------------------------
|
|
104
|
+
"agents": {
|
|
105
|
+
"coder": {
|
|
106
|
+
"enabled": true,
|
|
107
|
+
"model": "glm",
|
|
108
|
+
"mode": "subagent",
|
|
109
|
+
"maxSteps": 20,
|
|
110
|
+
"permission": {
|
|
111
|
+
"edit": "allow",
|
|
112
|
+
"bash": "allow",
|
|
113
|
+
"webfetch": "allow"
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
"reviewer": {
|
|
118
|
+
"enabled": true,
|
|
119
|
+
"model": "gpt",
|
|
120
|
+
"mode": "subagent",
|
|
121
|
+
"maxSteps": 8,
|
|
122
|
+
// Reviewer is read-only by design. edit and bash are denied even when
|
|
123
|
+
// autoApprove is true.
|
|
124
|
+
"permission": {
|
|
125
|
+
"edit": "deny",
|
|
126
|
+
"bash": "deny",
|
|
127
|
+
"webfetch": "allow"
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
"resolver": {
|
|
132
|
+
"enabled": true,
|
|
133
|
+
"model": "gpt",
|
|
134
|
+
"mode": "all",
|
|
135
|
+
"maxSteps": 30,
|
|
136
|
+
"permission": {
|
|
137
|
+
"edit": "allow",
|
|
138
|
+
"bash": "allow",
|
|
139
|
+
"webfetch": "allow"
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// Optional agents — disabled by default. Flip enabled:true to use them.
|
|
144
|
+
"architect": {
|
|
145
|
+
"enabled": false,
|
|
146
|
+
"model": "gpt",
|
|
147
|
+
"mode": "subagent",
|
|
148
|
+
"maxSteps": 10
|
|
149
|
+
},
|
|
150
|
+
"gpt-coder": {
|
|
151
|
+
"enabled": false,
|
|
152
|
+
"model": "gpt",
|
|
153
|
+
"mode": "subagent",
|
|
154
|
+
"maxSteps": 20
|
|
155
|
+
},
|
|
156
|
+
"debugger": {
|
|
157
|
+
"enabled": false,
|
|
158
|
+
"model": "glm",
|
|
159
|
+
"mode": "subagent",
|
|
160
|
+
"maxSteps": 14
|
|
161
|
+
},
|
|
162
|
+
"researcher": {
|
|
163
|
+
"enabled": false,
|
|
164
|
+
"model": "glm",
|
|
165
|
+
"mode": "subagent",
|
|
166
|
+
"maxSteps": 8
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-resolve",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "OpenCode plugin that adds a small coder/reviewer agent set while preserving native plan/build behavior.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/jshsakura/opencode-resolve#readme",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"dist",
|
|
24
24
|
"scripts",
|
|
25
25
|
"opencode-resolve.example.json",
|
|
26
|
+
"opencode-resolve.reference.jsonc",
|
|
26
27
|
"README.md",
|
|
27
28
|
"LICENSE"
|
|
28
29
|
],
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -11,6 +11,11 @@ const opencodeConfigPath = join(configDir, "opencode.json")
|
|
|
11
11
|
const resolveConfigPath = join(configDir, "resolve.json")
|
|
12
12
|
const exampleConfigPath = join(root, "opencode-resolve.example.json")
|
|
13
13
|
|
|
14
|
+
const ADDITIVE_DEFAULTS = {
|
|
15
|
+
autoApprove: true,
|
|
16
|
+
maxParallelSubagents: 2,
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
if (process.env.OPENCODE_RESOLVE_SKIP_POSTINSTALL === "1") {
|
|
15
20
|
process.exit(0)
|
|
16
21
|
}
|
|
@@ -39,6 +44,54 @@ async function registerPlugin() {
|
|
|
39
44
|
await assertReadable(exampleConfigPath)
|
|
40
45
|
await copyFile(exampleConfigPath, resolveConfigPath)
|
|
41
46
|
console.log(`[${packageName}] created ${resolveConfigPath}`)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await migrateResolveConfig()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function migrateResolveConfig() {
|
|
54
|
+
let raw
|
|
55
|
+
try {
|
|
56
|
+
raw = await readFile(resolveConfigPath, "utf8")
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.warn(`[${packageName}] could not read ${resolveConfigPath} for migration: ${formatError(error)}`)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let parsed
|
|
63
|
+
try {
|
|
64
|
+
parsed = JSON.parse(raw)
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.warn(`[${packageName}] ${resolveConfigPath} is not valid JSON; skipping migration: ${formatError(error)}`)
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!isObject(parsed)) {
|
|
71
|
+
console.warn(`[${packageName}] ${resolveConfigPath} must contain a JSON object; skipping migration`)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const updated = { ...parsed }
|
|
76
|
+
const added = []
|
|
77
|
+
for (const [key, value] of Object.entries(ADDITIVE_DEFAULTS)) {
|
|
78
|
+
if (updated[key] === undefined) {
|
|
79
|
+
updated[key] = value
|
|
80
|
+
added.push(`${key}=${JSON.stringify(value)}`)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (added.length > 0) {
|
|
85
|
+
await writeFile(resolveConfigPath, `${JSON.stringify(updated, null, 2)}\n`)
|
|
86
|
+
console.log(`[${packageName}] migrated ${resolveConfigPath}: added ${added.join(", ")}`)
|
|
87
|
+
} else {
|
|
88
|
+
console.log(`[${packageName}] ${resolveConfigPath} already up to date`)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (Array.isArray(updated.enabled) && !updated.enabled.includes("resolver")) {
|
|
92
|
+
console.log(
|
|
93
|
+
`[${packageName}] tip: add "resolver" to "enabled" in ${resolveConfigPath} to use the new orchestrator agent.`,
|
|
94
|
+
)
|
|
42
95
|
}
|
|
43
96
|
}
|
|
44
97
|
|