@h-rig/cli 0.0.6-alpha.7 → 0.0.6-alpha.71

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.
Files changed (53) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/rig.js +4507 -1506
  3. package/dist/src/commands/_async-ui.js +152 -0
  4. package/dist/src/commands/_authority-runs.js +2 -3
  5. package/dist/src/commands/_cli-format.js +369 -0
  6. package/dist/src/commands/_connection-state.js +30 -11
  7. package/dist/src/commands/_doctor-checks.js +177 -43
  8. package/dist/src/commands/_help-catalog.js +485 -0
  9. package/dist/src/commands/_json-output.js +56 -0
  10. package/dist/src/commands/_operator-surface.js +220 -0
  11. package/dist/src/commands/_operator-view.js +595 -72
  12. package/dist/src/commands/_parsers.js +18 -11
  13. package/dist/src/commands/_pi-frontend.js +411 -0
  14. package/dist/src/commands/_pi-install.js +4 -3
  15. package/dist/src/commands/_policy.js +12 -5
  16. package/dist/src/commands/_preflight.js +187 -127
  17. package/dist/src/commands/_run-driver-helpers.js +75 -22
  18. package/dist/src/commands/_run-replay.js +142 -0
  19. package/dist/src/commands/_server-client.js +343 -60
  20. package/dist/src/commands/_snapshot-upload.js +160 -38
  21. package/dist/src/commands/_spinner.js +65 -0
  22. package/dist/src/commands/_task-picker.js +44 -16
  23. package/dist/src/commands/agent.js +39 -20
  24. package/dist/src/commands/browser.js +28 -21
  25. package/dist/src/commands/connect.js +146 -33
  26. package/dist/src/commands/dist.js +19 -12
  27. package/dist/src/commands/doctor.js +304 -44
  28. package/dist/src/commands/github.js +301 -52
  29. package/dist/src/commands/inbox.js +679 -72
  30. package/dist/src/commands/init.js +622 -118
  31. package/dist/src/commands/inspect.js +515 -32
  32. package/dist/src/commands/inspector.js +20 -13
  33. package/dist/src/commands/pi.js +177 -0
  34. package/dist/src/commands/plugin.js +95 -27
  35. package/dist/src/commands/profile-and-review.js +26 -19
  36. package/dist/src/commands/queue.js +32 -12
  37. package/dist/src/commands/remote.js +43 -36
  38. package/dist/src/commands/repo-git-harness.js +22 -15
  39. package/dist/src/commands/run.js +1162 -158
  40. package/dist/src/commands/server.js +373 -56
  41. package/dist/src/commands/setup.js +316 -62
  42. package/dist/src/commands/stats.js +1030 -0
  43. package/dist/src/commands/task-report-bug.js +29 -22
  44. package/dist/src/commands/task-run-driver.js +862 -129
  45. package/dist/src/commands/task.js +1423 -311
  46. package/dist/src/commands/test.js +15 -8
  47. package/dist/src/commands/workspace.js +18 -11
  48. package/dist/src/commands.js +4446 -1499
  49. package/dist/src/index.js +4502 -1504
  50. package/dist/src/launcher.js +77 -13
  51. package/dist/src/report-bug.js +3 -3
  52. package/dist/src/runner.js +16 -22
  53. package/package.json +10 -5
@@ -4,18 +4,24 @@ import { mkdir, readdir, readFile, writeFile } from "fs/promises";
4
4
  import { dirname as dirname2, resolve as resolve3, relative, sep } from "path";
5
5
 
6
6
  // packages/cli/src/commands/_server-client.ts
7
- import { spawnSync } from "child_process";
8
7
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
9
8
  import { resolve as resolve2 } from "path";
10
9
 
11
10
  // packages/cli/src/runner.ts
12
11
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
13
- import { CliError } from "@rig/runtime/control-plane/errors";
12
+ import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
14
13
  import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
15
- import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
16
- import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
17
14
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
18
- import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
15
+
16
+ class CliError extends RuntimeCliError {
17
+ hint;
18
+ constructor(message, exitCode = 1, options = {}) {
19
+ super(message, exitCode);
20
+ if (options.hint?.trim()) {
21
+ this.hint = options.hint.trim();
22
+ }
23
+ }
24
+ }
19
25
 
