@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
@@ -1,9 +1,221 @@
1
1
  // @bun
2
2
  // packages/cli/src/commands/task.ts
3
- import { readFileSync as readFileSync3 } from "fs";
3
+ import { readFileSync as readFileSync4 } from "fs";
4
4
  import { spawnSync } from "child_process";
5
5
  import { resolve as resolve3 } from "path";
6
- import { cancel as cancel2, confirm, isCancel as isCancel2 } from "@clack/prompts";
6
+
7
+ // packages/cli/src/app/drone-ui.ts
8
+ import { ProcessTerminal, TUI, Text, Input, SelectList, matchesKey } from "@earendil-works/pi-tui";
9
+
10
+ // packages/cli/src/app/theme.ts
11
+ var RIG_PALETTE = {
12
+ ink: "#f2f3f6",
13
+ ink2: "#aeb0ba",
14
+ ink3: "#6c6e79",
15
+ ink4: "#44464f",
16
+ accent: "#ccff4d",
17
+ accentDim: "#a9d63f",
18
+ cyan: "#56d8ff",
19
+ red: "#ff5d5d",
20
+ yellow: "#ffd24d"
21
+ };
22
+ function hexToRgb(hex) {
23
+ const value = hex.replace("#", "");
24
+ return [
25
+ Number.parseInt(value.slice(0, 2), 16),
26
+ Number.parseInt(value.slice(2, 4), 16),
27
+ Number.parseInt(value.slice(4, 6), 16)
28
+ ];
29
+ }
30
+ function fg(hex) {
31
+ const [r, g, b] = hexToRgb(hex);
32
+ return (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
33
+ }
34
+ var ink = fg(RIG_PALETTE.ink);
35
+ var ink2 = fg(RIG_PALETTE.ink2);
36
+ var ink3 = fg(RIG_PALETTE.ink3);
37
+ var ink4 = fg(RIG_PALETTE.ink4);
38
+ var accent = fg(RIG_PALETTE.accent);
39
+ var accentDim = fg(RIG_PALETTE.accentDim);
40
+ var cyan = fg(RIG_PALETTE.cyan);
41
+ var red = fg(RIG_PALETTE.red);
42
+ var yellow = fg(RIG_PALETTE.yellow);
43
+ function bold(text) {
44
+ return `\x1B[1m${text}\x1B[22m`;
45
+ }
46
+ var DRONE_ART = [
47
+ " .-=-. .-=-. ",
48
+ " ( !!! ) ( !!! ) ",
49
+ " '-=-'._ _.'-=-' ",
50
+ " '._ _.' ",
51
+ " '=$$$$$$$=.' ",
52
+ " =$$$$$$$$$$$= ",
53
+ " $$$@@@@@@@@@@$$$ ",
54
+ " $$$@@ @@$$$ ",
55
+ " $$@ ? @$$$ ",
56
+ " $$$@ '-' @$$$ ",
57
+ " $$$@@ @@$$$ ",
58
+ " $$$@@@@@@@@@@$$$ ",
59
+ " =$$$$$$$$$$$= ",
60
+ " '=$$$$$$$=.' ",
61
+ " _.' '._ ",
62
+ " .-=-.' '.-=-. ",
63
+ " ( !!! ) ( !!! ) ",
64
+ " '-=-' '-=-' "
65
+ ];
66
+ var EYE_FRAMES = ["@", "o", "."];
67
+ var DRONE_WIDTH = DRONE_ART[0].length;
68
+ var DRONE_HEIGHT = DRONE_ART.length;
69
+ var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
70
+ function microDroneFrame(tick) {
71
+ const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
72
+ const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
73
+ return `(${blade})${eye}(${blade})`;
74
+ }
75
+ var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
76
+ function renderMicroDroneFrame(tick) {
77
+ const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
78
+ const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
79
+ return `${ink4("(")}${cyan(blade)}${ink4(")")}${bold(accent(eye))}${ink4("(")}${cyan(blade)}${ink4(")")}`;
80
+ }
81
+
82
+ // packages/cli/src/commands/_spinner.ts
83
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
84
+ function createTtySpinner(input) {
85
+ const output = input.output ?? process.stdout;
86
+ const isTty = output.isTTY === true;
87
+ const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
88
+ let label = input.label;
89
+ let frame = 0;
90
+ let paused = false;
91
+ let stopped = false;
92
+ let lastPrintedLabel = "";
93
+ const render = () => {
94
+ if (stopped || paused)
95
+ return;
96
+ if (!isTty) {
97
+ if (label !== lastPrintedLabel) {
98
+ output.write(`${label}
99
+ `);
100
+ lastPrintedLabel = label;
101
+ }
102
+ return;
103
+ }
104
+ frame = (frame + 1) % frames.length;
105
+ const glyph = frames[frame] ?? frames[0] ?? "";
106
+ output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
107
+ };
108
+ const clearLine = () => {
109
+ if (isTty)
110
+ output.write("\r\x1B[2K");
111
+ };
112
+ render();
113
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
114
+ return {
115
+ setLabel(next) {
116
+ label = next;
117
+ render();
118
+ },
119
+ pause() {
120
+ paused = true;
121
+ clearLine();
122
+ },
123
+ resume() {
124
+ if (stopped)
125
+ return;
126
+ paused = false;
127
+ render();
128
+ },
129
+ stop(finalLine) {
130
+ if (stopped)
131
+ return;
132
+ stopped = true;
133
+ if (timer)
134
+ clearInterval(timer);
135
+ clearLine();
136
+ if (finalLine)
137
+ output.write(`${finalLine}
138
+ `);
139
+ }
140
+ };
141
+ }
142
+
143
+ // packages/cli/src/app/drone-ui.ts
144
+ var isTty = () => Boolean(process.stdout.isTTY);
145
+ function droneNote(message, title) {
146
+ if (title)
147
+ console.log(` ${accentDim("\u25C7")} ${bold(ink2(title))}`);
148
+ for (const line of message.split(`
149
+ `)) {
150
+ console.log(` ${ink4("\u2502")} ${line}`);
151
+ }
152
+ }
153
+ function droneCancel(text) {
154
+ console.log(` ${red("\u2716")} ${ink3(text)}`);
155
+ }
156
+ var SELECT_THEME = {
157
+ selectedPrefix: (text) => accent(text),
158
+ selectedText: (text) => bold(ink(text)),
159
+ description: (text) => ink3(text),
160
+ scrollInfo: (text) => ink4(text),
161
+ noMatch: (text) => ink3(text)
162
+ };
163
+ async function runMiniTui(build) {
164
+ const terminal = new ProcessTerminal;
165
+ const tui = new TUI(terminal);
166
+ let settled = false;
167
+ return await new Promise((resolve) => {
168
+ const finish = (result) => {
169
+ if (settled)
170
+ return;
171
+ settled = true;
172
+ tui.stop();
173
+ resolve(result);
174
+ };
175
+ build(tui, finish);
176
+ tui.start();
177
+ });
178
+ }
179
+ async function droneSelect(input) {
180
+ if (!isTty() || input.options.length === 0) {
181
+ return input.initialValue ?? input.options[0]?.value ?? null;
182
+ }
183
+ return runMiniTui((tui, finish) => {
184
+ tui.addChild(new Text(` ${accent("\u258D")}${bold(ink(input.message))}`));
185
+ const items = input.options.map((option) => ({
186
+ value: option.value,
187
+ label: option.label,
188
+ ...option.hint ? { description: option.hint } : {}
189
+ }));
190
+ const list = new SelectList(items, Math.min(items.length, 12), SELECT_THEME);
191
+ const initialIndex = input.initialValue ? items.findIndex((item) => item.value === input.initialValue) : -1;
192
+ if (initialIndex > 0)
193
+ list.setSelectedIndex(initialIndex);
194
+ list.onSelect = (item) => finish(item.value);
195
+ list.onCancel = () => finish(null);
196
+ tui.addChild(list);
197
+ tui.addChild(new Text(ink4(" \u2191\u2193 navigate \xB7 enter select \xB7 esc cancel")));
198
+ tui.setFocus(list);
199
+ tui.addInputListener((data) => {
200
+ if (matchesKey(data, "ctrl+c") || matchesKey(data, "escape")) {
201
+ finish(null);
202
+ return { consume: true };
203
+ }
204
+ return;
205
+ });
206
+ });
207
+ }
208
+ async function droneConfirm(input) {
209
+ const answer = await droneSelect({
210
+ message: input.message,
211
+ options: [
212
+ { value: "yes", label: "Yes" },
213
+ { value: "no", label: "No" }
214
+ ],
215
+ initialValue: input.initialValue === false ? "no" : "yes"
216
+ });
217
+ return answer === null ? null : answer === "yes";
218
+ }
7
219
 
8
220
  // packages/cli/src/runner.ts
9
221
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
@@ -413,7 +625,9 @@ ${failure.contextLine}`, 1, { hint: failure.hint });
413
625
  })() : null;
414
626
  if (!response.ok) {
415
627
  const diagnostics = diagnosticMessage(payload);
416
- const detail = diagnostics ?? (text || response.statusText);
628
+ const rawDetail = diagnostics ?? (text || response.statusText);
629
+ const detail = diagnostics ? rawDetail : rawDetail.split(`
630
+ `).map((line) => line.trim()).find((line) => line.length > 0)?.slice(0, 200) ?? response.statusText;
417
631
  const failure = await buildServerFailureContext(context.projectRoot, server);
418
632
  throw new CliError(`Rig server request failed (${response.status}): ${detail}
419
633
  ${failure.contextLine}`, 1, { hint: failure.hint });
@@ -786,9 +1000,6 @@ function withMutedConsole(mute, fn) {
786
1000
  }
787
1001
  }
788
1002
 
789
- // packages/cli/src/commands/_task-picker.ts
790
- import { cancel, isCancel, select } from "@clack/prompts";
791
-
792
1003
  // packages/cli/src/commands/_operator-surface.ts
793
1004
  import { createInterface } from "readline";
794
1005
  import { createInterface as createPromptInterface } from "readline/promises";
@@ -1011,8 +1222,8 @@ async function selectTaskWithTextPicker(tasks, io = {}) {
1011
1222
  return null;
1012
1223
  if (tasks.length === 1)
1013
1224
  return tasks[0];
1014
- const isTty = io.isTty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
1015
- if (!isTty) {
1225
+ const isTty2 = io.isTty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
1226
+ if (!isTty2) {
1016
1227
  throw new Error("task run requires an interactive terminal to pick a task; pass --task <id>, --next, or --detach with a task id.");
1017
1228
  }
1018
1229
  if (io.prompt || io.renderer) {
@@ -1036,12 +1247,12 @@ async function selectTaskWithTextPicker(tasks, io = {}) {
1036
1247
  label: `${taskId2(task)} \xB7 ${typeof task.title === "string" && task.title.trim() ? task.title.trim() : "Untitled task"}`,
1037
1248
  hint: typeof task.status === "string" && task.status.trim() ? task.status.trim() : undefined
1038
1249
  }));
1039
- const answer = await select({
1250
+ const answer = await droneSelect({
1040
1251
  message: "Select Rig task",
1041
1252
  options
1042
1253
  });
1043
- if (isCancel(answer)) {
1044
- cancel("No task selected.");
1254
+ if (answer === null) {
1255
+ droneCancel("No task selected.");
1045
1256
  return null;
1046
1257
  }
1047
1258
  const index = Number.parseInt(String(answer), 10);
@@ -1050,70 +1261,6 @@ async function selectTaskWithTextPicker(tasks, io = {}) {
1050
1261
 
1051
1262
  // packages/cli/src/commands/_async-ui.ts
1052
1263
  import pc from "picocolors";
1053
-
1054
- // packages/cli/src/commands/_spinner.ts
1055
- var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1056
- function createTtySpinner(input) {
1057
- const output = input.output ?? process.stdout;
1058
- const isTty = output.isTTY === true;
1059
- const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
1060
- let label = input.label;
1061
- let frame = 0;
1062
- let paused = false;
1063
- let stopped = false;
1064
- let lastPrintedLabel = "";
1065
- const render = () => {
1066
- if (stopped || paused)
1067
- return;
1068
- if (!isTty) {
1069
- if (label !== lastPrintedLabel) {
1070
- output.write(`${label}
1071
- `);
1072
- lastPrintedLabel = label;
1073
- }
1074
- return;
1075
- }
1076
- frame = (frame + 1) % frames.length;
1077
- const glyph = frames[frame] ?? frames[0] ?? "";
1078
- output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
1079
- };
1080
- const clearLine = () => {
1081
- if (isTty)
1082
- output.write("\r\x1B[2K");
1083
- };
1084
- render();
1085
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
1086
- return {
1087
- setLabel(next) {
1088
- label = next;
1089
- render();
1090
- },
1091
- pause() {
1092
- paused = true;
1093
- clearLine();
1094
- },
1095
- resume() {
1096
- if (stopped)
1097
- return;
1098
- paused = false;
1099
- render();
1100
- },
1101
- stop(finalLine) {
1102
- if (stopped)
1103
- return;
1104
- stopped = true;
1105
- if (timer)
1106
- clearInterval(timer);
1107
- clearLine();
1108
- if (finalLine)
1109
- output.write(`${finalLine}
1110
- `);
1111
- }
1112
- };
1113
- }
1114
-
1115
- // packages/cli/src/commands/_async-ui.ts
1116
- var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
1117
1264
  var DONE_SYMBOL = pc.green("\u25C7");
1118
1265
  var FAIL_SYMBOL = pc.red("\u25A0");
1119
1266
  var activeUpdate = null;
@@ -1127,9 +1274,9 @@ async function withSpinner(label, work, options = {}) {
1127
1274
  return work(outer);
1128
1275
  }
1129
1276
  const output = options.output ?? process.stderr;
1130
- const isTty = output.isTTY === true;
1277
+ const isTty2 = output.isTTY === true;
1131
1278
  let lastLabel = label;
1132
- if (!isTty) {
1279
+ if (!isTty2) {
1133
1280
  output.write(`${label}
1134
1281
  `);
1135
1282
  const update2 = (next) => {
@@ -1147,8 +1294,8 @@ async function withSpinner(label, work, options = {}) {
1147
1294
  const spinner = createTtySpinner({
1148
1295
  label,
1149
1296
  output,
1150
- frames: CLACK_SPINNER_FRAMES,
1151
- styleFrame: (frame) => pc.magenta(frame)
1297
+ frames: MICRO_DRONE_FRAMES,
1298
+ styleFrame: (frame) => renderMicroDroneFrame(Math.max(0, MICRO_DRONE_FRAMES.indexOf(frame)))
1152
1299
  });
1153
1300
  const update = (next) => {
1154
1301
  lastLabel = next;
@@ -1170,8 +1317,8 @@ async function withSpinner(label, work, options = {}) {
1170
1317
  }
1171
1318
 
1172
1319
  // packages/cli/src/commands/_pi-frontend.ts
1173
- import { mkdtempSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
1174
- import { tmpdir } from "os";
1320
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, mkdtempSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
1321
+ import { homedir as homedir2, tmpdir } from "os";
1175
1322
  import { join } from "path";
1176
1323
  import { main as runPiMain } from "@earendil-works/pi-coding-agent";
1177
1324
  import createPiRigExtension from "@rig/pi-rig";
@@ -1194,6 +1341,7 @@ function buildOperatorPiEnv(input) {
1194
1341
  return {
1195
1342
  PI_CODING_AGENT_SESSION_DIR: input.sessionDir,
1196
1343
  PI_SKIP_VERSION_CHECK: "1",
1344
+ PI_HIDDEN_COMMANDS: "import,fork,clone,tree,new,resume,trust",
1197
1345
  RIG_PI_OPERATOR_SESSION: "1",
1198
1346
  RIG_RUN_ID: input.runId,
1199
1347
  RIG_SERVER_URL: input.serverUrl,
@@ -1203,6 +1351,10 @@ function buildOperatorPiEnv(input) {
1203
1351
  }
1204
1352
  async function prepareOperatorConsole(context, runId, tempSessionDir) {
1205
1353
  const server = await ensureServerForCli(context.projectRoot);
1354
+ const localCwd = join(homedir2(), ".rig", "drones", runId.slice(0, 8));
1355
+ mkdirSync2(localCwd, { recursive: true });
1356
+ trustDroneCwd(localCwd);
1357
+ installRigPiTheme();
1206
1358
  let sessionFileArg = [];
1207
1359
  try {
1208
1360
  const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/session-file`);
