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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node/bin.cjs CHANGED
@@ -161,11 +161,11 @@ var PrintTerminal = class {
161
161
  };
162
162
 
163
163
  // src/ui/render.tsx
164
- var import_ink10 = require("ink");
164
+ var import_ink11 = require("ink");
165
165
 
166
166
  // src/ui/App.tsx
167
167
  var import_react6 = require("react");
168
- var import_ink9 = require("ink");
168
+ var import_ink10 = require("ink");
169
169
 
170
170
  // src/commands/slash-executor.ts
171
171
  var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
@@ -1002,8 +1002,39 @@ function parseFirstArgValue(argsJson) {
1002
1002
  }
1003
1003
  }
1004
1004
 
1005
- // src/ui/App.tsx
1005
+ // src/ui/StreamingIndicator.tsx
1006
+ var import_ink9 = require("ink");
1006
1007
  var import_jsx_runtime9 = require("react/jsx-runtime");
1008
+ function StreamingIndicator({ text, activeTools }) {
1009
+ const hasTools = activeTools.length > 0;
1010
+ const hasText = text.length > 0;
1011
+ if (!hasTools && !hasText) {
1012
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "yellow", children: "Thinking..." });
1013
+ }
1014
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", children: [
1015
+ hasTools && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", marginBottom: 1, children: [
1016
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "gray", bold: true, children: "Tools:" }),
1017
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " }),
1018
+ activeTools.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { color: t.isRunning ? "yellow" : "green", children: [
1019
+ " ",
1020
+ t.isRunning ? "\u27F3" : "\u2713",
1021
+ " ",
1022
+ t.toolName,
1023
+ "(",
1024
+ t.firstArg,
1025
+ ")"
1026
+ ] }, `${t.toolName}-${i}`))
1027
+ ] }),
1028
+ hasText && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", marginBottom: 1, children: [
1029
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "cyan", bold: true, children: "Robota:" }),
1030
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " }),
1031
+ /* @__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) }) })
1032
+ ] })
1033
+ ] });
1034
+ }
1035
+
1036
+ // src/ui/App.tsx
1037
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1007
1038
  var msgIdCounter = 0;
1008
1039
  function nextId() {
1009
1040
  msgIdCounter += 1;
@@ -1027,6 +1058,7 @@ var NOOP_TERMINAL = {
1027
1058
  function useSession(props) {
1028
1059
  const [permissionRequest, setPermissionRequest] = (0, import_react6.useState)(null);
1029
1060
  const [streamingText, setStreamingText] = (0, import_react6.useState)("");
1061
+ const [activeTools, setActiveTools] = (0, import_react6.useState)([]);
1030
1062
  const permissionQueueRef = (0, import_react6.useRef)([]);
1031
1063
  const processingRef = (0, import_react6.useRef)(false);
1032
1064
  const processNextPermission = (0, import_react6.useCallback)(() => {
@@ -1060,6 +1092,25 @@ function useSession(props) {
1060
1092
  const onTextDelta = (delta) => {
1061
1093
  setStreamingText((prev) => prev + delta);
1062
1094
  };
1095
+ const TOOL_ARG_DISPLAY_MAX = 80;
1096
+ const TOOL_ARG_TRUNCATE_AT = 77;
1097
+ const onToolExecution = (event) => {
1098
+ if (event.type === "start") {
1099
+ let firstArg = "";
1100
+ if (event.toolArgs) {
1101
+ const firstVal = Object.values(event.toolArgs)[0];
1102
+ const raw = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal ?? "");
1103
+ firstArg = raw.length > TOOL_ARG_DISPLAY_MAX ? raw.slice(0, TOOL_ARG_TRUNCATE_AT) + "..." : raw;
1104
+ }
1105
+ setActiveTools((prev) => [...prev, { toolName: event.toolName, firstArg, isRunning: true }]);
1106
+ } else {
1107
+ setActiveTools(
1108
+ (prev) => prev.map(
1109
+ (t) => t.toolName === event.toolName && t.isRunning ? { ...t, isRunning: false } : t
1110
+ )
1111
+ );
1112
+ }
1113
+ };
1063
1114
  const paths = (0, import_agent_sdk.projectPaths)(props.cwd ?? process.cwd());
1064
1115
  sessionRef.current = (0, import_agent_sdk.createSession)({
1065
1116
  config: props.config,
@@ -1071,11 +1122,15 @@ function useSession(props) {
1071
1122
  permissionMode: props.permissionMode,
1072
1123
  maxTurns: props.maxTurns,
1073
1124
  permissionHandler,
1074
- onTextDelta
1125
+ onTextDelta,
1126
+ onToolExecution
1075
1127
  });
1076
1128
  }
1077
- const clearStreamingText = (0, import_react6.useCallback)(() => setStreamingText(""), []);
1078
- return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText };
1129
+ const clearStreamingText = (0, import_react6.useCallback)(() => {
1130
+ setStreamingText("");
1131
+ setActiveTools([]);
1132
+ }, []);
1133
+ return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText, activeTools };
1079
1134
  }
1080
1135
  function useMessages() {
1081
1136
  const [messages, setMessages] = (0, import_react6.useState)([]);
@@ -1105,20 +1160,11 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
1105
1160
  [session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId]
1106
1161
  );
1107
1162
  }
1108
- function StreamingIndicator({ text }) {
1109
- if (text) {
1110
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", children: [
1111
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { color: "cyan", bold: true, children: [
1112
- "Robota:",
1113
- " "
1114
- ] }),
1115
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " }),
1116
- /* @__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) }) })
1117
- ] });
1118
- }
1119
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "yellow", children: "Thinking..." });
1163
+ function syncContextState(session, setter) {
1164
+ const ctx = session.getContextState();
1165
+ setter({ percentage: ctx.usedPercentage, usedTokens: ctx.usedTokens, maxTokens: ctx.maxTokens });
1120
1166
  }
1121
- async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextPercentage) {
1167
+ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextState) {
1122
1168
  setIsThinking(true);
1123
1169
  clearStreamingText();
1124
1170
  const historyBefore = session.getHistory().length;
@@ -1134,7 +1180,7 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
1134
1180
  addMessage({ role: "tool", content: toolLines.join("\n"), toolName: `${toolLines.length} tools` });
1135
1181
  }
1136
1182
  addMessage({ role: "assistant", content: response || "(empty response)" });
1137
- setContextPercentage(session.getContextState().usedPercentage);
1183
+ syncContextState(session, setContextState);
1138
1184
  } catch (err) {
1139
1185
  clearStreamingText();
1140
1186
  if (err instanceof DOMException && err.name === "AbortError") {
@@ -1163,13 +1209,13 @@ Execute the "${cmd}" skill: ${userInstruction}`;
1163
1209
  }
1164
1210
  return `Use the "${cmd}" skill: ${userInstruction}`;
1165
1211
  }
1166
- function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextPercentage, registry) {
1212
+ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry) {
1167
1213
  return (0, import_react6.useCallback)(
1168
1214
  async (input) => {
1169
1215
  if (input.startsWith("/")) {
1170
1216
  const handled = await handleSlashCommand(input);
1171
1217
  if (handled) {
1172
- setContextPercentage(session.getContextState().usedPercentage);
1218
+ syncContextState(session, setContextState);
1173
1219
  return;
1174
1220
  }
1175
1221
  const prompt = buildSkillPrompt(input, registry);
@@ -1180,7 +1226,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
1180
1226
  addMessage,
1181
1227
  clearStreamingText,
1182
1228
  setIsThinking,
1183
- setContextPercentage
1229
+ setContextState
1184
1230
  );
1185
1231
  }
1186
1232
  addMessage({ role: "user", content: input });
@@ -1190,7 +1236,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
1190
1236
  addMessage,
1191
1237
  clearStreamingText,
1192
1238
  setIsThinking,
1193
- setContextPercentage
1239
+ setContextState
1194
1240
  );
1195
1241
  },
1196
1242
  [
@@ -1199,7 +1245,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
1199
1245
  handleSlashCommand,
1200
1246
  clearStreamingText,
1201
1247
  setIsThinking,
1202
- setContextPercentage,
1248
+ setContextState,
1203
1249
  registry
1204
1250
  ]
1205
1251
  );
@@ -1215,11 +1261,11 @@ function useCommandRegistry(cwd) {
1215
1261
  return registryRef.current;
1216
1262
  }
1217
1263
  function App(props) {
1218
- const { exit } = (0, import_ink9.useApp)();
1219
- const { session, permissionRequest, streamingText, clearStreamingText } = useSession(props);
1264
+ const { exit } = (0, import_ink10.useApp)();
1265
+ const { session, permissionRequest, streamingText, clearStreamingText, activeTools } = useSession(props);
1220
1266
  const { messages, setMessages, addMessage } = useMessages();
1221
1267
  const [isThinking, setIsThinking] = (0, import_react6.useState)(false);
1222
- const [contextPercentage, setContextPercentage] = (0, import_react6.useState)(0);
1268
+ const [contextState, setContextState] = (0, import_react6.useState)({ percentage: 0, usedTokens: 0, maxTokens: 0 });
1223
1269
  const registry = useCommandRegistry(props.cwd ?? process.cwd());
1224
1270
  const pendingModelChangeRef = (0, import_react6.useRef)(null);
1225
1271
  const [pendingModelId, setPendingModelId] = (0, import_react6.useState)(null);
@@ -1230,36 +1276,36 @@ function App(props) {
1230
1276
  handleSlashCommand,
1231
1277
  clearStreamingText,
1232
1278
  setIsThinking,
1233
- setContextPercentage,
1279
+ setContextState,
1234
1280
  registry
1235
1281
  );
1236
- (0, import_ink9.useInput)(
1282
+ (0, import_ink10.useInput)(
1237
1283
  (_input, key) => {
1238
1284
  if (key.ctrl && _input === "c") exit();
1239
1285
  if (key.escape && isThinking) session.abort();
1240
1286
  },
1241
1287
  { isActive: !permissionRequest }
1242
1288
  );
1243
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", children: [
1244
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
1245
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "cyan", bold: true, children: `
1289
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
1290
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
1291
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "cyan", bold: true, children: `
1246
1292
  ____ ___ ____ ___ _____ _
1247
1293
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
1248
1294
  | |_) | | | | _ \\| | | || | / _ \\
1249
1295
  | _ <| |_| | |_) | |_| || |/ ___ \\
1250
1296
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
1251
1297
  ` }),
1252
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { dimColor: true, children: [
1298
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Text, { dimColor: true, children: [
1253
1299
  " v",
1254
1300
  props.version ?? "0.0.0"
1255
1301
  ] })
1256
1302
  ] }),
1257
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
1258
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MessageList, { messages }),
1259
- isThinking && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StreamingIndicator, { text: streamingText }) })
1303
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
1304
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageList, { messages }),
1305
+ isThinking && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
1260
1306
  ] }),
1261
- permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PermissionPrompt, { request: permissionRequest }),
1262
- pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1307
+ permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PermissionPrompt, { request: permissionRequest }),
1308
+ pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1263
1309
  ConfirmPrompt,
1264
1310
  {
1265
1311
  message: `Change model to ${(0, import_agent_core3.getModelName)(pendingModelId)}? This will restart the session.`,
@@ -1281,7 +1327,7 @@ function App(props) {
1281
1327
  }
1282
1328
  }
1283
1329
  ),
1284
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1330
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1285
1331
  StatusBar,
1286
1332
  {
1287
1333
  permissionMode: session.getPermissionMode(),
@@ -1289,12 +1335,12 @@ function App(props) {
1289
1335
  sessionId: session.getSessionId(),
1290
1336
  messageCount: messages.length,
1291
1337
  isThinking,
1292
- contextPercentage,
1293
- contextUsedTokens: session.getContextState().usedTokens,
1294
- contextMaxTokens: session.getContextState().maxTokens
1338
+ contextPercentage: contextState.percentage,
1339
+ contextUsedTokens: contextState.usedTokens,
1340
+ contextMaxTokens: contextState.maxTokens
1295
1341
  }
1296
1342
  ),
1297
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1343
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1298
1344
  InputArea,
1299
1345
  {
1300
1346
  onSubmit: handleSubmit,
@@ -1302,12 +1348,12 @@ function App(props) {
1302
1348
  registry
1303
1349
  }
1304
1350
  ),
1305
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " })
1351
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { children: " " })
1306
1352
  ] });
1307
1353
  }
1308
1354
 
1309
1355
  // src/ui/render.tsx
1310
- var import_jsx_runtime10 = require("react/jsx-runtime");
1356
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1311
1357
  function renderApp(options) {
1312
1358
  process.on("unhandledRejection", (reason) => {
1313
1359
  process.stderr.write(`
@@ -1318,7 +1364,7 @@ function renderApp(options) {
1318
1364
  `);
1319
1365
  }
1320
1366
  });
1321
- const instance = (0, import_ink10.render)(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(App, { ...options }), {
1367
+ const instance = (0, import_ink11.render)(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(App, { ...options }), {
1322
1368
  exitOnCtrlC: true
1323
1369
  });
1324
1370
  instance.waitUntilExit().catch((err) => {
@@ -1332,6 +1378,18 @@ function renderApp(options) {
1332
1378
 
1333
1379
  // src/cli.ts
1334
1380
  var import_meta = {};
1381
+ function hasValidSettingsFile(filePath) {
1382
+ if (!(0, import_node_fs3.existsSync)(filePath)) return false;
1383
+ try {
1384
+ const raw = (0, import_node_fs3.readFileSync)(filePath, "utf8").trim();
1385
+ if (raw.length === 0) return false;
1386
+ const parsed = JSON.parse(raw);
1387
+ const provider = parsed.provider;
1388
+ return !!provider?.apiKey;
1389
+ } catch {
1390
+ return false;
1391
+ }
1392
+ }
1335
1393
  function readVersion() {
1336
1394
  try {
1337
1395
  const thisFile = (0, import_node_url.fileURLToPath)(import_meta.url);
@@ -1356,7 +1414,7 @@ async function ensureConfig(cwd) {
1356
1414
  const userPath = getUserSettingsPath();
1357
1415
  const projectPath = (0, import_node_path3.join)(cwd, ".robota", "settings.json");
1358
1416
  const localPath = (0, import_node_path3.join)(cwd, ".robota", "settings.local.json");
1359
- if ((0, import_node_fs3.existsSync)(userPath) || (0, import_node_fs3.existsSync)(projectPath) || (0, import_node_fs3.existsSync)(localPath)) {
1417
+ if (hasValidSettingsFile(userPath) || hasValidSettingsFile(projectPath) || hasValidSettingsFile(localPath)) {
1360
1418
  return;
1361
1419
  }
1362
1420
  process.stdout.write("\n");
package/dist/node/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startCli
4
- } from "./chunk-4WB3L3RU.js";
4
+ } from "./chunk-M723M4M4.js";
5
5
 
6
6
  // src/bin.ts
7
7
  process.on("uncaughtException", (err) => {
@@ -148,7 +148,7 @@ import { render } from "ink";
148
148
 
149
149
  // src/ui/App.tsx
150
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";
151
+ import { Box as Box8, Text as Text10, useApp, useInput as useInput5 } from "ink";
152
152
 
153
153
  // src/commands/slash-executor.ts
154
154
  var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
@@ -985,8 +985,39 @@ function parseFirstArgValue(argsJson) {
985
985
  }
986
986
  }
987
987
 
988
- // src/ui/App.tsx
988
+ // src/ui/StreamingIndicator.tsx
989
+ import { Box as Box7, Text as Text9 } from "ink";
989
990
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
991
+ function StreamingIndicator({ text, activeTools }) {
992
+ const hasTools = activeTools.length > 0;
993
+ const hasText = text.length > 0;
994
+ if (!hasTools && !hasText) {
995
+ return /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "Thinking..." });
996
+ }
997
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
998
+ hasTools && /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginBottom: 1, children: [
999
+ /* @__PURE__ */ jsx9(Text9, { color: "gray", bold: true, children: "Tools:" }),
1000
+ /* @__PURE__ */ jsx9(Text9, { children: " " }),
1001
+ activeTools.map((t, i) => /* @__PURE__ */ jsxs7(Text9, { color: t.isRunning ? "yellow" : "green", children: [
1002
+ " ",
1003
+ t.isRunning ? "\u27F3" : "\u2713",
1004
+ " ",
1005
+ t.toolName,
1006
+ "(",
1007
+ t.firstArg,
1008
+ ")"
1009
+ ] }, `${t.toolName}-${i}`))
1010
+ ] }),
1011
+ hasText && /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginBottom: 1, children: [
1012
+ /* @__PURE__ */ jsx9(Text9, { color: "cyan", bold: true, children: "Robota:" }),
1013
+ /* @__PURE__ */ jsx9(Text9, { children: " " }),
1014
+ /* @__PURE__ */ jsx9(Box7, { marginLeft: 2, children: /* @__PURE__ */ jsx9(Text9, { wrap: "wrap", children: renderMarkdown(text) }) })
1015
+ ] })
1016
+ ] });
1017
+ }
1018
+
1019
+ // src/ui/App.tsx
1020
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
990
1021
  var msgIdCounter = 0;
