opencode-resolve 0.1.3 → 0.1.4
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 +877 -0
- package/README.md +453 -118
- package/dist/index.js +95 -26
- package/opencode-resolve.example.json +14 -5
- package/opencode-resolve.reference.jsonc +96 -31
- package/package.json +1 -1
- package/scripts/install-local.mjs +97 -5
- package/scripts/postinstall.mjs +110 -4
package/dist/index.js
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
1
2
|
import { access, readFile } from "node:fs/promises";
|
|
2
3
|
import { homedir } from "node:os";
|
|
3
|
-
import { basename, isAbsolute, join, resolve } from "node:path";
|
|
4
|
+
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
const PLUGIN_VERSION = readPluginVersion();
|
|
7
|
+
function readPluginVersion() {
|
|
8
|
+
try {
|
|
9
|
+
const pkgRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
10
|
+
const pkg = JSON.parse(readFileSync(join(pkgRoot, "package.json"), "utf8"));
|
|
11
|
+
return typeof pkg?.version === "string" ? pkg.version : "unknown";
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return "unknown";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
console.log(`[opencode-resolve] v${PLUGIN_VERSION} loaded`);
|
|
4
18
|
const DEFAULT_MODELS = {};
|
|
5
|
-
const DEFAULT_ENABLED = ["coder", "reviewer", "
|
|
6
|
-
const VALID_AGENT_NAMES = ["coder", "reviewer", "resolver", "architect", "gpt-coder", "debugger", "researcher"];
|
|
19
|
+
const DEFAULT_ENABLED = ["coder", "resolver", "explorer", "reviewer", "deep-reviewer"];
|
|
20
|
+
const VALID_AGENT_NAMES = ["coder", "reviewer", "resolver", "architect", "gpt-coder", "debugger", "researcher", "explorer", "deep-reviewer"];
|
|
7
21
|
const VALID_AGENT_NAME_SET = new Set(VALID_AGENT_NAMES);
|
|
8
|
-
const VALID_MODEL_ALIASES = [...VALID_AGENT_NAMES, "glm", "gpt"];
|
|
22
|
+
const VALID_MODEL_ALIASES = [...VALID_AGENT_NAMES, "glm", "gpt", "quick", "deep", "fast", "strong", "mini", "codex"];
|
|
9
23
|
const VALID_MODEL_ALIAS_SET = new Set(VALID_MODEL_ALIASES);
|
|
10
24
|
const VALID_MODES = new Set(["subagent", "primary", "all"]);
|
|
11
25
|
const VALID_PERMISSION_VALUES = new Set(["ask", "allow", "deny"]);
|
|
@@ -35,22 +49,34 @@ const VALID_AGENT_KEYS = new Set([
|
|
|
35
49
|
function buildResolverPrompt(maxParallelSubagents) {
|
|
36
50
|
const limit = Math.max(1, Math.trunc(maxParallelSubagents));
|
|
37
51
|
const parallelRule = limit === 1
|
|
38
|
-
? "CRITICAL: Dispatch at most ONE subagent of each role concurrently. Never run two coders in parallel
|
|
39
|
-
: `CRITICAL: Dispatch at most ${limit} subagents of the same role concurrently. Never exceed ${limit} coders in parallel
|
|
52
|
+
? "CRITICAL: Dispatch at most ONE subagent of each role concurrently. Never run two coders in parallel. Wait for an in-flight coder to finish before dispatching another."
|
|
53
|
+
: `CRITICAL: Dispatch at most ${limit} subagents of the same role concurrently. Never exceed ${limit} coders in parallel. Wait for in-flight subagents of a given role to finish before dispatching more of that role.`;
|
|
40
54
|
return [
|
|
41
|
-
"You are Resolver, the
|
|
42
|
-
"Your job is to drive the user's task to a verified resolution
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
55
|
+
"You are Resolver, the context-efficient orchestrator agent for OpenCode Resolve.",
|
|
56
|
+
"Your job is to drive the user's task to a verified resolution using minimal context and the fewest LLM calls possible.",
|
|
57
|
+
"",
|
|
58
|
+
"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.",
|
|
60
|
+
"Dispatch them only when justified — avoid context waste.",
|
|
61
|
+
"",
|
|
62
|
+
"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.",
|
|
63
|
+
"",
|
|
64
|
+
"Workflow (default fixed-role path):",
|
|
65
|
+
"1. CLASSIFY the work as quick (trivial fix), normal (standard feature), deep (complex refactor), or risky (security/architecture/high-impact).",
|
|
66
|
+
"2. INSPECT only relevant files — avoid broad exploration. Use local tools (read, grep, glob) to gather facts, not subagents.",
|
|
67
|
+
"3. For trivial/quick work: inspect relevant files directly and apply a small edit yourself. No subagent needed.",
|
|
68
|
+
"4. PLAN the smallest correct patch. Dispatch coder with exact file paths and focused behavior instructions.",
|
|
69
|
+
`5. ${parallelRule}`,
|
|
70
|
+
"6. VERIFY with the cheapest meaningful check first (targeted test, type check, or lint). Do not run full suites unless the change is wide.",
|
|
71
|
+
"7. If issues remain, RETRY from verification logs: dispatch coder again with a focused fix. Max 3 attempts for the same failing checkpoint; then move forward or report the blocker.",
|
|
72
|
+
"8. REPORT a concise evidence summary: what changed, verification results, and any remaining blockers.",
|
|
73
|
+
"",
|
|
74
|
+
"Internal specialist subagents (available by default, but NOT the default path — use only when justified):",
|
|
75
|
+
"- 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
|
+
"- reviewer: lightweight read-only audit. Dispatch only for post-change verification gaps on non-trivial changes.",
|
|
77
|
+
"- deep-reviewer: thorough read-only review. Dispatch ONLY for risky, security-sensitive, architectural, or high-impact changes.",
|
|
78
|
+
"",
|
|
79
|
+
"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.",
|
|
54
80
|
].join("\n");
|
|
55
81
|
}
|
|
56
82
|
const DEFAULT_AGENT_CONFIG = {
|
|
@@ -61,10 +87,14 @@ const DEFAULT_AGENT_CONFIG = {
|
|
|
61
87
|
description: "Use for focused implementation, file edits, test runs, and fixing issues until the task is resolved.",
|
|
62
88
|
prompt: [
|
|
63
89
|
"You are Coder, a focused implementation subagent for OpenCode Resolve.",
|
|
90
|
+
"You are one of two default agents. Together with Resolver you form a verified resolve loop.",
|
|
91
|
+
"",
|
|
92
|
+
"Context budget: read ONLY the files you need. Avoid broad exploration or discovering the entire codebase.",
|
|
64
93
|
"Preserve native OpenCode behavior and make the smallest correct change.",
|
|
65
|
-
"Before editing, inspect the relevant files and existing patterns.",
|
|
66
|
-
"Implement
|
|
67
|
-
"
|
|
94
|
+
"Before editing, inspect only the relevant files and existing patterns in those files.",
|
|
95
|
+
"Implement the smallest patch that satisfies the requirement.",
|
|
96
|
+
"Run targeted verification when practical (single test file, type check, or lint — not full suites).",
|
|
97
|
+
"Return a concise summary: changed files list + command results. No unnecessary prose.",
|
|
68
98
|
].join("\n"),
|
|
69
99
|
permission: {
|
|
70
100
|
edit: "ask",
|
|
@@ -76,9 +106,10 @@ const DEFAULT_AGENT_CONFIG = {
|
|
|
76
106
|
mode: "subagent",
|
|
77
107
|
color: "#8A7CFF",
|
|
78
108
|
maxSteps: 8,
|
|
79
|
-
description: "
|
|
109
|
+
description: "Internal read-only verification-gap auditor. Enabled as subagent by default but not part of the core resolver→coder path. Resolver dispatches only when it judges a verification gap exists on non-trivial changes.",
|
|
80
110
|
prompt: [
|
|
81
|
-
"You are Reviewer, a strictly read-only review subagent for OpenCode Resolve.",
|
|
111
|
+
"You are Reviewer, a strictly read-only internal review subagent for OpenCode Resolve.",
|
|
112
|
+
"You are NOT part of the core path (resolver→coder). You are injected as an internal subagent so the resolver can dispatch you when it judges a verification gap exists on non-trivial changes.",
|
|
82
113
|
"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
114
|
"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.",
|
|
84
115
|
"Prioritize concrete bugs, behavioral regressions, security risks, missing tests, and maintainability issues.",
|
|
@@ -95,7 +126,7 @@ const DEFAULT_AGENT_CONFIG = {
|
|
|
95
126
|
mode: "all",
|
|
96
127
|
color: "#FF7AC6",
|
|
97
128
|
maxSteps: 30,
|
|
98
|
-
description: "Primary orchestrator
|
|
129
|
+
description: "Primary orchestrator in the fixed-role verified loop (resolver→coder). Decomposes work into verified checkpoints, dispatches coder, verifies each, and carries forward progress. Internal subagents (explorer, reviewer, deep-reviewer) are available by default but dispatched only when justified.",
|
|
99
130
|
prompt: buildResolverPrompt(DEFAULT_MAX_PARALLEL_SUBAGENTS),
|
|
100
131
|
permission: {
|
|
101
132
|
edit: "ask",
|
|
@@ -167,6 +198,44 @@ const DEFAULT_AGENT_CONFIG = {
|
|
|
167
198
|
webfetch: "ask",
|
|
168
199
|
},
|
|
169
200
|
},
|
|
201
|
+
explorer: {
|
|
202
|
+
mode: "subagent",
|
|
203
|
+
color: "#33CCFF",
|
|
204
|
+
maxSteps: 6,
|
|
205
|
+
description: "Internal pre-change fast scout for codebase/file/pattern/doc discovery. Enabled as subagent by default but not part of the core path. Read-only; quick model.",
|
|
206
|
+
prompt: [
|
|
207
|
+
"You are Explorer, a fast codebase scout subagent for OpenCode Resolve.",
|
|
208
|
+
"Your job is to quickly discover files, patterns, APIs, and relevant code locations before implementation begins.",
|
|
209
|
+
"You MUST NOT modify the project by any means: no file edits, no writes, no shell commands that change state.",
|
|
210
|
+
"Use read-only tools (read, grep, glob, list) and documentation tools (web fetch, Context7) to find what matters.",
|
|
211
|
+
"Return concise findings with file paths, relevant code snippets, APIs, and constraints.",
|
|
212
|
+
"Be fast and targeted — the resolver needs your discoveries to plan efficiently.",
|
|
213
|
+
].join("\n"),
|
|
214
|
+
permission: {
|
|
215
|
+
edit: "deny",
|
|
216
|
+
bash: "ask",
|
|
217
|
+
webfetch: "ask",
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
"deep-reviewer": {
|
|
221
|
+
mode: "subagent",
|
|
222
|
+
color: "#6A0DAD",
|
|
223
|
+
maxSteps: 12,
|
|
224
|
+
description: "Internal post-change strong read-only review for risky/security/architecture/high-impact changes. Enabled as subagent by default but not part of the core path. Read-only; deep model.",
|
|
225
|
+
prompt: [
|
|
226
|
+
"You are Deep Reviewer, a thorough read-only review subagent for risky, security-sensitive, or high-impact changes.",
|
|
227
|
+
"You MUST NOT modify the project by any means: no file edits, no writes, no shell commands that change state, no git commits.",
|
|
228
|
+
"Use read-only tools to deeply inspect the work against requirements, security best practices, architectural soundness, and behavioral correctness.",
|
|
229
|
+
"Focus on security vulnerabilities, data integrity risks, breaking API changes, performance regressions, and architectural drift.",
|
|
230
|
+
"Return findings ordered by severity with file and line references. For each finding, explain the risk and recommend a concrete fix.",
|
|
231
|
+
"If a fix is needed, describe it precisely and recommend dispatching the coder or resolver agent. Never apply fixes yourself.",
|
|
232
|
+
].join("\n"),
|
|
233
|
+
permission: {
|
|
234
|
+
edit: "deny",
|
|
235
|
+
bash: "deny",
|
|
236
|
+
webfetch: "ask",
|
|
237
|
+
},
|
|
238
|
+
},
|
|
170
239
|
};
|
|
171
240
|
export const OpencodeResolve = async ({ directory }, options) => {
|
|
172
241
|
return {
|
|
@@ -230,7 +299,7 @@ function applyResolveConfig(config, resolveConfig) {
|
|
|
230
299
|
if (resolveConfig.commands) {
|
|
231
300
|
config.command ??= {};
|
|
232
301
|
config.command["resolve"] ??= {
|
|
233
|
-
template: "Drive this task to a verified resolution end-to-end.
|
|
302
|
+
template: "Drive this task to a verified resolution end-to-end. Classify, explore when needed, dispatch focused subagents within the configured per-role limit, verify, and iterate. $ARGUMENTS",
|
|
234
303
|
description: "Run the OpenCode Resolve resolver agent end-to-end",
|
|
235
304
|
agent: "resolver",
|
|
236
305
|
subtask: true,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"enabled": ["coder", "reviewer", "
|
|
2
|
+
"enabled": ["coder", "resolver", "explorer", "reviewer", "deep-reviewer"],
|
|
3
3
|
"preserveNative": true,
|
|
4
4
|
"context7": true,
|
|
5
5
|
"commands": false,
|
|
@@ -8,14 +8,24 @@
|
|
|
8
8
|
"models": {},
|
|
9
9
|
"agents": {
|
|
10
10
|
"coder": {
|
|
11
|
-
"
|
|
12
|
-
},
|
|
13
|
-
"reviewer": {
|
|
11
|
+
"enabled": true,
|
|
14
12
|
"mode": "all"
|
|
15
13
|
},
|
|
16
14
|
"resolver": {
|
|
17
15
|
"enabled": true
|
|
18
16
|
},
|
|
17
|
+
"explorer": {
|
|
18
|
+
"enabled": true,
|
|
19
|
+
"mode": "subagent"
|
|
20
|
+
},
|
|
21
|
+
"reviewer": {
|
|
22
|
+
"enabled": true,
|
|
23
|
+
"mode": "subagent"
|
|
24
|
+
},
|
|
25
|
+
"deep-reviewer": {
|
|
26
|
+
"enabled": true,
|
|
27
|
+
"mode": "subagent"
|
|
28
|
+
},
|
|
19
29
|
"architect": {
|
|
20
30
|
"enabled": false
|
|
21
31
|
},
|
|
@@ -30,4 +40,3 @@
|
|
|
30
40
|
}
|
|
31
41
|
}
|
|
32
42
|
}
|
|
33
|
-
|
|
@@ -19,11 +19,18 @@
|
|
|
19
19
|
{
|
|
20
20
|
// -----------------------------------------------------------------------
|
|
21
21
|
// enabled (string[])
|
|
22
|
-
// Which resolve agents to inject.
|
|
22
|
+
// Which resolve agents to inject.
|
|
23
|
+
// Default: ["coder", "resolver", "explorer", "reviewer", "deep-reviewer"].
|
|
24
|
+
//
|
|
25
|
+
// Core path: resolver + coder form the fixed-role verified resolve loop.
|
|
26
|
+
// Internal specialists: explorer, reviewer, deep-reviewer are injected as
|
|
27
|
+
// subagents by default (OpenCode-native composition), but are NOT the
|
|
28
|
+
// default execution path. The resolver dispatches them only when justified.
|
|
29
|
+
//
|
|
23
30
|
// Native OpenCode agents (plan, build) are always preserved and never
|
|
24
31
|
// touched by this list. Per-agent `agents.<name>.enabled` overrides this.
|
|
25
32
|
// -----------------------------------------------------------------------
|
|
26
|
-
"enabled": ["coder", "reviewer", "
|
|
33
|
+
"enabled": ["coder", "resolver", "explorer", "reviewer", "deep-reviewer"],
|
|
27
34
|
|
|
28
35
|
// -----------------------------------------------------------------------
|
|
29
36
|
// preserveNative (boolean)
|
|
@@ -59,11 +66,8 @@
|
|
|
59
66
|
// -----------------------------------------------------------------------
|
|
60
67
|
// maxParallelSubagents (positive integer)
|
|
61
68
|
// Maximum number of subagents of the SAME ROLE the resolver may dispatch
|
|
62
|
-
// concurrently. Default: 2 — at most two coders in parallel
|
|
63
|
-
//
|
|
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)
|
|
69
|
+
// concurrently. Default: 2 — at most two coders in parallel.
|
|
70
|
+
// 1 = strict per-role serial
|
|
67
71
|
// 2 = default
|
|
68
72
|
// N = up to N of each role
|
|
69
73
|
// -----------------------------------------------------------------------
|
|
@@ -72,22 +76,58 @@
|
|
|
72
76
|
// -----------------------------------------------------------------------
|
|
73
77
|
// models (object)
|
|
74
78
|
// Alias map. Keys are either an agent name (coder, reviewer, resolver,
|
|
75
|
-
// architect, gpt-coder, debugger, researcher
|
|
76
|
-
// aliases (`glm`, `gpt`
|
|
79
|
+
// architect, gpt-coder, debugger, researcher, explorer, deep-reviewer)
|
|
80
|
+
// OR one of the well-known aliases (`glm`, `gpt`, `quick`, `deep`,
|
|
81
|
+
// `fast`, `strong`, `mini`, `codex`).
|
|
82
|
+
// Values are model identifiers OR another alias.
|
|
77
83
|
//
|
|
78
84
|
// Resolution order for a given agent:
|
|
79
85
|
// 1. agents.<name>.model
|
|
80
86
|
// 2. models.<name> (resolved through the alias map)
|
|
81
87
|
// 3. top-level OpenCode `model`
|
|
82
88
|
// 4. OpenCode's own fallback
|
|
89
|
+
//
|
|
90
|
+
// By default the models map is empty — all agents inherit the top-level
|
|
91
|
+
// OpenCode default model. Pin models only when you have measured a
|
|
92
|
+
// benefit from role-specific model selection.
|
|
93
|
+
//
|
|
94
|
+
// When resolve.json is created for the first time by postinstall, the
|
|
95
|
+
// plugin inspects your OpenCode model config and writes an adaptive
|
|
96
|
+
// preset:
|
|
97
|
+
// - GLM/ZAI detected → mixed GLM + GPT aliases
|
|
98
|
+
// - OpenAI/GPT detected → single-provider GPT aliases using your model
|
|
99
|
+
// - Other or no model → empty (model-neutral inheritance)
|
|
100
|
+
// Existing resolve.json files are never overwritten. Delete resolve.json
|
|
101
|
+
// and reinstall to regenerate the preset.
|
|
102
|
+
//
|
|
103
|
+
// Example — one efficient model for everything (recommended default):
|
|
104
|
+
// "models": {}
|
|
105
|
+
//
|
|
106
|
+
// Example — pin roles to specific models when needed:
|
|
107
|
+
// "models": {
|
|
108
|
+
// "fast": "openai/gpt-5-mini",
|
|
109
|
+
// "strong": "openai/gpt-5.3-codex",
|
|
110
|
+
// "coder": "fast",
|
|
111
|
+
// "resolver": "strong"
|
|
112
|
+
// }
|
|
113
|
+
//
|
|
114
|
+
// Example — GLM + GPT mixed preset (auto-generated for GLM/ZAI users):
|
|
115
|
+
// "models": {
|
|
116
|
+
// "glm": "zai-coding-plan/glm-5.1",
|
|
117
|
+
// "gpt": "openai/gpt-5.5",
|
|
118
|
+
// "fast": "glm",
|
|
119
|
+
// "strong": "gpt",
|
|
120
|
+
// "coder": "glm",
|
|
121
|
+
// "resolver": "gpt",
|
|
122
|
+
// "reviewer": "gpt",
|
|
123
|
+
// "deep-reviewer": "gpt",
|
|
124
|
+
// "explorer": "fast"
|
|
125
|
+
// }
|
|
126
|
+
//
|
|
127
|
+
// Legacy aliases (glm, gpt, quick, deep) remain supported for backward
|
|
128
|
+
// compatibility. New configs should prefer provider-neutral aliases.
|
|
83
129
|
// -----------------------------------------------------------------------
|
|
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
|
-
},
|
|
130
|
+
"models": {},
|
|
91
131
|
|
|
92
132
|
// -----------------------------------------------------------------------
|
|
93
133
|
// agents (object)
|
|
@@ -104,7 +144,6 @@
|
|
|
104
144
|
"agents": {
|
|
105
145
|
"coder": {
|
|
106
146
|
"enabled": true,
|
|
107
|
-
"model": "glm",
|
|
108
147
|
"mode": "subagent",
|
|
109
148
|
"maxSteps": 20,
|
|
110
149
|
"permission": {
|
|
@@ -114,9 +153,37 @@
|
|
|
114
153
|
}
|
|
115
154
|
},
|
|
116
155
|
|
|
117
|
-
"
|
|
156
|
+
"resolver": {
|
|
118
157
|
"enabled": true,
|
|
119
|
-
"
|
|
158
|
+
"mode": "all",
|
|
159
|
+
"maxSteps": 30,
|
|
160
|
+
"permission": {
|
|
161
|
+
"edit": "allow",
|
|
162
|
+
"bash": "allow",
|
|
163
|
+
"webfetch": "allow"
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
// Internal specialist subagents — enabled by default as subagents only.
|
|
168
|
+
// Core path is resolver→coder. These are dispatched only when justified.
|
|
169
|
+
|
|
170
|
+
// Explorer: internal pre-change fast scout for codebase/file/pattern
|
|
171
|
+
// discovery. Resolver prefers local read/grep/glob for narrow scope;
|
|
172
|
+
// dispatches explorer only when scope is genuinely unknown.
|
|
173
|
+
"explorer": {
|
|
174
|
+
"mode": "subagent",
|
|
175
|
+
"maxSteps": 6,
|
|
176
|
+
"permission": {
|
|
177
|
+
"edit": "deny",
|
|
178
|
+
"bash": "allow",
|
|
179
|
+
"webfetch": "allow"
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
// Reviewer: internal read-only verification-gap auditor. Not part of
|
|
184
|
+
// the core path. Resolver dispatches only for verification gaps on
|
|
185
|
+
// non-trivial changes.
|
|
186
|
+
"reviewer": {
|
|
120
187
|
"mode": "subagent",
|
|
121
188
|
"maxSteps": 8,
|
|
122
189
|
// Reviewer is read-only by design. edit and bash are denied even when
|
|
@@ -128,40 +195,38 @@
|
|
|
128
195
|
}
|
|
129
196
|
},
|
|
130
197
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
"
|
|
198
|
+
// Deep Reviewer: internal post-change strong read-only review for
|
|
199
|
+
// risky/security/architecture/high-impact changes. Resolver dispatches
|
|
200
|
+
// ONLY for risky, security-sensitive, or high-impact work.
|
|
201
|
+
"deep-reviewer": {
|
|
202
|
+
"mode": "subagent",
|
|
203
|
+
"maxSteps": 12,
|
|
136
204
|
"permission": {
|
|
137
|
-
"edit": "
|
|
138
|
-
"bash": "
|
|
205
|
+
"edit": "deny",
|
|
206
|
+
"bash": "deny",
|
|
139
207
|
"webfetch": "allow"
|
|
140
208
|
}
|
|
141
209
|
},
|
|
142
210
|
|
|
143
|
-
//
|
|
211
|
+
// Disabled by default — enable explicitly when needed.
|
|
212
|
+
|
|
144
213
|
"architect": {
|
|
145
214
|
"enabled": false,
|
|
146
|
-
"model": "gpt",
|
|
147
215
|
"mode": "subagent",
|
|
148
216
|
"maxSteps": 10
|
|
149
217
|
},
|
|
150
218
|
"gpt-coder": {
|
|
151
219
|
"enabled": false,
|
|
152
|
-
"model": "gpt",
|
|
153
220
|
"mode": "subagent",
|
|
154
221
|
"maxSteps": 20
|
|
155
222
|
},
|
|
156
223
|
"debugger": {
|
|
157
224
|
"enabled": false,
|
|
158
|
-
"model": "glm",
|
|
159
225
|
"mode": "subagent",
|
|
160
226
|
"maxSteps": 14
|
|
161
227
|
},
|
|
162
228
|
"researcher": {
|
|
163
229
|
"enabled": false,
|
|
164
|
-
"model": "glm",
|
|
165
230
|
"mode": "subagent",
|
|
166
231
|
"maxSteps": 8
|
|
167
232
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-resolve",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { constants } from "node:fs"
|
|
2
|
-
import { access,
|
|
2
|
+
import { access, mkdir, readFile, symlink, unlink, writeFile } from "node:fs/promises"
|
|
3
3
|
import { homedir } from "node:os"
|
|
4
4
|
import { dirname, join, resolve } from "node:path"
|
|
5
5
|
import { fileURLToPath } from "node:url"
|
|
@@ -8,8 +8,9 @@ const root = resolve(dirname(fileURLToPath(import.meta.url)), "..")
|
|
|
8
8
|
const pluginTarget = join(root, "dist", "index.js")
|
|
9
9
|
const configDir = join(homedir(), ".config", "opencode")
|
|
10
10
|
const pluginLink = join(configDir, "plugins", "opencode-resolve.js")
|
|
11
|
-
const
|
|
11
|
+
const resolveConfigPath = join(configDir, "resolve.json")
|
|
12
12
|
const exampleConfig = join(root, "opencode-resolve.example.json")
|
|
13
|
+
const opencodeConfigPath = join(configDir, "opencode.json")
|
|
13
14
|
|
|
14
15
|
await assertFile(pluginTarget)
|
|
15
16
|
await mkdir(dirname(pluginLink), { recursive: true })
|
|
@@ -22,12 +23,99 @@ try {
|
|
|
22
23
|
|
|
23
24
|
await symlink(pluginTarget, pluginLink)
|
|
24
25
|
|
|
25
|
-
if (!(await exists(
|
|
26
|
-
await
|
|
26
|
+
if (!(await exists(resolveConfigPath))) {
|
|
27
|
+
await createAdaptiveResolveConfig()
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
console.log(`Linked plugin: ${pluginLink} -> ${pluginTarget}`)
|
|
30
|
-
console.log(`Resolve config: ${
|
|
31
|
+
console.log(`Resolve config: ${resolveConfigPath}`)
|
|
32
|
+
|
|
33
|
+
async function createAdaptiveResolveConfig() {
|
|
34
|
+
const raw = await readFile(exampleConfig, "utf8")
|
|
35
|
+
const example = JSON.parse(raw)
|
|
36
|
+
|
|
37
|
+
let opencodeConfig = {}
|
|
38
|
+
try {
|
|
39
|
+
const configRaw = await readFile(opencodeConfigPath, "utf8")
|
|
40
|
+
opencodeConfig = JSON.parse(configRaw)
|
|
41
|
+
} catch {
|
|
42
|
+
// opencode.json not found or unreadable — use empty config
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const currentModel = detectOpenCodeModel(opencodeConfig)
|
|
46
|
+
const preset = buildModelPreset(currentModel)
|
|
47
|
+
|
|
48
|
+
const resolveConfig = { ...example }
|
|
49
|
+
if (preset && Object.keys(preset).length > 0) {
|
|
50
|
+
resolveConfig.models = preset
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await mkdir(configDir, { recursive: true })
|
|
54
|
+
await writeFile(resolveConfigPath, `${JSON.stringify(resolveConfig, null, 2)}\n`)
|
|
55
|
+
|
|
56
|
+
const label = getPresetLabel(currentModel)
|
|
57
|
+
console.log(`Created ${resolveConfigPath} (preset: ${label})`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function detectOpenCodeModel(config) {
|
|
61
|
+
if (typeof config.model === "string" && config.model.length > 0) {
|
|
62
|
+
return config.model
|
|
63
|
+
}
|
|
64
|
+
if (isObject(config.models)) {
|
|
65
|
+
for (const value of Object.values(config.models)) {
|
|
66
|
+
if (typeof value === "string" && value.length > 0) return value
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (isObject(config.agent)) {
|
|
70
|
+
for (const agentConfig of Object.values(config.agent)) {
|
|
71
|
+
if (isObject(agentConfig) && typeof agentConfig.model === "string" && agentConfig.model.length > 0) {
|
|
72
|
+
return agentConfig.model
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function buildModelPreset(currentModel) {
|
|
80
|
+
if (!currentModel) return {}
|
|
81
|
+
const lower = currentModel.toLowerCase()
|
|
82
|
+
if (lower.includes("glm") || lower.includes("zai")) {
|
|
83
|
+
return {
|
|
84
|
+
glm: "zai-coding-plan/glm-5.1",
|
|
85
|
+
gpt: "openai/gpt-5.5",
|
|
86
|
+
fast: "glm",
|
|
87
|
+
strong: "gpt",
|
|
88
|
+
coder: "glm",
|
|
89
|
+
resolver: "gpt",
|
|
90
|
+
reviewer: "gpt",
|
|
91
|
+
"deep-reviewer": "gpt",
|
|
92
|
+
explorer: "fast",
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (lower.includes("openai/") || lower.includes("gpt")) {
|
|
96
|
+
return {
|
|
97
|
+
gpt: currentModel,
|
|
98
|
+
fast: "gpt",
|
|
99
|
+
strong: "gpt",
|
|
100
|
+
mini: "gpt",
|
|
101
|
+
codex: "gpt",
|
|
102
|
+
coder: "gpt",
|
|
103
|
+
resolver: "gpt",
|
|
104
|
+
explorer: "gpt",
|
|
105
|
+
reviewer: "gpt",
|
|
106
|
+
"deep-reviewer": "gpt",
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return {}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function getPresetLabel(currentModel) {
|
|
113
|
+
if (!currentModel) return "inherited"
|
|
114
|
+
const lower = currentModel.toLowerCase()
|
|
115
|
+
if (lower.includes("glm") || lower.includes("zai")) return "glm+gpt"
|
|
116
|
+
if (lower.includes("openai/") || lower.includes("gpt")) return "gpt-only"
|
|
117
|
+
return "inherited"
|
|
118
|
+
}
|
|
31
119
|
|
|
32
120
|
async function assertFile(path) {
|
|
33
121
|
await access(path, constants.R_OK)
|
|
@@ -46,3 +134,7 @@ async function exists(path) {
|
|
|
46
134
|
function isMissingFileError(error) {
|
|
47
135
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT"
|
|
48
136
|
}
|
|
137
|
+
|
|
138
|
+
function isObject(value) {
|
|
139
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
140
|
+
}
|