@menteeai/menteeswe 0.1.7 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +450 -194
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -107,7 +107,12 @@ function killTree(child) {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
function killActiveChild() {
|
|
110
|
-
if (activeChild)
|
|
110
|
+
if (!activeChild) return;
|
|
111
|
+
try {
|
|
112
|
+
activeChild.kill("SIGKILL");
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
killTree(activeChild);
|
|
111
116
|
}
|
|
112
117
|
async function runShellCommand(command, cwd, timeoutMs = 12e4, maxChars = 1e5) {
|
|
113
118
|
return new Promise((resolve) => {
|
|
@@ -246,17 +251,17 @@ function friendlyToolName(name) {
|
|
|
246
251
|
function formatEvent(event) {
|
|
247
252
|
switch (event.type) {
|
|
248
253
|
case "task_started":
|
|
249
|
-
return chalk.bold.magenta(`\u25B8
|
|
254
|
+
return "\n" + chalk.bold.magenta(`\u25B8 Task: ${event.message ?? ""}`);
|
|
250
255
|
case "model_request":
|
|
251
|
-
return chalk.gray("\u25CF
|
|
256
|
+
return chalk.gray("\u25CF thinking...");
|
|
252
257
|
case "info":
|
|
253
|
-
return chalk.cyan.bold(`\u{1F4AD}
|
|
258
|
+
return chalk.cyan.bold(`\u{1F4AD} ${event.message ?? ""}`);
|
|
254
259
|
case "tool_started": {
|
|
255
260
|
const tool = typeof event.data?.tool === "string" ? event.data.tool : "";
|
|
256
261
|
const color = CATEGORY_COLOR[TOOL_CATEGORY[tool] ?? "other"];
|
|
257
262
|
const friendly = friendlyToolName(tool);
|
|
258
263
|
const preview = tool && event.message?.startsWith(tool) ? event.message.slice(tool.length).trim() : event.message ?? "";
|
|
259
|
-
return color(`\u2699
|
|
264
|
+
return color(`\u2699 ${friendly} (${preview})...`);
|
|
260
265
|
}
|
|
261
266
|
case "tool_completed": {
|
|
262
267
|
const tool = typeof event.data?.tool === "string" ? event.data.tool : "";
|
|
@@ -264,14 +269,32 @@ function formatEvent(event) {
|
|
|
264
269
|
const output = typeof event.data?.output === "string" ? event.data.output.trim() : "";
|
|
265
270
|
const firstLine = (output.split("\n")[0] ?? event.message ?? "").slice(0, 160);
|
|
266
271
|
const color = success ? CATEGORY_COLOR[TOOL_CATEGORY[tool] ?? "other"] : chalk.red;
|
|
267
|
-
return color(` ${success ? "\u2713" : "\u2717"}
|
|
272
|
+
return color(` ${success ? "\u2713" : "\u2717"} ${firstLine}`);
|
|
268
273
|
}
|
|
269
274
|
case "approval":
|
|
270
|
-
return chalk.magenta(`\u{1F510}
|
|
275
|
+
return chalk.magenta(`\u{1F510} Asking permission to use ${event.message ?? "tool"}...`);
|
|
271
276
|
case "warning":
|
|
272
|
-
return chalk.yellow(`\u26A0
|
|
277
|
+
return chalk.yellow(`\u26A0 ${event.message ?? ""}`);
|
|
273
278
|
case "error":
|
|
274
|
-
return chalk.red(`\u2716
|
|
279
|
+
return chalk.red(`\u2716 ${event.message ?? ""}`);
|
|
280
|
+
case "patch": {
|
|
281
|
+
const filePath = typeof event.data?.path === "string" ? event.data.path : "file";
|
|
282
|
+
const oldStr = typeof event.data?.old_string === "string" ? event.data.old_string : "";
|
|
283
|
+
const newStr = typeof event.data?.new_string === "string" ? event.data.new_string : "";
|
|
284
|
+
const MAX = 60;
|
|
285
|
+
const oldLines = oldStr.split(/\r?\n/);
|
|
286
|
+
const newLines = newStr.split(/\r?\n/);
|
|
287
|
+
const out = [chalk.bold.magenta(`\u270E ${filePath}`)];
|
|
288
|
+
for (const line of oldLines.slice(0, MAX)) {
|
|
289
|
+
out.push(chalk.bgRed.white(` - ${line}`));
|
|
290
|
+
}
|
|
291
|
+
if (oldLines.length > MAX) out.push(chalk.bgRed.white(` \u2026 ${oldLines.length - MAX} more removed line(s)`));
|
|
292
|
+
for (const line of newLines.slice(0, MAX)) {
|
|
293
|
+
out.push(chalk.bgGreen.black(` + ${line}`));
|
|
294
|
+
}
|
|
295
|
+
if (newLines.length > MAX) out.push(chalk.bgGreen.black(` \u2026 ${newLines.length - MAX} more added line(s)`));
|
|
296
|
+
return out.join("\n");
|
|
297
|
+
}
|
|
275
298
|
case "task_completed": {
|
|
276
299
|
const success = event.data?.success === true;
|
|
277
300
|
const usage = event.data?.usage;
|
|
@@ -292,10 +315,12 @@ function formatEvent(event) {
|
|
|
292
315
|
if (modifiedFiles.length > 0) stats.push(`files ${modifiedFiles.length}`);
|
|
293
316
|
const sep = chalk.dim("\u2500".repeat(48));
|
|
294
317
|
const statsLine = chalk.cyan(` ${stats.join(" \xB7 ")}`);
|
|
295
|
-
const
|
|
318
|
+
const cancelled = event.data?.cancelled === true;
|
|
319
|
+
const statusLine = cancelled ? chalk.yellow.bold("\u2716 Task cancelled") : success ? chalk.green.bold("\u2714 Task completed") : chalk.red.bold("\u2716 Task failed");
|
|
296
320
|
const filesLine = modifiedFiles.length > 0 ? chalk.cyan.dim(` \u{1F4DD} ${modifiedFiles.length} file(s): ${modifiedFiles.join(", ")}`) : "";
|
|
297
321
|
const tipLine = success ? chalk.dim(" Next: type a new task, or /help for commands") : chalk.yellow.dim(" The task did not finish \u2014 check the steps above and retry");
|
|
298
|
-
const
|
|
322
|
+
const detailsLine = toolCalls > 0 ? chalk.dim(` \u{1F50D} press d to toggle ${toolCalls} tool call(s)`) : "";
|
|
323
|
+
const block = [sep, statusLine, statsLine, filesLine, tipLine, detailsLine, sep, ""];
|
|
299
324
|
return block.filter((line) => line !== "").join("\n");
|
|
300
325
|
}
|
|
301
326
|
default:
|
|
@@ -459,6 +484,9 @@ function buildSystemPrompt(cwd) {
|
|
|
459
484
|
const tree = projectTree(cwd).trim() || "(empty)";
|
|
460
485
|
return `You are MenteE, an autonomous software-engineering agent working inside a repository on the user's machine.
|
|
461
486
|
|
|
487
|
+
# Your design philosophy
|
|
488
|
+
You are built around a tool harness: every meaningful action (reading, searching, editing, running, git) goes through a tool. Use the harness decisively and efficiently. Prefer precise tool calls over long prose so token usage stays very low while your engineering stays sharp and intelligent. A good session does more with fewer tokens \u2014 call exactly the tool needed, read only what you must, and let the harness do the heavy lifting.
|
|
489
|
+
|
|
462
490
|
# Environment
|
|
463
491
|
- Workspace: ${cwd}
|
|
464
492
|
- Platform: ${os2.platform()} (${os2.release()}), shell commands run with the system shell
|
|
@@ -479,6 +507,8 @@ ${tree}
|
|
|
479
507
|
# Tool usage notes
|
|
480
508
|
- Call only the tools needed for the immediate next step. Do NOT read files you already understand, and do NOT read build/config files (e.g. *.config.ts, tsconfig.json) unless the task specifically needs them.
|
|
481
509
|
- Prefer search_code over reading many files.
|
|
510
|
+
- NEVER start long-running servers, dev servers, or watchers (e.g. 'npm run dev', 'npm start', 'vite', 'npm run watch', 'python -m http.server') yourself. These block and never exit. If the user needs to run the app, give them the exact command to run in their OWN terminal. Only run commands that exit on their own.
|
|
511
|
+
- If a task result requires running the app to verify, say so and provide the command \u2014 do not run it for them.
|
|
482
512
|
|
|
483
513
|
# Research strategy (IMPORTANT \u2014 HARD RULE)
|
|
484
514
|
- The file tree above already shows the whole structure. Do NOT read files just to "see what's there".
|
|
@@ -563,16 +593,24 @@ function appendTurn(cwd, task, response) {
|
|
|
563
593
|
if (turns.length > 50) turns = turns.slice(-50);
|
|
564
594
|
fs9.writeFileSync(file, JSON.stringify(turns, null, 2));
|
|
565
595
|
}
|
|
566
|
-
function formatConversationContext(cwd, limit =
|
|
596
|
+
function formatConversationContext(cwd, limit = 10) {
|
|
567
597
|
const turns = loadConversation(cwd, limit);
|
|
568
598
|
if (turns.length === 0) return "";
|
|
569
|
-
const
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
599
|
+
const MAX_PRIOR_CHARS = 5e3;
|
|
600
|
+
const header = "# Prior conversation in this project (most recent shown)";
|
|
601
|
+
const blocks = [];
|
|
602
|
+
let total = header.length;
|
|
603
|
+
for (let i = turns.length - 1; i >= 0; i--) {
|
|
604
|
+
const t = turns[i];
|
|
605
|
+
if (!t) continue;
|
|
606
|
+
const block = `User: ${t.task}
|
|
607
|
+
Assistant: ${t.response.trim()}
|
|
608
|
+
`;
|
|
609
|
+
if (blocks.length > 0 && total + block.length > MAX_PRIOR_CHARS) break;
|
|
610
|
+
blocks.unshift(block);
|
|
611
|
+
total += block.length;
|
|
574
612
|
}
|
|
575
|
-
return
|
|
613
|
+
return [header, ...blocks, ""].join("\n");
|
|
576
614
|
}
|
|
577
615
|
var init_conversation = __esm({
|
|
578
616
|
"src/agent/conversation.ts"() {
|
|
@@ -631,11 +669,11 @@ function isRateLimitError(error) {
|
|
|
631
669
|
}
|
|
632
670
|
return /\b429\b|max rpm|rate limit|too many requests/i.test(error.message);
|
|
633
671
|
}
|
|
634
|
-
async function generateWithRetry(provider, model, system, messages, tools, bus) {
|
|
672
|
+
async function generateWithRetry(provider, model, system, messages, tools, bus, onToken) {
|
|
635
673
|
let lastError = null;
|
|
636
674
|
for (let attempt = 0; attempt <= RATE_LIMIT_MAX_ATTEMPTS; attempt++) {
|
|
637
675
|
try {
|
|
638
|
-
return await provider.generate({ system, messages, tools: tools.schemas() }, model);
|
|
676
|
+
return await provider.generate({ system, messages, tools: tools.schemas(), onToken }, model);
|
|
639
677
|
} catch (error) {
|
|
640
678
|
lastError = error;
|
|
641
679
|
if (attempt >= RATE_LIMIT_MAX_ATTEMPTS || !isRateLimitError(lastError)) {
|
|
@@ -670,10 +708,17 @@ function estimateContextChars(messages, system) {
|
|
|
670
708
|
return total;
|
|
671
709
|
}
|
|
672
710
|
function trimOldToolResults(messages) {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
711
|
+
let seen = 0;
|
|
712
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
713
|
+
const message = messages[i];
|
|
714
|
+
if (!message) continue;
|
|
715
|
+
if (message.role !== "tool") continue;
|
|
716
|
+
seen++;
|
|
717
|
+
if (seen <= KEEP_RECENT_TOOL) continue;
|
|
718
|
+
const content = message.content ?? "";
|
|
719
|
+
if (content.length <= 240) continue;
|
|
720
|
+
const head = content.replace(/\s+/g, " ").slice(0, 160);
|
|
721
|
+
message.content = `\u27E8prior tool output trimmed: ${head}\u2026 (${content.length} chars)\u27E9`;
|
|
677
722
|
}
|
|
678
723
|
}
|
|
679
724
|
async function runAgent(options) {
|
|
@@ -719,10 +764,24 @@ ${systemExtra}` : "");
|
|
|
719
764
|
state.iteration += 1;
|
|
720
765
|
state.usage.modelRequests += 1;
|
|
721
766
|
bus.emit("model_request", `iteration ${state.iteration}/${maxIterations}`);
|
|
767
|
+
let thinkBuf = "";
|
|
768
|
+
let thinkTimer = null;
|
|
769
|
+
const flushThink = () => {
|
|
770
|
+
if (thinkBuf) {
|
|
771
|
+
bus.emit("thinking", thinkBuf);
|
|
772
|
+
thinkBuf = "";
|
|
773
|
+
}
|
|
774
|
+
thinkTimer = null;
|
|
775
|
+
};
|
|
776
|
+
const onToken = (delta) => {
|
|
777
|
+
thinkBuf += delta;
|
|
778
|
+
if (thinkTimer == null) thinkTimer = setTimeout(flushThink, 80);
|
|
779
|
+
};
|
|
722
780
|
let response;
|
|
723
781
|
try {
|
|
724
|
-
response = await generateWithRetry(provider, model, system, messages, tools, bus);
|
|
782
|
+
response = await generateWithRetry(provider, model, system, messages, tools, bus, onToken);
|
|
725
783
|
} catch (error) {
|
|
784
|
+
flushThink();
|
|
726
785
|
const message = error.message;
|
|
727
786
|
let hint = "";
|
|
728
787
|
if (/insufficient balance|no resource package|recharge/i.test(message)) {
|
|
@@ -744,6 +803,7 @@ ${systemExtra}` : "");
|
|
|
744
803
|
});
|
|
745
804
|
return { success: false, finalText, state };
|
|
746
805
|
}
|
|
806
|
+
flushThink();
|
|
747
807
|
bus.emit(
|
|
748
808
|
"model_response",
|
|
749
809
|
void 0,
|
|
@@ -772,7 +832,7 @@ ${systemExtra}` : "");
|
|
|
772
832
|
content: response.content,
|
|
773
833
|
tool_calls: response.toolCalls
|
|
774
834
|
});
|
|
775
|
-
if (response.content && response.content.trim()) {
|
|
835
|
+
if (!onToken && response.content && response.content.trim()) {
|
|
776
836
|
bus.emit("info", response.content.trim().slice(0, 300));
|
|
777
837
|
}
|
|
778
838
|
for (const call of response.toolCalls) {
|
|
@@ -866,6 +926,13 @@ ${systemExtra}` : "");
|
|
|
866
926
|
success: result.success,
|
|
867
927
|
output: result.output.slice(0, 2e3)
|
|
868
928
|
});
|
|
929
|
+
if (tool.name === "apply_patch" && result.success && result.data?.kind === "patch") {
|
|
930
|
+
bus.emit("patch", void 0, {
|
|
931
|
+
path: result.data.path,
|
|
932
|
+
old_string: result.data.old_string,
|
|
933
|
+
new_string: result.data.new_string
|
|
934
|
+
});
|
|
935
|
+
}
|
|
869
936
|
messages.push({
|
|
870
937
|
role: "tool",
|
|
871
938
|
tool_call_id: call.id,
|
|
@@ -894,11 +961,12 @@ ${systemExtra}` : "");
|
|
|
894
961
|
toolCalls: state.totalToolCalls,
|
|
895
962
|
modifiedFiles: state.modifiedFiles,
|
|
896
963
|
usage: state.usage,
|
|
897
|
-
durationMs: Date.now() - state.startedAt
|
|
964
|
+
durationMs: Date.now() - state.startedAt,
|
|
965
|
+
cancelled: signal?.aborted === true
|
|
898
966
|
});
|
|
899
967
|
return { success, finalText, state };
|
|
900
968
|
}
|
|
901
|
-
var MAX_CONTEXT_CHARS, READ_BUDGET, RATE_LIMIT_MAX_ATTEMPTS;
|
|
969
|
+
var MAX_CONTEXT_CHARS, READ_BUDGET, RATE_LIMIT_MAX_ATTEMPTS, KEEP_RECENT_TOOL;
|
|
902
970
|
var init_loop = __esm({
|
|
903
971
|
"src/agent/loop.ts"() {
|
|
904
972
|
"use strict";
|
|
@@ -906,15 +974,116 @@ var init_loop = __esm({
|
|
|
906
974
|
init_conversation();
|
|
907
975
|
init_state();
|
|
908
976
|
init_base();
|
|
909
|
-
MAX_CONTEXT_CHARS =
|
|
977
|
+
MAX_CONTEXT_CHARS = 7e4;
|
|
910
978
|
READ_BUDGET = 6;
|
|
911
979
|
RATE_LIMIT_MAX_ATTEMPTS = 8;
|
|
980
|
+
KEEP_RECENT_TOOL = 4;
|
|
912
981
|
}
|
|
913
982
|
});
|
|
914
983
|
|
|
915
|
-
// src/tui/
|
|
916
|
-
import { Box, Text
|
|
984
|
+
// src/tui/markdown.tsx
|
|
985
|
+
import { Box, Text } from "ink";
|
|
917
986
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
987
|
+
function renderInline(text, keyPrefix) {
|
|
988
|
+
const nodes = [];
|
|
989
|
+
const regex = /(\*\*([^*]+)\*\*)|(\*([^*]+)\*)|(`([^`]+)`)/g;
|
|
990
|
+
let last = 0;
|
|
991
|
+
let m;
|
|
992
|
+
let i = 0;
|
|
993
|
+
while ((m = regex.exec(text)) !== null) {
|
|
994
|
+
if (m.index > last) nodes.push(text.slice(last, m.index));
|
|
995
|
+
if (m[2] !== void 0) {
|
|
996
|
+
nodes.push(
|
|
997
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: m[2] }, `${keyPrefix}-b${i}`)
|
|
998
|
+
);
|
|
999
|
+
} else if (m[4] !== void 0) {
|
|
1000
|
+
nodes.push(
|
|
1001
|
+
/* @__PURE__ */ jsx(Text, { italic: true, children: m[4] }, `${keyPrefix}-i${i}`)
|
|
1002
|
+
);
|
|
1003
|
+
} else if (m[6] !== void 0) {
|
|
1004
|
+
nodes.push(
|
|
1005
|
+
/* @__PURE__ */ jsx(Text, { color: "greenBright", children: m[6] }, `${keyPrefix}-c${i}`)
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
last = m.index + m[0].length;
|
|
1009
|
+
i++;
|
|
1010
|
+
}
|
|
1011
|
+
if (last < text.length) nodes.push(text.slice(last));
|
|
1012
|
+
return nodes;
|
|
1013
|
+
}
|
|
1014
|
+
function renderMarkdown(md) {
|
|
1015
|
+
const lines = md.split("\n");
|
|
1016
|
+
const blocks = [];
|
|
1017
|
+
let para = [];
|
|
1018
|
+
let key = 0;
|
|
1019
|
+
const flushPara = () => {
|
|
1020
|
+
if (para.length === 0) return;
|
|
1021
|
+
blocks.push(/* @__PURE__ */ jsx(Text, { children: renderInline(para.join(" "), `p${key}`) }, `p${key++}`));
|
|
1022
|
+
para = [];
|
|
1023
|
+
};
|
|
1024
|
+
for (const line of lines) {
|
|
1025
|
+
if (line.trim() === "") {
|
|
1026
|
+
flushPara();
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
const heading = /^(#{1,6})\s+(.*)$/.exec(line);
|
|
1030
|
+
if (heading) {
|
|
1031
|
+
flushPara();
|
|
1032
|
+
const level = heading[1]?.length ?? 0;
|
|
1033
|
+
blocks.push(
|
|
1034
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: level <= 2 ? "cyan" : "white", children: renderInline(heading[2] ?? "", `h${key}`) }, `h${key++}`)
|
|
1035
|
+
);
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
const bullet = /^\s*[-*]\s+(.*)$/.exec(line);
|
|
1039
|
+
if (bullet) {
|
|
1040
|
+
flushPara();
|
|
1041
|
+
blocks.push(
|
|
1042
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1043
|
+
" \u2022 ",
|
|
1044
|
+
renderInline(bullet[1] ?? "", `b${key}`)
|
|
1045
|
+
] }, `b${key++}`)
|
|
1046
|
+
);
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
1049
|
+
const numbered = /^\s*\d+\.\s+(.*)$/.exec(line);
|
|
1050
|
+
if (numbered) {
|
|
1051
|
+
flushPara();
|
|
1052
|
+
blocks.push(
|
|
1053
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1054
|
+
" ",
|
|
1055
|
+
renderInline(numbered[1] ?? "", `n${key}`)
|
|
1056
|
+
] }, `n${key++}`)
|
|
1057
|
+
);
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
para.push(line.trim());
|
|
1061
|
+
}
|
|
1062
|
+
flushPara();
|
|
1063
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: blocks });
|
|
1064
|
+
}
|
|
1065
|
+
function AnswerBlock({ text }) {
|
|
1066
|
+
return /* @__PURE__ */ jsx(
|
|
1067
|
+
Box,
|
|
1068
|
+
{
|
|
1069
|
+
flexDirection: "column",
|
|
1070
|
+
borderStyle: "round",
|
|
1071
|
+
borderColor: "cyan",
|
|
1072
|
+
paddingX: 1,
|
|
1073
|
+
marginY: 0,
|
|
1074
|
+
children: renderMarkdown(text)
|
|
1075
|
+
}
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
var init_markdown = __esm({
|
|
1079
|
+
"src/tui/markdown.tsx"() {
|
|
1080
|
+
"use strict";
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
// src/tui/Approval.tsx
|
|
1085
|
+
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
1086
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
918
1087
|
function argPreview(request) {
|
|
919
1088
|
const args = request.args;
|
|
920
1089
|
if (request.tool === "apply_patch" && typeof args.old_string === "string" && typeof args.new_string === "string") {
|
|
@@ -932,8 +1101,8 @@ function ApprovalPrompt({ request, onDecision }) {
|
|
|
932
1101
|
else if (input === "a") onDecision("always");
|
|
933
1102
|
else if (input === "n" || key.escape) onDecision("deny");
|
|
934
1103
|
});
|
|
935
|
-
return /* @__PURE__ */
|
|
936
|
-
/* @__PURE__ */
|
|
1104
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, marginY: 1, children: [
|
|
1105
|
+
/* @__PURE__ */ jsxs2(Text2, { bold: true, color: "magenta", children: [
|
|
937
1106
|
"Permission needed: ",
|
|
938
1107
|
friendlyToolName(request.tool),
|
|
939
1108
|
" (",
|
|
@@ -941,14 +1110,14 @@ function ApprovalPrompt({ request, onDecision }) {
|
|
|
941
1110
|
") \u2014 ",
|
|
942
1111
|
request.risk
|
|
943
1112
|
] }),
|
|
944
|
-
/* @__PURE__ */
|
|
945
|
-
/* @__PURE__ */
|
|
946
|
-
/* @__PURE__ */
|
|
1113
|
+
/* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { children: argPreview(request) }) }),
|
|
1114
|
+
/* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { children: [
|
|
1115
|
+
/* @__PURE__ */ jsx2(Text2, { color: "green", bold: true, children: "[y]" }),
|
|
947
1116
|
" allow once \xB7 ",
|
|
948
|
-
/* @__PURE__ */
|
|
1117
|
+
/* @__PURE__ */ jsx2(Text2, { color: "cyan", bold: true, children: "[a]" }),
|
|
949
1118
|
" allow this session \xB7",
|
|
950
1119
|
" ",
|
|
951
|
-
/* @__PURE__ */
|
|
1120
|
+
/* @__PURE__ */ jsx2(Text2, { color: "red", bold: true, children: "[n]" }),
|
|
952
1121
|
" deny"
|
|
953
1122
|
] }) })
|
|
954
1123
|
] });
|
|
@@ -962,8 +1131,8 @@ var init_Approval = __esm({
|
|
|
962
1131
|
|
|
963
1132
|
// src/tui/SelectDialog.tsx
|
|
964
1133
|
import { useEffect, useMemo, useState } from "react";
|
|
965
|
-
import { Box as
|
|
966
|
-
import { jsx as
|
|
1134
|
+
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
1135
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
967
1136
|
function SelectDialog({
|
|
968
1137
|
title,
|
|
969
1138
|
items,
|
|
@@ -998,34 +1167,34 @@ function SelectDialog({
|
|
|
998
1167
|
Math.min(cursor - Math.floor(VISIBLE / 2), Math.max(0, filtered.length - VISIBLE))
|
|
999
1168
|
);
|
|
1000
1169
|
const visible = filtered.slice(start, start + VISIBLE);
|
|
1001
|
-
return /* @__PURE__ */
|
|
1002
|
-
/* @__PURE__ */
|
|
1003
|
-
/* @__PURE__ */
|
|
1004
|
-
/* @__PURE__ */
|
|
1170
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginY: 1, children: [
|
|
1171
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: title }),
|
|
1172
|
+
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, children: [
|
|
1173
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1005
1174
|
"search: ",
|
|
1006
1175
|
filter
|
|
1007
1176
|
] }),
|
|
1008
|
-
/* @__PURE__ */
|
|
1177
|
+
/* @__PURE__ */ jsx3(Text3, { children: "\u258F" })
|
|
1009
1178
|
] }),
|
|
1010
|
-
loading ? /* @__PURE__ */
|
|
1011
|
-
error ? /* @__PURE__ */
|
|
1012
|
-
!loading && !error ? /* @__PURE__ */
|
|
1179
|
+
loading ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "loading\u2026" }) : null,
|
|
1180
|
+
error ? /* @__PURE__ */ jsx3(Text3, { color: "red", children: error }) : null,
|
|
1181
|
+
!loading && !error ? /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
1013
1182
|
visible.map((item, i) => {
|
|
1014
1183
|
const index = start + i;
|
|
1015
1184
|
const isCursor = index === cursor;
|
|
1016
1185
|
const isCurrent = item === current;
|
|
1017
1186
|
const description = descriptions?.[item];
|
|
1018
|
-
return /* @__PURE__ */
|
|
1187
|
+
return /* @__PURE__ */ jsxs3(Text3, { color: isCursor ? "cyan" : void 0, bold: isCursor, children: [
|
|
1019
1188
|
isCursor ? "\u276F " : " ",
|
|
1020
1189
|
isCurrent ? "\u25CF " : "\u25CB ",
|
|
1021
1190
|
item,
|
|
1022
1191
|
isCurrent ? " (current)" : "",
|
|
1023
|
-
description ? /* @__PURE__ */
|
|
1192
|
+
description ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: ` \u2014 ${description}` }) : null
|
|
1024
1193
|
] }, item);
|
|
1025
1194
|
}),
|
|
1026
|
-
filtered.length === 0 ? /* @__PURE__ */
|
|
1195
|
+
filtered.length === 0 ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "no matches" }) : null
|
|
1027
1196
|
] }) : null,
|
|
1028
|
-
/* @__PURE__ */
|
|
1197
|
+
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2191\u2193 move \xB7 type to filter \xB7 enter select \xB7 esc cancel" }) })
|
|
1029
1198
|
] });
|
|
1030
1199
|
}
|
|
1031
1200
|
var VISIBLE;
|
|
@@ -1038,22 +1207,22 @@ var init_SelectDialog = __esm({
|
|
|
1038
1207
|
|
|
1039
1208
|
// src/tui/KeyDialog.tsx
|
|
1040
1209
|
import { useState as useState2 } from "react";
|
|
1041
|
-
import { Box as
|
|
1210
|
+
import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
|
|
1042
1211
|
import TextInput from "ink-text-input";
|
|
1043
|
-
import { jsx as
|
|
1212
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1044
1213
|
function KeyDialog({ providerName, onSubmit, onClose }) {
|
|
1045
1214
|
const [value, setValue] = useState2("");
|
|
1046
1215
|
useInput3((_input, key) => {
|
|
1047
1216
|
if (key.escape) onClose();
|
|
1048
1217
|
});
|
|
1049
|
-
return /* @__PURE__ */
|
|
1050
|
-
/* @__PURE__ */
|
|
1218
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, marginY: 1, children: [
|
|
1219
|
+
/* @__PURE__ */ jsxs4(Text4, { bold: true, color: "magenta", children: [
|
|
1051
1220
|
"API key for ",
|
|
1052
1221
|
providerName
|
|
1053
1222
|
] }),
|
|
1054
|
-
/* @__PURE__ */
|
|
1055
|
-
/* @__PURE__ */
|
|
1056
|
-
/* @__PURE__ */
|
|
1223
|
+
/* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
|
|
1224
|
+
/* @__PURE__ */ jsx4(Text4, { children: "key: " }),
|
|
1225
|
+
/* @__PURE__ */ jsx4(
|
|
1057
1226
|
TextInput,
|
|
1058
1227
|
{
|
|
1059
1228
|
mask: "*",
|
|
@@ -1066,7 +1235,7 @@ function KeyDialog({ providerName, onSubmit, onClose }) {
|
|
|
1066
1235
|
}
|
|
1067
1236
|
)
|
|
1068
1237
|
] }),
|
|
1069
|
-
/* @__PURE__ */
|
|
1238
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "enter confirm \xB7 esc cancel \xB7 saved to ~/.mentee/config.json" }) })
|
|
1070
1239
|
] });
|
|
1071
1240
|
}
|
|
1072
1241
|
var init_KeyDialog = __esm({
|
|
@@ -1079,7 +1248,7 @@ var init_KeyDialog = __esm({
|
|
|
1079
1248
|
var version;
|
|
1080
1249
|
var init_package = __esm({
|
|
1081
1250
|
"package.json"() {
|
|
1082
|
-
version = "0.1.
|
|
1251
|
+
version = "0.1.10";
|
|
1083
1252
|
}
|
|
1084
1253
|
});
|
|
1085
1254
|
|
|
@@ -1089,11 +1258,12 @@ __export(App_exports, {
|
|
|
1089
1258
|
App: () => App
|
|
1090
1259
|
});
|
|
1091
1260
|
import { useEffect as useEffect2, useMemo as useMemo2, useRef, useState as useState3 } from "react";
|
|
1092
|
-
import { Box as
|
|
1261
|
+
import { Box as Box5, Static, Text as Text5, useApp, useInput as useInput4, useStdout } from "ink";
|
|
1093
1262
|
import TextInput2 from "ink-text-input";
|
|
1094
|
-
import { jsx as
|
|
1263
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1095
1264
|
function App(props) {
|
|
1096
1265
|
const { exit } = useApp();
|
|
1266
|
+
const { stdout } = useStdout();
|
|
1097
1267
|
const bus = useMemo2(() => new EventBus(), []);
|
|
1098
1268
|
const bridge = useMemo2(() => new ApprovalBridge(), []);
|
|
1099
1269
|
const [log, setLog] = useState3([]);
|
|
@@ -1116,24 +1286,32 @@ function App(props) {
|
|
|
1116
1286
|
const [toolHistory, setToolHistory] = useState3([]);
|
|
1117
1287
|
const [inspectorIndex, setInspectorIndex] = useState3(null);
|
|
1118
1288
|
const [detailsOpen, setDetailsOpen] = useState3(false);
|
|
1119
|
-
const [
|
|
1289
|
+
const [showDetails, setShowDetails] = useState3(false);
|
|
1290
|
+
const [thinkingText, setThinkingText] = useState3("");
|
|
1291
|
+
const finalTextRef = useRef(null);
|
|
1120
1292
|
const pendingTool = useRef(null);
|
|
1121
1293
|
const counter = useRef(0);
|
|
1122
1294
|
const stateRef = useRef({ running, approval, dialog, factoryResult, providerName, model });
|
|
1123
1295
|
stateRef.current = { running, approval, dialog, factoryResult, providerName, model };
|
|
1124
1296
|
const abortRef = useRef(null);
|
|
1125
|
-
const appendLog = (text) => {
|
|
1297
|
+
const appendLog = (text, noise = false, kind) => {
|
|
1126
1298
|
if (!text) return;
|
|
1127
1299
|
counter.current += 1;
|
|
1128
|
-
const line = { id: counter.current, text };
|
|
1300
|
+
const line = { id: counter.current, text, noise, kind };
|
|
1129
1301
|
setLog((prev) => [...prev, line]);
|
|
1130
1302
|
};
|
|
1131
1303
|
useEffect2(() => {
|
|
1132
|
-
const
|
|
1304
|
+
const TOOL_NOISE = /* @__PURE__ */ new Set(["tool_started", "tool_completed", "model_request"]);
|
|
1133
1305
|
const unsubscribeLog = bus.subscribe((event) => {
|
|
1134
|
-
if (
|
|
1135
|
-
|
|
1306
|
+
if (event.type === "thinking") {
|
|
1307
|
+
const delta = (event.message ?? "").replace(/[\r\n]+/g, " ");
|
|
1308
|
+
if (delta) setThinkingText((prev) => (prev + delta).slice(-4e3));
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
if (event.type === "model_request" || event.type === "task_completed") {
|
|
1312
|
+
setThinkingText("");
|
|
1136
1313
|
}
|
|
1314
|
+
appendLog(formatEvent(event), TOOL_NOISE.has(event.type));
|
|
1137
1315
|
if (event.type === "tool_started") {
|
|
1138
1316
|
const name = typeof event.data?.tool === "string" ? event.data.tool : "";
|
|
1139
1317
|
const target = typeof event.message === "string" ? event.message : "";
|
|
@@ -1150,10 +1328,12 @@ function App(props) {
|
|
|
1150
1328
|
} else if (event.type === "task_completed") {
|
|
1151
1329
|
const text = typeof event.data?.finalText === "string" ? event.data.finalText.trim() : "";
|
|
1152
1330
|
if (text) {
|
|
1153
|
-
|
|
1331
|
+
finalTextRef.current = text;
|
|
1154
1332
|
setFinalPending(text);
|
|
1155
1333
|
setTypedText("");
|
|
1156
1334
|
setTyping(true);
|
|
1335
|
+
} else {
|
|
1336
|
+
finalTextRef.current = null;
|
|
1157
1337
|
}
|
|
1158
1338
|
}
|
|
1159
1339
|
});
|
|
@@ -1166,7 +1346,7 @@ function App(props) {
|
|
|
1166
1346
|
unsubscribeLog();
|
|
1167
1347
|
unsubscribeLog2();
|
|
1168
1348
|
};
|
|
1169
|
-
}, [bus, bridge,
|
|
1349
|
+
}, [bus, bridge, showDetails]);
|
|
1170
1350
|
useEffect2(() => {
|
|
1171
1351
|
if (!running) return;
|
|
1172
1352
|
const timer = setInterval(() => setFrame((f) => (f + 1) % SPINNER_FRAMES.length), 80);
|
|
@@ -1185,8 +1365,11 @@ function App(props) {
|
|
|
1185
1365
|
return;
|
|
1186
1366
|
}
|
|
1187
1367
|
const timer = setTimeout(() => {
|
|
1188
|
-
|
|
1189
|
-
|
|
1368
|
+
const rest = finalPending.slice(typedText.length);
|
|
1369
|
+
const match = /^(\S+\s*)/.exec(rest);
|
|
1370
|
+
const step = match ? match[0].length : 1;
|
|
1371
|
+
setTypedText(finalPending.slice(0, typedText.length + step));
|
|
1372
|
+
}, 55);
|
|
1190
1373
|
return () => clearTimeout(timer);
|
|
1191
1374
|
}, [typing, finalPending, typedText]);
|
|
1192
1375
|
const updateConfig = (mutate) => {
|
|
@@ -1325,12 +1508,17 @@ function App(props) {
|
|
|
1325
1508
|
appendLog(factoryResult.error ?? "No provider configured. Press Ctrl+K to add an API key.");
|
|
1326
1509
|
return;
|
|
1327
1510
|
}
|
|
1511
|
+
if (finalTextRef.current) {
|
|
1512
|
+
appendLog(finalTextRef.current, false, "answer");
|
|
1513
|
+
finalTextRef.current = null;
|
|
1514
|
+
}
|
|
1328
1515
|
setFinalPending(null);
|
|
1329
1516
|
setTypedText("");
|
|
1330
1517
|
setTyping(false);
|
|
1331
1518
|
setToolHistory([]);
|
|
1332
1519
|
setInspectorIndex(null);
|
|
1333
1520
|
setDetailsOpen(false);
|
|
1521
|
+
setShowDetails(false);
|
|
1334
1522
|
setRunning(true);
|
|
1335
1523
|
const ac = new AbortController();
|
|
1336
1524
|
abortRef.current = ac;
|
|
@@ -1384,6 +1572,7 @@ function App(props) {
|
|
|
1384
1572
|
useInput4((input, key) => {
|
|
1385
1573
|
if (key.ctrl && input === "c") {
|
|
1386
1574
|
if (stateRef.current.running) {
|
|
1575
|
+
appendLog("\u23F9 Interrupted by you \u2014 stopping the current task and returning to the prompt.");
|
|
1387
1576
|
killActiveChild();
|
|
1388
1577
|
abortRef.current?.abort();
|
|
1389
1578
|
return;
|
|
@@ -1392,130 +1581,137 @@ function App(props) {
|
|
|
1392
1581
|
setTimeout(() => process.exit(0), 50);
|
|
1393
1582
|
return;
|
|
1394
1583
|
}
|
|
1584
|
+
if (key.meta && (input === "d" || input === "D")) {
|
|
1585
|
+
setShowDetails((v) => !v);
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1395
1588
|
if (stateRef.current.dialog || stateRef.current.approval || stateRef.current.running) return;
|
|
1396
1589
|
if (key.meta && (input === "m" || input === "M")) openModelDialog();
|
|
1397
1590
|
else if (key.ctrl && input === "p") setDialog({ kind: "provider" });
|
|
1398
1591
|
else if (key.ctrl && input === "k") setDialog({ kind: "key" });
|
|
1399
|
-
else if (input === "d" || input === "D") cycleInspector();
|
|
1400
|
-
else if (input === "t" || input === "T") setShowTools((v) => !v);
|
|
1401
1592
|
});
|
|
1402
1593
|
const modelLabel = model ?? factoryResult.provider?.defaultModel ?? (factoryResult.error ? "no key" : "");
|
|
1403
|
-
const header = /* @__PURE__ */
|
|
1404
|
-
/* @__PURE__ */
|
|
1405
|
-
/* @__PURE__ */
|
|
1594
|
+
const header = /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: 1, children: [
|
|
1595
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: BANNER }),
|
|
1596
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1406
1597
|
"v",
|
|
1407
1598
|
version,
|
|
1408
1599
|
" \xB7 ",
|
|
1409
1600
|
providerName,
|
|
1410
1601
|
modelLabel ? `:${modelLabel}` : "",
|
|
1411
|
-
" \xB7
|
|
1412
|
-
|
|
1602
|
+
" \xB7 details ",
|
|
1603
|
+
showDetails ? "open" : "collapsed",
|
|
1413
1604
|
" \xB7 ",
|
|
1414
1605
|
props.cwd
|
|
1415
1606
|
] }),
|
|
1416
|
-
/* @__PURE__ */
|
|
1607
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Alt+M model \xB7 Ctrl+P provider \xB7 Ctrl+K key \xB7 Alt+D details \xB7 /help" })
|
|
1417
1608
|
] }, "header");
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
setDialog(null)
|
|
1466
|
-
setModelAndPersist(id);
|
|
1467
|
-
},
|
|
1468
|
-
onClose: () => setDialog(null)
|
|
1469
|
-
}
|
|
1470
|
-
) : dialog?.kind === "provider" ? /* @__PURE__ */ jsx4(
|
|
1471
|
-
SelectDialog,
|
|
1472
|
-
{
|
|
1473
|
-
title: "Provider",
|
|
1474
|
-
items: PROVIDER_NAMES,
|
|
1475
|
-
descriptions: PROVIDER_DESCRIPTIONS,
|
|
1476
|
-
current: providerName,
|
|
1477
|
-
onSelect: (name) => {
|
|
1478
|
-
setDialog(null);
|
|
1479
|
-
switchProvider(name);
|
|
1480
|
-
},
|
|
1481
|
-
onClose: () => setDialog(null)
|
|
1482
|
-
}
|
|
1483
|
-
) : dialog?.kind === "key" ? /* @__PURE__ */ jsx4(
|
|
1484
|
-
KeyDialog,
|
|
1485
|
-
{
|
|
1486
|
-
providerName,
|
|
1487
|
-
onSubmit: (value) => {
|
|
1488
|
-
setDialog(null);
|
|
1489
|
-
saveKey(value);
|
|
1490
|
-
},
|
|
1491
|
-
onClose: () => setDialog(null)
|
|
1492
|
-
}
|
|
1493
|
-
) : approval ? /* @__PURE__ */ jsx4(
|
|
1494
|
-
ApprovalPrompt,
|
|
1495
|
-
{
|
|
1496
|
-
request: approval,
|
|
1497
|
-
onDecision: (decision) => {
|
|
1498
|
-
bridge.resolve(decision);
|
|
1499
|
-
if (decision === "always") bus.emit("info", "Approved for the rest of this session.");
|
|
1609
|
+
const cols = stdout.columns || 80;
|
|
1610
|
+
const marquee = thinkingText.slice(-Math.max(1, cols - 4));
|
|
1611
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
1612
|
+
/* @__PURE__ */ jsx5(Static, { items: [{ key: "header", text: header }], children: (item) => /* @__PURE__ */ jsx5(Box5, { children: item.text }, item.key) }),
|
|
1613
|
+
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
1614
|
+
log.filter((l) => showDetails || !l.noise).map(
|
|
1615
|
+
(line) => line.kind === "answer" ? /* @__PURE__ */ jsx5(AnswerBlock, { text: line.text }, line.id) : /* @__PURE__ */ jsx5(Text5, { children: line.text }, line.id)
|
|
1616
|
+
),
|
|
1617
|
+
finalPending !== null ? /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
|
|
1618
|
+
/* @__PURE__ */ jsx5(Text5, { color: "green", children: typedText }),
|
|
1619
|
+
typing ? /* @__PURE__ */ jsx5(Text5, { color: "green", children: "\u258C" }) : null
|
|
1620
|
+
] }) : null,
|
|
1621
|
+
detailsOpen && inspectorIndex !== null && toolHistory[inspectorIndex] ? (() => {
|
|
1622
|
+
const rec = toolHistory[inspectorIndex];
|
|
1623
|
+
const outLines = rec.output.split("\n").slice(0, 24).join("\n");
|
|
1624
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [
|
|
1625
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1626
|
+
" tool details (",
|
|
1627
|
+
inspectorIndex + 1,
|
|
1628
|
+
"/",
|
|
1629
|
+
toolHistory.length,
|
|
1630
|
+
") \xB7 press d to cycle "
|
|
1631
|
+
] }),
|
|
1632
|
+
/* @__PURE__ */ jsxs5(Text5, { color: toolColorName(rec.name), bold: true, children: [
|
|
1633
|
+
rec.name,
|
|
1634
|
+
" ",
|
|
1635
|
+
rec.target
|
|
1636
|
+
] }),
|
|
1637
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
1638
|
+
"args: ",
|
|
1639
|
+
rec.args.slice(0, 400) || "(none)"
|
|
1640
|
+
] }),
|
|
1641
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: outLines || "(no output)" })
|
|
1642
|
+
] });
|
|
1643
|
+
})() : null,
|
|
1644
|
+
dialog?.kind === "model" ? /* @__PURE__ */ jsx5(
|
|
1645
|
+
SelectDialog,
|
|
1646
|
+
{
|
|
1647
|
+
title: `Model \u2014 ${providerName}`,
|
|
1648
|
+
items: modelItems,
|
|
1649
|
+
loading: modelLoading,
|
|
1650
|
+
error: modelError,
|
|
1651
|
+
current: model ?? factoryResult.provider?.defaultModel,
|
|
1652
|
+
onSelect: (id) => {
|
|
1653
|
+
setDialog(null);
|
|
1654
|
+
setModelAndPersist(id);
|
|
1655
|
+
},
|
|
1656
|
+
onClose: () => setDialog(null)
|
|
1500
1657
|
}
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
/* @__PURE__ */ jsxs4(Text4, { color: "yellow", children: [
|
|
1504
|
-
SPINNER_FRAMES[frame],
|
|
1505
|
-
" "
|
|
1506
|
-
] }),
|
|
1507
|
-
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "agent working... (Ctrl+C to exit)" })
|
|
1508
|
-
] }) : /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
1509
|
-
/* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "\u25B8 " }),
|
|
1510
|
-
/* @__PURE__ */ jsx4(
|
|
1511
|
-
TextInput2,
|
|
1658
|
+
) : dialog?.kind === "provider" ? /* @__PURE__ */ jsx5(
|
|
1659
|
+
SelectDialog,
|
|
1512
1660
|
{
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1661
|
+
title: "Provider",
|
|
1662
|
+
items: PROVIDER_NAMES,
|
|
1663
|
+
descriptions: PROVIDER_DESCRIPTIONS,
|
|
1664
|
+
current: providerName,
|
|
1665
|
+
onSelect: (name) => {
|
|
1666
|
+
setDialog(null);
|
|
1667
|
+
switchProvider(name);
|
|
1668
|
+
},
|
|
1669
|
+
onClose: () => setDialog(null)
|
|
1517
1670
|
}
|
|
1518
|
-
)
|
|
1671
|
+
) : dialog?.kind === "key" ? /* @__PURE__ */ jsx5(
|
|
1672
|
+
KeyDialog,
|
|
1673
|
+
{
|
|
1674
|
+
providerName,
|
|
1675
|
+
onSubmit: (value) => {
|
|
1676
|
+
setDialog(null);
|
|
1677
|
+
saveKey(value);
|
|
1678
|
+
},
|
|
1679
|
+
onClose: () => setDialog(null)
|
|
1680
|
+
}
|
|
1681
|
+
) : approval ? /* @__PURE__ */ jsx5(
|
|
1682
|
+
ApprovalPrompt,
|
|
1683
|
+
{
|
|
1684
|
+
request: approval,
|
|
1685
|
+
onDecision: (decision) => {
|
|
1686
|
+
bridge.resolve(decision);
|
|
1687
|
+
if (decision === "always") bus.emit("info", "Approved for the rest of this session.");
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
) : running ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
1691
|
+
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
1692
|
+
/* @__PURE__ */ jsxs5(Text5, { color: "yellow", children: [
|
|
1693
|
+
SPINNER_FRAMES[frame],
|
|
1694
|
+
" "
|
|
1695
|
+
] }),
|
|
1696
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "agent working... (Ctrl+C to exit)" })
|
|
1697
|
+
] }),
|
|
1698
|
+
thinkingText ? /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
1699
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "\u{1F4AD} " }),
|
|
1700
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", dimColor: true, children: marquee }),
|
|
1701
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "\u258C" })
|
|
1702
|
+
] }) : null
|
|
1703
|
+
] }) : /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
1704
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", bold: true, children: "\u25B8 " }),
|
|
1705
|
+
/* @__PURE__ */ jsx5(
|
|
1706
|
+
TextInput2,
|
|
1707
|
+
{
|
|
1708
|
+
value: taskInput,
|
|
1709
|
+
onChange: setTaskInput,
|
|
1710
|
+
onSubmit: handleSubmit,
|
|
1711
|
+
placeholder: "Describe a task, or /help"
|
|
1712
|
+
}
|
|
1713
|
+
)
|
|
1714
|
+
] })
|
|
1519
1715
|
] })
|
|
1520
1716
|
] });
|
|
1521
1717
|
}
|
|
@@ -1526,6 +1722,7 @@ var init_App = __esm({
|
|
|
1526
1722
|
init_events();
|
|
1527
1723
|
init_logging();
|
|
1528
1724
|
init_render();
|
|
1725
|
+
init_markdown();
|
|
1529
1726
|
init_approval();
|
|
1530
1727
|
init_loop();
|
|
1531
1728
|
init_conversation();
|
|
@@ -1586,6 +1783,20 @@ function toWireTools(tools) {
|
|
|
1586
1783
|
}
|
|
1587
1784
|
}));
|
|
1588
1785
|
}
|
|
1786
|
+
function parseResponse(response) {
|
|
1787
|
+
const choice = response.choices?.[0];
|
|
1788
|
+
const message = choice?.message;
|
|
1789
|
+
const usage = response.usage ? {
|
|
1790
|
+
inputTokens: response.usage.prompt_tokens ?? 0,
|
|
1791
|
+
outputTokens: response.usage.completion_tokens ?? 0
|
|
1792
|
+
} : void 0;
|
|
1793
|
+
return {
|
|
1794
|
+
content: message?.content ?? null,
|
|
1795
|
+
toolCalls: message?.tool_calls ?? [],
|
|
1796
|
+
finishReason: mapFinishReason(choice?.finish_reason),
|
|
1797
|
+
usage
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1589
1800
|
var OpenAICompatProvider = class {
|
|
1590
1801
|
name;
|
|
1591
1802
|
defaultModel;
|
|
@@ -1603,26 +1814,71 @@ var OpenAICompatProvider = class {
|
|
|
1603
1814
|
{ role: "system", content: request.system },
|
|
1604
1815
|
...request.messages
|
|
1605
1816
|
];
|
|
1606
|
-
const
|
|
1817
|
+
const baseParams = {
|
|
1607
1818
|
model: model ?? this.defaultModel,
|
|
1608
1819
|
// the wire format matches our internal shape; keep loose typing at the boundary
|
|
1609
1820
|
messages,
|
|
1610
1821
|
...request.tools.length > 0 ? { tools: toWireTools(request.tools) } : {},
|
|
1611
1822
|
...request.temperature !== void 0 ? { temperature: request.temperature } : {},
|
|
1612
1823
|
...request.maxTokens ? { max_tokens: request.maxTokens } : {}
|
|
1613
|
-
});
|
|
1614
|
-
const choice = response.choices[0];
|
|
1615
|
-
const message = choice?.message;
|
|
1616
|
-
const usage = response.usage ? {
|
|
1617
|
-
inputTokens: response.usage.prompt_tokens ?? 0,
|
|
1618
|
-
outputTokens: response.usage.completion_tokens ?? 0
|
|
1619
|
-
} : void 0;
|
|
1620
|
-
return {
|
|
1621
|
-
content: message?.content ?? null,
|
|
1622
|
-
toolCalls: message?.tool_calls ?? [],
|
|
1623
|
-
finishReason: mapFinishReason(choice?.finish_reason),
|
|
1624
|
-
usage
|
|
1625
1824
|
};
|
|
1825
|
+
if (!request.onToken) {
|
|
1826
|
+
const response = await this.client.chat.completions.create(baseParams);
|
|
1827
|
+
return parseResponse(response);
|
|
1828
|
+
}
|
|
1829
|
+
try {
|
|
1830
|
+
const stream = await this.client.chat.completions.create({
|
|
1831
|
+
...baseParams,
|
|
1832
|
+
stream: true,
|
|
1833
|
+
stream_options: { include_usage: true }
|
|
1834
|
+
});
|
|
1835
|
+
let content = "";
|
|
1836
|
+
let finishReason = "stop";
|
|
1837
|
+
let usage;
|
|
1838
|
+
const tcMap = /* @__PURE__ */ new Map();
|
|
1839
|
+
for await (const chunk of stream) {
|
|
1840
|
+
const choice = chunk.choices?.[0];
|
|
1841
|
+
const delta = choice?.delta;
|
|
1842
|
+
if (delta?.content) {
|
|
1843
|
+
content += delta.content;
|
|
1844
|
+
request.onToken(delta.content);
|
|
1845
|
+
}
|
|
1846
|
+
if (delta?.tool_calls) {
|
|
1847
|
+
for (const tc of delta.tool_calls) {
|
|
1848
|
+
const idx = tc.index ?? 0;
|
|
1849
|
+
let acc = tcMap.get(idx);
|
|
1850
|
+
if (!acc) {
|
|
1851
|
+
acc = { args: "" };
|
|
1852
|
+
tcMap.set(idx, acc);
|
|
1853
|
+
}
|
|
1854
|
+
if (tc.id) acc.id = tc.id;
|
|
1855
|
+
if (tc.function?.name) acc.name = tc.function.name;
|
|
1856
|
+
if (tc.function?.arguments) acc.args += tc.function.arguments;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
1860
|
+
if (chunk.usage) {
|
|
1861
|
+
usage = {
|
|
1862
|
+
inputTokens: chunk.usage.prompt_tokens ?? 0,
|
|
1863
|
+
outputTokens: chunk.usage.completion_tokens ?? 0
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
const toolCalls = [...tcMap.entries()].sort((a, b) => a[0] - b[0]).map(([idx, acc]) => ({
|
|
1868
|
+
id: acc.id ?? `call_${idx}`,
|
|
1869
|
+
type: "function",
|
|
1870
|
+
function: { name: acc.name ?? "", arguments: acc.args }
|
|
1871
|
+
}));
|
|
1872
|
+
return {
|
|
1873
|
+
content,
|
|
1874
|
+
toolCalls,
|
|
1875
|
+
finishReason: mapFinishReason(finishReason),
|
|
1876
|
+
usage
|
|
1877
|
+
};
|
|
1878
|
+
} catch {
|
|
1879
|
+
const response = await this.client.chat.completions.create(baseParams);
|
|
1880
|
+
return parseResponse(response);
|
|
1881
|
+
}
|
|
1626
1882
|
}
|
|
1627
1883
|
async listModels() {
|
|
1628
1884
|
const page = await this.client.models.list();
|
|
@@ -3202,7 +3458,7 @@ Saved to ${configFilePath()}
|
|
|
3202
3458
|
|
|
3203
3459
|
// src/cli.tsx
|
|
3204
3460
|
import path9 from "path";
|
|
3205
|
-
import { jsx as
|
|
3461
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
3206
3462
|
var packageJson = { version: "0.1.0" };
|
|
3207
3463
|
var program = new Command();
|
|
3208
3464
|
program.name("mentee").description("MenteE SWE \u2014 an autonomous SWE agent in your terminal. Bring your own model: Kimi, GLM, and more.").version(packageJson.version);
|
|
@@ -3259,7 +3515,7 @@ program.argument("[task...]", "the software task to perform (omit to type it int
|
|
|
3259
3515
|
const { App: App2 } = await Promise.resolve().then(() => (init_App(), App_exports));
|
|
3260
3516
|
const { render } = await import("ink");
|
|
3261
3517
|
const { waitUntilExit } = render(
|
|
3262
|
-
/* @__PURE__ */
|
|
3518
|
+
/* @__PURE__ */ jsx6(
|
|
3263
3519
|
App2,
|
|
3264
3520
|
{
|
|
3265
3521
|
initialTask: task || void 0,
|
package/package.json
CHANGED