opencode-resolve 0.1.7 → 0.1.9
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 +121 -60
- package/README.md +121 -60
- package/dist/agents.d.ts +27 -0
- package/dist/agents.js +392 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.js +404 -0
- package/dist/hooks/index.d.ts +18 -0
- package/dist/hooks/index.js +493 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +11 -702
- package/dist/state.d.ts +33 -0
- package/dist/state.js +20 -0
- package/dist/tools/index.d.ts +252 -0
- package/dist/tools/index.js +1209 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +33 -0
- package/dist/utils.js +371 -0
- package/opencode-resolve.example.json +8 -2
- package/opencode-resolve.reference.jsonc +107 -27
- package/package.json +10 -2
- package/scripts/install-git-hooks.mjs +28 -0
- package/scripts/install-local.mjs +64 -4
- package/scripts/postinstall.mjs +552 -34
|
@@ -17,10 +17,50 @@
|
|
|
17
17
|
// permission values, and wrong types throw an error at load time.
|
|
18
18
|
|
|
19
19
|
{
|
|
20
|
+
// -----------------------------------------------------------------------
|
|
21
|
+
// profile ("mix" | "glm" | "gpt")
|
|
22
|
+
// Activates provider-specific agent configurations.
|
|
23
|
+
// Set automatically by postinstall based on detected providers.
|
|
24
|
+
//
|
|
25
|
+
// "mix" — explicit mixed/default profile. Uses standard resolver prompts,
|
|
26
|
+
// DEFAULT_ENABLED, and whatever model aliases you configure.
|
|
27
|
+
// "glm" — GLM-only, optimized for ZAI coding-plan: reduced maxSteps,
|
|
28
|
+
// token-efficient prompts, no hard concurrency cap by default.
|
|
29
|
+
// No deep-reviewer by default.
|
|
30
|
+
// "gpt" — GPT-only, high-performance: parallel coder dispatch, full agent
|
|
31
|
+
// roster, higher maxSteps.
|
|
32
|
+
//
|
|
33
|
+
// When omitted, the plugin defaults to "mix".
|
|
34
|
+
// -----------------------------------------------------------------------
|
|
35
|
+
"profile": "mix",
|
|
36
|
+
|
|
37
|
+
// -----------------------------------------------------------------------
|
|
38
|
+
// tier ("bronze" | "silver" | "gold")
|
|
39
|
+
// Agent roster preset. Overrides the default enabled list when set.
|
|
40
|
+
//
|
|
41
|
+
// "bronze" — Minimum agents: coder + resolver only. Maximum token savings.
|
|
42
|
+
// "silver" — Standard: coder, resolver, explorer, reviewer, planner.
|
|
43
|
+
// No deep-reviewer. Best balance for most projects.
|
|
44
|
+
// "gold" — Full power: all 8 agents including debugger and researcher.
|
|
45
|
+
// Maximum capability for complex tasks.
|
|
46
|
+
//
|
|
47
|
+
// Postinstall sets this automatically:
|
|
48
|
+
// GLM → silver, GPT → gold, Mix/unknown → no tier (DEFAULT_ENABLED applies).
|
|
49
|
+
//
|
|
50
|
+
// Explicit `enabled` array always wins over tier.
|
|
51
|
+
// -----------------------------------------------------------------------
|
|
52
|
+
// "tier": "silver",
|
|
53
|
+
|
|
20
54
|
// -----------------------------------------------------------------------
|
|
21
55
|
// enabled (string[])
|
|
22
|
-
// Which resolve agents to inject.
|
|
23
|
-
//
|
|
56
|
+
// Which resolve agents to inject. When omitted, the tier or profile default
|
|
57
|
+
// is used. Explicit `enabled` wins over both tier and profile defaults.
|
|
58
|
+
//
|
|
59
|
+
// Mix/default (no tier): ["coder", "resolver", "explorer", "reviewer",
|
|
60
|
+
// "deep-reviewer", "planner"].
|
|
61
|
+
//
|
|
62
|
+
// GLM profile default: ["coder", "resolver", "explorer", "reviewer",
|
|
63
|
+
// "planner"] — deep-reviewer omitted to save tokens.
|
|
24
64
|
//
|
|
25
65
|
// Core path: resolver + coder form the fixed-role verified resolve loop.
|
|
26
66
|
// Internal specialists: explorer, reviewer, deep-reviewer are injected as
|
|
@@ -30,7 +70,7 @@
|
|
|
30
70
|
// Native OpenCode agents (plan, build) are always preserved and never
|
|
31
71
|
// touched by this list. Per-agent `agents.<name>.enabled` overrides this.
|
|
32
72
|
// -----------------------------------------------------------------------
|
|
33
|
-
"enabled": ["coder", "resolver", "explorer", "reviewer", "deep-reviewer"],
|
|
73
|
+
// "enabled": ["coder", "resolver", "explorer", "reviewer", "deep-reviewer", "planner"],
|
|
34
74
|
|
|
35
75
|
// -----------------------------------------------------------------------
|
|
36
76
|
// preserveNative (boolean)
|
|
@@ -55,20 +95,23 @@
|
|
|
55
95
|
|
|
56
96
|
// -----------------------------------------------------------------------
|
|
57
97
|
// autoApprove (boolean)
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
98
|
+
// Documentary flag. The plugin's permission.ask hook automatically
|
|
99
|
+
// classifies bash commands as safe (allow) / dangerous (deny) / unknown (ask).
|
|
100
|
+
// Base agent permissions are already set to the correct defaults (allow for
|
|
101
|
+
// edit/webfetch, ask for bash on write agents, deny for read-only agents).
|
|
102
|
+
// Changing this flag has no effect on behavior — it exists for config
|
|
103
|
+
// readability and future compatibility. Default: true.
|
|
63
104
|
// -----------------------------------------------------------------------
|
|
64
105
|
"autoApprove": true,
|
|
65
106
|
|
|
66
107
|
// -----------------------------------------------------------------------
|
|
67
108
|
// maxParallelSubagents (positive integer)
|
|
68
|
-
//
|
|
69
|
-
//
|
|
109
|
+
// Optional prompt-level cap for how many subagents of the SAME ROLE the
|
|
110
|
+
// resolver may dispatch concurrently. When omitted, the resolver uses soft
|
|
111
|
+
// fan-out guidance and backs off on rate-limit errors. GLM profile does not
|
|
112
|
+
// impose a hard cap unless you set one.
|
|
70
113
|
// 1 = strict per-role serial
|
|
71
|
-
// 2 =
|
|
114
|
+
// 2 = up to two coders in parallel
|
|
72
115
|
// N = up to N of each role
|
|
73
116
|
// -----------------------------------------------------------------------
|
|
74
117
|
"maxParallelSubagents": 2,
|
|
@@ -76,9 +119,12 @@
|
|
|
76
119
|
// -----------------------------------------------------------------------
|
|
77
120
|
// models (object)
|
|
78
121
|
// Alias map. Keys are either an agent name (coder, reviewer, resolver,
|
|
79
|
-
// architect, gpt-coder, debugger, researcher, explorer,
|
|
122
|
+
// codex, glm, architect, gpt-coder, debugger, researcher, explorer,
|
|
123
|
+
// deep-reviewer, planner)
|
|
80
124
|
// OR one of the well-known aliases (`glm`, `gpt`, `quick`, `deep`,
|
|
81
|
-
// `fast`, `strong`, `mini`, `codex`
|
|
125
|
+
// `fast`, `strong`, `mini`, `codex`, `bronze`, `silver`, `gold`,
|
|
126
|
+
// `gpt-bronze`, `gpt-silver`, `gpt-gold`, `glm-bronze`, `glm-silver`,
|
|
127
|
+
// `glm-gold`).
|
|
82
128
|
// Values are model identifiers OR another alias.
|
|
83
129
|
//
|
|
84
130
|
// Resolution order for a given agent:
|
|
@@ -94,11 +140,17 @@
|
|
|
94
140
|
// When resolve.json is created for the first time by postinstall, the
|
|
95
141
|
// plugin inspects your OpenCode model config and writes an adaptive
|
|
96
142
|
// preset:
|
|
97
|
-
// -
|
|
98
|
-
//
|
|
99
|
-
// -
|
|
100
|
-
//
|
|
101
|
-
//
|
|
143
|
+
// - interactive terminal → always prompts for mix/gpt/glm, three-tier models,
|
|
144
|
+
// and optional dedicated codex/glm primary agents
|
|
145
|
+
// - non-interactive install → no guessed model pinning; profile mix,
|
|
146
|
+
// models {}, and resolver/codex/glm primary routes enabled
|
|
147
|
+
// - OPENCODE_RESOLVE_AUTO_PRESET=1 → legacy non-interactive
|
|
148
|
+
// provider-adapted presets
|
|
149
|
+
// Existing resolve.json files are never overwritten without consent.
|
|
150
|
+
// Interactive reinstall asks whether to update or back up and run fresh
|
|
151
|
+
// setup. Non-interactive automation can set:
|
|
152
|
+
// OPENCODE_RESOLVE_REINSTALL=fresh
|
|
153
|
+
// OPENCODE_RESOLVE_REINSTALL=update
|
|
102
154
|
//
|
|
103
155
|
// Example — one efficient model for everything (recommended default):
|
|
104
156
|
// "models": {}
|
|
@@ -111,17 +163,22 @@
|
|
|
111
163
|
// "resolver": "strong"
|
|
112
164
|
// }
|
|
113
165
|
//
|
|
114
|
-
// Example — GLM
|
|
166
|
+
// Example — GLM-only three-tier preset (auto-generated for GLM/ZAI users):
|
|
167
|
+
// GLM fills all tiers — no GPT dependency, so token exhaustion on
|
|
168
|
+
// third-party providers never blocks agent execution.
|
|
115
169
|
// "models": {
|
|
116
170
|
// "glm": "zai-coding-plan/glm-5.1",
|
|
117
|
-
// "
|
|
118
|
-
// "
|
|
119
|
-
// "
|
|
120
|
-
// "
|
|
121
|
-
// "
|
|
122
|
-
// "
|
|
123
|
-
// "
|
|
124
|
-
// "
|
|
171
|
+
// "bronze": "zai-coding-plan/glm-5.1",
|
|
172
|
+
// "silver": "zai-coding-plan/glm-5.1",
|
|
173
|
+
// "gold": "zai-coding-plan/glm-5.1",
|
|
174
|
+
// "fast": "bronze",
|
|
175
|
+
// "strong": "gold",
|
|
176
|
+
// "coder": "silver",
|
|
177
|
+
// "resolver": "gold",
|
|
178
|
+
// "reviewer": "gold",
|
|
179
|
+
// "deep-reviewer": "gold",
|
|
180
|
+
// "explorer": "bronze",
|
|
181
|
+
// "planner": "gold"
|
|
125
182
|
// }
|
|
126
183
|
//
|
|
127
184
|
// Legacy aliases (glm, gpt, quick, deep) remain supported for backward
|
|
@@ -164,6 +221,15 @@
|
|
|
164
221
|
}
|
|
165
222
|
},
|
|
166
223
|
|
|
224
|
+
// codex — Codex-optimized primary resolver (selectable in agent picker)
|
|
225
|
+
// Same verified resolve-loop style as resolver, with a Codex-specific
|
|
226
|
+
// system prompt. Enabled by GPT/mix first-install presets when selected.
|
|
227
|
+
"codex": {
|
|
228
|
+
"enabled": false,
|
|
229
|
+
"mode": "all",
|
|
230
|
+
"maxSteps": 35
|
|
231
|
+
},
|
|
232
|
+
|
|
167
233
|
// Internal specialist subagents — enabled by default as subagents only.
|
|
168
234
|
// Core path is resolver→coder. These are dispatched only when justified.
|
|
169
235
|
|
|
@@ -229,6 +295,20 @@
|
|
|
229
295
|
"enabled": false,
|
|
230
296
|
"mode": "subagent",
|
|
231
297
|
"maxSteps": 8
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
// -----------------------------------------------------------------
|
|
301
|
+
// glm — GLM-optimized orchestrator (selectable in agent picker)
|
|
302
|
+
// Disabled by default. Enable to get a standalone GLM agent with
|
|
303
|
+
// coding-plan optimized prompts: session limit handling,
|
|
304
|
+
// token-efficient execution, no hard concurrency cap unless configured.
|
|
305
|
+
// Users pick this from OpenCode's agent picker as an alternative
|
|
306
|
+
// to the default resolver.
|
|
307
|
+
// -----------------------------------------------------------------
|
|
308
|
+
"glm": {
|
|
309
|
+
"enabled": false,
|
|
310
|
+
"mode": "all",
|
|
311
|
+
"maxSteps": 30
|
|
232
312
|
}
|
|
233
313
|
}
|
|
234
314
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-resolve",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "OpenCode plugin that adds a
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"description": "OpenCode plugin that adds a lightweight resolver/coder harness for continuous agentic coding.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/jshsakura/opencode-resolve#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -25,13 +25,18 @@
|
|
|
25
25
|
"opencode-resolve.example.json",
|
|
26
26
|
"opencode-resolve.reference.jsonc",
|
|
27
27
|
"README.md",
|
|
28
|
+
"README.ko.md",
|
|
28
29
|
"LICENSE"
|
|
29
30
|
],
|
|
30
31
|
"scripts": {
|
|
31
32
|
"build": "tsc -p tsconfig.json",
|
|
33
|
+
"coverage": "npm run build && node --experimental-test-coverage --test-coverage-lines=79 --test-coverage-branches=69 --test-coverage-functions=85 --test test/*.mjs",
|
|
34
|
+
"hooks:install": "node scripts/install-git-hooks.mjs",
|
|
32
35
|
"install:local": "npm run build && node scripts/install-local.mjs",
|
|
33
36
|
"postinstall": "node scripts/postinstall.mjs",
|
|
34
37
|
"prepack": "npm test",
|
|
38
|
+
"precommit": "npm run typecheck && npm test && npm run coverage",
|
|
39
|
+
"prepush": "npm run precommit && npm pack --dry-run",
|
|
35
40
|
"test": "npm run build && node --test test/*.mjs",
|
|
36
41
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
37
42
|
},
|
|
@@ -39,6 +44,9 @@
|
|
|
39
44
|
"opencode",
|
|
40
45
|
"opencode-plugin",
|
|
41
46
|
"agents",
|
|
47
|
+
"agentic-coding",
|
|
48
|
+
"autonomous-coding",
|
|
49
|
+
"harness",
|
|
42
50
|
"taskflow",
|
|
43
51
|
"resolve",
|
|
44
52
|
"task-resolution",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process"
|
|
3
|
+
import { chmodSync, existsSync } from "node:fs"
|
|
4
|
+
import { dirname, join } from "node:path"
|
|
5
|
+
import { fileURLToPath } from "node:url"
|
|
6
|
+
|
|
7
|
+
const root = dirname(dirname(fileURLToPath(import.meta.url)))
|
|
8
|
+
const hooksDir = join(root, ".githooks")
|
|
9
|
+
|
|
10
|
+
if (!existsSync(join(root, ".git"))) {
|
|
11
|
+
console.error("[opencode-resolve] not a git checkout; cannot install hooks")
|
|
12
|
+
process.exit(1)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
for (const hook of ["pre-commit", "pre-push"]) {
|
|
16
|
+
chmodSync(join(hooksDir, hook), 0o755)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const result = spawnSync("git", ["config", "core.hooksPath", ".githooks"], {
|
|
20
|
+
cwd: root,
|
|
21
|
+
stdio: "inherit",
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
if (result.status !== 0) {
|
|
25
|
+
process.exit(result.status ?? 1)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log("[opencode-resolve] git hooks installed: core.hooksPath=.githooks")
|
|
@@ -2,6 +2,7 @@ import { constants } from "node:fs"
|
|
|
2
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
|
+
import { createInterface } from "node:readline/promises"
|
|
5
6
|
import { fileURLToPath } from "node:url"
|
|
6
7
|
|
|
7
8
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..")
|
|
@@ -25,11 +26,58 @@ await symlink(pluginTarget, pluginLink)
|
|
|
25
26
|
|
|
26
27
|
if (!(await exists(resolveConfigPath))) {
|
|
27
28
|
await createAdaptiveResolveConfig()
|
|
29
|
+
} else {
|
|
30
|
+
await handleExistingResolveConfig()
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
console.log(`Linked plugin: ${pluginLink} -> ${pluginTarget}`)
|
|
31
34
|
console.log(`Resolve config: ${resolveConfigPath}`)
|
|
32
35
|
|
|
36
|
+
async function handleExistingResolveConfig() {
|
|
37
|
+
const action = await chooseExistingResolveConfigAction()
|
|
38
|
+
if (action !== "fresh") {
|
|
39
|
+
console.log(`Existing resolve config preserved: ${resolveConfigPath}`)
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
await backupResolveConfig()
|
|
44
|
+
await createAdaptiveResolveConfig()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function chooseExistingResolveConfigAction() {
|
|
48
|
+
const requested = (process.env.OPENCODE_RESOLVE_REINSTALL ?? "").trim().toLowerCase()
|
|
49
|
+
if (["fresh", "reset", "recreate", "new"].includes(requested)) return "fresh"
|
|
50
|
+
if (["update", "keep", "migrate", "preserve"].includes(requested)) return "update"
|
|
51
|
+
if (requested) {
|
|
52
|
+
console.warn(`Ignoring unknown OPENCODE_RESOLVE_REINSTALL=${JSON.stringify(requested)}; use "fresh" or "update".`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
56
|
+
console.log(`Existing resolve config found; preserving it. Set OPENCODE_RESOLVE_REINSTALL=fresh for a fresh reinstall.`)
|
|
57
|
+
return "update"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
61
|
+
try {
|
|
62
|
+
console.log("")
|
|
63
|
+
console.log(`Existing resolve config found: ${resolveConfigPath}`)
|
|
64
|
+
console.log(" 1. update existing config — preserve your settings")
|
|
65
|
+
console.log(" 2. fresh reinstall — back up resolve.json and create a new config")
|
|
66
|
+
const raw = await rl.question("Existing config [1=update, 2=fresh reinstall, default 1]: ")
|
|
67
|
+
return raw.trim() === "2" ? "fresh" : "update"
|
|
68
|
+
} finally {
|
|
69
|
+
rl.close()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function backupResolveConfig() {
|
|
74
|
+
const raw = await readFile(resolveConfigPath, "utf8")
|
|
75
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-")
|
|
76
|
+
const backupPath = `${resolveConfigPath}.bak.${stamp}`
|
|
77
|
+
await writeFile(backupPath, raw)
|
|
78
|
+
console.log(`Backed up existing resolve config to ${backupPath}`)
|
|
79
|
+
}
|
|
80
|
+
|
|
33
81
|
async function createAdaptiveResolveConfig() {
|
|
34
82
|
const raw = await readFile(exampleConfig, "utf8")
|
|
35
83
|
const example = JSON.parse(raw)
|
|
@@ -42,10 +90,20 @@ async function createAdaptiveResolveConfig() {
|
|
|
42
90
|
// opencode.json not found or unreadable — use empty config
|
|
43
91
|
}
|
|
44
92
|
|
|
45
|
-
const currentModel = detectOpenCodeModel(opencodeConfig)
|
|
46
|
-
const preset = buildModelPreset(currentModel)
|
|
47
|
-
|
|
48
93
|
const resolveConfig = { ...example }
|
|
94
|
+
let preset = {}
|
|
95
|
+
if (process.env.OPENCODE_RESOLVE_AUTO_PRESET === "1") {
|
|
96
|
+
const currentModel = detectOpenCodeModel(opencodeConfig)
|
|
97
|
+
preset = buildModelPreset(currentModel)
|
|
98
|
+
} else {
|
|
99
|
+
resolveConfig.profile = "mix"
|
|
100
|
+
resolveConfig.models = {}
|
|
101
|
+
resolveConfig.agents = {
|
|
102
|
+
...resolveConfig.agents,
|
|
103
|
+
codex: { ...(resolveConfig.agents?.codex ?? {}), enabled: true },
|
|
104
|
+
glm: { ...(resolveConfig.agents?.glm ?? {}), enabled: true },
|
|
105
|
+
}
|
|
106
|
+
}
|
|
49
107
|
if (preset && Object.keys(preset).length > 0) {
|
|
50
108
|
resolveConfig.models = preset
|
|
51
109
|
}
|
|
@@ -53,7 +111,9 @@ async function createAdaptiveResolveConfig() {
|
|
|
53
111
|
await mkdir(configDir, { recursive: true })
|
|
54
112
|
await writeFile(resolveConfigPath, `${JSON.stringify(resolveConfig, null, 2)}\n`)
|
|
55
113
|
|
|
56
|
-
const label =
|
|
114
|
+
const label = process.env.OPENCODE_RESOLVE_AUTO_PRESET === "1"
|
|
115
|
+
? getPresetLabel(detectOpenCodeModel(opencodeConfig))
|
|
116
|
+
: "prompt-required"
|
|
57
117
|
console.log(`Created ${resolveConfigPath} (preset: ${label})`)
|
|
58
118
|
}
|
|
59
119
|
|