@vib795/agent-memory 0.5.2 → 0.6.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 +57 -15
- package/package.json +1 -1
- package/skills/recall/SKILL.md +1 -1
- package/src/cli.js +4 -1
- package/src/compact.js +43 -2
- package/src/digest.js +8 -1
- package/src/promptfile.js +1 -1
- package/src/store.js +0 -0
package/README.md
CHANGED
|
@@ -1,17 +1,45 @@
|
|
|
1
1
|
# agent-memory
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
**Durable memory for Claude Code and GitHub Copilot — that survives an IT security review.**
|
|
4
|
+
|
|
5
|
+
Your agent forgets everything between windows. Most tools that fix that cannot be
|
|
6
|
+
installed where you actually work.
|
|
7
|
+
|
|
8
|
+
This one has **zero runtime dependencies, zero dev dependencies, and no install
|
|
9
|
+
script**. Nothing runs when you install it; granting it your agents is a second,
|
|
10
|
+
separate command. It has been [independently scanned](#independently-scanned) and
|
|
11
|
+
passed. `npm ls -g --depth 0` shows nothing underneath it, because there is nothing
|
|
12
|
+
underneath it.
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
repository, and the week — using nothing an IT security review would need to approve.
|
|
14
|
+
## Install
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
Two commands, and the second one is not optional — it creates the store and installs
|
|
17
|
+
into every agent on the machine, in whatever format that tool reads:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g @vib795/agent-memory
|
|
21
|
+
agent-memory setup
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Node >= 22.5, and nothing else.
|
|
25
|
+
|
|
26
|
+
In Claude Code you can take the three skills as a plugin instead of letting `setup`
|
|
27
|
+
link them:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
/plugin marketplace add vib795/agent-memory
|
|
31
|
+
/plugin install agent-memory@vib795
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The plugin carries the skills; `npm` carries the store. You want both — the skills
|
|
35
|
+
will tell you so if you only have one.
|
|
36
|
+
|
|
37
|
+
Behind a proxy that quarantines npm, or want the GitHub Packages mirror?
|
|
38
|
+
[Every install path is here](#install-1).
|
|
39
|
+
|
|
40
|
+
## What you get
|
|
41
|
+
|
|
42
|
+
Three user-level Agent Skills over one local markdown store:
|
|
15
43
|
|
|
16
44
|
| Skill | What it does |
|
|
17
45
|
|---|---|
|
|
@@ -19,9 +47,23 @@ Three user-level Agent Skills over one local store:
|
|
|
19
47
|
| `/remember` | Captures durable knowledge into a cross-repo graph — and fires on its own when a juncture passes |
|
|
20
48
|
| `/recall` | Answers from that graph before deriving anything again |
|
|
21
49
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
50
|
+
The store is plain markdown in `~/.agents/memory/`, outside every repository — which
|
|
51
|
+
is what lets a note written in one project be read from another. Nothing leaves the
|
|
52
|
+
machine: no daemon, no scheduled task, no telemetry.
|
|
53
|
+
|
|
54
|
+
**New here?** [HOWTO.md](HOWTO.md) — install, first five minutes, and the per-tool
|
|
55
|
+
notes for Claude Code, Codex and Copilot.
|
|
56
|
+
|
|
57
|
+
**How does it work?** [ARCHITECTURE.md](ARCHITECTURE.md) — the source-of-truth split,
|
|
58
|
+
the write and read paths, the tiered context cost, and the invariants underneath.
|
|
59
|
+
|
|
60
|
+
[](https://www.npmjs.com/package/@vib795/agent-memory)
|
|
61
|
+
[](https://github.com/vib795/agent-memory/actions/workflows/test.yml)
|
|
62
|
+
[](https://github.com/vib795/agent-memory/actions/workflows/release.yml)
|
|
63
|
+
[](https://www.npmjs.com/package/@vib795/agent-memory)
|
|
64
|
+
[](https://nodejs.org)
|
|
65
|
+
[](package.json)
|
|
66
|
+
[](LICENSE)
|
|
25
67
|
|
|
26
68
|
## Why this exists
|
|
27
69
|
|
|
@@ -472,7 +514,7 @@ Claude Code can take the same three as a plugin:
|
|
|
472
514
|
|
|
473
515
|
```
|
|
474
516
|
/plugin marketplace add vib795/agent-memory
|
|
475
|
-
/plugin install agent-memory@
|
|
517
|
+
/plugin install agent-memory@vib795
|
|
476
518
|
```
|
|
477
519
|
|
|
478
520
|
The same caveat applies: the plugin carries the skills, `npm` carries the store.
|
|
@@ -499,7 +541,7 @@ is generated state, and the committed value is only a placeholder.
|
|
|
499
541
|
|
|
500
542
|
Needs Node 22.5 or newer; `doctor` says so plainly if the version is too old.
|
|
501
543
|
|
|
502
|
-
Run `npm test` for the suite (
|
|
544
|
+
Run `npm test` for the suite (91 tests, no dependencies). CI runs it on Linux,
|
|
503
545
|
macOS and Windows across Node 22 and 24, and separately installs the packed tarball
|
|
504
546
|
and exercises it end to end on all three.
|
|
505
547
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vib795/agent-memory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Durable cross-repo knowledge graph for GitHub Copilot and Claude Code. Markdown source of truth, disposable SQLite index, zero runtime dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"github-copilot",
|
package/skills/recall/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: recall
|
|
3
3
|
version: 0.1.0
|
|
4
|
-
description: "Durable project knowledge
|
|
4
|
+
description: "Durable project knowledge store, currently empty. Run /remember to capture the first note. Use when you need to know how a system works, why a decision was made, what convention applies, or what the environment forbids."
|
|
5
5
|
license: MIT
|
|
6
6
|
allowed-tools: Bash Read
|
|
7
7
|
triggers:
|
package/src/cli.js
CHANGED
|
@@ -75,7 +75,7 @@ function git(args, cwd = process.cwd()) {
|
|
|
75
75
|
const USAGE = `agent-memory — durable cross-repo knowledge for coding agents
|
|
76
76
|
|
|
77
77
|
setup link all three skills, build the store
|
|
78
|
-
(
|
|
78
|
+
(run this once, by hand, after install)
|
|
79
79
|
uninstall remove the skill links; keeps every note
|
|
80
80
|
init [--skills "<p1>,<p2>"] create the store; register skill files
|
|
81
81
|
index rebuild index.db from notes/
|
|
@@ -424,6 +424,9 @@ function cmdCompact() {
|
|
|
424
424
|
...r.malformed.map((m) => `warning: unparseable ${m.path}`),
|
|
425
425
|
`digest ${r.digestChars} chars`,
|
|
426
426
|
...r.skills.map((s) => `updated description in ${s}`),
|
|
427
|
+
...(r.skipped || []).map(
|
|
428
|
+
(s) => `skipped ${s}: inside this package's git checkout, so the file is tracked`
|
|
429
|
+
),
|
|
427
430
|
].join('\n');
|
|
428
431
|
return { ok: true, ...r, text };
|
|
429
432
|
}
|
package/src/compact.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
1
|
+
import { readFileSync, existsSync, realpathSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { join, sep } from 'node:path';
|
|
2
4
|
import { loadConfig, paths } from './config.js';
|
|
3
5
|
import { listNotes, archiveNote, contentHash, nowIso, serializeNote } from './store.js';
|
|
4
6
|
import { atomicWrite } from './atomic.js';
|
|
@@ -149,8 +151,40 @@ function decay(active, cfg, now) {
|
|
|
149
151
|
* reading its description, so regenerating that line is how the store advertises
|
|
150
152
|
* what it now knows without costing anything at chat time.
|
|
151
153
|
*/
|
|
154
|
+
// The directory this package was installed into. `setup` links skill directories at
|
|
155
|
+
// `<package>/skills/<name>`, so every description write lands in the package's own
|
|
156
|
+
// files. That is correct for an installed package and wrong for a git checkout, where
|
|
157
|
+
// those files are tracked: one developer's digest gets committed and then published to
|
|
158
|
+
// everyone. It shipped that way for twenty releases, advertising one machine's five
|
|
159
|
+
// notes to every user who installed the plugin.
|
|
160
|
+
const PACKAGE_ROOT = fileURLToPath(new URL('..', import.meta.url));
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Is this path inside the package's own git checkout?
|
|
164
|
+
*
|
|
165
|
+
* Resolved through `realpathSync` because the path arrives as a symlink planted by
|
|
166
|
+
* `setup`; the link sits outside the checkout even when its target is inside it, which
|
|
167
|
+
* is the whole reason this went unnoticed. The `.git` test separates a developer's
|
|
168
|
+
* working tree from an ordinary install, which has no `.git` and must keep being
|
|
169
|
+
* written to — that write is the Tier-1 mechanism, not a bug.
|
|
170
|
+
*/
|
|
171
|
+
export function insideCheckout(file) {
|
|
172
|
+
if (!existsSync(join(PACKAGE_ROOT, '.git'))) return false;
|
|
173
|
+
let real;
|
|
174
|
+
let root;
|
|
175
|
+
try {
|
|
176
|
+
real = realpathSync(file);
|
|
177
|
+
root = realpathSync(PACKAGE_ROOT);
|
|
178
|
+
} catch {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
if (root.endsWith(sep)) root = root.slice(0, -1);
|
|
182
|
+
return real === root || real.startsWith(root + sep);
|
|
183
|
+
}
|
|
184
|
+
|
|
152
185
|
export function writeSkillDescription(skillPath, description) {
|
|
153
186
|
if (!existsSync(skillPath)) return false;
|
|
187
|
+
if (insideCheckout(skillPath)) return false;
|
|
154
188
|
const src = readFileSync(skillPath, 'utf8');
|
|
155
189
|
if (!src.startsWith('---')) return false;
|
|
156
190
|
const end = src.indexOf('\n---', 3);
|
|
@@ -185,10 +219,17 @@ function regenerate(db, cfg) {
|
|
|
185
219
|
atomicWrite(paths.routing, routing);
|
|
186
220
|
|
|
187
221
|
const skills = [];
|
|
222
|
+
const skipped = [];
|
|
188
223
|
for (const p of cfg.skillPaths || []) {
|
|
224
|
+
// Reported, never swallowed. A description that silently did not update reads
|
|
225
|
+
// exactly like one that did, and this is the line that routes every recall.
|
|
226
|
+
if (insideCheckout(p)) {
|
|
227
|
+
skipped.push(p);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
189
230
|
if (writeSkillDescription(p, digest)) skills.push(p);
|
|
190
231
|
}
|
|
191
|
-
return { digest, digestChars: digest.length, routing: paths.routing, skills };
|
|
232
|
+
return { digest, digestChars: digest.length, routing: paths.routing, skills, skipped };
|
|
192
233
|
}
|
|
193
234
|
|
|
194
235
|
/**
|
package/src/digest.js
CHANGED
|
@@ -59,7 +59,14 @@ export function buildDigest(db, { cfg = loadConfig() } = {}) {
|
|
|
59
59
|
const cap = cfg.digestChars;
|
|
60
60
|
const total = db.prepare('SELECT COUNT(*) AS c FROM nodes WHERE archived = 0').get().c;
|
|
61
61
|
if (!total) {
|
|
62
|
-
|
|
62
|
+
// USE_WHEN belongs here too. This branch used to return without it, which stripped
|
|
63
|
+
// the routing triggers from Tier 1 on exactly the machines that need them most: a
|
|
64
|
+
// fresh install, where the store is empty and the skill still has to earn its
|
|
65
|
+
// first use. An empty store is a reason to say when to call recall, not to stop.
|
|
66
|
+
return (
|
|
67
|
+
'Durable project knowledge store, currently empty. ' +
|
|
68
|
+
`Run /remember to capture the first note. ${USE_WHEN}`
|
|
69
|
+
);
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
const constraints = db
|
package/src/promptfile.js
CHANGED
|
@@ -15,7 +15,7 @@ export const GENERATED_MARKER =
|
|
|
15
15
|
|
|
16
16
|
/** Split a leading `---` frontmatter block from the body. */
|
|
17
17
|
function splitFrontmatter(text) {
|
|
18
|
-
const src = String(text).replace(
|
|
18
|
+
const src = String(text).replace(/^\uFEFF/, '').replace(/\r\n/g, '\n');
|
|
19
19
|
if (!src.startsWith('---')) return { head: '', body: src.trim() };
|
|
20
20
|
const end = src.indexOf('\n---', 3);
|
|
21
21
|
if (end === -1) return { head: '', body: src.trim() };
|
package/src/store.js
CHANGED
|
Binary file
|