@robota-sdk/agent-cli 3.0.0-beta.40 → 3.0.0-beta.41
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 +129 -113
- package/dist/node/bin.js +1 -1
- package/dist/node/{chunk-CRPNSO52.js → chunk-KX3JUGSB.js} +459 -1274
- package/dist/node/index.cjs +510 -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");
|
|
@@ -815,518 +259,63 @@ async function preprocessShellCommands(content) {
|
|
|
815
259
|
let output = "";
|
|
816
260
|
try {
|
|
817
261
|
output = (0, import_node_child_process.execSync)(command, {
|
|
818
|
-
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
|
-
}
|
|
262
|
+
timeout: 5e3,
|
|
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,293 @@ 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
|
+
setIsAborting(false);
|
|
465
|
+
streamBuf = "";
|
|
466
|
+
setStreamingText("");
|
|
467
|
+
setActiveTools([]);
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
const onComplete = (result) => {
|
|
471
|
+
setContextState({
|
|
472
|
+
percentage: result.contextState.usedPercentage,
|
|
473
|
+
usedTokens: result.contextState.usedTokens,
|
|
474
|
+
maxTokens: result.contextState.maxTokens
|
|
475
|
+
});
|
|
476
|
+
};
|
|
477
|
+
const onInterrupted = () => {
|
|
478
|
+
};
|
|
479
|
+
const onError = () => {
|
|
480
|
+
};
|
|
481
|
+
interactiveSession.on("text_delta", onTextDelta);
|
|
482
|
+
interactiveSession.on("tool_start", onToolStart);
|
|
483
|
+
interactiveSession.on("tool_end", onToolEnd);
|
|
484
|
+
interactiveSession.on("thinking", onThinking);
|
|
485
|
+
interactiveSession.on("complete", onComplete);
|
|
486
|
+
interactiveSession.on("interrupted", onInterrupted);
|
|
487
|
+
interactiveSession.on("error", onError);
|
|
488
|
+
return () => {
|
|
489
|
+
interactiveSession.off("text_delta", onTextDelta);
|
|
490
|
+
interactiveSession.off("tool_start", onToolStart);
|
|
491
|
+
interactiveSession.off("tool_end", onToolEnd);
|
|
492
|
+
interactiveSession.off("thinking", onThinking);
|
|
493
|
+
interactiveSession.off("complete", onComplete);
|
|
494
|
+
interactiveSession.off("interrupted", onInterrupted);
|
|
495
|
+
interactiveSession.off("error", onError);
|
|
496
|
+
};
|
|
497
|
+
}, [interactiveSession]);
|
|
498
|
+
(0, import_react.useEffect)(() => {
|
|
499
|
+
if (!isThinking) {
|
|
500
|
+
const sessionMessages = interactiveSession.getMessages();
|
|
501
|
+
if (sessionMessages.length > 0) {
|
|
502
|
+
setMessages(
|
|
503
|
+
sessionMessages.length > MAX_RENDERED_MESSAGES ? sessionMessages.slice(-MAX_RENDERED_MESSAGES) : [...sessionMessages]
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
1389
507
|
}
|
|
1390
|
-
|
|
508
|
+
}, [isThinking, interactiveSession]);
|
|
509
|
+
const handleSubmit = (0, import_react.useCallback)(
|
|
510
|
+
async (input) => {
|
|
511
|
+
if (input.startsWith("/")) {
|
|
512
|
+
const parts = input.slice(1).split(/\s+/);
|
|
513
|
+
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
514
|
+
const args = parts.slice(1).join(" ");
|
|
515
|
+
const result = await commandExecutor.execute(cmd, interactiveSession, args);
|
|
516
|
+
if (result) {
|
|
517
|
+
addMessage((0, import_agent_core.createSystemMessage)(result.message));
|
|
518
|
+
const effects = interactiveSession;
|
|
519
|
+
if (result.data?.modelId) {
|
|
520
|
+
effects._pendingModelId = result.data.modelId;
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (result.data?.language) {
|
|
524
|
+
effects._pendingLanguage = result.data.language;
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
if (result.data?.resetRequested) {
|
|
528
|
+
effects._resetRequested = true;
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const ctx = interactiveSession.getContextState();
|
|
532
|
+
setContextState({
|
|
533
|
+
percentage: ctx.usedPercentage,
|
|
534
|
+
usedTokens: ctx.usedTokens,
|
|
535
|
+
maxTokens: ctx.maxTokens
|
|
536
|
+
});
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
540
|
+
if (skillCmd) {
|
|
541
|
+
const prompt = await buildSkillPrompt(input, registry);
|
|
542
|
+
if (prompt) {
|
|
543
|
+
await interactiveSession.submit(prompt);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (cmd === "exit") {
|
|
548
|
+
interactiveSession._exitRequested = true;
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
if (cmd === "plugin") {
|
|
552
|
+
interactiveSession._triggerPluginTUI = true;
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
addMessage((0, import_agent_core.createSystemMessage)(`Unknown command "/${cmd}". Type /help for help.`));
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
await interactiveSession.submit(input);
|
|
559
|
+
},
|
|
560
|
+
[interactiveSession, commandExecutor, registry, addMessage]
|
|
561
|
+
);
|
|
562
|
+
const handleAbort = (0, import_react.useCallback)(() => {
|
|
563
|
+
setIsAborting(true);
|
|
564
|
+
interactiveSession.abort();
|
|
565
|
+
}, [interactiveSession]);
|
|
566
|
+
const handleCancelQueue = (0, import_react.useCallback)(() => {
|
|
567
|
+
interactiveSession.cancelQueue();
|
|
568
|
+
setPendingPrompt(null);
|
|
569
|
+
}, [interactiveSession]);
|
|
570
|
+
if (contextState.maxTokens === 0) {
|
|
571
|
+
const ctx = interactiveSession.getContextState();
|
|
572
|
+
setContextState({
|
|
573
|
+
percentage: ctx.usedPercentage,
|
|
574
|
+
usedTokens: ctx.usedTokens,
|
|
575
|
+
maxTokens: ctx.maxTokens
|
|
576
|
+
});
|
|
1391
577
|
}
|
|
1392
|
-
return
|
|
578
|
+
return {
|
|
579
|
+
interactiveSession,
|
|
580
|
+
registry,
|
|
581
|
+
commandExecutor,
|
|
582
|
+
pluginHooks: stateRef.current.pluginHooks,
|
|
583
|
+
messages,
|
|
584
|
+
addMessage,
|
|
585
|
+
setMessages,
|
|
586
|
+
streamingText,
|
|
587
|
+
activeTools,
|
|
588
|
+
isThinking,
|
|
589
|
+
isAborting,
|
|
590
|
+
pendingPrompt,
|
|
591
|
+
permissionRequest,
|
|
592
|
+
contextState,
|
|
593
|
+
handleSubmit,
|
|
594
|
+
handleAbort,
|
|
595
|
+
handleCancelQueue
|
|
596
|
+
};
|
|
1393
597
|
}
|
|
1394
598
|
|
|
1395
599
|
// src/ui/hooks/usePluginCallbacks.ts
|
|
1396
|
-
var
|
|
1397
|
-
var
|
|
600
|
+
var import_react2 = require("react");
|
|
601
|
+
var import_node_os2 = require("os");
|
|
1398
602
|
var import_node_path4 = require("path");
|
|
1399
|
-
var
|
|
603
|
+
var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
|
|
1400
604
|
function usePluginCallbacks(cwd) {
|
|
1401
|
-
return (0,
|
|
1402
|
-
const home = (0,
|
|
605
|
+
return (0, import_react2.useMemo)(() => {
|
|
606
|
+
const home = (0, import_node_os2.homedir)();
|
|
1403
607
|
const pluginsDir = (0, import_node_path4.join)(home, ".robota", "plugins");
|
|
1404
608
|
const userSettingsPath = (0, import_node_path4.join)(home, ".robota", "settings.json");
|
|
1405
|
-
const settingsStore = new
|
|
1406
|
-
const marketplace = new
|
|
1407
|
-
const installer = new
|
|
609
|
+
const settingsStore = new import_agent_sdk2.PluginSettingsStore(userSettingsPath);
|
|
610
|
+
const marketplace = new import_agent_sdk2.MarketplaceClient({ pluginsDir });
|
|
611
|
+
const installer = new import_agent_sdk2.BundlePluginInstaller({
|
|
1408
612
|
pluginsDir,
|
|
1409
613
|
settingsStore,
|
|
1410
614
|
marketplaceClient: marketplace
|
|
1411
615
|
});
|
|
1412
|
-
const loader = new
|
|
616
|
+
const loader = new import_agent_sdk2.BundlePluginLoader(pluginsDir);
|
|
1413
617
|
return {
|
|
1414
618
|
listInstalled: async () => {
|
|
1415
619
|
const plugins = await loader.loadAll();
|
|
@@ -1448,7 +652,7 @@ function usePluginCallbacks(cwd) {
|
|
|
1448
652
|
}
|
|
1449
653
|
if (scope === "project") {
|
|
1450
654
|
const projectPluginsDir = (0, import_node_path4.join)(cwd, ".robota", "plugins");
|
|
1451
|
-
const projectInstaller = new
|
|
655
|
+
const projectInstaller = new import_agent_sdk2.BundlePluginInstaller({
|
|
1452
656
|
pluginsDir: projectPluginsDir,
|
|
1453
657
|
settingsStore,
|
|
1454
658
|
marketplaceClient: marketplace
|
|
@@ -1497,9 +701,9 @@ function usePluginCallbacks(cwd) {
|
|
|
1497
701
|
}
|
|
1498
702
|
|
|
1499
703
|
// src/ui/MessageList.tsx
|
|
1500
|
-
var
|
|
704
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
1501
705
|
var import_ink2 = require("ink");
|
|
1502
|
-
var
|
|
706
|
+
var import_agent_core2 = require("@robota-sdk/agent-core");
|
|
1503
707
|
|
|
1504
708
|
// src/ui/render-markdown.ts
|
|
1505
709
|
var import_marked = require("marked");
|
|
@@ -1585,7 +789,7 @@ function RoleLabel({ role }) {
|
|
|
1585
789
|
}
|
|
1586
790
|
}
|
|
1587
791
|
function ToolMessage({ message }) {
|
|
1588
|
-
if (!(0,
|
|
792
|
+
if (!(0, import_agent_core2.isToolMessage)(message)) {
|
|
1589
793
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
|
|
1590
794
|
}
|
|
1591
795
|
const toolName = message.name;
|
|
@@ -1645,10 +849,10 @@ function ToolMessage({ message }) {
|
|
|
1645
849
|
] }, i))
|
|
1646
850
|
] });
|
|
1647
851
|
}
|
|
1648
|
-
var MessageItem =
|
|
852
|
+
var MessageItem = import_react3.default.memo(function MessageItem2({
|
|
1649
853
|
message
|
|
1650
854
|
}) {
|
|
1651
|
-
if ((0,
|
|
855
|
+
if ((0, import_agent_core2.isToolMessage)(message)) {
|
|
1652
856
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolMessage, { message });
|
|
1653
857
|
}
|
|
1654
858
|
const content = message.content ?? "";
|
|
@@ -1656,7 +860,7 @@ var MessageItem = import_react7.default.memo(function MessageItem2({
|
|
|
1656
860
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
1657
861
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }) }),
|
|
1658
862
|
/* @__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,
|
|
863
|
+
/* @__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
864
|
] });
|
|
1661
865
|
});
|
|
1662
866
|
function MessageList({ messages }) {
|
|
@@ -1665,7 +869,7 @@ function MessageList({ messages }) {
|
|
|
1665
869
|
|
|
1666
870
|
// src/ui/StatusBar.tsx
|
|
1667
871
|
var import_ink3 = require("ink");
|
|
1668
|
-
var
|
|
872
|
+
var import_agent_core3 = require("@robota-sdk/agent-core");
|
|
1669
873
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1670
874
|
var CONTEXT_YELLOW_THRESHOLD = 70;
|
|
1671
875
|
var CONTEXT_RED_THRESHOLD = 90;
|
|
@@ -1705,9 +909,9 @@ function StatusBar({
|
|
|
1705
909
|
"Context: ",
|
|
1706
910
|
Math.round(contextPercentage),
|
|
1707
911
|
"% (",
|
|
1708
|
-
(0,
|
|
912
|
+
(0, import_agent_core3.formatTokenCount)(contextUsedTokens),
|
|
1709
913
|
"/",
|
|
1710
|
-
(0,
|
|
914
|
+
(0, import_agent_core3.formatTokenCount)(contextMaxTokens),
|
|
1711
915
|
")"
|
|
1712
916
|
] })
|
|
1713
917
|
] }),
|
|
@@ -1724,11 +928,11 @@ function StatusBar({
|
|
|
1724
928
|
}
|
|
1725
929
|
|
|
1726
930
|
// src/ui/InputArea.tsx
|
|
1727
|
-
var
|
|
931
|
+
var import_react6 = __toESM(require("react"), 1);
|
|
1728
932
|
var import_ink7 = require("ink");
|
|
1729
933
|
|
|
1730
934
|
// src/ui/CjkTextInput.tsx
|
|
1731
|
-
var
|
|
935
|
+
var import_react4 = require("react");
|
|
1732
936
|
var import_ink4 = require("ink");
|
|
1733
937
|
var import_chalk = __toESM(require("chalk"), 1);
|
|
1734
938
|
var import_string_width = __toESM(require("string-width"), 1);
|
|
@@ -1774,11 +978,11 @@ function CjkTextInput({
|
|
|
1774
978
|
showCursor = true,
|
|
1775
979
|
availableWidth
|
|
1776
980
|
}) {
|
|
1777
|
-
const valueRef = (0,
|
|
1778
|
-
const cursorRef = (0,
|
|
1779
|
-
const [, forceRender] = (0,
|
|
1780
|
-
const isPastingRef = (0,
|
|
1781
|
-
const pasteBufferRef = (0,
|
|
981
|
+
const valueRef = (0, import_react4.useRef)(value);
|
|
982
|
+
const cursorRef = (0, import_react4.useRef)(value.length);
|
|
983
|
+
const [, forceRender] = (0, import_react4.useState)(0);
|
|
984
|
+
const isPastingRef = (0, import_react4.useRef)(false);
|
|
985
|
+
const pasteBufferRef = (0, import_react4.useRef)("");
|
|
1782
986
|
if (value !== valueRef.current) {
|
|
1783
987
|
valueRef.current = value;
|
|
1784
988
|
if (cursorRef.current > value.length) {
|
|
@@ -1907,15 +1111,15 @@ function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
|
|
|
1907
1111
|
}
|
|
1908
1112
|
|
|
1909
1113
|
// src/ui/WaveText.tsx
|
|
1910
|
-
var
|
|
1114
|
+
var import_react5 = require("react");
|
|
1911
1115
|
var import_ink5 = require("ink");
|
|
1912
1116
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1913
1117
|
var WAVE_COLORS = ["#666666", "#888888", "#aaaaaa", "#888888"];
|
|
1914
1118
|
var INTERVAL_MS = 400;
|
|
1915
1119
|
var CHARS_PER_GROUP = 4;
|
|
1916
1120
|
function WaveText({ text }) {
|
|
1917
|
-
const [tick, setTick] = (0,
|
|
1918
|
-
(0,
|
|
1121
|
+
const [tick, setTick] = (0, import_react5.useState)(0);
|
|
1122
|
+
(0, import_react5.useEffect)(() => {
|
|
1919
1123
|
const timer = setInterval(() => {
|
|
1920
1124
|
setTick((prev) => prev + 1);
|
|
1921
1125
|
}, INTERVAL_MS);
|
|
@@ -1987,16 +1191,16 @@ function parseSlashInput(value) {
|
|
|
1987
1191
|
return { isSlash: true, parentCommand: parent, filter: rest };
|
|
1988
1192
|
}
|
|
1989
1193
|
function useAutocomplete(value, registry) {
|
|
1990
|
-
const [selectedIndex, setSelectedIndex] = (0,
|
|
1991
|
-
const [dismissed, setDismissed] = (0,
|
|
1992
|
-
const prevValueRef =
|
|
1194
|
+
const [selectedIndex, setSelectedIndex] = (0, import_react6.useState)(0);
|
|
1195
|
+
const [dismissed, setDismissed] = (0, import_react6.useState)(false);
|
|
1196
|
+
const prevValueRef = import_react6.default.useRef(value);
|
|
1993
1197
|
if (prevValueRef.current !== value) {
|
|
1994
1198
|
prevValueRef.current = value;
|
|
1995
1199
|
if (dismissed) setDismissed(false);
|
|
1996
1200
|
}
|
|
1997
1201
|
const parsed = parseSlashInput(value);
|
|
1998
1202
|
const isSubcommandMode = parsed.isSlash && parsed.parentCommand.length > 0;
|
|
1999
|
-
const filteredCommands = (0,
|
|
1203
|
+
const filteredCommands = (0, import_react6.useMemo)(() => {
|
|
2000
1204
|
if (!registry || !parsed.isSlash || dismissed) return [];
|
|
2001
1205
|
if (isSubcommandMode) {
|
|
2002
1206
|
const subs = registry.getSubcommands(parsed.parentCommand);
|
|
@@ -2041,12 +1245,12 @@ function InputArea({
|
|
|
2041
1245
|
pendingPrompt,
|
|
2042
1246
|
registry
|
|
2043
1247
|
}) {
|
|
2044
|
-
const [value, setValue] = (0,
|
|
2045
|
-
const pasteStore = (0,
|
|
1248
|
+
const [value, setValue] = (0, import_react6.useState)("");
|
|
1249
|
+
const pasteStore = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
|
|
2046
1250
|
const { stdout } = (0, import_ink7.useStdout)();
|
|
2047
1251
|
const terminalColumns = stdout?.columns ?? 80;
|
|
2048
1252
|
const availableWidth = Math.max(1, terminalColumns - INPUT_AREA_OVERHEAD);
|
|
2049
|
-
const pasteIdRef = (0,
|
|
1253
|
+
const pasteIdRef = (0, import_react6.useRef)(0);
|
|
2050
1254
|
const {
|
|
2051
1255
|
showPopup,
|
|
2052
1256
|
filteredCommands,
|
|
@@ -2055,7 +1259,7 @@ function InputArea({
|
|
|
2055
1259
|
isSubcommandMode,
|
|
2056
1260
|
setShowPopup
|
|
2057
1261
|
} = useAutocomplete(value, registry);
|
|
2058
|
-
const handlePaste = (0,
|
|
1262
|
+
const handlePaste = (0, import_react6.useCallback)((text) => {
|
|
2059
1263
|
pasteIdRef.current += 1;
|
|
2060
1264
|
const id = pasteIdRef.current;
|
|
2061
1265
|
pasteStore.current.set(id, text);
|
|
@@ -2063,7 +1267,7 @@ function InputArea({
|
|
|
2063
1267
|
const label = `[Pasted text #${id} +${lineCount} lines]`;
|
|
2064
1268
|
setValue((prev) => prev ? `${prev} ${label}` : label);
|
|
2065
1269
|
}, []);
|
|
2066
|
-
const handleSubmit = (0,
|
|
1270
|
+
const handleSubmit = (0, import_react6.useCallback)(
|
|
2067
1271
|
(text) => {
|
|
2068
1272
|
const trimmed = text.trim();
|
|
2069
1273
|
if (trimmed.length === 0) return;
|
|
@@ -2079,7 +1283,7 @@ function InputArea({
|
|
|
2079
1283
|
},
|
|
2080
1284
|
[showPopup, filteredCommands, selectedIndex, onSubmit]
|
|
2081
1285
|
);
|
|
2082
|
-
const selectCommand = (0,
|
|
1286
|
+
const selectCommand = (0, import_react6.useCallback)(
|
|
2083
1287
|
(cmd) => {
|
|
2084
1288
|
const parsed = parseSlashInput(value);
|
|
2085
1289
|
if (parsed.parentCommand) {
|
|
@@ -2164,7 +1368,7 @@ function InputArea({
|
|
|
2164
1368
|
}
|
|
2165
1369
|
|
|
2166
1370
|
// src/ui/ConfirmPrompt.tsx
|
|
2167
|
-
var
|
|
1371
|
+
var import_react7 = require("react");
|
|
2168
1372
|
var import_ink8 = require("ink");
|
|
2169
1373
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2170
1374
|
function ConfirmPrompt({
|
|
@@ -2172,9 +1376,9 @@ function ConfirmPrompt({
|
|
|
2172
1376
|
options = ["Yes", "No"],
|
|
2173
1377
|
onSelect
|
|
2174
1378
|
}) {
|
|
2175
|
-
const [selected, setSelected] = (0,
|
|
2176
|
-
const resolvedRef = (0,
|
|
2177
|
-
const doSelect = (0,
|
|
1379
|
+
const [selected, setSelected] = (0, import_react7.useState)(0);
|
|
1380
|
+
const resolvedRef = (0, import_react7.useRef)(false);
|
|
1381
|
+
const doSelect = (0, import_react7.useCallback)(
|
|
2178
1382
|
(index) => {
|
|
2179
1383
|
if (resolvedRef.current) return;
|
|
2180
1384
|
resolvedRef.current = true;
|
|
@@ -2207,7 +1411,7 @@ function ConfirmPrompt({
|
|
|
2207
1411
|
}
|
|
2208
1412
|
|
|
2209
1413
|
// src/ui/PermissionPrompt.tsx
|
|
2210
|
-
var
|
|
1414
|
+
var import_react8 = __toESM(require("react"), 1);
|
|
2211
1415
|
var import_ink9 = require("ink");
|
|
2212
1416
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2213
1417
|
var OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
|
|
@@ -2217,15 +1421,15 @@ function formatArgs(args) {
|
|
|
2217
1421
|
return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
2218
1422
|
}
|
|
2219
1423
|
function PermissionPrompt({ request }) {
|
|
2220
|
-
const [selected, setSelected] =
|
|
2221
|
-
const resolvedRef =
|
|
2222
|
-
const prevRequestRef =
|
|
1424
|
+
const [selected, setSelected] = import_react8.default.useState(0);
|
|
1425
|
+
const resolvedRef = import_react8.default.useRef(false);
|
|
1426
|
+
const prevRequestRef = import_react8.default.useRef(request);
|
|
2223
1427
|
if (prevRequestRef.current !== request) {
|
|
2224
1428
|
prevRequestRef.current = request;
|
|
2225
1429
|
resolvedRef.current = false;
|
|
2226
1430
|
setSelected(0);
|
|
2227
1431
|
}
|
|
2228
|
-
const doResolve =
|
|
1432
|
+
const doResolve = import_react8.default.useCallback(
|
|
2229
1433
|
(index) => {
|
|
2230
1434
|
if (resolvedRef.current) return;
|
|
2231
1435
|
resolvedRef.current = true;
|
|
@@ -2314,10 +1518,10 @@ function StreamingIndicator({ text, activeTools }) {
|
|
|
2314
1518
|
}
|
|
2315
1519
|
|
|
2316
1520
|
// src/ui/PluginTUI.tsx
|
|
2317
|
-
var
|
|
1521
|
+
var import_react11 = require("react");
|
|
2318
1522
|
|
|
2319
1523
|
// src/ui/MenuSelect.tsx
|
|
2320
|
-
var
|
|
1524
|
+
var import_react9 = require("react");
|
|
2321
1525
|
var import_ink11 = require("ink");
|
|
2322
1526
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2323
1527
|
function MenuSelect({
|
|
@@ -2328,10 +1532,10 @@ function MenuSelect({
|
|
|
2328
1532
|
loading,
|
|
2329
1533
|
error
|
|
2330
1534
|
}) {
|
|
2331
|
-
const [selected, setSelected] = (0,
|
|
2332
|
-
const selectedRef = (0,
|
|
2333
|
-
const resolvedRef = (0,
|
|
2334
|
-
const doSelect = (0,
|
|
1535
|
+
const [selected, setSelected] = (0, import_react9.useState)(0);
|
|
1536
|
+
const selectedRef = (0, import_react9.useRef)(0);
|
|
1537
|
+
const resolvedRef = (0, import_react9.useRef)(false);
|
|
1538
|
+
const doSelect = (0, import_react9.useCallback)(
|
|
2335
1539
|
(index) => {
|
|
2336
1540
|
if (resolvedRef.current || items.length === 0) return;
|
|
2337
1541
|
resolvedRef.current = true;
|
|
@@ -2381,7 +1585,7 @@ function MenuSelect({
|
|
|
2381
1585
|
}
|
|
2382
1586
|
|
|
2383
1587
|
// src/ui/TextPrompt.tsx
|
|
2384
|
-
var
|
|
1588
|
+
var import_react10 = require("react");
|
|
2385
1589
|
var import_ink12 = require("ink");
|
|
2386
1590
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2387
1591
|
function TextPrompt({
|
|
@@ -2391,11 +1595,11 @@ function TextPrompt({
|
|
|
2391
1595
|
onCancel,
|
|
2392
1596
|
validate
|
|
2393
1597
|
}) {
|
|
2394
|
-
const [value, setValue] = (0,
|
|
2395
|
-
const [error, setError] = (0,
|
|
2396
|
-
const resolvedRef = (0,
|
|
2397
|
-
const valueRef = (0,
|
|
2398
|
-
const handleSubmit = (0,
|
|
1598
|
+
const [value, setValue] = (0, import_react10.useState)("");
|
|
1599
|
+
const [error, setError] = (0, import_react10.useState)();
|
|
1600
|
+
const resolvedRef = (0, import_react10.useRef)(false);
|
|
1601
|
+
const valueRef = (0, import_react10.useRef)("");
|
|
1602
|
+
const handleSubmit = (0, import_react10.useCallback)(() => {
|
|
2399
1603
|
if (resolvedRef.current) return;
|
|
2400
1604
|
const trimmed = valueRef.current.trim();
|
|
2401
1605
|
if (!trimmed) return;
|
|
@@ -2539,19 +1743,19 @@ function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
|
|
|
2539
1743
|
// src/ui/PluginTUI.tsx
|
|
2540
1744
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2541
1745
|
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,
|
|
1746
|
+
const [stack, setStack] = (0, import_react11.useState)([{ screen: "main" }]);
|
|
1747
|
+
const [items, setItems] = (0, import_react11.useState)([]);
|
|
1748
|
+
const [loading, setLoading] = (0, import_react11.useState)(false);
|
|
1749
|
+
const [error, setError] = (0, import_react11.useState)();
|
|
1750
|
+
const [confirm, setConfirm] = (0, import_react11.useState)();
|
|
1751
|
+
const [refreshCounter, setRefreshCounter] = (0, import_react11.useState)(0);
|
|
2548
1752
|
const current = stack[stack.length - 1] ?? { screen: "main" };
|
|
2549
|
-
const push = (0,
|
|
1753
|
+
const push = (0, import_react11.useCallback)((state) => {
|
|
2550
1754
|
setStack((prev) => [...prev, state]);
|
|
2551
1755
|
setItems([]);
|
|
2552
1756
|
setError(void 0);
|
|
2553
1757
|
}, []);
|
|
2554
|
-
const pop = (0,
|
|
1758
|
+
const pop = (0, import_react11.useCallback)(() => {
|
|
2555
1759
|
setStack((prev) => {
|
|
2556
1760
|
if (prev.length <= 1) {
|
|
2557
1761
|
onClose();
|
|
@@ -2562,7 +1766,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2562
1766
|
setItems([]);
|
|
2563
1767
|
setError(void 0);
|
|
2564
1768
|
}, [onClose]);
|
|
2565
|
-
const popN = (0,
|
|
1769
|
+
const popN = (0, import_react11.useCallback)(
|
|
2566
1770
|
(n) => {
|
|
2567
1771
|
setStack((prev) => {
|
|
2568
1772
|
const next = prev.slice(0, Math.max(1, prev.length - n));
|
|
@@ -2577,18 +1781,18 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2577
1781
|
},
|
|
2578
1782
|
[onClose]
|
|
2579
1783
|
);
|
|
2580
|
-
const notify = (0,
|
|
1784
|
+
const notify = (0, import_react11.useCallback)(
|
|
2581
1785
|
(content) => {
|
|
2582
1786
|
addMessage?.({ role: "system", content });
|
|
2583
1787
|
},
|
|
2584
1788
|
[addMessage]
|
|
2585
1789
|
);
|
|
2586
|
-
const refresh = (0,
|
|
1790
|
+
const refresh = (0, import_react11.useCallback)(() => {
|
|
2587
1791
|
setItems([]);
|
|
2588
1792
|
setRefreshCounter((c) => c + 1);
|
|
2589
1793
|
}, []);
|
|
2590
1794
|
const nav = { push, pop, popN, notify, setConfirm, refresh };
|
|
2591
|
-
(0,
|
|
1795
|
+
(0, import_react11.useEffect)(() => {
|
|
2592
1796
|
const screen2 = current.screen;
|
|
2593
1797
|
if (screen2 === "marketplace-list") {
|
|
2594
1798
|
setLoading(true);
|
|
@@ -2638,7 +1842,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2638
1842
|
});
|
|
2639
1843
|
}
|
|
2640
1844
|
}, [stack.length, current.screen, current.context?.marketplace, callbacks, refreshCounter]);
|
|
2641
|
-
const handleSelect = (0,
|
|
1845
|
+
const handleSelect = (0, import_react11.useCallback)(
|
|
2642
1846
|
(value) => {
|
|
2643
1847
|
const screen2 = current.screen;
|
|
2644
1848
|
const ctx = current.context;
|
|
@@ -2656,7 +1860,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2656
1860
|
},
|
|
2657
1861
|
[current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
|
|
2658
1862
|
);
|
|
2659
|
-
const handleTextSubmit = (0,
|
|
1863
|
+
const handleTextSubmit = (0, import_react11.useCallback)(
|
|
2660
1864
|
(value) => {
|
|
2661
1865
|
if (current.screen === "marketplace-add") {
|
|
2662
1866
|
callbacks.marketplaceAdd(value).then((name) => {
|
|
@@ -2766,104 +1970,90 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2766
1970
|
|
|
2767
1971
|
// src/ui/App.tsx
|
|
2768
1972
|
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
|
-
}
|
|
1973
|
+
var EXIT_DELAY_MS = 500;
|
|
2786
1974
|
function App(props) {
|
|
2787
1975
|
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,
|
|
1976
|
+
const cwd = props.cwd ?? process.cwd();
|
|
1977
|
+
const {
|
|
1978
|
+
interactiveSession,
|
|
2819
1979
|
registry,
|
|
2820
|
-
|
|
2821
|
-
setPendingModelId,
|
|
2822
|
-
pluginCallbacks,
|
|
2823
|
-
setShowPluginTUI
|
|
2824
|
-
);
|
|
2825
|
-
const executePrompt = useSubmitHandler(
|
|
2826
|
-
session,
|
|
1980
|
+
messages,
|
|
2827
1981
|
addMessage,
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
1982
|
+
streamingText,
|
|
1983
|
+
activeTools,
|
|
1984
|
+
isThinking,
|
|
1985
|
+
isAborting,
|
|
1986
|
+
pendingPrompt,
|
|
1987
|
+
permissionRequest,
|
|
1988
|
+
contextState,
|
|
1989
|
+
handleSubmit: baseHandleSubmit,
|
|
1990
|
+
handleAbort,
|
|
1991
|
+
handleCancelQueue
|
|
1992
|
+
} = useInteractiveSession({
|
|
1993
|
+
config: props.config,
|
|
1994
|
+
context: props.context,
|
|
1995
|
+
projectInfo: props.projectInfo,
|
|
1996
|
+
sessionStore: props.sessionStore,
|
|
1997
|
+
permissionMode: props.permissionMode,
|
|
1998
|
+
maxTurns: props.maxTurns,
|
|
1999
|
+
cwd
|
|
2000
|
+
});
|
|
2001
|
+
const pluginCallbacks = usePluginCallbacks(cwd);
|
|
2002
|
+
const [pendingModelId, setPendingModelId] = (0, import_react12.useState)(null);
|
|
2003
|
+
const pendingModelChangeRef = (0, import_react12.useRef)(null);
|
|
2004
|
+
const [showPluginTUI, setShowPluginTUI] = (0, import_react12.useState)(false);
|
|
2005
|
+
const handleSubmit = async (input) => {
|
|
2006
|
+
await baseHandleSubmit(input);
|
|
2007
|
+
const sideEffects = interactiveSession;
|
|
2008
|
+
if (sideEffects._pendingModelId) {
|
|
2009
|
+
const modelId = sideEffects._pendingModelId;
|
|
2010
|
+
delete sideEffects._pendingModelId;
|
|
2011
|
+
pendingModelChangeRef.current = modelId;
|
|
2012
|
+
setPendingModelId(modelId);
|
|
2013
|
+
return;
|
|
2014
|
+
}
|
|
2015
|
+
if (sideEffects._pendingLanguage) {
|
|
2016
|
+
const lang = sideEffects._pendingLanguage;
|
|
2017
|
+
delete sideEffects._pendingLanguage;
|
|
2018
|
+
const settingsPath = getUserSettingsPath();
|
|
2019
|
+
const settings = readSettings(settingsPath);
|
|
2020
|
+
settings.language = lang;
|
|
2021
|
+
writeSettings(settingsPath, settings);
|
|
2022
|
+
addMessage((0, import_agent_core4.createSystemMessage)(`Language set to "${lang}". Restarting...`));
|
|
2023
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
if (sideEffects._resetRequested) {
|
|
2027
|
+
delete sideEffects._resetRequested;
|
|
2028
|
+
const settingsPath = getUserSettingsPath();
|
|
2029
|
+
if (deleteSettings(settingsPath)) {
|
|
2030
|
+
addMessage((0, import_agent_core4.createSystemMessage)(`Deleted ${settingsPath}. Exiting...`));
|
|
2031
|
+
} else {
|
|
2032
|
+
addMessage((0, import_agent_core4.createSystemMessage)("No user settings found."));
|
|
2840
2033
|
}
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2034
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
if (sideEffects._exitRequested) {
|
|
2038
|
+
delete sideEffects._exitRequested;
|
|
2039
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
if (sideEffects._triggerPluginTUI) {
|
|
2043
|
+
delete sideEffects._triggerPluginTUI;
|
|
2044
|
+
setShowPluginTUI(true);
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
};
|
|
2845
2048
|
(0, import_ink13.useInput)(
|
|
2846
2049
|
(_input, key) => {
|
|
2847
2050
|
if (key.escape && isThinking) {
|
|
2848
|
-
|
|
2849
|
-
setPendingPrompt(null);
|
|
2850
|
-
pendingPromptRef.current = null;
|
|
2851
|
-
session.abort();
|
|
2051
|
+
handleAbort();
|
|
2852
2052
|
}
|
|
2853
2053
|
},
|
|
2854
2054
|
{ isActive: !permissionRequest && !showPluginTUI }
|
|
2855
2055
|
);
|
|
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]);
|
|
2056
|
+
const session = interactiveSession.getSession();
|
|
2867
2057
|
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", children: [
|
|
2868
2058
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
2869
2059
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { color: "cyan", bold: true, children: `
|
|
@@ -2886,7 +2076,7 @@ function App(props) {
|
|
|
2886
2076
|
pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2887
2077
|
ConfirmPrompt,
|
|
2888
2078
|
{
|
|
2889
|
-
message: `Change model to ${(0,
|
|
2079
|
+
message: `Change model to ${(0, import_agent_core4.getModelName)(pendingModelId)}? This will restart the session.`,
|
|
2890
2080
|
onSelect: (index) => {
|
|
2891
2081
|
setPendingModelId(null);
|
|
2892
2082
|
pendingModelChangeRef.current = null;
|
|
@@ -2895,20 +2085,20 @@ function App(props) {
|
|
|
2895
2085
|
const settingsPath = getUserSettingsPath();
|
|
2896
2086
|
updateModelInSettings(settingsPath, pendingModelId);
|
|
2897
2087
|
addMessage(
|
|
2898
|
-
(0,
|
|
2899
|
-
`Model changed to ${(0,
|
|
2088
|
+
(0, import_agent_core4.createSystemMessage)(
|
|
2089
|
+
`Model changed to ${(0, import_agent_core4.getModelName)(pendingModelId)}. Restarting...`
|
|
2900
2090
|
)
|
|
2901
2091
|
);
|
|
2902
|
-
setTimeout(() => exit(),
|
|
2092
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2903
2093
|
} catch (err) {
|
|
2904
2094
|
addMessage(
|
|
2905
|
-
(0,
|
|
2095
|
+
(0, import_agent_core4.createSystemMessage)(
|
|
2906
2096
|
`Failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2907
2097
|
)
|
|
2908
2098
|
);
|
|
2909
2099
|
}
|
|
2910
2100
|
} else {
|
|
2911
|
-
addMessage((0,
|
|
2101
|
+
addMessage((0, import_agent_core4.createSystemMessage)("Model change cancelled."));
|
|
2912
2102
|
}
|
|
2913
2103
|
}
|
|
2914
2104
|
}
|
|
@@ -2918,14 +2108,14 @@ function App(props) {
|
|
|
2918
2108
|
{
|
|
2919
2109
|
callbacks: pluginCallbacks,
|
|
2920
2110
|
onClose: () => setShowPluginTUI(false),
|
|
2921
|
-
addMessage: (msg) => addMessage((0,
|
|
2111
|
+
addMessage: (msg) => addMessage((0, import_agent_core4.createSystemMessage)(msg.content))
|
|
2922
2112
|
}
|
|
2923
2113
|
),
|
|
2924
2114
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2925
2115
|
StatusBar,
|
|
2926
2116
|
{
|
|
2927
2117
|
permissionMode: session.getPermissionMode(),
|
|
2928
|
-
modelName: (0,
|
|
2118
|
+
modelName: (0, import_agent_core4.getModelName)(props.config.provider.model),
|
|
2929
2119
|
sessionId: session.getSessionId(),
|
|
2930
2120
|
messageCount: messages.length,
|
|
2931
2121
|
isThinking,
|
|
@@ -2938,10 +2128,7 @@ function App(props) {
|
|
|
2938
2128
|
InputArea,
|
|
2939
2129
|
{
|
|
2940
2130
|
onSubmit: handleSubmit,
|
|
2941
|
-
onCancelQueue:
|
|
2942
|
-
setPendingPrompt(null);
|
|
2943
|
-
pendingPromptRef.current = null;
|
|
2944
|
-
},
|
|
2131
|
+
onCancelQueue: handleCancelQueue,
|
|
2945
2132
|
isDisabled: !!permissionRequest || showPluginTUI || isThinking && !!pendingPrompt,
|
|
2946
2133
|
isAborting,
|
|
2947
2134
|
pendingPrompt,
|
|
@@ -2988,9 +2175,9 @@ function renderApp(options) {
|
|
|
2988
2175
|
// src/cli.ts
|
|
2989
2176
|
var import_meta = {};
|
|
2990
2177
|
function checkSettingsFile(filePath) {
|
|
2991
|
-
if (!(0,
|
|
2178
|
+
if (!(0, import_node_fs2.existsSync)(filePath)) return "missing";
|
|
2992
2179
|
try {
|
|
2993
|
-
const raw = (0,
|
|
2180
|
+
const raw = (0, import_node_fs2.readFileSync)(filePath, "utf8").trim();
|
|
2994
2181
|
if (raw.length === 0) return "incomplete";
|
|
2995
2182
|
const parsed = JSON.parse(raw);
|
|
2996
2183
|
const provider = parsed.provider;
|
|
@@ -3007,7 +2194,7 @@ function readVersion() {
|
|
|
3007
2194
|
const candidates = [(0, import_node_path5.join)(dir, "..", "..", "package.json"), (0, import_node_path5.join)(dir, "..", "package.json")];
|
|
3008
2195
|
for (const pkgPath of candidates) {
|
|
3009
2196
|
try {
|
|
3010
|
-
const raw = (0,
|
|
2197
|
+
const raw = (0, import_node_fs2.readFileSync)(pkgPath, "utf-8");
|
|
3011
2198
|
const pkg = JSON.parse(raw);
|
|
3012
2199
|
if (pkg.version !== void 0 && pkg.name !== void 0) {
|
|
3013
2200
|
return pkg.version;
|
|
@@ -3095,7 +2282,7 @@ async function ensureConfig(cwd) {
|
|
|
3095
2282
|
}
|
|
3096
2283
|
const language = await promptInput(" Response language (ko/en/ja/zh, default: en): ");
|
|
3097
2284
|
const settingsDir = (0, import_node_path5.dirname)(userPath);
|
|
3098
|
-
(0,
|
|
2285
|
+
(0, import_node_fs2.mkdirSync)(settingsDir, { recursive: true });
|
|
3099
2286
|
const settings = {
|
|
3100
2287
|
provider: {
|
|
3101
2288
|
name: "anthropic",
|
|
@@ -3106,7 +2293,7 @@ async function ensureConfig(cwd) {
|
|
|
3106
2293
|
if (language) {
|
|
3107
2294
|
settings.language = language;
|
|
3108
2295
|
}
|
|
3109
|
-
(0,
|
|
2296
|
+
(0, import_node_fs2.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
3110
2297
|
process.stdout.write(`
|
|
3111
2298
|
Config saved to ${userPath}
|
|
3112
2299
|
|
|
@@ -3135,9 +2322,9 @@ async function startCli() {
|
|
|
3135
2322
|
const cwd = process.cwd();
|
|
3136
2323
|
await ensureConfig(cwd);
|
|
3137
2324
|
const [config, context, projectInfo] = await Promise.all([
|
|
3138
|
-
(0,
|
|
3139
|
-
(0,
|
|
3140
|
-
(0,
|
|
2325
|
+
(0, import_agent_sdk3.loadConfig)(cwd),
|
|
2326
|
+
(0, import_agent_sdk3.loadContext)(cwd),
|
|
2327
|
+
(0, import_agent_sdk3.detectProject)(cwd)
|
|
3141
2328
|
]);
|
|
3142
2329
|
if (args.model !== void 0) {
|
|
3143
2330
|
config.provider.model = args.model;
|
|
@@ -3145,7 +2332,7 @@ async function startCli() {
|
|
|
3145
2332
|
if (args.language !== void 0) {
|
|
3146
2333
|
config.language = args.language;
|
|
3147
2334
|
}
|
|
3148
|
-
const sessionStore = new
|
|
2335
|
+
const sessionStore = new import_agent_sdk3.SessionStore();
|
|
3149
2336
|
if (args.printMode) {
|
|
3150
2337
|
const prompt = args.positional.join(" ").trim();
|
|
3151
2338
|
if (prompt.length === 0) {
|
|
@@ -3153,15 +2340,15 @@ async function startCli() {
|
|
|
3153
2340
|
process.exit(1);
|
|
3154
2341
|
}
|
|
3155
2342
|
const terminal = new PrintTerminal();
|
|
3156
|
-
const paths = (0,
|
|
3157
|
-
const session = (0,
|
|
2343
|
+
const paths = (0, import_agent_sdk3.projectPaths)(cwd);
|
|
2344
|
+
const session = (0, import_agent_sdk3.createSession)({
|
|
3158
2345
|
config,
|
|
3159
2346
|
context,
|
|
3160
2347
|
terminal,
|
|
3161
|
-
sessionLogger: new
|
|
2348
|
+
sessionLogger: new import_agent_sdk3.FileSessionLogger(paths.logs),
|
|
3162
2349
|
projectInfo,
|
|
3163
2350
|
permissionMode: args.permissionMode,
|
|
3164
|
-
promptForApproval:
|
|
2351
|
+
promptForApproval: import_agent_sdk4.promptForApproval
|
|
3165
2352
|
});
|
|
3166
2353
|
const response = await session.run(prompt);
|
|
3167
2354
|
process.stdout.write(response + "\n");
|