@h-rig/cli 0.0.6-alpha.76 → 0.0.6-alpha.78

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 (32) hide show
  1. package/dist/bin/rig.js +10130 -9246
  2. package/dist/src/app/board.js +1783 -0
  3. package/dist/src/app/drone-ui.js +294 -0
  4. package/dist/src/{commands/_tui-theme.js → app/theme.js} +16 -1
  5. package/dist/src/commands/_async-ui.js +74 -3
  6. package/dist/src/commands/_cli-format.js +107 -29
  7. package/dist/src/commands/_doctor-checks.js +3 -1
  8. package/dist/src/commands/_help-catalog.js +8 -70
  9. package/dist/src/commands/_operator-view.js +184 -7
  10. package/dist/src/commands/_pi-frontend.js +184 -7
  11. package/dist/src/commands/_preflight.js +3 -1
  12. package/dist/src/commands/_server-client.js +3 -1
  13. package/dist/src/commands/_snapshot-upload.js +3 -1
  14. package/dist/src/commands/_task-picker.js +132 -7
  15. package/dist/src/commands/browser.js +306 -30
  16. package/dist/src/commands/connect.js +146 -20
  17. package/dist/src/commands/doctor.js +77 -4
  18. package/dist/src/commands/github.js +77 -4
  19. package/dist/src/commands/inbox.js +171 -88
  20. package/dist/src/commands/init.js +349 -9
  21. package/dist/src/commands/inspect.js +77 -4
  22. package/dist/src/commands/run.js +214 -28
  23. package/dist/src/commands/server.js +149 -21
  24. package/dist/src/commands/setup.js +77 -4
  25. package/dist/src/commands/stats.js +109 -107
  26. package/dist/src/commands/task-report-bug.js +231 -40
  27. package/dist/src/commands/task-run-driver.js +3 -1
  28. package/dist/src/commands/task.js +355 -184
  29. package/dist/src/commands.js +10126 -9242
  30. package/dist/src/index.js +10131 -9247
  31. package/package.json +8 -9
  32. package/dist/src/commands/_operator-board.js +0 -730
@@ -41,24 +41,171 @@ Usage: ${usage}`);
41
41
  }
42
42
  }
43
43
 
44
+ // packages/cli/src/app/drone-ui.ts
45
+ import { ProcessTerminal, TUI, Text, Input, SelectList, matchesKey } from "@earendil-works/pi-tui";
46
+
47
+ // packages/cli/src/app/theme.ts
48
+ var RIG_PALETTE = {
49
+ ink: "#f2f3f6",
50
+ ink2: "#aeb0ba",
51
+ ink3: "#6c6e79",
52
+ ink4: "#44464f",
53
+ accent: "#ccff4d",
54
+ accentDim: "#a9d63f",
55
+ cyan: "#56d8ff",
56
+ red: "#ff5d5d",
57
+ yellow: "#ffd24d"
58
+ };
59
+ function hexToRgb(hex) {
60
+ const value = hex.replace("#", "");
61
+ return [
62
+ Number.parseInt(value.slice(0, 2), 16),
63
+ Number.parseInt(value.slice(2, 4), 16),
64
+ Number.parseInt(value.slice(4, 6), 16)
65
+ ];
66
+ }
67
+ function fg(hex) {
68
+ const [r, g, b] = hexToRgb(hex);
69
+ return (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
70
+ }
71
+ var ink = fg(RIG_PALETTE.ink);
72
+ var ink2 = fg(RIG_PALETTE.ink2);
73
+ var ink3 = fg(RIG_PALETTE.ink3);
74
+ var ink4 = fg(RIG_PALETTE.ink4);
75
+ var accent = fg(RIG_PALETTE.accent);
76
+ var accentDim = fg(RIG_PALETTE.accentDim);
77
+ var cyan = fg(RIG_PALETTE.cyan);
78
+ var red = fg(RIG_PALETTE.red);
79
+ var yellow = fg(RIG_PALETTE.yellow);
80
+ function bold(text) {
81
+ return `\x1B[1m${text}\x1B[22m`;
82
+ }
83
+ var DRONE_ART = [
84
+ " .-=-. .-=-. ",
85
+ " ( !!! ) ( !!! ) ",
86
+ " '-=-'._ _.'-=-' ",
87
+ " '._ _.' ",
88
+ " '=$$$$$$$=.' ",
89
+ " =$$$$$$$$$$$= ",
90
+ " $$$@@@@@@@@@@$$$ ",
91
+ " $$$@@ @@$$$ ",
92
+ " $$@ ? @$$$ ",
93
+ " $$$@ '-' @$$$ ",
94
+ " $$$@@ @@$$$ ",
95
+ " $$$@@@@@@@@@@$$$ ",
96
+ " =$$$$$$$$$$$= ",
97
+ " '=$$$$$$$=.' ",
98
+ " _.' '._ ",
99
+ " .-=-.' '.-=-. ",
100
+ " ( !!! ) ( !!! ) ",
101
+ " '-=-' '-=-' "
102
+ ];
103
+ var EYE_FRAMES = ["@", "o", "."];
104
+ var DRONE_WIDTH = DRONE_ART[0].length;
105
+ var DRONE_HEIGHT = DRONE_ART.length;
106
+ var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
107
+ function microDroneFrame(tick) {
108
+ const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
109
+ const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
110
+ return `(${blade})${eye}(${blade})`;
111
+ }
112
+ var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
113
+ function renderMicroDroneFrame(tick) {
114
+ const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
115
+ const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
116
+ return `${ink4("(")}${cyan(blade)}${ink4(")")}${bold(accent(eye))}${ink4("(")}${cyan(blade)}${ink4(")")}`;
117
+ }
118
+
119
+ // packages/cli/src/commands/_spinner.ts
120
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
121
+ function createTtySpinner(input) {
122
+ const output = input.output ?? process.stdout;
123
+ const isTty = output.isTTY === true;
124
+ const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
125
+ let label = input.label;
126
+ let frame = 0;
127
+ let paused = false;
128
+ let stopped = false;
129
+ let lastPrintedLabel = "";
130
+ const render = () => {
131
+ if (stopped || paused)
132
+ return;
133
+ if (!isTty) {
134
+ if (label !== lastPrintedLabel) {
135
+ output.write(`${label}
136
+ `);
137
+ lastPrintedLabel = label;
138
+ }
139
+ return;
140
+ }
141
+ frame = (frame + 1) % frames.length;
142
+ const glyph = frames[frame] ?? frames[0] ?? "";
143
+ output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
144
+ };
145
+ const clearLine = () => {
146
+ if (isTty)
147
+ output.write("\r\x1B[2K");
148
+ };
149
+ render();
150
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
151
+ return {
152
+ setLabel(next) {
153
+ label = next;
154
+ render();
155
+ },
156
+ pause() {
157
+ paused = true;
158
+ clearLine();
159
+ },
160
+ resume() {
161
+ if (stopped)
162
+ return;
163
+ paused = false;
164
+ render();
165
+ },
166
+ stop(finalLine) {
167
+ if (stopped)
168
+ return;
169
+ stopped = true;
170
+ if (timer)
171
+ clearInterval(timer);
172
+ clearLine();
173
+ if (finalLine)
174
+ output.write(`${finalLine}
175
+ `);
176
+ }
177
+ };
178
+ }
179
+
180
+ // packages/cli/src/app/drone-ui.ts
181
+ function droneNote(message, title) {
182
+ if (title)
183
+ console.log(` ${accentDim("\u25C7")} ${bold(ink2(title))}`);
184
+ for (const line of message.split(`
185
+ `)) {
186
+ console.log(` ${ink4("\u2502")} ${line}`);
187
+ }
188
+ }
189
+
44
190
  // packages/cli/src/commands/_cli-format.ts
45
- import { log, note } from "@clack/prompts";
46
191
  import pc from "picocolors";
192
+ var themeDim = (value) => ink3(value);
193
+ var themeFaint = (value) => ink4(value);
47
194
  function stringField(record, key, fallback = "") {
48
195
  const value = record[key];
49
196
  return typeof value === "string" && value.trim() ? value.trim() : fallback;
50
197
  }
51
198
  function statusColor(status) {
52
199
  const normalized = status.toLowerCase();
53
- if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved"].includes(normalized))
54
- return pc.green;
200
+ if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved", "running"].includes(normalized))
201
+ return accent;
55
202
  if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
56
- return pc.red;
57
- if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
58
- return pc.cyan;
59
- if (["ready", "open", "queued", "created", "preparing", "local", "pending"].includes(normalized))
60
- return pc.yellow;
61
- return pc.dim;
203
+ return red;
204
+ if (["reviewing", "validating", "in_progress", "in-progress", "remote", "preparing", "closing-out"].includes(normalized))
205
+ return cyan;
206
+ if (["ready", "open", "queued", "created", "local", "pending"].includes(normalized))
207
+ return yellow;
208
+ return themeDim;
62
209
  }
63
210
  function firstString(record, keys, fallback = "") {
64
211
  for (const key of keys) {
@@ -79,29 +226,26 @@ function printFormattedOutput(message, options = {}) {
79
226
  console.log(message);
80
227
  return;
81
228
  }
82
- if (options.title)
83
- note(message, options.title);
84
- else
85
- log.message(message);
229
+ droneNote(message, options.title);
86
230
  }
87
231
  function formatStatusPill(status) {
88
232
  const label = status || "unknown";
89
233
  return statusColor(label)(`\u25CF ${label}`);
90
234
  }
91
235
  function formatSection(title, subtitle) {
92
- return `${pc.bold(pc.cyan("\u25C6"))} ${pc.bold(title)}${subtitle ? pc.dim(` \u2014 ${subtitle}`) : ""}`;
236
+ return `${pc.bold(accent("\u25C6"))} ${pc.bold(title)}${subtitle ? themeDim(` \u2014 ${subtitle}`) : ""}`;
93
237
  }
94
238
  function formatNextSteps(steps) {
95
239
  if (steps.length === 0)
96
240
  return [];
97
- return [pc.bold("Next"), ...steps.map((step) => `${pc.dim("\u203A")} ${step}`)];
241
+ return [pc.bold("Next"), ...steps.map((step) => `${accent("\u203A")} ${step}`)];
98
242
  }
99
243
  function formatInboxList(kind, entries) {
100
244
  const title = kind === "approvals" ? "Approval inbox" : "Input inbox";
101
245
  if (entries.length === 0) {
102
246
  return [
103
247
  formatSection(title, "empty"),
104
- pc.dim(kind === "approvals" ? "No pending approvals." : "No pending user-input requests."),
248
+ themeDim(kind === "approvals" ? "No pending approvals." : "No pending user-input requests."),
105
249
  "",
106
250
  ...formatNextSteps(["Check runs: `rig run status`", "Start work: `rig task run --next`"])
107
251
  ].join(`
@@ -115,8 +259,8 @@ function formatInboxList(kind, entries) {
115
259
  const requestId = requestIdOf(record);
116
260
  const status = firstString(record, ["status", "state"], "pending");
117
261
  const prompt = firstString(record, ["prompt", "message", "reason", "title", "summary"], kind === "approvals" ? "Approval requested" : "Input requested");
118
- lines.push(`${pc.dim("\u2502")} ${pc.bold(requestId)} ${formatStatusPill(status)} ${prompt}`);
119
- lines.push(`${pc.dim("\u2502")} ${pc.dim("run ")} ${runId || "(unknown-run)"}${taskId ? pc.dim(` task ${taskId}`) : ""}`);
262
+ lines.push(`${themeFaint("\u2502")} ${pc.bold(requestId)} ${formatStatusPill(status)} ${prompt}`);
263
+ lines.push(`${themeFaint("\u2502")} ${themeDim("run ")} ${runId || "(unknown-run)"}${taskId ? themeDim(` task ${taskId}`) : ""}`);
120
264
  }
121
265
  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`"]));
122
266
  return lines.join(`
@@ -417,7 +561,9 @@ ${failure.contextLine}`, 1, { hint: failure.hint });
417
561
  })() : null;
418
562
  if (!response.ok) {
419
563
  const diagnostics = diagnosticMessage(payload);
420
- const detail = diagnostics ?? (text || response.statusText);
564
+ const rawDetail = diagnostics ?? (text || response.statusText);
565
+ const detail = diagnostics ? rawDetail : rawDetail.split(`
566
+ `).map((line) => line.trim()).find((line) => line.length > 0)?.slice(0, 200) ?? response.statusText;
421
567
  const failure = await buildServerFailureContext(context.projectRoot, server);
422
568
  throw new CliError(`Rig server request failed (${response.status}): ${detail}
423
569
  ${failure.contextLine}`, 1, { hint: failure.hint });
@@ -438,70 +584,6 @@ var RESUMABLE_RUN_STATUSES = new Set([
438
584
 
439
585
  // packages/cli/src/commands/_async-ui.ts
440
586
  import pc2 from "picocolors";
441
-
442
- // packages/cli/src/commands/_spinner.ts
443
- var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
444
- function createTtySpinner(input) {
445
- const output = input.output ?? process.stdout;
446
- const isTty = output.isTTY === true;
447
- const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
448
- let label = input.label;
449
- let frame = 0;
450
- let paused = false;
451
- let stopped = false;
452
- let lastPrintedLabel = "";
453
- const render = () => {
454
- if (stopped || paused)
455
- return;
456
- if (!isTty) {
457
- if (label !== lastPrintedLabel) {
458
- output.write(`${label}
459
- `);
460
- lastPrintedLabel = label;
461
- }
462
- return;
463
- }
464
- frame = (frame + 1) % frames.length;
465
- const glyph = frames[frame] ?? frames[0] ?? "";
466
- output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
467
- };
468
- const clearLine = () => {
469
- if (isTty)
470
- output.write("\r\x1B[2K");
471
- };
472
- render();
473
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
474
- return {
475
- setLabel(next) {
476
- label = next;
477
- render();
478
- },
479
- pause() {
480
- paused = true;
481
- clearLine();
482
- },
483
- resume() {
484
- if (stopped)
485
- return;
486
- paused = false;
487
- render();
488
- },
489
- stop(finalLine) {
490
- if (stopped)
491
- return;
492
- stopped = true;
493
- if (timer)
494
- clearInterval(timer);
495
- clearLine();
496
- if (finalLine)
497
- output.write(`${finalLine}
498
- `);
499
- }
500
- };
501
- }
502
-
503
- // packages/cli/src/commands/_async-ui.ts
504
- var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
505
587
  var DONE_SYMBOL = pc2.green("\u25C7");
506
588
  var FAIL_SYMBOL = pc2.red("\u25A0");
507
589
  var activeUpdate = null;
@@ -535,8 +617,8 @@ async function withSpinner(label, work, options = {}) {
535
617
  const spinner = createTtySpinner({
536
618
  label,
537
619
  output,
538
- frames: CLACK_SPINNER_FRAMES,
539
- styleFrame: (frame) => pc2.magenta(frame)
620
+ frames: MICRO_DRONE_FRAMES,
621
+ styleFrame: (frame) => renderMicroDroneFrame(Math.max(0, MICRO_DRONE_FRAMES.indexOf(frame)))
540
622
  });
541
623
  const update = (next) => {
542
624
  lastLabel = next;
@@ -684,8 +766,8 @@ async function executeInbox(context, args) {
684
766
  pending = request.rest;
685
767
  const decision = takeOption(pending, "--decision");
686
768
  pending = decision.rest;
687
- const note2 = takeOption(pending, "--note");
688
- pending = note2.rest;
769
+ const note = takeOption(pending, "--note");
770
+ pending = note.rest;
689
771
  requireNoExtraArgs(pending, "rig inbox approve --run <id> --request <id> --decision approve|reject [--note <text>]");
690
772
  if (!run.value || !request.value || !decision.value) {
691
773
  throw new CliError("approve requires --run, --request, and --decision. List pending requests with `rig inbox approvals`.");
@@ -700,7 +782,7 @@ async function executeInbox(context, args) {
700
782
  runId: run.value,
701
783
  requestId: request.value,
702
784
  decision: decision.value,
703
- note: note2.value ?? null
785
+ note: note.value ?? null
704
786
  })
705
787
  }), { outputMode: context.outputMode });
706
788
  return { ok: true, group: "inbox", command, details: { result } };
@@ -763,5 +845,6 @@ async function executeInbox(context, args) {
763
845
  export {
764
846
  readPendingInboxCounts,
765
847
  printPendingInboxFooter,
848
+ listInboxRecords,
766
849
  executeInbox
767
850
  };