sessionmem 1.3.0 → 1.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/.github/sessionmem-logo.svg +57 -0
- package/CHANGELOG.md +84 -0
- package/CONTRIBUTING.md +130 -0
- package/README.md +122 -83
- package/SECURITY.md +33 -0
- package/dist/cli/commands/dedupe.js +1 -1
- package/dist/cli/commands/import.js +15 -44
- package/dist/cli/commands/install.js +3 -3
- package/dist/cli/commands/list.js +3 -0
- package/dist/cli/commands/run.js +10 -12
- package/dist/cli/commands/search.js +8 -4
- package/dist/cli/commands/uninstall.js +2 -1
- package/dist/cli/context.js +8 -6
- package/dist/cli/index.js +6 -6
- package/dist/core/config/policyConfig.js +5 -1
- package/dist/core/injection/tokenBudget.js +10 -2
- package/dist/core/schema/runMigrations.js +9 -5
- package/dist/core/storage/db.js +31 -9
- package/dist/core/summarize/cloudSummarizer.js +3 -8
- package/docs/architecture.md +53 -0
- package/docs/benchmark.md +50 -0
- package/docs/cloud-summarization.md +57 -0
- package/docs/migration.md +42 -0
- package/docs/privacy-and-retention.md +139 -0
- package/docs/team-mode.md +117 -0
- package/docs/troubleshooting.md +78 -0
- package/docs/typescript-upgrade.md +48 -0
- package/package.json +25 -13
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<a href="https://github.com/catfish-1234/sessionmem/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
|
-
**85.6% fewer tokens
|
|
12
|
+
**Project memory between sessions, stored on your machine. Startup context used 85.6% fewer tokens than full session history in the included benchmark.**
|
|
13
13
|
|
|
14
14
|
```
|
|
15
15
|
New session. Claude starts fresh.
|
|
@@ -27,15 +27,15 @@ WITH sessionmem:
|
|
|
27
27
|
Claude: "Looks like you're mid-Stripe migration. Where do you want to pick up?"
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
`sessionmem` is an MCP server that
|
|
30
|
+
`sessionmem` is an MCP server that stores decisions, warnings, and project context across sessions. Claude Code hooks capture supported tool events and load relevant memories automatically. Other hosts use its MCP tools and installed instructions; session automation depends on the host. It works with Claude Code, Cursor, Cline, Codex, Windsurf, and other [MCP](https://modelcontextprotocol.io) hosts.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
By default, everything stays on your machine. You don't need an account or a cloud service. Data only leaves your computer if you enable sharing or cloud summarization.
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
36
36
|
## Quickstart
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
You need a terminal (Command Prompt, Terminal, or PowerShell) and [Node.js](https://nodejs.org), version 20.19+ on the Node 20 line, or 22.13+. Node 22 LTS is recommended. No programming experience is required.
|
|
39
39
|
|
|
40
40
|
### 1. Install sessionmem
|
|
41
41
|
|
|
@@ -45,31 +45,35 @@ npm install -g sessionmem
|
|
|
45
45
|
|
|
46
46
|
### 2. Register it with your AI tool
|
|
47
47
|
|
|
48
|
-
Run this
|
|
48
|
+
Run this in your project folder after configuring your AI tool (Claude Code, Cursor, etc.):
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
51
|
sessionmem install
|
|
52
|
+
# From a plain terminal, choose the host explicitly when needed:
|
|
53
|
+
sessionmem install --adapter claude-code
|
|
54
|
+
# Other names: cursor, cline, codex, windsurf, antigravity, qcoder, generic
|
|
52
55
|
```
|
|
53
56
|
|
|
54
|
-
This
|
|
55
|
-
- Tells your AI tool's MCP host about `sessionmem` so it can be launched automatically.
|
|
56
|
-
- Creates a config file at `~/.sessionmem/config.json` with safe, privacy-respecting defaults, but only if one doesn't already exist.
|
|
57
|
-
- Injects instructions into `~/.claude/CLAUDE.md` so Claude Code knows about sessionmem's tools and uses them proactively (idempotent, so safe to re-run).
|
|
57
|
+
This initializes the database and, for supported adapters:
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
- Registers `sessionmem` with your AI tool's MCP host so the host can start it automatically.
|
|
60
|
+
- Creates `~/.sessionmem/config.json` with secret redaction enabled and a 90-day retention window, if the file doesn't already exist.
|
|
61
|
+
- Adds instructions to the selected host's guidance file, such as `~/.claude/CLAUDE.md` for Claude Code or `~/.codex/AGENTS.md` for Codex. You can re-run the command without duplicating those instructions.
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
### 3. Restart your AI tool
|
|
64
|
+
|
|
65
|
+
Restart your editor or agent so it loads the MCP registration. Your host starts
|
|
66
|
+
`sessionmem run` automatically; running it directly starts a stdio server that
|
|
67
|
+
waits for MCP messages, so it can appear to hang in a terminal. For an unknown
|
|
68
|
+
host (`generic`), install writes a project-local `.mcp.json`; if your host uses
|
|
69
|
+
another format or location, copy its server entry into that host’s MCP config.
|
|
64
70
|
|
|
65
|
-
|
|
71
|
+
After restarting:
|
|
66
72
|
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
- At the end of each session, it writes down a short summary of what mattered.
|
|
70
|
-
- At the start of your next session, it quietly reminds your assistant of the relevant bits.
|
|
73
|
+
- In Claude Code, the installed PostToolUse, SessionEnd, and SessionStart hooks capture supported tool events, summarize them, and load prior memories automatically.
|
|
74
|
+
- Other MCP hosts use sessionmem's tools and installed instructions. Automatic capture and session lifecycle calls depend on the host or agent. MCP registration alone does not install Claude Code hooks there.
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
Check that the local database is accessible:
|
|
73
77
|
|
|
74
78
|
```bash
|
|
75
79
|
sessionmem ping
|
|
@@ -92,6 +96,7 @@ sessionmem ping
|
|
|
92
96
|
- [Further documentation](#further-documentation)
|
|
93
97
|
- [Troubleshooting](#troubleshooting)
|
|
94
98
|
- [FAQ](#faq)
|
|
99
|
+
- [Security](#security)
|
|
95
100
|
- [Contributing](#contributing)
|
|
96
101
|
- [License](#license)
|
|
97
102
|
|
|
@@ -99,44 +104,42 @@ sessionmem ping
|
|
|
99
104
|
|
|
100
105
|
## What problem does this solve?
|
|
101
106
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
> You spend twenty minutes explaining your project's setup, the libraries you use, a tricky bug you already fixed, and a decision you made about how authentication should work. The assistant nods along, helps you out... and then in your **next session**, it has forgotten all of it. You explain everything again.
|
|
105
|
-
|
|
106
|
-
This happens because most AI assistants only "know" what's inside the current conversation. Once that conversation ends, the context is gone.
|
|
107
|
+
Most AI coding assistants rely on the current conversation for context. Start a new session and you may need to explain the project's setup, libraries, fixed bugs, and design decisions again.
|
|
107
108
|
|
|
108
|
-
`sessionmem`
|
|
109
|
+
`sessionmem` keeps short notes between sessions:
|
|
109
110
|
|
|
110
|
-
1. While you work, it
|
|
111
|
-
2.
|
|
112
|
-
3.
|
|
111
|
+
1. While you work, it captures session events.
|
|
112
|
+
2. At session end, it summarizes decisions, warnings, and useful facts into stored memories.
|
|
113
|
+
3. At the next session start, it loads the most relevant memories within a small token budget.
|
|
113
114
|
|
|
114
|
-
|
|
115
|
+
Claude Code runs these steps through the installed hooks. Other hosts need their
|
|
116
|
+
agent to call the MCP memory and lifecycle tools; the installed guidance explains
|
|
117
|
+
how to do that.
|
|
115
118
|
|
|
116
119
|
---
|
|
117
120
|
|
|
118
121
|
## How is sessionmem different?
|
|
119
122
|
|
|
120
|
-
|
|
123
|
+
Compared with projects such as *claude-mem* and other cloud or Claude-only memory tools, `sessionmem` focuses on local storage and support for multiple MCP hosts:
|
|
121
124
|
|
|
122
125
|
| | **sessionmem** | Typical cloud/Claude-only memory tools |
|
|
123
126
|
|---|---|---|
|
|
124
127
|
| **Where is data stored?** | A single SQLite file on your computer (`~/.sessionmem/memories.db`) | Often a hosted service, a cloud vector database, or a separate server process you have to run |
|
|
125
|
-
| **Account / sign-up required?** | No
|
|
128
|
+
| **Account / sign-up required?** | No | Sometimes |
|
|
126
129
|
| **Which AI tools does it work with?** | Claude Code, Cursor, Codex, Cline, Windsurf, Antigravity, QCoder, and any other MCP-compatible host | Usually just one specific tool (commonly Claude Code only) |
|
|
127
130
|
| **Secret redaction** | Built in, on by default. API keys, tokens, passwords, and private keys are scrubbed before anything is saved. | Often not handled, or left to the user |
|
|
128
|
-
| **Token budget control** | Injected memories are trimmed to a small, fixed token budget
|
|
129
|
-
| **Old/stale memory cleanup** | Built-in retention policy automatically prunes old memories (configurable, on by default) | Often
|
|
131
|
+
| **Token budget control** | Injected memories are trimmed to a small, fixed token budget (see benchmarks below) | Varies, often unbounded |
|
|
132
|
+
| **Old/stale memory cleanup** | Built-in retention policy automatically prunes old memories (configurable, on by default) | Often no automatic cleanup ("memory rot") |
|
|
130
133
|
| **Team sharing** | Optional, via a shared folder you already control (network drive, synced directory). No server needed. | Usually requires a shared hosted backend |
|
|
131
|
-
| **Offline-capable** | Yes
|
|
134
|
+
| **Offline-capable** | Yes. Works with no network connection by default. | Usually requires network access to the memory service |
|
|
132
135
|
|
|
133
|
-
|
|
136
|
+
The SQLite file is easy to inspect, back up, or delete, and you can use it across AI tools from different vendors.
|
|
134
137
|
|
|
135
138
|
---
|
|
136
139
|
|
|
137
140
|
## Benchmark results
|
|
138
141
|
|
|
139
|
-
These numbers come from `npm run benchmark` (`scripts/benchmark.mjs`)
|
|
142
|
+
These numbers come from `npm run benchmark` (`scripts/benchmark.mjs`). It runs the production retrieval and injection code against fixed, synthetic test data without network calls. See [`docs/benchmark.md`](docs/benchmark.md) for the full report and reproduction steps.
|
|
140
143
|
|
|
141
144
|
### Token savings
|
|
142
145
|
|
|
@@ -147,11 +150,11 @@ These numbers come from `npm run benchmark` (`scripts/benchmark.mjs`), which run
|
|
|
147
150
|
| Full session history (baseline) | 1,587 |
|
|
148
151
|
| What sessionmem injects at the start of your next session | 228 |
|
|
149
152
|
|
|
150
|
-
In
|
|
153
|
+
In this benchmark, about 1,600 tokens of session history become roughly 230 tokens of decisions, warnings, and facts for the next session.
|
|
151
154
|
|
|
152
155
|
### Retrieval accuracy
|
|
153
156
|
|
|
154
|
-
> **100% hit-rate**:
|
|
157
|
+
> **100% hit-rate**: all 10 test queries retrieved the expected memory.
|
|
155
158
|
|
|
156
159
|
| Metric | Result |
|
|
157
160
|
|---|---|
|
|
@@ -159,9 +162,9 @@ In practice: instead of re-reading (or re-explaining) about 1,600 tokens of past
|
|
|
159
162
|
| Recall | 100.0% |
|
|
160
163
|
| Precision | 33.3% |
|
|
161
164
|
|
|
162
|
-
|
|
165
|
+
Each query returns 3 candidate memories, with one scored as the expected match. That gives 33.3% precision even when the expected memory is found every time. The other two candidates may provide context, but are not scored as matches. These results apply to the 10 curated queries; see the report for the embedding model's limits.
|
|
163
166
|
|
|
164
|
-
|
|
167
|
+
The benchmark is deterministic and reproducible:
|
|
165
168
|
|
|
166
169
|
```bash
|
|
167
170
|
npm run build # benchmark imports the compiled code from dist/
|
|
@@ -198,11 +201,11 @@ npm run benchmark # regenerates docs/benchmark.md
|
|
|
198
201
|
└──────────────────────────────────────────────┘
|
|
199
202
|
```
|
|
200
203
|
|
|
201
|
-
- **Adapters**
|
|
202
|
-
- **The core engine**
|
|
203
|
-
- **The database** is
|
|
204
|
+
- **Adapters** connect each AI tool to sessionmem. Supporting another tool doesn't require changing the memory engine.
|
|
205
|
+
- **The core engine** stores memories, ranks their relevance, and selects what fits in the next session's context budget. Every adapter uses the same engine.
|
|
206
|
+
- **The database** is a local file. You can back it up, move it, inspect it, or delete it.
|
|
204
207
|
|
|
205
|
-
|
|
208
|
+
See [`docs/architecture.md`](docs/architecture.md) for the technical overview.
|
|
206
209
|
|
|
207
210
|
---
|
|
208
211
|
|
|
@@ -210,33 +213,58 @@ For a deeper technical dive, see [`docs/architecture.md`](docs/architecture.md).
|
|
|
210
213
|
|
|
211
214
|
| Command | What it does |
|
|
212
215
|
|---------|--------------|
|
|
213
|
-
| `sessionmem install` | Register sessionmem with the current MCP host and write default config. |
|
|
214
|
-
| `sessionmem uninstall [--purge]` | Remove sessionmem from the host. `--purge` also deletes the local database. |
|
|
216
|
+
| `sessionmem install [--adapter <name>]` | Register sessionmem with the current MCP host and write default config. |
|
|
217
|
+
| `sessionmem uninstall [--adapter <name>] [--purge]` | Remove sessionmem from the host. `--purge` also deletes the local database. |
|
|
215
218
|
| `sessionmem run` | Start the MCP server. |
|
|
216
|
-
| `sessionmem ping` | Check
|
|
217
|
-
| `sessionmem search <query> [--limit <n>]` | Search memories by semantic query. |
|
|
218
|
-
| `sessionmem list` | List
|
|
219
|
-
| `sessionmem show <id>` | Show
|
|
219
|
+
| `sessionmem ping` | Check the local version and database reachability. |
|
|
220
|
+
| `sessionmem search <query> [--limit <n>]` | Search memories by semantic query (limit 1–100, default 10). |
|
|
221
|
+
| `sessionmem list` | List up to 200 recent memories for the current project; warns when truncated. |
|
|
222
|
+
| `sessionmem show <id>` | Show memory details (content capped at 2000 characters; use export for full content). |
|
|
220
223
|
| `sessionmem forget <id> [--force]` | Delete a memory by ID. |
|
|
221
|
-
| `sessionmem export [path]` | Export memories to
|
|
222
|
-
| `sessionmem import <path> [--merge]` | Import
|
|
224
|
+
| `sessionmem export [path]` | Export all current-project memories to JSON (default `~/.sessionmem/export-YYYY-MM-DD.json`). |
|
|
225
|
+
| `sessionmem import <path> [--merge]` | Import up to 1000 valid records from UTF-8 JSON into the current project. Existing IDs are preserved, including with `--merge`; invalid records are skipped with warnings. |
|
|
223
226
|
| `sessionmem stats` | Show memory statistics for the current project. |
|
|
224
227
|
| `sessionmem savings [--json]` | Show token savings from compression and injection, with percentage. |
|
|
225
228
|
| `sessionmem redact-scan [--apply]` | Scan stored memories for secrets; `--apply` redacts in place. |
|
|
226
229
|
| `sessionmem retention prune [--force] [--days <n>]` | Prune old memories (dry-run by default). |
|
|
227
230
|
| `sessionmem config get <key>` / `config set <key> <value>` | Read and write policy config. |
|
|
228
|
-
| `sessionmem team enable <path>` / `team disable` / `team status` | Manage shared-path team memory mode. |
|
|
231
|
+
| `sessionmem team enable <path>` / `team disable [--remove-team-memories]` / `team status` | Manage shared-path team memory mode. |
|
|
229
232
|
| `sessionmem sync` | Push local memories and pull teammate memories via the shared path. |
|
|
233
|
+
| `sessionmem browse [--project <id>]` | List projects or preview up to 50 recent memories from one project. |
|
|
234
|
+
| `sessionmem dedupe [--apply] [--threshold <n>]` | Preview duplicate memories; `--apply` deletes them (threshold >0–1, default 0.85). |
|
|
235
|
+
| `sessionmem re-embed` | Refresh memories with stale embedding versions. |
|
|
236
|
+
| `sessionmem session-start` | Print prior context as Claude Code SessionStart hook JSON. |
|
|
237
|
+
| `sessionmem ingest-hook` | Read a Claude Code PostToolUse JSON payload from stdin and capture supported tool events. |
|
|
238
|
+
| `sessionmem session-end` | Summarize ingested events and apply retention pruning; reads the hook session ID from stdin. |
|
|
239
|
+
|
|
240
|
+
Use `sessionmem --help` or `sessionmem <command> --help` for options, and
|
|
241
|
+
`sessionmem --version` for the installed version. Destructive commands require
|
|
242
|
+
`--force`, `--apply`, or `--purge` as shown above. `team disable` preserves
|
|
243
|
+
teammate memories unless `--remove-team-memories` is passed.
|
|
244
|
+
|
|
245
|
+
The default database is `~/.sessionmem/memories.db`. Set `SESSIONMEM_DB_PATH`
|
|
246
|
+
to use another file; its parent directories are created automatically and the
|
|
247
|
+
default directory is not created by database commands. Server logs are written
|
|
248
|
+
to `logs/mcp.log` beside the selected database (disabled for `:memory:`). Config remains at
|
|
249
|
+
`~/.sessionmem/config.json`. `uninstall --purge` deletes the selected database.
|
|
250
|
+
Quote paths containing spaces (for example `"C:\Users\Ada Lovelace\memory.db"`
|
|
251
|
+
on Windows). In JSON config files, use forward slashes or escaped backslashes
|
|
252
|
+
for Windows paths. Imports accept CRLF and a UTF-8 BOM; convert other encodings to UTF-8.
|
|
253
|
+
|
|
254
|
+
`SESSIONMEM_PROJECT_ID` pins the project bucket (useful when moving a repository);
|
|
255
|
+
otherwise it is derived from the repository root or working directory.
|
|
256
|
+
`SESSIONMEM_ADAPTER` selects the install/uninstall host unless `--adapter` is set.
|
|
257
|
+
`SESSIONMEM_DEBUG=1` enables ingest-hook diagnostics on stderr.
|
|
230
258
|
|
|
231
259
|
---
|
|
232
260
|
|
|
233
261
|
## Privacy, secrets, and your data
|
|
234
262
|
|
|
235
|
-
|
|
263
|
+
Storage, retrieval, and summarization run locally by default. There is no account, telemetry, or hosted memory service. Settings live in `~/.sessionmem/config.json`.
|
|
236
264
|
|
|
237
265
|
### Secrets are scrubbed automatically
|
|
238
266
|
|
|
239
|
-
Before
|
|
267
|
+
Before saving memory content, `sessionmem` replaces common secret patterns with `REDACTED`:
|
|
240
268
|
|
|
241
269
|
- Email addresses
|
|
242
270
|
- API keys (`sk-...`, AWS `AKIA...`, GitHub `ghp_...`/`gho_...`, etc.)
|
|
@@ -244,7 +272,7 @@ Before anything is saved, `sessionmem` automatically removes common secret patte
|
|
|
244
272
|
- Private key blocks (`-----BEGIN ... PRIVATE KEY-----`)
|
|
245
273
|
- Connection-string style secrets (`password=...`, `secret=...`)
|
|
246
274
|
|
|
247
|
-
|
|
275
|
+
Redaction is on by default. To scan and clean up older memories:
|
|
248
276
|
|
|
249
277
|
```bash
|
|
250
278
|
sessionmem redact-scan # see what would be redacted
|
|
@@ -257,26 +285,26 @@ Full details: [`docs/privacy-and-retention.md`](docs/privacy-and-retention.md).
|
|
|
257
285
|
|
|
258
286
|
## Memory rot: keeping memory accurate over time
|
|
259
287
|
|
|
260
|
-
|
|
288
|
+
Old decisions and duplicate facts can accumulate in a memory store, causing the assistant to use outdated information. This is sometimes called "memory rot."
|
|
261
289
|
|
|
262
|
-
`sessionmem`
|
|
290
|
+
`sessionmem` handles it through retention, ranking, and sync rules:
|
|
263
291
|
|
|
264
|
-
1. **Retention pruning**: memories older than a configurable window (
|
|
292
|
+
1. **Retention pruning**: memories older than a configurable window (90 days by default) are eligible for deletion. A lightweight check runs at session end. You can also run it manually:
|
|
265
293
|
|
|
266
294
|
```bash
|
|
267
295
|
sessionmem retention prune # dry run - shows what *would* be deleted
|
|
268
296
|
sessionmem retention prune --force # actually deletes
|
|
269
297
|
```
|
|
270
298
|
|
|
271
|
-
2. **Importance-weighted ranking**:
|
|
299
|
+
2. **Importance-weighted ranking**: retrieval ranks memories by semantic relevance, recency, and importance. Older, less important notes rank lower even before they are pruned.
|
|
272
300
|
|
|
273
|
-
3. **Token-budgeted injection**: only the
|
|
301
|
+
3. **Token-budgeted injection**: only the highest-ranked memories are loaded, trimmed to a small token budget (see [benchmarks](#benchmark-results)). The context stays within that budget as the store grows.
|
|
274
302
|
|
|
275
|
-
4. **Conflict resolution in team mode**:
|
|
303
|
+
4. **Conflict resolution in team mode**: merging teammate memories uses last-write-wins by id to avoid stale duplicates and keeps the higher importance score so a warning isn't downgraded.
|
|
276
304
|
|
|
277
|
-
The
|
|
305
|
+
The benchmark above found every expected memory across its 10 queries (100% hit-rate and recall) with ranking and trimming enabled.
|
|
278
306
|
|
|
279
|
-
|
|
307
|
+
Export your memories before pruning if you want to keep a copy:
|
|
280
308
|
|
|
281
309
|
```bash
|
|
282
310
|
sessionmem export
|
|
@@ -286,17 +314,17 @@ sessionmem export
|
|
|
286
314
|
|
|
287
315
|
## Team mode (optional)
|
|
288
316
|
|
|
289
|
-
|
|
317
|
+
To share decisions and warnings across your team's AI assistants, point `sessionmem` at a folder everyone can read and write, such as a network drive or synced directory:
|
|
290
318
|
|
|
291
319
|
```bash
|
|
292
320
|
sessionmem team enable <shared-path>
|
|
293
321
|
sessionmem sync
|
|
294
322
|
```
|
|
295
323
|
|
|
296
|
-
-
|
|
297
|
-
-
|
|
298
|
-
-
|
|
299
|
-
-
|
|
324
|
+
- Team mode is off by default. Nothing is shared until you enable it.
|
|
325
|
+
- Memories are shared as files in that folder; no server is needed.
|
|
326
|
+
- Teammate memories appear with an `author:` prefix identifying their source.
|
|
327
|
+
- Redaction runs on every pulled memory using your redaction policy.
|
|
300
328
|
|
|
301
329
|
Full details, including the trust model: [`docs/team-mode.md`](docs/team-mode.md).
|
|
302
330
|
|
|
@@ -304,9 +332,9 @@ Full details, including the trust model: [`docs/team-mode.md`](docs/team-mode.md
|
|
|
304
332
|
|
|
305
333
|
## Cloud summarization (optional, off by default)
|
|
306
334
|
|
|
307
|
-
By default,
|
|
335
|
+
By default, session summaries are created locally, with no API calls.
|
|
308
336
|
|
|
309
|
-
|
|
337
|
+
To use Claude's API for higher-quality summaries, set `allowCloudSummarization=true` and provide an `ANTHROPIC_API_KEY`. If cloud summarization fails after retries, sessionmem uses local summarization. Failures are recorded for diagnosis; see the configuration guide below.
|
|
310
338
|
|
|
311
339
|
Details: [`docs/cloud-summarization.md`](docs/cloud-summarization.md).
|
|
312
340
|
|
|
@@ -324,7 +352,7 @@ Details: [`docs/cloud-summarization.md`](docs/cloud-summarization.md).
|
|
|
324
352
|
- Antigravity
|
|
325
353
|
- QCoder
|
|
326
354
|
|
|
327
|
-
|
|
355
|
+
Other tools that implement the [Model Context Protocol](https://modelcontextprotocol.io) can connect too.
|
|
328
356
|
|
|
329
357
|
---
|
|
330
358
|
|
|
@@ -342,21 +370,21 @@ Details: [`docs/cloud-summarization.md`](docs/cloud-summarization.md).
|
|
|
342
370
|
|
|
343
371
|
## Troubleshooting
|
|
344
372
|
|
|
345
|
-
|
|
373
|
+
For install failures, adapter issues, missing session data, or `better-sqlite3` native-build problems, see [`docs/troubleshooting.md`](docs/troubleshooting.md).
|
|
346
374
|
|
|
347
|
-
|
|
375
|
+
To refresh the installation and check for stored session data:
|
|
348
376
|
|
|
349
377
|
```bash
|
|
350
|
-
sessionmem install # idempotent
|
|
378
|
+
sessionmem install # idempotent; Claude Code also installs all three hooks
|
|
351
379
|
sessionmem stats # memories, sessions, and session_events for the current project
|
|
352
380
|
```
|
|
353
381
|
|
|
354
382
|
If `stats` shows `sessions: 0` after real work, see [“0 sessions” / no session data recorded](docs/troubleshooting.md#0-sessions--no-session-data-recorded). Memories are keyed to the **repository root**, so every directory inside one repo shares a bucket; outside a repo the working directory itself is the key.
|
|
355
383
|
|
|
356
|
-
|
|
384
|
+
To check database access and memory counts:
|
|
357
385
|
|
|
358
386
|
```bash
|
|
359
|
-
sessionmem ping #
|
|
387
|
+
sessionmem ping # can the local database be opened?
|
|
360
388
|
sessionmem stats # is data being stored?
|
|
361
389
|
```
|
|
362
390
|
|
|
@@ -365,16 +393,16 @@ sessionmem stats # is data being stored?
|
|
|
365
393
|
## FAQ
|
|
366
394
|
|
|
367
395
|
**How do I give Cursor, Cline, or Windsurf memory between sessions?**
|
|
368
|
-
Install sessionmem (`npm i -g sessionmem`), then run `sessionmem install` in your project.
|
|
396
|
+
Install sessionmem (`npm i -g sessionmem`), then run `sessionmem install` in your project. Use `--adapter cursor`, `--adapter cline`, or `--adapter windsurf` when auto-detection cannot identify your host.
|
|
369
397
|
|
|
370
398
|
**How do I give Claude Code persistent memory?**
|
|
371
|
-
|
|
399
|
+
Use the same install steps above. sessionmem also ships as a Claude Code plugin (the `.claude-plugin` directory in the repo) for Claude Code's native plugin system.
|
|
372
400
|
|
|
373
401
|
**Is there a local MCP memory server that works offline with no API key?**
|
|
374
|
-
Yes. sessionmem stores
|
|
402
|
+
Yes. sessionmem stores memories in a single SQLite file at `~/.sessionmem/memories.db` and works offline by default. Sharing and cloud summarization are optional and off by default.
|
|
375
403
|
|
|
376
404
|
**How is sessionmem different from claude-mem?**
|
|
377
|
-
sessionmem
|
|
405
|
+
sessionmem works with Claude Code, Cursor, Cline, Codex, Windsurf, Antigravity, QCoder, and other MCP hosts. It redacts secrets (API keys, tokens, JWTs) by default, prunes old memories automatically, and includes reproducible benchmarks.
|
|
378
406
|
|
|
379
407
|
**Does sessionmem send my code to the cloud?**
|
|
380
408
|
No. Nothing leaves your machine by default. The optional cloud summarization path is opt-in and off by default.
|
|
@@ -384,18 +412,29 @@ Run `sessionmem savings` to see a breakdown of storage compression (raw session
|
|
|
384
412
|
|
|
385
413
|
---
|
|
386
414
|
|
|
415
|
+
## Security
|
|
416
|
+
|
|
417
|
+
See the [security policy](SECURITY.md) for supported versions, private vulnerability
|
|
418
|
+
reporting through GitHub security advisories, and response expectations.
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
387
422
|
## Contributing
|
|
388
423
|
|
|
389
424
|
Issues and pull requests are welcome. The codebase is TypeScript, tested with [Vitest](https://vitest.dev), and linted with ESLint:
|
|
390
425
|
|
|
391
426
|
```bash
|
|
392
|
-
npm
|
|
427
|
+
npm ci
|
|
393
428
|
npm run build
|
|
429
|
+
npm run typecheck
|
|
394
430
|
npm test
|
|
395
431
|
npm run lint
|
|
396
432
|
```
|
|
397
433
|
|
|
398
|
-
**MCP config for local development:**
|
|
434
|
+
**MCP config for local development:** Clone the repository, build it, copy
|
|
435
|
+
`.mcp.json.example` to `.mcp.json`, and replace the example path with the absolute
|
|
436
|
+
path to your checkout’s `dist/cli/index.js`. This runs your local build.
|
|
437
|
+
`sessionmem install` registers the `sessionmem` executable on PATH instead. The `.mcp.json` file is gitignored because it contains machine-specific paths.
|
|
399
438
|
|
|
400
439
|
---
|
|
401
440
|
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Security fixes are provided for the latest published stable release of
|
|
6
|
+
sessionmem. Older releases and prereleases are not supported;
|
|
7
|
+
upgrade to the latest stable release before checking whether an issue persists.
|
|
8
|
+
The main branch receives fixes for the next release but is not a supported
|
|
9
|
+
published version.
|
|
10
|
+
|
|
11
|
+
## Report a vulnerability privately
|
|
12
|
+
|
|
13
|
+
Use [GitHub private vulnerability reporting](https://github.com/catfish-1234/sessionmem/security/advisories/new)
|
|
14
|
+
to open a private security advisory. You need a GitHub account. Reports stay
|
|
15
|
+
private while maintainers investigate and coordinate a fix with the reporter.
|
|
16
|
+
Do not disclose vulnerabilities in public issues, pull requests, or discussions.
|
|
17
|
+
|
|
18
|
+
Include the affected version, operating system and Node.js version, reproduction
|
|
19
|
+
steps or a minimal proof of concept, expected impact, and any suggested mitigation.
|
|
20
|
+
Use synthetic data: do not include real API keys, private session transcripts,
|
|
21
|
+
production memory databases, or other sensitive information.
|
|
22
|
+
|
|
23
|
+
## Response expectations
|
|
24
|
+
|
|
25
|
+
Maintainers aim to acknowledge reports within 7 calendar days and provide an
|
|
26
|
+
initial assessment within 14 calendar days. These are best-effort targets for a
|
|
27
|
+
community-maintained project, not guaranteed service levels. If you have not
|
|
28
|
+
heard back after 7 days, follow up in the same private advisory.
|
|
29
|
+
|
|
30
|
+
For confirmed vulnerabilities, maintainers coordinate remediation, disclosure
|
|
31
|
+
timing, and reporter credit in the private advisory. Fix timing depends on
|
|
32
|
+
severity and complexity; maintainers will share progress and available
|
|
33
|
+
workarounds there. Keep details private until coordinated disclosure.
|
|
@@ -23,7 +23,7 @@ function cosineSimilarity(a, b) {
|
|
|
23
23
|
function resolveThreshold(raw) {
|
|
24
24
|
if (raw === undefined)
|
|
25
25
|
return SIMILARITY_THRESHOLD;
|
|
26
|
-
const parsed = Number.
|
|
26
|
+
const parsed = Number(raw.trim());
|
|
27
27
|
if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 1) {
|
|
28
28
|
console.error(`Invalid --threshold "${raw}": expected a number greater than 0 and at most 1.`);
|
|
29
29
|
process.exit(1);
|
|
@@ -14,7 +14,7 @@ export async function importCommand(pathArg, options, ctx) {
|
|
|
14
14
|
// Read and parse the JSON file
|
|
15
15
|
let parsed;
|
|
16
16
|
try {
|
|
17
|
-
const raw =
|
|
17
|
+
const raw = new TextDecoder("utf-8", { fatal: true }).decode(readFileSync(inPath));
|
|
18
18
|
parsed = JSON.parse(raw);
|
|
19
19
|
}
|
|
20
20
|
catch (err) {
|
|
@@ -27,61 +27,32 @@ export async function importCommand(pathArg, options, ctx) {
|
|
|
27
27
|
console.error("Import file must be a JSON array of memory records.");
|
|
28
28
|
process.exit(1);
|
|
29
29
|
}
|
|
30
|
-
const
|
|
31
|
-
let toImport = records;
|
|
32
|
-
let skippedCount = 0;
|
|
33
|
-
if (!options.merge) {
|
|
34
|
-
// By default, skip existing IDs (pre-filter). `id` is a
|
|
35
|
-
// globally-unique PRIMARY KEY (not scoped by project), so the duplicate
|
|
36
|
-
// check must consider every project's ids -- otherwise a record whose id
|
|
37
|
-
// collides with another project's memory would not be pre-filtered here
|
|
38
|
-
// and could fall through to the service's ON CONFLICT(id) upsert.
|
|
39
|
-
const existingIds = listAllMemoryIds(context.db);
|
|
40
|
-
const filtered = records.filter((r) => {
|
|
41
|
-
const id = typeof r.id === "string" ? r.id : undefined;
|
|
42
|
-
return id === undefined || !existingIds.has(id);
|
|
43
|
-
});
|
|
44
|
-
skippedCount = records.length - filtered.length;
|
|
45
|
-
toImport = filtered;
|
|
46
|
-
}
|
|
47
|
-
if (toImport.length === 0 && skippedCount > 0) {
|
|
48
|
-
console.log(`Imported 0, skipped ${skippedCount} duplicates.`);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
// Map to the expected shape, then validate each record before sending to the service
|
|
52
|
-
const mapped = toImport.map((r) => ({
|
|
53
|
-
id: r.id,
|
|
54
|
-
projectId: r.projectId ?? context.projectId,
|
|
55
|
-
sessionId: r.sessionId,
|
|
56
|
-
sourceAdapter: r.sourceAdapter,
|
|
57
|
-
kind: r.kind,
|
|
58
|
-
content: r.content,
|
|
59
|
-
importance: r.importance,
|
|
60
|
-
createdAt: r.createdAt,
|
|
61
|
-
updatedAt: r.updatedAt,
|
|
62
|
-
}));
|
|
63
|
-
// Validate each record but skip-and-warn on individual invalid
|
|
64
|
-
// records (consistent with the duplicate-skip UX) instead of aborting the
|
|
65
|
-
// entire import on the first invalid record, which would discard earlier
|
|
66
|
-
// valid records with no partial import.
|
|
30
|
+
const existingIds = options.merge ? undefined : listAllMemoryIds(context.db);
|
|
67
31
|
const validMemories = [];
|
|
32
|
+
let skippedCount = 0;
|
|
68
33
|
let invalidCount = 0;
|
|
69
|
-
for (
|
|
70
|
-
const
|
|
34
|
+
for (const [index, record] of parsed.entries()) {
|
|
35
|
+
const isRecord = record !== null && typeof record === "object" && !Array.isArray(record);
|
|
36
|
+
if (isRecord && typeof record.id === "string" && existingIds?.has(record.id)) {
|
|
37
|
+
skippedCount += 1;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
// Validate unknown values before accessing fields. Keep the source index in
|
|
41
|
+
// diagnostics even when earlier records were duplicates, and preserve the
|
|
42
|
+
// optional provenance fields carried by exports.
|
|
43
|
+
const check = importMemoryRecordSchema.safeParse(isRecord ? { ...record, projectId: record.projectId ?? context.projectId } : record);
|
|
71
44
|
if (!check.success) {
|
|
72
|
-
console.error(`Record at index ${
|
|
45
|
+
console.error(`Record at index ${index} is invalid, skipping: ${check.error.message}`);
|
|
73
46
|
invalidCount += 1;
|
|
74
47
|
continue;
|
|
75
48
|
}
|
|
76
|
-
// Use the parsed/transformed record so `kind` is narrowed to the
|
|
77
|
-
// canonical enum (memoryKindSchema maps legacy values like `architecture`).
|
|
78
49
|
validMemories.push(check.data);
|
|
79
50
|
}
|
|
80
51
|
if (validMemories.length === 0) {
|
|
81
52
|
if (invalidCount > 0) {
|
|
82
53
|
console.log(`Imported 0, skipped ${invalidCount} invalid record(s).`);
|
|
83
54
|
}
|
|
84
|
-
else
|
|
55
|
+
else {
|
|
85
56
|
console.log(`Imported 0, skipped ${skippedCount} duplicates.`);
|
|
86
57
|
}
|
|
87
58
|
return;
|
|
@@ -3,7 +3,7 @@ import { join } from "path";
|
|
|
3
3
|
import { homedir } from "os";
|
|
4
4
|
import { AdapterFactory } from "../../adapters/factory.js";
|
|
5
5
|
import { injectGuidanceBlock } from "../../adapters/claudeMdInjector.js";
|
|
6
|
-
import { createCliContext } from "../context.js";
|
|
6
|
+
import { createCliContext, resolveDbPath } from "../context.js";
|
|
7
7
|
/**
|
|
8
8
|
* Resolve the adapter to install into: an explicit `--adapter` flag wins, then
|
|
9
9
|
* the SESSIONMEM_ADAPTER env override, else auto-detection from the host env.
|
|
@@ -53,8 +53,8 @@ export async function installCommand(options, contextOverrides) {
|
|
|
53
53
|
dbPath = ctx.dbPath;
|
|
54
54
|
}
|
|
55
55
|
catch (err) {
|
|
56
|
-
console.error(`✗ DB init failed (
|
|
57
|
-
console.error("Hint: ensure
|
|
56
|
+
console.error(`✗ DB init failed (${resolveDbPath(contextOverrides?.dbPath)}): ${err instanceof Error ? err.message : String(err)}`);
|
|
57
|
+
console.error("Hint: ensure the database parent directory is writable and run `sessionmem install` again.");
|
|
58
58
|
process.exit(1);
|
|
59
59
|
}
|
|
60
60
|
console.log(`✓ DB initialized (${dbPath})`);
|
|
@@ -10,4 +10,7 @@ export async function listCommand(ctx) {
|
|
|
10
10
|
process.exit(1);
|
|
11
11
|
}
|
|
12
12
|
console.log(formatTable(result.memories));
|
|
13
|
+
if (result.total > result.memories.length) {
|
|
14
|
+
console.error(`Showing ${result.memories.length} of ${result.total} memories. Use \`sessionmem export\` for the full project.`);
|
|
15
|
+
}
|
|
13
16
|
}
|