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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,18 +2845,38 @@ 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));
2849
+ }
2850
+ function isRetryableProjectRootSwitchError(error) {
2851
+ if (!(error instanceof Error))
2852
+ return false;
2853
+ const message = error.message.toLowerCase();
2854
+ return message.includes("rig server request failed (401): auth-required") || message.includes("rig server request failed (401): github-token-required") || message.includes("rig server request failed (502)") || message.includes("rig server request failed (503)") || message.includes("bad gateway") || message.includes("fetch failed") || message.includes("econnrefused") || message.includes("connection refused");
2830
2855
  }
2831
2856
  async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
2832
- const switched = await requestServerJson(context, "/api/server/project-root", {
2833
- method: "POST",
2834
- headers: { "content-type": "application/json" },
2835
- body: JSON.stringify({ projectRoot })
2836
- });
2837
2857
  const timeoutMs = options.timeoutMs ?? 30000;
2838
2858
  const pollMs = options.pollMs ?? 1000;
2839
2859
  const deadline = Date.now() + timeoutMs;
2840
2860
  let lastError;
2861
+ let switched = null;
2862
+ while (Date.now() < deadline) {
2863
+ try {
2864
+ switched = await requestServerJson(context, "/api/server/project-root", {
2865
+ method: "POST",
2866
+ headers: { "content-type": "application/json" },
2867
+ body: JSON.stringify({ projectRoot })
2868
+ });
2869
+ break;
2870
+ } catch (error) {
2871
+ lastError = error;
2872
+ if (!isRetryableProjectRootSwitchError(error))
2873
+ throw error;
2874
+ await sleep(pollMs);
2875
+ }
2876
+ }
2877
+ if (!switched) {
2878
+ throw new CliError2(`Rig server did not accept project-root switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no response")}).`, 1);
2879
+ }
2841
2880
  while (Date.now() < deadline) {
2842
2881
  try {
2843
2882
  const status = await requestServerJson(context, "/api/server/status");
@@ -2917,9 +2956,9 @@ async function submitTaskRunViaServer(context, input) {
2917
2956
  }
2918
2957
 
2919
2958
  // packages/cli/src/commands/_pi-install.ts
2920
- import { existsSync as existsSync5, readFileSync as readFileSync3, rmSync as rmSync3 } from "fs";
2959
+ import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
2921
2960
  import { homedir as homedir3 } from "os";
2922
- import { resolve as resolve9 } from "path";
2961
+ import { resolve as resolve10 } from "path";
2923
2962
  var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
2924
2963
  var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
2925
2964
  export { default } from '@rig/pi-rig';
@@ -2934,11 +2973,11 @@ async function defaultCommandRunner(command, options = {}) {
2934
2973
  return { exitCode, stdout, stderr };
2935
2974
  }
2936
2975
  function resolvePiRigExtensionPath(homeDir) {
2937
- return resolve9(homeDir, ".pi", "agent", "extensions", "pi-rig");
2976
+ return resolve10(homeDir, ".pi", "agent", "extensions", "pi-rig");
2938
2977
  }
2939
- function resolvePiRigPackageSource(projectRoot, exists = existsSync5) {
2940
- const localPackage = resolve9(projectRoot, "packages", "pi-rig");
2941
- if (exists(resolve9(localPackage, "package.json")))
2978
+ function resolvePiRigPackageSource(projectRoot, exists = existsSync6) {
2979
+ const localPackage = resolve10(projectRoot, "packages", "pi-rig");
2980
+ if (exists(resolve10(localPackage, "package.json")))
2942
2981
  return localPackage;
2943
2982
  return `npm:${PI_RIG_PACKAGE_NAME}`;
2944
2983
  }
@@ -2989,13 +3028,13 @@ async function ensurePiBinaryAvailable(input) {
2989
3028
  ...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
2990
3029
  };
2991
3030
  }
2992
- function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync5) {
3031
+ function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync6) {
2993
3032
  const extensionPath = resolvePiRigExtensionPath(homeDir);
2994
- const indexPath = resolve9(extensionPath, "index.ts");
3033
+ const indexPath = resolve10(extensionPath, "index.ts");
2995
3034
  if (!exists(indexPath))
2996
3035
  return;
2997
3036
  try {
2998
- const content = readFileSync3(indexPath, "utf8");
3037
+ const content = readFileSync4(indexPath, "utf8");
2999
3038
  if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
3000
3039
  rmSync3(extensionPath, { recursive: true, force: true });
3001
3040
  }
@@ -3011,13 +3050,13 @@ async function checkPiRigInstall(input = {}) {
3011
3050
  piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
3012
3051
  };
3013
3052
  }
3014
- const exists = input.exists ?? existsSync5;
3053
+ const exists = input.exists ?? existsSync6;
3015
3054
  const runner = input.commandRunner ?? defaultCommandRunner;
3016
3055
  const piResult = await safeRun(runner, ["pi", "--version"]);
3017
3056
  const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
3018
3057
  const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
3019
3058
  ${piListResult.stderr}`);
3020
- const legacyBridge = exists(resolve9(extensionPath, "index.ts"));
3059
+ const legacyBridge = exists(resolve10(extensionPath, "index.ts"));
3021
3060
  const hasPiRig = listedPiRig;
3022
3061
  return {
3023
3062
  extensionPath,
@@ -3355,7 +3394,7 @@ async function executeQueue(context, args) {
3355
3394
  }
3356
3395
 
3357
3396
  // packages/cli/src/commands/agent.ts
3358
- import { resolve as resolve11 } from "path";
3397
+ import { resolve as resolve12 } from "path";
3359
3398
  import {
3360
3399
  agentId,
3361
3400
  cleanupAgentRuntime,
@@ -3365,8 +3404,8 @@ import {
3365
3404
  } from "@rig/runtime/control-plane/runtime/isolation";
3366
3405
 
3367
3406
  // packages/cli/src/commands/_authority-runs.ts
3368
- import { existsSync as existsSync6 } from "fs";
3369
- import { resolve as resolve10 } from "path";
3407
+ import { existsSync as existsSync7 } from "fs";
3408
+ import { resolve as resolve11 } from "path";
3370
3409
  import {
3371
3410
  readAuthorityRun,
3372
3411
  readJsonlFile as readJsonlFile2,
@@ -3388,8 +3427,8 @@ function normalizeRuntimeAdapter(value) {
3388
3427
  return "claude-code";
3389
3428
  }
3390
3429
  function readLatestBeadRecord(projectRoot, taskId) {
3391
- const issuesPath = resolve10(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3392
- if (!existsSync6(issuesPath)) {
3430
+ const issuesPath = resolve11(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3431
+ if (!existsSync7(issuesPath)) {
3393
3432
  return null;
3394
3433
  }
3395
3434
  let latest = null;
@@ -3424,6 +3463,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3424
3463
  const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
3425
3464
  const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
3426
3465
  const next = {
3466
+ ...existing ?? {},
3427
3467
  runId: input.runId,
3428
3468
  projectRoot,
3429
3469
  workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
@@ -3456,7 +3496,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3456
3496
  } else if ("errorText" in next) {
3457
3497
  delete next.errorText;
3458
3498
  }
3459
- writeJsonFile3(resolve10(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3499
+ writeJsonFile3(resolve11(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3460
3500
  return next;
3461
3501
  }
3462
3502
 
@@ -3577,10 +3617,10 @@ async function executeAgent(context, args) {
3577
3617
  status: "running",
3578
3618
  startedAt: createdAt,
3579
3619
  worktreePath: runtime.workspaceDir,
3580
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3620
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3581
3621
  logRoot: runtime.logsDir,
3582
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3583
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3622
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3623
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3584
3624
  pid: process.pid
3585
3625
  });
3586
3626
  const result = await runInAgentRuntime({
@@ -3600,10 +3640,10 @@ async function executeAgent(context, args) {
3600
3640
  startedAt: createdAt,
3601
3641
  completedAt: failedAt,
3602
3642
  worktreePath: runtime.workspaceDir,
3603
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3643
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3604
3644
  logRoot: runtime.logsDir,
3605
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3606
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3645
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3646
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3607
3647
  pid: process.pid,
3608
3648
  errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
3609
3649
  });
@@ -3620,10 +3660,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3620
3660
  startedAt: createdAt,
3621
3661
  completedAt,
3622
3662
  worktreePath: runtime.workspaceDir,
3623
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3663
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3624
3664
  logRoot: runtime.logsDir,
3625
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3626
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3665
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3666
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3627
3667
  pid: process.pid
3628
3668
  });
3629
3669
  return {
@@ -3697,7 +3737,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3697
3737
  import {
3698
3738
  chmodSync,
3699
3739
  copyFileSync as copyFileSync2,
3700
- existsSync as existsSync7,
3740
+ existsSync as existsSync8,
3701
3741
  mkdirSync as mkdirSync6,
3702
3742
  readdirSync,
3703
3743
  readlinkSync,
@@ -3707,7 +3747,7 @@ import {
3707
3747
  unlinkSync
3708
3748
  } from "fs";
3709
3749
  import { homedir as homedir4 } from "os";
3710
- import { resolve as resolve12 } from "path";
3750
+ import { resolve as resolve13 } from "path";
3711
3751
  import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
3712
3752
  import {
3713
3753
  computeRuntimeImageFingerprint,
@@ -3726,13 +3766,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
3726
3766
 
3727
3767
  // packages/cli/src/commands/dist.ts
3728
3768
  function collectRigValidatorBuildTargets(input) {
3729
- const validatorsRoot = resolve12(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3730
- if (!existsSync7(validatorsRoot))
3769
+ const validatorsRoot = resolve13(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3770
+ if (!existsSync8(validatorsRoot))
3731
3771
  return [];
3732
3772
  const targets = [];
3733
3773
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3734
3774
  for (const category of categories) {
3735
- const categoryDir = resolve12(validatorsRoot, category.name);
3775
+ const categoryDir = resolve13(validatorsRoot, category.name);
3736
3776
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3737
3777
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3738
3778
  continue;
@@ -3741,7 +3781,7 @@ function collectRigValidatorBuildTargets(input) {
3741
3781
  continue;
3742
3782
  targets.push({
3743
3783
  source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
3744
- dest: resolve12(input.imageDir, `bin/validators/${category.name}-${check}`),
3784
+ dest: resolve13(input.imageDir, `bin/validators/${category.name}-${check}`),
3745
3785
  cwd: input.hostProjectRoot
3746
3786
  });
3747
3787
  }
@@ -3750,16 +3790,16 @@ function collectRigValidatorBuildTargets(input) {
3750
3790
  }
3751
3791
  async function findLatestDistBinary(projectRoot) {
3752
3792
  const distRoot = resolveControlPlaneHostDistDir(projectRoot);
3753
- if (!existsSync7(distRoot)) {
3793
+ if (!existsSync8(distRoot)) {
3754
3794
  return null;
3755
3795
  }
3756
3796
  const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
3757
3797
  name: entry.name,
3758
- mtimeMs: statSync(resolve12(distRoot, entry.name)).mtimeMs
3798
+ mtimeMs: statSync(resolve13(distRoot, entry.name)).mtimeMs
3759
3799
  })).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
3760
3800
  for (const { name } of entries) {
3761
- const candidate = resolve12(distRoot, name, "bin", "rig");
3762
- if (existsSync7(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3801
+ const candidate = resolve13(distRoot, name, "bin", "rig");
3802
+ if (existsSync8(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3763
3803
  return candidate;
3764
3804
  }
3765
3805
  }
@@ -3771,7 +3811,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
3771
3811
  async function runDistDoctor(projectRoot) {
3772
3812
  const bunPath = Bun.which("bun");
3773
3813
  const rigPath = Bun.which("rig");
3774
- const userBinDir = resolve12(homedir4(), ".local/bin");
3814
+ const userBinDir = resolve13(homedir4(), ".local/bin");
3775
3815
  const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
3776
3816
  let rigRunnable = false;
3777
3817
  if (rigPath) {
@@ -3819,15 +3859,15 @@ async function executeDist(context, args) {
3819
3859
  let source = await findLatestDistBinary(context.projectRoot);
3820
3860
  let buildDir = null;
3821
3861
  if (!source) {
3822
- buildDir = resolve12(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3862
+ buildDir = resolve13(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3823
3863
  await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
3824
- source = resolve12(buildDir, "bin", "rig");
3864
+ source = resolve13(buildDir, "bin", "rig");
3825
3865
  }
3826
- if (!existsSync7(source)) {
3866
+ if (!existsSync8(source)) {
3827
3867
  throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
3828
3868
  }
3829
- const installedPath = resolve12(installDir, "rig");
3830
- if (existsSync7(installedPath)) {
3869
+ const installedPath = resolve13(installDir, "rig");
3870
+ if (existsSync8(installedPath)) {
3831
3871
  unlinkSync(installedPath);
3832
3872
  }
3833
3873
  copyFileSync2(source, installedPath);
@@ -3869,22 +3909,22 @@ async function executeDist(context, args) {
3869
3909
  requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
3870
3910
  const fp = await computeRuntimeImageFingerprint(context.projectRoot);
3871
3911
  const currentId = computeRuntimeImageId(fp);
3872
- const imagesDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3912
+ const imagesDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3873
3913
  mkdirSync6(imagesDir, { recursive: true });
3874
3914
  let pruned = 0;
3875
3915
  for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
3876
3916
  if (entry.isDirectory() && entry.name !== currentId) {
3877
- rmSync4(resolve12(imagesDir, entry.name), { recursive: true, force: true });
3917
+ rmSync4(resolve13(imagesDir, entry.name), { recursive: true, force: true });
3878
3918
  pruned++;
3879
3919
  }
3880
3920
  }
3881
3921
  if (pruned > 0 && context.outputMode === "text") {
3882
3922
  console.log(`Pruned ${pruned} stale image(s).`);
3883
3923
  }
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 });
3924
+ const imageDir = resolve13(imagesDir, currentId);
3925
+ mkdirSync6(resolve13(imageDir, "bin/hooks"), { recursive: true });
3926
+ mkdirSync6(resolve13(imageDir, "bin/plugins"), { recursive: true });
3927
+ mkdirSync6(resolve13(imageDir, "bin/validators"), { recursive: true });
3888
3928
  const hookNames = [
3889
3929
  "scope-guard",
3890
3930
  "import-guard",
@@ -3898,25 +3938,25 @@ async function executeDist(context, args) {
3898
3938
  ];
3899
3939
  const targets = [];
3900
3940
  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 });
3941
+ targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve13(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
3942
+ targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
3903
3943
  for (const hookName of hookNames) {
3904
3944
  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 });
3945
+ targets.push({ source: src, dest: resolve13(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
3946
+ targets.push({ source: src, dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
3907
3947
  }
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");
3948
+ const pluginsDir = resolve13(context.projectRoot, "rig/plugins");
3949
+ const binPluginsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
3950
+ const validatorsRoot = resolve13(hostProjectRoot, "packages/runtime/src/control-plane/validators");
3951
+ const binValidatorsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
3912
3952
  mkdirSync6(binPluginsDir, { recursive: true });
3913
3953
  mkdirSync6(binValidatorsDir, { recursive: true });
3914
- if (existsSync7(pluginsDir)) {
3954
+ if (existsSync8(pluginsDir)) {
3915
3955
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3916
3956
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3917
3957
  if (!m)
3918
3958
  continue;
3919
- targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve12(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3959
+ targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve13(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3920
3960
  }
3921
3961
  }
3922
3962
  targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
@@ -3927,17 +3967,17 @@ async function executeDist(context, args) {
3927
3967
  const isValidator = dest.includes("/bin/validators/");
3928
3968
  await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
3929
3969
  }
3930
- if (existsSync7(pluginsDir)) {
3970
+ if (existsSync8(pluginsDir)) {
3931
3971
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3932
3972
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3933
3973
  if (!m)
3934
3974
  continue;
3935
3975
  const pluginName = m[1];
3936
- const imageBin = resolve12(imageDir, `bin/plugins/${pluginName}`);
3976
+ const imageBin = resolve13(imageDir, `bin/plugins/${pluginName}`);
3937
3977
  if (!pluginName)
3938
3978
  continue;
3939
- const symlinkPath = resolve12(binPluginsDir, pluginName);
3940
- if (existsSync7(imageBin)) {
3979
+ const symlinkPath = resolve13(binPluginsDir, pluginName);
3980
+ if (existsSync8(imageBin)) {
3941
3981
  try {
3942
3982
  unlinkSync(symlinkPath);
3943
3983
  } catch {}
@@ -3945,10 +3985,10 @@ async function executeDist(context, args) {
3945
3985
  }
3946
3986
  }
3947
3987
  }
3948
- if (existsSync7(validatorsRoot)) {
3988
+ if (existsSync8(validatorsRoot)) {
3949
3989
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3950
3990
  for (const category of categories) {
3951
- const categoryDir = resolve12(validatorsRoot, category.name);
3991
+ const categoryDir = resolve13(validatorsRoot, category.name);
3952
3992
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3953
3993
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3954
3994
  continue;
@@ -3956,9 +3996,9 @@ async function executeDist(context, args) {
3956
3996
  if (!check || check === "index" || check === "shared")
3957
3997
  continue;
3958
3998
  const validatorName = `${category.name}-${check}`;
3959
- const imageBin = resolve12(imageDir, `bin/validators/${validatorName}`);
3960
- const symlinkPath = resolve12(binValidatorsDir, validatorName);
3961
- if (existsSync7(imageBin)) {
3999
+ const imageBin = resolve13(imageDir, `bin/validators/${validatorName}`);
4000
+ const symlinkPath = resolve13(binValidatorsDir, validatorName);
4001
+ if (existsSync8(imageBin)) {
3962
4002
  try {
3963
4003
  unlinkSync(symlinkPath);
3964
4004
  } catch {}
@@ -3967,18 +4007,18 @@ async function executeDist(context, args) {
3967
4007
  }
3968
4008
  }
3969
4009
  }
3970
- const agentsDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
3971
- if (existsSync7(agentsDir)) {
4010
+ const agentsDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
4011
+ if (existsSync8(agentsDir)) {
3972
4012
  let relinkCount = 0;
3973
4013
  for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
3974
4014
  if (!agentEntry.isDirectory())
3975
4015
  continue;
3976
- const agentBinDir = resolve12(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
3977
- if (!existsSync7(agentBinDir))
4016
+ const agentBinDir = resolve13(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
4017
+ if (!existsSync8(agentBinDir))
3978
4018
  continue;
3979
4019
  const walkDir = (dir) => {
3980
4020
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
3981
- const fullPath = resolve12(dir, entry.name);
4021
+ const fullPath = resolve13(dir, entry.name);
3982
4022
  if (entry.isDirectory()) {
3983
4023
  walkDir(fullPath);
3984
4024
  } else if (entry.isSymbolicLink()) {
@@ -4012,7 +4052,7 @@ async function executeDist(context, args) {
4012
4052
 
4013
4053
  // packages/cli/src/commands/inbox.ts
4014
4054
  import { writeFileSync as writeFileSync4 } from "fs";
4015
- import { resolve as resolve13 } from "path";
4055
+ import { resolve as resolve14 } from "path";
4016
4056
  import {
4017
4057
  listAuthorityRuns,
4018
4058
  readJsonlFile as readJsonlFile3,
@@ -4029,7 +4069,7 @@ async function executeInbox(context, args) {
4029
4069
  pending = task.rest;
4030
4070
  requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
4031
4071
  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) => ({
4072
+ const approvals = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
4033
4073
  runId: entry.runId,
4034
4074
  record
4035
4075
  })));
@@ -4057,7 +4097,7 @@ async function executeInbox(context, args) {
4057
4097
  if (decision.value !== "approve" && decision.value !== "reject") {
4058
4098
  throw new CliError2("decision must be approve or reject.");
4059
4099
  }
4060
- const approvalsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
4100
+ const approvalsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
4061
4101
  const approvals = readJsonlFile3(approvalsPath);
4062
4102
  const resolvedAt = new Date().toISOString();
4063
4103
  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 +4114,7 @@ async function executeInbox(context, args) {
4074
4114
  pending = task.rest;
4075
4115
  requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
4076
4116
  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) => ({
4117
+ const requests = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
4078
4118
  runId: entry.runId,
4079
4119
  record
4080
4120
  })));
@@ -4116,7 +4156,7 @@ async function executeInbox(context, args) {
4116
4156
  const [key, ...restValue] = entry.split("=");
4117
4157
  return [key, restValue.join("=")];
4118
4158
  }));
4119
- const requestsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
4159
+ const requestsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
4120
4160
  const requests = readJsonlFile3(requestsPath);
4121
4161
  const resolvedAt = new Date().toISOString();
4122
4162
  const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
@@ -4131,14 +4171,14 @@ async function executeInbox(context, args) {
4131
4171
  }
4132
4172
 
4133
4173
  // 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";
4174
+ import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
4135
4175
  import { spawnSync as spawnSync2 } from "child_process";
4136
- import { resolve as resolve16 } from "path";
4176
+ import { resolve as resolve17 } from "path";
4137
4177
  import { buildRigInitConfigSource } from "@rig/core";
4138
4178
 
4139
4179
  // packages/cli/src/commands/_snapshot-upload.ts
4140
4180
  import { mkdir, readdir, readFile, writeFile } from "fs/promises";
4141
- import { dirname as dirname2, resolve as resolve14, relative, sep } from "path";
4181
+ import { dirname as dirname2, resolve as resolve15, relative, sep } from "path";
4142
4182
  var SNAPSHOT_ARCHIVE_VERSION = 1;
4143
4183
  var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
4144
4184
  var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
@@ -4160,15 +4200,15 @@ function assertManifestPath(root, relativePath) {
4160
4200
  if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
4161
4201
  throw new Error(`Invalid snapshot path: ${relativePath}`);
4162
4202
  }
4163
- const resolved = resolve14(root, relativePath);
4203
+ const resolved = resolve15(root, relativePath);
4164
4204
  const relativeToRoot = relative(root, resolved);
4165
- if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve14(relativeToRoot) === resolved) {
4205
+ if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve15(relativeToRoot) === resolved) {
4166
4206
  throw new Error(`Snapshot path escapes project root: ${relativePath}`);
4167
4207
  }
4168
4208
  return resolved;
4169
4209
  }
4170
4210
  async function buildSnapshotUploadManifest(projectRoot, options = {}) {
4171
- const root = resolve14(projectRoot);
4211
+ const root = resolve15(projectRoot);
4172
4212
  const excludedDirectories = [...new Set([
4173
4213
  ...DEFAULT_EXCLUDED_DIRECTORIES,
4174
4214
  ...options.excludedDirectories ?? []
@@ -4180,7 +4220,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
4180
4220
  for (const entry of entries) {
4181
4221
  if (entry.isDirectory() && excludedSet.has(entry.name))
4182
4222
  continue;
4183
- const fullPath = resolve14(dir, entry.name);
4223
+ const fullPath = resolve15(dir, entry.name);
4184
4224
  if (entry.isDirectory()) {
4185
4225
  await visit(fullPath);
4186
4226
  continue;
@@ -4228,8 +4268,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
4228
4268
  }
4229
4269
 
4230
4270
  // packages/cli/src/commands/_doctor-checks.ts
4231
- import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
4232
- import { resolve as resolve15 } from "path";
4271
+ import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
4272
+ import { resolve as resolve16 } from "path";
4233
4273
  import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
4234
4274
  function check(id, label, status, detail, remediation) {
4235
4275
  return {
@@ -4269,11 +4309,11 @@ function repoSlugFromConfig(config) {
4269
4309
  function loadFallbackConfig(projectRoot) {
4270
4310
  const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
4271
4311
  for (const name of candidates) {
4272
- const path = resolve15(projectRoot, name);
4273
- if (!existsSync8(path))
4312
+ const path = resolve16(projectRoot, name);
4313
+ if (!existsSync9(path))
4274
4314
  continue;
4275
4315
  try {
4276
- const source = readFileSync4(path, "utf8");
4316
+ const source = readFileSync5(path, "utf8");
4277
4317
  if (name.endsWith(".json"))
4278
4318
  return JSON.parse(source);
4279
4319
  const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
@@ -4352,7 +4392,7 @@ async function runRigDoctorChecks(options) {
4352
4392
  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
4393
  const loadedConfig = await loadConfig(projectRoot).catch(() => null);
4354
4394
  const config = loadedConfig ?? loadFallbackConfig(projectRoot);
4355
- const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync8(resolve15(projectRoot, name)));
4395
+ const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync9(resolve16(projectRoot, name)));
4356
4396
  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
4397
  const taskSourceKind = config?.taskSource?.kind;
4358
4398
  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 +4487,10 @@ function countDoctorFailures(checks) {
4447
4487
  }
4448
4488
 
4449
4489
  // packages/cli/src/commands/init.ts
4450
- var RIG_CONFIG_PACKAGE_VERSION = "0.0.6-alpha.1";
4490
+ var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
4451
4491
  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}`
4492
+ "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
4493
+ "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
4454
4494
  };
