@saleem11kh/repomem 0.3.1 → 0.4.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/README.md +322 -72
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/store/file-store.d.ts +1 -1
- package/dist/store/file-store.d.ts.map +1 -1
- package/dist/store/file-store.js +29 -4
- package/dist/store/file-store.js.map +1 -1
- package/dist/store/git.d.ts +22 -0
- package/dist/store/git.d.ts.map +1 -0
- package/dist/store/git.js +69 -0
- package/dist/store/git.js.map +1 -0
- package/dist/tools/mem-context.d.ts.map +1 -1
- package/dist/tools/mem-context.js +67 -6
- package/dist/tools/mem-context.js.map +1 -1
- package/dist/tools/mem-handoff.d.ts.map +1 -1
- package/dist/tools/mem-handoff.js +46 -5
- package/dist/tools/mem-handoff.js.map +1 -1
- package/dist/tools/mem-save.d.ts +5 -0
- package/dist/tools/mem-save.d.ts.map +1 -1
- package/dist/tools/mem-save.js +60 -2
- package/dist/tools/mem-save.js.map +1 -1
- package/dist/tools/session.d.ts +40 -0
- package/dist/tools/session.d.ts.map +1 -0
- package/dist/tools/session.js +159 -0
- package/dist/tools/session.js.map +1 -0
- package/dist/tools/util.d.ts +2 -0
- package/dist/tools/util.d.ts.map +1 -1
- package/dist/tools/util.js +7 -0
- package/dist/tools/util.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,7 +37,8 @@ your-project/
|
|
|
37
37
|
├── decisions/ ← architectural choices + why
|
|
38
38
|
├── sessions/ ← what was done, what's next
|
|
39
39
|
├── patterns/ ← reusable conventions for this codebase
|
|
40
|
-
|
|
40
|
+
├── issues/ ← known gotchas, do-not-repeat mistakes
|
|
41
|
+
└── REPOMEM.md ← auto-generated index of everything above
|
|
41
42
|
```
|
|
42
43
|
|
|
43
44
|
Plain markdown files. No database. No cloud. No vendor lock-in.
|
|
@@ -48,63 +49,277 @@ Switch agents → same memory, because it's in the repo, not the tool.
|
|
|
48
49
|
|
|
49
50
|
---
|
|
50
51
|
|
|
51
|
-
##
|
|
52
|
+
## Quick start
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
```bash
|
|
55
|
+
# 1. Install
|
|
56
|
+
npm install -g @saleem11kh/repomem
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
decisions, patterns, and issues travel together. Already have a context-rich repo? Run
|
|
74
|
-
`mem_prime` once to seed memory from your existing `CLAUDE.md` and `docs/`.
|
|
58
|
+
# 2. Scaffold .repomem/ in your project
|
|
59
|
+
cd your-project
|
|
60
|
+
repomem init
|
|
61
|
+
|
|
62
|
+
# 3. Wire it to your agent
|
|
63
|
+
repomem setup claude-code # or: cursor | gemini | codex
|
|
64
|
+
|
|
65
|
+
# 4. Restart the agent, then approve the server when prompted
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Step 4 is not optional.** MCP servers are loaded when the agent starts, so a server
|
|
69
|
+
wired mid-session does not exist in that session. Claude Code additionally requires
|
|
70
|
+
you to approve any server declared in a project's `.mcp.json` the first time you
|
|
71
|
+
launch with it.
|
|
72
|
+
|
|
73
|
+
Verify with `/mcp` — you should see `repomem` connected with six tools. There are no
|
|
74
|
+
slash commands to look for; MCP servers contribute **tools**, which never appear in
|
|
75
|
+
the `/` menu.
|
|
75
76
|
|
|
76
77
|
---
|
|
77
78
|
|
|
78
|
-
##
|
|
79
|
+
## Usage
|
|
80
|
+
|
|
81
|
+
### The six tools
|
|
82
|
+
|
|
83
|
+
Your agent calls these; you don't type them.
|
|
84
|
+
|
|
85
|
+
| Tool | Arguments | What it does |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| `mem_context` | `brief?` | Session-start packet: the last session, plus one-line summaries of decisions, patterns, and issues. Call it first. |
|
|
88
|
+
| `mem_get` | `file` | Expand one entry in full — by `type/filename`, bare filename, or `[[wikilink]]` slug. |
|
|
89
|
+
| `mem_search` | `query`, `linked?` | BM25 + recency ranked search across all memory. `linked=true` also searches linked repos and the workspace. |
|
|
90
|
+
| `mem_save` | `type`, `title`, `content`, `summary?`, `tags?`, `links?`, `supersedes?`, `session?` | Write a `decision`, `pattern`, `issue`, or `session` note. |
|
|
91
|
+
| `mem_handoff` | `summary`, `done?`, `next?`, `blockers?`, `session?`, `git?` | Close out a session so the next one picks up exactly where you left off. Fills in what changed from git. |
|
|
92
|
+
| `mem_prime` | — | Bootstrap memory on an existing repo from its `CLAUDE.md` / `README.md` / `docs/`. Run once when adopting. |
|
|
93
|
+
|
|
94
|
+
### Progressive disclosure — why context stays small
|
|
95
|
+
|
|
96
|
+
`mem_context` deliberately returns **one-line summaries**, not full bodies. It runs at
|
|
97
|
+
the start of every session; if it dumped everything, a repo with 50 memories would
|
|
98
|
+
burn thousands of tokens before your first message.
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
## Recent decisions
|
|
102
|
+
- BM25 + recency ranking instead of embeddings — Search is BM25 over raw markdown
|
|
103
|
+
with an exponential recency boost — no embeddings, zero extra deps.
|
|
104
|
+
(decisions/2026-07-26-bm25-recency-ranking-instead-of-embeddings.md)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The agent expands only what it needs with `mem_get`. This is why **every entry should
|
|
108
|
+
have a `summary`** — it's the only thing most future sessions will ever read.
|
|
109
|
+
|
|
110
|
+
### A normal session
|
|
111
|
+
|
|
112
|
+
**At the start**, the agent calls `mem_context` and immediately knows what was worked
|
|
113
|
+
on last, the key decisions and why, the conventions for this codebase, and known
|
|
114
|
+
gotchas.
|
|
115
|
+
|
|
116
|
+
**During work**, you capture things as they happen:
|
|
117
|
+
|
|
118
|
+
> "Save that as a decision — include why we rejected the alternative."
|
|
119
|
+
|
|
120
|
+
**At the end:**
|
|
121
|
+
|
|
122
|
+
> "Run mem_handoff."
|
|
123
|
+
|
|
124
|
+
Then commit. Nothing is shared with your team until you do:
|
|
79
125
|
|
|
80
126
|
```bash
|
|
81
|
-
|
|
82
|
-
|
|
127
|
+
git add .repomem/ && git commit -m "chore: update memory"
|
|
128
|
+
```
|
|
83
129
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
repomem
|
|
130
|
+
### What to say
|
|
131
|
+
|
|
132
|
+
repomem has no slash commands. You talk to your agent normally; it picks the tool.
|
|
87
133
|
|
|
88
|
-
|
|
134
|
+
| You want | Say |
|
|
135
|
+
|---|---|
|
|
136
|
+
| Load context | *"What's in repomem for this project?"* |
|
|
137
|
+
| Capture a choice | *"Save that as a decision, with why we rejected X"* |
|
|
138
|
+
| Capture a convention | *"Save that as a pattern"* |
|
|
139
|
+
| Capture a gotcha | *"Save that as an issue, with the guard against it"* |
|
|
140
|
+
| Replace a stale decision | *"Save this as a decision that supersedes `<filename>`"* |
|
|
141
|
+
| Sweep the whole session | *"Update repomem with what we did — skip anything obvious from the code"* |
|
|
142
|
+
| Close out | *"Run mem_handoff"* |
|
|
143
|
+
| Recall | *"Search repomem for how we handle retries"* |
|
|
144
|
+
|
|
145
|
+
Adding **"skip anything obvious from the code"** matters — otherwise you accumulate
|
|
146
|
+
entries restating function signatures, and the signal-to-noise ratio that makes memory
|
|
147
|
+
worth loading degrades.
|
|
148
|
+
|
|
149
|
+
### Adopting on an existing project
|
|
150
|
+
|
|
151
|
+
`repomem init` gives you empty folders. To seed them from a repo that already has
|
|
152
|
+
years of context:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
cd your-existing-project
|
|
156
|
+
repomem init
|
|
89
157
|
repomem setup claude-code
|
|
158
|
+
# restart the agent
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Then, in the agent:
|
|
162
|
+
|
|
163
|
+
> "Call `mem_prime`, then save what it surfaces as decisions, patterns, and issues."
|
|
164
|
+
|
|
165
|
+
`mem_prime` gathers `CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, `README.md`, and
|
|
166
|
+
`docs/**.md` (up to 12 files) and returns them with instructions to distil them into
|
|
167
|
+
memory. **It writes nothing itself** — it needs an agent in the loop. Re-running it is
|
|
168
|
+
safe: it reads the existing count first and is told not to duplicate.
|
|
169
|
+
|
|
170
|
+
If your project has no docs, the higher-value move is to point the agent at the code:
|
|
171
|
+
|
|
172
|
+
> "Read the source under `src/`, then save what you learned as decisions, patterns,
|
|
173
|
+
> and issues. Give every entry a one-line summary and cross-link related entries."
|
|
174
|
+
|
|
175
|
+
Aim for 5–12 entries. Fewer and `mem_context` says nothing useful; many more in one
|
|
176
|
+
sitting and quality drops.
|
|
177
|
+
|
|
178
|
+
### Handoffs are half-derived
|
|
179
|
+
|
|
180
|
+
`mem_handoff` does not rely on the agent remembering what it touched. Since the
|
|
181
|
+
session knows when it started, repomem asks git directly:
|
|
182
|
+
|
|
183
|
+
```markdown
|
|
184
|
+
## 2026-07-26 14:30 — Handoff
|
|
185
|
+
|
|
186
|
+
Reworked how sessions are identified.
|
|
187
|
+
|
|
188
|
+
_branch: master_
|
|
189
|
+
|
|
190
|
+
**Committed this session:**
|
|
191
|
+
- 1038eb6 fix(cli): route MCP config through cmd.exe on Windows
|
|
192
|
+
- ce365ab chore(repomem): record the Windows MCP spawn gotcha
|
|
90
193
|
|
|
91
|
-
|
|
92
|
-
|
|
194
|
+
**Still uncommitted:**
|
|
195
|
+
- M src/tools/mem-context.ts
|
|
196
|
+
- ?? src/store/git.ts
|
|
93
197
|
|
|
94
|
-
|
|
95
|
-
|
|
198
|
+
**Next:**
|
|
199
|
+
- push the tag
|
|
96
200
|
```
|
|
97
201
|
|
|
98
|
-
|
|
202
|
+
The agent supplies only what git cannot know — *why*, and *what's next*. Commits and
|
|
203
|
+
uncommitted files are facts, and deriving them is both cheaper and more reliable than
|
|
204
|
+
asking a model to recall them.
|
|
99
205
|
|
|
206
|
+
Notes:
|
|
207
|
+
- Changes under `.repomem/` are filtered out; the handoff is writing there as it runs.
|
|
208
|
+
- Long lists are capped at 20 with an explicit `…and N more`, never silently truncated.
|
|
209
|
+
- Non-git projects, missing `git`, and empty repos all degrade quietly to a handoff
|
|
210
|
+
with no git detail. Pass `git: false` to skip it deliberately.
|
|
211
|
+
- Commits are matched by *time*, so parallel sessions each report the same commits.
|
|
212
|
+
Time is the only signal available without asking agents to tag their own work.
|
|
213
|
+
|
|
214
|
+
### Sessions and parallel agents
|
|
215
|
+
|
|
216
|
+
Each session owns one file:
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
.repomem/sessions/2026-07-26-0917-windows-mcp-fix.md
|
|
220
|
+
.repomem/sessions/2026-07-26-1430-auth-refactor.md
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`YYYY-MM-DD-HHMM-<name>.md`, where the timestamp is when the session *started* — so it
|
|
224
|
+
stays put as the session appends, and because it's zero-padded, filename order is
|
|
225
|
+
chronological order.
|
|
226
|
+
|
|
227
|
+
Name a session by passing `session` to `mem_save` or `mem_handoff` (*"run mem_handoff,
|
|
228
|
+
call this session auth-refactor"*). Unnamed sessions are `untitled`; naming one later
|
|
229
|
+
renames the file in place, so one session always maps to one file.
|
|
230
|
+
|
|
231
|
+
This matters most when **more than one session runs at once** — two terminals, two
|
|
232
|
+
agents, or two teammates:
|
|
233
|
+
|
|
234
|
+
- Separate files mean no interleaving and **no git merge conflicts**
|
|
235
|
+
- Sessions are linkable: a decision can point at `[[auth-refactor]]`
|
|
236
|
+
- `mem_context` inlines the newest session and lists the others under **Also today**,
|
|
237
|
+
so parallel work is visible without being loaded in full
|
|
238
|
+
- The connecting agent is recorded automatically (`agent: claude-code`), taken from the
|
|
239
|
+
MCP handshake — so parallel sessions from different tools are tellable apart
|
|
240
|
+
|
|
241
|
+
### Anatomy of a memory file
|
|
242
|
+
|
|
243
|
+
```markdown
|
|
100
244
|
---
|
|
245
|
+
date: 2026-07-26
|
|
246
|
+
summary: Search is BM25 over raw markdown with a recency boost — no embeddings.
|
|
247
|
+
tags: [search, ranking]
|
|
248
|
+
---
|
|
249
|
+
# BM25 + recency ranking instead of embeddings
|
|
101
250
|
|
|
102
|
-
|
|
251
|
+
Why not embeddings: they add a model dependency and a cache to invalidate, which
|
|
252
|
+
breaks the "plain files, no infra" promise.
|
|
103
253
|
|
|
104
|
-
|
|
254
|
+
Related: [[memory-lives-in-the-repo-as-plain-markdown]]
|
|
255
|
+
```
|
|
105
256
|
|
|
106
|
-
|
|
107
|
-
|
|
257
|
+
- **`summary`** — the one line `mem_context` and `mem_search` show. Always set it.
|
|
258
|
+
- **`tags`** — free-form, for retrieval.
|
|
259
|
+
- **`supersedes`** — on a decision, the filename it replaces. Keeps the history.
|
|
260
|
+
- **`[[wikilinks]]`** — resolve by slug regardless of date prefix. `mem_search` and
|
|
261
|
+
`mem_context` traverse them and show `→ related:`, so linked entries travel together.
|
|
262
|
+
|
|
263
|
+
Filenames are `YYYY-MM-DD-<slug>.md`. The date prefix is load-bearing: it drives
|
|
264
|
+
newest-first ordering and the search recency boost.
|
|
265
|
+
|
|
266
|
+
### Editing and correcting
|
|
267
|
+
|
|
268
|
+
There's no update tool. Two paths:
|
|
269
|
+
|
|
270
|
+
- **Superseding a decision** — `mem_save` with `supersedes: <filename>` records the
|
|
271
|
+
replacement and keeps the history. This is the intended path.
|
|
272
|
+
- **Fixing a mistake** — just edit the markdown file. It's plain text in your repo.
|
|
273
|
+
|
|
274
|
+
⚠️ Saving twice on the same day with the same title **overwrites silently** — the
|
|
275
|
+
filename is `date-slug` and `slugify` truncates at 60 characters, so two long titles
|
|
276
|
+
sharing a prefix collide. Use short, distinct titles.
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## CLI reference
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
repomem Start the MCP server (stdio) — how agents invoke it
|
|
284
|
+
repomem init Scaffold .repomem/ and repomem.config.json
|
|
285
|
+
repomem setup <agent> Wire repomem into claude-code | cursor | gemini | codex
|
|
286
|
+
repomem status Show memory counts, configured agents, linked repos
|
|
287
|
+
repomem sync Export all memory to stdout
|
|
288
|
+
repomem import [file] Import a sync bundle (file or stdin) into .repomem/
|
|
289
|
+
repomem pull Fetch remote linked repos' memory from GitHub
|
|
290
|
+
repomem help Show this help
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
`init` is idempotent — re-running it regenerates `REPOMEM.md` without touching config.
|
|
294
|
+
|
|
295
|
+
`sync` and `import` are inverses, for airgapped transfer:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
repomem sync > bundle.md # on the connected machine
|
|
299
|
+
repomem import bundle.md # on the airgapped one
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Where `setup` writes each agent's config:
|
|
303
|
+
|
|
304
|
+
| Agent | File |
|
|
305
|
+
|---|---|
|
|
306
|
+
| Claude Code | `.mcp.json` (repo root — **not** `.claude/mcp.json`) |
|
|
307
|
+
| Cursor | `.cursor/mcp.json` |
|
|
308
|
+
| Gemini CLI | `.gemini/settings.json` |
|
|
309
|
+
| Codex | `.codex/config.toml` |
|
|
310
|
+
|
|
311
|
+
On Windows the generated command is wrapped in `cmd /c`, because agents spawn MCP
|
|
312
|
+
servers without a shell and `npx` is a `.cmd` shim there. That makes the generated
|
|
313
|
+
config **host-specific** — a teammate on macOS or Linux should re-run `repomem setup`
|
|
314
|
+
to rewrite it.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Multi-repo
|
|
319
|
+
|
|
320
|
+
Working across microservices? Declare related repos in `repomem.config.json`:
|
|
321
|
+
|
|
322
|
+
```jsonc
|
|
108
323
|
{
|
|
109
324
|
"project": "payments-service",
|
|
110
325
|
"workspace": "../repomem-workspace",
|
|
@@ -117,19 +332,44 @@ Working across microservices or a multi-repo setup? repomem handles it.
|
|
|
117
332
|
```
|
|
118
333
|
|
|
119
334
|
Linked repos can be **local paths** or **remote GitHub repos** (`github:owner/name`,
|
|
120
|
-
optionally `#ref`). For remotes, run `repomem pull` once
|
|
121
|
-
into a local, gitignored cache
|
|
122
|
-
|
|
335
|
+
optionally `#ref`). For remotes, run `repomem pull` once — it fetches only their
|
|
336
|
+
`.repomem/` subtree through the GitHub API into a local, gitignored cache. No full
|
|
337
|
+
clone. Set `GITHUB_TOKEN`/`GH_TOKEN` for private repos and higher rate limits.
|
|
338
|
+
|
|
339
|
+
Then `mem_search` with `linked=true` searches current + linked + remote + workspace,
|
|
340
|
+
ranked together and labelled by source:
|
|
123
341
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
342
|
+
```
|
|
343
|
+
[current] [linked:auth-service] [remote:billing-svc] [workspace]
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Pull is explicit and manual — nothing refreshes in the background.
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Troubleshooting
|
|
351
|
+
|
|
352
|
+
**"I don't see any repomem commands."**
|
|
353
|
+
Expected. MCP servers contribute tools, not slash commands. Check `/mcp` instead — it
|
|
354
|
+
should list `repomem` as connected with six tools.
|
|
128
355
|
|
|
129
|
-
|
|
130
|
-
|
|
356
|
+
**`/mcp` shows repomem as failed or missing.**
|
|
357
|
+
Three causes, in order of likelihood:
|
|
358
|
+
1. You haven't restarted the agent since running `repomem setup`.
|
|
359
|
+
2. The project's `.mcp.json` was never approved — the prompt only fires at startup.
|
|
360
|
+
3. You're on Windows with a config generated by repomem ≤ 0.3.0, which used a bare
|
|
361
|
+
`npx` command that cannot be spawned without a shell. Re-run `repomem setup`.
|
|
131
362
|
|
|
132
|
-
|
|
363
|
+
**Tools appear but every call says `.repomem/ not found`.**
|
|
364
|
+
The server resolves the project root by walking up for `.repomem/` or
|
|
365
|
+
`repomem.config.json`. Run `repomem init` at the repo root.
|
|
366
|
+
|
|
367
|
+
**Working on repomem itself?** Point `.mcp.json` at your local build so you exercise
|
|
368
|
+
your changes rather than the published package:
|
|
369
|
+
|
|
370
|
+
```json
|
|
371
|
+
{ "mcpServers": { "repomem": { "command": "node", "args": ["dist/cli.js"] } } }
|
|
372
|
+
```
|
|
133
373
|
|
|
134
374
|
---
|
|
135
375
|
|
|
@@ -149,38 +389,49 @@ Cross-repository context that actually travels with the code — not locked in a
|
|
|
149
389
|
|
|
150
390
|
## Roadmap
|
|
151
391
|
|
|
152
|
-
- [x] npm package claimed
|
|
153
392
|
- [x] `repomem init` — scaffold `.repomem/` in any project
|
|
154
|
-
- [x]
|
|
155
|
-
- [x] Claude Code
|
|
156
|
-
- [x]
|
|
157
|
-
- [x] Gemini CLI / Codex wiring
|
|
158
|
-
- [x] Multi-repo `linked` support (local paths)
|
|
393
|
+
- [x] Six MCP tools (`mem_save`, `mem_search`, `mem_context`, `mem_get`, `mem_handoff`, `mem_prime`)
|
|
394
|
+
- [x] Claude Code, Cursor, Gemini CLI, and Codex wiring
|
|
395
|
+
- [x] Multi-repo `linked` support — local paths and remote GitHub repos
|
|
159
396
|
- [x] Workspace scope (cross-org shared memory repo)
|
|
160
|
-
- [x] `repomem sync`
|
|
161
|
-
- [x]
|
|
162
|
-
- [x] `repomem import` — import a sync bundle for airgapped environments
|
|
163
|
-
- [x] Smarter search ranking (BM25 + recency weighting)
|
|
397
|
+
- [x] `repomem sync` / `import` for airgapped transfer
|
|
398
|
+
- [x] BM25 + recency search ranking
|
|
164
399
|
- [x] Progressive disclosure — `mem_context` summaries + `mem_get` to expand
|
|
165
|
-
- [x] `[[wikilink]]` graph between memories
|
|
400
|
+
- [x] `[[wikilink]]` graph between memories
|
|
166
401
|
- [x] `mem_prime` — bootstrap memory from an existing repo's docs
|
|
402
|
+
- [x] One file per session, with names, timestamps, and agent attribution
|
|
403
|
+
- [x] Git-derived handoffs — "what changed" comes from commits, not recollection
|
|
404
|
+
- [ ] `init` learns the repo — stack, commands, layout, ADRs, and git conventions, without an LLM
|
|
405
|
+
- [ ] `repomem prime` as a CLI, so onboarding can be scripted end-to-end
|
|
406
|
+
- [ ] Auto-capture hooks, so memory does not depend on remembering to ask
|
|
407
|
+
- [ ] Task-scoped `mem_context` and a token budget, for repos with lots of memory
|
|
167
408
|
- [ ] Optional semantic search layer (off by default, local embedding cache)
|
|
168
409
|
|
|
169
410
|
---
|
|
170
411
|
|
|
171
412
|
## Status
|
|
172
413
|
|
|
173
|
-
**v0.
|
|
174
|
-
|
|
175
|
-
`mem_prime`
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
414
|
+
**v0.3.1 — working.** `init`, `setup`, `status`, `sync`, `import`, `pull`, and six MCP
|
|
415
|
+
tools are implemented and covered by tests. Context is token-lean by default, memories
|
|
416
|
+
cross-link with `[[wikilinks]]`, `mem_prime` bootstraps an existing repo from its docs,
|
|
417
|
+
and multi-repo search spans local paths, pulled GitHub remotes, and a shared workspace.
|
|
418
|
+
|
|
419
|
+
**On `master`, not yet published:** one file per session
|
|
420
|
+
(`YYYY-MM-DD-HHMM-<name>.md`) with front matter, session naming, agent attribution
|
|
421
|
+
from the MCP handshake, `mem_context` listing parallel same-day sessions, and
|
|
422
|
+
git-derived handoffs. Landing in v0.4.
|
|
423
|
+
|
|
424
|
+
**The known gap:** nothing is recorded unless the agent is asked. MCP servers are
|
|
425
|
+
passive — they act only when a tool is called. Git-derived handoffs remove the recall
|
|
426
|
+
burden once a handoff is requested, but *requesting* it is still manual. Auto-capture
|
|
427
|
+
hooks are the planned fix.
|
|
180
428
|
|
|
181
|
-
If this solves a problem you have, **star the repo** — it helps validate that this is
|
|
429
|
+
If this solves a problem you have, **star the repo** — it helps validate that this is
|
|
430
|
+
worth building and tells me which features to prioritise.
|
|
182
431
|
|
|
183
|
-
Have this exact problem on your team? Open an
|
|
432
|
+
Have this exact problem on your team? Open an
|
|
433
|
+
[issue](https://github.com/saleem786khan/repomem/issues) describing your setup — I'm
|
|
434
|
+
using real use cases to shape the roadmap.
|
|
184
435
|
|
|
185
436
|
---
|
|
186
437
|
|
|
@@ -192,11 +443,10 @@ repomem is being built in public. Contributions welcome at any stage.
|
|
|
192
443
|
git clone https://github.com/saleem786khan/repomem
|
|
193
444
|
cd repomem
|
|
194
445
|
npm install
|
|
195
|
-
npm
|
|
446
|
+
npm test # builds, then runs the suite
|
|
196
447
|
```
|
|
197
448
|
|
|
198
|
-
See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to get involved.
|
|
199
|
-
Good first issues will be labelled once the scaffold is up.
|
|
449
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to get involved.
|
|
200
450
|
|
|
201
451
|
---
|
|
202
452
|
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6BA,yDAAyD;AACzD,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAwDjD"}
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ const mem_context_js_1 = require("./tools/mem-context.js");
|
|
|
11
11
|
const mem_handoff_js_1 = require("./tools/mem-handoff.js");
|
|
12
12
|
const mem_get_js_1 = require("./tools/mem-get.js");
|
|
13
13
|
const mem_prime_js_1 = require("./tools/mem-prime.js");
|
|
14
|
+
const session_js_1 = require("./tools/session.js");
|
|
14
15
|
const TOOLS = [
|
|
15
16
|
mem_save_js_1.memSave,
|
|
16
17
|
mem_search_js_1.memSearch,
|
|
@@ -20,7 +21,7 @@ const TOOLS = [
|
|
|
20
21
|
mem_prime_js_1.memPrime,
|
|
21
22
|
];
|
|
22
23
|
const TOOL_MAP = new Map(TOOLS.map((t) => [t.name, t]));
|
|
23
|
-
const VERSION = "0.
|
|
24
|
+
const VERSION = "0.4.0";
|
|
24
25
|
/** Build and start the repomem MCP server over stdio. */
|
|
25
26
|
async function startServer() {
|
|
26
27
|
const server = new index_js_1.Server({ name: "repomem", version: VERSION }, {
|
|
@@ -32,6 +33,14 @@ async function startServer() {
|
|
|
32
33
|
"recall, and mem_handoff to close out a session. On a project new to " +
|
|
33
34
|
"repomem, call mem_prime once to bootstrap memory from existing docs.",
|
|
34
35
|
});
|
|
36
|
+
// Attribute session memory to whichever agent connected. The client announces
|
|
37
|
+
// itself in `initialize`, so this costs nothing and makes parallel sessions
|
|
38
|
+
// from different agents tellable apart.
|
|
39
|
+
server.oninitialized = () => {
|
|
40
|
+
const client = server.getClientVersion();
|
|
41
|
+
if (client?.name)
|
|
42
|
+
(0, session_js_1.setSessionAgent)(client.name);
|
|
43
|
+
};
|
|
35
44
|
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
36
45
|
tools: TOOLS.map((t) => ({
|
|
37
46
|
name: t.name,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,7 +18,7 @@ export declare function getRepomemRoot(projectRoot?: string): string;
|
|
|
18
18
|
export declare function isInitialized(projectRoot?: string): boolean;
|
|
19
19
|
/**
|
|
20
20
|
* List relative file paths (from .repomem/) of a given memory type, sorted
|
|
21
|
-
* newest-first
|
|
21
|
+
* newest-first. Returns [] when uninitialised or empty.
|
|
22
22
|
*/
|
|
23
23
|
export declare function listFiles(type: MemoryType, projectRoot?: string): string[];
|
|
24
24
|
/** Read a memory file by type + filename. Returns null when missing. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../../src/store/file-store.ts"],"names":[],"mappings":"AAEA,OAAO,EAA+B,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAA2B,SAAS,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE1E,eAAO,MAAM,YAAY,EAAE,UAAU,EAKpC,CAAC;AAEF,eAAO,MAAM,WAAW,aAAa,CAAC;AACtC,eAAO,MAAM,cAAc,eAAe,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,WAAW,GAAE,MAA0B,GAAG,MAAM,CAE9E;AAED,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,WAAW,GAAE,MAA0B,GAAG,OAAO,CAE9E;
|
|
1
|
+
{"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../../src/store/file-store.ts"],"names":[],"mappings":"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"}
|
package/dist/store/file-store.js
CHANGED
|
@@ -70,9 +70,24 @@ function getRepomemRoot(projectRoot = (0, config_js_1.findProjectRoot)()) {
|
|
|
70
70
|
function isInitialized(projectRoot = (0, config_js_1.findProjectRoot)()) {
|
|
71
71
|
return fs.existsSync(getRepomemRoot(projectRoot));
|
|
72
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Chronological sort key for a memory filename: its `YYYY-MM-DD` prefix plus the
|
|
75
|
+
* `HHMM` that session files carry.
|
|
76
|
+
*
|
|
77
|
+
* Sorting raw names would be wrong here: `-` (0x2D) sorts before `.` (0x2E), so
|
|
78
|
+
* a legacy whole-day `2026-07-26.md` would outrank `2026-07-26-0917-x.md` from
|
|
79
|
+
* the same date and be mistaken for the newer file. Legacy day files get `0000`
|
|
80
|
+
* so any timed session that day counts as newer.
|
|
81
|
+
*/
|
|
82
|
+
function fileOrderKey(filename) {
|
|
83
|
+
const m = filename.match(/^(\d{4}-\d{2}-\d{2})(?:-(\d{4})(?=-|\.))?/);
|
|
84
|
+
if (!m)
|
|
85
|
+
return "0000-00-00-0000";
|
|
86
|
+
return `${m[1]}-${m[2] ?? "0000"}`;
|
|
87
|
+
}
|
|
73
88
|
/**
|
|
74
89
|
* List relative file paths (from .repomem/) of a given memory type, sorted
|
|
75
|
-
* newest-first
|
|
90
|
+
* newest-first. Returns [] when uninitialised or empty.
|
|
76
91
|
*/
|
|
77
92
|
function listFiles(type, projectRoot = (0, config_js_1.findProjectRoot)()) {
|
|
78
93
|
const dir = path.join(getRepomemRoot(projectRoot), type);
|
|
@@ -80,7 +95,11 @@ function listFiles(type, projectRoot = (0, config_js_1.findProjectRoot)()) {
|
|
|
80
95
|
return fs
|
|
81
96
|
.readdirSync(dir)
|
|
82
97
|
.filter((f) => f.endsWith(".md"))
|
|
83
|
-
.sort()
|
|
98
|
+
.sort((a, b) => {
|
|
99
|
+
const ka = fileOrderKey(a);
|
|
100
|
+
const kb = fileOrderKey(b);
|
|
101
|
+
return ka === kb ? a.localeCompare(b) : ka < kb ? -1 : 1;
|
|
102
|
+
})
|
|
84
103
|
.reverse();
|
|
85
104
|
}
|
|
86
105
|
catch {
|
|
@@ -169,9 +188,15 @@ function parseLinks(raw) {
|
|
|
169
188
|
out.add(m[1].trim());
|
|
170
189
|
return [...out];
|
|
171
190
|
}
|
|
172
|
-
/**
|
|
191
|
+
/**
|
|
192
|
+
* Strip the date (and, for session files, the HHMM) prefix and the extension to
|
|
193
|
+
* get a memory's slug — the thing [[wikilinks]] match against.
|
|
194
|
+
*/
|
|
173
195
|
function fileSlug(filename) {
|
|
174
|
-
return filename
|
|
196
|
+
return filename
|
|
197
|
+
.replace(/\.md$/i, "")
|
|
198
|
+
.replace(/^\d{4}-\d{2}-\d{2}-?(?:\d{4}-)?/, "")
|
|
199
|
+
.toLowerCase();
|
|
175
200
|
}
|
|
176
201
|
/**
|
|
177
202
|
* Resolve a `[[target]]` to a memory file. Matches an exact filename, an exact
|