@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.
@@ -1,9 +1,7 @@
1
1
  // src/cli.ts
2
- import { parseArgs } from "util";
3
- import { readFileSync as readFileSync2, existsSync as existsSync2, mkdirSync, writeFileSync, unlinkSync } from "fs";
4
- import { join as join2, dirname } from "path";
2
+ import { readFileSync as readFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
3
+ import { join as join3, dirname as dirname2 } from "path";
5
4
  import { fileURLToPath } from "url";
6
- import * as readline from "readline";
7
5
  import {
8
6
  loadConfig,
9
7
  loadContext,
@@ -15,13 +13,269 @@ import {
15
13
  } from "@robota-sdk/agent-sdk";
16
14
  import { promptForApproval } from "@robota-sdk/agent-sdk";
17
15
 
16
+ // src/utils/cli-args.ts
17
+ import { parseArgs } from "util";
18
+ var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
19
+ function parsePermissionMode(raw) {
20
+ if (raw === void 0) return void 0;
21
+ if (!VALID_MODES.includes(raw)) {
22
+ process.stderr.write(`Invalid --permission-mode "${raw}". Valid: ${VALID_MODES.join(" | ")}
23
+ `);
24
+ process.exit(1);
25
+ }
26
+ return raw;
27
+ }
28
+ function parseMaxTurns(raw) {
29
+ if (raw === void 0) return void 0;
30
+ const n = parseInt(raw, 10);
31
+ if (isNaN(n) || n <= 0) {
32
+ process.stderr.write(`Invalid --max-turns "${raw}". Must be a positive integer.
33
+ `);
34
+ process.exit(1);
35
+ }
36
+ return n;
37
+ }
38
+ function parseCliArgs() {
39
+ const { values, positionals } = parseArgs({
40
+ allowPositionals: true,
41
+ options: {
42
+ p: { type: "boolean", short: "p", default: false },
43
+ c: { type: "boolean", short: "c", default: false },
44
+ r: { type: "string", short: "r" },
45
+ model: { type: "string" },
46
+ "permission-mode": { type: "string" },
47
+ "max-turns": { type: "string" },
48
+ version: { type: "boolean", default: false },
49
+ reset: { type: "boolean", default: false }
50
+ }
51
+ });
52
+ return {
53
+ positional: positionals,
54
+ printMode: values["p"] ?? false,
55
+ continueMode: values["c"] ?? false,
56
+ resumeId: values["r"],
57
+ model: values["model"],
58
+ permissionMode: parsePermissionMode(values["permission-mode"]),
59
+ maxTurns: parseMaxTurns(values["max-turns"]),
60
+ version: values["version"] ?? false,
61
+ reset: values["reset"] ?? false
62
+ };
63
+ }
64
+
65
+ // src/utils/settings-io.ts
66
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from "fs";
67
+ import { join, dirname } from "path";
68
+ function getUserSettingsPath() {
69
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
70
+ return join(home, ".robota", "settings.json");
71
+ }
72
+ function readSettings(path) {
73
+ if (!existsSync(path)) return {};
74
+ return JSON.parse(readFileSync(path, "utf8"));
75
+ }
76
+ function writeSettings(path, settings) {
77
+ mkdirSync(dirname(path), { recursive: true });
78
+ writeFileSync(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
79
+ }
80
+ function updateModelInSettings(settingsPath, modelId) {
81
+ const settings = readSettings(settingsPath);
82
+ const provider = settings.provider ?? {};
83
+ provider.model = modelId;
84
+ settings.provider = provider;
85
+ writeSettings(settingsPath, settings);
86
+ }
87
+ function deleteSettings(path) {
88
+ if (existsSync(path)) {
89
+ unlinkSync(path);
90
+ return true;
91
+ }
92
+ return false;
93
+ }
94
+
95
+ // src/print-terminal.ts
96
+ import * as readline from "readline";
97
+ var PrintTerminal = class {
98
+ write(text) {
99
+ process.stdout.write(text);
100
+ }
101
+ writeLine(text) {
102
+ process.stdout.write(text + "\n");
103
+ }
104
+ writeMarkdown(md) {
105
+ process.stdout.write(md);
106
+ }
107
+ writeError(text) {
108
+ process.stderr.write(text + "\n");
109
+ }
110
+ prompt(question) {
111
+ return new Promise((resolve) => {
112
+ const rl = readline.createInterface({
113
+ input: process.stdin,
114
+ output: process.stdout,
115
+ terminal: false,
116
+ historySize: 0
117
+ });
118
+ rl.question(question, (answer) => {
119
+ rl.close();
120
+ resolve(answer);
121
+ });
122
+ });
123
+ }
124
+ async select(options, initialIndex = 0) {
125
+ for (let i = 0; i < options.length; i++) {
126
+ const marker = i === initialIndex ? ">" : " ";
127
+ process.stdout.write(` ${marker} ${i + 1}) ${options[i]}
128
+ `);
129
+ }
130
+ const answer = await this.prompt(
131
+ ` Choose [1-${options.length}] (default: ${options[initialIndex]}): `
132
+ );
133
+ const trimmed = answer.trim().toLowerCase();
134
+ if (trimmed === "") return initialIndex;
135
+ const num = parseInt(trimmed, 10);
136
+ if (!isNaN(num) && num >= 1 && num <= options.length) return num - 1;
137
+ return initialIndex;
138
+ }
139
+ spinner(_message) {
140
+ return { stop() {
141
+ }, update() {
142
+ } };
143
+ }
144
+ };
145
+
18
146
  // src/ui/render.tsx
19
147
  import { render } from "ink";
20
148
 
21
149
  // src/ui/App.tsx
22
- import { useState as useState4, useCallback as useCallback2, useRef as useRef2 } from "react";
23
- import { Box as Box6, Text as Text8, useApp, useInput as useInput4 } from "ink";
150
+ import { useState as useState5, useCallback as useCallback3, useRef as useRef3 } from "react";
151
+ import { Box as Box7, Text as Text9, useApp, useInput as useInput5 } from "ink";
152
+
153
+ // src/commands/slash-executor.ts
154
+ var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
155
+ var HELP_TEXT = [
156
+ "Available commands:",
157
+ " /help \u2014 Show this help",
158
+ " /clear \u2014 Clear conversation",
159
+ " /compact [instr] \u2014 Compact context (optional focus instructions)",
160
+ " /mode [m] \u2014 Show/change permission mode",
161
+ " /cost \u2014 Show session info",
162
+ " /reset \u2014 Delete settings and exit",
163
+ " /exit \u2014 Exit CLI"
164
+ ].join("\n");
165
+ function handleHelp(addMessage) {
166
+ addMessage({ role: "system", content: HELP_TEXT });
167
+ return { handled: true };
168
+ }
169
+ function handleClear(addMessage, clearMessages, session) {
170
+ clearMessages();
171
+ session.clearHistory();
172
+ addMessage({ role: "system", content: "Conversation cleared." });
173
+ return { handled: true };
174
+ }
175
+ async function handleCompact(args, session, addMessage) {
176
+ const instructions = args.trim() || void 0;
177
+ const before = session.getContextState().usedPercentage;
178
+ addMessage({ role: "system", content: "Compacting context..." });
179
+ await session.compact(instructions);
180
+ const after = session.getContextState().usedPercentage;
181
+ addMessage({
182
+ role: "system",
183
+ content: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`
184
+ });
185
+ return { handled: true };
186
+ }
187
+ function handleMode(arg, session, addMessage) {
188
+ if (!arg) {
189
+ addMessage({ role: "system", content: `Current mode: ${session.getPermissionMode()}` });
190
+ } else if (VALID_MODES2.includes(arg)) {
191
+ session.setPermissionMode(arg);
192
+ addMessage({ role: "system", content: `Permission mode set to: ${arg}` });
193
+ } else {
194
+ addMessage({ role: "system", content: `Invalid mode. Valid: ${VALID_MODES2.join(" | ")}` });
195
+ }
196
+ return { handled: true };
197
+ }
198
+ function handleModel(modelId, addMessage) {
199
+ if (!modelId) {
200
+ addMessage({ role: "system", content: "Select a model from the /model submenu." });
201
+ return { handled: true };
202
+ }
203
+ return { handled: true, pendingModelId: modelId };
204
+ }
205
+ function handleCost(session, addMessage) {
206
+ addMessage({
207
+ role: "system",
208
+ content: `Session: ${session.getSessionId()}
209
+ Messages: ${session.getMessageCount()}`
210
+ });
211
+ return { handled: true };
212
+ }
213
+ function handlePermissions(session, addMessage) {
214
+ const mode = session.getPermissionMode();
215
+ const sessionAllowed = session.getSessionAllowedTools();
216
+ const lines = [`Permission mode: ${mode}`];
217
+ if (sessionAllowed.length > 0) {
218
+ lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
219
+ } else {
220
+ lines.push("No session-approved tools.");
221
+ }
222
+ addMessage({ role: "system", content: lines.join("\n") });
223
+ return { handled: true };
224
+ }
225
+ function handleContext(session, addMessage) {
226
+ const ctx = session.getContextState();
227
+ addMessage({
228
+ role: "system",
229
+ content: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`
230
+ });
231
+ return { handled: true };
232
+ }
233
+ function handleReset(addMessage) {
234
+ const settingsPath = getUserSettingsPath();
235
+ if (deleteSettings(settingsPath)) {
236
+ addMessage({ role: "system", content: `Deleted ${settingsPath}. Exiting...` });
237
+ } else {
238
+ addMessage({ role: "system", content: "No user settings found." });
239
+ }
240
+ return { handled: true, exitRequested: true };
241
+ }
242
+ async function executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry) {
243
+ switch (cmd) {
244
+ case "help":
245
+ return handleHelp(addMessage);
246
+ case "clear":
247
+ return handleClear(addMessage, clearMessages, session);
248
+ case "compact":
249
+ return handleCompact(args, session, addMessage);
250
+ case "mode":
251
+ return handleMode(args.split(/\s+/)[0] || void 0, session, addMessage);
252
+ case "model":
253
+ return handleModel(args.split(/\s+/)[0] || void 0, addMessage);
254
+ case "cost":
255
+ return handleCost(session, addMessage);
256
+ case "permissions":
257
+ return handlePermissions(session, addMessage);
258
+ case "context":
259
+ return handleContext(session, addMessage);
260
+ case "reset":
261
+ return handleReset(addMessage);
262
+ case "exit":
263
+ return { handled: true, exitRequested: true };
264
+ default: {
265
+ const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
266
+ if (skillCmd) {
267
+ addMessage({ role: "system", content: `Invoking skill: ${cmd}` });
268
+ return { handled: false };
269
+ }
270
+ addMessage({ role: "system", content: `Unknown command "/${cmd}". Type /help for help.` });
271
+ return { handled: true };
272
+ }
273
+ }
274
+ }
275
+
276
+ // src/ui/App.tsx
24
277
  import { createSession, FileSessionLogger, projectPaths } from "@robota-sdk/agent-sdk";
278
+ import { getModelName } from "@robota-sdk/agent-core";
25
279
 
26
280
  // src/commands/command-registry.ts
27
281
  var CommandRegistry = class {
@@ -54,6 +308,21 @@ var CommandRegistry = class {
54
308
  };
55
309
 
56
310
  // src/commands/builtin-source.ts
311
+ import { CLAUDE_MODELS, formatTokenCount } from "@robota-sdk/agent-core";
312
+ function buildModelSubcommands() {
313
+ const seen = /* @__PURE__ */ new Set();
314
+ const commands = [];
315
+ for (const model of Object.values(CLAUDE_MODELS)) {
316
+ if (seen.has(model.name)) continue;
317
+ seen.add(model.name);
318
+ commands.push({
319
+ name: model.id,
320
+ description: `${model.name} (${formatTokenCount(model.contextWindow).toUpperCase()})`,
321
+ source: "builtin"
322
+ });
323
+ }
324
+ return commands;
325
+ }
57
326
  function createBuiltinCommands() {
58
327
  return [
59
328
  { name: "help", description: "Show available commands", source: "builtin" },
@@ -73,11 +342,7 @@ function createBuiltinCommands() {
73
342
  name: "model",
74
343
  description: "Select AI model",
75
344
  source: "builtin",
76
- subcommands: [
77
- { name: "claude-opus-4-6", description: "Opus 4.6 (highest quality)", source: "builtin" },
78
- { name: "claude-sonnet-4-6", description: "Sonnet 4.6 (balanced)", source: "builtin" },
79
- { name: "claude-haiku-4-5", description: "Haiku 4.5 (fastest)", source: "builtin" }
80
- ]
345
+ subcommands: buildModelSubcommands()
81
346
  },
82
347
  { name: "compact", description: "Compress context window", source: "builtin" },
83
348
  { name: "cost", description: "Show session info", source: "builtin" },
@@ -99,8 +364,8 @@ var BuiltinCommandSource = class {
99
364
  };
100
365
 
101
366
  // src/commands/skill-source.ts
102
- import { readdirSync, readFileSync, existsSync } from "fs";
103
- import { join } from "path";
367
+ import { readdirSync, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
368
+ import { join as join2 } from "path";
104
369
  import { homedir } from "os";
105
370
  function parseFrontmatter(content) {
106
371
  const lines = content.split("\n");
@@ -123,14 +388,14 @@ function parseFrontmatter(content) {
123
388
  return name ? { name, description } : null;
124
389
  }
125
390
  function scanSkillsDir(skillsDir) {
126
- if (!existsSync(skillsDir)) return [];
391
+ if (!existsSync2(skillsDir)) return [];
127
392
  const commands = [];
128
393
  const entries = readdirSync(skillsDir, { withFileTypes: true });
129
394
  for (const entry of entries) {
130
395
  if (!entry.isDirectory()) continue;
131
- const skillFile = join(skillsDir, entry.name, "SKILL.md");
132
- if (!existsSync(skillFile)) continue;
133
- const content = readFileSync(skillFile, "utf-8");
396
+ const skillFile = join2(skillsDir, entry.name, "SKILL.md");
397
+ if (!existsSync2(skillFile)) continue;
398
+ const content = readFileSync2(skillFile, "utf-8");
134
399
  const frontmatter = parseFrontmatter(content);
135
400
  commands.push({
136
401
  name: frontmatter?.name ?? entry.name,
@@ -150,8 +415,8 @@ var SkillCommandSource = class {
150
415
  }
151
416
  getCommands() {
152
417
  if (this.cachedCommands) return this.cachedCommands;
153
- const projectSkills = scanSkillsDir(join(this.cwd, ".agents", "skills"));
154
- const userSkills = scanSkillsDir(join(homedir(), ".claude", "skills"));
418
+ const projectSkills = scanSkillsDir(join2(this.cwd, ".agents", "skills"));
419
+ const userSkills = scanSkillsDir(join2(homedir(), ".claude", "skills"));
155
420
  const seen = new Set(projectSkills.map((cmd) => cmd.name));
156
421
  const merged = [...projectSkills];
157
422
  for (const cmd of userSkills) {
@@ -225,6 +490,7 @@ function MessageList({ messages }) {
225
490
 
226
491
  // src/ui/StatusBar.tsx
227
492
  import { Box as Box2, Text as Text2 } from "ink";
493
+ import { formatTokenCount as formatTokenCount2 } from "@robota-sdk/agent-core";
228
494
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
229
495
  var CONTEXT_YELLOW_THRESHOLD = 70;
230
496
  var CONTEXT_RED_THRESHOLD = 90;
@@ -264,10 +530,10 @@ function StatusBar({
264
530
  "Context: ",
265
531
  Math.round(contextPercentage),
266
532
  "% (",
267
- (contextUsedTokens / 1e3).toFixed(1),
268
- "k/",
269
- (contextMaxTokens / 1e3).toFixed(0),
270
- "k)"
533
+ formatTokenCount2(contextUsedTokens),
534
+ "/",
535
+ formatTokenCount2(contextMaxTokens),
536
+ ")"
271
537
  ] })
272
538
  ] }),
273
539
  /* @__PURE__ */ jsxs2(Text2, { children: [
@@ -414,19 +680,13 @@ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
414
680
  var MAX_VISIBLE = 8;
415
681
  function CommandRow(props) {
416
682
  const { cmd, isSelected, showSlash } = props;
417
- const prefix = showSlash ? "/" : "";
418
683
  const indicator = isSelected ? "\u25B8 " : " ";
419
684
  const nameColor = isSelected ? "cyan" : void 0;
420
685
  const dimmed = !isSelected;
421
- return /* @__PURE__ */ jsxs3(Box3, { children: [
422
- /* @__PURE__ */ jsxs3(Text5, { color: nameColor, dimColor: dimmed, children: [
423
- indicator,
424
- prefix,
425
- cmd.name
426
- ] }),
427
- /* @__PURE__ */ jsx5(Text5, { dimColor: dimmed, children: " " }),
428
- /* @__PURE__ */ jsx5(Text5, { color: nameColor, dimColor: dimmed, children: cmd.description })
429
- ] });
686
+ return /* @__PURE__ */ jsx5(Box3, { children: /* @__PURE__ */ jsxs3(Text5, { color: nameColor, dimColor: dimmed, children: [
687
+ indicator,
688
+ showSlash ? `/${cmd.name} ${cmd.description}` : cmd.description
689
+ ] }) });
430
690
  }
431
691
  function SlashAutocomplete({
432
692
  commands,
@@ -591,10 +851,53 @@ function InputArea({ onSubmit, isDisabled, registry }) {
591
851
  ] });
592
852
  }
593
853
 
594
- // src/ui/PermissionPrompt.tsx
595
- import React4 from "react";
854
+ // src/ui/ConfirmPrompt.tsx
855
+ import { useState as useState4, useCallback as useCallback2, useRef as useRef2 } from "react";
596
856
  import { Box as Box5, Text as Text7, useInput as useInput3 } from "ink";
597
857
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
858
+ function ConfirmPrompt({
859
+ message,
860
+ options = ["Yes", "No"],
861
+ onSelect
862
+ }) {
863
+ const [selected, setSelected] = useState4(0);
864
+ const resolvedRef = useRef2(false);
865
+ const doSelect = useCallback2(
866
+ (index) => {
867
+ if (resolvedRef.current) return;
868
+ resolvedRef.current = true;
869
+ onSelect(index);
870
+ },
871
+ [onSelect]
872
+ );
873
+ useInput3((input, key) => {
874
+ if (resolvedRef.current) return;
875
+ if (key.leftArrow || key.upArrow) {
876
+ setSelected((prev) => prev > 0 ? prev - 1 : prev);
877
+ } else if (key.rightArrow || key.downArrow) {
878
+ setSelected((prev) => prev < options.length - 1 ? prev + 1 : prev);
879
+ } else if (key.return) {
880
+ doSelect(selected);
881
+ } else if (input === "y" && options.length === 2) {
882
+ doSelect(0);
883
+ } else if (input === "n" && options.length === 2) {
884
+ doSelect(1);
885
+ }
886
+ });
887
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
888
+ /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: message }),
889
+ /* @__PURE__ */ jsx7(Box5, { marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ jsx7(Box5, { marginRight: 2, children: /* @__PURE__ */ jsxs5(Text7, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
890
+ i === selected ? "> " : " ",
891
+ opt
892
+ ] }) }, opt)) }),
893
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " arrow keys to select, Enter to confirm" })
894
+ ] });
895
+ }
896
+
897
+ // src/ui/PermissionPrompt.tsx
898
+ import React5 from "react";
899
+ import { Box as Box6, Text as Text8, useInput as useInput4 } from "ink";
900
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
598
901
  var OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
599
902
  function formatArgs(args) {
600
903
  const entries = Object.entries(args);
@@ -602,15 +905,15 @@ function formatArgs(args) {
602
905
  return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
603
906
  }
604
907
  function PermissionPrompt({ request }) {
605
- const [selected, setSelected] = React4.useState(0);
606
- const resolvedRef = React4.useRef(false);
607
- const prevRequestRef = React4.useRef(request);
908
+ const [selected, setSelected] = React5.useState(0);
909
+ const resolvedRef = React5.useRef(false);
910
+ const prevRequestRef = React5.useRef(request);
608
911
  if (prevRequestRef.current !== request) {
609
912
  prevRequestRef.current = request;
610
913
  resolvedRef.current = false;
611
914
  setSelected(0);
612
915
  }
613
- const doResolve = React4.useCallback(
916
+ const doResolve = React5.useCallback(
614
917
  (index) => {
615
918
  if (resolvedRef.current) return;
616
919
  resolvedRef.current = true;
@@ -620,7 +923,7 @@ function PermissionPrompt({ request }) {
620
923
  },
621
924
  [request]
622
925
  );
623
- useInput3((input, key) => {
926
+ useInput4((input, key) => {
624
927
  if (resolvedRef.current) return;
625
928
  if (key.upArrow || key.leftArrow) {
626
929
  setSelected((prev) => prev > 0 ? prev - 1 : prev);
@@ -636,27 +939,54 @@ function PermissionPrompt({ request }) {
636
939
  doResolve(2);
637
940
  }
638
941
  });
639
- return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
640
- /* @__PURE__ */ jsx7(Text7, { color: "yellow", bold: true, children: "[Permission Required]" }),
641
- /* @__PURE__ */ jsxs5(Text7, { children: [
942
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
943
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", bold: true, children: "[Permission Required]" }),
944
+ /* @__PURE__ */ jsxs6(Text8, { children: [
642
945
  "Tool:",
643
946
  " ",
644
- /* @__PURE__ */ jsx7(Text7, { color: "cyan", bold: true, children: request.toolName })
947
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: request.toolName })
645
948
  ] }),
646
- /* @__PURE__ */ jsxs5(Text7, { dimColor: true, children: [
949
+ /* @__PURE__ */ jsxs6(Text8, { dimColor: true, children: [
647
950
  " ",
648
951
  formatArgs(request.toolArgs)
649
952
  ] }),
650
- /* @__PURE__ */ jsx7(Box5, { marginTop: 1, children: OPTIONS.map((opt, i) => /* @__PURE__ */ jsx7(Box5, { marginRight: 2, children: /* @__PURE__ */ jsxs5(Text7, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
953
+ /* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: OPTIONS.map((opt, i) => /* @__PURE__ */ jsx8(Box6, { marginRight: 2, children: /* @__PURE__ */ jsxs6(Text8, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
651
954
  i === selected ? "> " : " ",
652
955
  opt
653
956
  ] }) }, opt)) }),
654
- /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " left/right to select, Enter to confirm" })
957
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " left/right to select, Enter to confirm" })
655
958
  ] });
656
959
  }
657
960
 
961
+ // src/utils/tool-call-extractor.ts
962
+ var TOOL_ARG_MAX_LENGTH = 80;
963
+ var TOOL_ARG_TRUNCATE_LENGTH = 77;
964
+ function extractToolCalls(history, startIndex) {
965
+ const lines = [];
966
+ for (let i = startIndex; i < history.length; i++) {
967
+ const msg = history[i];
968
+ if (msg.role === "assistant" && msg.toolCalls) {
969
+ for (const tc of msg.toolCalls) {
970
+ const value = parseFirstArgValue(tc.function.arguments);
971
+ const truncated = value.length > TOOL_ARG_MAX_LENGTH ? value.slice(0, TOOL_ARG_TRUNCATE_LENGTH) + "..." : value;
972
+ lines.push(`${tc.function.name}(${truncated})`);
973
+ }
974
+ }
975
+ }
976
+ return lines;
977
+ }
978
+ function parseFirstArgValue(argsJson) {
979
+ try {
980
+ const parsed = JSON.parse(argsJson);
981
+ const firstVal = Object.values(parsed)[0];
982
+ return typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
983
+ } catch {
984
+ return argsJson;
985
+ }
986
+ }
987
+
658
988
  // src/ui/App.tsx
659
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
989
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
660
990
  var msgIdCounter = 0;
661
991
  function nextId() {
662
992
  msgIdCounter += 1;
@@ -678,11 +1008,11 @@ var NOOP_TERMINAL = {
678
1008
  } })
679
1009
  };
680
1010
  function useSession(props) {
681
- const [permissionRequest, setPermissionRequest] = useState4(null);
682
- const [streamingText, setStreamingText] = useState4("");
683
- const permissionQueueRef = useRef2([]);
684
- const processingRef = useRef2(false);
685
- const processNextPermission = useCallback2(() => {
1011
+ const [permissionRequest, setPermissionRequest] = useState5(null);
1012
+ const [streamingText, setStreamingText] = useState5("");
1013
+ const permissionQueueRef = useRef3([]);
1014
+ const processingRef = useRef3(false);
1015
+ const processNextPermission = useCallback3(() => {
686
1016
  if (processingRef.current) return;
687
1017
  const next = permissionQueueRef.current[0];
688
1018
  if (!next) {
@@ -702,7 +1032,7 @@ function useSession(props) {
702
1032
  }
703
1033
  });
704
1034
  }, []);
705
- const sessionRef = useRef2(null);
1035
+ const sessionRef = useRef3(null);
706
1036
  if (sessionRef.current === null) {
707
1037
  const permissionHandler = (toolName, toolArgs) => {
708
1038
  return new Promise((resolve) => {
@@ -727,138 +1057,49 @@ function useSession(props) {
727
1057
  onTextDelta
728
1058
  });
729
1059
  }
730
- const clearStreamingText = useCallback2(() => setStreamingText(""), []);
1060
+ const clearStreamingText = useCallback3(() => setStreamingText(""), []);
731
1061
  return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText };
732
1062
  }
733
1063
  function useMessages() {
734
- const [messages, setMessages] = useState4([]);
735
- const addMessage = useCallback2((msg) => {
1064
+ const [messages, setMessages] = useState5([]);
1065
+ const addMessage = useCallback3((msg) => {
736
1066
  setMessages((prev) => [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }]);
737
1067
  }, []);
738
1068
  return { messages, setMessages, addMessage };
739
1069
  }
740
- var HELP_TEXT = [
741
- "Available commands:",
742
- " /help \u2014 Show this help",
743
- " /clear \u2014 Clear conversation",
744
- " /compact [instr] \u2014 Compact context (optional focus instructions)",
745
- " /mode [m] \u2014 Show/change permission mode",
746
- " /cost \u2014 Show session info",
747
- " /reset \u2014 Delete settings and exit",
748
- " /exit \u2014 Exit CLI"
749
- ].join("\n");
750
- function handleModeCommand(arg, session, addMessage) {
751
- const validModes = ["plan", "default", "acceptEdits", "bypassPermissions"];
752
- if (!arg) {
753
- addMessage({ role: "system", content: `Current mode: ${session.getPermissionMode()}` });
754
- } else if (validModes.includes(arg)) {
755
- session.setPermissionMode(arg);
756
- addMessage({ role: "system", content: `Permission mode set to: ${arg}` });
757
- } else {
758
- addMessage({ role: "system", content: `Invalid mode. Valid: ${validModes.join(" | ")}` });
759
- }
760
- return true;
761
- }
762
- async function executeSlashCommand(cmd, parts, session, addMessage, setMessages, exit, registry) {
763
- switch (cmd) {
764
- case "help":
765
- addMessage({ role: "system", content: HELP_TEXT });
766
- return true;
767
- case "clear":
768
- setMessages([]);
769
- session.clearHistory();
770
- addMessage({ role: "system", content: "Conversation cleared." });
771
- return true;
772
- case "compact": {
773
- const instructions = parts.slice(1).join(" ").trim() || void 0;
774
- const before = session.getContextState().usedPercentage;
775
- addMessage({ role: "system", content: "Compacting context..." });
776
- await session.compact(instructions);
777
- const after = session.getContextState().usedPercentage;
778
- addMessage({
779
- role: "system",
780
- content: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`
781
- });
782
- return true;
783
- }
784
- case "mode":
785
- return handleModeCommand(parts[1], session, addMessage);
786
- case "cost":
787
- addMessage({
788
- role: "system",
789
- content: `Session: ${session.getSessionId()}
790
- Messages: ${session.getMessageCount()}`
791
- });
792
- return true;
793
- case "permissions": {
794
- const mode = session.getPermissionMode();
795
- const sessionAllowed = session.getSessionAllowedTools();
796
- const lines = [`Permission mode: ${mode}`];
797
- if (sessionAllowed.length > 0) {
798
- lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
799
- } else {
800
- lines.push("No session-approved tools.");
801
- }
802
- addMessage({ role: "system", content: lines.join("\n") });
803
- return true;
804
- }
805
- case "context": {
806
- const ctx = session.getContextState();
807
- addMessage({
808
- role: "system",
809
- content: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`
810
- });
811
- return true;
812
- }
813
- case "reset": {
814
- const { existsSync: exists, unlinkSync: unlink } = await import("fs");
815
- const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
816
- const settingsPath = `${home}/.robota/settings.json`;
817
- if (exists(settingsPath)) {
818
- unlink(settingsPath);
819
- addMessage({ role: "system", content: `Deleted ${settingsPath}. Exiting...` });
820
- } else {
821
- addMessage({ role: "system", content: "No user settings found." });
822
- }
823
- setTimeout(() => exit(), 500);
824
- return true;
825
- }
826
- case "exit":
827
- exit();
828
- return true;
829
- default: {
830
- const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
831
- if (skillCmd) {
832
- addMessage({ role: "system", content: `Invoking skill: ${cmd}` });
833
- return false;
834
- }
835
- addMessage({ role: "system", content: `Unknown command "/${cmd}". Type /help for help.` });
836
- return true;
837
- }
838
- }
839
- }
840
- function useSlashCommands(session, addMessage, setMessages, exit, registry) {
841
- return useCallback2(
1070
+ var EXIT_DELAY_MS = 500;
1071
+ function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId) {
1072
+ return useCallback3(
842
1073
  async (input) => {
843
1074
  const parts = input.slice(1).split(/\s+/);
844
1075
  const cmd = parts[0]?.toLowerCase() ?? "";
845
- return executeSlashCommand(cmd, parts, session, addMessage, setMessages, exit, registry);
1076
+ const args = parts.slice(1).join(" ");
1077
+ const clearMessages = () => setMessages([]);
1078
+ const result = await executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry);
1079
+ if (result.pendingModelId) {
1080
+ pendingModelChangeRef.current = result.pendingModelId;
1081
+ setPendingModelId(result.pendingModelId);
1082
+ }
1083
+ if (result.exitRequested) {
1084
+ setTimeout(() => exit(), EXIT_DELAY_MS);
1085
+ }
1086
+ return result.handled;
846
1087
  },
847
- [session, addMessage, setMessages, exit, registry]
1088
+ [session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId]
848
1089
  );
849
1090
  }
850
1091
  function StreamingIndicator({ text }) {
851
1092
  if (text) {
852
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
853
- /* @__PURE__ */ jsxs6(Text8, { color: "cyan", bold: true, children: [
1093
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1094
+ /* @__PURE__ */ jsxs7(Text9, { color: "cyan", bold: true, children: [
854
1095
  "Robota:",
855
1096
  " "
856
1097
  ] }),
857
- /* @__PURE__ */ jsx8(Text8, { children: " " }),
858
- /* @__PURE__ */ jsx8(Box6, { marginLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { wrap: "wrap", children: renderMarkdown(text) }) })
1098
+ /* @__PURE__ */ jsx9(Text9, { children: " " }),
1099
+ /* @__PURE__ */ jsx9(Box7, { marginLeft: 2, children: /* @__PURE__ */ jsx9(Text9, { wrap: "wrap", children: renderMarkdown(text) }) })
859
1100
  ] });
860
1101
  }
861
- return /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "Thinking..." });
1102
+ return /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "Thinking..." });
862
1103
  }
863
1104
  async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextPercentage) {
864
1105
  setIsThinking(true);
@@ -868,24 +1109,10 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
868
1109
  const response = await session.run(prompt);
869
1110
  clearStreamingText();
870
1111
  const history = session.getHistory();
871
- const toolLines = [];
872
- for (let i = historyBefore; i < history.length; i++) {
873
- const msg = history[i];
874
- if (msg.role === "assistant" && msg.toolCalls) {
875
- for (const tc of msg.toolCalls) {
876
- let value = "";
877
- try {
878
- const parsed = JSON.parse(tc.function.arguments);
879
- const firstVal = Object.values(parsed)[0];
880
- value = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
881
- } catch {
882
- value = tc.function.arguments;
883
- }
884
- const truncated = value.length > 80 ? value.slice(0, 77) + "..." : value;
885
- toolLines.push(`${tc.function.name}(${truncated})`);
886
- }
887
- }
888
- }
1112
+ const toolLines = extractToolCalls(
1113
+ history,
1114
+ historyBefore
1115
+ );
889
1116
  if (toolLines.length > 0) {
890
1117
  addMessage({ role: "tool", content: toolLines.join("\n"), toolName: `${toolLines.length} tools` });
891
1118
  }
@@ -920,7 +1147,7 @@ Execute the "${cmd}" skill: ${userInstruction}`;
920
1147
  return `Use the "${cmd}" skill: ${userInstruction}`;
921
1148
  }
922
1149
  function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextPercentage, registry) {
923
- return useCallback2(
1150
+ return useCallback3(
924
1151
  async (input) => {
925
1152
  if (input.startsWith("/")) {
926
1153
  const handled = await handleSlashCommand(input);
@@ -961,7 +1188,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
961
1188
  );
962
1189
  }
963
1190
  function useCommandRegistry(cwd) {
964
- const registryRef = useRef2(null);
1191
+ const registryRef = useRef3(null);
965
1192
  if (registryRef.current === null) {
966
1193
  const registry = new CommandRegistry();
967
1194
  registry.addSource(new BuiltinCommandSource());
@@ -974,10 +1201,12 @@ function App(props) {
974
1201
  const { exit } = useApp();
975
1202
  const { session, permissionRequest, streamingText, clearStreamingText } = useSession(props);
976
1203
  const { messages, setMessages, addMessage } = useMessages();
977
- const [isThinking, setIsThinking] = useState4(false);
978
- const [contextPercentage, setContextPercentage] = useState4(0);
1204
+ const [isThinking, setIsThinking] = useState5(false);
1205
+ const [contextPercentage, setContextPercentage] = useState5(0);
979
1206
  const registry = useCommandRegistry(props.cwd ?? process.cwd());
980
- const handleSlashCommand = useSlashCommands(session, addMessage, setMessages, exit, registry);
1207
+ const pendingModelChangeRef = useRef3(null);
1208
+ const [pendingModelId, setPendingModelId] = useState5(null);
1209
+ const handleSlashCommand = useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId);
981
1210
  const handleSubmit = useSubmitHandler(
982
1211
  session,
983
1212
  addMessage,
@@ -987,37 +1216,59 @@ function App(props) {
987
1216
  setContextPercentage,
988
1217
  registry
989
1218
  );
990
- useInput4(
1219
+ useInput5(
991
1220
  (_input, key) => {
992
1221
  if (key.ctrl && _input === "c") exit();
993
1222
  if (key.escape && isThinking) session.abort();
994
1223
  },
995
1224
  { isActive: !permissionRequest }
996
1225
  );
997
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
998
- /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
999
- /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: true, children: `
1226
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1227
+ /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
1228
+ /* @__PURE__ */ jsx9(Text9, { color: "cyan", bold: true, children: `
1000
1229
  ____ ___ ____ ___ _____ _
1001
1230
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
1002
1231
  | |_) | | | | _ \\| | | || | / _ \\
1003
1232
  | _ <| |_| | |_) | |_| || |/ ___ \\
1004
1233
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
1005
1234
  ` }),
1006
- /* @__PURE__ */ jsxs6(Text8, { dimColor: true, children: [
1235
+ /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
1007
1236
  " v",
1008
1237
  props.version ?? "0.0.0"
1009
1238
  ] })
1010
1239
  ] }),
