@robota-sdk/agent-cli 3.0.0-beta.15 → 3.0.0-beta.17

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.
@@ -40,21 +40,275 @@ module.exports = __toCommonJS(index_exports);
40
40
  var import_agent_sdk4 = require("@robota-sdk/agent-sdk");
41
41
 
42
42
  // src/cli.ts
43
- var import_node_util = require("util");
44
- var import_node_fs2 = require("fs");
45
- var import_node_path2 = require("path");
43
+ var import_node_fs3 = require("fs");
44
+ var import_node_path3 = require("path");
46
45
  var import_node_url = require("url");
47
- var readline = __toESM(require("readline"), 1);
48
46
  var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
49
47
  var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
50
48
 
49
+ // src/utils/cli-args.ts
50
+ var import_node_util = require("util");
51
+ var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
52
+ function parsePermissionMode(raw) {
53
+ if (raw === void 0) return void 0;
54
+ if (!VALID_MODES.includes(raw)) {
55
+ process.stderr.write(`Invalid --permission-mode "${raw}". Valid: ${VALID_MODES.join(" | ")}
56
+ `);
57
+ process.exit(1);
58
+ }
59
+ return raw;
60
+ }
61
+ function parseMaxTurns(raw) {
62
+ if (raw === void 0) return void 0;
63
+ const n = parseInt(raw, 10);
64
+ if (isNaN(n) || n <= 0) {
65
+ process.stderr.write(`Invalid --max-turns "${raw}". Must be a positive integer.
66
+ `);
67
+ process.exit(1);
68
+ }
69
+ return n;
70
+ }
71
+ function parseCliArgs() {
72
+ const { values, positionals } = (0, import_node_util.parseArgs)({
73
+ allowPositionals: true,
74
+ options: {
75
+ p: { type: "boolean", short: "p", default: false },
76
+ c: { type: "boolean", short: "c", default: false },
77
+ r: { type: "string", short: "r" },
78
+ model: { type: "string" },
79
+ "permission-mode": { type: "string" },
80
+ "max-turns": { type: "string" },
81
+ version: { type: "boolean", default: false },
82
+ reset: { type: "boolean", default: false }
83
+ }
84
+ });
85
+ return {
86
+ positional: positionals,
87
+ printMode: values["p"] ?? false,
88
+ continueMode: values["c"] ?? false,
89
+ resumeId: values["r"],
90
+ model: values["model"],
91
+ permissionMode: parsePermissionMode(values["permission-mode"]),
92
+ maxTurns: parseMaxTurns(values["max-turns"]),
93
+ version: values["version"] ?? false,
94
+ reset: values["reset"] ?? false
95
+ };
96
+ }
97
+
98
+ // src/utils/settings-io.ts
99
+ var import_node_fs = require("fs");
100
+ var import_node_path = require("path");
101
+ function getUserSettingsPath() {
102
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
103
+ return (0, import_node_path.join)(home, ".robota", "settings.json");
104
+ }
105
+ function readSettings(path) {
106
+ if (!(0, import_node_fs.existsSync)(path)) return {};
107
+ return JSON.parse((0, import_node_fs.readFileSync)(path, "utf8"));
108
+ }
109
+ function writeSettings(path, settings) {
110
+ (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path), { recursive: true });
111
+ (0, import_node_fs.writeFileSync)(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
112
+ }
113
+ function updateModelInSettings(settingsPath, modelId) {
114
+ const settings = readSettings(settingsPath);
115
+ const provider = settings.provider ?? {};
116
+ provider.model = modelId;
117
+ settings.provider = provider;
118
+ writeSettings(settingsPath, settings);
119
+ }
120
+ function deleteSettings(path) {
121
+ if ((0, import_node_fs.existsSync)(path)) {
122
+ (0, import_node_fs.unlinkSync)(path);
123
+ return true;
124
+ }
125
+ return false;
126
+ }
127
+
128
+ // src/print-terminal.ts
129
+ var readline = __toESM(require("readline"), 1);
130
+ var PrintTerminal = class {
131
+ write(text) {
132
+ process.stdout.write(text);
133
+ }
134
+ writeLine(text) {
135
+ process.stdout.write(text + "\n");
136
+ }
137
+ writeMarkdown(md) {
138
+ process.stdout.write(md);
139
+ }
140
+ writeError(text) {
141
+ process.stderr.write(text + "\n");
142
+ }
143
+ prompt(question) {
144
+ return new Promise((resolve) => {
145
+ const rl = readline.createInterface({
146
+ input: process.stdin,
147
+ output: process.stdout,
148
+ terminal: false,
149
+ historySize: 0
150
+ });
151
+ rl.question(question, (answer) => {
152
+ rl.close();
153
+ resolve(answer);
154
+ });
155
+ });
156
+ }
157
+ async select(options, initialIndex = 0) {
158
+ for (let i = 0; i < options.length; i++) {
159
+ const marker = i === initialIndex ? ">" : " ";
160
+ process.stdout.write(` ${marker} ${i + 1}) ${options[i]}
161
+ `);
162
+ }
163
+ const answer = await this.prompt(
164
+ ` Choose [1-${options.length}] (default: ${options[initialIndex]}): `
165
+ );
166
+ const trimmed = answer.trim().toLowerCase();
167
+ if (trimmed === "") return initialIndex;
168
+ const num = parseInt(trimmed, 10);
169
+ if (!isNaN(num) && num >= 1 && num <= options.length) return num - 1;
170
+ return initialIndex;
171
+ }
172
+ spinner(_message) {
173
+ return { stop() {
174
+ }, update() {
175
+ } };
176
+ }
177
+ };
178
+
51
179
  // src/ui/render.tsx
180
+ var import_ink10 = require("ink");
181
+
182
+ // src/ui/App.tsx
183
+ var import_react6 = require("react");
52
184
  var import_ink9 = require("ink");
53
185
 
