@h-rig/cli-surface-plugin 0.0.6-alpha.146

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 (94) hide show
  1. package/README.md +1 -0
  2. package/dist/src/app/drone-ui.d.ts +34 -0
  3. package/dist/src/app/drone-ui.js +278 -0
  4. package/dist/src/commands/_async-ui.d.ts +10 -0
  5. package/dist/src/commands/_async-ui.js +121 -0
  6. package/dist/src/commands/_cli-format.d.ts +56 -0
  7. package/dist/src/commands/_cli-format.js +332 -0
  8. package/dist/src/commands/_connection-state.d.ts +54 -0
  9. package/dist/src/commands/_connection-state.js +187 -0
  10. package/dist/src/commands/_doctor-checks.d.ts +9 -0
  11. package/dist/src/commands/_doctor-checks.js +24 -0
  12. package/dist/src/commands/_help-catalog.d.ts +29 -0
  13. package/dist/src/commands/_help-catalog.js +157 -0
  14. package/dist/src/commands/_inprocess-services.d.ts +33 -0
  15. package/dist/src/commands/_inprocess-services.js +102 -0
  16. package/dist/src/commands/_json-output.d.ts +11 -0
  17. package/dist/src/commands/_json-output.js +54 -0
  18. package/dist/src/commands/_parsers.d.ts +15 -0
  19. package/dist/src/commands/_parsers.js +114 -0
  20. package/dist/src/commands/_paths.d.ts +11 -0
  21. package/dist/src/commands/_paths.js +50 -0
  22. package/dist/src/commands/_pi-frontend.d.ts +35 -0
  23. package/dist/src/commands/_pi-frontend.js +64 -0
  24. package/dist/src/commands/_pi-install.d.ts +42 -0
  25. package/dist/src/commands/_pi-install.js +167 -0
  26. package/dist/src/commands/_policy.d.ts +8 -0
  27. package/dist/src/commands/_policy.js +138 -0
  28. package/dist/src/commands/_probes.d.ts +1 -0
  29. package/dist/src/commands/_probes.js +13 -0
  30. package/dist/src/commands/_run-driver-helpers.d.ts +26 -0
  31. package/dist/src/commands/_run-driver-helpers.js +132 -0
  32. package/dist/src/commands/_run-subcommands.d.ts +3 -0
  33. package/dist/src/commands/_run-subcommands.js +31 -0
  34. package/dist/src/commands/_spinner.d.ts +25 -0
  35. package/dist/src/commands/_spinner.js +65 -0
  36. package/dist/src/commands/agent.d.ts +3 -0
  37. package/dist/src/commands/agent.js +322 -0
  38. package/dist/src/commands/config.d.ts +3 -0
  39. package/dist/src/commands/config.js +193 -0
  40. package/dist/src/commands/dist.d.ts +28 -0
  41. package/dist/src/commands/dist.js +435 -0
  42. package/dist/src/commands/doctor.d.ts +3 -0
  43. package/dist/src/commands/doctor.js +171 -0
  44. package/dist/src/commands/github.d.ts +3 -0
  45. package/dist/src/commands/github.js +342 -0
  46. package/dist/src/commands/inbox.d.ts +19 -0
  47. package/dist/src/commands/inbox.js +241 -0
  48. package/dist/src/commands/init.d.ts +64 -0
  49. package/dist/src/commands/init.js +1449 -0
  50. package/dist/src/commands/inspect.d.ts +20 -0
  51. package/dist/src/commands/inspect.js +337 -0
  52. package/dist/src/commands/pi.d.ts +3 -0
  53. package/dist/src/commands/pi.js +177 -0
  54. package/dist/src/commands/plugin.d.ts +20 -0
  55. package/dist/src/commands/plugin.js +238 -0
  56. package/dist/src/commands/profile-and-review.d.ts +4 -0
  57. package/dist/src/commands/profile-and-review.js +223 -0
  58. package/dist/src/commands/queue.d.ts +3 -0
  59. package/dist/src/commands/queue.js +197 -0
  60. package/dist/src/commands/remote.d.ts +3 -0
  61. package/dist/src/commands/remote.js +516 -0
  62. package/dist/src/commands/repo-git-harness.d.ts +5 -0
  63. package/dist/src/commands/repo-git-harness.js +282 -0
  64. package/dist/src/commands/run.d.ts +22 -0
  65. package/dist/src/commands/run.js +645 -0
  66. package/dist/src/commands/server.d.ts +3 -0
  67. package/dist/src/commands/server.js +155 -0
  68. package/dist/src/commands/setup.d.ts +16 -0
  69. package/dist/src/commands/setup.js +356 -0
  70. package/dist/src/commands/stats.d.ts +11 -0
  71. package/dist/src/commands/stats.js +219 -0
  72. package/dist/src/commands/task-run-driver.d.ts +93 -0
  73. package/dist/src/commands/task-run-driver.js +136 -0
  74. package/dist/src/commands/task.d.ts +46 -0
  75. package/dist/src/commands/task.js +555 -0
  76. package/dist/src/commands/test.d.ts +3 -0
  77. package/dist/src/commands/test.js +46 -0
  78. package/dist/src/commands/triage.d.ts +11 -0
  79. package/dist/src/commands/triage.js +224 -0
  80. package/dist/src/commands/workspace.d.ts +3 -0
  81. package/dist/src/commands/workspace.js +130 -0
  82. package/dist/src/kernel-dispatch.d.ts +15 -0
  83. package/dist/src/kernel-dispatch.js +16 -0
  84. package/dist/src/plugin.d.ts +3 -0
  85. package/dist/src/plugin.js +5440 -0
  86. package/dist/src/rig-config-package-deps.d.ts +10 -0
  87. package/dist/src/rig-config-package-deps.js +272 -0
  88. package/dist/src/runner.d.ts +47 -0
  89. package/dist/src/runner.js +267 -0
  90. package/dist/src/version.d.ts +8 -0
  91. package/dist/src/version.js +47 -0
  92. package/dist/src/withMutedConsole.d.ts +2 -0
  93. package/dist/src/withMutedConsole.js +42 -0
  94. package/package.json +34 -0
