@h-rig/cli 0.0.6-alpha.41 → 0.0.6-alpha.43

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.
@@ -373,6 +373,10 @@ async function getRunPiCommandsViaServer(context, runId) {
373
373
  const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/commands`);
374
374
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { commands: [] };
375
375
  }
376
+ async function getRunPiCapabilitiesViaServer(context, runId) {
377
+ const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/capabilities`);
378
+ return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { capabilities: null };
379
+ }
376
380
  async function sendRunPiPromptViaServer(context, runId, text, streamingBehavior) {
377
381
  const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/prompt`, {
378
382
  method: "POST",
@@ -970,7 +974,7 @@ import {
970
974
  } from "@earendil-works/pi-coding-agent";
971
975
 
972
976
  // packages/cli/src/commands/_pi-remote-session.ts
973
- import { AgentSession as PiAgentSession } from "@earendil-works/pi-coding-agent";
977
+ import { AgentSession as PiAgentSession, expandPromptTemplate } from "@earendil-works/pi-coding-agent";
974
978
  var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
975
979
  function defaultTransport() {
976
980
  return {
@@ -978,6 +982,7 @@ function defaultTransport() {
978
982
  getMessages: getRunPiMessagesViaServer,
979
983
  getStatus: getRunPiStatusViaServer,
980
984
  getCommands: getRunPiCommandsViaServer,
985
+ getCapabilities: getRunPiCapabilitiesViaServer,
981
986
  sendPrompt: sendRunPiPromptViaServer,
982
987
  sendShell: sendRunPiShellViaServer,
983
988
  runCommand: runRunPiCommandViaServer,
@@ -1065,16 +1070,18 @@ class RigRemoteSessionController {
1065
1070
  this.hooks.onStatusText?.("worker Pi WebSocket disconnected");
1066
1071
  };
1067
1072
  try {
1068
- const [messagesPayload, statusPayload, commandsPayload] = await Promise.all([
1073
+ const [messagesPayload, statusPayload, commandsPayload, capabilitiesPayload] = await Promise.all([
1069
1074
  this.transport.getMessages(this.context, this.runId),
1070
1075
  this.transport.getStatus(this.context, this.runId),
1071
- this.transport.getCommands(this.context, this.runId)
1076
+ this.transport.getCommands(this.context, this.runId),
1077
+ this.transport.getCapabilities(this.context, this.runId).catch(() => ({ capabilities: null }))
1072
1078
  ]);
1073
1079
  const messages = Array.isArray(messagesPayload.messages) ? messagesPayload.messages : [];
1074
1080
  this.applyStatusPayload(statusPayload);
1075
1081
  this.session?.replaceRemoteMessages(messages);
1076
1082
  const commands = Array.isArray(commandsPayload.commands) ? commandsPayload.commands : [];
1077
- this.hooks.onCatchUp?.(messages, commands);
1083
+ const capabilities = capabilitiesPayload.capabilities && typeof capabilitiesPayload.capabilities === "object" && !Array.isArray(capabilitiesPayload.capabilities) ? capabilitiesPayload.capabilities : null;
1084
+ this.hooks.onCatchUp?.(messages, commands, capabilities);
1078
1085
  catchupDone = true;
1079
1086
  for (const payload of buffered.splice(0))
1080
1087
  this.applyEnvelope(payload);
@@ -1251,6 +1258,20 @@ class RigRemoteAgentSession extends PiAgentSession {
1251
1258
  replaceRemoteMessages(messages) {
1252
1259
  this.agent.state.messages = messages;
1253
1260
  }
1261
+ async expandLocalInput(text) {
1262
+ let current = text;
1263
+ const runner = this.extensionRunner;
1264
+ if (runner.hasHandlers("input")) {
1265
+ const result = await runner.emitInput(current, undefined, "interactive");
1266
+ if (result.action === "handled")
1267
+ return null;
1268
+ if (result.action === "transform")
1269
+ current = result.text;
1270
+ }
1271
+ current = this._expandSkillCommand(current);
1272
+ current = expandPromptTemplate(current, [...this.promptTemplates]);
1273
+ return current;
1274
+ }
1254
1275
  async prompt(text, options) {
1255
1276
  const trimmed = text.trim();
1256
1277
  if (!trimmed)
@@ -1258,6 +1279,15 @@ class RigRemoteAgentSession extends PiAgentSession {
1258
1279
  if (trimmed.startsWith("/")) {
1259
1280
  if (await this._tryExecuteExtensionCommand(trimmed))
1260
1281
  return;
1282
+ const expanded2 = await this.expandLocalInput(trimmed);
1283
+ if (expanded2 === null)
1284
+ return;
1285
+ if (expanded2 !== trimmed) {
1286
+ const behavior2 = options?.streamingBehavior ?? (this.isStreaming || this.isCompacting ? "steer" : undefined);
1287
+ options?.preflightResult?.(true);
1288
+ await this.remote.sendPrompt(expanded2, behavior2);
1289
+ return;
1290
+ }
1261
1291
  await this.remote.sendCommand(trimmed);
1262
1292
  return;
1263
1293
  }
@@ -1265,15 +1295,30 @@ class RigRemoteAgentSession extends PiAgentSession {
1265
1295
  await this.remote.sendShell(trimmed);
1266
1296
  return;
1267
1297
  }
1298
+ const expanded = await this.expandLocalInput(trimmed);
1299
+ if (expanded === null)
1300
+ return;
1268
1301
  const behavior = options?.streamingBehavior ?? (this.isStreaming || this.isCompacting ? "steer" : undefined);
1269
1302
  options?.preflightResult?.(true);
1270
- await this.remote.sendPrompt(trimmed, behavior);
1303
+ await this.remote.sendPrompt(expanded, behavior);
1271
1304
  }
1272
1305
  async steer(text) {
1273
- await this.remote.sendPrompt(text, "steer");
1306
+ const trimmed = text.trim();
1307
+ if (trimmed.startsWith("/") && await this._tryExecuteExtensionCommand(trimmed))
1308
+ return;
1309
+ const expanded = await this.expandLocalInput(trimmed);
1310
+ if (expanded === null)
1311
+ return;
1312
+ await this.remote.sendPrompt(expanded, "steer");
1274
1313
  }
1275
1314
  async followUp(text) {
1276
- await this.remote.sendPrompt(text, "followUp");
1315
+ const trimmed = text.trim();
1316
+ if (trimmed.startsWith("/") && await this._tryExecuteExtensionCommand(trimmed))
1317
+ return;
1318
+ const expanded = await this.expandLocalInput(trimmed);
1319
+ if (expanded === null)
1320
+ return;
1321
+ await this.remote.sendPrompt(expanded, "followUp");
1277
1322
  }
1278
1323
  async abort() {
1279
1324
  await this.remote.abort();
@@ -1386,6 +1431,9 @@ function createRemoteBashOperations(controller, excludeFromContext) {
1386
1431
  };
1387
1432
  }
1388
1433
 
1434
+ // packages/cli/src/commands/_spinner.ts
1435
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1436
+
1389
1437
  // packages/cli/src/commands/_pi-worker-bridge-extension.ts
1390
1438
  function recordOf2(value) {
1391
1439
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -1403,6 +1451,50 @@ function asText(value) {
1403
1451
  return String(value);
1404
1452
  }
1405
1453
  }
1454
+ function names(value, key = "name") {
1455
+ if (!Array.isArray(value))
1456
+ return [];
1457
+ return value.flatMap((entry) => {
1458
+ if (typeof entry === "string")
1459
+ return [entry];
1460
+ const record = recordOf2(entry);
1461
+ const name = record?.[key];
1462
+ return typeof name === "string" ? [name] : [];
1463
+ });
1464
+ }
1465
+ function renderWorkerCapabilities(capabilities) {
1466
+ const lines = ["Worker session capabilities (in effect for this run)"];
1467
+ const tools = Array.isArray(capabilities.tools) ? capabilities.tools : [];
1468
+ const activeTools = tools.flatMap((tool) => {
1469
+ const record = recordOf2(tool);
1470
+ return record && record.active === true && typeof record.name === "string" ? [record.name] : [];
1471
+ });
1472
+ const inactiveCount = tools.length - activeTools.length;
1473
+ lines.push(` tools ${activeTools.join(", ") || "(none)"}${inactiveCount > 0 ? ` (+${inactiveCount} inactive)` : ""}`);
1474
+ const extensions = Array.isArray(capabilities.extensions) ? capabilities.extensions : [];
1475
+ const extensionLabels = extensions.flatMap((entry) => {
1476
+ const record = recordOf2(entry);
1477
+ if (!record)
1478
+ return [];
1479
+ const path = typeof record.path === "string" ? record.path : "";
1480
+ const short = path.split("/").filter(Boolean).slice(-2).join("/") || path || "extension";
1481
+ return [short];
1482
+ });
1483
+ lines.push(` extensions ${extensionLabels.join(", ") || "(none)"}`);
1484
+ const hookEvents = names(capabilities.hookEvents);
1485
+ const hookList = Array.isArray(capabilities.hookEvents) ? capabilities.hookEvents.map(asText).filter(Boolean) : hookEvents;
1486
+ lines.push(` hooks ${hookList.join(", ") || "(none)"}`);
1487
+ lines.push(` skills ${names(capabilities.skills).join(", ") || "(none)"}`);
1488
+ lines.push(` prompts ${names(capabilities.prompts).join(", ") || "(none)"}`);
1489
+ const model = typeof capabilities.model === "string" ? capabilities.model : "(unknown)";
1490
+ const thinking = typeof capabilities.thinkingLevel === "string" ? capabilities.thinkingLevel : "";
1491
+ const cwd = typeof capabilities.cwd === "string" ? capabilities.cwd : "";
1492
+ lines.push(` model ${model}${thinking ? ` \xB7 ${thinking}` : ""}`);
1493
+ if (cwd)
1494
+ lines.push(` cwd ${cwd}`);
1495
+ lines.push(" (worker commands are in your palette as [worker \u2026] \xB7 /worker shows this again)");
1496
+ return lines;
1497
+ }
1406
1498
  async function answerExtensionUiRequest(options, ctx, request) {
1407
1499
  const requestId = String(request.requestId ?? request.id ?? `ui-${Date.now()}`);
1408
1500
  const method = String(request.method ?? request.type ?? "input");
@@ -1429,7 +1521,7 @@ async function answerExtensionUiRequest(options, ctx, request) {
1429
1521
  ctx.ui.notify(`Failed to answer worker UI request: ${error instanceof Error ? error.message : String(error)}`, "error");
1430
1522
  }
1431
1523
  }
1432
- var LOCALLY_OWNED_COMMANDS = new Set(["detach", "quit", "q", "stop", "settings", "model", "thinking", "compact", "session", "name", "reload"]);
1524
+ var LOCALLY_OWNED_COMMANDS = new Set(["detach", "quit", "q", "stop", "worker", "settings", "model", "thinking", "compact", "session", "name", "reload"]);
1433
1525
  function registerDaemonCommands(pi, options, ctx, commands, registered) {
1434
1526
  for (const command of commands) {
1435
1527
  const record = recordOf2(command);
@@ -1457,6 +1549,21 @@ function registerDaemonCommands(pi, options, ctx, commands, registered) {
1457
1549
  function createRigWorkerPiBridgeExtension(options) {
1458
1550
  return (pi) => {
1459
1551
  const registeredDaemonCommands = new Set;
1552
+ let capabilityLines = null;
1553
+ let statusText = "connecting to worker session";
1554
+ let busy = true;
1555
+ let connected = false;
1556
+ let frame = 0;
1557
+ let spinnerTimer = null;
1558
+ const renderStatus = (ctx) => {
1559
+ const prefix = busy ? `${SPINNER_FRAMES[frame]} ` : connected ? "\u25CF " : "\u25CB ";
1560
+ ctx.ui.setStatus("rig-worker-pi", `${prefix}${statusText}`);
1561
+ };
1562
+ const setStatus = (ctx, text, isBusy) => {
1563
+ statusText = text;
1564
+ busy = isBusy;
1565
+ renderStatus(ctx);
1566
+ };
1460
1567
  pi.registerCommand("detach", {
1461
1568
  description: "Detach from this run; the worker keeps going",
1462
1569
  handler: async (_args, ctx) => {
@@ -1472,31 +1579,57 @@ function createRigWorkerPiBridgeExtension(options) {
1472
1579
  ctx.shutdown();
1473
1580
  }
1474
1581
  });
1582
+ pi.registerCommand("worker", {
1583
+ description: "Show the worker session's real capabilities (tools, extensions, hooks, skills)",
1584
+ handler: async (_args, ctx) => {
1585
+ if (capabilityLines)
1586
+ ctx.ui.setWidget("rig-worker-capabilities", capabilityLines, { placement: "aboveEditor" });
1587
+ else
1588
+ ctx.ui.notify("Worker capabilities are not available yet (still connecting).", "info");
1589
+ }
1590
+ });
1475
1591
  pi.on("user_bash", (event) => ({
1476
1592
  operations: createRemoteBashOperations(options.controller, event.excludeFromContext === true)
1477
1593
  }));
1478
1594
  pi.on("session_start", async (_event, ctx) => {
1479
- ctx.ui.setTitle("Rig \xB7 worker session");
1480
- ctx.ui.setStatus("rig-worker-pi", "connecting to worker session");
1481
- ctx.ui.notify(`Attached to Rig run ${options.runId}. Native Pi, remote brain \u2014 /detach exits, /stop cancels the run.`, "info");
1595
+ ctx.ui.setTitle("Rig \xB7 enriched bundled Pi");
1596
+ setStatus(ctx, "waiting for worker Pi daemon", true);
1597
+ spinnerTimer = setInterval(() => {
1598
+ frame = (frame + 1) % SPINNER_FRAMES.length;
1599
+ if (busy)
1600
+ renderStatus(ctx);
1601
+ }, 150);
1602
+ ctx.ui.notify(`Enriched bundled Pi \u2014 native UI + Rig layers, worker brain. Attached to run ${options.runId}. /worker shows live capabilities \xB7 /detach exits \xB7 /stop cancels.`, "info");
1482
1603
  if (options.initialMessageSent)
1483
1604
  ctx.ui.notify("Initial message sent to the worker Pi daemon.", "info");
1484
1605
  const nativeUi = ctx.ui;
1485
1606
  options.controller.setUiHooks({
1486
- onStatusText: (text) => ctx.ui.setStatus("rig-worker-pi", text),
1487
- onActivity: (label, detail) => ctx.ui.setStatus("rig-worker-pi", [label, detail].filter(Boolean).join(" \u2014 ")),
1488
- onConnectionChange: (connected) => ctx.ui.setStatus("rig-worker-pi", connected ? "worker session live" : "worker session disconnected"),
1607
+ onStatusText: (text) => setStatus(ctx, text, !connected),
1608
+ onActivity: (label, detail) => {
1609
+ const active = label !== "idle" && !/complete|ready/i.test(label);
1610
+ setStatus(ctx, [label, detail].filter(Boolean).join(" \u2014 "), active);
1611
+ if (/agent running|tool:/.test(label))
1612
+ ctx.ui.setWidget("rig-worker-capabilities", undefined);
1613
+ },
1614
+ onConnectionChange: (nextConnected) => {
1615
+ connected = nextConnected;
1616
+ setStatus(ctx, nextConnected ? "worker session live" : "worker session disconnected", false);
1617
+ },
1489
1618
  onError: (message2) => ctx.ui.notify(message2, "error"),
1490
1619
  onExtensionUiRequest: (request) => {
1491
1620
  answerExtensionUiRequest(options, ctx, request);
1492
1621
  },
1493
- onCatchUp: (messages, commands) => {
1622
+ onCatchUp: (messages, commands, capabilities) => {
1494
1623
  if (nativeUi.appendSessionMessages)
1495
1624
  nativeUi.appendSessionMessages(messages);
1496
1625
  const cwd = options.controller.status.cwd;
1497
1626
  if (nativeUi.setDisplayCwd && cwd)
1498
1627
  nativeUi.setDisplayCwd(cwd);
1499
1628
  registerDaemonCommands(pi, options, ctx, commands, registeredDaemonCommands);
1629
+ if (capabilities) {
1630
+ capabilityLines = renderWorkerCapabilities(capabilities);
1631
+ ctx.ui.setWidget("rig-worker-capabilities", capabilityLines, { placement: "aboveEditor" });
1632
+ }
1500
1633
  }
1501
1634
  });
1502
1635
  options.controller.connect().catch((error) => {
@@ -1504,6 +1637,8 @@ function createRigWorkerPiBridgeExtension(options) {
1504
1637
  });
1505
1638
  });
1506
1639
  pi.on("session_shutdown", () => {
1640
+ if (spinnerTimer)
1641
+ clearInterval(spinnerTimer);
1507
1642
  options.controller.close();
1508
1643
  });
1509
1644
  };
@@ -1545,6 +1680,9 @@ async function attachRunBundledPiFrontend(context, input) {
1545
1680
  initialMessageSent: input.steered === true
1546
1681
  })
1547
1682
  ],
1683
+ noExtensions: true,
1684
+ noSkills: true,
1685
+ noPromptTemplates: true,
1548
1686
  noContextFiles: true
1549
1687
  }
1550
1688
  });
@@ -1579,7 +1717,7 @@ async function attachRunBundledPiFrontend(context, input) {
1579
1717
  timelineCursor: null,
1580
1718
  steered: input.steered === true,
1581
1719
  detached,
1582
- rendered: "native bundled Pi frontend with remote worker session runtime"
1720
+ rendered: "enriched bundled Pi frontend with remote worker session runtime"
1583
1721
  };
1584
1722
  }
1585
1723
 
@@ -1837,7 +1975,7 @@ function formatSubmittedRun(input) {
1837
1975
  ...formatNextSteps([
1838
1976
  `Attach: \`rig run attach ${input.runId} --follow\``,
1839
1977
  `Inspect: \`rig run show ${input.runId}\``,
1840
- input.detached ? "Submitted detached; attach when you are ready." : "Interactive mode opens the native bundled Pi frontend."
1978
+ input.detached ? "Submitted detached; attach when you are ready." : "Interactive mode opens the enriched bundled Pi (native UI + Rig layers, worker brain)."
1841
1979
  ])
