@robota-sdk/agent-cli 3.0.0-beta.31 → 3.0.0-beta.33

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/README.md CHANGED
@@ -177,17 +177,17 @@ Configure in `.robota/settings.json` or `.robota/settings.local.json`:
177
177
 
178
178
  ## Slash Commands
179
179
 
180
- | Command | Description |
181
- | -------------- | ------------------------------- |
182
- | `/help` | Show help |
183
- | `/clear` | Clear conversation history |
184
- | `/mode [mode]` | Show or change permission mode |
185
- | `/model [model]`| Select AI model (confirmation + restart) |
186
- | `/compact [instructions]` | Compress context window |
187
- | `/cost` | Show session info |
188
- | `/context` | Context window details |
189
- | `/permissions` | Show permission rules |
190
- | `/exit` | Exit CLI |
180
+ | Command | Description |
181
+ | ------------------------- | ---------------------------------------- |
182
+ | `/help` | Show help |
183
+ | `/clear` | Clear conversation history |
184
+ | `/mode [mode]` | Show or change permission mode |
185
+ | `/model [model]` | Select AI model (confirmation + restart) |
186
+ | `/compact [instructions]` | Compress context window |
187
+ | `/cost` | Show session info |
188
+ | `/context` | Context window details |
189
+ | `/permissions` | Show permission rules |
190
+ | `/exit` | Exit CLI |
191
191
 
192
192
  ## Configuration
193
193
 
@@ -220,6 +220,31 @@ Settings are loaded from (highest priority first):
220
220
 
221
221
  Copy `.env.example` to `.env` and set your key. The CLI reads `.env` automatically in dev mode.
222
222
 
223
+ ## Paste Template
224
+
225
+ When pasting multiline text, the input area collapses it into a label: `[Pasted text #1 +42 lines]`. Multiple pastes are numbered sequentially. The full content is expanded on submit, keeping the input area compact while preserving the complete text for the AI.
226
+
227
+ ## Edit Diff Display
228
+
229
+ After the Edit tool runs, a `DiffBlock` component renders the change with colored `+`/`-` line markers (green = added, red = removed), giving immediate visual feedback on file modifications.
230
+
231
+ ## Plugin Commands Display
232
+
233
+ Plugin-provided commands appear in the slash command autocomplete with their source plugin name as a hint. Commands are also accessible via colon format: `/plugin-name:command`.
234
+
235
+ ## Memory Management
236
+
237
+ The CLI applies two strategies to keep memory usage bounded during long sessions:
238
+
239
+ - **Message windowing** — Conversation history is capped at 100 messages. Older messages are pruned from the window.
240
+ - **Tool state cleanup** — Completed tool results older than 50 entries are cleaned up to reduce retained state.
241
+
242
+ React components use `React.memo` to avoid unnecessary re-renders.
243
+
244
+ ### Forced Summary on maxRounds
245
+
246
+ When the tool execution loop exhausts its maximum rounds, the CLI injects a synthetic user message requesting a summary. This ensures the user always receives a meaningful response even if the agent could not complete all planned tool calls.
247
+
223
248
  ## Context Discovery
224
249
 
225
250
  The CLI automatically discovers and loads:
package/dist/node/bin.cjs CHANGED
@@ -27,8 +27,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var import_node_fs3 = require("fs");
28
28
  var import_node_path5 = require("path");
29
29
  var import_node_url = require("url");
30
- var import_agent_sdk4 = require("@robota-sdk/agent-sdk");
31
30
  var import_agent_sdk5 = require("@robota-sdk/agent-sdk");
31
+ var import_agent_sdk6 = require("@robota-sdk/agent-sdk");
32
32
 
33
33
  // src/utils/cli-args.ts
34
34
  var import_node_util = require("util");
@@ -663,6 +663,7 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
663
663
 
664
664
  // src/ui/hooks/useSubmitHandler.ts
665
665
  var import_react4 = require("react");
666
+ var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
666
667
 
667
668
  // src/utils/tool-call-extractor.ts
668
669
  var TOOL_ARG_MAX_LENGTH = 80;
