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

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
@@ -2671,6 +2671,8 @@ function resolveSelectedConnection(projectRoot, options = {}) {
2671
2671
 
2672
2672
  // packages/cli/src/commands/_server-client.ts
2673
2673
  import { spawnSync } from "child_process";
2674
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
2675
+ import { resolve as resolve9 } from "path";
2674
2676
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
2675
2677
  var cachedGitHubBearerToken;
2676
2678
  function cleanToken(value) {
@@ -2680,9 +2682,25 @@ function cleanToken(value) {
2680
2682
  function setGitHubBearerTokenForCurrentProcess(token) {
2681
2683
  cachedGitHubBearerToken = cleanToken(token ?? undefined);
2682
2684
  }
2683
- function readGitHubBearerTokenForRemote() {
2685
+ function readPrivateRemoteSessionToken(projectRoot) {
2686
+ const path = resolve9(projectRoot, ".rig", "state", "github-auth.json");
2687
+ if (!existsSync5(path))
2688
+ return null;
2689
+ try {
2690
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
2691
+ return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
2692
+ } catch {
2693
+ return null;
2694
+ }
2695
+ }
2696
+ function readGitHubBearerTokenForRemote(projectRoot) {
2684
2697
  if (cachedGitHubBearerToken !== undefined)
2685
2698
  return cachedGitHubBearerToken;
2699
+ const privateSession = readPrivateRemoteSessionToken(projectRoot);
2700
+ if (privateSession) {
2701
+ cachedGitHubBearerToken = privateSession;
2702
+ return cachedGitHubBearerToken;
2703
+ }
2686
2704
  const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
2687
2705
  if (envToken) {
2688
2706
  cachedGitHubBearerToken = envToken;
@@ -2702,7 +2720,7 @@ async function ensureServerForCli(projectRoot) {
2702
2720
  if (selected?.connection.kind === "remote") {
2703
2721
  return {
2704
2722
  baseUrl: selected.connection.baseUrl,
2705
- authToken: readGitHubBearerTokenForRemote(),
2723
+ authToken: readGitHubBearerTokenForRemote(projectRoot),
2706
2724
  connectionKind: "remote"
2707
2725
  };
2708
2726
  }
@@ -2809,7 +2827,7 @@ async function postGitHubTokenViaServer(context, token, options = {}) {
2809
2827
  const payload = await requestServerJson(context, "/api/github/auth/token", {
2810
2828
  method: "POST",
2811
2829
  headers: { "content-type": "application/json" },
2812
- body: JSON.stringify({ token, selectedRepo: options.selectedRepo })
2830
+ body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
2813
2831
  });
2814
2832
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2815
2833
  }
@@ -2830,7 +2848,7 @@ async function registerProjectViaServer(context, input) {
2830
2848
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2831
2849
  }
2832
2850
  function sleep(ms) {
2833
- return new Promise((resolve9) => setTimeout(resolve9, ms));
2851
+ return new Promise((resolve10) => setTimeout(resolve10, ms));
2834
2852
  }
2835
2853
  async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
2836
2854
  const switched = await requestServerJson(context, "/api/server/project-root", {
@@ -2921,9 +2939,9 @@ async function submitTaskRunViaServer(context, input) {
2921
2939
  }
2922
2940
 
2923
2941
  // packages/cli/src/commands/_pi-install.ts
2924
- import { existsSync as existsSync5, readFileSync as readFileSync3, rmSync as rmSync3 } from "fs";
2942
+ import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
2925
2943
  import { homedir as homedir3 } from "os";
2926
- import { resolve as resolve9 } from "path";
2944
+ import { resolve as resolve10 } from "path";
2927
2945
  var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
2928
2946
  var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
2929
2947
  export { default } from '@rig/pi-rig';
@@ -2938,11 +2956,11 @@ async function defaultCommandRunner(command, options = {}) {
2938
2956
  return { exitCode, stdout, stderr };
2939
2957
  }
2940
2958
  function resolvePiRigExtensionPath(homeDir) {
2941
- return resolve9(homeDir, ".pi", "agent", "extensions", "pi-rig");
2959
+ return resolve10(homeDir, ".pi", "agent", "extensions", "pi-rig");
2942
2960
  }
2943
- function resolvePiRigPackageSource(projectRoot, exists = existsSync5) {
2944
- const localPackage = resolve9(projectRoot, "packages", "pi-rig");
2945
- if (exists(resolve9(localPackage, "package.json")))
2961
+ function resolvePiRigPackageSource(projectRoot, exists = existsSync6) {
2962
+ const localPackage = resolve10(projectRoot, "packages", "pi-rig");
2963
+ if (exists(resolve10(localPackage, "package.json")))
2946
2964
  return localPackage;
2947
2965
  return `npm:${PI_RIG_PACKAGE_NAME}`;
2948
2966
  }
@@ -2993,13 +3011,13 @@ async function ensurePiBinaryAvailable(input) {
2993
3011
  ...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
2994
3012
  };
2995
3013
  }
2996
- function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync5) {
3014
+ function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync6) {
2997
3015
  const extensionPath = resolvePiRigExtensionPath(homeDir);
2998
- const indexPath = resolve9(extensionPath, "index.ts");
3016
+ const indexPath = resolve10(extensionPath, "index.ts");
2999
3017
  if (!exists(indexPath))
3000
3018
  return;
3001
3019
  try {
3002
- const content = readFileSync3(indexPath, "utf8");
3020
+ const content = readFileSync4(indexPath, "utf8");
3003
3021
  if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
3004
3022
  rmSync3(extensionPath, { recursive: true, force: true });
3005
3023
  }
@@ -3015,13 +3033,13 @@ async function checkPiRigInstall(input = {}) {
3015
3033
  piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
3016
3034
  };
3017
3035
  }
3018
- const exists = input.exists ?? existsSync5;
3036
+ const exists = input.exists ?? existsSync6;
3019
3037
  const runner = input.commandRunner ?? defaultCommandRunner;
3020
3038
  const piResult = await safeRun(runner, ["pi", "--version"]);
3021
3039
  const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
3022
3040
  const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
3023
3041
  ${piListResult.stderr}`);
3024
- const legacyBridge = exists(resolve9(extensionPath, "index.ts"));
3042
+ const legacyBridge = exists(resolve10(extensionPath, "index.ts"));
3025
3043
  const hasPiRig = listedPiRig;
3026
3044
  return {
3027
3045
  extensionPath,
@@ -3359,7 +3377,7 @@ async function executeQueue(context, args) {
3359
3377
  }
3360
3378
 
3361
3379
  // packages/cli/src/commands/agent.ts
3362
- import { resolve as resolve11 } from "path";
3380
+ import { resolve as resolve12 } from "path";
3363
3381
  import {
3364
3382
  agentId,
3365
3383
  cleanupAgentRuntime,
@@ -3369,8 +3387,8 @@ import {
3369
3387
  } from "@rig/runtime/control-plane/runtime/isolation";
3370
3388
 
3371
3389
  // packages/cli/src/commands/_authority-runs.ts
3372
- import { existsSync as existsSync6 } from "fs";
3373
- import { resolve as resolve10 } from "path";
3390
+ import { existsSync as existsSync7 } from "fs";
3391
+ import { resolve as resolve11 } from "path";
3374
3392
  import {
3375
3393
  readAuthorityRun,
3376
3394
  readJsonlFile as readJsonlFile2,
@@ -3392,8 +3410,8 @@ function normalizeRuntimeAdapter(value) {
3392
3410
  return "claude-code";
3393
3411
  }
3394
3412
  function readLatestBeadRecord(projectRoot, taskId) {
3395
- const issuesPath = resolve10(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3396
- if (!existsSync6(issuesPath)) {
3413
+ const issuesPath = resolve11(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3414
+ if (!existsSync7(issuesPath)) {
3397
3415
  return null;
3398
3416
  }
3399
3417
  let latest = null;
@@ -3460,7 +3478,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3460
3478
  } else if ("errorText" in next) {
3461
3479
  delete next.errorText;
3462
3480
  }
3463
- writeJsonFile3(resolve10(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3481
+ writeJsonFile3(resolve11(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3464
3482
  return next;
3465
3483
  }
3466
3484
 
@@ -3581,10 +3599,10 @@ async function executeAgent(context, args) {
3581
3599
  status: "running",
3582
3600
  startedAt: createdAt,
3583
3601
  worktreePath: runtime.workspaceDir,
3584
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3602
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3585
3603
  logRoot: runtime.logsDir,
3586
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3587
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3604
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3605
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3588
3606
  pid: process.pid
3589
3607
  });
3590
3608
  const result = await runInAgentRuntime({
@@ -3604,10 +3622,10 @@ async function executeAgent(context, args) {
3604
3622
  startedAt: createdAt,
3605
3623
  completedAt: failedAt,
3606
3624
  worktreePath: runtime.workspaceDir,
3607
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3625
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3608
3626
  logRoot: runtime.logsDir,
3609
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3610
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3627
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3628
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3611
3629
  pid: process.pid,
3612
3630
  errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
3613
3631
  });
@@ -3624,10 +3642,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3624
3642
  startedAt: createdAt,
3625
3643
  completedAt,
3626
3644
  worktreePath: runtime.workspaceDir,
3627
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3645
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3628
3646
  logRoot: runtime.logsDir,
3629
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3630
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3647
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3648
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3631
3649
  pid: process.pid
3632
3650
  });
3633
3651
  return {
@@ -3701,7 +3719,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3701
3719
  import {
3702
3720
  chmodSync,
3703
3721
  copyFileSync as copyFileSync2,
3704
- existsSync as existsSync7,
3722
+ existsSync as existsSync8,
3705
3723
  mkdirSync as mkdirSync6,
3706
3724
  readdirSync,
3707
3725
  readlinkSync,
@@ -3711,7 +3729,7 @@ import {
3711
3729
  unlinkSync
3712
3730
  } from "fs";
3713
3731
  import { homedir as homedir4 } from "os";
3714
- import { resolve as resolve12 } from "path";
3732
+ import { resolve as resolve13 } from "path";
3715
3733
  import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
3716
3734
  import {
3717
3735
  computeRuntimeImageFingerprint,
@@ -3730,13 +3748,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
3730
3748
 
3731
3749
  // packages/cli/src/commands/dist.ts
3732
3750
  function collectRigValidatorBuildTargets(input) {
3733
- const validatorsRoot = resolve12(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3734
- if (!existsSync7(validatorsRoot))
3751
+ const validatorsRoot = resolve13(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3752
+ if (!existsSync8(validatorsRoot))
3735
3753
  return [];
3736
3754
  const targets = [];
3737
3755
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3738
3756
  for (const category of categories) {
3739
- const categoryDir = resolve12(validatorsRoot, category.name);
3757
+ const categoryDir = resolve13(validatorsRoot, category.name);
3740
3758
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3741
3759
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3742
3760
  continue;
@@ -3745,7 +3763,7 @@ function collectRigValidatorBuildTargets(input) {
3745
3763
  continue;
3746
3764
  targets.push({
3747
3765
  source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
3748
- dest: resolve12(input.imageDir, `bin/validators/${category.name}-${check}`),
3766
+ dest: resolve13(input.imageDir, `bin/validators/${category.name}-${check}`),
3749
3767
  cwd: input.hostProjectRoot
3750
3768
  });
3751
3769
  }
@@ -3754,16 +3772,16 @@ function collectRigValidatorBuildTargets(input) {
3754
3772
  }
3755
3773
  async function findLatestDistBinary(projectRoot) {
3756
3774
  const distRoot = resolveControlPlaneHostDistDir(projectRoot);
3757
- if (!existsSync7(distRoot)) {
3775
+ if (!existsSync8(distRoot)) {
3758
3776
  return null;
3759
3777
  }
3760
3778
  const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
3761
3779
  name: entry.name,
3762
- mtimeMs: statSync(resolve12(distRoot, entry.name)).mtimeMs
3780
+ mtimeMs: statSync(resolve13(distRoot, entry.name)).mtimeMs
3763
3781
  })).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
3764
3782
  for (const { name } of entries) {
3765
- const candidate = resolve12(distRoot, name, "bin", "rig");
3766
- if (existsSync7(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3783
+ const candidate = resolve13(distRoot, name, "bin", "rig");
3784
+ if (existsSync8(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3767
3785
  return candidate;
3768
3786
  }
3769
3787
  }
@@ -3775,7 +3793,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
3775
3793
  async function runDistDoctor(projectRoot) {
3776
3794
  const bunPath = Bun.which("bun");
3777
3795
  const rigPath = Bun.which("rig");
3778
- const userBinDir = resolve12(homedir4(), ".local/bin");
3796
+ const userBinDir = resolve13(homedir4(), ".local/bin");
3779
3797
  const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
3780
3798
  let rigRunnable = false;
3781
3799
  if (rigPath) {
@@ -3823,15 +3841,15 @@ async function executeDist(context, args) {
3823
3841
  let source = await findLatestDistBinary(context.projectRoot);
3824
3842
  let buildDir = null;
3825
3843
  if (!source) {
3826
- buildDir = resolve12(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3844
+ buildDir = resolve13(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3827
3845
  await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
3828
- source = resolve12(buildDir, "bin", "rig");
3846
+ source = resolve13(buildDir, "bin", "rig");
3829
3847
  }
3830
- if (!existsSync7(source)) {
3848
+ if (!existsSync8(source)) {
3831
3849
  throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
3832
3850
  }
3833
- const installedPath = resolve12(installDir, "rig");
3834
- if (existsSync7(installedPath)) {
3851
+ const installedPath = resolve13(installDir, "rig");
3852
+ if (existsSync8(installedPath)) {
3835
3853
  unlinkSync(installedPath);
3836
3854
  }
3837
3855
  copyFileSync2(source, installedPath);
@@ -3873,22 +3891,22 @@ async function executeDist(context, args) {
3873
3891
  requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
3874
3892
  const fp = await computeRuntimeImageFingerprint(context.projectRoot);
3875
3893
  const currentId = computeRuntimeImageId(fp);
3876
- const imagesDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3894
+ const imagesDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3877
3895
  mkdirSync6(imagesDir, { recursive: true });
3878
3896
  let pruned = 0;
3879
3897
  for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
3880
3898
  if (entry.isDirectory() && entry.name !== currentId) {
3881
- rmSync4(resolve12(imagesDir, entry.name), { recursive: true, force: true });
3899
+ rmSync4(resolve13(imagesDir, entry.name), { recursive: true, force: true });
3882
3900
  pruned++;
3883
3901
  }
3884
3902
  }
3885
3903
  if (pruned > 0 && context.outputMode === "text") {
3886
3904
  console.log(`Pruned ${pruned} stale image(s).`);
3887
3905
  }
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 });
3906
+ const imageDir = resolve13(imagesDir, currentId);
3907
+ mkdirSync6(resolve13(imageDir, "bin/hooks"), { recursive: true });
3908
+ mkdirSync6(resolve13(imageDir, "bin/plugins"), { recursive: true });
3909
+ mkdirSync6(resolve13(imageDir, "bin/validators"), { recursive: true });
3892
3910
  const hookNames = [
3893
3911
  "scope-guard",
3894
3912
  "import-guard",
@@ -3902,25 +3920,25 @@ async function executeDist(context, args) {
3902
3920
  ];
3903
3921
  const targets = [];
3904
3922
  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 });
3923
+ targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve13(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
3924
+ targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
3907
3925
  for (const hookName of hookNames) {
3908
3926
  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 });
3927
+ targets.push({ source: src, dest: resolve13(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
3928
+ targets.push({ source: src, dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
3911
3929
  }
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");
3930
+ const pluginsDir = resolve13(context.projectRoot, "rig/plugins");
3931
+ const binPluginsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
3932
+ const validatorsRoot = resolve13(hostProjectRoot, "packages/runtime/src/control-plane/validators");
3933
+ const binValidatorsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
3916
3934
  mkdirSync6(binPluginsDir, { recursive: true });
3917
3935
  mkdirSync6(binValidatorsDir, { recursive: true });
3918
- if (existsSync7(pluginsDir)) {
3936
+ if (existsSync8(pluginsDir)) {
3919
3937
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3920
3938
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3921
3939
  if (!m)
3922
3940
  continue;
3923
- targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve12(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3941
+ targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve13(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3924
3942
  }
3925
3943
  }
3926
3944
  targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
@@ -3931,17 +3949,17 @@ async function executeDist(context, args) {
3931
3949
  const isValidator = dest.includes("/bin/validators/");
3932
3950
  await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
3933
3951
  }
3934
- if (existsSync7(pluginsDir)) {
3952
+ if (existsSync8(pluginsDir)) {
3935
3953
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3936
3954
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3937
3955
  if (!m)
3938
3956
  continue;
3939
3957
  const pluginName = m[1];
3940
- const imageBin = resolve12(imageDir, `bin/plugins/${pluginName}`);
3958
+ const imageBin = resolve13(imageDir, `bin/plugins/${pluginName}`);
3941
3959
  if (!pluginName)
3942
3960
  continue;
3943
- const symlinkPath = resolve12(binPluginsDir, pluginName);
3944
- if (existsSync7(imageBin)) {
3961
+ const symlinkPath = resolve13(binPluginsDir, pluginName);
3962
+ if (existsSync8(imageBin)) {
3945
3963
  try {
3946
3964
  unlinkSync(symlinkPath);
3947
3965
  } catch {}
@@ -3949,10 +3967,10 @@ async function executeDist(context, args) {
3949
3967
  }
3950
3968
  }
3951
3969
  }
3952
- if (existsSync7(validatorsRoot)) {
3970
+ if (existsSync8(validatorsRoot)) {
3953
3971
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3954
3972
  for (const category of categories) {
3955
- const categoryDir = resolve12(validatorsRoot, category.name);
3973
+ const categoryDir = resolve13(validatorsRoot, category.name);
3956
3974
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3957
3975
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3958
3976
  continue;
@@ -3960,9 +3978,9 @@ async function executeDist(context, args) {
3960
3978
  if (!check || check === "index" || check === "shared")
3961
3979
  continue;
3962
3980
  const validatorName = `${category.name}-${check}`;
3963
- const imageBin = resolve12(imageDir, `bin/validators/${validatorName}`);
3964
- const symlinkPath = resolve12(binValidatorsDir, validatorName);
3965
- if (existsSync7(imageBin)) {
3981
+ const imageBin = resolve13(imageDir, `bin/validators/${validatorName}`);
3982
+ const symlinkPath = resolve13(binValidatorsDir, validatorName);
3983
+ if (existsSync8(imageBin)) {
3966
3984
  try {
3967
3985
  unlinkSync(symlinkPath);
3968
3986
  } catch {}
@@ -3971,18 +3989,18 @@ async function executeDist(context, args) {
3971
3989
  }
3972
3990
  }
3973
3991
  }
3974
- const agentsDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
3975
- if (existsSync7(agentsDir)) {
3992
+ const agentsDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
3993
+ if (existsSync8(agentsDir)) {
3976
3994
  let relinkCount = 0;
3977
3995
  for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
3978
3996
  if (!agentEntry.isDirectory())
3979
3997
  continue;
3980
- const agentBinDir = resolve12(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
3981
- if (!existsSync7(agentBinDir))
3998
+ const agentBinDir = resolve13(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
3999
+ if (!existsSync8(agentBinDir))
3982
4000
  continue;
3983
4001
  const walkDir = (dir) => {
3984
4002
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
3985
- const fullPath = resolve12(dir, entry.name);
4003
+ const fullPath = resolve13(dir, entry.name);
3986
4004
  if (entry.isDirectory()) {
3987
4005
  walkDir(fullPath);
3988
4006
  } else if (entry.isSymbolicLink()) {
@@ -4016,7 +4034,7 @@ async function executeDist(context, args) {
4016
4034
 
4017
4035
  // packages/cli/src/commands/inbox.ts
4018
4036
  import { writeFileSync as writeFileSync4 } from "fs";
4019
- import { resolve as resolve13 } from "path";
4037
+ import { resolve as resolve14 } from "path";
4020
4038
  import {
4021
4039
  listAuthorityRuns,
4022
4040
  readJsonlFile as readJsonlFile3,
@@ -4033,7 +4051,7 @@ async function executeInbox(context, args) {
4033
4051
  pending = task.rest;
4034
4052
  requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
4035
4053
  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) => ({
4054
+ const approvals = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
4037
4055
  runId: entry.runId,
4038
4056
  record
4039
4057
  })));
@@ -4061,7 +4079,7 @@ async function executeInbox(context, args) {
4061
4079
  if (decision.value !== "approve" && decision.value !== "reject") {
4062
4080
  throw new CliError2("decision must be approve or reject.");
4063
4081
  }
4064
- const approvalsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
4082
+ const approvalsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
4065
4083
  const approvals = readJsonlFile3(approvalsPath);
4066
4084
  const resolvedAt = new Date().toISOString();
4067
4085
  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 +4096,7 @@ async function executeInbox(context, args) {
4078
4096
  pending = task.rest;
4079
4097
  requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
4080
4098
  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) => ({
4099
+ const requests = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
4082
4100
  runId: entry.runId,
4083
4101
  record
4084
4102
  })));
@@ -4120,7 +4138,7 @@ async function executeInbox(context, args) {
4120
4138
  const [key, ...restValue] = entry.split("=");
4121
4139
  return [key, restValue.join("=")];
4122
4140
  }));
4123
- const requestsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
4141
+ const requestsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
4124
4142
  const requests = readJsonlFile3(requestsPath);
4125
4143
  const resolvedAt = new Date().toISOString();
4126
4144
  const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
@@ -4135,14 +4153,14 @@ async function executeInbox(context, args) {
4135
4153
  }
4136
4154
 
4137
4155
  // 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";
4156
+ import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
4139
4157
  import { spawnSync as spawnSync2 } from "child_process";
4140
- import { resolve as resolve16 } from "path";
4158
+ import { resolve as resolve17 } from "path";
4141
4159
  import { buildRigInitConfigSource } from "@rig/core";
4142
4160
 
4143
4161
  // packages/cli/src/commands/_snapshot-upload.ts
4144
4162
  import { mkdir, readdir, readFile, writeFile } from "fs/promises";
4145
- import { dirname as dirname2, resolve as resolve14, relative, sep } from "path";
4163
+ import { dirname as dirname2, resolve as resolve15, relative, sep } from "path";
4146
4164
  var SNAPSHOT_ARCHIVE_VERSION = 1;
4147
4165
  var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
4148
4166
  var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
@@ -4164,15 +4182,15 @@ function assertManifestPath(root, relativePath) {
4164
4182
  if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
4165
4183
  throw new Error(`Invalid snapshot path: ${relativePath}`);
4166
4184
  }
4167
- const resolved = resolve14(root, relativePath);
4185
+ const resolved = resolve15(root, relativePath);
4168
4186
  const relativeToRoot = relative(root, resolved);
4169
- if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve14(relativeToRoot) === resolved) {
4187
+ if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve15(relativeToRoot) === resolved) {
4170
4188
  throw new Error(`Snapshot path escapes project root: ${relativePath}`);
4171
4189
  }
4172
4190
  return resolved;
4173
4191
  }
4174
4192
  async function buildSnapshotUploadManifest(projectRoot, options = {}) {
4175
- const root = resolve14(projectRoot);
4193
+ const root = resolve15(projectRoot);
4176
4194
  const excludedDirectories = [...new Set([
4177
4195
  ...DEFAULT_EXCLUDED_DIRECTORIES,
4178
4196
  ...options.excludedDirectories ?? []
@@ -4184,7 +4202,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
4184
4202
  for (const entry of entries) {
4185
4203
  if (entry.isDirectory() && excludedSet.has(entry.name))
4186
4204
  continue;
4187
- const fullPath = resolve14(dir, entry.name);
4205
+ const fullPath = resolve15(dir, entry.name);
4188
4206
  if (entry.isDirectory()) {
4189
4207
  await visit(fullPath);
4190
4208
  continue;
@@ -4232,8 +4250,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
4232
4250
  }
4233
4251
 
4234
4252
  // packages/cli/src/commands/_doctor-checks.ts
4235
- import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
4236
- import { resolve as resolve15 } from "path";
4253
+ import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
4254
+ import { resolve as resolve16 } from "path";
4237
4255
  import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
4238
4256
  function check(id, label, status, detail, remediation) {
4239
4257
  return {
@@ -4273,11 +4291,11 @@ function repoSlugFromConfig(config) {
4273
4291
  function loadFallbackConfig(projectRoot) {
4274
4292
  const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
4275
4293
  for (const name of candidates) {
4276
- const path = resolve15(projectRoot, name);
4277
- if (!existsSync8(path))
4294
+ const path = resolve16(projectRoot, name);
4295
+ if (!existsSync9(path))
4278
4296
  continue;
4279
4297
  try {
4280
- const source = readFileSync4(path, "utf8");
4298
+ const source = readFileSync5(path, "utf8");
4281
4299
  if (name.endsWith(".json"))
4282
4300
  return JSON.parse(source);
4283
4301
  const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
@@ -4356,7 +4374,7 @@ async function runRigDoctorChecks(options) {
4356
4374
  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
4375
  const loadedConfig = await loadConfig(projectRoot).catch(() => null);
4358
4376
  const config = loadedConfig ?? loadFallbackConfig(projectRoot);
4359
- const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync8(resolve15(projectRoot, name)));
4377
+ const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync9(resolve16(projectRoot, name)));
4360
4378
  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
4379
  const taskSourceKind = config?.taskSource?.kind;
4362
4380
  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 +4469,10 @@ function countDoctorFailures(checks) {
4451
4469
  }
4452
4470
 
4453
4471
  // packages/cli/src/commands/init.ts
4454
- var RIG_CONFIG_PACKAGE_VERSION = "0.0.6-alpha.0";
4472
+ var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
4455
4473
  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}`
4474
+ "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
4475
+ "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
4458
4476
  };
4459
4477
  function parseRepoSlugFromRemote(remoteUrl) {
4460
4478
  const trimmed = remoteUrl.trim();
@@ -4474,20 +4492,20 @@ function parseRepoSlug(value) {
4474
4492
  return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
4475
4493
  }
4476
4494
  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))
4495
+ const rigDir = resolve17(projectRoot, ".rig");
4496
+ mkdirSync7(resolve17(rigDir, "state"), { recursive: true });
4497
+ mkdirSync7(resolve17(rigDir, "logs"), { recursive: true });
4498
+ mkdirSync7(resolve17(rigDir, "runs"), { recursive: true });
4499
+ mkdirSync7(resolve17(rigDir, "tmp"), { recursive: true });
4500
+ mkdirSync7(resolve17(projectRoot, "artifacts"), { recursive: true });
4501
+ const taskConfigPath = resolve17(rigDir, "task-config.json");
4502
+ if (!existsSync10(taskConfigPath))
4485
4503
  writeFileSync5(taskConfigPath, `{}
4486
4504
  `, "utf-8");
4487
4505
  }
4488
4506
  function ensureGitignoreEntries(projectRoot) {
4489
- const path = resolve16(projectRoot, ".gitignore");
4490
- const existing = existsSync9(path) ? readFileSync5(path, "utf8") : "";
4507
+ const path = resolve17(projectRoot, ".gitignore");
4508
+ const existing = existsSync10(path) ? readFileSync6(path, "utf8") : "";
4491
4509
  const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
4492
4510
  const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
4493
4511
  if (missing.length === 0)
@@ -4500,14 +4518,14 @@ function ensureGitignoreEntries(projectRoot) {
4500
4518
  `, "utf8");
4501
4519
  }
4502
4520
  function ensureRigConfigPackageDependencies(projectRoot) {
4503
- const path = resolve16(projectRoot, "package.json");
4504
- const existing = existsSync9(path) ? JSON.parse(readFileSync5(path, "utf8")) : {};
4521
+ const path = resolve17(projectRoot, "package.json");
4522
+ const existing = existsSync10(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
4505
4523
  const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
4506
4524
  for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
4507
4525
  devDependencies[name] = spec;
4508
4526
  }
4509
4527
  const next = {
4510
- ...existsSync9(path) ? existing : { name: "rig-project", private: true },
4528
+ ...existsSync10(path) ? existing : { name: "rig-project", private: true },
4511
4529
  devDependencies
4512
4530
  };
4513
4531
  writeFileSync5(path, `${JSON.stringify(next, null, 2)}
@@ -4577,15 +4595,54 @@ async function promptSelect(prompts, options) {
4577
4595
  throw new CliError2("Init cancelled.", 1);
4578
4596
  return String(value);
4579
4597
  }
4580
- async function pollDeviceAuthOnce(context, pollId) {
4598
+ function sleep2(ms) {
4599
+ return new Promise((resolve18) => setTimeout(resolve18, ms));
4600
+ }
4601
+ function positiveIntFromEnv(name, fallback) {
4602
+ const value = Number.parseInt(process.env[name] ?? "", 10);
4603
+ return Number.isFinite(value) && value >= 0 ? value : fallback;
4604
+ }
4605
+ function apiSessionTokenFrom(payload) {
4606
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
4607
+ return null;
4608
+ const token = payload.apiSessionToken;
4609
+ return typeof token === "string" && token.trim() ? token.trim() : null;
4610
+ }
4611
+ function writeRemoteGitHubAuthState(projectRoot, input) {
4612
+ writeFileSync5(resolve17(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
4613
+ authenticated: true,
4614
+ source: input.source,
4615
+ storedOnServer: true,
4616
+ selectedRepo: input.selectedRepo,
4617
+ ...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
4618
+ updatedAt: new Date().toISOString()
4619
+ }, null, 2)}
4620
+ `, "utf8");
4621
+ }
4622
+ async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
4581
4623
  if (typeof pollId !== "string" || !pollId.trim())
4582
4624
  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;
4625
+ const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
4626
+ const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
4627
+ const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
4628
+ const deadline = Date.now() + timeoutMs;
4629
+ let last = null;
4630
+ do {
4631
+ const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
4632
+ method: "POST",
4633
+ headers: { "content-type": "application/json" },
4634
+ body: JSON.stringify({ pollId })
4635
+ }).catch(() => null);
4636
+ last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
4637
+ const status = typeof last?.status === "string" ? last.status : null;
4638
+ if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
4639
+ return last;
4640
+ }
4641
+ if (timeoutMs <= 0)
4642
+ return last;
4643
+ await sleep2(intervalMs);
4644
+ } while (Date.now() < deadline);
4645
+ return last;
4589
4646
  }
4590
4647
  async function runControlPlaneInit(context, options) {
4591
4648
  const projectRoot = context.projectRoot;
@@ -4608,9 +4665,9 @@ async function runControlPlaneInit(context, options) {
4608
4665
  });
4609
4666
  ensureRigPrivateDirs(projectRoot);
4610
4667
  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);
4668
+ const configTsPath = resolve17(projectRoot, "rig.config.ts");
4669
+ const configJsonPath = resolve17(projectRoot, "rig.config.json");
4670
+ const configExists = existsSync10(configTsPath) || existsSync10(configJsonPath);
4614
4671
  if (!options.privateStateOnly) {
4615
4672
  if (configExists && !options.repair) {
4616
4673
  if (context.outputMode !== "json")
@@ -4626,7 +4683,7 @@ async function runControlPlaneInit(context, options) {
4626
4683
  }
4627
4684
  ensureRigConfigPackageDependencies(projectRoot);
4628
4685
  }
4629
- writeFileSync5(resolve16(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4686
+ writeFileSync5(resolve17(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4630
4687
  `, "utf8");
4631
4688
  const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
4632
4689
  let uploadedSnapshot = null;
@@ -4648,10 +4705,14 @@ async function runControlPlaneInit(context, options) {
4648
4705
  const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
4649
4706
  if (token) {
4650
4707
  githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
4651
- setGitHubBearerTokenForCurrentProcess(token);
4708
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4709
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4652
4710
  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");
4711
+ writeRemoteGitHubAuthState(projectRoot, {
4712
+ source: authMethod === "gh" ? "gh" : "init-token",
4713
+ selectedRepo: repo.slug,
4714
+ apiSessionToken
4715
+ });
4655
4716
  }
4656
4717
  } else if (authMethod === "device") {
4657
4718
  const payload = await requestServerJson(context, "/api/github/auth/device/start", {
@@ -4660,9 +4721,22 @@ async function runControlPlaneInit(context, options) {
4660
4721
  body: JSON.stringify({ repoSlug: repo.slug })
4661
4722
  });
4662
4723
  deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
4663
- const completed = await pollDeviceAuthOnce(context, deviceAuth.pollId);
4664
- if (completed)
4724
+ if (context.outputMode !== "json") {
4725
+ const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
4726
+ const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
4727
+ console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
4728
+ }
4729
+ const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
4730
+ if (completed) {
4731
+ const apiSessionToken = apiSessionTokenFrom(completed);
4732
+ if (apiSessionToken) {
4733
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken);
4734
+ if (serverKind === "remote") {
4735
+ writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken });
4736
+ }
4737
+ }
4665
4738
  deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
4739
+ }
4666
4740
  }
4667
4741
  let remoteCheckoutPreparation = null;
4668
4742
  if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
@@ -4682,6 +4756,12 @@ async function runControlPlaneInit(context, options) {
4682
4756
  });
4683
4757
  const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
4684
4758
  const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
4759
+ if (serverRootSwitch && token) {
4760
+ githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
4761
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4762
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4763
+ writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken });
4764
+ }
4685
4765
  const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
4686
4766
  const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
4687
4767
  remote: true,
@@ -4791,7 +4871,7 @@ function parseInitOptions(args) {
4791
4871
  async function runInteractiveControlPlaneInit(context, prompts) {
4792
4872
  prompts.intro?.("Initialize a Rig control-plane project");
4793
4873
  const projectRoot = context.projectRoot;
4794
- const existingConfig = existsSync9(resolve16(projectRoot, "rig.config.ts")) || existsSync9(resolve16(projectRoot, "rig.config.json"));
4874
+ const existingConfig = existsSync10(resolve17(projectRoot, "rig.config.ts")) || existsSync10(resolve17(projectRoot, "rig.config.json"));
4795
4875
  let repair = false;
4796
4876
  let privateStateOnly = false;
4797
4877
  if (existingConfig) {
@@ -4891,7 +4971,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
4891
4971
  });
4892
4972
  const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
4893
4973
  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")}.` : "";
4974
+ 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
4975
  prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
4896
4976
  return result;
4897
4977
  }
@@ -5053,8 +5133,8 @@ async function executeDoctor(context, args) {
5053
5133
  }
5054
5134
 
5055
5135
  // packages/cli/src/commands/_run-driver-helpers.ts
5056
- import { readFileSync as readFileSync6 } from "fs";
5057
- import { resolve as resolve17 } from "path";
5136
+ import { readFileSync as readFileSync7 } from "fs";
5137
+ import { resolve as resolve18 } from "path";
5058
5138
  import {
5059
5139
  appendJsonlRecord as appendJsonlRecord2,
5060
5140
  readAuthorityRun as readAuthorityRun2,
@@ -5074,7 +5154,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
5074
5154
  ...patch,
5075
5155
  updatedAt: new Date().toISOString()
5076
5156
  };
5077
- writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
5157
+ writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
5078
5158
  return next;
5079
5159
  }
5080
5160
  function touchAuthorityRun(projectRoot, runId) {
@@ -5082,21 +5162,21 @@ function touchAuthorityRun(projectRoot, runId) {
5082
5162
  if (!current) {
5083
5163
  return;
5084
5164
  }
5085
- writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
5165
+ writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
5086
5166
  ...current,
5087
5167
  updatedAt: new Date().toISOString()
5088
5168
  });
5089
5169
  }
