opencode-resolve 0.1.4 → 0.1.5
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.ko.md +84 -40
- package/README.md +84 -40
- package/dist/index.js +133 -5
- package/opencode-resolve.example.json +6 -1
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -222,6 +222,29 @@ opencode plugin opencode-resolve --global --force
|
|
|
222
222
|
|
|
223
223
|
> 이미 다른 플러그인이 있다면, 기존 배열에 `"opencode-resolve"`를 **추가**하세요. 기존 설정을 **덮어쓰지 마세요**.
|
|
224
224
|
|
|
225
|
+
#### 2b. 권장 companion 플러그인 제안 (2개)
|
|
226
|
+
|
|
227
|
+
모델 선택으로 넘어가기 전에, **독립적인** 두 companion 플러그인을 각각 한 번씩 물어봅니다 — 둘 다 권장 기본값. 별개의 OpenCode 플러그인이라 필수는 아니지만 opencode-resolve와 궁합이 좋습니다.
|
|
228
|
+
|
|
229
|
+
**Companion 1 — `@tarquinen/opencode-dcp` (Dynamic Context Pruning)**
|
|
230
|
+
|
|
231
|
+
> _opencode-resolve는 **`@tarquinen/opencode-dcp`** 와 궁합이 좋아요 — obsolete tool output을 가지치기해서 긴 resolver 루프의 토큰 비용을 줄입니다. 설치할까요? (권장)_
|
|
232
|
+
|
|
233
|
+
**Companion 2 — `@slkiser/opencode-quota` (사용량/quota 추적)**
|
|
234
|
+
|
|
235
|
+
> _그리고 **`@slkiser/opencode-quota`** — 컨텍스트 윈도우를 오염시키지 않고 실시간 토큰/quota 사용량을 보여줍니다. GLM coding-plan, OpenAI Plus/Pro, Qwen 등 지원. opencode-resolve가 실제로 얼마나 토큰 절약하는지 눈으로 확인하는 데 유용. 설치할까요? (권장)_
|
|
236
|
+
|
|
237
|
+
사용자가 **예** (각 질문마다):
|
|
238
|
+
|
|
239
|
+
1. `opencode plugin <package>@latest --global --force` 실행 (캐시 priming).
|
|
240
|
+
2. `~/.config/opencode/opencode.json`의 `plugin` 배열에 `"<package>@latest"` 추가 — 이미 있으면 스킵.
|
|
241
|
+
|
|
242
|
+
`<package>`는 `@tarquinen/opencode-dcp` 또는 `@slkiser/opencode-quota`.
|
|
243
|
+
|
|
244
|
+
각 **아니오**: 그 항목 스킵 — `opencode.json`을 그것 때문에 수정하지 마세요. 어느 쪽이든 3단계로 진행.
|
|
245
|
+
|
|
246
|
+
> `context7` MCP는 `resolve.json`에서 `context7: true`(기본값)이면 opencode-resolve가 런타임에 자동 등록합니다. 별도 질문 불필요.
|
|
247
|
+
|
|
225
248
|
### 3단계 — 모델을 인터랙티브하게 고른 뒤 `~/.config/opencode/resolve.json` 작성
|
|
226
249
|
|
|
227
250
|
여기서 LLM이 **짧은 Q&A를 주도**합니다. 목표: 사용자가 자신의 설정에서 직접 고른 모델 ID로 `resolve.json`을 마무리하는 것.
|
|
@@ -251,12 +274,17 @@ provider가 **하나도 설정되지 않은 경우** 진행을 멈추고, `openc
|
|
|
251
274
|
|
|
252
275
|
후보 provider가 정확히 하나면 질문을 건너뛰고 사용합니다.
|
|
253
276
|
|
|
254
|
-
#### 3c. 사용자에게 질문: 단일 /
|
|
277
|
+
#### 3c. 사용자에게 질문: 단일 / 2단계 / 3단계?
|
|
278
|
+
|
|
279
|
+
기본 권장은 **C (3단계, 브론즈/실버/골드)**. opencode-resolve는 질적으로 다른 세 가지 작업(read-only 스카웃 → write/patch → reason/judge)을 가지며, 이를 3단계로 매핑하는 게 2단계보다 역할별 비용을 더 정확히 반영합니다. 사용자가 모델 두 개만 있으면 B로 fallback, 하나뿐이면 A.
|
|
255
280
|
|
|
256
|
-
> _
|
|
281
|
+
> _권장: **3단계** — `explorer`에 가벼운 스카웃, `coder`에 중간 구현 모델, `resolver`/`reviewer`/`deep-reviewer`/`planner`에 강한 reasoner. 선택:_
|
|
257
282
|
>
|
|
258
|
-
>
|
|
259
|
-
> B.
|
|
283
|
+
> **C. 3단계 — 브론즈(스카웃) + 실버(코더) + 골드(추론) — 권장**
|
|
284
|
+
> B. 2단계 — fast + strong (모델 두 개만 있을 때)
|
|
285
|
+
> A. 모든 역할에 단일 모델 (모델 하나뿐이거나 최대한 단순한 게 필요한 경우만)
|
|
286
|
+
|
|
287
|
+
사용자가 그냥 엔터를 누르거나 "권장"이라고 답하면 C로. 모델 부족 시에만 fallback. **same-provider 분할도 완전 유효** — 예: `openai/gpt-4o-mini` / `openai/gpt-5.3-codex` / `openai/o4-mini`, 또는 `zai/glm-4.7-flash` / `zai-coding-plan/glm-5.1` / `zai/glm-5`.
|
|
260
288
|
|
|
261
289
|
#### 3d. 사용자에게 질문: 어떤 모델?
|
|
262
290
|
|
|
@@ -273,23 +301,39 @@ provider가 **하나도 설정되지 않은 경우** 진행을 멈추고, `openc
|
|
|
273
301
|
|
|
274
302
|
> _`coder`와 `explorer`를 `zai-coding-plan/glm-5.1`로, `resolver`/`reviewer`/`deep-reviewer`를 `openai/gpt-5.5`로 핀닝합니다. 진행할까요?_
|
|
275
303
|
|
|
304
|
+
#### 3d-bis. `coder` 모델 기준 `maxParallelSubagents` 결정
|
|
305
|
+
|
|
306
|
+
opencode-resolve의 정체성은 **토큰 절약** — resolver는 빠르게 계획하고 유형별로 subagent를 디스패치하며, 병렬 캡은 역할당입니다. 캡은 **coder 모델 하나만 보고** 정합니다 (coder가 fan-out 주체이므로 rate budget이 거기서 결정):
|
|
307
|
+
|
|
308
|
+
| `coder` 모델 ID에 포함된 | `maxParallelSubagents` |
|
|
309
|
+
|---|---|
|
|
310
|
+
| `glm` (어떤 GLM이든 — 보통 GLM coding-plan) | **2** |
|
|
311
|
+
| 그 외 (GPT, Claude, Gemini, local, …) | **4** |
|
|
312
|
+
|
|
313
|
+
GLM의 제약은 GLM coding-plan의 계정당 rate limit 때문 — 강력한 GLM이라도 burst 디스패치 시 throttle 걸림. 그 외 family는 4를 잘 받아냄. strong/reasoner 모델은 보지 마세요 — 캡은 doer를 따름.
|
|
314
|
+
|
|
315
|
+
단일(A) / 2단계(B) 케이스도 결국 coder가 쓰는 모델 하나만 보고 적용.
|
|
316
|
+
|
|
276
317
|
#### 3e. `~/.config/opencode/resolve.json` 작성
|
|
277
318
|
|
|
278
|
-
|
|
319
|
+
**3단계 (C, 권장)** — `bronze`/`silver`/`gold` alias 사용:
|
|
279
320
|
|
|
280
321
|
```json
|
|
281
322
|
{
|
|
282
|
-
"enabled": ["coder", "resolver", "explorer", "reviewer", "deep-reviewer"],
|
|
323
|
+
"enabled": ["coder", "resolver", "explorer", "reviewer", "deep-reviewer", "planner"],
|
|
283
324
|
"preserveNative": true,
|
|
284
325
|
"context7": true,
|
|
285
326
|
"commands": false,
|
|
286
327
|
"models": {
|
|
287
|
-
"
|
|
288
|
-
"
|
|
289
|
-
"
|
|
290
|
-
"
|
|
291
|
-
"
|
|
292
|
-
"
|
|
328
|
+
"bronze": "<provider>/<스카웃-모델>",
|
|
329
|
+
"silver": "<provider>/<코더-모델>",
|
|
330
|
+
"gold": "<provider>/<추론-모델>",
|
|
331
|
+
"explorer": "bronze",
|
|
332
|
+
"coder": "silver",
|
|
333
|
+
"resolver": "gold",
|
|
334
|
+
"reviewer": "gold",
|
|
335
|
+
"deep-reviewer": "gold",
|
|
336
|
+
"planner": "gold"
|
|
293
337
|
},
|
|
294
338
|
"agents": {
|
|
295
339
|
"coder": { "enabled": true, "mode": "all" },
|
|
@@ -297,49 +341,49 @@ provider가 **하나도 설정되지 않은 경우** 진행을 멈추고, `openc
|
|
|
297
341
|
"explorer": { "enabled": true, "mode": "subagent" },
|
|
298
342
|
"reviewer": { "enabled": true, "mode": "subagent" },
|
|
299
343
|
"deep-reviewer": { "enabled": true, "mode": "subagent" },
|
|
344
|
+
"planner": { "enabled": true, "mode": "subagent" },
|
|
300
345
|
"architect": { "enabled": false },
|
|
301
346
|
"gpt-coder": { "enabled": false },
|
|
302
347
|
"debugger": { "enabled": false },
|
|
303
348
|
"researcher": { "enabled": false }
|
|
304
349
|
},
|
|
305
350
|
"autoApprove": true,
|
|
351
|
+
"autoUpdate": true,
|
|
306
352
|
"maxParallelSubagents": 2
|
|
307
353
|
}
|
|
308
354
|
```
|
|
309
355
|
|
|
310
|
-
|
|
356
|
+
**2단계 (B)** — 모델이 두 개뿐이면 bronze와 silver 통합:
|
|
311
357
|
|
|
312
358
|
```json
|
|
313
|
-
{
|
|
314
|
-
"
|
|
315
|
-
"
|
|
316
|
-
"
|
|
317
|
-
"
|
|
318
|
-
"
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
"explorer": "fast",
|
|
323
|
-
"resolver": "strong",
|
|
324
|
-
"reviewer": "strong",
|
|
325
|
-
"deep-reviewer": "strong"
|
|
326
|
-
},
|
|
327
|
-
"agents": {
|
|
328
|
-
"coder": { "enabled": true, "mode": "all" },
|
|
329
|
-
"resolver": { "enabled": true },
|
|
330
|
-
"explorer": { "enabled": true, "mode": "subagent" },
|
|
331
|
-
"reviewer": { "enabled": true, "mode": "subagent" },
|
|
332
|
-
"deep-reviewer": { "enabled": true, "mode": "subagent" },
|
|
333
|
-
"architect": { "enabled": false },
|
|
334
|
-
"gpt-coder": { "enabled": false },
|
|
335
|
-
"debugger": { "enabled": false },
|
|
336
|
-
"researcher": { "enabled": false }
|
|
337
|
-
},
|
|
338
|
-
"autoApprove": true,
|
|
339
|
-
"maxParallelSubagents": 2
|
|
359
|
+
"models": {
|
|
360
|
+
"silver": "<provider>/<코더-모델>",
|
|
361
|
+
"gold": "<provider>/<추론-모델>",
|
|
362
|
+
"explorer": "silver",
|
|
363
|
+
"coder": "silver",
|
|
364
|
+
"resolver": "gold",
|
|
365
|
+
"reviewer": "gold",
|
|
366
|
+
"deep-reviewer": "gold",
|
|
367
|
+
"planner": "gold"
|
|
340
368
|
}
|
|
341
369
|
```
|
|
342
370
|
|
|
371
|
+
**단일 (A)** — 모든 역할이 단일 모델:
|
|
372
|
+
|
|
373
|
+
```json
|
|
374
|
+
"models": {
|
|
375
|
+
"gold": "<provider>/<단일-모델>",
|
|
376
|
+
"explorer": "gold",
|
|
377
|
+
"coder": "gold",
|
|
378
|
+
"resolver": "gold",
|
|
379
|
+
"reviewer": "gold",
|
|
380
|
+
"deep-reviewer": "gold",
|
|
381
|
+
"planner": "gold"
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
`<provider>/<모델>` 플레이스홀더는 모두 사용자가 고른 **정확한** ID로 교체. `maxParallelSubagents`는 **coder 모델 ID에 `glm` 포함되면 2, 그 외 4**.
|
|
386
|
+
|
|
343
387
|
`<provider>/<모델>` 플레이스홀더는 모두 사용자가 3b/3d에서 고른 **정확한** ID 문자열로 교체 — 임의 생성 금지, 자동완성 금지, 버전 드리프트 금지. 고른 모델을 `provider/model` 문자열로 매핑할 수 없으면 추측하지 말고 사용자에게 다시 물어보세요.
|
|
344
388
|
|
|
345
389
|
> 활성 에이전트마다 명시적인 `enabled: true`를 두어 파일이 자체 문서화됩니다. `enabled` 배열이 여전히 권위 있으며, 에이전트별 플래그는 사람이 파일을 읽을 때의 모호함만 제거합니다.
|
package/README.md
CHANGED
|
@@ -222,6 +222,29 @@ Add the string `"opencode-resolve"` to the `plugin` array. **Do not use the inli
|
|
|
222
222
|
|
|
223
223
|
> If the user already has other plugins, append `"opencode-resolve"` to the existing array. **Never overwrite** their existing config.
|
|
224
224
|
|
|
225
|
+
#### 2b. Offer the recommended companion plugins
|
|
226
|
+
|
|
227
|
+
Before moving to model selection, ask the user about two **independent** companion plugins — one question each, recommended-by-default. Both are separate OpenCode plugins; opencode-resolve does not require them but pairs well with both.
|
|
228
|
+
|
|
229
|
+
**Companion 1 — `@tarquinen/opencode-dcp` (Dynamic Context Pruning)**
|
|
230
|
+
|
|
231
|
+
> _opencode-resolve pairs well with **`@tarquinen/opencode-dcp`** — it prunes obsolete tool outputs so long resolver loops cost fewer tokens. Want to install it? (recommended)_
|
|
232
|
+
|
|
233
|
+
**Companion 2 — `@slkiser/opencode-quota` (quota & usage tracking)**
|
|
234
|
+
|
|
235
|
+
> _And **`@slkiser/opencode-quota`** — shows your live token/quota usage without polluting the context window. Supports GLM coding-plan, OpenAI Plus/Pro, Qwen, and more. Useful for actually seeing the token savings opencode-resolve gives you. Install it? (recommended)_
|
|
236
|
+
|
|
237
|
+
For each **yes** answer:
|
|
238
|
+
|
|
239
|
+
1. Run `opencode plugin <package>@latest --global --force` (primes the cache).
|
|
240
|
+
2. Append `"<package>@latest"` to the `plugin` array in `~/.config/opencode/opencode.json`, only if it is not already present.
|
|
241
|
+
|
|
242
|
+
Where `<package>` is `@tarquinen/opencode-dcp` or `@slkiser/opencode-quota`.
|
|
243
|
+
|
|
244
|
+
For each **no**, skip — do not modify `opencode.json` for that entry. Proceed to Step 3 either way.
|
|
245
|
+
|
|
246
|
+
> `context7` MCP is registered automatically by opencode-resolve at runtime when `context7: true` (the default in `resolve.json`). No separate question is needed for that.
|
|
247
|
+
|
|
225
248
|
### Step 3 — Pick models interactively, then write `~/.config/opencode/resolve.json`
|
|
226
249
|
|
|
227
250
|
The LLM **drives a short Q&A** here. Goal: end with `resolve.json` containing model IDs the user explicitly chose from their own configuration.
|
|
@@ -251,12 +274,17 @@ Print the candidate list and ask. Example wording the LLM should use:
|
|
|
251
274
|
|
|
252
275
|
If exactly one provider candidate exists, skip the question and use it.
|
|
253
276
|
|
|
254
|
-
#### 3c. Ask the user: single-tier or split?
|
|
277
|
+
#### 3c. Ask the user: single, two-tier, or three-tier split?
|
|
278
|
+
|
|
279
|
+
The default recommendation is **C (three-tier bronze/silver/gold)** — opencode-resolve has three qualitatively distinct workloads (read-only scout → write/patch → reason/judge) and three matching tiers cost about the same as two-tier but read role costs more accurately. Fall back to B if the user only has two models, A only when they have one.
|
|
255
280
|
|
|
256
|
-
>
|
|
281
|
+
> _Recommended: **three-tier** — a cheap scout model for `explorer`, a mid coder for `coder`, and a strong reasoner for `resolver`/`reviewer`/`deep-reviewer`/`planner`. Choose:_
|
|
257
282
|
>
|
|
258
|
-
>
|
|
259
|
-
> B.
|
|
283
|
+
> **C. Three-tier — bronze (scout) + silver (coder) + gold (reasoner) — recommended**
|
|
284
|
+
> B. Two-tier — fast + strong (when you only have two models)
|
|
285
|
+
> A. Single model for all roles (only if you have one model or want maximum simplicity)
|
|
286
|
+
|
|
287
|
+
Default to C if the user just hits enter or says "recommended". Only fall back if they don't have enough distinct models. Same-provider split is fully valid (e.g. `openai/gpt-4o-mini` / `openai/gpt-5.3-codex` / `openai/o4-mini`, or `zai/glm-4.7-flash` / `zai-coding-plan/glm-5.1` / `zai/glm-5`).
|
|
260
288
|
|
|
261
289
|
#### 3d. Ask the user: which model(s)?
|
|
262
290
|
|
|
@@ -273,23 +301,39 @@ Confirm each pick back to the user before writing the file. Example:
|
|
|
273
301
|
|
|
274
302
|
> _I'll pin `coder` and `explorer` to `zai-coding-plan/glm-5.1`, and `resolver`/`reviewer`/`deep-reviewer` to `openai/gpt-5.5`. Proceed?_
|
|
275
303
|
|
|
304
|
+
#### 3d-bis. Pick `maxParallelSubagents` from the `coder` model
|
|
305
|
+
|
|
306
|
+
opencode-resolve's identity is **token efficiency** — the resolver plans fast and dispatches subagents by type, and the parallel cap is per-role. The cap is determined by the `coder` model only — coder is the fan-out subject, so its rate budget is what matters:
|
|
307
|
+
|
|
308
|
+
| `coder` model ID contains | `maxParallelSubagents` |
|
|
309
|
+
|---|---|
|
|
310
|
+
| `glm` (any GLM model — most often the GLM coding-plan) | **2** |
|
|
311
|
+
| anything else (GPT, Claude, Gemini, local, …) | **4** |
|
|
312
|
+
|
|
313
|
+
The GLM constraint comes from the GLM coding-plan's per-account rate limits — even a powerful GLM gets throttled under burst dispatch. Every other family tested tolerates 4 well and the resolver gets meaningful speedup. Don't look at the strong/reasoner model — the cap follows the doer, not the judge.
|
|
314
|
+
|
|
315
|
+
For single-tier (A) and two-tier (B), apply the same rule to whichever model the `coder` agent ends up using.
|
|
316
|
+
|
|
276
317
|
#### 3e. Write `~/.config/opencode/resolve.json`
|
|
277
318
|
|
|
278
|
-
|
|
319
|
+
**Three-tier (C, recommended)** — use the `bronze`/`silver`/`gold` aliases:
|
|
279
320
|
|
|
280
321
|
```json
|
|
281
322
|
{
|
|
282
|
-
"enabled": ["coder", "resolver", "explorer", "reviewer", "deep-reviewer"],
|
|
323
|
+
"enabled": ["coder", "resolver", "explorer", "reviewer", "deep-reviewer", "planner"],
|
|
283
324
|
"preserveNative": true,
|
|
284
325
|
"context7": true,
|
|
285
326
|
"commands": false,
|
|
286
327
|
"models": {
|
|
287
|
-
"
|
|
288
|
-
"
|
|
289
|
-
"
|
|
290
|
-
"
|
|
291
|
-
"
|
|
292
|
-
"
|
|
328
|
+
"bronze": "<provider>/<scout-model>",
|
|
329
|
+
"silver": "<provider>/<coder-model>",
|
|
330
|
+
"gold": "<provider>/<reasoner-model>",
|
|
331
|
+
"explorer": "bronze",
|
|
332
|
+
"coder": "silver",
|
|
333
|
+
"resolver": "gold",
|
|
334
|
+
"reviewer": "gold",
|
|
335
|
+
"deep-reviewer": "gold",
|
|
336
|
+
"planner": "gold"
|
|
293
337
|
},
|
|
294
338
|
"agents": {
|
|
295
339
|
"coder": { "enabled": true, "mode": "all" },
|
|
@@ -297,49 +341,49 @@ If the user picked **A (single-tier)**, write:
|
|
|
297
341
|
"explorer": { "enabled": true, "mode": "subagent" },
|
|
298
342
|
"reviewer": { "enabled": true, "mode": "subagent" },
|
|
299
343
|
"deep-reviewer": { "enabled": true, "mode": "subagent" },
|
|
344
|
+
"planner": { "enabled": true, "mode": "subagent" },
|
|
300
345
|
"architect": { "enabled": false },
|
|
301
346
|
"gpt-coder": { "enabled": false },
|
|
302
347
|
"debugger": { "enabled": false },
|
|
303
348
|
"researcher": { "enabled": false }
|
|
304
349
|
},
|
|
305
350
|
"autoApprove": true,
|
|
351
|
+
"autoUpdate": true,
|
|
306
352
|
"maxParallelSubagents": 2
|
|
307
353
|
}
|
|
308
354
|
```
|
|
309
355
|
|
|
310
|
-
|
|
356
|
+
**Two-tier (B)** — collapse bronze and silver if the user has only two models:
|
|
311
357
|
|
|
312
358
|
```json
|
|
313
|
-
{
|
|
314
|
-
"
|
|
315
|
-
"
|
|
316
|
-
"
|
|
317
|
-
"
|
|
318
|
-
"
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
"explorer": "fast",
|
|
323
|
-
"resolver": "strong",
|
|
324
|
-
"reviewer": "strong",
|
|
325
|
-
"deep-reviewer": "strong"
|
|
326
|
-
},
|
|
327
|
-
"agents": {
|
|
328
|
-
"coder": { "enabled": true, "mode": "all" },
|
|
329
|
-
"resolver": { "enabled": true },
|
|
330
|
-
"explorer": { "enabled": true, "mode": "subagent" },
|
|
331
|
-
"reviewer": { "enabled": true, "mode": "subagent" },
|
|
332
|
-
"deep-reviewer": { "enabled": true, "mode": "subagent" },
|
|
333
|
-
"architect": { "enabled": false },
|
|
334
|
-
"gpt-coder": { "enabled": false },
|
|
335
|
-
"debugger": { "enabled": false },
|
|
336
|
-
"researcher": { "enabled": false }
|
|
337
|
-
},
|
|
338
|
-
"autoApprove": true,
|
|
339
|
-
"maxParallelSubagents": 2
|
|
359
|
+
"models": {
|
|
360
|
+
"silver": "<provider>/<coder-model>",
|
|
361
|
+
"gold": "<provider>/<reasoner-model>",
|
|
362
|
+
"explorer": "silver",
|
|
363
|
+
"coder": "silver",
|
|
364
|
+
"resolver": "gold",
|
|
365
|
+
"reviewer": "gold",
|
|
366
|
+
"deep-reviewer": "gold",
|
|
367
|
+
"planner": "gold"
|
|
340
368
|
}
|
|
341
369
|
```
|
|
342
370
|
|
|
371
|
+
**Single-tier (A)** — every role on one model:
|
|
372
|
+
|
|
373
|
+
```json
|
|
374
|
+
"models": {
|
|
375
|
+
"gold": "<provider>/<single-model>",
|
|
376
|
+
"explorer": "gold",
|
|
377
|
+
"coder": "gold",
|
|
378
|
+
"resolver": "gold",
|
|
379
|
+
"reviewer": "gold",
|
|
380
|
+
"deep-reviewer": "gold",
|
|
381
|
+
"planner": "gold"
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Replace every `<provider>/<model>` placeholder with the **exact** ID strings the user picked. Set `maxParallelSubagents` to **2** if the `coder` model ID contains `glm`, otherwise **4**.
|
|
386
|
+
|
|
343
387
|
Replace every `<provider>/<model>` placeholder with the **exact** ID strings the user picked in 3b/3d — no inventing, no autocompletion, no version drift. If you cannot map the picked model to a `provider/model` string, ask the user to clarify rather than guessing.
|
|
344
388
|
|
|
345
389
|
> Each enabled agent carries an explicit `enabled: true` so the file is self-documenting. The `enabled` array remains authoritative; the per-agent flag just removes ambiguity for humans reading the file.
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
3
|
import { access, readFile } from "node:fs/promises";
|
|
3
4
|
import { homedir } from "node:os";
|
|
4
5
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
6
7
|
const PLUGIN_VERSION = readPluginVersion();
|
|
8
|
+
const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
9
|
+
const UPDATE_CHECK_FILE = join(homedir(), ".cache", "opencode-resolve", "update-check.json");
|
|
10
|
+
const PLUGIN_CACHE_DIR = join(homedir(), ".cache", "opencode", "packages", "opencode-resolve@latest");
|
|
7
11
|
function readPluginVersion() {
|
|
8
12
|
try {
|
|
9
13
|
const pkgRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
@@ -16,10 +20,34 @@ function readPluginVersion() {
|
|
|
16
20
|
}
|
|
17
21
|
console.log(`[opencode-resolve] v${PLUGIN_VERSION} loaded`);
|
|
18
22
|
const DEFAULT_MODELS = {};
|
|
19
|
-
const DEFAULT_ENABLED = ["coder", "resolver", "explorer", "reviewer", "deep-reviewer"];
|
|
20
|
-
const VALID_AGENT_NAMES = [
|
|
23
|
+
const DEFAULT_ENABLED = ["coder", "resolver", "explorer", "reviewer", "deep-reviewer", "planner"];
|
|
24
|
+
const VALID_AGENT_NAMES = [
|
|
25
|
+
"coder",
|
|
26
|
+
"reviewer",
|
|
27
|
+
"resolver",
|
|
28
|
+
"architect",
|
|
29
|
+
"gpt-coder",
|
|
30
|
+
"debugger",
|
|
31
|
+
"researcher",
|
|
32
|
+
"explorer",
|
|
33
|
+
"deep-reviewer",
|
|
34
|
+
"planner",
|
|
35
|
+
];
|
|
21
36
|
const VALID_AGENT_NAME_SET = new Set(VALID_AGENT_NAMES);
|
|
22
|
-
const VALID_MODEL_ALIASES = [
|
|
37
|
+
const VALID_MODEL_ALIASES = [
|
|
38
|
+
...VALID_AGENT_NAMES,
|
|
39
|
+
"glm",
|
|
40
|
+
"gpt",
|
|
41
|
+
"quick",
|
|
42
|
+
"deep",
|
|
43
|
+
"fast",
|
|
44
|
+
"strong",
|
|
45
|
+
"mini",
|
|
46
|
+
"codex",
|
|
47
|
+
"bronze",
|
|
48
|
+
"silver",
|
|
49
|
+
"gold",
|
|
50
|
+
];
|
|
23
51
|
const VALID_MODEL_ALIAS_SET = new Set(VALID_MODEL_ALIASES);
|
|
24
52
|
const VALID_MODES = new Set(["subagent", "primary", "all"]);
|
|
25
53
|
const VALID_PERMISSION_VALUES = new Set(["ask", "allow", "deny"]);
|
|
@@ -32,6 +60,7 @@ const VALID_TOP_LEVEL_KEYS = new Set([
|
|
|
32
60
|
"commands",
|
|
33
61
|
"autoApprove",
|
|
34
62
|
"maxParallelSubagents",
|
|
63
|
+
"autoUpdate",
|
|
35
64
|
"config",
|
|
36
65
|
]);
|
|
37
66
|
const DEFAULT_MAX_PARALLEL_SUBAGENTS = 2;
|
|
@@ -56,7 +85,7 @@ function buildResolverPrompt(maxParallelSubagents) {
|
|
|
56
85
|
"Your job is to drive the user's task to a verified resolution using minimal context and the fewest LLM calls possible.",
|
|
57
86
|
"",
|
|
58
87
|
"Core path: You and Coder form the fixed-role verified resolve loop — this is the default path.",
|
|
59
|
-
"Internal specialist subagents (explorer, reviewer, deep-reviewer) are available by default as subagents, but they are NOT the default path.",
|
|
88
|
+
"Internal specialist subagents (explorer, reviewer, deep-reviewer, planner) are available by default as subagents, but they are NOT the default path.",
|
|
60
89
|
"Dispatch them only when justified — avoid context waste.",
|
|
61
90
|
"",
|
|
62
91
|
"Checkpointed execution: for large tasks, decompose work into small verified checkpoints. For each checkpoint, iterate up to 3 attempts on the same failing checkpoint. When a checkpoint passes verification, carry forward only: decisions, changed files, verification results, and blockers — then proceed to the next checkpoint. If blocked after max 3 attempts on one checkpoint, report the exact blocker with evidence. This preserves context and handles arbitrarily long tasks.",
|
|
@@ -75,6 +104,7 @@ function buildResolverPrompt(maxParallelSubagents) {
|
|
|
75
104
|
"- explorer: fast read-only codebase scout. Prefer local read/grep/glob for narrow scope; dispatch explorer only when scope is genuinely unknown and local tools are insufficient.",
|
|
76
105
|
"- reviewer: lightweight read-only audit. Dispatch only for post-change verification gaps on non-trivial changes.",
|
|
77
106
|
"- deep-reviewer: thorough read-only review. Dispatch ONLY for risky, security-sensitive, architectural, or high-impact changes.",
|
|
107
|
+
"- planner: advanced read-only planner. Dispatch ONLY when the user explicitly asks for a plan, decomposition, or implementation strategy. Do NOT call planner for routine sub-task planning you can absorb inline.",
|
|
78
108
|
"",
|
|
79
109
|
"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 context.",
|
|
80
110
|
].join("\n");
|
|
@@ -95,6 +125,8 @@ const DEFAULT_AGENT_CONFIG = {
|
|
|
95
125
|
"Implement the smallest patch that satisfies the requirement.",
|
|
96
126
|
"Run targeted verification when practical (single test file, type check, or lint — not full suites).",
|
|
97
127
|
"Return a concise summary: changed files list + command results. No unnecessary prose.",
|
|
128
|
+
"",
|
|
129
|
+
"Scope-discovery gate: dispatch the `explorer` subagent ONLY when the scope is genuinely unclear — i.e. you need to locate 3+ unknown files, OR a named pattern's location is genuinely unknown. For known files, single-file uncertainty, or routine doubt, read directly with the local read/grep/glob tools. Never dispatch explorer for casual exploration — token efficiency is the discipline.",
|
|
98
130
|
].join("\n"),
|
|
99
131
|
permission: {
|
|
100
132
|
edit: "ask",
|
|
@@ -236,15 +268,107 @@ const DEFAULT_AGENT_CONFIG = {
|
|
|
236
268
|
webfetch: "ask",
|
|
237
269
|
},
|
|
238
270
|
},
|
|
271
|
+
planner: {
|
|
272
|
+
mode: "subagent",
|
|
273
|
+
color: "#F4A300",
|
|
274
|
+
maxSteps: 10,
|
|
275
|
+
description: "Internal advanced planner dispatched by the resolver when the user explicitly asks for a plan, decomposition, or implementation strategy. Read-only. Returns a concrete plan; never edits code.",
|
|
276
|
+
prompt: [
|
|
277
|
+
"You are Planner, the advanced planning subagent for OpenCode Resolve.",
|
|
278
|
+
"You are dispatched by the resolver only when the user explicitly asks for a plan, decomposition, or implementation strategy — not for routine sub-task planning the resolver handles inline.",
|
|
279
|
+
"You MUST NOT modify the project: no file edits, no writes, no shell commands that change state.",
|
|
280
|
+
"Inspect the relevant code with read-only tools (read, grep, glob, list) before proposing.",
|
|
281
|
+
"Return: clear phasing, file-level boundaries per phase, verification checkpoints, risks, and explicit trade-offs. Be concrete — name files, name decisions, name the cost of each option.",
|
|
282
|
+
"Be token-efficient: produce the smallest plan that fully covers the user's intent. No filler, no boilerplate, no restating the request.",
|
|
283
|
+
].join("\n"),
|
|
284
|
+
permission: {
|
|
285
|
+
edit: "deny",
|
|
286
|
+
bash: "deny",
|
|
287
|
+
webfetch: "ask",
|
|
288
|
+
},
|
|
289
|
+
},
|
|
239
290
|
};
|
|
240
291
|
export const OpencodeResolve = async ({ directory }, options) => {
|
|
241
292
|
return {
|
|
242
293
|
config: async (config) => {
|
|
243
294
|
const resolveConfig = await loadResolveConfig(directory, config, options);
|
|
244
295
|
applyResolveConfig(config, resolveConfig);
|
|
296
|
+
if (resolveConfig.autoUpdate !== false && process.env.OPENCODE_RESOLVE_NO_AUTO_UPDATE !== "1") {
|
|
297
|
+
maybeAutoUpdate().catch(() => { });
|
|
298
|
+
}
|
|
245
299
|
},
|
|
246
300
|
};
|
|
247
301
|
};
|
|
302
|
+
async function maybeAutoUpdate() {
|
|
303
|
+
try {
|
|
304
|
+
const previous = readUpdateCheckCache();
|
|
305
|
+
if (previous && Date.now() - previous.checkedAt < UPDATE_CHECK_INTERVAL_MS) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
// ignore corrupt cache and re-check
|
|
311
|
+
}
|
|
312
|
+
let latest;
|
|
313
|
+
try {
|
|
314
|
+
const response = await fetch("https://registry.npmjs.org/opencode-resolve/latest", {
|
|
315
|
+
headers: { Accept: "application/json" },
|
|
316
|
+
signal: AbortSignal.timeout(5000),
|
|
317
|
+
});
|
|
318
|
+
if (!response.ok)
|
|
319
|
+
return;
|
|
320
|
+
const data = (await response.json());
|
|
321
|
+
if (typeof data?.version !== "string")
|
|
322
|
+
return;
|
|
323
|
+
latest = data.version;
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
try {
|
|
329
|
+
mkdirSync(dirname(UPDATE_CHECK_FILE), { recursive: true });
|
|
330
|
+
writeFileSync(UPDATE_CHECK_FILE, JSON.stringify({ checkedAt: Date.now(), latest }));
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
// best-effort; don't block on cache write failure
|
|
334
|
+
}
|
|
335
|
+
if (!isNewerVersion(latest, PLUGIN_VERSION))
|
|
336
|
+
return;
|
|
337
|
+
console.log(`[opencode-resolve] new version v${latest} available (current: v${PLUGIN_VERSION}) — refreshing cache in background. Restart OpenCode to activate (current session stays on v${PLUGIN_VERSION}).`);
|
|
338
|
+
try {
|
|
339
|
+
spawn("sh", ["-c", `rm -rf "${PLUGIN_CACHE_DIR}" && opencode plugin opencode-resolve --global --force`], { detached: true, stdio: "ignore" }).unref();
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
// If spawn fails, the user already saw the notice and can run the command manually.
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function readUpdateCheckCache() {
|
|
346
|
+
try {
|
|
347
|
+
const raw = readFileSync(UPDATE_CHECK_FILE, "utf8");
|
|
348
|
+
const parsed = JSON.parse(raw);
|
|
349
|
+
if (typeof parsed?.checkedAt === "number" &&
|
|
350
|
+
typeof parsed?.latest === "string") {
|
|
351
|
+
return { checkedAt: parsed.checkedAt, latest: parsed.latest };
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
// file missing or unparseable
|
|
356
|
+
}
|
|
357
|
+
return undefined;
|
|
358
|
+
}
|
|
359
|
+
function isNewerVersion(candidate, baseline) {
|
|
360
|
+
const a = candidate.split(".").map((n) => Number.parseInt(n, 10));
|
|
361
|
+
const b = baseline.split(".").map((n) => Number.parseInt(n, 10));
|
|
362
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
363
|
+
const av = Number.isFinite(a[i]) ? a[i] : 0;
|
|
364
|
+
const bv = Number.isFinite(b[i]) ? b[i] : 0;
|
|
365
|
+
if (av > bv)
|
|
366
|
+
return true;
|
|
367
|
+
if (av < bv)
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
248
372
|
export default OpencodeResolve;
|
|
249
373
|
async function loadResolveConfig(directory, opencodeConfig, options) {
|
|
250
374
|
const pluginOptions = normalizeResolveConfig(options ?? getPluginOptions(opencodeConfig), "plugin options");
|
|
@@ -328,6 +452,7 @@ function defaultResolveConfig() {
|
|
|
328
452
|
commands: false,
|
|
329
453
|
autoApprove: true,
|
|
330
454
|
maxParallelSubagents: DEFAULT_MAX_PARALLEL_SUBAGENTS,
|
|
455
|
+
autoUpdate: true,
|
|
331
456
|
};
|
|
332
457
|
}
|
|
333
458
|
function mergeResolveConfig(...configs) {
|
|
@@ -341,6 +466,7 @@ function mergeResolveConfig(...configs) {
|
|
|
341
466
|
result.commands = config.commands ?? result.commands;
|
|
342
467
|
result.autoApprove = config.autoApprove ?? result.autoApprove;
|
|
343
468
|
result.maxParallelSubagents = config.maxParallelSubagents ?? result.maxParallelSubagents;
|
|
469
|
+
result.autoUpdate = config.autoUpdate ?? result.autoUpdate;
|
|
344
470
|
result.models = { ...result.models, ...config.models };
|
|
345
471
|
result.agents = mergeAgents(result.agents, config.agents);
|
|
346
472
|
}
|
|
@@ -451,6 +577,8 @@ function normalizeResolveConfig(value, source) {
|
|
|
451
577
|
result.commands = expectBoolean(config.commands, `${source}.commands`);
|
|
452
578
|
if (config.autoApprove !== undefined)
|
|
453
579
|
result.autoApprove = expectBoolean(config.autoApprove, `${source}.autoApprove`);
|
|
580
|
+
if (config.autoUpdate !== undefined)
|
|
581
|
+
result.autoUpdate = expectBoolean(config.autoUpdate, `${source}.autoUpdate`);
|
|
454
582
|
if (config.maxParallelSubagents !== undefined) {
|
|
455
583
|
const limit = expectNumber(config.maxParallelSubagents, `${source}.maxParallelSubagents`);
|
|
456
584
|
if (!Number.isInteger(limit) || limit < 1) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"enabled": ["coder", "resolver", "explorer", "reviewer", "deep-reviewer"],
|
|
2
|
+
"enabled": ["coder", "resolver", "explorer", "reviewer", "deep-reviewer", "planner"],
|
|
3
3
|
"preserveNative": true,
|
|
4
4
|
"context7": true,
|
|
5
5
|
"commands": false,
|
|
6
6
|
"autoApprove": true,
|
|
7
|
+
"autoUpdate": true,
|
|
7
8
|
"maxParallelSubagents": 2,
|
|
8
9
|
"models": {},
|
|
9
10
|
"agents": {
|
|
@@ -26,6 +27,10 @@
|
|
|
26
27
|
"enabled": true,
|
|
27
28
|
"mode": "subagent"
|
|
28
29
|
},
|
|
30
|
+
"planner": {
|
|
31
|
+
"enabled": true,
|
|
32
|
+
"mode": "subagent"
|
|
33
|
+
},
|
|
29
34
|
"architect": {
|
|
30
35
|
"enabled": false
|
|
31
36
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-resolve",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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",
|