@robota-sdk/agent-cli 3.0.0-beta.23 → 3.0.0-beta.25
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/dist/node/bin.cjs +626 -104
- package/dist/node/bin.js +1 -1
- package/dist/node/{chunk-EFSOR6D7.js → chunk-GJ3LG37H.js} +601 -74
- package/dist/node/index.cjs +631 -109
- package/dist/node/index.js +1 -1
- package/package.json +3 -3
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_sdk6.Session,
|
|
34
|
+
SessionStore: () => import_agent_sdk6.SessionStore,
|
|
35
|
+
TRUST_TO_MODE: () => import_agent_sdk6.TRUST_TO_MODE,
|
|
36
|
+
query: () => import_agent_sdk6.query,
|
|
37
37
|
startCli: () => startCli
|
|
38
38
|
});
|
|
39
39
|
module.exports = __toCommonJS(index_exports);
|
|
40
|
-
var
|
|
40
|
+
var import_agent_sdk6 = require("@robota-sdk/agent-sdk");
|
|
41
41
|
|
|
42
42
|
// src/cli.ts
|
|
43
43
|
var import_node_fs3 = require("fs");
|
|
44
|
-
var
|
|
44
|
+
var import_node_path5 = require("path");
|
|
45
45
|
var import_node_url = require("url");
|
|
46
|
-
var
|
|
47
|
-
var
|
|
46
|
+
var import_agent_sdk4 = require("@robota-sdk/agent-sdk");
|
|
47
|
+
var import_agent_sdk5 = require("@robota-sdk/agent-sdk");
|
|
48
48
|
|
|
49
49
|
// src/utils/cli-args.ts
|
|
50
50
|
var import_node_util = require("util");
|
|
@@ -182,7 +182,7 @@ var PrintTerminal = class {
|
|
|
182
182
|
var import_ink11 = require("ink");
|
|
183
183
|
|
|
184
184
|
// src/ui/App.tsx
|
|
185
|
-
var
|
|
185
|
+
var import_react12 = require("react");
|
|
186
186
|
var import_ink10 = require("ink");
|
|
187
187
|
var import_agent_core3 = require("@robota-sdk/agent-core");
|
|
188
188
|
|
|
@@ -402,7 +402,122 @@ function handleReset(addMessage) {
|
|
|
402
402
|
}
|
|
403
403
|
return { handled: true, exitRequested: true };
|
|
404
404
|
}
|
|
405
|
-
async function
|
|
405
|
+
async function handlePluginCommand(args, addMessage, callbacks) {
|
|
406
|
+
const parts = args.trim().split(/\s+/);
|
|
407
|
+
const subcommand = parts[0] ?? "";
|
|
408
|
+
const subArgs = parts.slice(1).join(" ").trim();
|
|
409
|
+
try {
|
|
410
|
+
switch (subcommand) {
|
|
411
|
+
case "":
|
|
412
|
+
case void 0: {
|
|
413
|
+
const plugins = await callbacks.listInstalled();
|
|
414
|
+
if (plugins.length === 0) {
|
|
415
|
+
addMessage({ role: "system", content: "No plugins installed." });
|
|
416
|
+
} else {
|
|
417
|
+
const lines = plugins.map(
|
|
418
|
+
(p) => ` ${p.name} \u2014 ${p.description} [${p.enabled ? "enabled" : "disabled"}]`
|
|
419
|
+
);
|
|
420
|
+
addMessage({ role: "system", content: `Installed plugins:
|
|
421
|
+
${lines.join("\n")}` });
|
|
422
|
+
}
|
|
423
|
+
return { handled: true };
|
|
424
|
+
}
|
|
425
|
+
case "install": {
|
|
426
|
+
if (!subArgs) {
|
|
427
|
+
addMessage({ role: "system", content: "Usage: /plugin install <name>@<marketplace>" });
|
|
428
|
+
return { handled: true };
|
|
429
|
+
}
|
|
430
|
+
await callbacks.install(subArgs);
|
|
431
|
+
addMessage({ role: "system", content: `Installed plugin: ${subArgs}` });
|
|
432
|
+
return { handled: true };
|
|
433
|
+
}
|
|
434
|
+
case "uninstall": {
|
|
435
|
+
if (!subArgs) {
|
|
436
|
+
addMessage({ role: "system", content: "Usage: /plugin uninstall <name>@<marketplace>" });
|
|
437
|
+
return { handled: true };
|
|
438
|
+
}
|
|
439
|
+
await callbacks.uninstall(subArgs);
|
|
440
|
+
addMessage({ role: "system", content: `Uninstalled plugin: ${subArgs}` });
|
|
441
|
+
return { handled: true };
|
|
442
|
+
}
|
|
443
|
+
case "enable": {
|
|
444
|
+
if (!subArgs) {
|
|
445
|
+
addMessage({ role: "system", content: "Usage: /plugin enable <name>@<marketplace>" });
|
|
446
|
+
return { handled: true };
|
|
447
|
+
}
|
|
448
|
+
await callbacks.enable(subArgs);
|
|
449
|
+
addMessage({ role: "system", content: `Enabled plugin: ${subArgs}` });
|
|
450
|
+
return { handled: true };
|
|
451
|
+
}
|
|
452
|
+
case "disable": {
|
|
453
|
+
if (!subArgs) {
|
|
454
|
+
addMessage({ role: "system", content: "Usage: /plugin disable <name>@<marketplace>" });
|
|
455
|
+
return { handled: true };
|
|
456
|
+
}
|
|
457
|
+
await callbacks.disable(subArgs);
|
|
458
|
+
addMessage({ role: "system", content: `Disabled plugin: ${subArgs}` });
|
|
459
|
+
return { handled: true };
|
|
460
|
+
}
|
|
461
|
+
case "marketplace": {
|
|
462
|
+
const mpParts = subArgs.split(/\s+/);
|
|
463
|
+
const mpSubcommand = mpParts[0] ?? "";
|
|
464
|
+
const mpArgs = mpParts.slice(1).join(" ").trim();
|
|
465
|
+
if (mpSubcommand === "add" && mpArgs) {
|
|
466
|
+
const registeredName = await callbacks.marketplaceAdd(mpArgs);
|
|
467
|
+
addMessage({
|
|
468
|
+
role: "system",
|
|
469
|
+
content: `Added marketplace: "${registeredName}" (from ${mpArgs})
|
|
470
|
+
Install plugins with: /plugin install <name>@${registeredName}`
|
|
471
|
+
});
|
|
472
|
+
return { handled: true };
|
|
473
|
+
} else if (mpSubcommand === "remove" && mpArgs) {
|
|
474
|
+
await callbacks.marketplaceRemove(mpArgs);
|
|
475
|
+
addMessage({
|
|
476
|
+
role: "system",
|
|
477
|
+
content: `Removed marketplace "${mpArgs}" and uninstalled its plugins.`
|
|
478
|
+
});
|
|
479
|
+
return { handled: true };
|
|
480
|
+
} else if (mpSubcommand === "update" && mpArgs) {
|
|
481
|
+
await callbacks.marketplaceUpdate(mpArgs);
|
|
482
|
+
addMessage({
|
|
483
|
+
role: "system",
|
|
484
|
+
content: `Updated marketplace "${mpArgs}".`
|
|
485
|
+
});
|
|
486
|
+
return { handled: true };
|
|
487
|
+
} else if (mpSubcommand === "list") {
|
|
488
|
+
const sources = await callbacks.marketplaceList();
|
|
489
|
+
if (sources.length === 0) {
|
|
490
|
+
addMessage({ role: "system", content: "No marketplace sources configured." });
|
|
491
|
+
} else {
|
|
492
|
+
const lines = sources.map((s) => ` ${s.name} (${s.type})`);
|
|
493
|
+
addMessage({ role: "system", content: `Marketplace sources:
|
|
494
|
+
${lines.join("\n")}` });
|
|
495
|
+
}
|
|
496
|
+
return { handled: true };
|
|
497
|
+
} else {
|
|
498
|
+
addMessage({
|
|
499
|
+
role: "system",
|
|
500
|
+
content: "Usage: /plugin marketplace add <source> | remove <name> | update <name> | list"
|
|
501
|
+
});
|
|
502
|
+
return { handled: true };
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
default:
|
|
506
|
+
addMessage({ role: "system", content: `Unknown plugin subcommand: ${subcommand}` });
|
|
507
|
+
return { handled: true };
|
|
508
|
+
}
|
|
509
|
+
} catch (error) {
|
|
510
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
511
|
+
addMessage({ role: "system", content: `Plugin error: ${message}` });
|
|
512
|
+
return { handled: true };
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
async function handleReloadPlugins(addMessage, callbacks) {
|
|
516
|
+
await callbacks.reloadPlugins();
|
|
517
|
+
addMessage({ role: "system", content: "Plugins reload complete." });
|
|
518
|
+
return { handled: true };
|
|
519
|
+
}
|
|
520
|
+
async function executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry, pluginCallbacks) {
|
|
406
521
|
switch (cmd) {
|
|
407
522
|
case "help":
|
|
408
523
|
return handleHelp(addMessage);
|
|
@@ -426,10 +541,22 @@ async function executeSlashCommand(cmd, args, session, addMessage, clearMessages
|
|
|
426
541
|
return handleReset(addMessage);
|
|
427
542
|
case "exit":
|
|
428
543
|
return { handled: true, exitRequested: true };
|
|
544
|
+
case "plugin":
|
|
545
|
+
if (pluginCallbacks) {
|
|
546
|
+
return handlePluginCommand(args, addMessage, pluginCallbacks);
|
|
547
|
+
}
|
|
548
|
+
addMessage({ role: "system", content: "Plugin management is not available." });
|
|
549
|
+
return { handled: true };
|
|
550
|
+
case "reload-plugins":
|
|
551
|
+
if (pluginCallbacks) {
|
|
552
|
+
return handleReloadPlugins(addMessage, pluginCallbacks);
|
|
553
|
+
}
|
|
554
|
+
addMessage({ role: "system", content: "Plugin management is not available." });
|
|
555
|
+
return { handled: true };
|
|
429
556
|
default: {
|
|
430
|
-
const
|
|
431
|
-
if (
|
|
432
|
-
addMessage({ role: "system", content: `Invoking
|
|
557
|
+
const dynamicCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
558
|
+
if (dynamicCmd) {
|
|
559
|
+
addMessage({ role: "system", content: `Invoking ${dynamicCmd.source}: ${cmd}` });
|
|
433
560
|
return { handled: false };
|
|
434
561
|
}
|
|
435
562
|
addMessage({ role: "system", content: `Unknown command "/${cmd}". Type /help for help.` });
|
|
@@ -440,14 +567,22 @@ async function executeSlashCommand(cmd, args, session, addMessage, clearMessages
|
|
|
440
567
|
|
|
441
568
|
// src/ui/hooks/useSlashCommands.ts
|
|
442
569
|
var EXIT_DELAY_MS = 500;
|
|
443
|
-
function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId) {
|
|
570
|
+
function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId, pluginCallbacks) {
|
|
444
571
|
return (0, import_react3.useCallback)(
|
|
445
572
|
async (input) => {
|
|
446
573
|
const parts = input.slice(1).split(/\s+/);
|
|
447
574
|
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
448
575
|
const args = parts.slice(1).join(" ");
|
|
449
576
|
const clearMessages = () => setMessages([]);
|
|
450
|
-
const result = await executeSlashCommand(
|
|
577
|
+
const result = await executeSlashCommand(
|
|
578
|
+
cmd,
|
|
579
|
+
args,
|
|
580
|
+
session,
|
|
581
|
+
addMessage,
|
|
582
|
+
clearMessages,
|
|
583
|
+
registry,
|
|
584
|
+
pluginCallbacks
|
|
585
|
+
);
|
|
451
586
|
if (result.pendingModelId) {
|
|
452
587
|
pendingModelChangeRef.current = result.pendingModelId;
|
|
453
588
|
setPendingModelId(result.pendingModelId);
|
|
@@ -457,7 +592,16 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
|
|
|
457
592
|
}
|
|
458
593
|
return result.handled;
|
|
459
594
|
},
|
|
460
|
-
[
|
|
595
|
+
[
|
|
596
|
+
session,
|
|
597
|
+
addMessage,
|
|
598
|
+
setMessages,
|
|
599
|
+
exit,
|
|
600
|
+
registry,
|
|
601
|
+
pendingModelChangeRef,
|
|
602
|
+
setPendingModelId,
|
|
603
|
+
pluginCallbacks
|
|
604
|
+
]
|
|
461
605
|
);
|
|
462
606
|
}
|
|
463
607
|
|
|
@@ -492,16 +636,60 @@ function parseFirstArgValue(argsJson) {
|
|
|
492
636
|
}
|
|
493
637
|
|
|
494
638
|
// src/utils/skill-prompt.ts
|
|
495
|
-
|
|
639
|
+
var import_node_child_process = require("child_process");
|
|
640
|
+
function substituteVariables(content, args, context) {
|
|
641
|
+
const argParts = args ? args.split(/\s+/) : [];
|
|
642
|
+
let result = content;
|
|
643
|
+
result = result.replace(/\$ARGUMENTS\[(\d+)]/g, (_match, index) => {
|
|
644
|
+
return argParts[Number(index)] ?? "";
|
|
645
|
+
});
|
|
646
|
+
result = result.replace(/\$ARGUMENTS/g, args);
|
|
647
|
+
result = result.replace(/\$(\d)(?!\d|\w|\[)/g, (_match, digit) => {
|
|
648
|
+
return argParts[Number(digit)] ?? "";
|
|
649
|
+
});
|
|
650
|
+
result = result.replace(/\$\{CLAUDE_SESSION_ID}/g, context?.sessionId ?? "");
|
|
651
|
+
result = result.replace(/\$\{CLAUDE_SKILL_DIR}/g, context?.skillDir ?? "");
|
|
652
|
+
return result;
|
|
653
|
+
}
|
|
654
|
+
async function preprocessShellCommands(content) {
|
|
655
|
+
const shellPattern = /!`([^`]+)`/g;
|
|
656
|
+
if (!shellPattern.test(content)) {
|
|
657
|
+
return content;
|
|
658
|
+
}
|
|
659
|
+
shellPattern.lastIndex = 0;
|
|
660
|
+
let result = content;
|
|
661
|
+
let match;
|
|
662
|
+
const matches = [];
|
|
663
|
+
while ((match = shellPattern.exec(content)) !== null) {
|
|
664
|
+
matches.push({ full: match[0], command: match[1] });
|
|
665
|
+
}
|
|
666
|
+
for (const { full, command } of matches) {
|
|
667
|
+
let output = "";
|
|
668
|
+
try {
|
|
669
|
+
output = (0, import_node_child_process.execSync)(command, {
|
|
670
|
+
timeout: 5e3,
|
|
671
|
+
encoding: "utf-8",
|
|
672
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
673
|
+
}).trimEnd();
|
|
674
|
+
} catch {
|
|
675
|
+
output = "";
|
|
676
|
+
}
|
|
677
|
+
result = result.replace(full, output);
|
|
678
|
+
}
|
|
679
|
+
return result;
|
|
680
|
+
}
|
|
681
|
+
async function buildSkillPrompt(input, registry, context) {
|
|
496
682
|
const parts = input.slice(1).split(/\s+/);
|
|
497
683
|
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
498
|
-
const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
|
|
684
|
+
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
499
685
|
if (!skillCmd) return null;
|
|
500
686
|
const args = parts.slice(1).join(" ").trim();
|
|
501
687
|
const userInstruction = args || skillCmd.description;
|
|
502
688
|
if (skillCmd.skillContent) {
|
|
689
|
+
let processed = await preprocessShellCommands(skillCmd.skillContent);
|
|
690
|
+
processed = substituteVariables(processed, args, context);
|
|
503
691
|
return `<skill name="${cmd}">
|
|
504
|
-
${
|
|
692
|
+
${processed}
|
|
505
693
|
</skill>
|
|
506
694
|
|
|
507
695
|
Execute the "${cmd}" skill: ${userInstruction}`;
|
|
@@ -514,12 +702,12 @@ function syncContextState(session, setter) {
|
|
|
514
702
|
const ctx = session.getContextState();
|
|
515
703
|
setter({ percentage: ctx.usedPercentage, usedTokens: ctx.usedTokens, maxTokens: ctx.maxTokens });
|
|
516
704
|
}
|
|
517
|
-
async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextState) {
|
|
705
|
+
async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextState, rawInput) {
|
|
518
706
|
setIsThinking(true);
|
|
519
707
|
clearStreamingText();
|
|
520
708
|
const historyBefore = session.getHistory().length;
|
|
521
709
|
try {
|
|
522
|
-
const response = await session.run(prompt);
|
|
710
|
+
const response = await session.run(prompt, rawInput);
|
|
523
711
|
clearStreamingText();
|
|
524
712
|
const history = session.getHistory();
|
|
525
713
|
const toolLines = extractToolCalls(
|
|
@@ -527,7 +715,11 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
|
|
|
527
715
|
historyBefore
|
|
528
716
|
);
|
|
529
717
|
if (toolLines.length > 0) {
|
|
530
|
-
addMessage({
|
|
718
|
+
addMessage({
|
|
719
|
+
role: "tool",
|
|
720
|
+
content: toolLines.join("\n"),
|
|
721
|
+
toolName: `${toolLines.length} tools`
|
|
722
|
+
});
|
|
531
723
|
}
|
|
532
724
|
addMessage({ role: "assistant", content: response || "(empty response)" });
|
|
533
725
|
syncContextState(session, setContextState);
|
|
@@ -552,19 +744,45 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
|
|
|
552
744
|
syncContextState(session, setContextState);
|
|
553
745
|
return;
|
|
554
746
|
}
|
|
555
|
-
const prompt = buildSkillPrompt(input, registry);
|
|
747
|
+
const prompt = await buildSkillPrompt(input, registry);
|
|
556
748
|
if (!prompt) return;
|
|
557
|
-
return runSessionPrompt(
|
|
749
|
+
return runSessionPrompt(
|
|
750
|
+
prompt,
|
|
751
|
+
session,
|
|
752
|
+
addMessage,
|
|
753
|
+
clearStreamingText,
|
|
754
|
+
setIsThinking,
|
|
755
|
+
setContextState,
|
|
756
|
+
input
|
|
757
|
+
);
|
|
558
758
|
}
|
|
559
759
|
addMessage({ role: "user", content: input });
|
|
560
|
-
return runSessionPrompt(
|
|
760
|
+
return runSessionPrompt(
|
|
761
|
+
input,
|
|
762
|
+
session,
|
|
763
|
+
addMessage,
|
|
764
|
+
clearStreamingText,
|
|
765
|
+
setIsThinking,
|
|
766
|
+
setContextState
|
|
767
|
+
);
|
|
561
768
|
},
|
|
562
|
-
[
|
|
769
|
+
[
|
|
770
|
+
session,
|
|
771
|
+
addMessage,
|
|
772
|
+
handleSlashCommand,
|
|
773
|
+
clearStreamingText,
|
|
774
|
+
setIsThinking,
|
|
775
|
+
setContextState,
|
|
776
|
+
registry
|
|
777
|
+
]
|
|
563
778
|
);
|
|
564
779
|
}
|
|
565
780
|
|
|
566
781
|
// src/ui/hooks/useCommandRegistry.ts
|
|
567
782
|
var import_react5 = require("react");
|
|
783
|
+
var import_node_os2 = require("os");
|
|
784
|
+
var import_node_path3 = require("path");
|
|
785
|
+
var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
|
|
568
786
|
|
|
569
787
|
// src/commands/command-registry.ts
|
|
570
788
|
var CommandRegistry = class {
|
|
@@ -648,6 +866,23 @@ function createBuiltinCommands() {
|
|
|
648
866
|
{ name: "cost", description: "Show session info", source: "builtin" },
|
|
649
867
|
{ name: "context", description: "Context window info", source: "builtin" },
|
|
650
868
|
{ name: "permissions", description: "Permission rules", source: "builtin" },
|
|
869
|
+
{
|
|
870
|
+
name: "plugin",
|
|
871
|
+
description: "Manage plugins",
|
|
872
|
+
source: "builtin",
|
|
873
|
+
subcommands: [
|
|
874
|
+
{ name: "install", description: "Install a plugin (name@marketplace)", source: "builtin" },
|
|
875
|
+
{
|
|
876
|
+
name: "uninstall",
|
|
877
|
+
description: "Uninstall a plugin (name@marketplace)",
|
|
878
|
+
source: "builtin"
|
|
879
|
+
},
|
|
880
|
+
{ name: "enable", description: "Enable a plugin (name@marketplace)", source: "builtin" },
|
|
881
|
+
{ name: "disable", description: "Disable a plugin (name@marketplace)", source: "builtin" },
|
|
882
|
+
{ name: "marketplace", description: "Manage marketplace sources", source: "builtin" }
|
|
883
|
+
]
|
|
884
|
+
},
|
|
885
|
+
{ name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
|
|
651
886
|
{ name: "reset", description: "Delete settings and exit", source: "builtin" },
|
|
652
887
|
{ name: "exit", description: "Exit CLI", source: "builtin" }
|
|
653
888
|
];
|
|
@@ -667,25 +902,50 @@ var BuiltinCommandSource = class {
|
|
|
667
902
|
var import_node_fs2 = require("fs");
|
|
668
903
|
var import_node_path2 = require("path");
|
|
669
904
|
var import_node_os = require("os");
|
|
905
|
+
var BOOLEAN_KEYS = /* @__PURE__ */ new Set(["disable-model-invocation", "user-invocable"]);
|
|
906
|
+
var LIST_KEYS = /* @__PURE__ */ new Set(["allowed-tools"]);
|
|
907
|
+
function kebabToCamel(key) {
|
|
908
|
+
return key.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
|
|
909
|
+
}
|
|
670
910
|
function parseFrontmatter(content) {
|
|
671
911
|
const lines = content.split("\n");
|
|
672
912
|
if (lines[0]?.trim() !== "---") return null;
|
|
673
|
-
|
|
674
|
-
let description = "";
|
|
913
|
+
const result = {};
|
|
675
914
|
for (let i = 1; i < lines.length; i++) {
|
|
676
915
|
const line = lines[i];
|
|
677
916
|
if (line.trim() === "---") break;
|
|
678
|
-
const
|
|
679
|
-
if (
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
917
|
+
const match = line.match(/^([a-z][a-z0-9-]*):\s*(.+)/);
|
|
918
|
+
if (!match) continue;
|
|
919
|
+
const key = match[1];
|
|
920
|
+
const rawValue = match[2].trim();
|
|
921
|
+
const camelKey = kebabToCamel(key);
|
|
922
|
+
if (BOOLEAN_KEYS.has(key)) {
|
|
923
|
+
result[camelKey] = rawValue === "true";
|
|
924
|
+
} else if (LIST_KEYS.has(key)) {
|
|
925
|
+
result[camelKey] = rawValue.split(",").map((s) => s.trim());
|
|
926
|
+
} else {
|
|
927
|
+
result[camelKey] = rawValue;
|
|
686
928
|
}
|
|
687
929
|
}
|
|
688
|
-
return
|
|
930
|
+
return Object.keys(result).length > 0 ? result : null;
|
|
931
|
+
}
|
|
932
|
+
function buildCommand(frontmatter, content, fallbackName) {
|
|
933
|
+
const cmd = {
|
|
934
|
+
name: frontmatter?.name ?? fallbackName,
|
|
935
|
+
description: frontmatter?.description ?? `Skill: ${fallbackName}`,
|
|
936
|
+
source: "skill",
|
|
937
|
+
skillContent: content
|
|
938
|
+
};
|
|
939
|
+
if (frontmatter?.argumentHint !== void 0) cmd.argumentHint = frontmatter.argumentHint;
|
|
940
|
+
if (frontmatter?.disableModelInvocation !== void 0)
|
|
941
|
+
cmd.disableModelInvocation = frontmatter.disableModelInvocation;
|
|
942
|
+
if (frontmatter?.userInvocable !== void 0) cmd.userInvocable = frontmatter.userInvocable;
|
|
943
|
+
if (frontmatter?.allowedTools !== void 0) cmd.allowedTools = frontmatter.allowedTools;
|
|
944
|
+
if (frontmatter?.model !== void 0) cmd.model = frontmatter.model;
|
|
945
|
+
if (frontmatter?.effort !== void 0) cmd.effort = frontmatter.effort;
|
|
946
|
+
if (frontmatter?.context !== void 0) cmd.context = frontmatter.context;
|
|
947
|
+
if (frontmatter?.agent !== void 0) cmd.agent = frontmatter.agent;
|
|
948
|
+
return cmd;
|
|
689
949
|
}
|
|
690
950
|
function scanSkillsDir(skillsDir) {
|
|
691
951
|
if (!(0, import_node_fs2.existsSync)(skillsDir)) return [];
|
|
@@ -697,48 +957,246 @@ function scanSkillsDir(skillsDir) {
|
|
|
697
957
|
if (!(0, import_node_fs2.existsSync)(skillFile)) continue;
|
|
698
958
|
const content = (0, import_node_fs2.readFileSync)(skillFile, "utf-8");
|
|
699
959
|
const frontmatter = parseFrontmatter(content);
|
|
700
|
-
commands.push(
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
960
|
+
commands.push(buildCommand(frontmatter, content, entry.name));
|
|
961
|
+
}
|
|
962
|
+
return commands;
|
|
963
|
+
}
|
|
964
|
+
function scanCommandsDir(commandsDir) {
|
|
965
|
+
if (!(0, import_node_fs2.existsSync)(commandsDir)) return [];
|
|
966
|
+
const commands = [];
|
|
967
|
+
const entries = (0, import_node_fs2.readdirSync)(commandsDir, { withFileTypes: true });
|
|
968
|
+
for (const entry of entries) {
|
|
969
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
970
|
+
const filePath = (0, import_node_path2.join)(commandsDir, entry.name);
|
|
971
|
+
const content = (0, import_node_fs2.readFileSync)(filePath, "utf-8");
|
|
972
|
+
const frontmatter = parseFrontmatter(content);
|
|
973
|
+
const fallbackName = (0, import_node_path2.basename)(entry.name, ".md");
|
|
974
|
+
commands.push(buildCommand(frontmatter, content, fallbackName));
|
|
706
975
|
}
|
|
707
976
|
return commands;
|
|
708
977
|
}
|
|
709
978
|
var SkillCommandSource = class {
|
|
710
979
|
name = "skill";
|
|
711
980
|
cwd;
|
|
981
|
+
home;
|
|
712
982
|
cachedCommands = null;
|
|
713
|
-
constructor(cwd) {
|
|
983
|
+
constructor(cwd, home) {
|
|
714
984
|
this.cwd = cwd;
|
|
985
|
+
this.home = home ?? (0, import_node_os.homedir)();
|
|
715
986
|
}
|
|
716
987
|
getCommands() {
|
|
717
988
|
if (this.cachedCommands) return this.cachedCommands;
|
|
718
|
-
const
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
989
|
+
const sources = [
|
|
990
|
+
scanSkillsDir((0, import_node_path2.join)(this.cwd, ".claude", "skills")),
|
|
991
|
+
// 1. project .claude/skills
|
|
992
|
+
scanCommandsDir((0, import_node_path2.join)(this.cwd, ".claude", "commands")),
|
|
993
|
+
// 2. project .claude/commands (legacy)
|
|
994
|
+
scanSkillsDir((0, import_node_path2.join)(this.home, ".robota", "skills")),
|
|
995
|
+
// 3. user ~/.robota/skills
|
|
996
|
+
scanSkillsDir((0, import_node_path2.join)(this.cwd, ".agents", "skills"))
|
|
997
|
+
// 4. project .agents/skills
|
|
998
|
+
];
|
|
999
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1000
|
+
const merged = [];
|
|
1001
|
+
for (const commands of sources) {
|
|
1002
|
+
for (const cmd of commands) {
|
|
1003
|
+
if (!seen.has(cmd.name)) {
|
|
1004
|
+
seen.add(cmd.name);
|
|
1005
|
+
merged.push(cmd);
|
|
1006
|
+
}
|
|
725
1007
|
}
|
|
726
1008
|
}
|
|
727
1009
|
this.cachedCommands = merged;
|
|
728
1010
|
return this.cachedCommands;
|
|
729
1011
|
}
|
|
1012
|
+
/** Get skills that models can invoke (excludes disableModelInvocation: true) */
|
|
1013
|
+
getModelInvocableSkills() {
|
|
1014
|
+
return this.getCommands().filter((cmd) => cmd.disableModelInvocation !== true);
|
|
1015
|
+
}
|
|
1016
|
+
/** Get skills that users can invoke (excludes userInvocable: false) */
|
|
1017
|
+
getUserInvocableSkills() {
|
|
1018
|
+
return this.getCommands().filter((cmd) => cmd.userInvocable !== false);
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
|
|
1022
|
+
// src/commands/plugin-source.ts
|
|
1023
|
+
var PluginCommandSource = class {
|
|
1024
|
+
name = "plugin";
|
|
1025
|
+
plugins;
|
|
1026
|
+
constructor(plugins) {
|
|
1027
|
+
this.plugins = plugins;
|
|
1028
|
+
}
|
|
1029
|
+
getCommands() {
|
|
1030
|
+
const commands = [];
|
|
1031
|
+
for (const plugin of this.plugins) {
|
|
1032
|
+
for (const skill of plugin.skills) {
|
|
1033
|
+
const baseName = skill.name.includes("@") ? skill.name.split("@")[0] : skill.name;
|
|
1034
|
+
commands.push({
|
|
1035
|
+
name: baseName,
|
|
1036
|
+
description: `${skill.description} (${plugin.manifest.name})`,
|
|
1037
|
+
source: "plugin",
|
|
1038
|
+
skillContent: skill.skillContent,
|
|
1039
|
+
pluginDir: plugin.pluginDir
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
for (const cmd of plugin.commands) {
|
|
1043
|
+
commands.push({
|
|
1044
|
+
name: cmd.name,
|
|
1045
|
+
description: cmd.description,
|
|
1046
|
+
source: "plugin",
|
|
1047
|
+
skillContent: cmd.skillContent,
|
|
1048
|
+
pluginDir: plugin.pluginDir
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return commands;
|
|
1053
|
+
}
|
|
730
1054
|
};
|
|
731
1055
|
|
|
732
1056
|
// src/ui/hooks/useCommandRegistry.ts
|
|
1057
|
+
function buildPluginEnv(plugin) {
|
|
1058
|
+
const dataDir = (0, import_node_path3.join)((0, import_node_path3.dirname)((0, import_node_path3.dirname)(plugin.pluginDir)), "data", plugin.manifest.name);
|
|
1059
|
+
return {
|
|
1060
|
+
CLAUDE_PLUGIN_ROOT: plugin.pluginDir,
|
|
1061
|
+
CLAUDE_PLUGIN_PATH: plugin.pluginDir,
|
|
1062
|
+
CLAUDE_PLUGIN_DATA: dataDir
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
function mergePluginHooks(plugins) {
|
|
1066
|
+
const merged = {};
|
|
1067
|
+
for (const plugin of plugins) {
|
|
1068
|
+
const hooksObj = plugin.hooks;
|
|
1069
|
+
if (!hooksObj) continue;
|
|
1070
|
+
const pluginEnv = buildPluginEnv(plugin);
|
|
1071
|
+
const innerHooks = hooksObj.hooks ?? hooksObj;
|
|
1072
|
+
for (const [event, groups] of Object.entries(innerHooks)) {
|
|
1073
|
+
if (!Array.isArray(groups)) continue;
|
|
1074
|
+
if (!merged[event]) merged[event] = [];
|
|
1075
|
+
const resolved = groups.map((group) => {
|
|
1076
|
+
const resolved2 = resolvePluginRoot(group, plugin.pluginDir);
|
|
1077
|
+
if (typeof resolved2 === "object" && resolved2 !== null) {
|
|
1078
|
+
resolved2.env = pluginEnv;
|
|
1079
|
+
}
|
|
1080
|
+
return resolved2;
|
|
1081
|
+
});
|
|
1082
|
+
merged[event].push(...resolved);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
return merged;
|
|
1086
|
+
}
|
|
1087
|
+
function resolvePluginRoot(group, pluginDir) {
|
|
1088
|
+
if (typeof group !== "object" || group === null) return group;
|
|
1089
|
+
const obj = group;
|
|
1090
|
+
if (Array.isArray(obj.hooks)) {
|
|
1091
|
+
return {
|
|
1092
|
+
...obj,
|
|
1093
|
+
hooks: obj.hooks.map((h) => {
|
|
1094
|
+
if (typeof h !== "object" || h === null) return h;
|
|
1095
|
+
const hook = h;
|
|
1096
|
+
if (typeof hook.command === "string") {
|
|
1097
|
+
return {
|
|
1098
|
+
...hook,
|
|
1099
|
+
command: hook.command.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginDir)
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
return hook;
|
|
1103
|
+
})
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
return group;
|
|
1107
|
+
}
|
|
733
1108
|
function useCommandRegistry(cwd) {
|
|
734
|
-
const
|
|
735
|
-
if (
|
|
1109
|
+
const resultRef = (0, import_react5.useRef)(null);
|
|
1110
|
+
if (resultRef.current === null) {
|
|
736
1111
|
const registry = new CommandRegistry();
|
|
737
1112
|
registry.addSource(new BuiltinCommandSource());
|
|
738
1113
|
registry.addSource(new SkillCommandSource(cwd));
|
|
739
|
-
|
|
1114
|
+
let pluginHooks = {};
|
|
1115
|
+
const pluginsDir = (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".robota", "plugins");
|
|
1116
|
+
const loader = new import_agent_sdk2.BundlePluginLoader(pluginsDir);
|
|
1117
|
+
try {
|
|
1118
|
+
const plugins = loader.loadPluginsSync();
|
|
1119
|
+
if (plugins.length > 0) {
|
|
1120
|
+
registry.addSource(new PluginCommandSource(plugins));
|
|
1121
|
+
pluginHooks = mergePluginHooks(plugins);
|
|
1122
|
+
}
|
|
1123
|
+
} catch {
|
|
1124
|
+
}
|
|
1125
|
+
resultRef.current = { registry, pluginHooks };
|
|
740
1126
|
}
|
|
741
|
-
return
|
|
1127
|
+
return resultRef.current;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
// src/ui/hooks/usePluginCallbacks.ts
|
|
1131
|
+
var import_react6 = require("react");
|
|
1132
|
+
var import_node_os3 = require("os");
|
|
1133
|
+
var import_node_path4 = require("path");
|
|
1134
|
+
var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
|
|
1135
|
+
function usePluginCallbacks(cwd) {
|
|
1136
|
+
return (0, import_react6.useMemo)(() => {
|
|
1137
|
+
const home = (0, import_node_os3.homedir)();
|
|
1138
|
+
const pluginsDir = (0, import_node_path4.join)(home, ".robota", "plugins");
|
|
1139
|
+
const userSettingsPath = (0, import_node_path4.join)(home, ".robota", "settings.json");
|
|
1140
|
+
const settingsStore = new import_agent_sdk3.PluginSettingsStore(userSettingsPath);
|
|
1141
|
+
const marketplace = new import_agent_sdk3.MarketplaceClient({ pluginsDir });
|
|
1142
|
+
const installer = new import_agent_sdk3.BundlePluginInstaller({
|
|
1143
|
+
pluginsDir,
|
|
1144
|
+
settingsStore,
|
|
1145
|
+
marketplaceClient: marketplace
|
|
1146
|
+
});
|
|
1147
|
+
const loader = new import_agent_sdk3.BundlePluginLoader(pluginsDir);
|
|
1148
|
+
return {
|
|
1149
|
+
listInstalled: async () => {
|
|
1150
|
+
const plugins = await loader.loadAll();
|
|
1151
|
+
return plugins.map((p) => ({
|
|
1152
|
+
name: p.manifest.name,
|
|
1153
|
+
description: p.manifest.description,
|
|
1154
|
+
enabled: true
|
|
1155
|
+
}));
|
|
1156
|
+
},
|
|
1157
|
+
install: async (pluginId) => {
|
|
1158
|
+
const [name, marketplaceName] = pluginId.split("@");
|
|
1159
|
+
if (!name || !marketplaceName) {
|
|
1160
|
+
throw new Error("Plugin ID must be in format: name@marketplace");
|
|
1161
|
+
}
|
|
1162
|
+
await installer.install(name, marketplaceName);
|
|
1163
|
+
},
|
|
1164
|
+
uninstall: async (pluginId) => {
|
|
1165
|
+
await installer.uninstall(pluginId);
|
|
1166
|
+
},
|
|
1167
|
+
enable: async (pluginId) => {
|
|
1168
|
+
await installer.enable(pluginId);
|
|
1169
|
+
},
|
|
1170
|
+
disable: async (pluginId) => {
|
|
1171
|
+
await installer.disable(pluginId);
|
|
1172
|
+
},
|
|
1173
|
+
marketplaceAdd: async (source) => {
|
|
1174
|
+
if (source.includes("/") && !source.includes(":")) {
|
|
1175
|
+
return marketplace.addMarketplace({ type: "github", repo: source });
|
|
1176
|
+
} else {
|
|
1177
|
+
return marketplace.addMarketplace({ type: "git", url: source });
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
marketplaceRemove: async (name) => {
|
|
1181
|
+
const installedFromMarketplace = installer.getPluginsByMarketplace(name);
|
|
1182
|
+
for (const record of installedFromMarketplace) {
|
|
1183
|
+
await installer.uninstall(`${record.pluginName}@${record.marketplace}`);
|
|
1184
|
+
}
|
|
1185
|
+
marketplace.removeMarketplace(name);
|
|
1186
|
+
},
|
|
1187
|
+
marketplaceUpdate: async (name) => {
|
|
1188
|
+
marketplace.updateMarketplace(name);
|
|
1189
|
+
},
|
|
1190
|
+
marketplaceList: async () => {
|
|
1191
|
+
return marketplace.listMarketplaces().map((m) => ({
|
|
1192
|
+
name: m.name,
|
|
1193
|
+
type: m.source.type
|
|
1194
|
+
}));
|
|
1195
|
+
},
|
|
1196
|
+
reloadPlugins: async () => {
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
}, [cwd]);
|
|
742
1200
|
}
|
|
743
1201
|
|
|
744
1202
|
// src/ui/MessageList.tsx
|
|
@@ -861,11 +1319,11 @@ function StatusBar({
|
|
|
861
1319
|
}
|
|
862
1320
|
|
|
863
1321
|
// src/ui/InputArea.tsx
|
|
864
|
-
var
|
|
1322
|
+
var import_react9 = __toESM(require("react"), 1);
|
|
865
1323
|
var import_ink6 = require("ink");
|
|
866
1324
|
|
|
867
1325
|
// src/ui/CjkTextInput.tsx
|
|
868
|
-
var
|
|
1326
|
+
var import_react7 = require("react");
|
|
869
1327
|
var import_ink3 = require("ink");
|
|
870
1328
|
var import_chalk = __toESM(require("chalk"), 1);
|
|
871
1329
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
@@ -885,9 +1343,9 @@ function CjkTextInput({
|
|
|
885
1343
|
focus = true,
|
|
886
1344
|
showCursor = true
|
|
887
1345
|
}) {
|
|
888
|
-
const valueRef = (0,
|
|
889
|
-
const cursorRef = (0,
|
|
890
|
-
const [, forceRender] = (0,
|
|
1346
|
+
const valueRef = (0, import_react7.useRef)(value);
|
|
1347
|
+
const cursorRef = (0, import_react7.useRef)(value.length);
|
|
1348
|
+
const [, forceRender] = (0, import_react7.useState)(0);
|
|
891
1349
|
if (value !== valueRef.current) {
|
|
892
1350
|
valueRef.current = value;
|
|
893
1351
|
if (cursorRef.current > value.length) {
|
|
@@ -964,15 +1422,15 @@ function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
|
|
|
964
1422
|
}
|
|
965
1423
|
|
|
966
1424
|
// src/ui/WaveText.tsx
|
|
967
|
-
var
|
|
1425
|
+
var import_react8 = require("react");
|
|
968
1426
|
var import_ink4 = require("ink");
|
|
969
1427
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
970
1428
|
var WAVE_COLORS = ["#666666", "#888888", "#aaaaaa", "#888888"];
|
|
971
1429
|
var INTERVAL_MS = 400;
|
|
972
1430
|
var CHARS_PER_GROUP = 4;
|
|
973
1431
|
function WaveText({ text }) {
|
|
974
|
-
const [tick, setTick] = (0,
|
|
975
|
-
(0,
|
|
1432
|
+
const [tick, setTick] = (0, import_react8.useState)(0);
|
|
1433
|
+
(0, import_react8.useEffect)(() => {
|
|
976
1434
|
const timer = setInterval(() => {
|
|
977
1435
|
setTick((prev) => prev + 1);
|
|
978
1436
|
}, INTERVAL_MS);
|
|
@@ -1038,16 +1496,16 @@ function parseSlashInput(value) {
|
|
|
1038
1496
|
return { isSlash: true, parentCommand: parent, filter: rest };
|
|
1039
1497
|
}
|
|
1040
1498
|
function useAutocomplete(value, registry) {
|
|
1041
|
-
const [selectedIndex, setSelectedIndex] = (0,
|
|
1042
|
-
const [dismissed, setDismissed] = (0,
|
|
1043
|
-
const prevValueRef =
|
|
1499
|
+
const [selectedIndex, setSelectedIndex] = (0, import_react9.useState)(0);
|
|
1500
|
+
const [dismissed, setDismissed] = (0, import_react9.useState)(false);
|
|
1501
|
+
const prevValueRef = import_react9.default.useRef(value);
|
|
1044
1502
|
if (prevValueRef.current !== value) {
|
|
1045
1503
|
prevValueRef.current = value;
|
|
1046
1504
|
if (dismissed) setDismissed(false);
|
|
1047
1505
|
}
|
|
1048
1506
|
const parsed = parseSlashInput(value);
|
|
1049
1507
|
const isSubcommandMode = parsed.isSlash && parsed.parentCommand.length > 0;
|
|
1050
|
-
const filteredCommands = (0,
|
|
1508
|
+
const filteredCommands = (0, import_react9.useMemo)(() => {
|
|
1051
1509
|
if (!registry || !parsed.isSlash || dismissed) return [];
|
|
1052
1510
|
if (isSubcommandMode) {
|
|
1053
1511
|
const subs = registry.getSubcommands(parsed.parentCommand);
|
|
@@ -1081,7 +1539,7 @@ function useAutocomplete(value, registry) {
|
|
|
1081
1539
|
};
|
|
1082
1540
|
}
|
|
1083
1541
|
function InputArea({ onSubmit, isDisabled, registry }) {
|
|
1084
|
-
const [value, setValue] = (0,
|
|
1542
|
+
const [value, setValue] = (0, import_react9.useState)("");
|
|
1085
1543
|
const {
|
|
1086
1544
|
showPopup,
|
|
1087
1545
|
filteredCommands,
|
|
@@ -1090,7 +1548,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
|
|
|
1090
1548
|
isSubcommandMode,
|
|
1091
1549
|
setShowPopup
|
|
1092
1550
|
} = useAutocomplete(value, registry);
|
|
1093
|
-
const handleSubmit = (0,
|
|
1551
|
+
const handleSubmit = (0, import_react9.useCallback)(
|
|
1094
1552
|
(text) => {
|
|
1095
1553
|
const trimmed = text.trim();
|
|
1096
1554
|
if (trimmed.length === 0) return;
|
|
@@ -1103,7 +1561,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
|
|
|
1103
1561
|
},
|
|
1104
1562
|
[showPopup, filteredCommands, selectedIndex, onSubmit]
|
|
1105
1563
|
);
|
|
1106
|
-
const selectCommand = (0,
|
|
1564
|
+
const selectCommand = (0, import_react9.useCallback)(
|
|
1107
1565
|
(cmd) => {
|
|
1108
1566
|
const parsed = parseSlashInput(value);
|
|
1109
1567
|
if (parsed.parentCommand) {
|
|
@@ -1164,7 +1622,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
|
|
|
1164
1622
|
}
|
|
1165
1623
|
|
|
1166
1624
|
// src/ui/ConfirmPrompt.tsx
|
|
1167
|
-
var
|
|
1625
|
+
var import_react10 = require("react");
|
|
1168
1626
|
var import_ink7 = require("ink");
|
|
1169
1627
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1170
1628
|
function ConfirmPrompt({
|
|
@@ -1172,9 +1630,9 @@ function ConfirmPrompt({
|
|
|
1172
1630
|
options = ["Yes", "No"],
|
|
1173
1631
|
onSelect
|
|
1174
1632
|
}) {
|
|
1175
|
-
const [selected, setSelected] = (0,
|
|
1176
|
-
const resolvedRef = (0,
|
|
1177
|
-
const doSelect = (0,
|
|
1633
|
+
const [selected, setSelected] = (0, import_react10.useState)(0);
|
|
1634
|
+
const resolvedRef = (0, import_react10.useRef)(false);
|
|
1635
|
+
const doSelect = (0, import_react10.useCallback)(
|
|
1178
1636
|
(index) => {
|
|
1179
1637
|
if (resolvedRef.current) return;
|
|
1180
1638
|
resolvedRef.current = true;
|
|
@@ -1207,7 +1665,7 @@ function ConfirmPrompt({
|
|
|
1207
1665
|
}
|
|
1208
1666
|
|
|
1209
1667
|
// src/ui/PermissionPrompt.tsx
|
|
1210
|
-
var
|
|
1668
|
+
var import_react11 = __toESM(require("react"), 1);
|
|
1211
1669
|
var import_ink8 = require("ink");
|
|
1212
1670
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1213
1671
|
var OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
|
|
@@ -1217,15 +1675,15 @@ function formatArgs(args) {
|
|
|
1217
1675
|
return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
1218
1676
|
}
|
|
1219
1677
|
function PermissionPrompt({ request }) {
|
|
1220
|
-
const [selected, setSelected] =
|
|
1221
|
-
const resolvedRef =
|
|
1222
|
-
const prevRequestRef =
|
|
1678
|
+
const [selected, setSelected] = import_react11.default.useState(0);
|
|
1679
|
+
const resolvedRef = import_react11.default.useRef(false);
|
|
1680
|
+
const prevRequestRef = import_react11.default.useRef(request);
|
|
1223
1681
|
if (prevRequestRef.current !== request) {
|
|
1224
1682
|
prevRequestRef.current = request;
|
|
1225
1683
|
resolvedRef.current = false;
|
|
1226
1684
|
setSelected(0);
|
|
1227
1685
|
}
|
|
1228
|
-
const doResolve =
|
|
1686
|
+
const doResolve = import_react11.default.useCallback(
|
|
1229
1687
|
(index) => {
|
|
1230
1688
|
if (resolvedRef.current) return;
|
|
1231
1689
|
resolvedRef.current = true;
|
|
@@ -1304,21 +1762,56 @@ function StreamingIndicator({ text, activeTools }) {
|
|
|
1304
1762
|
// src/ui/App.tsx
|
|
1305
1763
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1306
1764
|
var EXIT_DELAY_MS2 = 500;
|
|
1765
|
+
function mergeHooksIntoConfig(configHooks, pluginHooks) {
|
|
1766
|
+
const pluginKeys = Object.keys(pluginHooks);
|
|
1767
|
+
if (pluginKeys.length === 0) return configHooks;
|
|
1768
|
+
const merged = {};
|
|
1769
|
+
for (const [event, groups] of Object.entries(pluginHooks)) {
|
|
1770
|
+
merged[event] = [...groups];
|
|
1771
|
+
}
|
|
1772
|
+
if (configHooks) {
|
|
1773
|
+
for (const [event, groups] of Object.entries(configHooks)) {
|
|
1774
|
+
if (!Array.isArray(groups)) continue;
|
|
1775
|
+
if (!merged[event]) merged[event] = [];
|
|
1776
|
+
merged[event].push(...groups);
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
return merged;
|
|
1780
|
+
}
|
|
1307
1781
|
function App(props) {
|
|
1308
1782
|
const { exit } = (0, import_ink10.useApp)();
|
|
1309
|
-
const {
|
|
1783
|
+
const { registry, pluginHooks } = useCommandRegistry(props.cwd ?? process.cwd());
|
|
1784
|
+
const configWithPluginHooks = {
|
|
1785
|
+
...props.config,
|
|
1786
|
+
hooks: mergeHooksIntoConfig(
|
|
1787
|
+
props.config.hooks,
|
|
1788
|
+
pluginHooks
|
|
1789
|
+
)
|
|
1790
|
+
};
|
|
1791
|
+
const { session, permissionRequest, streamingText, clearStreamingText, activeTools } = useSession(
|
|
1792
|
+
{ ...props, config: configWithPluginHooks }
|
|
1793
|
+
);
|
|
1310
1794
|
const { messages, setMessages, addMessage } = useMessages();
|
|
1311
|
-
const [isThinking, setIsThinking] = (0,
|
|
1795
|
+
const [isThinking, setIsThinking] = (0, import_react12.useState)(false);
|
|
1312
1796
|
const initialCtx = session.getContextState();
|
|
1313
|
-
const [contextState, setContextState] = (0,
|
|
1797
|
+
const [contextState, setContextState] = (0, import_react12.useState)({
|
|
1314
1798
|
percentage: initialCtx.usedPercentage,
|
|
1315
1799
|
usedTokens: initialCtx.usedTokens,
|
|
1316
1800
|
maxTokens: initialCtx.maxTokens
|
|
1317
1801
|
});
|
|
1318
|
-
const
|
|
1319
|
-
const
|
|
1320
|
-
const
|
|
1321
|
-
const handleSlashCommand = useSlashCommands(
|
|
1802
|
+
const pendingModelChangeRef = (0, import_react12.useRef)(null);
|
|
1803
|
+
const [pendingModelId, setPendingModelId] = (0, import_react12.useState)(null);
|
|
1804
|
+
const pluginCallbacks = usePluginCallbacks(props.cwd ?? process.cwd());
|
|
1805
|
+
const handleSlashCommand = useSlashCommands(
|
|
1806
|
+
session,
|
|
1807
|
+
addMessage,
|
|
1808
|
+
setMessages,
|
|
1809
|
+
exit,
|
|
1810
|
+
registry,
|
|
1811
|
+
pendingModelChangeRef,
|
|
1812
|
+
setPendingModelId,
|
|
1813
|
+
pluginCallbacks
|
|
1814
|
+
);
|
|
1322
1815
|
const handleSubmit = useSubmitHandler(
|
|
1323
1816
|
session,
|
|
1324
1817
|
addMessage,
|
|
@@ -1365,10 +1858,16 @@ function App(props) {
|
|
|
1365
1858
|
try {
|
|
1366
1859
|
const settingsPath = getUserSettingsPath();
|
|
1367
1860
|
updateModelInSettings(settingsPath, pendingModelId);
|
|
1368
|
-
addMessage({
|
|
1861
|
+
addMessage({
|
|
1862
|
+
role: "system",
|
|
1863
|
+
content: `Model changed to ${(0, import_agent_core3.getModelName)(pendingModelId)}. Restarting...`
|
|
1864
|
+
});
|
|
1369
1865
|
setTimeout(() => exit(), EXIT_DELAY_MS2);
|
|
1370
1866
|
} catch (err) {
|
|
1371
|
-
addMessage({
|
|
1867
|
+
addMessage({
|
|
1868
|
+
role: "system",
|
|
1869
|
+
content: `Failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1870
|
+
});
|
|
1372
1871
|
}
|
|
1373
1872
|
} else {
|
|
1374
1873
|
addMessage({ role: "system", content: "Model change cancelled." });
|
|
@@ -1427,23 +1926,24 @@ function renderApp(options) {
|
|
|
1427
1926
|
|
|
1428
1927
|
// src/cli.ts
|
|
1429
1928
|
var import_meta = {};
|
|
1430
|
-
function
|
|
1431
|
-
if (!(0, import_node_fs3.existsSync)(filePath)) return
|
|
1929
|
+
function checkSettingsFile(filePath) {
|
|
1930
|
+
if (!(0, import_node_fs3.existsSync)(filePath)) return "missing";
|
|
1432
1931
|
try {
|
|
1433
1932
|
const raw = (0, import_node_fs3.readFileSync)(filePath, "utf8").trim();
|
|
1434
|
-
if (raw.length === 0) return
|
|
1933
|
+
if (raw.length === 0) return "incomplete";
|
|
1435
1934
|
const parsed = JSON.parse(raw);
|
|
1436
1935
|
const provider = parsed.provider;
|
|
1437
|
-
|
|
1936
|
+
if (!provider?.apiKey) return "incomplete";
|
|
1937
|
+
return "valid";
|
|
1438
1938
|
} catch {
|
|
1439
|
-
return
|
|
1939
|
+
return "corrupt";
|
|
1440
1940
|
}
|
|
1441
1941
|
}
|
|
1442
1942
|
function readVersion() {
|
|
1443
1943
|
try {
|
|
1444
1944
|
const thisFile = (0, import_node_url.fileURLToPath)(import_meta.url);
|
|
1445
|
-
const dir = (0,
|
|
1446
|
-
const candidates = [(0,
|
|
1945
|
+
const dir = (0, import_node_path5.dirname)(thisFile);
|
|
1946
|
+
const candidates = [(0, import_node_path5.join)(dir, "..", "..", "package.json"), (0, import_node_path5.join)(dir, "..", "package.json")];
|
|
1447
1947
|
for (const pkgPath of candidates) {
|
|
1448
1948
|
try {
|
|
1449
1949
|
const raw = (0, import_node_fs3.readFileSync)(pkgPath, "utf-8");
|
|
@@ -1496,14 +1996,36 @@ function promptInput(label, masked = false) {
|
|
|
1496
1996
|
}
|
|
1497
1997
|
async function ensureConfig(cwd) {
|
|
1498
1998
|
const userPath = getUserSettingsPath();
|
|
1499
|
-
const projectPath = (0,
|
|
1500
|
-
const localPath = (0,
|
|
1501
|
-
|
|
1999
|
+
const projectPath = (0, import_node_path5.join)(cwd, ".robota", "settings.json");
|
|
2000
|
+
const localPath = (0, import_node_path5.join)(cwd, ".robota", "settings.local.json");
|
|
2001
|
+
const paths = [userPath, projectPath, localPath];
|
|
2002
|
+
const checks = paths.map((p) => ({ path: p, status: checkSettingsFile(p) }));
|
|
2003
|
+
if (checks.some((c) => c.status === "valid")) {
|
|
1502
2004
|
return;
|
|
1503
2005
|
}
|
|
2006
|
+
const corrupt = checks.filter((c) => c.status === "corrupt");
|
|
2007
|
+
const incomplete = checks.filter((c) => c.status === "incomplete");
|
|
1504
2008
|
process.stdout.write("\n");
|
|
1505
|
-
|
|
1506
|
-
|
|
2009
|
+
if (corrupt.length > 0) {
|
|
2010
|
+
for (const c of corrupt) {
|
|
2011
|
+
process.stderr.write(` ERROR: Settings file is corrupt (invalid JSON): ${c.path}
|
|
2012
|
+
`);
|
|
2013
|
+
}
|
|
2014
|
+
process.stdout.write("\n");
|
|
2015
|
+
}
|
|
2016
|
+
if (incomplete.length > 0) {
|
|
2017
|
+
for (const c of incomplete) {
|
|
2018
|
+
process.stderr.write(` WARNING: Settings file is missing provider.apiKey: ${c.path}
|
|
2019
|
+
`);
|
|
2020
|
+
}
|
|
2021
|
+
process.stdout.write("\n");
|
|
2022
|
+
}
|
|
2023
|
+
if (corrupt.length === 0 && incomplete.length === 0) {
|
|
2024
|
+
process.stdout.write(" Welcome to Robota CLI!\n");
|
|
2025
|
+
process.stdout.write(" No configuration found. Let's set up.\n");
|
|
2026
|
+
} else {
|
|
2027
|
+
process.stdout.write(" Reconfiguring...\n");
|
|
2028
|
+
}
|
|
1507
2029
|
process.stdout.write("\n");
|
|
1508
2030
|
const apiKey = await promptInput(" Anthropic API key: ", true);
|
|
1509
2031
|
if (!apiKey) {
|
|
@@ -1511,7 +2033,7 @@ async function ensureConfig(cwd) {
|
|
|
1511
2033
|
process.exit(1);
|
|
1512
2034
|
}
|
|
1513
2035
|
const language = await promptInput(" Response language (ko/en/ja/zh, default: en): ");
|
|
1514
|
-
const settingsDir = (0,
|
|
2036
|
+
const settingsDir = (0, import_node_path5.dirname)(userPath);
|
|
1515
2037
|
(0, import_node_fs3.mkdirSync)(settingsDir, { recursive: true });
|
|
1516
2038
|
const settings = {
|
|
1517
2039
|
provider: {
|
|
@@ -1552,9 +2074,9 @@ async function startCli() {
|
|
|
1552
2074
|
const cwd = process.cwd();
|
|
1553
2075
|
await ensureConfig(cwd);
|
|
1554
2076
|
const [config, context, projectInfo] = await Promise.all([
|
|
1555
|
-
(0,
|
|
1556
|
-
(0,
|
|
1557
|
-
(0,
|
|
2077
|
+
(0, import_agent_sdk4.loadConfig)(cwd),
|
|
2078
|
+
(0, import_agent_sdk4.loadContext)(cwd),
|
|
2079
|
+
(0, import_agent_sdk4.detectProject)(cwd)
|
|
1558
2080
|
]);
|
|
1559
2081
|
if (args.model !== void 0) {
|
|
1560
2082
|
config.provider.model = args.model;
|
|
@@ -1562,7 +2084,7 @@ async function startCli() {
|
|
|
1562
2084
|
if (args.language !== void 0) {
|
|
1563
2085
|
config.language = args.language;
|
|
1564
2086
|
}
|
|
1565
|
-
const sessionStore = new
|
|
2087
|
+
const sessionStore = new import_agent_sdk4.SessionStore();
|
|
1566
2088
|
if (args.printMode) {
|
|
1567
2089
|
const prompt = args.positional.join(" ").trim();
|
|
1568
2090
|
if (prompt.length === 0) {
|
|
@@ -1570,15 +2092,15 @@ async function startCli() {
|
|
|
1570
2092
|
process.exit(1);
|
|
1571
2093
|
}
|
|
1572
2094
|
const terminal = new PrintTerminal();
|
|
1573
|
-
const paths = (0,
|
|
1574
|
-
const session = (0,
|
|
2095
|
+
const paths = (0, import_agent_sdk4.projectPaths)(cwd);
|
|
2096
|
+
const session = (0, import_agent_sdk4.createSession)({
|
|
1575
2097
|
config,
|
|
1576
2098
|
context,
|
|
1577
2099
|
terminal,
|
|
1578
|
-
sessionLogger: new
|
|
2100
|
+
sessionLogger: new import_agent_sdk4.FileSessionLogger(paths.logs),
|
|
1579
2101
|
projectInfo,
|
|
1580
2102
|
permissionMode: args.permissionMode,
|
|
1581
|
-
promptForApproval:
|
|
2103
|
+
promptForApproval: import_agent_sdk5.promptForApproval
|
|
1582
2104
|
});
|
|
1583
2105
|
const response = await session.run(prompt);
|
|
1584
2106
|
process.stdout.write(response + "\n");
|