@menteeai/menteeswe 0.1.12 → 0.1.14
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 +270 -156
- 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.14";
|
|
992
1196
|
}
|
|
993
1197
|
});
|
|
994
1198
|
|
|
@@ -1354,6 +1558,8 @@ function App(props) {
|
|
|
1354
1558
|
const [showDetails, setShowDetails] = useState3(false);
|
|
1355
1559
|
const [thinkingText, setThinkingText] = useState3("");
|
|
1356
1560
|
const [currentEdit, setCurrentEdit] = useState3(null);
|
|
1561
|
+
const [phase, setPhase] = useState3("");
|
|
1562
|
+
const [showEdits, setShowEdits] = useState3(true);
|
|
1357
1563
|
const finalTextRef = useRef(null);
|
|
1358
1564
|
const pendingTool = useRef(null);
|
|
1359
1565
|
const counter = useRef(0);
|
|
@@ -1374,6 +1580,10 @@ function App(props) {
|
|
|
1374
1580
|
if (delta) setThinkingText((prev) => (prev + delta).slice(-4e3));
|
|
1375
1581
|
return;
|
|
1376
1582
|
}
|
|
1583
|
+
if (event.type === "phase") {
|
|
1584
|
+
setPhase(event.message ?? "");
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1377
1587
|
if (event.type === "model_request" || event.type === "task_completed") {
|
|
1378
1588
|
setThinkingText("");
|
|
1379
1589
|
}
|
|
@@ -1658,6 +1868,10 @@ function App(props) {
|
|
|
1658
1868
|
setShowDetails((v) => !v);
|
|
1659
1869
|
return;
|
|
1660
1870
|
}
|
|
1871
|
+
if (key.meta && (input === "e" || input === "E")) {
|
|
1872
|
+
setShowEdits((v) => !v);
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1661
1875
|
if (stateRef.current.dialog || stateRef.current.approval || stateRef.current.running) return;
|
|
1662
1876
|
if (key.meta && (input === "m" || input === "M")) openModelDialog();
|
|
1663
1877
|
else if (key.ctrl && input === "p") setDialog({ kind: "provider" });
|
|
@@ -1674,10 +1888,12 @@ function App(props) {
|
|
|
1674
1888
|
modelLabel ? `:${modelLabel}` : "",
|
|
1675
1889
|
" \xB7 details ",
|
|
1676
1890
|
showDetails ? "open" : "collapsed",
|
|
1891
|
+
" \xB7 edits ",
|
|
1892
|
+
showEdits ? "open" : "collapsed",
|
|
1677
1893
|
" \xB7 ",
|
|
1678
1894
|
props.cwd
|
|
1679
1895
|
] }),
|
|
1680
|
-
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Alt+M model \xB7 Ctrl+P provider \xB7 Ctrl+K key \xB7 Alt+D details \xB7 /help" })
|
|
1896
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Alt+M model \xB7 Ctrl+P provider \xB7 Ctrl+K key \xB7 Alt+D details \xB7 Alt+E edits \xB7 /help" })
|
|
1681
1897
|
] }, "header");
|
|
1682
1898
|
const cols = stdout.columns || 80;
|
|
1683
1899
|
const marquee = thinkingText.slice(-Math.max(1, cols - 4));
|
|
@@ -1691,7 +1907,7 @@ function App(props) {
|
|
|
1691
1907
|
typedText,
|
|
1692
1908
|
typing ? "\u258C" : ""
|
|
1693
1909
|
] }) }) : null,
|
|
1694
|
-
/* @__PURE__ */ jsx6(CurrentEditBox, { edit: currentEdit }),
|
|
1910
|
+
showEdits && /* @__PURE__ */ jsx6(CurrentEditBox, { edit: currentEdit }),
|
|
1695
1911
|
detailsOpen && inspectorIndex !== null && toolHistory[inspectorIndex] ? (() => {
|
|
1696
1912
|
const rec = toolHistory[inspectorIndex];
|
|
1697
1913
|
const outLines = rec.output.split("\n").slice(0, 24).join("\n");
|
|
@@ -1767,7 +1983,11 @@ function App(props) {
|
|
|
1767
1983
|
SPINNER_FRAMES[frame],
|
|
1768
1984
|
" "
|
|
1769
1985
|
] }),
|
|
1770
|
-
/* @__PURE__ */
|
|
1986
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
1987
|
+
"agent ",
|
|
1988
|
+
PHASE_LABEL[phase] ?? "working",
|
|
1989
|
+
"\u2026 (Ctrl+C to exit)"
|
|
1990
|
+
] })
|
|
1771
1991
|
] }),
|
|
1772
1992
|
thinkingText ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1773
1993
|
/* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u{1F4AD} " }),
|
|
@@ -1789,7 +2009,7 @@ function App(props) {
|
|
|
1789
2009
|
] })
|
|
1790
2010
|
] });
|
|
1791
2011
|
}
|
|
1792
|
-
var SPINNER_FRAMES, PROVIDER_NAMES, PROVIDER_DESCRIPTIONS, BANNER;
|
|
2012
|
+
var SPINNER_FRAMES, PHASE_LABEL, PROVIDER_NAMES, PROVIDER_DESCRIPTIONS, BANNER;
|
|
1793
2013
|
var init_App = __esm({
|
|
1794
2014
|
"src/tui/App.tsx"() {
|
|
1795
2015
|
"use strict";
|
|
@@ -1808,6 +2028,16 @@ var init_App = __esm({
|
|
|
1808
2028
|
init_KeyDialog();
|
|
1809
2029
|
init_package();
|
|
1810
2030
|
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
2031
|
+
PHASE_LABEL = {
|
|
2032
|
+
INITIALIZATION: "starting",
|
|
2033
|
+
EXPLORATION: "exploring",
|
|
2034
|
+
PLANNING: "planning",
|
|
2035
|
+
IMPLEMENTATION: "implementing",
|
|
2036
|
+
VALIDATION: "verifying",
|
|
2037
|
+
DEBUGGING: "debugging",
|
|
2038
|
+
REVIEW: "reviewing",
|
|
2039
|
+
COMPLETION: "finalizing"
|
|
2040
|
+
};
|
|
1811
2041
|
PROVIDER_NAMES = ["kimi", "glm", "zai", "zai-coding", "openrouter", "nvidia", "mock"];
|
|
1812
2042
|
PROVIDER_DESCRIPTIONS = {
|
|
1813
2043
|
kimi: "Moonshot \xB7 api.moonshot.ai",
|
|
@@ -2324,20 +2554,43 @@ var applyPatch = {
|
|
|
2324
2554
|
if (oldString === newString) {
|
|
2325
2555
|
return { success: false, output: "old_string and new_string are identical; nothing to change." };
|
|
2326
2556
|
}
|
|
2327
|
-
const
|
|
2328
|
-
|
|
2329
|
-
return
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2557
|
+
const tryMatch = (c, o, n) => {
|
|
2558
|
+
const occ = c.split(o).length - 1;
|
|
2559
|
+
return occ === 1 ? c.replace(o, n) : null;
|
|
2560
|
+
};
|
|
2561
|
+
let updated = tryMatch(content, oldString, newString);
|
|
2562
|
+
let note = "";
|
|
2563
|
+
if (updated === null) {
|
|
2564
|
+
const lfContent = content.replace(/\r\n/g, "\n");
|
|
2565
|
+
const lfOld = oldString.replace(/\r\n/g, "\n");
|
|
2566
|
+
const lfNew = newString.replace(/\r\n/g, "\n");
|
|
2567
|
+
const lfUpdated = tryMatch(lfContent, lfOld, lfNew);
|
|
2568
|
+
if (lfUpdated !== null) {
|
|
2569
|
+
updated = lfUpdated;
|
|
2570
|
+
note = " (applied after normalizing CRLF \u2192 LF line endings)";
|
|
2571
|
+
}
|
|
2333
2572
|
}
|
|
2334
|
-
if (
|
|
2573
|
+
if (updated === null) {
|
|
2574
|
+
const strip = (s) => s.split("\n").map((l) => l.replace(/\s+$/, "")).join("\n");
|
|
2575
|
+
const sUpdated = tryMatch(strip(content), strip(oldString), strip(newString));
|
|
2576
|
+
if (sUpdated !== null) {
|
|
2577
|
+
updated = sUpdated;
|
|
2578
|
+
note = " (applied after trimming trailing whitespace to match)";
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
if (updated === null) {
|
|
2582
|
+
const occ = content.split(oldString).length - 1;
|
|
2583
|
+
if (occ === 0) {
|
|
2584
|
+
return {
|
|
2585
|
+
success: false,
|
|
2586
|
+
output: "old_string not found in file. Read the file again and copy the exact text, including whitespace."
|
|
2587
|
+
};
|
|
2588
|
+
}
|
|
2335
2589
|
return {
|
|
2336
2590
|
success: false,
|
|
2337
|
-
output: `old_string occurs ${
|
|
2591
|
+
output: `old_string occurs ${occ} times. Include more surrounding lines to make it unique.`
|
|
2338
2592
|
};
|
|
2339
2593
|
}
|
|
2340
|
-
const updated = content.replace(oldString, newString);
|
|
2341
2594
|
fs2.writeFileSync(filePath, updated, "utf8");
|
|
2342
2595
|
const removed = oldString.split(/\r?\n/).length;
|
|
2343
2596
|
const added = newString.split(/\r?\n/).length;
|
|
@@ -2901,147 +3154,8 @@ var executeCommand = {
|
|
|
2901
3154
|
}
|
|
2902
3155
|
};
|
|
2903
3156
|
|
|
2904
|
-
// src/tools/
|
|
2905
|
-
|
|
2906
|
-
init_exec();
|
|
2907
|
-
import fs4 from "fs";
|
|
2908
|
-
import path4 from "path";
|
|
2909
|
-
var DEFAULT_TIMEOUT_MS2 = 18e4;
|
|
2910
|
-
function readPackageJson(cwd) {
|
|
2911
|
-
try {
|
|
2912
|
-
return JSON.parse(fs4.readFileSync(path4.join(cwd, "package.json"), "utf8"));
|
|
2913
|
-
} catch {
|
|
2914
|
-
return null;
|
|
2915
|
-
}
|
|
2916
|
-
}
|
|
2917
|
-
function readTextIfExists(cwd, file) {
|
|
2918
|
-
try {
|
|
2919
|
-
return fs4.readFileSync(path4.join(cwd, file), "utf8");
|
|
2920
|
-
} catch {
|
|
2921
|
-
return null;
|
|
2922
|
-
}
|
|
2923
|
-
}
|
|
2924
|
-
function fileExists(cwd, file) {
|
|
2925
|
-
try {
|
|
2926
|
-
return fs4.existsSync(path4.join(cwd, file));
|
|
2927
|
-
} catch {
|
|
2928
|
-
return false;
|
|
2929
|
-
}
|
|
2930
|
-
}
|
|
2931
|
-
function detectTestCommand(cwd) {
|
|
2932
|
-
const pkg = readPackageJson(cwd);
|
|
2933
|
-
const testScript = pkg && typeof pkg.scripts?.test === "string" ? pkg.scripts.test : null;
|
|
2934
|
-
if (testScript && !/error|no test specified/i.test(testScript)) {
|
|
2935
|
-
return "npm test";
|
|
2936
|
-
}
|
|
2937
|
-
if (fileExists(cwd, "pytest.ini") || fileExists(cwd, "tests")) return "pytest -q";
|
|
2938
|
-
const pyproject = readTextIfExists(cwd, "pyproject.toml");
|
|
2939
|
-
if (pyproject && /\[tool\.pytest/.test(pyproject)) return "pytest -q";
|
|
2940
|
-
if (fileExists(cwd, "go.mod")) return "go test ./...";
|
|
2941
|
-
if (fileExists(cwd, "Cargo.toml")) return "cargo test";
|
|
2942
|
-
return null;
|
|
2943
|
-
}
|
|
2944
|
-
function detectLintCommand(cwd) {
|
|
2945
|
-
if (fileExists(cwd, "eslint.config.js") || fileExists(cwd, "eslint.config.mjs") || fileExists(cwd, ".eslintrc.json") || fileExists(cwd, ".eslintrc.js")) {
|
|
2946
|
-
return "npx --no-install eslint .";
|
|
2947
|
-
}
|
|
2948
|
-
if (fileExists(cwd, "biome.json")) return "npx --no-install biome check .";
|
|
2949
|
-
if (fileExists(cwd, ".ruff.toml") || fileExists(cwd, "ruff.toml")) return "ruff check .";
|
|
2950
|
-
const pyproject = readTextIfExists(cwd, "pyproject.toml");
|
|
2951
|
-
if (pyproject && /\[tool\.ruff/.test(pyproject)) return "ruff check .";
|
|
2952
|
-
return null;
|
|
2953
|
-
}
|
|
2954
|
-
function detectTypecheckCommand(cwd) {
|
|
2955
|
-
if (fileExists(cwd, "tsconfig.json")) return "npx --no-install tsc --noEmit";
|
|
2956
|
-
if (fileExists(cwd, "mypy.ini")) return "python -m mypy .";
|
|
2957
|
-
const pyproject = readTextIfExists(cwd, "pyproject.toml");
|
|
2958
|
-
if (pyproject && /\[tool\.mypy/.test(pyproject)) return "python -m mypy .";
|
|
2959
|
-
if (fileExists(cwd, "go.mod")) return "go vet ./...";
|
|
2960
|
-
if (fileExists(cwd, "Cargo.toml")) return "cargo check";
|
|
2961
|
-
return null;
|
|
2962
|
-
}
|
|
2963
|
-
function makeRunnerTool(config) {
|
|
2964
|
-
return {
|
|
2965
|
-
name: config.name,
|
|
2966
|
-
description: config.description,
|
|
2967
|
-
risk: "safe",
|
|
2968
|
-
timeoutMs: DEFAULT_TIMEOUT_MS2,
|
|
2969
|
-
parameters: {
|
|
2970
|
-
type: "object",
|
|
2971
|
-
properties: {
|
|
2972
|
-
command: {
|
|
2973
|
-
type: "string",
|
|
2974
|
-
description: "Exact command to run. If omitted, the project type is auto-detected."
|
|
2975
|
-
},
|
|
2976
|
-
timeout_ms: { type: "number", description: `Timeout in ms. Defaults to ${DEFAULT_TIMEOUT_MS2}.` }
|
|
2977
|
-
}
|
|
2978
|
-
},
|
|
2979
|
-
async execute(args, ctx) {
|
|
2980
|
-
let command = firstString(args, "command");
|
|
2981
|
-
if (!command) {
|
|
2982
|
-
command = config.detect(ctx.cwd) ?? void 0;
|
|
2983
|
-
if (!command) {
|
|
2984
|
-
return {
|
|
2985
|
-
success: false,
|
|
2986
|
-
output: `Could not auto-detect a command for this project. ${config.fallbackHint} Use the command argument to run one explicitly.`
|
|
2987
|
-
};
|
|
2988
|
-
}
|
|
2989
|
-
}
|
|
2990
|
-
const timeoutMs = Math.min(6e5, firstNumber(args, "timeout_ms") ?? DEFAULT_TIMEOUT_MS2);
|
|
2991
|
-
const outcome = await runShellCommand(command, ctx.cwd, timeoutMs);
|
|
2992
|
-
const formatted = formatOutcome(outcome);
|
|
2993
|
-
return { success: formatted.success, output: formatted.output, truncated: formatted.truncated };
|
|
2994
|
-
}
|
|
2995
|
-
};
|
|
2996
|
-
}
|
|
2997
|
-
var runTests = makeRunnerTool({
|
|
2998
|
-
name: "run_tests",
|
|
2999
|
-
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.",
|
|
3000
|
-
detect: detectTestCommand,
|
|
3001
|
-
fallbackHint: "No test runner found (looked for package.json scripts.test, pytest, go, cargo)."
|
|
3002
|
-
});
|
|
3003
|
-
var runLinter = makeRunnerTool({
|
|
3004
|
-
name: "run_linter",
|
|
3005
|
-
description: "Run the project linter (eslint / biome / ruff auto-detected).",
|
|
3006
|
-
detect: detectLintCommand,
|
|
3007
|
-
fallbackHint: "No linter config found (looked for eslint, biome, ruff)."
|
|
3008
|
-
});
|
|
3009
|
-
var runTypecheck = makeRunnerTool({
|
|
3010
|
-
name: "run_typecheck",
|
|
3011
|
-
description: "Run static type checking (tsc --noEmit / mypy / go vet / cargo check auto-detected).",
|
|
3012
|
-
detect: detectTypecheckCommand,
|
|
3013
|
-
fallbackHint: "No typechecker config found (looked for tsconfig.json, mypy, go, cargo)."
|
|
3014
|
-
});
|
|
3015
|
-
var inspectEnv = {
|
|
3016
|
-
name: "inspect_env",
|
|
3017
|
-
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.",
|
|
3018
|
-
risk: "safe",
|
|
3019
|
-
parameters: { type: "object", properties: {} },
|
|
3020
|
-
async execute(_args, ctx) {
|
|
3021
|
-
const lines = [
|
|
3022
|
-
`os: ${process.platform} ${process.version} ${process.arch}`,
|
|
3023
|
-
`workspace: ${ctx.cwd}`
|
|
3024
|
-
];
|
|
3025
|
-
const probes = [
|
|
3026
|
-
["node", "node --version"],
|
|
3027
|
-
["npm", "npm --version"],
|
|
3028
|
-
["npx", "npx --version"],
|
|
3029
|
-
["python", "python --version"],
|
|
3030
|
-
["pip", "pip --version"],
|
|
3031
|
-
["git", "git --version"],
|
|
3032
|
-
["docker", "docker --version"]
|
|
3033
|
-
];
|
|
3034
|
-
const results = await Promise.all(
|
|
3035
|
-
probes.map(async ([label, probe]) => {
|
|
3036
|
-
const outcome = await runShellCommand(probe, ctx.cwd, 8e3);
|
|
3037
|
-
const version2 = `${outcome.stdout} ${outcome.stderr}`.trim().split("\n")[0] ?? "";
|
|
3038
|
-
return `${label}: ${outcome.exitCode === 0 ? version2 || "installed (version unknown)" : "not available"}`;
|
|
3039
|
-
})
|
|
3040
|
-
);
|
|
3041
|
-
lines.push(...results);
|
|
3042
|
-
return { success: true, output: lines.join("\n") };
|
|
3043
|
-
}
|
|
3044
|
-
};
|
|
3157
|
+
// src/tools/registry.ts
|
|
3158
|
+
init_testing();
|
|
3045
3159
|
|
|
3046
3160
|
// src/tools/memory.ts
|
|
3047
3161
|
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.14",
|
|
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",
|