@robota-sdk/agent-cli 3.0.0-beta.34 → 3.0.0-beta.36

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.
@@ -179,12 +179,13 @@ var PrintTerminal = class {
179
179
  };
180
180
 
181
181
  // src/ui/render.tsx
182
- var import_ink12 = require("ink");
182
+ var import_ink14 = require("ink");
183
183
 
184
184
  // src/ui/App.tsx
185
- var import_react13 = require("react");
186
- var import_ink11 = require("ink");
187
- var import_agent_core3 = require("@robota-sdk/agent-core");
185
+ var import_react16 = require("react");
186
+ var import_ink13 = require("ink");
187
+ var import_agent_core6 = require("@robota-sdk/agent-core");
188
+ var import_agent_core7 = require("@robota-sdk/agent-core");
188
189
 
189
190
  // src/ui/hooks/useSession.ts
190
191
  var import_react = require("react");
@@ -280,6 +281,7 @@ var NOOP_TERMINAL = {
280
281
  function useSession(props) {
281
282
  const [permissionRequest, setPermissionRequest] = (0, import_react.useState)(null);
282
283
  const [streamingText, setStreamingText] = (0, import_react.useState)("");
284
+ const streamingTextRef = (0, import_react.useRef)("");
283
285
  const [activeTools, setActiveTools] = (0, import_react.useState)([]);
284
286
  const permissionQueueRef = (0, import_react.useRef)([]);
285
287
  const processingRef = (0, import_react.useRef)(false);
@@ -311,8 +313,15 @@ function useSession(props) {
311
313
  processNextPermission();
312
314
  });
313
315
  };
316
+ let flushTimer = null;
314
317
  const onTextDelta = (delta) => {
315
- setStreamingText((prev) => prev + delta);
318
+ streamingTextRef.current += delta;
319
+ if (!flushTimer) {
320
+ flushTimer = setTimeout(() => {
321
+ setStreamingText(streamingTextRef.current);
322
+ flushTimer = null;
323
+ }, 16);
324
+ }
316
325
  };
