@north-light/crouter 0.3.201 → 0.3.202
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/builtin-memory/insights/capture.md +1 -1
- package/dist/clients/attach/viewer.js +650 -650
- package/dist/commands/memory/delete.js +14 -3
- package/dist/commands/memory/edit.d.ts +1 -0
- package/dist/commands/memory/edit.js +166 -0
- package/dist/commands/memory/history.d.ts +1 -0
- package/dist/commands/memory/history.js +179 -0
- package/dist/commands/memory/list.js +1 -1
- package/dist/commands/memory/read.js +2 -2
- package/dist/commands/memory/shared.d.ts +27 -0
- package/dist/commands/memory/shared.js +69 -0
- package/dist/commands/memory/write.js +54 -61
- package/dist/commands/memory.js +6 -3
- package/dist/core/memory/history.d.ts +54 -0
- package/dist/core/memory/history.js +202 -0
- package/dist/core/memory-resolver.d.ts +21 -0
- package/dist/core/memory-resolver.js +47 -16
- package/dist/core/preview-registry.js +2 -0
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
|
@@ -11,6 +11,20 @@ import { effectiveDocKind, normalizeDocName, normalizeNameSegment, resolveDocNam
|
|
|
11
11
|
import { loadProfileManifest, profileMemoryDir } from './profiles/manifest.js';
|
|
12
12
|
import { memoryDir as nodeMemoryDir } from './runtime/memory.js';
|
|
13
13
|
import { descendantStoreRoots } from './nested-stores.js';
|
|
14
|
+
/** The native (non-plugin) store dirs in resolution precedence — node >
|
|
15
|
+
* project stack > profile > user, builtin excluded as a read-only corpus.
|
|
16
|
+
* Addresses a store directly when the document itself may be absent, which is
|
|
17
|
+
* how a DELETED doc's revision log is still found: history outlives the doc,
|
|
18
|
+
* so its lookup cannot go through document resolution. */
|
|
19
|
+
export function nativeMemoryStoresInPrecedence(scope, includeDescendants = false) {
|
|
20
|
+
const out = [];
|
|
21
|
+
for (const source of memorySourcesInPrecedence(ambientTarget(), scope, includeDescendants)) {
|
|
22
|
+
if (source.scope === 'builtin' || source.memoryDir === null)
|
|
23
|
+
continue;
|
|
24
|
+
out.push({ scope: source.scope, memoryDir: source.memoryDir });
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
14
28
|
/** Canonical, unambiguous identifier for a memory document: `<scope>/<name>`. */
|
|
15
29
|
export function memoryDocId(doc) {
|
|
16
30
|
return `${doc.scope}/${doc.name}`;
|
|
@@ -109,9 +123,9 @@ function memorySourcesInPrecedence(target, scope, includeDescendants = false) {
|
|
|
109
123
|
}
|
|
110
124
|
return out;
|
|
111
125
|
}
|
|
112
|
-
function loadMemoryDoc(scope, path, fallbackName) {
|
|
126
|
+
function loadMemoryDoc(scope, root, path, fallbackName) {
|
|
113
127
|
const { data, body } = parseFrontmatterGeneric(readText(path));
|
|
114
|
-
return { name: resolveDocName(data, fallbackName), scope, path, frontmatter: data, body };
|
|
128
|
+
return { name: resolveDocName(data, fallbackName), scope, path, root, frontmatter: data, body };
|
|
115
129
|
}
|
|
116
130
|
/** All memory docs in one memory/ dir, scanned recursively for *.md (topical
|
|
117
131
|
* subdirs supported), sorted by resolver identity (explicit frontmatter name
|
|
@@ -157,7 +171,7 @@ function listMemoryDocsInDir(scope, dir, quiet = false) {
|
|
|
157
171
|
// `quiet` suppresses the notice for a targeted resolve (a leaf-name read),
|
|
158
172
|
// where another doc's health is irrelevant noise before the result.
|
|
159
173
|
try {
|
|
160
|
-
docs.push(loadMemoryDoc(scope, path, name));
|
|
174
|
+
docs.push(loadMemoryDoc(scope, dir, path, name));
|
|
161
175
|
}
|
|
162
176
|
catch (e) {
|
|
163
177
|
const msg = (e instanceof Error ? e.message : String(e)).split('\n')[0];
|
|
@@ -211,7 +225,7 @@ export function listPluginMemoryDocs(plugin, scope, quiet = false) {
|
|
|
211
225
|
continue;
|
|
212
226
|
const name = `${plugin.name}/${derived}`;
|
|
213
227
|
try {
|
|
214
|
-
docs.push({ ...loadMemoryDoc(scope, file, name), plugin: plugin.name });
|
|
228
|
+
docs.push({ ...loadMemoryDoc(scope, dir, file, name), plugin: plugin.name });
|
|
215
229
|
}
|
|
216
230
|
catch (e) {
|
|
217
231
|
const msg = (e instanceof Error ? e.message : String(e)).split('\n')[0];
|
|
@@ -250,13 +264,13 @@ export function listProjectMemoryDocs(startDir = process.cwd(), profileId = sele
|
|
|
250
264
|
export function listAllMemoryDocs(scope, quiet = false, includeDescendants = false) {
|
|
251
265
|
return memorySourcesInPrecedence(ambientTarget(), scope, includeDescendants).flatMap((source) => sourceMemoryDocs(source, quiet));
|
|
252
266
|
}
|
|
253
|
-
/** Find the direct child of `dir` —
|
|
267
|
+
/** Find the direct child of `dir` — an `ext` file (matched on name minus
|
|
254
268
|
* extension) or a directory — whose NORMALIZED display name equals
|
|
255
269
|
* `segment`. An exact literal match (no prefix to strip) wins over a
|
|
256
270
|
* normalized match, so a `spine` dir sitting beside a `01-spine` dir (a
|
|
257
271
|
* malformed corpus) never masks the literal one; a well-formed corpus has at
|
|
258
272
|
* most one candidate either way. Returns the absolute child path, or null. */
|
|
259
|
-
function matchNormalizedChild(dir, segment, want) {
|
|
273
|
+
function matchNormalizedChild(dir, segment, want, ext = '.md') {
|
|
260
274
|
let entries;
|
|
261
275
|
try {
|
|
262
276
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -276,9 +290,9 @@ function matchNormalizedChild(dir, segment, want) {
|
|
|
276
290
|
}
|
|
277
291
|
}
|
|
278
292
|
else {
|
|
279
|
-
if (!(e.isFile() && e.name.endsWith(
|
|
293
|
+
if (!(e.isFile() && e.name.endsWith(ext)))
|
|
280
294
|
continue;
|
|
281
|
-
const base = e.name.slice(0, -
|
|
295
|
+
const base = e.name.slice(0, -ext.length);
|
|
282
296
|
if (base === segment)
|
|
283
297
|
return join(dir, e.name);
|
|
284
298
|
if (normalizedHit === null && normalizeNameSegment(base) === segment) {
|
|
@@ -297,20 +311,37 @@ function matchNormalizedChild(dir, segment, want) {
|
|
|
297
311
|
* `00-runtime-base.md` findable as `runtime-base` and `01-spine/00-has-manager`
|
|
298
312
|
* findable as `spine/has-manager` — the physical path keeps its pins, only
|
|
299
313
|
* lookup is prefix-blind. */
|
|
300
|
-
function resolveNormalizedPath(baseDir, segments) {
|
|
314
|
+
function resolveNormalizedPath(baseDir, segments, ext = '.md') {
|
|
301
315
|
let curDir = baseDir;
|
|
302
316
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
303
|
-
const next = matchNormalizedChild(curDir, segments[i], 'dir');
|
|
317
|
+
const next = matchNormalizedChild(curDir, segments[i], 'dir', ext);
|
|
304
318
|
if (!next)
|
|
305
319
|
return { filePath: null, dirPath: null };
|
|
306
320
|
curDir = next;
|
|
307
321
|
}
|
|
308
322
|
const last = segments[segments.length - 1];
|
|
309
323
|
return {
|
|
310
|
-
filePath: matchNormalizedChild(curDir, last, 'file'),
|
|
311
|
-
dirPath: matchNormalizedChild(curDir, last, 'dir'),
|
|
324
|
+
filePath: matchNormalizedChild(curDir, last, 'file', ext),
|
|
325
|
+
dirPath: matchNormalizedChild(curDir, last, 'dir', ext),
|
|
312
326
|
};
|
|
313
327
|
}
|
|
328
|
+
/** Resolve a document NAME against a `.history` tree, which mirrors the doc
|
|
329
|
+
* tree segment for segment with `.jsonl` in place of `.md`. Reuses the doc
|
|
330
|
+
* resolution rules so a log outlives its doc under the same name the doc had:
|
|
331
|
+
* numeric prefixes stay prefix-blind (`00-topic.md` logged at
|
|
332
|
+
* `.history/00-topic.jsonl` still answers to `topic`) and a bare directory
|
|
333
|
+
* name falls back to its INDEX log. Returns null when nothing resolves. */
|
|
334
|
+
export function resolveHistoryLogPath(historyDir, segments) {
|
|
335
|
+
const { filePath, dirPath } = resolveNormalizedPath(historyDir, segments, '.jsonl');
|
|
336
|
+
if (filePath)
|
|
337
|
+
return filePath;
|
|
338
|
+
if (dirPath) {
|
|
339
|
+
const index = matchNormalizedChild(dirPath, 'INDEX', 'file', '.jsonl');
|
|
340
|
+
if (index)
|
|
341
|
+
return index;
|
|
342
|
+
}
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
314
345
|
/** Direct full-path lookup of memory/<name>.md within ONE source. Returns that
|
|
315
346
|
* source's single hit, or undefined — a source can produce at most one direct
|
|
316
347
|
* match (native wins over plugin within the source, and at most one plugin's
|
|
@@ -333,11 +364,11 @@ function findMemoryMatchInSource(name, segments, source) {
|
|
|
333
364
|
if (dir) {
|
|
334
365
|
const { filePath, dirPath } = resolveNormalizedPath(dir, segments);
|
|
335
366
|
if (!isLegacySkillDoc && filePath !== null)
|
|
336
|
-
return loadMemoryDoc(source.scope, filePath, name);
|
|
367
|
+
return loadMemoryDoc(source.scope, dir, filePath, name);
|
|
337
368
|
if (dirPath !== null) {
|
|
338
369
|
const indexPath = join(dirPath, 'INDEX.md');
|
|
339
370
|
if (pathExists(indexPath))
|
|
340
|
-
return loadMemoryDoc(source.scope, indexPath, name);
|
|
371
|
+
return loadMemoryDoc(source.scope, dir, indexPath, name);
|
|
341
372
|
}
|
|
342
373
|
}
|
|
343
374
|
// Plugin memory dir: a `<plugin>/<rest>` name resolves against that enabled
|
|
@@ -356,14 +387,14 @@ function findMemoryMatchInSource(name, segments, source) {
|
|
|
356
387
|
if (rest) {
|
|
357
388
|
const { filePath } = resolveNormalizedPath(pdir, restSegments);
|
|
358
389
|
if (restSegments.at(-1) !== 'SKILL' && filePath !== null)
|
|
359
|
-
return loadMemoryDoc(source.scope, filePath, name);
|
|
390
|
+
return loadMemoryDoc(source.scope, pdir, filePath, name);
|
|
360
391
|
}
|
|
361
392
|
// Bare name -> <plugin>/memory/INDEX.md; slashed name -> dir INDEX.
|
|
362
393
|
const pIndexDir = rest ? resolveNormalizedPath(pdir, restSegments).dirPath : pdir;
|
|
363
394
|
if (pIndexDir !== null) {
|
|
364
395
|
const pindex = join(pIndexDir, 'INDEX.md');
|
|
365
396
|
if (pathExists(pindex))
|
|
366
|
-
return loadMemoryDoc(source.scope, pindex, name);
|
|
397
|
+
return loadMemoryDoc(source.scope, pdir, pindex, name);
|
|
367
398
|
}
|
|
368
399
|
}
|
|
369
400
|
return undefined;
|
package/package.json
CHANGED
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/crouter",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.202",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/crouter",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.202",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|