991
1022
  function nextId() {
992
1023
  msgIdCounter += 1;
@@ -1010,6 +1041,7 @@ var NOOP_TERMINAL = {
1010
1041
  function useSession(props) {
1011
1042
  const [permissionRequest, setPermissionRequest] = useState5(null);
1012
1043
  const [streamingText, setStreamingText] = useState5("");
1044
+ const [activeTools, setActiveTools] = useState5([]);
1013
1045
  const permissionQueueRef = useRef3([]);
1014
1046
  const processingRef = useRef3(false);
1015
1047
  const processNextPermission = useCallback3(() => {
@@ -1043,6 +1075,25 @@ function useSession(props) {
1043
1075
  const onTextDelta = (delta) => {
1044
1076
  setStreamingText((prev) => prev + delta);
1045
1077
  };
1078
+ const TOOL_ARG_DISPLAY_MAX = 80;
1079
+ const TOOL_ARG_TRUNCATE_AT = 77;
1080
+ const onToolExecution = (event) => {
1081
+ if (event.type === "start") {
1082
+ let firstArg = "";
1083
+ if (event.toolArgs) {
1084
+ const firstVal = Object.values(event.toolArgs)[0];
1085
+ const raw = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal ?? "");
1086
+ firstArg = raw.length > TOOL_ARG_DISPLAY_MAX ? raw.slice(0, TOOL_ARG_TRUNCATE_AT) + "..." : raw;
1087
+ }
1088
+ setActiveTools((prev) => [...prev, { toolName: event.toolName, firstArg, isRunning: true }]);
1089
+ } else {
1090
+ setActiveTools(
1091
+ (prev) => prev.map(
1092
+ (t) => t.toolName === event.toolName && t.isRunning ? { ...t, isRunning: false } : t
1093
+ )
1094
+ );
1095
+ }
1096
+ };
1046
1097
  const paths = projectPaths(props.cwd ?? process.cwd());
1047
1098
  sessionRef.current = createSession({
1048
1099
  config: props.config,
@@ -1054,11 +1105,15 @@ function useSession(props) {
1054
1105
  permissionMode: props.permissionMode,
1055
1106
  maxTurns: props.maxTurns,
1056
1107
  permissionHandler,
1057
- onTextDelta
1108
+ onTextDelta,
1109
+ onToolExecution
1058
1110
  });
1059
1111
  }
1060
- const clearStreamingText = useCallback3(() => setStreamingText(""), []);
1061
- return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText };
1112
+ const clearStreamingText = useCallback3(() => {
1113
+ setStreamingText("");
1114
+ setActiveTools([]);
1115
+ }, []);
1116
+ return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText, activeTools };
1062
1117
  }
1063
1118
  function useMessages() {
1064
1119
  const [messages, setMessages] = useState5([]);
@@ -1088,20 +1143,11 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
1088
1143
  [session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId]
1089
1144
  );
1090
1145
  }
1091
- function StreamingIndicator({ text }) {
1092
- if (text) {
1093
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1094
- /* @__PURE__ */ jsxs7(Text9, { color: "cyan", bold: true, children: [
1095
- "Robota:",
1096
- " "
1097
- ] }),
1098
- /* @__PURE__ */ jsx9(Text9, { children: " " }),
1099
- /* @__PURE__ */ jsx9(Box7, { marginLeft: 2, children: /* @__PURE__ */ jsx9(Text9, { wrap: "wrap", children: renderMarkdown(text) }) })
1100
- ] });
1101
- }
1102
- return /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "Thinking..." });
1146
+ function syncContextState(session, setter) {
1147
+ const ctx = session.getContextState();
1148
+ setter({ percentage: ctx.usedPercentage, usedTokens: ctx.usedTokens, maxTokens: ctx.maxTokens });
1103
1149
  }
