@h-rig/cli 0.0.6-alpha.35 → 0.0.6-alpha.37

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 CHANGED
@@ -531,7 +531,8 @@ import { loadPolicy as loadPolicy3 } from "@rig/runtime/control-plane/runtime/gu
531
531
  // packages/cli/src/commands.ts
532
532
  init_runner();
533
533
  import {
534
- existsSync as existsSync16
534
+ existsSync as existsSync16,
535
+ readFileSync as readFileSync12
535
536
  } from "fs";
536
537
  import { resolve as resolve25 } from "path";
537
538
  import { readBuildConfig } from "@rig/runtime/build-time-config";
@@ -6305,6 +6306,7 @@ ${acceptance}` : null,
6305
6306
  ${sourceContractLines.join(`
6306
6307
  `)}` : null,
6307
6308
  scopeText || renderSourceScopeValidation(sourceTask, sourceValidation) || null,
6309
+ readPriorPrProgressForPrompt(input.projectRoot, input.taskId),
6308
6310
  initialPrompt ? `Additional operator guidance:
6309
6311
  ${initialPrompt}` : null,
6310
6312
  providerLines.length > 0 ? `Provider-specific runtime tooling:
@@ -6314,6 +6316,26 @@ ${providerLines.join(" ")}` : null,
6314
6316
 
