@h-rig/cli 0.0.6-alpha.19 → 0.0.6-alpha.20
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 +266 -56
- package/dist/src/commands/_cli-format.js +106 -0
- package/dist/src/commands/_doctor-checks.js +3 -2
- package/dist/src/commands/_pi-install.js +4 -3
- package/dist/src/commands/_pi-session.js +253 -0
- package/dist/src/commands/_preflight.js +3 -2
- package/dist/src/commands/_task-picker.js +31 -11
- package/dist/src/commands/doctor.js +3 -2
- package/dist/src/commands/init.js +4 -3
- package/dist/src/commands/run.js +169 -9
- package/dist/src/commands/setup.js +3 -2
- package/dist/src/commands/task.js +234 -42
- package/dist/src/commands.js +266 -56
- package/dist/src/index.js +266 -56
- package/package.json +5 -5
package/dist/bin/rig.js
CHANGED
|
@@ -2990,7 +2990,8 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
2990
2990
|
import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
|
|
2991
2991
|
import { homedir as homedir3 } from "os";
|
|
2992
2992
|
import { resolve as resolve10 } from "path";
|
|
2993
|
-
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
2993
|
+
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
2994
|
+
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
2994
2995
|
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
2995
2996
|
export { default } from '@rig/pi-rig';
|
|
2996
2997
|
`;
|
|
@@ -3018,7 +3019,7 @@ function resolvePiHomeDir(inputHomeDir) {
|
|
|
3018
3019
|
function piListContainsPiRig(output) {
|
|
3019
3020
|
return output.split(/\r?\n/).some((line) => {
|
|
3020
3021
|
const normalized = line.trim();
|
|
3021
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
3022
|
+
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
3022
3023
|
});
|
|
3023
3024
|
}
|
|
3024
3025
|
async function safeRun(runner, command, options) {
|
|
@@ -3134,7 +3135,7 @@ async function ensureRemotePiRigInstalled(input) {
|
|
|
3134
3135
|
const payload = await input.requestJson("/api/pi-rig/install", {
|
|
3135
3136
|
method: "POST",
|
|
3136
3137
|
headers: { "content-type": "application/json" },
|
|
3137
|
-
body: JSON.stringify({ package:
|
|
3138
|
+
body: JSON.stringify({ package: PI_RIG_PACKAGE_NAME, scope: "global" })
|
|
3138
3139
|
});
|
|
3139
3140
|
const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
3140
3141
|
const piOk = record.piOk === true || record.ok === true;
|
|
@@ -6676,6 +6677,193 @@ async function attachRunOperatorView(context, input) {
|
|
|
6676
6677
|
return { ...snapshot, steered, detached };
|
|
6677
6678
|
}
|
|
6678
6679
|
|
|
6680
|
+
// packages/cli/src/commands/_cli-format.ts
|
|
6681
|
+
import pc3 from "picocolors";
|
|
6682
|
+
function stringField(record, key, fallback = "") {
|
|
6683
|
+
const value = record[key];
|
|
6684
|
+
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
6685
|
+
}
|
|
6686
|
+
function arrayField(record, key) {
|
|
6687
|
+
const value = record[key];
|
|
6688
|
+
return Array.isArray(value) ? value.flatMap((entry) => typeof entry === "string" && entry.trim() ? [entry.trim()] : []) : [];
|
|
6689
|
+
}
|
|
6690
|
+
function rawObject(record) {
|
|
6691
|
+
const raw = record.raw;
|
|
6692
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
6693
|
+
}
|
|
6694
|
+
function truncate(value, width) {
|
|
6695
|
+
if (value.length <= width)
|
|
6696
|
+
return value;
|
|
6697
|
+
if (width <= 1)
|
|
6698
|
+
return "\u2026";
|
|
6699
|
+
return `${value.slice(0, width - 1)}\u2026`;
|
|
6700
|
+
}
|
|
6701
|
+
function pad(value, width) {
|
|
6702
|
+
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
6703
|
+
}
|
|
6704
|
+
function statusColor(status) {
|
|
6705
|
+
const normalized = status.toLowerCase();
|
|
6706
|
+
if (["completed", "merged", "closed", "done", "accepted"].includes(normalized))
|
|
6707
|
+
return pc3.green;
|
|
6708
|
+
if (["failed", "needs_attention", "needs-attention", "blocked"].includes(normalized))
|
|
6709
|
+
return pc3.red;
|
|
6710
|
+
if (["running", "reviewing", "validating", "in_progress", "in-progress"].includes(normalized))
|
|
6711
|
+
return pc3.cyan;
|
|
6712
|
+
if (["ready", "open", "queued", "created", "preparing"].includes(normalized))
|
|
6713
|
+
return pc3.yellow;
|
|
6714
|
+
return pc3.dim;
|
|
6715
|
+
}
|
|
6716
|
+
function formatTaskList(tasks, options = {}) {
|
|
6717
|
+
if (tasks.length === 0)
|
|
6718
|
+
return pc3.dim("No matching tasks.");
|
|
6719
|
+
if (options.raw)
|
|
6720
|
+
return tasks.map((task) => JSON.stringify(task)).join(`
|
|
6721
|
+
`);
|
|
6722
|
+
const rows = tasks.map((task) => {
|
|
6723
|
+
const raw = rawObject(task);
|
|
6724
|
+
const id = stringField(task, "id", "<unknown>");
|
|
6725
|
+
const status = stringField(task, "status", "unknown");
|
|
6726
|
+
const title = stringField(task, "title", "Untitled task");
|
|
6727
|
+
const source = stringField(task, "source", stringField(raw, "source", ""));
|
|
6728
|
+
const labels = arrayField(task, "labels").length > 0 ? arrayField(task, "labels") : arrayField(raw, "labels");
|
|
6729
|
+
return { id, status, title, source, labels };
|
|
6730
|
+
});
|
|
6731
|
+
const idWidth = Math.min(18, Math.max(4, ...rows.map((row) => row.id.length)));
|
|
6732
|
+
const statusWidth = Math.min(16, Math.max(6, ...rows.map((row) => row.status.length)));
|
|
6733
|
+
const header = `${pc3.bold(pad("TASK", idWidth))} ${pc3.bold(pad("STATUS", statusWidth))} ${pc3.bold("TITLE")}`;
|
|
6734
|
+
const body = rows.map((row) => {
|
|
6735
|
+
const labels = row.labels.length > 0 ? pc3.dim(` ${row.labels.slice(0, 4).map((label) => `#${label}`).join(" ")}`) : "";
|
|
6736
|
+
const source = row.source ? pc3.dim(` ${row.source}`) : "";
|
|
6737
|
+
return [
|
|
6738
|
+
pc3.bold(pad(truncate(row.id, idWidth), idWidth)),
|
|
6739
|
+
statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
|
|
6740
|
+
`${row.title}${labels}${source}`
|
|
6741
|
+
].join(" ");
|
|
6742
|
+
});
|
|
6743
|
+
return [pc3.bold("Rig tasks"), header, ...body].join(`
|
|
6744
|
+
`);
|
|
6745
|
+
}
|
|
6746
|
+
function formatRunList(runs, options = {}) {
|
|
6747
|
+
if (runs.length === 0) {
|
|
6748
|
+
return pc3.dim(options.source === "server" ? "No runs recorded on the selected Rig server." : "No runs recorded in .rig/runs.");
|
|
6749
|
+
}
|
|
6750
|
+
const rows = runs.map((run) => {
|
|
6751
|
+
const runId = stringField(run, "runId", stringField(run, "id", "(unknown-run)"));
|
|
6752
|
+
const status = stringField(run, "status", "unknown");
|
|
6753
|
+
const taskId2 = stringField(run, "taskId", "");
|
|
6754
|
+
const title = stringField(run, "title", taskId2 || "(untitled)");
|
|
6755
|
+
const runtime = stringField(run, "runtimeAdapter", "");
|
|
6756
|
+
return { runId, status, title, runtime };
|
|
6757
|
+
});
|
|
6758
|
+
const idWidth = Math.min(36, Math.max(6, ...rows.map((row) => row.runId.length)));
|
|
6759
|
+
const statusWidth = Math.min(16, Math.max(6, ...rows.map((row) => row.status.length)));
|
|
6760
|
+
const header = `${pc3.bold(pad("RUN", idWidth))} ${pc3.bold(pad("STATUS", statusWidth))} ${pc3.bold("TITLE")}`;
|
|
6761
|
+
const body = rows.map((row) => [
|
|
6762
|
+
pc3.bold(pad(truncate(row.runId, idWidth), idWidth)),
|
|
6763
|
+
statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
|
|
6764
|
+
`${row.title}${row.runtime ? pc3.dim(` ${row.runtime}`) : ""}`
|
|
6765
|
+
].join(" "));
|
|
6766
|
+
return [pc3.bold(options.source === "server" ? "Rig runs (server)" : "Rig runs"), header, ...body].join(`
|
|
6767
|
+
`);
|
|
6768
|
+
}
|
|
6769
|
+
function formatSubmittedRun(input) {
|
|
6770
|
+
const lines = [`${pc3.green("Run submitted")}: ${pc3.bold(input.runId)}`];
|
|
6771
|
+
if (input.task) {
|
|
6772
|
+
const id = stringField(input.task, "id", "<unknown>");
|
|
6773
|
+
const status = stringField(input.task, "status", "unknown");
|
|
6774
|
+
const title = stringField(input.task, "title", "Untitled task");
|
|
6775
|
+
lines.push(`${pc3.dim("task")} ${pc3.bold(id)} ${statusColor(status)(status)} ${title}`);
|
|
6776
|
+
}
|
|
6777
|
+
return lines.join(`
|
|
6778
|
+
`);
|
|
6779
|
+
}
|
|
6780
|
+
|
|
6781
|
+
// packages/cli/src/commands/_pi-session.ts
|
|
6782
|
+
import { spawn as spawn2 } from "child_process";
|
|
6783
|
+
function buildPiRigSessionEnv(input) {
|
|
6784
|
+
return {
|
|
6785
|
+
RIG_PROJECT_ROOT: input.projectRoot,
|
|
6786
|
+
PROJECT_RIG_ROOT: input.projectRoot,
|
|
6787
|
+
RIG_RUN_ID: input.runId,
|
|
6788
|
+
RIG_SERVER_RUN_ID: input.runId,
|
|
6789
|
+
RIG_RUNTIME_ADAPTER: "pi",
|
|
6790
|
+
RIG_SERVER_URL: input.serverUrl,
|
|
6791
|
+
RIG_SERVER_BASE_URL: input.serverUrl,
|
|
6792
|
+
RIG_STEERING_POLL_MS: process.env.RIG_STEERING_POLL_MS?.trim() || "1000",
|
|
6793
|
+
RIG_PI_OPERATOR_SESSION: "1",
|
|
6794
|
+
...input.taskId ? { RIG_TASK_ID: input.taskId } : {},
|
|
6795
|
+
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {}
|
|
6796
|
+
};
|
|
6797
|
+
}
|
|
6798
|
+
function shellBinary(name) {
|
|
6799
|
+
const explicit = process.env.RIG_PI_BINARY?.trim();
|
|
6800
|
+
if (explicit)
|
|
6801
|
+
return explicit;
|
|
6802
|
+
return Bun.which(name) || name;
|
|
6803
|
+
}
|
|
6804
|
+
function buildPiRigSessionCommand(input) {
|
|
6805
|
+
const configuredExtension = input.extensionSource ?? process.env.RIG_PI_RIG_EXTENSION_SOURCE?.trim();
|
|
6806
|
+
const extensionSource = configuredExtension && configuredExtension.length > 0 ? configuredExtension : resolvePiRigPackageSource(input.projectRoot);
|
|
6807
|
+
const initialCommand = `/rig attach ${input.runId}`;
|
|
6808
|
+
return [
|
|
6809
|
+
shellBinary("pi"),
|
|
6810
|
+
"--no-extensions",
|
|
6811
|
+
"--extension",
|
|
6812
|
+
extensionSource,
|
|
6813
|
+
initialCommand
|
|
6814
|
+
];
|
|
6815
|
+
}
|
|
6816
|
+
async function launchPiRigSession(context, input) {
|
|
6817
|
+
if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
6818
|
+
return { launched: false, exitCode: null, command: [] };
|
|
6819
|
+
}
|
|
6820
|
+
if (process.env.RIG_DISABLE_PI_LAUNCH === "1") {
|
|
6821
|
+
return { launched: false, exitCode: null, command: [] };
|
|
6822
|
+
}
|
|
6823
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
6824
|
+
const command = buildPiRigSessionCommand({ ...input, projectRoot: context.projectRoot });
|
|
6825
|
+
const env = {
|
|
6826
|
+
...process.env,
|
|
6827
|
+
...buildPiRigSessionEnv({
|
|
6828
|
+
projectRoot: context.projectRoot,
|
|
6829
|
+
runId: input.runId,
|
|
6830
|
+
taskId: input.taskId,
|
|
6831
|
+
serverUrl: server.baseUrl,
|
|
6832
|
+
authToken: server.authToken
|
|
6833
|
+
})
|
|
6834
|
+
};
|
|
6835
|
+
process.stdout.write(`Launching Pi for Rig run ${input.runId}\u2026
|
|
6836
|
+
`);
|
|
6837
|
+
process.stdout.write(`Pi command: ${formatCommand(command)}
|
|
6838
|
+
`);
|
|
6839
|
+
const launchedAt = Date.now();
|
|
6840
|
+
const child = spawn2(command[0], command.slice(1), {
|
|
6841
|
+
cwd: context.projectRoot,
|
|
6842
|
+
env,
|
|
6843
|
+
stdio: "inherit"
|
|
6844
|
+
});
|
|
6845
|
+
const launchError = await new Promise((resolve20) => {
|
|
6846
|
+
child.once("error", (error) => {
|
|
6847
|
+
resolve20({ error: error.message });
|
|
6848
|
+
});
|
|
6849
|
+
child.once("close", (code) => resolve20({ code }));
|
|
6850
|
+
});
|
|
6851
|
+
if ("error" in launchError) {
|
|
6852
|
+
process.stderr.write(`Failed to launch Pi; falling back to Rig attach view: ${launchError.error}
|
|
6853
|
+
`);
|
|
6854
|
+
return { launched: false, exitCode: null, command, error: launchError.error };
|
|
6855
|
+
}
|
|
6856
|
+
const exitCode = launchError.code;
|
|
6857
|
+
const elapsedMs = Date.now() - launchedAt;
|
|
6858
|
+
if (typeof exitCode === "number" && exitCode !== 0 && elapsedMs < 5000) {
|
|
6859
|
+
const error = `Pi exited during startup with code ${exitCode}.`;
|
|
6860
|
+
process.stderr.write(`${error} Falling back to Rig attach view.
|
|
6861
|
+
`);
|
|
6862
|
+
return { launched: false, exitCode, command, error };
|
|
6863
|
+
}
|
|
6864
|
+
return { launched: true, exitCode, command };
|
|
6865
|
+
}
|
|
6866
|
+
|
|
6679
6867
|
// packages/cli/src/commands/run.ts
|
|
6680
6868
|
function normalizeRemoteRunDetails(payload) {
|
|
6681
6869
|
const run = payload.run;
|
|
@@ -6777,13 +6965,7 @@ async function executeRun(context, args) {
|
|
|
6777
6965
|
requireNoExtraArgs(rest, "bun run rig run list");
|
|
6778
6966
|
const { runs, source } = await listRunsForSelectedConnection(context, { limit: 100 });
|
|
6779
6967
|
if (context.outputMode === "text") {
|
|
6780
|
-
|
|
6781
|
-
console.log(source === "server" ? "No runs recorded on the selected Rig server." : "No runs recorded in .rig/runs.");
|
|
6782
|
-
} else {
|
|
6783
|
-
for (const run of runs) {
|
|
6784
|
-
console.log(`- ${runStringField(run, "runId", "(unknown-run)")} \xB7 ${runStringField(run, "status", "unknown")} \xB7 ${runDisplayTitle(run)}`);
|
|
6785
|
-
}
|
|
6786
|
-
}
|
|
6968
|
+
console.log(formatRunList(runs, { source }));
|
|
6787
6969
|
}
|
|
6788
6970
|
return { ok: true, group: "run", command, details: { runs, source } };
|
|
6789
6971
|
}
|
|
@@ -6905,14 +7087,26 @@ async function executeRun(context, args) {
|
|
|
6905
7087
|
if (!runId) {
|
|
6906
7088
|
throw new CliError2("run attach requires a run id.", 2);
|
|
6907
7089
|
}
|
|
7090
|
+
let steered = false;
|
|
7091
|
+
const shouldTryPiAttach = context.outputMode === "text" && follow.value && !once.value && Boolean(process.stdin.isTTY && process.stdout.isTTY) && process.env.RIG_DISABLE_PI_LAUNCH !== "1";
|
|
7092
|
+
if (shouldTryPiAttach && messageOption.value?.trim()) {
|
|
7093
|
+
await steerRunViaServer(context, runId, messageOption.value.trim());
|
|
7094
|
+
steered = true;
|
|
7095
|
+
}
|
|
7096
|
+
if (shouldTryPiAttach) {
|
|
7097
|
+
const piSession = await launchPiRigSession(context, { runId });
|
|
7098
|
+
if (piSession.launched) {
|
|
7099
|
+
return { ok: true, group: "run", command, details: { runId, steered, mode: "pi", ...piSession } };
|
|
7100
|
+
}
|
|
7101
|
+
}
|
|
6908
7102
|
const attached = await attachRunOperatorView(context, {
|
|
6909
7103
|
runId,
|
|
6910
|
-
message: messageOption.value ?? null,
|
|
7104
|
+
message: shouldTryPiAttach ? null : messageOption.value ?? null,
|
|
6911
7105
|
once: once.value,
|
|
6912
7106
|
follow: follow.value,
|
|
6913
7107
|
pollMs: parsePositiveInt(pollMs.value, "--poll-ms", 2000)
|
|
6914
7108
|
});
|
|
6915
|
-
return { ok: true, group: "run", command, details: attached };
|
|
7109
|
+
return { ok: true, group: "run", command, details: { ...attached, steered: attached.steered || steered } };
|
|
6916
7110
|
}
|
|
6917
7111
|
case "status": {
|
|
6918
7112
|
requireNoExtraArgs(rest, "bun run rig run status");
|
|
@@ -7178,8 +7372,8 @@ async function executeServer(context, args, options) {
|
|
|
7178
7372
|
// packages/cli/src/commands/task.ts
|
|
7179
7373
|
import { readFileSync as readFileSync9 } from "fs";
|
|
7180
7374
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
7181
|
-
import { createInterface as createInterface3 } from "readline/promises";
|
|
7182
7375
|
import { resolve as resolve20 } from "path";
|
|
7376
|
+
import { cancel as cancel3, confirm as confirm2, isCancel as isCancel3 } from "@clack/prompts";
|
|
7183
7377
|
import {
|
|
7184
7378
|
taskArtifactDir,
|
|
7185
7379
|
taskArtifacts,
|
|
@@ -7197,6 +7391,7 @@ import {
|
|
|
7197
7391
|
} from "@rig/runtime/control-plane/native/task-ops";
|
|
7198
7392
|
|
|
7199
7393
|
// packages/cli/src/commands/_task-picker.ts
|
|
7394
|
+
import { cancel as cancel2, isCancel as isCancel2, select as select2 } from "@clack/prompts";
|
|
7200
7395
|
function taskId2(task) {
|
|
7201
7396
|
return typeof task.id === "string" && task.id.trim() ? task.id : "<unknown>";
|
|
7202
7397
|
}
|
|
@@ -7209,20 +7404,37 @@ async function selectTaskWithTextPicker(tasks, io = {}) {
|
|
|
7209
7404
|
if (!isTty) {
|
|
7210
7405
|
throw new Error("task run requires an interactive terminal to pick a task; pass --task <id>, --next, or --detach with a task id.");
|
|
7211
7406
|
}
|
|
7212
|
-
|
|
7213
|
-
|
|
7407
|
+
if (io.prompt || io.renderer) {
|
|
7408
|
+
const prompt = io.prompt ?? promptForTaskSelection;
|
|
7409
|
+
const renderer = io.renderer ?? { writeLine: (line) => process.stdout.write(`${line}
|
|
7214
7410
|
`) };
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7411
|
+
renderer.writeLine("Select Rig task:");
|
|
7412
|
+
for (const row of renderTaskPickerRows(tasks))
|
|
7413
|
+
renderer.writeLine(` ${row}`);
|
|
7414
|
+
const answer2 = (await prompt(`Task [1-${tasks.length}] or id: `)).trim();
|
|
7415
|
+
if (!answer2)
|
|
7416
|
+
return null;
|
|
7417
|
+
if (/^\d+$/.test(answer2)) {
|
|
7418
|
+
const index2 = Number.parseInt(answer2, 10) - 1;
|
|
7419
|
+
return tasks[index2] ?? null;
|
|
7420
|
+
}
|
|
7421
|
+
return tasks.find((task) => taskId2(task) === answer2) ?? null;
|
|
7422
|
+
}
|
|
7423
|
+
const options = tasks.map((task, index2) => ({
|
|
7424
|
+
value: `${index2}`,
|
|
7425
|
+
label: `${taskId2(task)} \xB7 ${typeof task.title === "string" && task.title.trim() ? task.title.trim() : "Untitled task"}`,
|
|
7426
|
+
hint: typeof task.status === "string" && task.status.trim() ? task.status.trim() : undefined
|
|
7427
|
+
}));
|
|
7428
|
+
const answer = await select2({
|
|
7429
|
+
message: "Select Rig task",
|
|
7430
|
+
options
|
|
7431
|
+
});
|
|
7432
|
+
if (isCancel2(answer)) {
|
|
7433
|
+
cancel2("No task selected.");
|
|
7220
7434
|
return null;
|
|
7221
|
-
if (/^\d+$/.test(answer)) {
|
|
7222
|
-
const index = Number.parseInt(answer, 10) - 1;
|
|
7223
|
-
return tasks[index] ?? null;
|
|
7224
7435
|
}
|
|
7225
|
-
|
|
7436
|
+
const index = Number.parseInt(String(answer), 10);
|
|
7437
|
+
return Number.isFinite(index) ? tasks[index] ?? null : null;
|
|
7226
7438
|
}
|
|
7227
7439
|
|
|
7228
7440
|
// packages/cli/src/commands/task.ts
|
|
@@ -7334,13 +7546,15 @@ async function resolveDirtyBaselineForTaskRun(context, explicit) {
|
|
|
7334
7546
|
if (explicit)
|
|
7335
7547
|
return { mode: explicit, state };
|
|
7336
7548
|
if (context.outputMode === "text" && process.stdin.isTTY && process.stdout.isTTY) {
|
|
7337
|
-
const
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7549
|
+
const answer = await confirm2({
|
|
7550
|
+
message: "Include current uncommitted changes in run baseline?",
|
|
7551
|
+
initialValue: false
|
|
7552
|
+
});
|
|
7553
|
+
if (isCancel3(answer)) {
|
|
7554
|
+
cancel3("Run cancelled.");
|
|
7555
|
+
throw new CliError2("Run cancelled by user.", 1);
|
|
7343
7556
|
}
|
|
7557
|
+
return { mode: answer ? "dirty-snapshot" : "head", state };
|
|
7344
7558
|
}
|
|
7345
7559
|
return { mode: "head", state };
|
|
7346
7560
|
}
|
|
@@ -7375,10 +7589,7 @@ function summarizeTask(task, options = {}) {
|
|
|
7375
7589
|
};
|
|
7376
7590
|
}
|
|
7377
7591
|
function printTaskSummary(task) {
|
|
7378
|
-
|
|
7379
|
-
const title = readTaskString(task, "title") ?? "Untitled task";
|
|
7380
|
-
const status = readTaskString(task, "status") ?? "unknown";
|
|
7381
|
-
console.log(`- ${id} \xB7 ${status} \xB7 ${title}`);
|
|
7592
|
+
console.log(formatTaskList([task]));
|
|
7382
7593
|
}
|
|
7383
7594
|
async function validatorRegistryForTaskCommands(projectRoot) {
|
|
7384
7595
|
return buildPluginHostContext(projectRoot).then((ctx) => ctx?.validatorRegistry ?? undefined).catch(() => {
|
|
@@ -7396,16 +7607,8 @@ async function executeTask(context, args, options) {
|
|
|
7396
7607
|
requireNoExtraArgs(remaining, "bun run rig task list [--raw] [--assignee <login|@me>] [--assigned-to <login|me|@me>] [--state open|closed] [--status <status>] [--limit <n>]");
|
|
7397
7608
|
const tasks = await listWorkspaceTasksViaServer(context, filters);
|
|
7398
7609
|
if (context.outputMode === "text") {
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
} else {
|
|
7402
|
-
for (const task of tasks) {
|
|
7403
|
-
if (rawResult.value)
|
|
7404
|
-
console.log(JSON.stringify(summarizeTask(task, { raw: true })));
|
|
7405
|
-
else
|
|
7406
|
-
printTaskSummary(task);
|
|
7407
|
-
}
|
|
7408
|
-
}
|
|
7610
|
+
const renderedTasks = rawResult.value ? tasks.map((task) => summarizeTask(task, { raw: true })) : tasks.map((task) => summarizeTask(task));
|
|
7611
|
+
console.log(formatTaskList(renderedTasks, { raw: rawResult.value }));
|
|
7409
7612
|
}
|
|
7410
7613
|
return {
|
|
7411
7614
|
ok: true,
|
|
@@ -7624,16 +7827,23 @@ async function executeTask(context, args, options) {
|
|
|
7624
7827
|
});
|
|
7625
7828
|
let attachDetails = null;
|
|
7626
7829
|
if (!detachResult.value && context.outputMode === "text") {
|
|
7627
|
-
console.log(
|
|
7628
|
-
if (
|
|
7629
|
-
|
|
7830
|
+
console.log(formatSubmittedRun({ runId: submitted.runId, task: selectedTask ? summarizeTask(selectedTask) : null }));
|
|
7831
|
+
if (runtimeAdapter === "pi") {
|
|
7832
|
+
const piSession = await launchPiRigSession(context, {
|
|
7833
|
+
runId: submitted.runId,
|
|
7834
|
+
taskId: selectedTaskId,
|
|
7835
|
+
title: titleResult.value ?? readTaskString(selectedTask ?? {}, "title"),
|
|
7836
|
+
runtimeAdapter
|
|
7837
|
+
});
|
|
7838
|
+
attachDetails = { mode: "pi", ...piSession };
|
|
7839
|
+
if (!piSession.launched) {
|
|
7840
|
+
attachDetails = await attachRunOperatorView(context, { runId: submitted.runId, follow: true });
|
|
7841
|
+
}
|
|
7842
|
+
} else {
|
|
7843
|
+
attachDetails = await attachRunOperatorView(context, { runId: submitted.runId, follow: true });
|
|
7630
7844
|
}
|
|
7631
|
-
attachDetails = await attachRunOperatorView(context, { runId: submitted.runId, follow: true });
|
|
7632
7845
|
} else if (context.outputMode === "text") {
|
|
7633
|
-
console.log(
|
|
7634
|
-
if (selectedTask) {
|
|
7635
|
-
printTaskSummary(selectedTask);
|
|
7636
|
-
}
|
|
7846
|
+
console.log(formatSubmittedRun({ runId: submitted.runId, task: selectedTask ? summarizeTask(selectedTask) : null }));
|
|
7637
7847
|
}
|
|
7638
7848
|
return {
|
|
7639
7849
|
ok: true,
|
|
@@ -7718,7 +7928,7 @@ async function executeTask(context, args, options) {
|
|
|
7718
7928
|
// packages/cli/src/commands/task-run-driver.ts
|
|
7719
7929
|
import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync10, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
7720
7930
|
import { resolve as resolve21 } from "path";
|
|
7721
|
-
import { spawn as
|
|
7931
|
+
import { spawn as spawn3, spawnSync as spawnSync4 } from "child_process";
|
|
7722
7932
|
import { createInterface as createLineInterface } from "readline";
|
|
7723
7933
|
import { loadConfig as loadConfig2 } from "@rig/core/load-config";
|
|
7724
7934
|
import {
|
|
@@ -7946,7 +8156,7 @@ async function runCheckedCommand(command, args, cwd, label = "git") {
|
|
|
7946
8156
|
}
|
|
7947
8157
|
function createCommandRunner(binary) {
|
|
7948
8158
|
return async (args, options) => {
|
|
7949
|
-
const child =
|
|
8159
|
+
const child = spawn3(binary, [...args], {
|
|
7950
8160
|
cwd: options?.cwd,
|
|
7951
8161
|
stdio: ["ignore", "pipe", "pipe"]
|
|
7952
8162
|
});
|
|
@@ -9060,7 +9270,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9060
9270
|
}
|
|
9061
9271
|
for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
|
|
9062
9272
|
const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
|
|
9063
|
-
const child =
|
|
9273
|
+
const child = spawn3(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
|
|
9064
9274
|
cwd: context.projectRoot,
|
|
9065
9275
|
env: childEnv,
|
|
9066
9276
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -9407,7 +9617,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
9407
9617
|
});
|
|
9408
9618
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Steering Pi from PR feedback" });
|
|
9409
9619
|
const feedbackCommand = buildHostAgentCommand(message2);
|
|
9410
|
-
const child =
|
|
9620
|
+
const child = spawn3(feedbackCommand[0], feedbackCommand.slice(1), {
|
|
9411
9621
|
cwd: latestRuntimeWorkspace ?? context.projectRoot,
|
|
9412
9622
|
env: childEnv,
|
|
9413
9623
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli/src/commands/_cli-format.ts
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
function stringField(record, key, fallback = "") {
|
|
5
|
+
const value = record[key];
|
|
6
|
+
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
7
|
+
}
|
|
8
|
+
function arrayField(record, key) {
|
|
9
|
+
const value = record[key];
|
|
10
|
+
return Array.isArray(value) ? value.flatMap((entry) => typeof entry === "string" && entry.trim() ? [entry.trim()] : []) : [];
|
|
11
|
+
}
|
|
12
|
+
function rawObject(record) {
|
|
13
|
+
const raw = record.raw;
|
|
14
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
15
|
+
}
|
|
16
|
+
function truncate(value, width) {
|
|
17
|
+
if (value.length <= width)
|
|
18
|
+
return value;
|
|
19
|
+
if (width <= 1)
|
|
20
|
+
return "\u2026";
|
|
21
|
+
return `${value.slice(0, width - 1)}\u2026`;
|
|
22
|
+
}
|
|
23
|
+
function pad(value, width) {
|
|
24
|
+
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
25
|
+
}
|
|
26
|
+
function statusColor(status) {
|
|
27
|
+
const normalized = status.toLowerCase();
|
|
28
|
+
if (["completed", "merged", "closed", "done", "accepted"].includes(normalized))
|
|
29
|
+
return pc.green;
|
|
30
|
+
if (["failed", "needs_attention", "needs-attention", "blocked"].includes(normalized))
|
|
31
|
+
return pc.red;
|
|
32
|
+
if (["running", "reviewing", "validating", "in_progress", "in-progress"].includes(normalized))
|
|
33
|
+
return pc.cyan;
|
|
34
|
+
if (["ready", "open", "queued", "created", "preparing"].includes(normalized))
|
|
35
|
+
return pc.yellow;
|
|
36
|
+
return pc.dim;
|
|
37
|
+
}
|
|
38
|
+
function formatTaskList(tasks, options = {}) {
|
|
39
|
+
if (tasks.length === 0)
|
|
40
|
+
return pc.dim("No matching tasks.");
|
|
41
|
+
if (options.raw)
|
|
42
|
+
return tasks.map((task) => JSON.stringify(task)).join(`
|
|
43
|
+
`);
|
|
44
|
+
const rows = tasks.map((task) => {
|
|
45
|
+
const raw = rawObject(task);
|
|
46
|
+
const id = stringField(task, "id", "<unknown>");
|
|
47
|
+
const status = stringField(task, "status", "unknown");
|
|
48
|
+
const title = stringField(task, "title", "Untitled task");
|
|
49
|
+
const source = stringField(task, "source", stringField(raw, "source", ""));
|
|
50
|
+
const labels = arrayField(task, "labels").length > 0 ? arrayField(task, "labels") : arrayField(raw, "labels");
|
|
51
|
+
return { id, status, title, source, labels };
|
|
52
|
+
});
|
|
53
|
+
const idWidth = Math.min(18, Math.max(4, ...rows.map((row) => row.id.length)));
|
|
54
|
+
const statusWidth = Math.min(16, Math.max(6, ...rows.map((row) => row.status.length)));
|
|
55
|
+
const header = `${pc.bold(pad("TASK", idWidth))} ${pc.bold(pad("STATUS", statusWidth))} ${pc.bold("TITLE")}`;
|
|
56
|
+
const body = rows.map((row) => {
|
|
57
|
+
const labels = row.labels.length > 0 ? pc.dim(` ${row.labels.slice(0, 4).map((label) => `#${label}`).join(" ")}`) : "";
|
|
58
|
+
const source = row.source ? pc.dim(` ${row.source}`) : "";
|
|
59
|
+
return [
|
|
60
|
+
pc.bold(pad(truncate(row.id, idWidth), idWidth)),
|
|
61
|
+
statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
|
|
62
|
+
`${row.title}${labels}${source}`
|
|
63
|
+
].join(" ");
|
|
64
|
+
});
|
|
65
|
+
return [pc.bold("Rig tasks"), header, ...body].join(`
|
|
66
|
+
`);
|
|
67
|
+
}
|
|
68
|
+
function formatRunList(runs, options = {}) {
|
|
69
|
+
if (runs.length === 0) {
|
|
70
|
+
return pc.dim(options.source === "server" ? "No runs recorded on the selected Rig server." : "No runs recorded in .rig/runs.");
|
|
71
|
+
}
|
|
72
|
+
const rows = runs.map((run) => {
|
|
73
|
+
const runId = stringField(run, "runId", stringField(run, "id", "(unknown-run)"));
|
|
74
|
+
const status = stringField(run, "status", "unknown");
|
|
75
|
+
const taskId = stringField(run, "taskId", "");
|
|
76
|
+
const title = stringField(run, "title", taskId || "(untitled)");
|
|
77
|
+
const runtime = stringField(run, "runtimeAdapter", "");
|
|
78
|
+
return { runId, status, title, runtime };
|
|
79
|
+
});
|
|
80
|
+
const idWidth = Math.min(36, Math.max(6, ...rows.map((row) => row.runId.length)));
|
|
81
|
+
const statusWidth = Math.min(16, Math.max(6, ...rows.map((row) => row.status.length)));
|
|
82
|
+
const header = `${pc.bold(pad("RUN", idWidth))} ${pc.bold(pad("STATUS", statusWidth))} ${pc.bold("TITLE")}`;
|
|
83
|
+
const body = rows.map((row) => [
|
|
84
|
+
pc.bold(pad(truncate(row.runId, idWidth), idWidth)),
|
|
85
|
+
statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
|
|
86
|
+
`${row.title}${row.runtime ? pc.dim(` ${row.runtime}`) : ""}`
|
|
87
|
+
].join(" "));
|
|
88
|
+
return [pc.bold(options.source === "server" ? "Rig runs (server)" : "Rig runs"), header, ...body].join(`
|
|
89
|
+
`);
|
|
90
|
+
}
|
|
91
|
+
function formatSubmittedRun(input) {
|
|
92
|
+
const lines = [`${pc.green("Run submitted")}: ${pc.bold(input.runId)}`];
|
|
93
|
+
if (input.task) {
|
|
94
|
+
const id = stringField(input.task, "id", "<unknown>");
|
|
95
|
+
const status = stringField(input.task, "status", "unknown");
|
|
96
|
+
const title = stringField(input.task, "title", "Untitled task");
|
|
97
|
+
lines.push(`${pc.dim("task")} ${pc.bold(id)} ${statusColor(status)(status)} ${title}`);
|
|
98
|
+
}
|
|
99
|
+
return lines.join(`
|
|
100
|
+
`);
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
formatTaskList,
|
|
104
|
+
formatSubmittedRun,
|
|
105
|
+
formatRunList
|
|
106
|
+
};
|
|
@@ -209,7 +209,8 @@ async function loadRigConfigOrNull(projectRoot) {
|
|
|
209
209
|
import { existsSync as existsSync3, readFileSync as readFileSync3, rmSync } from "fs";
|
|
210
210
|
import { homedir as homedir2 } from "os";
|
|
211
211
|
import { resolve as resolve3 } from "path";
|
|
212
|
-
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
212
|
+
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
213
|
+
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
213
214
|
async function defaultCommandRunner(command, options = {}) {
|
|
214
215
|
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
215
216
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
@@ -228,7 +229,7 @@ function resolvePiHomeDir(inputHomeDir) {
|
|
|
228
229
|
function piListContainsPiRig(output) {
|
|
229
230
|
return output.split(/\r?\n/).some((line) => {
|
|
230
231
|
const normalized = line.trim();
|
|
231
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
232
|
+
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
232
233
|
});
|
|
233
234
|
}
|
|
234
235
|
async function safeRun(runner, command, options) {
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import { existsSync, readFileSync, rmSync } from "fs";
|
|
4
4
|
import { homedir } from "os";
|
|
5
5
|
import { resolve } from "path";
|
|
6
|
-
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
6
|
+
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
7
|
+
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
7
8
|
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
8
9
|
export { default } from '@rig/pi-rig';
|
|
9
10
|
`;
|
|
@@ -31,7 +32,7 @@ function resolvePiHomeDir(inputHomeDir) {
|
|
|
31
32
|
function piListContainsPiRig(output) {
|
|
32
33
|
return output.split(/\r?\n/).some((line) => {
|
|
33
34
|
const normalized = line.trim();
|
|
34
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
35
|
+
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
35
36
|
});
|
|
36
37
|
}
|
|
37
38
|
async function safeRun(runner, command, options) {
|
|
@@ -147,7 +148,7 @@ async function ensureRemotePiRigInstalled(input) {
|
|
|
147
148
|
const payload = await input.requestJson("/api/pi-rig/install", {
|
|
148
149
|
method: "POST",
|
|
149
150
|
headers: { "content-type": "application/json" },
|
|
150
|
-
body: JSON.stringify({ package:
|
|
151
|
+
body: JSON.stringify({ package: PI_RIG_PACKAGE_NAME, scope: "global" })
|
|
151
152
|
});
|
|
152
153
|
const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
153
154
|
const piOk = record.piOk === true || record.ok === true;
|