From c5e9fd6cd6565b55fab1bf2b9d8dacf8ba72a9f4 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Sun, 21 Jun 2026 19:20:10 +0900 Subject: feat(cli): list, read, send commands (Wave 3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CLI gains three new sub-commands: - dispatch list [--server] — list conversations (short ID + title + activity) - dispatch read [--server] — block until turn settles, print last AI message - dispatch send --text [--queue] [--open] [--cwd] [--effort] [--server] - Default: blocking (consumes NDJSON stream, prints accumulated text + conv ID) - --queue: non-blocking (POST /conversations/:id/queue, exit immediately) - --open: signals frontend to open the conversation tab (POST /conversations/:id/open) Short-ID resolution: 4+ char prefix → GET /conversations?q= → resolve to full ID. 32+ char input is treated as a full UUID (no resolution). Errors on 0 or >1 matches. 48 new tests (108 total in cli). Pure arg parser + HTTP client functions, zero vi.mock. --- packages/cli/src/args.test.ts | 141 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) (limited to 'packages/cli/src/args.test.ts') diff --git a/packages/cli/src/args.test.ts b/packages/cli/src/args.test.ts index ce278bb..392d560 100644 --- a/packages/cli/src/args.test.ts +++ b/packages/cli/src/args.test.ts @@ -180,4 +180,145 @@ describe("parseArgs", () => { expect(result.kind).toBe("error"); }); }); + + describe("list", () => { + it("parses 'list' with no query", () => { + expect(parseArgs(["list"], { defaultServer })).toEqual({ + kind: "list", + server: "http://localhost:24203", + }); + }); + + it("parses 'list' with a query prefix", () => { + expect(parseArgs(["list", "abc12345"], { defaultServer })).toEqual({ + kind: "list", + server: "http://localhost:24203", + query: "abc12345", + }); + }); + + it("parses 'list' with --server after the prefix", () => { + expect(parseArgs(["list", "abc", "--server", "http://s"], { defaultServer })).toEqual({ + kind: "list", + server: "http://s", + query: "abc", + }); + }); + + it("errors on a second positional argument", () => { + const result = parseArgs(["list", "abc", "def"], { defaultServer }); + expect(result.kind).toBe("error"); + if (result.kind === "error") expect(result.message).toContain("Unexpected argument"); + }); + + it("errors on an unknown flag", () => { + const result = parseArgs(["list", "--bogus"], { defaultServer }); + expect(result.kind).toBe("error"); + if (result.kind === "error") expect(result.message).toContain("Unknown flag"); + }); + }); + + describe("read", () => { + it("parses 'read' with a conversation id", () => { + expect(parseArgs(["read", "deadbeef"], { defaultServer })).toEqual({ + kind: "read", + server: "http://localhost:24203", + conversationId: "deadbeef", + }); + }); + + it("parses 'read' with --server", () => { + expect(parseArgs(["read", "deadbeef", "--server", "http://s"], { defaultServer })).toEqual({ + kind: "read", + server: "http://s", + conversationId: "deadbeef", + }); + }); + + it("errors when no conversation id is given", () => { + const result = parseArgs(["read"], { defaultServer }); + expect(result.kind).toBe("error"); + if (result.kind === "error") expect(result.message).toContain("conversation id"); + }); + + it("errors on a second positional argument", () => { + const result = parseArgs(["read", "a", "b"], { defaultServer }); + expect(result.kind).toBe("error"); + }); + }); + + describe("send", () => { + it("parses 'send' with --text", () => { + expect(parseArgs(["send", "deadbeef", "--text", "hi"], { defaultServer })).toEqual({ + kind: "send", + server: "http://localhost:24203", + conversationId: "deadbeef", + text: "hi", + queue: false, + open: false, + }); + }); + + it("parses 'send' with --queue", () => { + const result = parseArgs(["send", "deadbeef", "--text", "hi", "--queue"], { + defaultServer, + }); + expect(result).toEqual({ + kind: "send", + server: "http://localhost:24203", + conversationId: "deadbeef", + text: "hi", + queue: true, + open: false, + }); + }); + + it("parses 'send' with --open", () => { + const result = parseArgs(["send", "deadbeef", "--text", "hi", "--open"], { + defaultServer, + }); + expect(result).toEqual({ + kind: "send", + server: "http://localhost:24203", + conversationId: "deadbeef", + text: "hi", + queue: false, + open: true, + }); + }); + + it("parses 'send' with --cwd and --effort", () => { + const result = parseArgs( + ["send", "deadbeef", "--text", "hi", "--cwd", "/tmp", "--effort", "xhigh"], + { defaultServer }, + ); + expect(result).toEqual({ + kind: "send", + server: "http://localhost:24203", + conversationId: "deadbeef", + text: "hi", + queue: false, + open: false, + cwd: "/tmp", + reasoningEffort: "xhigh", + }); + }); + + it("requires --text", () => { + const result = parseArgs(["send", "deadbeef"], { defaultServer }); + expect(result.kind).toBe("error"); + if (result.kind === "error") expect(result.message).toContain("--text"); + }); + + it("requires a conversation id", () => { + const result = parseArgs(["send", "--text", "hi"], { defaultServer }); + expect(result.kind).toBe("error"); + if (result.kind === "error") expect(result.message).toContain("conversation id"); + }); + + it("errors when --text has no value", () => { + const result = parseArgs(["send", "deadbeef", "--text"], { defaultServer }); + expect(result.kind).toBe("error"); + }); + }); }); -- cgit v1.2.3