opencode-resolve 0.1.13 → 0.1.17

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/state.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ProjectContext, ResolveConfig } from "./types.js";
2
+ import type { Locale } from "./messages.js";
2
3
  export declare const DIAGNOSTICS_TTL_MS = 30000;
3
4
  export declare const FAILURE_PATTERN_TTL_MS = 120000;
4
5
  export declare const FAILURE_THRESHOLD = 10;
@@ -29,5 +30,7 @@ export interface SessionState {
29
30
  sessionStartTime: number;
30
31
  loopWarnings: string[];
31
32
  lastStrategyHint: string;
33
+ currentAgent?: string;
34
+ locale: Locale;
32
35
  }
33
36
  export declare function createSessionState(): SessionState;
package/dist/state.js CHANGED
@@ -15,6 +15,7 @@ export function createSessionState() {
15
15
  totalToolCalls: 0,
16
16
  sessionStartTime: Date.now(),
17
17
  loopWarnings: [],
18
- lastStrategyHint: ""
18
+ lastStrategyHint: "",
19
+ locale: "en"
19
20
  };
20
21
  }
@@ -238,6 +238,11 @@ export declare function getTools(sessionState: SessionState): {
238
238
  args: {};
239
239
  execute(args: Record<string, never>, context: import("@opencode-ai/plugin").ToolContext): Promise<import("@opencode-ai/plugin").ToolResult>;
240
240
  };
241
+ "resolve-version": {
242
+ description: string;
243
+ args: {};
244
+ execute(args: Record<string, never>, context: import("@opencode-ai/plugin").ToolContext): Promise<import("@opencode-ai/plugin").ToolResult>;
245
+ };
241
246
  "resolve-state": {
242
247
  description: string;
243
248
  args: {
@@ -2,7 +2,9 @@ import { tool } from "@opencode-ai/plugin";
2
2
  import { stat, readFile, access } from "node:fs/promises";
3
3
  import { resolve, join } from "node:path";
4
4
  import { writeFileSync, mkdirSync } from "node:fs";
5
- import { classifyBashCommand, runCommand, sanitizeShellArg, truncateOutput } from "../utils.js";
5
+ import { fileURLToPath } from "node:url";
6
+ import { dirname } from "node:path";
7
+ import { classifyBashCommand, runCommand, sanitizeShellArg, truncateOutput, PLUGIN_VERSION } from "../utils.js";
6
8
  import { DIAGNOSTICS_TTL_MS } from "../state.js";
7
9
  import { VALID_PROFILES, VALID_TIERS, VALID_AGENT_NAME_SET, VALID_AGENT_NAMES } from "../agents.js";
8
10
  const WRITE_CAPABLE_AGENTS = new Set(["resolver", "codex", "coder", "glm", "gpt-coder", "debugger"]);
@@ -1140,6 +1142,23 @@ export function getTools(sessionState) {
1140
1142
  return results.join("\n");
1141
1143
  },
1142
1144
  }),
