@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.cjs
CHANGED
|
@@ -36,55 +36,40 @@ __export(index_exports, {
|
|
|
36
36
|
BundlePluginInstaller: () => BundlePluginInstaller,
|
|
37
37
|
BundlePluginLoader: () => BundlePluginLoader,
|
|
38
38
|
CommandRegistry: () => CommandRegistry,
|
|
39
|
-
DEFAULT_TOOL_DESCRIPTIONS: () => DEFAULT_TOOL_DESCRIPTIONS,
|
|
40
|
-
FileSessionLogger: () => import_agent_sessions6.FileSessionLogger,
|
|
41
39
|
InteractiveSession: () => InteractiveSession,
|
|
42
40
|
MarketplaceClient: () => MarketplaceClient,
|
|
41
|
+
PluginCommandSource: () => PluginCommandSource,
|
|
43
42
|
PluginSettingsStore: () => PluginSettingsStore,
|
|
44
43
|
PromptExecutor: () => PromptExecutor,
|
|
45
|
-
Session: () => import_agent_sessions5.Session,
|
|
46
|
-
SessionStore: () => import_agent_sessions7.SessionStore,
|
|
47
|
-
SilentSessionLogger: () => import_agent_sessions6.SilentSessionLogger,
|
|
48
44
|
SkillCommandSource: () => SkillCommandSource,
|
|
49
|
-
|
|
50
|
-
TRUST_TO_MODE: () => import_agent_core.TRUST_TO_MODE,
|
|
45
|
+
TRUST_TO_MODE: () => import_agent_core3.TRUST_TO_MODE,
|
|
51
46
|
assembleSubagentPrompt: () => assembleSubagentPrompt,
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
buildSkillPrompt: () => buildSkillPrompt,
|
|
48
|
+
chatEntryToMessage: () => import_agent_core4.chatEntryToMessage,
|
|
54
49
|
createAgentTool: () => createAgentTool,
|
|
55
|
-
|
|
56
|
-
createProvider: () => createProvider,
|
|
57
|
-
createSession: () => createSession,
|
|
50
|
+
createQuery: () => createQuery,
|
|
58
51
|
createSubagentLogger: () => createSubagentLogger,
|
|
59
52
|
createSubagentSession: () => createSubagentSession,
|
|
60
|
-
|
|
61
|
-
detectProject: () => detectProject,
|
|
62
|
-
editTool: () => import_agent_tools6.editTool,
|
|
63
|
-
evaluatePermission: () => import_agent_core4.evaluatePermission,
|
|
53
|
+
evaluatePermission: () => import_agent_core5.evaluatePermission,
|
|
64
54
|
getBuiltInAgent: () => getBuiltInAgent,
|
|
65
55
|
getForkWorkerSuffix: () => getForkWorkerSuffix,
|
|
56
|
+
getMessagesForAPI: () => import_agent_core4.getMessagesForAPI,
|
|
66
57
|
getSubagentSuffix: () => getSubagentSuffix,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
loadConfig: () => loadConfig,
|
|
70
|
-
loadContext: () => loadContext,
|
|
58
|
+
isChatEntry: () => import_agent_core4.isChatEntry,
|
|
59
|
+
messageToHistoryEntry: () => import_agent_core4.messageToHistoryEntry,
|
|
71
60
|
parseFrontmatter: () => parseFrontmatter2,
|
|
61
|
+
preprocessShellCommands: () => preprocessShellCommands,
|
|
72
62
|
projectPaths: () => projectPaths,
|
|
73
63
|
promptForApproval: () => promptForApproval,
|
|
74
|
-
query: () => query,
|
|
75
|
-
readTool: () => import_agent_tools4.readTool,
|
|
76
64
|
resolveSubagentLogDir: () => resolveSubagentLogDir,
|
|
77
65
|
retrieveAgentToolDeps: () => retrieveAgentToolDeps,
|
|
78
|
-
runHooks: () =>
|
|
66
|
+
runHooks: () => import_agent_core6.runHooks,
|
|
79
67
|
storeAgentToolDeps: () => storeAgentToolDeps,
|
|
80
|
-
|
|
81
|
-
|
|
68
|
+
substituteVariables: () => substituteVariables,
|
|
69
|
+
userPaths: () => userPaths
|
|
82
70
|
});
|
|
83
71
|
module.exports = __toCommonJS(index_exports);
|
|
84
72
|
|
|
85
|
-
// src/types.ts
|
|
86
|
-
var import_agent_core = require("@robota-sdk/agent-core");
|
|
87
|
-
|
|
88
73
|
// src/hooks/prompt-executor.ts
|
|
89
74
|
function extractJson(raw) {
|
|
90
75
|
const codeBlockMatch = /```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/.exec(raw);
|
|
@@ -314,18 +299,6 @@ function createDefaultTools() {
|
|
|
314
299
|
];
|
|
315
300
|
}
|
|
316
301
|
|
|
317
|
-
// src/assembly/create-provider.ts
|
|
318
|
-
var import_agent_provider_anthropic = require("@robota-sdk/agent-provider-anthropic");
|
|
319
|
-
function createProvider(config) {
|
|
320
|
-
const apiKey = config.provider.apiKey ?? process.env["ANTHROPIC_API_KEY"];
|
|
321
|
-
if (!apiKey) {
|
|
322
|
-
throw new Error(
|
|
323
|
-
"ANTHROPIC_API_KEY is not set. Set the environment variable or configure provider.apiKey in ~/.robota/settings.json"
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
return new import_agent_provider_anthropic.AnthropicProvider({ apiKey });
|
|
327
|
-
}
|
|
328
|
-
|
|
329
302
|
// src/tools/agent-tool.ts
|
|
330
303
|
var import_zod = require("zod");
|
|
331
304
|
var import_agent_tools2 = require("@robota-sdk/agent-tools");
|
|
@@ -455,7 +428,7 @@ function createSubagentSession(options) {
|
|
|
455
428
|
agentsMd: parentContext.agentsMd,
|
|
456
429
|
isForkWorker: options.isForkWorker ?? false
|
|
457
430
|
});
|
|
458
|
-
const provider =
|
|
431
|
+
const provider = options.provider;
|
|
459
432
|
return new import_agent_sessions.Session({
|
|
460
433
|
tools,
|
|
461
434
|
provider,
|
|
@@ -516,6 +489,7 @@ function createAgentTool(deps) {
|
|
|
516
489
|
parentConfig: deps.config,
|
|
517
490
|
parentContext: deps.context,
|
|
518
491
|
parentTools: deps.tools,
|
|
492
|
+
provider: deps.provider,
|
|
519
493
|
terminal: deps.terminal,
|
|
520
494
|
permissionMode: deps.permissionMode,
|
|
521
495
|
permissionHandler: deps.permissionHandler,
|
|
@@ -662,7 +636,12 @@ var AgentDefinitionLoader = class {
|
|
|
662
636
|
|
|
663
637
|
// src/assembly/create-session.ts
|
|
664
638
|
function createSession(options) {
|
|
665
|
-
|
|
639
|
+
if (!options.provider) {
|
|
640
|
+
throw new Error(
|
|
641
|
+
"provider is required. SDK is provider-neutral \u2014 consumer must create and pass a provider instance."
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
const provider = options.provider;
|
|
666
645
|
const defaultTools = createDefaultTools();
|
|
667
646
|
const tools = [...defaultTools, ...options.additionalTools ?? []];
|
|
668
647
|
const agentLoader = new AgentDefinitionLoader(process.cwd());
|
|
@@ -690,6 +669,7 @@ function createSession(options) {
|
|
|
690
669
|
context: options.context,
|
|
691
670
|
tools,
|
|
692
671
|
terminal: options.terminal,
|
|
672
|
+
provider,
|
|
693
673
|
permissionMode: options.permissionMode,
|
|
694
674
|
permissionHandler: options.permissionHandler,
|
|
695
675
|
hooks: options.config.hooks,
|
|
@@ -759,10 +739,28 @@ function resolveSubagentLogDir(parentSessionId, baseLogsDir) {
|
|
|
759
739
|
return (0, import_node_path2.join)(baseLogsDir, parentSessionId, "subagents");
|
|
760
740
|
}
|
|
761
741
|
|
|
762
|
-
// src/
|
|
763
|
-
var
|
|
764
|
-
|
|
765
|
-
|
|
742
|
+
// src/interactive/interactive-session.ts
|
|
743
|
+
var import_agent_sessions4 = require("@robota-sdk/agent-sessions");
|
|
744
|
+
|
|
745
|
+
// src/paths.ts
|
|
746
|
+
var import_node_path3 = require("path");
|
|
747
|
+
var import_node_os2 = require("os");
|
|
748
|
+
function projectPaths(cwd) {
|
|
749
|
+
const base = (0, import_node_path3.join)(cwd, ".robota");
|
|
750
|
+
return {
|
|
751
|
+
settings: (0, import_node_path3.join)(base, "settings.json"),
|
|
752
|
+
settingsLocal: (0, import_node_path3.join)(base, "settings.local.json"),
|
|
753
|
+
logs: (0, import_node_path3.join)(base, "logs"),
|
|
754
|
+
sessions: (0, import_node_path3.join)(base, "sessions")
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
function userPaths() {
|
|
758
|
+
const base = (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".robota");
|
|
759
|
+
return {
|
|
760
|
+
settings: (0, import_node_path3.join)(base, "settings.json"),
|
|
761
|
+
sessions: (0, import_node_path3.join)(base, "sessions")
|
|
762
|
+
};
|
|
763
|
+
}
|
|
766
764
|
|
|
767
765
|
// src/config/config-loader.ts
|
|
768
766
|
var import_fs = require("fs");
|
|
@@ -1096,89 +1094,207 @@ async function detectProject(cwd) {
|
|
|
1096
1094
|
};
|
|
1097
1095
|
}
|
|
1098
1096
|
|
|
1099
|
-
// src/
|
|
1100
|
-
var
|
|
1101
|
-
var
|
|
1102
|
-
var ALLOW_INDEX = 0;
|
|
1103
|
-
function formatArgs(toolArgs) {
|
|
1104
|
-
const entries = Object.entries(toolArgs);
|
|
1105
|
-
if (entries.length === 0) {
|
|
1106
|
-
return "(no arguments)";
|
|
1107
|
-
}
|
|
1108
|
-
return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
1109
|
-
}
|
|
1110
|
-
async function promptForApproval(terminal, toolName, toolArgs) {
|
|
1111
|
-
terminal.writeLine("");
|
|
1112
|
-
terminal.writeLine(import_chalk.default.yellow(`[Permission Required] Tool: ${toolName}`));
|
|
1113
|
-
terminal.writeLine(import_chalk.default.dim(` ${formatArgs(toolArgs)}`));
|
|
1114
|
-
terminal.writeLine("");
|
|
1115
|
-
const selected = await terminal.select(PERMISSION_OPTIONS, ALLOW_INDEX);
|
|
1116
|
-
return selected === ALLOW_INDEX;
|
|
1117
|
-
}
|
|
1097
|
+
// src/interactive/interactive-session.ts
|
|
1098
|
+
var import_agent_core = require("@robota-sdk/agent-core");
|
|
1099
|
+
var import_node_crypto = require("crypto");
|
|
1118
1100
|
|
|
1119
|
-
// src/
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1101
|
+
// src/commands/system-command.ts
|
|
1102
|
+
var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
1103
|
+
function createSystemCommands() {
|
|
1104
|
+
return [
|
|
1105
|
+
{
|
|
1106
|
+
name: "help",
|
|
1107
|
+
description: "Show available commands",
|
|
1108
|
+
execute: (_session, _args) => ({
|
|
1109
|
+
message: [
|
|
1110
|
+
"Available commands:",
|
|
1111
|
+
" help \u2014 Show this help",
|
|
1112
|
+
" clear \u2014 Clear conversation",
|
|
1113
|
+
" compact [instr] \u2014 Compact context (optional focus instructions)",
|
|
1114
|
+
" mode [m] \u2014 Show/change permission mode",
|
|
1115
|
+
" model <id> \u2014 Change AI model",
|
|
1116
|
+
" language <code> \u2014 Set response language (ko, en, ja, zh)",
|
|
1117
|
+
" cost \u2014 Show session info",
|
|
1118
|
+
" context \u2014 Context window info",
|
|
1119
|
+
" permissions \u2014 Permission rules",
|
|
1120
|
+
" reset \u2014 Delete settings and exit"
|
|
1121
|
+
].join("\n"),
|
|
1122
|
+
success: true
|
|
1123
|
+
})
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
name: "clear",
|
|
1127
|
+
description: "Clear conversation history",
|
|
1128
|
+
execute: (session, _args) => {
|
|
1129
|
+
const underlying = session.getSession();
|
|
1130
|
+
underlying.clearHistory();
|
|
1131
|
+
return { message: "Conversation cleared.", success: true };
|
|
1132
|
+
}
|
|
1133
|
+
},
|
|
1134
|
+
{
|
|
1135
|
+
name: "compact",
|
|
1136
|
+
description: "Compress context window",
|
|
1137
|
+
execute: async (session, args) => {
|
|
1138
|
+
const underlying = session.getSession();
|
|
1139
|
+
const instructions = args.trim() || void 0;
|
|
1140
|
+
const before = underlying.getContextState().usedPercentage;
|
|
1141
|
+
await underlying.compact(instructions);
|
|
1142
|
+
const after = underlying.getContextState().usedPercentage;
|
|
1143
|
+
return {
|
|
1144
|
+
message: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`,
|
|
1145
|
+
success: true,
|
|
1146
|
+
data: { before, after }
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
},
|
|
1150
|
+
{
|
|
1151
|
+
name: "mode",
|
|
1152
|
+
description: "Show/change permission mode",
|
|
1153
|
+
execute: (session, args) => {
|
|
1154
|
+
const underlying = session.getSession();
|
|
1155
|
+
const arg = args.trim().split(/\s+/)[0];
|
|
1156
|
+
if (!arg) {
|
|
1157
|
+
return {
|
|
1158
|
+
message: `Current mode: ${underlying.getPermissionMode()}`,
|
|
1159
|
+
success: true,
|
|
1160
|
+
data: { mode: underlying.getPermissionMode() }
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
if (VALID_MODES.includes(arg)) {
|
|
1164
|
+
underlying.setPermissionMode(arg);
|
|
1165
|
+
return {
|
|
1166
|
+
message: `Permission mode set to: ${arg}`,
|
|
1167
|
+
success: true,
|
|
1168
|
+
data: { mode: arg }
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
return {
|
|
1172
|
+
message: `Invalid mode. Valid: ${VALID_MODES.join(" | ")}`,
|
|
1173
|
+
success: false
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
name: "model",
|
|
1179
|
+
description: "Change AI model",
|
|
1180
|
+
execute: (_session, args) => {
|
|
1181
|
+
const modelId = args.trim().split(/\s+/)[0];
|
|
1182
|
+
if (!modelId) {
|
|
1183
|
+
return { message: "Usage: model <model-id>", success: false };
|
|
1184
|
+
}
|
|
1185
|
+
return {
|
|
1186
|
+
message: `Model change requested: ${modelId}`,
|
|
1187
|
+
success: true,
|
|
1188
|
+
data: { modelId }
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1129
1191
|
},
|
|
1130
|
-
|
|
1192
|
+
{
|
|
1193
|
+
name: "language",
|
|
1194
|
+
description: "Set response language",
|
|
1195
|
+
execute: (_session, args) => {
|
|
1196
|
+
const lang = args.trim().split(/\s+/)[0];
|
|
1197
|
+
if (!lang) {
|
|
1198
|
+
return { message: "Usage: language <code> (e.g., ko, en, ja, zh)", success: false };
|
|
1199
|
+
}
|
|
1200
|
+
return {
|
|
1201
|
+
message: `Language set to "${lang}".`,
|
|
1202
|
+
success: true,
|
|
1203
|
+
data: { language: lang }
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1131
1206
|
},
|
|
1132
|
-
|
|
1207
|
+
{
|
|
1208
|
+
name: "cost",
|
|
1209
|
+
description: "Show session info",
|
|
1210
|
+
execute: (session, _args) => {
|
|
1211
|
+
const underlying = session.getSession();
|
|
1212
|
+
const sessionId = underlying.getSessionId();
|
|
1213
|
+
const messageCount = underlying.getMessageCount();
|
|
1214
|
+
return {
|
|
1215
|
+
message: `Session: ${sessionId}
|
|
1216
|
+
Messages: ${messageCount}`,
|
|
1217
|
+
success: true,
|
|
1218
|
+
data: { sessionId, messageCount }
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1133
1221
|
},
|
|
1134
|
-
|
|
1222
|
+
{
|
|
1223
|
+
name: "context",
|
|
1224
|
+
description: "Context window info",
|
|
1225
|
+
execute: (session, _args) => {
|
|
1226
|
+
const ctx = session.getContextState();
|
|
1227
|
+
return {
|
|
1228
|
+
message: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`,
|
|
1229
|
+
success: true,
|
|
1230
|
+
data: {
|
|
1231
|
+
usedTokens: ctx.usedTokens,
|
|
1232
|
+
maxTokens: ctx.maxTokens,
|
|
1233
|
+
percentage: ctx.usedPercentage
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1135
1237
|
},
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
return {
|
|
1169
|
-
settings: (0, import_node_path3.join)(base, "settings.json"),
|
|
1170
|
-
settingsLocal: (0, import_node_path3.join)(base, "settings.local.json"),
|
|
1171
|
-
logs: (0, import_node_path3.join)(base, "logs"),
|
|
1172
|
-
sessions: (0, import_node_path3.join)(base, "sessions")
|
|
1173
|
-
};
|
|
1174
|
-
}
|
|
1175
|
-
function userPaths() {
|
|
1176
|
-
const base = (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".robota");
|
|
1177
|
-
return {
|
|
1178
|
-
settings: (0, import_node_path3.join)(base, "settings.json"),
|
|
1179
|
-
sessions: (0, import_node_path3.join)(base, "sessions")
|
|
1180
|
-
};
|
|
1238
|
+
{
|
|
1239
|
+
name: "permissions",
|
|
1240
|
+
description: "Show permission rules",
|
|
1241
|
+
execute: (session, _args) => {
|
|
1242
|
+
const underlying = session.getSession();
|
|
1243
|
+
const mode = underlying.getPermissionMode();
|
|
1244
|
+
const sessionAllowed = underlying.getSessionAllowedTools();
|
|
1245
|
+
const lines = [`Permission mode: ${mode}`];
|
|
1246
|
+
if (sessionAllowed.length > 0) {
|
|
1247
|
+
lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
|
|
1248
|
+
} else {
|
|
1249
|
+
lines.push("No session-approved tools.");
|
|
1250
|
+
}
|
|
1251
|
+
return {
|
|
1252
|
+
message: lines.join("\n"),
|
|
1253
|
+
success: true,
|
|
1254
|
+
data: { mode, sessionAllowed }
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
},
|
|
1258
|
+
{
|
|
1259
|
+
name: "reset",
|
|
1260
|
+
description: "Delete settings",
|
|
1261
|
+
execute: (_session, _args) => {
|
|
1262
|
+
return {
|
|
1263
|
+
message: "Reset requested.",
|
|
1264
|
+
success: true,
|
|
1265
|
+
data: { resetRequested: true }
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
];
|
|
1181
1270
|
}
|
|
1271
|
+
var SystemCommandExecutor = class {
|
|
1272
|
+
commands;
|
|
1273
|
+
constructor(commands) {
|
|
1274
|
+
this.commands = /* @__PURE__ */ new Map();
|
|
1275
|
+
for (const cmd of commands ?? createSystemCommands()) {
|
|
1276
|
+
this.commands.set(cmd.name, cmd);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
/** Register an additional command. */
|
|
1280
|
+
register(command) {
|
|
1281
|
+
this.commands.set(command.name, command);
|
|
1282
|
+
}
|
|
1283
|
+
/** Execute a command by name. Returns null if command not found. */
|
|
1284
|
+
async execute(name, session, args) {
|
|
1285
|
+
const cmd = this.commands.get(name);
|
|
1286
|
+
if (!cmd) return null;
|
|
1287
|
+
return await cmd.execute(session, args);
|
|
1288
|
+
}
|
|
1289
|
+
/** List all registered commands. */
|
|
1290
|
+
listCommands() {
|
|
1291
|
+
return [...this.commands.values()];
|
|
1292
|
+
}
|
|
1293
|
+
/** Check if a command exists. */
|
|
1294
|
+
hasCommand(name) {
|
|
1295
|
+
return this.commands.has(name);
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1182
1298
|
|
|
1183
1299
|
// src/plugins/plugin-settings-store.ts
|
|
1184
1300
|
var import_node_fs3 = require("fs");
|
|
@@ -1975,478 +2091,163 @@ var MarketplaceClient = class {
|
|
|
1975
2091
|
}
|
|
1976
2092
|
};
|
|
1977
2093
|
|
|
1978
|
-
// src/
|
|
1979
|
-
var
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
2094
|
+
// src/plugins/plugin-hooks-merger.ts
|
|
2095
|
+
var import_node_path8 = require("path");
|
|
2096
|
+
function buildPluginEnv(plugin) {
|
|
2097
|
+
const dataDir = (0, import_node_path8.join)((0, import_node_path8.dirname)((0, import_node_path8.dirname)(plugin.pluginDir)), "data", plugin.manifest.name);
|
|
2098
|
+
return {
|
|
2099
|
+
CLAUDE_PLUGIN_ROOT: plugin.pluginDir,
|
|
2100
|
+
CLAUDE_PLUGIN_PATH: plugin.pluginDir,
|
|
2101
|
+
CLAUDE_PLUGIN_DATA: dataDir
|
|
2102
|
+
};
|
|
2103
|
+
}
|
|
2104
|
+
function resolvePluginRoot(group, pluginDir) {
|
|
2105
|
+
if (Array.isArray(group.hooks)) {
|
|
2106
|
+
return {
|
|
2107
|
+
...group,
|
|
2108
|
+
hooks: group.hooks.map((h) => {
|
|
2109
|
+
if (typeof h.command === "string") {
|
|
2110
|
+
return {
|
|
2111
|
+
...h,
|
|
2112
|
+
command: h.command.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginDir)
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
return h;
|
|
2116
|
+
})
|
|
2117
|
+
};
|
|
1983
2118
|
}
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
2119
|
+
return group;
|
|
2120
|
+
}
|
|
2121
|
+
function mergePluginHooks(plugins) {
|
|
2122
|
+
const merged = {};
|
|
2123
|
+
for (const plugin of plugins) {
|
|
2124
|
+
const hooksObj = plugin.hooks;
|
|
2125
|
+
if (!hooksObj) continue;
|
|
2126
|
+
const pluginEnv = buildPluginEnv(plugin);
|
|
2127
|
+
const innerHooks = hooksObj.hooks ?? hooksObj;
|
|
2128
|
+
for (const [event, groups] of Object.entries(innerHooks)) {
|
|
2129
|
+
if (!Array.isArray(groups)) continue;
|
|
2130
|
+
if (!merged[event]) merged[event] = [];
|
|
2131
|
+
const resolved = groups.map((group) => {
|
|
2132
|
+
const r = resolvePluginRoot(group, plugin.pluginDir);
|
|
2133
|
+
r.env = pluginEnv;
|
|
2134
|
+
return r;
|
|
2135
|
+
});
|
|
2136
|
+
merged[event].push(...resolved);
|
|
1989
2137
|
}
|
|
1990
|
-
if (!filter) return all;
|
|
1991
|
-
const lower = filter.toLowerCase();
|
|
1992
|
-
return all.filter((cmd) => cmd.name.toLowerCase().startsWith(lower));
|
|
1993
2138
|
}
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2139
|
+
return merged;
|
|
2140
|
+
}
|
|
2141
|
+
function mergeHooksIntoConfig(configHooks, pluginHooks) {
|
|
2142
|
+
const pluginKeys = Object.keys(pluginHooks);
|
|
2143
|
+
if (pluginKeys.length === 0) return configHooks;
|
|
2144
|
+
const merged = {};
|
|
2145
|
+
for (const [event, groups] of Object.entries(pluginHooks)) {
|
|
2146
|
+
merged[event] = [...groups];
|
|
2001
2147
|
}
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
if (cmd.name.toLowerCase() === lower && cmd.subcommands) {
|
|
2008
|
-
return cmd.subcommands;
|
|
2009
|
-
}
|
|
2010
|
-
}
|
|
2148
|
+
if (configHooks) {
|
|
2149
|
+
for (const [event, groups] of Object.entries(configHooks)) {
|
|
2150
|
+
if (!Array.isArray(groups)) continue;
|
|
2151
|
+
if (!merged[event]) merged[event] = [];
|
|
2152
|
+
merged[event].push(...groups);
|
|
2011
2153
|
}
|
|
2012
|
-
return [];
|
|
2013
|
-
}
|
|
2014
|
-
};
|
|
2015
|
-
|
|
2016
|
-
// src/commands/builtin-source.ts
|
|
2017
|
-
var import_agent_core2 = require("@robota-sdk/agent-core");
|
|
2018
|
-
function buildModelSubcommands() {
|
|
2019
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2020
|
-
const commands = [];
|
|
2021
|
-
for (const model of Object.values(import_agent_core2.CLAUDE_MODELS)) {
|
|
2022
|
-
if (seen.has(model.name)) continue;
|
|
2023
|
-
seen.add(model.name);
|
|
2024
|
-
commands.push({
|
|
2025
|
-
name: model.id,
|
|
2026
|
-
description: `${model.name} (${(0, import_agent_core2.formatTokenCount)(model.contextWindow).toUpperCase()})`,
|
|
2027
|
-
source: "builtin"
|
|
2028
|
-
});
|
|
2029
2154
|
}
|
|
2030
|
-
return
|
|
2031
|
-
}
|
|
2032
|
-
function createBuiltinCommands() {
|
|
2033
|
-
return [
|
|
2034
|
-
{ name: "help", description: "Show available commands", source: "builtin" },
|
|
2035
|
-
{ name: "clear", description: "Clear conversation history", source: "builtin" },
|
|
2036
|
-
{
|
|
2037
|
-
name: "mode",
|
|
2038
|
-
description: "Permission mode",
|
|
2039
|
-
source: "builtin",
|
|
2040
|
-
subcommands: [
|
|
2041
|
-
{ name: "plan", description: "Plan only, no execution", source: "builtin" },
|
|
2042
|
-
{ name: "default", description: "Ask before risky actions", source: "builtin" },
|
|
2043
|
-
{ name: "acceptEdits", description: "Auto-approve file edits", source: "builtin" },
|
|
2044
|
-
{ name: "bypassPermissions", description: "Skip all permission checks", source: "builtin" }
|
|
2045
|
-
]
|
|
2046
|
-
},
|
|
2047
|
-
{
|
|
2048
|
-
name: "model",
|
|
2049
|
-
description: "Select AI model",
|
|
2050
|
-
source: "builtin",
|
|
2051
|
-
subcommands: buildModelSubcommands()
|
|
2052
|
-
},
|
|
2053
|
-
{
|
|
2054
|
-
name: "language",
|
|
2055
|
-
description: "Set response language",
|
|
2056
|
-
source: "builtin",
|
|
2057
|
-
subcommands: [
|
|
2058
|
-
{ name: "ko", description: "Korean", source: "builtin" },
|
|
2059
|
-
{ name: "en", description: "English", source: "builtin" },
|
|
2060
|
-
{ name: "ja", description: "Japanese", source: "builtin" },
|
|
2061
|
-
{ name: "zh", description: "Chinese", source: "builtin" }
|
|
2062
|
-
]
|
|
2063
|
-
},
|
|
2064
|
-
{ name: "compact", description: "Compress context window", source: "builtin" },
|
|
2065
|
-
{ name: "cost", description: "Show session info", source: "builtin" },
|
|
2066
|
-
{ name: "context", description: "Context window info", source: "builtin" },
|
|
2067
|
-
{ name: "permissions", description: "Permission rules", source: "builtin" },
|
|
2068
|
-
{ name: "plugin", description: "Manage plugins", source: "builtin" },
|
|
2069
|
-
{ name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
|
|
2070
|
-
{ name: "reset", description: "Delete settings and exit", source: "builtin" },
|
|
2071
|
-
{ name: "exit", description: "Exit CLI", source: "builtin" }
|
|
2072
|
-
];
|
|
2155
|
+
return merged;
|
|
2073
2156
|
}
|
|
2074
|
-
var BuiltinCommandSource = class {
|
|
2075
|
-
name = "builtin";
|
|
2076
|
-
commands;
|
|
2077
|
-
constructor() {
|
|
2078
|
-
this.commands = createBuiltinCommands();
|
|
2079
|
-
}
|
|
2080
|
-
getCommands() {
|
|
2081
|
-
return this.commands;
|
|
2082
|
-
}
|
|
2083
|
-
};
|
|
2084
2157
|
|
|
2085
|
-
// src/
|
|
2086
|
-
var import_node_fs7 = require("fs");
|
|
2087
|
-
var import_node_path8 = require("path");
|
|
2158
|
+
// src/interactive/interactive-session.ts
|
|
2088
2159
|
var import_node_os3 = require("os");
|
|
2089
|
-
var
|
|
2090
|
-
var
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2160
|
+
var import_node_path9 = require("path");
|
|
2161
|
+
var TOOL_ARG_DISPLAY_MAX = 80;
|
|
2162
|
+
var TAIL_KEEP = 30;
|
|
2163
|
+
var MAX_COMPLETED_TOOLS = 50;
|
|
2164
|
+
var STREAMING_FLUSH_INTERVAL_MS = 16;
|
|
2165
|
+
var InteractiveSession = class {
|
|
2166
|
+
session = null;
|
|
2167
|
+
commandExecutor;
|
|
2168
|
+
listeners = /* @__PURE__ */ new Map();
|
|
2169
|
+
initialized = false;
|
|
2170
|
+
initPromise = null;
|
|
2171
|
+
// Streaming state
|
|
2172
|
+
streamingText = "";
|
|
2173
|
+
flushTimer = null;
|
|
2174
|
+
// Tool state
|
|
2175
|
+
activeTools = [];
|
|
2176
|
+
// Execution state
|
|
2177
|
+
executing = false;
|
|
2178
|
+
pendingPrompt = null;
|
|
2179
|
+
pendingDisplayInput;
|
|
2180
|
+
pendingRawInput;
|
|
2181
|
+
// Full history timeline (chat messages + events)
|
|
2182
|
+
history = [];
|
|
2183
|
+
constructor(options) {
|
|
2184
|
+
this.commandExecutor = new SystemCommandExecutor(createSystemCommands());
|
|
2185
|
+
if ("session" in options && options.session) {
|
|
2186
|
+
this.session = options.session;
|
|
2187
|
+
this.initialized = true;
|
|
2110
2188
|
} else {
|
|
2111
|
-
|
|
2189
|
+
const stdOpts = options;
|
|
2190
|
+
this.initPromise = this.initializeAsync(stdOpts);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
async initializeAsync(options) {
|
|
2194
|
+
const cwd = options.cwd;
|
|
2195
|
+
const [config, context, projectInfo] = await Promise.all([
|
|
2196
|
+
loadConfig(cwd),
|
|
2197
|
+
loadContext(cwd),
|
|
2198
|
+
detectProject(cwd)
|
|
2199
|
+
]);
|
|
2200
|
+
const pluginsDir = (0, import_node_path9.join)((0, import_node_os3.homedir)(), ".robota", "plugins");
|
|
2201
|
+
const pluginLoader = new BundlePluginLoader(pluginsDir);
|
|
2202
|
+
let mergedConfig = config;
|
|
2203
|
+
try {
|
|
2204
|
+
const plugins = pluginLoader.loadPluginsSync();
|
|
2205
|
+
if (plugins.length > 0) {
|
|
2206
|
+
const pluginHooks = mergePluginHooks(plugins);
|
|
2207
|
+
mergedConfig = {
|
|
2208
|
+
...config,
|
|
2209
|
+
hooks: mergeHooksIntoConfig(
|
|
2210
|
+
config.hooks,
|
|
2211
|
+
pluginHooks
|
|
2212
|
+
)
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
} catch {
|
|
2112
2216
|
}
|
|
2217
|
+
const paths = projectPaths(cwd);
|
|
2218
|
+
this.session = createSession({
|
|
2219
|
+
config: mergedConfig,
|
|
2220
|
+
context,
|
|
2221
|
+
projectInfo,
|
|
2222
|
+
permissionMode: options.permissionMode,
|
|
2223
|
+
maxTurns: options.maxTurns,
|
|
2224
|
+
terminal: NOOP_TERMINAL,
|
|
2225
|
+
sessionLogger: new import_agent_sessions4.FileSessionLogger(paths.logs),
|
|
2226
|
+
permissionHandler: options.permissionHandler,
|
|
2227
|
+
provider: options.provider,
|
|
2228
|
+
onTextDelta: (delta) => this.handleTextDelta(delta),
|
|
2229
|
+
onToolExecution: (event) => this.handleToolExecution(event)
|
|
2230
|
+
});
|
|
2231
|
+
this.initialized = true;
|
|
2113
2232
|
}
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
const cmd = {
|
|
2118
|
-
name: frontmatter?.name ?? fallbackName,
|
|
2119
|
-
description: frontmatter?.description ?? `Skill: ${fallbackName}`,
|
|
2120
|
-
source: "skill",
|
|
2121
|
-
skillContent: content
|
|
2122
|
-
};
|
|
2123
|
-
if (frontmatter?.argumentHint !== void 0) cmd.argumentHint = frontmatter.argumentHint;
|
|
2124
|
-
if (frontmatter?.disableModelInvocation !== void 0)
|
|
2125
|
-
cmd.disableModelInvocation = frontmatter.disableModelInvocation;
|
|
2126
|
-
if (frontmatter?.userInvocable !== void 0) cmd.userInvocable = frontmatter.userInvocable;
|
|
2127
|
-
if (frontmatter?.allowedTools !== void 0) cmd.allowedTools = frontmatter.allowedTools;
|
|
2128
|
-
if (frontmatter?.model !== void 0) cmd.model = frontmatter.model;
|
|
2129
|
-
if (frontmatter?.effort !== void 0) cmd.effort = frontmatter.effort;
|
|
2130
|
-
if (frontmatter?.context !== void 0) cmd.context = frontmatter.context;
|
|
2131
|
-
if (frontmatter?.agent !== void 0) cmd.agent = frontmatter.agent;
|
|
2132
|
-
return cmd;
|
|
2133
|
-
}
|
|
2134
|
-
function scanSkillsDir(skillsDir) {
|
|
2135
|
-
if (!(0, import_node_fs7.existsSync)(skillsDir)) return [];
|
|
2136
|
-
const commands = [];
|
|
2137
|
-
const entries = (0, import_node_fs7.readdirSync)(skillsDir, { withFileTypes: true });
|
|
2138
|
-
for (const entry of entries) {
|
|
2139
|
-
if (!entry.isDirectory()) continue;
|
|
2140
|
-
const skillFile = (0, import_node_path8.join)(skillsDir, entry.name, "SKILL.md");
|
|
2141
|
-
if (!(0, import_node_fs7.existsSync)(skillFile)) continue;
|
|
2142
|
-
const content = (0, import_node_fs7.readFileSync)(skillFile, "utf-8");
|
|
2143
|
-
const frontmatter = parseFrontmatter2(content);
|
|
2144
|
-
commands.push(buildCommand(frontmatter, content, entry.name));
|
|
2145
|
-
}
|
|
2146
|
-
return commands;
|
|
2147
|
-
}
|
|
2148
|
-
function scanCommandsDir(commandsDir) {
|
|
2149
|
-
if (!(0, import_node_fs7.existsSync)(commandsDir)) return [];
|
|
2150
|
-
const commands = [];
|
|
2151
|
-
const entries = (0, import_node_fs7.readdirSync)(commandsDir, { withFileTypes: true });
|
|
2152
|
-
for (const entry of entries) {
|
|
2153
|
-
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
2154
|
-
const filePath = (0, import_node_path8.join)(commandsDir, entry.name);
|
|
2155
|
-
const content = (0, import_node_fs7.readFileSync)(filePath, "utf-8");
|
|
2156
|
-
const frontmatter = parseFrontmatter2(content);
|
|
2157
|
-
const fallbackName = (0, import_node_path8.basename)(entry.name, ".md");
|
|
2158
|
-
commands.push(buildCommand(frontmatter, content, fallbackName));
|
|
2233
|
+
async ensureInitialized() {
|
|
2234
|
+
if (this.initialized) return;
|
|
2235
|
+
if (this.initPromise) await this.initPromise;
|
|
2159
2236
|
}
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
cwd;
|
|
2165
|
-
home;
|
|
2166
|
-
cachedCommands = null;
|
|
2167
|
-
constructor(cwd, home) {
|
|
2168
|
-
this.cwd = cwd;
|
|
2169
|
-
this.home = home ?? (0, import_node_os3.homedir)();
|
|
2237
|
+
getSessionOrThrow() {
|
|
2238
|
+
if (!this.session)
|
|
2239
|
+
throw new Error("InteractiveSession not initialized. Call submit() or await initialization.");
|
|
2240
|
+
return this.session;
|
|
2170
2241
|
}
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
scanCommandsDir((0, import_node_path8.join)(this.cwd, ".claude", "commands")),
|
|
2176
|
-
scanSkillsDir((0, import_node_path8.join)(this.home, ".robota", "skills")),
|
|
2177
|
-
scanSkillsDir((0, import_node_path8.join)(this.cwd, ".agents", "skills"))
|
|
2178
|
-
];
|
|
2179
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2180
|
-
const merged = [];
|
|
2181
|
-
for (const commands of sources) {
|
|
2182
|
-
for (const cmd of commands) {
|
|
2183
|
-
if (!seen.has(cmd.name)) {
|
|
2184
|
-
seen.add(cmd.name);
|
|
2185
|
-
merged.push(cmd);
|
|
2186
|
-
}
|
|
2187
|
-
}
|
|
2242
|
+
// ── Event system ──────────────────────────────────────────────
|
|
2243
|
+
on(event, handler) {
|
|
2244
|
+
if (!this.listeners.has(event)) {
|
|
2245
|
+
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
2188
2246
|
}
|
|
2189
|
-
this.
|
|
2190
|
-
return this.cachedCommands;
|
|
2247
|
+
this.listeners.get(event).add(handler);
|
|
2191
2248
|
}
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
}
|
|
2195
|
-
getUserInvocableSkills() {
|
|
2196
|
-
return this.getCommands().filter((cmd) => cmd.userInvocable !== false);
|
|
2197
|
-
}
|
|
2198
|
-
};
|
|
2199
|
-
|
|
2200
|
-
// src/commands/system-command.ts
|
|
2201
|
-
var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
2202
|
-
function createSystemCommands() {
|
|
2203
|
-
return [
|
|
2204
|
-
{
|
|
2205
|
-
name: "help",
|
|
2206
|
-
description: "Show available commands",
|
|
2207
|
-
execute: (_session, _args) => ({
|
|
2208
|
-
message: [
|
|
2209
|
-
"Available commands:",
|
|
2210
|
-
" help \u2014 Show this help",
|
|
2211
|
-
" clear \u2014 Clear conversation",
|
|
2212
|
-
" compact [instr] \u2014 Compact context (optional focus instructions)",
|
|
2213
|
-
" mode [m] \u2014 Show/change permission mode",
|
|
2214
|
-
" model <id> \u2014 Change AI model",
|
|
2215
|
-
" language <code> \u2014 Set response language (ko, en, ja, zh)",
|
|
2216
|
-
" cost \u2014 Show session info",
|
|
2217
|
-
" context \u2014 Context window info",
|
|
2218
|
-
" permissions \u2014 Permission rules",
|
|
2219
|
-
" reset \u2014 Delete settings and exit"
|
|
2220
|
-
].join("\n"),
|
|
2221
|
-
success: true
|
|
2222
|
-
})
|
|
2223
|
-
},
|
|
2224
|
-
{
|
|
2225
|
-
name: "clear",
|
|
2226
|
-
description: "Clear conversation history",
|
|
2227
|
-
execute: (session, _args) => {
|
|
2228
|
-
const underlying = session.getSession();
|
|
2229
|
-
underlying.clearHistory();
|
|
2230
|
-
return { message: "Conversation cleared.", success: true };
|
|
2231
|
-
}
|
|
2232
|
-
},
|
|
2233
|
-
{
|
|
2234
|
-
name: "compact",
|
|
2235
|
-
description: "Compress context window",
|
|
2236
|
-
execute: async (session, args) => {
|
|
2237
|
-
const underlying = session.getSession();
|
|
2238
|
-
const instructions = args.trim() || void 0;
|
|
2239
|
-
const before = underlying.getContextState().usedPercentage;
|
|
2240
|
-
await underlying.compact(instructions);
|
|
2241
|
-
const after = underlying.getContextState().usedPercentage;
|
|
2242
|
-
return {
|
|
2243
|
-
message: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`,
|
|
2244
|
-
success: true,
|
|
2245
|
-
data: { before, after }
|
|
2246
|
-
};
|
|
2247
|
-
}
|
|
2248
|
-
},
|
|
2249
|
-
{
|
|
2250
|
-
name: "mode",
|
|
2251
|
-
description: "Show/change permission mode",
|
|
2252
|
-
execute: (session, args) => {
|
|
2253
|
-
const underlying = session.getSession();
|
|
2254
|
-
const arg = args.trim().split(/\s+/)[0];
|
|
2255
|
-
if (!arg) {
|
|
2256
|
-
return {
|
|
2257
|
-
message: `Current mode: ${underlying.getPermissionMode()}`,
|
|
2258
|
-
success: true,
|
|
2259
|
-
data: { mode: underlying.getPermissionMode() }
|
|
2260
|
-
};
|
|
2261
|
-
}
|
|
2262
|
-
if (VALID_MODES.includes(arg)) {
|
|
2263
|
-
underlying.setPermissionMode(arg);
|
|
2264
|
-
return {
|
|
2265
|
-
message: `Permission mode set to: ${arg}`,
|
|
2266
|
-
success: true,
|
|
2267
|
-
data: { mode: arg }
|
|
2268
|
-
};
|
|
2269
|
-
}
|
|
2270
|
-
return {
|
|
2271
|
-
message: `Invalid mode. Valid: ${VALID_MODES.join(" | ")}`,
|
|
2272
|
-
success: false
|
|
2273
|
-
};
|
|
2274
|
-
}
|
|
2275
|
-
},
|
|
2276
|
-
{
|
|
2277
|
-
name: "model",
|
|
2278
|
-
description: "Change AI model",
|
|
2279
|
-
execute: (_session, args) => {
|
|
2280
|
-
const modelId = args.trim().split(/\s+/)[0];
|
|
2281
|
-
if (!modelId) {
|
|
2282
|
-
return { message: "Usage: model <model-id>", success: false };
|
|
2283
|
-
}
|
|
2284
|
-
return {
|
|
2285
|
-
message: `Model change requested: ${modelId}`,
|
|
2286
|
-
success: true,
|
|
2287
|
-
data: { modelId }
|
|
2288
|
-
};
|
|
2289
|
-
}
|
|
2290
|
-
},
|
|
2291
|
-
{
|
|
2292
|
-
name: "language",
|
|
2293
|
-
description: "Set response language",
|
|
2294
|
-
execute: (_session, args) => {
|
|
2295
|
-
const lang = args.trim().split(/\s+/)[0];
|
|
2296
|
-
if (!lang) {
|
|
2297
|
-
return { message: "Usage: language <code> (e.g., ko, en, ja, zh)", success: false };
|
|
2298
|
-
}
|
|
2299
|
-
return {
|
|
2300
|
-
message: `Language set to "${lang}".`,
|
|
2301
|
-
success: true,
|
|
2302
|
-
data: { language: lang }
|
|
2303
|
-
};
|
|
2304
|
-
}
|
|
2305
|
-
},
|
|
2306
|
-
{
|
|
2307
|
-
name: "cost",
|
|
2308
|
-
description: "Show session info",
|
|
2309
|
-
execute: (session, _args) => {
|
|
2310
|
-
const underlying = session.getSession();
|
|
2311
|
-
const sessionId = underlying.getSessionId();
|
|
2312
|
-
const messageCount = underlying.getMessageCount();
|
|
2313
|
-
return {
|
|
2314
|
-
message: `Session: ${sessionId}
|
|
2315
|
-
Messages: ${messageCount}`,
|
|
2316
|
-
success: true,
|
|
2317
|
-
data: { sessionId, messageCount }
|
|
2318
|
-
};
|
|
2319
|
-
}
|
|
2320
|
-
},
|
|
2321
|
-
{
|
|
2322
|
-
name: "context",
|
|
2323
|
-
description: "Context window info",
|
|
2324
|
-
execute: (session, _args) => {
|
|
2325
|
-
const ctx = session.getContextState();
|
|
2326
|
-
return {
|
|
2327
|
-
message: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`,
|
|
2328
|
-
success: true,
|
|
2329
|
-
data: {
|
|
2330
|
-
usedTokens: ctx.usedTokens,
|
|
2331
|
-
maxTokens: ctx.maxTokens,
|
|
2332
|
-
percentage: ctx.usedPercentage
|
|
2333
|
-
}
|
|
2334
|
-
};
|
|
2335
|
-
}
|
|
2336
|
-
},
|
|
2337
|
-
{
|
|
2338
|
-
name: "permissions",
|
|
2339
|
-
description: "Show permission rules",
|
|
2340
|
-
execute: (session, _args) => {
|
|
2341
|
-
const underlying = session.getSession();
|
|
2342
|
-
const mode = underlying.getPermissionMode();
|
|
2343
|
-
const sessionAllowed = underlying.getSessionAllowedTools();
|
|
2344
|
-
const lines = [`Permission mode: ${mode}`];
|
|
2345
|
-
if (sessionAllowed.length > 0) {
|
|
2346
|
-
lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
|
|
2347
|
-
} else {
|
|
2348
|
-
lines.push("No session-approved tools.");
|
|
2349
|
-
}
|
|
2350
|
-
return {
|
|
2351
|
-
message: lines.join("\n"),
|
|
2352
|
-
success: true,
|
|
2353
|
-
data: { mode, sessionAllowed }
|
|
2354
|
-
};
|
|
2355
|
-
}
|
|
2356
|
-
},
|
|
2357
|
-
{
|
|
2358
|
-
name: "reset",
|
|
2359
|
-
description: "Delete settings",
|
|
2360
|
-
execute: (_session, _args) => {
|
|
2361
|
-
return {
|
|
2362
|
-
message: "Reset requested.",
|
|
2363
|
-
success: true,
|
|
2364
|
-
data: { resetRequested: true }
|
|
2365
|
-
};
|
|
2366
|
-
}
|
|
2367
|
-
}
|
|
2368
|
-
];
|
|
2369
|
-
}
|
|
2370
|
-
var SystemCommandExecutor = class {
|
|
2371
|
-
commands;
|
|
2372
|
-
constructor(commands) {
|
|
2373
|
-
this.commands = /* @__PURE__ */ new Map();
|
|
2374
|
-
for (const cmd of commands ?? createSystemCommands()) {
|
|
2375
|
-
this.commands.set(cmd.name, cmd);
|
|
2376
|
-
}
|
|
2377
|
-
}
|
|
2378
|
-
/** Register an additional command. */
|
|
2379
|
-
register(command) {
|
|
2380
|
-
this.commands.set(command.name, command);
|
|
2381
|
-
}
|
|
2382
|
-
/** Execute a command by name. Returns null if command not found. */
|
|
2383
|
-
async execute(name, session, args) {
|
|
2384
|
-
const cmd = this.commands.get(name);
|
|
2385
|
-
if (!cmd) return null;
|
|
2386
|
-
return await cmd.execute(session, args);
|
|
2387
|
-
}
|
|
2388
|
-
/** List all registered commands. */
|
|
2389
|
-
listCommands() {
|
|
2390
|
-
return [...this.commands.values()];
|
|
2391
|
-
}
|
|
2392
|
-
/** Check if a command exists. */
|
|
2393
|
-
hasCommand(name) {
|
|
2394
|
-
return this.commands.has(name);
|
|
2395
|
-
}
|
|
2396
|
-
};
|
|
2397
|
-
|
|
2398
|
-
// src/interactive/interactive-session.ts
|
|
2399
|
-
var import_agent_sessions4 = require("@robota-sdk/agent-sessions");
|
|
2400
|
-
var import_agent_core3 = require("@robota-sdk/agent-core");
|
|
2401
|
-
var TOOL_ARG_DISPLAY_MAX = 80;
|
|
2402
|
-
var TAIL_KEEP = 30;
|
|
2403
|
-
var MAX_COMPLETED_TOOLS = 50;
|
|
2404
|
-
var STREAMING_FLUSH_INTERVAL_MS = 16;
|
|
2405
|
-
var InteractiveSession = class {
|
|
2406
|
-
session;
|
|
2407
|
-
listeners = /* @__PURE__ */ new Map();
|
|
2408
|
-
// Streaming state
|
|
2409
|
-
streamingText = "";
|
|
2410
|
-
flushTimer = null;
|
|
2411
|
-
// Tool state
|
|
2412
|
-
activeTools = [];
|
|
2413
|
-
// Execution state
|
|
2414
|
-
executing = false;
|
|
2415
|
-
pendingPrompt = null;
|
|
2416
|
-
pendingDisplayInput;
|
|
2417
|
-
pendingRawInput;
|
|
2418
|
-
// Display messages (what clients render — not the raw session history)
|
|
2419
|
-
messages = [];
|
|
2420
|
-
constructor(options) {
|
|
2421
|
-
if (options.session) {
|
|
2422
|
-
this.session = options.session;
|
|
2423
|
-
} else {
|
|
2424
|
-
const cwd = options.cwd ?? process.cwd();
|
|
2425
|
-
const paths = projectPaths(cwd);
|
|
2426
|
-
this.session = createSession({
|
|
2427
|
-
config: options.config,
|
|
2428
|
-
context: options.context,
|
|
2429
|
-
projectInfo: options.projectInfo,
|
|
2430
|
-
sessionStore: options.sessionStore,
|
|
2431
|
-
permissionMode: options.permissionMode,
|
|
2432
|
-
maxTurns: options.maxTurns,
|
|
2433
|
-
terminal: NOOP_TERMINAL,
|
|
2434
|
-
sessionLogger: new import_agent_sessions4.FileSessionLogger(paths.logs),
|
|
2435
|
-
permissionHandler: options.permissionHandler,
|
|
2436
|
-
onTextDelta: (delta) => this.handleTextDelta(delta),
|
|
2437
|
-
onToolExecution: (event) => this.handleToolExecution(event)
|
|
2438
|
-
});
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
|
-
// ── Event system ──────────────────────────────────────────────
|
|
2442
|
-
on(event, handler) {
|
|
2443
|
-
if (!this.listeners.has(event)) {
|
|
2444
|
-
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
2445
|
-
}
|
|
2446
|
-
this.listeners.get(event).add(handler);
|
|
2447
|
-
}
|
|
2448
|
-
off(event, handler) {
|
|
2449
|
-
this.listeners.get(event)?.delete(handler);
|
|
2249
|
+
off(event, handler) {
|
|
2250
|
+
this.listeners.get(event)?.delete(handler);
|
|
2450
2251
|
}
|
|
2451
2252
|
emit(event, ...args) {
|
|
2452
2253
|
const handlers = this.listeners.get(event);
|
|
@@ -2457,10 +2258,9 @@ var InteractiveSession = class {
|
|
|
2457
2258
|
}
|
|
2458
2259
|
}
|
|
2459
2260
|
// ── Public API ────────────────────────────────────────────────
|
|
2460
|
-
/** Submit a prompt. Queues if already executing (max 1 queued).
|
|
2461
|
-
* displayInput overrides what appears as the user message (e.g., "/audit" instead of full skill prompt).
|
|
2462
|
-
* rawInput is passed to Session.run() for hook matching (e.g., "/rulebased-harness:audit"). */
|
|
2261
|
+
/** Submit a prompt. Queues if already executing (max 1 queued). */
|
|
2463
2262
|
async submit(input, displayInput, rawInput) {
|
|
2263
|
+
await this.ensureInitialized();
|
|
2464
2264
|
if (this.executing) {
|
|
2465
2265
|
this.pendingPrompt = input;
|
|
2466
2266
|
this.pendingDisplayInput = displayInput;
|
|
@@ -2469,14 +2269,30 @@ var InteractiveSession = class {
|
|
|
2469
2269
|
}
|
|
2470
2270
|
await this.executePrompt(input, displayInput, rawInput);
|
|
2471
2271
|
}
|
|
2272
|
+
/** Execute a system command by name. Returns null if not found. */
|
|
2273
|
+
async executeCommand(name, args) {
|
|
2274
|
+
await this.ensureInitialized();
|
|
2275
|
+
return this.commandExecutor.execute(name, this, args);
|
|
2276
|
+
}
|
|
2277
|
+
/** List all registered system commands. */
|
|
2278
|
+
listCommands() {
|
|
2279
|
+
return this.commandExecutor.listCommands().map((cmd) => ({
|
|
2280
|
+
name: cmd.name,
|
|
2281
|
+
description: cmd.description
|
|
2282
|
+
}));
|
|
2283
|
+
}
|
|
2472
2284
|
/** Abort current execution and clear queue. */
|
|
2473
2285
|
abort() {
|
|
2474
2286
|
this.pendingPrompt = null;
|
|
2475
|
-
this.
|
|
2287
|
+
this.pendingDisplayInput = void 0;
|
|
2288
|
+
this.pendingRawInput = void 0;
|
|
2289
|
+
this.session?.abort();
|
|
2476
2290
|
}
|
|
2477
2291
|
/** Cancel queued prompt without aborting current execution. */
|
|
2478
2292
|
cancelQueue() {
|
|
2479
2293
|
this.pendingPrompt = null;
|
|
2294
|
+
this.pendingDisplayInput = void 0;
|
|
2295
|
+
this.pendingRawInput = void 0;
|
|
2480
2296
|
}
|
|
2481
2297
|
isExecuting() {
|
|
2482
2298
|
return this.executing;
|
|
@@ -2484,8 +2300,13 @@ var InteractiveSession = class {
|
|
|
2484
2300
|
getPendingPrompt() {
|
|
2485
2301
|
return this.pendingPrompt;
|
|
2486
2302
|
}
|
|
2303
|
+
/** Get full history timeline (chat + events) for TUI rendering */
|
|
2304
|
+
getFullHistory() {
|
|
2305
|
+
return this.history;
|
|
2306
|
+
}
|
|
2307
|
+
/** Get chat messages only (backward compatible) */
|
|
2487
2308
|
getMessages() {
|
|
2488
|
-
return this.
|
|
2309
|
+
return this.history.filter((e) => e.category === "chat").map((e) => e.data);
|
|
2489
2310
|
}
|
|
2490
2311
|
getStreamingText() {
|
|
2491
2312
|
return this.streamingText;
|
|
@@ -2494,39 +2315,44 @@ var InteractiveSession = class {
|
|
|
2494
2315
|
return this.activeTools;
|
|
2495
2316
|
}
|
|
2496
2317
|
getContextState() {
|
|
2497
|
-
return this.
|
|
2318
|
+
return this.getSessionOrThrow().getContextState();
|
|
2498
2319
|
}
|
|
2320
|
+
/** Access underlying Session. For advanced use / testing only. */
|
|
2499
2321
|
getSession() {
|
|
2500
|
-
return this.
|
|
2322
|
+
return this.getSessionOrThrow();
|
|
2501
2323
|
}
|
|
2502
2324
|
// ── Execution ─────────────────────────────────────────────────
|
|
2503
2325
|
async executePrompt(input, displayInput, rawInput) {
|
|
2504
2326
|
this.executing = true;
|
|
2505
2327
|
this.clearStreaming();
|
|
2506
2328
|
this.emit("thinking", true);
|
|
2507
|
-
this.
|
|
2508
|
-
const historyBefore = this.
|
|
2329
|
+
this.history.push((0, import_agent_core.messageToHistoryEntry)((0, import_agent_core.createUserMessage)(displayInput ?? input)));
|
|
2330
|
+
const historyBefore = this.getSessionOrThrow().getHistory().length;
|
|
2509
2331
|
try {
|
|
2510
|
-
const response = await this.
|
|
2332
|
+
const response = await this.getSessionOrThrow().run(input, rawInput);
|
|
2511
2333
|
this.flushStreaming();
|
|
2334
|
+
this.pushToolSummaryMessage();
|
|
2512
2335
|
this.clearStreaming();
|
|
2513
2336
|
const result = this.buildResult(response || "(empty response)", historyBefore);
|
|
2514
|
-
this.
|
|
2337
|
+
this.history.push((0, import_agent_core.messageToHistoryEntry)((0, import_agent_core.createAssistantMessage)(result.response)));
|
|
2515
2338
|
this.emit("complete", result);
|
|
2516
2339
|
this.emit("context_update", this.getContextState());
|
|
2517
2340
|
} catch (err) {
|
|
2518
2341
|
this.flushStreaming();
|
|
2519
|
-
this.clearStreaming();
|
|
2520
2342
|
if (isAbortError(err)) {
|
|
2521
2343
|
const result = this.buildInterruptedResult(historyBefore);
|
|
2344
|
+
this.pushToolSummaryMessage();
|
|
2345
|
+
this.clearStreaming();
|
|
2522
2346
|
if (result.response) {
|
|
2523
|
-
this.
|
|
2347
|
+
this.history.push((0, import_agent_core.messageToHistoryEntry)((0, import_agent_core.createAssistantMessage)(result.response)));
|
|
2524
2348
|
}
|
|
2525
|
-
this.
|
|
2349
|
+
this.history.push((0, import_agent_core.messageToHistoryEntry)((0, import_agent_core.createSystemMessage)("Interrupted by user.")));
|
|
2526
2350
|
this.emit("interrupted", result);
|
|
2527
2351
|
} else {
|
|
2352
|
+
this.pushToolSummaryMessage();
|
|
2353
|
+
this.clearStreaming();
|
|
2528
2354
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2529
|
-
this.
|
|
2355
|
+
this.history.push((0, import_agent_core.messageToHistoryEntry)((0, import_agent_core.createSystemMessage)(`Error: ${errMsg}`)));
|
|
2530
2356
|
this.emit("error", err instanceof Error ? err : new Error(errMsg));
|
|
2531
2357
|
}
|
|
2532
2358
|
} finally {
|
|
@@ -2556,22 +2382,14 @@ var InteractiveSession = class {
|
|
|
2556
2382
|
handleToolExecution(event) {
|
|
2557
2383
|
if (event.type === "start") {
|
|
2558
2384
|
const firstArg = extractFirstArg(event.toolArgs);
|
|
2559
|
-
const state = {
|
|
2560
|
-
toolName: event.toolName,
|
|
2561
|
-
firstArg,
|
|
2562
|
-
isRunning: true
|
|
2563
|
-
};
|
|
2385
|
+
const state = { toolName: event.toolName, firstArg, isRunning: true };
|
|
2564
2386
|
this.activeTools.push(state);
|
|
2565
2387
|
this.emit("tool_start", state);
|
|
2566
2388
|
} else {
|
|
2567
2389
|
const result = event.denied ? "denied" : event.success === false ? "error" : "success";
|
|
2568
2390
|
const idx = this.activeTools.findIndex((t) => t.toolName === event.toolName && t.isRunning);
|
|
2569
2391
|
if (idx !== -1) {
|
|
2570
|
-
const finished = {
|
|
2571
|
-
...this.activeTools[idx],
|
|
2572
|
-
isRunning: false,
|
|
2573
|
-
result
|
|
2574
|
-
};
|
|
2392
|
+
const finished = { ...this.activeTools[idx], isRunning: false, result };
|
|
2575
2393
|
this.activeTools[idx] = finished;
|
|
2576
2394
|
this.trimCompletedTools();
|
|
2577
2395
|
this.emit("tool_end", finished);
|
|
@@ -2579,6 +2397,31 @@ var InteractiveSession = class {
|
|
|
2579
2397
|
}
|
|
2580
2398
|
}
|
|
2581
2399
|
// ── Helpers ───────────────────────────────────────────────────
|
|
2400
|
+
/** Push tool execution summary into messages (before Robota response).
|
|
2401
|
+
* Moves tool info from activeTools (real-time display) to messages (permanent display).
|
|
2402
|
+
* After this, activeTools will be cleared by clearStreaming(). */
|
|
2403
|
+
pushToolSummaryMessage() {
|
|
2404
|
+
if (this.activeTools.length === 0) return;
|
|
2405
|
+
const summary = this.activeTools.map((t) => {
|
|
2406
|
+
const status = t.isRunning ? "\u27F3" : t.result === "success" ? "\u2713" : t.result === "error" ? "\u2717" : "\u2298";
|
|
2407
|
+
return `${status} ${t.toolName}${t.firstArg ? `(${t.firstArg})` : ""}`;
|
|
2408
|
+
}).join("\n");
|
|
2409
|
+
this.history.push({
|
|
2410
|
+
id: (0, import_node_crypto.randomUUID)(),
|
|
2411
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
2412
|
+
category: "event",
|
|
2413
|
+
type: "tool-summary",
|
|
2414
|
+
data: {
|
|
2415
|
+
tools: this.activeTools.map((t) => ({
|
|
2416
|
+
toolName: t.toolName,
|
|
2417
|
+
firstArg: t.firstArg,
|
|
2418
|
+
isRunning: t.isRunning,
|
|
2419
|
+
result: t.result
|
|
2420
|
+
})),
|
|
2421
|
+
summary
|
|
2422
|
+
}
|
|
2423
|
+
});
|
|
2424
|
+
}
|
|
2582
2425
|
clearStreaming() {
|
|
2583
2426
|
this.streamingText = "";
|
|
2584
2427
|
this.activeTools = [];
|
|
@@ -2597,30 +2440,28 @@ var InteractiveSession = class {
|
|
|
2597
2440
|
const toolSummaries = this.extractToolSummaries(historyBefore);
|
|
2598
2441
|
return {
|
|
2599
2442
|
response,
|
|
2600
|
-
|
|
2443
|
+
history: this.history,
|
|
2601
2444
|
toolSummaries,
|
|
2602
2445
|
contextState: this.getContextState()
|
|
2603
2446
|
};
|
|
2604
2447
|
}
|
|
2605
2448
|
buildInterruptedResult(historyBefore) {
|
|
2606
|
-
const history = this.
|
|
2449
|
+
const history = this.getSessionOrThrow().getHistory();
|
|
2607
2450
|
const toolSummaries = this.extractToolSummaries(historyBefore);
|
|
2608
2451
|
const parts = [];
|
|
2609
2452
|
for (let i = historyBefore; i < history.length; i++) {
|
|
2610
2453
|
const msg = history[i];
|
|
2611
|
-
if (msg?.role === "assistant" && msg.content)
|
|
2612
|
-
parts.push(msg.content);
|
|
2613
|
-
}
|
|
2454
|
+
if (msg?.role === "assistant" && msg.content) parts.push(msg.content);
|
|
2614
2455
|
}
|
|
2615
2456
|
return {
|
|
2616
2457
|
response: parts.join("\n\n"),
|
|
2617
|
-
|
|
2458
|
+
history: this.history,
|
|
2618
2459
|
toolSummaries,
|
|
2619
2460
|
contextState: this.getContextState()
|
|
2620
2461
|
};
|
|
2621
2462
|
}
|
|
2622
2463
|
extractToolSummaries(historyBefore) {
|
|
2623
|
-
const history = this.
|
|
2464
|
+
const history = this.getSessionOrThrow().getHistory();
|
|
2624
2465
|
const summaries = [];
|
|
2625
2466
|
for (let i = historyBefore; i < history.length; i++) {
|
|
2626
2467
|
const msg = history[i];
|
|
@@ -2672,13 +2513,395 @@ var NOOP_TERMINAL = {
|
|
|
2672
2513
|
} })
|
|
2673
2514
|
};
|
|
2674
2515
|
|
|
2516
|
+
// src/query.ts
|
|
2517
|
+
function createQuery(options) {
|
|
2518
|
+
const session = new InteractiveSession({
|
|
2519
|
+
cwd: options.cwd ?? process.cwd(),
|
|
2520
|
+
provider: options.provider,
|
|
2521
|
+
permissionMode: options.permissionMode ?? "bypassPermissions",
|
|
2522
|
+
maxTurns: options.maxTurns,
|
|
2523
|
+
permissionHandler: options.permissionHandler
|
|
2524
|
+
});
|
|
2525
|
+
if (options.onTextDelta) {
|
|
2526
|
+
session.on("text_delta", options.onTextDelta);
|
|
2527
|
+
}
|
|
2528
|
+
return async (prompt) => {
|
|
2529
|
+
return new Promise((resolve2, reject) => {
|
|
2530
|
+
const onComplete = (result) => {
|
|
2531
|
+
cleanup();
|
|
2532
|
+
resolve2(result.response);
|
|
2533
|
+
};
|
|
2534
|
+
const onInterrupted = (result) => {
|
|
2535
|
+
cleanup();
|
|
2536
|
+
resolve2(result.response);
|
|
2537
|
+
};
|
|
2538
|
+
const onError = (error) => {
|
|
2539
|
+
cleanup();
|
|
2540
|
+
reject(error);
|
|
2541
|
+
};
|
|
2542
|
+
const cleanup = () => {
|
|
2543
|
+
session.off("complete", onComplete);
|
|
2544
|
+
session.off("interrupted", onInterrupted);
|
|
2545
|
+
session.off("error", onError);
|
|
2546
|
+
};
|
|
2547
|
+
session.on("complete", onComplete);
|
|
2548
|
+
session.on("interrupted", onInterrupted);
|
|
2549
|
+
session.on("error", onError);
|
|
2550
|
+
session.submit(prompt).catch((err) => {
|
|
2551
|
+
cleanup();
|
|
2552
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
2553
|
+
});
|
|
2554
|
+
});
|
|
2555
|
+
};
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
// src/commands/command-registry.ts
|
|
2559
|
+
var CommandRegistry = class {
|
|
2560
|
+
sources = [];
|
|
2561
|
+
addSource(source) {
|
|
2562
|
+
this.sources.push(source);
|
|
2563
|
+
}
|
|
2564
|
+
/** Get all commands, optionally filtered by prefix */
|
|
2565
|
+
getCommands(filter) {
|
|
2566
|
+
const all = [];
|
|
2567
|
+
for (const source of this.sources) {
|
|
2568
|
+
all.push(...source.getCommands());
|
|
2569
|
+
}
|
|
2570
|
+
if (!filter) return all;
|
|
2571
|
+
const lower = filter.toLowerCase();
|
|
2572
|
+
return all.filter((cmd) => cmd.name.toLowerCase().startsWith(lower));
|
|
2573
|
+
}
|
|
2574
|
+
/** Resolve a short name to its fully qualified plugin:name form */
|
|
2575
|
+
resolveQualifiedName(shortName) {
|
|
2576
|
+
const matches = this.getCommands().filter(
|
|
2577
|
+
(c) => c.source === "plugin" && c.name.includes(":") && c.name.endsWith(`:${shortName}`)
|
|
2578
|
+
);
|
|
2579
|
+
if (matches.length !== 1) return null;
|
|
2580
|
+
return matches[0].name;
|
|
2581
|
+
}
|
|
2582
|
+
/** Get subcommands for a specific command */
|
|
2583
|
+
getSubcommands(commandName) {
|
|
2584
|
+
const lower = commandName.toLowerCase();
|
|
2585
|
+
for (const source of this.sources) {
|
|
2586
|
+
for (const cmd of source.getCommands()) {
|
|
2587
|
+
if (cmd.name.toLowerCase() === lower && cmd.subcommands) {
|
|
2588
|
+
return cmd.subcommands;
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
return [];
|
|
2593
|
+
}
|
|
2594
|
+
};
|
|
2595
|
+
|
|
2596
|
+
// src/commands/builtin-source.ts
|
|
2597
|
+
var import_agent_core2 = require("@robota-sdk/agent-core");
|
|
2598
|
+
function buildModelSubcommands() {
|
|
2599
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2600
|
+
const commands = [];
|
|
2601
|
+
for (const model of Object.values(import_agent_core2.CLAUDE_MODELS)) {
|
|
2602
|
+
if (seen.has(model.name)) continue;
|
|
2603
|
+
seen.add(model.name);
|
|
2604
|
+
commands.push({
|
|
2605
|
+
name: model.id,
|
|
2606
|
+
description: `${model.name} (${(0, import_agent_core2.formatTokenCount)(model.contextWindow).toUpperCase()})`,
|
|
2607
|
+
source: "builtin"
|
|
2608
|
+
});
|
|
2609
|
+
}
|
|
2610
|
+
return commands;
|
|
2611
|
+
}
|
|
2612
|
+
function createBuiltinCommands() {
|
|
2613
|
+
return [
|
|
2614
|
+
{ name: "help", description: "Show available commands", source: "builtin" },
|
|
2615
|
+
{ name: "clear", description: "Clear conversation history", source: "builtin" },
|
|
2616
|
+
{
|
|
2617
|
+
name: "mode",
|
|
2618
|
+
description: "Permission mode",
|
|
2619
|
+
source: "builtin",
|
|
2620
|
+
subcommands: [
|
|
2621
|
+
{ name: "plan", description: "Plan only, no execution", source: "builtin" },
|
|
2622
|
+
{ name: "default", description: "Ask before risky actions", source: "builtin" },
|
|
2623
|
+
{ name: "acceptEdits", description: "Auto-approve file edits", source: "builtin" },
|
|
2624
|
+
{ name: "bypassPermissions", description: "Skip all permission checks", source: "builtin" }
|
|
2625
|
+
]
|
|
2626
|
+
},
|
|
2627
|
+
{
|
|
2628
|
+
name: "model",
|
|
2629
|
+
description: "Select AI model",
|
|
2630
|
+
source: "builtin",
|
|
2631
|
+
subcommands: buildModelSubcommands()
|
|
2632
|
+
},
|
|
2633
|
+
{
|
|
2634
|
+
name: "language",
|
|
2635
|
+
description: "Set response language",
|
|
2636
|
+
source: "builtin",
|
|
2637
|
+
subcommands: [
|
|
2638
|
+
{ name: "ko", description: "Korean", source: "builtin" },
|
|
2639
|
+
{ name: "en", description: "English", source: "builtin" },
|
|
2640
|
+
{ name: "ja", description: "Japanese", source: "builtin" },
|
|
2641
|
+
{ name: "zh", description: "Chinese", source: "builtin" }
|
|
2642
|
+
]
|
|
2643
|
+
},
|
|
2644
|
+
{ name: "compact", description: "Compress context window", source: "builtin" },
|
|
2645
|
+
{ name: "cost", description: "Show session info", source: "builtin" },
|
|
2646
|
+
{ name: "context", description: "Context window info", source: "builtin" },
|
|
2647
|
+
{ name: "permissions", description: "Permission rules", source: "builtin" },
|
|
2648
|
+
{ name: "plugin", description: "Manage plugins", source: "builtin" },
|
|
2649
|
+
{ name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
|
|
2650
|
+
{ name: "reset", description: "Delete settings and exit", source: "builtin" },
|
|
2651
|
+
{ name: "exit", description: "Exit CLI", source: "builtin" }
|
|
2652
|
+
];
|
|
2653
|
+
}
|
|
2654
|
+
var BuiltinCommandSource = class {
|
|
2655
|
+
name = "builtin";
|
|
2656
|
+
commands;
|
|
2657
|
+
constructor() {
|
|
2658
|
+
this.commands = createBuiltinCommands();
|
|
2659
|
+
}
|
|
2660
|
+
getCommands() {
|
|
2661
|
+
return this.commands;
|
|
2662
|
+
}
|
|
2663
|
+
};
|
|
2664
|
+
|
|
2665
|
+
// src/commands/skill-source.ts
|
|
2666
|
+
var import_node_fs7 = require("fs");
|
|
2667
|
+
var import_node_path10 = require("path");
|
|
2668
|
+
var import_node_os4 = require("os");
|
|
2669
|
+
var BOOLEAN_KEYS = /* @__PURE__ */ new Set(["disable-model-invocation", "user-invocable"]);
|
|
2670
|
+
var LIST_KEYS2 = /* @__PURE__ */ new Set(["allowed-tools"]);
|
|
2671
|
+
function kebabToCamel(key) {
|
|
2672
|
+
return key.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
|
|
2673
|
+
}
|
|
2674
|
+
function parseFrontmatter2(content) {
|
|
2675
|
+
const lines = content.split("\n");
|
|
2676
|
+
if (lines[0]?.trim() !== "---") return null;
|
|
2677
|
+
const result = {};
|
|
2678
|
+
for (let i = 1; i < lines.length; i++) {
|
|
2679
|
+
const line = lines[i];
|
|
2680
|
+
if (line.trim() === "---") break;
|
|
2681
|
+
const match = line.match(/^([a-z][a-z0-9-]*):\s*(.+)/);
|
|
2682
|
+
if (!match) continue;
|
|
2683
|
+
const key = match[1];
|
|
2684
|
+
const rawValue = match[2].trim();
|
|
2685
|
+
const camelKey = kebabToCamel(key);
|
|
2686
|
+
if (BOOLEAN_KEYS.has(key)) {
|
|
2687
|
+
result[camelKey] = rawValue === "true";
|
|
2688
|
+
} else if (LIST_KEYS2.has(key)) {
|
|
2689
|
+
result[camelKey] = rawValue.split(",").map((s) => s.trim());
|
|
2690
|
+
} else {
|
|
2691
|
+
result[camelKey] = rawValue;
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
return Object.keys(result).length > 0 ? result : null;
|
|
2695
|
+
}
|
|
2696
|
+
function buildCommand(frontmatter, content, fallbackName) {
|
|
2697
|
+
const cmd = {
|
|
2698
|
+
name: frontmatter?.name ?? fallbackName,
|
|
2699
|
+
description: frontmatter?.description ?? `Skill: ${fallbackName}`,
|
|
2700
|
+
source: "skill",
|
|
2701
|
+
skillContent: content
|
|
2702
|
+
};
|
|
2703
|
+
if (frontmatter?.argumentHint !== void 0) cmd.argumentHint = frontmatter.argumentHint;
|
|
2704
|
+
if (frontmatter?.disableModelInvocation !== void 0)
|
|
2705
|
+
cmd.disableModelInvocation = frontmatter.disableModelInvocation;
|
|
2706
|
+
if (frontmatter?.userInvocable !== void 0) cmd.userInvocable = frontmatter.userInvocable;
|
|
2707
|
+
if (frontmatter?.allowedTools !== void 0) cmd.allowedTools = frontmatter.allowedTools;
|
|
2708
|
+
if (frontmatter?.model !== void 0) cmd.model = frontmatter.model;
|
|
2709
|
+
if (frontmatter?.effort !== void 0) cmd.effort = frontmatter.effort;
|
|
2710
|
+
if (frontmatter?.context !== void 0) cmd.context = frontmatter.context;
|
|
2711
|
+
if (frontmatter?.agent !== void 0) cmd.agent = frontmatter.agent;
|
|
2712
|
+
return cmd;
|
|
2713
|
+
}
|
|
2714
|
+
function scanSkillsDir(skillsDir) {
|
|
2715
|
+
if (!(0, import_node_fs7.existsSync)(skillsDir)) return [];
|
|
2716
|
+
const commands = [];
|
|
2717
|
+
const entries = (0, import_node_fs7.readdirSync)(skillsDir, { withFileTypes: true });
|
|
2718
|
+
for (const entry of entries) {
|
|
2719
|
+
if (!entry.isDirectory()) continue;
|
|
2720
|
+
const skillFile = (0, import_node_path10.join)(skillsDir, entry.name, "SKILL.md");
|
|
2721
|
+
if (!(0, import_node_fs7.existsSync)(skillFile)) continue;
|
|
2722
|
+
const content = (0, import_node_fs7.readFileSync)(skillFile, "utf-8");
|
|
2723
|
+
const frontmatter = parseFrontmatter2(content);
|
|
2724
|
+
commands.push(buildCommand(frontmatter, content, entry.name));
|
|
2725
|
+
}
|
|
2726
|
+
return commands;
|
|
2727
|
+
}
|
|
2728
|
+
function scanCommandsDir(commandsDir) {
|
|
2729
|
+
if (!(0, import_node_fs7.existsSync)(commandsDir)) return [];
|
|
2730
|
+
const commands = [];
|
|
2731
|
+
const entries = (0, import_node_fs7.readdirSync)(commandsDir, { withFileTypes: true });
|
|
2732
|
+
for (const entry of entries) {
|
|
2733
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
2734
|
+
const filePath = (0, import_node_path10.join)(commandsDir, entry.name);
|
|
2735
|
+
const content = (0, import_node_fs7.readFileSync)(filePath, "utf-8");
|
|
2736
|
+
const frontmatter = parseFrontmatter2(content);
|
|
2737
|
+
const fallbackName = (0, import_node_path10.basename)(entry.name, ".md");
|
|
2738
|
+
commands.push(buildCommand(frontmatter, content, fallbackName));
|
|
2739
|
+
}
|
|
2740
|
+
return commands;
|
|
2741
|
+
}
|
|
2742
|
+
var SkillCommandSource = class {
|
|
2743
|
+
name = "skill";
|
|
2744
|
+
cwd;
|
|
2745
|
+
home;
|
|
2746
|
+
cachedCommands = null;
|
|
2747
|
+
constructor(cwd, home) {
|
|
2748
|
+
this.cwd = cwd;
|
|
2749
|
+
this.home = home ?? (0, import_node_os4.homedir)();
|
|
2750
|
+
}
|
|
2751
|
+
getCommands() {
|
|
2752
|
+
if (this.cachedCommands) return this.cachedCommands;
|
|
2753
|
+
const sources = [
|
|
2754
|
+
scanSkillsDir((0, import_node_path10.join)(this.cwd, ".claude", "skills")),
|
|
2755
|
+
scanCommandsDir((0, import_node_path10.join)(this.cwd, ".claude", "commands")),
|
|
2756
|
+
scanSkillsDir((0, import_node_path10.join)(this.home, ".robota", "skills")),
|
|
2757
|
+
scanSkillsDir((0, import_node_path10.join)(this.cwd, ".agents", "skills"))
|
|
2758
|
+
];
|
|
2759
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2760
|
+
const merged = [];
|
|
2761
|
+
for (const commands of sources) {
|
|
2762
|
+
for (const cmd of commands) {
|
|
2763
|
+
if (!seen.has(cmd.name)) {
|
|
2764
|
+
seen.add(cmd.name);
|
|
2765
|
+
merged.push(cmd);
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
this.cachedCommands = merged;
|
|
2770
|
+
return this.cachedCommands;
|
|
2771
|
+
}
|
|
2772
|
+
getModelInvocableSkills() {
|
|
2773
|
+
return this.getCommands().filter((cmd) => cmd.disableModelInvocation !== true);
|
|
2774
|
+
}
|
|
2775
|
+
getUserInvocableSkills() {
|
|
2776
|
+
return this.getCommands().filter((cmd) => cmd.userInvocable !== false);
|
|
2777
|
+
}
|
|
2778
|
+
};
|
|
2779
|
+
|
|
2780
|
+
// src/commands/plugin-source.ts
|
|
2781
|
+
var PluginCommandSource = class {
|
|
2782
|
+
name = "plugin";
|
|
2783
|
+
plugins;
|
|
2784
|
+
constructor(plugins) {
|
|
2785
|
+
this.plugins = plugins;
|
|
2786
|
+
}
|
|
2787
|
+
getCommands() {
|
|
2788
|
+
const commands = [];
|
|
2789
|
+
for (const plugin of this.plugins) {
|
|
2790
|
+
for (const skill of plugin.skills) {
|
|
2791
|
+
const baseName = skill.name.includes("@") ? skill.name.split("@")[0] : skill.name;
|
|
2792
|
+
commands.push({
|
|
2793
|
+
name: baseName,
|
|
2794
|
+
description: `(${plugin.manifest.name}) ${skill.description}`,
|
|
2795
|
+
source: "plugin",
|
|
2796
|
+
skillContent: skill.skillContent,
|
|
2797
|
+
pluginDir: plugin.pluginDir
|
|
2798
|
+
});
|
|
2799
|
+
}
|
|
2800
|
+
for (const cmd of plugin.commands) {
|
|
2801
|
+
commands.push({
|
|
2802
|
+
name: cmd.name,
|
|
2803
|
+
description: cmd.description,
|
|
2804
|
+
source: "plugin",
|
|
2805
|
+
skillContent: cmd.skillContent,
|
|
2806
|
+
pluginDir: plugin.pluginDir
|
|
2807
|
+
});
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2810
|
+
return commands;
|
|
2811
|
+
}
|
|
2812
|
+
};
|
|
2813
|
+
|
|
2814
|
+
// src/utils/skill-prompt.ts
|
|
2815
|
+
var import_node_child_process3 = require("child_process");
|
|
2816
|
+
function substituteVariables(content, args, context) {
|
|
2817
|
+
const argParts = args ? args.split(/\s+/) : [];
|
|
2818
|
+
let result = content;
|
|
2819
|
+
result = result.replace(/\$ARGUMENTS\[(\d+)]/g, (_match, index) => {
|
|
2820
|
+
return argParts[Number(index)] ?? "";
|
|
2821
|
+
});
|
|
2822
|
+
result = result.replace(/\$ARGUMENTS/g, args);
|
|
2823
|
+
result = result.replace(/\$(\d)(?!\d|\w|\[)/g, (_match, digit) => {
|
|
2824
|
+
return argParts[Number(digit)] ?? "";
|
|
2825
|
+
});
|
|
2826
|
+
result = result.replace(/\$\{CLAUDE_SESSION_ID}/g, context?.sessionId ?? "");
|
|
2827
|
+
result = result.replace(/\$\{CLAUDE_SKILL_DIR}/g, context?.skillDir ?? "");
|
|
2828
|
+
return result;
|
|
2829
|
+
}
|
|
2830
|
+
async function preprocessShellCommands(content) {
|
|
2831
|
+
const shellPattern = /!`([^`]+)`/g;
|
|
2832
|
+
if (!shellPattern.test(content)) {
|
|
2833
|
+
return content;
|
|
2834
|
+
}
|
|
2835
|
+
shellPattern.lastIndex = 0;
|
|
2836
|
+
let result = content;
|
|
2837
|
+
let match;
|
|
2838
|
+
const matches = [];
|
|
2839
|
+
while ((match = shellPattern.exec(content)) !== null) {
|
|
2840
|
+
matches.push({ full: match[0], command: match[1] });
|
|
2841
|
+
}
|
|
2842
|
+
for (const { full, command } of matches) {
|
|
2843
|
+
let output = "";
|
|
2844
|
+
try {
|
|
2845
|
+
output = (0, import_node_child_process3.execSync)(command, {
|
|
2846
|
+
timeout: 5e3,
|
|
2847
|
+
encoding: "utf-8",
|
|
2848
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2849
|
+
}).trimEnd();
|
|
2850
|
+
} catch {
|
|
2851
|
+
output = "";
|
|
2852
|
+
}
|
|
2853
|
+
result = result.replace(full, output);
|
|
2854
|
+
}
|
|
2855
|
+
return result;
|
|
2856
|
+
}
|
|
2857
|
+
async function buildSkillPrompt(input, registry, context) {
|
|
2858
|
+
const parts = input.slice(1).split(/\s+/);
|
|
2859
|
+
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
2860
|
+
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
2861
|
+
if (!skillCmd) return null;
|
|
2862
|
+
const args = parts.slice(1).join(" ").trim();
|
|
2863
|
+
const userInstruction = args || skillCmd.description;
|
|
2864
|
+
if (skillCmd.skillContent) {
|
|
2865
|
+
let processed = await preprocessShellCommands(skillCmd.skillContent);
|
|
2866
|
+
processed = substituteVariables(processed, args, context);
|
|
2867
|
+
return `<skill name="${cmd}">
|
|
2868
|
+
${processed}
|
|
2869
|
+
</skill>
|
|
2870
|
+
|
|
2871
|
+
Execute the "${cmd}" skill: ${userInstruction}`;
|
|
2872
|
+
}
|
|
2873
|
+
return `Use the "${cmd}" skill: ${userInstruction}`;
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
// src/types.ts
|
|
2877
|
+
var import_agent_core3 = require("@robota-sdk/agent-core");
|
|
2878
|
+
|
|
2879
|
+
// src/index.ts
|
|
2880
|
+
var import_agent_core4 = require("@robota-sdk/agent-core");
|
|
2881
|
+
var import_agent_core5 = require("@robota-sdk/agent-core");
|
|
2882
|
+
|
|
2883
|
+
// src/permissions/permission-prompt.ts
|
|
2884
|
+
var import_chalk = __toESM(require("chalk"), 1);
|
|
2885
|
+
var PERMISSION_OPTIONS = ["Allow", "Deny"];
|
|
2886
|
+
var ALLOW_INDEX = 0;
|
|
2887
|
+
function formatArgs(toolArgs) {
|
|
2888
|
+
const entries = Object.entries(toolArgs);
|
|
2889
|
+
if (entries.length === 0) {
|
|
2890
|
+
return "(no arguments)";
|
|
2891
|
+
}
|
|
2892
|
+
return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
2893
|
+
}
|
|
2894
|
+
async function promptForApproval(terminal, toolName, toolArgs) {
|
|
2895
|
+
terminal.writeLine("");
|
|
2896
|
+
terminal.writeLine(import_chalk.default.yellow(`[Permission Required] Tool: ${toolName}`));
|
|
2897
|
+
terminal.writeLine(import_chalk.default.dim(` ${formatArgs(toolArgs)}`));
|
|
2898
|
+
terminal.writeLine("");
|
|
2899
|
+
const selected = await terminal.select(PERMISSION_OPTIONS, ALLOW_INDEX);
|
|
2900
|
+
return selected === ALLOW_INDEX;
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2675
2903
|
// src/index.ts
|
|
2676
|
-
var
|
|
2677
|
-
var import_agent_tools4 = require("@robota-sdk/agent-tools");
|
|
2678
|
-
var import_agent_tools5 = require("@robota-sdk/agent-tools");
|
|
2679
|
-
var import_agent_tools6 = require("@robota-sdk/agent-tools");
|
|
2680
|
-
var import_agent_tools7 = require("@robota-sdk/agent-tools");
|
|
2681
|
-
var import_agent_tools8 = require("@robota-sdk/agent-tools");
|
|
2904
|
+
var import_agent_core6 = require("@robota-sdk/agent-core");
|
|
2682
2905
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2683
2906
|
0 && (module.exports = {
|
|
2684
2907
|
AgentExecutor,
|
|
@@ -2687,47 +2910,35 @@ var import_agent_tools8 = require("@robota-sdk/agent-tools");
|
|
|
2687
2910
|
BundlePluginInstaller,
|
|
2688
2911
|
BundlePluginLoader,
|
|
2689
2912
|
CommandRegistry,
|
|
2690
|
-
DEFAULT_TOOL_DESCRIPTIONS,
|
|
2691
|
-
FileSessionLogger,
|
|
2692
2913
|
InteractiveSession,
|
|
2693
2914
|
MarketplaceClient,
|
|
2915
|
+
PluginCommandSource,
|
|
2694
2916
|
PluginSettingsStore,
|
|
2695
2917
|
PromptExecutor,
|
|
2696
|
-
Session,
|
|
2697
|
-
SessionStore,
|
|
2698
|
-
SilentSessionLogger,
|
|
2699
2918
|
SkillCommandSource,
|
|
2700
|
-
SystemCommandExecutor,
|
|
2701
2919
|
TRUST_TO_MODE,
|
|
2702
2920
|
assembleSubagentPrompt,
|
|
2703
|
-
|
|
2704
|
-
|
|
2921
|
+
buildSkillPrompt,
|
|
2922
|
+
chatEntryToMessage,
|
|
2705
2923
|
createAgentTool,
|
|
2706
|
-
|
|
2707
|
-
createProvider,
|
|
2708
|
-
createSession,
|
|
2924
|
+
createQuery,
|
|
2709
2925
|
createSubagentLogger,
|
|
2710
2926
|
createSubagentSession,
|
|
2711
|
-
createSystemCommands,
|
|
2712
|
-
detectProject,
|
|
2713
|
-
editTool,
|
|
2714
2927
|
evaluatePermission,
|
|
2715
2928
|
getBuiltInAgent,
|
|
2716
2929
|
getForkWorkerSuffix,
|
|
2930
|
+
getMessagesForAPI,
|
|
2717
2931
|
getSubagentSuffix,
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
loadConfig,
|
|
2721
|
-
loadContext,
|
|
2932
|
+
isChatEntry,
|
|
2933
|
+
messageToHistoryEntry,
|
|
2722
2934
|
parseFrontmatter,
|
|
2935
|
+
preprocessShellCommands,
|
|
2723
2936
|
projectPaths,
|
|
2724
2937
|
promptForApproval,
|
|
2725
|
-
query,
|
|
2726
|
-
readTool,
|
|
2727
2938
|
resolveSubagentLogDir,
|
|
2728
2939
|
retrieveAgentToolDeps,
|
|
2729
2940
|
runHooks,
|
|
2730
2941
|
storeAgentToolDeps,
|
|
2731
|
-
|
|
2732
|
-
|
|
2942
|
+
substituteVariables,
|
|
2943
|
+
userPaths
|
|
2733
2944
|
});
|