@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.
@@ -135,6 +135,7 @@ function createBuiltinCommands() {
135
135
  { name: "cost", description: "Show session info", source: "builtin" },
136
136
  { name: "context", description: "Context window info", source: "builtin" },
137
137
  { name: "permissions", description: "Permission rules", source: "builtin" },
138
+ { name: "reset", description: "Delete settings and exit", source: "builtin" },
138
139
  { name: "exit", description: "Exit CLI", source: "builtin" }
139
140
  ];
140
141
  }
@@ -186,7 +187,8 @@ function scanSkillsDir(skillsDir) {
186
187
  commands.push({
187
188
  name: frontmatter?.name ?? entry.name,
188
189
  description: frontmatter?.description ?? `Skill: ${entry.name}`,
189
- source: "skill"
190
+ source: "skill",
191
+ skillContent: content
190
192
  });
191
193
  }
192
194
  return commands;
@@ -362,50 +364,60 @@ function CjkTextInput({
362
364
  }
363
365
  (0, import_ink3.useInput)(
364
366
  (input, key) => {
365
- if (key.upArrow || key.downArrow || key.ctrl && input === "c" || key.tab || key.shift && key.tab) {
366
- return;
367
- }
368
- if (key.return) {
369
- onSubmit?.(valueRef.current);
370
- return;
371
- }
372
- if (key.leftArrow) {
373
- if (cursorRef.current > 0) {
374
- cursorRef.current -= 1;
375
- forceRender((n) => n + 1);
367
+ try {
368
+ if (key.upArrow || key.downArrow || key.ctrl && input === "c" || key.tab || key.shift && key.tab) {
369
+ return;
376
370
  }
377
- return;
378
- }
379
- if (key.rightArrow) {
380
- if (cursorRef.current < valueRef.current.length) {
381
- cursorRef.current += 1;
382
- forceRender((n) => n + 1);
371
+ if (key.return) {
372
+ onSubmit?.(valueRef.current);
373
+ return;
383
374
  }
384
- return;
385
- }
386
- if (key.backspace || key.delete) {
387
- if (cursorRef.current > 0) {
388
- const v2 = valueRef.current;
389
- const next2 = v2.slice(0, cursorRef.current - 1) + v2.slice(cursorRef.current);
390
- cursorRef.current -= 1;
391
- valueRef.current = next2;
392
- onChange(next2);
375
+ if (key.leftArrow) {
376
+ if (cursorRef.current > 0) {
377
+ cursorRef.current -= 1;
378
+ forceRender((n) => n + 1);
379
+ }
380
+ return;
393
381
  }
394
- return;
382
+ if (key.rightArrow) {
383
+ if (cursorRef.current < valueRef.current.length) {
384
+ cursorRef.current += 1;
385
+ forceRender((n) => n + 1);
386
+ }
387
+ return;
388
+ }
389
+ if (key.backspace || key.delete) {
390
+ if (cursorRef.current > 0) {
391
+ const v2 = valueRef.current;
392
+ const next2 = v2.slice(0, cursorRef.current - 1) + v2.slice(cursorRef.current);
393
+ cursorRef.current -= 1;
394
+ valueRef.current = next2;
395
+ onChange(next2);
396
+ }
397
+ return;
398
+ }
399
+ if (!input || input.length === 0) return;
400
+ const printable = input.replace(/[\x00-\x1f\x7f]/g, "");
401
+ if (printable.length === 0) return;
402
+ const v = valueRef.current;
403
+ const c = cursorRef.current;
404
+ const next = v.slice(0, c) + printable + v.slice(c);
405
+ cursorRef.current = c + printable.length;
406
+ valueRef.current = next;
407
+ onChange(next);
408
+ } catch {
395
409
  }
396
- const v = valueRef.current;
397
- const c = cursorRef.current;
398
- const next = v.slice(0, c) + input + v.slice(c);
399
- cursorRef.current = c + input.length;
400
- valueRef.current = next;
401
- onChange(next);
402
410
  },
403
411
  { isActive: focus }
404
412
  );
405
413
  if (showCursor && focus) {
406
- const textBeforeCursor = [...valueRef.current].slice(0, cursorRef.current).join("");
407
- const cursorX = 4 + (0, import_string_width.default)(textBeforeCursor);
408
- setCursorPosition({ x: cursorX, y: 0 });
414
+ try {
415
+ const textBeforeCursor = [...valueRef.current].slice(0, cursorRef.current).join("");
416
+ const cursorX = 4 + (0, import_string_width.default)(textBeforeCursor);
417
+ setCursorPosition({ x: cursorX, y: 0 });
418
+ } catch {
419
+ setCursorPosition({ x: 4, y: 0 });
420
+ }
409
421
  }
410
422
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { children: renderWithCursor(valueRef.current, cursorRef.current, placeholder, showCursor && focus) });
411
423
  }
@@ -641,7 +653,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
641
653
  var import_react4 = __toESM(require("react"), 1);
642
654
  var import_ink7 = require("ink");
643
655
  var import_jsx_runtime7 = require("react/jsx-runtime");
644
- var OPTIONS = ["Allow", "Deny"];
656
+ var OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
645
657
  function formatArgs2(args) {
646
658
  const entries = Object.entries(args);
647
659
  if (entries.length === 0) return "(no arguments)";
@@ -657,10 +669,12 @@ function PermissionPrompt({ request }) {
657
669
  setSelected(0);
658
670
  }
659
671
  const doResolve = import_react4.default.useCallback(
660
- (allowed) => {
672
+ (index) => {
661
673
  if (resolvedRef.current) return;
662
674
  resolvedRef.current = true;
663
- request.resolve(allowed);
675
+ if (index === 0) request.resolve(true);
676
+ else if (index === 1) request.resolve("allow-session");
677
+ else request.resolve(false);
664
678
  },
665
679
  [request]
666
680
  );
@@ -671,11 +685,13 @@ function PermissionPrompt({ request }) {
671
685
  } else if (key.downArrow || key.rightArrow) {
672
686
  setSelected((prev) => prev < OPTIONS.length - 1 ? prev + 1 : prev);
673
687
  } else if (key.return) {
674
- doResolve(selected === 0);
675
- } else if (input === "y" || input === "a" || input === "1") {
676
- doResolve(true);
677
- } else if (input === "n" || input === "d" || input === "2") {
678
- doResolve(false);
688
+ doResolve(selected);
689
+ } else if (input === "y" || input === "1") {
690
+ doResolve(0);
691
+ } else if (input === "a" || input === "2") {
692
+ doResolve(1);
693
+ } else if (input === "n" || input === "d" || input === "3") {
694
+ doResolve(2);
679
695
  }
680
696
  });
681
697
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
@@ -735,11 +751,11 @@ function useSession(props) {
735
751
  setPermissionRequest({
736
752
  toolName: next.toolName,
737
753
  toolArgs: next.toolArgs,
738
- resolve: (allowed) => {
754
+ resolve: (result) => {
739
755
  permissionQueueRef.current.shift();
740
756
  processingRef.current = false;
741
757
  setPermissionRequest(null);
742
- next.resolve(allowed);
758
+ next.resolve(result);
743
759
  setTimeout(() => processNextPermission(), 0);
744
760
  }
745
761
  });
@@ -755,10 +771,12 @@ function useSession(props) {
755
771
  const onTextDelta = (delta) => {
756
772
  setStreamingText((prev) => prev + delta);
757
773
  };
758
- sessionRef.current = new import_agent_sdk.Session({
774
+ const paths = (0, import_agent_sdk.projectPaths)(props.cwd ?? process.cwd());
775
+ sessionRef.current = (0, import_agent_sdk.createSession)({
759
776
  config: props.config,
760
777
  context: props.context,
761
778
  terminal: NOOP_TERMINAL,
779
+ sessionLogger: new import_agent_sdk.FileSessionLogger(paths.logs),
762
780
  projectInfo: props.projectInfo,
763
781
  sessionStore: props.sessionStore,
764
782
  permissionMode: props.permissionMode,
@@ -784,6 +802,7 @@ var HELP_TEXT = [
784
802
  " /compact [instr] \u2014 Compact context (optional focus instructions)",
785
803
  " /mode [m] \u2014 Show/change permission mode",
786
804
  " /cost \u2014 Show session info",
805
+ " /reset \u2014 Delete settings and exit",
787
806
  " /exit \u2014 Exit CLI"
788
807
  ].join("\n");
789
808
  function handleModeCommand(arg, session, addMessage) {
@@ -829,6 +848,39 @@ async function executeSlashCommand(cmd, parts, session, addMessage, setMessages,
829
848
  Messages: ${session.getMessageCount()}`
830
849
  });
831
850
  return true;
851
+ case "permissions": {
852
+ const mode = session.getPermissionMode();
853
+ const sessionAllowed = session.getSessionAllowedTools();
854
+ const lines = [`Permission mode: ${mode}`];
855
+ if (sessionAllowed.length > 0) {
856
+ lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
857
+ } else {
858
+ lines.push("No session-approved tools.");
859
+ }
860
+ addMessage({ role: "system", content: lines.join("\n") });
861
+ return true;
862
+ }
863
+ case "context": {
864
+ const ctx = session.getContextState();
865
+ addMessage({
866
+ role: "system",
867
+ content: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`
868
+ });
869
+ return true;
870
+ }
871
+ case "reset": {
872
+ const { existsSync: exists, unlinkSync: unlink } = await import("fs");
873
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
874
+ const settingsPath = `${home}/.robota/settings.json`;
875
+ if (exists(settingsPath)) {
876
+ unlink(settingsPath);
877
+ addMessage({ role: "system", content: `Deleted ${settingsPath}. Exiting...` });
878
+ } else {
879
+ addMessage({ role: "system", content: "No user settings found." });
880
+ }
881
+ setTimeout(() => exit(), 500);
882
+ return true;
883
+ }
832
884
  case "exit":
833
885
  exit();
834
886
  return true;
@@ -869,15 +921,42 @@ function StreamingIndicator({ text }) {
869
921
  async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextPercentage) {
870
922
  setIsThinking(true);
871
923
  clearStreamingText();
924
+ const historyBefore = session.getHistory().length;
872
925
  try {
873
926
  const response = await session.run(prompt);
874
927
  clearStreamingText();
928
+ const history = session.getHistory();
929
+ const toolLines = [];
930
+ for (let i = historyBefore; i < history.length; i++) {
931
+ const msg = history[i];
932
+ if (msg.role === "assistant" && msg.toolCalls) {
933
+ for (const tc of msg.toolCalls) {
934
+ let value = "";
935
+ try {
936
+ const parsed = JSON.parse(tc.function.arguments);
937
+ const firstVal = Object.values(parsed)[0];
938
+ value = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
939
+ } catch {
940
+ value = tc.function.arguments;
941
+ }
942
+ const truncated = value.length > 80 ? value.slice(0, 77) + "..." : value;
943
+ toolLines.push(`${tc.function.name}(${truncated})`);
944
+ }
945
+ }
946
+ }
947
+ if (toolLines.length > 0) {
948
+ addMessage({ role: "tool", content: toolLines.join("\n"), toolName: `${toolLines.length} tools` });
949
+ }
875
950
  addMessage({ role: "assistant", content: response || "(empty response)" });
876
951
  setContextPercentage(session.getContextState().usedPercentage);
877
952
  } catch (err) {
878
953
  clearStreamingText();
879
- const errMsg = err instanceof Error ? err.message : String(err);
880
- addMessage({ role: "system", content: `Error: ${errMsg}` });
954
+ if (err instanceof DOMException && err.name === "AbortError") {
955
+ addMessage({ role: "system", content: "Cancelled." });
956
+ } else {
957
+ const errMsg = err instanceof Error ? err.message : String(err);
958
+ addMessage({ role: "system", content: `Error: ${errMsg}` });
959
+ }
881
960
  } finally {
882
961
  setIsThinking(false);
883
962
  }
@@ -888,7 +967,15 @@ function buildSkillPrompt(input, registry) {
888
967
  const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
889
968
  if (!skillCmd) return null;
890
969
  const args = parts.slice(1).join(" ").trim();
891
- return args ? `Use the "${cmd}" skill: ${args}` : `Use the "${cmd}" skill: ${skillCmd.description}`;
970
+ const userInstruction = args || skillCmd.description;
971
+ if (skillCmd.skillContent) {
972
+ return `<skill name="${cmd}">
973
+ ${skillCmd.skillContent}
974
+ </skill>
975
+
976
+ Execute the "${cmd}" skill: ${userInstruction}`;
977
+ }
978
+ return `Use the "${cmd}" skill: ${userInstruction}`;
892
979
  }
893
980
  function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextPercentage, registry) {
894
981
  return (0, import_react5.useCallback)(
@@ -961,8 +1048,9 @@ function App(props) {
961
1048
  (0, import_ink8.useInput)(
962
1049
  (_input, key) => {
963
1050
  if (key.ctrl && _input === "c") exit();
1051
+ if (key.escape && isThinking) session.abort();
964
1052
  },
965
- { isActive: !permissionRequest && !isThinking }
1053
+ { isActive: !permissionRequest }
966
1054
  );
967
1055
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", children: [
968
1056
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
@@ -1083,7 +1171,8 @@ function parseCliArgs() {
1083
1171
  model: { type: "string" },
1084
1172
  "permission-mode": { type: "string" },
1085
1173
  "max-turns": { type: "string" },
1086
- version: { type: "boolean", default: false }
1174
+ version: { type: "boolean", default: false },
1175
+ reset: { type: "boolean", default: false }
1087
1176
  }
1088
1177
  });
1089
1178
  return {
@@ -1094,7 +1183,8 @@ function parseCliArgs() {
1094
1183
  model: values["model"],
1095
1184
  permissionMode: parsePermissionMode(values["permission-mode"]),
1096
1185
  maxTurns: parseMaxTurns(values["max-turns"]),
1097
- version: values["version"] ?? false
1186
+ version: values["version"] ?? false,
1187
+ reset: values["reset"] ?? false
1098
1188
  };
1099
1189
  }
1100
1190
  var PrintTerminal = class {
@@ -1145,6 +1235,83 @@ var PrintTerminal = class {
1145
1235
  } };
1146
1236
  }
1147
1237
  };
1238
+ function getUserSettingsPath() {
1239
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
1240
+ return (0, import_node_path2.join)(home, ".robota", "settings.json");
1241
+ }
1242
+ async function ensureConfig(cwd) {
1243
+ const userPath = getUserSettingsPath();
1244
+ const projectPath = (0, import_node_path2.join)(cwd, ".robota", "settings.json");
1245
+ const localPath = (0, import_node_path2.join)(cwd, ".robota", "settings.local.json");
1246
+ if ((0, import_node_fs2.existsSync)(userPath) || (0, import_node_fs2.existsSync)(projectPath) || (0, import_node_fs2.existsSync)(localPath)) {
1247
+ return;
1248
+ }
1249
+ process.stdout.write("\n");
1250
+ process.stdout.write(" Welcome to Robota CLI!\n");
1251
+ process.stdout.write(" No configuration found. Let's set up your API key.\n");
1252
+ process.stdout.write("\n");
1253
+ const apiKey = await new Promise((resolve) => {
1254
+ process.stdout.write(" Anthropic API key: ");
1255
+ let input = "";
1256
+ const stdin = process.stdin;
1257
+ const wasRaw = stdin.isRaw;
1258
+ stdin.setRawMode(true);
1259
+ stdin.resume();
1260
+ stdin.setEncoding("utf8");
1261
+ const onData = (data) => {
1262
+ for (const ch of data) {
1263
+ if (ch === "\r" || ch === "\n") {
1264
+ stdin.removeListener("data", onData);
1265
+ stdin.setRawMode(wasRaw ?? false);
1266
+ stdin.pause();
1267
+ process.stdout.write("\n");
1268
+ resolve(input.trim());
1269
+ return;
1270
+ } else if (ch === "\x7F" || ch === "\b") {
1271
+ if (input.length > 0) {
1272
+ input = input.slice(0, -1);
1273
+ process.stdout.write("\b \b");
1274
+ }
1275
+ } else if (ch === "") {
1276
+ process.stdout.write("\n");
1277
+ process.exit(0);
1278
+ } else if (ch.charCodeAt(0) >= 32) {
1279
+ input += ch;
1280
+ process.stdout.write("*");
1281
+ }
1282
+ }
1283
+ };
1284
+ stdin.on("data", onData);
1285
+ });
1286
+ if (!apiKey) {
1287
+ process.stderr.write("\n No API key provided. Exiting.\n");
1288
+ process.exit(1);
1289
+ }
1290
+ const settingsDir = (0, import_node_path2.dirname)(userPath);
1291
+ (0, import_node_fs2.mkdirSync)(settingsDir, { recursive: true });
1292
+ const settings = {
1293
+ provider: {
1294
+ name: "anthropic",
1295
+ model: "claude-sonnet-4-6",
1296
+ apiKey
1297
+ }
1298
+ };
1299
+ (0, import_node_fs2.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
1300
+ process.stdout.write(`
1301
+ Config saved to ${userPath}
1302
+
1303
+ `);
1304
+ }
1305
+ function resetConfig() {
1306
+ const userPath = getUserSettingsPath();
1307
+ if ((0, import_node_fs2.existsSync)(userPath)) {
1308
+ (0, import_node_fs2.unlinkSync)(userPath);
1309
+ process.stdout.write(`Deleted ${userPath}
1310
+ `);
1311
+ } else {
1312
+ process.stdout.write("No user settings found.\n");
1313
+ }
1314
+ }
1148
1315
  async function startCli() {
1149
1316
  const args = parseCliArgs();
1150
1317
  if (args.version) {
@@ -1152,7 +1319,12 @@ async function startCli() {
1152
1319
  `);
1153
1320
  return;
1154
1321
  }
1322
+ if (args.reset) {
1323
+ resetConfig();
1324
+ return;
1325
+ }
1155
1326
  const cwd = process.cwd();
1327
+ await ensureConfig(cwd);
1156
1328
  const [config, context, projectInfo] = await Promise.all([
1157
1329
  (0, import_agent_sdk2.loadConfig)(cwd),
1158
1330
  (0, import_agent_sdk2.loadContext)(cwd),
@@ -1169,13 +1341,14 @@ async function startCli() {
1169
1341
  process.exit(1);
1170
1342
  }
1171
1343
  const terminal = new PrintTerminal();
1172
- const session = new import_agent_sdk2.Session({
1344
+ const paths = (0, import_agent_sdk2.projectPaths)(cwd);
1345
+ const session = (0, import_agent_sdk2.createSession)({
1173
1346
  config,
1174
1347
  context,
1175
1348
  terminal,
1349
+ sessionLogger: new import_agent_sdk2.FileSessionLogger(paths.logs),
1176
1350
  projectInfo,
1177
1351
  permissionMode: args.permissionMode,
1178
- systemPromptBuilder: import_agent_sdk2.buildSystemPrompt,
1179
1352
  promptForApproval
1180
1353
  });
1181
1354
  const response = await session.run(prompt);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  startCli
3
- } from "./chunk-C56WFH5S.js";
3
+ } from "./chunk-UT4RQDTI.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.1",
3
+ "version": "3.0.0-beta.10",
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.1",
39
- "@robota-sdk/agent-sdk": "3.0.0-beta.1"
38
+ "@robota-sdk/agent-core": "3.0.0-beta.10",
39
+ "@robota-sdk/agent-sdk": "3.0.0-beta.10"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/marked": "^6.0.0",