317
326
  const onToolExecution = (event) => {
318
327
  if (event.type === "start") {
@@ -391,6 +400,7 @@ function useSession(props) {
391
400
  }
392
401
  const clearStreamingText = (0, import_react.useCallback)(() => {
393
402
  setStreamingText("");
403
+ streamingTextRef.current = "";
394
404
  setActiveTools([]);
395
405
  }, []);
396
406
  return {
@@ -405,16 +415,11 @@ function useSession(props) {
405
415
  // src/ui/hooks/useMessages.ts
406
416
  var import_react2 = require("react");
407
417
  var MAX_RENDERED_MESSAGES = 100;
408
- var msgIdCounter = 0;
409
- function nextId() {
410
- msgIdCounter += 1;
411
- return `msg_${msgIdCounter}`;
412
- }
413
418
  function useMessages() {
414
419
  const [messages, setMessages] = (0, import_react2.useState)([]);
415
420
  const addMessage = (0, import_react2.useCallback)((msg) => {
416
421
  setMessages((prev) => {
417
- const updated = [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }];
422
+ const updated = [...prev, msg];
418
423
  if (updated.length > MAX_RENDERED_MESSAGES) {
419
424
  return updated.slice(-MAX_RENDERED_MESSAGES);
420
425
  }
@@ -426,6 +431,7 @@ function useMessages() {
426
431
 
427
432
  // src/ui/hooks/useSlashCommands.ts
428
433
  var import_react3 = require("react");
434
+ var import_agent_core = require("@robota-sdk/agent-core");
429
435
 
430
436
  // src/commands/slash-executor.ts
431
437
  var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
@@ -536,18 +542,9 @@ async function handlePluginCommand(args, addMessage, callbacks) {
536
542
  try {
537
543
  switch (subcommand) {
538
544
  case "":
539
- case void 0: {
540
- const plugins = await callbacks.listInstalled();
541
- if (plugins.length === 0) {
542
- addMessage({ role: "system", content: "No plugins installed." });
543
- } else {
544
- const lines = plugins.map(
545
- (p) => ` ${p.name} \u2014 ${p.description} [${p.enabled ? "enabled" : "disabled"}]`
546
- );
547
- addMessage({ role: "system", content: `Installed plugins:
548
- ${lines.join("\n")}` });
549
- }
550
- return { handled: true };
545
+ case void 0:
546
+ case "manage": {
547
+ return { handled: true, triggerPluginTUI: true };
551
548
  }
552
549
  case "install": {
553
550
  if (!subArgs) {
@@ -694,18 +691,21 @@ async function executeSlashCommand(cmd, args, session, addMessage, clearMessages
694
691
 
695
692
  // src/ui/hooks/useSlashCommands.ts
696
693
  var EXIT_DELAY_MS = 500;
697
- function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId, pluginCallbacks) {
694
+ function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId, pluginCallbacks, setShowPluginTUI) {
698
695
  return (0, import_react3.useCallback)(
699
696
  async (input) => {
700
697
  const parts = input.slice(1).split(/\s+/);
701
698
  const cmd = parts[0]?.toLowerCase() ?? "";
702
699
  const args = parts.slice(1).join(" ");
703
700
  const clearMessages = () => setMessages([]);
701
+ const slashAddMessage = (msg) => {
702
+ addMessage((0, import_agent_core.createSystemMessage)(msg.content));
703
+ };
704
704
  const result = await executeSlashCommand(
705
705
  cmd,
706
706
  args,
707
707
  session,
708
- addMessage,
708
+ slashAddMessage,
709
709
  clearMessages,
710
710
  registry,
711
711
  pluginCallbacks
@@ -714,6 +714,9 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
714
714
  pendingModelChangeRef.current = result.pendingModelId;
715
715
  setPendingModelId(result.pendingModelId);
716
716
  }
717
+ if (result.triggerPluginTUI) {
718
+ setShowPluginTUI?.(true);
719
+ }
717
720
  if (result.exitRequested) {
718
721
  setTimeout(() => exit(), EXIT_DELAY_MS);
719
722
  }
@@ -727,14 +730,17 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
727
730
  registry,
728
731
  pendingModelChangeRef,
729
732
  setPendingModelId,
730
- pluginCallbacks
733
+ pluginCallbacks,
734
+ setShowPluginTUI
731
735
  ]
732
736
  );
733
737
  }
734
738
 
735
739
  // src/ui/hooks/useSubmitHandler.ts
736
740
  var import_react4 = require("react");
741
+ var import_node_crypto = require("crypto");
737
742
  var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
743
+ var import_agent_core2 = require("@robota-sdk/agent-core");
738
744
 
739
745
  // src/utils/tool-call-extractor.ts
740
746
  var TOOL_ARG_MAX_LENGTH = 80;
@@ -891,21 +897,50 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
891
897
  historyBefore
892
898
  );
893
899
  if (toolSummaries.length > 0) {
894
- addMessage({
895
- role: "tool",
896
- content: JSON.stringify(toolSummaries),
897
- toolName: `${toolSummaries.length} tools`
898
- });
900
+ addMessage(
901
+ (0, import_agent_core2.createToolMessage)(JSON.stringify(toolSummaries), {
902
+ toolCallId: (0, import_node_crypto.randomUUID)(),
903
+ name: `${toolSummaries.length} tools`
904
+ })
905
+ );
899
906
  }
900
- addMessage({ role: "assistant", content: response || "(empty response)" });
907
+ addMessage((0, import_agent_core2.createAssistantMessage)(response || "(empty response)"));
901
908
  syncContextState(session, setContextState);
902
909
  } catch (err) {
903
910
  clearStreamingText();
904
- if (err instanceof DOMException && err.name === "AbortError") {
905
- addMessage({ role: "system", content: "Cancelled." });
911
+ const isAbortError = err instanceof DOMException && err.name === "AbortError" || err instanceof Error && (err.message.includes("aborted") || err.message.includes("abort"));
912
+ if (isAbortError) {
913
+ const history = session.getHistory();
914
+ const toolSummaries = extractToolCallsWithDiff(
915
+ history,
916
+ historyBefore
917
+ );
918
+ if (toolSummaries.length > 0) {
919
+ addMessage(
920
+ (0, import_agent_core2.createToolMessage)(JSON.stringify(toolSummaries), {
921
+ toolCallId: (0, import_node_crypto.randomUUID)(),
922
+ name: `${toolSummaries.length} tools`
923
+ })
924
+ );
925
+ }
926
+ const assistantParts = [];
927
+ let lastAssistantState = "complete";
928
+ for (let i = historyBefore; i < history.length; i++) {
929
+ const msg = history[i];
930
+ if (msg && msg.role === "assistant" && msg.content) {
931
+ assistantParts.push(msg.content);
932
+ if (msg.state === "interrupted") lastAssistantState = "interrupted";
933
+ }
934
+ }
935
+ if (assistantParts.length > 0) {
936
+ addMessage(
937
+ (0, import_agent_core2.createAssistantMessage)(assistantParts.join("\n\n"), { state: lastAssistantState })
938
+ );
939
+ }
940
+ addMessage((0, import_agent_core2.createSystemMessage)("Interrupted by user."));
906
941
  } else {
907
942
  const errMsg = err instanceof Error ? err.message : String(err);
908
- addMessage({ role: "system", content: `Error: ${errMsg}` });
943
+ addMessage((0, import_agent_core2.createSystemMessage)(`Error: ${errMsg}`));
909
944
  }
910
945
  } finally {
911
946
  setIsThinking(false);
@@ -958,7 +993,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
958
993
  const runInFork = createForkRunner(session);
959
994
  const result = await executeSkill(skill, args, { runInFork });
960
995
  if (result.mode === "fork") {
961
- addMessage({ role: "assistant", content: result.result ?? "(empty response)" });
996
+ addMessage((0, import_agent_core2.createAssistantMessage)(result.result ?? "(empty response)"));
962
997
  syncContextState(session, setContextState);
963
998
  return;
964
999
  }
@@ -993,7 +1028,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
993
1028
  hookInput
994
1029
  );
995
1030
  }
996
- addMessage({ role: "user", content: input });
1031
+ addMessage((0, import_agent_core2.createUserMessage)(input));
997
1032
  return runSessionPrompt(
998
1033
  input,
999
1034
  session,
@@ -1060,16 +1095,16 @@ var CommandRegistry = class {
1060
1095
  };
1061
1096
 
1062
1097
  // src/commands/builtin-source.ts
1063
- var import_agent_core = require("@robota-sdk/agent-core");
1098
+ var import_agent_core3 = require("@robota-sdk/agent-core");
1064
1099
  function buildModelSubcommands() {
1065
1100
  const seen = /* @__PURE__ */ new Set();
1066
1101
  const commands = [];
1067
- for (const model of Object.values(import_agent_core.CLAUDE_MODELS)) {
1102
+ for (const model of Object.values(import_agent_core3.CLAUDE_MODELS)) {
1068
1103
  if (seen.has(model.name)) continue;
1069
1104
  seen.add(model.name);
1070
1105
  commands.push({
1071
1106
  name: model.id,
1072
- description: `${model.name} (${(0, import_agent_core.formatTokenCount)(model.contextWindow).toUpperCase()})`,
1107
+ description: `${model.name} (${(0, import_agent_core3.formatTokenCount)(model.contextWindow).toUpperCase()})`,
1073
1108
  source: "builtin"
1074
1109
  });
1075
1110
  }
@@ -1111,22 +1146,7 @@ function createBuiltinCommands() {
1111
1146
  { name: "cost", description: "Show session info", source: "builtin" },
1112
1147
  { name: "context", description: "Context window info", source: "builtin" },
1113
1148
  { name: "permissions", description: "Permission rules", source: "builtin" },
1114
- {
1115
- name: "plugin",
1116
- description: "Manage plugins",
1117
- source: "builtin",
1118
- subcommands: [
1119
- { name: "install", description: "Install a plugin (name@marketplace)", source: "builtin" },
1120
- {
1121
- name: "uninstall",
1122
- description: "Uninstall a plugin (name@marketplace)",
1123
- source: "builtin"
1124
- },
1125
- { name: "enable", description: "Enable a plugin (name@marketplace)", source: "builtin" },
1126
- { name: "disable", description: "Disable a plugin (name@marketplace)", source: "builtin" },
1127
- { name: "marketplace", description: "Manage marketplace sources", source: "builtin" }
1128
- ]
1129
- },
1149
+ { name: "plugin", description: "Manage plugins", source: "builtin" },
1130
1150
  { name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
1131
1151
  { name: "reset", description: "Delete settings and exit", source: "builtin" },
1132
1152
  { name: "exit", description: "Exit CLI", source: "builtin" }
@@ -1393,18 +1413,50 @@ function usePluginCallbacks(cwd) {
1393
1413
  return {
1394
1414
  listInstalled: async () => {
1395
1415
  const plugins = await loader.loadAll();
1396
- return plugins.map((p) => ({
1397
- name: p.manifest.name,
1398
- description: p.manifest.description,
1399
- enabled: true
1416
+ const enabledMap = settingsStore.getEnabledPlugins();
1417
+ return plugins.map((p) => {
1418
+ const parts = p.pluginDir.split("/");
1419
+ const cacheIdx = parts.indexOf("cache");
1420
+ const marketplaceName = cacheIdx >= 0 ? parts[cacheIdx + 1] : "";
1421
+ const fullId = marketplaceName ? `${p.manifest.name}@${marketplaceName}` : p.manifest.name;
1422
+ return {
1423
+ name: fullId,
1424
+ description: p.manifest.description,
1425
+ enabled: enabledMap[fullId] !== false && enabledMap[p.manifest.name] !== false
1426
+ };
1427
+ });
1428
+ },
1429
+ listAvailablePlugins: async (marketplaceName) => {
1430
+ let manifest;
1431
+ try {
1432
+ manifest = marketplace.fetchManifest(marketplaceName);
1433
+ } catch {
1434
+ return [];
1435
+ }
1436
+ const installed = installer.getInstalledPlugins();
1437
+ const installedNames = new Set(Object.values(installed).map((r) => r.pluginName));
1438
+ return manifest.plugins.map((p) => ({
1439
+ name: p.name,
1440
+ description: p.description,
1441
+ installed: installedNames.has(p.name)
1400
1442
  }));
1401
1443
  },
1402
- install: async (pluginId) => {
1444
+ install: async (pluginId, scope) => {
1403
1445
  const [name, marketplaceName] = pluginId.split("@");
1404
1446
  if (!name || !marketplaceName) {
1405
1447
  throw new Error("Plugin ID must be in format: name@marketplace");
1406
1448
  }
1407
- await installer.install(name, marketplaceName);
1449
+ if (scope === "project") {
1450
+ const projectPluginsDir = (0, import_node_path4.join)(cwd, ".robota", "plugins");
1451
+ const projectInstaller = new import_agent_sdk4.BundlePluginInstaller({
1452
+ pluginsDir: projectPluginsDir,
1453
+ settingsStore,
1454
+ marketplaceClient: marketplace
1455
+ });
1456
+ await projectInstaller.install(name, marketplaceName);
1457
+ } else {
1458
+ await installer.install(name, marketplaceName);
1459
+ }
1408
1460
  },
1409
1461
  uninstall: async (pluginId) => {
1410
1462
  await installer.uninstall(pluginId);
@@ -1447,6 +1499,7 @@ function usePluginCallbacks(cwd) {
1447
1499
  // src/ui/MessageList.tsx
1448
1500
  var import_react7 = __toESM(require("react"), 1);
1449
1501
  var import_ink2 = require("ink");
1502
+ var import_agent_core4 = require("@robota-sdk/agent-core");
1450
1503
 
1451
1504
  // src/ui/render-markdown.ts
1452
1505
  var import_marked = require("marked");
@@ -1532,9 +1585,14 @@ function RoleLabel({ role }) {
1532
1585
  }
1533
1586
  }
1534
1587
  function ToolMessage({ message }) {
1588
+ if (!(0, import_agent_core4.isToolMessage)(message)) {
1589
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
1590
+ }
1591
+ const toolName = message.name;
1592
+ const content = message.content;
1535
1593
  let summaries = null;
1536
1594
  try {
1537
- const parsed = JSON.parse(message.content);
1595
+ const parsed = JSON.parse(content);
1538
1596
  if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0].line === "string") {
1539
1597
  summaries = parsed;
1540
1598
  }
@@ -1547,9 +1605,9 @@ function ToolMessage({ message }) {
1547
1605
  "Tool:",
1548
1606
  " "
1549
1607
  ] }),
1550
- message.toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1608
+ toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1551
1609
  "[",
1552
- message.toolName,
1610
+ toolName,
1553
1611
  "]"
1554
1612
  ] })
1555
1613
  ] }),
@@ -1565,16 +1623,16 @@ function ToolMessage({ message }) {
1565
1623
  ] }, i))
1566
1624
  ] });
1567
1625
  }
1568
- const lines = message.content.split("\n").filter((l) => l.trim());
1626
+ const lines = content.split("\n").filter((l) => l.trim());
1569
1627
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
1570
1628
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { children: [
1571
1629
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", bold: true, children: [
1572
1630
  "Tool:",
1573
1631
  " "
1574
1632
  ] }),
1575
- message.toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1633
+ toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1576
1634
  "[",
1577
- message.toolName,
1635
+ toolName,
1578
1636
  "]"
1579
1637
  ] })
1580
1638
  ] }),
@@ -1590,21 +1648,15 @@ function ToolMessage({ message }) {
1590
1648
  var MessageItem = import_react7.default.memo(function MessageItem2({
1591
1649
  message
1592
1650
  }) {
1593
- if (message.role === "tool") {
1651
+ if ((0, import_agent_core4.isToolMessage)(message)) {
1594
1652
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolMessage, { message });
1595
1653
  }
1654
+ const content = message.content ?? "";
1655
+ const isInterrupted = message.state === "interrupted";
1596
1656
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
1597
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { children: [
1598
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }),
1599
- message.toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "magenta", dimColor: true, children: [
1600
- "[",
1601
- message.toolName,
1602
- "]",
1603
- " "
1604
- ] })
1605
- ] }),
1657
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }) }),
1606
1658
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
1607
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { wrap: "wrap", children: message.role === "assistant" ? renderMarkdown(message.content) : message.content }) })
1659
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { wrap: "wrap", children: (0, import_agent_core4.isAssistantMessage)(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
1608
1660
  ] });