1104
- async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextPercentage) {
1150
+ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextState) {
1105
1151
  setIsThinking(true);
1106
1152
  clearStreamingText();
1107
1153
  const historyBefore = session.getHistory().length;
@@ -1117,7 +1163,7 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
1117
1163
  addMessage({ role: "tool", content: toolLines.join("\n"), toolName: `${toolLines.length} tools` });
1118
1164
  }
1119
1165
  addMessage({ role: "assistant", content: response || "(empty response)" });
1120
- setContextPercentage(session.getContextState().usedPercentage);
1166
+ syncContextState(session, setContextState);
1121
1167
  } catch (err) {
1122
1168
  clearStreamingText();
1123
1169
  if (err instanceof DOMException && err.name === "AbortError") {
@@ -1146,13 +1192,13 @@ Execute the "${cmd}" skill: ${userInstruction}`;
1146
1192
  }
1147
1193
  return `Use the "${cmd}" skill: ${userInstruction}`;
1148
1194
  }
1149
- function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextPercentage, registry) {
1195
+ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry) {
1150
1196
  return useCallback3(
1151
1197
  async (input) => {
1152
1198
  if (input.startsWith("/")) {
1153
1199
  const handled = await handleSlashCommand(input);
1154
1200
  if (handled) {
1155
- setContextPercentage(session.getContextState().usedPercentage);
1201
+ syncContextState(session, setContextState);
1156
1202
  return;
1157
1203
  }
1158
1204
  const prompt = buildSkillPrompt(input, registry);
@@ -1163,7 +1209,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
1163
1209
  addMessage,
1164
1210
  clearStreamingText,
1165
1211
  setIsThinking,
1166
- setContextPercentage
1212
+ setContextState
1167
1213
  );
1168
1214
  }
1169
1215
  addMessage({ role: "user", content: input });
@@ -1173,7 +1219,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
1173
1219
  addMessage,
1174
1220
  clearStreamingText,
1175
1221
  setIsThinking,
1176
- setContextPercentage
1222
+ setContextState
1177
1223
  );
1178
1224
  },
1179
1225
  [
@@ -1182,7 +1228,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
1182
1228
  handleSlashCommand,
1183
1229
  clearStreamingText,
1184
1230
  setIsThinking,
1185
- setContextPercentage,
1231
+ setContextState,
1186
1232
  registry
1187
1233
  ]
1188
1234
  );
@@ -1199,10 +1245,10 @@ function useCommandRegistry(cwd) {
1199
1245
  }
1200
1246
  function App(props) {
1201
1247
  const { exit } = useApp();
1202
- const { session, permissionRequest, streamingText, clearStreamingText } = useSession(props);
1248
+ const { session, permissionRequest, streamingText, clearStreamingText, activeTools } = useSession(props);
1203
1249
  const { messages, setMessages, addMessage } = useMessages();
1204
1250
  const [isThinking, setIsThinking] = useState5(false);
1205
- const [contextPercentage, setContextPercentage] = useState5(0);
1251
+ const [contextState, setContextState] = useState5({ percentage: 0, usedTokens: 0, maxTokens: 0 });
1206
1252
  const registry = useCommandRegistry(props.cwd ?? process.cwd());
1207
1253
  const pendingModelChangeRef = useRef3(null);
1208
1254
  const [pendingModelId, setPendingModelId] = useState5(null);
@@ -1213,7 +1259,7 @@ function App(props) {
1213
1259
  handleSlashCommand,
1214
1260
  clearStreamingText,
1215
1261
  setIsThinking,
1216
- setContextPercentage,
1262
+ setContextState,
1217
1263
  registry
1218
1264
  );
1219
1265
  useInput5(
@@ -1223,26 +1269,26 @@ function App(props) {
1223
1269
  },
1224
1270
  { isActive: !permissionRequest }
1225
1271
  );
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: `
1272
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
1273
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
1274
+ /* @__PURE__ */ jsx10(Text10, { color: "cyan", bold: true, children: `
1229
1275
  ____ ___ ____ ___ _____ _
1230
1276
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
1231
1277
  | |_) | | | | _ \\| | | || | / _ \\
1232
1278
  | _ <| |_| | |_) | |_| || |/ ___ \\
1233
1279
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
1234
1280
  ` }),
1235
- /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
1281
+ /* @__PURE__ */ jsxs8(Text10, { dimColor: true, children: [
1236
1282
  " v",
1237
1283
  props.version ?? "0.0.0"
1238
1284
  ] })
1239
1285
  ] }),
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 }) })
1286
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
1287
+ /* @__PURE__ */ jsx10(MessageList, { messages }),
1288
+ isThinking && /* @__PURE__ */ jsx10(Box8, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx10(StreamingIndicator, { text: streamingText, activeTools }) })
1243
1289
  ] }),