4455
4495
  function parseRepoSlugFromRemote(remoteUrl) {
4456
4496
  const trimmed = remoteUrl.trim();
@@ -4470,20 +4510,20 @@ function parseRepoSlug(value) {
4470
4510
  return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
4471
4511
  }
4472
4512
  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))
4513
+ const rigDir = resolve17(projectRoot, ".rig");
4514
+ mkdirSync7(resolve17(rigDir, "state"), { recursive: true });
4515
+ mkdirSync7(resolve17(rigDir, "logs"), { recursive: true });
4516
+ mkdirSync7(resolve17(rigDir, "runs"), { recursive: true });
4517
+ mkdirSync7(resolve17(rigDir, "tmp"), { recursive: true });
4518
+ mkdirSync7(resolve17(projectRoot, "artifacts"), { recursive: true });
4519
+ const taskConfigPath = resolve17(rigDir, "task-config.json");
4520
+ if (!existsSync10(taskConfigPath))
4481
4521
  writeFileSync5(taskConfigPath, `{}
4482
4522
  `, "utf-8");
4483
4523
  }
4484
4524
  function ensureGitignoreEntries(projectRoot) {
4485
- const path = resolve16(projectRoot, ".gitignore");
4486
- const existing = existsSync9(path) ? readFileSync5(path, "utf8") : "";
4525
+ const path = resolve17(projectRoot, ".gitignore");
4526
+ const existing = existsSync10(path) ? readFileSync6(path, "utf8") : "";
4487
4527
  const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
4488
4528
  const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
4489
4529
  if (missing.length === 0)
@@ -4496,14 +4536,14 @@ function ensureGitignoreEntries(projectRoot) {
4496
4536
  `, "utf8");
4497
4537
  }
4498
4538
  function ensureRigConfigPackageDependencies(projectRoot) {
4499
- const path = resolve16(projectRoot, "package.json");
4500
- const existing = existsSync9(path) ? JSON.parse(readFileSync5(path, "utf8")) : {};
4539
+ const path = resolve17(projectRoot, "package.json");
4540
+ const existing = existsSync10(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
4501
4541
  const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
4502
4542
  for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
4503
4543
  devDependencies[name] = spec;
4504
4544
  }
4505
4545
  const next = {
4506
- ...existsSync9(path) ? existing : { name: "rig-project", private: true },
4546
+ ...existsSync10(path) ? existing : { name: "rig-project", private: true },
4507
4547
  devDependencies
4508
4548
  };
4509
4549
  writeFileSync5(path, `${JSON.stringify(next, null, 2)}
@@ -4573,15 +4613,71 @@ async function promptSelect(prompts, options) {
4573
4613
  throw new CliError2("Init cancelled.", 1);
4574
4614
  return String(value);
4575
4615
  }
4576
- async function pollDeviceAuthOnce(context, pollId) {
4616
+ function sleep2(ms) {
4617
+ return new Promise((resolve18) => setTimeout(resolve18, ms));
4618
+ }
4619
+ function positiveIntFromEnv(name, fallback) {
4620
+ const value = Number.parseInt(process.env[name] ?? "", 10);
4621
+ return Number.isFinite(value) && value >= 0 ? value : fallback;
4622
+ }
4623
+ function apiSessionTokenFrom(payload) {
4624
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
4625
+ return null;
4626
+ const token = payload.apiSessionToken;
4627
+ return typeof token === "string" && token.trim() ? token.trim() : null;
4628
+ }
4629
+ function cleanPayloadString(value) {
4630
+ return typeof value === "string" && value.trim() ? value.trim() : null;
4631
+ }
4632
+ function remoteGitHubAuthMetadata(payload) {
4633
+ if (!payload)
4634
+ return {};
4635
+ const userNamespace = payload.userNamespace && typeof payload.userNamespace === "object" && !Array.isArray(payload.userNamespace) ? payload.userNamespace : null;
4636
+ return {
4637
+ ...cleanPayloadString(payload.login) ? { login: cleanPayloadString(payload.login) } : {},
4638
+ ...cleanPayloadString(payload.userId) ? { userId: cleanPayloadString(payload.userId) } : {},
4639
+ ...cleanPayloadString(userNamespace?.key) ? { userNamespaceKey: cleanPayloadString(userNamespace?.key) } : {},
4640
+ ...cleanPayloadString(userNamespace?.root) ? { userNamespaceRoot: cleanPayloadString(userNamespace?.root) } : {},
4641
+ ...cleanPayloadString(userNamespace?.checkoutBaseDir) ? { checkoutBaseDir: cleanPayloadString(userNamespace?.checkoutBaseDir) } : {},
4642
+ ...cleanPayloadString(userNamespace?.snapshotBaseDir) ? { snapshotBaseDir: cleanPayloadString(userNamespace?.snapshotBaseDir) } : {}
4643
+ };
4644
+ }
4645
+ function writeRemoteGitHubAuthState(projectRoot, input) {
4646
+ writeFileSync5(resolve17(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
4647
+ authenticated: true,
4648
+ source: input.source,
4649
+ storedOnServer: true,
4650
+ selectedRepo: input.selectedRepo,
4651
+ ...remoteGitHubAuthMetadata(input.authPayload ?? null),
4652
+ ...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
4653
+ updatedAt: new Date().toISOString()
4654
+ }, null, 2)}
4655
+ `, "utf8");
4656
+ }
4657
+ async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
4577
4658
  if (typeof pollId !== "string" || !pollId.trim())
4578
4659
  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;
4660
+ const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
4661
+ const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
4662
+ const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
4663
+ const deadline = Date.now() + timeoutMs;
4664
+ let last = null;
4665
+ do {
4666
+ const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
4667
+ method: "POST",
4668
+ headers: { "content-type": "application/json" },
4669
+ body: JSON.stringify({ pollId })
4670
+ }).catch(() => null);
4671
+ last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
4672
+ const status = typeof last?.status === "string" ? last.status : null;
4673
+ if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
4674
+ return last;
4675
+ }
4676
+ if (timeoutMs <= 0)
4677
+ return last;
4678
+ await sleep2(intervalMs);
4679
+ } while (Date.now() < deadline);
4680
+ return last;
4585
4681
  }
4586
4682
  async function runControlPlaneInit(context, options) {
4587
4683
  const projectRoot = context.projectRoot;
@@ -4604,9 +4700,9 @@ async function runControlPlaneInit(context, options) {
4604
4700
  });
4605
4701
  ensureRigPrivateDirs(projectRoot);
4606
4702
  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);
4703
+ const configTsPath = resolve17(projectRoot, "rig.config.ts");
4704
+ const configJsonPath = resolve17(projectRoot, "rig.config.json");
4705
+ const configExists = existsSync10(configTsPath) || existsSync10(configJsonPath);
4610
4706
  if (!options.privateStateOnly) {
4611
4707
  if (configExists && !options.repair) {
4612
4708
  if (context.outputMode !== "json")
@@ -4622,7 +4718,7 @@ async function runControlPlaneInit(context, options) {
4622
4718
  }
4623
4719
  ensureRigConfigPackageDependencies(projectRoot);
4624
4720
  }
4625
- writeFileSync5(resolve16(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4721
+ writeFileSync5(resolve17(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4626
4722
  `, "utf8");
4627
4723
  const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
4628
4724
  let uploadedSnapshot = null;
@@ -4644,10 +4740,15 @@ async function runControlPlaneInit(context, options) {
4644
4740
  const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
4645
4741
  if (token) {
4646
4742
  githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
4647
- setGitHubBearerTokenForCurrentProcess(token);
4743
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4744
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4648
4745
  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");
4746
+ writeRemoteGitHubAuthState(projectRoot, {
4747
+ source: authMethod === "gh" ? "gh" : "init-token",
4748
+ selectedRepo: repo.slug,
4749
+ apiSessionToken,
4750
+ authPayload: githubAuth
4751
+ });
4651
4752
  }
4652
4753
  } else if (authMethod === "device") {
4653
4754
  const payload = await requestServerJson(context, "/api/github/auth/device/start", {
@@ -4656,9 +4757,22 @@ async function runControlPlaneInit(context, options) {
4656
4757
  body: JSON.stringify({ repoSlug: repo.slug })
4657
4758
  });
4658
4759
  deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
4659
- const completed = await pollDeviceAuthOnce(context, deviceAuth.pollId);
4660
- if (completed)
4760
+ if (context.outputMode !== "json") {
4761
+ const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
4762
+ const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
4763
+ console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
4764
+ }
4765
+ const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
4766
+ if (completed) {
4767
+ const apiSessionToken = apiSessionTokenFrom(completed);
4768
+ if (apiSessionToken) {
4769
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken);
4770
+ if (serverKind === "remote") {
4771
+ writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken, authPayload: completed });
4772
+ }
4773
+ }
4661
4774
  deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
4775
+ }
4662
4776
  }
