@menteeai/menteeswe 0.1.18 → 0.1.19

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.
Files changed (2) hide show
  1. package/dist/cli.js +85 -13
  2. 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 crypto2 from "crypto";
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 = crypto2.createHash("sha1").update(cwd).digest("hex").slice(0, 16);
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,43 @@ 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 saveRecentFiles(cwd, reads, writes) {
755
+ try {
756
+ let prev = { reads: [], writes: [] };
757
+ try {
758
+ const parsed = JSON.parse(fs9.readFileSync(recentFile(cwd), "utf8"));
759
+ if (parsed && Array.isArray(parsed.reads) && Array.isArray(parsed.writes)) prev = parsed;
760
+ } catch {
761
+ }
762
+ const merge = (known, fresh) => [.../* @__PURE__ */ new Set([...fresh, ...known])].slice(0, 15);
763
+ fs9.writeFileSync(
764
+ recentFile(cwd),
765
+ JSON.stringify({ reads: merge(prev.reads, reads), writes: merge(prev.writes, writes) }, null, 2)
766
+ );
767
+ } catch {
768
+ }
769
+ }
770
+ function formatRecentFiles(cwd) {
771
+ try {
772
+ const parsed = JSON.parse(fs9.readFileSync(recentFile(cwd), "utf8"));
773
+ const writes = (parsed.writes ?? []).slice(0, 10);
774
+ const reads = (parsed.reads ?? []).filter((f) => !writes.includes(f)).slice(0, 10);
775
+ if (reads.length === 0 && writes.length === 0) return "";
776
+ const lines = ["# Known files from earlier tasks in this project"];
777
+ if (writes.length > 0) lines.push(`Modified recently: ${writes.join(", ")}`);
778
+ if (reads.length > 0) lines.push(`Read recently: ${reads.join(", ")}`);
779
+ lines.push(
780
+ "Reuse this knowledge \u2014 do not re-search or re-read these files unless you suspect they changed. If you read a file earlier and it is unchanged, you may apply_patch directly."
781
+ );
782
+ return lines.join("\n");
783
+ } catch {
784
+ return "";
785
+ }
786
+ }
750
787
  var init_conversation = __esm({
751
788
  "src/agent/conversation.ts"() {
752
789
  "use strict";
@@ -761,6 +798,7 @@ function createAgentState(task, maxIterations) {
761
798
  iteration: 0,
762
799
  maxIterations,
763
800
  modifiedFiles: [],
801
+ readFiles: [],
764
802
  commandsRun: [],
765
803
  errors: [],
766
804
  lastErrorSignature: null,
@@ -898,7 +936,10 @@ async function runAgent(options) {
898
936
  } = options;
899
937
  const state = createAgentState(task, maxIterations);
900
938
  const prior = formatConversationContext(cwd);
901
- const system = buildSystemPrompt(cwd) + (prior ? `
939
+ const knownFiles = formatRecentFiles(cwd);
940
+ const system = buildSystemPrompt(cwd) + (knownFiles ? `
941
+
942
+ ${knownFiles}` : "") + (prior ? `
902
943
 
903
944
  ${prior}
904
945
 
@@ -910,6 +951,7 @@ ${systemExtra}` : "");
910
951
  const toolCtx = {
911
952
  cwd,
912
953
  approval,
954
+ fileState: /* @__PURE__ */ new Map(),
913
955
  emit: (type, message, data) => {
914
956
  bus.emit(type, message, data);
915
957
  }
@@ -961,6 +1003,7 @@ ${systemExtra}` : "");
961
1003
  bus.emit("error", message + hint);
962
1004
  finalText = `Model request failed: ${message}`;
963
1005
  appendTurn(cwd, task, finalText);
1006
+ saveRecentFiles(cwd, state.readFiles, state.modifiedFiles);
964
1007
  bus.emit("task_completed", finalText.slice(0, 400), {
965
1008
  success: false,
966
1009
  finalText,
@@ -1121,7 +1164,10 @@ Investigate the root cause, fix it, then finish with a final answer.`
1121
1164
  }
1122
1165
  const readHeader = result.success && tool.name === "read_file" ? result.output.split("\n")[0] ?? "" : "";
1123
1166
  const readRel = readHeader ? normalizeRel(readHeader.replace(/\s+\(\d+ lines\)\s*$/, "").trim()) : "";
1124
- if (readRel) readMsgIndex.set(readRel, messages.length);
1167
+ if (readRel) {
1168
+ readMsgIndex.set(readRel, messages.length);
1169
+ if (!state.readFiles.includes(readRel) && state.readFiles.length < 20) state.readFiles.push(readRel);
1170
+ }
1125
1171
  if ((tool.name === "apply_patch" || tool.name === "write_file") && result.success) {
1126
1172
  const patchedRel = normalizeRel(
1127
1173
  typeof result.data?.path === "string" && result.data.path ? result.data.path : typeof args.path === "string" ? args.path : ""
@@ -1160,7 +1206,10 @@ Investigate the root cause, fix it, then finish with a final answer.`
1160
1206
  bus.emit("warning", finalText);
1161
1207
  }
1162
1208
  const success = sawFinish && state.errors.length === 0;
1163
- if (finalText && !signal?.aborted) appendTurn(cwd, task, finalText);
1209
+ if (finalText && !signal?.aborted) {
1210
+ appendTurn(cwd, task, finalText);
1211
+ saveRecentFiles(cwd, state.readFiles, state.modifiedFiles);
1212
+ }
1164
1213
  bus.emit("task_completed", finalText.slice(0, 400), {
1165
1214
  success,
1166
1215
  finalText,
@@ -1195,7 +1244,7 @@ var init_loop = __esm({
1195
1244
  var version;
1196
1245
  var init_package = __esm({
1197
1246
  "package.json"() {
1198
- version = "0.1.18";
1247
+ version = "0.1.19";
1199
1248
  }
1200
1249
  });
1201
1250
 
@@ -1558,7 +1607,7 @@ function App(props) {
1558
1607
  const [toolHistory, setToolHistory] = useState3([]);
1559
1608
  const [inspectorIndex, setInspectorIndex] = useState3(null);
1560
1609
  const [detailsOpen, setDetailsOpen] = useState3(false);
1561
- const [showDetails, setShowDetails] = useState3(false);
1610
+ const [showDetails, setShowDetails] = useState3(true);
1562
1611
  const [thinkingText, setThinkingText] = useState3("");
1563
1612
  const [phase, setPhase] = useState3("");
1564
1613
  const [showEdits, setShowEdits] = useState3(true);
@@ -2369,6 +2418,10 @@ function createProvider(provider, config, modelOverride) {
2369
2418
  init_base();
2370
2419
  import fs2 from "fs";
2371
2420
  import path2 from "path";
2421
+ import crypto from "crypto";
2422
+ function contentHash(content) {
2423
+ return crypto.createHash("sha1").update(content).digest("hex").slice(0, 12);
2424
+ }
2372
2425
  function resolveWithinWorkspace(cwd, target) {
2373
2426
  const resolved = path2.resolve(cwd, target);
2374
2427
  const normalizedCwd = path2.resolve(cwd);
@@ -2503,9 +2556,16 @@ var readFile = {
2503
2556
  if (start > lines.length) {
2504
2557
  return {
2505
2558
  success: false,
2506
- output: `start_line ${start} is beyond end of file (${lines.length} lines)`
2559
+ 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.`
2560
+ };
2561
+ }
2562
+ if (Number.isNaN(end) || end < start) {
2563
+ return {
2564
+ success: false,
2565
+ 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
2566
  };
2508
2567
  }
2568
+ ctx.fileState?.set(filePath, contentHash(content));
2509
2569
  const slice = lines.slice(start - 1, end).map((line, i) => `${start + i}: ${line}`);
2510
2570
  const { text, truncated } = truncateOutput(slice.join("\n"), 12e3);
2511
2571
  const note = end < lines.length ? `
@@ -2535,6 +2595,7 @@ var writeFile = {
2535
2595
  const content = requireString(args, "content");
2536
2596
  fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
2537
2597
  fs2.writeFileSync(filePath, content, "utf8");
2598
+ ctx.fileState?.set(filePath, contentHash(content));
2538
2599
  return {
2539
2600
  success: true,
2540
2601
  output: `Wrote ${Buffer.byteLength(content, "utf8")} bytes to ${relativeToWorkspace(ctx.cwd, filePath)}`
@@ -2567,6 +2628,16 @@ var applyPatch = {
2567
2628
  if (oldString === newString) {
2568
2629
  return { success: false, output: "old_string and new_string are identical; nothing to change." };
2569
2630
  }
2631
+ const relPath = relativeToWorkspace(ctx.cwd, filePath);
2632
+ const totalLines = content.split(/\r?\n/).length;
2633
+ const currentHash = contentHash(content);
2634
+ const knownHash = ctx.fileState?.get(filePath);
2635
+ if (knownHash && knownHash !== currentHash) {
2636
+ return {
2637
+ success: false,
2638
+ 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.`
2639
+ };
2640
+ }
2570
2641
  const tryMatch = (c, o, n) => {
2571
2642
  const occ = c.split(o).length - 1;
2572
2643
  return occ === 1 ? c.replace(o, n) : null;
@@ -2596,15 +2667,16 @@ var applyPatch = {
2596
2667
  if (occ === 0) {
2597
2668
  return {
2598
2669
  success: false,
2599
- output: "old_string not found in file. Read the file again and copy the exact text, including whitespace."
2670
+ 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
2671
  };
2601
2672
  }
2602
2673
  return {
2603
2674
  success: false,
2604
- output: `old_string occurs ${occ} times. Include more surrounding lines to make it unique.`
2675
+ output: `PATCH_FAILED: old_string occurs ${occ} times in ${relPath}. Recovery: include more surrounding lines to make it unique, then retry.`
2605
2676
  };
2606
2677
  }
2607
2678
  fs2.writeFileSync(filePath, updated, "utf8");
2679
+ ctx.fileState?.set(filePath, contentHash(updated));
2608
2680
  const removed = oldString.split(/\r?\n/).length;
2609
2681
  const added = newString.split(/\r?\n/).length;
2610
2682
  return {
@@ -3175,12 +3247,12 @@ init_config();
3175
3247
  init_base();
3176
3248
  import fs5 from "fs";
3177
3249
  import path5 from "path";
3178
- import crypto from "crypto";
3250
+ import crypto2 from "crypto";
3179
3251
  function memoryDir() {
3180
3252
  return path5.join(configDir(), "memory");
3181
3253
  }
3182
3254
  function memoryFile(cwd) {
3183
- const key = crypto.createHash("sha1").update(path5.resolve(cwd)).digest("hex").slice(0, 16);
3255
+ const key = crypto2.createHash("sha1").update(path5.resolve(cwd)).digest("hex").slice(0, 16);
3184
3256
  return path5.join(memoryDir(), `${key}.json`);
3185
3257
  }
3186
3258
  function load(cwd) {
@@ -3222,7 +3294,7 @@ var memoryTool = {
3222
3294
  }
3223
3295
  const topic = firstString(args, "topic") ?? "";
3224
3296
  const entry = {
3225
- id: crypto.randomBytes(4).toString("hex"),
3297
+ id: crypto2.randomBytes(4).toString("hex"),
3226
3298
  topic,
3227
3299
  content: content.trim(),
3228
3300
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@menteeai/menteeswe",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
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",