20
26
  // packages/cli/src/commands/_server-client.ts
21
27
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
@@ -42,9 +48,14 @@ function readJsonFile(path) {
42
48
  try {
43
49
  return JSON.parse(readFileSync(path, "utf8"));
44
50
  } catch (error) {
45
- throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
51
+ throw new CliError(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1, { hint: "Fix or delete that file, then re-select a server with `rig server use <alias|local>`." });
46
52
  }
47
53
  }
54
+ function writeJsonFile(path, value) {
55
+ mkdirSync(dirname(path), { recursive: true });
56
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}
57
+ `, "utf8");
58
+ }
48
59
  function normalizeConnection(value) {
49
60
  if (!value || typeof value !== "object" || Array.isArray(value))
50
61
  return null;
@@ -85,25 +96,39 @@ function readRepoConnection(projectRoot) {
85
96
  return {
86
97
  selected,
87
98
  project: typeof record.project === "string" ? record.project : undefined,
88
- linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
99
+ linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
100
+ serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
89
101
  };
90
102
  }
103
+ function writeRepoConnection(projectRoot, state) {
104
+ writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
105
+ }
91
106
  function resolveSelectedConnection(projectRoot, options = {}) {
92
107
  const repo = readRepoConnection(projectRoot);
93
108
  if (!repo)
94
109
  return null;
95
110
  if (repo.selected === "local")
96
- return { alias: "local", connection: { kind: "local", mode: "auto" } };
111
+ return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
97
112
  const global = readGlobalConnections(options);
98
113
  const connection = global.connections[repo.selected];
99
114
  if (!connection) {
100
- throw new CliError2(`Selected Rig connection "${repo.selected}" was not found. Run \`rig connect list\` or \`rig connect use local\`.`, 1);
115
+ throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
101
116
  }
102
- return { alias: repo.selected, connection };
117
+ return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
118
+ }
119
+ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
120
+ const repo = readRepoConnection(projectRoot);
121
+ if (!repo)
122
+ return;
123
+ writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
103
124
  }
104
125
 
105
126
  // packages/cli/src/commands/_server-client.ts
106
- var cachedGitHubBearerToken;
127
+ var scopedGitHubBearerTokens = new Map;
128
+ var serverPhaseListener = null;
129
+ function reportServerPhase(label) {
130
+ serverPhaseListener?.(label);
131
+ }
107
132
  function cleanToken(value) {
108
133
  const trimmed = value?.trim();
109
134
  return trimmed ? trimmed : null;
@@ -120,49 +145,80 @@ function readPrivateRemoteSessionToken(projectRoot) {
120
145
  }
121
146
  }
