@robota-sdk/agent-cli 3.0.0-beta.40 → 3.0.0-beta.42
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 +133 -112
- package/dist/node/bin.js +1 -1
- package/dist/node/{chunk-CRPNSO52.js → chunk-SYGOHAAL.js} +489 -1298
- package/dist/node/index.cjs +516 -1323
- package/dist/node/index.js +1 -1
- package/package.json +4 -4
- package/dist/node/bin.cjs +0 -3180
- package/dist/node/bin.d.cts +0 -1
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_sdk5.Session,
|
|
34
|
+
SessionStore: () => import_agent_sdk5.SessionStore,
|
|
35
|
+
TRUST_TO_MODE: () => import_agent_sdk5.TRUST_TO_MODE,
|
|
36
|
+
query: () => import_agent_sdk5.query,
|
|
37
37
|
startCli: () => startCli
|
|
38
38
|
});
|
|
39
39
|
module.exports = __toCommonJS(index_exports);
|
|
40
|
-
var
|
|
40
|
+
var import_agent_sdk5 = require("@robota-sdk/agent-sdk");
|
|
41
41
|
|
|
42
42
|
// src/cli.ts
|
|
43
|
-
var
|
|
43
|
+
var import_node_fs2 = require("fs");
|
|
44
44
|
var import_node_path5 = require("path");
|
|
45
45
|
var import_node_url = require("url");
|
|
46
|
-
var
|
|
47
|
-
var
|
|
46
|
+
var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
|
|
47
|
+
var import_agent_sdk4 = require("@robota-sdk/agent-sdk");
|
|
48
48
|
|
|
49
49
|
// src/utils/cli-args.ts
|
|
50
50
|
var import_node_util = require("util");
|
|
@@ -182,606 +182,50 @@ var PrintTerminal = class {
|
|
|
182
182
|
var import_ink14 = require("ink");
|
|
183
183
|
|
|
184
184
|
// src/ui/App.tsx
|
|
185
|
-
var
|
|
185
|
+
var import_react12 = require("react");
|
|
186
186
|
var import_ink13 = require("ink");
|
|
187
|
-
var
|
|
188
|
-
var import_agent_core7 = require("@robota-sdk/agent-core");
|
|
187
|
+
var import_agent_core4 = require("@robota-sdk/agent-core");
|
|
189
188
|
|
|
190
|
-
// src/ui/hooks/
|
|
189
|
+
// src/ui/hooks/useInteractiveSession.ts
|
|
191
190
|
var import_react = require("react");
|
|
191
|
+
var import_node_os = require("os");
|
|
192
|
+
var import_node_path3 = require("path");
|
|
192
193
|
var import_agent_sdk = require("@robota-sdk/agent-sdk");
|
|
193
|
-
|
|
194
|
-
// src/utils/edit-diff.ts
|
|
195
|
-
var import_node_fs2 = require("fs");
|
|
196
|
-
var CONTEXT_LINES = 2;
|
|
197
|
-
function generateDiffLines(oldStr, newStr, startLine = 1) {
|
|
198
|
-
if (oldStr === newStr) return [];
|
|
199
|
-
const lines = [];
|
|
200
|
-
const oldLines = oldStr.split("\n");
|
|
201
|
-
const newLines = newStr.split("\n");
|
|
202
|
-
for (let i = 0; i < oldLines.length; i++) {
|
|
203
|
-
lines.push({ type: "remove", text: oldLines[i], lineNumber: startLine + i });
|
|
204
|
-
}
|
|
205
|
-
for (let i = 0; i < newLines.length; i++) {
|
|
206
|
-
lines.push({ type: "add", text: newLines[i], lineNumber: startLine + i });
|
|
207
|
-
}
|
|
208
|
-
return lines;
|
|
209
|
-
}
|
|
210
|
-
function generateDiffLinesWithContext(oldStr, newStr, startLine, filePath) {
|
|
211
|
-
if (oldStr === newStr) return [];
|
|
212
|
-
const diffLines = generateDiffLines(oldStr, newStr, startLine);
|
|
213
|
-
let fileLines;
|
|
214
|
-
try {
|
|
215
|
-
fileLines = (0, import_node_fs2.readFileSync)(filePath, "utf-8").split("\n");
|
|
216
|
-
} catch {
|
|
217
|
-
return diffLines;
|
|
218
|
-
}
|
|
219
|
-
const result = [];
|
|
220
|
-
const contextStart = Math.max(0, startLine - 1 - CONTEXT_LINES);
|
|
221
|
-
for (let i = contextStart; i < startLine - 1; i++) {
|
|
222
|
-
if (i < fileLines.length) {
|
|
223
|
-
result.push({ type: "context", text: fileLines[i], lineNumber: i + 1 });
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
result.push(...diffLines);
|
|
227
|
-
const newLineCount = newStr.split("\n").length;
|
|
228
|
-
const afterStart = startLine - 1 + newLineCount;
|
|
229
|
-
for (let i = afterStart; i < afterStart + CONTEXT_LINES; i++) {
|
|
230
|
-
if (i < fileLines.length) {
|
|
231
|
-
result.push({ type: "context", text: fileLines[i], lineNumber: i + 1 });
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return result;
|
|
235
|
-
}
|
|
236
|
-
function extractEditDiff(toolName, toolArgs, startLine) {
|
|
237
|
-
if (toolName !== "Edit" || !toolArgs) return null;
|
|
238
|
-
const filePath = toolArgs.file_path ?? toolArgs.filePath;
|
|
239
|
-
const oldStr = toolArgs.old_string ?? toolArgs.oldString;
|
|
240
|
-
const newStr = toolArgs.new_string ?? toolArgs.newString;
|
|
241
|
-
if (typeof filePath !== "string") return null;
|
|
242
|
-
if (typeof oldStr !== "string" || typeof newStr !== "string") return null;
|
|
243
|
-
let sl = startLine ?? 0;
|
|
244
|
-
if (!sl) {
|
|
245
|
-
try {
|
|
246
|
-
const fileContent = (0, import_node_fs2.readFileSync)(filePath, "utf-8");
|
|
247
|
-
const idx = fileContent.indexOf(newStr);
|
|
248
|
-
if (idx >= 0) {
|
|
249
|
-
sl = fileContent.substring(0, idx).split("\n").length;
|
|
250
|
-
} else {
|
|
251
|
-
sl = 1;
|
|
252
|
-
}
|
|
253
|
-
} catch {
|
|
254
|
-
sl = 1;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
const lines = generateDiffLinesWithContext(oldStr, newStr, sl, filePath);
|
|
258
|
-
if (lines.length === 0) return null;
|
|
259
|
-
return { file: filePath, lines };
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// src/ui/hooks/useSession.ts
|
|
263
|
-
var TOOL_ARG_DISPLAY_MAX = 80;
|
|
264
|
-
var TAIL_KEEP = 30;
|
|
265
|
-
var MAX_COMPLETED_TOOLS = 50;
|
|
266
|
-
var NOOP_TERMINAL = {
|
|
267
|
-
write: () => {
|
|
268
|
-
},
|
|
269
|
-
writeLine: () => {
|
|
270
|
-
},
|
|
271
|
-
writeMarkdown: () => {
|
|
272
|
-
},
|
|
273
|
-
writeError: () => {
|
|
274
|
-
},
|
|
275
|
-
prompt: () => Promise.resolve(""),
|
|
276
|
-
select: () => Promise.resolve(0),
|
|
277
|
-
spinner: () => ({ stop: () => {
|
|
278
|
-
}, update: () => {
|
|
279
|
-
} })
|
|
280
|
-
};
|
|
281
|
-
function useSession(props) {
|
|
282
|
-
const [permissionRequest, setPermissionRequest] = (0, import_react.useState)(null);
|
|
283
|
-
const [streamingText, setStreamingText] = (0, import_react.useState)("");
|
|
284
|
-
const streamingTextRef = (0, import_react.useRef)("");
|
|
285
|
-
const [activeTools, setActiveTools] = (0, import_react.useState)([]);
|
|
286
|
-
const permissionQueueRef = (0, import_react.useRef)([]);
|
|
287
|
-
const processingRef = (0, import_react.useRef)(false);
|
|
288
|
-
const processNextPermission = (0, import_react.useCallback)(() => {
|
|
289
|
-
if (processingRef.current) return;
|
|
290
|
-
const next = permissionQueueRef.current[0];
|
|
291
|
-
if (!next) {
|
|
292
|
-
setPermissionRequest(null);
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
processingRef.current = true;
|
|
296
|
-
setPermissionRequest({
|
|
297
|
-
toolName: next.toolName,
|
|
298
|
-
toolArgs: next.toolArgs,
|
|
299
|
-
resolve: (result) => {
|
|
300
|
-
permissionQueueRef.current.shift();
|
|
301
|
-
processingRef.current = false;
|
|
302
|
-
setPermissionRequest(null);
|
|
303
|
-
next.resolve(result);
|
|
304
|
-
setTimeout(() => processNextPermission(), 0);
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
}, []);
|
|
308
|
-
const sessionRef = (0, import_react.useRef)(null);
|
|
309
|
-
if (sessionRef.current === null) {
|
|
310
|
-
const permissionHandler = (toolName, toolArgs) => {
|
|
311
|
-
return new Promise((resolve) => {
|
|
312
|
-
permissionQueueRef.current.push({ toolName, toolArgs, resolve });
|
|
313
|
-
processNextPermission();
|
|
314
|
-
});
|
|
315
|
-
};
|
|
316
|
-
let flushTimer = null;
|
|
317
|
-
const onTextDelta = (delta) => {
|
|
318
|
-
streamingTextRef.current += delta;
|
|
319
|
-
if (!flushTimer) {
|
|
320
|
-
flushTimer = setTimeout(() => {
|
|
321
|
-
setStreamingText(streamingTextRef.current);
|
|
322
|
-
flushTimer = null;
|
|
323
|
-
}, 16);
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
|
-
const onToolExecution = (event) => {
|
|
327
|
-
if (event.type === "start") {
|
|
328
|
-
let firstArg = "";
|
|
329
|
-
if (event.toolArgs) {
|
|
330
|
-
const firstVal = Object.values(event.toolArgs)[0];
|
|
331
|
-
const raw = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal ?? "");
|
|
332
|
-
firstArg = raw.length > TOOL_ARG_DISPLAY_MAX ? raw.slice(0, TOOL_ARG_DISPLAY_MAX - TAIL_KEEP - 3) + "..." + raw.slice(-TAIL_KEEP) : raw;
|
|
333
|
-
}
|
|
334
|
-
setActiveTools((prev) => [
|
|
335
|
-
...prev,
|
|
336
|
-
{ toolName: event.toolName, firstArg, isRunning: true, _toolArgs: event.toolArgs }
|
|
337
|
-
]);
|
|
338
|
-
} else {
|
|
339
|
-
const toolResult = event.denied ? "denied" : event.success === false ? "error" : "success";
|
|
340
|
-
setActiveTools((prev) => {
|
|
341
|
-
const updated = prev.map((t) => {
|
|
342
|
-
if (!(t.toolName === event.toolName && t.isRunning)) return t;
|
|
343
|
-
let startLine;
|
|
344
|
-
if (event.toolResultData && event.toolName === "Edit") {
|
|
345
|
-
try {
|
|
346
|
-
const parsed = JSON.parse(event.toolResultData);
|
|
347
|
-
if (typeof parsed.startLine === "number") {
|
|
348
|
-
startLine = parsed.startLine;
|
|
349
|
-
}
|
|
350
|
-
} catch {
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
const editDiff = extractEditDiff(
|
|
354
|
-
event.toolName,
|
|
355
|
-
t._toolArgs,
|
|
356
|
-
startLine
|
|
357
|
-
);
|
|
358
|
-
const finished = {
|
|
359
|
-
...t,
|
|
360
|
-
isRunning: false,
|
|
361
|
-
result: toolResult
|
|
362
|
-
};
|
|
363
|
-
if (editDiff) {
|
|
364
|
-
finished.diffLines = editDiff.lines;
|
|
365
|
-
finished.diffFile = editDiff.file;
|
|
366
|
-
}
|
|
367
|
-
delete finished._toolArgs;
|
|
368
|
-
return finished;
|
|
369
|
-
});
|
|
370
|
-
const completed = updated.filter((t) => !t.isRunning);
|
|
371
|
-
if (completed.length > MAX_COMPLETED_TOOLS) {
|
|
372
|
-
const excess = completed.length - MAX_COMPLETED_TOOLS;
|
|
373
|
-
let removed = 0;
|
|
374
|
-
return updated.filter((t) => {
|
|
375
|
-
if (!t.isRunning && removed < excess) {
|
|
376
|
-
removed++;
|
|
377
|
-
return false;
|
|
378
|
-
}
|
|
379
|
-
return true;
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
return updated;
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
};
|
|
386
|
-
const paths = (0, import_agent_sdk.projectPaths)(props.cwd ?? process.cwd());
|
|
387
|
-
sessionRef.current = (0, import_agent_sdk.createSession)({
|
|
388
|
-
config: props.config,
|
|
389
|
-
context: props.context,
|
|
390
|
-
terminal: NOOP_TERMINAL,
|
|
391
|
-
sessionLogger: new import_agent_sdk.FileSessionLogger(paths.logs),
|
|
392
|
-
projectInfo: props.projectInfo,
|
|
393
|
-
sessionStore: props.sessionStore,
|
|
394
|
-
permissionMode: props.permissionMode,
|
|
395
|
-
maxTurns: props.maxTurns,
|
|
396
|
-
permissionHandler,
|
|
397
|
-
onTextDelta,
|
|
398
|
-
onToolExecution
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
const clearStreamingText = (0, import_react.useCallback)(() => {
|
|
402
|
-
setStreamingText("");
|
|
403
|
-
streamingTextRef.current = "";
|
|
404
|
-
setActiveTools([]);
|
|
405
|
-
}, []);
|
|
406
|
-
return {
|
|
407
|
-
session: sessionRef.current,
|
|
408
|
-
permissionRequest,
|
|
409
|
-
streamingText,
|
|
410
|
-
clearStreamingText,
|
|
411
|
-
activeTools
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// src/ui/hooks/useMessages.ts
|
|
416
|
-
var import_react2 = require("react");
|
|
417
|
-
var MAX_RENDERED_MESSAGES = 100;
|
|
418
|
-
function useMessages() {
|
|
419
|
-
const [messages, setMessages] = (0, import_react2.useState)([]);
|
|
420
|
-
const addMessage = (0, import_react2.useCallback)((msg) => {
|
|
421
|
-
setMessages((prev) => {
|
|
422
|
-
const updated = [...prev, msg];
|
|
423
|
-
if (updated.length > MAX_RENDERED_MESSAGES) {
|
|
424
|
-
return updated.slice(-MAX_RENDERED_MESSAGES);
|
|
425
|
-
}
|
|
426
|
-
return updated;
|
|
427
|
-
});
|
|
428
|
-
}, []);
|
|
429
|
-
return { messages, setMessages, addMessage };
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// src/ui/hooks/useSlashCommands.ts
|
|
433
|
-
var import_react3 = require("react");
|
|
434
194
|
var import_agent_core = require("@robota-sdk/agent-core");
|
|
435
195
|
|
|
436
|
-
// src/commands/
|
|
437
|
-
var
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
" /compact [instr] \u2014 Compact context (optional focus instructions)",
|
|
443
|
-
" /mode [m] \u2014 Show/change permission mode",
|
|
444
|
-
" /language [lang] \u2014 Set response language (ko, en, ja, zh)",
|
|
445
|
-
" /cost \u2014 Show session info",
|
|
446
|
-
" /reset \u2014 Delete settings and exit",
|
|
447
|
-
" /exit \u2014 Exit CLI"
|
|
448
|
-
].join("\n");
|
|
449
|
-
function handleHelp(addMessage) {
|
|
450
|
-
addMessage({ role: "system", content: HELP_TEXT });
|
|
451
|
-
return { handled: true };
|
|
452
|
-
}
|
|
453
|
-
function handleClear(addMessage, clearMessages, session) {
|
|
454
|
-
clearMessages();
|
|
455
|
-
session.clearHistory();
|
|
456
|
-
addMessage({ role: "system", content: "Conversation cleared." });
|
|
457
|
-
return { handled: true };
|
|
458
|
-
}
|
|
459
|
-
async function handleCompact(args, session, addMessage) {
|
|
460
|
-
const instructions = args.trim() || void 0;
|
|
461
|
-
const before = session.getContextState().usedPercentage;
|
|
462
|
-
addMessage({ role: "system", content: "Compacting context..." });
|
|
463
|
-
await session.compact(instructions);
|
|
464
|
-
const after = session.getContextState().usedPercentage;
|
|
465
|
-
addMessage({
|
|
466
|
-
role: "system",
|
|
467
|
-
content: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`
|
|
468
|
-
});
|
|
469
|
-
return { handled: true };
|
|
470
|
-
}
|
|
471
|
-
function handleMode(arg, session, addMessage) {
|
|
472
|
-
if (!arg) {
|
|
473
|
-
addMessage({ role: "system", content: `Current mode: ${session.getPermissionMode()}` });
|
|
474
|
-
} else if (VALID_MODES2.includes(arg)) {
|
|
475
|
-
session.setPermissionMode(arg);
|
|
476
|
-
addMessage({ role: "system", content: `Permission mode set to: ${arg}` });
|
|
477
|
-
} else {
|
|
478
|
-
addMessage({ role: "system", content: `Invalid mode. Valid: ${VALID_MODES2.join(" | ")}` });
|
|
479
|
-
}
|
|
480
|
-
return { handled: true };
|
|
481
|
-
}
|
|
482
|
-
function handleModel(modelId, addMessage) {
|
|
483
|
-
if (!modelId) {
|
|
484
|
-
addMessage({ role: "system", content: "Select a model from the /model submenu." });
|
|
485
|
-
return { handled: true };
|
|
486
|
-
}
|
|
487
|
-
return { handled: true, pendingModelId: modelId };
|
|
488
|
-
}
|
|
489
|
-
function handleCost(session, addMessage) {
|
|
490
|
-
addMessage({
|
|
491
|
-
role: "system",
|
|
492
|
-
content: `Session: ${session.getSessionId()}
|
|
493
|
-
Messages: ${session.getMessageCount()}`
|
|
494
|
-
});
|
|
495
|
-
return { handled: true };
|
|
496
|
-
}
|
|
497
|
-
function handlePermissions(session, addMessage) {
|
|
498
|
-
const mode = session.getPermissionMode();
|
|
499
|
-
const sessionAllowed = session.getSessionAllowedTools();
|
|
500
|
-
const lines = [`Permission mode: ${mode}`];
|
|
501
|
-
if (sessionAllowed.length > 0) {
|
|
502
|
-
lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
|
|
503
|
-
} else {
|
|
504
|
-
lines.push("No session-approved tools.");
|
|
505
|
-
}
|
|
506
|
-
addMessage({ role: "system", content: lines.join("\n") });
|
|
507
|
-
return { handled: true };
|
|
508
|
-
}
|
|
509
|
-
function handleContext(session, addMessage) {
|
|
510
|
-
const ctx = session.getContextState();
|
|
511
|
-
addMessage({
|
|
512
|
-
role: "system",
|
|
513
|
-
content: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`
|
|
514
|
-
});
|
|
515
|
-
return { handled: true };
|
|
516
|
-
}
|
|
517
|
-
function handleLanguage(lang, addMessage) {
|
|
518
|
-
if (!lang) {
|
|
519
|
-
addMessage({ role: "system", content: "Usage: /language <code> (e.g., ko, en, ja, zh)" });
|
|
520
|
-
return { handled: true };
|
|
521
|
-
}
|
|
522
|
-
const settingsPath = getUserSettingsPath();
|
|
523
|
-
const settings = readSettings(settingsPath);
|
|
524
|
-
settings.language = lang;
|
|
525
|
-
writeSettings(settingsPath, settings);
|
|
526
|
-
addMessage({ role: "system", content: `Language set to "${lang}". Restarting...` });
|
|
527
|
-
return { handled: true, exitRequested: true };
|
|
528
|
-
}
|
|
529
|
-
function handleReset(addMessage) {
|
|
530
|
-
const settingsPath = getUserSettingsPath();
|
|
531
|
-
if (deleteSettings(settingsPath)) {
|
|
532
|
-
addMessage({ role: "system", content: `Deleted ${settingsPath}. Exiting...` });
|
|
533
|
-
} else {
|
|
534
|
-
addMessage({ role: "system", content: "No user settings found." });
|
|
535
|
-
}
|
|
536
|
-
return { handled: true, exitRequested: true };
|
|
537
|
-
}
|
|
538
|
-
async function handlePluginCommand(args, addMessage, callbacks) {
|
|
539
|
-
const parts = args.trim().split(/\s+/);
|
|
540
|
-
const subcommand = parts[0] ?? "";
|
|
541
|
-
const subArgs = parts.slice(1).join(" ").trim();
|
|
542
|
-
try {
|
|
543
|
-
switch (subcommand) {
|
|
544
|
-
case "":
|
|
545
|
-
case void 0:
|
|
546
|
-
case "manage": {
|
|
547
|
-
return { handled: true, triggerPluginTUI: true };
|
|
548
|
-
}
|
|
549
|
-
case "install": {
|
|
550
|
-
if (!subArgs) {
|
|
551
|
-
addMessage({ role: "system", content: "Usage: /plugin install <name>@<marketplace>" });
|
|
552
|
-
return { handled: true };
|
|
553
|
-
}
|
|
554
|
-
await callbacks.install(subArgs);
|
|
555
|
-
addMessage({ role: "system", content: `Installed plugin: ${subArgs}` });
|
|
556
|
-
return { handled: true };
|
|
557
|
-
}
|
|
558
|
-
case "uninstall": {
|
|
559
|
-
if (!subArgs) {
|
|
560
|
-
addMessage({ role: "system", content: "Usage: /plugin uninstall <name>@<marketplace>" });
|
|
561
|
-
return { handled: true };
|
|
562
|
-
}
|
|
563
|
-
await callbacks.uninstall(subArgs);
|
|
564
|
-
addMessage({ role: "system", content: `Uninstalled plugin: ${subArgs}` });
|
|
565
|
-
return { handled: true };
|
|
566
|
-
}
|
|
567
|
-
case "enable": {
|
|
568
|
-
if (!subArgs) {
|
|
569
|
-
addMessage({ role: "system", content: "Usage: /plugin enable <name>@<marketplace>" });
|
|
570
|
-
return { handled: true };
|
|
571
|
-
}
|
|
572
|
-
await callbacks.enable(subArgs);
|
|
573
|
-
addMessage({ role: "system", content: `Enabled plugin: ${subArgs}` });
|
|
574
|
-
return { handled: true };
|
|
575
|
-
}
|
|
576
|
-
case "disable": {
|
|
577
|
-
if (!subArgs) {
|
|
578
|
-
addMessage({ role: "system", content: "Usage: /plugin disable <name>@<marketplace>" });
|
|
579
|
-
return { handled: true };
|
|
580
|
-
}
|
|
581
|
-
await callbacks.disable(subArgs);
|
|
582
|
-
addMessage({ role: "system", content: `Disabled plugin: ${subArgs}` });
|
|
583
|
-
return { handled: true };
|
|
584
|
-
}
|
|
585
|
-
case "marketplace": {
|
|
586
|
-
const mpParts = subArgs.split(/\s+/);
|
|
587
|
-
const mpSubcommand = mpParts[0] ?? "";
|
|
588
|
-
const mpArgs = mpParts.slice(1).join(" ").trim();
|
|
589
|
-
if (mpSubcommand === "add" && mpArgs) {
|
|
590
|
-
const registeredName = await callbacks.marketplaceAdd(mpArgs);
|
|
591
|
-
addMessage({
|
|
592
|
-
role: "system",
|
|
593
|
-
content: `Added marketplace: "${registeredName}" (from ${mpArgs})
|
|
594
|
-
Install plugins with: /plugin install <name>@${registeredName}`
|
|
595
|
-
});
|
|
596
|
-
return { handled: true };
|
|
597
|
-
} else if (mpSubcommand === "remove" && mpArgs) {
|
|
598
|
-
await callbacks.marketplaceRemove(mpArgs);
|
|
599
|
-
addMessage({
|
|
600
|
-
role: "system",
|
|
601
|
-
content: `Removed marketplace "${mpArgs}" and uninstalled its plugins.`
|
|
602
|
-
});
|
|
603
|
-
return { handled: true };
|
|
604
|
-
} else if (mpSubcommand === "update" && mpArgs) {
|
|
605
|
-
await callbacks.marketplaceUpdate(mpArgs);
|
|
606
|
-
addMessage({
|
|
607
|
-
role: "system",
|
|
608
|
-
content: `Updated marketplace "${mpArgs}".`
|
|
609
|
-
});
|
|
610
|
-
return { handled: true };
|
|
611
|
-
} else if (mpSubcommand === "list") {
|
|
612
|
-
const sources = await callbacks.marketplaceList();
|
|
613
|
-
if (sources.length === 0) {
|
|
614
|
-
addMessage({ role: "system", content: "No marketplace sources configured." });
|
|
615
|
-
} else {
|
|
616
|
-
const lines = sources.map((s) => ` ${s.name} (${s.type})`);
|
|
617
|
-
addMessage({ role: "system", content: `Marketplace sources:
|
|
618
|
-
${lines.join("\n")}` });
|
|
619
|
-
}
|
|
620
|
-
return { handled: true };
|
|
621
|
-
} else {
|
|
622
|
-
addMessage({
|
|
623
|
-
role: "system",
|
|
624
|
-
content: "Usage: /plugin marketplace add <source> | remove <name> | update <name> | list"
|
|
625
|
-
});
|
|
626
|
-
return { handled: true };
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
default:
|
|
630
|
-
addMessage({ role: "system", content: `Unknown plugin subcommand: ${subcommand}` });
|
|
631
|
-
return { handled: true };
|
|
632
|
-
}
|
|
633
|
-
} catch (error) {
|
|
634
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
635
|
-
addMessage({ role: "system", content: `Plugin error: ${message}` });
|
|
636
|
-
return { handled: true };
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
async function handleReloadPlugins(addMessage, callbacks) {
|
|
640
|
-
await callbacks.reloadPlugins();
|
|
641
|
-
addMessage({ role: "system", content: "Plugins reload complete." });
|
|
642
|
-
return { handled: true };
|
|
643
|
-
}
|
|
644
|
-
async function executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry, pluginCallbacks) {
|
|
645
|
-
switch (cmd) {
|
|
646
|
-
case "help":
|
|
647
|
-
return handleHelp(addMessage);
|
|
648
|
-
case "clear":
|
|
649
|
-
return handleClear(addMessage, clearMessages, session);
|
|
650
|
-
case "compact":
|
|
651
|
-
return handleCompact(args, session, addMessage);
|
|
652
|
-
case "mode":
|
|
653
|
-
return handleMode(args.split(/\s+/)[0] || void 0, session, addMessage);
|
|
654
|
-
case "model":
|
|
655
|
-
return handleModel(args.split(/\s+/)[0] || void 0, addMessage);
|
|
656
|
-
case "language":
|
|
657
|
-
return handleLanguage(args.split(/\s+/)[0] || void 0, addMessage);
|
|
658
|
-
case "cost":
|
|
659
|
-
return handleCost(session, addMessage);
|
|
660
|
-
case "permissions":
|
|
661
|
-
return handlePermissions(session, addMessage);
|
|
662
|
-
case "context":
|
|
663
|
-
return handleContext(session, addMessage);
|
|
664
|
-
case "reset":
|
|
665
|
-
return handleReset(addMessage);
|
|
666
|
-
case "exit":
|
|
667
|
-
return { handled: true, exitRequested: true };
|
|
668
|
-
case "plugin":
|
|
669
|
-
if (pluginCallbacks) {
|
|
670
|
-
return handlePluginCommand(args, addMessage, pluginCallbacks);
|
|
671
|
-
}
|
|
672
|
-
addMessage({ role: "system", content: "Plugin management is not available." });
|
|
673
|
-
return { handled: true };
|
|
674
|
-
case "reload-plugins":
|
|
675
|
-
if (pluginCallbacks) {
|
|
676
|
-
return handleReloadPlugins(addMessage, pluginCallbacks);
|
|
677
|
-
}
|
|
678
|
-
addMessage({ role: "system", content: "Plugin management is not available." });
|
|
679
|
-
return { handled: true };
|
|
680
|
-
default: {
|
|
681
|
-
const dynamicCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
682
|
-
if (dynamicCmd) {
|
|
683
|
-
addMessage({ role: "system", content: `Invoking ${dynamicCmd.source}: ${cmd}` });
|
|
684
|
-
return { handled: false };
|
|
685
|
-
}
|
|
686
|
-
addMessage({ role: "system", content: `Unknown command "/${cmd}". Type /help for help.` });
|
|
687
|
-
return { handled: true };
|
|
688
|
-
}
|
|
196
|
+
// src/commands/plugin-source.ts
|
|
197
|
+
var PluginCommandSource = class {
|
|
198
|
+
name = "plugin";
|
|
199
|
+
plugins;
|
|
200
|
+
constructor(plugins) {
|
|
201
|
+
this.plugins = plugins;
|
|
689
202
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
addMessage((0, import_agent_core.createSystemMessage)(msg.content));
|
|
703
|
-
};
|
|
704
|
-
const result = await executeSlashCommand(
|
|
705
|
-
cmd,
|
|
706
|
-
args,
|
|
707
|
-
session,
|
|
708
|
-
slashAddMessage,
|
|
709
|
-
clearMessages,
|
|
710
|
-
registry,
|
|
711
|
-
pluginCallbacks
|
|
712
|
-
);
|
|
713
|
-
if (result.pendingModelId) {
|
|
714
|
-
pendingModelChangeRef.current = result.pendingModelId;
|
|
715
|
-
setPendingModelId(result.pendingModelId);
|
|
716
|
-
}
|
|
717
|
-
if (result.triggerPluginTUI) {
|
|
718
|
-
setShowPluginTUI?.(true);
|
|
719
|
-
}
|
|
720
|
-
if (result.exitRequested) {
|
|
721
|
-
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
203
|
+
getCommands() {
|
|
204
|
+
const commands = [];
|
|
205
|
+
for (const plugin of this.plugins) {
|
|
206
|
+
for (const skill of plugin.skills) {
|
|
207
|
+
const baseName = skill.name.includes("@") ? skill.name.split("@")[0] : skill.name;
|
|
208
|
+
commands.push({
|
|
209
|
+
name: baseName,
|
|
210
|
+
description: `(${plugin.manifest.name}) ${skill.description}`,
|
|
211
|
+
source: "plugin",
|
|
212
|
+
skillContent: skill.skillContent,
|
|
213
|
+
pluginDir: plugin.pluginDir
|
|
214
|
+
});
|
|
722
215
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
pendingModelChangeRef,
|
|
732
|
-
setPendingModelId,
|
|
733
|
-
pluginCallbacks,
|
|
734
|
-
setShowPluginTUI
|
|
735
|
-
]
|
|
736
|
-
);
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
// src/ui/hooks/useSubmitHandler.ts
|
|
740
|
-
var import_react4 = require("react");
|
|
741
|
-
var import_node_crypto = require("crypto");
|
|
742
|
-
var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
|
|
743
|
-
var import_agent_core2 = require("@robota-sdk/agent-core");
|
|
744
|
-
|
|
745
|
-
// src/utils/tool-call-extractor.ts
|
|
746
|
-
var TOOL_ARG_MAX_LENGTH = 80;
|
|
747
|
-
var TAIL_KEEP2 = 30;
|
|
748
|
-
function extractToolCallsWithDiff(history, startIndex) {
|
|
749
|
-
const summaries = [];
|
|
750
|
-
for (let i = startIndex; i < history.length; i++) {
|
|
751
|
-
const msg = history[i];
|
|
752
|
-
if (msg.role === "assistant" && msg.toolCalls) {
|
|
753
|
-
for (const tc of msg.toolCalls) {
|
|
754
|
-
const value = parseFirstArgValue(tc.function.arguments);
|
|
755
|
-
const truncated = value.length > TOOL_ARG_MAX_LENGTH ? value.slice(0, TOOL_ARG_MAX_LENGTH - TAIL_KEEP2 - 3) + "..." + value.slice(-TAIL_KEEP2) : value;
|
|
756
|
-
const summary = {
|
|
757
|
-
line: `${tc.function.name}(${truncated})`
|
|
758
|
-
};
|
|
759
|
-
if (tc.function.name === "Edit") {
|
|
760
|
-
try {
|
|
761
|
-
const args = JSON.parse(tc.function.arguments);
|
|
762
|
-
const diff = extractEditDiff("Edit", args);
|
|
763
|
-
if (diff) {
|
|
764
|
-
summary.diffLines = diff.lines;
|
|
765
|
-
summary.diffFile = diff.file;
|
|
766
|
-
}
|
|
767
|
-
} catch {
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
summaries.push(summary);
|
|
216
|
+
for (const cmd of plugin.commands) {
|
|
217
|
+
commands.push({
|
|
218
|
+
name: cmd.name,
|
|
219
|
+
description: cmd.description,
|
|
220
|
+
source: "plugin",
|
|
221
|
+
skillContent: cmd.skillContent,
|
|
222
|
+
pluginDir: plugin.pluginDir
|
|
223
|
+
});
|
|
771
224
|
}
|
|
772
225
|
}
|
|
226
|
+
return commands;
|
|
773
227
|
}
|
|
774
|
-
|
|
775
|
-
}
|
|
776
|
-
function parseFirstArgValue(argsJson) {
|
|
777
|
-
try {
|
|
778
|
-
const parsed = JSON.parse(argsJson);
|
|
779
|
-
const firstVal = Object.values(parsed)[0];
|
|
780
|
-
return typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
|
|
781
|
-
} catch {
|
|
782
|
-
return argsJson;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
228
|
+
};
|
|
785
229
|
|
|
786
230
|
// src/utils/skill-prompt.ts
|
|
787
231
|
var import_node_child_process = require("child_process");
|
|
@@ -816,517 +260,62 @@ async function preprocessShellCommands(content) {
|
|
|
816
260
|
try {
|
|
817
261
|
output = (0, import_node_child_process.execSync)(command, {
|
|
818
262
|
timeout: 5e3,
|
|
819
|
-
encoding: "utf-8",
|
|
820
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
821
|
-
}).trimEnd();
|
|
822
|
-
} catch {
|
|
823
|
-
output = "";
|
|
824
|
-
}
|
|
825
|
-
result = result.replace(full, output);
|
|
826
|
-
}
|
|
827
|
-
return result;
|
|
828
|
-
}
|
|
829
|
-
async function buildSkillPrompt(input, registry, context) {
|
|
830
|
-
const parts = input.slice(1).split(/\s+/);
|
|
831
|
-
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
832
|
-
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
833
|
-
if (!skillCmd) return null;
|
|
834
|
-
const args = parts.slice(1).join(" ").trim();
|
|
835
|
-
const userInstruction = args || skillCmd.description;
|
|
836
|
-
if (skillCmd.skillContent) {
|
|
837
|
-
let processed = await preprocessShellCommands(skillCmd.skillContent);
|
|
838
|
-
processed = substituteVariables(processed, args, context);
|
|
839
|
-
return `<skill name="${cmd}">
|
|
840
|
-
${processed}
|
|
841
|
-
</skill>
|
|
842
|
-
|
|
843
|
-
Execute the "${cmd}" skill: ${userInstruction}`;
|
|
844
|
-
}
|
|
845
|
-
return `Use the "${cmd}" skill: ${userInstruction}`;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
// src/commands/skill-executor.ts
|
|
849
|
-
function buildProcessedContent(skill, args, context) {
|
|
850
|
-
if (!skill.skillContent) return null;
|
|
851
|
-
return substituteVariables(skill.skillContent, args, context);
|
|
852
|
-
}
|
|
853
|
-
function buildInjectPrompt(skill, args, context) {
|
|
854
|
-
const processed = buildProcessedContent(skill, args, context);
|
|
855
|
-
if (processed) {
|
|
856
|
-
const userInstruction = args || skill.description;
|
|
857
|
-
return `<skill name="${skill.name}">
|
|
858
|
-
${processed}
|
|
859
|
-
</skill>
|
|
860
|
-
|
|
861
|
-
Execute the "${skill.name}" skill: ${userInstruction}`;
|
|
862
|
-
}
|
|
863
|
-
return `Use the "${skill.name}" skill: ${args || skill.description}`;
|
|
864
|
-
}
|
|
865
|
-
async function executeSkill(skill, args, callbacks, context) {
|
|
866
|
-
if (skill.context === "fork") {
|
|
867
|
-
if (!callbacks.runInFork) {
|
|
868
|
-
throw new Error("Fork execution is not available. Agent tool deps may not be initialized.");
|
|
869
|
-
}
|
|
870
|
-
const content = buildProcessedContent(skill, args, context);
|
|
871
|
-
const prompt2 = content ?? `Use the "${skill.name}" skill: ${args || skill.description}`;
|
|
872
|
-
const options = {};
|
|
873
|
-
if (skill.agent) options.agent = skill.agent;
|
|
874
|
-
if (skill.allowedTools) options.allowedTools = skill.allowedTools;
|
|
875
|
-
const result = await callbacks.runInFork(prompt2, options);
|
|
876
|
-
return { mode: "fork", result };
|
|
877
|
-
}
|
|
878
|
-
const prompt = buildInjectPrompt(skill, args, context);
|
|
879
|
-
return { mode: "inject", prompt };
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
// src/ui/hooks/useSubmitHandler.ts
|
|
883
|
-
function syncContextState(session, setter) {
|
|
884
|
-
const ctx = session.getContextState();
|
|
885
|
-
setter({ percentage: ctx.usedPercentage, usedTokens: ctx.usedTokens, maxTokens: ctx.maxTokens });
|
|
886
|
-
}
|
|
887
|
-
async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextState, rawInput) {
|
|
888
|
-
setIsThinking(true);
|
|
889
|
-
clearStreamingText();
|
|
890
|
-
const historyBefore = session.getHistory().length;
|
|
891
|
-
try {
|
|
892
|
-
const response = await session.run(prompt, rawInput);
|
|
893
|
-
clearStreamingText();
|
|
894
|
-
const history = session.getHistory();
|
|
895
|
-
const toolSummaries = extractToolCallsWithDiff(
|
|
896
|
-
history,
|
|
897
|
-
historyBefore
|
|
898
|
-
);
|
|
899
|
-
if (toolSummaries.length > 0) {
|
|
900
|
-
addMessage(
|
|
901
|
-
(0, import_agent_core2.createToolMessage)(JSON.stringify(toolSummaries), {
|
|
902
|
-
toolCallId: (0, import_node_crypto.randomUUID)(),
|
|
903
|
-
name: `${toolSummaries.length} tools`
|
|
904
|
-
})
|
|
905
|
-
);
|
|
906
|
-
}
|
|
907
|
-
addMessage((0, import_agent_core2.createAssistantMessage)(response || "(empty response)"));
|
|
908
|
-
syncContextState(session, setContextState);
|
|
909
|
-
} catch (err) {
|
|
910
|
-
clearStreamingText();
|
|
911
|
-
const isAbortError = err instanceof DOMException && err.name === "AbortError" || err instanceof Error && (err.message.includes("aborted") || err.message.includes("abort"));
|
|
912
|
-
if (isAbortError) {
|
|
913
|
-
const history = session.getHistory();
|
|
914
|
-
const toolSummaries = extractToolCallsWithDiff(
|
|
915
|
-
history,
|
|
916
|
-
historyBefore
|
|
917
|
-
);
|
|
918
|
-
if (toolSummaries.length > 0) {
|
|
919
|
-
addMessage(
|
|
920
|
-
(0, import_agent_core2.createToolMessage)(JSON.stringify(toolSummaries), {
|
|
921
|
-
toolCallId: (0, import_node_crypto.randomUUID)(),
|
|
922
|
-
name: `${toolSummaries.length} tools`
|
|
923
|
-
})
|
|
924
|
-
);
|
|
925
|
-
}
|
|
926
|
-
const assistantParts = [];
|
|
927
|
-
let lastAssistantState = "complete";
|
|
928
|
-
for (let i = historyBefore; i < history.length; i++) {
|
|
929
|
-
const msg = history[i];
|
|
930
|
-
if (msg && msg.role === "assistant" && msg.content) {
|
|
931
|
-
assistantParts.push(msg.content);
|
|
932
|
-
if (msg.state === "interrupted") lastAssistantState = "interrupted";
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
if (assistantParts.length > 0) {
|
|
936
|
-
addMessage(
|
|
937
|
-
(0, import_agent_core2.createAssistantMessage)(assistantParts.join("\n\n"), { state: lastAssistantState })
|
|
938
|
-
);
|
|
939
|
-
}
|
|
940
|
-
addMessage((0, import_agent_core2.createSystemMessage)("Interrupted by user."));
|
|
941
|
-
} else {
|
|
942
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
943
|
-
addMessage((0, import_agent_core2.createSystemMessage)(`Error: ${errMsg}`));
|
|
944
|
-
}
|
|
945
|
-
} finally {
|
|
946
|
-
setIsThinking(false);
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
function createForkRunner(sessionKey) {
|
|
950
|
-
const deps = (0, import_agent_sdk2.retrieveAgentToolDeps)(sessionKey);
|
|
951
|
-
if (!deps) return void 0;
|
|
952
|
-
return async (content, options) => {
|
|
953
|
-
const agentType = options.agent ?? "general-purpose";
|
|
954
|
-
const agentDef = (0, import_agent_sdk2.getBuiltInAgent)(agentType) ?? deps.customAgentRegistry?.(agentType);
|
|
955
|
-
if (!agentDef) {
|
|
956
|
-
throw new Error(`Unknown agent type for fork execution: ${agentType}`);
|
|
957
|
-
}
|
|
958
|
-
const effectiveDef = options.allowedTools ? { ...agentDef, tools: options.allowedTools } : agentDef;
|
|
959
|
-
const subSession = (0, import_agent_sdk2.createSubagentSession)({
|
|
960
|
-
agentDefinition: effectiveDef,
|
|
961
|
-
parentConfig: deps.config,
|
|
962
|
-
parentContext: deps.context,
|
|
963
|
-
parentTools: deps.tools,
|
|
964
|
-
terminal: deps.terminal,
|
|
965
|
-
isForkWorker: true,
|
|
966
|
-
permissionHandler: deps.permissionHandler,
|
|
967
|
-
onTextDelta: deps.onTextDelta,
|
|
968
|
-
onToolExecution: deps.onToolExecution
|
|
969
|
-
});
|
|
970
|
-
return await subSession.run(content);
|
|
971
|
-
};
|
|
972
|
-
}
|
|
973
|
-
function findSkillCommand(input, registry) {
|
|
974
|
-
const parts = input.slice(1).split(/\s+/);
|
|
975
|
-
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
976
|
-
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
977
|
-
if (!skillCmd) return null;
|
|
978
|
-
return { skill: skillCmd, args: parts.slice(1).join(" ").trim() };
|
|
979
|
-
}
|
|
980
|
-
function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry) {
|
|
981
|
-
return (0, import_react4.useCallback)(
|
|
982
|
-
async (input) => {
|
|
983
|
-
if (input.startsWith("/")) {
|
|
984
|
-
const handled = await handleSlashCommand(input);
|
|
985
|
-
if (handled) {
|
|
986
|
-
syncContextState(session, setContextState);
|
|
987
|
-
return;
|
|
988
|
-
}
|
|
989
|
-
const found = findSkillCommand(input, registry);
|
|
990
|
-
if (!found) return;
|
|
991
|
-
const { skill, args } = found;
|
|
992
|
-
if (skill.context === "fork") {
|
|
993
|
-
const runInFork = createForkRunner(session);
|
|
994
|
-
const result = await executeSkill(skill, args, { runInFork });
|
|
995
|
-
if (result.mode === "fork") {
|
|
996
|
-
addMessage((0, import_agent_core2.createAssistantMessage)(result.result ?? "(empty response)"));
|
|
997
|
-
syncContextState(session, setContextState);
|
|
998
|
-
return;
|
|
999
|
-
}
|
|
1000
|
-
if (result.prompt) {
|
|
1001
|
-
const cmdName2 = input.slice(1).split(/\s+/)[0]?.toLowerCase() ?? "";
|
|
1002
|
-
const qualifiedName2 = registry.resolveQualifiedName(cmdName2);
|
|
1003
|
-
const hookInput2 = qualifiedName2 ? `/${qualifiedName2}${input.slice(1 + cmdName2.length)}` : input;
|
|
1004
|
-
return runSessionPrompt(
|
|
1005
|
-
result.prompt,
|
|
1006
|
-
session,
|
|
1007
|
-
addMessage,
|
|
1008
|
-
clearStreamingText,
|
|
1009
|
-
setIsThinking,
|
|
1010
|
-
setContextState,
|
|
1011
|
-
hookInput2
|
|
1012
|
-
);
|
|
1013
|
-
}
|
|
1014
|
-
return;
|
|
1015
|
-
}
|
|
1016
|
-
const prompt = await buildSkillPrompt(input, registry);
|
|
1017
|
-
if (!prompt) return;
|
|
1018
|
-
const cmdName = input.slice(1).split(/\s+/)[0]?.toLowerCase() ?? "";
|
|
1019
|
-
const qualifiedName = registry.resolveQualifiedName(cmdName);
|
|
1020
|
-
const hookInput = qualifiedName ? `/${qualifiedName}${input.slice(1 + cmdName.length)}` : input;
|
|
1021
|
-
return runSessionPrompt(
|
|
1022
|
-
prompt,
|
|
1023
|
-
session,
|
|
1024
|
-
addMessage,
|
|
1025
|
-
clearStreamingText,
|
|
1026
|
-
setIsThinking,
|
|
1027
|
-
setContextState,
|
|
1028
|
-
hookInput
|
|
1029
|
-
);
|
|
1030
|
-
}
|
|
1031
|
-
addMessage((0, import_agent_core2.createUserMessage)(input));
|
|
1032
|
-
return runSessionPrompt(
|
|
1033
|
-
input,
|
|
1034
|
-
session,
|
|
1035
|
-
addMessage,
|
|
1036
|
-
clearStreamingText,
|
|
1037
|
-
setIsThinking,
|
|
1038
|
-
setContextState
|
|
1039
|
-
);
|
|
1040
|
-
},
|
|
1041
|
-
[
|
|
1042
|
-
session,
|
|
1043
|
-
addMessage,
|
|
1044
|
-
handleSlashCommand,
|
|
1045
|
-
clearStreamingText,
|
|
1046
|
-
setIsThinking,
|
|
1047
|
-
setContextState,
|
|
1048
|
-
registry
|
|
1049
|
-
]
|
|
1050
|
-
);
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
// src/ui/hooks/useCommandRegistry.ts
|
|
1054
|
-
var import_react5 = require("react");
|
|
1055
|
-
var import_node_os2 = require("os");
|
|
1056
|
-
var import_node_path3 = require("path");
|
|
1057
|
-
var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
|
|
1058
|
-
|
|
1059
|
-
// src/commands/command-registry.ts
|
|
1060
|
-
var CommandRegistry = class {
|
|
1061
|
-
sources = [];
|
|
1062
|
-
addSource(source) {
|
|
1063
|
-
this.sources.push(source);
|
|
1064
|
-
}
|
|
1065
|
-
/** Get all commands, optionally filtered by prefix */
|
|
1066
|
-
getCommands(filter) {
|
|
1067
|
-
const all = [];
|
|
1068
|
-
for (const source of this.sources) {
|
|
1069
|
-
all.push(...source.getCommands());
|
|
1070
|
-
}
|
|
1071
|
-
if (!filter) return all;
|
|
1072
|
-
const lower = filter.toLowerCase();
|
|
1073
|
-
return all.filter((cmd) => cmd.name.toLowerCase().startsWith(lower));
|
|
1074
|
-
}
|
|
1075
|
-
/** Resolve a short name to its fully qualified plugin:name form */
|
|
1076
|
-
resolveQualifiedName(shortName) {
|
|
1077
|
-
const matches = this.getCommands().filter(
|
|
1078
|
-
(c) => c.source === "plugin" && c.name.includes(":") && c.name.endsWith(`:${shortName}`)
|
|
1079
|
-
);
|
|
1080
|
-
if (matches.length !== 1) return null;
|
|
1081
|
-
return matches[0].name;
|
|
1082
|
-
}
|
|
1083
|
-
/** Get subcommands for a specific command */
|
|
1084
|
-
getSubcommands(commandName) {
|
|
1085
|
-
const lower = commandName.toLowerCase();
|
|
1086
|
-
for (const source of this.sources) {
|
|
1087
|
-
for (const cmd of source.getCommands()) {
|
|
1088
|
-
if (cmd.name.toLowerCase() === lower && cmd.subcommands) {
|
|
1089
|
-
return cmd.subcommands;
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
return [];
|
|
1094
|
-
}
|
|
1095
|
-
};
|
|
1096
|
-
|
|
1097
|
-
// src/commands/builtin-source.ts
|
|
1098
|
-
var import_agent_core3 = require("@robota-sdk/agent-core");
|
|
1099
|
-
function buildModelSubcommands() {
|
|
1100
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1101
|
-
const commands = [];
|
|
1102
|
-
for (const model of Object.values(import_agent_core3.CLAUDE_MODELS)) {
|
|
1103
|
-
if (seen.has(model.name)) continue;
|
|
1104
|
-
seen.add(model.name);
|
|
1105
|
-
commands.push({
|
|
1106
|
-
name: model.id,
|
|
1107
|
-
description: `${model.name} (${(0, import_agent_core3.formatTokenCount)(model.contextWindow).toUpperCase()})`,
|
|
1108
|
-
source: "builtin"
|
|
1109
|
-
});
|
|
1110
|
-
}
|
|
1111
|
-
return commands;
|
|
1112
|
-
}
|
|
1113
|
-
function createBuiltinCommands() {
|
|
1114
|
-
return [
|
|
1115
|
-
{ name: "help", description: "Show available commands", source: "builtin" },
|
|
1116
|
-
{ name: "clear", description: "Clear conversation history", source: "builtin" },
|
|
1117
|
-
{
|
|
1118
|
-
name: "mode",
|
|
1119
|
-
description: "Permission mode",
|
|
1120
|
-
source: "builtin",
|
|
1121
|
-
subcommands: [
|
|
1122
|
-
{ name: "plan", description: "Plan only, no execution", source: "builtin" },
|
|
1123
|
-
{ name: "default", description: "Ask before risky actions", source: "builtin" },
|
|
1124
|
-
{ name: "acceptEdits", description: "Auto-approve file edits", source: "builtin" },
|
|
1125
|
-
{ name: "bypassPermissions", description: "Skip all permission checks", source: "builtin" }
|
|
1126
|
-
]
|
|
1127
|
-
},
|
|
1128
|
-
{
|
|
1129
|
-
name: "model",
|
|
1130
|
-
description: "Select AI model",
|
|
1131
|
-
source: "builtin",
|
|
1132
|
-
subcommands: buildModelSubcommands()
|
|
1133
|
-
},
|
|
1134
|
-
{
|
|
1135
|
-
name: "language",
|
|
1136
|
-
description: "Set response language",
|
|
1137
|
-
source: "builtin",
|
|
1138
|
-
subcommands: [
|
|
1139
|
-
{ name: "ko", description: "Korean", source: "builtin" },
|
|
1140
|
-
{ name: "en", description: "English", source: "builtin" },
|
|
1141
|
-
{ name: "ja", description: "Japanese", source: "builtin" },
|
|
1142
|
-
{ name: "zh", description: "Chinese", source: "builtin" }
|
|
1143
|
-
]
|
|
1144
|
-
},
|
|
1145
|
-
{ name: "compact", description: "Compress context window", source: "builtin" },
|
|
1146
|
-
{ name: "cost", description: "Show session info", source: "builtin" },
|
|
1147
|
-
{ name: "context", description: "Context window info", source: "builtin" },
|
|
1148
|
-
{ name: "permissions", description: "Permission rules", source: "builtin" },
|
|
1149
|
-
{ name: "plugin", description: "Manage plugins", source: "builtin" },
|
|
1150
|
-
{ name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
|
|
1151
|
-
{ name: "reset", description: "Delete settings and exit", source: "builtin" },
|
|
1152
|
-
{ name: "exit", description: "Exit CLI", source: "builtin" }
|
|
1153
|
-
];
|
|
1154
|
-
}
|
|
1155
|
-
var BuiltinCommandSource = class {
|
|
1156
|
-
name = "builtin";
|
|
1157
|
-
commands;
|
|
1158
|
-
constructor() {
|
|
1159
|
-
this.commands = createBuiltinCommands();
|
|
1160
|
-
}
|
|
1161
|
-
getCommands() {
|
|
1162
|
-
return this.commands;
|
|
1163
|
-
}
|
|
1164
|
-
};
|
|
1165
|
-
|
|
1166
|
-
// src/commands/skill-source.ts
|
|
1167
|
-
var import_node_fs3 = require("fs");
|
|
1168
|
-
var import_node_path2 = require("path");
|
|
1169
|
-
var import_node_os = require("os");
|
|
1170
|
-
var BOOLEAN_KEYS = /* @__PURE__ */ new Set(["disable-model-invocation", "user-invocable"]);
|
|
1171
|
-
var LIST_KEYS = /* @__PURE__ */ new Set(["allowed-tools"]);
|
|
1172
|
-
function kebabToCamel(key) {
|
|
1173
|
-
return key.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
|
|
1174
|
-
}
|
|
1175
|
-
function parseFrontmatter(content) {
|
|
1176
|
-
const lines = content.split("\n");
|
|
1177
|
-
if (lines[0]?.trim() !== "---") return null;
|
|
1178
|
-
const result = {};
|
|
1179
|
-
for (let i = 1; i < lines.length; i++) {
|
|
1180
|
-
const line = lines[i];
|
|
1181
|
-
if (line.trim() === "---") break;
|
|
1182
|
-
const match = line.match(/^([a-z][a-z0-9-]*):\s*(.+)/);
|
|
1183
|
-
if (!match) continue;
|
|
1184
|
-
const key = match[1];
|
|
1185
|
-
const rawValue = match[2].trim();
|
|
1186
|
-
const camelKey = kebabToCamel(key);
|
|
1187
|
-
if (BOOLEAN_KEYS.has(key)) {
|
|
1188
|
-
result[camelKey] = rawValue === "true";
|
|
1189
|
-
} else if (LIST_KEYS.has(key)) {
|
|
1190
|
-
result[camelKey] = rawValue.split(",").map((s) => s.trim());
|
|
1191
|
-
} else {
|
|
1192
|
-
result[camelKey] = rawValue;
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
return Object.keys(result).length > 0 ? result : null;
|
|
1196
|
-
}
|
|
1197
|
-
function buildCommand(frontmatter, content, fallbackName) {
|
|
1198
|
-
const cmd = {
|
|
1199
|
-
name: frontmatter?.name ?? fallbackName,
|
|
1200
|
-
description: frontmatter?.description ?? `Skill: ${fallbackName}`,
|
|
1201
|
-
source: "skill",
|
|
1202
|
-
skillContent: content
|
|
1203
|
-
};
|
|
1204
|
-
if (frontmatter?.argumentHint !== void 0) cmd.argumentHint = frontmatter.argumentHint;
|
|
1205
|
-
if (frontmatter?.disableModelInvocation !== void 0)
|
|
1206
|
-
cmd.disableModelInvocation = frontmatter.disableModelInvocation;
|
|
1207
|
-
if (frontmatter?.userInvocable !== void 0) cmd.userInvocable = frontmatter.userInvocable;
|
|
1208
|
-
if (frontmatter?.allowedTools !== void 0) cmd.allowedTools = frontmatter.allowedTools;
|
|
1209
|
-
if (frontmatter?.model !== void 0) cmd.model = frontmatter.model;
|
|
1210
|
-
if (frontmatter?.effort !== void 0) cmd.effort = frontmatter.effort;
|
|
1211
|
-
if (frontmatter?.context !== void 0) cmd.context = frontmatter.context;
|
|
1212
|
-
if (frontmatter?.agent !== void 0) cmd.agent = frontmatter.agent;
|
|
1213
|
-
return cmd;
|
|
1214
|
-
}
|
|
1215
|
-
function scanSkillsDir(skillsDir) {
|
|
1216
|
-
if (!(0, import_node_fs3.existsSync)(skillsDir)) return [];
|
|
1217
|
-
const commands = [];
|
|
1218
|
-
const entries = (0, import_node_fs3.readdirSync)(skillsDir, { withFileTypes: true });
|
|
1219
|
-
for (const entry of entries) {
|
|
1220
|
-
if (!entry.isDirectory()) continue;
|
|
1221
|
-
const skillFile = (0, import_node_path2.join)(skillsDir, entry.name, "SKILL.md");
|
|
1222
|
-
if (!(0, import_node_fs3.existsSync)(skillFile)) continue;
|
|
1223
|
-
const content = (0, import_node_fs3.readFileSync)(skillFile, "utf-8");
|
|
1224
|
-
const frontmatter = parseFrontmatter(content);
|
|
1225
|
-
commands.push(buildCommand(frontmatter, content, entry.name));
|
|
1226
|
-
}
|
|
1227
|
-
return commands;
|
|
1228
|
-
}
|
|
1229
|
-
function scanCommandsDir(commandsDir) {
|
|
1230
|
-
if (!(0, import_node_fs3.existsSync)(commandsDir)) return [];
|
|
1231
|
-
const commands = [];
|
|
1232
|
-
const entries = (0, import_node_fs3.readdirSync)(commandsDir, { withFileTypes: true });
|
|
1233
|
-
for (const entry of entries) {
|
|
1234
|
-
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
1235
|
-
const filePath = (0, import_node_path2.join)(commandsDir, entry.name);
|
|
1236
|
-
const content = (0, import_node_fs3.readFileSync)(filePath, "utf-8");
|
|
1237
|
-
const frontmatter = parseFrontmatter(content);
|
|
1238
|
-
const fallbackName = (0, import_node_path2.basename)(entry.name, ".md");
|
|
1239
|
-
commands.push(buildCommand(frontmatter, content, fallbackName));
|
|
1240
|
-
}
|
|
1241
|
-
return commands;
|
|
1242
|
-
}
|
|
1243
|
-
var SkillCommandSource = class {
|
|
1244
|
-
name = "skill";
|
|
1245
|
-
cwd;
|
|
1246
|
-
home;
|
|
1247
|
-
cachedCommands = null;
|
|
1248
|
-
constructor(cwd, home) {
|
|
1249
|
-
this.cwd = cwd;
|
|
1250
|
-
this.home = home ?? (0, import_node_os.homedir)();
|
|
1251
|
-
}
|
|
1252
|
-
getCommands() {
|
|
1253
|
-
if (this.cachedCommands) return this.cachedCommands;
|
|
1254
|
-
const sources = [
|
|
1255
|
-
scanSkillsDir((0, import_node_path2.join)(this.cwd, ".claude", "skills")),
|
|
1256
|
-
// 1. project .claude/skills
|
|
1257
|
-
scanCommandsDir((0, import_node_path2.join)(this.cwd, ".claude", "commands")),
|
|
1258
|
-
// 2. project .claude/commands (legacy)
|
|
1259
|
-
scanSkillsDir((0, import_node_path2.join)(this.home, ".robota", "skills")),
|
|
1260
|
-
// 3. user ~/.robota/skills
|
|
1261
|
-
scanSkillsDir((0, import_node_path2.join)(this.cwd, ".agents", "skills"))
|
|
1262
|
-
// 4. project .agents/skills
|
|
1263
|
-
];
|
|
1264
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1265
|
-
const merged = [];
|
|
1266
|
-
for (const commands of sources) {
|
|
1267
|
-
for (const cmd of commands) {
|
|
1268
|
-
if (!seen.has(cmd.name)) {
|
|
1269
|
-
seen.add(cmd.name);
|
|
1270
|
-
merged.push(cmd);
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
263
|
+
encoding: "utf-8",
|
|
264
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
265
|
+
}).trimEnd();
|
|
266
|
+
} catch {
|
|
267
|
+
output = "";
|
|
1273
268
|
}
|
|
1274
|
-
|
|
1275
|
-
return this.cachedCommands;
|
|
1276
|
-
}
|
|
1277
|
-
/** Get skills that models can invoke (excludes disableModelInvocation: true) */
|
|
1278
|
-
getModelInvocableSkills() {
|
|
1279
|
-
return this.getCommands().filter((cmd) => cmd.disableModelInvocation !== true);
|
|
1280
|
-
}
|
|
1281
|
-
/** Get skills that users can invoke (excludes userInvocable: false) */
|
|
1282
|
-
getUserInvocableSkills() {
|
|
1283
|
-
return this.getCommands().filter((cmd) => cmd.userInvocable !== false);
|
|
269
|
+
result = result.replace(full, output);
|
|
1284
270
|
}
|
|
1285
|
-
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
async function buildSkillPrompt(input, registry, context) {
|
|
274
|
+
const parts = input.slice(1).split(/\s+/);
|
|
275
|
+
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
276
|
+
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
277
|
+
if (!skillCmd) return null;
|
|
278
|
+
const args = parts.slice(1).join(" ").trim();
|
|
279
|
+
const userInstruction = args || skillCmd.description;
|
|
280
|
+
if (skillCmd.skillContent) {
|
|
281
|
+
let processed = await preprocessShellCommands(skillCmd.skillContent);
|
|
282
|
+
processed = substituteVariables(processed, args, context);
|
|
283
|
+
return `<skill name="${cmd}">
|
|
284
|
+
${processed}
|
|
285
|
+
</skill>
|
|
1286
286
|
|
|
1287
|
-
|
|
1288
|
-
var PluginCommandSource = class {
|
|
1289
|
-
name = "plugin";
|
|
1290
|
-
plugins;
|
|
1291
|
-
constructor(plugins) {
|
|
1292
|
-
this.plugins = plugins;
|
|
1293
|
-
}
|
|
1294
|
-
getCommands() {
|
|
1295
|
-
const commands = [];
|
|
1296
|
-
for (const plugin of this.plugins) {
|
|
1297
|
-
for (const skill of plugin.skills) {
|
|
1298
|
-
const baseName = skill.name.includes("@") ? skill.name.split("@")[0] : skill.name;
|
|
1299
|
-
commands.push({
|
|
1300
|
-
name: baseName,
|
|
1301
|
-
description: `(${plugin.manifest.name}) ${skill.description}`,
|
|
1302
|
-
source: "plugin",
|
|
1303
|
-
skillContent: skill.skillContent,
|
|
1304
|
-
pluginDir: plugin.pluginDir
|
|
1305
|
-
});
|
|
1306
|
-
}
|
|
1307
|
-
for (const cmd of plugin.commands) {
|
|
1308
|
-
commands.push({
|
|
1309
|
-
name: cmd.name,
|
|
1310
|
-
description: cmd.description,
|
|
1311
|
-
source: "plugin",
|
|
1312
|
-
skillContent: cmd.skillContent,
|
|
1313
|
-
pluginDir: plugin.pluginDir
|
|
1314
|
-
});
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
return commands;
|
|
287
|
+
Execute the "${cmd}" skill: ${userInstruction}`;
|
|
1318
288
|
}
|
|
1319
|
-
}
|
|
289
|
+
return `Use the "${cmd}" skill: ${userInstruction}`;
|
|
290
|
+
}
|
|
1320
291
|
|
|
1321
|
-
// src/ui/hooks/
|
|
292
|
+
// src/ui/hooks/plugin-hooks-merger.ts
|
|
293
|
+
var import_node_path2 = require("path");
|
|
1322
294
|
function buildPluginEnv(plugin) {
|
|
1323
|
-
const dataDir = (0,
|
|
295
|
+
const dataDir = (0, import_node_path2.join)((0, import_node_path2.dirname)((0, import_node_path2.dirname)(plugin.pluginDir)), "data", plugin.manifest.name);
|
|
1324
296
|
return {
|
|
1325
297
|
CLAUDE_PLUGIN_ROOT: plugin.pluginDir,
|
|
1326
298
|
CLAUDE_PLUGIN_PATH: plugin.pluginDir,
|
|
1327
299
|
CLAUDE_PLUGIN_DATA: dataDir
|
|
1328
300
|
};
|
|
1329
301
|
}
|
|
302
|
+
function resolvePluginRoot(group, pluginDir) {
|
|
303
|
+
if (Array.isArray(group.hooks)) {
|
|
304
|
+
return {
|
|
305
|
+
...group,
|
|
306
|
+
hooks: group.hooks.map((h) => {
|
|
307
|
+
if (typeof h.command === "string") {
|
|
308
|
+
return {
|
|
309
|
+
...h,
|
|
310
|
+
command: h.command.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginDir)
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
return h;
|
|
314
|
+
})
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
return group;
|
|
318
|
+
}
|
|
1330
319
|
function mergePluginHooks(plugins) {
|
|
1331
320
|
const merged = {};
|
|
1332
321
|
for (const plugin of plugins) {
|
|
@@ -1338,78 +327,299 @@ function mergePluginHooks(plugins) {
|
|
|
1338
327
|
if (!Array.isArray(groups)) continue;
|
|
1339
328
|
if (!merged[event]) merged[event] = [];
|
|
1340
329
|
const resolved = groups.map((group) => {
|
|
1341
|
-
const
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
}
|
|
1345
|
-
return resolved2;
|
|
330
|
+
const r = resolvePluginRoot(group, plugin.pluginDir);
|
|
331
|
+
r.env = pluginEnv;
|
|
332
|
+
return r;
|
|
1346
333
|
});
|
|
1347
334
|
merged[event].push(...resolved);
|
|
1348
335
|
}
|
|
1349
336
|
}
|
|
1350
337
|
return merged;
|
|
1351
338
|
}
|
|
1352
|
-
function
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
hooks: obj.hooks.map((h) => {
|
|
1359
|
-
if (typeof h !== "object" || h === null) return h;
|
|
1360
|
-
const hook = h;
|
|
1361
|
-
if (typeof hook.command === "string") {
|
|
1362
|
-
return {
|
|
1363
|
-
...hook,
|
|
1364
|
-
command: hook.command.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginDir)
|
|
1365
|
-
};
|
|
1366
|
-
}
|
|
1367
|
-
return hook;
|
|
1368
|
-
})
|
|
1369
|
-
};
|
|
339
|
+
function mergeHooksIntoConfig(configHooks, pluginHooks) {
|
|
340
|
+
const pluginKeys = Object.keys(pluginHooks);
|
|
341
|
+
if (pluginKeys.length === 0) return configHooks;
|
|
342
|
+
const merged = {};
|
|
343
|
+
for (const [event, groups] of Object.entries(pluginHooks)) {
|
|
344
|
+
merged[event] = [...groups];
|
|
1370
345
|
}
|
|
1371
|
-
|
|
346
|
+
if (configHooks) {
|
|
347
|
+
for (const [event, groups] of Object.entries(configHooks)) {
|
|
348
|
+
if (!Array.isArray(groups)) continue;
|
|
349
|
+
if (!merged[event]) merged[event] = [];
|
|
350
|
+
merged[event].push(...groups);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return merged;
|
|
1372
354
|
}
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
355
|
+
|
|
356
|
+
// src/ui/hooks/useInteractiveSession.ts
|
|
357
|
+
var MAX_RENDERED_MESSAGES = 100;
|
|
358
|
+
function initializeSession(props, permissionHandler) {
|
|
359
|
+
const cwd = props.cwd ?? process.cwd();
|
|
360
|
+
const registry = new import_agent_sdk.CommandRegistry();
|
|
361
|
+
registry.addSource(new import_agent_sdk.BuiltinCommandSource());
|
|
362
|
+
registry.addSource(new import_agent_sdk.SkillCommandSource(cwd));
|
|
363
|
+
let pluginHooks = {};
|
|
364
|
+
const pluginsDir = (0, import_node_path3.join)((0, import_node_os.homedir)(), ".robota", "plugins");
|
|
365
|
+
const loader = new import_agent_sdk.BundlePluginLoader(pluginsDir);
|
|
366
|
+
try {
|
|
367
|
+
const plugins = loader.loadPluginsSync();
|
|
368
|
+
if (plugins.length > 0) {
|
|
369
|
+
registry.addSource(new PluginCommandSource(plugins));
|
|
370
|
+
pluginHooks = mergePluginHooks(plugins);
|
|
371
|
+
}
|
|
372
|
+
} catch {
|
|
373
|
+
}
|
|
374
|
+
const mergedConfig = {
|
|
375
|
+
...props.config,
|
|
376
|
+
hooks: mergeHooksIntoConfig(
|
|
377
|
+
props.config.hooks,
|
|
378
|
+
pluginHooks
|
|
379
|
+
)
|
|
380
|
+
};
|
|
381
|
+
const interactiveSession = new import_agent_sdk.InteractiveSession({
|
|
382
|
+
config: mergedConfig,
|
|
383
|
+
context: props.context,
|
|
384
|
+
projectInfo: props.projectInfo,
|
|
385
|
+
sessionStore: props.sessionStore,
|
|
386
|
+
permissionMode: props.permissionMode,
|
|
387
|
+
maxTurns: props.maxTurns,
|
|
388
|
+
cwd,
|
|
389
|
+
permissionHandler
|
|
390
|
+
});
|
|
391
|
+
return {
|
|
392
|
+
interactiveSession,
|
|
393
|
+
registry,
|
|
394
|
+
commandExecutor: new import_agent_sdk.SystemCommandExecutor(),
|
|
395
|
+
pluginHooks
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function useInteractiveSession(props) {
|
|
399
|
+
const [messages, setMessages] = (0, import_react.useState)([]);
|
|
400
|
+
const addMessage = (0, import_react.useCallback)((msg) => {
|
|
401
|
+
setMessages((prev) => {
|
|
402
|
+
const updated = [...prev, msg];
|
|
403
|
+
return updated.length > MAX_RENDERED_MESSAGES ? updated.slice(-MAX_RENDERED_MESSAGES) : updated;
|
|
404
|
+
});
|
|
405
|
+
}, []);
|
|
406
|
+
const [streamingText, setStreamingText] = (0, import_react.useState)("");
|
|
407
|
+
const [activeTools, setActiveTools] = (0, import_react.useState)([]);
|
|
408
|
+
const [isThinking, setIsThinking] = (0, import_react.useState)(false);
|
|
409
|
+
const [isAborting, setIsAborting] = (0, import_react.useState)(false);
|
|
410
|
+
const [pendingPrompt, setPendingPrompt] = (0, import_react.useState)(null);
|
|
411
|
+
const [contextState, setContextState] = (0, import_react.useState)({ percentage: 0, usedTokens: 0, maxTokens: 0 });
|
|
412
|
+
const [permissionRequest, setPermissionRequest] = (0, import_react.useState)(null);
|
|
413
|
+
const permissionQueueRef = (0, import_react.useRef)([]);
|
|
414
|
+
const processingRef = (0, import_react.useRef)(false);
|
|
415
|
+
const processNextPermission = (0, import_react.useCallback)(() => {
|
|
416
|
+
if (processingRef.current) return;
|
|
417
|
+
const next = permissionQueueRef.current[0];
|
|
418
|
+
if (!next) {
|
|
419
|
+
setPermissionRequest(null);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
processingRef.current = true;
|
|
423
|
+
setPermissionRequest({
|
|
424
|
+
toolName: next.toolName,
|
|
425
|
+
toolArgs: next.toolArgs,
|
|
426
|
+
resolve: (result) => {
|
|
427
|
+
permissionQueueRef.current.shift();
|
|
428
|
+
processingRef.current = false;
|
|
429
|
+
setPermissionRequest(null);
|
|
430
|
+
next.resolve(result);
|
|
431
|
+
setTimeout(() => processNextPermission(), 0);
|
|
1387
432
|
}
|
|
1388
|
-
}
|
|
433
|
+
});
|
|
434
|
+
}, []);
|
|
435
|
+
const permissionHandler = (0, import_react.useCallback)(
|
|
436
|
+
(toolName, toolArgs) => new Promise((resolve) => {
|
|
437
|
+
permissionQueueRef.current.push({ toolName, toolArgs, resolve });
|
|
438
|
+
processNextPermission();
|
|
439
|
+
}),
|
|
440
|
+
[processNextPermission]
|
|
441
|
+
);
|
|
442
|
+
const stateRef = (0, import_react.useRef)(null);
|
|
443
|
+
if (stateRef.current === null) {
|
|
444
|
+
stateRef.current = initializeSession(props, permissionHandler);
|
|
445
|
+
}
|
|
446
|
+
const { interactiveSession, registry, commandExecutor } = stateRef.current;
|
|
447
|
+
(0, import_react.useEffect)(() => {
|
|
448
|
+
let streamBuf = "";
|
|
449
|
+
const onTextDelta = (delta) => {
|
|
450
|
+
streamBuf += delta;
|
|
451
|
+
setStreamingText(streamBuf);
|
|
452
|
+
};
|
|
453
|
+
const onToolStart = (state) => {
|
|
454
|
+
setActiveTools((prev) => [...prev, state]);
|
|
455
|
+
};
|
|
456
|
+
const onToolEnd = (state) => {
|
|
457
|
+
setActiveTools(
|
|
458
|
+
(prev) => prev.map((t) => t.toolName === state.toolName && t.isRunning ? state : t)
|
|
459
|
+
);
|
|
460
|
+
};
|
|
461
|
+
const onThinking = (thinking) => {
|
|
462
|
+
setIsThinking(thinking);
|
|
463
|
+
if (thinking) {
|
|
464
|
+
streamBuf = "";
|
|
465
|
+
setStreamingText("");
|
|
466
|
+
setActiveTools([]);
|
|
467
|
+
} else {
|
|
468
|
+
setIsAborting(false);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
const onComplete = (result) => {
|
|
472
|
+
setContextState({
|
|
473
|
+
percentage: result.contextState.usedPercentage,
|
|
474
|
+
usedTokens: result.contextState.usedTokens,
|
|
475
|
+
maxTokens: result.contextState.maxTokens
|
|
476
|
+
});
|
|
477
|
+
};
|
|
478
|
+
const onInterrupted = () => {
|
|
479
|
+
};
|
|
480
|
+
const onError = () => {
|
|
481
|
+
};
|
|
482
|
+
interactiveSession.on("text_delta", onTextDelta);
|
|
483
|
+
interactiveSession.on("tool_start", onToolStart);
|
|
484
|
+
interactiveSession.on("tool_end", onToolEnd);
|
|
485
|
+
interactiveSession.on("thinking", onThinking);
|
|
486
|
+
interactiveSession.on("complete", onComplete);
|
|
487
|
+
interactiveSession.on("interrupted", onInterrupted);
|
|
488
|
+
interactiveSession.on("error", onError);
|
|
489
|
+
return () => {
|
|
490
|
+
interactiveSession.off("text_delta", onTextDelta);
|
|
491
|
+
interactiveSession.off("tool_start", onToolStart);
|
|
492
|
+
interactiveSession.off("tool_end", onToolEnd);
|
|
493
|
+
interactiveSession.off("thinking", onThinking);
|
|
494
|
+
interactiveSession.off("complete", onComplete);
|
|
495
|
+
interactiveSession.off("interrupted", onInterrupted);
|
|
496
|
+
interactiveSession.off("error", onError);
|
|
497
|
+
};
|
|
498
|
+
}, [interactiveSession]);
|
|
499
|
+
(0, import_react.useEffect)(() => {
|
|
500
|
+
if (!isThinking) {
|
|
501
|
+
const sessionMessages = interactiveSession.getMessages();
|
|
502
|
+
if (sessionMessages.length > 0) {
|
|
503
|
+
setMessages(
|
|
504
|
+
sessionMessages.length > MAX_RENDERED_MESSAGES ? sessionMessages.slice(-MAX_RENDERED_MESSAGES) : [...sessionMessages]
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
1389
508
|
}
|
|
1390
|
-
|
|
509
|
+
}, [isThinking, interactiveSession]);
|
|
510
|
+
const handleSubmit = (0, import_react.useCallback)(
|
|
511
|
+
async (input) => {
|
|
512
|
+
if (input.startsWith("/")) {
|
|
513
|
+
const parts = input.slice(1).split(/\s+/);
|
|
514
|
+
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
515
|
+
const args = parts.slice(1).join(" ");
|
|
516
|
+
const result = await commandExecutor.execute(cmd, interactiveSession, args);
|
|
517
|
+
if (result) {
|
|
518
|
+
addMessage((0, import_agent_core.createSystemMessage)(result.message));
|
|
519
|
+
const effects = interactiveSession;
|
|
520
|
+
if (result.data?.modelId) {
|
|
521
|
+
effects._pendingModelId = result.data.modelId;
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
if (result.data?.language) {
|
|
525
|
+
effects._pendingLanguage = result.data.language;
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
if (result.data?.resetRequested) {
|
|
529
|
+
effects._resetRequested = true;
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const ctx = interactiveSession.getContextState();
|
|
533
|
+
setContextState({
|
|
534
|
+
percentage: ctx.usedPercentage,
|
|
535
|
+
usedTokens: ctx.usedTokens,
|
|
536
|
+
maxTokens: ctx.maxTokens
|
|
537
|
+
});
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
541
|
+
if (skillCmd) {
|
|
542
|
+
addMessage((0, import_agent_core.createSystemMessage)(`Invoking ${skillCmd.source}: ${cmd}`));
|
|
543
|
+
const prompt = await buildSkillPrompt(input, registry);
|
|
544
|
+
if (prompt) {
|
|
545
|
+
const qualifiedName = registry.resolveQualifiedName(cmd);
|
|
546
|
+
const hookInput = qualifiedName ? `/${qualifiedName}${input.slice(1 + cmd.length)}` : input;
|
|
547
|
+
await interactiveSession.submit(prompt, input, hookInput);
|
|
548
|
+
setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (cmd === "exit") {
|
|
553
|
+
interactiveSession._exitRequested = true;
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
if (cmd === "plugin") {
|
|
557
|
+
interactiveSession._triggerPluginTUI = true;
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
addMessage((0, import_agent_core.createSystemMessage)(`Unknown command "/${cmd}". Type /help for help.`));
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
await interactiveSession.submit(input);
|
|
564
|
+
setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
565
|
+
},
|
|
566
|
+
[interactiveSession, commandExecutor, registry, addMessage]
|
|
567
|
+
);
|
|
568
|
+
const handleAbort = (0, import_react.useCallback)(() => {
|
|
569
|
+
setIsAborting(true);
|
|
570
|
+
interactiveSession.abort();
|
|
571
|
+
}, [interactiveSession]);
|
|
572
|
+
const handleCancelQueue = (0, import_react.useCallback)(() => {
|
|
573
|
+
interactiveSession.cancelQueue();
|
|
574
|
+
setPendingPrompt(null);
|
|
575
|
+
}, [interactiveSession]);
|
|
576
|
+
if (contextState.maxTokens === 0) {
|
|
577
|
+
const ctx = interactiveSession.getContextState();
|
|
578
|
+
setContextState({
|
|
579
|
+
percentage: ctx.usedPercentage,
|
|
580
|
+
usedTokens: ctx.usedTokens,
|
|
581
|
+
maxTokens: ctx.maxTokens
|
|
582
|
+
});
|
|
1391
583
|
}
|
|
1392
|
-
return
|
|
584
|
+
return {
|
|
585
|
+
interactiveSession,
|
|
586
|
+
registry,
|
|
587
|
+
commandExecutor,
|
|
588
|
+
pluginHooks: stateRef.current.pluginHooks,
|
|
589
|
+
messages,
|
|
590
|
+
addMessage,
|
|
591
|
+
setMessages,
|
|
592
|
+
streamingText,
|
|
593
|
+
activeTools,
|
|
594
|
+
isThinking,
|
|
595
|
+
isAborting,
|
|
596
|
+
pendingPrompt,
|
|
597
|
+
permissionRequest,
|
|
598
|
+
contextState,
|
|
599
|
+
handleSubmit,
|
|
600
|
+
handleAbort,
|
|
601
|
+
handleCancelQueue
|
|
602
|
+
};
|
|
1393
603
|
}
|
|
1394
604
|
|
|
1395
605
|
// src/ui/hooks/usePluginCallbacks.ts
|
|
1396
|
-
var
|
|
1397
|
-
var
|
|
606
|
+
var import_react2 = require("react");
|
|
607
|
+
var import_node_os2 = require("os");
|
|
1398
608
|
var import_node_path4 = require("path");
|
|
1399
|
-
var
|
|
609
|
+
var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
|
|
1400
610
|
function usePluginCallbacks(cwd) {
|
|
1401
|
-
return (0,
|
|
1402
|
-
const home = (0,
|
|
611
|
+
return (0, import_react2.useMemo)(() => {
|
|
612
|
+
const home = (0, import_node_os2.homedir)();
|
|
1403
613
|
const pluginsDir = (0, import_node_path4.join)(home, ".robota", "plugins");
|
|
1404
614
|
const userSettingsPath = (0, import_node_path4.join)(home, ".robota", "settings.json");
|
|
1405
|
-
const settingsStore = new
|
|
1406
|
-
const marketplace = new
|
|
1407
|
-
const installer = new
|
|
615
|
+
const settingsStore = new import_agent_sdk2.PluginSettingsStore(userSettingsPath);
|
|
616
|
+
const marketplace = new import_agent_sdk2.MarketplaceClient({ pluginsDir });
|
|
617
|
+
const installer = new import_agent_sdk2.BundlePluginInstaller({
|
|
1408
618
|
pluginsDir,
|
|
1409
619
|
settingsStore,
|
|
1410
620
|
marketplaceClient: marketplace
|
|
1411
621
|
});
|
|
1412
|
-
const loader = new
|
|
622
|
+
const loader = new import_agent_sdk2.BundlePluginLoader(pluginsDir);
|
|
1413
623
|
return {
|
|
1414
624
|
listInstalled: async () => {
|
|
1415
625
|
const plugins = await loader.loadAll();
|
|
@@ -1448,7 +658,7 @@ function usePluginCallbacks(cwd) {
|
|
|
1448
658
|
}
|
|
1449
659
|
if (scope === "project") {
|
|
1450
660
|
const projectPluginsDir = (0, import_node_path4.join)(cwd, ".robota", "plugins");
|
|
1451
|
-
const projectInstaller = new
|
|
661
|
+
const projectInstaller = new import_agent_sdk2.BundlePluginInstaller({
|
|
1452
662
|
pluginsDir: projectPluginsDir,
|
|
1453
663
|
settingsStore,
|
|
1454
664
|
marketplaceClient: marketplace
|
|
@@ -1497,9 +707,9 @@ function usePluginCallbacks(cwd) {
|
|
|
1497
707
|
}
|
|
1498
708
|
|
|
1499
709
|
// src/ui/MessageList.tsx
|
|
1500
|
-
var
|
|
710
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
1501
711
|
var import_ink2 = require("ink");
|
|
1502
|
-
var
|
|
712
|
+
var import_agent_core2 = require("@robota-sdk/agent-core");
|
|
1503
713
|
|
|
1504
714
|
// src/ui/render-markdown.ts
|
|
1505
715
|
var import_marked = require("marked");
|
|
@@ -1585,7 +795,7 @@ function RoleLabel({ role }) {
|
|
|
1585
795
|
}
|
|
1586
796
|
}
|
|
1587
797
|
function ToolMessage({ message }) {
|
|
1588
|
-
if (!(0,
|
|
798
|
+
if (!(0, import_agent_core2.isToolMessage)(message)) {
|
|
1589
799
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
|
|
1590
800
|
}
|
|
1591
801
|
const toolName = message.name;
|
|
@@ -1645,10 +855,10 @@ function ToolMessage({ message }) {
|
|
|
1645
855
|
] }, i))
|
|
1646
856
|
] });
|
|
1647
857
|
}
|
|
1648
|
-
var MessageItem =
|
|
858
|
+
var MessageItem = import_react3.default.memo(function MessageItem2({
|
|
1649
859
|
message
|
|
1650
860
|
}) {
|
|
1651
|
-
if ((0,
|
|
861
|
+
if ((0, import_agent_core2.isToolMessage)(message)) {
|
|
1652
862
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolMessage, { message });
|
|
1653
863
|
}
|
|
1654
864
|
const content = message.content ?? "";
|
|
@@ -1656,7 +866,7 @@ var MessageItem = import_react7.default.memo(function MessageItem2({
|
|
|
1656
866
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
1657
867
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }) }),
|
|
1658
868
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
|
|
1659
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { wrap: "wrap", children: (0,
|
|
869
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { wrap: "wrap", children: (0, import_agent_core2.isAssistantMessage)(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
|
|
1660
870
|
] });
|
|
1661
871
|
});
|
|
1662
872
|
function MessageList({ messages }) {
|
|
@@ -1665,7 +875,7 @@ function MessageList({ messages }) {
|
|
|
1665
875
|
|
|
1666
876
|
// src/ui/StatusBar.tsx
|
|
1667
877
|
var import_ink3 = require("ink");
|
|
1668
|
-
var
|
|
878
|
+
var import_agent_core3 = require("@robota-sdk/agent-core");
|
|
1669
879
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1670
880
|
var CONTEXT_YELLOW_THRESHOLD = 70;
|
|
1671
881
|
var CONTEXT_RED_THRESHOLD = 90;
|
|
@@ -1705,9 +915,9 @@ function StatusBar({
|
|
|
1705
915
|
"Context: ",
|
|
1706
916
|
Math.round(contextPercentage),
|
|
1707
917
|
"% (",
|
|
1708
|
-
(0,
|
|
918
|
+
(0, import_agent_core3.formatTokenCount)(contextUsedTokens),
|
|
1709
919
|
"/",
|
|
1710
|
-
(0,
|
|
920
|
+
(0, import_agent_core3.formatTokenCount)(contextMaxTokens),
|
|
1711
921
|
")"
|
|
1712
922
|
] })
|
|
1713
923
|
] }),
|
|
@@ -1724,11 +934,11 @@ function StatusBar({
|
|
|
1724
934
|
}
|
|
1725
935
|
|
|
1726
936
|
// src/ui/InputArea.tsx
|
|
1727
|
-
var
|
|
937
|
+
var import_react6 = __toESM(require("react"), 1);
|
|
1728
938
|
var import_ink7 = require("ink");
|
|
1729
939
|
|
|
1730
940
|
// src/ui/CjkTextInput.tsx
|
|
1731
|
-
var
|
|
941
|
+
var import_react4 = require("react");
|
|
1732
942
|
var import_ink4 = require("ink");
|
|
1733
943
|
var import_chalk = __toESM(require("chalk"), 1);
|
|
1734
944
|
var import_string_width = __toESM(require("string-width"), 1);
|
|
@@ -1774,11 +984,11 @@ function CjkTextInput({
|
|
|
1774
984
|
showCursor = true,
|
|
1775
985
|
availableWidth
|
|
1776
986
|
}) {
|
|
1777
|
-
const valueRef = (0,
|
|
1778
|
-
const cursorRef = (0,
|
|
1779
|
-
const [, forceRender] = (0,
|
|
1780
|
-
const isPastingRef = (0,
|
|
1781
|
-
const pasteBufferRef = (0,
|
|
987
|
+
const valueRef = (0, import_react4.useRef)(value);
|
|
988
|
+
const cursorRef = (0, import_react4.useRef)(value.length);
|
|
989
|
+
const [, forceRender] = (0, import_react4.useState)(0);
|
|
990
|
+
const isPastingRef = (0, import_react4.useRef)(false);
|
|
991
|
+
const pasteBufferRef = (0, import_react4.useRef)("");
|
|
1782
992
|
if (value !== valueRef.current) {
|
|
1783
993
|
valueRef.current = value;
|
|
1784
994
|
if (cursorRef.current > value.length) {
|
|
@@ -1907,15 +1117,15 @@ function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
|
|
|
1907
1117
|
}
|
|
1908
1118
|
|
|
1909
1119
|
// src/ui/WaveText.tsx
|
|
1910
|
-
var
|
|
1120
|
+
var import_react5 = require("react");
|
|
1911
1121
|
var import_ink5 = require("ink");
|
|
1912
1122
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1913
1123
|
var WAVE_COLORS = ["#666666", "#888888", "#aaaaaa", "#888888"];
|
|
1914
1124
|
var INTERVAL_MS = 400;
|
|
1915
1125
|
var CHARS_PER_GROUP = 4;
|
|
1916
1126
|
function WaveText({ text }) {
|
|
1917
|
-
const [tick, setTick] = (0,
|
|
1918
|
-
(0,
|
|
1127
|
+
const [tick, setTick] = (0, import_react5.useState)(0);
|
|
1128
|
+
(0, import_react5.useEffect)(() => {
|
|
1919
1129
|
const timer = setInterval(() => {
|
|
1920
1130
|
setTick((prev) => prev + 1);
|
|
1921
1131
|
}, INTERVAL_MS);
|
|
@@ -1987,16 +1197,16 @@ function parseSlashInput(value) {
|
|
|
1987
1197
|
return { isSlash: true, parentCommand: parent, filter: rest };
|
|
1988
1198
|
}
|
|
1989
1199
|
function useAutocomplete(value, registry) {
|
|
1990
|
-
const [selectedIndex, setSelectedIndex] = (0,
|
|
1991
|
-
const [dismissed, setDismissed] = (0,
|
|
1992
|
-
const prevValueRef =
|
|
1200
|
+
const [selectedIndex, setSelectedIndex] = (0, import_react6.useState)(0);
|
|
1201
|
+
const [dismissed, setDismissed] = (0, import_react6.useState)(false);
|
|
1202
|
+
const prevValueRef = import_react6.default.useRef(value);
|
|
1993
1203
|
if (prevValueRef.current !== value) {
|
|
1994
1204
|
prevValueRef.current = value;
|
|
1995
1205
|
if (dismissed) setDismissed(false);
|
|
1996
1206
|
}
|
|
1997
1207
|
const parsed = parseSlashInput(value);
|
|
1998
1208
|
const isSubcommandMode = parsed.isSlash && parsed.parentCommand.length > 0;
|
|
1999
|
-
const filteredCommands = (0,
|
|
1209
|
+
const filteredCommands = (0, import_react6.useMemo)(() => {
|
|
2000
1210
|
if (!registry || !parsed.isSlash || dismissed) return [];
|
|
2001
1211
|
if (isSubcommandMode) {
|
|
2002
1212
|
const subs = registry.getSubcommands(parsed.parentCommand);
|
|
@@ -2041,12 +1251,12 @@ function InputArea({
|
|
|
2041
1251
|
pendingPrompt,
|
|
2042
1252
|
registry
|
|
2043
1253
|
}) {
|
|
2044
|
-
const [value, setValue] = (0,
|
|
2045
|
-
const pasteStore = (0,
|
|
1254
|
+
const [value, setValue] = (0, import_react6.useState)("");
|
|
1255
|
+
const pasteStore = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
|
|
2046
1256
|
const { stdout } = (0, import_ink7.useStdout)();
|
|
2047
1257
|
const terminalColumns = stdout?.columns ?? 80;
|
|
2048
1258
|
const availableWidth = Math.max(1, terminalColumns - INPUT_AREA_OVERHEAD);
|
|
2049
|
-
const pasteIdRef = (0,
|
|
1259
|
+
const pasteIdRef = (0, import_react6.useRef)(0);
|
|
2050
1260
|
const {
|
|
2051
1261
|
showPopup,
|
|
2052
1262
|
filteredCommands,
|
|
@@ -2055,7 +1265,7 @@ function InputArea({
|
|
|
2055
1265
|
isSubcommandMode,
|
|
2056
1266
|
setShowPopup
|
|
2057
1267
|
} = useAutocomplete(value, registry);
|
|
2058
|
-
const handlePaste = (0,
|
|
1268
|
+
const handlePaste = (0, import_react6.useCallback)((text) => {
|
|
2059
1269
|
pasteIdRef.current += 1;
|
|
2060
1270
|
const id = pasteIdRef.current;
|
|
2061
1271
|
pasteStore.current.set(id, text);
|
|
@@ -2063,7 +1273,7 @@ function InputArea({
|
|
|
2063
1273
|
const label = `[Pasted text #${id} +${lineCount} lines]`;
|
|
2064
1274
|
setValue((prev) => prev ? `${prev} ${label}` : label);
|
|
2065
1275
|
}, []);
|
|
2066
|
-
const handleSubmit = (0,
|
|
1276
|
+
const handleSubmit = (0, import_react6.useCallback)(
|
|
2067
1277
|
(text) => {
|
|
2068
1278
|
const trimmed = text.trim();
|
|
2069
1279
|
if (trimmed.length === 0) return;
|
|
@@ -2079,7 +1289,7 @@ function InputArea({
|
|
|
2079
1289
|
},
|
|
2080
1290
|
[showPopup, filteredCommands, selectedIndex, onSubmit]
|
|
2081
1291
|
);
|
|
2082
|
-
const selectCommand = (0,
|
|
1292
|
+
const selectCommand = (0, import_react6.useCallback)(
|
|
2083
1293
|
(cmd) => {
|
|
2084
1294
|
const parsed = parseSlashInput(value);
|
|
2085
1295
|
if (parsed.parentCommand) {
|
|
@@ -2164,7 +1374,7 @@ function InputArea({
|
|
|
2164
1374
|
}
|
|
2165
1375
|
|
|
2166
1376
|
// src/ui/ConfirmPrompt.tsx
|
|
2167
|
-
var
|
|
1377
|
+
var import_react7 = require("react");
|
|
2168
1378
|
var import_ink8 = require("ink");
|
|
2169
1379
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2170
1380
|
function ConfirmPrompt({
|
|
@@ -2172,9 +1382,9 @@ function ConfirmPrompt({
|
|
|
2172
1382
|
options = ["Yes", "No"],
|
|
2173
1383
|
onSelect
|
|
2174
1384
|
}) {
|
|
2175
|
-
const [selected, setSelected] = (0,
|
|
2176
|
-
const resolvedRef = (0,
|
|
2177
|
-
const doSelect = (0,
|
|
1385
|
+
const [selected, setSelected] = (0, import_react7.useState)(0);
|
|
1386
|
+
const resolvedRef = (0, import_react7.useRef)(false);
|
|
1387
|
+
const doSelect = (0, import_react7.useCallback)(
|
|
2178
1388
|
(index) => {
|
|
2179
1389
|
if (resolvedRef.current) return;
|
|
2180
1390
|
resolvedRef.current = true;
|
|
@@ -2207,7 +1417,7 @@ function ConfirmPrompt({
|
|
|
2207
1417
|
}
|
|
2208
1418
|
|
|
2209
1419
|
// src/ui/PermissionPrompt.tsx
|
|
2210
|
-
var
|
|
1420
|
+
var import_react8 = __toESM(require("react"), 1);
|
|
2211
1421
|
var import_ink9 = require("ink");
|
|
2212
1422
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2213
1423
|
var OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
|
|
@@ -2217,15 +1427,15 @@ function formatArgs(args) {
|
|
|
2217
1427
|
return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
2218
1428
|
}
|
|
2219
1429
|
function PermissionPrompt({ request }) {
|
|
2220
|
-
const [selected, setSelected] =
|
|
2221
|
-
const resolvedRef =
|
|
2222
|
-
const prevRequestRef =
|
|
1430
|
+
const [selected, setSelected] = import_react8.default.useState(0);
|
|
1431
|
+
const resolvedRef = import_react8.default.useRef(false);
|
|
1432
|
+
const prevRequestRef = import_react8.default.useRef(request);
|
|
2223
1433
|
if (prevRequestRef.current !== request) {
|
|
2224
1434
|
prevRequestRef.current = request;
|
|
2225
1435
|
resolvedRef.current = false;
|
|
2226
1436
|
setSelected(0);
|
|
2227
1437
|
}
|
|
2228
|
-
const doResolve =
|
|
1438
|
+
const doResolve = import_react8.default.useCallback(
|
|
2229
1439
|
(index) => {
|
|
2230
1440
|
if (resolvedRef.current) return;
|
|
2231
1441
|
resolvedRef.current = true;
|
|
@@ -2314,10 +1524,10 @@ function StreamingIndicator({ text, activeTools }) {
|
|
|
2314
1524
|
}
|
|
2315
1525
|
|
|
2316
1526
|
// src/ui/PluginTUI.tsx
|
|
2317
|
-
var
|
|
1527
|
+
var import_react11 = require("react");
|
|
2318
1528
|
|
|
2319
1529
|
// src/ui/MenuSelect.tsx
|
|
2320
|
-
var
|
|
1530
|
+
var import_react9 = require("react");
|
|
2321
1531
|
var import_ink11 = require("ink");
|
|
2322
1532
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2323
1533
|
function MenuSelect({
|
|
@@ -2328,10 +1538,10 @@ function MenuSelect({
|
|
|
2328
1538
|
loading,
|
|
2329
1539
|
error
|
|
2330
1540
|
}) {
|
|
2331
|
-
const [selected, setSelected] = (0,
|
|
2332
|
-
const selectedRef = (0,
|
|
2333
|
-
const resolvedRef = (0,
|
|
2334
|
-
const doSelect = (0,
|
|
1541
|
+
const [selected, setSelected] = (0, import_react9.useState)(0);
|
|
1542
|
+
const selectedRef = (0, import_react9.useRef)(0);
|
|
1543
|
+
const resolvedRef = (0, import_react9.useRef)(false);
|
|
1544
|
+
const doSelect = (0, import_react9.useCallback)(
|
|
2335
1545
|
(index) => {
|
|
2336
1546
|
if (resolvedRef.current || items.length === 0) return;
|
|
2337
1547
|
resolvedRef.current = true;
|
|
@@ -2381,7 +1591,7 @@ function MenuSelect({
|
|
|
2381
1591
|
}
|
|
2382
1592
|
|
|
2383
1593
|
// src/ui/TextPrompt.tsx
|
|
2384
|
-
var
|
|
1594
|
+
var import_react10 = require("react");
|
|
2385
1595
|
var import_ink12 = require("ink");
|
|
2386
1596
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2387
1597
|
function TextPrompt({
|
|
@@ -2391,11 +1601,11 @@ function TextPrompt({
|
|
|
2391
1601
|
onCancel,
|
|
2392
1602
|
validate
|
|
2393
1603
|
}) {
|
|
2394
|
-
const [value, setValue] = (0,
|
|
2395
|
-
const [error, setError] = (0,
|
|
2396
|
-
const resolvedRef = (0,
|
|
2397
|
-
const valueRef = (0,
|
|
2398
|
-
const handleSubmit = (0,
|
|
1604
|
+
const [value, setValue] = (0, import_react10.useState)("");
|
|
1605
|
+
const [error, setError] = (0, import_react10.useState)();
|
|
1606
|
+
const resolvedRef = (0, import_react10.useRef)(false);
|
|
1607
|
+
const valueRef = (0, import_react10.useRef)("");
|
|
1608
|
+
const handleSubmit = (0, import_react10.useCallback)(() => {
|
|
2399
1609
|
if (resolvedRef.current) return;
|
|
2400
1610
|
const trimmed = valueRef.current.trim();
|
|
2401
1611
|
if (!trimmed) return;
|
|
@@ -2539,19 +1749,19 @@ function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
|
|
|
2539
1749
|
// src/ui/PluginTUI.tsx
|
|
2540
1750
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2541
1751
|
function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
2542
|
-
const [stack, setStack] = (0,
|
|
2543
|
-
const [items, setItems] = (0,
|
|
2544
|
-
const [loading, setLoading] = (0,
|
|
2545
|
-
const [error, setError] = (0,
|
|
2546
|
-
const [confirm, setConfirm] = (0,
|
|
2547
|
-
const [refreshCounter, setRefreshCounter] = (0,
|
|
1752
|
+
const [stack, setStack] = (0, import_react11.useState)([{ screen: "main" }]);
|
|
1753
|
+
const [items, setItems] = (0, import_react11.useState)([]);
|
|
1754
|
+
const [loading, setLoading] = (0, import_react11.useState)(false);
|
|
1755
|
+
const [error, setError] = (0, import_react11.useState)();
|
|
1756
|
+
const [confirm, setConfirm] = (0, import_react11.useState)();
|
|
1757
|
+
const [refreshCounter, setRefreshCounter] = (0, import_react11.useState)(0);
|
|
2548
1758
|
const current = stack[stack.length - 1] ?? { screen: "main" };
|
|
2549
|
-
const push = (0,
|
|
1759
|
+
const push = (0, import_react11.useCallback)((state) => {
|
|
2550
1760
|
setStack((prev) => [...prev, state]);
|
|
2551
1761
|
setItems([]);
|
|
2552
1762
|
setError(void 0);
|
|
2553
1763
|
}, []);
|
|
2554
|
-
const pop = (0,
|
|
1764
|
+
const pop = (0, import_react11.useCallback)(() => {
|
|
2555
1765
|
setStack((prev) => {
|
|
2556
1766
|
if (prev.length <= 1) {
|
|
2557
1767
|
onClose();
|
|
@@ -2562,7 +1772,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2562
1772
|
setItems([]);
|
|
2563
1773
|
setError(void 0);
|
|
2564
1774
|
}, [onClose]);
|
|
2565
|
-
const popN = (0,
|
|
1775
|
+
const popN = (0, import_react11.useCallback)(
|
|
2566
1776
|
(n) => {
|
|
2567
1777
|
setStack((prev) => {
|
|
2568
1778
|
const next = prev.slice(0, Math.max(1, prev.length - n));
|
|
@@ -2577,18 +1787,18 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2577
1787
|
},
|
|
2578
1788
|
[onClose]
|
|
2579
1789
|
);
|
|
2580
|
-
const notify = (0,
|
|
1790
|
+
const notify = (0, import_react11.useCallback)(
|
|
2581
1791
|
(content) => {
|
|
2582
1792
|
addMessage?.({ role: "system", content });
|
|
2583
1793
|
},
|
|
2584
1794
|
[addMessage]
|
|
2585
1795
|
);
|
|
2586
|
-
const refresh = (0,
|
|
1796
|
+
const refresh = (0, import_react11.useCallback)(() => {
|
|
2587
1797
|
setItems([]);
|
|
2588
1798
|
setRefreshCounter((c) => c + 1);
|
|
2589
1799
|
}, []);
|
|
2590
1800
|
const nav = { push, pop, popN, notify, setConfirm, refresh };
|
|
2591
|
-
(0,
|
|
1801
|
+
(0, import_react11.useEffect)(() => {
|
|
2592
1802
|
const screen2 = current.screen;
|
|
2593
1803
|
if (screen2 === "marketplace-list") {
|
|
2594
1804
|
setLoading(true);
|
|
@@ -2638,7 +1848,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2638
1848
|
});
|
|
2639
1849
|
}
|
|
2640
1850
|
}, [stack.length, current.screen, current.context?.marketplace, callbacks, refreshCounter]);
|
|
2641
|
-
const handleSelect = (0,
|
|
1851
|
+
const handleSelect = (0, import_react11.useCallback)(
|
|
2642
1852
|
(value) => {
|
|
2643
1853
|
const screen2 = current.screen;
|
|
2644
1854
|
const ctx = current.context;
|
|
@@ -2656,7 +1866,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2656
1866
|
},
|
|
2657
1867
|
[current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
|
|
2658
1868
|
);
|
|
2659
|
-
const handleTextSubmit = (0,
|
|
1869
|
+
const handleTextSubmit = (0, import_react11.useCallback)(
|
|
2660
1870
|
(value) => {
|
|
2661
1871
|
if (current.screen === "marketplace-add") {
|
|
2662
1872
|
callbacks.marketplaceAdd(value).then((name) => {
|
|
@@ -2766,104 +1976,90 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2766
1976
|
|
|
2767
1977
|
// src/ui/App.tsx
|
|
2768
1978
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2769
|
-
var
|
|
2770
|
-
function mergeHooksIntoConfig(configHooks, pluginHooks) {
|
|
2771
|
-
const pluginKeys = Object.keys(pluginHooks);
|
|
2772
|
-
if (pluginKeys.length === 0) return configHooks;
|
|
2773
|
-
const merged = {};
|
|
2774
|
-
for (const [event, groups] of Object.entries(pluginHooks)) {
|
|
2775
|
-
merged[event] = [...groups];
|
|
2776
|
-
}
|
|
2777
|
-
if (configHooks) {
|
|
2778
|
-
for (const [event, groups] of Object.entries(configHooks)) {
|
|
2779
|
-
if (!Array.isArray(groups)) continue;
|
|
2780
|
-
if (!merged[event]) merged[event] = [];
|
|
2781
|
-
merged[event].push(...groups);
|
|
2782
|
-
}
|
|
2783
|
-
}
|
|
2784
|
-
return merged;
|
|
2785
|
-
}
|
|
1979
|
+
var EXIT_DELAY_MS = 500;
|
|
2786
1980
|
function App(props) {
|
|
2787
1981
|
const { exit } = (0, import_ink13.useApp)();
|
|
2788
|
-
const
|
|
2789
|
-
const
|
|
2790
|
-
|
|
2791
|
-
hooks: mergeHooksIntoConfig(
|
|
2792
|
-
props.config.hooks,
|
|
2793
|
-
pluginHooks
|
|
2794
|
-
)
|
|
2795
|
-
};
|
|
2796
|
-
const { session, permissionRequest, streamingText, clearStreamingText, activeTools } = useSession(
|
|
2797
|
-
{ ...props, config: configWithPluginHooks }
|
|
2798
|
-
);
|
|
2799
|
-
const { messages, setMessages, addMessage } = useMessages();
|
|
2800
|
-
const [isThinking, setIsThinking] = (0, import_react16.useState)(false);
|
|
2801
|
-
const initialCtx = session.getContextState();
|
|
2802
|
-
const [contextState, setContextState] = (0, import_react16.useState)({
|
|
2803
|
-
percentage: initialCtx.usedPercentage,
|
|
2804
|
-
usedTokens: initialCtx.usedTokens,
|
|
2805
|
-
maxTokens: initialCtx.maxTokens
|
|
2806
|
-
});
|
|
2807
|
-
const pendingModelChangeRef = (0, import_react16.useRef)(null);
|
|
2808
|
-
const [pendingModelId, setPendingModelId] = (0, import_react16.useState)(null);
|
|
2809
|
-
const [showPluginTUI, setShowPluginTUI] = (0, import_react16.useState)(false);
|
|
2810
|
-
const [isAborting, setIsAborting] = (0, import_react16.useState)(false);
|
|
2811
|
-
const [pendingPrompt, setPendingPrompt] = (0, import_react16.useState)(null);
|
|
2812
|
-
const pendingPromptRef = (0, import_react16.useRef)(null);
|
|
2813
|
-
const pluginCallbacks = usePluginCallbacks(props.cwd ?? process.cwd());
|
|
2814
|
-
const handleSlashCommand = useSlashCommands(
|
|
2815
|
-
session,
|
|
2816
|
-
addMessage,
|
|
2817
|
-
setMessages,
|
|
2818
|
-
exit,
|
|
1982
|
+
const cwd = props.cwd ?? process.cwd();
|
|
1983
|
+
const {
|
|
1984
|
+
interactiveSession,
|
|
2819
1985
|
registry,
|
|
2820
|
-
|
|
2821
|
-
setPendingModelId,
|
|
2822
|
-
pluginCallbacks,
|
|
2823
|
-
setShowPluginTUI
|
|
2824
|
-
);
|
|
2825
|
-
const executePrompt = useSubmitHandler(
|
|
2826
|
-
session,
|
|
1986
|
+
messages,
|
|
2827
1987
|
addMessage,
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
1988
|
+
streamingText,
|
|
1989
|
+
activeTools,
|
|
1990
|
+
isThinking,
|
|
1991
|
+
isAborting,
|
|
1992
|
+
pendingPrompt,
|
|
1993
|
+
permissionRequest,
|
|
1994
|
+
contextState,
|
|
1995
|
+
handleSubmit: baseHandleSubmit,
|
|
1996
|
+
handleAbort,
|
|
1997
|
+
handleCancelQueue
|
|
1998
|
+
} = useInteractiveSession({
|
|
1999
|
+
config: props.config,
|
|
2000
|
+
context: props.context,
|
|
2001
|
+
projectInfo: props.projectInfo,
|
|
2002
|
+
sessionStore: props.sessionStore,
|
|
2003
|
+
permissionMode: props.permissionMode,
|
|
2004
|
+
maxTurns: props.maxTurns,
|
|
2005
|
+
cwd
|
|
2006
|
+
});
|
|
2007
|
+
const pluginCallbacks = usePluginCallbacks(cwd);
|
|
2008
|
+
const [pendingModelId, setPendingModelId] = (0, import_react12.useState)(null);
|
|
2009
|
+
const pendingModelChangeRef = (0, import_react12.useRef)(null);
|
|
2010
|
+
const [showPluginTUI, setShowPluginTUI] = (0, import_react12.useState)(false);
|
|
2011
|
+
const handleSubmit = async (input) => {
|
|
2012
|
+
await baseHandleSubmit(input);
|
|
2013
|
+
const sideEffects = interactiveSession;
|
|
2014
|
+
if (sideEffects._pendingModelId) {
|
|
2015
|
+
const modelId = sideEffects._pendingModelId;
|
|
2016
|
+
delete sideEffects._pendingModelId;
|
|
2017
|
+
pendingModelChangeRef.current = modelId;
|
|
2018
|
+
setPendingModelId(modelId);
|
|
2019
|
+
return;
|
|
2020
|
+
}
|
|
2021
|
+
if (sideEffects._pendingLanguage) {
|
|
2022
|
+
const lang = sideEffects._pendingLanguage;
|
|
2023
|
+
delete sideEffects._pendingLanguage;
|
|
2024
|
+
const settingsPath = getUserSettingsPath();
|
|
2025
|
+
const settings = readSettings(settingsPath);
|
|
2026
|
+
settings.language = lang;
|
|
2027
|
+
writeSettings(settingsPath, settings);
|
|
2028
|
+
addMessage((0, import_agent_core4.createSystemMessage)(`Language set to "${lang}". Restarting...`));
|
|
2029
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2030
|
+
return;
|
|
2031
|
+
}
|
|
2032
|
+
if (sideEffects._resetRequested) {
|
|
2033
|
+
delete sideEffects._resetRequested;
|
|
2034
|
+
const settingsPath = getUserSettingsPath();
|
|
2035
|
+
if (deleteSettings(settingsPath)) {
|
|
2036
|
+
addMessage((0, import_agent_core4.createSystemMessage)(`Deleted ${settingsPath}. Exiting...`));
|
|
2037
|
+
} else {
|
|
2038
|
+
addMessage((0, import_agent_core4.createSystemMessage)("No user settings found."));
|
|
2840
2039
|
}
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2040
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2041
|
+
return;
|
|
2042
|
+
}
|
|
2043
|
+
if (sideEffects._exitRequested) {
|
|
2044
|
+
delete sideEffects._exitRequested;
|
|
2045
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
if (sideEffects._triggerPluginTUI) {
|
|
2049
|
+
delete sideEffects._triggerPluginTUI;
|
|
2050
|
+
setShowPluginTUI(true);
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
};
|
|
2845
2054
|
(0, import_ink13.useInput)(
|
|
2846
2055
|
(_input, key) => {
|
|
2847
2056
|
if (key.escape && isThinking) {
|
|
2848
|
-
|
|
2849
|
-
setPendingPrompt(null);
|
|
2850
|
-
pendingPromptRef.current = null;
|
|
2851
|
-
session.abort();
|
|
2057
|
+
handleAbort();
|
|
2852
2058
|
}
|
|
2853
2059
|
},
|
|
2854
2060
|
{ isActive: !permissionRequest && !showPluginTUI }
|
|
2855
2061
|
);
|
|
2856
|
-
|
|
2857
|
-
if (!isThinking) {
|
|
2858
|
-
setIsAborting(false);
|
|
2859
|
-
if (pendingPromptRef.current) {
|
|
2860
|
-
const prompt = pendingPromptRef.current;
|
|
2861
|
-
setPendingPrompt(null);
|
|
2862
|
-
pendingPromptRef.current = null;
|
|
2863
|
-
setTimeout(() => executePrompt(prompt), 0);
|
|
2864
|
-
}
|
|
2865
|
-
}
|
|
2866
|
-
}, [isThinking, pendingPrompt, executePrompt]);
|
|
2062
|
+
const session = interactiveSession.getSession();
|
|
2867
2063
|
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", children: [
|
|
2868
2064
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
2869
2065
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { color: "cyan", bold: true, children: `
|
|
@@ -2880,13 +2076,13 @@ function App(props) {
|
|
|
2880
2076
|
] }),
|
|
2881
2077
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
|
|
2882
2078
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageList, { messages }),
|
|
2883
|
-
isThinking && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
|
|
2079
|
+
(isThinking || activeTools.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
|
|
2884
2080
|
] }),
|
|
2885
2081
|
permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PermissionPrompt, { request: permissionRequest }),
|
|
2886
2082
|
pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2887
2083
|
ConfirmPrompt,
|
|
2888
2084
|
{
|
|
2889
|
-
message: `Change model to ${(0,
|
|
2085
|
+
message: `Change model to ${(0, import_agent_core4.getModelName)(pendingModelId)}? This will restart the session.`,
|
|
2890
2086
|
onSelect: (index) => {
|
|
2891
2087
|
setPendingModelId(null);
|
|
2892
2088
|
pendingModelChangeRef.current = null;
|
|
@@ -2895,20 +2091,20 @@ function App(props) {
|
|
|
2895
2091
|
const settingsPath = getUserSettingsPath();
|
|
2896
2092
|
updateModelInSettings(settingsPath, pendingModelId);
|
|
2897
2093
|
addMessage(
|
|
2898
|
-
(0,
|
|
2899
|
-
`Model changed to ${(0,
|
|
2094
|
+
(0, import_agent_core4.createSystemMessage)(
|
|
2095
|
+
`Model changed to ${(0, import_agent_core4.getModelName)(pendingModelId)}. Restarting...`
|
|
2900
2096
|
)
|
|
2901
2097
|
);
|
|
2902
|
-
setTimeout(() => exit(),
|
|
2098
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2903
2099
|
} catch (err) {
|
|
2904
2100
|
addMessage(
|
|
2905
|
-
(0,
|
|
2101
|
+
(0, import_agent_core4.createSystemMessage)(
|
|
2906
2102
|
`Failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2907
2103
|
)
|
|
2908
2104
|
);
|
|
2909
2105
|
}
|
|
2910
2106
|
} else {
|
|
2911
|
-
addMessage((0,
|
|
2107
|
+
addMessage((0, import_agent_core4.createSystemMessage)("Model change cancelled."));
|
|
2912
2108
|
}
|
|
2913
2109
|
}
|
|
2914
2110
|
}
|
|
@@ -2918,14 +2114,14 @@ function App(props) {
|
|
|
2918
2114
|
{
|
|
2919
2115
|
callbacks: pluginCallbacks,
|
|
2920
2116
|
onClose: () => setShowPluginTUI(false),
|
|
2921
|
-
addMessage: (msg) => addMessage((0,
|
|
2117
|
+
addMessage: (msg) => addMessage((0, import_agent_core4.createSystemMessage)(msg.content))
|
|
2922
2118
|
}
|
|
2923
2119
|
),
|
|
2924
2120
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2925
2121
|
StatusBar,
|
|
2926
2122
|
{
|
|
2927
2123
|
permissionMode: session.getPermissionMode(),
|
|
2928
|
-
modelName: (0,
|
|
2124
|
+
modelName: (0, import_agent_core4.getModelName)(props.config.provider.model),
|
|
2929
2125
|
sessionId: session.getSessionId(),
|
|
2930
2126
|
messageCount: messages.length,
|
|
2931
2127
|
isThinking,
|
|
@@ -2938,10 +2134,7 @@ function App(props) {
|
|
|
2938
2134
|
InputArea,
|
|
2939
2135
|
{
|
|
2940
2136
|
onSubmit: handleSubmit,
|
|
2941
|
-
onCancelQueue:
|
|
2942
|
-
setPendingPrompt(null);
|
|
2943
|
-
pendingPromptRef.current = null;
|
|
2944
|
-
},
|
|
2137
|
+
onCancelQueue: handleCancelQueue,
|
|
2945
2138
|
isDisabled: !!permissionRequest || showPluginTUI || isThinking && !!pendingPrompt,
|
|
2946
2139
|
isAborting,
|
|
2947
2140
|
pendingPrompt,
|
|
@@ -2988,9 +2181,9 @@ function renderApp(options) {
|
|
|
2988
2181
|
// src/cli.ts
|
|
2989
2182
|
var import_meta = {};
|
|
2990
2183
|
function checkSettingsFile(filePath) {
|
|
2991
|
-
if (!(0,
|
|
2184
|
+
if (!(0, import_node_fs2.existsSync)(filePath)) return "missing";
|
|
2992
2185
|
try {
|
|
2993
|
-
const raw = (0,
|
|
2186
|
+
const raw = (0, import_node_fs2.readFileSync)(filePath, "utf8").trim();
|
|
2994
2187
|
if (raw.length === 0) return "incomplete";
|
|
2995
2188
|
const parsed = JSON.parse(raw);
|
|
2996
2189
|
const provider = parsed.provider;
|
|
@@ -3007,7 +2200,7 @@ function readVersion() {
|
|
|
3007
2200
|
const candidates = [(0, import_node_path5.join)(dir, "..", "..", "package.json"), (0, import_node_path5.join)(dir, "..", "package.json")];
|
|
3008
2201
|
for (const pkgPath of candidates) {
|
|
3009
2202
|
try {
|
|
3010
|
-
const raw = (0,
|
|
2203
|
+
const raw = (0, import_node_fs2.readFileSync)(pkgPath, "utf-8");
|
|
3011
2204
|
const pkg = JSON.parse(raw);
|
|
3012
2205
|
if (pkg.version !== void 0 && pkg.name !== void 0) {
|
|
3013
2206
|
return pkg.version;
|
|
@@ -3095,7 +2288,7 @@ async function ensureConfig(cwd) {
|
|
|
3095
2288
|
}
|
|
3096
2289
|
const language = await promptInput(" Response language (ko/en/ja/zh, default: en): ");
|
|
3097
2290
|
const settingsDir = (0, import_node_path5.dirname)(userPath);
|
|
3098
|
-
(0,
|
|
2291
|
+
(0, import_node_fs2.mkdirSync)(settingsDir, { recursive: true });
|
|
3099
2292
|
const settings = {
|
|
3100
2293
|
provider: {
|
|
3101
2294
|
name: "anthropic",
|
|
@@ -3106,7 +2299,7 @@ async function ensureConfig(cwd) {
|
|
|
3106
2299
|
if (language) {
|
|
3107
2300
|
settings.language = language;
|
|
3108
2301
|
}
|
|
3109
|
-
(0,
|
|
2302
|
+
(0, import_node_fs2.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
3110
2303
|
process.stdout.write(`
|
|
3111
2304
|
Config saved to ${userPath}
|
|
3112
2305
|
|
|
@@ -3135,9 +2328,9 @@ async function startCli() {
|
|
|
3135
2328
|
const cwd = process.cwd();
|
|
3136
2329
|
await ensureConfig(cwd);
|
|
3137
2330
|
const [config, context, projectInfo] = await Promise.all([
|
|
3138
|
-
(0,
|
|
3139
|
-
(0,
|
|
3140
|
-
(0,
|
|
2331
|
+
(0, import_agent_sdk3.loadConfig)(cwd),
|
|
2332
|
+
(0, import_agent_sdk3.loadContext)(cwd),
|
|
2333
|
+
(0, import_agent_sdk3.detectProject)(cwd)
|
|
3141
2334
|
]);
|
|
3142
2335
|
if (args.model !== void 0) {
|
|
3143
2336
|
config.provider.model = args.model;
|
|
@@ -3145,7 +2338,7 @@ async function startCli() {
|
|
|
3145
2338
|
if (args.language !== void 0) {
|
|
3146
2339
|
config.language = args.language;
|
|
3147
2340
|
}
|
|
3148
|
-
const sessionStore = new
|
|
2341
|
+
const sessionStore = new import_agent_sdk3.SessionStore();
|
|
3149
2342
|
if (args.printMode) {
|
|
3150
2343
|
const prompt = args.positional.join(" ").trim();
|
|
3151
2344
|
if (prompt.length === 0) {
|
|
@@ -3153,15 +2346,15 @@ async function startCli() {
|
|
|
3153
2346
|
process.exit(1);
|
|
3154
2347
|
}
|
|
3155
2348
|
const terminal = new PrintTerminal();
|
|
3156
|
-
const paths = (0,
|
|
3157
|
-
const session = (0,
|
|
2349
|
+
const paths = (0, import_agent_sdk3.projectPaths)(cwd);
|
|
2350
|
+
const session = (0, import_agent_sdk3.createSession)({
|
|
3158
2351
|
config,
|
|
3159
2352
|
context,
|
|
3160
2353
|
terminal,
|
|
3161
|
-
sessionLogger: new
|
|
2354
|
+
sessionLogger: new import_agent_sdk3.FileSessionLogger(paths.logs),
|
|
3162
2355
|
projectInfo,
|
|
3163
2356
|
permissionMode: args.permissionMode,
|
|
3164
|
-
promptForApproval:
|
|
2357
|
+
promptForApproval: import_agent_sdk4.promptForApproval
|
|
3165
2358
|
});
|
|
3166
2359
|
const response = await session.run(prompt);
|
|
3167
2360
|
process.stdout.write(response + "\n");
|