@h-rig/cli 0.0.6-alpha.67 → 0.0.6-alpha.69

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.
@@ -285,6 +285,15 @@ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
285
285
  import { resolve as resolve4 } from "path";
286
286
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
287
287
  var scopedGitHubBearerTokens = new Map;
288
+ var serverPhaseListener = null;
289
+ function setServerPhaseListener(listener) {
290
+ const previous = serverPhaseListener;
291
+ serverPhaseListener = listener;
292
+ return previous;
293
+ }
294
+ function reportServerPhase(label) {
295
+ serverPhaseListener?.(label);
296
+ }
288
297
  function cleanToken(value) {
289
298
  const trimmed = value?.trim();
290
299
  return trimmed ? trimmed : null;
@@ -327,6 +336,7 @@ async function ensureServerForCli(projectRoot) {
327
336
  try {
328
337
  const selected = resolveSelectedConnection(projectRoot);
329
338
  if (selected?.connection.kind === "remote") {
339
+ reportServerPhase(`Connecting to ${selected.alias}\u2026`);
330
340
  const authToken = readGitHubBearerTokenForRemote(projectRoot);
331
341
  const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
332
342
  return {
@@ -336,6 +346,7 @@ async function ensureServerForCli(projectRoot) {
336
346
  serverProjectRoot
337
347
  };
338
348
  }
349
+ reportServerPhase("Starting local Rig server\u2026");
339
350
  const connection = await ensureLocalRigServerConnection(projectRoot);
340
351
  return {
341
352
  baseUrl: connection.baseUrl,
@@ -442,6 +453,7 @@ async function requestServerJson(context, pathname, init = {}) {
442
453
  const headers = mergeHeaders(init.headers, server.authToken);
443
454
  if (server.serverProjectRoot)
444
455
  headers.set("x-rig-project-root", server.serverProjectRoot);
456
+ reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
445
457
  let response;
446
458
  try {
447
459
  response = await fetch(`${server.baseUrl}${pathname}`, {
@@ -470,6 +482,17 @@ ${failure.contextLine}`, 1, { hint: failure.hint });
470
482
  }
471
483
  return payload;
472
484
  }
485
+ var RESUMABLE_RUN_STATUSES = new Set([
486
+ "created",
487
+ "preparing",
488
+ "running",
489
+ "validating",
490
+ "reviewing",
491
+ "stopped",
492
+ "failed",
493
+ "needs-attention",
494
+ "needs_attention"
495
+ ]);
473
496
 
474
497
  // packages/cli/src/commands/_doctor-checks.ts
475
498
  function check(id, label, status, detail, remediation) {
@@ -590,7 +613,10 @@ async function runRigDoctorChecks(options) {
590
613
  const bunVersion = options.bunVersion ?? Bun.version;
591
614
  const request = options.requestJson ?? ((pathname, init) => requestServerJson({ projectRoot }, pathname, init));
592
615
  const loadConfig = options.loadConfig ?? loadRigConfigOrNull;
616
+ const progress = options.onProgress ?? (() => {});
617
+ progress("Checking local toolchain\u2026");
593
618
  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`)."));
619
+ progress("Loading rig.config\u2026");
594
620
  const loadedConfig = await loadConfig(projectRoot).catch(() => null);
595
621
  const config = loadedConfig ?? loadFallbackConfig(projectRoot);
596
622
  const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync4(resolve5(projectRoot, name)));
@@ -609,6 +635,7 @@ async function runRigDoctorChecks(options) {
609
635
  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));
610
636
  let server = null;
611
637
  try {
638
+ progress("Connecting to the selected Rig server\u2026");
612
639
  server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);
613
640
  checks.push(check("server", "Rig server reachable", "pass", `${server.connectionKind} ${server.baseUrl}`));
614
641
  } catch (error) {
@@ -616,18 +643,21 @@ async function runRigDoctorChecks(options) {
616
643
  }
617
644
  if (server || options.requestJson) {
618
645
  try {
646
+ progress("Checking server status\u2026");
619
647
  const status = await request("/api/server/status");
620
648
  checks.push(check("server-status", "server project status", "pass", JSON.stringify(status).slice(0, 180)));
621
649
  } catch (error) {
622
650
  checks.push(check("server-status", "server project status", "fail", errorMessage(error), "Run `rig doctor` after the selected server is reachable."));
623
651
  }
624
652
  try {
653
+ progress("Checking GitHub auth\u2026");
625
654
  const auth = await request("/api/github/auth/status");
626
655
  checks.push(isAuthenticated(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>`."));
627
656
  } catch (error) {
628
657
  checks.push(check("github-auth", "GitHub auth", "fail", errorMessage(error), "Authenticate GitHub through Rig and ensure the server exposes auth status."));
629
658
  }
630
659
  try {
660
+ progress("Checking GitHub repo permissions\u2026");
631
661
  const permissions = await request("/api/github/repo/permissions");
632
662
  const allowed = permissionAllowsPr(permissions);
633
663
  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."));
@@ -635,6 +665,7 @@ async function runRigDoctorChecks(options) {
635
665
  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."));
636
666
  }
637
667
  try {
668
+ progress("Checking GitHub issue labels\u2026");
638
669
  const labels = await request("/api/workspace/task-labels");
639
670
  const ready = labelsReady(labels);
640
671
  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."));
@@ -642,6 +673,7 @@ async function runRigDoctorChecks(options) {
642
673
  checks.push(check("task-labels", "GitHub issue labels", "warn", errorMessage(error), "Run `rig init`/`rig doctor` after label setup is wired on the server."));
643
674
  }
644
675
  try {
676
+ progress("Checking task projection\u2026");
645
677
  const projection = await request("/api/workspace/task-projection");
646
678
  checks.push(check("task-projection", "task projection", "pass", JSON.stringify(projection).slice(0, 180)));
647
679
  } catch (error) {
@@ -650,6 +682,7 @@ async function runRigDoctorChecks(options) {
650
682
  const slug = projectStatusSlug(projectRoot, config);
651
683
  if (slug) {
652
684
  try {
685
+ progress("Checking server project checkout\u2026");
653
686
  const project = await request(`/api/projects/${encodeURIComponent(slug)}`);
654
687
  checks.push(check("remote-checkout", "server project checkout", "pass", JSON.stringify(project).slice(0, 180)));
655
688
  } catch (error) {
@@ -664,6 +697,7 @@ async function runRigDoctorChecks(options) {
664
697
  }
665
698
  checks.push(githubProjectsCheck(config));
666
699
  checks.push(prMergeCheck(config));
700
+ progress("Checking Pi installation\u2026");
667
701
  const piChecks = await (options.piChecks ?? (() => buildPiSetupChecks()))().catch((error) => [{
668
702
  ok: false,
669
703
  label: "pi/pi-rig checks",
@@ -687,6 +721,127 @@ function countDoctorFailures(checks) {
687
721
  return checks.filter((entry) => entry.status === "fail").length;
688
722
  }
689
723
 
724
+ // packages/cli/src/commands/_async-ui.ts
725
+ import pc from "picocolors";
726
+
727
+ // packages/cli/src/commands/_spinner.ts
728
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
729
+ function createTtySpinner(input) {
730
+ const output = input.output ?? process.stdout;
731
+ const isTty = output.isTTY === true;
732
+ const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
733
+ let label = input.label;
734
+ let frame = 0;
735
+ let paused = false;
736
+ let stopped = false;
737
+ let lastPrintedLabel = "";
738
+ const render = () => {
739
+ if (stopped || paused)
740
+ return;
741
+ if (!isTty) {
742
+ if (label !== lastPrintedLabel) {
743
+ output.write(`${label}
744
+ `);
745
+ lastPrintedLabel = label;
746
+ }
747
+ return;
748
+ }
749
+ frame = (frame + 1) % frames.length;
750
+ const glyph = frames[frame] ?? frames[0] ?? "";
751
+ output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
752
+ };
753
+ const clearLine = () => {
754
+ if (isTty)
755
+ output.write("\r\x1B[2K");
756
+ };
757
+ render();
758
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
759
+ return {
760
+ setLabel(next) {
761
+ label = next;
762
+ render();
763
+ },
764
+ pause() {
765
+ paused = true;
766
+ clearLine();
767
+ },
768
+ resume() {
769
+ if (stopped)
770
+ return;
771
+ paused = false;
772
+ render();
773
+ },
774
+ stop(finalLine) {
775
+ if (stopped)
776
+ return;
777
+ stopped = true;
778
+ if (timer)
779
+ clearInterval(timer);
780
+ clearLine();
781
+ if (finalLine)
782
+ output.write(`${finalLine}
783
+ `);
784
+ }
785
+ };
786
+ }
787
+
788
+ // packages/cli/src/commands/_async-ui.ts
789
+ var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
790
+ var DONE_SYMBOL = pc.green("\u25C7");
791
+ var FAIL_SYMBOL = pc.red("\u25A0");
792
+ var activeUpdate = null;
793
+ async function withSpinner(label, work, options = {}) {
794
+ if (options.outputMode === "json") {
795
+ return work(() => {});
796
+ }
797
+ if (activeUpdate) {
798
+ const outer = activeUpdate;
799
+ outer(label);
800
+ return work(outer);
801
+ }
802
+ const output = options.output ?? process.stderr;
803
+ const isTty = output.isTTY === true;
804
+ let lastLabel = label;
805
+ if (!isTty) {
806
+ output.write(`${label}
807
+ `);
808
+ const update2 = (next) => {
809
+ lastLabel = next;
810
+ };
811
+ activeUpdate = update2;
812
+ const previousListener2 = setServerPhaseListener(update2);
813
+ try {
814
+ return await work(update2);
815
+ } finally {
816
+ activeUpdate = null;
817
+ setServerPhaseListener(previousListener2);
818
+ }
819
+ }
820
+ const spinner = createTtySpinner({
821
+ label,
822
+ output,
823
+ frames: CLACK_SPINNER_FRAMES,
824
+ styleFrame: (frame) => pc.magenta(frame)
825
+ });
826
+ const update = (next) => {
827
+ lastLabel = next;
828
+ spinner.setLabel(next);
829
+ };
830
+ activeUpdate = update;
831
+ const previousListener = setServerPhaseListener(update);
832
+ try {
833
+ const result = await work(update);
834
+ spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
835
+ return result;
836
+ } catch (error) {
837
+ spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
838
+ throw error;
839
+ } finally {
840
+ activeUpdate = null;
841
+ setServerPhaseListener(previousListener);
842
+ }
843
+ }
844
+
690
845
  // packages/cli/src/commands/setup.ts
691
846
  async function executeSetup(context, args) {
692
847
  const [command = "check", ...rest] = args;
@@ -717,7 +872,7 @@ async function executeSetup(context, args) {
717
872
  case "check":
718
873
  requireNoExtraArgs(rest, `rig setup ${command}`);
719
874
  {
720
- const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot));
875
+ const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot, context.outputMode));
721
876
  return { ok: true, group: "setup", command, details: { checks, failures: countDoctorFailures(checks) } };
722
877
  }
723
878
  case "setup":
@@ -726,7 +881,7 @@ async function executeSetup(context, args) {
726
881
  return { ok: true, group: "setup", command };
727
882
  case "preflight":
728
883
  requireNoExtraArgs(rest, "rig setup preflight");
729
- await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot));
884
+ await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot, context.outputMode));
730
885
  return { ok: true, group: "setup", command };
731
886
  default:
732
887
  throw new CliError(`Unknown setup command: ${command}`, 1, { hint: "Run `rig setup --help` \u2014 commands are bootstrap|check|preflight." });
@@ -763,8 +918,8 @@ function runSetupInit(projectRoot) {
763
918
  }
764
919
  console.log("Harness directories ready.");
765
920
  }
766
- async function runSetupCheck(projectRoot) {
767
- const doctorChecks = await runRigDoctorChecks({ projectRoot });
921
+ async function runSetupCheck(projectRoot, outputMode = "text") {
922
+ const doctorChecks = await withSpinner("Running setup checks\u2026", (update) => runRigDoctorChecks({ projectRoot, onProgress: update }), { outputMode });
768
923
  console.log(formatDoctorChecks(doctorChecks));
769
924
  const failures = countDoctorFailures(doctorChecks);
770
925
  if (failures > 0) {
@@ -772,8 +927,8 @@ async function runSetupCheck(projectRoot) {
772
927
  }
773
928
  return doctorChecks;
774
929
  }
775
- async function runSetupPreflight(projectRoot) {
776
- await runSetupCheck(projectRoot);
930
+ async function runSetupPreflight(projectRoot, outputMode = "text") {
931
+ await runSetupCheck(projectRoot, outputMode);
777
932
  const validationRoot = resolve6(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
778
933
  if (existsSync5(validationRoot)) {
779
934
  const validators = readdirSync(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
@@ -1,6 +1,6 @@
1
1
  // @bun
2
2
  // packages/cli/src/commands/stats.ts
3
- import pc3 from "picocolors";
3
+ import pc4 from "picocolors";
4
4
 
5
5
  // packages/cli/src/runner.ts
6
6
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
@@ -152,6 +152,15 @@ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
152
152
  import { resolve as resolve2 } from "path";
153
153
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
154
154
  var scopedGitHubBearerTokens = new Map;
155
+ var serverPhaseListener = null;
156
+ function setServerPhaseListener(listener) {
157
+ const previous = serverPhaseListener;
158
+ serverPhaseListener = listener;
159
+ return previous;
160
+ }
161
+ function reportServerPhase(label) {
162
+ serverPhaseListener?.(label);
163
+ }
155
164
  function cleanToken(value) {
156
165
  const trimmed = value?.trim();
157
166
  return trimmed ? trimmed : null;
@@ -194,6 +203,7 @@ async function ensureServerForCli(projectRoot) {
194
203
  try {
195
204
  const selected = resolveSelectedConnection(projectRoot);
196
205
  if (selected?.connection.kind === "remote") {
206
+ reportServerPhase(`Connecting to ${selected.alias}\u2026`);
197
207
  const authToken = readGitHubBearerTokenForRemote(projectRoot);
198
208
  const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
199
209
  return {
@@ -203,6 +213,7 @@ async function ensureServerForCli(projectRoot) {
203
213
  serverProjectRoot
204
214
  };
205
215
  }
216
+ reportServerPhase("Starting local Rig server\u2026");
206
217
  const connection = await ensureLocalRigServerConnection(projectRoot);
207
218
  return {
208
219
  baseUrl: connection.baseUrl,
@@ -309,6 +320,7 @@ async function requestServerJson(context, pathname, init = {}) {
309
320
  const headers = mergeHeaders(init.headers, server.authToken);
310
321
  if (server.serverProjectRoot)
311
322
  headers.set("x-rig-project-root", server.serverProjectRoot);
323
+ reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
312
324
  let response;
313
325
  try {
314
326
  response = await fetch(`${server.baseUrl}${pathname}`, {
@@ -337,6 +349,17 @@ ${failure.contextLine}`, 1, { hint: failure.hint });
337
349
  }
338
350
  return payload;
339
351
  }
352
+ var RESUMABLE_RUN_STATUSES = new Set([
353
+ "created",
354
+ "preparing",
355
+ "running",
356
+ "validating",
357
+ "reviewing",
358
+ "stopped",
359
+ "failed",
360
+ "needs-attention",
361
+ "needs_attention"
362
+ ]);
340
363
 
341
364
  // packages/cli/src/commands/_cli-format.ts
342
365
  import { log, note } from "@clack/prompts";
@@ -363,9 +386,130 @@ function formatNextSteps(steps) {
363
386
  return [pc.bold("Next"), ...steps.map((step) => `${pc.dim("\u203A")} ${step}`)];
364
387
  }
365
388
 
389
+ // packages/cli/src/commands/_async-ui.ts
390
+ import pc2 from "picocolors";
391
+
392
+ // packages/cli/src/commands/_spinner.ts
393
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
394
+ function createTtySpinner(input) {
395
+ const output = input.output ?? process.stdout;
396
+ const isTty = output.isTTY === true;
397
+ const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
398
+ let label = input.label;
399
+ let frame = 0;
400
+ let paused = false;
401
+ let stopped = false;
402
+ let lastPrintedLabel = "";
403
+ const render = () => {
404
+ if (stopped || paused)
405
+ return;
406
+ if (!isTty) {
407
+ if (label !== lastPrintedLabel) {
408
+ output.write(`${label}
409
+ `);
410
+ lastPrintedLabel = label;
411
+ }
412
+ return;
413
+ }
414
+ frame = (frame + 1) % frames.length;
415
+ const glyph = frames[frame] ?? frames[0] ?? "";
416
+ output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
417
+ };
418
+ const clearLine = () => {
419
+ if (isTty)
420
+ output.write("\r\x1B[2K");
421
+ };
422
+ render();
423
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
424
+ return {
425
+ setLabel(next) {
426
+ label = next;
427
+ render();
428
+ },
429
+ pause() {
430
+ paused = true;
431
+ clearLine();
432
+ },
433
+ resume() {
434
+ if (stopped)
435
+ return;
436
+ paused = false;
437
+ render();
438
+ },
439
+ stop(finalLine) {
440
+ if (stopped)
441
+ return;
442
+ stopped = true;
443
+ if (timer)
444
+ clearInterval(timer);
445
+ clearLine();
446
+ if (finalLine)
447
+ output.write(`${finalLine}
448
+ `);
449
+ }
450
+ };
451
+ }
452
+
453
+ // packages/cli/src/commands/_async-ui.ts
454
+ var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
455
+ var DONE_SYMBOL = pc2.green("\u25C7");
456
+ var FAIL_SYMBOL = pc2.red("\u25A0");
457
+ var activeUpdate = null;
458
+ async function withSpinner(label, work, options = {}) {
459
+ if (options.outputMode === "json") {
460
+ return work(() => {});
461
+ }
462
+ if (activeUpdate) {
463
+ const outer = activeUpdate;
464
+ outer(label);
465
+ return work(outer);
466
+ }
467
+ const output = options.output ?? process.stderr;
468
+ const isTty = output.isTTY === true;
469
+ let lastLabel = label;
470
+ if (!isTty) {
471
+ output.write(`${label}
472
+ `);
473
+ const update2 = (next) => {
474
+ lastLabel = next;
475
+ };
476
+ activeUpdate = update2;
477
+ const previousListener2 = setServerPhaseListener(update2);
478
+ try {
479
+ return await work(update2);
480
+ } finally {
481
+ activeUpdate = null;
482
+ setServerPhaseListener(previousListener2);
483
+ }
484
+ }
485
+ const spinner = createTtySpinner({
486
+ label,
487
+ output,
488
+ frames: CLACK_SPINNER_FRAMES,
489
+ styleFrame: (frame) => pc2.magenta(frame)
490
+ });
491
+ const update = (next) => {
492
+ lastLabel = next;
493
+ spinner.setLabel(next);
494
+ };
495
+ activeUpdate = update;
496
+ const previousListener = setServerPhaseListener(update);
497
+ try {
498
+ const result = await work(update);
499
+ spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
500
+ return result;
501
+ } catch (error) {
502
+ spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
503
+ throw error;
504
+ } finally {
505
+ activeUpdate = null;
506
+ setServerPhaseListener(previousListener);
507
+ }
508
+ }
509
+
366
510
  // packages/cli/src/commands/_help-catalog.ts
367
511
  import { intro, log as log2, note as note2, outro } from "@clack/prompts";
368
- import pc2 from "picocolors";
512
+ import pc3 from "picocolors";
369
513
  var TOP_LEVEL_SECTIONS = [
370
514
  {
371
515
  title: "Start here",
@@ -647,13 +791,13 @@ var ADVANCED_GROUPS = [
647
791
  ];
648
792
  var ALL_GROUPS = [...PRIMARY_GROUPS, ...ADVANCED_GROUPS];
649
793
  function heading(title) {
650
- return pc2.bold(pc2.cyan(title));
794
+ return pc3.bold(pc3.cyan(title));
651
795
  }
652
796
  function renderRigBanner(version) {
653
- const m = (s) => pc2.bold(pc2.magenta(s));
654
- const c = (s) => pc2.bold(pc2.cyan(s));
655
- const y = (s) => pc2.yellow(s);
656
- const d = (s) => pc2.dim(s);
797
+ const m = (s) => pc3.bold(pc3.magenta(s));
798
+ const c = (s) => pc3.bold(pc3.cyan(s));
799
+ const y = (s) => pc3.yellow(s);
800
+ const d = (s) => pc3.dim(s);
657
801
  const lines = [
658
802
  m(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + d(" \u2591\u2592\u2593\u2588 "),
659
803
  m(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ") + d("\u2593\u2592\u2591"),
@@ -662,7 +806,7 @@ function renderRigBanner(version) {
662
806
  y(" \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + d(" \u2592\u2591"),
663
807
  y(" \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D "),
664
808
  "",
665
- ` ${c("\u25E2\u25E4")} ${pc2.bold("the control rig for autonomous coding agents")} ${m("//")} ${d("you are the operator")}`,
809
+ ` ${c("\u25E2\u25E4")} ${pc3.bold("the control rig for autonomous coding agents")} ${m("//")} ${d("you are the operator")}`,
666
810
  version ? ` ${d(`v${version} \xB7 jack in: rig task run --next`)}` : ` ${d("jack in: rig task run --next")}`
667
811
  ];
668
812
  return lines.join(`
@@ -670,7 +814,7 @@ function renderRigBanner(version) {
670
814
  }
671
815
  function commandLine(command, description) {
672
816
  const commandColumn = command.length >= 38 ? `${command} ` : command.padEnd(38);
673
- return `${pc2.dim("\u2502")} ${pc2.bold(commandColumn)} ${description}`;
817
+ return `${pc3.dim("\u2502")} ${pc3.bold(commandColumn)} ${description}`;
674
818
  }
675
819
  function renderCommandBlock(commands) {
676
820
  return commands.map((entry) => commandLine(entry.command, entry.description)).join(`
@@ -680,37 +824,37 @@ function renderGroup(group) {
680
824
  const lines = [
681
825
  `${heading(`rig ${group.name}`)} \u2014 ${group.summary}`,
682
826
  "",
683
- pc2.bold("Usage"),
827
+ pc3.bold("Usage"),
684
828
  ...group.usage.map((line) => ` ${line}`),
685
829
  "",
686
- pc2.bold("Commands"),
830
+ pc3.bold("Commands"),
687
831
  ...group.commands.map((entry) => commandLine(entry.command, entry.description))
688
832
  ];
689
833
  if (group.examples?.length) {
690
- lines.push("", pc2.bold("Examples"), ...group.examples.map((line) => ` ${pc2.dim("$")} ${line}`));
834
+ lines.push("", pc3.bold("Examples"), ...group.examples.map((line) => ` ${pc3.dim("$")} ${line}`));
691
835
  }
692
836
  if (group.next?.length) {
693
- lines.push("", pc2.bold("Next steps"), ...group.next.map((line) => ` ${pc2.dim("\u203A")} ${line}`));
837
+ lines.push("", pc3.bold("Next steps"), ...group.next.map((line) => ` ${pc3.dim("\u203A")} ${line}`));
694
838
  }
695
839
  if (group.advanced?.length) {
696
- lines.push("", pc2.bold("Compatibility / advanced"), ...group.advanced.map((line) => ` ${pc2.dim("\u203A")} ${line}`));
840
+ lines.push("", pc3.bold("Compatibility / advanced"), ...group.advanced.map((line) => ` ${pc3.dim("\u203A")} ${line}`));
697
841
  }
698
842
  return lines.join(`
699
843
  `);
700
844
  }
701
845
  function renderTopLevelHelp() {
702
846
  return [
703
- `${heading("rig")} ${pc2.dim("\u2014 server-owned task/run control plane for Pi-backed engineering work")}`,
704
- pc2.dim("Current path: select a server, choose a task, submit a run, attach with native Pi, clear inbox gates."),
847
+ `${heading("rig")} ${pc3.dim("\u2014 server-owned task/run control plane for Pi-backed engineering work")}`,
848
+ pc3.dim("Current path: select a server, choose a task, submit a run, attach with native Pi, clear inbox gates."),
705
849
  "",
706
850
  ...TOP_LEVEL_SECTIONS.flatMap((section) => [
707
- `${pc2.bold(pc2.magenta(`\u25C7 ${section.title}`))} \u2014 ${pc2.dim(section.subtitle)}`,
851
+ `${pc3.bold(pc3.magenta(`\u25C7 ${section.title}`))} \u2014 ${pc3.dim(section.subtitle)}`,
708
852
  renderCommandBlock(section.commands),
709
853
  ""
710
854
  ]),
711
- pc2.dim("More: `rig help --advanced` for dev/compatibility commands; `rig <group> --help` for rich per-group help; `rig --version` for the installed version."),
855
+ pc3.dim("More: `rig help --advanced` for dev/compatibility commands; `rig <group> --help` for rich per-group help; `rig --version` for the installed version."),
712
856
  "",
713
- pc2.bold("Global options"),
857
+ pc3.bold("Global options"),
714
858
  commandLine("--project <path>", "Use a project root instead of auto-discovery."),
715
859
  commandLine("--json", "Emit structured output for scripts/agents."),
716
860
  commandLine("--dry-run", "Print the command plan without mutating state.")
@@ -834,11 +978,11 @@ function formatStatsReport(stats) {
834
978
  const keyWidth = Math.max(...rows.map(([key]) => key.length));
835
979
  const lines = [
836
980
  formatSection("Fleet stats", window),
837
- ...rows.map(([key, value]) => `${pc3.dim("\u2502")} ${pc3.dim(key.padEnd(keyWidth + 2))} ${value}`)
981
+ ...rows.map(([key, value]) => `${pc4.dim("\u2502")} ${pc4.dim(key.padEnd(keyWidth + 2))} ${value}`)
838
982
  ];
839
983
  const otherStatuses = Object.entries(stats.statusCounts).filter(([status]) => !["completed", "failed", "needs-attention"].includes(status)).sort(([, left], [, right]) => right - left);
840
984
  if (otherStatuses.length > 0) {
841
- lines.push(`${pc3.dim("\u2502")} ${pc3.dim("other".padEnd(keyWidth + 2))} ${otherStatuses.map(([status, count]) => `${status}:${count}`).join(" ")}`);
985
+ lines.push(`${pc4.dim("\u2502")} ${pc4.dim("other".padEnd(keyWidth + 2))} ${otherStatuses.map(([status, count]) => `${status}:${count}`).join(" ")}`);
842
986
  }
843
987
  lines.push("", ...formatNextSteps([
844
988
  "Inspect a run: `rig run list` then `rig run show <run-id>`",
@@ -857,7 +1001,8 @@ async function executeStats(context, args) {
857
1001
  const sinceResult = takeOption(pending, "--since");
858
1002
  requireNoExtraArgs(sinceResult.rest, "rig stats [show] [--since <7d|30d|ISO date>]");
859
1003
  const since = parseSinceOption(sinceResult.value);
860
- const stats = isRemoteConnectionSelected(context.projectRoot) ? await requestServerJson(context, `/api/stats${since ? `?since=${encodeURIComponent(since.toISOString())}` : ""}`) : computeRigStats(context.projectRoot, { since });
1004
+ const remoteStats = isRemoteConnectionSelected(context.projectRoot);
1005
+ 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 });
861
1006
  if (context.outputMode === "text") {
862
1007
  printFormattedOutput(formatStatsReport(stats));
863
1008
  }
@@ -543,6 +543,10 @@ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
543
543
 
544
544
  // packages/cli/src/commands/_server-client.ts
545
545
  var scopedGitHubBearerTokens = new Map;
546
+ var serverPhaseListener = null;
547
+ function reportServerPhase(label) {
548
+ serverPhaseListener?.(label);
549
+ }
546
550
  function cleanToken(value) {
547
551
  const trimmed = value?.trim();
548
552
  return trimmed ? trimmed : null;
@@ -585,6 +589,7 @@ async function ensureServerForCli(projectRoot) {
585
589
  try {
586
590
  const selected = resolveSelectedConnection(projectRoot);
587
591
  if (selected?.connection.kind === "remote") {
592
+ reportServerPhase(`Connecting to ${selected.alias}\u2026`);
588
593
  const authToken = readGitHubBearerTokenForRemote(projectRoot);
589
594
  const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
590
595
  return {
@@ -594,6 +599,7 @@ async function ensureServerForCli(projectRoot) {
594
599
  serverProjectRoot
595
600
  };
596
601
  }
602
+ reportServerPhase("Starting local Rig server\u2026");
597
603
  const connection = await ensureLocalRigServerConnection(projectRoot);
598
604
  return {
599
605
  baseUrl: connection.baseUrl,
@@ -700,6 +706,7 @@ async function requestServerJson(context, pathname, init = {}) {
700
706
  const headers = mergeHeaders(init.headers, server.authToken);
701
707
  if (server.serverProjectRoot)
702
708
  headers.set("x-rig-project-root", server.serverProjectRoot);
709
+ reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
703
710
  let response;
704
711
  try {
705
712
  response = await fetch(`${server.baseUrl}${pathname}`, {
@@ -736,6 +743,17 @@ async function updateWorkspaceTaskViaServer(context, input) {
736
743
  });
737
744
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
738
745
  }
746
+ var RESUMABLE_RUN_STATUSES = new Set([
747
+ "created",
748
+ "preparing",
749
+ "running",
750
+ "validating",
751
+ "reviewing",
752
+ "stopped",
753
+ "failed",
754
+ "needs-attention",
755
+ "needs_attention"
756
+ ]);
739
757
 
740
758
  // packages/cli/src/commands/task-run-driver.ts
741
759
  var PI_CANONICAL_RUN_STAGES = [