@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/bin/rig.js CHANGED
@@ -105,12 +105,13 @@ async function runCommand(context, parts) {
105
105
  const envMode = process.env.RIG_BASH_MODE;
106
106
  const effectiveMode = context.policyMode || (envMode === "off" || envMode === "observe" || envMode === "enforce" ? envMode : loadPolicy(context.projectRoot).mode);
107
107
  const controlledPath = `${resolve(context.projectRoot, ".rig", "bin")}:${context.projectRoot}/rig/tools:${process.env.PATH ?? ""}`;
108
- const controlledBash = await ensureAgentShellBinary(context.projectRoot);
108
+ const usesInfrastructureBinary = parts[0] === "rig-server";
109
+ const controlledBash = usesInfrastructureBinary ? null : await ensureAgentShellBinary(context.projectRoot);
109
110
  const commandEnv = [
110
111
  "env",
111
112
  `PATH=${controlledPath}`,
112
113
  `PROJECT_RIG_ROOT=${context.projectRoot}`,
113
- `BASH=${controlledBash}`,
114
+ ...controlledBash ? [`BASH=${controlledBash}`] : [],
114
115
  `RIG_BASH_MODE=${effectiveMode}`,
115
116
  `RIG_POLICY_FILE=${resolve(context.projectRoot, "rig/policy/policy.json")}`,
116
117
  ...context.eventBus.getEventsFile() ? [`RIG_EVENTS_FILE=${context.eventBus.getEventsFile()}`] : []
@@ -2671,6 +2672,8 @@ function resolveSelectedConnection(projectRoot, options = {}) {
2671
2672
 
2672
2673
  // packages/cli/src/commands/_server-client.ts
2673
2674
  import { spawnSync } from "child_process";
2675
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
2676
+ import { resolve as resolve9 } from "path";
2674
2677
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
2675
2678
  var cachedGitHubBearerToken;
2676
2679
  function cleanToken(value) {
@@ -2680,9 +2683,25 @@ function cleanToken(value) {
2680
2683
  function setGitHubBearerTokenForCurrentProcess(token) {
2681
2684
  cachedGitHubBearerToken = cleanToken(token ?? undefined);
2682
2685
  }
2683
- function readGitHubBearerTokenForRemote() {
2686
+ function readPrivateRemoteSessionToken(projectRoot) {
2687
+ const path = resolve9(projectRoot, ".rig", "state", "github-auth.json");
2688
+ if (!existsSync5(path))
2689
+ return null;
2690
+ try {
2691
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
2692
+ return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
2693
+ } catch {
2694
+ return null;
2695
+ }
2696
+ }
2697
+ function readGitHubBearerTokenForRemote(projectRoot) {
2684
2698
  if (cachedGitHubBearerToken !== undefined)
2685
2699
  return cachedGitHubBearerToken;
2700
+ const privateSession = readPrivateRemoteSessionToken(projectRoot);
2701
+ if (privateSession) {
2702
+ cachedGitHubBearerToken = privateSession;
2703
+ return cachedGitHubBearerToken;
2704
+ }
2686
2705
  const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
2687
2706
  if (envToken) {
2688
2707
  cachedGitHubBearerToken = envToken;
@@ -2702,7 +2721,7 @@ async function ensureServerForCli(projectRoot) {
2702
2721
  if (selected?.connection.kind === "remote") {
2703
2722
  return {
2704
2723
  baseUrl: selected.connection.baseUrl,
2705
- authToken: readGitHubBearerTokenForRemote(),
2724
+ authToken: readGitHubBearerTokenForRemote(projectRoot),
2706
2725
  connectionKind: "remote"
2707
2726
  };
2708
2727
  }
@@ -2809,7 +2828,7 @@ async function postGitHubTokenViaServer(context, token, options = {}) {
2809
2828
  const payload = await requestServerJson(context, "/api/github/auth/token", {
2810
2829
  method: "POST",
2811
2830
  headers: { "content-type": "application/json" },
2812
- body: JSON.stringify({ token, selectedRepo: options.selectedRepo })
2831
+ body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
2813
2832
  });
2814
2833
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2815
2834
  }
@@ -2830,18 +2849,38 @@ async function registerProjectViaServer(context, input) {
2830
2849
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2831
2850
  }
2832
2851
  function sleep(ms) {
2833
- return new Promise((resolve9) => setTimeout(resolve9, ms));
2852
+ return new Promise((resolve10) => setTimeout(resolve10, ms));
2853
+ }
2854
+ function isRetryableProjectRootSwitchError(error) {
2855
+ if (!(error instanceof Error))
2856
+ return false;
2857
+ const message = error.message.toLowerCase();
2858
+ 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");
2834
2859
  }
2835
2860
  async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
2836
- const switched = await requestServerJson(context, "/api/server/project-root", {
2837
- method: "POST",
2838
- headers: { "content-type": "application/json" },
2839
- body: JSON.stringify({ projectRoot })
2840
- });
2841
2861
  const timeoutMs = options.timeoutMs ?? 30000;
2842
2862
  const pollMs = options.pollMs ?? 1000;
2843
2863
  const deadline = Date.now() + timeoutMs;
2844
2864
  let lastError;
2865
+ let switched = null;
2866
+ while (Date.now() < deadline) {
2867
+ try {
2868
+ switched = await requestServerJson(context, "/api/server/project-root", {
2869
+ method: "POST",
2870
+ headers: { "content-type": "application/json" },
2871
+ body: JSON.stringify({ projectRoot })
2872
+ });
2873
+ break;
2874
+ } catch (error) {
2875
+ lastError = error;
2876
+ if (!isRetryableProjectRootSwitchError(error))
2877
+ throw error;
2878
+ await sleep(pollMs);
2879
+ }
2880
+ }
2881
+ if (!switched) {
2882
+ 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);
2883
+ }
2845
2884
  while (Date.now() < deadline) {
2846
2885
  try {
2847
2886
  const status = await requestServerJson(context, "/api/server/status");
@@ -2921,9 +2960,9 @@ async function submitTaskRunViaServer(context, input) {
2921
2960
  }
2922
2961
 
2923
2962
  // packages/cli/src/commands/_pi-install.ts
2924
- import { existsSync as existsSync5, readFileSync as readFileSync3, rmSync as rmSync3 } from "fs";
2963
+ import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
2925
2964
  import { homedir as homedir3 } from "os";
2926
- import { resolve as resolve9 } from "path";
2965
+ import { resolve as resolve10 } from "path";
2927
2966
  var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
2928
2967
  var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
2929
2968
  export { default } from '@rig/pi-rig';
@@ -2938,11 +2977,11 @@ async function defaultCommandRunner(command, options = {}) {
2938
2977
  return { exitCode, stdout, stderr };
2939
2978
  }
2940
2979
  function resolvePiRigExtensionPath(homeDir) {
2941
- return resolve9(homeDir, ".pi", "agent", "extensions", "pi-rig");
2980
+ return resolve10(homeDir, ".pi", "agent", "extensions", "pi-rig");
2942
2981
  }
2943
- function resolvePiRigPackageSource(projectRoot, exists = existsSync5) {
2944
- const localPackage = resolve9(projectRoot, "packages", "pi-rig");
2945
- if (exists(resolve9(localPackage, "package.json")))
2982
+ function resolvePiRigPackageSource(projectRoot, exists = existsSync6) {
2983
+ const localPackage = resolve10(projectRoot, "packages", "pi-rig");
2984
+ if (exists(resolve10(localPackage, "package.json")))
2946
2985
  return localPackage;
2947
2986
  return `npm:${PI_RIG_PACKAGE_NAME}`;
2948
2987
  }
@@ -2993,13 +3032,13 @@ async function ensurePiBinaryAvailable(input) {
2993
3032
  ...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
2994
3033
  };
2995
3034
  }
2996
- function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync5) {
3035
+ function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync6) {
2997
3036
  const extensionPath = resolvePiRigExtensionPath(homeDir);
2998
- const indexPath = resolve9(extensionPath, "index.ts");
3037
+ const indexPath = resolve10(extensionPath, "index.ts");
2999
3038
  if (!exists(indexPath))
3000
3039
  return;
3001
3040
  try {
3002
- const content = readFileSync3(indexPath, "utf8");
3041
+ const content = readFileSync4(indexPath, "utf8");
3003
3042
  if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
3004
3043
  rmSync3(extensionPath, { recursive: true, force: true });
3005
3044
  }
@@ -3015,13 +3054,13 @@ async function checkPiRigInstall(input = {}) {
3015
3054
  piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
3016
3055
  };
3017
3056
  }
3018
- const exists = input.exists ?? existsSync5;
3057
+ const exists = input.exists ?? existsSync6;
3019
3058
  const runner = input.commandRunner ?? defaultCommandRunner;
3020
3059
  const piResult = await safeRun(runner, ["pi", "--version"]);
3021
3060
  const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
3022
3061
  const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
3023
3062
  ${piListResult.stderr}`);
3024
- const legacyBridge = exists(resolve9(extensionPath, "index.ts"));
3063
+ const legacyBridge = exists(resolve10(extensionPath, "index.ts"));
3025
3064
  const hasPiRig = listedPiRig;
3026
3065
  return {
3027
3066
  extensionPath,
@@ -3359,7 +3398,7 @@ async function executeQueue(context, args) {
3359
3398
  }
3360
3399
 
3361
3400
  // packages/cli/src/commands/agent.ts
3362
- import { resolve as resolve11 } from "path";
3401
+ import { resolve as resolve12 } from "path";
3363
3402
  import {
3364
3403
  agentId,
3365
3404
  cleanupAgentRuntime,
@@ -3369,8 +3408,8 @@ import {
3369
3408
  } from "@rig/runtime/control-plane/runtime/isolation";
3370
3409
 
3371
3410
  // packages/cli/src/commands/_authority-runs.ts
3372
- import { existsSync as existsSync6 } from "fs";
3373
- import { resolve as resolve10 } from "path";
3411
+ import { existsSync as existsSync7 } from "fs";
3412
+ import { resolve as resolve11 } from "path";
3374
3413
  import {
3375
3414
  readAuthorityRun,
3376
3415
  readJsonlFile as readJsonlFile2,
@@ -3392,8 +3431,8 @@ function normalizeRuntimeAdapter(value) {
3392
3431
  return "claude-code";
3393
3432
  }
3394
3433
  function readLatestBeadRecord(projectRoot, taskId) {
3395
- const issuesPath = resolve10(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3396
- if (!existsSync6(issuesPath)) {
3434
+ const issuesPath = resolve11(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3435
+ if (!existsSync7(issuesPath)) {
3397
3436
  return null;
3398
3437
  }
3399
3438
  let latest = null;
@@ -3428,6 +3467,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3428
3467
  const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
3429
3468
  const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
3430
3469
  const next = {
3470
+ ...existing ?? {},
3431
3471
  runId: input.runId,
3432
3472
  projectRoot,
3433
3473
  workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
@@ -3460,7 +3500,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3460
3500
  } else if ("errorText" in next) {
3461
3501
  delete next.errorText;
3462
3502
  }
3463
- writeJsonFile3(resolve10(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3503
+ writeJsonFile3(resolve11(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3464
3504
  return next;
3465
3505
  }
3466
3506
 
@@ -3581,10 +3621,10 @@ async function executeAgent(context, args) {
3581
3621
  status: "running",
3582
3622
  startedAt: createdAt,
3583
3623
  worktreePath: runtime.workspaceDir,
3584
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3624
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3585
3625
  logRoot: runtime.logsDir,
3586
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3587
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3626
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3627
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3588
3628
  pid: process.pid
3589
3629
  });
3590
3630
  const result = await runInAgentRuntime({
@@ -3604,10 +3644,10 @@ async function executeAgent(context, args) {
3604
3644
  startedAt: createdAt,
3605
3645
  completedAt: failedAt,
3606
3646
  worktreePath: runtime.workspaceDir,
3607
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3647
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3608
3648
  logRoot: runtime.logsDir,
3609
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3610
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3649
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3650
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3611
3651
  pid: process.pid,
3612
3652
  errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
3613
3653
  });
@@ -3624,10 +3664,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3624
3664
  startedAt: createdAt,
3625
3665
  completedAt,
3626
3666
  worktreePath: runtime.workspaceDir,
3627
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3667
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3628
3668
  logRoot: runtime.logsDir,
3629
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3630
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3669
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3670
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3631
3671
  pid: process.pid
3632
3672
  });
3633
3673
  return {
@@ -3701,7 +3741,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3701
3741
  import {
3702
3742
  chmodSync,
3703
3743
  copyFileSync as copyFileSync2,
3704
- existsSync as existsSync7,
3744
+ existsSync as existsSync8,
3705
3745
  mkdirSync as mkdirSync6,
3706
3746
  readdirSync,
3707
3747
  readlinkSync,
@@ -3711,7 +3751,7 @@ import {
3711
3751
  unlinkSync
3712
3752
  } from "fs";
3713
3753
  import { homedir as homedir4 } from "os";
3714
- import { resolve as resolve12 } from "path";
3754
+ import { resolve as resolve13 } from "path";
3715
3755
  import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
3716
3756
  import {
3717
3757
  computeRuntimeImageFingerprint,
@@ -3730,13 +3770,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
3730
3770
 
3731
3771
  // packages/cli/src/commands/dist.ts
3732
3772
  function collectRigValidatorBuildTargets(input) {
3733
- const validatorsRoot = resolve12(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3734
- if (!existsSync7(validatorsRoot))
3773
+ const validatorsRoot = resolve13(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3774
+ if (!existsSync8(validatorsRoot))
3735
3775
  return [];
3736
3776
  const targets = [];
3737
3777
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3738
3778
  for (const category of categories) {
3739
- const categoryDir = resolve12(validatorsRoot, category.name);
3779
+ const categoryDir = resolve13(validatorsRoot, category.name);
3740
3780
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3741
3781
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3742
3782
  continue;
@@ -3745,7 +3785,7 @@ function collectRigValidatorBuildTargets(input) {
3745
3785
  continue;
3746
3786
  targets.push({
3747
3787
  source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
3748
- dest: resolve12(input.imageDir, `bin/validators/${category.name}-${check}`),
3788
+ dest: resolve13(input.imageDir, `bin/validators/${category.name}-${check}`),
3749
3789
  cwd: input.hostProjectRoot
3750
3790
  });
3751
3791
  }
@@ -3754,16 +3794,16 @@ function collectRigValidatorBuildTargets(input) {
3754
3794
  }
3755
3795
  async function findLatestDistBinary(projectRoot) {
3756
3796
  const distRoot = resolveControlPlaneHostDistDir(projectRoot);
3757
- if (!existsSync7(distRoot)) {
3797
+ if (!existsSync8(distRoot)) {
3758
3798
  return null;
3759
3799
  }
3760
3800
  const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
3761
3801
  name: entry.name,
3762
- mtimeMs: statSync(resolve12(distRoot, entry.name)).mtimeMs
3802
+ mtimeMs: statSync(resolve13(distRoot, entry.name)).mtimeMs
3763
3803
  })).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
3764
3804
  for (const { name } of entries) {
3765
- const candidate = resolve12(distRoot, name, "bin", "rig");
3766
- if (existsSync7(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3805
+ const candidate = resolve13(distRoot, name, "bin", "rig");
3806
+ if (existsSync8(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3767
3807
  return candidate;
3768
3808
  }
3769
3809
  }
@@ -3775,7 +3815,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
3775
3815
  async function runDistDoctor(projectRoot) {
3776
3816
  const bunPath = Bun.which("bun");
3777
3817
  const rigPath = Bun.which("rig");
3778
- const userBinDir = resolve12(homedir4(), ".local/bin");
3818
+ const userBinDir = resolve13(homedir4(), ".local/bin");
3779
3819
  const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
3780
3820
  let rigRunnable = false;
3781
3821
  if (rigPath) {
@@ -3823,15 +3863,15 @@ async function executeDist(context, args) {
3823
3863
  let source = await findLatestDistBinary(context.projectRoot);
3824
3864
  let buildDir = null;
3825
3865
  if (!source) {
3826
- buildDir = resolve12(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3866
+ buildDir = resolve13(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3827
3867
  await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
3828
- source = resolve12(buildDir, "bin", "rig");
3868
+ source = resolve13(buildDir, "bin", "rig");
3829
3869
  }
3830
- if (!existsSync7(source)) {
3870
+ if (!existsSync8(source)) {
3831
3871
  throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
3832
3872
  }
3833
- const installedPath = resolve12(installDir, "rig");
3834
- if (existsSync7(installedPath)) {
3873
+ const installedPath = resolve13(installDir, "rig");
3874
+ if (existsSync8(installedPath)) {
3835
3875
  unlinkSync(installedPath);
3836
3876
  }
3837
3877
  copyFileSync2(source, installedPath);
@@ -3873,22 +3913,22 @@ async function executeDist(context, args) {
3873
3913
  requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
3874
3914
  const fp = await computeRuntimeImageFingerprint(context.projectRoot);
3875
3915
  const currentId = computeRuntimeImageId(fp);
3876
- const imagesDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3916
+ const imagesDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3877
3917
  mkdirSync6(imagesDir, { recursive: true });
3878
3918
  let pruned = 0;
3879
3919
  for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
3880
3920
  if (entry.isDirectory() && entry.name !== currentId) {
3881
- rmSync4(resolve12(imagesDir, entry.name), { recursive: true, force: true });
3921
+ rmSync4(resolve13(imagesDir, entry.name), { recursive: true, force: true });
3882
3922
  pruned++;
3883
3923
  }
3884
3924
  }
3885
3925
  if (pruned > 0 && context.outputMode === "text") {
3886
3926
  console.log(`Pruned ${pruned} stale image(s).`);
3887
3927
  }
3888
- const imageDir = resolve12(imagesDir, currentId);
3889
- mkdirSync6(resolve12(imageDir, "bin/hooks"), { recursive: true });
3890
- mkdirSync6(resolve12(imageDir, "bin/plugins"), { recursive: true });
3891
- mkdirSync6(resolve12(imageDir, "bin/validators"), { recursive: true });
3928
+ const imageDir = resolve13(imagesDir, currentId);
3929
+ mkdirSync6(resolve13(imageDir, "bin/hooks"), { recursive: true });
3930
+ mkdirSync6(resolve13(imageDir, "bin/plugins"), { recursive: true });
3931
+ mkdirSync6(resolve13(imageDir, "bin/validators"), { recursive: true });
3892
3932
  const hookNames = [
3893
3933
  "scope-guard",
3894
3934
  "import-guard",
@@ -3902,25 +3942,25 @@ async function executeDist(context, args) {
3902
3942
  ];
3903
3943
  const targets = [];
3904
3944
  const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || context.projectRoot;
3905
- targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve12(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
3906
- targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
3945
+ targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve13(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
3946
+ targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
3907
3947
  for (const hookName of hookNames) {
3908
3948
  const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
3909
- targets.push({ source: src, dest: resolve12(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
3910
- targets.push({ source: src, dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
3949
+ targets.push({ source: src, dest: resolve13(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
3950
+ targets.push({ source: src, dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
3911
3951
  }
3912
- const pluginsDir = resolve12(context.projectRoot, "rig/plugins");
3913
- const binPluginsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
3914
- const validatorsRoot = resolve12(hostProjectRoot, "packages/runtime/src/control-plane/validators");
3915
- const binValidatorsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
3952
+ const pluginsDir = resolve13(context.projectRoot, "rig/plugins");
3953
+ const binPluginsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
3954
+ const validatorsRoot = resolve13(hostProjectRoot, "packages/runtime/src/control-plane/validators");
3955
+ const binValidatorsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
3916
3956
  mkdirSync6(binPluginsDir, { recursive: true });
3917
3957
  mkdirSync6(binValidatorsDir, { recursive: true });
3918
- if (existsSync7(pluginsDir)) {
3958
+ if (existsSync8(pluginsDir)) {
3919
3959
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3920
3960
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3921
3961
  if (!m)
3922
3962
  continue;
3923
- targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve12(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3963
+ targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve13(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3924
3964
  }
3925
3965
  }
3926
3966
  targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
@@ -3931,17 +3971,17 @@ async function executeDist(context, args) {
3931
3971
  const isValidator = dest.includes("/bin/validators/");
3932
3972
  await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
3933
3973
  }
3934
- if (existsSync7(pluginsDir)) {
3974
+ if (existsSync8(pluginsDir)) {
3935
3975
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3936
3976
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3937
3977
  if (!m)
3938
3978
  continue;
3939
3979
  const pluginName = m[1];
3940
- const imageBin = resolve12(imageDir, `bin/plugins/${pluginName}`);
3980
+ const imageBin = resolve13(imageDir, `bin/plugins/${pluginName}`);
3941
3981
  if (!pluginName)
3942
3982
  continue;
3943
- const symlinkPath = resolve12(binPluginsDir, pluginName);
3944
- if (existsSync7(imageBin)) {
3983
+ const symlinkPath = resolve13(binPluginsDir, pluginName);
3984
+ if (existsSync8(imageBin)) {
3945
3985
  try {
3946
3986
  unlinkSync(symlinkPath);
3947
3987
  } catch {}
@@ -3949,10 +3989,10 @@ async function executeDist(context, args) {
3949
3989
  }
3950
3990
  }
3951
3991
  }
3952
- if (existsSync7(validatorsRoot)) {
3992
+ if (existsSync8(validatorsRoot)) {
3953
3993
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3954
3994
  for (const category of categories) {
3955
- const categoryDir = resolve12(validatorsRoot, category.name);
3995
+ const categoryDir = resolve13(validatorsRoot, category.name);
3956
3996
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3957
3997
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3958
3998
  continue;
@@ -3960,9 +4000,9 @@ async function executeDist(context, args) {
3960
4000
  if (!check || check === "index" || check === "shared")
3961
4001
  continue;
3962
4002
  const validatorName = `${category.name}-${check}`;
3963
- const imageBin = resolve12(imageDir, `bin/validators/${validatorName}`);
3964
- const symlinkPath = resolve12(binValidatorsDir, validatorName);
3965
- if (existsSync7(imageBin)) {
4003
+ const imageBin = resolve13(imageDir, `bin/validators/${validatorName}`);
4004
+ const symlinkPath = resolve13(binValidatorsDir, validatorName);
4005
+ if (existsSync8(imageBin)) {
3966
4006
  try {
3967
4007
  unlinkSync(symlinkPath);
3968
4008
  } catch {}
@@ -3971,18 +4011,18 @@ async function executeDist(context, args) {
3971
4011
  }
3972
4012
  }
3973
4013
  }
3974
- const agentsDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
3975
- if (existsSync7(agentsDir)) {
4014
+ const agentsDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
4015
+ if (existsSync8(agentsDir)) {
3976
4016
  let relinkCount = 0;
3977
4017
  for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
3978
4018
  if (!agentEntry.isDirectory())
3979
4019
  continue;
3980
- const agentBinDir = resolve12(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
3981
- if (!existsSync7(agentBinDir))
4020
+ const agentBinDir = resolve13(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
4021
+ if (!existsSync8(agentBinDir))
3982
4022
  continue;
3983
4023
  const walkDir = (dir) => {
3984
4024
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
3985
- const fullPath = resolve12(dir, entry.name);
4025
+ const fullPath = resolve13(dir, entry.name);
3986
4026
  if (entry.isDirectory()) {
3987
4027
  walkDir(fullPath);
3988
4028
  } else if (entry.isSymbolicLink()) {
@@ -4016,7 +4056,7 @@ async function executeDist(context, args) {
4016
4056
 
4017
4057
  // packages/cli/src/commands/inbox.ts
4018
4058
  import { writeFileSync as writeFileSync4 } from "fs";
4019
- import { resolve as resolve13 } from "path";
4059
+ import { resolve as resolve14 } from "path";
4020
4060
  import {
4021
4061
  listAuthorityRuns,
4022
4062
  readJsonlFile as readJsonlFile3,
@@ -4033,7 +4073,7 @@ async function executeInbox(context, args) {
4033
4073
  pending = task.rest;
4034
4074
  requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
4035
4075
  const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
4036
- const approvals = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
4076
+ const approvals = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
4037
4077
  runId: entry.runId,
4038
4078
  record
4039
4079
  })));
@@ -4061,7 +4101,7 @@ async function executeInbox(context, args) {
4061
4101
  if (decision.value !== "approve" && decision.value !== "reject") {
4062
4102
  throw new CliError2("decision must be approve or reject.");
4063
4103
  }
4064
- const approvalsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
4104
+ const approvalsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
4065
4105
  const approvals = readJsonlFile3(approvalsPath);
4066
4106
  const resolvedAt = new Date().toISOString();
4067
4107
  const next = approvals.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", decision: decision.value, note: note3.value ?? null, resolvedAt } : entry);
@@ -4078,7 +4118,7 @@ async function executeInbox(context, args) {
4078
4118
  pending = task.rest;
4079
4119
  requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
4080
4120
  const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
4081
- const requests = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
4121
+ const requests = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
4082
4122
  runId: entry.runId,
4083
4123
  record
4084
4124
  })));
@@ -4120,7 +4160,7 @@ async function executeInbox(context, args) {
4120
4160
  const [key, ...restValue] = entry.split("=");
4121
4161
  return [key, restValue.join("=")];
4122
4162
  }));
4123
- const requestsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
4163
+ const requestsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
4124
4164
  const requests = readJsonlFile3(requestsPath);
4125
4165
  const resolvedAt = new Date().toISOString();
4126
4166
  const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
@@ -4135,14 +4175,14 @@ async function executeInbox(context, args) {
4135
4175
  }
4136
4176
 
4137
4177
  // packages/cli/src/commands/init.ts
4138
- import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
4178
+ import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
4139
4179
  import { spawnSync as spawnSync2 } from "child_process";
4140
- import { resolve as resolve16 } from "path";
4180
+ import { resolve as resolve17 } from "path";
4141
4181
  import { buildRigInitConfigSource } from "@rig/core";
4142
4182
 
4143
4183
  // packages/cli/src/commands/_snapshot-upload.ts
4144
4184
  import { mkdir, readdir, readFile, writeFile } from "fs/promises";
4145
- import { dirname as dirname2, resolve as resolve14, relative, sep } from "path";
4185
+ import { dirname as dirname2, resolve as resolve15, relative, sep } from "path";
4146
4186
  var SNAPSHOT_ARCHIVE_VERSION = 1;
4147
4187
  var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
4148
4188
  var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
@@ -4164,15 +4204,15 @@ function assertManifestPath(root, relativePath) {
4164
4204
  if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
4165
4205
  throw new Error(`Invalid snapshot path: ${relativePath}`);
4166
4206
  }
4167
- const resolved = resolve14(root, relativePath);
4207
+ const resolved = resolve15(root, relativePath);
4168
4208
  const relativeToRoot = relative(root, resolved);
4169
- if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve14(relativeToRoot) === resolved) {
4209
+ if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve15(relativeToRoot) === resolved) {
4170
4210
  throw new Error(`Snapshot path escapes project root: ${relativePath}`);
4171
4211
  }
4172
4212
  return resolved;
4173
4213
  }
4174
4214
  async function buildSnapshotUploadManifest(projectRoot, options = {}) {
4175
- const root = resolve14(projectRoot);
4215
+ const root = resolve15(projectRoot);
4176
4216
  const excludedDirectories = [...new Set([
4177
4217
  ...DEFAULT_EXCLUDED_DIRECTORIES,
4178
4218
  ...options.excludedDirectories ?? []
@@ -4184,7 +4224,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
4184
4224
  for (const entry of entries) {
4185
4225
  if (entry.isDirectory() && excludedSet.has(entry.name))
4186
4226
  continue;
4187
- const fullPath = resolve14(dir, entry.name);
4227
+ const fullPath = resolve15(dir, entry.name);
4188
4228
  if (entry.isDirectory()) {
4189
4229
  await visit(fullPath);
4190
4230
  continue;
@@ -4232,8 +4272,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
4232
4272
  }
4233
4273
 
4234
4274
  // packages/cli/src/commands/_doctor-checks.ts
4235
- import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
4236
- import { resolve as resolve15 } from "path";
4275
+ import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
4276
+ import { resolve as resolve16 } from "path";
4237
4277
  import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
4238
4278
  function check(id, label, status, detail, remediation) {
4239
4279
  return {
@@ -4273,11 +4313,11 @@ function repoSlugFromConfig(config) {
4273
4313
  function loadFallbackConfig(projectRoot) {
4274
4314
  const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
4275
4315
  for (const name of candidates) {
4276
- const path = resolve15(projectRoot, name);
4277
- if (!existsSync8(path))
4316
+ const path = resolve16(projectRoot, name);
4317
+ if (!existsSync9(path))
4278
4318
  continue;
4279
4319
  try {
4280
- const source = readFileSync4(path, "utf8");
4320
+ const source = readFileSync5(path, "utf8");
4281
4321
  if (name.endsWith(".json"))
4282
4322
  return JSON.parse(source);
4283
4323
  const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
@@ -4356,7 +4396,7 @@ async function runRigDoctorChecks(options) {
4356
4396
  checks.push(check("bun", `bun >= ${MIN_SUPPORTED_BUN_VERSION}`, isSupportedBunVersion(bunVersion) ? "pass" : "fail", `found ${bunVersion}`, `Install Bun ${MIN_SUPPORTED_BUN_VERSION} or newer.`), check("git", "git", which("git") ? "pass" : "fail", which("git") ?? undefined, "Install git and ensure it is on PATH."), check("jq", "jq", which("jq") ? "pass" : "warn", which("jq") ?? undefined, "Install jq (for example `brew install jq`)."));
4357
4397
  const loadedConfig = await loadConfig(projectRoot).catch(() => null);
4358
4398
  const config = loadedConfig ?? loadFallbackConfig(projectRoot);
4359
- const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync8(resolve15(projectRoot, name)));
4399
+ const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync9(resolve16(projectRoot, name)));
4360
4400
  checks.push(config ? check("config", "rig.config loadable", "pass") : check("config", "rig.config loadable", hasConfigFile ? "fail" : "fail", hasConfigFile ? "config file exists but failed to load" : "missing rig.config.ts/json", "Run `rig init` or fix the config error."));
4361
4401
  const taskSourceKind = config?.taskSource?.kind;
4362
4402
  checks.push(taskSourceKind ? check("task-source", "task source configured", "pass", taskSourceKind) : check("task-source", "task source configured", "fail", "missing taskSource", "Configure taskSource in rig.config.ts."));
@@ -4451,10 +4491,10 @@ function countDoctorFailures(checks) {
4451
4491
  }
4452
4492
 
4453
4493
  // packages/cli/src/commands/init.ts
4454
- var RIG_CONFIG_PACKAGE_VERSION = "0.0.6-alpha.1";
4494
+ var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
4455
4495
  var RIG_CONFIG_DEV_DEPENDENCIES = {
4456
- "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_VERSION}`,
4457
- "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_VERSION}`
4496
+ "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
4497
+ "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
4458
4498
  };
4459
4499
  function parseRepoSlugFromRemote(remoteUrl) {
4460
4500
  const trimmed = remoteUrl.trim();
@@ -4474,20 +4514,20 @@ function parseRepoSlug(value) {
4474
4514
  return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
4475
4515
  }
4476
4516
  function ensureRigPrivateDirs(projectRoot) {
4477
- const rigDir = resolve16(projectRoot, ".rig");
4478
- mkdirSync7(resolve16(rigDir, "state"), { recursive: true });
4479
- mkdirSync7(resolve16(rigDir, "logs"), { recursive: true });
4480
- mkdirSync7(resolve16(rigDir, "runs"), { recursive: true });
4481
- mkdirSync7(resolve16(rigDir, "tmp"), { recursive: true });
4482
- mkdirSync7(resolve16(projectRoot, "artifacts"), { recursive: true });
4483
- const taskConfigPath = resolve16(rigDir, "task-config.json");
4484
- if (!existsSync9(taskConfigPath))
4517
+ const rigDir = resolve17(projectRoot, ".rig");
4518
+ mkdirSync7(resolve17(rigDir, "state"), { recursive: true });
4519
+ mkdirSync7(resolve17(rigDir, "logs"), { recursive: true });
4520
+ mkdirSync7(resolve17(rigDir, "runs"), { recursive: true });
4521
+ mkdirSync7(resolve17(rigDir, "tmp"), { recursive: true });
4522
+ mkdirSync7(resolve17(projectRoot, "artifacts"), { recursive: true });
4523
+ const taskConfigPath = resolve17(rigDir, "task-config.json");
4524
+ if (!existsSync10(taskConfigPath))
4485
4525
  writeFileSync5(taskConfigPath, `{}
4486
4526
  `, "utf-8");
4487
4527
  }
4488
4528
  function ensureGitignoreEntries(projectRoot) {
4489
- const path = resolve16(projectRoot, ".gitignore");
4490
- const existing = existsSync9(path) ? readFileSync5(path, "utf8") : "";
4529
+ const path = resolve17(projectRoot, ".gitignore");
4530
+ const existing = existsSync10(path) ? readFileSync6(path, "utf8") : "";
4491
4531
  const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
4492
4532
  const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
4493
4533
  if (missing.length === 0)
@@ -4500,14 +4540,14 @@ function ensureGitignoreEntries(projectRoot) {
4500
4540
  `, "utf8");
4501
4541
  }
4502
4542
  function ensureRigConfigPackageDependencies(projectRoot) {
4503
- const path = resolve16(projectRoot, "package.json");
4504
- const existing = existsSync9(path) ? JSON.parse(readFileSync5(path, "utf8")) : {};
4543
+ const path = resolve17(projectRoot, "package.json");
4544
+ const existing = existsSync10(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
4505
4545
  const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
4506
4546
  for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
4507
4547
  devDependencies[name] = spec;
4508
4548
  }
4509
4549
  const next = {
4510
- ...existsSync9(path) ? existing : { name: "rig-project", private: true },
4550
+ ...existsSync10(path) ? existing : { name: "rig-project", private: true },
4511
4551
  devDependencies
4512
4552
  };
4513
4553
  writeFileSync5(path, `${JSON.stringify(next, null, 2)}
@@ -4577,15 +4617,71 @@ async function promptSelect(prompts, options) {
4577
4617
  throw new CliError2("Init cancelled.", 1);
4578
4618
  return String(value);
4579
4619
  }
4580
- async function pollDeviceAuthOnce(context, pollId) {
4620
+ function sleep2(ms) {
4621
+ return new Promise((resolve18) => setTimeout(resolve18, ms));
4622
+ }
4623
+ function positiveIntFromEnv(name, fallback) {
4624
+ const value = Number.parseInt(process.env[name] ?? "", 10);
4625
+ return Number.isFinite(value) && value >= 0 ? value : fallback;
4626
+ }
4627
+ function apiSessionTokenFrom(payload) {
4628
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
4629
+ return null;
4630
+ const token = payload.apiSessionToken;
4631
+ return typeof token === "string" && token.trim() ? token.trim() : null;
4632
+ }
4633
+ function cleanPayloadString(value) {
4634
+ return typeof value === "string" && value.trim() ? value.trim() : null;
4635
+ }
4636
+ function remoteGitHubAuthMetadata(payload) {
4637
+ if (!payload)
4638
+ return {};
4639
+ const userNamespace = payload.userNamespace && typeof payload.userNamespace === "object" && !Array.isArray(payload.userNamespace) ? payload.userNamespace : null;
4640
+ return {
4641
+ ...cleanPayloadString(payload.login) ? { login: cleanPayloadString(payload.login) } : {},
4642
+ ...cleanPayloadString(payload.userId) ? { userId: cleanPayloadString(payload.userId) } : {},
4643
+ ...cleanPayloadString(userNamespace?.key) ? { userNamespaceKey: cleanPayloadString(userNamespace?.key) } : {},
4644
+ ...cleanPayloadString(userNamespace?.root) ? { userNamespaceRoot: cleanPayloadString(userNamespace?.root) } : {},
4645
+ ...cleanPayloadString(userNamespace?.checkoutBaseDir) ? { checkoutBaseDir: cleanPayloadString(userNamespace?.checkoutBaseDir) } : {},
4646
+ ...cleanPayloadString(userNamespace?.snapshotBaseDir) ? { snapshotBaseDir: cleanPayloadString(userNamespace?.snapshotBaseDir) } : {}
4647
+ };
4648
+ }
4649
+ function writeRemoteGitHubAuthState(projectRoot, input) {
4650
+ writeFileSync5(resolve17(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
4651
+ authenticated: true,
4652
+ source: input.source,
4653
+ storedOnServer: true,
4654
+ selectedRepo: input.selectedRepo,
4655
+ ...remoteGitHubAuthMetadata(input.authPayload ?? null),
4656
+ ...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
4657
+ updatedAt: new Date().toISOString()
4658
+ }, null, 2)}
4659
+ `, "utf8");
4660
+ }
4661
+ async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
4581
4662
  if (typeof pollId !== "string" || !pollId.trim())
4582
4663
  return null;
4583
- const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
4584
- method: "POST",
4585
- headers: { "content-type": "application/json" },
4586
- body: JSON.stringify({ pollId })
4587
- }).catch(() => null);
4588
- return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
4664
+ const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
4665
+ const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
4666
+ const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
4667
+ const deadline = Date.now() + timeoutMs;
4668
+ let last = null;
4669
+ do {
4670
+ const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
4671
+ method: "POST",
4672
+ headers: { "content-type": "application/json" },
4673
+ body: JSON.stringify({ pollId })
4674
+ }).catch(() => null);
4675
+ last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
4676
+ const status = typeof last?.status === "string" ? last.status : null;
4677
+ if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
4678
+ return last;
4679
+ }
4680
+ if (timeoutMs <= 0)
4681
+ return last;
4682
+ await sleep2(intervalMs);
4683
+ } while (Date.now() < deadline);
4684
+ return last;
4589
4685
  }
4590
4686
  async function runControlPlaneInit(context, options) {
4591
4687
  const projectRoot = context.projectRoot;
@@ -4608,9 +4704,9 @@ async function runControlPlaneInit(context, options) {
4608
4704
  });
4609
4705
  ensureRigPrivateDirs(projectRoot);
4610
4706
  ensureGitignoreEntries(projectRoot);
4611
- const configTsPath = resolve16(projectRoot, "rig.config.ts");
4612
- const configJsonPath = resolve16(projectRoot, "rig.config.json");
4613
- const configExists = existsSync9(configTsPath) || existsSync9(configJsonPath);
4707
+ const configTsPath = resolve17(projectRoot, "rig.config.ts");
4708
+ const configJsonPath = resolve17(projectRoot, "rig.config.json");
4709
+ const configExists = existsSync10(configTsPath) || existsSync10(configJsonPath);
4614
4710
  if (!options.privateStateOnly) {
4615
4711
  if (configExists && !options.repair) {
4616
4712
  if (context.outputMode !== "json")
@@ -4626,7 +4722,7 @@ async function runControlPlaneInit(context, options) {
4626
4722
  }
4627
4723
  ensureRigConfigPackageDependencies(projectRoot);
4628
4724
  }
4629
- writeFileSync5(resolve16(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4725
+ writeFileSync5(resolve17(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4630
4726
  `, "utf8");
4631
4727
  const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
4632
4728
  let uploadedSnapshot = null;
@@ -4648,10 +4744,15 @@ async function runControlPlaneInit(context, options) {
4648
4744
  const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
4649
4745
  if (token) {
4650
4746
  githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
4651
- setGitHubBearerTokenForCurrentProcess(token);
4747
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4748
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4652
4749
  if (serverKind === "remote") {
4653
- writeFileSync5(resolve16(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({ authenticated: true, source: authMethod === "gh" ? "gh" : "init-token", storedOnServer: true, selectedRepo: repo.slug, updatedAt: new Date().toISOString() }, null, 2)}
4654
- `, "utf8");
4750
+ writeRemoteGitHubAuthState(projectRoot, {
4751
+ source: authMethod === "gh" ? "gh" : "init-token",
4752
+ selectedRepo: repo.slug,
4753
+ apiSessionToken,
4754
+ authPayload: githubAuth
4755
+ });
4655
4756
  }
4656
4757
  } else if (authMethod === "device") {
4657
4758
  const payload = await requestServerJson(context, "/api/github/auth/device/start", {
@@ -4660,9 +4761,22 @@ async function runControlPlaneInit(context, options) {
4660
4761
  body: JSON.stringify({ repoSlug: repo.slug })
4661
4762
  });
4662
4763
  deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
4663
- const completed = await pollDeviceAuthOnce(context, deviceAuth.pollId);
4664
- if (completed)
4764
+ if (context.outputMode !== "json") {
4765
+ const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
4766
+ const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
4767
+ console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
4768
+ }
4769
+ const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
4770
+ if (completed) {
4771
+ const apiSessionToken = apiSessionTokenFrom(completed);
4772
+ if (apiSessionToken) {
4773
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken);
4774
+ if (serverKind === "remote") {
4775
+ writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken, authPayload: completed });
4776
+ }
4777
+ }
4665
4778
  deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
4779
+ }
4666
4780
  }
4667
4781
  let remoteCheckoutPreparation = null;
4668
4782
  if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
@@ -4682,6 +4796,12 @@ async function runControlPlaneInit(context, options) {
4682
4796
  });
4683
4797
  const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
4684
4798
  const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
4799
+ if (serverRootSwitch && token) {
4800
+ githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
4801
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4802
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4803
+ writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken, authPayload: githubAuth });
4804
+ }
4685
4805
  const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
4686
4806
  const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
4687
4807
  remote: true,
@@ -4791,7 +4911,7 @@ function parseInitOptions(args) {
4791
4911
  async function runInteractiveControlPlaneInit(context, prompts) {
4792
4912
  prompts.intro?.("Initialize a Rig control-plane project");
4793
4913
  const projectRoot = context.projectRoot;
4794
- const existingConfig = existsSync9(resolve16(projectRoot, "rig.config.ts")) || existsSync9(resolve16(projectRoot, "rig.config.json"));
4914
+ const existingConfig = existsSync10(resolve17(projectRoot, "rig.config.ts")) || existsSync10(resolve17(projectRoot, "rig.config.json"));
4795
4915
  let repair = false;
4796
4916
  let privateStateOnly = false;
4797
4917
  if (existingConfig) {
@@ -4891,7 +5011,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
4891
5011
  });
4892
5012
  const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
4893
5013
  const deviceAuth = details.deviceAuth && typeof details.deviceAuth === "object" && !Array.isArray(details.deviceAuth) ? details.deviceAuth : null;
4894
- const deviceMessage = deviceAuth ? ` GitHub device flow: open ${String(deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server")} and enter ${String(deviceAuth.user_code ?? "the returned user code")}.` : "";
5014
+ const deviceMessage = deviceAuth ? ` GitHub device flow: open ${String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server")} and enter ${String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code")}.` : "";
4895
5015
  prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
4896
5016
  return result;
4897
5017
  }
@@ -5053,8 +5173,8 @@ async function executeDoctor(context, args) {
5053
5173
  }
5054
5174
 
5055
5175
  // packages/cli/src/commands/_run-driver-helpers.ts
5056
- import { readFileSync as readFileSync6 } from "fs";
5057
- import { resolve as resolve17 } from "path";
5176
+ import { readFileSync as readFileSync7 } from "fs";
5177
+ import { resolve as resolve18 } from "path";
5058
5178
  import {
5059
5179
  appendJsonlRecord as appendJsonlRecord2,
5060
5180
  readAuthorityRun as readAuthorityRun2,
@@ -5074,7 +5194,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
5074
5194
  ...patch,
5075
5195
  updatedAt: new Date().toISOString()
5076
5196
  };
5077
- writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
5197
+ writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
5078
5198
  return next;
5079
5199
  }
5080
5200
  function touchAuthorityRun(projectRoot, runId) {
@@ -5082,21 +5202,21 @@ function touchAuthorityRun(projectRoot, runId) {
5082
5202
  if (!current) {
5083
5203
  return;
5084
5204
  }
5085
- writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
5205
+ writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
5086
5206
  ...current,
5087
5207
  updatedAt: new Date().toISOString()
5088
5208
  });
5089
5209
  }
5090
5210
  function appendRunTimeline(projectRoot, runId, value) {
5091
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
5211
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
5092
5212
  touchAuthorityRun(projectRoot, runId);
5093
5213
  }
5094
5214
  function appendRunLog(projectRoot, runId, value) {
5095
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
5215
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
5096
5216
  touchAuthorityRun(projectRoot, runId);
5097
5217
  }
5098
5218
  function appendRunAction(projectRoot, runId, value) {
5099
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
5219
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
5100
5220
  id: value.id,
5101
5221
  type: "action",
5102
5222
  actionType: value.actionType,
@@ -5167,7 +5287,7 @@ function buildRunPrompt(input) {
5167
5287
  })();
5168
5288
  const scopeText = (() => {
5169
5289
  try {
5170
- const parsed = JSON.parse(readFileSync6(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
5290
+ const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
5171
5291
  const entry = parsed[input.taskId] ?? {};
5172
5292
  const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
5173
5293
  const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
@@ -5280,8 +5400,8 @@ function renderSourceScopeValidation(task, validation) {
5280
5400
  }
5281
5401
 
5282
5402
  // packages/cli/src/commands/inspect.ts
5283
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
5284
- import { resolve as resolve18 } from "path";
5403
+ import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
5404
+ import { resolve as resolve19 } from "path";
5285
5405
  import {
5286
5406
  listAuthorityRuns as listAuthorityRuns2,
5287
5407
  readAuthorityRun as readAuthorityRun3,
@@ -5302,8 +5422,8 @@ async function executeInspect(context, args) {
5302
5422
  if (!latestRun) {
5303
5423
  throw new CliError2(`No runs found for ${requiredTask}.`);
5304
5424
  }
5305
- const logsPath = resolve18(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5306
- if (!existsSync10(logsPath)) {
5425
+ const logsPath = resolve19(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5426
+ if (!existsSync11(logsPath)) {
5307
5427
  throw new CliError2(`No logs found for run ${latestRun.runId}.`);
5308
5428
  }
5309
5429
  await context.runCommand(["cat", logsPath]);
@@ -5313,7 +5433,7 @@ async function executeInspect(context, args) {
5313
5433
  const { value: task, rest: remaining } = takeOption(rest, "--task");
5314
5434
  requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
5315
5435
  const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
5316
- const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync10(path));
5436
+ const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync11(path));
5317
5437
  if (!artifactRoot) {
5318
5438
  throw new CliError2(`No artifacts found for ${requiredTask}.`);
5319
5439
  }
@@ -5370,10 +5490,10 @@ async function executeInspect(context, args) {
5370
5490
  case "failures": {
5371
5491
  requireNoExtraArgs(rest, "bun run rig inspect failures");
5372
5492
  const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
5373
- if (!existsSync10(failed)) {
5493
+ if (!existsSync11(failed)) {
5374
5494
  console.log("No failures recorded.");
5375
5495
  } else {
5376
- process.stdout.write(readFileSync7(failed, "utf-8"));
5496
+ process.stdout.write(readFileSync8(failed, "utf-8"));
5377
5497
  }
5378
5498
  return { ok: true, group: "inspect", command };
5379
5499
  }
@@ -5390,11 +5510,11 @@ async function executeInspect(context, args) {
5390
5510
  return { ok: true, group: "inspect", command };
5391
5511
  case "audit": {
5392
5512
  requireNoExtraArgs(rest, "bun run rig inspect audit");
5393
- const auditPath = resolve18(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5394
- if (!existsSync10(auditPath)) {
5513
+ const auditPath = resolve19(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5514
+ if (!existsSync11(auditPath)) {
5395
5515
  console.log("No audit log found.");
5396
5516
  } else {
5397
- const lines = readFileSync7(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5517
+ const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5398
5518
  for (const line of lines) {
5399
5519
  console.log(line);
5400
5520
  }
@@ -6023,8 +6143,8 @@ async function executeRemote(context, args) {
6023
6143
  }
6024
6144
 
6025
6145
  // packages/cli/src/commands/run.ts
6026
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
6027
- import { resolve as resolve19 } from "path";
6146
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
6147
+ import { resolve as resolve20 } from "path";
6028
6148
  import { createInterface as createInterface2 } from "readline/promises";
6029
6149
  import {
6030
6150
  listAuthorityRuns as listAuthorityRuns3,
@@ -6038,6 +6158,7 @@ import {
6038
6158
  listOpenEpics,
6039
6159
  resolveDefaultEpic,
6040
6160
  runResume,
6161
+ runRestart,
6041
6162
  runStatus,
6042
6163
  runStop,
6043
6164
  startRun,
@@ -6146,6 +6267,17 @@ async function attachRunOperatorView(context, input) {
6146
6267
  }
6147
6268
 
6148
6269
  // packages/cli/src/commands/run.ts
6270
+ function normalizeRemoteRunDetails(payload) {
6271
+ const run = payload.run;
6272
+ if (!run || typeof run !== "object" || Array.isArray(run))
6273
+ return null;
6274
+ return {
6275
+ ...run,
6276
+ ...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
6277
+ ...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
6278
+ ...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
6279
+ };
6280
+ }
6149
6281
  function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
6150
6282
  if (noEpicPrompt) {
6151
6283
  return false;
@@ -6284,7 +6416,7 @@ async function executeRun(context, args) {
6284
6416
  if (!run.value) {
6285
6417
  throw new CliError2("run show requires --run <id>.");
6286
6418
  }
6287
- const record = readAuthorityRun4(context.projectRoot, run.value);
6419
+ const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
6288
6420
  if (!record) {
6289
6421
  throw new CliError2(`Run not found: ${run.value}`, 2);
6290
6422
  }
@@ -6303,7 +6435,7 @@ async function executeRun(context, args) {
6303
6435
  if (!run.value) {
6304
6436
  throw new CliError2("run timeline requires --run <id>.");
6305
6437
  }
6306
- const timelinePath = resolve19(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6438
+ const timelinePath = resolve20(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6307
6439
  const printEvents = () => {
6308
6440
  const events2 = readJsonlFile4(timelinePath);
6309
6441
  if (context.outputMode === "text") {
@@ -6315,12 +6447,12 @@ async function executeRun(context, args) {
6315
6447
  };
6316
6448
  const events = printEvents();
6317
6449
  if (follow.value && context.outputMode === "text") {
6318
- let lastLength = existsSync11(timelinePath) ? readFileSync8(timelinePath, "utf8").length : 0;
6450
+ let lastLength = existsSync12(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
6319
6451
  while (true) {
6320
6452
  await Bun.sleep(1000);
6321
- if (!existsSync11(timelinePath))
6453
+ if (!existsSync12(timelinePath))
6322
6454
  continue;
6323
- const next = readFileSync8(timelinePath, "utf8");
6455
+ const next = readFileSync9(timelinePath, "utf8");
6324
6456
  if (next.length <= lastLength)
6325
6457
  continue;
6326
6458
  const delta = next.slice(lastLength);
@@ -6465,6 +6597,20 @@ async function executeRun(context, args) {
6465
6597
  }
6466
6598
  return { ok: true, group: "run", command, details: resumed };
6467
6599
  }
6600
+ case "restart": {
6601
+ requireNoExtraArgs(rest, "bun run rig run restart");
6602
+ if (context.dryRun) {
6603
+ if (context.outputMode === "text") {
6604
+ console.log("[dry-run] rig run restart");
6605
+ }
6606
+ return { ok: true, group: "run", command };
6607
+ }
6608
+ const restarted = await runRestart(context.projectRoot, runtimeContext);
6609
+ if (context.outputMode === "text") {
6610
+ console.log(`Restarted run: ${restarted.runId}`);
6611
+ }
6612
+ return { ok: true, group: "run", command, details: restarted };
6613
+ }
6468
6614
  case "stop": {
6469
6615
  const runOption = takeOption(rest, "--run");
6470
6616
  const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
@@ -6522,7 +6668,7 @@ async function executeServer(context, args, options) {
6522
6668
  const authTokenResult = takeOption(pending, "--auth-token");
6523
6669
  pending = authTokenResult.rest;
6524
6670
  requireNoExtraArgs(pending, "bun run rig server start [--host <host>] [--port <n>] [--poll-ms <n>] [--auth-token <token>]");
6525
- const commandParts = ["bun", "run", "packages/server/src/server.ts", "start"];
6671
+ const commandParts = ["rig-server", "start"];
6526
6672
  if (hostResult.value) {
6527
6673
  commandParts.push("--host", hostResult.value);
6528
6674
  }
@@ -6545,7 +6691,7 @@ async function executeServer(context, args, options) {
6545
6691
  const eventResult = takeOption(pending, "--event");
6546
6692
  pending = eventResult.rest;
6547
6693
  requireNoExtraArgs(pending, "bun run rig server notify-test [--event <type>]");
6548
- const commandParts = ["bun", "run", "packages/server/src/server.ts", "notify-test"];
6694
+ const commandParts = ["rig-server", "notify-test"];
6549
6695
  if (eventResult.value) {
6550
6696
  commandParts.push("--event", eventResult.value);
6551
6697
  }
@@ -6606,10 +6752,10 @@ async function executeServer(context, args, options) {
6606
6752
  }
6607
6753
 
6608
6754
  // packages/cli/src/commands/task.ts
6609
- import { readFileSync as readFileSync9 } from "fs";
6755
+ import { readFileSync as readFileSync10 } from "fs";
6610
6756
  import { spawnSync as spawnSync4 } from "child_process";
6611
6757
  import { createInterface as createInterface4 } from "readline/promises";
6612
- import { resolve as resolve20 } from "path";
6758
+ import { resolve as resolve21 } from "path";
6613
6759
  import {
6614
6760
  taskArtifactDir,
6615
6761
  taskArtifacts,
@@ -6940,7 +7086,7 @@ async function executeTask(context, args, options) {
6940
7086
  const fileFlag = takeOption(rest.slice(1), "--file");
6941
7087
  let content;
6942
7088
  if (fileFlag.value) {
6943
- content = readFileSync9(resolve20(context.projectRoot, fileFlag.value), "utf-8");
7089
+ content = readFileSync10(resolve21(context.projectRoot, fileFlag.value), "utf-8");
6944
7090
  } else {
6945
7091
  content = await readStdin();
6946
7092
  }
@@ -7161,8 +7307,8 @@ async function executeTask(context, args, options) {
7161
7307
  }
7162
7308
 
7163
7309
  // packages/cli/src/commands/task-run-driver.ts
7164
- import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync10, statSync as statSync2 } from "fs";
7165
- import { resolve as resolve21 } from "path";
7310
+ import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
7311
+ import { resolve as resolve22 } from "path";
7166
7312
  import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
7167
7313
  import { createInterface as createLineInterface } from "readline";
7168
7314
  import { loadConfig as loadConfig2 } from "@rig/core/load-config";
@@ -7196,7 +7342,24 @@ import {
7196
7342
  commitRunChanges,
7197
7343
  runPrAutomation
7198
7344
  } from "@rig/runtime/control-plane/native/pr-automation";
7345
+ function looksLikeGitHubToken(value) {
7346
+ const token = value?.trim();
7347
+ if (!token)
7348
+ return false;
7349
+ return /^(gh[opusr]_|github_pat_)/.test(token);
7350
+ }
7351
+ function githubBridgeEnv(token) {
7352
+ const clean = token?.trim();
7353
+ if (!clean)
7354
+ return {};
7355
+ return {
7356
+ RIG_GITHUB_TOKEN: clean,
7357
+ GITHUB_TOKEN: clean,
7358
+ GH_TOKEN: clean
7359
+ };
7360
+ }
7199
7361
  function buildPiRigBridgeEnv(input) {
7362
+ const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
7200
7363
  return {
7201
7364
  RIG_PROJECT_ROOT: input.projectRoot,
7202
7365
  PROJECT_RIG_ROOT: input.projectRoot,
@@ -7206,7 +7369,7 @@ function buildPiRigBridgeEnv(input) {
7206
7369
  RIG_RUNTIME_ADAPTER: "pi",
7207
7370
  ...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
7208
7371
  ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
7209
- ...input.githubToken ? { RIG_GITHUB_TOKEN: input.githubToken } : {}
7372
+ ...githubBridgeEnv(githubToken)
7210
7373
  };
7211
7374
  }
7212
7375
  function runGitSync(cwd, args, input) {
@@ -7228,12 +7391,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
7228
7391
  return 0;
7229
7392
  let copied = 0;
7230
7393
  for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
7231
- const sourcePath = resolve21(sourceRoot, relativePath);
7232
- const targetPath = resolve21(targetRoot, relativePath);
7394
+ const sourcePath = resolve22(sourceRoot, relativePath);
7395
+ const targetPath = resolve22(targetRoot, relativePath);
7233
7396
  try {
7234
7397
  if (!statSync2(sourcePath).isFile())
7235
7398
  continue;
7236
- mkdirSync8(resolve21(targetPath, ".."), { recursive: true });
7399
+ mkdirSync8(resolve22(targetPath, ".."), { recursive: true });
7237
7400
  copyFileSync3(sourcePath, targetPath);
7238
7401
  copied += 1;
7239
7402
  } catch {}
@@ -7267,6 +7430,14 @@ function buildTaskRunReviewEnv(config) {
7267
7430
  ...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
7268
7431
  };
7269
7432
  }
7433
+ function buildDirtyBaselineHandshakeEnv(input) {
7434
+ if (input.baselineMode !== "dirty-snapshot")
7435
+ return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
7436
+ return {
7437
+ RIG_BASELINE_MODE: "dirty-snapshot",
7438
+ RIG_DIRTY_BASELINE_READY_FILE: resolve22(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
7439
+ };
7440
+ }
7270
7441
  function positiveInt(value, fallback) {
7271
7442
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
7272
7443
  }
@@ -7375,9 +7546,9 @@ function createCommandRunner(binary) {
7375
7546
  const stderrChunks = [];
7376
7547
  child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7377
7548
  child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7378
- return await new Promise((resolve22) => {
7379
- child.once("error", (error) => resolve22({ exitCode: 1, stderr: error.message }));
7380
- child.once("close", (code) => resolve22({
7549
+ return await new Promise((resolve23) => {
7550
+ child.once("error", (error) => resolve23({ exitCode: 1, stderr: error.message }));
7551
+ child.once("close", (code) => resolve23({
7381
7552
  exitCode: code ?? 1,
7382
7553
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
7383
7554
  stderr: Buffer.concat(stderrChunks).toString("utf8")
@@ -7573,7 +7744,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
7573
7744
  return null;
7574
7745
  }
7575
7746
  for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
7576
- const summary = readJsonFile3(resolve21(artifactDir, "validation-summary.json"), null);
7747
+ const summary = readJsonFile3(resolve22(artifactDir, "validation-summary.json"), null);
7577
7748
  if (!summary || summary.status !== "fail") {
7578
7749
  continue;
7579
7750
  }
@@ -7654,9 +7825,9 @@ function readTaskRunAcceptedArtifactState(input) {
7654
7825
  if (!input.taskId || !input.workspaceDir) {
7655
7826
  return { accepted: false, reason: null };
7656
7827
  }
7657
- const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7658
- const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7659
- const taskResultPath = resolve21(artifactDir, "task-result.json");
7828
+ const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7829
+ const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7830
+ const taskResultPath = resolve22(artifactDir, "task-result.json");
7660
7831
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7661
7832
  if (reviewStatus !== "APPROVED") {
7662
7833
  return { accepted: false, reason: null };
@@ -7693,12 +7864,12 @@ function resolveTaskRunRetryContext(input) {
7693
7864
  if (!input.taskId || !input.workspaceDir) {
7694
7865
  return { shouldRetry: false, failureDetail: null, nextPrompt: null };
7695
7866
  }
7696
- const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7697
- const reviewStatePath = resolve21(artifactDir, "review-state.json");
7698
- const reviewFeedbackPath = resolve21(artifactDir, "review-feedback.md");
7699
- const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7700
- const failedApproachesPath = resolve21(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7701
- const validationSummaryPath = resolve21(artifactDir, "validation-summary.json");
7867
+ const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7868
+ const reviewStatePath = resolve22(artifactDir, "review-state.json");
7869
+ const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
7870
+ const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7871
+ const failedApproachesPath = resolve22(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7872
+ const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
7702
7873
  const reviewState = readJsonFile3(reviewStatePath, null);
7703
7874
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7704
7875
  const reviewRejected = isTaskRunReviewRejected(reviewState);
@@ -7753,11 +7924,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
7753
7924
  return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
7754
7925
  }
7755
7926
  function readTaskRunReviewStatus(reviewStatusPath) {
7756
- if (!existsSync12(reviewStatusPath)) {
7927
+ if (!existsSync13(reviewStatusPath)) {
7757
7928
  return null;
7758
7929
  }
7759
7930
  try {
7760
- const status = readFileSync10(reviewStatusPath, "utf8").trim().toUpperCase();
7931
+ const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
7761
7932
  return status === "APPROVED" || status === "REJECTED" ? status : null;
7762
7933
  } catch {
7763
7934
  return null;
@@ -7840,8 +8011,11 @@ function stringArrayField(record, key) {
7840
8011
  }
7841
8012
  async function executeRigOwnedTaskRun(context, input) {
7842
8013
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
8014
+ const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
8015
+ const resumeMode = process.env.RIG_RUN_RESUME === "1";
8016
+ const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
7843
8017
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
7844
- const prompt = buildRunPrompt({
8018
+ let prompt = buildRunPrompt({
7845
8019
  projectRoot: context.projectRoot,
7846
8020
  taskId: input.taskId,
7847
8021
  fallbackTitle: input.title,
@@ -7895,14 +8069,14 @@ async function executeRigOwnedTaskRun(context, input) {
7895
8069
  taskId: runtimeTaskId,
7896
8070
  createdAt: startedAt,
7897
8071
  runtimeAdapter: input.runtimeAdapter,
7898
- status: "created"
8072
+ status: resumeMode ? "preparing" : "created"
7899
8073
  });
7900
8074
  patchAuthorityRun(context.projectRoot, input.runId, {
7901
8075
  status: "preparing",
7902
8076
  startedAt,
7903
8077
  completedAt: null,
7904
8078
  errorText: null,
7905
- artifactRoot: null,
8079
+ artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
7906
8080
  runtimeAdapter: input.runtimeAdapter,
7907
8081
  runtimeMode: input.runtimeMode,
7908
8082
  interactionMode: input.interactionMode,
@@ -7918,9 +8092,9 @@ async function executeRigOwnedTaskRun(context, input) {
7918
8092
  detail: input.taskId ?? input.title ?? runtimeTaskId
7919
8093
  });
7920
8094
  appendRunLog(context.projectRoot, input.runId, {
7921
- id: `log:${input.runId}:start`,
7922
- title: "Rig task run started",
7923
- detail: input.taskId ?? input.title ?? runtimeTaskId,
8095
+ id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
8096
+ title: resumeMode ? "Rig task run resumed" : "Rig task run started",
8097
+ detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
7924
8098
  tone: "info",
7925
8099
  status: "preparing",
7926
8100
  createdAt: startedAt
@@ -7941,7 +8115,22 @@ async function executeRigOwnedTaskRun(context, input) {
7941
8115
  const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
7942
8116
  const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
7943
8117
  const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
7944
- patchAuthorityRun(context.projectRoot, input.runId, { planning: planningClassification });
8118
+ const planningArtifactPath = resolve22("artifacts", runtimeTaskId, "implementation-plan.md");
8119
+ const persistedPlanning = {
8120
+ ...planningClassification,
8121
+ classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
8122
+ artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
8123
+ classifiedAt: new Date().toISOString()
8124
+ };
8125
+ mkdirSync8(resolve22(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
8126
+ writeFileSync6(resolve22(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
8127
+ `, "utf8");
8128
+ patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
8129
+ prompt = `${prompt}
8130
+
8131
+ Rig planning classification:
8132
+ ${JSON.stringify(persistedPlanning, null, 2)}
8133
+ ${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."}`;
7945
8134
  if (input.runtimeAdapter === "pi") {
7946
8135
  for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
7947
8136
  appendPiStageLog({
@@ -7983,11 +8172,11 @@ async function executeRigOwnedTaskRun(context, input) {
7983
8172
  let reviewAction;
7984
8173
  let verificationStarted = false;
7985
8174
  let reviewStarted = false;
7986
- let latestRuntimeWorkspace = null;
7987
- let latestSessionDir = null;
7988
- let latestLogsDir = null;
8175
+ let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
8176
+ let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve22(existingRunRecord.sessionPath, "..") : null;
8177
+ let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
7989
8178
  let latestProviderCommand = null;
7990
- let latestRuntimeBranch = null;
8179
+ let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
7991
8180
  let snapshotSidecarPromise = null;
7992
8181
  let dirtyBaselineApplied = false;
7993
8182
  const childEnv = {
@@ -8005,9 +8194,14 @@ async function executeRigOwnedTaskRun(context, input) {
8005
8194
  RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
8006
8195
  RIG_SERVER_RUN_ID: input.runId
8007
8196
  },
8008
- ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
8197
+ ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
8198
+ ...resumeMode ? {
8199
+ RIG_RUN_RESUME: "1",
8200
+ RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
8201
+ } : {}
8009
8202
  };
8010
8203
  Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
8204
+ Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
8011
8205
  const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
8012
8206
  const maxAttempts = automationLimits.maxValidationAttempts;
8013
8207
  const promoteToValidating = (detail) => {
@@ -8062,22 +8256,29 @@ async function executeRigOwnedTaskRun(context, input) {
8062
8256
  patchAuthorityRun(context.projectRoot, input.runId, {
8063
8257
  status: "running",
8064
8258
  worktreePath: latestRuntimeWorkspace,
8065
- artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve21(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8259
+ artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve22(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8066
8260
  logRoot: latestLogsDir,
8067
- sessionPath: latestSessionDir ? resolve21(latestSessionDir, "session.json") : null,
8068
- sessionLogPath: latestLogsDir ? resolve21(latestLogsDir, "agent-stdout.log") : null,
8261
+ sessionPath: latestSessionDir ? resolve22(latestSessionDir, "session.json") : null,
8262
+ sessionLogPath: latestLogsDir ? resolve22(latestLogsDir, "agent-stdout.log") : null,
8069
8263
  branch: runtimeId
8070
8264
  });
8071
8265
  if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
8072
8266
  dirtyBaselineApplied = true;
8073
8267
  const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
8268
+ const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
8269
+ if (readyFile) {
8270
+ mkdirSync8(resolve22(readyFile, ".."), { recursive: true });
8271
+ writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
8272
+ `, "utf8");
8273
+ }
8074
8274
  appendRunLog(context.projectRoot, input.runId, {
8075
8275
  id: `log:${input.runId}:dirty-baseline`,
8076
8276
  title: "Dirty baseline snapshot",
8077
8277
  detail: dirty.detail,
8078
8278
  tone: dirty.applied ? "tool" : "info",
8079
8279
  status: dirty.applied ? "completed" : "skipped",
8080
- createdAt: new Date().toISOString()
8280
+ createdAt: new Date().toISOString(),
8281
+ payload: readyFile ? { readyFile } : undefined
8081
8282
  });
8082
8283
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
8083
8284
  }
@@ -8303,7 +8504,36 @@ async function executeRigOwnedTaskRun(context, input) {
8303
8504
  let reviewFailureDetail = null;
8304
8505
  const stderrLines = [];
8305
8506
  const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
8306
- for (let attempt = 1;attempt <= maxAttempts; attempt += 1) {
8507
+ if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
8508
+ appendRunLog(context.projectRoot, input.runId, {
8509
+ id: `log:${input.runId}:resume-closeout-phase`,
8510
+ title: "Resume continuing from closeout phase",
8511
+ detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
8512
+ tone: "info",
8513
+ status: resumePreviousStatus,
8514
+ createdAt: new Date().toISOString()
8515
+ });
8516
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
8517
+ exit = { code: 0, signal: null };
8518
+ } else if (resumeMode && latestRuntimeWorkspace) {
8519
+ const acceptedArtifactState = readTaskRunAcceptedArtifactState({
8520
+ taskId: input.taskId ?? runtimeTaskId,
8521
+ workspaceDir: latestRuntimeWorkspace
8522
+ });
8523
+ if (acceptedArtifactState.accepted) {
8524
+ appendRunLog(context.projectRoot, input.runId, {
8525
+ id: `log:${input.runId}:resume-accepted-artifacts`,
8526
+ title: "Resume found accepted artifacts; continuing closeout",
8527
+ detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
8528
+ tone: "info",
8529
+ status: "validating",
8530
+ createdAt: new Date().toISOString()
8531
+ });
8532
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
8533
+ exit = { code: 0, signal: null };
8534
+ }
8535
+ }
8536
+ for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
8307
8537
  const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
8308
8538
  const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
8309
8539
  cwd: context.projectRoot,
@@ -8344,7 +8574,7 @@ async function executeRigOwnedTaskRun(context, input) {
8344
8574
  let acceptedArtifactObservedAt = null;
8345
8575
  let acceptedArtifactPollTimer = null;
8346
8576
  let acceptedArtifactKillTimer = null;
8347
- const attemptExit = await new Promise((resolve22) => {
8577
+ const attemptExit = await new Promise((resolve23) => {
8348
8578
  let settled = false;
8349
8579
  const settle = (result) => {
8350
8580
  if (settled)
@@ -8352,7 +8582,7 @@ async function executeRigOwnedTaskRun(context, input) {
8352
8582
  settled = true;
8353
8583
  if (acceptedArtifactPollTimer)
8354
8584
  clearInterval(acceptedArtifactPollTimer);
8355
- resolve22(result);
8585
+ resolve23(result);
8356
8586
  };
8357
8587
  const pollAcceptedArtifacts = () => {
8358
8588
  const artifactState = readTaskRunAcceptedArtifactState({
@@ -8549,6 +8779,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8549
8779
  });
8550
8780
  throw new CliError2(terminalFailureDetail, exit.code ?? 1);
8551
8781
  }
8782
+ if (planningClassification.planningRequired) {
8783
+ const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
8784
+ const expectedPlanPath = resolve22(planWorkspace, planningArtifactPath);
8785
+ if (!existsSync13(expectedPlanPath)) {
8786
+ const failedAt = new Date().toISOString();
8787
+ const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
8788
+ patchAuthorityRun(context.projectRoot, input.runId, {
8789
+ status: "needs_attention",
8790
+ completedAt: failedAt,
8791
+ errorText: failureDetail
8792
+ });
8793
+ appendRunLog(context.projectRoot, input.runId, {
8794
+ id: `log:${input.runId}:plan-artifact-missing`,
8795
+ title: "Required plan artifact missing",
8796
+ detail: failureDetail,
8797
+ tone: "error",
8798
+ status: "needs_attention",
8799
+ createdAt: failedAt
8800
+ });
8801
+ emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
8802
+ throw new CliError2(failureDetail, 1);
8803
+ }
8804
+ }
8552
8805
  const runPiPrFeedbackFix = async (message2) => {
8553
8806
  appendPiStageLog({
8554
8807
  projectRoot: context.projectRoot,
@@ -8606,9 +8859,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8606
8859
  });
8607
8860
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
8608
8861
  });
8609
- const exitCode = await new Promise((resolve22) => {
8610
- child.once("error", () => resolve22(1));
8611
- child.once("close", (code) => resolve22(code ?? 1));
8862
+ const exitCode = await new Promise((resolve23) => {
8863
+ child.once("error", () => resolve23(1));
8864
+ child.once("close", (code) => resolve23(code ?? 1));
8612
8865
  });
8613
8866
  if (exitCode !== 0) {
8614
8867
  throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
@@ -8727,8 +8980,8 @@ async function executeTest(context, args) {
8727
8980
  }
8728
8981
 
8729
8982
  // packages/cli/src/commands/setup.ts
8730
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "fs";
8731
- import { resolve as resolve22 } from "path";
8983
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
8984
+ import { resolve as resolve23 } from "path";
8732
8985
  import { createPluginHost } from "@rig/core";
8733
8986
  import {
8734
8987
  isSupportedBunVersion as isSupportedBunVersion2,
@@ -8791,9 +9044,9 @@ function runSetupInit(projectRoot) {
8791
9044
  mkdirSync9(stateDir, { recursive: true });
8792
9045
  mkdirSync9(logsDir, { recursive: true });
8793
9046
  mkdirSync9(artifactsDir, { recursive: true });
8794
- const failuresPath = resolve22(stateDir, "failed_approaches.md");
8795
- if (!existsSync13(failuresPath)) {
8796
- writeFileSync6(failuresPath, `# Failed Approaches
9047
+ const failuresPath = resolve23(stateDir, "failed_approaches.md");
9048
+ if (!existsSync14(failuresPath)) {
9049
+ writeFileSync7(failuresPath, `# Failed Approaches
8797
9050
 
8798
9051
  `, "utf-8");
8799
9052
  }
@@ -8810,18 +9063,18 @@ async function runSetupCheck(projectRoot) {
8810
9063
  }
8811
9064
  async function runSetupPreflight(projectRoot) {
8812
9065
  await runSetupCheck(projectRoot);
8813
- const validationRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
8814
- if (existsSync13(validationRoot)) {
9066
+ const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
9067
+ if (existsSync14(validationRoot)) {
8815
9068
  const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
8816
9069
  for (const validator of validators) {
8817
- const script = resolve22(validationRoot, validator.name, "validate.sh");
8818
- if (existsSync13(script)) {
9070
+ const script = resolve23(validationRoot, validator.name, "validate.sh");
9071
+ if (existsSync14(script)) {
8819
9072
  console.log(`OK: validator script ${script}`);
8820
9073
  }
8821
9074
  }
8822
9075
  }
8823
- const hooksRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
8824
- if (existsSync13(hooksRoot)) {
9076
+ const hooksRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
9077
+ if (existsSync14(hooksRoot)) {
8825
9078
  const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
8826
9079
  for (const hook of hooks) {
8827
9080
  console.log(`OK: hook ${hook}`);
@@ -9198,8 +9451,8 @@ async function executeGroup(context, group, args) {
9198
9451
  }
9199
9452
  }
9200
9453
  // packages/cli/src/launcher.ts
9201
- import { existsSync as existsSync14 } from "fs";
9202
- import { resolve as resolve23 } from "path";
9454
+ import { existsSync as existsSync15 } from "fs";
9455
+ import { basename as basename2, resolve as resolve24 } from "path";
9203
9456
  import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
9204
9457
  import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
9205
9458
  function parsePolicyMode(value) {
@@ -9212,7 +9465,7 @@ function parsePolicyMode(value) {
9212
9465
  throw new Error(`Invalid --policy-mode value: ${value}. Use off|observe|enforce.`);
9213
9466
  }
9214
9467
  function hasRigProjectMarker(candidate) {
9215
- 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"));
9468
+ 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"));
9216
9469
  }
9217
9470
  function resolveProjectRoot({
9218
9471
  envProjectRoot,
@@ -9221,17 +9474,19 @@ function resolveProjectRoot({
9221
9474
  cwd = process.cwd()
9222
9475
  }) {
9223
9476
  if (envProjectRoot) {
9224
- return resolve23(cwd, envProjectRoot);
9477
+ return resolve24(cwd, envProjectRoot);
9225
9478
  }
9226
9479
  const fallbackImportDir = importDir ?? cwd;
9227
- const candidates = [cwd, resolve23(execPath, "..", ".."), resolve23(fallbackImportDir, "..")];
9480
+ const execName = basename2(execPath).toLowerCase();
9481
+ const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve24(execPath, "..", "..")] : [];
9482
+ const candidates = [cwd, ...execCandidates, resolve24(fallbackImportDir, "..")];
9228
9483
  for (const candidate of candidates) {
9229
9484
  const nearest = resolveNearestRigProjectRoot(candidate);
9230
9485
  if (hasRigProjectMarker(nearest)) {
9231
9486
  return nearest;
9232
9487
  }
9233
9488
  }
9234
- return resolve23(cwd);
9489
+ return resolve24(cwd);
9235
9490
  }
9236
9491
  function normalizeCliErrorCode(message2, isCliError) {
9237
9492
  if (message2.startsWith("Invalid --policy-mode value:")) {
@@ -9298,7 +9553,7 @@ async function runRigCli(module, options = {}) {
9298
9553
  runId: context.runId,
9299
9554
  outcome,
9300
9555
  eventsFile: context.eventBus.getEventsFile(),
9301
- policyFile: resolve23(projectRoot, "rig", "policy", "policy.json"),
9556
+ policyFile: resolve24(projectRoot, "rig", "policy", "policy.json"),
9302
9557
  policyMode: context.policyMode ?? policyMode ?? module.loadPolicy(projectRoot).mode
9303
9558
  }, null, 2));
9304
9559
  }