1145
+ "resolve-version": tool({
1146
+ description: "Report the currently loaded opencode-resolve plugin version and the cache path it was loaded from. Use to confirm whether a recent install/update actually took effect inside OpenCode.",
1147
+ args: {},
1148
+ async execute(_args, ctx) {
1149
+ let loadedFrom = "unknown";
1150
+ try {
1151
+ loadedFrom = dirname(fileURLToPath(import.meta.url));
1152
+ }
1153
+ catch { /* ignore */ }
1154
+ const lines = [
1155
+ `opencode-resolve v${PLUGIN_VERSION}`,
1156
+ `loaded from: ${loadedFrom}`,
1157
+ ];
1158
+ ctx.metadata({ title: `version: v${PLUGIN_VERSION}` });
1159
+ return lines.join("\n");
1160
+ },
1161
+ }),
1143
1162
  "resolve-state": tool({
1144
1163
  description: "Read or write session state checkpoints to .opencode/resolve-state.json. Enables session resumption and cross-turn state persistence. Use 'save' to checkpoint current progress, 'load' to read last checkpoint.",
1145
1164
  args: {
package/dist/types.d.ts CHANGED
@@ -21,6 +21,7 @@ export type ResolveAgentConfig = {
21
21
  };
22
22
  export type ProfileName = "mix" | "glm" | "gpt";
23
23
  export type TierName = "bronze" | "silver" | "gold";
24
+ export type LanguageSetting = "auto" | "en" | "ko";
24
25
  export type ResolveConfig = {
25
26
  profile?: ProfileName;
26
27
  tier?: TierName;
@@ -33,6 +34,7 @@ export type ResolveConfig = {
33
34
  autoApprove?: boolean;
34
35
  maxParallelSubagents?: number;
35
36
  autoUpdate?: boolean;
37
+ language?: LanguageSetting;
36
38
  };
37
39
  export type ResolvePluginOptions = ResolveConfig & {
38
40
  config?: string;
package/dist/utils.js CHANGED
@@ -246,9 +246,12 @@ export function isNewerVersion(candidate, baseline) {
246
246
  return false;
247
247
  }
248
248
  export async function maybeAutoUpdate() {
249
+ const previous = readUpdateCheckCache();
249
250
  try {
250
- const previous = readUpdateCheckCache();
251
251
  if (previous && Date.now() - previous.checkedAt < UPDATE_CHECK_INTERVAL_MS) {
252
+ if (isNewerVersion(previous.latest, PLUGIN_VERSION)) {
253
+ refreshPluginCacheInBackground(previous.latest, "cached");
254
+ }
252
255
  return;
253
256
  }
254
257
  }
@@ -280,9 +283,22 @@ export async function maybeAutoUpdate() {
280
283
  }
281
284
  if (!isNewerVersion(latest, PLUGIN_VERSION))
282
285
  return;
283
- 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}).`);
286
+ refreshPluginCacheInBackground(latest, "registry");
287
+ }
288
+ function refreshPluginCacheInBackground(latest, source) {
289
+ const sourceLabel = source === "cached" ? "cached latest" : "registry latest";
290
+ console.log(`[opencode-resolve] new version v${latest} available (${sourceLabel}, current: v${PLUGIN_VERSION}) — refreshing OpenCode plugin cache in background. Restart OpenCode to activate it.`);
284
291
  try {
285
- spawn("sh", ["-c", `rm -rf "${PLUGIN_CACHE_DIR}" && opencode plugin opencode-resolve --global --force`], { detached: true, stdio: "ignore" }).unref();
292
+ spawn("sh", ["-c", `rm -rf "${PLUGIN_CACHE_DIR}" && opencode plugin opencode-resolve@latest --global --force`], {
293
+ detached: true,
294
+ stdio: "ignore",
295
+ env: {
296
+ ...process.env,
297
+ OPENCODE_RESOLVE_REFRESHING_CACHE: "1",
298
+ OPENCODE_RESOLVE_SKIP_POSTINSTALL: "1",
299
+ OPENCODE_RESOLVE_SKIP_COMPANIONS: "1",
300
+ },
301
+ }).unref();
286
302
  }
287
303
  catch {
288
304
  // If spawn fails, the user already saw the notice and can run the command manually.
@@ -311,6 +327,7 @@ export const BANNED_COMMANDS = [
311
327
  /\b(irb|ghci|scala|jshell)\b(\s*$)/, // other REPLs
312
328
  /\b(bash|zsh|fish|sh)\s+-i\b/, // interactive shells
313
329
  /\bgit\s+add\s+-p\b/, // interactive git add
330
+ /\bgit\s+add\s+(\.|-A|--all)(\s|$)/, // broad staging can capture unrelated files
314
331
  /\bgit\s+rebase\s+-i\b/, // interactive rebase
315
332
  /\bgit\s+commit\b(?!\s+-m)/, // commit without -m
316
333
  /\bscreen\b/, // screen multiplexer
@@ -5,6 +5,7 @@
5
5
  "commands": false,
6
6
  "autoApprove": true,
7
7
  "autoUpdate": true,
8
+ "language": "auto",
8
9
  "models": {},
9
10
  "agents": {
10
11
  "coder": {
@@ -116,6 +116,22 @@
116
116
  // -----------------------------------------------------------------------
117
117
  "maxParallelSubagents": 2,
118
118
 
119
+ // -----------------------------------------------------------------------
120
+ // language ("auto" | "en" | "ko")
121
+ // Locale for hook-generated reminders, role-play narration, and system
122
+ // nudges. Does NOT change agent prompts or model output — only the
123
+ // plugin's own messages.
124
+ //
125
+ // "auto" — detect from $LANG (defaults to en if not Korean). Default.
126
+ // "en" — force English.
127
+ // "ko" — force Korean.
128
+ //
129
+ // Examples of what this affects:
130
+ // en: "[resolver] Reminder: verify your changes ..."
131
+ // ko: "[리졸버] 리마인더: 완료 보고 전에 변경사항을 검증하세요 ..."
132
+ // -----------------------------------------------------------------------
133
+ "language": "auto",
134
+
119
135
  // -----------------------------------------------------------------------
120
136
  // models (object)
121
137
  // Alias map. Keys are either an agent name (coder, reviewer, resolver,
@@ -145,7 +161,8 @@
145
161
  // - non-interactive install → no guessed model pinning; profile mix,
146
162
  // models {}, and resolver/codex/glm primary routes enabled
147
163
  // - OPENCODE_RESOLVE_AUTO_PRESET=1 → legacy non-interactive
148
- // provider-adapted presets
164
+ // provider-adapted presets. GLM presets prefer high-concurrency models
165
+ // such as GLM-5.1 / GLM-4.5 over low-concurrency Flash tiers.
149
166
  // Existing resolve.json files are never overwritten without consent.
150
167
  // Interactive reinstall asks whether to update or back up and run fresh
151
168
  // setup. Non-interactive automation can set:
@@ -168,8 +185,8 @@
168
185
  // third-party providers never blocks agent execution.
169
186
  // "models": {
170
187
  // "glm": "zai-coding-plan/glm-5.1",
171
- // "bronze": "zai-coding-plan/glm-4.7-flash",
172
- // "silver": "zai-coding-plan/glm-5",
188
+ // "bronze": "zai-coding-plan/glm-4.5",
189
+ // "silver": "zai-coding-plan/glm-5.1",
173
190
  // "gold": "zai-coding-plan/glm-5.1",
174
191
  // "fast": "bronze",
175
192
  // "strong": "gold",
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "opencode-resolve",
3
- "version": "0.1.13",
3
+ "version": "0.1.17",
4
4
  "description": "OpenCode plugin that adds a lightweight resolver/coder harness for continuous agentic coding.",
5
5
  "type": "module",
6
- "homepage": "https://github.com/jshsakura/opencode-resolve#readme",
6
+ "homepage": "https://jshsakura.github.io/opencode-resolve/",
7
7
  "bugs": {
8
8
  "url": "https://github.com/jshsakura/opencode-resolve/issues"
9
9
  },
@@ -19,6 +19,9 @@
19
19
  "types": "./dist/index.d.ts"
20
20
  }
21
21
  },
22
+ "bin": {
23
+ "opencode-resolve": "./scripts/cli.mjs"
24
+ },
22
25
  "files": [
23
26
  "dist",
24
27
  "scripts",
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process"
3
+ import { dirname, join, resolve } from "node:path"
4
+ import { fileURLToPath } from "node:url"
5
+
6
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), "..")
7
+ const postinstall = join(root, "scripts", "postinstall.mjs")
8
+ const args = process.argv.slice(2)
9
+ const command = args[0] && !args[0].startsWith("-") ? args.shift() : "setup"
10
+
11
+ if (command === "help" || args.includes("--help") || args.includes("-h")) {
12
+ printHelp()
13
+ process.exit(0)
14
+ }
15
+
16
+ if (command !== "setup") {
17
+ console.error(`Unknown command: ${command}`)
18
+ printHelp()
19
+ process.exit(1)
20
+ }
21
+
22
+ const env = { ...process.env }
23
+ if (args.includes("--fresh")) env.OPENCODE_RESOLVE_REINSTALL = "fresh"
24
+ if (args.includes("--reset-config")) {
25
+ env.OPENCODE_RESOLVE_REINSTALL = "fresh"
26
+ env.OPENCODE_RESOLVE_RESET_MODELS = "1"
27
+ }
28
+ if (args.includes("--update")) env.OPENCODE_RESOLVE_REINSTALL = "update"
29
+ if (args.includes("--models") || args.includes("--configure-models")) env.OPENCODE_RESOLVE_CONFIGURE_MODELS = "1"
30
+ if (args.includes("--auto-preset")) env.OPENCODE_RESOLVE_AUTO_PRESET = "1"
31
+ if (args.includes("--no-companions")) env.OPENCODE_RESOLVE_SKIP_COMPANIONS = "1"
32
+ if (args.includes("--force-cache") || args.includes("--refresh-cache")) env.OPENCODE_RESOLVE_FORCE_CACHE_REFRESH = "1"
33
+
34
+ const child = spawn(process.execPath, [postinstall], {
35
+ stdio: "inherit",
36
+ env,
37
+ })
38
+
39
+ child.on("exit", (code, signal) => {
40
+ if (signal) {
41
+ process.kill(process.pid, signal)
42
+ return
43
+ }
44
+ process.exit(code ?? 1)
45
+ })
46
+
47
+ child.on("error", (error) => {
48
+ console.error(error.message)
49
+ process.exit(1)
50
+ })
51
+
52
+ function printHelp() {
53
+ console.log(`opencode-resolve
54
+
55
+ Usage:
56
+ opencode-resolve setup [--fresh|--update|--reset-config] [--models] [--auto-preset] [--force-cache] [--no-companions]
57
+
58
+ Commands:
59
+ setup Register the OpenCode plugin, create or migrate resolve.json, and refresh stale plugin cache.
60
+
61
+ Options:
62
+ --fresh Back up existing resolve.json and run setup again, preserving existing model pins.
63
+ --update Preserve existing resolve.json and add missing defaults.
64
+ --reset-config Back up existing resolve.json and regenerate it, including model pins.
65
+ --models Reconfigure model pins without replacing the rest of resolve.json.
66
+ --auto-preset Non-interactive provider-based model preset.
67
+ --force-cache Force OpenCode plugin cache refresh without deleting resolve.json.
68
+ --no-companions Skip companion plugin suggestions.
69
+
70
+ Examples:
71
+ opencode-resolve setup --fresh
72
+ opencode-resolve setup --update
73
+ opencode-resolve setup --force-cache
74
+ `)
75
+ }
@@ -40,20 +40,23 @@ async function handleExistingResolveConfig() {
40
40
  return
41
41
  }
42
42
 
43
+ const existing = await readExistingResolveConfig()
43
44
  await backupResolveConfig()
44
- await createAdaptiveResolveConfig()
45
+ await createAdaptiveResolveConfig({
46
+ preservedModels: process.env.OPENCODE_RESOLVE_RESET_MODELS === "1" ? undefined : existing?.models,
47
+ })
45
48
  }
46
49
 
47
50
  async function chooseExistingResolveConfigAction() {
48
- const requested = (process.env.OPENCODE_RESOLVE_REINSTALL ?? "").trim().toLowerCase()
51
+ const requested = readInstallerOption("reinstall").trim().toLowerCase()
49
52
  if (["fresh", "reset", "recreate", "new"].includes(requested)) return "fresh"
50
53
  if (["update", "keep", "migrate", "preserve"].includes(requested)) return "update"
51
54
  if (requested) {
52
- console.warn(`Ignoring unknown OPENCODE_RESOLVE_REINSTALL=${JSON.stringify(requested)}; use "fresh" or "update".`)
55
+ console.warn(`Ignoring unknown reinstall mode ${JSON.stringify(requested)}; use "fresh" or "update".`)
53
56
  }
54
57
 
55
58
  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.`)
59
+ console.log("Existing resolve config found; preserving it. Run `opencode-resolve setup --models` for model setup or `opencode-resolve setup --fresh` to regenerate without dropping model pins.")
57
60
  return "update"
58
61
  }
59
62
 
@@ -62,7 +65,7 @@ async function chooseExistingResolveConfigAction() {
62
65
  console.log("")
63
66
  console.log(`Existing resolve config found: ${resolveConfigPath}`)
64
67
  console.log(" 1. update existing config — preserve your settings")
65
- console.log(" 2. fresh reinstall — back up resolve.json and create a new config")
68
+ console.log(" 2. fresh reinstall — back up resolve.json and create a new config, preserving model pins")
66
69
  const raw = await rl.question("Existing config [1=update, 2=fresh reinstall, default 1]: ")
67
70
  return raw.trim() === "2" ? "fresh" : "update"
68
71
  } finally {
@@ -78,9 +81,20 @@ async function backupResolveConfig() {
78
81
  console.log(`Backed up existing resolve config to ${backupPath}`)
79
82
  }
80
83
 
81
- async function createAdaptiveResolveConfig() {
84
+ async function readExistingResolveConfig() {
85
+ try {
86
+ const raw = await readFile(resolveConfigPath, "utf8")
87
+ const parsed = JSON.parse(raw)
88
+ return isObject(parsed) ? parsed : undefined
89
+ } catch {
90
+ return undefined
91
+ }
92
+ }
93
+
94
+ async function createAdaptiveResolveConfig(options = {}) {
82
95
  const raw = await readFile(exampleConfig, "utf8")
83
96
  const example = JSON.parse(raw)
97
+ const preservedModels = isObject(options.preservedModels) ? options.preservedModels : undefined
84
98
 
85
99
  let opencodeConfig = {}
86
100
  try {
@@ -105,7 +119,9 @@ async function createAdaptiveResolveConfig() {
105
119
  }
106
120
  }
107
121
  if (preset && Object.keys(preset).length > 0) {
108
- resolveConfig.models = preset
122
+ resolveConfig.models = preservedModels ? { ...preset, ...preservedModels } : preset
123
+ } else if (preservedModels) {
124
+ resolveConfig.models = { ...preservedModels }
109
125
  }
110
126
 
111
127
  await mkdir(configDir, { recursive: true })
@@ -198,3 +214,13 @@ function isMissingFileError(error) {
198
214
  function isObject(value) {
199
215
  return typeof value === "object" && value !== null && !Array.isArray(value)
200
216
  }
217
+
218
+ function readInstallerOption(name) {
219
+ const normalized = name.toUpperCase().replace(/-/g, "_")
220
+ const npmName = name.toLowerCase().replace(/-/g, "_")
221
+ return (
222
+ process.env[`OPENCODE_RESOLVE_${normalized}`] ??
223
+ process.env[`npm_config_opencode_resolve_${npmName}`] ??
224
+ ""
225
+ )
226
+ }