@robota-sdk/agent-cli 3.0.0-beta.1 → 3.0.0-beta.10

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
@@ -119,6 +119,7 @@ function createBuiltinCommands() {
119
119
  { name: "cost", description: "Show session info", source: "builtin" },
120
120
  { name: "context", description: "Context window info", source: "builtin" },
121
121
  { name: "permissions", description: "Permission rules", source: "builtin" },
122
+ { name: "reset", description: "Delete settings and exit", source: "builtin" },
122
123
  { name: "exit", description: "Exit CLI", source: "builtin" }
123
124
  ];
124
125
  }
@@ -170,7 +171,8 @@ function scanSkillsDir(skillsDir) {
170
171
  commands.push({
171
172
  name: frontmatter?.name ?? entry.name,
172
173
  description: frontmatter?.description ?? `Skill: ${entry.name}`,
173
- source: "skill"
174
+ source: "skill",
175
+ skillContent: content
174
176
  });
175
177
  }
176
178
  return commands;
@@ -346,50 +348,60 @@ function CjkTextInput({
346
348
  }
347
349
  (0, import_ink3.useInput)(
348
350
  (input, key) => {
349
- if (key.upArrow || key.downArrow || key.ctrl && input === "c" || key.tab || key.shift && key.tab) {
350
- return;
351
- }
352
- if (key.return) {
353
- onSubmit?.(valueRef.current);
354
- return;
355
- }
356
- if (key.leftArrow) {
357
- if (cursorRef.current > 0) {
358
- cursorRef.current -= 1;
359
- forceRender((n) => n + 1);
351
+ try {
352
+ if (key.upArrow || key.downArrow || key.ctrl && input === "c" || key.tab || key.shift && key.tab) {
353
+ return;
360
354
  }
361
- return;
362
- }
363
- if (key.rightArrow) {
364
- if (cursorRef.current < valueRef.current.length) {
365
- cursorRef.current += 1;
366
- forceRender((n) => n + 1);
355
+ if (key.return) {
356
+ onSubmit?.(valueRef.current);
357
+ return;
367
358
  }
368
- return;
369
- }
370
- if (key.backspace || key.delete) {
371
- if (cursorRef.current > 0) {
372
- const v2 = valueRef.current;
373
- const next2 = v2.slice(0, cursorRef.current - 1) + v2.slice(cursorRef.current);
374
- cursorRef.current -= 1;
375
- valueRef.current = next2;
376
- onChange(next2);
359
+ if (key.leftArrow) {
360
+ if (cursorRef.current > 0) {
361
+ cursorRef.current -= 1;
362
+ forceRender((n) => n + 1);
363
+ }
364
+ return;
377
365
  }
378
- return;
366
+ if (key.rightArrow) {
367
+ if (cursorRef.current < valueRef.current.length) {
368
+ cursorRef.current += 1;
369
+ forceRender((n) => n + 1);
370
+ }
371
+ return;
372
+ }
373
+ if (key.backspace || key.delete) {
374
+ if (cursorRef.current > 0) {
375
+ const v2 = valueRef.current;
376
+ const next2 = v2.slice(0, cursorRef.current - 1) + v2.slice(cursorRef.current);
377
+ cursorRef.current -= 1;
378
+ valueRef.current = next2;
379
+ onChange(next2);
380
+ }
381
+ return;
382
+ }
383
+ if (!input || input.length === 0) return;
384
+ const printable = input.replace(/[\x00-\x1f\x7f]/g, "");
385
+ if (printable.length === 0) return;
386
+ const v = valueRef.current;
387
+ const c = cursorRef.current;
388
+ const next = v.slice(0, c) + printable + v.slice(c);
389
+ cursorRef.current = c + printable.length;
390
+ valueRef.current = next;
391
+ onChange(next);
392
+ } catch {
379
393
  }
380
- const v = valueRef.current;
381
- const c = cursorRef.current;
382
- const next = v.slice(0, c) + input + v.slice(c);
383
- cursorRef.current = c + input.length;
384
- valueRef.current = next;
385
- onChange(next);
386
394
  },
387
395
  { isActive: focus }
388
396
  );
389
397
  if (showCursor && focus) {
390
- const textBeforeCursor = [...valueRef.current].slice(0, cursorRef.current).join("");
391
- const cursorX = 4 + (0, import_string_width.default)(textBeforeCursor);
392
- setCursorPosition({ x: cursorX, y: 0 });
398
+ try {
399
+ const textBeforeCursor = [...valueRef.current].slice(0, cursorRef.current).join("");
400
+ const cursorX = 4 + (0, import_string_width.default)(textBeforeCursor);
401
+ setCursorPosition({ x: cursorX, y: 0 });
402
+ } catch {
403
+ setCursorPosition({ x: 4, y: 0 });
404
+ }
393
405
  }
394
406
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { children: renderWithCursor(valueRef.current, cursorRef.current, placeholder, showCursor && focus) });
395
407
  }
@@ -625,7 +637,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
625
637
  var import_react4 = __toESM(require("react"), 1);
626
638
  var import_ink7 = require("ink");
627
639
  var import_jsx_runtime7 = require("react/jsx-runtime");
628
- var OPTIONS = ["Allow", "Deny"];
640
+ var OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
629
641
  function formatArgs2(args) {
630
642
  const entries = Object.entries(args);
631
643
  if (entries.length === 0) return "(no arguments)";
@@ -641,10 +653,12 @@ function PermissionPrompt({ request }) {
641
653
  setSelected(0);
642
654
  }
643
655
  const doResolve = import_react4.default.useCallback(
644
- (allowed) => {
656
+ (index) => {
645
657
  if (resolvedRef.current) return;
646
658
  resolvedRef.current = true;
647
- request.resolve(allowed);
659
+ if (index === 0) request.resolve(true);
660
+ else if (index === 1) request.resolve("allow-session");
661
+ else request.resolve(false);
648
662
  },
649
663
  [request]
650
664
  );
@@ -655,11 +669,13 @@ function PermissionPrompt({ request }) {
655
669
  } else if (key.downArrow || key.rightArrow) {
656
670
  setSelected((prev) => prev < OPTIONS.length - 1 ? prev + 1 : prev);
657
671
  } else if (key.return) {
658
- doResolve(selected === 0);
659
- } else if (input === "y" || input === "a" || input === "1") {
660
- doResolve(true);
661
- } else if (input === "n" || input === "d" || input === "2") {
662
- doResolve(false);
672
+ doResolve(selected);
673
+ } else if (input === "y" || input === "1") {
674
+ doResolve(0);
675
+ } else if (input === "a" || input === "2") {
676
+ doResolve(1);
677
+ } else if (input === "n" || input === "d" || input === "3") {
678
+ doResolve(2);
663
679
  }
664
680
  });
665
681
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
@@ -719,11 +735,11 @@ function useSession(props) {
719
735
  setPermissionRequest({
720
736
  toolName: next.toolName,
721
737
  toolArgs: next.toolArgs,
722
- resolve: (allowed) => {
738
+ resolve: (result) => {
723
739
  permissionQueueRef.current.shift();
724
740
  processingRef.current = false;
725
741
  setPermissionRequest(null);
726
- next.resolve(allowed);
742
+ next.resolve(result);
727
743
  setTimeout(() => processNextPermission(), 0);
728
744
  }
729
745
  });
@@ -739,10 +755,12 @@ function useSession(props) {
739
755
  const onTextDelta = (delta) => {
740
756
  setStreamingText((prev) => prev + delta);
741
757
  };
742
- sessionRef.current = new import_agent_sdk.Session({
758
+ const paths = (0, import_agent_sdk.projectPaths)(props.cwd ?? process.cwd());
759
+ sessionRef.current = (0, import_agent_sdk.createSession)({
743
760
  config: props.config,
744
761
  context: props.context,
745
762
  terminal: NOOP_TERMINAL,
763
+ sessionLogger: new import_agent_sdk.FileSessionLogger(paths.logs),
746
764
  projectInfo: props.projectInfo,
747
765
  sessionStore: props.sessionStore,
748
766
  permissionMode: props.permissionMode,
@@ -768,6 +786,7 @@ var HELP_TEXT = [
768
786
  " /compact [instr] \u2014 Compact context (optional focus instructions)",
769
787
  " /mode [m] \u2014 Show/change permission mode",
770
788
  " /cost \u2014 Show session info",
789
+ " /reset \u2014 Delete settings and exit",
771
790
  " /exit \u2014 Exit CLI"
772
791
  ].join("\n");
773
792
  function handleModeCommand(arg, session, addMessage) {
@@ -813,6 +832,39 @@ async function executeSlashCommand(cmd, parts, session, addMessage, setMessages,
813
832
  Messages: ${session.getMessageCount()}`
814
833
  });
815
834
  return true;
835
+ case "permissions": {
836
+ const mode = session.getPermissionMode();
837
+ const sessionAllowed = session.getSessionAllowedTools();
838
+ const lines = [`Permission mode: ${mode}`];
839
+ if (sessionAllowed.length > 0) {
840
+ lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
841
+ } else {
842
+ lines.push("No session-approved tools.");
843
+ }
844
+ addMessage({ role: "system", content: lines.join("\n") });
845
+ return true;
846
+ }
847
+ case "context": {
848
+ const ctx = session.getContextState();
849
+ addMessage({
850
+ role: "system",
851
+ content: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`
852
+ });
853
+ return true;
854
+ }
855
+ case "reset": {
856
+ const { existsSync: exists, unlinkSync: unlink } = await import("fs");
857
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
858
+ const settingsPath = `${home}/.robota/settings.json`;
859
+ if (exists(settingsPath)) {
860
+ unlink(settingsPath);
861
+ addMessage({ role: "system", content: `Deleted ${settingsPath}. Exiting...` });
862
+ } else {
863
+ addMessage({ role: "system", content: "No user settings found." });
864
+ }
865
+ setTimeout(() => exit(), 500);
866
+ return true;
867
+ }
816
868
  case "exit":
817
869
  exit();
818
870
  return true;
@@ -853,15 +905,42 @@ function StreamingIndicator({ text }) {
853
905
  async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextPercentage) {
854
906
  setIsThinking(true);
855
907
  clearStreamingText();
908
+ const historyBefore = session.getHistory().length;
856
909
  try {
857
910
  const response = await session.run(prompt);
858
911
  clearStreamingText();
912
+ const history = session.getHistory();
913
+ const toolLines = [];
914
+ for (let i = historyBefore; i < history.length; i++) {
915
+ const msg = history[i];
916
+ if (msg.role === "assistant" && msg.toolCalls) {
917
+ for (const tc of msg.toolCalls) {
918
+ let value = "";
919
+ try {
920
+ const parsed = JSON.parse(tc.function.arguments);
921
+ const firstVal = Object.values(parsed)[0];
922
+ value = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
923
+ } catch {
924
+ value = tc.function.arguments;
925
+ }
926
+ const truncated = value.length > 80 ? value.slice(0, 77) + "..." : value;
927
+ toolLines.push(`${tc.function.name}(${truncated})`);
928
+ }
929
+ }
930
+ }
931
+ if (toolLines.length > 0) {
932
+ addMessage({ role: "tool", content: toolLines.join("\n"), toolName: `${toolLines.length} tools` });
933
+ }
859
934
  addMessage({ role: "assistant", content: response || "(empty response)" });
860
935
  setContextPercentage(session.getContextState().usedPercentage);
861
936
  } catch (err) {
862
937
  clearStreamingText();
863
- const errMsg = err instanceof Error ? err.message : String(err);
864
- addMessage({ role: "system", content: `Error: ${errMsg}` });
938
+ if (err instanceof DOMException && err.name === "AbortError") {
939
+ addMessage({ role: "system", content: "Cancelled." });
940
+ } else {
941
+ const errMsg = err instanceof Error ? err.message : String(err);
942
+ addMessage({ role: "system", content: `Error: ${errMsg}` });
943
+ }
865
944
  } finally {
866
945
  setIsThinking(false);
867
946
  }
@@ -872,7 +951,15 @@ function buildSkillPrompt(input, registry) {
872
951
  const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
873
952
  if (!skillCmd) return null;
874
953
  const args = parts.slice(1).join(" ").trim();
875
- return args ? `Use the "${cmd}" skill: ${args}` : `Use the "${cmd}" skill: ${skillCmd.description}`;
954
+ const userInstruction = args || skillCmd.description;
955
+ if (skillCmd.skillContent) {
956
+ return `<skill name="${cmd}">
957
+ ${skillCmd.skillContent}
958
+ </skill>
959
+
960
+ Execute the "${cmd}" skill: ${userInstruction}`;
961
+ }
962
+ return `Use the "${cmd}" skill: ${userInstruction}`;
876
963
  }
877
964
  function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextPercentage, registry) {
878
965
  return (0, import_react5.useCallback)(
@@ -945,8 +1032,9 @@ function App(props) {
945
1032
  (0, import_ink8.useInput)(
946
1033
  (_input, key) => {
947
1034
  if (key.ctrl && _input === "c") exit();
1035
+ if (key.escape && isThinking) session.abort();
948
1036
  },
949
- { isActive: !permissionRequest && !isThinking }
1037
+ { isActive: !permissionRequest }
950
1038
  );
951
1039
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", children: [
952
1040
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
@@ -1067,7 +1155,8 @@ function parseCliArgs() {
1067
1155
  model: { type: "string" },
1068
1156
  "permission-mode": { type: "string" },
1069
1157
  "max-turns": { type: "string" },
1070
- version: { type: "boolean", default: false }
1158
+ version: { type: "boolean", default: false },
1159
+ reset: { type: "boolean", default: false }
1071
1160
  }
1072
1161
  });
1073
1162
  return {
@@ -1078,7 +1167,8 @@ function parseCliArgs() {
1078
1167
  model: values["model"],
1079
1168
  permissionMode: parsePermissionMode(values["permission-mode"]),
1080
1169
  maxTurns: parseMaxTurns(values["max-turns"]),
1081
- version: values["version"] ?? false
1170
+ version: values["version"] ?? false,
1171
+ reset: values["reset"] ?? false
1082
1172
  };
1083
1173
  }
1084
1174
  var PrintTerminal = class {
@@ -1129,6 +1219,83 @@ var PrintTerminal = class {
1129
1219
  } };
1130
1220
  }
1131
1221
  };
1222
+ function getUserSettingsPath() {
1223
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
1224
+ return (0, import_node_path2.join)(home, ".robota", "settings.json");
1225
+ }
1226
+ async function ensureConfig(cwd) {
1227
+ const userPath = getUserSettingsPath();
1228
+ const projectPath = (0, import_node_path2.join)(cwd, ".robota", "settings.json");
1229
+ const localPath = (0, import_node_path2.join)(cwd, ".robota", "settings.local.json");
1230
+ if ((0, import_node_fs2.existsSync)(userPath) || (0, import_node_fs2.existsSync)(projectPath) || (0, import_node_fs2.existsSync)(localPath)) {
1231
+ return;
1232
+ }
1233
+ process.stdout.write("\n");
1234
+ process.stdout.write(" Welcome to Robota CLI!\n");
1235
+ process.stdout.write(" No configuration found. Let's set up your API key.\n");
1236
+ process.stdout.write("\n");
1237
+ const apiKey = await new Promise((resolve) => {
1238
+ process.stdout.write(" Anthropic API key: ");
1239
+ let input = "";
1240
+ const stdin = process.stdin;
1241
+ const wasRaw = stdin.isRaw;
1242
+ stdin.setRawMode(true);
1243
+ stdin.resume();
1244
+ stdin.setEncoding("utf8");
1245
+ const onData = (data) => {
1246
+ for (const ch of data) {
1247
+ if (ch === "\r" || ch === "\n") {
1248
+ stdin.removeListener("data", onData);
1249
+ stdin.setRawMode(wasRaw ?? false);
1250
+ stdin.pause();
1251
+ process.stdout.write("\n");
1252
+ resolve(input.trim());
1253
+ return;
1254
+ } else if (ch === "\x7F" || ch === "\b") {
1255
+ if (input.length > 0) {
1256
+ input = input.slice(0, -1);
1257
+ process.stdout.write("\b \b");
1258
+ }
1259
+ } else if (ch === "") {
1260
+ process.stdout.write("\n");
1261
+ process.exit(0);
1262
+ } else if (ch.charCodeAt(0) >= 32) {
1263
+ input += ch;
1264
+ process.stdout.write("*");
1265
+ }
1266
+ }
1267
+ };
1268
+ stdin.on("data", onData);
1269
+ });
1270
+ if (!apiKey) {
1271
+ process.stderr.write("\n No API key provided. Exiting.\n");
1272
+ process.exit(1);
1273
+ }
1274
+ const settingsDir = (0, import_node_path2.dirname)(userPath);
1275
+ (0, import_node_fs2.mkdirSync)(settingsDir, { recursive: true });
1276
+ const settings = {
1277
+ provider: {
1278
+ name: "anthropic",
1279
+ model: "claude-sonnet-4-6",
1280
+ apiKey
1281
+ }
1282
+ };
1283
+ (0, import_node_fs2.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
1284
+ process.stdout.write(`
1285
+ Config saved to ${userPath}
1286
+
1287
+ `);
1288
+ }
1289
+ function resetConfig() {
1290
+ const userPath = getUserSettingsPath();
1291
+ if ((0, import_node_fs2.existsSync)(userPath)) {
1292
+ (0, import_node_fs2.unlinkSync)(userPath);
1293
+ process.stdout.write(`Deleted ${userPath}
1294
+ `);
1295
+ } else {
1296
+ process.stdout.write("No user settings found.\n");
1297
+ }
1298
+ }
1132
1299
  async function startCli() {
1133
1300
  const args = parseCliArgs();
1134
1301
  if (args.version) {
@@ -1136,7 +1303,12 @@ async function startCli() {
1136
1303
  `);
1137
1304
  return;
1138
1305
  }
1306
+ if (args.reset) {
1307
+ resetConfig();
1308
+ return;
1309
+ }
1139
1310
  const cwd = process.cwd();
1311
+ await ensureConfig(cwd);
1140
1312
  const [config, context, projectInfo] = await Promise.all([
1141
1313
  (0, import_agent_sdk2.loadConfig)(cwd),
1142
1314
  (0, import_agent_sdk2.loadContext)(cwd),
@@ -1153,13 +1325,14 @@ async function startCli() {
1153
1325
  process.exit(1);
1154
1326
  }
1155
1327
  const terminal = new PrintTerminal();
1156
- const session = new import_agent_sdk2.Session({
1328
+ const paths = (0, import_agent_sdk2.projectPaths)(cwd);
1329
+ const session = (0, import_agent_sdk2.createSession)({
1157
1330
  config,
1158
1331
  context,
1159
1332
  terminal,
1333
+ sessionLogger: new import_agent_sdk2.FileSessionLogger(paths.logs),
1160
1334
  projectInfo,
1161
1335
  permissionMode: args.permissionMode,
1162
- systemPromptBuilder: import_agent_sdk2.buildSystemPrompt,
1163
1336
  promptForApproval
1164
1337
  });
1165
1338
  const response = await session.run(prompt);
@@ -1178,6 +1351,16 @@ async function startCli() {
1178
1351
  }
1179
1352
 
1180
1353
  // src/bin.ts
1354
+ process.on("uncaughtException", (err) => {
1355
+ const msg = err.message ?? "";
1356
+ const isLikelyIME = msg.includes("string-width") || msg.includes("setCursorPosition") || msg.includes("getStringWidth") || msg.includes("slice") || msg.includes("charCodeAt");
1357
+ if (isLikelyIME) {
1358
+ process.stderr.write(`[robota] IME error suppressed: ${msg}
1359
+ `);
1360
+ return;
1361
+ }
1362
+ throw err;
1363
+ });
1181
1364
  startCli().catch((err) => {
1182
1365
  const message = err instanceof Error ? err.message : String(err);
1183
1366
  process.stderr.write(message + "\n");
package/dist/node/bin.js CHANGED
@@ -1,9 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startCli
4
- } from "./chunk-C56WFH5S.js";
4
+ } from "./chunk-UT4RQDTI.js";
5
5
 
6
6
  // src/bin.ts
7
+ process.on("uncaughtException", (err) => {
8
+ const msg = err.message ?? "";
9
+ const isLikelyIME = msg.includes("string-width") || msg.includes("setCursorPosition") || msg.includes("getStringWidth") || msg.includes("slice") || msg.includes("charCodeAt");
10
+ if (isLikelyIME) {
11
+ process.stderr.write(`[robota] IME error suppressed: ${msg}
12
+ `);
13
+ return;
14
+ }
15
+ throw err;
16
+ });
7
17
  startCli().catch((err) => {
8
18
  const message = err instanceof Error ? err.message : String(err);
9
19
  process.stderr.write(message + "\n");