@robota-sdk/agent-cli 3.0.0-beta.14 → 3.0.0-beta.16

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/README.md CHANGED
@@ -75,9 +75,14 @@ robota -r <session-id> # Resume session by ID
75
75
  robota --model <model> # Model override (e.g., claude-sonnet-4-6)
76
76
  robota --permission-mode <mode> # plan | default | acceptEdits | bypassPermissions
77
77
  robota --max-turns <n> # Limit agentic turns per interaction
78
+ robota --reset # Delete user settings and exit
78
79
  robota --version # Show version
79
80
  ```
80
81
 
82
+ ## First-Run Setup
83
+
84
+ When no settings file exists, the CLI prompts for an Anthropic API key (input masked with asterisks) and creates `~/.robota/settings.json`. Use `robota --reset` to return to first-run state.
85
+
81
86
  ## Built-in Tools
82
87
 
83
88
  The CLI provides 6 tools that the AI agent can invoke:
@@ -177,9 +182,11 @@ Configure in `.robota/settings.json` or `.robota/settings.local.json`:
177
182
  | `/help` | Show help |
178
183
  | `/clear` | Clear conversation history |
179
184
  | `/mode [mode]` | Show or change permission mode |
180
- | `/resume` | List and resume a saved session |
181
- | `/cost` | Show token usage |
182
- | `/model` | Show current model |
185
+ | `/model [model]`| Select AI model (confirmation + restart) |
186
+ | `/compact [instructions]` | Compress context window |
187
+ | `/cost` | Show session info |
188
+ | `/context` | Context window details |
189
+ | `/permissions` | Show permission rules |
183
190
  | `/exit` | Exit CLI |
184
191
 
185
192
  ## Configuration
package/dist/node/bin.cjs CHANGED
@@ -25,20 +25,23 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/cli.ts
27
27
  var import_node_util = require("util");
28
- var import_node_fs2 = require("fs");
29
- var import_node_path2 = require("path");
28
+ var import_node_fs3 = require("fs");
29
+ var import_node_path3 = require("path");
30
30
  var import_node_url = require("url");
31
31
  var readline = __toESM(require("readline"), 1);
32
32
  var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
33
33
  var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
34
34
 
35
35
  // src/ui/render.tsx
36
- var import_ink9 = require("ink");
36
+ var import_ink10 = require("ink");
37
37
 
38
38
  // src/ui/App.tsx
39
- var import_react5 = require("react");
40
- var import_ink8 = require("ink");
39
+ var import_react6 = require("react");
40
+ var import_ink9 = require("ink");
41
+ var import_node_fs2 = require("fs");
42
+ var import_node_path2 = require("path");
41
43
  var import_agent_sdk = require("@robota-sdk/agent-sdk");
44
+ var import_agent_core3 = require("@robota-sdk/agent-core");
42
45
 
43
46
  // src/commands/command-registry.ts
44
47
  var CommandRegistry = class {
@@ -71,6 +74,21 @@ var CommandRegistry = class {
71
74
  };
72
75
 
73
76
  // src/commands/builtin-source.ts
77
+ var import_agent_core = require("@robota-sdk/agent-core");
78
+ function buildModelSubcommands() {
79
+ const seen = /* @__PURE__ */ new Set();
80
+ const commands = [];
81
+ for (const model of Object.values(import_agent_core.CLAUDE_MODELS)) {
82
+ if (seen.has(model.name)) continue;
83
+ seen.add(model.name);
84
+ commands.push({
85
+ name: model.id,
86
+ description: `${model.name} (${(0, import_agent_core.formatTokenCount)(model.contextWindow).toUpperCase()})`,
87
+ source: "builtin"
88
+ });
89
+ }
90
+ return commands;
91
+ }
74
92
  function createBuiltinCommands() {
75
93
  return [
76
94
  { name: "help", description: "Show available commands", source: "builtin" },
@@ -90,11 +108,7 @@ function createBuiltinCommands() {
90
108
  name: "model",
91
109
  description: "Select AI model",
92
110
  source: "builtin",
93
- subcommands: [
94
- { name: "claude-opus-4-6", description: "Opus 4.6 (highest quality)", source: "builtin" },
95
- { name: "claude-sonnet-4-6", description: "Sonnet 4.6 (balanced)", source: "builtin" },
96
- { name: "claude-haiku-4-5", description: "Haiku 4.5 (fastest)", source: "builtin" }
97
- ]
111
+ subcommands: buildModelSubcommands()
98
112
  },
99
113
  { name: "compact", description: "Compress context window", source: "builtin" },
100
114
  { name: "cost", description: "Show session info", source: "builtin" },
@@ -242,6 +256,7 @@ function MessageList({ messages }) {
242
256
 
243
257
  // src/ui/StatusBar.tsx
244
258
  var import_ink2 = require("ink");
259
+ var import_agent_core2 = require("@robota-sdk/agent-core");
245
260
  var import_jsx_runtime2 = require("react/jsx-runtime");
246
261
  var CONTEXT_YELLOW_THRESHOLD = 70;
247
262
  var CONTEXT_RED_THRESHOLD = 90;
@@ -281,10 +296,10 @@ function StatusBar({
281
296
  "Context: ",
282
297
  Math.round(contextPercentage),
283
298
  "% (",
284
- (contextUsedTokens / 1e3).toFixed(1),
285
- "k/",
286
- (contextMaxTokens / 1e3).toFixed(0),
287
- "k)"
299
+ (0, import_agent_core2.formatTokenCount)(contextUsedTokens),
300
+ "/",
301
+ (0, import_agent_core2.formatTokenCount)(contextMaxTokens),
302
+ ")"
288
303
  ] })
289
304
  ] }),
290
305
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { children: [
@@ -431,19 +446,13 @@ var import_jsx_runtime5 = require("react/jsx-runtime");
431
446
  var MAX_VISIBLE = 8;
432
447
  function CommandRow(props) {
433
448
  const { cmd, isSelected, showSlash } = props;
434
- const prefix = showSlash ? "/" : "";
435
449
  const indicator = isSelected ? "\u25B8 " : " ";
436
450
  const nameColor = isSelected ? "cyan" : void 0;
437
451
  const dimmed = !isSelected;
438
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink5.Box, { children: [
439
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink5.Text, { color: nameColor, dimColor: dimmed, children: [
440
- indicator,
441
- prefix,
442
- cmd.name
443
- ] }),
444
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { dimColor: dimmed, children: " " }),
445
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { color: nameColor, dimColor: dimmed, children: cmd.description })
446
- ] });
452
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink5.Text, { color: nameColor, dimColor: dimmed, children: [
453
+ indicator,
454
+ showSlash ? `/${cmd.name} ${cmd.description}` : cmd.description
455
+ ] }) });
447
456
  }
448
457
  function SlashAutocomplete({
449
458
  commands,
@@ -608,10 +617,53 @@ function InputArea({ onSubmit, isDisabled, registry }) {
608
617
  ] });
609
618
  }
610
619
 
611
- // src/ui/PermissionPrompt.tsx
612
- var import_react4 = __toESM(require("react"), 1);
620
+ // src/ui/ConfirmPrompt.tsx
621
+ var import_react4 = require("react");
613
622
  var import_ink7 = require("ink");
614
623
  var import_jsx_runtime7 = require("react/jsx-runtime");
624
+ function ConfirmPrompt({
625
+ message,
626
+ options = ["Yes", "No"],
627
+ onSelect
628
+ }) {
629
+ const [selected, setSelected] = (0, import_react4.useState)(0);
630
+ const resolvedRef = (0, import_react4.useRef)(false);
631
+ const doSelect = (0, import_react4.useCallback)(
632
+ (index) => {
633
+ if (resolvedRef.current) return;
634
+ resolvedRef.current = true;
635
+ onSelect(index);
636
+ },
637
+ [onSelect]
638
+ );
639
+ (0, import_ink7.useInput)((input, key) => {
640
+ if (resolvedRef.current) return;
641
+ if (key.leftArrow || key.upArrow) {
642
+ setSelected((prev) => prev > 0 ? prev - 1 : prev);
643
+ } else if (key.rightArrow || key.downArrow) {
644
+ setSelected((prev) => prev < options.length - 1 ? prev + 1 : prev);
645
+ } else if (key.return) {
646
+ doSelect(selected);
647
+ } else if (input === "y" && options.length === 2) {
648
+ doSelect(0);
649
+ } else if (input === "n" && options.length === 2) {
650
+ doSelect(1);
651
+ }
652
+ });
653
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
654
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "yellow", children: message }),
655
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
656
+ i === selected ? "> " : " ",
657
+ opt
658
+ ] }) }, opt)) }),
659
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { dimColor: true, children: " arrow keys to select, Enter to confirm" })
660
+ ] });
661
+ }
662
+
663
+ // src/ui/PermissionPrompt.tsx
664
+ var import_react5 = __toESM(require("react"), 1);
665
+ var import_ink8 = require("ink");
666
+ var import_jsx_runtime8 = require("react/jsx-runtime");
615
667
  var OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
616
668
  function formatArgs(args) {
617
669
  const entries = Object.entries(args);
@@ -619,15 +671,15 @@ function formatArgs(args) {
619
671
  return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
620
672
  }
621
673
  function PermissionPrompt({ request }) {
622
- const [selected, setSelected] = import_react4.default.useState(0);
623
- const resolvedRef = import_react4.default.useRef(false);
624
- const prevRequestRef = import_react4.default.useRef(request);
674
+ const [selected, setSelected] = import_react5.default.useState(0);
675
+ const resolvedRef = import_react5.default.useRef(false);
676
+ const prevRequestRef = import_react5.default.useRef(request);
625
677
  if (prevRequestRef.current !== request) {
626
678
  prevRequestRef.current = request;
627
679
  resolvedRef.current = false;
628
680
  setSelected(0);
629
681
  }
630
- const doResolve = import_react4.default.useCallback(
682
+ const doResolve = import_react5.default.useCallback(
631
683
  (index) => {
632
684
  if (resolvedRef.current) return;
633
685
  resolvedRef.current = true;
@@ -637,7 +689,7 @@ function PermissionPrompt({ request }) {
637
689
  },
638
690
  [request]
639
691
  );
640
- (0, import_ink7.useInput)((input, key) => {
692
+ (0, import_ink8.useInput)((input, key) => {
641
693
  if (resolvedRef.current) return;
642
694
  if (key.upArrow || key.leftArrow) {
643
695
  setSelected((prev) => prev > 0 ? prev - 1 : prev);
@@ -653,27 +705,27 @@ function PermissionPrompt({ request }) {
653
705
  doResolve(2);
654
706
  }
655
707
  });
656
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
657
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "yellow", bold: true, children: "[Permission Required]" }),
658
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { children: [
708
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
709
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "yellow", bold: true, children: "[Permission Required]" }),
710
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { children: [
659
711
  "Tool:",
660
712
  " ",
661
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "cyan", bold: true, children: request.toolName })
713
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "cyan", bold: true, children: request.toolName })
662
714
  ] }),
663
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { dimColor: true, children: [
715
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { dimColor: true, children: [
664
716
  " ",
665
717
  formatArgs(request.toolArgs)
666
718
  ] }),
667
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { marginTop: 1, children: OPTIONS.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
719
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Box, { marginTop: 1, children: OPTIONS.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
668
720
  i === selected ? "> " : " ",
669
721
  opt
670
722
  ] }) }, opt)) }),
671
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { dimColor: true, children: " left/right to select, Enter to confirm" })
723
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { dimColor: true, children: " left/right to select, Enter to confirm" })
672
724
  ] });
673
725
  }
674
726
 
675
727
  // src/ui/App.tsx
676
- var import_jsx_runtime8 = require("react/jsx-runtime");
728
+ var import_jsx_runtime9 = require("react/jsx-runtime");
677
729
  var msgIdCounter = 0;
678
730
  function nextId() {
679
731
  msgIdCounter += 1;
@@ -695,11 +747,11 @@ var NOOP_TERMINAL = {
695
747
  } })
696
748
  };
697
749
  function useSession(props) {
698
- const [permissionRequest, setPermissionRequest] = (0, import_react5.useState)(null);
699
- const [streamingText, setStreamingText] = (0, import_react5.useState)("");
700
- const permissionQueueRef = (0, import_react5.useRef)([]);
701
- const processingRef = (0, import_react5.useRef)(false);
702
- const processNextPermission = (0, import_react5.useCallback)(() => {
750
+ const [permissionRequest, setPermissionRequest] = (0, import_react6.useState)(null);
751
+ const [streamingText, setStreamingText] = (0, import_react6.useState)("");
752
+ const permissionQueueRef = (0, import_react6.useRef)([]);
753
+ const processingRef = (0, import_react6.useRef)(false);
754
+ const processNextPermission = (0, import_react6.useCallback)(() => {
703
755
  if (processingRef.current) return;
704
756
  const next = permissionQueueRef.current[0];
705
757
  if (!next) {
@@ -719,7 +771,7 @@ function useSession(props) {
719
771
  }
720
772
  });
721
773
  }, []);
722
- const sessionRef = (0, import_react5.useRef)(null);
774
+ const sessionRef = (0, import_react6.useRef)(null);
723
775
  if (sessionRef.current === null) {
724
776
  const permissionHandler = (toolName, toolArgs) => {
725
777
  return new Promise((resolve) => {
@@ -744,12 +796,12 @@ function useSession(props) {
744
796
  onTextDelta
745
797
  });
746
798
  }
747
- const clearStreamingText = (0, import_react5.useCallback)(() => setStreamingText(""), []);
799
+ const clearStreamingText = (0, import_react6.useCallback)(() => setStreamingText(""), []);
748
800
  return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText };
749
801
  }
750
802
  function useMessages() {
751
- const [messages, setMessages] = (0, import_react5.useState)([]);
752
- const addMessage = (0, import_react5.useCallback)((msg) => {
803
+ const [messages, setMessages] = (0, import_react6.useState)([]);
804
+ const addMessage = (0, import_react6.useCallback)((msg) => {
753
805
  setMessages((prev) => [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }]);
754
806
  }, []);
755
807
  return { messages, setMessages, addMessage };
@@ -776,7 +828,7 @@ function handleModeCommand(arg, session, addMessage) {
776
828
  }
777
829
  return true;
778
830
  }
779
- async function executeSlashCommand(cmd, parts, session, addMessage, setMessages, exit, registry) {
831
+ async function executeSlashCommand(cmd, parts, session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId) {
780
832
  switch (cmd) {
781
833
  case "help":
782
834
  addMessage({ role: "system", content: HELP_TEXT });
@@ -800,6 +852,16 @@ async function executeSlashCommand(cmd, parts, session, addMessage, setMessages,
800
852
  }
801
853
  case "mode":
802
854
  return handleModeCommand(parts[1], session, addMessage);
855
+ case "model": {
856
+ const modelId = parts[1];
857
+ if (!modelId) {
858
+ addMessage({ role: "system", content: "Select a model from the /model submenu." });
859
+ return true;
860
+ }
861
+ pendingModelChangeRef.current = modelId;
862
+ setPendingModelId(modelId);
863
+ return true;
864
+ }
803
865
  case "cost":
804
866
  addMessage({
805
867
  role: "system",
@@ -828,11 +890,10 @@ Messages: ${session.getMessageCount()}`
828
890
  return true;
