claude-memory-admin 1.0.0
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/LICENSE +26 -0
- package/README.md +218 -0
- package/bin/claude-memory-admin.mjs +62 -0
- package/package.json +63 -0
- package/public/app.mjs +1156 -0
- package/public/assets/bitspire-logo.webp +0 -0
- package/public/graph.mjs +518 -0
- package/public/index.html +66 -0
- package/public/styles.css +402 -0
- package/server.mjs +239 -0
- package/src/model.mjs +241 -0
- package/src/mutate.mjs +414 -0
- package/src/parse.mjs +255 -0
- package/src/projects.mjs +190 -0
- package/src/search.mjs +127 -0
- package/src/stats.mjs +203 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
=====================
|
|
3
|
+
|
|
4
|
+
Copyright © 2026 Steven Giesel
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person
|
|
7
|
+
obtaining a copy of this software and associated documentation
|
|
8
|
+
files (the “Software”), to deal in the Software without
|
|
9
|
+
restriction, including without limitation the rights to use,
|
|
10
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the
|
|
12
|
+
Software is furnished to do so, subject to the following
|
|
13
|
+
conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be
|
|
16
|
+
included in all copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
|
19
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
20
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
21
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
22
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
23
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
24
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
25
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
26
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# claude-memory-admin
|
|
2
|
+
|
|
3
|
+
[](https://github.com/linkdotnet/claude-memory-admin/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/claude-memory-admin)
|
|
5
|
+
|
|
6
|
+
Browse, audit and prune the **auto memory** Claude Code writes for each project.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install -g claude-memory-admin
|
|
10
|
+
claude-memory-admin
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
It opens `http://localhost:4173` and reads `~/.claude/projects/*/memory/`.
|
|
14
|
+
Nothing leaves your machine; the server binds `127.0.0.1` only.
|
|
15
|
+
|
|
16
|
+
<sub>Brought to you by [BitSpire](https://bitspire.ch/).</sub>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
### Prune: see what MEMORY.md actually costs you
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
### Graph: see how memories link to each other
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## What this is for
|
|
31
|
+
|
|
32
|
+
Claude Code keeps two kinds of memory. This tool is about the second one:
|
|
33
|
+
|
|
34
|
+
| | CLAUDE.md | Auto memory |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| Written by | you | Claude |
|
|
37
|
+
| Lives in | `./CLAUDE.md`, `~/.claude/CLAUDE.md` | `~/.claude/projects/<project>/memory/` |
|
|
38
|
+
| Contains | rules and instructions | learnings Claude picked up |
|
|
39
|
+
|
|
40
|
+
The auto memory directory holds `MEMORY.md` (a concise index) plus one topic
|
|
41
|
+
file per memory, cross-linked with `[[wikilinks]]`.
|
|
42
|
+
|
|
43
|
+
**`MEMORY.md` is loaded at the start of every session, and only the first 200
|
|
44
|
+
lines or 25KB, whichever comes first.** Everything past that cutoff is silently
|
|
45
|
+
dropped. That single fact drives most of this tool: it shows you how close each
|
|
46
|
+
project is to the cliff, and makes it quick to get back under it.
|
|
47
|
+
|
|
48
|
+
## What it does
|
|
49
|
+
|
|
50
|
+
- **Projects by real path.** The directories on disk are slugified cwds
|
|
51
|
+
(`-Users-me-repos-Blog`) and the slugification is lossy. The true path is
|
|
52
|
+
recovered from the `cwd` field in the session transcripts stored next to each
|
|
53
|
+
memory directory; anything that cannot be confirmed is shown as the raw slug
|
|
54
|
+
rather than a plausible guess. `autoMemoryDirectory` in `~/.claude/settings.json`
|
|
55
|
+
is honoured if you have moved the store.
|
|
56
|
+
- **Search across every project**: names, descriptions, bodies and index hooks,
|
|
57
|
+
with snippets and match highlighting. Press `/` to jump to it.
|
|
58
|
+
- **Read each memory** with its frontmatter as structured metadata and
|
|
59
|
+
`[[wikilinks]]` as clickable links. Dead links are struck through in red.
|
|
60
|
+
- **Prune** (see below).
|
|
61
|
+
- **Graph** the wikilinks between memories. Hovering dims everything that is not
|
|
62
|
+
a neighbour, which is the only practical way to read a dense cluster.
|
|
63
|
+
- **Health**: orphans, dangling pointers, broken wikilinks, files linked only
|
|
64
|
+
mid-sentence, `name` fields that disagree with the filename.
|
|
65
|
+
- **Delete with cascade**, always reversible.
|
|
66
|
+
|
|
67
|
+
## Keeping MEMORY.md small
|
|
68
|
+
|
|
69
|
+
The **Prune** tab exists because a bloated index costs tokens on every single
|
|
70
|
+
session and, past the limit, silently stops loading.
|
|
71
|
+
|
|
72
|
+
- **Load meter**. How much of the 200-line / 25KB budget the index uses, and
|
|
73
|
+
which of the two is binding. Frontmatter and HTML comments are excluded,
|
|
74
|
+
because Claude Code strips those before loading.
|
|
75
|
+
- **Long hooks**. The hook is the text after the dash in `MEMORY.md`. A
|
|
76
|
+
400-character hook can cost more than the memory it points at, and shortening
|
|
77
|
+
it is the cheapest win available.
|
|
78
|
+
- **Possible overlap**. Pairs of memories ranked by shared *rare* vocabulary, to
|
|
79
|
+
surface the same lesson saved three times from three sessions. It is a hint,
|
|
80
|
+
not a verdict.
|
|
81
|
+
- **Bulk prune**. Sort by age, size, or inbound links, tick several, delete them
|
|
82
|
+
as one restore point.
|
|
83
|
+
|
|
84
|
+
Anthropic's own guidance for the index: one line per entry, detail in the topic
|
|
85
|
+
files, merge or drop stale entries.
|
|
86
|
+
|
|
87
|
+
## Deleting is reversible
|
|
88
|
+
|
|
89
|
+
Delete shows a preview first: the exact lines that will go, the prose mentions it
|
|
90
|
+
will deliberately leave alone, and which other memories link here and will break.
|
|
91
|
+
Those linking memories each get a checkbox, tick any you want removed in the same
|
|
92
|
+
operation, and they are trashed and restored as a single step.
|
|
93
|
+
|
|
94
|
+
**Delete all memory** clears one project: `MEMORY.md` and every memory file, as
|
|
95
|
+
one restore point. Session transcripts (`*.jsonl`) and the project folder are
|
|
96
|
+
never touched, only the contents of `memory/`.
|
|
97
|
+
|
|
98
|
+
**Remove link** in the Health tab clears a `[[wikilink]]` whose target no longer
|
|
99
|
+
exists. The markup goes and the words stay, so `see [[gone]] for details` becomes
|
|
100
|
+
`see gone for details`.
|
|
101
|
+
|
|
102
|
+
Everything lands in `memory/.trash/` with a restore record and comes back from the
|
|
103
|
+
Trash tab. The app never creates memories and never rewrites their prose beyond
|
|
104
|
+
clearing a dead link's brackets.
|
|
105
|
+
|
|
106
|
+
## Usage
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
claude-memory-admin [options]
|
|
110
|
+
|
|
111
|
+
-p, --port <n> port to listen on (default 4173)
|
|
112
|
+
-r, --root <dir> memory store to read (default ~/.claude/projects)
|
|
113
|
+
--no-open do not launch a browser
|
|
114
|
+
-h, --help show help
|
|
115
|
+
-v, --version print the version
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Point it at a copy if you want to experiment safely:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
cp -R ~/.claude/projects /tmp/memory-snapshot
|
|
122
|
+
claude-memory-admin --root /tmp/memory-snapshot
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Safety
|
|
126
|
+
|
|
127
|
+
- Binds `127.0.0.1`; no telemetry, no network calls.
|
|
128
|
+
- Every write target must resolve to a plain `.md` file inside that project's own
|
|
129
|
+
`memory/` directory. `..`, absolute paths and subdirectories are refused.
|
|
130
|
+
- `MEMORY.md` is replaced atomically (temp file, `fsync`, `rename`) with a backup
|
|
131
|
+
restored if anything throws.
|
|
132
|
+
- A test asserts that parsing and rewriting every real `MEMORY.md` with no
|
|
133
|
+
deletions reproduces it byte for byte. Real indexes are hand-written prose with
|
|
134
|
+
headings and nested bullets, and mangling one would be silent.
|
|
135
|
+
|
|
136
|
+
## Development
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
git clone https://github.com/linkdotnet/claude-memory-admin
|
|
140
|
+
cd claude-memory-admin
|
|
141
|
+
npm install
|
|
142
|
+
npm start # or: node server.mjs
|
|
143
|
+
npm test # runs on a throwaway copy of your real store
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
No bundler and no build step: the backend is `node:http` plus `node:fs`, and the
|
|
147
|
+
frontend is plain ES modules the browser loads directly. Two runtime dependencies,
|
|
148
|
+
`marked` and `dompurify`, both only for rendering memory bodies safely.
|
|
149
|
+
|
|
150
|
+
| Path | Purpose |
|
|
151
|
+
| --- | --- |
|
|
152
|
+
| `bin/claude-memory-admin.mjs` | CLI entry point and argument parsing |
|
|
153
|
+
| `server.mjs` | HTTP server: static files + JSON API |
|
|
154
|
+
| `src/projects.mjs` | Project discovery, slug → real path resolution |
|
|
155
|
+
| `src/parse.mjs` | `MEMORY.md` and frontmatter parsers, wikilinks |
|
|
156
|
+
| `src/model.mjs` | Joins index, files, graph and health into one model |
|
|
157
|
+
| `src/stats.mjs` | Load-limit accounting and overlap detection |
|
|
158
|
+
| `src/search.mjs` | Full-text search across projects |
|
|
159
|
+
| `src/mutate.mjs` | Delete / restore / unlink, the only code that writes |
|
|
160
|
+
| `public/` | Frontend |
|
|
161
|
+
|
|
162
|
+
Tests run against a committed fixture store under `test/fixtures/store/`, which
|
|
163
|
+
encodes the awkward shapes real memory directories contain. Your own
|
|
164
|
+
`~/.claude/projects` is additionally checked when it exists, always on a
|
|
165
|
+
throwaway copy.
|
|
166
|
+
|
|
167
|
+
### Releasing
|
|
168
|
+
|
|
169
|
+
Publishing runs in CI. You do not bump anything by hand.
|
|
170
|
+
|
|
171
|
+
Go to **Actions, Release, Run workflow**, pick `patch`, `minor` or `major`, and
|
|
172
|
+
run it. The workflow tests, bumps `package.json` and `package-lock.json`, commits
|
|
173
|
+
and tags the bump, pushes both, then publishes to npm.
|
|
174
|
+
|
|
175
|
+
If you would rather cut the version locally, that still works:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
npm version patch
|
|
179
|
+
git push --follow-tags
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Pushing a `v*` tag publishes whatever is in `package.json`, after checking the
|
|
183
|
+
two agree. Either route refuses to publish a version that is already on npm,
|
|
184
|
+
because npm never allows one to be replaced.
|
|
185
|
+
|
|
186
|
+
It needs one repository secret:
|
|
187
|
+
|
|
188
|
+
| Secret | Value |
|
|
189
|
+
| --- | --- |
|
|
190
|
+
| `NPM_TOKEN` | an npm **Automation** access token with publish rights |
|
|
191
|
+
|
|
192
|
+
Add it under *Settings, Secrets and variables, Actions, New repository secret*.
|
|
193
|
+
An Automation token is the right kind because it bypasses 2FA, which an
|
|
194
|
+
unattended workflow cannot satisfy.
|
|
195
|
+
|
|
196
|
+
Releases are published with [npm provenance](https://docs.npmjs.com/generating-provenance-statements),
|
|
197
|
+
so every version carries a signed, verifiable record of the workflow run and
|
|
198
|
+
commit that built it. That needs the `id-token: write` permission the workflow
|
|
199
|
+
already requests, a public repository, and the `repository` field in
|
|
200
|
+
`package.json` pointing at this repo.
|
|
201
|
+
|
|
202
|
+
Two things that will bite if they apply to you: the workflow pushes the bump
|
|
203
|
+
commit to the default branch, so a branch protection rule that blocks pushes
|
|
204
|
+
will stop it; and the tag it pushes uses `GITHUB_TOKEN`, which by design does
|
|
205
|
+
not trigger other workflows, so there is no double publish.
|
|
206
|
+
|
|
207
|
+
### Screenshots and demo data
|
|
208
|
+
|
|
209
|
+
The screenshots come from an invented store, never a real one:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
node scripts/demo-store.mjs /tmp/demo-store
|
|
213
|
+
npm start -- --root /tmp/demo-store
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// CLI entry point for the globally installed tool.
|
|
3
|
+
|
|
4
|
+
import { startServer } from '../server.mjs';
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
|
|
9
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const pkg = JSON.parse(readFileSync(path.join(here, '..', 'package.json'), 'utf8'));
|
|
11
|
+
|
|
12
|
+
const HELP = `
|
|
13
|
+
claude-memory-admin - browse and prune Claude Code's auto memory
|
|
14
|
+
|
|
15
|
+
Usage
|
|
16
|
+
claude-memory-admin [options]
|
|
17
|
+
|
|
18
|
+
Options
|
|
19
|
+
-p, --port <n> port to listen on (default 4173)
|
|
20
|
+
-r, --root <dir> memory store to read (default ~/.claude/projects,
|
|
21
|
+
or autoMemoryDirectory from ~/.claude/settings.json)
|
|
22
|
+
--no-open do not launch a browser
|
|
23
|
+
-h, --help show this help
|
|
24
|
+
-v, --version print the version
|
|
25
|
+
|
|
26
|
+
The store is read from disk on every request. The only writes are deletes,
|
|
27
|
+
restores, and clearing a broken link - each one reversible from the Trash tab.
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
function parseArgs(argv) {
|
|
31
|
+
const options = { port: undefined, root: undefined, open: true };
|
|
32
|
+
for (let i = 0; i < argv.length; i++) {
|
|
33
|
+
const arg = argv[i];
|
|
34
|
+
const next = () => {
|
|
35
|
+
const value = argv[++i];
|
|
36
|
+
if (value === undefined) {
|
|
37
|
+
console.error(`Missing value for ${arg}`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
};
|
|
42
|
+
switch (arg) {
|
|
43
|
+
case '-h': case '--help': console.log(HELP); process.exit(0); break;
|
|
44
|
+
case '-v': case '--version': console.log(pkg.version); process.exit(0); break;
|
|
45
|
+
case '-p': case '--port': options.port = Number(next()); break;
|
|
46
|
+
case '-r': case '--root': options.root = next(); break;
|
|
47
|
+
case '--no-open': options.open = false; break;
|
|
48
|
+
default:
|
|
49
|
+
if (arg.startsWith('-')) {
|
|
50
|
+
console.error(`Unknown option: ${arg}\n${HELP}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (options.port !== undefined && (!Number.isInteger(options.port) || options.port < 1 || options.port > 65535)) {
|
|
56
|
+
console.error(`Invalid port: ${options.port}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
return options;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
startServer(parseArgs(process.argv.slice(2)));
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-memory-admin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Browse, audit and prune the auto memory Claude Code keeps under ~/.claude/projects",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude",
|
|
7
|
+
"claude-code",
|
|
8
|
+
"anthropic",
|
|
9
|
+
"claude-memory",
|
|
10
|
+
"auto-memory",
|
|
11
|
+
"memory",
|
|
12
|
+
"memory-md",
|
|
13
|
+
"claude-md",
|
|
14
|
+
"cleanup",
|
|
15
|
+
"prune",
|
|
16
|
+
"declutter",
|
|
17
|
+
"audit",
|
|
18
|
+
"context-window",
|
|
19
|
+
"token-budget",
|
|
20
|
+
"knowledge-base",
|
|
21
|
+
"wikilinks",
|
|
22
|
+
"markdown",
|
|
23
|
+
"cli",
|
|
24
|
+
"local-first",
|
|
25
|
+
"developer-tools"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"author": "Steven Giesel",
|
|
29
|
+
"homepage": "https://github.com/linkdotnet/claude-memory-admin#readme",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/linkdotnet/claude-memory-admin.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/linkdotnet/claude-memory-admin/issues"
|
|
36
|
+
},
|
|
37
|
+
"type": "module",
|
|
38
|
+
"bin": {
|
|
39
|
+
"claude-memory-admin": "bin/claude-memory-admin.mjs"
|
|
40
|
+
},
|
|
41
|
+
"exports": {
|
|
42
|
+
".": "./server.mjs"
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"bin/",
|
|
46
|
+
"src/",
|
|
47
|
+
"public/",
|
|
48
|
+
"server.mjs",
|
|
49
|
+
"README.md",
|
|
50
|
+
"LICENSE"
|
|
51
|
+
],
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=20.6.0"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"start": "node bin/claude-memory-admin.mjs",
|
|
57
|
+
"test": "node --test test/*.test.mjs"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"dompurify": "^3.2.7",
|
|
61
|
+
"marked": "^16.4.0"
|
|
62
|
+
}
|
|
63
|
+
}
|