@h-rig/cli 0.0.6-alpha.67 → 0.0.6-alpha.69
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 -75
- package/dist/src/commands/_async-ui.js +152 -0
- package/dist/src/commands/_doctor-checks.js +29 -0
- package/dist/src/commands/_operator-view.js +154 -4
- package/dist/src/commands/_pi-frontend.js +24 -1
- package/dist/src/commands/_preflight.js +18 -0
- package/dist/src/commands/_server-client.js +46 -0
- package/dist/src/commands/_snapshot-upload.js +18 -0
- package/dist/src/commands/_spinner.js +4 -2
- package/dist/src/commands/agent.js +11 -0
- package/dist/src/commands/doctor.js +156 -1
- package/dist/src/commands/github.js +148 -3
- package/dist/src/commands/inbox.js +150 -6
- package/dist/src/commands/init.js +29 -0
- package/dist/src/commands/inspect.js +158 -8
- package/dist/src/commands/queue.js +11 -0
- package/dist/src/commands/run.js +207 -35
- package/dist/src/commands/server.js +18 -0
- package/dist/src/commands/setup.js +161 -6
- package/dist/src/commands/stats.js +167 -22
- package/dist/src/commands/task-run-driver.js +18 -0
- package/dist/src/commands/task.js +197 -47
- package/dist/src/commands.js +266 -75
- package/dist/src/index.js +266 -75
- package/package.json +8 -8
package/dist/src/commands.js
CHANGED
|
@@ -2740,6 +2740,15 @@ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
|
2740
2740
|
import { resolve as resolve10 } from "path";
|
|
2741
2741
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
2742
2742
|
var scopedGitHubBearerTokens = new Map;
|
|
2743
|
+
var serverPhaseListener = null;
|
|
2744
|
+
function setServerPhaseListener(listener) {
|
|
2745
|
+
const previous = serverPhaseListener;
|
|
2746
|
+
serverPhaseListener = listener;
|
|
2747
|
+
return previous;
|
|
2748
|
+
}
|
|
2749
|
+
function reportServerPhase(label) {
|
|
2750
|
+
serverPhaseListener?.(label);
|
|
2751
|
+
}
|
|
2743
2752
|
function cleanToken(value) {
|
|
2744
2753
|
const trimmed = value?.trim();
|
|
2745
2754
|
return trimmed ? trimmed : null;
|
|
@@ -2786,6 +2795,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2786
2795
|
try {
|
|
2787
2796
|
const selected = resolveSelectedConnection(projectRoot);
|
|
2788
2797
|
if (selected?.connection.kind === "remote") {
|
|
2798
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
2789
2799
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
2790
2800
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
2791
2801
|
return {
|
|
@@ -2795,6 +2805,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2795
2805
|
serverProjectRoot
|
|
2796
2806
|
};
|
|
2797
2807
|
}
|
|
2808
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
2798
2809
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
2799
2810
|
return {
|
|
2800
2811
|
baseUrl: connection.baseUrl,
|
|
@@ -2911,6 +2922,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
2911
2922
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
2912
2923
|
if (server.serverProjectRoot)
|
|
2913
2924
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
2925
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
2914
2926
|
let response;
|
|
2915
2927
|
try {
|
|
2916
2928
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -3088,6 +3100,38 @@ async function updateWorkspaceTaskViaServer(context, input) {
|
|
|
3088
3100
|
});
|
|
3089
3101
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
3090
3102
|
}
|
|
3103
|
+
var RESUMABLE_RUN_STATUSES = new Set([
|
|
3104
|
+
"created",
|
|
3105
|
+
"preparing",
|
|
3106
|
+
"running",
|
|
3107
|
+
"validating",
|
|
3108
|
+
"reviewing",
|
|
3109
|
+
"stopped",
|
|
3110
|
+
"failed",
|
|
3111
|
+
"needs-attention",
|
|
3112
|
+
"needs_attention"
|
|
3113
|
+
]);
|
|
3114
|
+
async function resumeRunViaServer(context, runId, options) {
|
|
3115
|
+
let targetRunId = runId?.trim() || null;
|
|
3116
|
+
if (!targetRunId) {
|
|
3117
|
+
const candidates = (await listRunsViaServer(context)).filter((run) => RESUMABLE_RUN_STATUSES.has(String(run.status ?? ""))).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")));
|
|
3118
|
+
targetRunId = typeof candidates[0]?.runId === "string" ? candidates[0].runId : null;
|
|
3119
|
+
}
|
|
3120
|
+
if (!targetRunId) {
|
|
3121
|
+
throw new CliError(options.restart ? "No run is available to restart." : "No resumable run is available.", 2, { hint: "List runs with `rig run list`, then pass an explicit id: `rig run resume <run-id>`." });
|
|
3122
|
+
}
|
|
3123
|
+
const payload = await requestServerJson(context, "/api/runs/resume", {
|
|
3124
|
+
method: "POST",
|
|
3125
|
+
headers: { "content-type": "application/json" },
|
|
3126
|
+
body: JSON.stringify({ runId: targetRunId, createdAt: new Date().toISOString(), restart: options.restart })
|
|
3127
|
+
});
|
|
3128
|
+
const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
3129
|
+
if (record.ok === false) {
|
|
3130
|
+
const message = typeof record.error === "string" && record.error.trim() ? record.error : "run resume failed";
|
|
3131
|
+
throw new CliError(`${options.restart ? "restart" : "resume"} failed for ${targetRunId}: ${message}`, 1);
|
|
3132
|
+
}
|
|
3133
|
+
return { ok: true, runId: targetRunId, ...record };
|
|
3134
|
+
}
|
|
3091
3135
|
async function stopRunViaServer(context, runId) {
|
|
3092
3136
|
const payload = await requestServerJson(context, "/api/runs/stop", {
|
|
3093
3137
|
method: "POST",
|
|
@@ -4431,6 +4475,127 @@ function formatConnectionStatus(selected, connections) {
|
|
|
4431
4475
|
`);
|
|
4432
4476
|
}
|
|
4433
4477
|
|
|
4478
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
4479
|
+
import pc4 from "picocolors";
|
|
4480
|
+
|
|
4481
|
+
// packages/cli/src/commands/_spinner.ts
|
|
4482
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
4483
|
+
function createTtySpinner(input) {
|
|
4484
|
+
const output = input.output ?? process.stdout;
|
|
4485
|
+
const isTty = output.isTTY === true;
|
|
4486
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
4487
|
+
let label = input.label;
|
|
4488
|
+
let frame = 0;
|
|
4489
|
+
let paused = false;
|
|
4490
|
+
let stopped = false;
|
|
4491
|
+
let lastPrintedLabel = "";
|
|
4492
|
+
const render = () => {
|
|
4493
|
+
if (stopped || paused)
|
|
4494
|
+
return;
|
|
4495
|
+
if (!isTty) {
|
|
4496
|
+
if (label !== lastPrintedLabel) {
|
|
4497
|
+
output.write(`${label}
|
|
4498
|
+
`);
|
|
4499
|
+
lastPrintedLabel = label;
|
|
4500
|
+
}
|
|
4501
|
+
return;
|
|
4502
|
+
}
|
|
4503
|
+
frame = (frame + 1) % frames.length;
|
|
4504
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
4505
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
4506
|
+
};
|
|
4507
|
+
const clearLine = () => {
|
|
4508
|
+
if (isTty)
|
|
4509
|
+
output.write("\r\x1B[2K");
|
|
4510
|
+
};
|
|
4511
|
+
render();
|
|
4512
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
4513
|
+
return {
|
|
4514
|
+
setLabel(next) {
|
|
4515
|
+
label = next;
|
|
4516
|
+
render();
|
|
4517
|
+
},
|
|
4518
|
+
pause() {
|
|
4519
|
+
paused = true;
|
|
4520
|
+
clearLine();
|
|
4521
|
+
},
|
|
4522
|
+
resume() {
|
|
4523
|
+
if (stopped)
|
|
4524
|
+
return;
|
|
4525
|
+
paused = false;
|
|
4526
|
+
render();
|
|
4527
|
+
},
|
|
4528
|
+
stop(finalLine) {
|
|
4529
|
+
if (stopped)
|
|
4530
|
+
return;
|
|
4531
|
+
stopped = true;
|
|
4532
|
+
if (timer)
|
|
4533
|
+
clearInterval(timer);
|
|
4534
|
+
clearLine();
|
|
4535
|
+
if (finalLine)
|
|
4536
|
+
output.write(`${finalLine}
|
|
4537
|
+
`);
|
|
4538
|
+
}
|
|
4539
|
+
};
|
|
4540
|
+
}
|
|
4541
|
+
|
|
4542
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
4543
|
+
var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
4544
|
+
var DONE_SYMBOL = pc4.green("\u25C7");
|
|
4545
|
+
var FAIL_SYMBOL = pc4.red("\u25A0");
|
|
4546
|
+
var activeUpdate = null;
|
|
4547
|
+
async function withSpinner(label, work, options = {}) {
|
|
4548
|
+
if (options.outputMode === "json") {
|
|
4549
|
+
return work(() => {});
|
|
4550
|
+
}
|
|
4551
|
+
if (activeUpdate) {
|
|
4552
|
+
const outer = activeUpdate;
|
|
4553
|
+
outer(label);
|
|
4554
|
+
return work(outer);
|
|
4555
|
+
}
|
|
4556
|
+
const output = options.output ?? process.stderr;
|
|
4557
|
+
const isTty = output.isTTY === true;
|
|
4558
|
+
let lastLabel = label;
|
|
4559
|
+
if (!isTty) {
|
|
4560
|
+
output.write(`${label}
|
|
4561
|
+
`);
|
|
4562
|
+
const update2 = (next) => {
|
|
4563
|
+
lastLabel = next;
|
|
4564
|
+
};
|
|
4565
|
+
activeUpdate = update2;
|
|
4566
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
4567
|
+
try {
|
|
4568
|
+
return await work(update2);
|
|
4569
|
+
} finally {
|
|
4570
|
+
activeUpdate = null;
|
|
4571
|
+
setServerPhaseListener(previousListener2);
|
|
4572
|
+
}
|
|
4573
|
+
}
|
|
4574
|
+
const spinner2 = createTtySpinner({
|
|
4575
|
+
label,
|
|
4576
|
+
output,
|
|
4577
|
+
frames: CLACK_SPINNER_FRAMES,
|
|
4578
|
+
styleFrame: (frame) => pc4.magenta(frame)
|
|
4579
|
+
});
|
|
4580
|
+
const update = (next) => {
|
|
4581
|
+
lastLabel = next;
|
|
4582
|
+
spinner2.setLabel(next);
|
|
4583
|
+
};
|
|
4584
|
+
activeUpdate = update;
|
|
4585
|
+
const previousListener = setServerPhaseListener(update);
|
|
4586
|
+
try {
|
|
4587
|
+
const result = await work(update);
|
|
4588
|
+
spinner2.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
4589
|
+
return result;
|
|
4590
|
+
} catch (error) {
|
|
4591
|
+
spinner2.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
4592
|
+
throw error;
|
|
4593
|
+
} finally {
|
|
4594
|
+
activeUpdate = null;
|
|
4595
|
+
setServerPhaseListener(previousListener);
|
|
4596
|
+
}
|
|
4597
|
+
}
|
|
4598
|
+
|
|
4434
4599
|
// packages/cli/src/commands/inbox.ts
|
|
4435
4600
|
async function listInboxRecords(context, kind, filters) {
|
|
4436
4601
|
const params = new URLSearchParams;
|
|
@@ -4535,7 +4700,7 @@ async function executeInbox(context, args) {
|
|
|
4535
4700
|
const task = takeOption(pending, "--task");
|
|
4536
4701
|
pending = task.rest;
|
|
4537
4702
|
requireNoExtraArgs(pending, "rig inbox approvals [--run <id>] [--task <id>]");
|
|
4538
|
-
const approvals = await listInboxRecords(context, "approvals", { run: run.value, task: task.value });
|
|
4703
|
+
const approvals = await withSpinner("Reading approvals from server\u2026", () => listInboxRecords(context, "approvals", { run: run.value, task: task.value }), { outputMode: context.outputMode });
|
|
4539
4704
|
renderList(context, "approvals", approvals);
|
|
4540
4705
|
return { ok: true, group: "inbox", command, details: { approvals } };
|
|
4541
4706
|
}
|
|
@@ -4546,7 +4711,7 @@ async function executeInbox(context, args) {
|
|
|
4546
4711
|
const task = takeOption(pending, "--task");
|
|
4547
4712
|
pending = task.rest;
|
|
4548
4713
|
requireNoExtraArgs(pending, "rig inbox inputs [--run <id>] [--task <id>]");
|
|
4549
|
-
const requests = await listInboxRecords(context, "inputs", { run: run.value, task: task.value });
|
|
4714
|
+
const requests = await withSpinner("Reading input requests from server\u2026", () => listInboxRecords(context, "inputs", { run: run.value, task: task.value }), { outputMode: context.outputMode });
|
|
4550
4715
|
renderList(context, "inputs", requests);
|
|
4551
4716
|
return { ok: true, group: "inbox", command, details: { requests } };
|
|
4552
4717
|
}
|
|
@@ -4567,7 +4732,7 @@ async function executeInbox(context, args) {
|
|
|
4567
4732
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
4568
4733
|
throw new CliError("decision must be approve or reject.", 1, { hint: "Re-run as `rig inbox approve --run <id> --request <id> --decision approve|reject`." });
|
|
4569
4734
|
}
|
|
4570
|
-
const result = await requestServerJson(context, "/api/inbox/approvals/resolve", {
|
|
4735
|
+
const result = await withSpinner(`Resolving approval ${request.value}\u2026`, () => requestServerJson(context, "/api/inbox/approvals/resolve", {
|
|
4571
4736
|
method: "POST",
|
|
4572
4737
|
headers: { "content-type": "application/json" },
|
|
4573
4738
|
body: JSON.stringify({
|
|
@@ -4576,7 +4741,7 @@ async function executeInbox(context, args) {
|
|
|
4576
4741
|
decision: decision.value,
|
|
4577
4742
|
note: note4.value ?? null
|
|
4578
4743
|
})
|
|
4579
|
-
});
|
|
4744
|
+
}), { outputMode: context.outputMode });
|
|
4580
4745
|
return { ok: true, group: "inbox", command, details: { result } };
|
|
4581
4746
|
}
|
|
4582
4747
|
case "respond": {
|
|
@@ -4610,7 +4775,7 @@ async function executeInbox(context, args) {
|
|
|
4610
4775
|
const [key, ...restValue] = entry.split("=");
|
|
4611
4776
|
return [key, restValue.join("=")];
|
|
4612
4777
|
}));
|
|
4613
|
-
const result = await requestServerJson(context, "/api/inbox/inputs/respond", {
|
|
4778
|
+
const result = await withSpinner(`Sending response for ${request.value}\u2026`, () => requestServerJson(context, "/api/inbox/inputs/respond", {
|
|
4614
4779
|
method: "POST",
|
|
4615
4780
|
headers: { "content-type": "application/json" },
|
|
4616
4781
|
body: JSON.stringify({
|
|
@@ -4618,7 +4783,7 @@ async function executeInbox(context, args) {
|
|
|
4618
4783
|
requestId: request.value,
|
|
4619
4784
|
answers: parsedAnswers
|
|
4620
4785
|
})
|
|
4621
|
-
});
|
|
4786
|
+
}), { outputMode: context.outputMode });
|
|
4622
4787
|
return { ok: true, group: "inbox", command, details: { result } };
|
|
4623
4788
|
}
|
|
4624
4789
|
case "watch": {
|
|
@@ -5035,7 +5200,10 @@ async function runRigDoctorChecks(options) {
|
|
|
5035
5200
|
const bunVersion = options.bunVersion ?? Bun.version;
|
|
5036
5201
|
const request = options.requestJson ?? ((pathname, init) => requestServerJson({ projectRoot }, pathname, init));
|
|
5037
5202
|
const loadConfig = options.loadConfig ?? loadRigConfigOrNull;
|
|
5203
|
+
const progress = options.onProgress ?? (() => {});
|
|
5204
|
+
progress("Checking local toolchain\u2026");
|
|
5038
5205
|
checks.push(check("bun", `bun >= ${MIN_SUPPORTED_BUN_VERSION}`, isSupportedBunVersion(bunVersion) ? "pass" : "fail", `found ${bunVersion}`, `Install Bun ${MIN_SUPPORTED_BUN_VERSION} or newer.`), check("git", "git", which("git") ? "pass" : "fail", which("git") ?? undefined, "Install git and ensure it is on PATH."), check("jq", "jq", which("jq") ? "pass" : "warn", which("jq") ?? undefined, "Install jq (for example `brew install jq`)."));
|
|
5206
|
+
progress("Loading rig.config\u2026");
|
|
5039
5207
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
5040
5208
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
5041
5209
|
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync10(resolve16(projectRoot, name)));
|
|
@@ -5054,6 +5222,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5054
5222
|
checks.push(selected ? check("connection", "selected server connection", "pass", selected.connection.kind === "remote" ? selected.connection.baseUrl : "local auto") : check("connection", "selected server", repo ? "fail" : "warn", repo ? "selected alias is missing" : "will auto-start local server", repo ? "Run `rig server list` and `rig server use <alias|local>`." : undefined));
|
|
5055
5223
|
let server = null;
|
|
5056
5224
|
try {
|
|
5225
|
+
progress("Connecting to the selected Rig server\u2026");
|
|
5057
5226
|
server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);
|
|
5058
5227
|
checks.push(check("server", "Rig server reachable", "pass", `${server.connectionKind} ${server.baseUrl}`));
|
|
5059
5228
|
} catch (error) {
|
|
@@ -5061,18 +5230,21 @@ async function runRigDoctorChecks(options) {
|
|
|
5061
5230
|
}
|
|
5062
5231
|
if (server || options.requestJson) {
|
|
5063
5232
|
try {
|
|
5233
|
+
progress("Checking server status\u2026");
|
|
5064
5234
|
const status = await request("/api/server/status");
|
|
5065
5235
|
checks.push(check("server-status", "server project status", "pass", JSON.stringify(status).slice(0, 180)));
|
|
5066
5236
|
} catch (error) {
|
|
5067
5237
|
checks.push(check("server-status", "server project status", "fail", errorMessage(error), "Run `rig doctor` after the selected server is reachable."));
|
|
5068
5238
|
}
|
|
5069
5239
|
try {
|
|
5240
|
+
progress("Checking GitHub auth\u2026");
|
|
5070
5241
|
const auth = await request("/api/github/auth/status");
|
|
5071
5242
|
checks.push(isAuthenticated2(auth) ? check("github-auth", "GitHub auth", "pass") : check("github-auth", "GitHub auth", "fail", "not authenticated", "Run `rig github auth import-gh` or `rig github auth token --token <token>`."));
|
|
5072
5243
|
} catch (error) {
|
|
5073
5244
|
checks.push(check("github-auth", "GitHub auth", "fail", errorMessage(error), "Authenticate GitHub through Rig and ensure the server exposes auth status."));
|
|
5074
5245
|
}
|
|
5075
5246
|
try {
|
|
5247
|
+
progress("Checking GitHub repo permissions\u2026");
|
|
5076
5248
|
const permissions = await request("/api/github/repo/permissions");
|
|
5077
5249
|
const allowed = permissionAllowsPr2(permissions);
|
|
5078
5250
|
checks.push(allowed === true ? check("github-repo-permissions", "GitHub repo PR permissions", "pass", JSON.stringify(permissions).slice(0, 180)) : allowed === false ? check("github-repo-permissions", "GitHub repo PR permissions", "fail", JSON.stringify(permissions).slice(0, 180), "Grant the selected GitHub token permission to push branches, open PRs, and merge according to repo rules.") : check("github-repo-permissions", "GitHub repo PR permissions", "warn", JSON.stringify(permissions).slice(0, 180), "Confirm the selected token can push branches and open PRs."));
|
|
@@ -5080,6 +5252,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5080
5252
|
checks.push(check("github-repo-permissions", "GitHub repo PR permissions", "warn", errorMessage(error), "Ensure the server exposes repo permission checks and the token can open PRs."));
|
|
5081
5253
|
}
|
|
5082
5254
|
try {
|
|
5255
|
+
progress("Checking GitHub issue labels\u2026");
|
|
5083
5256
|
const labels = await request("/api/workspace/task-labels");
|
|
5084
5257
|
const ready = labelsReady(labels);
|
|
5085
5258
|
checks.push(ready === false ? check("task-labels", "GitHub issue labels", "fail", JSON.stringify(labels).slice(0, 180), "Let Rig create required labels or create the configured lifecycle labels manually.") : check("task-labels", "GitHub issue labels", ready === true ? "pass" : "warn", JSON.stringify(labels).slice(0, 180), "Confirm required Rig lifecycle labels exist."));
|
|
@@ -5087,6 +5260,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5087
5260
|
checks.push(check("task-labels", "GitHub issue labels", "warn", errorMessage(error), "Run `rig init`/`rig doctor` after label setup is wired on the server."));
|
|
5088
5261
|
}
|
|
5089
5262
|
try {
|
|
5263
|
+
progress("Checking task projection\u2026");
|
|
5090
5264
|
const projection = await request("/api/workspace/task-projection");
|
|
5091
5265
|
checks.push(check("task-projection", "task projection", "pass", JSON.stringify(projection).slice(0, 180)));
|
|
5092
5266
|
} catch (error) {
|
|
@@ -5095,6 +5269,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5095
5269
|
const slug = projectStatusSlug(projectRoot, config);
|
|
5096
5270
|
if (slug) {
|
|
5097
5271
|
try {
|
|
5272
|
+
progress("Checking server project checkout\u2026");
|
|
5098
5273
|
const project = await request(`/api/projects/${encodeURIComponent(slug)}`);
|
|
5099
5274
|
checks.push(check("remote-checkout", "server project checkout", "pass", JSON.stringify(project).slice(0, 180)));
|
|
5100
5275
|
} catch (error) {
|
|
@@ -5109,6 +5284,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5109
5284
|
}
|
|
5110
5285
|
checks.push(githubProjectsCheck(config));
|
|
5111
5286
|
checks.push(prMergeCheck(config));
|
|
5287
|
+
progress("Checking Pi installation\u2026");
|
|
5112
5288
|
const piChecks = await (options.piChecks ?? (() => buildPiSetupChecks()))().catch((error) => [{
|
|
5113
5289
|
ok: false,
|
|
5114
5290
|
label: "pi/pi-rig checks",
|
|
@@ -6031,7 +6207,7 @@ async function executeGithub(context, args) {
|
|
|
6031
6207
|
case "status": {
|
|
6032
6208
|
if (rest.length > 0)
|
|
6033
6209
|
throw new CliError("Usage: rig github auth status", 1);
|
|
6034
|
-
const status = await getGitHubAuthStatusViaServer(context);
|
|
6210
|
+
const status = await withSpinner("Checking GitHub auth on the server\u2026", () => getGitHubAuthStatusViaServer(context), { outputMode: context.outputMode });
|
|
6035
6211
|
printPayload(context, status, `GitHub auth: ${status.authenticated ? "authenticated" : "unauthenticated"}`);
|
|
6036
6212
|
return { ok: true, group: "github", command: "auth status", details: status };
|
|
6037
6213
|
}
|
|
@@ -6042,14 +6218,15 @@ async function executeGithub(context, args) {
|
|
|
6042
6218
|
const token = parsed.value?.trim();
|
|
6043
6219
|
if (!token)
|
|
6044
6220
|
throw new CliError("Missing --token value.", 1, { hint: "Re-run as `rig github auth token --token <token>`." });
|
|
6045
|
-
const result = await postGitHubTokenViaServer(context, token);
|
|
6221
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, token), { outputMode: context.outputMode });
|
|
6046
6222
|
printPayload(context, result, "GitHub token stored on the selected Rig server.");
|
|
6047
6223
|
return { ok: true, group: "github", command: "auth token", details: result };
|
|
6048
6224
|
}
|
|
6049
6225
|
case "import-gh": {
|
|
6050
6226
|
if (rest.length > 0)
|
|
6051
6227
|
throw new CliError("Usage: rig github auth import-gh", 1);
|
|
6052
|
-
const
|
|
6228
|
+
const importedToken = readGhToken();
|
|
6229
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, importedToken), { outputMode: context.outputMode });
|
|
6053
6230
|
printPayload(context, result, "GitHub token imported from gh and stored on the selected Rig server.");
|
|
6054
6231
|
return { ok: true, group: "github", command: "auth import-gh", details: result };
|
|
6055
6232
|
}
|
|
@@ -6062,7 +6239,7 @@ async function executeGithub(context, args) {
|
|
|
6062
6239
|
init_runner();
|
|
6063
6240
|
async function executeDoctor(context, args) {
|
|
6064
6241
|
requireNoExtraArgs(args, "rig doctor");
|
|
6065
|
-
const checks = await runRigDoctorChecks({ projectRoot: context.projectRoot });
|
|
6242
|
+
const checks = await withSpinner("Running doctor checks\u2026", (update) => runRigDoctorChecks({ projectRoot: context.projectRoot, onProgress: update }), { outputMode: context.outputMode });
|
|
6066
6243
|
if (context.outputMode === "text") {
|
|
6067
6244
|
console.log(formatDoctorChecks(checks));
|
|
6068
6245
|
}
|
|
@@ -6369,13 +6546,19 @@ async function executeInspect(context, args) {
|
|
|
6369
6546
|
const requiredTask = requireTask(task, "rig inspect logs --task <task-id>");
|
|
6370
6547
|
const latestRun = listAuthorityRuns(context.projectRoot).map((entry) => readAuthorityRun3(context.projectRoot, entry.runId)).filter((run) => Boolean(run)).filter((run) => run.taskId === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
|
|
6371
6548
|
if (!latestRun) {
|
|
6372
|
-
const
|
|
6373
|
-
|
|
6374
|
-
|
|
6549
|
+
const fallback = await withSpinner(`Finding runs for ${requiredTask} on the server\u2026`, async (update) => {
|
|
6550
|
+
const serverRuns = await listRunsViaServer(context, { limit: 200 }).catch(() => []);
|
|
6551
|
+
const serverRun = serverRuns.filter((run) => String(run.taskId ?? "") === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
|
|
6552
|
+
if (!serverRun || typeof serverRun.runId !== "string")
|
|
6553
|
+
return null;
|
|
6554
|
+
update(`Reading logs for run ${serverRun.runId}\u2026`);
|
|
6555
|
+
const page = await getRunLogsViaServer(context, serverRun.runId, { limit: 500 });
|
|
6556
|
+
return { runId: serverRun.runId, page };
|
|
6557
|
+
}, { outputMode: context.outputMode });
|
|
6558
|
+
if (!fallback) {
|
|
6375
6559
|
throw new CliError(`No runs found for ${requiredTask} (local or on the selected server).`, 1, { hint: "Start one with `rig task run --task <id>`, or check `rig run list`." });
|
|
6376
6560
|
}
|
|
6377
|
-
const
|
|
6378
|
-
const entries = Array.isArray(page.entries) ? page.entries : [];
|
|
6561
|
+
const entries = Array.isArray(fallback.page.entries) ? fallback.page.entries : [];
|
|
6379
6562
|
if (context.outputMode === "text") {
|
|
6380
6563
|
for (const entry of entries) {
|
|
6381
6564
|
const record = entry && typeof entry === "object" ? entry : {};
|
|
@@ -6384,9 +6567,9 @@ async function executeInspect(context, args) {
|
|
|
6384
6567
|
console.log([title, detail].filter(Boolean).join(" \u2014 "));
|
|
6385
6568
|
}
|
|
6386
6569
|
if (entries.length === 0)
|
|
6387
|
-
console.log(`(no log entries for run ${
|
|
6570
|
+
console.log(`(no log entries for run ${fallback.runId})`);
|
|
6388
6571
|
}
|
|
6389
|
-
return { ok: true, group: "inspect", command, details: { task: requiredTask, runId:
|
|
6572
|
+
return { ok: true, group: "inspect", command, details: { task: requiredTask, runId: fallback.runId, source: "server", entries } };
|
|
6390
6573
|
}
|
|
6391
6574
|
const logsPath = resolve19(resolveAuthorityRunDir2(context.projectRoot, latestRun.runId), "logs.jsonl");
|
|
6392
6575
|
if (!existsSync12(logsPath)) {
|
|
@@ -6444,7 +6627,7 @@ async function executeInspect(context, args) {
|
|
|
6444
6627
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
6445
6628
|
requireNoExtraArgs(remaining, "rig inspect diff [--task <task-id>]");
|
|
6446
6629
|
if (task) {
|
|
6447
|
-
const files = changedFilesForTask(context.projectRoot, task, false);
|
|
6630
|
+
const files = await withSpinner(`Computing changed files for ${task}\u2026`, async () => changedFilesForTask(context.projectRoot, task, false), { outputMode: context.outputMode });
|
|
6448
6631
|
for (const file of files) {
|
|
6449
6632
|
console.log(file);
|
|
6450
6633
|
}
|
|
@@ -7125,8 +7308,6 @@ import {
|
|
|
7125
7308
|
deleteRunState,
|
|
7126
7309
|
listOpenEpics,
|
|
7127
7310
|
resolveDefaultEpic,
|
|
7128
|
-
runResume,
|
|
7129
|
-
runRestart,
|
|
7130
7311
|
runStatus,
|
|
7131
7312
|
runStop,
|
|
7132
7313
|
startRun,
|
|
@@ -7532,7 +7713,12 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
7532
7713
|
};
|
|
7533
7714
|
let detached = false;
|
|
7534
7715
|
try {
|
|
7535
|
-
await runPiMain([
|
|
7716
|
+
await runPiMain([
|
|
7717
|
+
"--no-extensions",
|
|
7718
|
+
"--no-skills",
|
|
7719
|
+
"--no-prompt-templates",
|
|
7720
|
+
"--no-context-files"
|
|
7721
|
+
], {
|
|
7536
7722
|
extensionFactories: [piRigExtensionFactory]
|
|
7537
7723
|
});
|
|
7538
7724
|
detached = true;
|
|
@@ -7597,8 +7783,9 @@ async function readOperatorSnapshot(context, runId, options = {}) {
|
|
|
7597
7783
|
}
|
|
7598
7784
|
async function attachRunOperatorView(context, input) {
|
|
7599
7785
|
let steered = false;
|
|
7600
|
-
|
|
7601
|
-
|
|
7786
|
+
const attachMessage = input.message?.trim();
|
|
7787
|
+
if (attachMessage) {
|
|
7788
|
+
await withSpinner("Queueing steering message\u2026", () => sendRunPiPromptViaServer(context, input.runId, attachMessage, "steer").catch(() => steerRunViaServer(context, input.runId, attachMessage)), { outputMode: context.outputMode });
|
|
7602
7789
|
steered = true;
|
|
7603
7790
|
}
|
|
7604
7791
|
if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
@@ -7608,7 +7795,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
7608
7795
|
});
|
|
7609
7796
|
}
|
|
7610
7797
|
const surface = createOperatorSurface({ interactive: input.interactive !== false });
|
|
7611
|
-
let snapshot = await readOperatorSnapshot(context, input.runId);
|
|
7798
|
+
let snapshot = await withSpinner(`Connecting to run ${input.runId}\u2026`, () => readOperatorSnapshot(context, input.runId), { outputMode: context.outputMode });
|
|
7612
7799
|
if (context.outputMode === "text") {
|
|
7613
7800
|
surface.renderSnapshot(snapshot);
|
|
7614
7801
|
surface.renderTimeline(snapshot.timeline);
|
|
@@ -7739,7 +7926,7 @@ async function executeRun(context, args) {
|
|
|
7739
7926
|
switch (command) {
|
|
7740
7927
|
case "list": {
|
|
7741
7928
|
requireNoExtraArgs(rest, "rig run list");
|
|
7742
|
-
const { runs, source } = await listRunsForSelectedConnection(context, { limit: 100 });
|
|
7929
|
+
const { runs, source } = isRemoteConnectionSelected(context.projectRoot) ? await withSpinner("Reading runs from server\u2026", () => listRunsForSelectedConnection(context, { limit: 100 }), { outputMode: context.outputMode }) : await listRunsForSelectedConnection(context, { limit: 100 });
|
|
7743
7930
|
if (context.outputMode === "text") {
|
|
7744
7931
|
printFormattedOutput(formatRunList(runs, { source }));
|
|
7745
7932
|
await printPendingInboxFooter(context);
|
|
@@ -7812,7 +7999,7 @@ async function executeRun(context, args) {
|
|
|
7812
7999
|
if (!runId) {
|
|
7813
8000
|
throw new CliError("run show requires a run id.", 1, { hint: "Run `rig run list` to find run ids, then `rig run show <run-id>`." });
|
|
7814
8001
|
}
|
|
7815
|
-
const record = readAuthorityRun5(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, runId).catch(() => ({})));
|
|
8002
|
+
const record = readAuthorityRun5(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await withSpinner(`Reading run ${runId} from server\u2026`, () => getRunDetailsViaServer(context, runId).catch(() => ({})), { outputMode: context.outputMode }));
|
|
7816
8003
|
if (!record) {
|
|
7817
8004
|
throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see recorded runs." });
|
|
7818
8005
|
}
|
|
@@ -7833,7 +8020,8 @@ async function executeRun(context, args) {
|
|
|
7833
8020
|
}
|
|
7834
8021
|
const renderer = createPiRunStreamRenderer();
|
|
7835
8022
|
let cursor = null;
|
|
7836
|
-
const
|
|
8023
|
+
const timelineRunId = run.value;
|
|
8024
|
+
const page = await withSpinner(`Reading timeline for ${timelineRunId}\u2026`, () => getRunTimelineViaServer(context, timelineRunId, { limit: 500 }), { outputMode: context.outputMode });
|
|
7837
8025
|
const events = Array.isArray(page.entries) ? page.entries.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
|
|
7838
8026
|
cursor = typeof page.nextCursor === "string" ? page.nextCursor : null;
|
|
7839
8027
|
if (context.outputMode === "text") {
|
|
@@ -7910,8 +8098,9 @@ async function executeRun(context, args) {
|
|
|
7910
8098
|
throw new CliError("run attach requires a run id.", 2, { hint: "Run `rig run list` to find run ids, then `rig run attach <run-id> --follow`." });
|
|
7911
8099
|
}
|
|
7912
8100
|
let steered = false;
|
|
7913
|
-
|
|
7914
|
-
|
|
8101
|
+
const steerMessage = messageOption.value?.trim();
|
|
8102
|
+
if (steerMessage) {
|
|
8103
|
+
await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, steerMessage), { outputMode: context.outputMode });
|
|
7915
8104
|
steered = true;
|
|
7916
8105
|
}
|
|
7917
8106
|
const attached = await attachRunOperatorView(context, {
|
|
@@ -7931,7 +8120,7 @@ async function executeRun(context, args) {
|
|
|
7931
8120
|
}
|
|
7932
8121
|
return { ok: true, group: "run", command };
|
|
7933
8122
|
}
|
|
7934
|
-
const summary = isRemoteConnectionSelected(context.projectRoot) ? buildServerRunStatus(await listRunsViaServer(context, { limit: 100 })) : runStatus(context.projectRoot, runtimeContext);
|
|
8123
|
+
const summary = isRemoteConnectionSelected(context.projectRoot) ? buildServerRunStatus(await withSpinner("Reading run status from server\u2026", () => listRunsViaServer(context, { limit: 100 }), { outputMode: context.outputMode })) : runStatus(context.projectRoot, runtimeContext);
|
|
7935
8124
|
const activeRuns = Array.isArray(summary.activeRuns) ? summary.activeRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
|
|
7936
8125
|
const recentRuns = Array.isArray(summary.recentRuns) ? summary.recentRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
|
|
7937
8126
|
if (context.outputMode === "text") {
|
|
@@ -8020,7 +8209,7 @@ async function executeRun(context, args) {
|
|
|
8020
8209
|
}
|
|
8021
8210
|
return { ok: true, group: "run", command };
|
|
8022
8211
|
}
|
|
8023
|
-
const resumed = await
|
|
8212
|
+
const resumed = await withSpinner(targetRunId ? `Resuming run ${targetRunId}\u2026` : "Resuming latest resumable run\u2026", () => resumeRunViaServer(context, targetRunId, { restart: false }), { outputMode: context.outputMode });
|
|
8024
8213
|
if (context.outputMode === "text") {
|
|
8025
8214
|
console.log(`Resumed run: ${resumed.runId}`);
|
|
8026
8215
|
}
|
|
@@ -8039,7 +8228,7 @@ async function executeRun(context, args) {
|
|
|
8039
8228
|
}
|
|
8040
8229
|
return { ok: true, group: "run", command };
|
|
8041
8230
|
}
|
|
8042
|
-
const restarted = await
|
|
8231
|
+
const restarted = await withSpinner(targetRunId ? `Restarting run ${targetRunId}\u2026` : "Restarting latest run\u2026", () => resumeRunViaServer(context, targetRunId, { restart: true }), { outputMode: context.outputMode });
|
|
8043
8232
|
if (context.outputMode === "text") {
|
|
8044
8233
|
console.log(`Restarted run: ${restarted.runId}`);
|
|
8045
8234
|
}
|
|
@@ -8066,7 +8255,8 @@ async function executeRun(context, args) {
|
|
|
8066
8255
|
}
|
|
8067
8256
|
return { ok: true, group: "run", command, details: { runId, dryRun: true } };
|
|
8068
8257
|
}
|
|
8069
|
-
|
|
8258
|
+
const trimmedMessage = message2.trim();
|
|
8259
|
+
await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, trimmedMessage), { outputMode: context.outputMode });
|
|
8070
8260
|
if (context.outputMode === "text") {
|
|
8071
8261
|
console.log(`Steering message queued for ${runId}.`);
|
|
8072
8262
|
}
|
|
@@ -8087,7 +8277,7 @@ async function executeRun(context, args) {
|
|
|
8087
8277
|
};
|
|
8088
8278
|
}
|
|
8089
8279
|
if (runId) {
|
|
8090
|
-
const stopped = await stopRunViaServer(context, runId);
|
|
8280
|
+
const stopped = await withSpinner(`Requesting stop for ${runId}\u2026`, () => stopRunViaServer(context, runId), { outputMode: context.outputMode });
|
|
8091
8281
|
if (context.outputMode === "text")
|
|
8092
8282
|
console.log(`Stop requested: ${runId}`);
|
|
8093
8283
|
return { ok: true, group: "run", command, details: stopped };
|
|
@@ -8333,12 +8523,12 @@ async function executeServer(context, args, options) {
|
|
|
8333
8523
|
|
|
8334
8524
|
// packages/cli/src/commands/stats.ts
|
|
8335
8525
|
init_runner();
|
|
8336
|
-
import
|
|
8526
|
+
import pc6 from "picocolors";
|
|
8337
8527
|
import { computeRigStats } from "@rig/runtime/control-plane/native/run-stats";
|
|
8338
8528
|
|
|
8339
8529
|
// packages/cli/src/commands/_help-catalog.ts
|
|
8340
8530
|
import { intro as intro3, log as log4, note as note4, outro as outro3 } from "@clack/prompts";
|
|
8341
|
-
import
|
|
8531
|
+
import pc5 from "picocolors";
|
|
8342
8532
|
var TOP_LEVEL_SECTIONS = [
|
|
8343
8533
|
{
|
|
8344
8534
|
title: "Start here",
|
|
@@ -8626,13 +8816,13 @@ var ADVANCED_COMMANDS = [
|
|
|
8626
8816
|
];
|
|
8627
8817
|
var ALL_GROUPS = [...PRIMARY_GROUPS, ...ADVANCED_GROUPS];
|
|
8628
8818
|
function heading(title) {
|
|
8629
|
-
return
|
|
8819
|
+
return pc5.bold(pc5.cyan(title));
|
|
8630
8820
|
}
|
|
8631
8821
|
function renderRigBanner(version) {
|
|
8632
|
-
const m = (s) =>
|
|
8633
|
-
const c = (s) =>
|
|
8634
|
-
const y = (s) =>
|
|
8635
|
-
const d = (s) =>
|
|
8822
|
+
const m = (s) => pc5.bold(pc5.magenta(s));
|
|
8823
|
+
const c = (s) => pc5.bold(pc5.cyan(s));
|
|
8824
|
+
const y = (s) => pc5.yellow(s);
|
|
8825
|
+
const d = (s) => pc5.dim(s);
|
|
8636
8826
|
const lines = [
|
|
8637
8827
|
m(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + d(" \u2591\u2592\u2593\u2588 "),
|
|
8638
8828
|
m(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ") + d("\u2593\u2592\u2591"),
|
|
@@ -8641,7 +8831,7 @@ function renderRigBanner(version) {
|
|
|
8641
8831
|
y(" \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + d(" \u2592\u2591"),
|
|
8642
8832
|
y(" \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D "),
|
|
8643
8833
|
"",
|
|
8644
|
-
` ${c("\u25E2\u25E4")} ${
|
|
8834
|
+
` ${c("\u25E2\u25E4")} ${pc5.bold("the control rig for autonomous coding agents")} ${m("//")} ${d("you are the operator")}`,
|
|
8645
8835
|
version ? ` ${d(`v${version} \xB7 jack in: rig task run --next`)}` : ` ${d("jack in: rig task run --next")}`
|
|
8646
8836
|
];
|
|
8647
8837
|
return lines.join(`
|
|
@@ -8649,7 +8839,7 @@ function renderRigBanner(version) {
|
|
|
8649
8839
|
}
|
|
8650
8840
|
function commandLine(command, description) {
|
|
8651
8841
|
const commandColumn = command.length >= 38 ? `${command} ` : command.padEnd(38);
|
|
8652
|
-
return `${
|
|
8842
|
+
return `${pc5.dim("\u2502")} ${pc5.bold(commandColumn)} ${description}`;
|
|
8653
8843
|
}
|
|
8654
8844
|
function renderCommandBlock(commands) {
|
|
8655
8845
|
return commands.map((entry) => commandLine(entry.command, entry.description)).join(`
|
|
@@ -8659,37 +8849,37 @@ function renderGroup(group) {
|
|
|
8659
8849
|
const lines = [
|
|
8660
8850
|
`${heading(`rig ${group.name}`)} \u2014 ${group.summary}`,
|
|
8661
8851
|
"",
|
|
8662
|
-
|
|
8852
|
+
pc5.bold("Usage"),
|
|
8663
8853
|
...group.usage.map((line) => ` ${line}`),
|
|
8664
8854
|
"",
|
|
8665
|
-
|
|
8855
|
+
pc5.bold("Commands"),
|
|
8666
8856
|
...group.commands.map((entry) => commandLine(entry.command, entry.description))
|
|
8667
8857
|
];
|
|
8668
8858
|
if (group.examples?.length) {
|
|
8669
|
-
lines.push("",
|
|
8859
|
+
lines.push("", pc5.bold("Examples"), ...group.examples.map((line) => ` ${pc5.dim("$")} ${line}`));
|
|
8670
8860
|
}
|
|
8671
8861
|
if (group.next?.length) {
|
|
8672
|
-
lines.push("",
|
|
8862
|
+
lines.push("", pc5.bold("Next steps"), ...group.next.map((line) => ` ${pc5.dim("\u203A")} ${line}`));
|
|
8673
8863
|
}
|
|
8674
8864
|
if (group.advanced?.length) {
|
|
8675
|
-
lines.push("",
|
|
8865
|
+
lines.push("", pc5.bold("Compatibility / advanced"), ...group.advanced.map((line) => ` ${pc5.dim("\u203A")} ${line}`));
|
|
8676
8866
|
}
|
|
8677
8867
|
return lines.join(`
|
|
8678
8868
|
`);
|
|
8679
8869
|
}
|
|
8680
8870
|
function renderTopLevelHelp() {
|
|
8681
8871
|
return [
|
|
8682
|
-
`${heading("rig")} ${
|
|
8683
|
-
|
|
8872
|
+
`${heading("rig")} ${pc5.dim("\u2014 server-owned task/run control plane for Pi-backed engineering work")}`,
|
|
8873
|
+
pc5.dim("Current path: select a server, choose a task, submit a run, attach with native Pi, clear inbox gates."),
|
|
8684
8874
|
"",
|
|
8685
8875
|
...TOP_LEVEL_SECTIONS.flatMap((section) => [
|
|
8686
|
-
`${
|
|
8876
|
+
`${pc5.bold(pc5.magenta(`\u25C7 ${section.title}`))} \u2014 ${pc5.dim(section.subtitle)}`,
|
|
8687
8877
|
renderCommandBlock(section.commands),
|
|
8688
8878
|
""
|
|
8689
8879
|
]),
|
|
8690
|
-
|
|
8880
|
+
pc5.dim("More: `rig help --advanced` for dev/compatibility commands; `rig <group> --help` for rich per-group help; `rig --version` for the installed version."),
|
|
8691
8881
|
"",
|
|
8692
|
-
|
|
8882
|
+
pc5.bold("Global options"),
|
|
8693
8883
|
commandLine("--project <path>", "Use a project root instead of auto-discovery."),
|
|
8694
8884
|
commandLine("--json", "Emit structured output for scripts/agents."),
|
|
8695
8885
|
commandLine("--dry-run", "Print the command plan without mutating state.")
|
|
@@ -8700,16 +8890,16 @@ function renderAdvancedHelp() {
|
|
|
8700
8890
|
return [
|
|
8701
8891
|
`${heading("rig advanced")} \u2014 compatibility, diagnostics, and internal surfaces`,
|
|
8702
8892
|
"",
|
|
8703
|
-
|
|
8893
|
+
pc5.bold("Primary groups"),
|
|
8704
8894
|
" init, server, task, run, inbox, repo, plugin, inspect, stats, doctor, github",
|
|
8705
8895
|
"",
|
|
8706
|
-
|
|
8896
|
+
pc5.bold("Advanced commands"),
|
|
8707
8897
|
...ADVANCED_COMMANDS.map((entry) => commandLine(entry.command, entry.description)),
|
|
8708
8898
|
"",
|
|
8709
|
-
|
|
8899
|
+
pc5.bold("Advanced groups"),
|
|
8710
8900
|
...ADVANCED_GROUPS.map((group) => commandLine(group.name, group.summary)),
|
|
8711
8901
|
"",
|
|
8712
|
-
|
|
8902
|
+
pc5.dim("All groups remain callable. Prefer `rig server`, `rig task`, `rig run`, and `rig inbox` for day-to-day work.")
|
|
8713
8903
|
].join(`
|
|
8714
8904
|
`);
|
|
8715
8905
|
}
|
|
@@ -8857,11 +9047,11 @@ function formatStatsReport(stats) {
|
|
|
8857
9047
|
const keyWidth = Math.max(...rows.map(([key]) => key.length));
|
|
8858
9048
|
const lines = [
|
|
8859
9049
|
formatSection("Fleet stats", window),
|
|
8860
|
-
...rows.map(([key, value]) => `${
|
|
9050
|
+
...rows.map(([key, value]) => `${pc6.dim("\u2502")} ${pc6.dim(key.padEnd(keyWidth + 2))} ${value}`)
|
|
8861
9051
|
];
|
|
8862
9052
|
const otherStatuses = Object.entries(stats.statusCounts).filter(([status]) => !["completed", "failed", "needs-attention"].includes(status)).sort(([, left], [, right]) => right - left);
|
|
8863
9053
|
if (otherStatuses.length > 0) {
|
|
8864
|
-
lines.push(`${
|
|
9054
|
+
lines.push(`${pc6.dim("\u2502")} ${pc6.dim("other".padEnd(keyWidth + 2))} ${otherStatuses.map(([status, count]) => `${status}:${count}`).join(" ")}`);
|
|
8865
9055
|
}
|
|
8866
9056
|
lines.push("", ...formatNextSteps([
|
|
8867
9057
|
"Inspect a run: `rig run list` then `rig run show <run-id>`",
|
|
@@ -8880,7 +9070,8 @@ async function executeStats(context, args) {
|
|
|
8880
9070
|
const sinceResult = takeOption(pending, "--since");
|
|
8881
9071
|
requireNoExtraArgs(sinceResult.rest, "rig stats [show] [--since <7d|30d|ISO date>]");
|
|
8882
9072
|
const since = parseSinceOption(sinceResult.value);
|
|
8883
|
-
const
|
|
9073
|
+
const remoteStats = isRemoteConnectionSelected(context.projectRoot);
|
|
9074
|
+
const stats = await withSpinner(remoteStats ? "Computing fleet stats on the server\u2026" : "Scanning local run state\u2026", async () => remoteStats ? await requestServerJson(context, `/api/stats${since ? `?since=${encodeURIComponent(since.toISOString())}` : ""}`) : computeRigStats(context.projectRoot, { since }), { outputMode: context.outputMode });
|
|
8884
9075
|
if (context.outputMode === "text") {
|
|
8885
9076
|
printFormattedOutput(formatStatsReport(stats));
|
|
8886
9077
|
}
|
|
@@ -9135,7 +9326,7 @@ async function executeTask(context, args, options) {
|
|
|
9135
9326
|
pending = rawResult.rest;
|
|
9136
9327
|
const { filters, rest: remaining } = parseTaskFilters(pending);
|
|
9137
9328
|
requireNoExtraArgs(remaining, "rig task list [--raw] [--assignee <login|me|@me>] [--state open|closed] [--status <status>] [--limit <n>]");
|
|
9138
|
-
const tasks = await listWorkspaceTasksViaServer(context, filters);
|
|
9329
|
+
const tasks = await withSpinner("Reading tasks from server\u2026", () => listWorkspaceTasksViaServer(context, filters), { outputMode: context.outputMode });
|
|
9139
9330
|
if (context.outputMode === "text") {
|
|
9140
9331
|
const renderedTasks = rawResult.value ? tasks.map((task) => summarizeTask(task, { raw: true })) : tasks.map((task) => summarizeTask(task));
|
|
9141
9332
|
printFormattedOutput(formatTaskList(renderedTasks, { raw: rawResult.value }));
|
|
@@ -9159,7 +9350,7 @@ async function executeTask(context, args, options) {
|
|
|
9159
9350
|
const taskId3 = normalizeTaskRunTaskId(taskOption.value ?? positional);
|
|
9160
9351
|
if (!taskId3)
|
|
9161
9352
|
throw new CliError("task show requires a task id.", 2, { hint: "Run `rig task list` to find ids, then `rig task show <id>`." });
|
|
9162
|
-
const task = await getWorkspaceTaskViaServer(context, taskId3);
|
|
9353
|
+
const task = await withSpinner(`Reading task ${taskId3} from server\u2026`, () => getWorkspaceTaskViaServer(context, taskId3), { outputMode: context.outputMode });
|
|
9163
9354
|
if (!task)
|
|
9164
9355
|
throw new CliError(`Task not found: ${taskId3}`, 3, { hint: "Run `rig task list` to see available tasks." });
|
|
9165
9356
|
const summary = summarizeTask(task, { raw: true });
|
|
@@ -9171,7 +9362,7 @@ async function executeTask(context, args, options) {
|
|
|
9171
9362
|
case "next": {
|
|
9172
9363
|
const { filters, rest: remaining } = parseTaskFilters(rest);
|
|
9173
9364
|
requireNoExtraArgs(remaining, "rig task next [--assignee <login|me|@me>] [--state open|closed] [--status <status>] [--limit <n>]");
|
|
9174
|
-
const selected = await selectNextWorkspaceTaskViaServer(context, filters);
|
|
9365
|
+
const selected = await withSpinner("Selecting next ready task\u2026", () => selectNextWorkspaceTaskViaServer(context, filters), { outputMode: context.outputMode });
|
|
9175
9366
|
if (context.outputMode === "text") {
|
|
9176
9367
|
if (selected.task) {
|
|
9177
9368
|
printFormattedOutput(formatTaskCard(summarizeTask(selected.task, { raw: true }), { title: "Selected task", selected: true }));
|
|
@@ -9321,7 +9512,7 @@ async function executeTask(context, args, options) {
|
|
|
9321
9512
|
let selectedTask = null;
|
|
9322
9513
|
let selectedTaskId = normalizeTaskRunTaskId(taskResult.value) ?? positionalTaskId;
|
|
9323
9514
|
if (nextResult.value) {
|
|
9324
|
-
const selected = await selectNextWorkspaceTaskViaServer(context, filterResult.filters);
|
|
9515
|
+
const selected = await withSpinner("Selecting next ready task\u2026", () => selectNextWorkspaceTaskViaServer(context, filterResult.filters), { outputMode: context.outputMode });
|
|
9325
9516
|
selectedTask = selected.task;
|
|
9326
9517
|
selectedTaskId = selected.task ? readTaskId(selected.task) : null;
|
|
9327
9518
|
if (!selectedTaskId) {
|
|
@@ -9332,7 +9523,7 @@ async function executeTask(context, args, options) {
|
|
|
9332
9523
|
if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
9333
9524
|
throw new CliError("task run requires an interactive terminal to pick a task; pass --next, --task <id>, or --initial-prompt/--title for an ad hoc run.", 2);
|
|
9334
9525
|
}
|
|
9335
|
-
const tasks = await listWorkspaceTasksViaServer(context, filterResult.filters);
|
|
9526
|
+
const tasks = await withSpinner("Reading tasks from server\u2026", () => listWorkspaceTasksViaServer(context, filterResult.filters), { outputMode: context.outputMode });
|
|
9336
9527
|
selectedTask = await selectTaskWithTextPicker(tasks);
|
|
9337
9528
|
selectedTaskId = selectedTask ? readTaskId(selectedTask) : null;
|
|
9338
9529
|
if (!selectedTaskId) {
|
|
@@ -9342,13 +9533,13 @@ async function executeTask(context, args, options) {
|
|
|
9342
9533
|
await runProjectMainSyncPreflight(context, { disabled: skipProjectSyncResult.value });
|
|
9343
9534
|
const projectDefaults = await loadTaskRunProjectDefaults(context.projectRoot);
|
|
9344
9535
|
const runtimeAdapter = normalizeRuntimeAdapter(runtimeAdapterResult.value ?? projectDefaults.runtimeAdapter);
|
|
9345
|
-
await runFastTaskRunPreflight(context, {
|
|
9536
|
+
await withSpinner("Running task-run preflight\u2026", () => runFastTaskRunPreflight(context, {
|
|
9346
9537
|
taskId: selectedTaskId,
|
|
9347
9538
|
runtimeAdapter
|
|
9348
|
-
});
|
|
9539
|
+
}), { outputMode: context.outputMode });
|
|
9349
9540
|
const dirtyBaseline = await resolveDirtyBaselineForTaskRun(context, dirtyBaselineResult.value);
|
|
9350
9541
|
const prMode = noPrResult.value ? "off" : normalizePrMode(prResult.value) ?? projectDefaults.prMode;
|
|
9351
|
-
const submitted = await submitTaskRunViaServer(context, {
|
|
9542
|
+
const submitted = await withSpinner("Dispatching run\u2026", () => submitTaskRunViaServer(context, {
|
|
9352
9543
|
runId: context.runId,
|
|
9353
9544
|
taskId: selectedTaskId ?? undefined,
|
|
9354
9545
|
title: titleResult.value ?? undefined,
|
|
@@ -9359,7 +9550,7 @@ async function executeTask(context, args, options) {
|
|
|
9359
9550
|
initialPrompt: initialPromptResult.value ?? undefined,
|
|
9360
9551
|
baselineMode: dirtyBaseline.mode,
|
|
9361
9552
|
prMode
|
|
9362
|
-
});
|
|
9553
|
+
}), { outputMode: context.outputMode });
|
|
9363
9554
|
let attachDetails = null;
|
|
9364
9555
|
if (!detachResult.value && context.outputMode === "text") {
|
|
9365
9556
|
printFormattedOutput(formatSubmittedRun({
|
|
@@ -11537,7 +11728,7 @@ async function executeSetup(context, args) {
|
|
|
11537
11728
|
case "check":
|
|
11538
11729
|
requireNoExtraArgs(rest, `rig setup ${command}`);
|
|
11539
11730
|
{
|
|
11540
|
-
const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot));
|
|
11731
|
+
const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot, context.outputMode));
|
|
11541
11732
|
return { ok: true, group: "setup", command, details: { checks, failures: countDoctorFailures(checks) } };
|
|
11542
11733
|
}
|
|
11543
11734
|
case "setup":
|
|
@@ -11546,7 +11737,7 @@ async function executeSetup(context, args) {
|
|
|
11546
11737
|
return { ok: true, group: "setup", command };
|
|
11547
11738
|
case "preflight":
|
|
11548
11739
|
requireNoExtraArgs(rest, "rig setup preflight");
|
|
11549
|
-
await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot));
|
|
11740
|
+
await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot, context.outputMode));
|
|
11550
11741
|
return { ok: true, group: "setup", command };
|
|
11551
11742
|
default:
|
|
11552
11743
|
throw new CliError(`Unknown setup command: ${command}`, 1, { hint: "Run `rig setup --help` \u2014 commands are bootstrap|check|preflight." });
|
|
@@ -11567,8 +11758,8 @@ function runSetupInit(projectRoot) {
|
|
|
11567
11758
|
}
|
|
11568
11759
|
console.log("Harness directories ready.");
|
|
11569
11760
|
}
|
|
11570
|
-
async function runSetupCheck(projectRoot) {
|
|
11571
|
-
const doctorChecks = await runRigDoctorChecks({ projectRoot });
|
|
11761
|
+
async function runSetupCheck(projectRoot, outputMode = "text") {
|
|
11762
|
+
const doctorChecks = await withSpinner("Running setup checks\u2026", (update) => runRigDoctorChecks({ projectRoot, onProgress: update }), { outputMode });
|
|
11572
11763
|
console.log(formatDoctorChecks(doctorChecks));
|
|
11573
11764
|
const failures = countDoctorFailures(doctorChecks);
|
|
11574
11765
|
if (failures > 0) {
|
|
@@ -11576,8 +11767,8 @@ async function runSetupCheck(projectRoot) {
|
|
|
11576
11767
|
}
|
|
11577
11768
|
return doctorChecks;
|
|
11578
11769
|
}
|
|
11579
|
-
async function runSetupPreflight(projectRoot) {
|
|
11580
|
-
await runSetupCheck(projectRoot);
|
|
11770
|
+
async function runSetupPreflight(projectRoot, outputMode = "text") {
|
|
11771
|
+
await runSetupCheck(projectRoot, outputMode);
|
|
11581
11772
|
const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
|
|
11582
11773
|
if (existsSync15(validationRoot)) {
|
|
11583
11774
|
const validators = readdirSync3(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|