pi-mono/packages/coding-agent/test/experimental-cli-command.test.ts
Armin Ronacher 4bd9438e5d feat(coding-agent): run experimental client as direct chat
Replace raw Harness and Chat service exposure with AgentController and replicated lane state. Start directly in new or resumed sessions and persist model selection.
2026-08-27 17:19:51 +02:00

269 lines
8.4 KiB
TypeScript

import { describe, expect, test } from "vitest";
import { cli } from "../src/cli/experimental/cli.ts";
describe("experimental CLI commands", () => {
test("selects pi mode and parses existing CLI arguments", () => {
expect(
cli.parse([
"--provider",
"anthropic",
"--model",
"claude-sonnet",
"--thinking",
"high",
"inspect",
"the project",
]),
).toMatchObject({
ok: true,
command: {
command: "pi",
options: {
provider: "anthropic",
model: "claude-sonnet",
thinking: "high",
messages: ["inspect", "the project"],
},
},
});
});
test("parses a server listener", () => {
expect(cli.parse(["server", "--listen", "unix:///tmp/pi.sock"])).toEqual({
ok: true,
command: {
command: "server",
listen: [{ transport: "unix", path: "/tmp/pi.sock" }],
},
});
});
test("parses an experimental server ID", () => {
expect(cli.parse(["server", "--server-id", "00000000-0000-4000-8000-000000000001"])).toEqual({
ok: true,
command: {
command: "server",
serverId: "00000000-0000-4000-8000-000000000001",
},
});
});
test("parses an experimental server session directory", () => {
expect(cli.parse(["server", "--session-dir", "~/pi-sessions"])).toEqual({
ok: true,
command: {
command: "server",
sessionDir: "~/pi-sessions",
},
});
});
test("parses an experimental server model", () => {
expect(cli.parse(["server", "--provider", "anthropic", "--model", "claude-sonnet-4-5"])).toEqual({
ok: true,
command: {
command: "server",
provider: "anthropic",
model: "claude-sonnet-4-5",
},
});
});
test("parses a provider-qualified server model", () => {
expect(cli.parse(["server", "--model", "anthropic/claude-sonnet-4-5:high"])).toEqual({
ok: true,
command: {
command: "server",
model: "anthropic/claude-sonnet-4-5:high",
},
});
});
test("leaves experimental-looking existing option values with the existing parser", () => {
expect(cli.parse(["--system-prompt", "--listen", "unix:///tmp/pi.sock"])).toMatchObject({
ok: true,
command: {
command: "pi",
options: { systemPrompt: "--listen", messages: ["unix:///tmp/pi.sock"] },
},
});
});
test("stops parsing command options when existing CLI arguments begin", () => {
const result = cli.parse(["--model", "claude-sonnet", "--listen=unix:///tmp/second.sock"]);
expect(result).toMatchObject({
ok: true,
command: { command: "pi", options: { model: "claude-sonnet" } },
});
if (!result.ok || result.command.command !== "pi") return;
expect(result.command.listen).toBeUndefined();
expect(result.command.options.unknownFlags.get("listen")).toBe("unix:///tmp/second.sock");
});
test("parses a client transport address", () => {
expect(cli.parse(["client", "--connect", "unix:///tmp/pi.sock"])).toEqual({
ok: true,
command: {
command: "client",
connect: { transport: "unix", path: "/tmp/pi.sock" },
},
});
});
test.each([
["-c", "continue"],
["--continue", "continue"],
["-r", "resume"],
["--resume", "resume"],
] as const)("parses client Session selection %s", (option, property) => {
expect(cli.parse(["client", option])).toEqual({
ok: true,
command: { command: "client", [property]: true },
});
});
test("parses a client session ID", () => {
expect(cli.parse(["client", "--session-id", "demo-1"])).toEqual({
ok: true,
command: {
command: "client",
sessionId: "demo-1",
},
});
});
test("parses a one-shot client prompt without a session ID", () => {
expect(cli.parse(["client", "Explain this project"])).toEqual({
ok: true,
command: {
command: "client",
prompt: "Explain this project",
},
});
});
test("parses the model used for cold client activation", () => {
expect(cli.parse(["client", "--provider", "anthropic", "--model", "claude-sonnet-4-5"])).toEqual({
ok: true,
command: {
command: "client",
provider: "anthropic",
model: "claude-sonnet-4-5",
},
});
});
test("parses a provider-qualified model used for cold client activation", () => {
expect(cli.parse(["client", "--model", "anthropic/claude-sonnet-4-5:high"])).toEqual({
ok: true,
command: {
command: "client",
model: "anthropic/claude-sonnet-4-5:high",
},
});
});
test.each([
[["--auth-token", "secret"], { type: "token", token: "secret" }],
[["--auth-token-file", "/tmp/token"], { type: "file", path: "/tmp/token" }],
] as const)("parses authentication source %j", (argv, auth) => {
expect(cli.parse(argv)).toMatchObject({
ok: true,
command: { command: "pi", auth },
});
});
test.each([[[]], [["server"]], [["client"]]] as const)(
"permits omitted authentication for later environment/default resolution",
(argv) => {
const result = cli.parse(argv);
expect(result).toMatchObject({ ok: true, command: { command: argv[0] ?? "pi" } });
if (result.ok) expect(result.command.auth).toBeUndefined();
},
);
test("passes unknown options, file arguments, and the positional separator to the existing parser", () => {
const result = cli.parse(["--unknown", "@prompt.md", "--", "--listen", "unix:///tmp/pi.sock"]);
expect(result).toMatchObject({
ok: true,
command: {
command: "pi",
options: { fileArgs: ["prompt.md"], messages: ["--listen", "unix:///tmp/pi.sock"] },
},
});
if (!result.ok || result.command.command !== "pi") return;
expect(result.command.options.unknownFlags).toEqual(new Map([["unknown", true]]));
});
test.each([
[
["--listen", "unix:///tmp/pi.sock", "--listen", "unix:///tmp/pi-admin.sock"],
"--listen may only be specified once",
],
[
["--auth-token", "secret", "--auth-token-file", "/tmp/token"],
"--auth-token and --auth-token-file are mutually exclusive",
],
[["--auth-token", "first", "--auth-token", "second"], "--auth-token may only be specified once"],
[
["--auth-token-file", "/tmp/first", "--auth-token-file=/tmp/second"],
"--auth-token-file may only be specified once",
],
[["--listen", "/tmp/pi.sock"], 'Invalid --listen address "/tmp/pi.sock"'],
[["--listen", "ws://localhost:8080"], 'Unsupported --listen transport "ws:"'],
[["--listen", "unix://relative.sock"], "Unix transport address must not include an authority"],
[["--listen", "unix:///tmp/pi.sock?wrong=value"], 'Invalid --listen address "unix:///tmp/pi.sock?wrong=value"'],
[["--listen", "unix:///tmp/pi.sock#fragment"], 'Invalid --listen address "unix:///tmp/pi.sock#fragment"'],
[["--listen", "unix:/tmp/pi.sock"], 'Invalid --listen address "unix:/tmp/pi.sock"'],
[["--listen", "unix:///tmp/%00pi.sock"], 'Invalid --listen address "unix:///tmp/%00pi.sock"'],
[
["client", "--listen", "unix:///tmp/pi.sock"],
"The experimental client command does not support existing CLI options yet",
],
[
["server", "--connect", "unix:///tmp/pi.sock"],
"The experimental server command does not support existing CLI options yet",
],
[["client", "--connect", "ws://localhost:8080"], 'Unsupported --connect transport "ws:"'],
[["client", "--provider", "anthropic"], "--provider requires --model"],
[["client", "-c", "-r"], "--session-id, --continue, and --resume are mutually exclusive"],
[["server", "--provider", "anthropic"], "--provider requires --model"],
[["server", "--server-id", "not-a-uuid"], "Invalid --server-id"],
[["server", "--server-id"], "--server-id requires a value"],
[["server", "--session-dir"], "--session-dir requires a value"],
[
["server", "--session-dir", "/tmp/first", "--session-dir=/tmp/second"],
"--session-dir may only be specified once",
],
[["--listen"], "--listen requires a value"],
[["--connect="], "--connect is only valid for client mode"],
] as const)("rejects invalid experimental input %j", (argv, error) => {
const result = cli.parse(argv);
expect(result).toMatchObject({ ok: false });
if (!result.ok) expect(result.errors).toContainEqual(expect.stringContaining(error));
});
test("rejects unsupported options without parsing them", () => {
expect(
cli.parse([
"client",
"--listen",
"ws://localhost:8080",
"--auth-token",
"secret",
"--auth-token-file",
"/tmp/token",
]),
).toEqual({
ok: false,
errors: ["The experimental client command does not support existing CLI options yet"],
});
});
test("treats command names after the first argument as existing CLI arguments", () => {
expect(cli.parse(["--cwd", "/workspace", "server"])).toMatchObject({
ok: true,
command: { command: "pi", options: { messages: ["server"] } },
});
});
});