march-cli 0.1.33 → 0.1.34
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/package.json
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function isMemoryIdLike(value) {
|
|
2
|
+
return /^mem_[a-z0-9_-]+$/i.test(String(value ?? ""));
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function isSingleEditAway(left, right) {
|
|
6
|
+
if (left === right) return true;
|
|
7
|
+
if (Math.abs(left.length - right.length) > 1) return false;
|
|
8
|
+
if (left.length === right.length) return hasSingleSubstitution(left, right);
|
|
9
|
+
return hasSingleInsertionOrDeletion(left, right);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function hasSingleSubstitution(left, right) {
|
|
13
|
+
let mismatches = 0;
|
|
14
|
+
for (let i = 0; i < left.length; i += 1) {
|
|
15
|
+
if (left[i] !== right[i]) mismatches += 1;
|
|
16
|
+
if (mismatches > 1) return false;
|
|
17
|
+
}
|
|
18
|
+
return mismatches === 1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function hasSingleInsertionOrDeletion(left, right) {
|
|
22
|
+
const shorter = left.length < right.length ? left : right;
|
|
23
|
+
const longer = left.length < right.length ? right : left;
|
|
24
|
+
let edits = 0;
|
|
25
|
+
for (let i = 0, j = 0; i < shorter.length && j < longer.length; ) {
|
|
26
|
+
if (shorter[i] === longer[j]) {
|
|
27
|
+
i += 1;
|
|
28
|
+
j += 1;
|
|
29
|
+
} else {
|
|
30
|
+
edits += 1;
|
|
31
|
+
j += 1;
|
|
32
|
+
if (edits > 1) return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { scoreEntry, toHint } from "./markdown/markdown-recall.mjs";
|
|
14
14
|
import { clearMarkdownMemoryIndex, loadMarkdownMemoryIndex, openMarkdownMemoryIndex, queryMarkdownMemoryIndex, replaceMarkdownMemoryIndex } from "./markdown/sqlite-index.mjs";
|
|
15
15
|
import { softDeleteMemoryFile } from "./markdown/markdown-delete.mjs";
|
|
16
|
+
import { isMemoryIdLike, isSingleEditAway } from "./markdown/memory-id.mjs";
|
|
16
17
|
import { openMarkdownRoot, searchMarkdownRoot } from "./search.mjs";
|
|
17
18
|
|
|
18
19
|
export { formatRecallHints } from "./markdown/markdown-recall.mjs";
|
|
@@ -154,10 +155,9 @@ export class MarkdownMemoryStore {
|
|
|
154
155
|
this.ensureFresh();
|
|
155
156
|
const raw = String(identifier ?? "").trim();
|
|
156
157
|
if (!raw) throw new Error("memory id or path is required");
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
return { ...opened, entry: entry ?? null };
|
|
158
|
+
const resolved = this.#resolveOpenTarget(raw);
|
|
159
|
+
const opened = openMarkdownRoot({ root: this.root, path: resolved.path, ...options });
|
|
160
|
+
return { ...opened, entry: resolved.entry, requestedId: resolved.requestedId };
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
save({ id = null, name = null, description = null, body = null, tags = null } = {}) {
|
|
@@ -261,11 +261,22 @@ export class MarkdownMemoryStore {
|
|
|
261
261
|
return path;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
+
#resolveOpenTarget(raw) {
|
|
265
|
+
const exact = this.entries.get(raw);
|
|
266
|
+
if (exact) return { path: exact.path, entry: exact, requestedId: null };
|
|
267
|
+
if (!isMemoryIdLike(raw)) return { path: this.#resolveMemoryPath(raw), entry: null, requestedId: null };
|
|
268
|
+
|
|
269
|
+
const candidates = [...this.entries.values()].filter((entry) => isSingleEditAway(raw, entry.id));
|
|
270
|
+
if (candidates.length === 1) return { path: candidates[0].path, entry: candidates[0], requestedId: raw };
|
|
271
|
+
if (candidates.length > 1) {
|
|
272
|
+
throw new Error(`memory id is ambiguous: ${raw}; candidates: ${candidates.map((entry) => entry.id).join(", ")}`);
|
|
273
|
+
}
|
|
274
|
+
throw new Error(`memory not found: ${raw}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
264
277
|
#activeMemoryPaths() {
|
|
265
278
|
return [...this.entries.values()]
|
|
266
279
|
.filter((entry) => entry.status === "active")
|
|
267
280
|
.map((entry) => entry.path);
|
|
268
281
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
282
|
}
|
|
@@ -146,12 +146,13 @@ function formatMemorySearchResults(results, requestedSource) {
|
|
|
146
146
|
|
|
147
147
|
function formatLocalOpen(opened) {
|
|
148
148
|
const range = opened.startLine && opened.endLine ? `lines: ${opened.startLine}-${opened.endLine}\n` : "";
|
|
149
|
-
|
|
149
|
+
const correction = opened.requestedId && opened.entry?.id ? `matched id: ${opened.entry.id} (requested: ${opened.requestedId})\n` : "";
|
|
150
|
+
return `path: ${opened.path}\n${correction}${range}Use edit_file with this path for targeted edits.\n\ncontent:\n${opened.content}`;
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
function formatRemoteOpen(opened) {
|
|
153
154
|
const range = opened.startLine && opened.endLine ? `lines: ${opened.startLine}-${opened.endLine}\n` : "";
|
|
154
|
-
return `source: ${opened.source}\npath: ${opened.path}\n${range}Remote memory is read-only.\n\n
|
|
155
|
+
return `source: ${opened.source}\npath: ${opened.path}\n${range}Remote memory is read-only.\n\ncontent:\n${opened.content}`;
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
function formatMemorySearchMiss(query, source) {
|