@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/bin/rig.js
CHANGED
|
@@ -2923,6 +2923,9 @@ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
|
2923
2923
|
return;
|
|
2924
2924
|
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
2925
2925
|
}
|
|
2926
|
+
function isRemoteConnectionSelected(projectRoot) {
|
|
2927
|
+
return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
|
|
2928
|
+
}
|
|
2926
2929
|
|
|
2927
2930
|
// packages/cli/src/commands/_server-client.ts
|
|
2928
2931
|
init_runner();
|
|
@@ -2930,6 +2933,15 @@ import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
|
|
|
2930
2933
|
import { resolve as resolve11 } from "path";
|
|
2931
2934
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
2932
2935
|
var scopedGitHubBearerTokens = new Map;
|
|
2936
|
+
var serverPhaseListener = null;
|
|
2937
|
+
function setServerPhaseListener(listener) {
|
|
2938
|
+
const previous = serverPhaseListener;
|
|
2939
|
+
serverPhaseListener = listener;
|
|
2940
|
+
return previous;
|
|
2941
|
+
}
|
|
2942
|
+
function reportServerPhase(label) {
|
|
2943
|
+
serverPhaseListener?.(label);
|
|
2944
|
+
}
|
|
2933
2945
|
function cleanToken(value) {
|
|
2934
2946
|
const trimmed = value?.trim();
|
|
2935
2947
|
return trimmed ? trimmed : null;
|
|
@@ -2976,6 +2988,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2976
2988
|
try {
|
|
2977
2989
|
const selected = resolveSelectedConnection(projectRoot);
|
|
2978
2990
|
if (selected?.connection.kind === "remote") {
|
|
2991
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
2979
2992
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
2980
2993
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
2981
2994
|
return {
|
|
@@ -2985,6 +2998,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2985
2998
|
serverProjectRoot
|
|
2986
2999
|
};
|
|
2987
3000
|
}
|
|
3001
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
2988
3002
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
2989
3003
|
return {
|
|
2990
3004
|
baseUrl: connection.baseUrl,
|
|
@@ -3101,6 +3115,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
3101
3115
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
3102
3116
|
if (server.serverProjectRoot)
|
|
3103
3117
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
3118
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
3104
3119
|
let response;
|
|
3105
3120
|
try {
|
|
3106
3121
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -4621,6 +4636,127 @@ function formatConnectionStatus(selected, connections) {
|
|
|
4621
4636
|
`);
|
|
4622
4637
|
}
|
|
4623
4638
|
|
|
4639
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
4640
|
+
import pc4 from "picocolors";
|
|
4641
|
+
|
|
4642
|
+
// packages/cli/src/commands/_spinner.ts
|
|
4643
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
4644
|
+
function createTtySpinner(input) {
|
|
4645
|
+
const output = input.output ?? process.stdout;
|
|
4646
|
+
const isTty = output.isTTY === true;
|
|
4647
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
4648
|
+
let label = input.label;
|
|
4649
|
+
let frame = 0;
|
|
4650
|
+
let paused = false;
|
|
4651
|
+
let stopped = false;
|
|
4652
|
+
let lastPrintedLabel = "";
|
|
4653
|
+
const render = () => {
|
|
4654
|
+
if (stopped || paused)
|
|
4655
|
+
return;
|
|
4656
|
+
if (!isTty) {
|
|
4657
|
+
if (label !== lastPrintedLabel) {
|
|
4658
|
+
output.write(`${label}
|
|
4659
|
+
`);
|
|
4660
|
+
lastPrintedLabel = label;
|
|
4661
|
+
}
|
|
4662
|
+
return;
|
|
4663
|
+
}
|
|
4664
|
+
frame = (frame + 1) % frames.length;
|
|
4665
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
4666
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
4667
|
+
};
|
|
4668
|
+
const clearLine = () => {
|
|
4669
|
+
if (isTty)
|
|
4670
|
+
output.write("\r\x1B[2K");
|
|
4671
|
+
};
|
|
4672
|
+
render();
|
|
4673
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
4674
|
+
return {
|
|
4675
|
+
setLabel(next) {
|
|
4676
|
+
label = next;
|
|
4677
|
+
render();
|
|
4678
|
+
},
|
|
4679
|
+
pause() {
|
|
4680
|
+
paused = true;
|
|
4681
|
+
clearLine();
|
|
4682
|
+
},
|
|
4683
|
+
resume() {
|
|
4684
|
+
if (stopped)
|
|
4685
|
+
return;
|
|
4686
|
+
paused = false;
|
|
4687
|
+
render();
|
|
4688
|
+
},
|
|
4689
|
+
stop(finalLine) {
|
|
4690
|
+
if (stopped)
|
|
4691
|
+
return;
|
|
4692
|
+
stopped = true;
|
|
4693
|
+
if (timer)
|
|
4694
|
+
clearInterval(timer);
|
|
4695
|
+
clearLine();
|
|
4696
|
+
if (finalLine)
|
|
4697
|
+
output.write(`${finalLine}
|
|
4698
|
+
`);
|
|
4699
|
+
}
|
|
4700
|
+
};
|
|
4701
|
+
}
|
|
4702
|
+
|
|
4703
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
4704
|
+
var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
4705
|
+
var DONE_SYMBOL = pc4.green("\u25C7");
|
|
4706
|
+
var FAIL_SYMBOL = pc4.red("\u25A0");
|
|
4707
|
+
var activeUpdate = null;
|
|
4708
|
+
async function withSpinner(label, work, options = {}) {
|
|
4709
|
+
if (options.outputMode === "json") {
|
|
4710
|
+
return work(() => {});
|
|
4711
|
+
}
|
|
4712
|
+
if (activeUpdate) {
|
|
4713
|
+
const outer = activeUpdate;
|
|
4714
|
+
outer(label);
|
|
4715
|
+
return work(outer);
|
|
4716
|
+
}
|
|
4717
|
+
const output = options.output ?? process.stderr;
|
|
4718
|
+
const isTty = output.isTTY === true;
|
|
4719
|
+
let lastLabel = label;
|
|
4720
|
+
if (!isTty) {
|
|
4721
|
+
output.write(`${label}
|
|
4722
|
+
`);
|
|
4723
|
+
const update2 = (next) => {
|
|
4724
|
+
lastLabel = next;
|
|
4725
|
+
};
|
|
4726
|
+
activeUpdate = update2;
|
|
4727
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
4728
|
+
try {
|
|
4729
|
+
return await work(update2);
|
|
4730
|
+
} finally {
|
|
4731
|
+
activeUpdate = null;
|
|
4732
|
+
setServerPhaseListener(previousListener2);
|
|
4733
|
+
}
|
|
4734
|
+
}
|
|
4735
|
+
const spinner2 = createTtySpinner({
|
|
4736
|
+
label,
|
|
4737
|
+
output,
|
|
4738
|
+
frames: CLACK_SPINNER_FRAMES,
|
|
4739
|
+
styleFrame: (frame) => pc4.magenta(frame)
|
|
4740
|
+
});
|
|
4741
|
+
const update = (next) => {
|
|
4742
|
+
lastLabel = next;
|
|
4743
|
+
spinner2.setLabel(next);
|
|
4744
|
+
};
|
|
4745
|
+
activeUpdate = update;
|
|
4746
|
+
const previousListener = setServerPhaseListener(update);
|
|
4747
|
+
try {
|
|
4748
|
+
const result = await work(update);
|
|
4749
|
+
spinner2.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
4750
|
+
return result;
|
|
4751
|
+
} catch (error) {
|
|
4752
|
+
spinner2.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
4753
|
+
throw error;
|
|
4754
|
+
} finally {
|
|
4755
|
+
activeUpdate = null;
|
|
4756
|
+
setServerPhaseListener(previousListener);
|
|
4757
|
+
}
|
|
4758
|
+
}
|
|
4759
|
+
|
|
4624
4760
|
// packages/cli/src/commands/inbox.ts
|
|
4625
4761
|
async function listInboxRecords(context, kind, filters) {
|
|
4626
4762
|
const params = new URLSearchParams;
|
|
@@ -4725,7 +4861,7 @@ async function executeInbox(context, args) {
|
|
|
4725
4861
|
const task = takeOption(pending, "--task");
|
|
4726
4862
|
pending = task.rest;
|
|
4727
4863
|
requireNoExtraArgs(pending, "rig inbox approvals [--run <id>] [--task <id>]");
|
|
4728
|
-
const approvals = await listInboxRecords(context, "approvals", { run: run.value, task: task.value });
|
|
4864
|
+
const approvals = await withSpinner("Reading approvals from server\u2026", () => listInboxRecords(context, "approvals", { run: run.value, task: task.value }), { outputMode: context.outputMode });
|
|
4729
4865
|
renderList(context, "approvals", approvals);
|
|
4730
4866
|
return { ok: true, group: "inbox", command, details: { approvals } };
|
|
4731
4867
|
}
|
|
@@ -4736,7 +4872,7 @@ async function executeInbox(context, args) {
|
|
|
4736
4872
|
const task = takeOption(pending, "--task");
|
|
4737
4873
|
pending = task.rest;
|
|
4738
4874
|
requireNoExtraArgs(pending, "rig inbox inputs [--run <id>] [--task <id>]");
|
|
4739
|
-
const requests = await listInboxRecords(context, "inputs", { run: run.value, task: task.value });
|
|
4875
|
+
const requests = await withSpinner("Reading input requests from server\u2026", () => listInboxRecords(context, "inputs", { run: run.value, task: task.value }), { outputMode: context.outputMode });
|
|
4740
4876
|
renderList(context, "inputs", requests);
|
|
4741
4877
|
return { ok: true, group: "inbox", command, details: { requests } };
|
|
4742
4878
|
}
|
|
@@ -4757,7 +4893,7 @@ async function executeInbox(context, args) {
|
|
|
4757
4893
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
4758
4894
|
throw new CliError("decision must be approve or reject.", 1, { hint: "Re-run as `rig inbox approve --run <id> --request <id> --decision approve|reject`." });
|
|
4759
4895
|
}
|
|
4760
|
-
const result = await requestServerJson(context, "/api/inbox/approvals/resolve", {
|
|
4896
|
+
const result = await withSpinner(`Resolving approval ${request.value}\u2026`, () => requestServerJson(context, "/api/inbox/approvals/resolve", {
|
|
4761
4897
|
method: "POST",
|
|
4762
4898
|
headers: { "content-type": "application/json" },
|
|
4763
4899
|
body: JSON.stringify({
|
|
@@ -4766,7 +4902,7 @@ async function executeInbox(context, args) {
|
|
|
4766
4902
|
decision: decision.value,
|
|
4767
4903
|
note: note4.value ?? null
|
|
4768
4904
|
})
|
|
4769
|
-
});
|
|
4905
|
+
}), { outputMode: context.outputMode });
|
|
4770
4906
|
return { ok: true, group: "inbox", command, details: { result } };
|
|
4771
4907
|
}
|
|
4772
4908
|
case "respond": {
|
|
@@ -4800,7 +4936,7 @@ async function executeInbox(context, args) {
|
|
|
4800
4936
|
const [key, ...restValue] = entry.split("=");
|
|
4801
4937
|
return [key, restValue.join("=")];
|
|
4802
4938
|
}));
|
|
4803
|
-
const result = await requestServerJson(context, "/api/inbox/inputs/respond", {
|
|
4939
|
+
const result = await withSpinner(`Sending response for ${request.value}\u2026`, () => requestServerJson(context, "/api/inbox/inputs/respond", {
|
|
4804
4940
|
method: "POST",
|
|
4805
4941
|
headers: { "content-type": "application/json" },
|
|
4806
4942
|
body: JSON.stringify({
|
|
@@ -4808,7 +4944,7 @@ async function executeInbox(context, args) {
|
|
|
4808
4944
|
requestId: request.value,
|
|
4809
4945
|
answers: parsedAnswers
|
|
4810
4946
|
})
|
|
4811
|
-
});
|
|
4947
|
+
}), { outputMode: context.outputMode });
|
|
4812
4948
|
return { ok: true, group: "inbox", command, details: { result } };
|
|
4813
4949
|
}
|
|
4814
4950
|
case "watch": {
|
|
@@ -5225,7 +5361,10 @@ async function runRigDoctorChecks(options) {
|
|
|
5225
5361
|
const bunVersion = options.bunVersion ?? Bun.version;
|
|
5226
5362
|
const request = options.requestJson ?? ((pathname, init) => requestServerJson({ projectRoot }, pathname, init));
|
|
5227
5363
|
const loadConfig = options.loadConfig ?? loadRigConfigOrNull;
|
|
5364
|
+
const progress = options.onProgress ?? (() => {});
|
|
5365
|
+
progress("Checking local toolchain\u2026");
|
|
5228
5366
|
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`)."));
|
|
5367
|
+
progress("Loading rig.config\u2026");
|
|
5229
5368
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
5230
5369
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
5231
5370
|
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync11(resolve17(projectRoot, name)));
|
|
@@ -5244,6 +5383,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5244
5383
|
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));
|
|
5245
5384
|
let server = null;
|
|
5246
5385
|
try {
|
|
5386
|
+
progress("Connecting to the selected Rig server\u2026");
|
|
5247
5387
|
server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);
|
|
5248
5388
|
checks.push(check("server", "Rig server reachable", "pass", `${server.connectionKind} ${server.baseUrl}`));
|
|
5249
5389
|
} catch (error) {
|
|
@@ -5251,18 +5391,21 @@ async function runRigDoctorChecks(options) {
|
|
|
5251
5391
|
}
|
|
5252
5392
|
if (server || options.requestJson) {
|
|
5253
5393
|
try {
|
|
5394
|
+
progress("Checking server status\u2026");
|
|
5254
5395
|
const status = await request("/api/server/status");
|
|
5255
5396
|
checks.push(check("server-status", "server project status", "pass", JSON.stringify(status).slice(0, 180)));
|
|
5256
5397
|
} catch (error) {
|
|
5257
5398
|
checks.push(check("server-status", "server project status", "fail", errorMessage(error), "Run `rig doctor` after the selected server is reachable."));
|
|
5258
5399
|
}
|
|
5259
5400
|
try {
|
|
5401
|
+
progress("Checking GitHub auth\u2026");
|
|
5260
5402
|
const auth = await request("/api/github/auth/status");
|
|
5261
5403
|
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>`."));
|
|
5262
5404
|
} catch (error) {
|
|
5263
5405
|
checks.push(check("github-auth", "GitHub auth", "fail", errorMessage(error), "Authenticate GitHub through Rig and ensure the server exposes auth status."));
|
|
5264
5406
|
}
|
|
5265
5407
|
try {
|
|
5408
|
+
progress("Checking GitHub repo permissions\u2026");
|
|
5266
5409
|
const permissions = await request("/api/github/repo/permissions");
|
|
5267
5410
|
const allowed = permissionAllowsPr2(permissions);
|
|
5268
5411
|
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."));
|
|
@@ -5270,6 +5413,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5270
5413
|
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."));
|
|
5271
5414
|
}
|
|
5272
5415
|
try {
|
|
5416
|
+
progress("Checking GitHub issue labels\u2026");
|
|
5273
5417
|
const labels = await request("/api/workspace/task-labels");
|
|
5274
5418
|
const ready = labelsReady(labels);
|
|
5275
5419
|
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."));
|
|
@@ -5277,6 +5421,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5277
5421
|
checks.push(check("task-labels", "GitHub issue labels", "warn", errorMessage(error), "Run `rig init`/`rig doctor` after label setup is wired on the server."));
|
|
5278
5422
|
}
|
|
5279
5423
|
try {
|
|
5424
|
+
progress("Checking task projection\u2026");
|
|
5280
5425
|
const projection = await request("/api/workspace/task-projection");
|
|
5281
5426
|
checks.push(check("task-projection", "task projection", "pass", JSON.stringify(projection).slice(0, 180)));
|
|
5282
5427
|
} catch (error) {
|
|
@@ -5285,6 +5430,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5285
5430
|
const slug = projectStatusSlug(projectRoot, config);
|
|
5286
5431
|
if (slug) {
|
|
5287
5432
|
try {
|
|
5433
|
+
progress("Checking server project checkout\u2026");
|
|
5288
5434
|
const project = await request(`/api/projects/${encodeURIComponent(slug)}`);
|
|
5289
5435
|
checks.push(check("remote-checkout", "server project checkout", "pass", JSON.stringify(project).slice(0, 180)));
|
|
5290
5436
|
} catch (error) {
|
|
@@ -5299,6 +5445,7 @@ async function runRigDoctorChecks(options) {
|
|
|
5299
5445
|
}
|
|
5300
5446
|
checks.push(githubProjectsCheck(config));
|
|
5301
5447
|
checks.push(prMergeCheck(config));
|
|
5448
|
+
progress("Checking Pi installation\u2026");
|
|
5302
5449
|
const piChecks = await (options.piChecks ?? (() => buildPiSetupChecks()))().catch((error) => [{
|
|
5303
5450
|
ok: false,
|
|
5304
5451
|
label: "pi/pi-rig checks",
|
|
@@ -6221,7 +6368,7 @@ async function executeGithub(context, args) {
|
|
|
6221
6368
|
case "status": {
|
|
6222
6369
|
if (rest.length > 0)
|
|
6223
6370
|
throw new CliError("Usage: rig github auth status", 1);
|
|
6224
|
-
const status = await getGitHubAuthStatusViaServer(context);
|
|
6371
|
+
const status = await withSpinner("Checking GitHub auth on the server\u2026", () => getGitHubAuthStatusViaServer(context), { outputMode: context.outputMode });
|
|
6225
6372
|
printPayload(context, status, `GitHub auth: ${status.authenticated ? "authenticated" : "unauthenticated"}`);
|
|
6226
6373
|
return { ok: true, group: "github", command: "auth status", details: status };
|
|
6227
6374
|
}
|
|
@@ -6232,14 +6379,15 @@ async function executeGithub(context, args) {
|
|
|
6232
6379
|
const token = parsed.value?.trim();
|
|
6233
6380
|
if (!token)
|
|
6234
6381
|
throw new CliError("Missing --token value.", 1, { hint: "Re-run as `rig github auth token --token <token>`." });
|
|
6235
|
-
const result = await postGitHubTokenViaServer(context, token);
|
|
6382
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, token), { outputMode: context.outputMode });
|
|
6236
6383
|
printPayload(context, result, "GitHub token stored on the selected Rig server.");
|
|
6237
6384
|
return { ok: true, group: "github", command: "auth token", details: result };
|
|
6238
6385
|
}
|
|
6239
6386
|
case "import-gh": {
|
|
6240
6387
|
if (rest.length > 0)
|
|
6241
6388
|
throw new CliError("Usage: rig github auth import-gh", 1);
|
|
6242
|
-
const
|
|
6389
|
+
const importedToken = readGhToken();
|
|
6390
|
+
const result = await withSpinner("Storing GitHub token on the server\u2026", () => postGitHubTokenViaServer(context, importedToken), { outputMode: context.outputMode });
|
|
6243
6391
|
printPayload(context, result, "GitHub token imported from gh and stored on the selected Rig server.");
|
|
6244
6392
|
return { ok: true, group: "github", command: "auth import-gh", details: result };
|
|
6245
6393
|
}
|
|
@@ -6252,7 +6400,7 @@ async function executeGithub(context, args) {
|
|
|
6252
6400
|
init_runner();
|
|
6253
6401
|
async function executeDoctor(context, args) {
|
|
6254
6402
|
requireNoExtraArgs(args, "rig doctor");
|
|
6255
|
-
const checks = await runRigDoctorChecks({ projectRoot: context.projectRoot });
|
|
6403
|
+
const checks = await withSpinner("Running doctor checks\u2026", (update) => runRigDoctorChecks({ projectRoot: context.projectRoot, onProgress: update }), { outputMode: context.outputMode });
|
|
6256
6404
|
if (context.outputMode === "text") {
|
|
6257
6405
|
console.log(formatDoctorChecks(checks));
|
|
6258
6406
|
}
|
|
@@ -6559,13 +6707,19 @@ async function executeInspect(context, args) {
|
|
|
6559
6707
|
const requiredTask = requireTask(task, "rig inspect logs --task <task-id>");
|
|
6560
6708
|
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];
|
|
6561
6709
|
if (!latestRun) {
|
|
6562
|
-
const
|
|
6563
|
-
|
|
6564
|
-
|
|
6710
|
+
const fallback = await withSpinner(`Finding runs for ${requiredTask} on the server\u2026`, async (update) => {
|
|
6711
|
+
const serverRuns = await listRunsViaServer(context, { limit: 200 }).catch(() => []);
|
|
6712
|
+
const serverRun = serverRuns.filter((run) => String(run.taskId ?? "") === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
|
|
6713
|
+
if (!serverRun || typeof serverRun.runId !== "string")
|
|
6714
|
+
return null;
|
|
6715
|
+
update(`Reading logs for run ${serverRun.runId}\u2026`);
|
|
6716
|
+
const page = await getRunLogsViaServer(context, serverRun.runId, { limit: 500 });
|
|
6717
|
+
return { runId: serverRun.runId, page };
|
|
6718
|
+
}, { outputMode: context.outputMode });
|
|
6719
|
+
if (!fallback) {
|
|
6565
6720
|
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`." });
|
|
6566
6721
|
}
|
|
6567
|
-
const
|
|
6568
|
-
const entries = Array.isArray(page.entries) ? page.entries : [];
|
|
6722
|
+
const entries = Array.isArray(fallback.page.entries) ? fallback.page.entries : [];
|
|
6569
6723
|
if (context.outputMode === "text") {
|
|
6570
6724
|
for (const entry of entries) {
|
|
6571
6725
|
const record = entry && typeof entry === "object" ? entry : {};
|
|
@@ -6574,9 +6728,9 @@ async function executeInspect(context, args) {
|
|
|
6574
6728
|
console.log([title, detail].filter(Boolean).join(" \u2014 "));
|
|
6575
6729
|
}
|
|
6576
6730
|
if (entries.length === 0)
|
|
6577
|
-
console.log(`(no log entries for run ${
|
|
6731
|
+
console.log(`(no log entries for run ${fallback.runId})`);
|
|
6578
6732
|
}
|
|
6579
|
-
return { ok: true, group: "inspect", command, details: { task: requiredTask, runId:
|
|
6733
|
+
return { ok: true, group: "inspect", command, details: { task: requiredTask, runId: fallback.runId, source: "server", entries } };
|
|
6580
6734
|
}
|
|
6581
6735
|
const logsPath = resolve20(resolveAuthorityRunDir2(context.projectRoot, latestRun.runId), "logs.jsonl");
|
|
6582
6736
|
if (!existsSync13(logsPath)) {
|
|
@@ -6634,7 +6788,7 @@ async function executeInspect(context, args) {
|
|
|
6634
6788
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
6635
6789
|
requireNoExtraArgs(remaining, "rig inspect diff [--task <task-id>]");
|
|
6636
6790
|
if (task) {
|
|
6637
|
-
const files = changedFilesForTask(context.projectRoot, task, false);
|
|
6791
|
+
const files = await withSpinner(`Computing changed files for ${task}\u2026`, async () => changedFilesForTask(context.projectRoot, task, false), { outputMode: context.outputMode });
|
|
6638
6792
|
for (const file of files) {
|
|
6639
6793
|
console.log(file);
|
|
6640
6794
|
}
|
|
@@ -7722,7 +7876,12 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
7722
7876
|
};
|
|
7723
7877
|
let detached = false;
|
|
7724
7878
|
try {
|
|
7725
|
-
await runPiMain([
|
|
7879
|
+
await runPiMain([
|
|
7880
|
+
"--no-extensions",
|
|
7881
|
+
"--no-skills",
|
|
7882
|
+
"--no-prompt-templates",
|
|
7883
|
+
"--no-context-files"
|
|
7884
|
+
], {
|
|
7726
7885
|
extensionFactories: [piRigExtensionFactory]
|
|
7727
7886
|
});
|
|
7728
7887
|
detached = true;
|
|
@@ -7787,8 +7946,9 @@ async function readOperatorSnapshot(context, runId, options = {}) {
|
|
|
7787
7946
|
}
|
|
7788
7947
|
async function attachRunOperatorView(context, input) {
|
|
7789
7948
|
let steered = false;
|
|
7790
|
-
|
|
7791
|
-
|
|
7949
|
+
const attachMessage = input.message?.trim();
|
|
7950
|
+
if (attachMessage) {
|
|
7951
|
+
await withSpinner("Queueing steering message\u2026", () => sendRunPiPromptViaServer(context, input.runId, attachMessage, "steer").catch(() => steerRunViaServer(context, input.runId, attachMessage)), { outputMode: context.outputMode });
|
|
7792
7952
|
steered = true;
|
|
7793
7953
|
}
|
|
7794
7954
|
if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
@@ -7798,7 +7958,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
7798
7958
|
});
|
|
7799
7959
|
}
|
|
7800
7960
|
const surface = createOperatorSurface({ interactive: input.interactive !== false });
|
|
7801
|
-
let snapshot = await readOperatorSnapshot(context, input.runId);
|
|
7961
|
+
let snapshot = await withSpinner(`Connecting to run ${input.runId}\u2026`, () => readOperatorSnapshot(context, input.runId), { outputMode: context.outputMode });
|
|
7802
7962
|
if (context.outputMode === "text") {
|
|
7803
7963
|
surface.renderSnapshot(snapshot);
|
|
7804
7964
|
surface.renderTimeline(snapshot.timeline);
|
|
@@ -7849,9 +8009,6 @@ function normalizeRemoteRunDetails(payload) {
|
|
|
7849
8009
|
};
|
|
7850
8010
|
}
|
|
7851
8011
|
var REMOTE_TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged"]);
|
|
7852
|
-
function isRemoteConnectionSelected(projectRoot) {
|
|
7853
|
-
return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
|
|
7854
|
-
}
|
|
7855
8012
|
async function listRunsForSelectedConnection(context, options = {}) {
|
|
7856
8013
|
if (isRemoteConnectionSelected(context.projectRoot)) {
|
|
7857
8014
|
return { runs: await listRunsViaServer(context, options), source: "server" };
|
|
@@ -7932,7 +8089,7 @@ async function executeRun(context, args) {
|
|
|
7932
8089
|
switch (command) {
|
|
7933
8090
|
case "list": {
|
|
7934
8091
|
requireNoExtraArgs(rest, "rig run list");
|
|
7935
|
-
const { runs, source } = await listRunsForSelectedConnection(context, { limit: 100 });
|
|
8092
|
+
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 });
|
|
7936
8093
|
if (context.outputMode === "text") {
|
|
7937
8094
|
printFormattedOutput(formatRunList(runs, { source }));
|
|
7938
8095
|
await printPendingInboxFooter(context);
|
|
@@ -8005,7 +8162,7 @@ async function executeRun(context, args) {
|
|
|
8005
8162
|
if (!runId) {
|
|
8006
8163
|
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>`." });
|
|
8007
8164
|
}
|
|
8008
|
-
const record = readAuthorityRun5(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, runId).catch(() => ({})));
|
|
8165
|
+
const record = readAuthorityRun5(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await withSpinner(`Reading run ${runId} from server\u2026`, () => getRunDetailsViaServer(context, runId).catch(() => ({})), { outputMode: context.outputMode }));
|
|
8009
8166
|
if (!record) {
|
|
8010
8167
|
throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see recorded runs." });
|
|
8011
8168
|
}
|
|
@@ -8026,7 +8183,8 @@ async function executeRun(context, args) {
|
|
|
8026
8183
|
}
|
|
8027
8184
|
const renderer = createPiRunStreamRenderer();
|
|
8028
8185
|
let cursor = null;
|
|
8029
|
-
const
|
|
8186
|
+
const timelineRunId = run.value;
|
|
8187
|
+
const page = await withSpinner(`Reading timeline for ${timelineRunId}\u2026`, () => getRunTimelineViaServer(context, timelineRunId, { limit: 500 }), { outputMode: context.outputMode });
|
|
8030
8188
|
const events = Array.isArray(page.entries) ? page.entries.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
|
|
8031
8189
|
cursor = typeof page.nextCursor === "string" ? page.nextCursor : null;
|
|
8032
8190
|
if (context.outputMode === "text") {
|
|
@@ -8103,8 +8261,9 @@ async function executeRun(context, args) {
|
|
|
8103
8261
|
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`." });
|
|
8104
8262
|
}
|
|
8105
8263
|
let steered = false;
|
|
8106
|
-
|
|
8107
|
-
|
|
8264
|
+
const steerMessage = messageOption.value?.trim();
|
|
8265
|
+
if (steerMessage) {
|
|
8266
|
+
await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, steerMessage), { outputMode: context.outputMode });
|
|
8108
8267
|
steered = true;
|
|
8109
8268
|
}
|
|
8110
8269
|
const attached = await attachRunOperatorView(context, {
|
|
@@ -8124,7 +8283,7 @@ async function executeRun(context, args) {
|
|
|
8124
8283
|
}
|
|
8125
8284
|
return { ok: true, group: "run", command };
|
|
8126
8285
|
}
|
|
8127
|
-
const summary = isRemoteConnectionSelected(context.projectRoot) ? buildServerRunStatus(await listRunsViaServer(context, { limit: 100 })) : runStatus(context.projectRoot, runtimeContext);
|
|
8286
|
+
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);
|
|
8128
8287
|
const activeRuns = Array.isArray(summary.activeRuns) ? summary.activeRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
|
|
8129
8288
|
const recentRuns = Array.isArray(summary.recentRuns) ? summary.recentRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
|
|
8130
8289
|
if (context.outputMode === "text") {
|
|
@@ -8201,28 +8360,38 @@ async function executeRun(context, args) {
|
|
|
8201
8360
|
};
|
|
8202
8361
|
}
|
|
8203
8362
|
case "resume": {
|
|
8204
|
-
|
|
8363
|
+
let pending = rest;
|
|
8364
|
+
const runOpt = takeOption(pending, "--run");
|
|
8365
|
+
pending = runOpt.rest;
|
|
8366
|
+
const positional = pending[0] && !pending[0].startsWith("-") ? pending.shift() : undefined;
|
|
8367
|
+
requireNoExtraArgs(pending, "rig run resume [<run-id>] [--run <id>]");
|
|
8368
|
+
const targetRunId = runOpt.value ?? positional ?? null;
|
|
8205
8369
|
if (context.dryRun) {
|
|
8206
8370
|
if (context.outputMode === "text") {
|
|
8207
8371
|
console.log("[dry-run] rig run resume");
|
|
8208
8372
|
}
|
|
8209
8373
|
return { ok: true, group: "run", command };
|
|
8210
8374
|
}
|
|
8211
|
-
const resumed = await runResume(context.projectRoot, runtimeContext);
|
|
8375
|
+
const resumed = await runResume(context.projectRoot, runtimeContext, targetRunId);
|
|
8212
8376
|
if (context.outputMode === "text") {
|
|
8213
8377
|
console.log(`Resumed run: ${resumed.runId}`);
|
|
8214
8378
|
}
|
|
8215
8379
|
return { ok: true, group: "run", command, details: resumed };
|
|
8216
8380
|
}
|
|
8217
8381
|
case "restart": {
|
|
8218
|
-
|
|
8382
|
+
let pending = rest;
|
|
8383
|
+
const runOpt = takeOption(pending, "--run");
|
|
8384
|
+
pending = runOpt.rest;
|
|
8385
|
+
const positional = pending[0] && !pending[0].startsWith("-") ? pending.shift() : undefined;
|
|
8386
|
+
requireNoExtraArgs(pending, "rig run restart [<run-id>] [--run <id>]");
|
|
8387
|
+
const targetRunId = runOpt.value ?? positional ?? null;
|
|
8219
8388
|
if (context.dryRun) {
|
|
8220
8389
|
if (context.outputMode === "text") {
|
|
8221
8390
|
console.log("[dry-run] rig run restart");
|
|
8222
8391
|
}
|
|
8223
8392
|
return { ok: true, group: "run", command };
|
|
8224
8393
|
}
|
|
8225
|
-
const restarted = await runRestart(context.projectRoot, runtimeContext);
|
|
8394
|
+
const restarted = await runRestart(context.projectRoot, runtimeContext, targetRunId);
|
|
8226
8395
|
if (context.outputMode === "text") {
|
|
8227
8396
|
console.log(`Restarted run: ${restarted.runId}`);
|
|
8228
8397
|
}
|
|
@@ -8249,7 +8418,8 @@ async function executeRun(context, args) {
|
|
|
8249
8418
|
}
|
|
8250
8419
|
return { ok: true, group: "run", command, details: { runId, dryRun: true } };
|
|
8251
8420
|
}
|
|
8252
|
-
|
|
8421
|
+
const trimmedMessage = message2.trim();
|
|
8422
|
+
await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, trimmedMessage), { outputMode: context.outputMode });
|
|
8253
8423
|
if (context.outputMode === "text") {
|
|
8254
8424
|
console.log(`Steering message queued for ${runId}.`);
|
|
8255
8425
|
}
|
|
@@ -8270,7 +8440,7 @@ async function executeRun(context, args) {
|
|
|
8270
8440
|
};
|
|
8271
8441
|
}
|
|
8272
8442
|
if (runId) {
|
|
8273
|
-
const stopped = await stopRunViaServer(context, runId);
|
|
8443
|
+
const stopped = await withSpinner(`Requesting stop for ${runId}\u2026`, () => stopRunViaServer(context, runId), { outputMode: context.outputMode });
|
|
8274
8444
|
if (context.outputMode === "text")
|
|
8275
8445
|
console.log(`Stop requested: ${runId}`);
|
|
8276
8446
|
return { ok: true, group: "run", command, details: stopped };
|
|
@@ -8516,15 +8686,12 @@ async function executeServer(context, args, options) {
|
|
|
8516
8686
|
|
|
8517
8687
|
// packages/cli/src/commands/stats.ts
|
|
8518
8688
|
init_runner();
|
|
8519
|
-
import
|
|
8520
|
-
import {
|
|
8521
|
-
listAuthorityRuns as listAuthorityRuns3,
|
|
8522
|
-
projectRunFromJournal
|
|
8523
|
-
} from "@rig/runtime/control-plane/authority-files";
|
|
8689
|
+
import pc6 from "picocolors";
|
|
8690
|
+
import { computeRigStats } from "@rig/runtime/control-plane/native/run-stats";
|
|
8524
8691
|
|
|
8525
8692
|
// packages/cli/src/commands/_help-catalog.ts
|
|
8526
8693
|
import { intro as intro3, log as log4, note as note4, outro as outro3 } from "@clack/prompts";
|
|
8527
|
-
import
|
|
8694
|
+
import pc5 from "picocolors";
|
|
8528
8695
|
var TOP_LEVEL_SECTIONS = [
|
|
8529
8696
|
{
|
|
8530
8697
|
title: "Start here",
|
|
@@ -8812,13 +8979,13 @@ var ADVANCED_COMMANDS = [
|
|
|
8812
8979
|
];
|
|
8813
8980
|
var ALL_GROUPS = [...PRIMARY_GROUPS, ...ADVANCED_GROUPS];
|
|
8814
8981
|
function heading(title) {
|
|
8815
|
-
return
|
|
8982
|
+
return pc5.bold(pc5.cyan(title));
|
|
8816
8983
|
}
|
|
8817
8984
|
function renderRigBanner(version) {
|
|
8818
|
-
const m = (s) =>
|
|
8819
|
-
const c = (s) =>
|
|
8820
|
-
const y = (s) =>
|
|
8821
|
-
const d = (s) =>
|
|
8985
|
+
const m = (s) => pc5.bold(pc5.magenta(s));
|
|
8986
|
+
const c = (s) => pc5.bold(pc5.cyan(s));
|
|
8987
|
+
const y = (s) => pc5.yellow(s);
|
|
8988
|
+
const d = (s) => pc5.dim(s);
|
|
8822
8989
|
const lines = [
|
|
8823
8990
|
m(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + d(" \u2591\u2592\u2593\u2588 "),
|
|
8824
8991
|
m(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ") + d("\u2593\u2592\u2591"),
|
|
@@ -8827,7 +8994,7 @@ function renderRigBanner(version) {
|
|
|
8827
8994
|
y(" \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + d(" \u2592\u2591"),
|
|
8828
8995
|
y(" \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D "),
|
|
8829
8996
|
"",
|
|
8830
|
-
` ${c("\u25E2\u25E4")} ${
|
|
8997
|
+
` ${c("\u25E2\u25E4")} ${pc5.bold("the control rig for autonomous coding agents")} ${m("//")} ${d("you are the operator")}`,
|
|
8831
8998
|
version ? ` ${d(`v${version} \xB7 jack in: rig task run --next`)}` : ` ${d("jack in: rig task run --next")}`
|
|
8832
8999
|
];
|
|
8833
9000
|
return lines.join(`
|
|
@@ -8835,7 +9002,7 @@ function renderRigBanner(version) {
|
|
|
8835
9002
|
}
|
|
8836
9003
|
function commandLine(command, description) {
|
|
8837
9004
|
const commandColumn = command.length >= 38 ? `${command} ` : command.padEnd(38);
|
|
8838
|
-
return `${
|
|
9005
|
+
return `${pc5.dim("\u2502")} ${pc5.bold(commandColumn)} ${description}`;
|
|
8839
9006
|
}
|
|
8840
9007
|
function renderCommandBlock(commands) {
|
|
8841
9008
|
return commands.map((entry) => commandLine(entry.command, entry.description)).join(`
|
|
@@ -8845,37 +9012,37 @@ function renderGroup(group) {
|
|
|
8845
9012
|
const lines = [
|
|
8846
9013
|
`${heading(`rig ${group.name}`)} \u2014 ${group.summary}`,
|
|
8847
9014
|
"",
|
|
8848
|
-
|
|
9015
|
+
pc5.bold("Usage"),
|
|
8849
9016
|
...group.usage.map((line) => ` ${line}`),
|
|
8850
9017
|
"",
|
|
8851
|
-
|
|
9018
|
+
pc5.bold("Commands"),
|
|
8852
9019
|
...group.commands.map((entry) => commandLine(entry.command, entry.description))
|
|
8853
9020
|
];
|
|
8854
9021
|
if (group.examples?.length) {
|
|
8855
|
-
lines.push("",
|
|
9022
|
+
lines.push("", pc5.bold("Examples"), ...group.examples.map((line) => ` ${pc5.dim("$")} ${line}`));
|
|
8856
9023
|
}
|
|
8857
9024
|
if (group.next?.length) {
|
|
8858
|
-
lines.push("",
|
|
9025
|
+
lines.push("", pc5.bold("Next steps"), ...group.next.map((line) => ` ${pc5.dim("\u203A")} ${line}`));
|
|
8859
9026
|
}
|
|
8860
9027
|
if (group.advanced?.length) {
|
|
8861
|
-
lines.push("",
|
|
9028
|
+
lines.push("", pc5.bold("Compatibility / advanced"), ...group.advanced.map((line) => ` ${pc5.dim("\u203A")} ${line}`));
|
|
8862
9029
|
}
|
|
8863
9030
|
return lines.join(`
|
|
8864
9031
|
`);
|
|
8865
9032
|
}
|
|
8866
9033
|
function renderTopLevelHelp() {
|
|
8867
9034
|
return [
|
|
8868
|
-
`${heading("rig")} ${
|
|
8869
|
-
|
|
9035
|
+
`${heading("rig")} ${pc5.dim("\u2014 server-owned task/run control plane for Pi-backed engineering work")}`,
|
|
9036
|
+
pc5.dim("Current path: select a server, choose a task, submit a run, attach with native Pi, clear inbox gates."),
|
|
8870
9037
|
"",
|
|
8871
9038
|
...TOP_LEVEL_SECTIONS.flatMap((section) => [
|
|
8872
|
-
`${
|
|
9039
|
+
`${pc5.bold(pc5.magenta(`\u25C7 ${section.title}`))} \u2014 ${pc5.dim(section.subtitle)}`,
|
|
8873
9040
|
renderCommandBlock(section.commands),
|
|
8874
9041
|
""
|
|
8875
9042
|
]),
|
|
8876
|
-
|
|
9043
|
+
pc5.dim("More: `rig help --advanced` for dev/compatibility commands; `rig <group> --help` for rich per-group help; `rig --version` for the installed version."),
|
|
8877
9044
|
"",
|
|
8878
|
-
|
|
9045
|
+
pc5.bold("Global options"),
|
|
8879
9046
|
commandLine("--project <path>", "Use a project root instead of auto-discovery."),
|
|
8880
9047
|
commandLine("--json", "Emit structured output for scripts/agents."),
|
|
8881
9048
|
commandLine("--dry-run", "Print the command plan without mutating state.")
|
|
@@ -8886,16 +9053,16 @@ function renderAdvancedHelp() {
|
|
|
8886
9053
|
return [
|
|
8887
9054
|
`${heading("rig advanced")} \u2014 compatibility, diagnostics, and internal surfaces`,
|
|
8888
9055
|
"",
|
|
8889
|
-
|
|
9056
|
+
pc5.bold("Primary groups"),
|
|
8890
9057
|
" init, server, task, run, inbox, repo, plugin, inspect, stats, doctor, github",
|
|
8891
9058
|
"",
|
|
8892
|
-
|
|
9059
|
+
pc5.bold("Advanced commands"),
|
|
8893
9060
|
...ADVANCED_COMMANDS.map((entry) => commandLine(entry.command, entry.description)),
|
|
8894
9061
|
"",
|
|
8895
|
-
|
|
9062
|
+
pc5.bold("Advanced groups"),
|
|
8896
9063
|
...ADVANCED_GROUPS.map((group) => commandLine(group.name, group.summary)),
|
|
8897
9064
|
"",
|
|
8898
|
-
|
|
9065
|
+
pc5.dim("All groups remain callable. Prefer `rig server`, `rig task`, `rig run`, and `rig inbox` for day-to-day work.")
|
|
8899
9066
|
].join(`
|
|
8900
9067
|
`);
|
|
8901
9068
|
}
|
|
@@ -9009,90 +9176,6 @@ function parseSinceOption(value, now = new Date) {
|
|
|
9009
9176
|
}
|
|
9010
9177
|
return new Date(parsed);
|
|
9011
9178
|
}
|
|
9012
|
-
function median(values) {
|
|
9013
|
-
if (values.length === 0)
|
|
9014
|
-
return null;
|
|
9015
|
-
const sorted = [...values].sort((left, right) => left - right);
|
|
9016
|
-
const middle = Math.floor(sorted.length / 2);
|
|
9017
|
-
const upper = sorted[middle];
|
|
9018
|
-
if (sorted.length % 2 === 1)
|
|
9019
|
-
return upper;
|
|
9020
|
-
return (sorted[middle - 1] + upper) / 2;
|
|
9021
|
-
}
|
|
9022
|
-
function rate(part, total) {
|
|
9023
|
-
if (total === 0)
|
|
9024
|
-
return null;
|
|
9025
|
-
return part / total;
|
|
9026
|
-
}
|
|
9027
|
-
function parseTimestamp(value) {
|
|
9028
|
-
if (!value)
|
|
9029
|
-
return null;
|
|
9030
|
-
const parsed = Date.parse(value);
|
|
9031
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
9032
|
-
}
|
|
9033
|
-
function computeRigStats(projectRoot, options = {}) {
|
|
9034
|
-
const since = options.since ?? null;
|
|
9035
|
-
const sinceMs = since ? since.getTime() : null;
|
|
9036
|
-
const runs = listAuthorityRuns3(projectRoot).filter((run) => {
|
|
9037
|
-
if (sinceMs === null)
|
|
9038
|
-
return true;
|
|
9039
|
-
const createdAt = parseTimestamp(run.createdAt) ?? parseTimestamp(run.updatedAt);
|
|
9040
|
-
return createdAt !== null && createdAt >= sinceMs;
|
|
9041
|
-
});
|
|
9042
|
-
const statusCounts = {};
|
|
9043
|
-
const completionDurations = [];
|
|
9044
|
-
let steeringTotal = 0;
|
|
9045
|
-
let stallTotal = 0;
|
|
9046
|
-
let approvalsApproved = 0;
|
|
9047
|
-
let approvalsRejected = 0;
|
|
9048
|
-
let approvalsPending = 0;
|
|
9049
|
-
for (const run of runs) {
|
|
9050
|
-
const status = run.status ?? "unknown";
|
|
9051
|
-
statusCounts[status] = (statusCounts[status] ?? 0) + 1;
|
|
9052
|
-
if (status === "completed") {
|
|
9053
|
-
const createdAt = parseTimestamp(run.createdAt);
|
|
9054
|
-
const completedAt = parseTimestamp(run.completedAt);
|
|
9055
|
-
if (createdAt !== null && completedAt !== null && completedAt >= createdAt) {
|
|
9056
|
-
completionDurations.push(completedAt - createdAt);
|
|
9057
|
-
}
|
|
9058
|
-
}
|
|
9059
|
-
const projection = projectRunFromJournal(projectRoot, run.runId);
|
|
9060
|
-
if (!projection)
|
|
9061
|
-
continue;
|
|
9062
|
-
steeringTotal += projection.steeringCount;
|
|
9063
|
-
stallTotal += projection.stallCount;
|
|
9064
|
-
approvalsPending += projection.pendingApprovals.length;
|
|
9065
|
-
for (const resolved of projection.resolvedApprovals) {
|
|
9066
|
-
if (resolved.decision === "approve")
|
|
9067
|
-
approvalsApproved += 1;
|
|
9068
|
-
else
|
|
9069
|
-
approvalsRejected += 1;
|
|
9070
|
-
}
|
|
9071
|
-
}
|
|
9072
|
-
const totalRuns = runs.length;
|
|
9073
|
-
const completedRuns = statusCounts["completed"] ?? 0;
|
|
9074
|
-
const failedRuns = statusCounts["failed"] ?? 0;
|
|
9075
|
-
const needsAttentionRuns = statusCounts["needs-attention"] ?? 0;
|
|
9076
|
-
return {
|
|
9077
|
-
since: since ? since.toISOString() : null,
|
|
9078
|
-
totalRuns,
|
|
9079
|
-
statusCounts,
|
|
9080
|
-
completedRuns,
|
|
9081
|
-
failedRuns,
|
|
9082
|
-
needsAttentionRuns,
|
|
9083
|
-
completionRate: rate(completedRuns, totalRuns),
|
|
9084
|
-
failureRate: rate(failedRuns, totalRuns),
|
|
9085
|
-
needsAttentionRate: rate(needsAttentionRuns, totalRuns),
|
|
9086
|
-
medianCompletionMs: median(completionDurations),
|
|
9087
|
-
steeringTotal,
|
|
9088
|
-
steeringPerRun: totalRuns === 0 ? null : steeringTotal / totalRuns,
|
|
9089
|
-
stallTotal,
|
|
9090
|
-
approvalsRequested: approvalsApproved + approvalsRejected + approvalsPending,
|
|
9091
|
-
approvalsApproved,
|
|
9092
|
-
approvalsRejected,
|
|
9093
|
-
approvalsPending
|
|
9094
|
-
};
|
|
9095
|
-
}
|
|
9096
9179
|
function formatPercent(value) {
|
|
9097
9180
|
if (value === null)
|
|
9098
9181
|
return "\u2014";
|
|
@@ -9127,11 +9210,11 @@ function formatStatsReport(stats) {
|
|
|
9127
9210
|
const keyWidth = Math.max(...rows.map(([key]) => key.length));
|
|
9128
9211
|
const lines = [
|
|
9129
9212
|
formatSection("Fleet stats", window),
|
|
9130
|
-
...rows.map(([key, value]) => `${
|
|
9213
|
+
...rows.map(([key, value]) => `${pc6.dim("\u2502")} ${pc6.dim(key.padEnd(keyWidth + 2))} ${value}`)
|
|
9131
9214
|
];
|
|
9132
9215
|
const otherStatuses = Object.entries(stats.statusCounts).filter(([status]) => !["completed", "failed", "needs-attention"].includes(status)).sort(([, left], [, right]) => right - left);
|
|
9133
9216
|
if (otherStatuses.length > 0) {
|
|
9134
|
-
lines.push(`${
|
|
9217
|
+
lines.push(`${pc6.dim("\u2502")} ${pc6.dim("other".padEnd(keyWidth + 2))} ${otherStatuses.map(([status, count]) => `${status}:${count}`).join(" ")}`);
|
|
9135
9218
|
}
|
|
9136
9219
|
lines.push("", ...formatNextSteps([
|
|
9137
9220
|
"Inspect a run: `rig run list` then `rig run show <run-id>`",
|
|
@@ -9150,7 +9233,8 @@ async function executeStats(context, args) {
|
|
|
9150
9233
|
const sinceResult = takeOption(pending, "--since");
|
|
9151
9234
|
requireNoExtraArgs(sinceResult.rest, "rig stats [show] [--since <7d|30d|ISO date>]");
|
|
9152
9235
|
const since = parseSinceOption(sinceResult.value);
|
|
9153
|
-
const
|
|
9236
|
+
const remoteStats = isRemoteConnectionSelected(context.projectRoot);
|
|
9237
|
+
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 });
|
|
9154
9238
|
if (context.outputMode === "text") {
|
|
9155
9239
|
printFormattedOutput(formatStatsReport(stats));
|
|
9156
9240
|
}
|
|
@@ -9405,7 +9489,7 @@ async function executeTask(context, args, options) {
|
|
|
9405
9489
|
pending = rawResult.rest;
|
|
9406
9490
|
const { filters, rest: remaining } = parseTaskFilters(pending);
|
|
9407
9491
|
requireNoExtraArgs(remaining, "rig task list [--raw] [--assignee <login|me|@me>] [--state open|closed] [--status <status>] [--limit <n>]");
|
|
9408
|
-
const tasks = await listWorkspaceTasksViaServer(context, filters);
|
|
9492
|
+
const tasks = await withSpinner("Reading tasks from server\u2026", () => listWorkspaceTasksViaServer(context, filters), { outputMode: context.outputMode });
|
|
9409
9493
|
if (context.outputMode === "text") {
|
|
9410
9494
|
const renderedTasks = rawResult.value ? tasks.map((task) => summarizeTask(task, { raw: true })) : tasks.map((task) => summarizeTask(task));
|
|
9411
9495
|
printFormattedOutput(formatTaskList(renderedTasks, { raw: rawResult.value }));
|
|
@@ -9429,7 +9513,7 @@ async function executeTask(context, args, options) {
|
|
|
9429
9513
|
const taskId3 = normalizeTaskRunTaskId(taskOption.value ?? positional);
|
|
9430
9514
|
if (!taskId3)
|
|
9431
9515
|
throw new CliError("task show requires a task id.", 2, { hint: "Run `rig task list` to find ids, then `rig task show <id>`." });
|
|
9432
|
-
const task = await getWorkspaceTaskViaServer(context, taskId3);
|
|
9516
|
+
const task = await withSpinner(`Reading task ${taskId3} from server\u2026`, () => getWorkspaceTaskViaServer(context, taskId3), { outputMode: context.outputMode });
|
|
9433
9517
|
if (!task)
|
|
9434
9518
|
throw new CliError(`Task not found: ${taskId3}`, 3, { hint: "Run `rig task list` to see available tasks." });
|
|
9435
9519
|
const summary = summarizeTask(task, { raw: true });
|
|
@@ -9441,7 +9525,7 @@ async function executeTask(context, args, options) {
|
|
|
9441
9525
|
case "next": {
|
|
9442
9526
|
const { filters, rest: remaining } = parseTaskFilters(rest);
|
|
9443
9527
|
requireNoExtraArgs(remaining, "rig task next [--assignee <login|me|@me>] [--state open|closed] [--status <status>] [--limit <n>]");
|
|
9444
|
-
const selected = await selectNextWorkspaceTaskViaServer(context, filters);
|
|
9528
|
+
const selected = await withSpinner("Selecting next ready task\u2026", () => selectNextWorkspaceTaskViaServer(context, filters), { outputMode: context.outputMode });
|
|
9445
9529
|
if (context.outputMode === "text") {
|
|
9446
9530
|
if (selected.task) {
|
|
9447
9531
|
printFormattedOutput(formatTaskCard(summarizeTask(selected.task, { raw: true }), { title: "Selected task", selected: true }));
|
|
@@ -9591,7 +9675,7 @@ async function executeTask(context, args, options) {
|
|
|
9591
9675
|
let selectedTask = null;
|
|
9592
9676
|
let selectedTaskId = normalizeTaskRunTaskId(taskResult.value) ?? positionalTaskId;
|
|
9593
9677
|
if (nextResult.value) {
|
|
9594
|
-
const selected = await selectNextWorkspaceTaskViaServer(context, filterResult.filters);
|
|
9678
|
+
const selected = await withSpinner("Selecting next ready task\u2026", () => selectNextWorkspaceTaskViaServer(context, filterResult.filters), { outputMode: context.outputMode });
|
|
9595
9679
|
selectedTask = selected.task;
|
|
9596
9680
|
selectedTaskId = selected.task ? readTaskId(selected.task) : null;
|
|
9597
9681
|
if (!selectedTaskId) {
|
|
@@ -9602,7 +9686,7 @@ async function executeTask(context, args, options) {
|
|
|
9602
9686
|
if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
9603
9687
|
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);
|
|
9604
9688
|
}
|
|
9605
|
-
const tasks = await listWorkspaceTasksViaServer(context, filterResult.filters);
|
|
9689
|
+
const tasks = await withSpinner("Reading tasks from server\u2026", () => listWorkspaceTasksViaServer(context, filterResult.filters), { outputMode: context.outputMode });
|
|
9606
9690
|
selectedTask = await selectTaskWithTextPicker(tasks);
|
|
9607
9691
|
selectedTaskId = selectedTask ? readTaskId(selectedTask) : null;
|
|
9608
9692
|
if (!selectedTaskId) {
|
|
@@ -9612,13 +9696,13 @@ async function executeTask(context, args, options) {
|
|
|
9612
9696
|
await runProjectMainSyncPreflight(context, { disabled: skipProjectSyncResult.value });
|
|
9613
9697
|
const projectDefaults = await loadTaskRunProjectDefaults(context.projectRoot);
|
|
9614
9698
|
const runtimeAdapter = normalizeRuntimeAdapter(runtimeAdapterResult.value ?? projectDefaults.runtimeAdapter);
|
|
9615
|
-
await runFastTaskRunPreflight(context, {
|
|
9699
|
+
await withSpinner("Running task-run preflight\u2026", () => runFastTaskRunPreflight(context, {
|
|
9616
9700
|
taskId: selectedTaskId,
|
|
9617
9701
|
runtimeAdapter
|
|
9618
|
-
});
|
|
9702
|
+
}), { outputMode: context.outputMode });
|
|
9619
9703
|
const dirtyBaseline = await resolveDirtyBaselineForTaskRun(context, dirtyBaselineResult.value);
|
|
9620
9704
|
const prMode = noPrResult.value ? "off" : normalizePrMode(prResult.value) ?? projectDefaults.prMode;
|
|
9621
|
-
const submitted = await submitTaskRunViaServer(context, {
|
|
9705
|
+
const submitted = await withSpinner("Dispatching run\u2026", () => submitTaskRunViaServer(context, {
|
|
9622
9706
|
runId: context.runId,
|
|
9623
9707
|
taskId: selectedTaskId ?? undefined,
|
|
9624
9708
|
title: titleResult.value ?? undefined,
|
|
@@ -9629,7 +9713,7 @@ async function executeTask(context, args, options) {
|
|
|
9629
9713
|
initialPrompt: initialPromptResult.value ?? undefined,
|
|
9630
9714
|
baselineMode: dirtyBaseline.mode,
|
|
9631
9715
|
prMode
|
|
9632
|
-
});
|
|
9716
|
+
}), { outputMode: context.outputMode });
|
|
9633
9717
|
let attachDetails = null;
|
|
9634
9718
|
if (!detachResult.value && context.outputMode === "text") {
|
|
9635
9719
|
printFormattedOutput(formatSubmittedRun({
|
|
@@ -11807,7 +11891,7 @@ async function executeSetup(context, args) {
|
|
|
11807
11891
|
case "check":
|
|
11808
11892
|
requireNoExtraArgs(rest, `rig setup ${command}`);
|
|
11809
11893
|
{
|
|
11810
|
-
const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot));
|
|
11894
|
+
const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot, context.outputMode));
|
|
11811
11895
|
return { ok: true, group: "setup", command, details: { checks, failures: countDoctorFailures(checks) } };
|
|
11812
11896
|
}
|
|
11813
11897
|
case "setup":
|
|
@@ -11816,7 +11900,7 @@ async function executeSetup(context, args) {
|
|
|
11816
11900
|
return { ok: true, group: "setup", command };
|
|
11817
11901
|
case "preflight":
|
|
11818
11902
|
requireNoExtraArgs(rest, "rig setup preflight");
|
|
11819
|
-
await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot));
|
|
11903
|
+
await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot, context.outputMode));
|
|
11820
11904
|
return { ok: true, group: "setup", command };
|
|
11821
11905
|
default:
|
|
11822
11906
|
throw new CliError(`Unknown setup command: ${command}`, 1, { hint: "Run `rig setup --help` \u2014 commands are bootstrap|check|preflight." });
|
|
@@ -11837,8 +11921,8 @@ function runSetupInit(projectRoot) {
|
|
|
11837
11921
|
}
|
|
11838
11922
|
console.log("Harness directories ready.");
|
|
11839
11923
|
}
|
|
11840
|
-
async function runSetupCheck(projectRoot) {
|
|
11841
|
-
const doctorChecks = await runRigDoctorChecks({ projectRoot });
|
|
11924
|
+
async function runSetupCheck(projectRoot, outputMode = "text") {
|
|
11925
|
+
const doctorChecks = await withSpinner("Running setup checks\u2026", (update) => runRigDoctorChecks({ projectRoot, onProgress: update }), { outputMode });
|
|
11842
11926
|
console.log(formatDoctorChecks(doctorChecks));
|
|
11843
11927
|
const failures = countDoctorFailures(doctorChecks);
|
|
11844
11928
|
if (failures > 0) {
|
|
@@ -11846,8 +11930,8 @@ async function runSetupCheck(projectRoot) {
|
|
|
11846
11930
|
}
|
|
11847
11931
|
return doctorChecks;
|
|
11848
11932
|
}
|
|
11849
|
-
async function runSetupPreflight(projectRoot) {
|
|
11850
|
-
await runSetupCheck(projectRoot);
|
|
11933
|
+
async function runSetupPreflight(projectRoot, outputMode = "text") {
|
|
11934
|
+
await runSetupCheck(projectRoot, outputMode);
|
|
11851
11935
|
const validationRoot = resolve24(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
|
|
11852
11936
|
if (existsSync16(validationRoot)) {
|
|
11853
11937
|
const validators = readdirSync3(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|