@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.
- package/dist/bin/rig.js +10130 -9246
- package/dist/src/app/board.js +1783 -0
- package/dist/src/app/drone-ui.js +294 -0
- package/dist/src/{commands/_tui-theme.js → app/theme.js} +16 -1
- package/dist/src/commands/_async-ui.js +74 -3
- package/dist/src/commands/_cli-format.js +107 -29
- package/dist/src/commands/_doctor-checks.js +3 -1
- package/dist/src/commands/_help-catalog.js +8 -70
- package/dist/src/commands/_operator-view.js +184 -7
- package/dist/src/commands/_pi-frontend.js +184 -7
- package/dist/src/commands/_preflight.js +3 -1
- package/dist/src/commands/_server-client.js +3 -1
- package/dist/src/commands/_snapshot-upload.js +3 -1
- package/dist/src/commands/_task-picker.js +132 -7
- package/dist/src/commands/browser.js +306 -30
- package/dist/src/commands/connect.js +146 -20
- package/dist/src/commands/doctor.js +77 -4
- package/dist/src/commands/github.js +77 -4
- package/dist/src/commands/inbox.js +171 -88
- package/dist/src/commands/init.js +349 -9
- package/dist/src/commands/inspect.js +77 -4
- package/dist/src/commands/run.js +214 -28
- package/dist/src/commands/server.js +149 -21
- package/dist/src/commands/setup.js +77 -4
- package/dist/src/commands/stats.js +109 -107
- package/dist/src/commands/task-report-bug.js +231 -40
- package/dist/src/commands/task-run-driver.js +3 -1
- package/dist/src/commands/task.js +355 -184
- package/dist/src/commands.js +10126 -9242
- package/dist/src/index.js +10131 -9247
- package/package.json +8 -9
- package/dist/src/commands/_operator-board.js +0 -730
package/dist/src/commands/run.js
CHANGED
|
@@ -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
|
|
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:
|
|
916
|
-
styleFrame: (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:
|
|
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
|
|
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
|
|
1154
|
-
if (["
|
|
1155
|
-
return
|
|
1156
|
-
if (["ready", "open", "queued", "created", "
|
|
1157
|
-
return
|
|
1158
|
-
return
|
|
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
|
-
|
|
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(
|
|
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]) => `${
|
|
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) => `${
|
|
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" ?
|
|
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 ?
|
|
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(
|
|
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(
|
|
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 `${
|
|
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/
|
|
73
|
-
import {
|
|
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
|
|
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
|
|
203
|
-
if (["
|
|
204
|
-
return
|
|
205
|
-
if (["ready", "open", "queued", "created", "
|
|
206
|
-
return
|
|
207
|
-
return
|
|
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(
|
|
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]) => `${
|
|
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) => `${
|
|
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
|
-
`${
|
|
243
|
-
`${
|
|
244
|
-
`${
|
|
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
|
|
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 (
|
|
295
|
-
|
|
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
|
|
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 });
|