opencode-resolve 0.1.6 → 0.1.7
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/package.json +1 -1
- package/scripts/postinstall.mjs +118 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-resolve",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
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",
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { spawn } from "node:child_process"
|
|
1
2
|
import { constants } from "node:fs"
|
|
2
3
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises"
|
|
3
4
|
import { homedir } from "node:os"
|
|
4
5
|
import { dirname, join, resolve } from "node:path"
|
|
6
|
+
import { createInterface } from "node:readline/promises"
|
|
5
7
|
import { fileURLToPath } from "node:url"
|
|
6
8
|
|
|
7
9
|
const packageName = "opencode-resolve"
|
|
@@ -15,6 +17,17 @@ const ADDITIVE_DEFAULTS = {
|
|
|
15
17
|
autoApprove: true,
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
const COMPANION_PLUGINS = [
|
|
21
|
+
{
|
|
22
|
+
pkg: "@tarquinen/opencode-dcp",
|
|
23
|
+
desc: "Dynamic Context Pruning — strips obsolete tool outputs so long resolver loops cost fewer tokens",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
pkg: "@slkiser/opencode-quota",
|
|
27
|
+
desc: "Live token/quota usage indicator — supports GLM coding-plan, OpenAI Plus/Pro, Qwen, and more",
|
|
28
|
+
},
|
|
29
|
+
]
|
|
30
|
+
|
|
18
31
|
if (process.env.OPENCODE_RESOLVE_SKIP_POSTINSTALL === "1") {
|
|
19
32
|
process.exit(0)
|
|
20
33
|
}
|
|
@@ -24,6 +37,7 @@ console.log(`[${packageName}] installing v${pluginVersion}`)
|
|
|
24
37
|
|
|
25
38
|
try {
|
|
26
39
|
await registerPlugin()
|
|
40
|
+
await offerCompanionPlugins()
|
|
27
41
|
console.log(`[${packageName}] v${pluginVersion} install complete — restart OpenCode to load the plugin`)
|
|
28
42
|
} catch (error) {
|
|
29
43
|
console.warn(`[${packageName}] automatic OpenCode registration skipped: ${formatError(error)}`)
|
|
@@ -261,3 +275,107 @@ function isObject(value) {
|
|
|
261
275
|
function formatError(error) {
|
|
262
276
|
return error instanceof Error ? error.message : String(error)
|
|
263
277
|
}
|
|
278
|
+
|
|
279
|
+
async function offerCompanionPlugins() {
|
|
280
|
+
if (process.env.OPENCODE_RESOLVE_SKIP_COMPANIONS === "1") return
|
|
281
|
+
|
|
282
|
+
const config = await readOpenCodeConfig()
|
|
283
|
+
const existing = collectPluginBaseNames(config.plugin ?? [])
|
|
284
|
+
const missing = COMPANION_PLUGINS.filter((c) => !existing.has(c.pkg))
|
|
285
|
+
|
|
286
|
+
if (missing.length === 0) {
|
|
287
|
+
console.log(`[${packageName}] recommended companion plugins already present — skipping`)
|
|
288
|
+
return
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const isInteractive = Boolean(process.stdin.isTTY && process.stdout.isTTY)
|
|
292
|
+
|
|
293
|
+
if (!isInteractive) {
|
|
294
|
+
console.log(`[${packageName}] recommended companion plugins not detected:`)
|
|
295
|
+
for (const c of missing) {
|
|
296
|
+
console.log(` - ${c.pkg} — ${c.desc}`)
|
|
297
|
+
console.log(` install: opencode plugin ${c.pkg}@latest --global --force`)
|
|
298
|
+
}
|
|
299
|
+
return
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
303
|
+
try {
|
|
304
|
+
for (const c of missing) {
|
|
305
|
+
console.log("")
|
|
306
|
+
console.log(`[${packageName}] recommended companion: ${c.pkg}`)
|
|
307
|
+
console.log(` ${c.desc}`)
|
|
308
|
+
const raw = await rl.question(" install now? [Y/n] ")
|
|
309
|
+
const answer = raw.trim().toLowerCase()
|
|
310
|
+
const accepted = answer === "" || answer === "y" || answer === "yes"
|
|
311
|
+
if (!accepted) {
|
|
312
|
+
console.log(` skipped — install later via: opencode plugin ${c.pkg}@latest --global --force`)
|
|
313
|
+
continue
|
|
314
|
+
}
|
|
315
|
+
const installed = await installCompanion(c.pkg)
|
|
316
|
+
if (!installed) {
|
|
317
|
+
console.warn(` ${c.pkg} install command failed — leave plugin list untouched, retry manually`)
|
|
318
|
+
continue
|
|
319
|
+
}
|
|
320
|
+
await addCompanionToOpenCodeConfig(`${c.pkg}@latest`)
|
|
321
|
+
console.log(` ${c.pkg} cached and registered — restart OpenCode to activate`)
|
|
322
|
+
}
|
|
323
|
+
} finally {
|
|
324
|
+
rl.close()
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function collectPluginBaseNames(plugins) {
|
|
329
|
+
const names = new Set()
|
|
330
|
+
for (const entry of plugins) {
|
|
331
|
+
const raw = typeof entry === "string"
|
|
332
|
+
? entry
|
|
333
|
+
: Array.isArray(entry) && typeof entry[0] === "string"
|
|
334
|
+
? entry[0]
|
|
335
|
+
: null
|
|
336
|
+
if (!raw) continue
|
|
337
|
+
names.add(stripVersionSuffix(raw))
|
|
338
|
+
}
|
|
339
|
+
return names
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function stripVersionSuffix(name) {
|
|
343
|
+
if (name.startsWith("@")) {
|
|
344
|
+
const slashIndex = name.indexOf("/")
|
|
345
|
+
if (slashIndex === -1) return name
|
|
346
|
+
const scope = name.slice(0, slashIndex)
|
|
347
|
+
const rest = name.slice(slashIndex + 1)
|
|
348
|
+
return `${scope}/${rest.split("@")[0]}`
|
|
349
|
+
}
|
|
350
|
+
return name.split("@")[0]
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async function installCompanion(pkg) {
|
|
354
|
+
return new Promise((resolveSpawn) => {
|
|
355
|
+
const child = spawn("opencode", ["plugin", `${pkg}@latest`, "--global", "--force"], {
|
|
356
|
+
stdio: "inherit",
|
|
357
|
+
})
|
|
358
|
+
child.on("exit", (code) => resolveSpawn(code === 0))
|
|
359
|
+
child.on("error", () => resolveSpawn(false))
|
|
360
|
+
})
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function addCompanionToOpenCodeConfig(pluginEntry) {
|
|
364
|
+
const config = await readOpenCodeConfig()
|
|
365
|
+
config.plugin ??= []
|
|
366
|
+
if (!Array.isArray(config.plugin)) {
|
|
367
|
+
throw new Error(`${opencodeConfigPath}.plugin must be an array`)
|
|
368
|
+
}
|
|
369
|
+
const baseName = stripVersionSuffix(pluginEntry)
|
|
370
|
+
const alreadyPresent = config.plugin.some((entry) => {
|
|
371
|
+
const raw = typeof entry === "string"
|
|
372
|
+
? entry
|
|
373
|
+
: Array.isArray(entry) && typeof entry[0] === "string"
|
|
374
|
+
? entry[0]
|
|
375
|
+
: null
|
|
376
|
+
return raw !== null && stripVersionSuffix(raw) === baseName
|
|
377
|
+
})
|
|
378
|
+
if (alreadyPresent) return
|
|
379
|
+
config.plugin.push(pluginEntry)
|
|
380
|
+
await writeFile(opencodeConfigPath, `${JSON.stringify(config, null, 2)}\n`)
|
|
381
|
+
}
|