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 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` policy keeps a project out of transitive auto-attach and the pooled digest; `noglobal` keeps it out of the pooled digest only.
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
- // private blocks reachability from repos that never linked it:
95
- // direct links (siblings) and explicit --also attach fine; it only
96
- // refuses when reached transitively through another project's links.
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, via: 'entry' }));
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, via: 'also' }))
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, via } = queue.shift();
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucid-mem",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "lucid-mem (amr) - autonomous git-backed agent memory (Agent Memory Repo spec)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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 a sibling project transitively; private blocks transitive only', () => {
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 -> transitive refusal in c
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, /omitted by private policy/);
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 });