1609
1661
  });
1610
1662
  function MessageList({ messages }) {
@@ -1613,7 +1665,7 @@ function MessageList({ messages }) {
1613
1665
 
1614
1666
  // src/ui/StatusBar.tsx
1615
1667
  var import_ink3 = require("ink");
1616
- var import_agent_core2 = require("@robota-sdk/agent-core");
1668
+ var import_agent_core5 = require("@robota-sdk/agent-core");
1617
1669
  var import_jsx_runtime3 = require("react/jsx-runtime");
1618
1670
  var CONTEXT_YELLOW_THRESHOLD = 70;
1619
1671
  var CONTEXT_RED_THRESHOLD = 90;
@@ -1653,9 +1705,9 @@ function StatusBar({
1653
1705
  "Context: ",
1654
1706
  Math.round(contextPercentage),
1655
1707
  "% (",
1656
- (0, import_agent_core2.formatTokenCount)(contextUsedTokens),
1708
+ (0, import_agent_core5.formatTokenCount)(contextUsedTokens),
1657
1709
  "/",
1658
- (0, import_agent_core2.formatTokenCount)(contextMaxTokens),
1710
+ (0, import_agent_core5.formatTokenCount)(contextMaxTokens),
1659
1711
  ")"
1660
1712
  ] })
1661
1713
  ] }),
