@h-rig/cli 0.0.6-alpha.13 → 0.0.6-alpha.15

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,17 +2671,17 @@ function resolveSelectedConnection(projectRoot, options = {}) {
2671
2671
  }
2672
2672
 
2673
2673
  // packages/cli/src/commands/_server-client.ts
2674
- import { spawnSync } from "child_process";
2675
2674
  import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
2676
2675
  import { resolve as resolve9 } from "path";
2677
2676
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
2678
- var cachedGitHubBearerToken;
2677
+ var scopedGitHubBearerTokens = new Map;
2679
2678
  function cleanToken(value) {
2680
2679
  const trimmed = value?.trim();
2681
2680
  return trimmed ? trimmed : null;
2682
2681
  }
2683
- function setGitHubBearerTokenForCurrentProcess(token) {
2684
- cachedGitHubBearerToken = cleanToken(token ?? undefined);
2682
+ function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
2683
+ const scopedKey = resolve9(projectRoot ?? process.cwd());
2684
+ scopedGitHubBearerTokens.set(scopedKey, cleanToken(token ?? undefined));
2685
2685
  }
2686
2686
  function readPrivateRemoteSessionToken(projectRoot) {
2687
2687
  const path = resolve9(projectRoot, ".rig", "state", "github-auth.json");
@@ -2695,25 +2695,13 @@ function readPrivateRemoteSessionToken(projectRoot) {
2695
2695
  }
2696
2696
  }
2697
2697
  function readGitHubBearerTokenForRemote(projectRoot) {
2698
- if (cachedGitHubBearerToken !== undefined)
2699
- return cachedGitHubBearerToken;
2698
+ const scopedKey = resolve9(projectRoot);
2699
+ if (scopedGitHubBearerTokens.has(scopedKey))
2700
+ return scopedGitHubBearerTokens.get(scopedKey) ?? null;
2700
2701
  const privateSession = readPrivateRemoteSessionToken(projectRoot);
2701
- if (privateSession) {
2702
- cachedGitHubBearerToken = privateSession;
2703
- return cachedGitHubBearerToken;
2704
- }
2705
- const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
2706
- if (envToken) {
2707
- cachedGitHubBearerToken = envToken;
2708
- return cachedGitHubBearerToken;
2709
- }
2710
- const result = spawnSync("gh", ["auth", "token"], {
2711
- encoding: "utf8",
2712
- timeout: 5000,
2713
- stdio: ["ignore", "pipe", "ignore"]
2714
- });
2715
- cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
2716
- return cachedGitHubBearerToken;
2702
+ if (privateSession)
2703
+ return privateSession;
2704
+ return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
2717
2705
  }
