sessionmem 1.4.0 → 1.6.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/CHANGELOG.md +66 -0
- package/README.md +233 -41
- package/dist/adapters/diagnostics.js +109 -0
- package/dist/adapters/generic.js +6 -2
- package/dist/adapters/global/antigravity.js +4 -1
- package/dist/adapters/global/claudeCode.js +19 -4
- package/dist/adapters/global/codex.js +7 -6
- package/dist/adapters/global/qcoder.js +4 -1
- package/dist/adapters/ide/cline.js +3 -0
- package/dist/adapters/ide/cursor.js +3 -0
- package/dist/adapters/ide/installer.js +17 -5
- package/dist/adapters/ide/windsurf.js +3 -0
- package/dist/adapters/installedHosts.js +51 -0
- package/dist/cli/commands/doctor.js +155 -0
- package/dist/cli/commands/entities.js +27 -0
- package/dist/cli/commands/history.js +23 -0
- package/dist/cli/commands/ingestHook.js +4 -78
- package/dist/cli/commands/install.js +34 -56
- package/dist/cli/commands/scan.js +16 -0
- package/dist/cli/commands/search.js +1 -0
- package/dist/cli/commands/team.js +7 -3
- package/dist/cli/commands/ui.js +67 -0
- package/dist/cli/commands/watch.js +48 -0
- package/dist/cli/index.js +44 -1
- package/dist/cli/projectId.js +1 -2
- package/dist/cli/transcriptSweep.js +142 -0
- package/dist/cli/transcripts/claudeCode.js +62 -0
- package/dist/cli/transcripts/codex.js +111 -0
- package/dist/cli/transcripts/cursor.js +295 -0
- package/dist/cli/transcripts/index.js +118 -0
- package/dist/cli/transcripts/types.js +15 -0
- package/dist/core/api/contracts.js +4 -0
- package/dist/core/api/memoryCoreService.js +9 -1
- package/dist/core/api/sessionLifecycleService.js +13 -1
- package/dist/core/capture/toolEvent.js +94 -0
- package/dist/core/extract/factClaims.js +97 -0
- package/dist/core/extract/knowledge.js +127 -0
- package/dist/core/retrieve/retrieveMemories.js +20 -4
- package/dist/core/schema/migrations/012_transcript_checkpoints.sql +4 -0
- package/dist/core/schema/migrations/013_fts_porter.sql +32 -0
- package/dist/core/schema/migrations/014_memory_entities.sql +19 -0
- package/dist/core/schema/migrations/015_memory_supersessions.sql +41 -0
- package/dist/core/schema/runMigrations.js +46 -0
- package/dist/core/storage/knowledgeRepo.js +67 -0
- package/dist/core/storage/memoryRepo.js +45 -12
- package/dist/core/storage/memorySearchRepo.js +30 -20
- package/dist/core/storage/sessionEventsRepo.js +12 -3
- package/dist/core/storage/supersessionRepo.js +101 -0
- package/dist/core/summarize/summaryShape.js +1 -1
- package/dist/ui/assets.js +181 -0
- package/dist/ui/server.js +147 -0
- package/docs/REGISTRY_LISTING.md +88 -0
- package/docs/architecture.md +27 -3
- package/docs/benchmark.md +105 -33
- package/docs/migration.md +2 -0
- package/docs/troubleshooting.md +52 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,59 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.6.0] - 2026-10-18
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Multi-host transcript imports: `sessionmem scan` and `sessionmem watch` now
|
|
13
|
+
sweep Claude Code, Codex CLI, and Cursor histories by default, with a
|
|
14
|
+
separate parser per host and an explicit unsupported-format error when a
|
|
15
|
+
claimed host's records cannot be parsed. `--host` selects one host.
|
|
16
|
+
([#52], [#53])
|
|
17
|
+
- Codex CLI support: dated rollout discovery, project and session identity from
|
|
18
|
+
session metadata, resume-file grouping with cross-file deduplication,
|
|
19
|
+
sub-agent session separation, native tool-call events, and filtering of
|
|
20
|
+
encrypted agent payloads and injected machine blocks. ([#52])
|
|
21
|
+
- Cursor support: chat history read from Cursor's SQLite storage through
|
|
22
|
+
private snapshot copies opened read-only, covering both composer index
|
|
23
|
+
layouts, with per-composer checkpoints and live-update handling. ([#53])
|
|
24
|
+
- `sessionmem doctor` now checks transcript discovery for each supported
|
|
25
|
+
host. ([#52], [#53])
|
|
26
|
+
|
|
27
|
+
## [1.5.0] - 2026-10-18
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- `sessionmem scan` to import existing Claude Code transcripts into memory, and
|
|
32
|
+
`sessionmem watch` to capture new and updated sessions continuously. Both are
|
|
33
|
+
idempotent across reruns and restarts and work fully offline. ([#45])
|
|
34
|
+
- Automatic host detection during `sessionmem install`, and a `sessionmem doctor`
|
|
35
|
+
command that checks the store, hooks, adapter wiring, and recall with fix
|
|
36
|
+
hints for each failure. ([#46])
|
|
37
|
+
- Deterministic entity and relation extraction during capture and scan, a
|
|
38
|
+
`sessionmem entities` command, and entity overlap as a recall ranking
|
|
39
|
+
signal. ([#48])
|
|
40
|
+
- Deterministic supersession handling: memories that replace an earlier fact are
|
|
41
|
+
linked with history, and recall prefers current facts while replaced ones stay
|
|
42
|
+
retrievable explicitly. ([#49])
|
|
43
|
+
- `sessionmem ui`, a local read-only web viewer for memories, entities,
|
|
44
|
+
relations, and supersession history, served on localhost with no external
|
|
45
|
+
services. ([#50])
|
|
46
|
+
- COMPARISON.md, a cited comparison with claude-mem, Mem0, and Zep, plus a draft
|
|
47
|
+
MCP registry listing. ([#51])
|
|
48
|
+
- A reproducible offline LoCoMo evaluation: committed corpus with provenance and
|
|
49
|
+
checksums, and a runner reporting hit rate, precision, recall, and latency
|
|
50
|
+
with all network access blocked. ([#44])
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- Retrieval now ranks with SQLite FTS5 BM25 and Porter stemming over memory
|
|
55
|
+
content and summaries. Measured on the committed LoCoMo evaluation, top-5 hit
|
|
56
|
+
rate improved from 1.44% to 61.95%, and to 63.92% with entity overlap. ([#47], [#48])
|
|
57
|
+
- The release workflow skips the npm publish when the version already exists on
|
|
58
|
+
the registry and creates the GitHub release with notes from this
|
|
59
|
+
changelog. ([#43])
|
|
60
|
+
|
|
8
61
|
## [1.4.0] - 2026-10-16
|
|
9
62
|
|
|
10
63
|
### Added
|
|
@@ -68,6 +121,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
68
121
|
errors and findings fail the job; added scanner dependency updates and
|
|
69
122
|
Dependabot cooldowns. ([#32])
|
|
70
123
|
|
|
124
|
+
[1.6.0]: https://github.com/catfish-1234/sessionmem/compare/v1.5.0...v1.6.0
|
|
125
|
+
[1.5.0]: https://github.com/catfish-1234/sessionmem/compare/679b8954f0b49f197985c40da47f181c00060cd9...v1.5.0
|
|
126
|
+
[#43]: https://github.com/catfish-1234/sessionmem/pull/43
|
|
127
|
+
[#44]: https://github.com/catfish-1234/sessionmem/pull/44
|
|
128
|
+
[#45]: https://github.com/catfish-1234/sessionmem/pull/45
|
|
129
|
+
[#46]: https://github.com/catfish-1234/sessionmem/pull/46
|
|
130
|
+
[#47]: https://github.com/catfish-1234/sessionmem/pull/47
|
|
131
|
+
[#48]: https://github.com/catfish-1234/sessionmem/pull/48
|
|
132
|
+
[#49]: https://github.com/catfish-1234/sessionmem/pull/49
|
|
133
|
+
[#50]: https://github.com/catfish-1234/sessionmem/pull/50
|
|
134
|
+
[#51]: https://github.com/catfish-1234/sessionmem/pull/51
|
|
135
|
+
[#52]: https://github.com/catfish-1234/sessionmem/pull/52
|
|
136
|
+
[#53]: https://github.com/catfish-1234/sessionmem/pull/53
|
|
71
137
|
[1.4.0]: https://github.com/catfish-1234/sessionmem/compare/a73f503123b1d33c35407ce66403f281ba3451ac...679b8954f0b49f197985c40da47f181c00060cd9
|
|
72
138
|
[#22]: https://github.com/catfish-1234/sessionmem/pull/22
|
|
73
139
|
[#23]: https://github.com/catfish-1234/sessionmem/pull/23
|
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
|
-
**Project memory between sessions, stored on your machine
|
|
12
|
+
**Project memory between sessions, stored on your machine.**
|
|
13
13
|
|
|
14
14
|
```
|
|
15
15
|
New session. Claude starts fresh.
|
|
@@ -49,16 +49,25 @@ Run this in your project folder after configuring your AI tool (Claude Code, Cur
|
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
51
|
sessionmem install
|
|
52
|
-
#
|
|
52
|
+
# Optional: select one host explicitly, including in automation:
|
|
53
53
|
sessionmem install --adapter claude-code
|
|
54
54
|
# Other names: cursor, cline, codex, windsurf, antigravity, qcoder, generic
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
`install` detects installed hosts from local executables, including from a plain
|
|
58
|
+
terminal, and configures every host found. Automatic discovery currently supports
|
|
59
|
+
Claude Code on `PATH` or in its standard native install locations. Use
|
|
60
|
+
`--adapter <name>` for the other adapters or to override discovery. The existing
|
|
61
|
+
`SESSIONMEM_ADAPTER` environment override also works; the flag takes precedence.
|
|
62
|
+
If no supported host is found, the command says so and exits nonzero before
|
|
63
|
+
creating files. Discovery and configuration work fully offline.
|
|
64
|
+
|
|
65
|
+
This initializes the database and, for each selected adapter:
|
|
58
66
|
|
|
59
67
|
- Registers `sessionmem` with your AI tool's MCP host so the host can start it automatically.
|
|
60
68
|
- Creates `~/.sessionmem/config.json` with secret redaction enabled and a 90-day retention window, if the file doesn't already exist.
|
|
61
69
|
- 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.
|
|
70
|
+
- Installs Claude Code's SessionStart, SessionEnd, and PostToolUse hooks and prints what was configured per host. If a host fails, the remaining hosts are still attempted and the command exits nonzero.
|
|
62
71
|
|
|
63
72
|
### 3. Restart your AI tool
|
|
64
73
|
|
|
@@ -73,10 +82,12 @@ After restarting:
|
|
|
73
82
|
- In Claude Code, the installed PostToolUse, SessionEnd, and SessionStart hooks capture supported tool events, summarize them, and load prior memories automatically.
|
|
74
83
|
- 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.
|
|
75
84
|
|
|
76
|
-
Check
|
|
85
|
+
Check the installation offline:
|
|
77
86
|
|
|
78
87
|
```bash
|
|
79
|
-
sessionmem
|
|
88
|
+
sessionmem doctor
|
|
89
|
+
# For an explicitly selected host:
|
|
90
|
+
sessionmem doctor --adapter claude-code
|
|
80
91
|
```
|
|
81
92
|
|
|
82
93
|
---
|
|
@@ -139,36 +150,21 @@ The SQLite file is easy to inspect, back up, or delete, and you can use it acros
|
|
|
139
150
|
|
|
140
151
|
## Benchmark results
|
|
141
152
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
> **~85.6% reduction** in tokens compared to carrying full session history.
|
|
147
|
-
|
|
148
|
-
| | Tokens |
|
|
149
|
-
|---|---|
|
|
150
|
-
| Full session history (baseline) | 1,587 |
|
|
151
|
-
| What sessionmem injects at the start of your next session | 228 |
|
|
152
|
-
|
|
153
|
-
In this benchmark, about 1,600 tokens of session history become roughly 230 tokens of decisions, warnings, and facts for the next session.
|
|
154
|
-
|
|
155
|
-
### Retrieval accuracy
|
|
153
|
+
`npm run benchmark` evaluates conversational retrieval on the committed LoCoMo
|
|
154
|
+
corpus: 10 conversations, 272 sessions, 5,882 turns, and 1,527 scored questions.
|
|
155
|
+
It captures dialogue through the public `storeMemory` API and asks the original
|
|
156
|
+
questions through `retrieveMemories`, fully offline with no API keys.
|
|
156
157
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
| Recall | 100.0% |
|
|
163
|
-
| Precision | 33.3% |
|
|
164
|
-
|
|
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.
|
|
166
|
-
|
|
167
|
-
The benchmark is deterministic and reproducible:
|
|
158
|
+
The evaluation reports hit rate, precision, recall, complete-evidence retrieval,
|
|
159
|
+
and latency. Production retrieval uses offline BM25 ranking with English stemming;
|
|
160
|
+
conversational questions test recall beyond exact copies of memory text.
|
|
161
|
+
See [benchmark results](docs/benchmark.md) and the
|
|
162
|
+
[methodology, dataset license, and metric definitions](benchmarks/README.md).
|
|
168
163
|
|
|
169
164
|
```bash
|
|
170
|
-
npm run build
|
|
171
|
-
npm run benchmark
|
|
165
|
+
npm run build
|
|
166
|
+
npm run benchmark
|
|
167
|
+
npm run benchmark -- --output benchmarks/results/locomo.json
|
|
172
168
|
```
|
|
173
169
|
|
|
174
170
|
---
|
|
@@ -213,16 +209,22 @@ See [`docs/architecture.md`](docs/architecture.md) for the technical overview.
|
|
|
213
209
|
|
|
214
210
|
| Command | What it does |
|
|
215
211
|
|---------|--------------|
|
|
216
|
-
| `sessionmem install [--adapter <name>]` |
|
|
212
|
+
| `sessionmem install [--adapter <name>]` | Detect installed hosts, register sessionmem with each, and write default config; the flag selects one host. |
|
|
213
|
+
| `sessionmem doctor [--adapter <name>]` | Check the store, schema, transcript discovery, hooks, MCP wiring, and canary recall offline; print PASS/FAIL and repair hints. |
|
|
217
214
|
| `sessionmem uninstall [--adapter <name>] [--purge]` | Remove sessionmem from the host. `--purge` also deletes the local database. |
|
|
218
215
|
| `sessionmem run` | Start the MCP server. |
|
|
216
|
+
| `sessionmem ui [--port N]` | Open the local read-only web viewer (default port 3777). |
|
|
219
217
|
| `sessionmem ping` | Check the local version and database reachability. |
|
|
220
|
-
| `sessionmem search <query> [--limit <n>]` | Search memories by
|
|
218
|
+
| `sessionmem search <query> [--limit <n>] [--include-history]` | Search current memories by text query (limit 1–100, default 10). Include superseded facts with `--include-history`. |
|
|
221
219
|
| `sessionmem list` | List up to 200 recent memories for the current project; warns when truncated. |
|
|
220
|
+
| `sessionmem entities [--project <id>] [--json]` | List extracted entities, typed relations, and their source memory IDs. Defaults to the current project. |
|
|
221
|
+
| `sessionmem history [--project <id>] [--json]` | List supersession chains with old/new values, source memory IDs, reasons, and timestamps. |
|
|
222
222
|
| `sessionmem show <id>` | Show memory details (content capped at 2000 characters; use export for full content). |
|
|
223
223
|
| `sessionmem forget <id> [--force]` | Delete a memory by ID. |
|
|
224
224
|
| `sessionmem export [path]` | Export all current-project memories to JSON (default `~/.sessionmem/export-YYYY-MM-DD.json`). |
|
|
225
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. |
|
|
226
|
+
| `sessionmem scan [--host <host>] [--transcripts-dir <path>]` | Import existing Claude Code, Codex CLI, and Cursor transcripts into local session memories. Reports files found, sessions imported, memories created, skips, and errors. |
|
|
227
|
+
| `sessionmem watch [--once] [--interval <seconds>] [--host <host>] [--transcripts-dir <path>]` | Capture new or updated Claude Code, Codex CLI, and Cursor transcripts. Polls every 5 seconds by default; `--once` runs a single sweep. |
|
|
226
228
|
| `sessionmem stats` | Show memory statistics for the current project. |
|
|
227
229
|
| `sessionmem savings [--json]` | Show token savings from compression and injection, with percentage. |
|
|
228
230
|
| `sessionmem redact-scan [--apply]` | Scan stored memories for secrets; `--apply` redacts in place. |
|
|
@@ -253,9 +255,185 @@ for Windows paths. Imports accept CRLF and a UTF-8 BOM; convert other encodings
|
|
|
253
255
|
|
|
254
256
|
`SESSIONMEM_PROJECT_ID` pins the project bucket (useful when moving a repository);
|
|
255
257
|
otherwise it is derived from the repository root or working directory.
|
|
256
|
-
`SESSIONMEM_ADAPTER` selects the install/uninstall host unless `--adapter` is set.
|
|
258
|
+
`SESSIONMEM_ADAPTER` selects the install/doctor/uninstall host unless `--adapter` is set.
|
|
257
259
|
`SESSIONMEM_DEBUG=1` enables ingest-hook diagnostics on stderr.
|
|
258
260
|
|
|
261
|
+
### Inspect memories in your browser
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
sessionmem ui
|
|
265
|
+
sessionmem ui --port 8080
|
|
266
|
+
sessionmem ui --port 0 # Pick an available port
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
The command prints its URL and opens your default browser. It binds only to
|
|
270
|
+
`127.0.0.1`, needs no authentication, and serves all assets locally using Node's
|
|
271
|
+
built-in HTTP server. No external services, CDNs, or additional runtime dependencies
|
|
272
|
+
are used. If browser launch is unavailable, open the printed URL manually. Press
|
|
273
|
+
Ctrl-C to stop the server and close its database connection.
|
|
274
|
+
|
|
275
|
+
The viewer starts on the current project, with a selector for other projects in
|
|
276
|
+
the same store. Browse saved memories, search through the existing recall path,
|
|
277
|
+
inspect extracted entities and relations with their source memories, and follow
|
|
278
|
+
supersession chains with old/new values, source IDs, assertion times, detection
|
|
279
|
+
times, and partial/full status. Search returns up to 50 results and defaults to
|
|
280
|
+
current facts; select **Include superseded facts in search** to recall history.
|
|
281
|
+
Browsing lists all saved records, including superseded and expired ones, in pages
|
|
282
|
+
of 50. Source IDs open full memory details. Entity visibility and history links
|
|
283
|
+
use the same rules as `sessionmem entities` and `sessionmem history`.
|
|
284
|
+
|
|
285
|
+
The database is opened read-only. Browsing and on-demand search do not increment
|
|
286
|
+
access counts, migrate the schema, or change memories. Only HTTP GET and HEAD are
|
|
287
|
+
accepted; no write, delete, or edit routes are exposed. Responses are not cached,
|
|
288
|
+
and cross-origin browser requests and unexpected Host headers are rejected. Run
|
|
289
|
+
`sessionmem ping` first to initialize a missing store or upgrade an older schema.
|
|
290
|
+
The usual `SESSIONMEM_DB_PATH` and `SESSIONMEM_PROJECT_ID` overrides
|
|
291
|
+
apply. Anyone with access to this computer can read the local viewer while it is
|
|
292
|
+
running; stop it when finished inspecting private memories.
|
|
293
|
+
|
|
294
|
+
### Import existing sessions and watch for new ones
|
|
295
|
+
|
|
296
|
+
When a fact changes, sessionmem can link its replacement locally. For example, “I use VS Code” followed by “I switched to Neovim” supersedes the earlier editor choice. Recall excludes fully superseded memories by default. `retrieveMemories` accepts the optional `includeHistory: true`; `sessionmem search --include-history` searches earlier facts too. `sessionmem history` shows what replaced what. The old memory stays available through show, list, and export.
|
|
297
|
+
|
|
298
|
+
Detection uses deterministic entity/relation rules, shared purpose words, a small tool-category vocabulary, and explicit negation or replacement phrases. It needs no API or model. Unfamiliar wording can be missed, and ambiguous changes can be misclassified. A memory with independent claims remains searchable while any claim is current; history labels these partial supersessions. See [supersession details](docs/architecture.md#contradiction-and-supersession).
|
|
299
|
+
|
|
300
|
+
Run a scan to capture Claude Code, Codex CLI, and Cursor sessions from before sessionmem was installed:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
sessionmem scan
|
|
304
|
+
sessionmem watch --once
|
|
305
|
+
sessionmem watch --interval 10
|
|
306
|
+
sessionmem scan --host cursor
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Both commands sweep all detected transcript histories by default:
|
|
310
|
+
|
|
311
|
+
- Claude Code: `~/.claude/projects/**/*.jsonl`.
|
|
312
|
+
- Codex CLI: `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl`.
|
|
313
|
+
- Cursor: `User/globalStorage/state.vscdb` under
|
|
314
|
+
`~/Library/Application Support/Cursor` on macOS, `~/.config/Cursor` on Linux,
|
|
315
|
+
or `%APPDATA%/Cursor` on Windows.
|
|
316
|
+
|
|
317
|
+
Missing history directories are allowed. Use `--transcripts-dir "/path/to/history"`
|
|
318
|
+
to select another transcript tree; each JSONL file's host is detected from its records.
|
|
319
|
+
For Cursor, the override accepts its `User` directory or a `state.vscdb` path.
|
|
320
|
+
Use `--host cursor`, `--host codex`, or `--host claude-code` to select one host,
|
|
321
|
+
including when using an override. A Cursor User directory or `.vscdb` file is
|
|
322
|
+
also detected without `--host`. Files that do not match a supported format, or
|
|
323
|
+
the selected host, produce an explicit `unsupported format` error.
|
|
324
|
+
|
|
325
|
+
Both commands work offline without API keys and always use the local summarizer,
|
|
326
|
+
even if cloud summarization is enabled elsewhere. `sessionmem doctor` checks
|
|
327
|
+
transcript discovery for all three hosts, parses JSONL samples, and validates
|
|
328
|
+
Cursor's composer indexes and message rows when history is present.
|
|
329
|
+
|
|
330
|
+
Transcript user/assistant text, supported Claude tool uses (Bash, Edit, Write,
|
|
331
|
+
MultiEdit), and Codex function calls pass through the existing event-ingestion,
|
|
332
|
+
redaction, and session-summary pipeline. Codex tool events retain the native tool
|
|
333
|
+
name and `call_id`. Encrypted `agent_message` payloads and obvious injected machine
|
|
334
|
+
blocks, including plugin catalogues and environment context, are excluded. Each
|
|
335
|
+
session gets one summary memory, including short sessions with only one event.
|
|
336
|
+
Updated sessions refresh that summary. Project buckets come from
|
|
337
|
+
each Claude transcript's `cwd`, Codex `session_meta.payload.cwd`, or Cursor's
|
|
338
|
+
workspace path, using the repository root when available; the directory where you run scan/watch does not
|
|
339
|
+
select the project. `SESSIONMEM_PROJECT_ID` can explicitly pin all imports to one
|
|
340
|
+
bucket. Transcripts without capturable events are skipped. Claude transcripts
|
|
341
|
+
without a working directory are also skipped; Codex session metadata must contain
|
|
342
|
+
an ID and working directory. Nested subagent files get separate session summaries.
|
|
343
|
+
Summary memories record `sourceAdapter` as `claude-code`, `codex`, or `cursor`
|
|
344
|
+
according to the transcript host. Cursor session IDs come from `composerId`.
|
|
345
|
+
|
|
346
|
+
Cursor stores messages in `cursorDiskKV` bubble rows in its global SQLite
|
|
347
|
+
database. The importer supports workspace `composer.composerData` indexes
|
|
348
|
+
(Cursor 2.6 and earlier) and the central `composer.composerHeaders` index
|
|
349
|
+
(Cursor 3.0 and later). It captures up to 4,000 characters of user/assistant text
|
|
350
|
+
per bubble and excludes tool-only bubbles and internal tool/code fields. Missing
|
|
351
|
+
tables, index keys, or required conversation data produce an explicit
|
|
352
|
+
`unsupported format for cursor` error.
|
|
353
|
+
|
|
354
|
+
Cursor databases are never written. Each sweep copies the database and its WAL
|
|
355
|
+
to a private temporary directory, checking for concurrent changes during the
|
|
356
|
+
copy. SQLite opens the copy read-only, using immutable mode when URI support is
|
|
357
|
+
available and there is no WAL to replay. Cursor's live database is never opened by SQLite. Busy
|
|
358
|
+
copies are reported and retried. Each sweep reads a fresh snapshot; per-composer
|
|
359
|
+
checkpoints skip unchanged messages, and streamed bubble updates refresh the
|
|
360
|
+
existing event without duplicating it.
|
|
361
|
+
|
|
362
|
+
Codex resumes create new rollout files. Files with the same session ID are grouped,
|
|
363
|
+
and replayed events are deduplicated across files, sweeps, and restarts. A Codex
|
|
364
|
+
sub-agent keeps its own `session_meta.payload.id`, even when `parent_thread_id`
|
|
365
|
+
points to a parent session.
|
|
366
|
+
|
|
367
|
+
Re-running either command does not duplicate memories or transcript events.
|
|
368
|
+
File and composer checkpoints live in the selected memory database, and unchanged
|
|
369
|
+
files or composers are skipped across restarts. For Cursor, `files found` counts
|
|
370
|
+
global databases, and `skipped` counts unchanged or empty composers.
|
|
371
|
+
Stable event IDs also make interrupted imports safe to
|
|
372
|
+
retry. Partial or malformed JSONL lines are reported and retried on later sweeps;
|
|
373
|
+
valid events in the same file can still be captured. A single sweep exits with
|
|
374
|
+
status 1 if it encounters errors. The daemon keeps running and retries errors.
|
|
375
|
+
|
|
376
|
+
`watch` prints its first sweep and subsequent imports or errors. It runs in the
|
|
377
|
+
foreground until SIGINT or SIGTERM. For a background process:
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
nohup sessionmem watch > sessionmem-watch.log 2>&1 &
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
For macOS launchd, save this as
|
|
384
|
+
`~/Library/LaunchAgents/io.sessionmem.watch.plist`. Replace the executable path
|
|
385
|
+
with the output of `command -v sessionmem`, and adjust PATH for your Node install:
|
|
386
|
+
|
|
387
|
+
```xml
|
|
388
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
389
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
390
|
+
<plist version="1.0">
|
|
391
|
+
<dict>
|
|
392
|
+
<key>Label</key><string>io.sessionmem.watch</string>
|
|
393
|
+
<key>ProgramArguments</key>
|
|
394
|
+
<array><string>/opt/homebrew/bin/sessionmem</string><string>watch</string></array>
|
|
395
|
+
<key>EnvironmentVariables</key>
|
|
396
|
+
<dict><key>PATH</key><string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string></dict>
|
|
397
|
+
<key>RunAtLoad</key><true/>
|
|
398
|
+
<key>KeepAlive</key><true/>
|
|
399
|
+
</dict>
|
|
400
|
+
</plist>
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/io.sessionmem.watch.plist
|
|
405
|
+
# Stop and unload:
|
|
406
|
+
launchctl bootout "gui/$(id -u)/io.sessionmem.watch"
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
For a Linux systemd user service, save this as
|
|
410
|
+
`~/.config/systemd/user/sessionmem-watch.service`. Replace the executable path
|
|
411
|
+
and adjust PATH for your Node install:
|
|
412
|
+
|
|
413
|
+
```ini
|
|
414
|
+
[Unit]
|
|
415
|
+
Description=Capture Claude Code, Codex, and Cursor transcripts with sessionmem
|
|
416
|
+
|
|
417
|
+
[Service]
|
|
418
|
+
ExecStart=/usr/local/bin/sessionmem watch
|
|
419
|
+
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
|
420
|
+
Restart=on-failure
|
|
421
|
+
RestartSec=5
|
|
422
|
+
|
|
423
|
+
[Install]
|
|
424
|
+
WantedBy=default.target
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
systemctl --user daemon-reload
|
|
429
|
+
systemctl --user enable --now sessionmem-watch.service
|
|
430
|
+
journalctl --user -u sessionmem-watch.service -f
|
|
431
|
+
# Stop and disable:
|
|
432
|
+
systemctl --user disable --now sessionmem-watch.service
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
These are optional examples; `sessionmem install` does not register a daemon.
|
|
436
|
+
|
|
259
437
|
---
|
|
260
438
|
|
|
261
439
|
## Privacy, secrets, and your data
|
|
@@ -296,13 +474,13 @@ Old decisions and duplicate facts can accumulate in a memory store, causing the
|
|
|
296
474
|
sessionmem retention prune --force # actually deletes
|
|
297
475
|
```
|
|
298
476
|
|
|
299
|
-
2. **Importance-weighted ranking**: retrieval ranks memories by
|
|
477
|
+
2. **Importance-weighted ranking**: retrieval ranks memories by offline BM25 lexical relevance, entity overlap, recency, and importance. Older, less important notes rank lower even before they are pruned.
|
|
300
478
|
|
|
301
479
|
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.
|
|
302
480
|
|
|
303
481
|
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.
|
|
304
482
|
|
|
305
|
-
The benchmark
|
|
483
|
+
The [conversational benchmark](benchmarks/README.md) measures retrieval quality separately from retention and startup token budgets.
|
|
306
484
|
|
|
307
485
|
Export your memories before pruning if you want to keep a copy:
|
|
308
486
|
|
|
@@ -359,7 +537,7 @@ Other tools that implement the [Model Context Protocol](https://modelcontextprot
|
|
|
359
537
|
## Further documentation
|
|
360
538
|
|
|
361
539
|
- [Architecture](docs/architecture.md): how the core engine, adapters, CLI, and SQLite storage fit together.
|
|
362
|
-
- [Benchmark](docs/benchmark.md):
|
|
540
|
+
- [Benchmark](docs/benchmark.md): conversational retrieval results and reproduction steps.
|
|
363
541
|
- [Privacy and retention](docs/privacy-and-retention.md): secret redaction, retention pruning, and config.
|
|
364
542
|
- [Team mode](docs/team-mode.md): shared-path team memory.
|
|
365
543
|
- [Cloud summarization](docs/cloud-summarization.md): the opt-in cloud summarization path.
|
|
@@ -372,13 +550,27 @@ Other tools that implement the [Model Context Protocol](https://modelcontextprot
|
|
|
372
550
|
|
|
373
551
|
For install failures, adapter issues, missing session data, or `better-sqlite3` native-build problems, see [`docs/troubleshooting.md`](docs/troubleshooting.md).
|
|
374
552
|
|
|
375
|
-
|
|
553
|
+
Start with offline diagnostics:
|
|
376
554
|
|
|
377
555
|
```bash
|
|
378
|
-
sessionmem
|
|
379
|
-
sessionmem
|
|
556
|
+
sessionmem doctor
|
|
557
|
+
sessionmem doctor --adapter claude-code # explicit host override
|
|
380
558
|
```
|
|
381
559
|
|
|
560
|
+
`doctor` reports a separate PASS or FAIL for the memory store, schema, transcript
|
|
561
|
+
discovery for Claude Code and Codex, hook definitions shipped with the current
|
|
562
|
+
version, configured MCP command, and a
|
|
563
|
+
canary write/recall. It exits nonzero if any check fails and prints fix hints,
|
|
564
|
+
including exact commands for safe repairs. It does not create a missing store or
|
|
565
|
+
apply migrations. Its temporary canary is isolated from your projects and rolled
|
|
566
|
+
back even on failure. The MCP check starts the installed local command and verifies
|
|
567
|
+
its version, tools, and a stats response; it does not require a running agent.
|
|
568
|
+
|
|
569
|
+
Use `sessionmem ping` to initialize a missing store or apply pending schema
|
|
570
|
+
migrations, or `sessionmem install --adapter claude-code` to refresh Claude Code's
|
|
571
|
+
MCP registration and hooks. Run repairs with the same `SESSIONMEM_DB_PATH` as
|
|
572
|
+
doctor when using a custom store.
|
|
573
|
+
|
|
382
574
|
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.
|
|
383
575
|
|
|
384
576
|
To check database access and memory counts:
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5
|
+
import { IDEInstaller, isManagedClaudeHook } from "./ide/installer.js";
|
|
6
|
+
import { resolveDbPath } from "../cli/context.js";
|
|
7
|
+
function object(value) {
|
|
8
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
9
|
+
? value
|
|
10
|
+
: {};
|
|
11
|
+
}
|
|
12
|
+
export function checkHooks(installation) {
|
|
13
|
+
const expected = installation.hooks;
|
|
14
|
+
if (!expected)
|
|
15
|
+
return "This host uses MCP tools; no lifecycle hooks are required";
|
|
16
|
+
const config = IDEInstaller.parseJsonc(readFileSync(expected.path, "utf8"));
|
|
17
|
+
if (config.disableAllHooks === true)
|
|
18
|
+
throw new Error("Claude Code has disableAllHooks enabled");
|
|
19
|
+
const hooks = object(config.hooks);
|
|
20
|
+
for (const spec of expected.entries) {
|
|
21
|
+
const entries = Array.isArray(hooks[spec.event]) ? hooks[spec.event] : [];
|
|
22
|
+
const managed = entries.flatMap((value) => {
|
|
23
|
+
const entry = object(value);
|
|
24
|
+
return Array.isArray(entry.hooks) ? entry.hooks.filter((value) => isManagedClaudeHook(object(value).command, spec.command)).map((hook) => ({ entry, hook: object(hook) })) : [];
|
|
25
|
+
});
|
|
26
|
+
const matches = managed.filter(({ entry, hook }) => {
|
|
27
|
+
if (entry.matcher !== spec.matcher || entry.async === true)
|
|
28
|
+
return false;
|
|
29
|
+
return hook.type === "command" && hook.command === spec.command && hook.async !== true;
|
|
30
|
+
});
|
|
31
|
+
if (managed.length !== 1 || matches.length !== 1)
|
|
32
|
+
throw new Error(`${spec.event} hook is missing, duplicated, or differs from the current definition`);
|
|
33
|
+
}
|
|
34
|
+
return `SessionStart, SessionEnd, PostToolUse match current hook definitions (${expected.path})`;
|
|
35
|
+
}
|
|
36
|
+
/** Check the installed launch entry before executing the known local command. */
|
|
37
|
+
export function readMcpLaunch(installation) {
|
|
38
|
+
const expected = installation.mcp;
|
|
39
|
+
const content = readFileSync(expected.path, "utf8");
|
|
40
|
+
let entry;
|
|
41
|
+
if (expected.format === "toml") {
|
|
42
|
+
// The Codex installer writes this small TOML section with JSON-compatible
|
|
43
|
+
// quoted values. Refuse unfamiliar syntax rather than claiming it works.
|
|
44
|
+
const section = content.match(/^\s*\[mcp_servers\.sessionmem\][^\S\r\n]*\r?\n([\s\S]*?)(?=^\s*\[|$(?![\s\S]))/m)?.[1];
|
|
45
|
+
if (!section)
|
|
46
|
+
throw new Error("sessionmem MCP section is missing");
|
|
47
|
+
if (/^\s*\[mcp_servers\.sessionmem\./m.test(content) || /^\s*(?:env|env_vars|cwd|url)\s*=/m.test(section)) {
|
|
48
|
+
throw new Error("Custom Codex launch settings require manual verification; doctor supports the installer-generated command and args");
|
|
49
|
+
}
|
|
50
|
+
if (/^\s*enabled\s*=\s*false\s*$/m.test(section))
|
|
51
|
+
throw new Error("sessionmem MCP server is disabled");
|
|
52
|
+
entry = {
|
|
53
|
+
command: JSON.parse(section.match(/^\s*command\s*=\s*(.+)$/m)?.[1] ?? "null"),
|
|
54
|
+
args: JSON.parse(section.match(/^\s*args\s*=\s*(.+)$/m)?.[1] ?? "null"),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const config = IDEInstaller.parseJsonc(content);
|
|
59
|
+
entry = object(object(config.mcpServers).sessionmem);
|
|
60
|
+
}
|
|
61
|
+
if (entry.disabled === true)
|
|
62
|
+
throw new Error("sessionmem MCP server is disabled");
|
|
63
|
+
if (entry.type !== undefined && entry.type !== "stdio")
|
|
64
|
+
throw new Error("sessionmem MCP server must use the local stdio transport");
|
|
65
|
+
if (entry.command !== expected.command || JSON.stringify(entry.args) !== JSON.stringify(expected.args)) {
|
|
66
|
+
throw new Error(`sessionmem MCP launch entry differs from the current definition (${expected.path})`);
|
|
67
|
+
}
|
|
68
|
+
if (entry.env !== undefined && (entry.env === null || typeof entry.env !== "object" || Array.isArray(entry.env))) {
|
|
69
|
+
throw new Error("sessionmem MCP environment must be an object");
|
|
70
|
+
}
|
|
71
|
+
const env = object(entry.env);
|
|
72
|
+
if (Object.values(env).some((value) => typeof value !== "string")) {
|
|
73
|
+
throw new Error("sessionmem MCP environment values must be strings");
|
|
74
|
+
}
|
|
75
|
+
return { command: expected.command, args: expected.args, env: env };
|
|
76
|
+
}
|
|
77
|
+
export async function probeMcpWiring(launch, dbPath, version) {
|
|
78
|
+
if (launch.env?.SESSIONMEM_DB_PATH && resolve(resolveDbPath(launch.env.SESSIONMEM_DB_PATH)) !== resolve(dbPath)) {
|
|
79
|
+
throw new Error("MCP configuration selects a different SESSIONMEM_DB_PATH; run doctor with that path");
|
|
80
|
+
}
|
|
81
|
+
const inherited = Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] !== undefined));
|
|
82
|
+
const client = new Client({ name: "sessionmem-doctor", version });
|
|
83
|
+
const transport = new StdioClientTransport({
|
|
84
|
+
command: launch.command,
|
|
85
|
+
args: launch.args,
|
|
86
|
+
env: { ...inherited, ...launch.env, SESSIONMEM_DB_PATH: dbPath },
|
|
87
|
+
stderr: "ignore",
|
|
88
|
+
});
|
|
89
|
+
try {
|
|
90
|
+
await client.connect(transport, { timeout: 5000 });
|
|
91
|
+
const server = client.getServerVersion();
|
|
92
|
+
if (server?.name !== "sessionmem" || server.version !== version) {
|
|
93
|
+
throw new Error(`MCP server version does not match sessionmem ${version}`);
|
|
94
|
+
}
|
|
95
|
+
const tools = await client.listTools({}, { timeout: 5000 });
|
|
96
|
+
for (const name of ["stats", "storeMemory", "retrieveMemories"]) {
|
|
97
|
+
if (!tools.tools.some((tool) => tool.name === name))
|
|
98
|
+
throw new Error(`MCP tool ${name} is missing`);
|
|
99
|
+
}
|
|
100
|
+
const result = await client.callTool({ name: "stats", arguments: {} }, undefined, { timeout: 5000 });
|
|
101
|
+
const payload = Array.isArray(result.content) ? object(result.content.find((part) => object(part).type === "text")) : {};
|
|
102
|
+
if (result.isError || typeof payload.text !== "string" || !JSON.parse(payload.text).ok) {
|
|
103
|
+
throw new Error("MCP stats tool failed");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
await transport.close();
|
|
108
|
+
}
|
|
109
|
+
}
|
package/dist/adapters/generic.js
CHANGED
|
@@ -69,6 +69,7 @@ const TOOL_DEFINITIONS = [
|
|
|
69
69
|
mode: retrieveMemoriesRequestSchema.shape.mode.describe("'auto' for the standard startup context-load path. 'on-demand' for an explicit mid-session retrieval triggered by a specific task or question."),
|
|
70
70
|
depth: retrieveMemoriesRequestSchema.shape.depth.describe("'default' for standard semantic search. 'deep' for a broader sweep that surfaces less-similar memories — use when the topic is new or unfamiliar."),
|
|
71
71
|
tags: retrieveMemoriesRequestSchema.shape.tags.describe("Optional tag filter. When provided, only return memories that have ALL listed tags."),
|
|
72
|
+
includeHistory: retrieveMemoriesRequestSchema.shape.includeHistory.describe("Include superseded facts for historical questions. Omit for current facts only."),
|
|
72
73
|
},
|
|
73
74
|
},
|
|
74
75
|
{
|
|
@@ -209,18 +210,21 @@ export class GenericMCPAdapter {
|
|
|
209
210
|
guidanceTargets() {
|
|
210
211
|
return [join(process.cwd(), "AGENTS.md")];
|
|
211
212
|
}
|
|
213
|
+
installation() {
|
|
214
|
+
return { mcp: { path: join(process.cwd(), ".mcp.json"), command: "sessionmem", args: ["run"] } };
|
|
215
|
+
}
|
|
212
216
|
/**
|
|
213
217
|
* Fallback for hosts that aren't specifically detected: register sessionmem
|
|
214
218
|
* in a project-local `.mcp.json` (the de-facto generic MCP config format).
|
|
215
219
|
*/
|
|
216
220
|
async install() {
|
|
217
|
-
const configPath =
|
|
221
|
+
const configPath = this.installation().mcp.path;
|
|
218
222
|
return IDEInstaller.injectMcpConfig(configPath, "sessionmem", "sessionmem", [
|
|
219
223
|
"run",
|
|
220
224
|
]);
|
|
221
225
|
}
|
|
222
226
|
async uninstall() {
|
|
223
|
-
const configPath =
|
|
227
|
+
const configPath = this.installation().mcp.path;
|
|
224
228
|
return IDEInstaller.removeMcpConfig(configPath, "sessionmem");
|
|
225
229
|
}
|
|
226
230
|
async call(_method, _request) {
|
|
@@ -11,13 +11,16 @@ import { IDEInstaller } from "../ide/installer.js";
|
|
|
11
11
|
const ANTIGRAVITY_MCP_CONFIG = [".gemini", "config", "mcp_config.json"];
|
|
12
12
|
export class AntigravityAdapter extends GenericMCPAdapter {
|
|
13
13
|
name = "Antigravity";
|
|
14
|
+
installation() {
|
|
15
|
+
return { mcp: { path: join(homedir(), ...ANTIGRAVITY_MCP_CONFIG), command: "sessionmem", args: ["run"] } };
|
|
16
|
+
}
|
|
14
17
|
// Capabilities inherited from GenericMCPAdapter (tools only).
|
|
15
18
|
/** Antigravity reads AGENTS.md-style guidance from its global config dir. */
|
|
16
19
|
guidanceTargets() {
|
|
17
20
|
return [join(homedir(), ".antigravity", "AGENTS.md")];
|
|
18
21
|
}
|
|
19
22
|
async install() {
|
|
20
|
-
const configPath =
|
|
23
|
+
const configPath = this.installation().mcp.path;
|
|
21
24
|
return IDEInstaller.injectMcpConfig(configPath, "sessionmem", "sessionmem", [
|
|
22
25
|
"run",
|
|
23
26
|
]);
|
|
@@ -21,15 +21,30 @@ export class ClaudeCodeAdapter extends GenericMCPAdapter {
|
|
|
21
21
|
settingsPath() {
|
|
22
22
|
return join(homedir(), ".claude", "settings.json");
|
|
23
23
|
}
|
|
24
|
+
installation() {
|
|
25
|
+
const isWindows = process.platform === "win32";
|
|
26
|
+
return {
|
|
27
|
+
mcp: {
|
|
28
|
+
path: join(homedir(), ".claude.json"),
|
|
29
|
+
command: isWindows ? "cmd" : "sessionmem",
|
|
30
|
+
args: isWindows ? ["/c", "sessionmem", "run"] : ["run"],
|
|
31
|
+
},
|
|
32
|
+
hooks: {
|
|
33
|
+
path: this.settingsPath(),
|
|
34
|
+
entries: [
|
|
35
|
+
{ event: "SessionStart", command: SESSIONMEM_HOOK_COMMAND },
|
|
36
|
+
{ event: "SessionEnd", command: SESSIONMEM_SESSION_END_HOOK_COMMAND },
|
|
37
|
+
{ event: "PostToolUse", command: SESSIONMEM_POST_TOOL_HOOK_COMMAND, matcher: SESSIONMEM_POST_TOOL_MATCHER },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
24
42
|
async install() {
|
|
25
|
-
const configPath = join(homedir(), ".claude.json");
|
|
26
43
|
// On Windows, the globally-installed `sessionmem` bin is a `.cmd` shim.
|
|
27
44
|
// Claude Code spawns MCP servers WITHOUT a shell, and `.cmd` shims require
|
|
28
45
|
// cmd.exe to execute, so a bare `command: "sessionmem"` fails to start.
|
|
29
46
|
// Route through `cmd /c sessionmem run` so the shim is resolved correctly.
|
|
30
|
-
const
|
|
31
|
-
const command = isWindows ? "cmd" : "sessionmem";
|
|
32
|
-
const args = isWindows ? ["/c", "sessionmem", "run"] : ["run"];
|
|
47
|
+
const { path: configPath, command, args } = this.installation().mcp;
|
|
33
48
|
const mcpOk = await IDEInstaller.injectMcpConfig(configPath, "sessionmem", command, args);
|
|
34
49
|
// Register a SessionStart hook so prior memories are injected automatically
|
|
35
50
|
// at the start of EVERY Claude Code session. This is the deterministic
|