4663
4777
  let remoteCheckoutPreparation = null;
4664
4778
  if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
@@ -4678,6 +4792,12 @@ async function runControlPlaneInit(context, options) {
4678
4792
  });
4679
4793
  const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
4680
4794
  const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
4795
+ if (serverRootSwitch && token) {
4796
+ githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
4797
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4798
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4799
+ writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken, authPayload: githubAuth });
4800
+ }
4681
4801
  const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
4682
4802
  const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
4683
4803
  remote: true,
@@ -4787,7 +4907,7 @@ function parseInitOptions(args) {
4787
4907
  async function runInteractiveControlPlaneInit(context, prompts) {
4788
4908
  prompts.intro?.("Initialize a Rig control-plane project");
4789
4909
  const projectRoot = context.projectRoot;
4790
- const existingConfig = existsSync9(resolve16(projectRoot, "rig.config.ts")) || existsSync9(resolve16(projectRoot, "rig.config.json"));
4910
+ const existingConfig = existsSync10(resolve17(projectRoot, "rig.config.ts")) || existsSync10(resolve17(projectRoot, "rig.config.json"));
4791
4911
  let repair = false;
4792
4912
  let privateStateOnly = false;
4793
4913
  if (existingConfig) {
@@ -4887,7 +5007,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
4887
5007
  });
4888
5008
  const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
4889
5009
  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")}.` : "";
5010
+ 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
5011
  prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
4892
5012
  return result;
4893
5013
  }