186
+ // src/commands/slash-executor.ts
187
+ var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
188
+ var HELP_TEXT = [
189
+ "Available commands:",
190
+ " /help \u2014 Show this help",
191
+ " /clear \u2014 Clear conversation",
192
+ " /compact [instr] \u2014 Compact context (optional focus instructions)",
193
+ " /mode [m] \u2014 Show/change permission mode",
194
+ " /cost \u2014 Show session info",
195
+ " /reset \u2014 Delete settings and exit",
196
+ " /exit \u2014 Exit CLI"
197
+ ].join("\n");
198
+ function handleHelp(addMessage) {
199
+ addMessage({ role: "system", content: HELP_TEXT });
200
+ return { handled: true };
201
+ }
202
+ function handleClear(addMessage, clearMessages, session) {
203
+ clearMessages();
204
+ session.clearHistory();
205
+ addMessage({ role: "system", content: "Conversation cleared." });
206
+ return { handled: true };
207
+ }
208
+ async function handleCompact(args, session, addMessage) {
209
+ const instructions = args.trim() || void 0;
210
+ const before = session.getContextState().usedPercentage;
211
+ addMessage({ role: "system", content: "Compacting context..." });
212
+ await session.compact(instructions);
213
+ const after = session.getContextState().usedPercentage;
214
+ addMessage({
215
+ role: "system",
216
+ content: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`
217
+ });
218
+ return { handled: true };
219
+ }
220
+ function handleMode(arg, session, addMessage) {
221
+ if (!arg) {
222
+ addMessage({ role: "system", content: `Current mode: ${session.getPermissionMode()}` });
223
+ } else if (VALID_MODES2.includes(arg)) {
224
+ session.setPermissionMode(arg);
225
+ addMessage({ role: "system", content: `Permission mode set to: ${arg}` });
226
+ } else {
227
+ addMessage({ role: "system", content: `Invalid mode. Valid: ${VALID_MODES2.join(" | ")}` });
228
+ }
229
+ return { handled: true };
230
+ }
231
+ function handleModel(modelId, addMessage) {
232
+ if (!modelId) {
233
+ addMessage({ role: "system", content: "Select a model from the /model submenu." });
234
+ return { handled: true };
235
+ }
236
+ return { handled: true, pendingModelId: modelId };
237
+ }
238
+ function handleCost(session, addMessage) {
239
+ addMessage({
240
+ role: "system",
241
+ content: `Session: ${session.getSessionId()}
242
+ Messages: ${session.getMessageCount()}`
243
+ });
244
+ return { handled: true };
245
+ }
246
+ function handlePermissions(session, addMessage) {
247
+ const mode = session.getPermissionMode();
248
+ const sessionAllowed = session.getSessionAllowedTools();
249
+ const lines = [`Permission mode: ${mode}`];
250
+ if (sessionAllowed.length > 0) {
251
+ lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
252
+ } else {
253
+ lines.push("No session-approved tools.");
254
+ }
255
+ addMessage({ role: "system", content: lines.join("\n") });
256
+ return { handled: true };
257
+ }
258
+ function handleContext(session, addMessage) {
259
+ const ctx = session.getContextState();
260
+ addMessage({
261
+ role: "system",
262
+ content: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`
263
+ });
264
+ return { handled: true };
265
+ }
266
+ function handleReset(addMessage) {
267
+ const settingsPath = getUserSettingsPath();
268
+ if (deleteSettings(settingsPath)) {
269
+ addMessage({ role: "system", content: `Deleted ${settingsPath}. Exiting...` });
270
+ } else {
271
+ addMessage({ role: "system", content: "No user settings found." });
272
+ }
273
+ return { handled: true, exitRequested: true };
274
+ }
275
+ async function executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry) {
276
+ switch (cmd) {
277
+ case "help":
278
+ return handleHelp(addMessage);
279
+ case "clear":
280
+ return handleClear(addMessage, clearMessages, session);
281
+ case "compact":
282
+ return handleCompact(args, session, addMessage);
283
+ case "mode":
284
+ return handleMode(args.split(/\s+/)[0] || void 0, session, addMessage);
285
+ case "model":
286
+ return handleModel(args.split(/\s+/)[0] || void 0, addMessage);
287
+ case "cost":
288
+ return handleCost(session, addMessage);
289
+ case "permissions":
290
+ return handlePermissions(session, addMessage);
291
+ case "context":
292
+ return handleContext(session, addMessage);
293
+ case "reset":
294
+ return handleReset(addMessage);
295
+ case "exit":
296
+ return { handled: true, exitRequested: true };
297
+ default: {
298
+ const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
299
+ if (skillCmd) {
300
+ addMessage({ role: "system", content: `Invoking skill: ${cmd}` });
301
+ return { handled: false };
302
+ }
303
+ addMessage({ role: "system", content: `Unknown command "/${cmd}". Type /help for help.` });
304
+ return { handled: true };
305
+ }
306
+ }
307
+ }
308
+
54
309
  // src/ui/App.tsx
55
- var import_react5 = require("react");
56
- var import_ink8 = require("ink");
57
310
  var import_agent_sdk = require("@robota-sdk/agent-sdk");
311
+ var import_agent_core3 = require("@robota-sdk/agent-core");
58
312
 
59
313
  // src/commands/command-registry.ts
