@h-rig/cli 0.0.6-alpha.27 → 0.0.6-alpha.29

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 (42) hide show
  1. package/dist/bin/rig.js +1505 -907
  2. package/dist/src/commands/_cli-format.js +211 -6
  3. package/dist/src/commands/_connection-state.js +1 -3
  4. package/dist/src/commands/_doctor-checks.js +3 -5
  5. package/dist/src/commands/_help-catalog.js +251 -66
  6. package/dist/src/commands/_operator-view.js +1 -3
  7. package/dist/src/commands/_parsers.js +0 -2
  8. package/dist/src/commands/_pi-frontend.js +1 -3
  9. package/dist/src/commands/_pi-worker-bridge-extension.js +1 -3
  10. package/dist/src/commands/_policy.js +0 -2
  11. package/dist/src/commands/_preflight.js +2 -4
  12. package/dist/src/commands/_run-driver-helpers.js +0 -2
  13. package/dist/src/commands/_server-client.js +1 -3
  14. package/dist/src/commands/_snapshot-upload.js +1 -3
  15. package/dist/src/commands/agent.js +7 -9
  16. package/dist/src/commands/browser.js +4 -6
  17. package/dist/src/commands/connect.js +5 -6
  18. package/dist/src/commands/dist.js +4 -6
  19. package/dist/src/commands/doctor.js +3 -5
  20. package/dist/src/commands/github.js +1 -3
  21. package/dist/src/commands/inbox.js +351 -31
  22. package/dist/src/commands/init.js +3 -5
  23. package/dist/src/commands/inspect.js +10 -12
  24. package/dist/src/commands/inspector.js +2 -4
  25. package/dist/src/commands/plugin.js +76 -22
  26. package/dist/src/commands/profile-and-review.js +8 -10
  27. package/dist/src/commands/queue.js +1 -3
  28. package/dist/src/commands/remote.js +18 -20
  29. package/dist/src/commands/repo-git-harness.js +6 -8
  30. package/dist/src/commands/run.js +159 -41
  31. package/dist/src/commands/server.js +6 -7
  32. package/dist/src/commands/setup.js +7 -15
  33. package/dist/src/commands/task-report-bug.js +5 -7
  34. package/dist/src/commands/task-run-driver.js +1 -3
  35. package/dist/src/commands/task.js +483 -50
  36. package/dist/src/commands/test.js +3 -5
  37. package/dist/src/commands/workspace.js +4 -6
  38. package/dist/src/commands.js +1508 -901
  39. package/dist/src/index.js +1511 -916
  40. package/dist/src/report-bug.js +3 -3
  41. package/dist/src/runner.js +2 -17
  42. package/package.json +6 -6
@@ -1,10 +1,15 @@
1
1
  // @bun
2
2
  // packages/cli/src/commands/_cli-format.ts
3
+ import { log, note } from "@clack/prompts";
3
4
  import pc from "picocolors";
4
5
  function stringField(record, key, fallback = "") {
5
6
  const value = record[key];
6
7
  return typeof value === "string" && value.trim() ? value.trim() : fallback;
7
8
  }