@@ -5049,8 +5169,8 @@ async function executeDoctor(context, args) {
5049
5169
  }
5050
5170
 
5051
5171
  // packages/cli/src/commands/_run-driver-helpers.ts
5052
- import { readFileSync as readFileSync6 } from "fs";
5053
- import { resolve as resolve17 } from "path";
5172
+ import { readFileSync as readFileSync7 } from "fs";
5173
+ import { resolve as resolve18 } from "path";
5054
5174
  import {
5055
5175
  appendJsonlRecord as appendJsonlRecord2,
5056
5176
  readAuthorityRun as readAuthorityRun2,
@@ -5070,7 +5190,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
5070
5190
  ...patch,
5071
5191
  updatedAt: new Date().toISOString()
5072
5192
  };
5073
- writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
5193
+ writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
5074
5194
  return next;
5075
5195
  }
5076
5196
  function touchAuthorityRun(projectRoot, runId) {
@@ -5078,21 +5198,21 @@ function touchAuthorityRun(projectRoot, runId) {
5078
5198
  if (!current) {
5079
5199
  return;
5080
5200
  }
5081
- writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
5201
+ writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
5082
5202
  ...current,
5083
5203
  updatedAt: new Date().toISOString()
5084
5204
  });
5085
5205
  }
