@h-rig/cli 0.0.6-alpha.67 → 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.
@@ -2740,6 +2740,15 @@ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
2740
2740
  import { resolve as resolve10 } from "path";
2741
2741
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
2742
2742
  var scopedGitHubBearerTokens = new Map;
2743
+ var serverPhaseListener = null;
2744
+ function setServerPhaseListener(listener) {
2745
+ const previous = serverPhaseListener;
2746
+ serverPhaseListener = listener;
2747
+ return previous;
2748
+ }
2749
+ function reportServerPhase(label) {
2750
+ serverPhaseListener?.(label);
2751
+ }
2743
2752
  function cleanToken(value) {
2744
2753
  const trimmed = value?.trim();
2745
2754
  return trimmed ? trimmed : null;
@@ -2786,6 +2795,7 @@ async function ensureServerForCli(projectRoot) {
2786
2795
  try {
2787
2796
  const selected = resolveSelectedConnection(projectRoot);
2788
2797
  if (selected?.connection.kind === "remote") {
2798
+ reportServerPhase(`Connecting to ${selected.alias}\u2026`);
2789
2799
  const authToken = readGitHubBearerTokenForRemote(projectRoot);
2790
2800
  const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
2791
2801
  return {
@@ -2795,6 +2805,7 @@ async function ensureServerForCli(projectRoot) {
2795
2805
  serverProjectRoot
2796
2806
  };
2797
2807
  }
2808
+ reportServerPhase("Starting local Rig server\u2026");
2798
2809
  const connection = await ensureLocalRigServerConnection(projectRoot);
2799
2810
  return {
2800
2811
  baseUrl: connection.baseUrl,
@@ -2911,6 +2922,7 @@ async function requestServerJson(context, pathname, init = {}) {
2911
2922
  const headers = mergeHeaders(init.headers, server.authToken);
2912
2923
  if (server.serverProjectRoot)
2913
2924
  headers.set("x-rig-project-root", server.serverProjectRoot);
2925
+ reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
2914
2926
  let response;
2915
2927
  try {
2916
2928
  response = await fetch(`${server.baseUrl}${pathname}`, {
@@ -4431,6 +4443,127 @@ function formatConnectionStatus(selected, connections) {
4431
4443
  `);
4432
4444
  }
4433
4445
 
4446
+ // packages/cli/src/commands/_async-ui.ts
4447
+ import pc4 from "picocolors";
4448
+
4449
+ // packages/cli/src/commands/_spinner.ts
4450
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
4451
+ function createTtySpinner(input) {
4452
+ const output = input.output ?? process.stdout;
4453
+ const isTty = output.isTTY === true;
4454
+ const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
4455
+ let label = input.label;
4456
+ let frame = 0;
4457
+ let paused = false;
4458
+ let stopped = false;
4459
+ let lastPrintedLabel = "";
4460
+ const render = () => {
4461
+ if (stopped || paused)
4462
+ return;
4463
+ if (!isTty) {
4464
+ if (label !== lastPrintedLabel) {
4465
+ output.write(`${label}
4466
+ `);
4467
+ lastPrintedLabel = label;
4468
+ }
4469
+ return;
4470
+ }
4471
+ frame = (frame + 1) % frames.length;
4472
+ const glyph = frames[frame] ?? frames[0] ?? "";
4473
+ output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
4474
+ };
4475
+ const clearLine = () => {
4476
+ if (isTty)
4477
+ output.write("\r\x1B[2K");
4478
+ };
4479
+ render();
4480
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
4481
+ return {
4482
+ setLabel(next) {
4483
+ label = next;
4484
+ render();
4485
+ },
4486
+ pause() {
4487
+ paused = true;
4488
+ clearLine();
4489
+ },
4490
+ resume() {
4491
+ if (stopped)
4492
+ return;
4493
+ paused = false;
4494
+ render();
4495
+ },
4496
+ stop(finalLine) {
4497
+ if (stopped)
4498
+ return;
4499
+ stopped = true;
4500
+ if (timer)
4501
+ clearInterval(timer);
4502
+ clearLine();
4503
+ if (finalLine)
4504
+ output.write(`${finalLine}
4505
+ `);
4506
+ }
4507
+ };
4508
+ }
4509
+
4510
+ // packages/cli/src/commands/_async-ui.ts
4511
+ var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
4512
+ var DONE_SYMBOL = pc4.green("\u25C7");
4513
+ var FAIL_SYMBOL = pc4.red("\u25A0");
4514
+ var activeUpdate = null;
4515
+ async function withSpinner(label, work, options = {}) {
4516
+ if (options.outputMode === "json") {
4517
+ return work(() => {});
4518
+ }
4519
+ if (activeUpdate) {
4520
+ const outer = activeUpdate;
4521
+ outer(label);
4522
+ return work(outer);
4523
+ }
4524
+ const output = options.output ?? process.stderr;
4525
+ const isTty = output.isTTY === true;
4526
+ let lastLabel = label;
4527
+ if (!isTty) {
4528
+ output.write(`${label}
4529
+ `);
4530
+ const update2 = (next) => {
4531
+ lastLabel = next;
4532
+ };
4533
+ activeUpdate = update2;
4534
+ const previousListener2 = setServerPhaseListener(update2);
4535
+ try {
4536
+ return await work(update2);
4537
+ } finally {
4538
+ activeUpdate = null;
4539
+ setServerPhaseListener(previousListener2);
4540
+ }
4541
+ }
4542
+ const spinner2 = createTtySpinner({
4543
+ label,
4544
+ output,
4545
+ frames: CLACK_SPINNER_FRAMES,
4546
+ styleFrame: (frame) => pc4.magenta(frame)
4547
+ });
4548
+ const update = (next) => {
4549
+ lastLabel = next;
4550
+ spinner2.setLabel(next);
4551
+ };
4552
+ activeUpdate = update;
4553
+ const previousListener = setServerPhaseListener(update);
4554
+ try {
4555
+ const result = await work(update);
4556
+ spinner2.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
4557
+ return result;
4558
+ } catch (error) {
4559
+ spinner2.stop(`${FAIL_SYMBOL} ${lastLabel}`);
4560
+ throw error;
4561
+ } finally {
4562
+ activeUpdate = null;
4563
+ setServerPhaseListener(previousListener);
4564
+ }
4565
+ }
4566
+
4434
4567
  // packages/cli/src/commands/inbox.ts
4435
4568
  async function listInboxRecords(context, kind, filters) {
4436
4569
  const params = new URLSearchParams;
@@ -4535,7 +4668,7 @@ async function executeInbox(context, args) {
4535
4668
  const task = takeOption(pending, "--task");
4536
4669
  pending = task.rest;
4537
4670
  requireNoExtraArgs(pending, "rig inbox approvals [--run <id>] [--task <id>]");
4538
- const approvals = await listInboxRecords(context, "approvals", { run: run.value, task: task.value });
4671
+ const approvals = await withSpinner("Reading approvals from server\u2026", () => listInboxRecords(context, "approvals", { run: run.value, task: task.value }), { outputMode: context.outputMode });
4539
4672
  renderList(context, "approvals", approvals);
4540
4673
  return { ok: true, group: "inbox", command, details: { approvals } };
4541
4674
  }
@@ -4546,7 +4679,7 @@ async function executeInbox(context, args) {
4546
4679
  const task = takeOption(pending, "--task");
4547
4680
  pending = task.rest;
4548
4681
  requireNoExtraArgs(pending, "rig inbox inputs [--run <id>] [--task <id>]");
4549
- const requests = await listInboxRecords(context, "inputs", { run: run.value, task: task.value });
4682
+ const requests = await withSpinner("Reading input requests from server\u2026", () => listInboxRecords(context, "inputs", { run: run.value, task: task.value }), { outputMode: context.outputMode });
4550
4683
  renderList(context, "inputs", requests);
4551
4684
  return { ok: true, group: "inbox", command, details: { requests } };
4552
4685
  }
@@ -4567,7 +4700,7 @@ async function executeInbox(context, args) {
4567
4700
  if (decision.value !== "approve" && decision.value !== "reject") {
4568
4701
  throw new CliError("decision must be approve or reject.", 1, { hint: "Re-run as `rig inbox approve --run <id> --request <id> --decision approve|reject`." });
4569
4702
  }
4570
- const result = await requestServerJson(context, "/api/inbox/approvals/resolve", {
4703
+ const result = await withSpinner(`Resolving approval ${request.value}\u2026`, () => requestServerJson(context, "/api/inbox/approvals/resolve", {
4571
4704
  method: "POST",
4572
4705
  headers: { "content-type": "application/json" },
4573
4706
  body: JSON.stringify({
@@ -4576,7 +4709,7 @@ async function executeInbox(context, args) {
4576
4709
  decision: decision.value,
4577
4710
  note: note4.value ?? null
4578
4711
  })
4579
- });
4712
+ }), { outputMode: context.outputMode });
4580
4713
  return { ok: true, group: "inbox", command, details: { result } };
4581
4714
  }
4582
4715
  case "respond": {
@@ -4610,7 +4743,7 @@ async function executeInbox(context, args) {
4610
4743
  const [key, ...restValue] = entry.split("=");
4611
4744
  return [key, restValue.join("=")];
4612
4745
  }));
4613
- const result = await requestServerJson(context, "/api/inbox/inputs/respond", {
4746
+ const result = await withSpinner(`Sending response for ${request.value}\u2026`, () => requestServerJson(context, "/api/inbox/inputs/respond", {
4614
4747
  method: "POST",
4615
4748
  headers: { "content-type": "application/json" },
4616
4749
  body: JSON.stringify({
@@ -4618,7 +4751,7 @@ async function executeInbox(context, args) {
4618
4751
  requestId: request.value,
4619
4752
  answers: parsedAnswers
4620
4753
  })
4621
- });
4754
+ }), { outputMode: context.outputMode });
4622
4755
  return { ok: true, group: "inbox", command, details: { result } };
4623
4756
  }
4624
4757
  case "watch": {
@@ -5035,7 +5168,10 @@ async function runRigDoctorChecks(options) {
5035
5168
  const bunVersion = options.bunVersion ?? Bun.version;
5036
5169
  const request = options.requestJson ?? ((pathname, init) => requestServerJson({ projectRoot }, pathname, init));
5037
5170
  const loadConfig = options.loadConfig ?? loadRigConfigOrNull;
5171
+ const progress = options.onProgress ?? (() => {});
5172
+ progress("Checking local toolchain\u2026");
5038
5173
  checks.push(check("bun", `bun >= ${MIN_SUPPORTED_BUN_VERSION}`, isSupportedBunVersion(bunVersion) ? "pass" : "fail", `found ${bunVersion}`, `Install Bun ${MIN_SUPPORTED_BUN_VERSION} or newer.`), check("git", "git", which("git") ? "pass" : "fail", which("git") ?? undefined, "Install git and ensure it is on PATH."), check("jq", "jq", which("jq") ? "pass" : "warn", which("jq") ?? undefined, "Install jq (for example `brew install jq`)."));
5174
+ progress("Loading rig.config\u2026");
5039
5175
  const loadedConfig = await loadConfig(projectRoot).catch(() => null);
5040
5176
  const config = loadedConfig ?? loadFallbackConfig(projectRoot);
5041
5177
  const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync10(resolve16(projectRoot, name)));
@@ -5054,6 +5190,7 @@ async function runRigDoctorChecks(options) {
5054
5190
  checks.push(selected ? check("connection", "selected server connection", "pass", selected.connection.kind === "remote" ? selected.connection.baseUrl : "local auto") : check("connection", "selected server", repo ? "fail" : "warn", repo ? "selected alias is missing" : "will auto-start local server", repo ? "Run `rig server list` and `rig server use <alias|local>`." : undefined));
5055
5191
  let server = null;
5056
5192
  try {
5193
+ progress("Connecting to the selected Rig server\u2026");
5057
5194
  server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);
5058
5195
  checks.push(check("server", "Rig server reachable", "pass", `${server.connectionKind} ${server.baseUrl}`));
5059
5196
  } catch (error) {
@@ -5061,18 +5198,21 @@ async function runRigDoctorChecks(options) {
5061
5198
  }
5062
5199
  if (server || options.requestJson) {
5063
5200
  try {
5201
+ progress("Checking server status\u2026");
5064
5202
  const status = await request("/api/server/status");
5065
5203
  checks.push(check("server-status", "server project status", "pass", JSON.stringify(status).slice(0, 180)));
5066
5204
  } catch (error) {
5067
5205
  checks.push(check("server-status", "server project status", "fail", errorMessage(error), "Run `rig doctor` after the selected server is reachable."));
5068
5206
  }
5069
5207
  try {
5208
+ progress("Checking GitHub auth\u2026");
5070
5209
  const auth = await request("/api/github/auth/status");
5071
5210
  checks.push(isAuthenticated2(auth) ? check("github-auth", "GitHub auth", "pass") : check("github-auth", "GitHub auth", "fail", "not authenticated", "Run `rig github auth import-gh` or `rig github auth token --token <token>`."));
5072
5211
  } catch (error) {
5073
5212
  checks.push(check("github-auth", "GitHub auth", "fail", errorMessage(error), "Authenticate GitHub through Rig and ensure the server exposes auth status."));
5074
5213
  }
5075
5214
  try {
5215
+ progress("Checking GitHub repo permissions\u2026");
5076
5216
  const permissions = await request("/api/github/repo/permissions");
5077
5217
  const allowed = permissionAllowsPr2(permissions);
5078
5218
  checks.push(allowed === true ? check("github-repo-permissions", "GitHub repo PR permissions", "pass", JSON.stringify(permissions).slice(0, 180)) : allowed === false ? check("github-repo-permissions", "GitHub repo PR permissions", "fail", JSON.stringify(permissions).slice(0, 180), "Grant the selected GitHub token permission to push branches, open PRs, and merge according to repo rules.") : check("github-repo-permissions", "GitHub repo PR permissions", "warn", JSON.stringify(permissions).slice(0, 180), "Confirm the selected token can push branches and open PRs."));
@@ -5080,6 +5220,7 @@ async function runRigDoctorChecks(options) {
5080
5220
  checks.push(check("github-repo-permissions", "GitHub repo PR permissions", "warn", errorMessage(error), "Ensure the server exposes repo permission checks and the token can open PRs."));
5081
5221
  }
5082
5222
  try {
5223
+ progress("Checking GitHub issue labels\u2026");
5083
5224
  const labels = await request("/api/workspace/task-labels");
5084
5225
  const ready = labelsReady(labels);
5085
5226
  checks.push(ready === false ? check("task-labels", "GitHub issue labels", "fail", JSON.stringify(labels).slice(0, 180), "Let Rig create required labels or create the configured lifecycle labels manually.") : check("task-labels", "GitHub issue labels", ready === true ? "pass" : "warn", JSON.stringify(labels).slice(0, 180), "Confirm required Rig lifecycle labels exist."));
@@ -5087,6 +5228,7 @@ async function runRigDoctorChecks(options) {
5087
5228
  checks.push(check("task-labels", "GitHub issue labels", "warn", errorMessage(error), "Run `rig init`/`rig doctor` after label setup is wired on the server."));
5088
5229
  }
5089
5230
  try {
5231
+ progress("Checking task projection\u2026");
5090
5232
  const projection = await request("/api/workspace/task-projection");
5091
5233
  checks.push(check("task-projection", "task projection", "pass", JSON.stringify(projection).slice(0, 180)));
5092
5234
  } catch (error) {
@@ -5095,6 +5237,7 @@ async function runRigDoctorChecks(options) {
5095
5237
  const slug = projectStatusSlug(projectRoot, config);
5096
5238
  if (slug) {
5097
5239
  try {
5240
+ progress("Checking server project checkout\u2026");
5098
5241
  const project = await request(`/api/projects/${encodeURIComponent(slug)}`);
5099
5242
  checks.push(check("remote-checkout", "server project checkout", "pass", JSON.stringify(project).slice(0, 180)));
5100
5243
  } catch (error) {
@@ -5109,6 +5252,7 @@ async function runRigDoctorChecks(options) {
5109
5252
  }
5110
5253
  checks.push(githubProjectsCheck(config));
5111
5254
  checks.push(prMergeCheck(config));
5255
+ progress("Checking Pi installation\u2026");
5112
5256
  const piChecks = await (options.piChecks ?? (() => buildPiSetupChecks()))().catch((error) => [{
5113
5257
  ok: false,
5114
5258
  label: "pi/pi-rig checks",
@@ -6031,7 +6175,7 @@ async function executeGithub(context, args) {
6031
6175
  case "status": {
6032
6176
  if (rest.length > 0)
6033
6177
  throw new CliError("Usage: rig github auth status", 1);
6034
- const status = await getGitHubAuthStatusViaServer(context);
6178
+ const status = await withSpinner("Checking GitHub auth on the server\u2026", () => getGitHubAuthStatusViaServer(context), { outputMode: context.outputMode });
6035
6179
  printPayload(context, status, `GitHub auth: ${status.authenticated ? "authenticated" : "unauthenticated"}`);
6036
6180
  return { ok: true, group: "github", command: "auth status", details: status };
6037
6181
  }
@@ -6042,14 +6186,15 @@ async function executeGithub(context, args) {
6042
6186
  const token = parsed.value?.trim();
6043
6187
  if (!token)
6044
6188
  throw new CliError("Missing --token value.", 1, { hint: "Re-run as `rig github auth token --token <token>`." });
6045
- const result = await postGitHubTokenViaServer(context, token);
6189
+ const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, token), { outputMode: context.outputMode });
6046
6190
  printPayload(context, result, "GitHub token stored on the selected Rig server.");
6047
6191
  return { ok: true, group: "github", command: "auth token", details: result };
6048
6192
  }
6049
6193
  case "import-gh": {
6050
6194
  if (rest.length > 0)
6051
6195
  throw new CliError("Usage: rig github auth import-gh", 1);
6052
- const result = await postGitHubTokenViaServer(context, readGhToken());
6196
+ const importedToken = readGhToken();
6197
+ const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, importedToken), { outputMode: context.outputMode });
6053
6198
  printPayload(context, result, "GitHub token imported from gh and stored on the selected Rig server.");
6054
6199
  return { ok: true, group: "github", command: "auth import-gh", details: result };
6055
6200
  }
@@ -6062,7 +6207,7 @@ async function executeGithub(context, args) {
6062
6207
  init_runner();
6063
6208
  async function executeDoctor(context, args) {
6064
6209
  requireNoExtraArgs(args, "rig doctor");
6065
- const checks = await runRigDoctorChecks({ projectRoot: context.projectRoot });
6210
+ const checks = await withSpinner("Running doctor checks\u2026", (update) => runRigDoctorChecks({ projectRoot: context.projectRoot, onProgress: update }), { outputMode: context.outputMode });
6066
6211
  if (context.outputMode === "text") {
6067
6212
  console.log(formatDoctorChecks(checks));
6068
6213
  }
@@ -6369,13 +6514,19 @@ async function executeInspect(context, args) {
6369
6514
  const requiredTask = requireTask(task, "rig inspect logs --task <task-id>");
6370
6515
  const latestRun = listAuthorityRuns(context.projectRoot).map((entry) => readAuthorityRun3(context.projectRoot, entry.runId)).filter((run) => Boolean(run)).filter((run) => run.taskId === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
6371
6516
  if (!latestRun) {
6372
- const serverRuns = await listRunsViaServer(context, { limit: 200 }).catch(() => []);
6373
- const serverRun = serverRuns.filter((run) => String(run.taskId ?? "") === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
6374
- if (!serverRun || typeof serverRun.runId !== "string") {
6517
+ const fallback = await withSpinner(`Finding runs for ${requiredTask} on the server\u2026`, async (update) => {
6518
+ const serverRuns = await listRunsViaServer(context, { limit: 200 }).catch(() => []);
6519
+ const serverRun = serverRuns.filter((run) => String(run.taskId ?? "") === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
6520
+ if (!serverRun || typeof serverRun.runId !== "string")
6521
+ return null;
6522
+ update(`Reading logs for run ${serverRun.runId}\u2026`);
6523
+ const page = await getRunLogsViaServer(context, serverRun.runId, { limit: 500 });
6524
+ return { runId: serverRun.runId, page };
6525
+ }, { outputMode: context.outputMode });
6526
+ if (!fallback) {
6375
6527
  throw new CliError(`No runs found for ${requiredTask} (local or on the selected server).`, 1, { hint: "Start one with `rig task run --task <id>`, or check `rig run list`." });
6376
6528
  }
6377
- const page = await getRunLogsViaServer(context, serverRun.runId, { limit: 500 });
6378
- const entries = Array.isArray(page.entries) ? page.entries : [];
6529
+ const entries = Array.isArray(fallback.page.entries) ? fallback.page.entries : [];
6379
6530
  if (context.outputMode === "text") {
6380
6531
  for (const entry of entries) {
6381
6532
  const record = entry && typeof entry === "object" ? entry : {};
@@ -6384,9 +6535,9 @@ async function executeInspect(context, args) {
6384
6535
  console.log([title, detail].filter(Boolean).join(" \u2014 "));
6385
6536
  }
6386
6537
  if (entries.length === 0)
6387
- console.log(`(no log entries for run ${serverRun.runId})`);
6538
+ console.log(`(no log entries for run ${fallback.runId})`);
6388
6539
  }
6389
- return { ok: true, group: "inspect", command, details: { task: requiredTask, runId: serverRun.runId, source: "server", entries } };
6540
+ return { ok: true, group: "inspect", command, details: { task: requiredTask, runId: fallback.runId, source: "server", entries } };
6390
6541
  }
6391
6542
  const logsPath = resolve19(resolveAuthorityRunDir2(context.projectRoot, latestRun.runId), "logs.jsonl");
6392
6543
  if (!existsSync12(logsPath)) {
@@ -6444,7 +6595,7 @@ async function executeInspect(context, args) {
6444
6595
  const { value: task, rest: remaining } = takeOption(rest, "--task");
6445
6596
  requireNoExtraArgs(remaining, "rig inspect diff [--task <task-id>]");
6446
6597
  if (task) {
6447
- const files = changedFilesForTask(context.projectRoot, task, false);
6598
+ const files = await withSpinner(`Computing changed files for ${task}\u2026`, async () => changedFilesForTask(context.projectRoot, task, false), { outputMode: context.outputMode });
6448
6599
  for (const file of files) {
6449
6600
  console.log(file);
6450
6601
  }
@@ -7532,7 +7683,12 @@ async function attachRunBundledPiFrontend(context, input) {
7532
7683
  };
7533
7684
  let detached = false;
7534
7685
  try {
7535
- await runPiMain([], {
7686
+ await runPiMain([
7687
+ "--no-extensions",
7688
+ "--no-skills",
7689
+ "--no-prompt-templates",
7690
+ "--no-context-files"
7691
+ ], {
7536
7692
  extensionFactories: [piRigExtensionFactory]
7537
7693
  });
7538
7694
  detached = true;
@@ -7597,8 +7753,9 @@ async function readOperatorSnapshot(context, runId, options = {}) {
7597
7753
  }
7598
7754
  async function attachRunOperatorView(context, input) {
7599
7755
  let steered = false;
7600
- if (input.message?.trim()) {
7601
- await sendRunPiPromptViaServer(context, input.runId, input.message.trim(), "steer").catch(() => steerRunViaServer(context, input.runId, input.message.trim()));
7756
+ const attachMessage = input.message?.trim();
7757
+ if (attachMessage) {
7758
+ await withSpinner("Queueing steering message\u2026", () => sendRunPiPromptViaServer(context, input.runId, attachMessage, "steer").catch(() => steerRunViaServer(context, input.runId, attachMessage)), { outputMode: context.outputMode });
7602
7759
  steered = true;
7603
7760
  }
7604
7761
  if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
@@ -7608,7 +7765,7 @@ async function attachRunOperatorView(context, input) {
7608
7765
  });
7609
7766
  }
7610
7767
  const surface = createOperatorSurface({ interactive: input.interactive !== false });
7611
- let snapshot = await readOperatorSnapshot(context, input.runId);
7768
+ let snapshot = await withSpinner(`Connecting to run ${input.runId}\u2026`, () => readOperatorSnapshot(context, input.runId), { outputMode: context.outputMode });
7612
7769
  if (context.outputMode === "text") {
7613
7770
  surface.renderSnapshot(snapshot);
7614
7771
  surface.renderTimeline(snapshot.timeline);
@@ -7739,7 +7896,7 @@ async function executeRun(context, args) {
7739
7896
  switch (command) {
7740
7897
  case "list": {
7741
7898
  requireNoExtraArgs(rest, "rig run list");
7742
- const { runs, source } = await listRunsForSelectedConnection(context, { limit: 100 });
7899
+ 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 });
7743
7900
  if (context.outputMode === "text") {
7744
7901
  printFormattedOutput(formatRunList(runs, { source }));
7745
7902
  await printPendingInboxFooter(context);
@@ -7812,7 +7969,7 @@ async function executeRun(context, args) {
7812
7969
  if (!runId) {
7813
7970
  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>`." });
7814
7971
  }
7815
- const record = readAuthorityRun5(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, runId).catch(() => ({})));
7972
+ const record = readAuthorityRun5(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await withSpinner(`Reading run ${runId} from server\u2026`, () => getRunDetailsViaServer(context, runId).catch(() => ({})), { outputMode: context.outputMode }));
7816
7973
  if (!record) {
7817
7974
  throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see recorded runs." });
7818
7975
  }
@@ -7833,7 +7990,8 @@ async function executeRun(context, args) {
7833
7990
  }
7834
7991
  const renderer = createPiRunStreamRenderer();
7835
7992
  let cursor = null;
7836
- const page = await getRunTimelineViaServer(context, run.value, { limit: 500 });
7993
+ const timelineRunId = run.value;
7994
+ const page = await withSpinner(`Reading timeline for ${timelineRunId}\u2026`, () => getRunTimelineViaServer(context, timelineRunId, { limit: 500 }), { outputMode: context.outputMode });
7837
7995
  const events = Array.isArray(page.entries) ? page.entries.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
7838
7996
  cursor = typeof page.nextCursor === "string" ? page.nextCursor : null;
7839
7997
  if (context.outputMode === "text") {
@@ -7910,8 +8068,9 @@ async function executeRun(context, args) {
7910
8068
  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`." });
7911
8069
  }
7912
8070
  let steered = false;
7913
- if (messageOption.value?.trim()) {
7914
- await steerRunViaServer(context, runId, messageOption.value.trim());
8071
+ const steerMessage = messageOption.value?.trim();
8072
+ if (steerMessage) {
8073
+ await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, steerMessage), { outputMode: context.outputMode });
7915
8074
  steered = true;
7916
8075
  }
7917
8076
  const attached = await attachRunOperatorView(context, {
@@ -7931,7 +8090,7 @@ async function executeRun(context, args) {
7931
8090
  }
7932
8091
  return { ok: true, group: "run", command };
7933
8092
  }
7934
- const summary = isRemoteConnectionSelected(context.projectRoot) ? buildServerRunStatus(await listRunsViaServer(context, { limit: 100 })) : runStatus(context.projectRoot, runtimeContext);
8093
+ 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);
7935
8094
  const activeRuns = Array.isArray(summary.activeRuns) ? summary.activeRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
7936
8095
  const recentRuns = Array.isArray(summary.recentRuns) ? summary.recentRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
7937
8096
  if (context.outputMode === "text") {
@@ -8066,7 +8225,8 @@ async function executeRun(context, args) {
8066
8225
  }
8067
8226
  return { ok: true, group: "run", command, details: { runId, dryRun: true } };
8068
8227
  }
8069
- await steerRunViaServer(context, runId, message2.trim());
8228
+ const trimmedMessage = message2.trim();
8229
+ await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, trimmedMessage), { outputMode: context.outputMode });
8070
8230
  if (context.outputMode === "text") {
8071
8231
  console.log(`Steering message queued for ${runId}.`);
8072
8232
  }
@@ -8087,7 +8247,7 @@ async function executeRun(context, args) {
8087
8247
  };
8088
8248
  }
8089
8249
  if (runId) {
8090
- const stopped = await stopRunViaServer(context, runId);
8250
+ const stopped = await withSpinner(`Requesting stop for ${runId}\u2026`, () => stopRunViaServer(context, runId), { outputMode: context.outputMode });
8091
8251
  if (context.outputMode === "text")
8092
8252
  console.log(`Stop requested: ${runId}`);
8093
8253
  return { ok: true, group: "run", command, details: stopped };
@@ -8333,12 +8493,12 @@ async function executeServer(context, args, options) {
8333
8493
 
8334
8494
  // packages/cli/src/commands/stats.ts
8335
8495
  init_runner();
8336
- import pc5 from "picocolors";
8496
+ import pc6 from "picocolors";
8337
8497
  import { computeRigStats } from "@rig/runtime/control-plane/native/run-stats";
8338
8498
 
8339
8499
  // packages/cli/src/commands/_help-catalog.ts
8340
8500
  import { intro as intro3, log as log4, note as note4, outro as outro3 } from "@clack/prompts";
8341
- import pc4 from "picocolors";
8501
+ import pc5 from "picocolors";
8342
8502
  var TOP_LEVEL_SECTIONS = [
8343
8503
  {
8344
8504
  title: "Start here",
@@ -8626,13 +8786,13 @@ var ADVANCED_COMMANDS = [
8626
8786
  ];
8627
8787
  var ALL_GROUPS = [...PRIMARY_GROUPS, ...ADVANCED_GROUPS];
8628
8788
  function heading(title) {
8629
- return pc4.bold(pc4.cyan(title));
8789
+ return pc5.bold(pc5.cyan(title));
8630
8790
  }
8631
8791
  function renderRigBanner(version) {
8632
- const m = (s) => pc4.bold(pc4.magenta(s));
8633
- const c = (s) => pc4.bold(pc4.cyan(s));
8634
- const y = (s) => pc4.yellow(s);
8635
- const d = (s) => pc4.dim(s);
8792
+ const m = (s) => pc5.bold(pc5.magenta(s));
8793
+ const c = (s) => pc5.bold(pc5.cyan(s));
8794
+ const y = (s) => pc5.yellow(s);
8795
+ const d = (s) => pc5.dim(s);
8636
8796
  const lines = [
8637
8797
  m(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + d(" \u2591\u2592\u2593\u2588 "),
8638
8798
  m(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ") + d("\u2593\u2592\u2591"),
@@ -8641,7 +8801,7 @@ function renderRigBanner(version) {
8641
8801
  y(" \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + d(" \u2592\u2591"),
8642
8802
  y(" \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D "),
8643
8803
  "",
8644
- ` ${c("\u25E2\u25E4")} ${pc4.bold("the control rig for autonomous coding agents")} ${m("//")} ${d("you are the operator")}`,
8804
+ ` ${c("\u25E2\u25E4")} ${pc5.bold("the control rig for autonomous coding agents")} ${m("//")} ${d("you are the operator")}`,
8645
8805
  version ? ` ${d(`v${version} \xB7 jack in: rig task run --next`)}` : ` ${d("jack in: rig task run --next")}`
8646
8806
  ];
8647
8807
  return lines.join(`
@@ -8649,7 +8809,7 @@ function renderRigBanner(version) {
8649
8809
  }
8650
8810
  function commandLine(command, description) {
8651
8811
  const commandColumn = command.length >= 38 ? `${command} ` : command.padEnd(38);
8652
- return `${pc4.dim("\u2502")} ${pc4.bold(commandColumn)} ${description}`;
8812
+ return `${pc5.dim("\u2502")} ${pc5.bold(commandColumn)} ${description}`;
8653
8813
  }
8654
8814
  function renderCommandBlock(commands) {
8655
8815
  return commands.map((entry) => commandLine(entry.command, entry.description)).join(`
@@ -8659,37 +8819,37 @@ function renderGroup(group) {
8659
8819
  const lines = [
8660
8820
  `${heading(`rig ${group.name}`)} \u2014 ${group.summary}`,
8661
8821
  "",
8662
- pc4.bold("Usage"),
8822
+ pc5.bold("Usage"),
8663
8823
  ...group.usage.map((line) => ` ${line}`),
8664
8824
  "",
8665
- pc4.bold("Commands"),
8825
+ pc5.bold("Commands"),
8666
8826
  ...group.commands.map((entry) => commandLine(entry.command, entry.description))
8667
8827
  ];
8668
8828
  if (group.examples?.length) {
8669
- lines.push("", pc4.bold("Examples"), ...group.examples.map((line) => ` ${pc4.dim("$")} ${line}`));
8829
+ lines.push("", pc5.bold("Examples"), ...group.examples.map((line) => ` ${pc5.dim("$")} ${line}`));
8670
8830
  }
8671
8831
  if (group.next?.length) {
8672
- lines.push("", pc4.bold("Next steps"), ...group.next.map((line) => ` ${pc4.dim("\u203A")} ${line}`));
8832
+ lines.push("", pc5.bold("Next steps"), ...group.next.map((line) => ` ${pc5.dim("\u203A")} ${line}`));
8673
8833
  }
8674
8834
  if (group.advanced?.length) {
8675
- lines.push("", pc4.bold("Compatibility / advanced"), ...group.advanced.map((line) => ` ${pc4.dim("\u203A")} ${line}`));
8835
+ lines.push("", pc5.bold("Compatibility / advanced"), ...group.advanced.map((line) => ` ${pc5.dim("\u203A")} ${line}`));
8676
8836
  }
8677
8837
  return lines.join(`
8678
8838
  `);
8679
8839
  }
8680
8840
  function renderTopLevelHelp() {
8681
8841
  return [
8682
- `${heading("rig")} ${pc4.dim("\u2014 server-owned task/run control plane for Pi-backed engineering work")}`,
8683
- pc4.dim("Current path: select a server, choose a task, submit a run, attach with native Pi, clear inbox gates."),
8842
+ `${heading("rig")} ${pc5.dim("\u2014 server-owned task/run control plane for Pi-backed engineering work")}`,
8843
+ pc5.dim("Current path: select a server, choose a task, submit a run, attach with native Pi, clear inbox gates."),
8684
8844
  "",
8685
8845
  ...TOP_LEVEL_SECTIONS.flatMap((section) => [
8686
- `${pc4.bold(pc4.magenta(`\u25C7 ${section.title}`))} \u2014 ${pc4.dim(section.subtitle)}`,
8846
+ `${pc5.bold(pc5.magenta(`\u25C7 ${section.title}`))} \u2014 ${pc5.dim(section.subtitle)}`,
8687
8847
  renderCommandBlock(section.commands),
8688
8848
  ""
8689
8849
  ]),
8690
- pc4.dim("More: `rig help --advanced` for dev/compatibility commands; `rig <group> --help` for rich per-group help; `rig --version` for the installed version."),
8850
+ pc5.dim("More: `rig help --advanced` for dev/compatibility commands; `rig <group> --help` for rich per-group help; `rig --version` for the installed version."),
8691
8851
  "",
8692
- pc4.bold("Global options"),
8852
+ pc5.bold("Global options"),
8693
8853
  commandLine("--project <path>", "Use a project root instead of auto-discovery."),
8694
8854
  commandLine("--json", "Emit structured output for scripts/agents."),
8695
8855
  commandLine("--dry-run", "Print the command plan without mutating state.")
@@ -8700,16 +8860,16 @@ function renderAdvancedHelp() {
8700
8860
  return [
8701
8861
  `${heading("rig advanced")} \u2014 compatibility, diagnostics, and internal surfaces`,
8702
8862
  "",
8703
- pc4.bold("Primary groups"),
8863
+ pc5.bold("Primary groups"),
8704
8864
  " init, server, task, run, inbox, repo, plugin, inspect, stats, doctor, github",
8705
8865
  "",
8706
- pc4.bold("Advanced commands"),
8866
+ pc5.bold("Advanced commands"),
8707
8867
  ...ADVANCED_COMMANDS.map((entry) => commandLine(entry.command, entry.description)),
8708
8868
  "",
8709
- pc4.bold("Advanced groups"),
8869
+ pc5.bold("Advanced groups"),
8710
8870
  ...ADVANCED_GROUPS.map((group) => commandLine(group.name, group.summary)),
8711
8871
  "",
8712
- pc4.dim("All groups remain callable. Prefer `rig server`, `rig task`, `rig run`, and `rig inbox` for day-to-day work.")
8872
+ pc5.dim("All groups remain callable. Prefer `rig server`, `rig task`, `rig run`, and `rig inbox` for day-to-day work.")
8713
8873
  ].join(`
8714
8874
  `);
8715
8875
  }
@@ -8857,11 +9017,11 @@ function formatStatsReport(stats) {
8857
9017
  const keyWidth = Math.max(...rows.map(([key]) => key.length));
8858
9018
  const lines = [
8859
9019
  formatSection("Fleet stats", window),
8860
- ...rows.map(([key, value]) => `${pc5.dim("\u2502")} ${pc5.dim(key.padEnd(keyWidth + 2))} ${value}`)
9020
+ ...rows.map(([key, value]) => `${pc6.dim("\u2502")} ${pc6.dim(key.padEnd(keyWidth + 2))} ${value}`)
8861
9021
  ];
8862
9022
  const otherStatuses = Object.entries(stats.statusCounts).filter(([status]) => !["completed", "failed", "needs-attention"].includes(status)).sort(([, left], [, right]) => right - left);
8863
9023
  if (otherStatuses.length > 0) {
8864
- lines.push(`${pc5.dim("\u2502")} ${pc5.dim("other".padEnd(keyWidth + 2))} ${otherStatuses.map(([status, count]) => `${status}:${count}`).join(" ")}`);
9024
+ lines.push(`${pc6.dim("\u2502")} ${pc6.dim("other".padEnd(keyWidth + 2))} ${otherStatuses.map(([status, count]) => `${status}:${count}`).join(" ")}`);
8865
9025
  }
8866
9026
  lines.push("", ...formatNextSteps([
8867
9027
  "Inspect a run: `rig run list` then `rig run show <run-id>`",
@@ -8880,7 +9040,8 @@ async function executeStats(context, args) {
8880
9040
  const sinceResult = takeOption(pending, "--since");
8881
9041
  requireNoExtraArgs(sinceResult.rest, "rig stats [show] [--since <7d|30d|ISO date>]");
8882
9042
  const since = parseSinceOption(sinceResult.value);
8883
- const stats = isRemoteConnectionSelected(context.projectRoot) ? await requestServerJson(context, `/api/stats${since ? `?since=${encodeURIComponent(since.toISOString())}` : ""}`) : computeRigStats(context.projectRoot, { since });
9043
+ const remoteStats = isRemoteConnectionSelected(context.projectRoot);
9044
+ const stats = await withSpinner(remoteStats ? "Computing fleet stats on the server\u2026" : "Scanning local run state\u2026", async () => remoteStats ? await requestServerJson(context, `/api/stats${since ? `?since=${encodeURIComponent(since.toISOString())}` : ""}`) : computeRigStats(context.projectRoot, { since }), { outputMode: context.outputMode });
8884
9045
  if (context.outputMode === "text") {
8885
9046
  printFormattedOutput(formatStatsReport(stats));
8886
9047
  }
@@ -9135,7 +9296,7 @@ async function executeTask(context, args, options) {
9135
9296
  pending = rawResult.rest;
9136
9297
  const { filters, rest: remaining } = parseTaskFilters(pending);
9137
9298
  requireNoExtraArgs(remaining, "rig task list [--raw] [--assignee <login|me|@me>] [--state open|closed] [--status <status>] [--limit <n>]");
9138
- const tasks = await listWorkspaceTasksViaServer(context, filters);
9299
+ const tasks = await withSpinner("Reading tasks from server\u2026", () => listWorkspaceTasksViaServer(context, filters), { outputMode: context.outputMode });
9139
9300
  if (context.outputMode === "text") {
9140
9301
  const renderedTasks = rawResult.value ? tasks.map((task) => summarizeTask(task, { raw: true })) : tasks.map((task) => summarizeTask(task));
9141
9302
  printFormattedOutput(formatTaskList(renderedTasks, { raw: rawResult.value }));
@@ -9159,7 +9320,7 @@ async function executeTask(context, args, options) {
9159
9320
  const taskId3 = normalizeTaskRunTaskId(taskOption.value ?? positional);
9160
9321
  if (!taskId3)
9161
9322
  throw new CliError("task show requires a task id.", 2, { hint: "Run `rig task list` to find ids, then `rig task show <id>`." });
9162
- const task = await getWorkspaceTaskViaServer(context, taskId3);
9323
+ const task = await withSpinner(`Reading task ${taskId3} from server\u2026`, () => getWorkspaceTaskViaServer(context, taskId3), { outputMode: context.outputMode });
9163
9324
  if (!task)
9164
9325
  throw new CliError(`Task not found: ${taskId3}`, 3, { hint: "Run `rig task list` to see available tasks." });
9165
9326
  const summary = summarizeTask(task, { raw: true });
@@ -9171,7 +9332,7 @@ async function executeTask(context, args, options) {
9171
9332
  case "next": {
9172
9333
  const { filters, rest: remaining } = parseTaskFilters(rest);
9173
9334
  requireNoExtraArgs(remaining, "rig task next [--assignee <login|me|@me>] [--state open|closed] [--status <status>] [--limit <n>]");
9174
- const selected = await selectNextWorkspaceTaskViaServer(context, filters);
9335
+ const selected = await withSpinner("Selecting next ready task\u2026", () => selectNextWorkspaceTaskViaServer(context, filters), { outputMode: context.outputMode });
9175
9336
  if (context.outputMode === "text") {
9176
9337
  if (selected.task) {
9177
9338
  printFormattedOutput(formatTaskCard(summarizeTask(selected.task, { raw: true }), { title: "Selected task", selected: true }));
@@ -9321,7 +9482,7 @@ async function executeTask(context, args, options) {
9321
9482
  let selectedTask = null;
9322
9483
  let selectedTaskId = normalizeTaskRunTaskId(taskResult.value) ?? positionalTaskId;
9323
9484
  if (nextResult.value) {
9324
- const selected = await selectNextWorkspaceTaskViaServer(context, filterResult.filters);
9485
+ const selected = await withSpinner("Selecting next ready task\u2026", () => selectNextWorkspaceTaskViaServer(context, filterResult.filters), { outputMode: context.outputMode });
9325
9486
  selectedTask = selected.task;
9326
9487
  selectedTaskId = selected.task ? readTaskId(selected.task) : null;
9327
9488
  if (!selectedTaskId) {
@@ -9332,7 +9493,7 @@ async function executeTask(context, args, options) {
9332
9493
  if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
9333
9494
  throw new CliError("task run requires an interactive terminal to pick a task; pass --next, --task <id>, or --initial-prompt/--title for an ad hoc run.", 2);
9334
9495
  }
9335
- const tasks = await listWorkspaceTasksViaServer(context, filterResult.filters);
9496
+ const tasks = await withSpinner("Reading tasks from server\u2026", () => listWorkspaceTasksViaServer(context, filterResult.filters), { outputMode: context.outputMode });
9336
9497
  selectedTask = await selectTaskWithTextPicker(tasks);
9337
9498
  selectedTaskId = selectedTask ? readTaskId(selectedTask) : null;
9338
9499
  if (!selectedTaskId) {
@@ -9342,13 +9503,13 @@ async function executeTask(context, args, options) {
9342
9503
  await runProjectMainSyncPreflight(context, { disabled: skipProjectSyncResult.value });
9343
9504
  const projectDefaults = await loadTaskRunProjectDefaults(context.projectRoot);
9344
9505
  const runtimeAdapter = normalizeRuntimeAdapter(runtimeAdapterResult.value ?? projectDefaults.runtimeAdapter);
9345
- await runFastTaskRunPreflight(context, {
9506
+ await withSpinner("Running task-run preflight\u2026", () => runFastTaskRunPreflight(context, {
9346
9507
  taskId: selectedTaskId,
9347
9508
  runtimeAdapter
9348
- });
9509
+ }), { outputMode: context.outputMode });
9349
9510
  const dirtyBaseline = await resolveDirtyBaselineForTaskRun(context, dirtyBaselineResult.value);
9350
9511
  const prMode = noPrResult.value ? "off" : normalizePrMode(prResult.value) ?? projectDefaults.prMode;
9351
- const submitted = await submitTaskRunViaServer(context, {
9512
+ const submitted = await withSpinner("Dispatching run\u2026", () => submitTaskRunViaServer(context, {
9352
9513
  runId: context.runId,
9353
9514
  taskId: selectedTaskId ?? undefined,
9354
9515
  title: titleResult.value ?? undefined,
@@ -9359,7 +9520,7 @@ async function executeTask(context, args, options) {
9359
9520
  initialPrompt: initialPromptResult.value ?? undefined,
9360
9521
  baselineMode: dirtyBaseline.mode,
9361
9522
  prMode
9362
- });
9523
+ }), { outputMode: context.outputMode });
9363
9524
  let attachDetails = null;
9364
9525
  if (!detachResult.value && context.outputMode === "text") {
9365
9526
  printFormattedOutput(formatSubmittedRun({
@@ -11537,7 +11698,7 @@ async function executeSetup(context, args) {
11537
11698
  case "check":
11538
11699
  requireNoExtraArgs(rest, `rig setup ${command}`);
11539
11700
  {
11540
- const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot));
11701
+ const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot, context.outputMode));
11541
11702
  return { ok: true, group: "setup", command, details: { checks, failures: countDoctorFailures(checks) } };
11542
11703
  }
11543
11704
  case "setup":
@@ -11546,7 +11707,7 @@ async function executeSetup(context, args) {
11546
11707
  return { ok: true, group: "setup", command };
11547
11708
  case "preflight":
11548
11709
  requireNoExtraArgs(rest, "rig setup preflight");
11549
- await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot));
11710
+ await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot, context.outputMode));
11550
11711
  return { ok: true, group: "setup", command };
11551
11712
  default:
11552
11713
  throw new CliError(`Unknown setup command: ${command}`, 1, { hint: "Run `rig setup --help` \u2014 commands are bootstrap|check|preflight." });
@@ -11567,8 +11728,8 @@ function runSetupInit(projectRoot) {
11567
11728
  }
11568
11729
  console.log("Harness directories ready.");
11569
11730
  }
11570
- async function runSetupCheck(projectRoot) {
11571
- const doctorChecks = await runRigDoctorChecks({ projectRoot });
11731
+ async function runSetupCheck(projectRoot, outputMode = "text") {
11732
+ const doctorChecks = await withSpinner("Running setup checks\u2026", (update) => runRigDoctorChecks({ projectRoot, onProgress: update }), { outputMode });
11572
11733
  console.log(formatDoctorChecks(doctorChecks));
11573
11734
  const failures = countDoctorFailures(doctorChecks);
11574
11735
  if (failures > 0) {
@@ -11576,8 +11737,8 @@ async function runSetupCheck(projectRoot) {
11576
11737
  }
11577
11738
  return doctorChecks;
11578
11739
  }
11579
- async function runSetupPreflight(projectRoot) {
11580
- await runSetupCheck(projectRoot);
11740
+ async function runSetupPreflight(projectRoot, outputMode = "text") {
11741
+ await runSetupCheck(projectRoot, outputMode);
11581
11742
  const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
11582
11743
  if (existsSync15(validationRoot)) {
11583
11744
  const validators = readdirSync3(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());