@h-rig/cli 0.0.6-alpha.88 → 0.0.6-alpha.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/bin/rig.js +1159 -292
  2. package/dist/src/app/board.js +462 -48
  3. package/dist/src/app-opentui/adapters/doctor.js +458 -46
  4. package/dist/src/app-opentui/adapters/family.js +670 -147
  5. package/dist/src/app-opentui/adapters/fleet.js +477 -46
  6. package/dist/src/app-opentui/adapters/inbox.js +477 -46
  7. package/dist/src/app-opentui/adapters/init.js +497 -74
  8. package/dist/src/app-opentui/adapters/inspect.js +477 -46
  9. package/dist/src/app-opentui/adapters/pi-attach.js +484 -53
  10. package/dist/src/app-opentui/adapters/run-detail.js +477 -46
  11. package/dist/src/app-opentui/adapters/server.d.ts +26 -0
  12. package/dist/src/app-opentui/adapters/server.js +676 -59
  13. package/dist/src/app-opentui/adapters/tasks.js +539 -81
  14. package/dist/src/app-opentui/autocomplete.js +4 -2
  15. package/dist/src/app-opentui/bootstrap.js +1155 -288
  16. package/dist/src/app-opentui/command-palette.js +37 -10
  17. package/dist/src/app-opentui/index.js +566 -89
  18. package/dist/src/app-opentui/intent.js +33 -8
  19. package/dist/src/app-opentui/keymap.js +43 -29
  20. package/dist/src/app-opentui/pi-host-child.js +465 -53
  21. package/dist/src/app-opentui/react/App.js +104 -44
  22. package/dist/src/app-opentui/react/ChromeHost.js +16 -4
  23. package/dist/src/app-opentui/react/launch.js +555 -88
  24. package/dist/src/app-opentui/react/nav.js +1 -1
  25. package/dist/src/app-opentui/registry.js +1062 -237
  26. package/dist/src/app-opentui/remote-link.d.ts +10 -0
  27. package/dist/src/app-opentui/remote-link.js +47 -0
  28. package/dist/src/app-opentui/runtime.js +566 -89
  29. package/dist/src/app-opentui/scenes/doctor.js +1 -1
  30. package/dist/src/app-opentui/scenes/error.js +50 -4
  31. package/dist/src/app-opentui/scenes/family.js +60 -6
  32. package/dist/src/app-opentui/scenes/fleet.js +65 -13
  33. package/dist/src/app-opentui/scenes/help.js +4 -2
  34. package/dist/src/app-opentui/scenes/init.js +12 -12
  35. package/dist/src/app-opentui/scenes/main.js +7 -7
  36. package/dist/src/app-opentui/scenes/server.js +83 -11
  37. package/dist/src/app-opentui/scenes/tasks.js +79 -16
  38. package/dist/src/app-opentui/state.js +25 -5
  39. package/dist/src/app-opentui/surface-catalog.js +4 -2
  40. package/dist/src/app-opentui/types.d.ts +1 -1
  41. package/dist/src/commands/_cli-format.d.ts +10 -1
  42. package/dist/src/commands/_cli-format.js +5 -2
  43. package/dist/src/commands/_connection-state.d.ts +11 -1
  44. package/dist/src/commands/_connection-state.js +50 -5
  45. package/dist/src/commands/_doctor-checks.js +458 -46
  46. package/dist/src/commands/_help-catalog.js +4 -2
  47. package/dist/src/commands/_json-output.js +4 -0
  48. package/dist/src/commands/_operator-view.js +465 -53
  49. package/dist/src/commands/_pi-frontend.js +465 -53
  50. package/dist/src/commands/_preflight.js +509 -72
  51. package/dist/src/commands/_server-client.d.ts +33 -0
  52. package/dist/src/commands/_server-client.js +477 -46
  53. package/dist/src/commands/_server-events.js +446 -41
  54. package/dist/src/commands/_snapshot-upload.js +460 -48
  55. package/dist/src/commands/connect.js +620 -15
  56. package/dist/src/commands/doctor.js +458 -46
  57. package/dist/src/commands/github.js +462 -50
  58. package/dist/src/commands/inbox.js +458 -46
  59. package/dist/src/commands/init.js +497 -74
  60. package/dist/src/commands/inspect.js +458 -46
  61. package/dist/src/commands/run.js +465 -53
  62. package/dist/src/commands/server.js +647 -163
  63. package/dist/src/commands/setup.js +463 -51
  64. package/dist/src/commands/stats.js +462 -48
  65. package/dist/src/commands/task-run-driver.js +464 -52
  66. package/dist/src/commands/task.js +520 -81
  67. package/dist/src/commands.js +670 -147
  68. package/dist/src/index.js +674 -147
  69. package/dist/src/launcher.js +4 -0
  70. package/package.json +8 -8
package/dist/bin/rig.js CHANGED
@@ -338,7 +338,8 @@ __export(exports__connection_state, {
338
338
  resolveGlobalConnectionsPath: () => resolveGlobalConnectionsPath,
339
339
  readRepoConnection: () => readRepoConnection,
340
340
  readGlobalConnections: () => readGlobalConnections,
341
- isRemoteConnectionSelected: () => isRemoteConnectionSelected
341
+ isRemoteConnectionSelected: () => isRemoteConnectionSelected,
342
+ clearRepoServerProjectRoot: () => clearRepoServerProjectRoot
342
343
  });
343
344
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
344
345
  import { homedir } from "os";
@@ -422,12 +423,28 @@ function readRepoConnection(projectRoot) {
422
423
  selected,
423
424
  project: typeof record.project === "string" ? record.project : undefined,
424
425
  linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
425
- serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
426
+ serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined,
427
+ serverProjectRootAlias: typeof record.serverProjectRootAlias === "string" && record.serverProjectRootAlias.trim() ? record.serverProjectRootAlias.trim() : undefined,
428
+ serverProjectRootBaseUrl: typeof record.serverProjectRootBaseUrl === "string" && record.serverProjectRootBaseUrl.trim() ? record.serverProjectRootBaseUrl.trim().replace(/\/+$/, "") : undefined
426
429
  };
427
430
  }
428
431
  function writeRepoConnection(projectRoot, state) {
429
432
  writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
430
433
  }
434
+ function rootAllowedForSelection(repo, connection) {
435
+ const root = repo.serverProjectRoot?.trim();
436
+ if (!root)
437
+ return;
438
+ if (connection.kind === "remote") {
439
+ if (repo.serverProjectRootAlias !== repo.selected)
440
+ return;
441
+ if (repo.serverProjectRootBaseUrl !== connection.baseUrl)
442
+ return;
443
+ } else if (repo.serverProjectRootAlias && repo.serverProjectRootAlias !== repo.selected) {
444
+ return;
445
+ }
446
+ return root;
447
+ }
431
448
  function resolveSelectedConnection(projectRoot, options = {}) {
432
449
  const repo = readRepoConnection(projectRoot);
433
450
  if (!repo)
@@ -439,13 +456,41 @@ function resolveSelectedConnection(projectRoot, options = {}) {
439
456
  if (!connection) {
440
457
  throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
441
458
  }
442
- return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
459
+ return { alias: repo.selected, connection, serverProjectRoot: rootAllowedForSelection(repo, connection) };
443
460
  }
444
- function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
461
+ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot, metadata = {}) {
445
462
  const repo = readRepoConnection(projectRoot);
446
463
  if (!repo)
447
464
  return;
448
- writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
465
+ let inferred = metadata;
466
+ if (!inferred.alias || !inferred.baseUrl) {
467
+ try {
468
+ const selected = resolveSelectedConnection(projectRoot);
469
+ if (selected?.connection.kind === "remote") {
470
+ inferred = {
471
+ alias: inferred.alias ?? selected.alias,
472
+ baseUrl: inferred.baseUrl ?? selected.connection.baseUrl
473
+ };
474
+ }
475
+ } catch {}
476
+ }
477
+ writeRepoConnection(projectRoot, {
478
+ ...repo,
479
+ ...metadata.project ? { project: metadata.project } : {},
480
+ serverProjectRoot,
481
+ ...inferred.alias ? { serverProjectRootAlias: inferred.alias } : {},
482
+ ...inferred.baseUrl ? { serverProjectRootBaseUrl: inferred.baseUrl.replace(/\/+$/, "") } : {}
483
+ });
484
+ }
485
+ function clearRepoServerProjectRoot(projectRoot) {
486
+ const repo = readRepoConnection(projectRoot);
487
+ if (!repo)
488
+ return;
489
+ writeRepoConnection(projectRoot, {
490
+ selected: repo.selected,
491
+ ...repo.project ? { project: repo.project } : {},
492
+ ...repo.linkedAt ? { linkedAt: repo.linkedAt } : {}
493
+ });
449
494
  }
