@tryarcanist/cli 0.1.0 → 0.1.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.
Files changed (2) hide show
  1. package/dist/index.js +541 -2
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
+ import { createRequire } from "module";
4
5
  import { Command } from "commander";
5
6
 
6
7
  // src/api.ts
7
- async function apiFetch(config, path, init) {
8
+ async function apiRequest(config, path, init) {
8
9
  const res = await fetch(`${config.apiUrl}${path}`, {
9
10
  ...init,
10
11
  headers: {
@@ -20,8 +21,16 @@ async function apiFetch(config, path, init) {
20
21
  const body = await res.text().catch(() => "");
21
22
  throw new Error(`API error ${res.status}: ${body}`);
22
23
  }
24
+ return res;
25
+ }
26
+ async function apiFetch(config, path, init) {
27
+ const res = await apiRequest(config, path, init);
23
28
  return res.json();
24
29
  }
30
+ async function apiFetchText(config, path, init) {
31
+ const res = await apiRequest(config, path, init);
32
+ return res.text();
33
+ }
25
34
 
26
35
  // src/config.ts
27
36
  import { mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -182,11 +191,541 @@ async function messageCommand(sessionId, prompt) {
182
191
  }
183
192
  }
184
193
 
194
+ // src/commands/stop.ts
195
+ async function stopCommand(sessionId) {
196
+ const config = requireConfig();
197
+ try {
198
+ await apiFetch(config, `/api/sessions/${sessionId}/stop`, {
199
+ method: "POST"
200
+ });
201
+ console.log(`Stop requested for session ${sessionId}.`);
202
+ } catch (err) {
203
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
204
+ process.exit(1);
205
+ }
206
+ }
207
+
208
+ // src/utils/session-output.ts
209
+ function flattenSessionEvents(raw) {
210
+ const merged = [];
211
+ const textIndexById = /* @__PURE__ */ new Map();
212
+ const toolCallIndexById = /* @__PURE__ */ new Map();
213
+ for (const event of raw) {
214
+ const data = event.data ?? {};
215
+ if (event.type === "sandbox_compaction_start") {
216
+ merged.push({ type: "compaction_start", id: `cs-${data.timestamp ?? merged.length}` });
217
+ continue;
218
+ }
219
+ if (event.type === "sandbox_compaction_complete") {
220
+ merged.push({ type: "compaction_complete", id: `cc-${data.timestamp ?? merged.length}` });
221
+ continue;
222
+ }
223
+ if (event.type === "sandbox_context_fill_warning") {
224
+ merged.push({
225
+ type: "context_fill_warning",
226
+ id: `cfw-${data.timestamp ?? merged.length}`,
227
+ fillPercent: Number(data.fillPercent ?? 0)
228
+ });
229
+ continue;
230
+ }
231
+ if (event.type === "sandbox_tool_truncated") {
232
+ merged.push({
233
+ type: "tool_truncated",
234
+ id: String(data.callId ?? merged.length),
235
+ tool: String(data.tool ?? "")
236
+ });
237
+ continue;
238
+ }
239
+ if (event.type === "branch_changed") {
240
+ merged.push({ type: "branch_changed", id: `bc-${data.timestamp ?? merged.length}`, branch: String(data.branch ?? "") });
241
+ continue;
242
+ }
243
+ if (event.type === "session_error") {
244
+ merged.push({
245
+ type: "session_error",
246
+ id: String(data.id ?? `err-${merged.length}`),
247
+ error: String(data.error ?? "Unknown error"),
248
+ ...data.code ? { code: String(data.code) } : {}
249
+ });
250
+ continue;
251
+ }
252
+ if (event.type === "raw_opencode") {
253
+ merged.push({ type: "raw_opencode", id: String(data.id ?? `raw-${merged.length}`), data });
254
+ continue;
255
+ }
256
+ if (event.type === "reasoning") {
257
+ const id = String(data.id ?? `reasoning-${merged.length}`);
258
+ const text = String(data.text ?? "");
259
+ const existingIdx = textIndexById.get(id);
260
+ if (existingIdx !== void 0) {
261
+ const existing = merged[existingIdx];
262
+ if (existing.type === "reasoning") existing.text += text;
263
+ } else {
264
+ textIndexById.set(id, merged.length);
265
+ merged.push({ type: "reasoning", id, text });
266
+ }
267
+ continue;
268
+ }
269
+ if (event.type === "patch") {
270
+ const files = Array.isArray(data.files) ? data.files.filter((item) => typeof item === "string") : [];
271
+ if (files.length > 0) merged.push({ type: "patch", id: `patch-${merged.length}`, files });
272
+ continue;
273
+ }
274
+ if (event.type === "todo_update") {
275
+ const todos = Array.isArray(data.todos) ? data.todos : [];
276
+ if (todos.length > 0) {
277
+ for (let i = merged.length - 1; i >= 0; i--) {
278
+ const existing = merged[i];
279
+ if (existing.type === "tool_call" && existing.tool.toLowerCase() === "todowrite") {
280
+ existing.input = { ...existing.input, todos };
281
+ break;
282
+ }
283
+ }
284
+ }
285
+ continue;
286
+ }
287
+ if (event.type !== "text" && event.type !== "tool_call" && event.type !== "tool_update" && event.type !== "question") {
288
+ continue;
289
+ }
290
+ if (event.type === "text") {
291
+ const id = String(data.id ?? `text-${merged.length}`);
292
+ const text = String(data.text ?? "");
293
+ const existingIdx = textIndexById.get(id);
294
+ if (existingIdx !== void 0) {
295
+ const existing = merged[existingIdx];
296
+ if (existing.type === "text") existing.text += text;
297
+ } else {
298
+ textIndexById.set(id, merged.length);
299
+ merged.push({ type: "text", id, text });
300
+ }
301
+ continue;
302
+ }
303
+ if (event.type === "tool_call") {
304
+ const id = String(data.id ?? `tool-${merged.length}`);
305
+ const nextEvent = {
306
+ type: "tool_call",
307
+ id,
308
+ tool: String(data.tool ?? "unknown"),
309
+ summary: String(data.summary ?? ""),
310
+ ...data.input && typeof data.input === "object" ? { input: data.input } : {}
311
+ };
312
+ const existingIdx = toolCallIndexById.get(id);
313
+ if (existingIdx !== void 0) {
314
+ const prev = merged[existingIdx];
315
+ if (prev.type === "tool_call") merged[existingIdx] = { ...nextEvent, ...prev.toolStatus ? { toolStatus: prev.toolStatus } : {} };
316
+ } else {
317
+ toolCallIndexById.set(id, merged.length);
318
+ merged.push(nextEvent);
319
+ }
320
+ continue;
321
+ }
322
+ if (event.type === "tool_update") {
323
+ const id = String(data.id ?? "");
324
+ const existingIdx = toolCallIndexById.get(id);
325
+ if (existingIdx !== void 0) {
326
+ const prev = merged[existingIdx];
327
+ if (prev.type === "tool_call") {
328
+ merged[existingIdx] = { ...prev, ...data.status ? { toolStatus: String(data.status) } : {} };
329
+ }
330
+ }
331
+ continue;
332
+ }
333
+ merged.push({
334
+ type: "question",
335
+ id: String(data.id ?? `question-${merged.length}`),
336
+ question: String(data.question ?? ""),
337
+ answer: data.answer == null ? null : String(data.answer),
338
+ ...Array.isArray(data.options) ? { options: data.options } : {}
339
+ });
340
+ }
341
+ return merged;
342
+ }
343
+ function partitionEventsByPrompt(allEvents, promptIds) {
344
+ const promptSet = new Set(promptIds);
345
+ const buckets = new Map(promptIds.map((id) => [id, []]));
346
+ let currentPromptId = null;
347
+ for (const event of allEvents) {
348
+ if (event.type === "prompt_processing") {
349
+ const promptId = typeof event.data?.promptId === "string" ? event.data.promptId : null;
350
+ currentPromptId = promptId && promptSet.has(promptId) ? promptId : null;
351
+ }
352
+ if (currentPromptId) {
353
+ const bucket = buckets.get(currentPromptId);
354
+ if (bucket) bucket.push(event);
355
+ }
356
+ }
357
+ return buckets;
358
+ }
359
+ function formatDate(value) {
360
+ const date = new Date(value);
361
+ if (Number.isNaN(date.getTime())) return value;
362
+ return date.toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC");
363
+ }
364
+ function renderTranscriptEvent(event) {
365
+ switch (event.type) {
366
+ case "text":
367
+ return event.text;
368
+ case "tool_call":
369
+ return `**Tool call:** ${event.tool}${event.summary ? ` - ${event.summary}` : ""}${event.toolStatus ? ` (${event.toolStatus})` : ""}
370
+ `;
371
+ case "reasoning":
372
+ return `<details><summary>Reasoning</summary>
373
+
374
+ ${event.text}
375
+ </details>
376
+ `;
377
+ case "patch":
378
+ return `**Files changed:** ${event.files.join(", ")}
379
+ `;
380
+ case "question":
381
+ return `**Question:** ${event.question}
382
+ ${event.answer ? `**Answer:** ${event.answer}
383
+ ` : ""}`;
384
+ case "compaction_start":
385
+ return "*[context compacted]*\n";
386
+ case "compaction_complete":
387
+ return "";
388
+ case "context_fill_warning":
389
+ return `*[context ${event.fillPercent}% full]*
390
+ `;
391
+ case "tool_truncated":
392
+ return `*[${event.tool} output truncated]*
393
+ `;
394
+ case "branch_changed":
395
+ return `*[switched to branch \`${event.branch}\`]*
396
+ `;
397
+ case "session_error":
398
+ return `**Error:** ${event.error}
399
+ `;
400
+ case "raw_opencode":
401
+ return "";
402
+ }
403
+ }
404
+ function formatNumber(value) {
405
+ return value.toLocaleString();
406
+ }
407
+ function renderSessionTranscript(exportData) {
408
+ const lines = [];
409
+ const promptIds = exportData.prompts.map((prompt) => prompt.id);
410
+ const eventBuckets = partitionEventsByPrompt(exportData.events, promptIds);
411
+ lines.push("# Session transcript\n");
412
+ if (exportData.session.repoUrl) lines.push(`**Repo:** ${exportData.session.repoUrl} `);
413
+ lines.push(`**Session:** ${exportData.session.id.slice(0, 8)} `);
414
+ lines.push(`**Created:** ${formatDate(exportData.session.createdAt)} `);
415
+ lines.push(`**Status:** ${exportData.session.status} `);
416
+ lines.push(`**Tokens:** ${formatNumber(exportData.tokens.inputTokens)} in / ${formatNumber(exportData.tokens.outputTokens)} out / ${formatNumber(exportData.tokens.totalTokens)} total`);
417
+ if (exportData.pr?.url) {
418
+ lines.push(`**PR:** ${exportData.pr.url}${exportData.pr.branch ? ` (${exportData.pr.branch})` : ""}`);
419
+ }
420
+ lines.push("");
421
+ for (let i = 0; i < exportData.prompts.length; i++) {
422
+ const prompt = exportData.prompts[i];
423
+ const rawEvents = eventBuckets.get(prompt.id) ?? [];
424
+ const events = flattenSessionEvents(rawEvents);
425
+ lines.push("---\n");
426
+ lines.push(`## Turn ${i + 1}
427
+ `);
428
+ lines.push(`**Prompt ID:** ${prompt.id} `);
429
+ lines.push(`**Prompt Status:** ${prompt.status}${prompt.error ? ` | ${prompt.error}` : ""}`);
430
+ lines.push("");
431
+ lines.push("**User:**\n");
432
+ lines.push(prompt.prompt);
433
+ lines.push("");
434
+ if (events.length > 0) {
435
+ lines.push("**Assistant:**\n");
436
+ let pendingText = "";
437
+ for (const event of events) {
438
+ const rendered = renderTranscriptEvent(event);
439
+ if (!rendered) continue;
440
+ if (event.type === "text") {
441
+ pendingText += rendered;
442
+ } else {
443
+ if (pendingText) {
444
+ lines.push(pendingText.trimEnd());
445
+ lines.push("");
446
+ pendingText = "";
447
+ }
448
+ lines.push(rendered);
449
+ }
450
+ }
451
+ if (pendingText) {
452
+ lines.push(pendingText.trimEnd());
453
+ lines.push("");
454
+ }
455
+ } else if (prompt.error) {
456
+ lines.push("**Assistant:**\n");
457
+ lines.push(`**Error:** ${prompt.error}`);
458
+ lines.push("");
459
+ }
460
+ }
461
+ return lines.join("\n");
462
+ }
463
+ function parseSsePayload(payload) {
464
+ const messages = [];
465
+ let currentEvent = "message";
466
+ let currentId;
467
+ let currentData = [];
468
+ function flush() {
469
+ if (currentData.length === 0 && currentId === void 0 && currentEvent === "message") return;
470
+ messages.push({ event: currentEvent, ...currentId !== void 0 ? { id: currentId } : {}, data: currentData.join("\n") });
471
+ currentEvent = "message";
472
+ currentId = void 0;
473
+ currentData = [];
474
+ }
475
+ for (const rawLine of payload.split(/\r?\n/)) {
476
+ const line = rawLine.trimEnd();
477
+ if (line.length === 0) {
478
+ flush();
479
+ continue;
480
+ }
481
+ if (line.startsWith("event:")) {
482
+ currentEvent = line.slice("event:".length).trim();
483
+ continue;
484
+ }
485
+ if (line.startsWith("id:")) {
486
+ const parsed = Number.parseInt(line.slice("id:".length).trim(), 10);
487
+ currentId = Number.isFinite(parsed) ? parsed : void 0;
488
+ continue;
489
+ }
490
+ if (line.startsWith("data:")) {
491
+ currentData.push(line.slice("data:".length).trimStart());
492
+ }
493
+ }
494
+ flush();
495
+ let status = null;
496
+ const events = [];
497
+ for (const message of messages) {
498
+ const data = message.data ? parseJsonObject(message.data) : {};
499
+ if (message.event === "status") {
500
+ status = {
501
+ status: typeof data.status === "string" ? data.status : "unknown",
502
+ ...typeof data.title === "string" ? { title: data.title } : {},
503
+ ...typeof data.spawnDurationMs === "number" || data.spawnDurationMs === null ? { spawnDurationMs: data.spawnDurationMs } : {}
504
+ };
505
+ continue;
506
+ }
507
+ events.push({
508
+ type: message.event,
509
+ ...message.id !== void 0 ? { id: message.id } : {},
510
+ data
511
+ });
512
+ }
513
+ return { status, events };
514
+ }
515
+ function parseJsonObject(value) {
516
+ try {
517
+ const parsed = JSON.parse(value);
518
+ return parsed && typeof parsed === "object" ? parsed : {};
519
+ } catch (err) {
520
+ throw new Error(`Malformed SSE JSON payload: ${err instanceof Error ? err.message : String(err)}`);
521
+ }
522
+ }
523
+ function formatPromptLabel(promptId, promptLabels) {
524
+ if (!promptId) return "";
525
+ const prompt = promptLabels.get(promptId);
526
+ return prompt ? ` - ${prompt}` : "";
527
+ }
528
+ function buildPromptLabelMap(prompts) {
529
+ const labels = /* @__PURE__ */ new Map();
530
+ for (const prompt of prompts) {
531
+ labels.set(prompt.promptId, prompt.prompt.replace(/\s+/g, " ").trim().slice(0, 80));
532
+ }
533
+ return labels;
534
+ }
535
+ function renderWatchEvent(event, state) {
536
+ const data = event.data;
537
+ switch (event.type) {
538
+ case "text":
539
+ return { kind: "text", text: String(data.text ?? "") };
540
+ case "prompt_enqueued": {
541
+ const promptId = typeof data.promptId === "string" ? data.promptId : void 0;
542
+ return { kind: "line", line: `[queued] ${promptId ?? "unknown"}${formatPromptLabel(promptId, state.promptLabels)}` };
543
+ }
544
+ case "prompt_processing": {
545
+ const promptId = typeof data.promptId === "string" ? data.promptId : void 0;
546
+ return { kind: "line", line: `[prompt] ${promptId ?? "unknown"} started${formatPromptLabel(promptId, state.promptLabels)}` };
547
+ }
548
+ case "prompt_completed": {
549
+ const promptId = typeof data.promptId === "string" ? data.promptId : void 0;
550
+ return { kind: "line", line: `[prompt] ${promptId ?? "unknown"} completed` };
551
+ }
552
+ case "prompt_failed": {
553
+ const promptId = typeof data.promptId === "string" ? data.promptId : void 0;
554
+ const error = typeof data.error === "string" ? data.error : "unknown error";
555
+ return { kind: "line", line: `[prompt] ${promptId ?? "unknown"} failed: ${error}` };
556
+ }
557
+ case "tool_call": {
558
+ const toolId = String(data.id ?? "");
559
+ const tool = String(data.tool ?? "unknown");
560
+ const summary = String(data.summary ?? "");
561
+ state.toolCalls.set(toolId, { tool, summary });
562
+ return { kind: "line", line: `[tool] ${tool}${summary ? ` - ${summary}` : ""}` };
563
+ }
564
+ case "tool_update": {
565
+ const toolId = String(data.id ?? "");
566
+ const status = typeof data.status === "string" ? data.status : null;
567
+ if (!status) return null;
568
+ const tool = state.toolCalls.get(toolId);
569
+ if (!tool) return { kind: "line", line: `[tool] ${toolId} ${status}` };
570
+ return { kind: "line", line: `[tool ${status}] ${tool.tool}${tool.summary ? ` - ${tool.summary}` : ""}` };
571
+ }
572
+ case "question":
573
+ return { kind: "line", line: `[question] ${String(data.question ?? "")}` };
574
+ case "patch": {
575
+ const files = Array.isArray(data.files) ? data.files.filter((item) => typeof item === "string") : [];
576
+ return { kind: "line", line: `[patch] ${files.join(", ")}` };
577
+ }
578
+ case "branch_changed":
579
+ return { kind: "line", line: `[branch] ${String(data.branch ?? "")}` };
580
+ case "session_error":
581
+ return { kind: "line", line: `[error] ${String(data.error ?? "Unknown error")}` };
582
+ case "pr_created":
583
+ case "pr_updated":
584
+ return { kind: "line", line: `[pr] ${String(data.prUrl ?? "")}` };
585
+ case "pr_failed":
586
+ return { kind: "line", line: `[pr error] ${String(data.error ?? "Unknown error")}` };
587
+ case "similar_sessions_searching":
588
+ return { kind: "line", line: "[similar] searching for related sessions" };
589
+ case "similar_sessions_found":
590
+ return { kind: "line", line: `[similar] found ${String(data.count ?? 0)} related sessions` };
591
+ case "similar_sessions_none":
592
+ return { kind: "line", line: "[similar] no related sessions found" };
593
+ case "session_idle":
594
+ return { kind: "line", line: "[idle] waiting for next prompt" };
595
+ default:
596
+ return null;
597
+ }
598
+ }
599
+
600
+ // src/commands/transcript.ts
601
+ async function transcriptCommand(sessionId, options) {
602
+ const config = requireConfig();
603
+ try {
604
+ const exportData = await apiFetch(config, `/api/sessions/${sessionId}/export`);
605
+ if (options.json) {
606
+ console.log(JSON.stringify(exportData, null, 2));
607
+ return;
608
+ }
609
+ console.log(renderSessionTranscript(exportData));
610
+ } catch (err) {
611
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
612
+ process.exit(1);
613
+ }
614
+ }
615
+
616
+ // src/constants/watch.ts
617
+ var DEFAULT_WATCH_POLL_INTERVAL_MS = 1e3;
618
+ var WATCH_REPLAY_PAGE_SIZE = 200;
619
+ var WATCH_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["idle", "archived"]);
620
+
621
+ // src/commands/watch.ts
622
+ function sleep(ms) {
623
+ return new Promise((resolve) => setTimeout(resolve, ms));
624
+ }
625
+ function parsePollInterval(raw) {
626
+ if (!raw) return DEFAULT_WATCH_POLL_INTERVAL_MS;
627
+ const parsed = Number.parseInt(raw, 10);
628
+ if (!Number.isFinite(parsed) || parsed < 0) {
629
+ throw new Error("Polling interval must be a non-negative integer.");
630
+ }
631
+ return parsed;
632
+ }
633
+ function formatStatusLine(status) {
634
+ const details = [];
635
+ if (status.title) details.push(status.title);
636
+ if (status.spawnDurationMs != null) details.push(`spawn ${(status.spawnDurationMs / 1e3).toFixed(1)}s`);
637
+ return details.length > 0 ? `[status] ${status.status} | ${details.join(" | ")}` : `[status] ${status.status}`;
638
+ }
639
+ async function fetchPromptLabels(config, sessionId) {
640
+ const data = await apiFetch(config, `/api/sessions/${sessionId}/prompts`);
641
+ return buildPromptLabelMap(data.prompts);
642
+ }
643
+ async function watchCommand(sessionId, options) {
644
+ const config = requireConfig();
645
+ let pollIntervalMs;
646
+ try {
647
+ pollIntervalMs = parsePollInterval(options.pollInterval);
648
+ } catch (err) {
649
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
650
+ process.exit(1);
651
+ }
652
+ let promptLabels = /* @__PURE__ */ new Map();
653
+ try {
654
+ promptLabels = await fetchPromptLabels(config, sessionId);
655
+ } catch (err) {
656
+ console.error(`Warning: failed to fetch prompt labels for session ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
657
+ }
658
+ const renderState = {
659
+ promptLabels,
660
+ toolCalls: /* @__PURE__ */ new Map()
661
+ };
662
+ let afterSequence = 0;
663
+ let lastStatusLine = null;
664
+ let textOpen = false;
665
+ console.log(`Watching session ${sessionId}...`);
666
+ try {
667
+ while (true) {
668
+ const query = new URLSearchParams({
669
+ afterSequence: String(afterSequence),
670
+ limit: String(WATCH_REPLAY_PAGE_SIZE)
671
+ });
672
+ const payload = await apiFetchText(config, `/api/sessions/${sessionId}/events?${query.toString()}`);
673
+ const parsed = parseSsePayload(payload);
674
+ const receivedFullPage = parsed.events.length >= WATCH_REPLAY_PAGE_SIZE;
675
+ if (parsed.status) {
676
+ const nextStatusLine = formatStatusLine(parsed.status);
677
+ if (nextStatusLine !== lastStatusLine) {
678
+ if (textOpen) {
679
+ process.stdout.write("\n");
680
+ textOpen = false;
681
+ }
682
+ console.log(nextStatusLine);
683
+ lastStatusLine = nextStatusLine;
684
+ }
685
+ }
686
+ for (const event of parsed.events) {
687
+ if (typeof event.id === "number" && event.id > afterSequence) afterSequence = event.id;
688
+ const rendered = renderWatchEvent(event, renderState);
689
+ if (!rendered) continue;
690
+ if (rendered.kind === "text") {
691
+ if (!rendered.text) continue;
692
+ if (!textOpen) {
693
+ process.stdout.write("assistant> ");
694
+ textOpen = true;
695
+ }
696
+ process.stdout.write(rendered.text);
697
+ continue;
698
+ }
699
+ if (textOpen) {
700
+ process.stdout.write("\n");
701
+ textOpen = false;
702
+ }
703
+ console.log(rendered.line);
704
+ }
705
+ if (receivedFullPage) continue;
706
+ if (parsed.status?.status && WATCH_TERMINAL_STATUSES.has(parsed.status.status)) break;
707
+ if (pollIntervalMs > 0) {
708
+ await sleep(pollIntervalMs);
709
+ }
710
+ }
711
+ } catch (err) {
712
+ if (textOpen) process.stdout.write("\n");
713
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
714
+ process.exit(1);
715
+ }
716
+ if (textOpen) process.stdout.write("\n");
717
+ }
718
+
185
719
  // src/index.ts
186
- var program = new Command().name("arcanist").description("Arcanist CLI").version("0.1.0");
720
+ var require2 = createRequire(import.meta.url);
721
+ var { version } = require2("../package.json");
722
+ var program = new Command().name("arcanist").description("Arcanist CLI").version(version);
187
723
  program.command("login").description("Authenticate with a personal access token").option("--token-stdin", "Read token from stdin instead of interactive prompt").option("--api-url <url>", "Set custom API URL").action(loginCommand);
188
724
  program.command("create").description("Create a session and send a prompt").argument("<repo-url>", "Repository URL").argument("<prompt>", "Prompt to send").option("--model <model>", "Model to use").action(createCommand);
189
725
  program.command("message").description("Send a message to an existing session").argument("<session-id>", "Session ID").argument("<prompt>", "Message to send").action(messageCommand);
726
+ program.command("stop").description("Stop the active run for a session").argument("<session-id>", "Session ID").action(stopCommand);
727
+ program.command("transcript").description("Render a session transcript").argument("<session-id>", "Session ID").option("--json", "Output raw session export JSON").action(transcriptCommand);
728
+ program.command("watch").description("Watch session activity until it becomes idle").argument("<session-id>", "Session ID").option("--poll-interval <ms>", "Polling interval in milliseconds", String(DEFAULT_WATCH_POLL_INTERVAL_MS)).action(watchCommand);
190
729
  async function main() {
191
730
  try {
192
731
  await program.parseAsync(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,6 +27,7 @@
27
27
  "commander": "^12.1.0"
28
28
  },
29
29
  "devDependencies": {
30
- "tsup": "^8.0.0"
30
+ "tsup": "^8.0.0",
31
+ "typescript": "^5.9.3"
31
32
  }
32
33
  }