9
+ function numberField(record, key) {
10
+ const value = record[key];
11
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
12
+ }
8
13
  function arrayField(record, key) {
9
14
  const value = record[key];
10
15
  return Array.isArray(value) ? value.flatMap((entry) => typeof entry === "string" && entry.trim() ? [entry.trim()] : []) : [];
@@ -25,16 +30,68 @@ function pad(value, width) {
25
30
  }
26
31
  function statusColor(status) {
27
32
  const normalized = status.toLowerCase();
28
- if (["completed", "merged", "closed", "done", "accepted", "pass", "selected"].includes(normalized))
33
+ if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved"].includes(normalized))
29
34
  return pc.green;
30
- if (["failed", "needs_attention", "needs-attention", "blocked", "error"].includes(normalized))
35
+ if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
31
36
  return pc.red;
32
37
  if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
33
38
  return pc.cyan;
34
- if (["ready", "open", "queued", "created", "preparing", "local"].includes(normalized))
39
+ if (["ready", "open", "queued", "created", "preparing", "local", "pending"].includes(normalized))
35
40
  return pc.yellow;
36
41
  return pc.dim;
37
42
  }
43
+ function compactDate(value) {
44
+ if (!value.trim())
45
+ return "";
46
+ const parsed = Date.parse(value);
47
+ if (!Number.isFinite(parsed))
48
+ return value;
49
+ return new Date(parsed).toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "Z");
50
+ }
51
+ function compactValue(value) {
52
+ if (value === null || value === undefined)
53
+ return "";
54
+ if (typeof value === "string")
55
+ return value;
56
+ if (typeof value === "number" || typeof value === "boolean")
57
+ return String(value);
58
+ if (Array.isArray(value))
59
+ return value.map(compactValue).filter(Boolean).join(", ");
60
+ return JSON.stringify(value);
61
+ }
62
+ function firstString(record, keys, fallback = "") {
63
+ for (const key of keys) {
64
+ const value = stringField(record, key);
65
+ if (value)
66
+ return value;
67
+ }
68
+ return fallback;
69
+ }
70
+ function runIdOf(run) {
71
+ return firstString(run, ["runId", "id"], "(unknown-run)");
72
+ }
73
+ function taskIdOf(run) {
74
+ return firstString(run, ["taskId", "task", "task_id"]);
75
+ }
76
+ function runTitleOf(run) {
77
+ return firstString(run, ["title", "summary", "name"], taskIdOf(run) || "(untitled)");
78
+ }
79
+ function requestIdOf(entry) {
80
+ return firstString(entry, ["requestId", "id", "approvalId", "inputId"], "(unknown-request)");
81
+ }
82
+ function shouldUseClackOutput() {
83
+ return Boolean(process.stdout.isTTY) && process.env.RIG_CLI_PLAIN_HELP !== "1";
84
+ }
85
+ function printFormattedOutput(message, options = {}) {
86
+ if (!shouldUseClackOutput()) {
87
+ console.log(message);
88
+ return;
89
+ }
90
+ if (options.title)
91
+ note(message, options.title);
92
+ else
93
+ log.message(message);
94
+ }
38
95
  function formatStatusPill(status) {
39
96
  const label = status || "unknown";
40
97
  return statusColor(label)(`\u25CF ${label}`);
@@ -43,7 +100,7 @@ function formatSection(title, subtitle) {
43
100
  return `${pc.bold(pc.cyan("\u25C6"))} ${pc.bold(title)}${subtitle ? pc.dim(` \u2014 ${subtitle}`) : ""}`;
44
101
  }
45
102
  function formatSuccessCard(title, rows = []) {
46
- const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${pc.dim("\u2502")} ${pc.dim(key.padEnd(9))} ${value}`);
103
+ const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${pc.dim("\u2502")} ${pc.dim(key.padEnd(12))} ${value}`);
47
104
  return [formatSection(title), ...body].join(`
48
105
  `);
49
106
  }
@@ -83,6 +140,40 @@ function formatTaskList(tasks, options = {}) {
83
140
  return [formatSection("Tasks", `${rows.length} shown`), header, ...body, "", ...formatNextSteps(["Run one: `rig task run <id>` or `rig task run --next`", "Attach later: `rig run attach <run-id> --follow`"])].join(`
84
141
  `);
85
142
  }