6315
6317
  `);
6316
6318
  }
6319
+ function readPriorPrProgressForPrompt(projectRoot, taskId) {
6320
+ for (const candidate of [
6321
+ resolve20(projectRoot, ".worktrees", taskId, "artifacts", taskId, "pr-state.json"),
6322
+ resolve20(projectRoot, "artifacts", taskId, "pr-state.json")
6323
+ ]) {
6324
+ try {
6325
+ const raw = JSON.parse(readFileSync8(candidate, "utf8"));
6326
+ const entries = Array.isArray(raw) ? raw : [raw];
6327
+ const first = entries.find((entry) => entry && typeof entry === "object" && typeof entry.url === "string");
6328
+ if (!first)
6329
+ continue;
6330
+ return [
6331
+ `Prior progress exists for this task: PR ${first.url}${first.branch ? ` (branch ${first.branch})` : ""} was opened by an earlier run.`,
6332
+ "Check its current state first (diff, checks, review). If the work is already complete and checks are green,",
6333
+ "run `rig-agent completion-verification` to merge and close instead of re-implementing anything."
6334
+ ].join(" ");
6335
+ } catch {}
6336
+ }
6337
+ return null;
6338
+ }
6317
6339
  function firstPromptString(...values) {
6318
6340
  for (const value of values) {
6319
6341
  const trimmed = typeof value === "string" ? value.trim() : "";
@@ -6398,7 +6420,24 @@ async function executeInspect(context, args) {
6398
6420
  const requiredTask = requireTask(task, "rig inspect logs --task <task-id>");
6399
6421
  const latestRun = listAuthorityRuns2(context.projectRoot).map((entry) => readAuthorityRun3(context.projectRoot, entry.runId)).filter((run) => Boolean(run)).filter((run) => run.taskId === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
6400
6422
  if (!latestRun) {
6401
- throw new CliError2(`No runs found for ${requiredTask}.`);
6423
+ const serverRuns = await listRunsViaServer(context, { limit: 200 }).catch(() => []);
6424
+ const serverRun = serverRuns.filter((run) => String(run.taskId ?? "") === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
6425
+ if (!serverRun || typeof serverRun.runId !== "string") {
6426
+ throw new CliError2(`No runs found for ${requiredTask} (local or on the selected server).`);
6427
+ }
6428
+ const page = await getRunLogsViaServer(context, serverRun.runId, { limit: 500 });
6429
+ const entries = Array.isArray(page.entries) ? page.entries : [];
6430
+ if (context.outputMode === "text") {
6431
+ for (const entry of entries) {
6432
+ const record = entry && typeof entry === "object" ? entry : {};
6433
+ const title = String(record.title ?? "");
6434
+ const detail = String(record.detail ?? "");
6435
+ console.log([title, detail].filter(Boolean).join(" \u2014 "));
6436
+ }
6437
+ if (entries.length === 0)
6438
+ console.log(`(no log entries for run ${serverRun.runId})`);
6439
+ }
6440
+ return { ok: true, group: "inspect", command, details: { task: requiredTask, runId: serverRun.runId, source: "server", entries } };
6402
6441
  }
6403
6442
  const logsPath = resolve21(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
6404
6443
  if (!existsSync13(logsPath)) {
@@ -7683,7 +7722,38 @@ function parseWsPayload(message2) {
7683
7722
  return JSON.parse(message2.data);
7684
7723
  return JSON.parse(Buffer.from(message2.data).toString("utf8"));
7685
7724
  }
7686
- async function connectWorkerStream(options, ctx, state) {
7725
+ var BRIDGE_LOCAL_COMMANDS = new Set(["detach", "quit", "q", "stop"]);
7726
+ function registerDaemonCommandsNatively(pi, options, ctx, state, commands, registered) {
7727
+ for (const command of commands) {
7728
+ const record = recordOf(command);
7729
+ const name = typeof record?.name === "string" ? record.name : "";
7730
+ if (!name || registered.has(name) || BRIDGE_LOCAL_COMMANDS.has(name))
7731
+ continue;
7732
+ registered.add(name);
7733
+ const description = typeof record?.description === "string" ? record.description : undefined;
7734
+ const source = typeof record?.source === "string" ? record.source : "worker";
7735
+ try {
7736
+ pi.registerCommand(name, {
7737
+ description: `[worker ${source}] ${description ?? ""}`.trim(),
7738
+ handler: async (args) => {
7739
+ const text2 = `/${name}${args ? ` ${args}` : ""}`;
7740
+ appendTranscript(state, "You", text2);
7741
+ try {
7742
+ const result = await runRunPiCommandViaServer(options.context, options.runId, text2);
7743
+ const message2 = typeof result.message === "string" ? result.message : "worker command accepted";
7744
+ appendTranscript(state, "System", message2);
7745
+ if (state.nativeStream)
7746
+ ctx.ui.notify(message2, "info");
7747
+ } catch (error) {
7748
+ reportBridgeError(ctx, state, error instanceof Error ? error.message : String(error));
7749
+ }
7750
+ updatePiUi(ctx, state);
7751
+ }
7752
+ });
7753
+ } catch {}
7754
+ }
7755
+ }
7756
+ async function connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands) {
7687
7757
  const ready = await waitForWorkerReady(options, ctx, state);
7688
7758
  if (!ready)
7689
7759
  return;
@@ -7738,6 +7808,7 @@ async function connectWorkerStream(options, ctx, state) {
7738
7808
  const record = recordOf(command);
7739
7809
  return typeof record?.name === "string" ? [`/${record.name}`] : [];
7740
7810
  });
7811
+ registerDaemonCommandsNatively(pi, options, ctx, state, commands, registeredDaemonCommands);
7741
7812
  catchupDone = true;
7742
7813
  for (const payload of buffered.splice(0))
7743
7814
  applyEnvelope(ctx, state, payload);
@@ -7865,6 +7936,7 @@ function createRigWorkerPiBridgeExtension(options) {
7865
7936
  };
7866
7937
  if (options.initialMessageSent)
7867
7938
  appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
7939
+ const registeredDaemonCommands = new Set;
7868
7940
  let nativePiUiContextAvailable = false;
7869
7941
  pi.on("user_bash", (event) => {
7870
7942
  state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
@@ -7897,7 +7969,7 @@ function createRigWorkerPiBridgeExtension(options) {
7897
7969
  });
7898
7970
  return { consume: true };
7899
7971
  });
7900
- connectWorkerStream(options, ctx, state).catch((error) => {
7972
+ connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands).catch((error) => {
7901
7973
  appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
7902
7974
  updatePiUi(ctx, state);
7903
7975
  });
@@ -7944,8 +8016,6 @@ async function attachRunBundledPiFrontend(context, input) {
7944
8016
  "--no-tools",
7945
8017
  "--no-builtin-tools",
7946
8018
  "--no-skills",
7947
- "--no-prompt-templates",
7948
- "--no-themes",
7949
8019
  "--no-context-files",
7950
8020
  "--no-approve"
7951
8021
  ], {
@@ -8490,6 +8560,7 @@ async function executeRun(context, args) {
8490
8560
 
8491
8561
  // packages/cli/src/commands/server.ts
8492
8562
  init_runner();
8563
+ import { resolveRigServerCommand } from "@rig/runtime/local-server";
8493
8564
  async function executeServer(context, args, options) {
8494
8565
  const [command = "status", ...rest] = args;
8495
8566
  if (["status", "list", "add", "use"].includes(command)) {
@@ -8507,7 +8578,8 @@ async function executeServer(context, args, options) {
8507
8578
  const authTokenResult = takeOption(pending, "--auth-token");
8508
8579
  pending = authTokenResult.rest;
8509
8580
  requireNoExtraArgs(pending, "rig server start [--host <host>] [--port <n>] [--poll-ms <n>] [--auth-token <token>]");
8510
- const commandParts = ["rig-server", "start"];
8581
+ const serverCommand = resolveRigServerCommand(context.projectRoot);
8582
+ const commandParts = [serverCommand.command, ...serverCommand.commandArgs, "start"];
8511
8583
  if (hostResult.value) {
8512
8584
  commandParts.push("--host", hostResult.value);
8513
8585
  }
@@ -8530,7 +8602,8 @@ async function executeServer(context, args, options) {
8530
8602
  const eventResult = takeOption(pending, "--event");
8531
8603
  pending = eventResult.rest;
8532
8604
  requireNoExtraArgs(pending, "rig server notify-test [--event <type>]");
8533
- const commandParts = ["rig-server", "notify-test"];
8605
+ const serverCommand = resolveRigServerCommand(context.projectRoot);
8606
+ const commandParts = [serverCommand.command, ...serverCommand.commandArgs, "notify-test"];
8534
8607
  if (eventResult.value) {
8535
8608
  commandParts.push("--event", eventResult.value);
8536
8609
  }
@@ -8664,52 +8737,50 @@ import { intro as intro3, log as log4, note as note4, outro as outro3 } from "@c
8664
8737
  import pc4 from "picocolors";
8665
8738
  var TOP_LEVEL_SECTIONS = [
8666
8739
  {
8667
- title: "Server",
8668
- subtitle: "choose the local or remote Rig server that owns this repo",
8740
+ title: "Start here",
8741
+ subtitle: "one-time setup for a repo",
8669
8742
  commands: [
8670
- { command: "rig server status", description: "Show the selected local/remote server for this repo." },
8671
- { command: "rig server use local", description: "Switch this repo back to the local Rig server." },
8672
- { command: "rig server add <alias> <url>", description: "Save a remote Rig server alias." },
8673
- { command: "rig server use <alias>", description: "Switch this repo to a saved remote server." },
8674
- { command: "rig server list", description: "Show saved server aliases, including local." }
8743
+ { command: "rig init", description: "Wizard: config, GitHub auth, task source, server, Pi wiring." },
8744
+ { command: "rig doctor", description: "Check every part of the wiring \u2014 run this when anything feels off." },
8745
+ { command: "rig server use <alias|local>", description: "Pick which Rig server owns this repo (`rig server list` to see them)." }
8675
8746
  ]
8676
8747
  },
8677
8748
  {
8678
- title: "Tasks",
8679
- subtitle: "find work, inspect it, and submit Pi-backed workers",
8749
+ title: "Work",
8750
+ subtitle: "find a task and put an agent on it",
8680
8751
  commands: [
8681
- { command: "rig task list", description: "List tasks from the selected task source/server." },
8682
- { command: "rig task next", description: "Show the next matching task as a selected-task card." },
8683
- { command: "rig task show <id>", description: "Show a human task summary; add --raw or --json for the full payload." },
8684
- { command: "rig task run <id|--next> [--detach]", description: "Submit a task run; interactive mode follows with bundled Pi." }
8752
+ { command: "rig task list", description: "What's on the board (from the selected source/server)." },
8753
+ { command: "rig task next", description: "The next ready task, as a card." },
8754
+ { command: "rig task run --next", description: "Dispatch an agent; interactive mode opens the native Pi session." },
8755
+ { command: "rig task run <id> --detach", description: "Fire-and-forget a specific task." }
8685
8756
  ]
8686
8757
  },
8687
8758
  {
8688
- title: "Runs",
8689
- subtitle: "observe, attach to, and stop live or recent runs",
8759
+ title: "Watch & steer",
8760
+ subtitle: "live runs are observable and steerable, attached or not",
8690
8761
  commands: [
8691
- { command: "rig run list", description: "List recent runs from the selected server or local state." },
8692
- { command: "rig run show <id>", description: "Show a human run summary; add --raw or --json for the full payload." },
8693
- { command: "rig run attach <id> --follow", description: "Open the native bundled Pi live view for a worker run." },
8694
- { command: "rig run stop <id>", description: "Request cancellation for a running worker." }
8762
+ { command: "rig run status", description: "Active and recent runs at a glance." },
8763
+ { command: "rig run attach <id> --follow", description: "Join the live Pi session (worker keeps running if you /detach)." },
8764
+ { command: "rig run steer <id> -m <text>", description: "Drop a message into a live worker without attaching." },
8765
+ { command: "rig run stop <id>", description: "Cancel a running worker." }
8695
8766
  ]
8696
8767
  },
8697
8768
  {
8698
- title: "Review / inbox",
8699
- subtitle: "clear blocked runs and configure completion review",
8769
+ title: "Unblock & gate",
8770
+ subtitle: "answer what workers ask; control what merges",
8700
8771
  commands: [
8701
- { command: "rig inbox approvals", description: "List pending approval requests from local/server run state." },
8702
- { command: "rig inbox inputs", description: "List pending user-input requests from local/server run state." },
8703
- { command: "rig review show|set", description: "Inspect or change the review gate policy." }
8772
+ { command: "rig inbox approvals", description: "Approvals workers are waiting on (then `rig inbox approve \u2026`)." },
8773
+ { command: "rig inbox inputs", description: "Questions workers asked (then `rig inbox respond \u2026`)." },
8774
+ { command: "rig review show|set", description: "The completion review gate (Greptile is mandatory on merges)." }
8704
8775
  ]
8705
8776
  },
8706
8777
  {
8707
- title: "Health / setup",
8708
- subtitle: "bootstrap and diagnose the repo/server/GitHub/Pi path",
8778
+ title: "Extend",
8779
+ subtitle: "more Pi, more plugins",
8709
8780
  commands: [
8710
- { command: "rig init", description: "Interactive setup: config, GitHub auth, task source, server, checkout, Pi." },
8711
- { command: "rig doctor", description: "Diagnose project/server/GitHub/task/Pi wiring." },
8712
- { command: "rig github auth status", description: "Show GitHub auth state on the selected Rig server." }
8781
+ { command: "rig pi search <term>", description: "Discover community Pi extensions on npm." },
8782
+ { command: "rig pi add <pkg>", description: "Add a Pi extension to this project (workers pick it up too)." },
8783
+ { command: "rig plugin list", description: "What the rig.config.ts plugins contribute." }
8713
8784
  ]
8714
8785
  }
8715
8786
  ];
@@ -8917,6 +8988,25 @@ var ALL_GROUPS = [...PRIMARY_GROUPS, ...ADVANCED_GROUPS];
8917
8988
  function heading(title) {
8918
8989
  return pc4.bold(pc4.cyan(title));
8919
8990
  }
8991
+ function renderRigBanner(version) {
8992
+ const m = (s) => pc4.bold(pc4.magenta(s));
8993
+ const c = (s) => pc4.bold(pc4.cyan(s));
8994
+ const y = (s) => pc4.yellow(s);
8995
+ const d = (s) => pc4.dim(s);
8996
+ const lines = [
8997
+ m(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + d(" \u2591\u2592\u2593\u2588 "),
8998
+ m(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ") + d("\u2593\u2592\u2591"),
8999
+ c(" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2557") + d(" \u2591\u2592"),
9000
+ c(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551") + d(" \u2588\u2593\u2591"),
9001
+ y(" \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + d(" \u2592\u2591"),
9002
+ y(" \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D "),
9003
+ "",
9004
+ ` ${c("\u25E2\u25E4")} ${pc4.bold("the control rig for autonomous coding agents")} ${m("//")} ${d("you are the operator")}`,
9005
+ version ? ` ${d(`v${version} \xB7 jack in: rig task run --next`)}` : ` ${d("jack in: rig task run --next")}`
9006
+ ];
9007
+ return lines.join(`
9008
+ `);
9009
+ }
8920
9010
  function commandLine(command, description) {
8921
9011
  const commandColumn = command.length >= 38 ? `${command} ` : command.padEnd(38);
8922
9012
  return `${pc4.dim("\u2502")} ${pc4.bold(commandColumn)} ${description}`;
@@ -8990,12 +9080,25 @@ function renderGroupHelp(groupName) {
8990
9080
  function shouldUseClackOutput2() {
8991
9081
  return Boolean(process.stdout.isTTY) && process.env.RIG_CLI_PLAIN_HELP !== "1";
8992
9082
  }
8993
- function printTopLevelHelp() {
9083
+ function printTopLevelHelp(state = {}) {
8994
9084
  if (!shouldUseClackOutput2()) {
8995
9085
  console.log(renderTopLevelHelp());
8996
9086
  return;
8997
9087
  }
8998
- intro3("rig");
9088
+ console.log(renderRigBanner(state.version));
9089
+ console.log("");
9090
+ if (state.projectInitialized === false) {
9091
+ intro3("no rig project in this directory");
9092
+ note4([
9093
+ commandLine("rig init", "Set this repo up: config, GitHub auth, task source, server, Pi."),
9094
+ commandLine("rig init --yes", "Same, non-interactive, sensible defaults."),
9095
+ commandLine("rig doctor", "Already initialized somewhere else? Check the wiring.")
9096
+ ].join(`
9097
+ `), "Get started");
9098
+ outro3("After init: rig task run --next puts an agent on your next task.");
9099
+ return;
9100
+ }
9101
+ intro3(state.selectedServer ? `server: ${state.selectedServer}` : "rig");
8999
9102
  for (const section of TOP_LEVEL_SECTIONS) {
9000
9103
  note4(renderCommandBlock(section.commands), `${section.title} \u2014 ${section.subtitle}`);
9001
9104
  }
@@ -9006,7 +9109,7 @@ function printTopLevelHelp() {
9006
9109
  commandLine("--dry-run", "Print the command plan without mutating state.")
9007
9110
  ].join(`