450
495
  function isRemoteConnectionSelected(projectRoot) {
451
496
  return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
@@ -472,12 +517,16 @@ __export(exports__server_client, {
472
517
  respondRunPiExtensionUiViaServer: () => respondRunPiExtensionUiViaServer,
473
518
  resolveServerConnectionLabel: () => resolveServerConnectionLabel,
474
519
  requestServerJson: () => requestServerJson,
520
+ repairRemoteProjectRootLink: () => repairRemoteProjectRootLink,
475
521
  registerProjectViaServer: () => registerProjectViaServer,
476
522
  prepareRemoteCheckoutViaServer: () => prepareRemoteCheckoutViaServer,
477
523
  postGitHubTokenViaServer: () => postGitHubTokenViaServer,
524
+ normalizeRepoSlug: () => normalizeRepoSlug,
478
525
  listWorkspaceTasksViaServer: () => listWorkspaceTasksViaServer,
479
526
  listRunsViaServer: () => listRunsViaServer,
480
527
  listGitHubProjectsViaServer: () => listGitHubProjectsViaServer,
528
+ isRemoteProjectRootLinkError: () => isRemoteProjectRootLinkError,
529
+ inspectRemoteProjectLink: () => inspectRemoteProjectLink,
481
530
  getWorkspaceTaskViaServer: () => getWorkspaceTaskViaServer,
482
531
  getRunTimelineViaServer: () => getRunTimelineViaServer,
483
532
  getRunPiStatusViaServer: () => getRunPiStatusViaServer,
@@ -489,13 +538,15 @@ __export(exports__server_client, {
489
538
  getRunDetailsViaServer: () => getRunDetailsViaServer,
490
539
  getGitHubProjectStatusFieldViaServer: () => getGitHubProjectStatusFieldViaServer,
491
540
  getGitHubAuthStatusViaServer: () => getGitHubAuthStatusViaServer,
541
+ formatRemoteProjectLinkHint: () => formatRemoteProjectLinkHint,
492
542
  ensureTaskLabelsViaServer: () => ensureTaskLabelsViaServer,
493
543
  ensureServerForCli: () => ensureServerForCli,
544
+ ensureRemoteProjectRootLink: () => ensureRemoteProjectRootLink,
494
545
  buildRunPiEventsWebSocketUrl: () => buildRunPiEventsWebSocketUrl,
495
546
  abortRunPiViaServer: () => abortRunPiViaServer
496
547
  });
497
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
498
- import { resolve as resolve3 } from "path";
548
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
549
+ import { dirname as dirname2, isAbsolute, resolve as resolve3 } from "path";
499
550
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
500
551
  function setServerPhaseListener(listener) {
501
552
  const previous = serverPhaseListener;
@@ -541,11 +592,10 @@ function readStoredGitHubAuthToken(projectRoot) {
541
592
  const parsed = readRemoteAuthState(projectRoot);
542
593
  return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
543
594
  }
544
- function inferRemoteServerProjectRootFromAuthState(projectRoot) {
545
- const repo = readRepoConnection(projectRoot);
546
- const slug = repo?.project?.trim();
547
- if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
595
+ function inferRemoteServerProjectRootCandidateFromAuthState(projectRoot, repoSlug) {
596
+ if (!/^[^/]+\/[^/]+$/.test(repoSlug))
548
597
  return null;
598
+ const repo = readRepoConnection(projectRoot);
549
599
  const auth = readRemoteAuthState(projectRoot);
550
600
  const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
551
601
  const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
@@ -554,25 +604,426 @@ function inferRemoteServerProjectRootFromAuthState(projectRoot) {
554
604
  const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
555
605
  if (!checkoutBaseDir)
556
606
  return null;
557
- const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
558
- writeRepoServerProjectRoot(projectRoot, inferred);
559
- return inferred;
607
+ return `${checkoutBaseDir.replace(/\/+$/, "")}/${repoSlug}`;
560
608
  }
561
609
  function readLocalConnectionFallbackToken(projectRoot) {
562
610
  return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
563
611
  }
612
+ function normalizeRepoSlug(value) {
613
+ const slug = value?.trim();
614
+ return slug && /^[^/\s]+\/[^/\s]+$/.test(slug) ? slug : null;
615
+ }
616
+ function readProjectLinkSlug(projectRoot) {
617
+ const path = resolve3(projectRoot, ".rig", "state", "project-link.json");
618
+ if (!existsSync3(path))
619
+ return null;
620
+ try {
621
+ const parsed = JSON.parse(readFileSync2(path, "utf8"));
622
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
623
+ return null;
624
+ return normalizeRepoSlug(typeof parsed.repoSlug === "string" ? String(parsed.repoSlug) : null);
625
+ } catch {
626
+ return null;
627
+ }
628
+ }
629
+ function writeProjectLinkConnection(projectRoot, repoSlug, alias) {
630
+ const path = resolve3(projectRoot, ".rig", "state", "project-link.json");
631
+ mkdirSync3(dirname2(path), { recursive: true });
632
+ writeFileSync2(path, `${JSON.stringify({ repoSlug, connection: alias, linkedAt: new Date().toISOString() }, null, 2)}
633
+ `, "utf8");
634
+ }
635
+ function remoteLinkRepairCommand(repoSlug) {
636
+ return repoSlug ? `rig server repair-link --repo ${repoSlug}` : "rig server repair-link --repo owner/repo";
637
+ }
638
+ function isRemoteProjectRootLinkError(error) {
639
+ const text = error instanceof Error ? error.message : String(error ?? "");
640
+ return /no server-host project root link|remote project(?:-root)? link|x-rig-project-root|serverProjectRoot/i.test(text);
641
+ }
642
+ function formatRemoteProjectLinkHint(resolution) {
643
+ const repair = remoteLinkRepairCommand(resolution.repoSlug);
644
+ if (resolution.status === "auth_required") {
645
+ return `Authenticate the selected remote${resolution.alias ? ` (${resolution.alias})` : ""}, then run \`${repair}\`. Use \`rig github auth import-gh\` or \`rig init --repair --server remote --github-auth device${resolution.repoSlug ? ` --repo ${resolution.repoSlug}` : ""}\`.`;
646
+ }
647
+ if (resolution.status === "missing_project") {
648
+ return `Record the repo slug, then run \`${repair}\` (or \`rig init --repo owner/repo --server remote --github-auth device\`).`;
649
+ }
650
+ if (resolution.status === "not_remote")
651
+ return "Select a remote server with `rig server use <alias>` before repairing a remote project link.";
652
+ return `Run \`${repair}\` to backfill or prepare a server-host checkout for the selected remote${resolution.alias ? ` (${resolution.alias})` : ""}.`;
653
+ }
654
+ function remoteProjectLinkFailure(input) {
655
+ const partial = {
656
+ status: input.status,
657
+ alias: input.alias,
658
+ baseUrl: input.baseUrl,
659
+ repoSlug: input.repoSlug
660
+ };
661
+ return {
662
+ ok: false,
663
+ ...input,
664
+ hint: formatRemoteProjectLinkHint(partial),
665
+ next: remoteLinkRepairCommand(input.repoSlug)
666
+ };
667
+ }
668
+ function remoteProjectLinkSuccess(input) {
669
+ return {
670
+ ok: true,
671
+ status: input.status,
672
+ alias: input.alias,
673
+ baseUrl: input.baseUrl,
674
+ repoSlug: input.repoSlug,
675
+ serverProjectRoot: input.serverProjectRoot,
676
+ source: input.source,
677
+ prepared: input.prepared ?? input.status === "prepared",
678
+ validated: input.validated ?? false,
679
+ message: input.message ?? `Remote project link ready for ${input.repoSlug}.`,
680
+ hint: "Remote-scoped task/run/status requests will send x-rig-project-root for this server-host checkout.",
681
+ next: "rig task list"
682
+ };
683
+ }
684
+ function localCheckoutPathCandidate(projectRoot, candidate) {
685
+ try {
686
+ const local = resolve3(projectRoot);
687
+ const resolved = resolve3(candidate);
688
+ return resolved === local || resolved.startsWith(`${local}/`);
689
+ } catch {
690
+ return false;
691
+ }
692
+ }
693
+ async function requestRemoteProjectLinkJson(baseUrl, pathname, authToken, init = {}) {
694
+ const requestUrl = new URL(`${baseUrl}${pathname}`);
695
+ if (authToken && queryAuthFallbackEnabled())
696
+ requestUrl.searchParams.set("rt", authToken);
697
+ const response = await fetch(requestUrl, {
698
+ ...init,
699
+ headers: mergeHeaders(init.headers, authToken)
700
+ });
701
+ const text = await response.text();
702
+ const payload = text.trim().length > 0 ? (() => {
703
+ try {
704
+ return JSON.parse(text);
705
+ } catch {
706
+ return null;
707
+ }
708
+ })() : null;
709
+ return { ok: response.ok, status: response.status, payload, text };
710
+ }
711
+ function payloadError(payload, fallback) {
712
+ if (payload && typeof payload === "object" && !Array.isArray(payload)) {
713
+ const record = payload;
714
+ const value = record.error ?? record.message ?? record.reason;
715
+ if (typeof value === "string" && value.trim())
716
+ return value.trim();
717
+ }
718
+ return fallback;
719
+ }
720
+ function checkoutPathsFromProjectPayload(payload) {
721
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
722
+ return [];
723
+ const project = payload.project;
724
+ if (!project || typeof project !== "object" || Array.isArray(project))
725
+ return [];
726
+ const checkouts = project.checkouts;
727
+ if (!Array.isArray(checkouts))
728
+ return [];
729
+ return [...checkouts].reverse().flatMap((entry) => {
730
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
731
+ return [];
732
+ const path = entry.path;
733
+ return typeof path === "string" && path.trim() ? [path.trim()] : [];
734
+ });
735
+ }
736
+ function checkoutPathFromPreparePayload(payload) {
737
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
738
+ return null;
739
+ const checkout = payload.checkout;
740
+ if (!checkout || typeof checkout !== "object" || Array.isArray(checkout))
741
+ return null;
742
+ const path = checkout.path;
743
+ return typeof path === "string" && path.trim() ? path.trim() : null;
744
+ }
745
+ async function validateAndPersistRemoteRoot(input) {
746
+ const candidate = input.candidate.trim();
747
+ if (!candidate || !isAbsolute(candidate)) {
748
+ return remoteProjectLinkFailure({
749
+ status: "invalid_root",
750
+ alias: input.alias,
751
+ baseUrl: input.baseUrl,
752
+ repoSlug: input.repoSlug,
753
+ message: `Remote project root candidate is not an absolute server-host path: ${candidate || "(empty)"}.`
754
+ });
755
+ }
756
+ if (localCheckoutPathCandidate(input.projectRoot, candidate)) {
757
+ return remoteProjectLinkFailure({
758
+ status: "invalid_root",
759
+ alias: input.alias,
760
+ baseUrl: input.baseUrl,
761
+ repoSlug: input.repoSlug,
762
+ message: `Refusing to use local checkout path ${candidate} as a remote server project root.`
763
+ });
764
+ }
765
+ let response;
766
+ try {
767
+ response = await requestRemoteProjectLinkJson(input.baseUrl, "/api/server/project-root", input.authToken, {
768
+ method: "POST",
769
+ headers: { "content-type": "application/json" },
770
+ body: JSON.stringify({ projectRoot: candidate })
771
+ });
772
+ } catch (error) {
773
+ return remoteProjectLinkFailure({
774
+ status: "error",
775
+ alias: input.alias,
776
+ baseUrl: input.baseUrl,
777
+ repoSlug: input.repoSlug,
778
+ message: `Could not validate remote project root ${candidate}: ${error instanceof Error ? error.message : String(error)}`
779
+ });
780
+ }
781
+ if (response.status === 401 || response.status === 403) {
782
+ return remoteProjectLinkFailure({
783
+ status: "auth_required",
784
+ alias: input.alias,
785
+ baseUrl: input.baseUrl,
786
+ repoSlug: input.repoSlug,
787
+ statusCode: response.status,
788
+ message: `Remote server ${input.alias} requires authentication before validating project root ${candidate}.`
789
+ });
790
+ }
791
+ if (!response.ok) {
792
+ return remoteProjectLinkFailure({
793
+ status: "invalid_root",
794
+ alias: input.alias,
795
+ baseUrl: input.baseUrl,
796
+ repoSlug: input.repoSlug,
797
+ statusCode: response.status,
798
+ message: `Remote server rejected project root ${candidate} (${response.status}): ${payloadError(response.payload, response.text || "project-root validation failed")}`
799
+ });
800
+ }
801
+ const record = response.payload && typeof response.payload === "object" && !Array.isArray(response.payload) ? response.payload : {};
802
+ if (record.ok !== true) {
803
+ return remoteProjectLinkFailure({
804
+ status: "invalid_root",
805
+ alias: input.alias,
806
+ baseUrl: input.baseUrl,
807
+ repoSlug: input.repoSlug,
808
+ message: `Remote server did not accept project root ${candidate}: ${payloadError(response.payload, "project-root validation failed")}`
809
+ });
810
+ }
811
+ const accepted = typeof record.projectRoot === "string" && record.projectRoot.trim() ? record.projectRoot.trim() : typeof record.requestedProjectRoot === "string" && record.requestedProjectRoot.trim() ? record.requestedProjectRoot.trim() : candidate;
812
+ writeRepoServerProjectRoot(input.projectRoot, accepted, { alias: input.alias, baseUrl: input.baseUrl, project: input.repoSlug });
813
+ writeProjectLinkConnection(input.projectRoot, input.repoSlug, input.alias);
814
+ const status = input.status ?? (input.prepared ? "prepared" : "backfilled");
815
+ return remoteProjectLinkSuccess({
816
+ status,
817
+ alias: input.alias,
818
+ baseUrl: input.baseUrl,
819
+ repoSlug: input.repoSlug,
820
+ serverProjectRoot: accepted,
821
+ source: input.source,
822
+ prepared: input.prepared,
823
+ validated: true,
824
+ message: status === "ready" ? `Remote project link already points to ${accepted} and was validated on ${input.alias}.` : input.prepared ? `Prepared and linked remote checkout ${accepted} for ${input.repoSlug}.` : `Backfilled remote checkout ${accepted} for ${input.repoSlug}.`
825
+ });
826
+ }
827
+ async function ensureRemoteProjectRootLink(projectRoot, options = {}) {
828
+ let selected;
829
+ try {
830
+ selected = resolveSelectedConnection(projectRoot);
831
+ } catch (error) {
832
+ return remoteProjectLinkFailure({ status: "error", message: error instanceof Error ? error.message : String(error) });
833
+ }
834
+ if (!selected || selected.connection.kind !== "remote") {
835
+ return remoteProjectLinkFailure({ status: "not_remote", message: "No remote Rig server is selected for this repo." });
836
+ }
837
+ const repo = readRepoConnection(projectRoot);
838
+ const explicitRepoRequested = options.repoSlug !== undefined;
839
+ const explicitRepoSlug = explicitRepoRequested ? normalizeRepoSlug(options.repoSlug) : null;
840
+ const repoProjectSlug = normalizeRepoSlug(repo?.project);
841
+ const repoSlug = explicitRepoSlug ?? repoProjectSlug ?? readProjectLinkSlug(projectRoot);
842
+ const alias = selected.alias;
843
+ const baseUrl = selected.connection.baseUrl;
844
+ const authToken = options.authToken === undefined ? readGitHubBearerTokenForRemote(projectRoot) : options.authToken;
845
+ const mode = options.mode ?? "backfill-only";
846
+ if (explicitRepoRequested && !explicitRepoSlug) {
847
+ return remoteProjectLinkFailure({
848
+ status: "missing_project",
849
+ alias,
850
+ baseUrl,
851
+ message: `Invalid --repo value ${JSON.stringify(options.repoSlug)}. Expected owner/repo.`
852
+ });
853
+ }
854
+ if (!repoSlug) {
855
+ return remoteProjectLinkFailure({
856
+ status: "missing_project",
857
+ alias,
858
+ baseUrl,
859
+ message: `Remote server ${alias} is selected, but this checkout has no GitHub repo slug recorded.`
860
+ });
861
+ }
862
+ const skippedCandidates = [];
863
+ const storedRoot = selected.serverProjectRoot?.trim() || null;
864
+ const storedRootMatchesResolvedRepo = repoProjectSlug === repoSlug;
865
+ if (storedRoot && storedRootMatchesResolvedRepo) {
866
+ if (localCheckoutPathCandidate(projectRoot, storedRoot)) {
867
+ clearRepoServerProjectRoot(projectRoot);
868
+ skippedCandidates.push(storedRoot);
869
+ } else {
870
+ const storedResult = await validateAndPersistRemoteRoot({
871
+ projectRoot,
872
+ alias,
873
+ baseUrl,
874
+ authToken,
875
+ repoSlug,
876
+ candidate: storedRoot,
877
+ source: "stored",
878
+ status: "ready"
879
+ });
880
+ if (storedResult.ok || storedResult.status === "auth_required" || storedResult.status === "error")
881
+ return storedResult;
882
+ clearRepoServerProjectRoot(projectRoot);
883
+ skippedCandidates.push(storedRoot);
884
+ }
885
+ } else if (storedRoot) {
886
+ skippedCandidates.push(storedRoot);
887
+ }
888
+ const authCandidate = inferRemoteServerProjectRootCandidateFromAuthState(projectRoot, repoSlug);
889
+ if (authCandidate) {
890
+ const authResult = await validateAndPersistRemoteRoot({ projectRoot, alias, baseUrl, authToken, repoSlug, candidate: authCandidate, source: "auth-state" });
891
+ if (authResult.ok || authResult.status === "auth_required")
892
+ return authResult;
893
+ }
894
+ let registryResponse;
895
+ try {
896
+ registryResponse = await requestRemoteProjectLinkJson(baseUrl, `/api/projects/${encodeURIComponent(repoSlug)}`, authToken);
897
+ } catch (error) {
898
+ return remoteProjectLinkFailure({
899
+ status: "error",
900
+ alias,
901
+ baseUrl,
902
+ repoSlug,
903
+ message: `Could not read remote project registry for ${repoSlug} on ${alias}: ${error instanceof Error ? error.message : String(error)}`
904
+ });
905
+ }
906
+ if (registryResponse.status === 401 || registryResponse.status === 403) {
907
+ return remoteProjectLinkFailure({
908
+ status: "auth_required",
909
+ alias,
910
+ baseUrl,
911
+ repoSlug,
912
+ statusCode: registryResponse.status,
913
+ message: `Remote server ${alias} requires authentication before reading the ${repoSlug} project registry.`
914
+ });
915
+ }
916
+ if (registryResponse.ok) {
917
+ const candidates = checkoutPathsFromProjectPayload(registryResponse.payload);
918
+ for (const candidate of candidates) {
919
+ const result = await validateAndPersistRemoteRoot({ projectRoot, alias, baseUrl, authToken, repoSlug, candidate, source: "registry" });
920
+ if (result.ok || result.status === "auth_required")
921
+ return result;
922
+ skippedCandidates.push(candidate);
923
+ }
924
+ if (mode === "backfill-only") {
925
+ return remoteProjectLinkFailure({
926
+ status: candidates.length > 0 ? "invalid_root" : "no_server_checkout",
927
+ alias,
928
+ baseUrl,
929
+ repoSlug,
930
+ message: candidates.length > 0 ? `Remote registry has checkout candidates for ${repoSlug}, but none validated for ${alias}.` : `Remote registry has ${repoSlug}, but no server checkout path is linked yet.`,
931
+ skippedCandidates
932
+ });
933
+ }
934
+ } else if (registryResponse.status === 404) {
935
+ if (mode === "backfill-only") {
936
+ return remoteProjectLinkFailure({
937
+ status: "project_not_registered",
938
+ alias,
939
+ baseUrl,
940
+ repoSlug,
941
+ statusCode: registryResponse.status,
942
+ message: `Remote server ${alias} has no registered project record for ${repoSlug}.`
943
+ });
944
+ }
945
+ } else {
946
+ return remoteProjectLinkFailure({
947
+ status: "error",
948
+ alias,
949
+ baseUrl,
950
+ repoSlug,
951
+ statusCode: registryResponse.status,
952
+ message: `Could not backfill ${repoSlug} from remote registry (${registryResponse.status}): ${payloadError(registryResponse.payload, registryResponse.text || "registry lookup failed")}`,
953
+ skippedCandidates
954
+ });
955
+ }
956
+ let prepareResponse;
957
+ try {
958
+ prepareResponse = await requestRemoteProjectLinkJson(baseUrl, `/api/projects/${encodeURIComponent(repoSlug)}/prepare-checkout`, authToken, {
959
+ method: "POST",
960
+ headers: { "content-type": "application/json" },
961
+ body: JSON.stringify({ checkout: { kind: "managed-clone" }, repoUrl: `https://github.com/${repoSlug}.git` })
962
+ });
963
+ } catch (error) {
964
+ return remoteProjectLinkFailure({
965
+ status: "error",
966
+ alias,
967
+ baseUrl,
968
+ repoSlug,
969
+ message: `Could not prepare remote checkout for ${repoSlug} on ${alias}: ${error instanceof Error ? error.message : String(error)}`
970
+ });
971
+ }
972
+ if (prepareResponse.status === 401 || prepareResponse.status === 403) {
973
+ return remoteProjectLinkFailure({
974
+ status: "auth_required",
975
+ alias,
976
+ baseUrl,
977
+ repoSlug,
978
+ statusCode: prepareResponse.status,
979
+ message: `Remote server ${alias} requires authentication before preparing ${repoSlug}.`
980
+ });
981
+ }
982
+ if (!prepareResponse.ok) {
983
+ return remoteProjectLinkFailure({
984
+ status: "error",
985
+ alias,
986
+ baseUrl,
987
+ repoSlug,
988
+ statusCode: prepareResponse.status,
989
+ message: `Remote checkout prepare failed for ${repoSlug} (${prepareResponse.status}): ${payloadError(prepareResponse.payload, prepareResponse.text || "prepare-checkout failed")}`,
990
+ skippedCandidates
991
+ });
992
+ }
993
+ const preparedPath = checkoutPathFromPreparePayload(prepareResponse.payload);
994
+ if (!preparedPath) {
995
+ return remoteProjectLinkFailure({
996
+ status: "invalid_root",
997
+ alias,
998
+ baseUrl,
999
+ repoSlug,
1000
+ message: `Remote checkout prepare for ${repoSlug} did not return a checkout.path.`,
1001
+ skippedCandidates
1002
+ });
1003
+ }
1004
+ return validateAndPersistRemoteRoot({ projectRoot, alias, baseUrl, authToken, repoSlug, candidate: preparedPath, source: "prepare", prepared: true });
1005
+ }
1006
+ async function inspectRemoteProjectLink(projectRoot) {
1007
+ return ensureRemoteProjectRootLink(projectRoot, { mode: "backfill-only" });
1008
+ }
1009
+ async function repairRemoteProjectRootLink(context, options = {}) {
1010
+ const resolution = await ensureRemoteProjectRootLink(context.projectRoot, { mode: options.mode ?? "prepare-if-missing", repoSlug: options.repoSlug });
1011
+ if (!resolution.ok)
1012
+ throw new CliError(resolution.message, 1, { hint: resolution.hint });
1013
+ return resolution;
1014
+ }
564
1015
  async function ensureServerForCli(projectRoot) {
565
1016
  try {
566
1017
  const selected = resolveSelectedConnection(projectRoot);
567
1018
  if (selected?.connection.kind === "remote") {
568
1019
  reportServerPhase(`Connecting to ${selected.alias}\u2026`);
569
1020
  const authToken = readGitHubBearerTokenForRemote(projectRoot);
570
- const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
1021
+ const link = await ensureRemoteProjectRootLink(projectRoot, { mode: "backfill-only", authToken });
571
1022
  return {
572
1023
  baseUrl: selected.connection.baseUrl,
573
1024
  authToken,
574
1025
  connectionKind: "remote",
575
- serverProjectRoot
1026
+ serverProjectRoot: link.ok ? link.serverProjectRoot ?? null : null
576
1027
  };
577
1028
  }
578
1029
  reportServerPhase("Starting local Rig server\u2026");
@@ -590,32 +1041,6 @@ async function ensureServerForCli(projectRoot) {
590
1041
  throw error;
591
1042
  }
592
1043
  }
593
- async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
594
- const repo = readRepoConnection(projectRoot);
595
- const slug = repo?.project?.trim();
596
- if (!slug)
597
- return null;
598
- try {
599
- const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
600
- if (authToken && queryAuthFallbackEnabled())
601
- url.searchParams.set("rt", authToken);
602
- const response = await fetch(url, {
603
- headers: mergeHeaders(undefined, authToken)
604
- });
605
- if (!response.ok)
606
- return null;
607
- const payload = await response.json();
608
- const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
609
- const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
610
- const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
611
- const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
612
- if (path)
613
- writeRepoServerProjectRoot(projectRoot, path);
614
- return path;
615
- } catch {
616
- return null;
617
- }
618
- }
619
1044
  function appendTaskFilterParams(url, filters) {
620
1045
  if (filters.assignee)
621
1046
  url.searchParams.set("assignee", filters.assignee);
@@ -716,13 +1141,20 @@ function canUseRemoteWithoutProjectRoot(pathname) {
716
1141
  async function requestServerJson(context, pathname, init = {}) {
717
1142
  const server = await ensureServerForCli(context.projectRoot);
718
1143
  const requestUrl = new URL(`${server.baseUrl}${pathname}`);
719
- if (server.connectionKind === "remote" && !server.serverProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
720
- const repo = readRepoConnection(context.projectRoot);
721
- throw new CliError(`Remote server is selected for ${repo?.project ?? "this repo"}, but this checkout has no server-host project root link.`, 1, { hint: "Run `rig init --repair` or `rig init --yes --repo owner/repo --server remote` to repair the remote project link before task/run commands." });
1144
+ let scopedServerProjectRoot = server.serverProjectRoot;
1145
+ if (server.connectionKind === "remote" && !scopedServerProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
1146
+ const link = await ensureRemoteProjectRootLink(context.projectRoot, { mode: "backfill-only", authToken: server.authToken });
1147
+ if (link.ok && link.serverProjectRoot) {
1148
+ scopedServerProjectRoot = link.serverProjectRoot;
1149
+ } else {
1150
+ const repo = readRepoConnection(context.projectRoot);
1151
+ const target = link.alias && link.baseUrl ? `${link.alias} (${link.baseUrl})` : server.baseUrl;
1152
+ throw new CliError(`Remote server ${target} is selected for ${link.repoSlug ?? repo?.project ?? "this repo"}, but this checkout has no server-host project root link. ${link.message}`, 1, { hint: link.hint });
1153
+ }
722
1154
  }
723
1155
  const headers = mergeHeaders(init.headers, server.authToken);
724
- if (server.serverProjectRoot)
725
- headers.set("x-rig-project-root", server.serverProjectRoot);
1156
+ if (scopedServerProjectRoot)
1157
+ headers.set("x-rig-project-root", scopedServerProjectRoot);
726
1158
  if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
727
1159
  requestUrl.searchParams.set("rt", server.authToken);
728
1160
  }
@@ -1354,7 +1786,7 @@ __export(exports__pi_frontend, {
1354
1786
  buildOperatorPiEnv: () => buildOperatorPiEnv,
1355
1787
  attachRunBundledPiFrontend: () => attachRunBundledPiFrontend
1356
1788
  });
1357
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, mkdtempSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
1789
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, mkdtempSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync3 } from "fs";
1358
1790
  import { homedir as homedir2, tmpdir } from "os";
1359
1791
  import { join } from "path";
1360
1792
  import { main as runPiMain } from "@earendil-works/pi-coding-agent";
@@ -1406,7 +1838,7 @@ function statusFromRunDetails(run) {
1406
1838
  async function prepareOperatorConsole(context, runId, tempSessionDir) {
1407
1839
  const server = await ensureServerForCli(context.projectRoot);
1408
1840
  const localCwd = join(homedir2(), ".rig", "drones", runId.slice(0, 8));
1409
- mkdirSync3(localCwd, { recursive: true });
1841
+ mkdirSync4(localCwd, { recursive: true });
1410
1842
  trustDroneCwd(localCwd);
1411
1843
  installRigPiTheme();
1412
1844
  let sessionFileArg = [];
@@ -1428,7 +1860,7 @@ async function prepareOperatorConsole(context, runId, tempSessionDir) {
1428
1860
  return line;
1429
1861
  }).join(`
1430
1862
  `);
1431
- writeFileSync2(localSessionPath, content);
1863
+ writeFileSync3(localSessionPath, content);
1432
1864
  sessionFileArg = ["--session", localSessionPath];
1433
1865
  }
1434
1866
  } catch {}
@@ -1444,12 +1876,12 @@ async function prepareOperatorConsole(context, runId, tempSessionDir) {
1444
1876
  function trustDroneCwd(localCwd) {
1445
1877
  try {
1446
1878
  const agentDir = join(homedir2(), ".pi", "agent");
1447
- mkdirSync3(agentDir, { recursive: true });
1879
+ mkdirSync4(agentDir, { recursive: true });
1448
1880
  const trustPath = join(agentDir, "trust.json");
1449
1881
  const store = existsSync4(trustPath) ? JSON.parse(readFileSync3(trustPath, "utf8")) : {};
1450
1882
  if (store[localCwd] !== true) {
1451
1883
  store[localCwd] = true;
1452
- writeFileSync2(trustPath, `${JSON.stringify(store, null, "\t")}
1884
+ writeFileSync3(trustPath, `${JSON.stringify(store, null, "\t")}
1453
1885
  `);
1454
1886
  }
1455
1887
  } catch {}
@@ -1457,12 +1889,12 @@ function trustDroneCwd(localCwd) {
1457
1889
  function installRigPiTheme() {
1458
1890
  try {
1459
1891
  const themesDir = join(homedir2(), ".pi", "agent", "themes");
1460
- mkdirSync3(themesDir, { recursive: true });
1892
+ mkdirSync4(themesDir, { recursive: true });
1461
1893
  const themePath = join(themesDir, "rig.json");
1462
1894
  const next = `${JSON.stringify(RIG_PI_THEME, null, "\t")}
1463
1895
  `;
1464
1896
  if (!existsSync4(themePath) || readFileSync3(themePath, "utf8") !== next) {
1465
- writeFileSync2(themePath, next);
1897
+ writeFileSync3(themePath, next);
1466
1898
  }
1467
1899
  } catch {}
1468
1900
  }
@@ -2777,12 +3209,13 @@ var init__help_catalog = __esm(() => {
2777
3209
  {
2778
3210
  name: "server",
2779
3211
  summary: "Choose, inspect, and start the Rig server that owns tasks and runs.",
2780
- usage: ["rig server <status|list|add|use|start> [options]"],
3212
+ usage: ["rig server <status|list|add|use|repair-link|start> [options]"],
2781
3213
  commands: [
2782
- { command: "status", description: "Show the selected server for this repo.", primary: true },
3214
+ { command: "status", description: "Show the selected server and remote project-root link for this repo.", primary: true },
2783
3215
  { command: "use local", description: "Switch this repo to the local Rig server.", primary: true },
2784
3216
  { command: "add <alias> <url>", description: "Save a remote Rig server URL.", primary: true },
2785
3217
  { command: "use <alias>", description: "Select a saved remote server alias.", primary: true },
3218
+ { command: "repair-link [--prepare|--backfill-only] [--repo owner/repo]", description: "Backfill or prepare the selected remote's server-host checkout link.", primary: true },
2786
3219
  { command: "list", description: "List saved local/remote server aliases.", primary: true },
2787
3220
  { command: "start [--host <host>] [--port <n>]", description: "Start a local rig-server process." }
2788
3221
  ],
@@ -2790,6 +3223,7 @@ var init__help_catalog = __esm(() => {
2790
3223
  "rig server status",
2791
3224
  "rig server add prod https://where.rig-does.work",
2792
3225
  "rig server use prod",
3226
+ "rig server repair-link --repo owner/repo",
2793
3227
  "rig server use local",
2794
3228
  "rig server start --port 3773"
2795
3229
  ],
@@ -3304,7 +3738,7 @@ function resolveControlPlaneDefinitionRoot(projectRoot) {
3304
3738
  var init__paths = () => {};
3305
3739
 
3306
3740
  // packages/cli/src/report-bug.ts
3307
- import { copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync4, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
3741
+ import { copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync5, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
3308
3742
  import { basename as basename2, extname, join as join2, resolve as resolve8 } from "path";
3309
3743
  function slugifyBugTitle(value) {
3310
3744
  const slug = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80).replace(/-+$/g, "");
@@ -3343,8 +3777,8 @@ function createBugReportFiles(input) {
3343
3777
  }
3344
3778
  rmSync3(reportDir, { recursive: true, force: true });
3345
3779
  }
3346
- mkdirSync4(assetDir, { recursive: true });
3347
- mkdirSync4(screenshotDir, { recursive: true });
3780
+ mkdirSync5(assetDir, { recursive: true });
3781
+ mkdirSync5(screenshotDir, { recursive: true });
3348
3782
  const copiedScreenshots = copyEvidenceFiles(input.projectRoot, screenshotDir, input.screenshots ?? [], "screenshot");
3349
3783
  const copiedAssets = copyEvidenceFiles(input.projectRoot, assetDir, input.assets ?? [], "asset");
3350
3784
  const manifestAssets = [
@@ -3374,15 +3808,15 @@ function createBugReportFiles(input) {
3374
3808
  platform: process.platform,
3375
3809
  arch: process.arch
3376
3810
  };
3377
- writeFileSync3(join2(assetDir, "README.md"), buildAssetReadme(input.title), "utf8");
3378
- writeFileSync3(join2(screenshotDir, "README.md"), buildScreenshotReadme(input.title), "utf8");
3811
+ writeFileSync4(join2(assetDir, "README.md"), buildAssetReadme(input.title), "utf8");
3812
+ writeFileSync4(join2(screenshotDir, "README.md"), buildScreenshotReadme(input.title), "utf8");
3379
3813
  if (browserPath && browser) {
3380
- writeFileSync3(browserPath, `${JSON.stringify(browser, null, 2)}
3814
+ writeFileSync4(browserPath, `${JSON.stringify(browser, null, 2)}
3381
3815
  `, "utf8");
3382
3816
  }
3383
- writeFileSync3(manifestPath, `${JSON.stringify(manifest, null, 2)}
3817
+ writeFileSync4(manifestPath, `${JSON.stringify(manifest, null, 2)}
3384
3818
  `, "utf8");
3385
- writeFileSync3(taskPath, buildBugReportMarkdown(input, browser, copiedScreenshots, copiedAssets), "utf8");
3819
+ writeFileSync4(taskPath, buildBugReportMarkdown(input, browser, copiedScreenshots, copiedAssets), "utf8");
3386
3820
  return {
3387
3821
  slug,
3388
3822
  reportDir,
@@ -3559,7 +3993,7 @@ function formatAssetMarkdown(name, path) {
3559
3993
  var init_report_bug = () => {};
3560
3994
 
3561
3995
  // packages/cli/src/commands/task-report-bug.ts
3562
- import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
3996
+ import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
3563
3997
  import { resolve as resolve9 } from "path";
3564
3998
  import pc3 from "picocolors";
3565
3999
  import {
@@ -3921,7 +4355,7 @@ function appendTaskConfigEntryPreservingText(taskConfigPath, issueId, entry) {
3921
4355
  `).map((line) => ` ${line}`).join(`
3922
4356
  `);
3923
4357
  if (!trimmed || trimmed === "{}") {
3924
- writeFileSync4(taskConfigPath, `{
4358
+ writeFileSync5(taskConfigPath, `{
3925
4359
  ${serializedEntry}
3926
4360
  }
3927
4361
  `, "utf8");
@@ -3937,7 +4371,7 @@ ${serializedEntry}
3937
4371
  throw new CliError(`Invalid trailing content in task config: ${taskConfigPath}`, 1, { hint: "Remove the trailing content so the file is a single JSON object, then retry." });
3938
4372
  }
3939
4373
  const comma = before.trim() === "{" ? "" : ",";
3940
- writeFileSync4(taskConfigPath, `${before}${comma}
4374
+ writeFileSync5(taskConfigPath, `${before}${comma}
3941
4375
  ${serializedEntry}
3942
4376
  }
3943
4377
  `, "utf8");
@@ -4322,7 +4756,7 @@ var init_task_report_bug = __esm(() => {
4322
4756
  });
4323
4757
 
4324
4758
  // packages/cli/src/commands/browser.ts
4325
- import { mkdirSync as mkdirSync5, rmSync as rmSync4 } from "fs";
4759
+ import { mkdirSync as mkdirSync6, rmSync as rmSync4 } from "fs";
4326
4760
  import { resolve as resolve10 } from "path";
4327
4761
  import { spawn } from "child_process";
4328
4762
  import { emitKeypressEvents } from "readline";
@@ -4900,7 +5334,7 @@ function readBrowserDemoCommandLine(command) {
4900
5334
  }
4901
5335
  async function launchBrowserDemo(runtime) {
4902
5336
  rmSync4(resolve10(runtime.browserDir, runtime.stateDir), { recursive: true, force: true });
4903
- mkdirSync5(resolve10(runtime.browserDir, runtime.stateDir), { recursive: true });
5337
+ mkdirSync6(resolve10(runtime.browserDir, runtime.stateDir), { recursive: true });
4904
5338
  const launcherPath = resolve10(runtime.browserDir, "scripts/electron-launcher.mjs");
4905
5339
  const launcher = await import(pathToFileURL(launcherPath).href);
4906
5340
  const electronPath = await launcher.resolveElectronPath();
@@ -5249,7 +5683,7 @@ var init_profile_and_review = __esm(() => {
5249
5683
  });
5250
5684
 
5251
5685
  // packages/cli/src/commands/_policy.ts
5252
- import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
5686
+ import { appendFileSync, mkdirSync as mkdirSync7 } from "fs";
5253
5687
  import { resolve as resolve11 } from "path";
5254
5688
  import { evaluate as evaluate2, loadPolicy as loadPolicy2, resolveAction as resolveAction2 } from "@rig/runtime/control-plane/runtime/guard";
5255
5689
  import { resolveHarnessPaths } from "@rig/runtime/control-plane/native/utils";
@@ -5266,7 +5700,7 @@ function appendControlledBashAudit(context, mode, command, args, matchedRuleIds,
5266
5700
  }
5267
5701
  const logsDir = resolveHarnessPaths(context.projectRoot).logsDir;
5268
5702
  const logFile = resolve11(logsDir, "controlled-bash.jsonl");
5269
- mkdirSync6(logsDir, { recursive: true });
5703
+ mkdirSync7(logsDir, { recursive: true });
5270
5704
  const payload = {
5271
5705
  timestamp: new Date().toISOString(),
5272
5706
  mode,
@@ -5560,9 +5994,9 @@ var exports_pi = {};
5560
5994
  __export(exports_pi, {
5561
5995
  executePi: () => executePi
5562
5996
  });
5563
- import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
5997
+ import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
5564
5998
  import { homedir as homedir5 } from "os";
5565
- import { dirname as dirname2, resolve as resolve13 } from "path";
5999
+ import { dirname as dirname3, resolve as resolve13 } from "path";
5566
6000
  function settingsPath(root) {
5567
6001
  return resolve13(root, ".pi", "settings.json");
5568
6002
  }
@@ -5587,8 +6021,8 @@ function packageKey(entry) {
5587
6021
  return JSON.stringify(entry);
5588
6022
  }
5589
6023
  function writeSettings(path, settings) {
5590
- mkdirSync7(dirname2(path), { recursive: true });
5591
- writeFileSync5(path, `${JSON.stringify(settings, null, 2)}
6024
+ mkdirSync8(dirname3(path), { recursive: true });
6025
+ writeFileSync6(path, `${JSON.stringify(settings, null, 2)}
5592
6026
  `, "utf-8");
5593
6027
  }
5594
6028
  async function searchNpmForPiExtensions(term) {
@@ -5771,6 +6205,14 @@ function permissionAllowsPr2(payload) {
5771
6205
  function isNotFoundError(error) {
5772
6206
  return /\b(404|not found)\b/i.test(message(error));
5773
6207
  }
6208
+ function isLoopbackBaseUrl(baseUrl) {
6209
+ try {
6210
+ const host = new URL(baseUrl).hostname.toLowerCase();
6211
+ return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
6212
+ } catch {
6213
+ return false;
6214
+ }
6215
+ }
5774
6216
  function projectCheckoutReady(payload) {
5775
6217
  if (!payload || typeof payload !== "object" || Array.isArray(payload))
5776
6218
  return null;
@@ -5825,25 +6267,37 @@ async function runFastTaskRunPreflight(context, options = {}) {
5825
6267
  }
5826
6268
  const repo = readRepoConnection(context.projectRoot);
5827
6269
  const remoteWithoutProjectLink = selectedServer?.connectionKind === "remote" && !repo?.project;
5828
- checks.push(repo ? preflightCheck("project-link", "project linked to selected server", repo.project ? "pass" : remoteWithoutProjectLink ? "fail" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : " (missing project link)"}`, remoteWithoutProjectLink ? "Run `rig init --repair` or `rig init --yes --repo owner/repo --server remote` to restore the remote project link." : "Run `rig init --yes --repo owner/repo` to record the GitHub repo slug.") : preflightCheck("project-link", "project linked to selected server", legacyServerCompatibility ? "warn" : "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig server use <alias|local>`."));
6270
+ let remoteRootBlocked = false;
6271
+ checks.push(repo ? preflightCheck("project-link", "project linked to selected server", repo.project ? "pass" : remoteWithoutProjectLink ? "fail" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : " (missing project link)"}`, remoteWithoutProjectLink ? "Run `rig server repair-link --repo owner/repo` or `rig init --repo owner/repo --server remote` to restore the remote project link." : "Run `rig init --yes --repo owner/repo` to record the GitHub repo slug.") : preflightCheck("project-link", "project linked to selected server", legacyServerCompatibility ? "warn" : "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig server use <alias|local>`."));
6272
+ if (selectedServer?.connectionKind === "remote" && repo?.project && !selectedServer.serverProjectRoot) {
6273
+ const link = await ensureRemoteProjectRootLink(context.projectRoot, { mode: "backfill-only", authToken: selectedServer.authToken });
6274
+ const loopbackDevRemote = !selectedServer.authToken && isLoopbackBaseUrl(selectedServer.baseUrl);
6275
+ checks.push(preflightCheck("remote-project-root-link", "remote server checkout/root linked", link.ok ? "pass" : loopbackDevRemote ? "warn" : "fail", link.ok ? `${link.repoSlug} -> ${link.serverProjectRoot}` : link.message, link.ok ? undefined : loopbackDevRemote ? "Loopback development remotes may continue without x-rig-project-root; production remotes must run `rig server repair-link`." : link.hint));
6276
+ remoteRootBlocked = !link.ok && !loopbackDevRemote;
6277
+ }
5829
6278
  try {
5830
6279
  const auth = await request("/api/github/auth/status");
5831
6280
  checks.push(isAuthenticated2(auth) ? preflightCheck("github-auth", "GitHub auth valid", "pass") : preflightCheck("github-auth", "GitHub auth valid", legacyServerCompatibility ? "warn" : "fail", "not authenticated", "Run `rig github auth import-gh` or `rig github auth token --token <token>`."));
5832
6281
  } catch (error) {
5833
6282
  checks.push(preflightCheck("github-auth", "GitHub auth valid", legacyServerCompatibility ? "warn" : "fail", message(error), "Fix GitHub auth on the selected Rig server."));
5834
6283
  }
5835
- try {
5836
- const projection = await request("/api/workspace/task-projection");
5837
- checks.push(preflightCheck("task-projection", "task projection ready", "pass", JSON.stringify(projection).slice(0, 120)));
5838
- } catch (error) {
5839
- checks.push(preflightCheck("task-projection", "task projection ready", "warn", message(error), "Refresh task projection with `rig task list` before launching."));
5840
- }
5841
- try {
5842
- const permissions = await request("/api/github/repo/permissions");
5843
- const allowed = permissionAllowsPr2(permissions);
5844
- checks.push(allowed === false ? preflightCheck("github-repo-permissions", "GitHub PR permissions", "fail", JSON.stringify(permissions).slice(0, 120), "Grant the selected GitHub token permission to push branches and open PRs.") : preflightCheck("github-repo-permissions", "GitHub PR permissions", allowed === true ? "pass" : "warn", JSON.stringify(permissions).slice(0, 120), "Confirm the selected token can push branches and open PRs."));
5845
- } catch (error) {
5846
- checks.push(preflightCheck("github-repo-permissions", "GitHub PR permissions", "warn", message(error), "Ensure the selected token can push branches and open PRs."));
6284
+ if (!remoteRootBlocked) {
6285
+ try {
6286
+ const projection = await request("/api/workspace/task-projection");
6287
+ checks.push(preflightCheck("task-projection", "task projection ready", "pass", JSON.stringify(projection).slice(0, 120)));
6288
+ } catch (error) {
6289
+ checks.push(preflightCheck("task-projection", "task projection ready", "warn", message(error), "Refresh task projection with `rig task list` before launching."));
6290
+ }
6291
+ try {
6292
+ const permissions = await request("/api/github/repo/permissions");
6293
+ const allowed = permissionAllowsPr2(permissions);
6294
+ checks.push(allowed === false ? preflightCheck("github-repo-permissions", "GitHub PR permissions", "fail", JSON.stringify(permissions).slice(0, 120), "Grant the selected GitHub token permission to push branches and open PRs.") : preflightCheck("github-repo-permissions", "GitHub PR permissions", allowed === true ? "pass" : "warn", JSON.stringify(permissions).slice(0, 120), "Confirm the selected token can push branches and open PRs."));
6295
+ } catch (error) {
6296
+ checks.push(preflightCheck("github-repo-permissions", "GitHub PR permissions", "warn", message(error), "Ensure the selected token can push branches and open PRs."));
6297
+ }
6298
+ } else {
6299
+ checks.push(preflightCheck("task-projection", "task projection ready", "warn", "skipped until remote project-root link is repaired", "Run `rig server repair-link`, then retry."));
6300
+ checks.push(preflightCheck("github-repo-permissions", "GitHub PR permissions", "warn", "skipped until remote project-root link is repaired", "Run `rig server repair-link`, then retry."));
5847
6301
  }
5848
6302
  if (repo?.project) {
5849
6303
  try {
@@ -5855,19 +6309,24 @@ async function runFastTaskRunPreflight(context, options = {}) {
5855
6309
  }
5856
6310
  }
5857
6311
  if (taskId) {
5858
- try {
5859
- const tasks = await request(`/api/workspace/tasks?limit=200&refresh=1`);
5860
- const found = Array.isArray(tasks) && tasks.some((task) => taskMatchesId(task, taskId));
5861
- checks.push(found ? preflightCheck("issue", "task/issue accessible", "pass", taskId) : preflightCheck("issue", "task/issue accessible", legacyServerCompatibility ? "warn" : "fail", taskId, "Confirm the issue exists and matches the configured task filters."));
5862
- } catch (error) {
5863
- checks.push(preflightCheck("issue", "task/issue accessible", legacyServerCompatibility ? "warn" : "fail", message(error), "Fix the task source before launching a run."));
5864
- }
5865
- try {
5866
- const runs = await request("/api/runs?limit=200");
5867
- const duplicate = activeDuplicateRun(runs, taskId);
5868
- checks.push(duplicate ? preflightCheck("duplicate-active-run", "one active run per task", "fail", String(duplicate.runId ?? taskId), "Attach to or stop the existing run before starting another one for this task.") : preflightCheck("duplicate-active-run", "one active run per task", "pass", taskId));
5869
- } catch (error) {
5870
- checks.push(preflightCheck("duplicate-active-run", "one active run per task", "warn", message(error), "Could not list active runs; the server will still reject conflicting runs if configured."));
6312
+ if (!remoteRootBlocked) {
6313
+ try {
6314
+ const tasks = await request(`/api/workspace/tasks?limit=200&refresh=1`);
6315
+ const found = Array.isArray(tasks) && tasks.some((task) => taskMatchesId(task, taskId));
6316
+ checks.push(found ? preflightCheck("issue", "task/issue accessible", "pass", taskId) : preflightCheck("issue", "task/issue accessible", legacyServerCompatibility ? "warn" : "fail", taskId, "Confirm the issue exists and matches the configured task filters."));
6317
+ } catch (error) {
6318
+ checks.push(preflightCheck("issue", "task/issue accessible", legacyServerCompatibility ? "warn" : "fail", message(error), "Fix the task source before launching a run."));
6319
+ }
6320
+ try {
6321
+ const runs = await request("/api/runs?limit=200");
6322
+ const duplicate = activeDuplicateRun(runs, taskId);
6323
+ checks.push(duplicate ? preflightCheck("duplicate-active-run", "one active run per task", "fail", String(duplicate.runId ?? taskId), "Attach to or stop the existing run before starting another one for this task.") : preflightCheck("duplicate-active-run", "one active run per task", "pass", taskId));
6324
+ } catch (error) {
6325
+ checks.push(preflightCheck("duplicate-active-run", "one active run per task", "warn", message(error), "Could not list active runs; the server will still reject conflicting runs if configured."));
6326
+ }
6327
+ } else {
6328
+ checks.push(preflightCheck("issue", "task/issue accessible", "fail", "skipped until remote project-root link is repaired", "Run `rig server repair-link`, then retry."));
6329
+ checks.push(preflightCheck("duplicate-active-run", "one active run per task", "warn", "skipped until remote project-root link is repaired", "Run `rig server repair-link`, then retry."));
5871
6330
  }
5872
6331
  }
5873
6332
  if ((options.runtimeAdapter ?? "pi") === "pi") {
@@ -6348,7 +6807,7 @@ import {
6348
6807
  chmodSync,
6349
6808
  copyFileSync as copyFileSync2,
6350
6809
  existsSync as existsSync12,
6351
- mkdirSync as mkdirSync8,
6810
+ mkdirSync as mkdirSync9,
6352
6811
  readdirSync,
6353
6812
  readlinkSync,
6354
6813
  rmSync as rmSync5,
@@ -6453,7 +6912,7 @@ async function executeDist(context, args) {
6453
6912
  requireNoExtraArgs(pending, "rig dist install [--scope user|system] [--path <dir>]");
6454
6913
  const scope = parseInstallScope(scopeResult.value);
6455
6914
  const installDir = resolveInstallDir(scope, pathResult.value);
6456
- mkdirSync8(installDir, { recursive: true });
6915
+ mkdirSync9(installDir, { recursive: true });
6457
6916
  let source = await findLatestDistBinary(context.projectRoot);
6458
6917
  let buildDir = null;
6459
6918
  if (!source) {
@@ -6508,7 +6967,7 @@ async function executeDist(context, args) {
6508
6967
  const fp = await computeRuntimeImageFingerprint(context.projectRoot);
6509
6968
  const currentId = computeRuntimeImageId(fp);
6510
6969
  const imagesDir = resolve16(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
6511
- mkdirSync8(imagesDir, { recursive: true });
6970
+ mkdirSync9(imagesDir, { recursive: true });
6512
6971
  let pruned = 0;
6513
6972
  for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
6514
6973
  if (entry.isDirectory() && entry.name !== currentId) {
@@ -6520,9 +6979,9 @@ async function executeDist(context, args) {
6520
6979
  console.log(`Pruned ${pruned} stale image(s).`);
6521
6980
  }
6522
6981
  const imageDir = resolve16(imagesDir, currentId);
6523
- mkdirSync8(resolve16(imageDir, "bin/hooks"), { recursive: true });
6524
- mkdirSync8(resolve16(imageDir, "bin/plugins"), { recursive: true });
6525
- mkdirSync8(resolve16(imageDir, "bin/validators"), { recursive: true });
6982
+ mkdirSync9(resolve16(imageDir, "bin/hooks"), { recursive: true });
6983
+ mkdirSync9(resolve16(imageDir, "bin/plugins"), { recursive: true });
6984
+ mkdirSync9(resolve16(imageDir, "bin/validators"), { recursive: true });
6526
6985
  const hookNames = [
6527
6986
  "scope-guard",
6528
6987
  "import-guard",
@@ -6547,8 +7006,8 @@ async function executeDist(context, args) {
6547
7006
  const binPluginsDir = resolve16(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
6548
7007
  const validatorsRoot = resolve16(hostProjectRoot, "packages/runtime/src/control-plane/validators");
6549
7008
  const binValidatorsDir = resolve16(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
6550
- mkdirSync8(binPluginsDir, { recursive: true });
6551
- mkdirSync8(binValidatorsDir, { recursive: true });
7009
+ mkdirSync9(binPluginsDir, { recursive: true });
7010
+ mkdirSync9(binValidatorsDir, { recursive: true });
6552
7011
  if (existsSync12(pluginsDir)) {
6553
7012
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
6554
7013
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
@@ -6987,16 +7446,19 @@ function formatConnectionList(connections) {
6987
7446
  return [formatSection("Rig servers", `${rows.length} available`), `${pc5.bold(pad("ALIAS", aliasWidth))} ${pc5.bold("KIND")} ${pc5.bold("TARGET")}`, ...lines, "", ...formatNextSteps(["Select one: `rig server use <alias|local>`"])].join(`
6988
7447
  `);
6989
7448
  }
6990
- function formatConnectionStatus(selected, connections) {
7449
+ function formatConnectionStatus(selected, connections, repo, remoteProjectLink) {
6991
7450
  const connection = selected === "local" ? { kind: "local", mode: "auto" } : connections[selected];
6992
7451
  const target = !connection ? "not configured" : connection.kind === "remote" ? connection.baseUrl : "local";
7452
+ const rootLabel = remoteProjectLink ? remoteProjectLink.ok ? `ready \xB7 ${remoteProjectLink.serverProjectRoot ?? repo?.serverProjectRoot ?? "linked"}` : `${remoteProjectLink.status ?? "needs repair"} \xB7 ${remoteProjectLink.hint ?? remoteProjectLink.message ?? "run rig server repair-link"}` : repo?.serverProjectRoot ? repo.serverProjectRoot : "not required";
6993
7453
  return [
6994
7454
  formatSection("Rig server", "selected for this repo"),
6995
7455
  `${themeFaint("\u2502")} ${themeDim("selected ")} ${pc5.bold(selected)}`,
6996
7456
  `${themeFaint("\u2502")} ${themeDim("kind ")} ${formatStatusPill(connection?.kind ?? "unknown")}`,
6997
7457
  `${themeFaint("\u2502")} ${themeDim("target ")} ${target ?? "not configured"}`,
7458
+ ...repo?.project ? [`${themeFaint("\u2502")} ${themeDim("project ")} ${repo.project}`] : [],
7459
+ ...connection?.kind === "remote" ? [`${themeFaint("\u2502")} ${themeDim("root ")} ${rootLabel}`] : [],
6998
7460
  "",
6999
- ...formatNextSteps(["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
7461
+ ...formatNextSteps(remoteProjectLink && !remoteProjectLink.ok ? ["Repair remote link: `rig server repair-link`", "Authenticate first if needed: `rig github auth import-gh`"] : ["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
7000
7462
  ].join(`
7001
7463
  `);
7002
7464
  }
@@ -7225,7 +7687,7 @@ var init_inbox = __esm(() => {
7225
7687
 
7226
7688
  // packages/cli/src/commands/_snapshot-upload.ts
7227
7689
  import { mkdir, readdir, readFile, writeFile } from "fs/promises";
7228
- import { dirname as dirname3, resolve as resolve17, relative, sep } from "path";
7690
+ import { dirname as dirname4, resolve as resolve17, relative, sep } from "path";
7229
7691
  function toPosixPath(path) {
7230
7692
  return path.split(sep).join("/");
7231
7693
  }
@@ -7326,7 +7788,7 @@ __export(exports_init, {
7326
7788
  DEMO_TASKS_RELATIVE_DIR: () => DEMO_TASKS_RELATIVE_DIR,
7327
7789
  DEMO_TASKS: () => DEMO_TASKS
7328
7790
  });
7329
- import { appendFileSync as appendFileSync2, existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
7791
+ import { appendFileSync as appendFileSync2, existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
7330
7792
  import { spawnSync } from "child_process";
7331
7793
  import { basename as basename3, resolve as resolve18 } from "path";
7332
7794
  import { buildRigInitConfigSource } from "@rig/core";
@@ -7350,14 +7812,14 @@ function parseRepoSlug(value) {
7350
7812
  }
7351
7813
  function ensureRigPrivateDirs(projectRoot) {
7352
7814
  const rigDir = resolve18(projectRoot, ".rig");
7353
- mkdirSync9(resolve18(rigDir, "state"), { recursive: true });
7354
- mkdirSync9(resolve18(rigDir, "logs"), { recursive: true });
7355
- mkdirSync9(resolve18(rigDir, "runs"), { recursive: true });
7356
- mkdirSync9(resolve18(rigDir, "tmp"), { recursive: true });
7357
- mkdirSync9(resolve18(projectRoot, "artifacts"), { recursive: true });
7815
+ mkdirSync10(resolve18(rigDir, "state"), { recursive: true });
7816
+ mkdirSync10(resolve18(rigDir, "logs"), { recursive: true });
7817
+ mkdirSync10(resolve18(rigDir, "runs"), { recursive: true });
7818
+ mkdirSync10(resolve18(rigDir, "tmp"), { recursive: true });
7819
+ mkdirSync10(resolve18(projectRoot, "artifacts"), { recursive: true });
7358
7820
  const taskConfigPath = resolve18(rigDir, "task-config.json");
7359
7821
  if (!existsSync13(taskConfigPath))
7360
- writeFileSync6(taskConfigPath, `{}
7822
+ writeFileSync7(taskConfigPath, `{}
7361
7823
  `, "utf-8");
7362
7824
  }
7363
7825
  function ensureGitignoreEntries(projectRoot) {
@@ -7385,7 +7847,7 @@ function ensureRigConfigPackageDependencies(projectRoot) {
7385
7847
  ...existsSync13(path) ? existing : { name: "rig-project", private: true },
7386
7848
  devDependencies
7387
7849
  };
7388
- writeFileSync6(path, `${JSON.stringify(next, null, 2)}
7850
+ writeFileSync7(path, `${JSON.stringify(next, null, 2)}
7389
7851
  `, "utf8");
7390
7852
  }
7391
7853
  function applyGitHubProjectConfig(source, options) {
@@ -7411,9 +7873,9 @@ function checkoutForInit(projectRoot, serverKind, strategy) {
7411
7873
  const selected = strategy ?? { kind: "managed-clone" };
7412
7874
  switch (selected.kind) {
7413
7875
  case "managed-clone":
7414
- return { kind: "managed-clone", path: projectRoot };
7876
+ return { kind: "managed-clone" };
7415
7877
  case "current-ref":
7416
- return { kind: "current-ref", path: projectRoot, ...selected.ref ? { ref: selected.ref } : {} };
7878
+ return { kind: "current-ref", ...selected.ref ? { ref: selected.ref } : {} };
7417
7879
  case "uploaded-snapshot":
7418
7880
  return { kind: "uploaded-snapshot", path: projectRoot, source: "local-working-tree" };
7419
7881
  case "existing-path":
@@ -7666,7 +8128,7 @@ function remoteGitHubAuthMetadata(payload) {
7666
8128
  };
7667
8129
  }
7668
8130
  function writeRemoteGitHubAuthState(projectRoot, input) {
7669
- writeFileSync6(resolve18(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
8131
+ writeFileSync7(resolve18(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
7670
8132
  authenticated: true,
7671
8133
  source: input.source,
7672
8134
  storedOnServer: true,
@@ -7714,7 +8176,7 @@ function runLocalFilesInit(context, options) {
7714
8176
  console.log("rig.config already exists; leaving it unchanged. Pass --repair to rewrite it.");
7715
8177
  } else {
7716
8178
  const projectName = basename3(projectRoot) || "rig-project";
7717
- writeFileSync6(configTsPath, buildRigInitConfigSource({
8179
+ writeFileSync7(configTsPath, buildRigInitConfigSource({
7718
8180
  projectName,
7719
8181
  taskSource: { kind: "files", path: "tasks" },
7720
8182
  useStandardPlugin: true
@@ -7723,8 +8185,8 @@ function runLocalFilesInit(context, options) {
7723
8185
  ensureRigConfigPackageDependencies(projectRoot);
7724
8186
  const tasksDir = resolve18(projectRoot, "tasks");
7725
8187
  if (!existsSync13(tasksDir)) {
7726
- mkdirSync9(tasksDir, { recursive: true });
7727
- writeFileSync6(resolve18(tasksDir, "T-1.json"), `${JSON.stringify({ id: "T-1", title: "My first Rig task", body: "Describe the change you want an agent to make." }, null, 2)}
8188
+ mkdirSync10(tasksDir, { recursive: true });
8189
+ writeFileSync7(resolve18(tasksDir, "T-1.json"), `${JSON.stringify({ id: "T-1", title: "My first Rig task", body: "Describe the change you want an agent to make." }, null, 2)}
7728
8190
  `, "utf-8");
7729
8191
  }
7730
8192
  if (context.outputMode !== "json") {
@@ -7748,7 +8210,7 @@ function runDemoInit(context, options) {
7748
8210
  console.log("rig.config already exists; leaving it unchanged. Pass --repair to rewrite it for the demo.");
7749
8211
  }
7750
8212
  } else {
7751
- writeFileSync6(configTsPath, buildRigInitConfigSource({
8213
+ writeFileSync7(configTsPath, buildRigInitConfigSource({
7752
8214
  projectName: basename3(projectRoot) || "rig-demo",
7753
8215
  taskSource: { kind: "files", path: DEMO_TASKS_RELATIVE_DIR },
7754
8216
  useStandardPlugin: true
@@ -7757,14 +8219,14 @@ function runDemoInit(context, options) {
7757
8219
  }
7758
8220
  ensureRigConfigPackageDependencies(projectRoot);
7759
8221
  const demoTasksDir = resolve18(projectRoot, DEMO_TASKS_RELATIVE_DIR);
7760
- mkdirSync9(demoTasksDir, { recursive: true });
8222
+ mkdirSync10(demoTasksDir, { recursive: true });
7761
8223
  const taskIds = [];
7762
8224
  for (const task of DEMO_TASKS) {
7763
8225
  const id = String(task.id);
7764
8226
  taskIds.push(id);
7765
8227
  const taskPath = resolve18(demoTasksDir, `${id}.json`);
7766
8228
  if (!existsSync13(taskPath)) {
7767
- writeFileSync6(taskPath, `${JSON.stringify(task, null, 2)}
8229
+ writeFileSync7(taskPath, `${JSON.stringify(task, null, 2)}
7768
8230
  `, "utf-8");
7769
8231
  }
7770
8232
  }
@@ -7792,21 +8254,32 @@ function runDemoInit(context, options) {
7792
8254
  }
7793
8255
  async function runControlPlaneInit(context, options) {
7794
8256
  const projectRoot = context.projectRoot;
7795
- const detectedSlug = options.repoSlug ?? detectOriginRepoSlug(projectRoot);
8257
+ const existingRepoConnection = readRepoConnection(projectRoot);
8258
+ const selectedConnection = (() => {
8259
+ try {
8260
+ return resolveSelectedConnection(projectRoot);
8261
+ } catch {
8262
+ return null;
8263
+ }
8264
+ })();
8265
+ const selectedRemote = selectedConnection?.connection.kind === "remote" ? { alias: selectedConnection.alias, connection: selectedConnection.connection } : null;
8266
+ const detectedSlug = options.repoSlug ?? existingRepoConnection?.project ?? detectOriginRepoSlug(projectRoot);
8267
+ const serverKind = options.server ?? (selectedRemote ? "remote" : "local");
7796
8268
  if (!detectedSlug) {
7797
8269
  const authMethod2 = options.githubAuthMethod ?? (options.githubToken ? "token" : "skip");
7798
- if ((options.server ?? "local") === "local" && authMethod2 === "skip") {
8270
+ if (serverKind === "local" && authMethod2 === "skip") {
7799
8271
  return runLocalFilesInit(context, options);
7800
8272
  }
7801
8273
  throw new CliError("Could not detect GitHub repo slug from origin. Pass --repo owner/repo \u2014 or run `rig init --yes --server local --github-auth skip` for a local files-source project without GitHub.", 1);
7802
8274
  }
7803
8275
  const repo = parseRepoSlug(detectedSlug);
7804
- const serverKind = options.server ?? "local";
7805
- const connectionAlias = options.connectionAlias ?? (serverKind === "local" ? "local" : "remote");
8276
+ const connectionAlias = options.connectionAlias ?? (serverKind === "local" ? "local" : selectedRemote?.alias ?? "remote");
8277
+ const selectedRemoteUrl = selectedRemote && (!options.connectionAlias || options.connectionAlias === selectedRemote.alias) ? selectedRemote.connection.baseUrl : undefined;
8278
+ const remoteUrl = serverKind === "remote" ? (options.remoteUrl ?? selectedRemoteUrl)?.replace(/\/+$/, "") : undefined;
7806
8279
  if (serverKind === "remote") {
7807
- if (!options.remoteUrl)
7808
- throw new CliError("Missing --remote-url for --server remote.", 1, { hint: "Re-run as `rig init --server remote --remote-url https://your-rig-server`." });
7809
- upsertGlobalConnection(connectionAlias, { kind: "remote", baseUrl: options.remoteUrl });
8280
+ if (!remoteUrl)
8281
+ throw new CliError("Missing --remote-url for --server remote.", 1, { hint: "Re-run as `rig init --server remote --remote-url https://your-rig-server`, or first select a saved remote with `rig server use <alias>`." });
8282
+ upsertGlobalConnection(connectionAlias, { kind: "remote", baseUrl: remoteUrl });
7810
8283
  }
7811
8284
  writeRepoConnection(projectRoot, {
7812
8285
  selected: connectionAlias,
@@ -7829,11 +8302,11 @@ async function runControlPlaneInit(context, options) {
7829
8302
  taskSource: { kind: "github-issues", owner: repo.owner, repo: repo.repo },
7830
8303
  useStandardPlugin: true
7831
8304
  }), options);
7832
- writeFileSync6(configTsPath, source, "utf-8");
8305
+ writeFileSync7(configTsPath, source, "utf-8");
7833
8306
  }
7834
8307
  ensureRigConfigPackageDependencies(projectRoot);
7835
8308
  }
7836
- writeFileSync6(resolve18(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
8309
+ writeFileSync7(resolve18(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
7837
8310
  `, "utf8");
7838
8311
  const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
7839
8312
  let uploadedSnapshot = null;
@@ -7848,7 +8321,7 @@ async function runControlPlaneInit(context, options) {
7848
8321
  let githubAuth = null;
7849
8322
  let deviceAuth = null;
7850
8323
  const authMethod = options.githubAuthMethod ?? (options.githubToken ? "token" : "skip");
7851
- const remoteGhTokenWarning = serverKind === "remote" && authMethod === "gh" ? `This sends a GitHub token from this machine to ${options.remoteUrl ?? "the remote Rig server"}.` : null;
8324
+ const remoteGhTokenWarning = serverKind === "remote" && authMethod === "gh" ? `This sends a GitHub token from this machine to ${remoteUrl ?? "the remote Rig server"}.` : null;
7852
8325
  if (remoteGhTokenWarning && !options.yes) {
7853
8326
  throw new CliError(`${remoteGhTokenWarning} Re-run with --yes to confirm this explicit token transfer.`, 1);
7854
8327
  }
@@ -8228,8 +8701,8 @@ var init_init = __esm(() => {
8228
8701
 
8229
8702
  // packages/cli/src/commands/github.ts
8230
8703
  import { spawnSync as spawnSync2 } from "child_process";
8231
- import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync7 } from "fs";
8232
- import { dirname as dirname4, resolve as resolve19 } from "path";
8704
+ import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync8 } from "fs";
8705
+ import { dirname as dirname5, resolve as resolve19 } from "path";
8233
8706
  function printPayload(context, payload, fallback) {
8234
8707
  if (context.outputMode === "json")
8235
8708
  console.log(JSON.stringify(payload, null, 2));
@@ -8268,8 +8741,8 @@ function persistRemoteAuthSession(context, source, result, fallbackToken) {
8268
8741
  return;
8269
8742
  const repo = readRepoConnection(context.projectRoot);
8270
8743
  const path = resolve19(context.projectRoot, ".rig", "state", "github-auth.json");
8271
- mkdirSync10(dirname4(path), { recursive: true });
8272
- writeFileSync7(path, `${JSON.stringify({
8744
+ mkdirSync11(dirname5(path), { recursive: true });
8745
+ writeFileSync8(path, `${JSON.stringify({
8273
8746
  authenticated: true,
8274
8747
  source,
8275
8748
  storedOnServer: true,
@@ -10393,13 +10866,46 @@ function parseConnection(alias, value, options) {
10393
10866
  }
10394
10867
  return { kind: "remote", baseUrl: parsed.toString().replace(/\/+$/, "") };
10395
10868
  }
10396
- function printJsonOrText(context, payload, text2) {
10397
- if (context.outputMode === "json") {
10398
- console.log(JSON.stringify(payload, null, 2));
10399
- } else {
10869
+ function printJsonOrText(context, _payload, text2) {
10870
+ if (context.outputMode !== "json") {
10400
10871
  console.log(text2);
10401
10872
  }
10402
10873
  }
10874
+ function formatRemoteProjectLinkText(result) {
10875
+ return formatSuccessCard(result.ok ? "Remote project link ready" : "Remote project link needs repair", [
10876
+ ["selected", result.alias ?? "remote"],
10877
+ ["target", result.baseUrl ?? "unknown"],
10878
+ ["repo", result.repoSlug ?? "owner/repo"],
10879
+ ["status", result.status],
10880
+ ...result.serverProjectRoot ? [["server root", result.serverProjectRoot]] : [],
10881
+ ["next", result.ok ? result.next ?? "rig task list" : result.hint]
10882
+ ]);
10883
+ }
10884
+ function parseRepairLinkArgs(rest, usage) {
10885
+ let repoSlug;
10886
+ let mode = "prepare-if-missing";
10887
+ const pending = [...rest];
10888
+ while (pending.length > 0) {
10889
+ const token = pending.shift();
10890
+ if (token === "--repo") {
10891
+ const value = pending.shift()?.trim();
10892
+ if (!value)
10893
+ throw new CliError(`Missing --repo value. Usage: ${usage}`, 1);
10894
+ const normalized = normalizeRepoSlug(value);
10895
+ if (!normalized)
10896
+ throw new CliError(`Invalid --repo value: ${value}`, 1, { hint: "Use the canonical owner/repo slug, e.g. `--repo humanity-org/humanwork`." });
10897
+ repoSlug = normalized;
10898
+ } else if (token === "--backfill-only") {
10899
+ mode = "backfill-only";
10900
+ } else if (token === "--prepare") {
10901
+ mode = "prepare-if-missing";
10902
+ } else {
10903
+ throw new CliError(`Unexpected argument: ${String(token)}
10904
+ Usage: ${usage}`, 1);
10905
+ }
10906
+ }
10907
+ return { repoSlug, mode };
10908
+ }
10403
10909
  async function promptForConnectionAlias(context) {
10404
10910
  const state = readGlobalConnections();
10405
10911
  const repo = readRepoConnection(context.projectRoot);
@@ -10453,37 +10959,67 @@ async function executeConnectionCommand(context, args, options) {
10453
10959
  }
10454
10960
  if (!alias)
10455
10961
  throw new CliError(`Missing alias. Usage: ${usageName(options)} use <alias|local>`, 1);
10962
+ let selectedConnection = { kind: "local", mode: "auto" };
10456
10963
  if (alias !== "local") {
10457
10964
  const state = readGlobalConnections();
10458
- if (!state.connections[alias])
10965
+ const connection = state.connections[alias];
10966
+ if (!connection)
10459
10967
  throw new CliError(`Unknown Rig server: ${alias}`, 1, { hint: "Run `rig server list` to see saved aliases, or save one with `rig server add <alias> <url>`." });
10968
+ selectedConnection = connection;
10460
10969
  }
10461
10970
  const previousRepo = readRepoConnection(context.projectRoot);
10971
+ const preserveRemoteRoot = Boolean(previousRepo?.serverProjectRoot && previousRepo.selected === alias && selectedConnection.kind === "remote" && previousRepo.serverProjectRootAlias === alias && previousRepo.serverProjectRootBaseUrl === selectedConnection.baseUrl);
10462
10972
  const repoState = {
10463
10973
  selected: alias,
10464
10974
  ...previousRepo?.project ? { project: previousRepo.project } : {},
10465
10975
  linkedAt: new Date().toISOString(),
10466
- ...previousRepo?.serverProjectRoot && previousRepo.selected === alias ? { serverProjectRoot: previousRepo.serverProjectRoot } : {}
10976
+ ...preserveRemoteRoot ? {
10977
+ serverProjectRoot: previousRepo.serverProjectRoot,
10978
+ serverProjectRootAlias: previousRepo.serverProjectRootAlias,
10979
+ serverProjectRootBaseUrl: previousRepo.serverProjectRootBaseUrl
10980
+ } : {}
10467
10981
  };
10468
10982
  writeRepoConnection(context.projectRoot, repoState);
10469
- printJsonOrText(context, repoState, formatSuccessCard("Rig server selected", [
10983
+ const remoteProjectLink = alias !== "local" ? await ensureRemoteProjectRootLink(context.projectRoot, { mode: "backfill-only" }).catch((error) => ({
10984
+ ok: false,
10985
+ status: "error",
10986
+ message: error instanceof Error ? error.message : String(error),
10987
+ hint: "Run `rig server repair-link` after authenticating the selected remote."
10988
+ })) : null;
10989
+ const latestRepo = readRepoConnection(context.projectRoot);
10990
+ const details = { selected: latestRepo?.selected ?? alias, repo: latestRepo, remoteProjectLink };
10991
+ printJsonOrText(context, details, formatSuccessCard("Rig server selected", [
10470
10992
  ["selected", alias],
10471
10993
  ["scope", "this repo"],
10472
- ["next", "rig task list"]
10994
+ ...remoteProjectLink ? [["remote link", remoteProjectLink.ok ? `ready (${remoteProjectLink.serverProjectRoot ?? "linked"})` : `${remoteProjectLink.status}: ${remoteProjectLink.hint}`]] : [],
10995
+ ["next", remoteProjectLink && !remoteProjectLink.ok ? "rig server repair-link" : "rig task list"]
10473
10996
  ]));
10474
- return { ok: true, group: options.group, command: "use", details: repoState };
10997
+ return { ok: true, group: options.group, command: "use", details };
10998
+ }
10999
+ case "repair-link": {
11000
+ const parsed = parseRepairLinkArgs(rest, `${usageName(options)} repair-link [--prepare|--backfill-only] [--repo owner/repo]`);
11001
+ const result = await repairRemoteProjectRootLink(context, parsed);
11002
+ printJsonOrText(context, result, formatRemoteProjectLinkText(result));
11003
+ return { ok: true, group: options.group, command: "repair-link", details: result };
10475
11004
  }
10476
11005
  case "status": {
10477
11006
  requireNoExtraArgs(rest, `${usageName(options)} status`);
10478
11007
  const repo = readRepoConnection(context.projectRoot);
10479
11008
  const global = readGlobalConnections();
10480
- const details = { selected: repo?.selected ?? "local", repo, connections: global.connections };
10481
- printJsonOrText(context, details, formatConnectionStatus(details.selected, global.connections));
11009
+ const remoteProjectLink = repo?.selected && repo.selected !== "local" ? await ensureRemoteProjectRootLink(context.projectRoot, { mode: "backfill-only" }).catch((error) => ({
11010
+ ok: false,
11011
+ status: "error",
11012
+ message: error instanceof Error ? error.message : String(error),
11013
+ hint: "Run `rig server repair-link` after authenticating the selected remote."
11014
+ })) : null;
11015
+ const latestRepo = readRepoConnection(context.projectRoot) ?? repo;
11016
+ const details = { selected: latestRepo?.selected ?? "local", repo: latestRepo, connections: global.connections, remoteProjectLink };
11017
+ printJsonOrText(context, details, formatConnectionStatus(details.selected, global.connections, latestRepo ?? null, remoteProjectLink));
10482
11018
  return { ok: true, group: options.group, command: "status", details };
10483
11019
  }
10484
11020
  default:
10485
11021
  throw new CliError(`Unknown ${options.group} command: ${String(command)}
10486
- Usage: ${usageName(options)} <list|add|use|status>`, 1);
11022
+ Usage: ${usageName(options)} <list|add|use|status|repair-link>`, 1);
10487
11023
  }
10488
11024
  }
10489
11025
  var init_connect = __esm(() => {
@@ -10491,13 +11027,14 @@ var init_connect = __esm(() => {
10491
11027
  init_runner();
10492
11028
  init__connection_state();
10493
11029
  init__cli_format();
11030
+ init__server_client();
10494
11031
  });
10495
11032
 
10496
11033
  // packages/cli/src/commands/server.ts
10497
11034
  import { resolveRigServerCommand } from "@rig/runtime/local-server";
10498
11035
  async function executeServer(context, args, options) {
10499
11036
  const [command = "status", ...rest] = args;
10500
- if (["status", "list", "add", "use"].includes(command)) {
11037
+ if (["status", "list", "add", "use", "repair-link"].includes(command)) {
10501
11038
  return executeConnectionCommand(context, [command, ...rest], { group: "server", interactiveUse: true });
10502
11039
  }
10503
11040
  switch (command) {
@@ -11284,7 +11821,7 @@ var init_task = __esm(() => {
11284
11821
  });
11285
11822
 
11286
11823
  // packages/cli/src/commands/task-run-driver.ts
11287
- import { copyFileSync as copyFileSync3, existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync12, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
11824
+ import { copyFileSync as copyFileSync3, existsSync as existsSync16, mkdirSync as mkdirSync12, readFileSync as readFileSync12, statSync as statSync2, writeFileSync as writeFileSync9 } from "fs";
11288
11825
  import { resolve as resolve24 } from "path";
11289
11826
  import { spawn as spawn2, spawnSync as spawnSync4 } from "child_process";
11290
11827
  import { createInterface as createLineInterface } from "readline";
@@ -11387,7 +11924,7 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
11387
11924
  try {
11388
11925
  if (!statSync2(sourcePath).isFile())
11389
11926
  continue;
11390
- mkdirSync11(resolve24(targetPath, ".."), { recursive: true });
11927
+ mkdirSync12(resolve24(targetPath, ".."), { recursive: true });
11391
11928
  copyFileSync3(sourcePath, targetPath);
11392
11929
  copied += 1;
11393
11930
  } catch {}
@@ -12258,8 +12795,8 @@ async function executeRigOwnedTaskRun(context, input) {
12258
12795
  artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
12259
12796
  classifiedAt: new Date().toISOString()
12260
12797
  };
12261
- mkdirSync11(resolve24(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
12262
- writeFileSync8(resolve24(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
12798
+ mkdirSync12(resolve24(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
12799
+ writeFileSync9(resolve24(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
12263
12800
  `, "utf8");
12264
12801
  patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
12265
12802
  prompt = `${prompt}
@@ -12406,8 +12943,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
12406
12943
  const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
12407
12944
  const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
12408
12945
  if (readyFile) {
12409
- mkdirSync11(resolve24(readyFile, ".."), { recursive: true });
12410
- writeFileSync8(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
12946
+ mkdirSync12(resolve24(readyFile, ".."), { recursive: true });
12947
+ writeFileSync9(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
12411
12948
  `, "utf8");
12412
12949
  }
12413
12950
  appendRunLog(context.projectRoot, input.runId, {
@@ -13353,7 +13890,7 @@ var init_test = __esm(() => {
13353
13890
  });
13354
13891
 
13355
13892
  // packages/cli/src/commands/setup.ts
13356
- import { existsSync as existsSync17, mkdirSync as mkdirSync12, readdirSync as readdirSync3, writeFileSync as writeFileSync9 } from "fs";
13893
+ import { existsSync as existsSync17, mkdirSync as mkdirSync13, readdirSync as readdirSync3, writeFileSync as writeFileSync10 } from "fs";
13357
13894
  import { resolve as resolve25 } from "path";
13358
13895
  import { createPluginHost } from "@rig/core";
13359
13896
  import {
@@ -13408,12 +13945,12 @@ function runSetupInit(projectRoot) {
13408
13945
  const stateDir = resolveControlPlaneHostStateDir(projectRoot);
13409
13946
  const logsDir = resolveControlPlaneHostLogsDir(projectRoot);
13410
13947
  const artifactsDir = resolveControlPlaneArtifactsDir(projectRoot);
13411
- mkdirSync12(stateDir, { recursive: true });
13412
- mkdirSync12(logsDir, { recursive: true });
13413
- mkdirSync12(artifactsDir, { recursive: true });
13948
+ mkdirSync13(stateDir, { recursive: true });
13949
+ mkdirSync13(logsDir, { recursive: true });
13950
+ mkdirSync13(artifactsDir, { recursive: true });
13414
13951
  const failuresPath = resolve25(stateDir, "failed_approaches.md");
13415
13952
  if (!existsSync17(failuresPath)) {
13416
- writeFileSync9(failuresPath, `# Failed Approaches
13953
+ writeFileSync10(failuresPath, `# Failed Approaches
13417
13954
 
13418
13955
  `, "utf-8");
13419
13956
  }
@@ -16291,6 +16828,7 @@ var LOG_PAGE = 100, TIMELINE_PAGE = 200;
16291
16828
  var init_run_detail = () => {};
16292
16829
 
16293
16830
  // packages/cli/src/app-opentui/adapters/server.ts
16831
+ import { spawnSync as spawnSync5 } from "child_process";
16294
16832
  function withTimeout(promise, ms, label) {
16295
16833
  return new Promise((resolve29, reject) => {
16296
16834
  const timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
@@ -16327,11 +16865,128 @@ function startLocalRequested(ctx) {
16327
16865
  const action = intent.action;
16328
16866
  return action?.payload?.startLocal === true;
16329
16867
  }
16868
+ async function serverAliases(projectRoot) {
16869
+ const { readGlobalConnections: readGlobalConnections2, readRepoConnection: readRepoConnection2 } = await Promise.resolve().then(() => (init__connection_state(), exports__connection_state));
16870
+ const selected = readRepoConnection2(projectRoot)?.selected ?? "local";
16871
+ const global = readGlobalConnections2();
16872
+ const aliases = [{ alias: "local", kind: "local", selected: selected === "local" }];
16873
+ for (const [alias, connection] of Object.entries(global.connections)) {
16874
+ aliases.push({
16875
+ alias,
16876
+ kind: connection.kind,
16877
+ ...connection.kind === "remote" ? { baseUrl: connection.baseUrl } : {},
16878
+ selected: alias === selected
16879
+ });
16880
+ }
16881
+ return aliases;
16882
+ }
16883
+ function firstString2(parts, offset = 0) {
16884
+ return parts.slice(offset).find((part) => part.trim() && !part.startsWith("-"))?.trim();
16885
+ }
16886
+ function normalizeUrl(value) {
16887
+ const trimmed = value?.trim().replace(/\/+$/, "");
16888
+ if (!trimmed)
16889
+ return;
16890
+ return /^https?:\/\//i.test(trimmed) ? trimmed : undefined;
16891
+ }
16892
+ async function useLocalServerFromApp(ctx) {
16893
+ const label = "Selecting local server";
16894
+ emitStarted(ctx, label);
16895
+ try {
16896
+ const projectRoot = projectRootOf(ctx);
16897
+ const { readRepoConnection: readRepoConnection2, writeRepoConnection: writeRepoConnection2 } = await Promise.resolve().then(() => (init__connection_state(), exports__connection_state));
16898
+ const previous = readRepoConnection2(projectRoot);
16899
+ writeRepoConnection2(projectRoot, {
16900
+ selected: "local",
16901
+ ...previous?.project ? { project: previous.project } : {},
16902
+ linkedAt: new Date().toISOString()
16903
+ });
16904
+ emitCompleted(ctx, label, { selected: "local" });
16905
+ return refreshServerStatus(ctx);
16906
+ } catch (error) {
16907
+ emitFailed(ctx, label, error);
16908
+ throw error;
16909
+ }
16910
+ }
16911
+ async function useRemoteServerFromApp(ctx, alias) {
16912
+ const label = alias ? `Selecting remote ${alias}` : "Selecting remote server";
16913
+ emitStarted(ctx, label);
16914
+ try {
16915
+ const cleanAlias = alias?.trim();
16916
+ if (!cleanAlias)
16917
+ throw new Error("Enter a saved remote alias, or add one with: add remote \u2192 <alias> <https-url>.");
16918
+ const projectRoot = projectRootOf(ctx);
16919
+ const { readGlobalConnections: readGlobalConnections2, readRepoConnection: readRepoConnection2, writeRepoConnection: writeRepoConnection2 } = await Promise.resolve().then(() => (init__connection_state(), exports__connection_state));
16920
+ const connection = readGlobalConnections2().connections[cleanAlias];
16921
+ if (!connection)
16922
+ throw new Error(`Remote alias ${cleanAlias} is not saved yet. Use add remote first.`);
16923
+ if (connection.kind !== "remote")
16924
+ throw new Error(`${cleanAlias} is not a remote server alias.`);
16925
+ const previous = readRepoConnection2(projectRoot);
16926
+ writeRepoConnection2(projectRoot, {
16927
+ selected: cleanAlias,
16928
+ ...previous?.project ? { project: previous.project } : {},
16929
+ linkedAt: new Date().toISOString()
16930
+ });
16931
+ emitCompleted(ctx, label, { selected: cleanAlias, baseUrl: connection.baseUrl });
16932
+ return refreshServerStatus(ctx);
16933
+ } catch (error) {
16934
+ emitFailed(ctx, label, error);
16935
+ throw error;
16936
+ }
16937
+ }
16938
+ async function addRemoteServerFromApp(ctx, args) {
16939
+ const label = "Adding remote server";
16940
+ emitStarted(ctx, label);
16941
+ try {
16942
+ const alias = firstString2(args, 2);
16943
+ const url = normalizeUrl(firstString2(args, alias ? args.indexOf(alias) + 1 : 3));
16944
+ if (!alias || !url)
16945
+ throw new Error("Add remote expects: <alias> <https-url>. Example: prod https://rig.example.com");
16946
+ const projectRoot = projectRootOf(ctx);
16947
+ const { readRepoConnection: readRepoConnection2, upsertGlobalConnection: upsertGlobalConnection2, writeRepoConnection: writeRepoConnection2 } = await Promise.resolve().then(() => (init__connection_state(), exports__connection_state));
16948
+ upsertGlobalConnection2(alias, { kind: "remote", baseUrl: url });
16949
+ const previous = readRepoConnection2(projectRoot);
16950
+ writeRepoConnection2(projectRoot, {
16951
+ selected: alias,
16952
+ ...previous?.project ? { project: previous.project } : {},
16953
+ linkedAt: new Date().toISOString()
16954
+ });
16955
+ emitCompleted(ctx, label, { alias, baseUrl: url });
16956
+ return refreshServerStatus(ctx);
16957
+ } catch (error) {
16958
+ emitFailed(ctx, label, error);
16959
+ throw error;
16960
+ }
16961
+ }
16962
+ async function importGitHubAuthFromGhApp(ctx) {
16963
+ const label = "Importing GitHub auth";
16964
+ emitStarted(ctx, label);
16965
+ try {
16966
+ const projectRoot = projectRootOf(ctx);
16967
+ const token = spawnSync5("gh", ["auth", "token"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 1e4 }).stdout.trim();
16968
+ if (!token)
16969
+ throw new Error("Could not read GitHub token from gh. Sign in with gh auth login, then retry import auth.");
16970
+ const runtime = await runtimeOf(ctx);
16971
+ const { postGitHubTokenViaServer: postGitHubTokenViaServer2, setGitHubBearerTokenForCurrentProcess: setGitHubBearerTokenForCurrentProcess2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
16972
+ const { readRepoConnection: readRepoConnection2 } = await Promise.resolve().then(() => (init__connection_state(), exports__connection_state));
16973
+ const selectedRepo = readRepoConnection2(projectRoot)?.project;
16974
+ const payload = await postGitHubTokenViaServer2(runtime, token, selectedRepo ? { selectedRepo } : {});
16975
+ const apiSessionToken = payload && typeof payload === "object" && !Array.isArray(payload) ? payload.apiSessionToken : undefined;
16976
+ setGitHubBearerTokenForCurrentProcess2(typeof apiSessionToken === "string" ? apiSessionToken : token, projectRoot);
16977
+ emitCompleted(ctx, label, { selectedRepo: selectedRepo ?? null });
16978
+ return refreshServerStatus(ctx);
16979
+ } catch (error) {
16980
+ emitFailed(ctx, label, error);
16981
+ throw error;
16982
+ }
16983
+ }
16330
16984
  async function startLocalServerFromApp(ctx) {
16331
16985
  const label = "Starting local server";
16332
16986
  emitStarted(ctx, label);
16333
16987
  patchData(ctx, {
16334
16988
  lastIntent: undefined,
16989
+ remoteProjectLink: undefined,
16335
16990
  server: { label: "starting local server", reachable: false, error: "spawning local rig-server (detached)\u2026" }
16336
16991
  });
16337
16992
  try {
@@ -16356,32 +17011,61 @@ async function startLocalServerFromApp(ctx) {
16356
17011
  reachable: false,
16357
17012
  error: error instanceof Error ? error.message : String(error)
16358
17013
  };
16359
- patchData(ctx, { server: state, lastRefreshError: state.error });
17014
+ patchData(ctx, { server: state, remoteProjectLink: undefined, lastRefreshError: state.error });
16360
17015
  patchFooter(ctx, { server: "unavailable" });
16361
- emitCompleted(ctx, label, { server: state.label, reachable: false, error: state.error });
17016
+ emitCompleted(ctx, label, { serverLabel: state.label, reachable: false, error: state.error });
16362
17017
  return state;
16363
17018
  }
16364
17019
  }
17020
+ async function repairRemoteProjectRootLinkFromApp(ctx) {
17021
+ const label = "Repairing remote project link";
17022
+ emitStarted(ctx, label);
17023
+ try {
17024
+ const projectRoot = projectRootOf(ctx);
17025
+ const { ensureRemoteProjectRootLink: ensureRemoteProjectRootLink2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
17026
+ emitProgress(ctx, label, "backfilling or preparing server-host checkout");
17027
+ const result = await ensureRemoteProjectRootLink2(projectRoot, { mode: "prepare-if-missing" });
17028
+ patchData(ctx, { remoteProjectLink: result, server: { label: result.alias ?? "remote", baseUrl: result.baseUrl, kind: "remote", reachable: result.status !== "not_remote", remoteProjectLink: result } });
17029
+ patchFooter(ctx, { server: result.alias ?? "remote", message: `remote link ${result.status}` });
17030
+ if (!result.ok) {
17031
+ const error = new Error(result.message);
17032
+ error.hint = result.hint;
17033
+ patchData(ctx, { lastRefreshError: result.message });
17034
+ throw error;
17035
+ }
17036
+ emitCompleted(ctx, label, { repoSlug: result.repoSlug, serverProjectRoot: result.serverProjectRoot, status: result.status });
17037
+ return result;
17038
+ } catch (error) {
17039
+ const message2 = error instanceof Error ? error.message : String(error);
17040
+ const existing = ctx.getState().data?.remoteProjectLink;
17041
+ const remoteProjectLink = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : { ok: false, status: "error", message: message2 };
17042
+ patchData(ctx, { lastRefreshError: message2, remoteProjectLink });
17043
+ emitCompleted(ctx, label, { ok: false, error: message2, remoteProjectLink });
17044
+ throw error;
17045
+ }
17046
+ }
16365
17047
  async function refreshServerStatus(ctx) {
16366
17048
  if (startLocalRequested(ctx)) {
16367
17049
  return startLocalServerFromApp(ctx);
16368
17050
  }
16369
17051
  const label = "Checking server";
16370
17052
  emitStarted(ctx, label);
16371
- patchData(ctx, { server: { label: "checking selected server", reachable: false, error: "checking selected server\u2026" } });
17053
+ patchData(ctx, { server: { label: "checking selected server", reachable: false, error: "checking selected server\u2026" }, remoteProjectLink: undefined });
16372
17054
  const started = Date.now();
16373
17055
  try {
16374
- const { ensureServerForCli: ensureServerForCli2, requestServerJson: requestServerJson2, resolveServerConnectionLabel: resolveServerConnectionLabel2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
17056
+ const { ensureRemoteProjectRootLink: ensureRemoteProjectRootLink2, ensureServerForCli: ensureServerForCli2, requestServerJson: requestServerJson2, resolveServerConnectionLabel: resolveServerConnectionLabel2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
16375
17057
  const projectRoot = projectRootOf(ctx);
17058
+ const aliases = await serverAliases(projectRoot);
16376
17059
  emitProgress(ctx, label, "resolving selected server");
16377
17060
  const [connectionLabel, server] = await Promise.all([
16378
17061
  resolveServerConnectionLabel2(projectRoot),
16379
17062
  withTimeout(ensureServerForCli2(projectRoot), 5000, "server connection")
16380
17063
  ]);
16381
17064
  emitProgress(ctx, label, "requesting server status", { baseUrl: server.baseUrl, kind: server.connectionKind });
16382
- const [status, auth] = await Promise.all([
17065
+ const [status, auth, remoteProjectLink] = await Promise.all([
16383
17066
  withRetry("server status", () => withTimeout(requestServerJson2({ projectRoot }, "/api/server/status"), SERVER_PROBE_TIMEOUT_MS, "server status")).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error) })),
16384
- withRetry("github auth status", () => withTimeout(requestServerJson2({ projectRoot }, "/api/github/auth/status"), SERVER_PROBE_TIMEOUT_MS, "github auth status")).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error) }))
17067
+ withRetry("github auth status", () => withTimeout(requestServerJson2({ projectRoot }, "/api/github/auth/status"), SERVER_PROBE_TIMEOUT_MS, "github auth status")).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error) })),
17068
+ server.connectionKind === "remote" ? withTimeout(ensureRemoteProjectRootLink2(projectRoot, { mode: "backfill-only", authToken: server.authToken }), SERVER_PROBE_TIMEOUT_MS, "remote project link").catch((error) => ({ ok: false, status: "error", message: error instanceof Error ? error.message : String(error) })) : Promise.resolve(null)
16385
17069
  ]);
16386
17070
  const state = {
16387
17071
  label: connectionLabel,
@@ -16390,22 +17074,25 @@ async function refreshServerStatus(ctx) {
16390
17074
  reachable: true,
16391
17075
  latencyMs: Date.now() - started,
16392
17076
  status: status && typeof status === "object" && !Array.isArray(status) ? status : {},
16393
- auth: auth && typeof auth === "object" && !Array.isArray(auth) ? auth : {}
17077
+ auth: auth && typeof auth === "object" && !Array.isArray(auth) ? auth : {},
17078
+ aliases,
17079
+ ...remoteProjectLink && typeof remoteProjectLink === "object" && !Array.isArray(remoteProjectLink) ? { remoteProjectLink } : {}
16394
17080
  };
16395
- patchData(ctx, { server: state });
17081
+ patchData(ctx, { server: state, remoteProjectLink: state.remoteProjectLink });
16396
17082
  patchFooter(ctx, { server: connectionLabel, latency: `${state.latencyMs}ms` });
16397
- emitCompleted(ctx, label, { server: connectionLabel, latencyMs: state.latencyMs });
17083
+ emitCompleted(ctx, label, { serverLabel: connectionLabel, latencyMs: state.latencyMs });
16398
17084
  return state;
16399
17085
  } catch (error) {
16400
17086
  const state = {
16401
17087
  label: "server unavailable",
16402
17088
  reachable: false,
16403
17089
  latencyMs: Date.now() - started,
17090
+ aliases: await serverAliases(projectRootOf(ctx)).catch(() => []),
16404
17091
  error: error instanceof Error ? error.message : String(error)
16405
17092
  };
16406
- patchData(ctx, { server: state });
17093
+ patchData(ctx, { server: state, remoteProjectLink: undefined });
16407
17094
  patchFooter(ctx, { server: "unavailable" });
16408
- emitCompleted(ctx, label, { server: state.label, reachable: false, error: state.error });
17095
+ emitCompleted(ctx, label, { serverLabel: state.label, reachable: false, error: state.error });
16409
17096
  return state;
16410
17097
  }
16411
17098
  }
@@ -17433,11 +18120,56 @@ var init_doctor3 = __esm(() => {
17433
18120
  DOCTOR_ACTIONS = [
17434
18121
  { detail: "run diagnostics now", item: { id: "run", label: "run", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "run diagnostics" } },
17435
18122
  { detail: "open server controls", item: { id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Open server controls" } }, message: "open server controls" } },
17436
- { detail: "repair project/server/auth link", item: { id: "repair", label: "repair", intent: { scene: "command", argv: ["init", "--repair"], action: { kind: "command-run", label: "Repair project link" } }, message: "repair project link" } },
18123
+ { detail: "repair project/server/auth link", item: { id: "repair", label: "repair", intent: { scene: "init", argv: ["init", "--repair", "--yes"], action: { kind: "init-start", label: "Repair project link" } }, message: "repair project link natively" } },
17437
18124
  { detail: "open tasks", item: { id: "tasks", label: "tasks", intent: { scene: "tasks", argv: ["tasks"], action: { kind: "refresh", label: "Open tasks" } }, message: "open tasks" } }
17438
18125
  ];
17439
18126
  });
17440
18127
 
18128
+ // packages/cli/src/app-opentui/remote-link.ts
18129
+ function remoteProjectLinkError(message2) {
18130
+ return /no server-host project root link|remote project(?:-root)? link|x-rig-project-root|serverProjectRoot/i.test(message2 ?? "");
18131
+ }
18132
+ function serverRecordForRemoteLink(state) {
18133
+ const server = state.data.server;
18134
+ return server && typeof server === "object" && !Array.isArray(server) ? server : null;
18135
+ }
18136
+ function remoteProjectLinkState(state) {
18137
+ const serverRecord = serverRecordForRemoteLink(state);
18138
+ if (!serverRecord || serverRecord.kind !== "remote")
18139
+ return null;
18140
+ const link = serverRecord.remoteProjectLink;
18141
+ if (link && typeof link === "object" && !Array.isArray(link))
18142
+ return link;
18143
+ const direct = state.data.remoteProjectLink;
18144
+ if (direct && typeof direct === "object" && !Array.isArray(direct))
18145
+ return direct;
18146
+ return null;
18147
+ }
18148
+ function shouldOfferRemoteLinkRepair(state, message2) {
18149
+ const link = remoteProjectLinkState(state);
18150
+ if (link) {
18151
+ if (link.ok === true)
18152
+ return false;
18153
+ const status = link.status ?? "";
18154
+ return REPAIRABLE_REMOTE_LINK_STATUSES.has(status);
18155
+ }
18156
+ const serverRecord = serverRecordForRemoteLink(state);
18157
+ if (serverRecord && serverRecord.kind !== "remote")
18158
+ return false;
18159
+ return remoteProjectLinkError(message2 ?? state.error?.message);
18160
+ }
18161
+ var REPAIRABLE_REMOTE_LINK_STATUSES;
18162
+ var init_remote_link = __esm(() => {
18163
+ REPAIRABLE_REMOTE_LINK_STATUSES = new Set([
18164
+ "auth_required",
18165
+ "project_not_registered",
18166
+ "no_server_checkout",
18167
+ "invalid_root",
18168
+ "needs_prepare",
18169
+ "error"
18170
+ ]);
18171
+ });
18172
+
17441
18173
  // packages/cli/src/app-opentui/fleet-stats.ts
17442
18174
  function normalizeStatus(status) {
17443
18175
  return status.replace(/_/g, "-").toLowerCase();
@@ -19353,13 +20085,18 @@ function overviewBody(state, family, snap, layout) {
19353
20085
  function familyRecoveryRows(state, family, errorMessage2) {
19354
20086
  const selected = Math.max(0, state.selection.index);
19355
20087
  const intro = errorMessage2 ? line(`\u2717 couldn't load ${family || "this family"} \u2014 ${errorMessage2}`, { fg: RIG_UI.red, bold: true }) : line(`No data for ${family || "this family"} yet.`, { fg: RIG_UI.ink2 });
19356
- return [
20088
+ const repairRemote = shouldOfferRemoteLinkRepair(state, errorMessage2);
20089
+ const rows = [
19357
20090
  intro,
19358
- blank(),
19359
- selectableDeckRow({ label: "retry", detail: `reload ${family || "the family"}`, index: 0, active: selected === 0 }, { id: "family-retry", label: "retry", intent: { scene: "family", argv: [family], action: { kind: "refresh", label: `Reload ${family}`, payload: { family } } }, message: "reload this family" }),
19360
- selectableDeckRow({ label: "cockpit", detail: "back to the project cockpit", index: 1, active: selected === 1 }, { id: "family-home", label: "cockpit", intent: { scene: "main", argv: [], action: { kind: "refresh", label: "Home" } }, message: "back to the cockpit" }),
19361
- selectableDeckRow({ label: "doctor", detail: "diagnose project/setup", index: 2, active: selected === 2 }, { id: "family-doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "diagnose setup" })
20091
+ blank()
19362
20092
  ];
20093
+ let index = 0;
20094
+ if (repairRemote) {
20095
+ rows.push(selectableDeckRow({ label: "repair link", detail: "backfill/prepare selected remote checkout link", index, active: selected === index }, { id: "family-repair-link", label: "repair link", intent: { scene: "server", argv: ["server", "repair-link"], action: { kind: "remote-link-repair", label: "Repair remote link", payload: { returnScene: "family", family } } }, message: "repair remote project link" }));
20096
+ index += 1;
20097
+ }
20098
+ rows.push(selectableDeckRow({ label: "retry", detail: `reload ${family || "the family"}`, index, active: selected === index }, { id: "family-retry", label: "retry", intent: { scene: "family", argv: [family], action: { kind: "refresh", label: `Reload ${family}`, payload: { family } } }, message: "reload this family" }), selectableDeckRow({ label: "cockpit", detail: "back to the project cockpit", index: index + 1, active: selected === index + 1 }, { id: "family-home", label: "cockpit", intent: { scene: "main", argv: [], action: { kind: "refresh", label: "Home" } }, message: "back to the cockpit" }), selectableDeckRow({ label: "doctor", detail: "diagnose project/setup", index: index + 2, active: selected === index + 2 }, { id: "family-doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "diagnose setup" }));
20099
+ return rows;
19363
20100
  }
19364
20101
  function renderFamilyScene(state, layout) {
19365
20102
  const family = typeof state.data.familyName === "string" ? state.data.familyName : "";
@@ -19368,9 +20105,16 @@ function renderFamilyScene(state, layout) {
19368
20105
  const loading = state.status === "loading" && !snap;
19369
20106
  const errorMessage2 = state.error?.message ?? lastRefreshError(state) ?? undefined;
19370
20107
  const probeFailed = snap ? snap.state.ran === false && Boolean(state.error) : Boolean(state.error);
20108
+ const selected = Math.max(0, state.selection.index);
19371
20109
  const body = [
19372
20110
  ...loading ? loadingRows(family || "family", state.tick) : snap ? [
19373
- ...probeFailed && errorMessage2 ? [...errorBanner(errorMessage2), blank()] : [],
20111
+ ...probeFailed && errorMessage2 ? [
20112
+ ...errorBanner(errorMessage2),
20113
+ ...shouldOfferRemoteLinkRepair(state, errorMessage2) ? [
20114
+ selectableDeckRow({ label: "repair link", detail: "backfill/prepare selected remote checkout link", index: 0, active: selected === 0 }, { id: "family-repair-link", label: "repair link", intent: { scene: "server", argv: ["server", "repair-link"], action: { kind: "remote-link-repair", label: "Repair remote link", payload: { returnScene: "family", family } } }, message: "repair remote project link" })
20115
+ ] : [],
20116
+ blank()
20117
+ ] : [],
19374
20118
  ...current.subId ? formView(state, family, snap, current).lines : overviewBody(state, family, snap, layout)
19375
20119
  ] : familyRecoveryRows(state, family, errorMessage2)
19376
20120
  ];
@@ -19404,6 +20148,7 @@ function renderFamilyScene(state, layout) {
19404
20148
  var init_family2 = __esm(() => {
19405
20149
  init_scene();
19406
20150
  init_selectable();
20151
+ init_remote_link();
19407
20152
  init_theme2();
19408
20153
  init_family_domains();
19409
20154
  init_kit();
@@ -19674,27 +20419,38 @@ function runRow(width, run, active, optimistic) {
19674
20419
  const item = runSelectableItem(run);
19675
20420
  return item ? withSelectable(row, item) : row;
19676
20421
  }
19677
- function recoveryRows(selected, reason) {
20422
+ function fleetRecoveryItems(state) {
20423
+ if (!shouldOfferRemoteLinkRepair(state))
20424
+ return BASE_FLEET_RECOVERY_ITEMS;
20425
+ return [BASE_FLEET_RECOVERY_ITEMS[0], FLEET_REPAIR_LINK_ITEM, ...BASE_FLEET_RECOVERY_ITEMS.slice(1)];
20426
+ }
20427
+ function fleetRecoveryDecks(state) {
20428
+ if (!shouldOfferRemoteLinkRepair(state))
20429
+ return BASE_RECOVERY_DECKS;
20430
+ return [BASE_RECOVERY_DECKS[0], { label: "repair link", detail: "backfill/prepare remote project-root link" }, ...BASE_RECOVERY_DECKS.slice(1)];
20431
+ }
20432
+ function recoveryRows(state, selected, reason) {
20433
+ const items = fleetRecoveryItems(state);
19678
20434
  return [
19679
20435
  ...reason ? [line(reason, { fg: RIG_UI.yellow }), line("", { fg: RIG_UI.ink3 })] : [],
19680
20436
  line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
19681
- ...RECOVERY_DECKS.map((deck, index) => selectableDeckRow({ ...deck, index, active: selected === index }, FLEET_RECOVERY_ITEMS[index]))
20437
+ ...fleetRecoveryDecks(state).map((deck, index) => selectableDeckRow({ ...deck, index, active: selected === index }, items[index]))
19682
20438
  ];
19683
20439
  }
19684
- function emptyRows(query, total, selected) {
20440
+ function emptyRows(state, query, total, selected) {
19685
20441
  if (!query.trim() && total === 0)
19686
- return recoveryRows(selected, "No runs yet.");
20442
+ return recoveryRows(state, selected, "No runs yet.");
19687
20443
  if (query.trim() && total > 0) {
19688
20444
  return [
19689
- ...recoveryRows(selected, `No matching runs for ${JSON.stringify(query)} (${total} total).`)
20445
+ ...recoveryRows(state, selected, `No matching runs for ${JSON.stringify(query)} (${total} total).`)
19690
20446
  ];
19691
20447
  }
19692
- return recoveryRows(selected, "No runs are visible with this filter.");
20448
+ return recoveryRows(state, selected, "No runs are visible with this filter.");
19693
20449
  }
19694
20450
  function degradedRows(state, selected) {
19695
20451
  if (!state.error)
19696
20452
  return [];
19697
- return recoveryRows(selected, state.error.message);
20453
+ return recoveryRows(state, selected, state.error.message);
19698
20454
  }
19699
20455
  function renderFleetScene(state, layout) {
19700
20456
  const width = panelWidth4(layout);
@@ -19704,7 +20460,7 @@ function renderFleetScene(state, layout) {
19704
20460
  const runs = sortRuns(filterRunsForSearch(allRuns, query), spec);
19705
20461
  const selectedRun = typeof state.data.selectedRunId === "string" && runs.some((run) => run.runId === state.data.selectedRunId) ? state.data.selectedRunId : runs[0]?.runId;
19706
20462
  const selectedIndex = Math.max(0, runs.findIndex((run) => run.runId === selectedRun));
19707
- const recoveryIndex = Math.max(0, Math.min(3, state.selection.index));
20463
+ const recoveryIndex = Math.max(0, Math.min(fleetRecoveryItems(state).length - 1, state.selection.index));
19708
20464
  const optimistic = optimisticStatuses(state);
19709
20465
  const updatedAgo = updatedAgoLabel(state);
19710
20466
  const panelTop = 0;
@@ -19736,7 +20492,7 @@ function renderFleetScene(state, layout) {
19736
20492
  ...runs.map((run, index) => runRow(contentWidth, run, index === selectedIndex, optimistic.get(run.runId)))
19737
20493
  ] : state.error ? degradedRows(state, recoveryIndex) : loading ? loadingRows("runs", state.tick) : [
19738
20494
  ...query.trim() ? [line(searchSummary("runs", query, runs.length, allRuns.length), { fg: RIG_UI.ink4 }), line("", { fg: RIG_UI.ink3 })] : [],
19739
- ...emptyRows(query, allRuns.length, recoveryIndex)
20495
+ ...emptyRows(state, query, allRuns.length, recoveryIndex)
19740
20496
  ]
19741
20497
  ];
19742
20498
  return makeSceneFrame({
@@ -19771,18 +20527,19 @@ function renderFleetScene(state, layout) {
19771
20527
  live: true
19772
20528
  });
19773
20529
  }
19774
- var FLEET_RECOVERY_ITEMS, COL2, GUTTER3 = " ", RUN_COLUMNS, RECOVERY_DECKS;
20530
+ var BASE_FLEET_RECOVERY_ITEMS, FLEET_REPAIR_LINK_ITEM, COL2, GUTTER3 = " ", RUN_COLUMNS, BASE_RECOVERY_DECKS;
19775
20531
  var init_fleet2 = __esm(() => {
19776
20532
  init_scene();
19777
20533
  init_selectable();
19778
20534
  init_fleet_stats();
20535
+ init_remote_link();
19779
20536
  init_theme2();
19780
- FLEET_RECOVERY_ITEMS = [
20537
+ BASE_FLEET_RECOVERY_ITEMS = [
19781
20538
  { id: "tasks", label: "tasks", intent: { scene: "tasks", argv: ["tasks"], action: { kind: "refresh", label: "Open tasks" } }, message: "open tasks and dispatch work" },
19782
- { id: "repair", label: "repair", intent: { scene: "command", argv: ["init", "--repair"], action: { kind: "command-run", label: "Repair project link" } }, message: "repair project/server/auth link" },
19783
20539
  { id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Open server controls" } }, message: "server controls" },
19784
20540
  { id: "doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "diagnose project" }
19785
20541
  ];
20542
+ FLEET_REPAIR_LINK_ITEM = { id: "repair-link", label: "repair link", intent: { scene: "server", argv: ["server", "repair-link"], action: { kind: "remote-link-repair", label: "Repair remote link", payload: { returnScene: "fleet" } } }, message: "backfill or prepare remote project link" };
19786
20543
  COL2 = {
19787
20544
  glyph: 1,
19788
20545
  runId: 8,
@@ -19796,9 +20553,8 @@ var init_fleet2 = __esm(() => {
19796
20553
  { key: "title", label: "task/title", hint: "by task title" },
19797
20554
  { key: "age", label: "age", hint: "by recency" }
19798
20555
  ];
19799
- RECOVERY_DECKS = [
20556
+ BASE_RECOVERY_DECKS = [
19800
20557
  { label: "tasks", detail: "open tasks and dispatch work" },
19801
- { label: "repair", detail: "repair project/server/auth link" },
19802
20558
  { label: "server", detail: "open server controls" },
19803
20559
  { label: "doctor", detail: "diagnose project" }
19804
20560
  ];
@@ -20378,11 +21134,11 @@ function renderInitScene(state, layout) {
20378
21134
  const allDone = completed === SETUP_STEPS.length;
20379
21135
  const nextStep = SETUP_STEPS.find((step) => !step.done(signals));
20380
21136
  const selected = Math.max(0, state.selection.index);
20381
- const primary = nextStep ? selectableDeckRow({ label: "next", detail: `${nextStep.cta} \u2014 recommended`, active: selected === 0 }, { ...nextStep.item, id: "init-primary" }) : selectableDeckRow({ label: "verify", detail: "Setup complete \u2014 run doctor to confirm", active: selected === 0 }, { id: "init-primary", label: "verify", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "verify setup end-to-end" });
21137
+ const primary = nextStep ? selectableDeckRow({ label: "next", detail: `${nextStep.cta} \u2014 recommended`, active: selected === 0 }, { ...nextStep.item, id: "init-primary" }) : selectableDeckRow({ label: "verify", detail: "Init complete \u2014 run doctor to confirm", active: selected === 0 }, { id: "init-primary", label: "verify", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "verify setup end-to-end" });
20382
21138
  const secondaryRows = SECONDARY_ACTIONS.map(({ label, detail, item }, index) => selectableDeckRow({ label, detail, active: selected === index + 1 }, item));
20383
21139
  const progress = allDone ? "all set" : `${completed} of ${SETUP_STEPS.length} complete`;
20384
21140
  const panelLines = [
20385
- line(allDone ? "SETUP \xB7 ready to go" : "SETUP \xB7 let's get you running", { fg: allDone ? RIG_UI.limeDim : RIG_UI.ink, bold: true }),
21141
+ line(allDone ? "INIT \xB7 ready to go" : "INIT \xB7 let's get you running", { fg: allDone ? RIG_UI.limeDim : RIG_UI.ink, bold: true }),
20386
21142
  line(`progress ${progress}`, { fg: allDone ? RIG_UI.limeDim : RIG_UI.ink2 }),
20387
21143
  blank(),
20388
21144
  line("CHECKLIST", { fg: RIG_UI.ink3, bold: true }),
@@ -20394,11 +21150,11 @@ function renderInitScene(state, layout) {
20394
21150
  line("OTHER PATHS", { fg: RIG_UI.ink3, bold: true }),
20395
21151
  ...secondaryRows,
20396
21152
  blank(),
20397
- line("enter/click runs the highlighted action \xB7 esc goes back \xB7 tab switches sections", { fg: RIG_UI.ink4 })
21153
+ line("enter/click runs the highlighted native action \xB7 esc goes back \xB7 tab switches sections", { fg: RIG_UI.ink4 })
20398
21154
  ];
20399
21155
  return makeSceneFrame({
20400
21156
  scene: "init",
20401
- title: "Setup",
21157
+ title: "Init",
20402
21158
  lines: [],
20403
21159
  panels: [{
20404
21160
  id: "init-setup",
@@ -20411,11 +21167,11 @@ function renderInitScene(state, layout) {
20411
21167
  opacity: 0.98,
20412
21168
  border: false,
20413
21169
  chrome: "ad-terminal",
20414
- headerText: "rig setup \xB7 guided onboarding",
21170
+ headerText: "rig init \xB7 guided onboarding",
20415
21171
  paddingX: 5,
20416
21172
  paddingY: 2
20417
21173
  }],
20418
- footer: { message: allDone ? "setup complete" : "setup in progress" },
21174
+ footer: { message: allDone ? "init complete" : "init in progress" },
20419
21175
  live: !allDone || state.status === "action"
20420
21176
  });
20421
21177
  }
@@ -20424,19 +21180,19 @@ var init_init3 = __esm(() => {
20424
21180
  init_scene();
20425
21181
  init_selectable();
20426
21182
  init_theme2();
20427
- STEP_CONFIG_RUN = { id: "step-config", label: "run setup wizard", intent: { scene: "command", argv: ["init"], action: { kind: "command-run", label: "Run setup wizard" } }, message: "full interactive setup wizard" };
21183
+ STEP_CONFIG_RUN = { id: "step-config", label: "run init", intent: { scene: "init", argv: ["init", "--yes"], action: { kind: "init-start", label: "Run init" } }, message: "initialize without embedded CLI prompts" };
20428
21184
  STEP_SERVER = { id: "step-server", label: "select server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Choose server" } }, message: "select local or remote server" };
20429
- STEP_AUTH = { id: "step-auth", label: "connect GitHub", intent: { scene: "command", argv: ["github", "auth", "import-gh"], action: { kind: "command-run", label: "Connect GitHub" } }, message: "import GitHub auth into the selected server" };
20430
- STEP_TASKS = { id: "step-tasks", label: "pick a task source", intent: { scene: "command", argv: ["init", "--repair"], action: { kind: "command-run", label: "Configure task source" } }, message: "configure or repair the task source" };
21185
+ STEP_AUTH = { id: "step-auth", label: "connect GitHub", intent: { scene: "server", argv: ["github", "auth", "status"], action: { kind: "refresh", label: "Open GitHub auth" } }, message: "open native server/auth controls" };
21186
+ STEP_TASKS = { id: "step-tasks", label: "repair task source", intent: { scene: "init", argv: ["init", "--repair", "--yes"], action: { kind: "init-start", label: "Repair task source" } }, message: "repair generated task-source config without embedded CLI prompts" };
20431
21187
  SETUP_STEPS = [
20432
- { id: "config", label: "Project config", detail: "rig.config + private state", done: (s) => s.configured, cta: "Run setup wizard", item: STEP_CONFIG_RUN },
21188
+ { id: "config", label: "Project config", detail: "rig.config + private state", done: (s) => s.configured, cta: "Run init", item: STEP_CONFIG_RUN },
20433
21189
  { id: "server", label: "Server target", detail: "local or remote, reachable", done: (s) => s.serverReachable, cta: "Choose a server", item: STEP_SERVER },
20434
21190
  { id: "auth", label: "GitHub auth", detail: "signed in on the selected server", done: (s) => s.authSignedIn, cta: "Connect GitHub", item: STEP_AUTH },
20435
21191
  { id: "tasks", label: "Task source", detail: "where work comes from", done: (s) => s.taskSourceReady, cta: "Pick a task source", item: STEP_TASKS }
20436
21192
  ];
20437
21193
  SECONDARY_ACTIONS = [
20438
- { label: "repair", detail: "reconfigure generated config and private state", item: { id: "repair", label: "repair", intent: { scene: "command", argv: ["init", "--repair"], action: { kind: "command-run", label: "Reconfigure project" } }, message: "verify or rewrite generated config" } },
20439
- { label: "demo", detail: "seed an offline demo task source to explore", item: { id: "demo", label: "demo", intent: { scene: "command", argv: ["init", "--demo", "--repair"], action: { kind: "command-run", label: "Seed demo project" } }, message: "offline demo project" } },
21194
+ { label: "repair", detail: "reconfigure generated config and private state", item: { id: "repair", label: "repair", intent: { scene: "init", argv: ["init", "--repair", "--yes"], action: { kind: "init-start", label: "Reconfigure project" } }, message: "verify or rewrite generated config natively" } },
21195
+ { label: "demo", detail: "seed an offline demo task source to explore", item: { id: "demo", label: "demo", intent: { scene: "init", argv: ["init", "--demo", "--repair", "--yes"], action: { kind: "init-start", label: "Seed demo project" } }, message: "offline demo project without embedded CLI prompts" } },
20440
21196
  { label: "doctor", detail: "diagnose what is still missing", item: { id: "doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "verify setup" } }
20441
21197
  ];
20442
21198
  });
@@ -21735,6 +22491,30 @@ var init_run_detail2 = __esm(() => {
21735
22491
  });
21736
22492
 
21737
22493
  // packages/cli/src/app-opentui/scenes/server.ts
22494
+ function remoteAliasActions(server) {
22495
+ const aliases = (server?.aliases ?? []).filter((alias) => alias.kind === "remote");
22496
+ return aliases.map((alias) => ({
22497
+ detail: `${alias.selected ? "selected" : "select"} ${alias.baseUrl ?? "remote endpoint"}`,
22498
+ item: {
22499
+ id: `remote-alias:${alias.alias}`,
22500
+ label: alias.selected ? `\u2713 ${alias.alias}` : `use ${alias.alias}`,
22501
+ intent: { scene: "server", argv: ["server", "use", alias.alias], action: { kind: "server-use-remote", label: `Use ${alias.alias}`, payload: { alias: alias.alias } } },
22502
+ message: `select ${alias.alias}`
22503
+ }
22504
+ }));
22505
+ }
22506
+ function serverActions(state) {
22507
+ const server = serverState(state);
22508
+ const remoteRows = remoteAliasActions(server);
22509
+ let actions = remoteRows.length > 0 ? [...SERVER_ACTIONS.slice(0, 3), ...remoteRows, ...SERVER_ACTIONS.slice(3)] : [...SERVER_ACTIONS];
22510
+ if (!shouldOfferRemoteLinkRepair(state))
22511
+ return actions;
22512
+ const repair = { detail: "backfill or prepare the selected remote checkout/root link", item: { id: "repair-link", label: "repair link", intent: { scene: "server", argv: ["server", "repair-link"], action: { kind: "remote-link-repair", label: "Repair remote link" } }, message: "prepare remote checkout link" } };
22513
+ const doctorIndex = actions.findIndex((entry) => entry.item.id === "doctor");
22514
+ if (doctorIndex < 0)
22515
+ return [...actions, repair];
22516
+ return [...actions.slice(0, doctorIndex), repair, ...actions.slice(doctorIndex)];
22517
+ }
21738
22518
  function cleanVisible(value, fallback = "unknown") {
21739
22519
  return value?.trim() || fallback;
21740
22520
  }
@@ -21759,31 +22539,37 @@ function selectedIndex(state, count) {
21759
22539
  function statusRows2(server) {
21760
22540
  const auth = server?.auth;
21761
22541
  const signedIn = auth && (auth.signedIn === true || auth.authenticated === true || auth.status === "authenticated") ? "authenticated" : "auth unknown";
22542
+ const link = server?.remoteProjectLink;
22543
+ const linkText = link ? link.ok ? `ready \xB7 ${link.serverProjectRoot ?? "linked"}` : `${link.status ?? "needs repair"} \xB7 ${link.hint ?? link.message ?? "run repair link"}` : server?.kind === "remote" ? "not checked" : "not required";
22544
+ const selectedLinkSummary = link?.ok ? " \xB7 root link ready" : server?.kind === "remote" && link ? ` \xB7 root link ${link.status ?? "needs repair"}` : "";
21762
22545
  return [
21763
- line(`selected ${cleanVisible(server?.label, "not checked")}`, { fg: RIG_UI.ink2 }),
22546
+ line(`selected ${cleanVisible(server?.label, "not checked")}${selectedLinkSummary}`, { fg: RIG_UI.ink2 }),
21764
22547
  line(`target ${targetLabel(server)}`, { fg: RIG_UI.ink3 }),
21765
22548
  line(`mode ${server?.kind ?? "local or remote"}`, { fg: RIG_UI.ink3 }),
21766
22549
  line(`health ${server ? server.reachable ? "reachable" : "unreachable" : "check pending"}`, { fg: server?.reachable ? RIG_UI.limeDim : server ? RIG_UI.red : RIG_UI.yellow }),
21767
22550
  line(`github ${signedIn}`, { fg: signedIn === "authenticated" ? RIG_UI.limeDim : RIG_UI.ink4 }),
22551
+ line(`root link ${linkText}`, { fg: link?.ok ? RIG_UI.limeDim : server?.kind === "remote" ? RIG_UI.yellow : RIG_UI.ink4 }),
22552
+ ...server?.aliases && server.aliases.length > 0 ? [line(`aliases ${server.aliases.map((alias) => `${alias.selected ? "*" : ""}${alias.alias}${alias.kind === "remote" && alias.baseUrl ? `=${alias.baseUrl}` : ""}`).join(", ")}`, { fg: RIG_UI.ink4 })] : [],
21768
22553
  ...server?.error ? [line(`attention ${server.error}`, { fg: RIG_UI.yellow })] : []
21769
22554
  ];
21770
22555
  }
21771
22556
  function renderServerScene(state, layout) {
21772
22557
  const server = serverState(state);
21773
- const selected = selectedIndex(state, 10);
22558
+ const actions = serverActions(state);
22559
+ const selected = selectedIndex(state, actions.length);
21774
22560
  const loading = state.status === "loading" && !server;
21775
22561
  const errorText = state.error?.message ?? lastRefreshError(state);
21776
22562
  const panelLines = [
21777
22563
  ...errorText ? errorBanner(errorText, "select refresh or doctor below to retry") : [],
21778
22564
  line("server controls", { fg: RIG_UI.ink3 }),
21779
22565
  blank(),
21780
- line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
21781
- ...SERVER_ACTIONS.map(({ detail, item }, index) => selectableDeckRow({ label: item.label, detail, index, active: selected === index }, item)),
21782
- blank(),
21783
22566
  line("STATUS", { fg: RIG_UI.ink3, bold: true }),
21784
22567
  ...loading ? loadingRows("server status", state.tick) : statusRows2(server),
21785
22568
  blank(),
21786
- line("enter/click runs action \xB7 command rows stay inside OpenTUI PTY", { fg: RIG_UI.ink4 })
22569
+ line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
22570
+ ...actions.map(({ detail, item }, index) => selectableDeckRow({ label: item.label, detail, index, active: selected === index }, item)),
22571
+ blank(),
22572
+ line("enter/click runs native actions \xB7 add/use remote prompts stay in OpenTUI", { fg: RIG_UI.ink4 })
21787
22573
  ];
21788
22574
  return makeSceneFrame({
21789
22575
  scene: "server",
@@ -21813,14 +22599,15 @@ var init_server3 = __esm(() => {
21813
22599
  init_scene();
21814
22600
  init_selectable();
21815
22601
  init_theme2();
22602
+ init_remote_link();
21816
22603
  SERVER_ACTIONS = [
21817
22604
  { detail: "probe selected server, project root, and GitHub auth", item: { id: "refresh", label: "refresh", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Refresh server" } }, message: "refresh selected server" } },
21818
- { detail: "select local server for this repo", item: { id: "local", label: "use local", intent: { scene: "command", argv: ["server", "use", "local"], action: { kind: "command-run", label: "Use local server" } }, message: "switch this repo to local" } },
21819
- { detail: "choose/switch to a saved remote server alias", item: { id: "remote", label: "use remote", intent: { scene: "command", argv: ["server", "use"], action: { kind: "command-run", label: "Choose remote server" } }, message: "choose a saved remote alias" } },
21820
- { detail: "show saved local/remote aliases", item: { id: "list", label: "list servers", intent: { scene: "command", argv: ["server", "list"], action: { kind: "command-run", label: "List saved servers" } }, message: "list saved server aliases" } },
21821
- { detail: "open server add command workspace", item: { id: "add", label: "add remote", intent: { scene: "command", argv: ["server", "add"], action: { kind: "command-run", label: "Add remote server" } }, message: "open server add workspace" } },
22605
+ { detail: "select local server for this repo", item: { id: "local", label: "use local", intent: { scene: "server", argv: ["server", "use", "local"], action: { kind: "server-use-local", label: "Use local server" } }, message: "switch this repo to local" } },
22606
+ { detail: "type a saved remote alias to select", item: { id: "remote", label: "use remote", prompt: { label: "Use remote alias", scene: "server", argv: ["server", "use"], intentKind: "server-use-remote", payloadBase: {}, payloadKey: "alias" }, message: "choose a saved remote alias" } },
22607
+ { detail: "show saved local/remote aliases in the status panel", item: { id: "list", label: "list servers", intent: { scene: "server", argv: ["server", "list"], action: { kind: "refresh", label: "List servers" } }, message: "list saved server aliases" } },
22608
+ { detail: "add and select a remote: enter <alias> <https-url>", item: { id: "add", label: "add remote", prompt: { label: "Add remote", scene: "server", argv: ["server", "add"], intentKind: "server-add-remote", payloadBase: {}, payloadKey: "remote", appendValueToArgv: true }, message: "add a remote server alias" } },
21822
22609
  { detail: "spawn the local rig-server detached and return here", item: { id: "start", label: "start local", intent: { scene: "server", argv: ["server", "start"], action: { kind: "refresh", payload: { startLocal: true }, label: "Start local server" } }, message: "start local server detached" } },
21823
- { detail: "import gh token into the selected server namespace", item: { id: "auth", label: "import auth", intent: { scene: "command", argv: ["github", "auth", "import-gh"], action: { kind: "command-run", label: "Import GitHub auth" } }, message: "import gh auth to selected server" } },
22610
+ { detail: "import gh token into the selected server namespace", item: { id: "auth", label: "import auth", intent: { scene: "server", argv: ["github", "auth", "import-gh"], action: { kind: "github-auth-import", label: "Import GitHub auth" } }, message: "import gh auth to selected server" } },
21824
22611
  { detail: "diagnose server/auth/project linkage", item: { id: "doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "verify server/auth" } },
21825
22612
  { detail: "verify selected server can list task source", item: { id: "tasks", label: "tasks", intent: { scene: "tasks", argv: ["task", "list"], action: { kind: "refresh", label: "Open tasks" } }, message: "verify task source" } },
21826
22613
  { detail: "verify selected server can list recent runs", item: { id: "runs", label: "runs", intent: { scene: "fleet", argv: ["run", "status"], action: { kind: "refresh", label: "Open runs" } }, message: "open recent runs" } }
@@ -21972,27 +22759,38 @@ function taskRow(width, task, active, optimistic) {
21972
22759
  const item = taskSelectableItem(task);
21973
22760
  return item ? withSelectable(row, item) : row;
21974
22761
  }
21975
- function recoveryRows2(selected, reason) {
22762
+ function taskRecoveryItems(state) {
22763
+ if (!shouldOfferRemoteLinkRepair(state))
22764
+ return BASE_TASK_RECOVERY_ITEMS;
22765
+ return [BASE_TASK_RECOVERY_ITEMS[0], BASE_TASK_RECOVERY_ITEMS[1], TASK_REPAIR_LINK_ITEM, ...BASE_TASK_RECOVERY_ITEMS.slice(2)];
22766
+ }
22767
+ function taskRecoveryDecks(state) {
22768
+ if (!shouldOfferRemoteLinkRepair(state))
22769
+ return BASE_RECOVERY_DECKS2;
22770
+ return [BASE_RECOVERY_DECKS2[0], BASE_RECOVERY_DECKS2[1], { label: "repair link", detail: "backfill/prepare remote project-root link" }, ...BASE_RECOVERY_DECKS2.slice(2)];
22771
+ }
22772
+ function recoveryRows2(state, selected, reason) {
22773
+ const items = taskRecoveryItems(state);
21976
22774
  return [
21977
22775
  ...reason ? [line(reason, { fg: RIG_UI.yellow }), line("", { fg: RIG_UI.ink3 })] : [],
21978
22776
  line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
21979
- ...RECOVERY_DECKS2.map((deck, index) => selectableDeckRow({ ...deck, index, active: selected === index }, TASK_RECOVERY_ITEMS[index]))
22777
+ ...taskRecoveryDecks(state).map((deck, index) => selectableDeckRow({ ...deck, index, active: selected === index }, items[index]))
21980
22778
  ];
21981
22779
  }
21982
- function emptyRows2(query, total, selected) {
22780
+ function emptyRows2(state, query, total, selected) {
21983
22781
  if (!query.trim() && total === 0)
21984
- return recoveryRows2(selected, "No tasks loaded.");
22782
+ return recoveryRows2(state, selected, "No tasks loaded.");
21985
22783
  if (query.trim() && total > 0) {
21986
22784
  return [
21987
- ...recoveryRows2(selected, `No matching tasks for ${JSON.stringify(query)} (${total} total).`)
22785
+ ...recoveryRows2(state, selected, `No matching tasks for ${JSON.stringify(query)} (${total} total).`)
21988
22786
  ];
21989
22787
  }
21990
- return recoveryRows2(selected, "No tasks are visible with this filter.");
22788
+ return recoveryRows2(state, selected, "No tasks are visible with this filter.");
21991
22789
  }
21992
22790
  function degradedRows2(state, selected) {
21993
22791
  if (!state.error)
21994
22792
  return [];
21995
- return recoveryRows2(selected, state.error.message);
22793
+ return recoveryRows2(state, selected, state.error.message);
21996
22794
  }
21997
22795
  function rawString(raw, key) {
21998
22796
  const value = raw?.[key];
@@ -22016,7 +22814,10 @@ function renderTaskDetail(state, taskId3, layout) {
22016
22814
  const activeRunId = task?.activeRun?.runId?.trim();
22017
22815
  const width = panelWidth13(layout);
22018
22816
  const selected = Math.max(0, state.selection.index);
22817
+ const errorText = state.error?.message ?? lastRefreshError(state);
22818
+ const offerRepairLink = shouldOfferRemoteLinkRepair(state, errorText);
22019
22819
  const headerLines = [
22820
+ ...errorText ? errorBanner(errorText, offerRepairLink ? "select repair link below to prepare the remote checkout" : "retry dispatch after resolving the issue") : [],
22020
22821
  line(`${glyph2} ${title}`, { fg: color, bold: true }),
22021
22822
  line(`task ${taskId3} \xB7 ${status}`, { fg: RIG_UI.ink3 }),
22022
22823
  ...task && task.labels.length > 0 ? [line(`labels: ${task.labels.join(", ")}`, { fg: RIG_UI.ink4 })] : [],
@@ -22028,8 +22829,16 @@ function renderTaskDetail(state, taskId3, layout) {
22028
22829
  const actionRows2 = [];
22029
22830
  actionRows2.push(selectableDeckRow({ label: "back", detail: "return to the task list", index, active: selected === index }, { id: "task-detail-back", label: "back", data: { taskDetailId: undefined }, message: "back to task list" }));
22030
22831
  index += 1;
22031
- actionRows2.push(selectableDeckRow({ label: "dispatch", detail: "submit a Pi run for this task", index, active: selected === index }, { id: `task-detail-run:${taskId3}`, label: `dispatch ${taskId3}`, intent: { scene: "tasks", argv: ["run", taskId3], action: { kind: "task-run-id", payload: { task: taskId3 }, label: `Dispatching ${taskId3}` } }, message: `dispatch task ${taskId3}` }));
22032
- index += 1;
22832
+ if (offerRepairLink) {
22833
+ actionRows2.push(selectableDeckRow({ label: "repair link", detail: "prepare/backfill the selected remote project-root link", index, active: selected === index }, { id: `task-detail-repair-link:${taskId3}`, label: "repair link", intent: { scene: "server", argv: ["server", "repair-link"], action: { kind: "remote-link-repair", label: "Repair remote link", payload: { returnScene: "tasks", taskDetailId: taskId3 } } }, message: "prepare remote checkout link" }));
22834
+ index += 1;
22835
+ }
22836
+ if (offerRepairLink) {
22837
+ actionRows2.push(line(" dispatch blocked until the remote project link is repaired", { fg: RIG_UI.ink4 }));
22838
+ } else {
22839
+ actionRows2.push(selectableDeckRow({ label: "dispatch", detail: "submit a Pi run for this task", index, active: selected === index }, { id: `task-detail-run:${taskId3}`, label: `dispatch ${taskId3}`, intent: { scene: "tasks", argv: ["run", taskId3], action: { kind: "task-run-id", payload: { task: taskId3 }, label: `Dispatching ${taskId3}` } }, message: `dispatch task ${taskId3}` }));
22840
+ index += 1;
22841
+ }
22033
22842
  if (activeRunId) {
22034
22843
  actionRows2.push(selectableDeckRow({ label: "attach", detail: `attach the Pi console to run ${activeRunId.slice(0, 8)}`, index, active: selected === index }, { id: `task-detail-attach:${taskId3}`, label: `attach ${activeRunId.slice(0, 8)}`, intent: { scene: "handoff", argv: ["attach", activeRunId], action: { kind: "run-attach", payload: { runId: activeRunId }, label: `Attach Pi ${activeRunId.slice(0, 8)}` } }, message: `attach Pi to ${activeRunId.slice(0, 8)}` }));
22035
22844
  index += 1;
@@ -22088,7 +22897,7 @@ function renderTasksScene(state, layout) {
22088
22897
  const tasks = sortTasks2(filterTasksForSearch(allTasks, query), spec);
22089
22898
  const selected = typeof state.data.selectedTaskId === "string" && tasks.some((task) => task.id === state.data.selectedTaskId) ? state.data.selectedTaskId : tasks[0]?.id;
22090
22899
  const selectedIndex2 = Math.max(0, tasks.findIndex((task) => task.id === selected));
22091
- const recoveryIndex = Math.max(0, Math.min(4, state.selection.index));
22900
+ const recoveryIndex = Math.max(0, Math.min(taskRecoveryItems(state).length - 1, state.selection.index));
22092
22901
  const dispatching = dispatchingFor(state);
22093
22902
  const dispatchLine = dispatching ? ` \xB7 ${state.data.dispatchingRun?.runId ?? "new"} ${dispatching.status}` : "";
22094
22903
  const updatedAgo = updatedAgoLabel2(state);
@@ -22108,7 +22917,7 @@ function renderTasksScene(state, layout) {
22108
22917
  ...tasks.map((task, index) => taskRow(contentWidth, task, index === selectedIndex2, dispatching && dispatching.taskId === task.id ? dispatching.status : undefined))
22109
22918
  ] : state.error ? degradedRows2(state, recoveryIndex) : loading ? loadingRows("tasks", state.tick) : [
22110
22919
  ...query.trim() ? [line(searchSummary("tasks", query, tasks.length, allTasks.length), { fg: RIG_UI.ink4 }), line("", { fg: RIG_UI.ink3 })] : [],
22111
- ...emptyRows2(query, allTasks.length, recoveryIndex)
22920
+ ...emptyRows2(state, query, allTasks.length, recoveryIndex)
22112
22921
  ]
22113
22922
  ];
22114
22923
  return makeSceneFrame({
@@ -22142,18 +22951,19 @@ function renderTasksScene(state, layout) {
22142
22951
  live: true
22143
22952
  });
22144
22953
  }
22145
- var TASK_RECOVERY_ITEMS, COL3, GUTTER4 = " ", TASK_COLUMNS, RECOVERY_DECKS2;
22954
+ var BASE_TASK_RECOVERY_ITEMS, TASK_REPAIR_LINK_ITEM, COL3, GUTTER4 = " ", TASK_COLUMNS, BASE_RECOVERY_DECKS2;
22146
22955
  var init_tasks2 = __esm(() => {
22147
22956
  init_scene();
22148
22957
  init_selectable();
22958
+ init_remote_link();
22149
22959
  init_theme2();
22150
- TASK_RECOVERY_ITEMS = [
22960
+ BASE_TASK_RECOVERY_ITEMS = [
22151
22961
  { id: "refresh", label: "refresh", intent: { scene: "tasks", argv: ["tasks"], action: { kind: "refresh", label: "Refresh tasks" } }, message: "refresh task source" },
22152
- { id: "next", label: "next", intent: { scene: "command", argv: ["task", "run", "--next"], action: { kind: "command-run", label: "Run next task" } }, message: "dispatch next runnable task" },
22153
- { id: "repair", label: "repair", intent: { scene: "command", argv: ["init", "--repair"], action: { kind: "command-run", label: "Repair task source" } }, message: "repair project/task source" },
22962
+ { id: "next", label: "next", intent: { scene: "tasks", argv: ["task", "run", "--next"], action: { kind: "task-run-next", label: "Dispatch next task" } }, message: "dispatch next runnable task" },
22154
22963
  { id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Open server controls" } }, message: "server controls" },
22155
22964
  { id: "doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "diagnose task source" }
22156
22965
  ];
22966
+ TASK_REPAIR_LINK_ITEM = { id: "repair-link", label: "repair link", intent: { scene: "server", argv: ["server", "repair-link"], action: { kind: "remote-link-repair", label: "Repair remote link", payload: { returnScene: "tasks" } } }, message: "backfill or prepare remote project link" };
22157
22967
  COL3 = {
22158
22968
  glyph: 1,
22159
22969
  id: 11,
@@ -22168,10 +22978,9 @@ var init_tasks2 = __esm(() => {
22168
22978
  { key: "assignee", label: "assignee", hint: "by assignee" },
22169
22979
  { key: "title", label: "title", hint: "by title" }
22170
22980
  ];
22171
- RECOVERY_DECKS2 = [
22981
+ BASE_RECOVERY_DECKS2 = [
22172
22982
  { label: "refresh", detail: "reload task source" },
22173
22983
  { label: "next", detail: "dispatch next runnable task" },
22174
- { label: "repair", detail: "repair project/task source link" },
22175
22984
  { label: "server", detail: "open server controls" },
22176
22985
  { label: "doctor", detail: "diagnose task source" }
22177
22986
  ];
@@ -22505,6 +23314,29 @@ function createDomainAdapter() {
22505
23314
  else
22506
23315
  await loadInitFacts(ctx);
22507
23316
  return true;
23317
+ case "server-use-local":
23318
+ await useLocalServerFromApp(ctx);
23319
+ return true;
23320
+ case "server-use-remote":
23321
+ await useRemoteServerFromApp(ctx, payloadString8(intent, "alias") ?? intent.argv[2]);
23322
+ return true;
23323
+ case "server-add-remote":
23324
+ await addRemoteServerFromApp(ctx, intent.argv);
23325
+ return true;
23326
+ case "github-auth-import":
23327
+ await importGitHubAuthFromGhApp(ctx);
23328
+ return true;
23329
+ case "remote-link-repair": {
23330
+ await repairRemoteProjectRootLinkFromApp(ctx);
23331
+ const returnScene = payloadString8(intent, "returnScene");
23332
+ if (returnScene && returnScene !== intent.scene) {
23333
+ ctx.emit({ type: "scene.change", scene: returnScene, intent: { scene: returnScene, argv: intent.argv, action: { kind: "refresh", label: `Refresh ${returnScene}`, payload: intent.action.payload } } });
23334
+ await refreshScene(ctx, returnScene, { scene: returnScene, argv: intent.argv, action: { kind: "refresh", label: `Refresh ${returnScene}`, payload: intent.action.payload } });
23335
+ } else {
23336
+ await refreshServerStatus(ctx);
23337
+ }
23338
+ return true;
23339
+ }
22508
23340
  case "doctor-run":
22509
23341
  await runDoctorChecksForApp(ctx);
22510
23342
  return true;
@@ -22607,7 +23439,9 @@ import {
22607
23439
  RigInboxInputsOutput,
22608
23440
  RigRunListOutput,
22609
23441
  RigRunShowOutput,
23442
+ RigServerRepairLinkOutput,
22610
23443
  RigServerStatusOutput,
23444
+ RigServerUseOutput,
22611
23445
  RigStatsOutput,
22612
23446
  RigTaskListOutput,
22613
23447
  RigTaskShowOutput
@@ -22643,7 +23477,9 @@ var init__json_output = __esm(() => {
22643
23477
  "task show": RigTaskShowOutput,
22644
23478
  "run list": RigRunListOutput,
22645
23479
  "run show": RigRunShowOutput,
23480
+ "server use": RigServerUseOutput,
22646
23481
  "server status": RigServerStatusOutput,
23482
+ "server repair-link": RigServerRepairLinkOutput,
22647
23483
  "inbox approvals": RigInboxApprovalsOutput,
22648
23484
  "inbox inputs": RigInboxInputsOutput,
22649
23485
  "doctor check": RigDoctorCheckOutput,
@@ -22974,16 +23810,31 @@ function taskViewIntent(argv, command = "", rest = [], raw) {
22974
23810
  const { payload, label } = taskViewPayload(command, rest);
22975
23811
  return intent("tasks", argv, "refresh", payload, label, raw);
22976
23812
  }
22977
- function routeServer(argv, command, raw, checkingLabel) {
22978
- if (!command || command === "status") {
23813
+ function routeServer(argv, command, rest, raw, checkingLabel) {
23814
+ if (!command || command === "status" || command === "list") {
22979
23815
  return intent("server", argv, "refresh", undefined, command ? checkingLabel : "Loading server", raw);
22980
23816
  }
23817
+ if (command === "use") {
23818
+ const alias = firstNonOption(rest);
23819
+ if (alias === "local")
23820
+ return intent("server", argv, "server-use-local", undefined, "Use local server", raw);
23821
+ if (alias)
23822
+ return intent("server", argv, "server-use-remote", { alias }, `Use ${alias}`, raw);
23823
+ return intent("server", argv, "refresh", undefined, "Select a remote alias in Server", raw);
23824
+ }
23825
+ if (command === "add")
23826
+ return intent("server", argv, "server-add-remote", { argv: [...argv] }, "Add remote server", raw);
23827
+ if (command === "repair-link")
23828
+ return intent("server", argv, "remote-link-repair", undefined, "Repair remote link", raw);
22981
23829
  return commandRunIntent(argv, raw);
22982
23830
  }
22983
23831
  function routeGithub(argv, command, rest, raw) {
22984
23832
  if (command === "auth" && (rest[0]?.toLowerCase() ?? "status") === "status" && rest.length <= 1) {
22985
23833
  return intent("server", argv, "refresh", undefined, "Checking GitHub auth", raw);
22986
23834
  }
23835
+ if (command === "auth" && rest[0]?.toLowerCase() === "import-gh" && rest.length <= 1) {
23836
+ return intent("server", argv, "github-auth-import", undefined, "Import GitHub auth", raw);
23837
+ }
22987
23838
  return commandRunIntent(argv, raw);
22988
23839
  }
22989
23840
  function routeDoctor(argv, command, raw) {
@@ -23032,11 +23883,16 @@ function intentFromArgv(argv) {
23032
23883
  return commandRunIntent(argv);
23033
23884
  }
23034
23885
  if (normalizedGroup === "server")
23035
- return routeServer(argv, normalizedCommand, undefined, "Checking server");
23886
+ return routeServer(argv, normalizedCommand, rest, undefined, "Checking server");
23036
23887
  if (normalizedGroup === "github")
23037
23888
  return routeGithub(argv, normalizedCommand, rest);
23038
- if (normalizedGroup === "init")
23039
- return commandRunIntent(argv);
23889
+ if (normalizedGroup === "init") {
23890
+ if (!normalizedCommand)
23891
+ return intent("init", argv, "refresh", undefined, "Open init");
23892
+ if (rest.includes("--yes") || normalizedCommand === "--yes")
23893
+ return intent("init", argv, "init-start", undefined, "Run init");
23894
+ return intent("init", argv, "refresh", undefined, "Open init");
23895
+ }
23040
23896
  if (normalizedGroup === "doctor")
23041
23897
  return routeDoctor(argv, normalizedCommand);
23042
23898
  if (normalizedGroup === "inbox")
@@ -23099,12 +23955,17 @@ function intentFromTypeBar(value) {
23099
23955
  return commandRunIntent(parts, value);
23100
23956
  return intent("tasks", parts, "refresh", undefined, "Select a task, then press Enter; or use run next", value);
23101
23957
  }
23102
- if (first === "init")
23103
- return commandRunIntent(parts, value);
23958
+ if (first === "init") {
23959
+ if (!second)
23960
+ return intent("init", parts, "refresh", undefined, "Open init", value);
23961
+ if (parts.includes("--yes"))
23962
+ return intent("init", parts, "init-start", undefined, "Run init", value);
23963
+ return intent("init", parts, "refresh", undefined, "Open init", value);
23964
+ }
23104
23965
  if (first === "doctor")
23105
23966
  return routeDoctor(parts, second, value);
23106
23967
  if (first === "server")
23107
- return routeServer(parts, second, value, "Loading server");
23968
+ return routeServer(parts, second, rest, value, "Loading server");
23108
23969
  if (first === "github")
23109
23970
  return routeGithub(parts, second, rest, value);
23110
23971
  if (first === "inbox")
@@ -23547,12 +24408,6 @@ var init_autocomplete = __esm(() => {
23547
24408
  NAV_VERBS = ["runs", "tasks", "inbox", "server", "doctor", "help", "init", "main"];
23548
24409
  });
23549
24410
 
23550
- // packages/cli/src/app-opentui/pi-pty-host.ts
23551
- function getActivePiHost() {
23552
- return null;
23553
- }
23554
- function stopActivePiHost(_reason) {}
23555
-
23556
24411
  // packages/cli/src/app-opentui/keymap.ts
23557
24412
  function clearTypeBar(context, message2) {
23558
24413
  const typeBar = context.getTypeBar();
@@ -23561,22 +24416,15 @@ function clearTypeBar(context, message2) {
23561
24416
  context.emitTypeBarPatch({ value: "", mode: "nav", prompt: undefined, message: message2 });
23562
24417
  }
23563
24418
  function handleEmbeddedTerminalKey(context, key) {
23564
- const state = context.getState();
23565
- const piHost = getActivePiHost();
23566
- const commandHost = getActiveCommandHost();
23567
- const host = state.scene === "handoff" ? piHost : state.scene === "command" ? commandHost : null;
24419
+ if (context.getState().scene !== "command")
24420
+ return false;
24421
+ const host = getActiveCommandHost();
23568
24422
  if (!host)
23569
24423
  return false;
23570
24424
  if (key.ctrl && key.name === "]") {
23571
- if (state.scene === "handoff") {
23572
- stopActivePiHost("operator detach");
23573
- clearTypeBar(context, "detached from Pi");
23574
- context.runAppAction("Opening runs", () => context.runtime.runIntent({ scene: "fleet", argv: ["runs"], action: { kind: "refresh", label: "Opening runs" } }));
23575
- } else {
23576
- stopActiveCommandHost("operator detach");
23577
- clearTypeBar(context, "closed command");
23578
- context.runAppAction("Project menu", () => context.runtime.runIntent({ scene: "main", argv: ["main"], action: { kind: "none", label: "Project menu" } }));
23579
- }
24425
+ stopActiveCommandHost("operator detach");
24426
+ clearTypeBar(context, "closed command");
24427
+ context.runAppAction("Project menu", () => context.runtime.runIntent({ scene: "main", argv: ["main"], action: { kind: "none", label: "Project menu" } }));
23580
24428
  return true;
23581
24429
  }
23582
24430
  const sequence = key.raw || key.sequence;
@@ -25085,10 +25933,14 @@ var init_preloader = __esm(() => {
25085
25933
  });
25086
25934
 
25087
25935
  // packages/cli/src/app-opentui/scenes/error.ts
25936
+ function errorActions(state) {
25937
+ return shouldOfferRemoteLinkRepair(state, state.error?.message) ? [...ERROR_ACTIONS, ERROR_REPAIR_LINK_ACTION] : ERROR_ACTIONS;
25938
+ }
25088
25939
  function renderErrorScene(state) {
25089
25940
  const message2 = state.error?.message ?? "Unknown app error";
25090
25941
  const hint = state.error?.hint ?? "Use the actions below to recover.";
25091
- const selected = Math.max(0, Math.min(3, state.selection.index));
25942
+ const actions = errorActions(state);
25943
+ const selected = Math.max(0, Math.min(actions.length - 1, state.selection.index));
25092
25944
  return makeSceneFrame({
25093
25945
  scene: "error",
25094
25946
  title: "Error",
@@ -25106,7 +25958,7 @@ function renderErrorScene(state) {
25106
25958
  ...hint ? [line(hint, { fg: RIG_UI.yellow })] : [],
25107
25959
  line("", { fg: RIG_UI.ink3 }),
25108
25960
  line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
25109
- ...ERROR_ACTIONS.map(({ detail, item }, index) => selectableDeckRow({ label: item.label, detail, index, active: selected === index }, item))
25961
+ ...actions.map(({ detail, item }, index) => selectableDeckRow({ label: item.label, detail, index, active: selected === index }, item))
25110
25962
  ],
25111
25963
  backgroundColor: RIG_UI.panel,
25112
25964
  backgroundAlpha: 184,
@@ -25123,18 +25975,19 @@ function renderErrorScene(state) {
25123
25975
  live: true
25124
25976
  });
25125
25977
  }
25126
- var ERROR_ACTIONS;
25978
+ var ERROR_ACTIONS, ERROR_REPAIR_LINK_ACTION;
25127
25979
  var init_error = __esm(() => {
25128
25980
  init_drone();
25129
25981
  init_theme2();
25982
+ init_remote_link();
25130
25983
  init_scene();
25131
25984
  init_selectable();
25132
25985
  ERROR_ACTIONS = [
25133
25986
  { detail: "return to dashboard", item: { id: "main", label: "main", intent: { scene: "main", argv: [], action: { kind: "refresh", label: "Back to dashboard" } }, message: "back to dashboard" } },
25134
25987
  { detail: "run diagnostics", item: { id: "doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "run diagnostics" } },
25135
- { detail: "open server controls", item: { id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Open server controls" } }, message: "open server controls" } },
25136
- { detail: "repair project/server/auth link", item: { id: "repair", label: "repair", intent: { scene: "command", argv: ["init", "--repair"], action: { kind: "command-run", label: "Repair project link" } }, message: "repair project link" } }
25988
+ { detail: "open server controls", item: { id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Open server controls" } }, message: "open server controls" } }
25137
25989
  ];
25990
+ ERROR_REPAIR_LINK_ACTION = { detail: "backfill or prepare selected remote project-root link", item: { id: "repair-link", label: "repair link", intent: { scene: "server", argv: ["server", "repair-link"], action: { kind: "remote-link-repair", label: "Repair remote link", payload: { returnScene: "server" } } }, message: "repair remote project link" } };
25138
25991
  });
25139
25992
 
25140
25993
  // packages/cli/src/app-opentui/scenes/help.ts
@@ -25309,7 +26162,7 @@ function configuredMenuRows(selectedIndex2) {
25309
26162
  }));
25310
26163
  }
25311
26164
  function onboardingRows(selectedIndex2) {
25312
- const primary = selectableDeckRow({ label: "setup", detail: "Start guided setup \u2014 recommended", active: selectedIndex2 === 0 }, ONBOARD_PRIMARY);
26165
+ const primary = selectableDeckRow({ label: "init", detail: "Start guided init \u2014 recommended", active: selectedIndex2 === 0 }, ONBOARD_PRIMARY);
25313
26166
  const secondary = ONBOARD_SECONDARY.map(([label, detail, item], index) => ({
25314
26167
  ...deckRow({ label, detail, index: index + 1, active: selectedIndex2 === index + 1, activateOnClick: true }),
25315
26168
  selectable: item
@@ -25369,7 +26222,7 @@ function renderMainScene(state, layout) {
25369
26222
  statusLine(state)
25370
26223
  ] : [
25371
26224
  line("WELCOME", { fg: RIG_UI.ink, bold: true }),
25372
- line("This project isn't set up yet. One guided flow gets you running.", { fg: RIG_UI.ink2 }),
26225
+ line("This project isn't initialized yet. One native init flow gets you running.", { fg: RIG_UI.ink2 }),
25373
26226
  blank(),
25374
26227
  line("GET STARTED", { fg: RIG_UI.ink, bold: true }),
25375
26228
  ...onboardingRows(actionSelected),
@@ -25410,7 +26263,7 @@ var init_main = __esm(() => {
25410
26263
  init_theme2();
25411
26264
  init_fleet_stats();
25412
26265
  CONFIGURED_MENU = [
25413
- ["init", "setup, reconfigure, repair project state", { id: "init", label: "init", intent: { scene: "command", argv: ["init"], action: { kind: "command-run", label: "Run init" } }, message: "run setup/reconfigure flow" }],
26266
+ ["init", "guided setup, reconfigure, repair project state", { id: "init", label: "init", intent: { scene: "init", argv: ["init"], action: { kind: "refresh", label: "Open init" } }, message: "open native init flow" }],
25414
26267
  ["server", "local/remote target, auth, linkage", { id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Checking server" } }, message: "check selected server" }],
25415
26268
  ["github", "auth, selected repo, stored token", { id: "github", label: "github", intent: { scene: "server", argv: ["github", "auth", "status"], action: { kind: "refresh", label: "Checking GitHub auth" } }, message: "check GitHub auth" }],
25416
26269
  ["tasks", "browse, filter, dispatch, attach active work", { id: "tasks", label: "tasks", intent: { scene: "tasks", argv: ["tasks"], action: { kind: "refresh", label: "Opening tasks" } }, message: "selected tasks" }],
@@ -25429,10 +26282,10 @@ var init_main = __esm(() => {
25429
26282
  ["help", "all actions, shortcuts, mouse controls", { id: "help", label: "help", intent: { scene: "help", argv: ["help"], action: { kind: "none", label: "Opening help" } }, message: "selected help" }]
25430
26283
  ];
25431
26284
  ONBOARD_PRIMARY = {
25432
- id: "onboard-setup",
25433
- label: "Start guided setup",
25434
- intent: { scene: "init", argv: ["init"], action: { kind: "refresh", label: "Setup" } },
25435
- message: "open the guided setup checklist"
26285
+ id: "onboard-init",
26286
+ label: "Start guided init",
26287
+ intent: { scene: "init", argv: ["init"], action: { kind: "refresh", label: "Init" } },
26288
+ message: "open the native init checklist"
25436
26289
  };
25437
26290
  ONBOARD_SECONDARY = [
25438
26291
  ["server", "select a local or remote target", { id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Checking server" } }, message: "check selected server" }],
@@ -25445,6 +26298,12 @@ var init_main = __esm(() => {
25445
26298
  TILE_TASKS = { id: "tile-tasks", label: "tasks", intent: { scene: "tasks", argv: ["tasks"], action: { kind: "refresh", label: "Opening tasks" } }, message: "ready tasks \u2014 dispatch work" };
25446
26299
  });
25447
26300
 
26301
+ // packages/cli/src/app-opentui/pi-pty-host.ts
26302
+ function getActivePiHost() {
26303
+ return null;
26304
+ }
26305
+ function stopActivePiHost(_reason) {}
26306
+
25448
26307
  // packages/cli/src/app-opentui/runtime.ts
25449
26308
  var exports_runtime = {};
25450
26309
  __export(exports_runtime, {
@@ -25511,7 +26370,7 @@ function entryIntentForScene(scene) {
25511
26370
  case "doctor":
25512
26371
  return { scene, argv: ["doctor"], action: { kind: "doctor-run", label: "Doctor" } };
25513
26372
  case "init":
25514
- return { scene, argv: ["init"], action: { kind: "refresh", label: "Setup" } };
26373
+ return { scene, argv: ["init"], action: { kind: "refresh", label: "Init" } };
25515
26374
  case "run-detail":
25516
26375
  return { scene, argv: ["run", "status"], action: { kind: "refresh", label: "Run" } };
25517
26376
  case "pi":
@@ -25600,7 +26459,7 @@ function sceneSectionLabel(scene) {
25600
26459
  handoff: "pi console",
25601
26460
  server: "server",
25602
26461
  doctor: "doctor",
25603
- init: "setup",
26462
+ init: "init",
25604
26463
  pi: "pi",
25605
26464
  plugin: "plugins",
25606
26465
  repo: "repo",
@@ -26090,7 +26949,7 @@ async function launchRigOpenTuiApp(options) {
26090
26949
  }
26091
26950
  for (const adapter of options.adapters ?? [])
26092
26951
  await adapter.start?.(runtime);
26093
- liveEvents = await connectRigServerEvents(options.projectRoot, {
26952
+ connectRigServerEvents(options.projectRoot, {
26094
26953
  onSnapshotInvalidated: () => refreshCurrentDataScene(),
26095
26954
  onRunLogAppended: () => {
26096
26955
  const scene = store.getState().scene;
@@ -26098,11 +26957,17 @@ async function launchRigOpenTuiApp(options) {
26098
26957
  refreshCurrentDataScene();
26099
26958
  },
26100
26959
  onStatus: (status) => store.patch({ footer: { live: liveLinkLabel(status) } })
26101
- }).catch(() => null);
26102
- if (!liveEvents)
26103
- store.patch({ footer: { live: liveLinkLabel("disconnected") } });
26104
- if (liveEvents)
26960
+ }).then((subscription) => {
26961
+ if (destroyed) {
26962
+ subscription.close();
26963
+ return;
26964
+ }
26965
+ liveEvents = subscription;
26105
26966
  resources.add(() => liveEvents?.close());
26967
+ }).catch(() => {
26968
+ if (!destroyed)
26969
+ store.patch({ footer: { live: liveLinkLabel("disconnected") } });
26970
+ });
26106
26971
  await runtime.runIntent(store.getState().intent);
26107
26972
  } catch (error) {
26108
26973
  const normalized = normalizeError(error);
@@ -26295,7 +27160,7 @@ var init_runtime = __esm(() => {
26295
27160
  { id: "tasks", label: "Tasks", scene: "tasks", argv: ["tasks"], enterLabel: "Tasks", member: ["tasks"] },
26296
27161
  { id: "inbox", label: "Inbox", scene: "inbox", argv: ["inbox"], enterLabel: "Inbox", member: ["inbox"], badge: inboxPendingCount },
26297
27162
  { id: "stats", label: "Stats", scene: "family", argv: ["stats"], enterLabel: "Stats", member: ["family"] },
26298
- { id: "setup", label: "Setup", scene: "init", argv: ["init"], enterLabel: "Setup", member: ["init", "doctor", "server", "pi", "plugin", "repo", "workspace"] },
27163
+ { id: "init", label: "Init", scene: "init", argv: ["init"], enterLabel: "Init", member: ["init", "doctor", "server", "pi", "plugin", "repo", "workspace"] },
26299
27164
  { id: "help", label: "Help", scene: "help", argv: ["help"], enterLabel: "Help", member: ["help"] }
26300
27165
  ];
26301
27166
  SCENE_DATA_KEY = {
@@ -26659,7 +27524,7 @@ var init_nav = __esm(() => {
26659
27524
  { id: "tasks", label: "Tasks", scene: "tasks", argv: ["tasks"], enterLabel: "Tasks", member: ["tasks"] },
26660
27525
  { id: "inbox", label: "Inbox", scene: "inbox", argv: ["inbox"], enterLabel: "Inbox", member: ["inbox"], badge: inboxPendingCount2 },
26661
27526
  { id: "stats", label: "Stats", scene: "family", argv: ["stats"], enterLabel: "Stats", member: ["family"] },
26662
- { id: "setup", label: "Setup", scene: "init", argv: ["init"], enterLabel: "Setup", member: ["init", "doctor", "server", "pi", "plugin", "repo", "workspace"] },
27527
+ { id: "init", label: "Init", scene: "init", argv: ["init"], enterLabel: "Init", member: ["init", "doctor", "server", "pi", "plugin", "repo", "workspace"] },
26663
27528
  { id: "help", label: "Help", scene: "help", argv: ["help"], enterLabel: "Help", member: ["help"] }
26664
27529
  ];
26665
27530
  });
@@ -26925,7 +27790,7 @@ async function launchRigReactApp(options) {
26925
27790
  }
26926
27791
  for (const adapter of options.adapters ?? [])
26927
27792
  await adapter.start?.(runtime);
26928
- liveEvents = await connectRigServerEvents(options.projectRoot, {
27793
+ connectRigServerEvents(options.projectRoot, {
26929
27794
  onSnapshotInvalidated: () => refreshCurrentDataScene(),
26930
27795
  onRunLogAppended: () => {
26931
27796
  const scene = store.getState().scene;
@@ -26933,9 +27798,11 @@ async function launchRigReactApp(options) {
26933
27798
  refreshCurrentDataScene();
26934
27799
  },
26935
27800
  onStatus: (status) => store.patch({ footer: { live: liveLinkLabel(status) } })
26936
- }).catch(() => null);
26937
- if (!liveEvents)
27801
+ }).then((subscription) => {
27802
+ liveEvents = subscription;
27803
+ }).catch(() => {
26938
27804
  store.patch({ footer: { live: liveLinkLabel("disconnected") } });
27805
+ });
26939
27806
  await runtime.runIntent(store.getState().intent);
26940
27807
  } catch (error) {
26941
27808
  const n = normalizeError(error);
@@ -27136,7 +28003,7 @@ var init_bootstrap = __esm(() => {
27136
28003
  // packages/cli/bin/rig.ts
27137
28004
  init_launch_routing();
27138
28005
  import { existsSync as existsSync25, readFileSync as readFileSync16 } from "fs";
27139
- import { dirname as dirname5, resolve as resolve32 } from "path";
28006
+ import { dirname as dirname6, resolve as resolve32 } from "path";
27140
28007
  function packageVersionFrom(startDir) {
27141
28008
  let current = resolve32(startDir);
27142
28009
  while (true) {
@@ -27148,7 +28015,7 @@ function packageVersionFrom(startDir) {
27148
28015
  return pkg.version;
27149
28016
  } catch {}
27150
28017
  }
27151
- const parent = dirname5(current);
28018
+ const parent = dirname6(current);
27152
28019
  if (parent === current)
27153
28020
  return null;
27154
28021
  current = parent;