143
+ function formatTaskCard(task, options = {}) {
144
+ const raw = rawObject(task);
145
+ const id = stringField(task, "id", stringField(raw, "id", "<unknown>"));
146
+ const status = stringField(task, "status", stringField(raw, "status", "unknown"));
147
+ const title = stringField(task, "title", stringField(raw, "title", "Untitled task"));
148
+ const source = stringField(task, "source", stringField(raw, "source", ""));
149
+ const url = stringField(task, "url", stringField(raw, "url", ""));
150
+ const number = numberField(task, "number") ?? numberField(raw, "number");
151
+ const labels = arrayField(task, "labels").length > 0 ? arrayField(task, "labels") : arrayField(raw, "labels");
152
+ const assignees = arrayField(task, "assignees").length > 0 ? arrayField(task, "assignees") : arrayField(raw, "assignees");
153
+ const readiness = compactValue(task.readiness ?? raw.readiness);
154
+ const validators = compactValue(task.validators ?? raw.validators ?? task.validation ?? raw.validation);
155
+ const rows = [
156
+ ["task", pc.bold(id)],
157
+ ["status", formatStatusPill(status)],
158
+ ["title", title],
159
+ ["source", source],
160
+ ["number", number],
161
+ ["labels", labels.length ? labels.map((label) => `#${label}`).join(" ") : ""],
162
+ ["assignees", assignees.join(", ")],
163
+ ["readiness", readiness],
164
+ ["validators", validators],
165
+ ["url", url]
166
+ ];
167
+ return [
168
+ formatSuccessCard(options.title ?? (options.selected ? "Selected task" : "Task"), rows),
169
+ "",
170
+ ...formatNextSteps([`Start: \`rig task run ${id}\``, `Details: \`rig task show ${id} --raw\``])
171
+ ].join(`
172
+ `);
173
+ }
174
+ function formatTaskDetails(task) {
175
+ return formatTaskCard(task, { title: "Task details" });
176
+ }
86
177
  function formatRunList(runs, options = {}) {
87
178
  if (runs.length === 0) {
88
179
  return [
@@ -109,7 +200,7 @@ function formatRunList(runs, options = {}) {
109
200
  statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
110
201
  `${row.title}${row.runtime ? pc.dim(` ${row.runtime}`) : ""}`
111
202
  ].join(" "));
112
- return [formatSection("Runs", options.source === "server" ? "selected server" : "local state"), header, ...body, "", ...formatNextSteps(["Follow live: `rig run attach <run-id> --follow`", "Details: `rig run show --run <run-id>`"])].join(`
203
+ return [formatSection("Runs", options.source === "server" ? "selected server" : "local state"), header, ...body, "", ...formatNextSteps(["Follow live: `rig run attach <run-id> --follow`", "Details: `rig run show <run-id>`"])].join(`
113
204
  `);
114
205
  }
115
206
  function formatSubmittedRun(input) {
@@ -120,13 +211,121 @@ function formatSubmittedRun(input) {
120
211
  const title = stringField(input.task, "title", "Untitled task");
121
212
  rows.push(["task", `${pc.bold(id)} ${formatStatusPill(status)} ${title}`]);
122
213
  }
214
+ const runtime = [input.runtimeAdapter || "pi", input.runtimeMode || "full-access", input.interactionMode || "default"].filter(Boolean).join(" \xB7 ");
215
+ rows.push(["runtime", runtime]);
123
216
  return [
124
217
  formatSuccessCard("Run submitted", rows),
125
218
  "",
126
- ...formatNextSteps([`Attach: \`rig run attach ${input.runId} --follow\``, `Inspect: \`rig run show --run ${input.runId}\``])
219
+ ...formatNextSteps([
220
+ `Attach: \`rig run attach ${input.runId} --follow\``,
221
+ `Inspect: \`rig run show ${input.runId}\``,
222
+ input.detached ? "Submitted detached; attach when you are ready." : "Interactive mode opens the native bundled Pi frontend."
223
+ ])
224
+ ].join(`
225
+ `);
226
+ }
227
+ function formatRunCard(run, options = {}) {
228
+ const raw = rawObject(run);
229
+ const merged = { ...raw, ...run };
230
+ const runId = runIdOf(merged);
231
+ const status = firstString(merged, ["status"], "unknown");
232
+ const taskId = taskIdOf(merged);
233
+ const title = runTitleOf(merged);
234
+ const runtime = firstString(merged, ["runtimeAdapter", "runtime", "adapter"]);
235
+ const mode = firstString(merged, ["runtimeMode", "mode"]);
236
+ const interaction = firstString(merged, ["interactionMode"]);
237
+ const created = compactDate(firstString(merged, ["createdAt"]));
238
+ const started = compactDate(firstString(merged, ["startedAt"]));
239
+ const updated = compactDate(firstString(merged, ["updatedAt"]));
240
+ const completed = compactDate(firstString(merged, ["completedAt", "finishedAt"]));
241
+ const worktree = firstString(merged, ["worktreePath", "cwd", "projectRoot"]);
242
+ const piSession = merged.piSession && typeof merged.piSession === "object" && !Array.isArray(merged.piSession) ? firstString(merged.piSession, ["sessionId", "id"]) : "";
243
+ const timeline = Array.isArray(merged.timeline) ? merged.timeline.length : null;
244
+ const approvals = Array.isArray(merged.approvals) ? merged.approvals.length : null;
245
+ const inputs = Array.isArray(merged.userInputs) ? merged.userInputs.length : null;
246
+ const rows = [
247
+ ["run", pc.bold(runId)],
248
+ ["status", formatStatusPill(status)],
249
+ ["task", taskId],
250
+ ["title", title],
251
+ ["runtime", [runtime, mode, interaction].filter(Boolean).join(" \xB7 ")],
252
+ ["created", created],
253
+ ["started", started],
254
+ ["updated", updated],
255
+ ["completed", completed],
256
+ ["worktree", worktree],
257
+ ["pi", piSession],
258
+ ["timeline", timeline],
259
+ ["approvals", approvals],
260
+ ["inputs", inputs]
261
+ ];
262
+ return [
263
+ formatSuccessCard(options.title ?? "Run details", rows),
264
+ "",
265
+ ...formatNextSteps([`Follow live: \`rig run attach ${runId} --follow\``, `Raw payload: \`rig run show ${runId} --raw\``])
127
266
  ].join(`
128
267
  `);
129
268
  }
