summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-25 10:30:41 +0900
committerAdam Malczewski <[email protected]>2026-06-25 10:30:41 +0900
commitd4b470ca582e3c69c40438895b90748d44fc4653 (patch)
tree1556ec4073013cec8d5599a6aa9911bedcfca7c3 /packages
parent8a74335c21a57ee63c9a0658754430d6520dbc87 (diff)
downloaddispatch-d4b470ca582e3c69c40438895b90748d44fc4653.tar.gz
dispatch-d4b470ca582e3c69c40438895b90748d44fc4653.zip
feat(cli): add --file flag to 'dispatch send' subcommand
Add the same --file <path> support that the summon (chat) command has to the 'dispatch send' subcommand. When --file is given, the file's contents are read and attached to the message (composed via composeMessage, identical to chat). - args.ts: add 'file' to the send ParsedCommand, make 'text' optional, parse --file, and require at least one of --text or --file. - main.ts: read the file and compose the message in the send case, using the composed message in both the --queue and streaming branches; update USAGE. - args.test.ts: cover --file parsing (alone, with --text, missing value) and update the existing send expectations + the both-missing error message.
Diffstat (limited to 'packages')
-rw-r--r--packages/cli/src/args.test.ts33
-rw-r--r--packages/cli/src/args.ts16
-rw-r--r--packages/cli/src/main.ts16
3 files changed, 57 insertions, 8 deletions
diff --git a/packages/cli/src/args.test.ts b/packages/cli/src/args.test.ts
index 3d07c96..7a45c02 100644
--- a/packages/cli/src/args.test.ts
+++ b/packages/cli/src/args.test.ts
@@ -320,11 +320,31 @@ describe("parseArgs", () => {
server: "http://localhost:24203",
conversationId: "deadbeef",
text: "hi",
+ file: undefined,
+ queue: false,
+ open: false,
+ });
+ });
+
+ it("parses 'send' with --file", () => {
+ expect(parseArgs(["send", "deadbeef", "--file", "foo.txt"], { defaultServer })).toEqual({
+ kind: "send",
+ server: "http://localhost:24203",
+ conversationId: "deadbeef",
+ text: undefined,
+ file: "foo.txt",
queue: false,
open: false,
});
});
+ it("parses 'send' with both --text and --file", () => {
+ const result = parseArgs(["send", "deadbeef", "--text", "hi", "--file", "f.txt"], {
+ defaultServer,
+ });
+ expect(result).toMatchObject({ kind: "send", text: "hi", file: "f.txt" });
+ });
+
it("parses 'send' with --queue", () => {
const result = parseArgs(["send", "deadbeef", "--text", "hi", "--queue"], {
defaultServer,
@@ -334,6 +354,7 @@ describe("parseArgs", () => {
server: "http://localhost:24203",
conversationId: "deadbeef",
text: "hi",
+ file: undefined,
queue: true,
open: false,
});
@@ -348,6 +369,7 @@ describe("parseArgs", () => {
server: "http://localhost:24203",
conversationId: "deadbeef",
text: "hi",
+ file: undefined,
queue: false,
open: true,
});
@@ -363,6 +385,7 @@ describe("parseArgs", () => {
server: "http://localhost:24203",
conversationId: "deadbeef",
text: "hi",
+ file: undefined,
queue: false,
open: false,
cwd: "/tmp",
@@ -370,10 +393,10 @@ describe("parseArgs", () => {
});
});
- it("requires --text", () => {
+ it("errors when --text and --file are both missing", () => {
const result = parseArgs(["send", "deadbeef"], { defaultServer });
expect(result.kind).toBe("error");
- if (result.kind === "error") expect(result.message).toContain("--text");
+ if (result.kind === "error") expect(result.message).toContain("--text or --file");
});
it("requires a conversation id", () => {
@@ -386,6 +409,12 @@ describe("parseArgs", () => {
const result = parseArgs(["send", "deadbeef", "--text"], { defaultServer });
expect(result.kind).toBe("error");
});
+
+ it("errors when --file has no value", () => {
+ const result = parseArgs(["send", "deadbeef", "--file"], { defaultServer });
+ expect(result.kind).toBe("error");
+ if (result.kind === "error") expect(result.message).toContain("--file requires a value");
+ });
});
describe("open", () => {
diff --git a/packages/cli/src/args.ts b/packages/cli/src/args.ts
index 8a63777..aaad2de 100644
--- a/packages/cli/src/args.ts
+++ b/packages/cli/src/args.ts
@@ -42,7 +42,8 @@ export type ParsedCommand =
readonly kind: "send";
readonly server: string;
readonly conversationId: string;
- readonly text: string;
+ readonly text?: string | undefined;
+ readonly file?: string | undefined;
readonly queue: boolean;
readonly open: boolean;
readonly cwd?: string;
@@ -204,6 +205,7 @@ export function parseArgs(argv: readonly string[], opts: ParseOpts): ParsedComma
let server = opts.defaultServer;
let conversationId: string | undefined;
let text: string | undefined;
+ let file: string | undefined;
let queue = false;
let open = false;
let cwd: string | undefined;
@@ -221,6 +223,10 @@ export function parseArgs(argv: readonly string[], opts: ParseOpts): ParsedComma
if (i + 1 >= argv.length) return { kind: "error", message: "--text requires a value" };
text = argv[++i];
break;
+ case "--file":
+ if (i + 1 >= argv.length) return { kind: "error", message: "--file requires a value" };
+ file = argv[++i];
+ break;
case "--queue":
queue = true;
break;
@@ -263,8 +269,11 @@ export function parseArgs(argv: readonly string[], opts: ParseOpts): ParsedComma
if (conversationId === undefined) {
return { kind: "error", message: "'send' requires a conversation id" };
}
- if (text === undefined) {
- return { kind: "error", message: "'send' requires --text" };
+ if (!text && !file) {
+ return {
+ kind: "error",
+ message: "At least one of --text or --file is required for 'send'",
+ };
}
return {
@@ -272,6 +281,7 @@ export function parseArgs(argv: readonly string[], opts: ParseOpts): ParsedComma
server,
conversationId,
text,
+ file,
queue,
open,
...(cwd !== undefined && { cwd }),
diff --git a/packages/cli/src/main.ts b/packages/cli/src/main.ts
index 9dfc317..b61be07 100644
--- a/packages/cli/src/main.ts
+++ b/packages/cli/src/main.ts
@@ -29,7 +29,7 @@ const USAGE = `Usage:
dispatch compact <conversationId> [--server <url>]
dispatch read <conversationId> [--server <url>]
dispatch open <conversationId> [--server <url>]
- dispatch send <conversationId> --text "..." [--queue] [--open] [--cwd <dir>] [--effort <level>] [--workspace <id>] [--server <url>]
+ dispatch send <conversationId> --text "..." [--file <path>] [--queue] [--open] [--cwd <dir>] [--effort <level>] [--workspace <id>] [--server <url>]
dispatch <modelName> --text "..." [--file <path>] [--cwd <dir>] [--conversation <id>] [--effort <level>] [--workspace <id>] [--server <url>] [--show-reasoning] [--open]
dispatch --help
@@ -156,10 +156,20 @@ async function main(): Promise<void> {
process.stdout.write(`Signaled frontend to open ${conversationId}\n`);
}
+ let fileContent: string | undefined;
+ if (parsed.file) {
+ fileContent = await readFile(parsed.file, "utf-8");
+ }
+ const message = composeMessage({
+ ...(parsed.text !== undefined && { text: parsed.text }),
+ ...(parsed.file !== undefined && { file: parsed.file }),
+ ...(fileContent !== undefined && { fileContent }),
+ });
+
if (parsed.queue) {
const queued = await enqueueMessage(
{ fetchImpl: globalThis.fetch },
- { server: parsed.server, conversationId, text: parsed.text },
+ { server: parsed.server, conversationId, text: message },
);
const line = queued.startedTurn
? `Started turn for ${conversationId}`
@@ -168,7 +178,7 @@ async function main(): Promise<void> {
} else {
const request = {
conversationId,
- message: parsed.text,
+ message,
...(parsed.cwd !== undefined && { cwd: parsed.cwd }),
...(parsed.reasoningEffort !== undefined && { reasoningEffort: parsed.reasoningEffort }),
...(parsed.workspaceId !== undefined && { workspaceId: parsed.workspaceId }),