@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/bin/rig.js CHANGED
@@ -105,12 +105,13 @@ async function runCommand(context, parts) {
105
105
  const envMode = process.env.RIG_BASH_MODE;
106
106
  const effectiveMode = context.policyMode || (envMode === "off" || envMode === "observe" || envMode === "enforce" ? envMode : loadPolicy(context.projectRoot).mode);
107
107
  const controlledPath = `${resolve(context.projectRoot, ".rig", "bin")}:${context.projectRoot}/rig/tools:${process.env.PATH ?? ""}`;
108
- const controlledBash = await ensureAgentShellBinary(context.projectRoot);
108
+ const usesInfrastructureBinary = parts[0] === "rig-server";
109
+ const controlledBash = usesInfrastructureBinary ? null : await ensureAgentShellBinary(context.projectRoot);
109
110
  const commandEnv = [
110
111
  "env",
111
112
  `PATH=${controlledPath}`,
112
113
  `PROJECT_RIG_ROOT=${context.projectRoot}`,
113
- `BASH=${controlledBash}`,
114
+ ...controlledBash ? [`BASH=${controlledBash}`] : [],
114
115
  `RIG_BASH_MODE=${effectiveMode}`,
115
116
  `RIG_POLICY_FILE=${resolve(context.projectRoot, "rig/policy/policy.json")}`,
116
117
  ...context.eventBus.getEventsFile() ? [`RIG_EVENTS_FILE=${context.eventBus.getEventsFile()}`] : []
@@ -2671,6 +2672,8 @@ function resolveSelectedConnection(projectRoot, options = {}) {
2671
2672
 
2672
2673
  // packages/cli/src/commands/_server-client.ts
2673
2674
  import { spawnSync } from "child_process";
2675
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
2676
+ import { resolve as resolve9 } from "path";
2674
2677
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
2675
2678
  var cachedGitHubBearerToken;
2676
2679
  function cleanToken(value) {
@@ -2680,9 +2683,25 @@ function cleanToken(value) {
2680
2683
  function setGitHubBearerTokenForCurrentProcess(token) {
2681
2684
  cachedGitHubBearerToken = cleanToken(token ?? undefined);
2682
2685
  }
2683
- function readGitHubBearerTokenForRemote() {
2686
+ function readPrivateRemoteSessionToken(projectRoot) {
2687
+ const path = resolve9(projectRoot, ".rig", "state", "github-auth.json");
2688
+ if (!existsSync5(path))
2689
+ return null;
2690
+ try {
2691
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
2692
+ return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
2693
+ } catch {
2694
+ return null;
2695
+ }
2696
+ }
2697
+ function readGitHubBearerTokenForRemote(projectRoot) {
2684
2698
  if (cachedGitHubBearerToken !== undefined)
2685
2699
  return cachedGitHubBearerToken;
2700
+ const privateSession = readPrivateRemoteSessionToken(projectRoot);
2701
+ if (privateSession) {
2702
+ cachedGitHubBearerToken = privateSession;
2703
+ return cachedGitHubBearerToken;
2704
+ }
2686
2705
  const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
2687
2706
  if (envToken) {
2688
2707
  cachedGitHubBearerToken = envToken;
@@ -2702,7 +2721,7 @@ async function ensureServerForCli(projectRoot) {
2702
2721
  if (selected?.connection.kind === "remote") {
2703
2722
  return {
2704
2723
  baseUrl: selected.connection.baseUrl,
2705
- authToken: readGitHubBearerTokenForRemote(),
2724
+ authToken: readGitHubBearerTokenForRemote(projectRoot),
2706
2725
  connectionKind: "remote"
2707
2726
  };
2708
2727
  }
@@ -2809,7 +2828,7 @@ async function postGitHubTokenViaServer(context, token, options = {}) {
2809
2828
  const payload = await requestServerJson(context, "/api/github/auth/token", {
2810
2829
  method: "POST",
2811
2830
  headers: { "content-type": "application/json" },
2812
- body: JSON.stringify({ token, selectedRepo: options.selectedRepo })
2831
+ body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
2813
2832
  });
2814
2833
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2815
2834
  }
@@ -2830,7 +2849,7 @@ async function registerProjectViaServer(context, input) {
2830
2849
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2831
2850
  }
2832
2851
  function sleep(ms) {
2833
- return new Promise((resolve9) => setTimeout(resolve9, ms));
2852
+ return new Promise((resolve10) => setTimeout(resolve10, ms));
2834
2853
  }
2835
2854
  async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
2836
2855
  const switched = await requestServerJson(context, "/api/server/project-root", {
@@ -2921,9 +2940,9 @@ async function submitTaskRunViaServer(context, input) {
2921
2940
  }
2922
2941
 
2923
2942
  // packages/cli/src/commands/_pi-install.ts
2924
- import { existsSync as existsSync5, readFileSync as readFileSync3, rmSync as rmSync3 } from "fs";
2943
+ import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
2925
2944
  import { homedir as homedir3 } from "os";
2926
- import { resolve as resolve9 } from "path";
2945
+ import { resolve as resolve10 } from "path";
2927
2946
  var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
2928
2947
  var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
2929
2948
  export { default } from '@rig/pi-rig';
@@ -2938,11 +2957,11 @@ async function defaultCommandRunner(command, options = {}) {
2938
2957
  return { exitCode, stdout, stderr };
2939
2958
  }
2940
2959
  function resolvePiRigExtensionPath(homeDir) {
2941
- return resolve9(homeDir, ".pi", "agent", "extensions", "pi-rig");
2960
+ return resolve10(homeDir, ".pi", "agent", "extensions", "pi-rig");
2942
2961
  }
2943
- function resolvePiRigPackageSource(projectRoot, exists = existsSync5) {
2944
- const localPackage = resolve9(projectRoot, "packages", "pi-rig");
2945
- if (exists(resolve9(localPackage, "package.json")))
2962
+ function resolvePiRigPackageSource(projectRoot, exists = existsSync6) {
2963
+ const localPackage = resolve10(projectRoot, "packages", "pi-rig");
2964
+ if (exists(resolve10(localPackage, "package.json")))
2946
2965
  return localPackage;
2947
2966
  return `npm:${PI_RIG_PACKAGE_NAME}`;
2948
2967
  }
@@ -2993,13 +3012,13 @@ async function ensurePiBinaryAvailable(input) {
2993
3012
  ...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
2994
3013
  };
2995
3014
  }
2996
- function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync5) {
3015
+ function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync6) {
2997
3016
  const extensionPath = resolvePiRigExtensionPath(homeDir);
2998
- const indexPath = resolve9(extensionPath, "index.ts");
3017
+ const indexPath = resolve10(extensionPath, "index.ts");
2999
3018
  if (!exists(indexPath))
3000
3019
  return;
3001
3020
  try {
3002
- const content = readFileSync3(indexPath, "utf8");
3021
+ const content = readFileSync4(indexPath, "utf8");
3003
3022
  if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
3004
3023
  rmSync3(extensionPath, { recursive: true, force: true });
3005
3024
  }
@@ -3015,13 +3034,13 @@ async function checkPiRigInstall(input = {}) {
3015
3034
  piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
3016
3035
  };
3017
3036
  }
3018
- const exists = input.exists ?? existsSync5;
3037
+ const exists = input.exists ?? existsSync6;
3019
3038
  const runner = input.commandRunner ?? defaultCommandRunner;
3020
3039
  const piResult = await safeRun(runner, ["pi", "--version"]);
3021
3040
  const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
3022
3041
  const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
3023
3042
  ${piListResult.stderr}`);
3024
- const legacyBridge = exists(resolve9(extensionPath, "index.ts"));
3043
+ const legacyBridge = exists(resolve10(extensionPath, "index.ts"));
3025
3044
  const hasPiRig = listedPiRig;
3026
3045
  return {
3027
3046
  extensionPath,
@@ -3359,7 +3378,7 @@ async function executeQueue(context, args) {
3359
3378
  }
3360
3379
 
3361
3380
  // packages/cli/src/commands/agent.ts
3362
- import { resolve as resolve11 } from "path";
3381
+ import { resolve as resolve12 } from "path";
3363
3382
  import {
3364
3383
  agentId,
3365
3384
  cleanupAgentRuntime,
@@ -3369,8 +3388,8 @@ import {
3369
3388
  } from "@rig/runtime/control-plane/runtime/isolation";
3370
3389
 
3371
3390
  // packages/cli/src/commands/_authority-runs.ts
3372
- import { existsSync as existsSync6 } from "fs";
3373
- import { resolve as resolve10 } from "path";
3391
+ import { existsSync as existsSync7 } from "fs";
3392
+ import { resolve as resolve11 } from "path";
3374
3393
  import {
3375
3394
  readAuthorityRun,
3376
3395
  readJsonlFile as readJsonlFile2,
@@ -3392,8 +3411,8 @@ function normalizeRuntimeAdapter(value) {
3392
3411
  return "claude-code";
3393
3412
  }
3394
3413
  function readLatestBeadRecord(projectRoot, taskId) {
3395
- const issuesPath = resolve10(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3396
- if (!existsSync6(issuesPath)) {
3414
+ const issuesPath = resolve11(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3415
+ if (!existsSync7(issuesPath)) {
3397
3416
  return null;
3398
3417
  }
3399
3418
  let latest = null;
@@ -3428,6 +3447,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3428
3447
  const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
3429
3448
  const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
3430
3449
  const next = {
3450
+ ...existing ?? {},
3431
3451
  runId: input.runId,
3432
3452
  projectRoot,
3433
3453
  workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
@@ -3460,7 +3480,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3460
3480
  } else if ("errorText" in next) {
3461
3481
  delete next.errorText;
3462
3482
  }
3463
- writeJsonFile3(resolve10(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3483
+ writeJsonFile3(resolve11(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3464
3484
  return next;
3465
3485
  }
3466
3486
 
@@ -3581,10 +3601,10 @@ async function executeAgent(context, args) {
3581
3601
  status: "running",
3582
3602
  startedAt: createdAt,
3583
3603
  worktreePath: runtime.workspaceDir,
3584
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3604
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3585
3605
  logRoot: runtime.logsDir,
3586
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3587
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3606
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3607
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3588
3608
  pid: process.pid
3589
3609
  });
3590
3610
  const result = await runInAgentRuntime({
@@ -3604,10 +3624,10 @@ async function executeAgent(context, args) {
3604
3624
  startedAt: createdAt,
3605
3625
  completedAt: failedAt,
3606
3626
  worktreePath: runtime.workspaceDir,
3607
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3627
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3608
3628
  logRoot: runtime.logsDir,
3609
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3610
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3629
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3630
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3611
3631
  pid: process.pid,
3612
3632
  errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
3613
3633
  });
@@ -3624,10 +3644,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3624
3644
  startedAt: createdAt,
3625
3645
  completedAt,
3626
3646
  worktreePath: runtime.workspaceDir,
3627
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3647
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3628
3648
  logRoot: runtime.logsDir,
3629
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3630
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3649
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3650
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3631
3651
  pid: process.pid
3632
3652
  });
3633
3653
  return {
@@ -3701,7 +3721,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3701
3721
  import {
3702
3722
  chmodSync,
3703
3723
  copyFileSync as copyFileSync2,
3704
- existsSync as existsSync7,
3724
+ existsSync as existsSync8,
3705
3725
  mkdirSync as mkdirSync6,
3706
3726
  readdirSync,
3707
3727
  readlinkSync,
@@ -3711,7 +3731,7 @@ import {
3711
3731
  unlinkSync
3712
3732
  } from "fs";
3713
3733
  import { homedir as homedir4 } from "os";
3714
- import { resolve as resolve12 } from "path";
3734
+ import { resolve as resolve13 } from "path";
3715
3735
  import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
3716
3736
  import {
3717
3737
  computeRuntimeImageFingerprint,
@@ -3730,13 +3750,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
3730
3750
 
3731
3751
  // packages/cli/src/commands/dist.ts
3732
3752
  function collectRigValidatorBuildTargets(input) {
3733
- const validatorsRoot = resolve12(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3734
- if (!existsSync7(validatorsRoot))
3753
+ const validatorsRoot = resolve13(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3754
+ if (!existsSync8(validatorsRoot))
3735
3755
  return [];
3736
3756
  const targets = [];
3737
3757
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3738
3758
  for (const category of categories) {
3739
- const categoryDir = resolve12(validatorsRoot, category.name);
3759
+ const categoryDir = resolve13(validatorsRoot, category.name);
3740
3760
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3741
3761
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3742
3762
  continue;
@@ -3745,7 +3765,7 @@ function collectRigValidatorBuildTargets(input) {
3745
3765
  continue;
3746
3766
  targets.push({
3747
3767
  source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
3748
- dest: resolve12(input.imageDir, `bin/validators/${category.name}-${check}`),
3768
+ dest: resolve13(input.imageDir, `bin/validators/${category.name}-${check}`),
3749
3769
  cwd: input.hostProjectRoot
3750
3770
  });
3751
3771
  }
@@ -3754,16 +3774,16 @@ function collectRigValidatorBuildTargets(input) {
3754
3774
  }
3755
3775
  async function findLatestDistBinary(projectRoot) {
3756
3776
  const distRoot = resolveControlPlaneHostDistDir(projectRoot);
3757
- if (!existsSync7(distRoot)) {
3777
+ if (!existsSync8(distRoot)) {
3758
3778
  return null;
3759
3779
  }
3760
3780
  const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
3761
3781
  name: entry.name,
3762
- mtimeMs: statSync(resolve12(distRoot, entry.name)).mtimeMs
3782
+ mtimeMs: statSync(resolve13(distRoot, entry.name)).mtimeMs
3763
3783
  })).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
3764
3784
  for (const { name } of entries) {
3765
- const candidate = resolve12(distRoot, name, "bin", "rig");
3766
- if (existsSync7(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3785
+ const candidate = resolve13(distRoot, name, "bin", "rig");
3786
+ if (existsSync8(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3767
3787
  return candidate;
3768
3788
  }
3769
3789
  }
@@ -3775,7 +3795,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
3775
3795
  async function runDistDoctor(projectRoot) {
3776
3796
  const bunPath = Bun.which("bun");
3777
3797
  const rigPath = Bun.which("rig");
3778
- const userBinDir = resolve12(homedir4(), ".local/bin");
3798
+ const userBinDir = resolve13(homedir4(), ".local/bin");
3779
3799
  const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
3780
3800
  let rigRunnable = false;
3781
3801
  if (rigPath) {
@@ -3823,15 +3843,15 @@ async function executeDist(context, args) {
3823
3843
  let source = await findLatestDistBinary(context.projectRoot);
3824
3844
  let buildDir = null;
3825
3845
  if (!source) {
3826
- buildDir = resolve12(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3846
+ buildDir = resolve13(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3827
3847
  await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
3828
- source = resolve12(buildDir, "bin", "rig");
3848
+ source = resolve13(buildDir, "bin", "rig");
3829
3849
  }
3830
- if (!existsSync7(source)) {
3850
+ if (!existsSync8(source)) {
3831
3851
  throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
3832
3852
  }
3833
- const installedPath = resolve12(installDir, "rig");
3834
- if (existsSync7(installedPath)) {
3853
+ const installedPath = resolve13(installDir, "rig");
3854
+ if (existsSync8(installedPath)) {
3835
3855
  unlinkSync(installedPath);
3836
3856
  }
3837
3857
  copyFileSync2(source, installedPath);
@@ -3873,22 +3893,22 @@ async function executeDist(context, args) {
3873
3893
  requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
3874
3894
  const fp = await computeRuntimeImageFingerprint(context.projectRoot);
3875
3895
  const currentId = computeRuntimeImageId(fp);
3876
- const imagesDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3896
+ const imagesDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3877
3897
  mkdirSync6(imagesDir, { recursive: true });
3878
3898
  let pruned = 0;
3879
3899
  for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
3880
3900
  if (entry.isDirectory() && entry.name !== currentId) {
3881
- rmSync4(resolve12(imagesDir, entry.name), { recursive: true, force: true });
3901
+ rmSync4(resolve13(imagesDir, entry.name), { recursive: true, force: true });
3882
3902
  pruned++;
3883
3903
  }
3884
3904
  }
3885
3905
  if (pruned > 0 && context.outputMode === "text") {
3886
3906
  console.log(`Pruned ${pruned} stale image(s).`);
3887
3907
  }
3888
- const imageDir = resolve12(imagesDir, currentId);
3889
- mkdirSync6(resolve12(imageDir, "bin/hooks"), { recursive: true });
3890
- mkdirSync6(resolve12(imageDir, "bin/plugins"), { recursive: true });
3891
- mkdirSync6(resolve12(imageDir, "bin/validators"), { recursive: true });
3908
+ const imageDir = resolve13(imagesDir, currentId);
3909
+ mkdirSync6(resolve13(imageDir, "bin/hooks"), { recursive: true });
3910
+ mkdirSync6(resolve13(imageDir, "bin/plugins"), { recursive: true });
3911
+ mkdirSync6(resolve13(imageDir, "bin/validators"), { recursive: true });
3892
3912
  const hookNames = [
3893
3913
  "scope-guard",
3894
3914
  "import-guard",
@@ -3902,25 +3922,25 @@ async function executeDist(context, args) {
3902
3922
  ];
3903
3923
  const targets = [];
3904
3924
  const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || context.projectRoot;
3905
- targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve12(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
3906
- targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
3925
+ targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve13(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
3926
+ targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
3907
3927
  for (const hookName of hookNames) {
3908
3928
  const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
3909
- targets.push({ source: src, dest: resolve12(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
3910
- targets.push({ source: src, dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
3929
+ targets.push({ source: src, dest: resolve13(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
3930
+ targets.push({ source: src, dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
3911
3931
  }
3912
- const pluginsDir = resolve12(context.projectRoot, "rig/plugins");
3913
- const binPluginsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
3914
- const validatorsRoot = resolve12(hostProjectRoot, "packages/runtime/src/control-plane/validators");
3915
- const binValidatorsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
3932
+ const pluginsDir = resolve13(context.projectRoot, "rig/plugins");
3933
+ const binPluginsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
3934
+ const validatorsRoot = resolve13(hostProjectRoot, "packages/runtime/src/control-plane/validators");
3935
+ const binValidatorsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
3916
3936
  mkdirSync6(binPluginsDir, { recursive: true });
3917
3937
  mkdirSync6(binValidatorsDir, { recursive: true });
3918
- if (existsSync7(pluginsDir)) {
3938
+ if (existsSync8(pluginsDir)) {
3919
3939
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3920
3940
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3921
3941
  if (!m)
3922
3942
  continue;
3923
- targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve12(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3943
+ targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve13(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3924
3944
  }
3925
3945
  }
3926
3946
  targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
@@ -3931,17 +3951,17 @@ async function executeDist(context, args) {
3931
3951
  const isValidator = dest.includes("/bin/validators/");
3932
3952
  await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
3933
3953
  }
3934
- if (existsSync7(pluginsDir)) {
3954
+ if (existsSync8(pluginsDir)) {
3935
3955
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3936
3956
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3937
3957
  if (!m)
3938
3958
  continue;
3939
3959
  const pluginName = m[1];
3940
- const imageBin = resolve12(imageDir, `bin/plugins/${pluginName}`);
3960
+ const imageBin = resolve13(imageDir, `bin/plugins/${pluginName}`);
3941
3961
  if (!pluginName)
3942
3962
  continue;
3943
- const symlinkPath = resolve12(binPluginsDir, pluginName);
3944
- if (existsSync7(imageBin)) {
3963
+ const symlinkPath = resolve13(binPluginsDir, pluginName);
3964
+ if (existsSync8(imageBin)) {
3945
3965
  try {
3946
3966
  unlinkSync(symlinkPath);
3947
3967
  } catch {}
@@ -3949,10 +3969,10 @@ async function executeDist(context, args) {
3949
3969
  }
3950
3970
  }
3951
3971
  }
3952
- if (existsSync7(validatorsRoot)) {
3972
+ if (existsSync8(validatorsRoot)) {
3953
3973
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3954
3974
  for (const category of categories) {
3955
- const categoryDir = resolve12(validatorsRoot, category.name);
3975
+ const categoryDir = resolve13(validatorsRoot, category.name);
3956
3976
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3957
3977
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3958
3978
  continue;
@@ -3960,9 +3980,9 @@ async function executeDist(context, args) {
3960
3980
  if (!check || check === "index" || check === "shared")
3961
3981
  continue;
3962
3982
  const validatorName = `${category.name}-${check}`;
3963
- const imageBin = resolve12(imageDir, `bin/validators/${validatorName}`);
3964
- const symlinkPath = resolve12(binValidatorsDir, validatorName);
3965
- if (existsSync7(imageBin)) {
3983
+ const imageBin = resolve13(imageDir, `bin/validators/${validatorName}`);
3984
+ const symlinkPath = resolve13(binValidatorsDir, validatorName);
3985
+ if (existsSync8(imageBin)) {
3966
3986
  try {
3967
3987
  unlinkSync(symlinkPath);
3968
3988
  } catch {}
@@ -3971,18 +3991,18 @@ async function executeDist(context, args) {
3971
3991
  }
3972
3992
  }
3973
3993
  }
3974
- const agentsDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
3975
- if (existsSync7(agentsDir)) {
3994
+ const agentsDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
3995
+ if (existsSync8(agentsDir)) {
3976
3996
  let relinkCount = 0;
3977
3997
  for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
3978
3998
  if (!agentEntry.isDirectory())
3979
3999
  continue;
3980
- const agentBinDir = resolve12(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
3981
- if (!existsSync7(agentBinDir))
4000
+ const agentBinDir = resolve13(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
4001
+ if (!existsSync8(agentBinDir))
3982
4002
  continue;
3983
4003
  const walkDir = (dir) => {
3984
4004
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
3985
- const fullPath = resolve12(dir, entry.name);
4005
+ const fullPath = resolve13(dir, entry.name);
3986
4006
  if (entry.isDirectory()) {
3987
4007
  walkDir(fullPath);
3988
4008
  } else if (entry.isSymbolicLink()) {
@@ -4016,7 +4036,7 @@ async function executeDist(context, args) {
4016
4036
 
4017
4037
  // packages/cli/src/commands/inbox.ts
4018
4038
  import { writeFileSync as writeFileSync4 } from "fs";
4019
- import { resolve as resolve13 } from "path";
4039
+ import { resolve as resolve14 } from "path";
4020
4040
  import {
4021
4041
  listAuthorityRuns,
4022
4042
  readJsonlFile as readJsonlFile3,
@@ -4033,7 +4053,7 @@ async function executeInbox(context, args) {
4033
4053
  pending = task.rest;
4034
4054
  requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
4035
4055
  const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
4036
- const approvals = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
4056
+ const approvals = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
4037
4057
  runId: entry.runId,
4038
4058
  record
4039
4059
  })));
@@ -4061,7 +4081,7 @@ async function executeInbox(context, args) {
4061
4081
  if (decision.value !== "approve" && decision.value !== "reject") {
4062
4082
  throw new CliError2("decision must be approve or reject.");
4063
4083
  }
4064
- const approvalsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
4084
+ const approvalsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
4065
4085
  const approvals = readJsonlFile3(approvalsPath);
4066
4086
  const resolvedAt = new Date().toISOString();
4067
4087
  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);
@@ -4078,7 +4098,7 @@ async function executeInbox(context, args) {
4078
4098
  pending = task.rest;
4079
4099
  requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
4080
4100
  const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
4081
- const requests = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
4101
+ const requests = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
4082
4102
  runId: entry.runId,
4083
4103
  record
4084
4104
  })));
@@ -4120,7 +4140,7 @@ async function executeInbox(context, args) {
4120
4140
  const [key, ...restValue] = entry.split("=");
4121
4141
  return [key, restValue.join("=")];
4122
4142
  }));
4123
- const requestsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
4143
+ const requestsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
4124
4144
  const requests = readJsonlFile3(requestsPath);
4125
4145
  const resolvedAt = new Date().toISOString();
4126
4146
  const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
@@ -4135,14 +4155,14 @@ async function executeInbox(context, args) {
4135
4155
  }
4136
4156
 
4137
4157
  // packages/cli/src/commands/init.ts
4138
- import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
4158
+ import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
4139
4159
  import { spawnSync as spawnSync2 } from "child_process";
4140
- import { resolve as resolve16 } from "path";
4160
+ import { resolve as resolve17 } from "path";
4141
4161
  import { buildRigInitConfigSource } from "@rig/core";
4142
4162
 
4143
4163
  // packages/cli/src/commands/_snapshot-upload.ts
4144
4164
  import { mkdir, readdir, readFile, writeFile } from "fs/promises";
4145
- import { dirname as dirname2, resolve as resolve14, relative, sep } from "path";
4165
+ import { dirname as dirname2, resolve as resolve15, relative, sep } from "path";
4146
4166
  var SNAPSHOT_ARCHIVE_VERSION = 1;
4147
4167
  var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
4148
4168
  var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
@@ -4164,15 +4184,15 @@ function assertManifestPath(root, relativePath) {
4164
4184
  if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
4165
4185
  throw new Error(`Invalid snapshot path: ${relativePath}`);
4166
4186
  }
4167
- const resolved = resolve14(root, relativePath);
4187
+ const resolved = resolve15(root, relativePath);
4168
4188
  const relativeToRoot = relative(root, resolved);
4169
- if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve14(relativeToRoot) === resolved) {
4189
+ if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve15(relativeToRoot) === resolved) {
4170
4190
  throw new Error(`Snapshot path escapes project root: ${relativePath}`);
4171
4191
  }
4172
4192
  return resolved;
4173
4193
  }
4174
4194
  async function buildSnapshotUploadManifest(projectRoot, options = {}) {
4175
- const root = resolve14(projectRoot);
4195
+ const root = resolve15(projectRoot);
4176
4196
  const excludedDirectories = [...new Set([
4177
4197
  ...DEFAULT_EXCLUDED_DIRECTORIES,
4178
4198
  ...options.excludedDirectories ?? []
@@ -4184,7 +4204,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
4184
4204
  for (const entry of entries) {
4185
4205
  if (entry.isDirectory() && excludedSet.has(entry.name))
4186
4206
  continue;
4187
- const fullPath = resolve14(dir, entry.name);
4207
+ const fullPath = resolve15(dir, entry.name);
4188
4208
  if (entry.isDirectory()) {
4189
4209
  await visit(fullPath);
4190
4210
  continue;
@@ -4232,8 +4252,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
4232
4252
  }
4233
4253
 
4234
4254
  // packages/cli/src/commands/_doctor-checks.ts
4235
- import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
4236
- import { resolve as resolve15 } from "path";
4255
+ import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
4256
+ import { resolve as resolve16 } from "path";
4237
4257
  import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
4238
4258
  function check(id, label, status, detail, remediation) {
4239
4259
  return {
@@ -4273,11 +4293,11 @@ function repoSlugFromConfig(config) {
4273
4293
  function loadFallbackConfig(projectRoot) {
4274
4294
  const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
4275
4295
  for (const name of candidates) {
4276
- const path = resolve15(projectRoot, name);
4277
- if (!existsSync8(path))
4296
+ const path = resolve16(projectRoot, name);
4297
+ if (!existsSync9(path))
4278
4298
  continue;
4279
4299
  try {
4280
- const source = readFileSync4(path, "utf8");
4300
+ const source = readFileSync5(path, "utf8");
4281
4301
  if (name.endsWith(".json"))
4282
4302
  return JSON.parse(source);
4283
4303
  const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
@@ -4356,7 +4376,7 @@ async function runRigDoctorChecks(options) {
4356
4376
  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`)."));
4357
4377
  const loadedConfig = await loadConfig(projectRoot).catch(() => null);
4358
4378
  const config = loadedConfig ?? loadFallbackConfig(projectRoot);
4359
- const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync8(resolve15(projectRoot, name)));
4379
+ const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync9(resolve16(projectRoot, name)));
4360
4380
  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."));
4361
4381
  const taskSourceKind = config?.taskSource?.kind;
4362
4382
  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."));
@@ -4451,10 +4471,10 @@ function countDoctorFailures(checks) {
4451
4471
  }
4452
4472
 
4453
4473
  // packages/cli/src/commands/init.ts
4454
- var RIG_CONFIG_PACKAGE_VERSION = "0.0.6-alpha.0";
4474
+ var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
4455
4475
  var RIG_CONFIG_DEV_DEPENDENCIES = {
4456
- "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_VERSION}`,
4457
- "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_VERSION}`
4476
+ "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
4477
+ "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
4458
4478
  };
4459
4479
  function parseRepoSlugFromRemote(remoteUrl) {
4460
4480
  const trimmed = remoteUrl.trim();
@@ -4474,20 +4494,20 @@ function parseRepoSlug(value) {
4474
4494
  return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
4475
4495
  }
4476
4496
  function ensureRigPrivateDirs(projectRoot) {
4477
- const rigDir = resolve16(projectRoot, ".rig");
4478
- mkdirSync7(resolve16(rigDir, "state"), { recursive: true });
4479
- mkdirSync7(resolve16(rigDir, "logs"), { recursive: true });
4480
- mkdirSync7(resolve16(rigDir, "runs"), { recursive: true });
4481
- mkdirSync7(resolve16(rigDir, "tmp"), { recursive: true });
4482
- mkdirSync7(resolve16(projectRoot, "artifacts"), { recursive: true });
4483
- const taskConfigPath = resolve16(rigDir, "task-config.json");
4484
- if (!existsSync9(taskConfigPath))
4497
+ const rigDir = resolve17(projectRoot, ".rig");
4498
+ mkdirSync7(resolve17(rigDir, "state"), { recursive: true });
4499
+ mkdirSync7(resolve17(rigDir, "logs"), { recursive: true });
4500
+ mkdirSync7(resolve17(rigDir, "runs"), { recursive: true });
4501
+ mkdirSync7(resolve17(rigDir, "tmp"), { recursive: true });
4502
+ mkdirSync7(resolve17(projectRoot, "artifacts"), { recursive: true });
4503
+ const taskConfigPath = resolve17(rigDir, "task-config.json");
4504
+ if (!existsSync10(taskConfigPath))
4485
4505
  writeFileSync5(taskConfigPath, `{}
4486
4506
  `, "utf-8");
4487
4507
  }
4488
4508
  function ensureGitignoreEntries(projectRoot) {
4489
- const path = resolve16(projectRoot, ".gitignore");
4490
- const existing = existsSync9(path) ? readFileSync5(path, "utf8") : "";
4509
+ const path = resolve17(projectRoot, ".gitignore");
4510
+ const existing = existsSync10(path) ? readFileSync6(path, "utf8") : "";
4491
4511
  const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
4492
4512
  const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
4493
4513
  if (missing.length === 0)
@@ -4500,14 +4520,14 @@ function ensureGitignoreEntries(projectRoot) {
4500
4520
  `, "utf8");
4501
4521
  }
4502
4522
  function ensureRigConfigPackageDependencies(projectRoot) {
4503
- const path = resolve16(projectRoot, "package.json");
4504
- const existing = existsSync9(path) ? JSON.parse(readFileSync5(path, "utf8")) : {};
4523
+ const path = resolve17(projectRoot, "package.json");
4524
+ const existing = existsSync10(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
4505
4525
  const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
4506
4526
  for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
4507
4527
  devDependencies[name] = spec;
4508
4528
  }
4509
4529
  const next = {
4510
- ...existsSync9(path) ? existing : { name: "rig-project", private: true },
4530
+ ...existsSync10(path) ? existing : { name: "rig-project", private: true },
4511
4531
  devDependencies
4512
4532
  };
4513
4533
  writeFileSync5(path, `${JSON.stringify(next, null, 2)}
@@ -4577,15 +4597,54 @@ async function promptSelect(prompts, options) {
4577
4597
  throw new CliError2("Init cancelled.", 1);
4578
4598
  return String(value);
4579
4599
  }
4580
- async function pollDeviceAuthOnce(context, pollId) {
4600
+ function sleep2(ms) {
4601
+ return new Promise((resolve18) => setTimeout(resolve18, ms));
4602
+ }
4603
+ function positiveIntFromEnv(name, fallback) {
4604
+ const value = Number.parseInt(process.env[name] ?? "", 10);
4605
+ return Number.isFinite(value) && value >= 0 ? value : fallback;
4606
+ }
4607
+ function apiSessionTokenFrom(payload) {
4608
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
4609
+ return null;
4610
+ const token = payload.apiSessionToken;
4611
+ return typeof token === "string" && token.trim() ? token.trim() : null;
4612
+ }
4613
+ function writeRemoteGitHubAuthState(projectRoot, input) {
4614
+ writeFileSync5(resolve17(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
4615
+ authenticated: true,
4616
+ source: input.source,
4617
+ storedOnServer: true,
4618
+ selectedRepo: input.selectedRepo,
4619
+ ...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
4620
+ updatedAt: new Date().toISOString()
4621
+ }, null, 2)}
4622
+ `, "utf8");
4623
+ }
4624
+ async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
4581
4625
  if (typeof pollId !== "string" || !pollId.trim())
4582
4626
  return null;
4583
- const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
4584
- method: "POST",
4585
- headers: { "content-type": "application/json" },
4586
- body: JSON.stringify({ pollId })
4587
- }).catch(() => null);
4588
- return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
4627
+ const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
4628
+ const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
4629
+ const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
4630
+ const deadline = Date.now() + timeoutMs;
4631
+ let last = null;
4632
+ do {
4633
+ const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
4634
+ method: "POST",
4635
+ headers: { "content-type": "application/json" },
4636
+ body: JSON.stringify({ pollId })
4637
+ }).catch(() => null);
4638
+ last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
4639
+ const status = typeof last?.status === "string" ? last.status : null;
4640
+ if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
4641
+ return last;
4642
+ }
4643
+ if (timeoutMs <= 0)
4644
+ return last;
4645
+ await sleep2(intervalMs);
4646
+ } while (Date.now() < deadline);
4647
+ return last;
4589
4648
  }
4590
4649
  async function runControlPlaneInit(context, options) {
4591
4650
  const projectRoot = context.projectRoot;
@@ -4608,9 +4667,9 @@ async function runControlPlaneInit(context, options) {
4608
4667
  });
4609
4668
  ensureRigPrivateDirs(projectRoot);
4610
4669
  ensureGitignoreEntries(projectRoot);
4611
- const configTsPath = resolve16(projectRoot, "rig.config.ts");
4612
- const configJsonPath = resolve16(projectRoot, "rig.config.json");
4613
- const configExists = existsSync9(configTsPath) || existsSync9(configJsonPath);
4670
+ const configTsPath = resolve17(projectRoot, "rig.config.ts");
4671
+ const configJsonPath = resolve17(projectRoot, "rig.config.json");
4672
+ const configExists = existsSync10(configTsPath) || existsSync10(configJsonPath);
4614
4673
  if (!options.privateStateOnly) {
4615
4674
  if (configExists && !options.repair) {
4616
4675
  if (context.outputMode !== "json")
@@ -4626,7 +4685,7 @@ async function runControlPlaneInit(context, options) {
4626
4685
  }
4627
4686
  ensureRigConfigPackageDependencies(projectRoot);
4628
4687
  }
4629
- writeFileSync5(resolve16(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4688
+ writeFileSync5(resolve17(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4630
4689
  `, "utf8");
4631
4690
  const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
4632
4691
  let uploadedSnapshot = null;
@@ -4648,10 +4707,14 @@ async function runControlPlaneInit(context, options) {
4648
4707
  const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
4649
4708
  if (token) {
4650
4709
  githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
4651
- setGitHubBearerTokenForCurrentProcess(token);
4710
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4711
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4652
4712
  if (serverKind === "remote") {
4653
- 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)}
4654
- `, "utf8");
4713
+ writeRemoteGitHubAuthState(projectRoot, {
4714
+ source: authMethod === "gh" ? "gh" : "init-token",
4715
+ selectedRepo: repo.slug,
4716
+ apiSessionToken
4717
+ });
4655
4718
  }
4656
4719
  } else if (authMethod === "device") {
4657
4720
  const payload = await requestServerJson(context, "/api/github/auth/device/start", {
@@ -4660,9 +4723,22 @@ async function runControlPlaneInit(context, options) {
4660
4723
  body: JSON.stringify({ repoSlug: repo.slug })
4661
4724
  });
4662
4725
  deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
4663
- const completed = await pollDeviceAuthOnce(context, deviceAuth.pollId);
4664
- if (completed)
4726
+ if (context.outputMode !== "json") {
4727
+ const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
4728
+ const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
4729
+ console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
4730
+ }
4731
+ const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
4732
+ if (completed) {
4733
+ const apiSessionToken = apiSessionTokenFrom(completed);
4734
+ if (apiSessionToken) {
4735
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken);
4736
+ if (serverKind === "remote") {
4737
+ writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken });
4738
+ }
4739
+ }
4665
4740
  deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
4741
+ }
4666
4742
  }
4667
4743
  let remoteCheckoutPreparation = null;
4668
4744
  if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
@@ -4682,6 +4758,12 @@ async function runControlPlaneInit(context, options) {
4682
4758
  });
4683
4759
  const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
4684
4760
  const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
4761
+ if (serverRootSwitch && token) {
4762
+ githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
4763
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4764
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4765
+ writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken });
4766
+ }
4685
4767
  const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
4686
4768
  const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
4687
4769
  remote: true,
@@ -4791,7 +4873,7 @@ function parseInitOptions(args) {
4791
4873
  async function runInteractiveControlPlaneInit(context, prompts) {
4792
4874
  prompts.intro?.("Initialize a Rig control-plane project");
4793
4875
  const projectRoot = context.projectRoot;
4794
- const existingConfig = existsSync9(resolve16(projectRoot, "rig.config.ts")) || existsSync9(resolve16(projectRoot, "rig.config.json"));
4876
+ const existingConfig = existsSync10(resolve17(projectRoot, "rig.config.ts")) || existsSync10(resolve17(projectRoot, "rig.config.json"));
4795
4877
  let repair = false;
4796
4878
  let privateStateOnly = false;
4797
4879
  if (existingConfig) {
@@ -4891,7 +4973,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
4891
4973
  });
4892
4974
  const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
4893
4975
  const deviceAuth = details.deviceAuth && typeof details.deviceAuth === "object" && !Array.isArray(details.deviceAuth) ? details.deviceAuth : null;
4894
- 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")}.` : "";
4976
+ 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")}.` : "";
4895
4977
  prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
4896
4978
  return result;
4897
4979
  }
@@ -5053,8 +5135,8 @@ async function executeDoctor(context, args) {
5053
5135
  }
5054
5136
 
5055
5137
  // packages/cli/src/commands/_run-driver-helpers.ts
5056
- import { readFileSync as readFileSync6 } from "fs";
5057
- import { resolve as resolve17 } from "path";
5138
+ import { readFileSync as readFileSync7 } from "fs";
5139
+ import { resolve as resolve18 } from "path";
5058
5140
  import {
5059
5141
  appendJsonlRecord as appendJsonlRecord2,
5060
5142
  readAuthorityRun as readAuthorityRun2,
@@ -5074,7 +5156,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
5074
5156
  ...patch,
5075
5157
  updatedAt: new Date().toISOString()
5076
5158
  };
5077
- writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
5159
+ writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
5078
5160
  return next;
5079
5161
  }
5080
5162
  function touchAuthorityRun(projectRoot, runId) {
@@ -5082,21 +5164,21 @@ function touchAuthorityRun(projectRoot, runId) {
5082
5164
  if (!current) {
5083
5165
  return;
5084
5166
  }
5085
- writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
5167
+ writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
5086
5168
  ...current,
5087
5169
  updatedAt: new Date().toISOString()
5088
5170
  });
5089
5171
  }
5090
5172
  function appendRunTimeline(projectRoot, runId, value) {
5091
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
5173
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
5092
5174
  touchAuthorityRun(projectRoot, runId);
5093
5175
  }
5094
5176
  function appendRunLog(projectRoot, runId, value) {
5095
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
5177
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
5096
5178
  touchAuthorityRun(projectRoot, runId);
5097
5179
  }
5098
5180
  function appendRunAction(projectRoot, runId, value) {
5099
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
5181
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
5100
5182
  id: value.id,
5101
5183
  type: "action",
5102
5184
  actionType: value.actionType,
@@ -5167,7 +5249,7 @@ function buildRunPrompt(input) {
5167
5249
  })();
5168
5250
  const scopeText = (() => {
5169
5251
  try {
5170
- const parsed = JSON.parse(readFileSync6(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
5252
+ const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
5171
5253
  const entry = parsed[input.taskId] ?? {};
5172
5254
  const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
5173
5255
  const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
@@ -5280,8 +5362,8 @@ function renderSourceScopeValidation(task, validation) {
5280
5362
  }
5281
5363
 
5282
5364
  // packages/cli/src/commands/inspect.ts
5283
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
5284
- import { resolve as resolve18 } from "path";
5365
+ import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
5366
+ import { resolve as resolve19 } from "path";
5285
5367
  import {
5286
5368
  listAuthorityRuns as listAuthorityRuns2,
5287
5369
  readAuthorityRun as readAuthorityRun3,
@@ -5302,8 +5384,8 @@ async function executeInspect(context, args) {
5302
5384
  if (!latestRun) {
5303
5385
  throw new CliError2(`No runs found for ${requiredTask}.`);
5304
5386
  }
5305
- const logsPath = resolve18(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5306
- if (!existsSync10(logsPath)) {
5387
+ const logsPath = resolve19(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5388
+ if (!existsSync11(logsPath)) {
5307
5389
  throw new CliError2(`No logs found for run ${latestRun.runId}.`);
5308
5390
  }
5309
5391
  await context.runCommand(["cat", logsPath]);
@@ -5313,7 +5395,7 @@ async function executeInspect(context, args) {
5313
5395
  const { value: task, rest: remaining } = takeOption(rest, "--task");
5314
5396
  requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
5315
5397
  const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
5316
- const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync10(path));
5398
+ const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync11(path));
5317
5399
  if (!artifactRoot) {
5318
5400
  throw new CliError2(`No artifacts found for ${requiredTask}.`);
5319
5401
  }
@@ -5370,10 +5452,10 @@ async function executeInspect(context, args) {
5370
5452
  case "failures": {
5371
5453
  requireNoExtraArgs(rest, "bun run rig inspect failures");
5372
5454
  const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
5373
- if (!existsSync10(failed)) {
5455
+ if (!existsSync11(failed)) {
5374
5456
  console.log("No failures recorded.");
5375
5457
  } else {
5376
- process.stdout.write(readFileSync7(failed, "utf-8"));
5458
+ process.stdout.write(readFileSync8(failed, "utf-8"));
5377
5459
  }
5378
5460
  return { ok: true, group: "inspect", command };
5379
5461
  }
@@ -5390,11 +5472,11 @@ async function executeInspect(context, args) {
5390
5472
  return { ok: true, group: "inspect", command };
5391
5473
  case "audit": {
5392
5474
  requireNoExtraArgs(rest, "bun run rig inspect audit");
5393
- const auditPath = resolve18(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5394
- if (!existsSync10(auditPath)) {
5475
+ const auditPath = resolve19(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5476
+ if (!existsSync11(auditPath)) {
5395
5477
  console.log("No audit log found.");
5396
5478
  } else {
5397
- const lines = readFileSync7(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5479
+ const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5398
5480
  for (const line of lines) {
5399
5481
  console.log(line);
5400
5482
  }
@@ -6023,8 +6105,8 @@ async function executeRemote(context, args) {
6023
6105
  }
6024
6106
 
6025
6107
  // packages/cli/src/commands/run.ts
6026
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
6027
- import { resolve as resolve19 } from "path";
6108
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
6109
+ import { resolve as resolve20 } from "path";
6028
6110
  import { createInterface as createInterface2 } from "readline/promises";
6029
6111
  import {
6030
6112
  listAuthorityRuns as listAuthorityRuns3,
@@ -6038,6 +6120,7 @@ import {
6038
6120
  listOpenEpics,
6039
6121
  resolveDefaultEpic,
6040
6122
  runResume,
6123
+ runRestart,
6041
6124
  runStatus,
6042
6125
  runStop,
6043
6126
  startRun,
@@ -6146,6 +6229,17 @@ async function attachRunOperatorView(context, input) {
6146
6229
  }
6147
6230
 
6148
6231
  // packages/cli/src/commands/run.ts
6232
+ function normalizeRemoteRunDetails(payload) {
6233
+ const run = payload.run;
6234
+ if (!run || typeof run !== "object" || Array.isArray(run))
6235
+ return null;
6236
+ return {
6237
+ ...run,
6238
+ ...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
6239
+ ...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
6240
+ ...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
6241
+ };
6242
+ }
6149
6243
  function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
6150
6244
  if (noEpicPrompt) {
6151
6245
  return false;
@@ -6284,7 +6378,7 @@ async function executeRun(context, args) {
6284
6378
  if (!run.value) {
6285
6379
  throw new CliError2("run show requires --run <id>.");
6286
6380
  }
6287
- const record = readAuthorityRun4(context.projectRoot, run.value);
6381
+ const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
6288
6382
  if (!record) {
6289
6383
  throw new CliError2(`Run not found: ${run.value}`, 2);
6290
6384
  }
@@ -6303,7 +6397,7 @@ async function executeRun(context, args) {
6303
6397
  if (!run.value) {
6304
6398
  throw new CliError2("run timeline requires --run <id>.");
6305
6399
  }
6306
- const timelinePath = resolve19(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6400
+ const timelinePath = resolve20(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6307
6401
  const printEvents = () => {
6308
6402
  const events2 = readJsonlFile4(timelinePath);
6309
6403
  if (context.outputMode === "text") {
@@ -6315,12 +6409,12 @@ async function executeRun(context, args) {
6315
6409
  };
6316
6410
  const events = printEvents();
6317
6411
  if (follow.value && context.outputMode === "text") {
6318
- let lastLength = existsSync11(timelinePath) ? readFileSync8(timelinePath, "utf8").length : 0;
6412
+ let lastLength = existsSync12(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
6319
6413
  while (true) {
6320
6414
  await Bun.sleep(1000);
6321
- if (!existsSync11(timelinePath))
6415
+ if (!existsSync12(timelinePath))
6322
6416
  continue;
6323
- const next = readFileSync8(timelinePath, "utf8");
6417
+ const next = readFileSync9(timelinePath, "utf8");
6324
6418
  if (next.length <= lastLength)
6325
6419
  continue;
6326
6420
  const delta = next.slice(lastLength);
@@ -6465,6 +6559,20 @@ async function executeRun(context, args) {
6465
6559
  }
6466
6560
  return { ok: true, group: "run", command, details: resumed };
6467
6561
  }
6562
+ case "restart": {
6563
+ requireNoExtraArgs(rest, "bun run rig run restart");
6564
+ if (context.dryRun) {
6565
+ if (context.outputMode === "text") {
6566
+ console.log("[dry-run] rig run restart");
6567
+ }
6568
+ return { ok: true, group: "run", command };
6569
+ }
6570
+ const restarted = await runRestart(context.projectRoot, runtimeContext);
6571
+ if (context.outputMode === "text") {
6572
+ console.log(`Restarted run: ${restarted.runId}`);
6573
+ }
6574
+ return { ok: true, group: "run", command, details: restarted };
6575
+ }
6468
6576
  case "stop": {
6469
6577
  const runOption = takeOption(rest, "--run");
6470
6578
  const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
@@ -6522,7 +6630,7 @@ async function executeServer(context, args, options) {
6522
6630
  const authTokenResult = takeOption(pending, "--auth-token");
6523
6631
  pending = authTokenResult.rest;
6524
6632
  requireNoExtraArgs(pending, "bun run rig server start [--host <host>] [--port <n>] [--poll-ms <n>] [--auth-token <token>]");
6525
- const commandParts = ["bun", "run", "packages/server/src/server.ts", "start"];
6633
+ const commandParts = ["rig-server", "start"];
6526
6634
  if (hostResult.value) {
6527
6635
  commandParts.push("--host", hostResult.value);
6528
6636
  }
@@ -6545,7 +6653,7 @@ async function executeServer(context, args, options) {
6545
6653
  const eventResult = takeOption(pending, "--event");
6546
6654
  pending = eventResult.rest;
6547
6655
  requireNoExtraArgs(pending, "bun run rig server notify-test [--event <type>]");
6548
- const commandParts = ["bun", "run", "packages/server/src/server.ts", "notify-test"];
6656
+ const commandParts = ["rig-server", "notify-test"];
6549
6657
  if (eventResult.value) {
6550
6658
  commandParts.push("--event", eventResult.value);
6551
6659
  }
@@ -6606,10 +6714,10 @@ async function executeServer(context, args, options) {
6606
6714
  }
6607
6715
 
6608
6716
  // packages/cli/src/commands/task.ts
6609
- import { readFileSync as readFileSync9 } from "fs";
6717
+ import { readFileSync as readFileSync10 } from "fs";
6610
6718
  import { spawnSync as spawnSync4 } from "child_process";
6611
6719
  import { createInterface as createInterface4 } from "readline/promises";
6612
- import { resolve as resolve20 } from "path";
6720
+ import { resolve as resolve21 } from "path";
6613
6721
  import {
6614
6722
  taskArtifactDir,
6615
6723
  taskArtifacts,
@@ -6940,7 +7048,7 @@ async function executeTask(context, args, options) {
6940
7048
  const fileFlag = takeOption(rest.slice(1), "--file");
6941
7049
  let content;
6942
7050
  if (fileFlag.value) {
6943
- content = readFileSync9(resolve20(context.projectRoot, fileFlag.value), "utf-8");
7051
+ content = readFileSync10(resolve21(context.projectRoot, fileFlag.value), "utf-8");
6944
7052
  } else {
6945
7053
  content = await readStdin();
6946
7054
  }
@@ -7161,8 +7269,8 @@ async function executeTask(context, args, options) {
7161
7269
  }
7162
7270
 
7163
7271
  // packages/cli/src/commands/task-run-driver.ts
7164
- import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync10, statSync as statSync2 } from "fs";
7165
- import { resolve as resolve21 } from "path";
7272
+ import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
7273
+ import { resolve as resolve22 } from "path";
7166
7274
  import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
7167
7275
  import { createInterface as createLineInterface } from "readline";
7168
7276
  import { loadConfig as loadConfig2 } from "@rig/core/load-config";
@@ -7196,7 +7304,24 @@ import {
7196
7304
  commitRunChanges,
7197
7305
  runPrAutomation
7198
7306
  } from "@rig/runtime/control-plane/native/pr-automation";
7307
+ function looksLikeGitHubToken(value) {
7308
+ const token = value?.trim();
7309
+ if (!token)
7310
+ return false;
7311
+ return /^(gh[opusr]_|github_pat_)/.test(token);
7312
+ }
7313
+ function githubBridgeEnv(token) {
7314
+ const clean = token?.trim();
7315
+ if (!clean)
7316
+ return {};
7317
+ return {
7318
+ RIG_GITHUB_TOKEN: clean,
7319
+ GITHUB_TOKEN: clean,
7320
+ GH_TOKEN: clean
7321
+ };
7322
+ }
7199
7323
  function buildPiRigBridgeEnv(input) {
7324
+ const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
7200
7325
  return {
7201
7326
  RIG_PROJECT_ROOT: input.projectRoot,
7202
7327
  PROJECT_RIG_ROOT: input.projectRoot,
@@ -7206,7 +7331,7 @@ function buildPiRigBridgeEnv(input) {
7206
7331
  RIG_RUNTIME_ADAPTER: "pi",
7207
7332
  ...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
7208
7333
  ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
7209
- ...input.githubToken ? { RIG_GITHUB_TOKEN: input.githubToken } : {}
7334
+ ...githubBridgeEnv(githubToken)
7210
7335
  };
7211
7336
  }
7212
7337
  function runGitSync(cwd, args, input) {
@@ -7228,12 +7353,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
7228
7353
  return 0;
7229
7354
  let copied = 0;
7230
7355
  for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
7231
- const sourcePath = resolve21(sourceRoot, relativePath);
7232
- const targetPath = resolve21(targetRoot, relativePath);
7356
+ const sourcePath = resolve22(sourceRoot, relativePath);
7357
+ const targetPath = resolve22(targetRoot, relativePath);
7233
7358
  try {
7234
7359
  if (!statSync2(sourcePath).isFile())
7235
7360
  continue;
7236
- mkdirSync8(resolve21(targetPath, ".."), { recursive: true });
7361
+ mkdirSync8(resolve22(targetPath, ".."), { recursive: true });
7237
7362
  copyFileSync3(sourcePath, targetPath);
7238
7363
  copied += 1;
7239
7364
  } catch {}
@@ -7267,6 +7392,14 @@ function buildTaskRunReviewEnv(config) {
7267
7392
  ...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
7268
7393
  };
7269
7394
  }
7395
+ function buildDirtyBaselineHandshakeEnv(input) {
7396
+ if (input.baselineMode !== "dirty-snapshot")
7397
+ return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
7398
+ return {
7399
+ RIG_BASELINE_MODE: "dirty-snapshot",
7400
+ RIG_DIRTY_BASELINE_READY_FILE: resolve22(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
7401
+ };
7402
+ }
7270
7403
  function positiveInt(value, fallback) {
7271
7404
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
7272
7405
  }
@@ -7359,6 +7492,12 @@ function appendPiStageLog(input) {
7359
7492
  });
7360
7493
  emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
7361
7494
  }
7495
+ async function runCheckedCommand(command, args, cwd, label = "git") {
7496
+ const result = await command(args, { cwd });
7497
+ if (result.exitCode !== 0) {
7498
+ throw new Error(`${label} ${args.join(" ")} failed (${result.exitCode}): ${result.stderr ?? result.stdout ?? ""}`.trim());
7499
+ }
7500
+ }
7362
7501
  function createCommandRunner(binary) {
7363
7502
  return async (args, options) => {
7364
7503
  const child = spawn2(binary, [...args], {
@@ -7369,9 +7508,9 @@ function createCommandRunner(binary) {
7369
7508
  const stderrChunks = [];
7370
7509
  child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7371
7510
  child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7372
- return await new Promise((resolve22) => {
7373
- child.once("error", (error) => resolve22({ exitCode: 1, stderr: error.message }));
7374
- child.once("close", (code) => resolve22({
7511
+ return await new Promise((resolve23) => {
7512
+ child.once("error", (error) => resolve23({ exitCode: 1, stderr: error.message }));
7513
+ child.once("close", (code) => resolve23({
7375
7514
  exitCode: code ?? 1,
7376
7515
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
7377
7516
  stderr: Buffer.concat(stderrChunks).toString("utf8")
@@ -7434,6 +7573,7 @@ async function runTaskRunPostValidationLifecycle(input) {
7434
7573
  command: gitCommand
7435
7574
  });
7436
7575
  stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
7576
+ await runCheckedCommand(gitCommand, ["push", "--set-upstream", "origin", branch], workspace, "git");
7437
7577
  stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
7438
7578
  const pr = await prAutomation({
7439
7579
  projectRoot: workspace,
@@ -7444,7 +7584,6 @@ async function runTaskRunPostValidationLifecycle(input) {
7444
7584
  sourceTask: input.sourceTask,
7445
7585
  uploadedSnapshot: input.uploadedSnapshot,
7446
7586
  command: ghCommand,
7447
- gitCommand,
7448
7587
  steerPi,
7449
7588
  lifecycle: {
7450
7589
  onPrOpened: async ({ prUrl }) => {
@@ -7567,7 +7706,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
7567
7706
  return null;
7568
7707
  }
7569
7708
  for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
7570
- const summary = readJsonFile3(resolve21(artifactDir, "validation-summary.json"), null);
7709
+ const summary = readJsonFile3(resolve22(artifactDir, "validation-summary.json"), null);
7571
7710
  if (!summary || summary.status !== "fail") {
7572
7711
  continue;
7573
7712
  }
@@ -7648,9 +7787,9 @@ function readTaskRunAcceptedArtifactState(input) {
7648
7787
  if (!input.taskId || !input.workspaceDir) {
7649
7788
  return { accepted: false, reason: null };
7650
7789
  }
7651
- const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7652
- const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7653
- const taskResultPath = resolve21(artifactDir, "task-result.json");
7790
+ const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7791
+ const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7792
+ const taskResultPath = resolve22(artifactDir, "task-result.json");
7654
7793
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7655
7794
  if (reviewStatus !== "APPROVED") {
7656
7795
  return { accepted: false, reason: null };
@@ -7687,12 +7826,12 @@ function resolveTaskRunRetryContext(input) {
7687
7826
  if (!input.taskId || !input.workspaceDir) {
7688
7827
  return { shouldRetry: false, failureDetail: null, nextPrompt: null };
7689
7828
  }
7690
- const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7691
- const reviewStatePath = resolve21(artifactDir, "review-state.json");
7692
- const reviewFeedbackPath = resolve21(artifactDir, "review-feedback.md");
7693
- const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7694
- const failedApproachesPath = resolve21(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7695
- const validationSummaryPath = resolve21(artifactDir, "validation-summary.json");
7829
+ const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7830
+ const reviewStatePath = resolve22(artifactDir, "review-state.json");
7831
+ const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
7832
+ const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7833
+ const failedApproachesPath = resolve22(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7834
+ const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
7696
7835
  const reviewState = readJsonFile3(reviewStatePath, null);
7697
7836
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7698
7837
  const reviewRejected = isTaskRunReviewRejected(reviewState);
@@ -7747,11 +7886,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
7747
7886
  return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
7748
7887
  }
7749
7888
  function readTaskRunReviewStatus(reviewStatusPath) {
7750
- if (!existsSync12(reviewStatusPath)) {
7889
+ if (!existsSync13(reviewStatusPath)) {
7751
7890
  return null;
7752
7891
  }
7753
7892
  try {
7754
- const status = readFileSync10(reviewStatusPath, "utf8").trim().toUpperCase();
7893
+ const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
7755
7894
  return status === "APPROVED" || status === "REJECTED" ? status : null;
7756
7895
  } catch {
7757
7896
  return null;
@@ -7834,8 +7973,11 @@ function stringArrayField(record, key) {
7834
7973
  }
7835
7974
  async function executeRigOwnedTaskRun(context, input) {
7836
7975
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
7976
+ const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
7977
+ const resumeMode = process.env.RIG_RUN_RESUME === "1";
7978
+ const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
7837
7979
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
7838
- const prompt = buildRunPrompt({
7980
+ let prompt = buildRunPrompt({
7839
7981
  projectRoot: context.projectRoot,
7840
7982
  taskId: input.taskId,
7841
7983
  fallbackTitle: input.title,
@@ -7889,14 +8031,14 @@ async function executeRigOwnedTaskRun(context, input) {
7889
8031
  taskId: runtimeTaskId,
7890
8032
  createdAt: startedAt,
7891
8033
  runtimeAdapter: input.runtimeAdapter,
7892
- status: "created"
8034
+ status: resumeMode ? "preparing" : "created"
7893
8035
  });
7894
8036
  patchAuthorityRun(context.projectRoot, input.runId, {
7895
8037
  status: "preparing",
7896
8038
  startedAt,
7897
8039
  completedAt: null,
7898
8040
  errorText: null,
7899
- artifactRoot: null,
8041
+ artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
7900
8042
  runtimeAdapter: input.runtimeAdapter,
7901
8043
  runtimeMode: input.runtimeMode,
7902
8044
  interactionMode: input.interactionMode,
@@ -7912,9 +8054,9 @@ async function executeRigOwnedTaskRun(context, input) {
7912
8054
  detail: input.taskId ?? input.title ?? runtimeTaskId
7913
8055
  });
7914
8056
  appendRunLog(context.projectRoot, input.runId, {
7915
- id: `log:${input.runId}:start`,
7916
- title: "Rig task run started",
7917
- detail: input.taskId ?? input.title ?? runtimeTaskId,
8057
+ id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
8058
+ title: resumeMode ? "Rig task run resumed" : "Rig task run started",
8059
+ detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
7918
8060
  tone: "info",
7919
8061
  status: "preparing",
7920
8062
  createdAt: startedAt
@@ -7935,7 +8077,22 @@ async function executeRigOwnedTaskRun(context, input) {
7935
8077
  const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
7936
8078
  const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
7937
8079
  const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
7938
- patchAuthorityRun(context.projectRoot, input.runId, { planning: planningClassification });
8080
+ const planningArtifactPath = resolve22("artifacts", runtimeTaskId, "implementation-plan.md");
8081
+ const persistedPlanning = {
8082
+ ...planningClassification,
8083
+ classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
8084
+ artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
8085
+ classifiedAt: new Date().toISOString()
8086
+ };
8087
+ mkdirSync8(resolve22(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
8088
+ writeFileSync6(resolve22(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
8089
+ `, "utf8");
8090
+ patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
8091
+ prompt = `${prompt}
8092
+
8093
+ Rig planning classification:
8094
+ ${JSON.stringify(persistedPlanning, null, 2)}
8095
+ ${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."}`;
7939
8096
  if (input.runtimeAdapter === "pi") {
7940
8097
  for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
7941
8098
  appendPiStageLog({
@@ -7977,11 +8134,11 @@ async function executeRigOwnedTaskRun(context, input) {
7977
8134
  let reviewAction;
7978
8135
  let verificationStarted = false;
7979
8136
  let reviewStarted = false;
7980
- let latestRuntimeWorkspace = null;
7981
- let latestSessionDir = null;
7982
- let latestLogsDir = null;
8137
+ let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
8138
+ let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve22(existingRunRecord.sessionPath, "..") : null;
8139
+ let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
7983
8140
  let latestProviderCommand = null;
7984
- let latestRuntimeBranch = null;
8141
+ let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
7985
8142
  let snapshotSidecarPromise = null;
7986
8143
  let dirtyBaselineApplied = false;
7987
8144
  const childEnv = {
@@ -7999,9 +8156,14 @@ async function executeRigOwnedTaskRun(context, input) {
7999
8156
  RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
8000
8157
  RIG_SERVER_RUN_ID: input.runId
8001
8158
  },
8002
- ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
8159
+ ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
8160
+ ...resumeMode ? {
8161
+ RIG_RUN_RESUME: "1",
8162
+ RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
8163
+ } : {}
8003
8164
  };
8004
8165
  Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
8166
+ Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
8005
8167
  const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
8006
8168
  const maxAttempts = automationLimits.maxValidationAttempts;
8007
8169
  const promoteToValidating = (detail) => {
@@ -8056,22 +8218,29 @@ async function executeRigOwnedTaskRun(context, input) {
8056
8218
  patchAuthorityRun(context.projectRoot, input.runId, {
8057
8219
  status: "running",
8058
8220
  worktreePath: latestRuntimeWorkspace,
8059
- artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve21(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8221
+ artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve22(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8060
8222
  logRoot: latestLogsDir,
8061
- sessionPath: latestSessionDir ? resolve21(latestSessionDir, "session.json") : null,
8062
- sessionLogPath: latestLogsDir ? resolve21(latestLogsDir, "agent-stdout.log") : null,
8223
+ sessionPath: latestSessionDir ? resolve22(latestSessionDir, "session.json") : null,
8224
+ sessionLogPath: latestLogsDir ? resolve22(latestLogsDir, "agent-stdout.log") : null,
8063
8225
  branch: runtimeId
8064
8226
  });
8065
8227
  if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
8066
8228
  dirtyBaselineApplied = true;
8067
8229
  const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
8230
+ const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
8231
+ if (readyFile) {
8232
+ mkdirSync8(resolve22(readyFile, ".."), { recursive: true });
8233
+ writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
8234
+ `, "utf8");
8235
+ }
8068
8236
  appendRunLog(context.projectRoot, input.runId, {
8069
8237
  id: `log:${input.runId}:dirty-baseline`,
8070
8238
  title: "Dirty baseline snapshot",
8071
8239
  detail: dirty.detail,
8072
8240
  tone: dirty.applied ? "tool" : "info",
8073
8241
  status: dirty.applied ? "completed" : "skipped",
8074
- createdAt: new Date().toISOString()
8242
+ createdAt: new Date().toISOString(),
8243
+ payload: readyFile ? { readyFile } : undefined
8075
8244
  });
8076
8245
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
8077
8246
  }
@@ -8297,7 +8466,36 @@ async function executeRigOwnedTaskRun(context, input) {
8297
8466
  let reviewFailureDetail = null;
8298
8467
  const stderrLines = [];
8299
8468
  const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
8300
- for (let attempt = 1;attempt <= maxAttempts; attempt += 1) {
8469
+ if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
8470
+ appendRunLog(context.projectRoot, input.runId, {
8471
+ id: `log:${input.runId}:resume-closeout-phase`,
8472
+ title: "Resume continuing from closeout phase",
8473
+ detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
8474
+ tone: "info",
8475
+ status: resumePreviousStatus,
8476
+ createdAt: new Date().toISOString()
8477
+ });
8478
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
8479
+ exit = { code: 0, signal: null };
8480
+ } else if (resumeMode && latestRuntimeWorkspace) {
8481
+ const acceptedArtifactState = readTaskRunAcceptedArtifactState({
8482
+ taskId: input.taskId ?? runtimeTaskId,
8483
+ workspaceDir: latestRuntimeWorkspace
8484
+ });
8485
+ if (acceptedArtifactState.accepted) {
8486
+ appendRunLog(context.projectRoot, input.runId, {
8487
+ id: `log:${input.runId}:resume-accepted-artifacts`,
8488
+ title: "Resume found accepted artifacts; continuing closeout",
8489
+ detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
8490
+ tone: "info",
8491
+ status: "validating",
8492
+ createdAt: new Date().toISOString()
8493
+ });
8494
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
8495
+ exit = { code: 0, signal: null };
8496
+ }
8497
+ }
8498
+ for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
8301
8499
  const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
8302
8500
  const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
8303
8501
  cwd: context.projectRoot,
@@ -8338,7 +8536,7 @@ async function executeRigOwnedTaskRun(context, input) {
8338
8536
  let acceptedArtifactObservedAt = null;
8339
8537
  let acceptedArtifactPollTimer = null;
8340
8538
  let acceptedArtifactKillTimer = null;
8341
- const attemptExit = await new Promise((resolve22) => {
8539
+ const attemptExit = await new Promise((resolve23) => {
8342
8540
  let settled = false;
8343
8541
  const settle = (result) => {
8344
8542
  if (settled)
@@ -8346,7 +8544,7 @@ async function executeRigOwnedTaskRun(context, input) {
8346
8544
  settled = true;
8347
8545
  if (acceptedArtifactPollTimer)
8348
8546
  clearInterval(acceptedArtifactPollTimer);
8349
- resolve22(result);
8547
+ resolve23(result);
8350
8548
  };
8351
8549
  const pollAcceptedArtifacts = () => {
8352
8550
  const artifactState = readTaskRunAcceptedArtifactState({
@@ -8543,6 +8741,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8543
8741
  });
8544
8742
  throw new CliError2(terminalFailureDetail, exit.code ?? 1);
8545
8743
  }
8744
+ if (planningClassification.planningRequired) {
8745
+ const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
8746
+ const expectedPlanPath = resolve22(planWorkspace, planningArtifactPath);
8747
+ if (!existsSync13(expectedPlanPath)) {
8748
+ const failedAt = new Date().toISOString();
8749
+ const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
8750
+ patchAuthorityRun(context.projectRoot, input.runId, {
8751
+ status: "needs_attention",
8752
+ completedAt: failedAt,
8753
+ errorText: failureDetail
8754
+ });
8755
+ appendRunLog(context.projectRoot, input.runId, {
8756
+ id: `log:${input.runId}:plan-artifact-missing`,
8757
+ title: "Required plan artifact missing",
8758
+ detail: failureDetail,
8759
+ tone: "error",
8760
+ status: "needs_attention",
8761
+ createdAt: failedAt
8762
+ });
8763
+ emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
8764
+ throw new CliError2(failureDetail, 1);
8765
+ }
8766
+ }
8546
8767
  const runPiPrFeedbackFix = async (message2) => {
8547
8768
  appendPiStageLog({
8548
8769
  projectRoot: context.projectRoot,
@@ -8600,9 +8821,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8600
8821
  });
8601
8822
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
8602
8823
  });
8603
- const exitCode = await new Promise((resolve22) => {
8604
- child.once("error", () => resolve22(1));
8605
- child.once("close", (code) => resolve22(code ?? 1));
8824
+ const exitCode = await new Promise((resolve23) => {
8825
+ child.once("error", () => resolve23(1));
8826
+ child.once("close", (code) => resolve23(code ?? 1));
8606
8827
  });
8607
8828
  if (exitCode !== 0) {
8608
8829
  throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
@@ -8721,8 +8942,8 @@ async function executeTest(context, args) {
8721
8942
  }
8722
8943
 
8723
8944
  // packages/cli/src/commands/setup.ts
8724
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "fs";
8725
- import { resolve as resolve22 } from "path";
8945
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
8946
+ import { resolve as resolve23 } from "path";
8726
8947
  import { createPluginHost } from "@rig/core";
8727
8948
  import {
8728
8949
  isSupportedBunVersion as isSupportedBunVersion2,
@@ -8785,9 +9006,9 @@ function runSetupInit(projectRoot) {
8785
9006
  mkdirSync9(stateDir, { recursive: true });
8786
9007
  mkdirSync9(logsDir, { recursive: true });
8787
9008
  mkdirSync9(artifactsDir, { recursive: true });
8788
- const failuresPath = resolve22(stateDir, "failed_approaches.md");
8789
- if (!existsSync13(failuresPath)) {
8790
- writeFileSync6(failuresPath, `# Failed Approaches
9009
+ const failuresPath = resolve23(stateDir, "failed_approaches.md");
9010
+ if (!existsSync14(failuresPath)) {
9011
+ writeFileSync7(failuresPath, `# Failed Approaches
8791
9012
 
8792
9013
  `, "utf-8");
8793
9014
  }
@@ -8804,18 +9025,18 @@ async function runSetupCheck(projectRoot) {
8804
9025
  }
8805
9026
  async function runSetupPreflight(projectRoot) {
8806
9027
  await runSetupCheck(projectRoot);
8807
- const validationRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
8808
- if (existsSync13(validationRoot)) {
9028
+ const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
9029
+ if (existsSync14(validationRoot)) {
8809
9030
  const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
8810
9031
  for (const validator of validators) {
8811
- const script = resolve22(validationRoot, validator.name, "validate.sh");
8812
- if (existsSync13(script)) {
9032
+ const script = resolve23(validationRoot, validator.name, "validate.sh");
9033
+ if (existsSync14(script)) {
8813
9034
  console.log(`OK: validator script ${script}`);
8814
9035
  }
8815
9036
  }
8816
9037
  }
8817
- const hooksRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
8818
- if (existsSync13(hooksRoot)) {
9038
+ const hooksRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
9039
+ if (existsSync14(hooksRoot)) {
8819
9040
  const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
8820
9041
  for (const hook of hooks) {
8821
9042
  console.log(`OK: hook ${hook}`);
@@ -9192,8 +9413,8 @@ async function executeGroup(context, group, args) {
9192
9413
  }
9193
9414
  }
9194
9415
  // packages/cli/src/launcher.ts
9195
- import { existsSync as existsSync14 } from "fs";
9196
- import { resolve as resolve23 } from "path";
9416
+ import { existsSync as existsSync15 } from "fs";
9417
+ import { basename as basename2, resolve as resolve24 } from "path";
9197
9418
  import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
9198
9419
  import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
9199
9420
  function parsePolicyMode(value) {
@@ -9206,7 +9427,7 @@ function parsePolicyMode(value) {
9206
9427
  throw new Error(`Invalid --policy-mode value: ${value}. Use off|observe|enforce.`);
9207
9428
  }
9208
9429
  function hasRigProjectMarker(candidate) {
9209
- 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"));
9430
+ 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"));
9210
9431
  }
9211
9432
  function resolveProjectRoot({
9212
9433
  envProjectRoot,
@@ -9215,17 +9436,19 @@ function resolveProjectRoot({
9215
9436
  cwd = process.cwd()
9216
9437
  }) {
9217
9438
  if (envProjectRoot) {
9218
- return resolve23(cwd, envProjectRoot);
9439
+ return resolve24(cwd, envProjectRoot);
9219
9440
  }
9220
9441
  const fallbackImportDir = importDir ?? cwd;
9221
- const candidates = [cwd, resolve23(execPath, "..", ".."), resolve23(fallbackImportDir, "..")];
9442
+ const execName = basename2(execPath).toLowerCase();
9443
+ const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve24(execPath, "..", "..")] : [];
9444
+ const candidates = [cwd, ...execCandidates, resolve24(fallbackImportDir, "..")];
9222
9445
  for (const candidate of candidates) {
9223
9446
  const nearest = resolveNearestRigProjectRoot(candidate);
9224
9447
  if (hasRigProjectMarker(nearest)) {
9225
9448
  return nearest;
9226
9449
  }
9227
9450
  }
9228
- return resolve23(cwd);
9451
+ return resolve24(cwd);
9229
9452
  }
9230
9453
  function normalizeCliErrorCode(message2, isCliError) {
9231
9454
  if (message2.startsWith("Invalid --policy-mode value:")) {
@@ -9292,7 +9515,7 @@ async function runRigCli(module, options = {}) {
9292
9515
  runId: context.runId,
9293
9516
  outcome,
9294
9517
  eventsFile: context.eventBus.getEventsFile(),
9295
- policyFile: resolve23(projectRoot, "rig", "policy", "policy.json"),
9518
+ policyFile: resolve24(projectRoot, "rig", "policy", "policy.json"),
9296
9519
  policyMode: context.policyMode ?? policyMode ?? module.loadPolicy(projectRoot).mode
9297
9520
  }, null, 2));
9298
9521
  }