@robota-sdk/agent-cli 3.0.0-beta.15 → 3.0.0-beta.17
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 +10 -3
- package/dist/node/bin.cjs +467 -315
- package/dist/node/bin.js +1 -1
- package/dist/node/{chunk-B423R5N3.js → chunk-4WB3L3RU.js} +465 -313
- package/dist/node/index.cjs +467 -315
- package/dist/node/index.js +1 -1
- package/package.json +4 -3
package/dist/node/bin.cjs
CHANGED
|
@@ -24,21 +24,275 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/cli.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
var import_node_path2 = require("path");
|
|
27
|
+
var import_node_fs3 = require("fs");
|
|
28
|
+
var import_node_path3 = require("path");
|
|
30
29
|
var import_node_url = require("url");
|
|
31
|
-
var readline = __toESM(require("readline"), 1);
|
|
32
30
|
var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
|
|
33
31
|
var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
|
|
34
32
|
|
|
33
|
+
// src/utils/cli-args.ts
|
|
34
|
+
var import_node_util = require("util");
|
|
35
|
+
var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
36
|
+
function parsePermissionMode(raw) {
|
|
37
|
+
if (raw === void 0) return void 0;
|
|
38
|
+
if (!VALID_MODES.includes(raw)) {
|
|
39
|
+
process.stderr.write(`Invalid --permission-mode "${raw}". Valid: ${VALID_MODES.join(" | ")}
|
|
40
|
+
`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
return raw;
|
|
44
|
+
}
|
|
45
|
+
function parseMaxTurns(raw) {
|
|
46
|
+
if (raw === void 0) return void 0;
|
|
47
|
+
const n = parseInt(raw, 10);
|
|
48
|
+
if (isNaN(n) || n <= 0) {
|
|
49
|
+
process.stderr.write(`Invalid --max-turns "${raw}". Must be a positive integer.
|
|
50
|
+
`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
return n;
|
|
54
|
+
}
|
|
55
|
+
function parseCliArgs() {
|
|
56
|
+
const { values, positionals } = (0, import_node_util.parseArgs)({
|
|
57
|
+
allowPositionals: true,
|
|
58
|
+
options: {
|
|
59
|
+
p: { type: "boolean", short: "p", default: false },
|
|
60
|
+
c: { type: "boolean", short: "c", default: false },
|
|
61
|
+
r: { type: "string", short: "r" },
|
|
62
|
+
model: { type: "string" },
|
|
63
|
+
"permission-mode": { type: "string" },
|
|
64
|
+
"max-turns": { type: "string" },
|
|
65
|
+
version: { type: "boolean", default: false },
|
|
66
|
+
reset: { type: "boolean", default: false }
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
positional: positionals,
|
|
71
|
+
printMode: values["p"] ?? false,
|
|
72
|
+
continueMode: values["c"] ?? false,
|
|
73
|
+
resumeId: values["r"],
|
|
74
|
+
model: values["model"],
|
|
75
|
+
permissionMode: parsePermissionMode(values["permission-mode"]),
|
|
76
|
+
maxTurns: parseMaxTurns(values["max-turns"]),
|
|
77
|
+
version: values["version"] ?? false,
|
|
78
|
+
reset: values["reset"] ?? false
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/utils/settings-io.ts
|
|
83
|
+
var import_node_fs = require("fs");
|
|
84
|
+
var import_node_path = require("path");
|
|
85
|
+
function getUserSettingsPath() {
|
|
86
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
|
|
87
|
+
return (0, import_node_path.join)(home, ".robota", "settings.json");
|
|
88
|
+
}
|
|
89
|
+
function readSettings(path) {
|
|
90
|
+
if (!(0, import_node_fs.existsSync)(path)) return {};
|
|
91
|
+
return JSON.parse((0, import_node_fs.readFileSync)(path, "utf8"));
|
|
92
|
+
}
|
|
93
|
+
function writeSettings(path, settings) {
|
|
94
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path), { recursive: true });
|
|
95
|
+
(0, import_node_fs.writeFileSync)(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
96
|
+
}
|
|
97
|
+
function updateModelInSettings(settingsPath, modelId) {
|
|
98
|
+
const settings = readSettings(settingsPath);
|
|
99
|
+
const provider = settings.provider ?? {};
|
|
100
|
+
provider.model = modelId;
|
|
101
|
+
settings.provider = provider;
|
|
102
|
+
writeSettings(settingsPath, settings);
|
|
103
|
+
}
|
|
104
|
+
function deleteSettings(path) {
|
|
105
|
+
if ((0, import_node_fs.existsSync)(path)) {
|
|
106
|
+
(0, import_node_fs.unlinkSync)(path);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/print-terminal.ts
|
|
113
|
+
var readline = __toESM(require("readline"), 1);
|
|
114
|
+
var PrintTerminal = class {
|
|
115
|
+
write(text) {
|
|
116
|
+
process.stdout.write(text);
|
|
117
|
+
}
|
|
118
|
+
writeLine(text) {
|
|
119
|
+
process.stdout.write(text + "\n");
|
|
120
|
+
}
|
|
121
|
+
writeMarkdown(md) {
|
|
122
|
+
process.stdout.write(md);
|
|
123
|
+
}
|
|
124
|
+
writeError(text) {
|
|
125
|
+
process.stderr.write(text + "\n");
|
|
126
|
+
}
|
|
127
|
+
prompt(question) {
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
const rl = readline.createInterface({
|
|
130
|
+
input: process.stdin,
|
|
131
|
+
output: process.stdout,
|
|
132
|
+
terminal: false,
|
|
133
|
+
historySize: 0
|
|
134
|
+
});
|
|
135
|
+
rl.question(question, (answer) => {
|
|
136
|
+
rl.close();
|
|
137
|
+
resolve(answer);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async select(options, initialIndex = 0) {
|
|
142
|
+
for (let i = 0; i < options.length; i++) {
|
|
143
|
+
const marker = i === initialIndex ? ">" : " ";
|
|
144
|
+
process.stdout.write(` ${marker} ${i + 1}) ${options[i]}
|
|
145
|
+
`);
|
|
146
|
+
}
|
|
147
|
+
const answer = await this.prompt(
|
|
148
|
+
` Choose [1-${options.length}] (default: ${options[initialIndex]}): `
|
|
149
|
+
);
|
|
150
|
+
const trimmed = answer.trim().toLowerCase();
|
|
151
|
+
if (trimmed === "") return initialIndex;
|
|
152
|
+
const num = parseInt(trimmed, 10);
|
|
153
|
+
if (!isNaN(num) && num >= 1 && num <= options.length) return num - 1;
|
|
154
|
+
return initialIndex;
|
|
155
|
+
}
|
|
156
|
+
spinner(_message) {
|
|
157
|
+
return { stop() {
|
|
158
|
+
}, update() {
|
|
159
|
+
} };
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
35
163
|
// src/ui/render.tsx
|
|
164
|
+
var import_ink10 = require("ink");
|
|
165
|
+
|
|
166
|
+
// src/ui/App.tsx
|
|
167
|
+
var import_react6 = require("react");
|
|
36
168
|
var import_ink9 = require("ink");
|
|
37
169
|
|
|
170
|
+
// src/commands/slash-executor.ts
|
|
171
|
+
var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
172
|
+
var HELP_TEXT = [
|
|
173
|
+
"Available commands:",
|
|
174
|
+
" /help \u2014 Show this help",
|
|
175
|
+
" /clear \u2014 Clear conversation",
|
|
176
|
+
" /compact [instr] \u2014 Compact context (optional focus instructions)",
|
|
177
|
+
" /mode [m] \u2014 Show/change permission mode",
|
|
178
|
+
" /cost \u2014 Show session info",
|
|
179
|
+
" /reset \u2014 Delete settings and exit",
|
|
180
|
+
" /exit \u2014 Exit CLI"
|
|
181
|
+
].join("\n");
|
|
182
|
+
function handleHelp(addMessage) {
|
|
183
|
+
addMessage({ role: "system", content: HELP_TEXT });
|
|
184
|
+
return { handled: true };
|
|
185
|
+
}
|
|
186
|
+
function handleClear(addMessage, clearMessages, session) {
|
|
187
|
+
clearMessages();
|
|
188
|
+
session.clearHistory();
|
|
189
|
+
addMessage({ role: "system", content: "Conversation cleared." });
|
|
190
|
+
return { handled: true };
|
|
191
|
+
}
|
|
192
|
+
async function handleCompact(args, session, addMessage) {
|
|
193
|
+
const instructions = args.trim() || void 0;
|
|
194
|
+
const before = session.getContextState().usedPercentage;
|
|
195
|
+
addMessage({ role: "system", content: "Compacting context..." });
|
|
196
|
+
await session.compact(instructions);
|
|
197
|
+
const after = session.getContextState().usedPercentage;
|
|
198
|
+
addMessage({
|
|
199
|
+
role: "system",
|
|
200
|
+
content: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`
|
|
201
|
+
});
|
|
202
|
+
return { handled: true };
|
|
203
|
+
}
|
|
204
|
+
function handleMode(arg, session, addMessage) {
|
|
205
|
+
if (!arg) {
|
|
206
|
+
addMessage({ role: "system", content: `Current mode: ${session.getPermissionMode()}` });
|
|
207
|
+
} else if (VALID_MODES2.includes(arg)) {
|
|
208
|
+
session.setPermissionMode(arg);
|
|
209
|
+
addMessage({ role: "system", content: `Permission mode set to: ${arg}` });
|
|
210
|
+
} else {
|
|
211
|
+
addMessage({ role: "system", content: `Invalid mode. Valid: ${VALID_MODES2.join(" | ")}` });
|
|
212
|
+
}
|
|
213
|
+
return { handled: true };
|
|
214
|
+
}
|
|
215
|
+
function handleModel(modelId, addMessage) {
|
|
216
|
+
if (!modelId) {
|
|
217
|
+
addMessage({ role: "system", content: "Select a model from the /model submenu." });
|
|
218
|
+
return { handled: true };
|
|
219
|
+
}
|
|
220
|
+
return { handled: true, pendingModelId: modelId };
|
|
221
|
+
}
|
|
222
|
+
function handleCost(session, addMessage) {
|
|
223
|
+
addMessage({
|
|
224
|
+
role: "system",
|
|
225
|
+
content: `Session: ${session.getSessionId()}
|
|
226
|
+
Messages: ${session.getMessageCount()}`
|
|
227
|
+
});
|
|
228
|
+
return { handled: true };
|
|
229
|
+
}
|
|
230
|
+
function handlePermissions(session, addMessage) {
|
|
231
|
+
const mode = session.getPermissionMode();
|
|
232
|
+
const sessionAllowed = session.getSessionAllowedTools();
|
|
233
|
+
const lines = [`Permission mode: ${mode}`];
|
|
234
|
+
if (sessionAllowed.length > 0) {
|
|
235
|
+
lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
|
|
236
|
+
} else {
|
|
237
|
+
lines.push("No session-approved tools.");
|
|
238
|
+
}
|
|
239
|
+
addMessage({ role: "system", content: lines.join("\n") });
|
|
240
|
+
return { handled: true };
|
|
241
|
+
}
|
|
242
|
+
function handleContext(session, addMessage) {
|
|
243
|
+
const ctx = session.getContextState();
|
|
244
|
+
addMessage({
|
|
245
|
+
role: "system",
|
|
246
|
+
content: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`
|
|
247
|
+
});
|
|
248
|
+
return { handled: true };
|
|
249
|
+
}
|
|
250
|
+
function handleReset(addMessage) {
|
|
251
|
+
const settingsPath = getUserSettingsPath();
|
|
252
|
+
if (deleteSettings(settingsPath)) {
|
|
253
|
+
addMessage({ role: "system", content: `Deleted ${settingsPath}. Exiting...` });
|
|
254
|
+
} else {
|
|
255
|
+
addMessage({ role: "system", content: "No user settings found." });
|
|
256
|
+
}
|
|
257
|
+
return { handled: true, exitRequested: true };
|
|
258
|
+
}
|
|
259
|
+
async function executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry) {
|
|
260
|
+
switch (cmd) {
|
|
261
|
+
case "help":
|
|
262
|
+
return handleHelp(addMessage);
|
|
263
|
+
case "clear":
|
|
264
|
+
return handleClear(addMessage, clearMessages, session);
|
|
265
|
+
case "compact":
|
|
266
|
+
return handleCompact(args, session, addMessage);
|
|
267
|
+
case "mode":
|
|
268
|
+
return handleMode(args.split(/\s+/)[0] || void 0, session, addMessage);
|
|
269
|
+
case "model":
|
|
270
|
+
return handleModel(args.split(/\s+/)[0] || void 0, addMessage);
|
|
271
|
+
case "cost":
|
|
272
|
+
return handleCost(session, addMessage);
|
|
273
|
+
case "permissions":
|
|
274
|
+
return handlePermissions(session, addMessage);
|
|
275
|
+
case "context":
|
|
276
|
+
return handleContext(session, addMessage);
|
|
277
|
+
case "reset":
|
|
278
|
+
return handleReset(addMessage);
|
|
279
|
+
case "exit":
|
|
280
|
+
return { handled: true, exitRequested: true };
|
|
281
|
+
default: {
|
|
282
|
+
const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
|
|
283
|
+
if (skillCmd) {
|
|
284
|
+
addMessage({ role: "system", content: `Invoking skill: ${cmd}` });
|
|
285
|
+
return { handled: false };
|
|
286
|
+
}
|
|
287
|
+
addMessage({ role: "system", content: `Unknown command "/${cmd}". Type /help for help.` });
|
|
288
|
+
return { handled: true };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
38
293
|
// src/ui/App.tsx
|
|
39
|
-
var import_react5 = require("react");
|
|
40
|
-
var import_ink8 = require("ink");
|
|
41
294
|
var import_agent_sdk = require("@robota-sdk/agent-sdk");
|
|
295
|
+
var import_agent_core3 = require("@robota-sdk/agent-core");
|
|
42
296
|
|
|
43
297
|
// src/commands/command-registry.ts
|
|
44
298
|
var CommandRegistry = class {
|
|
@@ -71,6 +325,21 @@ var CommandRegistry = class {
|
|
|
71
325
|
};
|
|
72
326
|
|
|
73
327
|
// src/commands/builtin-source.ts
|
|
328
|
+
var import_agent_core = require("@robota-sdk/agent-core");
|
|
329
|
+
function buildModelSubcommands() {
|
|
330
|
+
const seen = /* @__PURE__ */ new Set();
|
|
331
|
+
const commands = [];
|
|
332
|
+
for (const model of Object.values(import_agent_core.CLAUDE_MODELS)) {
|
|
333
|
+
if (seen.has(model.name)) continue;
|
|
334
|
+
seen.add(model.name);
|
|
335
|
+
commands.push({
|
|
336
|
+
name: model.id,
|
|
337
|
+
description: `${model.name} (${(0, import_agent_core.formatTokenCount)(model.contextWindow).toUpperCase()})`,
|
|
338
|
+
source: "builtin"
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return commands;
|
|
342
|
+
}
|
|
74
343
|
function createBuiltinCommands() {
|
|
75
344
|
return [
|
|
76
345
|
{ name: "help", description: "Show available commands", source: "builtin" },
|
|
@@ -90,11 +359,7 @@ function createBuiltinCommands() {
|
|
|
90
359
|
name: "model",
|
|
91
360
|
description: "Select AI model",
|
|
92
361
|
source: "builtin",
|
|
93
|
-
subcommands:
|
|
94
|
-
{ name: "claude-opus-4-6", description: "Opus 4.6 (highest quality)", source: "builtin" },
|
|
95
|
-
{ name: "claude-sonnet-4-6", description: "Sonnet 4.6 (balanced)", source: "builtin" },
|
|
96
|
-
{ name: "claude-haiku-4-5", description: "Haiku 4.5 (fastest)", source: "builtin" }
|
|
97
|
-
]
|
|
362
|
+
subcommands: buildModelSubcommands()
|
|
98
363
|
},
|
|
99
364
|
{ name: "compact", description: "Compress context window", source: "builtin" },
|
|
100
365
|
{ name: "cost", description: "Show session info", source: "builtin" },
|
|
@@ -116,8 +381,8 @@ var BuiltinCommandSource = class {
|
|
|
116
381
|
};
|
|
117
382
|
|
|
118
383
|
// src/commands/skill-source.ts
|
|
119
|
-
var
|
|
120
|
-
var
|
|
384
|
+
var import_node_fs2 = require("fs");
|
|
385
|
+
var import_node_path2 = require("path");
|
|
121
386
|
var import_node_os = require("os");
|
|
122
387
|
function parseFrontmatter(content) {
|
|
123
388
|
const lines = content.split("\n");
|
|
@@ -140,14 +405,14 @@ function parseFrontmatter(content) {
|
|
|
140
405
|
return name ? { name, description } : null;
|
|
141
406
|
}
|
|
142
407
|
function scanSkillsDir(skillsDir) {
|
|
143
|
-
if (!(0,
|
|
408
|
+
if (!(0, import_node_fs2.existsSync)(skillsDir)) return [];
|
|
144
409
|
const commands = [];
|
|
145
|
-
const entries = (0,
|
|
410
|
+
const entries = (0, import_node_fs2.readdirSync)(skillsDir, { withFileTypes: true });
|
|
146
411
|
for (const entry of entries) {
|
|
147
412
|
if (!entry.isDirectory()) continue;
|
|
148
|
-
const skillFile = (0,
|
|
149
|
-
if (!(0,
|
|
150
|
-
const content = (0,
|
|
413
|
+
const skillFile = (0, import_node_path2.join)(skillsDir, entry.name, "SKILL.md");
|
|
414
|
+
if (!(0, import_node_fs2.existsSync)(skillFile)) continue;
|
|
415
|
+
const content = (0, import_node_fs2.readFileSync)(skillFile, "utf-8");
|
|
151
416
|
const frontmatter = parseFrontmatter(content);
|
|
152
417
|
commands.push({
|
|
153
418
|
name: frontmatter?.name ?? entry.name,
|
|
@@ -167,8 +432,8 @@ var SkillCommandSource = class {
|
|
|
167
432
|
}
|
|
168
433
|
getCommands() {
|
|
169
434
|
if (this.cachedCommands) return this.cachedCommands;
|
|
170
|
-
const projectSkills = scanSkillsDir((0,
|
|
171
|
-
const userSkills = scanSkillsDir((0,
|
|
435
|
+
const projectSkills = scanSkillsDir((0, import_node_path2.join)(this.cwd, ".agents", "skills"));
|
|
436
|
+
const userSkills = scanSkillsDir((0, import_node_path2.join)((0, import_node_os.homedir)(), ".claude", "skills"));
|
|
172
437
|
const seen = new Set(projectSkills.map((cmd) => cmd.name));
|
|
173
438
|
const merged = [...projectSkills];
|
|
174
439
|
for (const cmd of userSkills) {
|
|
@@ -242,6 +507,7 @@ function MessageList({ messages }) {
|
|
|
242
507
|
|
|
243
508
|
// src/ui/StatusBar.tsx
|
|
244
509
|
var import_ink2 = require("ink");
|
|
510
|
+
var import_agent_core2 = require("@robota-sdk/agent-core");
|
|
245
511
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
246
512
|
var CONTEXT_YELLOW_THRESHOLD = 70;
|
|
247
513
|
var CONTEXT_RED_THRESHOLD = 90;
|
|
@@ -281,10 +547,10 @@ function StatusBar({
|
|
|
281
547
|
"Context: ",
|
|
282
548
|
Math.round(contextPercentage),
|
|
283
549
|
"% (",
|
|
284
|
-
(
|
|
285
|
-
"
|
|
286
|
-
(
|
|
287
|
-
"
|
|
550
|
+
(0, import_agent_core2.formatTokenCount)(contextUsedTokens),
|
|
551
|
+
"/",
|
|
552
|
+
(0, import_agent_core2.formatTokenCount)(contextMaxTokens),
|
|
553
|
+
")"
|
|
288
554
|
] })
|
|
289
555
|
] }),
|
|
290
556
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { children: [
|
|
@@ -431,19 +697,13 @@ var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
|
431
697
|
var MAX_VISIBLE = 8;
|
|
432
698
|
function CommandRow(props) {
|
|
433
699
|
const { cmd, isSelected, showSlash } = props;
|
|
434
|
-
const prefix = showSlash ? "/" : "";
|
|
435
700
|
const indicator = isSelected ? "\u25B8 " : " ";
|
|
436
701
|
const nameColor = isSelected ? "cyan" : void 0;
|
|
437
702
|
const dimmed = !isSelected;
|
|
438
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
cmd.name
|
|
443
|
-
] }),
|
|
444
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { dimColor: dimmed, children: " " }),
|
|
445
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { color: nameColor, dimColor: dimmed, children: cmd.description })
|
|
446
|
-
] });
|
|
703
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink5.Text, { color: nameColor, dimColor: dimmed, children: [
|
|
704
|
+
indicator,
|
|
705
|
+
showSlash ? `/${cmd.name} ${cmd.description}` : cmd.description
|
|
706
|
+
] }) });
|
|
447
707
|
}
|
|
448
708
|
function SlashAutocomplete({
|
|
449
709
|
commands,
|
|
@@ -608,10 +868,53 @@ function InputArea({ onSubmit, isDisabled, registry }) {
|
|
|
608
868
|
] });
|
|
609
869
|
}
|
|
610
870
|
|
|
611
|
-
// src/ui/
|
|
612
|
-
var import_react4 =
|
|
871
|
+
// src/ui/ConfirmPrompt.tsx
|
|
872
|
+
var import_react4 = require("react");
|
|
613
873
|
var import_ink7 = require("ink");
|
|
614
874
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
875
|
+
function ConfirmPrompt({
|
|
876
|
+
message,
|
|
877
|
+
options = ["Yes", "No"],
|
|
878
|
+
onSelect
|
|
879
|
+
}) {
|
|
880
|
+
const [selected, setSelected] = (0, import_react4.useState)(0);
|
|
881
|
+
const resolvedRef = (0, import_react4.useRef)(false);
|
|
882
|
+
const doSelect = (0, import_react4.useCallback)(
|
|
883
|
+
(index) => {
|
|
884
|
+
if (resolvedRef.current) return;
|
|
885
|
+
resolvedRef.current = true;
|
|
886
|
+
onSelect(index);
|
|
887
|
+
},
|
|
888
|
+
[onSelect]
|
|
889
|
+
);
|
|
890
|
+
(0, import_ink7.useInput)((input, key) => {
|
|
891
|
+
if (resolvedRef.current) return;
|
|
892
|
+
if (key.leftArrow || key.upArrow) {
|
|
893
|
+
setSelected((prev) => prev > 0 ? prev - 1 : prev);
|
|
894
|
+
} else if (key.rightArrow || key.downArrow) {
|
|
895
|
+
setSelected((prev) => prev < options.length - 1 ? prev + 1 : prev);
|
|
896
|
+
} else if (key.return) {
|
|
897
|
+
doSelect(selected);
|
|
898
|
+
} else if (input === "y" && options.length === 2) {
|
|
899
|
+
doSelect(0);
|
|
900
|
+
} else if (input === "n" && options.length === 2) {
|
|
901
|
+
doSelect(1);
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
905
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "yellow", children: message }),
|
|
906
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
|
|
907
|
+
i === selected ? "> " : " ",
|
|
908
|
+
opt
|
|
909
|
+
] }) }, opt)) }),
|
|
910
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { dimColor: true, children: " arrow keys to select, Enter to confirm" })
|
|
911
|
+
] });
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// src/ui/PermissionPrompt.tsx
|
|
915
|
+
var import_react5 = __toESM(require("react"), 1);
|
|
916
|
+
var import_ink8 = require("ink");
|
|
917
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
615
918
|
var OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
|
|
616
919
|
function formatArgs(args) {
|
|
617
920
|
const entries = Object.entries(args);
|
|
@@ -619,15 +922,15 @@ function formatArgs(args) {
|
|
|
619
922
|
return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
620
923
|
}
|
|
621
924
|
function PermissionPrompt({ request }) {
|
|
622
|
-
const [selected, setSelected] =
|
|
623
|
-
const resolvedRef =
|
|
624
|
-
const prevRequestRef =
|
|
925
|
+
const [selected, setSelected] = import_react5.default.useState(0);
|
|
926
|
+
const resolvedRef = import_react5.default.useRef(false);
|
|
927
|
+
const prevRequestRef = import_react5.default.useRef(request);
|
|
625
928
|
if (prevRequestRef.current !== request) {
|
|
626
929
|
prevRequestRef.current = request;
|
|
627
930
|
resolvedRef.current = false;
|
|
628
931
|
setSelected(0);
|
|
629
932
|
}
|
|
630
|
-
const doResolve =
|
|
933
|
+
const doResolve = import_react5.default.useCallback(
|
|
631
934
|
(index) => {
|
|
632
935
|
if (resolvedRef.current) return;
|
|
633
936
|
resolvedRef.current = true;
|
|
@@ -637,7 +940,7 @@ function PermissionPrompt({ request }) {
|
|
|
637
940
|
},
|
|
638
941
|
[request]
|
|
639
942
|
);
|
|
640
|
-
(0,
|
|
943
|
+
(0, import_ink8.useInput)((input, key) => {
|
|
641
944
|
if (resolvedRef.current) return;
|
|
642
945
|
if (key.upArrow || key.leftArrow) {
|
|
643
946
|
setSelected((prev) => prev > 0 ? prev - 1 : prev);
|
|
@@ -653,27 +956,54 @@ function PermissionPrompt({ request }) {
|
|
|
653
956
|
doResolve(2);
|
|
654
957
|
}
|
|
655
958
|
});
|
|
656
|
-
return /* @__PURE__ */ (0,
|
|
657
|
-
/* @__PURE__ */ (0,
|
|
658
|
-
/* @__PURE__ */ (0,
|
|
959
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
960
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "yellow", bold: true, children: "[Permission Required]" }),
|
|
961
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { children: [
|
|
659
962
|
"Tool:",
|
|
660
963
|
" ",
|
|
661
|
-
/* @__PURE__ */ (0,
|
|
964
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "cyan", bold: true, children: request.toolName })
|
|
662
965
|
] }),
|
|
663
|
-
/* @__PURE__ */ (0,
|
|
966
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { dimColor: true, children: [
|
|
664
967
|
" ",
|
|
665
968
|
formatArgs(request.toolArgs)
|
|
666
969
|
] }),
|
|
667
|
-
/* @__PURE__ */ (0,
|
|
970
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Box, { marginTop: 1, children: OPTIONS.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
|
|
668
971
|
i === selected ? "> " : " ",
|
|
669
972
|
opt
|
|
670
973
|
] }) }, opt)) }),
|
|
671
|
-
/* @__PURE__ */ (0,
|
|
974
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { dimColor: true, children: " left/right to select, Enter to confirm" })
|
|
672
975
|
] });
|
|
673
976
|
}
|
|
674
977
|
|
|
978
|
+
// src/utils/tool-call-extractor.ts
|
|
979
|
+
var TOOL_ARG_MAX_LENGTH = 80;
|
|
980
|
+
var TOOL_ARG_TRUNCATE_LENGTH = 77;
|
|
981
|
+
function extractToolCalls(history, startIndex) {
|
|
982
|
+
const lines = [];
|
|
983
|
+
for (let i = startIndex; i < history.length; i++) {
|
|
984
|
+
const msg = history[i];
|
|
985
|
+
if (msg.role === "assistant" && msg.toolCalls) {
|
|
986
|
+
for (const tc of msg.toolCalls) {
|
|
987
|
+
const value = parseFirstArgValue(tc.function.arguments);
|
|
988
|
+
const truncated = value.length > TOOL_ARG_MAX_LENGTH ? value.slice(0, TOOL_ARG_TRUNCATE_LENGTH) + "..." : value;
|
|
989
|
+
lines.push(`${tc.function.name}(${truncated})`);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return lines;
|
|
994
|
+
}
|
|
995
|
+
function parseFirstArgValue(argsJson) {
|
|
996
|
+
try {
|
|
997
|
+
const parsed = JSON.parse(argsJson);
|
|
998
|
+
const firstVal = Object.values(parsed)[0];
|
|
999
|
+
return typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
|
|
1000
|
+
} catch {
|
|
1001
|
+
return argsJson;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
675
1005
|
// src/ui/App.tsx
|
|
676
|
-
var
|
|
1006
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
677
1007
|
var msgIdCounter = 0;
|
|
678
1008
|
function nextId() {
|
|
679
1009
|
msgIdCounter += 1;
|
|
@@ -695,11 +1025,11 @@ var NOOP_TERMINAL = {
|
|
|
695
1025
|
} })
|
|
696
1026
|
};
|
|
697
1027
|
function useSession(props) {
|
|
698
|
-
const [permissionRequest, setPermissionRequest] = (0,
|
|
699
|
-
const [streamingText, setStreamingText] = (0,
|
|
700
|
-
const permissionQueueRef = (0,
|
|
701
|
-
const processingRef = (0,
|
|
702
|
-
const processNextPermission = (0,
|
|
1028
|
+
const [permissionRequest, setPermissionRequest] = (0, import_react6.useState)(null);
|
|
1029
|
+
const [streamingText, setStreamingText] = (0, import_react6.useState)("");
|
|
1030
|
+
const permissionQueueRef = (0, import_react6.useRef)([]);
|
|
1031
|
+
const processingRef = (0, import_react6.useRef)(false);
|
|
1032
|
+
const processNextPermission = (0, import_react6.useCallback)(() => {
|
|
703
1033
|
if (processingRef.current) return;
|
|
704
1034
|
const next = permissionQueueRef.current[0];
|
|
705
1035
|
if (!next) {
|
|
@@ -719,7 +1049,7 @@ function useSession(props) {
|
|
|
719
1049
|
}
|
|
720
1050
|
});
|
|
721
1051
|
}, []);
|
|
722
|
-
const sessionRef = (0,
|
|
1052
|
+
const sessionRef = (0, import_react6.useRef)(null);
|
|
723
1053
|
if (sessionRef.current === null) {
|
|
724
1054
|
const permissionHandler = (toolName, toolArgs) => {
|
|
725
1055
|
return new Promise((resolve) => {
|
|
@@ -744,138 +1074,49 @@ function useSession(props) {
|
|
|
744
1074
|
onTextDelta
|
|
745
1075
|
});
|
|
746
1076
|
}
|
|
747
|
-
const clearStreamingText = (0,
|
|
1077
|
+
const clearStreamingText = (0, import_react6.useCallback)(() => setStreamingText(""), []);
|
|
748
1078
|
return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText };
|
|
749
1079
|
}
|
|
750
1080
|
function useMessages() {
|
|
751
|
-
const [messages, setMessages] = (0,
|
|
752
|
-
const addMessage = (0,
|
|
1081
|
+
const [messages, setMessages] = (0, import_react6.useState)([]);
|
|
1082
|
+
const addMessage = (0, import_react6.useCallback)((msg) => {
|
|
753
1083
|
setMessages((prev) => [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }]);
|
|
754
1084
|
}, []);
|
|
755
1085
|
return { messages, setMessages, addMessage };
|
|
756
1086
|
}
|
|
757
|
-
var
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
" /clear \u2014 Clear conversation",
|
|
761
|
-
" /compact [instr] \u2014 Compact context (optional focus instructions)",
|
|
762
|
-
" /mode [m] \u2014 Show/change permission mode",
|
|
763
|
-
" /cost \u2014 Show session info",
|
|
764
|
-
" /reset \u2014 Delete settings and exit",
|
|
765
|
-
" /exit \u2014 Exit CLI"
|
|
766
|
-
].join("\n");
|
|
767
|
-
function handleModeCommand(arg, session, addMessage) {
|
|
768
|
-
const validModes = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
769
|
-
if (!arg) {
|
|
770
|
-
addMessage({ role: "system", content: `Current mode: ${session.getPermissionMode()}` });
|
|
771
|
-
} else if (validModes.includes(arg)) {
|
|
772
|
-
session.setPermissionMode(arg);
|
|
773
|
-
addMessage({ role: "system", content: `Permission mode set to: ${arg}` });
|
|
774
|
-
} else {
|
|
775
|
-
addMessage({ role: "system", content: `Invalid mode. Valid: ${validModes.join(" | ")}` });
|
|
776
|
-
}
|
|
777
|
-
return true;
|
|
778
|
-
}
|
|
779
|
-
async function executeSlashCommand(cmd, parts, session, addMessage, setMessages, exit, registry) {
|
|
780
|
-
switch (cmd) {
|
|
781
|
-
case "help":
|
|
782
|
-
addMessage({ role: "system", content: HELP_TEXT });
|
|
783
|
-
return true;
|
|
784
|
-
case "clear":
|
|
785
|
-
setMessages([]);
|
|
786
|
-
session.clearHistory();
|
|
787
|
-
addMessage({ role: "system", content: "Conversation cleared." });
|
|
788
|
-
return true;
|
|
789
|
-
case "compact": {
|
|
790
|
-
const instructions = parts.slice(1).join(" ").trim() || void 0;
|
|
791
|
-
const before = session.getContextState().usedPercentage;
|
|
792
|
-
addMessage({ role: "system", content: "Compacting context..." });
|
|
793
|
-
await session.compact(instructions);
|
|
794
|
-
const after = session.getContextState().usedPercentage;
|
|
795
|
-
addMessage({
|
|
796
|
-
role: "system",
|
|
797
|
-
content: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`
|
|
798
|
-
});
|
|
799
|
-
return true;
|
|
800
|
-
}
|
|
801
|
-
case "mode":
|
|
802
|
-
return handleModeCommand(parts[1], session, addMessage);
|
|
803
|
-
case "cost":
|
|
804
|
-
addMessage({
|
|
805
|
-
role: "system",
|
|
806
|
-
content: `Session: ${session.getSessionId()}
|
|
807
|
-
Messages: ${session.getMessageCount()}`
|
|
808
|
-
});
|
|
809
|
-
return true;
|
|
810
|
-
case "permissions": {
|
|
811
|
-
const mode = session.getPermissionMode();
|
|
812
|
-
const sessionAllowed = session.getSessionAllowedTools();
|
|
813
|
-
const lines = [`Permission mode: ${mode}`];
|
|
814
|
-
if (sessionAllowed.length > 0) {
|
|
815
|
-
lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
|
|
816
|
-
} else {
|
|
817
|
-
lines.push("No session-approved tools.");
|
|
818
|
-
}
|
|
819
|
-
addMessage({ role: "system", content: lines.join("\n") });
|
|
820
|
-
return true;
|
|
821
|
-
}
|
|
822
|
-
case "context": {
|
|
823
|
-
const ctx = session.getContextState();
|
|
824
|
-
addMessage({
|
|
825
|
-
role: "system",
|
|
826
|
-
content: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`
|
|
827
|
-
});
|
|
828
|
-
return true;
|
|
829
|
-
}
|
|
830
|
-
case "reset": {
|
|
831
|
-
const { existsSync: exists, unlinkSync: unlink } = await import("fs");
|
|
832
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
|
|
833
|
-
const settingsPath = `${home}/.robota/settings.json`;
|
|
834
|
-
if (exists(settingsPath)) {
|
|
835
|
-
unlink(settingsPath);
|
|
836
|
-
addMessage({ role: "system", content: `Deleted ${settingsPath}. Exiting...` });
|
|
837
|
-
} else {
|
|
838
|
-
addMessage({ role: "system", content: "No user settings found." });
|
|
839
|
-
}
|
|
840
|
-
setTimeout(() => exit(), 500);
|
|
841
|
-
return true;
|
|
842
|
-
}
|
|
843
|
-
case "exit":
|
|
844
|
-
exit();
|
|
845
|
-
return true;
|
|
846
|
-
default: {
|
|
847
|
-
const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
|
|
848
|
-
if (skillCmd) {
|
|
849
|
-
addMessage({ role: "system", content: `Invoking skill: ${cmd}` });
|
|
850
|
-
return false;
|
|
851
|
-
}
|
|
852
|
-
addMessage({ role: "system", content: `Unknown command "/${cmd}". Type /help for help.` });
|
|
853
|
-
return true;
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
function useSlashCommands(session, addMessage, setMessages, exit, registry) {
|
|
858
|
-
return (0, import_react5.useCallback)(
|
|
1087
|
+
var EXIT_DELAY_MS = 500;
|
|
1088
|
+
function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId) {
|
|
1089
|
+
return (0, import_react6.useCallback)(
|
|
859
1090
|
async (input) => {
|
|
860
1091
|
const parts = input.slice(1).split(/\s+/);
|
|
861
1092
|
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
862
|
-
|
|
1093
|
+
const args = parts.slice(1).join(" ");
|
|
1094
|
+
const clearMessages = () => setMessages([]);
|
|
1095
|
+
const result = await executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry);
|
|
1096
|
+
if (result.pendingModelId) {
|
|
1097
|
+
pendingModelChangeRef.current = result.pendingModelId;
|
|
1098
|
+
setPendingModelId(result.pendingModelId);
|
|
1099
|
+
}
|
|
1100
|
+
if (result.exitRequested) {
|
|
1101
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
1102
|
+
}
|
|
1103
|
+
return result.handled;
|
|
863
1104
|
},
|
|
864
|
-
[session, addMessage, setMessages, exit, registry]
|
|
1105
|
+
[session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId]
|
|
865
1106
|
);
|
|
866
1107
|
}
|
|
867
1108
|
function StreamingIndicator({ text }) {
|
|
868
1109
|
if (text) {
|
|
869
|
-
return /* @__PURE__ */ (0,
|
|
870
|
-
/* @__PURE__ */ (0,
|
|
1110
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", children: [
|
|
1111
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { color: "cyan", bold: true, children: [
|
|
871
1112
|
"Robota:",
|
|
872
1113
|
" "
|
|
873
1114
|
] }),
|
|
874
|
-
/* @__PURE__ */ (0,
|
|
875
|
-
/* @__PURE__ */ (0,
|
|
1115
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " }),
|
|
1116
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { wrap: "wrap", children: renderMarkdown(text) }) })
|
|
876
1117
|
] });
|
|
877
1118
|
}
|
|
878
|
-
return /* @__PURE__ */ (0,
|
|
1119
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "yellow", children: "Thinking..." });
|
|
879
1120
|
}
|
|
880
1121
|
async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextPercentage) {
|
|
881
1122
|
setIsThinking(true);
|
|
@@ -885,24 +1126,10 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
|
|
|
885
1126
|
const response = await session.run(prompt);
|
|
886
1127
|
clearStreamingText();
|
|
887
1128
|
const history = session.getHistory();
|
|
888
|
-
const toolLines =
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
for (const tc of msg.toolCalls) {
|
|
893
|
-
let value = "";
|
|
894
|
-
try {
|
|
895
|
-
const parsed = JSON.parse(tc.function.arguments);
|
|
896
|
-
const firstVal = Object.values(parsed)[0];
|
|
897
|
-
value = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
|
|
898
|
-
} catch {
|
|
899
|
-
value = tc.function.arguments;
|
|
900
|
-
}
|
|
901
|
-
const truncated = value.length > 80 ? value.slice(0, 77) + "..." : value;
|
|
902
|
-
toolLines.push(`${tc.function.name}(${truncated})`);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
}
|
|
1129
|
+
const toolLines = extractToolCalls(
|
|
1130
|
+
history,
|
|
1131
|
+
historyBefore
|
|
1132
|
+
);
|
|
906
1133
|
if (toolLines.length > 0) {
|
|
907
1134
|
addMessage({ role: "tool", content: toolLines.join("\n"), toolName: `${toolLines.length} tools` });
|
|
908
1135
|
}
|
|
@@ -937,7 +1164,7 @@ Execute the "${cmd}" skill: ${userInstruction}`;
|
|
|
937
1164
|
return `Use the "${cmd}" skill: ${userInstruction}`;
|
|
938
1165
|
}
|
|
939
1166
|
function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextPercentage, registry) {
|
|
940
|
-
return (0,
|
|
1167
|
+
return (0, import_react6.useCallback)(
|
|
941
1168
|
async (input) => {
|
|
942
1169
|
if (input.startsWith("/")) {
|
|
943
1170
|
const handled = await handleSlashCommand(input);
|
|
@@ -978,7 +1205,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
|
|
|
978
1205
|
);
|
|
979
1206
|
}
|
|
980
1207
|
function useCommandRegistry(cwd) {
|
|
981
|
-
const registryRef = (0,
|
|
1208
|
+
const registryRef = (0, import_react6.useRef)(null);
|
|
982
1209
|
if (registryRef.current === null) {
|
|
983
1210
|
const registry = new CommandRegistry();
|
|
984
1211
|
registry.addSource(new BuiltinCommandSource());
|
|
@@ -988,13 +1215,15 @@ function useCommandRegistry(cwd) {
|
|
|
988
1215
|
return registryRef.current;
|
|
989
1216
|
}
|
|
990
1217
|
function App(props) {
|
|
991
|
-
const { exit } = (0,
|
|
1218
|
+
const { exit } = (0, import_ink9.useApp)();
|
|
992
1219
|
const { session, permissionRequest, streamingText, clearStreamingText } = useSession(props);
|
|
993
1220
|
const { messages, setMessages, addMessage } = useMessages();
|
|
994
|
-
const [isThinking, setIsThinking] = (0,
|
|
995
|
-
const [contextPercentage, setContextPercentage] = (0,
|
|
1221
|
+
const [isThinking, setIsThinking] = (0, import_react6.useState)(false);
|
|
1222
|
+
const [contextPercentage, setContextPercentage] = (0, import_react6.useState)(0);
|
|
996
1223
|
const registry = useCommandRegistry(props.cwd ?? process.cwd());
|
|
997
|
-
const
|
|
1224
|
+
const pendingModelChangeRef = (0, import_react6.useRef)(null);
|
|
1225
|
+
const [pendingModelId, setPendingModelId] = (0, import_react6.useState)(null);
|
|
1226
|
+
const handleSlashCommand = useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId);
|
|
998
1227
|
const handleSubmit = useSubmitHandler(
|
|
999
1228
|
session,
|
|
1000
1229
|
addMessage,
|
|
@@ -1004,37 +1233,59 @@ function App(props) {
|
|
|
1004
1233
|
setContextPercentage,
|
|
1005
1234
|
registry
|
|
1006
1235
|
);
|
|
1007
|
-
(0,
|
|
1236
|
+
(0, import_ink9.useInput)(
|
|
1008
1237
|
(_input, key) => {
|
|
1009
1238
|
if (key.ctrl && _input === "c") exit();
|
|
1010
1239
|
if (key.escape && isThinking) session.abort();
|
|
1011
1240
|
},
|
|
1012
1241
|
{ isActive: !permissionRequest }
|
|
1013
1242
|
);
|
|
1014
|
-
return /* @__PURE__ */ (0,
|
|
1015
|
-
/* @__PURE__ */ (0,
|
|
1016
|
-
/* @__PURE__ */ (0,
|
|
1243
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", children: [
|
|
1244
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
1245
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "cyan", bold: true, children: `
|
|
1017
1246
|
____ ___ ____ ___ _____ _
|
|
1018
1247
|
| _ \\ / _ \\| __ ) / _ \\_ _|/ \\
|
|
1019
1248
|
| |_) | | | | _ \\| | | || | / _ \\
|
|
1020
1249
|
| _ <| |_| | |_) | |_| || |/ ___ \\
|
|
1021
1250
|
|_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
|
|
1022
1251
|
` }),
|
|
1023
|
-
/* @__PURE__ */ (0,
|
|
1252
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { dimColor: true, children: [
|
|
1024
1253
|
" v",
|
|
1025
1254
|
props.version ?? "0.0.0"
|
|
1026
1255
|
] })
|
|
1027
1256
|
] }),
|
|
1028
|
-
/* @__PURE__ */ (0,
|
|
1029
|
-
/* @__PURE__ */ (0,
|
|
1030
|
-
isThinking && /* @__PURE__ */ (0,
|
|
1257
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
|
|
1258
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MessageList, { messages }),
|
|
1259
|
+
isThinking && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StreamingIndicator, { text: streamingText }) })
|
|
1031
1260
|
] }),
|
|
1032
|
-
permissionRequest && /* @__PURE__ */ (0,
|
|
1033
|
-
/* @__PURE__ */ (0,
|
|
1261
|
+
permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PermissionPrompt, { request: permissionRequest }),
|
|
1262
|
+
pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1263
|
+
ConfirmPrompt,
|
|
1264
|
+
{
|
|
1265
|
+
message: `Change model to ${(0, import_agent_core3.getModelName)(pendingModelId)}? This will restart the session.`,
|
|
1266
|
+
onSelect: (index) => {
|
|
1267
|
+
setPendingModelId(null);
|
|
1268
|
+
pendingModelChangeRef.current = null;
|
|
1269
|
+
if (index === 0) {
|
|
1270
|
+
try {
|
|
1271
|
+
const settingsPath = getUserSettingsPath();
|
|
1272
|
+
updateModelInSettings(settingsPath, pendingModelId);
|
|
1273
|
+
addMessage({ role: "system", content: `Model changed to ${(0, import_agent_core3.getModelName)(pendingModelId)}. Restarting...` });
|
|
1274
|
+
setTimeout(() => exit(), 500);
|
|
1275
|
+
} catch (err) {
|
|
1276
|
+
addMessage({ role: "system", content: `Failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
1277
|
+
}
|
|
1278
|
+
} else {
|
|
1279
|
+
addMessage({ role: "system", content: "Model change cancelled." });
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
),
|
|
1284
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1034
1285
|
StatusBar,
|
|
1035
1286
|
{
|
|
1036
1287
|
permissionMode: session.getPermissionMode(),
|
|
1037
|
-
modelName: props.config.provider.model,
|
|
1288
|
+
modelName: (0, import_agent_core3.getModelName)(props.config.provider.model),
|
|
1038
1289
|
sessionId: session.getSessionId(),
|
|
1039
1290
|
messageCount: messages.length,
|
|
1040
1291
|
isThinking,
|
|
@@ -1043,7 +1294,7 @@ function App(props) {
|
|
|
1043
1294
|
contextMaxTokens: session.getContextState().maxTokens
|
|
1044
1295
|
}
|
|
1045
1296
|
),
|
|
1046
|
-
/* @__PURE__ */ (0,
|
|
1297
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1047
1298
|
InputArea,
|
|
1048
1299
|
{
|
|
1049
1300
|
onSubmit: handleSubmit,
|
|
@@ -1051,12 +1302,12 @@ function App(props) {
|
|
|
1051
1302
|
registry
|
|
1052
1303
|
}
|
|
1053
1304
|
),
|
|
1054
|
-
/* @__PURE__ */ (0,
|
|
1305
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " })
|
|
1055
1306
|
] });
|
|
1056
1307
|
}
|
|
1057
1308
|
|
|
1058
1309
|
// src/ui/render.tsx
|
|
1059
|
-
var
|
|
1310
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1060
1311
|
function renderApp(options) {
|
|
1061
1312
|
process.on("unhandledRejection", (reason) => {
|
|
1062
1313
|
process.stderr.write(`
|
|
@@ -1067,7 +1318,7 @@ function renderApp(options) {
|
|
|
1067
1318
|
`);
|
|
1068
1319
|
}
|
|
1069
1320
|
});
|
|
1070
|
-
const instance = (0,
|
|
1321
|
+
const instance = (0, import_ink10.render)(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(App, { ...options }), {
|
|
1071
1322
|
exitOnCtrlC: true
|
|
1072
1323
|
});
|
|
1073
1324
|
instance.waitUntilExit().catch((err) => {
|
|
@@ -1081,15 +1332,14 @@ function renderApp(options) {
|
|
|
1081
1332
|
|
|
1082
1333
|
// src/cli.ts
|
|
1083
1334
|
var import_meta = {};
|
|
1084
|
-
var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
1085
1335
|
function readVersion() {
|
|
1086
1336
|
try {
|
|
1087
1337
|
const thisFile = (0, import_node_url.fileURLToPath)(import_meta.url);
|
|
1088
|
-
const dir = (0,
|
|
1089
|
-
const candidates = [(0,
|
|
1338
|
+
const dir = (0, import_node_path3.dirname)(thisFile);
|
|
1339
|
+
const candidates = [(0, import_node_path3.join)(dir, "..", "..", "package.json"), (0, import_node_path3.join)(dir, "..", "package.json")];
|
|
1090
1340
|
for (const pkgPath of candidates) {
|
|
1091
1341
|
try {
|
|
1092
|
-
const raw = (0,
|
|
1342
|
+
const raw = (0, import_node_fs3.readFileSync)(pkgPath, "utf-8");
|
|
1093
1343
|
const pkg = JSON.parse(raw);
|
|
1094
1344
|
if (pkg.version !== void 0 && pkg.name !== void 0) {
|
|
1095
1345
|
return pkg.version;
|
|
@@ -1102,108 +1352,11 @@ function readVersion() {
|
|
|
1102
1352
|
return "0.0.0";
|
|
1103
1353
|
}
|
|
1104
1354
|
}
|
|
1105
|
-
function parsePermissionMode(raw) {
|
|
1106
|
-
if (raw === void 0) return void 0;
|
|
1107
|
-
if (!VALID_MODES.includes(raw)) {
|
|
1108
|
-
process.stderr.write(`Invalid --permission-mode "${raw}". Valid: ${VALID_MODES.join(" | ")}
|
|
1109
|
-
`);
|
|
1110
|
-
process.exit(1);
|
|
1111
|
-
}
|
|
1112
|
-
return raw;
|
|
1113
|
-
}
|
|
1114
|
-
function parseMaxTurns(raw) {
|
|
1115
|
-
if (raw === void 0) return void 0;
|
|
1116
|
-
const n = parseInt(raw, 10);
|
|
1117
|
-
if (isNaN(n) || n <= 0) {
|
|
1118
|
-
process.stderr.write(`Invalid --max-turns "${raw}". Must be a positive integer.
|
|
1119
|
-
`);
|
|
1120
|
-
process.exit(1);
|
|
1121
|
-
}
|
|
1122
|
-
return n;
|
|
1123
|
-
}
|
|
1124
|
-
function parseCliArgs() {
|
|
1125
|
-
const { values, positionals } = (0, import_node_util.parseArgs)({
|
|
1126
|
-
allowPositionals: true,
|
|
1127
|
-
options: {
|
|
1128
|
-
p: { type: "boolean", short: "p", default: false },
|
|
1129
|
-
c: { type: "boolean", short: "c", default: false },
|
|
1130
|
-
r: { type: "string", short: "r" },
|
|
1131
|
-
model: { type: "string" },
|
|
1132
|
-
"permission-mode": { type: "string" },
|
|
1133
|
-
"max-turns": { type: "string" },
|
|
1134
|
-
version: { type: "boolean", default: false },
|
|
1135
|
-
reset: { type: "boolean", default: false }
|
|
1136
|
-
}
|
|
1137
|
-
});
|
|
1138
|
-
return {
|
|
1139
|
-
positional: positionals,
|
|
1140
|
-
printMode: values["p"] ?? false,
|
|
1141
|
-
continueMode: values["c"] ?? false,
|
|
1142
|
-
resumeId: values["r"],
|
|
1143
|
-
model: values["model"],
|
|
1144
|
-
permissionMode: parsePermissionMode(values["permission-mode"]),
|
|
1145
|
-
maxTurns: parseMaxTurns(values["max-turns"]),
|
|
1146
|
-
version: values["version"] ?? false,
|
|
1147
|
-
reset: values["reset"] ?? false
|
|
1148
|
-
};
|
|
1149
|
-
}
|
|
1150
|
-
var PrintTerminal = class {
|
|
1151
|
-
write(text) {
|
|
1152
|
-
process.stdout.write(text);
|
|
1153
|
-
}
|
|
1154
|
-
writeLine(text) {
|
|
1155
|
-
process.stdout.write(text + "\n");
|
|
1156
|
-
}
|
|
1157
|
-
writeMarkdown(md) {
|
|
1158
|
-
process.stdout.write(md);
|
|
1159
|
-
}
|
|
1160
|
-
writeError(text) {
|
|
1161
|
-
process.stderr.write(text + "\n");
|
|
1162
|
-
}
|
|
1163
|
-
prompt(question) {
|
|
1164
|
-
return new Promise((resolve) => {
|
|
1165
|
-
const rl = readline.createInterface({
|
|
1166
|
-
input: process.stdin,
|
|
1167
|
-
output: process.stdout,
|
|
1168
|
-
terminal: false,
|
|
1169
|
-
historySize: 0
|
|
1170
|
-
});
|
|
1171
|
-
rl.question(question, (answer) => {
|
|
1172
|
-
rl.close();
|
|
1173
|
-
resolve(answer);
|
|
1174
|
-
});
|
|
1175
|
-
});
|
|
1176
|
-
}
|
|
1177
|
-
async select(options, initialIndex = 0) {
|
|
1178
|
-
for (let i = 0; i < options.length; i++) {
|
|
1179
|
-
const marker = i === initialIndex ? ">" : " ";
|
|
1180
|
-
process.stdout.write(` ${marker} ${i + 1}) ${options[i]}
|
|
1181
|
-
`);
|
|
1182
|
-
}
|
|
1183
|
-
const answer = await this.prompt(
|
|
1184
|
-
` Choose [1-${options.length}] (default: ${options[initialIndex]}): `
|
|
1185
|
-
);
|
|
1186
|
-
const trimmed = answer.trim().toLowerCase();
|
|
1187
|
-
if (trimmed === "") return initialIndex;
|
|
1188
|
-
const num = parseInt(trimmed, 10);
|
|
1189
|
-
if (!isNaN(num) && num >= 1 && num <= options.length) return num - 1;
|
|
1190
|
-
return initialIndex;
|
|
1191
|
-
}
|
|
1192
|
-
spinner(_message) {
|
|
1193
|
-
return { stop() {
|
|
1194
|
-
}, update() {
|
|
1195
|
-
} };
|
|
1196
|
-
}
|
|
1197
|
-
};
|
|
1198
|
-
function getUserSettingsPath() {
|
|
1199
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
|
|
1200
|
-
return (0, import_node_path2.join)(home, ".robota", "settings.json");
|
|
1201
|
-
}
|
|
1202
1355
|
async function ensureConfig(cwd) {
|
|
1203
1356
|
const userPath = getUserSettingsPath();
|
|
1204
|
-
const projectPath = (0,
|
|
1205
|
-
const localPath = (0,
|
|
1206
|
-
if ((0,
|
|
1357
|
+
const projectPath = (0, import_node_path3.join)(cwd, ".robota", "settings.json");
|
|
1358
|
+
const localPath = (0, import_node_path3.join)(cwd, ".robota", "settings.local.json");
|
|
1359
|
+
if ((0, import_node_fs3.existsSync)(userPath) || (0, import_node_fs3.existsSync)(projectPath) || (0, import_node_fs3.existsSync)(localPath)) {
|
|
1207
1360
|
return;
|
|
1208
1361
|
}
|
|
1209
1362
|
process.stdout.write("\n");
|
|
@@ -1247,8 +1400,8 @@ async function ensureConfig(cwd) {
|
|
|
1247
1400
|
process.stderr.write("\n No API key provided. Exiting.\n");
|
|
1248
1401
|
process.exit(1);
|
|
1249
1402
|
}
|
|
1250
|
-
const settingsDir = (0,
|
|
1251
|
-
(0,
|
|
1403
|
+
const settingsDir = (0, import_node_path3.dirname)(userPath);
|
|
1404
|
+
(0, import_node_fs3.mkdirSync)(settingsDir, { recursive: true });
|
|
1252
1405
|
const settings = {
|
|
1253
1406
|
provider: {
|
|
1254
1407
|
name: "anthropic",
|
|
@@ -1256,7 +1409,7 @@ async function ensureConfig(cwd) {
|
|
|
1256
1409
|
apiKey
|
|
1257
1410
|
}
|
|
1258
1411
|
};
|
|
1259
|
-
(0,
|
|
1412
|
+
(0, import_node_fs3.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
1260
1413
|
process.stdout.write(`
|
|
1261
1414
|
Config saved to ${userPath}
|
|
1262
1415
|
|
|
@@ -1264,8 +1417,7 @@ async function ensureConfig(cwd) {
|
|
|
1264
1417
|
}
|
|
1265
1418
|
function resetConfig() {
|
|
1266
1419
|
const userPath = getUserSettingsPath();
|
|
1267
|
-
if ((
|
|
1268
|
-
(0, import_node_fs2.unlinkSync)(userPath);
|
|
1420
|
+
if (deleteSettings(userPath)) {
|
|
1269
1421
|
process.stdout.write(`Deleted ${userPath}
|
|
1270
1422
|
`);
|
|
1271
1423
|
} else {
|