9008
9111
  `), "Global options");
9009
- outro3("Server \u2192 task \u2192 run \u2192 inbox/review.");
9112
+ outro3("init \u2192 task run \u2192 watch/steer \u2192 inbox/review \u2192 merged.");
9010
9113
  }
9011
9114
  function printAdvancedHelp() {
9012
9115
  if (!shouldUseClackOutput2()) {
@@ -11872,9 +11975,23 @@ function isHelpArg(arg) {
11872
11975
  function helpText() {
11873
11976
  return renderTopLevelHelp();
11874
11977
  }
11978
+ function resolveTopLevelLaunchState(context) {
11979
+ const projectInitialized = hasInitializedRigProject(context.projectRoot);
11980
+ let selectedServer = null;
11981
+ if (projectInitialized) {
11982
+ try {
11983
+ const statePath = resolve25(context.projectRoot, ".rig", "state", "connection.json");
11984
+ if (existsSync16(statePath)) {
11985
+ const parsed = JSON.parse(readFileSync12(statePath, "utf-8"));
11986
+ selectedServer = parsed.remoteUrl || parsed.url || parsed.selected || null;
11987
+ }
11988
+ } catch {}
11989
+ }
11990
+ return { projectInitialized, selectedServer, version: process.env.RIG_CLI_VERSION || readBuildConfig().RIG_CLI_VERSION || undefined };
11991
+ }
11875
11992
  async function execute(context, args) {
11876
11993
  if (args.length === 0) {
11877
- printTopLevelHelp();
11994
+ printTopLevelHelp(resolveTopLevelLaunchState(context));
11878
11995
  return { ok: true, group: "help", command: "show" };
11879
11996
  }
11880
11997
  const [first, ...rest] = args;
@@ -4,52 +4,50 @@ import { intro, log, note, outro } from "@clack/prompts";
4
4
  import pc from "picocolors";
5
5
  var TOP_LEVEL_SECTIONS = [
6
6
  {
7
- title: "Server",
8
- subtitle: "choose the local or remote Rig server that owns this repo",
7
+ title: "Start here",
8
+ subtitle: "one-time setup for a repo",
9
9
  commands: [
10
- { command: "rig server status", description: "Show the selected local/remote server for this repo." },
11
- { command: "rig server use local", description: "Switch this repo back to the local Rig server." },
12
- { command: "rig server add <alias> <url>", description: "Save a remote Rig server alias." },
13
- { command: "rig server use <alias>", description: "Switch this repo to a saved remote server." },
14
- { command: "rig server list", description: "Show saved server aliases, including local." }
10
+ { command: "rig init", description: "Wizard: config, GitHub auth, task source, server, Pi wiring." },
11
+ { command: "rig doctor", description: "Check every part of the wiring \u2014 run this when anything feels off." },
12
+ { command: "rig server use <alias|local>", description: "Pick which Rig server owns this repo (`rig server list` to see them)." }
15
13
  ]
16
14
  },
17
15
  {
18
- title: "Tasks",
19
- subtitle: "find work, inspect it, and submit Pi-backed workers",
16
+ title: "Work",
17
+ subtitle: "find a task and put an agent on it",
20
18
  commands: [
21
- { command: "rig task list", description: "List tasks from the selected task source/server." },
22
- { command: "rig task next", description: "Show the next matching task as a selected-task card." },
23
- { command: "rig task show <id>", description: "Show a human task summary; add --raw or --json for the full payload." },
24
- { command: "rig task run <id|--next> [--detach]", description: "Submit a task run; interactive mode follows with bundled Pi." }
19
+ { command: "rig task list", description: "What's on the board (from the selected source/server)." },
20
+ { command: "rig task next", description: "The next ready task, as a card." },
21
+ { command: "rig task run --next", description: "Dispatch an agent; interactive mode opens the native Pi session." },
22
+ { command: "rig task run <id> --detach", description: "Fire-and-forget a specific task." }
25
23
  ]
26
24
  },
27
25
  {
28
- title: "Runs",
29
- subtitle: "observe, attach to, and stop live or recent runs",
26
+ title: "Watch & steer",
27
+ subtitle: "live runs are observable and steerable, attached or not",
30
28
  commands: [
31
- { command: "rig run list", description: "List recent runs from the selected server or local state." },
32
- { command: "rig run show <id>", description: "Show a human run summary; add --raw or --json for the full payload." },
33
- { command: "rig run attach <id> --follow", description: "Open the native bundled Pi live view for a worker run." },
34
- { command: "rig run stop <id>", description: "Request cancellation for a running worker." }
29
+ { command: "rig run status", description: "Active and recent runs at a glance." },
30
+ { command: "rig run attach <id> --follow", description: "Join the live Pi session (worker keeps running if you /detach)." },
31
+ { command: "rig run steer <id> -m <text>", description: "Drop a message into a live worker without attaching." },
32
+ { command: "rig run stop <id>", description: "Cancel a running worker." }
35
33
  ]
36
34
  },
37
35
  {
38
- title: "Review / inbox",
39
- subtitle: "clear blocked runs and configure completion review",
36
+ title: "Unblock & gate",
37
+ subtitle: "answer what workers ask; control what merges",
40
38
  commands: [
41
- { command: "rig inbox approvals", description: "List pending approval requests from local/server run state." },
42
- { command: "rig inbox inputs", description: "List pending user-input requests from local/server run state." },
43
- { command: "rig review show|set", description: "Inspect or change the review gate policy." }
39
+ { command: "rig inbox approvals", description: "Approvals workers are waiting on (then `rig inbox approve \u2026`)." },
40
+ { command: "rig inbox inputs", description: "Questions workers asked (then `rig inbox respond \u2026`)." },
41
+ { command: "rig review show|set", description: "The completion review gate (Greptile is mandatory on merges)." }
44
42
  ]
45
43
  },
46
44
  {
47
- title: "Health / setup",
48
- subtitle: "bootstrap and diagnose the repo/server/GitHub/Pi path",
45
+ title: "Extend",
46
+ subtitle: "more Pi, more plugins",
49
47
  commands: [
50
- { command: "rig init", description: "Interactive setup: config, GitHub auth, task source, server, checkout, Pi." },
51
- { command: "rig doctor", description: "Diagnose project/server/GitHub/task/Pi wiring." },
52
- { command: "rig github auth status", description: "Show GitHub auth state on the selected Rig server." }
48
+ { command: "rig pi search <term>", description: "Discover community Pi extensions on npm." },
49
+ { command: "rig pi add <pkg>", description: "Add a Pi extension to this project (workers pick it up too)." },
50
+ { command: "rig plugin list", description: "What the rig.config.ts plugins contribute." }
53
51
  ]
54
52
  }
55
53
  ];
@@ -257,6 +255,25 @@ var ALL_GROUPS = [...PRIMARY_GROUPS, ...ADVANCED_GROUPS];
257
255
  function heading(title) {
258
256
  return pc.bold(pc.cyan(title));
259
257
  }
258
+ function renderRigBanner(version) {
259
+ const m = (s) => pc.bold(pc.magenta(s));
260
+ const c = (s) => pc.bold(pc.cyan(s));
261
+ const y = (s) => pc.yellow(s);
262
+ const d = (s) => pc.dim(s);
263
+ const lines = [
264
+ m(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + d(" \u2591\u2592\u2593\u2588 "),
265
+ m(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ") + d("\u2593\u2592\u2591"),
266
+ c(" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2557") + d(" \u2591\u2592"),
267
+ c(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551") + d(" \u2588\u2593\u2591"),
268
+ y(" \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + d(" \u2592\u2591"),
269
+ y(" \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D "),
270
+ "",
271
+ ` ${c("\u25E2\u25E4")} ${pc.bold("the control rig for autonomous coding agents")} ${m("//")} ${d("you are the operator")}`,
272
+ version ? ` ${d(`v${version} \xB7 jack in: rig task run --next`)}` : ` ${d("jack in: rig task run --next")}`
273
+ ];
274
+ return lines.join(`
275
+ `);
276
+ }
260
277
  function commandLine(command, description) {
261
278
  const commandColumn = command.length >= 38 ? `${command} ` : command.padEnd(38);
262
279
  return `${pc.dim("\u2502")} ${pc.bold(commandColumn)} ${description}`;
@@ -333,12 +350,25 @@ function listHelpGroups() {
333
350
  function shouldUseClackOutput() {
334
351
  return Boolean(process.stdout.isTTY) && process.env.RIG_CLI_PLAIN_HELP !== "1";
335
352
  }
336
- function printTopLevelHelp() {
353
+ function printTopLevelHelp(state = {}) {
337
354
  if (!shouldUseClackOutput()) {
338
355
  console.log(renderTopLevelHelp());
339
356
  return;
340
357
  }
341
- intro("rig");
358
+ console.log(renderRigBanner(state.version));
359
+ console.log("");
360
+ if (state.projectInitialized === false) {
361
+ intro("no rig project in this directory");
362
+ note([
363
+ commandLine("rig init", "Set this repo up: config, GitHub auth, task source, server, Pi."),
364
+ commandLine("rig init --yes", "Same, non-interactive, sensible defaults."),
365
+ commandLine("rig doctor", "Already initialized somewhere else? Check the wiring.")
366
+ ].join(`
367
+ `), "Get started");
368
+ outro("After init: rig task run --next puts an agent on your next task.");
369
+ return;
370
+ }
371
+ intro(state.selectedServer ? `server: ${state.selectedServer}` : "rig");
342
372
  for (const section of TOP_LEVEL_SECTIONS) {
343
373
  note(renderCommandBlock(section.commands), `${section.title} \u2014 ${section.subtitle}`);
344
374
  }
@@ -349,7 +379,7 @@ function printTopLevelHelp() {
349
379
  commandLine("--dry-run", "Print the command plan without mutating state.")
350
380
  ].join(`
