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

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
@@ -3446,6 +3446,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3446
3446
  const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
3447
3447
  const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
3448
3448
  const next = {
3449
+ ...existing ?? {},
3449
3450
  runId: input.runId,
3450
3451
  projectRoot,
3451
3452
  workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
@@ -6118,6 +6119,7 @@ import {
6118
6119
  listOpenEpics,
6119
6120
  resolveDefaultEpic,
6120
6121
  runResume,
6122
+ runRestart,
6121
6123
  runStatus,
6122
6124
  runStop,
6123
6125
  startRun,
@@ -6226,6 +6228,17 @@ async function attachRunOperatorView(context, input) {
6226
6228
  }
6227
6229
 
6228
6230
  // packages/cli/src/commands/run.ts
6231
+ function normalizeRemoteRunDetails(payload) {
6232
+ const run = payload.run;
6233
+ if (!run || typeof run !== "object" || Array.isArray(run))
6234
+ return null;
6235
+ return {
6236
+ ...run,
6237
+ ...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
6238
+ ...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
6239
+ ...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
6240
+ };
6241
+ }
6229
6242
  function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
6230
6243
  if (noEpicPrompt) {
6231
6244
  return false;
@@ -6364,7 +6377,7 @@ async function executeRun(context, args) {
6364
6377
  if (!run.value) {
6365
6378
  throw new CliError2("run show requires --run <id>.");
6366
6379
  }
6367
- const record = readAuthorityRun4(context.projectRoot, run.value);
6380
+ const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
6368
6381
  if (!record) {
6369
6382
  throw new CliError2(`Run not found: ${run.value}`, 2);
6370
6383
  }
@@ -6545,6 +6558,20 @@ async function executeRun(context, args) {
6545
6558
  }
6546
6559
  return { ok: true, group: "run", command, details: resumed };
6547
6560
  }
6561
+ case "restart": {
6562
+ requireNoExtraArgs(rest, "bun run rig run restart");
6563
+ if (context.dryRun) {
6564
+ if (context.outputMode === "text") {
6565
+ console.log("[dry-run] rig run restart");
6566
+ }
6567
+ return { ok: true, group: "run", command };
6568
+ }
6569
+ const restarted = await runRestart(context.projectRoot, runtimeContext);
6570
+ if (context.outputMode === "text") {
6571
+ console.log(`Restarted run: ${restarted.runId}`);
6572
+ }
6573
+ return { ok: true, group: "run", command, details: restarted };
6574
+ }
6548
6575
  case "stop": {
6549
6576
  const runOption = takeOption(rest, "--run");
6550
6577
  const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
@@ -7276,7 +7303,24 @@ import {
7276
7303
  commitRunChanges,
7277
7304
  runPrAutomation
7278
7305
  } from "@rig/runtime/control-plane/native/pr-automation";
7306
+ function looksLikeGitHubToken(value) {
7307
+ const token = value?.trim();
7308
+ if (!token)
7309
+ return false;
7310
+ return /^(gh[opusr]_|github_pat_)/.test(token);
7311
+ }
7312
+ function githubBridgeEnv(token) {
7313
+ const clean = token?.trim();
7314
+ if (!clean)
7315
+ return {};
7316
+ return {
7317
+ RIG_GITHUB_TOKEN: clean,
7318
+ GITHUB_TOKEN: clean,
7319
+ GH_TOKEN: clean
7320
+ };
7321
+ }
7279
7322
  function buildPiRigBridgeEnv(input) {
7323
+ const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
7280
7324
  return {
7281
7325
  RIG_PROJECT_ROOT: input.projectRoot,
7282
7326
  PROJECT_RIG_ROOT: input.projectRoot,
@@ -7286,7 +7330,7 @@ function buildPiRigBridgeEnv(input) {
7286
7330
  RIG_RUNTIME_ADAPTER: "pi",
7287
7331
  ...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
7288
7332
  ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
7289
- ...input.githubToken ? { RIG_GITHUB_TOKEN: input.githubToken } : {}
7333
+ ...githubBridgeEnv(githubToken)
7290
7334
  };
7291
7335
  }
7292
7336
  function runGitSync(cwd, args, input) {
@@ -7928,6 +7972,8 @@ function stringArrayField(record, key) {
7928
7972
  }
7929
7973
  async function executeRigOwnedTaskRun(context, input) {
7930
7974
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
7975
+ const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
7976
+ const resumeMode = process.env.RIG_RUN_RESUME === "1";
7931
7977
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
7932
7978
  let prompt = buildRunPrompt({
7933
7979
  projectRoot: context.projectRoot,
@@ -7983,14 +8029,14 @@ async function executeRigOwnedTaskRun(context, input) {
7983
8029
  taskId: runtimeTaskId,
7984
8030
  createdAt: startedAt,
7985
8031
  runtimeAdapter: input.runtimeAdapter,
7986
- status: "created"
8032
+ status: resumeMode ? "preparing" : "created"
7987
8033
  });
7988
8034
  patchAuthorityRun(context.projectRoot, input.runId, {
7989
8035
  status: "preparing",
7990
8036
  startedAt,
7991
8037
  completedAt: null,
7992
8038
  errorText: null,
7993
- artifactRoot: null,
8039
+ artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
7994
8040
  runtimeAdapter: input.runtimeAdapter,
7995
8041
  runtimeMode: input.runtimeMode,
7996
8042
  interactionMode: input.interactionMode,
@@ -8006,9 +8052,9 @@ async function executeRigOwnedTaskRun(context, input) {
8006
8052
  detail: input.taskId ?? input.title ?? runtimeTaskId
8007
8053
  });
8008
8054
  appendRunLog(context.projectRoot, input.runId, {
8009
- id: `log:${input.runId}:start`,
8010
- title: "Rig task run started",
8011
- detail: input.taskId ?? input.title ?? runtimeTaskId,
8055
+ id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
8056
+ title: resumeMode ? "Rig task run resumed" : "Rig task run started",
8057
+ detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
8012
8058
  tone: "info",
8013
8059
  status: "preparing",
8014
8060
  createdAt: startedAt
@@ -8086,11 +8132,11 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8086
8132
  let reviewAction;
8087
8133
  let verificationStarted = false;
8088
8134
  let reviewStarted = false;
8089
- let latestRuntimeWorkspace = null;
8090
- let latestSessionDir = null;
8091
- let latestLogsDir = null;
8135
+ let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
8136
+ let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve22(existingRunRecord.sessionPath, "..") : null;
8137
+ let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
8092
8138
  let latestProviderCommand = null;
8093
- let latestRuntimeBranch = null;
8139
+ let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
8094
8140
  let snapshotSidecarPromise = null;
8095
8141
  let dirtyBaselineApplied = false;
8096
8142
  const childEnv = {
@@ -8108,7 +8154,11 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8108
8154
  RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
8109
8155
  RIG_SERVER_RUN_ID: input.runId
8110
8156
  },
8111
- ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
8157
+ ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
8158
+ ...resumeMode ? {
8159
+ RIG_RUN_RESUME: "1",
8160
+ RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
8161
+ } : {}
8112
8162
  };
8113
8163
  Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
8114
8164
  Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
@@ -8414,7 +8464,25 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8414
8464
  let reviewFailureDetail = null;
8415
8465
  const stderrLines = [];
8416
8466
  const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
8417
- for (let attempt = 1;attempt <= maxAttempts; attempt += 1) {
8467
+ if (resumeMode && latestRuntimeWorkspace) {
8468
+ const acceptedArtifactState = readTaskRunAcceptedArtifactState({
8469
+ taskId: input.taskId ?? runtimeTaskId,
8470
+ workspaceDir: latestRuntimeWorkspace
8471
+ });
8472
+ if (acceptedArtifactState.accepted) {
8473
+ appendRunLog(context.projectRoot, input.runId, {
8474
+ id: `log:${input.runId}:resume-accepted-artifacts`,
8475
+ title: "Resume found accepted artifacts; continuing closeout",
8476
+ detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
8477
+ tone: "info",
8478
+ status: "validating",
8479
+ createdAt: new Date().toISOString()
8480
+ });
8481
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
8482
+ exit = { code: 0, signal: null };
8483
+ }
8484
+ }
8485
+ for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
8418
8486
  const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
8419
8487
  const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
8420
8488
  cwd: context.projectRoot,
@@ -67,6 +67,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
67
67
  const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
68
68
  const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
69
69
  const next = {
70
+ ...existing ?? {},
70
71
  runId: input.runId,
71
72
  projectRoot,
72
73
  workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
@@ -126,6 +126,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
126
126
  const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
127
127
  const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
128
128
  const next = {
129
+ ...existing ?? {},
129
130
  runId: input.runId,
130
131
  projectRoot,
131
132
  workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
@@ -64,6 +64,7 @@ import {
64
64
  listOpenEpics,
65
65
  resolveDefaultEpic,
66
66
  runResume,
67
+ runRestart,
67
68
  runStatus,
68
69
  runStop,
69
70
  startRun,
@@ -406,6 +407,17 @@ async function attachRunOperatorView(context, input) {
406
407
  }
407
408
 
408
409
  // packages/cli/src/commands/run.ts
410
+ function normalizeRemoteRunDetails(payload) {
411
+ const run = payload.run;
412
+ if (!run || typeof run !== "object" || Array.isArray(run))
413
+ return null;
414
+ return {
415
+ ...run,
416
+ ...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
417
+ ...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
418
+ ...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
419
+ };
420
+ }
409
421
  function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
410
422
  if (noEpicPrompt) {
411
423
  return false;
@@ -544,7 +556,7 @@ async function executeRun(context, args) {
544
556
  if (!run.value) {
545
557
  throw new CliError2("run show requires --run <id>.");
546
558
  }
547
- const record = readAuthorityRun(context.projectRoot, run.value);
559
+ const record = readAuthorityRun(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
548
560
  if (!record) {
549
561
  throw new CliError2(`Run not found: ${run.value}`, 2);
550
562
  }
@@ -725,6 +737,20 @@ async function executeRun(context, args) {
725
737
  }
726
738
  return { ok: true, group: "run", command, details: resumed };
727
739
  }
740
+ case "restart": {
741
+ requireNoExtraArgs(rest, "bun run rig run restart");
742
+ if (context.dryRun) {
743
+ if (context.outputMode === "text") {
744
+ console.log("[dry-run] rig run restart");
745
+ }
746
+ return { ok: true, group: "run", command };
747
+ }
748
+ const restarted = await runRestart(context.projectRoot, runtimeContext);
749
+ if (context.outputMode === "text") {
750
+ console.log(`Restarted run: ${restarted.runId}`);
751
+ }
752
+ return { ok: true, group: "run", command, details: restarted };
753
+ }
728
754
  case "stop": {
729
755
  const runOption = takeOption(rest, "--run");
730
756
  const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
@@ -135,6 +135,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
135
135
  const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
136
136
  const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
137
137
  const next = {
138
+ ...existing ?? {},
138
139
  runId: input.runId,
139
140
  projectRoot,
140
141
  workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
@@ -405,7 +406,24 @@ var PI_CANONICAL_RUN_STAGES = [
405
406
  function canonicalPiRunStages() {
406
407
  return [...PI_CANONICAL_RUN_STAGES];
407
408
  }
409
+ function looksLikeGitHubToken(value) {
410
+ const token = value?.trim();
411
+ if (!token)
412
+ return false;
413
+ return /^(gh[opusr]_|github_pat_)/.test(token);
414
+ }
415
+ function githubBridgeEnv(token) {
416
+ const clean = token?.trim();
417
+ if (!clean)
418
+ return {};
419
+ return {
420
+ RIG_GITHUB_TOKEN: clean,
421
+ GITHUB_TOKEN: clean,
422
+ GH_TOKEN: clean
423
+ };
424
+ }
408
425
  function buildPiRigBridgeEnv(input) {
426
+ const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
409
427
  return {
410
428
  RIG_PROJECT_ROOT: input.projectRoot,
411
429
  PROJECT_RIG_ROOT: input.projectRoot,
@@ -415,7 +433,7 @@ function buildPiRigBridgeEnv(input) {
415
433
  RIG_RUNTIME_ADAPTER: "pi",
416
434
  ...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
417
435
  ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
418
- ...input.githubToken ? { RIG_GITHUB_TOKEN: input.githubToken } : {}
436
+ ...githubBridgeEnv(githubToken)
419
437
  };
420
438
  }
421
439
  function runGitSync(cwd, args, input) {
@@ -1064,6 +1082,8 @@ function stringArrayField(record, key) {
1064
1082
  }
1065
1083
  async function executeRigOwnedTaskRun(context, input) {
1066
1084
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
1085
+ const existingRunRecord = readAuthorityRun3(context.projectRoot, input.runId);
1086
+ const resumeMode = process.env.RIG_RUN_RESUME === "1";
1067
1087
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
1068
1088
  let prompt = buildRunPrompt({
1069
1089
  projectRoot: context.projectRoot,
@@ -1119,14 +1139,14 @@ async function executeRigOwnedTaskRun(context, input) {
1119
1139
  taskId: runtimeTaskId,
1120
1140
  createdAt: startedAt,
1121
1141
  runtimeAdapter: input.runtimeAdapter,
1122
- status: "created"
1142
+ status: resumeMode ? "preparing" : "created"
1123
1143
  });
1124
1144
  patchAuthorityRun(context.projectRoot, input.runId, {
1125
1145
  status: "preparing",
1126
1146
  startedAt,
1127
1147
  completedAt: null,
1128
1148
  errorText: null,
1129
- artifactRoot: null,
1149
+ artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
1130
1150
  runtimeAdapter: input.runtimeAdapter,
1131
1151
  runtimeMode: input.runtimeMode,
1132
1152
  interactionMode: input.interactionMode,
@@ -1142,9 +1162,9 @@ async function executeRigOwnedTaskRun(context, input) {
1142
1162
  detail: input.taskId ?? input.title ?? runtimeTaskId
1143
1163
  });
1144
1164
  appendRunLog(context.projectRoot, input.runId, {
1145
- id: `log:${input.runId}:start`,
1146
- title: "Rig task run started",
1147
- detail: input.taskId ?? input.title ?? runtimeTaskId,
1165
+ id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
1166
+ title: resumeMode ? "Rig task run resumed" : "Rig task run started",
1167
+ detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
1148
1168
  tone: "info",
1149
1169
  status: "preparing",
1150
1170
  createdAt: startedAt
@@ -1222,11 +1242,11 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1222
1242
  let reviewAction;
1223
1243
  let verificationStarted = false;
1224
1244
  let reviewStarted = false;
1225
- let latestRuntimeWorkspace = null;
1226
- let latestSessionDir = null;
1227
- let latestLogsDir = null;
1245
+ let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
1246
+ let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve4(existingRunRecord.sessionPath, "..") : null;
1247
+ let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
1228
1248
  let latestProviderCommand = null;
1229
- let latestRuntimeBranch = null;
1249
+ let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
1230
1250
  let snapshotSidecarPromise = null;
1231
1251
  let dirtyBaselineApplied = false;
1232
1252
  const childEnv = {
@@ -1244,7 +1264,11 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1244
1264
  RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
1245
1265
  RIG_SERVER_RUN_ID: input.runId
1246
1266
  },
1247
- ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
1267
+ ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
1268
+ ...resumeMode ? {
1269
+ RIG_RUN_RESUME: "1",
1270
+ RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
1271
+ } : {}
1248
1272
  };
1249
1273
  Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
1250
1274
  Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
@@ -1550,7 +1574,25 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1550
1574
  let reviewFailureDetail = null;
1551
1575
  const stderrLines = [];
1552
1576
  const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
1553
- for (let attempt = 1;attempt <= maxAttempts; attempt += 1) {
1577
+ if (resumeMode && latestRuntimeWorkspace) {
1578
+ const acceptedArtifactState = readTaskRunAcceptedArtifactState({
1579
+ taskId: input.taskId ?? runtimeTaskId,
1580
+ workspaceDir: latestRuntimeWorkspace
1581
+ });
1582
+ if (acceptedArtifactState.accepted) {
1583
+ appendRunLog(context.projectRoot, input.runId, {
1584
+ id: `log:${input.runId}:resume-accepted-artifacts`,
1585
+ title: "Resume found accepted artifacts; continuing closeout",
1586
+ detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
1587
+ tone: "info",
1588
+ status: "validating",
1589
+ createdAt: new Date().toISOString()
1590
+ });
1591
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
1592
+ exit = { code: 0, signal: null };
1593
+ }
1594
+ }
1595
+ for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
1554
1596
  const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
1555
1597
  const child = spawn(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
1556
1598
  cwd: context.projectRoot,
@@ -3240,6 +3240,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3240
3240
  const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
3241
3241
  const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
3242
3242
  const next = {
3243
+ ...existing ?? {},
3243
3244
  runId: input.runId,
3244
3245
  projectRoot,
3245
3246
  workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
@@ -5912,6 +5913,7 @@ import {
5912
5913
  listOpenEpics,
5913
5914
  resolveDefaultEpic,
5914
5915
  runResume,
5916
+ runRestart,
5915
5917
  runStatus,
5916
5918
  runStop,
5917
5919
  startRun,
@@ -6020,6 +6022,17 @@ async function attachRunOperatorView(context, input) {
6020
6022
  }
6021
6023
 
6022
6024
  // packages/cli/src/commands/run.ts
6025
+ function normalizeRemoteRunDetails(payload) {
6026
+ const run = payload.run;
6027
+ if (!run || typeof run !== "object" || Array.isArray(run))
6028
+ return null;
6029
+ return {
6030
+ ...run,
6031
+ ...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
6032
+ ...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
6033
+ ...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
6034
+ };
6035
+ }
6023
6036
  function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
6024
6037
  if (noEpicPrompt) {
6025
6038
  return false;
@@ -6158,7 +6171,7 @@ async function executeRun(context, args) {
6158
6171
  if (!run.value) {
6159
6172
  throw new CliError2("run show requires --run <id>.");
6160
6173
  }
6161
- const record = readAuthorityRun4(context.projectRoot, run.value);
6174
+ const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
6162
6175
  if (!record) {
6163
6176
  throw new CliError2(`Run not found: ${run.value}`, 2);
6164
6177
  }
@@ -6339,6 +6352,20 @@ async function executeRun(context, args) {
6339
6352
  }
6340
6353
  return { ok: true, group: "run", command, details: resumed };
6341
6354
  }
6355
+ case "restart": {
6356
+ requireNoExtraArgs(rest, "bun run rig run restart");
6357
+ if (context.dryRun) {
6358
+ if (context.outputMode === "text") {
6359
+ console.log("[dry-run] rig run restart");
6360
+ }
6361
+ return { ok: true, group: "run", command };
6362
+ }
6363
+ const restarted = await runRestart(context.projectRoot, runtimeContext);
6364
+ if (context.outputMode === "text") {
6365
+ console.log(`Restarted run: ${restarted.runId}`);
6366
+ }
6367
+ return { ok: true, group: "run", command, details: restarted };
6368
+ }
6342
6369
  case "stop": {
6343
6370
  const runOption = takeOption(rest, "--run");
6344
6371
  const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
@@ -7070,7 +7097,24 @@ import {
7070
7097
  commitRunChanges,
7071
7098
  runPrAutomation
7072
7099
  } from "@rig/runtime/control-plane/native/pr-automation";
7100
+ function looksLikeGitHubToken(value) {
7101
+ const token = value?.trim();
7102
+ if (!token)
7103
+ return false;
7104
+ return /^(gh[opusr]_|github_pat_)/.test(token);
7105
+ }
7106
+ function githubBridgeEnv(token) {
7107
+ const clean = token?.trim();
7108
+ if (!clean)
7109
+ return {};
7110
+ return {
7111
+ RIG_GITHUB_TOKEN: clean,
7112
+ GITHUB_TOKEN: clean,
7113
+ GH_TOKEN: clean
7114
+ };
7115
+ }
7073
7116
  function buildPiRigBridgeEnv(input) {
7117
+ const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
7074
7118
  return {
7075
7119
  RIG_PROJECT_ROOT: input.projectRoot,
7076
7120
  PROJECT_RIG_ROOT: input.projectRoot,
@@ -7080,7 +7124,7 @@ function buildPiRigBridgeEnv(input) {
7080
7124
  RIG_RUNTIME_ADAPTER: "pi",
7081
7125
  ...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
7082
7126
  ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
7083
- ...input.githubToken ? { RIG_GITHUB_TOKEN: input.githubToken } : {}
7127
+ ...githubBridgeEnv(githubToken)
7084
7128
  };
7085
7129
  }
7086
7130
  function runGitSync(cwd, args, input) {
@@ -7722,6 +7766,8 @@ function stringArrayField(record, key) {
7722
7766
  }
7723
7767
  async function executeRigOwnedTaskRun(context, input) {
7724
7768
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
7769
+ const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
7770
+ const resumeMode = process.env.RIG_RUN_RESUME === "1";
7725
7771
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
7726
7772
  let prompt = buildRunPrompt({
7727
7773
  projectRoot: context.projectRoot,
@@ -7777,14 +7823,14 @@ async function executeRigOwnedTaskRun(context, input) {
7777
7823
  taskId: runtimeTaskId,
7778
7824
  createdAt: startedAt,
7779
7825
  runtimeAdapter: input.runtimeAdapter,
7780
- status: "created"
7826
+ status: resumeMode ? "preparing" : "created"
7781
7827
  });
7782
7828
  patchAuthorityRun(context.projectRoot, input.runId, {
7783
7829
  status: "preparing",
7784
7830
  startedAt,
7785
7831
  completedAt: null,
7786
7832
  errorText: null,
7787
- artifactRoot: null,
7833
+ artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
7788
7834
  runtimeAdapter: input.runtimeAdapter,
7789
7835
  runtimeMode: input.runtimeMode,
7790
7836
  interactionMode: input.interactionMode,
@@ -7800,9 +7846,9 @@ async function executeRigOwnedTaskRun(context, input) {
7800
7846
  detail: input.taskId ?? input.title ?? runtimeTaskId
7801
7847
  });
7802
7848
  appendRunLog(context.projectRoot, input.runId, {
7803
- id: `log:${input.runId}:start`,
7804
- title: "Rig task run started",
7805
- detail: input.taskId ?? input.title ?? runtimeTaskId,
7849
+ id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
7850
+ title: resumeMode ? "Rig task run resumed" : "Rig task run started",
7851
+ detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
7806
7852
  tone: "info",
7807
7853
  status: "preparing",
7808
7854
  createdAt: startedAt
@@ -7880,11 +7926,11 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
7880
7926
  let reviewAction;
7881
7927
  let verificationStarted = false;
7882
7928
  let reviewStarted = false;
7883
- let latestRuntimeWorkspace = null;
7884
- let latestSessionDir = null;
7885
- let latestLogsDir = null;
7929
+ let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
7930
+ let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve21(existingRunRecord.sessionPath, "..") : null;
7931
+ let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
7886
7932
  let latestProviderCommand = null;
7887
- let latestRuntimeBranch = null;
7933
+ let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
7888
7934
  let snapshotSidecarPromise = null;
7889
7935
  let dirtyBaselineApplied = false;
7890
7936
  const childEnv = {
@@ -7902,7 +7948,11 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
7902
7948
  RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
7903
7949
  RIG_SERVER_RUN_ID: input.runId
7904
7950
  },
7905
- ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
7951
+ ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
7952
+ ...resumeMode ? {
7953
+ RIG_RUN_RESUME: "1",
7954
+ RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
7955
+ } : {}
7906
7956
  };
7907
7957
  Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
7908
7958
  Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
@@ -8208,7 +8258,25 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8208
8258
  let reviewFailureDetail = null;
8209
8259
  const stderrLines = [];
8210
8260
  const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
8211
- for (let attempt = 1;attempt <= maxAttempts; attempt += 1) {
8261
+ if (resumeMode && latestRuntimeWorkspace) {
8262
+ const acceptedArtifactState = readTaskRunAcceptedArtifactState({
8263
+ taskId: input.taskId ?? runtimeTaskId,
8264
+ workspaceDir: latestRuntimeWorkspace
8265
+ });
8266
+ if (acceptedArtifactState.accepted) {
8267
+ appendRunLog(context.projectRoot, input.runId, {
8268
+ id: `log:${input.runId}:resume-accepted-artifacts`,
8269
+ title: "Resume found accepted artifacts; continuing closeout",
8270
+ detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
8271
+ tone: "info",
8272
+ status: "validating",
8273
+ createdAt: new Date().toISOString()
8274
+ });
8275
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
8276
+ exit = { code: 0, signal: null };
8277
+ }
8278
+ }
8279
+ for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
8212
8280
  const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
8213
8281
  const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
8214
8282
  cwd: context.projectRoot,
package/dist/src/index.js CHANGED
@@ -3442,6 +3442,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
3442
3442
  const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
3443
3443
  const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
3444
3444
  const next = {
3445
+ ...existing ?? {},
3445
3446
  runId: input.runId,
3446
3447
  projectRoot,
3447
3448
  workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
@@ -6114,6 +6115,7 @@ import {
6114
6115
  listOpenEpics,
6115
6116
  resolveDefaultEpic,
6116
6117
  runResume,
6118
+ runRestart,
6117
6119
  runStatus,
6118
6120
  runStop,
6119
6121
  startRun,
@@ -6222,6 +6224,17 @@ async function attachRunOperatorView(context, input) {
6222
6224
  }
6223
6225
 
6224
6226
  // packages/cli/src/commands/run.ts
6227
+ function normalizeRemoteRunDetails(payload) {
6228
+ const run = payload.run;
6229
+ if (!run || typeof run !== "object" || Array.isArray(run))
6230
+ return null;
6231
+ return {
6232
+ ...run,
6233
+ ...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
6234
+ ...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
6235
+ ...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
6236
+ };
6237
+ }
6225
6238
  function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
6226
6239
  if (noEpicPrompt) {
6227
6240
  return false;
@@ -6360,7 +6373,7 @@ async function executeRun(context, args) {
6360
6373
  if (!run.value) {
6361
6374
  throw new CliError2("run show requires --run <id>.");
6362
6375
  }
6363
- const record = readAuthorityRun4(context.projectRoot, run.value);
6376
+ const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
6364
6377
  if (!record) {
6365
6378
  throw new CliError2(`Run not found: ${run.value}`, 2);
6366
6379
  }
@@ -6541,6 +6554,20 @@ async function executeRun(context, args) {
6541
6554
  }
6542
6555
  return { ok: true, group: "run", command, details: resumed };
6543
6556
  }
6557
+ case "restart": {
6558
+ requireNoExtraArgs(rest, "bun run rig run restart");
6559
+ if (context.dryRun) {
6560
+ if (context.outputMode === "text") {
6561
+ console.log("[dry-run] rig run restart");
6562
+ }
6563
+ return { ok: true, group: "run", command };
6564
+ }
6565
+ const restarted = await runRestart(context.projectRoot, runtimeContext);
6566
+ if (context.outputMode === "text") {
6567
+ console.log(`Restarted run: ${restarted.runId}`);
6568
+ }
6569
+ return { ok: true, group: "run", command, details: restarted };
6570
+ }
6544
6571
  case "stop": {
6545
6572
  const runOption = takeOption(rest, "--run");
6546
6573
  const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
@@ -7272,7 +7299,24 @@ import {
7272
7299
  commitRunChanges,
7273
7300
  runPrAutomation
7274
7301
  } from "@rig/runtime/control-plane/native/pr-automation";
7302
+ function looksLikeGitHubToken(value) {
7303
+ const token = value?.trim();
7304
+ if (!token)
7305
+ return false;
7306
+ return /^(gh[opusr]_|github_pat_)/.test(token);
7307
+ }
7308
+ function githubBridgeEnv(token) {
7309
+ const clean = token?.trim();
7310
+ if (!clean)
7311
+ return {};
7312
+ return {
7313
+ RIG_GITHUB_TOKEN: clean,
7314
+ GITHUB_TOKEN: clean,
7315
+ GH_TOKEN: clean
7316
+ };
7317
+ }
7275
7318
  function buildPiRigBridgeEnv(input) {
7319
+ const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
7276
7320
  return {
7277
7321
  RIG_PROJECT_ROOT: input.projectRoot,
7278
7322
  PROJECT_RIG_ROOT: input.projectRoot,
@@ -7282,7 +7326,7 @@ function buildPiRigBridgeEnv(input) {
7282
7326
  RIG_RUNTIME_ADAPTER: "pi",
7283
7327
  ...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
7284
7328
  ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
7285
- ...input.githubToken ? { RIG_GITHUB_TOKEN: input.githubToken } : {}
7329
+ ...githubBridgeEnv(githubToken)
7286
7330
  };
7287
7331
  }
7288
7332
  function runGitSync(cwd, args, input) {
@@ -7924,6 +7968,8 @@ function stringArrayField(record, key) {
7924
7968
  }
7925
7969
  async function executeRigOwnedTaskRun(context, input) {
7926
7970
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
7971
+ const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
7972
+ const resumeMode = process.env.RIG_RUN_RESUME === "1";
7927
7973
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
7928
7974
  let prompt = buildRunPrompt({
7929
7975
  projectRoot: context.projectRoot,
@@ -7979,14 +8025,14 @@ async function executeRigOwnedTaskRun(context, input) {
7979
8025
  taskId: runtimeTaskId,
7980
8026
  createdAt: startedAt,
7981
8027
  runtimeAdapter: input.runtimeAdapter,
7982
- status: "created"
8028
+ status: resumeMode ? "preparing" : "created"
7983
8029
  });
7984
8030
  patchAuthorityRun(context.projectRoot, input.runId, {
7985
8031
  status: "preparing",
7986
8032
  startedAt,
7987
8033
  completedAt: null,
7988
8034
  errorText: null,
7989
- artifactRoot: null,
8035
+ artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
7990
8036
  runtimeAdapter: input.runtimeAdapter,
7991
8037
  runtimeMode: input.runtimeMode,
7992
8038
  interactionMode: input.interactionMode,
@@ -8002,9 +8048,9 @@ async function executeRigOwnedTaskRun(context, input) {
8002
8048
  detail: input.taskId ?? input.title ?? runtimeTaskId
8003
8049
  });
8004
8050
  appendRunLog(context.projectRoot, input.runId, {
8005
- id: `log:${input.runId}:start`,
8006
- title: "Rig task run started",
8007
- detail: input.taskId ?? input.title ?? runtimeTaskId,
8051
+ id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
8052
+ title: resumeMode ? "Rig task run resumed" : "Rig task run started",
8053
+ detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
8008
8054
  tone: "info",
8009
8055
  status: "preparing",
8010
8056
  createdAt: startedAt
@@ -8082,11 +8128,11 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8082
8128
  let reviewAction;
8083
8129
  let verificationStarted = false;
8084
8130
  let reviewStarted = false;
8085
- let latestRuntimeWorkspace = null;
8086
- let latestSessionDir = null;
8087
- let latestLogsDir = null;
8131
+ let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
8132
+ let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve22(existingRunRecord.sessionPath, "..") : null;
8133
+ let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
8088
8134
  let latestProviderCommand = null;
8089
- let latestRuntimeBranch = null;
8135
+ let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
8090
8136
  let snapshotSidecarPromise = null;
8091
8137
  let dirtyBaselineApplied = false;
8092
8138
  const childEnv = {
@@ -8104,7 +8150,11 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8104
8150
  RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
8105
8151
  RIG_SERVER_RUN_ID: input.runId
8106
8152
  },
8107
- ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
8153
+ ...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
8154
+ ...resumeMode ? {
8155
+ RIG_RUN_RESUME: "1",
8156
+ RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
8157
+ } : {}
8108
8158
  };
8109
8159
  Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
8110
8160
  Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
@@ -8410,7 +8460,25 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8410
8460
  let reviewFailureDetail = null;
8411
8461
  const stderrLines = [];
8412
8462
  const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
8413
- for (let attempt = 1;attempt <= maxAttempts; attempt += 1) {
8463
+ if (resumeMode && latestRuntimeWorkspace) {
8464
+ const acceptedArtifactState = readTaskRunAcceptedArtifactState({
8465
+ taskId: input.taskId ?? runtimeTaskId,
8466
+ workspaceDir: latestRuntimeWorkspace
8467
+ });
8468
+ if (acceptedArtifactState.accepted) {
8469
+ appendRunLog(context.projectRoot, input.runId, {
8470
+ id: `log:${input.runId}:resume-accepted-artifacts`,
8471
+ title: "Resume found accepted artifacts; continuing closeout",
8472
+ detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
8473
+ tone: "info",
8474
+ status: "validating",
8475
+ createdAt: new Date().toISOString()
8476
+ });
8477
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
8478
+ exit = { code: 0, signal: null };
8479
+ }
8480
+ }
8481
+ for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
8414
8482
  const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
8415
8483
  const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
8416
8484
  cwd: context.projectRoot,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@h-rig/cli",
3
- "version": "0.0.6-alpha.2",
3
+ "version": "0.0.6-alpha.4",
4
4
  "type": "module",
5
5
  "description": "Rig package",
6
6
  "license": "UNLICENSED",
@@ -23,9 +23,9 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@clack/prompts": "^1.2.0",
26
- "@rig/core": "npm:@h-rig/core@0.0.6-alpha.2",
27
- "@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.2",
28
- "@rig/client": "npm:@h-rig/client@0.0.6-alpha.2",
26
+ "@rig/core": "npm:@h-rig/core@0.0.6-alpha.4",
27
+ "@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.4",
28
+ "@rig/client": "npm:@h-rig/client@0.0.6-alpha.4",
29
29
  "picocolors": "^1.1.1"
30
30
  }
31
31
  }