@@ -767,6 +768,40 @@ Execute the "${cmd}" skill: ${userInstruction}`;
767
768
  return `Use the "${cmd}" skill: ${userInstruction}`;
768
769
  }
769
770
 
771
+ // src/commands/skill-executor.ts
772
+ function buildProcessedContent(skill, args, context) {
773
+ if (!skill.skillContent) return null;
774
+ return substituteVariables(skill.skillContent, args, context);
775
+ }
776
+ function buildInjectPrompt(skill, args, context) {
777
+ const processed = buildProcessedContent(skill, args, context);
778
+ if (processed) {
779
+ const userInstruction = args || skill.description;
780
+ return `<skill name="${skill.name}">
781
+ ${processed}
782
+ </skill>
783
+
784
+ Execute the "${skill.name}" skill: ${userInstruction}`;
785
+ }
786
+ return `Use the "${skill.name}" skill: ${args || skill.description}`;
787
+ }
788
+ async function executeSkill(skill, args, callbacks, context) {
789
+ if (skill.context === "fork") {
790
+ if (!callbacks.runInFork) {
791
+ throw new Error("Fork execution is not available. Agent tool deps may not be initialized.");
792
+ }
793
+ const content = buildProcessedContent(skill, args, context);
794
+ const prompt2 = content ?? `Use the "${skill.name}" skill: ${args || skill.description}`;
795
+ const options = {};
796
+ if (skill.agent) options.agent = skill.agent;
797
+ if (skill.allowedTools) options.allowedTools = skill.allowedTools;
798
+ const result = await callbacks.runInFork(prompt2, options);
799
+ return { mode: "fork", result };
800
+ }
801
+ const prompt = buildInjectPrompt(skill, args, context);
802
+ return { mode: "inject", prompt };
803
+ }
804
+
770
805
  // src/ui/hooks/useSubmitHandler.ts
771
806
  function syncContextState(session, setter) {
772
807
  const ctx = session.getContextState();
@@ -805,6 +840,37 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
805
840
  setIsThinking(false);
806
841
  }
807
842
  }
843
+ function createForkRunner(sessionKey) {
844
+ const deps = (0, import_agent_sdk2.retrieveAgentToolDeps)(sessionKey);
845
+ if (!deps) return void 0;
846
+ return async (content, options) => {
847
+ const agentType = options.agent ?? "general-purpose";
848
+ const agentDef = (0, import_agent_sdk2.getBuiltInAgent)(agentType) ?? deps.customAgentRegistry?.(agentType);
849
+ if (!agentDef) {
850
+ throw new Error(`Unknown agent type for fork execution: ${agentType}`);
851
+ }
852
+ const effectiveDef = options.allowedTools ? { ...agentDef, tools: options.allowedTools } : agentDef;
853
+ const subSession = (0, import_agent_sdk2.createSubagentSession)({
854
+ agentDefinition: effectiveDef,
855
+ parentConfig: deps.config,
856
+ parentContext: deps.context,
857
+ parentTools: deps.tools,
858
+ terminal: deps.terminal,
859
+ isForkWorker: true,
860
+ permissionHandler: deps.permissionHandler,
861
+ onTextDelta: deps.onTextDelta,
862
+ onToolExecution: deps.onToolExecution
863
+ });
864
+ return await subSession.run(content);
865
+ };
866
+ }
867
+ function findSkillCommand(input, registry) {
868
+ const parts = input.slice(1).split(/\s+/);
869
+ const cmd = parts[0]?.toLowerCase() ?? "";
870
+ const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
871
+ if (!skillCmd) return null;
872
+ return { skill: skillCmd, args: parts.slice(1).join(" ").trim() };
873
+ }
808
874
  function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry) {
809
875
  return (0, import_react4.useCallback)(
810
876
  async (input) => {
@@ -814,6 +880,33 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
814
880
  syncContextState(session, setContextState);
815
881
  return;
816
882
  }
883
+ const found = findSkillCommand(input, registry);
884
+ if (!found) return;
885
+ const { skill, args } = found;
886
+ if (skill.context === "fork") {
887
+ const runInFork = createForkRunner(session);
888
+ const result = await executeSkill(skill, args, { runInFork });
889
+ if (result.mode === "fork") {
890
+ addMessage({ role: "assistant", content: result.result ?? "(empty response)" });
891
+ syncContextState(session, setContextState);
892
+ return;
893
+ }
894
+ if (result.prompt) {
895
+ const cmdName2 = input.slice(1).split(/\s+/)[0]?.toLowerCase() ?? "";
896
+ const qualifiedName2 = registry.resolveQualifiedName(cmdName2);
897
+ const hookInput2 = qualifiedName2 ? `/${qualifiedName2}${input.slice(1 + cmdName2.length)}` : input;
898
+ return runSessionPrompt(
899
+ result.prompt,
900
+ session,
901
+ addMessage,
902
+ clearStreamingText,
903
+ setIsThinking,
904
+ setContextState,
905
+ hookInput2
906
+ );
907
+ }
908
+ return;
909
+ }
817
910
  const prompt = await buildSkillPrompt(input, registry);
818
911
  if (!prompt) return;
819
912
  const cmdName = input.slice(1).split(/\s+/)[0]?.toLowerCase() ?? "";
@@ -855,7 +948,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
855
948
  var import_react5 = require("react");
856
949
  var import_node_os2 = require("os");
857
950
  var import_node_path3 = require("path");
858
- var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
951
+ var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
859
952
 
860
953
  // src/commands/command-registry.ts
861
954
  var CommandRegistry = class {
@@ -1194,7 +1287,7 @@ function useCommandRegistry(cwd) {
1194
1287
  registry.addSource(new SkillCommandSource(cwd));
1195
1288
  let pluginHooks = {};
1196
1289
  const pluginsDir = (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".robota", "plugins");
1197
- const loader = new import_agent_sdk2.BundlePluginLoader(pluginsDir);
1290
+ const loader = new import_agent_sdk3.BundlePluginLoader(pluginsDir);
1198
1291
  try {
1199
1292
  const plugins = loader.loadPluginsSync();
1200
1293
  if (plugins.length > 0) {
@@ -1212,20 +1305,20 @@ function useCommandRegistry(cwd) {
1212
1305
  var import_react6 = require("react");
1213
1306
  var import_node_os3 = require("os");
1214
1307
  var import_node_path4 = require("path");
1215
- var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
1308
+ var import_agent_sdk4 = require("@robota-sdk/agent-sdk");
1216
1309
  function usePluginCallbacks(cwd) {
1217
1310
  return (0, import_react6.useMemo)(() => {
1218
1311
  const home = (0, import_node_os3.homedir)();
1219
1312
  const pluginsDir = (0, import_node_path4.join)(home, ".robota", "plugins");
1220
1313
  const userSettingsPath = (0, import_node_path4.join)(home, ".robota", "settings.json");
1221
- const settingsStore = new import_agent_sdk3.PluginSettingsStore(userSettingsPath);
1222
- const marketplace = new import_agent_sdk3.MarketplaceClient({ pluginsDir });
1223
- const installer = new import_agent_sdk3.BundlePluginInstaller({
1314
+ const settingsStore = new import_agent_sdk4.PluginSettingsStore(userSettingsPath);
1315
+ const marketplace = new import_agent_sdk4.MarketplaceClient({ pluginsDir });
1316
+ const installer = new import_agent_sdk4.BundlePluginInstaller({
1224
1317
  pluginsDir,
1225
1318
  settingsStore,
1226
1319
  marketplaceClient: marketplace
1227
1320
  });
1228
- const loader = new import_agent_sdk3.BundlePluginLoader(pluginsDir);
1321
+ const loader = new import_agent_sdk4.BundlePluginLoader(pluginsDir);
1229
1322
  return {
1230
1323
  listInstalled: async () => {
1231
1324
  const plugins = await loader.loadAll();
@@ -1510,6 +1603,7 @@ function CjkTextInput({
1510
1603
  value,
1511
1604
  onChange,
1512
1605
  onSubmit,
1606
+ onPaste,
1513
1607
  placeholder = "",
1514
1608
  focus = true,
1515
1609
  showCursor = true
@@ -1533,6 +1627,10 @@ function CjkTextInput({
1533
1627
  onSubmit?.(valueRef.current);
1534
1628
  return;
1535
1629
  }
1630
+ if (input.length > 1 && (input.includes("\n") || input.includes("\r")) && onPaste) {
1631
+ onPaste(input.replace(/\r\n?/g, "\n"));
1632
+ return;
1633
+ }
1536
1634
  if (key.leftArrow) {
1537
1635
  if (cursorRef.current > 0) {
1538
1636
  cursorRef.current -= 1;
@@ -1655,6 +1753,12 @@ function computeScrollOffset(selectedIndex, total) {
1655
1753
  return Math.min(selectedIndex - MAX_VISIBLE + 1, maxOffset);
1656
1754
  }
1657
1755
 
1756
+ // src/utils/paste-labels.ts
1757
+ var PASTE_LABEL_RE = /\[Pasted text #(\d+) \+\d+ lines\]/g;
1758
+ function expandPasteLabels(text, store) {
1759
+ return text.replace(PASTE_LABEL_RE, (_, id) => store.get(Number(id)) ?? "");
1760
+ }
1761
+
1658
1762
  // src/ui/InputArea.tsx
1659
1763
  var import_jsx_runtime7 = require("react/jsx-runtime");
1660
1764
  function parseSlashInput(value) {
@@ -1711,6 +1815,8 @@ function useAutocomplete(value, registry) {
1711
1815
  }
1712
1816
  function InputArea({ onSubmit, isDisabled, registry }) {
1713
1817
  const [value, setValue] = (0, import_react10.useState)("");
1818
+ const pasteStore = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
1819
+ const pasteIdRef = (0, import_react10.useRef)(0);
1714
1820
  const {
1715
1821
  showPopup,
1716
1822
  filteredCommands,
@@ -1719,6 +1825,14 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1719
1825
  isSubcommandMode,
1720
1826
  setShowPopup
1721
1827
  } = useAutocomplete(value, registry);
1828
+ const handlePaste = (0, import_react10.useCallback)((text) => {
1829
+ pasteIdRef.current += 1;
1830
+ const id = pasteIdRef.current;
1831
+ pasteStore.current.set(id, text);
1832
+ const lineCount = text.split("\n").length;
1833
+ const label = `[Pasted text #${id} +${lineCount} lines]`;
1834
+ setValue((prev) => prev ? `${prev} ${label}` : label);
1835
+ }, []);
1722
1836
  const handleSubmit = (0, import_react10.useCallback)(
1723
1837
  (text) => {
1724
1838
  const trimmed = text.trim();
@@ -1727,8 +1841,11 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1727
1841
  selectCommand(filteredCommands[selectedIndex]);
1728
1842
  return;
1729
1843
  }
1844
+ const expanded = expandPasteLabels(trimmed, pasteStore.current);
1730
1845
  setValue("");
1731
- onSubmit(trimmed);
1846
+ pasteStore.current.clear();
1847
+ pasteIdRef.current = 0;
1848
+ onSubmit(expanded);
1732
1849
  },