1011
- /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
1012
- /* @__PURE__ */ jsx8(MessageList, { messages }),
1013
- isThinking && /* @__PURE__ */ jsx8(Box6, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx8(StreamingIndicator, { text: streamingText }) })
1240
+ /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
1241
+ /* @__PURE__ */ jsx9(MessageList, { messages }),
1242
+ isThinking && /* @__PURE__ */ jsx9(Box7, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx9(StreamingIndicator, { text: streamingText }) })
1014
1243
  ] }),
1015
- permissionRequest && /* @__PURE__ */ jsx8(PermissionPrompt, { request: permissionRequest }),
1016
- /* @__PURE__ */ jsx8(
1244
+ permissionRequest && /* @__PURE__ */ jsx9(PermissionPrompt, { request: permissionRequest }),
1245
+ pendingModelId && /* @__PURE__ */ jsx9(
1246
+ ConfirmPrompt,
1247
+ {
1248
+ message: `Change model to ${getModelName(pendingModelId)}? This will restart the session.`,
1249
+ onSelect: (index) => {
1250
+ setPendingModelId(null);
1251
+ pendingModelChangeRef.current = null;
1252
+ if (index === 0) {
1253
+ try {
1254
+ const settingsPath = getUserSettingsPath();
1255
+ updateModelInSettings(settingsPath, pendingModelId);
1256
+ addMessage({ role: "system", content: `Model changed to ${getModelName(pendingModelId)}. Restarting...` });
1257
+ setTimeout(() => exit(), 500);
1258
+ } catch (err) {
1259
+ addMessage({ role: "system", content: `Failed: ${err instanceof Error ? err.message : String(err)}` });
1260
+ }
1261
+ } else {
1262
+ addMessage({ role: "system", content: "Model change cancelled." });
1263
+ }
1264
+ }
1265
+ }
1266
+ ),
1267
+ /* @__PURE__ */ jsx9(
1017
1268
  StatusBar,
1018
1269
  {
1019
1270
  permissionMode: session.getPermissionMode(),
1020
- modelName: props.config.provider.model,
1271
+ modelName: getModelName(props.config.provider.model),
1021
1272
  sessionId: session.getSessionId(),
1022
1273
  messageCount: messages.length,
1023
1274
  isThinking,
@@ -1026,7 +1277,7 @@ function App(props) {
1026
1277
  contextMaxTokens: session.getContextState().maxTokens
1027
1278
  }
1028
1279
  ),
1029
- /* @__PURE__ */ jsx8(
1280
+ /* @__PURE__ */ jsx9(
1030
1281
  InputArea,
1031
1282
  {
1032
1283
  onSubmit: handleSubmit,
@@ -1034,12 +1285,12 @@ function App(props) {
1034
1285
  registry
1035
1286
  }
1036
1287
  ),
1037
- /* @__PURE__ */ jsx8(Text8, { children: " " })
1288
+ /* @__PURE__ */ jsx9(Text9, { children: " " })
1038
1289
  ] });
1039
1290
  }