2718
2706
  async function ensureServerForCli(projectRoot) {
2719
2707
  try {
@@ -2911,6 +2899,37 @@ async function getRunLogsViaServer(context, runId, options = {}) {
2911
2899
  const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
2912
2900
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { entries: [] };
2913
2901
  }
2902
+ async function getRunTimelineViaServer(context, runId, options = {}) {
2903
+ const url = new URL(`http://rig.local/api/runs/${encodeURIComponent(runId)}/timeline`);
2904
+ if (options.limit !== undefined)
2905
+ url.searchParams.set("limit", String(options.limit));
2906
+ if (options.cursor)
2907
+ url.searchParams.set("cursor", options.cursor);
2908
+ const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
2909
+ return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { entries: [] };
2910
+ }
2911
+ async function ensureTaskLabelsViaServer(context) {
2912
+ const payload = await requestServerJson(context, "/api/workspace/task-labels", { method: "POST" });
2913
+ return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2914
+ }
2915
+ async function listGitHubProjectsViaServer(context, owner) {
2916
+ const url = new URL("http://rig.local/api/github/projects");
2917
+ url.searchParams.set("owner", owner);
2918
+ const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
2919
+ return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { projects: [] };
2920
+ }
2921
+ async function getGitHubProjectStatusFieldViaServer(context, projectId) {
2922
+ const payload = await requestServerJson(context, `/api/github/projects/${encodeURIComponent(projectId)}/status-field`);
2923
+ return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
2924
+ }
2925
+ async function updateWorkspaceTaskViaServer(context, input) {
2926
+ const payload = await requestServerJson(context, "/api/tasks/update", {
2927
+ method: "POST",
2928
+ headers: { "content-type": "application/json" },
2929
+ body: JSON.stringify(input)
2930
+ });
2931
+ return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
2932
+ }
2914
2933
  async function stopRunViaServer(context, runId) {
2915
2934
  const payload = await requestServerJson(context, "/api/runs/stop", {
2916
2935
  method: "POST",
@@ -3190,6 +3209,9 @@ function permissionAllowsPr(payload) {
3190
3209
  }
3191
3210
  return null;
3192
3211
  }
3212
+ function isNotFoundError(error) {
3213
+ return /\b(404|not found)\b/i.test(message(error));
3214
+ }
3193
3215
  function projectCheckoutReady(payload) {
3194
3216
  if (!payload || typeof payload !== "object" || Array.isArray(payload))
3195
3217
  return null;
@@ -3222,19 +3244,33 @@ async function runFastTaskRunPreflight(context, options = {}) {
3222
3244
  const checks = [];
3223
3245
  const request = options.requestJson ?? ((pathname, init) => requestServerJson(context, pathname, init));
3224
3246
  const taskId = options.taskId?.trim() || null;
3247
+ const requiresCurrentRunApi = Boolean(taskId);
3248
+ const selectedServer = options.requestJson ? null : await ensureServerForCli(context.projectRoot).catch(() => null);
3249
+ const allowLocalLegacyTaskRunCompatibility = selectedServer?.connectionKind === "local";
3250
+ let legacyServerCompatibility = false;
3225
3251
  try {
3226
3252
  await request("/api/server/status");
3227
3253
  checks.push(preflightCheck("server", "Rig server reachable", "pass"));
3228
3254
  } catch (error) {
3229
- checks.push(preflightCheck("server", "Rig server reachable", "fail", message(error), "Start or select a reachable Rig server."));
3255
+ if (isNotFoundError(error)) {
3256
+ try {
3257
+ await request("/health");
3258
+ legacyServerCompatibility = !requiresCurrentRunApi || allowLocalLegacyTaskRunCompatibility;
3259
+ checks.push(requiresCurrentRunApi && !allowLocalLegacyTaskRunCompatibility ? preflightCheck("server", "Rig server reachable", "fail", "legacy /health endpoint only; current task-run APIs are required", "Upgrade/select the Rig server before launching a task run.") : preflightCheck("server", "Rig server reachable", "pass", allowLocalLegacyTaskRunCompatibility ? "local legacy /health endpoint; submit endpoint will be authoritative" : "legacy /health endpoint"));
3260
+ } catch (healthError) {
3261
+ checks.push(preflightCheck("server", "Rig server reachable", "fail", message(healthError), "Start or select a reachable Rig server."));
3262
+ }
3263
+ } else {
3264
+ checks.push(preflightCheck("server", "Rig server reachable", "fail", message(error), "Start or select a reachable Rig server."));
3265
+ }
3230
3266
  }
3231
3267
  const repo = readRepoConnection(context.projectRoot);
3232
- checks.push(repo ? preflightCheck("project-link", "project linked to Rig connection", repo.project ? "pass" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : ""}`, "Run `rig init --yes --repo owner/repo` to record the GitHub repo slug.") : preflightCheck("project-link", "project linked to Rig connection", "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig connect use <alias|local>`."));
3268
+ checks.push(repo ? preflightCheck("project-link", "project linked to Rig connection", repo.project ? "pass" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : ""}`, "Run `rig init --yes --repo owner/repo` to record the GitHub repo slug.") : preflightCheck("project-link", "project linked to Rig connection", legacyServerCompatibility ? "warn" : "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig connect use <alias|local>`."));
3233
3269
  try {
3234
3270
  const auth = await request("/api/github/auth/status");
3235
- checks.push(isAuthenticated(auth) ? preflightCheck("github-auth", "GitHub auth valid", "pass") : preflightCheck("github-auth", "GitHub auth valid", "fail", "not authenticated", "Run `rig github auth import-gh` or `rig github auth token --token <token>`."));
3271
+ checks.push(isAuthenticated(auth) ? preflightCheck("github-auth", "GitHub auth valid", "pass") : preflightCheck("github-auth", "GitHub auth valid", legacyServerCompatibility ? "warn" : "fail", "not authenticated", "Run `rig github auth import-gh` or `rig github auth token --token <token>`."));
3236
3272
  } catch (error) {
3237
- checks.push(preflightCheck("github-auth", "GitHub auth valid", "fail", message(error), "Fix GitHub auth on the selected Rig server."));
3273
+ checks.push(preflightCheck("github-auth", "GitHub auth valid", legacyServerCompatibility ? "warn" : "fail", message(error), "Fix GitHub auth on the selected Rig server."));
3238
3274
  }
3239
3275
  try {
3240
3276
  const projection = await request("/api/workspace/task-projection");
@@ -3262,9 +3298,9 @@ async function runFastTaskRunPreflight(context, options = {}) {
3262
3298
  try {
3263
3299
  const tasks = await request(`/api/workspace/tasks?limit=200&refresh=1`);
3264
3300
  const found = Array.isArray(tasks) && tasks.some((task) => taskMatchesId(task, taskId));
3265
- checks.push(found ? preflightCheck("issue", "task/issue accessible", "pass", taskId) : preflightCheck("issue", "task/issue accessible", "fail", taskId, "Confirm the issue exists and matches the configured task filters."));
3301
+ checks.push(found ? preflightCheck("issue", "task/issue accessible", "pass", taskId) : preflightCheck("issue", "task/issue accessible", legacyServerCompatibility ? "warn" : "fail", taskId, "Confirm the issue exists and matches the configured task filters."));
3266
3302
  } catch (error) {
3267
- checks.push(preflightCheck("issue", "task/issue accessible", "fail", message(error), "Fix the task source before launching a run."));
3303
+ checks.push(preflightCheck("issue", "task/issue accessible", legacyServerCompatibility ? "warn" : "fail", message(error), "Fix the task source before launching a run."));
3268
3304
  }
3269
3305
  try {
3270
3306
  const runs = await request("/api/runs?limit=200");
@@ -4176,9 +4212,10 @@ async function executeInbox(context, args) {
4176
4212
 
4177
4213
  // packages/cli/src/commands/init.ts
4178
4214
  import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
4179
- import { spawnSync as spawnSync2 } from "child_process";
4215
+ import { spawnSync } from "child_process";
4180
4216
  import { resolve as resolve17 } from "path";
4181
4217
  import { buildRigInitConfigSource } from "@rig/core";
4218
+ import { listGitHubProjects as listGitHubProjectsDirect, resolveProjectStatusField as resolveProjectStatusFieldDirect } from "@rig/server";
4182
4219
 
4183
4220
  // packages/cli/src/commands/_snapshot-upload.ts
4184
4221
  import { mkdir, readdir, readFile, writeFile } from "fs/promises";
@@ -4502,7 +4539,7 @@ function parseRepoSlugFromRemote(remoteUrl) {
4502
4539
  return gitHubMatch ? `${gitHubMatch[1]}/${gitHubMatch[2]}` : null;
4503
4540
  }
4504
4541
  function detectOriginRepoSlug(projectRoot) {
4505
- const result = spawnSync2("git", ["-C", projectRoot, "remote", "get-url", "origin"], { encoding: "utf8" });
4542
+ const result = spawnSync("git", ["-C", projectRoot, "remote", "get-url", "origin"], { encoding: "utf8" });
4506
4543
  if (result.status !== 0)
4507
4544
  return null;
4508
4545
  return parseRepoSlugFromRemote(result.stdout.trim());
@@ -4558,11 +4595,14 @@ function applyGitHubProjectConfig(source, options) {
4558
4595
  return source;
4559
4596
  const projectId = JSON.stringify(options.githubProject);
4560
4597
  const statusFieldId = JSON.stringify(options.githubProjectStatusField ?? "Status");
4598
+ const statuses = options.githubProjectStatuses && Object.keys(options.githubProjectStatuses).length > 0 ? `
4599
+ statuses: ${JSON.stringify(options.githubProjectStatuses, null, 8).replace(/\n/g, `
4600
+ `)},` : "";
4561
4601
  return source.replace(` projects: { enabled: false },`, [
4562
4602
  ` projects: {`,
4563
4603
  ` enabled: true,`,
4564
4604
  ` projectId: ${projectId},`,
4565
- ` statusFieldId: ${statusFieldId},`,
4605
+ ` statusFieldId: ${statusFieldId},${statuses}`,
4566
4606
  ` },`
4567
4607
  ].join(`
4568
4608
  `));
@@ -4583,11 +4623,11 @@ function checkoutForInit(projectRoot, serverKind, strategy) {
4583
4623
  }
4584
4624
  }
4585
4625
  function detectGhLogin() {
4586
- const result = spawnSync2("gh", ["api", "user", "--jq", ".login"], { encoding: "utf8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"] });
4626
+ const result = spawnSync("gh", ["api", "user", "--jq", ".login"], { encoding: "utf8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"] });
4587
4627
  return result.status === 0 && result.stdout.trim() ? result.stdout.trim() : null;
4588
4628
  }
4589
4629
  function readGhAuthToken() {
4590
- const result = spawnSync2("gh", ["auth", "token"], { encoding: "utf8" });
4630
+ const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
4591
4631
  if (result.status !== 0 || !result.stdout.trim()) {
4592
4632
  throw new CliError2(result.stderr.trim() || "Could not read GitHub token from `gh auth token`.", result.status || 1);
4593
4633
  }
@@ -4596,8 +4636,15 @@ function readGhAuthToken() {
4596
4636
  async function loadClackPrompts() {
4597
4637
  return await import("@clack/prompts");
4598
4638
  }
4639
+ function clackTextOptions(options) {
4640
+ return {
4641
+ message: options.message,
4642
+ ...options.placeholder ? { placeholder: options.placeholder } : {},
4643
+ ...(options.initialValue ?? options.defaultValue)?.trim() ? { initialValue: (options.initialValue ?? options.defaultValue).trim() } : {}
4644
+ };
4645
+ }
4599
4646
  async function promptRequiredText(prompts, options) {
4600
- const value = await prompts.text(options);
4647
+ const value = await prompts.text(clackTextOptions(options));
4601
4648
  if (prompts.isCancel(value))
4602
4649
  throw new CliError2("Init cancelled.", 1);
4603
4650
  const text2 = String(value ?? "").trim();
@@ -4606,7 +4653,7 @@ async function promptRequiredText(prompts, options) {
4606
4653
  return text2;
4607
4654
  }
4608
4655
  async function promptOptionalText(prompts, options) {
4609
- const value = await prompts.text(options);
4656
+ const value = await prompts.text(clackTextOptions(options));
4610
4657
  if (prompts.isCancel(value))
4611
4658
  throw new CliError2("Init cancelled.", 1);
4612
4659
  return String(value ?? "").trim();
@@ -4617,6 +4664,132 @@ async function promptSelect(prompts, options) {
4617
4664
  throw new CliError2("Init cancelled.", 1);
4618
4665
  return String(value);
4619
4666
  }
4667
+ function repoOwnerFromSlug(repoSlug) {
4668
+ return repoSlug.trim().match(/^([^/]+)\/[^/]+$/)?.[1] ?? null;
4669
+ }
4670
+ function recordArray(value, key) {
4671
+ if (!value || typeof value !== "object" || Array.isArray(value))
4672
+ return [];
4673
+ const raw = value[key];
4674
+ return Array.isArray(raw) ? raw.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
4675
+ }
4676
+ async function listGitHubProjectsForInit(context, owner, token) {
4677
+ if (token?.trim()) {
4678
+ try {
4679
+ return { ok: true, projects: await listGitHubProjectsDirect({ owner, token: token.trim() }) };
4680
+ } catch (directError) {
4681
+ const serverPayload = await listGitHubProjectsViaServer(context, owner).catch(() => null);
4682
+ if (recordArray(serverPayload, "projects").length > 0)
4683
+ return serverPayload;
4684
+ return { ok: false, error: directError instanceof Error ? directError.message : String(directError), projects: [] };
4685
+ }
4686
+ }
4687
+ return listGitHubProjectsViaServer(context, owner);
4688
+ }
4689
+ async function getGitHubProjectStatusFieldForInit(context, projectId, token) {
4690
+ if (token?.trim()) {
4691
+ try {
4692
+ return { ok: true, field: await resolveProjectStatusFieldDirect({ projectId, token: token.trim() }) };
4693
+ } catch (directError) {
4694
+ const serverPayload = await getGitHubProjectStatusFieldViaServer(context, projectId).catch(() => null);
4695
+ if (serverPayload && typeof serverPayload === "object" && !Array.isArray(serverPayload) && "field" in serverPayload) {
4696
+ return serverPayload;
4697
+ }
4698
+ return { ok: false, error: directError instanceof Error ? directError.message : String(directError) };
4699
+ }
4700
+ }
4701
+ return getGitHubProjectStatusFieldViaServer(context, projectId);
4702
+ }
4703
+ var PROJECT_STATUS_PROMPTS = {
4704
+ running: "Running/In progress",
4705
+ prOpen: "PR open/review",
4706
+ ciFixing: "CI/review fixing",
4707
+ merging: "Merging",
4708
+ done: "Done",
4709
+ needsAttention: "Needs attention"
4710
+ };
4711
+ var DEFAULT_PROJECT_STATUS_OPTIONS = {
4712
+ running: "In Progress",
4713
+ prOpen: "In Review",
4714
+ ciFixing: "In Review",
4715
+ merging: "Merging",
4716
+ done: "Done",
4717
+ needsAttention: "Needs Attention"
4718
+ };
4719
+ async function promptManualProjectStatusMapping(prompts) {
4720
+ const statuses = {};
4721
+ for (const [key, label] of Object.entries(PROJECT_STATUS_PROMPTS)) {
4722
+ const defaultLabel = DEFAULT_PROJECT_STATUS_OPTIONS[key] ?? label;
4723
+ const value = await promptOptionalText(prompts, {
4724
+ message: `Project status option id/name for ${label} (blank for ${defaultLabel})`,
4725
+ placeholder: defaultLabel
4726
+ });
4727
+ statuses[key] = value || defaultLabel;
4728
+ }
4729
+ return statuses;
4730
+ }
4731
+ async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken) {
4732
+ const projectChoice = await promptSelect(prompts, {
4733
+ message: "GitHub Projects status sync",
4734
+ options: [
4735
+ { value: "off", label: "Off" },
4736
+ { value: "select", label: "Select accessible ProjectV2" },
4737
+ { value: "manual", label: "Enter ProjectV2 ids manually" }
4738
+ ]
4739
+ });
4740
+ if (projectChoice === "off")
4741
+ return { githubProject: "off" };
4742
+ if (projectChoice === "manual") {
4743
+ return {
4744
+ githubProject: await promptRequiredText(prompts, { message: "GitHub ProjectV2 id", placeholder: "PVT_..." }),
4745
+ githubProjectStatusField: await promptRequiredText(prompts, { message: "Project Status field id", placeholder: "field_status" }),
4746
+ githubProjectStatuses: await promptManualProjectStatusMapping(prompts)
4747
+ };
4748
+ }
4749
+ const owner = repoOwnerFromSlug(repoSlug);
4750
+ if (!owner)
4751
+ throw new CliError2(`Cannot derive GitHub owner from repo slug ${repoSlug}.`, 1);
4752
+ const projectsPayload = await listGitHubProjectsForInit(context, owner, githubToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error), projects: [] }));
4753
+ const projects = recordArray(projectsPayload, "projects");
4754
+ if (projects.length === 0) {
4755
+ const error = typeof projectsPayload.error === "string" ? ` (${projectsPayload.error})` : "";
4756
+ prompts.outro?.(`No accessible GitHub Projects were returned${error}; falling back to manual ProjectV2 ids.`);
4757
+ return {
4758
+ githubProject: await promptRequiredText(prompts, { message: "GitHub ProjectV2 id", placeholder: "PVT_..." }),
4759
+ githubProjectStatusField: await promptRequiredText(prompts, { message: "Project Status field id", placeholder: "field_status" }),
4760
+ githubProjectStatuses: await promptManualProjectStatusMapping(prompts)
4761
+ };
4762
+ }
4763
+ const selectedProjectId = await promptSelect(prompts, {
4764
+ message: "GitHub ProjectV2 project",
4765
+ options: [
4766
+ ...projects.map((project) => ({
4767
+ value: String(project.id),
4768
+ label: `${String(project.title ?? "Untitled project")} (#${String(project.number ?? "?")})`,
4769
+ hint: typeof project.url === "string" ? project.url : undefined
4770
+ })),
4771
+ { value: "manual", label: "Enter ProjectV2 id manually" }
4772
+ ]
4773
+ });
4774
+ const projectId = selectedProjectId === "manual" ? await promptRequiredText(prompts, { message: "GitHub ProjectV2 id", placeholder: "PVT_..." }) : selectedProjectId;
4775
+ const fieldPayload = await getGitHubProjectStatusFieldForInit(context, projectId, githubToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error) }));
4776
+ const fieldPayloadRecord = fieldPayload && typeof fieldPayload === "object" && !Array.isArray(fieldPayload) ? fieldPayload : {};
4777
+ const rawField = fieldPayloadRecord.field;
4778
+ const field = rawField && typeof rawField === "object" && !Array.isArray(rawField) ? rawField : null;
4779
+ const fieldId = typeof field?.id === "string" && field.id.trim() ? field.id : await promptRequiredText(prompts, { message: "Project Status field id", placeholder: "field_status" });
4780
+ const options = Array.isArray(field?.options) ? field.options.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
4781
+ if (options.length === 0) {
4782
+ return { githubProject: projectId, githubProjectStatusField: fieldId, githubProjectStatuses: await promptManualProjectStatusMapping(prompts) };
4783
+ }
4784
+ const statuses = {};
4785
+ for (const [key, label] of Object.entries(PROJECT_STATUS_PROMPTS)) {
4786
+ statuses[key] = await promptSelect(prompts, {
4787
+ message: `Project status option for ${label}`,
4788
+ options: options.map((option) => ({ value: String(option.id ?? option.name), label: String(option.name ?? option.id) }))
4789
+ });
4790
+ }
4791
+ return { githubProject: projectId, githubProjectStatusField: fieldId, githubProjectStatuses: Object.keys(statuses).length > 0 ? statuses : undefined };
4792
+ }
4620
4793
  function sleep2(ms) {
4621
4794
  return new Promise((resolve18) => setTimeout(resolve18, ms));
4622
4795
  }
@@ -4745,7 +4918,7 @@ async function runControlPlaneInit(context, options) {
4745
4918
  if (token) {
4746
4919
  githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
4747
4920
  const apiSessionToken = apiSessionTokenFrom(githubAuth);
4748
- setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4921
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token, projectRoot);
4749
4922
  if (serverKind === "remote") {
4750
4923
  writeRemoteGitHubAuthState(projectRoot, {
4751
4924
  source: authMethod === "gh" ? "gh" : "init-token",
@@ -4770,7 +4943,7 @@ async function runControlPlaneInit(context, options) {
4770
4943
  if (completed) {
4771
4944
  const apiSessionToken = apiSessionTokenFrom(completed);
4772
4945
  if (apiSessionToken) {
4773
- setGitHubBearerTokenForCurrentProcess(apiSessionToken);
4946
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken, projectRoot);
4774
4947
  if (serverKind === "remote") {
4775
4948
  writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken, authPayload: completed });
4776
4949
  }
@@ -4794,7 +4967,7 @@ async function runControlPlaneInit(context, options) {
4794
4967
  if (serverKind === "remote" && checkoutPath && token) {
4795
4968
  githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath });
4796
4969
  const apiSessionToken = apiSessionTokenFrom(githubAuth);
4797
- setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
4970
+ setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token, projectRoot);
4798
4971
  writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken, authPayload: githubAuth });
4799
4972
  }
4800
4973
  const registered = await registerProjectViaServer(context, {
@@ -4803,6 +4976,12 @@ async function runControlPlaneInit(context, options) {
4803
4976
  });
4804
4977
  const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
4805
4978
  const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
4979
+ const labelSetup = await ensureTaskLabelsViaServer(context).catch((error) => ({
4980
+ ok: false,
4981
+ ready: false,
4982
+ labelsReady: false,
4983
+ error: error instanceof Error ? error.message : String(error)
4984
+ }));
4806
4985
  const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
4807
4986
  remote: true,
4808
4987
  pi: { ok: false, label: "pi", hint: error instanceof Error ? error.message : String(error) },
@@ -4833,6 +5012,7 @@ async function runControlPlaneInit(context, options) {
4833
5012
  githubAuth,
4834
5013
  deviceAuth,
4835
5014
  githubAuthWarning: remoteGhTokenWarning,
5015
+ labelSetup,
4836
5016
  pi,
4837
5017
  doctor
4838
5018
  };
@@ -4987,24 +5167,17 @@ async function runInteractiveControlPlaneInit(context, prompts) {
4987
5167
  throw new CliError2("Remote gh-token import cancelled.", 1);
4988
5168
  }
4989
5169
  }
4990
- const githubToken = authMethod === "token" ? await promptRequiredText(prompts, { message: "GitHub token", placeholder: "ghp_..." }) : undefined;
4991
- const projectChoice = await promptSelect(prompts, {
4992
- message: "GitHub Projects status sync",
4993
- options: [
4994
- { value: "off", label: "Off" },
4995
- { value: "configure", label: "Configure ProjectV2 status field" }
4996
- ]
4997
- });
4998
- const githubProject = projectChoice === "configure" ? await promptRequiredText(prompts, { message: "GitHub ProjectV2 id", placeholder: "PVT_..." }) : "off";
4999
- const githubProjectStatusField = projectChoice === "configure" ? await promptRequiredText(prompts, { message: "Project Status field id", placeholder: "field_status" }) : undefined;
5170
+ const githubToken = authMethod === "token" ? await promptRequiredText(prompts, { message: "GitHub token", placeholder: "ghp_..." }) : authMethod === "gh" ? readGhAuthToken() : undefined;
5171
+ const projectConfig = await promptGitHubProjectConfig(context, prompts, repoSlug, githubToken);
5000
5172
  const result = await runControlPlaneInit(context, {
5001
5173
  server: serverChoice,
5002
5174
  remoteUrl,
5003
5175
  repoSlug,
5004
5176
  githubToken,
5005
5177
  githubAuthMethod: authMethod,
5006
- githubProject,
5007
- githubProjectStatusField,
5178
+ githubProject: projectConfig.githubProject,
5179
+ githubProjectStatusField: projectConfig.githubProjectStatusField,
5180
+ githubProjectStatuses: projectConfig.githubProjectStatuses,
5008
5181
  remoteCheckout,
5009
5182
  repair,
5010
5183
  privateStateOnly
@@ -5103,7 +5276,7 @@ Usage: rig connect <list|add|use|status>`, 1);
5103
5276
  }
5104
5277
 
5105
5278
  // packages/cli/src/commands/github.ts
5106
- import { spawnSync as spawnSync3 } from "child_process";
5279
+ import { spawnSync as spawnSync2 } from "child_process";
5107
5280
  function printPayload(context, payload, fallback) {
5108
5281
  if (context.outputMode === "json")
5109
5282
  console.log(JSON.stringify(payload, null, 2));
@@ -5111,7 +5284,7 @@ function printPayload(context, payload, fallback) {
5111
5284
  console.log(fallback);
5112
5285
  }
5113
5286
  function readGhToken() {
5114
- const result = spawnSync3("gh", ["auth", "token"], { encoding: "utf8" });
5287
+ const result = spawnSync2("gh", ["auth", "token"], { encoding: "utf8" });
5115
5288
  if (result.status !== 0) {
5116
5289
  const detail = result.stderr?.trim() || result.stdout?.trim() || "gh auth token failed";
5117
5290
  throw new CliError2(`Could not import GitHub token from gh: ${detail}`, 1);
@@ -6143,14 +6316,10 @@ async function executeRemote(context, args) {
6143
6316
  }
6144
6317
 
6145
6318
  // packages/cli/src/commands/run.ts
6146
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
6147
- import { resolve as resolve20 } from "path";
6148
6319
  import { createInterface as createInterface2 } from "readline/promises";
6149
6320
  import {
6150
6321
  listAuthorityRuns as listAuthorityRuns3,
6151
- readAuthorityRun as readAuthorityRun4,
6152
- readJsonlFile as readJsonlFile4,
6153
- resolveAuthorityRunDir as resolveAuthorityRunDir5
6322
+ readAuthorityRun as readAuthorityRun4
6154
6323
  } from "@rig/runtime/control-plane/authority-files";
6155
6324
  import {
6156
6325
  cleanupRunState,
@@ -6166,9 +6335,9 @@ import {
6166
6335
  } from "@rig/runtime/control-plane/native/run-ops";
6167
6336
  import { loadRuntimeContextFromEnv as loadRuntimeContextFromEnv2 } from "@rig/runtime/control-plane/runtime/context";
6168
6337
 
6169
- // packages/cli/src/commands/_operator-view.ts
6338
+ // packages/cli/src/commands/_operator-surface.ts
6170
6339
  import { createInterface } from "readline";
6171
- var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
6340
+ import { createInterface as createPromptInterface } from "readline/promises";
6172
6341
  var CANONICAL_STAGES = [
6173
6342
  "Connect",
6174
6343
  "GitHub/task sync",
@@ -6183,18 +6352,141 @@ var CANONICAL_STAGES = [
6183
6352
  "Merge",
6184
6353
  "Complete"
6185
6354
  ];
6355
+ function logDetail(log3) {
6356
+ return typeof log3.detail === "string" ? log3.detail.trim() : "";
6357
+ }
6358
+ function entryId(entry, fallback) {
6359
+ return typeof entry.id === "string" && entry.id.trim() ? entry.id : fallback;
6360
+ }
6186
6361
  function renderOperatorSnapshot(snapshot) {
6187
6362
  const run = snapshot.run.run && typeof snapshot.run.run === "object" ? snapshot.run.run : snapshot.run;
6188
6363
  const runId = String(run.runId ?? run.id ?? "run");
6189
6364
  const status = String(run.status ?? "unknown");
6190
6365
  const logs = snapshot.logs ?? [];
6366
+ const latestByStage = new Map;
6367
+ for (const log3 of logs) {
6368
+ const title = String(log3.title ?? "").toLowerCase();
6369
+ const stageName = String(log3.stage ?? "").toLowerCase();
6370
+ const stage = CANONICAL_STAGES.find((candidate) => candidate.toLowerCase() === title || candidate.toLowerCase() === stageName);
6371
+ if (stage)
6372
+ latestByStage.set(stage, log3);
6373
+ }
6191
6374
  const stageLines = CANONICAL_STAGES.flatMap((stage) => {
6192
- const match = logs.find((log3) => String(log3.title ?? "").toLowerCase() === stage.toLowerCase() || String(log3.stage ?? "").toLowerCase() === stage.toLowerCase());
6193
- return match ? [`${stage}: ${String(match.status ?? status)}`] : [];
6375
+ const match = latestByStage.get(stage);
6376
+ return match ? [`${stage}: ${String(match.status ?? status)}${logDetail(match) ? ` \u2014 ${logDetail(match)}` : ""}`] : [];
6194
6377
  });
6195
6378
  return [`Rig run ${runId}: ${status}`, ...stageLines].join(`
6196
6379
  `);
6197
6380
  }
6381
+ function createPiRunStreamRenderer(output = process.stdout) {
6382
+ let lastSnapshot = "";
6383
+ const assistantTextById = new Map;
6384
+ const seenTimeline = new Set;
6385
+ const seenLogs = new Set;
6386
+ const writeLine = (line) => output.write(`${line}
6387
+ `);
6388
+ return {
6389
+ renderSnapshot(snapshot) {
6390
+ const rendered = renderOperatorSnapshot(snapshot);
6391
+ if (rendered && rendered !== lastSnapshot) {
6392
+ writeLine(rendered);
6393
+ lastSnapshot = rendered;
6394
+ }
6395
+ },
6396
+ renderTimeline(entries) {
6397
+ for (const [index, entry] of entries.entries()) {
6398
+ const id = entryId(entry, `timeline:${index}:${String(entry.cursor ?? "")}`);
6399
+ if (entry.type === "assistant_message" && typeof entry.text === "string") {
6400
+ const text2 = entry.text;
6401
+ const previousText = assistantTextById.get(id) ?? "";
6402
+ if (text2.startsWith(previousText)) {
6403
+ const delta = text2.slice(previousText.length);
6404
+ if (delta)
6405
+ output.write(delta);
6406
+ } else if (text2.trim() && text2 !== previousText) {
6407
+ writeLine(`
6408
+ [Pi assistant]`);
6409
+ output.write(text2);
6410
+ }
6411
+ assistantTextById.set(id, text2);
6412
+ continue;
6413
+ }
6414
+ if (seenTimeline.has(id))
6415
+ continue;
6416
+ seenTimeline.add(id);
6417
+ if (entry.type === "tool_execution_start" || entry.type === "tool_execution_update" || entry.type === "tool_execution_end" || entry.type === "mcp_tool_call") {
6418
+ writeLine(`[Pi tool] ${String(entry.toolName ?? entry.name ?? entry.title ?? entry.type)} ${String(entry.status ?? entry.state ?? "")}`.trim());
6419
+ continue;
6420
+ }
6421
+ if (entry.type === "timeline_warning") {
6422
+ writeLine(`[Rig timeline] ${String(entry.detail ?? entry.message ?? "timeline unavailable")}`);
6423
+ }
6424
+ }
6425
+ },
6426
+ renderLogs(entries) {
6427
+ for (const [index, entry] of entries.entries()) {
6428
+ const id = entryId(entry, `log:${index}:${String(entry.createdAt ?? "")}:${String(entry.title ?? "")}`);
6429
+ if (seenLogs.has(id))
6430
+ continue;
6431
+ seenLogs.add(id);
6432
+ const title = String(entry.title ?? "");
6433
+ if (CANONICAL_STAGES.some((stage) => stage.toLowerCase() === title.toLowerCase()))
6434
+ continue;
6435
+ const detail = logDetail(entry);
6436
+ if (!detail)
6437
+ continue;
6438
+ writeLine(`[${title || "Rig log"}] ${detail}`);
6439
+ }
6440
+ }
6441
+ };
6442
+ }
6443
+ function createOperatorSurface(options = {}) {
6444
+ const input = options.input ?? process.stdin;
6445
+ const output = options.output ?? process.stdout;
6446
+ const errorOutput = options.errorOutput ?? process.stderr;
6447
+ const renderer = createPiRunStreamRenderer(output);
6448
+ const writeLine = (line) => output.write(`${line}
6449
+ `);
6450
+ return {
6451
+ mode: "pi-compatible-text",
6452
+ ...renderer,
6453
+ info: writeLine,
6454
+ error: (message2) => errorOutput.write(`${message2}
6455
+ `),
6456
+ attachCommandInput(handler) {
6457
+ if (options.interactive === false || !input.isTTY)
6458
+ return null;
6459
+ const rl = createInterface({ input, output: process.stdout, terminal: false });
6460
+ rl.on("line", (line) => {
6461
+ Promise.resolve(handler(line)).catch((error) => writeLine(`Operator command failed: ${error instanceof Error ? error.message : String(error)}`));
6462
+ });
6463
+ return { close: () => rl.close() };
6464
+ }
6465
+ };
6466
+ }
6467
+ function taskId(task) {
6468
+ return typeof task.id === "string" && task.id.trim() ? task.id : "<unknown>";
6469
+ }
6470
+ function taskTitle(task) {
6471
+ return typeof task.title === "string" && task.title.trim() ? task.title : "Untitled task";
6472
+ }
6473
+ function taskStatus(task) {
6474
+ return typeof task.status === "string" && task.status.trim() ? task.status : "unknown";
6475
+ }
6476
+ function renderTaskPickerRows(tasks) {
6477
+ return tasks.map((task, index) => `${index + 1}. ${taskId(task)} \xB7 ${taskStatus(task)} \xB7 ${taskTitle(task)}`);
6478
+ }
6479
+ async function promptForTaskSelection(question) {
6480
+ const rl = createPromptInterface({ input: process.stdin, output: process.stdout });
6481
+ try {
6482
+ return await rl.question(question);
6483
+ } finally {
6484
+ rl.close();
6485
+ }
6486
+ }
6487
+
6488
+ // packages/cli/src/commands/_operator-view.ts
6489
+ var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
6198
6490
  function runStatusFromPayload(payload) {
6199
6491
  const run = payload.run && typeof payload.run === "object" && !Array.isArray(payload.run) ? payload.run : payload;
6200
6492
  return String(run.status ?? "unknown").toLowerCase();
@@ -6216,11 +6508,22 @@ async function applyOperatorCommand(context, input, deps = {}) {
6216
6508
  await (deps.steer ?? steerRunViaServer)(context, input.runId, userMessage);
6217
6509
  return { action: "continue", message: "Steering message queued." };
6218
6510
  }
6219
- async function readOperatorSnapshot(context, runId) {
6511
+ async function readOperatorSnapshot(context, runId, options = {}) {
6220
6512
  const run = await getRunDetailsViaServer(context, runId);
6221
6513
  const logsPage = await getRunLogsViaServer(context, runId, { limit: 100 });
6222
- const entries = Array.isArray(logsPage.entries) ? logsPage.entries.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
6223
- return { run, logs: entries, rendered: renderOperatorSnapshot({ run, logs: entries }) };
6514
+ const timelinePage = await getRunTimelineViaServer(context, runId, { limit: 200, ...options.timelineCursor ? { cursor: options.timelineCursor } : {} }).catch((error) => ({
6515
+ entries: [{
6516
+ id: `timeline-unavailable:${runId}`,
6517
+ type: "timeline_warning",
6518
+ detail: `Selected Rig server did not provide run timeline events: ${error instanceof Error ? error.message : String(error)}`,
6519
+ createdAt: new Date().toISOString()
6520
+ }],
6521
+ nextCursor: options.timelineCursor ?? null
6522
+ }));
6523
+ const logs = Array.isArray(logsPage.entries) ? logsPage.entries.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))).toReversed() : [];
6524
+ const timeline = Array.isArray(timelinePage.entries) ? timelinePage.entries.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
6525
+ const timelineCursor = typeof timelinePage.nextCursor === "string" ? timelinePage.nextCursor : options.timelineCursor ?? null;
6526
+ return { run, logs, timeline, timelineCursor, rendered: renderOperatorSnapshot({ run, logs, timeline }) };
6224
6527
  }
6225
6528
  async function attachRunOperatorView(context, input) {
6226
6529
  let steered = false;
@@ -6228,40 +6531,41 @@ async function attachRunOperatorView(context, input) {
6228
6531
  await steerRunViaServer(context, input.runId, input.message.trim());
6229
6532
  steered = true;
6230
6533
  }
6534
+ const surface = createOperatorSurface({ interactive: input.interactive !== false });
6231
6535
  let snapshot = await readOperatorSnapshot(context, input.runId);
6232
6536
  if (context.outputMode === "text") {
6233
- console.log(snapshot.rendered);
6537
+ surface.renderSnapshot(snapshot);
6538
+ surface.renderTimeline(snapshot.timeline);
6539
+ surface.renderLogs(snapshot.logs);
6234
6540
  if (steered)
6235
- console.log("Steering message queued.");
6541
+ surface.info("Steering message queued.");
6236
6542
  }
6237
6543
  let detached = false;
6238
- let rl = null;
6544
+ let commandInput = null;
6239
6545
  if (input.follow && !input.once && context.outputMode === "text") {
6240
6546
  if (input.interactive !== false && process.stdin.isTTY) {
6241
- console.log("Controls: /user <message>, /stop, /detach");
6242
- rl = createInterface({ input: process.stdin, output: process.stdout, terminal: false });
6243
- rl.on("line", (line) => {
6244
- applyOperatorCommand(context, { runId: input.runId, line }).then((result) => {
6245
- if (result.message)
6246
- console.log(result.message);
6247
- if (result.action === "detach" || result.action === "stopped") {
6248
- detached = true;
6249
- rl?.close();
6250
- }
6251
- }).catch((error) => console.log(`Operator command failed: ${error instanceof Error ? error.message : String(error)}`));
6547
+ surface.info("Controls: /user <message>, /stop, /detach");
6548
+ commandInput = surface.attachCommandInput(async (line) => {
6549
+ const result = await applyOperatorCommand(context, { runId: input.runId, line });
6550
+ if (result.message)
6551
+ surface.info(result.message);
6552
+ if (result.action === "detach" || result.action === "stopped") {
6553
+ detached = true;
6554
+ commandInput?.close();
6555
+ }
6252
6556
  });
6253
6557
  }
6254
- let lastRendered = snapshot.rendered;
6255
6558
  const pollMs = Math.max(250, Math.trunc(input.pollMs ?? 2000));
6559
+ let timelineCursor = snapshot.timelineCursor;
6256
6560
  while (!detached && !TERMINAL_RUN_STATUSES.has(runStatusFromPayload(snapshot.run))) {
6257
6561
  await Bun.sleep(pollMs);
6258
- snapshot = await readOperatorSnapshot(context, input.runId);
6259
- if (snapshot.rendered !== lastRendered) {
6260
- console.log(snapshot.rendered);
6261
- lastRendered = snapshot.rendered;
6262
- }
6562
+ snapshot = await readOperatorSnapshot(context, input.runId, { timelineCursor });
6563
+ timelineCursor = snapshot.timelineCursor;
6564
+ surface.renderSnapshot(snapshot);
6565
+ surface.renderTimeline(snapshot.timeline);
6566
+ surface.renderLogs(snapshot.logs);
6263
6567
  }
6264
- rl?.close();
6568
+ commandInput?.close();
6265
6569
  }
6266
6570
  return { ...snapshot, steered, detached };
6267
6571
  }
@@ -6435,34 +6739,24 @@ async function executeRun(context, args) {
6435
6739
  if (!run.value) {
6436
6740
  throw new CliError2("run timeline requires --run <id>.");
6437
6741
  }
6438
- const timelinePath = resolve20(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
6439
- const printEvents = () => {
6440
- const events2 = readJsonlFile4(timelinePath);
6441
- if (context.outputMode === "text") {
6442
- for (const event of events2) {
6443
- console.log(JSON.stringify(event));
6444
- }
6445
- }
6446
- return events2;
6447
- };
6448
- const events = printEvents();
6742
+ const renderer = createPiRunStreamRenderer();
6743
+ let cursor = null;
6744
+ const page = await getRunTimelineViaServer(context, run.value, { limit: 500 });
6745
+ const events = Array.isArray(page.entries) ? page.entries.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
6746
+ cursor = typeof page.nextCursor === "string" ? page.nextCursor : null;
6747
+ if (context.outputMode === "text") {
6748
+ renderer.renderTimeline(events);
6749
+ }
6449
6750
  if (follow.value && context.outputMode === "text") {
6450
- let lastLength = existsSync12(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
6451
6751
  while (true) {
6452
6752
  await Bun.sleep(1000);
6453
- if (!existsSync12(timelinePath))
6454
- continue;
6455
- const next = readFileSync9(timelinePath, "utf8");
6456
- if (next.length <= lastLength)
6457
- continue;
6458
- const delta = next.slice(lastLength);
6459
- lastLength = next.length;
6460
- for (const line of delta.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean)) {
6461
- console.log(line);
6462
- }
6753
+ const nextPage = await getRunTimelineViaServer(context, run.value, { limit: 500, ...cursor ? { cursor } : {} });
6754
+ const nextEvents = Array.isArray(nextPage.entries) ? nextPage.entries.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
6755
+ cursor = typeof nextPage.nextCursor === "string" ? nextPage.nextCursor : cursor;
6756
+ renderer.renderTimeline(nextEvents);
6463
6757
  }
6464
6758
  }
6465
- return { ok: true, group: "run", command, details: { runId: run.value, events } };
6759
+ return { ok: true, group: "run", command, details: { runId: run.value, events, cursor } };
6466
6760
  }
6467
6761
  case "attach": {
6468
6762
  let pending = rest;
@@ -6752,10 +7046,10 @@ async function executeServer(context, args, options) {
6752
7046
  }
6753
7047
 
6754
7048
  // packages/cli/src/commands/task.ts
6755
- import { readFileSync as readFileSync10 } from "fs";
6756
- import { spawnSync as spawnSync4 } from "child_process";
6757
- import { createInterface as createInterface4 } from "readline/promises";
6758
- import { resolve as resolve21 } from "path";
7049
+ import { readFileSync as readFileSync9 } from "fs";
7050
+ import { spawnSync as spawnSync3 } from "child_process";
7051
+ import { createInterface as createInterface3 } from "readline/promises";
7052
+ import { resolve as resolve20 } from "path";
6759
7053
  import {
6760
7054
  taskArtifactDir,
6761
7055
  taskArtifacts,
@@ -6773,19 +7067,9 @@ import {
6773
7067
  } from "@rig/runtime/control-plane/native/task-ops";
6774
7068
 
6775
7069
  // packages/cli/src/commands/_task-picker.ts
6776
- import { createInterface as createInterface3 } from "readline/promises";
6777
- function taskId(task) {
7070
+ function taskId2(task) {
6778
7071
  return typeof task.id === "string" && task.id.trim() ? task.id : "<unknown>";
6779
7072
  }
6780
- function taskTitle(task) {
6781
- return typeof task.title === "string" && task.title.trim() ? task.title : "Untitled task";
6782
- }
6783
- function taskStatus(task) {
6784
- return typeof task.status === "string" && task.status.trim() ? task.status : "unknown";
6785
- }
6786
- function renderTaskPickerRows(tasks) {
6787
- return tasks.map((task, index) => `${index + 1}. ${taskId(task)} \xB7 ${taskStatus(task)} \xB7 ${taskTitle(task)}`);
6788
- }
6789
7073
  async function selectTaskWithTextPicker(tasks, io = {}) {
6790
7074
  if (tasks.length === 0)
6791
7075
  return null;
@@ -6795,17 +7079,12 @@ async function selectTaskWithTextPicker(tasks, io = {}) {
6795
7079
  if (!isTty) {
6796
7080
  throw new Error("task run requires an interactive terminal to pick a task; pass --task <id>, --next, or --detach with a task id.");
6797
7081
  }
6798
- const prompt = io.prompt ?? (async (question) => {
6799
- const rl = createInterface3({ input: process.stdin, output: process.stdout });
6800
- try {
6801
- return await rl.question(question);
6802
- } finally {
6803
- rl.close();
6804
- }
6805
- });
6806
- console.log("Select Rig task:");
7082
+ const prompt = io.prompt ?? promptForTaskSelection;
7083
+ const renderer = io.renderer ?? { writeLine: (line) => process.stdout.write(`${line}
7084
+ `) };
7085
+ renderer.writeLine("Select Rig task:");
6807
7086
  for (const row of renderTaskPickerRows(tasks))
6808
- console.log(` ${row}`);
7087
+ renderer.writeLine(` ${row}`);
6809
7088
  const answer = (await prompt(`Task [1-${tasks.length}] or id: `)).trim();
6810
7089
  if (!answer)
6811
7090
  return null;
@@ -6813,7 +7092,7 @@ async function selectTaskWithTextPicker(tasks, io = {}) {
6813
7092
  const index = Number.parseInt(answer, 10) - 1;
6814
7093
  return tasks[index] ?? null;
6815
7094
  }
6816
- return tasks.find((task) => taskId(task) === answer) ?? null;
7095
+ return tasks.find((task) => taskId2(task) === answer) ?? null;
6817
7096
  }
6818
7097
 
6819
7098
  // packages/cli/src/commands/task.ts
@@ -6891,7 +7170,7 @@ function normalizePrMode(value) {
6891
7170
  throw new CliError2("--pr must be auto, ask, or off.", 2);
6892
7171
  }
6893
7172
  function detectLocalDirtyState(projectRoot) {
6894
- const result = spawnSync4("git", ["-C", projectRoot, "status", "--porcelain"], { encoding: "utf8", timeout: 5000 });
7173
+ const result = spawnSync3("git", ["-C", projectRoot, "status", "--porcelain"], { encoding: "utf8", timeout: 5000 });
6895
7174
  if (result.status !== 0)
6896
7175
  return { dirty: false, modified: 0, untracked: 0, lines: [] };
6897
7176
  const lines = result.stdout.split(/\r?\n/).map((line) => line.trimEnd()).filter(Boolean);
@@ -6925,7 +7204,7 @@ async function resolveDirtyBaselineForTaskRun(context, explicit) {
6925
7204
  if (explicit)
6926
7205
  return { mode: explicit, state };
6927
7206
  if (context.outputMode === "text" && process.stdin.isTTY && process.stdout.isTTY) {
6928
- const rl = createInterface4({ input: process.stdin, output: process.stdout });
7207
+ const rl = createInterface3({ input: process.stdin, output: process.stdout });
6929
7208
  try {
6930
7209
  const answer = (await rl.question("Include current uncommitted changes in run baseline? [y/N] ")).trim().toLowerCase();
6931
7210
  return { mode: answer === "y" || answer === "yes" ? "dirty-snapshot" : "head", state };
@@ -7010,12 +7289,12 @@ async function executeTask(context, args, options) {
7010
7289
  const positional = taskOption.rest.length > 0 && taskOption.rest[0] && !taskOption.rest[0].startsWith("-") ? taskOption.rest[0] : undefined;
7011
7290
  const remaining = positional ? taskOption.rest.slice(1) : taskOption.rest;
7012
7291
  requireNoExtraArgs(remaining, "bun run rig task show <id>|--task <id>");
7013
- const taskId2 = normalizeTaskRunTaskId(taskOption.value ?? positional);
7014
- if (!taskId2)
7292
+ const taskId3 = normalizeTaskRunTaskId(taskOption.value ?? positional);
7293
+ if (!taskId3)
7015
7294
  throw new CliError2("task show requires a task id.", 2);
7016
- const task = await getWorkspaceTaskViaServer(context, taskId2);
7295
+ const task = await getWorkspaceTaskViaServer(context, taskId3);
7017
7296
  if (!task)
7018
- throw new CliError2(`Task not found: ${taskId2}`, 3);
7297
+ throw new CliError2(`Task not found: ${taskId3}`, 3);
7019
7298
  const summary = summarizeTask(task, { raw: true });
7020
7299
  if (context.outputMode === "text")
7021
7300
  console.log(JSON.stringify(summary, null, 2));
@@ -7086,7 +7365,7 @@ async function executeTask(context, args, options) {
7086
7365
  const fileFlag = takeOption(rest.slice(1), "--file");
7087
7366
  let content;
7088
7367
  if (fileFlag.value) {
7089
- content = readFileSync10(resolve21(context.projectRoot, fileFlag.value), "utf-8");
7368
+ content = readFileSync9(resolve20(context.projectRoot, fileFlag.value), "utf-8");
7090
7369
  } else {
7091
7370
  content = await readStdin();
7092
7371
  }
@@ -7307,9 +7586,9 @@ async function executeTask(context, args, options) {
7307
7586
  }
7308
7587
 
7309
7588
  // packages/cli/src/commands/task-run-driver.ts
7310
- import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
7311
- import { resolve as resolve22 } from "path";
7312
- import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
7589
+ import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync10, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
7590
+ import { resolve as resolve21 } from "path";
7591
+ import { spawn as spawn2, spawnSync as spawnSync4 } from "child_process";
7313
7592
  import { createInterface as createLineInterface } from "readline";
7314
7593
  import { loadConfig as loadConfig2 } from "@rig/core/load-config";
7315
7594
  import {
@@ -7334,8 +7613,7 @@ import {
7334
7613
  import { resolvePreferredShellBinary } from "@rig/runtime/control-plane/native/run-ops";
7335
7614
  import { readAuthorityRun as readAuthorityRun5, readJsonFile as readJsonFile3, resolveTaskArtifactDirs as resolveTaskArtifactDirs2 } from "@rig/runtime/control-plane/authority-files";
7336
7615
  import {
7337
- buildTaskRunLifecycleComment,
7338
- updateConfiguredTaskSourceTask
7616
+ buildTaskRunLifecycleComment
7339
7617
  } from "@rig/runtime/control-plane/tasks/source-lifecycle";
7340
7618
  import {
7341
7619
  closeIssueAfterMergedPr,
@@ -7373,7 +7651,7 @@ function buildPiRigBridgeEnv(input) {
7373
7651
  };
7374
7652
  }
7375
7653
  function runGitSync(cwd, args, input) {
7376
- const result = spawnSync5("git", [...args], {
7654
+ const result = spawnSync4("git", [...args], {
7377
7655
  cwd,
7378
7656
  input,
7379
7657
  encoding: "utf8",
@@ -7391,12 +7669,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
7391
7669
  return 0;
7392
7670
  let copied = 0;
7393
7671
  for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
7394
- const sourcePath = resolve22(sourceRoot, relativePath);
7395
- const targetPath = resolve22(targetRoot, relativePath);
7672
+ const sourcePath = resolve21(sourceRoot, relativePath);
7673
+ const targetPath = resolve21(targetRoot, relativePath);
7396
7674
  try {
7397
7675
  if (!statSync2(sourcePath).isFile())
7398
7676
  continue;
7399
- mkdirSync8(resolve22(targetPath, ".."), { recursive: true });
7677
+ mkdirSync8(resolve21(targetPath, ".."), { recursive: true });
7400
7678
  copyFileSync3(sourcePath, targetPath);
7401
7679
  copied += 1;
7402
7680
  } catch {}
@@ -7435,7 +7713,7 @@ function buildDirtyBaselineHandshakeEnv(input) {
7435
7713
  return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
7436
7714
  return {
7437
7715
  RIG_BASELINE_MODE: "dirty-snapshot",
7438
- RIG_DIRTY_BASELINE_READY_FILE: resolve22(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
7716
+ RIG_DIRTY_BASELINE_READY_FILE: resolve21(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
7439
7717
  };
7440
7718
  }
7441
7719
  function positiveInt(value, fallback) {
@@ -7546,9 +7824,9 @@ function createCommandRunner(binary) {
7546
7824
  const stderrChunks = [];
7547
7825
  child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7548
7826
  child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
7549
- return await new Promise((resolve23) => {
7550
- child.once("error", (error) => resolve23({ exitCode: 1, stderr: error.message }));
7551
- child.once("close", (code) => resolve23({
7827
+ return await new Promise((resolve22) => {
7828
+ child.once("error", (error) => resolve22({ exitCode: 1, stderr: error.message }));
7829
+ child.once("close", (code) => resolve22({
7552
7830
  exitCode: code ?? 1,
7553
7831
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
7554
7832
  stderr: Buffer.concat(stderrChunks).toString("utf8")
@@ -7568,12 +7846,13 @@ async function resolvePostValidationBranch(input) {
7568
7846
  return input.configuredBranch;
7569
7847
  }
7570
7848
  async function runTaskRunPostValidationLifecycle(input) {
7571
- const taskId2 = input.taskId?.trim();
7572
- if (!taskId2) {
7849
+ const taskId3 = input.taskId?.trim();
7850
+ if (!taskId3) {
7573
7851
  return { status: "skipped" };
7574
7852
  }
7575
- const config = input.config ?? {};
7576
- const prMode = config.pr?.mode ?? "auto";
7853
+ const configInput = input.config ?? null;
7854
+ const config = configInput ?? {};
7855
+ const prMode = configInput ? configInput.pr?.mode ?? "auto" : "off";
7577
7856
  if (prMode === "off" || prMode === "ask") {
7578
7857
  input.appendStage?.("Open PR", prMode === "off" ? "PR automation disabled by pr.mode=off." : "PR creation awaiting operator approval by pr.mode=ask.", "skipped", "info");
7579
7858
  input.appendStage?.("Complete", "Validation completed; no PR was opened and the issue was left open.", "completed", "info");
@@ -7589,7 +7868,7 @@ async function runTaskRunPostValidationLifecycle(input) {
7589
7868
  gitCommand
7590
7869
  });
7591
7870
  const prAutomation = input.prAutomation ?? runPrAutomation;
7592
- const updateTaskSource = input.updateTaskSource ?? updateConfiguredTaskSourceTask;
7871
+ const updateTaskSource = input.updateTaskSource ?? updateTaskSourceWithProjectSync;
7593
7872
  const stage = input.appendStage ?? (() => {
7594
7873
  return;
7595
7874
  });
@@ -7607,7 +7886,7 @@ async function runTaskRunPostValidationLifecycle(input) {
7607
7886
  stage("Commit", `Committing changes in ${workspace}.`, "running", "tool");
7608
7887
  const commit = await commitRunChanges({
7609
7888
  cwd: workspace,
7610
- message: `rig: complete task ${taskId2}`,
7889
+ message: `rig: complete task ${taskId3}`,
7611
7890
  command: gitCommand
7612
7891
  });
7613
7892
  stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
@@ -7615,14 +7894,15 @@ async function runTaskRunPostValidationLifecycle(input) {
7615
7894
  stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
7616
7895
  const pr = await prAutomation({
7617
7896
  projectRoot: workspace,
7618
- taskId: taskId2,
7897
+ taskId: taskId3,
7619
7898
  runId: input.runId,
7620
7899
  branch,
7621
7900
  config,
7622
7901
  sourceTask: input.sourceTask,
7623
7902
  uploadedSnapshot: input.uploadedSnapshot,
7624
- artifactRoot: resolve22(input.projectRoot, "artifacts", taskId2),
7903
+ artifactRoot: resolve21(input.projectRoot, "artifacts", taskId3),
7625
7904
  command: ghCommand,
7905
+ gitCommand,
7626
7906
  steerPi,
7627
7907
  lifecycle: {
7628
7908
  onPrOpened: async ({ prUrl }) => {
@@ -7630,7 +7910,7 @@ async function runTaskRunPostValidationLifecycle(input) {
7630
7910
  try {
7631
7911
  if (shouldWriteDriverIssueUpdate(config, "under_review")) {
7632
7912
  await updateTaskSource(input.projectRoot, {
7633
- taskId: taskId2,
7913
+ taskId: taskId3,
7634
7914
  sourceTask: runSourceTaskIdentity(input.sourceTask),
7635
7915
  update: {
7636
7916
  status: "under_review",
@@ -7660,7 +7940,7 @@ async function runTaskRunPostValidationLifecycle(input) {
7660
7940
  `), "reviewing", "error");
7661
7941
  if (shouldWriteDriverIssueUpdate(config, "ci_fixing")) {
7662
7942
  await updateTaskSource(input.projectRoot, {
7663
- taskId: taskId2,
7943
+ taskId: taskId3,
7664
7944
  sourceTask: runSourceTaskIdentity(input.sourceTask),
7665
7945
  update: {
7666
7946
  status: "ci_fixing",
@@ -7671,8 +7951,6 @@ async function runTaskRunPostValidationLifecycle(input) {
7671
7951
  runtimeWorkspace: input.runtimeWorkspace ?? null
7672
7952
  })
7673
7953
  }
7674
- }).catch(() => {
7675
- return;
7676
7954
  });
7677
7955
  }
7678
7956
  },
@@ -7680,7 +7958,7 @@ async function runTaskRunPostValidationLifecycle(input) {
7680
7958
  stage("Merge", prUrl, "running", "tool");
7681
7959
  if (shouldWriteDriverIssueUpdate(config, "merging")) {
7682
7960
  await updateTaskSource(input.projectRoot, {
7683
- taskId: taskId2,
7961
+ taskId: taskId3,
7684
7962
  sourceTask: runSourceTaskIdentity(input.sourceTask),
7685
7963
  update: {
7686
7964
  status: "merging",
@@ -7691,8 +7969,6 @@ async function runTaskRunPostValidationLifecycle(input) {
7691
7969
  runtimeWorkspace: input.runtimeWorkspace ?? null
7692
7970
  })
7693
7971
  }
7694
- }).catch(() => {
7695
- return;
7696
7972
  });
7697
7973
  }
7698
7974
  },
@@ -7709,7 +7985,7 @@ async function runTaskRunPostValidationLifecycle(input) {
7709
7985
  stage("Needs attention", detail, "needs_attention", "error");
7710
7986
  if (shouldWriteDriverIssueUpdate(config, "needs_attention")) {
7711
7987
  await updateTaskSource(input.projectRoot, {
7712
- taskId: taskId2,
7988
+ taskId: taskId3,
7713
7989
  sourceTask: runSourceTaskIdentity(input.sourceTask),
7714
7990
  update: {
7715
7991
  status: "needs_attention",
@@ -7721,8 +7997,17 @@ async function runTaskRunPostValidationLifecycle(input) {
7721
7997
  errorText: detail
7722
7998
  })
7723
7999
  }
7724
- }).catch(() => {
7725
- return;
8000
+ }).catch((error) => {
8001
+ try {
8002
+ appendRunLog(input.projectRoot, input.runId, {
8003
+ id: `log:${input.runId}:task-source-needs-attention-update`,
8004
+ title: "Task source needs-attention update failed",
8005
+ detail: error instanceof Error ? error.message : String(error),
8006
+ tone: "error",
8007
+ status: "needs_attention",
8008
+ createdAt: new Date().toISOString()
8009
+ });
8010
+ } catch {}
7726
8011
  });
7727
8012
  }
7728
8013
  return { status: "needs_attention", pr };
@@ -7730,7 +8015,7 @@ async function runTaskRunPostValidationLifecycle(input) {
7730
8015
  if (shouldWriteDriverIssueUpdate(config, "closed")) {
7731
8016
  await closeIssueAfterMergedPr({
7732
8017
  projectRoot: input.projectRoot,
7733
- taskId: taskId2,
8018
+ taskId: taskId3,
7734
8019
  runId: input.runId,
7735
8020
  prUrl: pr.prUrl,
7736
8021
  sourceTask: input.sourceTask,
@@ -7740,12 +8025,12 @@ async function runTaskRunPostValidationLifecycle(input) {
7740
8025
  stage("Complete", `PR merged and issue closed: ${pr.prUrl}`, "completed", "info");
7741
8026
  return { status: "completed", pr };
7742
8027
  }
7743
- function summarizeValidationFailure(projectRoot, taskId2) {
7744
- if (!taskId2) {
8028
+ function summarizeValidationFailure(projectRoot, taskId3) {
8029
+ if (!taskId3) {
7745
8030
  return null;
7746
8031
  }
7747
- for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
7748
- const summary = readJsonFile3(resolve22(artifactDir, "validation-summary.json"), null);
8032
+ for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId3)) {
8033
+ const summary = readJsonFile3(resolve21(artifactDir, "validation-summary.json"), null);
7749
8034
  if (!summary || summary.status !== "fail") {
7750
8035
  continue;
7751
8036
  }
@@ -7826,9 +8111,9 @@ function readTaskRunAcceptedArtifactState(input) {
7826
8111
  if (!input.taskId || !input.workspaceDir) {
7827
8112
  return { accepted: false, reason: null };
7828
8113
  }
7829
- const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7830
- const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7831
- const taskResultPath = resolve22(artifactDir, "task-result.json");
8114
+ const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
8115
+ const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
8116
+ const taskResultPath = resolve21(artifactDir, "task-result.json");
7832
8117
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7833
8118
  if (reviewStatus !== "APPROVED") {
7834
8119
  return { accepted: false, reason: null };
@@ -7865,12 +8150,12 @@ function resolveTaskRunRetryContext(input) {
7865
8150
  if (!input.taskId || !input.workspaceDir) {
7866
8151
  return { shouldRetry: false, failureDetail: null, nextPrompt: null };
7867
8152
  }
7868
- const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
7869
- const reviewStatePath = resolve22(artifactDir, "review-state.json");
7870
- const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
7871
- const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
7872
- const failedApproachesPath = resolve22(input.workspaceDir, ".rig", "state", "failed_approaches.md");
7873
- const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
8153
+ const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
8154
+ const reviewStatePath = resolve21(artifactDir, "review-state.json");
8155
+ const reviewFeedbackPath = resolve21(artifactDir, "review-feedback.md");
8156
+ const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
8157
+ const failedApproachesPath = resolve21(input.workspaceDir, ".rig", "state", "failed_approaches.md");
8158
+ const validationSummaryPath = resolve21(artifactDir, "validation-summary.json");
7874
8159
  const reviewState = readJsonFile3(reviewStatePath, null);
7875
8160
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
7876
8161
  const reviewRejected = isTaskRunReviewRejected(reviewState);
@@ -7924,12 +8209,60 @@ function summarizeTaskRunReviewFailure(reviewState) {
7924
8209
  }
7925
8210
  return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
7926
8211
  }
8212
+ function appendAssistantTimelineFromRecord(input) {
8213
+ let nextAssistantText = input.assistantText;
8214
+ if (input.record.type === "message_update") {
8215
+ const assistantMessageEvent = input.record.assistantMessageEvent && typeof input.record.assistantMessageEvent === "object" ? input.record.assistantMessageEvent : null;
8216
+ if (assistantMessageEvent?.type === "text_delta" && typeof assistantMessageEvent.delta === "string") {
8217
+ nextAssistantText += assistantMessageEvent.delta;
8218
+ }
8219
+ } else if (input.record.type === "stream_event") {
8220
+ const event = input.record.event && typeof input.record.event === "object" ? input.record.event : null;
8221
+ const delta = event?.delta && typeof event.delta === "object" ? event.delta : null;
8222
+ if (delta?.type === "text_delta" && typeof delta.text === "string") {
8223
+ nextAssistantText += delta.text;
8224
+ }
8225
+ } else if (input.record.type === "assistant") {
8226
+ const message2 = input.record.message && typeof input.record.message === "object" ? input.record.message : input.record;
8227
+ const content = Array.isArray(message2.content) ? message2.content : [];
8228
+ const fullText = content.map((entry) => entry && typeof entry === "object" && entry.type === "text" ? String(entry.text ?? "") : "").join("");
8229
+ if (fullText.length > nextAssistantText.length)
8230
+ nextAssistantText = fullText;
8231
+ }
8232
+ if (nextAssistantText !== input.assistantText) {
8233
+ appendRunTimeline(input.projectRoot, input.runId, {
8234
+ id: input.messageId,
8235
+ type: "assistant_message",
8236
+ text: nextAssistantText,
8237
+ state: "streaming",
8238
+ createdAt: new Date().toISOString()
8239
+ });
8240
+ }
8241
+ return nextAssistantText;
8242
+ }
8243
+ function appendToolTimelineFromLog(input) {
8244
+ const title = typeof input.log.title === "string" ? input.log.title : "";
8245
+ if (title !== "Tool activity")
8246
+ return;
8247
+ const payload = input.log.payload && typeof input.log.payload === "object" && !Array.isArray(input.log.payload) ? input.log.payload : {};
8248
+ const toolName = typeof payload.toolName === "string" && payload.toolName.trim() ? payload.toolName.trim() : typeof payload.tool_name === "string" && payload.tool_name.trim() ? payload.tool_name.trim() : null;
8249
+ const logId = typeof input.log.id === "string" && input.log.id.trim() ? input.log.id.trim() : `${Date.now()}`;
8250
+ appendRunTimeline(input.projectRoot, input.runId, {
8251
+ id: `tool:${logId}`,
8252
+ type: "tool_execution_update",
8253
+ toolName,
8254
+ status: typeof input.log.status === "string" ? input.log.status : "running",
8255
+ detail: typeof input.log.detail === "string" ? input.log.detail : null,
8256
+ payload,
8257
+ createdAt: typeof input.log.createdAt === "string" ? input.log.createdAt : new Date().toISOString()
8258
+ });
8259
+ }
7927
8260
  function readTaskRunReviewStatus(reviewStatusPath) {
7928
- if (!existsSync13(reviewStatusPath)) {
8261
+ if (!existsSync12(reviewStatusPath)) {
7929
8262
  return null;
7930
8263
  }
7931
8264
  try {
7932
- const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
8265
+ const status = readFileSync10(reviewStatusPath, "utf8").trim().toUpperCase();
7933
8266
  return status === "APPROVED" || status === "REJECTED" ? status : null;
7934
8267
  } catch {
7935
8268
  return null;
@@ -7947,14 +8280,45 @@ function isTaskRunReviewRejected(reviewState) {
7947
8280
  function runSourceTaskIdentity(sourceTask) {
7948
8281
  return sourceTask;
7949
8282
  }
7950
- async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId2, sourceTask, status, summary, input, updateTaskSource = updateConfiguredTaskSourceTask) {
7951
- if (!taskId2)
8283
+ function sourceTaskIssueNodeId(sourceTask) {
8284
+ if (!sourceTask || typeof sourceTask !== "object" || Array.isArray(sourceTask))
8285
+ return null;
8286
+ const record = sourceTask;
8287
+ const direct = typeof record.issueNodeId === "string" ? record.issueNodeId : typeof record.nodeId === "string" ? record.nodeId : typeof record.node_id === "string" ? record.node_id : null;
8288
+ if (direct?.trim())
8289
+ return direct.trim();
8290
+ const raw = record.raw && typeof record.raw === "object" && !Array.isArray(record.raw) ? record.raw : null;
8291
+ return typeof raw?.id === "string" && raw.id.trim() ? raw.id.trim() : null;
8292
+ }
8293
+ var updateTaskSourceWithProjectSync = async (projectRoot, input) => {
8294
+ const serverResult = await updateWorkspaceTaskViaServer({ projectRoot }, {
8295
+ id: input.taskId,
8296
+ ...input.update.status ? { status: input.update.status } : {},
8297
+ ...input.update.comment ? { comment: input.update.comment } : {},
8298
+ ...input.update.title ? { title: input.update.title } : {},
8299
+ ...typeof input.update.body === "string" ? { body: input.update.body } : {},
8300
+ issueNodeId: sourceTaskIssueNodeId(input.sourceTask)
8301
+ });
8302
+ if (serverResult.ok === false) {
8303
+ throw new Error(typeof serverResult.error === "string" ? serverResult.error : "Rig server task update failed.");
8304
+ }
8305
+ return {
8306
+ updated: serverResult.ok !== false,
8307
+ taskId: input.taskId,
8308
+ status: input.update.status,
8309
+ source: "server",
8310
+ sourceKind: "server",
8311
+ projectSync: serverResult.projectSync
8312
+ };
8313
+ };
8314
+ async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId3, sourceTask, status, summary, input, updateTaskSource = updateTaskSourceWithProjectSync) {
8315
+ if (!taskId3)
7952
8316
  return;
7953
8317
  const config = await loadTaskRunAutomationConfig(projectRoot);
7954
8318
  if (!shouldWriteDriverIssueUpdate(config, status))
7955
8319
  return;
7956
8320
  const result = await updateTaskSource(projectRoot, {
7957
- taskId: taskId2,
8321
+ taskId: taskId3,
7958
8322
  sourceTask: runSourceTaskIdentity(sourceTask),
7959
8323
  update: {
7960
8324
  status,
@@ -7973,14 +8337,14 @@ async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId2, sourc
7973
8337
  throw new Error(`Configured task source${result.sourceKind ? ` (${result.sourceKind})` : ""} did not accept lifecycle update for ${result.taskId}.`);
7974
8338
  }
7975
8339
  }
7976
- function readRunSourceTaskContract(projectRoot, runId, taskId2) {
8340
+ function readRunSourceTaskContract(projectRoot, runId, taskId3) {
7977
8341
  const run = readAuthorityRun5(projectRoot, runId);
7978
8342
  const raw = run?.sourceTask;
7979
8343
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
7980
8344
  return null;
7981
8345
  }
7982
8346
  const record = raw;
7983
- const id = typeof record.id === "string" && record.id.trim() ? record.id.trim() : taskId2?.trim();
8347
+ const id = typeof record.id === "string" && record.id.trim() ? record.id.trim() : taskId3?.trim();
7984
8348
  if (!id) {
7985
8349
  return null;
7986
8350
  }
@@ -8116,15 +8480,15 @@ async function executeRigOwnedTaskRun(context, input) {
8116
8480
  const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
8117
8481
  const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
8118
8482
  const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
8119
- const planningArtifactPath = resolve22("artifacts", runtimeTaskId, "implementation-plan.md");
8483
+ const planningArtifactPath = resolve21("artifacts", runtimeTaskId, "implementation-plan.md");
8120
8484
  const persistedPlanning = {
8121
8485
  ...planningClassification,
8122
8486
  classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
8123
8487
  artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
8124
8488
  classifiedAt: new Date().toISOString()
8125
8489
  };
8126
- mkdirSync8(resolve22(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
8127
- writeFileSync6(resolve22(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
8490
+ mkdirSync8(resolve21(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
8491
+ writeFileSync6(resolve21(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
8128
8492
  `, "utf8");
8129
8493
  patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
8130
8494
  prompt = `${prompt}
@@ -8174,7 +8538,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8174
8538
  let verificationStarted = false;
8175
8539
  let reviewStarted = false;
8176
8540
  let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
8177
- let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve22(existingRunRecord.sessionPath, "..") : null;
8541
+ let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve21(existingRunRecord.sessionPath, "..") : null;
8178
8542
  let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
8179
8543
  let latestProviderCommand = null;
8180
8544
  let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
@@ -8257,10 +8621,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8257
8621
  patchAuthorityRun(context.projectRoot, input.runId, {
8258
8622
  status: "running",
8259
8623
  worktreePath: latestRuntimeWorkspace,
8260
- artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve22(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8624
+ artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve21(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
8261
8625
  logRoot: latestLogsDir,
8262
- sessionPath: latestSessionDir ? resolve22(latestSessionDir, "session.json") : null,
8263
- sessionLogPath: latestLogsDir ? resolve22(latestLogsDir, "agent-stdout.log") : null,
8626
+ sessionPath: latestSessionDir ? resolve21(latestSessionDir, "session.json") : null,
8627
+ sessionLogPath: latestLogsDir ? resolve21(latestLogsDir, "agent-stdout.log") : null,
8264
8628
  branch: runtimeId
8265
8629
  });
8266
8630
  if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
@@ -8268,7 +8632,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8268
8632
  const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
8269
8633
  const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
8270
8634
  if (readyFile) {
8271
- mkdirSync8(resolve22(readyFile, ".."), { recursive: true });
8635
+ mkdirSync8(resolve21(readyFile, ".."), { recursive: true });
8272
8636
  writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
8273
8637
  `, "utf8");
8274
8638
  }
@@ -8417,7 +8781,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8417
8781
  if (providerLogs.length > 0) {
8418
8782
  for (const providerLog of providerLogs) {
8419
8783
  appendRunLog(context.projectRoot, input.runId, providerLog);
8784
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
8420
8785
  emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
8786
+ if (providerLog.title === "Tool activity")
8787
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
8421
8788
  }
8422
8789
  }
8423
8790
  if (input.runtimeAdapter === "codex") {
@@ -8575,7 +8942,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8575
8942
  let acceptedArtifactObservedAt = null;
8576
8943
  let acceptedArtifactPollTimer = null;
8577
8944
  let acceptedArtifactKillTimer = null;
8578
- const attemptExit = await new Promise((resolve23) => {
8945
+ const attemptExit = await new Promise((resolve22) => {
8579
8946
  let settled = false;
8580
8947
  const settle = (result) => {
8581
8948
  if (settled)
@@ -8583,7 +8950,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8583
8950
  settled = true;
8584
8951
  if (acceptedArtifactPollTimer)
8585
8952
  clearInterval(acceptedArtifactPollTimer);
8586
- resolve23(result);
8953
+ resolve22(result);
8587
8954
  };
8588
8955
  const pollAcceptedArtifacts = () => {
8589
8956
  const artifactState = readTaskRunAcceptedArtifactState({
@@ -8656,7 +9023,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8656
9023
  });
8657
9024
  for (const pendingLog of pendingLogs) {
8658
9025
  appendRunLog(context.projectRoot, input.runId, pendingLog);
9026
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
8659
9027
  emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
9028
+ if (pendingLog.title === "Tool activity")
9029
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
8660
9030
  }
8661
9031
  process.off("SIGTERM", forwardSigterm);
8662
9032
  if (attemptExit.error) {
@@ -8782,8 +9152,8 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8782
9152
  }
8783
9153
  if (planningClassification.planningRequired) {
8784
9154
  const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
8785
- const expectedPlanPath = resolve22(planWorkspace, planningArtifactPath);
8786
- if (!existsSync13(expectedPlanPath)) {
9155
+ const expectedPlanPath = resolve21(planWorkspace, planningArtifactPath);
9156
+ if (!existsSync12(expectedPlanPath)) {
8787
9157
  const failedAt = new Date().toISOString();
8788
9158
  const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
8789
9159
  patchAuthorityRun(context.projectRoot, input.runId, {
@@ -8803,6 +9173,65 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8803
9173
  throw new CliError2(failureDetail, 1);
8804
9174
  }
8805
9175
  }
9176
+ if (process.env.RIG_SERVER_OWNS_CLOSEOUT === "1") {
9177
+ appendPiStageLog({
9178
+ projectRoot: context.projectRoot,
9179
+ runId: input.runId,
9180
+ stage: "Validate",
9181
+ detail: "Rig validation accepted the task run; server will continue PR/review/merge closeout.",
9182
+ status: "completed"
9183
+ });
9184
+ if (verificationAction && !reviewStarted) {
9185
+ verificationAction.complete("Completion verification checks finished.");
9186
+ }
9187
+ if (!reviewAction) {
9188
+ promoteToReviewing("Server-owned closeout is queued.");
9189
+ }
9190
+ if (reviewAction) {
9191
+ reviewAction.complete("Provider work accepted; server-owned closeout requested.");
9192
+ }
9193
+ const requestedAt = new Date().toISOString();
9194
+ patchAuthorityRun(context.projectRoot, input.runId, {
9195
+ status: "reviewing",
9196
+ completedAt: null,
9197
+ errorText: null,
9198
+ serverCloseout: {
9199
+ status: "pending",
9200
+ phase: "queued",
9201
+ requestedAt,
9202
+ updatedAt: requestedAt,
9203
+ runtimeWorkspace: latestRuntimeWorkspace,
9204
+ branch: latestRuntimeBranch,
9205
+ taskId: input.taskId ?? runtimeTaskId
9206
+ }
9207
+ });
9208
+ appendRunLog(context.projectRoot, input.runId, {
9209
+ id: `log:${input.runId}:server-closeout-requested`,
9210
+ title: "Server-owned closeout requested",
9211
+ detail: "The CLI provider worker finished validation and handed commit/PR/review/merge closeout back to the Rig server.",
9212
+ tone: "info",
9213
+ status: "reviewing",
9214
+ createdAt: requestedAt,
9215
+ payload: { runtimeWorkspace: latestRuntimeWorkspace, branch: latestRuntimeBranch }
9216
+ });
9217
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Server-owned closeout requested" });
9218
+ emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: "Server-owned closeout requested." });
9219
+ await context.emitEvent("command.finished", {
9220
+ command: [
9221
+ "rig",
9222
+ "server",
9223
+ "task-run",
9224
+ ...input.taskId ? ["--task", input.taskId] : [],
9225
+ ...input.title ? ["--title", input.title] : []
9226
+ ],
9227
+ formattedCommand: input.taskId ? `rig server task-run --task ${input.taskId}` : `rig server task-run --title ${JSON.stringify(input.title ?? `Run ${input.runId}`)}`,
9228
+ exitCode: 0,
9229
+ durationMs: 0,
9230
+ startedAt,
9231
+ finishedAt: requestedAt
9232
+ });
9233
+ return;
9234
+ }
8806
9235
  const runPiPrFeedbackFix = async (message2) => {
8807
9236
  appendPiStageLog({
8808
9237
  projectRoot: context.projectRoot,
@@ -8830,11 +9259,45 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8830
9259
  child.stdin.write(message2);
8831
9260
  }
8832
9261
  child.stdin.end();
9262
+ const feedbackAssistantMessageId = `message:${input.runId}:pr-feedback:${Date.now()}:assistant`;
9263
+ let feedbackAssistantText = "";
9264
+ const feedbackPendingToolUses = new Map;
8833
9265
  const stdout = createLineInterface({ input: child.stdout });
8834
9266
  stdout.on("line", (line) => {
8835
9267
  const trimmed = line.trim();
8836
9268
  if (!trimmed)
8837
9269
  return;
9270
+ try {
9271
+ const record = JSON.parse(trimmed);
9272
+ const providerLogs = buildClaudeLogsFromRecord({
9273
+ runId: input.runId,
9274
+ record,
9275
+ createdAtFallback: new Date().toISOString(),
9276
+ status: "reviewing",
9277
+ pendingToolUses: feedbackPendingToolUses
9278
+ });
9279
+ for (const providerLog of providerLogs) {
9280
+ appendRunLog(context.projectRoot, input.runId, providerLog);
9281
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
9282
+ emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
9283
+ if (providerLog.title === "Tool activity")
9284
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
9285
+ }
9286
+ const nextFeedbackAssistantText = appendAssistantTimelineFromRecord({
9287
+ projectRoot: context.projectRoot,
9288
+ runId: input.runId,
9289
+ messageId: feedbackAssistantMessageId,
9290
+ record,
9291
+ assistantText: feedbackAssistantText
9292
+ });
9293
+ const hadAssistantDelta = nextFeedbackAssistantText !== feedbackAssistantText;
9294
+ if (hadAssistantDelta) {
9295
+ feedbackAssistantText = nextFeedbackAssistantText;
9296
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
9297
+ }
9298
+ if (providerLogs.length > 0 || hadAssistantDelta)
9299
+ return;
9300
+ } catch {}
8838
9301
  appendRunLog(context.projectRoot, input.runId, {
8839
9302
  id: nextRunLogId(),
8840
9303
  title: "Pi PR feedback fix output",
@@ -8860,10 +9323,31 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
8860
9323
  });
8861
9324
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
8862
9325
  });
8863
- const exitCode = await new Promise((resolve23) => {
8864
- child.once("error", () => resolve23(1));
8865
- child.once("close", (code) => resolve23(code ?? 1));
9326
+ const exitCode = await new Promise((resolve22) => {
9327
+ child.once("error", () => resolve22(1));
9328
+ child.once("close", (code) => resolve22(code ?? 1));
8866
9329
  });
9330
+ for (const pendingLog of flushPendingClaudeToolUseLogs({
9331
+ runId: input.runId,
9332
+ status: exitCode === 0 ? "completed" : "failed",
9333
+ pendingToolUses: feedbackPendingToolUses
9334
+ })) {
9335
+ appendRunLog(context.projectRoot, input.runId, pendingLog);
9336
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
9337
+ emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
9338
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
9339
+ }
9340
+ if (feedbackAssistantText.trim()) {
9341
+ appendRunTimeline(context.projectRoot, input.runId, {
9342
+ id: feedbackAssistantMessageId,
9343
+ type: "assistant_message",
9344
+ text: feedbackAssistantText,
9345
+ state: "completed",
9346
+ createdAt: new Date().toISOString(),
9347
+ completedAt: new Date().toISOString()
9348
+ });
9349
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
9350
+ }
8867
9351
  if (exitCode !== 0) {
8868
9352
  throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
8869
9353
  }
@@ -8981,8 +9465,8 @@ async function executeTest(context, args) {
8981
9465
  }
8982
9466
 
8983
9467
  // packages/cli/src/commands/setup.ts
8984
- import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
8985
- import { resolve as resolve23 } from "path";
9468
+ import { existsSync as existsSync13, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
9469
+ import { resolve as resolve22 } from "path";
8986
9470
  import { createPluginHost } from "@rig/core";
8987
9471
  import {
8988
9472
  isSupportedBunVersion as isSupportedBunVersion2,
@@ -9045,8 +9529,8 @@ function runSetupInit(projectRoot) {
9045
9529
  mkdirSync9(stateDir, { recursive: true });
9046
9530
  mkdirSync9(logsDir, { recursive: true });
9047
9531
  mkdirSync9(artifactsDir, { recursive: true });
9048
- const failuresPath = resolve23(stateDir, "failed_approaches.md");
9049
- if (!existsSync14(failuresPath)) {
9532
+ const failuresPath = resolve22(stateDir, "failed_approaches.md");
9533
+ if (!existsSync13(failuresPath)) {
9050
9534
  writeFileSync7(failuresPath, `# Failed Approaches
9051
9535
 
9052
9536
  `, "utf-8");
@@ -9064,18 +9548,18 @@ async function runSetupCheck(projectRoot) {
9064
9548
  }
9065
9549
  async function runSetupPreflight(projectRoot) {
9066
9550
  await runSetupCheck(projectRoot);
9067
- const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
9068
- if (existsSync14(validationRoot)) {
9551
+ const validationRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
9552
+ if (existsSync13(validationRoot)) {
9069
9553
  const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
9070
9554
  for (const validator of validators) {
9071
- const script = resolve23(validationRoot, validator.name, "validate.sh");
9072
- if (existsSync14(script)) {
9555
+ const script = resolve22(validationRoot, validator.name, "validate.sh");
9556
+ if (existsSync13(script)) {
9073
9557
  console.log(`OK: validator script ${script}`);
9074
9558
  }
9075
9559
  }
9076
9560
  }
9077
- const hooksRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
9078
- if (existsSync14(hooksRoot)) {
9561
+ const hooksRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
9562
+ if (existsSync13(hooksRoot)) {
9079
9563
  const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
9080
9564
  for (const hook of hooks) {
9081
9565
  console.log(`OK: hook ${hook}`);
@@ -9452,8 +9936,8 @@ async function executeGroup(context, group, args) {
9452
9936
  }
9453
9937
  }
9454
9938
  // packages/cli/src/launcher.ts
9455
- import { existsSync as existsSync15 } from "fs";
9456
- import { basename as basename2, resolve as resolve24 } from "path";
9939
+ import { existsSync as existsSync14 } from "fs";
9940
+ import { basename as basename2, resolve as resolve23 } from "path";
9457
9941
  import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
9458
9942
  import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
9459
9943
  function parsePolicyMode(value) {
@@ -9466,7 +9950,7 @@ function parsePolicyMode(value) {
9466
9950
  throw new Error(`Invalid --policy-mode value: ${value}. Use off|observe|enforce.`);
9467
9951
  }
9468
9952
  function hasRigProjectMarker(candidate) {
9469
- 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"));
9953
+ 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"));
9470
9954
  }
9471
9955
  function resolveProjectRoot({
9472
9956
  envProjectRoot,
@@ -9475,19 +9959,19 @@ function resolveProjectRoot({
9475
9959
  cwd = process.cwd()
9476
9960
  }) {
9477
9961
  if (envProjectRoot) {
9478
- return resolve24(cwd, envProjectRoot);
9962
+ return resolve23(cwd, envProjectRoot);
9479
9963
  }
9480
9964
  const fallbackImportDir = importDir ?? cwd;
9481
9965
  const execName = basename2(execPath).toLowerCase();
9482
- const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve24(execPath, "..", "..")] : [];
9483
- const candidates = [cwd, ...execCandidates, resolve24(fallbackImportDir, "..")];
9966
+ const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve23(execPath, "..", "..")] : [];
9967
+ const candidates = [cwd, ...execCandidates, resolve23(fallbackImportDir, "..")];
9484
9968
  for (const candidate of candidates) {
9485
9969
  const nearest = resolveNearestRigProjectRoot(candidate);
9486
9970
  if (hasRigProjectMarker(nearest)) {
9487
9971
  return nearest;
9488
9972
  }
9489
9973
  }
9490
- return resolve24(cwd);
9974
+ return resolve23(cwd);
9491
9975
  }
9492
9976
  function normalizeCliErrorCode(message2, isCliError) {
9493
9977
  if (message2.startsWith("Invalid --policy-mode value:")) {
@@ -9554,7 +10038,7 @@ async function runRigCli(module, options = {}) {
9554
10038
  runId: context.runId,
9555
10039
  outcome,
9556
10040
  eventsFile: context.eventBus.getEventsFile(),
9557
- policyFile: resolve24(projectRoot, "rig", "policy", "policy.json"),
10041
+ policyFile: resolve23(projectRoot, "rig", "policy", "policy.json"),
9558
10042
  policyMode: context.policyMode ?? policyMode ?? module.loadPolicy(projectRoot).mode
9559
10043
  }, null, 2));
9560
10044
  }