5086
5206
  function appendRunTimeline(projectRoot, runId, value) {
5087
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
5207
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
5088
5208
  touchAuthorityRun(projectRoot, runId);
5089
5209
  }
5090
5210
  function appendRunLog(projectRoot, runId, value) {
5091
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
5211
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
5092
5212
  touchAuthorityRun(projectRoot, runId);
5093
5213
  }
5094
5214
  function appendRunAction(projectRoot, runId, value) {
5095
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
5215
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
5096
5216
  id: value.id,
5097
5217
  type: "action",
5098
5218
  actionType: value.actionType,
@@ -5163,7 +5283,7 @@ function buildRunPrompt(input) {
5163
5283
  })();
5164
5284
  const scopeText = (() => {
5165
5285
  try {
5166
- const parsed = JSON.parse(readFileSync6(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
5286
+ const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
5167
5287
  const entry = parsed[input.taskId] ?? {};
5168
5288
  const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
5169
5289
  const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
@@ -5276,8 +5396,8 @@ function renderSourceScopeValidation(task, validation) {
5276
5396
  }
5277
5397
 
5278
5398
  // packages/cli/src/commands/inspect.ts
5279
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
5280
- import { resolve as resolve18 } from "path";
5399
+ import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
5400
+ import { resolve as resolve19 } from "path";
5281
5401
  import {
5282
5402
  listAuthorityRuns as listAuthorityRuns2,
5283
5403
  readAuthorityRun as readAuthorityRun3,
@@ -5298,8 +5418,8 @@ async function executeInspect(context, args) {
5298
5418
  if (!latestRun) {
5299
5419
  throw new CliError2(`No runs found for ${requiredTask}.`);
5300
5420
  }
5301
- const logsPath = resolve18(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5302
- if (!existsSync10(logsPath)) {
5421
+ const logsPath = resolve19(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5422
+ if (!existsSync11(logsPath)) {
5303
5423
  throw new CliError2(`No logs found for run ${latestRun.runId}.`);
5304
5424
  }
5305
5425
  await context.runCommand(["cat", logsPath]);
@@ -5309,7 +5429,7 @@ async function executeInspect(context, args) {
5309
5429
  const { value: task, rest: remaining } = takeOption(rest, "--task");
5310
5430
  requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
5311
5431
  const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
5312
- const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync10(path));
5432
+ const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync11(path));
5313
5433
  if (!artifactRoot) {
5314
5434
  throw new CliError2(`No artifacts found for ${requiredTask}.`);
5315
5435
  }
@@ -5366,10 +5486,10 @@ async function executeInspect(context, args) {
5366
5486
  case "failures": {
5367
5487
  requireNoExtraArgs(rest, "bun run rig inspect failures");
5368
5488
  const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
5369
- if (!existsSync10(failed)) {
5489
+ if (!existsSync11(failed)) {
5370
5490
  console.log("No failures recorded.");
5371
5491
  } else {
5372
- process.stdout.write(readFileSync7(failed, "utf-8"));
5492
+ process.stdout.write(readFileSync8(failed, "utf-8"));
5373
5493
  }
5374
5494
  return { ok: true, group: "inspect", command };
5375
5495
  }
@@ -5386,11 +5506,11 @@ async function executeInspect(context, args) {
5386
5506
  return { ok: true, group: "inspect", command };
5387
5507
  case "audit": {
5388
5508
  requireNoExtraArgs(rest, "bun run rig inspect audit");
5389
- const auditPath = resolve18(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5390
- if (!existsSync10(auditPath)) {
5509
+ const auditPath = resolve19(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5510
+ if (!existsSync11(auditPath)) {
5391
5511
  console.log("No audit log found.");
5392
5512
  } else {
5393
- const lines = readFileSync7(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5513
+ const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5394
5514
  for (const line of lines) {
5395
5515
  console.log(line);
5396
5516
  }
@@ -6019,8 +6139,8 @@ async function executeRemote(context, args) {
6019
6139
  }
6020
6140
 
6021
6141
  // packages/cli/src/commands/run.ts
6022
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
6023
- import { resolve as resolve19 } from "path";
6142
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
6143
+ import { resolve as resolve20 } from "path";
6024
6144
  import { createInterface as createInterface2 } from "readline/promises";
6025
6145
  import {
6026
6146
  listAuthorityRuns as listAuthorityRuns3,
@@ -6034,6 +6154,7 @@ import {
6034
6154
  listOpenEpics,
6035
6155
  resolveDefaultEpic,
6036
6156
  runResume,
6157
+ runRestart,
6037
6158
  runStatus,
6038
6159
  runStop,
6039
6160
  startRun,
@@ -6142,6 +6263,17 @@ async function attachRunOperatorView(context, input) {
6142
6263
  }
6143
6264
 
6144
6265
  // packages/cli/src/commands/run.ts
6266
+ function normalizeRemoteRunDetails(payload) {
6267
+ const run = payload.run;
6268
+ if (!run || typeof run !== "object" || Array.isArray(run))
6269
+ return null;
6270
+ return {
6271
+ ...run,
6272
+ ...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
6273
+ ...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
6274
+ ...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
6275
+ };
6276
+ }
6145
6277
  function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
6146
6278
  if (noEpicPrompt) {
6147
6279
  return false;
@@ -6280,7 +6412,7 @@ async function executeRun(context, args) {
6280
6412
  if (!run.value) {
6281
6413
  throw new CliError2("run show requires --run <id>.");
6282
6414
  }
6283
- const record = readAuthorityRun4(context.projectRoot, run.value);
6415
+ const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
6284
6416
  if (!record) {
6285
6417
  throw new CliError2(`Run not found: ${run.value}`, 2);
6286
6418
  }
@@ -6299,7 +6431,7 @@ async function executeRun(context, args) {
6299
6431
  if (!run.value) {
6300
6432
  throw new CliError2("run timeline requires --run <id>.");
6301
6433
  }
6302
- const timelinePath = resolve19(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6434
+ const timelinePath = resolve20(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6303
6435
  const printEvents = () => {
6304
6436
  const events2 = readJsonlFile4(timelinePath);
6305
6437
  if (context.outputMode === "text") {
@@ -6311,12 +6443,12 @@ async function executeRun(context, args) {
6311
6443
  };
6312
6444
  const events = printEvents();
6313
6445
  if (follow.value && context.outputMode === "text") {
6314
- let lastLength = existsSync11(timelinePath) ? readFileSync8(timelinePath, "utf8").length : 0;
6446
+ let lastLength = existsSync12(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
6315
6447
  while (true) {
6316
6448
  await Bun.sleep(1000);
6317
- if (!existsSync11(timelinePath))
6449
+ if (!existsSync12(timelinePath))
6318
6450
  continue;
6319
- const next = readFileSync8(timelinePath, "utf8");
6451
+ const next = readFileSync9(timelinePath, "utf8");
6320
6452
  if (next.length <= lastLength)
6321
6453
  continue;
6322
6454
  const delta = next.slice(lastLength);
@@ -6461,6 +6593,20 @@ async function executeRun(context, args) {
6461
6593
  }
6462
6594
  return { ok: true, group: "run", command, details: resumed };
6463
6595
  }
6596
+ case "restart": {
6597
+ requireNoExtraArgs(rest, "bun run rig run restart");
6598
+ if (context.dryRun) {
6599
+ if (context.outputMode === "text") {
6600
+ console.log("[dry-run] rig run restart");
6601
+ }
6602
+ return { ok: true, group: "run", command };
6603
+ }
6604
+ const restarted = await runRestart(context.projectRoot, runtimeContext);
6605
+ if (context.outputMode === "text") {
6606
+ console.log(`Restarted run: ${restarted.runId}`);
6607
+ }
6608
+ return { ok: true, group: "run", command, details: restarted };
6609
+ }
6464
6610
  case "stop": {
6465
6611
  const runOption = takeOption(rest, "--run");
6466
6612
  const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
@@ -6518,7 +6664,7 @@ async function executeServer(context, args, options) {
6518
6664
  const authTokenResult = takeOption(pending, "--auth-token");
6519
6665
  pending = authTokenResult.rest;
6520
6666
  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"];
6667
+ const commandParts = ["rig-server", "start"];
6522
6668
  if (hostResult.value) {
6523
6669
  commandParts.push("--host", hostResult.value);
6524
6670
  }
@@ -6541,7 +6687,7 @@ async function executeServer(context, args, options) {
6541
6687
  const eventResult = takeOption(pending, "--event");
6542
6688
  pending = eventResult.rest;
6543
6689
  requireNoExtraArgs(pending, "bun run rig server notify-test [--event <type>]");
6544
- const commandParts = ["bun", "run", "packages/server/src/server.ts", "notify-test"];
6690
+ const commandParts = ["rig-server", "notify-test"];
6545
6691
  if (eventResult.value) {
6546
6692
  commandParts.push("--event", eventResult.value);
6547
6693
  }
@@ -6602,10 +6748,10 @@ async function executeServer(context, args, options) {
6602
6748
  }
6603
6749
 
6604
6750
  // packages/cli/src/commands/task.ts
6605
- import { readFileSync as readFileSync9 } from "fs";
6751
+ import { readFileSync as readFileSync10 } from "fs";
6606
6752
  import { spawnSync as spawnSync4 } from "child_process";
6607
6753
  import { createInterface as createInterface4 } from "readline/promises";
6608
- import { resolve as resolve20 } from "path";
6754
+ import { resolve as resolve21 } from "path";
6609
6755
  import {
6610
6756
  taskArtifactDir,
6611
6757
  taskArtifacts,
@@ -6936,7 +7082,7 @@ async function executeTask(context, args, options) {
6936
7082
  const fileFlag = takeOption(rest.slice(1), "--file");
6937
7083
  let content;
6938
7084
  if (fileFlag.value) {
6939
- content = readFileSync9(resolve20(context.projectRoot, fileFlag.value), "utf-8");
7085
+ content = readFileSync10(resolve21(context.projectRoot, fileFlag.value), "utf-8");
6940
7086
  } else {
6941
7087
  content = await readStdin();
6942
7088
  }
@@ -7157,8 +7303,8 @@ async function executeTask(context, args, options) {
7157
7303
  }
7158
7304
 
7159
7305
  // 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";
7306
+ import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
7307
+ import { resolve as resolve22 } from "path";
7162
7308
  import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
7163
7309
  import { createInterface as createLineInterface } from "readline";
7164
7310
  import { loadConfig as loadConfig2 } from "@rig/core/load-config";
@@ -7192,7 +7338,24 @@ import {
7192
7338
  commitRunChanges,
7193
7339
  runPrAutomation
7194
7340
  } from "@rig/runtime/control-plane/native/pr-automation";
7341
+ function looksLikeGitHubToken(value) {
7342
+ const token = value?.trim();
7343
+ if (!token)
7344
+ return false;
7345
+ return /^(gh[opusr]_|github_pat_)/.test(token);
7346
+ }
7347
+ function githubBridgeEnv(token) {
7348
+ const clean = token?.trim();
7349
+ if (!clean)
7350
+ return {};
7351
+ return {
7352
+ RIG_GITHUB_TOKEN: clean,
7353
+ GITHUB_TOKEN: clean,
7354
+ GH_TOKEN: clean
7355
+ };
7356
+ }
7195
7357
  function buildPiRigBridgeEnv(input) {
7358
+ const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
7196
7359
  return {
7197
7360
  RIG_PROJECT_ROOT: input.projectRoot,
7198
7361
  PROJECT_RIG_ROOT: input.projectRoot,
@@ -7202,7 +7365,7 @@ function buildPiRigBridgeEnv(input) {
7202
7365
  RIG_RUNTIME_ADAPTER: "pi",
7203
7366
  ...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
7204
7367
  ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
7205
- ...input.githubToken ? { RIG_GITHUB_TOKEN: input.githubToken } : {}
7368
+ ...githubBridgeEnv(githubToken)
7206
7369
  };
7207
7370
  }
7208
7371
  function runGitSync(cwd, args, input) {
@@ -7224,12 +7387,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
7224
7387
  return 0;
7225
7388
  let copied = 0;
7226
7389
  for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
7227
- const sourcePath = resolve21(sourceRoot, relativePath);
7228
- const targetPath = resolve21(targetRoot, relativePath);
7390
+ const sourcePath = resolve22(sourceRoot, relativePath);
7391
+ const targetPath = resolve22(targetRoot, relativePath);
7229
7392
  try {
7230
7393
  if (!statSync2(sourcePath).isFile())
7231
7394
  continue;
7232
- mkdirSync8(resolve21(targetPath, ".."), { recursive: true });
7395
+ mkdirSync8(resolve22(targetPath, ".."), { recursive: true });
7233
7396
  copyFileSync3(sourcePath, targetPath);
7234
7397
  copied += 1;
7235
7398
  } catch {}
@@ -7263,6 +7426,14 @@ function buildTaskRunReviewEnv(config) {
7263
7426
  ...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
7264
7427
  };
7265
7428
  }
7429
+ function buildDirtyBaselineHandshakeEnv(input) {
7430
+ if (input.baselineMode !== "dirty-snapshot")
7431
+ return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
7432
+ return {
7433
+ RIG_BASELINE_MODE: "dirty-snapshot",
7434
+ RIG_DIRTY_BASELINE_READY_FILE: resolve22(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
7435
+ };
7436
+ }
7266
7437
  function positiveInt(value, fallback) {
7267
7438
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
7268
7439
  }
@@ -7371,9 +7542,9 @@ function createCommandRunner(binary) {
7371
7542
  const stderrChunks = [];
7372
7543
  child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7373
7544
  child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7374
- return await new Promise((resolve22) => {
7375
- child.once("error", (error) => resolve22({ exitCode: 1, stderr: error.message }));
7376
- child.once("close", (code) => resolve22({
7545
+ return await new Promise((resolve23) => {
7546
+ child.once("error", (error) => resolve23({ exitCode: 1, stderr: error.message }));
7547
+ child.once("close", (code) => resolve23({
7377
7548
  exitCode: code ?? 1,
7378
7549
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
7379
7550
  stderr: Buffer.concat(stderrChunks).toString("utf8")
@@ -7569,7 +7740,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
7569
7740
  return null;
7570
7741
  }
7571
7742
  for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
7572
- const summary = readJsonFile3(resolve21(artifactDir, "validation-summary.json"), null);
7743
+ const summary = readJsonFile3(resolve22(artifactDir, "validation-summary.json"), null);
7573
7744
  if (!summary || summary.status !== "fail") {
7574
7745
  continue;
7575
7746
  }
@@ -7650,9 +7821,9 @@ function readTaskRunAcceptedArtifactState(input) {
7650
7821
  if (!input.taskId || !input.workspaceDir) {
7651
7822
  return { accepted: false, reason: null };
7652
7823
  }
7653
- const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7654
- const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7655
- const taskResultPath = resolve21(artifactDir, "task-result.json");
7824
+ const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7825
+ const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7826
+ const taskResultPath = resolve22(artifactDir, "task-result.json");
7656
7827
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7657
7828
  if (reviewStatus !== "APPROVED") {
7658
7829
  return { accepted: false, reason: null };
@@ -7689,12 +7860,12 @@ function resolveTaskRunRetryContext(input) {
7689
7860
  if (!input.taskId || !input.workspaceDir) {
7690
7861
  return { shouldRetry: false, failureDetail: null, nextPrompt: null };
7691
7862
  }
7692
- const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7693
- const reviewStatePath = resolve21(artifactDir, "review-state.json");
7694
- const reviewFeedbackPath = resolve21(artifactDir, "review-feedback.md");
7695
- const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7696
- const failedApproachesPath = resolve21(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7697
- const validationSummaryPath = resolve21(artifactDir, "validation-summary.json");
7863
+ const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7864
+ const reviewStatePath = resolve22(artifactDir, "review-state.json");
7865
+ const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
7866
+ const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7867
+ const failedApproachesPath = resolve22(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7868
+ const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
7698
7869
  const reviewState = readJsonFile3(reviewStatePath, null);
7699
7870
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7700
7871
  const reviewRejected = isTaskRunReviewRejected(reviewState);
@@ -7749,11 +7920,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
7749
7920
  return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
7750
7921
  }
7751
7922
  function readTaskRunReviewStatus(reviewStatusPath) {
7752
- if (!existsSync12(reviewStatusPath)) {
7923
+ if (!existsSync13(reviewStatusPath)) {
7753
7924
  return null;
7754
7925
  }
7755
7926
  try {
7756
- const status = readFileSync10(reviewStatusPath, "utf8").trim().toUpperCase();
7927
+ const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
7757
7928
  return status === "APPROVED" || status === "REJECTED" ? status : null;
7758
7929
  } catch {
7759
7930
  return null;
@@ -7836,8 +8007,11 @@ function stringArrayField(record, key) {
7836
8007
  }
7837
8008
  async function executeRigOwnedTaskRun(context, input) {
7838
8009
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
8010
+ const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
8011
+ const resumeMode = process.env.RIG_RUN_RESUME === "1";
8012
+ const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
7839
8013
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
7840
- const prompt = buildRunPrompt({
8014
+ let prompt = buildRunPrompt({
7841
8015
  projectRoot: context.projectRoot,
7842
8016
  taskId: input.taskId,
7843
8017
  fallbackTitle: input.title,
@@ -7891,14 +8065,14 @@ async function executeRigOwnedTaskRun(context, input) {
7891
8065
  taskId: runtimeTaskId,
7892
8066
  createdAt: startedAt,
7893
8067
  runtimeAdapter: input.runtimeAdapter,
7894
- status: "created"
8068
+ status: resumeMode ? "preparing" : "created"
7895
8069
  });
7896
8070
  patchAuthorityRun(context.projectRoot, input.runId, {
7897
8071
  status: "preparing",
7898
8072
  startedAt,
7899
8073
  completedAt: null,
7900
8074
  errorText: null,
7901
- artifactRoot: null,
8075
+ artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
7902
8076
  runtimeAdapter: input.runtimeAdapter,
7903
8077
  runtimeMode: input.runtimeMode,
7904
8078
  interactionMode: input.interactionMode,
@@ -7914,9 +8088,9 @@ async function executeRigOwnedTaskRun(context, input) {
7914
8088
  detail: input.taskId ?? input.title ?? runtimeTaskId
7915
8089
  });
7916
8090
  appendRunLog(context.projectRoot, input.runId, {
7917
- id: `log:${input.runId}:start`,
7918
- title: "Rig task run started",
7919
- detail: input.taskId ?? input.title ?? runtimeTaskId,
8091
+ id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
8092
+ title: resumeMode ? "Rig task run resumed" : "Rig task run started",
8093
+ detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
7920
8094
  tone: "info",
7921
8095
  status: "preparing",
7922
8096
  createdAt: startedAt
@@ -7937,7 +8111,22 @@ async function executeRigOwnedTaskRun(context, input) {
7937
8111
  const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
7938
8112
  const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
7939
8113
  const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
7940
- patchAuthorityRun(context.projectRoot, input.runId, { planning: planningClassification });
8114
+ const planningArtifactPath = resolve22("artifacts", runtimeTaskId, "implementation-plan.md");
8115
+ const persistedPlanning = {
8116
+ ...planningClassification,
8117
+ classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
8118
+ artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
8119
+ classifiedAt: new Date().toISOString()
8120
+ };
8121
+ mkdirSync8(resolve22(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
8122
+ writeFileSync6(resolve22(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
8123
+ `, "utf8");
8124
+ patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
8125
+ prompt = `${prompt}
8126
+
8127
+ Rig planning classification:
8128
+ ${JSON.stringify(persistedPlanning, null, 2)}
8129
+ ${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."}`;
7941
8130
  if (input.runtimeAdapter === "pi") {
7942
8131
  for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
7943
8132
  appendPiStageLog({
@@ -7979,11 +8168,11 @@ async function executeRigOwnedTaskRun(context, input) {
7979
8168
  let reviewAction;
7980
8169
  let verificationStarted = false;
7981
8170
  let reviewStarted = false;
7982
- let latestRuntimeWorkspace = null;
7983
- let latestSessionDir = null;
7984
- let latestLogsDir = null;
8171
+ let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
8172
+ let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve22(existingRunRecord.sessionPath, "..") : null;
8173
+ let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
7985
8174
  let latestProviderCommand = null;
7986
- let latestRuntimeBranch = null;
8175
+ let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
7987
8176
  let snapshotSidecarPromise = null;
7988
8177
  let dirtyBaselineApplied = false;
7989
8178
  const childEnv = {
@@ -8001,9 +8190,14 @@ async function executeRigOwnedTaskRun(context, input) {
8001
8190
  RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
8002
8191
  RIG_SERVER_RUN_ID: input.runId
8003
8192
  },
8004
- ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
8193
+ ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
8194
+ ...resumeMode ? {
8195
+ RIG_RUN_RESUME: "1",
8196
+ RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
8197
+ } : {}
8005
8198
  };
8006
8199
  Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
8200
+ Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
8007
8201
  const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
8008
8202
  const maxAttempts = automationLimits.maxValidationAttempts;
8009
8203
  const promoteToValidating = (detail) => {
@@ -8058,22 +8252,29 @@ async function executeRigOwnedTaskRun(context, input) {
8058
8252
  patchAuthorityRun(context.projectRoot, input.runId, {
8059
8253
  status: "running",
8060
8254
  worktreePath: latestRuntimeWorkspace,
8061
- artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve21(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8255
+ artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve22(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8062
8256
  logRoot: latestLogsDir,
8063
- sessionPath: latestSessionDir ? resolve21(latestSessionDir, "session.json") : null,
8064
- sessionLogPath: latestLogsDir ? resolve21(latestLogsDir, "agent-stdout.log") : null,
8257
+ sessionPath: latestSessionDir ? resolve22(latestSessionDir, "session.json") : null,
8258
+ sessionLogPath: latestLogsDir ? resolve22(latestLogsDir, "agent-stdout.log") : null,
8065
8259
  branch: runtimeId
8066
8260
  });
8067
8261
  if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
8068
8262
  dirtyBaselineApplied = true;
8069
8263
  const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
8264
+ const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
8265
+ if (readyFile) {
8266
+ mkdirSync8(resolve22(readyFile, ".."), { recursive: true });
8267
+ writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
8268
+ `, "utf8");
8269
+ }
8070
8270
  appendRunLog(context.projectRoot, input.runId, {
8071
8271
  id: `log:${input.runId}:dirty-baseline`,
8072
8272
  title: "Dirty baseline snapshot",
8073
8273
  detail: dirty.detail,
8074
8274
  tone: dirty.applied ? "tool" : "info",
8075
8275
  status: dirty.applied ? "completed" : "skipped",
8076
- createdAt: new Date().toISOString()
8276
+ createdAt: new Date().toISOString(),
8277
+ payload: readyFile ? { readyFile } : undefined
8077
8278
  });
8078
8279
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
8079
8280
  }
@@ -8299,7 +8500,36 @@ async function executeRigOwnedTaskRun(context, input) {
8299
8500
  let reviewFailureDetail = null;
8300
8501
  const stderrLines = [];
8301
8502
  const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
8302
- for (let attempt = 1;attempt <= maxAttempts; attempt += 1) {
8503
+ if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
8504
+ appendRunLog(context.projectRoot, input.runId, {
8505
+ id: `log:${input.runId}:resume-closeout-phase`,
8506
+ title: "Resume continuing from closeout phase",
8507
+ detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
8508
+ tone: "info",
8509
+ status: resumePreviousStatus,
8510
+ createdAt: new Date().toISOString()
8511
+ });
8512
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
8513
+ exit = { code: 0, signal: null };
8514
+ } else if (resumeMode && latestRuntimeWorkspace) {
8515
+ const acceptedArtifactState = readTaskRunAcceptedArtifactState({
8516
+ taskId: input.taskId ?? runtimeTaskId,
8517
+ workspaceDir: latestRuntimeWorkspace
8518
+ });
8519
+ if (acceptedArtifactState.accepted) {
8520
+ appendRunLog(context.projectRoot, input.runId, {
8521
+ id: `log:${input.runId}:resume-accepted-artifacts`,
8522
+ title: "Resume found accepted artifacts; continuing closeout",
8523
+ detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
8524
+ tone: "info",
8525
+ status: "validating",
8526
+ createdAt: new Date().toISOString()
8527
+ });
8528
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
8529
+ exit = { code: 0, signal: null };
8530
+ }
8531
+ }
8532
+ for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
8303
8533
  const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
8304
8534
  const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
8305
8535
  cwd: context.projectRoot,
@@ -8340,7 +8570,7 @@ async function executeRigOwnedTaskRun(context, input) {
8340
8570
  let acceptedArtifactObservedAt = null;
8341
8571
  let acceptedArtifactPollTimer = null;
8342
8572
  let acceptedArtifactKillTimer = null;
8343
- const attemptExit = await new Promise((resolve22) => {
8573
+ const attemptExit = await new Promise((resolve23) => {
8344
8574
  let settled = false;
8345
8575
  const settle = (result) => {
8346
8576
  if (settled)
@@ -8348,7 +8578,7 @@ async function executeRigOwnedTaskRun(context, input) {
8348
8578
  settled = true;
8349
8579
  if (acceptedArtifactPollTimer)
8350
8580
  clearInterval(acceptedArtifactPollTimer);
8351
- resolve22(result);
8581
+ resolve23(result);
8352
8582
  };
8353
8583
  const pollAcceptedArtifacts = () => {
8354
8584
  const artifactState = readTaskRunAcceptedArtifactState({
@@ -8545,6 +8775,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8545
8775
  });
8546
8776
  throw new CliError2(terminalFailureDetail, exit.code ?? 1);
8547
8777
  }
8778
+ if (planningClassification.planningRequired) {
8779
+ const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
8780
+ const expectedPlanPath = resolve22(planWorkspace, planningArtifactPath);
8781
+ if (!existsSync13(expectedPlanPath)) {
8782
+ const failedAt = new Date().toISOString();
8783
+ const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
8784
+ patchAuthorityRun(context.projectRoot, input.runId, {
8785
+ status: "needs_attention",
8786
+ completedAt: failedAt,
8787
+ errorText: failureDetail
8788
+ });
8789
+ appendRunLog(context.projectRoot, input.runId, {
8790
+ id: `log:${input.runId}:plan-artifact-missing`,
8791
+ title: "Required plan artifact missing",
8792
+ detail: failureDetail,
8793
+ tone: "error",
8794
+ status: "needs_attention",
8795
+ createdAt: failedAt
8796
+ });
8797
+ emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
8798
+ throw new CliError2(failureDetail, 1);
8799
+ }
8800
+ }
8548
8801
  const runPiPrFeedbackFix = async (message2) => {
8549
8802
  appendPiStageLog({
8550
8803
  projectRoot: context.projectRoot,
@@ -8602,9 +8855,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8602
8855
  });
8603
8856
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
8604
8857
  });
8605
- const exitCode = await new Promise((resolve22) => {
8606
- child.once("error", () => resolve22(1));
8607
- child.once("close", (code) => resolve22(code ?? 1));
8858
+ const exitCode = await new Promise((resolve23) => {
8859
+ child.once("error", () => resolve23(1));
8860
+ child.once("close", (code) => resolve23(code ?? 1));
8608
8861
  });
8609
8862
  if (exitCode !== 0) {
8610
8863
  throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
@@ -8723,8 +8976,8 @@ async function executeTest(context, args) {
8723
8976
  }
8724
8977
 
8725
8978
  // packages/cli/src/commands/setup.ts
8726
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "fs";
8727
- import { resolve as resolve22 } from "path";
8979
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
8980
+ import { resolve as resolve23 } from "path";
8728
8981
  import { createPluginHost } from "@rig/core";
8729
8982
  import {
8730
8983
  isSupportedBunVersion as isSupportedBunVersion2,
@@ -8787,9 +9040,9 @@ function runSetupInit(projectRoot) {
8787
9040
  mkdirSync9(stateDir, { recursive: true });
8788
9041
  mkdirSync9(logsDir, { recursive: true });
8789
9042
  mkdirSync9(artifactsDir, { recursive: true });
8790
- const failuresPath = resolve22(stateDir, "failed_approaches.md");
8791
- if (!existsSync13(failuresPath)) {
8792
- writeFileSync6(failuresPath, `# Failed Approaches
9043
+ const failuresPath = resolve23(stateDir, "failed_approaches.md");
9044
+ if (!existsSync14(failuresPath)) {
9045
+ writeFileSync7(failuresPath, `# Failed Approaches
8793
9046
 
8794
9047
  `, "utf-8");
8795
9048
  }
@@ -8806,18 +9059,18 @@ async function runSetupCheck(projectRoot) {
8806
9059
  }
8807
9060
  async function runSetupPreflight(projectRoot) {
8808
9061
  await runSetupCheck(projectRoot);
8809
- const validationRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
8810
- if (existsSync13(validationRoot)) {
9062
+ const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
9063
+ if (existsSync14(validationRoot)) {
8811
9064
  const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
8812
9065
  for (const validator of validators) {
8813
- const script = resolve22(validationRoot, validator.name, "validate.sh");
8814
- if (existsSync13(script)) {
9066
+ const script = resolve23(validationRoot, validator.name, "validate.sh");
9067
+ if (existsSync14(script)) {
8815
9068
  console.log(`OK: validator script ${script}`);
8816
9069
  }
8817
9070
  }
8818
9071
  }
8819
- const hooksRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
8820
- if (existsSync13(hooksRoot)) {
9072
+ const hooksRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
9073
+ if (existsSync14(hooksRoot)) {
8821
9074
  const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
8822
9075
  for (const hook of hooks) {
8823
9076
  console.log(`OK: hook ${hook}`);
@@ -9211,8 +9464,8 @@ var __testOnly = {
9211
9464
  validateRequiredBugPromptValue
9212
9465
  };
9213
9466
  // packages/cli/src/launcher.ts
9214
- import { existsSync as existsSync14 } from "fs";
9215
- import { resolve as resolve23 } from "path";
9467
+ import { existsSync as existsSync15 } from "fs";
9468
+ import { basename as basename2, resolve as resolve24 } from "path";
9216
9469
  import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
9217
9470
  import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
9218
9471
  function parsePolicyMode(value) {
@@ -9225,7 +9478,7 @@ function parsePolicyMode(value) {
9225
9478
  throw new Error(`Invalid --policy-mode value: ${value}. Use off|observe|enforce.`);
9226
9479
  }
9227
9480
  function hasRigProjectMarker(candidate) {
9228
- 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"));
9481
+ 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"));
9229
9482
  }
9230
9483
  function resolveProjectRoot({
9231
9484
  envProjectRoot,
@@ -9234,17 +9487,19 @@ function resolveProjectRoot({
9234
9487
  cwd = process.cwd()
9235
9488
  }) {
9236
9489
  if (envProjectRoot) {
9237
- return resolve23(cwd, envProjectRoot);
9490
+ return resolve24(cwd, envProjectRoot);
9238
9491
  }
9239
9492
  const fallbackImportDir = importDir ?? cwd;
9240
- const candidates = [cwd, resolve23(execPath, "..", ".."), resolve23(fallbackImportDir, "..")];
9493
+ const execName = basename2(execPath).toLowerCase();
9494
+ const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve24(execPath, "..", "..")] : [];
9495
+ const candidates = [cwd, ...execCandidates, resolve24(fallbackImportDir, "..")];
9241
9496
  for (const candidate of candidates) {
9242
9497
  const nearest = resolveNearestRigProjectRoot(candidate);
9243
9498
  if (hasRigProjectMarker(nearest)) {
9244
9499
  return nearest;
9245
9500
  }
9246
9501
  }
9247
- return resolve23(cwd);
9502
+ return resolve24(cwd);
9248
9503
  }
9249
9504
  function normalizeCliErrorCode(message2, isCliError) {
9250
9505
  if (message2.startsWith("Invalid --policy-mode value:")) {
@@ -9311,7 +9566,7 @@ async function runRigCli(module, options = {}) {
9311
9566
  runId: context.runId,
9312
9567
  outcome,
9313
9568
  eventsFile: context.eventBus.getEventsFile(),
9314
- policyFile: resolve23(projectRoot, "rig", "policy", "policy.json"),
9569
+ policyFile: resolve24(projectRoot, "rig", "policy", "policy.json"),
9315
9570
  policyMode: context.policyMode ?? policyMode ?? module.loadPolicy(projectRoot).mode
9316
9571
  }, null, 2));
9317
9572
  }