1040
1291
 
1041
1292
  // src/ui/render.tsx
1042
- import { jsx as jsx9 } from "react/jsx-runtime";
1293
+ import { jsx as jsx10 } from "react/jsx-runtime";
1043
1294
  function renderApp(options) {
1044
1295
  process.on("unhandledRejection", (reason) => {
1045
1296
  process.stderr.write(`
@@ -1050,7 +1301,7 @@ function renderApp(options) {
1050
1301
  `);
1051
1302
  }
1052
1303
  });
1053
- const instance = render(/* @__PURE__ */ jsx9(App, { ...options }), {
1304
+ const instance = render(/* @__PURE__ */ jsx10(App, { ...options }), {
1054
1305
  exitOnCtrlC: true
1055
1306
  });
1056
1307
  instance.waitUntilExit().catch((err) => {
@@ -1063,15 +1314,14 @@ function renderApp(options) {
1063
1314
  }
1064
1315
 
1065
1316
  // src/cli.ts
1066
- var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
1067
1317
  function readVersion() {
1068
1318
  try {
1069
1319
  const thisFile = fileURLToPath(import.meta.url);
1070
- const dir = dirname(thisFile);
1071
- const candidates = [join2(dir, "..", "..", "package.json"), join2(dir, "..", "package.json")];
1320
+ const dir = dirname2(thisFile);
1321
+ const candidates = [join3(dir, "..", "..", "package.json"), join3(dir, "..", "package.json")];
1072
1322
  for (const pkgPath of candidates) {
1073
1323
  try {
1074
- const raw = readFileSync2(pkgPath, "utf-8");
1324
+ const raw = readFileSync3(pkgPath, "utf-8");
1075
1325
  const pkg = JSON.parse(raw);
1076
1326
  if (pkg.version !== void 0 && pkg.name !== void 0) {
1077
1327
  return pkg.version;
@@ -1084,108 +1334,11 @@ function readVersion() {
1084
1334
  return "0.0.0";
1085
1335
  }
1086
1336
  }
1087
- function parsePermissionMode(raw) {
1088
- if (raw === void 0) return void 0;
1089
- if (!VALID_MODES.includes(raw)) {
1090
- process.stderr.write(`Invalid --permission-mode "${raw}". Valid: ${VALID_MODES.join(" | ")}
1091
- `);
1092
- process.exit(1);
1093
- }
1094
- return raw;
1095
- }
1096
- function parseMaxTurns(raw) {
1097
- if (raw === void 0) return void 0;
1098
- const n = parseInt(raw, 10);
1099
- if (isNaN(n) || n <= 0) {
1100
- process.stderr.write(`Invalid --max-turns "${raw}". Must be a positive integer.
1101
- `);
1102
- process.exit(1);
1103
- }
1104
- return n;
1105
- }
1106
- function parseCliArgs() {
1107
- const { values, positionals } = parseArgs({
1108
- allowPositionals: true,
1109
- options: {
1110
- p: { type: "boolean", short: "p", default: false },
1111
- c: { type: "boolean", short: "c", default: false },
1112
- r: { type: "string", short: "r" },
1113
- model: { type: "string" },
1114
- "permission-mode": { type: "string" },
1115
- "max-turns": { type: "string" },
1116
- version: { type: "boolean", default: false },
1117
- reset: { type: "boolean", default: false }
1118
- }
1119
- });
1120
- return {
1121
- positional: positionals,
1122
- printMode: values["p"] ?? false,
1123
- continueMode: values["c"] ?? false,
1124
- resumeId: values["r"],
1125
- model: values["model"],
1126
- permissionMode: parsePermissionMode(values["permission-mode"]),
1127
- maxTurns: parseMaxTurns(values["max-turns"]),
1128
- version: values["version"] ?? false,
1129
- reset: values["reset"] ?? false
1130
- };
1131
- }
1132
- var PrintTerminal = class {
1133
- write(text) {
1134
- process.stdout.write(text);
1135
- }
1136
- writeLine(text) {
1137
- process.stdout.write(text + "\n");
1138
- }
1139
- writeMarkdown(md) {
1140
- process.stdout.write(md);
1141
- }
1142
- writeError(text) {
1143
- process.stderr.write(text + "\n");
1144
- }
1145
- prompt(question) {
1146
- return new Promise((resolve) => {
1147
- const rl = readline.createInterface({
1148
- input: process.stdin,
1149
- output: process.stdout,
1150
- terminal: false,
1151
- historySize: 0
1152
- });
1153
- rl.question(question, (answer) => {
1154
- rl.close();
1155
- resolve(answer);
1156
- });
1157
- });
1158
- }
1159
- async select(options, initialIndex = 0) {
1160
- for (let i = 0; i < options.length; i++) {
1161
- const marker = i === initialIndex ? ">" : " ";
1162
- process.stdout.write(` ${marker} ${i + 1}) ${options[i]}
1163
- `);
1164
- }
1165
- const answer = await this.prompt(
1166
- ` Choose [1-${options.length}] (default: ${options[initialIndex]}): `
1167
- );
1168
- const trimmed = answer.trim().toLowerCase();
1169
- if (trimmed === "") return initialIndex;
1170
- const num = parseInt(trimmed, 10);
1171
- if (!isNaN(num) && num >= 1 && num <= options.length) return num - 1;
1172
- return initialIndex;
1173
- }
1174
- spinner(_message) {
1175
- return { stop() {
1176
- }, update() {
1177
- } };
1178
- }
1179
- };
1180
- function getUserSettingsPath() {
1181
- const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
1182
- return join2(home, ".robota", "settings.json");
1183
- }
1184
1337
  async function ensureConfig(cwd) {
1185
1338
  const userPath = getUserSettingsPath();
1186
- const projectPath = join2(cwd, ".robota", "settings.json");
1187
- const localPath = join2(cwd, ".robota", "settings.local.json");
1188
- if (existsSync2(userPath) || existsSync2(projectPath) || existsSync2(localPath)) {
1339
+ const projectPath = join3(cwd, ".robota", "settings.json");
1340
+ const localPath = join3(cwd, ".robota", "settings.local.json");
1341
+ if (existsSync3(userPath) || existsSync3(projectPath) || existsSync3(localPath)) {
1189
1342
  return;
1190
1343
  }
1191
1344
  process.stdout.write("\n");
@@ -1229,8 +1382,8 @@ async function ensureConfig(cwd) {
1229
1382
  process.stderr.write("\n No API key provided. Exiting.\n");
1230
1383
  process.exit(1);
1231
1384
  }
1232
- const settingsDir = dirname(userPath);
1233
- mkdirSync(settingsDir, { recursive: true });
1385
+ const settingsDir = dirname2(userPath);
1386
+ mkdirSync2(settingsDir, { recursive: true });
1234
1387
  const settings = {
1235
1388
  provider: {
1236
1389
  name: "anthropic",
@@ -1238,7 +1391,7 @@ async function ensureConfig(cwd) {
1238
1391
  apiKey
1239
1392
  }
1240
1393
  };
1241
- writeFileSync(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
1394
+ writeFileSync2(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
1242
1395
  process.stdout.write(`
1243
1396
  Config saved to ${userPath}
1244
1397
 
@@ -1246,8 +1399,7 @@ async function ensureConfig(cwd) {
1246
1399
  }
1247
1400
  function resetConfig() {
1248
1401
  const userPath = getUserSettingsPath();
1249
- if (existsSync2(userPath)) {
1250
- unlinkSync(userPath);
1402
+ if (deleteSettings(userPath)) {
1251
1403
  process.stdout.write(`Deleted ${userPath}
1252
1404
  `);
1253
1405
  } else {