project-memory-mcp 0.1.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 ADDED
@@ -0,0 +1,640 @@
1
+ # Project Memory MCP Server
2
+
3
+ Shared, project-scoped memory for **Claude Code**, **Codex**, and **Gemini CLI** — so all three agents remember the same context.
4
+
5
+ ```
6
+ You (any CLI) ──► MCP Server ──► .ai/memory.json (per project)
7
+ ```
8
+
9
+ This repository ships a Node.js MCP server that persists project facts/decisions into `<project>/.ai/memory.json`. Any supported CLI can read/write the same file, so switching between Claude, Codex, and Gemini feels stateful: load context with `memory_get_bundle`, perform work, and save what changed with `memory_save`.
10
+
11
+ ## Why Project Memory MCP?
12
+
13
+ - **Shared context** – one `.ai/memory.json` per repo keeps Claude, Codex, and Gemini in sync.
14
+ - **Batteries included tools** – search, bundle generation, pinning, proposals, auto-compaction.
15
+ - **Silent capture** – optional hooks auto-save versions, deps, and commits at the end of a session.
16
+ - **CLI + programmatic** – run the stdio server via `project-memory-mcp` or import the build artifacts in Node projects.
17
+
18
+ ## Installation Options
19
+
20
+ Pick the workflow that fits your team:
21
+
22
+ ### 1. Global CLI (recommended for everyday use)
23
+
24
+ ```bash
25
+ npm install -g project-memory-mcp
26
+ project-memory-mcp setup
27
+ ```
28
+
29
+ This installs the CLI once, adds a globally available `project-memory-mcp` command, and stores the server inside your global npm cache.
30
+
31
+ ### 2. On-demand via npx (no global install)
32
+
33
+ ```bash
34
+ npx project-memory-mcp setup
35
+ ```
36
+
37
+ npx downloads the package on first run and reuses the cached copy afterwards. Great for trying the tool or wiring up a single machine.
38
+
39
+ ### 3. Local clone (development / hacking)
40
+
41
+ ```bash
42
+ git clone https://github.com/nicobailon/project-memory-mcp-js.git
43
+ cd project-memory-mcp-js
44
+ npm install
45
+ npm run build
46
+ ```
47
+
48
+ Then point your CLI at `node /absolute/path/dist/server.js`. This is also how you contribute and run the hook tests. See [Local development](#local-development) for details.
49
+
50
+ ## Quick Start (any install path)
51
+
52
+ 1. **Run the setup wizard** inside the repo you want to enable (or pass `--project /path`):
53
+ ```bash
54
+ project-memory-mcp setup # global install
55
+ # or
56
+ npx project-memory-mcp setup # on-demand
57
+ ```
58
+ 2. Choose how the CLI should spawn the server (`npx`, global binary, `node dist/server.js`, or a custom command).
59
+ 3. Pick which CLIs to configure (Claude Code, Gemini CLI, Codex CLI).
60
+ 4. Ask your CLI: “Call `memory_status` and show the output.” You should see the correct `projectRoot` and `.ai/memory.json`.
61
+
62
+ Prefer the manual route or need automation flags? Jump to [Local development](#local-development) and `docs/LOCAL_SETUP.md`.
63
+
64
+ ## Run it as a project dependency
65
+
66
+ If you want every teammate to install the server via your project’s `package.json`, add it as a dev dependency:
67
+
68
+ ```bash
69
+ npm install --save-dev project-memory-mcp
70
+ ```
71
+
72
+ Now you can expose scripts:
73
+
74
+ ```jsonc
75
+ {
76
+ "scripts": {
77
+ "memory:serve": "project-memory-mcp serve",
78
+ "memory:setup": "project-memory-mcp setup --yes --runner node"
79
+ }
80
+ }
81
+ ```
82
+
83
+ From there your CLI entries can point at `npx project-memory-mcp` or `node ./node_modules/project-memory-mcp/dist/server.js`. The published package bundles the `dist/` tree, so no build step is needed on consumer machines.
84
+
85
+ ## Local development
86
+
87
+ Working out of this repo?
88
+
89
+ 1. `npm install`
90
+ 2. `npm run build`
91
+ 3. `npm run test:hooks` (optional smoke test for hook flows)
92
+ 4. Wire your CLIs using `node dist/server.js` or `project-memory-mcp` (after linking via `npm link`)
93
+
94
+ See [`docs/LOCAL_SETUP.md`](docs/LOCAL_SETUP.md) for the long-form guide, env overrides, troubleshooting, and hook wiring defaults.
95
+
96
+ ## CLI Setup & Tool Map
97
+
98
+ Already installed globally, via npx, or from source? Run the wizard from the repo you want to enable:
99
+
100
+ ```bash
101
+ # Install globally (recommended)
102
+ npm install -g project-memory-mcp
103
+
104
+ # Or use npx (no install needed)
105
+ npx project-memory-mcp
106
+
107
+ # Or clone and run locally
108
+ git clone https://github.com/nicobailon/project-memory-mcp-js.git
109
+ cd project-memory-mcp-js && npm install && npm run build && npm start
110
+ ```
111
+
112
+ | What you want | Tool to call |
113
+ |---|---|
114
+ | Check setup is correct | `memory_status` |
115
+ | Load context before a task | `memory_get_bundle` |
116
+ | Save something to memory | `memory_save` |
117
+ | Search past memory | `memory_search` |
118
+
119
+ That's the core loop: **get bundle → do work → save what matters**.
120
+
121
+ ---
122
+
123
+ ## Quick setup (per project)
124
+
125
+ 1. **Install the package** (pick one)
126
+ ```bash
127
+ npm install -g project-memory-mcp # global install
128
+ # or use npx — no install needed
129
+ ```
130
+
131
+ 2. **Run the guided setup (recommended)**
132
+ ```bash
133
+ # from the repo you want to wire up (or pass --project)
134
+ npx project-memory-mcp setup
135
+ # or, after a global install:
136
+ project-memory-mcp setup
137
+ ```
138
+ - Prompts for the project directory (defaults to your current working folder).
139
+ - Lets you choose how to launch the MCP server (`npx`, global binary, `node /path/to/dist/server.js`, or a custom command).
140
+ - Lets you pick which CLIs to configure (Claude Code, Gemini CLI, Codex CLI).
141
+ - Automatically updates `~/.claude.json` (with a `.bak` backup) and runs the necessary `gemini mcp` / `codex mcp` commands so they point at the right project.
142
+ - Flags: `--project /path`, `--cli claude,gemini`, `--runner global`, `--yes`, `--command`, and `--args` let you script it or skip prompts. Run `project-memory-mcp setup --help` for the full list.
143
+ - Requires the corresponding CLIs to already be installed and on your `PATH`.
144
+
145
+ <details>
146
+ <summary>Prefer the fully manual wiring? Expand for the original commands.</summary>
147
+
148
+ - **Claude Code** – edit `~/.claude.json` and add:
149
+ ```json
150
+ "mcpServers": {
151
+ "project-memory": {
152
+ "command": "npx",
153
+ "args": ["project-memory-mcp"],
154
+ "cwd": "/path/to/my-app"
155
+ }
156
+ }
157
+ ```
158
+ Restart Claude inside `/path/to/my-app`.
159
+ - **Gemini CLI**
160
+ ```bash
161
+ cd /path/to/my-app
162
+ gemini mcp add project-memory npx project-memory-mcp --trust
163
+ gemini mcp list # should show CONNECTED
164
+ /mcp # (in-session) inspect tools/resources
165
+ ```
166
+ Edit `.gemini/settings.json` if you need custom `cwd` or env vars.
167
+ - **Codex CLI**
168
+ ```bash
169
+ cd /path/to/my-app
170
+ codex mcp add project-memory npx project-memory-mcp
171
+ codex mcp list
172
+ ```
173
+ Always start `codex` from `/path/to/my-app` so the server detects the right root.
174
+ </details>
175
+
176
+ 3. **Verify routing**
177
+ - In that project, ask your CLI to `Call memory_status and show the output.` You should see `projectRoot` = `/path/to/my-app` and `memoryFilePath` = `/path/to/my-app/.ai/memory.json`. If not, fix the MCP config (`cwd`, env vars, etc.) before continuing.
178
+ - Need a reminder of commands? Run `memory_help` anytime for the cheat sheet.
179
+
180
+ > Prefer the long-form guide (env overrides, troubleshooting, auto hooks)? See `docs/LOCAL_SETUP.md`.
181
+
182
+ ---
183
+
184
+ ## Daily Workflow
185
+
186
+ ```
187
+ 1. memory_get_bundle → load relevant context for your task
188
+ 2. ... do your work ...
189
+ 3. memory_save → store what changed / what you learned
190
+ ```
191
+
192
+ Any model (Claude, Codex, Gemini) can then pick up where another left off.
193
+
194
+ <details>
195
+ <summary><strong>Example: save context</strong></summary>
196
+
197
+ ```text
198
+ Call MCP tool `memory_save` with:
199
+ - title: "Project runtime versions"
200
+ - type: "fact"
201
+ - content: "PHP 7.4.33, Laravel 5.6.40"
202
+ - tags: ["php","laravel","environment"]
203
+ - source: "claude"
204
+ ```
205
+
206
+ </details>
207
+
208
+ <details>
209
+ <summary><strong>Example: load context</strong></summary>
210
+
211
+ ```text
212
+ Call `memory_get_bundle` with prompt "I am fixing login API bugs" and maxItems 12.
213
+ ```
214
+
215
+ </details>
216
+
217
+ ### Automatic compaction & archives
218
+
219
+ - The server automatically compacts when more than **400** active items exist (see `CONFIG.autoCompact`). Oldest entries are moved to `.ai/memory-archive.json`, and a summary note is added so you still know what was archived.
220
+ - To trigger compaction manually (or tune thresholds per project), call `memory_compact` and provide overrides such as `{ "maxItems": 250 }`.
221
+ - Archived content stays available for future manual review—`memory_get_bundle` only surfaces the most relevant active notes while summaries keep the historical trail discoverable.
222
+ - Want zero-click context? Configure the optional `dist/hooks/auto-memory.js` script (`... start` on `UserPromptSubmit`, `... stop` on `Stop`) to auto-inject bundles and auto-save transcripts.
223
+
224
+ ---
225
+
226
+ ## Reference
227
+
228
+ <details>
229
+ <summary><strong>All Tools</strong></summary>
230
+
231
+ ### Read
232
+
233
+ | Tool | Purpose |
234
+ |---|---|
235
+ | `memory_help` | Quick-start usage tips + sample prompts |
236
+ | `memory_status` | Show resolved project root, memory file path, counts, revision |
237
+ | `memory_search` | Keyword/tag search over saved items |
238
+ | `memory_get_bundle` | Compact ranked memory bundle for current task |
239
+ | `memory_list_proposals` | List proposals by status |
240
+
241
+ ### Write (direct)
242
+
243
+ | Tool | Purpose |
244
+ |---|---|
245
+ | `memory_save` | Save memory item immediately (no approval step) |
246
+ | `memory_pin` | Pin or unpin an existing item |
247
+
248
+ ### Write (gated)
249
+
250
+ | Tool | Purpose |
251
+ |---|---|
252
+ | `memory_propose` | Create proposals (pending approval) |
253
+ | `memory_approve_proposal` | Approve/reject proposal, optional edits |
254
+
255
+ ### Maintenance
256
+
257
+ | Tool | Purpose |
258
+ |---|---|
259
+ | `memory_compact` | Archive older items into `.ai/memory-archive.json` and add a summary note, keeping the active store lean |
260
+
261
+ All write tools accept an optional `projectRoot` input for multi-project routing.
262
+
263
+ </details>
264
+
265
+ ### Tool invocation cheatsheet
266
+
267
+ | Tool | Minimal CLI prompt | Notes |
268
+ |---|---|---|
269
+ | `memory_status` | `Call memory_status and show the output.` | Confirms `projectRoot` + `.ai/memory.json` before you start. |
270
+ | `memory_help` | `Call memory_help.` | Returns this cheat sheet + best-practice prompts. |
271
+ | `memory_get_bundle` | `Call memory_get_bundle with {"prompt":"Fixing login bugs"}` | Adjust `maxItems`, `types`, or `projectRoot` per task. |
272
+ | `memory_save` | `Call memory_save with {"title":"New API",...}` | Provide `content`; optionally `tags`, `pinned`, `source`. |
273
+ | `memory_search` | `Call memory_search with {"query":"redis"}` | Add `includeContent`, `tags`, or `types` filters. |
274
+ | `memory_propose` | `Call memory_propose with {"items":[...],"reason":"code review"}` | Use when you want an approval step before saving. |
275
+ | `memory_approve_proposal` | `Call memory_approve_proposal with {"proposalId":"prop_...","action":"approve"}` | Include `edits` to tweak proposal content before approval. |
276
+ | `memory_pin` | `Call memory_pin with {"itemId":"mem_...","pinned":true}` | Pinning keeps key notes surfaced in bundles. |
277
+ | `memory_compact` | `Call memory_compact with {"maxItems":250}` | Keeps the active store lean; omit payload to use defaults. |
278
+
279
+ Usage tips:
280
+
281
+ - **Claude Code / Codex CLI**: type the phrase exactly (e.g. “Call memory_status…”). They’ll run the tool and return the output inline.
282
+ - **Gemini CLI**: either type the sentence or run `memory_compact {"maxItems":250}` directly in the terminal. `/mcp` shows the live list of tools + descriptions.
283
+
284
+ <details>
285
+ <summary><strong>Data Model</strong></summary>
286
+
287
+ `memory.json` structure:
288
+
289
+ | Field | Description |
290
+ |---|---|
291
+ | `version` | Format version (currently `1`) |
292
+ | `project` | Metadata: `id`, `root`, timestamps |
293
+ | `items` | Approved/saved memory entries |
294
+ | `proposals` | Gated workflow entries (`pending`, `approved`, `rejected`) |
295
+ | `revision` | Incremented on every write |
296
+
297
+ </details>
298
+
299
+ <details>
300
+ <summary><strong>Memory Resolution Rules</strong></summary>
301
+
302
+ `projectRoot` is resolved in this priority:
303
+
304
+ 1. Tool input `projectRoot` (if provided)
305
+ 2. `MEMORY_PROJECT_ROOT` env var
306
+ 3. Nearest ancestor with `.git`
307
+ 4. Current working directory
308
+
309
+ Storage path:
310
+
311
+ - `MEMORY_FILE_PATH` env var if set (relative paths resolve from project root)
312
+ - Otherwise `<projectRoot>/.ai/memory.json`
313
+
314
+ </details>
315
+
316
+ <details>
317
+ <summary><strong>Update and Delete</strong></summary>
318
+
319
+ There is no `memory_update` or `memory_delete` tool yet.
320
+
321
+ **To update:** save a corrected item with `memory_save`. Add a tag like `supersedes:<old_id>` and optionally `memory_pin` the new one.
322
+
323
+ **To delete all:** `rm -f <projectRoot>/.ai/memory.json`
324
+
325
+ **To delete one item:** manually edit the `items` array in the JSON file.
326
+
327
+ </details>
328
+
329
+ <details>
330
+ <summary><strong>Environment Variables</strong></summary>
331
+
332
+ | Variable | Purpose |
333
+ |---|---|
334
+ | `MEMORY_PROJECT_ROOT` | Force a specific project root |
335
+ | `MEMORY_FILE_PATH` | Override the memory file path (relative resolves from project root) |
336
+
337
+ ```bash
338
+ MEMORY_PROJECT_ROOT=/path/to/project npm run start
339
+ ```
340
+
341
+ </details>
342
+
343
+ <details>
344
+ <summary><strong>File Layout</strong></summary>
345
+
346
+ ```text
347
+ server.ts # entrypoint (source)
348
+ dist/server.js # entrypoint (runtime)
349
+ src/main.ts # MCP bootstrap and transport connection
350
+ src/tools.ts # tool registration and handlers
351
+ src/storage.ts # lock, load/write, atomic persistence
352
+ src/runtime.ts # project root/path resolution
353
+ src/domain.ts # scoring/tokenization/validation helpers
354
+ src/config.ts # constants
355
+ src/logger.ts # stderr logger
356
+ .ai/memory.json # persisted project memory (per project)
357
+ ```
358
+
359
+ Add `.ai/` to `.gitignore` if you don't want memory committed to the repo.
360
+
361
+ </details>
362
+
363
+ ---
364
+
365
+ ## Troubleshooting
366
+
367
+ | Problem | Fix |
368
+ |---|---|
369
+ | `memory.json` not created | Call `memory_status` — project root is probably wrong. Fix `cwd` (Claude) or run CLI from correct folder. |
370
+ | Server not available in CLI | Check CLI MCP config. Confirm `node -v` works and server file exists. |
371
+ | Data not saved | You must call a write tool (`memory_save` or approve a proposal). Chat alone does not persist. |
372
+
373
+ ### Testing checklist
374
+
375
+ 1. Configure CLI MCP server for target project
376
+ 2. Restart CLI session
377
+ 3. `memory_status` returns expected path
378
+ 4. `memory_save` returns success
379
+ 5. `memory_search` finds the saved item
380
+ 6. Verify another CLI (Claude/Gemini/Codex) can see the same item
381
+
382
+ ---
383
+
384
+ <details>
385
+ <summary><strong>Auto-Save Hook (Claude Code + Gemini CLI + Codex CLI)</strong></summary>
386
+
387
+ The `hooks/` directory contains hooks that **automatically capture** memory items from your session — no manual `memory_save` needed for routine facts.
388
+
389
+ ### How it works
390
+
391
+ The auto-save hook runs **silently in the background** after each session:
392
+
393
+ 1. **Trigger**: When your CLI session ends:
394
+ - Claude Code: on `Stop` event (Ctrl+C or session end)
395
+ - Gemini CLI: on `SessionEnd` event
396
+ - Codex CLI: on notify events (when configured)
397
+
398
+ 2. **Processing**:
399
+ - `dist/hooks/auto-save.js` reads the session transcript (JSONL or JSON format)
400
+ - Heuristic extractors analyze tool calls and results
401
+ - Extracts structured facts like versions, dependencies, commits, error fixes
402
+
403
+ 3. **Saving**:
404
+ - Deduplicates against existing memory using title hashing and similarity checks
405
+ - Saves new items to `.ai/memory.json` with `source: "auto-hook"` and `tags: ["auto-hook"]`
406
+ - Updates cursor in `.ai/.auto-save-cursor.json` to track progress
407
+
408
+ 4. **Next session**: Only processes new transcript lines since last cursor position
409
+
410
+ ### What gets captured automatically
411
+
412
+ | Category | Example Command | Extracted Item | Type |
413
+ |---|---|---|---|
414
+ | **Version checks** | `node -v` | "node version: v20.11.0" | fact |
415
+ | | `python --version` | "python version: 3.11.5" | fact |
416
+ | | `npm -v`, `pip -v`, `go version` | version facts | fact |
417
+ | **Dependencies** | `npm install express` | "Added dependency: express" | fact |
418
+ | | `pip install requests` | "Added dependency: requests" | fact |
419
+ | | `cargo add tokio` | "Added dependency: tokio" | fact |
420
+ | **Git commits** | `git commit -m "fix auth bug"` | "Commit: fix auth bug" | note |
421
+ | **Error fixes** | Command fails → retried command succeeds | "Resolved: [error summary]" | fact |
422
+ | **File changes** | Write/Edit tool calls | "Files modified this session (5)" | note |
423
+
424
+ All auto-saved items get these tags:
425
+ - `auto-hook` - identifies auto-captured items
426
+ - Category-specific tags: `version`, `environment`, `dependency`, `commit`, `error-resolution`, `file-changes`
427
+
428
+ ### Example: What you'll see
429
+
430
+ **During session:**
431
+ ```bash
432
+ $ node -v
433
+ v20.11.0
434
+ $ npm install express
435
+ added 57 packages
436
+ $ git commit -m "Add express server"
437
+ [main abc1234] Add express server
438
+ ```
439
+
440
+ **After session ends** (automatic, silent):
441
+
442
+ Your `.ai/memory.json` will contain:
443
+ ```json
444
+ {
445
+ "items": [
446
+ {
447
+ "id": "mem_a1b2c3d4",
448
+ "type": "fact",
449
+ "title": "node version: v20.11.0",
450
+ "content": "Detected via `node -v`",
451
+ "tags": ["version", "environment", "auto-hook"],
452
+ "source": "auto-hook",
453
+ "createdAt": "2026-02-12T13:39:17.364Z"
454
+ },
455
+ {
456
+ "id": "mem_e5f6g7h8",
457
+ "type": "fact",
458
+ "title": "Added dependency: express",
459
+ "content": "Installed via `npm install express`",
460
+ "tags": ["dependency", "auto-hook"],
461
+ "source": "auto-hook",
462
+ "createdAt": "2026-02-12T13:39:18.123Z"
463
+ },
464
+ {
465
+ "id": "mem_i9j0k1l2",
466
+ "type": "note",
467
+ "title": "Commit: Add express server",
468
+ "content": "Full command: git commit -m \"Add express server\"",
469
+ "tags": ["commit", "auto-hook"],
470
+ "source": "auto-hook",
471
+ "createdAt": "2026-02-12T13:39:19.456Z"
472
+ }
473
+ ]
474
+ }
475
+ ```
476
+
477
+ ### Setup per CLI
478
+
479
+ #### Claude Code (already configured)
480
+
481
+ This repo includes `.claude/settings.json`:
482
+ ```json
483
+ {
484
+ "hooks": {
485
+ "Stop": [{
486
+ "hooks": [{
487
+ "type": "command",
488
+ "command": "node \"$CLAUDE_PROJECT_DIR/dist/hooks/auto-save.js\"",
489
+ "async": true,
490
+ "timeout": 15
491
+ }]
492
+ }]
493
+ }
494
+ }
495
+ ```
496
+
497
+ **Status**: ✅ Works automatically in this project
498
+
499
+ **To disable**: Remove or rename `.claude/settings.json`
500
+
501
+ **Environment**: Hook receives `CLAUDE_PROJECT_DIR` env var pointing to project root
502
+
503
+ ---
504
+
505
+ #### Gemini CLI (already configured)
506
+
507
+ This repo includes `.gemini/settings.json`:
508
+ ```json
509
+ {
510
+ "hooks": {
511
+ "SessionEnd": [{
512
+ "matcher": "*",
513
+ "hooks": [{
514
+ "name": "auto-save",
515
+ "type": "command",
516
+ "command": "node \"$GEMINI_PROJECT_DIR/dist/hooks/auto-save.js\""
517
+ }]
518
+ }]
519
+ }
520
+ }
521
+ ```
522
+
523
+ **Status**: ✅ Works automatically in this project
524
+
525
+ **To disable**: Remove or rename `.gemini/settings.json`
526
+
527
+ **Environment**: Hook receives `GEMINI_PROJECT_DIR` env var pointing to project root
528
+
529
+ **Important**: Gemini hooks must output valid JSON to stdout. The hook returns `{}` for compatibility.
530
+
531
+ ---
532
+
533
+ #### Codex CLI (needs manual setup)
534
+
535
+ **Setup required**: Edit your global Codex config at `~/.codex/config.toml`:
536
+
537
+ ```toml
538
+ # Enable history persistence (required for hooks to access transcript)
539
+ [history]
540
+ persistence = "save-all" # or just true
541
+
542
+ # Add the notify hook (adjust path to your installation)
543
+ [notify]
544
+ command = ["node", "/absolute/path/to/project-memory-mcp-js/dist/hooks/codex-notify.js"]
545
+ ```
546
+
547
+ Replace `/absolute/path/to/project-memory-mcp-js` with your actual installation path.
548
+
549
+ **Optional**: Pass explicit history file path:
550
+ ```toml
551
+ [notify]
552
+ command = ["node", "/path/to/dist/hooks/codex-notify.js", "--history", "/path/to/history.jsonl"]
553
+ ```
554
+
555
+ **To disable**: Remove the `[notify]` section from `config.toml`
556
+
557
+ **Environment**: Hook receives `CODEX_PROJECT_DIR` env var pointing to project root
558
+
559
+ **How it works**:
560
+ 1. Codex calls `codex-notify.js` with its notification payload
561
+ 2. The notify hook parses Codex's payload format and forwards it to `auto-save.js`
562
+ 3. Same extraction and saving process as Claude/Gemini
563
+
564
+ ---
565
+
566
+ ### Deduplication strategy
567
+
568
+ The hook prevents duplicate items using multiple strategies:
569
+
570
+ 1. **Title hash tracking**: SHA-256 hash of each title stored in cursor file
571
+ 2. **Jaccard similarity**: Compares word overlap between new and existing titles (threshold: 80%)
572
+ 3. **Cross-session dedup**: Hashes persist across sessions to prevent re-capturing the same facts
573
+ 4. **Cursor position**: Only processes new transcript lines since last run
574
+
575
+ ### Cursor tracking
576
+
577
+ State tracked in `.ai/.auto-save-cursor.json`:
578
+ ```json
579
+ {
580
+ "sessionId": "current-session-id",
581
+ "lastLineIndex": 42,
582
+ "itemHashes": ["hash1", "hash2", "..."],
583
+ "updatedAt": "2026-02-12T13:39:17.658Z"
584
+ }
585
+ ```
586
+
587
+ - `sessionId` - Current session ID (resets cursor position on session change)
588
+ - `lastLineIndex` - Last processed transcript line (0-indexed)
589
+ - `itemHashes` - Recent title hashes (keeps last 200 for dedup)
590
+ - `updatedAt` - Last update timestamp
591
+
592
+ **Session change behavior**: When `sessionId` changes, `lastLineIndex` resets to -1 but `itemHashes` are preserved for cross-session deduplication.
593
+
594
+ ### Minimum threshold
595
+
596
+ To reduce noise, the hook only processes transcripts with **at least 2 assistant messages** in the new lines since last cursor position.
597
+
598
+ Single-turn exchanges are skipped.
599
+
600
+ ### Testing the hook
601
+
602
+ **Automated test** (all three CLIs):
603
+ ```bash
604
+ npm run test:hooks
605
+ ```
606
+
607
+ This creates a test transcript and verifies all three hooks work correctly.
608
+
609
+ **Manual test** (single hook):
610
+ ```bash
611
+ echo '{"session_id":"test","transcript_path":"/tmp/test.jsonl","cwd":"'$(pwd)'"}' | node dist/hooks/auto-save.js
612
+ ```
613
+
614
+ **Verify saved items**:
615
+ ```bash
616
+ # Check memory file
617
+ cat .ai/memory.json | jq '.items[] | select(.source == "auto-hook")'
618
+
619
+ # Check cursor
620
+ cat .ai/.auto-save-cursor.json
621
+ ```
622
+
623
+ ### Troubleshooting
624
+
625
+ | Issue | Solution |
626
+ |---|---|
627
+ | Hook not running | Check CLI settings file exists (`.claude/settings.json`, `.gemini/settings.json`) or Codex `config.toml` |
628
+ | No items saved | Check transcript has at least 2 assistant messages. Try running `node -v` and ending session. |
629
+ | Items not appearing | Verify `.ai/memory.json` exists and check for errors in hook stderr |
630
+ | Duplicates appearing | Check cursor file `.ai/.auto-save-cursor.json` is being updated |
631
+ | Codex hook not working | Ensure `history.persistence` is enabled and history file exists |
632
+
633
+ **Debug mode**: Run hook manually with test payload to see errors:
634
+ ```bash
635
+ node dist/hooks/auto-save.js < test-payload.json
636
+ ```
637
+
638
+ </details>
639
+
640
+ ---