269
+ function formatRunStatus(summary, options = {}) {
270
+ const activeRuns = summary.activeRuns ?? [];
271
+ const recentRuns = summary.recentRuns ?? [];
272
+ const lines = [formatSection("Run status", options.source === "server" ? "selected server" : "local state")];
273
+ lines.push("", pc.bold(`Active runs (${activeRuns.length})`));
274
+ if (activeRuns.length === 0) {
275
+ lines.push(pc.dim("No active runs."));
276
+ } else {
277
+ for (const run of activeRuns) {
278
+ lines.push(formatRunSummaryLine(run));
279
+ }
280
+ }
281
+ lines.push("", pc.bold(`Recent runs (${recentRuns.length})`));
282
+ if (recentRuns.length === 0) {
283
+ lines.push(pc.dim("No recent terminal runs."));
284
+ } else {
285
+ for (const run of recentRuns.slice(0, 10)) {
286
+ lines.push(formatRunSummaryLine(run));
287
+ }
288
+ }
289
+ lines.push("", ...formatNextSteps(["Start work: `rig task run --next`", "Attach: `rig run attach <run-id> --follow`", "Details: `rig run show <run-id>`"]));
290
+ return lines.join(`
291
+ `);
292
+ }
293
+ function formatRunSummaryLine(run) {
294
+ const record = run;
295
+ const runId = runIdOf(record);
296
+ const status = firstString(record, ["status"], "unknown");
297
+ const taskId = taskIdOf(record);
298
+ const title = runTitleOf(record);
299
+ const runtime = firstString(record, ["runtimeAdapter", "runtime", "adapter"]);
300
+ const descriptor = [taskId, title].filter(Boolean).join(" \xB7 ");
301
+ return `${pc.dim("\u2502")} ${pc.bold(runId)} ${formatStatusPill(status)} ${descriptor}${runtime ? pc.dim(` ${runtime}`) : ""}`;
302
+ }
303
+ function formatInboxList(kind, entries) {
304
+ const title = kind === "approvals" ? "Approval inbox" : "Input inbox";
305
+ if (entries.length === 0) {
306
+ return [
307
+ formatSection(title, "empty"),
308
+ pc.dim(kind === "approvals" ? "No pending approvals." : "No pending user-input requests."),
309
+ "",
310
+ ...formatNextSteps(["Check runs: `rig run status`", "Start work: `rig task run --next`"])
311
+ ].join(`
312
+ `);
313
+ }
314
+ const lines = [formatSection(title, `${entries.length} pending`)];
315
+ for (const entry of entries) {
316
+ const record = entry.record && typeof entry.record === "object" && !Array.isArray(entry.record) ? entry.record : entry;
317
+ const runId = firstString(entry, ["runId"], firstString(record, ["runId"]));
318
+ const taskId = firstString(entry, ["taskId"], firstString(record, ["taskId", "task"]));
319
+ const requestId = requestIdOf(record);
320
+ const status = firstString(record, ["status", "state"], "pending");
321
+ const prompt = firstString(record, ["prompt", "message", "reason", "title", "summary"], kind === "approvals" ? "Approval requested" : "Input requested");
322
+ lines.push(`${pc.dim("\u2502")} ${pc.bold(requestId)} ${formatStatusPill(status)} ${prompt}`);
323
+ lines.push(`${pc.dim("\u2502")} ${pc.dim("run ")} ${runId || "(unknown-run)"}${taskId ? pc.dim(` task ${taskId}`) : ""}`);
324
+ }
325
+ lines.push("", ...formatNextSteps(kind === "approvals" ? ["Resolve: `rig inbox approve --run <run-id> --request <request-id> --decision approve|reject`", "Rejoin: `rig run attach <run-id> --follow`"] : ["Respond: `rig inbox respond --run <run-id> --request <request-id> --answer key=value`", "Rejoin: `rig run attach <run-id> --follow`"]));
326
+ return lines.join(`
327
+ `);
328
+ }
130
329
  function formatConnectionList(connections) {
131
330
  const rows = [["local", { kind: "local", mode: "auto" }], ...Object.entries(connections)];
132
331
  const aliasWidth = Math.min(24, Math.max(5, ...rows.map(([alias]) => alias.length)));
@@ -152,13 +351,19 @@ function formatConnectionStatus(selected, connections) {
152
351
  `);
153
352
  }
154
353
  export {
354
+ printFormattedOutput,
155
355
  formatTaskList,
356
+ formatTaskDetails,
357
+ formatTaskCard,
156
358
  formatSuccessCard,
157
359
  formatSubmittedRun,
158
360
  formatStatusPill,
159
361
  formatSection,
362
+ formatRunStatus,
160
363
  formatRunList,
364
+ formatRunCard,
161
365
  formatNextSteps,
366
+ formatInboxList,
162
367
  formatConnectionStatus,
163
368
  formatConnectionList
164
369
  };
@@ -8,8 +8,6 @@ import { dirname, resolve } from "path";
8
8
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
9
9
  import { CliError } from "@rig/runtime/control-plane/errors";
10
10
  import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
11
- import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
12
- import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
13
11
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
14
12
  import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
15
13
 
@@ -107,7 +105,7 @@ function resolveSelectedConnection(projectRoot, options = {}) {
107
105
  const global = readGlobalConnections(options);
108
106
  const connection = global.connections[repo.selected];
109
107
  if (!connection) {
110
- throw new CliError2(`Selected Rig connection "${repo.selected}" was not found. Run \`rig connect list\` or \`rig connect use local\`.`, 1);
108
+ throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
111
109
  }
