@h-rig/cli 0.0.6-alpha.9 → 0.0.6-alpha.90
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/README.md +1 -1
- package/dist/bin/build-rig-binaries.d.ts +2 -0
- package/dist/bin/rig.d.ts +2 -0
- package/dist/bin/rig.js +11052 -6668
- package/dist/src/app/board.d.ts +23 -0
- package/dist/src/app/board.js +1786 -0
- package/dist/src/app/drone-ui.d.ts +37 -0
- package/dist/src/app/drone-ui.js +294 -0
- package/dist/src/app/theme.d.ts +47 -0
- package/dist/src/app/theme.js +150 -0
- package/dist/src/commands/_async-ui.d.ts +13 -0
- package/dist/src/commands/_async-ui.js +223 -0
- package/dist/src/commands/_authority-runs.d.ts +22 -0
- package/dist/src/commands/_authority-runs.js +2 -3
- package/dist/src/commands/_cli-format.d.ts +49 -0
- package/dist/src/commands/_cli-format.js +447 -0
- package/dist/src/commands/_connection-state.d.ts +44 -0
- package/dist/src/commands/_connection-state.js +30 -11
- package/dist/src/commands/_doctor-checks.d.ts +52 -0
- package/dist/src/commands/_doctor-checks.js +180 -44
- package/dist/src/commands/_help-catalog.d.ts +51 -0
- package/dist/src/commands/_help-catalog.js +442 -0
- package/dist/src/commands/_json-output.d.ts +11 -0
- package/dist/src/commands/_json-output.js +56 -0
- package/dist/src/commands/_operator-surface.d.ts +34 -0
- package/dist/src/commands/_operator-surface.js +220 -0
- package/dist/src/commands/_operator-view.d.ts +30 -0
- package/dist/src/commands/_operator-view.js +803 -73
- package/dist/src/commands/_parsers.d.ts +15 -0
- package/dist/src/commands/_parsers.js +18 -11
- package/dist/src/commands/_paths.d.ts +11 -0
- package/dist/src/commands/_pi-frontend.d.ts +27 -0
- package/dist/src/commands/_pi-frontend.js +742 -0
- package/dist/src/commands/_pi-install.d.ts +42 -0
- package/dist/src/commands/_pi-install.js +5 -4
- package/dist/src/commands/_policy.d.ts +8 -0
- package/dist/src/commands/_policy.js +12 -5
- package/dist/src/commands/_preflight.d.ts +22 -0
- package/dist/src/commands/_preflight.js +190 -128
- package/dist/src/commands/_probes.d.ts +1 -0
- package/dist/src/commands/_run-driver-helpers.d.ts +99 -0
- package/dist/src/commands/_run-driver-helpers.js +75 -22
- package/dist/src/commands/_run-replay.d.ts +24 -0
- package/dist/src/commands/_run-replay.js +142 -0
- package/dist/src/commands/_server-client.d.ts +186 -0
- package/dist/src/commands/_server-client.js +360 -61
- package/dist/src/commands/_snapshot-upload.d.ts +39 -0
- package/dist/src/commands/_snapshot-upload.js +163 -39
- package/dist/src/commands/_spinner.d.ts +25 -0
- package/dist/src/commands/_spinner.js +65 -0
- package/dist/src/commands/_task-picker.d.ts +9 -0
- package/dist/src/commands/_task-picker.js +172 -19
- package/dist/src/commands/agent.d.ts +3 -0
- package/dist/src/commands/agent.js +39 -20
- package/dist/src/commands/browser.d.ts +65 -0
- package/dist/src/commands/browser.js +334 -51
- package/dist/src/commands/connect.d.ts +7 -0
- package/dist/src/commands/connect.js +272 -33
- package/dist/src/commands/dist.d.ts +28 -0
- package/dist/src/commands/dist.js +19 -12
- package/dist/src/commands/doctor.d.ts +3 -0
- package/dist/src/commands/doctor.js +378 -45
- package/dist/src/commands/github.d.ts +3 -0
- package/dist/src/commands/github.js +375 -53
- package/dist/src/commands/inbox.d.ts +30 -0
- package/dist/src/commands/inbox.js +760 -70
- package/dist/src/commands/init.d.ts +74 -0
- package/dist/src/commands/init.js +963 -119
- package/dist/src/commands/inspect.d.ts +3 -0
- package/dist/src/commands/inspect.js +588 -32
- package/dist/src/commands/inspector.d.ts +3 -0
- package/dist/src/commands/inspector.js +20 -13
- package/dist/src/commands/pi.d.ts +3 -0
- package/dist/src/commands/pi.js +177 -0
- package/dist/src/commands/plugin.d.ts +16 -0
- package/dist/src/commands/plugin.js +95 -27
- package/dist/src/commands/profile-and-review.d.ts +4 -0
- package/dist/src/commands/profile-and-review.js +26 -19
- package/dist/src/commands/queue.d.ts +3 -0
- package/dist/src/commands/queue.js +32 -12
- package/dist/src/commands/remote.d.ts +3 -0
- package/dist/src/commands/remote.js +43 -36
- package/dist/src/commands/repo-git-harness.d.ts +5 -0
- package/dist/src/commands/repo-git-harness.js +22 -15
- package/dist/src/commands/run.d.ts +3 -0
- package/dist/src/commands/run.js +1379 -159
- package/dist/src/commands/server.d.ts +7 -0
- package/dist/src/commands/server.js +502 -57
- package/dist/src/commands/setup.d.ts +16 -0
- package/dist/src/commands/setup.js +390 -63
- package/dist/src/commands/stats.d.ts +12 -0
- package/dist/src/commands/stats.js +1051 -0
- package/dist/src/commands/task-report-bug.d.ts +19 -0
- package/dist/src/commands/task-report-bug.js +260 -62
- package/dist/src/commands/task-run-driver.d.ts +132 -0
- package/dist/src/commands/task-run-driver.js +886 -130
- package/dist/src/commands/task.d.ts +14 -0
- package/dist/src/commands/task.js +1644 -314
- package/dist/src/commands/test.d.ts +3 -0
- package/dist/src/commands/test.js +15 -8
- package/dist/src/commands/workspace.d.ts +3 -0
- package/dist/src/commands/workspace.js +18 -11
- package/dist/src/commands.d.ts +29 -0
- package/dist/src/commands.js +10967 -6637
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +10168 -5787
- package/dist/src/launcher.d.ts +61 -0
- package/dist/src/launcher.js +77 -13
- package/dist/src/report-bug.d.ts +44 -0
- package/dist/src/report-bug.js +3 -3
- package/dist/src/runner.d.ts +47 -0
- package/dist/src/runner.js +16 -22
- package/dist/src/withMutedConsole.d.ts +2 -0
- package/package.json +13 -6
|
@@ -4,12 +4,19 @@ import { iterateServerSentEvents } from "@rig/client";
|
|
|
4
4
|
|
|
5
5
|
// packages/cli/src/runner.ts
|
|
6
6
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
7
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
7
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
8
8
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
9
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
10
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
11
9
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
12
|
-
|
|
10
|
+
|
|
11
|
+
class CliError extends RuntimeCliError {
|
|
12
|
+
hint;
|
|
13
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
14
|
+
super(message, exitCode);
|
|
15
|
+
if (options.hint?.trim()) {
|
|
16
|
+
this.hint = options.hint.trim();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
13
20
|
function takeFlag(args, flag) {
|
|
14
21
|
const rest = [];
|
|
15
22
|
let value = false;
|
|
@@ -30,7 +37,7 @@ function takeOption(args, option) {
|
|
|
30
37
|
if (current === option) {
|
|
31
38
|
const next = args[index + 1];
|
|
32
39
|
if (!next || next.startsWith("-")) {
|
|
33
|
-
throw new CliError(`Missing value for ${option}`);
|
|
40
|
+
throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
|
|
34
41
|
}
|
|
35
42
|
value = next;
|
|
36
43
|
index += 1;
|
|
@@ -55,11 +62,11 @@ import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
|
55
62
|
// packages/cli/src/commands/_parsers.ts
|
|
56
63
|
function parseRequiredPositiveInt(value, option) {
|
|
57
64
|
if (!value) {
|
|
58
|
-
throw new
|
|
65
|
+
throw new CliError(`Missing value for ${option}.`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} 10\`.` });
|
|
59
66
|
}
|
|
60
67
|
const parsed = Number.parseInt(value, 10);
|
|
61
68
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
62
|
-
throw new
|
|
69
|
+
throw new CliError(`Invalid ${option} value: ${value}`, 1, { hint: `Pass a positive integer, e.g. \`${option} 10\`.` });
|
|
63
70
|
}
|
|
64
71
|
return parsed;
|
|
65
72
|
}
|
|
@@ -104,11 +111,11 @@ async function executeInspector(context, args) {
|
|
|
104
111
|
pending = secondsResult.rest;
|
|
105
112
|
const pollMsResult = takeOption(pending, "--poll-ms");
|
|
106
113
|
pending = pollMsResult.rest;
|
|
107
|
-
requireNoExtraArgs(pending, "
|
|
114
|
+
requireNoExtraArgs(pending, "rig inspector stream [--once] [--seconds <n>] [--poll-ms <n>]");
|
|
108
115
|
const seconds = secondsResult.value ? parseRequiredPositiveInt(secondsResult.value, "--seconds") : null;
|
|
109
116
|
const pollMs = pollMsResult.value ? parseRequiredPositiveInt(pollMsResult.value, "--poll-ms") : null;
|
|
110
117
|
if (context.outputMode === "json" && !onceResult.value && !seconds) {
|
|
111
|
-
throw new
|
|
118
|
+
throw new CliError("--json inspector stream requires --once or --seconds <n>.", 2, { hint: "Re-run as `rig inspector stream --once --json` or `rig inspector stream --seconds 10 --json`." });
|
|
112
119
|
}
|
|
113
120
|
const connection = await ensureLocalRigServerConnection(context.projectRoot);
|
|
114
121
|
const streamUrl = new URL("/api/inspector/stream", connection.baseUrl);
|
|
@@ -135,7 +142,7 @@ async function executeInspector(context, args) {
|
|
|
135
142
|
signal: controller.signal
|
|
136
143
|
});
|
|
137
144
|
if (!response.ok) {
|
|
138
|
-
throw new
|
|
145
|
+
throw new CliError(`Inspector stream request failed (${response.status}).`, response.status);
|
|
139
146
|
}
|
|
140
147
|
let received = 0;
|
|
141
148
|
let lastEvent = null;
|
|
@@ -198,7 +205,7 @@ async function executeInspector(context, args) {
|
|
|
198
205
|
let pending = rest;
|
|
199
206
|
const scanIdResult = takeOption(pending, "--scan-id");
|
|
200
207
|
pending = scanIdResult.rest;
|
|
201
|
-
requireNoExtraArgs(pending, "
|
|
208
|
+
requireNoExtraArgs(pending, "rig inspector scan-upstream-drift [--scan-id <id>]");
|
|
202
209
|
const connection = await ensureLocalRigServerConnection(context.projectRoot);
|
|
203
210
|
const response = await fetch(new URL("/api/inspector/tools/invoke", connection.baseUrl), {
|
|
204
211
|
method: "POST",
|
|
@@ -212,7 +219,7 @@ async function executeInspector(context, args) {
|
|
|
212
219
|
})
|
|
213
220
|
});
|
|
214
221
|
if (!response.ok) {
|
|
215
|
-
throw new
|
|
222
|
+
throw new CliError(`Upstream drift scan request failed (${response.status}).`, response.status);
|
|
216
223
|
}
|
|
217
224
|
const result = await response.json();
|
|
218
225
|
if (context.outputMode === "text") {
|
|
@@ -248,7 +255,7 @@ async function executeInspector(context, args) {
|
|
|
248
255
|
};
|
|
249
256
|
}
|
|
250
257
|
default:
|
|
251
|
-
throw new
|
|
258
|
+
throw new CliError(`Unknown inspector command: ${command}`, 1, { hint: "Run `rig inspector --help` \u2014 commands are stream|scan-upstream-drift." });
|
|
252
259
|
}
|
|
253
260
|
}
|
|
254
261
|
export {
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli/src/commands/pi.ts
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import { dirname, resolve } from "path";
|
|
6
|
+
|
|
7
|
+
// packages/cli/src/runner.ts
|
|
8
|
+
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
9
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
10
|
+
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
11
|
+
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
12
|
+
|
|
13
|
+
class CliError extends RuntimeCliError {
|
|
14
|
+
hint;
|
|
15
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
16
|
+
super(message, exitCode);
|
|
17
|
+
if (options.hint?.trim()) {
|
|
18
|
+
this.hint = options.hint.trim();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function requireNoExtraArgs(args, usage) {
|
|
23
|
+
if (args.length > 0) {
|
|
24
|
+
throw new CliError(`Unexpected arguments: ${args.join(" ")}
|
|
25
|
+
Usage: ${usage}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// packages/cli/src/commands/pi.ts
|
|
30
|
+
function settingsPath(root) {
|
|
31
|
+
return resolve(root, ".pi", "settings.json");
|
|
32
|
+
}
|
|
33
|
+
function userSettingsPath() {
|
|
34
|
+
return resolve(homedir(), ".pi", "agent", "settings.json");
|
|
35
|
+
}
|
|
36
|
+
function readJson(path, fallback) {
|
|
37
|
+
if (!existsSync(path))
|
|
38
|
+
return fallback;
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
41
|
+
} catch {
|
|
42
|
+
return fallback;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function packageKey(entry) {
|
|
46
|
+
if (typeof entry === "string")
|
|
47
|
+
return entry;
|
|
48
|
+
if (entry && typeof entry === "object" && typeof entry.source === "string") {
|
|
49
|
+
return entry.source;
|
|
50
|
+
}
|
|
51
|
+
return JSON.stringify(entry);
|
|
52
|
+
}
|
|
53
|
+
function writeSettings(path, settings) {
|
|
54
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
55
|
+
writeFileSync(path, `${JSON.stringify(settings, null, 2)}
|
|
56
|
+
`, "utf-8");
|
|
57
|
+
}
|
|
58
|
+
async function searchNpmForPiExtensions(term) {
|
|
59
|
+
const query = encodeURIComponent(term ? `${term} pi extension` : "pi extension");
|
|
60
|
+
const url = `https://registry.npmjs.org/-/v1/search?text=${query}&size=20`;
|
|
61
|
+
const response = await fetch(url);
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
throw new CliError(`npm registry search failed (${response.status}).`, 2, { hint: "Check network access to registry.npmjs.org, then retry `rig pi search <term>`." });
|
|
64
|
+
}
|
|
65
|
+
const payload = await response.json();
|
|
66
|
+
const results = [];
|
|
67
|
+
for (const entry of payload.objects ?? []) {
|
|
68
|
+
const pkg = entry.package;
|
|
69
|
+
if (!pkg?.name)
|
|
70
|
+
continue;
|
|
71
|
+
const keywords = (pkg.keywords ?? []).map((k) => k.toLowerCase());
|
|
72
|
+
const piLike = pkg.name.startsWith("pi-") || pkg.name.includes("-pi") || keywords.includes("pi") || keywords.includes("pi-extension") || (pkg.description ?? "").toLowerCase().includes("pi extension") || (pkg.description ?? "").toLowerCase().includes("pi coding agent");
|
|
73
|
+
if (!piLike)
|
|
74
|
+
continue;
|
|
75
|
+
results.push({ name: pkg.name, version: pkg.version ?? "", description: pkg.description ?? "" });
|
|
76
|
+
}
|
|
77
|
+
return results;
|
|
78
|
+
}
|
|
79
|
+
async function executePi(context, args) {
|
|
80
|
+
const [command = "list", ...rest] = args;
|
|
81
|
+
const projectSettingsPath = settingsPath(context.projectRoot);
|
|
82
|
+
const managedRecordPath = resolve(context.projectRoot, ".rig", "state", "pi-managed-packages.json");
|
|
83
|
+
switch (command) {
|
|
84
|
+
case "list": {
|
|
85
|
+
requireNoExtraArgs(rest, "rig pi list");
|
|
86
|
+
const project = readJson(projectSettingsPath, {});
|
|
87
|
+
const managed = new Set(readJson(managedRecordPath, []));
|
|
88
|
+
const user = readJson(userSettingsPath(), {});
|
|
89
|
+
const projectPackages = (Array.isArray(project.packages) ? project.packages : []).map((entry) => ({
|
|
90
|
+
source: packageKey(entry),
|
|
91
|
+
managedByRigConfig: managed.has(packageKey(entry))
|
|
92
|
+
}));
|
|
93
|
+
const userPackages = (Array.isArray(user.packages) ? user.packages : []).map(packageKey);
|
|
94
|
+
if (context.outputMode === "text") {
|
|
95
|
+
console.log("Project Pi packages (.pi/settings.json):");
|
|
96
|
+
if (projectPackages.length === 0)
|
|
97
|
+
console.log(" (none)");
|
|
98
|
+
for (const pkg of projectPackages) {
|
|
99
|
+
console.log(` ${pkg.source}${pkg.managedByRigConfig ? " [from rig.config runtime.pi.packages]" : ""}`);
|
|
100
|
+
}
|
|
101
|
+
console.log("User Pi packages (~/.pi/agent/settings.json):");
|
|
102
|
+
if (userPackages.length === 0)
|
|
103
|
+
console.log(" (none)");
|
|
104
|
+
for (const pkg of userPackages)
|
|
105
|
+
console.log(` ${pkg}`);
|
|
106
|
+
console.log("Add more: `rig pi add <npm-package>` \xB7 discover: `rig pi search <term>`");
|
|
107
|
+
}
|
|
108
|
+
return { ok: true, group: "pi", command, details: { projectPackages, userPackages } };
|
|
109
|
+
}
|
|
110
|
+
case "add": {
|
|
111
|
+
const [source, ...extra] = rest;
|
|
112
|
+
requireNoExtraArgs(extra, "rig pi add <package-source>");
|
|
113
|
+
if (!source) {
|
|
114
|
+
throw new CliError("Usage: rig pi add <package-source> (npm name, name@version, or git URL)", 2);
|
|
115
|
+
}
|
|
116
|
+
const settings = readJson(projectSettingsPath, {});
|
|
117
|
+
const packages = Array.isArray(settings.packages) ? settings.packages : [];
|
|
118
|
+
if (packages.some((entry) => packageKey(entry) === source)) {
|
|
119
|
+
throw new CliError(`"${source}" is already in ${projectSettingsPath}.`, 2, { hint: "Run `rig pi list` to see installed extensions." });
|
|
120
|
+
}
|
|
121
|
+
writeSettings(projectSettingsPath, { ...settings, packages: [...packages, source] });
|
|
122
|
+
if (context.outputMode === "text") {
|
|
123
|
+
console.log(`Added ${source} to ${projectSettingsPath}.`);
|
|
124
|
+
console.log("Pi installs missing packages automatically at the next session start (local and worker).");
|
|
125
|
+
}
|
|
126
|
+
return { ok: true, group: "pi", command, details: { source, settingsPath: projectSettingsPath } };
|
|
127
|
+
}
|
|
128
|
+
case "remove": {
|
|
129
|
+
const [source, ...extra] = rest;
|
|
130
|
+
requireNoExtraArgs(extra, "rig pi remove <package-source>");
|
|
131
|
+
if (!source) {
|
|
132
|
+
throw new CliError("Usage: rig pi remove <package-source>", 2);
|
|
133
|
+
}
|
|
134
|
+
const managed = new Set(readJson(managedRecordPath, []));
|
|
135
|
+
if (managed.has(source)) {
|
|
136
|
+
throw new CliError(`"${source}" is managed by rig.config.ts (runtime.pi.packages); remove it there instead.`, 2);
|
|
137
|
+
}
|
|
138
|
+
const settings = readJson(projectSettingsPath, {});
|
|
139
|
+
const packages = Array.isArray(settings.packages) ? settings.packages : [];
|
|
140
|
+
const next = packages.filter((entry) => packageKey(entry) !== source);
|
|
141
|
+
if (next.length === packages.length) {
|
|
142
|
+
throw new CliError(`"${source}" is not in ${projectSettingsPath}.`, 2, { hint: "Run `rig pi list` to see what is installed, then `rig pi remove <source>` with an exact entry." });
|
|
143
|
+
}
|
|
144
|
+
const nextSettings = { ...settings };
|
|
145
|
+
if (next.length > 0)
|
|
146
|
+
nextSettings.packages = next;
|
|
147
|
+
else
|
|
148
|
+
delete nextSettings.packages;
|
|
149
|
+
writeSettings(projectSettingsPath, nextSettings);
|
|
150
|
+
if (context.outputMode === "text") {
|
|
151
|
+
console.log(`Removed ${source} from ${projectSettingsPath}.`);
|
|
152
|
+
}
|
|
153
|
+
return { ok: true, group: "pi", command, details: { source } };
|
|
154
|
+
}
|
|
155
|
+
case "search": {
|
|
156
|
+
const term = rest.join(" ").trim();
|
|
157
|
+
const results = await searchNpmForPiExtensions(term);
|
|
158
|
+
if (context.outputMode === "text") {
|
|
159
|
+
if (results.length === 0) {
|
|
160
|
+
console.log(`No Pi extension packages found on npm${term ? ` for "${term}"` : ""}.`);
|
|
161
|
+
} else {
|
|
162
|
+
console.log(`Pi extension packages on npm${term ? ` matching "${term}"` : ""}:`);
|
|
163
|
+
for (const pkg of results) {
|
|
164
|
+
console.log(` ${pkg.name}@${pkg.version} ${pkg.description.slice(0, 80)}`);
|
|
165
|
+
}
|
|
166
|
+
console.log("Install one: `rig pi add <name>`");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { ok: true, group: "pi", command, details: { term, results } };
|
|
170
|
+
}
|
|
171
|
+
default:
|
|
172
|
+
throw new CliError(`Unknown pi command: ${command}. Use list|add|remove|search.`, 1, { hint: "Run `rig pi --help` for usage." });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export {
|
|
176
|
+
executePi
|
|
177
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type RunnerContext } from "../runner";
|
|
2
|
+
import type { CommandOutcome } from "@rig/runtime/control-plane/runtime/types";
|
|
3
|
+
export declare function executePlugin(context: RunnerContext, args: string[]): Promise<CommandOutcome>;
|
|
4
|
+
/**
|
|
5
|
+
* Resolve a plugin CLI command by exact id ("my-plugin:deploy") or by the
|
|
6
|
+
* unambiguous local part after the namespace colon ("deploy").
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolvePluginCliCommand(commands: readonly {
|
|
9
|
+
id: string;
|
|
10
|
+
command: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
}[], requested: string): {
|
|
13
|
+
id: string;
|
|
14
|
+
command: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
} | undefined;
|
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
var __require = import.meta.require;
|
|
3
3
|
|
|
4
|
+
// packages/cli/src/commands/plugin.ts
|
|
5
|
+
import { existsSync } from "fs";
|
|
6
|
+
import { resolve } from "path";
|
|
7
|
+
|
|
4
8
|
// packages/cli/src/runner.ts
|
|
5
9
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
6
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
10
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
7
11
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
8
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
9
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
10
12
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
11
|
-
|
|
13
|
+
|
|
14
|
+
class CliError extends RuntimeCliError {
|
|
15
|
+
hint;
|
|
16
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
17
|
+
super(message, exitCode);
|
|
18
|
+
if (options.hint?.trim()) {
|
|
19
|
+
this.hint = options.hint.trim();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
12
23
|
function takeOption(args, option) {
|
|
13
24
|
const rest = [];
|
|
14
25
|
let value;
|
|
@@ -17,7 +28,7 @@ function takeOption(args, option) {
|
|
|
17
28
|
if (current === option) {
|
|
18
29
|
const next = args[index + 1];
|
|
19
30
|
if (!next || next.startsWith("-")) {
|
|
20
|
-
throw new CliError(`Missing value for ${option}`);
|
|
31
|
+
throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
|
|
21
32
|
}
|
|
22
33
|
value = next;
|
|
23
34
|
index += 1;
|
|
@@ -43,6 +54,9 @@ Usage: ${usage}`);
|
|
|
43
54
|
return taskId;
|
|
44
55
|
}
|
|
45
56
|
|
|
57
|
+
// packages/cli/src/commands/plugin.ts
|
|
58
|
+
import { buildPluginHostContext } from "@rig/runtime/control-plane/plugin-host-context";
|
|
59
|
+
|
|
46
60
|
// packages/cli/src/commands/_parsers.ts
|
|
47
61
|
async function loadRigConfigOrNull(projectRoot) {
|
|
48
62
|
try {
|
|
@@ -58,8 +72,7 @@ async function executePlugin(context, args) {
|
|
|
58
72
|
const [command = "list", ...rest] = args;
|
|
59
73
|
switch (command) {
|
|
60
74
|
case "list": {
|
|
61
|
-
requireNoExtraArgs(rest, "
|
|
62
|
-
const legacyPlugins = context.plugins.list();
|
|
75
|
+
requireNoExtraArgs(rest, "rig plugin list");
|
|
63
76
|
const declarative = [];
|
|
64
77
|
const config = await loadRigConfigOrNull(context.projectRoot);
|
|
65
78
|
if (config && Array.isArray(config.plugins)) {
|
|
@@ -75,16 +88,16 @@ async function executePlugin(context, args) {
|
|
|
75
88
|
taskSources: (c.taskSources ?? []).map((s) => s.id),
|
|
76
89
|
skills: (c.skills ?? []).map((s) => s.id),
|
|
77
90
|
taskFieldExtensions: (c.taskFieldSchemas ?? []).map((f) => f.id),
|
|
78
|
-
cliCommands: (c.cliCommands ?? []).map((
|
|
91
|
+
cliCommands: (c.cliCommands ?? []).map((entry) => entry.id)
|
|
79
92
|
});
|
|
80
93
|
}
|
|
81
94
|
}
|
|
82
95
|
if (context.outputMode === "text") {
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
console.log("
|
|
96
|
+
if (declarative.length === 0) {
|
|
97
|
+
const configExists = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync(resolve(context.projectRoot, name)));
|
|
98
|
+
console.log(configExists ? "rig.config found but no plugins could be loaded. If the config imports @rig/* packages, run `bun install` in this project first." : "No plugins loaded. Declare plugins in rig.config.ts.");
|
|
99
|
+
} else {
|
|
100
|
+
console.log("Plugins (rig.config.ts):");
|
|
88
101
|
for (const p of declarative) {
|
|
89
102
|
console.log(` ${p.name}@${p.version}`);
|
|
90
103
|
const lines = [];
|
|
@@ -103,31 +116,45 @@ async function executePlugin(context, args) {
|
|
|
103
116
|
if (p.taskFieldExtensions.length)
|
|
104
117
|
lines.push(` task-fields: ${p.taskFieldExtensions.join(", ")}`);
|
|
105
118
|
if (p.cliCommands.length)
|
|
106
|
-
lines.push(` cli-commands: ${p.cliCommands.join(", ")}`);
|
|
119
|
+
lines.push(` cli-commands: ${p.cliCommands.join(", ")} (run with \`rig plugin run <id>\`)`);
|
|
107
120
|
for (const line of lines)
|
|
108
121
|
console.log(line);
|
|
109
122
|
}
|
|
110
123
|
}
|
|
111
|
-
if (legacyPlugins.length > 0) {
|
|
112
|
-
console.log("Legacy disk-scan plugins (rig/plugins/):");
|
|
113
|
-
for (const plugin of legacyPlugins) {
|
|
114
|
-
const validators = plugin.validators.length > 0 ? plugin.validators.join(", ") : "none";
|
|
115
|
-
console.log(` ${plugin.name} (validators: ${validators})`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
124
|
}
|
|
119
125
|
return {
|
|
120
126
|
ok: true,
|
|
121
127
|
group: "plugin",
|
|
122
128
|
command,
|
|
123
|
-
details: { declarative
|
|
129
|
+
details: { declarative }
|
|
124
130
|
};
|
|
125
131
|
}
|
|
126
132
|
case "validate": {
|
|
127
133
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
128
|
-
requireNoExtraArgs(remaining, "
|
|
129
|
-
const taskId = requireTask(task, "
|
|
130
|
-
const
|
|
134
|
+
requireNoExtraArgs(remaining, "rig plugin validate --task <task-id>");
|
|
135
|
+
const taskId = requireTask(task, "rig plugin validate --task <task-id>");
|
|
136
|
+
const hostCtx = await buildPluginHostContext(context.projectRoot);
|
|
137
|
+
if (!hostCtx) {
|
|
138
|
+
throw new CliError(`No rig.config found at ${context.projectRoot}. Run \`rig init\` to set up plugins.`, 2);
|
|
139
|
+
}
|
|
140
|
+
const validators = hostCtx.validatorRegistry.list();
|
|
141
|
+
const results = [];
|
|
142
|
+
for (const validator of validators) {
|
|
143
|
+
try {
|
|
144
|
+
results.push(await validator.run({
|
|
145
|
+
taskId,
|
|
146
|
+
workspaceRoot: context.projectRoot,
|
|
147
|
+
scope: []
|
|
148
|
+
}));
|
|
149
|
+
} catch (error) {
|
|
150
|
+
results.push({
|
|
151
|
+
id: validator.id,
|
|
152
|
+
passed: false,
|
|
153
|
+
summary: `${validator.id} failed unexpectedly`,
|
|
154
|
+
details: `${error}`
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
131
158
|
const passed = results.filter((result) => result.passed).length;
|
|
132
159
|
const failed = results.length - passed;
|
|
133
160
|
if (context.outputMode === "text") {
|
|
@@ -144,7 +171,7 @@ async function executePlugin(context, args) {
|
|
|
144
171
|
}
|
|
145
172
|
}
|
|
146
173
|
if (failed > 0) {
|
|
147
|
-
throw new
|
|
174
|
+
throw new CliError(`Plugin validation failed for ${failed} validator(s).`, 2, { hint: "Fix the reported validators, then re-run `rig plugin validate --task <id>`." });
|
|
148
175
|
}
|
|
149
176
|
return {
|
|
150
177
|
ok: true,
|
|
@@ -158,10 +185,51 @@ async function executePlugin(context, args) {
|
|
|
158
185
|
}
|
|
159
186
|
};
|
|
160
187
|
}
|
|
188
|
+
case "run": {
|
|
189
|
+
const [commandId, ...commandArgs] = rest;
|
|
190
|
+
if (!commandId) {
|
|
191
|
+
throw new CliError("Usage: rig plugin run <command-id> [args...]", 1, { hint: "Run `rig plugin list` to see plugin-contributed command ids." });
|
|
192
|
+
}
|
|
193
|
+
const hostCtx = await buildPluginHostContext(context.projectRoot);
|
|
194
|
+
if (!hostCtx) {
|
|
195
|
+
throw new CliError(`No rig.config found at ${context.projectRoot}. Run \`rig init\` to set up plugins.`, 2);
|
|
196
|
+
}
|
|
197
|
+
const registration = resolvePluginCliCommand(hostCtx.pluginHost.listCliCommands(), commandId);
|
|
198
|
+
if (!registration) {
|
|
199
|
+
const available = hostCtx.pluginHost.listCliCommands().map((entry) => entry.id);
|
|
200
|
+
throw new CliError(available.length > 0 ? `Unknown plugin command "${commandId}". Available: ${available.join(", ")}` : `No plugin CLI commands are registered. Plugins contribute them via contributes.cliCommands.`, 2);
|
|
201
|
+
}
|
|
202
|
+
if (context.dryRun) {
|
|
203
|
+
if (context.outputMode === "text") {
|
|
204
|
+
console.log(`[dry-run] ${registration.command}${commandArgs.length ? ` ${commandArgs.join(" ")}` : ""}`);
|
|
205
|
+
}
|
|
206
|
+
return { ok: true, group: "plugin", command, details: { id: registration.id, dryRun: true } };
|
|
207
|
+
}
|
|
208
|
+
const proc = Bun.spawn(["bash", "-c", `${registration.command} "$@"`, registration.id, ...commandArgs], {
|
|
209
|
+
cwd: context.projectRoot,
|
|
210
|
+
env: process.env,
|
|
211
|
+
stdin: "inherit",
|
|
212
|
+
stdout: "inherit",
|
|
213
|
+
stderr: "inherit"
|
|
214
|
+
});
|
|
215
|
+
const exitCode = await proc.exited;
|
|
216
|
+
if (exitCode !== 0) {
|
|
217
|
+
throw new CliError(`Plugin command "${registration.id}" exited with code ${exitCode}.`, exitCode, { hint: "Run `rig plugin list` to inspect the contributing plugin, or re-run with --json for details." });
|
|
218
|
+
}
|
|
219
|
+
return { ok: true, group: "plugin", command, details: { id: registration.id, exitCode } };
|
|
220
|
+
}
|
|
161
221
|
default:
|
|
162
|
-
throw new
|
|
222
|
+
throw new CliError(`Unknown plugin command: ${command}`, 1, { hint: "Run `rig plugin --help` \u2014 commands are list|validate|run." });
|
|
163
223
|
}
|
|
164
224
|
}
|
|
225
|
+
function resolvePluginCliCommand(commands, requested) {
|
|
226
|
+
const exact = commands.find((entry) => entry.id === requested);
|
|
227
|
+
if (exact)
|
|
228
|
+
return exact;
|
|
229
|
+
const byLocalPart = commands.filter((entry) => entry.id.split(":").slice(1).join(":") === requested);
|
|
230
|
+
return byLocalPart.length === 1 ? byLocalPart[0] : undefined;
|
|
231
|
+
}
|
|
165
232
|
export {
|
|
233
|
+
resolvePluginCliCommand,
|
|
166
234
|
executePlugin
|
|
167
235
|
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type RunnerContext } from "../runner";
|
|
2
|
+
import type { CommandOutcome } from "@rig/runtime/control-plane/runtime/types";
|
|
3
|
+
export declare function executeProfile(context: RunnerContext, args: string[]): Promise<CommandOutcome>;
|
|
4
|
+
export declare function executeReview(context: RunnerContext, args: string[]): Promise<CommandOutcome>;
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/runner.ts
|
|
3
3
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
4
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
4
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
5
5
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
6
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
7
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
8
6
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
9
|
-
|
|
7
|
+
|
|
8
|
+
class CliError extends RuntimeCliError {
|
|
9
|
+
hint;
|
|
10
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
11
|
+
super(message, exitCode);
|
|
12
|
+
if (options.hint?.trim()) {
|
|
13
|
+
this.hint = options.hint.trim();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
10
17
|
function takeOption(args, option) {
|
|
11
18
|
const rest = [];
|
|
12
19
|
let value;
|
|
@@ -15,7 +22,7 @@ function takeOption(args, option) {
|
|
|
15
22
|
if (current === option) {
|
|
16
23
|
const next = args[index + 1];
|
|
17
24
|
if (!next || next.startsWith("-")) {
|
|
18
|
-
throw new CliError(`Missing value for ${option}`);
|
|
25
|
+
throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
|
|
19
26
|
}
|
|
20
27
|
value = next;
|
|
21
28
|
index += 1;
|
|
@@ -86,22 +93,22 @@ async function executeProfile(context, args) {
|
|
|
86
93
|
const [command = "show", ...rest] = args;
|
|
87
94
|
switch (command) {
|
|
88
95
|
case "show":
|
|
89
|
-
requireNoExtraArgs(rest, "
|
|
96
|
+
requireNoExtraArgs(rest, "rig profile show");
|
|
90
97
|
await withMutedConsole(context.outputMode === "json", () => showProfile(context.projectRoot));
|
|
91
98
|
return { ok: true, group: "profile", command };
|
|
92
99
|
case "set": {
|
|
93
100
|
if (rest.length === 0) {
|
|
94
|
-
throw new
|
|
101
|
+
throw new CliError("Usage: rig profile set <claude-code|codex-cli|pi> or set [--model ...] [--runtime ...] [--plugin ...]");
|
|
95
102
|
}
|
|
96
103
|
const preset = rest[0];
|
|
97
104
|
if (preset && !preset.startsWith("-")) {
|
|
98
105
|
if (rest.length !== 1) {
|
|
99
|
-
throw new
|
|
106
|
+
throw new CliError("Usage: rig profile set <claude-code|codex-cli|pi>");
|
|
100
107
|
}
|
|
101
108
|
try {
|
|
102
109
|
await withMutedConsole(context.outputMode === "json", () => setProfile(context.projectRoot, { preset }));
|
|
103
110
|
} catch (error) {
|
|
104
|
-
throw new
|
|
111
|
+
throw new CliError(error instanceof Error ? error.message : String(error), 2);
|
|
105
112
|
}
|
|
106
113
|
return { ok: true, group: "profile", command, details: { preset } };
|
|
107
114
|
}
|
|
@@ -112,9 +119,9 @@ async function executeProfile(context, args) {
|
|
|
112
119
|
pending = runtimeResult.rest;
|
|
113
120
|
const pluginResult = takeOption(pending, "--plugin");
|
|
114
121
|
pending = pluginResult.rest;
|
|
115
|
-
requireNoExtraArgs(pending, "
|
|
122
|
+
requireNoExtraArgs(pending, "rig profile set [--model ...] [--runtime ...] [--plugin ...]");
|
|
116
123
|
if (!modelResult.value && !runtimeResult.value && !pluginResult.value) {
|
|
117
|
-
throw new
|
|
124
|
+
throw new CliError("Provide at least one of --model, --runtime, or --plugin.", 1, { hint: "Example: `rig profile set --model claude-sonnet`. See current values with `rig profile show`." });
|
|
118
125
|
}
|
|
119
126
|
try {
|
|
120
127
|
await withMutedConsole(context.outputMode === "json", () => setProfile(context.projectRoot, {
|
|
@@ -123,7 +130,7 @@ async function executeProfile(context, args) {
|
|
|
123
130
|
plugin: pluginResult.value
|
|
124
131
|
}));
|
|
125
132
|
} catch (error) {
|
|
126
|
-
throw new
|
|
133
|
+
throw new CliError(error instanceof Error ? error.message : String(error), 2);
|
|
127
134
|
}
|
|
128
135
|
return {
|
|
129
136
|
ok: true,
|
|
@@ -137,39 +144,39 @@ async function executeProfile(context, args) {
|
|
|
137
144
|
};
|
|
138
145
|
}
|
|
139
146
|
default:
|
|
140
|
-
throw new
|
|
147
|
+
throw new CliError(`Unknown profile command: ${command}`, 1, { hint: "Run `rig profile --help` \u2014 commands are show|set." });
|
|
141
148
|
}
|
|
142
149
|
}
|
|
143
150
|
async function executeReview(context, args) {
|
|
144
151
|
const [command = "show", ...rest] = args;
|
|
145
152
|
switch (command) {
|
|
146
153
|
case "show":
|
|
147
|
-
requireNoExtraArgs(rest, "
|
|
154
|
+
requireNoExtraArgs(rest, "rig review show");
|
|
148
155
|
await withMutedConsole(context.outputMode === "json", () => showReviewProfile(context.projectRoot));
|
|
149
156
|
return { ok: true, group: "review", command };
|
|
150
157
|
case "set": {
|
|
151
158
|
if (rest.length === 0) {
|
|
152
|
-
throw new
|
|
159
|
+
throw new CliError("Usage: rig review set <off|advisory|required> [--provider greptile]");
|
|
153
160
|
}
|
|
154
161
|
const mode = rest[0];
|
|
155
162
|
if (!mode) {
|
|
156
|
-
throw new
|
|
163
|
+
throw new CliError("Usage: rig review set <off|advisory|required> [--provider greptile]");
|
|
157
164
|
}
|
|
158
165
|
let pending = rest.slice(1);
|
|
159
166
|
const providerResult = takeOption(pending, "--provider");
|
|
160
167
|
pending = providerResult.rest;
|
|
161
|
-
requireNoExtraArgs(pending, "
|
|
168
|
+
requireNoExtraArgs(pending, "rig review set <off|advisory|required> [--provider greptile]");
|
|
162
169
|
try {
|
|
163
170
|
await withMutedConsole(context.outputMode === "json", () => {
|
|
164
171
|
return setReviewProfile(context.projectRoot, mode, providerResult.value);
|
|
165
172
|
});
|
|
166
173
|
} catch (error) {
|
|
167
|
-
throw new
|
|
174
|
+
throw new CliError(error instanceof Error ? error.message : String(error), 2);
|
|
168
175
|
}
|
|
169
176
|
return { ok: true, group: "review", command, details: { mode, provider: providerResult.value || null } };
|
|
170
177
|
}
|
|
171
178
|
default:
|
|
172
|
-
throw new
|
|
179
|
+
throw new CliError(`Unknown review command: ${command}`, 1, { hint: "Run `rig review --help` \u2014 commands are show|set." });
|
|
173
180
|
}
|
|
174
181
|
}
|
|
175
182
|
export {
|