@@ -1216,7 +1368,7 @@ async function prepareOperatorConsole(context, runId, tempSessionDir) {
1216
1368
  try {
1217
1369
  const header = JSON.parse(line);
1218
1370
  if (header.type === "session" && typeof header.cwd === "string") {
1219
- return JSON.stringify({ ...header, cwd: process.cwd() });
1371
+ return JSON.stringify({ ...header, cwd: localCwd });
1220
1372
  }
1221
1373
  } catch {}
1222
1374
  return line;
@@ -1228,6 +1380,105 @@ async function prepareOperatorConsole(context, runId, tempSessionDir) {
1228
1380
  } catch {}
1229
1381
  return { server, sessionFileArg };
1230
1382
  }
1383
+ var RIG_PI_THEME = {
1384
+ $schema: "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
1385
+ name: "rig",
1386
+ vars: {
1387
+ acid: "#ccff4d",
1388
+ acidDim: "#a9d63f",
1389
+ cyan: "#56d8ff",
1390
+ red: "#ff5d5d",
1391
+ yellow: "#ffd24d",
1392
+ ink: "#f2f3f6",
1393
+ ink2: "#aeb0ba",
1394
+ ink3: "#6c6e79",
1395
+ ink4: "#44464f",
1396
+ panel: "#101115",
1397
+ panelUser: "#14161b",
1398
+ toolPending: "#0e1013",
1399
+ toolSuccess: "#10150c",
1400
+ toolError: "#1a0f0f",
1401
+ customMsg: "#0f1410"
1402
+ },
1403
+ colors: {
1404
+ accent: "acid",
1405
+ border: "ink4",
1406
+ borderAccent: "acid",
1407
+ borderMuted: "ink4",
1408
+ success: "acid",
1409
+ error: "red",
1410
+ warning: "yellow",
1411
+ muted: "ink3",
1412
+ dim: "ink4",
1413
+ text: "ink",
1414
+ thinkingText: "ink3",
1415
+ selectedBg: "panel",
1416
+ userMessageBg: "panelUser",
1417
+ userMessageText: "ink",
1418
+ customMessageBg: "customMsg",
1419
+ customMessageText: "ink2",
1420
+ customMessageLabel: "acidDim",
1421
+ toolPendingBg: "toolPending",
1422
+ toolSuccessBg: "toolSuccess",
1423
+ toolErrorBg: "toolError",
1424
+ toolTitle: "ink",
1425
+ toolOutput: "ink3",
1426
+ mdHeading: "acid",
1427
+ mdLink: "cyan",
1428
+ mdLinkUrl: "ink4",
1429
+ mdCode: "acidDim",
1430
+ mdCodeBlock: "ink2",
1431
+ mdCodeBlockBorder: "ink4",
1432
+ mdQuote: "ink3",
1433
+ mdQuoteBorder: "ink4",
1434
+ mdHr: "ink4",
1435
+ mdListBullet: "acid",
1436
+ toolDiffAdded: "acid",
1437
+ toolDiffRemoved: "red",
1438
+ toolDiffContext: "ink3",
1439
+ syntaxComment: "ink3",
1440
+ syntaxKeyword: "cyan",
1441
+ syntaxFunction: "acid",
1442
+ syntaxVariable: "ink",
1443
+ syntaxString: "acidDim",
1444
+ syntaxNumber: "yellow",
1445
+ syntaxType: "cyan",
1446
+ syntaxOperator: "ink2",
1447
+ syntaxPunctuation: "ink3",
1448
+ thinkingOff: "ink4",
1449
+ thinkingMinimal: "ink3",
1450
+ thinkingLow: "ink2",
1451
+ thinkingMedium: "cyan",
1452
+ thinkingHigh: "acidDim",
1453
+ thinkingXhigh: "acid",
1454
+ bashMode: "cyan"
1455
+ }
1456
+ };
1457
+ function trustDroneCwd(localCwd) {
1458
+ try {
1459
+ const agentDir = join(homedir2(), ".pi", "agent");
1460
+ mkdirSync2(agentDir, { recursive: true });
1461
+ const trustPath = join(agentDir, "trust.json");
1462
+ const store = existsSync3(trustPath) ? JSON.parse(readFileSync3(trustPath, "utf8")) : {};
1463
+ if (store[localCwd] !== true) {
1464
+ store[localCwd] = true;
1465
+ writeFileSync2(trustPath, `${JSON.stringify(store, null, "\t")}
1466
+ `);
1467
+ }
1468
+ } catch {}
1469
+ }
1470
+ function installRigPiTheme() {
1471
+ try {
1472
+ const themesDir = join(homedir2(), ".pi", "agent", "themes");
1473
+ mkdirSync2(themesDir, { recursive: true });
1474
+ const themePath = join(themesDir, "rig.json");
1475
+ const next = `${JSON.stringify(RIG_PI_THEME, null, "\t")}
1476
+ `;
1477
+ if (!existsSync3(themePath) || readFileSync3(themePath, "utf8") !== next) {
1478
+ writeFileSync2(themePath, next);
1479
+ }
1480
+ } catch {}
1481
+ }
1231
1482
  async function attachRunBundledPiFrontend(context, input) {
1232
1483
  const tempSessionDir = mkdtempSync(join(tmpdir(), "rig-pi-frontend-sessions-"));
1233
1484
  const { server, sessionFileArg } = await withSpinner(`Opening Pi console for run ${input.runId}\u2026`, () => prepareOperatorConsole(context, input.runId, tempSessionDir), { outputMode: context.outputMode });
@@ -1365,8 +1616,9 @@ async function attachRunOperatorView(context, input) {
1365
1616
  }
1366
1617
 
1367
1618
  // packages/cli/src/commands/_cli-format.ts
1368
- import { log, note } from "@clack/prompts";
1369
1619
  import pc2 from "picocolors";
1620
+ var themeDim = (value) => ink3(value);
1621
+ var themeFaint = (value) => ink4(value);
1370
1622
  function stringField(record, key, fallback = "") {
1371
1623
  const value = record[key];
1372
1624
  return typeof value === "string" && value.trim() ? value.trim() : fallback;
@@ -1395,15 +1647,15 @@ function pad(value, width) {
1395
1647
  }
1396
1648
  function statusColor(status) {
1397
1649
  const normalized = status.toLowerCase();
1398
- if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved"].includes(normalized))
1399
- return pc2.green;
1650
+ if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved", "running"].includes(normalized))
1651
+ return accent;
1400
1652
  if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
1401
- return pc2.red;
1402
- if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
1403
- return pc2.cyan;
1404
- if (["ready", "open", "queued", "created", "preparing", "local", "pending"].includes(normalized))
1405
- return pc2.yellow;
1406
- return pc2.dim;
1653
+ return red;
1654
+ if (["reviewing", "validating", "in_progress", "in-progress", "remote", "preparing", "closing-out"].includes(normalized))
1655
+ return cyan;
1656
+ if (["ready", "open", "queued", "created", "local", "pending"].includes(normalized))
1657
+ return yellow;
1658
+ return themeDim;
1407
1659
  }
1408
1660
  function compactValue(value) {
1409
1661
  if (value === null || value === undefined)
@@ -1424,27 +1676,24 @@ function printFormattedOutput(message2, options = {}) {
1424
1676
  console.log(message2);
1425
1677
  return;
1426
1678
  }
1427
- if (options.title)
1428
- note(message2, options.title);
1429
- else
1430
- log.message(message2);
1679
+ droneNote(message2, options.title);
1431
1680
  }
1432
1681
  function formatStatusPill(status) {
1433
1682
  const label = status || "unknown";
1434
1683
  return statusColor(label)(`\u25CF ${label}`);
1435
1684
  }
1436
1685
  function formatSection(title, subtitle) {
1437
- return `${pc2.bold(pc2.cyan("\u25C6"))} ${pc2.bold(title)}${subtitle ? pc2.dim(` \u2014 ${subtitle}`) : ""}`;
1686
+ return `${pc2.bold(accent("\u25C6"))} ${pc2.bold(title)}${subtitle ? themeDim(` \u2014 ${subtitle}`) : ""}`;
1438
1687
  }
1439
1688
  function formatSuccessCard(title, rows = []) {
1440
- 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}`);
1689
+ const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${themeFaint("\u2502")} ${themeDim(key.padEnd(12))} ${value}`);
1441
1690
  return [formatSection(title), ...body].join(`
1442
1691
  `);
1443
1692
  }
