@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 +36 -11
- package/dist/node/bin.cjs +135 -17
- package/dist/node/bin.js +1 -1
- package/dist/node/{chunk-2CGAQADC.js → chunk-EPCRZIQ6.js} +128 -6
- package/dist/node/index.cjs +140 -22
- package/dist/node/index.js +1 -1
- package/package.json +3 -3
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
|
|
181
|
-
|
|
|
182
|
-
| `/help`
|
|
183
|
-
| `/clear`
|
|
184
|
-
| `/mode [mode]`
|
|
185
|
-
| `/model [model]
|
|
186
|
-
| `/compact [instructions]` | Compress context window
|
|
187
|
-
| `/cost`
|
|
188
|
-
| `/context`
|
|
189
|
-
| `/permissions`
|
|
190
|
-
| `/exit`
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1222
|
-
const marketplace = new
|
|
1223
|
-
const installer = new
|
|
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
|
|
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
|
-
|
|
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,
|
|
2261
|
-
(0,
|
|
2262
|
-
(0,
|
|
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
|
|
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,
|
|
2279
|
-
const session = (0,
|
|
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
|
|
2401
|
+
sessionLogger: new import_agent_sdk5.FileSessionLogger(paths.logs),
|
|
2284
2402
|
projectInfo,
|
|
2285
2403
|
permissionMode: args.permissionMode,
|
|
2286
|
-
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
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
package/dist/node/index.cjs
CHANGED
|
@@ -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: () =>
|
|
34
|
-
SessionStore: () =>
|
|
35
|
-
TRUST_TO_MODE: () =>
|
|
36
|
-
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1238
|
-
const marketplace = new
|
|
1239
|
-
const installer = new
|
|
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
|
|
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
|
-
|
|
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,
|
|
2277
|
-
(0,
|
|
2278
|
-
(0,
|
|
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
|
|
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,
|
|
2295
|
-
const session = (0,
|
|
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
|
|
2417
|
+
sessionLogger: new import_agent_sdk5.FileSessionLogger(paths.logs),
|
|
2300
2418
|
projectInfo,
|
|
2301
2419
|
permissionMode: args.permissionMode,
|
|
2302
|
-
promptForApproval:
|
|
2420
|
+
promptForApproval: import_agent_sdk6.promptForApproval
|
|
2303
2421
|
});
|
|
2304
2422
|
const response = await session.run(prompt);
|
|
2305
2423
|
process.stdout.write(response + "\n");
|
package/dist/node/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robota-sdk/agent-cli",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
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.
|
|
39
|
-
"@robota-sdk/agent-sdk": "3.0.0-beta.
|
|
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",
|