@robota-sdk/agent-cli 3.0.0-beta.4 → 3.0.0-beta.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -86
- package/dist/node/bin.js +11 -1
- package/dist/node/chunk-KX3JUGSB.js +2348 -0
- package/dist/node/index.cjs +1825 -685
- package/dist/node/index.js +1 -1
- package/package.json +5 -4
- package/dist/node/bin.cjs +0 -1217
- package/dist/node/bin.d.cts +0 -1
- package/dist/node/chunk-4DYVT4WI.js +0 -1196
package/dist/node/index.cjs
CHANGED
|
@@ -30,192 +30,680 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
Session: () =>
|
|
34
|
-
SessionStore: () =>
|
|
35
|
-
TRUST_TO_MODE: () =>
|
|
36
|
-
query: () =>
|
|
33
|
+
Session: () => import_agent_sdk5.Session,
|
|
34
|
+
SessionStore: () => import_agent_sdk5.SessionStore,
|
|
35
|
+
TRUST_TO_MODE: () => import_agent_sdk5.TRUST_TO_MODE,
|
|
36
|
+
query: () => import_agent_sdk5.query,
|
|
37
37
|
startCli: () => startCli
|
|
38
38
|
});
|
|
39
39
|
module.exports = __toCommonJS(index_exports);
|
|
40
|
-
var
|
|
40
|
+
var import_agent_sdk5 = require("@robota-sdk/agent-sdk");
|
|
41
41
|
|
|
42
42
|
// src/cli.ts
|
|
43
|
-
var import_node_util = require("util");
|
|
44
43
|
var import_node_fs2 = require("fs");
|
|
45
|
-
var
|
|
44
|
+
var import_node_path5 = require("path");
|
|
46
45
|
var import_node_url = require("url");
|
|
47
|
-
var
|
|
48
|
-
var
|
|
46
|
+
var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
|
|
47
|
+
var import_agent_sdk4 = require("@robota-sdk/agent-sdk");
|
|
49
48
|
|
|
50
|
-
// src/
|
|
51
|
-
var
|
|
52
|
-
var
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
// src/utils/cli-args.ts
|
|
50
|
+
var import_node_util = require("util");
|
|
51
|
+
var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
52
|
+
function parsePermissionMode(raw) {
|
|
53
|
+
if (raw === void 0) return void 0;
|
|
54
|
+
if (!VALID_MODES.includes(raw)) {
|
|
55
|
+
process.stderr.write(`Invalid --permission-mode "${raw}". Valid: ${VALID_MODES.join(" | ")}
|
|
56
|
+
`);
|
|
57
|
+
process.exit(1);
|
|
58
58
|
}
|
|
59
|
-
return
|
|
59
|
+
return raw;
|
|
60
60
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
function parseMaxTurns(raw) {
|
|
62
|
+
if (raw === void 0) return void 0;
|
|
63
|
+
const n = parseInt(raw, 10);
|
|
64
|
+
if (isNaN(n) || n <= 0) {
|
|
65
|
+
process.stderr.write(`Invalid --max-turns "${raw}". Must be a positive integer.
|
|
66
|
+
`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
return n;
|
|
70
|
+
}
|
|
71
|
+
function parseCliArgs() {
|
|
72
|
+
const { values, positionals } = (0, import_node_util.parseArgs)({
|
|
73
|
+
allowPositionals: true,
|
|
74
|
+
options: {
|
|
75
|
+
p: { type: "boolean", short: "p", default: false },
|
|
76
|
+
c: { type: "boolean", short: "c", default: false },
|
|
77
|
+
r: { type: "string", short: "r" },
|
|
78
|
+
model: { type: "string" },
|
|
79
|
+
language: { type: "string" },
|
|
80
|
+
"permission-mode": { type: "string" },
|
|
81
|
+
"max-turns": { type: "string" },
|
|
82
|
+
version: { type: "boolean", default: false },
|
|
83
|
+
reset: { type: "boolean", default: false }
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
positional: positionals,
|
|
88
|
+
printMode: values["p"] ?? false,
|
|
89
|
+
continueMode: values["c"] ?? false,
|
|
90
|
+
resumeId: values["r"],
|
|
91
|
+
model: values["model"],
|
|
92
|
+
language: values["language"],
|
|
93
|
+
permissionMode: parsePermissionMode(values["permission-mode"]),
|
|
94
|
+
maxTurns: parseMaxTurns(values["max-turns"]),
|
|
95
|
+
version: values["version"] ?? false,
|
|
96
|
+
reset: values["reset"] ?? false
|
|
97
|
+
};
|
|
68
98
|
}
|
|
69
99
|
|
|
100
|
+
// src/utils/settings-io.ts
|
|
101
|
+
var import_node_fs = require("fs");
|
|
102
|
+
var import_node_path = require("path");
|
|
103
|
+
function getUserSettingsPath() {
|
|
104
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
|
|
105
|
+
return (0, import_node_path.join)(home, ".robota", "settings.json");
|
|
106
|
+
}
|
|
107
|
+
function readSettings(path) {
|
|
108
|
+
if (!(0, import_node_fs.existsSync)(path)) return {};
|
|
109
|
+
return JSON.parse((0, import_node_fs.readFileSync)(path, "utf8"));
|
|
110
|
+
}
|
|
111
|
+
function writeSettings(path, settings) {
|
|
112
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path), { recursive: true });
|
|
113
|
+
(0, import_node_fs.writeFileSync)(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
114
|
+
}
|
|
115
|
+
function updateModelInSettings(settingsPath, modelId) {
|
|
116
|
+
const settings = readSettings(settingsPath);
|
|
117
|
+
const provider = settings.provider ?? {};
|
|
118
|
+
provider.model = modelId;
|
|
119
|
+
settings.provider = provider;
|
|
120
|
+
writeSettings(settingsPath, settings);
|
|
121
|
+
}
|
|
122
|
+
function deleteSettings(path) {
|
|
123
|
+
if ((0, import_node_fs.existsSync)(path)) {
|
|
124
|
+
(0, import_node_fs.unlinkSync)(path);
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/print-terminal.ts
|
|
131
|
+
var readline = __toESM(require("readline"), 1);
|
|
132
|
+
var PrintTerminal = class {
|
|
133
|
+
write(text) {
|
|
134
|
+
process.stdout.write(text);
|
|
135
|
+
}
|
|
136
|
+
writeLine(text) {
|
|
137
|
+
process.stdout.write(text + "\n");
|
|
138
|
+
}
|
|
139
|
+
writeMarkdown(md) {
|
|
140
|
+
process.stdout.write(md);
|
|
141
|
+
}
|
|
142
|
+
writeError(text) {
|
|
143
|
+
process.stderr.write(text + "\n");
|
|
144
|
+
}
|
|
145
|
+
prompt(question) {
|
|
146
|
+
return new Promise((resolve) => {
|
|
147
|
+
const rl = readline.createInterface({
|
|
148
|
+
input: process.stdin,
|
|
149
|
+
output: process.stdout,
|
|
150
|
+
terminal: false,
|
|
151
|
+
historySize: 0
|
|
152
|
+
});
|
|
153
|
+
rl.question(question, (answer) => {
|
|
154
|
+
rl.close();
|
|
155
|
+
resolve(answer);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async select(options, initialIndex = 0) {
|
|
160
|
+
for (let i = 0; i < options.length; i++) {
|
|
161
|
+
const marker = i === initialIndex ? ">" : " ";
|
|
162
|
+
process.stdout.write(` ${marker} ${i + 1}) ${options[i]}
|
|
163
|
+
`);
|
|
164
|
+
}
|
|
165
|
+
const answer = await this.prompt(
|
|
166
|
+
` Choose [1-${options.length}] (default: ${options[initialIndex]}): `
|
|
167
|
+
);
|
|
168
|
+
const trimmed = answer.trim().toLowerCase();
|
|
169
|
+
if (trimmed === "") return initialIndex;
|
|
170
|
+
const num = parseInt(trimmed, 10);
|
|
171
|
+
if (!isNaN(num) && num >= 1 && num <= options.length) return num - 1;
|
|
172
|
+
return initialIndex;
|
|
173
|
+
}
|
|
174
|
+
spinner(_message) {
|
|
175
|
+
return { stop() {
|
|
176
|
+
}, update() {
|
|
177
|
+
} };
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
70
181
|
// src/ui/render.tsx
|
|
71
|
-
var
|
|
182
|
+
var import_ink14 = require("ink");
|
|
72
183
|
|
|
73
184
|
// src/ui/App.tsx
|
|
74
|
-
var
|
|
75
|
-
var
|
|
185
|
+
var import_react12 = require("react");
|
|
186
|
+
var import_ink13 = require("ink");
|
|
187
|
+
var import_agent_core4 = require("@robota-sdk/agent-core");
|
|
188
|
+
|
|
189
|
+
// src/ui/hooks/useInteractiveSession.ts
|
|
190
|
+
var import_react = require("react");
|
|
191
|
+
var import_node_os = require("os");
|
|
192
|
+
var import_node_path3 = require("path");
|
|
76
193
|
var import_agent_sdk = require("@robota-sdk/agent-sdk");
|
|
194
|
+
var import_agent_core = require("@robota-sdk/agent-core");
|
|
77
195
|
|
|
78
|
-
// src/commands/
|
|
79
|
-
var
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
getCommands(
|
|
86
|
-
const
|
|
87
|
-
for (const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
for (const cmd of
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
196
|
+
// src/commands/plugin-source.ts
|
|
197
|
+
var PluginCommandSource = class {
|
|
198
|
+
name = "plugin";
|
|
199
|
+
plugins;
|
|
200
|
+
constructor(plugins) {
|
|
201
|
+
this.plugins = plugins;
|
|
202
|
+
}
|
|
203
|
+
getCommands() {
|
|
204
|
+
const commands = [];
|
|
205
|
+
for (const plugin of this.plugins) {
|
|
206
|
+
for (const skill of plugin.skills) {
|
|
207
|
+
const baseName = skill.name.includes("@") ? skill.name.split("@")[0] : skill.name;
|
|
208
|
+
commands.push({
|
|
209
|
+
name: baseName,
|
|
210
|
+
description: `(${plugin.manifest.name}) ${skill.description}`,
|
|
211
|
+
source: "plugin",
|
|
212
|
+
skillContent: skill.skillContent,
|
|
213
|
+
pluginDir: plugin.pluginDir
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
for (const cmd of plugin.commands) {
|
|
217
|
+
commands.push({
|
|
218
|
+
name: cmd.name,
|
|
219
|
+
description: cmd.description,
|
|
220
|
+
source: "plugin",
|
|
221
|
+
skillContent: cmd.skillContent,
|
|
222
|
+
pluginDir: plugin.pluginDir
|
|
223
|
+
});
|
|
102
224
|
}
|
|
103
225
|
}
|
|
104
|
-
return
|
|
226
|
+
return commands;
|
|
105
227
|
}
|
|
106
228
|
};
|
|
107
229
|
|
|
108
|
-
// src/
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
name: "model",
|
|
126
|
-
description: "Select AI model",
|
|
127
|
-
source: "builtin",
|
|
128
|
-
subcommands: [
|
|
129
|
-
{ name: "claude-opus-4-6", description: "Opus 4.6 (highest quality)", source: "builtin" },
|
|
130
|
-
{ name: "claude-sonnet-4-6", description: "Sonnet 4.6 (balanced)", source: "builtin" },
|
|
131
|
-
{ name: "claude-haiku-4-5", description: "Haiku 4.5 (fastest)", source: "builtin" }
|
|
132
|
-
]
|
|
133
|
-
},
|
|
134
|
-
{ name: "compact", description: "Compress context window", source: "builtin" },
|
|
135
|
-
{ name: "cost", description: "Show session info", source: "builtin" },
|
|
136
|
-
{ name: "context", description: "Context window info", source: "builtin" },
|
|
137
|
-
{ name: "permissions", description: "Permission rules", source: "builtin" },
|
|
138
|
-
{ name: "exit", description: "Exit CLI", source: "builtin" }
|
|
139
|
-
];
|
|
230
|
+
// src/utils/skill-prompt.ts
|
|
231
|
+
var import_node_child_process = require("child_process");
|
|
232
|
+
function substituteVariables(content, args, context) {
|
|
233
|
+
const argParts = args ? args.split(/\s+/) : [];
|
|
234
|
+
let result = content;
|
|
235
|
+
result = result.replace(/\$ARGUMENTS\[(\d+)]/g, (_match, index) => {
|
|
236
|
+
return argParts[Number(index)] ?? "";
|
|
237
|
+
});
|
|
238
|
+
result = result.replace(/\$ARGUMENTS/g, args);
|
|
239
|
+
result = result.replace(/\$(\d)(?!\d|\w|\[)/g, (_match, digit) => {
|
|
240
|
+
return argParts[Number(digit)] ?? "";
|
|
241
|
+
});
|
|
242
|
+
result = result.replace(/\$\{CLAUDE_SESSION_ID}/g, context?.sessionId ?? "");
|
|
243
|
+
result = result.replace(/\$\{CLAUDE_SKILL_DIR}/g, context?.skillDir ?? "");
|
|
244
|
+
return result;
|
|
140
245
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
this.commands = createBuiltinCommands();
|
|
246
|
+
async function preprocessShellCommands(content) {
|
|
247
|
+
const shellPattern = /!`([^`]+)`/g;
|
|
248
|
+
if (!shellPattern.test(content)) {
|
|
249
|
+
return content;
|
|
146
250
|
}
|
|
147
|
-
|
|
148
|
-
|
|
251
|
+
shellPattern.lastIndex = 0;
|
|
252
|
+
let result = content;
|
|
253
|
+
let match;
|
|
254
|
+
const matches = [];
|
|
255
|
+
while ((match = shellPattern.exec(content)) !== null) {
|
|
256
|
+
matches.push({ full: match[0], command: match[1] });
|
|
149
257
|
}
|
|
150
|
-
}
|
|
258
|
+
for (const { full, command } of matches) {
|
|
259
|
+
let output = "";
|
|
260
|
+
try {
|
|
261
|
+
output = (0, import_node_child_process.execSync)(command, {
|
|
262
|
+
timeout: 5e3,
|
|
263
|
+
encoding: "utf-8",
|
|
264
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
265
|
+
}).trimEnd();
|
|
266
|
+
} catch {
|
|
267
|
+
output = "";
|
|
268
|
+
}
|
|
269
|
+
result = result.replace(full, output);
|
|
270
|
+
}
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
async function buildSkillPrompt(input, registry, context) {
|
|
274
|
+
const parts = input.slice(1).split(/\s+/);
|
|
275
|
+
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
276
|
+
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
277
|
+
if (!skillCmd) return null;
|
|
278
|
+
const args = parts.slice(1).join(" ").trim();
|
|
279
|
+
const userInstruction = args || skillCmd.description;
|
|
280
|
+
if (skillCmd.skillContent) {
|
|
281
|
+
let processed = await preprocessShellCommands(skillCmd.skillContent);
|
|
282
|
+
processed = substituteVariables(processed, args, context);
|
|
283
|
+
return `<skill name="${cmd}">
|
|
284
|
+
${processed}
|
|
285
|
+
</skill>
|
|
151
286
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
287
|
+
Execute the "${cmd}" skill: ${userInstruction}`;
|
|
288
|
+
}
|
|
289
|
+
return `Use the "${cmd}" skill: ${userInstruction}`;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/ui/hooks/plugin-hooks-merger.ts
|
|
293
|
+
var import_node_path2 = require("path");
|
|
294
|
+
function buildPluginEnv(plugin) {
|
|
295
|
+
const dataDir = (0, import_node_path2.join)((0, import_node_path2.dirname)((0, import_node_path2.dirname)(plugin.pluginDir)), "data", plugin.manifest.name);
|
|
296
|
+
return {
|
|
297
|
+
CLAUDE_PLUGIN_ROOT: plugin.pluginDir,
|
|
298
|
+
CLAUDE_PLUGIN_PATH: plugin.pluginDir,
|
|
299
|
+
CLAUDE_PLUGIN_DATA: dataDir
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function resolvePluginRoot(group, pluginDir) {
|
|
303
|
+
if (Array.isArray(group.hooks)) {
|
|
304
|
+
return {
|
|
305
|
+
...group,
|
|
306
|
+
hooks: group.hooks.map((h) => {
|
|
307
|
+
if (typeof h.command === "string") {
|
|
308
|
+
return {
|
|
309
|
+
...h,
|
|
310
|
+
command: h.command.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginDir)
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
return h;
|
|
314
|
+
})
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
return group;
|
|
318
|
+
}
|
|
319
|
+
function mergePluginHooks(plugins) {
|
|
320
|
+
const merged = {};
|
|
321
|
+
for (const plugin of plugins) {
|
|
322
|
+
const hooksObj = plugin.hooks;
|
|
323
|
+
if (!hooksObj) continue;
|
|
324
|
+
const pluginEnv = buildPluginEnv(plugin);
|
|
325
|
+
const innerHooks = hooksObj.hooks ?? hooksObj;
|
|
326
|
+
for (const [event, groups] of Object.entries(innerHooks)) {
|
|
327
|
+
if (!Array.isArray(groups)) continue;
|
|
328
|
+
if (!merged[event]) merged[event] = [];
|
|
329
|
+
const resolved = groups.map((group) => {
|
|
330
|
+
const r = resolvePluginRoot(group, plugin.pluginDir);
|
|
331
|
+
r.env = pluginEnv;
|
|
332
|
+
return r;
|
|
333
|
+
});
|
|
334
|
+
merged[event].push(...resolved);
|
|
168
335
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
336
|
+
}
|
|
337
|
+
return merged;
|
|
338
|
+
}
|
|
339
|
+
function mergeHooksIntoConfig(configHooks, pluginHooks) {
|
|
340
|
+
const pluginKeys = Object.keys(pluginHooks);
|
|
341
|
+
if (pluginKeys.length === 0) return configHooks;
|
|
342
|
+
const merged = {};
|
|
343
|
+
for (const [event, groups] of Object.entries(pluginHooks)) {
|
|
344
|
+
merged[event] = [...groups];
|
|
345
|
+
}
|
|
346
|
+
if (configHooks) {
|
|
347
|
+
for (const [event, groups] of Object.entries(configHooks)) {
|
|
348
|
+
if (!Array.isArray(groups)) continue;
|
|
349
|
+
if (!merged[event]) merged[event] = [];
|
|
350
|
+
merged[event].push(...groups);
|
|
172
351
|
}
|
|
173
352
|
}
|
|
174
|
-
return
|
|
353
|
+
return merged;
|
|
175
354
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
355
|
+
|
|
356
|
+
// src/ui/hooks/useInteractiveSession.ts
|
|
357
|
+
var MAX_RENDERED_MESSAGES = 100;
|
|
358
|
+
function initializeSession(props, permissionHandler) {
|
|
359
|
+
const cwd = props.cwd ?? process.cwd();
|
|
360
|
+
const registry = new import_agent_sdk.CommandRegistry();
|
|
361
|
+
registry.addSource(new import_agent_sdk.BuiltinCommandSource());
|
|
362
|
+
registry.addSource(new import_agent_sdk.SkillCommandSource(cwd));
|
|
363
|
+
let pluginHooks = {};
|
|
364
|
+
const pluginsDir = (0, import_node_path3.join)((0, import_node_os.homedir)(), ".robota", "plugins");
|
|
365
|
+
const loader = new import_agent_sdk.BundlePluginLoader(pluginsDir);
|
|
366
|
+
try {
|
|
367
|
+
const plugins = loader.loadPluginsSync();
|
|
368
|
+
if (plugins.length > 0) {
|
|
369
|
+
registry.addSource(new PluginCommandSource(plugins));
|
|
370
|
+
pluginHooks = mergePluginHooks(plugins);
|
|
371
|
+
}
|
|
372
|
+
} catch {
|
|
191
373
|
}
|
|
192
|
-
|
|
374
|
+
const mergedConfig = {
|
|
375
|
+
...props.config,
|
|
376
|
+
hooks: mergeHooksIntoConfig(
|
|
377
|
+
props.config.hooks,
|
|
378
|
+
pluginHooks
|
|
379
|
+
)
|
|
380
|
+
};
|
|
381
|
+
const interactiveSession = new import_agent_sdk.InteractiveSession({
|
|
382
|
+
config: mergedConfig,
|
|
383
|
+
context: props.context,
|
|
384
|
+
projectInfo: props.projectInfo,
|
|
385
|
+
sessionStore: props.sessionStore,
|
|
386
|
+
permissionMode: props.permissionMode,
|
|
387
|
+
maxTurns: props.maxTurns,
|
|
388
|
+
cwd,
|
|
389
|
+
permissionHandler
|
|
390
|
+
});
|
|
391
|
+
return {
|
|
392
|
+
interactiveSession,
|
|
393
|
+
registry,
|
|
394
|
+
commandExecutor: new import_agent_sdk.SystemCommandExecutor(),
|
|
395
|
+
pluginHooks
|
|
396
|
+
};
|
|
193
397
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
398
|
+
function useInteractiveSession(props) {
|
|
399
|
+
const [messages, setMessages] = (0, import_react.useState)([]);
|
|
400
|
+
const addMessage = (0, import_react.useCallback)((msg) => {
|
|
401
|
+
setMessages((prev) => {
|
|
402
|
+
const updated = [...prev, msg];
|
|
403
|
+
return updated.length > MAX_RENDERED_MESSAGES ? updated.slice(-MAX_RENDERED_MESSAGES) : updated;
|
|
404
|
+
});
|
|
405
|
+
}, []);
|
|
406
|
+
const [streamingText, setStreamingText] = (0, import_react.useState)("");
|
|
407
|
+
const [activeTools, setActiveTools] = (0, import_react.useState)([]);
|
|
408
|
+
const [isThinking, setIsThinking] = (0, import_react.useState)(false);
|
|
409
|
+
const [isAborting, setIsAborting] = (0, import_react.useState)(false);
|
|
410
|
+
const [pendingPrompt, setPendingPrompt] = (0, import_react.useState)(null);
|
|
411
|
+
const [contextState, setContextState] = (0, import_react.useState)({ percentage: 0, usedTokens: 0, maxTokens: 0 });
|
|
412
|
+
const [permissionRequest, setPermissionRequest] = (0, import_react.useState)(null);
|
|
413
|
+
const permissionQueueRef = (0, import_react.useRef)([]);
|
|
414
|
+
const processingRef = (0, import_react.useRef)(false);
|
|
415
|
+
const processNextPermission = (0, import_react.useCallback)(() => {
|
|
416
|
+
if (processingRef.current) return;
|
|
417
|
+
const next = permissionQueueRef.current[0];
|
|
418
|
+
if (!next) {
|
|
419
|
+
setPermissionRequest(null);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
processingRef.current = true;
|
|
423
|
+
setPermissionRequest({
|
|
424
|
+
toolName: next.toolName,
|
|
425
|
+
toolArgs: next.toolArgs,
|
|
426
|
+
resolve: (result) => {
|
|
427
|
+
permissionQueueRef.current.shift();
|
|
428
|
+
processingRef.current = false;
|
|
429
|
+
setPermissionRequest(null);
|
|
430
|
+
next.resolve(result);
|
|
431
|
+
setTimeout(() => processNextPermission(), 0);
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
}, []);
|
|
435
|
+
const permissionHandler = (0, import_react.useCallback)(
|
|
436
|
+
(toolName, toolArgs) => new Promise((resolve) => {
|
|
437
|
+
permissionQueueRef.current.push({ toolName, toolArgs, resolve });
|
|
438
|
+
processNextPermission();
|
|
439
|
+
}),
|
|
440
|
+
[processNextPermission]
|
|
441
|
+
);
|
|
442
|
+
const stateRef = (0, import_react.useRef)(null);
|
|
443
|
+
if (stateRef.current === null) {
|
|
444
|
+
stateRef.current = initializeSession(props, permissionHandler);
|
|
200
445
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
446
|
+
const { interactiveSession, registry, commandExecutor } = stateRef.current;
|
|
447
|
+
(0, import_react.useEffect)(() => {
|
|
448
|
+
let streamBuf = "";
|
|
449
|
+
const onTextDelta = (delta) => {
|
|
450
|
+
streamBuf += delta;
|
|
451
|
+
setStreamingText(streamBuf);
|
|
452
|
+
};
|
|
453
|
+
const onToolStart = (state) => {
|
|
454
|
+
setActiveTools((prev) => [...prev, state]);
|
|
455
|
+
};
|
|
456
|
+
const onToolEnd = (state) => {
|
|
457
|
+
setActiveTools(
|
|
458
|
+
(prev) => prev.map((t) => t.toolName === state.toolName && t.isRunning ? state : t)
|
|
459
|
+
);
|
|
460
|
+
};
|
|
461
|
+
const onThinking = (thinking) => {
|
|
462
|
+
setIsThinking(thinking);
|
|
463
|
+
if (!thinking) {
|
|
464
|
+
setIsAborting(false);
|
|
465
|
+
streamBuf = "";
|
|
466
|
+
setStreamingText("");
|
|
467
|
+
setActiveTools([]);
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
const onComplete = (result) => {
|
|
471
|
+
setContextState({
|
|
472
|
+
percentage: result.contextState.usedPercentage,
|
|
473
|
+
usedTokens: result.contextState.usedTokens,
|
|
474
|
+
maxTokens: result.contextState.maxTokens
|
|
475
|
+
});
|
|
476
|
+
};
|
|
477
|
+
const onInterrupted = () => {
|
|
478
|
+
};
|
|
479
|
+
const onError = () => {
|
|
480
|
+
};
|
|
481
|
+
interactiveSession.on("text_delta", onTextDelta);
|
|
482
|
+
interactiveSession.on("tool_start", onToolStart);
|
|
483
|
+
interactiveSession.on("tool_end", onToolEnd);
|
|
484
|
+
interactiveSession.on("thinking", onThinking);
|
|
485
|
+
interactiveSession.on("complete", onComplete);
|
|
486
|
+
interactiveSession.on("interrupted", onInterrupted);
|
|
487
|
+
interactiveSession.on("error", onError);
|
|
488
|
+
return () => {
|
|
489
|
+
interactiveSession.off("text_delta", onTextDelta);
|
|
490
|
+
interactiveSession.off("tool_start", onToolStart);
|
|
491
|
+
interactiveSession.off("tool_end", onToolEnd);
|
|
492
|
+
interactiveSession.off("thinking", onThinking);
|
|
493
|
+
interactiveSession.off("complete", onComplete);
|
|
494
|
+
interactiveSession.off("interrupted", onInterrupted);
|
|
495
|
+
interactiveSession.off("error", onError);
|
|
496
|
+
};
|
|
497
|
+
}, [interactiveSession]);
|
|
498
|
+
(0, import_react.useEffect)(() => {
|
|
499
|
+
if (!isThinking) {
|
|
500
|
+
const sessionMessages = interactiveSession.getMessages();
|
|
501
|
+
if (sessionMessages.length > 0) {
|
|
502
|
+
setMessages(
|
|
503
|
+
sessionMessages.length > MAX_RENDERED_MESSAGES ? sessionMessages.slice(-MAX_RENDERED_MESSAGES) : [...sessionMessages]
|
|
504
|
+
);
|
|
210
505
|
}
|
|
506
|
+
setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
211
507
|
}
|
|
212
|
-
|
|
213
|
-
|
|
508
|
+
}, [isThinking, interactiveSession]);
|
|
509
|
+
const handleSubmit = (0, import_react.useCallback)(
|
|
510
|
+
async (input) => {
|
|
511
|
+
if (input.startsWith("/")) {
|
|
512
|
+
const parts = input.slice(1).split(/\s+/);
|
|
513
|
+
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
514
|
+
const args = parts.slice(1).join(" ");
|
|
515
|
+
const result = await commandExecutor.execute(cmd, interactiveSession, args);
|
|
516
|
+
if (result) {
|
|
517
|
+
addMessage((0, import_agent_core.createSystemMessage)(result.message));
|
|
518
|
+
const effects = interactiveSession;
|
|
519
|
+
if (result.data?.modelId) {
|
|
520
|
+
effects._pendingModelId = result.data.modelId;
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (result.data?.language) {
|
|
524
|
+
effects._pendingLanguage = result.data.language;
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
if (result.data?.resetRequested) {
|
|
528
|
+
effects._resetRequested = true;
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const ctx = interactiveSession.getContextState();
|
|
532
|
+
setContextState({
|
|
533
|
+
percentage: ctx.usedPercentage,
|
|
534
|
+
usedTokens: ctx.usedTokens,
|
|
535
|
+
maxTokens: ctx.maxTokens
|
|
536
|
+
});
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
540
|
+
if (skillCmd) {
|
|
541
|
+
const prompt = await buildSkillPrompt(input, registry);
|
|
542
|
+
if (prompt) {
|
|
543
|
+
await interactiveSession.submit(prompt);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (cmd === "exit") {
|
|
548
|
+
interactiveSession._exitRequested = true;
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
if (cmd === "plugin") {
|
|
552
|
+
interactiveSession._triggerPluginTUI = true;
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
addMessage((0, import_agent_core.createSystemMessage)(`Unknown command "/${cmd}". Type /help for help.`));
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
await interactiveSession.submit(input);
|
|
559
|
+
},
|
|
560
|
+
[interactiveSession, commandExecutor, registry, addMessage]
|
|
561
|
+
);
|
|
562
|
+
const handleAbort = (0, import_react.useCallback)(() => {
|
|
563
|
+
setIsAborting(true);
|
|
564
|
+
interactiveSession.abort();
|
|
565
|
+
}, [interactiveSession]);
|
|
566
|
+
const handleCancelQueue = (0, import_react.useCallback)(() => {
|
|
567
|
+
interactiveSession.cancelQueue();
|
|
568
|
+
setPendingPrompt(null);
|
|
569
|
+
}, [interactiveSession]);
|
|
570
|
+
if (contextState.maxTokens === 0) {
|
|
571
|
+
const ctx = interactiveSession.getContextState();
|
|
572
|
+
setContextState({
|
|
573
|
+
percentage: ctx.usedPercentage,
|
|
574
|
+
usedTokens: ctx.usedTokens,
|
|
575
|
+
maxTokens: ctx.maxTokens
|
|
576
|
+
});
|
|
214
577
|
}
|
|
215
|
-
|
|
578
|
+
return {
|
|
579
|
+
interactiveSession,
|
|
580
|
+
registry,
|
|
581
|
+
commandExecutor,
|
|
582
|
+
pluginHooks: stateRef.current.pluginHooks,
|
|
583
|
+
messages,
|
|
584
|
+
addMessage,
|
|
585
|
+
setMessages,
|
|
586
|
+
streamingText,
|
|
587
|
+
activeTools,
|
|
588
|
+
isThinking,
|
|
589
|
+
isAborting,
|
|
590
|
+
pendingPrompt,
|
|
591
|
+
permissionRequest,
|
|
592
|
+
contextState,
|
|
593
|
+
handleSubmit,
|
|
594
|
+
handleAbort,
|
|
595
|
+
handleCancelQueue
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// src/ui/hooks/usePluginCallbacks.ts
|
|
600
|
+
var import_react2 = require("react");
|
|
601
|
+
var import_node_os2 = require("os");
|
|
602
|
+
var import_node_path4 = require("path");
|
|
603
|
+
var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
|
|
604
|
+
function usePluginCallbacks(cwd) {
|
|
605
|
+
return (0, import_react2.useMemo)(() => {
|
|
606
|
+
const home = (0, import_node_os2.homedir)();
|
|
607
|
+
const pluginsDir = (0, import_node_path4.join)(home, ".robota", "plugins");
|
|
608
|
+
const userSettingsPath = (0, import_node_path4.join)(home, ".robota", "settings.json");
|
|
609
|
+
const settingsStore = new import_agent_sdk2.PluginSettingsStore(userSettingsPath);
|
|
610
|
+
const marketplace = new import_agent_sdk2.MarketplaceClient({ pluginsDir });
|
|
611
|
+
const installer = new import_agent_sdk2.BundlePluginInstaller({
|
|
612
|
+
pluginsDir,
|
|
613
|
+
settingsStore,
|
|
614
|
+
marketplaceClient: marketplace
|
|
615
|
+
});
|
|
616
|
+
const loader = new import_agent_sdk2.BundlePluginLoader(pluginsDir);
|
|
617
|
+
return {
|
|
618
|
+
listInstalled: async () => {
|
|
619
|
+
const plugins = await loader.loadAll();
|
|
620
|
+
const enabledMap = settingsStore.getEnabledPlugins();
|
|
621
|
+
return plugins.map((p) => {
|
|
622
|
+
const parts = p.pluginDir.split("/");
|
|
623
|
+
const cacheIdx = parts.indexOf("cache");
|
|
624
|
+
const marketplaceName = cacheIdx >= 0 ? parts[cacheIdx + 1] : "";
|
|
625
|
+
const fullId = marketplaceName ? `${p.manifest.name}@${marketplaceName}` : p.manifest.name;
|
|
626
|
+
return {
|
|
627
|
+
name: fullId,
|
|
628
|
+
description: p.manifest.description,
|
|
629
|
+
enabled: enabledMap[fullId] !== false && enabledMap[p.manifest.name] !== false
|
|
630
|
+
};
|
|
631
|
+
});
|
|
632
|
+
},
|
|
633
|
+
listAvailablePlugins: async (marketplaceName) => {
|
|
634
|
+
let manifest;
|
|
635
|
+
try {
|
|
636
|
+
manifest = marketplace.fetchManifest(marketplaceName);
|
|
637
|
+
} catch {
|
|
638
|
+
return [];
|
|
639
|
+
}
|
|
640
|
+
const installed = installer.getInstalledPlugins();
|
|
641
|
+
const installedNames = new Set(Object.values(installed).map((r) => r.pluginName));
|
|
642
|
+
return manifest.plugins.map((p) => ({
|
|
643
|
+
name: p.name,
|
|
644
|
+
description: p.description,
|
|
645
|
+
installed: installedNames.has(p.name)
|
|
646
|
+
}));
|
|
647
|
+
},
|
|
648
|
+
install: async (pluginId, scope) => {
|
|
649
|
+
const [name, marketplaceName] = pluginId.split("@");
|
|
650
|
+
if (!name || !marketplaceName) {
|
|
651
|
+
throw new Error("Plugin ID must be in format: name@marketplace");
|
|
652
|
+
}
|
|
653
|
+
if (scope === "project") {
|
|
654
|
+
const projectPluginsDir = (0, import_node_path4.join)(cwd, ".robota", "plugins");
|
|
655
|
+
const projectInstaller = new import_agent_sdk2.BundlePluginInstaller({
|
|
656
|
+
pluginsDir: projectPluginsDir,
|
|
657
|
+
settingsStore,
|
|
658
|
+
marketplaceClient: marketplace
|
|
659
|
+
});
|
|
660
|
+
await projectInstaller.install(name, marketplaceName);
|
|
661
|
+
} else {
|
|
662
|
+
await installer.install(name, marketplaceName);
|
|
663
|
+
}
|
|
664
|
+
},
|
|
665
|
+
uninstall: async (pluginId) => {
|
|
666
|
+
await installer.uninstall(pluginId);
|
|
667
|
+
},
|
|
668
|
+
enable: async (pluginId) => {
|
|
669
|
+
await installer.enable(pluginId);
|
|
670
|
+
},
|
|
671
|
+
disable: async (pluginId) => {
|
|
672
|
+
await installer.disable(pluginId);
|
|
673
|
+
},
|
|
674
|
+
marketplaceAdd: async (source) => {
|
|
675
|
+
if (source.includes("/") && !source.includes(":")) {
|
|
676
|
+
return marketplace.addMarketplace({ type: "github", repo: source });
|
|
677
|
+
} else {
|
|
678
|
+
return marketplace.addMarketplace({ type: "git", url: source });
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
marketplaceRemove: async (name) => {
|
|
682
|
+
const installedFromMarketplace = installer.getPluginsByMarketplace(name);
|
|
683
|
+
for (const record of installedFromMarketplace) {
|
|
684
|
+
await installer.uninstall(`${record.pluginName}@${record.marketplace}`);
|
|
685
|
+
}
|
|
686
|
+
marketplace.removeMarketplace(name);
|
|
687
|
+
},
|
|
688
|
+
marketplaceUpdate: async (name) => {
|
|
689
|
+
marketplace.updateMarketplace(name);
|
|
690
|
+
},
|
|
691
|
+
marketplaceList: async () => {
|
|
692
|
+
return marketplace.listMarketplaces().map((m) => ({
|
|
693
|
+
name: m.name,
|
|
694
|
+
type: m.source.type
|
|
695
|
+
}));
|
|
696
|
+
},
|
|
697
|
+
reloadPlugins: async () => {
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
}, [cwd]);
|
|
701
|
+
}
|
|
216
702
|
|
|
217
703
|
// src/ui/MessageList.tsx
|
|
218
|
-
var
|
|
704
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
705
|
+
var import_ink2 = require("ink");
|
|
706
|
+
var import_agent_core2 = require("@robota-sdk/agent-core");
|
|
219
707
|
|
|
220
708
|
// src/ui/render-markdown.ts
|
|
221
709
|
var import_marked = require("marked");
|
|
@@ -228,54 +716,161 @@ function renderMarkdown(md) {
|
|
|
228
716
|
return typeof result === "string" ? result.trimEnd() : md;
|
|
229
717
|
}
|
|
230
718
|
|
|
231
|
-
// src/ui/
|
|
719
|
+
// src/ui/DiffBlock.tsx
|
|
720
|
+
var import_ink = require("ink");
|
|
232
721
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
722
|
+
var MAX_DIFF_LINES = 12;
|
|
723
|
+
var TRUNCATED_SHOW = 10;
|
|
724
|
+
function DiffBlock({ file, lines }) {
|
|
725
|
+
const truncated = lines.length > MAX_DIFF_LINES;
|
|
726
|
+
const visible = truncated ? lines.slice(0, TRUNCATED_SHOW) : lines;
|
|
727
|
+
const remaining = lines.length - TRUNCATED_SHOW;
|
|
728
|
+
const maxLineNum = Math.max(...visible.map((l) => l.lineNumber), 0);
|
|
729
|
+
const numWidth = String(maxLineNum).length;
|
|
730
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { flexDirection: "column", marginLeft: 4, children: [
|
|
731
|
+
file && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "white", dimColor: true, children: [
|
|
732
|
+
"\u2502 ",
|
|
733
|
+
file
|
|
734
|
+
] }),
|
|
735
|
+
visible.map((line, i) => {
|
|
736
|
+
const lineNum = String(line.lineNumber).padStart(numWidth, " ");
|
|
737
|
+
if (line.type === "context") {
|
|
738
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "white", dimColor: true, children: [
|
|
739
|
+
"\u2502 ",
|
|
740
|
+
lineNum,
|
|
741
|
+
" ",
|
|
742
|
+
line.text
|
|
743
|
+
] }, i);
|
|
744
|
+
}
|
|
745
|
+
const prefix = line.type === "remove" ? "-" : "+";
|
|
746
|
+
const bgColor = line.type === "remove" ? "#5c1a1a" : "#1a3d1a";
|
|
747
|
+
const fgColor = line.type === "remove" ? "#ff9999" : "#99ff99";
|
|
748
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: fgColor, backgroundColor: bgColor, children: [
|
|
749
|
+
"\u2502 ",
|
|
750
|
+
lineNum,
|
|
751
|
+
" ",
|
|
752
|
+
prefix,
|
|
753
|
+
" ",
|
|
754
|
+
line.text
|
|
755
|
+
] }, i);
|
|
756
|
+
}),
|
|
757
|
+
truncated && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "white", dimColor: true, children: [
|
|
758
|
+
"\u2502 ... and ",
|
|
759
|
+
remaining,
|
|
760
|
+
" more lines"
|
|
761
|
+
] })
|
|
762
|
+
] });
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/ui/MessageList.tsx
|
|
766
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
233
767
|
function RoleLabel({ role }) {
|
|
234
768
|
switch (role) {
|
|
235
769
|
case "user":
|
|
236
|
-
return /* @__PURE__ */ (0,
|
|
770
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "green", bold: true, children: [
|
|
237
771
|
"You:",
|
|
238
772
|
" "
|
|
239
773
|
] });
|
|
240
774
|
case "assistant":
|
|
241
|
-
return /* @__PURE__ */ (0,
|
|
775
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "cyan", bold: true, children: [
|
|
242
776
|
"Robota:",
|
|
243
777
|
" "
|
|
244
778
|
] });
|
|
245
779
|
case "system":
|
|
246
|
-
return /* @__PURE__ */ (0,
|
|
780
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "yellow", bold: true, children: [
|
|
247
781
|
"System:",
|
|
248
782
|
" "
|
|
249
783
|
] });
|
|
250
784
|
case "tool":
|
|
251
|
-
return /* @__PURE__ */ (0,
|
|
785
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", bold: true, children: [
|
|
252
786
|
"Tool:",
|
|
253
787
|
" "
|
|
254
788
|
] });
|
|
255
789
|
}
|
|
256
790
|
}
|
|
257
|
-
function
|
|
258
|
-
|
|
259
|
-
/* @__PURE__ */ (0,
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
791
|
+
function ToolMessage({ message }) {
|
|
792
|
+
if (!(0, import_agent_core2.isToolMessage)(message)) {
|
|
793
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
|
|
794
|
+
}
|
|
795
|
+
const toolName = message.name;
|
|
796
|
+
const content = message.content;
|
|
797
|
+
let summaries = null;
|
|
798
|
+
try {
|
|
799
|
+
const parsed = JSON.parse(content);
|
|
800
|
+
if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0].line === "string") {
|
|
801
|
+
summaries = parsed;
|
|
802
|
+
}
|
|
803
|
+
} catch {
|
|
804
|
+
}
|
|
805
|
+
if (summaries) {
|
|
806
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
807
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { children: [
|
|
808
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", bold: true, children: [
|
|
809
|
+
"Tool:",
|
|
810
|
+
" "
|
|
811
|
+
] }),
|
|
812
|
+
toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
|
|
813
|
+
"[",
|
|
814
|
+
toolName,
|
|
815
|
+
"]"
|
|
816
|
+
] })
|
|
817
|
+
] }),
|
|
818
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
|
|
819
|
+
summaries.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", children: [
|
|
820
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "green", children: [
|
|
821
|
+
" ",
|
|
822
|
+
"\u2713",
|
|
823
|
+
" ",
|
|
824
|
+
s.line
|
|
825
|
+
] }),
|
|
826
|
+
s.diffLines && s.diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DiffBlock, { file: s.diffFile, lines: s.diffLines })
|
|
827
|
+
] }, i))
|
|
828
|
+
] });
|
|
829
|
+
}
|
|
830
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
831
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
832
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { children: [
|
|
833
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", bold: true, children: [
|
|
834
|
+
"Tool:",
|
|
265
835
|
" "
|
|
836
|
+
] }),
|
|
837
|
+
toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
|
|
838
|
+
"[",
|
|
839
|
+
toolName,
|
|
840
|
+
"]"
|
|
266
841
|
] })
|
|
267
842
|
] }),
|
|
268
|
-
/* @__PURE__ */ (0,
|
|
269
|
-
|
|
843
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
|
|
844
|
+
lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "green", children: [
|
|
845
|
+
" ",
|
|
846
|
+
"\u2713",
|
|
847
|
+
" ",
|
|
848
|
+
line
|
|
849
|
+
] }, i))
|
|
270
850
|
] });
|
|
271
851
|
}
|
|
852
|
+
var MessageItem = import_react3.default.memo(function MessageItem2({
|
|
853
|
+
message
|
|
854
|
+
}) {
|
|
855
|
+
if ((0, import_agent_core2.isToolMessage)(message)) {
|
|
856
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolMessage, { message });
|
|
857
|
+
}
|
|
858
|
+
const content = message.content ?? "";
|
|
859
|
+
const isInterrupted = message.state === "interrupted";
|
|
860
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
861
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }) }),
|
|
862
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
|
|
863
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { wrap: "wrap", children: (0, import_agent_core2.isAssistantMessage)(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
|
|
864
|
+
] });
|
|
865
|
+
});
|
|
272
866
|
function MessageList({ messages }) {
|
|
273
|
-
return /* @__PURE__ */ (0,
|
|
867
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { flexDirection: "column", children: messages.map((msg) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MessageItem, { message: msg }, msg.id)) });
|
|
274
868
|
}
|
|
275
869
|
|
|
276
870
|
// src/ui/StatusBar.tsx
|
|
277
|
-
var
|
|
278
|
-
var
|
|
871
|
+
var import_ink3 = require("ink");
|
|
872
|
+
var import_agent_core3 = require("@robota-sdk/agent-core");
|
|
873
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
279
874
|
var CONTEXT_YELLOW_THRESHOLD = 70;
|
|
280
875
|
var CONTEXT_RED_THRESHOLD = 90;
|
|
281
876
|
function getContextColor(percentage) {
|
|
@@ -294,8 +889,8 @@ function StatusBar({
|
|
|
294
889
|
contextMaxTokens
|
|
295
890
|
}) {
|
|
296
891
|
const contextColor = getContextColor(contextPercentage);
|
|
297
|
-
return /* @__PURE__ */ (0,
|
|
298
|
-
|
|
892
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
893
|
+
import_ink3.Box,
|
|
299
894
|
{
|
|
300
895
|
borderStyle: "single",
|
|
301
896
|
borderColor: "gray",
|
|
@@ -303,26 +898,26 @@ function StatusBar({
|
|
|
303
898
|
paddingRight: 1,
|
|
304
899
|
justifyContent: "space-between",
|
|
305
900
|
children: [
|
|
306
|
-
/* @__PURE__ */ (0,
|
|
307
|
-
/* @__PURE__ */ (0,
|
|
901
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink3.Text, { children: [
|
|
902
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { color: "cyan", bold: true, children: "Mode:" }),
|
|
308
903
|
" ",
|
|
309
|
-
/* @__PURE__ */ (0,
|
|
904
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { children: permissionMode }),
|
|
310
905
|
" | ",
|
|
311
|
-
/* @__PURE__ */ (0,
|
|
906
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { dimColor: true, children: modelName }),
|
|
312
907
|
" | ",
|
|
313
|
-
/* @__PURE__ */ (0,
|
|
908
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink3.Text, { color: contextColor, children: [
|
|
314
909
|
"Context: ",
|
|
315
910
|
Math.round(contextPercentage),
|
|
316
911
|
"% (",
|
|
317
|
-
(
|
|
318
|
-
"
|
|
319
|
-
(
|
|
320
|
-
"
|
|
912
|
+
(0, import_agent_core3.formatTokenCount)(contextUsedTokens),
|
|
913
|
+
"/",
|
|
914
|
+
(0, import_agent_core3.formatTokenCount)(contextMaxTokens),
|
|
915
|
+
")"
|
|
321
916
|
] })
|
|
322
917
|
] }),
|
|
323
|
-
/* @__PURE__ */ (0,
|
|
324
|
-
isThinking && /* @__PURE__ */ (0,
|
|
325
|
-
/* @__PURE__ */ (0,
|
|
918
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink3.Text, { children: [
|
|
919
|
+
isThinking && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { color: "yellow", children: "Thinking... " }),
|
|
920
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink3.Text, { dimColor: true, children: [
|
|
326
921
|
"msgs: ",
|
|
327
922
|
messageCount
|
|
328
923
|
] })
|
|
@@ -333,146 +928,224 @@ function StatusBar({
|
|
|
333
928
|
}
|
|
334
929
|
|
|
335
930
|
// src/ui/InputArea.tsx
|
|
336
|
-
var
|
|
337
|
-
var
|
|
931
|
+
var import_react6 = __toESM(require("react"), 1);
|
|
932
|
+
var import_ink7 = require("ink");
|
|
338
933
|
|
|
339
934
|
// src/ui/CjkTextInput.tsx
|
|
340
|
-
var
|
|
341
|
-
var
|
|
935
|
+
var import_react4 = require("react");
|
|
936
|
+
var import_ink4 = require("ink");
|
|
937
|
+
var import_chalk = __toESM(require("chalk"), 1);
|
|
342
938
|
var import_string_width = __toESM(require("string-width"), 1);
|
|
343
|
-
var
|
|
344
|
-
var
|
|
939
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
940
|
+
var PASTE_START = "[200~";
|
|
941
|
+
var PASTE_END = "[201~";
|
|
942
|
+
function filterPrintable(input) {
|
|
943
|
+
if (!input || input.length === 0) return "";
|
|
944
|
+
return input.replace(/[\x00-\x1f\x7f]/g, "");
|
|
945
|
+
}
|
|
946
|
+
function insertAtCursor(value, cursor, input) {
|
|
947
|
+
const next = value.slice(0, cursor) + input + value.slice(cursor);
|
|
948
|
+
return { value: next, cursor: cursor + input.length };
|
|
949
|
+
}
|
|
950
|
+
function displayOffset(chars, charIndex, width) {
|
|
951
|
+
let offset = 0;
|
|
952
|
+
for (let i = 0; i < charIndex && i < chars.length; i++) {
|
|
953
|
+
const w = (0, import_string_width.default)(chars[i]);
|
|
954
|
+
const col = offset % width;
|
|
955
|
+
if (col + w > width) offset += width - col;
|
|
956
|
+
offset += w;
|
|
957
|
+
}
|
|
958
|
+
return offset;
|
|
959
|
+
}
|
|
960
|
+
function charIndexAtDisplayOffset(chars, target, width) {
|
|
961
|
+
let offset = 0;
|
|
962
|
+
for (let i = 0; i < chars.length; i++) {
|
|
963
|
+
if (offset >= target) return i;
|
|
964
|
+
const w = (0, import_string_width.default)(chars[i]);
|
|
965
|
+
const col = offset % width;
|
|
966
|
+
if (col + w > width) offset += width - col;
|
|
967
|
+
offset += w;
|
|
968
|
+
}
|
|
969
|
+
return chars.length;
|
|
970
|
+
}
|
|
345
971
|
function CjkTextInput({
|
|
346
972
|
value,
|
|
347
973
|
onChange,
|
|
348
974
|
onSubmit,
|
|
975
|
+
onPaste,
|
|
349
976
|
placeholder = "",
|
|
350
977
|
focus = true,
|
|
351
|
-
showCursor = true
|
|
978
|
+
showCursor = true,
|
|
979
|
+
availableWidth
|
|
352
980
|
}) {
|
|
353
|
-
const valueRef = (0,
|
|
354
|
-
const cursorRef = (0,
|
|
355
|
-
const [, forceRender] = (0,
|
|
356
|
-
const
|
|
981
|
+
const valueRef = (0, import_react4.useRef)(value);
|
|
982
|
+
const cursorRef = (0, import_react4.useRef)(value.length);
|
|
983
|
+
const [, forceRender] = (0, import_react4.useState)(0);
|
|
984
|
+
const isPastingRef = (0, import_react4.useRef)(false);
|
|
985
|
+
const pasteBufferRef = (0, import_react4.useRef)("");
|
|
357
986
|
if (value !== valueRef.current) {
|
|
358
987
|
valueRef.current = value;
|
|
359
988
|
if (cursorRef.current > value.length) {
|
|
360
989
|
cursorRef.current = value.length;
|
|
361
990
|
}
|
|
362
991
|
}
|
|
363
|
-
(0,
|
|
992
|
+
(0, import_ink4.useInput)(
|
|
364
993
|
(input, key) => {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
if (cursorRef.current > 0) {
|
|
374
|
-
cursorRef.current -= 1;
|
|
375
|
-
forceRender((n) => n + 1);
|
|
994
|
+
try {
|
|
995
|
+
if (input === PASTE_START || input.startsWith(PASTE_START)) {
|
|
996
|
+
isPastingRef.current = true;
|
|
997
|
+
const afterMarker = input.slice(PASTE_START.length);
|
|
998
|
+
if (afterMarker.length > 0) {
|
|
999
|
+
pasteBufferRef.current += afterMarker;
|
|
1000
|
+
}
|
|
1001
|
+
return;
|
|
376
1002
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
1003
|
+
if (isPastingRef.current) {
|
|
1004
|
+
if (input === PASTE_END || input.includes(PASTE_END)) {
|
|
1005
|
+
const beforeMarker = input.split(PASTE_END)[0] ?? "";
|
|
1006
|
+
pasteBufferRef.current += beforeMarker;
|
|
1007
|
+
const text = pasteBufferRef.current.replace(/\r\n?/g, "\n");
|
|
1008
|
+
pasteBufferRef.current = "";
|
|
1009
|
+
isPastingRef.current = false;
|
|
1010
|
+
if (text.length > 0) {
|
|
1011
|
+
if (text.includes("\n") && onPaste) {
|
|
1012
|
+
onPaste(text);
|
|
1013
|
+
} else {
|
|
1014
|
+
const printable2 = filterPrintable(text);
|
|
1015
|
+
if (printable2.length > 0) {
|
|
1016
|
+
const result2 = insertAtCursor(valueRef.current, cursorRef.current, printable2);
|
|
1017
|
+
cursorRef.current = result2.cursor;
|
|
1018
|
+
valueRef.current = result2.value;
|
|
1019
|
+
onChange(result2.value);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
} else {
|
|
1024
|
+
pasteBufferRef.current += input;
|
|
1025
|
+
}
|
|
1026
|
+
return;
|
|
383
1027
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
if (key.backspace || key.delete) {
|
|
387
|
-
if (cursorRef.current > 0) {
|
|
388
|
-
const v2 = valueRef.current;
|
|
389
|
-
const next2 = v2.slice(0, cursorRef.current - 1) + v2.slice(cursorRef.current);
|
|
390
|
-
cursorRef.current -= 1;
|
|
391
|
-
valueRef.current = next2;
|
|
392
|
-
onChange(next2);
|
|
1028
|
+
if (key.ctrl && input === "c" || key.tab || key.shift && key.tab) {
|
|
1029
|
+
return;
|
|
393
1030
|
}
|
|
394
|
-
|
|
1031
|
+
if (key.upArrow || key.downArrow) {
|
|
1032
|
+
if (availableWidth && availableWidth > 0) {
|
|
1033
|
+
const chars = [...valueRef.current];
|
|
1034
|
+
const offset = displayOffset(chars, cursorRef.current, availableWidth);
|
|
1035
|
+
const target = key.upArrow ? offset - availableWidth : offset + availableWidth;
|
|
1036
|
+
if (target >= 0) {
|
|
1037
|
+
const newCursor = charIndexAtDisplayOffset(chars, target, availableWidth);
|
|
1038
|
+
if (newCursor !== cursorRef.current) {
|
|
1039
|
+
cursorRef.current = newCursor;
|
|
1040
|
+
forceRender((n) => n + 1);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
if (key.return) {
|
|
1047
|
+
onSubmit?.(valueRef.current);
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
if (input.length > 1 && (input.includes("\n") || input.includes("\r")) && onPaste) {
|
|
1051
|
+
onPaste(input.replace(/\r\n?/g, "\n"));
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
if (key.leftArrow) {
|
|
1055
|
+
if (cursorRef.current > 0) {
|
|
1056
|
+
cursorRef.current -= 1;
|
|
1057
|
+
forceRender((n) => n + 1);
|
|
1058
|
+
}
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
if (key.rightArrow) {
|
|
1062
|
+
if (cursorRef.current < valueRef.current.length) {
|
|
1063
|
+
cursorRef.current += 1;
|
|
1064
|
+
forceRender((n) => n + 1);
|
|
1065
|
+
}
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
if (key.backspace || key.delete) {
|
|
1069
|
+
if (cursorRef.current > 0) {
|
|
1070
|
+
const v = valueRef.current;
|
|
1071
|
+
const next = v.slice(0, cursorRef.current - 1) + v.slice(cursorRef.current);
|
|
1072
|
+
cursorRef.current -= 1;
|
|
1073
|
+
valueRef.current = next;
|
|
1074
|
+
onChange(next);
|
|
1075
|
+
}
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
const printable = filterPrintable(input);
|
|
1079
|
+
if (printable.length === 0) return;
|
|
1080
|
+
const result = insertAtCursor(valueRef.current, cursorRef.current, printable);
|
|
1081
|
+
cursorRef.current = result.cursor;
|
|
1082
|
+
valueRef.current = result.value;
|
|
1083
|
+
onChange(result.value);
|
|
1084
|
+
} catch {
|
|
395
1085
|
}
|
|
396
|
-
const v = valueRef.current;
|
|
397
|
-
const c = cursorRef.current;
|
|
398
|
-
const next = v.slice(0, c) + input + v.slice(c);
|
|
399
|
-
cursorRef.current = c + input.length;
|
|
400
|
-
valueRef.current = next;
|
|
401
|
-
onChange(next);
|
|
402
1086
|
},
|
|
403
1087
|
{ isActive: focus }
|
|
404
1088
|
);
|
|
405
|
-
|
|
406
|
-
const textBeforeCursor = [...valueRef.current].slice(0, cursorRef.current).join("");
|
|
407
|
-
const cursorX = 4 + (0, import_string_width.default)(textBeforeCursor);
|
|
408
|
-
setCursorPosition({ x: cursorX, y: 0 });
|
|
409
|
-
}
|
|
410
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { children: renderWithCursor(valueRef.current, cursorRef.current, placeholder, showCursor && focus) });
|
|
1089
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink4.Text, { children: renderWithCursor(valueRef.current, cursorRef.current, placeholder, showCursor && focus) });
|
|
411
1090
|
}
|
|
412
1091
|
function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
|
|
413
1092
|
if (!showCursor) {
|
|
414
|
-
return value.length > 0 ? value : placeholder ?
|
|
1093
|
+
return value.length > 0 ? value : placeholder ? import_chalk.default.gray(placeholder) : "";
|
|
415
1094
|
}
|
|
416
1095
|
if (value.length === 0) {
|
|
417
1096
|
if (placeholder.length > 0) {
|
|
418
|
-
return
|
|
1097
|
+
return import_chalk.default.inverse(placeholder[0]) + import_chalk.default.gray(placeholder.slice(1));
|
|
419
1098
|
}
|
|
420
|
-
return
|
|
1099
|
+
return import_chalk.default.inverse(" ");
|
|
421
1100
|
}
|
|
422
1101
|
const chars = [...value];
|
|
423
1102
|
let rendered = "";
|
|
424
1103
|
for (let i = 0; i < chars.length; i++) {
|
|
425
1104
|
const char = chars[i] ?? "";
|
|
426
|
-
rendered += i === cursorOffset ?
|
|
1105
|
+
rendered += i === cursorOffset ? import_chalk.default.inverse(char) : char;
|
|
427
1106
|
}
|
|
428
1107
|
if (cursorOffset >= chars.length) {
|
|
429
|
-
rendered +=
|
|
1108
|
+
rendered += import_chalk.default.inverse(" ");
|
|
430
1109
|
}
|
|
431
1110
|
return rendered;
|
|
432
1111
|
}
|
|
433
1112
|
|
|
434
1113
|
// src/ui/WaveText.tsx
|
|
435
|
-
var
|
|
436
|
-
var
|
|
437
|
-
var
|
|
1114
|
+
var import_react5 = require("react");
|
|
1115
|
+
var import_ink5 = require("ink");
|
|
1116
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
438
1117
|
var WAVE_COLORS = ["#666666", "#888888", "#aaaaaa", "#888888"];
|
|
439
1118
|
var INTERVAL_MS = 400;
|
|
440
1119
|
var CHARS_PER_GROUP = 4;
|
|
441
1120
|
function WaveText({ text }) {
|
|
442
|
-
const [tick, setTick] = (0,
|
|
443
|
-
(0,
|
|
1121
|
+
const [tick, setTick] = (0, import_react5.useState)(0);
|
|
1122
|
+
(0, import_react5.useEffect)(() => {
|
|
444
1123
|
const timer = setInterval(() => {
|
|
445
1124
|
setTick((prev) => prev + 1);
|
|
446
1125
|
}, INTERVAL_MS);
|
|
447
1126
|
return () => clearInterval(timer);
|
|
448
1127
|
}, []);
|
|
449
1128
|
const chars = [...text];
|
|
450
|
-
return /* @__PURE__ */ (0,
|
|
1129
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { children: chars.map((char, i) => {
|
|
451
1130
|
const group = Math.floor(i / CHARS_PER_GROUP);
|
|
452
1131
|
const colorIndex = (tick + group) % WAVE_COLORS.length;
|
|
453
|
-
return /* @__PURE__ */ (0,
|
|
1132
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { color: WAVE_COLORS[colorIndex], children: char }, i);
|
|
454
1133
|
}) });
|
|
455
1134
|
}
|
|
456
1135
|
|
|
457
1136
|
// src/ui/SlashAutocomplete.tsx
|
|
458
|
-
var
|
|
459
|
-
var
|
|
1137
|
+
var import_ink6 = require("ink");
|
|
1138
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
460
1139
|
var MAX_VISIBLE = 8;
|
|
461
1140
|
function CommandRow(props) {
|
|
462
1141
|
const { cmd, isSelected, showSlash } = props;
|
|
463
|
-
const prefix = showSlash ? "/" : "";
|
|
464
1142
|
const indicator = isSelected ? "\u25B8 " : " ";
|
|
465
1143
|
const nameColor = isSelected ? "cyan" : void 0;
|
|
466
1144
|
const dimmed = !isSelected;
|
|
467
|
-
return /* @__PURE__ */ (0,
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
cmd.name
|
|
472
|
-
] }),
|
|
473
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { dimColor: dimmed, children: " " }),
|
|
474
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { color: nameColor, dimColor: dimmed, children: cmd.description })
|
|
475
|
-
] });
|
|
1145
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ink6.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ink6.Text, { color: nameColor, dimColor: dimmed, children: [
|
|
1146
|
+
indicator,
|
|
1147
|
+
showSlash ? `/${cmd.name} ${cmd.description}` : cmd.description
|
|
1148
|
+
] }) });
|
|
476
1149
|
}
|
|
477
1150
|
function SlashAutocomplete({
|
|
478
1151
|
commands,
|
|
@@ -483,7 +1156,7 @@ function SlashAutocomplete({
|
|
|
483
1156
|
if (!visible || commands.length === 0) return null;
|
|
484
1157
|
const scrollOffset = computeScrollOffset(selectedIndex, commands.length);
|
|
485
1158
|
const visibleCommands = commands.slice(scrollOffset, scrollOffset + MAX_VISIBLE);
|
|
486
|
-
return /* @__PURE__ */ (0,
|
|
1159
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ink6.Box, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: visibleCommands.map((cmd, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
487
1160
|
CommandRow,
|
|
488
1161
|
{
|
|
489
1162
|
cmd,
|
|
@@ -500,8 +1173,14 @@ function computeScrollOffset(selectedIndex, total) {
|
|
|
500
1173
|
return Math.min(selectedIndex - MAX_VISIBLE + 1, maxOffset);
|
|
501
1174
|
}
|
|
502
1175
|
|
|
1176
|
+
// src/utils/paste-labels.ts
|
|
1177
|
+
var PASTE_LABEL_RE = /\[Pasted text #(\d+)(?: \+\d+ lines)?\]/g;
|
|
1178
|
+
function expandPasteLabels(text, store) {
|
|
1179
|
+
return text.replace(PASTE_LABEL_RE, (_, id) => store.get(Number(id)) ?? "");
|
|
1180
|
+
}
|
|
1181
|
+
|
|
503
1182
|
// src/ui/InputArea.tsx
|
|
504
|
-
var
|
|
1183
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
505
1184
|
function parseSlashInput(value) {
|
|
506
1185
|
if (!value.startsWith("/")) return { isSlash: false, parentCommand: "", filter: "" };
|
|
507
1186
|
const afterSlash = value.slice(1);
|
|
@@ -512,16 +1191,16 @@ function parseSlashInput(value) {
|
|
|
512
1191
|
return { isSlash: true, parentCommand: parent, filter: rest };
|
|
513
1192
|
}
|
|
514
1193
|
function useAutocomplete(value, registry) {
|
|
515
|
-
const [selectedIndex, setSelectedIndex] = (0,
|
|
516
|
-
const [dismissed, setDismissed] = (0,
|
|
517
|
-
const prevValueRef =
|
|
1194
|
+
const [selectedIndex, setSelectedIndex] = (0, import_react6.useState)(0);
|
|
1195
|
+
const [dismissed, setDismissed] = (0, import_react6.useState)(false);
|
|
1196
|
+
const prevValueRef = import_react6.default.useRef(value);
|
|
518
1197
|
if (prevValueRef.current !== value) {
|
|
519
1198
|
prevValueRef.current = value;
|
|
520
1199
|
if (dismissed) setDismissed(false);
|
|
521
1200
|
}
|
|
522
1201
|
const parsed = parseSlashInput(value);
|
|
523
1202
|
const isSubcommandMode = parsed.isSlash && parsed.parentCommand.length > 0;
|
|
524
|
-
const filteredCommands = (0,
|
|
1203
|
+
const filteredCommands = (0, import_react6.useMemo)(() => {
|
|
525
1204
|
if (!registry || !parsed.isSlash || dismissed) return [];
|
|
526
1205
|
if (isSubcommandMode) {
|
|
527
1206
|
const subs = registry.getSubcommands(parsed.parentCommand);
|
|
@@ -554,8 +1233,24 @@ function useAutocomplete(value, registry) {
|
|
|
554
1233
|
}
|
|
555
1234
|
};
|
|
556
1235
|
}
|
|
557
|
-
|
|
558
|
-
|
|
1236
|
+
var BORDER_HORIZONTAL = 2;
|
|
1237
|
+
var PADDING_LEFT = 1;
|
|
1238
|
+
var PROMPT_WIDTH = 2;
|
|
1239
|
+
var INPUT_AREA_OVERHEAD = BORDER_HORIZONTAL + PADDING_LEFT + PROMPT_WIDTH;
|
|
1240
|
+
function InputArea({
|
|
1241
|
+
onSubmit,
|
|
1242
|
+
onCancelQueue,
|
|
1243
|
+
isDisabled,
|
|
1244
|
+
isAborting,
|
|
1245
|
+
pendingPrompt,
|
|
1246
|
+
registry
|
|
1247
|
+
}) {
|
|
1248
|
+
const [value, setValue] = (0, import_react6.useState)("");
|
|
1249
|
+
const pasteStore = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
|
|
1250
|
+
const { stdout } = (0, import_ink7.useStdout)();
|
|
1251
|
+
const terminalColumns = stdout?.columns ?? 80;
|
|
1252
|
+
const availableWidth = Math.max(1, terminalColumns - INPUT_AREA_OVERHEAD);
|
|
1253
|
+
const pasteIdRef = (0, import_react6.useRef)(0);
|
|
559
1254
|
const {
|
|
560
1255
|
showPopup,
|
|
561
1256
|
filteredCommands,
|
|
@@ -564,7 +1259,15 @@ function InputArea({ onSubmit, isDisabled, registry }) {
|
|
|
564
1259
|
isSubcommandMode,
|
|
565
1260
|
setShowPopup
|
|
566
1261
|
} = useAutocomplete(value, registry);
|
|
567
|
-
const
|
|
1262
|
+
const handlePaste = (0, import_react6.useCallback)((text) => {
|
|
1263
|
+
pasteIdRef.current += 1;
|
|
1264
|
+
const id = pasteIdRef.current;
|
|
1265
|
+
pasteStore.current.set(id, text);
|
|
1266
|
+
const lineCount = text.split("\n").length;
|
|
1267
|
+
const label = `[Pasted text #${id} +${lineCount} lines]`;
|
|
1268
|
+
setValue((prev) => prev ? `${prev} ${label}` : label);
|
|
1269
|
+
}, []);
|
|
1270
|
+
const handleSubmit = (0, import_react6.useCallback)(
|
|
568
1271
|
(text) => {
|
|
569
1272
|
const trimmed = text.trim();
|
|
570
1273
|
if (trimmed.length === 0) return;
|
|
@@ -572,12 +1275,15 @@ function InputArea({ onSubmit, isDisabled, registry }) {
|
|
|
572
1275
|
selectCommand(filteredCommands[selectedIndex]);
|
|
573
1276
|
return;
|
|
574
1277
|
}
|
|
1278
|
+
const expanded = expandPasteLabels(trimmed, pasteStore.current);
|
|
575
1279
|
setValue("");
|
|
576
|
-
|
|
1280
|
+
pasteStore.current.clear();
|
|
1281
|
+
pasteIdRef.current = 0;
|
|
1282
|
+
onSubmit(expanded);
|
|
577
1283
|
},
|
|
578
1284
|
[showPopup, filteredCommands, selectedIndex, onSubmit]
|
|
579
1285
|
);
|
|
580
|
-
const selectCommand = (0,
|
|
1286
|
+
const selectCommand = (0, import_react6.useCallback)(
|
|
581
1287
|
(cmd) => {
|
|
582
1288
|
const parsed = parseSlashInput(value);
|
|
583
1289
|
if (parsed.parentCommand) {
|
|
@@ -596,7 +1302,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
|
|
|
596
1302
|
},
|
|
597
1303
|
[value, onSubmit, setSelectedIndex]
|
|
598
1304
|
);
|
|
599
|
-
(0,
|
|
1305
|
+
(0, import_ink7.useInput)(
|
|
600
1306
|
(_input, key) => {
|
|
601
1307
|
if (!showPopup) return;
|
|
602
1308
|
if (key.upArrow) {
|
|
@@ -612,8 +1318,16 @@ function InputArea({ onSubmit, isDisabled, registry }) {
|
|
|
612
1318
|
},
|
|
613
1319
|
{ isActive: showPopup && !isDisabled }
|
|
614
1320
|
);
|
|
615
|
-
|
|
616
|
-
|
|
1321
|
+
(0, import_ink7.useInput)(
|
|
1322
|
+
(_input, key) => {
|
|
1323
|
+
if ((key.backspace || key.delete) && pendingPrompt) {
|
|
1324
|
+
onCancelQueue?.();
|
|
1325
|
+
}
|
|
1326
|
+
},
|
|
1327
|
+
{ isActive: !!pendingPrompt }
|
|
1328
|
+
);
|
|
1329
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { flexDirection: "column", children: [
|
|
1330
|
+
showPopup && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
617
1331
|
SlashAutocomplete,
|
|
618
1332
|
{
|
|
619
1333
|
commands: filteredCommands,
|
|
@@ -622,41 +1336,100 @@ function InputArea({ onSubmit, isDisabled, registry }) {
|
|
|
622
1336
|
isSubcommandMode
|
|
623
1337
|
}
|
|
624
1338
|
),
|
|
625
|
-
/* @__PURE__ */ (0,
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
1339
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1340
|
+
import_ink7.Box,
|
|
1341
|
+
{
|
|
1342
|
+
borderStyle: "single",
|
|
1343
|
+
borderColor: isAborting ? "yellow" : pendingPrompt ? "cyan" : isDisabled ? "gray" : "green",
|
|
1344
|
+
paddingLeft: 1,
|
|
1345
|
+
children: isAborting ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "yellow", children: " Interrupting..." }) : pendingPrompt ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { color: "cyan", children: [
|
|
1346
|
+
" ",
|
|
1347
|
+
"Queued: ",
|
|
1348
|
+
pendingPrompt.length > 50 ? pendingPrompt.slice(0, 47) + "..." : pendingPrompt,
|
|
1349
|
+
" ",
|
|
1350
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { dimColor: true, children: "(Backspace to cancel)" })
|
|
1351
|
+
] }) : isDisabled ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(WaveText, { text: " Waiting for response... (ESC to interrupt)" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { children: [
|
|
1352
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "green", bold: true, children: "> " }),
|
|
1353
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1354
|
+
CjkTextInput,
|
|
1355
|
+
{
|
|
1356
|
+
value,
|
|
1357
|
+
onChange: setValue,
|
|
1358
|
+
onSubmit: handleSubmit,
|
|
1359
|
+
onPaste: handlePaste,
|
|
1360
|
+
placeholder: "Type a message or /help",
|
|
1361
|
+
availableWidth
|
|
1362
|
+
}
|
|
1363
|
+
)
|
|
1364
|
+
] })
|
|
1365
|
+
}
|
|
1366
|
+
)
|
|
1367
|
+
] });
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
// src/ui/ConfirmPrompt.tsx
|
|
1371
|
+
var import_react7 = require("react");
|
|
1372
|
+
var import_ink8 = require("ink");
|
|
1373
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1374
|
+
function ConfirmPrompt({
|
|
1375
|
+
message,
|
|
1376
|
+
options = ["Yes", "No"],
|
|
1377
|
+
onSelect
|
|
1378
|
+
}) {
|
|
1379
|
+
const [selected, setSelected] = (0, import_react7.useState)(0);
|
|
1380
|
+
const resolvedRef = (0, import_react7.useRef)(false);
|
|
1381
|
+
const doSelect = (0, import_react7.useCallback)(
|
|
1382
|
+
(index) => {
|
|
1383
|
+
if (resolvedRef.current) return;
|
|
1384
|
+
resolvedRef.current = true;
|
|
1385
|
+
onSelect(index);
|
|
1386
|
+
},
|
|
1387
|
+
[onSelect]
|
|
1388
|
+
);
|
|
1389
|
+
(0, import_ink8.useInput)((input, key) => {
|
|
1390
|
+
if (resolvedRef.current) return;
|
|
1391
|
+
if (key.leftArrow || key.upArrow) {
|
|
1392
|
+
setSelected((prev) => prev > 0 ? prev - 1 : prev);
|
|
1393
|
+
} else if (key.rightArrow || key.downArrow) {
|
|
1394
|
+
setSelected((prev) => prev < options.length - 1 ? prev + 1 : prev);
|
|
1395
|
+
} else if (key.return) {
|
|
1396
|
+
doSelect(selected);
|
|
1397
|
+
} else if (input === "y" && options.length === 2) {
|
|
1398
|
+
doSelect(0);
|
|
1399
|
+
} else if (input === "n" && options.length === 2) {
|
|
1400
|
+
doSelect(1);
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
1404
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "yellow", children: message }),
|
|
1405
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Box, { marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
|
|
1406
|
+
i === selected ? "> " : " ",
|
|
1407
|
+
opt
|
|
1408
|
+
] }) }, opt)) }),
|
|
1409
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { dimColor: true, children: " arrow keys to select, Enter to confirm" })
|
|
637
1410
|
] });
|
|
638
1411
|
}
|
|
639
1412
|
|
|
640
1413
|
// src/ui/PermissionPrompt.tsx
|
|
641
|
-
var
|
|
642
|
-
var
|
|
643
|
-
var
|
|
1414
|
+
var import_react8 = __toESM(require("react"), 1);
|
|
1415
|
+
var import_ink9 = require("ink");
|
|
1416
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
644
1417
|
var OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
|
|
645
|
-
function
|
|
1418
|
+
function formatArgs(args) {
|
|
646
1419
|
const entries = Object.entries(args);
|
|
647
1420
|
if (entries.length === 0) return "(no arguments)";
|
|
648
1421
|
return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
649
1422
|
}
|
|
650
1423
|
function PermissionPrompt({ request }) {
|
|
651
|
-
const [selected, setSelected] =
|
|
652
|
-
const resolvedRef =
|
|
653
|
-
const prevRequestRef =
|
|
1424
|
+
const [selected, setSelected] = import_react8.default.useState(0);
|
|
1425
|
+
const resolvedRef = import_react8.default.useRef(false);
|
|
1426
|
+
const prevRequestRef = import_react8.default.useRef(request);
|
|
654
1427
|
if (prevRequestRef.current !== request) {
|
|
655
1428
|
prevRequestRef.current = request;
|
|
656
1429
|
resolvedRef.current = false;
|
|
657
1430
|
setSelected(0);
|
|
658
1431
|
}
|
|
659
|
-
const doResolve =
|
|
1432
|
+
const doResolve = import_react8.default.useCallback(
|
|
660
1433
|
(index) => {
|
|
661
1434
|
if (resolvedRef.current) return;
|
|
662
1435
|
resolvedRef.current = true;
|
|
@@ -666,7 +1439,7 @@ function PermissionPrompt({ request }) {
|
|
|
666
1439
|
},
|
|
667
1440
|
[request]
|
|
668
1441
|
);
|
|
669
|
-
(0,
|
|
1442
|
+
(0, import_ink9.useInput)((input, key) => {
|
|
670
1443
|
if (resolvedRef.current) return;
|
|
671
1444
|
if (key.upArrow || key.leftArrow) {
|
|
672
1445
|
setSelected((prev) => prev > 0 ? prev - 1 : prev);
|
|
@@ -682,364 +1455,692 @@ function PermissionPrompt({ request }) {
|
|
|
682
1455
|
doResolve(2);
|
|
683
1456
|
}
|
|
684
1457
|
});
|
|
685
|
-
return /* @__PURE__ */ (0,
|
|
686
|
-
/* @__PURE__ */ (0,
|
|
687
|
-
/* @__PURE__ */ (0,
|
|
688
|
-
"Tool:",
|
|
689
|
-
" ",
|
|
690
|
-
/* @__PURE__ */ (0,
|
|
1458
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
1459
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "yellow", bold: true, children: "[Permission Required]" }),
|
|
1460
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { children: [
|
|
1461
|
+
"Tool:",
|
|
1462
|
+
" ",
|
|
1463
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "cyan", bold: true, children: request.toolName })
|
|
1464
|
+
] }),
|
|
1465
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { dimColor: true, children: [
|
|
1466
|
+
" ",
|
|
1467
|
+
formatArgs(request.toolArgs)
|
|
1468
|
+
] }),
|
|
1469
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { marginTop: 1, children: OPTIONS.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
|
|
1470
|
+
i === selected ? "> " : " ",
|
|
1471
|
+
opt
|
|
1472
|
+
] }) }, opt)) }),
|
|
1473
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { dimColor: true, children: " left/right to select, Enter to confirm" })
|
|
1474
|
+
] });
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/ui/StreamingIndicator.tsx
|
|
1478
|
+
var import_ink10 = require("ink");
|
|
1479
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1480
|
+
function getToolStyle(t) {
|
|
1481
|
+
if (t.isRunning) return { color: "yellow", icon: "\u27F3", strikethrough: false };
|
|
1482
|
+
if (t.result === "error") return { color: "red", icon: "\u2717", strikethrough: true };
|
|
1483
|
+
if (t.result === "denied") return { color: "yellowBright", icon: "\u2298", strikethrough: true };
|
|
1484
|
+
return { color: "green", icon: "\u2713", strikethrough: false };
|
|
1485
|
+
}
|
|
1486
|
+
function StreamingIndicator({ text, activeTools }) {
|
|
1487
|
+
const hasTools = activeTools.length > 0;
|
|
1488
|
+
const hasText = text.length > 0;
|
|
1489
|
+
if (!hasTools && !hasText) {
|
|
1490
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, {});
|
|
1491
|
+
}
|
|
1492
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
|
|
1493
|
+
hasTools && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
1494
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "white", bold: true, children: "Tools:" }),
|
|
1495
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { children: " " }),
|
|
1496
|
+
activeTools.map((t, i) => {
|
|
1497
|
+
const { color, icon, strikethrough } = getToolStyle(t);
|
|
1498
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
|
|
1499
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Text, { color, strikethrough, children: [
|
|
1500
|
+
" ",
|
|
1501
|
+
icon,
|
|
1502
|
+
" ",
|
|
1503
|
+
t.toolName,
|
|
1504
|
+
"(",
|
|
1505
|
+
t.firstArg,
|
|
1506
|
+
")"
|
|
1507
|
+
] }),
|
|
1508
|
+
t.diffLines && t.diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DiffBlock, { file: t.diffFile, lines: t.diffLines })
|
|
1509
|
+
] }, `${t.toolName}-${i}`);
|
|
1510
|
+
})
|
|
1511
|
+
] }),
|
|
1512
|
+
hasText && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
1513
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "cyan", bold: true, children: "Robota:" }),
|
|
1514
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { children: " " }),
|
|
1515
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { wrap: "wrap", children: renderMarkdown(text) }) })
|
|
1516
|
+
] })
|
|
1517
|
+
] });
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
// src/ui/PluginTUI.tsx
|
|
1521
|
+
var import_react11 = require("react");
|
|
1522
|
+
|
|
1523
|
+
// src/ui/MenuSelect.tsx
|
|
1524
|
+
var import_react9 = require("react");
|
|
1525
|
+
var import_ink11 = require("ink");
|
|
1526
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1527
|
+
function MenuSelect({
|
|
1528
|
+
title,
|
|
1529
|
+
items,
|
|
1530
|
+
onSelect,
|
|
1531
|
+
onBack,
|
|
1532
|
+
loading,
|
|
1533
|
+
error
|
|
1534
|
+
}) {
|
|
1535
|
+
const [selected, setSelected] = (0, import_react9.useState)(0);
|
|
1536
|
+
const selectedRef = (0, import_react9.useRef)(0);
|
|
1537
|
+
const resolvedRef = (0, import_react9.useRef)(false);
|
|
1538
|
+
const doSelect = (0, import_react9.useCallback)(
|
|
1539
|
+
(index) => {
|
|
1540
|
+
if (resolvedRef.current || items.length === 0) return;
|
|
1541
|
+
resolvedRef.current = true;
|
|
1542
|
+
onSelect(items[index].value);
|
|
1543
|
+
},
|
|
1544
|
+
[items, onSelect]
|
|
1545
|
+
);
|
|
1546
|
+
(0, import_ink11.useInput)((input, key) => {
|
|
1547
|
+
if (resolvedRef.current) return;
|
|
1548
|
+
if (key.escape) {
|
|
1549
|
+
resolvedRef.current = true;
|
|
1550
|
+
onBack();
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
if (loading || error || items.length === 0) return;
|
|
1554
|
+
if (key.upArrow) {
|
|
1555
|
+
const next = selectedRef.current > 0 ? selectedRef.current - 1 : selectedRef.current;
|
|
1556
|
+
selectedRef.current = next;
|
|
1557
|
+
setSelected(next);
|
|
1558
|
+
} else if (key.downArrow) {
|
|
1559
|
+
const next = selectedRef.current < items.length - 1 ? selectedRef.current + 1 : selectedRef.current;
|
|
1560
|
+
selectedRef.current = next;
|
|
1561
|
+
setSelected(next);
|
|
1562
|
+
} else if (key.return) {
|
|
1563
|
+
doSelect(selectedRef.current);
|
|
1564
|
+
}
|
|
1565
|
+
});
|
|
1566
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
1567
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "yellow", bold: true, children: title }),
|
|
1568
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: "Loading..." }) }),
|
|
1569
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { marginTop: 1, flexDirection: "column", children: [
|
|
1570
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "red", children: error }),
|
|
1571
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: "Press Esc to go back" })
|
|
1572
|
+
] }),
|
|
1573
|
+
!loading && !error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { flexDirection: "column", marginTop: 1, children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { children: [
|
|
1574
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
|
|
1575
|
+
i === selected ? "> " : " ",
|
|
1576
|
+
item.label
|
|
1577
|
+
] }),
|
|
1578
|
+
item.hint && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { dimColor: true, children: [
|
|
1579
|
+
" ",
|
|
1580
|
+
item.hint
|
|
1581
|
+
] })
|
|
1582
|
+
] }, item.value)) }),
|
|
1583
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
|
|
1584
|
+
] });
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
// src/ui/TextPrompt.tsx
|
|
1588
|
+
var import_react10 = require("react");
|
|
1589
|
+
var import_ink12 = require("ink");
|
|
1590
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1591
|
+
function TextPrompt({
|
|
1592
|
+
title,
|
|
1593
|
+
placeholder,
|
|
1594
|
+
onSubmit,
|
|
1595
|
+
onCancel,
|
|
1596
|
+
validate
|
|
1597
|
+
}) {
|
|
1598
|
+
const [value, setValue] = (0, import_react10.useState)("");
|
|
1599
|
+
const [error, setError] = (0, import_react10.useState)();
|
|
1600
|
+
const resolvedRef = (0, import_react10.useRef)(false);
|
|
1601
|
+
const valueRef = (0, import_react10.useRef)("");
|
|
1602
|
+
const handleSubmit = (0, import_react10.useCallback)(() => {
|
|
1603
|
+
if (resolvedRef.current) return;
|
|
1604
|
+
const trimmed = valueRef.current.trim();
|
|
1605
|
+
if (!trimmed) return;
|
|
1606
|
+
if (validate) {
|
|
1607
|
+
const err = validate(trimmed);
|
|
1608
|
+
if (err) {
|
|
1609
|
+
setError(err);
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
resolvedRef.current = true;
|
|
1614
|
+
onSubmit(trimmed);
|
|
1615
|
+
}, [validate, onSubmit]);
|
|
1616
|
+
(0, import_ink12.useInput)((input, key) => {
|
|
1617
|
+
if (resolvedRef.current) return;
|
|
1618
|
+
if (key.escape) {
|
|
1619
|
+
resolvedRef.current = true;
|
|
1620
|
+
onCancel();
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
if (key.return) {
|
|
1624
|
+
handleSubmit();
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
if (key.backspace || key.delete) {
|
|
1628
|
+
valueRef.current = valueRef.current.slice(0, -1);
|
|
1629
|
+
setValue(valueRef.current);
|
|
1630
|
+
setError(void 0);
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
if (input && !key.ctrl && !key.meta) {
|
|
1634
|
+
valueRef.current = valueRef.current + input;
|
|
1635
|
+
setValue(valueRef.current);
|
|
1636
|
+
setError(void 0);
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
1640
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "yellow", bold: true, children: title }),
|
|
1641
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { marginTop: 1, children: [
|
|
1642
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", children: "> " }),
|
|
1643
|
+
value ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { children: value }) : placeholder ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { dimColor: true, children: placeholder }) : null,
|
|
1644
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", children: "\u2588" })
|
|
691
1645
|
] }),
|
|
692
|
-
/* @__PURE__ */ (0,
|
|
693
|
-
|
|
694
|
-
formatArgs2(request.toolArgs)
|
|
695
|
-
] }),
|
|
696
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { marginTop: 1, children: OPTIONS.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
|
|
697
|
-
i === selected ? "> " : " ",
|
|
698
|
-
opt
|
|
699
|
-
] }) }, opt)) }),
|
|
700
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { dimColor: true, children: " left/right to select, Enter to confirm" })
|
|
1646
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "red", children: error }),
|
|
1647
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { dimColor: true, children: " Enter Submit Esc Cancel" })
|
|
701
1648
|
] });
|
|
702
1649
|
}
|
|
703
1650
|
|
|
704
|
-
// src/ui/
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
1651
|
+
// src/ui/plugin-tui-handlers.ts
|
|
1652
|
+
function handleMainSelect(value, nav) {
|
|
1653
|
+
if (value === "marketplace") {
|
|
1654
|
+
nav.push({ screen: "marketplace-list" });
|
|
1655
|
+
} else if (value === "installed") {
|
|
1656
|
+
nav.push({ screen: "installed-list" });
|
|
1657
|
+
}
|
|
710
1658
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
const [permissionRequest, setPermissionRequest] = (0, import_react5.useState)(null);
|
|
728
|
-
const [streamingText, setStreamingText] = (0, import_react5.useState)("");
|
|
729
|
-
const permissionQueueRef = (0, import_react5.useRef)([]);
|
|
730
|
-
const processingRef = (0, import_react5.useRef)(false);
|
|
731
|
-
const processNextPermission = (0, import_react5.useCallback)(() => {
|
|
732
|
-
if (processingRef.current) return;
|
|
733
|
-
const next = permissionQueueRef.current[0];
|
|
734
|
-
if (!next) {
|
|
735
|
-
setPermissionRequest(null);
|
|
736
|
-
return;
|
|
737
|
-
}
|
|
738
|
-
processingRef.current = true;
|
|
739
|
-
setPermissionRequest({
|
|
740
|
-
toolName: next.toolName,
|
|
741
|
-
toolArgs: next.toolArgs,
|
|
742
|
-
resolve: (result) => {
|
|
743
|
-
permissionQueueRef.current.shift();
|
|
744
|
-
processingRef.current = false;
|
|
745
|
-
setPermissionRequest(null);
|
|
746
|
-
next.resolve(result);
|
|
747
|
-
setTimeout(() => processNextPermission(), 0);
|
|
748
|
-
}
|
|
1659
|
+
function handleMarketplaceListSelect(value, nav) {
|
|
1660
|
+
if (value === "__add__") {
|
|
1661
|
+
nav.push({ screen: "marketplace-add" });
|
|
1662
|
+
} else {
|
|
1663
|
+
nav.push({ screen: "marketplace-action", context: { marketplace: value } });
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
function handleMarketplaceActionSelect(value, marketplace, callbacks, nav) {
|
|
1667
|
+
if (value === "browse") {
|
|
1668
|
+
nav.push({ screen: "marketplace-browse", context: { marketplace } });
|
|
1669
|
+
} else if (value === "update") {
|
|
1670
|
+
callbacks.marketplaceUpdate(marketplace).then(() => {
|
|
1671
|
+
nav.notify(`Updated marketplace "${marketplace}".`);
|
|
1672
|
+
nav.pop();
|
|
1673
|
+
}).catch((err) => {
|
|
1674
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
749
1675
|
});
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
sessionRef.current = (0, import_agent_sdk.createSession)({
|
|
764
|
-
config: props.config,
|
|
765
|
-
context: props.context,
|
|
766
|
-
terminal: NOOP_TERMINAL,
|
|
767
|
-
sessionLogger: new import_agent_sdk.FileSessionLogger(paths.logs),
|
|
768
|
-
projectInfo: props.projectInfo,
|
|
769
|
-
sessionStore: props.sessionStore,
|
|
770
|
-
permissionMode: props.permissionMode,
|
|
771
|
-
maxTurns: props.maxTurns,
|
|
772
|
-
permissionHandler,
|
|
773
|
-
onTextDelta
|
|
1676
|
+
} else if (value === "remove") {
|
|
1677
|
+
nav.setConfirm({
|
|
1678
|
+
message: `Remove marketplace "${marketplace}" and all its plugins?`,
|
|
1679
|
+
onConfirm: () => {
|
|
1680
|
+
nav.setConfirm(void 0);
|
|
1681
|
+
callbacks.marketplaceRemove(marketplace).then(() => {
|
|
1682
|
+
nav.notify(`Removed marketplace "${marketplace}".`);
|
|
1683
|
+
nav.popN(2);
|
|
1684
|
+
}).catch((err) => {
|
|
1685
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1686
|
+
});
|
|
1687
|
+
},
|
|
1688
|
+
onCancel: () => nav.setConfirm(void 0)
|
|
774
1689
|
});
|
|
775
1690
|
}
|
|
776
|
-
const clearStreamingText = (0, import_react5.useCallback)(() => setStreamingText(""), []);
|
|
777
|
-
return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText };
|
|
778
|
-
}
|
|
779
|
-
function useMessages() {
|
|
780
|
-
const [messages, setMessages] = (0, import_react5.useState)([]);
|
|
781
|
-
const addMessage = (0, import_react5.useCallback)((msg) => {
|
|
782
|
-
setMessages((prev) => [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }]);
|
|
783
|
-
}, []);
|
|
784
|
-
return { messages, setMessages, addMessage };
|
|
785
1691
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
" /mode [m] \u2014 Show/change permission mode",
|
|
792
|
-
" /cost \u2014 Show session info",
|
|
793
|
-
" /exit \u2014 Exit CLI"
|
|
794
|
-
].join("\n");
|
|
795
|
-
function handleModeCommand(arg, session, addMessage) {
|
|
796
|
-
const validModes = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
797
|
-
if (!arg) {
|
|
798
|
-
addMessage({ role: "system", content: `Current mode: ${session.getPermissionMode()}` });
|
|
799
|
-
} else if (validModes.includes(arg)) {
|
|
800
|
-
session.setPermissionMode(arg);
|
|
801
|
-
addMessage({ role: "system", content: `Permission mode set to: ${arg}` });
|
|
1692
|
+
function handleMarketplaceBrowseSelect(value, marketplace, items, nav) {
|
|
1693
|
+
const fullId = `${value}@${marketplace}`;
|
|
1694
|
+
const item = items.find((i) => i.value === value);
|
|
1695
|
+
if (item?.hint === "installed") {
|
|
1696
|
+
nav.push({ screen: "installed-action", context: { pluginId: fullId } });
|
|
802
1697
|
} else {
|
|
803
|
-
|
|
1698
|
+
nav.push({ screen: "marketplace-install-scope", context: { marketplace, pluginId: fullId } });
|
|
804
1699
|
}
|
|
805
|
-
return true;
|
|
806
1700
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
addMessage({ role: "system", content: "Conversation cleared." });
|
|
816
|
-
return true;
|
|
817
|
-
case "compact": {
|
|
818
|
-
const instructions = parts.slice(1).join(" ").trim() || void 0;
|
|
819
|
-
const before = session.getContextState().usedPercentage;
|
|
820
|
-
addMessage({ role: "system", content: "Compacting context..." });
|
|
821
|
-
await session.compact(instructions);
|
|
822
|
-
const after = session.getContextState().usedPercentage;
|
|
823
|
-
addMessage({
|
|
824
|
-
role: "system",
|
|
825
|
-
content: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`
|
|
826
|
-
});
|
|
827
|
-
return true;
|
|
828
|
-
}
|
|
829
|
-
case "mode":
|
|
830
|
-
return handleModeCommand(parts[1], session, addMessage);
|
|
831
|
-
case "cost":
|
|
832
|
-
addMessage({
|
|
833
|
-
role: "system",
|
|
834
|
-
content: `Session: ${session.getSessionId()}
|
|
835
|
-
Messages: ${session.getMessageCount()}`
|
|
836
|
-
});
|
|
837
|
-
return true;
|
|
838
|
-
case "permissions": {
|
|
839
|
-
const mode = session.getPermissionMode();
|
|
840
|
-
const sessionAllowed = session.getSessionAllowedTools();
|
|
841
|
-
const lines = [`Permission mode: ${mode}`];
|
|
842
|
-
if (sessionAllowed.length > 0) {
|
|
843
|
-
lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
|
|
844
|
-
} else {
|
|
845
|
-
lines.push("No session-approved tools.");
|
|
846
|
-
}
|
|
847
|
-
addMessage({ role: "system", content: lines.join("\n") });
|
|
848
|
-
return true;
|
|
849
|
-
}
|
|
850
|
-
case "context": {
|
|
851
|
-
const ctx = session.getContextState();
|
|
852
|
-
addMessage({
|
|
853
|
-
role: "system",
|
|
854
|
-
content: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`
|
|
855
|
-
});
|
|
856
|
-
return true;
|
|
857
|
-
}
|
|
858
|
-
case "exit":
|
|
859
|
-
exit();
|
|
860
|
-
return true;
|
|
861
|
-
default: {
|
|
862
|
-
const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
|
|
863
|
-
if (skillCmd) {
|
|
864
|
-
addMessage({ role: "system", content: `Invoking skill: ${cmd}` });
|
|
865
|
-
return false;
|
|
866
|
-
}
|
|
867
|
-
addMessage({ role: "system", content: `Unknown command "/${cmd}". Type /help for help.` });
|
|
868
|
-
return true;
|
|
869
|
-
}
|
|
870
|
-
}
|
|
1701
|
+
function handleInstallScopeSelect(value, pluginId, callbacks, nav) {
|
|
1702
|
+
const scope = value;
|
|
1703
|
+
callbacks.install(pluginId, scope).then(() => {
|
|
1704
|
+
nav.notify(`Installed plugin "${pluginId}" (${scope} scope).`);
|
|
1705
|
+
nav.popN(2);
|
|
1706
|
+
}).catch((err) => {
|
|
1707
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1708
|
+
});
|
|
871
1709
|
}
|
|
872
|
-
function
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1710
|
+
function handleInstalledListSelect(value, callbacks, nav) {
|
|
1711
|
+
nav.setConfirm({
|
|
1712
|
+
message: `Uninstall plugin "${value}"?`,
|
|
1713
|
+
onConfirm: () => {
|
|
1714
|
+
nav.setConfirm(void 0);
|
|
1715
|
+
callbacks.uninstall(value).then(() => {
|
|
1716
|
+
nav.notify(`Uninstalled plugin "${value}".`);
|
|
1717
|
+
nav.refresh();
|
|
1718
|
+
}).catch((err) => {
|
|
1719
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1720
|
+
});
|
|
878
1721
|
},
|
|
879
|
-
|
|
880
|
-
);
|
|
881
|
-
}
|
|
882
|
-
function StreamingIndicator({ text }) {
|
|
883
|
-
if (text) {
|
|
884
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", children: [
|
|
885
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { color: "cyan", bold: true, children: [
|
|
886
|
-
"Robota:",
|
|
887
|
-
" "
|
|
888
|
-
] }),
|
|
889
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { children: " " }),
|
|
890
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { wrap: "wrap", children: renderMarkdown(text) }) })
|
|
891
|
-
] });
|
|
892
|
-
}
|
|
893
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "yellow", children: "Thinking..." });
|
|
1722
|
+
onCancel: () => nav.setConfirm(void 0)
|
|
1723
|
+
});
|
|
894
1724
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
}
|
|
911
|
-
} finally {
|
|
912
|
-
setIsThinking(false);
|
|
1725
|
+
function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
|
|
1726
|
+
if (value === "uninstall") {
|
|
1727
|
+
nav.setConfirm({
|
|
1728
|
+
message: `Uninstall plugin "${pluginId}"?`,
|
|
1729
|
+
onConfirm: () => {
|
|
1730
|
+
nav.setConfirm(void 0);
|
|
1731
|
+
callbacks.uninstall(pluginId).then(() => {
|
|
1732
|
+
nav.notify(`Uninstalled plugin "${pluginId}".`);
|
|
1733
|
+
nav.popN(2);
|
|
1734
|
+
}).catch((err) => {
|
|
1735
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1736
|
+
});
|
|
1737
|
+
},
|
|
1738
|
+
onCancel: () => nav.setConfirm(void 0)
|
|
1739
|
+
});
|
|
913
1740
|
}
|
|
914
1741
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1742
|
+
|
|
1743
|
+
// src/ui/PluginTUI.tsx
|
|
1744
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1745
|
+
function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
1746
|
+
const [stack, setStack] = (0, import_react11.useState)([{ screen: "main" }]);
|
|
1747
|
+
const [items, setItems] = (0, import_react11.useState)([]);
|
|
1748
|
+
const [loading, setLoading] = (0, import_react11.useState)(false);
|
|
1749
|
+
const [error, setError] = (0, import_react11.useState)();
|
|
1750
|
+
const [confirm, setConfirm] = (0, import_react11.useState)();
|
|
1751
|
+
const [refreshCounter, setRefreshCounter] = (0, import_react11.useState)(0);
|
|
1752
|
+
const current = stack[stack.length - 1] ?? { screen: "main" };
|
|
1753
|
+
const push = (0, import_react11.useCallback)((state) => {
|
|
1754
|
+
setStack((prev) => [...prev, state]);
|
|
1755
|
+
setItems([]);
|
|
1756
|
+
setError(void 0);
|
|
1757
|
+
}, []);
|
|
1758
|
+
const pop = (0, import_react11.useCallback)(() => {
|
|
1759
|
+
setStack((prev) => {
|
|
1760
|
+
if (prev.length <= 1) {
|
|
1761
|
+
onClose();
|
|
1762
|
+
return prev;
|
|
1763
|
+
}
|
|
1764
|
+
return prev.slice(0, -1);
|
|
1765
|
+
});
|
|
1766
|
+
setItems([]);
|
|
1767
|
+
setError(void 0);
|
|
1768
|
+
}, [onClose]);
|
|
1769
|
+
const popN = (0, import_react11.useCallback)(
|
|
1770
|
+
(n) => {
|
|
1771
|
+
setStack((prev) => {
|
|
1772
|
+
const next = prev.slice(0, Math.max(1, prev.length - n));
|
|
1773
|
+
if (next.length === 0) {
|
|
1774
|
+
onClose();
|
|
1775
|
+
return prev;
|
|
931
1776
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1777
|
+
return next;
|
|
1778
|
+
});
|
|
1779
|
+
setItems([]);
|
|
1780
|
+
setError(void 0);
|
|
1781
|
+
},
|
|
1782
|
+
[onClose]
|
|
1783
|
+
);
|
|
1784
|
+
const notify = (0, import_react11.useCallback)(
|
|
1785
|
+
(content) => {
|
|
1786
|
+
addMessage?.({ role: "system", content });
|
|
1787
|
+
},
|
|
1788
|
+
[addMessage]
|
|
1789
|
+
);
|
|
1790
|
+
const refresh = (0, import_react11.useCallback)(() => {
|
|
1791
|
+
setItems([]);
|
|
1792
|
+
setRefreshCounter((c) => c + 1);
|
|
1793
|
+
}, []);
|
|
1794
|
+
const nav = { push, pop, popN, notify, setConfirm, refresh };
|
|
1795
|
+
(0, import_react11.useEffect)(() => {
|
|
1796
|
+
const screen2 = current.screen;
|
|
1797
|
+
if (screen2 === "marketplace-list") {
|
|
1798
|
+
setLoading(true);
|
|
1799
|
+
callbacks.marketplaceList().then((sources) => {
|
|
1800
|
+
const baseItems = [{ label: "Add Marketplace", value: "__add__" }];
|
|
1801
|
+
const sourceItems = sources.map((s) => ({
|
|
1802
|
+
label: s.name,
|
|
1803
|
+
value: s.name,
|
|
1804
|
+
hint: s.type
|
|
1805
|
+
}));
|
|
1806
|
+
setItems([...baseItems, ...sourceItems]);
|
|
1807
|
+
setLoading(false);
|
|
1808
|
+
}).catch((err) => {
|
|
1809
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
1810
|
+
setLoading(false);
|
|
1811
|
+
});
|
|
1812
|
+
} else if (screen2 === "marketplace-browse") {
|
|
1813
|
+
const marketplace = current.context?.marketplace ?? "";
|
|
1814
|
+
setLoading(true);
|
|
1815
|
+
callbacks.listAvailablePlugins(marketplace).then((plugins) => {
|
|
1816
|
+
setItems(
|
|
1817
|
+
plugins.map((p) => ({
|
|
1818
|
+
label: p.name,
|
|
1819
|
+
value: p.name,
|
|
1820
|
+
hint: p.installed ? "installed" : p.description
|
|
1821
|
+
}))
|
|
1822
|
+
);
|
|
1823
|
+
setLoading(false);
|
|
1824
|
+
}).catch((err) => {
|
|
1825
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
1826
|
+
setLoading(false);
|
|
1827
|
+
});
|
|
1828
|
+
} else if (screen2 === "installed-list") {
|
|
1829
|
+
setLoading(true);
|
|
1830
|
+
callbacks.listInstalled().then((plugins) => {
|
|
1831
|
+
setItems(
|
|
1832
|
+
plugins.map((p) => ({
|
|
1833
|
+
label: p.name,
|
|
1834
|
+
value: p.name,
|
|
1835
|
+
hint: p.description
|
|
1836
|
+
}))
|
|
941
1837
|
);
|
|
1838
|
+
setLoading(false);
|
|
1839
|
+
}).catch((err) => {
|
|
1840
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
1841
|
+
setLoading(false);
|
|
1842
|
+
});
|
|
1843
|
+
}
|
|
1844
|
+
}, [stack.length, current.screen, current.context?.marketplace, callbacks, refreshCounter]);
|
|
1845
|
+
const handleSelect = (0, import_react11.useCallback)(
|
|
1846
|
+
(value) => {
|
|
1847
|
+
const screen2 = current.screen;
|
|
1848
|
+
const ctx = current.context;
|
|
1849
|
+
if (screen2 === "main") handleMainSelect(value, nav);
|
|
1850
|
+
else if (screen2 === "marketplace-list") handleMarketplaceListSelect(value, nav);
|
|
1851
|
+
else if (screen2 === "marketplace-action")
|
|
1852
|
+
handleMarketplaceActionSelect(value, ctx?.marketplace ?? "", callbacks, nav);
|
|
1853
|
+
else if (screen2 === "marketplace-browse")
|
|
1854
|
+
handleMarketplaceBrowseSelect(value, ctx?.marketplace ?? "", items, nav);
|
|
1855
|
+
else if (screen2 === "marketplace-install-scope")
|
|
1856
|
+
handleInstallScopeSelect(value, ctx?.pluginId ?? "", callbacks, nav);
|
|
1857
|
+
else if (screen2 === "installed-list") handleInstalledListSelect(value, callbacks, nav);
|
|
1858
|
+
else if (screen2 === "installed-action")
|
|
1859
|
+
handleInstalledActionSelect(value, ctx?.pluginId ?? "", callbacks, nav);
|
|
1860
|
+
},
|
|
1861
|
+
[current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
|
|
1862
|
+
);
|
|
1863
|
+
const handleTextSubmit = (0, import_react11.useCallback)(
|
|
1864
|
+
(value) => {
|
|
1865
|
+
if (current.screen === "marketplace-add") {
|
|
1866
|
+
callbacks.marketplaceAdd(value).then((name) => {
|
|
1867
|
+
notify(`Added marketplace "${name}" from ${value}.`);
|
|
1868
|
+
pop();
|
|
1869
|
+
}).catch((err) => {
|
|
1870
|
+
notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1871
|
+
pop();
|
|
1872
|
+
});
|
|
942
1873
|
}
|
|
943
|
-
addMessage({ role: "user", content: input });
|
|
944
|
-
return runSessionPrompt(
|
|
945
|
-
input,
|
|
946
|
-
session,
|
|
947
|
-
addMessage,
|
|
948
|
-
clearStreamingText,
|
|
949
|
-
setIsThinking,
|
|
950
|
-
setContextPercentage
|
|
951
|
-
);
|
|
952
1874
|
},
|
|
953
|
-
[
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1875
|
+
[current.screen, callbacks, notify, pop]
|
|
1876
|
+
);
|
|
1877
|
+
if (confirm) {
|
|
1878
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1879
|
+
ConfirmPrompt,
|
|
1880
|
+
{
|
|
1881
|
+
message: confirm.message,
|
|
1882
|
+
onSelect: (index) => {
|
|
1883
|
+
if (index === 0) confirm.onConfirm();
|
|
1884
|
+
else confirm.onCancel();
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
);
|
|
1888
|
+
}
|
|
1889
|
+
const screen = current.screen;
|
|
1890
|
+
if (screen === "marketplace-add") {
|
|
1891
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1892
|
+
TextPrompt,
|
|
1893
|
+
{
|
|
1894
|
+
title: "Add Marketplace Source",
|
|
1895
|
+
placeholder: "owner/repo or git URL",
|
|
1896
|
+
onSubmit: handleTextSubmit,
|
|
1897
|
+
onCancel: pop,
|
|
1898
|
+
validate: (v) => !v.includes("/") ? "Must be owner/repo or a git URL" : void 0
|
|
1899
|
+
}
|
|
1900
|
+
);
|
|
1901
|
+
}
|
|
1902
|
+
if (screen === "marketplace-action") {
|
|
1903
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1904
|
+
MenuSelect,
|
|
1905
|
+
{
|
|
1906
|
+
title: `Marketplace: ${current.context?.marketplace ?? ""}`,
|
|
1907
|
+
items: [
|
|
1908
|
+
{ label: "Browse plugins", value: "browse" },
|
|
1909
|
+
{ label: "Update", value: "update" },
|
|
1910
|
+
{ label: "Remove", value: "remove" }
|
|
1911
|
+
],
|
|
1912
|
+
onSelect: handleSelect,
|
|
1913
|
+
onBack: pop
|
|
1914
|
+
},
|
|
1915
|
+
stack.length
|
|
1916
|
+
);
|
|
1917
|
+
}
|
|
1918
|
+
if (screen === "marketplace-install-scope") {
|
|
1919
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1920
|
+
MenuSelect,
|
|
1921
|
+
{
|
|
1922
|
+
title: `Install scope for "${current.context?.pluginId ?? ""}"`,
|
|
1923
|
+
items: [
|
|
1924
|
+
{ label: "User scope", value: "user" },
|
|
1925
|
+
{ label: "Project scope", value: "project" }
|
|
1926
|
+
],
|
|
1927
|
+
onSelect: handleSelect,
|
|
1928
|
+
onBack: pop
|
|
1929
|
+
},
|
|
1930
|
+
stack.length
|
|
1931
|
+
);
|
|
1932
|
+
}
|
|
1933
|
+
if (screen === "installed-action") {
|
|
1934
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1935
|
+
MenuSelect,
|
|
1936
|
+
{
|
|
1937
|
+
title: `Plugin: ${current.context?.pluginId ?? ""}`,
|
|
1938
|
+
items: [{ label: "Uninstall", value: "uninstall" }],
|
|
1939
|
+
onSelect: handleSelect,
|
|
1940
|
+
onBack: pop
|
|
1941
|
+
},
|
|
1942
|
+
stack.length
|
|
1943
|
+
);
|
|
1944
|
+
}
|
|
1945
|
+
const titleMap = {
|
|
1946
|
+
main: "Plugin Management",
|
|
1947
|
+
"marketplace-list": "Marketplace",
|
|
1948
|
+
"marketplace-browse": `Browse: ${current.context?.marketplace ?? ""}`,
|
|
1949
|
+
"installed-list": "Installed Plugins"
|
|
1950
|
+
};
|
|
1951
|
+
const staticItemsMap = {
|
|
1952
|
+
main: [
|
|
1953
|
+
{ label: "Marketplace", value: "marketplace" },
|
|
1954
|
+
{ label: "Installed Plugins", value: "installed" }
|
|
961
1955
|
]
|
|
1956
|
+
};
|
|
1957
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1958
|
+
MenuSelect,
|
|
1959
|
+
{
|
|
1960
|
+
title: titleMap[screen] ?? "Plugin Management",
|
|
1961
|
+
items: staticItemsMap[screen] ?? items,
|
|
1962
|
+
onSelect: handleSelect,
|
|
1963
|
+
onBack: pop,
|
|
1964
|
+
loading,
|
|
1965
|
+
error
|
|
1966
|
+
},
|
|
1967
|
+
`${screen}-${stack.length}-${refreshCounter}`
|
|
962
1968
|
);
|
|
963
1969
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
registry.addSource(new BuiltinCommandSource());
|
|
969
|
-
registry.addSource(new SkillCommandSource(cwd));
|
|
970
|
-
registryRef.current = registry;
|
|
971
|
-
}
|
|
972
|
-
return registryRef.current;
|
|
973
|
-
}
|
|
1970
|
+
|
|
1971
|
+
// src/ui/App.tsx
|
|
1972
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1973
|
+
var EXIT_DELAY_MS = 500;
|
|
974
1974
|
function App(props) {
|
|
975
|
-
const { exit } = (0,
|
|
976
|
-
const
|
|
977
|
-
const {
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
const handleSlashCommand = useSlashCommands(session, addMessage, setMessages, exit, registry);
|
|
982
|
-
const handleSubmit = useSubmitHandler(
|
|
983
|
-
session,
|
|
1975
|
+
const { exit } = (0, import_ink13.useApp)();
|
|
1976
|
+
const cwd = props.cwd ?? process.cwd();
|
|
1977
|
+
const {
|
|
1978
|
+
interactiveSession,
|
|
1979
|
+
registry,
|
|
1980
|
+
messages,
|
|
984
1981
|
addMessage,
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1982
|
+
streamingText,
|
|
1983
|
+
activeTools,
|
|
1984
|
+
isThinking,
|
|
1985
|
+
isAborting,
|
|
1986
|
+
pendingPrompt,
|
|
1987
|
+
permissionRequest,
|
|
1988
|
+
contextState,
|
|
1989
|
+
handleSubmit: baseHandleSubmit,
|
|
1990
|
+
handleAbort,
|
|
1991
|
+
handleCancelQueue
|
|
1992
|
+
} = useInteractiveSession({
|
|
1993
|
+
config: props.config,
|
|
1994
|
+
context: props.context,
|
|
1995
|
+
projectInfo: props.projectInfo,
|
|
1996
|
+
sessionStore: props.sessionStore,
|
|
1997
|
+
permissionMode: props.permissionMode,
|
|
1998
|
+
maxTurns: props.maxTurns,
|
|
1999
|
+
cwd
|
|
2000
|
+
});
|
|
2001
|
+
const pluginCallbacks = usePluginCallbacks(cwd);
|
|
2002
|
+
const [pendingModelId, setPendingModelId] = (0, import_react12.useState)(null);
|
|
2003
|
+
const pendingModelChangeRef = (0, import_react12.useRef)(null);
|
|
2004
|
+
const [showPluginTUI, setShowPluginTUI] = (0, import_react12.useState)(false);
|
|
2005
|
+
const handleSubmit = async (input) => {
|
|
2006
|
+
await baseHandleSubmit(input);
|
|
2007
|
+
const sideEffects = interactiveSession;
|
|
2008
|
+
if (sideEffects._pendingModelId) {
|
|
2009
|
+
const modelId = sideEffects._pendingModelId;
|
|
2010
|
+
delete sideEffects._pendingModelId;
|
|
2011
|
+
pendingModelChangeRef.current = modelId;
|
|
2012
|
+
setPendingModelId(modelId);
|
|
2013
|
+
return;
|
|
2014
|
+
}
|
|
2015
|
+
if (sideEffects._pendingLanguage) {
|
|
2016
|
+
const lang = sideEffects._pendingLanguage;
|
|
2017
|
+
delete sideEffects._pendingLanguage;
|
|
2018
|
+
const settingsPath = getUserSettingsPath();
|
|
2019
|
+
const settings = readSettings(settingsPath);
|
|
2020
|
+
settings.language = lang;
|
|
2021
|
+
writeSettings(settingsPath, settings);
|
|
2022
|
+
addMessage((0, import_agent_core4.createSystemMessage)(`Language set to "${lang}". Restarting...`));
|
|
2023
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
if (sideEffects._resetRequested) {
|
|
2027
|
+
delete sideEffects._resetRequested;
|
|
2028
|
+
const settingsPath = getUserSettingsPath();
|
|
2029
|
+
if (deleteSettings(settingsPath)) {
|
|
2030
|
+
addMessage((0, import_agent_core4.createSystemMessage)(`Deleted ${settingsPath}. Exiting...`));
|
|
2031
|
+
} else {
|
|
2032
|
+
addMessage((0, import_agent_core4.createSystemMessage)("No user settings found."));
|
|
2033
|
+
}
|
|
2034
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
if (sideEffects._exitRequested) {
|
|
2038
|
+
delete sideEffects._exitRequested;
|
|
2039
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
if (sideEffects._triggerPluginTUI) {
|
|
2043
|
+
delete sideEffects._triggerPluginTUI;
|
|
2044
|
+
setShowPluginTUI(true);
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
};
|
|
2048
|
+
(0, import_ink13.useInput)(
|
|
992
2049
|
(_input, key) => {
|
|
993
|
-
if (key.
|
|
994
|
-
|
|
2050
|
+
if (key.escape && isThinking) {
|
|
2051
|
+
handleAbort();
|
|
2052
|
+
}
|
|
995
2053
|
},
|
|
996
|
-
{ isActive: !permissionRequest }
|
|
2054
|
+
{ isActive: !permissionRequest && !showPluginTUI }
|
|
997
2055
|
);
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
2056
|
+
const session = interactiveSession.getSession();
|
|
2057
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", children: [
|
|
2058
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
2059
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { color: "cyan", bold: true, children: `
|
|
1001
2060
|
____ ___ ____ ___ _____ _
|
|
1002
2061
|
| _ \\ / _ \\| __ ) / _ \\_ _|/ \\
|
|
1003
2062
|
| |_) | | | | _ \\| | | || | / _ \\
|
|
1004
2063
|
| _ <| |_| | |_) | |_| || |/ ___ \\
|
|
1005
2064
|
|_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
|
|
1006
2065
|
` }),
|
|
1007
|
-
/* @__PURE__ */ (0,
|
|
2066
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Text, { dimColor: true, children: [
|
|
1008
2067
|
" v",
|
|
1009
2068
|
props.version ?? "0.0.0"
|
|
1010
2069
|
] })
|
|
1011
2070
|
] }),
|
|
1012
|
-
/* @__PURE__ */ (0,
|
|
1013
|
-
/* @__PURE__ */ (0,
|
|
1014
|
-
isThinking && /* @__PURE__ */ (0,
|
|
2071
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
|
|
2072
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageList, { messages }),
|
|
2073
|
+
isThinking && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
|
|
1015
2074
|
] }),
|
|
1016
|
-
permissionRequest && /* @__PURE__ */ (0,
|
|
1017
|
-
/* @__PURE__ */ (0,
|
|
2075
|
+
permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PermissionPrompt, { request: permissionRequest }),
|
|
2076
|
+
pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2077
|
+
ConfirmPrompt,
|
|
2078
|
+
{
|
|
2079
|
+
message: `Change model to ${(0, import_agent_core4.getModelName)(pendingModelId)}? This will restart the session.`,
|
|
2080
|
+
onSelect: (index) => {
|
|
2081
|
+
setPendingModelId(null);
|
|
2082
|
+
pendingModelChangeRef.current = null;
|
|
2083
|
+
if (index === 0) {
|
|
2084
|
+
try {
|
|
2085
|
+
const settingsPath = getUserSettingsPath();
|
|
2086
|
+
updateModelInSettings(settingsPath, pendingModelId);
|
|
2087
|
+
addMessage(
|
|
2088
|
+
(0, import_agent_core4.createSystemMessage)(
|
|
2089
|
+
`Model changed to ${(0, import_agent_core4.getModelName)(pendingModelId)}. Restarting...`
|
|
2090
|
+
)
|
|
2091
|
+
);
|
|
2092
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2093
|
+
} catch (err) {
|
|
2094
|
+
addMessage(
|
|
2095
|
+
(0, import_agent_core4.createSystemMessage)(
|
|
2096
|
+
`Failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2097
|
+
)
|
|
2098
|
+
);
|
|
2099
|
+
}
|
|
2100
|
+
} else {
|
|
2101
|
+
addMessage((0, import_agent_core4.createSystemMessage)("Model change cancelled."));
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
),
|
|
2106
|
+
showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2107
|
+
PluginTUI,
|
|
2108
|
+
{
|
|
2109
|
+
callbacks: pluginCallbacks,
|
|
2110
|
+
onClose: () => setShowPluginTUI(false),
|
|
2111
|
+
addMessage: (msg) => addMessage((0, import_agent_core4.createSystemMessage)(msg.content))
|
|
2112
|
+
}
|
|
2113
|
+
),
|
|
2114
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1018
2115
|
StatusBar,
|
|
1019
2116
|
{
|
|
1020
2117
|
permissionMode: session.getPermissionMode(),
|
|
1021
|
-
modelName: props.config.provider.model,
|
|
2118
|
+
modelName: (0, import_agent_core4.getModelName)(props.config.provider.model),
|
|
1022
2119
|
sessionId: session.getSessionId(),
|
|
1023
2120
|
messageCount: messages.length,
|
|
1024
2121
|
isThinking,
|
|
1025
|
-
contextPercentage,
|
|
1026
|
-
contextUsedTokens:
|
|
1027
|
-
contextMaxTokens:
|
|
2122
|
+
contextPercentage: contextState.percentage,
|
|
2123
|
+
contextUsedTokens: contextState.usedTokens,
|
|
2124
|
+
contextMaxTokens: contextState.maxTokens
|
|
1028
2125
|
}
|
|
1029
2126
|
),
|
|
1030
|
-
/* @__PURE__ */ (0,
|
|
2127
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1031
2128
|
InputArea,
|
|
1032
2129
|
{
|
|
1033
2130
|
onSubmit: handleSubmit,
|
|
1034
|
-
|
|
2131
|
+
onCancelQueue: handleCancelQueue,
|
|
2132
|
+
isDisabled: !!permissionRequest || showPluginTUI || isThinking && !!pendingPrompt,
|
|
2133
|
+
isAborting,
|
|
2134
|
+
pendingPrompt,
|
|
1035
2135
|
registry
|
|
1036
2136
|
}
|
|
1037
|
-
)
|
|
2137
|
+
),
|
|
2138
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { children: " " })
|
|
1038
2139
|
] });
|
|
1039
2140
|
}
|
|
1040
2141
|
|
|
1041
2142
|
// src/ui/render.tsx
|
|
1042
|
-
var
|
|
2143
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1043
2144
|
function renderApp(options) {
|
|
1044
2145
|
process.on("unhandledRejection", (reason) => {
|
|
1045
2146
|
process.stderr.write(`
|
|
@@ -1050,26 +2151,47 @@ function renderApp(options) {
|
|
|
1050
2151
|
`);
|
|
1051
2152
|
}
|
|
1052
2153
|
});
|
|
1053
|
-
|
|
2154
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
2155
|
+
process.stdout.write("\x1B[?2004h");
|
|
2156
|
+
}
|
|
2157
|
+
const instance = (0, import_ink14.render)(/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(App, { ...options }), {
|
|
1054
2158
|
exitOnCtrlC: true
|
|
1055
2159
|
});
|
|
1056
|
-
instance.waitUntilExit().
|
|
2160
|
+
instance.waitUntilExit().then(() => {
|
|
2161
|
+
if (process.stdout.isTTY) {
|
|
2162
|
+
process.stdout.write("\x1B[?2004l");
|
|
2163
|
+
}
|
|
2164
|
+
process.exit(0);
|
|
2165
|
+
}).catch((err) => {
|
|
1057
2166
|
if (err) {
|
|
1058
2167
|
process.stderr.write(`
|
|
1059
2168
|
[EXIT ERROR] ${err}
|
|
1060
2169
|
`);
|
|
1061
2170
|
}
|
|
2171
|
+
process.exit(1);
|
|
1062
2172
|
});
|
|
1063
2173
|
}
|
|
1064
2174
|
|
|
1065
2175
|
// src/cli.ts
|
|
1066
2176
|
var import_meta = {};
|
|
1067
|
-
|
|
2177
|
+
function checkSettingsFile(filePath) {
|
|
2178
|
+
if (!(0, import_node_fs2.existsSync)(filePath)) return "missing";
|
|
2179
|
+
try {
|
|
2180
|
+
const raw = (0, import_node_fs2.readFileSync)(filePath, "utf8").trim();
|
|
2181
|
+
if (raw.length === 0) return "incomplete";
|
|
2182
|
+
const parsed = JSON.parse(raw);
|
|
2183
|
+
const provider = parsed.provider;
|
|
2184
|
+
if (!provider?.apiKey) return "incomplete";
|
|
2185
|
+
return "valid";
|
|
2186
|
+
} catch {
|
|
2187
|
+
return "corrupt";
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
1068
2190
|
function readVersion() {
|
|
1069
2191
|
try {
|
|
1070
2192
|
const thisFile = (0, import_node_url.fileURLToPath)(import_meta.url);
|
|
1071
|
-
const dir = (0,
|
|
1072
|
-
const candidates = [(0,
|
|
2193
|
+
const dir = (0, import_node_path5.dirname)(thisFile);
|
|
2194
|
+
const candidates = [(0, import_node_path5.join)(dir, "..", "..", "package.json"), (0, import_node_path5.join)(dir, "..", "package.json")];
|
|
1073
2195
|
for (const pkgPath of candidates) {
|
|
1074
2196
|
try {
|
|
1075
2197
|
const raw = (0, import_node_fs2.readFileSync)(pkgPath, "utf-8");
|
|
@@ -1085,97 +2207,107 @@ function readVersion() {
|
|
|
1085
2207
|
return "0.0.0";
|
|
1086
2208
|
}
|
|
1087
2209
|
}
|
|
1088
|
-
function
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
2210
|
+
function promptInput(label, masked = false) {
|
|
2211
|
+
return new Promise((resolve) => {
|
|
2212
|
+
process.stdout.write(label);
|
|
2213
|
+
let input = "";
|
|
2214
|
+
const stdin = process.stdin;
|
|
2215
|
+
const wasRaw = stdin.isRaw;
|
|
2216
|
+
stdin.setRawMode(true);
|
|
2217
|
+
stdin.resume();
|
|
2218
|
+
stdin.setEncoding("utf8");
|
|
2219
|
+
const onData = (data) => {
|
|
2220
|
+
for (const ch of data) {
|
|
2221
|
+
if (ch === "\r" || ch === "\n") {
|
|
2222
|
+
stdin.removeListener("data", onData);
|
|
2223
|
+
stdin.setRawMode(wasRaw ?? false);
|
|
2224
|
+
stdin.pause();
|
|
2225
|
+
process.stdout.write("\n");
|
|
2226
|
+
resolve(input.trim());
|
|
2227
|
+
return;
|
|
2228
|
+
} else if (ch === "\x7F" || ch === "\b") {
|
|
2229
|
+
if (input.length > 0) {
|
|
2230
|
+
input = input.slice(0, -1);
|
|
2231
|
+
process.stdout.write("\b \b");
|
|
2232
|
+
}
|
|
2233
|
+
} else if (ch === "") {
|
|
2234
|
+
process.stdout.write("\n");
|
|
2235
|
+
process.exit(0);
|
|
2236
|
+
} else if (ch.charCodeAt(0) >= 32) {
|
|
2237
|
+
input += ch;
|
|
2238
|
+
process.stdout.write(masked ? "*" : ch);
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
};
|
|
2242
|
+
stdin.on("data", onData);
|
|
1119
2243
|
});
|
|
1120
|
-
return {
|
|
1121
|
-
positional: positionals,
|
|
1122
|
-
printMode: values["p"] ?? false,
|
|
1123
|
-
continueMode: values["c"] ?? false,
|
|
1124
|
-
resumeId: values["r"],
|
|
1125
|
-
model: values["model"],
|
|
1126
|
-
permissionMode: parsePermissionMode(values["permission-mode"]),
|
|
1127
|
-
maxTurns: parseMaxTurns(values["max-turns"]),
|
|
1128
|
-
version: values["version"] ?? false
|
|
1129
|
-
};
|
|
1130
2244
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
2245
|
+
async function ensureConfig(cwd) {
|
|
2246
|
+
const userPath = getUserSettingsPath();
|
|
2247
|
+
const projectPath = (0, import_node_path5.join)(cwd, ".robota", "settings.json");
|
|
2248
|
+
const localPath = (0, import_node_path5.join)(cwd, ".robota", "settings.local.json");
|
|
2249
|
+
const paths = [userPath, projectPath, localPath];
|
|
2250
|
+
const checks = paths.map((p) => ({ path: p, status: checkSettingsFile(p) }));
|
|
2251
|
+
if (checks.some((c) => c.status === "valid")) {
|
|
2252
|
+
return;
|
|
1134
2253
|
}
|
|
1135
|
-
|
|
1136
|
-
|
|
2254
|
+
const corrupt = checks.filter((c) => c.status === "corrupt");
|
|
2255
|
+
const incomplete = checks.filter((c) => c.status === "incomplete");
|
|
2256
|
+
process.stdout.write("\n");
|
|
2257
|
+
if (corrupt.length > 0) {
|
|
2258
|
+
for (const c of corrupt) {
|
|
2259
|
+
process.stderr.write(` ERROR: Settings file is corrupt (invalid JSON): ${c.path}
|
|
2260
|
+
`);
|
|
2261
|
+
}
|
|
2262
|
+
process.stdout.write("\n");
|
|
1137
2263
|
}
|
|
1138
|
-
|
|
1139
|
-
|
|
2264
|
+
if (incomplete.length > 0) {
|
|
2265
|
+
for (const c of incomplete) {
|
|
2266
|
+
process.stderr.write(` WARNING: Settings file is missing provider.apiKey: ${c.path}
|
|
2267
|
+
`);
|
|
2268
|
+
}
|
|
2269
|
+
process.stdout.write("\n");
|
|
1140
2270
|
}
|
|
1141
|
-
|
|
1142
|
-
process.
|
|
2271
|
+
if (corrupt.length === 0 && incomplete.length === 0) {
|
|
2272
|
+
process.stdout.write(" Welcome to Robota CLI!\n");
|
|
2273
|
+
process.stdout.write(" No configuration found. Let's set up.\n");
|
|
2274
|
+
} else {
|
|
2275
|
+
process.stdout.write(" Reconfiguring...\n");
|
|
1143
2276
|
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
terminal: false,
|
|
1150
|
-
historySize: 0
|
|
1151
|
-
});
|
|
1152
|
-
rl.question(question, (answer) => {
|
|
1153
|
-
rl.close();
|
|
1154
|
-
resolve(answer);
|
|
1155
|
-
});
|
|
1156
|
-
});
|
|
2277
|
+
process.stdout.write("\n");
|
|
2278
|
+
const apiKey = await promptInput(" Anthropic API key: ", true);
|
|
2279
|
+
if (!apiKey) {
|
|
2280
|
+
process.stderr.write("\n No API key provided. Exiting.\n");
|
|
2281
|
+
process.exit(1);
|
|
1157
2282
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
2283
|
+
const language = await promptInput(" Response language (ko/en/ja/zh, default: en): ");
|
|
2284
|
+
const settingsDir = (0, import_node_path5.dirname)(userPath);
|
|
2285
|
+
(0, import_node_fs2.mkdirSync)(settingsDir, { recursive: true });
|
|
2286
|
+
const settings = {
|
|
2287
|
+
provider: {
|
|
2288
|
+
name: "anthropic",
|
|
2289
|
+
model: "claude-sonnet-4-6",
|
|
2290
|
+
apiKey
|
|
1163
2291
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
const trimmed = answer.trim().toLowerCase();
|
|
1168
|
-
if (trimmed === "") return initialIndex;
|
|
1169
|
-
const num = parseInt(trimmed, 10);
|
|
1170
|
-
if (!isNaN(num) && num >= 1 && num <= options.length) return num - 1;
|
|
1171
|
-
return initialIndex;
|
|
2292
|
+
};
|
|
2293
|
+
if (language) {
|
|
2294
|
+
settings.language = language;
|
|
1172
2295
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
2296
|
+
(0, import_node_fs2.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
2297
|
+
process.stdout.write(`
|
|
2298
|
+
Config saved to ${userPath}
|
|
2299
|
+
|
|
2300
|
+
`);
|
|
2301
|
+
}
|
|
2302
|
+
function resetConfig() {
|
|
2303
|
+
const userPath = getUserSettingsPath();
|
|
2304
|
+
if (deleteSettings(userPath)) {
|
|
2305
|
+
process.stdout.write(`Deleted ${userPath}
|
|
2306
|
+
`);
|
|
2307
|
+
} else {
|
|
2308
|
+
process.stdout.write("No user settings found.\n");
|
|
1177
2309
|
}
|
|
1178
|
-
}
|
|
2310
|
+
}
|
|
1179
2311
|
async function startCli() {
|
|
1180
2312
|
const args = parseCliArgs();
|
|
1181
2313
|
if (args.version) {
|
|
@@ -1183,16 +2315,24 @@ async function startCli() {
|
|
|
1183
2315
|
`);
|
|
1184
2316
|
return;
|
|
1185
2317
|
}
|
|
2318
|
+
if (args.reset) {
|
|
2319
|
+
resetConfig();
|
|
2320
|
+
return;
|
|
2321
|
+
}
|
|
1186
2322
|
const cwd = process.cwd();
|
|
2323
|
+
await ensureConfig(cwd);
|
|
1187
2324
|
const [config, context, projectInfo] = await Promise.all([
|
|
1188
|
-
(0,
|
|
1189
|
-
(0,
|
|
1190
|
-
(0,
|
|
2325
|
+
(0, import_agent_sdk3.loadConfig)(cwd),
|
|
2326
|
+
(0, import_agent_sdk3.loadContext)(cwd),
|
|
2327
|
+
(0, import_agent_sdk3.detectProject)(cwd)
|
|
1191
2328
|
]);
|
|
1192
2329
|
if (args.model !== void 0) {
|
|
1193
2330
|
config.provider.model = args.model;
|
|
1194
2331
|
}
|
|
1195
|
-
|
|
2332
|
+
if (args.language !== void 0) {
|
|
2333
|
+
config.language = args.language;
|
|
2334
|
+
}
|
|
2335
|
+
const sessionStore = new import_agent_sdk3.SessionStore();
|
|
1196
2336
|
if (args.printMode) {
|
|
1197
2337
|
const prompt = args.positional.join(" ").trim();
|
|
1198
2338
|
if (prompt.length === 0) {
|
|
@@ -1200,15 +2340,15 @@ async function startCli() {
|
|
|
1200
2340
|
process.exit(1);
|
|
1201
2341
|
}
|
|
1202
2342
|
const terminal = new PrintTerminal();
|
|
1203
|
-
const paths = (0,
|
|
1204
|
-
const session = (0,
|
|
2343
|
+
const paths = (0, import_agent_sdk3.projectPaths)(cwd);
|
|
2344
|
+
const session = (0, import_agent_sdk3.createSession)({
|
|
1205
2345
|
config,
|
|
1206
2346
|
context,
|
|
1207
2347
|
terminal,
|
|
1208
|
-
sessionLogger: new
|
|
2348
|
+
sessionLogger: new import_agent_sdk3.FileSessionLogger(paths.logs),
|
|
1209
2349
|
projectInfo,
|
|
1210
2350
|
permissionMode: args.permissionMode,
|
|
1211
|
-
promptForApproval
|
|
2351
|
+
promptForApproval: import_agent_sdk4.promptForApproval
|
|
1212
2352
|
});
|
|
1213
2353
|
const response = await session.run(prompt);
|
|
1214
2354
|
process.stdout.write(response + "\n");
|