Skip to content

Commit b85bdcd

Browse files
ralyodioclaude
andcommitted
fix(engines): /agents opencode|privacycode opens a session, not a list dump
`agentsView: ["agent", "list"]` made `/agents opencode` run `opencode agent list` — a machine-readable, one-shot command that prints every agent's permission config and exits straight back to the pit. On opencode 1.18.x that output is now raw JSON, so it reads as a wall of errors followed by a crash. `/agents` promises to hand the terminal to a live session, so neither engine gets an agentsView: both fall through to `--auto`, which opens the TUI (where `tab` reaches the agent list anyway). Also declare binDirs for both. Their installers only append ~/.opencode/bin and ~/.privacycode/bin to a shell rc, so the moshcode process that ran the installer reports them missing until the next shell — the same bridge kimi already had. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent df83275 commit b85bdcd

3 files changed

Lines changed: 27 additions & 16 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ use this only in an isolated container, VM, or workspace you trust:
7676

7777
```sh
7878
moshcode agents claude # claude agents --dangerously-skip-permissions (agent view)
79-
moshcode agents opencode # opencode agent list (agent view)
80-
moshcode agents privacycode # privacycode agent list (agent view)
79+
moshcode agents opencode # opencode --auto (autonomous)
80+
moshcode agents privacycode # privacycode --auto (autonomous)
8181
moshcode agents codex # codex --dangerously-bypass-approvals-and-sandbox (autonomous)
8282
moshcode agents gemini # gemini --approval-mode=yolo (autonomous)
8383
moshcode agents kimi # kimi --yolo (autonomous)

src/engines.mjs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
//
66
// `agentsView` (optional) is the exact argv that opens the engine's native
77
// agent list/view — used by `/agents <name>` when the engine actually has one
8-
// (claude, opencode). It's the FULL leading args (subcommand + any flags that
9-
// subcommand accepts), because not every agents-subcommand takes the engine's
10-
// bypass flag (e.g. `opencode agent list` takes none). Engines without an
11-
// `agentsView` fall back to `agentArgs` — an autonomous session with native
12-
// approvals bypassed/auto-approved.
8+
// (currently claude). It's the FULL leading args (subcommand + any flags that
9+
// subcommand accepts). Engines without an `agentsView` fall back to
10+
// `agentArgs` — an autonomous session with native approvals
11+
// bypassed/auto-approved. Do not use a machine-readable, one-shot list command
12+
// as an agents view: `/agents` promises to hand the terminal to a live session.
1313
import { spawn } from "node:child_process";
1414
import { existsSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
1515
import { homedir, tmpdir } from "node:os";
@@ -22,17 +22,19 @@ export const ENGINES = {
2222
desc: "opencode — the open-source coding agent (SST/anomalyco)",
2323
bin: "opencode",
2424
agentArgs: ["--auto"],
25-
agentsView: ["agent", "list"], // `opencode agent list` — lists agents; the `agent` subcommand takes no bypass flag
2625
install: { cmd: "bash", args: ["-c", "curl -fsSL https://opencode.ai/install | bash"] },
2726
upgrade: { cmd: "opencode", args: ["upgrade"] },
27+
// The installer appends this directory to a shell profile. The moshcode
28+
// process that ran it cannot see that PATH change, so search it directly.
29+
binDirs: [path.join(homedir(), ".opencode", "bin")],
2830
},
2931
privacycode: {
3032
desc: "privacycode — privacy-first coding agent (profullstack)",
3133
bin: "privacycode",
3234
// An opencode derivative, so it speaks the same flags/subcommands.
3335
agentArgs: ["--auto"],
34-
agentsView: ["agent", "list"],
3536
install: { cmd: "sh", args: ["-c", "curl -fsSL https://getprivacycode.com/install | sh"] },
37+
binDirs: [path.join(homedir(), ".privacycode", "bin")],
3638
// Deliberately no native updater. `privacycode upgrade` is opencode's, and
3739
// it works out how to update itself by recognising where it was installed —
3840
// it knows opencode's own locations, not this fork's ~/.privacycode/bin. It
@@ -263,9 +265,10 @@ export function pickAiEngine(preferred) {
263265

264266
/** Engine entries annotated with install status. */
265267
export function engineStatus() {
266-
// Search each engine's own install dir as well as PATH — kimi's installer only
267-
// adds ~/.kimi-code/bin to your shell rc, so PATH alone reports it missing in
268-
// the very session that installed it. (Inert for engines without binDirs.)
268+
// Search each engine's own install dir as well as PATH — several curl-based
269+
// installers only add their bin directory to a shell rc, so PATH alone
270+
// reports them missing in the very session that installed them. (Inert for
271+
// engines without binDirs.)
269272
return Object.entries(ENGINES).map(([key, e]) => ({ key, ...e, installed: isInstalled(e.bin, e.binDirs) }));
270273
}
271274

test/engines.test.mjs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
readFileSync,
99
writeFileSync,
1010
} from "node:fs";
11-
import { tmpdir } from "node:os";
11+
import { homedir, tmpdir } from "node:os";
1212
import path from "node:path";
1313
import { fileURLToPath } from "node:url";
1414
import { spawn } from "node:child_process";
@@ -31,10 +31,12 @@ const EXPECTED_AGENT_ARGS = {
3131
};
3232

3333
// What an agent-mode launch actually runs (agentLaunchArgs): the engine's native
34-
// agents-view invocation where it has one, else its autonomous bypass flags.
34+
// interactive agents view where it has one, else its autonomous bypass flags.
35+
// `opencode agent list` and `privacycode agent list` print data and exit, so
36+
// those are not interactive views and must not be used here.
3537
const EXPECTED_LAUNCH_ARGS = {
36-
opencode: ["agent", "list"],
37-
privacycode: ["agent", "list"],
38+
opencode: ["--auto"],
39+
privacycode: ["--auto"],
3840
claude: ["agents", "--dangerously-skip-permissions"],
3941
codex: ["--dangerously-bypass-approvals-and-sandbox"],
4042
gemini: ["--approval-mode=yolo"],
@@ -219,6 +221,12 @@ test("executable lookup searches a tool's own install dir when PATH misses it",
219221
assert.equal(r.code, 0);
220222
});
221223

224+
test("curl-installed engines declare their installer bin directories", () => {
225+
assert.deepEqual(ENGINES.opencode.binDirs, [path.join(homedir(), ".opencode", "bin")]);
226+
assert.deepEqual(ENGINES.privacycode.binDirs, [path.join(homedir(), ".privacycode", "bin")]);
227+
assert.deepEqual(ENGINES.kimi.binDirs, [path.join(homedir(), ".kimi-code", "bin")]);
228+
});
229+
222230
test("PATH still wins over a tool's install dir", async () => {
223231
// Two copies, different exit codes: whichever one runs identifies itself.
224232
const pathDir = tempDir("moshcode-bindirs-path-");

0 commit comments

Comments
 (0)