@h-rig/cli 0.0.6-alpha.3 → 0.0.6-alpha.31

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.
Files changed (47) hide show
  1. package/dist/bin/rig.js +3562 -1125
  2. package/dist/src/commands/_authority-runs.js +1 -0
  3. package/dist/src/commands/_cli-format.js +369 -0
  4. package/dist/src/commands/_connection-state.js +1 -3
  5. package/dist/src/commands/_doctor-checks.js +13 -27
  6. package/dist/src/commands/_help-catalog.js +388 -0
  7. package/dist/src/commands/_operator-surface.js +204 -0
  8. package/dist/src/commands/_operator-view.js +861 -56
  9. package/dist/src/commands/_parsers.js +0 -2
  10. package/dist/src/commands/_pi-frontend.js +841 -0
  11. package/dist/src/commands/_pi-install.js +4 -3
  12. package/dist/src/commands/_pi-worker-bridge-extension.js +759 -0
  13. package/dist/src/commands/_policy.js +0 -2
  14. package/dist/src/commands/_preflight.js +32 -109
  15. package/dist/src/commands/_run-driver-helpers.js +0 -2
  16. package/dist/src/commands/_server-client.js +161 -31
  17. package/dist/src/commands/_snapshot-upload.js +8 -23
  18. package/dist/src/commands/_task-picker.js +44 -16
  19. package/dist/src/commands/agent.js +9 -9
  20. package/dist/src/commands/browser.js +4 -6
  21. package/dist/src/commands/connect.js +132 -25
  22. package/dist/src/commands/dist.js +4 -6
  23. package/dist/src/commands/doctor.js +13 -27
  24. package/dist/src/commands/github.js +10 -25
  25. package/dist/src/commands/inbox.js +351 -31
  26. package/dist/src/commands/init.js +298 -71
  27. package/dist/src/commands/inspect.js +10 -12
  28. package/dist/src/commands/inspector.js +2 -4
  29. package/dist/src/commands/plugin.js +81 -22
  30. package/dist/src/commands/profile-and-review.js +8 -10
  31. package/dist/src/commands/queue.js +2 -3
  32. package/dist/src/commands/remote.js +18 -20
  33. package/dist/src/commands/repo-git-harness.js +6 -8
  34. package/dist/src/commands/run.js +1157 -122
  35. package/dist/src/commands/server.js +217 -33
  36. package/dist/src/commands/setup.js +17 -37
  37. package/dist/src/commands/task-report-bug.js +5 -7
  38. package/dist/src/commands/task-run-driver.js +660 -73
  39. package/dist/src/commands/task.js +1542 -252
  40. package/dist/src/commands/test.js +3 -5
  41. package/dist/src/commands/workspace.js +4 -6
  42. package/dist/src/commands.js +3548 -1105
  43. package/dist/src/index.js +3562 -1128
  44. package/dist/src/launcher.js +5 -3
  45. package/dist/src/report-bug.js +3 -3
  46. package/dist/src/runner.js +5 -19
  47. package/package.json +6 -4
@@ -1,6 +1,9 @@
1
1
  // @bun
2
2
  // packages/cli/src/commands/_task-picker.ts
3
- import { createInterface } from "readline/promises";
3
+ import { cancel, isCancel, select } from "@clack/prompts";
4
+
5
+ // packages/cli/src/commands/_operator-surface.ts
6
+ import { createInterface as createPromptInterface } from "readline/promises";
4
7
  function taskId(task) {
5
8
  return typeof task.id === "string" && task.id.trim() ? task.id : "<unknown>";
6
9
  }