@@ -1983,7 +2035,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1983
2035
  isSubcommandMode
1984
2036
  }
1985
2037
  ),
1986
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { borderStyle: "single", borderColor: isDisabled ? "gray" : "green", paddingLeft: 1, children: isDisabled ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(WaveText, { text: " Waiting for response..." }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { children: [
2038
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { borderStyle: "single", borderColor: isDisabled ? "gray" : "green", paddingLeft: 1, children: isDisabled ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(WaveText, { text: " Waiting for response... (ESC to interrupt)" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { children: [
1987
2039
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "green", bold: true, children: "> " }),
1988
2040
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1989
2041
  CjkTextInput,
@@ -2119,7 +2171,7 @@ function StreamingIndicator({ text, activeTools }) {
2119
2171
  const hasTools = activeTools.length > 0;
2120
2172
  const hasText = text.length > 0;
2121
2173
  if (!hasTools && !hasText) {
2122
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "yellow", children: "Thinking..." });
2174
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, {});
2123
2175
  }
2124
2176
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
2125
2177
  hasTools && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", marginBottom: 1, children: [
@@ -2149,8 +2201,459 @@ function StreamingIndicator({ text, activeTools }) {
2149
2201
  ] });
2150
2202
  }
2151
2203
 
2152
- // src/ui/App.tsx
2204
+ // src/ui/PluginTUI.tsx
2205
+ var import_react15 = require("react");
2206
+
2207
+ // src/ui/MenuSelect.tsx
2208
+ var import_react13 = require("react");
2209
+ var import_ink11 = require("ink");
2153
2210
  var import_jsx_runtime11 = require("react/jsx-runtime");
2211
+ function MenuSelect({
2212
+ title,
2213
+ items,
2214
+ onSelect,
2215
+ onBack,
2216
+ loading,
2217
+ error
2218
+ }) {
2219
+ const [selected, setSelected] = (0, import_react13.useState)(0);
2220
+ const selectedRef = (0, import_react13.useRef)(0);
2221
+ const resolvedRef = (0, import_react13.useRef)(false);
2222
+ const doSelect = (0, import_react13.useCallback)(
2223
+ (index) => {
2224
+ if (resolvedRef.current || items.length === 0) return;
2225
+ resolvedRef.current = true;
2226
+ onSelect(items[index].value);
2227
+ },
2228
+ [items, onSelect]
2229
+ );
2230
+ (0, import_ink11.useInput)((input, key) => {
2231
+ if (resolvedRef.current) return;
2232
+ if (key.escape) {
2233
+ resolvedRef.current = true;
2234
+ onBack();
2235
+ return;
2236
+ }
2237
+ if (loading || error || items.length === 0) return;
2238
+ if (key.upArrow) {
2239
+ const next = selectedRef.current > 0 ? selectedRef.current - 1 : selectedRef.current;
2240
+ selectedRef.current = next;
2241
+ setSelected(next);
2242
+ } else if (key.downArrow) {
2243
+ const next = selectedRef.current < items.length - 1 ? selectedRef.current + 1 : selectedRef.current;
2244
+ selectedRef.current = next;
2245
+ setSelected(next);
2246
+ } else if (key.return) {
2247
+ doSelect(selectedRef.current);
2248
+ }
2249
+ });
2250
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2251
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "yellow", bold: true, children: title }),
2252
+ loading && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: "Loading..." }) }),
2253
+ error && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { marginTop: 1, flexDirection: "column", children: [
2254
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "red", children: error }),
2255
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: "Press Esc to go back" })
2256
+ ] }),
2257
+ !loading && !error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { flexDirection: "column", marginTop: 1, children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { children: [
2258
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
2259
+ i === selected ? "> " : " ",
2260
+ item.label
2261
+ ] }),
2262
+ item.hint && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { dimColor: true, children: [
2263
+ " ",
2264
+ item.hint
2265
+ ] })
2266
+ ] }, item.value)) }),
2267
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
2268
+ ] });
2269
+ }
2270
+
2271
+ // src/ui/TextPrompt.tsx
2272
+ var import_react14 = require("react");
2273
+ var import_ink12 = require("ink");
2274
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2275
+ function TextPrompt({
2276
+ title,
2277
+ placeholder,
2278
+ onSubmit,
2279
+ onCancel,
2280
+ validate
2281
+ }) {
2282
+ const [value, setValue] = (0, import_react14.useState)("");
2283
+ const [error, setError] = (0, import_react14.useState)();
2284
+ const resolvedRef = (0, import_react14.useRef)(false);
2285
+ const valueRef = (0, import_react14.useRef)("");
2286
+ const handleSubmit = (0, import_react14.useCallback)(() => {
2287
+ if (resolvedRef.current) return;
2288
+ const trimmed = valueRef.current.trim();
2289
+ if (!trimmed) return;
2290
+ if (validate) {
2291
+ const err = validate(trimmed);
2292
+ if (err) {
2293
+ setError(err);
2294
+ return;
2295
+ }
2296
+ }
2297
+ resolvedRef.current = true;
2298
+ onSubmit(trimmed);
2299
+ }, [validate, onSubmit]);
2300
+ (0, import_ink12.useInput)((input, key) => {
2301
+ if (resolvedRef.current) return;
2302
+ if (key.escape) {
2303
+ resolvedRef.current = true;
2304
+ onCancel();
2305
+ return;
2306
+ }
2307
+ if (key.return) {
2308
+ handleSubmit();
2309
+ return;
2310
+ }
2311
+ if (key.backspace || key.delete) {
2312
+ valueRef.current = valueRef.current.slice(0, -1);
2313
+ setValue(valueRef.current);
2314
+ setError(void 0);
2315
+ return;
2316
+ }
2317
+ if (input && !key.ctrl && !key.meta) {
2318
+ valueRef.current = valueRef.current + input;
2319
+ setValue(valueRef.current);
2320
+ setError(void 0);
2321
+ }
2322
+ });
2323
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2324
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "yellow", bold: true, children: title }),
2325
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { marginTop: 1, children: [
2326
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", children: "> " }),
2327
+ value ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { children: value }) : placeholder ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { dimColor: true, children: placeholder }) : null,
2328
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", children: "\u2588" })
2329
+ ] }),
2330
+ error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "red", children: error }),
2331
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { dimColor: true, children: " Enter Submit Esc Cancel" })
2332
+ ] });
2333
+ }
2334
+
2335
+ // src/ui/plugin-tui-handlers.ts
2336
+ function handleMainSelect(value, nav) {
2337
+ if (value === "marketplace") {
2338
+ nav.push({ screen: "marketplace-list" });
2339
+ } else if (value === "installed") {
2340
+ nav.push({ screen: "installed-list" });
2341
+ }
2342
+ }
2343
+ function handleMarketplaceListSelect(value, nav) {
2344
+ if (value === "__add__") {
2345
+ nav.push({ screen: "marketplace-add" });
2346
+ } else {
2347
+ nav.push({ screen: "marketplace-action", context: { marketplace: value } });
2348
+ }
2349
+ }
2350
+ function handleMarketplaceActionSelect(value, marketplace, callbacks, nav) {
2351
+ if (value === "browse") {
2352
+ nav.push({ screen: "marketplace-browse", context: { marketplace } });
2353
+ } else if (value === "update") {
2354
+ callbacks.marketplaceUpdate(marketplace).then(() => {
2355
+ nav.notify(`Updated marketplace "${marketplace}".`);
2356
+ nav.pop();
2357
+ }).catch((err) => {
2358
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2359
+ });
2360
+ } else if (value === "remove") {
2361
+ nav.setConfirm({
2362
+ message: `Remove marketplace "${marketplace}" and all its plugins?`,
2363
+ onConfirm: () => {
2364
+ nav.setConfirm(void 0);
2365
+ callbacks.marketplaceRemove(marketplace).then(() => {
2366
+ nav.notify(`Removed marketplace "${marketplace}".`);
2367
+ nav.popN(2);
2368
+ }).catch((err) => {
2369
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2370
+ });
2371
+ },
2372
+ onCancel: () => nav.setConfirm(void 0)
2373
+ });
2374
+ }
2375
+ }
2376
+ function handleMarketplaceBrowseSelect(value, marketplace, items, nav) {
2377
+ const fullId = `${value}@${marketplace}`;
2378
+ const item = items.find((i) => i.value === value);
2379
+ if (item?.hint === "installed") {
2380
+ nav.push({ screen: "installed-action", context: { pluginId: fullId } });
2381
+ } else {
2382
+ nav.push({ screen: "marketplace-install-scope", context: { marketplace, pluginId: fullId } });
2383
+ }
2384
+ }
2385
+ function handleInstallScopeSelect(value, pluginId, callbacks, nav) {
2386
+ const scope = value;
2387
+ callbacks.install(pluginId, scope).then(() => {
2388
+ nav.notify(`Installed plugin "${pluginId}" (${scope} scope).`);
2389
+ nav.popN(2);
2390
+ }).catch((err) => {
2391
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2392
+ });
2393
+ }
2394
+ function handleInstalledListSelect(value, callbacks, nav) {
2395
+ nav.setConfirm({
2396
+ message: `Uninstall plugin "${value}"?`,
2397
+ onConfirm: () => {
2398
+ nav.setConfirm(void 0);
2399
+ callbacks.uninstall(value).then(() => {
2400
+ nav.notify(`Uninstalled plugin "${value}".`);
2401
+ nav.refresh();
2402
+ }).catch((err) => {
2403
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2404
+ });
2405
+ },
2406
+ onCancel: () => nav.setConfirm(void 0)
2407
+ });
2408
+ }
2409
+ function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
2410
+ if (value === "uninstall") {
2411
+ nav.setConfirm({
2412
+ message: `Uninstall plugin "${pluginId}"?`,
2413
+ onConfirm: () => {
2414
+ nav.setConfirm(void 0);
2415
+ callbacks.uninstall(pluginId).then(() => {
2416
+ nav.notify(`Uninstalled plugin "${pluginId}".`);
2417
+ nav.popN(2);
2418
+ }).catch((err) => {
2419
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2420
+ });
2421
+ },
2422
+ onCancel: () => nav.setConfirm(void 0)
2423
+ });
2424
+ }
2425
+ }
2426
+
2427
+ // src/ui/PluginTUI.tsx
2428
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2429
+ function PluginTUI({ callbacks, onClose, addMessage }) {
2430
+ const [stack, setStack] = (0, import_react15.useState)([{ screen: "main" }]);
2431
+ const [items, setItems] = (0, import_react15.useState)([]);
2432
+ const [loading, setLoading] = (0, import_react15.useState)(false);
2433
+ const [error, setError] = (0, import_react15.useState)();
2434
+ const [confirm, setConfirm] = (0, import_react15.useState)();
2435
+ const [refreshCounter, setRefreshCounter] = (0, import_react15.useState)(0);
2436
+ const current = stack[stack.length - 1] ?? { screen: "main" };
2437
+ const push = (0, import_react15.useCallback)((state) => {
2438
+ setStack((prev) => [...prev, state]);
2439
+ setItems([]);
2440
+ setError(void 0);
2441
+ }, []);
2442
+ const pop = (0, import_react15.useCallback)(() => {
2443
+ setStack((prev) => {
2444
+ if (prev.length <= 1) {
2445
+ onClose();
2446
+ return prev;
2447
+ }
2448
+ return prev.slice(0, -1);
2449
+ });
2450
+ setItems([]);
2451
+ setError(void 0);
2452
+ }, [onClose]);
2453
+ const popN = (0, import_react15.useCallback)(
2454
+ (n) => {
2455
+ setStack((prev) => {
2456
+ const next = prev.slice(0, Math.max(1, prev.length - n));
2457
+ if (next.length === 0) {
2458
+ onClose();
2459
+ return prev;
2460
+ }
2461
+ return next;
2462
+ });
2463
+ setItems([]);
2464
+ setError(void 0);
2465
+ },
2466
+ [onClose]
2467
+ );
2468
+ const notify = (0, import_react15.useCallback)(
2469
+ (content) => {
2470
+ addMessage?.({ role: "system", content });
2471
+ },
2472
+ [addMessage]
2473
+ );
2474
+ const refresh = (0, import_react15.useCallback)(() => {
2475
+ setItems([]);
2476
+ setRefreshCounter((c) => c + 1);
2477
+ }, []);
2478
+ const nav = { push, pop, popN, notify, setConfirm, refresh };
2479
+ (0, import_react15.useEffect)(() => {
2480
+ const screen2 = current.screen;
2481
+ if (screen2 === "marketplace-list") {
2482
+ setLoading(true);
2483
+ callbacks.marketplaceList().then((sources) => {
2484
+ const baseItems = [{ label: "Add Marketplace", value: "__add__" }];
2485
+ const sourceItems = sources.map((s) => ({
2486
+ label: s.name,
2487
+ value: s.name,
2488
+ hint: s.type
2489
+ }));
2490
+ setItems([...baseItems, ...sourceItems]);
2491
+ setLoading(false);
2492
+ }).catch((err) => {
2493
+ setError(err instanceof Error ? err.message : String(err));
2494
+ setLoading(false);
2495
+ });
2496
+ } else if (screen2 === "marketplace-browse") {
2497
+ const marketplace = current.context?.marketplace ?? "";
2498
+ setLoading(true);
2499
+ callbacks.listAvailablePlugins(marketplace).then((plugins) => {
2500
+ setItems(
2501
+ plugins.map((p) => ({
2502
+ label: p.name,
2503
+ value: p.name,
2504
+ hint: p.installed ? "installed" : p.description
2505
+ }))
2506
+ );
2507
+ setLoading(false);
2508
+ }).catch((err) => {
2509
+ setError(err instanceof Error ? err.message : String(err));
2510
+ setLoading(false);
2511
+ });
2512
+ } else if (screen2 === "installed-list") {
2513
+ setLoading(true);
2514
+ callbacks.listInstalled().then((plugins) => {
2515
+ setItems(
2516
+ plugins.map((p) => ({
2517
+ label: p.name,
2518
+ value: p.name,
2519
+ hint: p.description
2520
+ }))
2521
+ );
2522
+ setLoading(false);
2523
+ }).catch((err) => {
2524
+ setError(err instanceof Error ? err.message : String(err));
2525
+ setLoading(false);
2526
+ });
2527
+ }
2528
+ }, [stack.length, current.screen, current.context?.marketplace, callbacks, refreshCounter]);
2529
+ const handleSelect = (0, import_react15.useCallback)(
2530
+ (value) => {
2531
+ const screen2 = current.screen;
2532
+ const ctx = current.context;
2533
+ if (screen2 === "main") handleMainSelect(value, nav);
2534
+ else if (screen2 === "marketplace-list") handleMarketplaceListSelect(value, nav);
2535
+ else if (screen2 === "marketplace-action")
2536
+ handleMarketplaceActionSelect(value, ctx?.marketplace ?? "", callbacks, nav);
2537
+ else if (screen2 === "marketplace-browse")
2538
+ handleMarketplaceBrowseSelect(value, ctx?.marketplace ?? "", items, nav);
2539
+ else if (screen2 === "marketplace-install-scope")
2540
+ handleInstallScopeSelect(value, ctx?.pluginId ?? "", callbacks, nav);
2541
+ else if (screen2 === "installed-list") handleInstalledListSelect(value, callbacks, nav);
2542
+ else if (screen2 === "installed-action")
2543
+ handleInstalledActionSelect(value, ctx?.pluginId ?? "", callbacks, nav);
2544
+ },
2545
+ [current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
2546
+ );
2547
+ const handleTextSubmit = (0, import_react15.useCallback)(
2548
+ (value) => {
2549
+ if (current.screen === "marketplace-add") {
2550
+ callbacks.marketplaceAdd(value).then((name) => {
2551
+ notify(`Added marketplace "${name}" from ${value}.`);
2552
+ pop();
2553
+ }).catch((err) => {
2554
+ notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2555
+ pop();
2556
+ });
2557
+ }
2558
+ },
2559
+ [current.screen, callbacks, notify, pop]
2560
+ );
2561
+ if (confirm) {
2562
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2563
+ ConfirmPrompt,
2564
+ {
2565
+ message: confirm.message,
2566
+ onSelect: (index) => {
2567
+ if (index === 0) confirm.onConfirm();
2568
+ else confirm.onCancel();
2569
+ }
2570
+ }
2571
+ );
2572
+ }
2573
+ const screen = current.screen;
2574
+ if (screen === "marketplace-add") {
2575
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2576
+ TextPrompt,
2577
+ {
2578
+ title: "Add Marketplace Source",
2579
+ placeholder: "owner/repo or git URL",
2580
+ onSubmit: handleTextSubmit,
2581
+ onCancel: pop,
2582
+ validate: (v) => !v.includes("/") ? "Must be owner/repo or a git URL" : void 0
2583
+ }
2584
+ );
2585
+ }
2586
+ if (screen === "marketplace-action") {
2587
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2588
+ MenuSelect,
2589
+ {
2590
+ title: `Marketplace: ${current.context?.marketplace ?? ""}`,
2591
+ items: [
2592
+ { label: "Browse plugins", value: "browse" },
2593
+ { label: "Update", value: "update" },
2594
+ { label: "Remove", value: "remove" }
2595
+ ],
2596
+ onSelect: handleSelect,
2597
+ onBack: pop
2598
+ },
2599
+ stack.length
2600
+ );
2601
+ }
2602
+ if (screen === "marketplace-install-scope") {
2603
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2604
+ MenuSelect,
2605
+ {
2606
+ title: `Install scope for "${current.context?.pluginId ?? ""}"`,
2607
+ items: [
2608
+ { label: "User scope", value: "user" },
2609
+ { label: "Project scope", value: "project" }
2610
+ ],
2611
+ onSelect: handleSelect,
2612
+ onBack: pop
2613
+ },
2614
+ stack.length
2615
+ );
2616
+ }
2617
+ if (screen === "installed-action") {
2618
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2619
+ MenuSelect,
2620
+ {
2621
+ title: `Plugin: ${current.context?.pluginId ?? ""}`,
2622
+ items: [{ label: "Uninstall", value: "uninstall" }],
2623
+ onSelect: handleSelect,
2624
+ onBack: pop
2625
+ },
2626
+ stack.length
2627
+ );
2628
+ }
2629
+ const titleMap = {
2630
+ main: "Plugin Management",
2631
+ "marketplace-list": "Marketplace",
2632
+ "marketplace-browse": `Browse: ${current.context?.marketplace ?? ""}`,
2633
+ "installed-list": "Installed Plugins"
2634
+ };
2635
+ const staticItemsMap = {
2636
+ main: [
2637
+ { label: "Marketplace", value: "marketplace" },
2638
+ { label: "Installed Plugins", value: "installed" }
2639
+ ]
2640
+ };
2641
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2642
+ MenuSelect,
2643
+ {
2644
+ title: titleMap[screen] ?? "Plugin Management",
2645
+ items: staticItemsMap[screen] ?? items,
2646
+ onSelect: handleSelect,
2647
+ onBack: pop,
2648
+ loading,
2649
+ error
2650
+ },
2651
+ `${screen}-${stack.length}-${refreshCounter}`
2652
+ );
2653
+ }
2654
+
2655
+ // src/ui/App.tsx
2656
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2154
2657
  var EXIT_DELAY_MS2 = 500;
