@h-rig/cli 0.0.6-alpha.66 → 0.0.6-alpha.68

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.
@@ -188,9 +188,21 @@ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
188
188
  return;
189
189
  writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
190
190
  }
191
+ function isRemoteConnectionSelected(projectRoot) {
192
+ return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
193
+ }
191
194
 
192
195
  // packages/cli/src/commands/_server-client.ts
193
196
  var scopedGitHubBearerTokens = new Map;
197
+ var serverPhaseListener = null;
198
+ function setServerPhaseListener(listener) {
199
+ const previous = serverPhaseListener;
200
+ serverPhaseListener = listener;
201
+ return previous;
202
+ }
203
+ function reportServerPhase(label) {
204
+ serverPhaseListener?.(label);
205
+ }
194
206
  function cleanToken(value) {
195
207
  const trimmed = value?.trim();
196
208
  return trimmed ? trimmed : null;
@@ -233,6 +245,7 @@ async function ensureServerForCli(projectRoot) {
233
245
  try {
234
246
  const selected = resolveSelectedConnection(projectRoot);
235
247
  if (selected?.connection.kind === "remote") {
248
+ reportServerPhase(`Connecting to ${selected.alias}\u2026`);
236
249
  const authToken = readGitHubBearerTokenForRemote(projectRoot);
237
250
  const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
238
251
  return {
@@ -242,6 +255,7 @@ async function ensureServerForCli(projectRoot) {
242
255
  serverProjectRoot
243
256
  };
244
257
  }
258
+ reportServerPhase("Starting local Rig server\u2026");
245
259
  const connection = await ensureLocalRigServerConnection(projectRoot);
246
260
  return {
247
261
  baseUrl: connection.baseUrl,
@@ -348,6 +362,7 @@ async function requestServerJson(context, pathname, init = {}) {
348
362
  const headers = mergeHeaders(init.headers, server.authToken);
349
363
  if (server.serverProjectRoot)
350
364
  headers.set("x-rig-project-root", server.serverProjectRoot);
365
+ reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
351
366
  let response;
352
367
  try {
353
368
  response = await fetch(`${server.baseUrl}${pathname}`, {
@@ -808,7 +823,12 @@ async function attachRunBundledPiFrontend(context, input) {
808
823
  };
809
824
  let detached = false;
810
825
  try {
811
- await runPiMain([], {
826
+ await runPiMain([
827
+ "--no-extensions",
828
+ "--no-skills",
829
+ "--no-prompt-templates",
830
+ "--no-context-files"
831
+ ], {
812
832
  extensionFactories: [piRigExtensionFactory]
813
833
  });
814
834
  detached = true;
@@ -831,6 +851,127 @@ async function attachRunBundledPiFrontend(context, input) {
831
851
  };
832
852
  }
833
853
 
854
+ // packages/cli/src/commands/_async-ui.ts
855
+ import pc from "picocolors";
856
+
857
+ // packages/cli/src/commands/_spinner.ts
858
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
859
+ function createTtySpinner(input) {
860
+ const output = input.output ?? process.stdout;
861
+ const isTty = output.isTTY === true;
862
+ const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
863
+ let label = input.label;
864
+ let frame = 0;
865
+ let paused = false;
866
+ let stopped = false;
867
+ let lastPrintedLabel = "";
868
+ const render = () => {
869
+ if (stopped || paused)
870
+ return;
871
+ if (!isTty) {
872
+ if (label !== lastPrintedLabel) {
873
+ output.write(`${label}
874
+ `);
875
+ lastPrintedLabel = label;
876
+ }
877
+ return;
878
+ }
879
+ frame = (frame + 1) % frames.length;
880
+ const glyph = frames[frame] ?? frames[0] ?? "";
881
+ output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
882
+ };
883
+ const clearLine = () => {
884
+ if (isTty)
885
+ output.write("\r\x1B[2K");
886
+ };
887
+ render();
888
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
889
+ return {
890
+ setLabel(next) {
891
+ label = next;
892
+ render();
893
+ },
894
+ pause() {
895
+ paused = true;
896
+ clearLine();
897
+ },
898
+ resume() {
899
+ if (stopped)
900
+ return;
901
+ paused = false;
902
+ render();
903
+ },
904
+ stop(finalLine) {
905
+ if (stopped)
906
+ return;
907
+ stopped = true;
908
+ if (timer)
909
+ clearInterval(timer);
910
+ clearLine();
911
+ if (finalLine)
912
+ output.write(`${finalLine}
913
+ `);
914
+ }
915
+ };
916
+ }
917
+
918
+ // packages/cli/src/commands/_async-ui.ts
919
+ var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
920
+ var DONE_SYMBOL = pc.green("\u25C7");
921
+ var FAIL_SYMBOL = pc.red("\u25A0");
922
+ var activeUpdate = null;
923
+ async function withSpinner(label, work, options = {}) {
924
+ if (options.outputMode === "json") {
925
+ return work(() => {});
926
+ }
927
+ if (activeUpdate) {
928
+ const outer = activeUpdate;
929
+ outer(label);
930
+ return work(outer);
931
+ }
932
+ const output = options.output ?? process.stderr;
933
+ const isTty = output.isTTY === true;
934
+ let lastLabel = label;
935
+ if (!isTty) {
936
+ output.write(`${label}
937
+ `);
938
+ const update2 = (next) => {
939
+ lastLabel = next;
940
+ };
941
+ activeUpdate = update2;
942
+ const previousListener2 = setServerPhaseListener(update2);
943
+ try {
944
+ return await work(update2);
945
+ } finally {
946
+ activeUpdate = null;
947
+ setServerPhaseListener(previousListener2);
948
+ }
949
+ }
950
+ const spinner = createTtySpinner({
951
+ label,
952
+ output,
953
+ frames: CLACK_SPINNER_FRAMES,
954
+ styleFrame: (frame) => pc.magenta(frame)
955
+ });
956
+ const update = (next) => {
957
+ lastLabel = next;
958
+ spinner.setLabel(next);
959
+ };
960
+ activeUpdate = update;
961
+ const previousListener = setServerPhaseListener(update);
962
+ try {
963
+ const result = await work(update);
964
+ spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
965
+ return result;
966
+ } catch (error) {
967
+ spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
968
+ throw error;
969
+ } finally {
970
+ activeUpdate = null;
971
+ setServerPhaseListener(previousListener);
972
+ }
973
+ }
974
+
834
975
  // packages/cli/src/commands/_operator-view.ts
835
976
  var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
836
977
  function runStatusFromPayload(payload) {
@@ -873,8 +1014,9 @@ async function readOperatorSnapshot(context, runId, options = {}) {
873
1014
  }
874
1015
  async function attachRunOperatorView(context, input) {
875
1016
  let steered = false;
876
- if (input.message?.trim()) {
877
- await sendRunPiPromptViaServer(context, input.runId, input.message.trim(), "steer").catch(() => steerRunViaServer(context, input.runId, input.message.trim()));
1017
+ const attachMessage = input.message?.trim();
1018
+ if (attachMessage) {
1019
+ await withSpinner("Queueing steering message\u2026", () => sendRunPiPromptViaServer(context, input.runId, attachMessage, "steer").catch(() => steerRunViaServer(context, input.runId, attachMessage)), { outputMode: context.outputMode });
878
1020
  steered = true;
879
1021
  }
880
1022
  if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
@@ -884,7 +1026,7 @@ async function attachRunOperatorView(context, input) {
884
1026
  });
885
1027
  }
886
1028
  const surface = createOperatorSurface({ interactive: input.interactive !== false });
887
- let snapshot = await readOperatorSnapshot(context, input.runId);
1029
+ let snapshot = await withSpinner(`Connecting to run ${input.runId}\u2026`, () => readOperatorSnapshot(context, input.runId), { outputMode: context.outputMode });
888
1030
  if (context.outputMode === "text") {
889
1031
  surface.renderSnapshot(snapshot);
890
1032
  surface.renderTimeline(snapshot.timeline);
@@ -924,7 +1066,7 @@ async function attachRunOperatorView(context, input) {
924
1066
 
925
1067
  // packages/cli/src/commands/_cli-format.ts
926
1068
  import { log, note } from "@clack/prompts";
927
- import pc from "picocolors";
1069
+ import pc2 from "picocolors";
928
1070
  function stringField(record, key, fallback = "") {
929
1071
  const value = record[key];
930
1072
  return typeof value === "string" && value.trim() ? value.trim() : fallback;
@@ -946,14 +1088,14 @@ function pad(value, width) {
946
1088
  function statusColor(status) {
947
1089
  const normalized = status.toLowerCase();
948
1090
  if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved"].includes(normalized))
949
- return pc.green;
1091
+ return pc2.green;
950
1092
  if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
951
- return pc.red;
1093
+ return pc2.red;
952
1094
  if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
953
- return pc.cyan;
1095
+ return pc2.cyan;
954
1096
  if (["ready", "open", "queued", "created", "preparing", "local", "pending"].includes(normalized))
955
- return pc.yellow;
956
- return pc.dim;
1097
+ return pc2.yellow;
1098
+ return pc2.dim;
957
1099
  }
958
1100
  function compactDate(value) {
959
1101
  if (!value.trim())
@@ -998,23 +1140,23 @@ function formatStatusPill(status) {
998
1140
  return statusColor(label)(`\u25CF ${label}`);
999
1141
  }
1000
1142
  function formatSection(title, subtitle) {
1001
- return `${pc.bold(pc.cyan("\u25C6"))} ${pc.bold(title)}${subtitle ? pc.dim(` \u2014 ${subtitle}`) : ""}`;
1143
+ return `${pc2.bold(pc2.cyan("\u25C6"))} ${pc2.bold(title)}${subtitle ? pc2.dim(` \u2014 ${subtitle}`) : ""}`;
1002
1144
  }
1003
1145
  function formatSuccessCard(title, rows = []) {
1004
- const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${pc.dim("\u2502")} ${pc.dim(key.padEnd(12))} ${value}`);
1146
+ const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${pc2.dim("\u2502")} ${pc2.dim(key.padEnd(12))} ${value}`);
1005
1147
  return [formatSection(title), ...body].join(`
1006
1148
  `);
1007
1149
  }
1008
1150
  function formatNextSteps(steps) {
1009
1151
  if (steps.length === 0)
1010
1152
  return [];
1011
- return [pc.bold("Next"), ...steps.map((step) => `${pc.dim("\u203A")} ${step}`)];
1153
+ return [pc2.bold("Next"), ...steps.map((step) => `${pc2.dim("\u203A")} ${step}`)];
1012
1154
  }
1013
1155
  function formatRunList(runs, options = {}) {
1014
1156
  if (runs.length === 0) {
1015
1157
  return [
1016
1158
  formatSection("Runs", "none recorded"),
1017
- options.source === "server" ? pc.dim("No runs recorded on the selected Rig server.") : pc.dim("No runs recorded in .rig/runs."),
1159
+ options.source === "server" ? pc2.dim("No runs recorded on the selected Rig server.") : pc2.dim("No runs recorded in .rig/runs."),
1018
1160
  "",
1019
1161
  ...formatNextSteps(["Start one: `rig task run --next`", "Check server: `rig server status`"])
1020
1162
  ].join(`
@@ -1030,11 +1172,11 @@ function formatRunList(runs, options = {}) {
1030
1172
  });
1031
1173
  const idWidth = Math.min(36, Math.max(6, ...rows.map((row) => row.runId.length)));
1032
1174
  const statusWidth = Math.min(16, Math.max(6, ...rows.map((row) => row.status.length)));
1033
- const header = `${pc.bold(pad("RUN", idWidth))} ${pc.bold(pad("STATUS", statusWidth))} ${pc.bold("TITLE")}`;
1175
+ const header = `${pc2.bold(pad("RUN", idWidth))} ${pc2.bold(pad("STATUS", statusWidth))} ${pc2.bold("TITLE")}`;
1034
1176
  const body = rows.map((row) => [
1035
- pc.bold(pad(truncate(row.runId, idWidth), idWidth)),
1177
+ pc2.bold(pad(truncate(row.runId, idWidth), idWidth)),
1036
1178
  statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
1037
- `${row.title}${row.runtime ? pc.dim(` ${row.runtime}`) : ""}`
1179
+ `${row.title}${row.runtime ? pc2.dim(` ${row.runtime}`) : ""}`
1038
1180
  ].join(" "));
1039
1181
  return [formatSection("Runs", options.source === "server" ? "selected server" : "local state"), header, ...body, "", ...formatNextSteps(["Follow live: `rig run attach <run-id> --follow`", "Details: `rig run show <run-id>`"])].join(`
1040
1182
  `);
@@ -1059,7 +1201,7 @@ function formatRunCard(run, options = {}) {
1059
1201
  const approvals = Array.isArray(merged.approvals) ? merged.approvals.length : null;
1060
1202
  const inputs = Array.isArray(merged.userInputs) ? merged.userInputs.length : null;
1061
1203
  const rows = [
1062
- ["run", pc.bold(runId)],
1204
+ ["run", pc2.bold(runId)],
1063
1205
  ["status", formatStatusPill(status)],
1064
1206
  ["task", taskId],
1065
1207
  ["title", title],
@@ -1085,17 +1227,17 @@ function formatRunStatus(summary, options = {}) {
1085
1227
  const activeRuns = summary.activeRuns ?? [];
1086
1228
  const recentRuns = summary.recentRuns ?? [];
1087
1229
  const lines = [formatSection("Run status", options.source === "server" ? "selected server" : "local state")];
1088
- lines.push("", pc.bold(`Active runs (${activeRuns.length})`));
1230
+ lines.push("", pc2.bold(`Active runs (${activeRuns.length})`));
1089
1231
  if (activeRuns.length === 0) {
1090
- lines.push(pc.dim("No active runs."));
1232
+ lines.push(pc2.dim("No active runs."));
1091
1233
  } else {
1092
1234
  for (const run of activeRuns) {
1093
1235
  lines.push(formatRunSummaryLine(run));
1094
1236
  }
1095
1237
  }
1096
- lines.push("", pc.bold(`Recent runs (${recentRuns.length})`));
1238
+ lines.push("", pc2.bold(`Recent runs (${recentRuns.length})`));
1097
1239
  if (recentRuns.length === 0) {
1098
- lines.push(pc.dim("No recent terminal runs."));
1240
+ lines.push(pc2.dim("No recent terminal runs."));
1099
1241
  } else {
1100
1242
  for (const run of recentRuns.slice(0, 10)) {
1101
1243
  lines.push(formatRunSummaryLine(run));
@@ -1113,7 +1255,7 @@ function formatRunSummaryLine(run) {
1113
1255
  const title = runTitleOf(record);
1114
1256
  const runtime = firstString(record, ["runtimeAdapter", "runtime", "adapter"]);
1115
1257
  const descriptor = [taskId, title].filter(Boolean).join(" \xB7 ");
1116
- return `${pc.dim("\u2502")} ${pc.bold(runId)} ${formatStatusPill(status)} ${descriptor}${runtime ? pc.dim(` ${runtime}`) : ""}`;
1258
+ return `${pc2.dim("\u2502")} ${pc2.bold(runId)} ${formatStatusPill(status)} ${descriptor}${runtime ? pc2.dim(` ${runtime}`) : ""}`;
1117
1259
  }
1118
1260
 
1119
1261
  // packages/cli/src/commands/inbox.ts
@@ -1167,9 +1309,6 @@ function normalizeRemoteRunDetails(payload) {
1167
1309
  };
1168
1310
  }
1169
1311
  var REMOTE_TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged"]);
1170
- function isRemoteConnectionSelected(projectRoot) {
1171
- return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
1172
- }
1173
1312
  async function listRunsForSelectedConnection(context, options = {}) {
1174
1313
  if (isRemoteConnectionSelected(context.projectRoot)) {
1175
1314
  return { runs: await listRunsViaServer(context, options), source: "server" };
@@ -1250,7 +1389,7 @@ async function executeRun(context, args) {
1250
1389
  switch (command) {
1251
1390
  case "list": {
1252
1391
  requireNoExtraArgs(rest, "rig run list");
1253
- const { runs, source } = await listRunsForSelectedConnection(context, { limit: 100 });
1392
+ const { runs, source } = isRemoteConnectionSelected(context.projectRoot) ? await withSpinner("Reading runs from server\u2026", () => listRunsForSelectedConnection(context, { limit: 100 }), { outputMode: context.outputMode }) : await listRunsForSelectedConnection(context, { limit: 100 });
1254
1393
  if (context.outputMode === "text") {
1255
1394
  printFormattedOutput(formatRunList(runs, { source }));
1256
1395
  await printPendingInboxFooter(context);
@@ -1323,7 +1462,7 @@ async function executeRun(context, args) {
1323
1462
  if (!runId) {
1324
1463
  throw new CliError("run show requires a run id.", 1, { hint: "Run `rig run list` to find run ids, then `rig run show <run-id>`." });
1325
1464
  }
1326
- const record = readAuthorityRun2(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, runId).catch(() => ({})));
1465
+ const record = readAuthorityRun2(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await withSpinner(`Reading run ${runId} from server\u2026`, () => getRunDetailsViaServer(context, runId).catch(() => ({})), { outputMode: context.outputMode }));
1327
1466
  if (!record) {
1328
1467
  throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see recorded runs." });
1329
1468
  }
@@ -1344,7 +1483,8 @@ async function executeRun(context, args) {
1344
1483
  }
1345
1484
  const renderer = createPiRunStreamRenderer();
1346
1485
  let cursor = null;
1347
- const page = await getRunTimelineViaServer(context, run.value, { limit: 500 });
1486
+ const timelineRunId = run.value;
1487
+ const page = await withSpinner(`Reading timeline for ${timelineRunId}\u2026`, () => getRunTimelineViaServer(context, timelineRunId, { limit: 500 }), { outputMode: context.outputMode });
1348
1488
  const events = Array.isArray(page.entries) ? page.entries.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
1349
1489
  cursor = typeof page.nextCursor === "string" ? page.nextCursor : null;
1350
1490
  if (context.outputMode === "text") {
@@ -1421,8 +1561,9 @@ async function executeRun(context, args) {
1421
1561
  throw new CliError("run attach requires a run id.", 2, { hint: "Run `rig run list` to find run ids, then `rig run attach <run-id> --follow`." });
1422
1562
  }
1423
1563
  let steered = false;
1424
- if (messageOption.value?.trim()) {
1425
- await steerRunViaServer(context, runId, messageOption.value.trim());
1564
+ const steerMessage = messageOption.value?.trim();
1565
+ if (steerMessage) {
1566
+ await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, steerMessage), { outputMode: context.outputMode });
1426
1567
  steered = true;
1427
1568
  }
1428
1569
  const attached = await attachRunOperatorView(context, {
@@ -1442,7 +1583,7 @@ async function executeRun(context, args) {
1442
1583
  }
1443
1584
  return { ok: true, group: "run", command };
1444
1585
  }
1445
- const summary = isRemoteConnectionSelected(context.projectRoot) ? buildServerRunStatus(await listRunsViaServer(context, { limit: 100 })) : runStatus(context.projectRoot, runtimeContext);
1586
+ const summary = isRemoteConnectionSelected(context.projectRoot) ? buildServerRunStatus(await withSpinner("Reading run status from server\u2026", () => listRunsViaServer(context, { limit: 100 }), { outputMode: context.outputMode })) : runStatus(context.projectRoot, runtimeContext);
1446
1587
  const activeRuns = Array.isArray(summary.activeRuns) ? summary.activeRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
1447
1588
  const recentRuns = Array.isArray(summary.recentRuns) ? summary.recentRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
1448
1589
  if (context.outputMode === "text") {
@@ -1519,28 +1660,38 @@ async function executeRun(context, args) {
1519
1660
  };
1520
1661
  }
1521
1662
  case "resume": {
1522
- requireNoExtraArgs(rest, "rig run resume");
1663
+ let pending = rest;
1664
+ const runOpt = takeOption(pending, "--run");
1665
+ pending = runOpt.rest;
1666
+ const positional = pending[0] && !pending[0].startsWith("-") ? pending.shift() : undefined;
1667
+ requireNoExtraArgs(pending, "rig run resume [<run-id>] [--run <id>]");
1668
+ const targetRunId = runOpt.value ?? positional ?? null;
1523
1669
  if (context.dryRun) {
1524
1670
  if (context.outputMode === "text") {
1525
1671
  console.log("[dry-run] rig run resume");
1526
1672
  }
1527
1673
  return { ok: true, group: "run", command };
1528
1674
  }
1529
- const resumed = await runResume(context.projectRoot, runtimeContext);
1675
+ const resumed = await runResume(context.projectRoot, runtimeContext, targetRunId);
1530
1676
  if (context.outputMode === "text") {
1531
1677
  console.log(`Resumed run: ${resumed.runId}`);
1532
1678
  }
1533
1679
  return { ok: true, group: "run", command, details: resumed };
1534
1680
  }
1535
1681
  case "restart": {
1536
- requireNoExtraArgs(rest, "rig run restart");
1682
+ let pending = rest;
1683
+ const runOpt = takeOption(pending, "--run");
1684
+ pending = runOpt.rest;
1685
+ const positional = pending[0] && !pending[0].startsWith("-") ? pending.shift() : undefined;
1686
+ requireNoExtraArgs(pending, "rig run restart [<run-id>] [--run <id>]");
1687
+ const targetRunId = runOpt.value ?? positional ?? null;
1537
1688
  if (context.dryRun) {
1538
1689
  if (context.outputMode === "text") {
1539
1690
  console.log("[dry-run] rig run restart");
1540
1691
  }
1541
1692
  return { ok: true, group: "run", command };
1542
1693
  }
1543
- const restarted = await runRestart(context.projectRoot, runtimeContext);
1694
+ const restarted = await runRestart(context.projectRoot, runtimeContext, targetRunId);
1544
1695
  if (context.outputMode === "text") {
1545
1696
  console.log(`Restarted run: ${restarted.runId}`);
1546
1697
  }
@@ -1567,7 +1718,8 @@ async function executeRun(context, args) {
1567
1718
  }
1568
1719
  return { ok: true, group: "run", command, details: { runId, dryRun: true } };
1569
1720
  }
1570
- await steerRunViaServer(context, runId, message.trim());
1721
+ const trimmedMessage = message.trim();
1722
+ await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, trimmedMessage), { outputMode: context.outputMode });
1571
1723
  if (context.outputMode === "text") {
1572
1724
  console.log(`Steering message queued for ${runId}.`);
1573
1725
  }
@@ -1588,7 +1740,7 @@ async function executeRun(context, args) {
1588
1740
  };
1589
1741
  }
1590
1742
  if (runId) {
1591
- const stopped = await stopRunViaServer(context, runId);
1743
+ const stopped = await withSpinner(`Requesting stop for ${runId}\u2026`, () => stopRunViaServer(context, runId), { outputMode: context.outputMode });
1592
1744
  if (context.outputMode === "text")
1593
1745
  console.log(`Stop requested: ${runId}`);
1594
1746
  return { ok: true, group: "run", command, details: stopped };
@@ -361,6 +361,10 @@ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
361
361
  import { resolve as resolve2 } from "path";
362
362
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
363
363
  var scopedGitHubBearerTokens = new Map;
364
+ var serverPhaseListener = null;
365
+ function reportServerPhase(label) {
366
+ serverPhaseListener?.(label);
367
+ }
364
368
  function cleanToken(value) {
365
369
  const trimmed = value?.trim();
366
370
  return trimmed ? trimmed : null;
@@ -403,6 +407,7 @@ async function ensureServerForCli(projectRoot) {
403
407
  try {
404
408
  const selected = resolveSelectedConnection(projectRoot);
405
409
  if (selected?.connection.kind === "remote") {
410
+ reportServerPhase(`Connecting to ${selected.alias}\u2026`);
406
411
  const authToken = readGitHubBearerTokenForRemote(projectRoot);
407
412
  const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
408
413
  return {
@@ -412,6 +417,7 @@ async function ensureServerForCli(projectRoot) {
412
417
  serverProjectRoot
413
418
  };
414
419
  }
420
+ reportServerPhase("Starting local Rig server\u2026");
415
421
  const connection = await ensureLocalRigServerConnection(projectRoot);
416
422
  return {
417
423
  baseUrl: connection.baseUrl,
@@ -518,6 +524,7 @@ async function requestServerJson(context, pathname, init = {}) {
518
524
  const headers = mergeHeaders(init.headers, server.authToken);
519
525
  if (server.serverProjectRoot)
520
526
  headers.set("x-rig-project-root", server.serverProjectRoot);
527
+ reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
521
528
  let response;
522
529
  try {
523
530
  response = await fetch(`${server.baseUrl}${pathname}`, {