@@ -13,6 +16,19 @@ function taskStatus(task) {
13
16
  function renderTaskPickerRows(tasks) {
14
17
  return tasks.map((task, index) => `${index + 1}. ${taskId(task)} \xB7 ${taskStatus(task)} \xB7 ${taskTitle(task)}`);
15
18
  }
19
+ async function promptForTaskSelection(question) {
20
+ const rl = createPromptInterface({ input: process.stdin, output: process.stdout });
21
+ try {
22
+ return await rl.question(question);
23
+ } finally {
24
+ rl.close();
25
+ }
26
+ }
27
+
28
+ // packages/cli/src/commands/_task-picker.ts
29
+ function taskId2(task) {
30
+ return typeof task.id === "string" && task.id.trim() ? task.id : "<unknown>";
31
+ }
16
32
  async function selectTaskWithTextPicker(tasks, io = {}) {
17
33
  if (tasks.length === 0)
18
34
  return null;
@@ -22,25 +38,37 @@ async function selectTaskWithTextPicker(tasks, io = {}) {
22
38
  if (!isTty) {
23
39
  throw new Error("task run requires an interactive terminal to pick a task; pass --task <id>, --next, or --detach with a task id.");
24
40
  }
25
- const prompt = io.prompt ?? (async (question) => {
26
- const rl = createInterface({ input: process.stdin, output: process.stdout });
27
- try {
28
- return await rl.question(question);
29
- } finally {
30
- rl.close();
41
+ if (io.prompt || io.renderer) {
42
+ const prompt = io.prompt ?? promptForTaskSelection;
43
+ const renderer = io.renderer ?? { writeLine: (line) => process.stdout.write(`${line}
44
+ `) };
45
+ renderer.writeLine("Select Rig task:");
46
+ for (const row of renderTaskPickerRows(tasks))
47
+ renderer.writeLine(` ${row}`);
48
+ const answer2 = (await prompt(`Task [1-${tasks.length}] or id: `)).trim();
49
+ if (!answer2)
50
+ return null;
51
+ if (/^\d+$/.test(answer2)) {
52
+ const index2 = Number.parseInt(answer2, 10) - 1;
53
+ return tasks[index2] ?? null;
31
54
  }
55
+ return tasks.find((task) => taskId2(task) === answer2) ?? null;
56
+ }
57
+ const options = tasks.map((task, index2) => ({
58
+ value: `${index2}`,
59
+ label: `${taskId2(task)} \xB7 ${typeof task.title === "string" && task.title.trim() ? task.title.trim() : "Untitled task"}`,
60
+ hint: typeof task.status === "string" && task.status.trim() ? task.status.trim() : undefined
61
+ }));
62
+ const answer = await select({
63
+ message: "Select Rig task",
64
+ options
32
65
  });
33
- console.log("Select Rig task:");
34
- for (const row of renderTaskPickerRows(tasks))
35
- console.log(` ${row}`);
36
- const answer = (await prompt(`Task [1-${tasks.length}] or id: `)).trim();
37
- if (!answer)
66
+ if (isCancel(answer)) {
67
+ cancel("No task selected.");
38
68
  return null;
39
- if (/^\d+$/.test(answer)) {
40
- const index = Number.parseInt(answer, 10) - 1;
41
- return tasks[index] ?? null;
42
69
  }
43
- return tasks.find((task) => taskId(task) === answer) ?? null;
70
+ const index = Number.parseInt(String(answer), 10);
71
+ return Number.isFinite(index) ? tasks[index] ?? null : null;
44
72
  }
45
73
  export {
46
74
  selectTaskWithTextPicker,
@@ -6,8 +6,6 @@ import { resolve as resolve2 } from "path";
6
6
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
7
7
  import { CliError } 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
  import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
13
11
  function takeFlag(args, flag) {
@@ -126,6 +124,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
126
124
  const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
127
125
  const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
128
126
  const next = {
127
+ ...existing ?? {},
129
128
  runId: input.runId,
130
129
  projectRoot,
131
130
  workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
@@ -220,6 +219,7 @@ import { ensureProjectMainFreshBeforeRun } from "@rig/runtime/control-plane/proj
220
219
 
221
220
  // packages/cli/src/commands/_server-client.ts
222
221
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
222
+ var scopedGitHubBearerTokens = new Map;
223
223
 
224
224
  // packages/cli/src/commands/_preflight.ts
225
225
  async function runProjectMainSyncPreflight(context, options) {
@@ -288,7 +288,7 @@ async function executeAgent(context, args) {
288
288
  const [command = "list", ...rest] = args;
289
289
  switch (command) {
290
290
  case "list": {
291
- requireNoExtraArgs(rest, "bun run rig agent list");
291
+ requireNoExtraArgs(rest, "rig agent list");
292
292
  const runtimes = await listAgentRuntimes(context.projectRoot);
293
293
  if (context.outputMode === "text") {
294
294
  if (runtimes.length === 0) {
@@ -309,12 +309,12 @@ async function executeAgent(context, args) {
309
309
  pending = modeResult.rest;
310
310
  const taskResult = takeOption(pending, "--task");
311
311
  pending = taskResult.rest;
312
- requireNoExtraArgs(pending, "bun run rig agent prepare --task <id> [--id <id>] [--mode worktree]");
312
+ requireNoExtraArgs(pending, "rig agent prepare --task <id> [--id <id>] [--mode worktree]");
313
313
  const mode = parseIsolationMode(modeResult.value, false);
314
314
  const id = idResult.value || agentId("agent");
315
315
  const taskId = taskResult.value?.trim();
316
316
  if (!taskId) {
317
- throw new CliError2("Usage: bun run rig agent prepare --task <id> [--id <id>] [--mode worktree]");
317
+ throw new CliError2("Usage: rig agent prepare --task <id> [--id <id>] [--mode worktree]");
318
318
  }
319
319
  const runtime = await withMutedConsole(context.outputMode === "json", () => ensureAgentRuntime({
320
320
  projectRoot: context.projectRoot,
@@ -332,7 +332,7 @@ async function executeAgent(context, args) {
332
332
  case "run": {
333
333
  const { options, commandParts } = splitAtDoubleDash(rest);
334
334
  if (commandParts.length === 0) {
335
- throw new CliError2("Usage: bun run rig agent run [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
335
+ throw new CliError2("Usage: rig agent run [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
336
336
  }
337
337
  let pending = options;
338
338
  const idResult = takeOption(pending, "--id");
@@ -343,12 +343,12 @@ async function executeAgent(context, args) {
343
343
  pending = taskResult.rest;
344
344
  const skipProjectSyncResult = takeFlag(pending, "--skip-project-sync");
345
345
  pending = skipProjectSyncResult.rest;
346
- requireNoExtraArgs(pending, "bun run rig agent run --task <id> [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
346
+ requireNoExtraArgs(pending, "rig agent run --task <id> [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
347
347
  const mode = parseIsolationMode(modeResult.value, false);
348
348
  const id = idResult.value || agentId("agent-run");
349
349
  const taskId = taskResult.value?.trim();
350
350
  if (!taskId) {
351
- throw new CliError2("Usage: bun run rig agent run --task <id> [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
351
+ throw new CliError2("Usage: rig agent run --task <id> [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
352
352
  }
353
353
  await runProjectMainSyncPreflight(context, { disabled: skipProjectSyncResult.value });
354
354
  const createdAt = new Date().toISOString();
@@ -462,7 +462,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
462
462
  pending = allResult.rest;
463
463
  const idResult = takeOption(pending, "--id");
464
464
  pending = idResult.rest;
465
- requireNoExtraArgs(pending, "bun run rig agent cleanup (--id <id> | --all)");
465
+ requireNoExtraArgs(pending, "rig agent cleanup (--id <id> | --all)");
466
466
  if (!allResult.value && !idResult.value) {
467
467
  throw new CliError2("Provide --id <id> or --all.");
468
468
  }
@@ -12,8 +12,6 @@ import pc2 from "picocolors";
12
12
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
13
13
  import { CliError } from "@rig/runtime/control-plane/errors";
14
14
  import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
15
- import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
16
- import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
17
15
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
18
16
  import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
19
17
  function takeFlag(args, flag) {
@@ -351,7 +349,7 @@ async function executeBrowser(context, args) {
351
349
  return { ok: true, group: "browser", command: "help" };
352
350
  }
353
351
  if (command === "explain") {
354
- requireNoExtraArgs(rest, "bun run rig browser explain");
352
+ requireNoExtraArgs(rest, "rig browser explain");
355
353
  console.log(browserAgentUsageText());
356
354
  return { ok: true, group: "browser", command: "explain" };
357
355
  }
@@ -377,7 +375,7 @@ ${browserHelpText()}`);
377
375
 
378
376
  ${browserHelpText()}`);
379
377
  }
380
- requireNoExtraArgs(appRest, `bun run rig browser ${command} ${subcommand}`);
378
+ requireNoExtraArgs(appRest, `rig browser ${command} ${subcommand}`);
381
379
  await context.runCommand(["bun", "run", `app:${subcommand}:browser:${appSlug}`]);
382
380
  return { ok: true, group: "browser", command: `${command}-${subcommand}` };
383
381
  }
@@ -389,7 +387,7 @@ ${browserHelpText()}`);
389
387
  };
390
388
  const packageScript = packageScripts[command];
391
389
  if (packageScript) {
392
- requireNoExtraArgs(rest, `bun run rig browser ${command}`);
390
+ requireNoExtraArgs(rest, `rig browser ${command}`);
393
391
  await context.runCommand(["bun", "run", "--filter=@rig/browser", packageScript]);
394
392
  return { ok: true, group: "browser", command };
395
393
  }
@@ -416,7 +414,7 @@ async function executeBrowserDemo(context, args) {
416
414
  pending = keepOpenFlag.rest;
417
415
  const noBuildFlag = takeFlag(pending, "--no-build");
418
416
  pending = noBuildFlag.rest;
419
- requireNoExtraArgs(pending, "bun run rig browser demo [--port <n>] [--profile <name>] [--state-dir <path>] [--target-url <url>] [--keep-open] [--no-build]");
417
+ requireNoExtraArgs(pending, "rig browser demo [--port <n>] [--profile <name>] [--state-dir <path>] [--target-url <url>] [--keep-open] [--no-build]");
420
418
  if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
421
419
  throw new CliError2("rig browser demo requires an interactive TTY in text mode.");
422
420
  }
@@ -1,10 +1,11 @@
1
1
  // @bun
2
+ // packages/cli/src/commands/connect.ts
3
+ import { cancel, isCancel, select } from "@clack/prompts";
4
+
2
5
  // packages/cli/src/runner.ts
3
6
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
4
7
  import { CliError } from "@rig/runtime/control-plane/errors";
5
8
  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
9
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
9
10
  import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
10
11
  function requireNoExtraArgs(args, usage) {
@@ -103,12 +104,82 @@ function writeRepoConnection(projectRoot, state) {
103
104
  writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
104
105
  }
105
106
 
107
+ // packages/cli/src/commands/_cli-format.ts
108
+ import { log, note } from "@clack/prompts";
109
+ import pc from "picocolors";
110
+ function truncate(value, width) {
111
+ if (value.length <= width)
112
+ return value;
113
+ if (width <= 1)
114
+ return "\u2026";
115
+ return `${value.slice(0, width - 1)}\u2026`;
116
+ }
117
+ function pad(value, width) {
118
+ return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
119
+ }
120
+ function statusColor(status) {
121
+ const normalized = status.toLowerCase();
122
+ if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved"].includes(normalized))
123
+ return pc.green;
124
+ if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
125
+ return pc.red;
126
+ if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
127
+ return pc.cyan;
128
+ if (["ready", "open", "queued", "created", "preparing", "local", "pending"].includes(normalized))
129
+ return pc.yellow;
130
+ return pc.dim;
131
+ }
132
+ function formatStatusPill(status) {
133
+ const label = status || "unknown";
134
+ return statusColor(label)(`\u25CF ${label}`);
135
+ }
136
+ function formatSection(title, subtitle) {
137
+ return `${pc.bold(pc.cyan("\u25C6"))} ${pc.bold(title)}${subtitle ? pc.dim(` \u2014 ${subtitle}`) : ""}`;
138
+ }
139
+ function formatSuccessCard(title, rows = []) {
140
+ const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${pc.dim("\u2502")} ${pc.dim(key.padEnd(12))} ${value}`);
141
+ return [formatSection(title), ...body].join(`
142
+ `);
143
+ }
144
+ function formatNextSteps(steps) {
145
+ if (steps.length === 0)
146
+ return [];
147
+ return [pc.bold("Next"), ...steps.map((step) => `${pc.dim("\u203A")} ${step}`)];
148
+ }
149
+ function formatConnectionList(connections) {
150
+ const rows = [["local", { kind: "local", mode: "auto" }], ...Object.entries(connections)];
151
+ const aliasWidth = Math.min(24, Math.max(5, ...rows.map(([alias]) => alias.length)));
152
+ const lines = rows.map(([alias, connection]) => [
153
+ pc.bold(pad(truncate(alias, aliasWidth), aliasWidth)),
154
+ formatStatusPill(connection.kind),
155
+ connection.kind === "remote" ? connection.baseUrl ?? "" : connection.mode ?? "local"
156
+ ].join(" "));
157
+ return [formatSection("Rig servers", `${rows.length} available`), `${pc.bold(pad("ALIAS", aliasWidth))} ${pc.bold("KIND")} ${pc.bold("TARGET")}`, ...lines, "", ...formatNextSteps(["Select one: `rig server use <alias|local>`"])].join(`
158
+ `);
159
+ }
160
+ function formatConnectionStatus(selected, connections) {
161
+ const connection = selected === "local" ? { kind: "local", mode: "auto" } : connections[selected];
162
+ const target = !connection ? "not configured" : connection.kind === "remote" ? connection.baseUrl : "local";
163
+ return [
164
+ formatSection("Rig server", "selected for this repo"),
165
+ `${pc.dim("\u2502")} ${pc.dim("selected ")} ${pc.bold(selected)}`,
166
+ `${pc.dim("\u2502")} ${pc.dim("kind ")} ${formatStatusPill(connection?.kind ?? "unknown")}`,
167
+ `${pc.dim("\u2502")} ${pc.dim("target ")} ${target ?? "not configured"}`,
168
+ "",
169
+ ...formatNextSteps(["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
170
+ ].join(`
171
+ `);
172
+ }
173
+
106
174
  // packages/cli/src/commands/connect.ts
107
- function parseConnection(alias, value) {
175
+ function usageName(options) {
176
+ return `rig ${options.group}`;
177
+ }
178
+ function parseConnection(alias, value, options) {
108
179
  if (alias === "local" && !value)
109
180
  return { kind: "local", mode: "auto" };
110
181
  if (!value)
111
- throw new CliError2("Missing remote server URL. Usage: rig connect add <alias> <url>", 1);
182
+ throw new CliError2(`Missing remote server URL. Usage: ${usageName(options)} add <alias> <url>`, 1);
112
183
  let parsed;
113
184
  try {
114
185
  parsed = new URL(value);
@@ -127,54 +198,90 @@ function printJsonOrText(context, payload, text) {
127
198
  console.log(text);
128
199
  }
129
200
  }
130
- async function executeConnect(context, args) {
201
+ async function promptForConnectionAlias(context) {
202
+ const state = readGlobalConnections();
203
+ const repo = readRepoConnection(context.projectRoot);
204
+ const options = [
205
+ { value: "local", label: "local", hint: "Use/start a local Rig server" },
206
+ ...Object.entries(state.connections).map(([alias, connection]) => ({
207
+ value: alias,
208
+ label: alias,
209
+ hint: connection.kind === "remote" ? connection.baseUrl : "local"
210
+ }))
211
+ ].filter((option, index, all) => all.findIndex((candidate) => candidate.value === option.value) === index);
212
+ const answer = await select({
213
+ message: "Select Rig server for this repo",
214
+ initialValue: repo?.selected ?? "local",
215
+ options
216
+ });
217
+ if (isCancel(answer)) {
218
+ cancel("No server selected.");
219
+ throw new CliError2("No server selected.", 3);
220
+ }
221
+ return String(answer);
222
+ }
223
+ async function executeConnectionCommand(context, args, options) {
131
224
  const [command, ...rest] = args;
132
225
  switch (command ?? "status") {
133
226
  case "list": {
134
- requireNoExtraArgs(rest, "rig connect list");
227
+ requireNoExtraArgs(rest, `${usageName(options)} list`);
135
228
  const state = readGlobalConnections();
136
- printJsonOrText(context, state, Object.entries(state.connections).map(([alias, connection]) => `${alias} ${connection.kind === "remote" ? connection.baseUrl : "local"}`).join(`
137
- `) || "No Rig connections configured.");
138
- return { ok: true, group: "connect", command: "list", details: state };
229
+ printJsonOrText(context, state, formatConnectionList(state.connections));
230
+ return { ok: true, group: options.group, command: "list", details: state };
139
231
  }
140
232
  case "add": {
141
233
  const [alias, url, ...extra] = rest;
142
234
  if (!alias)
143
- throw new CliError2("Missing alias. Usage: rig connect add <alias> <url>", 1);
144
- requireNoExtraArgs(extra, "rig connect add <alias> <url>");
145
- const connection = parseConnection(alias, url);
235
+ throw new CliError2(`Missing alias. Usage: ${usageName(options)} add <alias> <url>`, 1);
236
+ requireNoExtraArgs(extra, `${usageName(options)} add <alias> <url>`);
237
+ const connection = parseConnection(alias, url, options);
146
238
  const state = upsertGlobalConnection(alias, connection);
147
- printJsonOrText(context, { alias, connection }, `Added Rig connection ${alias}.`);
148
- return { ok: true, group: "connect", command: "add", details: { alias, connection, count: Object.keys(state.connections).length } };
239
+ printJsonOrText(context, { alias, connection }, formatSuccessCard("Rig server saved", [
240
+ ["alias", alias],
241
+ ["target", connection.kind === "remote" ? connection.baseUrl : "local"],
242
+ ["next", `${usageName(options)} use ${alias}`]
243
+ ]));
244
+ return { ok: true, group: options.group, command: "add", details: { alias, connection, count: Object.keys(state.connections).length } };
149
245
  }
150
246
  case "use": {
151
- const [alias, ...extra] = rest;
247
+ let [alias, ...extra] = rest;
248
+ requireNoExtraArgs(extra, `${usageName(options)} use <alias|local>`);
249
+ if (!alias && options.interactiveUse && context.outputMode === "text" && process.stdin.isTTY) {
250
+ alias = await promptForConnectionAlias(context);
251
+ }
152
252
  if (!alias)
153
- throw new CliError2("Missing alias. Usage: rig connect use <alias|local>", 1);
154
- requireNoExtraArgs(extra, "rig connect use <alias|local>");
253
+ throw new CliError2(`Missing alias. Usage: ${usageName(options)} use <alias|local>`, 1);
155
254
  if (alias !== "local") {
156
255
  const state = readGlobalConnections();
157
256
  if (!state.connections[alias])
158
- throw new CliError2(`Unknown Rig connection: ${alias}`, 1);
257
+ throw new CliError2(`Unknown Rig server: ${alias}`, 1);
159
258
  }
160
259
  const repoState = { selected: alias, linkedAt: new Date().toISOString() };
161
260
  writeRepoConnection(context.projectRoot, repoState);
162
- printJsonOrText(context, repoState, `Selected Rig connection ${alias} for this repo.`);
163
- return { ok: true, group: "connect", command: "use", details: repoState };
261
+ printJsonOrText(context, repoState, formatSuccessCard("Rig server selected", [
262
+ ["selected", alias],
263
+ ["scope", "this repo"],
264
+ ["next", "rig task list"]
265
+ ]));
266
+ return { ok: true, group: options.group, command: "use", details: repoState };
164
267
  }
165
268
  case "status": {
166
- requireNoExtraArgs(rest, "rig connect status");
269
+ requireNoExtraArgs(rest, `${usageName(options)} status`);
167
270
  const repo = readRepoConnection(context.projectRoot);
168
271
  const global = readGlobalConnections();
169
272
  const details = { selected: repo?.selected ?? "local", repo, connections: global.connections };
170
- printJsonOrText(context, details, `Selected Rig connection: ${details.selected}`);
171
- return { ok: true, group: "connect", command: "status", details };
273
+ printJsonOrText(context, details, formatConnectionStatus(details.selected, global.connections));
274
+ return { ok: true, group: options.group, command: "status", details };
172
275
  }
173
276
  default:
174
- throw new CliError2(`Unknown connect command: ${String(command)}
175
- Usage: rig connect <list|add|use|status>`, 1);
277
+ throw new CliError2(`Unknown ${options.group} command: ${String(command)}
278
+ Usage: ${usageName(options)} <list|add|use|status>`, 1);
176
279
  }
177
280
  }
281
+ async function executeConnect(context, args) {
282
+ return executeConnectionCommand(context, args, { group: "connect" });
283
+ }
178
284
  export {
285
+ executeConnectionCommand,
179
286
  executeConnect
180
287
  };
@@ -19,8 +19,6 @@ import { resolve as resolve3 } from "path";
19
19
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
20
20
  import { CliError } from "@rig/runtime/control-plane/errors";
21
21
  import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
22
- import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
23
- import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
24
22
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
25
23
  import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
26
24
  function takeOption(args, option) {
@@ -185,7 +183,7 @@ async function executeDist(context, args) {
185
183
  switch (command) {
186
184
  case "build": {
187
185
  const { value: outputDir, rest: pending } = takeOption(rest, "--output-dir");
188
- requireNoExtraArgs(pending, "bun run rig dist build [--output-dir <dir>]");
186
+ requireNoExtraArgs(pending, "rig dist build [--output-dir <dir>]");
189
187
  const commandParts = ["bun", "run", "packages/cli/bin/build-rig-binaries.ts"];
190
188
  if (outputDir) {
191
189
  commandParts.push("--output-dir", outputDir);
@@ -199,7 +197,7 @@ async function executeDist(context, args) {
199
197
  pending = scopeResult.rest;
200
198
  const pathResult = takeOption(pending, "--path");
201
199
  pending = pathResult.rest;
202
- requireNoExtraArgs(pending, "bun run rig dist install [--scope user|system] [--path <dir>]");
200
+ requireNoExtraArgs(pending, "rig dist install [--scope user|system] [--path <dir>]");
203
201
  const scope = parseInstallScope(scopeResult.value);
204
202
  const installDir = resolveInstallDir(scope, pathResult.value);
205
203
  mkdirSync(installDir, { recursive: true });
@@ -242,7 +240,7 @@ async function executeDist(context, args) {
242
240
  };
243
241
  }
244
242
  case "doctor": {
245
- requireNoExtraArgs(rest, "bun run rig dist doctor");
243
+ requireNoExtraArgs(rest, "rig dist doctor");
246
244
  const details = await runDistDoctor(context.projectRoot);
247
245
  if (context.outputMode === "text") {
248
246
  console.log(`bun: ${details.bun.available ? `ok (${details.bun.version})` : "missing"}`);
@@ -253,7 +251,7 @@ async function executeDist(context, args) {
253
251
  return { ok: true, group: "dist", command, details };
254
252
  }
255
253
  case "rebuild-agent": {
256
- requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
254
+ requireNoExtraArgs(rest, "rig dist rebuild-agent");
257
255
  const fp = await computeRuntimeImageFingerprint(context.projectRoot);
258
256
  const currentId = computeRuntimeImageId(fp);
259
257
  const imagesDir = resolve3(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
@@ -5,8 +5,6 @@ var __require = import.meta.require;
5
5
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
6
6
  import { CliError } from "@rig/runtime/control-plane/errors";
7
7
  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
8
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
11
9
  import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
12
10
  function requireNoExtraArgs(args, usage) {
@@ -98,17 +96,16 @@ function resolveSelectedConnection(projectRoot, options = {}) {
98
96
  const global = readGlobalConnections(options);
99
97
  const connection = global.connections[repo.selected];
100
98
  if (!connection) {
101
- throw new CliError2(`Selected Rig connection "${repo.selected}" was not found. Run \`rig connect list\` or \`rig connect use local\`.`, 1);
99
+ throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
102
100
  }
103
101
  return { alias: repo.selected, connection };
104
102
  }
105
103
 
106
104
  // packages/cli/src/commands/_server-client.ts
107
- import { spawnSync } from "child_process";
108
105
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
109
106
  import { resolve as resolve2 } from "path";
110
107
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
111
- var cachedGitHubBearerToken;
108
+ var scopedGitHubBearerTokens = new Map;
112
109
  function cleanToken(value) {
113
110
  const trimmed = value?.trim();
114
111
  return trimmed ? trimmed : null;
@@ -125,25 +122,13 @@ function readPrivateRemoteSessionToken(projectRoot) {
125
122
  }
126
123
  }
127
124
  function readGitHubBearerTokenForRemote(projectRoot) {
128
- if (cachedGitHubBearerToken !== undefined)
129
- return cachedGitHubBearerToken;
125
+ const scopedKey = resolve2(projectRoot);
126
+ if (scopedGitHubBearerTokens.has(scopedKey))
127
+ return scopedGitHubBearerTokens.get(scopedKey) ?? null;
130
128
  const privateSession = readPrivateRemoteSessionToken(projectRoot);
131
- if (privateSession) {
132
- cachedGitHubBearerToken = privateSession;
133
- return cachedGitHubBearerToken;
134
- }
135
- const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
136
- if (envToken) {
137
- cachedGitHubBearerToken = envToken;
138
- return cachedGitHubBearerToken;
139
- }
140
- const result = spawnSync("gh", ["auth", "token"], {
141
- encoding: "utf8",
142
- timeout: 5000,
143
- stdio: ["ignore", "pipe", "ignore"]
144
- });
145
- cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
146
- return cachedGitHubBearerToken;
129
+ if (privateSession)
130
+ return privateSession;
131
+ return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
147
132
  }
148
133
  async function ensureServerForCli(projectRoot) {
149
134
  try {
@@ -226,7 +211,8 @@ async function loadRigConfigOrNull(projectRoot) {
226
211
  import { existsSync as existsSync3, readFileSync as readFileSync3, rmSync } from "fs";
227
212
  import { homedir as homedir2 } from "os";
228
213
  import { resolve as resolve3 } from "path";
229
- var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
214
+ var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
215
+ var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
230
216
  async function defaultCommandRunner(command, options = {}) {
231
217
  const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
232
218
  const [stdout, stderr, exitCode] = await Promise.all([
@@ -245,7 +231,7 @@ function resolvePiHomeDir(inputHomeDir) {
245
231
  function piListContainsPiRig(output) {
246
232
  return output.split(/\r?\n/).some((line) => {
247
233
  const normalized = line.trim();
248
- return normalized.includes(PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
234
+ return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
249
235
  });
250
236
  }
251
237
  async function safeRun(runner, command, options) {
@@ -421,7 +407,7 @@ async function runRigDoctorChecks(options) {
421
407
  const taskSourceKind = config?.taskSource?.kind;
422
408
  checks.push(taskSourceKind ? check("task-source", "task source configured", "pass", taskSourceKind) : check("task-source", "task source configured", "fail", "missing taskSource", "Configure taskSource in rig.config.ts."));
423
409
  const repo = readRepoConnection(projectRoot);
424
- checks.push(repo ? check("project-link", "repo selected Rig connection", repo.project ? "pass" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : ""}`, "Run `rig init --yes --repo owner/repo` to link this checkout to a GitHub repo slug.") : check("project-link", "repo selected Rig connection", "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig connect use <alias|local>`."));
410
+ checks.push(repo ? check("project-link", "repo selected Rig server", repo.project ? "pass" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : ""}`, "Run `rig init --yes --repo owner/repo` to link this checkout to a GitHub repo slug.") : check("project-link", "repo selected Rig server", "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig server use <alias|local>`."));
425
411
  const selected = (() => {
426
412
  try {
427
413
  return resolveSelectedConnection(projectRoot);
@@ -429,7 +415,7 @@ async function runRigDoctorChecks(options) {
429
415
  return null;
430
416
  }
431
417
  })();
432
- checks.push(selected ? check("connection", "selected server connection", "pass", selected.connection.kind === "remote" ? selected.connection.baseUrl : "local auto") : check("connection", "selected server connection", repo ? "fail" : "warn", repo ? "selected alias is missing" : "will auto-start local server", repo ? "Run `rig connect list` and `rig connect use <alias|local>`." : undefined));
418
+ 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));
433
419
  let server = null;
434
420
  try {
435
421
  server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);
@@ -1,13 +1,11 @@
1
1
  // @bun
2
2
  // packages/cli/src/commands/github.ts
3
- import { spawnSync as spawnSync2 } from "child_process";
3
+ import { spawnSync } from "child_process";
4
4
 
5
5
  // packages/cli/src/runner.ts
6
6
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
7
7
  import { CliError } 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
  import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
13
11
  function takeOption(args, option) {
@@ -32,7 +30,6 @@ function takeOption(args, option) {
32
30
  }
33
31
 
34
32
  // packages/cli/src/commands/_server-client.ts
35
- import { spawnSync } from "child_process";
36
33
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
37
34
  import { resolve as resolve2 } from "path";
38
35
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
@@ -114,13 +111,13 @@ function resolveSelectedConnection(projectRoot, options = {}) {
114
111
  const global = readGlobalConnections(options);
115
112
  const connection = global.connections[repo.selected];
116
113
  if (!connection) {
117
- throw new CliError2(`Selected Rig connection "${repo.selected}" was not found. Run \`rig connect list\` or \`rig connect use local\`.`, 1);
114
+ throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
118
115
  }
119
116
  return { alias: repo.selected, connection };
120
117
  }
121
118
 
122
119
  // packages/cli/src/commands/_server-client.ts
123
- var cachedGitHubBearerToken;
120
+ var scopedGitHubBearerTokens = new Map;
124
121
  function cleanToken(value) {
125
122
  const trimmed = value?.trim();
126
123
  return trimmed ? trimmed : null;
@@ -137,25 +134,13 @@ function readPrivateRemoteSessionToken(projectRoot) {
137
134
  }
138
135
  }
139
136
  function readGitHubBearerTokenForRemote(projectRoot) {
140
- if (cachedGitHubBearerToken !== undefined)
141
- return cachedGitHubBearerToken;
137
+ const scopedKey = resolve2(projectRoot);
138
+ if (scopedGitHubBearerTokens.has(scopedKey))
139
+ return scopedGitHubBearerTokens.get(scopedKey) ?? null;
142
140
  const privateSession = readPrivateRemoteSessionToken(projectRoot);
143
- if (privateSession) {
144
- cachedGitHubBearerToken = privateSession;
145
- return cachedGitHubBearerToken;
146
- }
147
- const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
148
- if (envToken) {
149
- cachedGitHubBearerToken = envToken;
150
- return cachedGitHubBearerToken;
151
- }
152
- const result = spawnSync("gh", ["auth", "token"], {
153
- encoding: "utf8",
154
- timeout: 5000,
155
- stdio: ["ignore", "pipe", "ignore"]
156
- });
157
- cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
158
- return cachedGitHubBearerToken;
141
+ if (privateSession)
142
+ return privateSession;
143
+ return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
159
144
  }
160
145
  async function ensureServerForCli(projectRoot) {
161
146
  try {
@@ -244,7 +229,7 @@ function printPayload(context, payload, fallback) {
244
229
  console.log(fallback);
245
230
  }
246
231
  function readGhToken() {
247
- const result = spawnSync2("gh", ["auth", "token"], { encoding: "utf8" });
232
+ const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
248
233
  if (result.status !== 0) {
249
234
  const detail = result.stderr?.trim() || result.stdout?.trim() || "gh auth token failed";
250
235
  throw new CliError2(`Could not import GitHub token from gh: ${detail}`, 1);