@@ -0,0 +1,645 @@
1
+ // @bun
2
+ var __require = import.meta.require;
3
+
4
+ // packages/cli-surface-plugin/src/commands/run.ts
5
+ import { TERMINAL_RUN_STATUSES } from "@rig/contracts";
6
+ import {
7
+ activeRunByTaskId,
8
+ assertNoActiveRunForTask,
9
+ deliverRemoteRunControl,
10
+ deliverRunControl,
11
+ getRun,
12
+ isReadyTask,
13
+ listRuns,
14
+ listTasks,
15
+ normalizeTaskId,
16
+ readSessionRunEntries,
17
+ readTaskTitle,
18
+ removeRegistryRoom,
19
+ resolveJoinTarget,
20
+ resolveStartTask,
21
+ selectNextReadyTask
22
+ } from "@rig/client";
23
+
24
+ // packages/cli-surface-plugin/src/runner.ts
25
+ import { EventBus } from "@rig/runtime/control-plane/runtime/events";
26
+ import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
27
+ import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
28
+ import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
29
+
30
+ class CliError extends RuntimeCliError {
31
+ hint;
32
+ constructor(message, exitCode = 1, options = {}) {
33
+ super(message, exitCode);
34
+ if (options.hint?.trim()) {
35
+ this.hint = options.hint.trim();
36
+ }
37
+ }
38
+ }
39
+ function takeFlag(args, flag) {
40
+ const rest = [];
41
+ let value = false;
42
+ for (const arg of args) {
43
+ if (arg === flag) {
44
+ value = true;
45
+ continue;
46
+ }
47
+ rest.push(arg);
48
+ }
49
+ return { value, rest };
50
+ }
51
+ function takeOption(args, option) {
52
+ const rest = [];
53
+ let value;
54
+ for (let index = 0;index < args.length; index += 1) {
55
+ const current = args[index];
56
+ if (current === option) {
57
+ const next = args[index + 1];
58
+ if (!next || next.startsWith("-")) {
59
+ throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
60
+ }
61
+ value = next;
62
+ index += 1;
63
+ continue;
64
+ }
65
+ if (current !== undefined) {
66
+ rest.push(current);
67
+ }
68
+ }
69
+ return { value, rest };
70
+ }
71
+ function requireNoExtraArgs(args, usage) {
72
+ if (args.length > 0) {
73
+ throw new CliError(`Unexpected arguments: ${args.join(" ")}
74
+ Usage: ${usage}`);
75
+ }
76
+ }
77
+
78
+ // packages/cli-surface-plugin/src/kernel-dispatch.ts
79
+ async function dispatchThroughKernel(input, options) {
80
+ const { getProcessKernel } = await import("@rig/kernel/boot-default");
81
+ const kernel = getProcessKernel();
82
+ if (!kernel) {
83
+ throw new Error("Kernel not booted: cannot dispatch a run through the transport capability.");
84
+ }
85
+ const runId = await kernel.transport.dispatch(input, options);
86
+ return { runId };
87
+ }
88
+
89
+ // packages/cli-surface-plugin/src/commands/_cli-format.ts
90
+ import pc from "picocolors";
91
+ import { runStatusColorRole, runStatusText, statusColorRole } from "@rig/client";
92
+ var dim = pc.dim;
93
+ var faintBar = pc.dim("\u2502");
94
+ var accent = pc.cyan;
95
+ function truncate(value, width) {
96
+ return value.length <= width ? value : `${value.slice(0, Math.max(0, width - 1))}\u2026`;
97
+ }
98
+ function pad(value, width) {
99
+ return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
100
+ }
101
+ function colorForRole(role) {
102
+ switch (role) {
103
+ case "success":
104
+ return pc.green;
105
+ case "action-yellow":
106
+ return pc.yellow;
107
+ case "active-cyan":
108
+ return pc.cyan;
109
+ case "failure":
110
+ return pc.red;
111
+ case "muted":
112
+ case "neutral":
113
+ return pc.dim;
114
+ }
115
+ }
116
+ function compactDate(value) {
117
+ const parsed = Date.parse(value);
118
+ if (!Number.isFinite(parsed))
119
+ return value;
120
+ return new Date(parsed).toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "Z");
121
+ }
122
+ function firstString(record, keys, fallback = "") {
123
+ for (const key of keys) {
124
+ const value = record[key];
125
+ if (typeof value === "string" && value.trim())
126
+ return value;
127
+ }
128
+ return fallback;
129
+ }
130
+ function runIdOf(run) {
131
+ return firstString(run, ["runId", "id"], "(unknown-run)");
132
+ }
133
+ function taskIdOf(run) {
134
+ return firstString(run, ["taskId", "task", "task_id"]);
135
+ }
136
+ function runTitleOf(run) {
137
+ return firstString(run, ["title", "summary", "name"], taskIdOf(run) || "(untitled)");
138
+ }
139
+ function runLikeStatusText(run) {
140
+ return runStatusText(run);
141
+ }
142
+ function runLikeStatusColor(run) {
143
+ return colorForRole(runStatusColorRole(run));
144
+ }
145
+ function printFormattedOutput(message) {
146
+ console.log(message);
147
+ }
148
+ function formatSection(title, subtitle) {
149
+ return `${pc.bold(accent("\u25C6"))} ${pc.bold(title)}${subtitle ? dim(` \u2014 ${subtitle}`) : ""}`;
150
+ }
151
+ function formatSuccessCard(title, rows = []) {
152
+ const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${faintBar} ${dim(key.padEnd(12))} ${value}`);
153
+ return [formatSection(title), ...body].join(`
154
+ `);
155
+ }
156
+ function formatLegacyAutomationSurface() {
157
+ return [];
158
+ }
159
+ function formatRunList(runs, options = {}) {
160
+ if (runs.length === 0) {
161
+ return [
162
+ formatSection("Runs", "none recorded"),
163
+ options.source === "server" ? dim("No runs recorded on the selected server.") : dim("No runs recorded in local state."),
164
+ "",
165
+ ...formatLegacyAutomationSurface()
166
+ ].join(`
167
+ `);
168
+ }
169
+ const body = runs.map((run) => {
170
+ const row = run;
171
+ const runId = runIdOf(row);
172
+ const status = runLikeStatusText(run);
173
+ const runtime = firstString(row, ["runtime", "runtimeAdapter"], "");
174
+ return [
175
+ pc.bold(runId),
176
+ runLikeStatusColor(run)(pad(truncate(status, 12), 12)),
177
+ `${runTitleOf(row)}${runtime ? dim(` ${runtime}`) : ""}`
178
+ ].join(" ");
179
+ });
180
+ return [formatSection("Runs", options.source === "server" ? "selected server" : "local state"), ...body, "", ...formatLegacyAutomationSurface()].join(`
181
+ `);
182
+ }
183
+ function formatSubmittedRun(input) {
184
+ return formatSuccessCard("Run submitted", [
185
+ ["run", input.runId],
186
+ ["task", input.taskId ?? undefined],
187
+ ["title", input.title ?? undefined],
188
+ ["queue", input.queuePosition ?? undefined]
189
+ ]);
190
+ }
191
+ function formatRunCard(run, options = {}) {
192
+ const record = run;
193
+ const lines = [formatSection(options.title ?? "Run")];
194
+ for (const [label, value] of [
195
+ ["run", runIdOf(record)],
196
+ ["task", taskIdOf(record)],
197
+ ["title", runTitleOf(record)],
198
+ ["status", runLikeStatusText(run)],
199
+ ["runtime", firstString(record, ["runtime", "runtimeAdapter"], "")],
200
+ ["created", firstString(record, ["createdAt"], "")],
201
+ ["updated", firstString(record, ["updatedAt"], "")]
202
+ ]) {
203
+ if (value)
204
+ lines.push(`${faintBar} ${dim(label.padEnd(12))} ${label === "status" ? runLikeStatusColor(run)(value) : label === "created" || label === "updated" ? compactDate(value) : value}`);
205
+ }
206
+ const errorSummary = firstString(record, ["errorSummary"]);
207
+ if (errorSummary)
208
+ lines.push(`${faintBar} Error: ${errorSummary}`);
209
+ return lines.join(`
210
+ `);
211
+ }
212
+ function formatRunStatus(summary, options = {}) {
213
+ const activeRuns = summary.activeRuns ?? [];
214
+ const recentRuns = summary.recentRuns ?? [];
215
+ const lines = [formatSection("Run status", options.source === "server" ? "selected server" : "local state")];
216
+ lines.push(`Active runs (${activeRuns.length})`);
217
+ if (activeRuns.length === 0)
218
+ lines.push(dim("No active runs."));
219
+ else
220
+ for (const run of activeRuns)
221
+ lines.push(formatRunSummaryLine(run));
222
+ lines.push("", `Recent runs (${recentRuns.length})`);
223
+ if (recentRuns.length === 0)
224
+ lines.push(dim("No recent terminal runs."));
225
+ else
226
+ for (const run of recentRuns.slice(0, 10))
227
+ lines.push(formatRunSummaryLine(run));
228
+ lines.push("", ...formatLegacyAutomationSurface());
229
+ return lines.join(`
230
+ `);
231
+ }
232
+ function formatRunSummaryLine(run) {
233
+ const record = run;
234
+ const runId = runIdOf(record);
235
+ const taskId = taskIdOf(record);
236
+ const title = runTitleOf(record);
237
+ const status = runLikeStatusText(run);
238
+ const runtime = firstString(record, ["runtime", "runtimeAdapter"], "");
239
+ const descriptor = [taskId, title].filter(Boolean).join(" \xB7 ");
240
+ return `${faintBar} ${pc.bold(runId)} ${runLikeStatusColor(run)(`\u25CF ${status}`)} ${descriptor}${runtime ? dim(` ${runtime}`) : ""}`;
241
+ }
242
+
243
+ // packages/cli-surface-plugin/src/commands/run.ts
244
+ var TERMINAL = new Set(TERMINAL_RUN_STATUSES);
245
+ var RUN_COMMANDS = new Set([
246
+ "list",
247
+ "status",
248
+ "start",
249
+ "start-parallel",
250
+ "start-serial",
251
+ "show",
252
+ "timeline",
253
+ "replay",
254
+ "attach",
255
+ "steer",
256
+ "stop",
257
+ "pause",
258
+ "resume",
259
+ "restart",
260
+ "delete",
261
+ "cleanup"
262
+ ]);
263
+ function normalizeRunArgs(args) {
264
+ if (args.length === 0)
265
+ return ["status"];
266
+ const first = args[0];
267
+ if (first && RUN_COMMANDS.has(first))
268
+ return args;
269
+ if (first?.startsWith("-") || first?.startsWith("#") || /[\d-]/.test(first ?? ""))
270
+ return ["start", ...args];
271
+ return args;
272
+ }
273
+ function parsePromptOverride(prompt, initialPrompt) {
274
+ const promptText = prompt?.trim();
275
+ const initialPromptText = initialPrompt?.trim();
276
+ if (promptText && initialPromptText) {
277
+ throw new CliError("Pass only one of --prompt or --initial-prompt.", 2, {
278
+ hint: "Use `--prompt <text>`; `--initial-prompt` is accepted as a compatibility alias."
279
+ });
280
+ }
281
+ return promptText || initialPromptText || null;
282
+ }
283
+ function parsePositiveLimit(value) {
284
+ if (value === undefined)
285
+ return;
286
+ const parsed = Number.parseInt(value, 10);
287
+ if (!Number.isFinite(parsed) || parsed <= 0 || String(parsed) !== value.trim()) {
288
+ throw new CliError("--limit must be a positive integer.", 1, { hint: "Re-run with a positive number, e.g. `rig run start-parallel --limit 4`." });
289
+ }
290
+ return parsed;
291
+ }
292
+ function printText(context, message) {
293
+ if (context.outputMode === "text")
294
+ printFormattedOutput(message);
295
+ }
296
+ function toRunLike(run) {
297
+ return {
298
+ runId: run.runId,
299
+ taskId: run.taskId,
300
+ title: run.title,
301
+ status: run.status,
302
+ source: run.source,
303
+ live: run.live,
304
+ stale: run.stale,
305
+ startedAt: run.startedAt,
306
+ updatedAt: run.updatedAt,
307
+ completedAt: run.completedAt,
308
+ joinLink: run.joinLink,
309
+ prUrl: run.prUrl,
310
+ pendingApprovals: run.pendingApprovals,
311
+ pendingInputs: run.pendingInputs,
312
+ steeringCount: run.steeringCount,
313
+ stallCount: run.stallCount
314
+ };
315
+ }
316
+ function positional(args) {
317
+ const idx = args.findIndex((arg) => !arg.startsWith("-"));
318
+ if (idx < 0)
319
+ return { runId: undefined, rest: args };
320
+ return { runId: args[idx], rest: [...args.slice(0, idx), ...args.slice(idx + 1)] };
321
+ }
322
+ function requireRunId(value, usage) {
323
+ if (!value || !value.trim()) {
324
+ throw new CliError(`${usage} requires a run id.`, 2, { hint: "Run `rig run list` to find run ids." });
325
+ }
326
+ return value.trim();
327
+ }
328
+ async function executeRun(context, args, deps = {}) {
329
+ const listRunRecords = deps.listRuns ?? ((root) => listRuns(root));
330
+ const getRunRecord = deps.getRun ?? ((root, id) => getRun(root, id));
331
+ const joinTarget = deps.resolveJoin ?? ((root, id) => resolveJoinTarget(root, id));
332
+ const dispatch = deps.dispatch ?? ((input) => dispatchThroughKernel(input));
333
+ const listTaskRecords = deps.listTasks ?? ((root) => listTasks(root));
334
+ const [command = "status", ...rest] = normalizeRunArgs(args);
335
+ const text = context.outputMode === "text";
336
+ switch (command) {
337
+ case "list": {
338
+ requireNoExtraArgs(rest, "rig run list");
339
+ const runs = await listRunRecords(context.projectRoot);
340
+ if (text)
341
+ printFormattedOutput(formatRunList(runs.map(toRunLike)));
342
+ return { ok: true, group: "run", command, details: { runs: runs.map(toRunLike), source: "local" } };
343
+ }
344
+ case "status": {
345
+ requireNoExtraArgs(rest, "rig run status");
346
+ const runs = await listRunRecords(context.projectRoot);
347
+ const activeRuns = runs.filter((run) => !TERMINAL.has(run.status)).map(toRunLike);
348
+ const recentRuns = runs.filter((run) => TERMINAL.has(run.status)).map(toRunLike);
349
+ if (text)
350
+ printFormattedOutput(formatRunStatus({ activeRuns, recentRuns, runs: [...activeRuns, ...recentRuns] }));
351
+ return { ok: true, group: "run", command, details: { activeRuns, recentRuns } };
352
+ }
353
+ case "start": {
354
+ let pending = rest;
355
+ const nextResult = takeFlag(pending, "--next");
356
+ pending = nextResult.rest;
357
+ const forceResult = takeFlag(pending, "--force");
358
+ pending = forceResult.rest;
359
+ const taskResult = takeOption(pending, "--task");
360
+ pending = taskResult.rest;
361
+ const titleResult = takeOption(pending, "--title");
362
+ pending = titleResult.rest;
363
+ const modelResult = takeOption(pending, "--model");
364
+ pending = modelResult.rest;
365
+ const promptResult = takeOption(pending, "--prompt");
366
+ pending = promptResult.rest;
367
+ const initialPromptResult = takeOption(pending, "--initial-prompt");
368
+ pending = initialPromptResult.rest;
369
+ const taskRef = pending[0]?.startsWith("-") ? undefined : pending[0];
370
+ requireNoExtraArgs(taskRef ? pending.slice(1) : pending, "rig run [start] [#<issue>|<task-id>|--next|--task <id>] [--title <t>] [--model <m>] [--prompt <p>|--initial-prompt <p>] [--force]");
371
+ const { taskId, task } = await resolveStartTask({ projectRoot: context.projectRoot, taskId: taskResult.value ?? taskRef, next: nextResult.value, listTasks: listTaskRecords });
372
+ const title = titleResult.value ?? readTaskTitle(task);
373
+ const model = modelResult.value?.trim() || null;
374
+ const prompt = parsePromptOverride(promptResult.value, initialPromptResult.value);
375
+ if (context.dryRun)
376
+ return { ok: true, group: "run", command, details: { taskId, title: title ?? null, model, prompt, dryRun: true } };
377
+ if (!forceResult.value)
378
+ await assertNoActiveRunForTask(listRunRecords, context.projectRoot, taskId);
379
+ const submitted = await dispatch({ projectRoot: context.projectRoot, taskId, title, force: forceResult.value, model, prompt });
380
+ printText(context, `${formatSubmittedRun({ runId: submitted.runId, taskId, title })}
381
+
382
+ Next: rig run attach ${submitted.runId}`);
383
+ return { ok: true, group: "run", command, details: { runId: submitted.runId, taskId, title: title ?? null, model, prompt, detached: true } };
384
+ }
385
+ case "start-parallel": {
386
+ const forceResult = takeFlag(rest, "--force");
387
+ const limitResult = takeOption(forceResult.rest, "--limit");
388
+ requireNoExtraArgs(limitResult.rest, "rig run start-parallel [--limit <n>] [--force]");
389
+ const limit = parsePositiveLimit(limitResult.value);
390
+ let readyTasks = (await listTaskRecords(context.projectRoot)).filter(isReadyTask);
391
+ let skipped = 0;
392
+ if (!forceResult.value) {
393
+ const active = await activeRunByTaskId(listRunRecords, context.projectRoot);
394
+ const before = readyTasks.length;
395
+ readyTasks = readyTasks.filter((task) => {
396
+ const id = normalizeTaskId(task.id);
397
+ return !(id !== undefined && active.has(id));
398
+ });
399
+ skipped = before - readyTasks.length;
400
+ }
401
+ const selectedTasks = limit === undefined ? readyTasks : readyTasks.slice(0, limit);
402
+ if (selectedTasks.length === 0) {
403
+ throw new CliError(skipped > 0 ? `No ready tasks without an active run (${skipped} already running; use --force to re-dispatch).` : "No ready tasks found.", 1, { hint: "Run `rig task list` to inspect available tasks." });
404
+ }
405
+ if (context.dryRun) {
406
+ return { ok: true, group: "run", command, details: { count: selectedTasks.length, taskIds: selectedTasks.map((task) => task.id), limit: limit ?? null, skipped, dryRun: true } };
407
+ }
408
+ const submittedRuns = [];
409
+ for (const task of selectedTasks) {
410
+ const taskId = normalizeTaskId(task.id);
411
+ if (!taskId)
412
+ continue;
413
+ const title = readTaskTitle(task);
414
+ const submitted = await dispatch({ projectRoot: context.projectRoot, taskId, title });
415
+ submittedRuns.push({ runId: submitted.runId, taskId, title: title ?? null });
416
+ }
417
+ if (submittedRuns.length === 0)
418
+ throw new CliError("No ready tasks with task ids found.", 1, { hint: "Run `rig task list` to inspect available tasks." });
419
+ printText(context, [
420
+ `Dispatched ${submittedRuns.length} ready task${submittedRuns.length === 1 ? "" : "s"} as detached runs:`,
421
+ ...submittedRuns.map((run) => `- ${run.taskId} -> ${run.runId}`),
422
+ ...skipped > 0 ? [`(skipped ${skipped} task${skipped === 1 ? "" : "s"} already running; --force to re-dispatch)`] : [],
423
+ "",
424
+ `Next: rig run attach <runId> (for example: rig run attach ${submittedRuns[0].runId})`
425
+ ].join(`
426
+ `));
427
+ return { ok: true, group: "run", command, details: { count: submittedRuns.length, runs: submittedRuns, limit: limit ?? null, skipped, detached: true } };
428
+ }
429
+ case "start-serial": {
430
+ const force = takeFlag(rest, "--force");
431
+ requireNoExtraArgs(force.rest, "rig run start-serial [--force]");
432
+ const task = selectNextReadyTask(await listTaskRecords(context.projectRoot));
433
+ const taskId = normalizeTaskId(task?.id);
434
+ if (!task || !taskId)
435
+ throw new CliError("No ready task found.", 1, { hint: "Run `rig task list` to inspect available tasks." });
436
+ const title = readTaskTitle(task);
437
+ if (context.dryRun)
438
+ return { ok: true, group: "run", command, details: { taskId, title: title ?? null, dryRun: true, serialStepOnly: true } };
439
+ if (!force.value)
440
+ await assertNoActiveRunForTask(listRunRecords, context.projectRoot, taskId);
441
+ const submitted = await dispatch({ projectRoot: context.projectRoot, taskId, title });
442
+ printText(context, `${formatSubmittedRun({ runId: submitted.runId, taskId, title })}
443
+
444
+ Note: start-serial dispatches only the next ready task; run it again after that run finishes to continue the serial queue.
445
+
446
+ Next: rig run attach ${submitted.runId}`);
447
+ return { ok: true, group: "run", command, details: { runId: submitted.runId, taskId, title: title ?? null, detached: true, serialStepOnly: true } };
448
+ }
449
+ case "show": {
450
+ const raw = takeFlag(rest, "--raw");
451
+ const run = takeOption(raw.rest, "--run");
452
+ const { runId: pos, rest: extra } = positional(run.rest);
453
+ requireNoExtraArgs(extra, "rig run show <id>|--run <id> [--raw]");
454
+ const runId = requireRunId(run.value ?? pos, "rig run show");
455
+ const record = await getRunRecord(context.projectRoot, runId);
456
+ if (!record)
457
+ throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see runs." });
458
+ if (text)
459
+ printFormattedOutput(raw.value ? JSON.stringify(record, null, 2) : formatRunCard(toRunLike(record)));
460
+ return { ok: true, group: "run", command, details: { run: toRunLike(record), projection: record.projection } };
461
+ }
462
+ case "timeline":
463
+ case "replay": {
464
+ const withSession = takeFlag(rest, "--with-session");
465
+ const run = takeOption(withSession.rest, "--run");
466
+ const { runId: pos, rest: extra } = positional(run.rest);
467
+ requireNoExtraArgs(extra, `rig run ${command} <id>|--run <id>`);
468
+ const runId = requireRunId(run.value ?? pos, `rig run ${command}`);
469
+ const record = await getRunRecord(context.projectRoot, runId);
470
+ if (!record)
471
+ throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see runs." });
472
+ const entries = readSessionRunEntries(record.sessionPath).filter((entry) => typeof entry.customType === "string" && entry.customType.startsWith("rig.run."));
473
+ if (text) {
474
+ printFormattedOutput(formatRunCard(toRunLike(record)));
475
+ if (entries.length === 0) {
476
+ printFormattedOutput("(no run journal entries recorded for this run yet)");
477
+ } else {
478
+ for (const entry of entries) {
479
+ const data = entry.data;
480
+ const at = data && typeof data === "object" && "at" in data && typeof data.at === "string" ? data.at : "";
481
+ const kind = String(entry.customType).replace("rig.run.", "");
482
+ printFormattedOutput(`${at ? `${at} ` : ""}${kind} ${JSON.stringify(data)}`.trim());
483
+ }
484
+ }
485
+ }
486
+ return { ok: true, group: "run", command, details: { runId: record.runId, entryCount: entries.length, entries } };
487
+ }
488
+ case "attach": {
489
+ const message = takeOption(rest, "--message");
490
+ const run = takeOption(message.rest, "--run");
491
+ const { runId: pos, rest: extra } = positional(run.rest);
492
+ requireNoExtraArgs(extra, "rig run attach <id>|--run <id>");
493
+ const runId = requireRunId(run.value ?? pos, "rig run attach");
494
+ if (message.value?.trim()) {
495
+ throw new CliError("`rig run attach` does not deliver messages; steer the run instead.", 2, { hint: `rig run steer ${runId} --message <text>` });
496
+ }
497
+ const target = await joinTarget(context.projectRoot, runId);
498
+ if (!target)
499
+ throw new CliError(`Run ${runId} is not currently joinable.`, 2, { hint: "It may be finished or offline. Run `rig run show <id>` for its recorded state." });
500
+ if (target.stale)
501
+ throw new CliError(`Run ${runId} is stale (no recent heartbeat); it may have exited.`, 2, { hint: "Run `rig run show <id>` for its last recorded state." });
502
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
503
+ if (text) {
504
+ printFormattedOutput(`Run ${runId} live collab link:`);
505
+ printFormattedOutput(target.joinLink);
506
+ printFormattedOutput("Attach interactively with: rig join <link>");
507
+ }
508
+ return { ok: true, group: "run", command, details: { runId, joinLink: target.joinLink, attached: false } };
509
+ }
510
+ if (text) {
511
+ printFormattedOutput(`Run ${runId} live collab link:`);
512
+ printFormattedOutput(target.joinLink);
513
+ printFormattedOutput("Attach interactively with: rig join <link>");
514
+ }
515
+ return { ok: true, group: "run", command, details: { runId, joinLink: target.joinLink, attached: false } };
516
+ }
517
+ case "steer": {
518
+ const message = takeOption(rest, "--message");
519
+ const short = takeOption(message.rest, "-m");
520
+ const run = takeOption(short.rest, "--run");
521
+ const { runId: pos, rest: extra } = positional(run.rest);
522
+ requireNoExtraArgs(extra, "rig run steer <id>|--run <id> --message <text>");
523
+ const runId = requireRunId(run.value ?? pos, "rig run steer");
524
+ const messageText = (message.value ?? short.value)?.trim();
525
+ if (!messageText)
526
+ throw new CliError("rig run steer requires --message <text>.", 2);
527
+ const record = await getRunRecord(context.projectRoot, runId);
528
+ if (!record)
529
+ throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see runs." });
530
+ await deliverRunControl(context.projectRoot, record, { kind: "steer", message: messageText }, { deliverRemote: deps.deliverRemote ?? deliverRemoteRunControl });
531
+ if (text)
532
+ printFormattedOutput(`Steered run ${record.runId}.`);
533
+ return { ok: true, group: "run", command, details: { runId: record.runId, steered: true } };
534
+ }
535
+ case "stop": {
536
+ const run = takeOption(rest, "--run");
537
+ const reasonOpt = takeOption(run.rest, "--reason");
538
+ const { runId: pos, rest: extra } = positional(reasonOpt.rest);
539
+ requireNoExtraArgs(extra, "rig run stop <id>|--run <id> [--reason <text>]");
540
+ const runId = requireRunId(run.value ?? pos, "rig run stop");
541
+ const record = await getRunRecord(context.projectRoot, runId);
542
+ if (!record)
543
+ throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see runs." });
544
+ await deliverRunControl(context.projectRoot, record, { kind: "stop", reason: reasonOpt.value ?? "operator requested stop" }, { deliverRemote: deps.deliverRemote ?? deliverRemoteRunControl });
545
+ if (text)
546
+ printFormattedOutput(`Requested stop for run ${record.runId}.`);
547
+ return { ok: true, group: "run", command, details: { runId: record.runId, stopRequested: true } };
548
+ }
549
+ case "pause": {
550
+ const run = takeOption(rest, "--run");
551
+ const { runId: pos, rest: extra } = positional(run.rest);
552
+ requireNoExtraArgs(extra, "rig run pause <id>|--run <id>");
553
+ const runId = requireRunId(run.value ?? pos, "rig run pause");
554
+ const record = await getRunRecord(context.projectRoot, runId);
555
+ if (!record)
556
+ throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see runs." });
557
+ if (!record.live || record.stale)
558
+ throw new CliError(`Run ${runId} is not live; only a running run can be paused.`, 2, { hint: `Re-dispatch it instead: rig run restart ${runId}` });
559
+ if (context.dryRun)
560
+ return { ok: true, group: "run", command, details: { runId: record.runId, dryRun: true } };
561
+ await deliverRunControl(context.projectRoot, record, { kind: "pause" }, { deliverRemote: deps.deliverRemote ?? deliverRemoteRunControl });
562
+ if (text)
563
+ printFormattedOutput(`Requested pause for run ${record.runId}. Resume it with \`rig run resume ${record.runId}\`.`);
564
+ return { ok: true, group: "run", command, details: { runId: record.runId, pauseRequested: true } };
565
+ }
566
+ case "resume": {
567
+ const run = takeOption(rest, "--run");
568
+ const { runId: pos, rest: extra } = positional(run.rest);
569
+ requireNoExtraArgs(extra, "rig run resume <id>|--run <id>");
570
+ const runId = requireRunId(run.value ?? pos, "rig run resume");
571
+ const record = await getRunRecord(context.projectRoot, runId);
572
+ if (!record)
573
+ throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see runs." });
574
+ if (record.live && !record.stale) {
575
+ if (context.dryRun)
576
+ return { ok: true, group: "run", command, details: { runId: record.runId, dryRun: true } };
577
+ await deliverRunControl(context.projectRoot, record, { kind: "resume" }, { deliverRemote: deps.deliverRemote ?? deliverRemoteRunControl });
578
+ if (text)
579
+ printFormattedOutput(`Requested resume for run ${record.runId}.`);
580
+ return { ok: true, group: "run", command, details: { runId: record.runId, resumeRequested: true } };
581
+ }
582
+ if (!record.taskId)
583
+ throw new CliError(`Run ${runId} is not live and has no task id to re-dispatch.`, 2);
584
+ if (context.dryRun)
585
+ return { ok: true, group: "run", command, details: { taskId: record.taskId, dryRun: true } };
586
+ const redispatched = await dispatch({ projectRoot: context.projectRoot, taskId: record.taskId, title: record.title });
587
+ if (text)
588
+ printFormattedOutput(`Run ${runId} was not live \u2014 re-dispatched ${record.taskId} as run ${redispatched.runId}.`);
589
+ return { ok: true, group: "run", command, details: { previousRunId: runId, runId: redispatched.runId, taskId: record.taskId } };
590
+ }
591
+ case "restart": {
592
+ const run = takeOption(rest, "--run");
593
+ const { runId: pos, rest: extra } = positional(run.rest);
594
+ requireNoExtraArgs(extra, `rig run ${command} <id>|--run <id>`);
595
+ const runId = requireRunId(run.value ?? pos, `rig run ${command}`);
596
+ const record = await getRunRecord(context.projectRoot, runId);
597
+ if (!record)
598
+ throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see runs." });
599
+ if (!record.taskId)
600
+ throw new CliError(`Run ${runId} has no task id; it cannot be re-dispatched.`, 2);
601
+ if (record.live && !record.stale)
602
+ throw new CliError(`Run ${runId} is still live.`, 2, { hint: `Stop it first (rig run stop ${runId}) or attach: rig run attach ${runId}` });
603
+ if (context.dryRun)
604
+ return { ok: true, group: "run", command, details: { taskId: record.taskId, dryRun: true } };
605
+ const dispatched = await dispatch({ projectRoot: context.projectRoot, taskId: record.taskId, title: record.title });
606
+ if (text)
607
+ printFormattedOutput(`Re-dispatched ${record.taskId} as run ${dispatched.runId} (detached runs start fresh).`);
608
+ return { ok: true, group: "run", command, details: { previousRunId: runId, runId: dispatched.runId, taskId: record.taskId } };
609
+ }
610
+ case "delete":
611
+ case "cleanup": {
612
+ const run = takeOption(rest, "--run");
613
+ const { runId: pos, rest: extra } = positional(run.rest);
614
+ requireNoExtraArgs(extra, `rig run ${command} [<id>|--run <id>]`);
615
+ const runIdArg = run.value ?? pos;
616
+ if (!runIdArg) {
617
+ const staleRuns = (await listRunRecords(context.projectRoot)).filter((r) => r.stale);
618
+ if (context.dryRun)
619
+ return { ok: true, group: "run", command, details: { staleCount: staleRuns.length, dryRun: true } };
620
+ let removed2 = 0;
621
+ for (const r of staleRuns)
622
+ if (await removeRegistryRoom(context.projectRoot, r.runId))
623
+ removed2 += 1;
624
+ if (text)
625
+ printFormattedOutput(staleRuns.length ? `Pruned ${removed2}/${staleRuns.length} stale run(s) from the live registry. (On-disk session journals are retained.)` : "No stale runs to prune.");
626
+ return { ok: true, group: "run", command, details: { staleCount: staleRuns.length, removed: removed2 } };
627
+ }
628
+ const runId = requireRunId(runIdArg, `rig run ${command}`);
629
+ const record = await getRunRecord(context.projectRoot, runId);
630
+ if (record && record.live && !record.stale)
631
+ throw new CliError(`Run ${runId} is still live; stop it before deleting.`, 2, { hint: `Attach to stop: rig run attach ${runId}` });
632
+ if (context.dryRun)
633
+ return { ok: true, group: "run", command, details: { runId, dryRun: true } };
634
+ const removed = await removeRegistryRoom(context.projectRoot, record?.runId ?? runId);
635
+ if (text)
636
+ printFormattedOutput(removed ? `Removed run ${runId} from the live registry.` : `Run ${runId} had no live registry entry to remove; its on-disk session journal is retained.`);
637
+ return { ok: true, group: "run", command, details: { runId, registryRemoved: removed } };
638
+ }
639
+ default:
640
+ throw new CliError(`Unknown run command: ${command}`, 1, { hint: "Run `rig run --help` to list run commands." });
641
+ }
642
+ }
643
+ export {
644
+ executeRun
645
+ };
@@ -0,0 +1,3 @@
1
+ import type { CommandOutcome } from "@rig/runtime";
2
+ import { type RunnerContext } from "../runner";
3
+ export declare function executeServer(context: RunnerContext, args: string[]): Promise<CommandOutcome>;