60
314
  var CommandRegistry = class {
@@ -87,6 +341,21 @@ var CommandRegistry = class {
87
341
  };
88
342
 
89
343
  // src/commands/builtin-source.ts
344
+ var import_agent_core = require("@robota-sdk/agent-core");
345
+ function buildModelSubcommands() {
346
+ const seen = /* @__PURE__ */ new Set();
347
+ const commands = [];
348
+ for (const model of Object.values(import_agent_core.CLAUDE_MODELS)) {
349
+ if (seen.has(model.name)) continue;
350
+ seen.add(model.name);
351
+ commands.push({
352
+ name: model.id,
353
+ description: `${model.name} (${(0, import_agent_core.formatTokenCount)(model.contextWindow).toUpperCase()})`,
354
+ source: "builtin"
355
+ });
356
+ }
357
+ return commands;
358
+ }
90
359
  function createBuiltinCommands() {
91
360
  return [
92
361
  { name: "help", description: "Show available commands", source: "builtin" },
@@ -106,11 +375,7 @@ function createBuiltinCommands() {
106
375
  name: "model",
107
376
  description: "Select AI model",
108
377
  source: "builtin",
109
- subcommands: [
110
- { name: "claude-opus-4-6", description: "Opus 4.6 (highest quality)", source: "builtin" },
111
- { name: "claude-sonnet-4-6", description: "Sonnet 4.6 (balanced)", source: "builtin" },
112
- { name: "claude-haiku-4-5", description: "Haiku 4.5 (fastest)", source: "builtin" }
113
- ]
378
+ subcommands: buildModelSubcommands()
114
379
  },
115
380
  { name: "compact", description: "Compress context window", source: "builtin" },
116
381
  { name: "cost", description: "Show session info", source: "builtin" },
@@ -132,8 +397,8 @@ var BuiltinCommandSource = class {
132
397
  };
133
398
 
134
399
  // src/commands/skill-source.ts
135
- var import_node_fs = require("fs");
136
- var import_node_path = require("path");
400
+ var import_node_fs2 = require("fs");
401
+ var import_node_path2 = require("path");
137
402
  var import_node_os = require("os");
138
403
  function parseFrontmatter(content) {
139
404
  const lines = content.split("\n");
@@ -156,14 +421,14 @@ function parseFrontmatter(content) {
156
421
  return name ? { name, description } : null;
157
422
  }
158
423
  function scanSkillsDir(skillsDir) {
159
- if (!(0, import_node_fs.existsSync)(skillsDir)) return [];
424
+ if (!(0, import_node_fs2.existsSync)(skillsDir)) return [];
160
425
  const commands = [];
161
- const entries = (0, import_node_fs.readdirSync)(skillsDir, { withFileTypes: true });
426
+ const entries = (0, import_node_fs2.readdirSync)(skillsDir, { withFileTypes: true });
162
427
  for (const entry of entries) {
163
428
  if (!entry.isDirectory()) continue;
164
- const skillFile = (0, import_node_path.join)(skillsDir, entry.name, "SKILL.md");
165
- if (!(0, import_node_fs.existsSync)(skillFile)) continue;
166
- const content = (0, import_node_fs.readFileSync)(skillFile, "utf-8");
429
+ const skillFile = (0, import_node_path2.join)(skillsDir, entry.name, "SKILL.md");
430
+ if (!(0, import_node_fs2.existsSync)(skillFile)) continue;
431
+ const content = (0, import_node_fs2.readFileSync)(skillFile, "utf-8");
167
432
  const frontmatter = parseFrontmatter(content);
168
433
  commands.push({
169
434
  name: frontmatter?.name ?? entry.name,
@@ -183,8 +448,8 @@ var SkillCommandSource = class {
183
448
  }
184
449
  getCommands() {
185
450
  if (this.cachedCommands) return this.cachedCommands;
186
- const projectSkills = scanSkillsDir((0, import_node_path.join)(this.cwd, ".agents", "skills"));
187
- const userSkills = scanSkillsDir((0, import_node_path.join)((0, import_node_os.homedir)(), ".claude", "skills"));
451
+ const projectSkills = scanSkillsDir((0, import_node_path2.join)(this.cwd, ".agents", "skills"));
452
+ const userSkills = scanSkillsDir((0, import_node_path2.join)((0, import_node_os.homedir)(), ".claude", "skills"));
188
453
  const seen = new Set(projectSkills.map((cmd) => cmd.name));
189
454
  const merged = [...projectSkills];
190
455
  for (const cmd of userSkills) {
@@ -258,6 +523,7 @@ function MessageList({ messages }) {
258
523
 
259
524
  // src/ui/StatusBar.tsx
260
525
  var import_ink2 = require("ink");
526
+ var import_agent_core2 = require("@robota-sdk/agent-core");
261
527
  var import_jsx_runtime2 = require("react/jsx-runtime");
262
528
  var CONTEXT_YELLOW_THRESHOLD = 70;
263
529
  var CONTEXT_RED_THRESHOLD = 90;
@@ -297,10 +563,10 @@ function StatusBar({
297
563
  "Context: ",
298
564
  Math.round(contextPercentage),
299
565
  "% (",
300
- (contextUsedTokens / 1e3).toFixed(1),
301
- "k/",
302
- (contextMaxTokens / 1e3).toFixed(0),
303
- "k)"
566
+ (0, import_agent_core2.formatTokenCount)(contextUsedTokens),
567
+ "/",
568
+ (0, import_agent_core2.formatTokenCount)(contextMaxTokens),
569
+ ")"
304
570
  ] })
305
571
  ] }),
306
572
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { children: [
@@ -447,19 +713,13 @@ var import_jsx_runtime5 = require("react/jsx-runtime");
447
713
  var MAX_VISIBLE = 8;
448
714
  function CommandRow(props) {
449
715
  const { cmd, isSelected, showSlash } = props;
450
- const prefix = showSlash ? "/" : "";
451
716
  const indicator = isSelected ? "\u25B8 " : " ";
452
717
  const nameColor = isSelected ? "cyan" : void 0;
453
718
  const dimmed = !isSelected;
454
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink5.Box, { children: [
455
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink5.Text, { color: nameColor, dimColor: dimmed, children: [
456
- indicator,
457
- prefix,
458
- cmd.name
459
- ] }),
460
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { dimColor: dimmed, children: " " }),
461
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { color: nameColor, dimColor: dimmed, children: cmd.description })
462
- ] });
719
+ 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: [
720
+ indicator,
721
+ showSlash ? `/${cmd.name} ${cmd.description}` : cmd.description
722
+ ] }) });
463
723
  }
464
724
  function SlashAutocomplete({
465
725
  commands,
@@ -624,10 +884,53 @@ function InputArea({ onSubmit, isDisabled, registry }) {
624
884
  ] });
625
885
  }