1842
1980
  ].join(`
1843
1981
  `);
@@ -1952,7 +2090,7 @@ var PRIMARY_GROUPS = [
1952
2090
  { command: "list", description: "List recent runs from the selected server or local state.", primary: true },
1953
2091
  { command: "status", description: "Render active and recent run groups.", primary: true },
1954
2092
  { command: "show <id>|--run <id> [--raw]", description: "Show a human run summary; --raw prints the full payload.", primary: true },
1955
- { command: "attach <run-id>|--run <id> [--follow]", description: "Attach to the run; --follow launches native bundled Pi for live Pi runs.", primary: true },
2093
+ { command: "attach <run-id>|--run <id> [--follow]", description: "Attach to the run; --follow opens the enriched bundled Pi (worker brain).", primary: true },
1956
2094
  { command: "stop [<run-id>|--run <id>]", description: "Request stop for one run or local active runs.", primary: true },
1957
2095
  { command: "steer <run-id> --message <text>", description: "Queue a steering message into a live worker without attaching." },
1958
2096
  { command: "timeline --run <id> [--follow]", description: "Stream raw run timeline events." },
@@ -3015,6 +3015,10 @@ async function getRunPiCommandsViaServer(context, runId) {
3015
3015
  const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/commands`);
3016
3016
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { commands: [] };
3017
3017
  }
3018
+ async function getRunPiCapabilitiesViaServer(context, runId) {
3019
+ const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/capabilities`);
3020
+ return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { capabilities: null };
3021
+ }
3018
3022
  async function sendRunPiPromptViaServer(context, runId, text2, streamingBehavior) {
3019
3023
  const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/prompt`, {
3020
3024
  method: "POST",
@@ -4254,7 +4258,7 @@ function formatSubmittedRun(input) {
4254
4258
  ...formatNextSteps([
4255
4259
  `Attach: \`rig run attach ${input.runId} --follow\``,
4256
4260
  `Inspect: \`rig run show ${input.runId}\``,
4257
- input.detached ? "Submitted detached; attach when you are ready." : "Interactive mode opens the native bundled Pi frontend."
4261
+ input.detached ? "Submitted detached; attach when you are ready." : "Interactive mode opens the enriched bundled Pi (native UI + Rig layers, worker brain)."
4258
4262
  ])
