pluidr 0.6.1 → 0.7.0
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.md +32 -24
- package/package.json +1 -1
- package/src/cli/commands/doctor.js +8 -6
- package/src/cli/commands/init.js +1 -1
- package/src/cli/commands/launch.js +44 -0
- package/src/cli/commands/uninstall.js +2 -6
- package/src/cli/commands/update.js +3 -19
- package/src/cli/index.js +16 -12
- package/src/cli/wizard/selectModelTier.js +3 -2
- package/src/core/backup.js +3 -1
- package/src/core/configBuilder.test.js +48 -2
- package/src/core/version.js +8 -0
- package/src/core/versionCheck.js +35 -0
- package/src/core/versionCheck.test.js +24 -0
- package/src/templates/model-defaults.json +27 -9
package/README.md
CHANGED
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
## What is Pluidr?
|
|
12
12
|
|
|
13
|
-
Pluidr installs
|
|
13
|
+
Pluidr installs **3 primary agents + 14 subagents** into OpenCode — each primary agent owns its exclusive subagents, scoped permissions, and enforced workflow rules. No shared subagents. No ad-hoc delegation.
|
|
14
14
|
|
|
15
|
-
| Primary Agent | Purpose | Subagents |
|
|
16
|
-
|
|
17
|
-
| **Composer** | Feature work — Explore → Plan → Build | Researcher, Plan-Writer, Plan-Checker, Coder, Tester, Reviewer, Compose-Reporter |
|
|
18
|
-
| **Debugger** | Bug investigation — Investigate → Fix → Report | Inspector, Fixer, Debug-Reporter |
|
|
19
|
-
| **Prober** | Security audit — Trace → Patch → Audit | Tracer, Patcher, Auditor, Probe-Reporter |
|
|
15
|
+
| Primary Agent | Model Tier | Purpose | Subagents |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| **Composer** | 🧠 Reasoning | Feature work — Explore → Plan → Build | Researcher, Plan-Writer, Plan-Checker, Coder, Tester, Reviewer, Compose-Reporter |
|
|
18
|
+
| **Debugger** | 🧠 Reasoning | Bug investigation — Investigate → Fix → Report | Inspector, Fixer, Debug-Reporter |
|
|
19
|
+
| **Prober** | 🧠 Reasoning | Security audit — Trace → Patch → Audit | Tracer, Patcher, Auditor, Probe-Reporter |
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
@@ -231,22 +231,22 @@ You trigger a security audit
|
|
|
231
231
|
|
|
232
232
|
Each subagent belongs to exactly one primary agent and cannot be invoked by anyone else:
|
|
233
233
|
|
|
234
|
-
| Primary | Phase | Subagent | Role |
|
|
235
|
-
|
|
236
|
-
| Composer | EXPLORE | Researcher | Fact-finding: confirmed_facts / inferred_facts / unknowns / risks |
|
|
237
|
-
| Composer | PLAN | Plan-Writer | **Formatter** — writes PRD to `docs/plans/`, missing input = TBD |
|
|
238
|
-
| Composer | PLAN | Plan-Checker | **Gate** — PASS/FAIL + gap list only, no suggestions |
|
|
239
|
-
| Composer | BUILD | Coder | Implements code from PRD |
|
|
240
|
-
| Composer | BUILD | Tester | PASS/FAIL/BLOCKED + coverage gaps only |
|
|
241
|
-
| Composer | BUILD | Reviewer | **Gate** — PASS/FAIL + gap list only, no suggestions |
|
|
242
|
-
| Composer | BUILD | Compose-Reporter | **Formatter** — completion report to `docs/reports/` |
|
|
243
|
-
| Debugger |
|
|
244
|
-
| Debugger |
|
|
245
|
-
| Debugger |
|
|
246
|
-
| Prober | TRACE | Tracer | WSTG recon + vuln path tracing, no remedies |
|
|
247
|
-
| Prober | PATCH | Patcher | Minimal, security-targeted fix (Ponytail, max 10 lines) |
|
|
248
|
-
| Prober | AUDIT | Auditor | **Gate** — PASS/FAIL + Gap List + BLOAT List only |
|
|
249
|
-
| Prober | AUDIT | Probe-Reporter | **Formatter** — security audit report to `docs/reports/` |
|
|
234
|
+
| Primary | Phase | Subagent | Model Tier | Role |
|
|
235
|
+
|---------|-------|----------|------------|------|
|
|
236
|
+
| Composer | EXPLORE | Researcher | 🧠 Reasoning | Fact-finding: confirmed_facts / inferred_facts / unknowns / risks |
|
|
237
|
+
| Composer | PLAN | Plan-Writer | ⚡ Fast | **Formatter** — writes PRD to `docs/plans/`, missing input = TBD |
|
|
238
|
+
| Composer | PLAN | Plan-Checker | 🧠 Reasoning | **Gate** — PASS/FAIL + gap list only, no suggestions |
|
|
239
|
+
| Composer | BUILD | Coder | ⚡ Fast | Implements code from PRD |
|
|
240
|
+
| Composer | BUILD | Tester | 🎯 Precision | PASS/FAIL/BLOCKED + coverage gaps only |
|
|
241
|
+
| Composer | BUILD | Reviewer | 🎯 Precision | **Gate** — PASS/FAIL + gap list only, no suggestions |
|
|
242
|
+
| Composer | BUILD | Compose-Reporter | ⚡ Fast | **Formatter** — completion report to `docs/reports/` |
|
|
243
|
+
| Debugger | INVESTIGATE | Inspector | 🧠 Reasoning | Brooks-Lint RCA (Iron Law + 6 decay risks + 4 review modes) |
|
|
244
|
+
| Debugger | FIX | Fixer | ⚡ Fast | Minimal, root-cause-targeted fix |
|
|
245
|
+
| Debugger | REPORT | Debug-Reporter | ⚡ Fast | **Formatter** — Iron Law diagnosis report to `docs/reports/` |
|
|
246
|
+
| Prober | TRACE | Tracer | 🎯 Precision | WSTG recon + vuln path tracing, no remedies |
|
|
247
|
+
| Prober | PATCH | Patcher | ⚡ Fast | Minimal, security-targeted fix (Ponytail, max 10 lines) |
|
|
248
|
+
| Prober | AUDIT | Auditor | 🎯 Precision | **Gate** — PASS/FAIL + Gap List + BLOAT List only |
|
|
249
|
+
| Prober | AUDIT | Probe-Reporter | ⚡ Fast | **Formatter** — security audit report to `docs/reports/` |
|
|
250
250
|
|
|
251
251
|
---
|
|
252
252
|
|
|
@@ -301,9 +301,17 @@ npx pluidr init
|
|
|
301
301
|
|
|
302
302
|
## Usage
|
|
303
303
|
|
|
304
|
+
### `pluidr`
|
|
305
|
+
|
|
306
|
+
The everyday command. Runs three steps in sequence:
|
|
307
|
+
|
|
308
|
+
1. **Update check** — compares local version against npm registry; prompts to `npm install -g pluidr` if a new version is available
|
|
309
|
+
2. **Doctor check** — verifies installation health; prompts to repair with `pluidr init` if any checks fail
|
|
310
|
+
3. **Launch** — spawns `opencode`
|
|
311
|
+
|
|
304
312
|
### `pluidr init`
|
|
305
313
|
|
|
306
|
-
Prompts you to select models for
|
|
314
|
+
Prompts you to select models for three agent tiers (reasoning, precision, and fast), then:
|
|
307
315
|
|
|
308
316
|
- Asks whether to install the pluidr-squeeze plugin (can decline)
|
|
309
317
|
- Builds a complete `opencode.jsonc` config with the chosen models injected into the right agents
|
|
@@ -328,7 +336,7 @@ Exits with code `0` if all pass, `1` if any fail.
|
|
|
328
336
|
|
|
329
337
|
### `pluidr update`
|
|
330
338
|
|
|
331
|
-
|
|
339
|
+
Checks the npm registry for a newer version of pluidr and prompts to install it.
|
|
332
340
|
|
|
333
341
|
### `pluidr uninstall`
|
|
334
342
|
|
package/package.json
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync } from "node:fs"
|
|
2
|
-
import { join
|
|
3
|
-
import { fileURLToPath } from "node:url"
|
|
4
|
-
import { execFileSync } from "node:child_process"
|
|
2
|
+
import { join } from "node:path"
|
|
5
3
|
import { getConfigDir, getConfigPath, getPromptsDir } from "../../core/paths.js"
|
|
6
4
|
import { findSqueezePath } from "../../core/squeezeInstaller.js"
|
|
7
5
|
|
|
8
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
9
6
|
const EXPECTED_PROMPT_COUNT = 18
|
|
10
7
|
const PLUGIN_FILES = ["pluidr-flow.js", "pluidr-squeeze.js"]
|
|
11
8
|
|
|
12
|
-
export
|
|
9
|
+
export function collectChecks() {
|
|
13
10
|
const configDir = getConfigDir()
|
|
14
11
|
const configPath = getConfigPath()
|
|
15
12
|
const promptsDir = getPromptsDir()
|
|
@@ -79,8 +76,13 @@ export async function runDoctor() {
|
|
|
79
76
|
}
|
|
80
77
|
checks.push({ component: "config JSON validity", pass: configValid })
|
|
81
78
|
|
|
82
|
-
|
|
79
|
+
return checks
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function runDoctor() {
|
|
83
|
+
const checks = collectChecks()
|
|
83
84
|
const allPass = checks.every((c) => c.pass)
|
|
85
|
+
|
|
84
86
|
console.log("\nPluidr Doctor — Installation Health Check\n")
|
|
85
87
|
for (const c of checks) {
|
|
86
88
|
const icon = c.pass ? "\u2713" : "\u2717"
|
package/src/cli/commands/init.js
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { spawn } from "node:child_process"
|
|
2
|
+
import { confirm, isCancel } from "@clack/prompts"
|
|
3
|
+
import { checkAndPromptUpdate } from "../../core/versionCheck.js"
|
|
4
|
+
import { version } from "../../core/version.js"
|
|
5
|
+
import { collectChecks } from "./doctor.js"
|
|
6
|
+
import { runInit } from "./init.js"
|
|
7
|
+
|
|
8
|
+
export async function runLaunch() {
|
|
9
|
+
// 1. Update check
|
|
10
|
+
await checkAndPromptUpdate(version)
|
|
11
|
+
|
|
12
|
+
// 2. Doctor check
|
|
13
|
+
process.stdout.write("Running doctor... ")
|
|
14
|
+
const checks = collectChecks()
|
|
15
|
+
const allPass = checks.every((c) => c.pass)
|
|
16
|
+
|
|
17
|
+
if (allPass) {
|
|
18
|
+
console.log("✓ All checks passed")
|
|
19
|
+
} else {
|
|
20
|
+
const failed = checks.filter((c) => !c.pass)
|
|
21
|
+
console.log(`✗ ${failed.length} check${failed.length > 1 ? "s" : ""} failed`)
|
|
22
|
+
for (const c of failed) {
|
|
23
|
+
const detail = c.detail ? ` (${c.detail})` : ""
|
|
24
|
+
console.log(` ✗ ${c.component}${detail}`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const answer = await confirm({ message: "Repair with pluidr init?", initialValue: true })
|
|
28
|
+
if (!isCancel(answer) && answer) {
|
|
29
|
+
await runInit()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 3. Launch opencode
|
|
34
|
+
console.log("Launching opencode...")
|
|
35
|
+
const child = spawn("opencode", [], { stdio: "inherit" })
|
|
36
|
+
child.on("error", (err) => {
|
|
37
|
+
if (err.code === "ENOENT") {
|
|
38
|
+
console.error("Error: opencode not found on PATH. Install it from https://opencode.ai")
|
|
39
|
+
} else {
|
|
40
|
+
console.error(`Error launching opencode: ${err.message}`)
|
|
41
|
+
}
|
|
42
|
+
process.exit(1)
|
|
43
|
+
})
|
|
44
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, copyFileSync, rmSync, readdirSync } from "node:fs"
|
|
2
|
-
import { join
|
|
2
|
+
import { join } from "node:path"
|
|
3
3
|
import { getConfigDir, getConfigPath } from "../../core/paths.js"
|
|
4
4
|
|
|
5
5
|
function findLatestBackup() {
|
|
@@ -27,11 +27,7 @@ export async function runUninstall() {
|
|
|
27
27
|
|
|
28
28
|
// Restore latest backup
|
|
29
29
|
const latestBackup = findLatestBackup()
|
|
30
|
-
if (latestBackup
|
|
31
|
-
copyFileSync(latestBackup, configPath)
|
|
32
|
-
summary.restored = latestBackup
|
|
33
|
-
} else if (latestBackup) {
|
|
34
|
-
// Config doesn't exist but backup does — restore anyway
|
|
30
|
+
if (latestBackup) {
|
|
35
31
|
copyFileSync(latestBackup, configPath)
|
|
36
32
|
summary.restored = latestBackup
|
|
37
33
|
}
|
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { getConfigPath } from "../../core/paths.js"
|
|
4
|
-
import { runInit } from "./init.js"
|
|
1
|
+
import { checkAndPromptUpdate } from "../../core/versionCheck.js"
|
|
2
|
+
import { version } from "../../core/version.js"
|
|
5
3
|
|
|
6
4
|
export async function runUpdate() {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if (existsSync(configPath)) {
|
|
10
|
-
const answer = await confirm({
|
|
11
|
-
message: "Existing Pluidr config found. Overwrite? (Y/n)",
|
|
12
|
-
initialValue: true,
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
if (isCancel(answer) || !answer) {
|
|
16
|
-
console.log("Update cancelled.")
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
await runInit()
|
|
5
|
+
await checkAndPromptUpdate(version)
|
|
22
6
|
}
|
package/src/cli/index.js
CHANGED
|
@@ -5,30 +5,33 @@ import { runInit } from "./commands/init.js"
|
|
|
5
5
|
import { runUninstall } from "./commands/uninstall.js"
|
|
6
6
|
import { runUpdate } from "./commands/update.js"
|
|
7
7
|
import { runDoctor } from "./commands/doctor.js"
|
|
8
|
+
import { runLaunch } from "./commands/launch.js"
|
|
9
|
+
import { version } from "../core/version.js"
|
|
8
10
|
|
|
9
|
-
const
|
|
10
|
-
const pkg = JSON.parse(readFileSync(resolve(__dirname, "../../package.json"), "utf-8"))
|
|
11
|
+
const HELP = `Usage: pluidr [command]
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
uninstall
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
--version, -v Print version number
|
|
20
|
-
--help, -h Show this help message`
|
|
13
|
+
pluidr Check for updates, run doctor, launch opencode
|
|
14
|
+
pluidr init Set up OpenCode with Pluidr's 17-agent pipeline
|
|
15
|
+
pluidr update Check for pluidr updates
|
|
16
|
+
pluidr doctor Verify installation health
|
|
17
|
+
pluidr uninstall Remove Pluidr artifacts and restore previous config
|
|
18
|
+
--version, -v Print version number
|
|
19
|
+
--help, -h Show this help message`
|
|
21
20
|
|
|
22
21
|
export function run(argv) {
|
|
23
22
|
const cmd = argv[2]
|
|
24
23
|
|
|
24
|
+
if (!cmd) {
|
|
25
|
+
return runLaunch()
|
|
26
|
+
}
|
|
27
|
+
|
|
25
28
|
if (cmd === "--help" || cmd === "-h") {
|
|
26
29
|
console.log(HELP)
|
|
27
30
|
return
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
if (cmd === "--version" || cmd === "-v") {
|
|
31
|
-
console.log(
|
|
34
|
+
console.log(version)
|
|
32
35
|
return
|
|
33
36
|
}
|
|
34
37
|
|
|
@@ -48,6 +51,7 @@ export function run(argv) {
|
|
|
48
51
|
return runDoctor()
|
|
49
52
|
}
|
|
50
53
|
|
|
54
|
+
console.error(`Unknown command: ${cmd}\n`)
|
|
51
55
|
console.log(HELP)
|
|
52
56
|
process.exit(1)
|
|
53
57
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { select, text, isCancel } from "@clack/prompts"
|
|
2
2
|
|
|
3
3
|
const TIER_LABELS = {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
reasoning: "Model for reasoning task (orchestrators + deep analysis)",
|
|
5
|
+
precision: "Model for precision task (quality gates + verification)",
|
|
6
|
+
fast: "Model for fast/cheap task (builders + formatters)",
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
export async function selectModelTier(modelDefaults) {
|
package/src/core/backup.js
CHANGED
|
@@ -20,7 +20,9 @@ function rotateBackups(configPath) {
|
|
|
20
20
|
.filter((f) => f.startsWith(baseName + "."))
|
|
21
21
|
.map((f) => join(dir, f))
|
|
22
22
|
.sort()
|
|
23
|
-
} catch {
|
|
23
|
+
} catch (err) {
|
|
24
|
+
if (err.code !== "ENOENT") throw err
|
|
25
|
+
}
|
|
24
26
|
|
|
25
27
|
while (backups.length >= MAX_BACKUPS) {
|
|
26
28
|
const oldest = backups.shift()
|
|
@@ -12,15 +12,21 @@ describe("configBuilder", () => {
|
|
|
12
12
|
const configJson = {
|
|
13
13
|
agent: {
|
|
14
14
|
composer: {},
|
|
15
|
+
reviewer: {},
|
|
15
16
|
coder: {},
|
|
16
17
|
},
|
|
17
18
|
}
|
|
18
19
|
const defaultsJson = {
|
|
19
|
-
|
|
20
|
+
reasoning: {
|
|
20
21
|
provider: "test",
|
|
21
22
|
model: "big-model",
|
|
22
23
|
agents: ["composer"],
|
|
23
24
|
},
|
|
25
|
+
precision: {
|
|
26
|
+
provider: "test",
|
|
27
|
+
model: "mid-model",
|
|
28
|
+
agents: ["reviewer"],
|
|
29
|
+
},
|
|
24
30
|
fast: {
|
|
25
31
|
provider: "test",
|
|
26
32
|
model: "small-model",
|
|
@@ -33,15 +39,55 @@ describe("configBuilder", () => {
|
|
|
33
39
|
|
|
34
40
|
const result = buildConfig(
|
|
35
41
|
{
|
|
36
|
-
|
|
42
|
+
reasoning: "test/big-model",
|
|
43
|
+
precision: "test/mid-model",
|
|
37
44
|
fast: "test/small-model",
|
|
38
45
|
},
|
|
39
46
|
tmpDir,
|
|
40
47
|
)
|
|
41
48
|
|
|
42
49
|
assert.strictEqual(result.agent.composer.model, "test/big-model")
|
|
50
|
+
assert.strictEqual(result.agent.reviewer.model, "test/mid-model")
|
|
43
51
|
assert.strictEqual(result.agent.coder.model, "test/small-model")
|
|
44
52
|
|
|
45
53
|
rmSync(tmpDir, { recursive: true })
|
|
46
54
|
})
|
|
55
|
+
|
|
56
|
+
it("throws an error if a defaulted agent is missing from config template", () => {
|
|
57
|
+
const tmpDir = mkdtempSync(join(tmpdir(), "configbuilder-test-err-"))
|
|
58
|
+
|
|
59
|
+
const configJson = {
|
|
60
|
+
agent: {
|
|
61
|
+
// missing 'composer'
|
|
62
|
+
coder: {},
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
const defaultsJson = {
|
|
66
|
+
reasoning: {
|
|
67
|
+
provider: "test",
|
|
68
|
+
model: "big-model",
|
|
69
|
+
agents: ["composer"],
|
|
70
|
+
},
|
|
71
|
+
fast: {
|
|
72
|
+
provider: "test",
|
|
73
|
+
model: "small-model",
|
|
74
|
+
agents: ["coder"],
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
writeFileSync(join(tmpDir, "opencode.config.json"), JSON.stringify(configJson), "utf-8")
|
|
79
|
+
writeFileSync(join(tmpDir, "model-defaults.json"), JSON.stringify(defaultsJson), "utf-8")
|
|
80
|
+
|
|
81
|
+
assert.throws(() => {
|
|
82
|
+
buildConfig(
|
|
83
|
+
{
|
|
84
|
+
reasoning: "test/big-model",
|
|
85
|
+
fast: "test/small-model",
|
|
86
|
+
},
|
|
87
|
+
tmpDir,
|
|
88
|
+
)
|
|
89
|
+
}, /Template validation failed: agent\(s\) not found/)
|
|
90
|
+
|
|
91
|
+
rmSync(tmpDir, { recursive: true })
|
|
92
|
+
})
|
|
47
93
|
})
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs"
|
|
2
|
+
import { resolve, dirname } from "node:path"
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
4
|
+
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
6
|
+
export const version = JSON.parse(
|
|
7
|
+
readFileSync(resolve(__dirname, "../../package.json"), "utf-8")
|
|
8
|
+
).version
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process"
|
|
2
|
+
import { confirm, isCancel } from "@clack/prompts"
|
|
3
|
+
|
|
4
|
+
export function fetchLatestVersion() {
|
|
5
|
+
try {
|
|
6
|
+
return execFileSync("npm", ["view", "pluidr", "version"], { stdio: "pipe" })
|
|
7
|
+
.toString()
|
|
8
|
+
.trim()
|
|
9
|
+
} catch {
|
|
10
|
+
return null
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function checkAndPromptUpdate(currentVersion) {
|
|
15
|
+
process.stdout.write("Checking for updates... ")
|
|
16
|
+
|
|
17
|
+
const latest = fetchLatestVersion()
|
|
18
|
+
if (!latest) {
|
|
19
|
+
console.log("⚠ Could not reach npm registry — skipping")
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (latest === currentVersion) {
|
|
24
|
+
console.log(`✓ Up to date (${currentVersion})`)
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log(`⚠ pluidr ${latest} available (you have ${currentVersion})`)
|
|
29
|
+
|
|
30
|
+
const answer = await confirm({ message: "Update now?", initialValue: true })
|
|
31
|
+
if (isCancel(answer) || !answer) return false
|
|
32
|
+
|
|
33
|
+
execFileSync("npm", ["install", "-g", "pluidr"], { stdio: "inherit" })
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, it } from "node:test"
|
|
2
|
+
import assert from "node:assert"
|
|
3
|
+
import { execFileSync } from "node:child_process"
|
|
4
|
+
import { fetchLatestVersion } from "./versionCheck.js"
|
|
5
|
+
|
|
6
|
+
describe("fetchLatestVersion", () => {
|
|
7
|
+
it("returns a string or null", () => {
|
|
8
|
+
// Either reaches npm registry (string) or fails gracefully (null)
|
|
9
|
+
const result = fetchLatestVersion()
|
|
10
|
+
assert.ok(result === null || typeof result === "string")
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it("returns null when npm command throws", () => {
|
|
14
|
+
// Verify the try/catch in fetchLatestVersion swallows errors
|
|
15
|
+
let caught = null
|
|
16
|
+
try {
|
|
17
|
+
execFileSync("npm", ["view", "__nonexistent_pkg_xyz_pluidr__", "version"], { stdio: "pipe" })
|
|
18
|
+
} catch (err) {
|
|
19
|
+
caught = err
|
|
20
|
+
}
|
|
21
|
+
// npm throws on unknown package — fetchLatestVersion handles this and returns null
|
|
22
|
+
assert.ok(caught instanceof Error, "npm should throw for unknown package")
|
|
23
|
+
})
|
|
24
|
+
})
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"reasoning": {
|
|
3
3
|
"provider": "opencode",
|
|
4
4
|
"model": "big-pickle",
|
|
5
5
|
"agents": [
|
|
6
6
|
"composer",
|
|
7
7
|
"debugger",
|
|
8
|
+
"prober",
|
|
8
9
|
"researcher",
|
|
9
10
|
"plan-checker",
|
|
10
|
-
"inspector"
|
|
11
|
-
"prober"
|
|
11
|
+
"inspector"
|
|
12
12
|
],
|
|
13
13
|
|
|
14
14
|
"recommended": [
|
|
@@ -23,20 +23,38 @@
|
|
|
23
23
|
{ "value": "openai/gpt-5.5", "label": "openai/gpt-5.5" }
|
|
24
24
|
]
|
|
25
25
|
},
|
|
26
|
+
"precision": {
|
|
27
|
+
"provider": "opencode",
|
|
28
|
+
"model": "big-pickle",
|
|
29
|
+
"agents": [
|
|
30
|
+
"tester",
|
|
31
|
+
"reviewer",
|
|
32
|
+
"auditor",
|
|
33
|
+
"tracer"
|
|
34
|
+
],
|
|
35
|
+
|
|
36
|
+
"recommended": [
|
|
37
|
+
{
|
|
38
|
+
"value": "opencode/big-pickle",
|
|
39
|
+
"label": "default (opencode/big-pickle)"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"value": "anthropic/claude-sonnet-4-5",
|
|
43
|
+
"label": "anthropic/claude-sonnet-4-5"
|
|
44
|
+
},
|
|
45
|
+
{ "value": "openai/gpt-5.1", "label": "openai/gpt-5.1" }
|
|
46
|
+
]
|
|
47
|
+
},
|
|
26
48
|
"fast": {
|
|
27
49
|
"provider": "opencode",
|
|
28
50
|
"model": "deepseek-v4-flash-free",
|
|
29
51
|
"agents": [
|
|
30
52
|
"coder",
|
|
31
|
-
"tester",
|
|
32
|
-
"reviewer",
|
|
33
|
-
"compose-reporter",
|
|
34
53
|
"fixer",
|
|
54
|
+
"patcher",
|
|
35
55
|
"plan-writer",
|
|
56
|
+
"compose-reporter",
|
|
36
57
|
"debug-reporter",
|
|
37
|
-
"tracer",
|
|
38
|
-
"patcher",
|
|
39
|
-
"auditor",
|
|
40
58
|
"probe-reporter"
|
|
41
59
|
],
|
|
42
60
|
|