829
891
  }
830
892
  case "reset": {
831
- const { existsSync: exists, unlinkSync: unlink } = await import("fs");
832
893
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
833
894
  const settingsPath = `${home}/.robota/settings.json`;
834
- if (exists(settingsPath)) {
835
- unlink(settingsPath);
895
+ if ((0, import_node_fs2.existsSync)(settingsPath)) {
896
+ (0, import_node_fs2.unlinkSync)(settingsPath);
836
897
  addMessage({ role: "system", content: `Deleted ${settingsPath}. Exiting...` });
837
898
  } else {
838
899
  addMessage({ role: "system", content: "No user settings found." });
@@ -854,28 +915,28 @@ Messages: ${session.getMessageCount()}`
854
915
  }
855
916
  }
856
917
  }
857
- function useSlashCommands(session, addMessage, setMessages, exit, registry) {
858
- return (0, import_react5.useCallback)(
918
+ function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId) {
919
+ return (0, import_react6.useCallback)(
859
920
  async (input) => {
860
921
  const parts = input.slice(1).split(/\s+/);
861
922
  const cmd = parts[0]?.toLowerCase() ?? "";
862
- return executeSlashCommand(cmd, parts, session, addMessage, setMessages, exit, registry);
923
+ return executeSlashCommand(cmd, parts, session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId);
863
924
  },
864
- [session, addMessage, setMessages, exit, registry]
925
+ [session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId]
865
926
  );
866
927
  }
867
928
  function StreamingIndicator({ text }) {
868
929
  if (text) {
869
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", children: [
870
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { color: "cyan", bold: true, children: [
930
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", children: [
931
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { color: "cyan", bold: true, children: [
871
932
  "Robota:",
872
933
  " "
873
934
  ] }),
874
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { children: " " }),
875
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { wrap: "wrap", children: renderMarkdown(text) }) })
935
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " }),
936
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { wrap: "wrap", children: renderMarkdown(text) }) })
876
937
  ] });
877
938
  }
878
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "yellow", children: "Thinking..." });
939
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "yellow", children: "Thinking..." });
879
940
  }
880
941
  async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextPercentage) {
881
942
  setIsThinking(true);
@@ -937,7 +998,7 @@ Execute the "${cmd}" skill: ${userInstruction}`;
937
998
  return `Use the "${cmd}" skill: ${userInstruction}`;
938
999
  }
939
1000
  function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextPercentage, registry) {
940
- return (0, import_react5.useCallback)(
1001
+ return (0, import_react6.useCallback)(
941
1002
  async (input) => {
942
1003
  if (input.startsWith("/")) {
943
1004
  const handled = await handleSlashCommand(input);
@@ -978,7 +1039,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
978
1039
  );
979
1040
  }
980
1041
  function useCommandRegistry(cwd) {
981
- const registryRef = (0, import_react5.useRef)(null);
1042
+ const registryRef = (0, import_react6.useRef)(null);
982
1043
  if (registryRef.current === null) {
983
1044
  const registry = new CommandRegistry();
984
1045
  registry.addSource(new BuiltinCommandSource());
@@ -988,13 +1049,15 @@ function useCommandRegistry(cwd) {
988
1049
  return registryRef.current;
989
1050
  }
990
1051
  function App(props) {
991
- const { exit } = (0, import_ink8.useApp)();
1052
+ const { exit } = (0, import_ink9.useApp)();
992
1053
  const { session, permissionRequest, streamingText, clearStreamingText } = useSession(props);
993
1054
  const { messages, setMessages, addMessage } = useMessages();
994
- const [isThinking, setIsThinking] = (0, import_react5.useState)(false);
995
- const [contextPercentage, setContextPercentage] = (0, import_react5.useState)(0);
1055
+ const [isThinking, setIsThinking] = (0, import_react6.useState)(false);
1056
+ const [contextPercentage, setContextPercentage] = (0, import_react6.useState)(0);
996
1057
  const registry = useCommandRegistry(props.cwd ?? process.cwd());
997
- const handleSlashCommand = useSlashCommands(session, addMessage, setMessages, exit, registry);
1058
+ const pendingModelChangeRef = (0, import_react6.useRef)(null);
1059
+ const [pendingModelId, setPendingModelId] = (0, import_react6.useState)(null);
1060
+ const handleSlashCommand = useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId);
998
1061
  const handleSubmit = useSubmitHandler(
999
1062
  session,
1000
1063
  addMessage,
@@ -1004,37 +1067,68 @@ function App(props) {
1004
1067
  setContextPercentage,
1005
1068
  registry
1006
1069
  );
1007
- (0, import_ink8.useInput)(
1070
+ (0, import_ink9.useInput)(
1008
1071
  (_input, key) => {
1009
1072
  if (key.ctrl && _input === "c") exit();
1010
1073
  if (key.escape && isThinking) session.abort();
1011
1074
  },
1012
1075
  { isActive: !permissionRequest }
1013
1076
  );
1014
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", children: [
1015
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
1016
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "cyan", bold: true, children: `
1077
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", children: [
1078
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
1079
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "cyan", bold: true, children: `
1017
1080
  ____ ___ ____ ___ _____ _
1018
1081
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
1019
1082
  | |_) | | | | _ \\| | | || | / _ \\
1020
1083
  | _ <| |_| | |_) | |_| || |/ ___ \\
1021
1084
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
1022
1085
  ` }),
1023
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { dimColor: true, children: [
1086
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { dimColor: true, children: [
1024
1087
  " v",
1025
1088
  props.version ?? "0.0.0"
1026
1089
  ] })
1027
1090
  ] }),
1028
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
1029
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MessageList, { messages }),
1030
- isThinking && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StreamingIndicator, { text: streamingText }) })
1091
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
1092
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MessageList, { messages }),
1093
+ isThinking && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StreamingIndicator, { text: streamingText }) })
1031
1094
  ] }),
1032
- permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PermissionPrompt, { request: permissionRequest }),
1033
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1095
+ permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PermissionPrompt, { request: permissionRequest }),
1096
+ pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1097
+ ConfirmPrompt,
1098
+ {
1099
+ message: `Change model to ${(0, import_agent_core3.getModelName)(pendingModelId)}? This will restart the session.`,
1100
+ onSelect: (index) => {
1101
+ setPendingModelId(null);
1102
+ pendingModelChangeRef.current = null;
1103
+ if (index === 0) {
1104
+ try {
1105
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
1106
+ const settingsPath = (0, import_node_path2.join)(home, ".robota", "settings.json");
1107
+ let settings = {};
1108
+ if ((0, import_node_fs2.existsSync)(settingsPath)) {
1109
+ settings = JSON.parse((0, import_node_fs2.readFileSync)(settingsPath, "utf8"));
1110
+ }
1111
+ const provider = settings.provider ?? {};
1112
+ provider.model = pendingModelId;
1113
+ settings.provider = provider;
1114
+ (0, import_node_fs2.mkdirSync)((0, import_node_path2.join)(home, ".robota"), { recursive: true });
1115
+ (0, import_node_fs2.writeFileSync)(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
1116
+ addMessage({ role: "system", content: `Model changed to ${(0, import_agent_core3.getModelName)(pendingModelId)}. Restarting...` });
1117
+ setTimeout(() => exit(), 500);
1118
+ } catch (err) {
1119
+ addMessage({ role: "system", content: `Failed: ${err instanceof Error ? err.message : String(err)}` });
1120
+ }
1121
+ } else {
1122
+ addMessage({ role: "system", content: "Model change cancelled." });
1123
+ }
1124
+ }
1125
+ }
1126
+ ),
1127
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1034
1128
  StatusBar,
1035
1129
  {
1036
1130
  permissionMode: session.getPermissionMode(),
1037
- modelName: props.config.provider.model,
1131
+ modelName: (0, import_agent_core3.getModelName)(props.config.provider.model),
1038
1132
  sessionId: session.getSessionId(),
1039
1133
  messageCount: messages.length,
1040
1134
  isThinking,
@@ -1043,19 +1137,20 @@ function App(props) {
1043
1137
  contextMaxTokens: session.getContextState().maxTokens
1044
1138
  }
1045
1139
  ),
1046
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1140
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1047
1141
  InputArea,
1048
1142
  {
1049
1143
  onSubmit: handleSubmit,
1050
1144
  isDisabled: isThinking || !!permissionRequest,
1051
1145
  registry
1052
1146
  }
1053
- )
1147
+ ),
1148
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " })
1054
1149
  ] });
1055
1150
  }
1056
1151
 
1057
1152
  // src/ui/render.tsx
1058
- var import_jsx_runtime9 = require("react/jsx-runtime");
1153
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1059
1154
  function renderApp(options) {
1060
1155
  process.on("unhandledRejection", (reason) => {
1061
1156
  process.stderr.write(`
@@ -1066,7 +1161,7 @@ function renderApp(options) {
1066
1161
  `);
1067
1162
  }
1068
1163
  });
1069
- const instance = (0, import_ink9.render)(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(App, { ...options }), {
1164
+ const instance = (0, import_ink10.render)(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(App, { ...options }), {
1070
1165
  exitOnCtrlC: true
1071
1166
  });
1072
1167
  instance.waitUntilExit().catch((err) => {
@@ -1084,11 +1179,11 @@ var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
1084
1179
  function readVersion() {
1085
1180
  try {
1086
1181
  const thisFile = (0, import_node_url.fileURLToPath)(import_meta.url);
1087
- const dir = (0, import_node_path2.dirname)(thisFile);
1088
- const candidates = [(0, import_node_path2.join)(dir, "..", "..", "package.json"), (0, import_node_path2.join)(dir, "..", "package.json")];
1182
+ const dir = (0, import_node_path3.dirname)(thisFile);
1183
+ const candidates = [(0, import_node_path3.join)(dir, "..", "..", "package.json"), (0, import_node_path3.join)(dir, "..", "package.json")];
1089
1184
  for (const pkgPath of candidates) {
1090
1185
  try {
1091
- const raw = (0, import_node_fs2.readFileSync)(pkgPath, "utf-8");
1186
+ const raw = (0, import_node_fs3.readFileSync)(pkgPath, "utf-8");
1092
1187
  const pkg = JSON.parse(raw);
1093
1188
  if (pkg.version !== void 0 && pkg.name !== void 0) {
1094
1189
  return pkg.version;
@@ -1196,13 +1291,13 @@ var PrintTerminal = class {
1196
1291
  };
1197
1292
  function getUserSettingsPath() {
1198
1293
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
1199
- return (0, import_node_path2.join)(home, ".robota", "settings.json");
1294
+ return (0, import_node_path3.join)(home, ".robota", "settings.json");
1200
1295
  }
1201
1296
  async function ensureConfig(cwd) {
1202
1297
  const userPath = getUserSettingsPath();
1203
- const projectPath = (0, import_node_path2.join)(cwd, ".robota", "settings.json");
1204
- const localPath = (0, import_node_path2.join)(cwd, ".robota", "settings.local.json");
1205
- if ((0, import_node_fs2.existsSync)(userPath) || (0, import_node_fs2.existsSync)(projectPath) || (0, import_node_fs2.existsSync)(localPath)) {
1298
+ const projectPath = (0, import_node_path3.join)(cwd, ".robota", "settings.json");
1299
+ const localPath = (0, import_node_path3.join)(cwd, ".robota", "settings.local.json");
1300
+ if ((0, import_node_fs3.existsSync)(userPath) || (0, import_node_fs3.existsSync)(projectPath) || (0, import_node_fs3.existsSync)(localPath)) {
1206
1301
  return;
1207
1302
  }
1208
1303
  process.stdout.write("\n");
@@ -1246,8 +1341,8 @@ async function ensureConfig(cwd) {
1246
1341
  process.stderr.write("\n No API key provided. Exiting.\n");
1247
1342
  process.exit(1);
1248
1343
  }
1249
- const settingsDir = (0, import_node_path2.dirname)(userPath);
1250
- (0, import_node_fs2.mkdirSync)(settingsDir, { recursive: true });
1344
+ const settingsDir = (0, import_node_path3.dirname)(userPath);
1345
+ (0, import_node_fs3.mkdirSync)(settingsDir, { recursive: true });
1251
1346
  const settings = {
1252
1347
  provider: {
1253
1348
  name: "anthropic",
@@ -1255,7 +1350,7 @@ async function ensureConfig(cwd) {
1255
1350
  apiKey
1256
1351
  }
1257
1352
  };
1258
- (0, import_node_fs2.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
1353
+ (0, import_node_fs3.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
1259
1354
  process.stdout.write(`
1260
1355
  Config saved to ${userPath}
1261
1356
 
@@ -1263,8 +1358,8 @@ async function ensureConfig(cwd) {
1263
1358
  }
1264
1359
  function resetConfig() {
1265
1360
  const userPath = getUserSettingsPath();
1266
- if ((0, import_node_fs2.existsSync)(userPath)) {
1267
- (0, import_node_fs2.unlinkSync)(userPath);
1361
+ if ((0, import_node_fs3.existsSync)(userPath)) {
1362
+ (0, import_node_fs3.unlinkSync)(userPath);
1268
1363
  process.stdout.write(`Deleted ${userPath}
1269
1364
  `);
1270
1365
  } else {
package/dist/node/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startCli
4
- } from "./chunk-73QW5EEP.js";
4
+ } from "./chunk-OZHWJAII.js";
5
5
 
6
6
  // src/bin.ts
7
7
  process.on("uncaughtException", (err) => {