@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.
package/dist/node/bin.cjs CHANGED
@@ -163,12 +163,13 @@ var PrintTerminal = class {
163
163
  };
164
164
 
165
165
  // src/ui/render.tsx
166
- var import_ink12 = require("ink");
166
+ var import_ink14 = require("ink");
167
167
 
168
168
  // src/ui/App.tsx
169
- var import_react13 = require("react");
170
- var import_ink11 = require("ink");
171
- var import_agent_core3 = require("@robota-sdk/agent-core");
169
+ var import_react16 = require("react");
170
+ var import_ink13 = require("ink");
171
+ var import_agent_core6 = require("@robota-sdk/agent-core");
172
+ var import_agent_core7 = require("@robota-sdk/agent-core");
172
173
 
173
174
  // src/ui/hooks/useSession.ts
174
175
  var import_react = require("react");
@@ -264,6 +265,7 @@ var NOOP_TERMINAL = {
264
265
  function useSession(props) {
265
266
  const [permissionRequest, setPermissionRequest] = (0, import_react.useState)(null);
266
267
  const [streamingText, setStreamingText] = (0, import_react.useState)("");
268
+ const streamingTextRef = (0, import_react.useRef)("");
267
269
  const [activeTools, setActiveTools] = (0, import_react.useState)([]);
268
270
  const permissionQueueRef = (0, import_react.useRef)([]);
269
271
  const processingRef = (0, import_react.useRef)(false);
@@ -295,8 +297,15 @@ function useSession(props) {
295
297
  processNextPermission();
296
298
  });
297
299
  };
300
+ let flushTimer = null;
298
301
  const onTextDelta = (delta) => {
299
- setStreamingText((prev) => prev + delta);
302
+ streamingTextRef.current += delta;
303
+ if (!flushTimer) {
304
+ flushTimer = setTimeout(() => {
305
+ setStreamingText(streamingTextRef.current);
306
+ flushTimer = null;
307
+ }, 16);
308
+ }
300
309
  };
