@h-rig/cli 0.0.6-alpha.66 → 0.0.6-alpha.68
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 +250 -166
- package/dist/src/commands/_async-ui.js +141 -0
- package/dist/src/commands/_connection-state.js +5 -1
- package/dist/src/commands/_doctor-checks.js +18 -0
- package/dist/src/commands/_operator-view.js +143 -4
- package/dist/src/commands/_pi-frontend.js +13 -1
- package/dist/src/commands/_preflight.js +7 -0
- package/dist/src/commands/_server-client.js +13 -0
- package/dist/src/commands/_snapshot-upload.js +7 -0
- package/dist/src/commands/_spinner.js +4 -2
- package/dist/src/commands/doctor.js +145 -1
- package/dist/src/commands/github.js +137 -3
- package/dist/src/commands/inbox.js +139 -6
- package/dist/src/commands/init.js +18 -0
- package/dist/src/commands/inspect.js +147 -8
- package/dist/src/commands/run.js +190 -38
- package/dist/src/commands/server.js +7 -0
- package/dist/src/commands/setup.js +150 -6
- package/dist/src/commands/stats.js +448 -110
- package/dist/src/commands/task-run-driver.js +7 -0
- package/dist/src/commands/task.js +186 -47
- package/dist/src/commands.js +250 -166
- package/dist/src/index.js +250 -166
- package/package.json +8 -8
package/dist/src/commands.js
CHANGED
|
@@ -2730,6 +2730,9 @@ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
|
2730
2730
|
return;
|
|
2731
2731
|
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
2732
2732
|
}
|
|
2733
|
+
function isRemoteConnectionSelected(projectRoot) {
|
|
2734
|
+
return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
|
|
2735
|
+
}
|
|
2733
2736
|
|
|
2734
2737
|
// packages/cli/src/commands/_server-client.ts
|
|
2735
2738
|
init_runner();
|
|
@@ -2737,6 +2740,15 @@ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
|
2737
2740
|
import { resolve as resolve10 } from "path";
|
|
2738
2741
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
2739
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
|
+
}
|
|
2740
2752
|
function cleanToken(value) {
|
|
2741
2753
|
const trimmed = value?.trim();
|
|
2742
2754
|
return trimmed ? trimmed : null;
|
|
@@ -2783,6 +2795,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2783
2795
|
try {
|
|
2784
2796
|
const selected = resolveSelectedConnection(projectRoot);
|
|
2785
2797
|
if (selected?.connection.kind === "remote") {
|
|
2798
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
2786
2799
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
2787
2800
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
2788
2801
|
return {
|
|
@@ -2792,6 +2805,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2792
2805
|
serverProjectRoot
|
|
2793
2806
|
};
|
|
2794
2807
|
}
|
|
2808
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
2795
2809
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
2796
2810
|
return {
|
|
2797
2811
|
baseUrl: connection.baseUrl,
|
|
@@ -2908,6 +2922,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
2908
2922
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
2909
2923
|
if (server.serverProjectRoot)
|
|
2910
2924
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
2925
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
2911
2926
|
let response;
|
|
2912
2927
|
try {
|
|
2913
2928
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -4428,6 +4443,127 @@ function formatConnectionStatus(selected, connections) {
|
|
|
4428
4443
|
`);
|
|
4429
4444
|
}
|
|
4430
4445
|
|
|
4446
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
4447
|
+
import pc4 from "picocolors";
|
|
4448
|
+
|
|
4449
|
+
// packages/cli/src/commands/_spinner.ts
|
|
4450
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
4451
|
+
function createTtySpinner(input) {
|
|
4452
|
+
const output = input.output ?? process.stdout;
|
|
4453
|
+
const isTty = output.isTTY === true;
|
|
4454
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
4455
|
+
let label = input.label;
|
|
4456
|
+
let frame = 0;
|
|
4457
|
+
let paused = false;
|
|
4458
|
+
let stopped = false;
|
|
4459
|
+
let lastPrintedLabel = "";
|
|
4460
|
+
const render = () => {
|
|
4461
|
+
if (stopped || paused)
|
|
4462
|
+
return;
|
|
4463
|
+
if (!isTty) {
|
|
4464
|
+
if (label !== lastPrintedLabel) {
|
|
4465
|
+
output.write(`${label}
|
|
4466
|
+
`);
|
|
4467
|
+
lastPrintedLabel = label;
|
|
4468
|
+
}
|
|
4469
|
+
return;
|
|
4470
|
+
}
|
|
4471
|
+
frame = (frame + 1) % frames.length;
|
|
4472
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
4473
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
4474
|
+
};
|
|
4475
|
+
const clearLine = () => {
|
|
4476
|
+
if (isTty)
|
|
4477
|
+
output.write("\r\x1B[2K");
|
|
4478
|
+
};
|
|
4479
|
+
render();
|
|
4480
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
4481
|
+
return {
|
|
4482
|
+
setLabel(next) {
|
|
4483
|
+
label = next;
|
|
4484
|
+
render();
|
|
4485
|
+
},
|
|
4486
|
+
pause() {
|
|
4487
|
+
paused = true;
|
|
4488
|
+
clearLine();
|
|
4489
|
+
},
|
|
4490
|
+
resume() {
|
|
4491
|
+
if (stopped)
|
|
4492
|
+
return;
|
|
4493
|
+
paused = false;
|
|
4494
|
+
render();
|
|
4495
|
+
},
|
|
4496
|
+
stop(finalLine) {
|
|
4497
|
+
if (stopped)
|
|
4498
|
+
return;
|
|
4499
|
+
stopped = true;
|
|
4500
|
+
if (timer)
|
|
4501
|
+
clearInterval(timer);
|
|
4502
|
+
clearLine();
|
|
4503
|
+
if (finalLine)
|
|
4504
|
+
output.write(`${finalLine}
|
|
4505
|
+
`);
|
|
4506
|
+
}
|
|
4507
|
+
};
|
|
4508
|
+
}
|
|
4509
|
+
|
|
4510
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
4511
|
+
var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
4512
|
+
var DONE_SYMBOL = pc4.green("\u25C7");
|
|
4513
|
+
var FAIL_SYMBOL = pc4.red("\u25A0");
|
|
4514
|
+
var activeUpdate = null;
|
|
4515
|
+
async function withSpinner(label, work, options = {}) {
|
|
4516
|
+
if (options.outputMode === "json") {
|
|
4517
|
+
return work(() => {});
|
|
4518
|
+
}
|
|
4519
|
+
if (activeUpdate) {
|
|
4520
|
+
const outer = activeUpdate;
|
|
4521
|
+
outer(label);
|
|
4522
|
+
return work(outer);
|
|
4523
|
+
}
|
|
4524
|
+
const output = options.output ?? process.stderr;
|
|
4525
|
+
const isTty = output.isTTY === true;
|
|
4526
|
+
let lastLabel = label;
|
|
4527
|
+
if (!isTty) {
|
|
4528
|
+
output.write(`${label}
|
|
4529
|
+
`);
|
|
4530
|
+
const update2 = (next) => {
|
|
4531
|
+
lastLabel = next;
|
|
4532
|
+
};
|
|
4533
|
+
activeUpdate = update2;
|
|
4534
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
4535
|
+
try {
|
|
4536
|
+
return await work(update2);
|
|
4537
|
+
} finally {
|
|
4538
|
+
activeUpdate = null;
|
|
4539
|
+
setServerPhaseListener(previousListener2);
|
|
4540
|
+
}
|
|
4541
|
+
}
|
|
4542
|
+
const spinner2 = createTtySpinner({
|
|
4543
|
+
label,
|
|
4544
|
+
output,
|
|
4545
|
+
frames: CLACK_SPINNER_FRAMES,
|
|
4546
|
+
styleFrame: (frame) => pc4.magenta(frame)
|
|
4547
|
+
});
|
|
4548
|
+
const update = (next) => {
|
|
4549
|
+
lastLabel = next;
|
|
4550
|
+
spinner2.setLabel(next);
|
|
4551
|
+
};
|
|
4552
|
+
activeUpdate = update;
|
|
4553
|
+
const previousListener = setServerPhaseListener(update);
|
|
4554
|
+
try {
|
|
4555
|
+
const result = await work(update);
|
|
4556
|
+
spinner2.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
4557
|
+
return result;
|
|
4558
|
+
} catch (error) {
|
|
4559
|
+
spinner2.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
4560
|
+
throw error;
|
|
4561
|
+
} finally {
|
|
4562
|
+
activeUpdate = null;
|
|
4563
|
+
setServerPhaseListener(previousListener);
|
|
4564
|
+
}
|
|
4565
|
+
}
|
|
4566
|
+
|
|
4431
4567
|
// packages/cli/src/commands/inbox.ts
|
|
4432
4568
|
async function listInboxRecords(context, kind, filters) {
|
|
4433
4569
|
const params = new URLSearchParams;
|
|
@@ -4532,7 +4668,7 @@ async function executeInbox(context, args) {
|
|
|
4532
4668
|
const task = takeOption(pending, "--task");
|
|
4533
4669
|
pending = task.rest;
|
|
4534
4670
|
requireNoExtraArgs(pending, "rig inbox approvals [--run <id>] [--task <id>]");
|
|
4535
|
-
const approvals = await listInboxRecords(context, "approvals", { run: run.value, task: task.value });
|
|
4671
|
+
const approvals = await withSpinner("Reading approvals from server\u2026", () => listInboxRecords(context, "approvals", { run: run.value, task: task.value }), { outputMode: context.outputMode });
|
|
4536
4672
|
renderList(context, "approvals", approvals);
|
|
4537
4673
|
return { ok: true, group: "inbox", command, details: { approvals } };
|
|
4538
4674
|
}
|
|
@@ -4543,7 +4679,7 @@ async function executeInbox(context, args) {
|
|
|
4543
4679
|
const task = takeOption(pending, "--task");
|
|
4544
4680
|
pending = task.rest;
|
|
4545
4681
|
requireNoExtraArgs(pending, "rig inbox inputs [--run <id>] [--task <id>]");
|
|
4546
|
-
const requests = await listInboxRecords(context, "inputs", { run: run.value, task: task.value });
|
|
4682
|
+
const requests = await withSpinner("Reading input requests from server\u2026", () => listInboxRecords(context, "inputs", { run: run.value, task: task.value }), { outputMode: context.outputMode });
|
|
4547
4683
|
renderList(context, "inputs", requests);
|
|
4548
4684
|
return { ok: true, group: "inbox", command, details: { requests } };
|
|
4549
4685
|
}
|
|
@@ -4564,7 +4700,7 @@ async function executeInbox(context, args) {
|
|
|
4564
4700
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
4565
4701
|
throw new CliError("decision must be approve or reject.", 1, { hint: "Re-run as `rig inbox approve --run <id> --request <id> --decision approve|reject`." });
|
|
4566
4702
|
}
|
|
4567
|
-
const result = await requestServerJson(context, "/api/inbox/approvals/resolve", {
|
|
4703
|
+
const result = await withSpinner(`Resolving approval ${request.value}\u2026`, () => requestServerJson(context, "/api/inbox/approvals/resolve", {
|
|
4568
4704
|
method: "POST",
|
|
4569
4705
|
headers: { "content-type": "application/json" },
|
|
4570
4706
|
body: JSON.stringify({
|
|
@@ -4573,7 +4709,7 @@ async function executeInbox(context, args) {
|
|
|
4573
4709
|
decision: decision.value,
|
|
4574
4710
|
note: note4.value ?? null
|
|
4575
4711
|
})
|
|
4576
|
-
});
|
|
4712
|
+
}), { outputMode: context.outputMode });
|
|
4577
4713
|
return { ok: true, group: "inbox", command, details: { result } };
|
|
4578
4714
|
}
|
|
4579
4715
|
case "respond": {
|
|
@@ -4607,7 +4743,7 @@ async function executeInbox(context, args) {
|
|
|
4607
4743
|
const [key, ...restValue] = entry.split("=");
|
|
4608
4744
|
return [key, restValue.join("=")];
|
|
4609
4745
|
}));
|
|
4610
|
-
const result = await requestServerJson(context, "/api/inbox/inputs/respond", {
|
|
4746
|
+
const result = await withSpinner(`Sending response for ${request.value}\u2026`, () => requestServerJson(context, "/api/inbox/inputs/respond", {
|
|
4611
4747
|
method: "POST",
|
|
4612
4748
|
headers: { "content-type": "application/json" },
|
|
4613
4749
|
body: JSON.stringify({
|
|
@@ -4615,7 +4751,7 @@ async function executeInbox(context, args) {
|
|
|
4615
4751
|
requestId: request.value,
|
|
4616
4752
|
answers: parsedAnswers
|
|
4617
4753
|
})
|
|
4618
|
-
});
|
|
4754
|
+
}), { outputMode: context.outputMode });
|
|
4619
4755
|
return { ok: true, group: "inbox", command, details: { result } };
|
|
4620
4756
|
}
|
|
4621
4757
|
case "watch": {
|
|
@@ -5032,7 +5168,10 @@ async function runRigDoctorChecks(options) {
|
|
|
5032
5168
|
const bunVersion = options.bunVersion ?? Bun.version;
|
|
5033
5169
|
const request = options.requestJson ?? ((pathname, init) => requestServerJson({ projectRoot }, pathname, init));
|
|
5034
5170
|
const loadConfig = options.loadConfig ?? loadRigConfigOrNull;
|
|
5171
|
+
const progress = options.onProgress ?? (() => {});
|
|
5172
|
+
progress("Checking local toolchain\u2026");
|
|
5035
5173
|
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`)."));
|
|
5174
|
+
progress("Loading rig.config\u2026");
|
|
5036
5175
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
5037
5176
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
5038
5177
|
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync10(resolve16(projectRoot, name)));
|
|
@@ -5051,6 +5190,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5051
5190
|
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));
|
|
5052
5191
|
let server = null;
|
|
5053
5192
|
try {
|
|
5193
|
+
progress("Connecting to the selected Rig server\u2026");
|
|
5054
5194
|
server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);
|
|
5055
5195
|
checks.push(check("server", "Rig server reachable", "pass", `${server.connectionKind} ${server.baseUrl}`));
|
|
5056
5196
|
} catch (error) {
|
|
@@ -5058,18 +5198,21 @@ async function runRigDoctorChecks(options) {
|
|
|
5058
5198
|
}
|
|
5059
5199
|
if (server || options.requestJson) {
|
|
5060
5200
|
try {
|
|
5201
|
+
progress("Checking server status\u2026");
|
|
5061
5202
|
const status = await request("/api/server/status");
|
|
5062
5203
|
checks.push(check("server-status", "server project status", "pass", JSON.stringify(status).slice(0, 180)));
|
|
5063
5204
|
} catch (error) {
|
|
5064
5205
|
checks.push(check("server-status", "server project status", "fail", errorMessage(error), "Run `rig doctor` after the selected server is reachable."));
|
|
5065
5206
|
}
|
|
5066
5207
|
try {
|
|
5208
|
+
progress("Checking GitHub auth\u2026");
|
|
5067
5209
|
const auth = await request("/api/github/auth/status");
|
|
5068
5210
|
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>`."));
|
|
5069
5211
|
} catch (error) {
|
|
5070
5212
|
checks.push(check("github-auth", "GitHub auth", "fail", errorMessage(error), "Authenticate GitHub through Rig and ensure the server exposes auth status."));
|
|
5071
5213
|
}
|
|
5072
5214
|
try {
|
|
5215
|
+
progress("Checking GitHub repo permissions\u2026");
|
|
5073
5216
|
const permissions = await request("/api/github/repo/permissions");
|
|
5074
5217
|
const allowed = permissionAllowsPr2(permissions);
|
|
5075
5218
|
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."));
|
|
@@ -5077,6 +5220,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5077
5220
|
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."));
|
|
5078
5221
|
}
|
|
5079
5222
|
try {
|
|
5223
|
+
progress("Checking GitHub issue labels\u2026");
|
|
5080
5224
|
const labels = await request("/api/workspace/task-labels");
|
|
5081
5225
|
const ready = labelsReady(labels);
|
|
5082
5226
|
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."));
|
|
@@ -5084,6 +5228,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5084
5228
|
checks.push(check("task-labels", "GitHub issue labels", "warn", errorMessage(error), "Run `rig init`/`rig doctor` after label setup is wired on the server."));
|
|
5085
5229
|
}
|
|
5086
5230
|
try {
|
|
5231
|
+
progress("Checking task projection\u2026");
|
|
5087
5232
|
const projection = await request("/api/workspace/task-projection");
|
|
5088
5233
|
checks.push(check("task-projection", "task projection", "pass", JSON.stringify(projection).slice(0, 180)));
|
|
5089
5234
|
} catch (error) {
|
|
@@ -5092,6 +5237,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5092
5237
|
const slug = projectStatusSlug(projectRoot, config);
|
|
5093
5238
|
if (slug) {
|
|
5094
5239
|
try {
|
|
5240
|
+
progress("Checking server project checkout\u2026");
|
|
5095
5241
|
const project = await request(`/api/projects/${encodeURIComponent(slug)}`);
|
|
5096
5242
|
checks.push(check("remote-checkout", "server project checkout", "pass", JSON.stringify(project).slice(0, 180)));
|
|
5097
5243
|
} catch (error) {
|
|
@@ -5106,6 +5252,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5106
5252
|
}
|
|
5107
5253
|
checks.push(githubProjectsCheck(config));
|
|
5108
5254
|
checks.push(prMergeCheck(config));
|
|
5255
|
+
progress("Checking Pi installation\u2026");
|
|
5109
5256
|
const piChecks = await (options.piChecks ?? (() => buildPiSetupChecks()))().catch((error) => [{
|
|
5110
5257
|
ok: false,
|
|
5111
5258
|
label: "pi/pi-rig checks",
|
|
@@ -6028,7 +6175,7 @@ async function executeGithub(context, args) {
|
|
|
6028
6175
|
case "status": {
|
|
6029
6176
|
if (rest.length > 0)
|
|
6030
6177
|
throw new CliError("Usage: rig github auth status", 1);
|
|
6031
|
-
const status = await getGitHubAuthStatusViaServer(context);
|
|
6178
|
+
const status = await withSpinner("Checking GitHub auth on the server\u2026", () => getGitHubAuthStatusViaServer(context), { outputMode: context.outputMode });
|
|
6032
6179
|
printPayload(context, status, `GitHub auth: ${status.authenticated ? "authenticated" : "unauthenticated"}`);
|
|
6033
6180
|
return { ok: true, group: "github", command: "auth status", details: status };
|
|
6034
6181
|
}
|
|
@@ -6039,14 +6186,15 @@ async function executeGithub(context, args) {
|
|
|
6039
6186
|
const token = parsed.value?.trim();
|
|
6040
6187
|
if (!token)
|
|
6041
6188
|
throw new CliError("Missing --token value.", 1, { hint: "Re-run as `rig github auth token --token <token>`." });
|
|
6042
|
-
const result = await postGitHubTokenViaServer(context, token);
|
|
6189
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, token), { outputMode: context.outputMode });
|
|
6043
6190
|
printPayload(context, result, "GitHub token stored on the selected Rig server.");
|
|
6044
6191
|
return { ok: true, group: "github", command: "auth token", details: result };
|
|
6045
6192
|
}
|
|
6046
6193
|
case "import-gh": {
|
|
6047
6194
|
if (rest.length > 0)
|
|
6048
6195
|
throw new CliError("Usage: rig github auth import-gh", 1);
|
|
6049
|
-
const
|
|
6196
|
+
const importedToken = readGhToken();
|
|
6197
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, importedToken), { outputMode: context.outputMode });
|
|
6050
6198
|
printPayload(context, result, "GitHub token imported from gh and stored on the selected Rig server.");
|
|
6051
6199
|
return { ok: true, group: "github", command: "auth import-gh", details: result };
|
|
6052
6200
|
}
|
|
@@ -6059,7 +6207,7 @@ async function executeGithub(context, args) {
|
|
|
6059
6207
|
init_runner();
|
|
6060
6208
|
async function executeDoctor(context, args) {
|
|
6061
6209
|
requireNoExtraArgs(args, "rig doctor");
|
|
6062
|
-
const checks = await runRigDoctorChecks({ projectRoot: context.projectRoot });
|
|
6210
|
+
const checks = await withSpinner("Running doctor checks\u2026", (update) => runRigDoctorChecks({ projectRoot: context.projectRoot, onProgress: update }), { outputMode: context.outputMode });
|
|
6063
6211
|
if (context.outputMode === "text") {
|
|
6064
6212
|
console.log(formatDoctorChecks(checks));
|
|
6065
6213
|
}
|
|
@@ -6366,13 +6514,19 @@ async function executeInspect(context, args) {
|
|
|
6366
6514
|
const requiredTask = requireTask(task, "rig inspect logs --task <task-id>");
|
|
6367
6515
|
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];
|
|
6368
6516
|
if (!latestRun) {
|
|
6369
|
-
const
|
|
6370
|
-
|
|
6371
|
-
|
|
6517
|
+
const fallback = await withSpinner(`Finding runs for ${requiredTask} on the server\u2026`, async (update) => {
|
|
6518
|
+
const serverRuns = await listRunsViaServer(context, { limit: 200 }).catch(() => []);
|
|
6519
|
+
const serverRun = serverRuns.filter((run) => String(run.taskId ?? "") === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
|
|
6520
|
+
if (!serverRun || typeof serverRun.runId !== "string")
|
|
6521
|
+
return null;
|
|
6522
|
+
update(`Reading logs for run ${serverRun.runId}\u2026`);
|
|
6523
|
+
const page = await getRunLogsViaServer(context, serverRun.runId, { limit: 500 });
|
|
6524
|
+
return { runId: serverRun.runId, page };
|
|
6525
|
+
}, { outputMode: context.outputMode });
|
|
6526
|
+
if (!fallback) {
|
|
6372
6527
|
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`." });
|
|
6373
6528
|
}
|
|
6374
|
-
const
|
|
6375
|
-
const entries = Array.isArray(page.entries) ? page.entries : [];
|
|
6529
|
+
const entries = Array.isArray(fallback.page.entries) ? fallback.page.entries : [];
|
|
6376
6530
|
if (context.outputMode === "text") {
|
|
6377
6531
|
for (const entry of entries) {
|
|
6378
6532
|
const record = entry && typeof entry === "object" ? entry : {};
|
|
@@ -6381,9 +6535,9 @@ async function executeInspect(context, args) {
|
|
|
6381
6535
|
console.log([title, detail].filter(Boolean).join(" \u2014 "));
|
|
6382
6536
|
}
|
|
6383
6537
|
if (entries.length === 0)
|
|
6384
|
-
console.log(`(no log entries for run ${
|
|
6538
|
+
console.log(`(no log entries for run ${fallback.runId})`);
|
|
6385
6539
|
}
|
|
6386
|
-
return { ok: true, group: "inspect", command, details: { task: requiredTask, runId:
|
|
6540
|
+
return { ok: true, group: "inspect", command, details: { task: requiredTask, runId: fallback.runId, source: "server", entries } };
|
|
6387
6541
|
}
|
|
6388
6542
|
const logsPath = resolve19(resolveAuthorityRunDir2(context.projectRoot, latestRun.runId), "logs.jsonl");
|
|
6389
6543
|
if (!existsSync12(logsPath)) {
|
|
@@ -6441,7 +6595,7 @@ async function executeInspect(context, args) {
|
|
|
6441
6595
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
6442
6596
|
requireNoExtraArgs(remaining, "rig inspect diff [--task <task-id>]");
|
|
6443
6597
|
if (task) {
|
|
6444
|
-
const files = changedFilesForTask(context.projectRoot, task, false);
|
|
6598
|
+
const files = await withSpinner(`Computing changed files for ${task}\u2026`, async () => changedFilesForTask(context.projectRoot, task, false), { outputMode: context.outputMode });
|
|
6445
6599
|
for (const file of files) {
|
|
6446
6600
|
console.log(file);
|
|
6447
6601
|
}
|
|
@@ -7529,7 +7683,12 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
7529
7683
|
};
|
|
7530
7684
|
let detached = false;
|
|
7531
7685
|
try {
|
|
7532
|
-
await runPiMain([
|
|
7686
|
+
await runPiMain([
|
|
7687
|
+
"--no-extensions",
|
|
7688
|
+
"--no-skills",
|
|
7689
|
+
"--no-prompt-templates",
|
|
7690
|
+
"--no-context-files"
|
|
7691
|
+
], {
|
|
7533
7692
|
extensionFactories: [piRigExtensionFactory]
|
|
7534
7693
|
});
|
|
7535
7694
|
detached = true;
|
|
@@ -7594,8 +7753,9 @@ async function readOperatorSnapshot(context, runId, options = {}) {
|
|
|
7594
7753
|
}
|
|
7595
7754
|
async function attachRunOperatorView(context, input) {
|
|
7596
7755
|
let steered = false;
|
|
7597
|
-
|
|
7598
|
-
|
|
7756
|
+
const attachMessage = input.message?.trim();
|
|
7757
|
+
if (attachMessage) {
|
|
7758
|
+
await withSpinner("Queueing steering message\u2026", () => sendRunPiPromptViaServer(context, input.runId, attachMessage, "steer").catch(() => steerRunViaServer(context, input.runId, attachMessage)), { outputMode: context.outputMode });
|
|
7599
7759
|
steered = true;
|
|
7600
7760
|
}
|
|
7601
7761
|
if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
@@ -7605,7 +7765,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
7605
7765
|
});
|
|
7606
7766
|
}
|
|
7607
7767
|
const surface = createOperatorSurface({ interactive: input.interactive !== false });
|
|
7608
|
-
let snapshot = await readOperatorSnapshot(context, input.runId);
|
|
7768
|
+
let snapshot = await withSpinner(`Connecting to run ${input.runId}\u2026`, () => readOperatorSnapshot(context, input.runId), { outputMode: context.outputMode });
|
|
7609
7769
|
if (context.outputMode === "text") {
|
|
7610
7770
|
surface.renderSnapshot(snapshot);
|
|
7611
7771
|
surface.renderTimeline(snapshot.timeline);
|
|
@@ -7656,9 +7816,6 @@ function normalizeRemoteRunDetails(payload) {
|
|
|
7656
7816
|
};
|
|
7657
7817
|
}
|
|
7658
7818
|
var REMOTE_TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged"]);
|
|
7659
|
-
function isRemoteConnectionSelected(projectRoot) {
|
|
7660
|
-
return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
|
|
7661
|
-
}
|
|
7662
7819
|
async function listRunsForSelectedConnection(context, options = {}) {
|
|
7663
7820
|
if (isRemoteConnectionSelected(context.projectRoot)) {
|
|
7664
7821
|
return { runs: await listRunsViaServer(context, options), source: "server" };
|
|
@@ -7739,7 +7896,7 @@ async function executeRun(context, args) {
|
|
|
7739
7896
|
switch (command) {
|
|
7740
7897
|
case "list": {
|
|
7741
7898
|
requireNoExtraArgs(rest, "rig run list");
|
|
7742
|
-
const { runs, source } = await listRunsForSelectedConnection(context, { limit: 100 });
|
|
7899
|
+
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
7900
|
if (context.outputMode === "text") {
|
|
7744
7901
|
printFormattedOutput(formatRunList(runs, { source }));
|
|
7745
7902
|
await printPendingInboxFooter(context);
|
|
@@ -7812,7 +7969,7 @@ async function executeRun(context, args) {
|
|
|
7812
7969
|
if (!runId) {
|
|
7813
7970
|
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
7971
|
}
|
|
7815
|
-
const record = readAuthorityRun5(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, runId).catch(() => ({})));
|
|
7972
|
+
const record = readAuthorityRun5(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await withSpinner(`Reading run ${runId} from server\u2026`, () => getRunDetailsViaServer(context, runId).catch(() => ({})), { outputMode: context.outputMode }));
|
|
7816
7973
|
if (!record) {
|
|
7817
7974
|
throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see recorded runs." });
|
|
7818
7975
|
}
|
|
@@ -7833,7 +7990,8 @@ async function executeRun(context, args) {
|
|
|
7833
7990
|
}
|
|
7834
7991
|
const renderer = createPiRunStreamRenderer();
|
|
7835
7992
|
let cursor = null;
|
|
7836
|
-
const
|
|
7993
|
+
const timelineRunId = run.value;
|
|
7994
|
+
const page = await withSpinner(`Reading timeline for ${timelineRunId}\u2026`, () => getRunTimelineViaServer(context, timelineRunId, { limit: 500 }), { outputMode: context.outputMode });
|
|
7837
7995
|
const events = Array.isArray(page.entries) ? page.entries.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
|
|
7838
7996
|
cursor = typeof page.nextCursor === "string" ? page.nextCursor : null;
|
|
7839
7997
|
if (context.outputMode === "text") {
|
|
@@ -7910,8 +8068,9 @@ async function executeRun(context, args) {
|
|
|
7910
8068
|
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
8069
|
}
|
|
7912
8070
|
let steered = false;
|
|
7913
|
-
|
|
7914
|
-
|
|
8071
|
+
const steerMessage = messageOption.value?.trim();
|
|
8072
|
+
if (steerMessage) {
|
|
8073
|
+
await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, steerMessage), { outputMode: context.outputMode });
|
|
7915
8074
|
steered = true;
|
|
7916
8075
|
}
|
|
7917
8076
|
const attached = await attachRunOperatorView(context, {
|
|
@@ -7931,7 +8090,7 @@ async function executeRun(context, args) {
|
|
|
7931
8090
|
}
|
|
7932
8091
|
return { ok: true, group: "run", command };
|
|
7933
8092
|
}
|
|
7934
|
-
const summary = isRemoteConnectionSelected(context.projectRoot) ? buildServerRunStatus(await listRunsViaServer(context, { limit: 100 })) : runStatus(context.projectRoot, runtimeContext);
|
|
8093
|
+
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
8094
|
const activeRuns = Array.isArray(summary.activeRuns) ? summary.activeRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
|
|
7936
8095
|
const recentRuns = Array.isArray(summary.recentRuns) ? summary.recentRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
|
|
7937
8096
|
if (context.outputMode === "text") {
|
|
@@ -8008,28 +8167,38 @@ async function executeRun(context, args) {
|
|
|
8008
8167
|
};
|
|
8009
8168
|
}
|
|
8010
8169
|
case "resume": {
|
|
8011
|
-
|
|
8170
|
+
let pending = rest;
|
|
8171
|
+
const runOpt = takeOption(pending, "--run");
|
|
8172
|
+
pending = runOpt.rest;
|
|
8173
|
+
const positional = pending[0] && !pending[0].startsWith("-") ? pending.shift() : undefined;
|
|
8174
|
+
requireNoExtraArgs(pending, "rig run resume [<run-id>] [--run <id>]");
|
|
8175
|
+
const targetRunId = runOpt.value ?? positional ?? null;
|
|
8012
8176
|
if (context.dryRun) {
|
|
8013
8177
|
if (context.outputMode === "text") {
|
|
8014
8178
|
console.log("[dry-run] rig run resume");
|
|
8015
8179
|
}
|
|
8016
8180
|
return { ok: true, group: "run", command };
|
|
8017
8181
|
}
|
|
8018
|
-
const resumed = await runResume(context.projectRoot, runtimeContext);
|
|
8182
|
+
const resumed = await runResume(context.projectRoot, runtimeContext, targetRunId);
|
|
8019
8183
|
if (context.outputMode === "text") {
|
|
8020
8184
|
console.log(`Resumed run: ${resumed.runId}`);
|
|
8021
8185
|
}
|
|
8022
8186
|
return { ok: true, group: "run", command, details: resumed };
|
|
8023
8187
|
}
|
|
8024
8188
|
case "restart": {
|
|
8025
|
-
|
|
8189
|
+
let pending = rest;
|
|
8190
|
+
const runOpt = takeOption(pending, "--run");
|
|
8191
|
+
pending = runOpt.rest;
|
|
8192
|
+
const positional = pending[0] && !pending[0].startsWith("-") ? pending.shift() : undefined;
|
|
8193
|
+
requireNoExtraArgs(pending, "rig run restart [<run-id>] [--run <id>]");
|
|
8194
|
+
const targetRunId = runOpt.value ?? positional ?? null;
|
|
8026
8195
|
if (context.dryRun) {
|
|
8027
8196
|
if (context.outputMode === "text") {
|
|
8028
8197
|
console.log("[dry-run] rig run restart");
|
|
8029
8198
|
}
|
|
8030
8199
|
return { ok: true, group: "run", command };
|
|
8031
8200
|
}
|
|
8032
|
-
const restarted = await runRestart(context.projectRoot, runtimeContext);
|
|
8201
|
+
const restarted = await runRestart(context.projectRoot, runtimeContext, targetRunId);
|
|
8033
8202
|
if (context.outputMode === "text") {
|
|
8034
8203
|
console.log(`Restarted run: ${restarted.runId}`);
|
|
8035
8204
|
}
|
|
@@ -8056,7 +8225,8 @@ async function executeRun(context, args) {
|
|
|
8056
8225
|
}
|
|
8057
8226
|
return { ok: true, group: "run", command, details: { runId, dryRun: true } };
|
|
8058
8227
|
}
|
|
8059
|
-
|
|
8228
|
+
const trimmedMessage = message2.trim();
|
|
8229
|
+
await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, trimmedMessage), { outputMode: context.outputMode });
|
|
8060
8230
|
if (context.outputMode === "text") {
|
|
8061
8231
|
console.log(`Steering message queued for ${runId}.`);
|
|
8062
8232
|
}
|
|
@@ -8077,7 +8247,7 @@ async function executeRun(context, args) {
|
|
|
8077
8247
|
};
|
|
8078
8248
|
}
|
|
8079
8249
|
if (runId) {
|
|
8080
|
-
const stopped = await stopRunViaServer(context, runId);
|
|
8250
|
+
const stopped = await withSpinner(`Requesting stop for ${runId}\u2026`, () => stopRunViaServer(context, runId), { outputMode: context.outputMode });
|
|
8081
8251
|
if (context.outputMode === "text")
|
|
8082
8252
|
console.log(`Stop requested: ${runId}`);
|
|
8083
8253
|
return { ok: true, group: "run", command, details: stopped };
|
|
@@ -8323,15 +8493,12 @@ async function executeServer(context, args, options) {
|
|
|
8323
8493
|
|
|
8324
8494
|
// packages/cli/src/commands/stats.ts
|
|
8325
8495
|
init_runner();
|
|
8326
|
-
import
|
|
8327
|
-
import {
|
|
8328
|
-
listAuthorityRuns as listAuthorityRuns3,
|
|
8329
|
-
projectRunFromJournal
|
|
8330
|
-
} from "@rig/runtime/control-plane/authority-files";
|
|
8496
|
+
import pc6 from "picocolors";
|
|
8497
|
+
import { computeRigStats } from "@rig/runtime/control-plane/native/run-stats";
|
|
8331
8498
|
|
|
8332
8499
|
// packages/cli/src/commands/_help-catalog.ts
|
|
8333
8500
|
import { intro as intro3, log as log4, note as note4, outro as outro3 } from "@clack/prompts";
|
|
8334
|
-
import
|
|
8501
|
+
import pc5 from "picocolors";
|
|
8335
8502
|
var TOP_LEVEL_SECTIONS = [
|
|
8336
8503
|
{
|
|
8337
8504
|
title: "Start here",
|
|
@@ -8619,13 +8786,13 @@ var ADVANCED_COMMANDS = [
|
|
|
8619
8786
|
];
|
|
8620
8787
|
var ALL_GROUPS = [...PRIMARY_GROUPS, ...ADVANCED_GROUPS];
|
|
8621
8788
|
function heading(title) {
|
|
8622
|
-
return
|
|
8789
|
+
return pc5.bold(pc5.cyan(title));
|
|
8623
8790
|
}
|
|
8624
8791
|
function renderRigBanner(version) {
|
|
8625
|
-
const m = (s) =>
|
|
8626
|
-
const c = (s) =>
|
|
8627
|
-
const y = (s) =>
|
|
8628
|
-
const d = (s) =>
|
|
8792
|
+
const m = (s) => pc5.bold(pc5.magenta(s));
|
|
8793
|
+
const c = (s) => pc5.bold(pc5.cyan(s));
|
|
8794
|
+
const y = (s) => pc5.yellow(s);
|
|
8795
|
+
const d = (s) => pc5.dim(s);
|
|
8629
8796
|
const lines = [
|
|
8630
8797
|
m(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + d(" \u2591\u2592\u2593\u2588 "),
|
|
8631
8798
|
m(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ") + d("\u2593\u2592\u2591"),
|
|
@@ -8634,7 +8801,7 @@ function renderRigBanner(version) {
|
|
|
8634
8801
|
y(" \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + d(" \u2592\u2591"),
|
|
8635
8802
|
y(" \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D "),
|
|
8636
8803
|
"",
|
|
8637
|
-
` ${c("\u25E2\u25E4")} ${
|
|
8804
|
+
` ${c("\u25E2\u25E4")} ${pc5.bold("the control rig for autonomous coding agents")} ${m("//")} ${d("you are the operator")}`,
|
|
8638
8805
|
version ? ` ${d(`v${version} \xB7 jack in: rig task run --next`)}` : ` ${d("jack in: rig task run --next")}`
|
|
8639
8806
|
];
|
|
8640
8807
|
return lines.join(`
|
|
@@ -8642,7 +8809,7 @@ function renderRigBanner(version) {
|
|
|
8642
8809
|
}
|
|
8643
8810
|
function commandLine(command, description) {
|
|
8644
8811
|
const commandColumn = command.length >= 38 ? `${command} ` : command.padEnd(38);
|
|
8645
|
-
return `${
|
|
8812
|
+
return `${pc5.dim("\u2502")} ${pc5.bold(commandColumn)} ${description}`;
|
|
8646
8813
|
}
|
|
8647
8814
|
function renderCommandBlock(commands) {
|
|
8648
8815
|
return commands.map((entry) => commandLine(entry.command, entry.description)).join(`
|
|
@@ -8652,37 +8819,37 @@ function renderGroup(group) {
|
|
|
8652
8819
|
const lines = [
|
|
8653
8820
|
`${heading(`rig ${group.name}`)} \u2014 ${group.summary}`,
|
|
8654
8821
|
"",
|
|
8655
|
-
|
|
8822
|
+
pc5.bold("Usage"),
|
|
8656
8823
|
...group.usage.map((line) => ` ${line}`),
|
|
8657
8824
|
"",
|
|
8658
|
-
|
|
8825
|
+
pc5.bold("Commands"),
|
|
8659
8826
|
...group.commands.map((entry) => commandLine(entry.command, entry.description))
|
|
8660
8827
|
];
|
|
8661
8828
|
if (group.examples?.length) {
|
|
8662
|
-
lines.push("",
|
|
8829
|
+
lines.push("", pc5.bold("Examples"), ...group.examples.map((line) => ` ${pc5.dim("$")} ${line}`));
|
|
8663
8830
|
}
|
|
8664
8831
|
if (group.next?.length) {
|
|
8665
|
-
lines.push("",
|
|
8832
|
+
lines.push("", pc5.bold("Next steps"), ...group.next.map((line) => ` ${pc5.dim("\u203A")} ${line}`));
|
|
8666
8833
|
}
|
|
8667
8834
|
if (group.advanced?.length) {
|
|
8668
|
-
lines.push("",
|
|
8835
|
+
lines.push("", pc5.bold("Compatibility / advanced"), ...group.advanced.map((line) => ` ${pc5.dim("\u203A")} ${line}`));
|
|
8669
8836
|
}
|
|
8670
8837
|
return lines.join(`
|
|
8671
8838
|
`);
|
|
8672
8839
|
}
|
|
8673
8840
|
function renderTopLevelHelp() {
|
|
8674
8841
|
return [
|
|
8675
|
-
`${heading("rig")} ${
|
|
8676
|
-
|
|
8842
|
+
`${heading("rig")} ${pc5.dim("\u2014 server-owned task/run control plane for Pi-backed engineering work")}`,
|
|
8843
|
+
pc5.dim("Current path: select a server, choose a task, submit a run, attach with native Pi, clear inbox gates."),
|
|
8677
8844
|
"",
|
|
8678
8845
|
...TOP_LEVEL_SECTIONS.flatMap((section) => [
|
|
8679
|
-
`${
|
|
8846
|
+
`${pc5.bold(pc5.magenta(`\u25C7 ${section.title}`))} \u2014 ${pc5.dim(section.subtitle)}`,
|
|
8680
8847
|
renderCommandBlock(section.commands),
|
|
8681
8848
|
""
|
|
8682
8849
|
]),
|
|
8683
|
-
|
|
8850
|
+
pc5.dim("More: `rig help --advanced` for dev/compatibility commands; `rig <group> --help` for rich per-group help; `rig --version` for the installed version."),
|
|
8684
8851
|
"",
|
|
8685
|
-
|
|
8852
|
+
pc5.bold("Global options"),
|
|
8686
8853
|
commandLine("--project <path>", "Use a project root instead of auto-discovery."),
|
|
8687
8854
|
commandLine("--json", "Emit structured output for scripts/agents."),
|
|
8688
8855
|
commandLine("--dry-run", "Print the command plan without mutating state.")
|
|
@@ -8693,16 +8860,16 @@ function renderAdvancedHelp() {
|
|
|
8693
8860
|
return [
|
|
8694
8861
|
`${heading("rig advanced")} \u2014 compatibility, diagnostics, and internal surfaces`,
|
|
8695
8862
|
"",
|
|
8696
|
-
|
|
8863
|
+
pc5.bold("Primary groups"),
|
|
8697
8864
|
" init, server, task, run, inbox, repo, plugin, inspect, stats, doctor, github",
|
|
8698
8865
|
"",
|
|
8699
|
-
|
|
8866
|
+
pc5.bold("Advanced commands"),
|
|
8700
8867
|
...ADVANCED_COMMANDS.map((entry) => commandLine(entry.command, entry.description)),
|
|
8701
8868
|
"",
|
|
8702
|
-
|
|
8869
|
+
pc5.bold("Advanced groups"),
|
|
8703
8870
|
...ADVANCED_GROUPS.map((group) => commandLine(group.name, group.summary)),
|
|
8704
8871
|
"",
|
|
8705
|
-
|
|
8872
|
+
pc5.dim("All groups remain callable. Prefer `rig server`, `rig task`, `rig run`, and `rig inbox` for day-to-day work.")
|
|
8706
8873
|
].join(`
|
|
8707
8874
|
`);
|
|
8708
8875
|
}
|
|
@@ -8816,90 +8983,6 @@ function parseSinceOption(value, now = new Date) {
|
|
|
8816
8983
|
}
|
|
8817
8984
|
return new Date(parsed);
|
|
8818
8985
|
}
|
|
8819
|
-
function median(values) {
|
|
8820
|
-
if (values.length === 0)
|
|
8821
|
-
return null;
|
|
8822
|
-
const sorted = [...values].sort((left, right) => left - right);
|
|
8823
|
-
const middle = Math.floor(sorted.length / 2);
|
|
8824
|
-
const upper = sorted[middle];
|
|
8825
|
-
if (sorted.length % 2 === 1)
|
|
8826
|
-
return upper;
|
|
8827
|
-
return (sorted[middle - 1] + upper) / 2;
|
|
8828
|
-
}
|
|
8829
|
-
function rate(part, total) {
|
|
8830
|
-
if (total === 0)
|
|
8831
|
-
return null;
|
|
8832
|
-
return part / total;
|
|
8833
|
-
}
|
|
8834
|
-
function parseTimestamp(value) {
|
|
8835
|
-
if (!value)
|
|
8836
|
-
return null;
|
|
8837
|
-
const parsed = Date.parse(value);
|
|
8838
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
8839
|
-
}
|
|
8840
|
-
function computeRigStats(projectRoot, options = {}) {
|
|
8841
|
-
const since = options.since ?? null;
|
|
8842
|
-
const sinceMs = since ? since.getTime() : null;
|
|
8843
|
-
const runs = listAuthorityRuns3(projectRoot).filter((run) => {
|
|
8844
|
-
if (sinceMs === null)
|
|
8845
|
-
return true;
|
|
8846
|
-
const createdAt = parseTimestamp(run.createdAt) ?? parseTimestamp(run.updatedAt);
|
|
8847
|
-
return createdAt !== null && createdAt >= sinceMs;
|
|
8848
|
-
});
|
|
8849
|
-
const statusCounts = {};
|
|
8850
|
-
const completionDurations = [];
|
|
8851
|
-
let steeringTotal = 0;
|
|
8852
|
-
let stallTotal = 0;
|
|
8853
|
-
let approvalsApproved = 0;
|
|
8854
|
-
let approvalsRejected = 0;
|
|
8855
|
-
let approvalsPending = 0;
|
|
8856
|
-
for (const run of runs) {
|
|
8857
|
-
const status = run.status ?? "unknown";
|
|
8858
|
-
statusCounts[status] = (statusCounts[status] ?? 0) + 1;
|
|
8859
|
-
if (status === "completed") {
|
|
8860
|
-
const createdAt = parseTimestamp(run.createdAt);
|
|
8861
|
-
const completedAt = parseTimestamp(run.completedAt);
|
|
8862
|
-
if (createdAt !== null && completedAt !== null && completedAt >= createdAt) {
|
|
8863
|
-
completionDurations.push(completedAt - createdAt);
|
|
8864
|
-
}
|
|
8865
|
-
}
|
|
8866
|
-
const projection = projectRunFromJournal(projectRoot, run.runId);
|
|
8867
|
-
if (!projection)
|
|
8868
|
-
continue;
|
|
8869
|
-
steeringTotal += projection.steeringCount;
|
|
8870
|
-
stallTotal += projection.stallCount;
|
|
8871
|
-
approvalsPending += projection.pendingApprovals.length;
|
|
8872
|
-
for (const resolved of projection.resolvedApprovals) {
|
|
8873
|
-
if (resolved.decision === "approve")
|
|
8874
|
-
approvalsApproved += 1;
|
|
8875
|
-
else
|
|
8876
|
-
approvalsRejected += 1;
|
|
8877
|
-
}
|
|
8878
|
-
}
|
|
8879
|
-
const totalRuns = runs.length;
|
|
8880
|
-
const completedRuns = statusCounts["completed"] ?? 0;
|
|
8881
|
-
const failedRuns = statusCounts["failed"] ?? 0;
|
|
8882
|
-
const needsAttentionRuns = statusCounts["needs-attention"] ?? 0;
|
|
8883
|
-
return {
|
|
8884
|
-
since: since ? since.toISOString() : null,
|
|
8885
|
-
totalRuns,
|
|
8886
|
-
statusCounts,
|
|
8887
|
-
completedRuns,
|
|
8888
|
-
failedRuns,
|
|
8889
|
-
needsAttentionRuns,
|
|
8890
|
-
completionRate: rate(completedRuns, totalRuns),
|
|
8891
|
-
failureRate: rate(failedRuns, totalRuns),
|
|
8892
|
-
needsAttentionRate: rate(needsAttentionRuns, totalRuns),
|
|
8893
|
-
medianCompletionMs: median(completionDurations),
|
|
8894
|
-
steeringTotal,
|
|
8895
|
-
steeringPerRun: totalRuns === 0 ? null : steeringTotal / totalRuns,
|
|
8896
|
-
stallTotal,
|
|
8897
|
-
approvalsRequested: approvalsApproved + approvalsRejected + approvalsPending,
|
|
8898
|
-
approvalsApproved,
|
|
8899
|
-
approvalsRejected,
|
|
8900
|
-
approvalsPending
|
|
8901
|
-
};
|
|
8902
|
-
}
|
|
8903
8986
|
function formatPercent(value) {
|
|
8904
8987
|
if (value === null)
|
|
8905
8988
|
return "\u2014";
|
|
@@ -8934,11 +9017,11 @@ function formatStatsReport(stats) {
|
|
|
8934
9017
|
const keyWidth = Math.max(...rows.map(([key]) => key.length));
|
|
8935
9018
|
const lines = [
|
|
8936
9019
|
formatSection("Fleet stats", window),
|
|
8937
|
-
...rows.map(([key, value]) => `${
|
|
9020
|
+
...rows.map(([key, value]) => `${pc6.dim("\u2502")} ${pc6.dim(key.padEnd(keyWidth + 2))} ${value}`)
|
|
8938
9021
|
];
|
|
8939
9022
|
const otherStatuses = Object.entries(stats.statusCounts).filter(([status]) => !["completed", "failed", "needs-attention"].includes(status)).sort(([, left], [, right]) => right - left);
|
|
8940
9023
|
if (otherStatuses.length > 0) {
|
|
8941
|
-
lines.push(`${
|
|
9024
|
+
lines.push(`${pc6.dim("\u2502")} ${pc6.dim("other".padEnd(keyWidth + 2))} ${otherStatuses.map(([status, count]) => `${status}:${count}`).join(" ")}`);
|
|
8942
9025
|
}
|
|
8943
9026
|
lines.push("", ...formatNextSteps([
|
|
8944
9027
|
"Inspect a run: `rig run list` then `rig run show <run-id>`",
|
|
@@ -8957,7 +9040,8 @@ async function executeStats(context, args) {
|
|
|
8957
9040
|
const sinceResult = takeOption(pending, "--since");
|
|
8958
9041
|
requireNoExtraArgs(sinceResult.rest, "rig stats [show] [--since <7d|30d|ISO date>]");
|
|
8959
9042
|
const since = parseSinceOption(sinceResult.value);
|
|
8960
|
-
const
|
|
9043
|
+
const remoteStats = isRemoteConnectionSelected(context.projectRoot);
|
|
9044
|
+
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 });
|
|
8961
9045
|
if (context.outputMode === "text") {
|
|
8962
9046
|
printFormattedOutput(formatStatsReport(stats));
|
|
8963
9047
|
}
|
|
@@ -9212,7 +9296,7 @@ async function executeTask(context, args, options) {
|
|
|
9212
9296
|
pending = rawResult.rest;
|
|
9213
9297
|
const { filters, rest: remaining } = parseTaskFilters(pending);
|
|
9214
9298
|
requireNoExtraArgs(remaining, "rig task list [--raw] [--assignee <login|me|@me>] [--state open|closed] [--status <status>] [--limit <n>]");
|
|
9215
|
-
const tasks = await listWorkspaceTasksViaServer(context, filters);
|
|
9299
|
+
const tasks = await withSpinner("Reading tasks from server\u2026", () => listWorkspaceTasksViaServer(context, filters), { outputMode: context.outputMode });
|
|
9216
9300
|
if (context.outputMode === "text") {
|
|
9217
9301
|
const renderedTasks = rawResult.value ? tasks.map((task) => summarizeTask(task, { raw: true })) : tasks.map((task) => summarizeTask(task));
|
|
9218
9302
|
printFormattedOutput(formatTaskList(renderedTasks, { raw: rawResult.value }));
|
|
@@ -9236,7 +9320,7 @@ async function executeTask(context, args, options) {
|
|
|
9236
9320
|
const taskId3 = normalizeTaskRunTaskId(taskOption.value ?? positional);
|
|
9237
9321
|
if (!taskId3)
|
|
9238
9322
|
throw new CliError("task show requires a task id.", 2, { hint: "Run `rig task list` to find ids, then `rig task show <id>`." });
|
|
9239
|
-
const task = await getWorkspaceTaskViaServer(context, taskId3);
|
|
9323
|
+
const task = await withSpinner(`Reading task ${taskId3} from server\u2026`, () => getWorkspaceTaskViaServer(context, taskId3), { outputMode: context.outputMode });
|
|
9240
9324
|
if (!task)
|
|
9241
9325
|
throw new CliError(`Task not found: ${taskId3}`, 3, { hint: "Run `rig task list` to see available tasks." });
|
|
9242
9326
|
const summary = summarizeTask(task, { raw: true });
|
|
@@ -9248,7 +9332,7 @@ async function executeTask(context, args, options) {
|
|
|
9248
9332
|
case "next": {
|
|
9249
9333
|
const { filters, rest: remaining } = parseTaskFilters(rest);
|
|
9250
9334
|
requireNoExtraArgs(remaining, "rig task next [--assignee <login|me|@me>] [--state open|closed] [--status <status>] [--limit <n>]");
|
|
9251
|
-
const selected = await selectNextWorkspaceTaskViaServer(context, filters);
|
|
9335
|
+
const selected = await withSpinner("Selecting next ready task\u2026", () => selectNextWorkspaceTaskViaServer(context, filters), { outputMode: context.outputMode });
|
|
9252
9336
|
if (context.outputMode === "text") {
|
|
9253
9337
|
if (selected.task) {
|
|
9254
9338
|
printFormattedOutput(formatTaskCard(summarizeTask(selected.task, { raw: true }), { title: "Selected task", selected: true }));
|
|
@@ -9398,7 +9482,7 @@ async function executeTask(context, args, options) {
|
|
|
9398
9482
|
let selectedTask = null;
|
|
9399
9483
|
let selectedTaskId = normalizeTaskRunTaskId(taskResult.value) ?? positionalTaskId;
|
|
9400
9484
|
if (nextResult.value) {
|
|
9401
|
-
const selected = await selectNextWorkspaceTaskViaServer(context, filterResult.filters);
|
|
9485
|
+
const selected = await withSpinner("Selecting next ready task\u2026", () => selectNextWorkspaceTaskViaServer(context, filterResult.filters), { outputMode: context.outputMode });
|
|
9402
9486
|
selectedTask = selected.task;
|
|
9403
9487
|
selectedTaskId = selected.task ? readTaskId(selected.task) : null;
|
|
9404
9488
|
if (!selectedTaskId) {
|
|
@@ -9409,7 +9493,7 @@ async function executeTask(context, args, options) {
|
|
|
9409
9493
|
if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
9410
9494
|
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);
|
|
9411
9495
|
}
|
|
9412
|
-
const tasks = await listWorkspaceTasksViaServer(context, filterResult.filters);
|
|
9496
|
+
const tasks = await withSpinner("Reading tasks from server\u2026", () => listWorkspaceTasksViaServer(context, filterResult.filters), { outputMode: context.outputMode });
|
|
9413
9497
|
selectedTask = await selectTaskWithTextPicker(tasks);
|
|
9414
9498
|
selectedTaskId = selectedTask ? readTaskId(selectedTask) : null;
|
|
9415
9499
|
if (!selectedTaskId) {
|
|
@@ -9419,13 +9503,13 @@ async function executeTask(context, args, options) {
|
|
|
9419
9503
|
await runProjectMainSyncPreflight(context, { disabled: skipProjectSyncResult.value });
|
|
9420
9504
|
const projectDefaults = await loadTaskRunProjectDefaults(context.projectRoot);
|
|
9421
9505
|
const runtimeAdapter = normalizeRuntimeAdapter(runtimeAdapterResult.value ?? projectDefaults.runtimeAdapter);
|
|
9422
|
-
await runFastTaskRunPreflight(context, {
|
|
9506
|
+
await withSpinner("Running task-run preflight\u2026", () => runFastTaskRunPreflight(context, {
|
|
9423
9507
|
taskId: selectedTaskId,
|
|
9424
9508
|
runtimeAdapter
|
|
9425
|
-
});
|
|
9509
|
+
}), { outputMode: context.outputMode });
|
|
9426
9510
|
const dirtyBaseline = await resolveDirtyBaselineForTaskRun(context, dirtyBaselineResult.value);
|
|
9427
9511
|
const prMode = noPrResult.value ? "off" : normalizePrMode(prResult.value) ?? projectDefaults.prMode;
|
|
9428
|
-
const submitted = await submitTaskRunViaServer(context, {
|
|
9512
|
+
const submitted = await withSpinner("Dispatching run\u2026", () => submitTaskRunViaServer(context, {
|
|
9429
9513
|
runId: context.runId,
|
|
9430
9514
|
taskId: selectedTaskId ?? undefined,
|
|
9431
9515
|
title: titleResult.value ?? undefined,
|
|
@@ -9436,7 +9520,7 @@ async function executeTask(context, args, options) {
|
|
|
9436
9520
|
initialPrompt: initialPromptResult.value ?? undefined,
|
|
9437
9521
|
baselineMode: dirtyBaseline.mode,
|
|
9438
9522
|
prMode
|
|
9439
|
-
});
|
|
9523
|
+
}), { outputMode: context.outputMode });
|
|
9440
9524
|
let attachDetails = null;
|
|
9441
9525
|
if (!detachResult.value && context.outputMode === "text") {
|
|
9442
9526
|
printFormattedOutput(formatSubmittedRun({
|
|
@@ -11614,7 +11698,7 @@ async function executeSetup(context, args) {
|
|
|
11614
11698
|
case "check":
|
|
11615
11699
|
requireNoExtraArgs(rest, `rig setup ${command}`);
|
|
11616
11700
|
{
|
|
11617
|
-
const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot));
|
|
11701
|
+
const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot, context.outputMode));
|
|
11618
11702
|
return { ok: true, group: "setup", command, details: { checks, failures: countDoctorFailures(checks) } };
|
|
11619
11703
|
}
|
|
11620
11704
|
case "setup":
|
|
@@ -11623,7 +11707,7 @@ async function executeSetup(context, args) {
|
|
|
11623
11707
|
return { ok: true, group: "setup", command };
|
|
11624
11708
|
case "preflight":
|
|
11625
11709
|
requireNoExtraArgs(rest, "rig setup preflight");
|
|
11626
|
-
await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot));
|
|
11710
|
+
await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot, context.outputMode));
|
|
11627
11711
|
return { ok: true, group: "setup", command };
|
|
11628
11712
|
default:
|
|
11629
11713
|
throw new CliError(`Unknown setup command: ${command}`, 1, { hint: "Run `rig setup --help` \u2014 commands are bootstrap|check|preflight." });
|
|
@@ -11644,8 +11728,8 @@ function runSetupInit(projectRoot) {
|
|
|
11644
11728
|
}
|
|
11645
11729
|
console.log("Harness directories ready.");
|
|
11646
11730
|
}
|
|
11647
|
-
async function runSetupCheck(projectRoot) {
|
|
11648
|
-
const doctorChecks = await runRigDoctorChecks({ projectRoot });
|
|
11731
|
+
async function runSetupCheck(projectRoot, outputMode = "text") {
|
|
11732
|
+
const doctorChecks = await withSpinner("Running setup checks\u2026", (update) => runRigDoctorChecks({ projectRoot, onProgress: update }), { outputMode });
|
|
11649
11733
|
console.log(formatDoctorChecks(doctorChecks));
|
|
11650
11734
|
const failures = countDoctorFailures(doctorChecks);
|
|
11651
11735
|
if (failures > 0) {
|
|
@@ -11653,8 +11737,8 @@ async function runSetupCheck(projectRoot) {
|
|
|
11653
11737
|
}
|
|
11654
11738
|
return doctorChecks;
|
|
11655
11739
|
}
|
|
11656
|
-
async function runSetupPreflight(projectRoot) {
|
|
11657
|
-
await runSetupCheck(projectRoot);
|
|
11740
|
+
async function runSetupPreflight(projectRoot, outputMode = "text") {
|
|
11741
|
+
await runSetupCheck(projectRoot, outputMode);
|
|
11658
11742
|
const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
|
|
11659
11743
|
if (existsSync15(validationRoot)) {
|
|
11660
11744
|
const validators = readdirSync3(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|