@h-rig/cli 0.0.6-alpha.6 → 0.0.6-alpha.60

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 (50) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/rig.js +4184 -1228
  3. package/dist/src/commands/_cli-format.js +369 -0
  4. package/dist/src/commands/_connection-state.js +12 -6
  5. package/dist/src/commands/_doctor-checks.js +79 -34
  6. package/dist/src/commands/_help-catalog.js +445 -0
  7. package/dist/src/commands/_operator-surface.js +220 -0
  8. package/dist/src/commands/_operator-view.js +1124 -64
  9. package/dist/src/commands/_parsers.js +0 -2
  10. package/dist/src/commands/_pi-frontend.js +1080 -0
  11. package/dist/src/commands/_pi-install.js +4 -3
  12. package/dist/src/commands/_pi-remote-session.js +771 -0
  13. package/dist/src/commands/_pi-worker-bridge-extension.js +834 -0
  14. package/dist/src/commands/_policy.js +0 -2
  15. package/dist/src/commands/_preflight.js +98 -116
  16. package/dist/src/commands/_run-driver-helpers.js +34 -2
  17. package/dist/src/commands/_server-client.js +225 -48
  18. package/dist/src/commands/_snapshot-upload.js +74 -30
  19. package/dist/src/commands/_spinner.js +63 -0
  20. package/dist/src/commands/_task-picker.js +44 -16
  21. package/dist/src/commands/agent.js +8 -9
  22. package/dist/src/commands/browser.js +4 -6
  23. package/dist/src/commands/connect.js +134 -26
  24. package/dist/src/commands/dist.js +4 -6
  25. package/dist/src/commands/doctor.js +79 -34
  26. package/dist/src/commands/github.js +76 -32
  27. package/dist/src/commands/inbox.js +410 -31
  28. package/dist/src/commands/init.js +398 -90
  29. package/dist/src/commands/inspect.js +296 -23
  30. package/dist/src/commands/inspector.js +2 -4
  31. package/dist/src/commands/pi.js +168 -0
  32. package/dist/src/commands/plugin.js +81 -22
  33. package/dist/src/commands/profile-and-review.js +8 -10
  34. package/dist/src/commands/queue.js +2 -3
  35. package/dist/src/commands/remote.js +18 -20
  36. package/dist/src/commands/repo-git-harness.js +6 -8
  37. package/dist/src/commands/run.js +1422 -131
  38. package/dist/src/commands/server.js +280 -40
  39. package/dist/src/commands/setup.js +84 -45
  40. package/dist/src/commands/task-report-bug.js +5 -7
  41. package/dist/src/commands/task-run-driver.js +710 -70
  42. package/dist/src/commands/task.js +1861 -260
  43. package/dist/src/commands/test.js +3 -5
  44. package/dist/src/commands/workspace.js +4 -6
  45. package/dist/src/commands.js +4143 -1181
  46. package/dist/src/index.js +4156 -1203
  47. package/dist/src/launcher.js +5 -3
  48. package/dist/src/report-bug.js +3 -3
  49. package/dist/src/runner.js +5 -19
  50. package/package.json +7 -5
