@menteeai/menteeswe 0.1.18 → 0.1.20
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 +140 -13
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -694,7 +694,7 @@ var init_prompts = __esm({
|
|
|
694
694
|
// src/agent/conversation.ts
|
|
695
695
|
import fs9 from "fs";
|
|
696
696
|
import path8 from "path";
|
|
697
|
-
import
|
|
697
|
+
import crypto3 from "crypto";
|
|
698
698
|
import os3 from "os";
|
|
699
699
|
function sessionsDir() {
|
|
700
700
|
const dir = path8.join(os3.homedir(), ".mentee", "sessions");
|
|
@@ -702,7 +702,7 @@ function sessionsDir() {
|
|
|
702
702
|
return dir;
|
|
703
703
|
}
|
|
704
704
|
function sessionFile(cwd) {
|
|
705
|
-
const hash =
|
|
705
|
+
const hash = crypto3.createHash("sha1").update(cwd).digest("hex").slice(0, 16);
|
|
706
706
|
return path8.join(sessionsDir(), `${hash}.json`);
|
|
707
707
|
}
|
|
708
708
|
function loadConversation(cwd, limit = 12) {
|
|
@@ -747,6 +747,97 @@ Assistant: ${t.response.trim()}
|
|
|
747
747
|
}
|
|
748
748
|
return [header, ...blocks, ""].join("\n");
|
|
749
749
|
}
|
|
750
|
+
function recentFile(cwd) {
|
|
751
|
+
const hash = crypto3.createHash("sha1").update(cwd).digest("hex").slice(0, 16);
|
|
752
|
+
return path8.join(sessionsDir(), `${hash}.files.json`);
|
|
753
|
+
}
|
|
754
|
+
function fileHash(content) {
|
|
755
|
+
return crypto3.createHash("sha1").update(content).digest("hex").slice(0, 12);
|
|
756
|
+
}
|
|
757
|
+
function normalizeRecords(value) {
|
|
758
|
+
if (!Array.isArray(value)) return [];
|
|
759
|
+
const out = [];
|
|
760
|
+
for (const item of value) {
|
|
761
|
+
if (typeof item === "string") out.push({ path: item });
|
|
762
|
+
else if (item && typeof item === "object" && typeof item.path === "string") {
|
|
763
|
+
out.push(item);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return out;
|
|
767
|
+
}
|
|
768
|
+
function loadRecentFiles(cwd) {
|
|
769
|
+
try {
|
|
770
|
+
const parsed = JSON.parse(fs9.readFileSync(recentFile(cwd), "utf8"));
|
|
771
|
+
return { reads: normalizeRecords(parsed.reads), writes: normalizeRecords(parsed.writes) };
|
|
772
|
+
} catch {
|
|
773
|
+
return { reads: [], writes: [] };
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
function saveRecentFiles(cwd, reads, writes, hashes) {
|
|
777
|
+
try {
|
|
778
|
+
const prev = loadRecentFiles(cwd);
|
|
779
|
+
const absHash = (rel) => hashes?.get(path8.resolve(cwd, rel));
|
|
780
|
+
const merge = (known, fresh) => {
|
|
781
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
782
|
+
for (const rel of fresh) {
|
|
783
|
+
byPath.set(rel, { path: rel, hash: absHash(rel) });
|
|
784
|
+
}
|
|
785
|
+
for (const rec of known) {
|
|
786
|
+
if (!byPath.has(rec.path)) byPath.set(rec.path, rec);
|
|
787
|
+
}
|
|
788
|
+
return [...byPath.values()].slice(0, 15);
|
|
789
|
+
};
|
|
790
|
+
fs9.writeFileSync(
|
|
791
|
+
recentFile(cwd),
|
|
792
|
+
JSON.stringify({ reads: merge(prev.reads, reads), writes: merge(prev.writes, writes) }, null, 2)
|
|
793
|
+
);
|
|
794
|
+
} catch {
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
function formatRecentFiles(cwd, prewarm) {
|
|
798
|
+
const stored = loadRecentFiles(cwd);
|
|
799
|
+
if (stored.reads.length === 0 && stored.writes.length === 0) return "";
|
|
800
|
+
const verify = (records) => {
|
|
801
|
+
const unchanged = [];
|
|
802
|
+
const changed = [];
|
|
803
|
+
const legacy = [];
|
|
804
|
+
for (const rec of records.slice(0, 15)) {
|
|
805
|
+
if (!rec.hash) {
|
|
806
|
+
legacy.push(rec);
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
try {
|
|
810
|
+
const content = fs9.readFileSync(path8.resolve(cwd, rec.path), "utf8");
|
|
811
|
+
const current = fileHash(content);
|
|
812
|
+
if (current === rec.hash) {
|
|
813
|
+
unchanged.push(rec);
|
|
814
|
+
prewarm?.set(path8.resolve(cwd, rec.path), current);
|
|
815
|
+
} else {
|
|
816
|
+
changed.push(rec);
|
|
817
|
+
}
|
|
818
|
+
} catch {
|
|
819
|
+
changed.push(rec);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
return { unchanged, changed, legacy };
|
|
823
|
+
};
|
|
824
|
+
const w = verify(stored.writes);
|
|
825
|
+
const writePaths = new Set(stored.writes.map((r2) => r2.path));
|
|
826
|
+
const r = verify(stored.reads.filter((rec) => !writePaths.has(rec.path)));
|
|
827
|
+
const lines = ["# Known files from earlier tasks in this project"];
|
|
828
|
+
const join = (list) => list.map((rec) => rec.path).join(", ");
|
|
829
|
+
if (w.unchanged.length > 0) lines.push(`Modified recently, verified unchanged on disk: ${join(w.unchanged)}`);
|
|
830
|
+
if (r.unchanged.length > 0) lines.push(`Read recently, verified unchanged on disk: ${join(r.unchanged)}`);
|
|
831
|
+
const stale = [...w.changed, ...r.changed];
|
|
832
|
+
if (stale.length > 0) lines.push(`Changed (or missing) on disk since then \u2014 re-read before editing: ${join(stale)}`);
|
|
833
|
+
for (const rec of [...w.legacy, ...r.legacy]) {
|
|
834
|
+
lines.push(`Known from earlier tasks (state unknown): ${rec.path}`);
|
|
835
|
+
}
|
|
836
|
+
lines.push(
|
|
837
|
+
"For files verified unchanged you may apply_patch directly without re-reading; the harness validates the hash and will tell you if the file changed. Do not re-search or re-read verified files unless needed."
|
|
838
|
+
);
|
|
839
|
+
return lines.join("\n");
|
|
840
|
+
}
|
|
750
841
|
var init_conversation = __esm({
|
|
751
842
|
"src/agent/conversation.ts"() {
|
|
752
843
|
"use strict";
|
|
@@ -761,6 +852,7 @@ function createAgentState(task, maxIterations) {
|
|
|
761
852
|
iteration: 0,
|
|
762
853
|
maxIterations,
|
|
763
854
|
modifiedFiles: [],
|
|
855
|
+
readFiles: [],
|
|
764
856
|
commandsRun: [],
|
|
765
857
|
errors: [],
|
|
766
858
|
lastErrorSignature: null,
|
|
@@ -897,8 +989,12 @@ async function runAgent(options) {
|
|
|
897
989
|
signal
|
|
898
990
|
} = options;
|
|
899
991
|
const state = createAgentState(task, maxIterations);
|
|
992
|
+
const fileState = /* @__PURE__ */ new Map();
|
|
900
993
|
const prior = formatConversationContext(cwd);
|
|
901
|
-
const
|
|
994
|
+
const knownFiles = formatRecentFiles(cwd, fileState);
|
|
995
|
+
const system = buildSystemPrompt(cwd) + (knownFiles ? `
|
|
996
|
+
|
|
997
|
+
${knownFiles}` : "") + (prior ? `
|
|
902
998
|
|
|
903
999
|
${prior}
|
|
904
1000
|
|
|
@@ -910,6 +1006,7 @@ ${systemExtra}` : "");
|
|
|
910
1006
|
const toolCtx = {
|
|
911
1007
|
cwd,
|
|
912
1008
|
approval,
|
|
1009
|
+
fileState,
|
|
913
1010
|
emit: (type, message, data) => {
|
|
914
1011
|
bus.emit(type, message, data);
|
|
915
1012
|
}
|
|
@@ -961,6 +1058,7 @@ ${systemExtra}` : "");
|
|
|
961
1058
|
bus.emit("error", message + hint);
|
|
962
1059
|
finalText = `Model request failed: ${message}`;
|
|
963
1060
|
appendTurn(cwd, task, finalText);
|
|
1061
|
+
saveRecentFiles(cwd, state.readFiles, state.modifiedFiles, fileState);
|
|
964
1062
|
bus.emit("task_completed", finalText.slice(0, 400), {
|
|
965
1063
|
success: false,
|
|
966
1064
|
finalText,
|
|
@@ -1121,7 +1219,10 @@ Investigate the root cause, fix it, then finish with a final answer.`
|
|
|
1121
1219
|
}
|
|
1122
1220
|
const readHeader = result.success && tool.name === "read_file" ? result.output.split("\n")[0] ?? "" : "";
|
|
1123
1221
|
const readRel = readHeader ? normalizeRel(readHeader.replace(/\s+\(\d+ lines\)\s*$/, "").trim()) : "";
|
|
1124
|
-
if (readRel)
|
|
1222
|
+
if (readRel) {
|
|
1223
|
+
readMsgIndex.set(readRel, messages.length);
|
|
1224
|
+
if (!state.readFiles.includes(readRel) && state.readFiles.length < 20) state.readFiles.push(readRel);
|
|
1225
|
+
}
|
|
1125
1226
|
if ((tool.name === "apply_patch" || tool.name === "write_file") && result.success) {
|
|
1126
1227
|
const patchedRel = normalizeRel(
|
|
1127
1228
|
typeof result.data?.path === "string" && result.data.path ? result.data.path : typeof args.path === "string" ? args.path : ""
|
|
@@ -1160,7 +1261,10 @@ Investigate the root cause, fix it, then finish with a final answer.`
|
|
|
1160
1261
|
bus.emit("warning", finalText);
|
|
1161
1262
|
}
|
|
1162
1263
|
const success = sawFinish && state.errors.length === 0;
|
|
1163
|
-
if (finalText && !signal?.aborted)
|
|
1264
|
+
if (finalText && !signal?.aborted) {
|
|
1265
|
+
appendTurn(cwd, task, finalText);
|
|
1266
|
+
saveRecentFiles(cwd, state.readFiles, state.modifiedFiles, fileState);
|
|
1267
|
+
}
|
|
1164
1268
|
bus.emit("task_completed", finalText.slice(0, 400), {
|
|
1165
1269
|
success,
|
|
1166
1270
|
finalText,
|
|
@@ -1195,7 +1299,7 @@ var init_loop = __esm({
|
|
|
1195
1299
|
var version;
|
|
1196
1300
|
var init_package = __esm({
|
|
1197
1301
|
"package.json"() {
|
|
1198
|
-
version = "0.1.
|
|
1302
|
+
version = "0.1.20";
|
|
1199
1303
|
}
|
|
1200
1304
|
});
|
|
1201
1305
|
|
|
@@ -1558,7 +1662,7 @@ function App(props) {
|
|
|
1558
1662
|
const [toolHistory, setToolHistory] = useState3([]);
|
|
1559
1663
|
const [inspectorIndex, setInspectorIndex] = useState3(null);
|
|
1560
1664
|
const [detailsOpen, setDetailsOpen] = useState3(false);
|
|
1561
|
-
const [showDetails, setShowDetails] = useState3(
|
|
1665
|
+
const [showDetails, setShowDetails] = useState3(true);
|
|
1562
1666
|
const [thinkingText, setThinkingText] = useState3("");
|
|
1563
1667
|
const [phase, setPhase] = useState3("");
|
|
1564
1668
|
const [showEdits, setShowEdits] = useState3(true);
|
|
@@ -2369,6 +2473,10 @@ function createProvider(provider, config, modelOverride) {
|
|
|
2369
2473
|
init_base();
|
|
2370
2474
|
import fs2 from "fs";
|
|
2371
2475
|
import path2 from "path";
|
|
2476
|
+
import crypto from "crypto";
|
|
2477
|
+
function contentHash(content) {
|
|
2478
|
+
return crypto.createHash("sha1").update(content).digest("hex").slice(0, 12);
|
|
2479
|
+
}
|
|
2372
2480
|
function resolveWithinWorkspace(cwd, target) {
|
|
2373
2481
|
const resolved = path2.resolve(cwd, target);
|
|
2374
2482
|
const normalizedCwd = path2.resolve(cwd);
|
|
@@ -2503,9 +2611,16 @@ var readFile = {
|
|
|
2503
2611
|
if (start > lines.length) {
|
|
2504
2612
|
return {
|
|
2505
2613
|
success: false,
|
|
2506
|
-
output: `start_line ${start} is beyond end of file (${lines.length} lines)
|
|
2614
|
+
output: `READ_RANGE_INVALID: start_line ${start} is beyond end of file (total_lines ${lines.length}). Valid range: 1-${lines.length}. Recovery: retry read_file with start_line <= ${Math.max(1, lines.length)} or omit the range.`
|
|
2615
|
+
};
|
|
2616
|
+
}
|
|
2617
|
+
if (Number.isNaN(end) || end < start) {
|
|
2618
|
+
return {
|
|
2619
|
+
success: false,
|
|
2620
|
+
output: `READ_RANGE_INVALID: end_line (${args.end_line}) is before start_line (${start}). Valid range: 1-${lines.length}. Recovery: retry with end_line >= start_line.`
|
|
2507
2621
|
};
|
|
2508
2622
|
}
|
|
2623
|
+
ctx.fileState?.set(filePath, contentHash(content));
|
|
2509
2624
|
const slice = lines.slice(start - 1, end).map((line, i) => `${start + i}: ${line}`);
|
|
2510
2625
|
const { text, truncated } = truncateOutput(slice.join("\n"), 12e3);
|
|
2511
2626
|
const note = end < lines.length ? `
|
|
@@ -2535,6 +2650,7 @@ var writeFile = {
|
|
|
2535
2650
|
const content = requireString(args, "content");
|
|
2536
2651
|
fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
|
|
2537
2652
|
fs2.writeFileSync(filePath, content, "utf8");
|
|
2653
|
+
ctx.fileState?.set(filePath, contentHash(content));
|
|
2538
2654
|
return {
|
|
2539
2655
|
success: true,
|
|
2540
2656
|
output: `Wrote ${Buffer.byteLength(content, "utf8")} bytes to ${relativeToWorkspace(ctx.cwd, filePath)}`
|
|
@@ -2567,6 +2683,16 @@ var applyPatch = {
|
|
|
2567
2683
|
if (oldString === newString) {
|
|
2568
2684
|
return { success: false, output: "old_string and new_string are identical; nothing to change." };
|
|
2569
2685
|
}
|
|
2686
|
+
const relPath = relativeToWorkspace(ctx.cwd, filePath);
|
|
2687
|
+
const totalLines = content.split(/\r?\n/).length;
|
|
2688
|
+
const currentHash = contentHash(content);
|
|
2689
|
+
const knownHash = ctx.fileState?.get(filePath);
|
|
2690
|
+
if (knownHash && knownHash !== currentHash) {
|
|
2691
|
+
return {
|
|
2692
|
+
success: false,
|
|
2693
|
+
output: `FILE_CHANGED: ${relPath} changed since it was last read (expected hash ${knownHash}, current ${currentHash}, total_lines ${totalLines}). Recovery: read_file ${relPath} again, then retry apply_patch against the current text.`
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2570
2696
|
const tryMatch = (c, o, n) => {
|
|
2571
2697
|
const occ = c.split(o).length - 1;
|
|
2572
2698
|
return occ === 1 ? c.replace(o, n) : null;
|
|
@@ -2596,15 +2722,16 @@ var applyPatch = {
|
|
|
2596
2722
|
if (occ === 0) {
|
|
2597
2723
|
return {
|
|
2598
2724
|
success: false,
|
|
2599
|
-
output:
|
|
2725
|
+
output: `PATCH_FAILED: old_string not found in ${relPath} (total_lines ${totalLines}, hash ${currentHash}). Recovery: read_file ${relPath} (or the relevant line range) and copy the exact text including whitespace, then retry. Do not guess.`
|
|
2600
2726
|
};
|
|
2601
2727
|
}
|
|
2602
2728
|
return {
|
|
2603
2729
|
success: false,
|
|
2604
|
-
output: `old_string occurs ${occ} times.
|
|
2730
|
+
output: `PATCH_FAILED: old_string occurs ${occ} times in ${relPath}. Recovery: include more surrounding lines to make it unique, then retry.`
|
|
2605
2731
|
};
|
|
2606
2732
|
}
|
|
2607
2733
|
fs2.writeFileSync(filePath, updated, "utf8");
|
|
2734
|
+
ctx.fileState?.set(filePath, contentHash(updated));
|
|
2608
2735
|
const removed = oldString.split(/\r?\n/).length;
|
|
2609
2736
|
const added = newString.split(/\r?\n/).length;
|
|
2610
2737
|
return {
|
|
@@ -3175,12 +3302,12 @@ init_config();
|
|
|
3175
3302
|
init_base();
|
|
3176
3303
|
import fs5 from "fs";
|
|
3177
3304
|
import path5 from "path";
|
|
3178
|
-
import
|
|
3305
|
+
import crypto2 from "crypto";
|
|
3179
3306
|
function memoryDir() {
|
|
3180
3307
|
return path5.join(configDir(), "memory");
|
|
3181
3308
|
}
|
|
3182
3309
|
function memoryFile(cwd) {
|
|
3183
|
-
const key =
|
|
3310
|
+
const key = crypto2.createHash("sha1").update(path5.resolve(cwd)).digest("hex").slice(0, 16);
|
|
3184
3311
|
return path5.join(memoryDir(), `${key}.json`);
|
|
3185
3312
|
}
|
|
3186
3313
|
function load(cwd) {
|
|
@@ -3222,7 +3349,7 @@ var memoryTool = {
|
|
|
3222
3349
|
}
|
|
3223
3350
|
const topic = firstString(args, "topic") ?? "";
|
|
3224
3351
|
const entry = {
|
|
3225
|
-
id:
|
|
3352
|
+
id: crypto2.randomBytes(4).toString("hex"),
|
|
3226
3353
|
topic,
|
|
3227
3354
|
content: content.trim(),
|
|
3228
3355
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@menteeai/menteeswe",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
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",
|