@tryarcanist/cli 0.1.85 → 0.1.87

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 +89 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -562,6 +562,50 @@ function normalizeUploadedFileOptions(files) {
562
562
  return Array.isArray(files) ? files : [files];
563
563
  }
564
564
 
565
+ // ../../shared/session/no-change-outcome.ts
566
+ function isNoChangesPromptResult(result) {
567
+ return typeof result === "object" && result !== null && "noChanges" in result && result.noChanges === true;
568
+ }
569
+ function noChangeOutcomeCopy(reason) {
570
+ switch (reason) {
571
+ case "no_diff":
572
+ return {
573
+ state: "no_changes",
574
+ tone: "info",
575
+ title: "Completed without code changes - no PR created.",
576
+ detail: null
577
+ };
578
+ case "no_staged_files":
579
+ return {
580
+ state: "no_changes",
581
+ tone: "info",
582
+ title: "Completed without publishable code changes - no PR created.",
583
+ detail: null
584
+ };
585
+ case "repoless_benchmark":
586
+ return {
587
+ state: "no_changes",
588
+ tone: "info",
589
+ title: "Completed without code changes - no PR created.",
590
+ detail: null
591
+ };
592
+ case "prep_failed":
593
+ return {
594
+ state: "no_change_abnormal",
595
+ tone: "error",
596
+ title: "Finalization failed before changes could be prepared.",
597
+ detail: null
598
+ };
599
+ default:
600
+ return {
601
+ state: "no_change_abnormal",
602
+ tone: "error",
603
+ title: "Completed without code changes, but finalization did not report a clean no-op reason.",
604
+ detail: null
605
+ };
606
+ }
607
+ }
608
+
565
609
  // ../../shared/utils/type-guards.ts