1244
- permissionRequest && /* @__PURE__ */ jsx9(PermissionPrompt, { request: permissionRequest }),
1245
- pendingModelId && /* @__PURE__ */ jsx9(
1290
+ permissionRequest && /* @__PURE__ */ jsx10(PermissionPrompt, { request: permissionRequest }),
1291
+ pendingModelId && /* @__PURE__ */ jsx10(
1246
1292
  ConfirmPrompt,
1247
1293
  {
1248
1294
  message: `Change model to ${getModelName(pendingModelId)}? This will restart the session.`,
@@ -1264,7 +1310,7 @@ function App(props) {
1264
1310
  }
1265
1311
  }
1266
1312
  ),
1267
- /* @__PURE__ */ jsx9(
1313
+ /* @__PURE__ */ jsx10(
1268
1314
  StatusBar,
1269
1315
  {
1270
1316
  permissionMode: session.getPermissionMode(),
@@ -1272,12 +1318,12 @@ function App(props) {
1272
1318
  sessionId: session.getSessionId(),
1273
1319
  messageCount: messages.length,
1274
1320
  isThinking,
1275
- contextPercentage,
1276
- contextUsedTokens: session.getContextState().usedTokens,
1277
- contextMaxTokens: session.getContextState().maxTokens
1321
+ contextPercentage: contextState.percentage,
1322
+ contextUsedTokens: contextState.usedTokens,
1323
+ contextMaxTokens: contextState.maxTokens
1278
1324
  }
1279
1325
  ),
1280
- /* @__PURE__ */ jsx9(
1326
+ /* @__PURE__ */ jsx10(
1281
1327
  InputArea,
1282
1328
  {
1283
1329
  onSubmit: handleSubmit,
@@ -1285,12 +1331,12 @@ function App(props) {
1285
1331
  registry
1286
1332
  }
1287
1333
  ),
1288
- /* @__PURE__ */ jsx9(Text9, { children: " " })
1334
+ /* @__PURE__ */ jsx10(Text10, { children: " " })
1289
1335
  ] });
1290
1336
  }
1291
1337
 
1292
1338
  // src/ui/render.tsx
1293
- import { jsx as jsx10 } from "react/jsx-runtime";
1339
+ import { jsx as jsx11 } from "react/jsx-runtime";
1294
1340
  function renderApp(options) {
1295
1341
  process.on("unhandledRejection", (reason) => {
1296
1342
  process.stderr.write(`
@@ -1301,7 +1347,7 @@ function renderApp(options) {
1301
1347
  `);
1302
1348
  }
1303
1349
  });
1304
- const instance = render(/* @__PURE__ */ jsx10(App, { ...options }), {
1350
+ const instance = render(/* @__PURE__ */ jsx11(App, { ...options }), {
1305
1351
  exitOnCtrlC: true
1306
1352
  });
1307
1353
  instance.waitUntilExit().catch((err) => {
@@ -1314,6 +1360,18 @@ function renderApp(options) {
1314
1360
  }
1315
1361
 
1316
1362
  // src/cli.ts
1363
+ function hasValidSettingsFile(filePath) {
1364
+ if (!existsSync3(filePath)) return false;
1365
+ try {
1366
+ const raw = readFileSync3(filePath, "utf8").trim();
1367
+ if (raw.length === 0) return false;
1368
+ const parsed = JSON.parse(raw);
1369
+ const provider = parsed.provider;
1370
+ return !!provider?.apiKey;
1371
+ } catch {
1372
+ return false;
1373
+ }
1374
+ }
1317
1375
  function readVersion() {
1318
1376
  try {
1319
1377
  const thisFile = fileURLToPath(import.meta.url);
@@ -1338,7 +1396,7 @@ async function ensureConfig(cwd) {
1338
1396
  const userPath = getUserSettingsPath();
1339
1397
  const projectPath = join3(cwd, ".robota", "settings.json");
1340
1398
  const localPath = join3(cwd, ".robota", "settings.local.json");
1341
- if (existsSync3(userPath) || existsSync3(projectPath) || existsSync3(localPath)) {
1399
+ if (hasValidSettingsFile(userPath) || hasValidSettingsFile(projectPath) || hasValidSettingsFile(localPath)) {
1342
1400
  return;
1343
1401
  }
1344
1402
  process.stdout.write("\n");
@@ -177,11 +177,11 @@ var PrintTerminal = class {
177
177
  };
178
178
 
179
179
  // src/ui/render.tsx
180
- var import_ink10 = require("ink");
180
+ var import_ink11 = require("ink");
181
181
 
182
182
  // src/ui/App.tsx
183
183
  var import_react6 = require("react");
184
- var import_ink9 = require("ink");
184
+ var import_ink10 = require("ink");
185
185
 
186
186
  // src/commands/slash-executor.ts
187
187
  var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
@@ -1018,8 +1018,39 @@ function parseFirstArgValue(argsJson) {
1018
1018
  }
1019
1019
  }
1020
1020
 
1021
- // src/ui/App.tsx
1021
+ // src/ui/StreamingIndicator.tsx
1022
+ var import_ink9 = require("ink");
1022
1023
  var import_jsx_runtime9 = require("react/jsx-runtime");
1024
+ function StreamingIndicator({ text, activeTools }) {
1025
+ const hasTools = activeTools.length > 0;
1026
+ const hasText = text.length > 0;
1027
+ if (!hasTools && !hasText) {
1028
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "yellow", children: "Thinking..." });
1029
+ }
1030
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", children: [
1031
+ hasTools && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", marginBottom: 1, children: [
1032
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "gray", bold: true, children: "Tools:" }),
1033
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " }),
1034
+ activeTools.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { color: t.isRunning ? "yellow" : "green", children: [
1035
+ " ",
1036
+ t.isRunning ? "\u27F3" : "\u2713",
1037
+ " ",
1038
+ t.toolName,
1039
+ "(",
1040
+ t.firstArg,
1041
+ ")"
1042
+ ] }, `${t.toolName}-${i}`))
1043
+ ] }),
1044
+ hasText && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", marginBottom: 1, children: [
1045
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "cyan", bold: true, children: "Robota:" }),
1046
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " }),
1047
+ /* @__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) }) })
1048
+ ] })
1049
+ ] });
1050
+ }
1051
+
1052
+ // src/ui/App.tsx
1053
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1023
1054
  var msgIdCounter = 0;
1024
1055
  function nextId() {
1025
1056
  msgIdCounter += 1;
@@ -1043,6 +1074,7 @@ var NOOP_TERMINAL = {
1043
1074
  function useSession(props) {
1044
1075
  const [permissionRequest, setPermissionRequest] = (0, import_react6.useState)(null);
1045
1076
  const [streamingText, setStreamingText] = (0, import_react6.useState)("");
1077
+ const [activeTools, setActiveTools] = (0, import_react6.useState)([]);
1046
1078
  const permissionQueueRef = (0, import_react6.useRef)([]);
1047
1079
  const processingRef = (0, import_react6.useRef)(false);
1048
1080
  const processNextPermission = (0, import_react6.useCallback)(() => {
@@ -1076,6 +1108,25 @@ function useSession(props) {
1076
1108
  const onTextDelta = (delta) => {
1077
1109
  setStreamingText((prev) => prev + delta);
1078
1110
  };
1111
+ const TOOL_ARG_DISPLAY_MAX = 80;
1112
+ const TOOL_ARG_TRUNCATE_AT = 77;
1113
+ const onToolExecution = (event) => {
1114
+ if (event.type === "start") {
1115
+ let firstArg = "";
1116
+ if (event.toolArgs) {
1117
+ const firstVal = Object.values(event.toolArgs)[0];
1118
+ const raw = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal ?? "");
1119
+ firstArg = raw.length > TOOL_ARG_DISPLAY_MAX ? raw.slice(0, TOOL_ARG_TRUNCATE_AT) + "..." : raw;
1120
+ }
1121
+ setActiveTools((prev) => [...prev, { toolName: event.toolName, firstArg, isRunning: true }]);
1122
+ } else {
1123
+ setActiveTools(
1124
+ (prev) => prev.map(
1125
+ (t) => t.toolName === event.toolName && t.isRunning ? { ...t, isRunning: false } : t
1126
+ )
1127
+ );
1128
+ }
1129
+ };
1079
1130
  const paths = (0, import_agent_sdk.projectPaths)(props.cwd ?? process.cwd());
1080
1131
  sessionRef.current = (0, import_agent_sdk.createSession)({
1081
1132
  config: props.config,
@@ -1087,11 +1138,15 @@ function useSession(props) {
1087
1138
  permissionMode: props.permissionMode,
1088
1139
  maxTurns: props.maxTurns,
1089
1140
  permissionHandler,
1090
- onTextDelta
1141
+ onTextDelta,
1142
+ onToolExecution
1091
1143
  });
1092
1144
  }
1093
- const clearStreamingText = (0, import_react6.useCallback)(() => setStreamingText(""), []);
1094
- return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText };
1145
+ const clearStreamingText = (0, import_react6.useCallback)(() => {
1146
+ setStreamingText("");
1147
+ setActiveTools([]);
1148
+ }, []);
1149
+ return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText, activeTools };
1095
1150
  }
1096
1151
  function useMessages() {
1097
1152
  const [messages, setMessages] = (0, import_react6.useState)([]);
@@ -1121,20 +1176,11 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
1121
1176
  [session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId]
1122
1177
  );
1123
1178
  }
1124
- function StreamingIndicator({ text }) {
1125
- if (text) {
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: [
1128
- "Robota:",
1129
- " "
1130
- ] }),
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) }) })
1133
- ] });
1134
- }
1135
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "yellow", children: "Thinking..." });
1179
+ function syncContextState(session, setter) {
1180
+ const ctx = session.getContextState();
1181
+ setter({ percentage: ctx.usedPercentage, usedTokens: ctx.usedTokens, maxTokens: ctx.maxTokens });
1136
1182
  }
1137
- async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextPercentage) {
1183
+ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextState) {
1138
1184
  setIsThinking(true);
1139
1185
  clearStreamingText();
1140
1186
  const historyBefore = session.getHistory().length;
@@ -1150,7 +1196,7 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
1150
1196
  addMessage({ role: "tool", content: toolLines.join("\n"), toolName: `${toolLines.length} tools` });
1151
1197
  }
1152
1198
  addMessage({ role: "assistant", content: response || "(empty response)" });
1153
- setContextPercentage(session.getContextState().usedPercentage);
1199
+ syncContextState(session, setContextState);
1154
1200
  } catch (err) {
1155
1201
  clearStreamingText();
1156
1202
  if (err instanceof DOMException && err.name === "AbortError") {
@@ -1179,13 +1225,13 @@ Execute the "${cmd}" skill: ${userInstruction}`;
1179
1225
  }
1180
1226
  return `Use the "${cmd}" skill: ${userInstruction}`;
1181
1227
  }
1182
- function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextPercentage, registry) {
1228
+ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry) {
1183
1229
  return (0, import_react6.useCallback)(
1184
1230
  async (input) => {
1185
1231
  if (input.startsWith("/")) {
1186
1232
  const handled = await handleSlashCommand(input);
1187
1233
  if (handled) {
1188
- setContextPercentage(session.getContextState().usedPercentage);
1234
+ syncContextState(session, setContextState);
1189
1235
  return;
1190
1236
  }
1191
1237
  const prompt = buildSkillPrompt(input, registry);
@@ -1196,7 +1242,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
1196
1242
  addMessage,
1197
1243
  clearStreamingText,
1198
1244
  setIsThinking,
1199
- setContextPercentage
1245
+ setContextState
1200
1246
  );
1201
1247
  }
1202
1248
  addMessage({ role: "user", content: input });
@@ -1206,7 +1252,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
1206
1252
  addMessage,
1207
1253
  clearStreamingText,
1208
1254
  setIsThinking,
1209
- setContextPercentage
1255
+ setContextState
1210
1256
  );
1211
1257
  },
1212
1258
  [
@@ -1215,7 +1261,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
1215
1261
  handleSlashCommand,
1216
1262
  clearStreamingText,
1217
1263
  setIsThinking,
1218
- setContextPercentage,
1264
+ setContextState,
1219
1265
  registry
1220
1266
  ]
1221
1267
  );
@@ -1231,11 +1277,11 @@ function useCommandRegistry(cwd) {
1231
1277
  return registryRef.current;
1232
1278
  }
1233
1279
  function App(props) {
1234
- const { exit } = (0, import_ink9.useApp)();
1235
- const { session, permissionRequest, streamingText, clearStreamingText } = useSession(props);
1280
+ const { exit } = (0, import_ink10.useApp)();
1281
+ const { session, permissionRequest, streamingText, clearStreamingText, activeTools } = useSession(props);
1236
1282
  const { messages, setMessages, addMessage } = useMessages();
1237
1283
  const [isThinking, setIsThinking] = (0, import_react6.useState)(false);
1238
- const [contextPercentage, setContextPercentage] = (0, import_react6.useState)(0);
1284
+ const [contextState, setContextState] = (0, import_react6.useState)({ percentage: 0, usedTokens: 0, maxTokens: 0 });
1239
1285
  const registry = useCommandRegistry(props.cwd ?? process.cwd());
1240
1286
  const pendingModelChangeRef = (0, import_react6.useRef)(null);
1241
1287
  const [pendingModelId, setPendingModelId] = (0, import_react6.useState)(null);
@@ -1246,36 +1292,36 @@ function App(props) {
1246
1292
  handleSlashCommand,
1247
1293
  clearStreamingText,
1248
1294
  setIsThinking,
1249
- setContextPercentage,
1295
+ setContextState,
1250
1296
  registry
1251
1297
  );
1252
- (0, import_ink9.useInput)(
1298
+ (0, import_ink10.useInput)(
1253
1299
  (_input, key) => {
1254
1300
  if (key.ctrl && _input === "c") exit();
1255
1301
  if (key.escape && isThinking) session.abort();
1256
1302
  },
1257
1303
  { isActive: !permissionRequest }
1258
1304
  );
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: `
1305
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
1306
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
1307
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "cyan", bold: true, children: `
1262
1308
  ____ ___ ____ ___ _____ _
1263
1309
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
1264
1310
  | |_) | | | | _ \\| | | || | / _ \\
1265
1311
  | _ <| |_| | |_) | |_| || |/ ___ \\
1266
1312
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
1267
1313
  ` }),
1268
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { dimColor: true, children: [
1314
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Text, { dimColor: true, children: [
1269
1315
  " v",
1270
1316
  props.version ?? "0.0.0"
1271
1317
  ] })
1272
1318
  ] }),
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 }) })
1319
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
1320
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageList, { messages }),
1321
+ isThinking && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
1276
1322
  ] }),
1277
- permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PermissionPrompt, { request: permissionRequest }),
1278
- pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1323
+ permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PermissionPrompt, { request: permissionRequest }),
1324
+ pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1279
1325
  ConfirmPrompt,
1280
1326
  {
1281
1327
  message: `Change model to ${(0, import_agent_core3.getModelName)(pendingModelId)}? This will restart the session.`,
@@ -1297,7 +1343,7 @@ function App(props) {
1297
1343
  }
1298
1344
  }
1299
1345
  ),
1300
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1346
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1301
1347
  StatusBar,
1302
1348
  {
1303
1349
  permissionMode: session.getPermissionMode(),
@@ -1305,12 +1351,12 @@ function App(props) {
1305
1351
  sessionId: session.getSessionId(),
1306
1352
  messageCount: messages.length,
1307
1353
  isThinking,
1308
- contextPercentage,
1309
- contextUsedTokens: session.getContextState().usedTokens,
1310
- contextMaxTokens: session.getContextState().maxTokens
1354
+ contextPercentage: contextState.percentage,
1355
+ contextUsedTokens: contextState.usedTokens,
1356
+ contextMaxTokens: contextState.maxTokens
1311
1357
  }
1312
1358
  ),
1313
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1359
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1314
1360
  InputArea,
1315
1361
  {
1316
1362
  onSubmit: handleSubmit,
@@ -1318,12 +1364,12 @@ function App(props) {
1318
1364
  registry
1319
1365
  }
1320
1366
  ),
1321
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " })
1367
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { children: " " })
1322
1368
  ] });
1323
1369
  }
1324
1370
 
1325
1371
  // src/ui/render.tsx
1326
- var import_jsx_runtime10 = require("react/jsx-runtime");
1372
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1327
1373
  function renderApp(options) {
1328
1374
  process.on("unhandledRejection", (reason) => {
1329
1375
  process.stderr.write(`
@@ -1334,7 +1380,7 @@ function renderApp(options) {
1334
1380
  `);
1335
1381
  }
1336
1382
  });
1337
- const instance = (0, import_ink10.render)(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(App, { ...options }), {
1383
+ const instance = (0, import_ink11.render)(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(App, { ...options }), {
1338
1384
  exitOnCtrlC: true
1339
1385
  });
1340
1386
  instance.waitUntilExit().catch((err) => {
@@ -1348,6 +1394,18 @@ function renderApp(options) {
1348
1394
 
1349
1395
  // src/cli.ts
1350
1396
  var import_meta = {};
1397
+ function hasValidSettingsFile(filePath) {
1398
+ if (!(0, import_node_fs3.existsSync)(filePath)) return false;
1399
+ try {
1400
+ const raw = (0, import_node_fs3.readFileSync)(filePath, "utf8").trim();
1401
+ if (raw.length === 0) return false;
1402
+ const parsed = JSON.parse(raw);
1403
+ const provider = parsed.provider;
1404
+ return !!provider?.apiKey;
1405
+ } catch {
1406
+ return false;
1407
+ }
1408
+ }
1351
1409
  function readVersion() {
1352
1410
  try {
1353
1411
  const thisFile = (0, import_node_url.fileURLToPath)(import_meta.url);
@@ -1372,7 +1430,7 @@ async function ensureConfig(cwd) {
1372
1430
  const userPath = getUserSettingsPath();
1373
1431
  const projectPath = (0, import_node_path3.join)(cwd, ".robota", "settings.json");
1374
1432
  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)) {
1433
+ if (hasValidSettingsFile(userPath) || hasValidSettingsFile(projectPath) || hasValidSettingsFile(localPath)) {
1376
1434
  return;
1377
1435
  }
1378
1436
  process.stdout.write("\n");
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  startCli
3
- } from "./chunk-4WB3L3RU.js";
3
+ } from "./chunk-M723M4M4.js";
4
4
 
5
5
  // src/index.ts
6
6
  import { Session, SessionStore, query, TRUST_TO_MODE } from "@robota-sdk/agent-sdk";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robota-sdk/agent-cli",
3
- "version": "3.0.0-beta.17",
3
+ "version": "3.0.0-beta.18",
4
4
  "description": "AI coding assistant CLI built on Robota SDK",
5
5
  "type": "module",
6
6
  "bin": {
@@ -35,8 +35,8 @@
35
35
  "marked-terminal": "^7.3.0",
36
36
  "react": "19.2.4",
37
37
  "string-width": "^8.2.0",
38
- "@robota-sdk/agent-core": "3.0.0-beta.17",
39
- "@robota-sdk/agent-sdk": "3.0.0-beta.17"
38
+ "@robota-sdk/agent-core": "3.0.0-beta.18",
39
+ "@robota-sdk/agent-sdk": "3.0.0-beta.18"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/marked": "^6.0.0",