351
381
  `), "Global options");
352
- outro("Server \u2192 task \u2192 run \u2192 inbox/review.");
382
+ outro("init \u2192 task run \u2192 watch/steer \u2192 inbox/review \u2192 merged.");
353
383
  }
354
384
  function printAdvancedHelp() {
355
385
  if (!shouldUseClackOutput()) {
@@ -393,6 +423,7 @@ function printGroupHelpDocument(groupName) {
393
423
  }
394
424
  export {
395
425
  renderTopLevelHelp,
426
+ renderRigBanner,
396
427
  renderGroupHelp,
397
428
  renderAdvancedHelp,
398
429
  printTopLevelHelp,
@@ -802,7 +802,38 @@ function parseWsPayload(message) {
802
802
  return JSON.parse(message.data);
803
803
  return JSON.parse(Buffer.from(message.data).toString("utf8"));
804
804
  }
805
- async function connectWorkerStream(options, ctx, state) {
805
+ var BRIDGE_LOCAL_COMMANDS = new Set(["detach", "quit", "q", "stop"]);
806
+ function registerDaemonCommandsNatively(pi, options, ctx, state, commands, registered) {
807
+ for (const command of commands) {
808
+ const record = recordOf(command);
809
+ const name = typeof record?.name === "string" ? record.name : "";
810
+ if (!name || registered.has(name) || BRIDGE_LOCAL_COMMANDS.has(name))
811
+ continue;
812
+ registered.add(name);
813
+ const description = typeof record?.description === "string" ? record.description : undefined;
814
+ const source = typeof record?.source === "string" ? record.source : "worker";
815
+ try {
816
+ pi.registerCommand(name, {
817
+ description: `[worker ${source}] ${description ?? ""}`.trim(),
818
+ handler: async (args) => {
819
+ const text = `/${name}${args ? ` ${args}` : ""}`;
820
+ appendTranscript(state, "You", text);
821
+ try {
822
+ const result = await runRunPiCommandViaServer(options.context, options.runId, text);
823
+ const message = typeof result.message === "string" ? result.message : "worker command accepted";
824
+ appendTranscript(state, "System", message);
825
+ if (state.nativeStream)
826
+ ctx.ui.notify(message, "info");
827
+ } catch (error) {
828
+ reportBridgeError(ctx, state, error instanceof Error ? error.message : String(error));
829
+ }
830
+ updatePiUi(ctx, state);
831
+ }
832
+ });
833
+ } catch {}
834
+ }
835
+ }
836
+ async function connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands) {
806
837
  const ready = await waitForWorkerReady(options, ctx, state);
807
838
  if (!ready)
808
839
  return;
@@ -857,6 +888,7 @@ async function connectWorkerStream(options, ctx, state) {
857
888
  const record = recordOf(command);
858
889
  return typeof record?.name === "string" ? [`/${record.name}`] : [];
859
890
  });
891
+ registerDaemonCommandsNatively(pi, options, ctx, state, commands, registeredDaemonCommands);
860
892
  catchupDone = true;
861
893
  for (const payload of buffered.splice(0))
862
894
  applyEnvelope(ctx, state, payload);
@@ -984,6 +1016,7 @@ function createRigWorkerPiBridgeExtension(options) {
984
1016
  };
985
1017
  if (options.initialMessageSent)
986
1018
  appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
1019
+ const registeredDaemonCommands = new Set;
987
1020
  let nativePiUiContextAvailable = false;
988
1021
  pi.on("user_bash", (event) => {
989
1022
  state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
@@ -1016,7 +1049,7 @@ function createRigWorkerPiBridgeExtension(options) {
1016
1049
  });
1017
1050
  return { consume: true };
1018
1051
  });
1019
- connectWorkerStream(options, ctx, state).catch((error) => {
1052
+ connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands).catch((error) => {
1020
1053
  appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
1021
1054
  updatePiUi(ctx, state);
1022
1055
  });
@@ -1063,8 +1096,6 @@ async function attachRunBundledPiFrontend(context, input) {
1063
1096
  "--no-tools",
1064
1097
  "--no-builtin-tools",
1065
1098
  "--no-skills",
1066
- "--no-prompt-templates",
1067
- "--no-themes",
1068
1099
  "--no-context-files",
1069
1100
  "--no-approve"
1070
1101
  ], {
@@ -576,7 +576,38 @@ function parseWsPayload(message) {
576
576
  return JSON.parse(message.data);
577
577
  return JSON.parse(Buffer.from(message.data).toString("utf8"));
578
578
  }
579
- async function connectWorkerStream(options, ctx, state) {
579
+ var BRIDGE_LOCAL_COMMANDS = new Set(["detach", "quit", "q", "stop"]);
580
+ function registerDaemonCommandsNatively(pi, options, ctx, state, commands, registered) {
581
+ for (const command of commands) {
582
+ const record = recordOf(command);
583
+ const name = typeof record?.name === "string" ? record.name : "";
584
+ if (!name || registered.has(name) || BRIDGE_LOCAL_COMMANDS.has(name))
585
+ continue;
586
+ registered.add(name);
587
+ const description = typeof record?.description === "string" ? record.description : undefined;
588
+ const source = typeof record?.source === "string" ? record.source : "worker";
589
+ try {
590
+ pi.registerCommand(name, {
591
+ description: `[worker ${source}] ${description ?? ""}`.trim(),
592
+ handler: async (args) => {
593
+ const text = `/${name}${args ? ` ${args}` : ""}`;
594
+ appendTranscript(state, "You", text);
595
+ try {
596
+ const result = await runRunPiCommandViaServer(options.context, options.runId, text);
597
+ const message = typeof result.message === "string" ? result.message : "worker command accepted";
598
+ appendTranscript(state, "System", message);
599
+ if (state.nativeStream)
600
+ ctx.ui.notify(message, "info");
601
+ } catch (error) {
602
+ reportBridgeError(ctx, state, error instanceof Error ? error.message : String(error));
603
+ }
604
+ updatePiUi(ctx, state);
605
+ }
606
+ });
607
+ } catch {}
608
+ }
609
+ }
610
+ async function connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands) {
580
611
  const ready = await waitForWorkerReady(options, ctx, state);
581
612
  if (!ready)
582
613
  return;
@@ -631,6 +662,7 @@ async function connectWorkerStream(options, ctx, state) {
631
662
  const record = recordOf(command);
632
663
  return typeof record?.name === "string" ? [`/${record.name}`] : [];
633
664
  });
665
+ registerDaemonCommandsNatively(pi, options, ctx, state, commands, registeredDaemonCommands);
634
666
  catchupDone = true;
635
667
  for (const payload of buffered.splice(0))
636
668
  applyEnvelope(ctx, state, payload);
@@ -758,6 +790,7 @@ function createRigWorkerPiBridgeExtension(options) {
758
790
  };
759
791
  if (options.initialMessageSent)
760
792
  appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
793
+ const registeredDaemonCommands = new Set;
761
794
  let nativePiUiContextAvailable = false;
762
795
  pi.on("user_bash", (event) => {
763
796
  state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
@@ -790,7 +823,7 @@ function createRigWorkerPiBridgeExtension(options) {
790
823
  });
791
824
  return { consume: true };
792
825
  });
793
- connectWorkerStream(options, ctx, state).catch((error) => {
826
+ connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands).catch((error) => {
794
827
  appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
795
828
  updatePiUi(ctx, state);
796
829
  });
@@ -837,8 +870,6 @@ async function attachRunBundledPiFrontend(context, input) {
837
870
  "--no-tools",
838
871
  "--no-builtin-tools",
839
872
  "--no-skills",
840
- "--no-prompt-templates",
841
- "--no-themes",
842
873
  "--no-context-files",
843
874
  "--no-approve"
844
875
  ], {