2155
2658
  function mergeHooksIntoConfig(configHooks, pluginHooks) {
2156
2659
  const pluginKeys = Object.keys(pluginHooks);
@@ -2169,7 +2672,7 @@ function mergeHooksIntoConfig(configHooks, pluginHooks) {
2169
2672
  return merged;
2170
2673
  }
2171
2674
  function App(props) {
2172
- const { exit } = (0, import_ink11.useApp)();
2675
+ const { exit } = (0, import_ink13.useApp)();
2173
2676
  const { registry, pluginHooks } = useCommandRegistry(props.cwd ?? process.cwd());
2174
2677
  const configWithPluginHooks = {
2175
2678
  ...props.config,
@@ -2182,15 +2685,16 @@ function App(props) {
2182
2685
  { ...props, config: configWithPluginHooks }
2183
2686
  );
2184
2687
  const { messages, setMessages, addMessage } = useMessages();
2185
- const [isThinking, setIsThinking] = (0, import_react13.useState)(false);
2688
+ const [isThinking, setIsThinking] = (0, import_react16.useState)(false);
2186
2689
  const initialCtx = session.getContextState();
2187
- const [contextState, setContextState] = (0, import_react13.useState)({
2690
+ const [contextState, setContextState] = (0, import_react16.useState)({
2188
2691
  percentage: initialCtx.usedPercentage,
2189
2692
  usedTokens: initialCtx.usedTokens,
2190
2693
  maxTokens: initialCtx.maxTokens
2191
2694
  });
2192
- const pendingModelChangeRef = (0, import_react13.useRef)(null);
2193
- const [pendingModelId, setPendingModelId] = (0, import_react13.useState)(null);
2695
+ const pendingModelChangeRef = (0, import_react16.useRef)(null);
2696
+ const [pendingModelId, setPendingModelId] = (0, import_react16.useState)(null);
2697
+ const [showPluginTUI, setShowPluginTUI] = (0, import_react16.useState)(false);
2194
2698
  const pluginCallbacks = usePluginCallbacks(props.cwd ?? process.cwd());
2195
2699
  const handleSlashCommand = useSlashCommands(
2196
2700
  session,
@@ -2200,7 +2704,8 @@ function App(props) {
2200
2704
  registry,
2201
2705
  pendingModelChangeRef,
2202
2706
  setPendingModelId,
2203
- pluginCallbacks
2707
+ pluginCallbacks,
2708
+ setShowPluginTUI
2204
2709
  );
2205
2710
  const handleSubmit = useSubmitHandler(
2206
2711
  session,
@@ -2211,36 +2716,36 @@ function App(props) {
2211
2716
  setContextState,
2212
2717
  registry
2213
2718
  );
2214
- (0, import_ink11.useInput)(
2719
+ (0, import_ink13.useInput)(
2215
2720
  (_input, key) => {
2216
2721
  if (key.ctrl && _input === "c") exit();
2217
2722
  if (key.escape && isThinking) session.abort();
2218
2723
  },
2219
- { isActive: !permissionRequest }
2724
+ { isActive: !permissionRequest && !showPluginTUI }
2220
2725
  );
2221
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", children: [
2222
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2223
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "cyan", bold: true, children: `
2726
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", children: [
2727
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2728
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { color: "cyan", bold: true, children: `
2224
2729
  ____ ___ ____ ___ _____ _
2225
2730
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
2226
2731
  | |_) | | | | _ \\| | | || | / _ \\
2227
2732
  | _ <| |_| | |_) | |_| || |/ ___ \\
2228
2733
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
2229
2734
  ` }),
2230
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { dimColor: true, children: [
2735
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Text, { dimColor: true, children: [
2231
2736
  " v",
2232
2737
  props.version ?? "0.0.0"
2233
2738
  ] })
2234
2739
  ] }),
2235
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
2236
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MessageList, { messages }),
2237
- isThinking && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
2740
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
2741
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageList, { messages }),
2742
+ isThinking && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
2238
2743
  ] }),
2239
- permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PermissionPrompt, { request: permissionRequest }),
2240
- pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2744
+ permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PermissionPrompt, { request: permissionRequest }),
2745
+ pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2241
2746
  ConfirmPrompt,
2242
2747
  {
2243
- message: `Change model to ${(0, import_agent_core3.getModelName)(pendingModelId)}? This will restart the session.`,
2748
+ message: `Change model to ${(0, import_agent_core6.getModelName)(pendingModelId)}? This will restart the session.`,
2244
2749
  onSelect: (index) => {
2245
2750
  setPendingModelId(null);
2246
2751
  pendingModelChangeRef.current = null;
@@ -2248,28 +2753,38 @@ function App(props) {
2248
2753
  try {
2249
2754
  const settingsPath = getUserSettingsPath();
2250
2755
  updateModelInSettings(settingsPath, pendingModelId);
2251
- addMessage({
2252
- role: "system",
2253
- content: `Model changed to ${(0, import_agent_core3.getModelName)(pendingModelId)}. Restarting...`
2254
- });
2756
+ addMessage(
2757
+ (0, import_agent_core7.createSystemMessage)(
2758
+ `Model changed to ${(0, import_agent_core6.getModelName)(pendingModelId)}. Restarting...`
2759
+ )
2760
+ );
2255
2761
  setTimeout(() => exit(), EXIT_DELAY_MS2);
2256
2762
  } catch (err) {
2257
- addMessage({
2258
- role: "system",
2259
- content: `Failed: ${err instanceof Error ? err.message : String(err)}`
2260
- });
2763
+ addMessage(
2764
+ (0, import_agent_core7.createSystemMessage)(
2765
+ `Failed: ${err instanceof Error ? err.message : String(err)}`
2766
+ )
2767
+ );
2261
2768
  }
2262
2769
  } else {
2263
- addMessage({ role: "system", content: "Model change cancelled." });
2770
+ addMessage((0, import_agent_core7.createSystemMessage)("Model change cancelled."));
2264
2771
  }
2265
2772
  }
2266
2773
  }
2267
2774
  ),
2268
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2775
+ showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2776
+ PluginTUI,
2777
+ {
2778
+ callbacks: pluginCallbacks,
2779
+ onClose: () => setShowPluginTUI(false),
2780
+ addMessage: (msg) => addMessage((0, import_agent_core7.createSystemMessage)(msg.content))
2781
+ }
2782
+ ),
2783
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2269
2784
  StatusBar,
2270
2785
  {
2271
2786
  permissionMode: session.getPermissionMode(),
2272
- modelName: (0, import_agent_core3.getModelName)(props.config.provider.model),
2787
+ modelName: (0, import_agent_core6.getModelName)(props.config.provider.model),
2273
2788
  sessionId: session.getSessionId(),
2274
2789
  messageCount: messages.length,
2275
2790
  isThinking,
@@ -2278,20 +2793,20 @@ function App(props) {
2278
2793
  contextMaxTokens: contextState.maxTokens
2279
2794
  }
2280
2795
  ),
2281
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2796
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2282
2797
  InputArea,
2283
2798
  {
2284
2799
  onSubmit: handleSubmit,
2285
- isDisabled: isThinking || !!permissionRequest,
2800
+ isDisabled: isThinking || !!permissionRequest || showPluginTUI,
2286
2801
  registry
2287
2802
  }
2288
2803
  ),
2289
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { children: " " })
2804
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { children: " " })
2290
2805
  ] });
2291
2806
  }
2292
2807
 
2293
2808
  // src/ui/render.tsx
2294
- var import_jsx_runtime12 = require("react/jsx-runtime");
2809
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2295
2810
  function renderApp(options) {
2296
2811
  process.on("unhandledRejection", (reason) => {
2297
2812
  process.stderr.write(`
@@ -2302,7 +2817,7 @@ function renderApp(options) {
2302
2817
  `);
2303
2818
  }
2304
2819
  });
2305
- const instance = (0, import_ink12.render)(/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(App, { ...options }), {
2820
+ const instance = (0, import_ink14.render)(/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(App, { ...options }), {
2306
2821
  exitOnCtrlC: true
2307
2822
  });
2308
2823
  instance.waitUntilExit().catch((err) => {