@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/src/index.js CHANGED
@@ -2667,6 +2667,8 @@ function resolveSelectedConnection(projectRoot, options = {}) {
2667
2667
 
2668
2668
  // packages/cli/src/commands/_server-client.ts
2669
2669
  import { spawnSync } from "child_process";
2670
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
2671
+ import { resolve as resolve9 } from "path";
2670
2672
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
2671
2673
  var cachedGitHubBearerToken;
2672
2674
  function cleanToken(value) {
@@ -2676,9 +2678,25 @@ function cleanToken(value) {
2676
2678
  function setGitHubBearerTokenForCurrentProcess(token) {
2677
2679
  cachedGitHubBearerToken = cleanToken(token ?? undefined);
2678
2680
  }
2679
- function readGitHubBearerTokenForRemote() {
2681
+ function readPrivateRemoteSessionToken(projectRoot) {
2682
+ const path = resolve9(projectRoot, ".rig", "state", "github-auth.json");
2683
+ if (!existsSync5(path))
2684
+ return null;
2685
+ try {
2686
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
2687
+ return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
2688
+ } catch {
2689
+ return null;
2690
+ }
2691
+ }
2692
+ function readGitHubBearerTokenForRemote(projectRoot) {
2680
2693
  if (cachedGitHubBearerToken !== undefined)
2681
2694
  return cachedGitHubBearerToken;
2695
+ const privateSession = readPrivateRemoteSessionToken(projectRoot);
2696
+ if (privateSession) {
2697
+ cachedGitHubBearerToken = privateSession;
2698
+ return cachedGitHubBearerToken;
2699
+ }
2682
2700
  const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
2683
2701
  if (envToken) {
2684
2702
  cachedGitHubBearerToken = envToken;
@@ -2698,7 +2716,7 @@ async function ensureServerForCli(projectRoot) {
2698
2716
  if (selected?.connection.kind === "remote") {
2699
2717
  return {
2700
2718
  baseUrl: selected.connection.baseUrl,
2701
- authToken: readGitHubBearerTokenForRemote(),
2719
+ authToken: readGitHubBearerTokenForRemote(projectRoot),
2702
2720
  connectionKind: "remote"
2703
2721
  };
2704
2722
  }
@@ -2805,7 +2823,7 @@ async function postGitHubTokenViaServer(context, token, options = {}) {
2805
2823
  const payload = await requestServerJson(context, "/api/github/auth/token", {
2806
2824
  method: "POST",
2807
2825
  headers: { "content-type": "application/json" },
2808
- body: JSON.stringify({ token, selectedRepo: options.selectedRepo })
2826
+ body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
2809
2827
  });
2810
2828
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2811
2829
  }
@@ -2826,7 +2844,7 @@ async function registerProjectViaServer(context, input) {
2826
2844
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2827
2845
  }
2828
2846
  function sleep(ms) {
2829
- return new Promise((resolve9) => setTimeout(resolve9, ms));
2847
+ return new Promise((resolve10) => setTimeout(resolve10, ms));
2830
2848
  }
2831
2849
  async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
2832
2850
  const switched = await requestServerJson(context, "/api/server/project-root", {
@@ -2917,9 +2935,9 @@ async function submitTaskRunViaServer(context, input) {
2917
2935
  }
2918
2936
 
2919
2937
  // packages/cli/src/commands/_pi-install.ts
2920
- import { existsSync as existsSync5, readFileSync as readFileSync3, rmSync as rmSync3 } from "fs";
2938
+ import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
2921
2939
  import { homedir as homedir3 } from "os";
2922
- import { resolve as resolve9 } from "path";
2940
+ import { resolve as resolve10 } from "path";
2923
2941
  var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
2924
2942
  var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
2925
2943
  export { default } from '@rig/pi-rig';
@@ -2934,11 +2952,11 @@ async function defaultCommandRunner(command, options = {}) {
2934
2952
  return { exitCode, stdout, stderr };
2935
2953
  }
2936
2954
  function resolvePiRigExtensionPath(homeDir) {
2937
- return resolve9(homeDir, ".pi", "agent", "extensions", "pi-rig");
2955
+ return resolve10(homeDir, ".pi", "agent", "extensions", "pi-rig");
2938
2956
  }
2939
- function resolvePiRigPackageSource(projectRoot, exists = existsSync5) {
2940
- const localPackage = resolve9(projectRoot, "packages", "pi-rig");
2941
- if (exists(resolve9(localPackage, "package.json")))
2957
+ function resolvePiRigPackageSource(projectRoot, exists = existsSync6) {
2958
+ const localPackage = resolve10(projectRoot, "packages", "pi-rig");
2959
+ if (exists(resolve10(localPackage, "package.json")))
2942
2960
  return localPackage;
2943
2961
  return `npm:${PI_RIG_PACKAGE_NAME}`;
2944
2962
  }
@@ -2989,13 +3007,13 @@ async function ensurePiBinaryAvailable(input) {
2989
3007
  ...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
2990
3008
  };
2991
3009
  }
2992
- function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync5) {
3010
+ function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync6) {
2993
3011
  const extensionPath = resolvePiRigExtensionPath(homeDir);
2994
- const indexPath = resolve9(extensionPath, "index.ts");
3012
+ const indexPath = resolve10(extensionPath, "index.ts");
2995
3013
  if (!exists(indexPath))
2996
3014
  return;
2997
3015
  try {
2998
- const content = readFileSync3(indexPath, "utf8");
3016
+ const content = readFileSync4(indexPath, "utf8");
2999
3017
  if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
3000
3018
  rmSync3(extensionPath, { recursive: true, force: true });
3001
3019
  }
@@ -3011,13 +3029,13 @@ async function checkPiRigInstall(input = {}) {
3011
3029
  piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
3012
3030
  };
3013
3031
  }
3014
- const exists = input.exists ?? existsSync5;
3032
+ const exists = input.exists ?? existsSync6;
3015
3033
  const runner = input.commandRunner ?? defaultCommandRunner;
3016
3034
  const piResult = await safeRun(runner, ["pi", "--version"]);
3017
3035
  const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
3018
3036
  const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
3019
3037
  ${piListResult.stderr}`);
3020
- const legacyBridge = exists(resolve9(extensionPath, "index.ts"));
3038
+ const legacyBridge = exists(resolve10(extensionPath, "index.ts"));
3021
3039
  const hasPiRig = listedPiRig;
3022
3040
  return {
3023
3041
  extensionPath,
@@ -3355,7 +3373,7 @@ async function executeQueue(context, args) {
3355
3373
  }
3356
3374
 
3357
3375
  // packages/cli/src/commands/agent.ts
3358
- import { resolve as resolve11 } from "path";
3376
+ import { resolve as resolve12 } from "path";
3359
3377
  import {
3360
3378
  agentId,
3361
3379
  cleanupAgentRuntime,
@@ -3365,8 +3383,8 @@ import {
3365
3383
  } from "@rig/runtime/control-plane/runtime/isolation";
3366
3384
 
3367
3385
  // packages/cli/src/commands/_authority-runs.ts
3368
- import { existsSync as existsSync6 } from "fs";
3369
- import { resolve as resolve10 } from "path";
3386
+ import { existsSync as existsSync7 } from "fs";
3387
+ import { resolve as resolve11 } from "path";
3370
3388
  import {
3371
3389
  readAuthorityRun,
3372
3390
  readJsonlFile as readJsonlFile2,
@@ -3388,8 +3406,8 @@ function normalizeRuntimeAdapter(value) {
3388
3406
  return "claude-code";
3389
3407
  }
3390
3408
  function readLatestBeadRecord(projectRoot, taskId) {
3391
- const issuesPath = resolve10(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3392
- if (!existsSync6(issuesPath)) {
3409
+ const issuesPath = resolve11(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
3410
+ if (!existsSync7(issuesPath)) {
3393
3411
  return null;
3394
3412
  }
3395
3413
  let latest = null;
@@ -3456,7 +3474,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3456
3474
  } else if ("errorText" in next) {
3457
3475
  delete next.errorText;
3458
3476
  }
3459
- writeJsonFile3(resolve10(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3477
+ writeJsonFile3(resolve11(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
3460
3478
  return next;
3461
3479
  }
3462
3480
 
@@ -3577,10 +3595,10 @@ async function executeAgent(context, args) {
3577
3595
  status: "running",
3578
3596
  startedAt: createdAt,
3579
3597
  worktreePath: runtime.workspaceDir,
3580
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3598
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3581
3599
  logRoot: runtime.logsDir,
3582
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3583
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3600
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3601
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3584
3602
  pid: process.pid
3585
3603
  });
3586
3604
  const result = await runInAgentRuntime({
@@ -3600,10 +3618,10 @@ async function executeAgent(context, args) {
3600
3618
  startedAt: createdAt,
3601
3619
  completedAt: failedAt,
3602
3620
  worktreePath: runtime.workspaceDir,
3603
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3621
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3604
3622
  logRoot: runtime.logsDir,
3605
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3606
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3623
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3624
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3607
3625
  pid: process.pid,
3608
3626
  errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
3609
3627
  });
@@ -3620,10 +3638,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3620
3638
  startedAt: createdAt,
3621
3639
  completedAt,
3622
3640
  worktreePath: runtime.workspaceDir,
3623
- artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
3641
+ artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
3624
3642
  logRoot: runtime.logsDir,
3625
- sessionPath: resolve11(runtime.sessionDir, "session.json"),
3626
- sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
3643
+ sessionPath: resolve12(runtime.sessionDir, "session.json"),
3644
+ sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
3627
3645
  pid: process.pid
3628
3646
  });
3629
3647
  return {
@@ -3697,7 +3715,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
3697
3715
  import {
3698
3716
  chmodSync,
3699
3717
  copyFileSync as copyFileSync2,
3700
- existsSync as existsSync7,
3718
+ existsSync as existsSync8,
3701
3719
  mkdirSync as mkdirSync6,
3702
3720
  readdirSync,
3703
3721
  readlinkSync,
@@ -3707,7 +3725,7 @@ import {
3707
3725
  unlinkSync
3708
3726
  } from "fs";
3709
3727
  import { homedir as homedir4 } from "os";
3710
- import { resolve as resolve12 } from "path";
3728
+ import { resolve as resolve13 } from "path";
3711
3729
  import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
3712
3730
  import {
3713
3731
  computeRuntimeImageFingerprint,
@@ -3726,13 +3744,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
3726
3744
 
3727
3745
  // packages/cli/src/commands/dist.ts
3728
3746
  function collectRigValidatorBuildTargets(input) {
3729
- const validatorsRoot = resolve12(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3730
- if (!existsSync7(validatorsRoot))
3747
+ const validatorsRoot = resolve13(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
3748
+ if (!existsSync8(validatorsRoot))
3731
3749
  return [];
3732
3750
  const targets = [];
3733
3751
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3734
3752
  for (const category of categories) {
3735
- const categoryDir = resolve12(validatorsRoot, category.name);
3753
+ const categoryDir = resolve13(validatorsRoot, category.name);
3736
3754
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3737
3755
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3738
3756
  continue;
@@ -3741,7 +3759,7 @@ function collectRigValidatorBuildTargets(input) {
3741
3759
  continue;
3742
3760
  targets.push({
3743
3761
  source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
3744
- dest: resolve12(input.imageDir, `bin/validators/${category.name}-${check}`),
3762
+ dest: resolve13(input.imageDir, `bin/validators/${category.name}-${check}`),
3745
3763
  cwd: input.hostProjectRoot
3746
3764
  });
3747
3765
  }
@@ -3750,16 +3768,16 @@ function collectRigValidatorBuildTargets(input) {
3750
3768
  }
3751
3769
  async function findLatestDistBinary(projectRoot) {
3752
3770
  const distRoot = resolveControlPlaneHostDistDir(projectRoot);
3753
- if (!existsSync7(distRoot)) {
3771
+ if (!existsSync8(distRoot)) {
3754
3772
  return null;
3755
3773
  }
3756
3774
  const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
3757
3775
  name: entry.name,
3758
- mtimeMs: statSync(resolve12(distRoot, entry.name)).mtimeMs
3776
+ mtimeMs: statSync(resolve13(distRoot, entry.name)).mtimeMs
3759
3777
  })).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
3760
3778
  for (const { name } of entries) {
3761
- const candidate = resolve12(distRoot, name, "bin", "rig");
3762
- if (existsSync7(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3779
+ const candidate = resolve13(distRoot, name, "bin", "rig");
3780
+ if (existsSync8(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
3763
3781
  return candidate;
3764
3782
  }
3765
3783
  }
@@ -3771,7 +3789,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
3771
3789
  async function runDistDoctor(projectRoot) {
3772
3790
  const bunPath = Bun.which("bun");
3773
3791
  const rigPath = Bun.which("rig");
3774
- const userBinDir = resolve12(homedir4(), ".local/bin");
3792
+ const userBinDir = resolve13(homedir4(), ".local/bin");
3775
3793
  const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
3776
3794
  let rigRunnable = false;
3777
3795
  if (rigPath) {
@@ -3819,15 +3837,15 @@ async function executeDist(context, args) {
3819
3837
  let source = await findLatestDistBinary(context.projectRoot);
3820
3838
  let buildDir = null;
3821
3839
  if (!source) {
3822
- buildDir = resolve12(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3840
+ buildDir = resolve13(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
3823
3841
  await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
3824
- source = resolve12(buildDir, "bin", "rig");
3842
+ source = resolve13(buildDir, "bin", "rig");
3825
3843
  }
3826
- if (!existsSync7(source)) {
3844
+ if (!existsSync8(source)) {
3827
3845
  throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
3828
3846
  }
3829
- const installedPath = resolve12(installDir, "rig");
3830
- if (existsSync7(installedPath)) {
3847
+ const installedPath = resolve13(installDir, "rig");
3848
+ if (existsSync8(installedPath)) {
3831
3849
  unlinkSync(installedPath);
3832
3850
  }
3833
3851
  copyFileSync2(source, installedPath);
@@ -3869,22 +3887,22 @@ async function executeDist(context, args) {
3869
3887
  requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
3870
3888
  const fp = await computeRuntimeImageFingerprint(context.projectRoot);
3871
3889
  const currentId = computeRuntimeImageId(fp);
3872
- const imagesDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3890
+ const imagesDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
3873
3891
  mkdirSync6(imagesDir, { recursive: true });
3874
3892
  let pruned = 0;
3875
3893
  for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
3876
3894
  if (entry.isDirectory() && entry.name !== currentId) {
3877
- rmSync4(resolve12(imagesDir, entry.name), { recursive: true, force: true });
3895
+ rmSync4(resolve13(imagesDir, entry.name), { recursive: true, force: true });
3878
3896
  pruned++;
3879
3897
  }
3880
3898
  }
3881
3899
  if (pruned > 0 && context.outputMode === "text") {
3882
3900
  console.log(`Pruned ${pruned} stale image(s).`);
3883
3901
  }
3884
- const imageDir = resolve12(imagesDir, currentId);
3885
- mkdirSync6(resolve12(imageDir, "bin/hooks"), { recursive: true });
3886
- mkdirSync6(resolve12(imageDir, "bin/plugins"), { recursive: true });
3887
- mkdirSync6(resolve12(imageDir, "bin/validators"), { recursive: true });
3902
+ const imageDir = resolve13(imagesDir, currentId);
3903
+ mkdirSync6(resolve13(imageDir, "bin/hooks"), { recursive: true });
3904
+ mkdirSync6(resolve13(imageDir, "bin/plugins"), { recursive: true });
3905
+ mkdirSync6(resolve13(imageDir, "bin/validators"), { recursive: true });
3888
3906
  const hookNames = [
3889
3907
  "scope-guard",
3890
3908
  "import-guard",
@@ -3898,25 +3916,25 @@ async function executeDist(context, args) {
3898
3916
  ];
3899
3917
  const targets = [];
3900
3918
  const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || context.projectRoot;
3901
- targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve12(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
3902
- targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
3919
+ targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve13(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
3920
+ targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
3903
3921
  for (const hookName of hookNames) {
3904
3922
  const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
3905
- targets.push({ source: src, dest: resolve12(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
3906
- targets.push({ source: src, dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
3923
+ targets.push({ source: src, dest: resolve13(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
3924
+ targets.push({ source: src, dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
3907
3925
  }
3908
- const pluginsDir = resolve12(context.projectRoot, "rig/plugins");
3909
- const binPluginsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
3910
- const validatorsRoot = resolve12(hostProjectRoot, "packages/runtime/src/control-plane/validators");
3911
- const binValidatorsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
3926
+ const pluginsDir = resolve13(context.projectRoot, "rig/plugins");
3927
+ const binPluginsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
3928
+ const validatorsRoot = resolve13(hostProjectRoot, "packages/runtime/src/control-plane/validators");
3929
+ const binValidatorsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
3912
3930
  mkdirSync6(binPluginsDir, { recursive: true });
3913
3931
  mkdirSync6(binValidatorsDir, { recursive: true });
3914
- if (existsSync7(pluginsDir)) {
3932
+ if (existsSync8(pluginsDir)) {
3915
3933
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3916
3934
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3917
3935
  if (!m)
3918
3936
  continue;
3919
- targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve12(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3937
+ targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve13(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
3920
3938
  }
3921
3939
  }
3922
3940
  targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
@@ -3927,17 +3945,17 @@ async function executeDist(context, args) {
3927
3945
  const isValidator = dest.includes("/bin/validators/");
3928
3946
  await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
3929
3947
  }
3930
- if (existsSync7(pluginsDir)) {
3948
+ if (existsSync8(pluginsDir)) {
3931
3949
  for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
3932
3950
  const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
3933
3951
  if (!m)
3934
3952
  continue;
3935
3953
  const pluginName = m[1];
3936
- const imageBin = resolve12(imageDir, `bin/plugins/${pluginName}`);
3954
+ const imageBin = resolve13(imageDir, `bin/plugins/${pluginName}`);
3937
3955
  if (!pluginName)
3938
3956
  continue;
3939
- const symlinkPath = resolve12(binPluginsDir, pluginName);
3940
- if (existsSync7(imageBin)) {
3957
+ const symlinkPath = resolve13(binPluginsDir, pluginName);
3958
+ if (existsSync8(imageBin)) {
3941
3959
  try {
3942
3960
  unlinkSync(symlinkPath);
3943
3961
  } catch {}
@@ -3945,10 +3963,10 @@ async function executeDist(context, args) {
3945
3963
  }
3946
3964
  }
3947
3965
  }
3948
- if (existsSync7(validatorsRoot)) {
3966
+ if (existsSync8(validatorsRoot)) {
3949
3967
  const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
3950
3968
  for (const category of categories) {
3951
- const categoryDir = resolve12(validatorsRoot, category.name);
3969
+ const categoryDir = resolve13(validatorsRoot, category.name);
3952
3970
  for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
3953
3971
  if (!entry.isFile() || !entry.name.endsWith(".ts"))
3954
3972
  continue;
@@ -3956,9 +3974,9 @@ async function executeDist(context, args) {
3956
3974
  if (!check || check === "index" || check === "shared")
3957
3975
  continue;
3958
3976
  const validatorName = `${category.name}-${check}`;
3959
- const imageBin = resolve12(imageDir, `bin/validators/${validatorName}`);
3960
- const symlinkPath = resolve12(binValidatorsDir, validatorName);
3961
- if (existsSync7(imageBin)) {
3977
+ const imageBin = resolve13(imageDir, `bin/validators/${validatorName}`);
3978
+ const symlinkPath = resolve13(binValidatorsDir, validatorName);
3979
+ if (existsSync8(imageBin)) {
3962
3980
  try {
3963
3981
  unlinkSync(symlinkPath);
3964
3982
  } catch {}
@@ -3967,18 +3985,18 @@ async function executeDist(context, args) {
3967
3985
  }
3968
3986
  }
3969
3987
  }
3970
- const agentsDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
3971
- if (existsSync7(agentsDir)) {
3988
+ const agentsDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
3989
+ if (existsSync8(agentsDir)) {
3972
3990
  let relinkCount = 0;
3973
3991
  for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
3974
3992
  if (!agentEntry.isDirectory())
3975
3993
  continue;
3976
- const agentBinDir = resolve12(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
3977
- if (!existsSync7(agentBinDir))
3994
+ const agentBinDir = resolve13(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
3995
+ if (!existsSync8(agentBinDir))
3978
3996
  continue;
3979
3997
  const walkDir = (dir) => {
3980
3998
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
3981
- const fullPath = resolve12(dir, entry.name);
3999
+ const fullPath = resolve13(dir, entry.name);
3982
4000
  if (entry.isDirectory()) {
3983
4001
  walkDir(fullPath);
3984
4002
  } else if (entry.isSymbolicLink()) {
@@ -4012,7 +4030,7 @@ async function executeDist(context, args) {
4012
4030
 
4013
4031
  // packages/cli/src/commands/inbox.ts
4014
4032
  import { writeFileSync as writeFileSync4 } from "fs";
4015
- import { resolve as resolve13 } from "path";
4033
+ import { resolve as resolve14 } from "path";
4016
4034
  import {
4017
4035
  listAuthorityRuns,
4018
4036
  readJsonlFile as readJsonlFile3,
@@ -4029,7 +4047,7 @@ async function executeInbox(context, args) {
4029
4047
  pending = task.rest;
4030
4048
  requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
4031
4049
  const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
4032
- const approvals = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
4050
+ const approvals = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
4033
4051
  runId: entry.runId,
4034
4052
  record
4035
4053
  })));
@@ -4057,7 +4075,7 @@ async function executeInbox(context, args) {
4057
4075
  if (decision.value !== "approve" && decision.value !== "reject") {
4058
4076
  throw new CliError2("decision must be approve or reject.");
4059
4077
  }
4060
- const approvalsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
4078
+ const approvalsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
4061
4079
  const approvals = readJsonlFile3(approvalsPath);
4062
4080
  const resolvedAt = new Date().toISOString();
4063
4081
  const next = approvals.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", decision: decision.value, note: note3.value ?? null, resolvedAt } : entry);
@@ -4074,7 +4092,7 @@ async function executeInbox(context, args) {
4074
4092
  pending = task.rest;
4075
4093
  requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
4076
4094
  const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
4077
- const requests = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
4095
+ const requests = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
4078
4096
  runId: entry.runId,
4079
4097
  record
4080
4098
  })));
@@ -4116,7 +4134,7 @@ async function executeInbox(context, args) {
4116
4134
  const [key, ...restValue] = entry.split("=");
4117
4135
  return [key, restValue.join("=")];
4118
4136
  }));
4119
- const requestsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
4137
+ const requestsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
4120
4138
  const requests = readJsonlFile3(requestsPath);
4121
4139
  const resolvedAt = new Date().toISOString();
4122
4140
  const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
@@ -4131,14 +4149,14 @@ async function executeInbox(context, args) {
4131
4149
  }
4132
4150
 
4133
4151
  // packages/cli/src/commands/init.ts
4134
- import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
4152
+ import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
4135
4153
  import { spawnSync as spawnSync2 } from "child_process";
4136
- import { resolve as resolve16 } from "path";
4154
+ import { resolve as resolve17 } from "path";
4137
4155
  import { buildRigInitConfigSource } from "@rig/core";
4138
4156
 
4139
4157
  // packages/cli/src/commands/_snapshot-upload.ts
4140
4158
  import { mkdir, readdir, readFile, writeFile } from "fs/promises";
4141
- import { dirname as dirname2, resolve as resolve14, relative, sep } from "path";
4159
+ import { dirname as dirname2, resolve as resolve15, relative, sep } from "path";
4142
4160
  var SNAPSHOT_ARCHIVE_VERSION = 1;
4143
4161
  var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
4144
4162
  var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
@@ -4160,15 +4178,15 @@ function assertManifestPath(root, relativePath) {
4160
4178
  if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
4161
4179
  throw new Error(`Invalid snapshot path: ${relativePath}`);
4162
4180
  }
4163
- const resolved = resolve14(root, relativePath);
4181
+ const resolved = resolve15(root, relativePath);
4164
4182
  const relativeToRoot = relative(root, resolved);
4165
- if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve14(relativeToRoot) === resolved) {
4183
+ if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve15(relativeToRoot) === resolved) {
4166
4184
  throw new Error(`Snapshot path escapes project root: ${relativePath}`);
4167
4185
  }
4168
4186
  return resolved;
4169
4187
  }
4170
4188
  async function buildSnapshotUploadManifest(projectRoot, options = {}) {
4171
- const root = resolve14(projectRoot);
4189
+ const root = resolve15(projectRoot);
4172
4190
  const excludedDirectories = [...new Set([
4173
4191
  ...DEFAULT_EXCLUDED_DIRECTORIES,
4174
4192
  ...options.excludedDirectories ?? []
@@ -4180,7 +4198,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
4180
4198
  for (const entry of entries) {
4181
4199
  if (entry.isDirectory() && excludedSet.has(entry.name))
4182
4200
  continue;
4183
- const fullPath = resolve14(dir, entry.name);
4201
+ const fullPath = resolve15(dir, entry.name);
4184
4202
  if (entry.isDirectory()) {
4185
4203
  await visit(fullPath);
4186
4204
  continue;
@@ -4228,8 +4246,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
4228
4246
  }
4229
4247
 
4230
4248
  // packages/cli/src/commands/_doctor-checks.ts
4231
- import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
4232
- import { resolve as resolve15 } from "path";
4249
+ import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
4250
+ import { resolve as resolve16 } from "path";
4233
4251
  import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
4234
4252
  function check(id, label, status, detail, remediation) {
4235
4253
  return {
@@ -4269,11 +4287,11 @@ function repoSlugFromConfig(config) {
4269
4287
  function loadFallbackConfig(projectRoot) {
4270
4288
  const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
4271
4289
  for (const name of candidates) {
4272
- const path = resolve15(projectRoot, name);
4273
- if (!existsSync8(path))
4290
+ const path = resolve16(projectRoot, name);
4291
+ if (!existsSync9(path))
4274
4292
  continue;
4275
4293
  try {
4276
- const source = readFileSync4(path, "utf8");
4294
+ const source = readFileSync5(path, "utf8");
4277
4295
  if (name.endsWith(".json"))
4278
4296
  return JSON.parse(source);
4279
4297
  const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
@@ -4352,7 +4370,7 @@ async function runRigDoctorChecks(options) {
4352
4370
  checks.push(check("bun", `bun >= ${MIN_SUPPORTED_BUN_VERSION}`, isSupportedBunVersion(bunVersion) ? "pass" : "fail", `found ${bunVersion}`, `Install Bun ${MIN_SUPPORTED_BUN_VERSION} or newer.`), check("git", "git", which("git") ? "pass" : "fail", which("git") ?? undefined, "Install git and ensure it is on PATH."), check("jq", "jq", which("jq") ? "pass" : "warn", which("jq") ?? undefined, "Install jq (for example `brew install jq`)."));
4353
4371
  const loadedConfig = await loadConfig(projectRoot).catch(() => null);
4354
4372
  const config = loadedConfig ?? loadFallbackConfig(projectRoot);
4355
- const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync8(resolve15(projectRoot, name)));
4373
+ const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync9(resolve16(projectRoot, name)));
4356
4374
  checks.push(config ? check("config", "rig.config loadable", "pass") : check("config", "rig.config loadable", hasConfigFile ? "fail" : "fail", hasConfigFile ? "config file exists but failed to load" : "missing rig.config.ts/json", "Run `rig init` or fix the config error."));
4357
4375
  const taskSourceKind = config?.taskSource?.kind;
4358
4376
  checks.push(taskSourceKind ? check("task-source", "task source configured", "pass", taskSourceKind) : check("task-source", "task source configured", "fail", "missing taskSource", "Configure taskSource in rig.config.ts."));
@@ -4447,10 +4465,10 @@ function countDoctorFailures(checks) {
4447
4465
  }
4448
4466
 
4449
4467
  // packages/cli/src/commands/init.ts
4450
- var RIG_CONFIG_PACKAGE_VERSION = "0.0.6-alpha.0";
4468
+ var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
4451
4469
  var RIG_CONFIG_DEV_DEPENDENCIES = {
4452
- "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_VERSION}`,
4453
- "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_VERSION}`
4470
+ "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
4471
+ "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
4454
4472
  };
4455
4473
  function parseRepoSlugFromRemote(remoteUrl) {
4456
4474
  const trimmed = remoteUrl.trim();
@@ -4470,20 +4488,20 @@ function parseRepoSlug(value) {
4470
4488
  return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
4471
4489
  }
4472
4490
  function ensureRigPrivateDirs(projectRoot) {
4473
- const rigDir = resolve16(projectRoot, ".rig");
4474
- mkdirSync7(resolve16(rigDir, "state"), { recursive: true });
4475
- mkdirSync7(resolve16(rigDir, "logs"), { recursive: true });
4476
- mkdirSync7(resolve16(rigDir, "runs"), { recursive: true });
4477
- mkdirSync7(resolve16(rigDir, "tmp"), { recursive: true });
4478
- mkdirSync7(resolve16(projectRoot, "artifacts"), { recursive: true });
4479
- const taskConfigPath = resolve16(rigDir, "task-config.json");
4480
- if (!existsSync9(taskConfigPath))
4491
+ const rigDir = resolve17(projectRoot, ".rig");
4492
+ mkdirSync7(resolve17(rigDir, "state"), { recursive: true });
4493
+ mkdirSync7(resolve17(rigDir, "logs"), { recursive: true });
4494
+ mkdirSync7(resolve17(rigDir, "runs"), { recursive: true });
4495
+ mkdirSync7(resolve17(rigDir, "tmp"), { recursive: true });
4496
+ mkdirSync7(resolve17(projectRoot, "artifacts"), { recursive: true });
4497
+ const taskConfigPath = resolve17(rigDir, "task-config.json");
4498
+ if (!existsSync10(taskConfigPath))
4481
4499
  writeFileSync5(taskConfigPath, `{}
4482
4500
  `, "utf-8");
4483
4501
  }
4484
4502
  function ensureGitignoreEntries(projectRoot) {
4485
- const path = resolve16(projectRoot, ".gitignore");
4486
- const existing = existsSync9(path) ? readFileSync5(path, "utf8") : "";
4503
+ const path = resolve17(projectRoot, ".gitignore");
4504
+ const existing = existsSync10(path) ? readFileSync6(path, "utf8") : "";
4487
4505
  const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
4488
4506
  const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
4489
4507
  if (missing.length === 0)
@@ -4496,14 +4514,14 @@ function ensureGitignoreEntries(projectRoot) {
4496
4514
  `, "utf8");
4497
4515
  }
4498
4516
  function ensureRigConfigPackageDependencies(projectRoot) {
4499
- const path = resolve16(projectRoot, "package.json");
4500
- const existing = existsSync9(path) ? JSON.parse(readFileSync5(path, "utf8")) : {};
4517
+ const path = resolve17(projectRoot, "package.json");
4518
+ const existing = existsSync10(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
4501
4519
  const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
4502
4520
  for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
4503
4521
  devDependencies[name] = spec;
4504
4522
  }
4505
4523
  const next = {
4506
- ...existsSync9(path) ? existing : { name: "rig-project", private: true },
4524
+ ...existsSync10(path) ? existing : { name: "rig-project", private: true },
4507
4525
  devDependencies
4508
4526
  };
4509
4527
  writeFileSync5(path, `${JSON.stringify(next, null, 2)}
@@ -4573,15 +4591,54 @@ async function promptSelect(prompts, options) {
4573
4591
  throw new CliError2("Init cancelled.", 1);
4574
4592
  return String(value);
4575
4593
  }
4576
- async function pollDeviceAuthOnce(context, pollId) {
4594
+ function sleep2(ms) {
4595
+ return new Promise((resolve18) => setTimeout(resolve18, ms));
4596
+ }
4597
+ function positiveIntFromEnv(name, fallback) {
4598
+ const value = Number.parseInt(process.env[name] ?? "", 10);
4599
+ return Number.isFinite(value) && value >= 0 ? value : fallback;
4600
+ }
4601
+ function apiSessionTokenFrom(payload) {
4602
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
4603
+ return null;
4604
+ const token = payload.apiSessionToken;
4605
+ return typeof token === "string" && token.trim() ? token.trim() : null;
4606
+ }
4607
+ function writeRemoteGitHubAuthState(projectRoot, input) {
4608
+ writeFileSync5(resolve17(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
4609
+ authenticated: true,
4610
+ source: input.source,
4611
+ storedOnServer: true,
4612
+ selectedRepo: input.selectedRepo,
4613
+ ...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
4614
+ updatedAt: new Date().toISOString()
4615
+ }, null, 2)}
4616
+ `, "utf8");
4617
+ }
4618
+ async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
4577
4619
  if (typeof pollId !== "string" || !pollId.trim())
4578
4620
  return null;
4579
- const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
4580
- method: "POST",
4581
- headers: { "content-type": "application/json" },
4582
- body: JSON.stringify({ pollId })
4583
- }).catch(() => null);
4584
- return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
4621
+ const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
4622
+ const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
4623
+ const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
4624
+ const deadline = Date.now() + timeoutMs;
4625
+ let last = null;
4626
+ do {
4627
+ const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
4628
+ method: "POST",
4629
+ headers: { "content-type": "application/json" },
4630
+ body: JSON.stringify({ pollId })
4631
+ }).catch(() => null);
4632
+ last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
4633
+ const status = typeof last?.status === "string" ? last.status : null;
4634
+ if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
4635
+ return last;
4636
+ }
4637
+ if (timeoutMs <= 0)
4638
+ return last;
4639
+ await sleep2(intervalMs);
4640
+ } while (Date.now() < deadline);
4641
+ return last;
4585
4642
  }
4586
4643
  async function runControlPlaneInit(context, options) {
4587
4644
  const projectRoot = context.projectRoot;
@@ -4604,9 +4661,9 @@ async function runControlPlaneInit(context, options) {
4604
4661
  });
4605
4662
  ensureRigPrivateDirs(projectRoot);
4606
4663
  ensureGitignoreEntries(projectRoot);
4607
- const configTsPath = resolve16(projectRoot, "rig.config.ts");
4608
- const configJsonPath = resolve16(projectRoot, "rig.config.json");
4609
- const configExists = existsSync9(configTsPath) || existsSync9(configJsonPath);
4664
+ const configTsPath = resolve17(projectRoot, "rig.config.ts");
4665
+ const configJsonPath = resolve17(projectRoot, "rig.config.json");
4666
+ const configExists = existsSync10(configTsPath) || existsSync10(configJsonPath);
4610
4667
  if (!options.privateStateOnly) {
4611
4668
  if (configExists && !options.repair) {
4612
4669
  if (context.outputMode !== "json")
@@ -4622,7 +4679,7 @@ async function runControlPlaneInit(context, options) {
4622
4679
  }
4623
4680
  ensureRigConfigPackageDependencies(projectRoot);
4624
4681
  }
4625
- writeFileSync5(resolve16(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4682
+ writeFileSync5(resolve17(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
4626
4683
  `, "utf8");
4627
4684
  const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
4628
4685
  let uploadedSnapshot = null;
@@ -4644,10 +4701,14 @@ async function runControlPlaneInit(context, options) {
4644
4701
  const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
4645
4702
  if (token) {
4646
4703
  githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
4647
- setGitHubBearerTokenForCurrentProcess(token);
4704
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4705
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4648
4706
  if (serverKind === "remote") {
4649
- writeFileSync5(resolve16(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({ authenticated: true, source: authMethod === "gh" ? "gh" : "init-token", storedOnServer: true, selectedRepo: repo.slug, updatedAt: new Date().toISOString() }, null, 2)}
4650
- `, "utf8");
4707
+ writeRemoteGitHubAuthState(projectRoot, {
4708
+ source: authMethod === "gh" ? "gh" : "init-token",
4709
+ selectedRepo: repo.slug,
4710
+ apiSessionToken
4711
+ });
4651
4712
  }
4652
4713
  } else if (authMethod === "device") {
4653
4714
  const payload = await requestServerJson(context, "/api/github/auth/device/start", {
@@ -4656,9 +4717,22 @@ async function runControlPlaneInit(context, options) {
4656
4717
  body: JSON.stringify({ repoSlug: repo.slug })
4657
4718
  });
4658
4719
  deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
4659
- const completed = await pollDeviceAuthOnce(context, deviceAuth.pollId);
4660
- if (completed)
4720
+ if (context.outputMode !== "json") {
4721
+ const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
4722
+ const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
4723
+ console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
4724
+ }
4725
+ const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
4726
+ if (completed) {
4727
+ const apiSessionToken = apiSessionTokenFrom(completed);
4728
+ if (apiSessionToken) {
4729
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken);
4730
+ if (serverKind === "remote") {
4731
+ writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken });
4732
+ }
4733
+ }
4661
4734
  deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
4735
+ }
4662
4736
  }
4663
4737
  let remoteCheckoutPreparation = null;
4664
4738
  if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
@@ -4678,6 +4752,12 @@ async function runControlPlaneInit(context, options) {
4678
4752
  });
4679
4753
  const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
4680
4754
  const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
4755
+ if (serverRootSwitch && token) {
4756
+ githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
4757
+ const apiSessionToken = apiSessionTokenFrom(githubAuth);
4758
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4759
+ writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken });
4760
+ }
4681
4761
  const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
4682
4762
  const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
4683
4763
  remote: true,
@@ -4787,7 +4867,7 @@ function parseInitOptions(args) {
4787
4867
  async function runInteractiveControlPlaneInit(context, prompts) {
4788
4868
  prompts.intro?.("Initialize a Rig control-plane project");
4789
4869
  const projectRoot = context.projectRoot;
4790
- const existingConfig = existsSync9(resolve16(projectRoot, "rig.config.ts")) || existsSync9(resolve16(projectRoot, "rig.config.json"));
4870
+ const existingConfig = existsSync10(resolve17(projectRoot, "rig.config.ts")) || existsSync10(resolve17(projectRoot, "rig.config.json"));
4791
4871
  let repair = false;
4792
4872
  let privateStateOnly = false;
4793
4873
  if (existingConfig) {
@@ -4887,7 +4967,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
4887
4967
  });
4888
4968
  const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
4889
4969
  const deviceAuth = details.deviceAuth && typeof details.deviceAuth === "object" && !Array.isArray(details.deviceAuth) ? details.deviceAuth : null;
4890
- const deviceMessage = deviceAuth ? ` GitHub device flow: open ${String(deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server")} and enter ${String(deviceAuth.user_code ?? "the returned user code")}.` : "";
4970
+ const deviceMessage = deviceAuth ? ` GitHub device flow: open ${String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server")} and enter ${String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code")}.` : "";
4891
4971
  prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
4892
4972
  return result;
4893
4973
  }
@@ -5049,8 +5129,8 @@ async function executeDoctor(context, args) {
5049
5129
  }
5050
5130
 
5051
5131
  // packages/cli/src/commands/_run-driver-helpers.ts
5052
- import { readFileSync as readFileSync6 } from "fs";
5053
- import { resolve as resolve17 } from "path";
5132
+ import { readFileSync as readFileSync7 } from "fs";
5133
+ import { resolve as resolve18 } from "path";
5054
5134
  import {
5055
5135
  appendJsonlRecord as appendJsonlRecord2,
5056
5136
  readAuthorityRun as readAuthorityRun2,
@@ -5070,7 +5150,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
5070
5150
  ...patch,
5071
5151
  updatedAt: new Date().toISOString()
5072
5152
  };
5073
- writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
5153
+ writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
5074
5154
  return next;
5075
5155
  }
5076
5156
  function touchAuthorityRun(projectRoot, runId) {
@@ -5078,21 +5158,21 @@ function touchAuthorityRun(projectRoot, runId) {
5078
5158
  if (!current) {
5079
5159
  return;
5080
5160
  }
5081
- writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
5161
+ writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
5082
5162
  ...current,
5083
5163
  updatedAt: new Date().toISOString()
5084
5164
  });
5085
5165
  }
5086
5166
  function appendRunTimeline(projectRoot, runId, value) {
5087
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
5167
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
5088
5168
  touchAuthorityRun(projectRoot, runId);
5089
5169
  }
5090
5170
  function appendRunLog(projectRoot, runId, value) {
5091
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
5171
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
5092
5172
  touchAuthorityRun(projectRoot, runId);
5093
5173
  }
5094
5174
  function appendRunAction(projectRoot, runId, value) {
5095
- appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
5175
+ appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
5096
5176
  id: value.id,
5097
5177
  type: "action",
5098
5178
  actionType: value.actionType,
@@ -5163,7 +5243,7 @@ function buildRunPrompt(input) {
5163
5243
  })();
5164
5244
  const scopeText = (() => {
5165
5245
  try {
5166
- const parsed = JSON.parse(readFileSync6(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
5246
+ const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
5167
5247
  const entry = parsed[input.taskId] ?? {};
5168
5248
  const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
5169
5249
  const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
@@ -5276,8 +5356,8 @@ function renderSourceScopeValidation(task, validation) {
5276
5356
  }
5277
5357
 
5278
5358
  // packages/cli/src/commands/inspect.ts
5279
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
5280
- import { resolve as resolve18 } from "path";
5359
+ import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
5360
+ import { resolve as resolve19 } from "path";
5281
5361
  import {
5282
5362
  listAuthorityRuns as listAuthorityRuns2,
5283
5363
  readAuthorityRun as readAuthorityRun3,
@@ -5298,8 +5378,8 @@ async function executeInspect(context, args) {
5298
5378
  if (!latestRun) {
5299
5379
  throw new CliError2(`No runs found for ${requiredTask}.`);
5300
5380
  }
5301
- const logsPath = resolve18(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5302
- if (!existsSync10(logsPath)) {
5381
+ const logsPath = resolve19(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
5382
+ if (!existsSync11(logsPath)) {
5303
5383
  throw new CliError2(`No logs found for run ${latestRun.runId}.`);
5304
5384
  }
5305
5385
  await context.runCommand(["cat", logsPath]);
@@ -5309,7 +5389,7 @@ async function executeInspect(context, args) {
5309
5389
  const { value: task, rest: remaining } = takeOption(rest, "--task");
5310
5390
  requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
5311
5391
  const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
5312
- const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync10(path));
5392
+ const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync11(path));
5313
5393
  if (!artifactRoot) {
5314
5394
  throw new CliError2(`No artifacts found for ${requiredTask}.`);
5315
5395
  }
@@ -5366,10 +5446,10 @@ async function executeInspect(context, args) {
5366
5446
  case "failures": {
5367
5447
  requireNoExtraArgs(rest, "bun run rig inspect failures");
5368
5448
  const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
5369
- if (!existsSync10(failed)) {
5449
+ if (!existsSync11(failed)) {
5370
5450
  console.log("No failures recorded.");
5371
5451
  } else {
5372
- process.stdout.write(readFileSync7(failed, "utf-8"));
5452
+ process.stdout.write(readFileSync8(failed, "utf-8"));
5373
5453
  }
5374
5454
  return { ok: true, group: "inspect", command };
5375
5455
  }
@@ -5386,11 +5466,11 @@ async function executeInspect(context, args) {
5386
5466
  return { ok: true, group: "inspect", command };
5387
5467
  case "audit": {
5388
5468
  requireNoExtraArgs(rest, "bun run rig inspect audit");
5389
- const auditPath = resolve18(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5390
- if (!existsSync10(auditPath)) {
5469
+ const auditPath = resolve19(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
5470
+ if (!existsSync11(auditPath)) {
5391
5471
  console.log("No audit log found.");
5392
5472
  } else {
5393
- const lines = readFileSync7(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5473
+ const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
5394
5474
  for (const line of lines) {
5395
5475
  console.log(line);
5396
5476
  }
@@ -6019,8 +6099,8 @@ async function executeRemote(context, args) {
6019
6099
  }
6020
6100
 
6021
6101
  // packages/cli/src/commands/run.ts
6022
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
6023
- import { resolve as resolve19 } from "path";
6102
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
6103
+ import { resolve as resolve20 } from "path";
6024
6104
  import { createInterface as createInterface2 } from "readline/promises";
6025
6105
  import {
6026
6106
  listAuthorityRuns as listAuthorityRuns3,
@@ -6299,7 +6379,7 @@ async function executeRun(context, args) {
6299
6379
  if (!run.value) {
6300
6380
  throw new CliError2("run timeline requires --run <id>.");
6301
6381
  }
6302
- const timelinePath = resolve19(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6382
+ const timelinePath = resolve20(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6303
6383
  const printEvents = () => {
6304
6384
  const events2 = readJsonlFile4(timelinePath);
6305
6385
  if (context.outputMode === "text") {
@@ -6311,12 +6391,12 @@ async function executeRun(context, args) {
6311
6391
  };
6312
6392
  const events = printEvents();
6313
6393
  if (follow.value && context.outputMode === "text") {
6314
- let lastLength = existsSync11(timelinePath) ? readFileSync8(timelinePath, "utf8").length : 0;
6394
+ let lastLength = existsSync12(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
6315
6395
  while (true) {
6316
6396
  await Bun.sleep(1000);
6317
- if (!existsSync11(timelinePath))
6397
+ if (!existsSync12(timelinePath))
6318
6398
  continue;
6319
- const next = readFileSync8(timelinePath, "utf8");
6399
+ const next = readFileSync9(timelinePath, "utf8");
6320
6400
  if (next.length <= lastLength)
6321
6401
  continue;
6322
6402
  const delta = next.slice(lastLength);
@@ -6602,10 +6682,10 @@ async function executeServer(context, args, options) {
6602
6682
  }
6603
6683
 
6604
6684
  // packages/cli/src/commands/task.ts
6605
- import { readFileSync as readFileSync9 } from "fs";
6685
+ import { readFileSync as readFileSync10 } from "fs";
6606
6686
  import { spawnSync as spawnSync4 } from "child_process";
6607
6687
  import { createInterface as createInterface4 } from "readline/promises";
6608
- import { resolve as resolve20 } from "path";
6688
+ import { resolve as resolve21 } from "path";
6609
6689
  import {
6610
6690
  taskArtifactDir,
6611
6691
  taskArtifacts,
@@ -6936,7 +7016,7 @@ async function executeTask(context, args, options) {
6936
7016
  const fileFlag = takeOption(rest.slice(1), "--file");
6937
7017
  let content;
6938
7018
  if (fileFlag.value) {
6939
- content = readFileSync9(resolve20(context.projectRoot, fileFlag.value), "utf-8");
7019
+ content = readFileSync10(resolve21(context.projectRoot, fileFlag.value), "utf-8");
6940
7020
  } else {
6941
7021
  content = await readStdin();
6942
7022
  }
@@ -7157,8 +7237,8 @@ async function executeTask(context, args, options) {
7157
7237
  }
7158
7238
 
7159
7239
  // packages/cli/src/commands/task-run-driver.ts
7160
- import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync10, statSync as statSync2 } from "fs";
7161
- import { resolve as resolve21 } from "path";
7240
+ import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
7241
+ import { resolve as resolve22 } from "path";
7162
7242
  import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
7163
7243
  import { createInterface as createLineInterface } from "readline";
7164
7244
  import { loadConfig as loadConfig2 } from "@rig/core/load-config";
@@ -7224,12 +7304,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
7224
7304
  return 0;
7225
7305
  let copied = 0;
7226
7306
  for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
7227
- const sourcePath = resolve21(sourceRoot, relativePath);
7228
- const targetPath = resolve21(targetRoot, relativePath);
7307
+ const sourcePath = resolve22(sourceRoot, relativePath);
7308
+ const targetPath = resolve22(targetRoot, relativePath);
7229
7309
  try {
7230
7310
  if (!statSync2(sourcePath).isFile())
7231
7311
  continue;
7232
- mkdirSync8(resolve21(targetPath, ".."), { recursive: true });
7312
+ mkdirSync8(resolve22(targetPath, ".."), { recursive: true });
7233
7313
  copyFileSync3(sourcePath, targetPath);
7234
7314
  copied += 1;
7235
7315
  } catch {}
@@ -7263,6 +7343,14 @@ function buildTaskRunReviewEnv(config) {
7263
7343
  ...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
7264
7344
  };
7265
7345
  }
7346
+ function buildDirtyBaselineHandshakeEnv(input) {
7347
+ if (input.baselineMode !== "dirty-snapshot")
7348
+ return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
7349
+ return {
7350
+ RIG_BASELINE_MODE: "dirty-snapshot",
7351
+ RIG_DIRTY_BASELINE_READY_FILE: resolve22(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
7352
+ };
7353
+ }
7266
7354
  function positiveInt(value, fallback) {
7267
7355
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
7268
7356
  }
@@ -7355,6 +7443,12 @@ function appendPiStageLog(input) {
7355
7443
  });
7356
7444
  emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
7357
7445
  }
7446
+ async function runCheckedCommand(command, args, cwd, label = "git") {
7447
+ const result = await command(args, { cwd });
7448
+ if (result.exitCode !== 0) {
7449
+ throw new Error(`${label} ${args.join(" ")} failed (${result.exitCode}): ${result.stderr ?? result.stdout ?? ""}`.trim());
7450
+ }
7451
+ }
7358
7452
  function createCommandRunner(binary) {
7359
7453
  return async (args, options) => {
7360
7454
  const child = spawn2(binary, [...args], {
@@ -7365,9 +7459,9 @@ function createCommandRunner(binary) {
7365
7459
  const stderrChunks = [];
7366
7460
  child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7367
7461
  child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7368
- return await new Promise((resolve22) => {
7369
- child.once("error", (error) => resolve22({ exitCode: 1, stderr: error.message }));
7370
- child.once("close", (code) => resolve22({
7462
+ return await new Promise((resolve23) => {
7463
+ child.once("error", (error) => resolve23({ exitCode: 1, stderr: error.message }));
7464
+ child.once("close", (code) => resolve23({
7371
7465
  exitCode: code ?? 1,
7372
7466
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
7373
7467
  stderr: Buffer.concat(stderrChunks).toString("utf8")
@@ -7430,6 +7524,7 @@ async function runTaskRunPostValidationLifecycle(input) {
7430
7524
  command: gitCommand
7431
7525
  });
7432
7526
  stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
7527
+ await runCheckedCommand(gitCommand, ["push", "--set-upstream", "origin", branch], workspace, "git");
7433
7528
  stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
7434
7529
  const pr = await prAutomation({
7435
7530
  projectRoot: workspace,
@@ -7440,7 +7535,6 @@ async function runTaskRunPostValidationLifecycle(input) {
7440
7535
  sourceTask: input.sourceTask,
7441
7536
  uploadedSnapshot: input.uploadedSnapshot,
7442
7537
  command: ghCommand,
7443
- gitCommand,
7444
7538
  steerPi,
7445
7539
  lifecycle: {
7446
7540
  onPrOpened: async ({ prUrl }) => {
@@ -7563,7 +7657,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
7563
7657
  return null;
7564
7658
  }
7565
7659
  for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
7566
- const summary = readJsonFile3(resolve21(artifactDir, "validation-summary.json"), null);
7660
+ const summary = readJsonFile3(resolve22(artifactDir, "validation-summary.json"), null);
7567
7661
  if (!summary || summary.status !== "fail") {
7568
7662
  continue;
7569
7663
  }
@@ -7644,9 +7738,9 @@ function readTaskRunAcceptedArtifactState(input) {
7644
7738
  if (!input.taskId || !input.workspaceDir) {
7645
7739
  return { accepted: false, reason: null };
7646
7740
  }
7647
- const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7648
- const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7649
- const taskResultPath = resolve21(artifactDir, "task-result.json");
7741
+ const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7742
+ const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7743
+ const taskResultPath = resolve22(artifactDir, "task-result.json");
7650
7744
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7651
7745
  if (reviewStatus !== "APPROVED") {
7652
7746
  return { accepted: false, reason: null };
@@ -7683,12 +7777,12 @@ function resolveTaskRunRetryContext(input) {
7683
7777
  if (!input.taskId || !input.workspaceDir) {
7684
7778
  return { shouldRetry: false, failureDetail: null, nextPrompt: null };
7685
7779
  }
7686
- const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
7687
- const reviewStatePath = resolve21(artifactDir, "review-state.json");
7688
- const reviewFeedbackPath = resolve21(artifactDir, "review-feedback.md");
7689
- const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
7690
- const failedApproachesPath = resolve21(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7691
- const validationSummaryPath = resolve21(artifactDir, "validation-summary.json");
7780
+ const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7781
+ const reviewStatePath = resolve22(artifactDir, "review-state.json");
7782
+ const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
7783
+ const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7784
+ const failedApproachesPath = resolve22(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7785
+ const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
7692
7786
  const reviewState = readJsonFile3(reviewStatePath, null);
7693
7787
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7694
7788
  const reviewRejected = isTaskRunReviewRejected(reviewState);
@@ -7743,11 +7837,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
7743
7837
  return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
7744
7838
  }
7745
7839
  function readTaskRunReviewStatus(reviewStatusPath) {
7746
- if (!existsSync12(reviewStatusPath)) {
7840
+ if (!existsSync13(reviewStatusPath)) {
7747
7841
  return null;
7748
7842
  }
7749
7843
  try {
7750
- const status = readFileSync10(reviewStatusPath, "utf8").trim().toUpperCase();
7844
+ const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
7751
7845
  return status === "APPROVED" || status === "REJECTED" ? status : null;
7752
7846
  } catch {
7753
7847
  return null;
@@ -7831,7 +7925,7 @@ function stringArrayField(record, key) {
7831
7925
  async function executeRigOwnedTaskRun(context, input) {
7832
7926
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
7833
7927
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
7834
- const prompt = buildRunPrompt({
7928
+ let prompt = buildRunPrompt({
7835
7929
  projectRoot: context.projectRoot,
7836
7930
  taskId: input.taskId,
7837
7931
  fallbackTitle: input.title,
@@ -7931,7 +8025,22 @@ async function executeRigOwnedTaskRun(context, input) {
7931
8025
  const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
7932
8026
  const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
7933
8027
  const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
7934
- patchAuthorityRun(context.projectRoot, input.runId, { planning: planningClassification });
8028
+ const planningArtifactPath = resolve22("artifacts", runtimeTaskId, "implementation-plan.md");
8029
+ const persistedPlanning = {
8030
+ ...planningClassification,
8031
+ classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
8032
+ artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
8033
+ classifiedAt: new Date().toISOString()
8034
+ };
8035
+ mkdirSync8(resolve22(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
8036
+ writeFileSync6(resolve22(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
8037
+ `, "utf8");
8038
+ patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
8039
+ prompt = `${prompt}
8040
+
8041
+ Rig planning classification:
8042
+ ${JSON.stringify(persistedPlanning, null, 2)}
8043
+ ${planningClassification.planningRequired ? `Before implementing, write a concise implementation plan to ${planningArtifactPath}. Treat that plan artifact as required acceptance evidence for the Plan stage.` : "Planning is not required for this run; briefly state why before implementation."}`;
7935
8044
  if (input.runtimeAdapter === "pi") {
7936
8045
  for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
7937
8046
  appendPiStageLog({
@@ -7998,6 +8107,7 @@ async function executeRigOwnedTaskRun(context, input) {
7998
8107
  ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
7999
8108
  };
8000
8109
  Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
8110
+ Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
8001
8111
  const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
8002
8112
  const maxAttempts = automationLimits.maxValidationAttempts;
8003
8113
  const promoteToValidating = (detail) => {
@@ -8052,22 +8162,29 @@ async function executeRigOwnedTaskRun(context, input) {
8052
8162
  patchAuthorityRun(context.projectRoot, input.runId, {
8053
8163
  status: "running",
8054
8164
  worktreePath: latestRuntimeWorkspace,
8055
- artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve21(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8165
+ artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve22(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8056
8166
  logRoot: latestLogsDir,
8057
- sessionPath: latestSessionDir ? resolve21(latestSessionDir, "session.json") : null,
8058
- sessionLogPath: latestLogsDir ? resolve21(latestLogsDir, "agent-stdout.log") : null,
8167
+ sessionPath: latestSessionDir ? resolve22(latestSessionDir, "session.json") : null,
8168
+ sessionLogPath: latestLogsDir ? resolve22(latestLogsDir, "agent-stdout.log") : null,
8059
8169
  branch: runtimeId
8060
8170
  });
8061
8171
  if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
8062
8172
  dirtyBaselineApplied = true;
8063
8173
  const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
8174
+ const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
8175
+ if (readyFile) {
8176
+ mkdirSync8(resolve22(readyFile, ".."), { recursive: true });
8177
+ writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
8178
+ `, "utf8");
8179
+ }
8064
8180
  appendRunLog(context.projectRoot, input.runId, {
8065
8181
  id: `log:${input.runId}:dirty-baseline`,
8066
8182
  title: "Dirty baseline snapshot",
8067
8183
  detail: dirty.detail,
8068
8184
  tone: dirty.applied ? "tool" : "info",
8069
8185
  status: dirty.applied ? "completed" : "skipped",
8070
- createdAt: new Date().toISOString()
8186
+ createdAt: new Date().toISOString(),
8187
+ payload: readyFile ? { readyFile } : undefined
8071
8188
  });
8072
8189
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
8073
8190
  }
@@ -8334,7 +8451,7 @@ async function executeRigOwnedTaskRun(context, input) {
8334
8451
  let acceptedArtifactObservedAt = null;
8335
8452
  let acceptedArtifactPollTimer = null;
8336
8453
  let acceptedArtifactKillTimer = null;
8337
- const attemptExit = await new Promise((resolve22) => {
8454
+ const attemptExit = await new Promise((resolve23) => {
8338
8455
  let settled = false;
8339
8456
  const settle = (result) => {
8340
8457
  if (settled)
@@ -8342,7 +8459,7 @@ async function executeRigOwnedTaskRun(context, input) {
8342
8459
  settled = true;
8343
8460
  if (acceptedArtifactPollTimer)
8344
8461
  clearInterval(acceptedArtifactPollTimer);
8345
- resolve22(result);
8462
+ resolve23(result);
8346
8463
  };
8347
8464
  const pollAcceptedArtifacts = () => {
8348
8465
  const artifactState = readTaskRunAcceptedArtifactState({
@@ -8539,6 +8656,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8539
8656
  });
8540
8657
  throw new CliError2(terminalFailureDetail, exit.code ?? 1);
8541
8658
  }
8659
+ if (planningClassification.planningRequired) {
8660
+ const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
8661
+ const expectedPlanPath = resolve22(planWorkspace, planningArtifactPath);
8662
+ if (!existsSync13(expectedPlanPath)) {
8663
+ const failedAt = new Date().toISOString();
8664
+ const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
8665
+ patchAuthorityRun(context.projectRoot, input.runId, {
8666
+ status: "needs_attention",
8667
+ completedAt: failedAt,
8668
+ errorText: failureDetail
8669
+ });
8670
+ appendRunLog(context.projectRoot, input.runId, {
8671
+ id: `log:${input.runId}:plan-artifact-missing`,
8672
+ title: "Required plan artifact missing",
8673
+ detail: failureDetail,
8674
+ tone: "error",
8675
+ status: "needs_attention",
8676
+ createdAt: failedAt
8677
+ });
8678
+ emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
8679
+ throw new CliError2(failureDetail, 1);
8680
+ }
8681
+ }
8542
8682
  const runPiPrFeedbackFix = async (message2) => {
8543
8683
  appendPiStageLog({
8544
8684
  projectRoot: context.projectRoot,
@@ -8596,9 +8736,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8596
8736
  });
8597
8737
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
8598
8738
  });
8599
- const exitCode = await new Promise((resolve22) => {
8600
- child.once("error", () => resolve22(1));
8601
- child.once("close", (code) => resolve22(code ?? 1));
8739
+ const exitCode = await new Promise((resolve23) => {
8740
+ child.once("error", () => resolve23(1));
8741
+ child.once("close", (code) => resolve23(code ?? 1));
8602
8742
  });
8603
8743
  if (exitCode !== 0) {
8604
8744
  throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
@@ -8717,8 +8857,8 @@ async function executeTest(context, args) {
8717
8857
  }
8718
8858
 
8719
8859
  // packages/cli/src/commands/setup.ts
8720
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "fs";
8721
- import { resolve as resolve22 } from "path";
8860
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
8861
+ import { resolve as resolve23 } from "path";
8722
8862
  import { createPluginHost } from "@rig/core";
8723
8863
  import {
8724
8864
  isSupportedBunVersion as isSupportedBunVersion2,
@@ -8781,9 +8921,9 @@ function runSetupInit(projectRoot) {
8781
8921
  mkdirSync9(stateDir, { recursive: true });
8782
8922
  mkdirSync9(logsDir, { recursive: true });
8783
8923
  mkdirSync9(artifactsDir, { recursive: true });
8784
- const failuresPath = resolve22(stateDir, "failed_approaches.md");
8785
- if (!existsSync13(failuresPath)) {
8786
- writeFileSync6(failuresPath, `# Failed Approaches
8924
+ const failuresPath = resolve23(stateDir, "failed_approaches.md");
8925
+ if (!existsSync14(failuresPath)) {
8926
+ writeFileSync7(failuresPath, `# Failed Approaches
8787
8927
 
8788
8928
  `, "utf-8");
8789
8929
  }
@@ -8800,18 +8940,18 @@ async function runSetupCheck(projectRoot) {
8800
8940
  }
8801
8941
  async function runSetupPreflight(projectRoot) {
8802
8942
  await runSetupCheck(projectRoot);
8803
- const validationRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
8804
- if (existsSync13(validationRoot)) {
8943
+ const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
8944
+ if (existsSync14(validationRoot)) {
8805
8945
  const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
8806
8946
  for (const validator of validators) {
8807
- const script = resolve22(validationRoot, validator.name, "validate.sh");
8808
- if (existsSync13(script)) {
8947
+ const script = resolve23(validationRoot, validator.name, "validate.sh");
8948
+ if (existsSync14(script)) {
8809
8949
  console.log(`OK: validator script ${script}`);
8810
8950
  }
8811
8951
  }
8812
8952
  }
8813
- const hooksRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
8814
- if (existsSync13(hooksRoot)) {
8953
+ const hooksRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
8954
+ if (existsSync14(hooksRoot)) {
8815
8955
  const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
8816
8956
  for (const hook of hooks) {
8817
8957
  console.log(`OK: hook ${hook}`);
@@ -9205,8 +9345,8 @@ var __testOnly = {
9205
9345
  validateRequiredBugPromptValue
9206
9346
  };
9207
9347
  // packages/cli/src/launcher.ts
9208
- import { existsSync as existsSync14 } from "fs";
9209
- import { resolve as resolve23 } from "path";
9348
+ import { existsSync as existsSync15 } from "fs";
9349
+ import { resolve as resolve24 } from "path";
9210
9350
  import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
9211
9351
  import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
9212
9352
  function parsePolicyMode(value) {
@@ -9219,7 +9359,7 @@ function parsePolicyMode(value) {
9219
9359
  throw new Error(`Invalid --policy-mode value: ${value}. Use off|observe|enforce.`);
9220
9360
  }
9221
9361
  function hasRigProjectMarker(candidate) {
9222
- return existsSync14(resolve23(candidate, RIG_DEFINITION_DIRNAME)) || existsSync14(resolve23(candidate, RIG_STATE_DIRNAME)) || existsSync14(resolve23(candidate, "rig.config.ts")) || existsSync14(resolve23(candidate, "rig.config.json"));
9362
+ return existsSync15(resolve24(candidate, RIG_DEFINITION_DIRNAME)) || existsSync15(resolve24(candidate, RIG_STATE_DIRNAME)) || existsSync15(resolve24(candidate, "rig.config.ts")) || existsSync15(resolve24(candidate, "rig.config.json"));
9223
9363
  }
9224
9364
  function resolveProjectRoot({
9225
9365
  envProjectRoot,
@@ -9228,17 +9368,17 @@ function resolveProjectRoot({
9228
9368
  cwd = process.cwd()
9229
9369
  }) {
9230
9370
  if (envProjectRoot) {
9231
- return resolve23(cwd, envProjectRoot);
9371
+ return resolve24(cwd, envProjectRoot);
9232
9372
  }
9233
9373
  const fallbackImportDir = importDir ?? cwd;
9234
- const candidates = [cwd, resolve23(execPath, "..", ".."), resolve23(fallbackImportDir, "..")];
9374
+ const candidates = [cwd, resolve24(execPath, "..", ".."), resolve24(fallbackImportDir, "..")];
9235
9375
  for (const candidate of candidates) {
9236
9376
  const nearest = resolveNearestRigProjectRoot(candidate);
9237
9377
  if (hasRigProjectMarker(nearest)) {
9238
9378
  return nearest;
9239
9379
  }
9240
9380
  }
9241
- return resolve23(cwd);
9381
+ return resolve24(cwd);
9242
9382
  }
9243
9383
  function normalizeCliErrorCode(message2, isCliError) {
9244
9384
  if (message2.startsWith("Invalid --policy-mode value:")) {
@@ -9305,7 +9445,7 @@ async function runRigCli(module, options = {}) {
9305
9445
  runId: context.runId,
9306
9446
  outcome,
9307
9447
  eventsFile: context.eventBus.getEventsFile(),
9308
- policyFile: resolve23(projectRoot, "rig", "policy", "policy.json"),
9448
+ policyFile: resolve24(projectRoot, "rig", "policy", "policy.json"),
9309
9449
  policyMode: context.policyMode ?? policyMode ?? module.loadPolicy(projectRoot).mode
9310
9450
  }, null, 2));
9311
9451
  }