122
147
  function readGitHubBearerTokenForRemote(projectRoot) {
123
- if (cachedGitHubBearerToken !== undefined)
124
- return cachedGitHubBearerToken;
148
+ const scopedKey = resolve2(projectRoot);
149
+ if (scopedGitHubBearerTokens.has(scopedKey))
150
+ return scopedGitHubBearerTokens.get(scopedKey) ?? null;
125
151
  const privateSession = readPrivateRemoteSessionToken(projectRoot);
126
- if (privateSession) {
127
- cachedGitHubBearerToken = privateSession;
128
- return cachedGitHubBearerToken;
129
- }
130
- const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
131
- if (envToken) {
132
- cachedGitHubBearerToken = envToken;
133
- return cachedGitHubBearerToken;
152
+ if (privateSession)
153
+ return privateSession;
154
+ return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
155
+ }
156
+ function readStoredGitHubAuthToken(projectRoot) {
157
+ const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
158
+ if (!existsSync2(path))
159
+ return null;
160
+ try {
161
+ const parsed = JSON.parse(readFileSync2(path, "utf8"));
162
+ return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
163
+ } catch {
164
+ return null;
134
165
  }
135
- const result = spawnSync("gh", ["auth", "token"], {
136
- encoding: "utf8",
137
- timeout: 5000,
138
- stdio: ["ignore", "pipe", "ignore"]
139
- });
140
- cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
141
- return cachedGitHubBearerToken;
166
+ }
167
+ function readLocalConnectionFallbackToken(projectRoot) {
168
+ return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
142
169
  }
143
170
  async function ensureServerForCli(projectRoot) {
144
171
  try {
145
172
  const selected = resolveSelectedConnection(projectRoot);
146
173
  if (selected?.connection.kind === "remote") {
174
+ reportServerPhase(`Connecting to ${selected.alias}\u2026`);
175
+ const authToken = readGitHubBearerTokenForRemote(projectRoot);
176
+ const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
147
177
  return {
148
178
  baseUrl: selected.connection.baseUrl,
149
- authToken: readGitHubBearerTokenForRemote(projectRoot),
150
- connectionKind: "remote"
179
+ authToken,
180
+ connectionKind: "remote",
181
+ serverProjectRoot
151
182
  };
152
183
  }
184
+ reportServerPhase("Starting local Rig server\u2026");
153
185
  const connection = await ensureLocalRigServerConnection(projectRoot);
154
186
  return {
155
187
  baseUrl: connection.baseUrl,
156
- authToken: connection.authToken,
157
- connectionKind: "local"
188
+ authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
189
+ connectionKind: "local",
190
+ serverProjectRoot: resolve2(projectRoot)
158
191
  };
159
192
  } catch (error) {
160
193
  if (error instanceof Error) {
161
- throw new CliError2(error.message, 1);
194
+ throw new CliError(error.message, 1);
162
195
  }
163
196
  throw error;
164
197
  }
165
198
  }
199
+ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
200
+ const repo = readRepoConnection(projectRoot);
201
+ const slug = repo?.project?.trim();
202
+ if (!slug)
203
+ return null;
204
+ try {
205
+ const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
206
+ headers: mergeHeaders(undefined, authToken)
207
+ });
208
+ if (!response.ok)
209
+ return null;
210
+ const payload = await response.json();
211
+ const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
212
+ const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
213
+ const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
214
+ const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
215
+ if (path)
216
+ writeRepoServerProjectRoot(projectRoot, path);
217
+ return path;
218
+ } catch {
219
+ return null;
220
+ }
221
+ }
166
222
  function mergeHeaders(headers, authToken) {
167
223
  const merged = new Headers(headers);
168
224
  if (authToken) {
@@ -185,12 +241,65 @@ function diagnosticMessage(payload) {
185
241
  });
186
242
  return messages.length > 0 ? messages.join("; ") : null;
187
243
  }
