lucid-mem 0.5.0 → 0.5.1
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/README.md +1 -1
- package/lib/commands.mjs +7 -18
- package/package.json +1 -1
- package/test/context.test.mjs +4 -4
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ Memory lives in `~/agent-memory` — a real git repo you can read, diff, and poi
|
|
|
34
34
|
|
|
35
35
|
## Multi-repo memory
|
|
36
36
|
|
|
37
|
-
One memory repo serves every project. `projects/<name>-<hash>.md` files key off the repo's origin remote, so clones share memory and same-named repos don't collide. Inside a known repo, `amr context` is project-scoped — the project's latest 25 plus linked siblings (up to 8 projects). Outside one, it shows global bullets plus the latest 25 pooled across all projects. `private`
|
|
37
|
+
One memory repo serves every project. `projects/<name>-<hash>.md` files key off the repo's origin remote, so clones share memory and same-named repos don't collide. Inside a known repo, `amr context` is project-scoped — the project's latest 25 plus linked siblings (up to 8 projects). Outside one, it shows global bullets plus the latest 25 pooled across all projects. `private` and `noglobal` policies keep a project out of the pooled digest (links still reach private projects — your link graph is your own curation).
|
|
38
38
|
|
|
39
39
|
## MCP
|
|
40
40
|
|
package/lib/commands.mjs
CHANGED
|
@@ -91,9 +91,9 @@ export function cmdContext(flags) {
|
|
|
91
91
|
|
|
92
92
|
// Attach list: the current project, then its config links
|
|
93
93
|
// (transitive, deduped, max 8 total) and any --also names.
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
94
|
+
// Links are the owner's curation — private projects attach fine
|
|
95
|
+
// through them. `private` only keeps a project out of the pooled
|
|
96
|
+
// outside digest, the one unlinked exposure path.
|
|
97
97
|
const bySlug = new Map(listProjects(home).map((p) => [p.slug, p]));
|
|
98
98
|
const attach = [];
|
|
99
99
|
const seen = new Set();
|
|
@@ -103,17 +103,16 @@ export function cmdContext(flags) {
|
|
|
103
103
|
attach.push({ slug, name: bySlug.get(slug)?.name || slug, linked });
|
|
104
104
|
};
|
|
105
105
|
if (proj) push(proj.slug, false);
|
|
106
|
-
const queue = (entry?.links || []).map((k) => ({ k
|
|
106
|
+
const queue = (entry?.links || []).map((k) => ({ k }));
|
|
107
107
|
if (flags.also)
|
|
108
108
|
queue.push(
|
|
109
109
|
...String(flags.also)
|
|
110
110
|
.split(',')
|
|
111
|
-
.map((k) => ({ k
|
|
111
|
+
.map((k) => ({ k }))
|
|
112
112
|
);
|
|
113
|
-
const refused = [];
|
|
114
113
|
const unknown = [];
|
|
115
114
|
while (queue.length && attach.length < 8) {
|
|
116
|
-
const { k: raw
|
|
115
|
+
const { k: raw } = queue.shift();
|
|
117
116
|
const key = String(raw).trim().toLowerCase();
|
|
118
117
|
if (!key) continue;
|
|
119
118
|
const slug = resolveProjectSlug(home, key);
|
|
@@ -123,13 +122,8 @@ export function cmdContext(flags) {
|
|
|
123
122
|
}
|
|
124
123
|
if (seen.has(slug)) continue;
|
|
125
124
|
const e = projectEntry(cfg, { slug, name: key });
|
|
126
|
-
if (e?.private && via === 'transitive') {
|
|
127
|
-
refused.push(key);
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
125
|
push(slug, true);
|
|
131
|
-
if (e?.links)
|
|
132
|
-
queue.push(...e.links.map((l) => ({ k: l, via: 'transitive' })));
|
|
126
|
+
if (e?.links) queue.push(...e.links.map((l) => ({ k: l })));
|
|
133
127
|
}
|
|
134
128
|
|
|
135
129
|
const lines = [`<agent-memory repo="${home}">`];
|
|
@@ -165,11 +159,6 @@ export function cmdContext(flags) {
|
|
|
165
159
|
lines.push('', '## Recent project memory', recent.join('\n'));
|
|
166
160
|
}
|
|
167
161
|
if (indexLinks.length) lines.push('', '## Index', indexLinks.join('\n'));
|
|
168
|
-
if (refused.length)
|
|
169
|
-
lines.push(
|
|
170
|
-
'',
|
|
171
|
-
`<!-- ${refused.length} linked project(s) omitted by private policy: ${refused.join(', ')} -->`
|
|
172
|
-
);
|
|
173
162
|
if (unknown.length)
|
|
174
163
|
lines.push(
|
|
175
164
|
'',
|
package/package.json
CHANGED
package/test/context.test.mjs
CHANGED
|
@@ -28,7 +28,7 @@ test('context in a repo shows its project memory', () => {
|
|
|
28
28
|
assert.match(r.stdout, /app uses redis cache/);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
test('link attaches
|
|
31
|
+
test('link attaches siblings and private transitively (links are owner curation)', () => {
|
|
32
32
|
const home = tmpdir();
|
|
33
33
|
const base = tmpdir();
|
|
34
34
|
const repoA = initRepo(path.join(base, 'a'), 'https://github.com/acme/a.git');
|
|
@@ -45,13 +45,13 @@ test('link attaches a sibling project transitively; private blocks transitive on
|
|
|
45
45
|
const inB = run(['context'], { amrHome: home, cwd: repoB });
|
|
46
46
|
assert.match(inB.stdout, /a uses sqlite/);
|
|
47
47
|
|
|
48
|
-
// c links to b; a is private ->
|
|
48
|
+
// c links to b; a is private -> still attaches transitively, since
|
|
49
|
+
// the link chain is the owner's own curation
|
|
49
50
|
run(['link', 'b'], { amrHome: home, cwd: repoC });
|
|
50
51
|
run(['policy', 'a', 'private', 'true'], { amrHome: home, cwd: repoA });
|
|
51
52
|
const inC = run(['context'], { amrHome: home, cwd: repoC });
|
|
52
53
|
assert.match(inC.stdout, /b uses kafka/);
|
|
53
|
-
assert.match(inC.stdout, /
|
|
54
|
-
assert.ok(!inC.stdout.includes('a uses sqlite'));
|
|
54
|
+
assert.match(inC.stdout, /a uses sqlite/);
|
|
55
55
|
|
|
56
56
|
// explicit --also still attaches the private project
|
|
57
57
|
const explicit = run(['context', '--also', 'a'], { amrHome: home, cwd: repoC });
|