112
110
  return { alias: repo.selected, connection };
113
111
  }
@@ -9,8 +9,6 @@ import { resolve as resolve4 } from "path";
9
9
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
10
10
  import { CliError } from "@rig/runtime/control-plane/errors";
11
11
  import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
12
- import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
13
- import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
14
12
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
15
13
  import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
16
14
 
@@ -94,7 +92,7 @@ function resolveSelectedConnection(projectRoot, options = {}) {
94
92
  const global = readGlobalConnections(options);
95
93
  const connection = global.connections[repo.selected];
96
94
  if (!connection) {
97
- throw new CliError2(`Selected Rig connection "${repo.selected}" was not found. Run \`rig connect list\` or \`rig connect use local\`.`, 1);
95
+ throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
98
96
  }
99
97
  return { alias: repo.selected, connection };
100
98
  }
@@ -405,7 +403,7 @@ async function runRigDoctorChecks(options) {
405
403
  const taskSourceKind = config?.taskSource?.kind;
406
404
  checks.push(taskSourceKind ? check("task-source", "task source configured", "pass", taskSourceKind) : check("task-source", "task source configured", "fail", "missing taskSource", "Configure taskSource in rig.config.ts."));
407
405
  const repo = readRepoConnection(projectRoot);
408
- checks.push(repo ? check("project-link", "repo selected Rig connection", repo.project ? "pass" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : ""}`, "Run `rig init --yes --repo owner/repo` to link this checkout to a GitHub repo slug.") : check("project-link", "repo selected Rig connection", "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig connect use <alias|local>`."));
406
+ checks.push(repo ? check("project-link", "repo selected Rig server", repo.project ? "pass" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : ""}`, "Run `rig init --yes --repo owner/repo` to link this checkout to a GitHub repo slug.") : check("project-link", "repo selected Rig server", "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig server use <alias|local>`."));
409
407
  const selected = (() => {
410
408
  try {
411
409
  return resolveSelectedConnection(projectRoot);
@@ -413,7 +411,7 @@ async function runRigDoctorChecks(options) {
413
411
  return null;
414
412
  }
415
413
  })();
416
- checks.push(selected ? check("connection", "selected server connection", "pass", selected.connection.kind === "remote" ? selected.connection.baseUrl : "local auto") : check("connection", "selected server connection", repo ? "fail" : "warn", repo ? "selected alias is missing" : "will auto-start local server", repo ? "Run `rig connect list` and `rig connect use <alias|local>`." : undefined));
414
+ checks.push(selected ? check("connection", "selected server connection", "pass", selected.connection.kind === "remote" ? selected.connection.baseUrl : "local auto") : check("connection", "selected server", repo ? "fail" : "warn", repo ? "selected alias is missing" : "will auto-start local server", repo ? "Run `rig server list` and `rig server use <alias|local>`." : undefined));
417
415
  let server = null;
418
416
  try {
419
417
  server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);