@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/index.js
CHANGED
|
@@ -2919,6 +2919,9 @@ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
|
2919
2919
|
return;
|
|
2920
2920
|
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
2921
2921
|
}
|
|
2922
|
+
function isRemoteConnectionSelected(projectRoot) {
|
|
2923
|
+
return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
|
|
2924
|
+
}
|
|
2922
2925
|
|
|
2923
2926
|
// packages/cli/src/commands/_server-client.ts
|
|
2924
2927
|
init_runner();
|
|
@@ -2926,6 +2929,15 @@ import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
|
|
|
2926
2929
|
import { resolve as resolve11 } from "path";
|
|
2927
2930
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
2928
2931
|
var scopedGitHubBearerTokens = new Map;
|
|
2932
|
+
var serverPhaseListener = null;
|
|
2933
|
+
function setServerPhaseListener(listener) {
|
|
2934
|
+
const previous = serverPhaseListener;
|
|
2935
|
+
serverPhaseListener = listener;
|
|
2936
|
+
return previous;
|
|
2937
|
+
}
|
|
2938
|
+
function reportServerPhase(label) {
|
|
2939
|
+
serverPhaseListener?.(label);
|
|
2940
|
+
}
|
|
2929
2941
|
function cleanToken(value) {
|
|
2930
2942
|
const trimmed = value?.trim();
|
|
2931
2943
|
return trimmed ? trimmed : null;
|
|
@@ -2972,6 +2984,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2972
2984
|
try {
|
|
2973
2985
|
const selected = resolveSelectedConnection(projectRoot);
|
|
2974
2986
|
if (selected?.connection.kind === "remote") {
|
|
2987
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
2975
2988
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
2976
2989
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
2977
2990
|
return {
|
|
@@ -2981,6 +2994,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2981
2994
|
serverProjectRoot
|
|
2982
2995
|
};
|
|
2983
2996
|
}
|
|
2997
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
2984
2998
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
2985
2999
|
return {
|
|
2986
3000
|
baseUrl: connection.baseUrl,
|
|
@@ -3097,6 +3111,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
3097
3111
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
3098
3112
|
if (server.serverProjectRoot)
|
|
3099
3113
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
3114
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
3100
3115
|
let response;
|
|
3101
3116
|
try {
|
|
3102
3117
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -4617,6 +4632,127 @@ function formatConnectionStatus(selected, connections) {
|
|
|
4617
4632
|
`);
|
|
4618
4633
|
}
|
|
4619
4634
|
|
|
4635
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
4636
|
+
import pc4 from "picocolors";
|
|
4637
|
+
|
|
4638
|
+
// packages/cli/src/commands/_spinner.ts
|
|
4639
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
4640
|
+
function createTtySpinner(input) {
|
|
4641
|
+
const output = input.output ?? process.stdout;
|
|
4642
|
+
const isTty = output.isTTY === true;
|
|
4643
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
4644
|
+
let label = input.label;
|
|
4645
|
+
let frame = 0;
|
|
4646
|
+
let paused = false;
|
|
4647
|
+
let stopped = false;
|
|
4648
|
+
let lastPrintedLabel = "";
|
|
4649
|
+
const render = () => {
|
|
4650
|
+
if (stopped || paused)
|
|
4651
|
+
return;
|
|
4652
|
+
if (!isTty) {
|
|
4653
|
+
if (label !== lastPrintedLabel) {
|
|
4654
|
+
output.write(`${label}
|
|
4655
|
+
`);
|
|
4656
|
+
lastPrintedLabel = label;
|
|
4657
|
+
}
|
|
4658
|
+
return;
|
|
4659
|
+
}
|
|
4660
|
+
frame = (frame + 1) % frames.length;
|
|
4661
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
4662
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
4663
|
+
};
|
|
4664
|
+
const clearLine = () => {
|
|
4665
|
+
if (isTty)
|
|
4666
|
+
output.write("\r\x1B[2K");
|
|
4667
|
+
};
|
|
4668
|
+
render();
|
|
4669
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
4670
|
+
return {
|
|
4671
|
+
setLabel(next) {
|
|
4672
|
+
label = next;
|
|
4673
|
+
render();
|
|
4674
|
+
},
|
|
4675
|
+
pause() {
|
|
4676
|
+
paused = true;
|
|
4677
|
+
clearLine();
|
|
4678
|
+
},
|
|
4679
|
+
resume() {
|
|
4680
|
+
if (stopped)
|
|
4681
|
+
return;
|
|
4682
|
+
paused = false;
|
|
4683
|
+
render();
|
|
4684
|
+
},
|
|
4685
|
+
stop(finalLine) {
|
|
4686
|
+
if (stopped)
|
|
4687
|
+
return;
|
|
4688
|
+
stopped = true;
|
|
4689
|
+
if (timer)
|
|
4690
|
+
clearInterval(timer);
|
|
4691
|
+
clearLine();
|
|
4692
|
+
if (finalLine)
|
|
4693
|
+
output.write(`${finalLine}
|
|
4694
|
+
`);
|
|
4695
|
+
}
|
|
4696
|
+
};
|
|
4697
|
+
}
|
|
4698
|
+
|
|
4699
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
4700
|
+
var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
4701
|
+
var DONE_SYMBOL = pc4.green("\u25C7");
|
|
4702
|
+
var FAIL_SYMBOL = pc4.red("\u25A0");
|
|
4703
|
+
var activeUpdate = null;
|
|
4704
|
+
async function withSpinner(label, work, options = {}) {
|
|
4705
|
+
if (options.outputMode === "json") {
|
|
4706
|
+
return work(() => {});
|
|
4707
|
+
}
|
|
4708
|
+
if (activeUpdate) {
|
|
4709
|
+
const outer = activeUpdate;
|
|
4710
|
+
outer(label);
|
|
4711
|
+
return work(outer);
|
|
4712
|
+
}
|
|
4713
|
+
const output = options.output ?? process.stderr;
|
|
4714
|
+
const isTty = output.isTTY === true;
|
|
4715
|
+
let lastLabel = label;
|
|
4716
|
+
if (!isTty) {
|
|
4717
|
+
output.write(`${label}
|
|
4718
|
+
`);
|
|
4719
|
+
const update2 = (next) => {
|
|
4720
|
+
lastLabel = next;
|
|
4721
|
+
};
|
|
4722
|
+
activeUpdate = update2;
|
|
4723
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
4724
|
+
try {
|
|
4725
|
+
return await work(update2);
|
|
4726
|
+
} finally {
|
|
4727
|
+
activeUpdate = null;
|
|
4728
|
+
setServerPhaseListener(previousListener2);
|
|
4729
|
+
}
|
|
4730
|
+
}
|
|
4731
|
+
const spinner2 = createTtySpinner({
|
|
4732
|
+
label,
|
|
4733
|
+
output,
|
|
4734
|
+
frames: CLACK_SPINNER_FRAMES,
|
|
4735
|
+
styleFrame: (frame) => pc4.magenta(frame)
|
|
4736
|
+
});
|
|
4737
|
+
const update = (next) => {
|
|
4738
|
+
lastLabel = next;
|
|
4739
|
+
spinner2.setLabel(next);
|
|
4740
|
+
};
|
|
4741
|
+
activeUpdate = update;
|
|
4742
|
+
const previousListener = setServerPhaseListener(update);
|
|
4743
|
+
try {
|
|
4744
|
+
const result = await work(update);
|
|
4745
|
+
spinner2.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
4746
|
+
return result;
|
|
4747
|
+
} catch (error) {
|
|
4748
|
+
spinner2.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
4749
|
+
throw error;
|
|
4750
|
+
} finally {
|
|
4751
|
+
activeUpdate = null;
|
|
4752
|
+
setServerPhaseListener(previousListener);
|
|
4753
|
+
}
|
|
4754
|
+
}
|
|
4755
|
+
|
|
4620
4756
|
// packages/cli/src/commands/inbox.ts
|
|
4621
4757
|
async function listInboxRecords(context, kind, filters) {
|
|
4622
4758
|
const params = new URLSearchParams;
|
|
@@ -4721,7 +4857,7 @@ async function executeInbox(context, args) {
|
|
|
4721
4857
|
const task = takeOption(pending, "--task");
|
|
4722
4858
|
pending = task.rest;
|
|
4723
4859
|
requireNoExtraArgs(pending, "rig inbox approvals [--run <id>] [--task <id>]");
|
|
4724
|
-
const approvals = await listInboxRecords(context, "approvals", { run: run.value, task: task.value });
|
|
4860
|
+
const approvals = await withSpinner("Reading approvals from server\u2026", () => listInboxRecords(context, "approvals", { run: run.value, task: task.value }), { outputMode: context.outputMode });
|
|
4725
4861
|
renderList(context, "approvals", approvals);
|
|
4726
4862
|
return { ok: true, group: "inbox", command, details: { approvals } };
|
|
4727
4863
|
}
|
|
@@ -4732,7 +4868,7 @@ async function executeInbox(context, args) {
|
|
|
4732
4868
|
const task = takeOption(pending, "--task");
|
|
4733
4869
|
pending = task.rest;
|
|
4734
4870
|
requireNoExtraArgs(pending, "rig inbox inputs [--run <id>] [--task <id>]");
|
|
4735
|
-
const requests = await listInboxRecords(context, "inputs", { run: run.value, task: task.value });
|
|
4871
|
+
const requests = await withSpinner("Reading input requests from server\u2026", () => listInboxRecords(context, "inputs", { run: run.value, task: task.value }), { outputMode: context.outputMode });
|
|
4736
4872
|
renderList(context, "inputs", requests);
|
|
4737
4873
|
return { ok: true, group: "inbox", command, details: { requests } };
|
|
4738
4874
|
}
|
|
@@ -4753,7 +4889,7 @@ async function executeInbox(context, args) {
|
|
|
4753
4889
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
4754
4890
|
throw new CliError("decision must be approve or reject.", 1, { hint: "Re-run as `rig inbox approve --run <id> --request <id> --decision approve|reject`." });
|
|
4755
4891
|
}
|
|
4756
|
-
const result = await requestServerJson(context, "/api/inbox/approvals/resolve", {
|
|
4892
|
+
const result = await withSpinner(`Resolving approval ${request.value}\u2026`, () => requestServerJson(context, "/api/inbox/approvals/resolve", {
|
|
4757
4893
|
method: "POST",
|
|
4758
4894
|
headers: { "content-type": "application/json" },
|
|
4759
4895
|
body: JSON.stringify({
|
|
@@ -4762,7 +4898,7 @@ async function executeInbox(context, args) {
|
|
|
4762
4898
|
decision: decision.value,
|
|
4763
4899
|
note: note4.value ?? null
|
|
4764
4900
|
})
|
|
4765
|
-
});
|
|
4901
|
+
}), { outputMode: context.outputMode });
|
|
4766
4902
|
return { ok: true, group: "inbox", command, details: { result } };
|
|
4767
4903
|
}
|
|
4768
4904
|
case "respond": {
|
|
@@ -4796,7 +4932,7 @@ async function executeInbox(context, args) {
|
|
|
4796
4932
|
const [key, ...restValue] = entry.split("=");
|
|
4797
4933
|
return [key, restValue.join("=")];
|
|
4798
4934
|
}));
|
|
4799
|
-
const result = await requestServerJson(context, "/api/inbox/inputs/respond", {
|
|
4935
|
+
const result = await withSpinner(`Sending response for ${request.value}\u2026`, () => requestServerJson(context, "/api/inbox/inputs/respond", {
|
|
4800
4936
|
method: "POST",
|
|
4801
4937
|
headers: { "content-type": "application/json" },
|
|
4802
4938
|
body: JSON.stringify({
|
|
@@ -4804,7 +4940,7 @@ async function executeInbox(context, args) {
|
|
|
4804
4940
|
requestId: request.value,
|
|
4805
4941
|
answers: parsedAnswers
|
|
4806
4942
|
})
|
|
4807
|
-
});
|
|
4943
|
+
}), { outputMode: context.outputMode });
|
|
4808
4944
|
return { ok: true, group: "inbox", command, details: { result } };
|
|
4809
4945
|
}
|
|
4810
4946
|
case "watch": {
|
|
@@ -5221,7 +5357,10 @@ async function runRigDoctorChecks(options) {
|
|
|
5221
5357
|
const bunVersion = options.bunVersion ?? Bun.version;
|
|
5222
5358
|
const request = options.requestJson ?? ((pathname, init) => requestServerJson({ projectRoot }, pathname, init));
|
|
5223
5359
|
const loadConfig = options.loadConfig ?? loadRigConfigOrNull;
|
|
5360
|
+
const progress = options.onProgress ?? (() => {});
|
|
5361
|
+
progress("Checking local toolchain\u2026");
|
|
5224
5362
|
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`)."));
|
|
5363
|
+
progress("Loading rig.config\u2026");
|
|
5225
5364
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
5226
5365
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
5227
5366
|
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync11(resolve17(projectRoot, name)));
|
|
@@ -5240,6 +5379,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5240
5379
|
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));
|
|
5241
5380
|
let server = null;
|
|
5242
5381
|
try {
|
|
5382
|
+
progress("Connecting to the selected Rig server\u2026");
|
|
5243
5383
|
server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);
|
|
5244
5384
|
checks.push(check("server", "Rig server reachable", "pass", `${server.connectionKind} ${server.baseUrl}`));
|
|
5245
5385
|
} catch (error) {
|
|
@@ -5247,18 +5387,21 @@ async function runRigDoctorChecks(options) {
|
|
|
5247
5387
|
}
|
|
5248
5388
|
if (server || options.requestJson) {
|
|
5249
5389
|
try {
|
|
5390
|
+
progress("Checking server status\u2026");
|
|
5250
5391
|
const status = await request("/api/server/status");
|
|
5251
5392
|
checks.push(check("server-status", "server project status", "pass", JSON.stringify(status).slice(0, 180)));
|
|
5252
5393
|
} catch (error) {
|
|
5253
5394
|
checks.push(check("server-status", "server project status", "fail", errorMessage(error), "Run `rig doctor` after the selected server is reachable."));
|
|
5254
5395
|
}
|
|
5255
5396
|
try {
|
|
5397
|
+
progress("Checking GitHub auth\u2026");
|
|
5256
5398
|
const auth = await request("/api/github/auth/status");
|
|
5257
5399
|
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>`."));
|
|
5258
5400
|
} catch (error) {
|
|
5259
5401
|
checks.push(check("github-auth", "GitHub auth", "fail", errorMessage(error), "Authenticate GitHub through Rig and ensure the server exposes auth status."));
|
|
5260
5402
|
}
|
|
5261
5403
|
try {
|
|
5404
|
+
progress("Checking GitHub repo permissions\u2026");
|
|
5262
5405
|
const permissions = await request("/api/github/repo/permissions");
|
|
5263
5406
|
const allowed = permissionAllowsPr2(permissions);
|
|
5264
5407
|
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."));
|
|
@@ -5266,6 +5409,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5266
5409
|
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."));
|
|
5267
5410
|
}
|
|
5268
5411
|
try {
|
|
5412
|
+
progress("Checking GitHub issue labels\u2026");
|
|
5269
5413
|
const labels = await request("/api/workspace/task-labels");
|
|
5270
5414
|
const ready = labelsReady(labels);
|
|
5271
5415
|
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."));
|
|
@@ -5273,6 +5417,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5273
5417
|
checks.push(check("task-labels", "GitHub issue labels", "warn", errorMessage(error), "Run `rig init`/`rig doctor` after label setup is wired on the server."));
|
|
5274
5418
|
}
|
|
5275
5419
|
try {
|
|
5420
|
+
progress("Checking task projection\u2026");
|
|
5276
5421
|
const projection = await request("/api/workspace/task-projection");
|
|
5277
5422
|
checks.push(check("task-projection", "task projection", "pass", JSON.stringify(projection).slice(0, 180)));
|
|
5278
5423
|
} catch (error) {
|
|
@@ -5281,6 +5426,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5281
5426
|
const slug = projectStatusSlug(projectRoot, config);
|
|
5282
5427
|
if (slug) {
|
|
5283
5428
|
try {
|
|
5429
|
+
progress("Checking server project checkout\u2026");
|
|
5284
5430
|
const project = await request(`/api/projects/${encodeURIComponent(slug)}`);
|
|
5285
5431
|
checks.push(check("remote-checkout", "server project checkout", "pass", JSON.stringify(project).slice(0, 180)));
|
|
5286
5432
|
} catch (error) {
|
|
@@ -5295,6 +5441,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5295
5441
|
}
|
|
5296
5442
|
checks.push(githubProjectsCheck(config));
|
|
5297
5443
|
checks.push(prMergeCheck(config));
|
|
5444
|
+
progress("Checking Pi installation\u2026");
|
|
5298
5445
|
const piChecks = await (options.piChecks ?? (() => buildPiSetupChecks()))().catch((error) => [{
|
|
5299
5446
|
ok: false,
|
|
5300
5447
|
label: "pi/pi-rig checks",
|
|
@@ -6217,7 +6364,7 @@ async function executeGithub(context, args) {
|
|
|
6217
6364
|
case "status": {
|
|
6218
6365
|
if (rest.length > 0)
|
|
6219
6366
|
throw new CliError("Usage: rig github auth status", 1);
|
|
6220
|
-
const status = await getGitHubAuthStatusViaServer(context);
|
|
6367
|
+
const status = await withSpinner("Checking GitHub auth on the server\u2026", () => getGitHubAuthStatusViaServer(context), { outputMode: context.outputMode });
|
|
6221
6368
|
printPayload(context, status, `GitHub auth: ${status.authenticated ? "authenticated" : "unauthenticated"}`);
|
|
6222
6369
|
return { ok: true, group: "github", command: "auth status", details: status };
|
|
6223
6370
|
}
|
|
@@ -6228,14 +6375,15 @@ async function executeGithub(context, args) {
|
|
|
6228
6375
|
const token = parsed.value?.trim();
|
|
6229
6376
|
if (!token)
|
|
6230
6377
|
throw new CliError("Missing --token value.", 1, { hint: "Re-run as `rig github auth token --token <token>`." });
|
|
6231
|
-
const result = await postGitHubTokenViaServer(context, token);
|
|
6378
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, token), { outputMode: context.outputMode });
|
|
6232
6379
|
printPayload(context, result, "GitHub token stored on the selected Rig server.");
|
|
6233
6380
|
return { ok: true, group: "github", command: "auth token", details: result };
|
|
6234
6381
|
}
|
|
6235
6382
|
case "import-gh": {
|
|
6236
6383
|
if (rest.length > 0)
|
|
6237
6384
|
throw new CliError("Usage: rig github auth import-gh", 1);
|
|
6238
|
-
const
|
|
6385
|
+
const importedToken = readGhToken();
|
|
6386
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, importedToken), { outputMode: context.outputMode });
|
|
6239
6387
|
printPayload(context, result, "GitHub token imported from gh and stored on the selected Rig server.");
|
|
6240
6388
|
return { ok: true, group: "github", command: "auth import-gh", details: result };
|
|
6241
6389
|
}
|
|
@@ -6248,7 +6396,7 @@ async function executeGithub(context, args) {
|
|
|
6248
6396
|
init_runner();
|
|
6249
6397
|
async function executeDoctor(context, args) {
|
|
6250
6398
|
requireNoExtraArgs(args, "rig doctor");
|
|
6251
|
-
const checks = await runRigDoctorChecks({ projectRoot: context.projectRoot });
|
|
6399
|
+
const checks = await withSpinner("Running doctor checks\u2026", (update) => runRigDoctorChecks({ projectRoot: context.projectRoot, onProgress: update }), { outputMode: context.outputMode });
|
|
6252
6400
|
if (context.outputMode === "text") {
|
|
6253
6401
|
console.log(formatDoctorChecks(checks));
|
|
6254
6402
|
}
|
|
@@ -6555,13 +6703,19 @@ async function executeInspect(context, args) {
|
|
|
6555
6703
|
const requiredTask = requireTask(task, "rig inspect logs --task <task-id>");
|
|
6556
6704
|
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];
|
|
6557
6705
|
if (!latestRun) {
|
|
6558
|
-
const
|
|
6559
|
-
|
|
6560
|
-
|
|
6706
|
+
const fallback = await withSpinner(`Finding runs for ${requiredTask} on the server\u2026`, async (update) => {
|
|
6707
|
+
const serverRuns = await listRunsViaServer(context, { limit: 200 }).catch(() => []);
|
|
6708
|
+
const serverRun = serverRuns.filter((run) => String(run.taskId ?? "") === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
|
|
6709
|
+
if (!serverRun || typeof serverRun.runId !== "string")
|
|
6710
|
+
return null;
|
|
6711
|
+
update(`Reading logs for run ${serverRun.runId}\u2026`);
|
|
6712
|
+
const page = await getRunLogsViaServer(context, serverRun.runId, { limit: 500 });
|
|
6713
|
+
return { runId: serverRun.runId, page };
|
|
6714
|
+
}, { outputMode: context.outputMode });
|
|
6715
|
+
if (!fallback) {
|
|
6561
6716
|
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`." });
|
|
6562
6717
|
}
|
|
6563
|
-
const
|
|
6564
|
-
const entries = Array.isArray(page.entries) ? page.entries : [];
|
|
6718
|
+
const entries = Array.isArray(fallback.page.entries) ? fallback.page.entries : [];
|
|
6565
6719
|
if (context.outputMode === "text") {
|
|
6566
6720
|
for (const entry of entries) {
|
|
6567
6721
|
const record = entry && typeof entry === "object" ? entry : {};
|
|
@@ -6570,9 +6724,9 @@ async function executeInspect(context, args) {
|
|
|
6570
6724
|
console.log([title, detail].filter(Boolean).join(" \u2014 "));
|
|
6571
6725
|
}
|
|
6572
6726
|
if (entries.length === 0)
|
|
6573
|
-
console.log(`(no log entries for run ${
|
|
6727
|
+
console.log(`(no log entries for run ${fallback.runId})`);
|
|
6574
6728
|
}
|
|
6575
|
-
return { ok: true, group: "inspect", command, details: { task: requiredTask, runId:
|
|
6729
|
+
return { ok: true, group: "inspect", command, details: { task: requiredTask, runId: fallback.runId, source: "server", entries } };
|
|
6576
6730
|
}
|
|
6577
6731
|
const logsPath = resolve20(resolveAuthorityRunDir2(context.projectRoot, latestRun.runId), "logs.jsonl");
|
|
6578
6732
|
if (!existsSync13(logsPath)) {
|
|
@@ -6630,7 +6784,7 @@ async function executeInspect(context, args) {
|
|
|
6630
6784
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
6631
6785
|
requireNoExtraArgs(remaining, "rig inspect diff [--task <task-id>]");
|
|
6632
6786
|
if (task) {
|
|
6633
|
-
const files = changedFilesForTask(context.projectRoot, task, false);
|
|
6787
|
+
const files = await withSpinner(`Computing changed files for ${task}\u2026`, async () => changedFilesForTask(context.projectRoot, task, false), { outputMode: context.outputMode });
|
|
6634
6788
|
for (const file of files) {
|
|
6635
6789
|
console.log(file);
|
|
6636
6790
|
}
|
|
@@ -7718,7 +7872,12 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
7718
7872
|
};
|
|
7719
7873
|
let detached = false;
|
|
7720
7874
|
try {
|
|
7721
|
-
await runPiMain([
|
|
7875
|
+
await runPiMain([
|
|
7876
|
+
"--no-extensions",
|
|
7877
|
+
"--no-skills",
|
|
7878
|
+
"--no-prompt-templates",
|
|
7879
|
+
"--no-context-files"
|
|
7880
|
+
], {
|
|
7722
7881
|
extensionFactories: [piRigExtensionFactory]
|
|
7723
7882
|
});
|
|
7724
7883
|
detached = true;
|
|
@@ -7783,8 +7942,9 @@ async function readOperatorSnapshot(context, runId, options = {}) {
|
|
|
7783
7942
|
}
|
|
7784
7943
|
async function attachRunOperatorView(context, input) {
|
|
7785
7944
|
let steered = false;
|
|
7786
|
-
|
|
7787
|
-
|
|
7945
|
+
const attachMessage = input.message?.trim();
|
|
7946
|
+
if (attachMessage) {
|
|
7947
|
+
await withSpinner("Queueing steering message\u2026", () => sendRunPiPromptViaServer(context, input.runId, attachMessage, "steer").catch(() => steerRunViaServer(context, input.runId, attachMessage)), { outputMode: context.outputMode });
|
|
7788
7948
|
steered = true;
|
|
7789
7949
|
}
|
|
7790
7950
|
if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
@@ -7794,7 +7954,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
7794
7954
|
});
|
|
7795
7955
|
}
|
|
7796
7956
|
const surface = createOperatorSurface({ interactive: input.interactive !== false });
|
|
7797
|
-
let snapshot = await readOperatorSnapshot(context, input.runId);
|
|
7957
|
+
let snapshot = await withSpinner(`Connecting to run ${input.runId}\u2026`, () => readOperatorSnapshot(context, input.runId), { outputMode: context.outputMode });
|
|
7798
7958
|
if (context.outputMode === "text") {
|
|
7799
7959
|
surface.renderSnapshot(snapshot);
|
|
7800
7960
|
surface.renderTimeline(snapshot.timeline);
|
|
@@ -7845,9 +8005,6 @@ function normalizeRemoteRunDetails(payload) {
|
|
|
7845
8005
|
};
|
|
7846
8006
|
}
|
|
7847
8007
|
var REMOTE_TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged"]);
|
|
7848
|
-
function isRemoteConnectionSelected(projectRoot) {
|
|
7849
|
-
return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
|
|
7850
|
-
}
|
|
7851
8008
|
async function listRunsForSelectedConnection(context, options = {}) {
|
|
7852
8009
|
if (isRemoteConnectionSelected(context.projectRoot)) {
|
|
7853
8010
|
return { runs: await listRunsViaServer(context, options), source: "server" };
|
|
@@ -7928,7 +8085,7 @@ async function executeRun(context, args) {
|
|
|
7928
8085
|
switch (command) {
|
|
7929
8086
|
case "list": {
|
|
7930
8087
|
requireNoExtraArgs(rest, "rig run list");
|
|
7931
|
-
const { runs, source } = await listRunsForSelectedConnection(context, { limit: 100 });
|
|
8088
|
+
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 });
|
|
7932
8089
|
if (context.outputMode === "text") {
|
|
7933
8090
|
printFormattedOutput(formatRunList(runs, { source }));
|
|
7934
8091
|
await printPendingInboxFooter(context);
|
|
@@ -8001,7 +8158,7 @@ async function executeRun(context, args) {
|
|
|
8001
8158
|
if (!runId) {
|
|
8002
8159
|
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>`." });
|
|
8003
8160
|
}
|
|
8004
|
-
const record = readAuthorityRun5(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, runId).catch(() => ({})));
|
|
8161
|
+
const record = readAuthorityRun5(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await withSpinner(`Reading run ${runId} from server\u2026`, () => getRunDetailsViaServer(context, runId).catch(() => ({})), { outputMode: context.outputMode }));
|
|
8005
8162
|
if (!record) {
|
|
8006
8163
|
throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see recorded runs." });
|
|
8007
8164
|
}
|
|
@@ -8022,7 +8179,8 @@ async function executeRun(context, args) {
|
|
|
8022
8179
|
}
|
|
8023
8180
|
const renderer = createPiRunStreamRenderer();
|
|
8024
8181
|
let cursor = null;
|
|
8025
|
-
const
|
|
8182
|
+
const timelineRunId = run.value;
|
|
8183
|
+
const page = await withSpinner(`Reading timeline for ${timelineRunId}\u2026`, () => getRunTimelineViaServer(context, timelineRunId, { limit: 500 }), { outputMode: context.outputMode });
|
|
8026
8184
|
const events = Array.isArray(page.entries) ? page.entries.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
|
|
8027
8185
|
cursor = typeof page.nextCursor === "string" ? page.nextCursor : null;
|
|
8028
8186
|
if (context.outputMode === "text") {
|
|
@@ -8099,8 +8257,9 @@ async function executeRun(context, args) {
|
|
|
8099
8257
|
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`." });
|
|
8100
8258
|
}
|
|
8101
8259
|
let steered = false;
|
|
8102
|
-
|
|
8103
|
-
|
|
8260
|
+
const steerMessage = messageOption.value?.trim();
|
|
8261
|
+
if (steerMessage) {
|
|
8262
|
+
await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, steerMessage), { outputMode: context.outputMode });
|
|
8104
8263
|
steered = true;
|
|
8105
8264
|
}
|
|
8106
8265
|
const attached = await attachRunOperatorView(context, {
|
|
@@ -8120,7 +8279,7 @@ async function executeRun(context, args) {
|
|
|
8120
8279
|
}
|
|
8121
8280
|
return { ok: true, group: "run", command };
|
|
8122
8281
|
}
|
|
8123
|
-
const summary = isRemoteConnectionSelected(context.projectRoot) ? buildServerRunStatus(await listRunsViaServer(context, { limit: 100 })) : runStatus(context.projectRoot, runtimeContext);
|
|
8282
|
+
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);
|
|
8124
8283
|
const activeRuns = Array.isArray(summary.activeRuns) ? summary.activeRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
|
|
8125
8284
|
const recentRuns = Array.isArray(summary.recentRuns) ? summary.recentRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
|
|
8126
8285
|
if (context.outputMode === "text") {
|
|
@@ -8197,28 +8356,38 @@ async function executeRun(context, args) {
|
|
|
8197
8356
|
};
|
|
8198
8357
|
}
|
|
8199
8358
|
case "resume": {
|
|
8200
|
-
|
|
8359
|
+
let pending = rest;
|
|
8360
|
+
const runOpt = takeOption(pending, "--run");
|
|
8361
|
+
pending = runOpt.rest;
|
|
8362
|
+
const positional = pending[0] && !pending[0].startsWith("-") ? pending.shift() : undefined;
|
|
8363
|
+
requireNoExtraArgs(pending, "rig run resume [<run-id>] [--run <id>]");
|
|
8364
|
+
const targetRunId = runOpt.value ?? positional ?? null;
|
|
8201
8365
|
if (context.dryRun) {
|
|
8202
8366
|
if (context.outputMode === "text") {
|
|
8203
8367
|
console.log("[dry-run] rig run resume");
|
|
8204
8368
|
}
|
|
8205
8369
|
return { ok: true, group: "run", command };
|
|
8206
8370
|
}
|
|
8207
|
-
const resumed = await runResume(context.projectRoot, runtimeContext);
|
|
8371
|
+
const resumed = await runResume(context.projectRoot, runtimeContext, targetRunId);
|
|
8208
8372
|
if (context.outputMode === "text") {
|
|
8209
8373
|
console.log(`Resumed run: ${resumed.runId}`);
|
|
8210
8374
|
}
|
|
8211
8375
|
return { ok: true, group: "run", command, details: resumed };
|
|
8212
8376
|
}
|
|
8213
8377
|
case "restart": {
|
|
8214
|
-
|
|
8378
|
+
let pending = rest;
|
|
8379
|
+
const runOpt = takeOption(pending, "--run");
|
|
8380
|
+
pending = runOpt.rest;
|
|
8381
|
+
const positional = pending[0] && !pending[0].startsWith("-") ? pending.shift() : undefined;
|
|
8382
|
+
requireNoExtraArgs(pending, "rig run restart [<run-id>] [--run <id>]");
|
|
8383
|
+
const targetRunId = runOpt.value ?? positional ?? null;
|
|
8215
8384
|
if (context.dryRun) {
|
|
8216
8385
|
if (context.outputMode === "text") {
|
|
8217
8386
|
console.log("[dry-run] rig run restart");
|
|
8218
8387
|
}
|
|
8219
8388
|
return { ok: true, group: "run", command };
|
|
8220
8389
|
}
|
|
8221
|
-
const restarted = await runRestart(context.projectRoot, runtimeContext);
|
|
8390
|
+
const restarted = await runRestart(context.projectRoot, runtimeContext, targetRunId);
|
|
8222
8391
|
if (context.outputMode === "text") {
|
|
8223
8392
|
console.log(`Restarted run: ${restarted.runId}`);
|
|
8224
8393
|
}
|
|
@@ -8245,7 +8414,8 @@ async function executeRun(context, args) {
|
|
|
8245
8414
|
}
|
|
8246
8415
|
return { ok: true, group: "run", command, details: { runId, dryRun: true } };
|
|
8247
8416
|
}
|
|
8248
|
-
|
|
8417
|
+
const trimmedMessage = message2.trim();
|
|
8418
|
+
await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, trimmedMessage), { outputMode: context.outputMode });
|
|
8249
8419
|
if (context.outputMode === "text") {
|
|
8250
8420
|
console.log(`Steering message queued for ${runId}.`);
|
|
8251
8421
|
}
|
|
@@ -8266,7 +8436,7 @@ async function executeRun(context, args) {
|
|
|
8266
8436
|
};
|
|
8267
8437
|
}
|
|
8268
8438
|
if (runId) {
|
|
8269
|
-
const stopped = await stopRunViaServer(context, runId);
|
|
8439
|
+
const stopped = await withSpinner(`Requesting stop for ${runId}\u2026`, () => stopRunViaServer(context, runId), { outputMode: context.outputMode });
|
|
8270
8440
|
if (context.outputMode === "text")
|
|
8271
8441
|
console.log(`Stop requested: ${runId}`);
|
|
8272
8442
|
return { ok: true, group: "run", command, details: stopped };
|
|
@@ -8512,15 +8682,12 @@ async function executeServer(context, args, options) {
|
|
|
8512
8682
|
|
|
8513
8683
|
// packages/cli/src/commands/stats.ts
|
|
8514
8684
|
init_runner();
|
|
8515
|
-
import
|
|
8516
|
-
import {
|
|
8517
|
-
listAuthorityRuns as listAuthorityRuns3,
|
|
8518
|
-
projectRunFromJournal
|
|
8519
|
-
} from "@rig/runtime/control-plane/authority-files";
|
|
8685
|
+
import pc6 from "picocolors";
|
|
8686
|
+
import { computeRigStats } from "@rig/runtime/control-plane/native/run-stats";
|
|
8520
8687
|
|
|
8521
8688
|
// packages/cli/src/commands/_help-catalog.ts
|
|
8522
8689
|
import { intro as intro3, log as log4, note as note4, outro as outro3 } from "@clack/prompts";
|
|
8523
|
-
import
|
|
8690
|
+
import pc5 from "picocolors";
|
|
8524
8691
|
var TOP_LEVEL_SECTIONS = [
|
|
8525
8692
|
{
|
|
8526
8693
|
title: "Start here",
|
|
@@ -8808,13 +8975,13 @@ var ADVANCED_COMMANDS = [
|
|
|
8808
8975
|
];
|
|
8809
8976
|
var ALL_GROUPS = [...PRIMARY_GROUPS, ...ADVANCED_GROUPS];
|
|
8810
8977
|
function heading(title) {
|
|
8811
|
-
return
|
|
8978
|
+
return pc5.bold(pc5.cyan(title));
|
|
8812
8979
|
}
|
|
8813
8980
|
function renderRigBanner(version) {
|
|
8814
|
-
const m = (s) =>
|
|
8815
|
-
const c = (s) =>
|
|
8816
|
-
const y = (s) =>
|
|
8817
|
-
const d = (s) =>
|
|
8981
|
+
const m = (s) => pc5.bold(pc5.magenta(s));
|
|
8982
|
+
const c = (s) => pc5.bold(pc5.cyan(s));
|
|
8983
|
+
const y = (s) => pc5.yellow(s);
|
|
8984
|
+
const d = (s) => pc5.dim(s);
|
|
8818
8985
|
const lines = [
|
|
8819
8986
|
m(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + d(" \u2591\u2592\u2593\u2588 "),
|
|
8820
8987
|
m(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ") + d("\u2593\u2592\u2591"),
|
|
@@ -8823,7 +8990,7 @@ function renderRigBanner(version) {
|
|
|
8823
8990
|
y(" \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + d(" \u2592\u2591"),
|
|
8824
8991
|
y(" \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D "),
|
|
8825
8992
|
"",
|
|
8826
|
-
` ${c("\u25E2\u25E4")} ${
|
|
8993
|
+
` ${c("\u25E2\u25E4")} ${pc5.bold("the control rig for autonomous coding agents")} ${m("//")} ${d("you are the operator")}`,
|
|
8827
8994
|
version ? ` ${d(`v${version} \xB7 jack in: rig task run --next`)}` : ` ${d("jack in: rig task run --next")}`
|
|
8828
8995
|
];
|
|
8829
8996
|
return lines.join(`
|
|
@@ -8831,7 +8998,7 @@ function renderRigBanner(version) {
|
|
|
8831
8998
|
}
|
|
8832
8999
|
function commandLine(command, description) {
|
|
8833
9000
|
const commandColumn = command.length >= 38 ? `${command} ` : command.padEnd(38);
|
|
8834
|
-
return `${
|
|
9001
|
+
return `${pc5.dim("\u2502")} ${pc5.bold(commandColumn)} ${description}`;
|
|
8835
9002
|
}
|
|
8836
9003
|
function renderCommandBlock(commands) {
|
|
8837
9004
|
return commands.map((entry) => commandLine(entry.command, entry.description)).join(`
|
|
@@ -8841,37 +9008,37 @@ function renderGroup(group) {
|
|
|
8841
9008
|
const lines = [
|
|
8842
9009
|
`${heading(`rig ${group.name}`)} \u2014 ${group.summary}`,
|
|
8843
9010
|
"",
|
|
8844
|
-
|
|
9011
|
+
pc5.bold("Usage"),
|
|
8845
9012
|
...group.usage.map((line) => ` ${line}`),
|
|
8846
9013
|
"",
|
|
8847
|
-
|
|
9014
|
+
pc5.bold("Commands"),
|
|
8848
9015
|
...group.commands.map((entry) => commandLine(entry.command, entry.description))
|
|
8849
9016
|
];
|
|
8850
9017
|
if (group.examples?.length) {
|
|
8851
|
-
lines.push("",
|
|
9018
|
+
lines.push("", pc5.bold("Examples"), ...group.examples.map((line) => ` ${pc5.dim("$")} ${line}`));
|
|
8852
9019
|
}
|
|
8853
9020
|
if (group.next?.length) {
|
|
8854
|
-
lines.push("",
|
|
9021
|
+
lines.push("", pc5.bold("Next steps"), ...group.next.map((line) => ` ${pc5.dim("\u203A")} ${line}`));
|
|
8855
9022
|
}
|
|
8856
9023
|
if (group.advanced?.length) {
|
|
8857
|
-
lines.push("",
|
|
9024
|
+
lines.push("", pc5.bold("Compatibility / advanced"), ...group.advanced.map((line) => ` ${pc5.dim("\u203A")} ${line}`));
|
|
8858
9025
|
}
|
|
8859
9026
|
return lines.join(`
|
|
8860
9027
|
`);
|
|
8861
9028
|
}
|
|
8862
9029
|
function renderTopLevelHelp() {
|
|
8863
9030
|
return [
|
|
8864
|
-
`${heading("rig")} ${
|
|
8865
|
-
|
|
9031
|
+
`${heading("rig")} ${pc5.dim("\u2014 server-owned task/run control plane for Pi-backed engineering work")}`,
|
|
9032
|
+
pc5.dim("Current path: select a server, choose a task, submit a run, attach with native Pi, clear inbox gates."),
|
|
8866
9033
|
"",
|
|
8867
9034
|
...TOP_LEVEL_SECTIONS.flatMap((section) => [
|
|
8868
|
-
`${
|
|
9035
|
+
`${pc5.bold(pc5.magenta(`\u25C7 ${section.title}`))} \u2014 ${pc5.dim(section.subtitle)}`,
|
|
8869
9036
|
renderCommandBlock(section.commands),
|
|
8870
9037
|
""
|
|
8871
9038
|
]),
|
|
8872
|
-
|
|
9039
|
+
pc5.dim("More: `rig help --advanced` for dev/compatibility commands; `rig <group> --help` for rich per-group help; `rig --version` for the installed version."),
|
|
8873
9040
|
"",
|
|
8874
|
-
|
|
9041
|
+
pc5.bold("Global options"),
|
|
8875
9042
|
commandLine("--project <path>", "Use a project root instead of auto-discovery."),
|
|
8876
9043
|
commandLine("--json", "Emit structured output for scripts/agents."),
|
|
8877
9044
|
commandLine("--dry-run", "Print the command plan without mutating state.")
|
|
@@ -8882,16 +9049,16 @@ function renderAdvancedHelp() {
|
|
|
8882
9049
|
return [
|
|
8883
9050
|
`${heading("rig advanced")} \u2014 compatibility, diagnostics, and internal surfaces`,
|
|
8884
9051
|
"",
|
|
8885
|
-
|
|
9052
|
+
pc5.bold("Primary groups"),
|
|
8886
9053
|
" init, server, task, run, inbox, repo, plugin, inspect, stats, doctor, github",
|
|
8887
9054
|
"",
|
|
8888
|
-
|
|
9055
|
+
pc5.bold("Advanced commands"),
|
|
8889
9056
|
...ADVANCED_COMMANDS.map((entry) => commandLine(entry.command, entry.description)),
|
|
8890
9057
|
"",
|
|
8891
|
-
|
|
9058
|
+
pc5.bold("Advanced groups"),
|
|
8892
9059
|
...ADVANCED_GROUPS.map((group) => commandLine(group.name, group.summary)),
|
|
8893
9060
|
"",
|
|
8894
|
-
|
|
9061
|
+
pc5.dim("All groups remain callable. Prefer `rig server`, `rig task`, `rig run`, and `rig inbox` for day-to-day work.")
|
|
8895
9062
|
].join(`
|
|
8896
9063
|
`);
|
|
8897
9064
|
}
|
|
@@ -9005,90 +9172,6 @@ function parseSinceOption(value, now = new Date) {
|
|
|
9005
9172
|
}
|
|
9006
9173
|
return new Date(parsed);
|
|
9007
9174
|
}
|
|
9008
|
-
function median(values) {
|
|
9009
|
-
if (values.length === 0)
|
|
9010
|
-
return null;
|
|
9011
|
-
const sorted = [...values].sort((left, right) => left - right);
|
|
9012
|
-
const middle = Math.floor(sorted.length / 2);
|
|
9013
|
-
const upper = sorted[middle];
|
|
9014
|
-
if (sorted.length % 2 === 1)
|
|
9015
|
-
return upper;
|
|
9016
|
-
return (sorted[middle - 1] + upper) / 2;
|
|
9017
|
-
}
|
|
9018
|
-
function rate(part, total) {
|
|
9019
|
-
if (total === 0)
|
|
9020
|
-
return null;
|
|
9021
|
-
return part / total;
|
|
9022
|
-
}
|
|
9023
|
-
function parseTimestamp(value) {
|
|
9024
|
-
if (!value)
|
|
9025
|
-
return null;
|
|
9026
|
-
const parsed = Date.parse(value);
|
|
9027
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
9028
|
-
}
|
|
9029
|
-
function computeRigStats(projectRoot, options = {}) {
|
|
9030
|
-
const since = options.since ?? null;
|
|
9031
|
-
const sinceMs = since ? since.getTime() : null;
|
|
9032
|
-
const runs = listAuthorityRuns3(projectRoot).filter((run) => {
|
|
9033
|
-
if (sinceMs === null)
|
|
9034
|
-
return true;
|
|
9035
|
-
const createdAt = parseTimestamp(run.createdAt) ?? parseTimestamp(run.updatedAt);
|
|
9036
|
-
return createdAt !== null && createdAt >= sinceMs;
|
|
9037
|
-
});
|
|
9038
|
-
const statusCounts = {};
|
|
9039
|
-
const completionDurations = [];
|
|
9040
|
-
let steeringTotal = 0;
|
|
9041
|
-
let stallTotal = 0;
|
|
9042
|
-
let approvalsApproved = 0;
|
|
9043
|
-
let approvalsRejected = 0;
|
|
9044
|
-
let approvalsPending = 0;
|
|
9045
|
-
for (const run of runs) {
|
|
9046
|
-
const status = run.status ?? "unknown";
|
|
9047
|
-
statusCounts[status] = (statusCounts[status] ?? 0) + 1;
|
|
9048
|
-
if (status === "completed") {
|
|
9049
|
-
const createdAt = parseTimestamp(run.createdAt);
|
|
9050
|
-
const completedAt = parseTimestamp(run.completedAt);
|
|
9051
|
-
if (createdAt !== null && completedAt !== null && completedAt >= createdAt) {
|
|
9052
|
-
completionDurations.push(completedAt - createdAt);
|
|
9053
|
-
}
|
|
9054
|
-
}
|
|
9055
|
-
const projection = projectRunFromJournal(projectRoot, run.runId);
|
|
9056
|
-
if (!projection)
|
|
9057
|
-
continue;
|
|
9058
|
-
steeringTotal += projection.steeringCount;
|
|
9059
|
-
stallTotal += projection.stallCount;
|
|
9060
|
-
approvalsPending += projection.pendingApprovals.length;
|
|
9061
|
-
for (const resolved of projection.resolvedApprovals) {
|
|
9062
|
-
if (resolved.decision === "approve")
|
|
9063
|
-
approvalsApproved += 1;
|
|
9064
|
-
else
|
|
9065
|
-
approvalsRejected += 1;
|
|
9066
|
-
}
|
|
9067
|
-
}
|
|
9068
|
-
const totalRuns = runs.length;
|
|
9069
|
-
const completedRuns = statusCounts["completed"] ?? 0;
|
|
9070
|
-
const failedRuns = statusCounts["failed"] ?? 0;
|
|
9071
|
-
const needsAttentionRuns = statusCounts["needs-attention"] ?? 0;
|
|
9072
|
-
return {
|
|
9073
|
-
since: since ? since.toISOString() : null,
|
|
9074
|
-
totalRuns,
|
|
9075
|
-
statusCounts,
|
|
9076
|
-
completedRuns,
|
|
9077
|
-
failedRuns,
|
|
9078
|
-
needsAttentionRuns,
|
|
9079
|
-
completionRate: rate(completedRuns, totalRuns),
|
|
9080
|
-
failureRate: rate(failedRuns, totalRuns),
|
|
9081
|
-
needsAttentionRate: rate(needsAttentionRuns, totalRuns),
|
|
9082
|
-
medianCompletionMs: median(completionDurations),
|
|
9083
|
-
steeringTotal,
|
|
9084
|
-
steeringPerRun: totalRuns === 0 ? null : steeringTotal / totalRuns,
|
|
9085
|
-
stallTotal,
|
|
9086
|
-
approvalsRequested: approvalsApproved + approvalsRejected + approvalsPending,
|
|
9087
|
-
approvalsApproved,
|
|
9088
|
-
approvalsRejected,
|
|
9089
|
-
approvalsPending
|
|
9090
|
-
};
|
|
9091
|
-
}
|
|
9092
9175
|
function formatPercent(value) {
|
|
9093
9176
|
if (value === null)
|
|
9094
9177
|
return "\u2014";
|
|
@@ -9123,11 +9206,11 @@ function formatStatsReport(stats) {
|
|
|
9123
9206
|
const keyWidth = Math.max(...rows.map(([key]) => key.length));
|
|
9124
9207
|
const lines = [
|
|
9125
9208
|
formatSection("Fleet stats", window),
|
|
9126
|
-
...rows.map(([key, value]) => `${
|
|
9209
|
+
...rows.map(([key, value]) => `${pc6.dim("\u2502")} ${pc6.dim(key.padEnd(keyWidth + 2))} ${value}`)
|
|
9127
9210
|
];
|
|
9128
9211
|
const otherStatuses = Object.entries(stats.statusCounts).filter(([status]) => !["completed", "failed", "needs-attention"].includes(status)).sort(([, left], [, right]) => right - left);
|
|
9129
9212
|
if (otherStatuses.length > 0) {
|
|
9130
|
-
lines.push(`${
|
|
9213
|
+
lines.push(`${pc6.dim("\u2502")} ${pc6.dim("other".padEnd(keyWidth + 2))} ${otherStatuses.map(([status, count]) => `${status}:${count}`).join(" ")}`);
|
|
9131
9214
|
}
|
|
9132
9215
|
lines.push("", ...formatNextSteps([
|
|
9133
9216
|
"Inspect a run: `rig run list` then `rig run show <run-id>`",
|
|
@@ -9146,7 +9229,8 @@ async function executeStats(context, args) {
|
|
|
9146
9229
|
const sinceResult = takeOption(pending, "--since");
|
|
9147
9230
|
requireNoExtraArgs(sinceResult.rest, "rig stats [show] [--since <7d|30d|ISO date>]");
|
|
9148
9231
|
const since = parseSinceOption(sinceResult.value);
|
|
9149
|
-
const
|
|
9232
|
+
const remoteStats = isRemoteConnectionSelected(context.projectRoot);
|
|
9233
|
+
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 });
|
|
9150
9234
|
if (context.outputMode === "text") {
|
|
9151
9235
|
printFormattedOutput(formatStatsReport(stats));
|
|
9152
9236
|
}
|
|
@@ -9401,7 +9485,7 @@ async function executeTask(context, args, options) {
|
|
|
9401
9485
|
pending = rawResult.rest;
|
|
9402
9486
|
const { filters, rest: remaining } = parseTaskFilters(pending);
|
|
9403
9487
|
requireNoExtraArgs(remaining, "rig task list [--raw] [--assignee <login|me|@me>] [--state open|closed] [--status <status>] [--limit <n>]");
|
|
9404
|
-
const tasks = await listWorkspaceTasksViaServer(context, filters);
|
|
9488
|
+
const tasks = await withSpinner("Reading tasks from server\u2026", () => listWorkspaceTasksViaServer(context, filters), { outputMode: context.outputMode });
|
|
9405
9489
|
if (context.outputMode === "text") {
|
|
9406
9490
|
const renderedTasks = rawResult.value ? tasks.map((task) => summarizeTask(task, { raw: true })) : tasks.map((task) => summarizeTask(task));
|
|
9407
9491
|
printFormattedOutput(formatTaskList(renderedTasks, { raw: rawResult.value }));
|
|
@@ -9425,7 +9509,7 @@ async function executeTask(context, args, options) {
|
|
|
9425
9509
|
const taskId3 = normalizeTaskRunTaskId(taskOption.value ?? positional);
|
|
9426
9510
|
if (!taskId3)
|
|
9427
9511
|
throw new CliError("task show requires a task id.", 2, { hint: "Run `rig task list` to find ids, then `rig task show <id>`." });
|
|
9428
|
-
const task = await getWorkspaceTaskViaServer(context, taskId3);
|
|
9512
|
+
const task = await withSpinner(`Reading task ${taskId3} from server\u2026`, () => getWorkspaceTaskViaServer(context, taskId3), { outputMode: context.outputMode });
|
|
9429
9513
|
if (!task)
|
|
9430
9514
|
throw new CliError(`Task not found: ${taskId3}`, 3, { hint: "Run `rig task list` to see available tasks." });
|
|
9431
9515
|
const summary = summarizeTask(task, { raw: true });
|
|
@@ -9437,7 +9521,7 @@ async function executeTask(context, args, options) {
|
|
|
9437
9521
|
case "next": {
|
|
9438
9522
|
const { filters, rest: remaining } = parseTaskFilters(rest);
|
|
9439
9523
|
requireNoExtraArgs(remaining, "rig task next [--assignee <login|me|@me>] [--state open|closed] [--status <status>] [--limit <n>]");
|
|
9440
|
-
const selected = await selectNextWorkspaceTaskViaServer(context, filters);
|
|
9524
|
+
const selected = await withSpinner("Selecting next ready task\u2026", () => selectNextWorkspaceTaskViaServer(context, filters), { outputMode: context.outputMode });
|
|
9441
9525
|
if (context.outputMode === "text") {
|
|
9442
9526
|
if (selected.task) {
|
|
9443
9527
|
printFormattedOutput(formatTaskCard(summarizeTask(selected.task, { raw: true }), { title: "Selected task", selected: true }));
|
|
@@ -9587,7 +9671,7 @@ async function executeTask(context, args, options) {
|
|
|
9587
9671
|
let selectedTask = null;
|
|
9588
9672
|
let selectedTaskId = normalizeTaskRunTaskId(taskResult.value) ?? positionalTaskId;
|
|
9589
9673
|
if (nextResult.value) {
|
|
9590
|
-
const selected = await selectNextWorkspaceTaskViaServer(context, filterResult.filters);
|
|
9674
|
+
const selected = await withSpinner("Selecting next ready task\u2026", () => selectNextWorkspaceTaskViaServer(context, filterResult.filters), { outputMode: context.outputMode });
|
|
9591
9675
|
selectedTask = selected.task;
|
|
9592
9676
|
selectedTaskId = selected.task ? readTaskId(selected.task) : null;
|
|
9593
9677
|
if (!selectedTaskId) {
|
|
@@ -9598,7 +9682,7 @@ async function executeTask(context, args, options) {
|
|
|
9598
9682
|
if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
9599
9683
|
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);
|
|
9600
9684
|
}
|
|
9601
|
-
const tasks = await listWorkspaceTasksViaServer(context, filterResult.filters);
|
|
9685
|
+
const tasks = await withSpinner("Reading tasks from server\u2026", () => listWorkspaceTasksViaServer(context, filterResult.filters), { outputMode: context.outputMode });
|
|
9602
9686
|
selectedTask = await selectTaskWithTextPicker(tasks);
|
|
9603
9687
|
selectedTaskId = selectedTask ? readTaskId(selectedTask) : null;
|
|
9604
9688
|
if (!selectedTaskId) {
|
|
@@ -9608,13 +9692,13 @@ async function executeTask(context, args, options) {
|
|
|
9608
9692
|
await runProjectMainSyncPreflight(context, { disabled: skipProjectSyncResult.value });
|
|
9609
9693
|
const projectDefaults = await loadTaskRunProjectDefaults(context.projectRoot);
|
|
9610
9694
|
const runtimeAdapter = normalizeRuntimeAdapter(runtimeAdapterResult.value ?? projectDefaults.runtimeAdapter);
|
|
9611
|
-
await runFastTaskRunPreflight(context, {
|
|
9695
|
+
await withSpinner("Running task-run preflight\u2026", () => runFastTaskRunPreflight(context, {
|
|
9612
9696
|
taskId: selectedTaskId,
|
|
9613
9697
|
runtimeAdapter
|
|
9614
|
-
});
|
|
9698
|
+
}), { outputMode: context.outputMode });
|
|
9615
9699
|
const dirtyBaseline = await resolveDirtyBaselineForTaskRun(context, dirtyBaselineResult.value);
|
|
9616
9700
|
const prMode = noPrResult.value ? "off" : normalizePrMode(prResult.value) ?? projectDefaults.prMode;
|
|
9617
|
-
const submitted = await submitTaskRunViaServer(context, {
|
|
9701
|
+
const submitted = await withSpinner("Dispatching run\u2026", () => submitTaskRunViaServer(context, {
|
|
9618
9702
|
runId: context.runId,
|
|
9619
9703
|
taskId: selectedTaskId ?? undefined,
|
|
9620
9704
|
title: titleResult.value ?? undefined,
|
|
@@ -9625,7 +9709,7 @@ async function executeTask(context, args, options) {
|
|
|
9625
9709
|
initialPrompt: initialPromptResult.value ?? undefined,
|
|
9626
9710
|
baselineMode: dirtyBaseline.mode,
|
|
9627
9711
|
prMode
|
|
9628
|
-
});
|
|
9712
|
+
}), { outputMode: context.outputMode });
|
|
9629
9713
|
let attachDetails = null;
|
|
9630
9714
|
if (!detachResult.value && context.outputMode === "text") {
|
|
9631
9715
|
printFormattedOutput(formatSubmittedRun({
|
|
@@ -11803,7 +11887,7 @@ async function executeSetup(context, args) {
|
|
|
11803
11887
|
case "check":
|
|
11804
11888
|
requireNoExtraArgs(rest, `rig setup ${command}`);
|
|
11805
11889
|
{
|
|
11806
|
-
const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot));
|
|
11890
|
+
const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot, context.outputMode));
|
|
11807
11891
|
return { ok: true, group: "setup", command, details: { checks, failures: countDoctorFailures(checks) } };
|
|
11808
11892
|
}
|
|
11809
11893
|
case "setup":
|
|
@@ -11812,7 +11896,7 @@ async function executeSetup(context, args) {
|
|
|
11812
11896
|
return { ok: true, group: "setup", command };
|
|
11813
11897
|
case "preflight":
|
|
11814
11898
|
requireNoExtraArgs(rest, "rig setup preflight");
|
|
11815
|
-
await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot));
|
|
11899
|
+
await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot, context.outputMode));
|
|
11816
11900
|
return { ok: true, group: "setup", command };
|
|
11817
11901
|
default:
|
|
11818
11902
|
throw new CliError(`Unknown setup command: ${command}`, 1, { hint: "Run `rig setup --help` \u2014 commands are bootstrap|check|preflight." });
|
|
@@ -11833,8 +11917,8 @@ function runSetupInit(projectRoot) {
|
|
|
11833
11917
|
}
|
|
11834
11918
|
console.log("Harness directories ready.");
|
|
11835
11919
|
}
|
|
11836
|
-
async function runSetupCheck(projectRoot) {
|
|
11837
|
-
const doctorChecks = await runRigDoctorChecks({ projectRoot });
|
|
11920
|
+
async function runSetupCheck(projectRoot, outputMode = "text") {
|
|
11921
|
+
const doctorChecks = await withSpinner("Running setup checks\u2026", (update) => runRigDoctorChecks({ projectRoot, onProgress: update }), { outputMode });
|
|
11838
11922
|
console.log(formatDoctorChecks(doctorChecks));
|
|
11839
11923
|
const failures = countDoctorFailures(doctorChecks);
|
|
11840
11924
|
if (failures > 0) {
|
|
@@ -11842,8 +11926,8 @@ async function runSetupCheck(projectRoot) {
|
|
|
11842
11926
|
}
|
|
11843
11927
|
return doctorChecks;
|
|
11844
11928
|
}
|
|
11845
|
-
async function runSetupPreflight(projectRoot) {
|
|
11846
|
-
await runSetupCheck(projectRoot);
|
|
11929
|
+
async function runSetupPreflight(projectRoot, outputMode = "text") {
|
|
11930
|
+
await runSetupCheck(projectRoot, outputMode);
|
|
11847
11931
|
const validationRoot = resolve24(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
|
|
11848
11932
|
if (existsSync16(validationRoot)) {
|
|
11849
11933
|
const validators = readdirSync3(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|