@robota-sdk/agent-sdk 3.0.0-beta.43 → 3.0.0-beta.45
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 +8 -1
- package/dist/node/index.cjs +850 -639
- package/dist/node/index.d.cts +344 -314
- package/dist/node/index.d.ts +344 -314
- package/dist/node/index.js +881 -652
- package/package.json +4 -5
package/dist/node/index.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// src/types.ts
|
|
2
|
-
import { TRUST_TO_MODE } from "@robota-sdk/agent-core";
|
|
3
|
-
|
|
4
1
|
// src/hooks/prompt-executor.ts
|
|
5
2
|
function extractJson(raw) {
|
|
6
3
|
const codeBlockMatch = /```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/.exec(raw);
|
|
@@ -239,18 +236,6 @@ function createDefaultTools() {
|
|
|
239
236
|
];
|
|
240
237
|
}
|
|
241
238
|
|
|
242
|
-
// src/assembly/create-provider.ts
|
|
243
|
-
import { AnthropicProvider } from "@robota-sdk/agent-provider-anthropic";
|
|
244
|
-
function createProvider(config) {
|
|
245
|
-
const apiKey = config.provider.apiKey ?? process.env["ANTHROPIC_API_KEY"];
|
|
246
|
-
if (!apiKey) {
|
|
247
|
-
throw new Error(
|
|
248
|
-
"ANTHROPIC_API_KEY is not set. Set the environment variable or configure provider.apiKey in ~/.robota/settings.json"
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
return new AnthropicProvider({ apiKey });
|
|
252
|
-
}
|
|
253
|
-
|
|
254
239
|
// src/tools/agent-tool.ts
|
|
255
240
|
import { z } from "zod";
|
|
256
241
|
import { createZodFunctionTool } from "@robota-sdk/agent-tools";
|
|
@@ -380,7 +365,7 @@ function createSubagentSession(options) {
|
|
|
380
365
|
agentsMd: parentContext.agentsMd,
|
|
381
366
|
isForkWorker: options.isForkWorker ?? false
|
|
382
367
|
});
|
|
383
|
-
const provider =
|
|
368
|
+
const provider = options.provider;
|
|
384
369
|
return new Session({
|
|
385
370
|
tools,
|
|
386
371
|
provider,
|
|
@@ -441,6 +426,7 @@ function createAgentTool(deps) {
|
|
|
441
426
|
parentConfig: deps.config,
|
|
442
427
|
parentContext: deps.context,
|
|
443
428
|
parentTools: deps.tools,
|
|
429
|
+
provider: deps.provider,
|
|
444
430
|
terminal: deps.terminal,
|
|
445
431
|
permissionMode: deps.permissionMode,
|
|
446
432
|
permissionHandler: deps.permissionHandler,
|
|
@@ -587,7 +573,12 @@ var AgentDefinitionLoader = class {
|
|
|
587
573
|
|
|
588
574
|
// src/assembly/create-session.ts
|
|
589
575
|
function createSession(options) {
|
|
590
|
-
|
|
576
|
+
if (!options.provider) {
|
|
577
|
+
throw new Error(
|
|
578
|
+
"provider is required. SDK is provider-neutral \u2014 consumer must create and pass a provider instance."
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
const provider = options.provider;
|
|
591
582
|
const defaultTools = createDefaultTools();
|
|
592
583
|
const tools = [...defaultTools, ...options.additionalTools ?? []];
|
|
593
584
|
const agentLoader = new AgentDefinitionLoader(process.cwd());
|
|
@@ -615,6 +606,7 @@ function createSession(options) {
|
|
|
615
606
|
context: options.context,
|
|
616
607
|
tools,
|
|
617
608
|
terminal: options.terminal,
|
|
609
|
+
provider,
|
|
618
610
|
permissionMode: options.permissionMode,
|
|
619
611
|
permissionHandler: options.permissionHandler,
|
|
620
612
|
hooks: options.config.hooks,
|
|
@@ -684,14 +676,32 @@ function resolveSubagentLogDir(parentSessionId, baseLogsDir) {
|
|
|
684
676
|
return join2(baseLogsDir, parentSessionId, "subagents");
|
|
685
677
|
}
|
|
686
678
|
|
|
687
|
-
// src/
|
|
688
|
-
import {
|
|
689
|
-
|
|
690
|
-
|
|
679
|
+
// src/interactive/interactive-session.ts
|
|
680
|
+
import { FileSessionLogger as FileSessionLogger2 } from "@robota-sdk/agent-sessions";
|
|
681
|
+
|
|
682
|
+
// src/paths.ts
|
|
683
|
+
import { join as join3 } from "path";
|
|
684
|
+
import { homedir as homedir2 } from "os";
|
|
685
|
+
function projectPaths(cwd) {
|
|
686
|
+
const base = join3(cwd, ".robota");
|
|
687
|
+
return {
|
|
688
|
+
settings: join3(base, "settings.json"),
|
|
689
|
+
settingsLocal: join3(base, "settings.local.json"),
|
|
690
|
+
logs: join3(base, "logs"),
|
|
691
|
+
sessions: join3(base, "sessions")
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
function userPaths() {
|
|
695
|
+
const base = join3(homedir2(), ".robota");
|
|
696
|
+
return {
|
|
697
|
+
settings: join3(base, "settings.json"),
|
|
698
|
+
sessions: join3(base, "sessions")
|
|
699
|
+
};
|
|
700
|
+
}
|
|
691
701
|
|
|
692
702
|
// src/config/config-loader.ts
|
|
693
703
|
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
694
|
-
import { join as
|
|
704
|
+
import { join as join4 } from "path";
|
|
695
705
|
|
|
696
706
|
// src/config/config-types.ts
|
|
697
707
|
import { z as z2 } from "zod";
|
|
@@ -867,15 +877,15 @@ function toResolvedConfig(merged) {
|
|
|
867
877
|
function getSettingsPaths(cwd) {
|
|
868
878
|
const home = getHomeDir();
|
|
869
879
|
return [
|
|
870
|
-
|
|
880
|
+
join4(home, ".robota", "settings.json"),
|
|
871
881
|
// 1. user (lowest)
|
|
872
|
-
|
|
882
|
+
join4(cwd, ".robota", "settings.json"),
|
|
873
883
|
// 2. project
|
|
874
|
-
|
|
884
|
+
join4(cwd, ".robota", "settings.local.json"),
|
|
875
885
|
// 3. project-local
|
|
876
|
-
|
|
886
|
+
join4(cwd, ".claude", "settings.json"),
|
|
877
887
|
// 4. project, Claude Code compat
|
|
878
|
-
|
|
888
|
+
join4(cwd, ".claude", "settings.local.json")
|
|
879
889
|
// 5. project-local (highest)
|
|
880
890
|
];
|
|
881
891
|
}
|
|
@@ -901,7 +911,7 @@ async function loadConfig(cwd) {
|
|
|
901
911
|
|
|
902
912
|
// src/context/context-loader.ts
|
|
903
913
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
904
|
-
import { join as
|
|
914
|
+
import { join as join5, dirname, resolve } from "path";
|
|
905
915
|
var AGENTS_FILENAME = "AGENTS.md";
|
|
906
916
|
var CLAUDE_FILENAME = "CLAUDE.md";
|
|
907
917
|
function collectFilesWalkingUp(startDir, filename) {
|
|
@@ -909,7 +919,7 @@ function collectFilesWalkingUp(startDir, filename) {
|
|
|
909
919
|
let current = resolve(startDir);
|
|
910
920
|
let atRoot = false;
|
|
911
921
|
while (!atRoot) {
|
|
912
|
-
const candidate =
|
|
922
|
+
const candidate = join5(current, filename);
|
|
913
923
|
if (existsSync3(candidate)) {
|
|
914
924
|
found.push(candidate);
|
|
915
925
|
}
|
|
@@ -956,7 +966,7 @@ async function loadContext(cwd) {
|
|
|
956
966
|
|
|
957
967
|
// src/context/project-detector.ts
|
|
958
968
|
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
959
|
-
import { join as
|
|
969
|
+
import { join as join6 } from "path";
|
|
960
970
|
function tryReadJson(filePath) {
|
|
961
971
|
if (!existsSync4(filePath)) return void 0;
|
|
962
972
|
try {
|
|
@@ -966,26 +976,26 @@ function tryReadJson(filePath) {
|
|
|
966
976
|
}
|
|
967
977
|
}
|
|
968
978
|
function detectPackageManager(cwd) {
|
|
969
|
-
if (existsSync4(
|
|
979
|
+
if (existsSync4(join6(cwd, "pnpm-workspace.yaml")) || existsSync4(join6(cwd, "pnpm-lock.yaml"))) {
|
|
970
980
|
return "pnpm";
|
|
971
981
|
}
|
|
972
|
-
if (existsSync4(
|
|
982
|
+
if (existsSync4(join6(cwd, "yarn.lock"))) {
|
|
973
983
|
return "yarn";
|
|
974
984
|
}
|
|
975
|
-
if (existsSync4(
|
|
985
|
+
if (existsSync4(join6(cwd, "bun.lockb"))) {
|
|
976
986
|
return "bun";
|
|
977
987
|
}
|
|
978
|
-
if (existsSync4(
|
|
988
|
+
if (existsSync4(join6(cwd, "package-lock.json"))) {
|
|
979
989
|
return "npm";
|
|
980
990
|
}
|
|
981
991
|
return void 0;
|
|
982
992
|
}
|
|
983
993
|
async function detectProject(cwd) {
|
|
984
|
-
const pkgJsonPath =
|
|
985
|
-
const tsconfigPath =
|
|
986
|
-
const pyprojectPath =
|
|
987
|
-
const cargoPath =
|
|
988
|
-
const goModPath =
|
|
994
|
+
const pkgJsonPath = join6(cwd, "package.json");
|
|
995
|
+
const tsconfigPath = join6(cwd, "tsconfig.json");
|
|
996
|
+
const pyprojectPath = join6(cwd, "pyproject.toml");
|
|
997
|
+
const cargoPath = join6(cwd, "Cargo.toml");
|
|
998
|
+
const goModPath = join6(cwd, "go.mod");
|
|
989
999
|
if (existsSync4(pkgJsonPath)) {
|
|
990
1000
|
const pkgJson = tryReadJson(pkgJsonPath);
|
|
991
1001
|
const language = existsSync4(tsconfigPath) ? "typescript" : "javascript";
|
|
@@ -997,7 +1007,7 @@ async function detectProject(cwd) {
|
|
|
997
1007
|
language
|
|
998
1008
|
};
|
|
999
1009
|
}
|
|
1000
|
-
if (existsSync4(pyprojectPath) || existsSync4(
|
|
1010
|
+
if (existsSync4(pyprojectPath) || existsSync4(join6(cwd, "setup.py"))) {
|
|
1001
1011
|
return {
|
|
1002
1012
|
type: "python",
|
|
1003
1013
|
language: "python"
|
|
@@ -1021,102 +1031,225 @@ async function detectProject(cwd) {
|
|
|
1021
1031
|
};
|
|
1022
1032
|
}
|
|
1023
1033
|
|
|
1024
|
-
// src/
|
|
1025
|
-
import
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
}
|
|
1033
|
-
return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
1034
|
-
}
|
|
1035
|
-
async function promptForApproval(terminal, toolName, toolArgs) {
|
|
1036
|
-
terminal.writeLine("");
|
|
1037
|
-
terminal.writeLine(chalk.yellow(`[Permission Required] Tool: ${toolName}`));
|
|
1038
|
-
terminal.writeLine(chalk.dim(` ${formatArgs(toolArgs)}`));
|
|
1039
|
-
terminal.writeLine("");
|
|
1040
|
-
const selected = await terminal.select(PERMISSION_OPTIONS, ALLOW_INDEX);
|
|
1041
|
-
return selected === ALLOW_INDEX;
|
|
1042
|
-
}
|
|
1034
|
+
// src/interactive/interactive-session.ts
|
|
1035
|
+
import {
|
|
1036
|
+
createUserMessage,
|
|
1037
|
+
createAssistantMessage,
|
|
1038
|
+
createSystemMessage,
|
|
1039
|
+
messageToHistoryEntry
|
|
1040
|
+
} from "@robota-sdk/agent-core";
|
|
1041
|
+
import { randomUUID } from "crypto";
|
|
1043
1042
|
|
|
1044
|
-
// src/
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1043
|
+
// src/commands/system-command.ts
|
|
1044
|
+
var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
1045
|
+
function createSystemCommands() {
|
|
1046
|
+
return [
|
|
1047
|
+
{
|
|
1048
|
+
name: "help",
|
|
1049
|
+
description: "Show available commands",
|
|
1050
|
+
execute: (_session, _args) => ({
|
|
1051
|
+
message: [
|
|
1052
|
+
"Available commands:",
|
|
1053
|
+
" help \u2014 Show this help",
|
|
1054
|
+
" clear \u2014 Clear conversation",
|
|
1055
|
+
" compact [instr] \u2014 Compact context (optional focus instructions)",
|
|
1056
|
+
" mode [m] \u2014 Show/change permission mode",
|
|
1057
|
+
" model <id> \u2014 Change AI model",
|
|
1058
|
+
" language <code> \u2014 Set response language (ko, en, ja, zh)",
|
|
1059
|
+
" cost \u2014 Show session info",
|
|
1060
|
+
" context \u2014 Context window info",
|
|
1061
|
+
" permissions \u2014 Permission rules",
|
|
1062
|
+
" reset \u2014 Delete settings and exit"
|
|
1063
|
+
].join("\n"),
|
|
1064
|
+
success: true
|
|
1065
|
+
})
|
|
1054
1066
|
},
|
|
1055
|
-
|
|
1067
|
+
{
|
|
1068
|
+
name: "clear",
|
|
1069
|
+
description: "Clear conversation history",
|
|
1070
|
+
execute: (session, _args) => {
|
|
1071
|
+
const underlying = session.getSession();
|
|
1072
|
+
underlying.clearHistory();
|
|
1073
|
+
return { message: "Conversation cleared.", success: true };
|
|
1074
|
+
}
|
|
1056
1075
|
},
|
|
1057
|
-
|
|
1076
|
+
{
|
|
1077
|
+
name: "compact",
|
|
1078
|
+
description: "Compress context window",
|
|
1079
|
+
execute: async (session, args) => {
|
|
1080
|
+
const underlying = session.getSession();
|
|
1081
|
+
const instructions = args.trim() || void 0;
|
|
1082
|
+
const before = underlying.getContextState().usedPercentage;
|
|
1083
|
+
await underlying.compact(instructions);
|
|
1084
|
+
const after = underlying.getContextState().usedPercentage;
|
|
1085
|
+
return {
|
|
1086
|
+
message: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`,
|
|
1087
|
+
success: true,
|
|
1088
|
+
data: { before, after }
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1058
1091
|
},
|
|
1059
|
-
|
|
1092
|
+
{
|
|
1093
|
+
name: "mode",
|
|
1094
|
+
description: "Show/change permission mode",
|
|
1095
|
+
execute: (session, args) => {
|
|
1096
|
+
const underlying = session.getSession();
|
|
1097
|
+
const arg = args.trim().split(/\s+/)[0];
|
|
1098
|
+
if (!arg) {
|
|
1099
|
+
return {
|
|
1100
|
+
message: `Current mode: ${underlying.getPermissionMode()}`,
|
|
1101
|
+
success: true,
|
|
1102
|
+
data: { mode: underlying.getPermissionMode() }
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
if (VALID_MODES.includes(arg)) {
|
|
1106
|
+
underlying.setPermissionMode(arg);
|
|
1107
|
+
return {
|
|
1108
|
+
message: `Permission mode set to: ${arg}`,
|
|
1109
|
+
success: true,
|
|
1110
|
+
data: { mode: arg }
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
return {
|
|
1114
|
+
message: `Invalid mode. Valid: ${VALID_MODES.join(" | ")}`,
|
|
1115
|
+
success: false
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1060
1118
|
},
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1119
|
+
{
|
|
1120
|
+
name: "model",
|
|
1121
|
+
description: "Change AI model",
|
|
1122
|
+
execute: (_session, args) => {
|
|
1123
|
+
const modelId = args.trim().split(/\s+/)[0];
|
|
1124
|
+
if (!modelId) {
|
|
1125
|
+
return { message: "Usage: model <model-id>", success: false };
|
|
1126
|
+
}
|
|
1127
|
+
return {
|
|
1128
|
+
message: `Model change requested: ${modelId}`,
|
|
1129
|
+
success: true,
|
|
1130
|
+
data: { modelId }
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
},
|
|
1134
|
+
{
|
|
1135
|
+
name: "language",
|
|
1136
|
+
description: "Set response language",
|
|
1137
|
+
execute: (_session, args) => {
|
|
1138
|
+
const lang = args.trim().split(/\s+/)[0];
|
|
1139
|
+
if (!lang) {
|
|
1140
|
+
return { message: "Usage: language <code> (e.g., ko, en, ja, zh)", success: false };
|
|
1141
|
+
}
|
|
1142
|
+
return {
|
|
1143
|
+
message: `Language set to "${lang}".`,
|
|
1144
|
+
success: true,
|
|
1145
|
+
data: { language: lang }
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
{
|
|
1150
|
+
name: "cost",
|
|
1151
|
+
description: "Show session info",
|
|
1152
|
+
execute: (session, _args) => {
|
|
1153
|
+
const underlying = session.getSession();
|
|
1154
|
+
const sessionId = underlying.getSessionId();
|
|
1155
|
+
const messageCount = underlying.getMessageCount();
|
|
1156
|
+
return {
|
|
1157
|
+
message: `Session: ${sessionId}
|
|
1158
|
+
Messages: ${messageCount}`,
|
|
1159
|
+
success: true,
|
|
1160
|
+
data: { sessionId, messageCount }
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
name: "context",
|
|
1166
|
+
description: "Context window info",
|
|
1167
|
+
execute: (session, _args) => {
|
|
1168
|
+
const ctx = session.getContextState();
|
|
1169
|
+
return {
|
|
1170
|
+
message: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`,
|
|
1171
|
+
success: true,
|
|
1172
|
+
data: {
|
|
1173
|
+
usedTokens: ctx.usedTokens,
|
|
1174
|
+
maxTokens: ctx.maxTokens,
|
|
1175
|
+
percentage: ctx.usedPercentage
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
{
|
|
1181
|
+
name: "permissions",
|
|
1182
|
+
description: "Show permission rules",
|
|
1183
|
+
execute: (session, _args) => {
|
|
1184
|
+
const underlying = session.getSession();
|
|
1185
|
+
const mode = underlying.getPermissionMode();
|
|
1186
|
+
const sessionAllowed = underlying.getSessionAllowedTools();
|
|
1187
|
+
const lines = [`Permission mode: ${mode}`];
|
|
1188
|
+
if (sessionAllowed.length > 0) {
|
|
1189
|
+
lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
|
|
1190
|
+
} else {
|
|
1191
|
+
lines.push("No session-approved tools.");
|
|
1192
|
+
}
|
|
1193
|
+
return {
|
|
1194
|
+
message: lines.join("\n"),
|
|
1195
|
+
success: true,
|
|
1196
|
+
data: { mode, sessionAllowed }
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
},
|
|
1200
|
+
{
|
|
1201
|
+
name: "reset",
|
|
1202
|
+
description: "Delete settings",
|
|
1203
|
+
execute: (_session, _args) => {
|
|
1204
|
+
return {
|
|
1205
|
+
message: "Reset requested.",
|
|
1206
|
+
success: true,
|
|
1207
|
+
data: { resetRequested: true }
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
];
|
|
1106
1212
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1213
|
+
var SystemCommandExecutor = class {
|
|
1214
|
+
commands;
|
|
1215
|
+
constructor(commands) {
|
|
1216
|
+
this.commands = /* @__PURE__ */ new Map();
|
|
1217
|
+
for (const cmd of commands ?? createSystemCommands()) {
|
|
1218
|
+
this.commands.set(cmd.name, cmd);
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
/** Register an additional command. */
|
|
1222
|
+
register(command) {
|
|
1223
|
+
this.commands.set(command.name, command);
|
|
1224
|
+
}
|
|
1225
|
+
/** Execute a command by name. Returns null if command not found. */
|
|
1226
|
+
async execute(name, session, args) {
|
|
1227
|
+
const cmd = this.commands.get(name);
|
|
1228
|
+
if (!cmd) return null;
|
|
1229
|
+
return await cmd.execute(session, args);
|
|
1230
|
+
}
|
|
1231
|
+
/** List all registered commands. */
|
|
1232
|
+
listCommands() {
|
|
1233
|
+
return [...this.commands.values()];
|
|
1234
|
+
}
|
|
1235
|
+
/** Check if a command exists. */
|
|
1236
|
+
hasCommand(name) {
|
|
1237
|
+
return this.commands.has(name);
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
|
|
1241
|
+
// src/plugins/plugin-settings-store.ts
|
|
1242
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
1243
|
+
import { dirname as dirname2 } from "path";
|
|
1244
|
+
var PluginSettingsStore = class {
|
|
1245
|
+
settingsPath;
|
|
1246
|
+
constructor(settingsPath) {
|
|
1247
|
+
this.settingsPath = settingsPath;
|
|
1248
|
+
}
|
|
1249
|
+
/** Read the full settings file from disk. */
|
|
1250
|
+
readAll() {
|
|
1251
|
+
if (!existsSync5(this.settingsPath)) {
|
|
1252
|
+
return {};
|
|
1120
1253
|
}
|
|
1121
1254
|
try {
|
|
1122
1255
|
const raw = readFileSync5(this.settingsPath, "utf-8");
|
|
@@ -1908,479 +2041,160 @@ var MarketplaceClient = class {
|
|
|
1908
2041
|
}
|
|
1909
2042
|
};
|
|
1910
2043
|
|
|
1911
|
-
// src/
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
2044
|
+
// src/plugins/plugin-hooks-merger.ts
|
|
2045
|
+
import { join as join10, dirname as dirname5 } from "path";
|
|
2046
|
+
function buildPluginEnv(plugin) {
|
|
2047
|
+
const dataDir = join10(dirname5(dirname5(plugin.pluginDir)), "data", plugin.manifest.name);
|
|
2048
|
+
return {
|
|
2049
|
+
CLAUDE_PLUGIN_ROOT: plugin.pluginDir,
|
|
2050
|
+
CLAUDE_PLUGIN_PATH: plugin.pluginDir,
|
|
2051
|
+
CLAUDE_PLUGIN_DATA: dataDir
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
function resolvePluginRoot(group, pluginDir) {
|
|
2055
|
+
if (Array.isArray(group.hooks)) {
|
|
2056
|
+
return {
|
|
2057
|
+
...group,
|
|
2058
|
+
hooks: group.hooks.map((h) => {
|
|
2059
|
+
if (typeof h.command === "string") {
|
|
2060
|
+
return {
|
|
2061
|
+
...h,
|
|
2062
|
+
command: h.command.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginDir)
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
return h;
|
|
2066
|
+
})
|
|
2067
|
+
};
|
|
1916
2068
|
}
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
2069
|
+
return group;
|
|
2070
|
+
}
|
|
2071
|
+
function mergePluginHooks(plugins) {
|
|
2072
|
+
const merged = {};
|
|
2073
|
+
for (const plugin of plugins) {
|
|
2074
|
+
const hooksObj = plugin.hooks;
|
|
2075
|
+
if (!hooksObj) continue;
|
|
2076
|
+
const pluginEnv = buildPluginEnv(plugin);
|
|
2077
|
+
const innerHooks = hooksObj.hooks ?? hooksObj;
|
|
2078
|
+
for (const [event, groups] of Object.entries(innerHooks)) {
|
|
2079
|
+
if (!Array.isArray(groups)) continue;
|
|
2080
|
+
if (!merged[event]) merged[event] = [];
|
|
2081
|
+
const resolved = groups.map((group) => {
|
|
2082
|
+
const r = resolvePluginRoot(group, plugin.pluginDir);
|
|
2083
|
+
r.env = pluginEnv;
|
|
2084
|
+
return r;
|
|
2085
|
+
});
|
|
2086
|
+
merged[event].push(...resolved);
|
|
1922
2087
|
}
|
|
1923
|
-
if (!filter) return all;
|
|
1924
|
-
const lower = filter.toLowerCase();
|
|
1925
|
-
return all.filter((cmd) => cmd.name.toLowerCase().startsWith(lower));
|
|
1926
2088
|
}
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
2089
|
+
return merged;
|
|
2090
|
+
}
|
|
2091
|
+
function mergeHooksIntoConfig(configHooks, pluginHooks) {
|
|
2092
|
+
const pluginKeys = Object.keys(pluginHooks);
|
|
2093
|
+
if (pluginKeys.length === 0) return configHooks;
|
|
2094
|
+
const merged = {};
|
|
2095
|
+
for (const [event, groups] of Object.entries(pluginHooks)) {
|
|
2096
|
+
merged[event] = [...groups];
|
|
1934
2097
|
}
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
if (cmd.name.toLowerCase() === lower && cmd.subcommands) {
|
|
1941
|
-
return cmd.subcommands;
|
|
1942
|
-
}
|
|
1943
|
-
}
|
|
2098
|
+
if (configHooks) {
|
|
2099
|
+
for (const [event, groups] of Object.entries(configHooks)) {
|
|
2100
|
+
if (!Array.isArray(groups)) continue;
|
|
2101
|
+
if (!merged[event]) merged[event] = [];
|
|
2102
|
+
merged[event].push(...groups);
|
|
1944
2103
|
}
|
|
1945
|
-
return [];
|
|
1946
|
-
}
|
|
1947
|
-
};
|
|
1948
|
-
|
|
1949
|
-
// src/commands/builtin-source.ts
|
|
1950
|
-
import { CLAUDE_MODELS, formatTokenCount } from "@robota-sdk/agent-core";
|
|
1951
|
-
function buildModelSubcommands() {
|
|
1952
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1953
|
-
const commands = [];
|
|
1954
|
-
for (const model of Object.values(CLAUDE_MODELS)) {
|
|
1955
|
-
if (seen.has(model.name)) continue;
|
|
1956
|
-
seen.add(model.name);
|
|
1957
|
-
commands.push({
|
|
1958
|
-
name: model.id,
|
|
1959
|
-
description: `${model.name} (${formatTokenCount(model.contextWindow).toUpperCase()})`,
|
|
1960
|
-
source: "builtin"
|
|
1961
|
-
});
|
|
1962
2104
|
}
|
|
1963
|
-
return
|
|
1964
|
-
}
|
|
1965
|
-
function createBuiltinCommands() {
|
|
1966
|
-
return [
|
|
1967
|
-
{ name: "help", description: "Show available commands", source: "builtin" },
|
|
1968
|
-
{ name: "clear", description: "Clear conversation history", source: "builtin" },
|
|
1969
|
-
{
|
|
1970
|
-
name: "mode",
|
|
1971
|
-
description: "Permission mode",
|
|
1972
|
-
source: "builtin",
|
|
1973
|
-
subcommands: [
|
|
1974
|
-
{ name: "plan", description: "Plan only, no execution", source: "builtin" },
|
|
1975
|
-
{ name: "default", description: "Ask before risky actions", source: "builtin" },
|
|
1976
|
-
{ name: "acceptEdits", description: "Auto-approve file edits", source: "builtin" },
|
|
1977
|
-
{ name: "bypassPermissions", description: "Skip all permission checks", source: "builtin" }
|
|
1978
|
-
]
|
|
1979
|
-
},
|
|
1980
|
-
{
|
|
1981
|
-
name: "model",
|
|
1982
|
-
description: "Select AI model",
|
|
1983
|
-
source: "builtin",
|
|
1984
|
-
subcommands: buildModelSubcommands()
|
|
1985
|
-
},
|
|
1986
|
-
{
|
|
1987
|
-
name: "language",
|
|
1988
|
-
description: "Set response language",
|
|
1989
|
-
source: "builtin",
|
|
1990
|
-
subcommands: [
|
|
1991
|
-
{ name: "ko", description: "Korean", source: "builtin" },
|
|
1992
|
-
{ name: "en", description: "English", source: "builtin" },
|
|
1993
|
-
{ name: "ja", description: "Japanese", source: "builtin" },
|
|
1994
|
-
{ name: "zh", description: "Chinese", source: "builtin" }
|
|
1995
|
-
]
|
|
1996
|
-
},
|
|
1997
|
-
{ name: "compact", description: "Compress context window", source: "builtin" },
|
|
1998
|
-
{ name: "cost", description: "Show session info", source: "builtin" },
|
|
1999
|
-
{ name: "context", description: "Context window info", source: "builtin" },
|
|
2000
|
-
{ name: "permissions", description: "Permission rules", source: "builtin" },
|
|
2001
|
-
{ name: "plugin", description: "Manage plugins", source: "builtin" },
|
|
2002
|
-
{ name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
|
|
2003
|
-
{ name: "reset", description: "Delete settings and exit", source: "builtin" },
|
|
2004
|
-
{ name: "exit", description: "Exit CLI", source: "builtin" }
|
|
2005
|
-
];
|
|
2105
|
+
return merged;
|
|
2006
2106
|
}
|
|
2007
|
-
var BuiltinCommandSource = class {
|
|
2008
|
-
name = "builtin";
|
|
2009
|
-
commands;
|
|
2010
|
-
constructor() {
|
|
2011
|
-
this.commands = createBuiltinCommands();
|
|
2012
|
-
}
|
|
2013
|
-
getCommands() {
|
|
2014
|
-
return this.commands;
|
|
2015
|
-
}
|
|
2016
|
-
};
|
|
2017
2107
|
|
|
2018
|
-
// src/
|
|
2019
|
-
import { readdirSync as readdirSync3, readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
|
|
2020
|
-
import { join as join10, basename as basename2 } from "path";
|
|
2108
|
+
// src/interactive/interactive-session.ts
|
|
2021
2109
|
import { homedir as homedir3 } from "os";
|
|
2022
|
-
|
|
2023
|
-
var
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2110
|
+
import { join as join11 } from "path";
|
|
2111
|
+
var TOOL_ARG_DISPLAY_MAX = 80;
|
|
2112
|
+
var TAIL_KEEP = 30;
|
|
2113
|
+
var MAX_COMPLETED_TOOLS = 50;
|
|
2114
|
+
var STREAMING_FLUSH_INTERVAL_MS = 16;
|
|
2115
|
+
var InteractiveSession = class {
|
|
2116
|
+
session = null;
|
|
2117
|
+
commandExecutor;
|
|
2118
|
+
listeners = /* @__PURE__ */ new Map();
|
|
2119
|
+
initialized = false;
|
|
2120
|
+
initPromise = null;
|
|
2121
|
+
// Streaming state
|
|
2122
|
+
streamingText = "";
|
|
2123
|
+
flushTimer = null;
|
|
2124
|
+
// Tool state
|
|
2125
|
+
activeTools = [];
|
|
2126
|
+
// Execution state
|
|
2127
|
+
executing = false;
|
|
2128
|
+
pendingPrompt = null;
|
|
2129
|
+
pendingDisplayInput;
|
|
2130
|
+
pendingRawInput;
|
|
2131
|
+
// Full history timeline (chat messages + events)
|
|
2132
|
+
history = [];
|
|
2133
|
+
constructor(options) {
|
|
2134
|
+
this.commandExecutor = new SystemCommandExecutor(createSystemCommands());
|
|
2135
|
+
if ("session" in options && options.session) {
|
|
2136
|
+
this.session = options.session;
|
|
2137
|
+
this.initialized = true;
|
|
2043
2138
|
} else {
|
|
2044
|
-
|
|
2139
|
+
const stdOpts = options;
|
|
2140
|
+
this.initPromise = this.initializeAsync(stdOpts);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
async initializeAsync(options) {
|
|
2144
|
+
const cwd = options.cwd;
|
|
2145
|
+
const [config, context, projectInfo] = await Promise.all([
|
|
2146
|
+
loadConfig(cwd),
|
|
2147
|
+
loadContext(cwd),
|
|
2148
|
+
detectProject(cwd)
|
|
2149
|
+
]);
|
|
2150
|
+
const pluginsDir = join11(homedir3(), ".robota", "plugins");
|
|
2151
|
+
const pluginLoader = new BundlePluginLoader(pluginsDir);
|
|
2152
|
+
let mergedConfig = config;
|
|
2153
|
+
try {
|
|
2154
|
+
const plugins = pluginLoader.loadPluginsSync();
|
|
2155
|
+
if (plugins.length > 0) {
|
|
2156
|
+
const pluginHooks = mergePluginHooks(plugins);
|
|
2157
|
+
mergedConfig = {
|
|
2158
|
+
...config,
|
|
2159
|
+
hooks: mergeHooksIntoConfig(
|
|
2160
|
+
config.hooks,
|
|
2161
|
+
pluginHooks
|
|
2162
|
+
)
|
|
2163
|
+
};
|
|
2164
|
+
}
|
|
2165
|
+
} catch {
|
|
2045
2166
|
}
|
|
2167
|
+
const paths = projectPaths(cwd);
|
|
2168
|
+
this.session = createSession({
|
|
2169
|
+
config: mergedConfig,
|
|
2170
|
+
context,
|
|
2171
|
+
projectInfo,
|
|
2172
|
+
permissionMode: options.permissionMode,
|
|
2173
|
+
maxTurns: options.maxTurns,
|
|
2174
|
+
terminal: NOOP_TERMINAL,
|
|
2175
|
+
sessionLogger: new FileSessionLogger2(paths.logs),
|
|
2176
|
+
permissionHandler: options.permissionHandler,
|
|
2177
|
+
provider: options.provider,
|
|
2178
|
+
onTextDelta: (delta) => this.handleTextDelta(delta),
|
|
2179
|
+
onToolExecution: (event) => this.handleToolExecution(event)
|
|
2180
|
+
});
|
|
2181
|
+
this.initialized = true;
|
|
2046
2182
|
}
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
const cmd = {
|
|
2051
|
-
name: frontmatter?.name ?? fallbackName,
|
|
2052
|
-
description: frontmatter?.description ?? `Skill: ${fallbackName}`,
|
|
2053
|
-
source: "skill",
|
|
2054
|
-
skillContent: content
|
|
2055
|
-
};
|
|
2056
|
-
if (frontmatter?.argumentHint !== void 0) cmd.argumentHint = frontmatter.argumentHint;
|
|
2057
|
-
if (frontmatter?.disableModelInvocation !== void 0)
|
|
2058
|
-
cmd.disableModelInvocation = frontmatter.disableModelInvocation;
|
|
2059
|
-
if (frontmatter?.userInvocable !== void 0) cmd.userInvocable = frontmatter.userInvocable;
|
|
2060
|
-
if (frontmatter?.allowedTools !== void 0) cmd.allowedTools = frontmatter.allowedTools;
|
|
2061
|
-
if (frontmatter?.model !== void 0) cmd.model = frontmatter.model;
|
|
2062
|
-
if (frontmatter?.effort !== void 0) cmd.effort = frontmatter.effort;
|
|
2063
|
-
if (frontmatter?.context !== void 0) cmd.context = frontmatter.context;
|
|
2064
|
-
if (frontmatter?.agent !== void 0) cmd.agent = frontmatter.agent;
|
|
2065
|
-
return cmd;
|
|
2066
|
-
}
|
|
2067
|
-
function scanSkillsDir(skillsDir) {
|
|
2068
|
-
if (!existsSync9(skillsDir)) return [];
|
|
2069
|
-
const commands = [];
|
|
2070
|
-
const entries = readdirSync3(skillsDir, { withFileTypes: true });
|
|
2071
|
-
for (const entry of entries) {
|
|
2072
|
-
if (!entry.isDirectory()) continue;
|
|
2073
|
-
const skillFile = join10(skillsDir, entry.name, "SKILL.md");
|
|
2074
|
-
if (!existsSync9(skillFile)) continue;
|
|
2075
|
-
const content = readFileSync9(skillFile, "utf-8");
|
|
2076
|
-
const frontmatter = parseFrontmatter2(content);
|
|
2077
|
-
commands.push(buildCommand(frontmatter, content, entry.name));
|
|
2183
|
+
async ensureInitialized() {
|
|
2184
|
+
if (this.initialized) return;
|
|
2185
|
+
if (this.initPromise) await this.initPromise;
|
|
2078
2186
|
}
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
const commands = [];
|
|
2084
|
-
const entries = readdirSync3(commandsDir, { withFileTypes: true });
|
|
2085
|
-
for (const entry of entries) {
|
|
2086
|
-
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
2087
|
-
const filePath = join10(commandsDir, entry.name);
|
|
2088
|
-
const content = readFileSync9(filePath, "utf-8");
|
|
2089
|
-
const frontmatter = parseFrontmatter2(content);
|
|
2090
|
-
const fallbackName = basename2(entry.name, ".md");
|
|
2091
|
-
commands.push(buildCommand(frontmatter, content, fallbackName));
|
|
2187
|
+
getSessionOrThrow() {
|
|
2188
|
+
if (!this.session)
|
|
2189
|
+
throw new Error("InteractiveSession not initialized. Call submit() or await initialization.");
|
|
2190
|
+
return this.session;
|
|
2092
2191
|
}
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
cachedCommands = null;
|
|
2100
|
-
constructor(cwd, home) {
|
|
2101
|
-
this.cwd = cwd;
|
|
2102
|
-
this.home = home ?? homedir3();
|
|
2103
|
-
}
|
|
2104
|
-
getCommands() {
|
|
2105
|
-
if (this.cachedCommands) return this.cachedCommands;
|
|
2106
|
-
const sources = [
|
|
2107
|
-
scanSkillsDir(join10(this.cwd, ".claude", "skills")),
|
|
2108
|
-
scanCommandsDir(join10(this.cwd, ".claude", "commands")),
|
|
2109
|
-
scanSkillsDir(join10(this.home, ".robota", "skills")),
|
|
2110
|
-
scanSkillsDir(join10(this.cwd, ".agents", "skills"))
|
|
2111
|
-
];
|
|
2112
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2113
|
-
const merged = [];
|
|
2114
|
-
for (const commands of sources) {
|
|
2115
|
-
for (const cmd of commands) {
|
|
2116
|
-
if (!seen.has(cmd.name)) {
|
|
2117
|
-
seen.add(cmd.name);
|
|
2118
|
-
merged.push(cmd);
|
|
2119
|
-
}
|
|
2120
|
-
}
|
|
2121
|
-
}
|
|
2122
|
-
this.cachedCommands = merged;
|
|
2123
|
-
return this.cachedCommands;
|
|
2124
|
-
}
|
|
2125
|
-
getModelInvocableSkills() {
|
|
2126
|
-
return this.getCommands().filter((cmd) => cmd.disableModelInvocation !== true);
|
|
2127
|
-
}
|
|
2128
|
-
getUserInvocableSkills() {
|
|
2129
|
-
return this.getCommands().filter((cmd) => cmd.userInvocable !== false);
|
|
2130
|
-
}
|
|
2131
|
-
};
|
|
2132
|
-
|
|
2133
|
-
// src/commands/system-command.ts
|
|
2134
|
-
var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
2135
|
-
function createSystemCommands() {
|
|
2136
|
-
return [
|
|
2137
|
-
{
|
|
2138
|
-
name: "help",
|
|
2139
|
-
description: "Show available commands",
|
|
2140
|
-
execute: (_session, _args) => ({
|
|
2141
|
-
message: [
|
|
2142
|
-
"Available commands:",
|
|
2143
|
-
" help \u2014 Show this help",
|
|
2144
|
-
" clear \u2014 Clear conversation",
|
|
2145
|
-
" compact [instr] \u2014 Compact context (optional focus instructions)",
|
|
2146
|
-
" mode [m] \u2014 Show/change permission mode",
|
|
2147
|
-
" model <id> \u2014 Change AI model",
|
|
2148
|
-
" language <code> \u2014 Set response language (ko, en, ja, zh)",
|
|
2149
|
-
" cost \u2014 Show session info",
|
|
2150
|
-
" context \u2014 Context window info",
|
|
2151
|
-
" permissions \u2014 Permission rules",
|
|
2152
|
-
" reset \u2014 Delete settings and exit"
|
|
2153
|
-
].join("\n"),
|
|
2154
|
-
success: true
|
|
2155
|
-
})
|
|
2156
|
-
},
|
|
2157
|
-
{
|
|
2158
|
-
name: "clear",
|
|
2159
|
-
description: "Clear conversation history",
|
|
2160
|
-
execute: (session, _args) => {
|
|
2161
|
-
const underlying = session.getSession();
|
|
2162
|
-
underlying.clearHistory();
|
|
2163
|
-
return { message: "Conversation cleared.", success: true };
|
|
2164
|
-
}
|
|
2165
|
-
},
|
|
2166
|
-
{
|
|
2167
|
-
name: "compact",
|
|
2168
|
-
description: "Compress context window",
|
|
2169
|
-
execute: async (session, args) => {
|
|
2170
|
-
const underlying = session.getSession();
|
|
2171
|
-
const instructions = args.trim() || void 0;
|
|
2172
|
-
const before = underlying.getContextState().usedPercentage;
|
|
2173
|
-
await underlying.compact(instructions);
|
|
2174
|
-
const after = underlying.getContextState().usedPercentage;
|
|
2175
|
-
return {
|
|
2176
|
-
message: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`,
|
|
2177
|
-
success: true,
|
|
2178
|
-
data: { before, after }
|
|
2179
|
-
};
|
|
2180
|
-
}
|
|
2181
|
-
},
|
|
2182
|
-
{
|
|
2183
|
-
name: "mode",
|
|
2184
|
-
description: "Show/change permission mode",
|
|
2185
|
-
execute: (session, args) => {
|
|
2186
|
-
const underlying = session.getSession();
|
|
2187
|
-
const arg = args.trim().split(/\s+/)[0];
|
|
2188
|
-
if (!arg) {
|
|
2189
|
-
return {
|
|
2190
|
-
message: `Current mode: ${underlying.getPermissionMode()}`,
|
|
2191
|
-
success: true,
|
|
2192
|
-
data: { mode: underlying.getPermissionMode() }
|
|
2193
|
-
};
|
|
2194
|
-
}
|
|
2195
|
-
if (VALID_MODES.includes(arg)) {
|
|
2196
|
-
underlying.setPermissionMode(arg);
|
|
2197
|
-
return {
|
|
2198
|
-
message: `Permission mode set to: ${arg}`,
|
|
2199
|
-
success: true,
|
|
2200
|
-
data: { mode: arg }
|
|
2201
|
-
};
|
|
2202
|
-
}
|
|
2203
|
-
return {
|
|
2204
|
-
message: `Invalid mode. Valid: ${VALID_MODES.join(" | ")}`,
|
|
2205
|
-
success: false
|
|
2206
|
-
};
|
|
2207
|
-
}
|
|
2208
|
-
},
|
|
2209
|
-
{
|
|
2210
|
-
name: "model",
|
|
2211
|
-
description: "Change AI model",
|
|
2212
|
-
execute: (_session, args) => {
|
|
2213
|
-
const modelId = args.trim().split(/\s+/)[0];
|
|
2214
|
-
if (!modelId) {
|
|
2215
|
-
return { message: "Usage: model <model-id>", success: false };
|
|
2216
|
-
}
|
|
2217
|
-
return {
|
|
2218
|
-
message: `Model change requested: ${modelId}`,
|
|
2219
|
-
success: true,
|
|
2220
|
-
data: { modelId }
|
|
2221
|
-
};
|
|
2222
|
-
}
|
|
2223
|
-
},
|
|
2224
|
-
{
|
|
2225
|
-
name: "language",
|
|
2226
|
-
description: "Set response language",
|
|
2227
|
-
execute: (_session, args) => {
|
|
2228
|
-
const lang = args.trim().split(/\s+/)[0];
|
|
2229
|
-
if (!lang) {
|
|
2230
|
-
return { message: "Usage: language <code> (e.g., ko, en, ja, zh)", success: false };
|
|
2231
|
-
}
|
|
2232
|
-
return {
|
|
2233
|
-
message: `Language set to "${lang}".`,
|
|
2234
|
-
success: true,
|
|
2235
|
-
data: { language: lang }
|
|
2236
|
-
};
|
|
2237
|
-
}
|
|
2238
|
-
},
|
|
2239
|
-
{
|
|
2240
|
-
name: "cost",
|
|
2241
|
-
description: "Show session info",
|
|
2242
|
-
execute: (session, _args) => {
|
|
2243
|
-
const underlying = session.getSession();
|
|
2244
|
-
const sessionId = underlying.getSessionId();
|
|
2245
|
-
const messageCount = underlying.getMessageCount();
|
|
2246
|
-
return {
|
|
2247
|
-
message: `Session: ${sessionId}
|
|
2248
|
-
Messages: ${messageCount}`,
|
|
2249
|
-
success: true,
|
|
2250
|
-
data: { sessionId, messageCount }
|
|
2251
|
-
};
|
|
2252
|
-
}
|
|
2253
|
-
},
|
|
2254
|
-
{
|
|
2255
|
-
name: "context",
|
|
2256
|
-
description: "Context window info",
|
|
2257
|
-
execute: (session, _args) => {
|
|
2258
|
-
const ctx = session.getContextState();
|
|
2259
|
-
return {
|
|
2260
|
-
message: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`,
|
|
2261
|
-
success: true,
|
|
2262
|
-
data: {
|
|
2263
|
-
usedTokens: ctx.usedTokens,
|
|
2264
|
-
maxTokens: ctx.maxTokens,
|
|
2265
|
-
percentage: ctx.usedPercentage
|
|
2266
|
-
}
|
|
2267
|
-
};
|
|
2268
|
-
}
|
|
2269
|
-
},
|
|
2270
|
-
{
|
|
2271
|
-
name: "permissions",
|
|
2272
|
-
description: "Show permission rules",
|
|
2273
|
-
execute: (session, _args) => {
|
|
2274
|
-
const underlying = session.getSession();
|
|
2275
|
-
const mode = underlying.getPermissionMode();
|
|
2276
|
-
const sessionAllowed = underlying.getSessionAllowedTools();
|
|
2277
|
-
const lines = [`Permission mode: ${mode}`];
|
|
2278
|
-
if (sessionAllowed.length > 0) {
|
|
2279
|
-
lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
|
|
2280
|
-
} else {
|
|
2281
|
-
lines.push("No session-approved tools.");
|
|
2282
|
-
}
|
|
2283
|
-
return {
|
|
2284
|
-
message: lines.join("\n"),
|
|
2285
|
-
success: true,
|
|
2286
|
-
data: { mode, sessionAllowed }
|
|
2287
|
-
};
|
|
2288
|
-
}
|
|
2289
|
-
},
|
|
2290
|
-
{
|
|
2291
|
-
name: "reset",
|
|
2292
|
-
description: "Delete settings",
|
|
2293
|
-
execute: (_session, _args) => {
|
|
2294
|
-
return {
|
|
2295
|
-
message: "Reset requested.",
|
|
2296
|
-
success: true,
|
|
2297
|
-
data: { resetRequested: true }
|
|
2298
|
-
};
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
];
|
|
2302
|
-
}
|
|
2303
|
-
var SystemCommandExecutor = class {
|
|
2304
|
-
commands;
|
|
2305
|
-
constructor(commands) {
|
|
2306
|
-
this.commands = /* @__PURE__ */ new Map();
|
|
2307
|
-
for (const cmd of commands ?? createSystemCommands()) {
|
|
2308
|
-
this.commands.set(cmd.name, cmd);
|
|
2309
|
-
}
|
|
2310
|
-
}
|
|
2311
|
-
/** Register an additional command. */
|
|
2312
|
-
register(command) {
|
|
2313
|
-
this.commands.set(command.name, command);
|
|
2314
|
-
}
|
|
2315
|
-
/** Execute a command by name. Returns null if command not found. */
|
|
2316
|
-
async execute(name, session, args) {
|
|
2317
|
-
const cmd = this.commands.get(name);
|
|
2318
|
-
if (!cmd) return null;
|
|
2319
|
-
return await cmd.execute(session, args);
|
|
2320
|
-
}
|
|
2321
|
-
/** List all registered commands. */
|
|
2322
|
-
listCommands() {
|
|
2323
|
-
return [...this.commands.values()];
|
|
2324
|
-
}
|
|
2325
|
-
/** Check if a command exists. */
|
|
2326
|
-
hasCommand(name) {
|
|
2327
|
-
return this.commands.has(name);
|
|
2328
|
-
}
|
|
2329
|
-
};
|
|
2330
|
-
|
|
2331
|
-
// src/interactive/interactive-session.ts
|
|
2332
|
-
import { FileSessionLogger as FileSessionLogger2 } from "@robota-sdk/agent-sessions";
|
|
2333
|
-
import {
|
|
2334
|
-
createUserMessage,
|
|
2335
|
-
createAssistantMessage,
|
|
2336
|
-
createSystemMessage
|
|
2337
|
-
} from "@robota-sdk/agent-core";
|
|
2338
|
-
var TOOL_ARG_DISPLAY_MAX = 80;
|
|
2339
|
-
var TAIL_KEEP = 30;
|
|
2340
|
-
var MAX_COMPLETED_TOOLS = 50;
|
|
2341
|
-
var STREAMING_FLUSH_INTERVAL_MS = 16;
|
|
2342
|
-
var InteractiveSession = class {
|
|
2343
|
-
session;
|
|
2344
|
-
listeners = /* @__PURE__ */ new Map();
|
|
2345
|
-
// Streaming state
|
|
2346
|
-
streamingText = "";
|
|
2347
|
-
flushTimer = null;
|
|
2348
|
-
// Tool state
|
|
2349
|
-
activeTools = [];
|
|
2350
|
-
// Execution state
|
|
2351
|
-
executing = false;
|
|
2352
|
-
pendingPrompt = null;
|
|
2353
|
-
pendingDisplayInput;
|
|
2354
|
-
pendingRawInput;
|
|
2355
|
-
// Display messages (what clients render — not the raw session history)
|
|
2356
|
-
messages = [];
|
|
2357
|
-
constructor(options) {
|
|
2358
|
-
if (options.session) {
|
|
2359
|
-
this.session = options.session;
|
|
2360
|
-
} else {
|
|
2361
|
-
const cwd = options.cwd ?? process.cwd();
|
|
2362
|
-
const paths = projectPaths(cwd);
|
|
2363
|
-
this.session = createSession({
|
|
2364
|
-
config: options.config,
|
|
2365
|
-
context: options.context,
|
|
2366
|
-
projectInfo: options.projectInfo,
|
|
2367
|
-
sessionStore: options.sessionStore,
|
|
2368
|
-
permissionMode: options.permissionMode,
|
|
2369
|
-
maxTurns: options.maxTurns,
|
|
2370
|
-
terminal: NOOP_TERMINAL,
|
|
2371
|
-
sessionLogger: new FileSessionLogger2(paths.logs),
|
|
2372
|
-
permissionHandler: options.permissionHandler,
|
|
2373
|
-
onTextDelta: (delta) => this.handleTextDelta(delta),
|
|
2374
|
-
onToolExecution: (event) => this.handleToolExecution(event)
|
|
2375
|
-
});
|
|
2376
|
-
}
|
|
2377
|
-
}
|
|
2378
|
-
// ── Event system ──────────────────────────────────────────────
|
|
2379
|
-
on(event, handler) {
|
|
2380
|
-
if (!this.listeners.has(event)) {
|
|
2381
|
-
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
2382
|
-
}
|
|
2383
|
-
this.listeners.get(event).add(handler);
|
|
2192
|
+
// ── Event system ──────────────────────────────────────────────
|
|
2193
|
+
on(event, handler) {
|
|
2194
|
+
if (!this.listeners.has(event)) {
|
|
2195
|
+
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
2196
|
+
}
|
|
2197
|
+
this.listeners.get(event).add(handler);
|
|
2384
2198
|
}
|
|
2385
2199
|
off(event, handler) {
|
|
2386
2200
|
this.listeners.get(event)?.delete(handler);
|
|
@@ -2394,10 +2208,9 @@ var InteractiveSession = class {
|
|
|
2394
2208
|
}
|
|
2395
2209
|
}
|
|
2396
2210
|
// ── Public API ────────────────────────────────────────────────
|
|
2397
|
-
/** Submit a prompt. Queues if already executing (max 1 queued).
|
|
2398
|
-
* displayInput overrides what appears as the user message (e.g., "/audit" instead of full skill prompt).
|
|
2399
|
-
* rawInput is passed to Session.run() for hook matching (e.g., "/rulebased-harness:audit"). */
|
|
2211
|
+
/** Submit a prompt. Queues if already executing (max 1 queued). */
|
|
2400
2212
|
async submit(input, displayInput, rawInput) {
|
|
2213
|
+
await this.ensureInitialized();
|
|
2401
2214
|
if (this.executing) {
|
|
2402
2215
|
this.pendingPrompt = input;
|
|
2403
2216
|
this.pendingDisplayInput = displayInput;
|
|
@@ -2406,14 +2219,30 @@ var InteractiveSession = class {
|
|
|
2406
2219
|
}
|
|
2407
2220
|
await this.executePrompt(input, displayInput, rawInput);
|
|
2408
2221
|
}
|
|
2409
|
-
/**
|
|
2410
|
-
|
|
2411
|
-
this.
|
|
2412
|
-
this.
|
|
2222
|
+
/** Execute a system command by name. Returns null if not found. */
|
|
2223
|
+
async executeCommand(name, args) {
|
|
2224
|
+
await this.ensureInitialized();
|
|
2225
|
+
return this.commandExecutor.execute(name, this, args);
|
|
2226
|
+
}
|
|
2227
|
+
/** List all registered system commands. */
|
|
2228
|
+
listCommands() {
|
|
2229
|
+
return this.commandExecutor.listCommands().map((cmd) => ({
|
|
2230
|
+
name: cmd.name,
|
|
2231
|
+
description: cmd.description
|
|
2232
|
+
}));
|
|
2233
|
+
}
|
|
2234
|
+
/** Abort current execution and clear queue. */
|
|
2235
|
+
abort() {
|
|
2236
|
+
this.pendingPrompt = null;
|
|
2237
|
+
this.pendingDisplayInput = void 0;
|
|
2238
|
+
this.pendingRawInput = void 0;
|
|
2239
|
+
this.session?.abort();
|
|
2413
2240
|
}
|
|
2414
2241
|
/** Cancel queued prompt without aborting current execution. */
|
|
2415
2242
|
cancelQueue() {
|
|
2416
2243
|
this.pendingPrompt = null;
|
|
2244
|
+
this.pendingDisplayInput = void 0;
|
|
2245
|
+
this.pendingRawInput = void 0;
|
|
2417
2246
|
}
|
|
2418
2247
|
isExecuting() {
|
|
2419
2248
|
return this.executing;
|
|
@@ -2421,8 +2250,13 @@ var InteractiveSession = class {
|
|
|
2421
2250
|
getPendingPrompt() {
|
|
2422
2251
|
return this.pendingPrompt;
|
|
2423
2252
|
}
|
|
2253
|
+
/** Get full history timeline (chat + events) for TUI rendering */
|
|
2254
|
+
getFullHistory() {
|
|
2255
|
+
return this.history;
|
|
2256
|
+
}
|
|
2257
|
+
/** Get chat messages only (backward compatible) */
|
|
2424
2258
|
getMessages() {
|
|
2425
|
-
return this.
|
|
2259
|
+
return this.history.filter((e) => e.category === "chat").map((e) => e.data);
|
|
2426
2260
|
}
|
|
2427
2261
|
getStreamingText() {
|
|
2428
2262
|
return this.streamingText;
|
|
@@ -2431,39 +2265,44 @@ var InteractiveSession = class {
|
|
|
2431
2265
|
return this.activeTools;
|
|
2432
2266
|
}
|
|
2433
2267
|
getContextState() {
|
|
2434
|
-
return this.
|
|
2268
|
+
return this.getSessionOrThrow().getContextState();
|
|
2435
2269
|
}
|
|
2270
|
+
/** Access underlying Session. For advanced use / testing only. */
|
|
2436
2271
|
getSession() {
|
|
2437
|
-
return this.
|
|
2272
|
+
return this.getSessionOrThrow();
|
|
2438
2273
|
}
|
|
2439
2274
|
// ── Execution ─────────────────────────────────────────────────
|
|
2440
2275
|
async executePrompt(input, displayInput, rawInput) {
|
|
2441
2276
|
this.executing = true;
|
|
2442
2277
|
this.clearStreaming();
|
|
2443
2278
|
this.emit("thinking", true);
|
|
2444
|
-
this.
|
|
2445
|
-
const historyBefore = this.
|
|
2279
|
+
this.history.push(messageToHistoryEntry(createUserMessage(displayInput ?? input)));
|
|
2280
|
+
const historyBefore = this.getSessionOrThrow().getHistory().length;
|
|
2446
2281
|
try {
|
|
2447
|
-
const response = await this.
|
|
2282
|
+
const response = await this.getSessionOrThrow().run(input, rawInput);
|
|
2448
2283
|
this.flushStreaming();
|
|
2284
|
+
this.pushToolSummaryMessage();
|
|
2449
2285
|
this.clearStreaming();
|
|
2450
2286
|
const result = this.buildResult(response || "(empty response)", historyBefore);
|
|
2451
|
-
this.
|
|
2287
|
+
this.history.push(messageToHistoryEntry(createAssistantMessage(result.response)));
|
|
2452
2288
|
this.emit("complete", result);
|
|
2453
2289
|
this.emit("context_update", this.getContextState());
|
|
2454
2290
|
} catch (err) {
|
|
2455
2291
|
this.flushStreaming();
|
|
2456
|
-
this.clearStreaming();
|
|
2457
2292
|
if (isAbortError(err)) {
|
|
2458
2293
|
const result = this.buildInterruptedResult(historyBefore);
|
|
2294
|
+
this.pushToolSummaryMessage();
|
|
2295
|
+
this.clearStreaming();
|
|
2459
2296
|
if (result.response) {
|
|
2460
|
-
this.
|
|
2297
|
+
this.history.push(messageToHistoryEntry(createAssistantMessage(result.response)));
|
|
2461
2298
|
}
|
|
2462
|
-
this.
|
|
2299
|
+
this.history.push(messageToHistoryEntry(createSystemMessage("Interrupted by user.")));
|
|
2463
2300
|
this.emit("interrupted", result);
|
|
2464
2301
|
} else {
|
|
2302
|
+
this.pushToolSummaryMessage();
|
|
2303
|
+
this.clearStreaming();
|
|
2465
2304
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2466
|
-
this.
|
|
2305
|
+
this.history.push(messageToHistoryEntry(createSystemMessage(`Error: ${errMsg}`)));
|
|
2467
2306
|
this.emit("error", err instanceof Error ? err : new Error(errMsg));
|
|
2468
2307
|
}
|
|
2469
2308
|
} finally {
|
|
@@ -2493,22 +2332,14 @@ var InteractiveSession = class {
|
|
|
2493
2332
|
handleToolExecution(event) {
|
|
2494
2333
|
if (event.type === "start") {
|
|
2495
2334
|
const firstArg = extractFirstArg(event.toolArgs);
|
|
2496
|
-
const state = {
|
|
2497
|
-
toolName: event.toolName,
|
|
2498
|
-
firstArg,
|
|
2499
|
-
isRunning: true
|
|
2500
|
-
};
|
|
2335
|
+
const state = { toolName: event.toolName, firstArg, isRunning: true };
|
|
2501
2336
|
this.activeTools.push(state);
|
|
2502
2337
|
this.emit("tool_start", state);
|
|
2503
2338
|
} else {
|
|
2504
2339
|
const result = event.denied ? "denied" : event.success === false ? "error" : "success";
|
|
2505
2340
|
const idx = this.activeTools.findIndex((t) => t.toolName === event.toolName && t.isRunning);
|
|
2506
2341
|
if (idx !== -1) {
|
|
2507
|
-
const finished = {
|
|
2508
|
-
...this.activeTools[idx],
|
|
2509
|
-
isRunning: false,
|
|
2510
|
-
result
|
|
2511
|
-
};
|
|
2342
|
+
const finished = { ...this.activeTools[idx], isRunning: false, result };
|
|
2512
2343
|
this.activeTools[idx] = finished;
|
|
2513
2344
|
this.trimCompletedTools();
|
|
2514
2345
|
this.emit("tool_end", finished);
|
|
@@ -2516,6 +2347,31 @@ var InteractiveSession = class {
|
|
|
2516
2347
|
}
|
|
2517
2348
|
}
|
|
2518
2349
|
// ── Helpers ───────────────────────────────────────────────────
|
|
2350
|
+
/** Push tool execution summary into messages (before Robota response).
|
|
2351
|
+
* Moves tool info from activeTools (real-time display) to messages (permanent display).
|
|
2352
|
+
* After this, activeTools will be cleared by clearStreaming(). */
|
|
2353
|
+
pushToolSummaryMessage() {
|
|
2354
|
+
if (this.activeTools.length === 0) return;
|
|
2355
|
+
const summary = this.activeTools.map((t) => {
|
|
2356
|
+
const status = t.isRunning ? "\u27F3" : t.result === "success" ? "\u2713" : t.result === "error" ? "\u2717" : "\u2298";
|
|
2357
|
+
return `${status} ${t.toolName}${t.firstArg ? `(${t.firstArg})` : ""}`;
|
|
2358
|
+
}).join("\n");
|
|
2359
|
+
this.history.push({
|
|
2360
|
+
id: randomUUID(),
|
|
2361
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
2362
|
+
category: "event",
|
|
2363
|
+
type: "tool-summary",
|
|
2364
|
+
data: {
|
|
2365
|
+
tools: this.activeTools.map((t) => ({
|
|
2366
|
+
toolName: t.toolName,
|
|
2367
|
+
firstArg: t.firstArg,
|
|
2368
|
+
isRunning: t.isRunning,
|
|
2369
|
+
result: t.result
|
|
2370
|
+
})),
|
|
2371
|
+
summary
|
|
2372
|
+
}
|
|
2373
|
+
});
|
|
2374
|
+
}
|
|
2519
2375
|
clearStreaming() {
|
|
2520
2376
|
this.streamingText = "";
|
|
2521
2377
|
this.activeTools = [];
|
|
@@ -2534,30 +2390,28 @@ var InteractiveSession = class {
|
|
|
2534
2390
|
const toolSummaries = this.extractToolSummaries(historyBefore);
|
|
2535
2391
|
return {
|
|
2536
2392
|
response,
|
|
2537
|
-
|
|
2393
|
+
history: this.history,
|
|
2538
2394
|
toolSummaries,
|
|
2539
2395
|
contextState: this.getContextState()
|
|
2540
2396
|
};
|
|
2541
2397
|
}
|
|
2542
2398
|
buildInterruptedResult(historyBefore) {
|
|
2543
|
-
const history = this.
|
|
2399
|
+
const history = this.getSessionOrThrow().getHistory();
|
|
2544
2400
|
const toolSummaries = this.extractToolSummaries(historyBefore);
|
|
2545
2401
|
const parts = [];
|
|
2546
2402
|
for (let i = historyBefore; i < history.length; i++) {
|
|
2547
2403
|
const msg = history[i];
|
|
2548
|
-
if (msg?.role === "assistant" && msg.content)
|
|
2549
|
-
parts.push(msg.content);
|
|
2550
|
-
}
|
|
2404
|
+
if (msg?.role === "assistant" && msg.content) parts.push(msg.content);
|
|
2551
2405
|
}
|
|
2552
2406
|
return {
|
|
2553
2407
|
response: parts.join("\n\n"),
|
|
2554
|
-
|
|
2408
|
+
history: this.history,
|
|
2555
2409
|
toolSummaries,
|
|
2556
2410
|
contextState: this.getContextState()
|
|
2557
2411
|
};
|
|
2558
2412
|
}
|
|
2559
2413
|
extractToolSummaries(historyBefore) {
|
|
2560
|
-
const history = this.
|
|
2414
|
+
const history = this.getSessionOrThrow().getHistory();
|
|
2561
2415
|
const summaries = [];
|
|
2562
2416
|
for (let i = historyBefore; i < history.length; i++) {
|
|
2563
2417
|
const msg = history[i];
|
|
@@ -2609,13 +2463,400 @@ var NOOP_TERMINAL = {
|
|
|
2609
2463
|
} })
|
|
2610
2464
|
};
|
|
2611
2465
|
|
|
2466
|
+
// src/query.ts
|
|
2467
|
+
function createQuery(options) {
|
|
2468
|
+
const session = new InteractiveSession({
|
|
2469
|
+
cwd: options.cwd ?? process.cwd(),
|
|
2470
|
+
provider: options.provider,
|
|
2471
|
+
permissionMode: options.permissionMode ?? "bypassPermissions",
|
|
2472
|
+
maxTurns: options.maxTurns,
|
|
2473
|
+
permissionHandler: options.permissionHandler
|
|
2474
|
+
});
|
|
2475
|
+
if (options.onTextDelta) {
|
|
2476
|
+
session.on("text_delta", options.onTextDelta);
|
|
2477
|
+
}
|
|
2478
|
+
return async (prompt) => {
|
|
2479
|
+
return new Promise((resolve2, reject) => {
|
|
2480
|
+
const onComplete = (result) => {
|
|
2481
|
+
cleanup();
|
|
2482
|
+
resolve2(result.response);
|
|
2483
|
+
};
|
|
2484
|
+
const onInterrupted = (result) => {
|
|
2485
|
+
cleanup();
|
|
2486
|
+
resolve2(result.response);
|
|
2487
|
+
};
|
|
2488
|
+
const onError = (error) => {
|
|
2489
|
+
cleanup();
|
|
2490
|
+
reject(error);
|
|
2491
|
+
};
|
|
2492
|
+
const cleanup = () => {
|
|
2493
|
+
session.off("complete", onComplete);
|
|
2494
|
+
session.off("interrupted", onInterrupted);
|
|
2495
|
+
session.off("error", onError);
|
|
2496
|
+
};
|
|
2497
|
+
session.on("complete", onComplete);
|
|
2498
|
+
session.on("interrupted", onInterrupted);
|
|
2499
|
+
session.on("error", onError);
|
|
2500
|
+
session.submit(prompt).catch((err) => {
|
|
2501
|
+
cleanup();
|
|
2502
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
2503
|
+
});
|
|
2504
|
+
});
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
// src/commands/command-registry.ts
|
|
2509
|
+
var CommandRegistry = class {
|
|
2510
|
+
sources = [];
|
|
2511
|
+
addSource(source) {
|
|
2512
|
+
this.sources.push(source);
|
|
2513
|
+
}
|
|
2514
|
+
/** Get all commands, optionally filtered by prefix */
|
|
2515
|
+
getCommands(filter) {
|
|
2516
|
+
const all = [];
|
|
2517
|
+
for (const source of this.sources) {
|
|
2518
|
+
all.push(...source.getCommands());
|
|
2519
|
+
}
|
|
2520
|
+
if (!filter) return all;
|
|
2521
|
+
const lower = filter.toLowerCase();
|
|
2522
|
+
return all.filter((cmd) => cmd.name.toLowerCase().startsWith(lower));
|
|
2523
|
+
}
|
|
2524
|
+
/** Resolve a short name to its fully qualified plugin:name form */
|
|
2525
|
+
resolveQualifiedName(shortName) {
|
|
2526
|
+
const matches = this.getCommands().filter(
|
|
2527
|
+
(c) => c.source === "plugin" && c.name.includes(":") && c.name.endsWith(`:${shortName}`)
|
|
2528
|
+
);
|
|
2529
|
+
if (matches.length !== 1) return null;
|
|
2530
|
+
return matches[0].name;
|
|
2531
|
+
}
|
|
2532
|
+
/** Get subcommands for a specific command */
|
|
2533
|
+
getSubcommands(commandName) {
|
|
2534
|
+
const lower = commandName.toLowerCase();
|
|
2535
|
+
for (const source of this.sources) {
|
|
2536
|
+
for (const cmd of source.getCommands()) {
|
|
2537
|
+
if (cmd.name.toLowerCase() === lower && cmd.subcommands) {
|
|
2538
|
+
return cmd.subcommands;
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
return [];
|
|
2543
|
+
}
|
|
2544
|
+
};
|
|
2545
|
+
|
|
2546
|
+
// src/commands/builtin-source.ts
|
|
2547
|
+
import { CLAUDE_MODELS, formatTokenCount } from "@robota-sdk/agent-core";
|
|
2548
|
+
function buildModelSubcommands() {
|
|
2549
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2550
|
+
const commands = [];
|
|
2551
|
+
for (const model of Object.values(CLAUDE_MODELS)) {
|
|
2552
|
+
if (seen.has(model.name)) continue;
|
|
2553
|
+
seen.add(model.name);
|
|
2554
|
+
commands.push({
|
|
2555
|
+
name: model.id,
|
|
2556
|
+
description: `${model.name} (${formatTokenCount(model.contextWindow).toUpperCase()})`,
|
|
2557
|
+
source: "builtin"
|
|
2558
|
+
});
|
|
2559
|
+
}
|
|
2560
|
+
return commands;
|
|
2561
|
+
}
|
|
2562
|
+
function createBuiltinCommands() {
|
|
2563
|
+
return [
|
|
2564
|
+
{ name: "help", description: "Show available commands", source: "builtin" },
|
|
2565
|
+
{ name: "clear", description: "Clear conversation history", source: "builtin" },
|
|
2566
|
+
{
|
|
2567
|
+
name: "mode",
|
|
2568
|
+
description: "Permission mode",
|
|
2569
|
+
source: "builtin",
|
|
2570
|
+
subcommands: [
|
|
2571
|
+
{ name: "plan", description: "Plan only, no execution", source: "builtin" },
|
|
2572
|
+
{ name: "default", description: "Ask before risky actions", source: "builtin" },
|
|
2573
|
+
{ name: "acceptEdits", description: "Auto-approve file edits", source: "builtin" },
|
|
2574
|
+
{ name: "bypassPermissions", description: "Skip all permission checks", source: "builtin" }
|
|
2575
|
+
]
|
|
2576
|
+
},
|
|
2577
|
+
{
|
|
2578
|
+
name: "model",
|
|
2579
|
+
description: "Select AI model",
|
|
2580
|
+
source: "builtin",
|
|
2581
|
+
subcommands: buildModelSubcommands()
|
|
2582
|
+
},
|
|
2583
|
+
{
|
|
2584
|
+
name: "language",
|
|
2585
|
+
description: "Set response language",
|
|
2586
|
+
source: "builtin",
|
|
2587
|
+
subcommands: [
|
|
2588
|
+
{ name: "ko", description: "Korean", source: "builtin" },
|
|
2589
|
+
{ name: "en", description: "English", source: "builtin" },
|
|
2590
|
+
{ name: "ja", description: "Japanese", source: "builtin" },
|
|
2591
|
+
{ name: "zh", description: "Chinese", source: "builtin" }
|
|
2592
|
+
]
|
|
2593
|
+
},
|
|
2594
|
+
{ name: "compact", description: "Compress context window", source: "builtin" },
|
|
2595
|
+
{ name: "cost", description: "Show session info", source: "builtin" },
|
|
2596
|
+
{ name: "context", description: "Context window info", source: "builtin" },
|
|
2597
|
+
{ name: "permissions", description: "Permission rules", source: "builtin" },
|
|
2598
|
+
{ name: "plugin", description: "Manage plugins", source: "builtin" },
|
|
2599
|
+
{ name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
|
|
2600
|
+
{ name: "reset", description: "Delete settings and exit", source: "builtin" },
|
|
2601
|
+
{ name: "exit", description: "Exit CLI", source: "builtin" }
|
|
2602
|
+
];
|
|
2603
|
+
}
|
|
2604
|
+
var BuiltinCommandSource = class {
|
|
2605
|
+
name = "builtin";
|
|
2606
|
+
commands;
|
|
2607
|
+
constructor() {
|
|
2608
|
+
this.commands = createBuiltinCommands();
|
|
2609
|
+
}
|
|
2610
|
+
getCommands() {
|
|
2611
|
+
return this.commands;
|
|
2612
|
+
}
|
|
2613
|
+
};
|
|
2614
|
+
|
|
2615
|
+
// src/commands/skill-source.ts
|
|
2616
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
|
|
2617
|
+
import { join as join12, basename as basename2 } from "path";
|
|
2618
|
+
import { homedir as homedir4 } from "os";
|
|
2619
|
+
var BOOLEAN_KEYS = /* @__PURE__ */ new Set(["disable-model-invocation", "user-invocable"]);
|
|
2620
|
+
var LIST_KEYS2 = /* @__PURE__ */ new Set(["allowed-tools"]);
|
|
2621
|
+
function kebabToCamel(key) {
|
|
2622
|
+
return key.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
|
|
2623
|
+
}
|
|
2624
|
+
function parseFrontmatter2(content) {
|
|
2625
|
+
const lines = content.split("\n");
|
|
2626
|
+
if (lines[0]?.trim() !== "---") return null;
|
|
2627
|
+
const result = {};
|
|
2628
|
+
for (let i = 1; i < lines.length; i++) {
|
|
2629
|
+
const line = lines[i];
|
|
2630
|
+
if (line.trim() === "---") break;
|
|
2631
|
+
const match = line.match(/^([a-z][a-z0-9-]*):\s*(.+)/);
|
|
2632
|
+
if (!match) continue;
|
|
2633
|
+
const key = match[1];
|
|
2634
|
+
const rawValue = match[2].trim();
|
|
2635
|
+
const camelKey = kebabToCamel(key);
|
|
2636
|
+
if (BOOLEAN_KEYS.has(key)) {
|
|
2637
|
+
result[camelKey] = rawValue === "true";
|
|
2638
|
+
} else if (LIST_KEYS2.has(key)) {
|
|
2639
|
+
result[camelKey] = rawValue.split(",").map((s) => s.trim());
|
|
2640
|
+
} else {
|
|
2641
|
+
result[camelKey] = rawValue;
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
return Object.keys(result).length > 0 ? result : null;
|
|
2645
|
+
}
|
|
2646
|
+
function buildCommand(frontmatter, content, fallbackName) {
|
|
2647
|
+
const cmd = {
|
|
2648
|
+
name: frontmatter?.name ?? fallbackName,
|
|
2649
|
+
description: frontmatter?.description ?? `Skill: ${fallbackName}`,
|
|
2650
|
+
source: "skill",
|
|
2651
|
+
skillContent: content
|
|
2652
|
+
};
|
|
2653
|
+
if (frontmatter?.argumentHint !== void 0) cmd.argumentHint = frontmatter.argumentHint;
|
|
2654
|
+
if (frontmatter?.disableModelInvocation !== void 0)
|
|
2655
|
+
cmd.disableModelInvocation = frontmatter.disableModelInvocation;
|
|
2656
|
+
if (frontmatter?.userInvocable !== void 0) cmd.userInvocable = frontmatter.userInvocable;
|
|
2657
|
+
if (frontmatter?.allowedTools !== void 0) cmd.allowedTools = frontmatter.allowedTools;
|
|
2658
|
+
if (frontmatter?.model !== void 0) cmd.model = frontmatter.model;
|
|
2659
|
+
if (frontmatter?.effort !== void 0) cmd.effort = frontmatter.effort;
|
|
2660
|
+
if (frontmatter?.context !== void 0) cmd.context = frontmatter.context;
|
|
2661
|
+
if (frontmatter?.agent !== void 0) cmd.agent = frontmatter.agent;
|
|
2662
|
+
return cmd;
|
|
2663
|
+
}
|
|
2664
|
+
function scanSkillsDir(skillsDir) {
|
|
2665
|
+
if (!existsSync9(skillsDir)) return [];
|
|
2666
|
+
const commands = [];
|
|
2667
|
+
const entries = readdirSync3(skillsDir, { withFileTypes: true });
|
|
2668
|
+
for (const entry of entries) {
|
|
2669
|
+
if (!entry.isDirectory()) continue;
|
|
2670
|
+
const skillFile = join12(skillsDir, entry.name, "SKILL.md");
|
|
2671
|
+
if (!existsSync9(skillFile)) continue;
|
|
2672
|
+
const content = readFileSync9(skillFile, "utf-8");
|
|
2673
|
+
const frontmatter = parseFrontmatter2(content);
|
|
2674
|
+
commands.push(buildCommand(frontmatter, content, entry.name));
|
|
2675
|
+
}
|
|
2676
|
+
return commands;
|
|
2677
|
+
}
|
|
2678
|
+
function scanCommandsDir(commandsDir) {
|
|
2679
|
+
if (!existsSync9(commandsDir)) return [];
|
|
2680
|
+
const commands = [];
|
|
2681
|
+
const entries = readdirSync3(commandsDir, { withFileTypes: true });
|
|
2682
|
+
for (const entry of entries) {
|
|
2683
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
2684
|
+
const filePath = join12(commandsDir, entry.name);
|
|
2685
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
2686
|
+
const frontmatter = parseFrontmatter2(content);
|
|
2687
|
+
const fallbackName = basename2(entry.name, ".md");
|
|
2688
|
+
commands.push(buildCommand(frontmatter, content, fallbackName));
|
|
2689
|
+
}
|
|
2690
|
+
return commands;
|
|
2691
|
+
}
|
|
2692
|
+
var SkillCommandSource = class {
|
|
2693
|
+
name = "skill";
|
|
2694
|
+
cwd;
|
|
2695
|
+
home;
|
|
2696
|
+
cachedCommands = null;
|
|
2697
|
+
constructor(cwd, home) {
|
|
2698
|
+
this.cwd = cwd;
|
|
2699
|
+
this.home = home ?? homedir4();
|
|
2700
|
+
}
|
|
2701
|
+
getCommands() {
|
|
2702
|
+
if (this.cachedCommands) return this.cachedCommands;
|
|
2703
|
+
const sources = [
|
|
2704
|
+
scanSkillsDir(join12(this.cwd, ".claude", "skills")),
|
|
2705
|
+
scanCommandsDir(join12(this.cwd, ".claude", "commands")),
|
|
2706
|
+
scanSkillsDir(join12(this.home, ".robota", "skills")),
|
|
2707
|
+
scanSkillsDir(join12(this.cwd, ".agents", "skills"))
|
|
2708
|
+
];
|
|
2709
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2710
|
+
const merged = [];
|
|
2711
|
+
for (const commands of sources) {
|
|
2712
|
+
for (const cmd of commands) {
|
|
2713
|
+
if (!seen.has(cmd.name)) {
|
|
2714
|
+
seen.add(cmd.name);
|
|
2715
|
+
merged.push(cmd);
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
this.cachedCommands = merged;
|
|
2720
|
+
return this.cachedCommands;
|
|
2721
|
+
}
|
|
2722
|
+
getModelInvocableSkills() {
|
|
2723
|
+
return this.getCommands().filter((cmd) => cmd.disableModelInvocation !== true);
|
|
2724
|
+
}
|
|
2725
|
+
getUserInvocableSkills() {
|
|
2726
|
+
return this.getCommands().filter((cmd) => cmd.userInvocable !== false);
|
|
2727
|
+
}
|
|
2728
|
+
};
|
|
2729
|
+
|
|
2730
|
+
// src/commands/plugin-source.ts
|
|
2731
|
+
var PluginCommandSource = class {
|
|
2732
|
+
name = "plugin";
|
|
2733
|
+
plugins;
|
|
2734
|
+
constructor(plugins) {
|
|
2735
|
+
this.plugins = plugins;
|
|
2736
|
+
}
|
|
2737
|
+
getCommands() {
|
|
2738
|
+
const commands = [];
|
|
2739
|
+
for (const plugin of this.plugins) {
|
|
2740
|
+
for (const skill of plugin.skills) {
|
|
2741
|
+
const baseName = skill.name.includes("@") ? skill.name.split("@")[0] : skill.name;
|
|
2742
|
+
commands.push({
|
|
2743
|
+
name: baseName,
|
|
2744
|
+
description: `(${plugin.manifest.name}) ${skill.description}`,
|
|
2745
|
+
source: "plugin",
|
|
2746
|
+
skillContent: skill.skillContent,
|
|
2747
|
+
pluginDir: plugin.pluginDir
|
|
2748
|
+
});
|
|
2749
|
+
}
|
|
2750
|
+
for (const cmd of plugin.commands) {
|
|
2751
|
+
commands.push({
|
|
2752
|
+
name: cmd.name,
|
|
2753
|
+
description: cmd.description,
|
|
2754
|
+
source: "plugin",
|
|
2755
|
+
skillContent: cmd.skillContent,
|
|
2756
|
+
pluginDir: plugin.pluginDir
|
|
2757
|
+
});
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
return commands;
|
|
2761
|
+
}
|
|
2762
|
+
};
|
|
2763
|
+
|
|
2764
|
+
// src/utils/skill-prompt.ts
|
|
2765
|
+
import { execSync as execSync3 } from "child_process";
|
|
2766
|
+
function substituteVariables(content, args, context) {
|
|
2767
|
+
const argParts = args ? args.split(/\s+/) : [];
|
|
2768
|
+
let result = content;
|
|
2769
|
+
result = result.replace(/\$ARGUMENTS\[(\d+)]/g, (_match, index) => {
|
|
2770
|
+
return argParts[Number(index)] ?? "";
|
|
2771
|
+
});
|
|
2772
|
+
result = result.replace(/\$ARGUMENTS/g, args);
|
|
2773
|
+
result = result.replace(/\$(\d)(?!\d|\w|\[)/g, (_match, digit) => {
|
|
2774
|
+
return argParts[Number(digit)] ?? "";
|
|
2775
|
+
});
|
|
2776
|
+
result = result.replace(/\$\{CLAUDE_SESSION_ID}/g, context?.sessionId ?? "");
|
|
2777
|
+
result = result.replace(/\$\{CLAUDE_SKILL_DIR}/g, context?.skillDir ?? "");
|
|
2778
|
+
return result;
|
|
2779
|
+
}
|
|
2780
|
+
async function preprocessShellCommands(content) {
|
|
2781
|
+
const shellPattern = /!`([^`]+)`/g;
|
|
2782
|
+
if (!shellPattern.test(content)) {
|
|
2783
|
+
return content;
|
|
2784
|
+
}
|
|
2785
|
+
shellPattern.lastIndex = 0;
|
|
2786
|
+
let result = content;
|
|
2787
|
+
let match;
|
|
2788
|
+
const matches = [];
|
|
2789
|
+
while ((match = shellPattern.exec(content)) !== null) {
|
|
2790
|
+
matches.push({ full: match[0], command: match[1] });
|
|
2791
|
+
}
|
|
2792
|
+
for (const { full, command } of matches) {
|
|
2793
|
+
let output = "";
|
|
2794
|
+
try {
|
|
2795
|
+
output = execSync3(command, {
|
|
2796
|
+
timeout: 5e3,
|
|
2797
|
+
encoding: "utf-8",
|
|
2798
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2799
|
+
}).trimEnd();
|
|
2800
|
+
} catch {
|
|
2801
|
+
output = "";
|
|
2802
|
+
}
|
|
2803
|
+
result = result.replace(full, output);
|
|
2804
|
+
}
|
|
2805
|
+
return result;
|
|
2806
|
+
}
|
|
2807
|
+
async function buildSkillPrompt(input, registry, context) {
|
|
2808
|
+
const parts = input.slice(1).split(/\s+/);
|
|
2809
|
+
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
2810
|
+
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
2811
|
+
if (!skillCmd) return null;
|
|
2812
|
+
const args = parts.slice(1).join(" ").trim();
|
|
2813
|
+
const userInstruction = args || skillCmd.description;
|
|
2814
|
+
if (skillCmd.skillContent) {
|
|
2815
|
+
let processed = await preprocessShellCommands(skillCmd.skillContent);
|
|
2816
|
+
processed = substituteVariables(processed, args, context);
|
|
2817
|
+
return `<skill name="${cmd}">
|
|
2818
|
+
${processed}
|
|
2819
|
+
</skill>
|
|
2820
|
+
|
|
2821
|
+
Execute the "${cmd}" skill: ${userInstruction}`;
|
|
2822
|
+
}
|
|
2823
|
+
return `Use the "${cmd}" skill: ${userInstruction}`;
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
// src/types.ts
|
|
2827
|
+
import { TRUST_TO_MODE } from "@robota-sdk/agent-core";
|
|
2828
|
+
|
|
2829
|
+
// src/index.ts
|
|
2830
|
+
import {
|
|
2831
|
+
isChatEntry,
|
|
2832
|
+
chatEntryToMessage,
|
|
2833
|
+
messageToHistoryEntry as messageToHistoryEntry2,
|
|
2834
|
+
getMessagesForAPI
|
|
2835
|
+
} from "@robota-sdk/agent-core";
|
|
2836
|
+
import { evaluatePermission } from "@robota-sdk/agent-core";
|
|
2837
|
+
|
|
2838
|
+
// src/permissions/permission-prompt.ts
|
|
2839
|
+
import chalk from "chalk";
|
|
2840
|
+
var PERMISSION_OPTIONS = ["Allow", "Deny"];
|
|
2841
|
+
var ALLOW_INDEX = 0;
|
|
2842
|
+
function formatArgs(toolArgs) {
|
|
2843
|
+
const entries = Object.entries(toolArgs);
|
|
2844
|
+
if (entries.length === 0) {
|
|
2845
|
+
return "(no arguments)";
|
|
2846
|
+
}
|
|
2847
|
+
return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
2848
|
+
}
|
|
2849
|
+
async function promptForApproval(terminal, toolName, toolArgs) {
|
|
2850
|
+
terminal.writeLine("");
|
|
2851
|
+
terminal.writeLine(chalk.yellow(`[Permission Required] Tool: ${toolName}`));
|
|
2852
|
+
terminal.writeLine(chalk.dim(` ${formatArgs(toolArgs)}`));
|
|
2853
|
+
terminal.writeLine("");
|
|
2854
|
+
const selected = await terminal.select(PERMISSION_OPTIONS, ALLOW_INDEX);
|
|
2855
|
+
return selected === ALLOW_INDEX;
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2612
2858
|
// src/index.ts
|
|
2613
|
-
import {
|
|
2614
|
-
import { readTool as readTool2 } from "@robota-sdk/agent-tools";
|
|
2615
|
-
import { writeTool as writeTool2 } from "@robota-sdk/agent-tools";
|
|
2616
|
-
import { editTool as editTool2 } from "@robota-sdk/agent-tools";
|
|
2617
|
-
import { globTool as globTool2 } from "@robota-sdk/agent-tools";
|
|
2618
|
-
import { grepTool as grepTool2 } from "@robota-sdk/agent-tools";
|
|
2859
|
+
import { runHooks } from "@robota-sdk/agent-core";
|
|
2619
2860
|
export {
|
|
2620
2861
|
AgentExecutor,
|
|
2621
2862
|
BUILT_IN_AGENTS,
|
|
@@ -2623,47 +2864,35 @@ export {
|
|
|
2623
2864
|
BundlePluginInstaller,
|
|
2624
2865
|
BundlePluginLoader,
|
|
2625
2866
|
CommandRegistry,
|
|
2626
|
-
DEFAULT_TOOL_DESCRIPTIONS,
|
|
2627
|
-
FileSessionLogger3 as FileSessionLogger,
|
|
2628
2867
|
InteractiveSession,
|
|
2629
2868
|
MarketplaceClient,
|
|
2869
|
+
PluginCommandSource,
|
|
2630
2870
|
PluginSettingsStore,
|
|
2631
2871
|
PromptExecutor,
|
|
2632
|
-
Session3 as Session,
|
|
2633
|
-
SessionStore,
|
|
2634
|
-
SilentSessionLogger,
|
|
2635
2872
|
SkillCommandSource,
|
|
2636
|
-
SystemCommandExecutor,
|
|
2637
2873
|
TRUST_TO_MODE,
|
|
2638
2874
|
assembleSubagentPrompt,
|
|
2639
|
-
|
|
2640
|
-
|
|
2875
|
+
buildSkillPrompt,
|
|
2876
|
+
chatEntryToMessage,
|
|
2641
2877
|
createAgentTool,
|
|
2642
|
-
|
|
2643
|
-
createProvider,
|
|
2644
|
-
createSession,
|
|
2878
|
+
createQuery,
|
|
2645
2879
|
createSubagentLogger,
|
|
2646
2880
|
createSubagentSession,
|
|
2647
|
-
createSystemCommands,
|
|
2648
|
-
detectProject,
|
|
2649
|
-
editTool2 as editTool,
|
|
2650
2881
|
evaluatePermission,
|
|
2651
2882
|
getBuiltInAgent,
|
|
2652
2883
|
getForkWorkerSuffix,
|
|
2884
|
+
getMessagesForAPI,
|
|
2653
2885
|
getSubagentSuffix,
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
loadConfig,
|
|
2657
|
-
loadContext,
|
|
2886
|
+
isChatEntry,
|
|
2887
|
+
messageToHistoryEntry2 as messageToHistoryEntry,
|
|
2658
2888
|
parseFrontmatter2 as parseFrontmatter,
|
|
2889
|
+
preprocessShellCommands,
|
|
2659
2890
|
projectPaths,
|
|
2660
2891
|
promptForApproval,
|
|
2661
|
-
query,
|
|
2662
|
-
readTool2 as readTool,
|
|
2663
2892
|
resolveSubagentLogDir,
|
|
2664
2893
|
retrieveAgentToolDeps,
|
|
2665
2894
|
runHooks,
|
|
2666
2895
|
storeAgentToolDeps,
|
|
2667
|
-
|
|
2668
|
-
|
|
2896
|
+
substituteVariables,
|
|
2897
|
+
userPaths
|
|
2669
2898
|
};
|