@dexh/shannon 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +129 -0
- package/bin/shannon.mjs +24 -0
- package/index.ts +1056 -0
- package/package.json +60 -0
- package/src/sdk.ts +440 -0
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dexh/shannon",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CLI and SDK wrapper that drives interactive Claude Code through tmux and emits stream-json.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/humanlayer/shannon#readme",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/humanlayer/shannon/issues"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"claude",
|
|
12
|
+
"claude-code",
|
|
13
|
+
"agent-sdk",
|
|
14
|
+
"tmux",
|
|
15
|
+
"cli"
|
|
16
|
+
],
|
|
17
|
+
"module": "index.ts",
|
|
18
|
+
"type": "module",
|
|
19
|
+
"bin": {
|
|
20
|
+
"shannon": "bin/shannon.mjs"
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
".": "./src/sdk.ts",
|
|
24
|
+
"./cli": "./index.ts"
|
|
25
|
+
},
|
|
26
|
+
"workspaces": [
|
|
27
|
+
"packages/*"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"test": "bun test",
|
|
31
|
+
"typecheck": "bunx tsc --noEmit",
|
|
32
|
+
"pack:check": "bun pm pack --dry-run && (cd packages/shannon-agent-sdk && bun pm pack --dry-run)"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"index.ts",
|
|
36
|
+
"src/sdk.ts",
|
|
37
|
+
"bin/shannon.mjs",
|
|
38
|
+
"README.md",
|
|
39
|
+
"LICENSE"
|
|
40
|
+
],
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/humanlayer/shannon.git"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"private": false,
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/bun": "latest"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"typescript": "^5"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.140",
|
|
57
|
+
"commander": "^14.0.3",
|
|
58
|
+
"zod": "^4.4.3"
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/sdk.ts
ADDED
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export type ShannonMessage = Record<string, unknown> & {
|
|
4
|
+
type: string;
|
|
5
|
+
session_id?: string;
|
|
6
|
+
uuid?: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type ShannonUserMessage = {
|
|
10
|
+
type: "user";
|
|
11
|
+
message: {
|
|
12
|
+
role: "user";
|
|
13
|
+
content: string | Array<{ type: "text"; text: string }>;
|
|
14
|
+
};
|
|
15
|
+
parent_tool_use_id?: string | null;
|
|
16
|
+
session_id?: string;
|
|
17
|
+
uuid?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type QueryOptions = {
|
|
21
|
+
command?: string;
|
|
22
|
+
cwd?: string;
|
|
23
|
+
verbose?: boolean;
|
|
24
|
+
outputFormat?: "stream-json" | "json" | "text";
|
|
25
|
+
replayUserMessages?: boolean;
|
|
26
|
+
additionalDirectories?: string[];
|
|
27
|
+
agent?: string;
|
|
28
|
+
agents?: Record<string, unknown>;
|
|
29
|
+
allowedTools?: string[];
|
|
30
|
+
appendSystemPrompt?: string;
|
|
31
|
+
betas?: string[];
|
|
32
|
+
continue?: boolean;
|
|
33
|
+
debug?: boolean | string;
|
|
34
|
+
debugFile?: string;
|
|
35
|
+
disallowedTools?: string[];
|
|
36
|
+
effort?: "low" | "medium" | "high" | "xhigh" | "max";
|
|
37
|
+
fallbackModel?: string;
|
|
38
|
+
forkSession?: boolean;
|
|
39
|
+
fromPr?: string | boolean;
|
|
40
|
+
includeHookEvents?: boolean;
|
|
41
|
+
includePartialMessages?: boolean;
|
|
42
|
+
maxBudgetUsd?: number;
|
|
43
|
+
mcpConfig?: string[];
|
|
44
|
+
model?: string;
|
|
45
|
+
name?: string;
|
|
46
|
+
permissionMode?: "acceptEdits" | "auto" | "bypassPermissions" | "default" | "dontAsk" | "plan";
|
|
47
|
+
remoteControl?: string | boolean;
|
|
48
|
+
remoteControlSessionNamePrefix?: string;
|
|
49
|
+
resume?: string | boolean;
|
|
50
|
+
sessionId?: string;
|
|
51
|
+
settings?: string | Record<string, unknown>;
|
|
52
|
+
systemPrompt?: string;
|
|
53
|
+
tools?: string[];
|
|
54
|
+
tmux?: string | boolean;
|
|
55
|
+
worktree?: string | boolean;
|
|
56
|
+
dangerouslySkipPermissions?: boolean;
|
|
57
|
+
allowDangerouslySkipPermissions?: boolean;
|
|
58
|
+
abortController?: AbortController;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type QueryParams = {
|
|
62
|
+
prompt: string | AsyncIterable<ShannonUserMessage>;
|
|
63
|
+
options?: QueryOptions;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const shannonTextBlockSchema = z.object({
|
|
67
|
+
type: z.literal("text"),
|
|
68
|
+
text: z.string(),
|
|
69
|
+
}).catchall(z.unknown());
|
|
70
|
+
|
|
71
|
+
export const shannonUserMessageSchema = z.object({
|
|
72
|
+
type: z.literal("user"),
|
|
73
|
+
message: z.object({
|
|
74
|
+
role: z.literal("user"),
|
|
75
|
+
content: z.union([z.string(), z.array(shannonTextBlockSchema)]),
|
|
76
|
+
}).catchall(z.unknown()),
|
|
77
|
+
parent_tool_use_id: z.string().nullable().optional(),
|
|
78
|
+
session_id: z.string().optional(),
|
|
79
|
+
uuid: z.string().optional(),
|
|
80
|
+
}).catchall(z.unknown());
|
|
81
|
+
|
|
82
|
+
export const shannonMessageSchema = z.object({
|
|
83
|
+
type: z.string(),
|
|
84
|
+
session_id: z.string().optional(),
|
|
85
|
+
uuid: z.string().optional(),
|
|
86
|
+
}).catchall(z.unknown());
|
|
87
|
+
|
|
88
|
+
export const shannonSystemInitSchema = z.object({
|
|
89
|
+
type: z.literal("system"),
|
|
90
|
+
subtype: z.literal("init"),
|
|
91
|
+
cwd: z.string(),
|
|
92
|
+
session_id: z.string(),
|
|
93
|
+
tools: z.array(z.string()),
|
|
94
|
+
mcp_servers: z.array(z.object({
|
|
95
|
+
name: z.string(),
|
|
96
|
+
status: z.string(),
|
|
97
|
+
}).catchall(z.unknown())),
|
|
98
|
+
model: z.string(),
|
|
99
|
+
permissionMode: z.string(),
|
|
100
|
+
apiKeySource: z.string().optional(),
|
|
101
|
+
claude_code_version: z.string().optional(),
|
|
102
|
+
output_style: z.string().optional(),
|
|
103
|
+
agents: z.array(z.string()).optional(),
|
|
104
|
+
slash_commands: z.array(z.string()),
|
|
105
|
+
skills: z.array(z.string()),
|
|
106
|
+
plugins: z.array(z.object({
|
|
107
|
+
name: z.string().optional(),
|
|
108
|
+
path: z.string().optional(),
|
|
109
|
+
source: z.string().optional(),
|
|
110
|
+
}).catchall(z.unknown())).optional(),
|
|
111
|
+
uuid: z.string(),
|
|
112
|
+
}).catchall(z.unknown());
|
|
113
|
+
|
|
114
|
+
export const shannonHookStartedSchema = z.object({
|
|
115
|
+
type: z.literal("system"),
|
|
116
|
+
subtype: z.literal("hook_started"),
|
|
117
|
+
hook_id: z.string(),
|
|
118
|
+
hook_name: z.string(),
|
|
119
|
+
hook_event: z.string(),
|
|
120
|
+
session_id: z.string().optional(),
|
|
121
|
+
uuid: z.string(),
|
|
122
|
+
}).catchall(z.unknown());
|
|
123
|
+
|
|
124
|
+
export const shannonHookResponseSchema = z.object({
|
|
125
|
+
type: z.literal("system"),
|
|
126
|
+
subtype: z.literal("hook_response"),
|
|
127
|
+
hook_id: z.string(),
|
|
128
|
+
hook_name: z.string(),
|
|
129
|
+
hook_event: z.string(),
|
|
130
|
+
output: z.string(),
|
|
131
|
+
stdout: z.string(),
|
|
132
|
+
stderr: z.string(),
|
|
133
|
+
exit_code: z.number().optional(),
|
|
134
|
+
outcome: z.string(),
|
|
135
|
+
session_id: z.string().optional(),
|
|
136
|
+
uuid: z.string(),
|
|
137
|
+
}).catchall(z.unknown());
|
|
138
|
+
|
|
139
|
+
export const shannonAssistantMessageSchema = z.object({
|
|
140
|
+
type: z.literal("assistant"),
|
|
141
|
+
message: z.object({
|
|
142
|
+
role: z.literal("assistant"),
|
|
143
|
+
model: z.string().optional(),
|
|
144
|
+
content: z.array(z.object({ type: z.string() }).catchall(z.unknown())),
|
|
145
|
+
usage: z.record(z.string(), z.unknown()).optional(),
|
|
146
|
+
}).catchall(z.unknown()),
|
|
147
|
+
parent_tool_use_id: z.string().nullable().optional(),
|
|
148
|
+
session_id: z.string(),
|
|
149
|
+
uuid: z.string(),
|
|
150
|
+
}).catchall(z.unknown());
|
|
151
|
+
|
|
152
|
+
export const shannonResultMessageSchema = z.object({
|
|
153
|
+
type: z.literal("result"),
|
|
154
|
+
subtype: z.string(),
|
|
155
|
+
is_error: z.boolean(),
|
|
156
|
+
duration_ms: z.number(),
|
|
157
|
+
duration_api_ms: z.number(),
|
|
158
|
+
num_turns: z.number(),
|
|
159
|
+
result: z.string(),
|
|
160
|
+
stop_reason: z.string().nullable().optional(),
|
|
161
|
+
session_id: z.string(),
|
|
162
|
+
total_cost_usd: z.number(),
|
|
163
|
+
usage: z.record(z.string(), z.unknown()),
|
|
164
|
+
modelUsage: z.record(z.string(), z.unknown()),
|
|
165
|
+
permission_denials: z.array(z.unknown()),
|
|
166
|
+
terminal_reason: z.string(),
|
|
167
|
+
uuid: z.string(),
|
|
168
|
+
}).catchall(z.unknown());
|
|
169
|
+
|
|
170
|
+
export const shannonSessionMetadataSchema = z.object({
|
|
171
|
+
type: z.literal("shannon_session"),
|
|
172
|
+
subtype: z.literal("metadata"),
|
|
173
|
+
session_id: z.string(),
|
|
174
|
+
session_folder: z.string(),
|
|
175
|
+
transcript_path: z.string(),
|
|
176
|
+
tmux_session: z.string(),
|
|
177
|
+
cwd: z.string(),
|
|
178
|
+
cleanup: z.record(z.string(), z.unknown()),
|
|
179
|
+
uuid: z.string(),
|
|
180
|
+
}).catchall(z.unknown());
|
|
181
|
+
|
|
182
|
+
export const shannonRateLimitInfoSchema = z.object({
|
|
183
|
+
status: z.enum(["allowed", "allowed_warning", "rejected"]),
|
|
184
|
+
resetsAt: z.number().optional(),
|
|
185
|
+
rateLimitType: z.enum(["five_hour", "seven_day", "seven_day_opus", "seven_day_sonnet", "overage"]).optional(),
|
|
186
|
+
utilization: z.number().optional(),
|
|
187
|
+
overageStatus: z.enum(["allowed", "allowed_warning", "rejected"]).optional(),
|
|
188
|
+
overageResetsAt: z.number().optional(),
|
|
189
|
+
overageDisabledReason: z.enum([
|
|
190
|
+
"overage_not_provisioned",
|
|
191
|
+
"org_level_disabled",
|
|
192
|
+
"org_level_disabled_until",
|
|
193
|
+
"out_of_credits",
|
|
194
|
+
"seat_tier_level_disabled",
|
|
195
|
+
"member_level_disabled",
|
|
196
|
+
"seat_tier_zero_credit_limit",
|
|
197
|
+
"group_zero_credit_limit",
|
|
198
|
+
"member_zero_credit_limit",
|
|
199
|
+
"org_service_level_disabled",
|
|
200
|
+
"no_limits_configured",
|
|
201
|
+
"fetch_error",
|
|
202
|
+
"unknown",
|
|
203
|
+
]).optional(),
|
|
204
|
+
isUsingOverage: z.boolean().optional(),
|
|
205
|
+
surpassedThreshold: z.number().optional(),
|
|
206
|
+
}).catchall(z.unknown());
|
|
207
|
+
|
|
208
|
+
export const shannonRateLimitEventSchema = z.object({
|
|
209
|
+
type: z.literal("rate_limit_event"),
|
|
210
|
+
rate_limit_info: shannonRateLimitInfoSchema,
|
|
211
|
+
session_id: z.string(),
|
|
212
|
+
uuid: z.string(),
|
|
213
|
+
}).catchall(z.unknown());
|
|
214
|
+
|
|
215
|
+
export const shannonStreamMessageSchema = z.union([
|
|
216
|
+
shannonUserMessageSchema,
|
|
217
|
+
shannonSystemInitSchema,
|
|
218
|
+
shannonHookStartedSchema,
|
|
219
|
+
shannonHookResponseSchema,
|
|
220
|
+
shannonAssistantMessageSchema,
|
|
221
|
+
shannonResultMessageSchema,
|
|
222
|
+
shannonSessionMetadataSchema,
|
|
223
|
+
shannonRateLimitEventSchema,
|
|
224
|
+
]);
|
|
225
|
+
|
|
226
|
+
export const shannonOutputFormatSchema = z.enum(["stream-json", "json", "text"]);
|
|
227
|
+
|
|
228
|
+
export const shannonQueryOptionsSchema = z.object({
|
|
229
|
+
command: z.string().optional(),
|
|
230
|
+
cwd: z.string().optional(),
|
|
231
|
+
verbose: z.boolean().optional(),
|
|
232
|
+
outputFormat: shannonOutputFormatSchema.optional(),
|
|
233
|
+
replayUserMessages: z.boolean().optional(),
|
|
234
|
+
additionalDirectories: z.array(z.string()).optional(),
|
|
235
|
+
agent: z.string().optional(),
|
|
236
|
+
agents: z.record(z.string(), z.unknown()).optional(),
|
|
237
|
+
allowedTools: z.array(z.string()).optional(),
|
|
238
|
+
appendSystemPrompt: z.string().optional(),
|
|
239
|
+
betas: z.array(z.string()).optional(),
|
|
240
|
+
continue: z.boolean().optional(),
|
|
241
|
+
debug: z.union([z.boolean(), z.string()]).optional(),
|
|
242
|
+
debugFile: z.string().optional(),
|
|
243
|
+
disallowedTools: z.array(z.string()).optional(),
|
|
244
|
+
effort: z.enum(["low", "medium", "high", "xhigh", "max"]).optional(),
|
|
245
|
+
fallbackModel: z.string().optional(),
|
|
246
|
+
forkSession: z.boolean().optional(),
|
|
247
|
+
fromPr: z.union([z.string(), z.boolean()]).optional(),
|
|
248
|
+
includeHookEvents: z.boolean().optional(),
|
|
249
|
+
includePartialMessages: z.boolean().optional(),
|
|
250
|
+
maxBudgetUsd: z.number().optional(),
|
|
251
|
+
mcpConfig: z.array(z.string()).optional(),
|
|
252
|
+
model: z.string().optional(),
|
|
253
|
+
name: z.string().optional(),
|
|
254
|
+
permissionMode: z.enum(["acceptEdits", "auto", "bypassPermissions", "default", "dontAsk", "plan"]).optional(),
|
|
255
|
+
remoteControl: z.union([z.string(), z.boolean()]).optional(),
|
|
256
|
+
remoteControlSessionNamePrefix: z.string().optional(),
|
|
257
|
+
resume: z.union([z.string(), z.boolean()]).optional(),
|
|
258
|
+
sessionId: z.string().optional(),
|
|
259
|
+
settings: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),
|
|
260
|
+
systemPrompt: z.string().optional(),
|
|
261
|
+
tools: z.array(z.string()).optional(),
|
|
262
|
+
tmux: z.union([z.string(), z.boolean()]).optional(),
|
|
263
|
+
worktree: z.union([z.string(), z.boolean()]).optional(),
|
|
264
|
+
dangerouslySkipPermissions: z.boolean().optional(),
|
|
265
|
+
allowDangerouslySkipPermissions: z.boolean().optional(),
|
|
266
|
+
abortController: z.instanceof(AbortController).optional(),
|
|
267
|
+
}).strict();
|
|
268
|
+
|
|
269
|
+
export const shannonQueryParamsSchema = z.object({
|
|
270
|
+
prompt: z.union([
|
|
271
|
+
z.string(),
|
|
272
|
+
z.custom<AsyncIterable<ShannonUserMessage>>(
|
|
273
|
+
(value) => Boolean(value && typeof value === "object" && Symbol.asyncIterator in value),
|
|
274
|
+
"Expected an async iterable of Shannon user messages",
|
|
275
|
+
),
|
|
276
|
+
]),
|
|
277
|
+
options: shannonQueryOptionsSchema.optional(),
|
|
278
|
+
}).strict();
|
|
279
|
+
|
|
280
|
+
export function query({ prompt, options = {} }: QueryParams): AsyncIterable<ShannonMessage> {
|
|
281
|
+
return runQuery(prompt, options);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export async function* parseJsonlStream(stream: ReadableStream<Uint8Array>): AsyncIterable<ShannonMessage> {
|
|
285
|
+
const reader = stream.getReader();
|
|
286
|
+
const decoder = new TextDecoder();
|
|
287
|
+
let buffer = "";
|
|
288
|
+
|
|
289
|
+
while (true) {
|
|
290
|
+
const { done, value } = await reader.read();
|
|
291
|
+
if (done) break;
|
|
292
|
+
|
|
293
|
+
buffer += decoder.decode(value, { stream: true });
|
|
294
|
+
const lines = buffer.split("\n");
|
|
295
|
+
buffer = lines.pop() ?? "";
|
|
296
|
+
|
|
297
|
+
for (const line of lines) {
|
|
298
|
+
if (line.trim()) yield JSON.parse(line) as ShannonMessage;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
buffer += decoder.decode();
|
|
303
|
+
if (buffer.trim()) {
|
|
304
|
+
yield JSON.parse(buffer) as ShannonMessage;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async function* runQuery(
|
|
309
|
+
prompt: string | AsyncIterable<ShannonUserMessage>,
|
|
310
|
+
options: QueryOptions,
|
|
311
|
+
): AsyncIterable<ShannonMessage> {
|
|
312
|
+
const command = options.command ?? "shannon";
|
|
313
|
+
const outputFormat = options.outputFormat ?? "stream-json";
|
|
314
|
+
const isStreamingInput = typeof prompt !== "string";
|
|
315
|
+
const args = [
|
|
316
|
+
command,
|
|
317
|
+
...(typeof prompt === "string" ? ["-p", prompt] : ["--input-format=stream-json"]),
|
|
318
|
+
`--output-format=${outputFormat}`,
|
|
319
|
+
...(options.verbose ?? true ? ["--verbose"] : []),
|
|
320
|
+
...(options.replayUserMessages ? ["--replay-user-messages"] : []),
|
|
321
|
+
...optionsToCliArgs(options),
|
|
322
|
+
];
|
|
323
|
+
const signal = options.abortController?.signal;
|
|
324
|
+
|
|
325
|
+
if (signal?.aborted) {
|
|
326
|
+
throw new Error("Shannon query aborted before start");
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const proc = Bun.spawn(args, {
|
|
330
|
+
cwd: options.cwd,
|
|
331
|
+
stdin: isStreamingInput ? "pipe" : "ignore",
|
|
332
|
+
stdout: "pipe",
|
|
333
|
+
stderr: "pipe",
|
|
334
|
+
});
|
|
335
|
+
const abortHandler = () => {
|
|
336
|
+
proc.kill("SIGTERM");
|
|
337
|
+
};
|
|
338
|
+
signal?.addEventListener("abort", abortHandler, { once: true });
|
|
339
|
+
const stderrPromise = new Response(proc.stderr).text();
|
|
340
|
+
const stdinPromise = isStreamingInput
|
|
341
|
+
? writeStreamingInput(proc.stdin, prompt)
|
|
342
|
+
: Promise.resolve();
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
for await (const message of parseJsonlStream(proc.stdout)) {
|
|
346
|
+
yield message;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const [exitCode, stderr] = await Promise.all([proc.exited, stderrPromise, stdinPromise]);
|
|
350
|
+
if (signal?.aborted) {
|
|
351
|
+
throw new Error("Shannon query aborted");
|
|
352
|
+
}
|
|
353
|
+
if (exitCode !== 0) {
|
|
354
|
+
throw new Error(`shannon exited with ${exitCode}${stderr.trim() ? `: ${stderr.trim()}` : ""}`);
|
|
355
|
+
}
|
|
356
|
+
} finally {
|
|
357
|
+
signal?.removeEventListener("abort", abortHandler);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export function optionsToCliArgs(options: QueryOptions): string[] {
|
|
362
|
+
const args: string[] = [];
|
|
363
|
+
|
|
364
|
+
addRepeated(args, "--add-dir", options.additionalDirectories);
|
|
365
|
+
addString(args, "--agent", options.agent);
|
|
366
|
+
addJson(args, "--agents", options.agents);
|
|
367
|
+
addBoolean(args, "--allow-dangerously-skip-permissions", options.allowDangerouslySkipPermissions);
|
|
368
|
+
addRepeated(args, "--allowed-tools", options.allowedTools);
|
|
369
|
+
addString(args, "--append-system-prompt", options.appendSystemPrompt);
|
|
370
|
+
addRepeated(args, "--betas", options.betas);
|
|
371
|
+
addBoolean(args, "--continue", options.continue);
|
|
372
|
+
addOptionalString(args, "--debug", options.debug);
|
|
373
|
+
addString(args, "--debug-file", options.debugFile);
|
|
374
|
+
addRepeated(args, "--disallowed-tools", options.disallowedTools);
|
|
375
|
+
addString(args, "--effort", options.effort);
|
|
376
|
+
addString(args, "--fallback-model", options.fallbackModel);
|
|
377
|
+
addBoolean(args, "--fork-session", options.forkSession);
|
|
378
|
+
addOptionalString(args, "--from-pr", options.fromPr);
|
|
379
|
+
addBoolean(args, "--include-hook-events", options.includeHookEvents);
|
|
380
|
+
addBoolean(args, "--include-partial-messages", options.includePartialMessages);
|
|
381
|
+
addString(args, "--max-budget-usd", options.maxBudgetUsd);
|
|
382
|
+
addRepeated(args, "--mcp-config", options.mcpConfig);
|
|
383
|
+
addString(args, "--model", options.model);
|
|
384
|
+
addString(args, "--name", options.name);
|
|
385
|
+
addString(args, "--permission-mode", options.permissionMode);
|
|
386
|
+
addOptionalString(args, "--remote-control", options.remoteControl);
|
|
387
|
+
addString(args, "--remote-control-session-name-prefix", options.remoteControlSessionNamePrefix);
|
|
388
|
+
addOptionalString(args, "--resume", options.resume);
|
|
389
|
+
addString(args, "--session-id", options.sessionId);
|
|
390
|
+
addSettings(args, options.settings);
|
|
391
|
+
addString(args, "--system-prompt", options.systemPrompt);
|
|
392
|
+
addRepeated(args, "--tools", options.tools);
|
|
393
|
+
addOptionalString(args, "--tmux", options.tmux);
|
|
394
|
+
addOptionalString(args, "--worktree", options.worktree);
|
|
395
|
+
addBoolean(args, "--dangerously-skip-permissions", options.dangerouslySkipPermissions);
|
|
396
|
+
|
|
397
|
+
return args;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async function writeStreamingInput(
|
|
401
|
+
stdin: { write: (chunk: string | Uint8Array) => unknown; end: () => unknown } | undefined,
|
|
402
|
+
prompt: AsyncIterable<ShannonUserMessage>,
|
|
403
|
+
) {
|
|
404
|
+
if (!stdin) throw new Error("shannon subprocess stdin was not available");
|
|
405
|
+
|
|
406
|
+
try {
|
|
407
|
+
for await (const message of prompt) {
|
|
408
|
+
await stdin.write(`${JSON.stringify(message)}\n`);
|
|
409
|
+
}
|
|
410
|
+
} finally {
|
|
411
|
+
await stdin.end();
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function addString(args: string[], flag: string, value: unknown) {
|
|
416
|
+
if (typeof value === "string" && value.length > 0) args.push(flag, value);
|
|
417
|
+
else if (typeof value === "number") args.push(flag, String(value));
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function addOptionalString(args: string[], flag: string, value: unknown) {
|
|
421
|
+
if (value === true) args.push(flag);
|
|
422
|
+
else addString(args, flag, value);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function addBoolean(args: string[], flag: string, value: unknown) {
|
|
426
|
+
if (value === true) args.push(flag);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function addRepeated(args: string[], flag: string, value: unknown) {
|
|
430
|
+
if (Array.isArray(value) && value.length > 0) args.push(flag, ...value.map(String));
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function addJson(args: string[], flag: string, value: unknown) {
|
|
434
|
+
if (value && typeof value === "object") args.push(flag, JSON.stringify(value));
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function addSettings(args: string[], value: unknown) {
|
|
438
|
+
if (typeof value === "string") args.push("--settings", value);
|
|
439
|
+
else if (value && typeof value === "object") args.push("--settings", JSON.stringify(value));
|
|
440
|
+
}
|