301
310
  const onToolExecution = (event) => {
302
311
  if (event.type === "start") {
@@ -375,6 +384,7 @@ function useSession(props) {
375
384
  }
376
385
  const clearStreamingText = (0, import_react.useCallback)(() => {
377
386
  setStreamingText("");
387
+ streamingTextRef.current = "";
378
388
  setActiveTools([]);
379
389
  }, []);
380
390
  return {
@@ -389,16 +399,11 @@ function useSession(props) {
389
399
  // src/ui/hooks/useMessages.ts
390
400
  var import_react2 = require("react");
391
401
  var MAX_RENDERED_MESSAGES = 100;
392
- var msgIdCounter = 0;
393
- function nextId() {
394
- msgIdCounter += 1;
395
- return `msg_${msgIdCounter}`;
396
- }
397
402
  function useMessages() {
398
403
  const [messages, setMessages] = (0, import_react2.useState)([]);
399
404
  const addMessage = (0, import_react2.useCallback)((msg) => {
400
405
  setMessages((prev) => {
401
- const updated = [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }];
406
+ const updated = [...prev, msg];
402
407
  if (updated.length > MAX_RENDERED_MESSAGES) {
403
408
  return updated.slice(-MAX_RENDERED_MESSAGES);
404
409
  }
@@ -410,6 +415,7 @@ function useMessages() {
410
415
 
411
416
  // src/ui/hooks/useSlashCommands.ts
412
417
  var import_react3 = require("react");
418
+ var import_agent_core = require("@robota-sdk/agent-core");
413
419
 
414
420
  // src/commands/slash-executor.ts
415
421
  var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
@@ -520,18 +526,9 @@ async function handlePluginCommand(args, addMessage, callbacks) {
520
526
  try {
521
527
  switch (subcommand) {
522
528
  case "":
523
- case void 0: {
524
- const plugins = await callbacks.listInstalled();
525
- if (plugins.length === 0) {
526
- addMessage({ role: "system", content: "No plugins installed." });
527
- } else {
528
- const lines = plugins.map(
529
- (p) => ` ${p.name} \u2014 ${p.description} [${p.enabled ? "enabled" : "disabled"}]`
530
- );
531
- addMessage({ role: "system", content: `Installed plugins:
532
- ${lines.join("\n")}` });
533
- }
534
- return { handled: true };
529
+ case void 0:
530
+ case "manage": {
531
+ return { handled: true, triggerPluginTUI: true };
535
532
  }
536
533
  case "install": {
537
534
  if (!subArgs) {
@@ -678,18 +675,21 @@ async function executeSlashCommand(cmd, args, session, addMessage, clearMessages
678
675
 
679
676
  // src/ui/hooks/useSlashCommands.ts
680
677
  var EXIT_DELAY_MS = 500;
681
- function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId, pluginCallbacks) {
678
+ function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId, pluginCallbacks, setShowPluginTUI) {
682
679
  return (0, import_react3.useCallback)(
683
680
  async (input) => {
684
681
  const parts = input.slice(1).split(/\s+/);
685
682
  const cmd = parts[0]?.toLowerCase() ?? "";
686
683
  const args = parts.slice(1).join(" ");
687
684
  const clearMessages = () => setMessages([]);
685
+ const slashAddMessage = (msg) => {
686
+ addMessage((0, import_agent_core.createSystemMessage)(msg.content));
687
+ };
688
688
  const result = await executeSlashCommand(
689
689
  cmd,
690
690
  args,
691
691
  session,
692
- addMessage,
692
+ slashAddMessage,
693
693
  clearMessages,
694
694
  registry,
695
695
  pluginCallbacks
@@ -698,6 +698,9 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
698
698
  pendingModelChangeRef.current = result.pendingModelId;
699
699
  setPendingModelId(result.pendingModelId);
700
700
  }
701
+ if (result.triggerPluginTUI) {
702
+ setShowPluginTUI?.(true);
703
+ }
701
704
  if (result.exitRequested) {
702
705
  setTimeout(() => exit(), EXIT_DELAY_MS);
703
706
  }
@@ -711,14 +714,17 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
711
714
  registry,
712
715
  pendingModelChangeRef,
713
716
  setPendingModelId,
714
- pluginCallbacks
717
+ pluginCallbacks,
718
+ setShowPluginTUI
715
719
  ]
716
720
  );
717
721
  }
718
722
 
719
723
  // src/ui/hooks/useSubmitHandler.ts
720
724
  var import_react4 = require("react");
725
+ var import_node_crypto = require("crypto");
721
726
  var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
727
+ var import_agent_core2 = require("@robota-sdk/agent-core");
722
728
 
723
729
  // src/utils/tool-call-extractor.ts
724
730
  var TOOL_ARG_MAX_LENGTH = 80;
@@ -875,21 +881,50 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
875
881
  historyBefore
876
882
  );
877
883
  if (toolSummaries.length > 0) {
878
- addMessage({
879
- role: "tool",
880
- content: JSON.stringify(toolSummaries),
881
- toolName: `${toolSummaries.length} tools`
882
- });
884
+ addMessage(
885
+ (0, import_agent_core2.createToolMessage)(JSON.stringify(toolSummaries), {
886
+ toolCallId: (0, import_node_crypto.randomUUID)(),
887
+ name: `${toolSummaries.length} tools`
888
+ })
889
+ );
883
890
  }
884
- addMessage({ role: "assistant", content: response || "(empty response)" });
891
+ addMessage((0, import_agent_core2.createAssistantMessage)(response || "(empty response)"));
885
892
  syncContextState(session, setContextState);
886
893
  } catch (err) {
887
894
  clearStreamingText();
888
- if (err instanceof DOMException && err.name === "AbortError") {
889
- addMessage({ role: "system", content: "Cancelled." });
895
+ const isAbortError = err instanceof DOMException && err.name === "AbortError" || err instanceof Error && (err.message.includes("aborted") || err.message.includes("abort"));
896
+ if (isAbortError) {
897
+ const history = session.getHistory();
898
+ const toolSummaries = extractToolCallsWithDiff(
899
+ history,
900
+ historyBefore
901
+ );
902
+ if (toolSummaries.length > 0) {
903
+ addMessage(
904
+ (0, import_agent_core2.createToolMessage)(JSON.stringify(toolSummaries), {
905
+ toolCallId: (0, import_node_crypto.randomUUID)(),
906
+ name: `${toolSummaries.length} tools`
907
+ })
908
+ );
909
+ }
910
+ const assistantParts = [];
911
+ let lastAssistantState = "complete";
912
+ for (let i = historyBefore; i < history.length; i++) {
913
+ const msg = history[i];
914
+ if (msg && msg.role === "assistant" && msg.content) {
915
+ assistantParts.push(msg.content);
916
+ if (msg.state === "interrupted") lastAssistantState = "interrupted";
917
+ }
918
+ }
919
+ if (assistantParts.length > 0) {
920
+ addMessage(
921
+ (0, import_agent_core2.createAssistantMessage)(assistantParts.join("\n\n"), { state: lastAssistantState })
922
+ );
923
+ }
924
+ addMessage((0, import_agent_core2.createSystemMessage)("Interrupted by user."));
890
925
  } else {
891
926
  const errMsg = err instanceof Error ? err.message : String(err);
892
- addMessage({ role: "system", content: `Error: ${errMsg}` });
927
+ addMessage((0, import_agent_core2.createSystemMessage)(`Error: ${errMsg}`));
893
928
  }
894
929
  } finally {
895
930
  setIsThinking(false);
@@ -942,7 +977,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
942
977
  const runInFork = createForkRunner(session);
943
978
  const result = await executeSkill(skill, args, { runInFork });
944
979
  if (result.mode === "fork") {
945
- addMessage({ role: "assistant", content: result.result ?? "(empty response)" });
980
+ addMessage((0, import_agent_core2.createAssistantMessage)(result.result ?? "(empty response)"));
946
981
  syncContextState(session, setContextState);
947
982
  return;
948
983
  }
@@ -977,7 +1012,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
977
1012
  hookInput
978
1013
  );
979
1014
  }
980
- addMessage({ role: "user", content: input });
1015
+ addMessage((0, import_agent_core2.createUserMessage)(input));
981
1016
  return runSessionPrompt(
982
1017
  input,
983
1018
  session,
@@ -1044,16 +1079,16 @@ var CommandRegistry = class {
1044
1079
  };
1045
1080
 
1046
1081
  // src/commands/builtin-source.ts
1047
- var import_agent_core = require("@robota-sdk/agent-core");
1082
+ var import_agent_core3 = require("@robota-sdk/agent-core");
1048
1083
  function buildModelSubcommands() {
1049
1084
  const seen = /* @__PURE__ */ new Set();
1050
1085
  const commands = [];
1051
- for (const model of Object.values(import_agent_core.CLAUDE_MODELS)) {
1086
+ for (const model of Object.values(import_agent_core3.CLAUDE_MODELS)) {
1052
1087
  if (seen.has(model.name)) continue;
1053
1088
  seen.add(model.name);
1054
1089
  commands.push({
1055
1090
  name: model.id,
1056
- description: `${model.name} (${(0, import_agent_core.formatTokenCount)(model.contextWindow).toUpperCase()})`,
1091
+ description: `${model.name} (${(0, import_agent_core3.formatTokenCount)(model.contextWindow).toUpperCase()})`,
1057
1092
  source: "builtin"
1058
1093
  });
1059
1094
  }
@@ -1095,22 +1130,7 @@ function createBuiltinCommands() {
1095
1130
  { name: "cost", description: "Show session info", source: "builtin" },
1096
1131
  { name: "context", description: "Context window info", source: "builtin" },
1097
1132
  { name: "permissions", description: "Permission rules", source: "builtin" },
1098
- {
1099
- name: "plugin",
1100
- description: "Manage plugins",
1101
- source: "builtin",
1102
- subcommands: [
1103
- { name: "install", description: "Install a plugin (name@marketplace)", source: "builtin" },
1104
- {
1105
- name: "uninstall",
1106
- description: "Uninstall a plugin (name@marketplace)",
1107
- source: "builtin"
1108
- },
1109
- { name: "enable", description: "Enable a plugin (name@marketplace)", source: "builtin" },
1110
- { name: "disable", description: "Disable a plugin (name@marketplace)", source: "builtin" },
1111
- { name: "marketplace", description: "Manage marketplace sources", source: "builtin" }
1112
- ]
1113
- },
1133
+ { name: "plugin", description: "Manage plugins", source: "builtin" },
1114
1134
  { name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
1115
1135
  { name: "reset", description: "Delete settings and exit", source: "builtin" },
1116
1136
  { name: "exit", description: "Exit CLI", source: "builtin" }
@@ -1377,18 +1397,50 @@ function usePluginCallbacks(cwd) {
1377
1397
  return {
1378
1398
  listInstalled: async () => {
1379
1399
  const plugins = await loader.loadAll();
1380
- return plugins.map((p) => ({
1381
- name: p.manifest.name,
1382
- description: p.manifest.description,
1383
- enabled: true
1400
+ const enabledMap = settingsStore.getEnabledPlugins();
1401
+ return plugins.map((p) => {
1402
+ const parts = p.pluginDir.split("/");
1403
+ const cacheIdx = parts.indexOf("cache");
1404
+ const marketplaceName = cacheIdx >= 0 ? parts[cacheIdx + 1] : "";
1405
+ const fullId = marketplaceName ? `${p.manifest.name}@${marketplaceName}` : p.manifest.name;
1406
+ return {
1407
+ name: fullId,
1408
+ description: p.manifest.description,
1409
+ enabled: enabledMap[fullId] !== false && enabledMap[p.manifest.name] !== false
1410
+ };
1411
+ });
1412
+ },
1413
+ listAvailablePlugins: async (marketplaceName) => {
1414
+ let manifest;
1415
+ try {
1416
+ manifest = marketplace.fetchManifest(marketplaceName);
1417
+ } catch {
1418
+ return [];
1419
+ }
1420
+ const installed = installer.getInstalledPlugins();
1421
+ const installedNames = new Set(Object.values(installed).map((r) => r.pluginName));
1422
+ return manifest.plugins.map((p) => ({
1423
+ name: p.name,
1424
+ description: p.description,
1425
+ installed: installedNames.has(p.name)
1384
1426
  }));
1385
1427
  },
1386
- install: async (pluginId) => {
1428
+ install: async (pluginId, scope) => {
1387
1429
  const [name, marketplaceName] = pluginId.split("@");
1388
1430
  if (!name || !marketplaceName) {
1389
1431
  throw new Error("Plugin ID must be in format: name@marketplace");
1390
1432
  }
1391
- await installer.install(name, marketplaceName);
1433
+ if (scope === "project") {
1434
+ const projectPluginsDir = (0, import_node_path4.join)(cwd, ".robota", "plugins");
1435
+ const projectInstaller = new import_agent_sdk4.BundlePluginInstaller({
1436
+ pluginsDir: projectPluginsDir,
1437
+ settingsStore,
1438
+ marketplaceClient: marketplace
1439
+ });
1440
+ await projectInstaller.install(name, marketplaceName);
1441
+ } else {
1442
+ await installer.install(name, marketplaceName);
1443
+ }
1392
1444
  },
1393
1445
  uninstall: async (pluginId) => {
1394
1446
  await installer.uninstall(pluginId);
@@ -1431,6 +1483,7 @@ function usePluginCallbacks(cwd) {
1431
1483
  // src/ui/MessageList.tsx
1432
1484
  var import_react7 = __toESM(require("react"), 1);
1433
1485
  var import_ink2 = require("ink");
1486
+ var import_agent_core4 = require("@robota-sdk/agent-core");
1434
1487
 
1435
1488
  // src/ui/render-markdown.ts
1436
1489
  var import_marked = require("marked");
@@ -1516,9 +1569,14 @@ function RoleLabel({ role }) {
1516
1569
  }
1517
1570
  }
1518
1571
  function ToolMessage({ message }) {
1572
+ if (!(0, import_agent_core4.isToolMessage)(message)) {
1573
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
1574
+ }
1575
+ const toolName = message.name;
1576
+ const content = message.content;
1519
1577
  let summaries = null;
1520
1578
  try {
1521
- const parsed = JSON.parse(message.content);
1579
+ const parsed = JSON.parse(content);
1522
1580
  if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0].line === "string") {
1523
1581
  summaries = parsed;
1524
1582
  }
@@ -1531,9 +1589,9 @@ function ToolMessage({ message }) {
1531
1589
  "Tool:",
1532
1590
  " "
1533
1591
  ] }),
1534
- message.toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1592
+ toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1535
1593
  "[",
1536
- message.toolName,
1594
+ toolName,
1537
1595
  "]"
1538
1596
  ] })
1539
1597
  ] }),
@@ -1549,16 +1607,16 @@ function ToolMessage({ message }) {
1549
1607
  ] }, i))
1550
1608
  ] });
1551
1609
  }
1552
- const lines = message.content.split("\n").filter((l) => l.trim());
1610
+ const lines = content.split("\n").filter((l) => l.trim());
1553
1611
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
1554
1612
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { children: [
1555
1613
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", bold: true, children: [
1556
1614
  "Tool:",
1557
1615
  " "
1558
1616
  ] }),
1559
- message.toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1617
+ toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1560
1618
  "[",
1561
- message.toolName,
1619
+ toolName,
1562
1620
  "]"
1563
1621
  ] })
1564
1622
  ] }),
@@ -1574,21 +1632,15 @@ function ToolMessage({ message }) {
1574
1632
  var MessageItem = import_react7.default.memo(function MessageItem2({
1575
1633
  message
1576
1634
  }) {
1577
- if (message.role === "tool") {
1635
+ if ((0, import_agent_core4.isToolMessage)(message)) {
1578
1636
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolMessage, { message });
1579
1637
  }
1638
+ const content = message.content ?? "";
1639
+ const isInterrupted = message.state === "interrupted";
1580
1640
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
1581
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { children: [
1582
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }),
1583
- message.toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "magenta", dimColor: true, children: [
1584
- "[",
1585
- message.toolName,
1586
- "]",
1587
- " "
1588
- ] })
1589
- ] }),
1641
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }) }),
1590
1642
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
1591
- /* @__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 }) })
1643
+ /* @__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 }) })
1592
1644
  ] });
1593
1645
  });
1594
1646
  function MessageList({ messages }) {
@@ -1597,7 +1649,7 @@ function MessageList({ messages }) {
1597
1649
 
1598
1650
  // src/ui/StatusBar.tsx
1599
1651
  var import_ink3 = require("ink");
1600
- var import_agent_core2 = require("@robota-sdk/agent-core");
1652
+ var import_agent_core5 = require("@robota-sdk/agent-core");
1601
1653
  var import_jsx_runtime3 = require("react/jsx-runtime");
1602
1654
  var CONTEXT_YELLOW_THRESHOLD = 70;
1603
1655
  var CONTEXT_RED_THRESHOLD = 90;
@@ -1637,9 +1689,9 @@ function StatusBar({
1637
1689
  "Context: ",
1638
1690
  Math.round(contextPercentage),
1639
1691
  "% (",
1640
- (0, import_agent_core2.formatTokenCount)(contextUsedTokens),
1692
+ (0, import_agent_core5.formatTokenCount)(contextUsedTokens),
1641
1693
  "/",
1642
- (0, import_agent_core2.formatTokenCount)(contextMaxTokens),
1694
+ (0, import_agent_core5.formatTokenCount)(contextMaxTokens),
1643
1695
  ")"
1644
1696
  ] })
1645
1697
  ] }),
@@ -1967,7 +2019,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1967
2019
  isSubcommandMode
1968
2020
  }
1969
2021
  ),
1970
- /* @__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: [
2022
+ /* @__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: [
1971
2023
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "green", bold: true, children: "> " }),
1972
2024
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1973
2025
  CjkTextInput,
@@ -2103,7 +2155,7 @@ function StreamingIndicator({ text, activeTools }) {
2103
2155
  const hasTools = activeTools.length > 0;
2104
2156
  const hasText = text.length > 0;
2105
2157
  if (!hasTools && !hasText) {
2106
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "yellow", children: "Thinking..." });
2158
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, {});
2107
2159
  }
2108
2160
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
2109
2161
  hasTools && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", marginBottom: 1, children: [
@@ -2133,8 +2185,459 @@ function StreamingIndicator({ text, activeTools }) {
2133
2185
  ] });
2134
2186
  }
2135
2187
 
2136
- // src/ui/App.tsx
2188
+ // src/ui/PluginTUI.tsx
2189
+ var import_react15 = require("react");
2190
+
2191
+ // src/ui/MenuSelect.tsx
2192
+ var import_react13 = require("react");
2193
+ var import_ink11 = require("ink");
2137
2194
  var import_jsx_runtime11 = require("react/jsx-runtime");
2195
+ function MenuSelect({
2196
+ title,
2197
+ items,
2198
+ onSelect,
2199
+ onBack,
2200
+ loading,
2201
+ error
2202
+ }) {
2203
+ const [selected, setSelected] = (0, import_react13.useState)(0);
2204
+ const selectedRef = (0, import_react13.useRef)(0);
2205
+ const resolvedRef = (0, import_react13.useRef)(false);
2206
+ const doSelect = (0, import_react13.useCallback)(
2207
+ (index) => {
2208
+ if (resolvedRef.current || items.length === 0) return;
2209
+ resolvedRef.current = true;
2210
+ onSelect(items[index].value);
2211
+ },
2212
+ [items, onSelect]
2213
+ );
2214
+ (0, import_ink11.useInput)((input, key) => {
2215
+ if (resolvedRef.current) return;
2216
+ if (key.escape) {
2217
+ resolvedRef.current = true;
2218
+ onBack();
2219
+ return;
2220
+ }
2221
+ if (loading || error || items.length === 0) return;
2222
+ if (key.upArrow) {
2223
+ const next = selectedRef.current > 0 ? selectedRef.current - 1 : selectedRef.current;
2224
+ selectedRef.current = next;
2225
+ setSelected(next);
2226
+ } else if (key.downArrow) {
2227
+ const next = selectedRef.current < items.length - 1 ? selectedRef.current + 1 : selectedRef.current;
2228
+ selectedRef.current = next;
2229
+ setSelected(next);
2230
+ } else if (key.return) {
2231
+ doSelect(selectedRef.current);
2232
+ }
2233
+ });
2234
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2235
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "yellow", bold: true, children: title }),
2236
+ 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..." }) }),
2237
+ error && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { marginTop: 1, flexDirection: "column", children: [
2238
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "red", children: error }),
2239
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: "Press Esc to go back" })
2240
+ ] }),
2241
+ !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: [
2242
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
2243
+ i === selected ? "> " : " ",
2244
+ item.label
2245
+ ] }),
2246
+ item.hint && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { dimColor: true, children: [
2247
+ " ",
2248
+ item.hint
2249
+ ] })
2250
+ ] }, item.value)) }),
2251
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
2252
+ ] });
2253
+ }
2254
+
2255
+ // src/ui/TextPrompt.tsx
2256
+ var import_react14 = require("react");
2257
+ var import_ink12 = require("ink");
2258
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2259
+ function TextPrompt({
2260
+ title,
2261
+ placeholder,
2262
+ onSubmit,
2263
+ onCancel,
2264
+ validate
2265
+ }) {
2266
+ const [value, setValue] = (0, import_react14.useState)("");
2267
+ const [error, setError] = (0, import_react14.useState)();
2268
+ const resolvedRef = (0, import_react14.useRef)(false);
2269
+ const valueRef = (0, import_react14.useRef)("");
2270
+ const handleSubmit = (0, import_react14.useCallback)(() => {
2271
+ if (resolvedRef.current) return;
2272
+ const trimmed = valueRef.current.trim();
2273
+ if (!trimmed) return;
2274
+ if (validate) {
2275
+ const err = validate(trimmed);
2276
+ if (err) {
2277
+ setError(err);
2278
+ return;
2279
+ }
2280
+ }
2281
+ resolvedRef.current = true;
2282
+ onSubmit(trimmed);
2283
+ }, [validate, onSubmit]);
2284
+ (0, import_ink12.useInput)((input, key) => {
2285
+ if (resolvedRef.current) return;
2286
+ if (key.escape) {
2287
+ resolvedRef.current = true;
2288
+ onCancel();
2289
+ return;
2290
+ }
2291
+ if (key.return) {
2292
+ handleSubmit();
2293
+ return;
2294
+ }
2295
+ if (key.backspace || key.delete) {
2296
+ valueRef.current = valueRef.current.slice(0, -1);
2297
+ setValue(valueRef.current);
2298
+ setError(void 0);
2299
+ return;
2300
+ }
2301
+ if (input && !key.ctrl && !key.meta) {
2302
+ valueRef.current = valueRef.current + input;
2303
+ setValue(valueRef.current);
2304
+ setError(void 0);
2305
+ }
2306
+ });
2307
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2308
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "yellow", bold: true, children: title }),
2309
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { marginTop: 1, children: [
2310
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", children: "> " }),
2311
+ 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,
2312
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", children: "\u2588" })
2313
+ ] }),
2314
+ error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "red", children: error }),
2315
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { dimColor: true, children: " Enter Submit Esc Cancel" })
2316
+ ] });
2317
+ }
2318
+
2319
+ // src/ui/plugin-tui-handlers.ts
2320
+ function handleMainSelect(value, nav) {
2321
+ if (value === "marketplace") {
2322
+ nav.push({ screen: "marketplace-list" });
2323
+ } else if (value === "installed") {
2324
+ nav.push({ screen: "installed-list" });
2325
+ }
2326
+ }
2327
+ function handleMarketplaceListSelect(value, nav) {
2328
+ if (value === "__add__") {
2329
+ nav.push({ screen: "marketplace-add" });
2330
+ } else {
2331
+ nav.push({ screen: "marketplace-action", context: { marketplace: value } });
2332
+ }
2333
+ }
2334
+ function handleMarketplaceActionSelect(value, marketplace, callbacks, nav) {
2335
+ if (value === "browse") {
2336
+ nav.push({ screen: "marketplace-browse", context: { marketplace } });
2337
+ } else if (value === "update") {
2338
+ callbacks.marketplaceUpdate(marketplace).then(() => {
2339
+ nav.notify(`Updated marketplace "${marketplace}".`);
2340
+ nav.pop();
2341
+ }).catch((err) => {
2342
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2343
+ });
2344
+ } else if (value === "remove") {
2345
+ nav.setConfirm({
2346
+ message: `Remove marketplace "${marketplace}" and all its plugins?`,
2347
+ onConfirm: () => {
2348
+ nav.setConfirm(void 0);
2349
+ callbacks.marketplaceRemove(marketplace).then(() => {
2350
+ nav.notify(`Removed marketplace "${marketplace}".`);
2351
+ nav.popN(2);
2352
+ }).catch((err) => {
2353
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2354
+ });
2355
+ },
2356
+ onCancel: () => nav.setConfirm(void 0)
2357
+ });
2358
+ }
2359
+ }
2360
+ function handleMarketplaceBrowseSelect(value, marketplace, items, nav) {
2361
+ const fullId = `${value}@${marketplace}`;
2362
+ const item = items.find((i) => i.value === value);
2363
+ if (item?.hint === "installed") {
2364
+ nav.push({ screen: "installed-action", context: { pluginId: fullId } });
2365
+ } else {
2366
+ nav.push({ screen: "marketplace-install-scope", context: { marketplace, pluginId: fullId } });
2367
+ }
2368
+ }
2369
+ function handleInstallScopeSelect(value, pluginId, callbacks, nav) {
2370
+ const scope = value;
2371
+ callbacks.install(pluginId, scope).then(() => {
2372
+ nav.notify(`Installed plugin "${pluginId}" (${scope} scope).`);
2373
+ nav.popN(2);
2374
+ }).catch((err) => {
2375
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2376
+ });
2377
+ }
2378
+ function handleInstalledListSelect(value, callbacks, nav) {
2379
+ nav.setConfirm({
2380
+ message: `Uninstall plugin "${value}"?`,
2381
+ onConfirm: () => {
2382
+ nav.setConfirm(void 0);
2383
+ callbacks.uninstall(value).then(() => {
2384
+ nav.notify(`Uninstalled plugin "${value}".`);
2385
+ nav.refresh();
2386
+ }).catch((err) => {
2387
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2388
+ });
2389
+ },
2390
+ onCancel: () => nav.setConfirm(void 0)
2391
+ });
2392
+ }
2393
+ function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
2394
+ if (value === "uninstall") {
2395
+ nav.setConfirm({
2396
+ message: `Uninstall plugin "${pluginId}"?`,
2397
+ onConfirm: () => {
2398
+ nav.setConfirm(void 0);
2399
+ callbacks.uninstall(pluginId).then(() => {
2400
+ nav.notify(`Uninstalled plugin "${pluginId}".`);
2401
+ nav.popN(2);
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
+ }
2410
+
2411
+ // src/ui/PluginTUI.tsx
2412
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2413
+ function PluginTUI({ callbacks, onClose, addMessage }) {
2414
+ const [stack, setStack] = (0, import_react15.useState)([{ screen: "main" }]);
2415
+ const [items, setItems] = (0, import_react15.useState)([]);
2416
+ const [loading, setLoading] = (0, import_react15.useState)(false);
2417
+ const [error, setError] = (0, import_react15.useState)();
2418
+ const [confirm, setConfirm] = (0, import_react15.useState)();
2419
+ const [refreshCounter, setRefreshCounter] = (0, import_react15.useState)(0);
2420
+ const current = stack[stack.length - 1] ?? { screen: "main" };
2421
+ const push = (0, import_react15.useCallback)((state) => {
2422
+ setStack((prev) => [...prev, state]);
2423
+ setItems([]);
2424
+ setError(void 0);
2425
+ }, []);
2426
+ const pop = (0, import_react15.useCallback)(() => {
2427
+ setStack((prev) => {
2428
+ if (prev.length <= 1) {
2429
+ onClose();
2430
+ return prev;
2431
+ }
2432
+ return prev.slice(0, -1);
2433
+ });
2434
+ setItems([]);
2435
+ setError(void 0);
2436
+ }, [onClose]);
2437
+ const popN = (0, import_react15.useCallback)(
2438
+ (n) => {
2439
+ setStack((prev) => {
2440
+ const next = prev.slice(0, Math.max(1, prev.length - n));
2441
+ if (next.length === 0) {
2442
+ onClose();
2443
+ return prev;
2444
+ }
2445
+ return next;
2446
+ });
2447
+ setItems([]);
2448
+ setError(void 0);
2449
+ },
2450
+ [onClose]
2451
+ );
2452
+ const notify = (0, import_react15.useCallback)(
2453
+ (content) => {
2454
+ addMessage?.({ role: "system", content });
2455
+ },
2456
+ [addMessage]
2457
+ );
2458
+ const refresh = (0, import_react15.useCallback)(() => {
2459
+ setItems([]);
2460
+ setRefreshCounter((c) => c + 1);
2461
+ }, []);
2462
+ const nav = { push, pop, popN, notify, setConfirm, refresh };
2463
+ (0, import_react15.useEffect)(() => {
2464
+ const screen2 = current.screen;
2465
+ if (screen2 === "marketplace-list") {
2466
+ setLoading(true);
2467
+ callbacks.marketplaceList().then((sources) => {
2468
+ const baseItems = [{ label: "Add Marketplace", value: "__add__" }];
2469
+ const sourceItems = sources.map((s) => ({
2470
+ label: s.name,
2471
+ value: s.name,
2472
+ hint: s.type
2473
+ }));
2474
+ setItems([...baseItems, ...sourceItems]);
2475
+ setLoading(false);
2476
+ }).catch((err) => {
2477
+ setError(err instanceof Error ? err.message : String(err));
2478
+ setLoading(false);
2479
+ });
2480
+ } else if (screen2 === "marketplace-browse") {
2481
+ const marketplace = current.context?.marketplace ?? "";
2482
+ setLoading(true);
2483
+ callbacks.listAvailablePlugins(marketplace).then((plugins) => {
2484
+ setItems(
2485
+ plugins.map((p) => ({
2486
+ label: p.name,
2487
+ value: p.name,
2488
+ hint: p.installed ? "installed" : p.description
2489
+ }))
2490
+ );
2491
+ setLoading(false);
2492
+ }).catch((err) => {
2493
+ setError(err instanceof Error ? err.message : String(err));
2494
+ setLoading(false);
2495
+ });
2496
+ } else if (screen2 === "installed-list") {
2497
+ setLoading(true);
2498
+ callbacks.listInstalled().then((plugins) => {
2499
+ setItems(
2500
+ plugins.map((p) => ({
2501
+ label: p.name,
2502
+ value: p.name,
2503
+ hint: p.description
2504
+ }))
2505
+ );
2506
+ setLoading(false);
2507
+ }).catch((err) => {
2508
+ setError(err instanceof Error ? err.message : String(err));
2509
+ setLoading(false);
2510
+ });
2511
+ }
2512
+ }, [stack.length, current.screen, current.context?.marketplace, callbacks, refreshCounter]);
2513
+ const handleSelect = (0, import_react15.useCallback)(
2514
+ (value) => {
2515
+ const screen2 = current.screen;
2516
+ const ctx = current.context;
2517
+ if (screen2 === "main") handleMainSelect(value, nav);
2518
+ else if (screen2 === "marketplace-list") handleMarketplaceListSelect(value, nav);
2519
+ else if (screen2 === "marketplace-action")
2520
+ handleMarketplaceActionSelect(value, ctx?.marketplace ?? "", callbacks, nav);
2521
+ else if (screen2 === "marketplace-browse")
2522
+ handleMarketplaceBrowseSelect(value, ctx?.marketplace ?? "", items, nav);
2523
+ else if (screen2 === "marketplace-install-scope")
2524
+ handleInstallScopeSelect(value, ctx?.pluginId ?? "", callbacks, nav);
2525
+ else if (screen2 === "installed-list") handleInstalledListSelect(value, callbacks, nav);
2526
+ else if (screen2 === "installed-action")
2527
+ handleInstalledActionSelect(value, ctx?.pluginId ?? "", callbacks, nav);
2528
+ },
2529
+ [current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
2530
+ );
2531
+ const handleTextSubmit = (0, import_react15.useCallback)(
2532
+ (value) => {
2533
+ if (current.screen === "marketplace-add") {
2534
+ callbacks.marketplaceAdd(value).then((name) => {
2535
+ notify(`Added marketplace "${name}" from ${value}.`);
2536
+ pop();
2537
+ }).catch((err) => {
2538
+ notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2539
+ pop();
2540
+ });
2541
+ }
2542
+ },
2543
+ [current.screen, callbacks, notify, pop]
2544
+ );
2545
+ if (confirm) {
2546
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2547
+ ConfirmPrompt,
2548
+ {
2549
+ message: confirm.message,
2550
+ onSelect: (index) => {
2551
+ if (index === 0) confirm.onConfirm();
2552
+ else confirm.onCancel();
2553
+ }
2554
+ }
2555
+ );
2556
+ }
2557
+ const screen = current.screen;
2558
+ if (screen === "marketplace-add") {
2559
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2560
+ TextPrompt,
2561
+ {
2562
+ title: "Add Marketplace Source",
2563
+ placeholder: "owner/repo or git URL",
2564
+ onSubmit: handleTextSubmit,
2565
+ onCancel: pop,
2566
+ validate: (v) => !v.includes("/") ? "Must be owner/repo or a git URL" : void 0
2567
+ }
2568
+ );
2569
+ }
2570
+ if (screen === "marketplace-action") {
2571
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2572
+ MenuSelect,
2573
+ {
2574
+ title: `Marketplace: ${current.context?.marketplace ?? ""}`,
2575
+ items: [
2576
+ { label: "Browse plugins", value: "browse" },
2577
+ { label: "Update", value: "update" },
2578
+ { label: "Remove", value: "remove" }
2579
+ ],
2580
+ onSelect: handleSelect,
2581
+ onBack: pop
2582
+ },
2583
+ stack.length
2584
+ );
2585
+ }
2586
+ if (screen === "marketplace-install-scope") {
2587
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2588
+ MenuSelect,
2589
+ {
2590
+ title: `Install scope for "${current.context?.pluginId ?? ""}"`,
2591
+ items: [
2592
+ { label: "User scope", value: "user" },
2593
+ { label: "Project scope", value: "project" }
2594
+ ],
2595
+ onSelect: handleSelect,
2596
+ onBack: pop
2597
+ },
2598
+ stack.length
2599
+ );
2600
+ }
2601
+ if (screen === "installed-action") {
2602
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2603
+ MenuSelect,
2604
+ {
2605
+ title: `Plugin: ${current.context?.pluginId ?? ""}`,
2606
+ items: [{ label: "Uninstall", value: "uninstall" }],
2607
+ onSelect: handleSelect,
2608
+ onBack: pop
2609
+ },
2610
+ stack.length
2611
+ );
2612
+ }
2613
+ const titleMap = {
2614
+ main: "Plugin Management",
2615
+ "marketplace-list": "Marketplace",
2616
+ "marketplace-browse": `Browse: ${current.context?.marketplace ?? ""}`,
2617
+ "installed-list": "Installed Plugins"
2618
+ };
2619
+ const staticItemsMap = {
2620
+ main: [
2621
+ { label: "Marketplace", value: "marketplace" },
2622
+ { label: "Installed Plugins", value: "installed" }
2623
+ ]
2624
+ };
2625
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2626
+ MenuSelect,
2627
+ {
2628
+ title: titleMap[screen] ?? "Plugin Management",
2629
+ items: staticItemsMap[screen] ?? items,
2630
+ onSelect: handleSelect,
2631
+ onBack: pop,
2632
+ loading,
2633
+ error
2634
+ },
2635
+ `${screen}-${stack.length}-${refreshCounter}`
2636
+ );
2637
+ }
2638
+
2639
+ // src/ui/App.tsx
2640
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2138
2641
  var EXIT_DELAY_MS2 = 500;
2139
2642
  function mergeHooksIntoConfig(configHooks, pluginHooks) {
2140
2643
  const pluginKeys = Object.keys(pluginHooks);
@@ -2153,7 +2656,7 @@ function mergeHooksIntoConfig(configHooks, pluginHooks) {
2153
2656
  return merged;
2154
2657
  }
2155
2658
  function App(props) {
2156
- const { exit } = (0, import_ink11.useApp)();
2659
+ const { exit } = (0, import_ink13.useApp)();
2157
2660
  const { registry, pluginHooks } = useCommandRegistry(props.cwd ?? process.cwd());
2158
2661
  const configWithPluginHooks = {
2159
2662
  ...props.config,
@@ -2166,15 +2669,16 @@ function App(props) {
2166
2669
  { ...props, config: configWithPluginHooks }
2167
2670
  );
2168
2671
  const { messages, setMessages, addMessage } = useMessages();
2169
- const [isThinking, setIsThinking] = (0, import_react13.useState)(false);
2672
+ const [isThinking, setIsThinking] = (0, import_react16.useState)(false);
2170
2673
  const initialCtx = session.getContextState();
2171
- const [contextState, setContextState] = (0, import_react13.useState)({
2674
+ const [contextState, setContextState] = (0, import_react16.useState)({
2172
2675
  percentage: initialCtx.usedPercentage,
2173
2676
  usedTokens: initialCtx.usedTokens,
2174
2677
  maxTokens: initialCtx.maxTokens
2175
2678
  });
2176
- const pendingModelChangeRef = (0, import_react13.useRef)(null);
2177
- const [pendingModelId, setPendingModelId] = (0, import_react13.useState)(null);
2679
+ const pendingModelChangeRef = (0, import_react16.useRef)(null);
2680
+ const [pendingModelId, setPendingModelId] = (0, import_react16.useState)(null);
2681
+ const [showPluginTUI, setShowPluginTUI] = (0, import_react16.useState)(false);
2178
2682
  const pluginCallbacks = usePluginCallbacks(props.cwd ?? process.cwd());
2179
2683
  const handleSlashCommand = useSlashCommands(
2180
2684
  session,
@@ -2184,7 +2688,8 @@ function App(props) {
2184
2688
  registry,
2185
2689
  pendingModelChangeRef,
2186
2690
  setPendingModelId,
2187
- pluginCallbacks
2691
+ pluginCallbacks,
2692
+ setShowPluginTUI
2188
2693
  );
2189
2694
  const handleSubmit = useSubmitHandler(
2190
2695
  session,
@@ -2195,36 +2700,36 @@ function App(props) {
2195
2700
  setContextState,
2196
2701
  registry
2197
2702
  );
2198
- (0, import_ink11.useInput)(
2703
+ (0, import_ink13.useInput)(
2199
2704
  (_input, key) => {
2200
2705
  if (key.ctrl && _input === "c") exit();
2201
2706
  if (key.escape && isThinking) session.abort();
2202
2707
  },
2203
- { isActive: !permissionRequest }
2708
+ { isActive: !permissionRequest && !showPluginTUI }
2204
2709
  );
2205
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", children: [
2206
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2207
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "cyan", bold: true, children: `
2710
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", children: [
2711
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2712
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { color: "cyan", bold: true, children: `
2208
2713
  ____ ___ ____ ___ _____ _
2209
2714
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
2210
2715
  | |_) | | | | _ \\| | | || | / _ \\
2211
2716
  | _ <| |_| | |_) | |_| || |/ ___ \\
2212
2717
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
2213
2718
  ` }),
2214
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { dimColor: true, children: [
2719
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Text, { dimColor: true, children: [
2215
2720
  " v",
2216
2721
  props.version ?? "0.0.0"
2217
2722
  ] })
2218
2723
  ] }),
2219
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
2220
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MessageList, { messages }),
2221
- 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 }) })
2724
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
2725
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageList, { messages }),
2726
+ 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 }) })
2222
2727
  ] }),
2223
- permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PermissionPrompt, { request: permissionRequest }),
2224
- pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2728
+ permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PermissionPrompt, { request: permissionRequest }),
2729
+ pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2225
2730
  ConfirmPrompt,
2226
2731
  {
2227
- message: `Change model to ${(0, import_agent_core3.getModelName)(pendingModelId)}? This will restart the session.`,
2732
+ message: `Change model to ${(0, import_agent_core6.getModelName)(pendingModelId)}? This will restart the session.`,
2228
2733
  onSelect: (index) => {
2229
2734
  setPendingModelId(null);
2230
2735
  pendingModelChangeRef.current = null;
@@ -2232,28 +2737,38 @@ function App(props) {
2232
2737
  try {
2233
2738
  const settingsPath = getUserSettingsPath();
2234
2739
  updateModelInSettings(settingsPath, pendingModelId);
2235
- addMessage({
2236
- role: "system",
2237
- content: `Model changed to ${(0, import_agent_core3.getModelName)(pendingModelId)}. Restarting...`
2238
- });
2740
+ addMessage(
2741
+ (0, import_agent_core7.createSystemMessage)(
2742
+ `Model changed to ${(0, import_agent_core6.getModelName)(pendingModelId)}. Restarting...`
2743
+ )
2744
+ );
2239
2745
  setTimeout(() => exit(), EXIT_DELAY_MS2);
2240
2746
  } catch (err) {
2241
- addMessage({
2242
- role: "system",
2243
- content: `Failed: ${err instanceof Error ? err.message : String(err)}`
2244
- });
2747
+ addMessage(
2748
+ (0, import_agent_core7.createSystemMessage)(
2749
+ `Failed: ${err instanceof Error ? err.message : String(err)}`
2750
+ )
2751
+ );
2245
2752
  }
2246
2753
  } else {
2247
- addMessage({ role: "system", content: "Model change cancelled." });
2754
+ addMessage((0, import_agent_core7.createSystemMessage)("Model change cancelled."));
2248
2755
  }
2249
2756
  }
2250
2757
  }
2251
2758
  ),
2252
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2759
+ showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2760
+ PluginTUI,
2761
+ {
2762
+ callbacks: pluginCallbacks,
2763
+ onClose: () => setShowPluginTUI(false),
2764
+ addMessage: (msg) => addMessage((0, import_agent_core7.createSystemMessage)(msg.content))
2765
+ }
2766
+ ),
2767
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2253
2768
  StatusBar,
2254
2769
  {
2255
2770
  permissionMode: session.getPermissionMode(),
2256
- modelName: (0, import_agent_core3.getModelName)(props.config.provider.model),
2771
+ modelName: (0, import_agent_core6.getModelName)(props.config.provider.model),
2257
2772
  sessionId: session.getSessionId(),
2258
2773
  messageCount: messages.length,
2259
2774
  isThinking,
@@ -2262,20 +2777,20 @@ function App(props) {
2262
2777
  contextMaxTokens: contextState.maxTokens
2263
2778
  }
2264
2779
  ),
2265
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2780
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2266
2781
  InputArea,
2267
2782
  {
2268
2783
  onSubmit: handleSubmit,
2269
- isDisabled: isThinking || !!permissionRequest,
2784
+ isDisabled: isThinking || !!permissionRequest || showPluginTUI,
2270
2785
  registry
2271
2786
  }
2272
2787
  ),
2273
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { children: " " })
2788
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { children: " " })
2274
2789
  ] });
2275
2790
  }
2276
2791
 
2277
2792
  // src/ui/render.tsx
2278
- var import_jsx_runtime12 = require("react/jsx-runtime");
2793
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2279
2794
  function renderApp(options) {
2280
2795
  process.on("unhandledRejection", (reason) => {
2281
2796
  process.stderr.write(`
@@ -2286,7 +2801,7 @@ function renderApp(options) {
2286
2801
  `);
2287
2802
  }
2288
2803
  });
2289
- const instance = (0, import_ink12.render)(/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(App, { ...options }), {
2804
+ const instance = (0, import_ink14.render)(/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(App, { ...options }), {
2290
2805
  exitOnCtrlC: true
2291
2806
  });
2292
2807
  instance.waitUntilExit().catch((err) => {