@menteeai/menteeswe 0.1.11 → 0.1.13
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 +315 -215
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -194,6 +194,154 @@ var init_exec = __esm({
|
|
|
194
194
|
}
|
|
195
195
|
});
|
|
196
196
|
|
|
197
|
+
// src/tools/testing.ts
|
|
198
|
+
import fs4 from "fs";
|
|
199
|
+
import path4 from "path";
|
|
200
|
+
function readPackageJson(cwd) {
|
|
201
|
+
try {
|
|
202
|
+
return JSON.parse(fs4.readFileSync(path4.join(cwd, "package.json"), "utf8"));
|
|
203
|
+
} catch {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function readTextIfExists(cwd, file) {
|
|
208
|
+
try {
|
|
209
|
+
return fs4.readFileSync(path4.join(cwd, file), "utf8");
|
|
210
|
+
} catch {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function fileExists(cwd, file) {
|
|
215
|
+
try {
|
|
216
|
+
return fs4.existsSync(path4.join(cwd, file));
|
|
217
|
+
} catch {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function detectTestCommand(cwd) {
|
|
222
|
+
const pkg = readPackageJson(cwd);
|
|
223
|
+
const testScript = pkg && typeof pkg.scripts?.test === "string" ? pkg.scripts.test : null;
|
|
224
|
+
if (testScript && !/error|no test specified/i.test(testScript)) {
|
|
225
|
+
return "npm test";
|
|
226
|
+
}
|
|
227
|
+
if (fileExists(cwd, "pytest.ini") || fileExists(cwd, "tests")) return "pytest -q";
|
|
228
|
+
const pyproject = readTextIfExists(cwd, "pyproject.toml");
|
|
229
|
+
if (pyproject && /\[tool\.pytest/.test(pyproject)) return "pytest -q";
|
|
230
|
+
if (fileExists(cwd, "go.mod")) return "go test ./...";
|
|
231
|
+
if (fileExists(cwd, "Cargo.toml")) return "cargo test";
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
function detectLintCommand(cwd) {
|
|
235
|
+
if (fileExists(cwd, "eslint.config.js") || fileExists(cwd, "eslint.config.mjs") || fileExists(cwd, ".eslintrc.json") || fileExists(cwd, ".eslintrc.js")) {
|
|
236
|
+
return "npx --no-install eslint .";
|
|
237
|
+
}
|
|
238
|
+
if (fileExists(cwd, "biome.json")) return "npx --no-install biome check .";
|
|
239
|
+
if (fileExists(cwd, ".ruff.toml") || fileExists(cwd, "ruff.toml")) return "ruff check .";
|
|
240
|
+
const pyproject = readTextIfExists(cwd, "pyproject.toml");
|
|
241
|
+
if (pyproject && /\[tool\.ruff/.test(pyproject)) return "ruff check .";
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
function detectTypecheckCommand(cwd) {
|
|
245
|
+
if (fileExists(cwd, "tsconfig.json")) return "npx --no-install tsc --noEmit";
|
|
246
|
+
if (fileExists(cwd, "mypy.ini")) return "python -m mypy .";
|
|
247
|
+
const pyproject = readTextIfExists(cwd, "pyproject.toml");
|
|
248
|
+
if (pyproject && /\[tool\.mypy/.test(pyproject)) return "python -m mypy .";
|
|
249
|
+
if (fileExists(cwd, "go.mod")) return "go vet ./...";
|
|
250
|
+
if (fileExists(cwd, "Cargo.toml")) return "cargo check";
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
function makeRunnerTool(config) {
|
|
254
|
+
return {
|
|
255
|
+
name: config.name,
|
|
256
|
+
description: config.description,
|
|
257
|
+
risk: "safe",
|
|
258
|
+
timeoutMs: DEFAULT_TIMEOUT_MS2,
|
|
259
|
+
parameters: {
|
|
260
|
+
type: "object",
|
|
261
|
+
properties: {
|
|
262
|
+
command: {
|
|
263
|
+
type: "string",
|
|
264
|
+
description: "Exact command to run. If omitted, the project type is auto-detected."
|
|
265
|
+
},
|
|
266
|
+
timeout_ms: { type: "number", description: `Timeout in ms. Defaults to ${DEFAULT_TIMEOUT_MS2}.` }
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
async execute(args, ctx) {
|
|
270
|
+
let command = firstString(args, "command");
|
|
271
|
+
if (!command) {
|
|
272
|
+
command = config.detect(ctx.cwd) ?? void 0;
|
|
273
|
+
if (!command) {
|
|
274
|
+
return {
|
|
275
|
+
success: false,
|
|
276
|
+
output: `Could not auto-detect a command for this project. ${config.fallbackHint} Use the command argument to run one explicitly.`
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const timeoutMs = Math.min(6e5, firstNumber(args, "timeout_ms") ?? DEFAULT_TIMEOUT_MS2);
|
|
281
|
+
const outcome = await runShellCommand(command, ctx.cwd, timeoutMs);
|
|
282
|
+
const formatted = formatOutcome(outcome);
|
|
283
|
+
return { success: formatted.success, output: formatted.output, truncated: formatted.truncated };
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
var DEFAULT_TIMEOUT_MS2, runTests, runLinter, runTypecheck, inspectEnv;
|
|
288
|
+
var init_testing = __esm({
|
|
289
|
+
"src/tools/testing.ts"() {
|
|
290
|
+
"use strict";
|
|
291
|
+
init_base();
|
|
292
|
+
init_exec();
|
|
293
|
+
DEFAULT_TIMEOUT_MS2 = 18e4;
|
|
294
|
+
runTests = makeRunnerTool({
|
|
295
|
+
name: "run_tests",
|
|
296
|
+
description: "Run the project test suite. Auto-detects npm test / pytest / go test / cargo test from the project files, or run an exact command. Use this to verify changes.",
|
|
297
|
+
detect: detectTestCommand,
|
|
298
|
+
fallbackHint: "No test runner found (looked for package.json scripts.test, pytest, go, cargo)."
|
|
299
|
+
});
|
|
300
|
+
runLinter = makeRunnerTool({
|
|
301
|
+
name: "run_linter",
|
|
302
|
+
description: "Run the project linter (eslint / biome / ruff auto-detected).",
|
|
303
|
+
detect: detectLintCommand,
|
|
304
|
+
fallbackHint: "No linter config found (looked for eslint, biome, ruff)."
|
|
305
|
+
});
|
|
306
|
+
runTypecheck = makeRunnerTool({
|
|
307
|
+
name: "run_typecheck",
|
|
308
|
+
description: "Run static type checking (tsc --noEmit / mypy / go vet / cargo check auto-detected).",
|
|
309
|
+
detect: detectTypecheckCommand,
|
|
310
|
+
fallbackHint: "No typechecker config found (looked for tsconfig.json, mypy, go, cargo)."
|
|
311
|
+
});
|
|
312
|
+
inspectEnv = {
|
|
313
|
+
name: "inspect_env",
|
|
314
|
+
description: "Inspect the environment: OS info, and which toolchains are installed with versions (node, npm, python, pip, git). Use this to adapt commands to the machine.",
|
|
315
|
+
risk: "safe",
|
|
316
|
+
parameters: { type: "object", properties: {} },
|
|
317
|
+
async execute(_args, ctx) {
|
|
318
|
+
const lines = [
|
|
319
|
+
`os: ${process.platform} ${process.version} ${process.arch}`,
|
|
320
|
+
`workspace: ${ctx.cwd}`
|
|
321
|
+
];
|
|
322
|
+
const probes = [
|
|
323
|
+
["node", "node --version"],
|
|
324
|
+
["npm", "npm --version"],
|
|
325
|
+
["npx", "npx --version"],
|
|
326
|
+
["python", "python --version"],
|
|
327
|
+
["pip", "pip --version"],
|
|
328
|
+
["git", "git --version"],
|
|
329
|
+
["docker", "docker --version"]
|
|
330
|
+
];
|
|
331
|
+
const results = await Promise.all(
|
|
332
|
+
probes.map(async ([label, probe]) => {
|
|
333
|
+
const outcome = await runShellCommand(probe, ctx.cwd, 8e3);
|
|
334
|
+
const version2 = `${outcome.stdout} ${outcome.stderr}`.trim().split("\n")[0] ?? "";
|
|
335
|
+
return `${label}: ${outcome.exitCode === 0 ? version2 || "installed (version unknown)" : "not available"}`;
|
|
336
|
+
})
|
|
337
|
+
);
|
|
338
|
+
lines.push(...results);
|
|
339
|
+
return { success: true, output: lines.join("\n") };
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
197
345
|
// src/events.ts
|
|
198
346
|
var EventBus;
|
|
199
347
|
var init_events = __esm({
|
|
@@ -635,6 +783,7 @@ function createAgentState(task, maxIterations) {
|
|
|
635
783
|
sameErrorCount: 0,
|
|
636
784
|
totalToolCalls: 0,
|
|
637
785
|
readCount: 0,
|
|
786
|
+
verifyAttempts: 0,
|
|
638
787
|
usage: { inputTokens: 0, outputTokens: 0, modelRequests: 0 },
|
|
639
788
|
startedAt: Date.now()
|
|
640
789
|
};
|
|
@@ -724,6 +873,31 @@ function trimOldToolResults(messages) {
|
|
|
724
873
|
message.content = `\u27E8prior tool output trimmed: ${head}\u2026 (${content.length} chars)\u27E9`;
|
|
725
874
|
}
|
|
726
875
|
}
|
|
876
|
+
async function runVerification(tools, ctx, bus, state, cwd) {
|
|
877
|
+
const checks = [
|
|
878
|
+
{ tool: tools.get("run_typecheck"), detect: detectTypecheckCommand },
|
|
879
|
+
{ tool: tools.get("run_tests"), detect: detectTestCommand }
|
|
880
|
+
];
|
|
881
|
+
let combined = "";
|
|
882
|
+
for (const { tool, detect } of checks) {
|
|
883
|
+
if (!tool) continue;
|
|
884
|
+
if (!detect(cwd)) continue;
|
|
885
|
+
bus.emit("tool_started", `verify \xB7 ${tool.name}`, { tool: tool.name });
|
|
886
|
+
const res = await tool.execute({}, ctx);
|
|
887
|
+
bus.emit("tool_completed", `verify \xB7 ${tool.name}`, {
|
|
888
|
+
tool: tool.name,
|
|
889
|
+
success: res.success,
|
|
890
|
+
output: res.output.slice(0, 2e3)
|
|
891
|
+
});
|
|
892
|
+
combined += `${tool.name}: ${res.output}
|
|
893
|
+
`;
|
|
894
|
+
if (!res.success) {
|
|
895
|
+
state.errors.push(res.output.slice(0, 500));
|
|
896
|
+
return { ok: false, output: combined };
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
return { ok: true, output: combined };
|
|
900
|
+
}
|
|
727
901
|
async function runAgent(options) {
|
|
728
902
|
const {
|
|
729
903
|
task,
|
|
@@ -756,7 +930,12 @@ ${systemExtra}` : "");
|
|
|
756
930
|
}
|
|
757
931
|
};
|
|
758
932
|
const messages = [{ role: "user", content: task }];
|
|
933
|
+
const setPhase = (p) => {
|
|
934
|
+
state.phase = p;
|
|
935
|
+
bus.emit("phase", p);
|
|
936
|
+
};
|
|
759
937
|
bus.emit("task_started", task, { provider: provider.name, model: model ?? provider.defaultModel });
|
|
938
|
+
setPhase("EXPLORATION");
|
|
760
939
|
let finalText = "";
|
|
761
940
|
let sawFinish = false;
|
|
762
941
|
while (state.iteration < maxIterations) {
|
|
@@ -828,6 +1007,27 @@ ${systemExtra}` : "");
|
|
|
828
1007
|
if (response.toolCalls.length === 0) {
|
|
829
1008
|
finalText = response.content ?? "";
|
|
830
1009
|
sawFinish = true;
|
|
1010
|
+
if (state.modifiedFiles.length > 0 && state.verifyAttempts < MAX_VERIFY) {
|
|
1011
|
+
setPhase("VALIDATION");
|
|
1012
|
+
const verdict = await runVerification(tools, toolCtx, bus, state, cwd);
|
|
1013
|
+
if (!verdict.ok) {
|
|
1014
|
+
state.verifyAttempts += 1;
|
|
1015
|
+
if (state.verifyAttempts >= MAX_VERIFY) {
|
|
1016
|
+
bus.emit("warning", "Verification failed after multiple attempts; finishing with the issue reported.");
|
|
1017
|
+
break;
|
|
1018
|
+
}
|
|
1019
|
+
setPhase("DEBUGGING");
|
|
1020
|
+
messages.push({
|
|
1021
|
+
role: "user",
|
|
1022
|
+
content: `SYSTEM NOTE: Verification failed.
|
|
1023
|
+
${verdict.output}
|
|
1024
|
+
Investigate the root cause, fix it, then finish with a final answer.`
|
|
1025
|
+
});
|
|
1026
|
+
sawFinish = false;
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
setPhase("REVIEW");
|
|
1030
|
+
}
|
|
831
1031
|
break;
|
|
832
1032
|
}
|
|
833
1033
|
messages.push({
|
|
@@ -859,6 +1059,7 @@ ${systemExtra}` : "");
|
|
|
859
1059
|
});
|
|
860
1060
|
continue;
|
|
861
1061
|
}
|
|
1062
|
+
if (EDIT_TOOLS.has(tool.name) && state.phase === "EXPLORATION") setPhase("IMPLEMENTATION");
|
|
862
1063
|
bus.emit("tool_started", toolCallPreview(call), { tool: tool.name, args });
|
|
863
1064
|
const risk = tool.dynamicRisk ? tool.dynamicRisk(args) : tool.risk;
|
|
864
1065
|
let approved = true;
|
|
@@ -969,7 +1170,7 @@ ${systemExtra}` : "");
|
|
|
969
1170
|
});
|
|
970
1171
|
return { success, finalText, state };
|
|
971
1172
|
}
|
|
972
|
-
var MAX_CONTEXT_CHARS, READ_BUDGET, RATE_LIMIT_MAX_ATTEMPTS, KEEP_RECENT_TOOL;
|
|
1173
|
+
var MAX_CONTEXT_CHARS, READ_BUDGET, RATE_LIMIT_MAX_ATTEMPTS, MAX_VERIFY, EDIT_TOOLS, KEEP_RECENT_TOOL;
|
|
973
1174
|
var init_loop = __esm({
|
|
974
1175
|
"src/agent/loop.ts"() {
|
|
975
1176
|
"use strict";
|
|
@@ -977,9 +1178,12 @@ var init_loop = __esm({
|
|
|
977
1178
|
init_conversation();
|
|
978
1179
|
init_state();
|
|
979
1180
|
init_base();
|
|
1181
|
+
init_testing();
|
|
980
1182
|
MAX_CONTEXT_CHARS = 7e4;
|
|
981
1183
|
READ_BUDGET = 6;
|
|
982
1184
|
RATE_LIMIT_MAX_ATTEMPTS = 8;
|
|
1185
|
+
MAX_VERIFY = 3;
|
|
1186
|
+
EDIT_TOOLS = /* @__PURE__ */ new Set(["apply_patch", "write_file", "move_path"]);
|
|
983
1187
|
KEEP_RECENT_TOOL = 4;
|
|
984
1188
|
}
|
|
985
1189
|
});
|
|
@@ -988,7 +1192,7 @@ var init_loop = __esm({
|
|
|
988
1192
|
var version;
|
|
989
1193
|
var init_package = __esm({
|
|
990
1194
|
"package.json"() {
|
|
991
|
-
version = "0.1.
|
|
1195
|
+
version = "0.1.13";
|
|
992
1196
|
}
|
|
993
1197
|
});
|
|
994
1198
|
|
|
@@ -1112,59 +1316,45 @@ function compactChanges(oldStr, newStr) {
|
|
|
1112
1316
|
added: newLines.slice(start, endNew)
|
|
1113
1317
|
};
|
|
1114
1318
|
}
|
|
1115
|
-
function
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
const
|
|
1319
|
+
function CurrentEditBox({ edit }) {
|
|
1320
|
+
if (!edit) return null;
|
|
1321
|
+
const c = compactChanges(edit.old, edit.new);
|
|
1322
|
+
const removed = c.removed.slice(0, MAX_LINES);
|
|
1323
|
+
const added = c.added.slice(0, MAX_LINES);
|
|
1324
|
+
const lines = [];
|
|
1119
1325
|
let key = 0;
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
let removedTotal = 0;
|
|
1124
|
-
let addedTotal = 0;
|
|
1125
|
-
const lines = [];
|
|
1126
|
-
for (const edit of edits) {
|
|
1127
|
-
const c = compactChanges(edit.old, edit.new);
|
|
1128
|
-
removedTotal += c.removed.length;
|
|
1129
|
-
addedTotal += c.added.length;
|
|
1130
|
-
if (lineBudget > 0) {
|
|
1131
|
-
for (const l of c.removed) {
|
|
1132
|
-
if (lineBudget <= 0) break;
|
|
1133
|
-
lines.push(
|
|
1134
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "redBright", color: "white", children: ` - ${l}` }, `r${key++}`)
|
|
1135
|
-
);
|
|
1136
|
-
lineBudget--;
|
|
1137
|
-
}
|
|
1138
|
-
for (const l of c.added) {
|
|
1139
|
-
if (lineBudget <= 0) break;
|
|
1140
|
-
lines.push(
|
|
1141
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "greenBright", color: "black", children: ` + ${l}` }, `a${key++}`)
|
|
1142
|
-
);
|
|
1143
|
-
lineBudget--;
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
blocks.push(
|
|
1148
|
-
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, children: [
|
|
1149
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "magenta", children: `\u270E ${file}` }),
|
|
1150
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: ` ${edits.length} edit(s) \xB7 +${addedTotal} / -${removedTotal}` }),
|
|
1151
|
-
lines
|
|
1152
|
-
] }, `f${fi}`)
|
|
1326
|
+
for (const l of removed) {
|
|
1327
|
+
lines.push(
|
|
1328
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "redBright", color: "white", children: ` - ${l}` }, `r${key++}`)
|
|
1153
1329
|
);
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
/* @__PURE__ */ jsx2(Text2, {
|
|
1330
|
+
}
|
|
1331
|
+
for (const l of added) {
|
|
1332
|
+
lines.push(
|
|
1333
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "greenBright", color: "black", children: ` + ${l}` }, `a${key++}`)
|
|
1158
1334
|
);
|
|
1159
1335
|
}
|
|
1160
|
-
return /* @__PURE__ */
|
|
1336
|
+
return /* @__PURE__ */ jsxs2(
|
|
1337
|
+
Box2,
|
|
1338
|
+
{
|
|
1339
|
+
flexDirection: "column",
|
|
1340
|
+
marginTop: 1,
|
|
1341
|
+
borderStyle: "single",
|
|
1342
|
+
borderColor: "gray",
|
|
1343
|
+
borderLeft: false,
|
|
1344
|
+
borderRight: false,
|
|
1345
|
+
paddingX: 1,
|
|
1346
|
+
children: [
|
|
1347
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "magenta", children: `\u270E ${edit.path}` }),
|
|
1348
|
+
lines.length > 0 ? lines : /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " (no text change)" })
|
|
1349
|
+
]
|
|
1350
|
+
}
|
|
1351
|
+
);
|
|
1161
1352
|
}
|
|
1162
|
-
var
|
|
1353
|
+
var MAX_LINES;
|
|
1163
1354
|
var init_diff = __esm({
|
|
1164
1355
|
"src/tui/diff.tsx"() {
|
|
1165
1356
|
"use strict";
|
|
1166
|
-
|
|
1167
|
-
MAX_LINES = 160;
|
|
1357
|
+
MAX_LINES = 24;
|
|
1168
1358
|
}
|
|
1169
1359
|
});
|
|
1170
1360
|
|
|
@@ -1367,7 +1557,8 @@ function App(props) {
|
|
|
1367
1557
|
const [detailsOpen, setDetailsOpen] = useState3(false);
|
|
1368
1558
|
const [showDetails, setShowDetails] = useState3(false);
|
|
1369
1559
|
const [thinkingText, setThinkingText] = useState3("");
|
|
1370
|
-
const [
|
|
1560
|
+
const [currentEdit, setCurrentEdit] = useState3(null);
|
|
1561
|
+
const [phase, setPhase] = useState3("");
|
|
1371
1562
|
const finalTextRef = useRef(null);
|
|
1372
1563
|
const pendingTool = useRef(null);
|
|
1373
1564
|
const counter = useRef(0);
|
|
@@ -1388,6 +1579,10 @@ function App(props) {
|
|
|
1388
1579
|
if (delta) setThinkingText((prev) => (prev + delta).slice(-4e3));
|
|
1389
1580
|
return;
|
|
1390
1581
|
}
|
|
1582
|
+
if (event.type === "phase") {
|
|
1583
|
+
setPhase(event.message ?? "");
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1391
1586
|
if (event.type === "model_request" || event.type === "task_completed") {
|
|
1392
1587
|
setThinkingText("");
|
|
1393
1588
|
}
|
|
@@ -1395,10 +1590,7 @@ function App(props) {
|
|
|
1395
1590
|
const filePath = typeof event.data?.path === "string" ? event.data.path : "file";
|
|
1396
1591
|
const oldStr = typeof event.data?.old_string === "string" ? event.data.old_string : "";
|
|
1397
1592
|
const newStr = typeof event.data?.new_string === "string" ? event.data.new_string : "";
|
|
1398
|
-
|
|
1399
|
-
const arr = prev[filePath] ? [...prev[filePath], { old: oldStr, new: newStr }] : [{ old: oldStr, new: newStr }];
|
|
1400
|
-
return { ...prev, [filePath]: arr };
|
|
1401
|
-
});
|
|
1593
|
+
setCurrentEdit({ path: filePath, old: oldStr, new: newStr });
|
|
1402
1594
|
return;
|
|
1403
1595
|
}
|
|
1404
1596
|
appendLog(formatEvent(event), TOOL_NOISE.has(event.type));
|
|
@@ -1608,9 +1800,8 @@ function App(props) {
|
|
|
1608
1800
|
setToolHistory([]);
|
|
1609
1801
|
setInspectorIndex(null);
|
|
1610
1802
|
setDetailsOpen(false);
|
|
1611
|
-
setShowDetails(false);
|
|
1612
1803
|
setRunning(true);
|
|
1613
|
-
|
|
1804
|
+
setCurrentEdit(null);
|
|
1614
1805
|
const ac = new AbortController();
|
|
1615
1806
|
abortRef.current = ac;
|
|
1616
1807
|
try {
|
|
@@ -1709,7 +1900,7 @@ function App(props) {
|
|
|
1709
1900
|
typedText,
|
|
1710
1901
|
typing ? "\u258C" : ""
|
|
1711
1902
|
] }) }) : null,
|
|
1712
|
-
/* @__PURE__ */ jsx6(
|
|
1903
|
+
/* @__PURE__ */ jsx6(CurrentEditBox, { edit: currentEdit }),
|
|
1713
1904
|
detailsOpen && inspectorIndex !== null && toolHistory[inspectorIndex] ? (() => {
|
|
1714
1905
|
const rec = toolHistory[inspectorIndex];
|
|
1715
1906
|
const outLines = rec.output.split("\n").slice(0, 24).join("\n");
|
|
@@ -1785,7 +1976,11 @@ function App(props) {
|
|
|
1785
1976
|
SPINNER_FRAMES[frame],
|
|
1786
1977
|
" "
|
|
1787
1978
|
] }),
|
|
1788
|
-
/* @__PURE__ */
|
|
1979
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1980
|
+
"agent ",
|
|
1981
|
+
PHASE_LABEL[phase] ?? "working",
|
|
1982
|
+
"\u2026 (Ctrl+C to exit)"
|
|
1983
|
+
] })
|
|
1789
1984
|
] }),
|
|
1790
1985
|
thinkingText ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1791
1986
|
/* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u{1F4AD} " }),
|
|
@@ -1807,7 +2002,7 @@ function App(props) {
|
|
|
1807
2002
|
] })
|
|
1808
2003
|
] });
|
|
1809
2004
|
}
|
|
1810
|
-
var SPINNER_FRAMES, PROVIDER_NAMES, PROVIDER_DESCRIPTIONS, BANNER;
|
|
2005
|
+
var SPINNER_FRAMES, PHASE_LABEL, PROVIDER_NAMES, PROVIDER_DESCRIPTIONS, BANNER;
|
|
1811
2006
|
var init_App = __esm({
|
|
1812
2007
|
"src/tui/App.tsx"() {
|
|
1813
2008
|
"use strict";
|
|
@@ -1826,6 +2021,16 @@ var init_App = __esm({
|
|
|
1826
2021
|
init_KeyDialog();
|
|
1827
2022
|
init_package();
|
|
1828
2023
|
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
2024
|
+
PHASE_LABEL = {
|
|
2025
|
+
INITIALIZATION: "starting",
|
|
2026
|
+
EXPLORATION: "exploring",
|
|
2027
|
+
PLANNING: "planning",
|
|
2028
|
+
IMPLEMENTATION: "implementing",
|
|
2029
|
+
VALIDATION: "verifying",
|
|
2030
|
+
DEBUGGING: "debugging",
|
|
2031
|
+
REVIEW: "reviewing",
|
|
2032
|
+
COMPLETION: "finalizing"
|
|
2033
|
+
};
|
|
1829
2034
|
PROVIDER_NAMES = ["kimi", "glm", "zai", "zai-coding", "openrouter", "nvidia", "mock"];
|
|
1830
2035
|
PROVIDER_DESCRIPTIONS = {
|
|
1831
2036
|
kimi: "Moonshot \xB7 api.moonshot.ai",
|
|
@@ -1918,14 +2123,14 @@ var OpenAICompatProvider = class {
|
|
|
1918
2123
|
...request.maxTokens ? { max_tokens: request.maxTokens } : {}
|
|
1919
2124
|
};
|
|
1920
2125
|
if (!request.onToken) {
|
|
1921
|
-
const
|
|
1922
|
-
return parseResponse(
|
|
2126
|
+
const response2 = await this.client.chat.completions.create(baseParams);
|
|
2127
|
+
return parseResponse(response2);
|
|
1923
2128
|
}
|
|
1924
|
-
|
|
2129
|
+
const streamOnce = async (withUsage) => {
|
|
1925
2130
|
const stream = await this.client.chat.completions.create({
|
|
1926
2131
|
...baseParams,
|
|
1927
2132
|
stream: true,
|
|
1928
|
-
stream_options: { include_usage: true }
|
|
2133
|
+
...withUsage ? { stream_options: { include_usage: true } } : {}
|
|
1929
2134
|
});
|
|
1930
2135
|
let content = "";
|
|
1931
2136
|
let finishReason = "stop";
|
|
@@ -1936,7 +2141,7 @@ var OpenAICompatProvider = class {
|
|
|
1936
2141
|
const delta = choice?.delta;
|
|
1937
2142
|
if (delta?.content) {
|
|
1938
2143
|
content += delta.content;
|
|
1939
|
-
request.onToken(delta.content);
|
|
2144
|
+
request.onToken?.(delta.content);
|
|
1940
2145
|
}
|
|
1941
2146
|
if (delta?.tool_calls) {
|
|
1942
2147
|
for (const tc of delta.tool_calls) {
|
|
@@ -1970,10 +2175,21 @@ var OpenAICompatProvider = class {
|
|
|
1970
2175
|
finishReason: mapFinishReason(finishReason),
|
|
1971
2176
|
usage
|
|
1972
2177
|
};
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
|
|
2178
|
+
};
|
|
2179
|
+
for (const withUsage of [true, false]) {
|
|
2180
|
+
try {
|
|
2181
|
+
return await streamOnce(withUsage);
|
|
2182
|
+
} catch {
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
const response = await this.client.chat.completions.create(baseParams);
|
|
2186
|
+
const parsed = parseResponse(response);
|
|
2187
|
+
if (parsed.content) {
|
|
2188
|
+
for (let i = 0; i < parsed.content.length; i += 3) {
|
|
2189
|
+
request.onToken?.(parsed.content.slice(i, i + 3));
|
|
2190
|
+
}
|
|
1976
2191
|
}
|
|
2192
|
+
return parsed;
|
|
1977
2193
|
}
|
|
1978
2194
|
async listModels() {
|
|
1979
2195
|
const page = await this.client.models.list();
|
|
@@ -2331,20 +2547,43 @@ var applyPatch = {
|
|
|
2331
2547
|
if (oldString === newString) {
|
|
2332
2548
|
return { success: false, output: "old_string and new_string are identical; nothing to change." };
|
|
2333
2549
|
}
|
|
2334
|
-
const
|
|
2335
|
-
|
|
2336
|
-
return
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2550
|
+
const tryMatch = (c, o, n) => {
|
|
2551
|
+
const occ = c.split(o).length - 1;
|
|
2552
|
+
return occ === 1 ? c.replace(o, n) : null;
|
|
2553
|
+
};
|
|
2554
|
+
let updated = tryMatch(content, oldString, newString);
|
|
2555
|
+
let note = "";
|
|
2556
|
+
if (updated === null) {
|
|
2557
|
+
const lfContent = content.replace(/\r\n/g, "\n");
|
|
2558
|
+
const lfOld = oldString.replace(/\r\n/g, "\n");
|
|
2559
|
+
const lfNew = newString.replace(/\r\n/g, "\n");
|
|
2560
|
+
const lfUpdated = tryMatch(lfContent, lfOld, lfNew);
|
|
2561
|
+
if (lfUpdated !== null) {
|
|
2562
|
+
updated = lfUpdated;
|
|
2563
|
+
note = " (applied after normalizing CRLF \u2192 LF line endings)";
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
if (updated === null) {
|
|
2567
|
+
const strip = (s) => s.split("\n").map((l) => l.replace(/\s+$/, "")).join("\n");
|
|
2568
|
+
const sUpdated = tryMatch(strip(content), strip(oldString), strip(newString));
|
|
2569
|
+
if (sUpdated !== null) {
|
|
2570
|
+
updated = sUpdated;
|
|
2571
|
+
note = " (applied after trimming trailing whitespace to match)";
|
|
2572
|
+
}
|
|
2340
2573
|
}
|
|
2341
|
-
if (
|
|
2574
|
+
if (updated === null) {
|
|
2575
|
+
const occ = content.split(oldString).length - 1;
|
|
2576
|
+
if (occ === 0) {
|
|
2577
|
+
return {
|
|
2578
|
+
success: false,
|
|
2579
|
+
output: "old_string not found in file. Read the file again and copy the exact text, including whitespace."
|
|
2580
|
+
};
|
|
2581
|
+
}
|
|
2342
2582
|
return {
|
|
2343
2583
|
success: false,
|
|
2344
|
-
output: `old_string occurs ${
|
|
2584
|
+
output: `old_string occurs ${occ} times. Include more surrounding lines to make it unique.`
|
|
2345
2585
|
};
|
|
2346
2586
|
}
|
|
2347
|
-
const updated = content.replace(oldString, newString);
|
|
2348
2587
|
fs2.writeFileSync(filePath, updated, "utf8");
|
|
2349
2588
|
const removed = oldString.split(/\r?\n/).length;
|
|
2350
2589
|
const added = newString.split(/\r?\n/).length;
|
|
@@ -2908,147 +3147,8 @@ var executeCommand = {
|
|
|
2908
3147
|
}
|
|
2909
3148
|
};
|
|
2910
3149
|
|
|
2911
|
-
// src/tools/
|
|
2912
|
-
|
|
2913
|
-
init_exec();
|
|
2914
|
-
import fs4 from "fs";
|
|
2915
|
-
import path4 from "path";
|
|
2916
|
-
var DEFAULT_TIMEOUT_MS2 = 18e4;
|
|
2917
|
-
function readPackageJson(cwd) {
|
|
2918
|
-
try {
|
|
2919
|
-
return JSON.parse(fs4.readFileSync(path4.join(cwd, "package.json"), "utf8"));
|
|
2920
|
-
} catch {
|
|
2921
|
-
return null;
|
|
2922
|
-
}
|
|
2923
|
-
}
|
|
2924
|
-
function readTextIfExists(cwd, file) {
|
|
2925
|
-
try {
|
|
2926
|
-
return fs4.readFileSync(path4.join(cwd, file), "utf8");
|
|
2927
|
-
} catch {
|
|
2928
|
-
return null;
|
|
2929
|
-
}
|
|
2930
|
-
}
|
|
2931
|
-
function fileExists(cwd, file) {
|
|
2932
|
-
try {
|
|
2933
|
-
return fs4.existsSync(path4.join(cwd, file));
|
|
2934
|
-
} catch {
|
|
2935
|
-
return false;
|
|
2936
|
-
}
|
|
2937
|
-
}
|
|
2938
|
-
function detectTestCommand(cwd) {
|
|
2939
|
-
const pkg = readPackageJson(cwd);
|
|
2940
|
-
const testScript = pkg && typeof pkg.scripts?.test === "string" ? pkg.scripts.test : null;
|
|
2941
|
-
if (testScript && !/error|no test specified/i.test(testScript)) {
|
|
2942
|
-
return "npm test";
|
|
2943
|
-
}
|
|
2944
|
-
if (fileExists(cwd, "pytest.ini") || fileExists(cwd, "tests")) return "pytest -q";
|
|
2945
|
-
const pyproject = readTextIfExists(cwd, "pyproject.toml");
|
|
2946
|
-
if (pyproject && /\[tool\.pytest/.test(pyproject)) return "pytest -q";
|
|
2947
|
-
if (fileExists(cwd, "go.mod")) return "go test ./...";
|
|
2948
|
-
if (fileExists(cwd, "Cargo.toml")) return "cargo test";
|
|
2949
|
-
return null;
|
|
2950
|
-
}
|
|
2951
|
-
function detectLintCommand(cwd) {
|
|
2952
|
-
if (fileExists(cwd, "eslint.config.js") || fileExists(cwd, "eslint.config.mjs") || fileExists(cwd, ".eslintrc.json") || fileExists(cwd, ".eslintrc.js")) {
|
|
2953
|
-
return "npx --no-install eslint .";
|
|
2954
|
-
}
|
|
2955
|
-
if (fileExists(cwd, "biome.json")) return "npx --no-install biome check .";
|
|
2956
|
-
if (fileExists(cwd, ".ruff.toml") || fileExists(cwd, "ruff.toml")) return "ruff check .";
|
|
2957
|
-
const pyproject = readTextIfExists(cwd, "pyproject.toml");
|
|
2958
|
-
if (pyproject && /\[tool\.ruff/.test(pyproject)) return "ruff check .";
|
|
2959
|
-
return null;
|
|
2960
|
-
}
|
|
2961
|
-
function detectTypecheckCommand(cwd) {
|
|
2962
|
-
if (fileExists(cwd, "tsconfig.json")) return "npx --no-install tsc --noEmit";
|
|
2963
|
-
if (fileExists(cwd, "mypy.ini")) return "python -m mypy .";
|
|
2964
|
-
const pyproject = readTextIfExists(cwd, "pyproject.toml");
|
|
2965
|
-
if (pyproject && /\[tool\.mypy/.test(pyproject)) return "python -m mypy .";
|
|
2966
|
-
if (fileExists(cwd, "go.mod")) return "go vet ./...";
|
|
2967
|
-
if (fileExists(cwd, "Cargo.toml")) return "cargo check";
|
|
2968
|
-
return null;
|
|
2969
|
-
}
|
|
2970
|
-
function makeRunnerTool(config) {
|
|
2971
|
-
return {
|
|
2972
|
-
name: config.name,
|
|
2973
|
-
description: config.description,
|
|
2974
|
-
risk: "safe",
|
|
2975
|
-
timeoutMs: DEFAULT_TIMEOUT_MS2,
|
|
2976
|
-
parameters: {
|
|
2977
|
-
type: "object",
|
|
2978
|
-
properties: {
|
|
2979
|
-
command: {
|
|
2980
|
-
type: "string",
|
|
2981
|
-
description: "Exact command to run. If omitted, the project type is auto-detected."
|
|
2982
|
-
},
|
|
2983
|
-
timeout_ms: { type: "number", description: `Timeout in ms. Defaults to ${DEFAULT_TIMEOUT_MS2}.` }
|
|
2984
|
-
}
|
|
2985
|
-
},
|
|
2986
|
-
async execute(args, ctx) {
|
|
2987
|
-
let command = firstString(args, "command");
|
|
2988
|
-
if (!command) {
|
|
2989
|
-
command = config.detect(ctx.cwd) ?? void 0;
|
|
2990
|
-
if (!command) {
|
|
2991
|
-
return {
|
|
2992
|
-
success: false,
|
|
2993
|
-
output: `Could not auto-detect a command for this project. ${config.fallbackHint} Use the command argument to run one explicitly.`
|
|
2994
|
-
};
|
|
2995
|
-
}
|
|
2996
|
-
}
|
|
2997
|
-
const timeoutMs = Math.min(6e5, firstNumber(args, "timeout_ms") ?? DEFAULT_TIMEOUT_MS2);
|
|
2998
|
-
const outcome = await runShellCommand(command, ctx.cwd, timeoutMs);
|
|
2999
|
-
const formatted = formatOutcome(outcome);
|
|
3000
|
-
return { success: formatted.success, output: formatted.output, truncated: formatted.truncated };
|
|
3001
|
-
}
|
|
3002
|
-
};
|
|
3003
|
-
}
|
|
3004
|
-
var runTests = makeRunnerTool({
|
|
3005
|
-
name: "run_tests",
|
|
3006
|
-
description: "Run the project test suite. Auto-detects npm test / pytest / go test / cargo test from the project files, or run an exact command. Use this to verify changes.",
|
|
3007
|
-
detect: detectTestCommand,
|
|
3008
|
-
fallbackHint: "No test runner found (looked for package.json scripts.test, pytest, go, cargo)."
|
|
3009
|
-
});
|
|
3010
|
-
var runLinter = makeRunnerTool({
|
|
3011
|
-
name: "run_linter",
|
|
3012
|
-
description: "Run the project linter (eslint / biome / ruff auto-detected).",
|
|
3013
|
-
detect: detectLintCommand,
|
|
3014
|
-
fallbackHint: "No linter config found (looked for eslint, biome, ruff)."
|
|
3015
|
-
});
|
|
3016
|
-
var runTypecheck = makeRunnerTool({
|
|
3017
|
-
name: "run_typecheck",
|
|
3018
|
-
description: "Run static type checking (tsc --noEmit / mypy / go vet / cargo check auto-detected).",
|
|
3019
|
-
detect: detectTypecheckCommand,
|
|
3020
|
-
fallbackHint: "No typechecker config found (looked for tsconfig.json, mypy, go, cargo)."
|
|
3021
|
-
});
|
|
3022
|
-
var inspectEnv = {
|
|
3023
|
-
name: "inspect_env",
|
|
3024
|
-
description: "Inspect the environment: OS info, and which toolchains are installed with versions (node, npm, python, pip, git). Use this to adapt commands to the machine.",
|
|
3025
|
-
risk: "safe",
|
|
3026
|
-
parameters: { type: "object", properties: {} },
|
|
3027
|
-
async execute(_args, ctx) {
|
|
3028
|
-
const lines = [
|
|
3029
|
-
`os: ${process.platform} ${process.version} ${process.arch}`,
|
|
3030
|
-
`workspace: ${ctx.cwd}`
|
|
3031
|
-
];
|
|
3032
|
-
const probes = [
|
|
3033
|
-
["node", "node --version"],
|
|
3034
|
-
["npm", "npm --version"],
|
|
3035
|
-
["npx", "npx --version"],
|
|
3036
|
-
["python", "python --version"],
|
|
3037
|
-
["pip", "pip --version"],
|
|
3038
|
-
["git", "git --version"],
|
|
3039
|
-
["docker", "docker --version"]
|
|
3040
|
-
];
|
|
3041
|
-
const results = await Promise.all(
|
|
3042
|
-
probes.map(async ([label, probe]) => {
|
|
3043
|
-
const outcome = await runShellCommand(probe, ctx.cwd, 8e3);
|
|
3044
|
-
const version2 = `${outcome.stdout} ${outcome.stderr}`.trim().split("\n")[0] ?? "";
|
|
3045
|
-
return `${label}: ${outcome.exitCode === 0 ? version2 || "installed (version unknown)" : "not available"}`;
|
|
3046
|
-
})
|
|
3047
|
-
);
|
|
3048
|
-
lines.push(...results);
|
|
3049
|
-
return { success: true, output: lines.join("\n") };
|
|
3050
|
-
}
|
|
3051
|
-
};
|
|
3150
|
+
// src/tools/registry.ts
|
|
3151
|
+
init_testing();
|
|
3052
3152
|
|
|
3053
3153
|
// src/tools/memory.ts
|
|
3054
3154
|
init_config();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@menteeai/menteeswe",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "MenteE SWE — a model-agnostic autonomous software-engineering agent CLI. Bring your own intelligence: Kimi, GLM/Z.ai, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|