summaryrefslogtreecommitdiffhomepage
path: root/packages/cli/src/args.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-05 21:20:34 +0900
committerAdam Malczewski <[email protected]>2026-06-05 21:20:34 +0900
commit552c22d74e5df915088d9e9ff4a286c96c2a54d6 (patch)
tree7d9db1052bab91ef994446d80efc3bfc38026cad /packages/cli/src/args.ts
parent7fb3269c698ae583ea7997ce206c4ae252fd3218 (diff)
downloaddispatch-552c22d74e5df915088d9e9ff4a286c96c2a54d6.tar.gz
dispatch-552c22d74e5df915088d9e9ff4a286c96c2a54d6.zip
feat(cli): one-shot terminal client (models, chat, --text/--file/--cwd/--conversation)
HTTP client of transport-contract; pure-core arg/render/ndjson + injected fetch/fs shell. Docs: GLOSSARY (credential/key/model name/model catalog), tasks.md milestone, ORCHESTRATOR geography.
Diffstat (limited to 'packages/cli/src/args.ts')
-rw-r--r--packages/cli/src/args.ts108
1 files changed, 108 insertions, 0 deletions
diff --git a/packages/cli/src/args.ts b/packages/cli/src/args.ts
new file mode 100644
index 0000000..2c554e8
--- /dev/null
+++ b/packages/cli/src/args.ts
@@ -0,0 +1,108 @@
+/**
+ * Pure argument parser — zero I/O, zero ambient state.
+ *
+ * Parses process.argv-style strings into a discriminated command union.
+ * Validates required flags and reports unknown flags as errors.
+ */
+
+export type ParsedCommand =
+ | { readonly kind: "models"; readonly server: string }
+ | {
+ readonly kind: "chat";
+ readonly server: string;
+ readonly modelName: string;
+ readonly text?: string | undefined;
+ readonly file?: string | undefined;
+ readonly cwd?: string | undefined;
+ readonly conversationId?: string | undefined;
+ readonly showReasoning: boolean;
+ }
+ | { readonly kind: "help" }
+ | { readonly kind: "error"; readonly message: string };
+
+interface ParseOpts {
+ readonly defaultServer: string;
+}
+
+export function parseArgs(argv: readonly string[], opts: ParseOpts): ParsedCommand {
+ if (argv.length === 0) {
+ return { kind: "help" };
+ }
+
+ const first = argv[0] as string;
+
+ if (first === "--help" || first === "-h") {
+ return { kind: "help" };
+ }
+
+ if (first === "models") {
+ let server = opts.defaultServer;
+ for (let i = 1; i < argv.length; i++) {
+ if (argv[i] === "--server" && i + 1 < argv.length) {
+ server = argv[++i] as string;
+ } else {
+ return { kind: "error", message: `Unknown argument for 'models': ${argv[i]}` };
+ }
+ }
+ return { kind: "models", server };
+ }
+
+ // Chat mode: first arg is the model name
+ const modelName = first;
+ let text: string | undefined;
+ let file: string | undefined;
+ let cwd: string | undefined;
+ let conversationId: string | undefined;
+ let showReasoning = false;
+ let server = opts.defaultServer;
+
+ for (let i = 1; i < argv.length; i++) {
+ const arg = argv[i] as string;
+ switch (arg) {
+ case "--text":
+ 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 "--cwd":
+ if (i + 1 >= argv.length) return { kind: "error", message: "--cwd requires a value" };
+ cwd = argv[++i];
+ break;
+ case "--conversation":
+ if (i + 1 >= argv.length)
+ return { kind: "error", message: "--conversation requires a value" };
+ conversationId = argv[++i];
+ break;
+ case "--server":
+ if (i + 1 >= argv.length) return { kind: "error", message: "--server requires a value" };
+ server = argv[++i] as string;
+ break;
+ case "--show-reasoning":
+ showReasoning = true;
+ break;
+ default:
+ return { kind: "error", message: `Unknown flag: ${arg}` };
+ }
+ }
+
+ if (!text && !file) {
+ return {
+ kind: "error",
+ message: "At least one of --text or --file is required for a chat command",
+ };
+ }
+
+ return {
+ kind: "chat",
+ server,
+ modelName,
+ text,
+ file,
+ cwd,
+ conversationId,
+ showReasoning,
+ };
+}