626
886
 
627
- // src/ui/PermissionPrompt.tsx
628
- var import_react4 = __toESM(require("react"), 1);
887
+ // src/ui/ConfirmPrompt.tsx
888
+ var import_react4 = require("react");
629
889
  var import_ink7 = require("ink");
630
890
  var import_jsx_runtime7 = require("react/jsx-runtime");
891
+ function ConfirmPrompt({
892
+ message,
893
+ options = ["Yes", "No"],
894
+ onSelect
895
+ }) {
896
+ const [selected, setSelected] = (0, import_react4.useState)(0);
897
+ const resolvedRef = (0, import_react4.useRef)(false);
898
+ const doSelect = (0, import_react4.useCallback)(
899
+ (index) => {
900
+ if (resolvedRef.current) return;
901
+ resolvedRef.current = true;
902
+ onSelect(index);
903
+ },
904
+ [onSelect]
905
+ );
906
+ (0, import_ink7.useInput)((input, key) => {
907
+ if (resolvedRef.current) return;
908
+ if (key.leftArrow || key.upArrow) {
909
+ setSelected((prev) => prev > 0 ? prev - 1 : prev);
910
+ } else if (key.rightArrow || key.downArrow) {
911
+ setSelected((prev) => prev < options.length - 1 ? prev + 1 : prev);
912
+ } else if (key.return) {
913
+ doSelect(selected);
914
+ } else if (input === "y" && options.length === 2) {
915
+ doSelect(0);
916
+ } else if (input === "n" && options.length === 2) {
917
+ doSelect(1);
918
+ }
919
+ });
920
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
921
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "yellow", children: message }),
922
+ /* @__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: [
923
+ i === selected ? "> " : " ",
924
+ opt
925
+ ] }) }, opt)) }),
926
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { dimColor: true, children: " arrow keys to select, Enter to confirm" })
927
+ ] });
928
+ }
929
+
930
+ // src/ui/PermissionPrompt.tsx
931
+ var import_react5 = __toESM(require("react"), 1);
932
+ var import_ink8 = require("ink");
933
+ var import_jsx_runtime8 = require("react/jsx-runtime");
631
934
  var OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
632
935
  function formatArgs(args) {
633
936
  const entries = Object.entries(args);
@@ -635,15 +938,15 @@ function formatArgs(args) {
635
938
  return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
636
939
  }
637
940
  function PermissionPrompt({ request }) {
638
- const [selected, setSelected] = import_react4.default.useState(0);
639
- const resolvedRef = import_react4.default.useRef(false);
640
- const prevRequestRef = import_react4.default.useRef(request);
941
+ const [selected, setSelected] = import_react5.default.useState(0);
942
+ const resolvedRef = import_react5.default.useRef(false);
943
+ const prevRequestRef = import_react5.default.useRef(request);
641
944
  if (prevRequestRef.current !== request) {
642
945
  prevRequestRef.current = request;
643
946
  resolvedRef.current = false;
644
947
  setSelected(0);
645
948
  }
646
- const doResolve = import_react4.default.useCallback(
949
+ const doResolve = import_react5.default.useCallback(
647
950
  (index) => {
648
951
  if (resolvedRef.current) return;
649
952
  resolvedRef.current = true;
@@ -653,7 +956,7 @@ function PermissionPrompt({ request }) {
653
956
  },
654
957
  [request]
655
958
  );
656
- (0, import_ink7.useInput)((input, key) => {
959
+ (0, import_ink8.useInput)((input, key) => {
657
960
  if (resolvedRef.current) return;
658
961
  if (key.upArrow || key.leftArrow) {
659
962
  setSelected((prev) => prev > 0 ? prev - 1 : prev);
@@ -669,27 +972,54 @@ function PermissionPrompt({ request }) {
669
972
  doResolve(2);
670
973
  }
671
974
  });
672
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
673
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "yellow", bold: true, children: "[Permission Required]" }),
674
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { children: [
975
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
976
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "yellow", bold: true, children: "[Permission Required]" }),
977
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { children: [
675
978
  "Tool:",
676
979
  " ",
677
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "cyan", bold: true, children: request.toolName })
980
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "cyan", bold: true, children: request.toolName })
678
981
  ] }),
679
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { dimColor: true, children: [
982
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { dimColor: true, children: [
680
983
  " ",
681
984
  formatArgs(request.toolArgs)
682
985
  ] }),
683
- /* @__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: [
986
+ /* @__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: [
684
987
  i === selected ? "> " : " ",
685
988
  opt
686
989
  ] }) }, opt)) }),
687
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { dimColor: true, children: " left/right to select, Enter to confirm" })
990
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { dimColor: true, children: " left/right to select, Enter to confirm" })
688
991
  ] });
689
992
  }
690
993
 
994
+ // src/utils/tool-call-extractor.ts
995
+ var TOOL_ARG_MAX_LENGTH = 80;
996
+ var TOOL_ARG_TRUNCATE_LENGTH = 77;
997
+ function extractToolCalls(history, startIndex) {
998
+ const lines = [];
999
+ for (let i = startIndex; i < history.length; i++) {
1000
+ const msg = history[i];
1001
+ if (msg.role === "assistant" && msg.toolCalls) {
1002
+ for (const tc of msg.toolCalls) {
1003
+ const value = parseFirstArgValue(tc.function.arguments);
1004
+ const truncated = value.length > TOOL_ARG_MAX_LENGTH ? value.slice(0, TOOL_ARG_TRUNCATE_LENGTH) + "..." : value;
1005
+ lines.push(`${tc.function.name}(${truncated})`);
1006
+ }
1007
+ }
1008
+ }
1009
+ return lines;
1010
+ }
1011
+ function parseFirstArgValue(argsJson) {
1012
+ try {
1013
+ const parsed = JSON.parse(argsJson);
1014
+ const firstVal = Object.values(parsed)[0];
1015
+ return typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
1016
+ } catch {
1017
+ return argsJson;
1018
+ }
1019
+ }
1020
+
691
1021
  // src/ui/App.tsx
692
- var import_jsx_runtime8 = require("react/jsx-runtime");
1022
+ var import_jsx_runtime9 = require("react/jsx-runtime");
693
1023
  var msgIdCounter = 0;
694
1024
  function nextId() {
695
1025
  msgIdCounter += 1;
@@ -711,11 +1041,11 @@ var NOOP_TERMINAL = {
711
1041
  } })
712
1042
  };
713
1043
  function useSession(props) {
714
- const [permissionRequest, setPermissionRequest] = (0, import_react5.useState)(null);
715
- const [streamingText, setStreamingText] = (0, import_react5.useState)("");
716
- const permissionQueueRef = (0, import_react5.useRef)([]);
717
- const processingRef = (0, import_react5.useRef)(false);
718
- const processNextPermission = (0, import_react5.useCallback)(() => {
1044
+ const [permissionRequest, setPermissionRequest] = (0, import_react6.useState)(null);
1045
+ const [streamingText, setStreamingText] = (0, import_react6.useState)("");
1046
+ const permissionQueueRef = (0, import_react6.useRef)([]);
1047
+ const processingRef = (0, import_react6.useRef)(false);
1048
+ const processNextPermission = (0, import_react6.useCallback)(() => {
719
1049
  if (processingRef.current) return;
720
1050
  const next = permissionQueueRef.current[0];
721
1051
  if (!next) {
@@ -735,7 +1065,7 @@ function useSession(props) {
735
1065
  }
736
1066
  });
737
1067
  }, []);
738
- const sessionRef = (0, import_react5.useRef)(null);
1068
+ const sessionRef = (0, import_react6.useRef)(null);
739
1069
  if (sessionRef.current === null) {
740
1070
  const permissionHandler = (toolName, toolArgs) => {
741
1071
  return new Promise((resolve) => {
@@ -760,138 +1090,49 @@ function useSession(props) {
760
1090
  onTextDelta
761
1091
  });
762
1092
  }
763
- const clearStreamingText = (0, import_react5.useCallback)(() => setStreamingText(""), []);
1093
+ const clearStreamingText = (0, import_react6.useCallback)(() => setStreamingText(""), []);
764
1094
  return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText };
765
1095
  }
766
1096
  function useMessages() {
767
- const [messages, setMessages] = (0, import_react5.useState)([]);
768
- const addMessage = (0, import_react5.useCallback)((msg) => {
1097
+ const [messages, setMessages] = (0, import_react6.useState)([]);
1098
+ const addMessage = (0, import_react6.useCallback)((msg) => {
769
1099
  setMessages((prev) => [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }]);
770
1100
  }, []);
771
1101
  return { messages, setMessages, addMessage };
772
1102
  }
773
- var HELP_TEXT = [
774
- "Available commands:",
775
- " /help \u2014 Show this help",
776
- " /clear \u2014 Clear conversation",
777
- " /compact [instr] \u2014 Compact context (optional focus instructions)",
778
- " /mode [m] \u2014 Show/change permission mode",
779
- " /cost \u2014 Show session info",
780
- " /reset \u2014 Delete settings and exit",
781
- " /exit \u2014 Exit CLI"
782
- ].join("\n");
783
- function handleModeCommand(arg, session, addMessage) {
784
- const validModes = ["plan", "default", "acceptEdits", "bypassPermissions"];
785
- if (!arg) {
786
- addMessage({ role: "system", content: `Current mode: ${session.getPermissionMode()}` });
787
- } else if (validModes.includes(arg)) {
788
- session.setPermissionMode(arg);
789
- addMessage({ role: "system", content: `Permission mode set to: ${arg}` });
790
- } else {
791
- addMessage({ role: "system", content: `Invalid mode. Valid: ${validModes.join(" | ")}` });
792
- }
793
- return true;
794
- }
795
- async function executeSlashCommand(cmd, parts, session, addMessage, setMessages, exit, registry) {
796
- switch (cmd) {
797
- case "help":
798
- addMessage({ role: "system", content: HELP_TEXT });
799
- return true;
800
- case "clear":
801
- setMessages([]);
802
- session.clearHistory();
803
- addMessage({ role: "system", content: "Conversation cleared." });
804
- return true;
805
- case "compact": {
806
- const instructions = parts.slice(1).join(" ").trim() || void 0;
807
- const before = session.getContextState().usedPercentage;
808
- addMessage({ role: "system", content: "Compacting context..." });
809
- await session.compact(instructions);
810
- const after = session.getContextState().usedPercentage;
811
- addMessage({
812
- role: "system",
813
- content: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`
814
- });
815
- return true;
816
- }
817
- case "mode":
818
- return handleModeCommand(parts[1], session, addMessage);
819
- case "cost":
820
- addMessage({
821
- role: "system",
822
- content: `Session: ${session.getSessionId()}
823
- Messages: ${session.getMessageCount()}`
824
- });
825
- return true;
826
- case "permissions": {
827
- const mode = session.getPermissionMode();
828
- const sessionAllowed = session.getSessionAllowedTools();
829
- const lines = [`Permission mode: ${mode}`];
830
- if (sessionAllowed.length > 0) {
831
- lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
832
- } else {
833
- lines.push("No session-approved tools.");
834
- }
835
- addMessage({ role: "system", content: lines.join("\n") });
836
- return true;
837
- }
838
- case "context": {
839
- const ctx = session.getContextState();
840
- addMessage({
841
- role: "system",
842
- content: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`
843
- });
844
- return true;
845
- }
846
- case "reset": {
847
- const { existsSync: exists, unlinkSync: unlink } = await import("fs");
848
- const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
849
- const settingsPath = `${home}/.robota/settings.json`;
850
- if (exists(settingsPath)) {
851
- unlink(settingsPath);
852
- addMessage({ role: "system", content: `Deleted ${settingsPath}. Exiting...` });
853
- } else {
854
- addMessage({ role: "system", content: "No user settings found." });
855
- }
856
- setTimeout(() => exit(), 500);
857
- return true;
858
- }
859
- case "exit":
860
- exit();
861
- return true;
862
- default: {
863
- const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
864
- if (skillCmd) {
865
- addMessage({ role: "system", content: `Invoking skill: ${cmd}` });
866
- return false;
867
- }
868
- addMessage({ role: "system", content: `Unknown command "/${cmd}". Type /help for help.` });
869
- return true;
870
- }
871
- }
872
- }
873
- function useSlashCommands(session, addMessage, setMessages, exit, registry) {
874
- return (0, import_react5.useCallback)(
1103
+ var EXIT_DELAY_MS = 500;
1104
+ function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId) {
1105
+ return (0, import_react6.useCallback)(
875
1106
  async (input) => {
876
1107
  const parts = input.slice(1).split(/\s+/);
877
1108
  const cmd = parts[0]?.toLowerCase() ?? "";
878
- return executeSlashCommand(cmd, parts, session, addMessage, setMessages, exit, registry);
1109
+ const args = parts.slice(1).join(" ");
1110
+ const clearMessages = () => setMessages([]);
1111
+ const result = await executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry);
1112
+ if (result.pendingModelId) {
1113
+ pendingModelChangeRef.current = result.pendingModelId;
1114
+ setPendingModelId(result.pendingModelId);
1115
+ }
1116
+ if (result.exitRequested) {
1117
+ setTimeout(() => exit(), EXIT_DELAY_MS);
1118
+ }
1119
+ return result.handled;
879
1120
  },
880
- [session, addMessage, setMessages, exit, registry]
1121
+ [session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId]
881
1122
  );
882
1123
  }
883
1124
  function StreamingIndicator({ text }) {
884
1125
  if (text) {
885
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", children: [
886
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { color: "cyan", bold: true, children: [
1126
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", children: [
1127
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { color: "cyan", bold: true, children: [
887
1128
  "Robota:",
888
1129
  " "
889
1130
  ] }),
890
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { children: " " }),
891
- /* @__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) }) })
1131
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " }),
1132
+ /* @__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) }) })
892
1133
  ] });
893
1134
  }
894
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "yellow", children: "Thinking..." });
1135
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "yellow", children: "Thinking..." });
895
1136
  }
896
1137
  async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextPercentage) {
897
1138
  setIsThinking(true);
@@ -901,24 +1142,10 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
901
1142
  const response = await session.run(prompt);
902
1143
  clearStreamingText();
903
1144
  const history = session.getHistory();
904
- const toolLines = [];
905
- for (let i = historyBefore; i < history.length; i++) {
906
- const msg = history[i];
907
- if (msg.role === "assistant" && msg.toolCalls) {
908
- for (const tc of msg.toolCalls) {
909
- let value = "";
910
- try {
911
- const parsed = JSON.parse(tc.function.arguments);
912
- const firstVal = Object.values(parsed)[0];
913
- value = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
914
- } catch {
915
- value = tc.function.arguments;
916
- }
917
- const truncated = value.length > 80 ? value.slice(0, 77) + "..." : value;
918
- toolLines.push(`${tc.function.name}(${truncated})`);
919
- }
920
- }
921
- }
1145
+ const toolLines = extractToolCalls(
1146
+ history,
1147
+ historyBefore
1148
+ );
922
1149
  if (toolLines.length > 0) {
923
1150
  addMessage({ role: "tool", content: toolLines.join("\n"), toolName: `${toolLines.length} tools` });
924
1151
  }
@@ -953,7 +1180,7 @@ Execute the "${cmd}" skill: ${userInstruction}`;
953
1180
  return `Use the "${cmd}" skill: ${userInstruction}`;
954
1181
  }
955
1182
  function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextPercentage, registry) {
956
- return (0, import_react5.useCallback)(
1183
+ return (0, import_react6.useCallback)(
957
1184
  async (input) => {
958
1185
  if (input.startsWith("/")) {
959
1186
  const handled = await handleSlashCommand(input);
@@ -994,7 +1221,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
994
1221
  );
995
1222
  }
996
1223
  function useCommandRegistry(cwd) {
997
- const registryRef = (0, import_react5.useRef)(null);
1224
+ const registryRef = (0, import_react6.useRef)(null);
998
1225
  if (registryRef.current === null) {
999
1226
  const registry = new CommandRegistry();
1000
1227
  registry.addSource(new BuiltinCommandSource());
@@ -1004,13 +1231,15 @@ function useCommandRegistry(cwd) {
1004
1231
  return registryRef.current;
1005
1232
  }
1006
1233
  function App(props) {
1007
- const { exit } = (0, import_ink8.useApp)();
1234
+ const { exit } = (0, import_ink9.useApp)();
1008
1235
  const { session, permissionRequest, streamingText, clearStreamingText } = useSession(props);
1009
1236
  const { messages, setMessages, addMessage } = useMessages();
1010
- const [isThinking, setIsThinking] = (0, import_react5.useState)(false);
1011
- const [contextPercentage, setContextPercentage] = (0, import_react5.useState)(0);
1237
+ const [isThinking, setIsThinking] = (0, import_react6.useState)(false);
1238
+ const [contextPercentage, setContextPercentage] = (0, import_react6.useState)(0);
1012
1239
  const registry = useCommandRegistry(props.cwd ?? process.cwd());
1013
- const handleSlashCommand = useSlashCommands(session, addMessage, setMessages, exit, registry);
1240
+ const pendingModelChangeRef = (0, import_react6.useRef)(null);
1241
+ const [pendingModelId, setPendingModelId] = (0, import_react6.useState)(null);
1242
+ const handleSlashCommand = useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId);
1014
1243
  const handleSubmit = useSubmitHandler(
1015
1244
  session,
1016
1245
  addMessage,
@@ -1020,37 +1249,59 @@ function App(props) {
1020
1249
  setContextPercentage,
1021
1250
  registry
1022
1251
  );
1023
- (0, import_ink8.useInput)(
1252
+ (0, import_ink9.useInput)(
1024
1253
  (_input, key) => {
1025
1254
  if (key.ctrl && _input === "c") exit();
1026
1255
  if (key.escape && isThinking) session.abort();
1027
1256
  },
1028
1257
  { isActive: !permissionRequest }
1029
1258
  );
1030
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", children: [
1031
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
1032
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "cyan", bold: true, children: `
1259
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", children: [
1260
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
1261
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "cyan", bold: true, children: `
1033
1262
  ____ ___ ____ ___ _____ _
1034
1263
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
1035
1264
  | |_) | | | | _ \\| | | || | / _ \\
1036
1265
  | _ <| |_| | |_) | |_| || |/ ___ \\
1037
1266
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
1038
1267
  ` }),
1039
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { dimColor: true, children: [
1268
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { dimColor: true, children: [
1040
1269
  " v",
1041
1270
  props.version ?? "0.0.0"
1042
1271
  ] })
1043
1272
  ] }),
1044
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
1045
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MessageList, { messages }),
1046
- isThinking && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StreamingIndicator, { text: streamingText }) })
1273
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
1274
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MessageList, { messages }),
1275
+ isThinking && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StreamingIndicator, { text: streamingText }) })
1047
1276
  ] }),
1048
- permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PermissionPrompt, { request: permissionRequest }),
1049
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1277
+ permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PermissionPrompt, { request: permissionRequest }),
1278
+ pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1279
+ ConfirmPrompt,
1280
+ {
1281
+ message: `Change model to ${(0, import_agent_core3.getModelName)(pendingModelId)}? This will restart the session.`,
1282
+ onSelect: (index) => {
1283
+ setPendingModelId(null);
1284
+ pendingModelChangeRef.current = null;
1285
+ if (index === 0) {
1286
+ try {
1287
+ const settingsPath = getUserSettingsPath();
1288
+ updateModelInSettings(settingsPath, pendingModelId);
1289
+ addMessage({ role: "system", content: `Model changed to ${(0, import_agent_core3.getModelName)(pendingModelId)}. Restarting...` });
1290
+ setTimeout(() => exit(), 500);
1291
+ } catch (err) {
1292
+ addMessage({ role: "system", content: `Failed: ${err instanceof Error ? err.message : String(err)}` });
1293
+ }
1294
+ } else {
1295
+ addMessage({ role: "system", content: "Model change cancelled." });
1296
+ }
1297
+ }
1298
+ }
1299
+ ),
1300
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1050
1301
  StatusBar,
1051
1302
  {
1052
1303
  permissionMode: session.getPermissionMode(),
1053
- modelName: props.config.provider.model,
1304
+ modelName: (0, import_agent_core3.getModelName)(props.config.provider.model),
1054
1305
  sessionId: session.getSessionId(),
1055
1306
  messageCount: messages.length,
1056
1307
  isThinking,
@@ -1059,7 +1310,7 @@ function App(props) {
1059
1310
  contextMaxTokens: session.getContextState().maxTokens
1060
1311
  }
1061
1312
  ),
1062
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1313
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1063
1314
  InputArea,
1064
1315
  {
1065
1316
  onSubmit: handleSubmit,
@@ -1067,12 +1318,12 @@ function App(props) {
1067
1318
  registry
1068
1319
  }
1069
1320
  ),
1070
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { children: " " })
1321
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " })
1071
1322
  ] });
1072
1323
  }
1073
1324
 
1074
1325
  // src/ui/render.tsx
1075
- var import_jsx_runtime9 = require("react/jsx-runtime");
1326
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1076
1327
  function renderApp(options) {
1077
1328
  process.on("unhandledRejection", (reason) => {
1078
1329
  process.stderr.write(`
@@ -1083,7 +1334,7 @@ function renderApp(options) {
1083
1334
  `);
1084
1335
  }
1085
1336
  });
1086
- const instance = (0, import_ink9.render)(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(App, { ...options }), {
1337
+ const instance = (0, import_ink10.render)(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(App, { ...options }), {
1087
1338
  exitOnCtrlC: true
1088
1339
  });
1089
1340
  instance.waitUntilExit().catch((err) => {
@@ -1097,15 +1348,14 @@ function renderApp(options) {
1097
1348
 
1098
1349
  // src/cli.ts
1099
1350
  var import_meta = {};
1100
- var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
1101
1351
  function readVersion() {
1102
1352
  try {
1103
1353
  const thisFile = (0, import_node_url.fileURLToPath)(import_meta.url);
1104
- const dir = (0, import_node_path2.dirname)(thisFile);
1105
- const candidates = [(0, import_node_path2.join)(dir, "..", "..", "package.json"), (0, import_node_path2.join)(dir, "..", "package.json")];
1354
+ const dir = (0, import_node_path3.dirname)(thisFile);
1355
+ const candidates = [(0, import_node_path3.join)(dir, "..", "..", "package.json"), (0, import_node_path3.join)(dir, "..", "package.json")];
1106
1356
  for (const pkgPath of candidates) {
1107
1357
  try {
1108
- const raw = (0, import_node_fs2.readFileSync)(pkgPath, "utf-8");
1358
+ const raw = (0, import_node_fs3.readFileSync)(pkgPath, "utf-8");
1109
1359
  const pkg = JSON.parse(raw);
1110
1360
  if (pkg.version !== void 0 && pkg.name !== void 0) {
1111
1361
  return pkg.version;
@@ -1118,108 +1368,11 @@ function readVersion() {
1118
1368
  return "0.0.0";
1119
1369
  }
1120
1370
  }
1121
- function parsePermissionMode(raw) {
1122
- if (raw === void 0) return void 0;
1123
- if (!VALID_MODES.includes(raw)) {
1124
- process.stderr.write(`Invalid --permission-mode "${raw}". Valid: ${VALID_MODES.join(" | ")}
1125
- `);
1126
- process.exit(1);
1127
- }
1128
- return raw;
1129
- }
1130
- function parseMaxTurns(raw) {
1131
- if (raw === void 0) return void 0;
1132
- const n = parseInt(raw, 10);
1133
- if (isNaN(n) || n <= 0) {
1134
- process.stderr.write(`Invalid --max-turns "${raw}". Must be a positive integer.
1135
- `);
1136
- process.exit(1);
1137
- }
1138
- return n;
1139
- }
1140
- function parseCliArgs() {
1141
- const { values, positionals } = (0, import_node_util.parseArgs)({
1142
- allowPositionals: true,
1143
- options: {
1144
- p: { type: "boolean", short: "p", default: false },
1145
- c: { type: "boolean", short: "c", default: false },
1146
- r: { type: "string", short: "r" },
1147
- model: { type: "string" },
1148
- "permission-mode": { type: "string" },
1149
- "max-turns": { type: "string" },
1150
- version: { type: "boolean", default: false },
1151
- reset: { type: "boolean", default: false }
1152
- }
1153
- });
1154
- return {
1155
- positional: positionals,
1156
- printMode: values["p"] ?? false,
1157
- continueMode: values["c"] ?? false,
1158
- resumeId: values["r"],
1159
- model: values["model"],
1160
- permissionMode: parsePermissionMode(values["permission-mode"]),
1161
- maxTurns: parseMaxTurns(values["max-turns"]),
1162
- version: values["version"] ?? false,
1163
- reset: values["reset"] ?? false
1164
- };
1165
- }
1166
- var PrintTerminal = class {
1167
- write(text) {
1168
- process.stdout.write(text);
1169
- }
1170
- writeLine(text) {
1171
- process.stdout.write(text + "\n");
1172
- }
1173
- writeMarkdown(md) {
1174
- process.stdout.write(md);
1175
- }
1176
- writeError(text) {
1177
- process.stderr.write(text + "\n");
1178
- }
1179
- prompt(question) {
1180
- return new Promise((resolve) => {
1181
- const rl = readline.createInterface({
1182
- input: process.stdin,
1183
- output: process.stdout,
1184
- terminal: false,
1185
- historySize: 0
1186
- });
1187
- rl.question(question, (answer) => {
1188
- rl.close();
1189
- resolve(answer);
1190
- });
1191
- });
1192
- }
1193
- async select(options, initialIndex = 0) {
1194
- for (let i = 0; i < options.length; i++) {
1195
- const marker = i === initialIndex ? ">" : " ";
1196
- process.stdout.write(` ${marker} ${i + 1}) ${options[i]}
1197
- `);
1198
- }
1199
- const answer = await this.prompt(
1200
- ` Choose [1-${options.length}] (default: ${options[initialIndex]}): `
1201
- );
1202
- const trimmed = answer.trim().toLowerCase();
1203
- if (trimmed === "") return initialIndex;
1204
- const num = parseInt(trimmed, 10);
1205
- if (!isNaN(num) && num >= 1 && num <= options.length) return num - 1;
1206
- return initialIndex;
1207
- }
1208
- spinner(_message) {
1209
- return { stop() {
1210
- }, update() {
1211
- } };
1212
- }
1213
- };
1214
- function getUserSettingsPath() {
1215
- const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
1216
- return (0, import_node_path2.join)(home, ".robota", "settings.json");
1217
- }
1218
1371
  async function ensureConfig(cwd) {
1219
1372
  const userPath = getUserSettingsPath();
1220
- const projectPath = (0, import_node_path2.join)(cwd, ".robota", "settings.json");
1221
- const localPath = (0, import_node_path2.join)(cwd, ".robota", "settings.local.json");
1222
- if ((0, import_node_fs2.existsSync)(userPath) || (0, import_node_fs2.existsSync)(projectPath) || (0, import_node_fs2.existsSync)(localPath)) {
1373
+ const projectPath = (0, import_node_path3.join)(cwd, ".robota", "settings.json");
1374
+ const localPath = (0, import_node_path3.join)(cwd, ".robota", "settings.local.json");
1375
+ if ((0, import_node_fs3.existsSync)(userPath) || (0, import_node_fs3.existsSync)(projectPath) || (0, import_node_fs3.existsSync)(localPath)) {
1223
1376
  return;
1224
1377
  }
1225
1378
  process.stdout.write("\n");
@@ -1263,8 +1416,8 @@ async function ensureConfig(cwd) {
1263
1416
  process.stderr.write("\n No API key provided. Exiting.\n");
1264
1417
  process.exit(1);
1265
1418
  }
1266
- const settingsDir = (0, import_node_path2.dirname)(userPath);
1267
- (0, import_node_fs2.mkdirSync)(settingsDir, { recursive: true });
1419
+ const settingsDir = (0, import_node_path3.dirname)(userPath);
1420
+ (0, import_node_fs3.mkdirSync)(settingsDir, { recursive: true });
1268
1421
  const settings = {
1269
1422
  provider: {
1270
1423
  name: "anthropic",
@@ -1272,7 +1425,7 @@ async function ensureConfig(cwd) {
1272
1425
  apiKey
1273
1426
  }
1274
1427
  };
1275
- (0, import_node_fs2.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
1428
+ (0, import_node_fs3.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
1276
1429
  process.stdout.write(`
1277
1430
  Config saved to ${userPath}
1278
1431
 
@@ -1280,8 +1433,7 @@ async function ensureConfig(cwd) {
1280
1433
  }
1281
1434
  function resetConfig() {
1282
1435
  const userPath = getUserSettingsPath();
1283
- if ((0, import_node_fs2.existsSync)(userPath)) {
1284
- (0, import_node_fs2.unlinkSync)(userPath);
1436
+ if (deleteSettings(userPath)) {
1285
1437
  process.stdout.write(`Deleted ${userPath}
1286
1438
  `);
1287
1439
  } else {