@h-rig/cli 0.0.6-alpha.0 → 0.0.6-alpha.10

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.
package/dist/src/index.js CHANGED
@@ -101,12 +101,13 @@ async function runCommand(context, parts) {
101
101
  const envMode = process.env.RIG_BASH_MODE;
102
102
  const effectiveMode = context.policyMode || (envMode === "off" || envMode === "observe" || envMode === "enforce" ? envMode : loadPolicy(context.projectRoot).mode);
103
103
  const controlledPath = `${resolve(context.projectRoot, ".rig", "bin")}:${context.projectRoot}/rig/tools:${process.env.PATH ?? ""}`;
104
- const controlledBash = await ensureAgentShellBinary(context.projectRoot);
104
+ const usesInfrastructureBinary = parts[0] === "rig-server";
105
+ const controlledBash = usesInfrastructureBinary ? null : await ensureAgentShellBinary(context.projectRoot);
105
106
  const commandEnv = [
106
107
  "env",
107
108
  `PATH=${controlledPath}`,
108
109
  `PROJECT_RIG_ROOT=${context.projectRoot}`,
109
- `BASH=${controlledBash}`,
110
+ ...controlledBash ? [`BASH=${controlledBash}`] : [],
110
111
  `RIG_BASH_MODE=${effectiveMode}`,
111
112
  `RIG_POLICY_FILE=${resolve(context.projectRoot, "rig/policy/policy.json")}`,
112
113
  ...context.eventBus.getEventsFile() ? [`RIG_EVENTS_FILE=${context.eventBus.getEventsFile()}`] : []
@@ -2667,6 +2668,8 @@ function resolveSelectedConnection(projectRoot, options = {}) {
2667
2668
 
2668
2669
  // packages/cli/src/commands/_server-client.ts
2669
2670
  import { spawnSync } from "child_process";
2671
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
2672
+ import { resolve as resolve9 } from "path";
2670
2673
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
2671
2674
  var cachedGitHubBearerToken;
2672
2675
  function cleanToken(value) {
@@ -2676,9 +2679,25 @@ function cleanToken(value) {
2676
2679
  function setGitHubBearerTokenForCurrentProcess(token) {
2677
2680
  cachedGitHubBearerToken = cleanToken(token ?? undefined);
2678
2681
  }
2679
- function readGitHubBearerTokenForRemote() {
2682
+ function readPrivateRemoteSessionToken(projectRoot) {
2683
+ const path = resolve9(projectRoot, ".rig", "state", "github-auth.json");
2684
+ if (!existsSync5(path))
2685
+ return null;
2686
+ try {
2687
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
2688
+ return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
2689
+ } catch {
2690
+ return null;
2691
+ }
2692
+ }
2693
+ function readGitHubBearerTokenForRemote(projectRoot) {
2680
2694
  if (cachedGitHubBearerToken !== undefined)
2681
2695
  return cachedGitHubBearerToken;
2696
+ const privateSession = readPrivateRemoteSessionToken(projectRoot);
2697
+ if (privateSession) {
2698
+ cachedGitHubBearerToken = privateSession;
2699
+ return cachedGitHubBearerToken;
2700
+ }
2682
2701
  const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
2683
2702
  if (envToken) {
2684
2703
  cachedGitHubBearerToken = envToken;
@@ -2698,7 +2717,7 @@ async function ensureServerForCli(projectRoot) {
2698
2717
  if (selected?.connection.kind === "remote") {
2699
2718
  return {
2700
2719
  baseUrl: selected.connection.baseUrl,
2701
- authToken: readGitHubBearerTokenForRemote(),
2720
+ authToken: readGitHubBearerTokenForRemote(projectRoot),
2702
2721
  connectionKind: "remote"
2703
2722
  };
2704
2723
  }
@@ -2805,7 +2824,7 @@ async function postGitHubTokenViaServer(context, token, options = {}) {
2805
2824
  const payload = await requestServerJson(context, "/api/github/auth/token", {
2806
2825
  method: "POST",
2807
2826
  headers: { "content-type": "application/json" },
2808
- body: JSON.stringify({ token, selectedRepo: options.selectedRepo })
2827
+ body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
2809
2828
  });
2810
2829
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2811
2830
  }
@@ -2826,7 +2845,7 @@ async function registerProjectViaServer(context, input) {
2826
2845
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2827
2846
  }
2828
2847
  function sleep(ms) {
2829
- return new Promise((resolve9) => setTimeout(resolve9, ms));
2848
+ return new Promise((resolve10) => setTimeout(resolve10, ms));
2830
2849
  }
2831
2850
  async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
2832
2851
  const switched = await requestServerJson(context, "/api/server/project-root", {
@@ -2917,9 +2936,9 @@ async function submitTaskRunViaServer(context, input) {
2917
2936
  }
2918
2937
 
2919
2938
  // packages/cli/src/commands/_pi-install.ts
2920
- import { existsSync as existsSync5, readFileSync as readFileSync3, rmSync as rmSync3 } from "fs";
2939
+ import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
2921
2940
  import { homedir as homedir3 } from "os";
2922
- import { resolve as resolve9 } from "path";
2941
+ import { resolve as resolve10 } from "path";
2923
2942
  var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
2924
2943
  var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
2925
2944
  export { default } from '@rig/pi-rig';
@@ -2934,11 +2953,11 @@ async function defaultCommandRunner(command, options = {}) {
2934
2953
  return { exitCode, stdout, stderr };
2935
2954
  }
2936
2955
  function resolvePiRigExtensionPath(homeDir) {
2937
- return resolve9(homeDir, ".pi", "agent", "extensions", "pi-rig");
2956
+ return resolve10(homeDir, ".pi", "agent", "extensions", "pi-rig");
2938
2957
  }
2939
- function resolvePiRigPackageSource(projectRoot, exists = existsSync5) {
2940
- const localPackage = resolve9(projectRoot, "packages", "pi-rig");
2941
- if (exists(resolve9(localPackage, "package.json")))
2958
+ function resolvePiRigPackageSource(projectRoot, exists = existsSync6) {
2959
+ const localPackage = resolve10(projectRoot, "packages", "pi-rig");
2960
+ if (exists(resolve10(localPackage, "package.json")))
2942
2961
  return localPackage;
2943
2962
  return `npm:${PI_RIG_PACKAGE_NAME}`;
2944
2963
  }
@@ -2989,13 +3008,13 @@ async function ensurePiBinaryAvailable(input) {
2989
3008
  ...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
2990
3009
  };
2991
3010
  }
2992
- function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync5) {
3011
+ function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync6) {
2993
3012
  const extensionPath = resolvePiRigExtensionPath(homeDir);
2994
- const indexPath = resolve9(extensionPath, "index.ts");
3013
+ const indexPath = resolve10(extensionPath, "index.ts");
2995
3014
  if (!exists(indexPath))
2996
3015
  return;
2997
3016
  try {
2998
- const content = readFileSync3(indexPath, "utf8");
3017
+ const content = readFileSync4(indexPath, "utf8");
2999
3018
  if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
3000
3019
  rmSync3(extensionPath, { recursive: true, force: true });
3001
3020
  }
@@ -3011,13 +3030,13 @@ async function checkPiRigInstall(input = {}) {
3011
3030
  piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
3012
3031
  };
3013
3032
  }
3014
- const exists = input.exists ?? existsSync5;
3033
+ const exists = input.exists ?? existsSync6;
3015
3034
  const runner = input.commandRunner ?? defaultCommandRunner;
3016
3035
  const piResult = await safeRun(runner, ["pi", "--version"]);
3017
3036
  const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
3018
3037
  const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
3019
3038
  ${piListResult.stderr}`);
3020
- const legacyBridge = exists(resolve9(extensionPath, "index.ts"));
3039
+ const legacyBridge = exists(resolve10(extensionPath, "index.ts"));
3021
3040
  const hasPiRig = listedPiRig;
3022
3041
  return {
3023
3042
  extensionPath,
@@ -3355,7 +3374,7 @@ async function executeQueue(context, args) {
3355
3374
  }
3356
3375
 
3357
3376
  // packages/cli/src/commands/agent.ts
3358
- import { resolve as resolve11 } from "path";
3377
+ import { resolve as resolve12 } from "path";
3359
3378
  import {
3360
3379
  agentId,
3361
3380
  cleanupAgentRuntime,
@@ -3365,8 +3384,8 @@ import {
3365
3384
  } from "@rig/runtime/control-plane/runtime/isolation";
3366
3385
 
3367
3386
  // packages/cli/src/commands/_authority-runs.ts
3368
- import { existsSync as existsSync6 } from "fs";
3369
- import { resolve as resolve10 } from "path";
3387
+ import { existsSync as existsSync7 } from "fs";
3388
+ import { resolve as resolve11 } from "path";
3370
3389
  import {
3371
3390
  readAuthorityRun,
3372
3391
  readJsonlFile as readJsonlFile2,
@@ -3388,8 +3407,8 @@ function normalizeRuntimeAdapter(value) {
3388
3407
  return "claude-code";
3389
3408
  }
3390
3409
  function readLatestBeadRecord(projectRoot, taskId) {
3391
- const issuesPath = resolve10(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3392
- if (!existsSync6(issuesPath)) {
3410
+ const issuesPath = resolve11(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3411
+ if (!existsSync7(issuesPath)) {
3393
3412
  return null;
3394
3413
  }
3395
3414
  let latest = null;
@@ -3424,6 +3443,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3424
3443
  const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
3425
3444
  const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
3426
3445
  const next = {
3446
+ ...existing ?? {},
3427
3447
  runId: input.runId,
3428
3448
  projectRoot,
3429
3449
  workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
@@ -3456,7 +3476,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3456
3476
  } else if ("errorText" in next) {
3457
3477
  delete next.errorText;
3458
3478
  }
3459
- writeJsonFile3(resolve10(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3479
+ writeJsonFile3(resolve11(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3460
3480
  return next;
3461
3481
  }
3462
3482
 
@@ -3577,10 +3597,10 @@ async function executeAgent(context, args) {
3577
3597
  status: "running",
3578
3598
  startedAt: createdAt,
3579
3599
  worktreePath: runtime.workspaceDir,
3580
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3600
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3581
3601
  logRoot: runtime.logsDir,
3582
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3583
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3602
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3603
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3584
3604
  pid: process.pid
3585
3605
  });
3586
3606
  const result = await runInAgentRuntime({
@@ -3600,10 +3620,10 @@ async function executeAgent(context, args) {
3600
3620
  startedAt: createdAt,
3601
3621
  completedAt: failedAt,
3602
3622
  worktreePath: runtime.workspaceDir,
3603
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3623
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3604
3624
  logRoot: runtime.logsDir,
3605
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3606
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3625
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3626
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3607
3627
  pid: process.pid,
3608
3628
  errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
3609
3629
  });
@@ -3620,10 +3640,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3620
3640
  startedAt: createdAt,
3621
3641
  completedAt,
3622
3642
  worktreePath: runtime.workspaceDir,
3623
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3643
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3624
3644
  logRoot: runtime.logsDir,
3625
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3626
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3645
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3646
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3627
3647
  pid: process.pid
3628
3648
  });
3629
3649
  return {
@@ -3697,7 +3717,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3697
3717
  import {
3698
3718
  chmodSync,
3699
3719
  copyFileSync as copyFileSync2,
3700
- existsSync as existsSync7,
3720
+ existsSync as existsSync8,
3701
3721
  mkdirSync as mkdirSync6,
3702
3722
  readdirSync,
3703
3723
  readlinkSync,
@@ -3707,7 +3727,7 @@ import {
3707
3727
  unlinkSync
3708
3728
  } from "fs";
3709
3729
  import { homedir as homedir4 } from "os";
3710
- import { resolve as resolve12 } from "path";
3730
+ import { resolve as resolve13 } from "path";
3711
3731
  import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
3712
3732
  import {
3713
3733
  computeRuntimeImageFingerprint,
@@ -3726,13 +3746,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
3726
3746
 
3727
3747
  // packages/cli/src/commands/dist.ts
3728
3748
  function collectRigValidatorBuildTargets(input) {
3729
- const validatorsRoot = resolve12(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3730
- if (!existsSync7(validatorsRoot))
3749
+ const validatorsRoot = resolve13(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3750
+ if (!existsSync8(validatorsRoot))
3731
3751
  return [];
3732
3752
  const targets = [];
3733
3753
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3734
3754
  for (const category of categories) {
3735
- const categoryDir = resolve12(validatorsRoot, category.name);
3755
+ const categoryDir = resolve13(validatorsRoot, category.name);
3736
3756
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3737
3757
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3738
3758
  continue;
@@ -3741,7 +3761,7 @@ function collectRigValidatorBuildTargets(input) {
3741
3761
  continue;
3742
3762
  targets.push({
3743
3763
  source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
3744
- dest: resolve12(input.imageDir, `bin/validators/${category.name}-${check}`),
3764
+ dest: resolve13(input.imageDir, `bin/validators/${category.name}-${check}`),
3745
3765
  cwd: input.hostProjectRoot
3746
3766
  });
3747
3767
  }
@@ -3750,16 +3770,16 @@ function collectRigValidatorBuildTargets(input) {
3750
3770
  }
3751
3771
  async function findLatestDistBinary(projectRoot) {
3752
3772
  const distRoot = resolveControlPlaneHostDistDir(projectRoot);
3753
- if (!existsSync7(distRoot)) {
3773
+ if (!existsSync8(distRoot)) {
3754
3774
  return null;
3755
3775
  }
3756
3776
  const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
3757
3777
  name: entry.name,
3758
- mtimeMs: statSync(resolve12(distRoot, entry.name)).mtimeMs
3778
+ mtimeMs: statSync(resolve13(distRoot, entry.name)).mtimeMs
3759
3779
  })).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
3760
3780
  for (const { name } of entries) {
3761
- const candidate = resolve12(distRoot, name, "bin", "rig");
3762
- if (existsSync7(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3781
+ const candidate = resolve13(distRoot, name, "bin", "rig");
3782
+ if (existsSync8(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3763
3783
  return candidate;
3764
3784
  }
3765
3785
  }
@@ -3771,7 +3791,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
3771
3791
  async function runDistDoctor(projectRoot) {
3772
3792
  const bunPath = Bun.which("bun");
3773
3793
  const rigPath = Bun.which("rig");
3774
- const userBinDir = resolve12(homedir4(), ".local/bin");
3794
+ const userBinDir = resolve13(homedir4(), ".local/bin");
3775
3795
  const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
3776
3796
  let rigRunnable = false;
3777
3797
  if (rigPath) {
@@ -3819,15 +3839,15 @@ async function executeDist(context, args) {
3819
3839
  let source = await findLatestDistBinary(context.projectRoot);
3820
3840
  let buildDir = null;
3821
3841
  if (!source) {
3822
- buildDir = resolve12(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3842
+ buildDir = resolve13(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3823
3843
  await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
3824
- source = resolve12(buildDir, "bin", "rig");
3844
+ source = resolve13(buildDir, "bin", "rig");
3825
3845
  }
3826
- if (!existsSync7(source)) {
3846
+ if (!existsSync8(source)) {
3827
3847
  throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
3828
3848
  }
3829
- const installedPath = resolve12(installDir, "rig");
3830
- if (existsSync7(installedPath)) {
3849
+ const installedPath = resolve13(installDir, "rig");
3850
+ if (existsSync8(installedPath)) {
3831
3851
  unlinkSync(installedPath);
3832
3852
  }
3833
3853
  copyFileSync2(source, installedPath);
@@ -3869,22 +3889,22 @@ async function executeDist(context, args) {
3869
3889
  requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
3870
3890
  const fp = await computeRuntimeImageFingerprint(context.projectRoot);
3871
3891
  const currentId = computeRuntimeImageId(fp);
3872
- const imagesDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3892
+ const imagesDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3873
3893
  mkdirSync6(imagesDir, { recursive: true });
3874
3894
  let pruned = 0;
3875
3895
  for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
3876
3896
  if (entry.isDirectory() && entry.name !== currentId) {
3877
- rmSync4(resolve12(imagesDir, entry.name), { recursive: true, force: true });
3897
+ rmSync4(resolve13(imagesDir, entry.name), { recursive: true, force: true });
3878
3898
  pruned++;
3879
3899
  }
3880
3900
  }
3881
3901
  if (pruned > 0 && context.outputMode === "text") {
3882
3902
  console.log(`Pruned ${pruned} stale image(s).`);
3883
3903
  }
3884
- const imageDir = resolve12(imagesDir, currentId);
3885
- mkdirSync6(resolve12(imageDir, "bin/hooks"), { recursive: true });
3886
- mkdirSync6(resolve12(imageDir, "bin/plugins"), { recursive: true });
3887
- mkdirSync6(resolve12(imageDir, "bin/validators"), { recursive: true });
3904
+ const imageDir = resolve13(imagesDir, currentId);
3905
+ mkdirSync6(resolve13(imageDir, "bin/hooks"), { recursive: true });
3906
+ mkdirSync6(resolve13(imageDir, "bin/plugins"), { recursive: true });
3907
+ mkdirSync6(resolve13(imageDir, "bin/validators"), { recursive: true });
3888
3908
  const hookNames = [
3889
3909
  "scope-guard",
3890
3910
  "import-guard",
@@ -3898,25 +3918,25 @@ async function executeDist(context, args) {
3898
3918
  ];
3899
3919
  const targets = [];
3900
3920
  const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || context.projectRoot;
3901
- targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve12(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
3902
- targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
3921
+ targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve13(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
3922
+ targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
3903
3923
  for (const hookName of hookNames) {
3904
3924
  const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
3905
- targets.push({ source: src, dest: resolve12(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
3906
- targets.push({ source: src, dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
3925
+ targets.push({ source: src, dest: resolve13(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
3926
+ targets.push({ source: src, dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
3907
3927
  }
3908
- const pluginsDir = resolve12(context.projectRoot, "rig/plugins");
3909
- const binPluginsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
3910
- const validatorsRoot = resolve12(hostProjectRoot, "packages/runtime/src/control-plane/validators");
3911
- const binValidatorsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
3928
+ const pluginsDir = resolve13(context.projectRoot, "rig/plugins");
3929
+ const binPluginsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
3930
+ const validatorsRoot = resolve13(hostProjectRoot, "packages/runtime/src/control-plane/validators");
3931
+ const binValidatorsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
3912
3932
  mkdirSync6(binPluginsDir, { recursive: true });
3913
3933
  mkdirSync6(binValidatorsDir, { recursive: true });
3914
- if (existsSync7(pluginsDir)) {
3934
+ if (existsSync8(pluginsDir)) {
3915
3935
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3916
3936
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3917
3937
  if (!m)
3918
3938
  continue;
3919
- targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve12(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3939
+ targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve13(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3920
3940
  }
3921
3941
  }
3922
3942
  targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
@@ -3927,17 +3947,17 @@ async function executeDist(context, args) {
3927
3947
  const isValidator = dest.includes("/bin/validators/");
3928
3948
  await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
3929
3949
  }
3930
- if (existsSync7(pluginsDir)) {
3950
+ if (existsSync8(pluginsDir)) {
3931
3951
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3932
3952
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3933
3953
  if (!m)
3934
3954
  continue;
3935
3955
  const pluginName = m[1];
3936
- const imageBin = resolve12(imageDir, `bin/plugins/${pluginName}`);
3956
+ const imageBin = resolve13(imageDir, `bin/plugins/${pluginName}`);
3937
3957
  if (!pluginName)
3938
3958
  continue;
3939
- const symlinkPath = resolve12(binPluginsDir, pluginName);
3940
- if (existsSync7(imageBin)) {
3959
+ const symlinkPath = resolve13(binPluginsDir, pluginName);
3960
+ if (existsSync8(imageBin)) {
3941
3961
  try {
3942
3962
  unlinkSync(symlinkPath);
3943
3963
  } catch {}
@@ -3945,10 +3965,10 @@ async function executeDist(context, args) {
3945
3965
  }
3946
3966
  }
3947
3967
  }
3948
- if (existsSync7(validatorsRoot)) {
3968
+ if (existsSync8(validatorsRoot)) {
3949
3969
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3950
3970
  for (const category of categories) {
3951
- const categoryDir = resolve12(validatorsRoot, category.name);
3971
+ const categoryDir = resolve13(validatorsRoot, category.name);
3952
3972
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3953
3973
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3954
3974
  continue;
@@ -3956,9 +3976,9 @@ async function executeDist(context, args) {
3956
3976
  if (!check || check === "index" || check === "shared")
3957
3977
  continue;
3958
3978
  const validatorName = `${category.name}-${check}`;
3959
- const imageBin = resolve12(imageDir, `bin/validators/${validatorName}`);
3960
- const symlinkPath = resolve12(binValidatorsDir, validatorName);
3961
- if (existsSync7(imageBin)) {
3979
+ const imageBin = resolve13(imageDir, `bin/validators/${validatorName}`);
3980
+ const symlinkPath = resolve13(binValidatorsDir, validatorName);
3981
+ if (existsSync8(imageBin)) {
3962
3982
  try {
3963
3983
  unlinkSync(symlinkPath);
3964
3984
  } catch {}
@@ -3967,18 +3987,18 @@ async function executeDist(context, args) {
3967
3987
  }
3968
3988
  }
3969
3989
  }
3970
- const agentsDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
3971
- if (existsSync7(agentsDir)) {
3990
+ const agentsDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
3991
+ if (existsSync8(agentsDir)) {
3972
3992
  let relinkCount = 0;
3973
3993
  for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
3974
3994
  if (!agentEntry.isDirectory())
3975
3995
  continue;
3976
- const agentBinDir = resolve12(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
3977
- if (!existsSync7(agentBinDir))
3996
+ const agentBinDir = resolve13(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
3997
+ if (!existsSync8(agentBinDir))
3978
3998
  continue;
3979
3999
  const walkDir = (dir) => {
3980
4000
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
3981
- const fullPath = resolve12(dir, entry.name);
4001
+ const fullPath = resolve13(dir, entry.name);
3982
4002
  if (entry.isDirectory()) {
3983
4003
  walkDir(fullPath);
3984
4004
  } else if (entry.isSymbolicLink()) {
@@ -4012,7 +4032,7 @@ async function executeDist(context, args) {
4012
4032
 
4013
4033
  // packages/cli/src/commands/inbox.ts
4014
4034
  import { writeFileSync as writeFileSync4 } from "fs";
4015
- import { resolve as resolve13 } from "path";
4035
+ import { resolve as resolve14 } from "path";
4016
4036
  import {
4017
4037
  listAuthorityRuns,
4018
4038
  readJsonlFile as readJsonlFile3,
@@ -4029,7 +4049,7 @@ async function executeInbox(context, args) {
4029
4049
  pending = task.rest;
4030
4050
  requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
4031
4051
  const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
4032
- const approvals = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
4052
+ const approvals = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
4033
4053
  runId: entry.runId,
4034
4054
  record
4035
4055
  })));
@@ -4057,7 +4077,7 @@ async function executeInbox(context, args) {
4057
4077
  if (decision.value !== "approve" && decision.value !== "reject") {
4058
4078
  throw new CliError2("decision must be approve or reject.");
4059
4079
  }
4060
- const approvalsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
4080
+ const approvalsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
4061
4081
  const approvals = readJsonlFile3(approvalsPath);
4062
4082
  const resolvedAt = new Date().toISOString();
4063
4083
  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);
@@ -4074,7 +4094,7 @@ async function executeInbox(context, args) {
4074
4094
  pending = task.rest;
4075
4095
  requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
4076
4096
  const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
4077
- const requests = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
4097
+ const requests = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
4078
4098
  runId: entry.runId,
4079
4099
  record
4080
4100
  })));
@@ -4116,7 +4136,7 @@ async function executeInbox(context, args) {
4116
4136
  const [key, ...restValue] = entry.split("=");
4117
4137
  return [key, restValue.join("=")];
4118
4138
  }));
4119
- const requestsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
4139
+ const requestsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
4120
4140
  const requests = readJsonlFile3(requestsPath);
4121
4141
  const resolvedAt = new Date().toISOString();
4122
4142
  const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
@@ -4131,14 +4151,14 @@ async function executeInbox(context, args) {
4131
4151
  }
4132
4152
 
4133
4153
  // packages/cli/src/commands/init.ts
4134
- import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
4154
+ import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
4135
4155
  import { spawnSync as spawnSync2 } from "child_process";
4136
- import { resolve as resolve16 } from "path";
4156
+ import { resolve as resolve17 } from "path";
4137
4157
  import { buildRigInitConfigSource } from "@rig/core";
4138
4158
 
4139
4159
  // packages/cli/src/commands/_snapshot-upload.ts
4140
4160
  import { mkdir, readdir, readFile, writeFile } from "fs/promises";
4141
- import { dirname as dirname2, resolve as resolve14, relative, sep } from "path";
4161
+ import { dirname as dirname2, resolve as resolve15, relative, sep } from "path";
4142
4162
  var SNAPSHOT_ARCHIVE_VERSION = 1;
4143
4163
  var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
4144
4164
  var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
@@ -4160,15 +4180,15 @@ function assertManifestPath(root, relativePath) {
4160
4180
  if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
4161
4181
  throw new Error(`Invalid snapshot path: ${relativePath}`);
4162
4182
  }
4163
- const resolved = resolve14(root, relativePath);
4183
+ const resolved = resolve15(root, relativePath);
4164
4184
  const relativeToRoot = relative(root, resolved);
4165
- if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve14(relativeToRoot) === resolved) {
4185
+ if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve15(relativeToRoot) === resolved) {
4166
4186
  throw new Error(`Snapshot path escapes project root: ${relativePath}`);
4167
4187
  }
4168
4188
  return resolved;
4169
4189
  }
4170
4190
  async function buildSnapshotUploadManifest(projectRoot, options = {}) {
4171
- const root = resolve14(projectRoot);
4191
+ const root = resolve15(projectRoot);
4172
4192
  const excludedDirectories = [...new Set([
4173
4193
  ...DEFAULT_EXCLUDED_DIRECTORIES,
4174
4194
  ...options.excludedDirectories ?? []
@@ -4180,7 +4200,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
4180
4200
  for (const entry of entries) {
4181
4201
  if (entry.isDirectory() && excludedSet.has(entry.name))
4182
4202
  continue;
4183
- const fullPath = resolve14(dir, entry.name);
4203
+ const fullPath = resolve15(dir, entry.name);
4184
4204
  if (entry.isDirectory()) {
4185
4205
  await visit(fullPath);
4186
4206
  continue;
@@ -4228,8 +4248,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
4228
4248
  }
4229
4249
 
4230
4250
  // packages/cli/src/commands/_doctor-checks.ts
4231
- import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
4232
- import { resolve as resolve15 } from "path";
4251
+ import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
4252
+ import { resolve as resolve16 } from "path";
4233
4253
  import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
4234
4254
  function check(id, label, status, detail, remediation) {
4235
4255
  return {
@@ -4269,11 +4289,11 @@ function repoSlugFromConfig(config) {
4269
4289
  function loadFallbackConfig(projectRoot) {
4270
4290
  const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
4271
4291
  for (const name of candidates) {
4272
- const path = resolve15(projectRoot, name);
4273
- if (!existsSync8(path))
4292
+ const path = resolve16(projectRoot, name);
4293
+ if (!existsSync9(path))
4274
4294
  continue;
4275
4295
  try {
4276
- const source = readFileSync4(path, "utf8");
4296
+ const source = readFileSync5(path, "utf8");
4277
4297
  if (name.endsWith(".json"))
4278
4298
  return JSON.parse(source);
4279
4299
  const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
@@ -4352,7 +4372,7 @@ async function runRigDoctorChecks(options) {
4352
4372
  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`)."));
4353
4373
  const loadedConfig = await loadConfig(projectRoot).catch(() => null);
4354
4374
  const config = loadedConfig ?? loadFallbackConfig(projectRoot);
4355
- const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync8(resolve15(projectRoot, name)));
4375
+ const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync9(resolve16(projectRoot, name)));
4356
4376
  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."));
4357
4377
  const taskSourceKind = config?.taskSource?.kind;
4358
4378
  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."));
@@ -4447,10 +4467,10 @@ function countDoctorFailures(checks) {
4447
4467
  }
4448
4468
 
4449
4469
  // packages/cli/src/commands/init.ts
4450
- var RIG_CONFIG_PACKAGE_VERSION = "0.0.6-alpha.0";
4470
+ var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
4451
4471
  var RIG_CONFIG_DEV_DEPENDENCIES = {
4452
- "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_VERSION}`,
4453
- "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_VERSION}`
4472
+ "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
4473
+ "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
4454
4474
  };
4455
4475
  function parseRepoSlugFromRemote(remoteUrl) {
4456
4476
  const trimmed = remoteUrl.trim();
@@ -4470,20 +4490,20 @@ function parseRepoSlug(value) {
4470
4490
  return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
4471
4491
  }
4472
4492
  function ensureRigPrivateDirs(projectRoot) {
4473
- const rigDir = resolve16(projectRoot, ".rig");
4474
- mkdirSync7(resolve16(rigDir, "state"), { recursive: true });
4475
- mkdirSync7(resolve16(rigDir, "logs"), { recursive: true });
4476
- mkdirSync7(resolve16(rigDir, "runs"), { recursive: true });
4477
- mkdirSync7(resolve16(rigDir, "tmp"), { recursive: true });
4478
- mkdirSync7(resolve16(projectRoot, "artifacts"), { recursive: true });
4479
- const taskConfigPath = resolve16(rigDir, "task-config.json");
4480
- if (!existsSync9(taskConfigPath))
4493
+ const rigDir = resolve17(projectRoot, ".rig");
4494
+ mkdirSync7(resolve17(rigDir, "state"), { recursive: true });
4495
+ mkdirSync7(resolve17(rigDir, "logs"), { recursive: true });
4496
+ mkdirSync7(resolve17(rigDir, "runs"), { recursive: true });
4497
+ mkdirSync7(resolve17(rigDir, "tmp"), { recursive: true });
4498
+ mkdirSync7(resolve17(projectRoot, "artifacts"), { recursive: true });
4499
+ const taskConfigPath = resolve17(rigDir, "task-config.json");
4500
+ if (!existsSync10(taskConfigPath))
4481
4501
  writeFileSync5(taskConfigPath, `{}
4482
4502
  `, "utf-8");
4483
4503
  }
4484
4504
  function ensureGitignoreEntries(projectRoot) {
4485
- const path = resolve16(projectRoot, ".gitignore");
4486
- const existing = existsSync9(path) ? readFileSync5(path, "utf8") : "";
4505
+ const path = resolve17(projectRoot, ".gitignore");
4506
+ const existing = existsSync10(path) ? readFileSync6(path, "utf8") : "";
4487
4507
  const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
4488
4508
  const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
4489
4509
  if (missing.length === 0)
@@ -4496,14 +4516,14 @@ function ensureGitignoreEntries(projectRoot) {
4496
4516
  `, "utf8");
4497
4517
  }
4498
4518
  function ensureRigConfigPackageDependencies(projectRoot) {
4499
- const path = resolve16(projectRoot, "package.json");
4500
- const existing = existsSync9(path) ? JSON.parse(readFileSync5(path, "utf8")) : {};
4519
+ const path = resolve17(projectRoot, "package.json");
4520
+ const existing = existsSync10(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
4501
4521
  const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
4502
4522
  for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
4503
4523
  devDependencies[name] = spec;
4504
4524
  }
4505
4525
  const next = {
4506
- ...existsSync9(path) ? existing : { name: "rig-project", private: true },
4526
+ ...existsSync10(path) ? existing : { name: "rig-project", private: true },
4507
4527
  devDependencies
4508
4528
  };
4509
4529
  writeFileSync5(path, `${JSON.stringify(next, null, 2)}
@@ -4573,15 +4593,54 @@ async function promptSelect(prompts, options) {
4573
4593
  throw new CliError2("Init cancelled.", 1);
4574
4594
  return String(value);
4575
4595
  }
4576
- async function pollDeviceAuthOnce(context, pollId) {
4596
+ function sleep2(ms) {
4597
+ return new Promise((resolve18) => setTimeout(resolve18, ms));
4598
+ }
4599
+ function positiveIntFromEnv(name, fallback) {
4600
+ const value = Number.parseInt(process.env[name] ?? "", 10);
4601
+ return Number.isFinite(value) && value >= 0 ? value : fallback;
4602
+ }
4603
+ function apiSessionTokenFrom(payload) {
4604
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
4605
+ return null;
4606
+ const token = payload.apiSessionToken;
4607
+ return typeof token === "string" && token.trim() ? token.trim() : null;
4608
+ }
4609
+ function writeRemoteGitHubAuthState(projectRoot, input) {
4610
+ writeFileSync5(resolve17(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
4611
+ authenticated: true,
4612
+ source: input.source,
4613
+ storedOnServer: true,
4614
+ selectedRepo: input.selectedRepo,
4615
+ ...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
4616
+ updatedAt: new Date().toISOString()
4617
+ }, null, 2)}
4618
+ `, "utf8");
4619
+ }
4620
+ async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
4577
4621
  if (typeof pollId !== "string" || !pollId.trim())
4578
4622
  return null;
4579
- const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
4580
- method: "POST",
4581
- headers: { "content-type": "application/json" },
4582
- body: JSON.stringify({ pollId })
4583
- }).catch(() => null);
4584
- return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
4623
+ const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
4624
+ const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
4625
+ const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
4626
+ const deadline = Date.now() + timeoutMs;
4627
+ let last = null;
4628
+ do {
4629
+ const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
4630
+ method: "POST",
4631
+ headers: { "content-type": "application/json" },
4632
+ body: JSON.stringify({ pollId })
4633
+ }).catch(() => null);
4634
+ last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
4635
+ const status = typeof last?.status === "string" ? last.status : null;
4636
+ if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
4637
+ return last;
4638
+ }
4639
+ if (timeoutMs <= 0)
4640
+ return last;
4641
+ await sleep2(intervalMs);
4642
+ } while (Date.now() < deadline);
4643
+ return last;
4585
4644
  }
4586
4645
  async function runControlPlaneInit(context, options) {
4587
4646
  const projectRoot = context.projectRoot;
@@ -4604,9 +4663,9 @@ async function runControlPlaneInit(context, options) {
4604
4663
  });
4605
4664
  ensureRigPrivateDirs(projectRoot);
4606
4665
  ensureGitignoreEntries(projectRoot);
4607
- const configTsPath = resolve16(projectRoot, "rig.config.ts");
4608
- const configJsonPath = resolve16(projectRoot, "rig.config.json");
4609
- const configExists = existsSync9(configTsPath) || existsSync9(configJsonPath);
4666
+ const configTsPath = resolve17(projectRoot, "rig.config.ts");
4667
+ const configJsonPath = resolve17(projectRoot, "rig.config.json");
4668
+ const configExists = existsSync10(configTsPath) || existsSync10(configJsonPath);
4610
4669
  if (!options.privateStateOnly) {
4611
4670
  if (configExists && !options.repair) {
4612
4671
  if (context.outputMode !== "json")
@@ -4622,7 +4681,7 @@ async function runControlPlaneInit(context, options) {
4622
4681
  }
4623
4682
  ensureRigConfigPackageDependencies(projectRoot);
4624
4683
  }
4625
- writeFileSync5(resolve16(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4684
+ writeFileSync5(resolve17(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4626
4685
  `, "utf8");
4627
4686
  const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
4628
4687
  let uploadedSnapshot = null;
@@ -4644,10 +4703,14 @@ async function runControlPlaneInit(context, options) {
4644
4703
  const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
4645
4704
  if (token) {
4646
4705
  githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
4647
- setGitHubBearerTokenForCurrentProcess(token);
4706
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4707
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4648
4708
  if (serverKind === "remote") {
4649
- writeFileSync5(resolve16(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)}
4650
- `, "utf8");
4709
+ writeRemoteGitHubAuthState(projectRoot, {
4710
+ source: authMethod === "gh" ? "gh" : "init-token",
4711
+ selectedRepo: repo.slug,
4712
+ apiSessionToken
4713
+ });
4651
4714
  }
4652
4715
  } else if (authMethod === "device") {
4653
4716
  const payload = await requestServerJson(context, "/api/github/auth/device/start", {
@@ -4656,9 +4719,22 @@ async function runControlPlaneInit(context, options) {
4656
4719
  body: JSON.stringify({ repoSlug: repo.slug })
4657
4720
  });
4658
4721
  deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
4659
- const completed = await pollDeviceAuthOnce(context, deviceAuth.pollId);
4660
- if (completed)
4722
+ if (context.outputMode !== "json") {
4723
+ const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
4724
+ const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
4725
+ console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
4726
+ }
4727
+ const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
4728
+ if (completed) {
4729
+ const apiSessionToken = apiSessionTokenFrom(completed);
4730
+ if (apiSessionToken) {
4731
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken);
4732
+ if (serverKind === "remote") {
4733
+ writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken });
4734
+ }
4735
+ }
4661
4736
  deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
4737
+ }
4662
4738
  }
4663
4739
  let remoteCheckoutPreparation = null;
4664
4740
  if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
@@ -4678,6 +4754,12 @@ async function runControlPlaneInit(context, options) {
4678
4754
  });
4679
4755
  const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
4680
4756
  const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
4757
+ if (serverRootSwitch && token) {
4758
+ githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
4759
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4760
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4761
+ writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken });
4762
+ }
4681
4763
  const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
4682
4764
  const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
4683
4765
  remote: true,
@@ -4787,7 +4869,7 @@ function parseInitOptions(args) {
4787
4869
  async function runInteractiveControlPlaneInit(context, prompts) {
4788
4870
  prompts.intro?.("Initialize a Rig control-plane project");
4789
4871
  const projectRoot = context.projectRoot;
4790
- const existingConfig = existsSync9(resolve16(projectRoot, "rig.config.ts")) || existsSync9(resolve16(projectRoot, "rig.config.json"));
4872
+ const existingConfig = existsSync10(resolve17(projectRoot, "rig.config.ts")) || existsSync10(resolve17(projectRoot, "rig.config.json"));
4791
4873
  let repair = false;
4792
4874
  let privateStateOnly = false;
4793
4875
  if (existingConfig) {
@@ -4887,7 +4969,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
4887
4969
  });
4888
4970
  const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
4889
4971
  const deviceAuth = details.deviceAuth && typeof details.deviceAuth === "object" && !Array.isArray(details.deviceAuth) ? details.deviceAuth : null;
4890
- 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")}.` : "";
4972
+ 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")}.` : "";
4891
4973
  prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
4892
4974
  return result;
4893
4975
  }
@@ -5049,8 +5131,8 @@ async function executeDoctor(context, args) {
5049
5131
  }
5050
5132
 
5051
5133
  // packages/cli/src/commands/_run-driver-helpers.ts
5052
- import { readFileSync as readFileSync6 } from "fs";
5053
- import { resolve as resolve17 } from "path";
5134
+ import { readFileSync as readFileSync7 } from "fs";
5135
+ import { resolve as resolve18 } from "path";
5054
5136
  import {
5055
5137
  appendJsonlRecord as appendJsonlRecord2,
5056
5138
  readAuthorityRun as readAuthorityRun2,
@@ -5070,7 +5152,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
5070
5152
  ...patch,
5071
5153
  updatedAt: new Date().toISOString()
5072
5154
  };
5073
- writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
5155
+ writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
5074
5156
  return next;
5075
5157
  }
5076
5158
  function touchAuthorityRun(projectRoot, runId) {
@@ -5078,21 +5160,21 @@ function touchAuthorityRun(projectRoot, runId) {
5078
5160
  if (!current) {
5079
5161
  return;
5080
5162
  }
5081
- writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
5163
+ writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
5082
5164
  ...current,
5083
5165
  updatedAt: new Date().toISOString()
5084
5166
  });
5085
5167
  }
5086
5168
  function appendRunTimeline(projectRoot, runId, value) {
5087
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
5169
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
5088
5170
  touchAuthorityRun(projectRoot, runId);
5089
5171
  }
5090
5172
  function appendRunLog(projectRoot, runId, value) {
5091
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
5173
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
5092
5174
  touchAuthorityRun(projectRoot, runId);
5093
5175
  }
5094
5176
  function appendRunAction(projectRoot, runId, value) {
5095
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
5177
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
5096
5178
  id: value.id,
5097
5179
  type: "action",
5098
5180
  actionType: value.actionType,
@@ -5163,7 +5245,7 @@ function buildRunPrompt(input) {
5163
5245
  })();
5164
5246
  const scopeText = (() => {
5165
5247
  try {
5166
- const parsed = JSON.parse(readFileSync6(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
5248
+ const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
5167
5249
  const entry = parsed[input.taskId] ?? {};
5168
5250
  const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
5169
5251
  const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
@@ -5276,8 +5358,8 @@ function renderSourceScopeValidation(task, validation) {
5276
5358
  }
5277
5359
 
5278
5360
  // packages/cli/src/commands/inspect.ts
5279
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
5280
- import { resolve as resolve18 } from "path";
5361
+ import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
5362
+ import { resolve as resolve19 } from "path";
5281
5363
  import {
5282
5364
  listAuthorityRuns as listAuthorityRuns2,
5283
5365
  readAuthorityRun as readAuthorityRun3,
@@ -5298,8 +5380,8 @@ async function executeInspect(context, args) {
5298
5380
  if (!latestRun) {
5299
5381
  throw new CliError2(`No runs found for ${requiredTask}.`);
5300
5382
  }
5301
- const logsPath = resolve18(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5302
- if (!existsSync10(logsPath)) {
5383
+ const logsPath = resolve19(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5384
+ if (!existsSync11(logsPath)) {
5303
5385
  throw new CliError2(`No logs found for run ${latestRun.runId}.`);
5304
5386
  }
5305
5387
  await context.runCommand(["cat", logsPath]);
@@ -5309,7 +5391,7 @@ async function executeInspect(context, args) {
5309
5391
  const { value: task, rest: remaining } = takeOption(rest, "--task");
5310
5392
  requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
5311
5393
  const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
5312
- const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync10(path));
5394
+ const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync11(path));
5313
5395
  if (!artifactRoot) {
5314
5396
  throw new CliError2(`No artifacts found for ${requiredTask}.`);
5315
5397
  }
@@ -5366,10 +5448,10 @@ async function executeInspect(context, args) {
5366
5448
  case "failures": {
5367
5449
  requireNoExtraArgs(rest, "bun run rig inspect failures");
5368
5450
  const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
5369
- if (!existsSync10(failed)) {
5451
+ if (!existsSync11(failed)) {
5370
5452
  console.log("No failures recorded.");
5371
5453
  } else {
5372
- process.stdout.write(readFileSync7(failed, "utf-8"));
5454
+ process.stdout.write(readFileSync8(failed, "utf-8"));
5373
5455
  }
5374
5456
  return { ok: true, group: "inspect", command };
5375
5457
  }
@@ -5386,11 +5468,11 @@ async function executeInspect(context, args) {
5386
5468
  return { ok: true, group: "inspect", command };
5387
5469
  case "audit": {
5388
5470
  requireNoExtraArgs(rest, "bun run rig inspect audit");
5389
- const auditPath = resolve18(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5390
- if (!existsSync10(auditPath)) {
5471
+ const auditPath = resolve19(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5472
+ if (!existsSync11(auditPath)) {
5391
5473
  console.log("No audit log found.");
5392
5474
  } else {
5393
- const lines = readFileSync7(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5475
+ const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5394
5476
  for (const line of lines) {
5395
5477
  console.log(line);
5396
5478
  }
@@ -6019,8 +6101,8 @@ async function executeRemote(context, args) {
6019
6101
  }
6020
6102
 
6021
6103
  // packages/cli/src/commands/run.ts
6022
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
6023
- import { resolve as resolve19 } from "path";
6104
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
6105
+ import { resolve as resolve20 } from "path";
6024
6106
  import { createInterface as createInterface2 } from "readline/promises";
6025
6107
  import {
6026
6108
  listAuthorityRuns as listAuthorityRuns3,
@@ -6034,6 +6116,7 @@ import {
6034
6116
  listOpenEpics,
6035
6117
  resolveDefaultEpic,
6036
6118
  runResume,
6119
+ runRestart,
6037
6120
  runStatus,
6038
6121
  runStop,
6039
6122
  startRun,
@@ -6142,6 +6225,17 @@ async function attachRunOperatorView(context, input) {
6142
6225
  }
6143
6226
 
6144
6227
  // packages/cli/src/commands/run.ts
6228
+ function normalizeRemoteRunDetails(payload) {
6229
+ const run = payload.run;
6230
+ if (!run || typeof run !== "object" || Array.isArray(run))
6231
+ return null;
6232
+ return {
6233
+ ...run,
6234
+ ...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
6235
+ ...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
6236
+ ...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
6237
+ };
6238
+ }
6145
6239
  function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
6146
6240
  if (noEpicPrompt) {
6147
6241
  return false;
@@ -6280,7 +6374,7 @@ async function executeRun(context, args) {
6280
6374
  if (!run.value) {
6281
6375
  throw new CliError2("run show requires --run <id>.");
6282
6376
  }
6283
- const record = readAuthorityRun4(context.projectRoot, run.value);
6377
+ const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
6284
6378
  if (!record) {
6285
6379
  throw new CliError2(`Run not found: ${run.value}`, 2);
6286
6380
  }
@@ -6299,7 +6393,7 @@ async function executeRun(context, args) {
6299
6393
  if (!run.value) {
6300
6394
  throw new CliError2("run timeline requires --run <id>.");
6301
6395
  }
6302
- const timelinePath = resolve19(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6396
+ const timelinePath = resolve20(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6303
6397
  const printEvents = () => {
6304
6398
  const events2 = readJsonlFile4(timelinePath);
6305
6399
  if (context.outputMode === "text") {
@@ -6311,12 +6405,12 @@ async function executeRun(context, args) {
6311
6405
  };
6312
6406
  const events = printEvents();
6313
6407
  if (follow.value && context.outputMode === "text") {
6314
- let lastLength = existsSync11(timelinePath) ? readFileSync8(timelinePath, "utf8").length : 0;
6408
+ let lastLength = existsSync12(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
6315
6409
  while (true) {
6316
6410
  await Bun.sleep(1000);
6317
- if (!existsSync11(timelinePath))
6411
+ if (!existsSync12(timelinePath))
6318
6412
  continue;
6319
- const next = readFileSync8(timelinePath, "utf8");
6413
+ const next = readFileSync9(timelinePath, "utf8");
6320
6414
  if (next.length <= lastLength)
6321
6415
  continue;
6322
6416
  const delta = next.slice(lastLength);
@@ -6461,6 +6555,20 @@ async function executeRun(context, args) {
6461
6555
  }
6462
6556
  return { ok: true, group: "run", command, details: resumed };
6463
6557
  }
6558
+ case "restart": {
6559
+ requireNoExtraArgs(rest, "bun run rig run restart");
6560
+ if (context.dryRun) {
6561
+ if (context.outputMode === "text") {
6562
+ console.log("[dry-run] rig run restart");
6563
+ }
6564
+ return { ok: true, group: "run", command };
6565
+ }
6566
+ const restarted = await runRestart(context.projectRoot, runtimeContext);
6567
+ if (context.outputMode === "text") {
6568
+ console.log(`Restarted run: ${restarted.runId}`);
6569
+ }
6570
+ return { ok: true, group: "run", command, details: restarted };
6571
+ }
6464
6572
  case "stop": {
6465
6573
  const runOption = takeOption(rest, "--run");
6466
6574
  const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
@@ -6518,7 +6626,7 @@ async function executeServer(context, args, options) {
6518
6626
  const authTokenResult = takeOption(pending, "--auth-token");
6519
6627
  pending = authTokenResult.rest;
6520
6628
  requireNoExtraArgs(pending, "bun run rig server start [--host <host>] [--port <n>] [--poll-ms <n>] [--auth-token <token>]");
6521
- const commandParts = ["bun", "run", "packages/server/src/server.ts", "start"];
6629
+ const commandParts = ["rig-server", "start"];
6522
6630
  if (hostResult.value) {
6523
6631
  commandParts.push("--host", hostResult.value);
6524
6632
  }
@@ -6541,7 +6649,7 @@ async function executeServer(context, args, options) {
6541
6649
  const eventResult = takeOption(pending, "--event");
6542
6650
  pending = eventResult.rest;
6543
6651
  requireNoExtraArgs(pending, "bun run rig server notify-test [--event <type>]");
6544
- const commandParts = ["bun", "run", "packages/server/src/server.ts", "notify-test"];
6652
+ const commandParts = ["rig-server", "notify-test"];
6545
6653
  if (eventResult.value) {
6546
6654
  commandParts.push("--event", eventResult.value);
6547
6655
  }
@@ -6602,10 +6710,10 @@ async function executeServer(context, args, options) {
6602
6710
  }
6603
6711
 
6604
6712
  // packages/cli/src/commands/task.ts
6605
- import { readFileSync as readFileSync9 } from "fs";
6713
+ import { readFileSync as readFileSync10 } from "fs";
6606
6714
  import { spawnSync as spawnSync4 } from "child_process";
6607
6715
  import { createInterface as createInterface4 } from "readline/promises";
6608
- import { resolve as resolve20 } from "path";
6716
+ import { resolve as resolve21 } from "path";
6609
6717
  import {
6610
6718
  taskArtifactDir,
6611
6719
  taskArtifacts,
@@ -6936,7 +7044,7 @@ async function executeTask(context, args, options) {
6936
7044
  const fileFlag = takeOption(rest.slice(1), "--file");
6937
7045
  let content;
6938
7046
  if (fileFlag.value) {
6939
- content = readFileSync9(resolve20(context.projectRoot, fileFlag.value), "utf-8");
7047
+ content = readFileSync10(resolve21(context.projectRoot, fileFlag.value), "utf-8");
6940
7048
  } else {
6941
7049
  content = await readStdin();
6942
7050
  }
@@ -7157,8 +7265,8 @@ async function executeTask(context, args, options) {
7157
7265
  }
7158
7266
 
7159
7267
  // packages/cli/src/commands/task-run-driver.ts
7160
- import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync10, statSync as statSync2 } from "fs";
7161
- import { resolve as resolve21 } from "path";
7268
+ import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
7269
+ import { resolve as resolve22 } from "path";
7162
7270
  import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
7163
7271
  import { createInterface as createLineInterface } from "readline";
7164
7272
  import { loadConfig as loadConfig2 } from "@rig/core/load-config";
@@ -7192,7 +7300,24 @@ import {
7192
7300
  commitRunChanges,
7193
7301
  runPrAutomation
7194
7302
  } from "@rig/runtime/control-plane/native/pr-automation";
7303
+ function looksLikeGitHubToken(value) {
7304
+ const token = value?.trim();
7305
+ if (!token)
7306
+ return false;
7307
+ return /^(gh[opusr]_|github_pat_)/.test(token);
7308
+ }
7309
+ function githubBridgeEnv(token) {
7310
+ const clean = token?.trim();
7311
+ if (!clean)
7312
+ return {};
7313
+ return {
7314
+ RIG_GITHUB_TOKEN: clean,
7315
+ GITHUB_TOKEN: clean,
7316
+ GH_TOKEN: clean
7317
+ };
7318
+ }
7195
7319
  function buildPiRigBridgeEnv(input) {
7320
+ const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
7196
7321
  return {
7197
7322
  RIG_PROJECT_ROOT: input.projectRoot,
7198
7323
  PROJECT_RIG_ROOT: input.projectRoot,
@@ -7202,7 +7327,7 @@ function buildPiRigBridgeEnv(input) {
7202
7327
  RIG_RUNTIME_ADAPTER: "pi",
7203
7328
  ...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
7204
7329
  ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
7205
- ...input.githubToken ? { RIG_GITHUB_TOKEN: input.githubToken } : {}
7330
+ ...githubBridgeEnv(githubToken)
7206
7331
  };
7207
7332
  }
7208
7333
  function runGitSync(cwd, args, input) {
@@ -7224,12 +7349,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
7224
7349
  return 0;
7225
7350
  let copied = 0;
7226
7351
  for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
7227
- const sourcePath = resolve21(sourceRoot, relativePath);
7228
- const targetPath = resolve21(targetRoot, relativePath);
7352
+ const sourcePath = resolve22(sourceRoot, relativePath);
7353
+ const targetPath = resolve22(targetRoot, relativePath);
7229
7354
  try {
7230
7355
  if (!statSync2(sourcePath).isFile())
7231
7356
  continue;
7232
- mkdirSync8(resolve21(targetPath, ".."), { recursive: true });
7357
+ mkdirSync8(resolve22(targetPath, ".."), { recursive: true });
7233
7358
  copyFileSync3(sourcePath, targetPath);
7234
7359
  copied += 1;
7235
7360
  } catch {}
@@ -7263,6 +7388,14 @@ function buildTaskRunReviewEnv(config) {
7263
7388
  ...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
7264
7389
  };
7265
7390
  }
7391
+ function buildDirtyBaselineHandshakeEnv(input) {
7392
+ if (input.baselineMode !== "dirty-snapshot")
7393
+ return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
7394
+ return {
7395
+ RIG_BASELINE_MODE: "dirty-snapshot",
7396
+ RIG_DIRTY_BASELINE_READY_FILE: resolve22(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
7397
+ };
7398
+ }
7266
7399
  function positiveInt(value, fallback) {
7267
7400
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
7268
7401
  }
@@ -7355,6 +7488,12 @@ function appendPiStageLog(input) {
7355
7488
  });
7356
7489
  emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
7357
7490
  }
7491
+ async function runCheckedCommand(command, args, cwd, label = "git") {
7492
+ const result = await command(args, { cwd });
7493
+ if (result.exitCode !== 0) {
7494
+ throw new Error(`${label} ${args.join(" ")} failed (${result.exitCode}): ${result.stderr ?? result.stdout ?? ""}`.trim());
7495
+ }
7496
+ }
7358
7497
  function createCommandRunner(binary) {
7359
7498
  return async (args, options) => {
7360
7499
  const child = spawn2(binary, [...args], {
@@ -7365,9 +7504,9 @@ function createCommandRunner(binary) {
7365
7504
  const stderrChunks = [];
7366
7505
  child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7367
7506
  child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7368
- return await new Promise((resolve22) => {
7369
- child.once("error", (error) => resolve22({ exitCode: 1, stderr: error.message }));
7370
- child.once("close", (code) => resolve22({
7507
+ return await new Promise((resolve23) => {
7508
+ child.once("error", (error) => resolve23({ exitCode: 1, stderr: error.message }));
7509
+ child.once("close", (code) => resolve23({
7371
7510
  exitCode: code ?? 1,
7372
7511
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
7373
7512
  stderr: Buffer.concat(stderrChunks).toString("utf8")
@@ -7430,6 +7569,7 @@ async function runTaskRunPostValidationLifecycle(input) {
7430
7569
  command: gitCommand
7431
7570
  });
7432
7571
  stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
7572
+ await runCheckedCommand(gitCommand, ["push", "--set-upstream", "origin", branch], workspace, "git");
7433
7573
  stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
7434
7574
  const pr = await prAutomation({
7435
7575
  projectRoot: workspace,
@@ -7440,7 +7580,6 @@ async function runTaskRunPostValidationLifecycle(input) {
7440
7580
  sourceTask: input.sourceTask,
7441
7581
  uploadedSnapshot: input.uploadedSnapshot,
7442
7582
  command: ghCommand,
7443
- gitCommand,
7444
7583
  steerPi,
7445
7584
  lifecycle: {
7446
7585
  onPrOpened: async ({ prUrl }) => {
@@ -7563,7 +7702,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
7563
7702
  return null;
7564
7703
  }
7565
7704
  for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
7566
- const summary = readJsonFile3(resolve21(artifactDir, "validation-summary.json"), null);
7705
+ const summary = readJsonFile3(resolve22(artifactDir, "validation-summary.json"), null);
7567
7706
  if (!summary || summary.status !== "fail") {
7568
7707
  continue;
7569
7708
  }
@@ -7644,9 +7783,9 @@ function readTaskRunAcceptedArtifactState(input) {
7644
7783
  if (!input.taskId || !input.workspaceDir) {
7645
7784
  return { accepted: false, reason: null };
7646
7785
  }
7647
- const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7648
- const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7649
- const taskResultPath = resolve21(artifactDir, "task-result.json");
7786
+ const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7787
+ const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7788
+ const taskResultPath = resolve22(artifactDir, "task-result.json");
7650
7789
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7651
7790
  if (reviewStatus !== "APPROVED") {
7652
7791
  return { accepted: false, reason: null };
@@ -7683,12 +7822,12 @@ function resolveTaskRunRetryContext(input) {
7683
7822
  if (!input.taskId || !input.workspaceDir) {
7684
7823
  return { shouldRetry: false, failureDetail: null, nextPrompt: null };
7685
7824
  }
7686
- const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7687
- const reviewStatePath = resolve21(artifactDir, "review-state.json");
7688
- const reviewFeedbackPath = resolve21(artifactDir, "review-feedback.md");
7689
- const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7690
- const failedApproachesPath = resolve21(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7691
- const validationSummaryPath = resolve21(artifactDir, "validation-summary.json");
7825
+ const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7826
+ const reviewStatePath = resolve22(artifactDir, "review-state.json");
7827
+ const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
7828
+ const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7829
+ const failedApproachesPath = resolve22(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7830
+ const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
7692
7831
  const reviewState = readJsonFile3(reviewStatePath, null);
7693
7832
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7694
7833
  const reviewRejected = isTaskRunReviewRejected(reviewState);
@@ -7743,11 +7882,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
7743
7882
  return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
7744
7883
  }
7745
7884
  function readTaskRunReviewStatus(reviewStatusPath) {
7746
- if (!existsSync12(reviewStatusPath)) {
7885
+ if (!existsSync13(reviewStatusPath)) {
7747
7886
  return null;
7748
7887
  }
7749
7888
  try {
7750
- const status = readFileSync10(reviewStatusPath, "utf8").trim().toUpperCase();
7889
+ const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
7751
7890
  return status === "APPROVED" || status === "REJECTED" ? status : null;
7752
7891
  } catch {
7753
7892
  return null;
@@ -7830,8 +7969,11 @@ function stringArrayField(record, key) {
7830
7969
  }
7831
7970
  async function executeRigOwnedTaskRun(context, input) {
7832
7971
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
7972
+ const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
7973
+ const resumeMode = process.env.RIG_RUN_RESUME === "1";
7974
+ const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
7833
7975
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
7834
- const prompt = buildRunPrompt({
7976
+ let prompt = buildRunPrompt({
7835
7977
  projectRoot: context.projectRoot,
7836
7978
  taskId: input.taskId,
7837
7979
  fallbackTitle: input.title,
@@ -7885,14 +8027,14 @@ async function executeRigOwnedTaskRun(context, input) {
7885
8027
  taskId: runtimeTaskId,
7886
8028
  createdAt: startedAt,
7887
8029
  runtimeAdapter: input.runtimeAdapter,
7888
- status: "created"
8030
+ status: resumeMode ? "preparing" : "created"
7889
8031
  });
7890
8032
  patchAuthorityRun(context.projectRoot, input.runId, {
7891
8033
  status: "preparing",
7892
8034
  startedAt,
7893
8035
  completedAt: null,
7894
8036
  errorText: null,
7895
- artifactRoot: null,
8037
+ artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
7896
8038
  runtimeAdapter: input.runtimeAdapter,
7897
8039
  runtimeMode: input.runtimeMode,
7898
8040
  interactionMode: input.interactionMode,
@@ -7908,9 +8050,9 @@ async function executeRigOwnedTaskRun(context, input) {
7908
8050
  detail: input.taskId ?? input.title ?? runtimeTaskId
7909
8051
  });
7910
8052
  appendRunLog(context.projectRoot, input.runId, {
7911
- id: `log:${input.runId}:start`,
7912
- title: "Rig task run started",
7913
- detail: input.taskId ?? input.title ?? runtimeTaskId,
8053
+ id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
8054
+ title: resumeMode ? "Rig task run resumed" : "Rig task run started",
8055
+ detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
7914
8056
  tone: "info",
7915
8057
  status: "preparing",
7916
8058
  createdAt: startedAt
@@ -7931,7 +8073,22 @@ async function executeRigOwnedTaskRun(context, input) {
7931
8073
  const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
7932
8074
  const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
7933
8075
  const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
7934
- patchAuthorityRun(context.projectRoot, input.runId, { planning: planningClassification });
8076
+ const planningArtifactPath = resolve22("artifacts", runtimeTaskId, "implementation-plan.md");
8077
+ const persistedPlanning = {
8078
+ ...planningClassification,
8079
+ classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
8080
+ artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
8081
+ classifiedAt: new Date().toISOString()
8082
+ };
8083
+ mkdirSync8(resolve22(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
8084
+ writeFileSync6(resolve22(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
8085
+ `, "utf8");
8086
+ patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
8087
+ prompt = `${prompt}
8088
+
8089
+ Rig planning classification:
8090
+ ${JSON.stringify(persistedPlanning, null, 2)}
8091
+ ${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."}`;
7935
8092
  if (input.runtimeAdapter === "pi") {
7936
8093
  for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
7937
8094
  appendPiStageLog({
@@ -7973,11 +8130,11 @@ async function executeRigOwnedTaskRun(context, input) {
7973
8130
  let reviewAction;
7974
8131
  let verificationStarted = false;
7975
8132
  let reviewStarted = false;
7976
- let latestRuntimeWorkspace = null;
7977
- let latestSessionDir = null;
7978
- let latestLogsDir = null;
8133
+ let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
8134
+ let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve22(existingRunRecord.sessionPath, "..") : null;
8135
+ let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
7979
8136
  let latestProviderCommand = null;
7980
- let latestRuntimeBranch = null;
8137
+ let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
7981
8138
  let snapshotSidecarPromise = null;
7982
8139
  let dirtyBaselineApplied = false;
7983
8140
  const childEnv = {
@@ -7995,9 +8152,14 @@ async function executeRigOwnedTaskRun(context, input) {
7995
8152
  RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
7996
8153
  RIG_SERVER_RUN_ID: input.runId
7997
8154
  },
7998
- ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
8155
+ ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
8156
+ ...resumeMode ? {
8157
+ RIG_RUN_RESUME: "1",
8158
+ RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
8159
+ } : {}
7999
8160
  };
8000
8161
  Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
8162
+ Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
8001
8163
  const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
8002
8164
  const maxAttempts = automationLimits.maxValidationAttempts;
8003
8165
  const promoteToValidating = (detail) => {
@@ -8052,22 +8214,29 @@ async function executeRigOwnedTaskRun(context, input) {
8052
8214
  patchAuthorityRun(context.projectRoot, input.runId, {
8053
8215
  status: "running",
8054
8216
  worktreePath: latestRuntimeWorkspace,
8055
- artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve21(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8217
+ artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve22(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8056
8218
  logRoot: latestLogsDir,
8057
- sessionPath: latestSessionDir ? resolve21(latestSessionDir, "session.json") : null,
8058
- sessionLogPath: latestLogsDir ? resolve21(latestLogsDir, "agent-stdout.log") : null,
8219
+ sessionPath: latestSessionDir ? resolve22(latestSessionDir, "session.json") : null,
8220
+ sessionLogPath: latestLogsDir ? resolve22(latestLogsDir, "agent-stdout.log") : null,
8059
8221
  branch: runtimeId
8060
8222
  });
8061
8223
  if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
8062
8224
  dirtyBaselineApplied = true;
8063
8225
  const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
8226
+ const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
8227
+ if (readyFile) {
8228
+ mkdirSync8(resolve22(readyFile, ".."), { recursive: true });
8229
+ writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
8230
+ `, "utf8");
8231
+ }
8064
8232
  appendRunLog(context.projectRoot, input.runId, {
8065
8233
  id: `log:${input.runId}:dirty-baseline`,
8066
8234
  title: "Dirty baseline snapshot",
8067
8235
  detail: dirty.detail,
8068
8236
  tone: dirty.applied ? "tool" : "info",
8069
8237
  status: dirty.applied ? "completed" : "skipped",
8070
- createdAt: new Date().toISOString()
8238
+ createdAt: new Date().toISOString(),
8239
+ payload: readyFile ? { readyFile } : undefined
8071
8240
  });
8072
8241
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
8073
8242
  }
@@ -8293,7 +8462,36 @@ async function executeRigOwnedTaskRun(context, input) {
8293
8462
  let reviewFailureDetail = null;
8294
8463
  const stderrLines = [];
8295
8464
  const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
8296
- for (let attempt = 1;attempt <= maxAttempts; attempt += 1) {
8465
+ if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
8466
+ appendRunLog(context.projectRoot, input.runId, {
8467
+ id: `log:${input.runId}:resume-closeout-phase`,
8468
+ title: "Resume continuing from closeout phase",
8469
+ detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
8470
+ tone: "info",
8471
+ status: resumePreviousStatus,
8472
+ createdAt: new Date().toISOString()
8473
+ });
8474
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
8475
+ exit = { code: 0, signal: null };
8476
+ } else if (resumeMode && latestRuntimeWorkspace) {
8477
+ const acceptedArtifactState = readTaskRunAcceptedArtifactState({
8478
+ taskId: input.taskId ?? runtimeTaskId,
8479
+ workspaceDir: latestRuntimeWorkspace
8480
+ });
8481
+ if (acceptedArtifactState.accepted) {
8482
+ appendRunLog(context.projectRoot, input.runId, {
8483
+ id: `log:${input.runId}:resume-accepted-artifacts`,
8484
+ title: "Resume found accepted artifacts; continuing closeout",
8485
+ detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
8486
+ tone: "info",
8487
+ status: "validating",
8488
+ createdAt: new Date().toISOString()
8489
+ });
8490
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
8491
+ exit = { code: 0, signal: null };
8492
+ }
8493
+ }
8494
+ for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
8297
8495
  const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
8298
8496
  const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
8299
8497
  cwd: context.projectRoot,
@@ -8334,7 +8532,7 @@ async function executeRigOwnedTaskRun(context, input) {
8334
8532
  let acceptedArtifactObservedAt = null;
8335
8533
  let acceptedArtifactPollTimer = null;
8336
8534
  let acceptedArtifactKillTimer = null;
8337
- const attemptExit = await new Promise((resolve22) => {
8535
+ const attemptExit = await new Promise((resolve23) => {
8338
8536
  let settled = false;
8339
8537
  const settle = (result) => {
8340
8538
  if (settled)
@@ -8342,7 +8540,7 @@ async function executeRigOwnedTaskRun(context, input) {
8342
8540
  settled = true;
8343
8541
  if (acceptedArtifactPollTimer)
8344
8542
  clearInterval(acceptedArtifactPollTimer);
8345
- resolve22(result);
8543
+ resolve23(result);
8346
8544
  };
8347
8545
  const pollAcceptedArtifacts = () => {
8348
8546
  const artifactState = readTaskRunAcceptedArtifactState({
@@ -8539,6 +8737,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8539
8737
  });
8540
8738
  throw new CliError2(terminalFailureDetail, exit.code ?? 1);
8541
8739
  }
8740
+ if (planningClassification.planningRequired) {
8741
+ const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
8742
+ const expectedPlanPath = resolve22(planWorkspace, planningArtifactPath);
8743
+ if (!existsSync13(expectedPlanPath)) {
8744
+ const failedAt = new Date().toISOString();
8745
+ const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
8746
+ patchAuthorityRun(context.projectRoot, input.runId, {
8747
+ status: "needs_attention",
8748
+ completedAt: failedAt,
8749
+ errorText: failureDetail
8750
+ });
8751
+ appendRunLog(context.projectRoot, input.runId, {
8752
+ id: `log:${input.runId}:plan-artifact-missing`,
8753
+ title: "Required plan artifact missing",
8754
+ detail: failureDetail,
8755
+ tone: "error",
8756
+ status: "needs_attention",
8757
+ createdAt: failedAt
8758
+ });
8759
+ emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
8760
+ throw new CliError2(failureDetail, 1);
8761
+ }
8762
+ }
8542
8763
  const runPiPrFeedbackFix = async (message2) => {
8543
8764
  appendPiStageLog({
8544
8765
  projectRoot: context.projectRoot,
@@ -8596,9 +8817,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8596
8817
  });
8597
8818
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
8598
8819
  });
8599
- const exitCode = await new Promise((resolve22) => {
8600
- child.once("error", () => resolve22(1));
8601
- child.once("close", (code) => resolve22(code ?? 1));
8820
+ const exitCode = await new Promise((resolve23) => {
8821
+ child.once("error", () => resolve23(1));
8822
+ child.once("close", (code) => resolve23(code ?? 1));
8602
8823
  });
8603
8824
  if (exitCode !== 0) {
8604
8825
  throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
@@ -8717,8 +8938,8 @@ async function executeTest(context, args) {
8717
8938
  }
8718
8939
 
8719
8940
  // packages/cli/src/commands/setup.ts
8720
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "fs";
8721
- import { resolve as resolve22 } from "path";
8941
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
8942
+ import { resolve as resolve23 } from "path";
8722
8943
  import { createPluginHost } from "@rig/core";
8723
8944
  import {
8724
8945
  isSupportedBunVersion as isSupportedBunVersion2,
@@ -8781,9 +9002,9 @@ function runSetupInit(projectRoot) {
8781
9002
  mkdirSync9(stateDir, { recursive: true });
8782
9003
  mkdirSync9(logsDir, { recursive: true });
8783
9004
  mkdirSync9(artifactsDir, { recursive: true });
8784
- const failuresPath = resolve22(stateDir, "failed_approaches.md");
8785
- if (!existsSync13(failuresPath)) {
8786
- writeFileSync6(failuresPath, `# Failed Approaches
9005
+ const failuresPath = resolve23(stateDir, "failed_approaches.md");
9006
+ if (!existsSync14(failuresPath)) {
9007
+ writeFileSync7(failuresPath, `# Failed Approaches
8787
9008
 
8788
9009
  `, "utf-8");
8789
9010
  }
@@ -8800,18 +9021,18 @@ async function runSetupCheck(projectRoot) {
8800
9021
  }
8801
9022
  async function runSetupPreflight(projectRoot) {
8802
9023
  await runSetupCheck(projectRoot);
8803
- const validationRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
8804
- if (existsSync13(validationRoot)) {
9024
+ const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
9025
+ if (existsSync14(validationRoot)) {
8805
9026
  const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
8806
9027
  for (const validator of validators) {
8807
- const script = resolve22(validationRoot, validator.name, "validate.sh");
8808
- if (existsSync13(script)) {
9028
+ const script = resolve23(validationRoot, validator.name, "validate.sh");
9029
+ if (existsSync14(script)) {
8809
9030
  console.log(`OK: validator script ${script}`);
8810
9031
  }
8811
9032
  }
8812
9033
  }
8813
- const hooksRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
8814
- if (existsSync13(hooksRoot)) {
9034
+ const hooksRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
9035
+ if (existsSync14(hooksRoot)) {
8815
9036
  const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
8816
9037
  for (const hook of hooks) {
8817
9038
  console.log(`OK: hook ${hook}`);
@@ -9205,8 +9426,8 @@ var __testOnly = {
9205
9426
  validateRequiredBugPromptValue
9206
9427
  };
9207
9428
  // packages/cli/src/launcher.ts
9208
- import { existsSync as existsSync14 } from "fs";
9209
- import { resolve as resolve23 } from "path";
9429
+ import { existsSync as existsSync15 } from "fs";
9430
+ import { basename as basename2, resolve as resolve24 } from "path";
9210
9431
  import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
9211
9432
  import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
9212
9433
  function parsePolicyMode(value) {
@@ -9219,7 +9440,7 @@ function parsePolicyMode(value) {
9219
9440
  throw new Error(`Invalid --policy-mode value: ${value}. Use off|observe|enforce.`);
9220
9441
  }
9221
9442
  function hasRigProjectMarker(candidate) {
9222
- return existsSync14(resolve23(candidate, RIG_DEFINITION_DIRNAME)) || existsSync14(resolve23(candidate, RIG_STATE_DIRNAME)) || existsSync14(resolve23(candidate, "rig.config.ts")) || existsSync14(resolve23(candidate, "rig.config.json"));
9443
+ return existsSync15(resolve24(candidate, RIG_DEFINITION_DIRNAME)) || existsSync15(resolve24(candidate, RIG_STATE_DIRNAME)) || existsSync15(resolve24(candidate, "rig.config.ts")) || existsSync15(resolve24(candidate, "rig.config.json"));
9223
9444
  }
9224
9445
  function resolveProjectRoot({
9225
9446
  envProjectRoot,
@@ -9228,17 +9449,19 @@ function resolveProjectRoot({
9228
9449
  cwd = process.cwd()
9229
9450
  }) {
9230
9451
  if (envProjectRoot) {
9231
- return resolve23(cwd, envProjectRoot);
9452
+ return resolve24(cwd, envProjectRoot);
9232
9453
  }
9233
9454
  const fallbackImportDir = importDir ?? cwd;
9234
- const candidates = [cwd, resolve23(execPath, "..", ".."), resolve23(fallbackImportDir, "..")];
9455
+ const execName = basename2(execPath).toLowerCase();
9456
+ const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve24(execPath, "..", "..")] : [];
9457
+ const candidates = [cwd, ...execCandidates, resolve24(fallbackImportDir, "..")];
9235
9458
  for (const candidate of candidates) {
9236
9459
  const nearest = resolveNearestRigProjectRoot(candidate);
9237
9460
  if (hasRigProjectMarker(nearest)) {
9238
9461
  return nearest;
9239
9462
  }
9240
9463
  }
9241
- return resolve23(cwd);
9464
+ return resolve24(cwd);
9242
9465
  }
9243
9466
  function normalizeCliErrorCode(message2, isCliError) {
9244
9467
  if (message2.startsWith("Invalid --policy-mode value:")) {
@@ -9305,7 +9528,7 @@ async function runRigCli(module, options = {}) {
9305
9528
  runId: context.runId,
9306
9529
  outcome,
9307
9530
  eventsFile: context.eventBus.getEventsFile(),
9308
- policyFile: resolve23(projectRoot, "rig", "policy", "policy.json"),
9531
+ policyFile: resolve24(projectRoot, "rig", "policy", "policy.json"),
9309
9532
  policyMode: context.policyMode ?? policyMode ?? module.loadPolicy(projectRoot).mode
9310
9533
  }, null, 2));
9311
9534
  }