1444
1693
  function formatNextSteps(steps) {
1445
1694
  if (steps.length === 0)
1446
1695
  return [];
1447
- return [pc2.bold("Next"), ...steps.map((step) => `${pc2.dim("\u203A")} ${step}`)];
1696
+ return [pc2.bold("Next"), ...steps.map((step) => `${accent("\u203A")} ${step}`)];
1448
1697
  }
1449
1698
  function formatTaskList(tasks, options = {}) {
1450
1699
  if (options.raw)
@@ -1466,8 +1715,8 @@ function formatTaskList(tasks, options = {}) {
1466
1715
  const statusWidth = Math.min(16, Math.max(6, ...rows.map((row) => row.status.length)));
1467
1716
  const header = `${pc2.bold(pad("TASK", idWidth))} ${pc2.bold(pad("STATUS", statusWidth))} ${pc2.bold("TITLE")}`;
1468
1717
  const body = rows.map((row) => {
1469
- const labels = row.labels.length > 0 ? pc2.dim(` ${row.labels.slice(0, 4).map((label) => `#${label}`).join(" ")}`) : "";
1470
- const source = row.source ? pc2.dim(` ${row.source}`) : "";
1718
+ const labels = row.labels.length > 0 ? themeDim(` ${row.labels.slice(0, 4).map((label) => `#${label}`).join(" ")}`) : "";
1719
+ const source = row.source ? themeDim(` ${row.source}`) : "";
1471
1720
  return [
1472
1721
  pc2.bold(pad(truncate(row.id, idWidth), idWidth)),
1473
1722
  statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
@@ -1572,7 +1821,6 @@ async function printPendingInboxFooter(context) {
1572
1821
  }
1573
1822
 
1574
1823
  // packages/cli/src/commands/_help-catalog.ts
1575
- import { intro, log as log2, note as note2, outro } from "@clack/prompts";
1576
1824
  import pc3 from "picocolors";
1577
1825
  var TOP_LEVEL_SECTIONS = [
1578
1826
  {
@@ -1875,25 +2123,6 @@ var ALL_GROUPS = [...PRIMARY_GROUPS, ...ADVANCED_GROUPS];
1875
2123
  function heading(title) {
1876
2124
  return pc3.bold(pc3.cyan(title));
1877
2125
  }
1878
- function renderRigBanner(version) {
1879
- const m = (s) => pc3.bold(pc3.magenta(s));
1880
- const c = (s) => pc3.bold(pc3.cyan(s));
1881
- const y = (s) => pc3.yellow(s);
1882
- const d = (s) => pc3.dim(s);
1883
- const lines = [
1884
- m(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + d(" \u2591\u2592\u2593\u2588 "),
1885
- m(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ") + d("\u2593\u2592\u2591"),
1886
- c(" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2557") + d(" \u2591\u2592"),
1887
- c(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551") + d(" \u2588\u2593\u2591"),
1888
- y(" \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + d(" \u2592\u2591"),
1889
- y(" \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D "),
1890
- "",
1891
- ` ${c("\u25E2\u25E4")} ${pc3.bold("the control rig for autonomous coding agents")} ${m("//")} ${d("you are the operator")}`,
1892
- version ? ` ${d(`v${version} \xB7 jack in: rig task run --next`)}` : ` ${d("jack in: rig task run --next")}`
1893
- ];
1894
- return lines.join(`
1895
- `);
1896
- }
1897
2126
  function commandLine(command, description) {
1898
2127
  const commandColumn = command.length >= 38 ? `${command} ` : command.padEnd(38);
1899
2128
  return `${pc3.dim("\u2502")} ${pc3.bold(commandColumn)} ${description}`;
@@ -1947,67 +2176,8 @@ function renderGroupHelp(groupName) {
1947
2176
  const group = ALL_GROUPS.find((candidate) => candidate.name === groupName);
1948
2177
  return group ? renderGroup(group) : null;
1949
2178
  }
1950
- function shouldUseClackOutput2() {
1951
- return Boolean(process.stdout.isTTY) && process.env.RIG_CLI_PLAIN_HELP !== "1";
1952
- }
1953
- function printTopLevelHelp(state = {}) {
1954
- if (!shouldUseClackOutput2()) {
1955
- console.log(renderTopLevelHelp());
1956
- return;
1957
- }
1958
- console.log(renderRigBanner(state.version));
1959
- console.log("");
1960
- if (state.projectInitialized === false) {
1961
- intro("no rig project in this directory");
1962
- note2([
1963
- commandLine("rig init", "Set this repo up: config, GitHub auth, task source, server, Pi."),
1964
- commandLine("rig init --yes", "Same, non-interactive, sensible defaults."),
1965
- commandLine("rig doctor", "Already initialized somewhere else? Check the wiring.")
1966
- ].join(`
1967
- `), "Get started");
1968
- outro("After init: rig task run --next puts an agent on your next task.");
1969
- return;
1970
- }
1971
- intro(state.selectedServer ? `server: ${state.selectedServer}` : "rig");
1972
- for (const section of TOP_LEVEL_SECTIONS) {
1973
- note2(renderCommandBlock(section.commands), `${section.title} \u2014 ${section.subtitle}`);
1974
- }
1975
- log2.info("More: rig help --advanced \xB7 rig <group> --help \xB7 rig --version");
1976
- note2([
1977
- commandLine("--project <path>", "Use a project root instead of auto-discovery."),
1978
- commandLine("--json", "Emit structured output for scripts/agents."),
1979
- commandLine("--dry-run", "Print the command plan without mutating state.")
1980
- ].join(`
1981
- `), "Global options");
1982
- outro("init \u2192 task run \u2192 attach (Pi console: watch + steer live) \u2192 inbox \u2192 merged.");
1983
- }
1984
2179
  function printGroupHelpDocument(groupName) {
1985
- const rendered = renderGroupHelp(groupName) ?? renderTopLevelHelp();
1986
- if (!shouldUseClackOutput2()) {
1987
- console.log(rendered);
1988
- return;
1989
- }
1990
- const group = ALL_GROUPS.find((candidate) => candidate.name === groupName);
1991
- if (!group) {
1992
- printTopLevelHelp();
1993
- return;
1994
- }
1995
- intro(`rig ${group.name}`);
1996
- note2(group.summary, "Purpose");
1997
- note2(group.usage.join(`
1998
- `), "Usage");
1999
- note2(group.commands.map((entry) => commandLine(entry.command, entry.description)).join(`
2000
- `), "Commands");
2001
- if (group.examples?.length)
2002
- note2(group.examples.map((line) => `$ ${line}`).join(`
2003
- `), "Examples");
2004
- if (group.next?.length)
2005
- note2(group.next.map((line) => `\u203A ${line}`).join(`
2006
- `), "Next steps");
2007
- if (group.advanced?.length)
2008
- log2.info(group.advanced.join(`
2009
- `));
2010
- outro("Run with --json when scripts need structured output.");
2180
+ console.log(renderGroupHelp(groupName) ?? renderTopLevelHelp());
2011
2181
  }
2012
2182
 
2013
2183
  // packages/cli/src/commands/task.ts
@@ -2113,12 +2283,12 @@ async function resolveDirtyBaselineForTaskRun(context, explicit) {
2113
2283
  if (explicit)
2114
2284
  return { mode: explicit, state };
2115
2285
  if (context.outputMode === "text" && process.stdin.isTTY && process.stdout.isTTY) {
2116
- const answer = await confirm({
2286
+ const answer = await droneConfirm({
2117
2287
  message: "Include current uncommitted changes in run baseline?",
2118
2288
  initialValue: false
2119
2289
  });
2120
- if (isCancel2(answer)) {
2121
- cancel2("Run cancelled.");
2290
+ if (answer === null) {
2291
+ droneCancel("Run cancelled.");
2122
2292
  throw new CliError("Run cancelled by user.", 1);
2123
2293
  }
2124
2294
  return { mode: answer ? "dirty-snapshot" : "head", state };
@@ -2273,7 +2443,7 @@ async function executeTask(context, args, options) {
2273
2443
  const fileFlag = takeOption(rest.slice(1), "--file");
2274
2444
  let content;
2275
2445
  if (fileFlag.value) {
2276
- content = readFileSync3(resolve3(context.projectRoot, fileFlag.value), "utf-8");
2446
+ content = readFileSync4(resolve3(context.projectRoot, fileFlag.value), "utf-8");
2277
2447
  } else {
2278
2448
  content = await readStdin();
2279
2449
  }
@@ -2497,5 +2667,6 @@ async function executeTask(context, args, options) {
2497
2667
  }
2498
2668
  }
2499
2669
  export {
2670
+ loadTaskRunProjectDefaults,
2500
2671
  executeTask
2501
2672
  };