@h-rig/cli 0.0.6-alpha.1 → 0.0.6-alpha.11

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.
@@ -2465,6 +2465,8 @@ function resolveSelectedConnection(projectRoot, options = {}) {
2465
2465
 
2466
2466
  // packages/cli/src/commands/_server-client.ts
2467
2467
  import { spawnSync } from "child_process";
2468
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
2469
+ import { resolve as resolve8 } from "path";
2468
2470
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
2469
2471
  var cachedGitHubBearerToken;
2470
2472
  function cleanToken(value) {
@@ -2474,9 +2476,25 @@ function cleanToken(value) {
2474
2476
  function setGitHubBearerTokenForCurrentProcess(token) {
2475
2477
  cachedGitHubBearerToken = cleanToken(token ?? undefined);
2476
2478
  }
2477
- function readGitHubBearerTokenForRemote() {
2479
+ function readPrivateRemoteSessionToken(projectRoot) {
2480
+ const path = resolve8(projectRoot, ".rig", "state", "github-auth.json");
2481
+ if (!existsSync4(path))
2482
+ return null;
2483
+ try {
2484
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
2485
+ return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
2486
+ } catch {
2487
+ return null;
2488
+ }
2489
+ }
2490
+ function readGitHubBearerTokenForRemote(projectRoot) {
2478
2491
  if (cachedGitHubBearerToken !== undefined)
2479
2492
  return cachedGitHubBearerToken;
2493
+ const privateSession = readPrivateRemoteSessionToken(projectRoot);
2494
+ if (privateSession) {
2495
+ cachedGitHubBearerToken = privateSession;
2496
+ return cachedGitHubBearerToken;
2497
+ }
2480
2498
  const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
2481
2499
  if (envToken) {
2482
2500
  cachedGitHubBearerToken = envToken;
@@ -2496,7 +2514,7 @@ async function ensureServerForCli(projectRoot) {
2496
2514
  if (selected?.connection.kind === "remote") {
2497
2515
  return {
2498
2516
  baseUrl: selected.connection.baseUrl,
2499
- authToken: readGitHubBearerTokenForRemote(),
2517
+ authToken: readGitHubBearerTokenForRemote(projectRoot),
2500
2518
  connectionKind: "remote"
2501
2519
  };
2502
2520
  }
@@ -2603,7 +2621,7 @@ async function postGitHubTokenViaServer(context, token, options = {}) {
2603
2621
  const payload = await requestServerJson(context, "/api/github/auth/token", {
2604
2622
  method: "POST",
2605
2623
  headers: { "content-type": "application/json" },
2606
- body: JSON.stringify({ token, selectedRepo: options.selectedRepo })
2624
+ body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
2607
2625
  });
2608
2626
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2609
2627
  }
@@ -2624,18 +2642,38 @@ async function registerProjectViaServer(context, input) {
2624
2642
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2625
2643
  }
2626
2644
  function sleep(ms) {
2627
- return new Promise((resolve8) => setTimeout(resolve8, ms));
2645
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
2646
+ }
2647
+ function isRetryableProjectRootSwitchError(error) {
2648
+ if (!(error instanceof Error))
2649
+ return false;
2650
+ const message = error.message.toLowerCase();
2651
+ return message.includes("rig server request failed (401): auth-required") || message.includes("rig server request failed (401): github-token-required") || message.includes("rig server request failed (502)") || message.includes("rig server request failed (503)") || message.includes("bad gateway") || message.includes("fetch failed") || message.includes("econnrefused") || message.includes("connection refused");
2628
2652
  }
2629
2653
  async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
2630
- const switched = await requestServerJson(context, "/api/server/project-root", {
2631
- method: "POST",
2632
- headers: { "content-type": "application/json" },
2633
- body: JSON.stringify({ projectRoot })
2634
- });
2635
2654
  const timeoutMs = options.timeoutMs ?? 30000;
2636
2655
  const pollMs = options.pollMs ?? 1000;
2637
2656
  const deadline = Date.now() + timeoutMs;
2638
2657
  let lastError;
2658
+ let switched = null;
2659
+ while (Date.now() < deadline) {
2660
+ try {
2661
+ switched = await requestServerJson(context, "/api/server/project-root", {
2662
+ method: "POST",
2663
+ headers: { "content-type": "application/json" },
2664
+ body: JSON.stringify({ projectRoot })
2665
+ });
2666
+ break;
2667
+ } catch (error) {
2668
+ lastError = error;
2669
+ if (!isRetryableProjectRootSwitchError(error))
2670
+ throw error;
2671
+ await sleep(pollMs);
2672
+ }
2673
+ }
2674
+ if (!switched) {
2675
+ throw new CliError2(`Rig server did not accept project-root switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no response")}).`, 1);
2676
+ }
2639
2677
  while (Date.now() < deadline) {
2640
2678
  try {
2641
2679
  const status = await requestServerJson(context, "/api/server/status");
@@ -2715,9 +2753,9 @@ async function submitTaskRunViaServer(context, input) {
2715
2753
  }
2716
2754
 
2717
2755
  // packages/cli/src/commands/_pi-install.ts
2718
- import { existsSync as existsSync4, readFileSync as readFileSync3, rmSync as rmSync3 } from "fs";
2756
+ import { existsSync as existsSync5, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
2719
2757
  import { homedir as homedir3 } from "os";
2720
- import { resolve as resolve8 } from "path";
2758
+ import { resolve as resolve9 } from "path";
2721
2759
  var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
2722
2760
  var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
2723
2761
  export { default } from '@rig/pi-rig';
@@ -2732,11 +2770,11 @@ async function defaultCommandRunner(command, options = {}) {
2732
2770
  return { exitCode, stdout, stderr };
2733
2771
  }
2734
2772
  function resolvePiRigExtensionPath(homeDir) {
2735
- return resolve8(homeDir, ".pi", "agent", "extensions", "pi-rig");
2773
+ return resolve9(homeDir, ".pi", "agent", "extensions", "pi-rig");
2736
2774
  }
2737
- function resolvePiRigPackageSource(projectRoot, exists = existsSync4) {
2738
- const localPackage = resolve8(projectRoot, "packages", "pi-rig");
2739
- if (exists(resolve8(localPackage, "package.json")))
2775
+ function resolvePiRigPackageSource(projectRoot, exists = existsSync5) {
2776
+ const localPackage = resolve9(projectRoot, "packages", "pi-rig");
2777
+ if (exists(resolve9(localPackage, "package.json")))
2740
2778
  return localPackage;
2741
2779
  return `npm:${PI_RIG_PACKAGE_NAME}`;
2742
2780
  }
@@ -2787,13 +2825,13 @@ async function ensurePiBinaryAvailable(input) {
2787
2825
  ...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
2788
2826
  };
2789
2827
  }
2790
- function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync4) {
2828
+ function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync5) {
2791
2829
  const extensionPath = resolvePiRigExtensionPath(homeDir);
2792
- const indexPath = resolve8(extensionPath, "index.ts");
2830
+ const indexPath = resolve9(extensionPath, "index.ts");
2793
2831
  if (!exists(indexPath))
2794
2832
  return;
2795
2833
  try {
2796
- const content = readFileSync3(indexPath, "utf8");
2834
+ const content = readFileSync4(indexPath, "utf8");
2797
2835
  if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
2798
2836
  rmSync3(extensionPath, { recursive: true, force: true });
2799
2837
  }
@@ -2809,13 +2847,13 @@ async function checkPiRigInstall(input = {}) {
2809
2847
  piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
2810
2848
  };
2811
2849
  }
2812
- const exists = input.exists ?? existsSync4;
2850
+ const exists = input.exists ?? existsSync5;
2813
2851
  const runner = input.commandRunner ?? defaultCommandRunner;
2814
2852
  const piResult = await safeRun(runner, ["pi", "--version"]);
2815
2853
  const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
2816
2854
  const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
2817
2855
  ${piListResult.stderr}`);
2818
- const legacyBridge = exists(resolve8(extensionPath, "index.ts"));
2856
+ const legacyBridge = exists(resolve9(extensionPath, "index.ts"));
2819
2857
  const hasPiRig = listedPiRig;
2820
2858
  return {
2821
2859
  extensionPath,
@@ -3153,7 +3191,7 @@ async function executeQueue(context, args) {
3153
3191
  }
3154
3192
 
3155
3193
  // packages/cli/src/commands/agent.ts
3156
- import { resolve as resolve10 } from "path";
3194
+ import { resolve as resolve11 } from "path";
3157
3195
  import {
3158
3196
  agentId,
3159
3197
  cleanupAgentRuntime,
@@ -3163,8 +3201,8 @@ import {
3163
3201
  } from "@rig/runtime/control-plane/runtime/isolation";
3164
3202
 
3165
3203
  // packages/cli/src/commands/_authority-runs.ts
3166
- import { existsSync as existsSync5 } from "fs";
3167
- import { resolve as resolve9 } from "path";
3204
+ import { existsSync as existsSync6 } from "fs";
3205
+ import { resolve as resolve10 } from "path";
3168
3206
  import {
3169
3207
  readAuthorityRun,
3170
3208
  readJsonlFile as readJsonlFile2,
@@ -3186,8 +3224,8 @@ function normalizeRuntimeAdapter(value) {
3186
3224
  return "claude-code";
3187
3225
  }
3188
3226
  function readLatestBeadRecord(projectRoot, taskId) {
3189
- const issuesPath = resolve9(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3190
- if (!existsSync5(issuesPath)) {
3227
+ const issuesPath = resolve10(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3228
+ if (!existsSync6(issuesPath)) {
3191
3229
  return null;
3192
3230
  }
3193
3231
  let latest = null;
@@ -3222,6 +3260,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3222
3260
  const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
3223
3261
  const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
3224
3262
  const next = {
3263
+ ...existing ?? {},
3225
3264
  runId: input.runId,
3226
3265
  projectRoot,
3227
3266
  workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
@@ -3254,7 +3293,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3254
3293
  } else if ("errorText" in next) {
3255
3294
  delete next.errorText;
3256
3295
  }
3257
- writeJsonFile3(resolve9(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3296
+ writeJsonFile3(resolve10(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3258
3297
  return next;
3259
3298
  }
3260
3299
 
@@ -3375,10 +3414,10 @@ async function executeAgent(context, args) {
3375
3414
  status: "running",
3376
3415
  startedAt: createdAt,
3377
3416
  worktreePath: runtime.workspaceDir,
3378
- artifactRoot: resolve10(runtime.workspaceDir, "artifacts", taskId),
3417
+ artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3379
3418
  logRoot: runtime.logsDir,
3380
- sessionPath: resolve10(runtime.sessionDir, "session.json"),
3381
- sessionLogPath: resolve10(runtime.logsDir, "agent-stdout.log"),
3419
+ sessionPath: resolve11(runtime.sessionDir, "session.json"),
3420
+ sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3382
3421
  pid: process.pid
3383
3422
  });
3384
3423
  const result = await runInAgentRuntime({
@@ -3398,10 +3437,10 @@ async function executeAgent(context, args) {
3398
3437
  startedAt: createdAt,
3399
3438
  completedAt: failedAt,
3400
3439
  worktreePath: runtime.workspaceDir,
3401
- artifactRoot: resolve10(runtime.workspaceDir, "artifacts", taskId),
3440
+ artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3402
3441
  logRoot: runtime.logsDir,
3403
- sessionPath: resolve10(runtime.sessionDir, "session.json"),
3404
- sessionLogPath: resolve10(runtime.logsDir, "agent-stdout.log"),
3442
+ sessionPath: resolve11(runtime.sessionDir, "session.json"),
3443
+ sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3405
3444
  pid: process.pid,
3406
3445
  errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
3407
3446
  });
@@ -3418,10 +3457,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3418
3457
  startedAt: createdAt,
3419
3458
  completedAt,
3420
3459
  worktreePath: runtime.workspaceDir,
3421
- artifactRoot: resolve10(runtime.workspaceDir, "artifacts", taskId),
3460
+ artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3422
3461
  logRoot: runtime.logsDir,
3423
- sessionPath: resolve10(runtime.sessionDir, "session.json"),
3424
- sessionLogPath: resolve10(runtime.logsDir, "agent-stdout.log"),
3462
+ sessionPath: resolve11(runtime.sessionDir, "session.json"),
3463
+ sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3425
3464
  pid: process.pid
3426
3465
  });
3427
3466
  return {
@@ -3495,7 +3534,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3495
3534
  import {
3496
3535
  chmodSync,
3497
3536
  copyFileSync as copyFileSync2,
3498
- existsSync as existsSync6,
3537
+ existsSync as existsSync7,
3499
3538
  mkdirSync as mkdirSync5,
3500
3539
  readdirSync,
3501
3540
  readlinkSync,
@@ -3505,7 +3544,7 @@ import {
3505
3544
  unlinkSync
3506
3545
  } from "fs";
3507
3546
  import { homedir as homedir4 } from "os";
3508
- import { resolve as resolve11 } from "path";
3547
+ import { resolve as resolve12 } from "path";
3509
3548
  import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
3510
3549
  import {
3511
3550
  computeRuntimeImageFingerprint,
@@ -3524,13 +3563,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
3524
3563
 
3525
3564
  // packages/cli/src/commands/dist.ts
3526
3565
  function collectRigValidatorBuildTargets(input) {
3527
- const validatorsRoot = resolve11(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3528
- if (!existsSync6(validatorsRoot))
3566
+ const validatorsRoot = resolve12(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3567
+ if (!existsSync7(validatorsRoot))
3529
3568
  return [];
3530
3569
  const targets = [];
3531
3570
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3532
3571
  for (const category of categories) {
3533
- const categoryDir = resolve11(validatorsRoot, category.name);
3572
+ const categoryDir = resolve12(validatorsRoot, category.name);
3534
3573
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3535
3574
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3536
3575
  continue;
@@ -3539,7 +3578,7 @@ function collectRigValidatorBuildTargets(input) {
3539
3578
  continue;
3540
3579
  targets.push({
3541
3580
  source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
3542
- dest: resolve11(input.imageDir, `bin/validators/${category.name}-${check}`),
3581
+ dest: resolve12(input.imageDir, `bin/validators/${category.name}-${check}`),
3543
3582
  cwd: input.hostProjectRoot
3544
3583
  });
3545
3584
  }
@@ -3548,16 +3587,16 @@ function collectRigValidatorBuildTargets(input) {
3548
3587
  }
3549
3588
  async function findLatestDistBinary(projectRoot) {
3550
3589
  const distRoot = resolveControlPlaneHostDistDir(projectRoot);
3551
- if (!existsSync6(distRoot)) {
3590
+ if (!existsSync7(distRoot)) {
3552
3591
  return null;
3553
3592
  }
3554
3593
  const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
3555
3594
  name: entry.name,
3556
- mtimeMs: statSync(resolve11(distRoot, entry.name)).mtimeMs
3595
+ mtimeMs: statSync(resolve12(distRoot, entry.name)).mtimeMs
3557
3596
  })).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
3558
3597
  for (const { name } of entries) {
3559
- const candidate = resolve11(distRoot, name, "bin", "rig");
3560
- if (existsSync6(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3598
+ const candidate = resolve12(distRoot, name, "bin", "rig");
3599
+ if (existsSync7(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3561
3600
  return candidate;
3562
3601
  }
3563
3602
  }
@@ -3569,7 +3608,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
3569
3608
  async function runDistDoctor(projectRoot) {
3570
3609
  const bunPath = Bun.which("bun");
3571
3610
  const rigPath = Bun.which("rig");
3572
- const userBinDir = resolve11(homedir4(), ".local/bin");
3611
+ const userBinDir = resolve12(homedir4(), ".local/bin");
3573
3612
  const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
3574
3613
  let rigRunnable = false;
3575
3614
  if (rigPath) {
@@ -3617,15 +3656,15 @@ async function executeDist(context, args) {
3617
3656
  let source = await findLatestDistBinary(context.projectRoot);
3618
3657
  let buildDir = null;
3619
3658
  if (!source) {
3620
- buildDir = resolve11(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3659
+ buildDir = resolve12(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3621
3660
  await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
3622
- source = resolve11(buildDir, "bin", "rig");
3661
+ source = resolve12(buildDir, "bin", "rig");
3623
3662
  }
3624
- if (!existsSync6(source)) {
3663
+ if (!existsSync7(source)) {
3625
3664
  throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
3626
3665
  }
3627
- const installedPath = resolve11(installDir, "rig");
3628
- if (existsSync6(installedPath)) {
3666
+ const installedPath = resolve12(installDir, "rig");
3667
+ if (existsSync7(installedPath)) {
3629
3668
  unlinkSync(installedPath);
3630
3669
  }
3631
3670
  copyFileSync2(source, installedPath);
@@ -3667,22 +3706,22 @@ async function executeDist(context, args) {
3667
3706
  requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
3668
3707
  const fp = await computeRuntimeImageFingerprint(context.projectRoot);
3669
3708
  const currentId = computeRuntimeImageId(fp);
3670
- const imagesDir = resolve11(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3709
+ const imagesDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3671
3710
  mkdirSync5(imagesDir, { recursive: true });
3672
3711
  let pruned = 0;
3673
3712
  for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
3674
3713
  if (entry.isDirectory() && entry.name !== currentId) {
3675
- rmSync4(resolve11(imagesDir, entry.name), { recursive: true, force: true });
3714
+ rmSync4(resolve12(imagesDir, entry.name), { recursive: true, force: true });
3676
3715
  pruned++;
3677
3716
  }
3678
3717
  }
3679
3718
  if (pruned > 0 && context.outputMode === "text") {
3680
3719
  console.log(`Pruned ${pruned} stale image(s).`);
3681
3720
  }
3682
- const imageDir = resolve11(imagesDir, currentId);
3683
- mkdirSync5(resolve11(imageDir, "bin/hooks"), { recursive: true });
3684
- mkdirSync5(resolve11(imageDir, "bin/plugins"), { recursive: true });
3685
- mkdirSync5(resolve11(imageDir, "bin/validators"), { recursive: true });
3721
+ const imageDir = resolve12(imagesDir, currentId);
3722
+ mkdirSync5(resolve12(imageDir, "bin/hooks"), { recursive: true });
3723
+ mkdirSync5(resolve12(imageDir, "bin/plugins"), { recursive: true });
3724
+ mkdirSync5(resolve12(imageDir, "bin/validators"), { recursive: true });
3686
3725
  const hookNames = [
3687
3726
  "scope-guard",
3688
3727
  "import-guard",
@@ -3696,25 +3735,25 @@ async function executeDist(context, args) {
3696
3735
  ];
3697
3736
  const targets = [];
3698
3737
  const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || context.projectRoot;
3699
- targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve11(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
3700
- targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve11(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
3738
+ targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve12(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
3739
+ targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
3701
3740
  for (const hookName of hookNames) {
3702
3741
  const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
3703
- targets.push({ source: src, dest: resolve11(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
3704
- targets.push({ source: src, dest: resolve11(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
3742
+ targets.push({ source: src, dest: resolve12(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
3743
+ targets.push({ source: src, dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
3705
3744
  }
3706
- const pluginsDir = resolve11(context.projectRoot, "rig/plugins");
3707
- const binPluginsDir = resolve11(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
3708
- const validatorsRoot = resolve11(hostProjectRoot, "packages/runtime/src/control-plane/validators");
3709
- const binValidatorsDir = resolve11(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
3745
+ const pluginsDir = resolve12(context.projectRoot, "rig/plugins");
3746
+ const binPluginsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
3747
+ const validatorsRoot = resolve12(hostProjectRoot, "packages/runtime/src/control-plane/validators");
3748
+ const binValidatorsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
3710
3749
  mkdirSync5(binPluginsDir, { recursive: true });
3711
3750
  mkdirSync5(binValidatorsDir, { recursive: true });
3712
- if (existsSync6(pluginsDir)) {
3751
+ if (existsSync7(pluginsDir)) {
3713
3752
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3714
3753
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3715
3754
  if (!m)
3716
3755
  continue;
3717
- targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve11(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3756
+ targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve12(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3718
3757
  }
3719
3758
  }
3720
3759
  targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
@@ -3725,17 +3764,17 @@ async function executeDist(context, args) {
3725
3764
  const isValidator = dest.includes("/bin/validators/");
3726
3765
  await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
3727
3766
  }
3728
- if (existsSync6(pluginsDir)) {
3767
+ if (existsSync7(pluginsDir)) {
3729
3768
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3730
3769
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3731
3770
  if (!m)
3732
3771
  continue;
3733
3772
  const pluginName = m[1];
3734
- const imageBin = resolve11(imageDir, `bin/plugins/${pluginName}`);
3773
+ const imageBin = resolve12(imageDir, `bin/plugins/${pluginName}`);
3735
3774
  if (!pluginName)
3736
3775
  continue;
3737
- const symlinkPath = resolve11(binPluginsDir, pluginName);
3738
- if (existsSync6(imageBin)) {
3776
+ const symlinkPath = resolve12(binPluginsDir, pluginName);
3777
+ if (existsSync7(imageBin)) {
3739
3778
  try {
3740
3779
  unlinkSync(symlinkPath);
3741
3780
  } catch {}
@@ -3743,10 +3782,10 @@ async function executeDist(context, args) {
3743
3782
  }
3744
3783
  }
3745
3784
  }
3746
- if (existsSync6(validatorsRoot)) {
3785
+ if (existsSync7(validatorsRoot)) {
3747
3786
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3748
3787
  for (const category of categories) {
3749
- const categoryDir = resolve11(validatorsRoot, category.name);
3788
+ const categoryDir = resolve12(validatorsRoot, category.name);
3750
3789
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3751
3790
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3752
3791
  continue;
@@ -3754,9 +3793,9 @@ async function executeDist(context, args) {
3754
3793
  if (!check || check === "index" || check === "shared")
3755
3794
  continue;
3756
3795
  const validatorName = `${category.name}-${check}`;
3757
- const imageBin = resolve11(imageDir, `bin/validators/${validatorName}`);
3758
- const symlinkPath = resolve11(binValidatorsDir, validatorName);
3759
- if (existsSync6(imageBin)) {
3796
+ const imageBin = resolve12(imageDir, `bin/validators/${validatorName}`);
3797
+ const symlinkPath = resolve12(binValidatorsDir, validatorName);
3798
+ if (existsSync7(imageBin)) {
3760
3799
  try {
3761
3800
  unlinkSync(symlinkPath);
3762
3801
  } catch {}
@@ -3765,18 +3804,18 @@ async function executeDist(context, args) {
3765
3804
  }
3766
3805
  }
3767
3806
  }
3768
- const agentsDir = resolve11(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
3769
- if (existsSync6(agentsDir)) {
3807
+ const agentsDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
3808
+ if (existsSync7(agentsDir)) {
3770
3809
  let relinkCount = 0;
3771
3810
  for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
3772
3811
  if (!agentEntry.isDirectory())
3773
3812
  continue;
3774
- const agentBinDir = resolve11(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
3775
- if (!existsSync6(agentBinDir))
3813
+ const agentBinDir = resolve12(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
3814
+ if (!existsSync7(agentBinDir))
3776
3815
  continue;
3777
3816
  const walkDir = (dir) => {
3778
3817
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
3779
- const fullPath = resolve11(dir, entry.name);
3818
+ const fullPath = resolve12(dir, entry.name);
3780
3819
  if (entry.isDirectory()) {
3781
3820
  walkDir(fullPath);
3782
3821
  } else if (entry.isSymbolicLink()) {
@@ -3810,7 +3849,7 @@ async function executeDist(context, args) {
3810
3849
 
3811
3850
  // packages/cli/src/commands/inbox.ts
3812
3851
  import { writeFileSync as writeFileSync4 } from "fs";
3813
- import { resolve as resolve12 } from "path";
3852
+ import { resolve as resolve13 } from "path";
3814
3853
  import {
3815
3854
  listAuthorityRuns,
3816
3855
  readJsonlFile as readJsonlFile3,
@@ -3827,7 +3866,7 @@ async function executeInbox(context, args) {
3827
3866
  pending = task.rest;
3828
3867
  requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
3829
3868
  const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
3830
- const approvals = runs.flatMap((entry) => readJsonlFile3(resolve12(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
3869
+ const approvals = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
3831
3870
  runId: entry.runId,
3832
3871
  record
3833
3872
  })));
@@ -3855,7 +3894,7 @@ async function executeInbox(context, args) {
3855
3894
  if (decision.value !== "approve" && decision.value !== "reject") {
3856
3895
  throw new CliError2("decision must be approve or reject.");
3857
3896
  }
3858
- const approvalsPath = resolve12(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
3897
+ const approvalsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
3859
3898
  const approvals = readJsonlFile3(approvalsPath);
3860
3899
  const resolvedAt = new Date().toISOString();
3861
3900
  const next = approvals.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", decision: decision.value, note: note3.value ?? null, resolvedAt } : entry);
@@ -3872,7 +3911,7 @@ async function executeInbox(context, args) {
3872
3911
  pending = task.rest;
3873
3912
  requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
3874
3913
  const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
3875
- const requests = runs.flatMap((entry) => readJsonlFile3(resolve12(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
3914
+ const requests = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
3876
3915
  runId: entry.runId,
3877
3916
  record
3878
3917
  })));
@@ -3914,7 +3953,7 @@ async function executeInbox(context, args) {
3914
3953
  const [key, ...restValue] = entry.split("=");
3915
3954
  return [key, restValue.join("=")];
3916
3955
  }));
3917
- const requestsPath = resolve12(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
3956
+ const requestsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
3918
3957
  const requests = readJsonlFile3(requestsPath);
3919
3958
  const resolvedAt = new Date().toISOString();
3920
3959
  const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
@@ -3929,14 +3968,14 @@ async function executeInbox(context, args) {
3929
3968
  }
3930
3969
 
3931
3970
  // packages/cli/src/commands/init.ts
3932
- import { appendFileSync as appendFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
3971
+ import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
3933
3972
  import { spawnSync as spawnSync2 } from "child_process";
3934
- import { resolve as resolve15 } from "path";
3973
+ import { resolve as resolve16 } from "path";
3935
3974
  import { buildRigInitConfigSource } from "@rig/core";
3936
3975
 
3937
3976
  // packages/cli/src/commands/_snapshot-upload.ts
3938
3977
  import { mkdir, readdir, readFile, writeFile } from "fs/promises";
3939
- import { dirname as dirname2, resolve as resolve13, relative, sep } from "path";
3978
+ import { dirname as dirname2, resolve as resolve14, relative, sep } from "path";
3940
3979
  var SNAPSHOT_ARCHIVE_VERSION = 1;
3941
3980
  var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
3942
3981
  var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
@@ -3958,15 +3997,15 @@ function assertManifestPath(root, relativePath) {
3958
3997
  if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
3959
3998
  throw new Error(`Invalid snapshot path: ${relativePath}`);
3960
3999
  }
3961
- const resolved = resolve13(root, relativePath);
4000
+ const resolved = resolve14(root, relativePath);
3962
4001
  const relativeToRoot = relative(root, resolved);
3963
- if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve13(relativeToRoot) === resolved) {
4002
+ if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve14(relativeToRoot) === resolved) {
3964
4003
  throw new Error(`Snapshot path escapes project root: ${relativePath}`);
3965
4004
  }
3966
4005
  return resolved;
3967
4006
  }
3968
4007
  async function buildSnapshotUploadManifest(projectRoot, options = {}) {
3969
- const root = resolve13(projectRoot);
4008
+ const root = resolve14(projectRoot);
3970
4009
  const excludedDirectories = [...new Set([
3971
4010
  ...DEFAULT_EXCLUDED_DIRECTORIES,
3972
4011
  ...options.excludedDirectories ?? []
@@ -3978,7 +4017,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
3978
4017
  for (const entry of entries) {
3979
4018
  if (entry.isDirectory() && excludedSet.has(entry.name))
3980
4019
  continue;
3981
- const fullPath = resolve13(dir, entry.name);
4020
+ const fullPath = resolve14(dir, entry.name);
3982
4021
  if (entry.isDirectory()) {
3983
4022
  await visit(fullPath);
3984
4023
  continue;
@@ -4026,8 +4065,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
4026
4065
  }
4027
4066
 
4028
4067
  // packages/cli/src/commands/_doctor-checks.ts
4029
- import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
4030
- import { resolve as resolve14 } from "path";
4068
+ import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
4069
+ import { resolve as resolve15 } from "path";
4031
4070
  import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
4032
4071
  function check(id, label, status, detail, remediation) {
4033
4072
  return {
@@ -4067,11 +4106,11 @@ function repoSlugFromConfig(config) {
4067
4106
  function loadFallbackConfig(projectRoot) {
4068
4107
  const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
4069
4108
  for (const name of candidates) {
4070
- const path = resolve14(projectRoot, name);
4071
- if (!existsSync7(path))
4109
+ const path = resolve15(projectRoot, name);
4110
+ if (!existsSync8(path))
4072
4111
  continue;
4073
4112
  try {
4074
- const source = readFileSync4(path, "utf8");
4113
+ const source = readFileSync5(path, "utf8");
4075
4114
  if (name.endsWith(".json"))
4076
4115
  return JSON.parse(source);
4077
4116
  const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
@@ -4150,7 +4189,7 @@ async function runRigDoctorChecks(options) {
4150
4189
  checks.push(check("bun", `bun >= ${MIN_SUPPORTED_BUN_VERSION}`, isSupportedBunVersion(bunVersion) ? "pass" : "fail", `found ${bunVersion}`, `Install Bun ${MIN_SUPPORTED_BUN_VERSION} or newer.`), check("git", "git", which("git") ? "pass" : "fail", which("git") ?? undefined, "Install git and ensure it is on PATH."), check("jq", "jq", which("jq") ? "pass" : "warn", which("jq") ?? undefined, "Install jq (for example `brew install jq`)."));
4151
4190
  const loadedConfig = await loadConfig(projectRoot).catch(() => null);
4152
4191
  const config = loadedConfig ?? loadFallbackConfig(projectRoot);
4153
- const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync7(resolve14(projectRoot, name)));
4192
+ const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync8(resolve15(projectRoot, name)));
4154
4193
  checks.push(config ? check("config", "rig.config loadable", "pass") : check("config", "rig.config loadable", hasConfigFile ? "fail" : "fail", hasConfigFile ? "config file exists but failed to load" : "missing rig.config.ts/json", "Run `rig init` or fix the config error."));
4155
4194
  const taskSourceKind = config?.taskSource?.kind;
4156
4195
  checks.push(taskSourceKind ? check("task-source", "task source configured", "pass", taskSourceKind) : check("task-source", "task source configured", "fail", "missing taskSource", "Configure taskSource in rig.config.ts."));
@@ -4245,10 +4284,10 @@ function countDoctorFailures(checks) {
4245
4284
  }
4246
4285
 
4247
4286
  // packages/cli/src/commands/init.ts
4248
- var RIG_CONFIG_PACKAGE_VERSION = "0.0.6-alpha.1";
4287
+ var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
4249
4288
  var RIG_CONFIG_DEV_DEPENDENCIES = {
4250
- "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_VERSION}`,
4251
- "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_VERSION}`
4289
+ "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
4290
+ "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
4252
4291
  };
4253
4292
  function parseRepoSlugFromRemote(remoteUrl) {
4254
4293
  const trimmed = remoteUrl.trim();
@@ -4268,20 +4307,20 @@ function parseRepoSlug(value) {
4268
4307
  return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
4269
4308
  }
4270
4309
  function ensureRigPrivateDirs(projectRoot) {
4271
- const rigDir = resolve15(projectRoot, ".rig");
4272
- mkdirSync6(resolve15(rigDir, "state"), { recursive: true });
4273
- mkdirSync6(resolve15(rigDir, "logs"), { recursive: true });
4274
- mkdirSync6(resolve15(rigDir, "runs"), { recursive: true });
4275
- mkdirSync6(resolve15(rigDir, "tmp"), { recursive: true });
4276
- mkdirSync6(resolve15(projectRoot, "artifacts"), { recursive: true });
4277
- const taskConfigPath = resolve15(rigDir, "task-config.json");
4278
- if (!existsSync8(taskConfigPath))
4310
+ const rigDir = resolve16(projectRoot, ".rig");
4311
+ mkdirSync6(resolve16(rigDir, "state"), { recursive: true });
4312
+ mkdirSync6(resolve16(rigDir, "logs"), { recursive: true });
4313
+ mkdirSync6(resolve16(rigDir, "runs"), { recursive: true });
4314
+ mkdirSync6(resolve16(rigDir, "tmp"), { recursive: true });
4315
+ mkdirSync6(resolve16(projectRoot, "artifacts"), { recursive: true });
4316
+ const taskConfigPath = resolve16(rigDir, "task-config.json");
4317
+ if (!existsSync9(taskConfigPath))
4279
4318
  writeFileSync5(taskConfigPath, `{}
4280
4319
  `, "utf-8");
4281
4320
  }
4282
4321
  function ensureGitignoreEntries(projectRoot) {
4283
- const path = resolve15(projectRoot, ".gitignore");
4284
- const existing = existsSync8(path) ? readFileSync5(path, "utf8") : "";
4322
+ const path = resolve16(projectRoot, ".gitignore");
4323
+ const existing = existsSync9(path) ? readFileSync6(path, "utf8") : "";
4285
4324
  const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
4286
4325
  const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
4287
4326
  if (missing.length === 0)
@@ -4294,14 +4333,14 @@ function ensureGitignoreEntries(projectRoot) {
4294
4333
  `, "utf8");
4295
4334
  }
4296
4335
  function ensureRigConfigPackageDependencies(projectRoot) {
4297
- const path = resolve15(projectRoot, "package.json");
4298
- const existing = existsSync8(path) ? JSON.parse(readFileSync5(path, "utf8")) : {};
4336
+ const path = resolve16(projectRoot, "package.json");
4337
+ const existing = existsSync9(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
4299
4338
  const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
4300
4339
  for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
4301
4340
  devDependencies[name] = spec;
4302
4341
  }
4303
4342
  const next = {
4304
- ...existsSync8(path) ? existing : { name: "rig-project", private: true },
4343
+ ...existsSync9(path) ? existing : { name: "rig-project", private: true },
4305
4344
  devDependencies
4306
4345
  };
4307
4346
  writeFileSync5(path, `${JSON.stringify(next, null, 2)}
@@ -4371,15 +4410,71 @@ async function promptSelect(prompts, options) {
4371
4410
  throw new CliError2("Init cancelled.", 1);
4372
4411
  return String(value);
4373
4412
  }
4374
- async function pollDeviceAuthOnce(context, pollId) {
4413
+ function sleep2(ms) {
4414
+ return new Promise((resolve17) => setTimeout(resolve17, ms));
4415
+ }
4416
+ function positiveIntFromEnv(name, fallback) {
4417
+ const value = Number.parseInt(process.env[name] ?? "", 10);
4418
+ return Number.isFinite(value) && value >= 0 ? value : fallback;
4419
+ }
4420
+ function apiSessionTokenFrom(payload) {
4421
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
4422
+ return null;
4423
+ const token = payload.apiSessionToken;
4424
+ return typeof token === "string" && token.trim() ? token.trim() : null;
4425
+ }
4426
+ function cleanPayloadString(value) {
4427
+ return typeof value === "string" && value.trim() ? value.trim() : null;
4428
+ }
4429
+ function remoteGitHubAuthMetadata(payload) {
4430
+ if (!payload)
4431
+ return {};
4432
+ const userNamespace = payload.userNamespace && typeof payload.userNamespace === "object" && !Array.isArray(payload.userNamespace) ? payload.userNamespace : null;
4433
+ return {
4434
+ ...cleanPayloadString(payload.login) ? { login: cleanPayloadString(payload.login) } : {},
4435
+ ...cleanPayloadString(payload.userId) ? { userId: cleanPayloadString(payload.userId) } : {},
4436
+ ...cleanPayloadString(userNamespace?.key) ? { userNamespaceKey: cleanPayloadString(userNamespace?.key) } : {},
4437
+ ...cleanPayloadString(userNamespace?.root) ? { userNamespaceRoot: cleanPayloadString(userNamespace?.root) } : {},
4438
+ ...cleanPayloadString(userNamespace?.checkoutBaseDir) ? { checkoutBaseDir: cleanPayloadString(userNamespace?.checkoutBaseDir) } : {},
4439
+ ...cleanPayloadString(userNamespace?.snapshotBaseDir) ? { snapshotBaseDir: cleanPayloadString(userNamespace?.snapshotBaseDir) } : {}
4440
+ };
4441
+ }
4442
+ function writeRemoteGitHubAuthState(projectRoot, input) {
4443
+ writeFileSync5(resolve16(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
4444
+ authenticated: true,
4445
+ source: input.source,
4446
+ storedOnServer: true,
4447
+ selectedRepo: input.selectedRepo,
4448
+ ...remoteGitHubAuthMetadata(input.authPayload ?? null),
4449
+ ...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
4450
+ updatedAt: new Date().toISOString()
4451
+ }, null, 2)}
4452
+ `, "utf8");
4453
+ }
4454
+ async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
4375
4455
  if (typeof pollId !== "string" || !pollId.trim())
4376
4456
  return null;
4377
- const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
4378
- method: "POST",
4379
- headers: { "content-type": "application/json" },
4380
- body: JSON.stringify({ pollId })
4381
- }).catch(() => null);
4382
- return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
4457
+ const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
4458
+ const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
4459
+ const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
4460
+ const deadline = Date.now() + timeoutMs;
4461
+ let last = null;
4462
+ do {
4463
+ const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
4464
+ method: "POST",
4465
+ headers: { "content-type": "application/json" },
4466
+ body: JSON.stringify({ pollId })
4467
+ }).catch(() => null);
4468
+ last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
4469
+ const status = typeof last?.status === "string" ? last.status : null;
4470
+ if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
4471
+ return last;
4472
+ }
4473
+ if (timeoutMs <= 0)
4474
+ return last;
4475
+ await sleep2(intervalMs);
4476
+ } while (Date.now() < deadline);
4477
+ return last;
4383
4478
  }
4384
4479
  async function runControlPlaneInit(context, options) {
4385
4480
  const projectRoot = context.projectRoot;
@@ -4402,9 +4497,9 @@ async function runControlPlaneInit(context, options) {
4402
4497
  });
4403
4498
  ensureRigPrivateDirs(projectRoot);
4404
4499
  ensureGitignoreEntries(projectRoot);
4405
- const configTsPath = resolve15(projectRoot, "rig.config.ts");
4406
- const configJsonPath = resolve15(projectRoot, "rig.config.json");
4407
- const configExists = existsSync8(configTsPath) || existsSync8(configJsonPath);
4500
+ const configTsPath = resolve16(projectRoot, "rig.config.ts");
4501
+ const configJsonPath = resolve16(projectRoot, "rig.config.json");
4502
+ const configExists = existsSync9(configTsPath) || existsSync9(configJsonPath);
4408
4503
  if (!options.privateStateOnly) {
4409
4504
  if (configExists && !options.repair) {
4410
4505
  if (context.outputMode !== "json")
@@ -4420,7 +4515,7 @@ async function runControlPlaneInit(context, options) {
4420
4515
  }
4421
4516
  ensureRigConfigPackageDependencies(projectRoot);
4422
4517
  }
4423
- writeFileSync5(resolve15(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4518
+ writeFileSync5(resolve16(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4424
4519
  `, "utf8");
4425
4520
  const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
4426
4521
  let uploadedSnapshot = null;
@@ -4442,10 +4537,15 @@ async function runControlPlaneInit(context, options) {
4442
4537
  const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
4443
4538
  if (token) {
4444
4539
  githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
4445
- setGitHubBearerTokenForCurrentProcess(token);
4540
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4541
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4446
4542
  if (serverKind === "remote") {
4447
- writeFileSync5(resolve15(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({ authenticated: true, source: authMethod === "gh" ? "gh" : "init-token", storedOnServer: true, selectedRepo: repo.slug, updatedAt: new Date().toISOString() }, null, 2)}
4448
- `, "utf8");
4543
+ writeRemoteGitHubAuthState(projectRoot, {
4544
+ source: authMethod === "gh" ? "gh" : "init-token",
4545
+ selectedRepo: repo.slug,
4546
+ apiSessionToken,
4547
+ authPayload: githubAuth
4548
+ });
4449
4549
  }
4450
4550
  } else if (authMethod === "device") {
4451
4551
  const payload = await requestServerJson(context, "/api/github/auth/device/start", {
@@ -4454,9 +4554,22 @@ async function runControlPlaneInit(context, options) {
4454
4554
  body: JSON.stringify({ repoSlug: repo.slug })
4455
4555
  });
4456
4556
  deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
4457
- const completed = await pollDeviceAuthOnce(context, deviceAuth.pollId);
4458
- if (completed)
4557
+ if (context.outputMode !== "json") {
4558
+ const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
4559
+ const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
4560
+ console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
4561
+ }
4562
+ const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
4563
+ if (completed) {
4564
+ const apiSessionToken = apiSessionTokenFrom(completed);
4565
+ if (apiSessionToken) {
4566
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken);
4567
+ if (serverKind === "remote") {
4568
+ writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken, authPayload: completed });
4569
+ }
4570
+ }
4459
4571
  deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
4572
+ }
4460
4573
  }
4461
4574
  let remoteCheckoutPreparation = null;
4462
4575
  if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
@@ -4476,6 +4589,12 @@ async function runControlPlaneInit(context, options) {
4476
4589
  });
4477
4590
  const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
4478
4591
  const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
4592
+ if (serverRootSwitch && token) {
4593
+ githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
4594
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4595
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4596
+ writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken, authPayload: githubAuth });
4597
+ }
4479
4598
  const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
4480
4599
  const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
4481
4600
  remote: true,
@@ -4585,7 +4704,7 @@ function parseInitOptions(args) {
4585
4704
  async function runInteractiveControlPlaneInit(context, prompts) {
4586
4705
  prompts.intro?.("Initialize a Rig control-plane project");
4587
4706
  const projectRoot = context.projectRoot;
4588
- const existingConfig = existsSync8(resolve15(projectRoot, "rig.config.ts")) || existsSync8(resolve15(projectRoot, "rig.config.json"));
4707
+ const existingConfig = existsSync9(resolve16(projectRoot, "rig.config.ts")) || existsSync9(resolve16(projectRoot, "rig.config.json"));
4589
4708
  let repair = false;
4590
4709
  let privateStateOnly = false;
4591
4710
  if (existingConfig) {
@@ -4685,7 +4804,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
4685
4804
  });
4686
4805
  const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
4687
4806
  const deviceAuth = details.deviceAuth && typeof details.deviceAuth === "object" && !Array.isArray(details.deviceAuth) ? details.deviceAuth : null;
4688
- const deviceMessage = deviceAuth ? ` GitHub device flow: open ${String(deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server")} and enter ${String(deviceAuth.user_code ?? "the returned user code")}.` : "";
4807
+ const deviceMessage = deviceAuth ? ` GitHub device flow: open ${String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server")} and enter ${String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code")}.` : "";
4689
4808
  prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
4690
4809
  return result;
4691
4810
  }
@@ -4847,8 +4966,8 @@ async function executeDoctor(context, args) {
4847
4966
  }
4848
4967
 
4849
4968
  // packages/cli/src/commands/_run-driver-helpers.ts
4850
- import { readFileSync as readFileSync6 } from "fs";
4851
- import { resolve as resolve16 } from "path";
4969
+ import { readFileSync as readFileSync7 } from "fs";
4970
+ import { resolve as resolve17 } from "path";
4852
4971
  import {
4853
4972
  appendJsonlRecord as appendJsonlRecord2,
4854
4973
  readAuthorityRun as readAuthorityRun2,
@@ -4868,7 +4987,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
4868
4987
  ...patch,
4869
4988
  updatedAt: new Date().toISOString()
4870
4989
  };
4871
- writeJsonFile4(resolve16(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
4990
+ writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
4872
4991
  return next;
4873
4992
  }
4874
4993
  function touchAuthorityRun(projectRoot, runId) {
@@ -4876,21 +4995,21 @@ function touchAuthorityRun(projectRoot, runId) {
4876
4995
  if (!current) {
4877
4996
  return;
4878
4997
  }
4879
- writeJsonFile4(resolve16(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
4998
+ writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
4880
4999
  ...current,
4881
5000
  updatedAt: new Date().toISOString()
4882
5001
  });
4883
5002
  }
4884
5003
  function appendRunTimeline(projectRoot, runId, value) {
4885
- appendJsonlRecord2(resolve16(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
5004
+ appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
4886
5005
  touchAuthorityRun(projectRoot, runId);
4887
5006
  }
4888
5007
  function appendRunLog(projectRoot, runId, value) {
4889
- appendJsonlRecord2(resolve16(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
5008
+ appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
4890
5009
  touchAuthorityRun(projectRoot, runId);
4891
5010
  }
4892
5011
  function appendRunAction(projectRoot, runId, value) {
4893
- appendJsonlRecord2(resolve16(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
5012
+ appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
4894
5013
  id: value.id,
4895
5014
  type: "action",
4896
5015
  actionType: value.actionType,
@@ -4961,7 +5080,7 @@ function buildRunPrompt(input) {
4961
5080
  })();
4962
5081
  const scopeText = (() => {
4963
5082
  try {
4964
- const parsed = JSON.parse(readFileSync6(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
5083
+ const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
4965
5084
  const entry = parsed[input.taskId] ?? {};
4966
5085
  const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
4967
5086
  const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
@@ -5074,8 +5193,8 @@ function renderSourceScopeValidation(task, validation) {
5074
5193
  }
5075
5194
 
5076
5195
  // packages/cli/src/commands/inspect.ts
5077
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
5078
- import { resolve as resolve17 } from "path";
5196
+ import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
5197
+ import { resolve as resolve18 } from "path";
5079
5198
  import {
5080
5199
  listAuthorityRuns as listAuthorityRuns2,
5081
5200
  readAuthorityRun as readAuthorityRun3,
@@ -5096,8 +5215,8 @@ async function executeInspect(context, args) {
5096
5215
  if (!latestRun) {
5097
5216
  throw new CliError2(`No runs found for ${requiredTask}.`);
5098
5217
  }
5099
- const logsPath = resolve17(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5100
- if (!existsSync9(logsPath)) {
5218
+ const logsPath = resolve18(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5219
+ if (!existsSync10(logsPath)) {
5101
5220
  throw new CliError2(`No logs found for run ${latestRun.runId}.`);
5102
5221
  }
5103
5222
  await context.runCommand(["cat", logsPath]);
@@ -5107,7 +5226,7 @@ async function executeInspect(context, args) {
5107
5226
  const { value: task, rest: remaining } = takeOption(rest, "--task");
5108
5227
  requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
5109
5228
  const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
5110
- const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync9(path));
5229
+ const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync10(path));
5111
5230
  if (!artifactRoot) {
5112
5231
  throw new CliError2(`No artifacts found for ${requiredTask}.`);
5113
5232
  }
@@ -5164,10 +5283,10 @@ async function executeInspect(context, args) {
5164
5283
  case "failures": {
5165
5284
  requireNoExtraArgs(rest, "bun run rig inspect failures");
5166
5285
  const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
5167
- if (!existsSync9(failed)) {
5286
+ if (!existsSync10(failed)) {
5168
5287
  console.log("No failures recorded.");
5169
5288
  } else {
5170
- process.stdout.write(readFileSync7(failed, "utf-8"));
5289
+ process.stdout.write(readFileSync8(failed, "utf-8"));
5171
5290
  }
5172
5291
  return { ok: true, group: "inspect", command };
5173
5292
  }
@@ -5184,11 +5303,11 @@ async function executeInspect(context, args) {
5184
5303
  return { ok: true, group: "inspect", command };
5185
5304
  case "audit": {
5186
5305
  requireNoExtraArgs(rest, "bun run rig inspect audit");
5187
- const auditPath = resolve17(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5188
- if (!existsSync9(auditPath)) {
5306
+ const auditPath = resolve18(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5307
+ if (!existsSync10(auditPath)) {
5189
5308
  console.log("No audit log found.");
5190
5309
  } else {
5191
- const lines = readFileSync7(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5310
+ const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5192
5311
  for (const line of lines) {
5193
5312
  console.log(line);
5194
5313
  }
@@ -5817,8 +5936,8 @@ async function executeRemote(context, args) {
5817
5936
  }
5818
5937
 
5819
5938
  // packages/cli/src/commands/run.ts
5820
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
5821
- import { resolve as resolve18 } from "path";
5939
+ import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
5940
+ import { resolve as resolve19 } from "path";
5822
5941
  import { createInterface as createInterface2 } from "readline/promises";
5823
5942
  import {
5824
5943
  listAuthorityRuns as listAuthorityRuns3,
@@ -5832,6 +5951,7 @@ import {
5832
5951
  listOpenEpics,
5833
5952
  resolveDefaultEpic,
5834
5953
  runResume,
5954
+ runRestart,
5835
5955
  runStatus,
5836
5956
  runStop,
5837
5957
  startRun,
@@ -5940,6 +6060,17 @@ async function attachRunOperatorView(context, input) {
5940
6060
  }
5941
6061
 
5942
6062
  // packages/cli/src/commands/run.ts
6063
+ function normalizeRemoteRunDetails(payload) {
6064
+ const run = payload.run;
6065
+ if (!run || typeof run !== "object" || Array.isArray(run))
6066
+ return null;
6067
+ return {
6068
+ ...run,
6069
+ ...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
6070
+ ...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
6071
+ ...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
6072
+ };
6073
+ }
5943
6074
  function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
5944
6075
  if (noEpicPrompt) {
5945
6076
  return false;
@@ -6078,7 +6209,7 @@ async function executeRun(context, args) {
6078
6209
  if (!run.value) {
6079
6210
  throw new CliError2("run show requires --run <id>.");
6080
6211
  }
6081
- const record = readAuthorityRun4(context.projectRoot, run.value);
6212
+ const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
6082
6213
  if (!record) {
6083
6214
  throw new CliError2(`Run not found: ${run.value}`, 2);
6084
6215
  }
@@ -6097,7 +6228,7 @@ async function executeRun(context, args) {
6097
6228
  if (!run.value) {
6098
6229
  throw new CliError2("run timeline requires --run <id>.");
6099
6230
  }
6100
- const timelinePath = resolve18(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6231
+ const timelinePath = resolve19(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6101
6232
  const printEvents = () => {
6102
6233
  const events2 = readJsonlFile4(timelinePath);
6103
6234
  if (context.outputMode === "text") {
@@ -6109,12 +6240,12 @@ async function executeRun(context, args) {
6109
6240
  };
6110
6241
  const events = printEvents();
6111
6242
  if (follow.value && context.outputMode === "text") {
6112
- let lastLength = existsSync10(timelinePath) ? readFileSync8(timelinePath, "utf8").length : 0;
6243
+ let lastLength = existsSync11(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
6113
6244
  while (true) {
6114
6245
  await Bun.sleep(1000);
6115
- if (!existsSync10(timelinePath))
6246
+ if (!existsSync11(timelinePath))
6116
6247
  continue;
6117
- const next = readFileSync8(timelinePath, "utf8");
6248
+ const next = readFileSync9(timelinePath, "utf8");
6118
6249
  if (next.length <= lastLength)
6119
6250
  continue;
6120
6251
  const delta = next.slice(lastLength);
@@ -6259,6 +6390,20 @@ async function executeRun(context, args) {
6259
6390
  }
6260
6391
  return { ok: true, group: "run", command, details: resumed };
6261
6392
  }
6393
+ case "restart": {
6394
+ requireNoExtraArgs(rest, "bun run rig run restart");
6395
+ if (context.dryRun) {
6396
+ if (context.outputMode === "text") {
6397
+ console.log("[dry-run] rig run restart");
6398
+ }
6399
+ return { ok: true, group: "run", command };
6400
+ }
6401
+ const restarted = await runRestart(context.projectRoot, runtimeContext);
6402
+ if (context.outputMode === "text") {
6403
+ console.log(`Restarted run: ${restarted.runId}`);
6404
+ }
6405
+ return { ok: true, group: "run", command, details: restarted };
6406
+ }
6262
6407
  case "stop": {
6263
6408
  const runOption = takeOption(rest, "--run");
6264
6409
  const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
@@ -6316,7 +6461,7 @@ async function executeServer(context, args, options) {
6316
6461
  const authTokenResult = takeOption(pending, "--auth-token");
6317
6462
  pending = authTokenResult.rest;
6318
6463
  requireNoExtraArgs(pending, "bun run rig server start [--host <host>] [--port <n>] [--poll-ms <n>] [--auth-token <token>]");
6319
- const commandParts = ["bun", "run", "packages/server/src/server.ts", "start"];
6464
+ const commandParts = ["rig-server", "start"];
6320
6465
  if (hostResult.value) {
6321
6466
  commandParts.push("--host", hostResult.value);
6322
6467
  }
@@ -6339,7 +6484,7 @@ async function executeServer(context, args, options) {
6339
6484
  const eventResult = takeOption(pending, "--event");
6340
6485
  pending = eventResult.rest;
6341
6486
  requireNoExtraArgs(pending, "bun run rig server notify-test [--event <type>]");
6342
- const commandParts = ["bun", "run", "packages/server/src/server.ts", "notify-test"];
6487
+ const commandParts = ["rig-server", "notify-test"];
6343
6488
  if (eventResult.value) {
6344
6489
  commandParts.push("--event", eventResult.value);
6345
6490
  }
@@ -6400,10 +6545,10 @@ async function executeServer(context, args, options) {
6400
6545
  }
6401
6546
 
6402
6547
  // packages/cli/src/commands/task.ts
6403
- import { readFileSync as readFileSync9 } from "fs";
6548
+ import { readFileSync as readFileSync10 } from "fs";
6404
6549
  import { spawnSync as spawnSync4 } from "child_process";
6405
6550
  import { createInterface as createInterface4 } from "readline/promises";
6406
- import { resolve as resolve19 } from "path";
6551
+ import { resolve as resolve20 } from "path";
6407
6552
  import {
6408
6553
  taskArtifactDir,
6409
6554
  taskArtifacts,
@@ -6734,7 +6879,7 @@ async function executeTask(context, args, options) {
6734
6879
  const fileFlag = takeOption(rest.slice(1), "--file");
6735
6880
  let content;
6736
6881
  if (fileFlag.value) {
6737
- content = readFileSync9(resolve19(context.projectRoot, fileFlag.value), "utf-8");
6882
+ content = readFileSync10(resolve20(context.projectRoot, fileFlag.value), "utf-8");
6738
6883
  } else {
6739
6884
  content = await readStdin();
6740
6885
  }
@@ -6955,8 +7100,8 @@ async function executeTask(context, args, options) {
6955
7100
  }
6956
7101
 
6957
7102
  // packages/cli/src/commands/task-run-driver.ts
6958
- import { copyFileSync as copyFileSync3, existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync10, statSync as statSync2 } from "fs";
6959
- import { resolve as resolve20 } from "path";
7103
+ import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
7104
+ import { resolve as resolve21 } from "path";
6960
7105
  import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
6961
7106
  import { createInterface as createLineInterface } from "readline";
6962
7107
  import { loadConfig as loadConfig2 } from "@rig/core/load-config";
@@ -6990,7 +7135,24 @@ import {
6990
7135
  commitRunChanges,
6991
7136
  runPrAutomation
6992
7137
  } from "@rig/runtime/control-plane/native/pr-automation";
7138
+ function looksLikeGitHubToken(value) {
7139
+ const token = value?.trim();
7140
+ if (!token)
7141
+ return false;
7142
+ return /^(gh[opusr]_|github_pat_)/.test(token);
7143
+ }
7144
+ function githubBridgeEnv(token) {
7145
+ const clean = token?.trim();
7146
+ if (!clean)
7147
+ return {};
7148
+ return {
7149
+ RIG_GITHUB_TOKEN: clean,
7150
+ GITHUB_TOKEN: clean,
7151
+ GH_TOKEN: clean
7152
+ };
7153
+ }
6993
7154
  function buildPiRigBridgeEnv(input) {
7155
+ const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
6994
7156
  return {
6995
7157
  RIG_PROJECT_ROOT: input.projectRoot,
6996
7158
  PROJECT_RIG_ROOT: input.projectRoot,
@@ -7000,7 +7162,7 @@ function buildPiRigBridgeEnv(input) {
7000
7162
  RIG_RUNTIME_ADAPTER: "pi",
7001
7163
  ...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
7002
7164
  ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
7003
- ...input.githubToken ? { RIG_GITHUB_TOKEN: input.githubToken } : {}
7165
+ ...githubBridgeEnv(githubToken)
7004
7166
  };
7005
7167
  }
7006
7168
  function runGitSync(cwd, args, input) {
@@ -7022,12 +7184,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
7022
7184
  return 0;
7023
7185
  let copied = 0;
7024
7186
  for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
7025
- const sourcePath = resolve20(sourceRoot, relativePath);
7026
- const targetPath = resolve20(targetRoot, relativePath);
7187
+ const sourcePath = resolve21(sourceRoot, relativePath);
7188
+ const targetPath = resolve21(targetRoot, relativePath);
7027
7189
  try {
7028
7190
  if (!statSync2(sourcePath).isFile())
7029
7191
  continue;
7030
- mkdirSync7(resolve20(targetPath, ".."), { recursive: true });
7192
+ mkdirSync7(resolve21(targetPath, ".."), { recursive: true });
7031
7193
  copyFileSync3(sourcePath, targetPath);
7032
7194
  copied += 1;
7033
7195
  } catch {}
@@ -7061,6 +7223,14 @@ function buildTaskRunReviewEnv(config) {
7061
7223
  ...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
7062
7224
  };
7063
7225
  }
7226
+ function buildDirtyBaselineHandshakeEnv(input) {
7227
+ if (input.baselineMode !== "dirty-snapshot")
7228
+ return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
7229
+ return {
7230
+ RIG_BASELINE_MODE: "dirty-snapshot",
7231
+ RIG_DIRTY_BASELINE_READY_FILE: resolve21(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
7232
+ };
7233
+ }
7064
7234
  function positiveInt(value, fallback) {
7065
7235
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
7066
7236
  }
@@ -7169,9 +7339,9 @@ function createCommandRunner(binary) {
7169
7339
  const stderrChunks = [];
7170
7340
  child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7171
7341
  child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7172
- return await new Promise((resolve21) => {
7173
- child.once("error", (error) => resolve21({ exitCode: 1, stderr: error.message }));
7174
- child.once("close", (code) => resolve21({
7342
+ return await new Promise((resolve22) => {
7343
+ child.once("error", (error) => resolve22({ exitCode: 1, stderr: error.message }));
7344
+ child.once("close", (code) => resolve22({
7175
7345
  exitCode: code ?? 1,
7176
7346
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
7177
7347
  stderr: Buffer.concat(stderrChunks).toString("utf8")
@@ -7367,7 +7537,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
7367
7537
  return null;
7368
7538
  }
7369
7539
  for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
7370
- const summary = readJsonFile3(resolve20(artifactDir, "validation-summary.json"), null);
7540
+ const summary = readJsonFile3(resolve21(artifactDir, "validation-summary.json"), null);
7371
7541
  if (!summary || summary.status !== "fail") {
7372
7542
  continue;
7373
7543
  }
@@ -7448,9 +7618,9 @@ function readTaskRunAcceptedArtifactState(input) {
7448
7618
  if (!input.taskId || !input.workspaceDir) {
7449
7619
  return { accepted: false, reason: null };
7450
7620
  }
7451
- const artifactDir = resolve20(input.workspaceDir, "artifacts", input.taskId);
7452
- const reviewStatusPath = resolve20(artifactDir, "review-status.txt");
7453
- const taskResultPath = resolve20(artifactDir, "task-result.json");
7621
+ const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7622
+ const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7623
+ const taskResultPath = resolve21(artifactDir, "task-result.json");
7454
7624
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7455
7625
  if (reviewStatus !== "APPROVED") {
7456
7626
  return { accepted: false, reason: null };
@@ -7487,12 +7657,12 @@ function resolveTaskRunRetryContext(input) {
7487
7657
  if (!input.taskId || !input.workspaceDir) {
7488
7658
  return { shouldRetry: false, failureDetail: null, nextPrompt: null };
7489
7659
  }
7490
- const artifactDir = resolve20(input.workspaceDir, "artifacts", input.taskId);
7491
- const reviewStatePath = resolve20(artifactDir, "review-state.json");
7492
- const reviewFeedbackPath = resolve20(artifactDir, "review-feedback.md");
7493
- const reviewStatusPath = resolve20(artifactDir, "review-status.txt");
7494
- const failedApproachesPath = resolve20(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7495
- const validationSummaryPath = resolve20(artifactDir, "validation-summary.json");
7660
+ const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7661
+ const reviewStatePath = resolve21(artifactDir, "review-state.json");
7662
+ const reviewFeedbackPath = resolve21(artifactDir, "review-feedback.md");
7663
+ const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7664
+ const failedApproachesPath = resolve21(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7665
+ const validationSummaryPath = resolve21(artifactDir, "validation-summary.json");
7496
7666
  const reviewState = readJsonFile3(reviewStatePath, null);
7497
7667
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7498
7668
  const reviewRejected = isTaskRunReviewRejected(reviewState);
@@ -7547,11 +7717,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
7547
7717
  return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
7548
7718
  }
7549
7719
  function readTaskRunReviewStatus(reviewStatusPath) {
7550
- if (!existsSync11(reviewStatusPath)) {
7720
+ if (!existsSync12(reviewStatusPath)) {
7551
7721
  return null;
7552
7722
  }
7553
7723
  try {
7554
- const status = readFileSync10(reviewStatusPath, "utf8").trim().toUpperCase();
7724
+ const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
7555
7725
  return status === "APPROVED" || status === "REJECTED" ? status : null;
7556
7726
  } catch {
7557
7727
  return null;
@@ -7634,8 +7804,11 @@ function stringArrayField(record, key) {
7634
7804
  }
7635
7805
  async function executeRigOwnedTaskRun(context, input) {
7636
7806
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
7807
+ const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
7808
+ const resumeMode = process.env.RIG_RUN_RESUME === "1";
7809
+ const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
7637
7810
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
7638
- const prompt = buildRunPrompt({
7811
+ let prompt = buildRunPrompt({
7639
7812
  projectRoot: context.projectRoot,
7640
7813
  taskId: input.taskId,
7641
7814
  fallbackTitle: input.title,
@@ -7689,14 +7862,14 @@ async function executeRigOwnedTaskRun(context, input) {
7689
7862
  taskId: runtimeTaskId,
7690
7863
  createdAt: startedAt,
7691
7864
  runtimeAdapter: input.runtimeAdapter,
7692
- status: "created"
7865
+ status: resumeMode ? "preparing" : "created"
7693
7866
  });
7694
7867
  patchAuthorityRun(context.projectRoot, input.runId, {
7695
7868
  status: "preparing",
7696
7869
  startedAt,
7697
7870
  completedAt: null,
7698
7871
  errorText: null,
7699
- artifactRoot: null,
7872
+ artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
7700
7873
  runtimeAdapter: input.runtimeAdapter,
7701
7874
  runtimeMode: input.runtimeMode,
7702
7875
  interactionMode: input.interactionMode,
@@ -7712,9 +7885,9 @@ async function executeRigOwnedTaskRun(context, input) {
7712
7885
  detail: input.taskId ?? input.title ?? runtimeTaskId
7713
7886
  });
7714
7887
  appendRunLog(context.projectRoot, input.runId, {
7715
- id: `log:${input.runId}:start`,
7716
- title: "Rig task run started",
7717
- detail: input.taskId ?? input.title ?? runtimeTaskId,
7888
+ id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
7889
+ title: resumeMode ? "Rig task run resumed" : "Rig task run started",
7890
+ detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
7718
7891
  tone: "info",
7719
7892
  status: "preparing",
7720
7893
  createdAt: startedAt
@@ -7735,7 +7908,22 @@ async function executeRigOwnedTaskRun(context, input) {
7735
7908
  const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
7736
7909
  const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
7737
7910
  const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
7738
- patchAuthorityRun(context.projectRoot, input.runId, { planning: planningClassification });
7911
+ const planningArtifactPath = resolve21("artifacts", runtimeTaskId, "implementation-plan.md");
7912
+ const persistedPlanning = {
7913
+ ...planningClassification,
7914
+ classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
7915
+ artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
7916
+ classifiedAt: new Date().toISOString()
7917
+ };
7918
+ mkdirSync7(resolve21(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
7919
+ writeFileSync6(resolve21(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
7920
+ `, "utf8");
7921
+ patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
7922
+ prompt = `${prompt}
7923
+
7924
+ Rig planning classification:
7925
+ ${JSON.stringify(persistedPlanning, null, 2)}
7926
+ ${planningClassification.planningRequired ? `Before implementing, write a concise implementation plan to ${planningArtifactPath}. Treat that plan artifact as required acceptance evidence for the Plan stage.` : "Planning is not required for this run; briefly state why before implementation."}`;
7739
7927
  if (input.runtimeAdapter === "pi") {
7740
7928
  for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
7741
7929
  appendPiStageLog({
@@ -7777,11 +7965,11 @@ async function executeRigOwnedTaskRun(context, input) {
7777
7965
  let reviewAction;
7778
7966
  let verificationStarted = false;
7779
7967
  let reviewStarted = false;
7780
- let latestRuntimeWorkspace = null;
7781
- let latestSessionDir = null;
7782
- let latestLogsDir = null;
7968
+ let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
7969
+ let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve21(existingRunRecord.sessionPath, "..") : null;
7970
+ let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
7783
7971
  let latestProviderCommand = null;
7784
- let latestRuntimeBranch = null;
7972
+ let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
7785
7973
  let snapshotSidecarPromise = null;
7786
7974
  let dirtyBaselineApplied = false;
7787
7975
  const childEnv = {
@@ -7799,9 +7987,14 @@ async function executeRigOwnedTaskRun(context, input) {
7799
7987
  RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
7800
7988
  RIG_SERVER_RUN_ID: input.runId
7801
7989
  },
7802
- ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
7990
+ ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
7991
+ ...resumeMode ? {
7992
+ RIG_RUN_RESUME: "1",
7993
+ RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
7994
+ } : {}
7803
7995
  };
7804
7996
  Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
7997
+ Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
7805
7998
  const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
7806
7999
  const maxAttempts = automationLimits.maxValidationAttempts;
7807
8000
  const promoteToValidating = (detail) => {
@@ -7856,22 +8049,29 @@ async function executeRigOwnedTaskRun(context, input) {
7856
8049
  patchAuthorityRun(context.projectRoot, input.runId, {
7857
8050
  status: "running",
7858
8051
  worktreePath: latestRuntimeWorkspace,
7859
- artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve20(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8052
+ artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve21(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
7860
8053
  logRoot: latestLogsDir,
7861
- sessionPath: latestSessionDir ? resolve20(latestSessionDir, "session.json") : null,
7862
- sessionLogPath: latestLogsDir ? resolve20(latestLogsDir, "agent-stdout.log") : null,
8054
+ sessionPath: latestSessionDir ? resolve21(latestSessionDir, "session.json") : null,
8055
+ sessionLogPath: latestLogsDir ? resolve21(latestLogsDir, "agent-stdout.log") : null,
7863
8056
  branch: runtimeId
7864
8057
  });
7865
8058
  if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
7866
8059
  dirtyBaselineApplied = true;
7867
8060
  const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
8061
+ const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
8062
+ if (readyFile) {
8063
+ mkdirSync7(resolve21(readyFile, ".."), { recursive: true });
8064
+ writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
8065
+ `, "utf8");
8066
+ }
7868
8067
  appendRunLog(context.projectRoot, input.runId, {
7869
8068
  id: `log:${input.runId}:dirty-baseline`,
7870
8069
  title: "Dirty baseline snapshot",
7871
8070
  detail: dirty.detail,
7872
8071
  tone: dirty.applied ? "tool" : "info",
7873
8072
  status: dirty.applied ? "completed" : "skipped",
7874
- createdAt: new Date().toISOString()
8073
+ createdAt: new Date().toISOString(),
8074
+ payload: readyFile ? { readyFile } : undefined
7875
8075
  });
7876
8076
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
7877
8077
  }
@@ -8097,7 +8297,36 @@ async function executeRigOwnedTaskRun(context, input) {
8097
8297
  let reviewFailureDetail = null;
8098
8298
  const stderrLines = [];
8099
8299
  const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
8100
- for (let attempt = 1;attempt <= maxAttempts; attempt += 1) {
8300
+ if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
8301
+ appendRunLog(context.projectRoot, input.runId, {
8302
+ id: `log:${input.runId}:resume-closeout-phase`,
8303
+ title: "Resume continuing from closeout phase",
8304
+ detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
8305
+ tone: "info",
8306
+ status: resumePreviousStatus,
8307
+ createdAt: new Date().toISOString()
8308
+ });
8309
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
8310
+ exit = { code: 0, signal: null };
8311
+ } else if (resumeMode && latestRuntimeWorkspace) {
8312
+ const acceptedArtifactState = readTaskRunAcceptedArtifactState({
8313
+ taskId: input.taskId ?? runtimeTaskId,
8314
+ workspaceDir: latestRuntimeWorkspace
8315
+ });
8316
+ if (acceptedArtifactState.accepted) {
8317
+ appendRunLog(context.projectRoot, input.runId, {
8318
+ id: `log:${input.runId}:resume-accepted-artifacts`,
8319
+ title: "Resume found accepted artifacts; continuing closeout",
8320
+ detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
8321
+ tone: "info",
8322
+ status: "validating",
8323
+ createdAt: new Date().toISOString()
8324
+ });
8325
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
8326
+ exit = { code: 0, signal: null };
8327
+ }
8328
+ }
8329
+ for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
8101
8330
  const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
8102
8331
  const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
8103
8332
  cwd: context.projectRoot,
@@ -8138,7 +8367,7 @@ async function executeRigOwnedTaskRun(context, input) {
8138
8367
  let acceptedArtifactObservedAt = null;
8139
8368
  let acceptedArtifactPollTimer = null;
8140
8369
  let acceptedArtifactKillTimer = null;
8141
- const attemptExit = await new Promise((resolve21) => {
8370
+ const attemptExit = await new Promise((resolve22) => {
8142
8371
  let settled = false;
8143
8372
  const settle = (result) => {
8144
8373
  if (settled)
@@ -8146,7 +8375,7 @@ async function executeRigOwnedTaskRun(context, input) {
8146
8375
  settled = true;
8147
8376
  if (acceptedArtifactPollTimer)
8148
8377
  clearInterval(acceptedArtifactPollTimer);
8149
- resolve21(result);
8378
+ resolve22(result);
8150
8379
  };
8151
8380
  const pollAcceptedArtifacts = () => {
8152
8381
  const artifactState = readTaskRunAcceptedArtifactState({
@@ -8343,6 +8572,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8343
8572
  });
8344
8573
  throw new CliError2(terminalFailureDetail, exit.code ?? 1);
8345
8574
  }
8575
+ if (planningClassification.planningRequired) {
8576
+ const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
8577
+ const expectedPlanPath = resolve21(planWorkspace, planningArtifactPath);
8578
+ if (!existsSync12(expectedPlanPath)) {
8579
+ const failedAt = new Date().toISOString();
8580
+ const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
8581
+ patchAuthorityRun(context.projectRoot, input.runId, {
8582
+ status: "needs_attention",
8583
+ completedAt: failedAt,
8584
+ errorText: failureDetail
8585
+ });
8586
+ appendRunLog(context.projectRoot, input.runId, {
8587
+ id: `log:${input.runId}:plan-artifact-missing`,
8588
+ title: "Required plan artifact missing",
8589
+ detail: failureDetail,
8590
+ tone: "error",
8591
+ status: "needs_attention",
8592
+ createdAt: failedAt
8593
+ });
8594
+ emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
8595
+ throw new CliError2(failureDetail, 1);
8596
+ }
8597
+ }
8346
8598
  const runPiPrFeedbackFix = async (message2) => {
8347
8599
  appendPiStageLog({
8348
8600
  projectRoot: context.projectRoot,
@@ -8400,9 +8652,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8400
8652
  });
8401
8653
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
8402
8654
  });
8403
- const exitCode = await new Promise((resolve21) => {
8404
- child.once("error", () => resolve21(1));
8405
- child.once("close", (code) => resolve21(code ?? 1));
8655
+ const exitCode = await new Promise((resolve22) => {
8656
+ child.once("error", () => resolve22(1));
8657
+ child.once("close", (code) => resolve22(code ?? 1));
8406
8658
  });
8407
8659
  if (exitCode !== 0) {
8408
8660
  throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
@@ -8521,8 +8773,8 @@ async function executeTest(context, args) {
8521
8773
  }
8522
8774
 
8523
8775
  // packages/cli/src/commands/setup.ts
8524
- import { existsSync as existsSync12, mkdirSync as mkdirSync8, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "fs";
8525
- import { resolve as resolve21 } from "path";
8776
+ import { existsSync as existsSync13, mkdirSync as mkdirSync8, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
8777
+ import { resolve as resolve22 } from "path";
8526
8778
  import { createPluginHost } from "@rig/core";
8527
8779
  import {
8528
8780
  isSupportedBunVersion as isSupportedBunVersion2,
@@ -8585,9 +8837,9 @@ function runSetupInit(projectRoot) {
8585
8837
  mkdirSync8(stateDir, { recursive: true });
8586
8838
  mkdirSync8(logsDir, { recursive: true });
8587
8839
  mkdirSync8(artifactsDir, { recursive: true });
8588
- const failuresPath = resolve21(stateDir, "failed_approaches.md");
8589
- if (!existsSync12(failuresPath)) {
8590
- writeFileSync6(failuresPath, `# Failed Approaches
8840
+ const failuresPath = resolve22(stateDir, "failed_approaches.md");
8841
+ if (!existsSync13(failuresPath)) {
8842
+ writeFileSync7(failuresPath, `# Failed Approaches
8591
8843
 
8592
8844
  `, "utf-8");
8593
8845
  }
@@ -8604,18 +8856,18 @@ async function runSetupCheck(projectRoot) {
8604
8856
  }
8605
8857
  async function runSetupPreflight(projectRoot) {
8606
8858
  await runSetupCheck(projectRoot);
8607
- const validationRoot = resolve21(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
8608
- if (existsSync12(validationRoot)) {
8859
+ const validationRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
8860
+ if (existsSync13(validationRoot)) {
8609
8861
  const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
8610
8862
  for (const validator of validators) {
8611
- const script = resolve21(validationRoot, validator.name, "validate.sh");
8612
- if (existsSync12(script)) {
8863
+ const script = resolve22(validationRoot, validator.name, "validate.sh");
8864
+ if (existsSync13(script)) {
8613
8865
  console.log(`OK: validator script ${script}`);
8614
8866
  }
8615
8867
  }
8616
8868
  }
8617
- const hooksRoot = resolve21(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
8618
- if (existsSync12(hooksRoot)) {
8869
+ const hooksRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
8870
+ if (existsSync13(hooksRoot)) {
8619
8871
  const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
8620
8872
  for (const hook of hooks) {
8621
8873
  console.log(`OK: hook ${hook}`);