1733
1850
  [showPopup, filteredCommands, selectedIndex, onSubmit]
1734
1851
  );
@@ -1785,6 +1902,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1785
1902
  value,
1786
1903
  onChange: setValue,
1787
1904
  onSubmit: handleSubmit,
1905
+ onPaste: handlePaste,
1788
1906
  placeholder: "Type a message or /help"
1789
1907
  }
1790
1908
  )
@@ -2257,9 +2375,9 @@ async function startCli() {
2257
2375
  const cwd = process.cwd();
2258
2376
  await ensureConfig(cwd);
2259
2377
  const [config, context, projectInfo] = await Promise.all([
2260
- (0, import_agent_sdk4.loadConfig)(cwd),
2261
- (0, import_agent_sdk4.loadContext)(cwd),
2262
- (0, import_agent_sdk4.detectProject)(cwd)
2378
+ (0, import_agent_sdk5.loadConfig)(cwd),
2379
+ (0, import_agent_sdk5.loadContext)(cwd),
2380
+ (0, import_agent_sdk5.detectProject)(cwd)
2263
2381
  ]);
2264
2382
  if (args.model !== void 0) {
2265
2383
  config.provider.model = args.model;
@@ -2267,7 +2385,7 @@ async function startCli() {
2267
2385
  if (args.language !== void 0) {
2268
2386
  config.language = args.language;
2269
2387
  }
2270
- const sessionStore = new import_agent_sdk4.SessionStore();
2388
+ const sessionStore = new import_agent_sdk5.SessionStore();
2271
2389
  if (args.printMode) {
2272
2390
  const prompt = args.positional.join(" ").trim();
2273
2391
  if (prompt.length === 0) {
@@ -2275,15 +2393,15 @@ async function startCli() {
2275
2393
  process.exit(1);
2276
2394
  }
2277
2395
  const terminal = new PrintTerminal();
2278
- const paths = (0, import_agent_sdk4.projectPaths)(cwd);
2279
- const session = (0, import_agent_sdk4.createSession)({
2396
+ const paths = (0, import_agent_sdk5.projectPaths)(cwd);
2397
+ const session = (0, import_agent_sdk5.createSession)({
2280
2398
  config,
2281
2399
  context,
2282
2400
  terminal,
2283
- sessionLogger: new import_agent_sdk4.FileSessionLogger(paths.logs),
2401
+ sessionLogger: new import_agent_sdk5.FileSessionLogger(paths.logs),
2284
2402
  projectInfo,
2285
2403
  permissionMode: args.permissionMode,
2286
- promptForApproval: import_agent_sdk5.promptForApproval
2404
+ promptForApproval: import_agent_sdk6.promptForApproval
2287
2405
  });
2288
2406
  const response = await session.run(prompt);
2289
2407
  process.stdout.write(response + "\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-2CGAQADC.js";
4
+ } from "./chunk-EPCRZIQ6.js";
5
5
 
6
6
  // src/bin.ts
7
7
  process.on("uncaughtException", (err) => {
@@ -149,7 +149,7 @@ var PrintTerminal = class {
149
149
  import { render } from "ink";
150
150
 
151
151
  // src/ui/App.tsx
152
- import { useState as useState7, useRef as useRef5 } from "react";
152
+ import { useState as useState7, useRef as useRef6 } from "react";
153
153
  import { Box as Box9, Text as Text11, useApp, useInput as useInput5 } from "ink";
154
154
  import { getModelName } from "@robota-sdk/agent-core";
155
155
 
@@ -646,6 +646,11 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
646
646
 
647
647
  // src/ui/hooks/useSubmitHandler.ts
648
648
  import { useCallback as useCallback4 } from "react";
649
+ import {
650
+ createSubagentSession,
651
+ getBuiltInAgent,
652
+ retrieveAgentToolDeps
653
+ } from "@robota-sdk/agent-sdk";
649
654
 
650
655
  // src/utils/tool-call-extractor.ts
651
656
  var TOOL_ARG_MAX_LENGTH = 80;
@@ -750,6 +755,40 @@ Execute the "${cmd}" skill: ${userInstruction}`;
750
755
  return `Use the "${cmd}" skill: ${userInstruction}`;
751
756
  }
752
757
 
758
+ // src/commands/skill-executor.ts
759
+ function buildProcessedContent(skill, args, context) {
760
+ if (!skill.skillContent) return null;
761
+ return substituteVariables(skill.skillContent, args, context);
762
+ }
763
+ function buildInjectPrompt(skill, args, context) {
764
+ const processed = buildProcessedContent(skill, args, context);
765
+ if (processed) {
766
+ const userInstruction = args || skill.description;
767
+ return `<skill name="${skill.name}">
768
+ ${processed}
769
+ </skill>
770
+
771
+ Execute the "${skill.name}" skill: ${userInstruction}`;
772
+ }
773
+ return `Use the "${skill.name}" skill: ${args || skill.description}`;
774
+ }
775
+ async function executeSkill(skill, args, callbacks, context) {
776
+ if (skill.context === "fork") {
777
+ if (!callbacks.runInFork) {
778
+ throw new Error("Fork execution is not available. Agent tool deps may not be initialized.");
779
+ }
780
+ const content = buildProcessedContent(skill, args, context);
781
+ const prompt2 = content ?? `Use the "${skill.name}" skill: ${args || skill.description}`;
782
+ const options = {};
783
+ if (skill.agent) options.agent = skill.agent;
784
+ if (skill.allowedTools) options.allowedTools = skill.allowedTools;
785
+ const result = await callbacks.runInFork(prompt2, options);
786
+ return { mode: "fork", result };
787
+ }
788
+ const prompt = buildInjectPrompt(skill, args, context);
789
+ return { mode: "inject", prompt };
790
+ }
791
+
753
792
  // src/ui/hooks/useSubmitHandler.ts
754
793
  function syncContextState(session, setter) {
755
794
  const ctx = session.getContextState();
@@ -788,6 +827,37 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
788
827
  setIsThinking(false);
789
828
  }
790
829
  }
830
+ function createForkRunner(sessionKey) {
831
+ const deps = retrieveAgentToolDeps(sessionKey);
832
+ if (!deps) return void 0;
833
+ return async (content, options) => {
834
+ const agentType = options.agent ?? "general-purpose";
835
+ const agentDef = getBuiltInAgent(agentType) ?? deps.customAgentRegistry?.(agentType);
836
+ if (!agentDef) {
837
+ throw new Error(`Unknown agent type for fork execution: ${agentType}`);
838
+ }
839
+ const effectiveDef = options.allowedTools ? { ...agentDef, tools: options.allowedTools } : agentDef;
840
+ const subSession = createSubagentSession({
841
+ agentDefinition: effectiveDef,
842
+ parentConfig: deps.config,
843
+ parentContext: deps.context,
844
+ parentTools: deps.tools,
845
+ terminal: deps.terminal,
846
+ isForkWorker: true,
847
+ permissionHandler: deps.permissionHandler,
848
+ onTextDelta: deps.onTextDelta,
849
+ onToolExecution: deps.onToolExecution
850
+ });
851
+ return await subSession.run(content);
852
+ };
853
+ }
854
+ function findSkillCommand(input, registry) {
855
+ const parts = input.slice(1).split(/\s+/);
856
+ const cmd = parts[0]?.toLowerCase() ?? "";
857
+ const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
858
+ if (!skillCmd) return null;
859
+ return { skill: skillCmd, args: parts.slice(1).join(" ").trim() };
860
+ }
791
861
  function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry) {
792
862
  return useCallback4(
793
863
  async (input) => {
@@ -797,6 +867,33 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
797
867
  syncContextState(session, setContextState);
798
868
  return;
799
869
  }
870
+ const found = findSkillCommand(input, registry);
871
+ if (!found) return;
872
+ const { skill, args } = found;
873
+ if (skill.context === "fork") {
874
+ const runInFork = createForkRunner(session);
875
+ const result = await executeSkill(skill, args, { runInFork });
876
+ if (result.mode === "fork") {
877
+ addMessage({ role: "assistant", content: result.result ?? "(empty response)" });
878
+ syncContextState(session, setContextState);
879
+ return;
880
+ }
881
+ if (result.prompt) {
882
+ const cmdName2 = input.slice(1).split(/\s+/)[0]?.toLowerCase() ?? "";
883
+ const qualifiedName2 = registry.resolveQualifiedName(cmdName2);
884
+ const hookInput2 = qualifiedName2 ? `/${qualifiedName2}${input.slice(1 + cmdName2.length)}` : input;
885
+ return runSessionPrompt(
886
+ result.prompt,
887
+ session,
888
+ addMessage,
889
+ clearStreamingText,
890
+ setIsThinking,
891
+ setContextState,
892
+ hookInput2
893
+ );
894
+ }
895
+ return;
896
+ }
800
897
  const prompt = await buildSkillPrompt(input, registry);
801
898
  if (!prompt) return;
802
899
  const cmdName = input.slice(1).split(/\s+/)[0]?.toLowerCase() ?? "";
@@ -1478,7 +1575,7 @@ function StatusBar({
1478
1575
  }
1479
1576
 
1480
1577
  // src/ui/InputArea.tsx
1481
- import React5, { useState as useState5, useCallback as useCallback5, useMemo as useMemo2 } from "react";
1578
+ import React5, { useState as useState5, useCallback as useCallback5, useMemo as useMemo2, useRef as useRef4 } from "react";
1482
1579
  import { Box as Box5, Text as Text7, useInput as useInput2 } from "ink";
1483
1580
 
1484
1581
  // src/ui/CjkTextInput.tsx
@@ -1498,6 +1595,7 @@ function CjkTextInput({
1498
1595
  value,
1499
1596
  onChange,
1500
1597
  onSubmit,
1598
+ onPaste,
1501
1599
  placeholder = "",
1502
1600
  focus = true,
1503
1601
  showCursor = true
@@ -1521,6 +1619,10 @@ function CjkTextInput({
1521
1619
  onSubmit?.(valueRef.current);
1522
1620
  return;
1523
1621
  }
1622
+ if (input.length > 1 && (input.includes("\n") || input.includes("\r")) && onPaste) {
1623
+ onPaste(input.replace(/\r\n?/g, "\n"));
1624
+ return;
1625
+ }
1524
1626
  if (key.leftArrow) {
1525
1627
  if (cursorRef.current > 0) {
1526
1628
  cursorRef.current -= 1;
@@ -1643,6 +1745,12 @@ function computeScrollOffset(selectedIndex, total) {
1643
1745
  return Math.min(selectedIndex - MAX_VISIBLE + 1, maxOffset);
1644
1746
  }
1645
1747
 
1748
+ // src/utils/paste-labels.ts
1749
+ var PASTE_LABEL_RE = /\[Pasted text #(\d+) \+\d+ lines\]/g;
1750
+ function expandPasteLabels(text, store) {
1751
+ return text.replace(PASTE_LABEL_RE, (_, id) => store.get(Number(id)) ?? "");
1752
+ }
1753
+
1646
1754
  // src/ui/InputArea.tsx
1647
1755
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1648
1756
  function parseSlashInput(value) {
@@ -1699,6 +1807,8 @@ function useAutocomplete(value, registry) {
1699
1807
  }
1700
1808
  function InputArea({ onSubmit, isDisabled, registry }) {
1701
1809
  const [value, setValue] = useState5("");
1810
+ const pasteStore = useRef4(/* @__PURE__ */ new Map());
1811
+ const pasteIdRef = useRef4(0);
1702
1812
  const {
1703
1813
  showPopup,
1704
1814
  filteredCommands,
@@ -1707,6 +1817,14 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1707
1817
  isSubcommandMode,
1708
1818
  setShowPopup
1709
1819
  } = useAutocomplete(value, registry);
1820
+ const handlePaste = useCallback5((text) => {
1821
+ pasteIdRef.current += 1;
1822
+ const id = pasteIdRef.current;
1823
+ pasteStore.current.set(id, text);
1824
+ const lineCount = text.split("\n").length;
1825
+ const label = `[Pasted text #${id} +${lineCount} lines]`;
1826
+ setValue((prev) => prev ? `${prev} ${label}` : label);
1827
+ }, []);
1710
1828
  const handleSubmit = useCallback5(
1711
1829
  (text) => {
1712
1830
  const trimmed = text.trim();
@@ -1715,8 +1833,11 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1715
1833
  selectCommand(filteredCommands[selectedIndex]);
1716
1834
  return;
1717
1835
  }
1836
+ const expanded = expandPasteLabels(trimmed, pasteStore.current);
1718
1837
  setValue("");
1719
- onSubmit(trimmed);
1838
+ pasteStore.current.clear();
1839
+ pasteIdRef.current = 0;
1840
+ onSubmit(expanded);
1720
1841
  },
1721
1842
  [showPopup, filteredCommands, selectedIndex, onSubmit]
1722
1843
  );
@@ -1773,6 +1894,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1773
1894
  value,
1774
1895
  onChange: setValue,
1775
1896
  onSubmit: handleSubmit,
1897
+ onPaste: handlePaste,
1776
1898
  placeholder: "Type a message or /help"
1777
1899
  }
1778
1900
  )
@@ -1781,7 +1903,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1781
1903
  }
1782
1904
 
1783
1905
  // src/ui/ConfirmPrompt.tsx
1784
- import { useState as useState6, useCallback as useCallback6, useRef as useRef4 } from "react";
1906
+ import { useState as useState6, useCallback as useCallback6, useRef as useRef5 } from "react";
1785
1907
  import { Box as Box6, Text as Text8, useInput as useInput3 } from "ink";
1786
1908
  import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1787
1909
  function ConfirmPrompt({
@@ -1790,7 +1912,7 @@ function ConfirmPrompt({
1790
1912
  onSelect
1791
1913
  }) {
1792
1914
  const [selected, setSelected] = useState6(0);
1793
- const resolvedRef = useRef4(false);
1915
+ const resolvedRef = useRef5(false);
1794
1916
  const doSelect = useCallback6(
1795
1917
  (index) => {
1796
1918
  if (resolvedRef.current) return;
@@ -1970,7 +2092,7 @@ function App(props) {
1970
2092
  usedTokens: initialCtx.usedTokens,
1971
2093
  maxTokens: initialCtx.maxTokens
1972
2094
  });
1973
- const pendingModelChangeRef = useRef5(null);
2095
+ const pendingModelChangeRef = useRef6(null);
1974
2096
  const [pendingModelId, setPendingModelId] = useState7(null);
1975
2097
  const pluginCallbacks = usePluginCallbacks(props.cwd ?? process.cwd());
1976
2098
  const handleSlashCommand = useSlashCommands(
@@ -30,21 +30,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- Session: () => import_agent_sdk6.Session,
34
- SessionStore: () => import_agent_sdk6.SessionStore,
35
- TRUST_TO_MODE: () => import_agent_sdk6.TRUST_TO_MODE,
36
- query: () => import_agent_sdk6.query,
33
+ Session: () => import_agent_sdk7.Session,
34
+ SessionStore: () => import_agent_sdk7.SessionStore,
35
+ TRUST_TO_MODE: () => import_agent_sdk7.TRUST_TO_MODE,
36
+ query: () => import_agent_sdk7.query,
37
37
  startCli: () => startCli
38
38
  });
39
39
  module.exports = __toCommonJS(index_exports);
40
- var import_agent_sdk6 = require("@robota-sdk/agent-sdk");
40
+ var import_agent_sdk7 = require("@robota-sdk/agent-sdk");
41
41
 
42
42
  // src/cli.ts
43
43
  var import_node_fs3 = require("fs");
44
44
  var import_node_path5 = require("path");
45
45
  var import_node_url = require("url");
46
- var import_agent_sdk4 = require("@robota-sdk/agent-sdk");
47
46
  var import_agent_sdk5 = require("@robota-sdk/agent-sdk");
47
+ var import_agent_sdk6 = require("@robota-sdk/agent-sdk");
48
48
 
49
49
  // src/utils/cli-args.ts
50
50
  var import_node_util = require("util");
@@ -679,6 +679,7 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
679
679
 
680
680
  // src/ui/hooks/useSubmitHandler.ts
681
681
  var import_react4 = require("react");
682
+ var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
682
683
 
683
684
  // src/utils/tool-call-extractor.ts
684
685
  var TOOL_ARG_MAX_LENGTH = 80;
@@ -783,6 +784,40 @@ Execute the "${cmd}" skill: ${userInstruction}`;
783
784
  return `Use the "${cmd}" skill: ${userInstruction}`;
784
785
  }
785
786
 
787
+ // src/commands/skill-executor.ts
788
+ function buildProcessedContent(skill, args, context) {
789
+ if (!skill.skillContent) return null;
790
+ return substituteVariables(skill.skillContent, args, context);
791
+ }
792
+ function buildInjectPrompt(skill, args, context) {
793
+ const processed = buildProcessedContent(skill, args, context);
794
+ if (processed) {
795
+ const userInstruction = args || skill.description;
796
+ return `<skill name="${skill.name}">
797
+ ${processed}
798
+ </skill>
799
+
800
+ Execute the "${skill.name}" skill: ${userInstruction}`;
801
+ }
802
+ return `Use the "${skill.name}" skill: ${args || skill.description}`;
803
+ }
804
+ async function executeSkill(skill, args, callbacks, context) {
805
+ if (skill.context === "fork") {
806
+ if (!callbacks.runInFork) {
807
+ throw new Error("Fork execution is not available. Agent tool deps may not be initialized.");
808
+ }
809
+ const content = buildProcessedContent(skill, args, context);
810
+ const prompt2 = content ?? `Use the "${skill.name}" skill: ${args || skill.description}`;
811
+ const options = {};
812
+ if (skill.agent) options.agent = skill.agent;
813
+ if (skill.allowedTools) options.allowedTools = skill.allowedTools;
814
+ const result = await callbacks.runInFork(prompt2, options);
815
+ return { mode: "fork", result };
816
+ }
817
+ const prompt = buildInjectPrompt(skill, args, context);
818
+ return { mode: "inject", prompt };
819
+ }
820
+
786
821
  // src/ui/hooks/useSubmitHandler.ts
787
822
  function syncContextState(session, setter) {
788
823
  const ctx = session.getContextState();
@@ -821,6 +856,37 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
821
856
  setIsThinking(false);
822
857
  }
823
858
  }
859
+ function createForkRunner(sessionKey) {
860
+ const deps = (0, import_agent_sdk2.retrieveAgentToolDeps)(sessionKey);
861
+ if (!deps) return void 0;
862
+ return async (content, options) => {
863
+ const agentType = options.agent ?? "general-purpose";
864
+ const agentDef = (0, import_agent_sdk2.getBuiltInAgent)(agentType) ?? deps.customAgentRegistry?.(agentType);
865
+ if (!agentDef) {
866
+ throw new Error(`Unknown agent type for fork execution: ${agentType}`);
867
+ }
868
+ const effectiveDef = options.allowedTools ? { ...agentDef, tools: options.allowedTools } : agentDef;
869
+ const subSession = (0, import_agent_sdk2.createSubagentSession)({
870
+ agentDefinition: effectiveDef,
871
+ parentConfig: deps.config,
872
+ parentContext: deps.context,
873
+ parentTools: deps.tools,
874
+ terminal: deps.terminal,
875
+ isForkWorker: true,
876
+ permissionHandler: deps.permissionHandler,
877
+ onTextDelta: deps.onTextDelta,
878
+ onToolExecution: deps.onToolExecution
879
+ });
880
+ return await subSession.run(content);
881
+ };
882
+ }
883
+ function findSkillCommand(input, registry) {
884
+ const parts = input.slice(1).split(/\s+/);
885
+ const cmd = parts[0]?.toLowerCase() ?? "";
886
+ const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
887
+ if (!skillCmd) return null;
888
+ return { skill: skillCmd, args: parts.slice(1).join(" ").trim() };
889
+ }
824
890
  function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry) {
825
891
  return (0, import_react4.useCallback)(
826
892
  async (input) => {
@@ -830,6 +896,33 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
830
896
  syncContextState(session, setContextState);
831
897
  return;
832
898
  }
899
+ const found = findSkillCommand(input, registry);
900
+ if (!found) return;
901
+ const { skill, args } = found;
902
+ if (skill.context === "fork") {
903
+ const runInFork = createForkRunner(session);
904
+ const result = await executeSkill(skill, args, { runInFork });
905
+ if (result.mode === "fork") {
906
+ addMessage({ role: "assistant", content: result.result ?? "(empty response)" });
907
+ syncContextState(session, setContextState);
908
+ return;
909
+ }
910
+ if (result.prompt) {
911
+ const cmdName2 = input.slice(1).split(/\s+/)[0]?.toLowerCase() ?? "";
912
+ const qualifiedName2 = registry.resolveQualifiedName(cmdName2);
913
+ const hookInput2 = qualifiedName2 ? `/${qualifiedName2}${input.slice(1 + cmdName2.length)}` : input;
914
+ return runSessionPrompt(
915
+ result.prompt,
916
+ session,
917
+ addMessage,
918
+ clearStreamingText,
919
+ setIsThinking,
920
+ setContextState,
921
+ hookInput2
922
+ );
923
+ }
924
+ return;
925
+ }
833
926
  const prompt = await buildSkillPrompt(input, registry);
834
927
  if (!prompt) return;
835
928
  const cmdName = input.slice(1).split(/\s+/)[0]?.toLowerCase() ?? "";
@@ -871,7 +964,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
871
964
  var import_react5 = require("react");
872
965
  var import_node_os2 = require("os");
873
966
  var import_node_path3 = require("path");
874
- var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
967
+ var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
875
968
 
876
969
  // src/commands/command-registry.ts
877
970
  var CommandRegistry = class {
@@ -1210,7 +1303,7 @@ function useCommandRegistry(cwd) {
1210
1303
  registry.addSource(new SkillCommandSource(cwd));
1211
1304
  let pluginHooks = {};
1212
1305
  const pluginsDir = (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".robota", "plugins");
1213
- const loader = new import_agent_sdk2.BundlePluginLoader(pluginsDir);
1306
+ const loader = new import_agent_sdk3.BundlePluginLoader(pluginsDir);
1214
1307
  try {
1215
1308
  const plugins = loader.loadPluginsSync();
1216
1309
  if (plugins.length > 0) {
@@ -1228,20 +1321,20 @@ function useCommandRegistry(cwd) {
1228
1321
  var import_react6 = require("react");
1229
1322
  var import_node_os3 = require("os");
1230
1323
  var import_node_path4 = require("path");
1231
- var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
1324
+ var import_agent_sdk4 = require("@robota-sdk/agent-sdk");
1232
1325
  function usePluginCallbacks(cwd) {
1233
1326
  return (0, import_react6.useMemo)(() => {
1234
1327
  const home = (0, import_node_os3.homedir)();
1235
1328
  const pluginsDir = (0, import_node_path4.join)(home, ".robota", "plugins");
1236
1329
  const userSettingsPath = (0, import_node_path4.join)(home, ".robota", "settings.json");
1237
- const settingsStore = new import_agent_sdk3.PluginSettingsStore(userSettingsPath);
1238
- const marketplace = new import_agent_sdk3.MarketplaceClient({ pluginsDir });
1239
- const installer = new import_agent_sdk3.BundlePluginInstaller({
1330
+ const settingsStore = new import_agent_sdk4.PluginSettingsStore(userSettingsPath);
1331
+ const marketplace = new import_agent_sdk4.MarketplaceClient({ pluginsDir });
1332
+ const installer = new import_agent_sdk4.BundlePluginInstaller({
1240
1333
  pluginsDir,
1241
1334
  settingsStore,
1242
1335
  marketplaceClient: marketplace
1243
1336
  });
1244
- const loader = new import_agent_sdk3.BundlePluginLoader(pluginsDir);
1337
+ const loader = new import_agent_sdk4.BundlePluginLoader(pluginsDir);
1245
1338
  return {
1246
1339
  listInstalled: async () => {
1247
1340
  const plugins = await loader.loadAll();
@@ -1526,6 +1619,7 @@ function CjkTextInput({
1526
1619
  value,
1527
1620
  onChange,
1528
1621
  onSubmit,
1622
+ onPaste,
1529
1623
  placeholder = "",
1530
1624
  focus = true,
1531
1625
  showCursor = true
@@ -1549,6 +1643,10 @@ function CjkTextInput({
1549
1643
  onSubmit?.(valueRef.current);
1550
1644
  return;
1551
1645
  }
1646
+ if (input.length > 1 && (input.includes("\n") || input.includes("\r")) && onPaste) {
1647
+ onPaste(input.replace(/\r\n?/g, "\n"));
1648
+ return;
1649
+ }
1552
1650
  if (key.leftArrow) {
1553
1651
  if (cursorRef.current > 0) {
1554
1652
  cursorRef.current -= 1;
@@ -1671,6 +1769,12 @@ function computeScrollOffset(selectedIndex, total) {
1671
1769
  return Math.min(selectedIndex - MAX_VISIBLE + 1, maxOffset);
1672
1770
  }
1673
1771
 
1772
+ // src/utils/paste-labels.ts
1773
+ var PASTE_LABEL_RE = /\[Pasted text #(\d+) \+\d+ lines\]/g;
1774
+ function expandPasteLabels(text, store) {
1775
+ return text.replace(PASTE_LABEL_RE, (_, id) => store.get(Number(id)) ?? "");
1776
+ }
1777
+
1674
1778
  // src/ui/InputArea.tsx
1675
1779
  var import_jsx_runtime7 = require("react/jsx-runtime");
1676
1780
  function parseSlashInput(value) {
@@ -1727,6 +1831,8 @@ function useAutocomplete(value, registry) {
1727
1831
  }
1728
1832
  function InputArea({ onSubmit, isDisabled, registry }) {
1729
1833
  const [value, setValue] = (0, import_react10.useState)("");
1834
+ const pasteStore = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
1835
+ const pasteIdRef = (0, import_react10.useRef)(0);
1730
1836
  const {
1731
1837
  showPopup,
1732
1838
  filteredCommands,
@@ -1735,6 +1841,14 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1735
1841
  isSubcommandMode,
1736
1842
  setShowPopup
1737
1843
  } = useAutocomplete(value, registry);
1844
+ const handlePaste = (0, import_react10.useCallback)((text) => {
1845
+ pasteIdRef.current += 1;
1846
+ const id = pasteIdRef.current;
1847
+ pasteStore.current.set(id, text);
1848
+ const lineCount = text.split("\n").length;
1849
+ const label = `[Pasted text #${id} +${lineCount} lines]`;
1850
+ setValue((prev) => prev ? `${prev} ${label}` : label);
1851
+ }, []);
1738
1852
  const handleSubmit = (0, import_react10.useCallback)(
1739
1853
  (text) => {
1740
1854
  const trimmed = text.trim();
@@ -1743,8 +1857,11 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1743
1857
  selectCommand(filteredCommands[selectedIndex]);
1744
1858
  return;
1745
1859
  }
1860
+ const expanded = expandPasteLabels(trimmed, pasteStore.current);
1746
1861
  setValue("");
1747
- onSubmit(trimmed);
1862
+ pasteStore.current.clear();
1863
+ pasteIdRef.current = 0;
1864
+ onSubmit(expanded);
1748
1865
  },
1749
1866
  [showPopup, filteredCommands, selectedIndex, onSubmit]
1750
1867
  );
@@ -1801,6 +1918,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1801
1918
  value,
1802
1919
  onChange: setValue,
1803
1920
  onSubmit: handleSubmit,
1921
+ onPaste: handlePaste,
1804
1922
  placeholder: "Type a message or /help"
1805
1923
  }
1806
1924
  )
@@ -2273,9 +2391,9 @@ async function startCli() {
2273
2391
  const cwd = process.cwd();
2274
2392
  await ensureConfig(cwd);
2275
2393
  const [config, context, projectInfo] = await Promise.all([
2276
- (0, import_agent_sdk4.loadConfig)(cwd),
2277
- (0, import_agent_sdk4.loadContext)(cwd),
2278
- (0, import_agent_sdk4.detectProject)(cwd)
2394
+ (0, import_agent_sdk5.loadConfig)(cwd),
2395
+ (0, import_agent_sdk5.loadContext)(cwd),
2396
+ (0, import_agent_sdk5.detectProject)(cwd)
2279
2397
  ]);
2280
2398
  if (args.model !== void 0) {
2281
2399
  config.provider.model = args.model;
@@ -2283,7 +2401,7 @@ async function startCli() {
2283
2401
  if (args.language !== void 0) {
2284
2402
  config.language = args.language;
2285
2403
  }
2286
- const sessionStore = new import_agent_sdk4.SessionStore();
2404
+ const sessionStore = new import_agent_sdk5.SessionStore();
2287
2405
  if (args.printMode) {
2288
2406
  const prompt = args.positional.join(" ").trim();
2289
2407
  if (prompt.length === 0) {
@@ -2291,15 +2409,15 @@ async function startCli() {
2291
2409
  process.exit(1);
2292
2410
  }
2293
2411
  const terminal = new PrintTerminal();
2294
- const paths = (0, import_agent_sdk4.projectPaths)(cwd);
2295
- const session = (0, import_agent_sdk4.createSession)({
2412
+ const paths = (0, import_agent_sdk5.projectPaths)(cwd);
2413
+ const session = (0, import_agent_sdk5.createSession)({
2296
2414
  config,
2297
2415
  context,
2298
2416
  terminal,
2299
- sessionLogger: new import_agent_sdk4.FileSessionLogger(paths.logs),
2417
+ sessionLogger: new import_agent_sdk5.FileSessionLogger(paths.logs),
2300
2418
  projectInfo,
2301
2419
  permissionMode: args.permissionMode,
2302
- promptForApproval: import_agent_sdk5.promptForApproval
2420
+ promptForApproval: import_agent_sdk6.promptForApproval
2303
2421
  });
2304
2422
  const response = await session.run(prompt);
2305
2423
  process.stdout.write(response + "\n");
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  startCli
3
- } from "./chunk-2CGAQADC.js";
3
+ } from "./chunk-EPCRZIQ6.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.31",
3
+ "version": "3.0.0-beta.33",
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.31",
39
- "@robota-sdk/agent-sdk": "3.0.0-beta.31"
38
+ "@robota-sdk/agent-core": "3.0.0-beta.33",
39
+ "@robota-sdk/agent-sdk": "3.0.0-beta.33"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/marked": "^6.0.0",