1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
/**
* 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 ReasoningEffort = "low" | "medium" | "high" | "xhigh" | "max";
const VALID_EFFORTS: readonly ReasoningEffort[] = ["low", "medium", "high", "xhigh", "max"];
export function isValidEffort(value: string): value is ReasoningEffort {
return (VALID_EFFORTS as readonly string[]).includes(value);
}
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 reasoningEffort?: ReasoningEffort | 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 reasoningEffort: ReasoningEffort | 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;
case "--effort":
if (i + 1 >= argv.length)
return {
kind: "error",
message: `--effort requires a value (one of: ${VALID_EFFORTS.join(", ")})`,
};
{
const val = argv[++i] as string;
if (!isValidEffort(val))
return {
kind: "error",
message: `Invalid effort level "${val}". Must be one of: ${VALID_EFFORTS.join(", ")}`,
};
reasoningEffort = val;
}
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,
reasoningEffort,
showReasoning,
};
}
|