244
+ var serverReachabilityCache = new Map;
245
+ async function probeServerReachability(baseUrl, authToken) {
246
+ try {
247
+ const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/api/server/status`, {
248
+ headers: mergeHeaders(undefined, authToken),
249
+ signal: AbortSignal.timeout(1500)
250
+ });
251
+ return response.ok;
252
+ } catch {
253
+ return false;
254
+ }
255
+ }
256
+ function cachedServerReachability(projectRoot, baseUrl, authToken) {
257
+ const key = resolve2(projectRoot);
258
+ const cached = serverReachabilityCache.get(key);
259
+ if (cached)
260
+ return cached;
261
+ const probe = probeServerReachability(baseUrl, authToken);
262
+ serverReachabilityCache.set(key, probe);
263
+ return probe;
264
+ }
265
+ function describeSelectedServer(projectRoot, server) {
266
+ try {
267
+ const selected = resolveSelectedConnection(projectRoot);
268
+ if (selected) {
269
+ return {
270
+ alias: selected.alias,
271
+ target: selected.connection.kind === "remote" ? selected.connection.baseUrl : server.baseUrl
272
+ };
273
+ }
274
+ } catch {}
275
+ return { alias: server.connectionKind === "remote" ? "remote" : "local", target: server.baseUrl };
276
+ }
277
+ async function buildServerFailureContext(projectRoot, server) {
278
+ const { alias, target } = describeSelectedServer(projectRoot, server);
279
+ const reachable = await cachedServerReachability(projectRoot, server.baseUrl, server.authToken);
280
+ const reachability = reachable ? "server is reachable" : "server is unreachable";
281
+ return {
282
+ contextLine: `Currently connected to: ${alias} at ${target} (${reachability}).`,
283
+ hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
284
+ };
285
+ }
188
286
  async function requestServerJson(context, pathname, init = {}) {
189
287
  const server = await ensureServerForCli(context.projectRoot);
190
- const response = await fetch(`${server.baseUrl}${pathname}`, {
191
- ...init,
192
- headers: mergeHeaders(init.headers, server.authToken)
193
- });
288
+ const headers = mergeHeaders(init.headers, server.authToken);
289
+ if (server.serverProjectRoot)
290
+ headers.set("x-rig-project-root", server.serverProjectRoot);
291
+ reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
292
+ let response;
293
+ try {
294
+ response = await fetch(`${server.baseUrl}${pathname}`, {
295
+ ...init,
296
+ headers
297
+ });
298
+ } catch (error) {
299
+ const failure = await buildServerFailureContext(context.projectRoot, server);
300
+ throw new CliError(`Rig server request failed: ${error instanceof Error ? error.message : String(error)}
301
+ ${failure.contextLine}`, 1, { hint: failure.hint });
302
+ }
194
303
  const text = await response.text();
195
304
  const payload = text.trim().length > 0 ? (() => {
196
305
  try {
@@ -202,10 +311,23 @@ async function requestServerJson(context, pathname, init = {}) {
202
311
  if (!response.ok) {
203
312
  const diagnostics = diagnosticMessage(payload);
204
313
  const detail = diagnostics ?? (text || response.statusText);
205
- throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
314
+ const failure = await buildServerFailureContext(context.projectRoot, server);
315
+ throw new CliError(`Rig server request failed (${response.status}): ${detail}
316
+ ${failure.contextLine}`, 1, { hint: failure.hint });
206
317
  }
207
318
  return payload;
208
319
  }
320
+ var RESUMABLE_RUN_STATUSES = new Set([
321
+ "created",
322
+ "preparing",
323
+ "running",
324
+ "validating",
325
+ "reviewing",
326
+ "stopped",
327
+ "failed",
328
+ "needs-attention",
329
+ "needs_attention"
330
+ ]);
209
331
 
210
332
  // packages/cli/src/commands/_snapshot-upload.ts
211
333
  var UPLOADED_SNAPSHOT_PR_MARKER = "<!-- rig:uploaded-snapshot -->";
@@ -0,0 +1,65 @@
1
+ // @bun
2
+ // packages/cli/src/commands/_spinner.ts
3
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
4
+ function createTtySpinner(input) {
5
+ const output = input.output ?? process.stdout;
6
+ const isTty = output.isTTY === true;
7
+ const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
8
+ let label = input.label;
9
+ let frame = 0;
10
+ let paused = false;
11
+ let stopped = false;
12
+ let lastPrintedLabel = "";
13
+ const render = () => {
14
+ if (stopped || paused)
15
+ return;
16
+ if (!isTty) {
17
+ if (label !== lastPrintedLabel) {
18
+ output.write(`${label}
19
+ `);
20
+ lastPrintedLabel = label;
21
+ }
22
+ return;
23
+ }
24
+ frame = (frame + 1) % frames.length;
25
+ const glyph = frames[frame] ?? frames[0] ?? "";
26
+ output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
27
+ };
28
+ const clearLine = () => {
29
+ if (isTty)
30
+ output.write("\r\x1B[2K");
31
+ };
32
+ render();
33
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
34
+ return {
35
+ setLabel(next) {
36
+ label = next;
37
+ render();
38
+ },
39
+ pause() {
40
+ paused = true;
41
+ clearLine();
42
+ },
43
+ resume() {
44
+ if (stopped)
45
+ return;
46
+ paused = false;
47
+ render();
48
+ },
49
+ stop(finalLine) {
50
+ if (stopped)
51
+ return;
52
+ stopped = true;
53
+ if (timer)
54
+ clearInterval(timer);
55
+ clearLine();
56
+ if (finalLine)
57
+ output.write(`${finalLine}
58
+ `);
59
+ }
60
+ };
61
+ }
62
+ export {
63
+ createTtySpinner,
64
+ SPINNER_FRAMES
65
+ };
@@ -1,6 +1,9 @@
1
1
  // @bun
2
2
  // packages/cli/src/commands/_task-picker.ts
3
- import { createInterface } from "readline/promises";
3
+ import { cancel, isCancel, select } from "@clack/prompts";
4
+
5
+ // packages/cli/src/commands/_operator-surface.ts
6
+ import { createInterface as createPromptInterface } from "readline/promises";
4
7
  function taskId(task) {
5
8
  return typeof task.id === "string" && task.id.trim() ? task.id : "<unknown>";
6
9
  }
@@ -13,6 +16,19 @@ function taskStatus(task) {
13
16
  function renderTaskPickerRows(tasks) {
14
17
  return tasks.map((task, index) => `${index + 1}. ${taskId(task)} \xB7 ${taskStatus(task)} \xB7 ${taskTitle(task)}`);
15
18
  }
19
+ async function promptForTaskSelection(question) {
20
+ const rl = createPromptInterface({ input: process.stdin, output: process.stdout });
21
+ try {
22
+ return await rl.question(question);
23
+ } finally {
24
+ rl.close();
25
+ }
26
+ }
27
+
28
+ // packages/cli/src/commands/_task-picker.ts
29
+ function taskId2(task) {
30
+ return typeof task.id === "string" && task.id.trim() ? task.id : "<unknown>";
31
+ }
16
32
  async function selectTaskWithTextPicker(tasks, io = {}) {
17
33
  if (tasks.length === 0)
18
34
  return null;
@@ -22,25 +38,37 @@ async function selectTaskWithTextPicker(tasks, io = {}) {
22
38
  if (!isTty) {
23
39
  throw new Error("task run requires an interactive terminal to pick a task; pass --task <id>, --next, or --detach with a task id.");
24
40
  }
25
- const prompt = io.prompt ?? (async (question) => {
26
- const rl = createInterface({ input: process.stdin, output: process.stdout });
27
- try {
28
- return await rl.question(question);
29
- } finally {
30
- rl.close();
41
+ if (io.prompt || io.renderer) {
42
+ const prompt = io.prompt ?? promptForTaskSelection;
43
+ const renderer = io.renderer ?? { writeLine: (line) => process.stdout.write(`${line}
44
+ `) };
45
+ renderer.writeLine("Select Rig task:");
46
+ for (const row of renderTaskPickerRows(tasks))
47
+ renderer.writeLine(` ${row}`);
48
+ const answer2 = (await prompt(`Task [1-${tasks.length}] or id: `)).trim();
49
+ if (!answer2)
50
+ return null;
51
+ if (/^\d+$/.test(answer2)) {
52
+ const index2 = Number.parseInt(answer2, 10) - 1;
53
+ return tasks[index2] ?? null;
31
54
  }
55
+ return tasks.find((task) => taskId2(task) === answer2) ?? null;
56
+ }
57
+ const options = tasks.map((task, index2) => ({
58
+ value: `${index2}`,
59
+ label: `${taskId2(task)} \xB7 ${typeof task.title === "string" && task.title.trim() ? task.title.trim() : "Untitled task"}`,
60
+ hint: typeof task.status === "string" && task.status.trim() ? task.status.trim() : undefined
61
+ }));
62
+ const answer = await select({
63
+ message: "Select Rig task",
64
+ options
32
65
  });
33
- console.log("Select Rig task:");
34
- for (const row of renderTaskPickerRows(tasks))
35
- console.log(` ${row}`);
36
- const answer = (await prompt(`Task [1-${tasks.length}] or id: `)).trim();
37
- if (!answer)
66
+ if (isCancel(answer)) {
67
+ cancel("No task selected.");
38
68
  return null;
39
- if (/^\d+$/.test(answer)) {
40
- const index = Number.parseInt(answer, 10) - 1;
41
- return tasks[index] ?? null;
42
69
  }
43
- return tasks.find((task) => taskId(task) === answer) ?? null;
70
+ const index = Number.parseInt(String(answer), 10);
71
+ return Number.isFinite(index) ? tasks[index] ?? null : null;
44
72
  }
45
73
  export {
46
74
  selectTaskWithTextPicker,
@@ -4,12 +4,19 @@ import { resolve as resolve2 } from "path";
4
4
 
5
5
  // packages/cli/src/runner.ts
6
6
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
7
- import { CliError } from "@rig/runtime/control-plane/errors";
7
+ import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
8
8
  import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
9
- import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
10
- import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
11
9
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
12
- import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
10
+
11
+ class CliError extends RuntimeCliError {
12
+ hint;
13
+ constructor(message, exitCode = 1, options = {}) {
14
+ super(message, exitCode);
15
+ if (options.hint?.trim()) {
16
+ this.hint = options.hint.trim();
17
+ }
18
+ }
19
+ }
13
20
  function takeFlag(args, flag) {
14
21
  const rest = [];
15
22
  let value = false;
@@ -30,7 +37,7 @@ function takeOption(args, option) {
30
37
  if (current === option) {
31
38
  const next = args[index + 1];
32
39
  if (!next || next.startsWith("-")) {
33
- throw new CliError(`Missing value for ${option}`);
40
+ throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
34
41
  }
35
42
  value = next;
36
43
  index += 1;
@@ -64,8 +71,7 @@ import { resolve } from "path";
64
71
  import {
65
72
  readAuthorityRun,
66
73
  readJsonlFile,
67
- resolveAuthorityRunDir,
68
- writeJsonFile
74
+ writeAuthorityRunRecord
69
75
  } from "@rig/runtime/control-plane/authority-files";
70
76
 
71
77
  // packages/cli/src/commands/_paths.ts
@@ -159,7 +165,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
159
165
  } else if ("errorText" in next) {
160
166
  delete next.errorText;
161
167
  }
162
- writeJsonFile(resolve(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
168
+ writeAuthorityRunRecord(projectRoot, input.runId, next);
163
169
  return next;
164
170
  }
165
171
 
@@ -213,7 +219,7 @@ function parseIsolationMode(value, allowOff) {
213
219
  if (allowOff && value === "off") {
214
220
  return value;
215
221
  }
216
- throw new CliError2(`Invalid isolation mode: ${value}. Use ${allowOff ? "off|" : ""}worktree.`);
222
+ throw new CliError(`Invalid isolation mode: ${value}. Use ${allowOff ? "off|" : ""}worktree.`);
217
223
  }
218
224
 
219
225
  // packages/cli/src/commands/_preflight.ts
@@ -221,6 +227,19 @@ import { ensureProjectMainFreshBeforeRun } from "@rig/runtime/control-plane/proj
221
227
 
222
228
  // packages/cli/src/commands/_server-client.ts
223
229
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
230
+ var scopedGitHubBearerTokens = new Map;
231
+ var serverReachabilityCache = new Map;
232
+ var RESUMABLE_RUN_STATUSES = new Set([
233
+ "created",
234
+ "preparing",
235
+ "running",
236
+ "validating",
237
+ "reviewing",
238
+ "stopped",
239
+ "failed",
240
+ "needs-attention",
241
+ "needs_attention"
242
+ ]);
224
243
 
225
244
  // packages/cli/src/commands/_preflight.ts
226
245
  async function runProjectMainSyncPreflight(context, options) {
@@ -236,7 +255,7 @@ async function runProjectMainSyncPreflight(context, options) {
236
255
  runBootstrap: async () => {
237
256
  const bootstrap = await context.runCommand(["bun", "run", "bootstrap"]);
238
257
  if (bootstrap.exitCode !== 0) {
239
- throw new CliError2(bootstrap.stderr || bootstrap.stdout || "bun run bootstrap failed during project pre-run sync", bootstrap.exitCode || 1);
258
+ throw new CliError(bootstrap.stderr || bootstrap.stdout || "bun run bootstrap failed during project pre-run sync", bootstrap.exitCode || 1);
240
259
  }
241
260
  }
242
261
  });
@@ -289,7 +308,7 @@ async function executeAgent(context, args) {
289
308
  const [command = "list", ...rest] = args;
290
309
  switch (command) {
291
310
  case "list": {
292
- requireNoExtraArgs(rest, "bun run rig agent list");
311
+ requireNoExtraArgs(rest, "rig agent list");
293
312
  const runtimes = await listAgentRuntimes(context.projectRoot);
294
313
  if (context.outputMode === "text") {
295
314
  if (runtimes.length === 0) {
@@ -310,12 +329,12 @@ async function executeAgent(context, args) {
310
329
  pending = modeResult.rest;
311
330
  const taskResult = takeOption(pending, "--task");
312
331
  pending = taskResult.rest;
313
- requireNoExtraArgs(pending, "bun run rig agent prepare --task <id> [--id <id>] [--mode worktree]");
332
+ requireNoExtraArgs(pending, "rig agent prepare --task <id> [--id <id>] [--mode worktree]");
314
333
  const mode = parseIsolationMode(modeResult.value, false);
315
334
  const id = idResult.value || agentId("agent");
316
335
  const taskId = taskResult.value?.trim();
317
336
  if (!taskId) {
318
- throw new CliError2("Usage: bun run rig agent prepare --task <id> [--id <id>] [--mode worktree]");
337
+ throw new CliError("Usage: rig agent prepare --task <id> [--id <id>] [--mode worktree]");
319
338
  }
320
339
  const runtime = await withMutedConsole(context.outputMode === "json", () => ensureAgentRuntime({
321
340
  projectRoot: context.projectRoot,
@@ -333,7 +352,7 @@ async function executeAgent(context, args) {
333
352
  case "run": {
334
353
  const { options, commandParts } = splitAtDoubleDash(rest);
335
354
  if (commandParts.length === 0) {
336
- throw new CliError2("Usage: bun run rig agent run [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
355
+ throw new CliError("Usage: rig agent run [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
337
356
  }
338
357
  let pending = options;
339
358
  const idResult = takeOption(pending, "--id");
@@ -344,12 +363,12 @@ async function executeAgent(context, args) {
344
363
  pending = taskResult.rest;
345
364
  const skipProjectSyncResult = takeFlag(pending, "--skip-project-sync");
346
365
  pending = skipProjectSyncResult.rest;
347
- requireNoExtraArgs(pending, "bun run rig agent run --task <id> [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
366
+ requireNoExtraArgs(pending, "rig agent run --task <id> [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
348
367
  const mode = parseIsolationMode(modeResult.value, false);
349
368
  const id = idResult.value || agentId("agent-run");
350
369
  const taskId = taskResult.value?.trim();
351
370
  if (!taskId) {
352
- throw new CliError2("Usage: bun run rig agent run --task <id> [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
371
+ throw new CliError("Usage: rig agent run --task <id> [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
353
372
  }
354
373
  await runProjectMainSyncPreflight(context, { disabled: skipProjectSyncResult.value });
355
374
  const createdAt = new Date().toISOString();
@@ -408,7 +427,7 @@ async function executeAgent(context, args) {
408
427
  pid: process.pid,
409
428
  errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
410
429
  });
411
- throw new CliError2(`Agent runtime command failed (${result.exitCode}) in ${runtime.id}${result.stderr ? `
430
+ throw new CliError(`Agent runtime command failed (${result.exitCode}) in ${runtime.id}${result.stderr ? `
412
431
  ${result.stderr.trim()}` : ""}`, result.exitCode);
413
432
  }
414
433
  const completedAt = new Date().toISOString();
@@ -463,9 +482,9 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
463
482
  pending = allResult.rest;
464
483
  const idResult = takeOption(pending, "--id");
465
484
  pending = idResult.rest;
466
- requireNoExtraArgs(pending, "bun run rig agent cleanup (--id <id> | --all)");
485
+ requireNoExtraArgs(pending, "rig agent cleanup (--id <id> | --all)");
467
486
  if (!allResult.value && !idResult.value) {
468
- throw new CliError2("Provide --id <id> or --all.");
487
+ throw new CliError("Provide --id <id> or --all.", 1, { hint: "Run `rig agent list` to find agent ids." });
469
488
  }
470
489
  const runtimes = await listAgentRuntimes(context.projectRoot);
471
490
  const targets = allResult.value ? runtimes.map((runtime) => runtime.id) : [idResult.value];
@@ -490,7 +509,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
490
509
  };
491
510
  }
492
511
  default:
493
- throw new CliError2(`Unknown agent command: ${command}`);
512
+ throw new CliError(`Unknown agent command: ${command}`, 1, { hint: "Run `rig agent --help` to list agent commands." });
494
513
  }
495
514
  }
496
515
  export {