5090
5170
  function appendRunTimeline(projectRoot, runId, value) {
5091
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
5171
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
5092
5172
  touchAuthorityRun(projectRoot, runId);
5093
5173
  }
5094
5174
  function appendRunLog(projectRoot, runId, value) {
5095
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
5175
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
5096
5176
  touchAuthorityRun(projectRoot, runId);
5097
5177
  }
5098
5178
  function appendRunAction(projectRoot, runId, value) {
5099
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
5179
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
5100
5180
  id: value.id,
5101
5181
  type: "action",
5102
5182
  actionType: value.actionType,
@@ -5167,7 +5247,7 @@ function buildRunPrompt(input) {
5167
5247
  })();
5168
5248
  const scopeText = (() => {
5169
5249
  try {
5170
- const parsed = JSON.parse(readFileSync6(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
5250
+ const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
5171
5251
  const entry = parsed[input.taskId] ?? {};
5172
5252
  const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
5173
5253
  const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
@@ -5280,8 +5360,8 @@ function renderSourceScopeValidation(task, validation) {
5280
5360
  }
5281
5361
 
5282
5362
  // packages/cli/src/commands/inspect.ts
5283
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
5284
- import { resolve as resolve18 } from "path";
5363
+ import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
5364
+ import { resolve as resolve19 } from "path";
5285
5365
  import {
5286
5366
  listAuthorityRuns as listAuthorityRuns2,
5287
5367
  readAuthorityRun as readAuthorityRun3,
@@ -5302,8 +5382,8 @@ async function executeInspect(context, args) {
5302
5382
  if (!latestRun) {
5303
5383
  throw new CliError2(`No runs found for ${requiredTask}.`);
5304
5384
  }
5305
- const logsPath = resolve18(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5306
- if (!existsSync10(logsPath)) {
5385
+ const logsPath = resolve19(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5386
+ if (!existsSync11(logsPath)) {
5307
5387
  throw new CliError2(`No logs found for run ${latestRun.runId}.`);
5308
5388
  }
5309
5389
  await context.runCommand(["cat", logsPath]);
@@ -5313,7 +5393,7 @@ async function executeInspect(context, args) {
5313
5393
  const { value: task, rest: remaining } = takeOption(rest, "--task");
5314
5394
  requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
5315
5395
  const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
5316
- const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync10(path));
5396
+ const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync11(path));
5317
5397
  if (!artifactRoot) {
5318
5398
  throw new CliError2(`No artifacts found for ${requiredTask}.`);
5319
5399
  }
@@ -5370,10 +5450,10 @@ async function executeInspect(context, args) {
5370
5450
  case "failures": {
5371
5451
  requireNoExtraArgs(rest, "bun run rig inspect failures");
5372
5452
  const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
5373
- if (!existsSync10(failed)) {
5453
+ if (!existsSync11(failed)) {
5374
5454
  console.log("No failures recorded.");
5375
5455
  } else {
5376
- process.stdout.write(readFileSync7(failed, "utf-8"));
5456
+ process.stdout.write(readFileSync8(failed, "utf-8"));
5377
5457
  }
5378
5458
  return { ok: true, group: "inspect", command };
5379
5459
  }
@@ -5390,11 +5470,11 @@ async function executeInspect(context, args) {
5390
5470
  return { ok: true, group: "inspect", command };
5391
5471
  case "audit": {
5392
5472
  requireNoExtraArgs(rest, "bun run rig inspect audit");
5393
- const auditPath = resolve18(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5394
- if (!existsSync10(auditPath)) {
5473
+ const auditPath = resolve19(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5474
+ if (!existsSync11(auditPath)) {
5395
5475
  console.log("No audit log found.");
5396
5476
  } else {
5397
- const lines = readFileSync7(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5477
+ const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5398
5478
  for (const line of lines) {
5399
5479
  console.log(line);
5400
5480
  }
@@ -6023,8 +6103,8 @@ async function executeRemote(context, args) {
6023
6103
  }
6024
6104
 
6025
6105
  // packages/cli/src/commands/run.ts
6026
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
6027
- import { resolve as resolve19 } from "path";
6106
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
6107
+ import { resolve as resolve20 } from "path";
6028
6108
  import { createInterface as createInterface2 } from "readline/promises";
6029
6109
  import {
6030
6110
  listAuthorityRuns as listAuthorityRuns3,
@@ -6303,7 +6383,7 @@ async function executeRun(context, args) {
6303
6383
  if (!run.value) {
6304
6384
  throw new CliError2("run timeline requires --run <id>.");
6305
6385
  }
6306
- const timelinePath = resolve19(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6386
+ const timelinePath = resolve20(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6307
6387
  const printEvents = () => {
6308
6388
  const events2 = readJsonlFile4(timelinePath);
6309
6389
  if (context.outputMode === "text") {
@@ -6315,12 +6395,12 @@ async function executeRun(context, args) {
6315
6395
  };
6316
6396
  const events = printEvents();
6317
6397
  if (follow.value && context.outputMode === "text") {
6318
- let lastLength = existsSync11(timelinePath) ? readFileSync8(timelinePath, "utf8").length : 0;
6398
+ let lastLength = existsSync12(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
6319
6399
  while (true) {
6320
6400
  await Bun.sleep(1000);
6321
- if (!existsSync11(timelinePath))
6401
+ if (!existsSync12(timelinePath))
6322
6402
  continue;
6323
- const next = readFileSync8(timelinePath, "utf8");
6403
+ const next = readFileSync9(timelinePath, "utf8");
6324
6404
  if (next.length <= lastLength)
6325
6405
  continue;
6326
6406
  const delta = next.slice(lastLength);
@@ -6606,10 +6686,10 @@ async function executeServer(context, args, options) {
6606
6686
  }
6607
6687
 
6608
6688
  // packages/cli/src/commands/task.ts
6609
- import { readFileSync as readFileSync9 } from "fs";
6689
+ import { readFileSync as readFileSync10 } from "fs";
6610
6690
  import { spawnSync as spawnSync4 } from "child_process";
6611
6691
  import { createInterface as createInterface4 } from "readline/promises";
6612
- import { resolve as resolve20 } from "path";
6692
+ import { resolve as resolve21 } from "path";
6613
6693
  import {
6614
6694
  taskArtifactDir,
6615
6695
  taskArtifacts,
@@ -6940,7 +7020,7 @@ async function executeTask(context, args, options) {
6940
7020
  const fileFlag = takeOption(rest.slice(1), "--file");
6941
7021
  let content;
6942
7022
  if (fileFlag.value) {
6943
- content = readFileSync9(resolve20(context.projectRoot, fileFlag.value), "utf-8");
7023
+ content = readFileSync10(resolve21(context.projectRoot, fileFlag.value), "utf-8");
6944
7024
  } else {
6945
7025
  content = await readStdin();
6946
7026
  }
@@ -7161,8 +7241,8 @@ async function executeTask(context, args, options) {
7161
7241
  }
7162
7242
 
7163
7243
  // 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";
7244
+ import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
7245
+ import { resolve as resolve22 } from "path";
7166
7246
  import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
7167
7247
  import { createInterface as createLineInterface } from "readline";
7168
7248
  import { loadConfig as loadConfig2 } from "@rig/core/load-config";
@@ -7228,12 +7308,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
7228
7308
  return 0;
7229
7309
  let copied = 0;
7230
7310
  for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
7231
- const sourcePath = resolve21(sourceRoot, relativePath);
7232
- const targetPath = resolve21(targetRoot, relativePath);
7311
+ const sourcePath = resolve22(sourceRoot, relativePath);
7312
+ const targetPath = resolve22(targetRoot, relativePath);
7233
7313
  try {
7234
7314
  if (!statSync2(sourcePath).isFile())
7235
7315
  continue;
7236
- mkdirSync8(resolve21(targetPath, ".."), { recursive: true });
7316
+ mkdirSync8(resolve22(targetPath, ".."), { recursive: true });
7237
7317
  copyFileSync3(sourcePath, targetPath);
7238
7318
  copied += 1;
7239
7319
  } catch {}
@@ -7267,6 +7347,14 @@ function buildTaskRunReviewEnv(config) {
7267
7347
  ...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
7268
7348
  };
7269
7349
  }
7350
+ function buildDirtyBaselineHandshakeEnv(input) {
7351
+ if (input.baselineMode !== "dirty-snapshot")
7352
+ return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
7353
+ return {
7354
+ RIG_BASELINE_MODE: "dirty-snapshot",
7355
+ RIG_DIRTY_BASELINE_READY_FILE: resolve22(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
7356
+ };
7357
+ }
7270
7358
  function positiveInt(value, fallback) {
7271
7359
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
7272
7360
  }
@@ -7359,6 +7447,12 @@ function appendPiStageLog(input) {
7359
7447
  });
7360
7448
  emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
7361
7449
  }
7450
+ async function runCheckedCommand(command, args, cwd, label = "git") {
7451
+ const result = await command(args, { cwd });
7452
+ if (result.exitCode !== 0) {
7453
+ throw new Error(`${label} ${args.join(" ")} failed (${result.exitCode}): ${result.stderr ?? result.stdout ?? ""}`.trim());
7454
+ }
7455
+ }
7362
7456
  function createCommandRunner(binary) {
7363
7457
  return async (args, options) => {
7364
7458
  const child = spawn2(binary, [...args], {
@@ -7369,9 +7463,9 @@ function createCommandRunner(binary) {
7369
7463
  const stderrChunks = [];
7370
7464
  child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7371
7465
  child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7372
- return await new Promise((resolve22) => {
7373
- child.once("error", (error) => resolve22({ exitCode: 1, stderr: error.message }));
7374
- child.once("close", (code) => resolve22({
7466
+ return await new Promise((resolve23) => {
7467
+ child.once("error", (error) => resolve23({ exitCode: 1, stderr: error.message }));
7468
+ child.once("close", (code) => resolve23({
7375
7469
  exitCode: code ?? 1,
7376
7470
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
7377
7471
  stderr: Buffer.concat(stderrChunks).toString("utf8")
@@ -7434,6 +7528,7 @@ async function runTaskRunPostValidationLifecycle(input) {
7434
7528
  command: gitCommand
7435
7529
  });
7436
7530
  stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
7531
+ await runCheckedCommand(gitCommand, ["push", "--set-upstream", "origin", branch], workspace, "git");
7437
7532
  stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
7438
7533
  const pr = await prAutomation({
7439
7534
  projectRoot: workspace,
@@ -7444,7 +7539,6 @@ async function runTaskRunPostValidationLifecycle(input) {
7444
7539
  sourceTask: input.sourceTask,
7445
7540
  uploadedSnapshot: input.uploadedSnapshot,
7446
7541
  command: ghCommand,
7447
- gitCommand,
7448
7542
  steerPi,
7449
7543
  lifecycle: {
7450
7544
  onPrOpened: async ({ prUrl }) => {
@@ -7567,7 +7661,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
7567
7661
  return null;
7568
7662
  }
7569
7663
  for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
7570
- const summary = readJsonFile3(resolve21(artifactDir, "validation-summary.json"), null);
7664
+ const summary = readJsonFile3(resolve22(artifactDir, "validation-summary.json"), null);
7571
7665
  if (!summary || summary.status !== "fail") {
7572
7666
  continue;
7573
7667
  }
@@ -7648,9 +7742,9 @@ function readTaskRunAcceptedArtifactState(input) {
7648
7742
  if (!input.taskId || !input.workspaceDir) {
7649
7743
  return { accepted: false, reason: null };
7650
7744
  }
7651
- const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7652
- const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7653
- const taskResultPath = resolve21(artifactDir, "task-result.json");
7745
+ const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7746
+ const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7747
+ const taskResultPath = resolve22(artifactDir, "task-result.json");
7654
7748
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7655
7749
  if (reviewStatus !== "APPROVED") {
7656
7750
  return { accepted: false, reason: null };
@@ -7687,12 +7781,12 @@ function resolveTaskRunRetryContext(input) {
7687
7781
  if (!input.taskId || !input.workspaceDir) {
7688
7782
  return { shouldRetry: false, failureDetail: null, nextPrompt: null };
7689
7783
  }
7690
- const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7691
- const reviewStatePath = resolve21(artifactDir, "review-state.json");
7692
- const reviewFeedbackPath = resolve21(artifactDir, "review-feedback.md");
7693
- const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7694
- const failedApproachesPath = resolve21(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7695
- const validationSummaryPath = resolve21(artifactDir, "validation-summary.json");
7784
+ const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7785
+ const reviewStatePath = resolve22(artifactDir, "review-state.json");
7786
+ const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
7787
+ const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7788
+ const failedApproachesPath = resolve22(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7789
+ const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
7696
7790
  const reviewState = readJsonFile3(reviewStatePath, null);
7697
7791
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7698
7792
  const reviewRejected = isTaskRunReviewRejected(reviewState);
@@ -7747,11 +7841,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
7747
7841
  return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
7748
7842
  }
7749
7843
  function readTaskRunReviewStatus(reviewStatusPath) {
7750
- if (!existsSync12(reviewStatusPath)) {
7844
+ if (!existsSync13(reviewStatusPath)) {
7751
7845
  return null;
7752
7846
  }
7753
7847
  try {
7754
- const status = readFileSync10(reviewStatusPath, "utf8").trim().toUpperCase();
7848
+ const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
7755
7849
  return status === "APPROVED" || status === "REJECTED" ? status : null;
7756
7850
  } catch {
7757
7851
  return null;
@@ -7835,7 +7929,7 @@ function stringArrayField(record, key) {
7835
7929
  async function executeRigOwnedTaskRun(context, input) {
7836
7930
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
7837
7931
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
7838
- const prompt = buildRunPrompt({
7932
+ let prompt = buildRunPrompt({
7839
7933
  projectRoot: context.projectRoot,
7840
7934
  taskId: input.taskId,
7841
7935
  fallbackTitle: input.title,
@@ -7935,7 +8029,22 @@ async function executeRigOwnedTaskRun(context, input) {
7935
8029
  const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
7936
8030
  const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
7937
8031
  const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
7938
- patchAuthorityRun(context.projectRoot, input.runId, { planning: planningClassification });
8032
+ const planningArtifactPath = resolve22("artifacts", runtimeTaskId, "implementation-plan.md");
8033
+ const persistedPlanning = {
8034
+ ...planningClassification,
8035
+ classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
8036
+ artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
8037
+ classifiedAt: new Date().toISOString()
8038
+ };
8039
+ mkdirSync8(resolve22(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
8040
+ writeFileSync6(resolve22(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
8041
+ `, "utf8");
8042
+ patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
8043
+ prompt = `${prompt}
8044
+
8045
+ Rig planning classification:
8046
+ ${JSON.stringify(persistedPlanning, null, 2)}
8047
+ ${planningClassification.planningRequired ? `Before implementing, write a concise implementation plan to ${planningArtifactPath}. Treat that plan artifact as required acceptance evidence for the Plan stage.` : "Planning is not required for this run; briefly state why before implementation."}`;
7939
8048
  if (input.runtimeAdapter === "pi") {
7940
8049
  for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
7941
8050
  appendPiStageLog({
@@ -8002,6 +8111,7 @@ async function executeRigOwnedTaskRun(context, input) {
8002
8111
  ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
8003
8112
  };
8004
8113
  Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
8114
+ Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
8005
8115
  const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
8006
8116
  const maxAttempts = automationLimits.maxValidationAttempts;
8007
8117
  const promoteToValidating = (detail) => {
@@ -8056,22 +8166,29 @@ async function executeRigOwnedTaskRun(context, input) {
8056
8166
  patchAuthorityRun(context.projectRoot, input.runId, {
8057
8167
  status: "running",
8058
8168
  worktreePath: latestRuntimeWorkspace,
8059
- artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve21(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8169
+ artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve22(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8060
8170
  logRoot: latestLogsDir,
8061
- sessionPath: latestSessionDir ? resolve21(latestSessionDir, "session.json") : null,
8062
- sessionLogPath: latestLogsDir ? resolve21(latestLogsDir, "agent-stdout.log") : null,
8171
+ sessionPath: latestSessionDir ? resolve22(latestSessionDir, "session.json") : null,
8172
+ sessionLogPath: latestLogsDir ? resolve22(latestLogsDir, "agent-stdout.log") : null,
8063
8173
  branch: runtimeId
8064
8174
  });
8065
8175
  if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
8066
8176
  dirtyBaselineApplied = true;
8067
8177
  const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
8178
+ const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
8179
+ if (readyFile) {
8180
+ mkdirSync8(resolve22(readyFile, ".."), { recursive: true });
8181
+ writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
8182
+ `, "utf8");
8183
+ }
8068
8184
  appendRunLog(context.projectRoot, input.runId, {
8069
8185
  id: `log:${input.runId}:dirty-baseline`,
8070
8186
  title: "Dirty baseline snapshot",
8071
8187
  detail: dirty.detail,
8072
8188
  tone: dirty.applied ? "tool" : "info",
8073
8189
  status: dirty.applied ? "completed" : "skipped",
8074
- createdAt: new Date().toISOString()
8190
+ createdAt: new Date().toISOString(),
8191
+ payload: readyFile ? { readyFile } : undefined
8075
8192
  });
8076
8193
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
8077
8194
  }
@@ -8338,7 +8455,7 @@ async function executeRigOwnedTaskRun(context, input) {
8338
8455
  let acceptedArtifactObservedAt = null;
8339
8456
  let acceptedArtifactPollTimer = null;
8340
8457
  let acceptedArtifactKillTimer = null;
8341
- const attemptExit = await new Promise((resolve22) => {
8458
+ const attemptExit = await new Promise((resolve23) => {
8342
8459
  let settled = false;
8343
8460
  const settle = (result) => {
8344
8461
  if (settled)
@@ -8346,7 +8463,7 @@ async function executeRigOwnedTaskRun(context, input) {
8346
8463
  settled = true;
8347
8464
  if (acceptedArtifactPollTimer)
8348
8465
  clearInterval(acceptedArtifactPollTimer);
8349
- resolve22(result);
8466
+ resolve23(result);
8350
8467
  };
8351
8468
  const pollAcceptedArtifacts = () => {
8352
8469
  const artifactState = readTaskRunAcceptedArtifactState({
@@ -8543,6 +8660,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8543
8660
  });
8544
8661
  throw new CliError2(terminalFailureDetail, exit.code ?? 1);
8545
8662
  }
8663
+ if (planningClassification.planningRequired) {
8664
+ const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
8665
+ const expectedPlanPath = resolve22(planWorkspace, planningArtifactPath);
8666
+ if (!existsSync13(expectedPlanPath)) {
8667
+ const failedAt = new Date().toISOString();
8668
+ const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
8669
+ patchAuthorityRun(context.projectRoot, input.runId, {
8670
+ status: "needs_attention",
8671
+ completedAt: failedAt,
8672
+ errorText: failureDetail
8673
+ });
8674
+ appendRunLog(context.projectRoot, input.runId, {
8675
+ id: `log:${input.runId}:plan-artifact-missing`,
8676
+ title: "Required plan artifact missing",
8677
+ detail: failureDetail,
8678
+ tone: "error",
8679
+ status: "needs_attention",
8680
+ createdAt: failedAt
8681
+ });
8682
+ emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
8683
+ throw new CliError2(failureDetail, 1);
8684
+ }
8685
+ }
8546
8686
  const runPiPrFeedbackFix = async (message2) => {
8547
8687
  appendPiStageLog({
8548
8688
  projectRoot: context.projectRoot,
@@ -8600,9 +8740,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8600
8740
  });
8601
8741
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
8602
8742
  });
8603
- const exitCode = await new Promise((resolve22) => {
8604
- child.once("error", () => resolve22(1));
8605
- child.once("close", (code) => resolve22(code ?? 1));
8743
+ const exitCode = await new Promise((resolve23) => {
8744
+ child.once("error", () => resolve23(1));
8745
+ child.once("close", (code) => resolve23(code ?? 1));
8606
8746
  });
8607
8747
  if (exitCode !== 0) {
8608
8748
  throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
@@ -8721,8 +8861,8 @@ async function executeTest(context, args) {
8721
8861
  }
8722
8862
 
8723
8863
  // packages/cli/src/commands/setup.ts
8724
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "fs";
8725
- import { resolve as resolve22 } from "path";
8864
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
8865
+ import { resolve as resolve23 } from "path";
8726
8866
  import { createPluginHost } from "@rig/core";
8727
8867
  import {
8728
8868
  isSupportedBunVersion as isSupportedBunVersion2,
@@ -8785,9 +8925,9 @@ function runSetupInit(projectRoot) {
8785
8925
  mkdirSync9(stateDir, { recursive: true });
8786
8926
  mkdirSync9(logsDir, { recursive: true });
8787
8927
  mkdirSync9(artifactsDir, { recursive: true });
8788
- const failuresPath = resolve22(stateDir, "failed_approaches.md");
8789
- if (!existsSync13(failuresPath)) {
8790
- writeFileSync6(failuresPath, `# Failed Approaches
8928
+ const failuresPath = resolve23(stateDir, "failed_approaches.md");
8929
+ if (!existsSync14(failuresPath)) {
8930
+ writeFileSync7(failuresPath, `# Failed Approaches
8791
8931
 
8792
8932
  `, "utf-8");
8793
8933
  }
@@ -8804,18 +8944,18 @@ async function runSetupCheck(projectRoot) {
8804
8944
  }
8805
8945
  async function runSetupPreflight(projectRoot) {
8806
8946
  await runSetupCheck(projectRoot);
8807
- const validationRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
8808
- if (existsSync13(validationRoot)) {
8947
+ const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
8948
+ if (existsSync14(validationRoot)) {
8809
8949
  const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
8810
8950
  for (const validator of validators) {
8811
- const script = resolve22(validationRoot, validator.name, "validate.sh");
8812
- if (existsSync13(script)) {
8951
+ const script = resolve23(validationRoot, validator.name, "validate.sh");
8952
+ if (existsSync14(script)) {
8813
8953
  console.log(`OK: validator script ${script}`);
8814
8954
  }
8815
8955
  }
8816
8956
  }
8817
- const hooksRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
8818
- if (existsSync13(hooksRoot)) {
8957
+ const hooksRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
8958
+ if (existsSync14(hooksRoot)) {
8819
8959
  const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
8820
8960
  for (const hook of hooks) {
8821
8961
  console.log(`OK: hook ${hook}`);
@@ -9192,8 +9332,8 @@ async function executeGroup(context, group, args) {
9192
9332
  }
9193
9333
  }
9194
9334
  // packages/cli/src/launcher.ts
9195
- import { existsSync as existsSync14 } from "fs";
9196
- import { resolve as resolve23 } from "path";
9335
+ import { existsSync as existsSync15 } from "fs";
9336
+ import { resolve as resolve24 } from "path";
9197
9337
  import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
9198
9338
  import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
9199
9339
  function parsePolicyMode(value) {
@@ -9206,7 +9346,7 @@ function parsePolicyMode(value) {
9206
9346
  throw new Error(`Invalid --policy-mode value: ${value}. Use off|observe|enforce.`);
9207
9347
  }
9208
9348
  function hasRigProjectMarker(candidate) {
9209
- return existsSync14(resolve23(candidate, RIG_DEFINITION_DIRNAME)) || existsSync14(resolve23(candidate, RIG_STATE_DIRNAME)) || existsSync14(resolve23(candidate, "rig.config.ts")) || existsSync14(resolve23(candidate, "rig.config.json"));
9349
+ return existsSync15(resolve24(candidate, RIG_DEFINITION_DIRNAME)) || existsSync15(resolve24(candidate, RIG_STATE_DIRNAME)) || existsSync15(resolve24(candidate, "rig.config.ts")) || existsSync15(resolve24(candidate, "rig.config.json"));
9210
9350
  }
9211
9351
  function resolveProjectRoot({
9212
9352
  envProjectRoot,
@@ -9215,17 +9355,17 @@ function resolveProjectRoot({
9215
9355
  cwd = process.cwd()
9216
9356
  }) {
9217
9357
  if (envProjectRoot) {
9218
- return resolve23(cwd, envProjectRoot);
9358
+ return resolve24(cwd, envProjectRoot);
9219
9359
  }
9220
9360
  const fallbackImportDir = importDir ?? cwd;
9221
- const candidates = [cwd, resolve23(execPath, "..", ".."), resolve23(fallbackImportDir, "..")];
9361
+ const candidates = [cwd, resolve24(execPath, "..", ".."), resolve24(fallbackImportDir, "..")];
9222
9362
  for (const candidate of candidates) {
9223
9363
  const nearest = resolveNearestRigProjectRoot(candidate);
9224
9364
  if (hasRigProjectMarker(nearest)) {
9225
9365
  return nearest;
9226
9366
  }
9227
9367
  }
9228
- return resolve23(cwd);
9368
+ return resolve24(cwd);
9229
9369
  }
9230
9370
  function normalizeCliErrorCode(message2, isCliError) {
9231
9371
  if (message2.startsWith("Invalid --policy-mode value:")) {
@@ -9292,7 +9432,7 @@ async function runRigCli(module, options = {}) {
9292
9432
  runId: context.runId,
9293
9433
  outcome,
9294
9434
  eventsFile: context.eventBus.getEventsFile(),
9295
- policyFile: resolve23(projectRoot, "rig", "policy", "policy.json"),
9435
+ policyFile: resolve24(projectRoot, "rig", "policy", "policy.json"),
9296
9436
  policyMode: context.policyMode ?? policyMode ?? module.loadPolicy(projectRoot).mode
9297
9437
  }, null, 2));
9298
9438
  }