opencode-resolve 0.1.2 → 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/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", "resolver"];
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"]);
@@ -20,7 +34,7 @@ const VALID_TOP_LEVEL_KEYS = new Set([
20
34
  "maxParallelSubagents",
21
35
  "config",
22
36
  ]);
23
- const DEFAULT_MAX_PARALLEL_SUBAGENTS = 1;
37
+ const DEFAULT_MAX_PARALLEL_SUBAGENTS = 2;
24
38
  const VALID_AGENT_KEYS = new Set([
25
39
  "enabled",
26
40
  "model",
@@ -35,21 +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 only ONE subagent at a time. Never call multiple subagents (coder, reviewer, or any other) in parallel. Wait for the current subagent to finish and evaluate its result before dispatching the next one."
39
- : `CRITICAL: Dispatch at most ${limit} subagents in parallel. Never exceed this limit across coder, reviewer, or any other subagent. Wait for in-flight subagents to finish and evaluate their results before launching new ones.`;
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 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 parallel-dispatch 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.",
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.",
53
80
  ].join("\n");
54
81
  }
55
82
  const DEFAULT_AGENT_CONFIG = {
@@ -60,10 +87,14 @@ const DEFAULT_AGENT_CONFIG = {
60
87
  description: "Use for focused implementation, file edits, test runs, and fixing issues until the task is resolved.",
61
88
  prompt: [
62
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.",
63
93
  "Preserve native OpenCode behavior and make the smallest correct change.",
64
- "Before editing, inspect the relevant files and existing patterns.",
65
- "Implement, run targeted verification when practical, and keep iterating on failures until the task is resolved or clearly blocked.",
66
- "Return a concise summary of changed files, verification results, and any remaining blockers.",
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.",
67
98
  ].join("\n"),
68
99
  permission: {
69
100
  edit: "ask",
@@ -75,9 +106,10 @@ const DEFAULT_AGENT_CONFIG = {
75
106
  mode: "subagent",
76
107
  color: "#8A7CFF",
77
108
  maxSteps: 8,
78
- description: "Read-only Oracle-style reviewer. Inspects code for requirements fit, correctness, security, tests, and maintainability risks. Never modifies anything.",
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.",
79
110
  prompt: [
80
- "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.",
81
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.",
82
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.",
83
115
  "Prioritize concrete bugs, behavioral regressions, security risks, missing tests, and maintainability issues.",
@@ -94,7 +126,7 @@ const DEFAULT_AGENT_CONFIG = {
94
126
  mode: "all",
95
127
  color: "#FF7AC6",
96
128
  maxSteps: 30,
97
- 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.",
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.",
98
130
  prompt: buildResolverPrompt(DEFAULT_MAX_PARALLEL_SUBAGENTS),
99
131
  permission: {
100
132
  edit: "ask",
@@ -166,6 +198,44 @@ const DEFAULT_AGENT_CONFIG = {
166
198
  webfetch: "ask",
167
199
  },
168
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
+ },
169
239
  };
170
240
  export const OpencodeResolve = async ({ directory }, options) => {
171
241
  return {
@@ -229,7 +299,7 @@ function applyResolveConfig(config, resolveConfig) {
229
299
  if (resolveConfig.commands) {
230
300
  config.command ??= {};
231
301
  config.command["resolve"] ??= {
232
- template: "Drive this task to a verified resolution end-to-end. Plan, dispatch one coder at a time, verify, and iterate. $ARGUMENTS",
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",
233
303
  description: "Run the OpenCode Resolve resolver agent end-to-end",
234
304
  agent: "resolver",
235
305
  subtask: true,
@@ -1,20 +1,30 @@
1
1
  {
2
- "enabled": ["coder", "reviewer", "resolver"],
2
+ "enabled": ["coder", "resolver", "explorer", "reviewer", "deep-reviewer"],
3
3
  "preserveNative": true,
4
4
  "context7": true,
5
5
  "commands": false,
6
6
  "autoApprove": true,
7
- "maxParallelSubagents": 1,
7
+ "maxParallelSubagents": 2,
8
8
  "models": {},
9
9
  "agents": {
10
10
  "coder": {
11
+ "enabled": true,
12
+ "mode": "all"
13
+ },
14
+ "resolver": {
11
15
  "enabled": true
12
16
  },
17
+ "explorer": {
18
+ "enabled": true,
19
+ "mode": "subagent"
20
+ },
13
21
  "reviewer": {
14
- "enabled": true
22
+ "enabled": true,
23
+ "mode": "subagent"
15
24
  },
16
- "resolver": {
17
- "enabled": true
25
+ "deep-reviewer": {
26
+ "enabled": true,
27
+ "mode": "subagent"
18
28
  },
19
29
  "architect": {
20
30
  "enabled": false
@@ -19,11 +19,18 @@
19
19
  {
20
20
  // -----------------------------------------------------------------------
21
21
  // enabled (string[])
22
- // Which resolve agents to inject. Default: ["coder", "reviewer", "resolver"].
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", "resolver"],
33
+ "enabled": ["coder", "resolver", "explorer", "reviewer", "deep-reviewer"],
27
34
 
28
35
  // -----------------------------------------------------------------------
29
36
  // preserveNative (boolean)
@@ -58,31 +65,69 @@
58
65
 
59
66
  // -----------------------------------------------------------------------
60
67
  // maxParallelSubagents (positive integer)
61
- // Maximum number of subagents the resolver may dispatch in parallel
62
- // (across coder, reviewer, etc.). Default: 1strictly serial.
63
- // Increase if you want resolver to fan out independent subtasks.
68
+ // Maximum number of subagents of the SAME ROLE the resolver may dispatch
69
+ // concurrently. Default: 2at most two coders in parallel.
70
+ // 1 = strict per-role serial
71
+ // 2 = default
72
+ // N = up to N of each role
64
73
  // -----------------------------------------------------------------------
65
- "maxParallelSubagents": 1,
74
+ "maxParallelSubagents": 2,
66
75
 
67
76
  // -----------------------------------------------------------------------
68
77
  // models (object)
69
78
  // Alias map. Keys are either an agent name (coder, reviewer, resolver,
70
- // architect, gpt-coder, debugger, researcher) OR one of the well-known
71
- // aliases (`glm`, `gpt`). Values are model identifiers OR another alias.
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.
72
83
  //
73
84
  // Resolution order for a given agent:
74
85
  // 1. agents.<name>.model
75
86
  // 2. models.<name> (resolved through the alias map)
76
87
  // 3. top-level OpenCode `model`
77
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.
78
129
  // -----------------------------------------------------------------------
79
- "models": {
80
- "glm": "zai-coding-plan/glm-5.1",
81
- "gpt": "openai/gpt-5.5",
82
- "coder": "glm",
83
- "resolver": "gpt",
84
- "reviewer": "gpt"
85
- },
130
+ "models": {},
86
131
 
87
132
  // -----------------------------------------------------------------------
88
133
  // agents (object)
@@ -99,7 +144,6 @@
99
144
  "agents": {
100
145
  "coder": {
101
146
  "enabled": true,
102
- "model": "glm",
103
147
  "mode": "subagent",
104
148
  "maxSteps": 20,
105
149
  "permission": {
@@ -109,9 +153,37 @@
109
153
  }
110
154
  },
111
155
 
112
- "reviewer": {
156
+ "resolver": {
113
157
  "enabled": true,
114
- "model": "gpt",
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": {
115
187
  "mode": "subagent",
116
188
  "maxSteps": 8,
117
189
  // Reviewer is read-only by design. edit and bash are denied even when
@@ -123,40 +195,38 @@
123
195
  }
124
196
  },
125
197
 
126
- "resolver": {
127
- "enabled": true,
128
- "model": "gpt",
129
- "mode": "all",
130
- "maxSteps": 30,
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,
131
204
  "permission": {
132
- "edit": "allow",
133
- "bash": "allow",
205
+ "edit": "deny",
206
+ "bash": "deny",
134
207
  "webfetch": "allow"
135
208
  }
136
209
  },
137
210
 
138
- // Optional agents — disabled by default. Flip enabled:true to use them.
211
+ // Disabled by default enable explicitly when needed.
212
+
139
213
  "architect": {
140
214
  "enabled": false,
141
- "model": "gpt",
142
215
  "mode": "subagent",
143
216
  "maxSteps": 10
144
217
  },
145
218
  "gpt-coder": {
146
219
  "enabled": false,
147
- "model": "gpt",
148
220
  "mode": "subagent",
149
221
  "maxSteps": 20
150
222
  },
151
223
  "debugger": {
152
224
  "enabled": false,
153
- "model": "glm",
154
225
  "mode": "subagent",
155
226
  "maxSteps": 14
156
227
  },
157
228
  "researcher": {
158
229
  "enabled": false,
159
- "model": "glm",
160
230
  "mode": "subagent",
161
231
  "maxSteps": 8
162
232
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-resolve",
3
- "version": "0.1.2",
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, copyFile, mkdir, symlink, unlink } from "node:fs/promises"
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 resolveConfig = join(configDir, "resolve.json")
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(resolveConfig))) {
26
- await copyFile(exampleConfig, resolveConfig)
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: ${resolveConfig}`)
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
+ }