@h-rig/cli 0.0.6-alpha.33 → 0.0.6-alpha.35
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 +442 -231
- package/dist/src/commands/_help-catalog.js +14 -0
- package/dist/src/commands/_operator-surface.js +16 -0
- package/dist/src/commands/_operator-view.js +16 -0
- package/dist/src/commands/pi.js +168 -0
- package/dist/src/commands/run.js +43 -0
- package/dist/src/commands/task.js +30 -0
- package/dist/src/commands.js +434 -223
- package/dist/src/index.js +442 -231
- package/package.json +6 -6
|
@@ -112,6 +112,7 @@ var PRIMARY_GROUPS = [
|
|
|
112
112
|
{ command: "show <id>|--run <id> [--raw]", description: "Show a human run summary; --raw prints the full payload.", primary: true },
|
|
113
113
|
{ command: "attach <run-id>|--run <id> [--follow]", description: "Attach to the run; --follow launches native bundled Pi for live Pi runs.", primary: true },
|
|
114
114
|
{ command: "stop [<run-id>|--run <id>]", description: "Request stop for one run or local active runs.", primary: true },
|
|
115
|
+
{ command: "steer <run-id> --message <text>", description: "Queue a steering message into a live worker without attaching." },
|
|
115
116
|
{ command: "timeline --run <id> [--follow]", description: "Stream raw run timeline events." },
|
|
116
117
|
{ command: "resume", description: "Resume the most recent interrupted local run." },
|
|
117
118
|
{ command: "restart", description: "Restart the most recent local run from a clean runtime." },
|
|
@@ -213,6 +214,19 @@ var ADVANCED_GROUPS = [
|
|
|
213
214
|
{ command: "hp-next <dev|check|e2e|reset>", description: "Drive the hp-next browser test harness." }
|
|
214
215
|
]
|
|
215
216
|
},
|
|
217
|
+
{
|
|
218
|
+
name: "pi",
|
|
219
|
+
summary: "Manage Pi extension packages for this project (community extensions from npm/git).",
|
|
220
|
+
usage: ["rig pi <list|add|remove|search> [args]"],
|
|
221
|
+
commands: [
|
|
222
|
+
{ command: "list", description: "Show project and user Pi extension packages." },
|
|
223
|
+
{ command: "add <source>", description: "Add an npm/git Pi extension to .pi/settings.json (auto-installs at next session)." },
|
|
224
|
+
{ command: "remove <source>", description: "Remove an operator-added Pi extension." },
|
|
225
|
+
{ command: "search [term]", description: "Discover Pi extension packages on the npm registry." }
|
|
226
|
+
],
|
|
227
|
+
examples: ["rig pi search subagents", "rig pi add pi-subagents", "rig pi list"],
|
|
228
|
+
next: ["Config-managed extensions: declare `runtime: { pi: { packages: [...] } }` in rig.config.ts \u2014 workers pick them up automatically."]
|
|
229
|
+
},
|
|
216
230
|
{
|
|
217
231
|
name: "plugin",
|
|
218
232
|
summary: "Plugin listing, validation, and plugin-contributed commands.",
|
|
@@ -124,7 +124,23 @@ function createPiRunStreamRenderer(output = process.stdout) {
|
|
|
124
124
|
}
|
|
125
125
|
if (entry.type === "timeline_warning") {
|
|
126
126
|
writeLine(`[Rig timeline] ${String(entry.detail ?? entry.message ?? "timeline unavailable")}`);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (entry.type === "action") {
|
|
130
|
+
const text = String(entry.detail ?? entry.message ?? entry.title ?? "").trim();
|
|
131
|
+
if (text)
|
|
132
|
+
writeLine(`[Rig action] ${text}`);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (entry.type === "user_message") {
|
|
136
|
+
const text = String(entry.text ?? entry.message ?? entry.detail ?? "").trim();
|
|
137
|
+
if (text)
|
|
138
|
+
writeLine(`[Operator] ${text}`);
|
|
139
|
+
continue;
|
|
127
140
|
}
|
|
141
|
+
const fallback = String(entry.detail ?? entry.message ?? entry.text ?? entry.title ?? "").trim();
|
|
142
|
+
if (fallback)
|
|
143
|
+
writeLine(`[${String(entry.type ?? "timeline")}] ${fallback}`);
|
|
128
144
|
}
|
|
129
145
|
},
|
|
130
146
|
renderLogs(entries) {
|
|
@@ -410,7 +410,23 @@ function createPiRunStreamRenderer(output = process.stdout) {
|
|
|
410
410
|
}
|
|
411
411
|
if (entry.type === "timeline_warning") {
|
|
412
412
|
writeLine(`[Rig timeline] ${String(entry.detail ?? entry.message ?? "timeline unavailable")}`);
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
if (entry.type === "action") {
|
|
416
|
+
const text = String(entry.detail ?? entry.message ?? entry.title ?? "").trim();
|
|
417
|
+
if (text)
|
|
418
|
+
writeLine(`[Rig action] ${text}`);
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
if (entry.type === "user_message") {
|
|
422
|
+
const text = String(entry.text ?? entry.message ?? entry.detail ?? "").trim();
|
|
423
|
+
if (text)
|
|
424
|
+
writeLine(`[Operator] ${text}`);
|
|
425
|
+
continue;
|
|
413
426
|
}
|
|
427
|
+
const fallback = String(entry.detail ?? entry.message ?? entry.text ?? entry.title ?? "").trim();
|
|
428
|
+
if (fallback)
|
|
429
|
+
writeLine(`[${String(entry.type ?? "timeline")}] ${fallback}`);
|
|
414
430
|
}
|
|
415
431
|
},
|
|
416
432
|
renderLogs(entries) {
|
|
@@ -0,0 +1,168 @@
|
|
|
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 } 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
|
+
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
13
|
+
function requireNoExtraArgs(args, usage) {
|
|
14
|
+
if (args.length > 0) {
|
|
15
|
+
throw new CliError(`Unexpected arguments: ${args.join(" ")}
|
|
16
|
+
Usage: ${usage}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// packages/cli/src/commands/pi.ts
|
|
21
|
+
function settingsPath(root) {
|
|
22
|
+
return resolve(root, ".pi", "settings.json");
|
|
23
|
+
}
|
|
24
|
+
function userSettingsPath() {
|
|
25
|
+
return resolve(homedir(), ".pi", "agent", "settings.json");
|
|
26
|
+
}
|
|
27
|
+
function readJson(path, fallback) {
|
|
28
|
+
if (!existsSync(path))
|
|
29
|
+
return fallback;
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
32
|
+
} catch {
|
|
33
|
+
return fallback;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function packageKey(entry) {
|
|
37
|
+
if (typeof entry === "string")
|
|
38
|
+
return entry;
|
|
39
|
+
if (entry && typeof entry === "object" && typeof entry.source === "string") {
|
|
40
|
+
return entry.source;
|
|
41
|
+
}
|
|
42
|
+
return JSON.stringify(entry);
|
|
43
|
+
}
|
|
44
|
+
function writeSettings(path, settings) {
|
|
45
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
46
|
+
writeFileSync(path, `${JSON.stringify(settings, null, 2)}
|
|
47
|
+
`, "utf-8");
|
|
48
|
+
}
|
|
49
|
+
async function searchNpmForPiExtensions(term) {
|
|
50
|
+
const query = encodeURIComponent(term ? `${term} pi extension` : "pi extension");
|
|
51
|
+
const url = `https://registry.npmjs.org/-/v1/search?text=${query}&size=20`;
|
|
52
|
+
const response = await fetch(url);
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
throw new CliError2(`npm registry search failed (${response.status}).`, 2);
|
|
55
|
+
}
|
|
56
|
+
const payload = await response.json();
|
|
57
|
+
const results = [];
|
|
58
|
+
for (const entry of payload.objects ?? []) {
|
|
59
|
+
const pkg = entry.package;
|
|
60
|
+
if (!pkg?.name)
|
|
61
|
+
continue;
|
|
62
|
+
const keywords = (pkg.keywords ?? []).map((k) => k.toLowerCase());
|
|
63
|
+
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");
|
|
64
|
+
if (!piLike)
|
|
65
|
+
continue;
|
|
66
|
+
results.push({ name: pkg.name, version: pkg.version ?? "", description: pkg.description ?? "" });
|
|
67
|
+
}
|
|
68
|
+
return results;
|
|
69
|
+
}
|
|
70
|
+
async function executePi(context, args) {
|
|
71
|
+
const [command = "list", ...rest] = args;
|
|
72
|
+
const projectSettingsPath = settingsPath(context.projectRoot);
|
|
73
|
+
const managedRecordPath = resolve(context.projectRoot, ".rig", "state", "pi-managed-packages.json");
|
|
74
|
+
switch (command) {
|
|
75
|
+
case "list": {
|
|
76
|
+
requireNoExtraArgs(rest, "rig pi list");
|
|
77
|
+
const project = readJson(projectSettingsPath, {});
|
|
78
|
+
const managed = new Set(readJson(managedRecordPath, []));
|
|
79
|
+
const user = readJson(userSettingsPath(), {});
|
|
80
|
+
const projectPackages = (Array.isArray(project.packages) ? project.packages : []).map((entry) => ({
|
|
81
|
+
source: packageKey(entry),
|
|
82
|
+
managedByRigConfig: managed.has(packageKey(entry))
|
|
83
|
+
}));
|
|
84
|
+
const userPackages = (Array.isArray(user.packages) ? user.packages : []).map(packageKey);
|
|
85
|
+
if (context.outputMode === "text") {
|
|
86
|
+
console.log("Project Pi packages (.pi/settings.json):");
|
|
87
|
+
if (projectPackages.length === 0)
|
|
88
|
+
console.log(" (none)");
|
|
89
|
+
for (const pkg of projectPackages) {
|
|
90
|
+
console.log(` ${pkg.source}${pkg.managedByRigConfig ? " [from rig.config runtime.pi.packages]" : ""}`);
|
|
91
|
+
}
|
|
92
|
+
console.log("User Pi packages (~/.pi/agent/settings.json):");
|
|
93
|
+
if (userPackages.length === 0)
|
|
94
|
+
console.log(" (none)");
|
|
95
|
+
for (const pkg of userPackages)
|
|
96
|
+
console.log(` ${pkg}`);
|
|
97
|
+
console.log("Add more: `rig pi add <npm-package>` \xB7 discover: `rig pi search <term>`");
|
|
98
|
+
}
|
|
99
|
+
return { ok: true, group: "pi", command, details: { projectPackages, userPackages } };
|
|
100
|
+
}
|
|
101
|
+
case "add": {
|
|
102
|
+
const [source, ...extra] = rest;
|
|
103
|
+
requireNoExtraArgs(extra, "rig pi add <package-source>");
|
|
104
|
+
if (!source) {
|
|
105
|
+
throw new CliError2("Usage: rig pi add <package-source> (npm name, name@version, or git URL)", 2);
|
|
106
|
+
}
|
|
107
|
+
const settings = readJson(projectSettingsPath, {});
|
|
108
|
+
const packages = Array.isArray(settings.packages) ? settings.packages : [];
|
|
109
|
+
if (packages.some((entry) => packageKey(entry) === source)) {
|
|
110
|
+
throw new CliError2(`"${source}" is already in ${projectSettingsPath}.`, 2);
|
|
111
|
+
}
|
|
112
|
+
writeSettings(projectSettingsPath, { ...settings, packages: [...packages, source] });
|
|
113
|
+
if (context.outputMode === "text") {
|
|
114
|
+
console.log(`Added ${source} to ${projectSettingsPath}.`);
|
|
115
|
+
console.log("Pi installs missing packages automatically at the next session start (local and worker).");
|
|
116
|
+
}
|
|
117
|
+
return { ok: true, group: "pi", command, details: { source, settingsPath: projectSettingsPath } };
|
|
118
|
+
}
|
|
119
|
+
case "remove": {
|
|
120
|
+
const [source, ...extra] = rest;
|
|
121
|
+
requireNoExtraArgs(extra, "rig pi remove <package-source>");
|
|
122
|
+
if (!source) {
|
|
123
|
+
throw new CliError2("Usage: rig pi remove <package-source>", 2);
|
|
124
|
+
}
|
|
125
|
+
const managed = new Set(readJson(managedRecordPath, []));
|
|
126
|
+
if (managed.has(source)) {
|
|
127
|
+
throw new CliError2(`"${source}" is managed by rig.config.ts (runtime.pi.packages); remove it there instead.`, 2);
|
|
128
|
+
}
|
|
129
|
+
const settings = readJson(projectSettingsPath, {});
|
|
130
|
+
const packages = Array.isArray(settings.packages) ? settings.packages : [];
|
|
131
|
+
const next = packages.filter((entry) => packageKey(entry) !== source);
|
|
132
|
+
if (next.length === packages.length) {
|
|
133
|
+
throw new CliError2(`"${source}" is not in ${projectSettingsPath}.`, 2);
|
|
134
|
+
}
|
|
135
|
+
const nextSettings = { ...settings };
|
|
136
|
+
if (next.length > 0)
|
|
137
|
+
nextSettings.packages = next;
|
|
138
|
+
else
|
|
139
|
+
delete nextSettings.packages;
|
|
140
|
+
writeSettings(projectSettingsPath, nextSettings);
|
|
141
|
+
if (context.outputMode === "text") {
|
|
142
|
+
console.log(`Removed ${source} from ${projectSettingsPath}.`);
|
|
143
|
+
}
|
|
144
|
+
return { ok: true, group: "pi", command, details: { source } };
|
|
145
|
+
}
|
|
146
|
+
case "search": {
|
|
147
|
+
const term = rest.join(" ").trim();
|
|
148
|
+
const results = await searchNpmForPiExtensions(term);
|
|
149
|
+
if (context.outputMode === "text") {
|
|
150
|
+
if (results.length === 0) {
|
|
151
|
+
console.log(`No Pi extension packages found on npm${term ? ` for "${term}"` : ""}.`);
|
|
152
|
+
} else {
|
|
153
|
+
console.log(`Pi extension packages on npm${term ? ` matching "${term}"` : ""}:`);
|
|
154
|
+
for (const pkg of results) {
|
|
155
|
+
console.log(` ${pkg.name}@${pkg.version} ${pkg.description.slice(0, 80)}`);
|
|
156
|
+
}
|
|
157
|
+
console.log("Install one: `rig pi add <name>`");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return { ok: true, group: "pi", command, details: { term, results } };
|
|
161
|
+
}
|
|
162
|
+
default:
|
|
163
|
+
throw new CliError2(`Unknown pi command: ${command}. Use list|add|remove|search.`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
export {
|
|
167
|
+
executePi
|
|
168
|
+
};
|
package/dist/src/commands/run.js
CHANGED
|
@@ -488,7 +488,23 @@ function createPiRunStreamRenderer(output = process.stdout) {
|
|
|
488
488
|
}
|
|
489
489
|
if (entry.type === "timeline_warning") {
|
|
490
490
|
writeLine(`[Rig timeline] ${String(entry.detail ?? entry.message ?? "timeline unavailable")}`);
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
if (entry.type === "action") {
|
|
494
|
+
const text = String(entry.detail ?? entry.message ?? entry.title ?? "").trim();
|
|
495
|
+
if (text)
|
|
496
|
+
writeLine(`[Rig action] ${text}`);
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
if (entry.type === "user_message") {
|
|
500
|
+
const text = String(entry.text ?? entry.message ?? entry.detail ?? "").trim();
|
|
501
|
+
if (text)
|
|
502
|
+
writeLine(`[Operator] ${text}`);
|
|
503
|
+
continue;
|
|
491
504
|
}
|
|
505
|
+
const fallback = String(entry.detail ?? entry.message ?? entry.text ?? entry.title ?? "").trim();
|
|
506
|
+
if (fallback)
|
|
507
|
+
writeLine(`[${String(entry.type ?? "timeline")}] ${fallback}`);
|
|
492
508
|
}
|
|
493
509
|
},
|
|
494
510
|
renderLogs(entries) {
|
|
@@ -1794,6 +1810,33 @@ async function executeRun(context, args) {
|
|
|
1794
1810
|
}
|
|
1795
1811
|
return { ok: true, group: "run", command, details: restarted };
|
|
1796
1812
|
}
|
|
1813
|
+
case "steer": {
|
|
1814
|
+
const runOption = takeOption(rest, "--run");
|
|
1815
|
+
const messageOption = takeOption(runOption.rest, "--message");
|
|
1816
|
+
const shortMessageOption = takeOption(messageOption.rest, "-m");
|
|
1817
|
+
const positionalRunId = shortMessageOption.rest.length > 0 ? shortMessageOption.rest[0] : undefined;
|
|
1818
|
+
const extra = positionalRunId ? shortMessageOption.rest.slice(1) : shortMessageOption.rest;
|
|
1819
|
+
requireNoExtraArgs(extra, "rig run steer [<run-id>|--run <id>] --message <text>");
|
|
1820
|
+
const runId = runOption.value ?? positionalRunId;
|
|
1821
|
+
const message = messageOption.value ?? shortMessageOption.value;
|
|
1822
|
+
if (!runId) {
|
|
1823
|
+
throw new CliError2("run steer requires a run id (positional or --run <id>).", 2);
|
|
1824
|
+
}
|
|
1825
|
+
if (!message?.trim()) {
|
|
1826
|
+
throw new CliError2("run steer requires --message <text>.", 2);
|
|
1827
|
+
}
|
|
1828
|
+
if (context.dryRun) {
|
|
1829
|
+
if (context.outputMode === "text") {
|
|
1830
|
+
console.log(`[dry-run] rig run steer ${runId} --message ${JSON.stringify(message)}`);
|
|
1831
|
+
}
|
|
1832
|
+
return { ok: true, group: "run", command, details: { runId, dryRun: true } };
|
|
1833
|
+
}
|
|
1834
|
+
await steerRunViaServer(context, runId, message.trim());
|
|
1835
|
+
if (context.outputMode === "text") {
|
|
1836
|
+
console.log(`Steering message queued for ${runId}.`);
|
|
1837
|
+
}
|
|
1838
|
+
return { ok: true, group: "run", command, details: { runId, queued: true } };
|
|
1839
|
+
}
|
|
1797
1840
|
case "stop": {
|
|
1798
1841
|
const runOption = takeOption(rest, "--run");
|
|
1799
1842
|
const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
|
|
@@ -825,7 +825,23 @@ function createPiRunStreamRenderer(output = process.stdout) {
|
|
|
825
825
|
}
|
|
826
826
|
if (entry.type === "timeline_warning") {
|
|
827
827
|
writeLine(`[Rig timeline] ${String(entry.detail ?? entry.message ?? "timeline unavailable")}`);
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
if (entry.type === "action") {
|
|
831
|
+
const text = String(entry.detail ?? entry.message ?? entry.title ?? "").trim();
|
|
832
|
+
if (text)
|
|
833
|
+
writeLine(`[Rig action] ${text}`);
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (entry.type === "user_message") {
|
|
837
|
+
const text = String(entry.text ?? entry.message ?? entry.detail ?? "").trim();
|
|
838
|
+
if (text)
|
|
839
|
+
writeLine(`[Operator] ${text}`);
|
|
840
|
+
continue;
|
|
828
841
|
}
|
|
842
|
+
const fallback = String(entry.detail ?? entry.message ?? entry.text ?? entry.title ?? "").trim();
|
|
843
|
+
if (fallback)
|
|
844
|
+
writeLine(`[${String(entry.type ?? "timeline")}] ${fallback}`);
|
|
829
845
|
}
|
|
830
846
|
},
|
|
831
847
|
renderLogs(entries) {
|
|
@@ -1935,6 +1951,7 @@ var PRIMARY_GROUPS = [
|
|
|
1935
1951
|
{ command: "show <id>|--run <id> [--raw]", description: "Show a human run summary; --raw prints the full payload.", primary: true },
|
|
1936
1952
|
{ command: "attach <run-id>|--run <id> [--follow]", description: "Attach to the run; --follow launches native bundled Pi for live Pi runs.", primary: true },
|
|
1937
1953
|
{ command: "stop [<run-id>|--run <id>]", description: "Request stop for one run or local active runs.", primary: true },
|
|
1954
|
+
{ command: "steer <run-id> --message <text>", description: "Queue a steering message into a live worker without attaching." },
|
|
1938
1955
|
{ command: "timeline --run <id> [--follow]", description: "Stream raw run timeline events." },
|
|
1939
1956
|
{ command: "resume", description: "Resume the most recent interrupted local run." },
|
|
1940
1957
|
{ command: "restart", description: "Restart the most recent local run from a clean runtime." },
|
|
@@ -2036,6 +2053,19 @@ var ADVANCED_GROUPS = [
|
|
|
2036
2053
|
{ command: "hp-next <dev|check|e2e|reset>", description: "Drive the hp-next browser test harness." }
|
|
2037
2054
|
]
|
|
2038
2055
|
},
|
|
2056
|
+
{
|
|
2057
|
+
name: "pi",
|
|
2058
|
+
summary: "Manage Pi extension packages for this project (community extensions from npm/git).",
|
|
2059
|
+
usage: ["rig pi <list|add|remove|search> [args]"],
|
|
2060
|
+
commands: [
|
|
2061
|
+
{ command: "list", description: "Show project and user Pi extension packages." },
|
|
2062
|
+
{ command: "add <source>", description: "Add an npm/git Pi extension to .pi/settings.json (auto-installs at next session)." },
|
|
2063
|
+
{ command: "remove <source>", description: "Remove an operator-added Pi extension." },
|
|
2064
|
+
{ command: "search [term]", description: "Discover Pi extension packages on the npm registry." }
|
|
2065
|
+
],
|
|
2066
|
+
examples: ["rig pi search subagents", "rig pi add pi-subagents", "rig pi list"],
|
|
2067
|
+
next: ["Config-managed extensions: declare `runtime: { pi: { packages: [...] } }` in rig.config.ts \u2014 workers pick them up automatically."]
|
|
2068
|
+
},
|
|
2039
2069
|
{
|
|
2040
2070
|
name: "plugin",
|
|
2041
2071
|
summary: "Plugin listing, validation, and plugin-contributed commands.",
|