566
610
  function isRecord(value) {
567
611
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -1377,6 +1421,7 @@ var ERROR_CODES = [
1377
1421
  "codex_session_create_timeout",
1378
1422
  "codex_prompt_dispatch_timeout",
1379
1423
  "codex_not_ready",
1424
+ "codex_transport_closed",
1380
1425
  "codex_unrecoverable",
1381
1426
  "unknown"
1382
1427
  ];
@@ -1410,6 +1455,7 @@ var ERROR_CODE_LABELS = {
1410
1455
  codex_session_create_timeout: "Codex session creation timed out",
1411
1456
  codex_prompt_dispatch_timeout: "Codex prompt dispatch timed out",
1412
1457
  codex_not_ready: "Codex did not become ready",
1458
+ codex_transport_closed: "Codex transport closed",
1413
1459
  codex_unrecoverable: "Codex unrecoverable failure",
1414
1460
  unknown: "Unknown failure"
1415
1461
  };
@@ -1521,6 +1567,12 @@ ${event.answer ? `**Answer:** ${event.answer}
1521
1567
  function formatNumber(value) {
1522
1568
  return value.toLocaleString();
1523
1569
  }
1570
+ function latestCompletedPromptResult(prompts) {
1571
+ for (let i = prompts.length - 1; i >= 0; i--) {
1572
+ if (prompts[i].status === "completed") return prompts[i].result;
1573
+ }
1574
+ return null;
1575
+ }
1524
1576
  function renderSessionTranscript(exportData) {
1525
1577
  const lines = [];
1526
1578
  const promptIds = exportData.prompts.map((prompt) => prompt.id);
@@ -1530,6 +1582,10 @@ function renderSessionTranscript(exportData) {
1530
1582
  lines.push(`**Session:** ${exportData.session.id.slice(0, 8)} `);
1531
1583
  lines.push(`**Created:** ${formatDate(exportData.session.createdAt)} `);
1532
1584
  lines.push(`**Status:** ${exportData.session.status} `);
1585
+ const noChangeResult = latestCompletedPromptResult(exportData.prompts);
1586
+ if (!exportData.pr?.url && isNoChangesPromptResult(noChangeResult)) {
1587
+ lines.push(`**Outcome:** ${noChangeOutcomeCopy(noChangeResult.noChangeReason).title} `);
1588
+ }
1533
1589
  lines.push(
1534
1590
  `**Tokens:** ${formatNumber(exportData.tokens.inputTokens)} in / ${formatNumber(exportData.tokens.outputTokens)} out / ${formatNumber(exportData.tokens.totalTokens)} total`
1535
1591
  );
@@ -1795,6 +1851,24 @@ function formatStatusLine(status) {
1795
1851
  if (status.spawnDurationMs != null) details.push(`spawn ${(status.spawnDurationMs / 1e3).toFixed(1)}s`);
1796
1852
  return details.length > 0 ? `[status] ${phaseLabel} | ${details.join(" | ")}` : `[status] ${phaseLabel}`;
1797
1853
  }
1854
+ async function printNoChangeOutcome(config, sessionId) {
1855
+ try {
1856
+ const data = await apiFetch(
1857
+ config,
1858
+ `/api/sessions/${sessionId}/prompts`
1859
+ );
1860
+ const prompts = data.prompts ?? [];
1861
+ for (let i = prompts.length - 1; i >= 0; i--) {
1862
+ if (prompts[i].status !== "completed") continue;
1863
+ const result = prompts[i].result;
1864
+ if (isNoChangesPromptResult(result)) {
1865
+ console.log(`[outcome] ${noChangeOutcomeCopy(result.noChangeReason).title}`);
1866
+ }
1867
+ break;
1868
+ }
1869
+ } catch {
1870
+ }
1871
+ }
1798
1872
  async function fetchPromptLabels(config, sessionId) {
1799
1873
  const data = await apiFetch(config, `/api/sessions/${sessionId}/prompts`);
1800
1874
  return buildPromptLabelMap(data.prompts);
@@ -1873,7 +1947,16 @@ async function watchCommand(sessionId, options, command) {
1873
1947
  console.log(rendered.line);
1874
1948
  }
1875
1949
  if (receivedFullPage) continue;
1876
- if (parsed.status?.phase && isWatchTerminal(parsed.status.phase, parsed.status.sessionKind)) break;
1950
+ if (parsed.status?.phase && isWatchTerminal(parsed.status.phase, parsed.status.sessionKind)) {
1951
+ if (!json && parsed.status.phase === "completed") {
1952
+ if (textOpen) {
1953
+ process.stdout.write("\n");
1954
+ textOpen = false;
1955
+ }
1956
+ await printNoChangeOutcome(config, sessionId);
1957
+ }
1958
+ break;
1959
+ }
1877
1960
  await sleep(effectivePollIntervalMs);
1878
1961
  }
1879
1962
  } finally {
@@ -2106,7 +2189,6 @@ async function listSessionsCommand(options, command) {
2106
2189
  if (options.status) query.set("status", options.status);
2107
2190
  if (options.scope) query.set("scope", options.scope);
2108
2191
  if (options.search) query.set("q", options.search);
2109
- if (options.tag) query.set("tag", options.tag);
2110
2192
  if (options.repo) query.set("repo", options.repo);
2111
2193
  if (options.limit) query.set("limit", options.limit);
2112
2194
  if (options.cursor) query.set("cursor", options.cursor);
@@ -2126,9 +2208,7 @@ async function listSessionsCommand(options, command) {
2126
2208
  const id = String(session.id ?? session.sessionId ?? "");
2127
2209
  const status = String(session.status ?? "");
2128
2210
  const title = typeof session.title === "string" ? ` ${session.title}` : "";
2129
- const tags = Array.isArray(session.titleTags) ? session.titleTags.filter((tag) => typeof tag === "string") : [];
2130
- const tagText = tags.length > 0 ? ` tags: ${tags.join(", ")}` : "";
2131
- console.log(`${id} ${status}${title}${tagText}`);
2211
+ console.log(`${id} ${status}${title}`);
2132
2212
  }
2133
2213
  if (payload.nextCursor) console.log(`Next cursor: ${payload.nextCursor}`);
2134
2214
  }
@@ -2525,21 +2605,21 @@ Examples:
2525
2605
  arcanist sessions get <session-id> --json
2526
2606
  `
2527
2607
  ).action((sessionId, options, command) => getSessionCommand(sessionId, options, command));
2528
- sessions.command("list").description("List sessions").option("--status <status>", "Filter by session status").option("--scope <scope>", "Session scope: mine or business").option("--search <query>", "Search session titles, tags, and repo metadata").option("--tag <tag>", "Filter by generated session tag").option("--repo <repo>", "Filter by repo metadata").option("--limit <n>", "Maximum sessions to return").option("--cursor <cursor>", "Pagination cursor").addHelpText(
2608
+ sessions.command("list").description("List sessions").option("--status <status>", "Filter by session status").option("--scope <scope>", "Session scope: mine or business").option("--search <query>", "Search session titles and repo metadata").option("--repo <repo>", "Filter by repo metadata").option("--limit <n>", "Maximum sessions to return").option("--cursor <cursor>", "Pagination cursor").addHelpText(
2529
2609
  "after",
2530
2610
  `
2531
2611
  Examples:
2532
2612
  arcanist sessions list
2533
2613
  arcanist sessions list --status idle --json
2534
- arcanist sessions list --search "architect agent" --tag codex
2614
+ arcanist sessions list --search "architect agent" --repo owner/repo
2535
2615
  `
2536
2616
  ).action((options, command) => listSessionsCommand(options, command));
2537
- sessions.command("search").description("Search sessions by title, tag, and repo metadata").argument("<query>", "Search query").option("--status <status>", "Filter by session status").option("--scope <scope>", "Session scope: mine or business").option("--tag <tag>", "Filter by generated session tag").option("--repo <repo>", "Filter by repo metadata").option("--limit <n>", "Maximum sessions to return").option("--cursor <cursor>", "Pagination cursor").addHelpText(
2617
+ sessions.command("search").description("Search sessions by title and repo metadata").argument("<query>", "Search query").option("--status <status>", "Filter by session status").option("--scope <scope>", "Session scope: mine or business").option("--repo <repo>", "Filter by repo metadata").option("--limit <n>", "Maximum sessions to return").option("--cursor <cursor>", "Pagination cursor").addHelpText(
2538
2618
  "after",
2539
2619
  `
2540
2620
  Examples:
2541
2621
  arcanist sessions search "architect agent"
2542
- arcanist sessions search "mcp debugging" --tag codex --json
2622
+ arcanist sessions search "mcp debugging" --repo owner/repo --json
2543
2623
  `
2544
2624
  ).action((query, options, command) => searchSessionsCommand(query, options, command));
2545
2625
  sessions.command("events").description("Read or follow session replay events").argument("<session-id>", "Session ID").option("--after-sequence <n>", "Return events after this sequence").option("--after <n>", "Alias for --after-sequence").option("--before-sequence <n>", "Return events before this sequence").option("--before <n>", "Alias for --before-sequence").option("--prompt-id <id>", "Filter events by prompt ID").option("--limit <n>", "Maximum events to return").option("--follow", "Follow events until the session is idle").option("--poll-interval <ms>", "Polling interval in milliseconds", String(DEFAULT_WATCH_POLL_INTERVAL_MS)).addHelpText(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.85",
3
+ "version": "0.1.87",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {