@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
@@ -382,7 +382,9 @@ ${failure.contextLine}`, 1, { hint: failure.hint });
382
382
  })() : null;
383
383
  if (!response.ok) {
384
384
  const diagnostics = diagnosticMessage(payload);
385
- const detail = diagnostics ?? (text || response.statusText);
385
+ const rawDetail = diagnostics ?? (text || response.statusText);
386
+ const detail = diagnostics ? rawDetail : rawDetail.split(`
387
+ `).map((line) => line.trim()).find((line) => line.length > 0)?.slice(0, 200) ?? response.statusText;
386
388
  const failure = await buildServerFailureContext(context.projectRoot, server);
387
389
  throw new CliError(`Rig server request failed (${response.status}): ${detail}
388
390
  ${failure.contextLine}`, 1, { hint: failure.hint });
@@ -807,8 +809,8 @@ function createOperatorSurface(options = {}) {
807
809
  }
808
810
 
809
811
  // packages/cli/src/commands/_pi-frontend.ts
810
- import { mkdtempSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
811
- import { tmpdir } from "os";
812
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, mkdtempSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
813
+ import { homedir as homedir2, tmpdir } from "os";
812
814
  import { join as join2 } from "path";
813
815
  import { main as runPiMain } from "@earendil-works/pi-coding-agent";
814
816
  import createPiRigExtension from "@rig/pi-rig";
@@ -877,8 +879,79 @@ function createTtySpinner(input) {
877
879
  };
878
880
  }
879
881
 
882
+ // packages/cli/src/app/theme.ts
883
+ var RIG_PALETTE = {
884
+ ink: "#f2f3f6",
885
+ ink2: "#aeb0ba",
886
+ ink3: "#6c6e79",
887
+ ink4: "#44464f",
888
+ accent: "#ccff4d",
889
+ accentDim: "#a9d63f",
890
+ cyan: "#56d8ff",
891
+ red: "#ff5d5d",
892
+ yellow: "#ffd24d"
893
+ };
894
+ function hexToRgb(hex) {
895
+ const value = hex.replace("#", "");
896
+ return [
897
+ Number.parseInt(value.slice(0, 2), 16),
898
+ Number.parseInt(value.slice(2, 4), 16),
899
+ Number.parseInt(value.slice(4, 6), 16)
900
+ ];
901
+ }
902
+ function fg(hex) {
903
+ const [r, g, b] = hexToRgb(hex);
904
+ return (text2) => `\x1B[38;2;${r};${g};${b}m${text2}\x1B[39m`;
905
+ }
906
+ var ink = fg(RIG_PALETTE.ink);
907
+ var ink2 = fg(RIG_PALETTE.ink2);
908
+ var ink3 = fg(RIG_PALETTE.ink3);
909
+ var ink4 = fg(RIG_PALETTE.ink4);
910
+ var accent = fg(RIG_PALETTE.accent);
911
+ var accentDim = fg(RIG_PALETTE.accentDim);
912
+ var cyan = fg(RIG_PALETTE.cyan);
913
+ var red = fg(RIG_PALETTE.red);
914
+ var yellow = fg(RIG_PALETTE.yellow);
915
+ function bold(text2) {
916
+ return `\x1B[1m${text2}\x1B[22m`;
917
+ }
918
+ var DRONE_ART = [
919
+ " .-=-. .-=-. ",
920
+ " ( !!! ) ( !!! ) ",
921
+ " '-=-'._ _.'-=-' ",
922
+ " '._ _.' ",
923
+ " '=$$$$$$$=.' ",
924
+ " =$$$$$$$$$$$= ",
925
+ " $$$@@@@@@@@@@$$$ ",
926
+ " $$$@@ @@$$$ ",
927
+ " $$@ ? @$$$ ",
928
+ " $$$@ '-' @$$$ ",
929
+ " $$$@@ @@$$$ ",
930
+ " $$$@@@@@@@@@@$$$ ",
931
+ " =$$$$$$$$$$$= ",
932
+ " '=$$$$$$$=.' ",
933
+ " _.' '._ ",
934
+ " .-=-.' '.-=-. ",
935
+ " ( !!! ) ( !!! ) ",
936
+ " '-=-' '-=-' "
937
+ ];
938
+ var EYE_FRAMES = ["@", "o", "."];
939
+ var DRONE_WIDTH = DRONE_ART[0].length;
940
+ var DRONE_HEIGHT = DRONE_ART.length;
941
+ var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
942
+ function microDroneFrame(tick) {
943
+ const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
944
+ const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
945
+ return `(${blade})${eye}(${blade})`;
946
+ }
947
+ var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
948
+ function renderMicroDroneFrame(tick) {
949
+ const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
950
+ const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
951
+ return `${ink4("(")}${cyan(blade)}${ink4(")")}${bold(accent(eye))}${ink4("(")}${cyan(blade)}${ink4(")")}`;
952
+ }
953
+
880
954
  // packages/cli/src/commands/_async-ui.ts
881
- var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
882
955
  var DONE_SYMBOL = pc.green("\u25C7");
883
956
  var FAIL_SYMBOL = pc.red("\u25A0");
884
957
  var activeUpdate = null;
@@ -912,8 +985,8 @@ async function withSpinner(label, work, options = {}) {
912
985
  const spinner = createTtySpinner({
913
986
  label,
914
987
  output,
915
- frames: CLACK_SPINNER_FRAMES,
916
- styleFrame: (frame) => pc.magenta(frame)
988
+ frames: MICRO_DRONE_FRAMES,
989
+ styleFrame: (frame) => renderMicroDroneFrame(Math.max(0, MICRO_DRONE_FRAMES.indexOf(frame)))
917
990
  });
918
991
  const update = (next) => {
919
992
  lastLabel = next;
@@ -954,6 +1027,7 @@ function buildOperatorPiEnv(input) {
954
1027
  return {
955
1028
  PI_CODING_AGENT_SESSION_DIR: input.sessionDir,
956
1029
  PI_SKIP_VERSION_CHECK: "1",
1030
+ PI_HIDDEN_COMMANDS: "import,fork,clone,tree,new,resume,trust",
957
1031
  RIG_PI_OPERATOR_SESSION: "1",
958
1032
  RIG_RUN_ID: input.runId,
959
1033
  RIG_SERVER_URL: input.serverUrl,
@@ -963,6 +1037,10 @@ function buildOperatorPiEnv(input) {
963
1037
  }
964
1038
  async function prepareOperatorConsole(context, runId, tempSessionDir) {
965
1039
  const server = await ensureServerForCli(context.projectRoot);
1040
+ const localCwd = join2(homedir2(), ".rig", "drones", runId.slice(0, 8));
1041
+ mkdirSync2(localCwd, { recursive: true });
1042
+ trustDroneCwd(localCwd);
1043
+ installRigPiTheme();
966
1044
  let sessionFileArg = [];
967
1045
  try {
968
1046
  const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/session-file`);
@@ -976,7 +1054,7 @@ async function prepareOperatorConsole(context, runId, tempSessionDir) {
976
1054
  try {
977
1055
  const header = JSON.parse(line);
978
1056
  if (header.type === "session" && typeof header.cwd === "string") {
979
- return JSON.stringify({ ...header, cwd: process.cwd() });
1057
+ return JSON.stringify({ ...header, cwd: localCwd });
980
1058
  }
981
1059
  } catch {}
982
1060
  return line;
@@ -988,6 +1066,105 @@ async function prepareOperatorConsole(context, runId, tempSessionDir) {
988
1066
  } catch {}
989
1067
  return { server, sessionFileArg };
990
1068
  }
1069
+ var RIG_PI_THEME = {
1070
+ $schema: "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
1071
+ name: "rig",
1072
+ vars: {
1073
+ acid: "#ccff4d",
1074
+ acidDim: "#a9d63f",
1075
+ cyan: "#56d8ff",
1076
+ red: "#ff5d5d",
1077
+ yellow: "#ffd24d",
1078
+ ink: "#f2f3f6",
1079
+ ink2: "#aeb0ba",
1080
+ ink3: "#6c6e79",
1081
+ ink4: "#44464f",
1082
+ panel: "#101115",
1083
+ panelUser: "#14161b",
1084
+ toolPending: "#0e1013",
1085
+ toolSuccess: "#10150c",
1086
+ toolError: "#1a0f0f",
1087
+ customMsg: "#0f1410"
1088
+ },
1089
+ colors: {
1090
+ accent: "acid",
1091
+ border: "ink4",
1092
+ borderAccent: "acid",
1093
+ borderMuted: "ink4",
1094
+ success: "acid",
1095
+ error: "red",
1096
+ warning: "yellow",
1097
+ muted: "ink3",
1098
+ dim: "ink4",
1099
+ text: "ink",
1100
+ thinkingText: "ink3",
1101
+ selectedBg: "panel",
1102
+ userMessageBg: "panelUser",
1103
+ userMessageText: "ink",
1104
+ customMessageBg: "customMsg",
1105
+ customMessageText: "ink2",
1106
+ customMessageLabel: "acidDim",
1107
+ toolPendingBg: "toolPending",
1108
+ toolSuccessBg: "toolSuccess",
1109
+ toolErrorBg: "toolError",
1110
+ toolTitle: "ink",
1111
+ toolOutput: "ink3",
1112
+ mdHeading: "acid",
1113
+ mdLink: "cyan",
1114
+ mdLinkUrl: "ink4",
1115
+ mdCode: "acidDim",
1116
+ mdCodeBlock: "ink2",
1117
+ mdCodeBlockBorder: "ink4",
1118
+ mdQuote: "ink3",
1119
+ mdQuoteBorder: "ink4",
1120
+ mdHr: "ink4",
1121
+ mdListBullet: "acid",
1122
+ toolDiffAdded: "acid",
1123
+ toolDiffRemoved: "red",
1124
+ toolDiffContext: "ink3",
1125
+ syntaxComment: "ink3",
1126
+ syntaxKeyword: "cyan",
1127
+ syntaxFunction: "acid",
1128
+ syntaxVariable: "ink",
1129
+ syntaxString: "acidDim",
1130
+ syntaxNumber: "yellow",
1131
+ syntaxType: "cyan",
1132
+ syntaxOperator: "ink2",
1133
+ syntaxPunctuation: "ink3",
1134
+ thinkingOff: "ink4",
1135
+ thinkingMinimal: "ink3",
1136
+ thinkingLow: "ink2",
1137
+ thinkingMedium: "cyan",
1138
+ thinkingHigh: "acidDim",
1139
+ thinkingXhigh: "acid",
1140
+ bashMode: "cyan"
1141
+ }
1142
+ };
1143
+ function trustDroneCwd(localCwd) {
1144
+ try {
1145
+ const agentDir = join2(homedir2(), ".pi", "agent");
1146
+ mkdirSync2(agentDir, { recursive: true });
1147
+ const trustPath = join2(agentDir, "trust.json");
1148
+ const store = existsSync4(trustPath) ? JSON.parse(readFileSync3(trustPath, "utf8")) : {};
1149
+ if (store[localCwd] !== true) {
1150
+ store[localCwd] = true;
1151
+ writeFileSync2(trustPath, `${JSON.stringify(store, null, "\t")}
1152
+ `);
1153
+ }
1154
+ } catch {}
1155
+ }
1156
+ function installRigPiTheme() {
1157
+ try {
1158
+ const themesDir = join2(homedir2(), ".pi", "agent", "themes");
1159
+ mkdirSync2(themesDir, { recursive: true });
1160
+ const themePath = join2(themesDir, "rig.json");
1161
+ const next = `${JSON.stringify(RIG_PI_THEME, null, "\t")}
1162
+ `;
1163
+ if (!existsSync4(themePath) || readFileSync3(themePath, "utf8") !== next) {
1164
+ writeFileSync2(themePath, next);
1165
+ }
1166
+ } catch {}
1167
+ }
991
1168
  async function attachRunBundledPiFrontend(context, input) {
992
1169
  const tempSessionDir = mkdtempSync(join2(tmpdir(), "rig-pi-frontend-sessions-"));
993
1170
  const { server, sessionFileArg } = await withSpinner(`Opening Pi console for run ${input.runId}\u2026`, () => prepareOperatorConsole(context, input.runId, tempSessionDir), { outputMode: context.outputMode });
@@ -1124,9 +1301,21 @@ async function attachRunOperatorView(context, input) {
1124
1301
  return { ...snapshot, steered, detached };
1125
1302
  }
1126
1303
 
1304
+ // packages/cli/src/app/drone-ui.ts
1305
+ import { ProcessTerminal, TUI, Text, Input, SelectList, matchesKey } from "@earendil-works/pi-tui";
1306
+ function droneNote(message, title) {
1307
+ if (title)
1308
+ console.log(` ${accentDim("\u25C7")} ${bold(ink2(title))}`);
1309
+ for (const line of message.split(`
1310
+ `)) {
1311
+ console.log(` ${ink4("\u2502")} ${line}`);
1312
+ }
1313
+ }
1314
+
1127
1315
  // packages/cli/src/commands/_cli-format.ts
1128
- import { log, note } from "@clack/prompts";
1129
1316
  import pc2 from "picocolors";
1317
+ var themeDim = (value) => ink3(value);
1318
+ var themeFaint = (value) => ink4(value);
1130
1319
  function stringField(record, key, fallback = "") {
1131
1320
  const value = record[key];
1132
1321
  return typeof value === "string" && value.trim() ? value.trim() : fallback;
@@ -1147,15 +1336,15 @@ function pad(value, width) {
1147
1336
  }
1148
1337
  function statusColor(status) {
1149
1338
  const normalized = status.toLowerCase();
1150
- if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved"].includes(normalized))
1151
- return pc2.green;
1339
+ if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved", "running"].includes(normalized))
1340
+ return accent;
1152
1341
  if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
1153
- return pc2.red;
1154
- if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
1155
- return pc2.cyan;
1156
- if (["ready", "open", "queued", "created", "preparing", "local", "pending"].includes(normalized))
1157
- return pc2.yellow;
1158
- return pc2.dim;
1342
+ return red;
1343
+ if (["reviewing", "validating", "in_progress", "in-progress", "remote", "preparing", "closing-out"].includes(normalized))
1344
+ return cyan;
1345
+ if (["ready", "open", "queued", "created", "local", "pending"].includes(normalized))
1346
+ return yellow;
1347
+ return themeDim;
1159
1348
  }
1160
1349
  function compactDate(value) {
1161
1350
  if (!value.trim())
@@ -1190,33 +1379,30 @@ function printFormattedOutput(message, options = {}) {
1190
1379
  console.log(message);
1191
1380
  return;
1192
1381
  }
1193
- if (options.title)
1194
- note(message, options.title);
1195
- else
1196
- log.message(message);
1382
+ droneNote(message, options.title);
1197
1383
  }
1198
1384
  function formatStatusPill(status) {
1199
1385
  const label = status || "unknown";
1200
1386
  return statusColor(label)(`\u25CF ${label}`);
1201
1387
  }
1202
1388
  function formatSection(title, subtitle) {
1203
- return `${pc2.bold(pc2.cyan("\u25C6"))} ${pc2.bold(title)}${subtitle ? pc2.dim(` \u2014 ${subtitle}`) : ""}`;
1389
+ return `${pc2.bold(accent("\u25C6"))} ${pc2.bold(title)}${subtitle ? themeDim(` \u2014 ${subtitle}`) : ""}`;
1204
1390
  }
1205
1391
  function formatSuccessCard(title, rows = []) {
1206
- const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${pc2.dim("\u2502")} ${pc2.dim(key.padEnd(12))} ${value}`);
1392
+ const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${themeFaint("\u2502")} ${themeDim(key.padEnd(12))} ${value}`);
1207
1393
  return [formatSection(title), ...body].join(`
1208
1394
  `);
1209
1395
  }
1210
1396
  function formatNextSteps(steps) {
1211
1397
  if (steps.length === 0)
1212
1398
  return [];
1213
- return [pc2.bold("Next"), ...steps.map((step) => `${pc2.dim("\u203A")} ${step}`)];
1399
+ return [pc2.bold("Next"), ...steps.map((step) => `${accent("\u203A")} ${step}`)];
1214
1400
  }
1215
1401
  function formatRunList(runs, options = {}) {
1216
1402
  if (runs.length === 0) {
1217
1403
  return [
1218
1404
  formatSection("Runs", "none recorded"),
1219
- options.source === "server" ? pc2.dim("No runs recorded on the selected Rig server.") : pc2.dim("No runs recorded in .rig/runs."),
1405
+ options.source === "server" ? themeDim("No runs recorded on the selected Rig server.") : themeDim("No runs recorded in .rig/runs."),
1220
1406
  "",
1221
1407
  ...formatNextSteps(["Start one: `rig task run --next`", "Check server: `rig server status`"])
1222
1408
  ].join(`
@@ -1236,7 +1422,7 @@ function formatRunList(runs, options = {}) {
1236
1422
  const body = rows.map((row) => [
1237
1423
  pc2.bold(pad(truncate(row.runId, idWidth), idWidth)),
1238
1424
  statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
1239
- `${row.title}${row.runtime ? pc2.dim(` ${row.runtime}`) : ""}`
1425
+ `${row.title}${row.runtime ? themeDim(` ${row.runtime}`) : ""}`
1240
1426
  ].join(" "));
1241
1427
  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(`
1242
1428
  `);
@@ -1289,7 +1475,7 @@ function formatRunStatus(summary, options = {}) {
1289
1475
  const lines = [formatSection("Run status", options.source === "server" ? "selected server" : "local state")];
1290
1476
  lines.push("", pc2.bold(`Active runs (${activeRuns.length})`));
1291
1477
  if (activeRuns.length === 0) {
1292
- lines.push(pc2.dim("No active runs."));
1478
+ lines.push(themeDim("No active runs."));
1293
1479
  } else {
1294
1480
  for (const run of activeRuns) {
1295
1481
  lines.push(formatRunSummaryLine(run));
@@ -1297,7 +1483,7 @@ function formatRunStatus(summary, options = {}) {
1297
1483
  }
1298
1484
  lines.push("", pc2.bold(`Recent runs (${recentRuns.length})`));
1299
1485
  if (recentRuns.length === 0) {
1300
- lines.push(pc2.dim("No recent terminal runs."));
1486
+ lines.push(themeDim("No recent terminal runs."));
1301
1487
  } else {
1302
1488
  for (const run of recentRuns.slice(0, 10)) {
1303
1489
  lines.push(formatRunSummaryLine(run));
@@ -1315,7 +1501,7 @@ function formatRunSummaryLine(run) {
1315
1501
  const title = runTitleOf(record);
1316
1502
  const runtime = firstString(record, ["runtimeAdapter", "runtime", "adapter"]);
1317
1503
  const descriptor = [taskId, title].filter(Boolean).join(" \xB7 ");
1318
- return `${pc2.dim("\u2502")} ${pc2.bold(runId)} ${formatStatusPill(status)} ${descriptor}${runtime ? pc2.dim(` ${runtime}`) : ""}`;
1504
+ return `${themeFaint("\u2502")} ${pc2.bold(runId)} ${formatStatusPill(status)} ${descriptor}${runtime ? themeDim(` ${runtime}`) : ""}`;
1319
1505
  }
1320
1506
 
1321
1507
  // packages/cli/src/commands/inbox.ts
@@ -69,8 +69,133 @@ function normalizeRuntimeAdapter(value) {
69
69
  return "claude-code";
70
70
  }
71
71
 
72
- // packages/cli/src/commands/connect.ts
73
- import { cancel, isCancel, select } from "@clack/prompts";
72
+ // packages/cli/src/app/drone-ui.ts
73
+ import { ProcessTerminal, TUI, Text, Input, SelectList, matchesKey } from "@earendil-works/pi-tui";
74
+
75
+ // packages/cli/src/app/theme.ts
76
+ var RIG_PALETTE = {
77
+ ink: "#f2f3f6",
78
+ ink2: "#aeb0ba",
79
+ ink3: "#6c6e79",
80
+ ink4: "#44464f",
81
+ accent: "#ccff4d",
82
+ accentDim: "#a9d63f",
83
+ cyan: "#56d8ff",
84
+ red: "#ff5d5d",
85
+ yellow: "#ffd24d"
86
+ };
87
+ function hexToRgb(hex) {
88
+ const value = hex.replace("#", "");
89
+ return [
90
+ Number.parseInt(value.slice(0, 2), 16),
91
+ Number.parseInt(value.slice(2, 4), 16),
92
+ Number.parseInt(value.slice(4, 6), 16)
93
+ ];
94
+ }
95
+ function fg(hex) {
96
+ const [r, g, b] = hexToRgb(hex);
97
+ return (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
98
+ }
99
+ var ink = fg(RIG_PALETTE.ink);
100
+ var ink2 = fg(RIG_PALETTE.ink2);
101
+ var ink3 = fg(RIG_PALETTE.ink3);
102
+ var ink4 = fg(RIG_PALETTE.ink4);
103
+ var accent = fg(RIG_PALETTE.accent);
104
+ var accentDim = fg(RIG_PALETTE.accentDim);
105
+ var cyan = fg(RIG_PALETTE.cyan);
106
+ var red = fg(RIG_PALETTE.red);
107
+ var yellow = fg(RIG_PALETTE.yellow);
108
+ function bold(text) {
109
+ return `\x1B[1m${text}\x1B[22m`;
110
+ }
111
+ var DRONE_ART = [
112
+ " .-=-. .-=-. ",
113
+ " ( !!! ) ( !!! ) ",
114
+ " '-=-'._ _.'-=-' ",
115
+ " '._ _.' ",
116
+ " '=$$$$$$$=.' ",
117
+ " =$$$$$$$$$$$= ",
118
+ " $$$@@@@@@@@@@$$$ ",
119
+ " $$$@@ @@$$$ ",
120
+ " $$@ ? @$$$ ",
121
+ " $$$@ '-' @$$$ ",
122
+ " $$$@@ @@$$$ ",
123
+ " $$$@@@@@@@@@@$$$ ",
124
+ " =$$$$$$$$$$$= ",
125
+ " '=$$$$$$$=.' ",
126
+ " _.' '._ ",
127
+ " .-=-.' '.-=-. ",
128
+ " ( !!! ) ( !!! ) ",
129
+ " '-=-' '-=-' "
130
+ ];
131
+ var EYE_FRAMES = ["@", "o", "."];
132
+ var DRONE_WIDTH = DRONE_ART[0].length;
133
+ var DRONE_HEIGHT = DRONE_ART.length;
134
+ var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
135
+ function microDroneFrame(tick) {
136
+ const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
137
+ const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
138
+ return `(${blade})${eye}(${blade})`;
139
+ }
140
+ var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
141
+
142
+ // packages/cli/src/app/drone-ui.ts
143
+ var isTty = () => Boolean(process.stdout.isTTY);
144
+ function droneCancel(text) {
145
+ console.log(` ${red("\u2716")} ${ink3(text)}`);
146
+ }
147
+ var SELECT_THEME = {
148
+ selectedPrefix: (text) => accent(text),
149
+ selectedText: (text) => bold(ink(text)),
150
+ description: (text) => ink3(text),
151
+ scrollInfo: (text) => ink4(text),
152
+ noMatch: (text) => ink3(text)
153
+ };
154
+ async function runMiniTui(build) {
155
+ const terminal = new ProcessTerminal;
156
+ const tui = new TUI(terminal);
157
+ let settled = false;
158
+ return await new Promise((resolve) => {
159
+ const finish = (result) => {
160
+ if (settled)
161
+ return;
162
+ settled = true;
163
+ tui.stop();
164
+ resolve(result);
165
+ };
166
+ build(tui, finish);
167
+ tui.start();
168
+ });
169
+ }
170
+ async function droneSelect(input) {
171
+ if (!isTty() || input.options.length === 0) {
172
+ return input.initialValue ?? input.options[0]?.value ?? null;
173
+ }
174
+ return runMiniTui((tui, finish) => {
175
+ tui.addChild(new Text(` ${accent("\u258D")}${bold(ink(input.message))}`));
176
+ const items = input.options.map((option) => ({
177
+ value: option.value,
178
+ label: option.label,
179
+ ...option.hint ? { description: option.hint } : {}
180
+ }));
181
+ const list = new SelectList(items, Math.min(items.length, 12), SELECT_THEME);
182
+ const initialIndex = input.initialValue ? items.findIndex((item) => item.value === input.initialValue) : -1;
183
+ if (initialIndex > 0)
184
+ list.setSelectedIndex(initialIndex);
185
+ list.onSelect = (item) => finish(item.value);
186
+ list.onCancel = () => finish(null);
187
+ tui.addChild(list);
188
+ tui.addChild(new Text(ink4(" \u2191\u2193 navigate \xB7 enter select \xB7 esc cancel")));
189
+ tui.setFocus(list);
190
+ tui.addInputListener((data) => {
191
+ if (matchesKey(data, "ctrl+c") || matchesKey(data, "escape")) {
192
+ finish(null);
193
+ return { consume: true };
194
+ }
195
+ return;
196
+ });
197
+ });
198
+ }
74
199
 
75
200
  // packages/cli/src/commands/_connection-state.ts
76
201
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -182,8 +307,9 @@ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
182
307
  }
183
308
 
184
309
  // packages/cli/src/commands/_cli-format.ts
185
- import { log, note } from "@clack/prompts";
186
310
  import pc from "picocolors";
311
+ var themeDim = (value) => ink3(value);
312
+ var themeFaint = (value) => ink4(value);
187
313
  function truncate(value, width) {
188
314
  if (value.length <= width)
189
315
  return value;
@@ -196,32 +322,32 @@ function pad(value, width) {
196
322
  }
197
323
  function statusColor(status) {
198
324
  const normalized = status.toLowerCase();
199
- if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved"].includes(normalized))
200
- return pc.green;
325
+ if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved", "running"].includes(normalized))
326
+ return accent;
201
327
  if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
202
- return pc.red;
203
- if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
204
- return pc.cyan;
205
- if (["ready", "open", "queued", "created", "preparing", "local", "pending"].includes(normalized))
206
- return pc.yellow;
207
- return pc.dim;
328
+ return red;
329
+ if (["reviewing", "validating", "in_progress", "in-progress", "remote", "preparing", "closing-out"].includes(normalized))
330
+ return cyan;
331
+ if (["ready", "open", "queued", "created", "local", "pending"].includes(normalized))
332
+ return yellow;
333
+ return themeDim;
208
334
  }
209
335
  function formatStatusPill(status) {
210
336
  const label = status || "unknown";
211
337
  return statusColor(label)(`\u25CF ${label}`);
212
338
  }
213
339
  function formatSection(title, subtitle) {
214
- return `${pc.bold(pc.cyan("\u25C6"))} ${pc.bold(title)}${subtitle ? pc.dim(` \u2014 ${subtitle}`) : ""}`;
340
+ return `${pc.bold(accent("\u25C6"))} ${pc.bold(title)}${subtitle ? themeDim(` \u2014 ${subtitle}`) : ""}`;
215
341
  }
216
342
  function formatSuccessCard(title, rows = []) {
217
- 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}`);
343
+ const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${themeFaint("\u2502")} ${themeDim(key.padEnd(12))} ${value}`);
218
344
  return [formatSection(title), ...body].join(`
219
345
  `);
220
346
  }
221
347
  function formatNextSteps(steps) {
222
348
  if (steps.length === 0)
223
349
  return [];
224
- return [pc.bold("Next"), ...steps.map((step) => `${pc.dim("\u203A")} ${step}`)];
350
+ return [pc.bold("Next"), ...steps.map((step) => `${accent("\u203A")} ${step}`)];
225
351
  }
226
352
  function formatConnectionList(connections) {
227
353
  const rows = [["local", { kind: "local", mode: "auto" }], ...Object.entries(connections)];
@@ -239,9 +365,9 @@ function formatConnectionStatus(selected, connections) {
239
365
  const target = !connection ? "not configured" : connection.kind === "remote" ? connection.baseUrl : "local";
240
366
  return [
241
367
  formatSection("Rig server", "selected for this repo"),
242
- `${pc.dim("\u2502")} ${pc.dim("selected ")} ${pc.bold(selected)}`,
243
- `${pc.dim("\u2502")} ${pc.dim("kind ")} ${formatStatusPill(connection?.kind ?? "unknown")}`,
244
- `${pc.dim("\u2502")} ${pc.dim("target ")} ${target ?? "not configured"}`,
368
+ `${themeFaint("\u2502")} ${themeDim("selected ")} ${pc.bold(selected)}`,
369
+ `${themeFaint("\u2502")} ${themeDim("kind ")} ${formatStatusPill(connection?.kind ?? "unknown")}`,
370
+ `${themeFaint("\u2502")} ${themeDim("target ")} ${target ?? "not configured"}`,
245
371
  "",
246
372
  ...formatNextSteps(["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
247
373
  ].join(`
@@ -286,13 +412,13 @@ async function promptForConnectionAlias(context) {
286
412
  hint: connection.kind === "remote" ? connection.baseUrl : "local"
287
413
  }))
288
414
  ].filter((option, index, all) => all.findIndex((candidate) => candidate.value === option.value) === index);
289
- const answer = await select({
415
+ const answer = await droneSelect({
290
416
  message: "Select Rig server for this repo",
291
417
  initialValue: repo?.selected ?? "local",
292
418
  options
293
419
  });
294
- if (isCancel(answer)) {
295
- cancel("No server selected.");
420
+ if (answer === null) {
421
+ droneCancel("No server selected.");
296
422
  throw new CliError("No server selected.", 3);
297
423
  }
298
424
  return String(answer);
@@ -546,7 +672,9 @@ ${failure.contextLine}`, 1, { hint: failure.hint });
546
672
  })() : null;
547
673
  if (!response.ok) {
548
674
  const diagnostics = diagnosticMessage(payload);
549
- const detail = diagnostics ?? (text || response.statusText);
675
+ const rawDetail = diagnostics ?? (text || response.statusText);
676
+ const detail = diagnostics ? rawDetail : rawDetail.split(`
677
+ `).map((line) => line.trim()).find((line) => line.length > 0)?.slice(0, 200) ?? response.statusText;
550
678
  const failure = await buildServerFailureContext(context.projectRoot, server);
551
679
  throw new CliError(`Rig server request failed (${response.status}): ${detail}
552
680
  ${failure.contextLine}`, 1, { hint: failure.hint });