4259
4263
  ].join(`
4260
4264
  `);
@@ -7196,7 +7200,7 @@ import {
7196
7200
  } from "@earendil-works/pi-coding-agent";
7197
7201
 
7198
7202
  // packages/cli/src/commands/_pi-remote-session.ts
7199
- import { AgentSession as PiAgentSession } from "@earendil-works/pi-coding-agent";
7203
+ import { AgentSession as PiAgentSession, expandPromptTemplate } from "@earendil-works/pi-coding-agent";
7200
7204
  var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
7201
7205
  function defaultTransport() {
7202
7206
  return {
@@ -7204,6 +7208,7 @@ function defaultTransport() {
7204
7208
  getMessages: getRunPiMessagesViaServer,
7205
7209
  getStatus: getRunPiStatusViaServer,
7206
7210
  getCommands: getRunPiCommandsViaServer,
7211
+ getCapabilities: getRunPiCapabilitiesViaServer,
7207
7212
  sendPrompt: sendRunPiPromptViaServer,
7208
7213
  sendShell: sendRunPiShellViaServer,
7209
7214
  runCommand: runRunPiCommandViaServer,
@@ -7291,16 +7296,18 @@ class RigRemoteSessionController {
7291
7296
  this.hooks.onStatusText?.("worker Pi WebSocket disconnected");
7292
7297
  };
7293
7298
  try {
7294
- const [messagesPayload, statusPayload, commandsPayload] = await Promise.all([
7299
+ const [messagesPayload, statusPayload, commandsPayload, capabilitiesPayload] = await Promise.all([
7295
7300
  this.transport.getMessages(this.context, this.runId),
7296
7301
  this.transport.getStatus(this.context, this.runId),
7297
- this.transport.getCommands(this.context, this.runId)
7302
+ this.transport.getCommands(this.context, this.runId),
7303
+ this.transport.getCapabilities(this.context, this.runId).catch(() => ({ capabilities: null }))
7298
7304
  ]);
7299
7305
  const messages = Array.isArray(messagesPayload.messages) ? messagesPayload.messages : [];
7300
7306
  this.applyStatusPayload(statusPayload);
7301
7307
  this.session?.replaceRemoteMessages(messages);
7302
7308
  const commands = Array.isArray(commandsPayload.commands) ? commandsPayload.commands : [];
7303
- this.hooks.onCatchUp?.(messages, commands);
7309
+ const capabilities = capabilitiesPayload.capabilities && typeof capabilitiesPayload.capabilities === "object" && !Array.isArray(capabilitiesPayload.capabilities) ? capabilitiesPayload.capabilities : null;
7310
+ this.hooks.onCatchUp?.(messages, commands, capabilities);
7304
7311
  catchupDone = true;
7305
7312
  for (const payload of buffered.splice(0))
7306
7313
  this.applyEnvelope(payload);
@@ -7477,6 +7484,20 @@ class RigRemoteAgentSession extends PiAgentSession {
7477
7484
  replaceRemoteMessages(messages) {
7478
7485
  this.agent.state.messages = messages;
7479
7486
  }
7487
+ async expandLocalInput(text2) {
7488
+ let current = text2;
7489
+ const runner = this.extensionRunner;
7490
+ if (runner.hasHandlers("input")) {
7491
+ const result = await runner.emitInput(current, undefined, "interactive");
7492
+ if (result.action === "handled")
7493
+ return null;
7494
+ if (result.action === "transform")
7495
+ current = result.text;
7496
+ }
7497
+ current = this._expandSkillCommand(current);
7498
+ current = expandPromptTemplate(current, [...this.promptTemplates]);
7499
+ return current;
7500
+ }
7480
7501
  async prompt(text2, options) {
7481
7502
  const trimmed = text2.trim();
7482
7503
  if (!trimmed)
@@ -7484,6 +7505,15 @@ class RigRemoteAgentSession extends PiAgentSession {
7484
7505
  if (trimmed.startsWith("/")) {
7485
7506
  if (await this._tryExecuteExtensionCommand(trimmed))
7486
7507
  return;
7508
+ const expanded2 = await this.expandLocalInput(trimmed);
7509
+ if (expanded2 === null)
7510
+ return;
7511
+ if (expanded2 !== trimmed) {
7512
+ const behavior2 = options?.streamingBehavior ?? (this.isStreaming || this.isCompacting ? "steer" : undefined);
7513
+ options?.preflightResult?.(true);
7514
+ await this.remote.sendPrompt(expanded2, behavior2);
7515
+ return;
7516
+ }
7487
7517
  await this.remote.sendCommand(trimmed);
7488
7518
  return;
7489
7519
  }
@@ -7491,15 +7521,30 @@ class RigRemoteAgentSession extends PiAgentSession {
7491
7521
  await this.remote.sendShell(trimmed);
7492
7522
  return;
7493
7523
  }
7524
+ const expanded = await this.expandLocalInput(trimmed);
7525
+ if (expanded === null)
7526
+ return;
7494
7527
  const behavior = options?.streamingBehavior ?? (this.isStreaming || this.isCompacting ? "steer" : undefined);
7495
7528
  options?.preflightResult?.(true);
7496
- await this.remote.sendPrompt(trimmed, behavior);
7529
+ await this.remote.sendPrompt(expanded, behavior);
7497
7530
  }
7498
7531
  async steer(text2) {
7499
- await this.remote.sendPrompt(text2, "steer");
7532
+ const trimmed = text2.trim();
7533
+ if (trimmed.startsWith("/") && await this._tryExecuteExtensionCommand(trimmed))
7534
+ return;
7535
+ const expanded = await this.expandLocalInput(trimmed);
7536
+ if (expanded === null)
7537
+ return;
7538
+ await this.remote.sendPrompt(expanded, "steer");
7500
7539
  }
7501
7540
  async followUp(text2) {
7502
- await this.remote.sendPrompt(text2, "followUp");
7541
+ const trimmed = text2.trim();
7542
+ if (trimmed.startsWith("/") && await this._tryExecuteExtensionCommand(trimmed))
7543
+ return;
7544
+ const expanded = await this.expandLocalInput(trimmed);
7545
+ if (expanded === null)
7546
+ return;
7547
+ await this.remote.sendPrompt(expanded, "followUp");
7503
7548
  }
7504
7549
  async abort() {
7505
7550
  await this.remote.abort();
@@ -7612,6 +7657,9 @@ function createRemoteBashOperations(controller, excludeFromContext) {
7612
7657
  };
7613
7658
  }
7614
7659
 
7660
+ // packages/cli/src/commands/_spinner.ts
7661
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
7662
+
7615
7663
  // packages/cli/src/commands/_pi-worker-bridge-extension.ts
7616
7664
  function recordOf2(value) {
7617
7665
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -7629,6 +7677,50 @@ function asText(value) {
7629
7677
  return String(value);
7630
7678
  }
7631
7679
  }
7680
+ function names(value, key = "name") {
7681
+ if (!Array.isArray(value))
7682
+ return [];
7683
+ return value.flatMap((entry) => {
7684
+ if (typeof entry === "string")
7685
+ return [entry];
7686
+ const record = recordOf2(entry);
7687
+ const name = record?.[key];
7688
+ return typeof name === "string" ? [name] : [];
7689
+ });
7690
+ }
7691
+ function renderWorkerCapabilities(capabilities) {
7692
+ const lines = ["Worker session capabilities (in effect for this run)"];
7693
+ const tools = Array.isArray(capabilities.tools) ? capabilities.tools : [];
7694
+ const activeTools = tools.flatMap((tool) => {
7695
+ const record = recordOf2(tool);
7696
+ return record && record.active === true && typeof record.name === "string" ? [record.name] : [];
7697
+ });
7698
+ const inactiveCount = tools.length - activeTools.length;
7699
+ lines.push(` tools ${activeTools.join(", ") || "(none)"}${inactiveCount > 0 ? ` (+${inactiveCount} inactive)` : ""}`);
7700
+ const extensions = Array.isArray(capabilities.extensions) ? capabilities.extensions : [];
7701
+ const extensionLabels = extensions.flatMap((entry) => {
7702
+ const record = recordOf2(entry);
7703
+ if (!record)
7704
+ return [];
7705
+ const path = typeof record.path === "string" ? record.path : "";
7706
+ const short = path.split("/").filter(Boolean).slice(-2).join("/") || path || "extension";
7707
+ return [short];
7708
+ });
7709
+ lines.push(` extensions ${extensionLabels.join(", ") || "(none)"}`);
7710
+ const hookEvents = names(capabilities.hookEvents);
7711
+ const hookList = Array.isArray(capabilities.hookEvents) ? capabilities.hookEvents.map(asText).filter(Boolean) : hookEvents;
7712
+ lines.push(` hooks ${hookList.join(", ") || "(none)"}`);
7713
+ lines.push(` skills ${names(capabilities.skills).join(", ") || "(none)"}`);
7714
+ lines.push(` prompts ${names(capabilities.prompts).join(", ") || "(none)"}`);
7715
+ const model = typeof capabilities.model === "string" ? capabilities.model : "(unknown)";
7716
+ const thinking = typeof capabilities.thinkingLevel === "string" ? capabilities.thinkingLevel : "";
7717
+ const cwd = typeof capabilities.cwd === "string" ? capabilities.cwd : "";
7718
+ lines.push(` model ${model}${thinking ? ` \xB7 ${thinking}` : ""}`);
7719
+ if (cwd)
7720
+ lines.push(` cwd ${cwd}`);
7721
+ lines.push(" (worker commands are in your palette as [worker \u2026] \xB7 /worker shows this again)");
7722
+ return lines;
7723
+ }
7632
7724
  async function answerExtensionUiRequest(options, ctx, request) {
7633
7725
  const requestId = String(request.requestId ?? request.id ?? `ui-${Date.now()}`);
7634
7726
  const method = String(request.method ?? request.type ?? "input");
@@ -7655,7 +7747,7 @@ async function answerExtensionUiRequest(options, ctx, request) {
7655
7747
  ctx.ui.notify(`Failed to answer worker UI request: ${error instanceof Error ? error.message : String(error)}`, "error");
7656
7748
  }
7657
7749
  }
7658
- var LOCALLY_OWNED_COMMANDS = new Set(["detach", "quit", "q", "stop", "settings", "model", "thinking", "compact", "session", "name", "reload"]);
7750
+ var LOCALLY_OWNED_COMMANDS = new Set(["detach", "quit", "q", "stop", "worker", "settings", "model", "thinking", "compact", "session", "name", "reload"]);
7659
7751
  function registerDaemonCommands(pi, options, ctx, commands, registered) {
7660
7752
  for (const command of commands) {
7661
7753
  const record = recordOf2(command);
@@ -7683,6 +7775,21 @@ function registerDaemonCommands(pi, options, ctx, commands, registered) {
7683
7775
  function createRigWorkerPiBridgeExtension(options) {
7684
7776
  return (pi) => {
7685
7777
  const registeredDaemonCommands = new Set;
7778
+ let capabilityLines = null;
7779
+ let statusText = "connecting to worker session";
7780
+ let busy = true;
7781
+ let connected = false;
7782
+ let frame = 0;
7783
+ let spinnerTimer = null;
7784
+ const renderStatus = (ctx) => {
7785
+ const prefix = busy ? `${SPINNER_FRAMES[frame]} ` : connected ? "\u25CF " : "\u25CB ";
7786
+ ctx.ui.setStatus("rig-worker-pi", `${prefix}${statusText}`);
7787
+ };
7788
+ const setStatus = (ctx, text2, isBusy) => {
7789
+ statusText = text2;
7790
+ busy = isBusy;
7791
+ renderStatus(ctx);
7792
+ };
7686
7793
  pi.registerCommand("detach", {
7687
7794
  description: "Detach from this run; the worker keeps going",
7688
7795
  handler: async (_args, ctx) => {
@@ -7698,31 +7805,57 @@ function createRigWorkerPiBridgeExtension(options) {
7698
7805
  ctx.shutdown();
7699
7806
  }
7700
7807
  });
7808
+ pi.registerCommand("worker", {
7809
+ description: "Show the worker session's real capabilities (tools, extensions, hooks, skills)",
7810
+ handler: async (_args, ctx) => {
7811
+ if (capabilityLines)
7812
+ ctx.ui.setWidget("rig-worker-capabilities", capabilityLines, { placement: "aboveEditor" });
7813
+ else
7814
+ ctx.ui.notify("Worker capabilities are not available yet (still connecting).", "info");
7815
+ }
7816
+ });
7701
7817
  pi.on("user_bash", (event) => ({
7702
7818
  operations: createRemoteBashOperations(options.controller, event.excludeFromContext === true)
7703
7819
  }));
7704
7820
  pi.on("session_start", async (_event, ctx) => {
7705
- ctx.ui.setTitle("Rig \xB7 worker session");
7706
- ctx.ui.setStatus("rig-worker-pi", "connecting to worker session");
7707
- ctx.ui.notify(`Attached to Rig run ${options.runId}. Native Pi, remote brain \u2014 /detach exits, /stop cancels the run.`, "info");
7821
+ ctx.ui.setTitle("Rig \xB7 enriched bundled Pi");
7822
+ setStatus(ctx, "waiting for worker Pi daemon", true);
7823
+ spinnerTimer = setInterval(() => {
7824
+ frame = (frame + 1) % SPINNER_FRAMES.length;
7825
+ if (busy)
7826
+ renderStatus(ctx);
7827
+ }, 150);
7828
+ ctx.ui.notify(`Enriched bundled Pi \u2014 native UI + Rig layers, worker brain. Attached to run ${options.runId}. /worker shows live capabilities \xB7 /detach exits \xB7 /stop cancels.`, "info");
7708
7829
  if (options.initialMessageSent)
7709
7830
  ctx.ui.notify("Initial message sent to the worker Pi daemon.", "info");
7710
7831
  const nativeUi = ctx.ui;
7711
7832
  options.controller.setUiHooks({
7712
- onStatusText: (text2) => ctx.ui.setStatus("rig-worker-pi", text2),
7713
- onActivity: (label, detail) => ctx.ui.setStatus("rig-worker-pi", [label, detail].filter(Boolean).join(" \u2014 ")),
7714
- onConnectionChange: (connected) => ctx.ui.setStatus("rig-worker-pi", connected ? "worker session live" : "worker session disconnected"),
7833
+ onStatusText: (text2) => setStatus(ctx, text2, !connected),
7834
+ onActivity: (label, detail) => {
7835
+ const active = label !== "idle" && !/complete|ready/i.test(label);
7836
+ setStatus(ctx, [label, detail].filter(Boolean).join(" \u2014 "), active);
7837
+ if (/agent running|tool:/.test(label))
7838
+ ctx.ui.setWidget("rig-worker-capabilities", undefined);
7839
+ },
7840
+ onConnectionChange: (nextConnected) => {
7841
+ connected = nextConnected;
7842
+ setStatus(ctx, nextConnected ? "worker session live" : "worker session disconnected", false);
7843
+ },
7715
7844
  onError: (message2) => ctx.ui.notify(message2, "error"),
7716
7845
  onExtensionUiRequest: (request) => {
7717
7846
  answerExtensionUiRequest(options, ctx, request);
7718
7847
  },
7719
- onCatchUp: (messages, commands) => {
7848
+ onCatchUp: (messages, commands, capabilities) => {
7720
7849
  if (nativeUi.appendSessionMessages)
7721
7850
  nativeUi.appendSessionMessages(messages);
7722
7851
  const cwd = options.controller.status.cwd;
7723
7852
  if (nativeUi.setDisplayCwd && cwd)
7724
7853
  nativeUi.setDisplayCwd(cwd);
7725
7854
  registerDaemonCommands(pi, options, ctx, commands, registeredDaemonCommands);
7855
+ if (capabilities) {
7856
+ capabilityLines = renderWorkerCapabilities(capabilities);
7857
+ ctx.ui.setWidget("rig-worker-capabilities", capabilityLines, { placement: "aboveEditor" });
7858
+ }
7726
7859
  }
7727
7860
  });
7728
7861
  options.controller.connect().catch((error) => {
@@ -7730,6 +7863,8 @@ function createRigWorkerPiBridgeExtension(options) {
7730
7863
  });
7731
7864
  });
7732
7865
  pi.on("session_shutdown", () => {
7866
+ if (spinnerTimer)
7867
+ clearInterval(spinnerTimer);
7733
7868
  options.controller.close();
7734
7869
  });
7735
7870
  };
@@ -7771,6 +7906,9 @@ async function attachRunBundledPiFrontend(context, input) {
7771
7906
  initialMessageSent: input.steered === true
7772
7907
  })
7773
7908
  ],
7909
+ noExtensions: true,
7910
+ noSkills: true,
7911
+ noPromptTemplates: true,
7774
7912
  noContextFiles: true
7775
7913
  }
7776
7914
  });
@@ -7805,7 +7943,7 @@ async function attachRunBundledPiFrontend(context, input) {
7805
7943
  timelineCursor: null,
7806
7944
  steered: input.steered === true,
7807
7945
  detached,
7808
- rendered: "native bundled Pi frontend with remote worker session runtime"
7946
+ rendered: "enriched bundled Pi frontend with remote worker session runtime"
7809
7947
  };
7810
7948
  }
7811
7949
 
@@ -8602,7 +8740,7 @@ var PRIMARY_GROUPS = [
8602
8740
  { command: "list", description: "List recent runs from the selected server or local state.", primary: true },
8603
8741
  { command: "status", description: "Render active and recent run groups.", primary: true },
8604
8742
  { command: "show <id>|--run <id> [--raw]", description: "Show a human run summary; --raw prints the full payload.", primary: true },
8605
- { command: "attach <run-id>|--run <id> [--follow]", description: "Attach to the run; --follow launches native bundled Pi for live Pi runs.", primary: true },
8743
+ { command: "attach <run-id>|--run <id> [--follow]", description: "Attach to the run; --follow opens the enriched bundled Pi (worker brain).", primary: true },
8606
8744
  { command: "stop [<run-id>|--run <id>]", description: "Request stop for one run or local active runs.", primary: true },
8607
8745
  { command: "steer <run-id> --message <text>", description: "Queue a steering message into a live worker without attaching." },
8608
8746
  { command: "timeline --run <id> [--follow]", description: "Stream raw run timeline events." },