@@ -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) {
@@ -221,6 +219,7 @@ import { ensureProjectMainFreshBeforeRun } from "@rig/runtime/control-plane/proj
221
219
 
222
220
  // packages/cli/src/commands/_server-client.ts
223
221
  import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
222
+ var scopedGitHubBearerTokens = new Map;
224
223
 
225
224
  // packages/cli/src/commands/_preflight.ts
226
225
  async function runProjectMainSyncPreflight(context, options) {
@@ -289,7 +288,7 @@ async function executeAgent(context, args) {
289
288
  const [command = "list", ...rest] = args;
290
289
  switch (command) {
291
290
  case "list": {
292
- requireNoExtraArgs(rest, "bun run rig agent list");
291
+ requireNoExtraArgs(rest, "rig agent list");
293
292
  const runtimes = await listAgentRuntimes(context.projectRoot);
294
293
  if (context.outputMode === "text") {
295
294
  if (runtimes.length === 0) {
@@ -310,12 +309,12 @@ async function executeAgent(context, args) {
310
309
  pending = modeResult.rest;
311
310
  const taskResult = takeOption(pending, "--task");
312
311
  pending = taskResult.rest;
313
- 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]");
314
313
  const mode = parseIsolationMode(modeResult.value, false);
315
314
  const id = idResult.value || agentId("agent");
316
315
  const taskId = taskResult.value?.trim();
317
316
  if (!taskId) {
318
- 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]");
319
318
  }
320
319
  const runtime = await withMutedConsole(context.outputMode === "json", () => ensureAgentRuntime({
321
320
  projectRoot: context.projectRoot,
@@ -333,7 +332,7 @@ async function executeAgent(context, args) {
333
332
  case "run": {
334
333
  const { options, commandParts } = splitAtDoubleDash(rest);
335
334
  if (commandParts.length === 0) {
336
- 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...>");
337
336
  }
338
337
  let pending = options;
339
338
  const idResult = takeOption(pending, "--id");
@@ -344,12 +343,12 @@ async function executeAgent(context, args) {
344
343
  pending = taskResult.rest;
345
344
  const skipProjectSyncResult = takeFlag(pending, "--skip-project-sync");
346
345
  pending = skipProjectSyncResult.rest;
347
- 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...>");
348
347
  const mode = parseIsolationMode(modeResult.value, false);
349
348
  const id = idResult.value || agentId("agent-run");
350
349
  const taskId = taskResult.value?.trim();
351
350
  if (!taskId) {
352
- 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...>");
353
352
  }
354
353
  await runProjectMainSyncPreflight(context, { disabled: skipProjectSyncResult.value });
355
354
  const createdAt = new Date().toISOString();
@@ -463,7 +462,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
463
462
  pending = allResult.rest;
464
463
  const idResult = takeOption(pending, "--id");
465
464
  pending = idResult.rest;
466
- requireNoExtraArgs(pending, "bun run rig agent cleanup (--id <id> | --all)");
465
+ requireNoExtraArgs(pending, "rig agent cleanup (--id <id> | --all)");
467
466
  if (!allResult.value && !idResult.value) {
468
467
  throw new CliError2("Provide --id <id> or --all.");
469
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) {
@@ -96,19 +97,90 @@ function readRepoConnection(projectRoot) {
96
97
  return {
97
98
  selected,
98
99
  project: typeof record.project === "string" ? record.project : undefined,
99
- linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
100
+ linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
101
+ serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
100
102
  };
101
103
  }
102
104
  function writeRepoConnection(projectRoot, state) {
103
105
  writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
104
106
  }
105
107
 
108
+ // packages/cli/src/commands/_cli-format.ts
109
+ import { log, note } from "@clack/prompts";
110
+ import pc from "picocolors";
111
+ function truncate(value, width) {
112
+ if (value.length <= width)
113
+ return value;
114
+ if (width <= 1)
115
+ return "\u2026";
116
+ return `${value.slice(0, width - 1)}\u2026`;
117
+ }
118
+ function pad(value, width) {
119
+ return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
120
+ }
121
+ function statusColor(status) {
122
+ const normalized = status.toLowerCase();
123
+ if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved"].includes(normalized))
124
+ return pc.green;
125
+ if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
126
+ return pc.red;
127
+ if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
128
+ return pc.cyan;
129
+ if (["ready", "open", "queued", "created", "preparing", "local", "pending"].includes(normalized))
130
+ return pc.yellow;
131
+ return pc.dim;
132
+ }
133
+ function formatStatusPill(status) {
134
+ const label = status || "unknown";
135
+ return statusColor(label)(`\u25CF ${label}`);
136
+ }
137
+ function formatSection(title, subtitle) {
138
+ return `${pc.bold(pc.cyan("\u25C6"))} ${pc.bold(title)}${subtitle ? pc.dim(` \u2014 ${subtitle}`) : ""}`;
139
+ }
140
+ function formatSuccessCard(title, rows = []) {
141
+ 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}`);
142
+ return [formatSection(title), ...body].join(`
143
+ `);
144
+ }
145
+ function formatNextSteps(steps) {
146
+ if (steps.length === 0)
147
+ return [];
148
+ return [pc.bold("Next"), ...steps.map((step) => `${pc.dim("\u203A")} ${step}`)];
149
+ }
150
+ function formatConnectionList(connections) {
151
+ const rows = [["local", { kind: "local", mode: "auto" }], ...Object.entries(connections)];
152
+ const aliasWidth = Math.min(24, Math.max(5, ...rows.map(([alias]) => alias.length)));
153
+ const lines = rows.map(([alias, connection]) => [
154
+ pc.bold(pad(truncate(alias, aliasWidth), aliasWidth)),
155
+ formatStatusPill(connection.kind),
156
+ connection.kind === "remote" ? connection.baseUrl ?? "" : connection.mode ?? "local"
157
+ ].join(" "));
158
+ 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(`
159
+ `);
160
+ }
161
+ function formatConnectionStatus(selected, connections) {
162
+ const connection = selected === "local" ? { kind: "local", mode: "auto" } : connections[selected];
163
+ const target = !connection ? "not configured" : connection.kind === "remote" ? connection.baseUrl : "local";
164
+ return [
165
+ formatSection("Rig server", "selected for this repo"),
166
+ `${pc.dim("\u2502")} ${pc.dim("selected ")} ${pc.bold(selected)}`,
167
+ `${pc.dim("\u2502")} ${pc.dim("kind ")} ${formatStatusPill(connection?.kind ?? "unknown")}`,
168
+ `${pc.dim("\u2502")} ${pc.dim("target ")} ${target ?? "not configured"}`,
169
+ "",
170
+ ...formatNextSteps(["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
171
+ ].join(`
172
+ `);
173
+ }
174
+
106
175
  // packages/cli/src/commands/connect.ts
107
- function parseConnection(alias, value) {
176
+ function usageName(options) {
177
+ return `rig ${options.group}`;
178
+ }
179
+ function parseConnection(alias, value, options) {
108
180
  if (alias === "local" && !value)
109
181
  return { kind: "local", mode: "auto" };
110
182
  if (!value)
111
- throw new CliError2("Missing remote server URL. Usage: rig connect add <alias> <url>", 1);
183
+ throw new CliError2(`Missing remote server URL. Usage: ${usageName(options)} add <alias> <url>`, 1);
112
184
  let parsed;
113
185
  try {
114
186
  parsed = new URL(value);
@@ -127,54 +199,90 @@ function printJsonOrText(context, payload, text) {
127
199
  console.log(text);
128
200
  }
129
201
  }
130
- async function executeConnect(context, args) {
202
+ async function promptForConnectionAlias(context) {
203
+ const state = readGlobalConnections();
204
+ const repo = readRepoConnection(context.projectRoot);
205
+ const options = [
206
+ { value: "local", label: "local", hint: "Use/start a local Rig server" },
207
+ ...Object.entries(state.connections).map(([alias, connection]) => ({
208
+ value: alias,
209
+ label: alias,
210
+ hint: connection.kind === "remote" ? connection.baseUrl : "local"
211
+ }))
212
+ ].filter((option, index, all) => all.findIndex((candidate) => candidate.value === option.value) === index);
213
+ const answer = await select({
214
+ message: "Select Rig server for this repo",
215
+ initialValue: repo?.selected ?? "local",
216
+ options
217
+ });
218
+ if (isCancel(answer)) {
219
+ cancel("No server selected.");
220
+ throw new CliError2("No server selected.", 3);
221
+ }
222
+ return String(answer);
223
+ }
224
+ async function executeConnectionCommand(context, args, options) {
131
225
  const [command, ...rest] = args;
132
226
  switch (command ?? "status") {
133
227
  case "list": {
134
- requireNoExtraArgs(rest, "rig connect list");
228
+ requireNoExtraArgs(rest, `${usageName(options)} list`);
135
229
  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 };
230
+ printJsonOrText(context, state, formatConnectionList(state.connections));
231
+ return { ok: true, group: options.group, command: "list", details: state };
139
232
  }
140
233
  case "add": {
141
234
  const [alias, url, ...extra] = rest;
142
235
  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);
236
+ throw new CliError2(`Missing alias. Usage: ${usageName(options)} add <alias> <url>`, 1);
237
+ requireNoExtraArgs(extra, `${usageName(options)} add <alias> <url>`);
238
+ const connection = parseConnection(alias, url, options);
146
239
  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 } };
240
+ printJsonOrText(context, { alias, connection }, formatSuccessCard("Rig server saved", [
241
+ ["alias", alias],
242
+ ["target", connection.kind === "remote" ? connection.baseUrl : "local"],
243
+ ["next", `${usageName(options)} use ${alias}`]
244
+ ]));
245
+ return { ok: true, group: options.group, command: "add", details: { alias, connection, count: Object.keys(state.connections).length } };
149
246
  }
150
247
  case "use": {
151
- const [alias, ...extra] = rest;
248
+ let [alias, ...extra] = rest;
249
+ requireNoExtraArgs(extra, `${usageName(options)} use <alias|local>`);
250
+ if (!alias && options.interactiveUse && context.outputMode === "text" && process.stdin.isTTY) {
251
+ alias = await promptForConnectionAlias(context);
252
+ }
152
253
  if (!alias)
153
- throw new CliError2("Missing alias. Usage: rig connect use <alias|local>", 1);
154
- requireNoExtraArgs(extra, "rig connect use <alias|local>");
254
+ throw new CliError2(`Missing alias. Usage: ${usageName(options)} use <alias|local>`, 1);
155
255
  if (alias !== "local") {
156
256
  const state = readGlobalConnections();
157
257
  if (!state.connections[alias])
158
- throw new CliError2(`Unknown Rig connection: ${alias}`, 1);
258
+ throw new CliError2(`Unknown Rig server: ${alias}`, 1);
159
259
  }
160
260
  const repoState = { selected: alias, linkedAt: new Date().toISOString() };
161
261
  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 };
262
+ printJsonOrText(context, repoState, formatSuccessCard("Rig server selected", [
263
+ ["selected", alias],
264
+ ["scope", "this repo"],
265
+ ["next", "rig task list"]
266
+ ]));
267
+ return { ok: true, group: options.group, command: "use", details: repoState };
164
268
  }
165
269
  case "status": {
166
- requireNoExtraArgs(rest, "rig connect status");
270
+ requireNoExtraArgs(rest, `${usageName(options)} status`);
167
271
  const repo = readRepoConnection(context.projectRoot);
168
272
  const global = readGlobalConnections();
169
273
  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 };
274
+ printJsonOrText(context, details, formatConnectionStatus(details.selected, global.connections));
275
+ return { ok: true, group: options.group, command: "status", details };
172
276
  }
173
277
  default:
174
- throw new CliError2(`Unknown connect command: ${String(command)}
175
- Usage: rig connect <list|add|use|status>`, 1);
278
+ throw new CliError2(`Unknown ${options.group} command: ${String(command)}
279
+ Usage: ${usageName(options)} <list|add|use|status>`, 1);
176
280
  }
177
281
  }
282
+ async function executeConnect(context, args) {
283
+ return executeConnectionCommand(context, args, { group: "connect" });
284
+ }
178
285
  export {
286
+ executeConnectionCommand,
179
287
  executeConnect
180
288
  };
@@ -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");