Compare commits
518 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 06ba1cd92c | |||
| b003a43e73 | |||
| 5210bc74c7 | |||
| a00ca2b3ec | |||
| ba2c098ec1 | |||
| 5550ecf623 | |||
| 9a27f380c3 | |||
| 577cac8831 | |||
| 8da92c6569 | |||
| a18b43744c | |||
| 7c4979eba1 | |||
| ffe1e1622d | |||
| 8cfa04f93d | |||
| deef86380f | |||
| aa1e65cbb6 | |||
| 9192bb6f21 | |||
| 512486bd85 | |||
| b9814e87f4 | |||
| 54c53fda04 | |||
| f494d3b168 | |||
| 9cb4b9d02a | |||
| 922f04e66a | |||
| b1fb135d9c | |||
| 476f81ceca | |||
| 34ba526fa8 | |||
| efcc557e4f | |||
| 0a34786df9 | |||
| 7175b527a6 | |||
| c415ff5120 | |||
| 83b4806718 | |||
| cb1d939750 | |||
| 9855ccf66d | |||
| 85f30126aa | |||
| 225424a19e | |||
| 2e67821445 | |||
| bbed39c71b | |||
| 3aaee6f13a | |||
| 795a430f1a | |||
| 0a667afc0f | |||
| 8e0b1ee4e1 | |||
| d3aaef926b | |||
| 42fde819a3 | |||
| 4eeb8391c6 | |||
| 213557dd6e | |||
| e1d2ffeb02 | |||
| 9e66a4843e | |||
| 0a3b50c875 | |||
| a8d31d465f | |||
| 186f54b3fd | |||
| be28c095e2 | |||
| 28305f73bb | |||
| c6708b3684 | |||
| 375dd1c3d6 | |||
| c78500cac2 | |||
| 2b683f99bb | |||
| 00e2b0c55f | |||
| fbd4df4285 | |||
| ca24048e15 | |||
| f9fd85fa4d | |||
| bc7e0ba3e0 | |||
| cbfc94bc26 | |||
| c768a80bf0 | |||
| 6dc648f07c | |||
| b116681529 | |||
| d1876cb6e0 | |||
| e1017b483b | |||
| 8d5b886f63 | |||
| 6e8d823139 | |||
| e1212d2dd9 | |||
| d3ae18434f | |||
| 8bdf228a92 | |||
| 2b223b7cd9 | |||
| 7cad4f0114 | |||
| 41835954be | |||
| 2431a1bd9e | |||
| 7fd0f28343 | |||
| 50535499d9 | |||
| 4eb6557fbb | |||
| a22098d661 | |||
| 69b17e15a2 | |||
| de279ef6bf | |||
| c6fed386ef | |||
| ffcd7d21b3 | |||
| efb7507a8f | |||
| 01b3103567 | |||
| 4739b9e413 | |||
| 45acdf84c1 | |||
| 03b85d2af2 | |||
| 03fefca884 | |||
| 01be3156fb | |||
| a48a67ca76 | |||
| 155465f52a | |||
| 0a70bcecc5 | |||
| 4e5913611a | |||
| 73982dc709 | |||
| 93de6d97f5 | |||
| 29e6e026b6 | |||
| 1b394cdf4e | |||
| 6ffa4cfde5 | |||
| 8caf159d99 | |||
| 2a2206d886 | |||
| e3a63c0294 | |||
| 634033b730 | |||
| 54ef1496d2 | |||
| 5d23c60b76 | |||
| 9314ede6e9 | |||
| 58f4053a61 | |||
| a08e5068c8 | |||
| 9f1b653f2f | |||
| c5d89142b9 | |||
| b44d4702b1 | |||
| 4988b4b317 | |||
| c5e68a17c8 | |||
| f35764aa14 | |||
| 601bded789 | |||
| c231e8d076 | |||
| b62e93577c | |||
| 0787e47b1a | |||
| f9843fe593 | |||
| 5b772ee768 | |||
| baabf14bfa | |||
| c126a7083f | |||
| f1170512d6 | |||
| 6c9a8d1dca | |||
| d64939c379 | |||
| 97d8bd3e62 | |||
| 6cd904a81c | |||
| 74c8afd0e0 | |||
| 02444392da | |||
| d175f1759c | |||
| d55db524f0 | |||
| 7d44fdb289 | |||
| 812de2940d | |||
| 95edf31c14 | |||
| a9ae89a198 | |||
| 047914d087 | |||
| bdf79a439b | |||
| 99b6b85d67 | |||
| 798dec972e | |||
| 286343fef6 | |||
| 9285826547 | |||
| ce3b3733fa | |||
| cf1c966409 | |||
| 02fef487e7 | |||
| 20d45006c0 | |||
| 4f1cd309fd | |||
| c46e4a341a | |||
| 60d5f8fbf1 | |||
| c0778bef00 | |||
| 3cbc041c8b | |||
| 0f96476987 | |||
| cd6f883020 | |||
| 9fb7383ab3 | |||
| e8e7fc81af | |||
| e3283c2a1d | |||
| 581e940659 | |||
| 915dbc1aa9 | |||
| 5a84198529 | |||
| f5b25e8fc4 | |||
| 7d1e6af5c5 | |||
| 8b4cc4f6bf | |||
| 68290a9121 | |||
| ab5d78717f | |||
| cb4aea57a8 | |||
| 7bdf6dbfe1 | |||
| 1c9da73d5f | |||
| 5f7aa0710e | |||
| 39fedfc5fc | |||
| ecb8b39f6d | |||
| fe0902b48f | |||
| 4ab9739e4c | |||
| 6ddef1093a | |||
| 97d565e3cd | |||
| eafdd6a7be | |||
| 3529f9274b | |||
| 30ebe92a53 | |||
| 1bb203cbb5 | |||
| 9f8499fe54 | |||
| 40d105d7de | |||
| cda12d95c9 | |||
| bbd6113f69 | |||
| 51d1315562 | |||
| 85763d575d | |||
| c76ddc2f83 | |||
| 348cc7f7ac | |||
| ed19e92f75 | |||
| 2a96214456 | |||
| eaa2268bf9 | |||
| 730c420a13 | |||
| d0bdc6ae9b | |||
| d46fbb7166 | |||
| a60ef6eacb | |||
| 13941a1e72 | |||
| bd809c860e | |||
| b4b90faa1e | |||
| 9e235b5b57 | |||
| 5fdf25d60f | |||
| a367436a78 | |||
| 5d64df2ba5 | |||
| 22a04ac461 | |||
| 170b66f623 | |||
| ca4f046777 | |||
| 0475a57fb1 | |||
| fda069bb07 | |||
| 4c1f7b4ff1 | |||
| 35b98c53f0 | |||
| e029903a66 | |||
| 5666f7dd1c | |||
| 57f0c9fd92 | |||
| 5482052c16 | |||
| ccb7001ff0 | |||
| edc20a12a1 | |||
| 45c35c6bdd | |||
| 47ee0838e3 | |||
| 809c9e6639 | |||
| 9646527d66 | |||
| f27c73b469 | |||
| 7f9959fdb7 | |||
| 012774b83c | |||
| e22edaddf4 | |||
| 6204fe9b9d | |||
| 30a42036aa | |||
| d6f1237283 | |||
| 700e3253fa | |||
| 740d65b5a5 | |||
| 4bc467f7ed | |||
| 7fdfdd5d5e | |||
| e872c2da38 | |||
| 13643a5b18 | |||
| 980151a50e | |||
| 9eddc51979 | |||
| 7c3477b7e1 | |||
| f45c782c07 | |||
| 3bdf0b41bc | |||
| 3030f518b5 | |||
| 0b476e971a | |||
| f7b51a963e | |||
| f8dc7f940f | |||
| b68ea38bcc | |||
| 12459eab3b | |||
| 5e6ef4aeb1 | |||
| f46b5b452f | |||
| 2af8db6b82 | |||
| 6a4fa85c10 | |||
| 22f4655a8c | |||
| 79ff1849f0 | |||
| ff28db9d76 | |||
| 268b78083e | |||
| a1f76af902 | |||
| d5e392ea69 | |||
| 66ee9395cb | |||
| 7e75c0b22f | |||
| c506390007 | |||
| f8695516a4 | |||
| 0127c7639a | |||
| 6e66d78825 | |||
| 0b4cfcbe56 | |||
| c8a9486832 | |||
| 9fb43d8b06 | |||
| 1b9cec2a95 | |||
| ddec9835d2 | |||
| 2d97ffc9a8 | |||
| d8886619b8 | |||
| 280cb2fe54 | |||
| c8a206b682 | |||
| c03457d2d4 | |||
| a46a028ddb | |||
| 37f836b719 | |||
| bc8bc10b0b | |||
| 5169cfa46d | |||
| 03ba89b703 | |||
| 263a8d4c18 | |||
| b25b312bf3 | |||
| 633f89a5fb | |||
| c6bf72ca72 | |||
| bda39733e0 | |||
| 80935fbc66 | |||
| bcfd036dfe | |||
| ebf53e9fb0 | |||
| 7133108f15 | |||
| f20bb5bced | |||
| e238a81b34 | |||
| 9215c7e1f5 | |||
| b694f233db | |||
| 02130c49d1 | |||
| c9e0301e3e | |||
| 65c89ea2f0 | |||
| 9a9b00c6d8 | |||
| 309e8a7139 | |||
| 867226c19d | |||
| 192adf5cbc | |||
| 140a5f3469 | |||
| 5c4b44d4c7 | |||
| 3bbacb8fa4 | |||
| d69a95bd29 | |||
| 90b209081c | |||
| 6d145c035c | |||
| bd5ad6e5c2 | |||
| 28005b75af | |||
| 322f81e515 | |||
| ea54a03fae | |||
| 15c55a57a3 | |||
| 056cd12558 | |||
| b0fae0cfd4 | |||
| 44b69b737b | |||
| 56213ef84a | |||
| 64dfc0467d | |||
| c75563a89b | |||
| fb00b517c0 | |||
| df44bd8fd3 | |||
| e9c0ec45db | |||
| d363dfd668 | |||
| c06abbc6f2 | |||
| f499810c7a | |||
| 609d8f5c88 | |||
| 5ebf6c8aec | |||
| d94a11e2e1 | |||
| d4d6185bb4 | |||
| 2df50bcaa0 | |||
| 19ecc7845f | |||
| 142d6ae56f | |||
| 7db39bb482 | |||
| e600a0f702 | |||
| b87654d452 | |||
| a3b0b70a98 | |||
| 81f8aa7eef | |||
| 8f8649c2a8 | |||
| ab0938633a | |||
| d8d4d2464f | |||
| 9d32629f9d | |||
| 6a007a26fe | |||
| 6131f9694a | |||
| 22777bdcfe | |||
| d7946522e9 | |||
| 43262d7b53 | |||
| 80fc1588d3 | |||
| 70ba785364 | |||
| 7fb990f845 | |||
| 9f3bf55c76 | |||
| 83d9747627 | |||
| ae2c789781 | |||
| 81d7ab59ac | |||
| e82d9e075b | |||
| 99af5fdf13 | |||
| b88ce840fa | |||
| 516e136966 | |||
| 051bc8dd67 | |||
| 05ddf3540c | |||
| e18f02e2af | |||
| 77885db345 | |||
| 50d504715d | |||
| 28d9c43f85 | |||
| adc5853c73 | |||
| 81fdf28347 | |||
| b3a565c448 | |||
| 5b28c23b20 | |||
| f4217cb2b9 | |||
| e7252c8999 | |||
| 74637705d7 | |||
| 322cb94c43 | |||
| 21c7ab2929 | |||
| 3846d66ccc | |||
| 817a069323 | |||
| d7b9f68d80 | |||
| c48290a156 | |||
| 226a52f8b8 | |||
| 93b5f80168 | |||
| f6cf895847 | |||
| dba29de2a7 | |||
| c16f453017 | |||
| 8b4c962e62 | |||
| 12149470a2 | |||
| fd4cd0444c | |||
| 0adbf38c39 | |||
| 15362d1aed | |||
| c04e5c571c | |||
| 66a69f3044 | |||
| e50c6142bb | |||
| 8d5bfec90e | |||
| ab2c44069b | |||
| babb375955 | |||
| 334fbdd913 | |||
| 4a269183a3 | |||
| 37cd8b8328 | |||
| 33d2422faa | |||
| dad3a104b4 | |||
| bcad4c484d | |||
| 4284b31f42 | |||
| f556546994 | |||
| 6441d9103c | |||
| 2952b7fb8d | |||
| 63f930a433 | |||
| 3cb003f4e4 | |||
| 8b5e5efeb5 | |||
| a57aba82a4 | |||
| 7b7a53ee19 | |||
| e57bfc4187 | |||
| 3cfdd5fd65 | |||
| 1a226b08f0 | |||
| 15b5176fe6 | |||
| 418dc963b2 | |||
| 2314362028 | |||
| f373b682dd | |||
| 1c25c3a7e7 | |||
| 3ca5e33b4a | |||
| 3042de1093 | |||
| f7f62ef3f3 | |||
| 726f167ebf | |||
| a62887a6e0 | |||
| 635cc87462 | |||
| c27f07023c | |||
| e073c0b75f | |||
| 562194612e | |||
| 02bce8a6e6 | |||
| e9bcb7e9db | |||
| 86214b93a9 | |||
| ef572ec032 | |||
| 3cb99ab7f4 | |||
| 20136121d8 | |||
| df0f3fd96c | |||
| fe861a85bd | |||
| 32f45a1100 | |||
| 1ea692e0b0 | |||
| 861cd20adf | |||
| d275715974 | |||
| fd4684dcb3 | |||
| c54e50ec0c | |||
| c42444e06c | |||
| cb2f2a0432 | |||
| 692bb07dfe | |||
| 58e9dcc0fc | |||
| f60ee3b4f5 | |||
| 7ed166323c | |||
| 651989c423 | |||
| 9dd8b180ac | |||
| 9b1d801a46 | |||
| 6fb9570291 | |||
| ae90b26995 | |||
| 3b0cd0b3f6 | |||
| f849a69506 | |||
| daf368e343 | |||
| cbd43240c7 | |||
| 47c1398ce7 | |||
| b44359c136 | |||
| be0ab12789 | |||
| 7ff611feb5 | |||
| cf9d1d4a0b | |||
| 9149acf4d1 | |||
| 002f7a94b8 | |||
| 6d68fd44ca | |||
| bc41e367c0 | |||
| c41c4b21ea | |||
| 5dd663730b | |||
| 56167c47a2 | |||
| 115270c35e | |||
| c27682c799 | |||
| 1fbba55aa3 | |||
| 05f3889deb | |||
| 874815770a | |||
| d452913487 | |||
| 635f456ecf | |||
| 81101ef1a6 | |||
| 938eb9dc0e | |||
| a11199a527 | |||
| 015b38c763 | |||
| be936d8413 | |||
| d4a71c994d | |||
| 372854948c | |||
| d6462919cb | |||
| ec79e085b2 | |||
| cedb635176 | |||
| 6f62a569df | |||
| 7c5e5b1941 | |||
| 8e460a8c2a | |||
| 18d5e0d3bb | |||
| 3e617a8b1e | |||
| 307c87b9f6 | |||
| 2d080b0264 | |||
| 834cf4095e | |||
| 723f1f5374 | |||
| 5f05f991bc | |||
| b44853fb2c | |||
| 29aa945ae0 | |||
| f2551ac366 | |||
| 2ba840aaac | |||
| eddb321489 | |||
| 6e9be84a01 | |||
| 18aa4f2538 | |||
| 4489249ecc | |||
| 2608fb180e | |||
| edeed2ee2c | |||
| 01b477da26 | |||
| 58a9554bb3 | |||
| 29f1cb3b4c | |||
| 7307563cfe | |||
| 047298a183 | |||
| 78fd1368db | |||
| d07a40616d | |||
| e81ea69143 | |||
| 917ab9740c | |||
| b43b7f02ae | |||
| d989ba62f9 | |||
| 87f26d7b0d | |||
| 7fac3e3bb6 | |||
| 2663121d9f | |||
| b5bfc029c3 | |||
| 5886fe7d8f | |||
| 5f15695c3f | |||
| c49533c250 | |||
| 4f49cb1bc9 | |||
| 874726b193 | |||
| 5244a12422 | |||
| 5b30764fa8 | |||
| 85ed7c3d2f | |||
| 4d5b307a74 | |||
| 68566b556c | |||
| b0032c1745 | |||
| 35b7aab174 |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "thedotmack",
|
||||
"owner": {
|
||||
"name": "Alex Newman"
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Plugins by Alex Newman (thedotmack)",
|
||||
"homepage": "https://github.com/thedotmack/claude-mem"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "7.0.2",
|
||||
"source": "./plugin",
|
||||
"description": "Persistent memory system for Claude Code - context compression across sessions"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"env": {},
|
||||
"permissions": {
|
||||
"deny": [
|
||||
"Read(./package-lock.json)",
|
||||
"Read(./node_modules/**)",
|
||||
"Read(./.DS_Store)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Project-Level Skills
|
||||
|
||||
This directory contains skills **for developing and maintaining the claude-mem project itself**, not skills that are released as part of the plugin.
|
||||
|
||||
## Distinction
|
||||
|
||||
**Project Skills** (`.claude/skills/`):
|
||||
- Used by developers working on claude-mem
|
||||
- Not included in the plugin distribution
|
||||
- Project-specific workflows (version bumps, release management, etc.)
|
||||
- Not synced to `~/.claude/plugins/marketplaces/thedotmack/`
|
||||
|
||||
**Plugin Skills** (`plugin/skills/`):
|
||||
- Released as part of the claude-mem plugin
|
||||
- Available to all users who install the plugin
|
||||
- General-purpose memory search functionality
|
||||
- Synced to user installations via `npm run sync-marketplace`
|
||||
|
||||
## Skills in This Directory
|
||||
|
||||
### version-bump
|
||||
Manages semantic versioning for the claude-mem project itself. Handles updating all four version files (package.json, marketplace.json, plugin.json, CLAUDE.md), creating git tags, and GitHub releases.
|
||||
|
||||
**Usage**: Only for claude-mem maintainers releasing new versions.
|
||||
|
||||
## Adding New Skills
|
||||
|
||||
**For claude-mem development** → Add to `.claude/skills/`
|
||||
**For end users** → Add to `plugin/skills/` (gets distributed with plugin)
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
name: version-bump
|
||||
description: Manage semantic version updates for claude-mem project. Handles patch, minor, and major version increments following semantic versioning. Updates package.json, marketplace.json, plugin.json, and CLAUDE.md version number (NOT version history). Creates git tags and GitHub releases. Auto-generates CHANGELOG.md from releases.
|
||||
---
|
||||
|
||||
# Version Bump Skill
|
||||
|
||||
Manage semantic versioning across the claude-mem project with consistent updates to all version-tracked files.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
**Files requiring updates (ALL FOUR):**
|
||||
1. `package.json` (line 3)
|
||||
2. `.claude-plugin/marketplace.json` (line 13)
|
||||
3. `plugin/.claude-plugin/plugin.json` (line 3)
|
||||
4. `CLAUDE.md` (line 9 ONLY - version number, NOT version history)
|
||||
|
||||
**Semantic versioning:**
|
||||
- **PATCH** (x.y.Z): Bugfixes only
|
||||
- **MINOR** (x.Y.0): New features, backward compatible
|
||||
- **MAJOR** (X.0.0): Breaking changes
|
||||
|
||||
## Quick Decision Guide
|
||||
|
||||
**What changed?**
|
||||
- "Fixed a bug" → PATCH (5.3.0 → 5.3.1)
|
||||
- "Added new feature" → MINOR (5.3.0 → 5.4.0)
|
||||
- "Breaking change" → MAJOR (5.3.0 → 6.0.0)
|
||||
|
||||
**If unclear, ASK THE USER explicitly.**
|
||||
|
||||
## Standard Workflow
|
||||
|
||||
See [operations/workflow.md](operations/workflow.md) for detailed step-by-step process.
|
||||
|
||||
**Quick version:**
|
||||
1. Determine version type (PATCH/MINOR/MAJOR)
|
||||
2. Calculate new version from current
|
||||
3. Preview changes to user
|
||||
4. Update ALL FOUR files
|
||||
5. Verify consistency
|
||||
6. Build and test
|
||||
7. Commit and create git tag
|
||||
8. Push and create GitHub release
|
||||
9. Generate CHANGELOG.md from releases and commit
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
See [operations/scenarios.md](operations/scenarios.md) for examples:
|
||||
- Bug fix releases
|
||||
- New feature releases
|
||||
- Breaking change releases
|
||||
|
||||
## Critical Rules
|
||||
|
||||
**ALWAYS:**
|
||||
- Update ALL FOUR files with matching version numbers
|
||||
- Create git tag with format `vX.Y.Z`
|
||||
- Create GitHub release from the tag
|
||||
- Generate CHANGELOG.md from releases after creating release
|
||||
- Ask user if version type is unclear
|
||||
|
||||
**NEVER:**
|
||||
- Update only one, two, or three files
|
||||
- Skip the verification step
|
||||
- Forget to create git tag or GitHub release
|
||||
- Add version history entries to CLAUDE.md (that's managed separately)
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
Before considering the task complete:
|
||||
- [ ] All FOUR files have matching version numbers
|
||||
- [ ] `npm run build` succeeds
|
||||
- [ ] Git commit created with all version files
|
||||
- [ ] Git tag created (format: vX.Y.Z)
|
||||
- [ ] Commit and tags pushed to remote
|
||||
- [ ] GitHub release created from the tag
|
||||
- [ ] CHANGELOG.md generated and committed
|
||||
- [ ] CLAUDE.md: ONLY line 9 updated (version number), NOT version history
|
||||
|
||||
## Reference Commands
|
||||
|
||||
```bash
|
||||
# View current version
|
||||
grep '"version"' package.json
|
||||
|
||||
# Verify consistency across all version files
|
||||
grep '"version"' package.json .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json
|
||||
|
||||
# View git tags
|
||||
git tag -l -n1
|
||||
|
||||
# Check what will be committed
|
||||
git status
|
||||
git diff package.json .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json CLAUDE.md
|
||||
```
|
||||
|
||||
For more commands, see [operations/reference.md](operations/reference.md).
|
||||
@@ -0,0 +1,275 @@
|
||||
# Version Bump Reference
|
||||
|
||||
Quick reference for version bump commands and file locations.
|
||||
|
||||
## File Locations
|
||||
|
||||
### Version-Tracked Files (ALL FOUR)
|
||||
|
||||
1. **package.json**
|
||||
- Path: `package.json`
|
||||
- Line: 3
|
||||
- Format: `"version": "X.Y.Z",`
|
||||
|
||||
2. **marketplace.json**
|
||||
- Path: `.claude-plugin/marketplace.json`
|
||||
- Line: 13
|
||||
- Format: `"version": "X.Y.Z",`
|
||||
|
||||
3. **plugin.json**
|
||||
- Path: `plugin/.claude-plugin/plugin.json`
|
||||
- Line: 3
|
||||
- Format: `"version": "X.Y.Z",`
|
||||
|
||||
4. **CLAUDE.md**
|
||||
- Path: `CLAUDE.md`
|
||||
- Line: 9
|
||||
- Format: `**Current Version**: X.Y.Z`
|
||||
|
||||
## Essential Commands
|
||||
|
||||
### View Current Version
|
||||
|
||||
```bash
|
||||
# From package.json
|
||||
grep '"version"' package.json
|
||||
|
||||
# Extract just the version number
|
||||
grep '"version"' package.json | head -1 | sed 's/.*"version": "\(.*\)".*/\1/'
|
||||
|
||||
# From all version files
|
||||
grep '"version"' package.json .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json
|
||||
grep "Current Version" CLAUDE.md
|
||||
```
|
||||
|
||||
### Verify Version Consistency
|
||||
|
||||
```bash
|
||||
# Check all JSON files match
|
||||
grep '"version"' package.json .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json
|
||||
|
||||
# Should output identical version in all three:
|
||||
# package.json:3: "version": "5.3.0",
|
||||
# .claude-plugin/marketplace.json:13: "version": "5.3.0",
|
||||
# plugin/.claude-plugin/plugin.json:3: "version": "5.3.0",
|
||||
|
||||
# Check CLAUDE.md
|
||||
grep "Current Version" CLAUDE.md
|
||||
# Should output: **Current Version**: 5.3.0
|
||||
```
|
||||
|
||||
### Git Commands
|
||||
|
||||
```bash
|
||||
# View recent commits
|
||||
git log --oneline -5
|
||||
|
||||
# View changes since last tag
|
||||
LAST_TAG=$(git describe --tags --abbrev=0)
|
||||
git log $LAST_TAG..HEAD --oneline
|
||||
git diff $LAST_TAG..HEAD
|
||||
|
||||
# List all tags
|
||||
git tag -l
|
||||
|
||||
# View tag details
|
||||
git show vX.Y.Z
|
||||
|
||||
# List tags with messages
|
||||
git tag -l -n1
|
||||
```
|
||||
|
||||
### Build and Test
|
||||
|
||||
```bash
|
||||
# Build plugin
|
||||
npm run build
|
||||
|
||||
# Sync to marketplace
|
||||
npm run sync-marketplace
|
||||
|
||||
# Run tests (if available)
|
||||
npm test
|
||||
```
|
||||
|
||||
### Commit and Tag
|
||||
|
||||
```bash
|
||||
# Stage version files
|
||||
git add package.json .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json CLAUDE.md plugin/scripts/
|
||||
|
||||
# Commit
|
||||
git commit -m "Release vX.Y.Z: [Description]"
|
||||
|
||||
# Create tag
|
||||
git tag vX.Y.Z -m "Release vX.Y.Z: [Description]"
|
||||
|
||||
# Push
|
||||
git push && git push --tags
|
||||
```
|
||||
|
||||
### GitHub Release
|
||||
|
||||
```bash
|
||||
# Create release
|
||||
gh release create vX.Y.Z --title "vX.Y.Z" --notes "[Release notes]"
|
||||
|
||||
# Create with auto-generated notes
|
||||
gh release create vX.Y.Z --title "vX.Y.Z" --generate-notes
|
||||
|
||||
# View release
|
||||
gh release view vX.Y.Z
|
||||
|
||||
# List all releases
|
||||
gh release list
|
||||
|
||||
# Delete release (if needed)
|
||||
gh release delete vX.Y.Z
|
||||
```
|
||||
|
||||
## Semantic Versioning Rules
|
||||
|
||||
### Version Format: MAJOR.MINOR.PATCH
|
||||
|
||||
**MAJOR (X.0.0):**
|
||||
- Breaking changes
|
||||
- Incompatible API changes
|
||||
- Schema changes requiring migration
|
||||
- Removes features
|
||||
|
||||
**MINOR (x.Y.0):**
|
||||
- New features (backward compatible)
|
||||
- New functionality
|
||||
- Deprecations (but not removals)
|
||||
- Resets PATCH to 0
|
||||
|
||||
**PATCH (x.y.Z):**
|
||||
- Bug fixes
|
||||
- Performance improvements
|
||||
- Documentation fixes
|
||||
- No new features
|
||||
|
||||
### Incrementing Rules
|
||||
|
||||
```
|
||||
PATCH: 5.3.2 → 5.3.3
|
||||
MINOR: 5.3.2 → 5.4.0 (resets patch)
|
||||
MAJOR: 5.3.2 → 6.0.0 (resets minor and patch)
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Bug Fix Release
|
||||
|
||||
```bash
|
||||
# Example: 5.3.0 → 5.3.1
|
||||
# 1. Update all four files to 5.3.1
|
||||
# 2. Build and test
|
||||
npm run build
|
||||
# 3. Commit and tag
|
||||
git add package.json .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json CLAUDE.md plugin/scripts/
|
||||
git commit -m "Release v5.3.1: Fixed observer crash"
|
||||
git tag v5.3.1 -m "Release v5.3.1: Fixed observer crash"
|
||||
git push && git push --tags
|
||||
# 4. Create release
|
||||
gh release create v5.3.1 --title "v5.3.1" --notes "Fixed observer crash on empty content"
|
||||
```
|
||||
|
||||
### Feature Release
|
||||
|
||||
```bash
|
||||
# Example: 5.3.0 → 5.4.0
|
||||
# 1. Update all four files to 5.4.0
|
||||
# 2. Build and test
|
||||
npm run build
|
||||
# 3. Commit and tag
|
||||
git add package.json .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json CLAUDE.md plugin/scripts/
|
||||
git commit -m "Release v5.4.0: Added dark mode support"
|
||||
git tag v5.4.0 -m "Release v5.4.0: Added dark mode support"
|
||||
git push && git push --tags
|
||||
# 4. Create release
|
||||
gh release create v5.4.0 --title "v5.4.0" --generate-notes
|
||||
```
|
||||
|
||||
### Breaking Change Release
|
||||
|
||||
```bash
|
||||
# Example: 5.3.0 → 6.0.0
|
||||
# 1. Update all four files to 6.0.0
|
||||
# 2. Build and test
|
||||
npm run build
|
||||
# 3. Commit and tag
|
||||
git add package.json .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json CLAUDE.md plugin/scripts/
|
||||
git commit -m "Release v6.0.0: Storage layer redesign"
|
||||
git tag v6.0.0 -m "Release v6.0.0: Storage layer redesign"
|
||||
git push && git push --tags
|
||||
# 4. Create release with warning
|
||||
gh release create v6.0.0 --title "v6.0.0" --notes "⚠️ Breaking change: Storage layer redesigned. Migration required."
|
||||
```
|
||||
|
||||
## Rollback Commands
|
||||
|
||||
### Delete Tag
|
||||
|
||||
```bash
|
||||
# Delete local tag
|
||||
git tag -d vX.Y.Z
|
||||
|
||||
# Delete remote tag
|
||||
git push origin :refs/tags/vX.Y.Z
|
||||
# Or: git push --delete origin vX.Y.Z
|
||||
```
|
||||
|
||||
### Delete Release
|
||||
|
||||
```bash
|
||||
# Delete GitHub release
|
||||
gh release delete vX.Y.Z
|
||||
|
||||
# Confirm deletion
|
||||
gh release delete vX.Y.Z --yes
|
||||
```
|
||||
|
||||
### Revert Commit
|
||||
|
||||
```bash
|
||||
# Revert last commit (creates new commit)
|
||||
git revert HEAD
|
||||
|
||||
# Reset to previous commit (destructive)
|
||||
git reset --hard HEAD~1
|
||||
git push --force # Dangerous! Only if not shared
|
||||
```
|
||||
|
||||
## Error Prevention
|
||||
|
||||
### Pre-commit Checks
|
||||
|
||||
```bash
|
||||
# Check all versions match before committing
|
||||
V1=$(grep '"version"' package.json | head -1 | sed 's/.*"\([^"]*\)".*/\1/')
|
||||
V2=$(grep '"version"' .claude-plugin/marketplace.json | sed 's/.*"\([^"]*\)".*/\1/')
|
||||
V3=$(grep '"version"' plugin/.claude-plugin/plugin.json | head -1 | sed 's/.*"\([^"]*\)".*/\1/')
|
||||
|
||||
if [ "$V1" = "$V2" ] && [ "$V2" = "$V3" ]; then
|
||||
echo "✓ All versions match: $V1"
|
||||
else
|
||||
echo "✗ Version mismatch!"
|
||||
echo " package.json: $V1"
|
||||
echo " marketplace.json: $V2"
|
||||
echo " plugin.json: $V3"
|
||||
fi
|
||||
```
|
||||
|
||||
### Pre-push Checks
|
||||
|
||||
```bash
|
||||
# Check tag exists
|
||||
git tag -l | grep vX.Y.Z || echo "Warning: Tag not created"
|
||||
|
||||
# Check build succeeds
|
||||
npm run build || echo "Error: Build failed"
|
||||
|
||||
# Check no uncommitted changes
|
||||
git status --porcelain | grep -q . && echo "Warning: Uncommitted changes"
|
||||
```
|
||||
@@ -0,0 +1,218 @@
|
||||
# Common Version Bump Scenarios
|
||||
|
||||
Real-world examples of version bumps with decision rationale.
|
||||
|
||||
## Scenario 1: Bug Fix After Testing
|
||||
|
||||
**User request:**
|
||||
> "Fixed the memory leak in the search function"
|
||||
|
||||
**Analysis:**
|
||||
- What changed: Bug fix
|
||||
- Breaking changes: No
|
||||
- New features: No
|
||||
- **Decision: PATCH**
|
||||
|
||||
**Workflow:**
|
||||
```
|
||||
Current: 4.2.8
|
||||
New: 4.2.9 (PATCH)
|
||||
|
||||
Steps:
|
||||
1. Update all four files to 4.2.9
|
||||
2. npm run build
|
||||
3. git commit -m "Release v4.2.9: Fixed memory leak in search"
|
||||
4. git tag v4.2.9 -m "Release v4.2.9: Fixed memory leak in search"
|
||||
5. git push && git push --tags
|
||||
6. gh release create v4.2.9 --title "v4.2.9" --notes "Fixed memory leak in search function"
|
||||
```
|
||||
|
||||
## Scenario 2: New Feature Added
|
||||
|
||||
**User request:**
|
||||
> "Added web search MCP integration"
|
||||
|
||||
**Analysis:**
|
||||
- What changed: New feature (MCP integration)
|
||||
- Breaking changes: No
|
||||
- Backward compatible: Yes
|
||||
- **Decision: MINOR**
|
||||
|
||||
**Workflow:**
|
||||
```
|
||||
Current: 4.2.8
|
||||
New: 4.3.0 (MINOR - reset patch to 0)
|
||||
|
||||
Steps:
|
||||
1. Update all four files to 4.3.0
|
||||
2. npm run build
|
||||
3. git commit -m "Release v4.3.0: Added web search MCP integration"
|
||||
4. git tag v4.3.0 -m "Release v4.3.0: Added web search MCP integration"
|
||||
5. git push && git push --tags
|
||||
6. gh release create v4.3.0 --title "v4.3.0" --generate-notes
|
||||
```
|
||||
|
||||
## Scenario 3: Database Schema Redesign
|
||||
|
||||
**User request:**
|
||||
> "Rewrote storage layer, old data needs migration"
|
||||
|
||||
**Analysis:**
|
||||
- What changed: Storage layer rewrite
|
||||
- Breaking changes: Yes (requires migration)
|
||||
- Backward compatible: No
|
||||
- **Decision: MAJOR**
|
||||
|
||||
**Workflow:**
|
||||
```
|
||||
Current: 4.2.8
|
||||
New: 5.0.0 (MAJOR - reset minor and patch to 0)
|
||||
|
||||
Steps:
|
||||
1. Update all four files to 5.0.0
|
||||
2. npm run build
|
||||
3. git commit -m "Release v5.0.0: Storage layer redesign with migration required"
|
||||
4. git tag v5.0.0 -m "Release v5.0.0: Storage layer redesign"
|
||||
5. git push && git push --tags
|
||||
6. gh release create v5.0.0 --title "v5.0.0" --notes "⚠️ Breaking change: Storage layer redesigned. Migration required."
|
||||
```
|
||||
|
||||
## Scenario 4: Multiple Small Bug Fixes
|
||||
|
||||
**User request:**
|
||||
> "Fixed three bugs: observer crash, viewer pagination, and date formatting"
|
||||
|
||||
**Analysis:**
|
||||
- What changed: Multiple bug fixes
|
||||
- Breaking changes: No
|
||||
- New features: No
|
||||
- **Decision: PATCH** (one patch covers all fixes)
|
||||
|
||||
**Workflow:**
|
||||
```
|
||||
Current: 4.2.8
|
||||
New: 4.2.9 (PATCH)
|
||||
|
||||
Steps:
|
||||
1. Update all four files to 4.2.9
|
||||
2. npm run build
|
||||
3. git commit -m "Release v4.2.9: Multiple bug fixes
|
||||
|
||||
- Fixed observer crash on empty content
|
||||
- Fixed viewer pagination edge case
|
||||
- Fixed date formatting in timeline"
|
||||
4. git tag v4.2.9 -m "Release v4.2.9: Multiple bug fixes"
|
||||
5. git push && git push --tags
|
||||
6. gh release create v4.2.9 --title "v4.2.9" --generate-notes
|
||||
```
|
||||
|
||||
## Scenario 5: Feature + Bug Fix
|
||||
|
||||
**User request:**
|
||||
> "Added dark mode support and fixed the viewer crash bug"
|
||||
|
||||
**Analysis:**
|
||||
- What changed: New feature + bug fix
|
||||
- Breaking changes: No
|
||||
- **Decision: MINOR** (feature trumps bug fix)
|
||||
|
||||
**Workflow:**
|
||||
```
|
||||
Current: 5.1.0
|
||||
New: 5.2.0 (MINOR)
|
||||
|
||||
Steps:
|
||||
1. Update all four files to 5.2.0
|
||||
2. npm run build
|
||||
3. git commit -m "Release v5.2.0: Dark mode support + bug fixes
|
||||
|
||||
Features:
|
||||
- Added dark mode toggle to viewer UI
|
||||
|
||||
Bug fixes:
|
||||
- Fixed viewer crash on empty database"
|
||||
4. git tag v5.2.0 -m "Release v5.2.0: Dark mode support"
|
||||
5. git push && git push --tags
|
||||
6. gh release create v5.2.0 --title "v5.2.0" --generate-notes
|
||||
```
|
||||
|
||||
## Scenario 6: Documentation Only
|
||||
|
||||
**User request:**
|
||||
> "Updated README with new installation instructions"
|
||||
|
||||
**Analysis:**
|
||||
- What changed: Documentation only
|
||||
- Breaking changes: No
|
||||
- Code changes: No
|
||||
- **Decision: PATCH** (or skip version bump if no code changes)
|
||||
|
||||
**Workflow:**
|
||||
```
|
||||
Option 1: PATCH (if you want to tag doc improvements)
|
||||
Current: 4.2.8
|
||||
New: 4.2.9
|
||||
|
||||
Option 2: No version bump (documentation-only changes don't require versioning)
|
||||
Just commit without bumping version
|
||||
```
|
||||
|
||||
**Recommendation:** Skip version bump for documentation-only changes unless it's a significant documentation overhaul.
|
||||
|
||||
## Scenario 7: Configuration Change
|
||||
|
||||
**User request:**
|
||||
> "Changed default observation count from 50 to 30"
|
||||
|
||||
**Analysis:**
|
||||
- What changed: Default configuration
|
||||
- Breaking changes: Yes (behavior changes)
|
||||
- Users might notice different context size
|
||||
- **Decision: MINOR or MAJOR** (ask user)
|
||||
|
||||
**Workflow:**
|
||||
```
|
||||
Ask user:
|
||||
"This changes default behavior (context size). Users will see different results.
|
||||
Is this:
|
||||
- MINOR (acceptable behavior change): 4.2.8 → 4.3.0
|
||||
- MAJOR (breaking change): 4.2.8 → 5.0.0
|
||||
|
||||
Which should I use?"
|
||||
```
|
||||
|
||||
## Scenario 8: Dependency Update
|
||||
|
||||
**User request:**
|
||||
> "Updated Claude SDK from 1.2.0 to 1.3.0"
|
||||
|
||||
**Analysis:**
|
||||
- What changed: Dependency version
|
||||
- Breaking changes: Depends on SDK changes
|
||||
- **Decision: Ask user or check SDK changelog**
|
||||
|
||||
**Workflow:**
|
||||
```
|
||||
1. Check SDK changelog for breaking changes
|
||||
2. If SDK has breaking changes → MAJOR
|
||||
3. If SDK adds features → MINOR
|
||||
4. If SDK only fixes bugs → PATCH
|
||||
|
||||
Typical: PATCH (unless SDK breaks compatibility)
|
||||
```
|
||||
|
||||
## Decision Tree
|
||||
|
||||
```
|
||||
Is there a breaking change?
|
||||
├─ Yes → MAJOR (X.0.0)
|
||||
└─ No
|
||||
├─ Is there a new feature?
|
||||
│ ├─ Yes → MINOR (x.Y.0)
|
||||
│ └─ No
|
||||
│ ├─ Is there a bug fix?
|
||||
│ │ ├─ Yes → PATCH (x.y.Z)
|
||||
│ │ └─ No → Don't bump version (docs only, etc.)
|
||||
│ └─ Configuration change? → Ask user (MINOR or MAJOR)
|
||||
└─ Multiple changes? → Use highest level (MAJOR > MINOR > PATCH)
|
||||
```
|
||||
@@ -0,0 +1,251 @@
|
||||
# Detailed Version Bump Workflow
|
||||
|
||||
Step-by-step process for bumping versions in the claude-mem project.
|
||||
|
||||
## Step 1: Analyze Changes
|
||||
|
||||
First, understand what changed:
|
||||
|
||||
```bash
|
||||
# View recent commits
|
||||
git log --oneline -5
|
||||
|
||||
# See what changed in last commit
|
||||
git diff HEAD~1
|
||||
|
||||
# Or see all changes since last tag
|
||||
LAST_TAG=$(git describe --tags --abbrev=0)
|
||||
git log $LAST_TAG..HEAD --oneline
|
||||
git diff $LAST_TAG..HEAD
|
||||
```
|
||||
|
||||
## Step 2: Determine Version Type
|
||||
|
||||
Ask yourself:
|
||||
- **Breaking changes?** → MAJOR
|
||||
- **New features?** → MINOR
|
||||
- **Bugfixes only?** → PATCH
|
||||
|
||||
**If unclear, ASK THE USER explicitly.**
|
||||
|
||||
### Decision Matrix
|
||||
|
||||
| Change Type | Version Bump | Example |
|
||||
|------------|--------------|---------|
|
||||
| Bug fix | PATCH | 4.2.8 → 4.2.9 |
|
||||
| New feature (backward compatible) | MINOR | 4.2.8 → 4.3.0 |
|
||||
| Breaking change | MAJOR | 4.2.8 → 5.0.0 |
|
||||
| Multiple features | MINOR | 4.2.8 → 4.3.0 |
|
||||
| Feature + breaking change | MAJOR | 4.2.8 → 5.0.0 |
|
||||
|
||||
## Step 3: Calculate New Version
|
||||
|
||||
From current version in `package.json`:
|
||||
|
||||
```bash
|
||||
grep '"version"' package.json
|
||||
```
|
||||
|
||||
Apply semantic versioning rules:
|
||||
- **Patch:** increment Z (4.2.8 → 4.2.9)
|
||||
- **Minor:** increment Y, reset Z (4.2.8 → 4.3.0)
|
||||
- **Major:** increment X, reset Y and Z (4.2.8 → 5.0.0)
|
||||
|
||||
## Step 4: Preview Changes
|
||||
|
||||
**BEFORE making changes, show the user:**
|
||||
|
||||
```
|
||||
Current version: 4.2.8
|
||||
New version: 4.2.9 (PATCH)
|
||||
Reason: Fixed database query bug
|
||||
|
||||
Files to update:
|
||||
- package.json: "version": "4.2.9"
|
||||
- marketplace.json: "version": "4.2.9"
|
||||
- plugin.json: "version": "4.2.9"
|
||||
- CLAUDE.md line 9: "**Current Version**: 4.2.9" (version number ONLY)
|
||||
- Git tag: v4.2.9
|
||||
|
||||
Proceed? (yes/no)
|
||||
```
|
||||
|
||||
Wait for user confirmation before proceeding.
|
||||
|
||||
## Step 5: Update Files
|
||||
|
||||
### Update package.json
|
||||
|
||||
File: `package.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "4.2.9",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Update line 3 with new version.
|
||||
|
||||
### Update marketplace.json
|
||||
|
||||
File: `.claude-plugin/marketplace.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "4.2.9",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Update line 13 with new version.
|
||||
|
||||
### Update plugin.json
|
||||
|
||||
File: `plugin/.claude-plugin/plugin.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "4.2.9",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Update line 3 with new version.
|
||||
|
||||
### Update CLAUDE.md
|
||||
|
||||
File: `CLAUDE.md`
|
||||
|
||||
**ONLY update line 9 with the version number:**
|
||||
|
||||
```markdown
|
||||
**Current Version**: 4.2.9
|
||||
```
|
||||
|
||||
**CRITICAL:** DO NOT add version history entries to CLAUDE.md. Version history is managed separately outside this skill.
|
||||
|
||||
## Step 6: Verify Consistency
|
||||
|
||||
```bash
|
||||
# Check all versions match
|
||||
grep -n '"version"' package.json .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json
|
||||
|
||||
# Should show same version in all three files:
|
||||
# package.json:3: "version": "4.2.9",
|
||||
# .claude-plugin/marketplace.json:13: "version": "4.2.9",
|
||||
# plugin/.claude-plugin/plugin.json:3: "version": "4.2.9",
|
||||
```
|
||||
|
||||
All three must match exactly.
|
||||
|
||||
## Step 7: Test
|
||||
|
||||
```bash
|
||||
# Verify the plugin loads correctly
|
||||
npm run build
|
||||
```
|
||||
|
||||
Build must succeed before proceeding.
|
||||
|
||||
## Step 8: Commit and Tag
|
||||
|
||||
```bash
|
||||
# Stage all version files
|
||||
git add package.json .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json CLAUDE.md plugin/scripts/
|
||||
|
||||
# Commit with descriptive message
|
||||
git commit -m "Release vX.Y.Z: [Brief description]
|
||||
|
||||
[Optional detailed description]
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>"
|
||||
|
||||
# Create annotated git tag
|
||||
git tag vX.Y.Z -m "Release vX.Y.Z: [Brief description]"
|
||||
|
||||
# Push commit and tags
|
||||
git push && git push --tags
|
||||
```
|
||||
|
||||
Replace `X.Y.Z` with actual version (e.g., `4.2.9`).
|
||||
|
||||
## Step 9: Create GitHub Release
|
||||
|
||||
```bash
|
||||
# Create GitHub release from the tag
|
||||
gh release create vX.Y.Z --title "vX.Y.Z" --notes "[Brief release notes]"
|
||||
|
||||
# Or generate notes automatically from commits
|
||||
gh release create vX.Y.Z --title "vX.Y.Z" --generate-notes
|
||||
```
|
||||
|
||||
**IMPORTANT:** Always create the GitHub release immediately after pushing the tag. This makes the release discoverable to users and triggers any automated workflows.
|
||||
|
||||
## Step 10: Generate CHANGELOG
|
||||
|
||||
After creating the GitHub release, regenerate CHANGELOG.md from all releases:
|
||||
|
||||
```bash
|
||||
# Generate CHANGELOG.md from all GitHub releases
|
||||
npm run changelog:generate
|
||||
|
||||
# Review the generated changelog
|
||||
git diff CHANGELOG.md
|
||||
|
||||
# Commit and push the updated changelog
|
||||
git add CHANGELOG.md
|
||||
git commit -m "Update CHANGELOG.md for vX.Y.Z release"
|
||||
git push
|
||||
```
|
||||
|
||||
**Why this step:**
|
||||
- CHANGELOG.md is auto-generated from GitHub releases
|
||||
- Keeps the changelog in sync with release notes
|
||||
- No manual editing required
|
||||
- Single source of truth: GitHub releases
|
||||
|
||||
## Verification
|
||||
|
||||
After completing all steps, verify:
|
||||
|
||||
```bash
|
||||
# Check git tag created
|
||||
git tag -l | grep vX.Y.Z
|
||||
|
||||
# Check remote has tag
|
||||
git ls-remote --tags origin | grep vX.Y.Z
|
||||
|
||||
# Check GitHub release exists
|
||||
gh release view vX.Y.Z
|
||||
|
||||
# Verify versions match
|
||||
grep '"version"' package.json .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json
|
||||
```
|
||||
|
||||
All checks should pass.
|
||||
|
||||
## Rollback (If Needed)
|
||||
|
||||
If you made a mistake:
|
||||
|
||||
```bash
|
||||
# Delete local tag
|
||||
git tag -d vX.Y.Z
|
||||
|
||||
# Delete remote tag (if already pushed)
|
||||
git push origin :refs/tags/vX.Y.Z
|
||||
|
||||
# Delete GitHub release (if created)
|
||||
gh release delete vX.Y.Z
|
||||
|
||||
# Revert commits if needed
|
||||
git revert HEAD
|
||||
```
|
||||
|
||||
Then restart the workflow with correct version.
|
||||
@@ -0,0 +1,57 @@
|
||||
name: Claude Code Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
# Optional: Only run on specific file changes
|
||||
# paths:
|
||||
# - "src/**/*.ts"
|
||||
# - "src/**/*.tsx"
|
||||
# - "src/**/*.js"
|
||||
# - "src/**/*.jsx"
|
||||
|
||||
jobs:
|
||||
claude-review:
|
||||
# Optional: Filter by PR author
|
||||
# if: |
|
||||
# github.event.pull_request.user.login == 'external-contributor' ||
|
||||
# github.event.pull_request.user.login == 'new-developer' ||
|
||||
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
issues: read
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude Code Review
|
||||
id: claude-review
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
prompt: |
|
||||
REPO: ${{ github.repository }}
|
||||
PR NUMBER: ${{ github.event.pull_request.number }}
|
||||
|
||||
Please review this pull request and provide feedback on:
|
||||
- Code quality and best practices
|
||||
- Potential bugs or issues
|
||||
- Performance considerations
|
||||
- Security concerns
|
||||
- Test coverage
|
||||
|
||||
Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback.
|
||||
|
||||
Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR.
|
||||
|
||||
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
||||
# or https://docs.claude.com/en/docs/claude-code/cli-reference for available options
|
||||
claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"'
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Claude Code
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
types: [opened, assigned]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
|
||||
jobs:
|
||||
claude:
|
||||
if: |
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
||||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
issues: read
|
||||
id-token: write
|
||||
actions: read # Required for Claude to read CI results on PRs
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude Code
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
|
||||
# This is an optional setting that allows Claude to read CI results on PRs
|
||||
additional_permissions: |
|
||||
actions: read
|
||||
|
||||
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
|
||||
# prompt: 'Update the pull request description to include a summary of changes.'
|
||||
|
||||
# Optional: Add claude_args to customize behavior and configuration
|
||||
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
||||
# or https://docs.claude.com/en/docs/claude-code/cli-reference for available options
|
||||
# claude_args: '--allowed-tools Bash(gh pr:*)'
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
node_modules/
|
||||
dist/
|
||||
*.log
|
||||
.DS_Store
|
||||
.env
|
||||
.env.local
|
||||
*.tmp
|
||||
*.temp
|
||||
.install-version
|
||||
.claude/settings.local.json
|
||||
plugin/data/
|
||||
plugin/data.backup/
|
||||
package-lock.json
|
||||
private/
|
||||
|
||||
# Generated UI files (built from viewer-template.html)
|
||||
src/ui/viewer.html
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"old-claude-mem": {
|
||||
"command": "uvx",
|
||||
"args": [
|
||||
"chroma-mcp",
|
||||
"--client-type",
|
||||
"persistent",
|
||||
"--data-dir",
|
||||
"/Users/alexnewman/.claude-mem/backups/chroma-backup-20251005-222403"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/* To @claude: be vigilant about only leaving evergreen context in this file, claude-mem handles working context separately. */
|
||||
|
||||
# Claude-Mem: AI Development Instructions
|
||||
|
||||
## What This Project Is
|
||||
|
||||
Claude-mem is a Claude Code plugin providing persistent memory across sessions. It captures tool usage, compresses observations using the Claude Agent SDK, and injects relevant context into future sessions.
|
||||
|
||||
**Current Version**: 7.0.2
|
||||
|
||||
## Architecture
|
||||
|
||||
**5 Lifecycle Hooks**: SessionStart → UserPromptSubmit → PostToolUse → Summary → SessionEnd
|
||||
|
||||
**Hooks** (`src/hooks/*.ts`) - TypeScript → ESM, built to `plugin/scripts/*-hook.js`
|
||||
|
||||
**Worker Service** (`src/services/worker-service.ts`) - Express API on port 37777, PM2-managed, handles AI processing asynchronously
|
||||
|
||||
**Database** (`src/services/sqlite/`) - SQLite3 at `~/.claude-mem/claude-mem.db` with FTS5 full-text search
|
||||
|
||||
**Search Skill** (`plugin/skills/mem-search/SKILL.md`) - HTTP API for searching past work, auto-invoked when users ask about history
|
||||
|
||||
**Chroma** (`src/services/sync/ChromaSync.ts`) - Vector embeddings for semantic search
|
||||
|
||||
**Viewer UI** (`src/ui/viewer/`) - React interface at http://localhost:37777, built to `plugin/ui/viewer.html`
|
||||
|
||||
## Privacy Tags
|
||||
|
||||
**Dual-Tag System** for meta-observation control:
|
||||
- `<private>content</private>` - User-level privacy control (manual, prevents storage)
|
||||
- `<claude-mem-context>content</claude-mem-context>` - System-level tag (auto-injected observations, prevents recursive storage)
|
||||
|
||||
**Implementation**: Tag stripping happens at hook layer (edge processing) before data reaches worker/database. See `src/utils/tag-stripping.ts` for shared utilities.
|
||||
|
||||
## Build Commands
|
||||
|
||||
**Hooks only**: `npm run build && npm run sync-marketplace`
|
||||
|
||||
**Worker changes**: `npm run build && npm run sync-marketplace && npm run worker:restart`
|
||||
|
||||
**Skills only**: `npm run sync-marketplace`
|
||||
|
||||
**Viewer UI**: `npm run build && npm run sync-marketplace && npm run worker:restart`
|
||||
|
||||
## Environment Variables
|
||||
|
||||
- `CLAUDE_MEM_MODEL` - Model for observations/summaries (default: claude-haiku-4-5)
|
||||
- `CLAUDE_MEM_CONTEXT_OBSERVATIONS` - Observations injected at SessionStart (default: 50)
|
||||
- `CLAUDE_MEM_WORKER_PORT` - Worker service port (default: 37777)
|
||||
- `CLAUDE_MEM_PYTHON_VERSION` - Python version for uvx/chroma-mcp (default: 3.13, avoids onnxruntime compatibility issues with Python 3.14+)
|
||||
|
||||
## File Locations
|
||||
|
||||
- **Source**: `<project-root>/src/`
|
||||
- **Built Plugin**: `<project-root>/plugin/`
|
||||
- **Installed Plugin**: `~/.claude/plugins/marketplaces/thedotmack/`
|
||||
- **Database**: `~/.claude-mem/claude-mem.db`
|
||||
- **Chroma**: `~/.claude-mem/chroma/`
|
||||
- **Usage Logs**: `~/.claude-mem/usage-logs/usage-YYYY-MM-DD.jsonl`
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
npm run build # Compile TypeScript
|
||||
npm run sync-marketplace # Copy to ~/.claude/plugins
|
||||
npm run worker:restart # Restart PM2 worker
|
||||
npm run worker:logs # View worker logs
|
||||
pm2 list # Check worker status
|
||||
pm2 delete claude-mem-worker # Force clean start
|
||||
```
|
||||
|
||||
**Viewer UI**: http://localhost:37777
|
||||
@@ -1,86 +1,406 @@
|
||||
# 🧠 Claude Memory System (claude-mem)
|
||||
<h1 align="center">
|
||||
<br>
|
||||
<a href="https://github.com/thedotmack/claude-mem">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/claude-mem-logo-for-dark-mode.webp">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/claude-mem-logo-for-light-mode.webp">
|
||||
<img src="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/claude-mem-logo-for-light-mode.webp" alt="Claude-Mem" width="400">
|
||||
</picture>
|
||||
</a>
|
||||
<br>
|
||||
</h1>
|
||||
|
||||
## Remember that one thing? Neither do we… but `claude-mem` does! 😵💫
|
||||
<h4 align="center">Persistent memory compression system built for <a href="https://claude.com/claude-code" target="_blank">Claude Code</a>.</h4>
|
||||
|
||||
Stop repeating yourself. `claude-mem` remembers what you and Claude Code figure out, so every new chat starts smarter than the last.
|
||||
<p align="center">
|
||||
<a href="LICENSE">
|
||||
<img src="https://img.shields.io/badge/License-AGPL%203.0-blue.svg" alt="License">
|
||||
</a>
|
||||
<a href="package.json">
|
||||
<img src="https://img.shields.io/badge/version-6.5.0-green.svg" alt="Version">
|
||||
</a>
|
||||
<a href="package.json">
|
||||
<img src="https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg" alt="Node">
|
||||
</a>
|
||||
<a href="https://github.com/thedotmack/awesome-claude-code">
|
||||
<img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Claude Code">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## ⚡️ 10‑Second Setup
|
||||
<br>
|
||||
|
||||
```bash
|
||||
npm install -g claude-mem && claude-mem install
|
||||
```
|
||||
<p align="center">
|
||||
<a href="https://github.com/thedotmack/claude-mem">
|
||||
<picture>
|
||||
<img src="https://raw.githubusercontent.com/thedotmack/claude-mem/main/docs/public/cm-preview.gif" alt="Claude-Mem Preview" width="800">
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
That’s it. Restart Claude Code and you’re good. No config. No tedious setup or dependencies.
|
||||
<p align="center">
|
||||
<a href="#quick-start">Quick Start</a> •
|
||||
<a href="#how-it-works">How It Works</a> •
|
||||
<a href="#mcp-search-tools">Search Tools</a> •
|
||||
<a href="#documentation">Documentation</a> •
|
||||
<a href="#configuration">Configuration</a> •
|
||||
<a href="#troubleshooting">Troubleshooting</a> •
|
||||
<a href="#license">License</a>
|
||||
</p>
|
||||
|
||||
## ✨ What You Get
|
||||
|
||||
- Remembers key insights from your chats with Claude Code
|
||||
- Starts new sessions with the right context
|
||||
- Works quietly in the background
|
||||
- One-command install and status check
|
||||
|
||||
## 🗑️ Smart Trash™ (Your Panic Button)
|
||||
|
||||
Delete something by accident? It’s not gone.
|
||||
- Everything goes to `~/.claude-mem/trash/`
|
||||
- Restore with a single command: `claude-mem restore`
|
||||
- Timestamped so you can see when things moved
|
||||
|
||||
## 🎯 Why It’s Useful
|
||||
|
||||
- No more re-explaining your project over and over
|
||||
- Pick up exactly where you left off
|
||||
- Find past solutions fast when you face a familiar bug
|
||||
- Your knowledge compounds the more you use it
|
||||
|
||||
## 🧭 Minimal Commands You’ll Ever Need
|
||||
|
||||
```bash
|
||||
claude-mem install # Set up/repair integration
|
||||
claude-mem status # Check everything’s working
|
||||
claude-mem load-context # Peek at what it remembers
|
||||
claude-mem logs # If you’re curious
|
||||
claude-mem uninstall # Remove hooks
|
||||
|
||||
# Extras
|
||||
claude-mem trash-view # See what’s in Smart Trash™
|
||||
claude-mem restore # Restore deleted items
|
||||
```
|
||||
|
||||
## 📁 Where Stuff Lives (super simple)
|
||||
|
||||
```
|
||||
~/.claude-mem/
|
||||
├── index/ # memory index
|
||||
├── archives/ # transcripts
|
||||
├── hooks/ # integration bits
|
||||
├── trash/ # Smart Trash™
|
||||
└── logs/ # diagnostics
|
||||
```
|
||||
|
||||
## ✅ Requirements
|
||||
|
||||
- Node.js 18+
|
||||
- Claude Code
|
||||
|
||||
## 🆘 If Something’s Weird
|
||||
|
||||
```bash
|
||||
claude-mem status # quick health check
|
||||
claude-mem install --force # fixes most issues
|
||||
```
|
||||
|
||||
## 📄 License
|
||||
|
||||
Licensed under AGPL-3.0. See `LICENSE`.
|
||||
<p align="center">
|
||||
Claude-Mem seamlessly preserves context across sessions by automatically capturing tool usage observations, generating semantic summaries, and making them available to future sessions. This enables Claude to maintain continuity of knowledge about projects even after sessions end or reconnect.
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## Ready to remember more and repeat less?
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
npm install -g claude-mem
|
||||
claude-mem install
|
||||
Start a new Claude Code session in the terminal and enter the following commands:
|
||||
|
||||
```
|
||||
> /plugin marketplace add thedotmack/claude-mem
|
||||
|
||||
> /plugin install claude-mem
|
||||
```
|
||||
|
||||
Your future self will thank you. 🧠✨
|
||||
Restart Claude Code. Context from previous sessions will automatically appear in new sessions.
|
||||
|
||||
**Key Features:**
|
||||
|
||||
- 🧠 **Persistent Memory** - Context survives across sessions
|
||||
- 📊 **Progressive Disclosure** - Layered memory retrieval with token cost visibility
|
||||
- 🔍 **Skill-Based Search** - Query your project history with mem-search skill (~2,250 token savings)
|
||||
- 🖥️ **Web Viewer UI** - Real-time memory stream at http://localhost:37777
|
||||
- 🔒 **Privacy Control** - Use `<private>` tags to exclude sensitive content from storage
|
||||
- ⚙️ **Context Configuration** - Fine-grained control over what context gets injected
|
||||
- 🤖 **Automatic Operation** - No manual intervention required
|
||||
- 🔗 **Citations** - Reference past decisions with `claude-mem://` URIs
|
||||
- 🧪 **Beta Channel** - Try experimental features like Endless Mode via version switching
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
📚 **[View Full Documentation](docs/)** - Browse markdown docs on GitHub
|
||||
|
||||
💻 **Local Preview**: Run Mintlify docs locally:
|
||||
|
||||
```bash
|
||||
cd docs
|
||||
npx mintlify dev
|
||||
```
|
||||
|
||||
### Getting Started
|
||||
|
||||
- **[Installation Guide](https://docs.claude-mem.ai/installation)** - Quick start & advanced installation
|
||||
- **[Usage Guide](https://docs.claude-mem.ai/usage/getting-started)** - How Claude-Mem works automatically
|
||||
- **[Search Tools](https://docs.claude-mem.ai/usage/search-tools)** - Query your project history with natural language
|
||||
- **[Beta Features](https://docs.claude-mem.ai/beta-features)** - Try experimental features like Endless Mode
|
||||
|
||||
### Best Practices
|
||||
|
||||
- **[Context Engineering](https://docs.claude-mem.ai/context-engineering)** - AI agent context optimization principles
|
||||
- **[Progressive Disclosure](https://docs.claude-mem.ai/progressive-disclosure)** - Philosophy behind Claude-Mem's context priming strategy
|
||||
|
||||
### Architecture
|
||||
|
||||
- **[Overview](https://docs.claude-mem.ai/architecture/overview)** - System components & data flow
|
||||
- **[Architecture Evolution](https://docs.claude-mem.ai/architecture-evolution)** - The journey from v3 to v5
|
||||
- **[Hooks Architecture](https://docs.claude-mem.ai/hooks-architecture)** - How Claude-Mem uses lifecycle hooks
|
||||
- **[Hooks Reference](https://docs.claude-mem.ai/architecture/hooks)** - 7 hook scripts explained
|
||||
- **[Worker Service](https://docs.claude-mem.ai/architecture/worker-service)** - HTTP API & PM2 management
|
||||
- **[Database](https://docs.claude-mem.ai/architecture/database)** - SQLite schema & FTS5 search
|
||||
- **[Search Architecture](https://docs.claude-mem.ai/architecture/search-architecture)** - Hybrid search with Chroma vector database
|
||||
|
||||
### Configuration & Development
|
||||
|
||||
- **[Configuration](https://docs.claude-mem.ai/configuration)** - Environment variables & settings
|
||||
- **[Development](https://docs.claude-mem.ai/development)** - Building, testing, contributing
|
||||
- **[Troubleshooting](https://docs.claude-mem.ai/troubleshooting)** - Common issues & solutions
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Session Start → Inject recent observations as context │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ User Prompts → Create session, save user prompts │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Tool Executions → Capture observations (Read, Write, etc.) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Worker Processes → Extract learnings via Claude Agent SDK │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Session Ends → Generate summary, ready for next session │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Core Components:**
|
||||
|
||||
1. **5 Lifecycle Hooks** - SessionStart, UserPromptSubmit, PostToolUse, Stop, SessionEnd (6 hook scripts)
|
||||
2. **Smart Install** - Cached dependency checker (pre-hook script, not a lifecycle hook)
|
||||
3. **Worker Service** - HTTP API on port 37777 with web viewer UI and 10 search endpoints, managed by PM2
|
||||
4. **SQLite Database** - Stores sessions, observations, summaries with FTS5 full-text search
|
||||
5. **mem-search Skill** - Natural language queries with progressive disclosure (~2,250 token savings vs MCP)
|
||||
6. **Chroma Vector Database** - Hybrid semantic + keyword search for intelligent context retrieval
|
||||
|
||||
See [Architecture Overview](https://docs.claude-mem.ai/architecture/overview) for details.
|
||||
|
||||
---
|
||||
|
||||
## mem-search Skill
|
||||
|
||||
Claude-Mem provides intelligent search through the mem-search skill that auto-invokes when you ask about past work:
|
||||
|
||||
**How It Works:**
|
||||
- Just ask naturally: *"What did we do last session?"* or *"Did we fix this bug before?"*
|
||||
- Claude automatically invokes the mem-search skill to find relevant context
|
||||
- ~2,250 token savings per session start vs MCP approach
|
||||
|
||||
**Available Search Operations:**
|
||||
|
||||
1. **Search Observations** - Full-text search across observations
|
||||
2. **Search Sessions** - Full-text search across session summaries
|
||||
3. **Search Prompts** - Search raw user requests
|
||||
4. **By Concept** - Find by concept tags (discovery, problem-solution, pattern, etc.)
|
||||
5. **By File** - Find observations referencing specific files
|
||||
6. **By Type** - Find by type (decision, bugfix, feature, refactor, discovery, change)
|
||||
7. **Recent Context** - Get recent session context for a project
|
||||
8. **Timeline** - Get unified timeline of context around a specific point in time
|
||||
9. **Timeline by Query** - Search for observations and get timeline context around best match
|
||||
10. **API Help** - Get search API documentation
|
||||
|
||||
**Example Natural Language Queries:**
|
||||
|
||||
```
|
||||
"What bugs did we fix last session?"
|
||||
"How did we implement authentication?"
|
||||
"What changes were made to worker-service.ts?"
|
||||
"Show me recent work on this project"
|
||||
"What was happening when we added the viewer UI?"
|
||||
```
|
||||
|
||||
See [Search Tools Guide](https://docs.claude-mem.ai/usage/search-tools) for detailed examples.
|
||||
|
||||
---
|
||||
|
||||
## Beta Features & Endless Mode
|
||||
|
||||
Claude-Mem offers a **beta channel** with experimental features. Switch between stable and beta versions directly from the web viewer UI.
|
||||
|
||||
### How to Try Beta
|
||||
|
||||
1. Open http://localhost:37777
|
||||
2. Click Settings (gear icon)
|
||||
3. In **Version Channel**, click "Try Beta (Endless Mode)"
|
||||
4. Wait for the worker to restart
|
||||
|
||||
Your memory data is preserved when switching versions.
|
||||
|
||||
### Endless Mode (Beta)
|
||||
|
||||
The flagship beta feature is **Endless Mode** - a biomimetic memory architecture that dramatically extends session length:
|
||||
|
||||
**The Problem**: Standard Claude Code sessions hit context limits after ~50 tool uses. Each tool adds 1-10k+ tokens, and Claude re-synthesizes all previous outputs on every response (O(N²) complexity).
|
||||
|
||||
**The Solution**: Endless Mode compresses tool outputs into ~500-token observations and transforms the transcript in real-time:
|
||||
|
||||
```
|
||||
Working Memory (Context): Compressed observations (~500 tokens each)
|
||||
Archive Memory (Disk): Full tool outputs preserved for recall
|
||||
```
|
||||
|
||||
**Expected Results**:
|
||||
- ~95% token reduction in context window
|
||||
- ~20x more tool uses before context exhaustion
|
||||
- Linear O(N) scaling instead of quadratic O(N²)
|
||||
- Full transcripts preserved for perfect recall
|
||||
|
||||
**Caveats**: Adds latency (60-90s per tool for observation generation), still experimental.
|
||||
|
||||
See [Beta Features Documentation](https://docs.claude-mem.ai/beta-features) for details.
|
||||
|
||||
---
|
||||
|
||||
## What's New
|
||||
|
||||
**v6.4.9 - Context Configuration Settings:**
|
||||
- 11 new settings for fine-grained control over context injection
|
||||
- Configure token economics display, observation filtering by type/concept
|
||||
- Control number of observations and which fields to display
|
||||
|
||||
**v6.4.0 - Dual-Tag Privacy System:**
|
||||
- `<private>` tags for user-controlled privacy - wrap sensitive content to exclude from storage
|
||||
- System-level `<claude-mem-context>` tags prevent recursive observation storage
|
||||
- Edge processing ensures private content never reaches database
|
||||
|
||||
**v6.3.0 - Version Channel:**
|
||||
- Switch between stable and beta versions from the web viewer UI
|
||||
- Try experimental features like Endless Mode without manual git operations
|
||||
|
||||
**Previous Highlights:**
|
||||
- **v6.0.0**: Major session management & transcript processing improvements
|
||||
- **v5.5.0**: mem-search skill enhancement with 100% effectiveness rate
|
||||
- **v5.4.0**: Skill-based search architecture (~2,250 tokens saved per session)
|
||||
- **v5.1.0**: Web-based viewer UI with real-time updates
|
||||
- **v5.0.0**: Hybrid search with Chroma vector database
|
||||
|
||||
See [CHANGELOG.md](CHANGELOG.md) for complete version history.
|
||||
|
||||
---
|
||||
|
||||
## System Requirements
|
||||
|
||||
- **Node.js**: 18.0.0 or higher
|
||||
- **Claude Code**: Latest version with plugin support
|
||||
- **PM2**: Process manager (bundled - no global install required)
|
||||
- **SQLite 3**: For persistent storage (bundled)
|
||||
|
||||
---
|
||||
|
||||
## Key Benefits
|
||||
|
||||
### Progressive Disclosure Context
|
||||
|
||||
- **Layered memory retrieval** mirrors human memory patterns
|
||||
- **Layer 1 (Index)**: See what observations exist with token costs at session start
|
||||
- **Layer 2 (Details)**: Fetch full narratives on-demand via MCP search
|
||||
- **Layer 3 (Perfect Recall)**: Access source code and original transcripts
|
||||
- **Smart decision-making**: Token counts help Claude choose between fetching details or reading code
|
||||
- **Type indicators**: Visual cues (🔴 critical, 🟤 decision, 🔵 informational) highlight observation importance
|
||||
|
||||
### Automatic Memory
|
||||
|
||||
- Context automatically injected when Claude starts
|
||||
- No manual commands or configuration needed
|
||||
- Works transparently in the background
|
||||
|
||||
### Full History Search
|
||||
|
||||
- Search across all sessions and observations
|
||||
- FTS5 full-text search for fast queries
|
||||
- Citations link back to specific observations
|
||||
|
||||
### Structured Observations
|
||||
|
||||
- AI-powered extraction of learnings
|
||||
- Categorized by type (decision, bugfix, feature, etc.)
|
||||
- Tagged with concepts and file references
|
||||
|
||||
### Multi-Prompt Sessions
|
||||
|
||||
- Sessions span multiple user prompts
|
||||
- Context preserved across `/clear` commands
|
||||
- Track entire conversation threads
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
**Model Selection:**
|
||||
|
||||
```bash
|
||||
./claude-mem-settings.sh
|
||||
```
|
||||
|
||||
**Environment Variables:**
|
||||
|
||||
- `CLAUDE_MEM_MODEL` - AI model for processing (default: claude-haiku-4-5)
|
||||
- `CLAUDE_MEM_WORKER_PORT` - Worker port (default: 37777)
|
||||
- `CLAUDE_MEM_DATA_DIR` - Data directory override (dev only)
|
||||
- `CLAUDE_MEM_PYTHON_VERSION` - Python version for uvx/chroma-mcp (default: 3.13)
|
||||
|
||||
See [Configuration Guide](https://docs.claude-mem.ai/configuration) for details.
|
||||
|
||||
---
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# Clone and build
|
||||
git clone https://github.com/thedotmack/claude-mem.git
|
||||
cd claude-mem
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
# Run tests
|
||||
npm test
|
||||
|
||||
# Start worker
|
||||
npm run worker:start
|
||||
|
||||
# View logs
|
||||
npm run worker:logs
|
||||
```
|
||||
|
||||
See [Development Guide](https://docs.claude-mem.ai/development) for detailed instructions.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Quick Diagnostic:**
|
||||
|
||||
If you're experiencing issues, describe the problem to Claude and the troubleshoot skill will automatically activate to diagnose and provide fixes.
|
||||
|
||||
**Common Issues:**
|
||||
|
||||
- Worker not starting → `npm run worker:restart`
|
||||
- No context appearing → `npm run test:context`
|
||||
- Database issues → `sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;"`
|
||||
- Search not working → Check FTS5 tables exist
|
||||
|
||||
See [Troubleshooting Guide](https://docs.claude-mem.ai/troubleshooting) for complete solutions.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please:
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes with tests
|
||||
4. Update documentation
|
||||
5. Submit a Pull Request
|
||||
|
||||
See [Development Guide](https://docs.claude-mem.ai/development) for contribution workflow.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the **GNU Affero General Public License v3.0** (AGPL-3.0).
|
||||
|
||||
Copyright (C) 2025 Alex Newman (@thedotmack). All rights reserved.
|
||||
|
||||
See the [LICENSE](LICENSE) file for full details.
|
||||
|
||||
**What This Means:**
|
||||
|
||||
- You can use, modify, and distribute this software freely
|
||||
- If you modify and deploy on a network server, you must make your source code available
|
||||
- Derivative works must also be licensed under AGPL-3.0
|
||||
- There is NO WARRANTY for this software
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
- **Documentation**: [docs/](docs/)
|
||||
- **Issues**: [GitHub Issues](https://github.com/thedotmack/claude-mem/issues)
|
||||
- **Repository**: [github.com/thedotmack/claude-mem](https://github.com/thedotmack/claude-mem)
|
||||
- **Author**: Alex Newman ([@thedotmack](https://github.com/thedotmack))
|
||||
|
||||
---
|
||||
|
||||
**Built with Claude Agent SDK** | **Powered by Claude Code** | **Made with TypeScript**
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
argument-hint: help | save [message] | remember [context] | (no args for help)
|
||||
description: Manage claude-mem operations and memory context
|
||||
allowed-tools: Bash(claude-mem:*), Bash(echo:*), Bash(cat:*)
|
||||
---
|
||||
|
||||
## Claude-Mem Command Handler
|
||||
|
||||
### Check for help command first
|
||||
!`[ -z "$ARGUMENTS" ] || [ "$ARGUMENTS" = "help" ] && printf '%s\n' '## 🧠 Claude-Mem Help' '' '**Available Commands:**' '' '• /claude-mem save [message] - Quick save of conversation overview' '• /claude-mem remember [query] - Search saved memories' '• /claude-mem help - Show this help' '' '**Quick Shortcuts:**' '• /save - Direct save' '• /remember - Direct search' '' '**About /save:**' 'Quick way to save an overview to claude-mem without processing the' 'entire transcript. Use this when you dont need a detailed archive,' 'just a summary of key points and decisions.' '' '**Optional Features (configure during install):**' '• Compress on /clear: Archives full transcript when clearing (off by default)' '• Session start: Loads recent memories when starting Claude Code' '' 'For more details: claude-mem --help' && exit 0`
|
||||
|
||||
### Process other commands
|
||||
Handle claude-mem operation: $ARGUMENTS
|
||||
|
||||
If $ARGUMENTS starts with "save":
|
||||
- Write an overview of the current conversation context
|
||||
- Add it to claude-mem using the chroma MCP tools
|
||||
- Save the overview using: `claude-mem save "your overview message"`
|
||||
|
||||
If $ARGUMENTS starts with "remember":
|
||||
- Search claude-mem for relevant memories using the query
|
||||
- Display the most relevant memories from previous sessions
|
||||
- Use chroma_query_documents to find and present context
|
||||
@@ -1 +0,0 @@
|
||||
Search claude-mem for #$ARGUMENTS and look up relevant context to help clarify what we are working on.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
allowed-tools: Bash
|
||||
description: Write an overview and save with claude-mem
|
||||
---
|
||||
**Write an overview** of the current conversation context and:
|
||||
1. **Add it to claude-mem** using the chroma MCP tools. Always use primitive types (strings, numbers, booleans) when calling MCP Chroma tools directly. Arrays should be comma-separated strings, and nested objects should be flattened.
|
||||
2. **Save the overview to index** using the claude-mem CLI tool: `claude-mem save "your overview message"`
|
||||
@@ -0,0 +1,360 @@
|
||||
# Dual-Tag System Architecture
|
||||
|
||||
**Date**: 2025-11-30
|
||||
**Branch**: `feature/meta-observation-control`
|
||||
**Status**: Implemented
|
||||
**Based on**: PR #105 dual-tag system
|
||||
|
||||
## Overview
|
||||
|
||||
The dual-tag system provides fine-grained control over what content gets persisted in claude-mem's observation database. It uses an edge processing pattern to filter tagged content at the hook layer before it reaches the worker service.
|
||||
|
||||
## The Two Tags
|
||||
|
||||
### Tag 1: `<private>`
|
||||
**Purpose**: User-controlled privacy
|
||||
**Status**: User-facing feature (documented)
|
||||
**Use case**: Users wrap content they don't want persisted
|
||||
|
||||
```xml
|
||||
<private>
|
||||
This content won't be stored in observations
|
||||
</private>
|
||||
```
|
||||
|
||||
**Examples**:
|
||||
- Sensitive information (API keys, credentials, internal URLs)
|
||||
- Temporary context (deadlines, personal notes)
|
||||
- Debug output (logs, stack traces)
|
||||
- Exploratory prompts (brainstorming, hypotheticals)
|
||||
|
||||
### Tag 2: `<claude-mem-context>`
|
||||
**Purpose**: System-level meta-observation control
|
||||
**Status**: Infrastructure-ready (not user-facing yet)
|
||||
**Use case**: Prevents recursive storage when real-time context injection is active
|
||||
|
||||
```xml
|
||||
<claude-mem-context>
|
||||
# Relevant Context from Past Sessions
|
||||
|
||||
[Auto-injected past observations...]
|
||||
</claude-mem-context>
|
||||
```
|
||||
|
||||
**Context**: This tag is used by the real-time context injection feature (not yet shipped). When past observations are injected into new prompts, they're wrapped in this tag to prevent them from being re-stored as new observations (recursive storage problem).
|
||||
|
||||
## Architecture Pattern: Edge Processing
|
||||
|
||||
**Principle**: "Process at edge, send clean data to server"
|
||||
|
||||
The dual-tag system follows the edge processing pattern from hooks-in-composition:
|
||||
|
||||
```text
|
||||
UserPrompt → [Hook Layer] → Worker → Database
|
||||
↑
|
||||
Filter here
|
||||
(strip tags at edge)
|
||||
```
|
||||
|
||||
### Data Flow
|
||||
|
||||
**Without Filtering** (broken):
|
||||
```
|
||||
UserPrompt with <private> → PostToolUse hook → Worker → Memory Agent → Database
|
||||
↓
|
||||
Private content stored
|
||||
```
|
||||
|
||||
**With Edge Processing** (correct):
|
||||
```
|
||||
UserPrompt with <private> → PostToolUse hook → stripMemoryTags() → Worker → Memory Agent → Database
|
||||
↑ ↓
|
||||
Filter at edge Only clean data stored
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### File: `src/hooks/save-hook.ts`
|
||||
|
||||
**Function Added** (lines 31-53):
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Strip memory tags to prevent recursive storage and enable privacy control
|
||||
*/
|
||||
function stripMemoryTags(content: string): string {
|
||||
if (typeof content !== 'string') {
|
||||
silentDebug('[save-hook] stripMemoryTags received non-string:', { type: typeof content });
|
||||
return '{}'; // Safe default for JSON context
|
||||
}
|
||||
|
||||
return content
|
||||
.replace(/<claude-mem-context>[\s\S]*?<\/claude-mem-context>/g, '')
|
||||
.replace(/<private>[\s\S]*?<\/private>/g, '')
|
||||
.trim();
|
||||
}
|
||||
```
|
||||
|
||||
**Application** (lines 95-100):
|
||||
|
||||
```typescript
|
||||
tool_input: tool_input !== undefined
|
||||
? stripMemoryTags(JSON.stringify(tool_input))
|
||||
: '{}',
|
||||
tool_response: tool_response !== undefined
|
||||
? stripMemoryTags(JSON.stringify(tool_response))
|
||||
: '{}',
|
||||
```
|
||||
|
||||
### File: `tests/strip-memory-tags.test.ts`
|
||||
|
||||
**Test Coverage**: 19 tests across 4 categories:
|
||||
|
||||
1. **Basic Functionality** (7 tests)
|
||||
- Strip `<claude-mem-context>` tags
|
||||
- Strip `<private>` tags
|
||||
- Strip both tag types
|
||||
- Handle nested tags
|
||||
- Multiline content
|
||||
- Multiple tags
|
||||
- Empty results
|
||||
|
||||
2. **Edge Cases** (5 tests)
|
||||
- Malformed tags (unclosed)
|
||||
- Tag-like strings (not actual tags)
|
||||
- Very large content (10k+ chars)
|
||||
- Whitespace trimming
|
||||
- Strings without tags
|
||||
|
||||
3. **Type Safety** (5 tests)
|
||||
- Non-string inputs (number, null, undefined, object, array)
|
||||
- All return safe default '{}'
|
||||
|
||||
4. **Real-World Scenarios** (2 tests)
|
||||
- JSON.stringify output
|
||||
- Efficient large content handling
|
||||
|
||||
**All tests passing** ✅ (19/19)
|
||||
|
||||
## Design Decisions
|
||||
|
||||
### 1. Always Active (No Configuration)
|
||||
|
||||
**Decision**: Tag stripping is always on, no environment variable needed
|
||||
**Rationale**: Privacy and anti-recursion protection should be default, not opt-in
|
||||
|
||||
### 2. Edge Processing (Not Worker-Level)
|
||||
|
||||
**Decision**: Filter at hook layer before sending to worker
|
||||
**Rationale**:
|
||||
- Keeps worker service simple
|
||||
- Follows one-way data stream
|
||||
- No worker changes needed
|
||||
- Hook becomes a filter/gateway
|
||||
|
||||
### 3. Defensive Coding with Silent Debug
|
||||
|
||||
**Decision**: Handle non-string inputs with silentDebug, return safe default
|
||||
**Rationale**:
|
||||
- Never block the agent (hooks-in-composition principle)
|
||||
- Log issues for observability
|
||||
- Safe fallback maintains system stability
|
||||
|
||||
### 4. Both Tags Now (Progressive Enhancement)
|
||||
|
||||
**Decision**: Implement both tags even though only `<private>` is user-facing
|
||||
**Rationale**:
|
||||
- Infrastructure ready for real-time context feature
|
||||
- No rework needed when context injection ships
|
||||
- Same code path for both tags (simple)
|
||||
- Progressive enhancement approach
|
||||
|
||||
### 5. Regex-Based Stripping
|
||||
|
||||
**Decision**: Use regex `/<tag>[\s\S]*?<\/tag>/g` instead of XML parser
|
||||
**Rationale**:
|
||||
- No dependencies needed
|
||||
- Handles multiline content (`[\s\S]*?`)
|
||||
- Non-greedy (`*?`) prevents over-matching
|
||||
- Global flag (`g`) handles multiple tags
|
||||
- Good enough for this use case
|
||||
|
||||
## Edge Cases Handled
|
||||
|
||||
| Case | Input | Output | Why |
|
||||
|------|-------|--------|-----|
|
||||
| Nested tags | `<private>a <private>b</private> a</private>` | `` | Outer tag matches all |
|
||||
| Malformed | `<private>unclosed` | `<private>unclosed` | Regex requires closing tag |
|
||||
| Multiple | `<private>a</private> b <private>c</private>` | `b` | Global flag removes all |
|
||||
| Empty | `<private></private>` | `` | Matches and removes |
|
||||
| Tag-like | `<tag>not private</tag>` | `<tag>not private</tag>` | Different tag name |
|
||||
| Large content | 10MB+ string | (stripped) | O(n) regex handles it |
|
||||
| Non-string | `123`, `null`, `{}` | `'{}'` | Defensive default |
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### 1. Real-Time Context Injection
|
||||
|
||||
**Status**: Deferred (not in this PR)
|
||||
**When ready**: The `<claude-mem-context>` tag infrastructure is already in place
|
||||
|
||||
The missing piece is in `src/hooks/new-hook.ts`:
|
||||
- Select relevant observations from timeline
|
||||
- Wrap in `<claude-mem-context>` tags
|
||||
- Return via `hookSpecificOutput`
|
||||
- Tag stripping already handles the rest
|
||||
|
||||
### 2. System-Level Meta-Observation Tagging
|
||||
|
||||
**Concept**: Auto-tag observations about observations
|
||||
**Examples**:
|
||||
- Search skill results: `<claude-mem-context>[search results]</claude-mem-context>`
|
||||
- Memory lookups: Fetched observations wrapped in tag
|
||||
- Observation summaries: Meta-level analysis wrapped
|
||||
|
||||
**Implementation**: Tools/skills that produce meta-observations can wrap output in `<claude-mem-context>` tags to prevent recursive storage.
|
||||
|
||||
### 3. Additional Tag Types
|
||||
|
||||
**Potential tags**:
|
||||
- `<ephemeral>`: Content that should be seen but not stored (alias for `<private>`)
|
||||
- `<debug>`: Debug output that should be logged but not persisted
|
||||
- `<scratch>`: Thinking/planning content not meant for observations
|
||||
|
||||
**Note**: Current implementation handles any tag you add to the regex. Adding new tags requires one line change in `stripMemoryTags()`.
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
```bash
|
||||
node --test tests/strip-memory-tags.test.ts
|
||||
```
|
||||
**Expected**: 19/19 passing ✅
|
||||
|
||||
### Integration Tests
|
||||
|
||||
**Test 1: Basic Privacy**
|
||||
```bash
|
||||
# Submit prompt with <private> tag
|
||||
# Query database: should not contain private content
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations WHERE narrative LIKE '%<private>%';"
|
||||
# Expected: 0
|
||||
```
|
||||
|
||||
**Test 2: Dual Tags**
|
||||
```bash
|
||||
# Submit prompt with both tags
|
||||
# Verify neither tag appears in database
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations WHERE narrative LIKE '%<private>%' OR narrative LIKE '%<claude-mem-context>%';"
|
||||
# Expected: 0
|
||||
```
|
||||
|
||||
**Test 3: Function Exists**
|
||||
```bash
|
||||
# Verify stripMemoryTags in built file
|
||||
grep -c "claude-mem-context.*private.*trim" ~/.claude/plugins/marketplaces/thedotmack/plugin/scripts/save-hook.js
|
||||
# Expected: 1
|
||||
```
|
||||
|
||||
### Regression Tests
|
||||
|
||||
**Ensure**:
|
||||
- Normal observations still work (no tags broken)
|
||||
- Worker service receives clean data
|
||||
- No errors in `~/.claude-mem/silent.log`
|
||||
- Tool executions still captured correctly
|
||||
|
||||
## Known Limitations
|
||||
|
||||
### 1. Tag Format is Fixed
|
||||
|
||||
Tags must use exact XML-style format: `<tag>content</tag>`
|
||||
|
||||
**Won't work**:
|
||||
- `[private]content[/private]` (wrong syntax)
|
||||
- `<!-- private -->content<!-- /private -->` (comment syntax)
|
||||
- `{{private}}content{{/private}}` (curly braces)
|
||||
|
||||
**Future**: Could add support for alternative formats if needed.
|
||||
|
||||
### 2. Partial Tag Matching
|
||||
|
||||
If user writes about tags without intending to use them:
|
||||
```
|
||||
I want to add a <private> tag feature to my app
|
||||
```
|
||||
|
||||
This won't be stripped (no closing tag). But if they accidentally write:
|
||||
```
|
||||
I want to add a <private>tag</private> feature
|
||||
```
|
||||
|
||||
"tag" gets stripped.
|
||||
|
||||
**Mitigation**: Documentation educates users on proper usage.
|
||||
|
||||
### 3. Performance with Very Large Content
|
||||
|
||||
Regex performance is O(n) where n = content length.
|
||||
|
||||
**Tested**: Works fine with 10,000 character strings
|
||||
**Unknown**: Performance with multi-megabyte tool responses
|
||||
|
||||
**Mitigation**: Most tool I/O is small. If issues arise, could optimize with:
|
||||
- Early exit if no '<' character found
|
||||
- Streaming regex for very large content
|
||||
- Size limits on stripMemoryTags input
|
||||
|
||||
## Documentation
|
||||
|
||||
### User-Facing
|
||||
|
||||
**Location**: `docs/public/usage/private-tags.mdx`
|
||||
**Content**:
|
||||
- How to use `<private>` tags
|
||||
- Use cases and examples
|
||||
- Best practices
|
||||
- Troubleshooting
|
||||
|
||||
**Available in**: Mintlify docs site, navigation under "Get Started"
|
||||
|
||||
### Technical/Internal
|
||||
|
||||
**Location**: `docs/context/dual-tag-system-architecture.md` (this file)
|
||||
**Content**:
|
||||
- Complete dual-tag system architecture
|
||||
- Implementation details
|
||||
- Design decisions
|
||||
- Future enhancements
|
||||
|
||||
**Audience**: Contributors, maintainers, future developers
|
||||
|
||||
## References
|
||||
|
||||
### Original Work
|
||||
- **PR #105**: Real-time context injection with dual-tag system
|
||||
- **Branch**: `feature/real-time-context` (merged to main)
|
||||
- **Investigator**: @basher83
|
||||
|
||||
### Documentation
|
||||
- **Investigation**: `docs/context/real-time-context-recursive-memory-investigation.md`
|
||||
- **User Guide**: `docs/public/usage/private-tags.mdx`
|
||||
- **This Document**: `docs/context/dual-tag-system-architecture.md`
|
||||
|
||||
### Patterns Applied
|
||||
- **Edge Processing**: From hooks-in-composition pattern
|
||||
- **Never Block the Agent**: Defensive coding, safe defaults
|
||||
- **One-Way Data Stream**: Hook → Worker → Database
|
||||
|
||||
## Summary
|
||||
|
||||
The dual-tag system is a complete, production-ready implementation that:
|
||||
- ✅ Gives users privacy control via `<private>` tags
|
||||
- ✅ Prepares infrastructure for real-time context injection
|
||||
- ✅ Uses edge processing pattern for clean architecture
|
||||
- ✅ Has comprehensive test coverage (19 tests, all passing)
|
||||
- ✅ Includes user documentation and technical reference
|
||||
- ✅ Requires no configuration (always active)
|
||||
- ✅ Handles edge cases defensively
|
||||
|
||||
**Status**: Ready to ship 🚀
|
||||
@@ -0,0 +1,88 @@
|
||||
# Claude-Mem Public Documentation
|
||||
|
||||
## What This Folder Is
|
||||
|
||||
This `docs/public/` folder contains the **Mintlify documentation site** - the official user-facing documentation for claude-mem. It's a structured documentation platform with a specific file format and organization.
|
||||
|
||||
## Folder Structure
|
||||
|
||||
```
|
||||
docs/
|
||||
├── public/ ← You are here (Mintlify MDX files)
|
||||
│ ├── *.mdx - User-facing documentation pages
|
||||
│ ├── docs.json - Mintlify configuration and navigation
|
||||
│ ├── architecture/ - Technical architecture docs
|
||||
│ ├── usage/ - User guides and workflows
|
||||
│ └── *.webp, *.gif - Assets (logos, screenshots)
|
||||
└── context/ ← Internal documentation (DO NOT put here)
|
||||
└── *.md - Planning docs, audits, references
|
||||
```
|
||||
|
||||
## File Requirements
|
||||
|
||||
### Mintlify Documentation Files (.mdx)
|
||||
All official documentation files must be:
|
||||
- Written in `.mdx` format (Markdown with JSX support)
|
||||
- Listed in `docs.json` navigation structure
|
||||
- Follow Mintlify's schema and conventions
|
||||
|
||||
The documentation is organized into these sections:
|
||||
- **Get Started**: Introduction, installation, usage guides
|
||||
- **Best Practices**: Context engineering, progressive disclosure
|
||||
- **Configuration & Development**: Settings, dev workflow, troubleshooting
|
||||
- **Architecture**: System design, components, technical details
|
||||
|
||||
### Configuration File
|
||||
`docs.json` defines:
|
||||
- Site metadata (name, description, theme)
|
||||
- Navigation structure
|
||||
- Branding (logos, colors)
|
||||
- Footer links and social media
|
||||
|
||||
## What Does NOT Belong Here
|
||||
|
||||
**Planning documents, design docs, and reference materials go in `/docs/context/` instead:**
|
||||
|
||||
Files that belong in `/docs/context/` (NOT here):
|
||||
- Planning documents (`*-plan.md`, `*-outline.md`)
|
||||
- Implementation analysis (`*-audit.md`, `*-code-reference.md`)
|
||||
- Error tracking (`typescript-errors.md`)
|
||||
- Internal design documents
|
||||
- PR review responses
|
||||
- Reference materials (like `agent-sdk-ref.md`)
|
||||
- Work-in-progress documentation
|
||||
|
||||
## How to Add Official Documentation
|
||||
|
||||
1. Create a new `.mdx` file in the appropriate subdirectory
|
||||
2. Add the file path to `docs.json` navigation
|
||||
3. Use Mintlify's frontmatter and components
|
||||
4. Follow the existing documentation style
|
||||
5. Test locally: `npx mintlify dev`
|
||||
|
||||
## Development Workflow
|
||||
|
||||
**For contributors working on claude-mem:**
|
||||
- Read `/CLAUDE.md` in the project root for development instructions
|
||||
- Place planning/design docs in `/docs/context/`
|
||||
- Only add user-facing documentation to `/docs/public/`
|
||||
- Test documentation locally with Mintlify CLI before committing
|
||||
|
||||
## Testing Documentation
|
||||
|
||||
```bash
|
||||
# Validate docs structure
|
||||
npx mintlify validate
|
||||
|
||||
# Check for broken links
|
||||
npx mintlify broken-links
|
||||
|
||||
# Run local dev server
|
||||
npx mintlify dev
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
**Simple Rule**:
|
||||
- `/docs/public/` = Official user documentation (Mintlify .mdx files) ← YOU ARE HERE
|
||||
- `/docs/context/` = Internal docs, plans, references, audits
|
||||
@@ -0,0 +1,309 @@
|
||||
---
|
||||
title: "Database Architecture"
|
||||
description: "SQLite schema, FTS5 search, and data storage"
|
||||
---
|
||||
|
||||
# Database Architecture
|
||||
|
||||
Claude-Mem uses SQLite 3 with the better-sqlite3 native module for persistent storage and FTS5 for full-text search.
|
||||
|
||||
## Database Location
|
||||
|
||||
- **Current**: `~/.claude-mem/claude-mem.db`
|
||||
|
||||
**Note**: Despite the README claiming v4.0.0+ moved the database to `${CLAUDE_PLUGIN_ROOT}/data/`, the actual implementation still uses `~/.claude-mem/`.
|
||||
|
||||
## Database Implementation
|
||||
|
||||
**Primary Implementation**: better-sqlite3 (native SQLite module)
|
||||
- Used by: SessionStore and SessionSearch
|
||||
- Format: Synchronous API with better performance
|
||||
- **Note**: Database.ts (using bun:sqlite) is legacy code
|
||||
|
||||
## Core Tables
|
||||
|
||||
### 1. sdk_sessions
|
||||
|
||||
Tracks active and completed sessions.
|
||||
|
||||
```sql
|
||||
CREATE TABLE sdk_sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sdk_session_id TEXT UNIQUE NOT NULL,
|
||||
claude_session_id TEXT,
|
||||
project TEXT NOT NULL,
|
||||
prompt_counter INTEGER DEFAULT 0,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
created_at TEXT NOT NULL,
|
||||
created_at_epoch INTEGER NOT NULL,
|
||||
completed_at TEXT,
|
||||
completed_at_epoch INTEGER,
|
||||
last_activity_at TEXT,
|
||||
last_activity_epoch INTEGER
|
||||
);
|
||||
```
|
||||
|
||||
**Indexes**:
|
||||
- `idx_sdk_sessions_claude_session` on `claude_session_id`
|
||||
- `idx_sdk_sessions_project` on `project`
|
||||
- `idx_sdk_sessions_status` on `status`
|
||||
- `idx_sdk_sessions_created_at` on `created_at_epoch DESC`
|
||||
|
||||
### 2. observations
|
||||
|
||||
Individual tool executions with hierarchical structure.
|
||||
|
||||
```sql
|
||||
CREATE TABLE observations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
session_id TEXT NOT NULL,
|
||||
sdk_session_id TEXT NOT NULL,
|
||||
claude_session_id TEXT,
|
||||
project TEXT NOT NULL,
|
||||
prompt_number INTEGER,
|
||||
tool_name TEXT NOT NULL,
|
||||
correlation_id TEXT,
|
||||
|
||||
-- Hierarchical fields
|
||||
title TEXT,
|
||||
subtitle TEXT,
|
||||
narrative TEXT,
|
||||
text TEXT,
|
||||
facts TEXT,
|
||||
concepts TEXT,
|
||||
type TEXT,
|
||||
files_read TEXT,
|
||||
files_modified TEXT,
|
||||
|
||||
created_at TEXT NOT NULL,
|
||||
created_at_epoch INTEGER NOT NULL,
|
||||
|
||||
FOREIGN KEY (sdk_session_id) REFERENCES sdk_sessions(sdk_session_id)
|
||||
);
|
||||
```
|
||||
|
||||
**Observation Types**:
|
||||
- `decision` - Architectural or design decisions
|
||||
- `bugfix` - Bug fixes and corrections
|
||||
- `feature` - New features or capabilities
|
||||
- `refactor` - Code refactoring and cleanup
|
||||
- `discovery` - Learnings about the codebase
|
||||
- `change` - General changes and modifications
|
||||
|
||||
**Indexes**:
|
||||
- `idx_observations_session` on `session_id`
|
||||
- `idx_observations_sdk_session` on `sdk_session_id`
|
||||
- `idx_observations_project` on `project`
|
||||
- `idx_observations_tool_name` on `tool_name`
|
||||
- `idx_observations_created_at` on `created_at_epoch DESC`
|
||||
- `idx_observations_type` on `type`
|
||||
|
||||
### 3. session_summaries
|
||||
|
||||
AI-generated session summaries (multiple per session).
|
||||
|
||||
```sql
|
||||
CREATE TABLE session_summaries (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sdk_session_id TEXT NOT NULL,
|
||||
claude_session_id TEXT,
|
||||
project TEXT NOT NULL,
|
||||
prompt_number INTEGER,
|
||||
|
||||
-- Summary fields
|
||||
request TEXT,
|
||||
investigated TEXT,
|
||||
learned TEXT,
|
||||
completed TEXT,
|
||||
next_steps TEXT,
|
||||
notes TEXT,
|
||||
|
||||
created_at TEXT NOT NULL,
|
||||
created_at_epoch INTEGER NOT NULL,
|
||||
|
||||
FOREIGN KEY (sdk_session_id) REFERENCES sdk_sessions(sdk_session_id)
|
||||
);
|
||||
```
|
||||
|
||||
**Indexes**:
|
||||
- `idx_session_summaries_sdk_session` on `sdk_session_id`
|
||||
- `idx_session_summaries_project` on `project`
|
||||
- `idx_session_summaries_created_at` on `created_at_epoch DESC`
|
||||
|
||||
### 4. user_prompts
|
||||
|
||||
Raw user prompts with FTS5 search (as of v4.2.0).
|
||||
|
||||
```sql
|
||||
CREATE TABLE user_prompts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sdk_session_id TEXT NOT NULL,
|
||||
claude_session_id TEXT,
|
||||
project TEXT NOT NULL,
|
||||
prompt_number INTEGER,
|
||||
prompt_text TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
created_at_epoch INTEGER NOT NULL,
|
||||
|
||||
FOREIGN KEY (sdk_session_id) REFERENCES sdk_sessions(sdk_session_id)
|
||||
);
|
||||
```
|
||||
|
||||
**Indexes**:
|
||||
- `idx_user_prompts_sdk_session` on `sdk_session_id`
|
||||
- `idx_user_prompts_project` on `project`
|
||||
- `idx_user_prompts_created_at` on `created_at_epoch DESC`
|
||||
|
||||
### Legacy Tables
|
||||
|
||||
- **sessions**: Legacy session tracking (v3.x)
|
||||
- **memories**: Legacy compressed memory chunks (v3.x)
|
||||
- **overviews**: Legacy session summaries (v3.x)
|
||||
|
||||
## FTS5 Full-Text Search
|
||||
|
||||
SQLite FTS5 (Full-Text Search) virtual tables enable fast full-text search across observations, summaries, and user prompts.
|
||||
|
||||
### FTS5 Virtual Tables
|
||||
|
||||
#### observations_fts
|
||||
|
||||
```sql
|
||||
CREATE VIRTUAL TABLE observations_fts USING fts5(
|
||||
title,
|
||||
subtitle,
|
||||
narrative,
|
||||
text,
|
||||
facts,
|
||||
concepts,
|
||||
content='observations',
|
||||
content_rowid='id'
|
||||
);
|
||||
```
|
||||
|
||||
#### session_summaries_fts
|
||||
|
||||
```sql
|
||||
CREATE VIRTUAL TABLE session_summaries_fts USING fts5(
|
||||
request,
|
||||
investigated,
|
||||
learned,
|
||||
completed,
|
||||
next_steps,
|
||||
notes,
|
||||
content='session_summaries',
|
||||
content_rowid='id'
|
||||
);
|
||||
```
|
||||
|
||||
#### user_prompts_fts
|
||||
|
||||
```sql
|
||||
CREATE VIRTUAL TABLE user_prompts_fts USING fts5(
|
||||
prompt_text,
|
||||
content='user_prompts',
|
||||
content_rowid='id'
|
||||
);
|
||||
```
|
||||
|
||||
### Automatic Synchronization
|
||||
|
||||
FTS5 tables stay in sync via triggers:
|
||||
|
||||
```sql
|
||||
-- Insert trigger example
|
||||
CREATE TRIGGER observations_ai AFTER INSERT ON observations BEGIN
|
||||
INSERT INTO observations_fts(rowid, title, subtitle, narrative, text, facts, concepts)
|
||||
VALUES (new.id, new.title, new.subtitle, new.narrative, new.text, new.facts, new.concepts);
|
||||
END;
|
||||
|
||||
-- Update trigger example
|
||||
CREATE TRIGGER observations_au AFTER UPDATE ON observations BEGIN
|
||||
INSERT INTO observations_fts(observations_fts, rowid, title, subtitle, narrative, text, facts, concepts)
|
||||
VALUES('delete', old.id, old.title, old.subtitle, old.narrative, old.text, old.facts, old.concepts);
|
||||
INSERT INTO observations_fts(rowid, title, subtitle, narrative, text, facts, concepts)
|
||||
VALUES (new.id, new.title, new.subtitle, new.narrative, new.text, new.facts, new.concepts);
|
||||
END;
|
||||
|
||||
-- Delete trigger example
|
||||
CREATE TRIGGER observations_ad AFTER DELETE ON observations BEGIN
|
||||
INSERT INTO observations_fts(observations_fts, rowid, title, subtitle, narrative, text, facts, concepts)
|
||||
VALUES('delete', old.id, old.title, old.subtitle, old.narrative, old.text, old.facts, old.concepts);
|
||||
END;
|
||||
```
|
||||
|
||||
### FTS5 Query Syntax
|
||||
|
||||
FTS5 supports rich query syntax:
|
||||
|
||||
- **Simple**: `"error handling"`
|
||||
- **AND**: `"error" AND "handling"`
|
||||
- **OR**: `"bug" OR "fix"`
|
||||
- **NOT**: `"bug" NOT "feature"`
|
||||
- **Phrase**: `"'exact phrase'"`
|
||||
- **Column**: `title:"authentication"`
|
||||
|
||||
### Security
|
||||
|
||||
As of v4.2.3, all FTS5 queries are properly escaped to prevent SQL injection:
|
||||
- Double quotes are escaped: `query.replace(/"/g, '""')`
|
||||
- Comprehensive test suite with 332 injection attack tests
|
||||
|
||||
## Database Classes
|
||||
|
||||
### SessionStore
|
||||
|
||||
CRUD operations for sessions, observations, summaries, and user prompts.
|
||||
|
||||
**Location**: `src/services/sqlite/SessionStore.ts`
|
||||
|
||||
**Methods**:
|
||||
- `createSession()`
|
||||
- `getSession()`
|
||||
- `updateSession()`
|
||||
- `createObservation()`
|
||||
- `getObservations()`
|
||||
- `createSummary()`
|
||||
- `getSummaries()`
|
||||
- `createUserPrompt()`
|
||||
|
||||
### SessionSearch
|
||||
|
||||
FTS5 full-text search with 8 specialized search methods.
|
||||
|
||||
**Location**: `src/services/sqlite/SessionSearch.ts`
|
||||
|
||||
**Methods**:
|
||||
- `searchObservations()` - Full-text search across observations
|
||||
- `searchSessions()` - Full-text search across summaries
|
||||
- `searchUserPrompts()` - Full-text search across user prompts
|
||||
- `findByConcept()` - Find by concept tags
|
||||
- `findByFile()` - Find by file references
|
||||
- `findByType()` - Find by observation type
|
||||
- `getRecentContext()` - Get recent session context
|
||||
- `advancedSearch()` - Combined filters
|
||||
|
||||
## Migrations
|
||||
|
||||
Database schema is managed via migrations in `src/services/sqlite/migrations.ts`.
|
||||
|
||||
**Migration History**:
|
||||
- Migration 001: Initial schema (sessions, memories, overviews, diagnostics, transcript_events)
|
||||
- Migration 002: Hierarchical memory fields (title, subtitle, facts, concepts, files_touched)
|
||||
- Migration 003: SDK sessions and observations
|
||||
- Migration 004: Session summaries
|
||||
- Migration 005: Multi-prompt sessions (prompt_counter, prompt_number)
|
||||
- Migration 006: FTS5 virtual tables and triggers
|
||||
- Migration 007-010: Various improvements and user prompts table
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- **Indexes**: All foreign keys and frequently queried columns are indexed
|
||||
- **FTS5**: Full-text search is significantly faster than LIKE queries
|
||||
- **Triggers**: Automatic synchronization has minimal overhead
|
||||
- **Connection Pooling**: better-sqlite3 reuses connections efficiently
|
||||
- **Synchronous API**: better-sqlite3 uses synchronous API for better performance
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
See [Troubleshooting - Database Issues](../troubleshooting.md#database-issues) for common problems and solutions.
|
||||
@@ -0,0 +1,959 @@
|
||||
---
|
||||
title: "Hook Lifecycle"
|
||||
description: "Complete guide to the 5-stage memory agent lifecycle for platform implementers"
|
||||
---
|
||||
|
||||
# Hook Lifecycle
|
||||
|
||||
Claude-Mem implements a **5-stage hook system** that captures development work across Claude Code sessions. This document provides a complete technical reference for developers implementing this pattern on other platforms.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### System Architecture
|
||||
|
||||
This two-process architecture works in both Claude Code and VS Code:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph EXT["Extension Process (runs in IDE)"]
|
||||
direction TB
|
||||
ACT[Extension Activation]
|
||||
HOOKS[Hook Event Handlers]
|
||||
ACT --> HOOKS
|
||||
|
||||
subgraph HOOK_HANDLERS["5 Lifecycle Hooks"]
|
||||
H1[SessionStart<br/>activate function]
|
||||
H2[UserPromptSubmit<br/>command handler]
|
||||
H3[PostToolUse<br/>middleware]
|
||||
H4[Stop<br/>idle timeout]
|
||||
H5[SessionEnd<br/>deactivate function]
|
||||
end
|
||||
|
||||
HOOKS --> HOOK_HANDLERS
|
||||
end
|
||||
|
||||
HOOK_HANDLERS -->|"HTTP<br/>(fire-and-forget<br/>2s timeout)"| HTTP[Worker HTTP API<br/>Port 37777]
|
||||
|
||||
subgraph WORKER["Worker Process (separate Node.js)"]
|
||||
direction TB
|
||||
HTTP --> API[Express Server]
|
||||
API --> SESS[Session Manager]
|
||||
API --> AGENT[SDK Agent]
|
||||
API --> DB[Database Manager]
|
||||
|
||||
AGENT -->|Event-Driven| CLAUDE[Claude Agent SDK]
|
||||
CLAUDE --> SQLITE[(SQLite + FTS5)]
|
||||
CLAUDE --> CHROMA[(Chroma Vectors)]
|
||||
end
|
||||
|
||||
style EXT fill:#e1f5ff
|
||||
style WORKER fill:#fff4e1
|
||||
style HOOK_HANDLERS fill:#f0f0f0
|
||||
```
|
||||
|
||||
**Key Principles:**
|
||||
- Extension process never blocks (fire-and-forget HTTP)
|
||||
- Worker processes observations asynchronously
|
||||
- Session state persists across IDE restarts
|
||||
|
||||
### VS Code Extension API Integration Points
|
||||
|
||||
For developers porting to VS Code, here's where to hook into the VS Code Extension API:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph VSCODE["VS Code Extension API"]
|
||||
direction TB
|
||||
A["activate(context)"]
|
||||
B["commands.registerCommand()"]
|
||||
C["chat.createChatParticipant()"]
|
||||
D["workspace.onDidSaveTextDocument()"]
|
||||
E["window.onDidChangeActiveTextEditor()"]
|
||||
F["deactivate()"]
|
||||
end
|
||||
|
||||
subgraph HOOKS["Hook Equivalents"]
|
||||
direction TB
|
||||
G[SessionStart]
|
||||
H[UserPromptSubmit]
|
||||
I[PostToolUse]
|
||||
J[Stop/Summary]
|
||||
K[SessionEnd]
|
||||
end
|
||||
|
||||
subgraph WORKER_API["Worker HTTP Endpoints"]
|
||||
direction TB
|
||||
L[GET /api/context/inject]
|
||||
M[POST /sessions/init]
|
||||
N[POST /sessions/observations]
|
||||
O[POST /sessions/summarize]
|
||||
P[POST /sessions/complete]
|
||||
end
|
||||
|
||||
A --> G
|
||||
B --> H
|
||||
C --> H
|
||||
D --> I
|
||||
E --> I
|
||||
F --> K
|
||||
|
||||
G --> L
|
||||
H --> M
|
||||
I --> N
|
||||
J --> O
|
||||
K --> P
|
||||
|
||||
style VSCODE fill:#007acc,color:#fff
|
||||
style HOOKS fill:#f0f0f0
|
||||
style WORKER_API fill:#4caf50,color:#fff
|
||||
```
|
||||
|
||||
**Implementation Examples:**
|
||||
|
||||
```typescript
|
||||
// VS Code Extension - SessionStart Hook
|
||||
export async function activate(context: vscode.ExtensionContext) {
|
||||
const sessionId = generateSessionId()
|
||||
const project = workspace.name || 'default'
|
||||
|
||||
// Fetch context from worker
|
||||
const response = await fetch(`http://localhost:37777/api/context/inject?project=${project}`)
|
||||
const context = await response.text()
|
||||
|
||||
// Inject into chat or UI panel
|
||||
injectContextToChat(context)
|
||||
}
|
||||
|
||||
// VS Code Extension - UserPromptSubmit Hook
|
||||
const command = vscode.commands.registerCommand('extension.command', async (prompt) => {
|
||||
await fetch('http://localhost:37777/sessions/init', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ sessionId, project, userPrompt: prompt })
|
||||
})
|
||||
})
|
||||
|
||||
// VS Code Extension - PostToolUse Hook (middleware pattern)
|
||||
workspace.onDidSaveTextDocument(async (document) => {
|
||||
await fetch('http://localhost:37777/api/sessions/observations', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
claudeSessionId: sessionId,
|
||||
tool_name: 'FileSave',
|
||||
tool_input: { path: document.uri.path },
|
||||
tool_response: 'File saved successfully'
|
||||
})
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Async Processing Pipeline
|
||||
|
||||
How observations flow from extension to database without blocking the IDE:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
A["Extension: Tool Use Event"] --> B{"Skip List?<br/>(TodoWrite, AskUserQuestion, etc.)"}
|
||||
B -->|"Skip"| X["Discard"]
|
||||
B -->|"Keep"| C["Strip Privacy Tags<br/><private>...</private>"]
|
||||
C --> D["HTTP POST to Worker<br/>Port 37777"]
|
||||
D --> E["2s timeout<br/>fire-and-forget"]
|
||||
E --> F["Extension continues<br/>(non-blocking)"]
|
||||
|
||||
D -.Async Path.-> G["Worker: Queue Observation"]
|
||||
G --> H["SDK Agent picks up<br/>(event-driven)"]
|
||||
H --> I["Call Claude API<br/>(compress observation)"]
|
||||
I --> J["Parse XML response"]
|
||||
J --> K["Save to SQLite<br/>(sdk_sessions table)"]
|
||||
K --> L["Sync to Chroma<br/>(vector embeddings)"]
|
||||
|
||||
style F fill:#90EE90,stroke:#2d6b2d,stroke-width:3px
|
||||
style L fill:#87CEEB,stroke:#2d5f8d,stroke-width:3px
|
||||
style E fill:#ffeb3b,stroke:#c6a700,stroke-width:2px
|
||||
```
|
||||
|
||||
**Critical Pattern:** The extension's HTTP call has a 2-second timeout and doesn't wait for AI processing. The worker handles compression asynchronously using an event-driven queue.
|
||||
|
||||
## The 5 Lifecycle Stages
|
||||
|
||||
| Stage | Hook | Trigger | Purpose |
|
||||
|-------|------|---------|---------|
|
||||
| **1. SessionStart** | `context-hook.js` + `user-message-hook.js` | User opens Claude Code | Inject prior context, show UI messages |
|
||||
| **2. UserPromptSubmit** | `new-hook.js` | User submits a prompt | Create/get session, save prompt, init worker |
|
||||
| **3. PostToolUse** | `save-hook.js` | Claude uses any tool | Queue observation for AI compression |
|
||||
| **4. Stop** | `summary-hook.js` | User stops asking questions | Generate session summary |
|
||||
| **5. SessionEnd** | `cleanup-hook.js` | Session closes | Mark session completed |
|
||||
|
||||
## Hook Configuration
|
||||
|
||||
Hooks are configured in `plugin/hooks/hooks.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [{
|
||||
"matcher": "startup|clear|compact",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/context-hook.js",
|
||||
"timeout": 300
|
||||
}, {
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/user-message-hook.js",
|
||||
"timeout": 10
|
||||
}]
|
||||
}],
|
||||
"UserPromptSubmit": [{
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/new-hook.js",
|
||||
"timeout": 120
|
||||
}]
|
||||
}],
|
||||
"PostToolUse": [{
|
||||
"matcher": "*",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/save-hook.js",
|
||||
"timeout": 120
|
||||
}]
|
||||
}],
|
||||
"Stop": [{
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/summary-hook.js",
|
||||
"timeout": 120
|
||||
}]
|
||||
}],
|
||||
"SessionEnd": [{
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/cleanup-hook.js",
|
||||
"timeout": 120
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Stage 1: SessionStart
|
||||
|
||||
**Timing**: When user opens Claude Code or resumes session
|
||||
|
||||
**Hooks Triggered** (in order):
|
||||
1. `context-hook.js` - Fetches and injects prior session context
|
||||
2. `user-message-hook.js` - Displays context info to user via stderr
|
||||
|
||||
### Sequence Diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant IDE as IDE/Extension
|
||||
participant ContextHook as context-hook.js
|
||||
participant Worker as Worker Service
|
||||
participant DB as SQLite Database
|
||||
|
||||
User->>IDE: Opens workspace / resumes session
|
||||
IDE->>ContextHook: Trigger SessionStart hook
|
||||
ContextHook->>ContextHook: Generate/reuse session_id
|
||||
ContextHook->>Worker: Health check (max 10s retry)
|
||||
|
||||
alt Worker Ready
|
||||
ContextHook->>Worker: GET /api/context/inject?project=X
|
||||
Worker->>DB: SELECT * FROM observations<br/>WHERE project=X<br/>ORDER BY created_at DESC<br/>LIMIT 50
|
||||
DB-->>Worker: Last 50 observations
|
||||
Worker-->>ContextHook: Context markdown
|
||||
ContextHook-->>IDE: hookSpecificOutput.additionalContext
|
||||
IDE->>IDE: Inject context to Claude's prompt
|
||||
IDE-->>User: Session ready with context
|
||||
else Worker Not Ready
|
||||
ContextHook-->>IDE: Empty context (graceful degradation)
|
||||
IDE-->>User: Session ready without context
|
||||
end
|
||||
|
||||
Note over User,DB: Total time: <300ms (with health check)
|
||||
```
|
||||
|
||||
### Context Hook (`context-hook.js`)
|
||||
|
||||
**Purpose**: Inject context from previous sessions into Claude's initial context.
|
||||
|
||||
**Input** (via stdin):
|
||||
```json
|
||||
{
|
||||
"session_id": "claude-session-123",
|
||||
"cwd": "/path/to/project",
|
||||
"source": "startup"
|
||||
}
|
||||
```
|
||||
|
||||
**Processing**:
|
||||
1. Wait for worker to be available (health check, max 10 seconds)
|
||||
2. Call: `GET http://127.0.0.1:37777/api/context/inject?project={project}`
|
||||
3. Return formatted context as `additionalContext` in `hookSpecificOutput`
|
||||
|
||||
**Output** (via stdout):
|
||||
```json
|
||||
{
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "SessionStart",
|
||||
"additionalContext": "<<formatted context markdown>>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation**: `src/hooks/context-hook.ts`
|
||||
|
||||
### User Message Hook (`user-message-hook.js`)
|
||||
|
||||
**Purpose**: Display helpful user messages during first-time setup or when viewing context.
|
||||
|
||||
**Behavior**:
|
||||
- Shows first-time setup message when `node_modules` is missing
|
||||
- Displays formatted context information with colors
|
||||
- Provides tips for using claude-mem effectively
|
||||
- Shows link to viewer UI (`http://localhost:37777`)
|
||||
- Uses stderr as communication channel (only output available in Claude Code UI)
|
||||
|
||||
**Implementation**: `src/hooks/user-message-hook.ts`
|
||||
|
||||
---
|
||||
|
||||
## Stage 2: UserPromptSubmit
|
||||
|
||||
**Timing**: When user submits any prompt in a session
|
||||
|
||||
**Hook**: `new-hook.js`
|
||||
|
||||
### Sequence Diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant IDE as IDE/Extension
|
||||
participant NewHook as new-hook.js
|
||||
participant DB as Direct SQLite Access
|
||||
participant Worker as Worker Service
|
||||
|
||||
User->>IDE: Submits prompt: "Add login feature"
|
||||
IDE->>NewHook: Trigger UserPromptSubmit<br/>{ session_id, cwd, prompt }
|
||||
|
||||
NewHook->>NewHook: Extract project = basename(cwd)
|
||||
NewHook->>NewHook: Strip privacy tags<br/><private>...</private>
|
||||
|
||||
alt Prompt fully private (empty after stripping)
|
||||
NewHook-->>IDE: Skip (don't save)
|
||||
else Prompt has content
|
||||
NewHook->>DB: INSERT OR IGNORE INTO sdk_sessions<br/>(claude_session_id, project, first_user_prompt)
|
||||
DB-->>NewHook: sessionDbId (new or existing)
|
||||
|
||||
NewHook->>DB: UPDATE sdk_sessions<br/>SET prompt_counter = prompt_counter + 1<br/>WHERE id = sessionDbId
|
||||
DB-->>NewHook: promptNumber (e.g., 1 for first, 2 for continuation)
|
||||
|
||||
NewHook->>DB: INSERT INTO user_prompts<br/>(session_id, prompt_number, prompt)
|
||||
|
||||
NewHook->>Worker: POST /sessions/{sessionDbId}/init<br/>{ project, userPrompt, promptNumber }<br/>(fire-and-forget, 2s timeout)
|
||||
Worker-->>NewHook: 200 OK (or timeout)
|
||||
|
||||
NewHook-->>IDE: { continue: true, suppressOutput: true }
|
||||
IDE-->>User: Prompt accepted
|
||||
end
|
||||
|
||||
Note over NewHook,DB: Idempotent: Same session_id → same sessionDbId
|
||||
```
|
||||
|
||||
**Key Pattern:** The `INSERT OR IGNORE` ensures the same `session_id` always maps to the same `sessionDbId`, enabling conversation continuations.
|
||||
|
||||
**Input** (via stdin):
|
||||
```json
|
||||
{
|
||||
"session_id": "claude-session-123",
|
||||
"cwd": "/path/to/project",
|
||||
"prompt": "User's actual prompt text"
|
||||
}
|
||||
```
|
||||
|
||||
**Processing Steps**:
|
||||
|
||||
```typescript
|
||||
// 1. Extract project name from working directory
|
||||
project = path.basename(cwd)
|
||||
|
||||
// 2. Create or get database session (IDEMPOTENT)
|
||||
sessionDbId = db.createSDKSession(session_id, project, prompt)
|
||||
// INSERT OR IGNORE: Creates new row if first prompt, returns existing if continuation
|
||||
|
||||
// 3. Increment prompt counter
|
||||
promptNumber = db.incrementPromptCounter(sessionDbId)
|
||||
// Returns 1 for first prompt, 2 for continuation, etc.
|
||||
|
||||
// 4. Strip privacy tags
|
||||
cleanedPrompt = stripMemoryTagsFromPrompt(prompt)
|
||||
// Removes <private>...</private> and <claude-mem-context>...</claude-mem-context>
|
||||
|
||||
// 5. Skip if fully private
|
||||
if (!cleanedPrompt || cleanedPrompt.trim() === '') {
|
||||
return // Don't save, don't call worker
|
||||
}
|
||||
|
||||
// 6. Save user prompt to database
|
||||
db.saveUserPrompt(session_id, promptNumber, cleanedPrompt)
|
||||
|
||||
// 7. Initialize session via worker HTTP
|
||||
POST http://127.0.0.1:37777/sessions/{sessionDbId}/init
|
||||
Body: { project, userPrompt, promptNumber }
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```json
|
||||
{ "continue": true, "suppressOutput": true }
|
||||
```
|
||||
|
||||
**Implementation**: `src/hooks/new-hook.ts`
|
||||
|
||||
<Note>
|
||||
The same `session_id` flows through ALL hooks in a conversation. The `createSDKSession` call is idempotent - it returns the existing session for continuation prompts.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Stage 3: PostToolUse
|
||||
|
||||
**Timing**: After Claude uses any tool (Read, Bash, Grep, Write, etc.)
|
||||
|
||||
**Hook**: `save-hook.js`
|
||||
|
||||
### Sequence Diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Claude as Claude AI
|
||||
participant IDE as IDE/Extension
|
||||
participant SaveHook as save-hook.js
|
||||
participant Worker as Worker Service
|
||||
participant Agent as SDK Agent
|
||||
participant DB as SQLite + Chroma
|
||||
|
||||
Claude->>IDE: Uses tool: Read("/src/auth.ts")
|
||||
IDE->>SaveHook: PostToolUse hook triggered<br/>{ session_id, tool_name, tool_input, tool_response }
|
||||
|
||||
SaveHook->>SaveHook: Check skip list<br/>(TodoWrite, AskUserQuestion, etc.)
|
||||
|
||||
alt Tool in skip list
|
||||
SaveHook-->>IDE: Discard (low-value tool)
|
||||
else Tool allowed
|
||||
SaveHook->>SaveHook: Strip privacy tags from input/response
|
||||
|
||||
SaveHook->>SaveHook: Ensure worker running<br/>(PM2 health check)
|
||||
|
||||
SaveHook->>Worker: POST /api/sessions/observations<br/>{ claudeSessionId, tool_name, tool_input, tool_response, cwd }<br/>(fire-and-forget, 2s timeout)
|
||||
|
||||
SaveHook-->>IDE: { continue: true, suppressOutput: true }
|
||||
IDE-->>Claude: Tool execution complete
|
||||
|
||||
Note over Worker,DB: Async path (doesn't block IDE)
|
||||
|
||||
Worker->>Worker: createSDKSession(claudeSessionId)<br/>→ returns sessionDbId
|
||||
Worker->>Worker: Check if prompt was private<br/>(skip if fully private)
|
||||
Worker->>Agent: Queue observation for processing
|
||||
Agent->>Agent: Call Claude SDK to compress<br/>observation into structured format
|
||||
Agent->>DB: Save compressed observation<br/>to sdk_sessions table
|
||||
Agent->>DB: Sync to Chroma vector DB
|
||||
end
|
||||
|
||||
Note over SaveHook,DB: Total sync time: ~2ms<br/>AI processing: 1-3s (async)
|
||||
```
|
||||
|
||||
**Key Pattern:** The hook returns immediately after HTTP POST. AI compression happens asynchronously in the worker without blocking Claude's tool execution.
|
||||
|
||||
**Input** (via stdin):
|
||||
```json
|
||||
{
|
||||
"session_id": "claude-session-123",
|
||||
"cwd": "/path/to/project",
|
||||
"tool_name": "Read",
|
||||
"tool_input": { "file_path": "/src/index.ts" },
|
||||
"tool_response": "file contents..."
|
||||
}
|
||||
```
|
||||
|
||||
**Processing Steps**:
|
||||
|
||||
```typescript
|
||||
// 1. Check blocklist - skip low-value tools
|
||||
const SKIP_TOOLS = {
|
||||
'ListMcpResourcesTool', // MCP infrastructure noise
|
||||
'SlashCommand', // Command invocation
|
||||
'Skill', // Skill invocation
|
||||
'TodoWrite', // Task management meta-tool
|
||||
'AskUserQuestion' // User interaction
|
||||
}
|
||||
|
||||
if (SKIP_TOOLS[tool_name]) return
|
||||
|
||||
// 2. Ensure worker is running
|
||||
await ensureWorkerRunning()
|
||||
|
||||
// 3. Send to worker (fire-and-forget HTTP)
|
||||
POST http://127.0.0.1:37777/api/sessions/observations
|
||||
Body: {
|
||||
claudeSessionId: session_id,
|
||||
tool_name,
|
||||
tool_input,
|
||||
tool_response,
|
||||
cwd
|
||||
}
|
||||
Timeout: 2000ms
|
||||
```
|
||||
|
||||
**Worker Processing**:
|
||||
1. Looks up or creates session: `createSDKSession(claudeSessionId, '', '')`
|
||||
2. Gets prompt counter
|
||||
3. Checks privacy (skips if user prompt was entirely private)
|
||||
4. Strips memory tags from `tool_input` and `tool_response`
|
||||
5. Queues observation for SDK agent processing
|
||||
6. SDK agent calls Claude to compress into structured observation
|
||||
7. Stores observation in database and syncs to Chroma
|
||||
|
||||
**Output**:
|
||||
```json
|
||||
{ "continue": true, "suppressOutput": true }
|
||||
```
|
||||
|
||||
**Implementation**: `src/hooks/save-hook.ts`
|
||||
|
||||
---
|
||||
|
||||
## Stage 4: Stop
|
||||
|
||||
**Timing**: When user stops or pauses asking questions
|
||||
|
||||
**Hook**: `summary-hook.js`
|
||||
|
||||
### Sequence Diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant IDE as IDE/Extension
|
||||
participant SummaryHook as summary-hook.js
|
||||
participant Worker as Worker Service
|
||||
participant Agent as SDK Agent
|
||||
participant DB as SQLite Database
|
||||
|
||||
User->>IDE: Stops asking questions<br/>(pause, idle, or explicit stop)
|
||||
IDE->>SummaryHook: Stop hook triggered<br/>{ session_id, cwd, transcript_path }
|
||||
|
||||
SummaryHook->>SummaryHook: Read transcript JSONL file
|
||||
SummaryHook->>SummaryHook: Extract last user message<br/>(type: "user")
|
||||
SummaryHook->>SummaryHook: Extract last assistant message<br/>(type: "assistant", filter <system-reminder>)
|
||||
|
||||
SummaryHook->>Worker: POST /api/sessions/summarize<br/>{ claudeSessionId, last_user_message, last_assistant_message }<br/>(fire-and-forget, 2s timeout)
|
||||
|
||||
SummaryHook->>Worker: POST /api/processing<br/>{ isProcessing: false }<br/>(stop spinner)
|
||||
|
||||
SummaryHook-->>IDE: { continue: true, suppressOutput: true }
|
||||
IDE-->>User: Session paused/stopped
|
||||
|
||||
Note over Worker,DB: Async path
|
||||
|
||||
Worker->>Worker: Lookup sessionDbId from claudeSessionId
|
||||
Worker->>Agent: Queue summarization request
|
||||
Agent->>Agent: Call Claude SDK with prompt:<br/>"Summarize: request, investigated, learned, completed, next_steps"
|
||||
Agent->>Agent: Parse XML response
|
||||
Agent->>DB: INSERT INTO session_summaries<br/>{ session_id, request, investigated, learned, completed, next_steps }
|
||||
Agent->>DB: Sync to Chroma (for semantic search)
|
||||
|
||||
Note over SummaryHook,DB: Total sync time: ~2ms<br/>AI summarization: 2-5s (async)
|
||||
```
|
||||
|
||||
**Key Pattern:** The summary is generated asynchronously and doesn't block the user from resuming work or closing the session.
|
||||
|
||||
**Input** (via stdin):
|
||||
```json
|
||||
{
|
||||
"session_id": "claude-session-123",
|
||||
"cwd": "/path/to/project",
|
||||
"transcript_path": "/path/to/transcript.jsonl"
|
||||
}
|
||||
```
|
||||
|
||||
**Processing Steps**:
|
||||
|
||||
```typescript
|
||||
// 1. Extract last messages from transcript JSONL
|
||||
const lines = fs.readFileSync(transcript_path, 'utf-8').split('\n')
|
||||
// Find last user message (type: "user")
|
||||
// Find last assistant message (type: "assistant", filter <system-reminder> tags)
|
||||
|
||||
// 2. Ensure worker is running
|
||||
await ensureWorkerRunning()
|
||||
|
||||
// 3. Send summarization request (fire-and-forget HTTP)
|
||||
POST http://127.0.0.1:37777/api/sessions/summarize
|
||||
Body: {
|
||||
claudeSessionId: session_id,
|
||||
last_user_message: string,
|
||||
last_assistant_message: string
|
||||
}
|
||||
Timeout: 2000ms
|
||||
|
||||
// 4. Stop processing spinner
|
||||
POST http://127.0.0.1:37777/api/processing
|
||||
Body: { isProcessing: false }
|
||||
```
|
||||
|
||||
**Worker Processing**:
|
||||
1. Queues summarization for SDK agent
|
||||
2. Agent calls Claude to generate structured summary
|
||||
3. Summary stored in database with fields: `request`, `investigated`, `learned`, `completed`, `next_steps`
|
||||
|
||||
**Output**:
|
||||
```json
|
||||
{ "continue": true, "suppressOutput": true }
|
||||
```
|
||||
|
||||
**Implementation**: `src/hooks/summary-hook.ts`
|
||||
|
||||
---
|
||||
|
||||
## Stage 5: SessionEnd
|
||||
|
||||
**Timing**: When Claude Code session closes (exit, clear, logout, etc.)
|
||||
|
||||
**Hook**: `cleanup-hook.js`
|
||||
|
||||
### Sequence Diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant IDE as IDE/Extension
|
||||
participant CleanupHook as cleanup-hook.js
|
||||
participant Worker as Worker Service
|
||||
participant DB as SQLite Database
|
||||
participant SSE as SSE Clients (Viewer UI)
|
||||
|
||||
User->>IDE: Closes session<br/>(exit, clear, logout)
|
||||
IDE->>CleanupHook: SessionEnd hook triggered<br/>{ session_id, cwd, transcript_path, reason }
|
||||
|
||||
CleanupHook->>Worker: POST /api/sessions/complete<br/>{ claudeSessionId, reason }<br/>(fire-and-forget, 2s timeout)
|
||||
|
||||
CleanupHook-->>IDE: { continue: true, suppressOutput: true }
|
||||
IDE-->>User: Session closed
|
||||
|
||||
Note over Worker,SSE: Async path
|
||||
|
||||
Worker->>Worker: Lookup sessionDbId from claudeSessionId
|
||||
Worker->>DB: UPDATE sdk_sessions<br/>SET status = 'completed', completed_at = NOW()<br/>WHERE claude_session_id = claudeSessionId
|
||||
Worker->>SSE: Broadcast session completion event<br/>(for live viewer UI updates)
|
||||
|
||||
SSE-->>SSE: Update UI to show session as completed
|
||||
|
||||
Note over CleanupHook,SSE: Total sync time: ~2ms
|
||||
```
|
||||
|
||||
**Key Pattern:** Session completion is tracked for analytics and UI updates, but doesn't prevent the user from closing the IDE.
|
||||
|
||||
**Input** (via stdin):
|
||||
```json
|
||||
{
|
||||
"session_id": "claude-session-123",
|
||||
"cwd": "/path/to/project",
|
||||
"transcript_path": "/path/to/transcript.jsonl",
|
||||
"reason": "exit"
|
||||
}
|
||||
```
|
||||
|
||||
**Processing Steps**:
|
||||
|
||||
```typescript
|
||||
// Send session complete (fire-and-forget HTTP)
|
||||
POST http://127.0.0.1:37777/api/sessions/complete
|
||||
Body: {
|
||||
claudeSessionId: session_id,
|
||||
reason: string // 'exit' | 'clear' | 'logout' | 'prompt_input_exit' | 'other'
|
||||
}
|
||||
Timeout: 2000ms
|
||||
```
|
||||
|
||||
**Worker Processing**:
|
||||
1. Finds session by `claudeSessionId`
|
||||
2. Marks session as 'completed' in database
|
||||
3. Broadcasts session completion event to SSE clients
|
||||
|
||||
**Output**:
|
||||
```json
|
||||
{ "continue": true, "suppressOutput": true }
|
||||
```
|
||||
|
||||
**Implementation**: `src/hooks/cleanup-hook.ts`
|
||||
|
||||
---
|
||||
|
||||
## Session State Machine
|
||||
|
||||
Understanding session lifecycle and state transitions:
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Initialized: SessionStart hook<br/>(generate session_id)
|
||||
|
||||
Initialized --> Active: UserPromptSubmit<br/>(first prompt)
|
||||
|
||||
Active --> Active: UserPromptSubmit<br/>(continuation prompts)<br/>promptNumber++
|
||||
|
||||
Active --> ObservationQueued: PostToolUse hook<br/>(tool execution captured)
|
||||
|
||||
ObservationQueued --> Active: Observation processed<br/>(async, non-blocking)
|
||||
|
||||
Active --> Summarizing: Stop hook<br/>(user pauses/stops)
|
||||
|
||||
Summarizing --> Active: User resumes<br/>(new prompt submitted)
|
||||
|
||||
Summarizing --> Completed: SessionEnd hook<br/>(session closes)
|
||||
|
||||
Active --> Completed: SessionEnd hook<br/>(session closes)
|
||||
|
||||
Completed --> [*]
|
||||
|
||||
note right of Active
|
||||
session_id: constant (e.g., "claude-session-abc123")
|
||||
sessionDbId: constant (e.g., 42)
|
||||
promptNumber: increments (1, 2, 3, ...)
|
||||
All operations use same sessionDbId
|
||||
end note
|
||||
|
||||
note right of ObservationQueued
|
||||
Fire-and-forget HTTP
|
||||
AI compression happens async
|
||||
IDE never blocks
|
||||
end note
|
||||
```
|
||||
|
||||
**Key Insights:**
|
||||
- `session_id` never changes during a conversation
|
||||
- `sessionDbId` is the database primary key for the session
|
||||
- `promptNumber` increments with each user prompt
|
||||
- State transitions are non-blocking (fire-and-forget pattern)
|
||||
|
||||
---
|
||||
|
||||
## Database Schema
|
||||
|
||||
The session-centric data model that enables cross-session memory:
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
SDK_SESSIONS ||--o{ USER_PROMPTS : "has many"
|
||||
SDK_SESSIONS ||--o{ OBSERVATIONS : "has many"
|
||||
SDK_SESSIONS ||--o{ SESSION_SUMMARIES : "has many"
|
||||
|
||||
SDK_SESSIONS {
|
||||
integer id PK "Auto-increment primary key"
|
||||
text claude_session_id UK "From IDE (e.g., 'claude-session-123')"
|
||||
text project "Project name from cwd basename"
|
||||
text first_user_prompt "Initial prompt that started session"
|
||||
integer prompt_counter "Increments with each UserPromptSubmit"
|
||||
text status "initialized | active | completed"
|
||||
datetime created_at
|
||||
datetime completed_at
|
||||
}
|
||||
|
||||
USER_PROMPTS {
|
||||
integer id PK
|
||||
integer session_id FK "References SDK_SESSIONS.id"
|
||||
integer prompt_number "1, 2, 3, ... matches prompt_counter"
|
||||
text prompt "User's actual prompt (tags stripped)"
|
||||
datetime created_at
|
||||
}
|
||||
|
||||
OBSERVATIONS {
|
||||
integer id PK
|
||||
integer session_id FK "References SDK_SESSIONS.id"
|
||||
integer prompt_number "Which prompt this observation belongs to"
|
||||
text tool_name "Read, Bash, Grep, Write, etc."
|
||||
text tool_input_json "Stripped of privacy tags"
|
||||
text tool_response_text "Stripped of privacy tags"
|
||||
text compressed_observation "AI-generated structured observation"
|
||||
datetime created_at
|
||||
}
|
||||
|
||||
SESSION_SUMMARIES {
|
||||
integer id PK
|
||||
integer session_id FK "References SDK_SESSIONS.id"
|
||||
text request "What user requested"
|
||||
text investigated "What was explored"
|
||||
text learned "What was discovered"
|
||||
text completed "What was accomplished"
|
||||
text next_steps "What remains to be done"
|
||||
datetime created_at
|
||||
}
|
||||
```
|
||||
|
||||
**Idempotency Pattern:**
|
||||
|
||||
```sql
|
||||
-- This ensures same session_id always maps to same sessionDbId
|
||||
INSERT OR IGNORE INTO sdk_sessions (claude_session_id, project, first_user_prompt)
|
||||
VALUES (?, ?, ?)
|
||||
RETURNING id;
|
||||
|
||||
-- If already exists, returns existing row
|
||||
-- If new, creates and returns new row
|
||||
```
|
||||
|
||||
**Foreign Key Cascade:**
|
||||
|
||||
All child tables (user_prompts, observations, session_summaries) use `session_id` foreign key referencing `SDK_SESSIONS.id`. This ensures:
|
||||
- All data for a session is queryable by sessionDbId
|
||||
- Session deletions cascade to child tables
|
||||
- Efficient joins for context injection
|
||||
|
||||
<Warning>
|
||||
Never generate your own session IDs. Always use the `session_id` provided by the IDE - this is the source of truth for linking all data together.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Privacy & Tag Stripping
|
||||
|
||||
### Dual-Tag System
|
||||
|
||||
```typescript
|
||||
// User-Level Privacy Control (manual)
|
||||
<private>sensitive data</private>
|
||||
|
||||
// System-Level Recursion Prevention (auto-injected)
|
||||
<claude-mem-context>...</claude-mem-context>
|
||||
```
|
||||
|
||||
### Processing Pipeline
|
||||
|
||||
**Location**: `src/utils/tag-stripping.ts`
|
||||
|
||||
```typescript
|
||||
// Called by: new-hook.js (user prompts)
|
||||
stripMemoryTagsFromPrompt(prompt: string): string
|
||||
|
||||
// Called by: save-hook.js (tool_input, tool_response)
|
||||
stripMemoryTagsFromJson(jsonString: string): string
|
||||
```
|
||||
|
||||
**Execution Order** (Edge Processing):
|
||||
1. `new-hook.js` strips tags from user prompt before saving
|
||||
2. `save-hook.js` strips tags from tool data before sending to worker
|
||||
3. Worker strips tags again (defense in depth) before storing
|
||||
|
||||
---
|
||||
|
||||
## SDK Agent Processing
|
||||
|
||||
### Query Loop (Event-Driven)
|
||||
|
||||
**Location**: `src/services/worker/SDKAgent.ts`
|
||||
|
||||
```typescript
|
||||
async startSession(session: ActiveSession, worker?: any) {
|
||||
// 1. Create event-driven message generator
|
||||
const messageGenerator = this.createMessageGenerator(session)
|
||||
|
||||
// 2. Run Agent SDK query loop
|
||||
const queryResult = query({
|
||||
prompt: messageGenerator,
|
||||
options: {
|
||||
model: 'claude-haiku-4-5',
|
||||
disallowedTools: ['Bash', 'Read', 'Write', ...], // Observer-only
|
||||
abortController: session.abortController
|
||||
}
|
||||
})
|
||||
|
||||
// 3. Process responses
|
||||
for await (const message of queryResult) {
|
||||
if (message.type === 'assistant') {
|
||||
await this.processSDKResponse(session, text, worker)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Message Types
|
||||
|
||||
The message generator yields three types of prompts:
|
||||
|
||||
1. **Initial Prompt** (prompt #1): Full instructions for starting observation
|
||||
2. **Continuation Prompt** (prompt #2+): Context-only for continuing work
|
||||
3. **Observation Prompts**: Tool use data to compress into observations
|
||||
4. **Summary Prompts**: Session data to summarize
|
||||
|
||||
---
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
For developers implementing this pattern on other platforms:
|
||||
|
||||
### Hook Registration
|
||||
- [ ] Define hook entry points in platform config
|
||||
- [ ] 5 hook types: SessionStart (2 hooks), UserPromptSubmit, PostToolUse, Stop, SessionEnd
|
||||
- [ ] Pass `session_id`, `cwd`, and context-specific data
|
||||
|
||||
### Database Schema
|
||||
- [ ] SQLite with WAL mode
|
||||
- [ ] 4 main tables: `sdk_sessions`, `user_prompts`, `observations`, `session_summaries`
|
||||
- [ ] Indices for common queries
|
||||
|
||||
### Worker Service
|
||||
- [ ] HTTP server on configurable port (default 37777)
|
||||
- [ ] PM2 or equivalent process management
|
||||
- [ ] 3 core services: SessionManager, SDKAgent, DatabaseManager
|
||||
|
||||
### Hook Implementation
|
||||
- [ ] context-hook: `GET /api/context/inject` (with health check)
|
||||
- [ ] new-hook: createSDKSession, saveUserPrompt, `POST /sessions/{id}/init`
|
||||
- [ ] save-hook: Skip low-value tools, `POST /api/sessions/observations`
|
||||
- [ ] summary-hook: Parse transcript, `POST /api/sessions/summarize`
|
||||
- [ ] cleanup-hook: `POST /api/sessions/complete`
|
||||
|
||||
### Privacy & Tags
|
||||
- [ ] Implement `stripMemoryTagsFromPrompt()` and `stripMemoryTagsFromJson()`
|
||||
- [ ] Process tags at hook layer (edge processing)
|
||||
- [ ] Max tag count = 100 (ReDoS protection)
|
||||
|
||||
### SDK Integration
|
||||
- [ ] Call Claude Agent SDK to process observations/summaries
|
||||
- [ ] Parse XML responses for structured data
|
||||
- [ ] Store to database + sync to vector DB
|
||||
|
||||
---
|
||||
|
||||
## Key Design Principles
|
||||
|
||||
1. **Session ID is Source of Truth**: Never generate your own session IDs
|
||||
2. **Idempotent Database Operations**: Use `INSERT OR IGNORE` for session creation
|
||||
3. **Edge Processing for Privacy**: Strip tags at hook layer before data reaches worker
|
||||
4. **Fire-and-Forget for Non-Blocking**: HTTP timeouts prevent IDE blocking
|
||||
5. **Event-Driven, Not Polling**: Zero-latency queue notification to SDK agent
|
||||
6. **Everything Saves Always**: No "orphaned" sessions
|
||||
|
||||
---
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
| Problem | Root Cause | Solution |
|
||||
|---------|-----------|----------|
|
||||
| Session ID mismatch | Different `session_id` used in different hooks | Always use ID from hook input |
|
||||
| Duplicate sessions | Creating new session instead of using existing | Use `INSERT OR IGNORE` with `session_id` as key |
|
||||
| Blocking IDE | Waiting for full response | Use fire-and-forget with short timeouts |
|
||||
| Memory tags in DB | Stripping tags in wrong layer | Strip at hook layer, before HTTP send |
|
||||
| Worker not found | Health check too fast | Add retry loop with exponential backoff |
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Worker Service](/architecture/worker-service) - HTTP API and async processing
|
||||
- [Database Schema](/architecture/database) - SQLite tables and FTS5 search
|
||||
- [Privacy Tags](/usage/private-tags) - Using `<private>` tags
|
||||
- [Troubleshooting](/troubleshooting) - Common hook issues
|
||||
@@ -0,0 +1,240 @@
|
||||
---
|
||||
title: "Architecture Overview"
|
||||
description: "System components and data flow in Claude-Mem"
|
||||
---
|
||||
|
||||
# Architecture Overview
|
||||
|
||||
## System Components
|
||||
|
||||
Claude-Mem operates as a Claude Code plugin with five core components:
|
||||
|
||||
1. **Plugin Hooks** - Capture lifecycle events (6 hook files)
|
||||
2. **Smart Install** - Cached dependency checker (pre-hook script, runs before context-hook)
|
||||
3. **Worker Service** - Process observations via Claude Agent SDK + HTTP API (10 search endpoints)
|
||||
4. **Database Layer** - Store sessions and observations (SQLite + FTS5 + ChromaDB)
|
||||
5. **mem-search Skill** - Skill-based search with progressive disclosure (v5.4.0+)
|
||||
6. **Viewer UI** - Web-based real-time memory stream visualization
|
||||
|
||||
## Technology Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|------------------------|-------------------------------------------|
|
||||
| **Language** | TypeScript (ES2022, ESNext modules) |
|
||||
| **Runtime** | Node.js 18+ |
|
||||
| **Database** | SQLite 3 with better-sqlite3 driver |
|
||||
| **Vector Store** | ChromaDB (optional, for semantic search) |
|
||||
| **HTTP Server** | Express.js 4.18 |
|
||||
| **Real-time** | Server-Sent Events (SSE) |
|
||||
| **UI Framework** | React + TypeScript |
|
||||
| **AI SDK** | @anthropic-ai/claude-agent-sdk |
|
||||
| **Build Tool** | esbuild (bundles TypeScript) |
|
||||
| **Process Manager** | PM2 |
|
||||
| **Testing** | Node.js built-in test runner |
|
||||
|
||||
## Data Flow
|
||||
|
||||
### Memory Pipeline
|
||||
```
|
||||
Hook (stdin) → Database → Worker Service → SDK Processor → Database → Next Session Hook
|
||||
```
|
||||
|
||||
1. **Input**: Claude Code sends tool execution data via stdin to hooks
|
||||
2. **Storage**: Hooks write observations to SQLite database
|
||||
3. **Processing**: Worker service reads observations, processes via SDK
|
||||
4. **Output**: Processed summaries written back to database
|
||||
5. **Retrieval**: Next session's context hook reads summaries from database
|
||||
|
||||
### Search Pipeline (v5.4.0+)
|
||||
```
|
||||
User Query → mem-search Skill Invoked → HTTP API → SessionSearch Service → FTS5 Database → Search Results → Claude
|
||||
```
|
||||
|
||||
1. **User Query**: User asks naturally: "What bugs did we fix?"
|
||||
2. **Skill Invoked**: Claude recognizes intent and invokes mem-search skill
|
||||
3. **HTTP API**: Skill uses curl to call HTTP endpoint (e.g., `/api/search/observations`)
|
||||
4. **SessionSearch**: Worker service queries FTS5 virtual tables
|
||||
5. **Format**: Results formatted and returned to skill
|
||||
6. **Return**: Claude presents formatted results to user
|
||||
|
||||
**Token Savings**: ~2,250 tokens per session vs MCP approach through progressive disclosure
|
||||
|
||||
## Session Lifecycle
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 0. Smart Install Pre-Hook Fires │
|
||||
│ Checks dependencies (cached), only runs on version changes │
|
||||
│ Not a lifecycle hook - runs before context-hook starts │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 1. Session Starts → Context Hook Fires │
|
||||
│ Starts PM2 worker if needed, injects context from previous │
|
||||
│ sessions (configurable observation count) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 2. User Types Prompt → UserPromptSubmit Hook Fires │
|
||||
│ Creates session in database, saves raw user prompt for FTS5 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 3. Claude Uses Tools → PostToolUse Hook Fires (100+ times) │
|
||||
│ Captures tool executions, sends to worker for AI compression │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 4. Worker Processes → Claude Agent SDK Analyzes │
|
||||
│ Extracts structured learnings via iterative AI processing │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 5. Claude Stops → Summary Hook Fires │
|
||||
│ Generates final summary with request, completions, learnings │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 6. Session Ends → Cleanup Hook Fires │
|
||||
│ Marks session complete (graceful, not DELETE), ready for │
|
||||
│ next session context. Skips on /clear to preserve ongoing │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
claude-mem/
|
||||
├── src/
|
||||
│ ├── hooks/ # Hook implementations (6 hooks)
|
||||
│ │ ├── context-hook.ts # SessionStart
|
||||
│ │ ├── user-message-hook.ts # UserMessage (for debugging)
|
||||
│ │ ├── new-hook.ts # UserPromptSubmit
|
||||
│ │ ├── save-hook.ts # PostToolUse
|
||||
│ │ ├── summary-hook.ts # Stop
|
||||
│ │ ├── cleanup-hook.ts # SessionEnd
|
||||
│ │ └── hook-response.ts # Hook response utilities
|
||||
│ │
|
||||
│ ├── sdk/ # Claude Agent SDK integration
|
||||
│ │ ├── prompts.ts # XML prompt builders
|
||||
│ │ ├── parser.ts # XML response parser
|
||||
│ │ └── worker.ts # Main SDK agent loop
|
||||
│ │
|
||||
│ ├── services/
|
||||
│ │ ├── worker-service.ts # Express HTTP + SSE service
|
||||
│ │ └── sqlite/ # Database layer
|
||||
│ │ ├── SessionStore.ts # CRUD operations
|
||||
│ │ ├── SessionSearch.ts # FTS5 search service
|
||||
│ │ ├── migrations.ts
|
||||
│ │ └── types.ts
|
||||
│ │
|
||||
│ ├── ui/ # Viewer UI
|
||||
│ │ └── viewer/ # React + TypeScript web interface
|
||||
│ │ ├── components/ # UI components
|
||||
│ │ ├── hooks/ # React hooks
|
||||
│ │ ├── utils/ # Utilities
|
||||
│ │ └── assets/ # Fonts, logos
|
||||
│ │
|
||||
│ ├── shared/ # Shared utilities
|
||||
│ │ ├── config.ts
|
||||
│ │ ├── paths.ts
|
||||
│ │ └── storage.ts
|
||||
│ │
|
||||
│ └── utils/
|
||||
│ ├── logger.ts
|
||||
│ ├── platform.ts
|
||||
│ └── port-allocator.ts
|
||||
│
|
||||
├── scripts/ # Build and utility scripts
|
||||
│ └── smart-install.js # Cached dependency checker (pre-hook)
|
||||
│
|
||||
├── plugin/ # Plugin distribution
|
||||
│ ├── .claude-plugin/
|
||||
│ │ └── plugin.json
|
||||
│ ├── hooks/
|
||||
│ │ └── hooks.json
|
||||
│ ├── scripts/ # Built executables
|
||||
│ │ ├── context-hook.js
|
||||
│ │ ├── user-message-hook.js
|
||||
│ │ ├── new-hook.js
|
||||
│ │ ├── save-hook.js
|
||||
│ │ ├── summary-hook.js
|
||||
│ │ ├── cleanup-hook.js
|
||||
│ │ └── worker-service.cjs # Background worker + HTTP API
|
||||
│ │
|
||||
│ ├── skills/ # Agent skills (v5.4.0+)
|
||||
│ │ ├── mem-search/ # Search skill with progressive disclosure (v5.5.0)
|
||||
│ │ │ ├── SKILL.md # Skill frontmatter (~250 tokens)
|
||||
│ │ │ ├── operations/ # 12 detailed operation docs
|
||||
│ │ │ └── principles/ # 2 principle guides
|
||||
│ │ ├── troubleshoot/ # Troubleshooting skill
|
||||
│ │ │ ├── SKILL.md
|
||||
│ │ │ └── operations/ # 6 operation docs
|
||||
│ │ └── version-bump/ # Version management skill (deprecated)
|
||||
│ │
|
||||
│ └── ui/ # Built viewer UI
|
||||
│ └── viewer.html # Self-contained bundle
|
||||
│
|
||||
├── tests/ # Test suite
|
||||
├── docs/ # Documentation
|
||||
└── ecosystem.config.cjs # PM2 configuration
|
||||
```
|
||||
|
||||
## Component Details
|
||||
|
||||
### 1. Plugin Hooks (6 Hooks)
|
||||
- **context-hook.js** - SessionStart: Starts PM2 worker, injects context
|
||||
- **user-message-hook.js** - UserMessage: Debugging hook
|
||||
- **new-hook.js** - UserPromptSubmit: Creates session, saves prompt
|
||||
- **save-hook.js** - PostToolUse: Captures tool executions
|
||||
- **summary-hook.js** - Stop: Generates session summary
|
||||
- **cleanup-hook.js** - SessionEnd: Marks session complete
|
||||
|
||||
**Note**: smart-install.js is a pre-hook dependency checker (not a lifecycle hook). It's called before context-hook via command chaining in hooks.json and only runs when dependencies need updating.
|
||||
|
||||
See [Plugin Hooks](/architecture/hooks) for detailed hook documentation.
|
||||
|
||||
### 2. Worker Service
|
||||
Express.js HTTP server on port 37777 (configurable) with:
|
||||
- 10 search HTTP API endpoints (v5.4.0+)
|
||||
- 8 viewer UI HTTP/SSE endpoints
|
||||
- Async observation processing via Claude Agent SDK
|
||||
- Real-time updates via Server-Sent Events
|
||||
- Auto-managed by PM2 process manager
|
||||
|
||||
See [Worker Service](/architecture/worker-service) for HTTP API and endpoints.
|
||||
|
||||
### 3. Database Layer
|
||||
SQLite3 with better-sqlite3 driver featuring:
|
||||
- FTS5 virtual tables for full-text search
|
||||
- SessionStore for CRUD operations
|
||||
- SessionSearch for FTS5 queries
|
||||
- Location: `~/.claude-mem/claude-mem.db`
|
||||
|
||||
See [Database Architecture](/architecture/database) for schema and FTS5 search.
|
||||
|
||||
### 4. mem-search Skill (v5.4.0+)
|
||||
Skill-based search with progressive disclosure providing 10 search operations:
|
||||
- Search observations, sessions, prompts (full-text FTS5)
|
||||
- Filter by type, concept, file
|
||||
- Get recent context, timeline, timeline by query
|
||||
- API help documentation
|
||||
|
||||
**Token Savings**: ~2,250 tokens per session vs MCP approach
|
||||
- Skill frontmatter: ~250 tokens (loaded at session start)
|
||||
- Full instructions: ~2,500 tokens (loaded on-demand when invoked)
|
||||
- HTTP API endpoints instead of MCP tools
|
||||
|
||||
**Skill Enhancement (v5.5.0)**: Renamed from "search" to "mem-search" for better scope differentiation. Effectiveness increased from 67% to 100% with enhanced triggers and comprehensive documentation.
|
||||
|
||||
See [Search Architecture](/architecture/search-architecture) for technical details and examples.
|
||||
|
||||
### 5. Viewer UI
|
||||
React + TypeScript web interface at http://localhost:37777 featuring:
|
||||
- Real-time memory stream via Server-Sent Events
|
||||
- Infinite scroll pagination with automatic deduplication
|
||||
- Project filtering and settings persistence
|
||||
- GPU-accelerated animations
|
||||
- Self-contained HTML bundle (viewer.html)
|
||||
|
||||
Built with esbuild into a single file deployment.
|
||||
@@ -0,0 +1,448 @@
|
||||
---
|
||||
title: "Search Architecture"
|
||||
description: "mem-search skill with HTTP API and progressive disclosure"
|
||||
---
|
||||
|
||||
# Search Architecture
|
||||
|
||||
Claude-Mem uses a skill-based search architecture that provides intelligent memory retrieval through natural language queries. This replaced the MCP-based approach in v5.4.0, saving ~2,250 tokens per session start. The skill was enhanced and renamed to "mem-search" in v5.5.0 for better scope differentiation.
|
||||
|
||||
## Overview
|
||||
|
||||
**Architecture**: Skill-Based Search + HTTP API + Progressive Disclosure
|
||||
|
||||
**Key Components**:
|
||||
1. **mem-search Skill** (`plugin/skills/mem-search/SKILL.md`) - Auto-invoked when users ask about past work
|
||||
2. **HTTP API Endpoints** (10 routes) - Fast, efficient search operations on port 37777
|
||||
3. **Worker Service** - Express.js server with FTS5 full-text search
|
||||
4. **SQLite Database** - Persistent storage with FTS5 virtual tables
|
||||
5. **Chroma Vector DB** - Semantic search with hybrid retrieval
|
||||
|
||||
**v5.5.0 Enhancement**: Renamed from "search" to "mem-search" with:
|
||||
- Effectiveness increased from 67% to 100%
|
||||
- Concrete triggers increased from 44% to 85%
|
||||
- 5+ unique identifiers for better scope differentiation
|
||||
- Comprehensive documentation (17 files, 12 operation guides)
|
||||
|
||||
## How It Works
|
||||
|
||||
### 1. User Query (Natural Language)
|
||||
|
||||
```
|
||||
User: "What bugs did we fix last session?"
|
||||
```
|
||||
|
||||
### 2. Skill Invocation
|
||||
|
||||
Claude recognizes the intent and invokes the mem-search skill:
|
||||
- Skill frontmatter (~250 tokens) loaded at session start
|
||||
- Full skill instructions loaded on-demand when skill is invoked
|
||||
- Progressive disclosure pattern minimizes context overhead
|
||||
- "mem-search" naming provides clear scope differentiation from native memory
|
||||
|
||||
### 3. HTTP API Call
|
||||
|
||||
The skill uses `curl` to call the HTTP API:
|
||||
|
||||
```bash
|
||||
curl "http://localhost:37777/api/search/observations?query=bugs&type=bugfix&limit=5"
|
||||
```
|
||||
|
||||
### 4. FTS5 Search
|
||||
|
||||
Worker service queries SQLite FTS5 virtual tables:
|
||||
|
||||
```sql
|
||||
SELECT * FROM observations_fts
|
||||
WHERE observations_fts MATCH ?
|
||||
AND type = 'bugfix'
|
||||
ORDER BY rank
|
||||
LIMIT 5
|
||||
```
|
||||
|
||||
### 5. Results Formatted
|
||||
|
||||
Skill formats results and returns to Claude:
|
||||
|
||||
```
|
||||
## Recent Bugfixes
|
||||
|
||||
1. [bugfix] Fixed authentication token expiry
|
||||
Date: 2025-11-08 14:23:45
|
||||
Files: src/auth/jwt.ts
|
||||
|
||||
2. [bugfix] Resolved database connection leak
|
||||
Date: 2025-11-08 13:15:22
|
||||
Files: src/services/database.ts
|
||||
```
|
||||
|
||||
### 6. User Sees Answer
|
||||
|
||||
Claude presents the formatted results naturally in conversation.
|
||||
|
||||
## Architecture Change (v5.4.0)
|
||||
|
||||
### Before: MCP-Based Search
|
||||
|
||||
**Approach**: 9 MCP tools registered at session start
|
||||
|
||||
**Token Cost**: ~2,500 tokens in tool definitions per session
|
||||
- Each tool's schema, parameters, descriptions loaded
|
||||
- All 9 tools available whether needed or not
|
||||
- No progressive disclosure
|
||||
|
||||
**Example MCP Tool**:
|
||||
```json
|
||||
{
|
||||
"name": "search_observations",
|
||||
"description": "Full-text search across observations...",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": { "type": "string", "description": "..." },
|
||||
"type": { "type": "array", "items": { "enum": [...] } },
|
||||
"format": { "enum": ["index", "full"] },
|
||||
// ... many more parameters
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### After: Skill-Based Search
|
||||
|
||||
**Approach**: 1 mem-search skill with progressive disclosure
|
||||
|
||||
**Token Cost**: ~250 tokens in skill frontmatter per session
|
||||
- Only skill description loaded at session start
|
||||
- Full instructions loaded on-demand when skill is invoked
|
||||
- HTTP API endpoints instead of MCP protocol
|
||||
|
||||
**Example Skill Frontmatter**:
|
||||
```markdown
|
||||
# Claude-Mem mem-search Skill
|
||||
|
||||
Access claude-mem's persistent memory through a comprehensive HTTP API.
|
||||
Search for past work, understand context, and learn from previous decisions.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Invoke this skill when users ask about:
|
||||
- Past work: "What did we do last session?"
|
||||
- Bug fixes: "Did we fix this before?"
|
||||
- Features: "How did we implement authentication?"
|
||||
...
|
||||
```
|
||||
|
||||
**Token Savings**: ~2,250 tokens per session start (90% reduction)
|
||||
|
||||
## HTTP API Endpoints
|
||||
|
||||
The worker service exposes 10 search endpoints:
|
||||
|
||||
### Full-Text Search
|
||||
|
||||
```
|
||||
GET /api/search/observations
|
||||
GET /api/search/sessions
|
||||
GET /api/search/prompts
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `query` - FTS5 search query (required)
|
||||
- `type` - Filter by type (bugfix, feature, refactor, etc.)
|
||||
- `project` - Filter by project name
|
||||
- `limit` - Maximum results (default: 20)
|
||||
- `offset` - Pagination offset
|
||||
- `format` - Response format (index or full)
|
||||
|
||||
**Example**:
|
||||
```bash
|
||||
curl "http://localhost:37777/api/search/observations?query=authentication&type=decision&limit=5"
|
||||
```
|
||||
|
||||
### Filtered Search
|
||||
|
||||
```
|
||||
GET /api/search/by-type
|
||||
GET /api/search/by-concept
|
||||
GET /api/search/by-file
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `type` / `concept` / `filePath` - Filter criteria (required)
|
||||
- `project` - Filter by project
|
||||
- `limit` - Maximum results
|
||||
- `format` - Response format
|
||||
|
||||
**Example**:
|
||||
```bash
|
||||
curl "http://localhost:37777/api/search/by-file?filePath=worker-service.ts&limit=10"
|
||||
```
|
||||
|
||||
### Context Retrieval
|
||||
|
||||
```
|
||||
GET /api/context/recent
|
||||
GET /api/context/timeline
|
||||
GET /api/timeline/by-query
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `project` - Filter by project
|
||||
- `limit` - Number of sessions/records
|
||||
- `anchor` - Timeline anchor point (ID or timestamp)
|
||||
- `depth_before` - Records before anchor
|
||||
- `depth_after` - Records after anchor
|
||||
|
||||
**Example**:
|
||||
```bash
|
||||
curl "http://localhost:37777/api/context/recent?project=claude-mem&limit=5"
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
```
|
||||
GET /api/search/help
|
||||
```
|
||||
|
||||
Returns API documentation in JSON format.
|
||||
|
||||
## Progressive Disclosure Pattern
|
||||
|
||||
The mem-search skill uses progressive disclosure to minimize token usage:
|
||||
|
||||
### Layer 1: Skill Frontmatter (Session Start)
|
||||
|
||||
**What's Loaded**: Skill description and when to use it (~250 tokens)
|
||||
|
||||
**Purpose**: Claude can recognize when to invoke the skill
|
||||
|
||||
**Example**:
|
||||
```markdown
|
||||
# Claude-Mem mem-search Skill
|
||||
|
||||
Access claude-mem's persistent memory through a comprehensive HTTP API.
|
||||
|
||||
## When to Use This Skill
|
||||
Invoke this skill when users ask about:
|
||||
- Past work: "What did we do last session?"
|
||||
- Bug fixes: "Did we fix this before?"
|
||||
...
|
||||
```
|
||||
|
||||
### Layer 2: Full Skill Instructions (On-Demand)
|
||||
|
||||
**What's Loaded**: Complete operation documentation (~2,500 tokens)
|
||||
|
||||
**Purpose**: Detailed instructions for each search operation
|
||||
|
||||
**When Loaded**: Only when Claude invokes the skill
|
||||
|
||||
**Example Structure**:
|
||||
```
|
||||
/skills/search/
|
||||
├── SKILL.md (main frontmatter)
|
||||
├── operations/
|
||||
│ ├── observations.md (detailed instructions)
|
||||
│ ├── sessions.md
|
||||
│ ├── prompts.md
|
||||
│ ├── by-type.md
|
||||
│ ├── by-concept.md
|
||||
│ ├── by-file.md
|
||||
│ ├── recent-context.md
|
||||
│ ├── timeline.md
|
||||
│ ├── timeline-by-query.md
|
||||
│ ├── help.md
|
||||
│ ├── formatting.md
|
||||
│ └── common-workflows.md
|
||||
```
|
||||
|
||||
### Layer 3: API Response
|
||||
|
||||
**What's Returned**: Search results in requested format
|
||||
|
||||
**Format Options**:
|
||||
- `index` - Titles, dates, IDs only (~50-100 tokens per result)
|
||||
- `full` - Complete details (~500-1000 tokens per result)
|
||||
|
||||
**Progressive Usage**: Start with `index`, drill down with `full` as needed
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### mem-search Skill Structure
|
||||
|
||||
```
|
||||
plugin/skills/mem-search/
|
||||
├── SKILL.md # Main frontmatter (~250 tokens)
|
||||
├── operations/
|
||||
│ ├── observations.md # Search observations
|
||||
│ ├── sessions.md # Search sessions
|
||||
│ ├── prompts.md # Search prompts
|
||||
│ ├── by-type.md # Filter by type
|
||||
│ ├── by-concept.md # Filter by concept
|
||||
│ ├── by-file.md # Filter by file
|
||||
│ ├── recent-context.md # Get recent context
|
||||
│ ├── timeline.md # Timeline around point
|
||||
│ ├── timeline-by-query.md # Search + timeline
|
||||
│ ├── help.md # API documentation
|
||||
│ ├── formatting.md # Result formatting guide
|
||||
│ └── common-workflows.md # Usage patterns
|
||||
```
|
||||
|
||||
### Worker Service Integration
|
||||
|
||||
**File**: `src/services/worker-service.ts`
|
||||
|
||||
**Search Routes**:
|
||||
```typescript
|
||||
// Full-text search
|
||||
app.get('/api/search/observations', handleSearchObservations);
|
||||
app.get('/api/search/sessions', handleSearchSessions);
|
||||
app.get('/api/search/prompts', handleSearchPrompts);
|
||||
|
||||
// Filtered search
|
||||
app.get('/api/search/by-type', handleSearchByType);
|
||||
app.get('/api/search/by-concept', handleSearchByConcept);
|
||||
app.get('/api/search/by-file', handleSearchByFile);
|
||||
|
||||
// Context retrieval
|
||||
app.get('/api/context/recent', handleRecentContext);
|
||||
app.get('/api/context/timeline', handleTimeline);
|
||||
app.get('/api/timeline/by-query', handleTimelineByQuery);
|
||||
|
||||
// Documentation
|
||||
app.get('/api/search/help', handleHelp);
|
||||
```
|
||||
|
||||
**Database Access**:
|
||||
- Uses `SessionSearch` service for FTS5 queries
|
||||
- Uses `SessionStore` for structured queries
|
||||
- Hybrid search with ChromaDB for semantic similarity
|
||||
|
||||
### Security
|
||||
|
||||
**FTS5 Injection Prevention** (v4.2.3):
|
||||
```typescript
|
||||
function escapeFTS5Query(query: string): string {
|
||||
return query.replace(/"/g, '""');
|
||||
}
|
||||
```
|
||||
|
||||
All user-provided search queries are properly escaped to prevent SQL injection.
|
||||
|
||||
**Comprehensive Testing**: 332 injection attack tests covering:
|
||||
- Special characters
|
||||
- SQL keywords
|
||||
- Quote escaping
|
||||
- Boolean operators
|
||||
|
||||
## Benefits
|
||||
|
||||
### 1. Token Efficiency
|
||||
|
||||
**Before (MCP)**:
|
||||
- Session start: ~2,500 tokens for tool definitions
|
||||
- Every session pays this cost
|
||||
- No progressive disclosure
|
||||
|
||||
**After (Skill)**:
|
||||
- Session start: ~250 tokens for skill frontmatter
|
||||
- Full instructions: ~2,500 tokens (only when invoked)
|
||||
- Net savings: ~2,250 tokens per session (~90% reduction)
|
||||
|
||||
### 2. Natural Language Interface
|
||||
|
||||
**Before**: Users needed to learn MCP tool syntax
|
||||
```
|
||||
search_observations with query="authentication" and type="decision"
|
||||
```
|
||||
|
||||
**After**: Users ask naturally
|
||||
```
|
||||
"What decisions did we make about authentication?"
|
||||
```
|
||||
|
||||
Claude translates to appropriate API call.
|
||||
|
||||
### 3. Flexibility
|
||||
|
||||
**HTTP API Benefits**:
|
||||
- Can be called from skills, MCP tools, or other clients
|
||||
- Easy to test with curl
|
||||
- Standard REST conventions
|
||||
- JSON responses
|
||||
|
||||
**Progressive Disclosure**:
|
||||
- Loads only what's needed
|
||||
- Can add more operations without increasing base cost
|
||||
- Documentation co-located with operations
|
||||
|
||||
### 4. Performance
|
||||
|
||||
**Fast Queries**: FTS5 full-text search under 10ms for typical queries
|
||||
|
||||
**Caching**: HTTP layer allows response caching
|
||||
|
||||
**Pagination**: Efficient result pagination with offset/limit
|
||||
|
||||
## Migration Notes
|
||||
|
||||
### For Users
|
||||
|
||||
**No Action Required**: The migration from MCP to skill-based search is transparent.
|
||||
|
||||
**Same Questions Work**: Natural language queries work exactly the same way.
|
||||
|
||||
**Invisible Change**: Users won't notice any difference except better performance.
|
||||
|
||||
### For Developers
|
||||
|
||||
**Deprecated**: MCP search server (`src/servers/search-server.ts`)
|
||||
- Source file kept for reference
|
||||
- No longer built or registered
|
||||
- MCP configuration removed from `plugin/.mcp.json`
|
||||
|
||||
**New Implementation**: Skill-based search
|
||||
- Skill files: `plugin/skills/mem-search/`
|
||||
- HTTP endpoints: `src/services/worker-service.ts` (lines 200-400)
|
||||
- Build script: `npm run build` includes skill files
|
||||
- Sync script: `npm run sync-marketplace` copies to plugin directory
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Worker Service Not Running
|
||||
|
||||
If searches fail, check worker service:
|
||||
|
||||
```bash
|
||||
pm2 list # Check status
|
||||
npm run worker:restart # Restart worker
|
||||
npm run worker:logs # View logs
|
||||
```
|
||||
|
||||
### HTTP Endpoints Not Responding
|
||||
|
||||
Test endpoints directly:
|
||||
|
||||
```bash
|
||||
# Health check
|
||||
curl http://localhost:37777/health
|
||||
|
||||
# Search test
|
||||
curl "http://localhost:37777/api/search/observations?query=test&limit=1"
|
||||
```
|
||||
|
||||
### Skill Not Invoking
|
||||
|
||||
If Claude doesn't invoke the mem-search skill automatically:
|
||||
|
||||
1. Check skill files exist: `ls ~/.claude/plugins/marketplaces/thedotmack/plugin/skills/mem-search/`
|
||||
2. Restart Claude Code session to reload skill definitions
|
||||
3. Try more explicit phrasing: "Search past sessions for bug fixes" or "What did we do in yesterday's session?"
|
||||
4. Ensure your question is about previous sessions (not current conversation context)
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Search Tools Usage](/usage/search-tools) - User guide with examples
|
||||
- [Worker Service Architecture](/architecture/worker-service) - HTTP API details
|
||||
- [Database Schema](/architecture/database) - FTS5 tables and indexes
|
||||
@@ -0,0 +1,443 @@
|
||||
---
|
||||
title: "Worker Service"
|
||||
description: "HTTP API and PM2 process management"
|
||||
---
|
||||
|
||||
# Worker Service
|
||||
|
||||
The worker service is a long-running HTTP API built with Express.js and managed by PM2. It processes observations through the Claude Agent SDK separately from hook execution to prevent timeout issues.
|
||||
|
||||
## Overview
|
||||
|
||||
- **Technology**: Express.js HTTP server
|
||||
- **Process Manager**: PM2
|
||||
- **Port**: Fixed port 37777 (configurable via `CLAUDE_MEM_WORKER_PORT`)
|
||||
- **Location**: `src/services/worker-service.ts`
|
||||
- **Built Output**: `plugin/scripts/worker-service.cjs`
|
||||
- **Model**: Configurable via `CLAUDE_MEM_MODEL` environment variable (default: claude-sonnet-4-5)
|
||||
|
||||
## REST API Endpoints
|
||||
|
||||
The worker service exposes 14 HTTP endpoints organized into four categories:
|
||||
|
||||
### Viewer & Health Endpoints
|
||||
|
||||
#### 1. Viewer UI
|
||||
```
|
||||
GET /
|
||||
```
|
||||
|
||||
**Purpose**: Serves the web-based viewer UI (v5.1.0+)
|
||||
|
||||
**Response**: HTML page with embedded React application
|
||||
|
||||
**Features**:
|
||||
- Real-time memory stream visualization
|
||||
- Infinite scroll pagination
|
||||
- Project filtering
|
||||
- SSE-based live updates
|
||||
- Theme toggle (light/dark mode) as of v5.1.2
|
||||
|
||||
#### 2. Health Check
|
||||
```
|
||||
GET /health
|
||||
```
|
||||
|
||||
**Purpose**: Worker health status check
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"uptime": 12345,
|
||||
"port": 37777
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Server-Sent Events Stream
|
||||
```
|
||||
GET /stream
|
||||
```
|
||||
|
||||
**Purpose**: Real-time updates for viewer UI
|
||||
|
||||
**Response**: SSE stream with events:
|
||||
- `observation-created`: New observation added
|
||||
- `session-summary-created`: New summary generated
|
||||
- `user-prompt-created`: New prompt recorded
|
||||
|
||||
**Event Format**:
|
||||
```
|
||||
event: observation-created
|
||||
data: {"id": 123, "title": "...", ...}
|
||||
```
|
||||
|
||||
### Data Retrieval Endpoints
|
||||
|
||||
#### 4. Get Prompts
|
||||
```
|
||||
GET /api/prompts?project=my-project&limit=20&offset=0
|
||||
```
|
||||
|
||||
**Purpose**: Retrieve paginated user prompts
|
||||
|
||||
**Query Parameters**:
|
||||
- `project` (optional): Filter by project name
|
||||
- `limit` (default: 20): Number of results
|
||||
- `offset` (default: 0): Pagination offset
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"prompts": [{
|
||||
"id": 1,
|
||||
"session_id": "abc123",
|
||||
"prompt": "User's prompt text",
|
||||
"prompt_number": 1,
|
||||
"created_at": "2025-11-06T10:30:00Z"
|
||||
}],
|
||||
"total": 150,
|
||||
"hasMore": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 5. Get Observations
|
||||
```
|
||||
GET /api/observations?project=my-project&limit=20&offset=0
|
||||
```
|
||||
|
||||
**Purpose**: Retrieve paginated observations
|
||||
|
||||
**Query Parameters**:
|
||||
- `project` (optional): Filter by project name
|
||||
- `limit` (default: 20): Number of results
|
||||
- `offset` (default: 0): Pagination offset
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"observations": [{
|
||||
"id": 123,
|
||||
"title": "Fix authentication bug",
|
||||
"type": "bugfix",
|
||||
"narrative": "...",
|
||||
"created_at": "2025-11-06T10:30:00Z"
|
||||
}],
|
||||
"total": 500,
|
||||
"hasMore": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 6. Get Summaries
|
||||
```
|
||||
GET /api/summaries?project=my-project&limit=20&offset=0
|
||||
```
|
||||
|
||||
**Purpose**: Retrieve paginated session summaries
|
||||
|
||||
**Query Parameters**:
|
||||
- `project` (optional): Filter by project name
|
||||
- `limit` (default: 20): Number of results
|
||||
- `offset` (default: 0): Pagination offset
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"summaries": [{
|
||||
"id": 456,
|
||||
"session_id": "abc123",
|
||||
"request": "User's original request",
|
||||
"completed": "Work finished",
|
||||
"created_at": "2025-11-06T10:30:00Z"
|
||||
}],
|
||||
"total": 100,
|
||||
"hasMore": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 7. Get Stats
|
||||
```
|
||||
GET /api/stats
|
||||
```
|
||||
|
||||
**Purpose**: Get database statistics by project
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"byProject": {
|
||||
"my-project": {
|
||||
"observations": 245,
|
||||
"summaries": 12,
|
||||
"prompts": 48
|
||||
},
|
||||
"other-project": {
|
||||
"observations": 156,
|
||||
"summaries": 8,
|
||||
"prompts": 32
|
||||
}
|
||||
},
|
||||
"total": {
|
||||
"observations": 401,
|
||||
"summaries": 20,
|
||||
"prompts": 80,
|
||||
"sessions": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Settings Endpoints
|
||||
|
||||
#### 8. Get Settings
|
||||
```
|
||||
GET /api/settings
|
||||
```
|
||||
|
||||
**Purpose**: Retrieve user settings
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"sidebarOpen": true,
|
||||
"selectedProject": "my-project",
|
||||
"theme": "dark"
|
||||
}
|
||||
```
|
||||
|
||||
#### 9. Save Settings
|
||||
```
|
||||
POST /api/settings
|
||||
```
|
||||
|
||||
**Purpose**: Persist user settings
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"sidebarOpen": false,
|
||||
"selectedProject": "other-project",
|
||||
"theme": "light"
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
### Session Management Endpoints
|
||||
|
||||
#### 10. Initialize Session
|
||||
```
|
||||
POST /sessions/:sessionDbId/init
|
||||
```
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"sdk_session_id": "abc-123",
|
||||
"project": "my-project"
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"session_id": "abc-123"
|
||||
}
|
||||
```
|
||||
|
||||
#### 11. Add Observation
|
||||
```
|
||||
POST /sessions/:sessionDbId/observations
|
||||
```
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"tool_name": "Read",
|
||||
"tool_input": {...},
|
||||
"tool_result": "...",
|
||||
"correlation_id": "xyz-789"
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"observation_id": 123
|
||||
}
|
||||
```
|
||||
|
||||
#### 12. Generate Summary
|
||||
```
|
||||
POST /sessions/:sessionDbId/summarize
|
||||
```
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"trigger": "stop"
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"summary_id": 456
|
||||
}
|
||||
```
|
||||
|
||||
#### 13. Session Status
|
||||
```
|
||||
GET /sessions/:sessionDbId/status
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"session_id": "abc-123",
|
||||
"status": "active",
|
||||
"observation_count": 42,
|
||||
"summary_count": 1
|
||||
}
|
||||
```
|
||||
|
||||
#### 14. Delete Session
|
||||
```
|
||||
DELETE /sessions/:sessionDbId
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: As of v4.1.0, the cleanup hook no longer calls this endpoint. Sessions are marked complete instead of deleted to allow graceful worker shutdown.
|
||||
|
||||
## PM2 Management
|
||||
|
||||
### Configuration
|
||||
|
||||
The worker is configured via `ecosystem.config.cjs`:
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
apps: [{
|
||||
name: 'claude-mem-worker',
|
||||
script: './plugin/scripts/worker-service.cjs',
|
||||
instances: 1,
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
max_memory_restart: '1G',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
FORCE_COLOR: '1'
|
||||
}
|
||||
}]
|
||||
};
|
||||
```
|
||||
|
||||
### Commands
|
||||
|
||||
```bash
|
||||
# Start worker (auto-starts on first session)
|
||||
npm run worker:start
|
||||
|
||||
# Stop worker
|
||||
npm run worker:stop
|
||||
|
||||
# Restart worker
|
||||
npm run worker:restart
|
||||
|
||||
# View logs
|
||||
npm run worker:logs
|
||||
|
||||
# Check status
|
||||
npm run worker:status
|
||||
```
|
||||
|
||||
### Auto-Start Behavior
|
||||
|
||||
As of v4.0.0, the worker service auto-starts when the SessionStart hook fires. Manual start is optional.
|
||||
|
||||
## Claude Agent SDK Integration
|
||||
|
||||
The worker service routes observations to the Claude Agent SDK for AI-powered processing:
|
||||
|
||||
### Processing Flow
|
||||
|
||||
1. **Observation Queue**: Observations accumulate in memory
|
||||
2. **SDK Processing**: Observations sent to Claude via Agent SDK
|
||||
3. **XML Parsing**: Responses parsed for structured data
|
||||
4. **Database Storage**: Processed observations stored in SQLite
|
||||
|
||||
### SDK Components
|
||||
|
||||
- **Prompts** (`src/sdk/prompts.ts`): Builds XML-structured prompts
|
||||
- **Parser** (`src/sdk/parser.ts`): Parses Claude's XML responses
|
||||
- **Worker** (`src/sdk/worker.ts`): Main SDK agent loop
|
||||
|
||||
### Model Configuration
|
||||
|
||||
Set the AI model used for processing via environment variable:
|
||||
|
||||
```bash
|
||||
export CLAUDE_MEM_MODEL=claude-sonnet-4-5
|
||||
```
|
||||
|
||||
Available models:
|
||||
- `claude-haiku-4-5` - Fast, cost-efficient
|
||||
- `claude-sonnet-4-5` - Balanced (default)
|
||||
- `claude-opus-4` - Most capable
|
||||
- `claude-3-7-sonnet` - Alternative version
|
||||
|
||||
## Port Allocation
|
||||
|
||||
The worker uses a fixed port (37777 by default) for consistent communication:
|
||||
|
||||
- **Default**: Port 37777
|
||||
- **Override**: Set `CLAUDE_MEM_WORKER_PORT` environment variable
|
||||
- **Port File**: `${CLAUDE_PLUGIN_ROOT}/data/worker.port` tracks current port
|
||||
|
||||
If port 37777 is in use, the worker will fail to start. Set a custom port via environment variable.
|
||||
|
||||
## Data Storage
|
||||
|
||||
The worker service stores data in the plugin data directory:
|
||||
|
||||
```
|
||||
${CLAUDE_PLUGIN_ROOT}/data/
|
||||
├── claude-mem.db # SQLite database
|
||||
├── worker.port # Current worker port file
|
||||
└── logs/
|
||||
├── worker-out.log # Worker stdout logs
|
||||
└── worker-error.log # Worker stderr logs
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
The worker implements graceful degradation:
|
||||
|
||||
- **Database Errors**: Logged but don't crash the service
|
||||
- **SDK Errors**: Retried with exponential backoff
|
||||
- **Network Errors**: Logged and skipped
|
||||
- **Invalid Input**: Validated and rejected with error response
|
||||
|
||||
## Performance
|
||||
|
||||
- **Async Processing**: Observations processed asynchronously
|
||||
- **In-Memory Queue**: Fast observation accumulation
|
||||
- **Batch Processing**: Multiple observations processed together
|
||||
- **Connection Pooling**: SQLite connections reused
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
See [Troubleshooting - Worker Issues](../troubleshooting.md#worker-service-issues) for common problems and solutions.
|
||||
@@ -0,0 +1,144 @@
|
||||
---
|
||||
title: "Beta Features"
|
||||
description: "Try experimental features like Endless Mode before they're released"
|
||||
---
|
||||
|
||||
# Beta Features
|
||||
|
||||
Claude-Mem offers a beta channel for users who want to try experimental features before they're released to the stable channel.
|
||||
|
||||
## Version Channel Switching
|
||||
|
||||
You can switch between stable and beta versions directly from the web viewer UI at http://localhost:37777.
|
||||
|
||||
### How to Access
|
||||
|
||||
1. Open the Claude-Mem viewer at http://localhost:37777
|
||||
2. Click the **Settings** gear icon in the top-right
|
||||
3. Find the **Version Channel** section
|
||||
4. Click **Try Beta (Endless Mode)** to switch to beta, or **Switch to Stable** to return
|
||||
|
||||
### What Happens When You Switch
|
||||
|
||||
When switching versions:
|
||||
|
||||
1. **Local changes are discarded** - Any modifications in the plugin directory are reset
|
||||
2. **Git fetch and checkout** - The installed plugin switches to the target branch
|
||||
3. **Dependencies reinstall** - `npm install` runs to ensure correct dependencies
|
||||
4. **Worker restarts automatically** - The background service restarts with the new version
|
||||
|
||||
**Your memory data is always preserved.** The database at `~/.claude-mem/claude-mem.db` is not affected by version switching. All your observations, sessions, and summaries remain intact.
|
||||
|
||||
### Version Indicators
|
||||
|
||||
The Version Channel section shows your current status:
|
||||
|
||||
- **Stable** (green badge) - You're running the production release
|
||||
- **Beta** (orange badge) - You're running the beta with experimental features
|
||||
|
||||
You'll also see the exact branch name (e.g., `main` for stable, `beta/7.0` for beta).
|
||||
|
||||
## Endless Mode (Beta)
|
||||
|
||||
The flagship experimental feature in beta is **Endless Mode** - a biomimetic memory architecture that dramatically extends how long Claude can maintain context in a session.
|
||||
|
||||
### The Problem Endless Mode Solves
|
||||
|
||||
In standard Claude Code sessions:
|
||||
|
||||
- Tool outputs (file reads, bash output, search results) accumulate in the context window
|
||||
- Each tool can add 1-10k+ tokens to the context
|
||||
- After ~50 tool uses, the context window fills up (~200k tokens)
|
||||
- You're forced to start a new session, losing conversational continuity
|
||||
|
||||
Worse, Claude **re-synthesizes all previous tool outputs** on every response. This is O(N²) complexity - quadratically growing both in tokens and compute.
|
||||
|
||||
### How Endless Mode Works
|
||||
|
||||
Endless Mode applies a biomimetic memory architecture inspired by how human memory works:
|
||||
|
||||
**Two-Tier Memory System:**
|
||||
|
||||
```
|
||||
Working Memory (Context Window):
|
||||
→ Compressed observations only (~500 tokens each)
|
||||
→ Fast, efficient, manageable
|
||||
|
||||
Archive Memory (Transcript File):
|
||||
→ Full tool outputs preserved on disk
|
||||
→ Perfect recall, searchable
|
||||
```
|
||||
|
||||
**The Key Innovation**: After each tool use, Endless Mode:
|
||||
1. Waits for the worker to generate a compressed observation (blocking)
|
||||
2. Transforms the transcript file on disk
|
||||
3. Replaces the full tool output with the compressed observation
|
||||
4. Claude resumes with the compressed context
|
||||
|
||||
This transforms O(N²) scaling into O(N) - linear instead of quadratic.
|
||||
|
||||
### Expected Results
|
||||
|
||||
Based on analysis of real sessions:
|
||||
|
||||
- **Token savings**: ~95% reduction in context window usage
|
||||
- **Efficiency gain**: ~20x more tool uses before context exhaustion
|
||||
- **Quality preservation**: Observations cache the synthesis result, so no information is lost
|
||||
|
||||
### Caveats
|
||||
|
||||
Endless Mode is experimental:
|
||||
|
||||
- **Adds latency** - Blocking hooks wait for observation generation (60-90s per tool use)
|
||||
- **Requires working database** - Observations must save successfully for transformation
|
||||
- **New architecture** - Less battle-tested than standard mode
|
||||
|
||||
### When to Use Beta
|
||||
|
||||
Consider switching to beta if you:
|
||||
|
||||
- Frequently hit context window limits
|
||||
- Work on long, complex sessions with many tool uses
|
||||
- Want to help test and provide feedback on new features
|
||||
- Are comfortable with experimental software
|
||||
|
||||
### When to Stay on Stable
|
||||
|
||||
Stay on stable if you:
|
||||
|
||||
- Need maximum reliability for critical work
|
||||
- Prefer battle-tested, production-ready features
|
||||
- Don't frequently hit context limits
|
||||
- Want the smoothest, fastest experience
|
||||
|
||||
## Checking for Updates
|
||||
|
||||
While on beta (or stable), you can check for updates:
|
||||
|
||||
1. Open Settings in the viewer
|
||||
2. In the Version Channel section, click **Check for Updates**
|
||||
3. The plugin will pull the latest changes and restart
|
||||
|
||||
## Switching Back
|
||||
|
||||
If you encounter issues on beta:
|
||||
|
||||
1. Open Settings in the viewer
|
||||
2. Click **Switch to Stable**
|
||||
3. Wait for the worker to restart
|
||||
|
||||
Your memory data is preserved, and you'll be back on the stable release.
|
||||
|
||||
## Providing Feedback
|
||||
|
||||
If you encounter bugs or have feedback about beta features:
|
||||
|
||||
- Open an issue at [GitHub Issues](https://github.com/thedotmack/claude-mem/issues)
|
||||
- Include your branch (`beta/7.0` etc.) in the report
|
||||
- Describe what you expected vs. what happened
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Configuration](configuration) - Customize other Claude-Mem settings
|
||||
- [Troubleshooting](troubleshooting) - Common issues and solutions
|
||||
- [Architecture Overview](architecture/overview) - Understand how Claude-Mem works
|
||||
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 2.1 MiB |
@@ -0,0 +1,439 @@
|
||||
---
|
||||
title: "Configuration"
|
||||
description: "Environment variables and settings for Claude-Mem"
|
||||
---
|
||||
|
||||
# Configuration
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|-------------------------------|---------------------------------|---------------------------------------|
|
||||
| `CLAUDE_PLUGIN_ROOT` | Set by Claude Code | Plugin installation directory |
|
||||
| `CLAUDE_MEM_DATA_DIR` | `~/.claude-mem/` | Data directory (production default) |
|
||||
| `CLAUDE_CODE_PATH` | Auto-detected | Path to Claude Code CLI (for Windows) |
|
||||
| `CLAUDE_MEM_WORKER_PORT` | `37777` | Worker service port |
|
||||
| `CLAUDE_MEM_MODEL` | `claude-haiku-4-5` | AI model for processing observations |
|
||||
| `CLAUDE_MEM_CONTEXT_OBSERVATIONS` | `50` | Number of observations to inject |
|
||||
| `NODE_ENV` | `production` | Environment mode |
|
||||
| `FORCE_COLOR` | `1` | Enable colored logs |
|
||||
|
||||
## Model Configuration
|
||||
|
||||
Configure which AI model processes your observations.
|
||||
|
||||
### Available Models
|
||||
|
||||
- `claude-haiku-4-5` - Fast, cost-efficient (default)
|
||||
- `claude-sonnet-4-5` - Balanced
|
||||
- `claude-opus-4` - Most capable
|
||||
- `claude-3-7-sonnet` - Alternative version
|
||||
|
||||
### Using the Interactive Script
|
||||
|
||||
```bash
|
||||
./claude-mem-settings.sh
|
||||
```
|
||||
|
||||
This script manages `CLAUDE_MEM_MODEL` in `~/.claude/settings.json`.
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
Edit `~/.claude/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"CLAUDE_MEM_MODEL": "claude-haiku-4-5"
|
||||
}
|
||||
```
|
||||
|
||||
## Files and Directories
|
||||
|
||||
### Data Directory Structure
|
||||
|
||||
The data directory location depends on the environment:
|
||||
- **Production (installed plugin)**: `~/.claude-mem/` (always, regardless of CLAUDE_PLUGIN_ROOT)
|
||||
- **Development**: Can be overridden with `CLAUDE_MEM_DATA_DIR`
|
||||
|
||||
```
|
||||
~/.claude-mem/
|
||||
├── claude-mem.db # SQLite database
|
||||
├── .install-version # Cached version for smart installer
|
||||
├── worker.port # Current worker port file
|
||||
└── logs/
|
||||
├── worker-out.log # Worker stdout logs
|
||||
└── worker-error.log # Worker stderr logs
|
||||
```
|
||||
|
||||
### Plugin Directory Structure
|
||||
|
||||
```
|
||||
${CLAUDE_PLUGIN_ROOT}/
|
||||
├── .claude-plugin/
|
||||
│ └── plugin.json # Plugin metadata
|
||||
├── .mcp.json # MCP server configuration
|
||||
├── hooks/
|
||||
│ └── hooks.json # Hook configuration
|
||||
├── scripts/ # Built executables
|
||||
│ ├── smart-install.js # Smart installer script
|
||||
│ ├── context-hook.js # Context injection hook
|
||||
│ ├── new-hook.js # Session creation hook
|
||||
│ ├── save-hook.js # Observation capture hook
|
||||
│ ├── summary-hook.js # Summary generation hook
|
||||
│ ├── cleanup-hook.js # Session cleanup hook
|
||||
│ ├── worker-service.cjs # Worker service (CJS)
|
||||
│ └── search-server.cjs # MCP search server (CJS)
|
||||
└── ui/
|
||||
└── viewer.html # Web viewer UI bundle
|
||||
```
|
||||
|
||||
## Plugin Configuration
|
||||
|
||||
### Hooks Configuration
|
||||
|
||||
Hooks are configured in `plugin/hooks/hooks.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Claude-mem memory system hooks",
|
||||
"hooks": {
|
||||
"SessionStart": [{
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/smart-install.js && node ${CLAUDE_PLUGIN_ROOT}/scripts/context-hook.js",
|
||||
"timeout": 120
|
||||
}]
|
||||
}],
|
||||
"UserPromptSubmit": [{
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/new-hook.js",
|
||||
"timeout": 120
|
||||
}]
|
||||
}],
|
||||
"PostToolUse": [{
|
||||
"matcher": "*",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/save-hook.js",
|
||||
"timeout": 120
|
||||
}]
|
||||
}],
|
||||
"Stop": [{
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/summary-hook.js",
|
||||
"timeout": 120
|
||||
}]
|
||||
}],
|
||||
"SessionEnd": [{
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/cleanup-hook.js",
|
||||
"timeout": 120
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Search Configuration (v5.4.0+)
|
||||
|
||||
**Migration Note**: As of v5.4.0, Claude-Mem uses skill-based search instead of MCP tools. As of v5.5.0, the skill was renamed to "mem-search" for better scope differentiation.
|
||||
|
||||
**Previous (v5.3.x and earlier)**: MCP search server with 9 tools (~2,500 tokens per session)
|
||||
**Current (v5.4.0+)**: mem-search skill with HTTP API (~250 tokens per session)
|
||||
|
||||
**No configuration required** - the mem-search skill is automatically available in Claude Code sessions.
|
||||
|
||||
Search operations are now provided via:
|
||||
- **Skill**: `plugin/skills/mem-search/SKILL.md` (auto-invoked when users ask about past work)
|
||||
- **HTTP API**: 10 endpoints on worker service port 37777
|
||||
- **Progressive Disclosure**: Full instructions loaded on-demand only when needed
|
||||
|
||||
## Version Channel
|
||||
|
||||
Claude-Mem supports switching between stable and beta versions via the web viewer UI.
|
||||
|
||||
### Accessing Version Channel
|
||||
|
||||
1. Open the viewer at http://localhost:37777
|
||||
2. Click the Settings gear icon
|
||||
3. Find the **Version Channel** section
|
||||
|
||||
### Switching Versions
|
||||
|
||||
- **Try Beta**: Click "Try Beta (Endless Mode)" to switch to the beta branch with experimental features
|
||||
- **Switch to Stable**: Click "Switch to Stable" to return to the production release
|
||||
- **Check for Updates**: Pull the latest changes for your current branch
|
||||
|
||||
**Your memory data is preserved** when switching versions. Only the plugin code changes.
|
||||
|
||||
See [Beta Features](beta-features) for details on what's available in beta.
|
||||
|
||||
## PM2 Configuration
|
||||
|
||||
Worker service is managed by PM2 via `ecosystem.config.cjs`:
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
apps: [{
|
||||
name: 'claude-mem-worker',
|
||||
script: './plugin/scripts/worker-service.cjs',
|
||||
instances: 1,
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
max_memory_restart: '1G',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
FORCE_COLOR: '1'
|
||||
}
|
||||
}]
|
||||
};
|
||||
```
|
||||
|
||||
### PM2 Settings
|
||||
|
||||
- **instances**: 1 (single instance)
|
||||
- **autorestart**: true (auto-restart on crash)
|
||||
- **watch**: false (no file watching)
|
||||
- **max_memory_restart**: 1G (restart if memory exceeds 1GB)
|
||||
|
||||
## Context Injection Configuration
|
||||
|
||||
Claude-Mem injects past observations into each new session, giving Claude awareness of recent work. You can configure exactly what gets injected using the **Context Settings Modal**.
|
||||
|
||||
### Context Settings Modal
|
||||
|
||||
Access the settings modal from the web viewer at http://localhost:37777:
|
||||
|
||||
1. Click the **gear icon** in the header
|
||||
2. Adjust settings in the right panel
|
||||
3. See changes reflected live in the **Terminal Preview** on the left
|
||||
4. Settings auto-save as you change them
|
||||
|
||||
The Terminal Preview shows exactly what will be injected at the start of your next Claude Code session for the selected project.
|
||||
|
||||
### Loading Settings
|
||||
|
||||
Control how many observations are injected:
|
||||
|
||||
| Setting | Default | Range | Description |
|
||||
|---------|---------|-------|-------------|
|
||||
| **Observations** | 50 | 1-200 | Total number of recent observations to include |
|
||||
| **Sessions** | 10 | 1-50 | Number of recent sessions to pull observations from |
|
||||
|
||||
**Considerations**:
|
||||
- **Higher values** = More context but slower SessionStart and more tokens used
|
||||
- **Lower values** = Faster SessionStart but less historical awareness
|
||||
- Default of 50 observations from 10 sessions balances context richness with performance
|
||||
|
||||
### Filter Settings
|
||||
|
||||
Control which observation types and concepts are included:
|
||||
|
||||
**Types** (select any combination):
|
||||
- `bugfix` - Bug fixes and error resolutions
|
||||
- `feature` - New functionality additions
|
||||
- `refactor` - Code restructuring
|
||||
- `discovery` - Learnings about how code works
|
||||
- `decision` - Architectural or design decisions
|
||||
- `change` - General code changes
|
||||
|
||||
**Concepts** (select any combination):
|
||||
- `how-it-works` - System behavior explanations
|
||||
- `why-it-exists` - Rationale for code/design
|
||||
- `what-changed` - Change summaries
|
||||
- `problem-solution` - Problem/solution pairs
|
||||
- `gotcha` - Edge cases and pitfalls
|
||||
- `pattern` - Recurring patterns
|
||||
- `trade-off` - Design trade-offs
|
||||
|
||||
Use "All" or "None" buttons to quickly select/deselect all options.
|
||||
|
||||
### Display Settings
|
||||
|
||||
Control how observations appear in the context:
|
||||
|
||||
**Full Observations**:
|
||||
| Setting | Default | Options | Description |
|
||||
|---------|---------|---------|-------------|
|
||||
| **Count** | 5 | 0-20 | How many observations show expanded details |
|
||||
| **Field** | narrative | narrative, facts | Which field to expand |
|
||||
|
||||
The most recent N observations (set by Count) show their full narrative or facts. Remaining observations show only title, type, and token counts in a compact table format.
|
||||
|
||||
**Token Economics** (toggles):
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| **Read cost** | true | Show tokens to read each observation |
|
||||
| **Work investment** | true | Show tokens spent creating the observation |
|
||||
| **Savings** | true | Show total tokens saved by reusing context |
|
||||
|
||||
Token economics help you understand the value of cached observations vs. re-reading files.
|
||||
|
||||
### Advanced Settings
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| **Model** | claude-haiku-4-5 | AI model for generating observations |
|
||||
| **Worker Port** | 37777 | Port for background worker service |
|
||||
| **MCP search server** | true | Enable Model Context Protocol search tools |
|
||||
| **Include last summary** | false | Add previous session's summary to context |
|
||||
| **Include last message** | false | Add previous session's final message |
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
Settings are stored in `~/.claude-mem/settings.json`. You can also configure via environment variables in `~/.claude/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"env": {
|
||||
"CLAUDE_MEM_CONTEXT_OBSERVATIONS": "100",
|
||||
"CLAUDE_MEM_CONTEXT_SESSION_COUNT": "20",
|
||||
"CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES": "bugfix,decision,discovery",
|
||||
"CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS": "how-it-works,gotcha",
|
||||
"CLAUDE_MEM_CONTEXT_FULL_COUNT": "10",
|
||||
"CLAUDE_MEM_CONTEXT_FULL_FIELD": "narrative",
|
||||
"CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS": "true",
|
||||
"CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS": "true",
|
||||
"CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT": "true",
|
||||
"CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY": "false",
|
||||
"CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE": "false"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: The Context Settings Modal is the recommended way to configure these settings, as it provides live preview of changes.
|
||||
|
||||
## Customization
|
||||
|
||||
### Custom Data Directory
|
||||
|
||||
For development or testing, override the data directory:
|
||||
|
||||
```bash
|
||||
export CLAUDE_MEM_DATA_DIR=/custom/path
|
||||
```
|
||||
|
||||
### Custom Worker Port
|
||||
|
||||
If port 37777 is in use:
|
||||
|
||||
```bash
|
||||
export CLAUDE_MEM_WORKER_PORT=38000
|
||||
npm run worker:restart
|
||||
```
|
||||
|
||||
### Custom Model
|
||||
|
||||
Use a different AI model:
|
||||
|
||||
```bash
|
||||
export CLAUDE_MEM_MODEL=claude-opus-4
|
||||
npm run worker:restart
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Hook Timeouts
|
||||
|
||||
Modify timeouts in `plugin/hooks/hooks.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"timeout": 120 // Default: 120 seconds
|
||||
}
|
||||
```
|
||||
|
||||
Recommended values:
|
||||
- SessionStart: 120s (needs time for smart install check and context retrieval)
|
||||
- UserPromptSubmit: 60s
|
||||
- PostToolUse: 120s (can process many observations)
|
||||
- Stop: 60s
|
||||
- SessionEnd: 60s
|
||||
|
||||
**Note**: With smart install caching (v5.0.3+), SessionStart is typically very fast (10ms) unless dependencies need installation.
|
||||
|
||||
### Worker Memory Limit
|
||||
|
||||
Modify PM2 memory limit in `ecosystem.config.cjs`:
|
||||
|
||||
```javascript
|
||||
{
|
||||
max_memory_restart: '2G' // Increase if needed
|
||||
}
|
||||
```
|
||||
|
||||
### Logging Verbosity
|
||||
|
||||
Enable debug logging:
|
||||
|
||||
```bash
|
||||
export DEBUG=claude-mem:*
|
||||
npm run worker:restart
|
||||
npm run worker:logs
|
||||
```
|
||||
|
||||
## Configuration Best Practices
|
||||
|
||||
1. **Use defaults**: Default configuration works for most use cases
|
||||
2. **Override selectively**: Only change what you need
|
||||
3. **Document changes**: Keep track of custom configurations
|
||||
4. **Test after changes**: Verify worker restarts successfully
|
||||
5. **Monitor logs**: Check worker logs after configuration changes
|
||||
|
||||
## Troubleshooting Configuration
|
||||
|
||||
### Configuration Not Applied
|
||||
|
||||
1. Restart worker after changes:
|
||||
```bash
|
||||
npm run worker:restart
|
||||
```
|
||||
|
||||
2. Verify environment variables:
|
||||
```bash
|
||||
echo $CLAUDE_MEM_MODEL
|
||||
echo $CLAUDE_MEM_WORKER_PORT
|
||||
```
|
||||
|
||||
3. Check worker logs:
|
||||
```bash
|
||||
npm run worker:logs
|
||||
```
|
||||
|
||||
### Invalid Model Name
|
||||
|
||||
If you specify an invalid model name, the worker will fall back to `claude-haiku-4-5` and log a warning.
|
||||
|
||||
Valid models:
|
||||
- claude-haiku-4-5
|
||||
- claude-sonnet-4-5
|
||||
- claude-opus-4
|
||||
- claude-3-7-sonnet
|
||||
|
||||
### Port Already in Use
|
||||
|
||||
If port 37777 is already in use:
|
||||
|
||||
1. Set custom port:
|
||||
```bash
|
||||
export CLAUDE_MEM_WORKER_PORT=38000
|
||||
```
|
||||
|
||||
2. Restart worker:
|
||||
```bash
|
||||
npm run worker:restart
|
||||
```
|
||||
|
||||
3. Verify new port:
|
||||
```bash
|
||||
cat ~/.claude-mem/worker.port
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Architecture Overview](architecture/overview) - Understand the system
|
||||
- [Troubleshooting](troubleshooting) - Common issues
|
||||
- [Development](development) - Building from source
|
||||
@@ -0,0 +1,222 @@
|
||||
# Context Engineering for AI Agents: Best Practices Cheat Sheet
|
||||
|
||||
## Core Principle
|
||||
**Find the smallest possible set of high-signal tokens that maximize the likelihood of your desired outcome.**
|
||||
|
||||
---
|
||||
|
||||
## Context Engineering vs Prompt Engineering
|
||||
|
||||
**Prompt Engineering**: Writing and organizing LLM instructions for optimal outcomes (one-time task)
|
||||
|
||||
**Context Engineering**: Curating and maintaining the optimal set of tokens during inference across multiple turns (iterative process)
|
||||
|
||||
Context engineering manages:
|
||||
- System instructions
|
||||
- Tools
|
||||
- Model Context Protocol (MCP)
|
||||
- External data
|
||||
- Message history
|
||||
- Runtime data retrieval
|
||||
|
||||
---
|
||||
|
||||
## The Problem: Context Rot
|
||||
|
||||
**Key Insight**: LLMs have an "attention budget" that gets depleted as context grows
|
||||
|
||||
- Every token attends to every other token (n² relationships)
|
||||
- As context length increases, model accuracy decreases
|
||||
- Models have less training experience with longer sequences
|
||||
- Context must be treated as a finite resource with diminishing marginal returns
|
||||
|
||||
---
|
||||
|
||||
## System Prompts: Find the "Right Altitude"
|
||||
|
||||
### The Goldilocks Zone
|
||||
|
||||
**Too Prescriptive** ❌
|
||||
- Hardcoded if-else logic
|
||||
- Brittle and fragile
|
||||
- High maintenance complexity
|
||||
|
||||
**Too Vague** ❌
|
||||
- High-level guidance without concrete signals
|
||||
- Falsely assumes shared context
|
||||
- Lacks actionable direction
|
||||
|
||||
**Just Right** ✅
|
||||
- Specific enough to guide behavior effectively
|
||||
- Flexible enough to provide strong heuristics
|
||||
- Minimal set of information that fully outlines expected behavior
|
||||
|
||||
### Best Practices
|
||||
- Use simple, direct language
|
||||
- Organize into distinct sections (`<background_information>`, `<instructions>`, `## Tool guidance`, etc.)
|
||||
- Use XML tags or Markdown headers for structure
|
||||
- Start with minimal prompt, add based on failure modes
|
||||
- Note: Minimal ≠ short (provide sufficient information upfront)
|
||||
|
||||
---
|
||||
|
||||
## Tools: Minimal and Clear
|
||||
|
||||
### Design Principles
|
||||
- **Self-contained**: Each tool has a single, clear purpose
|
||||
- **Robust to error**: Handle edge cases gracefully
|
||||
- **Extremely clear**: Intended use is unambiguous
|
||||
- **Token-efficient**: Returns relevant information without bloat
|
||||
- **Descriptive parameters**: Unambiguous input names (e.g., `user_id` not `user`)
|
||||
|
||||
### Critical Rule
|
||||
**If a human engineer can't definitively say which tool to use in a given situation, an AI agent can't be expected to do better.**
|
||||
|
||||
### Common Failure Modes to Avoid
|
||||
- Bloated tool sets covering too much functionality
|
||||
- Tools with overlapping purposes
|
||||
- Ambiguous decision points about which tool to use
|
||||
|
||||
---
|
||||
|
||||
## Examples: Diverse, Not Exhaustive
|
||||
|
||||
**Do** ✅
|
||||
- Curate a set of diverse, canonical examples
|
||||
- Show expected behavior effectively
|
||||
- Think "pictures worth a thousand words"
|
||||
|
||||
**Don't** ❌
|
||||
- Stuff in a laundry list of edge cases
|
||||
- Try to articulate every possible rule
|
||||
- Overwhelm with exhaustive scenarios
|
||||
|
||||
---
|
||||
|
||||
## Context Retrieval Strategies
|
||||
|
||||
### Just-In-Time Context (Recommended for Agents)
|
||||
**Approach**: Maintain lightweight identifiers (file paths, queries, links) and dynamically load data at runtime
|
||||
|
||||
**Benefits**:
|
||||
- Avoids context pollution
|
||||
- Enables progressive disclosure
|
||||
- Mirrors human cognition (we don't memorize everything)
|
||||
- Leverages metadata (file names, folder structure, timestamps)
|
||||
- Agents discover context incrementally
|
||||
|
||||
**Trade-offs**:
|
||||
- Slower than pre-computed retrieval
|
||||
- Requires proper tool guidance to avoid dead-ends
|
||||
|
||||
### Pre-Inference Retrieval (Traditional RAG)
|
||||
**Approach**: Use embedding-based retrieval to surface context before inference
|
||||
|
||||
**When to Use**: Static content that won't change during interaction
|
||||
|
||||
### Hybrid Strategy (Best of Both)
|
||||
**Approach**: Retrieve some data upfront, enable autonomous exploration as needed
|
||||
|
||||
**Example**: Claude Code loads CLAUDE.md files upfront, uses glob/grep for just-in-time retrieval
|
||||
|
||||
**Rule of Thumb**: "Do the simplest thing that works"
|
||||
|
||||
---
|
||||
|
||||
## Long-Horizon Tasks: Three Techniques
|
||||
|
||||
### 1. Compaction
|
||||
**What**: Summarize conversation nearing context limit, reinitiate with summary
|
||||
|
||||
**Implementation**:
|
||||
- Pass message history to model for compression
|
||||
- Preserve critical details (architectural decisions, bugs, implementation)
|
||||
- Discard redundant outputs
|
||||
- Continue with compressed context + recently accessed files
|
||||
|
||||
**Tuning Process**:
|
||||
1. **First**: Maximize recall (capture all relevant information)
|
||||
2. **Then**: Improve precision (eliminate superfluous content)
|
||||
|
||||
**Low-Hanging Fruit**: Clear old tool calls and results
|
||||
|
||||
**Best For**: Tasks requiring extensive back-and-forth
|
||||
|
||||
### 2. Structured Note-Taking (Agentic Memory)
|
||||
**What**: Agent writes notes persisted outside context window, retrieved later
|
||||
|
||||
**Examples**:
|
||||
- To-do lists
|
||||
- NOTES.md files
|
||||
- Game state tracking (Pokémon example: tracking 1,234 steps of training)
|
||||
- Project progress logs
|
||||
|
||||
**Benefits**:
|
||||
- Persistent memory with minimal overhead
|
||||
- Maintains critical context across tool calls
|
||||
- Enables multi-hour coherent strategies
|
||||
|
||||
**Best For**: Iterative development with clear milestones
|
||||
|
||||
### 3. Sub-Agent Architectures
|
||||
**What**: Specialized sub-agents handle focused tasks with clean context windows
|
||||
|
||||
**How It Works**:
|
||||
- Main agent coordinates high-level plan
|
||||
- Sub-agents perform deep technical work
|
||||
- Sub-agents explore extensively (tens of thousands of tokens)
|
||||
- Return condensed summaries (1,000-2,000 tokens)
|
||||
|
||||
**Benefits**:
|
||||
- Clear separation of concerns
|
||||
- Parallel exploration
|
||||
- Detailed context remains isolated
|
||||
|
||||
**Best For**: Complex research and analysis tasks
|
||||
|
||||
---
|
||||
|
||||
## Quick Decision Framework
|
||||
|
||||
| Scenario | Recommended Approach |
|
||||
|----------|---------------------|
|
||||
| Static content | Pre-inference retrieval or hybrid |
|
||||
| Dynamic exploration needed | Just-in-time context |
|
||||
| Extended back-and-forth | Compaction |
|
||||
| Iterative development | Structured note-taking |
|
||||
| Complex research | Sub-agent architectures |
|
||||
| Rapid model improvement | "Do the simplest thing that works" |
|
||||
|
||||
---
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
1. **Context is finite**: Treat it as a precious resource with an attention budget
|
||||
2. **Think holistically**: Consider the entire state available to the LLM
|
||||
3. **Stay minimal**: More context isn't always better
|
||||
4. **Be iterative**: Context curation happens each time you pass to the model
|
||||
5. **Design for autonomy**: As models improve, let them act intelligently
|
||||
6. **Start simple**: Test with minimal setup, add based on failure modes
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- ❌ Cramming everything into prompts
|
||||
- ❌ Creating brittle if-else logic
|
||||
- ❌ Building bloated tool sets
|
||||
- ❌ Stuffing exhaustive edge cases as examples
|
||||
- ❌ Assuming larger context windows solve everything
|
||||
- ❌ Ignoring context pollution over long interactions
|
||||
|
||||
---
|
||||
|
||||
## Remember
|
||||
|
||||
> "Even as models continue to improve, the challenge of maintaining coherence across extended interactions will remain central to building more effective agents."
|
||||
|
||||
Context engineering will evolve, but the core principle stays the same: **optimize signal-to-noise ratio in your token budget**.
|
||||
|
||||
---
|
||||
|
||||
*Based on Anthropic's "Effective context engineering for AI agents" (September 2025)*
|
||||
@@ -0,0 +1,671 @@
|
||||
---
|
||||
title: "Development"
|
||||
description: "Build from source, run tests, and contribute to Claude-Mem"
|
||||
---
|
||||
|
||||
# Development Guide
|
||||
|
||||
## Building from Source
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js 18.0.0 or higher
|
||||
- npm (comes with Node.js)
|
||||
- Git
|
||||
|
||||
### Clone and Build
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone https://github.com/thedotmack/claude-mem.git
|
||||
cd claude-mem
|
||||
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Build all components
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Build Process
|
||||
|
||||
The build process uses esbuild to compile TypeScript:
|
||||
|
||||
1. Compiles TypeScript to JavaScript
|
||||
2. Creates standalone executables for each hook in `plugin/scripts/`
|
||||
3. Bundles MCP search server to `plugin/scripts/search-server.cjs`
|
||||
4. Bundles worker service to `plugin/scripts/worker-service.cjs`
|
||||
5. Bundles web viewer UI to `plugin/ui/viewer.html`
|
||||
|
||||
**Build Output**:
|
||||
- Hook executables: `*-hook.js` (ESM format)
|
||||
- Smart installer: `smart-install.js` (ESM format)
|
||||
- Worker service: `worker-service.cjs` (CJS format)
|
||||
- Search server: `search-server.cjs` (CJS format)
|
||||
- Viewer UI: `viewer.html` (self-contained HTML bundle)
|
||||
|
||||
### Build Scripts
|
||||
|
||||
```bash
|
||||
# Build everything
|
||||
npm run build
|
||||
|
||||
# Build only hooks
|
||||
npm run build:hooks
|
||||
|
||||
# The build script is defined in scripts/build-hooks.js
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### 1. Make Changes
|
||||
|
||||
Edit TypeScript source files in `src/`:
|
||||
|
||||
```
|
||||
src/
|
||||
├── hooks/ # Hook implementations (entry points + logic)
|
||||
├── services/ # Worker service and database
|
||||
├── servers/ # MCP search server
|
||||
├── sdk/ # Claude Agent SDK integration
|
||||
├── shared/ # Shared utilities
|
||||
├── ui/
|
||||
│ └── viewer/ # React web viewer UI components
|
||||
└── utils/ # General utilities
|
||||
```
|
||||
|
||||
### 2. Build
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 3. Test
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
npm test
|
||||
|
||||
# Test specific file
|
||||
node --test tests/session-lifecycle.test.ts
|
||||
|
||||
# Test context injection
|
||||
npm run test:context
|
||||
|
||||
# Verbose context test
|
||||
npm run test:context:verbose
|
||||
```
|
||||
|
||||
### 4. Manual Testing
|
||||
|
||||
```bash
|
||||
# Start worker manually
|
||||
npm run worker:start
|
||||
|
||||
# Check worker status
|
||||
npm run worker:status
|
||||
|
||||
# View logs
|
||||
npm run worker:logs
|
||||
|
||||
# Test hooks manually
|
||||
echo '{"session_id":"test-123","cwd":"'$(pwd)'","source":"startup"}' | node plugin/scripts/context-hook.js
|
||||
```
|
||||
|
||||
### 5. Iterate
|
||||
|
||||
Repeat steps 1-4 until your changes work as expected.
|
||||
|
||||
## Viewer UI Development
|
||||
|
||||
### Working with the React Viewer
|
||||
|
||||
The web viewer UI is a React application built into a self-contained HTML bundle.
|
||||
|
||||
**Location**: `src/ui/viewer/`
|
||||
|
||||
**Structure**:
|
||||
```
|
||||
src/ui/viewer/
|
||||
├── index.tsx # Entry point
|
||||
├── App.tsx # Main application component
|
||||
├── components/ # React components
|
||||
│ ├── Header.tsx # Header with logo and actions
|
||||
│ ├── Sidebar.tsx # Project filter sidebar
|
||||
│ ├── Feed.tsx # Main feed with infinite scroll
|
||||
│ ├── cards/ # Card components
|
||||
│ │ ├── ObservationCard.tsx
|
||||
│ │ ├── PromptCard.tsx
|
||||
│ │ ├── SummaryCard.tsx
|
||||
│ │ └── SkeletonCard.tsx
|
||||
├── hooks/ # Custom React hooks
|
||||
│ ├── useSSE.ts # Server-Sent Events connection
|
||||
│ ├── usePagination.ts # Infinite scroll pagination
|
||||
│ ├── useSettings.ts # Settings persistence
|
||||
│ └── useStats.ts # Database statistics
|
||||
├── utils/ # Utilities
|
||||
│ ├── constants.ts # Constants (API URLs, etc.)
|
||||
│ ├── formatters.ts # Date/time formatting
|
||||
│ └── merge.ts # Data merging and deduplication
|
||||
└── assets/ # Static assets (fonts, logos)
|
||||
```
|
||||
|
||||
### Building Viewer UI
|
||||
|
||||
```bash
|
||||
# Build everything including viewer
|
||||
npm run build
|
||||
|
||||
# The viewer is built to plugin/ui/viewer.html
|
||||
# It's a self-contained HTML file with inlined JS and CSS
|
||||
```
|
||||
|
||||
### Testing Viewer Changes
|
||||
|
||||
1. Make changes to React components in `src/ui/viewer/`
|
||||
2. Build: `npm run build`
|
||||
3. Sync to installed plugin: `npm run sync-marketplace`
|
||||
4. Restart worker: `npm run worker:restart`
|
||||
5. Refresh browser at http://localhost:37777
|
||||
|
||||
**Hot Reload**: Not currently supported. Full rebuild + restart required for changes.
|
||||
|
||||
### Adding New Viewer Features
|
||||
|
||||
**Example: Adding a new card type**
|
||||
|
||||
1. Create component in `src/ui/viewer/components/cards/YourCard.tsx`:
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
|
||||
export interface YourCardProps {
|
||||
// Your data structure
|
||||
}
|
||||
|
||||
export const YourCard: React.FC<YourCardProps> = ({ ... }) => {
|
||||
return (
|
||||
<div className="card">
|
||||
{/* Your UI */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
2. Import and use in `Feed.tsx`:
|
||||
|
||||
```tsx
|
||||
import { YourCard } from './cards/YourCard';
|
||||
|
||||
// In render logic:
|
||||
{item.type === 'your_type' && <YourCard {...item} />}
|
||||
```
|
||||
|
||||
3. Update types if needed in `src/ui/viewer/types.ts`
|
||||
|
||||
4. Rebuild and test
|
||||
|
||||
### Viewer UI Architecture
|
||||
|
||||
**Data Flow**:
|
||||
1. Worker service exposes HTTP + SSE endpoints
|
||||
2. React app fetches initial data via HTTP (paginated)
|
||||
3. SSE connection provides real-time updates
|
||||
4. Custom hooks handle state management and data merging
|
||||
5. Components render cards based on item type
|
||||
|
||||
**Key Patterns**:
|
||||
- **Infinite Scroll**: `usePagination` hook with Intersection Observer
|
||||
- **Real-Time Updates**: `useSSE` hook with auto-reconnection
|
||||
- **Deduplication**: `merge.ts` utilities prevent duplicate items
|
||||
- **Settings Persistence**: `useSettings` hook with localStorage
|
||||
- **Theme Support**: CSS variables with light/dark/system themes
|
||||
|
||||
## Adding New Features
|
||||
|
||||
### Adding a New Hook
|
||||
|
||||
1. Create hook implementation in `src/hooks/your-hook.ts`:
|
||||
|
||||
```typescript
|
||||
#!/usr/bin/env node
|
||||
import { readStdin } from '../shared/stdin';
|
||||
|
||||
async function main() {
|
||||
const input = await readStdin();
|
||||
|
||||
// Hook implementation
|
||||
const result = {
|
||||
hookSpecificOutput: 'Optional output'
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(result));
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
```
|
||||
|
||||
**Note**: As of v4.3.1, hooks are self-contained files. The shebang will be added automatically by esbuild during the build process.
|
||||
|
||||
2. Add to `plugin/hooks/hooks.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"YourHook": [{
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/your-hook.js",
|
||||
"timeout": 120
|
||||
}]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
4. Rebuild:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Modifying Database Schema
|
||||
|
||||
1. Add migration to `src/services/sqlite/migrations.ts`:
|
||||
|
||||
```typescript
|
||||
export const migration011: Migration = {
|
||||
version: 11,
|
||||
up: (db: Database) => {
|
||||
db.run(`
|
||||
ALTER TABLE observations ADD COLUMN new_field TEXT;
|
||||
`);
|
||||
},
|
||||
down: (db: Database) => {
|
||||
// Optional: define rollback
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
2. Update types in `src/services/sqlite/types.ts`:
|
||||
|
||||
```typescript
|
||||
export interface Observation {
|
||||
// ... existing fields
|
||||
new_field?: string;
|
||||
}
|
||||
```
|
||||
|
||||
3. Update database methods in `src/services/sqlite/SessionStore.ts`:
|
||||
|
||||
```typescript
|
||||
createObservation(obs: Observation) {
|
||||
// Include new_field in INSERT
|
||||
}
|
||||
```
|
||||
|
||||
4. Test migration:
|
||||
|
||||
```bash
|
||||
# Backup database first!
|
||||
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup
|
||||
|
||||
# Run tests
|
||||
npm test
|
||||
```
|
||||
|
||||
### Extending SDK Prompts
|
||||
|
||||
1. Modify prompts in `src/sdk/prompts.ts`:
|
||||
|
||||
```typescript
|
||||
export function buildObservationPrompt(observation: Observation): string {
|
||||
return `
|
||||
<observation>
|
||||
<!-- Add new XML structure -->
|
||||
</observation>
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
2. Update parser in `src/sdk/parser.ts`:
|
||||
|
||||
```typescript
|
||||
export function parseObservation(xml: string): ParsedObservation {
|
||||
// Parse new XML fields
|
||||
}
|
||||
```
|
||||
|
||||
3. Test:
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
### Adding MCP Search Tools
|
||||
|
||||
1. Add tool definition in `src/servers/search-server.ts`:
|
||||
|
||||
```typescript
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
if (request.params.name === 'your_new_tool') {
|
||||
// Implement tool logic
|
||||
const results = await search.yourNewSearch(params);
|
||||
return formatResults(results);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
2. Add search method in `src/services/sqlite/SessionSearch.ts`:
|
||||
|
||||
```typescript
|
||||
yourNewSearch(params: YourParams): SearchResult[] {
|
||||
// Implement FTS5 search
|
||||
}
|
||||
```
|
||||
|
||||
3. Rebuild and test:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npm test
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# All tests
|
||||
npm test
|
||||
|
||||
# Specific test file
|
||||
node --test tests/your-test.test.ts
|
||||
|
||||
# With coverage (if configured)
|
||||
npm test -- --coverage
|
||||
```
|
||||
|
||||
### Writing Tests
|
||||
|
||||
Create test files in `tests/`:
|
||||
|
||||
```typescript
|
||||
import { describe, it } from 'node:test';
|
||||
import assert from 'node:assert';
|
||||
|
||||
describe('YourFeature', () => {
|
||||
it('should do something', () => {
|
||||
// Test implementation
|
||||
assert.strictEqual(result, expected);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Test Database
|
||||
|
||||
Use a separate test database:
|
||||
|
||||
```typescript
|
||||
import { SessionStore } from '../src/services/sqlite/SessionStore';
|
||||
|
||||
const store = new SessionStore(':memory:'); // In-memory database
|
||||
```
|
||||
|
||||
## Code Style
|
||||
|
||||
### TypeScript Guidelines
|
||||
|
||||
- Use TypeScript strict mode
|
||||
- Define interfaces for all data structures
|
||||
- Use async/await for asynchronous code
|
||||
- Handle errors explicitly
|
||||
- Add JSDoc comments for public APIs
|
||||
|
||||
### Formatting
|
||||
|
||||
- Follow existing code formatting
|
||||
- Use 2-space indentation
|
||||
- Use single quotes for strings
|
||||
- Add trailing commas in objects/arrays
|
||||
|
||||
### Example
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Create a new observation in the database
|
||||
*/
|
||||
export async function createObservation(
|
||||
obs: Observation
|
||||
): Promise<number> {
|
||||
try {
|
||||
const result = await db.insert('observations', {
|
||||
session_id: obs.session_id,
|
||||
tool_name: obs.tool_name,
|
||||
// ...
|
||||
});
|
||||
return result.id;
|
||||
} catch (error) {
|
||||
logger.error('Failed to create observation', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
### Enable Debug Logging
|
||||
|
||||
```bash
|
||||
export DEBUG=claude-mem:*
|
||||
npm run worker:restart
|
||||
npm run worker:logs
|
||||
```
|
||||
|
||||
### Inspect Database
|
||||
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db
|
||||
|
||||
# View schema
|
||||
.schema observations
|
||||
|
||||
# Query data
|
||||
SELECT * FROM observations LIMIT 10;
|
||||
```
|
||||
|
||||
### Trace Observations
|
||||
|
||||
Use correlation IDs to trace observations through the pipeline:
|
||||
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db
|
||||
SELECT correlation_id, tool_name, created_at
|
||||
FROM observations
|
||||
WHERE session_id = 'YOUR_SESSION_ID'
|
||||
ORDER BY created_at;
|
||||
```
|
||||
|
||||
### Debug Hooks
|
||||
|
||||
Run hooks manually with test input:
|
||||
|
||||
```bash
|
||||
# Test context hook
|
||||
echo '{"session_id":"test-123","cwd":"'$(pwd)'","source":"startup"}' | node plugin/scripts/context-hook.js
|
||||
|
||||
# Test new hook
|
||||
echo '{"session_id":"test-123","cwd":"'$(pwd)'","prompt":"test"}' | node plugin/scripts/new-hook.js
|
||||
```
|
||||
|
||||
## Publishing
|
||||
|
||||
### NPM Publishing
|
||||
|
||||
```bash
|
||||
# Update version in package.json
|
||||
npm version patch # or minor, or major
|
||||
|
||||
# Build
|
||||
npm run build
|
||||
|
||||
# Publish to NPM
|
||||
npm run release
|
||||
```
|
||||
|
||||
The `release` script:
|
||||
1. Runs tests
|
||||
2. Builds all components
|
||||
3. Publishes to NPM registry
|
||||
|
||||
### Creating a Release
|
||||
|
||||
1. Update version in `package.json`
|
||||
2. Update `CHANGELOG.md`
|
||||
3. Commit changes
|
||||
4. Create git tag
|
||||
5. Push to GitHub
|
||||
6. Publish to NPM
|
||||
|
||||
```bash
|
||||
# Manual version bump:
|
||||
# 1. Update version in package.json
|
||||
# 2. Update version in plugin/.claude-plugin/plugin.json
|
||||
# 3. Update version at top of CLAUDE.md
|
||||
# 4. Update version badge in README.md
|
||||
# 5. Run: npm run build && npm run sync-marketplace
|
||||
|
||||
# Or use npm version command:
|
||||
npm version 4.3.2
|
||||
|
||||
# Update changelog
|
||||
# Edit CHANGELOG.md manually
|
||||
|
||||
# Commit
|
||||
git add .
|
||||
git commit -m "chore: Release v4.3.2"
|
||||
|
||||
# Tag
|
||||
git tag v4.3.2
|
||||
|
||||
# Push
|
||||
git push origin main --tags
|
||||
|
||||
# Publish to NPM
|
||||
npm run release
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
### Contribution Workflow
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
||||
3. Make your changes
|
||||
4. Write tests
|
||||
5. Update documentation
|
||||
6. Commit your changes (`git commit -m 'Add amazing feature'`)
|
||||
7. Push to the branch (`git push origin feature/amazing-feature`)
|
||||
8. Open a Pull Request
|
||||
|
||||
### Pull Request Guidelines
|
||||
|
||||
- **Clear title**: Describe what the PR does
|
||||
- **Description**: Explain why the change is needed
|
||||
- **Tests**: Include tests for new features
|
||||
- **Documentation**: Update docs as needed
|
||||
- **Changelog**: Add entry to CHANGELOG.md
|
||||
- **Commits**: Use clear, descriptive commit messages
|
||||
|
||||
### Code Review Process
|
||||
|
||||
1. Automated tests must pass
|
||||
2. Code review by maintainer
|
||||
3. Address feedback
|
||||
4. Final approval
|
||||
5. Merge to main
|
||||
|
||||
## Development Tools
|
||||
|
||||
### Recommended VSCode Extensions
|
||||
|
||||
- TypeScript
|
||||
- ESLint
|
||||
- Prettier
|
||||
- SQLite Viewer
|
||||
|
||||
### Useful Commands
|
||||
|
||||
```bash
|
||||
# Check TypeScript types
|
||||
npx tsc --noEmit
|
||||
|
||||
# Lint code (if configured)
|
||||
npm run lint
|
||||
|
||||
# Format code (if configured)
|
||||
npm run format
|
||||
|
||||
# Clean build artifacts
|
||||
rm -rf plugin/scripts/*.js plugin/scripts/*.cjs
|
||||
```
|
||||
|
||||
## Troubleshooting Development
|
||||
|
||||
### Build Fails
|
||||
|
||||
1. Clean node_modules:
|
||||
```bash
|
||||
rm -rf node_modules
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Check Node.js version:
|
||||
```bash
|
||||
node --version # Should be >= 18.0.0
|
||||
```
|
||||
|
||||
3. Check for syntax errors:
|
||||
```bash
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
### Tests Fail
|
||||
|
||||
1. Check database:
|
||||
```bash
|
||||
rm ~/.claude-mem/claude-mem.db
|
||||
npm test
|
||||
```
|
||||
|
||||
2. Check worker status:
|
||||
```bash
|
||||
npm run worker:status
|
||||
```
|
||||
|
||||
3. View logs:
|
||||
```bash
|
||||
npm run worker:logs
|
||||
```
|
||||
|
||||
### Worker Won't Start
|
||||
|
||||
1. Kill existing process:
|
||||
```bash
|
||||
pm2 delete claude-mem-worker
|
||||
```
|
||||
|
||||
2. Check port:
|
||||
```bash
|
||||
lsof -i :37777
|
||||
```
|
||||
|
||||
3. Try custom port:
|
||||
```bash
|
||||
export CLAUDE_MEM_WORKER_PORT=38000
|
||||
npm run worker:start
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Architecture Overview](architecture/overview) - Understand the system
|
||||
- [Configuration](configuration) - Customize Claude-Mem
|
||||
- [Troubleshooting](troubleshooting) - Common issues
|
||||
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"$schema": "https://mintlify.com/schema.json",
|
||||
"name": "Claude-Mem",
|
||||
"description": "Persistent memory compression system that preserves context across Claude Code sessions",
|
||||
"theme": "mint",
|
||||
"favicon": "/claude-mem-logomark.webp",
|
||||
"logo": {
|
||||
"light": "/claude-mem-logo-for-light-mode.webp",
|
||||
"dark": "/claude-mem-logo-for-dark-mode.webp",
|
||||
"href": "https://github.com/thedotmack/claude-mem"
|
||||
},
|
||||
"colors": {
|
||||
"primary": "#3B82F6",
|
||||
"light": "#EFF6FF",
|
||||
"dark": "#1E40AF"
|
||||
},
|
||||
"navbar": {
|
||||
"links": [
|
||||
{
|
||||
"label": "GitHub",
|
||||
"href": "https://github.com/thedotmack/claude-mem"
|
||||
}
|
||||
],
|
||||
"primary": {
|
||||
"type": "button",
|
||||
"label": "Install",
|
||||
"href": "https://github.com/thedotmack/claude-mem#quick-start"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"groups": [
|
||||
{
|
||||
"group": "Get Started",
|
||||
"icon": "rocket",
|
||||
"pages": [
|
||||
"introduction",
|
||||
"installation",
|
||||
"usage/getting-started",
|
||||
"usage/search-tools",
|
||||
"usage/private-tags",
|
||||
"beta-features"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Best Practices",
|
||||
"icon": "lightbulb",
|
||||
"pages": [
|
||||
"context-engineering",
|
||||
"progressive-disclosure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Configuration & Development",
|
||||
"icon": "gear",
|
||||
"pages": [
|
||||
"configuration",
|
||||
"development",
|
||||
"troubleshooting",
|
||||
"platform-integration"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Architecture",
|
||||
"icon": "diagram-project",
|
||||
"pages": [
|
||||
"architecture/overview",
|
||||
"architecture-evolution",
|
||||
"hooks-architecture",
|
||||
"architecture/hooks",
|
||||
"architecture/worker-service",
|
||||
"architecture/database",
|
||||
"architecture/search-architecture"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"github": "https://github.com/thedotmack/claude-mem"
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"header": "Resources",
|
||||
"items": [
|
||||
{
|
||||
"label": "Documentation",
|
||||
"href": "https://github.com/thedotmack/claude-mem"
|
||||
},
|
||||
{
|
||||
"label": "Issues",
|
||||
"href": "https://github.com/thedotmack/claude-mem/issues"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"header": "Legal",
|
||||
"items": [
|
||||
{
|
||||
"label": "License (AGPL-3.0)",
|
||||
"href": "https://github.com/thedotmack/claude-mem/blob/main/LICENSE"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"seo": {
|
||||
"indexing": "all",
|
||||
"metatags": {
|
||||
"og:type": "website",
|
||||
"og:site_name": "Claude-Mem Documentation",
|
||||
"og:description": "Persistent memory compression system that preserves context across Claude Code sessions"
|
||||
}
|
||||
},
|
||||
"contextual": {
|
||||
"options": [
|
||||
"copy",
|
||||
"view",
|
||||
"chatgpt",
|
||||
"claude",
|
||||
"cursor"
|
||||
]
|
||||
},
|
||||
"integrations": {
|
||||
"telemetry": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,869 @@
|
||||
# How Claude-Mem Uses Hooks: A Lifecycle-Driven Architecture
|
||||
|
||||
## Core Principle
|
||||
**Observe the main Claude Code session from the outside, process observations in the background, inject context at the right time.**
|
||||
|
||||
---
|
||||
|
||||
## The Big Picture
|
||||
|
||||
Claude-Mem is fundamentally a **hook-driven system**. Every piece of functionality happens in response to lifecycle events:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ CLAUDE CODE SESSION │
|
||||
│ (Main session - user interacting with Claude) │
|
||||
│ │
|
||||
│ SessionStart → UserPromptSubmit → Tool Use → Stop │
|
||||
│ ↓ ↓ ↓ ↓ ↓ ↓ │
|
||||
│ [3 Hooks] [Hook] [Hook] [Hook] │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓ ↓ ↓ ↓ ↓ ↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ CLAUDE-MEM SYSTEM │
|
||||
│ │
|
||||
│ Smart Context User New Obs │
|
||||
│ Install Inject Message Session Capture │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Key insight:** Claude-Mem doesn't interrupt or modify Claude Code's behavior. It observes from the outside and provides value through lifecycle hooks.
|
||||
|
||||
---
|
||||
|
||||
## Why Hooks?
|
||||
|
||||
### The Non-Invasive Requirement
|
||||
|
||||
Claude-Mem had several architectural constraints:
|
||||
|
||||
1. **Can't modify Claude Code**: It's a closed-source binary
|
||||
2. **Must be fast**: Can't slow down the main session
|
||||
3. **Must be reliable**: Can't break Claude Code if it fails
|
||||
4. **Must be portable**: Works on any project without configuration
|
||||
|
||||
**Solution:** External command hooks configured via settings.json
|
||||
|
||||
### The Hook System Advantage
|
||||
|
||||
Claude Code's hook system provides exactly what we need:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Lifecycle Events" icon="clock">
|
||||
SessionStart, UserPromptSubmit, PostToolUse, Stop
|
||||
</Card>
|
||||
|
||||
<Card title="Non-Blocking" icon="forward">
|
||||
Hooks run in parallel, don't wait for completion
|
||||
</Card>
|
||||
|
||||
<Card title="Context Injection" icon="upload">
|
||||
SessionStart and UserPromptSubmit can add context
|
||||
</Card>
|
||||
|
||||
<Card title="Tool Observation" icon="eye">
|
||||
PostToolUse sees all tool inputs and outputs
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
## The Six Hook Scripts + Pre-Hook
|
||||
|
||||
Claude-Mem uses 6 lifecycle hook scripts across 5 lifecycle events, plus 1 pre-hook script for dependency management. SessionStart runs 2 hooks in sequence (after the pre-hook script).
|
||||
|
||||
### Pre-Hook: Smart Install (Before SessionStart)
|
||||
|
||||
**Purpose:** Intelligently manage dependencies and start worker service
|
||||
|
||||
**Note:** This is NOT a lifecycle hook - it's a pre-hook script executed via command chaining before context-hook runs.
|
||||
|
||||
**When:** Claude Code starts (startup, clear, or compact)
|
||||
|
||||
**What it does:**
|
||||
1. Checks if dependencies need installation (version marker)
|
||||
2. Only runs `npm install` when necessary:
|
||||
- First-time installation
|
||||
- Version changed in package.json
|
||||
- Critical dependency missing (better-sqlite3)
|
||||
3. Provides Windows-specific error messages
|
||||
4. Starts PM2 worker service
|
||||
|
||||
**Configuration:**
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [{
|
||||
"matcher": "startup|clear|compact",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/../scripts/smart-install.js\" && node ${CLAUDE_PLUGIN_ROOT}/scripts/context-hook.js",
|
||||
"timeout": 300
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key Features:**
|
||||
- ✅ Version caching (`.install-version` file)
|
||||
- ✅ Fast when already installed (~10ms vs 2-5 seconds)
|
||||
- ✅ Cross-platform compatible
|
||||
- ✅ Helpful Windows error messages for build tools
|
||||
|
||||
**v5.0.3 Enhancement:** Smart caching eliminates redundant installs
|
||||
|
||||
**Source:** `scripts/smart-install.js`
|
||||
|
||||
---
|
||||
|
||||
### Hook 1: SessionStart - Context Injection
|
||||
|
||||
**Purpose:** Inject relevant context from previous sessions
|
||||
|
||||
**When:** Claude Code starts (runs after smart-install pre-hook)
|
||||
|
||||
**What it does:**
|
||||
1. Extracts project name from current working directory
|
||||
2. Queries SQLite for recent session summaries (last 10)
|
||||
3. Queries SQLite for recent observations (configurable, default 50)
|
||||
4. Formats as progressive disclosure index
|
||||
5. Outputs to stdout (automatically injected into context)
|
||||
|
||||
**Key decisions:**
|
||||
- ✅ Runs on startup, clear, and compact
|
||||
- ✅ 300-second timeout (allows for npm install if needed)
|
||||
- ✅ Progressive disclosure format (index, not full details)
|
||||
- ✅ Configurable observation count via `CLAUDE_MEM_CONTEXT_OBSERVATIONS`
|
||||
|
||||
**Output format:**
|
||||
```markdown
|
||||
# [claude-mem] recent context
|
||||
|
||||
**Legend:** 🎯 session-request | 🔴 gotcha | 🟡 problem-solution ...
|
||||
|
||||
### Oct 26, 2025
|
||||
|
||||
**General**
|
||||
| ID | Time | T | Title | Tokens |
|
||||
|----|------|---|-------|--------|
|
||||
| #2586 | 12:58 AM | 🔵 | Context hook file empty | ~51 |
|
||||
|
||||
*Use mem-search skill to access full details*
|
||||
```
|
||||
|
||||
**Source:** `src/hooks/context-hook.ts` → `plugin/scripts/context-hook.js`
|
||||
|
||||
---
|
||||
|
||||
### Hook 2: SessionStart - User Message
|
||||
|
||||
**Purpose:** Display helpful user messages during first-time setup
|
||||
|
||||
**When:** Claude Code starts (runs after context-hook)
|
||||
|
||||
**What it does:**
|
||||
1. Checks if dependencies are installed
|
||||
2. Shows first-time setup message if needed
|
||||
3. Displays formatted context information with colors
|
||||
4. Shows link to viewer UI (http://localhost:37777)
|
||||
5. Exits with code 3 (informational, not error)
|
||||
|
||||
**Configuration:**
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [{
|
||||
"matcher": "startup|clear|compact",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/user-message-hook.js",
|
||||
"timeout": 10
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Output Example:**
|
||||
```
|
||||
📝 Claude-Mem Context Loaded
|
||||
ℹ️ Note: This appears as stderr but is informational only
|
||||
|
||||
[Context details with colors...]
|
||||
|
||||
📺 Watch live in browser http://localhost:37777/ (New! v5.1)
|
||||
```
|
||||
|
||||
**Key Features:**
|
||||
- ✅ User-friendly first-time experience
|
||||
- ✅ Visual context display
|
||||
- ✅ Links to viewer UI
|
||||
- ✅ Non-intrusive (exit code 3)
|
||||
|
||||
**Source:** `plugin/scripts/user-message-hook.js` (minified)
|
||||
|
||||
---
|
||||
|
||||
### Hook 3: UserPromptSubmit (New Session Hook)
|
||||
|
||||
**Purpose:** Initialize session tracking when user submits a prompt
|
||||
|
||||
**When:** Before Claude processes the user's message
|
||||
|
||||
**What it does:**
|
||||
1. Reads user prompt and session ID from stdin
|
||||
2. Creates new session record in SQLite
|
||||
3. Saves raw user prompt for full-text search (v4.2.0+)
|
||||
4. Starts PM2 worker service if not running
|
||||
5. Returns immediately (non-blocking)
|
||||
|
||||
**Configuration:**
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"UserPromptSubmit": [{
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/new-hook.js"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key decisions:**
|
||||
- ✅ No matcher (runs for all prompts)
|
||||
- ✅ Creates session record immediately
|
||||
- ✅ Stores raw prompts for search (privacy note: local SQLite only)
|
||||
- ✅ Auto-starts worker service
|
||||
- ✅ Suppresses output (`suppressOutput: true`)
|
||||
|
||||
**Database operations:**
|
||||
```sql
|
||||
INSERT INTO sdk_sessions (claude_session_id, project, user_prompt, ...)
|
||||
VALUES (?, ?, ?, ...)
|
||||
|
||||
INSERT INTO user_prompts (session_id, prompt, prompt_number, ...)
|
||||
VALUES (?, ?, ?, ...)
|
||||
```
|
||||
|
||||
**Source:** `src/hooks/new-hook.ts` → `plugin/scripts/new-hook.js`
|
||||
|
||||
---
|
||||
|
||||
### Hook 4: PostToolUse (Save Observation Hook)
|
||||
|
||||
**Purpose:** Capture tool execution observations for later processing
|
||||
|
||||
**When:** Immediately after any tool completes successfully
|
||||
|
||||
**What it does:**
|
||||
1. Receives tool name, input, output from stdin
|
||||
2. Finds active session for current project
|
||||
3. Enqueues observation in observation_queue table
|
||||
4. Returns immediately (processing happens in worker)
|
||||
|
||||
**Configuration:**
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [{
|
||||
"matcher": "*",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/save-hook.js"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key decisions:**
|
||||
- ✅ Matcher: `*` (captures all tools)
|
||||
- ✅ Non-blocking (just enqueues, doesn't process)
|
||||
- ✅ Worker processes observations asynchronously
|
||||
- ✅ Parallel execution safe (each hook gets own stdin)
|
||||
|
||||
**Database operations:**
|
||||
```sql
|
||||
INSERT INTO observation_queue (session_id, tool_name, tool_input, tool_output, ...)
|
||||
VALUES (?, ?, ?, ?, ...)
|
||||
```
|
||||
|
||||
**What gets queued:**
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"tool_name": "Edit",
|
||||
"tool_input": {
|
||||
"file_path": "/path/to/file.ts",
|
||||
"old_string": "...",
|
||||
"new_string": "..."
|
||||
},
|
||||
"tool_output": {
|
||||
"success": true,
|
||||
"linesChanged": 5
|
||||
},
|
||||
"created_at_epoch": 1698765432
|
||||
}
|
||||
```
|
||||
|
||||
**Source:** `src/hooks/save-hook.ts` → `plugin/scripts/save-hook.js`
|
||||
|
||||
---
|
||||
|
||||
### Hook 5: Stop Hook (Summary Generation)
|
||||
|
||||
**Purpose:** Generate AI-powered session summaries during the session
|
||||
|
||||
**When:** When Claude stops (triggered by Stop lifecycle event)
|
||||
|
||||
**What it does:**
|
||||
1. Gathers session observations from database
|
||||
2. Sends to Claude Agent SDK for summarization
|
||||
3. Processes response and extracts structured summary
|
||||
4. Stores in session_summaries table
|
||||
|
||||
**Configuration:**
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"Stop": [{
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/summary-hook.js"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key decisions:**
|
||||
- ✅ Triggered by Stop lifecycle event
|
||||
- ✅ Multiple summaries per session (v4.2.0+)
|
||||
- ✅ Summaries are checkpoints, not endings
|
||||
- ✅ Uses Claude Agent SDK for AI compression
|
||||
|
||||
**Summary structure:**
|
||||
```xml
|
||||
<summary>
|
||||
<request>User's original request</request>
|
||||
<investigated>What was examined</investigated>
|
||||
<learned>Key discoveries</learned>
|
||||
<completed>Work finished</completed>
|
||||
<next_steps>Remaining tasks</next_steps>
|
||||
<files_read>
|
||||
<file>path/to/file1.ts</file>
|
||||
<file>path/to/file2.ts</file>
|
||||
</files_read>
|
||||
<files_modified>
|
||||
<file>path/to/file3.ts</file>
|
||||
</files_modified>
|
||||
<notes>Additional context</notes>
|
||||
</summary>
|
||||
```
|
||||
|
||||
**Source:** `src/hooks/summary-hook.ts` → `plugin/scripts/summary-hook.js`
|
||||
|
||||
---
|
||||
|
||||
### Hook 6: SessionEnd (Cleanup Hook)
|
||||
|
||||
**Purpose:** Mark sessions as completed when they end
|
||||
|
||||
**When:** Claude Code session ends (not on `/clear`)
|
||||
|
||||
**What it does:**
|
||||
1. Marks session as completed in database
|
||||
2. Allows worker to finish processing
|
||||
3. Performs graceful cleanup
|
||||
|
||||
**Configuration:**
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"SessionEnd": [{
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/cleanup-hook.js"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key decisions:**
|
||||
- ✅ Graceful completion (v4.1.0+)
|
||||
- ✅ No longer sends DELETE to workers
|
||||
- ✅ Skips cleanup on `/clear` commands
|
||||
- ✅ Preserves ongoing sessions
|
||||
|
||||
**Why graceful cleanup?**
|
||||
|
||||
**Old approach (v3):**
|
||||
```typescript
|
||||
// ❌ Aggressive cleanup
|
||||
SessionEnd → DELETE /worker/session → Worker stops immediately
|
||||
```
|
||||
|
||||
**Problems:**
|
||||
- Interrupted summary generation
|
||||
- Lost pending observations
|
||||
- Race conditions
|
||||
|
||||
**New approach (v4.1.0+):**
|
||||
```typescript
|
||||
// ✅ Graceful completion
|
||||
SessionEnd → UPDATE sessions SET completed_at = NOW()
|
||||
Worker sees completion → Finishes processing → Exits naturally
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Worker finishes important operations
|
||||
- Summaries complete successfully
|
||||
- Clean state transitions
|
||||
|
||||
**Source:** `src/hooks/cleanup-hook.ts` → `plugin/scripts/cleanup-hook.js`
|
||||
|
||||
---
|
||||
|
||||
## Hook Execution Flow
|
||||
|
||||
### Session Lifecycle
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Claude
|
||||
participant Hooks
|
||||
participant Worker
|
||||
participant DB
|
||||
|
||||
User->>Claude: Start Claude Code
|
||||
Claude->>Hooks: SessionStart hook
|
||||
Hooks->>DB: Query recent context
|
||||
DB-->>Hooks: Session summaries + observations
|
||||
Hooks-->>Claude: Inject context
|
||||
Note over Claude: Context available for session
|
||||
|
||||
User->>Claude: Submit prompt
|
||||
Claude->>Hooks: UserPromptSubmit hook
|
||||
Hooks->>DB: Create session record
|
||||
Hooks->>Worker: Start worker (if not running)
|
||||
Worker-->>DB: Ready to process
|
||||
|
||||
Claude->>Claude: Execute tools
|
||||
Claude->>Hooks: PostToolUse (multiple times)
|
||||
Hooks->>DB: Queue observations
|
||||
Note over Worker: Polls queue, processes observations
|
||||
|
||||
Worker->>Worker: AI compression
|
||||
Worker->>DB: Store compressed observations
|
||||
Worker->>Hooks: Trigger summary hook
|
||||
Hooks->>DB: Store session summary
|
||||
|
||||
User->>Claude: Finish
|
||||
Claude->>Hooks: SessionEnd hook
|
||||
Hooks->>DB: Mark session complete
|
||||
Worker->>DB: Check completion
|
||||
Worker->>Worker: Finish processing
|
||||
Worker->>Worker: Exit gracefully
|
||||
```
|
||||
|
||||
### Hook Timing
|
||||
|
||||
| Event | Timing | Blocking | Timeout | Output Handling |
|
||||
|-------|--------|----------|---------|-----------------|
|
||||
| **SessionStart (smart-install)** | Before session | No | 300s | stderr (info) |
|
||||
| **SessionStart (context)** | Before session | No | 300s | stdout → context |
|
||||
| **SessionStart (user-message)** | Before session | No | 10s | stderr (info) |
|
||||
| **UserPromptSubmit** | Before processing | No | 120s | stdout → context |
|
||||
| **PostToolUse** | After tool | No | 120s | Transcript only |
|
||||
| **Summary** | Worker triggered | No | 120s | Database |
|
||||
| **SessionEnd** | On exit | No | 120s | Log only |
|
||||
|
||||
---
|
||||
|
||||
## The Worker Service Architecture
|
||||
|
||||
### Why a Background Worker?
|
||||
|
||||
**Problem:** Hooks must be fast (< 1 second)
|
||||
|
||||
**Reality:** AI compression takes 5-30 seconds per observation
|
||||
|
||||
**Solution:** Hooks enqueue observations, worker processes async
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ HOOK (Fast) │
|
||||
│ 1. Read stdin (< 1ms) │
|
||||
│ 2. Insert into queue (< 10ms) │
|
||||
│ 3. Return success (< 20ms total) │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓ (queue)
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ WORKER (Slow) │
|
||||
│ 1. Poll queue every 1s │
|
||||
│ 2. Process observation via Claude SDK (5-30s) │
|
||||
│ 3. Parse and store results │
|
||||
│ 4. Mark observation processed │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### PM2 Process Management
|
||||
|
||||
**Technology:** PM2 (process manager for Node.js)
|
||||
|
||||
**Why PM2:**
|
||||
- Auto-restart on failure
|
||||
- Log management
|
||||
- Process monitoring
|
||||
- Cross-platform (works on macOS, Linux, Windows)
|
||||
- No systemd/launchd needed
|
||||
|
||||
**Configuration:**
|
||||
```javascript
|
||||
// ecosystem.config.cjs
|
||||
module.exports = {
|
||||
apps: [{
|
||||
name: 'claude-mem-worker',
|
||||
script: './plugin/scripts/worker-service.cjs',
|
||||
instances: 1,
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
max_memory_restart: '500M',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
CLAUDE_MEM_WORKER_PORT: 37777
|
||||
}
|
||||
}]
|
||||
};
|
||||
```
|
||||
|
||||
**Worker lifecycle:**
|
||||
```bash
|
||||
# Started by new-hook (if not running)
|
||||
pm2 start ecosystem.config.cjs
|
||||
|
||||
# Status check
|
||||
pm2 status claude-mem-worker
|
||||
|
||||
# View logs
|
||||
pm2 logs claude-mem-worker
|
||||
|
||||
# Restart
|
||||
pm2 restart claude-mem-worker
|
||||
```
|
||||
|
||||
### Worker HTTP API
|
||||
|
||||
**Technology:** Express.js REST API on port 37777
|
||||
|
||||
**Endpoints:**
|
||||
|
||||
| Endpoint | Method | Purpose |
|
||||
|----------|--------|---------|
|
||||
| `/health` | GET | Health check |
|
||||
| `/sessions` | POST | Create session |
|
||||
| `/sessions/:id` | GET | Get session status |
|
||||
| `/sessions/:id` | PATCH | Update session |
|
||||
| `/observations` | POST | Enqueue observation |
|
||||
| `/observations/:id` | GET | Get observation |
|
||||
|
||||
**Why HTTP API?**
|
||||
- Language-agnostic (hooks can be any language)
|
||||
- Easy debugging (curl commands)
|
||||
- Standard error handling
|
||||
- Proper async handling
|
||||
|
||||
---
|
||||
|
||||
## Design Patterns
|
||||
|
||||
### Pattern 1: Fire-and-Forget Hooks
|
||||
|
||||
**Principle:** Hooks should return immediately, not wait for completion
|
||||
|
||||
```typescript
|
||||
// ❌ Bad: Hook waits for processing
|
||||
export async function saveHook(stdin: HookInput) {
|
||||
const observation = parseInput(stdin);
|
||||
await processObservation(observation); // BLOCKS!
|
||||
return success();
|
||||
}
|
||||
|
||||
// ✅ Good: Hook enqueues and returns
|
||||
export async function saveHook(stdin: HookInput) {
|
||||
const observation = parseInput(stdin);
|
||||
await enqueueObservation(observation); // Fast
|
||||
return success(); // Immediate
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 2: Queue-Based Processing
|
||||
|
||||
**Principle:** Decouple capture from processing
|
||||
|
||||
```
|
||||
Hook (capture) → Queue (buffer) → Worker (process)
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Parallel hook execution safe
|
||||
- Worker failure doesn't affect hooks
|
||||
- Retry logic centralized
|
||||
- Backpressure handling
|
||||
|
||||
### Pattern 3: Graceful Degradation
|
||||
|
||||
**Principle:** Memory system failure shouldn't break Claude Code
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await captureObservation();
|
||||
} catch (error) {
|
||||
// Log error, but don't throw
|
||||
console.error('Memory capture failed:', error);
|
||||
return { continue: true, suppressOutput: true };
|
||||
}
|
||||
```
|
||||
|
||||
**Failure modes:**
|
||||
- Database locked → Skip observation, log error
|
||||
- Worker crashed → Auto-restart via PM2
|
||||
- Network issue → Retry with exponential backoff
|
||||
- Disk full → Warn user, disable memory
|
||||
|
||||
### Pattern 4: Progressive Enhancement
|
||||
|
||||
**Principle:** Core functionality works without memory, memory enhances it
|
||||
|
||||
```
|
||||
Without memory: Claude Code works normally
|
||||
With memory: Claude Code + context from past sessions
|
||||
Memory broken: Falls back to working normally
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hook Debugging
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable detailed hook execution logs:
|
||||
|
||||
```bash
|
||||
claude --debug
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
[DEBUG] Executing hooks for PostToolUse:Write
|
||||
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
|
||||
[DEBUG] Found 1 hook matchers in settings
|
||||
[DEBUG] Matched 1 hooks for query "Write"
|
||||
[DEBUG] Found 1 hook commands to execute
|
||||
[DEBUG] Executing hook command: ${CLAUDE_PLUGIN_ROOT}/scripts/save-hook.js with timeout 60000ms
|
||||
[DEBUG] Hook command completed with status 0: {"continue":true,"suppressOutput":true}
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Hook not executing">
|
||||
**Symptoms:** Hook command never runs
|
||||
|
||||
**Debugging:**
|
||||
1. Check `/hooks` menu - is hook registered?
|
||||
2. Verify matcher pattern (case-sensitive!)
|
||||
3. Test command manually: `echo '{}' | node save-hook.js`
|
||||
4. Check file permissions (executable?)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Hook times out">
|
||||
**Symptoms:** Hook execution exceeds timeout
|
||||
|
||||
**Debugging:**
|
||||
1. Check timeout setting (default 60s)
|
||||
2. Identify slow operation (database? network?)
|
||||
3. Move slow operation to worker
|
||||
4. Increase timeout if necessary
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Context not injecting">
|
||||
**Symptoms:** SessionStart hook runs but context missing
|
||||
|
||||
**Debugging:**
|
||||
1. Check stdout (must be valid JSON or plain text)
|
||||
2. Verify no stderr output (pollutes JSON)
|
||||
3. Check exit code (must be 0)
|
||||
4. Look for npm install output (v4.3.1 fix)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Observations not captured">
|
||||
**Symptoms:** PostToolUse hook runs but observations missing
|
||||
|
||||
**Debugging:**
|
||||
1. Check database: `sqlite3 ~/.claude-mem/claude-mem.db "SELECT * FROM observation_queue"`
|
||||
2. Verify session exists: `SELECT * FROM sdk_sessions`
|
||||
3. Check worker status: `pm2 status`
|
||||
4. View worker logs: `pm2 logs claude-mem-worker`
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Testing Hooks Manually
|
||||
|
||||
```bash
|
||||
# Test context hook
|
||||
echo '{
|
||||
"session_id": "test123",
|
||||
"cwd": "/Users/alex/projects/my-app",
|
||||
"hook_event_name": "SessionStart",
|
||||
"source": "startup"
|
||||
}' | node plugin/scripts/context-hook.js
|
||||
|
||||
# Test save hook
|
||||
echo '{
|
||||
"session_id": "test123",
|
||||
"tool_name": "Edit",
|
||||
"tool_input": {"file_path": "test.ts"},
|
||||
"tool_output": {"success": true}
|
||||
}' | node plugin/scripts/save-hook.js
|
||||
|
||||
# Test with actual Claude Code
|
||||
claude --debug
|
||||
/hooks # View registered hooks
|
||||
# Submit prompt and watch debug output
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Hook Execution Time
|
||||
|
||||
**Target:** < 100ms per hook
|
||||
|
||||
**Actual measurements:**
|
||||
|
||||
| Hook | Average | p95 | p99 |
|
||||
|------|---------|-----|-----|
|
||||
| SessionStart (smart-install, cached) | 10ms | 20ms | 40ms |
|
||||
| SessionStart (smart-install, first run) | 2500ms | 5000ms | 8000ms |
|
||||
| SessionStart (context) | 45ms | 120ms | 250ms |
|
||||
| SessionStart (user-message) | 5ms | 10ms | 15ms |
|
||||
| UserPromptSubmit | 12ms | 25ms | 50ms |
|
||||
| PostToolUse | 8ms | 15ms | 30ms |
|
||||
| SessionEnd | 5ms | 10ms | 20ms |
|
||||
|
||||
**Why smart-install is sometimes slow:**
|
||||
- First-time: Full npm install (2-5 seconds)
|
||||
- Cached: Version check only (~10ms)
|
||||
- Version change: Full npm install + PM2 restart
|
||||
|
||||
**Optimization (v5.0.3):**
|
||||
- Version caching with `.install-version` marker
|
||||
- Only install on version change or missing deps
|
||||
- Windows-specific error messages with build tool help
|
||||
|
||||
### Database Performance
|
||||
|
||||
**Schema optimizations:**
|
||||
- Indexes on `project`, `created_at_epoch`, `claude_session_id`
|
||||
- FTS5 virtual tables for full-text search
|
||||
- WAL mode for concurrent reads/writes
|
||||
|
||||
**Query patterns:**
|
||||
```sql
|
||||
-- Fast: Uses index on (project, created_at_epoch)
|
||||
SELECT * FROM session_summaries
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT 10
|
||||
|
||||
-- Fast: Uses index on claude_session_id
|
||||
SELECT * FROM sdk_sessions
|
||||
WHERE claude_session_id = ?
|
||||
LIMIT 1
|
||||
|
||||
-- Fast: FTS5 full-text search
|
||||
SELECT * FROM observations_fts
|
||||
WHERE observations_fts MATCH ?
|
||||
ORDER BY rank
|
||||
LIMIT 20
|
||||
```
|
||||
|
||||
### Worker Throughput
|
||||
|
||||
**Bottleneck:** Claude API latency (5-30s per observation)
|
||||
|
||||
**Mitigation:**
|
||||
- Process observations sequentially (simpler, more predictable)
|
||||
- Skip low-value observations (TodoWrite, ListMcpResourcesTool)
|
||||
- Batch summaries (generate every N observations, not every observation)
|
||||
|
||||
**Future optimization:**
|
||||
- Parallel processing (multiple workers)
|
||||
- Smart batching (combine related observations)
|
||||
- Lazy summarization (summarize only when needed)
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Hook Command Safety
|
||||
|
||||
**Risk:** Hooks execute arbitrary commands with user permissions
|
||||
|
||||
**Mitigations:**
|
||||
1. **Frozen at startup:** Hook configuration captured at start, changes require review
|
||||
2. **User review required:** `/hooks` menu shows changes, requires approval
|
||||
3. **Plugin isolation:** `${CLAUDE_PLUGIN_ROOT}` prevents path traversal
|
||||
4. **Input validation:** Hooks validate stdin schema before processing
|
||||
|
||||
### Data Privacy
|
||||
|
||||
**What gets stored:**
|
||||
- User prompts (raw text) - v4.2.0+
|
||||
- Tool inputs and outputs
|
||||
- File paths read/modified
|
||||
- Session summaries
|
||||
|
||||
**Privacy guarantees:**
|
||||
- All data stored locally in `~/.claude-mem/claude-mem.db`
|
||||
- No cloud uploads (API calls only for AI compression)
|
||||
- SQLite file permissions: user-only read/write
|
||||
- No analytics or telemetry
|
||||
|
||||
### API Key Protection
|
||||
|
||||
**Configuration:**
|
||||
- Anthropic API key in `~/.anthropic/api_key` or `ANTHROPIC_API_KEY` env var
|
||||
- Worker inherits environment from Claude Code
|
||||
- Never logged or stored in database
|
||||
|
||||
---
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
1. **Hooks are interfaces**: They define clean boundaries between systems
|
||||
2. **Non-blocking is critical**: Hooks must return fast, workers do the heavy lifting
|
||||
3. **Graceful degradation**: Memory system can fail without breaking Claude Code
|
||||
4. **Queue-based decoupling**: Capture and processing happen independently
|
||||
5. **Progressive disclosure**: Context injection uses index-first approach
|
||||
6. **Lifecycle alignment**: Each hook has a clear, single purpose
|
||||
|
||||
---
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [Claude Code Hooks Reference](https://docs.claude.com/claude-code/hooks) - Official documentation
|
||||
- [Progressive Disclosure](progressive-disclosure) - Context priming philosophy
|
||||
- [Architecture Evolution](architecture-evolution) - v3 to v4 journey
|
||||
- [Worker Service Design](architecture/worker-service) - Background processing details
|
||||
|
||||
---
|
||||
|
||||
*The hook-driven architecture enables Claude-Mem to be both powerful and invisible. Users never notice the memory system working - it just makes Claude smarter over time.*
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: "Installation"
|
||||
description: "Install Claude-Mem plugin for persistent memory across sessions"
|
||||
---
|
||||
|
||||
# Installation Guide
|
||||
|
||||
## Quick Start
|
||||
|
||||
Install Claude-Mem directly from the plugin marketplace:
|
||||
|
||||
```bash
|
||||
/plugin marketplace add thedotmack/claude-mem
|
||||
/plugin install claude-mem
|
||||
```
|
||||
|
||||
That's it! The plugin will automatically:
|
||||
- Download prebuilt binaries (no compilation needed)
|
||||
- Install all dependencies (including PM2 and SQLite binaries)
|
||||
- Configure hooks for session lifecycle management
|
||||
- Auto-start the worker service on first session
|
||||
|
||||
Start a new Claude Code session and you'll see context from previous sessions automatically loaded.
|
||||
|
||||
## System Requirements
|
||||
|
||||
- **Node.js**: 18.0.0 or higher
|
||||
- **Claude Code**: Latest version with plugin support
|
||||
- **PM2**: Process manager (bundled with plugin - no global install required)
|
||||
- **SQLite 3**: For persistent storage (bundled)
|
||||
|
||||
## Advanced Installation
|
||||
|
||||
For development or testing, you can clone and build from source:
|
||||
|
||||
### Clone and Build
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/thedotmack/claude-mem.git
|
||||
cd claude-mem
|
||||
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Build hooks and worker service
|
||||
npm run build
|
||||
|
||||
# Worker service will auto-start on first Claude Code session
|
||||
# Or manually start with:
|
||||
npm run worker:start
|
||||
|
||||
# Verify worker is running
|
||||
npm run worker:status
|
||||
```
|
||||
|
||||
### Post-Installation Verification
|
||||
|
||||
#### 1. Automatic Dependency Installation
|
||||
|
||||
Dependencies are installed automatically during plugin installation. The SessionStart hook also ensures dependencies are up-to-date on each session start (this is fast and idempotent). Works cross-platform on Windows, macOS, and Linux.
|
||||
|
||||
#### 2. Verify Plugin Installation
|
||||
|
||||
Check that hooks are configured in Claude Code:
|
||||
```bash
|
||||
cat plugin/hooks/hooks.json
|
||||
```
|
||||
|
||||
#### 3. Data Directory Location
|
||||
|
||||
v4.0.0+ stores data in `${CLAUDE_PLUGIN_ROOT}/data/`:
|
||||
- Database: `${CLAUDE_PLUGIN_ROOT}/data/claude-mem.db`
|
||||
- Worker port file: `${CLAUDE_PLUGIN_ROOT}/data/worker.port`
|
||||
- Logs: `${CLAUDE_PLUGIN_ROOT}/data/logs/`
|
||||
|
||||
For development/testing, you can override:
|
||||
```bash
|
||||
export CLAUDE_MEM_DATA_DIR=/custom/path
|
||||
```
|
||||
|
||||
#### 4. Check Worker Logs
|
||||
|
||||
```bash
|
||||
npm run worker:logs
|
||||
```
|
||||
|
||||
#### 5. Test Context Retrieval
|
||||
|
||||
```bash
|
||||
npm run test:context
|
||||
```
|
||||
|
||||
## Upgrading from v3.x
|
||||
|
||||
**BREAKING CHANGES - Please Read:**
|
||||
|
||||
v4.0.0 introduces breaking changes:
|
||||
|
||||
- **Data Location Changed**: Database moved from `~/.claude-mem/` to `${CLAUDE_PLUGIN_ROOT}/data/` (inside plugin directory)
|
||||
- **Fresh Start Required**: No automatic migration from v3.x. You must start with a clean database
|
||||
- **Worker Auto-Starts**: Worker service now starts automatically - no manual `npm run worker:start` needed
|
||||
- **MCP Search Server**: 7 new search tools with full-text search and citations
|
||||
- **Enhanced Architecture**: Improved plugin integration and data organization
|
||||
|
||||
See [CHANGELOG](https://github.com/thedotmack/claude-mem/blob/main/CHANGELOG.md) for complete details.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Getting Started Guide](usage/getting-started) - Learn how Claude-Mem works automatically
|
||||
- [MCP Search Tools](usage/search-tools) - Query your project history
|
||||
- [Configuration](configuration) - Customize Claude-Mem behavior
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: "Introduction"
|
||||
description: "Persistent memory compression system for Claude Code"
|
||||
---
|
||||
|
||||
# Claude-Mem
|
||||
|
||||
**Persistent memory compression system for Claude Code**
|
||||
|
||||
Claude-Mem seamlessly preserves context across sessions by automatically capturing tool usage observations, generating semantic summaries, and making them available to future sessions. This enables Claude to maintain continuity of knowledge about projects even after sessions end or reconnect.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Start a new Claude Code session in the terminal and enter the following commands:
|
||||
|
||||
```bash
|
||||
/plugin marketplace add thedotmack/claude-mem
|
||||
/plugin install claude-mem
|
||||
```
|
||||
|
||||
Restart Claude Code. Context from previous sessions will automatically appear in new sessions.
|
||||
|
||||
## Key Features
|
||||
|
||||
- 🧠 **Persistent Memory** - Context survives across sessions
|
||||
- 🔍 **mem-search Skill** - Query your project history with natural language (~2,250 token savings)
|
||||
- 🌐 **Web Viewer UI** - Real-time memory stream visualization at http://localhost:37777
|
||||
- 🔒 **Privacy Control** - Use `<private>` tags to exclude sensitive content from storage
|
||||
- ⚙️ **Context Configuration** - Fine-grained control over what context gets injected
|
||||
- 🤖 **Automatic Operation** - No manual intervention required
|
||||
- 📊 **FTS5 Search** - Fast full-text search across observations
|
||||
- 🔗 **Citations** - Reference past decisions with `claude-mem://` URIs
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Session Start → Inject context from last 10 sessions │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ User Prompts → Create session, save user prompts │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Tool Executions → Capture observations (Read, Write, etc.) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Worker Processes → Extract learnings via Claude Agent SDK │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Session Ends → Generate summary, ready for next session │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Core Components:**
|
||||
1. **5 Lifecycle Hooks** - SessionStart, UserPromptSubmit, PostToolUse, Stop, SessionEnd (6 hook scripts)
|
||||
2. **Smart Install** - Cached dependency checker (pre-hook script)
|
||||
3. **Worker Service** - HTTP API on port 37777 managed by PM2
|
||||
4. **SQLite Database** - Stores sessions, observations, summaries with FTS5 search
|
||||
5. **mem-search Skill** - Query historical context with natural language
|
||||
6. **Web Viewer UI** - Real-time visualization with SSE and infinite scroll
|
||||
|
||||
See [Architecture Overview](architecture/overview) for details.
|
||||
|
||||
## System Requirements
|
||||
|
||||
- **Node.js**: 18.0.0 or higher
|
||||
- **Claude Code**: Latest version with plugin support
|
||||
- **PM2**: Process manager (bundled - no global install required)
|
||||
- **SQLite 3**: For persistent storage (bundled)
|
||||
|
||||
## What's New
|
||||
|
||||
**v6.4.9 - Context Configuration Settings:**
|
||||
- 11 new settings for fine-grained control over context injection
|
||||
- Configure token economics display, observation filtering by type/concept
|
||||
|
||||
**v6.4.0 - Dual-Tag Privacy System:**
|
||||
- `<private>` tags for user-controlled privacy - wrap sensitive content to exclude from storage
|
||||
- Edge processing ensures private content never reaches database
|
||||
|
||||
**v6.3.0 - Version Channel:**
|
||||
- Switch between stable and beta versions from the web viewer UI
|
||||
|
||||
**Previous Highlights:**
|
||||
- **v6.0.0**: Major session management & transcript processing improvements
|
||||
- **v5.5.0**: mem-search skill enhancement with 100% effectiveness rate
|
||||
- **v5.4.0**: Skill-based search architecture (~2,250 tokens saved per session)
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Installation" icon="download" href="/installation">
|
||||
Quick start & advanced installation
|
||||
</Card>
|
||||
<Card title="Getting Started" icon="rocket" href="/usage/getting-started">
|
||||
Learn how Claude-Mem works automatically
|
||||
</Card>
|
||||
<Card title="Architecture" icon="sitemap" href="/architecture/overview">
|
||||
System components & data flow
|
||||
</Card>
|
||||
<Card title="Search Tools" icon="magnifying-glass" href="/usage/search-tools">
|
||||
Query your project history
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,655 @@
|
||||
# Progressive Disclosure: Claude-Mem's Context Priming Philosophy
|
||||
|
||||
## Core Principle
|
||||
**Show what exists and its retrieval cost first. Let the agent decide what to fetch based on relevance and need.**
|
||||
|
||||
---
|
||||
|
||||
## What is Progressive Disclosure?
|
||||
|
||||
Progressive disclosure is an information architecture pattern where you reveal complexity gradually rather than all at once. In the context of AI agents, it means:
|
||||
|
||||
1. **Layer 1 (Index)**: Show lightweight metadata (titles, dates, types, token counts)
|
||||
2. **Layer 2 (Details)**: Fetch full content only when needed
|
||||
3. **Layer 3 (Deep Dive)**: Read original source files if required
|
||||
|
||||
This mirrors how humans work: We scan headlines before reading articles, review table of contents before diving into chapters, and check file names before opening files.
|
||||
|
||||
---
|
||||
|
||||
## The Problem: Context Pollution
|
||||
|
||||
Traditional RAG (Retrieval-Augmented Generation) systems fetch everything upfront:
|
||||
|
||||
```
|
||||
❌ Traditional Approach:
|
||||
┌─────────────────────────────────────┐
|
||||
│ Session Start │
|
||||
│ │
|
||||
│ [15,000 tokens of past sessions] │
|
||||
│ [8,000 tokens of observations] │
|
||||
│ [12,000 tokens of file summaries] │
|
||||
│ │
|
||||
│ Total: 35,000 tokens │
|
||||
│ Relevant: ~2,000 tokens (6%) │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Problems:**
|
||||
- Wastes 94% of attention budget on irrelevant context
|
||||
- User prompt gets buried under mountain of history
|
||||
- Agent must process everything before understanding task
|
||||
- No way to know what's actually useful until after reading
|
||||
|
||||
---
|
||||
|
||||
## Claude-Mem's Solution: Progressive Disclosure
|
||||
|
||||
```
|
||||
✅ Progressive Disclosure Approach:
|
||||
┌─────────────────────────────────────┐
|
||||
│ Session Start │
|
||||
│ │
|
||||
│ Index of 50 observations: ~800 tokens│
|
||||
│ ↓ │
|
||||
│ Agent sees: "🔴 Hook timeout issue" │
|
||||
│ Agent decides: "Relevant!" │
|
||||
│ ↓ │
|
||||
│ Fetch observation #2543: ~120 tokens│
|
||||
│ │
|
||||
│ Total: 920 tokens │
|
||||
│ Relevant: 920 tokens (100%) │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Agent controls its own context consumption
|
||||
- Directly relevant to current task
|
||||
- Can fetch more if needed
|
||||
- Can skip everything if not relevant
|
||||
- Clear cost/benefit for each retrieval decision
|
||||
|
||||
---
|
||||
|
||||
## How It Works in Claude-Mem
|
||||
|
||||
### The Index Format
|
||||
|
||||
Every SessionStart hook provides a compact index:
|
||||
|
||||
```markdown
|
||||
### Oct 26, 2025
|
||||
|
||||
**General**
|
||||
| ID | Time | T | Title | Tokens |
|
||||
|----|------|---|-------|--------|
|
||||
| #2586 | 12:58 AM | 🔵 | Context hook file exists but is empty | ~51 |
|
||||
| #2587 | ″ | 🔵 | Context hook script file is empty | ~46 |
|
||||
| #2589 | ″ | 🟡 | Investigated hook debug output docs | ~105 |
|
||||
|
||||
**src/hooks/context-hook.ts**
|
||||
| ID | Time | T | Title | Tokens |
|
||||
|----|------|---|-------|--------|
|
||||
| #2591 | 1:15 AM | ⚖️ | Stderr messaging abandoned | ~155 |
|
||||
| #2592 | 1:16 AM | ⚖️ | Web UI strategy redesigned | ~193 |
|
||||
```
|
||||
|
||||
**What the agent sees:**
|
||||
- **What exists**: Observation titles give semantic meaning
|
||||
- **When it happened**: Timestamps for temporal context
|
||||
- **What type**: Icons indicate observation category
|
||||
- **Retrieval cost**: Token counts for informed decisions
|
||||
- **Where to get it**: MCP search tools referenced at bottom
|
||||
|
||||
### The Legend System
|
||||
|
||||
```
|
||||
🎯 session-request - User's original goal
|
||||
🔴 gotcha - Critical edge case or pitfall
|
||||
🟡 problem-solution - Bug fix or workaround
|
||||
🔵 how-it-works - Technical explanation
|
||||
🟢 what-changed - Code/architecture change
|
||||
🟣 discovery - Learning or insight
|
||||
🟠 why-it-exists - Design rationale
|
||||
🟤 decision - Architecture decision
|
||||
⚖️ trade-off - Deliberate compromise
|
||||
```
|
||||
|
||||
**Purpose:**
|
||||
- Visual scanning (humans and AI both benefit)
|
||||
- Semantic categorization
|
||||
- Priority signaling (🔴 gotchas are more critical)
|
||||
- Pattern recognition across sessions
|
||||
|
||||
### Progressive Disclosure Instructions
|
||||
|
||||
The index includes usage guidance:
|
||||
|
||||
```markdown
|
||||
💡 **Progressive Disclosure:** This index shows WHAT exists and retrieval COST.
|
||||
- Use MCP search tools to fetch full observation details on-demand
|
||||
- Prefer searching observations over re-reading code for past decisions
|
||||
- Critical types (🔴 gotcha, 🟤 decision, ⚖️ trade-off) often worth fetching immediately
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
- Teaches the agent the pattern
|
||||
- Suggests when to fetch (critical types)
|
||||
- Recommends search over code re-reading (efficiency)
|
||||
- Makes the system self-documenting
|
||||
|
||||
---
|
||||
|
||||
## The Philosophy: Context as Currency
|
||||
|
||||
### Mental Model: Token Budget as Money
|
||||
|
||||
Think of context window as a bank account:
|
||||
|
||||
| Approach | Metaphor | Outcome |
|
||||
|----------|----------|---------|
|
||||
| **Dump everything** | Spending your entire paycheck on groceries you might need someday | Waste, clutter, can't afford what you actually need |
|
||||
| **Fetch nothing** | Refusing to spend any money | Starvation, can't accomplish tasks |
|
||||
| **Progressive disclosure** | Check your pantry, make a shopping list, buy only what you need | Efficiency, room for unexpected needs |
|
||||
|
||||
### The Attention Budget
|
||||
|
||||
LLMs have finite attention:
|
||||
- Every token attends to every other token (n² relationships)
|
||||
- 100,000 token window ≠ 100,000 tokens of useful attention
|
||||
- Context "rot" happens as window fills
|
||||
- Later tokens get less attention than earlier ones
|
||||
|
||||
**Claude-Mem's approach:**
|
||||
- Start with ~1,000 tokens of index
|
||||
- Agent has 99,000 tokens free for task
|
||||
- Agent fetches ~200 tokens when needed
|
||||
- Final budget: ~98,000 tokens for actual work
|
||||
|
||||
### Design for Autonomy
|
||||
|
||||
> "As models improve, let them act intelligently"
|
||||
|
||||
Progressive disclosure treats the agent as an **intelligent information forager**, not a passive recipient of pre-selected context.
|
||||
|
||||
**Traditional RAG:**
|
||||
```
|
||||
System → [Decides relevance] → Agent
|
||||
↑
|
||||
Hope this helps!
|
||||
```
|
||||
|
||||
**Progressive Disclosure:**
|
||||
```
|
||||
System → [Shows index] → Agent → [Decides relevance] → [Fetches details]
|
||||
↑
|
||||
You know best!
|
||||
```
|
||||
|
||||
The agent knows:
|
||||
- The current task context
|
||||
- What information would help
|
||||
- How much budget to spend
|
||||
- When to stop searching
|
||||
|
||||
We don't.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Principles
|
||||
|
||||
### 1. Make Costs Visible
|
||||
|
||||
Every item in the index shows token count:
|
||||
|
||||
```
|
||||
| #2591 | 1:15 AM | ⚖️ | Stderr messaging abandoned | ~155 |
|
||||
^^^^
|
||||
Retrieval cost
|
||||
```
|
||||
|
||||
**Why:**
|
||||
- Agent can make informed ROI decisions
|
||||
- Small observations (~50 tokens) are "cheap" to fetch
|
||||
- Large observations (~500 tokens) require stronger justification
|
||||
- Matches how humans think about effort
|
||||
|
||||
### 2. Use Semantic Compression
|
||||
|
||||
Titles compress full observations into ~10 words:
|
||||
|
||||
**Bad title:**
|
||||
```
|
||||
Observation about a thing
|
||||
```
|
||||
|
||||
**Good title:**
|
||||
```
|
||||
🔴 Hook timeout issue: 60s default too short for npm install
|
||||
```
|
||||
|
||||
**What makes a good title:**
|
||||
- Specific: Identifies exact issue
|
||||
- Actionable: Clear what to do
|
||||
- Self-contained: Doesn't require reading observation
|
||||
- Searchable: Contains key terms (hook, timeout, npm)
|
||||
- Categorized: Icon indicates type
|
||||
|
||||
### 3. Group by Context
|
||||
|
||||
Observations are grouped by:
|
||||
- **Date**: Temporal context
|
||||
- **File path**: Spatial context (work on specific files)
|
||||
- **Project**: Logical context
|
||||
|
||||
```markdown
|
||||
**src/hooks/context-hook.ts**
|
||||
| ID | Time | T | Title | Tokens |
|
||||
|----|------|---|-------|--------|
|
||||
| #2591 | 1:15 AM | ⚖️ | Stderr messaging abandoned | ~155 |
|
||||
| #2594 | 1:17 AM | 🟠 | Removed stderr section from docs | ~93 |
|
||||
```
|
||||
|
||||
**Benefit:** If agent is working on `src/hooks/context-hook.ts`, related observations are already grouped together.
|
||||
|
||||
### 4. Provide Retrieval Tools
|
||||
|
||||
The index is useless without retrieval mechanisms:
|
||||
|
||||
```markdown
|
||||
*Use claude-mem MCP search to access records with the given ID*
|
||||
```
|
||||
|
||||
**Available tools:**
|
||||
- `search_observations` - Full-text search
|
||||
- `find_by_concept` - Concept-based retrieval
|
||||
- `find_by_file` - File-based retrieval
|
||||
- `find_by_type` - Type-based retrieval
|
||||
- `get_recent_context` - Recent session summaries
|
||||
|
||||
Each tool supports `format: "index"` (default) and `format: "full"`.
|
||||
|
||||
---
|
||||
|
||||
## Real-World Example
|
||||
|
||||
### Scenario: Agent asked to fix a bug in hooks
|
||||
|
||||
**Without progressive disclosure:**
|
||||
```
|
||||
SessionStart injects 25,000 tokens of past context
|
||||
Agent reads everything
|
||||
Agent finds 1 relevant observation (buried in middle)
|
||||
Total tokens consumed: 25,000
|
||||
Relevant tokens: ~200
|
||||
Efficiency: 0.8%
|
||||
```
|
||||
|
||||
**With progressive disclosure:**
|
||||
```
|
||||
SessionStart shows index: ~800 tokens
|
||||
Agent sees title: "🔴 Hook timeout issue: 60s too short"
|
||||
Agent thinks: "This looks relevant to my bug!"
|
||||
Agent fetches observation #2543: ~155 tokens
|
||||
Total tokens consumed: 955
|
||||
Relevant tokens: 955
|
||||
Efficiency: 100%
|
||||
```
|
||||
|
||||
### The Index Entry
|
||||
|
||||
```markdown
|
||||
| #2543 | 2:14 PM | 🔴 | Hook timeout: 60s too short for npm install | ~155 |
|
||||
```
|
||||
|
||||
**What the agent learns WITHOUT fetching:**
|
||||
- There's a known gotcha (🔴) about hook timeouts
|
||||
- It's related to npm install taking too long
|
||||
- Full details are ~155 tokens (cheap)
|
||||
- Happened at 2:14 PM (recent)
|
||||
|
||||
**Decision tree:**
|
||||
```
|
||||
Is my task related to hooks? → YES
|
||||
Is my task related to timeouts? → YES
|
||||
Is my task related to npm? → YES
|
||||
155 tokens is cheap → FETCH IT
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## The Two-Tier Search Strategy
|
||||
|
||||
Claude-Mem implements progressive disclosure in search results too:
|
||||
|
||||
### Tier 1: Index Format (Default)
|
||||
|
||||
```typescript
|
||||
search_observations({
|
||||
query: "hook timeout",
|
||||
format: "index" // Default
|
||||
})
|
||||
```
|
||||
|
||||
**Returns:**
|
||||
```
|
||||
Found 3 observations matching "hook timeout":
|
||||
|
||||
| ID | Date | Type | Title | Tokens |
|
||||
|----|------|------|-------|--------|
|
||||
| #2543 | Oct 26 | gotcha | Hook timeout: 60s too short | ~155 |
|
||||
| #2891 | Oct 25 | how-it-works | Hook timeout configuration | ~203 |
|
||||
| #2102 | Oct 20 | problem-solution | Fixed timeout in CI | ~89 |
|
||||
```
|
||||
|
||||
**Cost:** ~100 tokens for 3 results
|
||||
**Value:** Agent can scan and decide which to fetch
|
||||
|
||||
### Tier 2: Full Format (On-Demand)
|
||||
|
||||
```typescript
|
||||
search_observations({
|
||||
query: "hook timeout",
|
||||
format: "full",
|
||||
limit: 1 // Fetch just the most relevant
|
||||
})
|
||||
```
|
||||
|
||||
**Returns:**
|
||||
```
|
||||
#2543 🔴 Hook timeout: 60s too short for npm install
|
||||
─────────────────────────────────────────────────
|
||||
Date: Oct 26, 2025 2:14 PM
|
||||
Type: gotcha
|
||||
Project: claude-mem
|
||||
|
||||
Narrative:
|
||||
Discovered that the default 60-second hook timeout is insufficient
|
||||
for npm install operations, especially with large dependency trees
|
||||
or slow network conditions. This causes SessionStart hook to fail
|
||||
silently, preventing context injection.
|
||||
|
||||
Facts:
|
||||
- Default timeout: 60 seconds
|
||||
- npm install with cold cache: ~90 seconds
|
||||
- Configured timeout: 120 seconds in plugin/hooks/hooks.json:25
|
||||
|
||||
Files Modified:
|
||||
- plugin/hooks/hooks.json
|
||||
|
||||
Concepts: hooks, timeout, npm, configuration
|
||||
```
|
||||
|
||||
**Cost:** ~155 tokens for full details
|
||||
**Value:** Complete understanding of the issue
|
||||
|
||||
---
|
||||
|
||||
## Cognitive Load Theory
|
||||
|
||||
Progressive disclosure is grounded in **Cognitive Load Theory**:
|
||||
|
||||
### Intrinsic Load
|
||||
The inherent difficulty of the task itself.
|
||||
|
||||
**Example:** "Fix authentication bug"
|
||||
- Must understand auth system
|
||||
- Must understand the bug
|
||||
- Must write the fix
|
||||
|
||||
This load is unavoidable.
|
||||
|
||||
### Extraneous Load
|
||||
The cognitive burden of poorly presented information.
|
||||
|
||||
**Traditional RAG adds extraneous load:**
|
||||
- Scanning irrelevant observations
|
||||
- Filtering out noise
|
||||
- Remembering what to ignore
|
||||
- Re-contextualizing after each section
|
||||
|
||||
**Progressive disclosure minimizes extraneous load:**
|
||||
- Scan titles (low effort)
|
||||
- Fetch only relevant (targeted effort)
|
||||
- Full attention on current task
|
||||
|
||||
### Germane Load
|
||||
The effort of building mental models and schemas.
|
||||
|
||||
**Progressive disclosure supports germane load:**
|
||||
- Consistent structure (legend, grouping)
|
||||
- Clear categorization (types, icons)
|
||||
- Semantic compression (good titles)
|
||||
- Explicit costs (token counts)
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
### ❌ Verbose Titles
|
||||
|
||||
**Bad:**
|
||||
```
|
||||
| #2543 | 2:14 PM | 🔴 | Investigation into the issue where hooks time out | ~155 |
|
||||
```
|
||||
|
||||
**Good:**
|
||||
```
|
||||
| #2543 | 2:14 PM | 🔴 | Hook timeout: 60s too short for npm install | ~155 |
|
||||
```
|
||||
|
||||
### ❌ Hiding Costs
|
||||
|
||||
**Bad:**
|
||||
```
|
||||
| #2543 | 2:14 PM | 🔴 | Hook timeout issue |
|
||||
```
|
||||
|
||||
**Good:**
|
||||
```
|
||||
| #2543 | 2:14 PM | 🔴 | Hook timeout issue | ~155 |
|
||||
```
|
||||
|
||||
### ❌ No Retrieval Path
|
||||
|
||||
**Bad:**
|
||||
```
|
||||
Here are 10 observations. [No instructions on how to get full details]
|
||||
```
|
||||
|
||||
**Good:**
|
||||
```
|
||||
Here are 10 observations.
|
||||
*Use MCP search tools to fetch full observation details on-demand*
|
||||
```
|
||||
|
||||
### ❌ Defaulting to Full Format
|
||||
|
||||
**Bad:**
|
||||
```typescript
|
||||
search_observations({
|
||||
query: "hooks",
|
||||
format: "full" // Fetches everything
|
||||
})
|
||||
```
|
||||
|
||||
**Good:**
|
||||
```typescript
|
||||
search_observations({
|
||||
query: "hooks",
|
||||
format: "index", // Scan first
|
||||
limit: 20
|
||||
})
|
||||
|
||||
// Then, if needed:
|
||||
search_observations({
|
||||
query: "hooks",
|
||||
format: "full",
|
||||
limit: 1 // Just the most relevant
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
### Why Token Counts?
|
||||
|
||||
**Decision:** Show approximate token counts (~155, ~203) rather than exact counts.
|
||||
|
||||
**Rationale:**
|
||||
- Communicates scale (50 vs 500) without false precision
|
||||
- Maps to human intuition (small/medium/large)
|
||||
- Allows agent to budget attention
|
||||
- Encourages cost-conscious retrieval
|
||||
|
||||
### Why Icons Instead of Text Labels?
|
||||
|
||||
**Decision:** Use emoji icons (🔴, 🟡, 🔵) rather than text (GOTCHA, PROBLEM, HOWTO).
|
||||
|
||||
**Rationale:**
|
||||
- Visual scanning (pattern recognition)
|
||||
- Token efficient (1 char vs 10 chars)
|
||||
- Language-agnostic
|
||||
- Aesthetically distinct
|
||||
- Works for both humans and AI
|
||||
|
||||
### Why Index-First, Not Smart Pre-Fetch?
|
||||
|
||||
**Decision:** Always show index first, even if we "know" what's relevant.
|
||||
|
||||
**Rationale:**
|
||||
- We can't know what's relevant better than the agent
|
||||
- Pre-fetching assumes we understand the task
|
||||
- Agent knows current context, we don't
|
||||
- Respects agent autonomy
|
||||
- Fails gracefully (can always fetch more)
|
||||
|
||||
### Why Group by File Path?
|
||||
|
||||
**Decision:** Group observations by file path in addition to date.
|
||||
|
||||
**Rationale:**
|
||||
- Spatial locality: Work on file X likely needs context about file X
|
||||
- Reduces scanning effort
|
||||
- Matches how developers think
|
||||
- Clear semantic boundaries
|
||||
|
||||
---
|
||||
|
||||
## Measuring Success
|
||||
|
||||
Progressive disclosure is working when:
|
||||
|
||||
### ✅ Low Waste Ratio
|
||||
```
|
||||
Relevant Tokens / Total Context Tokens > 80%
|
||||
```
|
||||
|
||||
Most of the context consumed is actually useful.
|
||||
|
||||
### ✅ Selective Fetching
|
||||
```
|
||||
Index Shown: 50 observations
|
||||
Details Fetched: 2-3 observations
|
||||
```
|
||||
|
||||
Agent is being selective, not fetching everything.
|
||||
|
||||
### ✅ Fast Task Completion
|
||||
```
|
||||
Session with index: 30 seconds to find relevant context
|
||||
Session without: 90 seconds scanning all context
|
||||
```
|
||||
|
||||
Time-to-relevant-information is faster.
|
||||
|
||||
### ✅ Appropriate Depth
|
||||
```
|
||||
Simple task: Only index needed
|
||||
Medium task: 1-2 observations fetched
|
||||
Complex task: 5-10 observations + code reads
|
||||
```
|
||||
|
||||
Depth scales with task complexity.
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Adaptive Index Size
|
||||
|
||||
```typescript
|
||||
// Vary index size based on session type
|
||||
SessionStart({ source: "startup" }):
|
||||
→ Show last 10 sessions (small index)
|
||||
|
||||
SessionStart({ source: "resume" }):
|
||||
→ Show only current session (micro index)
|
||||
|
||||
SessionStart({ source: "compact" }):
|
||||
→ Show last 20 sessions (larger index)
|
||||
```
|
||||
|
||||
### Relevance Scoring
|
||||
|
||||
```typescript
|
||||
// Use embeddings to pre-sort index by relevance
|
||||
search_observations({
|
||||
query: "authentication bug",
|
||||
format: "index",
|
||||
sort: "relevance" // Based on semantic similarity
|
||||
})
|
||||
```
|
||||
|
||||
### Cost Forecasting
|
||||
|
||||
```markdown
|
||||
💡 **Budget Estimate:**
|
||||
- Fetching all 🔴 gotchas: ~450 tokens
|
||||
- Fetching all file-related: ~1,200 tokens
|
||||
- Fetching everything: ~8,500 tokens
|
||||
```
|
||||
|
||||
### Progressive Detail Levels
|
||||
|
||||
```
|
||||
Layer 1: Index (titles only)
|
||||
Layer 2: Summaries (2-3 sentences)
|
||||
Layer 3: Full details (complete observation)
|
||||
Layer 4: Source files (referenced code)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
1. **Show, don't tell**: Index reveals what exists without forcing consumption
|
||||
2. **Cost-conscious**: Make retrieval costs visible for informed decisions
|
||||
3. **Agent autonomy**: Let the agent decide what's relevant
|
||||
4. **Semantic compression**: Good titles make or break the system
|
||||
5. **Consistent structure**: Patterns reduce cognitive load
|
||||
6. **Two-tier everything**: Index first, details on-demand
|
||||
7. **Context as currency**: Spend wisely on high-value information
|
||||
|
||||
---
|
||||
|
||||
## Remember
|
||||
|
||||
> "The best interface is one that disappears when not needed, and appears exactly when it is."
|
||||
|
||||
Progressive disclosure respects the agent's intelligence and autonomy. We provide the map; the agent chooses the path.
|
||||
|
||||
---
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [Context Engineering for AI Agents](context-engineering) - Foundational principles
|
||||
- [Claude-Mem Architecture](architecture/overview) - How it all fits together
|
||||
- Cognitive Load Theory (Sweller, 1988)
|
||||
- Information Foraging Theory (Pirolli & Card, 1999)
|
||||
- Progressive Disclosure (Nielsen Norman Group)
|
||||
|
||||
---
|
||||
|
||||
*This philosophy emerged from real-world usage of Claude-Mem across hundreds of coding sessions. The pattern works because it aligns with both human cognition and LLM attention mechanics.*
|
||||
@@ -0,0 +1,861 @@
|
||||
---
|
||||
title: "Troubleshooting"
|
||||
description: "Common issues and solutions for Claude-Mem"
|
||||
---
|
||||
|
||||
# Troubleshooting Guide
|
||||
|
||||
## Quick Diagnostic Tool
|
||||
|
||||
Describe any issues you're experiencing to Claude, and the troubleshoot skill will automatically activate to provide diagnosis and fixes.
|
||||
|
||||
The troubleshoot skill will:
|
||||
- ✅ Check PM2 worker status and health
|
||||
- ✅ Verify database existence and integrity
|
||||
- ✅ Test worker service connectivity
|
||||
- ✅ Validate dependencies installation
|
||||
- ✅ Check port configuration and availability
|
||||
- ✅ Provide automated fixes for common issues
|
||||
|
||||
The skill includes comprehensive diagnostics, automated repair sequences, and detailed troubleshooting workflows for all common issues. Simply describe the problem naturally to invoke it.
|
||||
|
||||
---
|
||||
|
||||
## v5.x Specific Issues
|
||||
|
||||
### Viewer UI Not Loading
|
||||
|
||||
**Symptoms**: Cannot access http://localhost:37777, page doesn't load, or shows connection error.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check if worker is running on port 37777:
|
||||
```bash
|
||||
lsof -i :37777
|
||||
# or
|
||||
npm run worker:status
|
||||
```
|
||||
|
||||
2. Verify worker is healthy:
|
||||
```bash
|
||||
curl http://localhost:37777/health
|
||||
```
|
||||
|
||||
3. Check worker logs for errors:
|
||||
```bash
|
||||
npm run worker:logs
|
||||
```
|
||||
|
||||
4. Restart worker service:
|
||||
```bash
|
||||
npm run worker:restart
|
||||
```
|
||||
|
||||
5. Check for port conflicts:
|
||||
```bash
|
||||
# If port 37777 is in use by another service
|
||||
export CLAUDE_MEM_WORKER_PORT=38000
|
||||
npm run worker:restart
|
||||
```
|
||||
|
||||
### Theme Toggle Not Persisting
|
||||
|
||||
**Symptoms**: Theme preference (light/dark mode) resets after browser refresh.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check browser localStorage is enabled:
|
||||
```javascript
|
||||
// In browser console
|
||||
localStorage.getItem('claude-mem-settings')
|
||||
```
|
||||
|
||||
2. Verify settings endpoint is working:
|
||||
```bash
|
||||
curl http://localhost:37777/api/settings
|
||||
```
|
||||
|
||||
3. Clear localStorage and try again:
|
||||
```javascript
|
||||
// In browser console
|
||||
localStorage.removeItem('claude-mem-settings')
|
||||
```
|
||||
|
||||
4. Check for browser privacy mode (blocks localStorage)
|
||||
|
||||
### SSE Connection Issues
|
||||
|
||||
**Symptoms**: Viewer shows "Disconnected" status, updates not appearing in real-time.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check SSE endpoint is accessible:
|
||||
```bash
|
||||
curl -N http://localhost:37777/stream
|
||||
```
|
||||
|
||||
2. Check browser console for errors:
|
||||
- Open DevTools (F12)
|
||||
- Look for EventSource errors
|
||||
- Check Network tab for failed /stream requests
|
||||
|
||||
3. Verify worker is running:
|
||||
```bash
|
||||
npm run worker:status
|
||||
```
|
||||
|
||||
4. Check for network/proxy issues blocking SSE
|
||||
- Corporate firewalls may block SSE
|
||||
- Try disabling VPN temporarily
|
||||
|
||||
5. Restart worker and refresh browser:
|
||||
```bash
|
||||
npm run worker:restart
|
||||
```
|
||||
|
||||
### Chroma/Python Dependency Issues (v5.0.0+)
|
||||
|
||||
**Symptoms**: Installation fails with chromadb or Python-related errors.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Verify Python 3.8+ is installed:
|
||||
```bash
|
||||
python --version
|
||||
# or
|
||||
python3 --version
|
||||
```
|
||||
|
||||
2. Install chromadb manually:
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack
|
||||
npm install chromadb
|
||||
```
|
||||
|
||||
3. Check chromadb health:
|
||||
```bash
|
||||
npm run chroma:health
|
||||
```
|
||||
|
||||
4. Windows-specific: Ensure Python is in PATH:
|
||||
```bash
|
||||
where python
|
||||
# Should show Python installation path
|
||||
```
|
||||
|
||||
5. If Chroma continues to fail, hybrid search will gracefully degrade to SQLite FTS5 only
|
||||
|
||||
### Smart Install Caching Issues (v5.0.3+)
|
||||
|
||||
**Symptoms**: Dependencies not updating after plugin update, stale version marker.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Clear install cache:
|
||||
```bash
|
||||
rm ~/.claude/plugins/marketplaces/thedotmack/.install-version
|
||||
```
|
||||
|
||||
2. Force reinstall:
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack
|
||||
npm install --force
|
||||
```
|
||||
|
||||
3. Check version marker:
|
||||
```bash
|
||||
cat ~/.claude/plugins/marketplaces/thedotmack/.install-version
|
||||
cat ~/.claude/plugins/marketplaces/thedotmack/package.json | grep version
|
||||
```
|
||||
|
||||
4. Restart Claude Code after manual install
|
||||
|
||||
### PM2 ENOENT Error on Windows (v5.1.1 Fix)
|
||||
|
||||
**Symptoms**: Worker fails to start with "ENOENT" error on Windows.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. This was fixed in v5.1.1 - update to latest version:
|
||||
```bash
|
||||
/plugin update claude-mem
|
||||
```
|
||||
|
||||
2. If still experiencing issues, verify PM2 path:
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack
|
||||
dir node_modules\.bin\pm2.cmd
|
||||
```
|
||||
|
||||
3. Manual PM2 install if needed:
|
||||
```bash
|
||||
npm install pm2@latest
|
||||
```
|
||||
|
||||
## Worker Service Issues
|
||||
|
||||
### Worker Service Not Starting
|
||||
|
||||
**Symptoms**: Worker doesn't start, or `pm2 status` shows no processes.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check if PM2 is running:
|
||||
```bash
|
||||
pm2 status
|
||||
```
|
||||
|
||||
2. Try starting manually:
|
||||
```bash
|
||||
npm run worker:start
|
||||
```
|
||||
|
||||
3. Check worker logs for errors:
|
||||
```bash
|
||||
npm run worker:logs
|
||||
```
|
||||
|
||||
4. Full reset:
|
||||
```bash
|
||||
pm2 delete claude-mem-worker
|
||||
npm run worker:start
|
||||
```
|
||||
|
||||
5. Verify PM2 is installed:
|
||||
```bash
|
||||
which pm2
|
||||
npm list pm2
|
||||
```
|
||||
|
||||
### Port Allocation Failed
|
||||
|
||||
**Symptoms**: Worker fails to start with "port already in use" error.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check if port 37777 is in use:
|
||||
```bash
|
||||
lsof -i :37777
|
||||
```
|
||||
|
||||
2. Kill process using the port:
|
||||
```bash
|
||||
kill -9 $(lsof -t -i:37777)
|
||||
```
|
||||
|
||||
3. Or use a different port:
|
||||
```bash
|
||||
export CLAUDE_MEM_WORKER_PORT=38000
|
||||
npm run worker:restart
|
||||
```
|
||||
|
||||
4. Verify new port:
|
||||
```bash
|
||||
cat ~/.claude-mem/worker.port
|
||||
```
|
||||
|
||||
### Worker Keeps Crashing
|
||||
|
||||
**Symptoms**: Worker restarts repeatedly, PM2 shows high restart count.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check error logs:
|
||||
```bash
|
||||
npm run worker:logs
|
||||
```
|
||||
|
||||
2. Check memory usage:
|
||||
```bash
|
||||
pm2 status
|
||||
```
|
||||
|
||||
3. Increase memory limit in `ecosystem.config.cjs`:
|
||||
```javascript
|
||||
{
|
||||
max_memory_restart: '2G' // Increase if needed
|
||||
}
|
||||
```
|
||||
|
||||
4. Check database for corruption:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;"
|
||||
```
|
||||
|
||||
### Worker Not Processing Observations
|
||||
|
||||
**Symptoms**: Observations saved but not processed, no summaries generated.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check worker is running:
|
||||
```bash
|
||||
npm run worker:status
|
||||
```
|
||||
|
||||
2. Check worker logs:
|
||||
```bash
|
||||
npm run worker:logs
|
||||
```
|
||||
|
||||
3. Verify database has observations:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations;"
|
||||
```
|
||||
|
||||
4. Restart worker:
|
||||
```bash
|
||||
npm run worker:restart
|
||||
```
|
||||
|
||||
## Hook Issues
|
||||
|
||||
### Hooks Not Firing
|
||||
|
||||
**Symptoms**: No context appears, observations not saved.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Verify hooks are configured:
|
||||
```bash
|
||||
cat plugin/hooks/hooks.json
|
||||
```
|
||||
|
||||
2. Test hooks manually:
|
||||
```bash
|
||||
# Test context hook
|
||||
echo '{"session_id":"test-123","cwd":"'$(pwd)'","source":"startup"}' | node plugin/scripts/context-hook.js
|
||||
```
|
||||
|
||||
3. Check hook permissions:
|
||||
```bash
|
||||
ls -la plugin/scripts/*.js
|
||||
```
|
||||
|
||||
4. Verify hooks.json is valid JSON:
|
||||
```bash
|
||||
cat plugin/hooks/hooks.json | jq .
|
||||
```
|
||||
|
||||
### Context Not Appearing
|
||||
|
||||
**Symptoms**: No session context when Claude starts.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check if summaries exist:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM session_summaries;"
|
||||
```
|
||||
|
||||
2. View recent sessions:
|
||||
```bash
|
||||
npm run test:context:verbose
|
||||
```
|
||||
|
||||
3. Check database integrity:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;"
|
||||
```
|
||||
|
||||
4. Manually test context hook:
|
||||
```bash
|
||||
npm run test:context
|
||||
```
|
||||
|
||||
### Hooks Timeout
|
||||
|
||||
**Symptoms**: Hook execution times out, errors in Claude Code.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Increase timeout in `plugin/hooks/hooks.json`:
|
||||
```json
|
||||
{
|
||||
"timeout": 180 // Increase from 120
|
||||
}
|
||||
```
|
||||
|
||||
2. Check worker is running (prevents timeout waiting for worker):
|
||||
```bash
|
||||
npm run worker:status
|
||||
```
|
||||
|
||||
3. Check database size (large database = slow queries):
|
||||
```bash
|
||||
ls -lh ~/.claude-mem/claude-mem.db
|
||||
```
|
||||
|
||||
4. Optimize database:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "VACUUM;"
|
||||
```
|
||||
|
||||
### Dependencies Not Installing
|
||||
|
||||
**Symptoms**: SessionStart hook fails with "module not found" errors.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Manually install dependencies:
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Check npm is available:
|
||||
```bash
|
||||
which npm
|
||||
npm --version
|
||||
```
|
||||
|
||||
3. Check package.json exists:
|
||||
```bash
|
||||
ls -la ~/.claude/plugins/marketplaces/thedotmack/package.json
|
||||
```
|
||||
|
||||
## Database Issues
|
||||
|
||||
### Database Locked
|
||||
|
||||
**Symptoms**: "database is locked" errors in logs.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Close all connections:
|
||||
```bash
|
||||
pm2 stop claude-mem-worker
|
||||
```
|
||||
|
||||
2. Check for stale locks:
|
||||
```bash
|
||||
lsof ~/.claude-mem/claude-mem.db
|
||||
```
|
||||
|
||||
3. Kill processes holding locks:
|
||||
```bash
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
4. Restart worker:
|
||||
```bash
|
||||
npm run worker:start
|
||||
```
|
||||
|
||||
### Database Corruption
|
||||
|
||||
**Symptoms**: Integrity check fails, weird errors.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check database integrity:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;"
|
||||
```
|
||||
|
||||
2. Backup database:
|
||||
```bash
|
||||
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup
|
||||
```
|
||||
|
||||
3. Try to repair:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "VACUUM;"
|
||||
```
|
||||
|
||||
4. Nuclear option - recreate database:
|
||||
```bash
|
||||
rm ~/.claude-mem/claude-mem.db
|
||||
npm run worker:start # Will recreate schema
|
||||
```
|
||||
|
||||
### FTS5 Search Not Working
|
||||
|
||||
**Symptoms**: Search returns no results, FTS5 errors.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check FTS5 tables exist:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%_fts';"
|
||||
```
|
||||
|
||||
2. Rebuild FTS5 tables:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
|
||||
INSERT INTO session_summaries_fts(session_summaries_fts) VALUES('rebuild');
|
||||
INSERT INTO user_prompts_fts(user_prompts_fts) VALUES('rebuild');
|
||||
"
|
||||
```
|
||||
|
||||
3. Check triggers exist:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT name FROM sqlite_master WHERE type='trigger';"
|
||||
```
|
||||
|
||||
### Database Too Large
|
||||
|
||||
**Symptoms**: Slow performance, large database file.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check database size:
|
||||
```bash
|
||||
ls -lh ~/.claude-mem/claude-mem.db
|
||||
```
|
||||
|
||||
2. Vacuum database:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "VACUUM;"
|
||||
```
|
||||
|
||||
3. Delete old sessions:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
DELETE FROM observations WHERE created_at_epoch < $(date -v-30d +%s);
|
||||
DELETE FROM session_summaries WHERE created_at_epoch < $(date -v-30d +%s);
|
||||
DELETE FROM sdk_sessions WHERE created_at_epoch < $(date -v-30d +%s);
|
||||
"
|
||||
```
|
||||
|
||||
4. Rebuild FTS5 after deletion:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
|
||||
INSERT INTO session_summaries_fts(session_summaries_fts) VALUES('rebuild');
|
||||
"
|
||||
```
|
||||
|
||||
## MCP Search Issues
|
||||
|
||||
### Search Tools Not Available
|
||||
|
||||
**Symptoms**: MCP search tools not visible in Claude Code.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check MCP configuration:
|
||||
```bash
|
||||
cat plugin/.mcp.json
|
||||
```
|
||||
|
||||
2. Verify search server is built:
|
||||
```bash
|
||||
ls -l plugin/scripts/search-server.js
|
||||
```
|
||||
|
||||
3. Rebuild if needed:
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
4. Restart Claude Code
|
||||
|
||||
### Search Returns No Results
|
||||
|
||||
**Symptoms**: Valid queries return empty results.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check database has data:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations;"
|
||||
```
|
||||
|
||||
2. Verify FTS5 tables populated:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations_fts;"
|
||||
```
|
||||
|
||||
3. Test simple query:
|
||||
```bash
|
||||
# In Claude Code
|
||||
search_observations with query="test"
|
||||
```
|
||||
|
||||
4. Check query syntax:
|
||||
```bash
|
||||
# Bad: Special characters
|
||||
search_observations with query="[test]"
|
||||
|
||||
# Good: Simple words
|
||||
search_observations with query="test"
|
||||
```
|
||||
|
||||
### Token Limit Errors
|
||||
|
||||
**Symptoms**: "exceeded token limit" errors from MCP.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Use index format:
|
||||
```bash
|
||||
search_observations with query="..." and format="index"
|
||||
```
|
||||
|
||||
2. Reduce limit:
|
||||
```bash
|
||||
search_observations with query="..." and limit=3
|
||||
```
|
||||
|
||||
3. Use filters to narrow results:
|
||||
```bash
|
||||
search_observations with query="..." and type="decision" and limit=5
|
||||
```
|
||||
|
||||
4. Paginate results:
|
||||
```bash
|
||||
# First page
|
||||
search_observations with query="..." and limit=5 and offset=0
|
||||
|
||||
# Second page
|
||||
search_observations with query="..." and limit=5 and offset=5
|
||||
```
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### Slow Context Injection
|
||||
|
||||
**Symptoms**: SessionStart hook takes too long.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Reduce context sessions:
|
||||
```typescript
|
||||
// In src/hooks/context.ts
|
||||
const CONTEXT_SESSIONS = 5; // Reduce from 10
|
||||
```
|
||||
|
||||
2. Optimize database:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
ANALYZE;
|
||||
VACUUM;
|
||||
"
|
||||
```
|
||||
|
||||
3. Add indexes (if missing):
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_project_created ON sdk_sessions(project, created_at_epoch DESC);
|
||||
"
|
||||
```
|
||||
|
||||
### Slow Search Queries
|
||||
|
||||
**Symptoms**: MCP search tools take too long.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Use more specific queries
|
||||
2. Add date range filters
|
||||
3. Add type/concept filters
|
||||
4. Reduce result limit
|
||||
5. Use index format instead of full format
|
||||
|
||||
### High Memory Usage
|
||||
|
||||
**Symptoms**: Worker uses too much memory, frequent restarts.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check current usage:
|
||||
```bash
|
||||
pm2 status
|
||||
```
|
||||
|
||||
2. Increase memory limit:
|
||||
```javascript
|
||||
// In ecosystem.config.cjs
|
||||
{
|
||||
max_memory_restart: '2G'
|
||||
}
|
||||
```
|
||||
|
||||
3. Restart worker:
|
||||
```bash
|
||||
npm run worker:restart
|
||||
```
|
||||
|
||||
4. Clean up old data (see "Database Too Large" above)
|
||||
|
||||
## Installation Issues
|
||||
|
||||
### Plugin Not Found
|
||||
|
||||
**Symptoms**: `/plugin install claude-mem` fails.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Add marketplace first:
|
||||
```bash
|
||||
/plugin marketplace add thedotmack/claude-mem
|
||||
```
|
||||
|
||||
2. Then install:
|
||||
```bash
|
||||
/plugin install claude-mem
|
||||
```
|
||||
|
||||
3. Verify installation:
|
||||
```bash
|
||||
ls -la ~/.claude/plugins/marketplaces/thedotmack/
|
||||
```
|
||||
|
||||
### Build Failures
|
||||
|
||||
**Symptoms**: `npm run build` fails.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Clean and reinstall:
|
||||
```bash
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Check Node.js version:
|
||||
```bash
|
||||
node --version # Should be >= 18.0.0
|
||||
```
|
||||
|
||||
3. Check for TypeScript errors:
|
||||
```bash
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
### Missing Dependencies
|
||||
|
||||
**Symptoms**: "Cannot find module" errors.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Install dependencies:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Check package.json:
|
||||
```bash
|
||||
cat package.json
|
||||
```
|
||||
|
||||
3. Verify node_modules exists:
|
||||
```bash
|
||||
ls -la node_modules/
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
### Enable Verbose Logging
|
||||
|
||||
```bash
|
||||
export DEBUG=claude-mem:*
|
||||
npm run worker:restart
|
||||
npm run worker:logs
|
||||
```
|
||||
|
||||
### Check Correlation IDs
|
||||
|
||||
Trace observations through the pipeline:
|
||||
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
SELECT correlation_id, tool_name, created_at
|
||||
FROM observations
|
||||
WHERE session_id = 'YOUR_SESSION_ID'
|
||||
ORDER BY created_at;
|
||||
"
|
||||
```
|
||||
|
||||
### Inspect Worker State
|
||||
|
||||
```bash
|
||||
# Check if worker is running
|
||||
pm2 status
|
||||
|
||||
# View logs
|
||||
pm2 logs claude-mem-worker
|
||||
|
||||
# Check port file
|
||||
cat ~/.claude-mem/worker.port
|
||||
|
||||
# Test worker health
|
||||
curl http://localhost:37777/health
|
||||
```
|
||||
|
||||
### Database Inspection
|
||||
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db
|
||||
|
||||
# View schema
|
||||
.schema
|
||||
|
||||
# Check table counts
|
||||
SELECT 'sessions', COUNT(*) FROM sdk_sessions
|
||||
UNION ALL
|
||||
SELECT 'observations', COUNT(*) FROM observations
|
||||
UNION ALL
|
||||
SELECT 'summaries', COUNT(*) FROM session_summaries
|
||||
UNION ALL
|
||||
SELECT 'prompts', COUNT(*) FROM user_prompts;
|
||||
|
||||
# View recent activity
|
||||
SELECT created_at, tool_name FROM observations ORDER BY created_at DESC LIMIT 10;
|
||||
```
|
||||
|
||||
## Common Error Messages
|
||||
|
||||
### "Worker service not responding"
|
||||
|
||||
**Cause**: Worker not running or port mismatch.
|
||||
|
||||
**Solution**: Restart worker with `npm run worker:restart`.
|
||||
|
||||
### "Database is locked"
|
||||
|
||||
**Cause**: Multiple processes accessing database.
|
||||
|
||||
**Solution**: Stop worker, kill stale processes, restart.
|
||||
|
||||
### "FTS5: syntax error"
|
||||
|
||||
**Cause**: Invalid search query syntax.
|
||||
|
||||
**Solution**: Use simpler query, avoid special characters.
|
||||
|
||||
### "SQLITE_CANTOPEN"
|
||||
|
||||
**Cause**: Database file permissions or missing directory.
|
||||
|
||||
**Solution**: Check `~/.claude-mem/` exists and is writable.
|
||||
|
||||
### "Module not found"
|
||||
|
||||
**Cause**: Missing dependencies.
|
||||
|
||||
**Solution**: Run `npm install`.
|
||||
|
||||
## Getting Help
|
||||
|
||||
If none of these solutions work:
|
||||
|
||||
1. **Check logs**:
|
||||
```bash
|
||||
npm run worker:logs
|
||||
```
|
||||
|
||||
2. **Create issue**: [GitHub Issues](https://github.com/thedotmack/claude-mem/issues)
|
||||
- Include error messages
|
||||
- Include relevant logs
|
||||
- Include steps to reproduce
|
||||
|
||||
3. **Check existing issues**: Someone may have already solved your problem
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Configuration](configuration) - Customize Claude-Mem
|
||||
- [Development](development) - Build from source
|
||||
- [Architecture](architecture/overview) - Understand the system
|
||||
@@ -0,0 +1,215 @@
|
||||
---
|
||||
title: "Getting Started"
|
||||
description: "Learn how Claude-Mem works automatically in the background"
|
||||
---
|
||||
|
||||
# Getting Started with Claude-Mem
|
||||
|
||||
## Automatic Operation
|
||||
|
||||
Claude-Mem works automatically once installed. No manual intervention required!
|
||||
|
||||
### The Full Cycle
|
||||
|
||||
1. **Start Claude Code** - Context from last 10 sessions appears automatically
|
||||
2. **Work normally** - Every tool execution is captured
|
||||
3. **Claude finishes responding** - Stop hook automatically generates and saves a summary
|
||||
4. **Next session** - Previous work appears in context
|
||||
|
||||
### What Gets Captured
|
||||
|
||||
Every time Claude uses a tool, claude-mem captures it:
|
||||
|
||||
- **Read** - File reads and content access
|
||||
- **Write** - New file creation
|
||||
- **Edit** - File modifications
|
||||
- **Bash** - Command executions
|
||||
- **Glob** - File pattern searches
|
||||
- **Grep** - Content searches
|
||||
- And all other Claude Code tools
|
||||
|
||||
### What Gets Processed
|
||||
|
||||
The worker service processes tool observations and extracts:
|
||||
|
||||
- **Title** - Brief description of what happened
|
||||
- **Subtitle** - Additional context
|
||||
- **Narrative** - Detailed explanation
|
||||
- **Facts** - Key learnings as bullet points
|
||||
- **Concepts** - Relevant tags and categories
|
||||
- **Type** - Classification (decision, bugfix, feature, etc.)
|
||||
- **Files** - Which files were read or modified
|
||||
|
||||
### Session Summaries
|
||||
|
||||
When Claude finishes responding (triggering the Stop hook), a summary is automatically generated with:
|
||||
|
||||
- **Request** - What you asked for
|
||||
- **Investigated** - What Claude explored
|
||||
- **Learned** - Key discoveries and insights
|
||||
- **Completed** - What was accomplished
|
||||
- **Next Steps** - What to do next
|
||||
|
||||
### Context Injection
|
||||
|
||||
When you start a new Claude Code session, the SessionStart hook:
|
||||
|
||||
1. Queries the database for recent observations in your project (default: 50)
|
||||
2. Retrieves recent session summaries for context
|
||||
3. Displays observations in a chronological timeline with session markers
|
||||
4. Shows full summary details (Investigated, Learned, Completed, Next Steps) **only if the summary was generated after the last observation**
|
||||
5. Injects formatted context into Claude's initial context
|
||||
|
||||
**Summary Display Logic:**
|
||||
|
||||
The most recent summary's full details appear at the end of the context display **only when** the summary was generated after the most recent observation. This ensures you see summary details when they represent the latest state of your project, but not when new observations have been captured since the last summary.
|
||||
|
||||
For example:
|
||||
- ✅ **Shows summary**: Last observation at 2:00 PM, summary generated at 2:05 PM → Summary details appear
|
||||
- ❌ **Hides summary**: Summary generated at 2:00 PM, new observation at 2:05 PM → Summary details hidden (outdated)
|
||||
|
||||
This prevents showing stale summaries when new work has been captured but not yet summarized.
|
||||
|
||||
This means Claude "remembers" what happened in previous sessions!
|
||||
|
||||
## Manual Commands (Optional)
|
||||
|
||||
### Worker Management
|
||||
|
||||
v4.0+ auto-starts the worker on first session. Manual commands below are optional.
|
||||
|
||||
```bash
|
||||
# Start worker service (optional - auto-starts automatically)
|
||||
npm run worker:start
|
||||
|
||||
# Stop worker service
|
||||
npm run worker:stop
|
||||
|
||||
# Restart worker service
|
||||
npm run worker:restart
|
||||
|
||||
# View worker logs
|
||||
npm run worker:logs
|
||||
|
||||
# Check worker status
|
||||
npm run worker:status
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
npm test
|
||||
|
||||
# Test context injection
|
||||
npm run test:context
|
||||
|
||||
# Verbose context test
|
||||
npm run test:context:verbose
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
# Build hooks and worker
|
||||
npm run build
|
||||
|
||||
# Build only hooks
|
||||
npm run build:hooks
|
||||
|
||||
# Publish to NPM (maintainers only)
|
||||
npm run publish:npm
|
||||
```
|
||||
|
||||
## Viewing Stored Context
|
||||
|
||||
Context is stored in SQLite database at `~/.claude-mem/claude-mem.db`.
|
||||
|
||||
Query the database directly:
|
||||
|
||||
```bash
|
||||
# Open database
|
||||
sqlite3 ~/.claude-mem/claude-mem.db
|
||||
|
||||
# View recent sessions
|
||||
SELECT session_id, project, created_at, status
|
||||
FROM sdk_sessions
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 10;
|
||||
|
||||
# View session summaries
|
||||
SELECT session_id, request, completed, learned
|
||||
FROM session_summaries
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 5;
|
||||
|
||||
# View observations for a session
|
||||
SELECT tool_name, created_at
|
||||
FROM observations
|
||||
WHERE session_id = 'YOUR_SESSION_ID';
|
||||
```
|
||||
|
||||
## Understanding Progressive Disclosure
|
||||
|
||||
Context injection uses progressive disclosure for efficient token usage:
|
||||
|
||||
### Layer 1: Index Display (Session Start)
|
||||
- Shows observation titles with token cost estimates
|
||||
- Displays session markers in chronological timeline
|
||||
- Groups observations by file for visual clarity
|
||||
- Shows full summary details **only if** generated after last observation
|
||||
- Token cost: ~50-200 tokens for index view
|
||||
|
||||
### Layer 2: On-Demand Details (mem-search Skill)
|
||||
- Ask naturally: "What bugs did we fix?" or "How did we implement X?"
|
||||
- Claude auto-invokes mem-search skill to fetch full details
|
||||
- Search by concept, file, type, or keyword
|
||||
- Timeline context around specific observations
|
||||
- Token cost: ~100-500 tokens per observation fetched
|
||||
- Skill uses HTTP API (v5.4.0+) for efficient retrieval
|
||||
|
||||
### Layer 3: Perfect Recall (Code Access)
|
||||
- Read source files directly when needed
|
||||
- Access original transcripts and raw data
|
||||
- Full context available on-demand
|
||||
|
||||
This ensures efficient token usage while maintaining access to complete history when needed.
|
||||
|
||||
## Multi-Prompt Sessions & `/clear` Behavior
|
||||
|
||||
Claude-Mem supports sessions that span multiple user prompts:
|
||||
|
||||
- **prompt_counter**: Tracks total prompts in a session
|
||||
- **prompt_number**: Identifies specific prompt within session
|
||||
- **Session continuity**: Observations and summaries link across prompts
|
||||
|
||||
### Important Note About `/clear`
|
||||
|
||||
When you use `/clear`, the session doesn't end - it continues with a new prompt number. This means:
|
||||
|
||||
- ✅ **Context is re-injected** from recent sessions (SessionStart hook fires with `source: "clear"`)
|
||||
- ✅ **Observations are still being captured** and added to the current session
|
||||
- ✅ **A summary will be generated** when Claude finishes responding (Stop hook fires)
|
||||
|
||||
The `/clear` command clears the conversation context visible to Claude AND re-injects fresh context from recent sessions, while the underlying session continues tracking observations.
|
||||
|
||||
## Searching Your History (v5.4.0+)
|
||||
|
||||
Claude-Mem uses the mem-search skill for querying your project history. Simply ask naturally:
|
||||
|
||||
```
|
||||
"What bugs did we fix last session?"
|
||||
"How did we implement authentication?"
|
||||
"What changes were made to worker-service.ts?"
|
||||
"Show me recent work on this project"
|
||||
```
|
||||
|
||||
Claude automatically recognizes your intent and invokes the mem-search skill, which uses HTTP API endpoints to query your memory efficiently.
|
||||
|
||||
**Token Savings**: ~2,250 tokens per session start vs previous MCP approach
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Skill-Based Search](/usage/search-tools) - Learn how to search your project history
|
||||
- [Architecture Overview](/architecture/overview) - Understand how it works
|
||||
- [Troubleshooting](/troubleshooting) - Common issues and solutions
|
||||
@@ -0,0 +1,195 @@
|
||||
---
|
||||
title: "Private Tags"
|
||||
description: "Control what gets stored in memory with <private> tags"
|
||||
---
|
||||
|
||||
# Private Tags
|
||||
|
||||
## Overview
|
||||
|
||||
Use `<private>` tags to mark content you don't want persisted in claude-mem's observation database. This gives you fine-grained control over what gets remembered across sessions.
|
||||
|
||||
## How It Works
|
||||
|
||||
Wrap any content in `<private>` tags:
|
||||
|
||||
```
|
||||
<private>
|
||||
This content will not be stored in memory
|
||||
</private>
|
||||
```
|
||||
|
||||
Claude can see and use this content during the current session, but it won't be saved as an observation.
|
||||
|
||||
## Use Cases
|
||||
|
||||
### 1. Sensitive Information
|
||||
|
||||
```
|
||||
Please analyze this error:
|
||||
|
||||
<private>
|
||||
Error: Database connection failed
|
||||
Host: internal-db-prod.company.com
|
||||
Port: 5432
|
||||
User: admin_user
|
||||
</private>
|
||||
|
||||
What might be causing this?
|
||||
```
|
||||
|
||||
Claude sees the full error but only the question gets stored.
|
||||
|
||||
### 2. Temporary Context
|
||||
|
||||
```
|
||||
<private>
|
||||
Here's some background context just for this session:
|
||||
- Project deadline is tomorrow
|
||||
- This is a hotfix for production
|
||||
- Manager asked for this specifically
|
||||
</private>
|
||||
|
||||
Help me fix this bug quickly.
|
||||
```
|
||||
|
||||
### 3. Debugging Information
|
||||
|
||||
```
|
||||
<private>
|
||||
Debug output from previous run:
|
||||
[... 500 lines of logs ...]
|
||||
</private>
|
||||
|
||||
Based on these logs, what's the root cause?
|
||||
```
|
||||
|
||||
### 4. Exploratory Prompts
|
||||
|
||||
```
|
||||
<private>
|
||||
I'm just brainstorming here, not making a final decision
|
||||
</private>
|
||||
|
||||
What are some wild approaches to solving this?
|
||||
```
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Tag Behavior
|
||||
|
||||
- **Multiline support**: Tags can wrap multiple lines of content
|
||||
- **Multiple tags**: You can use multiple `<private>` sections in one message
|
||||
- **Nested tags**: Inner tags are included in outer tag removal
|
||||
- **Always active**: No configuration needed - works automatically
|
||||
|
||||
### What Gets Filtered
|
||||
|
||||
The `<private>` tag filters content from storage and memory:
|
||||
- **User prompt storage** - Tags are stripped before saving to the user_prompts table
|
||||
- **Tool inputs** - Parameters passed to tools are filtered before observation creation
|
||||
- **Tool responses** - Output from tools is filtered before observation creation
|
||||
- **All searchable content** - Private content never reaches the database or search indices
|
||||
|
||||
**Important**: Tags are stripped during storage, not from the live conversation. Claude sees the full content including `<private>` tags during the session, and they only disappear when content is persisted to the database.
|
||||
|
||||
### What Doesn't Get Filtered
|
||||
|
||||
- Session summaries (generated from non-private observations only)
|
||||
- Claude's responses (not captured by claude-mem)
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: API Keys
|
||||
|
||||
```
|
||||
<private>
|
||||
API_KEY=sk-proj-abc123xyz789
|
||||
</private>
|
||||
|
||||
Test this API connection for me
|
||||
```
|
||||
|
||||
The API key won't be stored, but Claude can use it during the session.
|
||||
|
||||
### Example 2: Personal Notes
|
||||
|
||||
```
|
||||
<private>
|
||||
Note to self: This is for the Smith project - the one we discussed
|
||||
last Tuesday. Don't confuse with the Jones project.
|
||||
</private>
|
||||
|
||||
Review the authentication implementation and suggest improvements.
|
||||
```
|
||||
|
||||
The personal context helps Claude understand your request without polluting your observation history.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Don't over-tag**: Only use `<private>` for content you genuinely don't want stored
|
||||
2. **Context matters**: Claude's understanding of your project comes from observations - excessive private tagging reduces future context quality
|
||||
3. **Secrets belong elsewhere**: While `<private>` prevents storage, sensitive data should still use proper secrets management
|
||||
4. **Test it works**: Check `~/.claude-mem/silent.log` if you're unsure whether tags are being stripped
|
||||
|
||||
## Verification
|
||||
|
||||
To verify tags are working:
|
||||
|
||||
1. Submit a prompt with `<private>` tags
|
||||
2. Check the database to ensure private content is not stored:
|
||||
```bash
|
||||
# Check user prompts
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT prompt_text FROM user_prompts ORDER BY created_at_epoch DESC LIMIT 1;"
|
||||
|
||||
# Check observations
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT narrative FROM observations ORDER BY created_at_epoch DESC LIMIT 1;"
|
||||
```
|
||||
3. The private content should NOT appear in either user_prompts or observations
|
||||
4. The `<private>` tags themselves should also be stripped
|
||||
|
||||
## Architecture
|
||||
|
||||
The `<private>` tag uses an **edge processing pattern**:
|
||||
|
||||
- Content is filtered at the hook layer before any storage
|
||||
- **UserPromptSubmit hook**: Strips tags from user prompts before saving to the user_prompts table (your typed prompts are cleaned before database storage)
|
||||
- **PostToolUse hook**: Strips tags from serialized tool_input and tool_response JSON before observation creation
|
||||
- Filtering happens before data reaches the worker service or database
|
||||
- This keeps the worker simple and follows a one-way data stream
|
||||
- Tags remain visible in the live conversation but are stripped from all persistent storage
|
||||
|
||||
**Tag Stripping Scope**: The implementation strips tags from the *serialized JSON representations* of tool inputs and tool responses, not from the original user prompt text in the conversation UI. The user prompt text you type is stored in a separate table (user_prompts) where tags are also stripped before storage.
|
||||
|
||||
This design ensures that private content never reaches the database, search indices, or memory agent, maintaining a clean separation between ephemeral and persistent data.
|
||||
|
||||
## Related Features
|
||||
|
||||
- [Search Tools](/usage/search-tools) - How to search past observations
|
||||
- [Getting Started](/usage/getting-started) - Basic usage guide
|
||||
- [Configuration](/configuration) - System settings and environment variables
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Tags Not Being Stripped
|
||||
|
||||
1. Verify correct syntax: `<private>content</private>`
|
||||
2. Check `~/.claude-mem/silent.log` for errors
|
||||
3. Ensure worker is running: `pm2 list`
|
||||
4. Restart worker: `npm run worker:restart`
|
||||
|
||||
### Partial Content Stored
|
||||
|
||||
If content appears partially in observations:
|
||||
- Ensure tags are properly closed
|
||||
- Check for typos in tag names
|
||||
- Verify content is inside tool executions (not just in your prompt text)
|
||||
|
||||
### Silent Log Shows Errors
|
||||
|
||||
If you see errors in `~/.claude-mem/silent.log`:
|
||||
```
|
||||
[save-hook] stripMemoryTags received non-string: { type: 'object' }
|
||||
```
|
||||
|
||||
This is usually harmless - it indicates defensive type checking is working. However, if you see these frequently, it may indicate a bug. Please report it at https://github.com/thedotmack/claude-mem/issues
|
||||
@@ -0,0 +1,404 @@
|
||||
---
|
||||
title: "mem-search Skill"
|
||||
description: "Query your project history with natural language"
|
||||
---
|
||||
|
||||
# mem-search Skill Usage
|
||||
|
||||
Once claude-mem is installed as a plugin, you can search your project history using natural language. Claude automatically invokes the mem-search skill when you ask about past work.
|
||||
|
||||
## How It Works
|
||||
|
||||
**v5.5.0 Enhancement**: The search skill was renamed to "mem-search" for better scope differentiation, with effectiveness increased from 67% to 100% and enhanced concrete triggers (85% vs 44%).
|
||||
|
||||
**v5.4.0 Architecture**: Claude-Mem uses a skill-based search architecture instead of MCP tools, saving ~2,250 tokens per session start through progressive disclosure.
|
||||
|
||||
**Simple Usage:**
|
||||
- Just ask naturally: *"What did we do last session?"*
|
||||
- Claude recognizes the intent and invokes the mem-search skill
|
||||
- The skill uses HTTP API endpoints to query your memory
|
||||
- Results are formatted and presented to you
|
||||
|
||||
**Benefits:**
|
||||
- **Token Efficient**: ~250 tokens (skill frontmatter) vs ~2,500 tokens (MCP tool definitions)
|
||||
- **Natural Language**: No need to learn specific tool syntax
|
||||
- **Progressive Disclosure**: Only loads detailed instructions when needed
|
||||
- **Auto-Invoked**: Claude knows when to search based on your questions
|
||||
- **Scope Differentiation**: "mem-search" clearly distinguishes from native conversation memory
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Operation | Purpose |
|
||||
|-------------------------|----------------------------------------------|
|
||||
| Search Observations | Full-text search across observations |
|
||||
| Search Sessions | Full-text search across session summaries |
|
||||
| Search Prompts | Full-text search across raw user prompts |
|
||||
| By Concept | Find observations tagged with concepts |
|
||||
| By File | Find observations referencing files |
|
||||
| By Type | Find observations by type |
|
||||
| Recent Context | Get recent session context |
|
||||
| Timeline | Get unified timeline around a specific point |
|
||||
| Timeline by Query | Search and get timeline context in one step |
|
||||
| API Help | Get search API documentation |
|
||||
|
||||
## Example Queries
|
||||
|
||||
### Natural Language Queries
|
||||
|
||||
**Search Observations:**
|
||||
```
|
||||
"What bugs did we fix related to authentication?"
|
||||
"Show me all decisions about the build system"
|
||||
"Find refactoring work on the database"
|
||||
```
|
||||
|
||||
**Search Sessions:**
|
||||
```
|
||||
"What did we learn about hooks?"
|
||||
"What was accomplished in the API implementation?"
|
||||
"Show me recent work on this project"
|
||||
```
|
||||
|
||||
**Search Prompts:**
|
||||
```
|
||||
"When did I ask about authentication features?"
|
||||
"Find all my requests about dark mode"
|
||||
```
|
||||
|
||||
**Note**: Claude automatically translates your natural language queries into the appropriate search operations.
|
||||
|
||||
### Search by File
|
||||
|
||||
```
|
||||
"Show me everything related to worker-service.ts"
|
||||
"What changes were made to migrations.ts?"
|
||||
"Find all work on the database file"
|
||||
```
|
||||
|
||||
### Search by Concept
|
||||
|
||||
```
|
||||
"Show observations tagged with architecture"
|
||||
"Find all security-related observations"
|
||||
"What patterns have we used?"
|
||||
```
|
||||
|
||||
### Search by Type
|
||||
|
||||
```
|
||||
"Find all feature implementations"
|
||||
"Show me all decisions and discoveries"
|
||||
"What bugs have we fixed?"
|
||||
```
|
||||
|
||||
### Recent Context
|
||||
|
||||
```
|
||||
"Show me what we've been working on"
|
||||
"Get context from the last 5 sessions"
|
||||
"What happened recently on this project?"
|
||||
```
|
||||
|
||||
### Timeline Queries
|
||||
|
||||
**Get timeline around a specific point:**
|
||||
```
|
||||
"What was happening when we implemented authentication?"
|
||||
"Show me the context around that bug fix"
|
||||
"What led to the decision to refactor the database?"
|
||||
```
|
||||
|
||||
**Timeline by query:**
|
||||
```
|
||||
"Find when we added the viewer UI and show what happened around that time"
|
||||
"Search for authentication work and show the timeline"
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- See the complete narrative arc around key events
|
||||
- All record types (observations, sessions, prompts) in chronological view
|
||||
- Understand what was happening before and after important changes
|
||||
|
||||
## Search Strategy
|
||||
|
||||
The mem-search skill uses a progressive disclosure pattern to efficiently retrieve information:
|
||||
|
||||
### 1. Ask Naturally
|
||||
|
||||
Start with a natural language question:
|
||||
```
|
||||
"What bugs did we fix related to authentication?"
|
||||
```
|
||||
|
||||
### 2. Claude Invokes mem-search Skill
|
||||
|
||||
Claude recognizes your intent and loads the mem-search skill (~250 tokens for skill frontmatter).
|
||||
|
||||
### 3. Skill Uses HTTP API
|
||||
|
||||
The skill calls the appropriate HTTP endpoint (e.g., `/api/search/observations`) with the query.
|
||||
|
||||
### 4. Results Formatted
|
||||
|
||||
Results are formatted and presented to you, usually starting with an index/summary format.
|
||||
|
||||
### 5. Deep Dive if Needed
|
||||
|
||||
If you need more details, ask follow-up questions:
|
||||
```
|
||||
"Tell me more about observation #123"
|
||||
"Show me the full details of that decision"
|
||||
```
|
||||
|
||||
**Benefits of This Approach:**
|
||||
- **Token Efficient**: Only loads what you need, when you need it
|
||||
- **Natural**: No syntax to learn
|
||||
- **Progressive**: Start with overview, drill down as needed
|
||||
- **Automatic**: Claude handles the search invocation
|
||||
|
||||
## Advanced Filtering
|
||||
|
||||
You can refine searches using natural language filters:
|
||||
|
||||
### Date Ranges
|
||||
|
||||
```
|
||||
"What bugs did we fix in October?"
|
||||
"Show me work from last week"
|
||||
"Find decisions made between October 1-31"
|
||||
```
|
||||
|
||||
### Multiple Types
|
||||
|
||||
```
|
||||
"Show me all decisions and features"
|
||||
"Find bugfixes and refactorings"
|
||||
```
|
||||
|
||||
### Concepts
|
||||
|
||||
```
|
||||
"Find database work related to architecture and performance"
|
||||
"Show security observations"
|
||||
```
|
||||
|
||||
### File-Specific
|
||||
|
||||
```
|
||||
"Show refactoring work that touched worker-service.ts"
|
||||
"Find changes to auth files"
|
||||
```
|
||||
|
||||
### Project Filtering
|
||||
|
||||
```
|
||||
"Show authentication work on my-app project"
|
||||
"What have we done on this codebase?"
|
||||
```
|
||||
|
||||
**Note**: Claude translates your natural language into the appropriate API filters automatically.
|
||||
|
||||
## Under the Hood: HTTP API
|
||||
|
||||
The mem-search skill uses HTTP endpoints on the worker service (port 37777):
|
||||
|
||||
- `GET /api/search/observations` - Full-text search observations
|
||||
- `GET /api/search/sessions` - Full-text search session summaries
|
||||
- `GET /api/search/prompts` - Full-text search user prompts
|
||||
- `GET /api/search/by-concept` - Find observations by concept tag
|
||||
- `GET /api/search/by-file` - Find work related to specific files
|
||||
- `GET /api/search/by-type` - Find observations by type
|
||||
- `GET /api/context/recent` - Get recent session context
|
||||
- `GET /api/context/timeline` - Get timeline around specific point
|
||||
- `GET /api/timeline/by-query` - Search + timeline in one call
|
||||
- `GET /api/search/help` - API documentation
|
||||
|
||||
These endpoints use FTS5 full-text search with support for:
|
||||
- Boolean operators (AND, OR, NOT)
|
||||
- Phrase searches
|
||||
- Column-specific searches
|
||||
- Date range filtering
|
||||
- Project filtering
|
||||
|
||||
## Result Metadata
|
||||
|
||||
All results include rich metadata:
|
||||
|
||||
```
|
||||
## JWT authentication decision
|
||||
|
||||
**Type**: decision
|
||||
**Date**: 2025-10-21 14:23:45
|
||||
**Concepts**: authentication, security, architecture
|
||||
**Files Read**: src/auth/middleware.ts, src/utils/jwt.ts
|
||||
**Files Modified**: src/auth/jwt-strategy.ts
|
||||
|
||||
**Narrative**:
|
||||
Decided to implement JWT-based authentication instead of session-based
|
||||
authentication for better scalability and stateless design...
|
||||
|
||||
**Facts**:
|
||||
• JWT tokens expire after 1 hour
|
||||
• Refresh tokens stored in httpOnly cookies
|
||||
• Token signing uses RS256 algorithm
|
||||
• Public keys rotated every 30 days
|
||||
```
|
||||
|
||||
## Citations
|
||||
|
||||
All search results include citations using the `claude-mem://` URI scheme:
|
||||
|
||||
- `claude-mem://observation/123` - Specific observation
|
||||
- `claude-mem://session/abc-456` - Specific session
|
||||
- `claude-mem://user-prompt/789` - Specific user prompt
|
||||
|
||||
These citations enable referencing specific historical context in your work.
|
||||
|
||||
## Token Management
|
||||
|
||||
### Token Efficiency Tips
|
||||
|
||||
1. **Start with index format**: ~50-100 tokens per result
|
||||
2. **Use small limits**: Start with 3-5 results
|
||||
3. **Apply filters**: Narrow results before searching
|
||||
4. **Paginate**: Use offset to browse results in batches
|
||||
|
||||
### Token Estimates
|
||||
|
||||
| Format | Tokens per Result |
|
||||
|--------|-------------------|
|
||||
| Index | 50-100 |
|
||||
| Full | 500-1000 |
|
||||
|
||||
**Example**:
|
||||
- 20 results in index format: ~1,000-2,000 tokens
|
||||
- 20 results in full format: ~10,000-20,000 tokens
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### 1. Debugging Issues
|
||||
|
||||
Find what went wrong:
|
||||
```
|
||||
search_observations with query="error database connection" and type="bugfix"
|
||||
```
|
||||
|
||||
### 2. Understanding Decisions
|
||||
|
||||
Review architectural choices:
|
||||
```
|
||||
find_by_type with type="decision" and format="index"
|
||||
```
|
||||
|
||||
Then deep dive on specific decisions:
|
||||
```
|
||||
search_observations with query="[DECISION TITLE]" and format="full"
|
||||
```
|
||||
|
||||
### 3. Code Archaeology
|
||||
|
||||
Find when a file was modified:
|
||||
```
|
||||
find_by_file with filePath="worker-service.ts"
|
||||
```
|
||||
|
||||
### 4. Feature History
|
||||
|
||||
Track feature development:
|
||||
```
|
||||
search_sessions with query="authentication feature"
|
||||
search_user_prompts with query="add authentication"
|
||||
```
|
||||
|
||||
### 5. Learning from Past Work
|
||||
|
||||
Review refactoring patterns:
|
||||
```
|
||||
find_by_type with type="refactor" and limit=10
|
||||
```
|
||||
|
||||
### 6. Context Recovery
|
||||
|
||||
Restore context after time away:
|
||||
```
|
||||
get_recent_context with limit=5
|
||||
search_sessions with query="[YOUR PROJECT NAME]" and orderBy="date_desc"
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Index first, full later**: Always start with index format
|
||||
2. **Small limits**: Start with 3-5 results to avoid token limits
|
||||
3. **Use filters**: Narrow results before searching
|
||||
4. **Specific queries**: More specific = better results
|
||||
5. **Review citations**: Use citations to reference past decisions
|
||||
6. **Date filtering**: Use date ranges for time-based searches
|
||||
7. **Type filtering**: Use types to categorize searches
|
||||
8. **Concept tags**: Use concepts for thematic searches
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No Results Found
|
||||
|
||||
1. Check database has data:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations;"
|
||||
```
|
||||
|
||||
2. Try broader natural language query:
|
||||
```
|
||||
"Show me anything about authentication" # Broader
|
||||
vs
|
||||
"Find exact JWT authentication implementation" # Too specific
|
||||
```
|
||||
|
||||
3. Ask without filters first:
|
||||
```
|
||||
"What do we have about auth?"
|
||||
# Then narrow down
|
||||
"Show me auth-related decisions"
|
||||
```
|
||||
|
||||
### Worker Service Not Running
|
||||
|
||||
If search isn't working, check the worker service:
|
||||
|
||||
```bash
|
||||
pm2 list # Check worker status
|
||||
npm run worker:restart # Restart if needed
|
||||
npm run worker:logs # View logs
|
||||
```
|
||||
|
||||
Or describe the issue to Claude and the troubleshoot skill will automatically activate to provide diagnosis.
|
||||
|
||||
### Performance Issues
|
||||
|
||||
If searches seem slow:
|
||||
1. Be more specific in your queries
|
||||
2. Ask for recent work (naturally filters by date)
|
||||
3. Specify the project you're interested in
|
||||
4. Ask for fewer results initially
|
||||
|
||||
## Technical Details
|
||||
|
||||
**Architecture Change (v5.4.0)**:
|
||||
- **Before**: 9 MCP tools (~2,500 tokens in tool definitions per session start)
|
||||
- **After**: 1 mem-search skill (~250 tokens in frontmatter, full instructions loaded on-demand)
|
||||
- **Savings**: ~2,250 tokens per session start
|
||||
- **Migration**: Transparent - users don't need to change how they ask questions
|
||||
|
||||
**v5.5.0 Enhancement**: Renamed from "search" to "mem-search" with improved effectiveness (67% → 100%) and enhanced triggers (44% → 85%).
|
||||
|
||||
**How the Skill Works:**
|
||||
1. User asks a question about past work
|
||||
2. Claude recognizes the intent matches the mem-search skill description
|
||||
3. Skill loads full instructions from `plugin/skills/mem-search/SKILL.md`
|
||||
4. Skill uses `curl` to call HTTP API endpoints
|
||||
5. Results formatted and returned to Claude
|
||||
6. Claude presents results to user
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Architecture Overview](/architecture/overview) - System components
|
||||
- [Database Schema](/architecture/database) - Understanding the data
|
||||
- [Getting Started](/usage/getting-started) - Automatic operation
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* PM2 Ecosystem Configuration for claude-mem Worker Service
|
||||
*
|
||||
* Usage:
|
||||
* pm2 start ecosystem.config.cjs
|
||||
* pm2 stop claude-mem-worker
|
||||
* pm2 restart claude-mem-worker
|
||||
* pm2 logs claude-mem-worker
|
||||
* pm2 status
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'claude-mem-worker',
|
||||
script: './plugin/scripts/worker-service.cjs',
|
||||
// INTENTIONAL: Watch mode enables auto-restart on plugin updates
|
||||
//
|
||||
// Why this is enabled:
|
||||
// - When you run `npm run sync-marketplace` or rebuild the plugin,
|
||||
// files in ~/.claude/plugins/marketplaces/thedotmack/ change
|
||||
// - Watch mode detects these changes and auto-restarts the worker
|
||||
// - Users get the latest code without manually running `pm2 restart`
|
||||
//
|
||||
// This is a feature, not a bug - it ensures users always run the
|
||||
// latest version after plugin updates.
|
||||
watch: true,
|
||||
ignore_watch: [
|
||||
'node_modules',
|
||||
'logs',
|
||||
'*.log',
|
||||
'*.db',
|
||||
'*.db-*',
|
||||
'.git'
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,89 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Pre-Compact Hook for Claude Memory System
|
||||
*
|
||||
* Updated to use the centralized PromptOrchestrator and HookTemplates system.
|
||||
* This hook validates the pre-compact request and executes compression using
|
||||
* standardized response templates for consistent Claude Code integration.
|
||||
*/
|
||||
|
||||
import { loadCliCommand } from './shared/config-loader.js';
|
||||
import { getLogsDir } from './shared/path-resolver.js';
|
||||
import {
|
||||
createHookResponse,
|
||||
executeCliCommand,
|
||||
validateHookPayload,
|
||||
debugLog
|
||||
} from './shared/hook-helpers.js';
|
||||
|
||||
|
||||
// Set up stdin immediately
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.resume(); // Explicitly enter flowing mode to prevent data loss
|
||||
|
||||
|
||||
// Read input from stdin
|
||||
let input = '';
|
||||
process.stdin.on('data', chunk => {
|
||||
input += chunk;
|
||||
});
|
||||
|
||||
process.stdin.on('end', async () => {
|
||||
|
||||
try {
|
||||
// Load CLI command inside try-catch to handle config errors properly
|
||||
const cliCommand = loadCliCommand();
|
||||
|
||||
const payload = JSON.parse(input);
|
||||
debugLog('Pre-compact hook started', { payload });
|
||||
|
||||
// Validate payload using centralized validation
|
||||
const validation = validateHookPayload(payload, 'PreCompact');
|
||||
if (!validation.valid) {
|
||||
const response = createHookResponse('PreCompact', false, { reason: validation.error });
|
||||
debugLog('Validation failed', { response });
|
||||
// Exit silently - validation failure is expected flow control
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Check for environment-based blocking conditions
|
||||
if (payload.trigger === 'auto' && process.env.DISABLE_AUTO_COMPRESSION === 'true') {
|
||||
const response = createHookResponse('PreCompact', false, {
|
||||
reason: 'Auto-compression disabled by configuration'
|
||||
});
|
||||
debugLog('Auto-compression disabled', { response });
|
||||
// Exit silently - disabled compression is expected flow control
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Execute compression using standardized CLI execution helper
|
||||
debugLog('Executing compression command', {
|
||||
command: cliCommand,
|
||||
args: ['compress', payload.transcript_path]
|
||||
});
|
||||
|
||||
const result = await executeCliCommand(cliCommand, ['compress', payload.transcript_path]);
|
||||
|
||||
if (!result.success) {
|
||||
const response = createHookResponse('PreCompact', false, {
|
||||
reason: `Compression failed: ${result.stderr || 'Unknown error'}`
|
||||
});
|
||||
debugLog('Compression command failed', { stderr: result.stderr, response });
|
||||
console.log(`claude-mem error: compression failed, see logs at ${getLogsDir()}`);
|
||||
process.exit(1); // Exit with error code for actual compression failure
|
||||
}
|
||||
|
||||
// Success - exit silently (suppressOutput is true)
|
||||
debugLog('Compression completed successfully');
|
||||
process.exit(0);
|
||||
|
||||
} catch (error) {
|
||||
const response = createHookResponse('PreCompact', false, {
|
||||
reason: `Hook execution error: ${error.message}`
|
||||
});
|
||||
debugLog('Pre-compact hook error', { error: error.message, response });
|
||||
console.log(`claude-mem error: hook failed, see logs at ${getLogsDir()}`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
@@ -1,61 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Session End Hook - Handles session end events including /clear
|
||||
*/
|
||||
|
||||
import { loadCliCommand } from './shared/config-loader.js';
|
||||
import { getSettingsPath, getArchivesDir } from './shared/path-resolver.js';
|
||||
import { execSync } from 'child_process';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
|
||||
const cliCommand = loadCliCommand();
|
||||
|
||||
// Check if save-on-clear is enabled
|
||||
function isSaveOnClearEnabled() {
|
||||
const settingsPath = getSettingsPath();
|
||||
if (existsSync(settingsPath)) {
|
||||
try {
|
||||
const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
||||
return settings.saveMemoriesOnClear === true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up stdin immediately before any async operations
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.resume(); // Explicitly enter flowing mode to prevent data loss
|
||||
|
||||
// Read input
|
||||
let input = '';
|
||||
process.stdin.on('data', chunk => {
|
||||
input += chunk;
|
||||
});
|
||||
|
||||
process.stdin.on('end', async () => {
|
||||
const data = JSON.parse(input);
|
||||
|
||||
// Check if this is a clear event and save-on-clear is enabled
|
||||
if (data.reason === 'clear' && isSaveOnClearEnabled()) {
|
||||
console.error('🧠 Saving memories before clearing context...');
|
||||
|
||||
try {
|
||||
// Use the CLI to compress current transcript
|
||||
execSync(`${cliCommand} compress --output ${getArchivesDir()}`, {
|
||||
stdio: 'inherit',
|
||||
env: { ...process.env, CLAUDE_MEM_SILENT: 'true' }
|
||||
});
|
||||
|
||||
console.error('✅ Memories saved successfully');
|
||||
} catch (error) {
|
||||
console.error('[session-end] Failed to save memories:', error.message);
|
||||
// Don't block the clear operation if memory saving fails
|
||||
}
|
||||
}
|
||||
|
||||
// Always continue
|
||||
console.log(JSON.stringify({ continue: true }));
|
||||
});
|
||||
@@ -1,170 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Session Start Hook - Load context when Claude Code starts
|
||||
*
|
||||
* Updated to use the centralized PromptOrchestrator and HookTemplates system.
|
||||
* This hook loads previous session context using standardized formatting and
|
||||
* provides rich context messages for Claude Code integration.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import { loadCliCommand } from './shared/config-loader.js';
|
||||
import {
|
||||
createHookResponse,
|
||||
formatSessionStartContext,
|
||||
executeCliCommand,
|
||||
parseContextData,
|
||||
validateHookPayload,
|
||||
debugLog
|
||||
} from './shared/hook-helpers.js';
|
||||
|
||||
const cliCommand = loadCliCommand();
|
||||
|
||||
// Set up stdin immediately before any async operations
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.resume(); // Explicitly enter flowing mode to prevent data loss
|
||||
|
||||
// Read input from stdin
|
||||
let input = '';
|
||||
process.stdin.on('data', chunk => {
|
||||
input += chunk;
|
||||
});
|
||||
|
||||
process.stdin.on('end', async () => {
|
||||
try {
|
||||
const payload = JSON.parse(input);
|
||||
debugLog('Session start hook started', { payload });
|
||||
|
||||
// Validate payload using centralized validation
|
||||
const validation = validateHookPayload(payload, 'SessionStart');
|
||||
if (!validation.valid) {
|
||||
debugLog('Payload validation failed', { error: validation.error });
|
||||
// For session start, continue even with invalid payload but log the error
|
||||
const response = createHookResponse('SessionStart', false, {
|
||||
error: `Payload validation failed: ${validation.error}`
|
||||
});
|
||||
console.log(JSON.stringify(response));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Skip load-context when source is "resume" to avoid duplicate context
|
||||
if (payload.source === 'resume') {
|
||||
debugLog('Skipping load-context for resume source');
|
||||
// Output valid JSON response with suppressOutput for resume
|
||||
const response = createHookResponse('SessionStart', true);
|
||||
console.log(JSON.stringify(response));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Extract project name from current working directory
|
||||
const projectName = path.basename(process.cwd());
|
||||
|
||||
// Load context using standardized CLI execution helper
|
||||
const contextResult = await executeCliCommand(cliCommand, [
|
||||
'load-context',
|
||||
'--format', 'session-start',
|
||||
'--project', projectName
|
||||
]);
|
||||
|
||||
if (!contextResult.success) {
|
||||
debugLog('Context loading failed', { stderr: contextResult.stderr });
|
||||
// Don't fail the session start, just provide error context
|
||||
const response = createHookResponse('SessionStart', false, {
|
||||
error: contextResult.stderr || 'Failed to load context'
|
||||
});
|
||||
console.log(JSON.stringify(response));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const rawContext = contextResult.stdout;
|
||||
debugLog('Raw context loaded', { contextLength: rawContext.length });
|
||||
|
||||
// Check if the output is actually an error message (starts with ❌)
|
||||
if (rawContext && rawContext.trim().startsWith('❌')) {
|
||||
debugLog('Detected error message in stdout', { rawContext });
|
||||
// Extract the clean error message without the emoji and format
|
||||
const errorMatch = rawContext.match(/❌\s*[^:]+:\s*([^\n]+)(?:\n\n💡\s*(.+))?/);
|
||||
let errorMsg = 'No previous memories found';
|
||||
let suggestion = '';
|
||||
|
||||
if (errorMatch) {
|
||||
errorMsg = errorMatch[1] || errorMsg;
|
||||
suggestion = errorMatch[2] || '';
|
||||
}
|
||||
|
||||
// Create a clean response without duplicating the error formatting
|
||||
const response = createHookResponse('SessionStart', false, {
|
||||
error: errorMsg + (suggestion ? `. ${suggestion}` : '')
|
||||
});
|
||||
console.log(JSON.stringify(response));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!rawContext || !rawContext.trim()) {
|
||||
debugLog('No context available, creating empty response');
|
||||
// No context available - use standardized empty response
|
||||
const response = createHookResponse('SessionStart', true);
|
||||
console.log(JSON.stringify(response));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Parse context data and format using centralized templates
|
||||
const contextData = parseContextData(rawContext);
|
||||
contextData.projectName = projectName;
|
||||
|
||||
// If we have raw context (not structured data), use it directly
|
||||
let formattedContext;
|
||||
if (contextData.rawContext) {
|
||||
formattedContext = contextData.rawContext;
|
||||
} else {
|
||||
// Use standardized formatting for structured context
|
||||
formattedContext = formatSessionStartContext(contextData);
|
||||
}
|
||||
|
||||
debugLog('Context formatted successfully', {
|
||||
memoryCount: contextData.memoryCount,
|
||||
hasStructuredData: !contextData.rawContext
|
||||
});
|
||||
|
||||
// Create standardized session start response using HookTemplates
|
||||
const response = createHookResponse('SessionStart', true, {
|
||||
context: formattedContext
|
||||
});
|
||||
|
||||
console.log(JSON.stringify(response));
|
||||
process.exit(0);
|
||||
|
||||
} catch (error) {
|
||||
debugLog('Session start hook error', { error: error.message });
|
||||
// Even on error, continue the session with error information
|
||||
const response = createHookResponse('SessionStart', false, {
|
||||
error: `Hook execution error: ${error.message}`
|
||||
});
|
||||
console.log(JSON.stringify(response));
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Extracts project name from transcript path
|
||||
* @param {string} transcriptPath - Path to transcript file
|
||||
* @returns {string|null} Extracted project name or null
|
||||
*/
|
||||
function extractProjectName(transcriptPath) {
|
||||
if (!transcriptPath) return null;
|
||||
|
||||
// Look for project pattern: /path/to/PROJECT_NAME/.claude/
|
||||
// Need to get PROJECT_NAME, not the parent directory
|
||||
const parts = transcriptPath.split(path.sep);
|
||||
const claudeIndex = parts.indexOf('.claude');
|
||||
|
||||
if (claudeIndex > 0) {
|
||||
// Get the directory immediately before .claude
|
||||
return parts[claudeIndex - 1];
|
||||
}
|
||||
|
||||
// Fall back to directory containing the transcript
|
||||
const dir = path.dirname(transcriptPath);
|
||||
return path.basename(dir);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Shared configuration loader utility for Claude Memory hooks
|
||||
* Loads CLI command name from config.json with proper fallback handling
|
||||
*/
|
||||
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { readFileSync, existsSync } from 'fs';
|
||||
|
||||
/**
|
||||
* Loads the CLI command name from the hooks config.json file
|
||||
* @returns {string} The CLI command name (defaults to 'claude-mem')
|
||||
*/
|
||||
export function loadCliCommand() {
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const configPath = join(__dirname, '..', 'config.json');
|
||||
|
||||
if (existsSync(configPath)) {
|
||||
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
||||
return config.cliCommand || 'claude-mem';
|
||||
}
|
||||
|
||||
return 'claude-mem';
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Hook Helper Functions
|
||||
*
|
||||
* This module provides JavaScript wrappers around the TypeScript PromptOrchestrator
|
||||
* and HookTemplates system, making them accessible to the JavaScript hook scripts.
|
||||
*/
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/**
|
||||
* Creates a standardized hook response using the HookTemplates system
|
||||
* @param {string} hookType - Type of hook ('PreCompact' or 'SessionStart')
|
||||
* @param {boolean} success - Whether the operation was successful
|
||||
* @param {Object} options - Additional options
|
||||
* @returns {Object} Formatted hook response
|
||||
*/
|
||||
export function createHookResponse(hookType, success, options = {}) {
|
||||
if (hookType === 'PreCompact') {
|
||||
if (success) {
|
||||
return {
|
||||
continue: true,
|
||||
suppressOutput: true
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
continue: false,
|
||||
stopReason: options.reason || 'Pre-compact operation failed',
|
||||
suppressOutput: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (hookType === 'SessionStart') {
|
||||
if (success && options.context) {
|
||||
return {
|
||||
continue: true,
|
||||
suppressOutput: true,
|
||||
hookSpecificOutput: {
|
||||
hookEventName: 'SessionStart',
|
||||
additionalContext: options.context
|
||||
}
|
||||
};
|
||||
} else if (success) {
|
||||
// No context - just suppress output without any message
|
||||
return {
|
||||
continue: true,
|
||||
suppressOutput: true
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
continue: true, // Continue even on context loading failure
|
||||
suppressOutput: true,
|
||||
hookSpecificOutput: {
|
||||
hookEventName: 'SessionStart',
|
||||
additionalContext: `Context loading encountered an issue: ${options.error || 'Unknown error'}. Starting without previous context.`
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Generic response for unknown hook types
|
||||
return {
|
||||
continue: success,
|
||||
suppressOutput: true,
|
||||
...(options.reason && !success ? { stopReason: options.reason } : {})
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a session start context message using standardized templates
|
||||
* @param {Object} contextData - Context information
|
||||
* @returns {string} Formatted context message
|
||||
*/
|
||||
export function formatSessionStartContext(contextData) {
|
||||
const {
|
||||
projectName = 'unknown project',
|
||||
memoryCount = 0,
|
||||
lastSessionTime,
|
||||
recentComponents = [],
|
||||
recentDecisions = []
|
||||
} = contextData;
|
||||
|
||||
const timeInfo = lastSessionTime ? ` (last worked: ${lastSessionTime})` : '';
|
||||
const contextParts = [];
|
||||
|
||||
contextParts.push(`🧠 Loaded ${memoryCount} memories from previous sessions for ${projectName}${timeInfo}`);
|
||||
|
||||
if (recentComponents.length > 0) {
|
||||
contextParts.push(`\n🎯 Recent components: ${recentComponents.slice(0, 3).join(', ')}`);
|
||||
}
|
||||
|
||||
if (recentDecisions.length > 0) {
|
||||
contextParts.push(`\n🔄 Recent decisions: ${recentDecisions.slice(0, 2).join(', ')}`);
|
||||
}
|
||||
|
||||
if (memoryCount > 0) {
|
||||
contextParts.push('\n💡 Use search_nodes("keywords") to find related work or open_nodes(["entity_name"]) to load specific components');
|
||||
}
|
||||
|
||||
return contextParts.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a CLI command and returns the result
|
||||
* @param {string} command - CLI command to execute
|
||||
* @param {Array} args - Command arguments
|
||||
* @param {Object} options - Spawn options
|
||||
* @returns {Promise<{stdout: string, stderr: string, success: boolean}>}
|
||||
*/
|
||||
export async function executeCliCommand(command, args = [], options = {}) {
|
||||
return new Promise((resolve) => {
|
||||
const process = spawn(command, args, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
...options
|
||||
});
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
if (process.stdout) {
|
||||
process.stdout.on('data', (data) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
}
|
||||
|
||||
if (process.stderr) {
|
||||
process.stderr.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
}
|
||||
|
||||
process.on('close', (code) => {
|
||||
resolve({
|
||||
stdout: stdout.trim(),
|
||||
stderr: stderr.trim(),
|
||||
success: code === 0
|
||||
});
|
||||
});
|
||||
|
||||
process.on('error', (error) => {
|
||||
resolve({
|
||||
stdout: '',
|
||||
stderr: error.message,
|
||||
success: false
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses context data from CLI output
|
||||
* @param {string} output - Raw CLI output
|
||||
* @returns {Object} Parsed context data
|
||||
*/
|
||||
export function parseContextData(output) {
|
||||
if (!output || !output.trim()) {
|
||||
return {
|
||||
memoryCount: 0,
|
||||
recentComponents: [],
|
||||
recentDecisions: []
|
||||
};
|
||||
}
|
||||
|
||||
// Try to parse as JSON first (if CLI outputs structured data)
|
||||
try {
|
||||
const parsed = JSON.parse(output);
|
||||
return {
|
||||
memoryCount: parsed.memoryCount || 0,
|
||||
recentComponents: parsed.recentComponents || [],
|
||||
recentDecisions: parsed.recentDecisions || [],
|
||||
lastSessionTime: parsed.lastSessionTime
|
||||
};
|
||||
} catch (e) {
|
||||
// If not JSON, treat as plain text context
|
||||
const lines = output.split('\n').filter(line => line.trim());
|
||||
return {
|
||||
memoryCount: lines.length,
|
||||
recentComponents: [],
|
||||
recentDecisions: [],
|
||||
rawContext: output
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates hook payload structure
|
||||
* @param {Object} payload - Hook payload to validate
|
||||
* @param {string} expectedHookType - Expected hook event name
|
||||
* @returns {{valid: boolean, error?: string}} Validation result
|
||||
*/
|
||||
export function validateHookPayload(payload, expectedHookType) {
|
||||
if (!payload || typeof payload !== 'object') {
|
||||
return { valid: false, error: 'Payload must be a valid object' };
|
||||
}
|
||||
|
||||
if (!payload.session_id || typeof payload.session_id !== 'string') {
|
||||
return { valid: false, error: 'Missing or invalid session_id' };
|
||||
}
|
||||
|
||||
if (!payload.transcript_path || typeof payload.transcript_path !== 'string') {
|
||||
return { valid: false, error: 'Missing or invalid transcript_path' };
|
||||
}
|
||||
|
||||
if (expectedHookType && payload.hook_event_name !== expectedHookType) {
|
||||
return { valid: false, error: `Expected hook_event_name to be ${expectedHookType}` };
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs debug information if debug mode is enabled
|
||||
* @param {string} message - Debug message
|
||||
* @param {Object} data - Additional data to log
|
||||
*/
|
||||
export function debugLog(message, data = {}) {
|
||||
if (process.env.DEBUG === 'true' || process.env.CLAUDE_MEM_DEBUG === 'true') {
|
||||
const timestamp = new Date().toISOString();
|
||||
console.error(`[${timestamp}] HOOK DEBUG: ${message}`, data);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Path resolver utility for Claude Memory hooks
|
||||
* Provides proper path handling using environment variables
|
||||
*/
|
||||
|
||||
import { join } from 'path';
|
||||
import { homedir } from 'os';
|
||||
|
||||
/**
|
||||
* Gets the base data directory for claude-mem
|
||||
* @returns {string} Data directory path
|
||||
*/
|
||||
export function getDataDir() {
|
||||
return process.env.CLAUDE_MEM_DATA_DIR || join(homedir(), '.claude-mem');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the settings file path
|
||||
* @returns {string} Settings file path
|
||||
*/
|
||||
export function getSettingsPath() {
|
||||
return join(getDataDir(), 'settings.json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the archives directory path
|
||||
* @returns {string} Archives directory path
|
||||
*/
|
||||
export function getArchivesDir() {
|
||||
return process.env.CLAUDE_MEM_ARCHIVES_DIR || join(getDataDir(), 'archives');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the logs directory path
|
||||
* @returns {string} Logs directory path
|
||||
*/
|
||||
export function getLogsDir() {
|
||||
return process.env.CLAUDE_MEM_LOGS_DIR || join(getDataDir(), 'logs');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the compact flag file path
|
||||
* @returns {string} Compact flag file path
|
||||
*/
|
||||
export function getCompactFlagPath() {
|
||||
return join(getDataDir(), '.compact-running');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all common paths used by hooks
|
||||
* @returns {Object} Object containing all common paths
|
||||
*/
|
||||
export function getPaths() {
|
||||
return {
|
||||
dataDir: getDataDir(),
|
||||
settingsPath: getSettingsPath(),
|
||||
archivesDir: getArchivesDir(),
|
||||
logsDir: getLogsDir(),
|
||||
compactFlagPath: getCompactFlagPath()
|
||||
};
|
||||
}
|
||||
@@ -1,21 +1,22 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "3.6.9",
|
||||
"version": "7.0.2",
|
||||
"description": "Memory compression system for Claude Code - persist context across sessions",
|
||||
"keywords": [
|
||||
"claude",
|
||||
"claude-code",
|
||||
"claude-agent-sdk",
|
||||
"mcp",
|
||||
"plugin",
|
||||
"memory",
|
||||
"compression",
|
||||
"knowledge-graph",
|
||||
"transcript",
|
||||
"cli",
|
||||
"typescript",
|
||||
"bun"
|
||||
"nodejs"
|
||||
],
|
||||
"author": "Alex Newman",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"license": "AGPL-3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thedotmack/claude-mem.git"
|
||||
@@ -24,36 +25,50 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/thedotmack/claude-mem/issues"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"claude-mem": "./dist/claude-mem.min.js"
|
||||
"scripts": {
|
||||
"build": "node scripts/build-hooks.js",
|
||||
"test": "vitest",
|
||||
"test:parser": "npx tsx src/sdk/parser.test.ts",
|
||||
"test:context": "echo '{\"session_id\":\"test-'$(date +%s)'\",\"cwd\":\"'$(pwd)'\",\"source\":\"startup\"}' | node plugin/scripts/context-hook.js 2>/dev/null",
|
||||
"test:context:verbose": "echo '{\"session_id\":\"test-'$(date +%s)'\",\"cwd\":\"'$(pwd)'\",\"source\":\"startup\"}' | node plugin/scripts/context-hook.js",
|
||||
"sync-marketplace": "node scripts/sync-marketplace.cjs",
|
||||
"sync-marketplace:force": "node scripts/sync-marketplace.cjs --force",
|
||||
"worker:start": "pm2 start ecosystem.config.cjs",
|
||||
"worker:stop": "pm2 stop claude-mem-worker",
|
||||
"worker:restart": "pm2 restart claude-mem-worker",
|
||||
"worker:logs": "pm2 flush claude-mem-worker && pm2 logs claude-mem-worker --lines 100 --nostream",
|
||||
"worker:logs:no-flush": "pm2 logs claude-mem-worker --lines 100 --nostream",
|
||||
"changelog:generate": "node scripts/generate-changelog.js",
|
||||
"usage:analyze": "node scripts/analyze-usage.js",
|
||||
"usage:today": "node scripts/analyze-usage.js $(date +%Y-%m-%d)"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-code": "^1.0.88",
|
||||
"@clack/prompts": "^0.11.0",
|
||||
"@modelcontextprotocol/sdk": "^0.5.0",
|
||||
"boxen": "^8.0.1",
|
||||
"chalk": "^5.6.0",
|
||||
"chromadb": "^3.0.14",
|
||||
"commander": "^14.0.0",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.1.27",
|
||||
"@modelcontextprotocol/sdk": "^1.20.1",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"better-sqlite3": "^12.5.0",
|
||||
"express": "^4.18.2",
|
||||
"glob": "^11.0.3",
|
||||
"gradient-string": "^3.0.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"oh-my-logo": "^0.3.2"
|
||||
"pm2": "^6.0.13",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"zod-to-json-schema": "^3.24.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"hooks",
|
||||
"commands",
|
||||
"src",
|
||||
".mcp.json",
|
||||
"CHANGELOG.md"
|
||||
]
|
||||
"devDependencies": {
|
||||
"@types/better-sqlite3": "^7.6.8",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/react": "^18.3.5",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"esbuild": "^0.25.12",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "^5.3.0",
|
||||
"vitest": "^4.0.15"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "7.0.2",
|
||||
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
|
||||
"author": {
|
||||
"name": "Alex Newman"
|
||||
},
|
||||
"repository": "https://github.com/thedotmack/claude-mem",
|
||||
"license": "AGPL-3.0",
|
||||
"keywords": [
|
||||
"memory",
|
||||
"context",
|
||||
"persistence",
|
||||
"hooks",
|
||||
"mcp"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"claude-mem-search": {
|
||||
"type": "stdio",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/mcp-server.cjs"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"description": "Claude-mem memory system hooks",
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"matcher": "startup|clear|compact",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/../scripts/smart-install.js\" && node ${CLAUDE_PLUGIN_ROOT}/scripts/context-hook.js",
|
||||
"timeout": 300
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/user-message-hook.js",
|
||||
"timeout": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"UserPromptSubmit": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/new-hook.js",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/save-hook.js",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Stop": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/summary-hook.js",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"SessionEnd": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/cleanup-hook.js",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/bin/bash
|
||||
# claude-mem-settings.sh - User settings manager for claude-mem plugin
|
||||
|
||||
USER_SETTINGS_FILE="$HOME/.claude/settings.json"
|
||||
|
||||
# Function to check if jq is available
|
||||
check_jq() {
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "Error: jq is required for JSON manipulation"
|
||||
echo "Install with: brew install jq"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to create settings file if it doesn't exist
|
||||
ensure_settings_file() {
|
||||
if [ ! -f "$USER_SETTINGS_FILE" ]; then
|
||||
mkdir -p "$(dirname "$USER_SETTINGS_FILE")"
|
||||
echo '{}' > "$USER_SETTINGS_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to get current model setting
|
||||
get_model() {
|
||||
if [ -f "$USER_SETTINGS_FILE" ]; then
|
||||
jq -r '.env.CLAUDE_MEM_MODEL // "claude-sonnet-4-5"' "$USER_SETTINGS_FILE"
|
||||
else
|
||||
echo "claude-sonnet-4-5"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to set model setting
|
||||
set_model() {
|
||||
local model=$1
|
||||
|
||||
ensure_settings_file
|
||||
|
||||
# Update or create the env.CLAUDE_MEM_MODEL setting
|
||||
jq --arg model "$model" '.env.CLAUDE_MEM_MODEL = $model' "$USER_SETTINGS_FILE" > tmp.json && mv tmp.json "$USER_SETTINGS_FILE"
|
||||
echo "Set CLAUDE_MEM_MODEL to: $model"
|
||||
}
|
||||
|
||||
# Function to remove model setting
|
||||
remove_model() {
|
||||
if [ -f "$USER_SETTINGS_FILE" ]; then
|
||||
jq 'del(.env.CLAUDE_MEM_MODEL)' "$USER_SETTINGS_FILE" > tmp.json && mv tmp.json "$USER_SETTINGS_FILE"
|
||||
echo "Removed CLAUDE_MEM_MODEL (will use default: claude-sonnet-4-5)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to list available models
|
||||
list_models() {
|
||||
echo "Available models:"
|
||||
echo " claude-haiku-4-5 - Fast and efficient"
|
||||
echo " claude-sonnet-4-5 - Balanced (default)"
|
||||
echo " claude-opus-4 - Most capable"
|
||||
echo " claude-3-7-sonnet - Alternative version"
|
||||
}
|
||||
|
||||
# Interactive menu
|
||||
show_menu() {
|
||||
echo "Claude Mem Plugin - Model Configuration"
|
||||
echo "======================================"
|
||||
echo "Current model: $(get_model)"
|
||||
echo "Settings file: $USER_SETTINGS_FILE"
|
||||
echo ""
|
||||
echo "1) Set model"
|
||||
echo "2) Remove model setting (use default)"
|
||||
echo "3) List available models"
|
||||
echo "4) Exit"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main interactive loop
|
||||
main() {
|
||||
check_jq
|
||||
|
||||
while true; do
|
||||
show_menu
|
||||
read -p "Choose an option (1-4): " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
list_models
|
||||
echo ""
|
||||
read -p "Enter model name: " model
|
||||
set_model "$model"
|
||||
;;
|
||||
2)
|
||||
remove_model
|
||||
;;
|
||||
3)
|
||||
list_models
|
||||
;;
|
||||
4)
|
||||
echo "Goodbye!"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option. Please choose 1-4."
|
||||
;;
|
||||
esac
|
||||
echo ""
|
||||
read -p "Press Enter to continue..."
|
||||
done
|
||||
}
|
||||
|
||||
# Run main if script is executed directly
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
import{stdin as u}from"process";import T from"path";import{existsSync as f}from"fs";import{homedir as d}from"os";import{spawnSync as U}from"child_process";import{readFileSync as D,existsSync as h}from"fs";var N=["bugfix","feature","refactor","discovery","decision","change"],L=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var p=N.join(","),g=L.join(",");var _=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:p,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:g,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return process.env[t]||this.DEFAULTS[t]}static getInt(t){let n=this.get(t);return parseInt(n,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!h(t))return this.getAllDefaults();let n=D(t,"utf-8"),o=JSON.parse(n).env||{},s={...this.DEFAULTS};for(let i of Object.keys(this.DEFAULTS))o[i]!==void 0&&(s[i]=o[i]);return s}};var E=T.join(d(),".claude","plugins","marketplaces","thedotmack"),y=100,R=500,I=10;function l(){let e=T.join(d(),".claude-mem","settings.json"),t=_.loadFromFile(e);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function A(){try{let e=l();return(await fetch(`http://127.0.0.1:${e}/health`,{signal:AbortSignal.timeout(y)})).ok}catch{return!1}}async function P(){try{let e=T.join(E,"ecosystem.config.cjs");if(!f(e))throw new Error(`Ecosystem config not found at ${e}`);let t=T.join(E,"node_modules",".bin","pm2"),n=process.platform==="win32"?t+".cmd":t,r=f(n)?n:"pm2",o=U(r,["start",e],{cwd:E,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(o.status!==0)throw new Error(o.stderr||"PM2 start failed");for(let s=0;s<I;s++)if(await new Promise(i=>setTimeout(i,R)),await A())return!0;return!1}catch{return!1}}async function C(){if(await A())return;if(!await P()){let t=l();throw new Error(`Worker service failed to start on port ${t}.
|
||||
|
||||
To start manually, run:
|
||||
cd ${E}
|
||||
npx pm2 start ecosystem.config.cjs
|
||||
|
||||
If already running, try: npx pm2 restart claude-mem-worker`)}}import{appendFileSync as k}from"fs";import{homedir as w}from"os";import{join as x}from"path";var W=x(w(),".claude-mem","silent.log");function a(e,t,n=""){let r=new Date().toISOString(),S=((new Error().stack||"").split(`
|
||||
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),M=S?`${S[1].split("/").pop()}:${S[2]}`:"unknown",c=`[${r}] [${M}] ${e}`;if(t!==void 0)try{c+=` ${JSON.stringify(t)}`}catch(O){c+=` [stringify error: ${O}]`}c+=`
|
||||
`;try{k(W,c)}catch(O){console.error("[silent-debug] Failed to write to log:",O)}return n}async function m(e){await C(),a("[cleanup-hook] Hook fired",{session_id:e?.session_id,cwd:e?.cwd,reason:e?.reason}),e||(console.log("No input provided - this script is designed to run as a Claude Code SessionEnd hook"),console.log(`
|
||||
Expected input format:`),console.log(JSON.stringify({session_id:"string",cwd:"string",transcript_path:"string",hook_event_name:"SessionEnd",reason:"exit"},null,2)),process.exit(0));let{session_id:t,reason:n}=e,r=l();try{let o=await fetch(`http://127.0.0.1:${r}/api/sessions/complete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,reason:n}),signal:AbortSignal.timeout(2e3)});if(o.ok){let s=await o.json();a("[cleanup-hook] Session cleanup completed",s)}else a("[cleanup-hook] Session not found or already cleaned up")}catch(o){a("[cleanup-hook] Worker not reachable (non-critical)",{error:o.message})}console.log('{"continue": true, "suppressOutput": true}'),process.exit(0)}if(u.isTTY)m(void 0);else{let e="";u.on("data",t=>e+=t),u.on("end",async()=>{let t=e?JSON.parse(e):void 0;await m(t)})}
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
import R from"path";import{stdin as T}from"process";import{execSync as y}from"child_process";import _ from"path";import{existsSync as u}from"fs";import{homedir as p}from"os";import{spawnSync as m}from"child_process";import{readFileSync as N,existsSync as g}from"fs";var M=["bugfix","feature","refactor","discovery","decision","change"],l=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var O=M.join(","),S=l.join(",");var i=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:O,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:S,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return process.env[t]||this.DEFAULTS[t]}static getInt(t){let r=this.get(t);return parseInt(r,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!g(t))return this.getAllDefaults();let r=N(t,"utf-8"),s=JSON.parse(r).env||{},n={...this.DEFAULTS};for(let o of Object.keys(this.DEFAULTS))s[o]!==void 0&&(n[o]=s[o]);return n}};var E=_.join(p(),".claude","plugins","marketplaces","thedotmack"),d=100,D=500,L=10;function a(){let e=_.join(p(),".claude-mem","settings.json"),t=i.loadFromFile(e);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function C(){try{let e=a();return(await fetch(`http://127.0.0.1:${e}/health`,{signal:AbortSignal.timeout(d)})).ok}catch{return!1}}async function U(){try{let e=_.join(E,"ecosystem.config.cjs");if(!u(e))throw new Error(`Ecosystem config not found at ${e}`);let t=_.join(E,"node_modules",".bin","pm2"),r=process.platform==="win32"?t+".cmd":t,c=u(r)?r:"pm2",s=m(c,["start",e],{cwd:E,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(s.status!==0)throw new Error(s.stderr||"PM2 start failed");for(let n=0;n<L;n++)if(await new Promise(o=>setTimeout(o,D)),await C())return!0;return!1}catch{return!1}}async function A(){if(await C())return;if(!await U()){let t=a();throw new Error(`Worker service failed to start on port ${t}.
|
||||
|
||||
To start manually, run:
|
||||
cd ${E}
|
||||
npx pm2 start ecosystem.config.cjs
|
||||
|
||||
If already running, try: npx pm2 restart claude-mem-worker`)}}async function f(e){await A();let t=e?.cwd??process.cwd(),r=t?R.basename(t):"unknown-project",s=`http://127.0.0.1:${a()}/api/context/inject?project=${encodeURIComponent(r)}`;return y(`curl -s "${s}"`,{encoding:"utf-8",timeout:5e3}).trim()}var I=process.argv.includes("--colors");if(T.isTTY||I)f(void 0).then(e=>{console.log(e),process.exit(0)});else{let e="";T.on("data",t=>e+=t),T.on("end",async()=>{let t=e.trim()?JSON.parse(e):void 0,r=await f(t);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:r}})),process.exit(0)})}
|
||||
@@ -0,0 +1,428 @@
|
||||
#!/usr/bin/env node
|
||||
import _e from"path";import{stdin as W}from"process";import q from"better-sqlite3";import{join as m,dirname as G,basename as le}from"path";import{homedir as v}from"os";import{existsSync as be,mkdirSync as Y}from"fs";import{fileURLToPath as V}from"url";function K(){return typeof __dirname<"u"?__dirname:G(V(import.meta.url))}var Oe=K(),l=process.env.CLAUDE_MEM_DATA_DIR||m(v(),".claude-mem"),I=process.env.CLAUDE_CONFIG_DIR||m(v(),".claude"),he=m(l,"archives"),Ne=m(l,"logs"),fe=m(l,"trash"),Ie=m(l,"backups"),Ae=m(l,"settings.json"),y=m(l,"claude-mem.db"),Le=m(l,"vector-db"),Ce=m(I,"settings.json"),De=m(I,"commands"),ve=m(I,"CLAUDE.md");function k(a){Y(a,{recursive:!0})}var A=(o=>(o[o.DEBUG=0]="DEBUG",o[o.INFO=1]="INFO",o[o.WARN=2]="WARN",o[o.ERROR=3]="ERROR",o[o.SILENT=4]="SILENT",o))(A||{}),L=class{level;useColor;constructor(){let e=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=A[e]??1,this.useColor=process.stdout.isTTY??!1}correlationId(e,s){return`obs-${e}-${s}`}sessionId(e){return`session-${e}`}formatData(e){if(e==null)return"";if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean")return e.toString();if(typeof e=="object"){if(e instanceof Error)return this.level===0?`${e.message}
|
||||
${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Object.keys(e);return s.length===0?"{}":s.length<=3?JSON.stringify(e):`{${s.length} keys: ${s.slice(0,3).join(", ")}...}`}return String(e)}formatTool(e,s){if(!s)return e;try{let t=typeof s=="string"?JSON.parse(s):s;if(e==="Bash"&&t.command){let r=t.command.length>50?t.command.substring(0,50)+"...":t.command;return`${e}(${r})`}if(e==="Read"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}if(e==="Edit"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}if(e==="Write"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}return e}catch{return e}}log(e,s,t,r,o){if(e<this.level)return;let n=new Date().toISOString().replace("T"," ").substring(0,23),i=A[e].padEnd(5),p=s.padEnd(6),d="";r?.correlationId?d=`[${r.correlationId}] `:r?.sessionId&&(d=`[session-${r.sessionId}] `);let E="";o!=null&&(this.level===0&&typeof o=="object"?E=`
|
||||
`+JSON.stringify(o,null,2):E=" "+this.formatData(o));let _="";if(r){let{sessionId:S,sdkSessionId:b,correlationId:u,...c}=r;Object.keys(c).length>0&&(_=` {${Object.entries(c).map(([j,$])=>`${j}=${$}`).join(", ")}}`)}let T=`[${n}] [${i}] [${p}] ${d}${t}${_}${E}`;e===3?console.error(T):console.log(T)}debug(e,s,t,r){this.log(0,e,s,t,r)}info(e,s,t,r){this.log(1,e,s,t,r)}warn(e,s,t,r){this.log(2,e,s,t,r)}error(e,s,t,r){this.log(3,e,s,t,r)}dataIn(e,s,t,r){this.info(e,`\u2192 ${s}`,t,r)}dataOut(e,s,t,r){this.info(e,`\u2190 ${s}`,t,r)}success(e,s,t,r){this.info(e,`\u2713 ${s}`,t,r)}failure(e,s,t,r){this.error(e,`\u2717 ${s}`,t,r)}timing(e,s,t,r){this.info(e,`\u23F1 ${s}`,r,{duration:`${t}ms`})}},M=new L;var R=class{db;constructor(){k(l),this.db=new q(y),this.db.pragma("journal_mode = WAL"),this.db.pragma("synchronous = NORMAL"),this.db.pragma("foreign_keys = ON"),this.initializeSchema(),this.ensureWorkerPortColumn(),this.ensurePromptTrackingColumns(),this.removeSessionSummariesUniqueConstraint(),this.addObservationHierarchicalFields(),this.makeObservationsTextNullable(),this.createUserPromptsTable(),this.ensureDiscoveryTokensColumn()}initializeSchema(){try{this.db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS schema_versions (
|
||||
id INTEGER PRIMARY KEY,
|
||||
version INTEGER UNIQUE NOT NULL,
|
||||
applied_at TEXT NOT NULL
|
||||
)
|
||||
`);let e=this.db.prepare("SELECT version FROM schema_versions ORDER BY version").all();(e.length>0?Math.max(...e.map(t=>t.version)):0)===0&&(console.error("[SessionStore] Initializing fresh database with migration004..."),this.db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS sdk_sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
claude_session_id TEXT UNIQUE NOT NULL,
|
||||
sdk_session_id TEXT UNIQUE,
|
||||
project TEXT NOT NULL,
|
||||
user_prompt TEXT,
|
||||
started_at TEXT NOT NULL,
|
||||
started_at_epoch INTEGER NOT NULL,
|
||||
completed_at TEXT,
|
||||
completed_at_epoch INTEGER,
|
||||
status TEXT CHECK(status IN ('active', 'completed', 'failed')) NOT NULL DEFAULT 'active'
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_sdk_sessions_claude_id ON sdk_sessions(claude_session_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sdk_sessions_sdk_id ON sdk_sessions(sdk_session_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sdk_sessions_project ON sdk_sessions(project);
|
||||
CREATE INDEX IF NOT EXISTS idx_sdk_sessions_status ON sdk_sessions(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_sdk_sessions_started ON sdk_sessions(started_at_epoch DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS observations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sdk_session_id TEXT NOT NULL,
|
||||
project TEXT NOT NULL,
|
||||
text TEXT NOT NULL,
|
||||
type TEXT NOT NULL CHECK(type IN ('decision', 'bugfix', 'feature', 'refactor', 'discovery')),
|
||||
created_at TEXT NOT NULL,
|
||||
created_at_epoch INTEGER NOT NULL,
|
||||
FOREIGN KEY(sdk_session_id) REFERENCES sdk_sessions(sdk_session_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_observations_sdk_session ON observations(sdk_session_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project);
|
||||
CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_observations_created ON observations(created_at_epoch DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS session_summaries (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sdk_session_id TEXT UNIQUE NOT NULL,
|
||||
project TEXT NOT NULL,
|
||||
request TEXT,
|
||||
investigated TEXT,
|
||||
learned TEXT,
|
||||
completed TEXT,
|
||||
next_steps TEXT,
|
||||
files_read TEXT,
|
||||
files_edited TEXT,
|
||||
notes TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
created_at_epoch INTEGER NOT NULL,
|
||||
FOREIGN KEY(sdk_session_id) REFERENCES sdk_sessions(sdk_session_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_session_summaries_sdk_session ON session_summaries(sdk_session_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_session_summaries_project ON session_summaries(project);
|
||||
CREATE INDEX IF NOT EXISTS idx_session_summaries_created ON session_summaries(created_at_epoch DESC);
|
||||
`),this.db.prepare("INSERT INTO schema_versions (version, applied_at) VALUES (?, ?)").run(4,new Date().toISOString()),console.error("[SessionStore] Migration004 applied successfully"))}catch(e){throw console.error("[SessionStore] Schema initialization error:",e.message),e}}ensureWorkerPortColumn(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(5))return;this.db.pragma("table_info(sdk_sessions)").some(r=>r.name==="worker_port")||(this.db.exec("ALTER TABLE sdk_sessions ADD COLUMN worker_port INTEGER"),console.error("[SessionStore] Added worker_port column to sdk_sessions table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(5,new Date().toISOString())}catch(e){console.error("[SessionStore] Migration error:",e.message)}}ensurePromptTrackingColumns(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(6))return;this.db.pragma("table_info(sdk_sessions)").some(p=>p.name==="prompt_counter")||(this.db.exec("ALTER TABLE sdk_sessions ADD COLUMN prompt_counter INTEGER DEFAULT 0"),console.error("[SessionStore] Added prompt_counter column to sdk_sessions table")),this.db.pragma("table_info(observations)").some(p=>p.name==="prompt_number")||(this.db.exec("ALTER TABLE observations ADD COLUMN prompt_number INTEGER"),console.error("[SessionStore] Added prompt_number column to observations table")),this.db.pragma("table_info(session_summaries)").some(p=>p.name==="prompt_number")||(this.db.exec("ALTER TABLE session_summaries ADD COLUMN prompt_number INTEGER"),console.error("[SessionStore] Added prompt_number column to session_summaries table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(6,new Date().toISOString())}catch(e){console.error("[SessionStore] Prompt tracking migration error:",e.message)}}removeSessionSummariesUniqueConstraint(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(7))return;if(!this.db.pragma("index_list(session_summaries)").some(r=>r.unique===1)){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(7,new Date().toISOString());return}console.error("[SessionStore] Removing UNIQUE constraint from session_summaries.sdk_session_id..."),this.db.exec("BEGIN TRANSACTION");try{this.db.exec(`
|
||||
CREATE TABLE session_summaries_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sdk_session_id TEXT NOT NULL,
|
||||
project TEXT NOT NULL,
|
||||
request TEXT,
|
||||
investigated TEXT,
|
||||
learned TEXT,
|
||||
completed TEXT,
|
||||
next_steps TEXT,
|
||||
files_read TEXT,
|
||||
files_edited TEXT,
|
||||
notes TEXT,
|
||||
prompt_number INTEGER,
|
||||
created_at TEXT NOT NULL,
|
||||
created_at_epoch INTEGER NOT NULL,
|
||||
FOREIGN KEY(sdk_session_id) REFERENCES sdk_sessions(sdk_session_id) ON DELETE CASCADE
|
||||
)
|
||||
`),this.db.exec(`
|
||||
INSERT INTO session_summaries_new
|
||||
SELECT id, sdk_session_id, project, request, investigated, learned,
|
||||
completed, next_steps, files_read, files_edited, notes,
|
||||
prompt_number, created_at, created_at_epoch
|
||||
FROM session_summaries
|
||||
`),this.db.exec("DROP TABLE session_summaries"),this.db.exec("ALTER TABLE session_summaries_new RENAME TO session_summaries"),this.db.exec(`
|
||||
CREATE INDEX idx_session_summaries_sdk_session ON session_summaries(sdk_session_id);
|
||||
CREATE INDEX idx_session_summaries_project ON session_summaries(project);
|
||||
CREATE INDEX idx_session_summaries_created ON session_summaries(created_at_epoch DESC);
|
||||
`),this.db.exec("COMMIT"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(7,new Date().toISOString()),console.error("[SessionStore] Successfully removed UNIQUE constraint from session_summaries.sdk_session_id")}catch(r){throw this.db.exec("ROLLBACK"),r}}catch(e){console.error("[SessionStore] Migration error (remove UNIQUE constraint):",e.message)}}addObservationHierarchicalFields(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(8))return;if(this.db.pragma("table_info(observations)").some(r=>r.name==="title")){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(8,new Date().toISOString());return}console.error("[SessionStore] Adding hierarchical fields to observations table..."),this.db.exec(`
|
||||
ALTER TABLE observations ADD COLUMN title TEXT;
|
||||
ALTER TABLE observations ADD COLUMN subtitle TEXT;
|
||||
ALTER TABLE observations ADD COLUMN facts TEXT;
|
||||
ALTER TABLE observations ADD COLUMN narrative TEXT;
|
||||
ALTER TABLE observations ADD COLUMN concepts TEXT;
|
||||
ALTER TABLE observations ADD COLUMN files_read TEXT;
|
||||
ALTER TABLE observations ADD COLUMN files_modified TEXT;
|
||||
`),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(8,new Date().toISOString()),console.error("[SessionStore] Successfully added hierarchical fields to observations table")}catch(e){console.error("[SessionStore] Migration error (add hierarchical fields):",e.message)}}makeObservationsTextNullable(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(9))return;let t=this.db.pragma("table_info(observations)").find(r=>r.name==="text");if(!t||t.notnull===0){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(9,new Date().toISOString());return}console.error("[SessionStore] Making observations.text nullable..."),this.db.exec("BEGIN TRANSACTION");try{this.db.exec(`
|
||||
CREATE TABLE observations_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sdk_session_id TEXT NOT NULL,
|
||||
project TEXT NOT NULL,
|
||||
text TEXT,
|
||||
type TEXT NOT NULL CHECK(type IN ('decision', 'bugfix', 'feature', 'refactor', 'discovery', 'change')),
|
||||
title TEXT,
|
||||
subtitle TEXT,
|
||||
facts TEXT,
|
||||
narrative TEXT,
|
||||
concepts TEXT,
|
||||
files_read TEXT,
|
||||
files_modified TEXT,
|
||||
prompt_number INTEGER,
|
||||
created_at TEXT NOT NULL,
|
||||
created_at_epoch INTEGER NOT NULL,
|
||||
FOREIGN KEY(sdk_session_id) REFERENCES sdk_sessions(sdk_session_id) ON DELETE CASCADE
|
||||
)
|
||||
`),this.db.exec(`
|
||||
INSERT INTO observations_new
|
||||
SELECT id, sdk_session_id, project, text, type, title, subtitle, facts,
|
||||
narrative, concepts, files_read, files_modified, prompt_number,
|
||||
created_at, created_at_epoch
|
||||
FROM observations
|
||||
`),this.db.exec("DROP TABLE observations"),this.db.exec("ALTER TABLE observations_new RENAME TO observations"),this.db.exec(`
|
||||
CREATE INDEX idx_observations_sdk_session ON observations(sdk_session_id);
|
||||
CREATE INDEX idx_observations_project ON observations(project);
|
||||
CREATE INDEX idx_observations_type ON observations(type);
|
||||
CREATE INDEX idx_observations_created ON observations(created_at_epoch DESC);
|
||||
`),this.db.exec("COMMIT"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(9,new Date().toISOString()),console.error("[SessionStore] Successfully made observations.text nullable")}catch(r){throw this.db.exec("ROLLBACK"),r}}catch(e){console.error("[SessionStore] Migration error (make text nullable):",e.message)}}createUserPromptsTable(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(10))return;if(this.db.pragma("table_info(user_prompts)").length>0){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(10,new Date().toISOString());return}console.error("[SessionStore] Creating user_prompts table with FTS5 support..."),this.db.exec("BEGIN TRANSACTION");try{this.db.exec(`
|
||||
CREATE TABLE user_prompts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
claude_session_id TEXT NOT NULL,
|
||||
prompt_number INTEGER NOT NULL,
|
||||
prompt_text TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
created_at_epoch INTEGER NOT NULL,
|
||||
FOREIGN KEY(claude_session_id) REFERENCES sdk_sessions(claude_session_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_user_prompts_claude_session ON user_prompts(claude_session_id);
|
||||
CREATE INDEX idx_user_prompts_created ON user_prompts(created_at_epoch DESC);
|
||||
CREATE INDEX idx_user_prompts_prompt_number ON user_prompts(prompt_number);
|
||||
CREATE INDEX idx_user_prompts_lookup ON user_prompts(claude_session_id, prompt_number);
|
||||
`),this.db.exec(`
|
||||
CREATE VIRTUAL TABLE user_prompts_fts USING fts5(
|
||||
prompt_text,
|
||||
content='user_prompts',
|
||||
content_rowid='id'
|
||||
);
|
||||
`),this.db.exec(`
|
||||
CREATE TRIGGER user_prompts_ai AFTER INSERT ON user_prompts BEGIN
|
||||
INSERT INTO user_prompts_fts(rowid, prompt_text)
|
||||
VALUES (new.id, new.prompt_text);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER user_prompts_ad AFTER DELETE ON user_prompts BEGIN
|
||||
INSERT INTO user_prompts_fts(user_prompts_fts, rowid, prompt_text)
|
||||
VALUES('delete', old.id, old.prompt_text);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER user_prompts_au AFTER UPDATE ON user_prompts BEGIN
|
||||
INSERT INTO user_prompts_fts(user_prompts_fts, rowid, prompt_text)
|
||||
VALUES('delete', old.id, old.prompt_text);
|
||||
INSERT INTO user_prompts_fts(rowid, prompt_text)
|
||||
VALUES (new.id, new.prompt_text);
|
||||
END;
|
||||
`),this.db.exec("COMMIT"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(10,new Date().toISOString()),console.error("[SessionStore] Successfully created user_prompts table with FTS5 support")}catch(t){throw this.db.exec("ROLLBACK"),t}}catch(e){console.error("[SessionStore] Migration error (create user_prompts table):",e.message)}}ensureDiscoveryTokensColumn(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(11))return;this.db.pragma("table_info(observations)").some(n=>n.name==="discovery_tokens")||(this.db.exec("ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER DEFAULT 0"),console.error("[SessionStore] Added discovery_tokens column to observations table")),this.db.pragma("table_info(session_summaries)").some(n=>n.name==="discovery_tokens")||(this.db.exec("ALTER TABLE session_summaries ADD COLUMN discovery_tokens INTEGER DEFAULT 0"),console.error("[SessionStore] Added discovery_tokens column to session_summaries table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(11,new Date().toISOString())}catch(e){throw console.error("[SessionStore] Discovery tokens migration error:",e.message),e}}getRecentSummaries(e,s=10){return this.db.prepare(`
|
||||
SELECT
|
||||
request, investigated, learned, completed, next_steps,
|
||||
files_read, files_edited, notes, prompt_number, created_at
|
||||
FROM session_summaries
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT ?
|
||||
`).all(e,s)}getRecentSummariesWithSessionInfo(e,s=3){return this.db.prepare(`
|
||||
SELECT
|
||||
sdk_session_id, request, learned, completed, next_steps,
|
||||
prompt_number, created_at
|
||||
FROM session_summaries
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT ?
|
||||
`).all(e,s)}getRecentObservations(e,s=20){return this.db.prepare(`
|
||||
SELECT type, text, prompt_number, created_at
|
||||
FROM observations
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT ?
|
||||
`).all(e,s)}getAllRecentObservations(e=100){return this.db.prepare(`
|
||||
SELECT id, type, title, subtitle, text, project, prompt_number, created_at, created_at_epoch
|
||||
FROM observations
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT ?
|
||||
`).all(e)}getAllRecentSummaries(e=50){return this.db.prepare(`
|
||||
SELECT id, request, investigated, learned, completed, next_steps,
|
||||
files_read, files_edited, notes, project, prompt_number,
|
||||
created_at, created_at_epoch
|
||||
FROM session_summaries
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT ?
|
||||
`).all(e)}getAllRecentUserPrompts(e=100){return this.db.prepare(`
|
||||
SELECT
|
||||
up.id,
|
||||
up.claude_session_id,
|
||||
s.project,
|
||||
up.prompt_number,
|
||||
up.prompt_text,
|
||||
up.created_at,
|
||||
up.created_at_epoch
|
||||
FROM user_prompts up
|
||||
LEFT JOIN sdk_sessions s ON up.claude_session_id = s.claude_session_id
|
||||
ORDER BY up.created_at_epoch DESC
|
||||
LIMIT ?
|
||||
`).all(e)}getAllProjects(){return this.db.prepare(`
|
||||
SELECT DISTINCT project
|
||||
FROM sdk_sessions
|
||||
WHERE project IS NOT NULL AND project != ''
|
||||
ORDER BY project ASC
|
||||
`).all().map(t=>t.project)}getLatestUserPrompt(e){return this.db.prepare(`
|
||||
SELECT
|
||||
up.*,
|
||||
s.sdk_session_id,
|
||||
s.project
|
||||
FROM user_prompts up
|
||||
JOIN sdk_sessions s ON up.claude_session_id = s.claude_session_id
|
||||
WHERE up.claude_session_id = ?
|
||||
ORDER BY up.created_at_epoch DESC
|
||||
LIMIT 1
|
||||
`).get(e)}getRecentSessionsWithStatus(e,s=3){return this.db.prepare(`
|
||||
SELECT * FROM (
|
||||
SELECT
|
||||
s.sdk_session_id,
|
||||
s.status,
|
||||
s.started_at,
|
||||
s.started_at_epoch,
|
||||
s.user_prompt,
|
||||
CASE WHEN sum.sdk_session_id IS NOT NULL THEN 1 ELSE 0 END as has_summary
|
||||
FROM sdk_sessions s
|
||||
LEFT JOIN session_summaries sum ON s.sdk_session_id = sum.sdk_session_id
|
||||
WHERE s.project = ? AND s.sdk_session_id IS NOT NULL
|
||||
GROUP BY s.sdk_session_id
|
||||
ORDER BY s.started_at_epoch DESC
|
||||
LIMIT ?
|
||||
)
|
||||
ORDER BY started_at_epoch ASC
|
||||
`).all(e,s)}getObservationsForSession(e){return this.db.prepare(`
|
||||
SELECT title, subtitle, type, prompt_number
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
ORDER BY created_at_epoch ASC
|
||||
`).all(e)}getObservationById(e){return this.db.prepare(`
|
||||
SELECT *
|
||||
FROM observations
|
||||
WHERE id = ?
|
||||
`).get(e)||null}getObservationsByIds(e,s={}){if(e.length===0)return[];let{orderBy:t="date_desc",limit:r}=s,o=t==="date_asc"?"ASC":"DESC",n=r?`LIMIT ${r}`:"",i=e.map(()=>"?").join(",");return this.db.prepare(`
|
||||
SELECT *
|
||||
FROM observations
|
||||
WHERE id IN (${i})
|
||||
ORDER BY created_at_epoch ${o}
|
||||
${n}
|
||||
`).all(...e)}getSummaryForSession(e){return this.db.prepare(`
|
||||
SELECT
|
||||
request, investigated, learned, completed, next_steps,
|
||||
files_read, files_edited, notes, prompt_number, created_at
|
||||
FROM session_summaries
|
||||
WHERE sdk_session_id = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT 1
|
||||
`).get(e)||null}getFilesForSession(e){let t=this.db.prepare(`
|
||||
SELECT files_read, files_modified
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
`).all(e),r=new Set,o=new Set;for(let n of t){if(n.files_read)try{let i=JSON.parse(n.files_read);Array.isArray(i)&&i.forEach(p=>r.add(p))}catch{}if(n.files_modified)try{let i=JSON.parse(n.files_modified);Array.isArray(i)&&i.forEach(p=>o.add(p))}catch{}}return{filesRead:Array.from(r),filesModified:Array.from(o)}}getSessionById(e){return this.db.prepare(`
|
||||
SELECT id, claude_session_id, sdk_session_id, project, user_prompt
|
||||
FROM sdk_sessions
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
`).get(e)||null}findActiveSDKSession(e){return this.db.prepare(`
|
||||
SELECT id, sdk_session_id, project, worker_port
|
||||
FROM sdk_sessions
|
||||
WHERE claude_session_id = ? AND status = 'active'
|
||||
LIMIT 1
|
||||
`).get(e)||null}findAnySDKSession(e){return this.db.prepare(`
|
||||
SELECT id
|
||||
FROM sdk_sessions
|
||||
WHERE claude_session_id = ?
|
||||
LIMIT 1
|
||||
`).get(e)||null}reactivateSession(e,s){this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET status = 'active', user_prompt = ?, worker_port = NULL
|
||||
WHERE id = ?
|
||||
`).run(s,e)}incrementPromptCounter(e){return this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET prompt_counter = COALESCE(prompt_counter, 0) + 1
|
||||
WHERE id = ?
|
||||
`).run(e),this.db.prepare(`
|
||||
SELECT prompt_counter FROM sdk_sessions WHERE id = ?
|
||||
`).get(e)?.prompt_counter||1}getPromptCounter(e){return this.db.prepare(`
|
||||
SELECT prompt_counter FROM sdk_sessions WHERE id = ?
|
||||
`).get(e)?.prompt_counter||0}createSDKSession(e,s,t){let r=new Date,o=r.getTime(),i=this.db.prepare(`
|
||||
INSERT OR IGNORE INTO sdk_sessions
|
||||
(claude_session_id, sdk_session_id, project, user_prompt, started_at, started_at_epoch, status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, 'active')
|
||||
`).run(e,e,s,t,r.toISOString(),o);return i.lastInsertRowid===0||i.changes===0?(s&&s.trim()!==""&&this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET project = ?, user_prompt = ?
|
||||
WHERE claude_session_id = ?
|
||||
`).run(s,t,e),this.db.prepare(`
|
||||
SELECT id FROM sdk_sessions WHERE claude_session_id = ? LIMIT 1
|
||||
`).get(e).id):i.lastInsertRowid}updateSDKSessionId(e,s){return this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET sdk_session_id = ?
|
||||
WHERE id = ? AND sdk_session_id IS NULL
|
||||
`).run(s,e).changes===0?(M.debug("DB","sdk_session_id already set, skipping update",{sessionId:e,sdkSessionId:s}),!1):!0}setWorkerPort(e,s){this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET worker_port = ?
|
||||
WHERE id = ?
|
||||
`).run(s,e)}getWorkerPort(e){return this.db.prepare(`
|
||||
SELECT worker_port
|
||||
FROM sdk_sessions
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
`).get(e)?.worker_port||null}saveUserPrompt(e,s,t){let r=new Date,o=r.getTime();return this.db.prepare(`
|
||||
INSERT INTO user_prompts
|
||||
(claude_session_id, prompt_number, prompt_text, created_at, created_at_epoch)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
`).run(e,s,t,r.toISOString(),o).lastInsertRowid}getUserPrompt(e,s){return this.db.prepare(`
|
||||
SELECT prompt_text
|
||||
FROM user_prompts
|
||||
WHERE claude_session_id = ? AND prompt_number = ?
|
||||
LIMIT 1
|
||||
`).get(e,s)?.prompt_text??null}storeObservation(e,s,t,r,o=0){let n=new Date,i=n.getTime();this.db.prepare(`
|
||||
SELECT id FROM sdk_sessions WHERE sdk_session_id = ?
|
||||
`).get(e)||(this.db.prepare(`
|
||||
INSERT INTO sdk_sessions
|
||||
(claude_session_id, sdk_session_id, project, started_at, started_at_epoch, status)
|
||||
VALUES (?, ?, ?, ?, ?, 'active')
|
||||
`).run(e,e,s,n.toISOString(),i),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`));let _=this.db.prepare(`
|
||||
INSERT INTO observations
|
||||
(sdk_session_id, project, type, title, subtitle, facts, narrative, concepts,
|
||||
files_read, files_modified, prompt_number, discovery_tokens, created_at, created_at_epoch)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(e,s,t.type,t.title,t.subtitle,JSON.stringify(t.facts),t.narrative,JSON.stringify(t.concepts),JSON.stringify(t.files_read),JSON.stringify(t.files_modified),r||null,o,n.toISOString(),i);return{id:Number(_.lastInsertRowid),createdAtEpoch:i}}storeSummary(e,s,t,r,o=0){let n=new Date,i=n.getTime();this.db.prepare(`
|
||||
SELECT id FROM sdk_sessions WHERE sdk_session_id = ?
|
||||
`).get(e)||(this.db.prepare(`
|
||||
INSERT INTO sdk_sessions
|
||||
(claude_session_id, sdk_session_id, project, started_at, started_at_epoch, status)
|
||||
VALUES (?, ?, ?, ?, ?, 'active')
|
||||
`).run(e,e,s,n.toISOString(),i),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`));let _=this.db.prepare(`
|
||||
INSERT INTO session_summaries
|
||||
(sdk_session_id, project, request, investigated, learned, completed,
|
||||
next_steps, notes, prompt_number, discovery_tokens, created_at, created_at_epoch)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(e,s,t.request,t.investigated,t.learned,t.completed,t.next_steps,t.notes,r||null,o,n.toISOString(),i);return{id:Number(_.lastInsertRowid),createdAtEpoch:i}}markSessionCompleted(e){let s=new Date,t=s.getTime();this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET status = 'completed', completed_at = ?, completed_at_epoch = ?
|
||||
WHERE id = ?
|
||||
`).run(s.toISOString(),t,e)}markSessionFailed(e){let s=new Date,t=s.getTime();this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET status = 'failed', completed_at = ?, completed_at_epoch = ?
|
||||
WHERE id = ?
|
||||
`).run(s.toISOString(),t,e)}getSessionSummariesByIds(e,s={}){if(e.length===0)return[];let{orderBy:t="date_desc",limit:r}=s,o=t==="date_asc"?"ASC":"DESC",n=r?`LIMIT ${r}`:"",i=e.map(()=>"?").join(",");return this.db.prepare(`
|
||||
SELECT * FROM session_summaries
|
||||
WHERE id IN (${i})
|
||||
ORDER BY created_at_epoch ${o}
|
||||
${n}
|
||||
`).all(...e)}getUserPromptsByIds(e,s={}){if(e.length===0)return[];let{orderBy:t="date_desc",limit:r}=s,o=t==="date_asc"?"ASC":"DESC",n=r?`LIMIT ${r}`:"",i=e.map(()=>"?").join(",");return this.db.prepare(`
|
||||
SELECT
|
||||
up.*,
|
||||
s.project,
|
||||
s.sdk_session_id
|
||||
FROM user_prompts up
|
||||
JOIN sdk_sessions s ON up.claude_session_id = s.claude_session_id
|
||||
WHERE up.id IN (${i})
|
||||
ORDER BY up.created_at_epoch ${o}
|
||||
${n}
|
||||
`).all(...e)}getTimelineAroundTimestamp(e,s=10,t=10,r){return this.getTimelineAroundObservation(null,e,s,t,r)}getTimelineAroundObservation(e,s,t=10,r=10,o){let n=o?"AND project = ?":"",i=o?[o]:[],p,d;if(e!==null){let S=`
|
||||
SELECT id, created_at_epoch
|
||||
FROM observations
|
||||
WHERE id <= ? ${n}
|
||||
ORDER BY id DESC
|
||||
LIMIT ?
|
||||
`,b=`
|
||||
SELECT id, created_at_epoch
|
||||
FROM observations
|
||||
WHERE id >= ? ${n}
|
||||
ORDER BY id ASC
|
||||
LIMIT ?
|
||||
`;try{let u=this.db.prepare(S).all(e,...i,t+1),c=this.db.prepare(b).all(e,...i,r+1);if(u.length===0&&c.length===0)return{observations:[],sessions:[],prompts:[]};p=u.length>0?u[u.length-1].created_at_epoch:s,d=c.length>0?c[c.length-1].created_at_epoch:s}catch(u){return console.error("[SessionStore] Error getting boundary observations:",u.message),{observations:[],sessions:[],prompts:[]}}}else{let S=`
|
||||
SELECT created_at_epoch
|
||||
FROM observations
|
||||
WHERE created_at_epoch <= ? ${n}
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT ?
|
||||
`,b=`
|
||||
SELECT created_at_epoch
|
||||
FROM observations
|
||||
WHERE created_at_epoch >= ? ${n}
|
||||
ORDER BY created_at_epoch ASC
|
||||
LIMIT ?
|
||||
`;try{let u=this.db.prepare(S).all(s,...i,t),c=this.db.prepare(b).all(s,...i,r+1);if(u.length===0&&c.length===0)return{observations:[],sessions:[],prompts:[]};p=u.length>0?u[u.length-1].created_at_epoch:s,d=c.length>0?c[c.length-1].created_at_epoch:s}catch(u){return console.error("[SessionStore] Error getting boundary timestamps:",u.message),{observations:[],sessions:[],prompts:[]}}}let E=`
|
||||
SELECT *
|
||||
FROM observations
|
||||
WHERE created_at_epoch >= ? AND created_at_epoch <= ? ${n}
|
||||
ORDER BY created_at_epoch ASC
|
||||
`,_=`
|
||||
SELECT *
|
||||
FROM session_summaries
|
||||
WHERE created_at_epoch >= ? AND created_at_epoch <= ? ${n}
|
||||
ORDER BY created_at_epoch ASC
|
||||
`,T=`
|
||||
SELECT up.*, s.project, s.sdk_session_id
|
||||
FROM user_prompts up
|
||||
JOIN sdk_sessions s ON up.claude_session_id = s.claude_session_id
|
||||
WHERE up.created_at_epoch >= ? AND up.created_at_epoch <= ? ${n.replace("project","s.project")}
|
||||
ORDER BY up.created_at_epoch ASC
|
||||
`;try{let S=this.db.prepare(E).all(p,d,...i),b=this.db.prepare(_).all(p,d,...i),u=this.db.prepare(T).all(p,d,...i);return{observations:S,sessions:b.map(c=>({id:c.id,sdk_session_id:c.sdk_session_id,project:c.project,request:c.request,completed:c.completed,next_steps:c.next_steps,created_at:c.created_at,created_at_epoch:c.created_at_epoch})),prompts:u.map(c=>({id:c.id,claude_session_id:c.claude_session_id,project:c.project,prompt:c.prompt_text,created_at:c.created_at,created_at_epoch:c.created_at_epoch}))}}catch(S){return console.error("[SessionStore] Error querying timeline records:",S.message),{observations:[],sessions:[],prompts:[]}}}close(){this.db.close()}};function J(a,e,s){return a==="PreCompact"?e?{continue:!0,suppressOutput:!0}:{continue:!1,stopReason:s.reason||"Pre-compact operation failed",suppressOutput:!0}:a==="SessionStart"?e&&s.context?{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:s.context}}:{continue:!0,suppressOutput:!0}:a==="UserPromptSubmit"||a==="PostToolUse"?{continue:!0,suppressOutput:!0}:a==="Stop"?{continue:!0,suppressOutput:!0}:{continue:e,suppressOutput:!0,...s.reason&&!e?{stopReason:s.reason}:{}}}function C(a,e,s={}){let t=J(a,e,s);return JSON.stringify(t)}import N from"path";import{existsSync as w}from"fs";import{homedir as F}from"os";import{spawnSync as se}from"child_process";import{readFileSync as Z,existsSync as ee}from"fs";var Q=["bugfix","feature","refactor","discovery","decision","change"],z=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var U=Q.join(","),x=z.join(",");var O=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:U,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:x,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(e){return process.env[e]||this.DEFAULTS[e]}static getInt(e){let s=this.get(e);return parseInt(s,10)}static getBool(e){return this.get(e)==="true"}static loadFromFile(e){if(!ee(e))return this.getAllDefaults();let s=Z(e,"utf-8"),r=JSON.parse(s).env||{},o={...this.DEFAULTS};for(let n of Object.keys(this.DEFAULTS))r[n]!==void 0&&(o[n]=r[n]);return o}};var h=N.join(F(),".claude","plugins","marketplaces","thedotmack"),te=100,re=500,oe=10;function f(){let a=N.join(F(),".claude-mem","settings.json"),e=O.loadFromFile(a);return parseInt(e.CLAUDE_MEM_WORKER_PORT,10)}async function X(){try{let a=f();return(await fetch(`http://127.0.0.1:${a}/health`,{signal:AbortSignal.timeout(te)})).ok}catch{return!1}}async function ne(){try{let a=N.join(h,"ecosystem.config.cjs");if(!w(a))throw new Error(`Ecosystem config not found at ${a}`);let e=N.join(h,"node_modules",".bin","pm2"),s=process.platform==="win32"?e+".cmd":e,t=w(s)?s:"pm2",r=se(t,["start",a],{cwd:h,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(r.status!==0)throw new Error(r.stderr||"PM2 start failed");for(let o=0;o<oe;o++)if(await new Promise(n=>setTimeout(n,re)),await X())return!0;return!1}catch{return!1}}async function P(){if(await X())return;if(!await ne()){let e=f();throw new Error(`Worker service failed to start on port ${e}.
|
||||
|
||||
To start manually, run:
|
||||
cd ${h}
|
||||
npx pm2 start ecosystem.config.cjs
|
||||
|
||||
If already running, try: npx pm2 restart claude-mem-worker`)}}import{appendFileSync as ie}from"fs";import{homedir as ae}from"os";import{join as pe}from"path";var ce=pe(ae(),".claude-mem","silent.log");function g(a,e,s=""){let t=new Date().toISOString(),i=((new Error().stack||"").split(`
|
||||
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),p=i?`${i[1].split("/").pop()}:${i[2]}`:"unknown",d=`[${t}] [${p}] ${a}`;if(e!==void 0)try{d+=` ${JSON.stringify(e)}`}catch(E){d+=` [stringify error: ${E}]`}d+=`
|
||||
`;try{ie(ce,d)}catch(E){console.error("[silent-debug] Failed to write to log:",E)}return s}var H=100;function de(a){let e=(a.match(/<private>/g)||[]).length,s=(a.match(/<claude-mem-context>/g)||[]).length;return e+s}function B(a){if(typeof a!="string")return g("[tag-stripping] received non-string for prompt context:",{type:typeof a}),"";let e=de(a);return e>H&&g("[tag-stripping] tag count exceeds limit, truncating:",{tagCount:e,maxAllowed:H,contentLength:a.length}),a.replace(/<claude-mem-context>[\s\S]*?<\/claude-mem-context>/g,"").replace(/<private>[\s\S]*?<\/private>/g,"").trim()}async function ue(a){if(await P(),!a)throw new Error("newHook requires input");let{session_id:e,cwd:s,prompt:t}=a;g("[new-hook] Input received",{session_id:e,cwd:s,cwd_type:typeof s,cwd_length:s?.length,has_cwd:!!s,prompt_length:t?.length});let r=_e.basename(s);g("[new-hook] Project extracted",{project:r,project_type:typeof r,project_length:r?.length,is_empty:r==="",cwd_was:s});let o=new R,n=o.createSDKSession(e,r,t),i=o.incrementPromptCounter(n),p=B(t);if(!p||p.trim()===""){g("[new-hook] Prompt entirely private, skipping memory operations",{session_id:e,promptNumber:i,originalLength:t.length}),o.close(),console.error(`[new-hook] Session ${n}, prompt #${i} (fully private - skipped)`),console.log(C("UserPromptSubmit",!0));return}o.saveUserPrompt(e,i,p),console.error(`[new-hook] Session ${n}, prompt #${i}`),o.close();let d=f(),E=t.startsWith("/")?t.substring(1):t;try{let _=await fetch(`http://127.0.0.1:${d}/sessions/${n}/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({project:r,userPrompt:E,promptNumber:i}),signal:AbortSignal.timeout(5e3)});if(!_.ok){let T=await _.text();throw new Error(`Failed to initialize session: ${_.status} ${T}`)}}catch(_){throw _.cause?.code==="ECONNREFUSED"||_.name==="TimeoutError"||_.message.includes("fetch failed")?new Error("There's a problem with the worker. If you just updated, type `pm2 restart claude-mem-worker` in your terminal to continue"):_}console.log(C("UserPromptSubmit",!0))}var D="";W.on("data",a=>D+=a);W.on("end",async()=>{let a=D?JSON.parse(D):void 0;await ue(a)});
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
import{stdin as U}from"process";function P(r,t,e){return r==="PreCompact"?t?{continue:!0,suppressOutput:!0}:{continue:!1,stopReason:e.reason||"Pre-compact operation failed",suppressOutput:!0}:r==="SessionStart"?t&&e.context?{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.context}}:{continue:!0,suppressOutput:!0}:r==="UserPromptSubmit"||r==="PostToolUse"?{continue:!0,suppressOutput:!0}:r==="Stop"?{continue:!0,suppressOutput:!0}:{continue:t,suppressOutput:!0,...e.reason&&!t?{stopReason:e.reason}:{}}}function S(r,t,e={}){let o=P(r,t,e);return JSON.stringify(o)}var T=(s=>(s[s.DEBUG=0]="DEBUG",s[s.INFO=1]="INFO",s[s.WARN=2]="WARN",s[s.ERROR=3]="ERROR",s[s.SILENT=4]="SILENT",s))(T||{}),g=class{level;useColor;constructor(){let t=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=T[t]??1,this.useColor=process.stdout.isTTY??!1}correlationId(t,e){return`obs-${t}-${e}`}sessionId(t){return`session-${t}`}formatData(t){if(t==null)return"";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return t.toString();if(typeof t=="object"){if(t instanceof Error)return this.level===0?`${t.message}
|
||||
${t.stack}`:t.message;if(Array.isArray(t))return`[${t.length} items]`;let e=Object.keys(t);return e.length===0?"{}":e.length<=3?JSON.stringify(t):`{${e.length} keys: ${e.slice(0,3).join(", ")}...}`}return String(t)}formatTool(t,e){if(!e)return t;try{let o=typeof e=="string"?JSON.parse(e):e;if(t==="Bash"&&o.command){let n=o.command.length>50?o.command.substring(0,50)+"...":o.command;return`${t}(${n})`}if(t==="Read"&&o.file_path){let n=o.file_path.split("/").pop()||o.file_path;return`${t}(${n})`}if(t==="Edit"&&o.file_path){let n=o.file_path.split("/").pop()||o.file_path;return`${t}(${n})`}if(t==="Write"&&o.file_path){let n=o.file_path.split("/").pop()||o.file_path;return`${t}(${n})`}return t}catch{return t}}log(t,e,o,n,s){if(t<this.level)return;let a=new Date().toISOString().replace("T"," ").substring(0,23),f=T[t].padEnd(5),i=e.padEnd(6),u="";n?.correlationId?u=`[${n.correlationId}] `:n?.sessionId&&(u=`[session-${n.sessionId}] `);let O="";s!=null&&(this.level===0&&typeof s=="object"?O=`
|
||||
`+JSON.stringify(s,null,2):O=" "+this.formatData(s));let C="";if(n){let{sessionId:K,sdkSessionId:j,correlationId:V,...A}=n;Object.keys(A).length>0&&(C=` {${Object.entries(A).map(([D,I])=>`${D}=${I}`).join(", ")}}`)}let d=`[${a}] [${f}] [${i}] ${u}${o}${C}${O}`;t===3?console.error(d):console.log(d)}debug(t,e,o,n){this.log(0,t,e,o,n)}info(t,e,o,n){this.log(1,t,e,o,n)}warn(t,e,o,n){this.log(2,t,e,o,n)}error(t,e,o,n){this.log(3,t,e,o,n)}dataIn(t,e,o,n){this.info(t,`\u2192 ${e}`,o,n)}dataOut(t,e,o,n){this.info(t,`\u2190 ${e}`,o,n)}success(t,e,o,n){this.info(t,`\u2713 ${e}`,o,n)}failure(t,e,o,n){this.error(t,`\u2717 ${e}`,o,n)}timing(t,e,o,n){this.info(t,`\u23F1 ${e}`,n,{duration:`${o}ms`})}},c=new g;import _ from"path";import{existsSync as h}from"fs";import{homedir as R}from"os";import{spawnSync as x}from"child_process";import{readFileSync as v,existsSync as w}from"fs";var b=["bugfix","feature","refactor","discovery","decision","change"],k=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var y=b.join(","),M=k.join(",");var p=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:y,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:M,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return process.env[t]||this.DEFAULTS[t]}static getInt(t){let e=this.get(t);return parseInt(e,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!w(t))return this.getAllDefaults();let e=v(t,"utf-8"),n=JSON.parse(e).env||{},s={...this.DEFAULTS};for(let a of Object.keys(this.DEFAULTS))n[a]!==void 0&&(s[a]=n[a]);return s}};var E=_.join(R(),".claude","plugins","marketplaces","thedotmack"),$=100,H=500,W=10;function l(){let r=_.join(R(),".claude-mem","settings.json"),t=p.loadFromFile(r);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function L(){try{let r=l();return(await fetch(`http://127.0.0.1:${r}/health`,{signal:AbortSignal.timeout($)})).ok}catch{return!1}}async function F(){try{let r=_.join(E,"ecosystem.config.cjs");if(!h(r))throw new Error(`Ecosystem config not found at ${r}`);let t=_.join(E,"node_modules",".bin","pm2"),e=process.platform==="win32"?t+".cmd":t,o=h(e)?e:"pm2",n=x(o,["start",r],{cwd:E,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(n.status!==0)throw new Error(n.stderr||"PM2 start failed");for(let s=0;s<W;s++)if(await new Promise(a=>setTimeout(a,H)),await L())return!0;return!1}catch{return!1}}async function N(){if(await L())return;if(!await F()){let t=l();throw new Error(`Worker service failed to start on port ${t}.
|
||||
|
||||
To start manually, run:
|
||||
cd ${E}
|
||||
npx pm2 start ecosystem.config.cjs
|
||||
|
||||
If already running, try: npx pm2 restart claude-mem-worker`)}}var X=new Set(["ListMcpResourcesTool","SlashCommand","Skill","TodoWrite","AskUserQuestion"]);async function B(r){if(await N(),!r)throw new Error("saveHook requires input");let{session_id:t,cwd:e,tool_name:o,tool_input:n,tool_response:s}=r;if(X.has(o)){console.log(S("PostToolUse",!0));return}let a=l(),f=c.formatTool(o,n);c.dataIn("HOOK",`PostToolUse: ${f}`,{workerPort:a});try{let i=await fetch(`http://127.0.0.1:${a}/api/sessions/observations`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,tool_name:o,tool_input:n,tool_response:s,cwd:e||""}),signal:AbortSignal.timeout(2e3)});if(!i.ok){let u=await i.text();throw c.failure("HOOK","Failed to send observation",{status:i.status},u),new Error(`Failed to send observation to worker: ${i.status} ${u}`)}c.debug("HOOK","Observation sent successfully",{toolName:o})}catch(i){throw i.cause?.code==="ECONNREFUSED"||i.name==="TimeoutError"||i.message.includes("fetch failed")?new Error("There's a problem with the worker. If you just updated, type `pm2 restart claude-mem-worker` in your terminal to continue"):i}console.log(S("PostToolUse",!0))}var m="";U.on("data",r=>m+=r);U.on("end",async()=>{let r=m?JSON.parse(m):void 0;await B(r)});
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env node
|
||||
import{stdin as D}from"process";import{readFileSync as U,existsSync as I}from"fs";function k(o,t,e){return o==="PreCompact"?t?{continue:!0,suppressOutput:!0}:{continue:!1,stopReason:e.reason||"Pre-compact operation failed",suppressOutput:!0}:o==="SessionStart"?t&&e.context?{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.context}}:{continue:!0,suppressOutput:!0}:o==="UserPromptSubmit"||o==="PostToolUse"?{continue:!0,suppressOutput:!0}:o==="Stop"?{continue:!0,suppressOutput:!0}:{continue:t,suppressOutput:!0,...e.reason&&!t?{stopReason:e.reason}:{}}}function d(o,t,e={}){let n=k(o,t,e);return JSON.stringify(n)}var O=(s=>(s[s.DEBUG=0]="DEBUG",s[s.INFO=1]="INFO",s[s.WARN=2]="WARN",s[s.ERROR=3]="ERROR",s[s.SILENT=4]="SILENT",s))(O||{}),S=class{level;useColor;constructor(){let t=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=O[t]??1,this.useColor=process.stdout.isTTY??!1}correlationId(t,e){return`obs-${t}-${e}`}sessionId(t){return`session-${t}`}formatData(t){if(t==null)return"";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return t.toString();if(typeof t=="object"){if(t instanceof Error)return this.level===0?`${t.message}
|
||||
${t.stack}`:t.message;if(Array.isArray(t))return`[${t.length} items]`;let e=Object.keys(t);return e.length===0?"{}":e.length<=3?JSON.stringify(t):`{${e.length} keys: ${e.slice(0,3).join(", ")}...}`}return String(t)}formatTool(t,e){if(!e)return t;try{let n=typeof e=="string"?JSON.parse(e):e;if(t==="Bash"&&n.command){let r=n.command.length>50?n.command.substring(0,50)+"...":n.command;return`${t}(${r})`}if(t==="Read"&&n.file_path){let r=n.file_path.split("/").pop()||n.file_path;return`${t}(${r})`}if(t==="Edit"&&n.file_path){let r=n.file_path.split("/").pop()||n.file_path;return`${t}(${r})`}if(t==="Write"&&n.file_path){let r=n.file_path.split("/").pop()||n.file_path;return`${t}(${r})`}return t}catch{return t}}log(t,e,n,r,s){if(t<this.level)return;let i=new Date().toISOString().replace("T"," ").substring(0,23),a=O[t].padEnd(5),u=e.padEnd(6),l="";r?.correlationId?l=`[${r.correlationId}] `:r?.sessionId&&(l=`[session-${r.sessionId}] `);let g="";s!=null&&(this.level===0&&typeof s=="object"?g=`
|
||||
`+JSON.stringify(s,null,2):g=" "+this.formatData(s));let T="";if(r){let{sessionId:G,sdkSessionId:Y,correlationId:J,...C}=r;Object.keys(C).length>0&&(T=` {${Object.entries(C).map(([x,b])=>`${x}=${b}`).join(", ")}}`)}let y=`[${i}] [${a}] [${u}] ${l}${n}${T}${g}`;t===3?console.error(y):console.log(y)}debug(t,e,n,r){this.log(0,t,e,n,r)}info(t,e,n,r){this.log(1,t,e,n,r)}warn(t,e,n,r){this.log(2,t,e,n,r)}error(t,e,n,r){this.log(3,t,e,n,r)}dataIn(t,e,n,r){this.info(t,`\u2192 ${e}`,n,r)}dataOut(t,e,n,r){this.info(t,`\u2190 ${e}`,n,r)}success(t,e,n,r){this.info(t,`\u2713 ${e}`,n,r)}failure(t,e,n,r){this.error(t,`\u2717 ${e}`,n,r)}timing(t,e,n,r){this.info(t,`\u23F1 ${e}`,r,{duration:`${n}ms`})}},c=new S;import f from"path";import{existsSync as M}from"fs";import{homedir as R}from"os";import{spawnSync as H}from"child_process";import{readFileSync as w,existsSync as $}from"fs";var P=["bugfix","feature","refactor","discovery","decision","change"],v=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var A=P.join(","),h=v.join(",");var p=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:A,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:h,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return process.env[t]||this.DEFAULTS[t]}static getInt(t){let e=this.get(t);return parseInt(e,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!$(t))return this.getAllDefaults();let e=w(t,"utf-8"),r=JSON.parse(e).env||{},s={...this.DEFAULTS};for(let i of Object.keys(this.DEFAULTS))r[i]!==void 0&&(s[i]=r[i]);return s}};var E=f.join(R(),".claude","plugins","marketplaces","thedotmack"),W=100,F=500,X=10;function _(){let o=f.join(R(),".claude-mem","settings.json"),t=p.loadFromFile(o);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function L(){try{let o=_();return(await fetch(`http://127.0.0.1:${o}/health`,{signal:AbortSignal.timeout(W)})).ok}catch{return!1}}async function j(){try{let o=f.join(E,"ecosystem.config.cjs");if(!M(o))throw new Error(`Ecosystem config not found at ${o}`);let t=f.join(E,"node_modules",".bin","pm2"),e=process.platform==="win32"?t+".cmd":t,n=M(e)?e:"pm2",r=H(n,["start",o],{cwd:E,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(r.status!==0)throw new Error(r.stderr||"PM2 start failed");for(let s=0;s<X;s++)if(await new Promise(i=>setTimeout(i,F)),await L())return!0;return!1}catch{return!1}}async function N(){if(await L())return;if(!await j()){let t=_();throw new Error(`Worker service failed to start on port ${t}.
|
||||
|
||||
To start manually, run:
|
||||
cd ${E}
|
||||
npx pm2 start ecosystem.config.cjs
|
||||
|
||||
If already running, try: npx pm2 restart claude-mem-worker`)}}function B(o){if(!o||!I(o))return"";try{let t=U(o,"utf-8").trim();if(!t)return"";let e=t.split(`
|
||||
`);for(let n=e.length-1;n>=0;n--)try{let r=JSON.parse(e[n]);if(r.type==="user"&&r.message?.content){let s=r.message.content;if(typeof s=="string")return s;if(Array.isArray(s))return s.filter(a=>a.type==="text").map(a=>a.text).join(`
|
||||
`)}}catch{continue}}catch(t){c.error("HOOK","Failed to read transcript",{transcriptPath:o},t)}return""}function K(o){if(!o||!I(o))return"";try{let t=U(o,"utf-8").trim();if(!t)return"";let e=t.split(`
|
||||
`);for(let n=e.length-1;n>=0;n--)try{let r=JSON.parse(e[n]);if(r.type==="assistant"&&r.message?.content){let s="",i=r.message.content;return typeof i=="string"?s=i:Array.isArray(i)&&(s=i.filter(u=>u.type==="text").map(u=>u.text).join(`
|
||||
`)),s=s.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g,""),s=s.replace(/\n{3,}/g,`
|
||||
|
||||
`).trim(),s}}catch{continue}}catch(t){c.error("HOOK","Failed to read transcript",{transcriptPath:o},t)}return""}async function V(o){if(await N(),!o)throw new Error("summaryHook requires input");let{session_id:t}=o,e=_(),n=B(o.transcript_path||""),r=K(o.transcript_path||"");c.dataIn("HOOK","Stop: Requesting summary",{workerPort:e,hasLastUserMessage:!!n,hasLastAssistantMessage:!!r});try{let s=await fetch(`http://127.0.0.1:${e}/api/sessions/summarize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,last_user_message:n,last_assistant_message:r}),signal:AbortSignal.timeout(2e3)});if(!s.ok){let i=await s.text();throw c.failure("HOOK","Failed to generate summary",{status:s.status},i),new Error(`Failed to request summary from worker: ${s.status} ${i}`)}c.debug("HOOK","Summary request sent successfully")}catch(s){throw s.cause?.code==="ECONNREFUSED"||s.name==="TimeoutError"||s.message.includes("fetch failed")?new Error("There's a problem with the worker. If you just updated, type `pm2 restart claude-mem-worker` in your terminal to continue"):s}finally{fetch(`http://127.0.0.1:${e}/api/processing`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({isProcessing:!1})}).catch(()=>{})}console.log(d("Stop",!0))}var m="";D.on("data",o=>m+=o);D.on("end",async()=>{let o=m?JSON.parse(m):void 0;await V(o)});
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env node
|
||||
import{join as M,basename as k}from"path";import{homedir as v}from"os";import{existsSync as x}from"fs";import c from"path";import{existsSync as m}from"fs";import{homedir as C}from"os";import{spawnSync as R}from"child_process";import{readFileSync as L,existsSync as y}from"fs";var h=["bugfix","feature","refactor","discovery","decision","change"],U=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var p=h.join(","),S=U.join(",");var a=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:p,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:S,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return process.env[t]||this.DEFAULTS[t]}static getInt(t){let n=this.get(t);return parseInt(n,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!y(t))return this.getAllDefaults();let n=L(t,"utf-8"),o=JSON.parse(n).env||{},r={...this.DEFAULTS};for(let s of Object.keys(this.DEFAULTS))o[s]!==void 0&&(r[s]=o[s]);return r}};var E=c.join(C(),".claude","plugins","marketplaces","thedotmack"),w=100,I=500,P=10;function _(){let e=c.join(C(),".claude-mem","settings.json"),t=a.loadFromFile(e);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function f(){try{let e=_();return(await fetch(`http://127.0.0.1:${e}/health`,{signal:AbortSignal.timeout(w)})).ok}catch{return!1}}async function W(){try{let e=c.join(E,"ecosystem.config.cjs");if(!m(e))throw new Error(`Ecosystem config not found at ${e}`);let t=c.join(E,"node_modules",".bin","pm2"),n=process.platform==="win32"?t+".cmd":t,i=m(n)?n:"pm2",o=R(i,["start",e],{cwd:E,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(o.status!==0)throw new Error(o.stderr||"PM2 start failed");for(let r=0;r<P;r++)if(await new Promise(s=>setTimeout(s,I)),await f())return!0;return!1}catch{return!1}}async function A(){if(await f())return;if(!await W()){let t=_();throw new Error(`Worker service failed to start on port ${t}.
|
||||
|
||||
To start manually, run:
|
||||
cd ${E}
|
||||
npx pm2 start ecosystem.config.cjs
|
||||
|
||||
If already running, try: npx pm2 restart claude-mem-worker`)}}var b=M(v(),".claude","plugins","marketplaces","thedotmack"),X=M(b,"node_modules");x(X)||(console.error(`
|
||||
---
|
||||
\u{1F389} Note: This appears under Plugin Hook Error, but it's not an error. That's the only option for
|
||||
user messages in Claude Code UI until a better method is provided.
|
||||
---
|
||||
|
||||
\u26A0\uFE0F Claude-Mem: First-Time Setup
|
||||
|
||||
Dependencies have been installed in the background. This only happens once.
|
||||
|
||||
\u{1F4A1} TIPS:
|
||||
\u2022 Memories will start generating while you work
|
||||
\u2022 Use /init to write or update your CLAUDE.md for better project context
|
||||
\u2022 Try /clear after one session to see what context looks like
|
||||
|
||||
Thank you for installing Claude-Mem!
|
||||
|
||||
This message was not added to your startup context, so you can continue working as normal.
|
||||
`),process.exit(3));try{await A();let e=_(),t=k(process.cwd()),n=await fetch(`http://127.0.0.1:${e}/api/context/inject?project=${encodeURIComponent(t)}&colors=true`,{method:"GET",signal:AbortSignal.timeout(5e3)});if(!n.ok)throw new Error(`Worker error ${n.status}`);let i=await n.text(),o=new Date,r=new Date("2025-12-06T00:00:00Z"),s=new Date("2025-12-05T05:00:00Z"),u="";o<s&&(u=`
|
||||
|
||||
\u{1F680} \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 \u{1F680}
|
||||
|
||||
We launched on Product Hunt!
|
||||
https://tinyurl.com/claude-mem-ph
|
||||
|
||||
\u2B50 Your upvote means the world - thank you!
|
||||
|
||||
\u{1F680} \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501 \u{1F680}
|
||||
`);let T="";if(o<r){let d=o.getUTCHours()*60+o.getUTCMinutes(),l=Math.floor((d-300+1440)%1440/60),O=o.getUTCDate(),g=o.getUTCMonth(),N=o.getUTCFullYear()===2025&&g===11&&O>=1&&O<=5,D=l>=17&&l<19;N&&D?T=`
|
||||
\u{1F534} LIVE NOW: AMA w/ Dev (@thedotmack) until 7pm EST
|
||||
`:T=`
|
||||
\u2013 LIVE AMA w/ Dev (@thedotmack) Dec 1st\u20135th, 5pm to 7pm EST
|
||||
`}console.error(`
|
||||
|
||||
\u{1F4DD} Claude-Mem Context Loaded
|
||||
\u2139\uFE0F Note: This appears as stderr but is informational only
|
||||
|
||||
`+i+`
|
||||
|
||||
\u{1F4A1} New! Wrap all or part of any message with <private> ... </private> to prevent storing sensitive information in your observation history.
|
||||
|
||||
\u{1F4AC} Community https://discord.gg/J4wttp9vDu`+u+T+`
|
||||
\u{1F4FA} Watch live in browser http://localhost:${e}/
|
||||
`)}catch(e){console.error(`\u274C Failed to load context display: ${e}`)}process.exit(3);
|
||||
@@ -0,0 +1,143 @@
|
||||
---
|
||||
name: mem-search
|
||||
description: Search claude-mem's persistent cross-session memory database. Use when user asks "did we already solve this?", "how did we do X last time?", or needs work from previous sessions.
|
||||
---
|
||||
|
||||
# Memory Search
|
||||
|
||||
Search past work across all sessions. Simple workflow: search → get IDs → fetch details by ID.
|
||||
|
||||
## When to Use
|
||||
|
||||
Use when users ask about PREVIOUS sessions (not current conversation):
|
||||
- "Did we already fix this?"
|
||||
- "How did we solve X last time?"
|
||||
- "What happened last week?"
|
||||
|
||||
## The Workflow
|
||||
|
||||
**ALWAYS follow this exact flow:**
|
||||
|
||||
1. **Search** - Get an index of results with IDs
|
||||
2. **Timeline** (optional) - Get context around top results to understand what was happening
|
||||
3. **Review** - Look at titles/dates/context, pick relevant IDs
|
||||
4. **Fetch** - Get full details ONLY for those IDs
|
||||
|
||||
### Step 1: Search Everything
|
||||
|
||||
```bash
|
||||
curl "http://localhost:37777/api/search?query=authentication&format=index&limit=5"
|
||||
```
|
||||
|
||||
**Required parameters:**
|
||||
- `query` - Search term
|
||||
- `format=index` - ALWAYS start with index (lightweight)
|
||||
- `limit=5` - Start small (3-5 results)
|
||||
|
||||
**Returns:**
|
||||
```
|
||||
1. [feature] Added JWT authentication
|
||||
Date: 11/17/2025, 3:48:45 PM
|
||||
ID: 11131
|
||||
|
||||
2. [bugfix] Fixed auth token expiration
|
||||
Date: 11/16/2025, 2:15:22 PM
|
||||
ID: 10942
|
||||
```
|
||||
|
||||
### Step 2: Get Timeline Context (Optional)
|
||||
|
||||
When you need to understand "what was happening" around a result:
|
||||
|
||||
```bash
|
||||
# Get timeline around an observation ID
|
||||
curl "http://localhost:37777/api/timeline?anchor=11131&depth_before=3&depth_after=3"
|
||||
|
||||
# Or use query to find + get timeline in one step
|
||||
curl "http://localhost:37777/api/timeline?query=authentication&depth_before=3&depth_after=3"
|
||||
```
|
||||
|
||||
**Returns exactly `depth_before + 1 + depth_after` items** - observations, sessions, and prompts interleaved chronologically around the anchor.
|
||||
|
||||
**When to use:**
|
||||
- User asks "what was happening when..."
|
||||
- Need to understand sequence of events
|
||||
- Want broader context around a specific observation
|
||||
|
||||
### Step 3: Pick IDs
|
||||
|
||||
Review the index results (and timeline if used). Identify which IDs are actually relevant. Discard the rest.
|
||||
|
||||
### Step 4: Fetch by ID
|
||||
|
||||
For each relevant ID, fetch full details:
|
||||
|
||||
```bash
|
||||
# Fetch observation
|
||||
curl "http://localhost:37777/api/observation/11131"
|
||||
|
||||
# Fetch session
|
||||
curl "http://localhost:37777/api/session/2005"
|
||||
|
||||
# Fetch prompt
|
||||
curl "http://localhost:37777/api/prompt/5421"
|
||||
```
|
||||
|
||||
**ID formats:**
|
||||
- Observations: Just the number (11131)
|
||||
- Sessions: Just the number (2005) from "S2005"
|
||||
- Prompts: Just the number (5421)
|
||||
|
||||
## Search Parameters
|
||||
|
||||
**Basic:**
|
||||
- `query` - What to search for (required)
|
||||
- `format` - "index" or "full" (always use "index" first)
|
||||
- `limit` - How many results (default 5, max 100)
|
||||
|
||||
**Filters (optional):**
|
||||
- `type` - Filter to "observations", "sessions", or "prompts"
|
||||
- `project` - Filter by project name
|
||||
- `dateStart` - Start date (YYYY-MM-DD or epoch timestamp)
|
||||
- `dateEnd` - End date (YYYY-MM-DD or epoch timestamp)
|
||||
- `obs_type` - Filter observations by type (comma-separated): bugfix, feature, decision, discovery, change
|
||||
|
||||
## Examples
|
||||
|
||||
**Find recent bug fixes:**
|
||||
```bash
|
||||
curl "http://localhost:37777/api/search?query=bug&type=observations&obs_type=bugfix&format=index&limit=5"
|
||||
```
|
||||
|
||||
**Find what happened last week:**
|
||||
```bash
|
||||
curl "http://localhost:37777/api/search?query=&type=observations&dateStart=2025-11-11&format=index&limit=10"
|
||||
```
|
||||
|
||||
**Search everything:**
|
||||
```bash
|
||||
curl "http://localhost:37777/api/search?query=database+migration&format=index&limit=5"
|
||||
```
|
||||
|
||||
## Why This Workflow?
|
||||
|
||||
**Token efficiency:**
|
||||
- Index format: ~50-100 tokens per result
|
||||
- Full format: ~500-1000 tokens per result
|
||||
- **10x difference** - only fetch full when you know it's relevant
|
||||
|
||||
**Clarity:**
|
||||
- See everything first
|
||||
- Pick what matters
|
||||
- Get details only for what you need
|
||||
|
||||
## Error Handling
|
||||
|
||||
If search fails, tell the user the worker isn't available and suggest:
|
||||
```bash
|
||||
pm2 list # Check if worker is running
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Remember:** ALWAYS search with format=index first. ALWAYS fetch by ID for details. The IDs are there for a reason - USE THEM.
|
||||
@@ -0,0 +1,124 @@
|
||||
# Search by Concept
|
||||
|
||||
Find observations tagged with specific concepts.
|
||||
|
||||
## When to Use
|
||||
|
||||
- User asks: "What discoveries did we make?"
|
||||
- User asks: "What patterns did we identify?"
|
||||
- User asks: "What gotchas did we encounter?"
|
||||
- Looking for observations with semantic tags
|
||||
|
||||
## Command
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/by-concept?concept=discovery&format=index&limit=5"
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- **concept** (required): Concept tag to search for
|
||||
- `discovery` - New discoveries and insights
|
||||
- `problem-solution` - Problems and their solutions
|
||||
- `what-changed` - Change descriptions
|
||||
- `how-it-works` - Explanations of mechanisms
|
||||
- `pattern` - Identified patterns
|
||||
- `gotcha` - Edge cases and gotchas
|
||||
- `change` - General changes
|
||||
- **format**: "index" (summary) or "full" (complete details). Default: "full"
|
||||
- **limit**: Number of results (default: 20, max: 100)
|
||||
- **project**: Filter by project name (optional)
|
||||
- **dateStart/dateEnd**: Filter by date range (optional)
|
||||
|
||||
## When to Use Each Format
|
||||
|
||||
**Use format=index for:**
|
||||
- Quick overviews of observations by concept
|
||||
- Finding IDs for deeper investigation
|
||||
- Listing multiple results
|
||||
- **Token cost: ~50-100 per result**
|
||||
|
||||
**Use format=full for:**
|
||||
- Complete details including narrative, facts, files, concepts
|
||||
- Understanding the full context of specific observations
|
||||
- **Token cost: ~500-1000 per result**
|
||||
|
||||
## Example Response (format=index)
|
||||
|
||||
```json
|
||||
{
|
||||
"concept": "discovery",
|
||||
"count": 3,
|
||||
"format": "index",
|
||||
"results": [
|
||||
{
|
||||
"id": 1240,
|
||||
"type": "discovery",
|
||||
"title": "Worker service uses PM2 for process management",
|
||||
"subtitle": "Discovered persistent background worker pattern",
|
||||
"created_at_epoch": 1699564800000,
|
||||
"project": "claude-mem",
|
||||
"concepts": ["discovery", "how-it-works"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## How to Present Results
|
||||
|
||||
For format=index, present as a compact list:
|
||||
|
||||
```markdown
|
||||
Found 3 observations tagged with "discovery":
|
||||
|
||||
🔵 **#1240** Worker service uses PM2 for process management
|
||||
> Discovered persistent background worker pattern
|
||||
> Nov 9, 2024 • claude-mem
|
||||
> Tags: discovery, how-it-works
|
||||
|
||||
🔵 **#1241** FTS5 full-text search enables instant searches
|
||||
> SQLite FTS5 virtual tables provide sub-100ms search
|
||||
> Nov 9, 2024 • claude-mem
|
||||
> Tags: discovery, pattern
|
||||
```
|
||||
|
||||
For complete formatting guidelines, see [formatting.md](formatting.md).
|
||||
|
||||
## Available Concepts
|
||||
|
||||
| Concept | Description | When to Use |
|
||||
|---------|-------------|-------------|
|
||||
| `discovery` | New discoveries and insights | Finding what was learned |
|
||||
| `problem-solution` | Problems and their solutions | Finding how issues were resolved |
|
||||
| `what-changed` | Change descriptions | Understanding what changed |
|
||||
| `how-it-works` | Explanations of mechanisms | Learning how things work |
|
||||
| `pattern` | Identified patterns | Finding design patterns |
|
||||
| `gotcha` | Edge cases and gotchas | Learning about pitfalls |
|
||||
| `change` | General changes | Tracking modifications |
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Missing concept parameter:**
|
||||
```json
|
||||
{"error": "Missing required parameter: concept"}
|
||||
```
|
||||
Fix: Add the concept parameter
|
||||
|
||||
**Invalid concept:**
|
||||
```json
|
||||
{"error": "Invalid concept: foobar. Valid concepts: discovery, problem-solution, what-changed, how-it-works, pattern, gotcha, change"}
|
||||
```
|
||||
Fix: Use one of the valid concept values
|
||||
|
||||
## Tips
|
||||
|
||||
1. Use format=index first to see overview
|
||||
2. Start with limit=5-10 to avoid token overload
|
||||
3. Combine concepts with type filtering for precision
|
||||
4. Use `discovery` for learning what was found during investigation
|
||||
5. Use `problem-solution` for finding how past issues were resolved
|
||||
|
||||
**Token Efficiency:**
|
||||
- Start with format=index (~50-100 tokens per result)
|
||||
- Use format=full only for relevant items (~500-1000 tokens per result)
|
||||
- See [../principles/progressive-disclosure.md](../principles/progressive-disclosure.md)
|
||||
@@ -0,0 +1,127 @@
|
||||
# Search by File
|
||||
|
||||
Find all work related to a specific file path.
|
||||
|
||||
## When to Use
|
||||
|
||||
- User asks: "What changes to auth/login.ts?"
|
||||
- User asks: "What work was done on this file?"
|
||||
- User asks: "Show me the history of src/services/worker.ts"
|
||||
- Looking for all observations that reference a file
|
||||
|
||||
## Command
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/by-file?filePath=src/services/worker-service.ts&format=index&limit=10"
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- **filePath** (required): File path to search for (supports partial matching)
|
||||
- Full path: `src/services/worker-service.ts`
|
||||
- Partial path: `worker-service.ts`
|
||||
- Directory: `src/hooks/`
|
||||
- **format**: "index" (summary) or "full" (complete details). Default: "full"
|
||||
- **limit**: Number of results (default: 20, max: 100)
|
||||
- **project**: Filter by project name (optional)
|
||||
- **dateStart/dateEnd**: Filter by date range (optional)
|
||||
|
||||
## When to Use Each Format
|
||||
|
||||
**Use format=index for:**
|
||||
- Quick overviews of work on a file
|
||||
- Finding IDs for deeper investigation
|
||||
- Listing multiple changes
|
||||
- **Token cost: ~50-100 per result**
|
||||
|
||||
**Use format=full for:**
|
||||
- Complete details including narrative, facts, files, concepts
|
||||
- Understanding the full context of specific changes
|
||||
- **Token cost: ~500-1000 per result**
|
||||
|
||||
## Example Response (format=index)
|
||||
|
||||
```json
|
||||
{
|
||||
"filePath": "src/services/worker-service.ts",
|
||||
"count": 8,
|
||||
"format": "index",
|
||||
"results": [
|
||||
{
|
||||
"id": 1245,
|
||||
"type": "refactor",
|
||||
"title": "Simplified worker health check logic",
|
||||
"subtitle": "Removed redundant PM2 status check",
|
||||
"created_at_epoch": 1699564800000,
|
||||
"project": "claude-mem",
|
||||
"files": ["src/services/worker-service.ts", "src/services/worker-utils.ts"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## How to Present Results
|
||||
|
||||
For format=index, present as a compact list:
|
||||
|
||||
```markdown
|
||||
Found 8 observations related to "src/services/worker-service.ts":
|
||||
|
||||
🔄 **#1245** Simplified worker health check logic
|
||||
> Removed redundant PM2 status check
|
||||
> Nov 9, 2024 • claude-mem
|
||||
> Files: worker-service.ts, worker-utils.ts
|
||||
|
||||
🟣 **#1246** Added SSE endpoint for real-time updates
|
||||
> Implemented Server-Sent Events for viewer UI
|
||||
> Nov 8, 2024 • claude-mem
|
||||
> Files: worker-service.ts
|
||||
```
|
||||
|
||||
For complete formatting guidelines, see [formatting.md](formatting.md).
|
||||
|
||||
## Partial Path Matching
|
||||
|
||||
The file path parameter supports partial matching:
|
||||
|
||||
```bash
|
||||
# These all match "src/services/worker-service.ts"
|
||||
curl -s "http://localhost:37777/api/search/by-file?filePath=worker-service.ts&format=index"
|
||||
curl -s "http://localhost:37777/api/search/by-file?filePath=services/worker&format=index"
|
||||
curl -s "http://localhost:37777/api/search/by-file?filePath=worker-service&format=index"
|
||||
```
|
||||
|
||||
## Directory Searches
|
||||
|
||||
Search for all work in a directory:
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/by-file?filePath=src/hooks/&format=index&limit=20"
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Missing filePath parameter:**
|
||||
```json
|
||||
{"error": "Missing required parameter: filePath"}
|
||||
```
|
||||
Fix: Add the filePath parameter
|
||||
|
||||
**No results found:**
|
||||
```json
|
||||
{"filePath": "nonexistent.ts", "count": 0, "results": []}
|
||||
```
|
||||
Response: "No observations found for 'nonexistent.ts'. Try a partial path or check the spelling."
|
||||
|
||||
## Tips
|
||||
|
||||
1. Use format=index first to see overview of all changes
|
||||
2. Start with partial paths (e.g., filename only) for broader matches
|
||||
3. Use full paths when you need specific file matches
|
||||
4. Combine with dateStart to see recent changes: `?filePath=worker.ts&dateStart=2024-11-01`
|
||||
5. Use directory searches to see all work in a module
|
||||
|
||||
**Token Efficiency:**
|
||||
- Start with format=index (~50-100 tokens per result)
|
||||
- Use format=full only for relevant items (~500-1000 tokens per result)
|
||||
- See [../principles/progressive-disclosure.md](../principles/progressive-disclosure.md)
|
||||
@@ -0,0 +1,123 @@
|
||||
# Search by Type
|
||||
|
||||
Find observations by type: bugfix, feature, refactor, decision, discovery, or change.
|
||||
|
||||
## When to Use
|
||||
|
||||
- User asks: "What bugs did we fix?"
|
||||
- User asks: "What features did we add?"
|
||||
- User asks: "What decisions did we make?"
|
||||
- Looking for specific types of work
|
||||
|
||||
## Command
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/by-type?type=bugfix&format=index&limit=5"
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- **type** (required): One or more types (comma-separated)
|
||||
- `bugfix` - Bug fixes
|
||||
- `feature` - New features
|
||||
- `refactor` - Code refactoring
|
||||
- `decision` - Architectural/design decisions
|
||||
- `discovery` - Discoveries and insights
|
||||
- `change` - General changes
|
||||
- **format**: "index" (summary) or "full" (complete details). Default: "full"
|
||||
- **limit**: Number of results (default: 20, max: 100)
|
||||
- **project**: Filter by project name (optional)
|
||||
- **dateStart/dateEnd**: Filter by date range (optional)
|
||||
|
||||
## When to Use Each Format
|
||||
|
||||
**Use format=index for:**
|
||||
- Quick overviews of work by type
|
||||
- Finding IDs for deeper investigation
|
||||
- Listing multiple results
|
||||
- **Token cost: ~50-100 per result**
|
||||
|
||||
**Use format=full for:**
|
||||
- Complete details including narrative, facts, files, concepts
|
||||
- Understanding the full context of specific observations
|
||||
- **Token cost: ~500-1000 per result**
|
||||
|
||||
## Example Response (format=index)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "bugfix",
|
||||
"count": 5,
|
||||
"format": "index",
|
||||
"results": [
|
||||
{
|
||||
"id": 1235,
|
||||
"type": "bugfix",
|
||||
"title": "Fixed token expiration edge case",
|
||||
"subtitle": "Handled race condition in refresh flow",
|
||||
"created_at_epoch": 1699564800000,
|
||||
"project": "api-server"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## How to Present Results
|
||||
|
||||
For format=index, present as a compact list with type emojis:
|
||||
|
||||
```markdown
|
||||
Found 5 bugfixes:
|
||||
|
||||
🔴 **#1235** Fixed token expiration edge case
|
||||
> Handled race condition in refresh flow
|
||||
> Nov 9, 2024 • api-server
|
||||
|
||||
🔴 **#1236** Resolved memory leak in worker
|
||||
> Fixed event listener cleanup
|
||||
> Nov 8, 2024 • worker-service
|
||||
```
|
||||
|
||||
**Type Emojis:**
|
||||
- 🔴 bugfix
|
||||
- 🟣 feature
|
||||
- 🔄 refactor
|
||||
- 🔵 discovery
|
||||
- 🧠 decision
|
||||
- ✅ change
|
||||
|
||||
For complete formatting guidelines, see [formatting.md](formatting.md).
|
||||
|
||||
## Multiple Types
|
||||
|
||||
To search for multiple types:
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/by-type?type=bugfix,feature&format=index&limit=10"
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Missing type parameter:**
|
||||
```json
|
||||
{"error": "Missing required parameter: type"}
|
||||
```
|
||||
Fix: Add the type parameter
|
||||
|
||||
**Invalid type:**
|
||||
```json
|
||||
{"error": "Invalid type: foobar. Valid types: bugfix, feature, refactor, decision, discovery, change"}
|
||||
```
|
||||
Fix: Use one of the valid type values
|
||||
|
||||
## Tips
|
||||
|
||||
1. Use format=index first to see overview
|
||||
2. Start with limit=5-10 to avoid token overload
|
||||
3. Combine with dateStart for recent work: `?type=bugfix&dateStart=2024-11-01`
|
||||
4. Use project filtering when working on one codebase
|
||||
|
||||
**Token Efficiency:**
|
||||
- Start with format=index (~50-100 tokens per result)
|
||||
- Use format=full only for relevant items (~500-1000 tokens per result)
|
||||
- See [../principles/progressive-disclosure.md](../principles/progressive-disclosure.md)
|
||||
@@ -0,0 +1,251 @@
|
||||
# Common Workflows
|
||||
|
||||
Step-by-step guides for typical user requests using the search API.
|
||||
|
||||
## Workflow 1: Understanding Past Work
|
||||
|
||||
**User asks:** "What did we do last session?" or "Catch me up on recent work"
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Get recent context** (fastest path):
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/context/recent?limit=3"
|
||||
```
|
||||
|
||||
2. **Present as narrative:**
|
||||
```markdown
|
||||
## Recent Work
|
||||
|
||||
### Session #545 - Nov 9, 2024
|
||||
Implemented JWT authentication system
|
||||
|
||||
**Completed:**
|
||||
- Added token-based auth with refresh tokens
|
||||
- Created JWT signing and verification logic
|
||||
|
||||
**Key Learning:** JWT expiration requires careful handling of refresh race conditions
|
||||
```
|
||||
|
||||
**Why this workflow:**
|
||||
- Single request gets both sessions and observations
|
||||
- Optimized for "catch me up" questions
|
||||
- ~1,500-2,500 tokens for 3 sessions
|
||||
|
||||
---
|
||||
|
||||
## Workflow 2: Finding Specific Bug Fixes
|
||||
|
||||
**User asks:** "What bugs did we fix?" or "Show me recent bug fixes"
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Search by type** (index format first):
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/by-type?type=bugfix&format=index&limit=5"
|
||||
```
|
||||
|
||||
2. **Review index results**, identify relevant items
|
||||
|
||||
3. **Get full details** for specific bugs:
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/by-type?type=bugfix&format=full&limit=1&offset=2"
|
||||
```
|
||||
|
||||
4. **Present findings:**
|
||||
```markdown
|
||||
Found 5 bug fixes:
|
||||
|
||||
🔴 **#1235** Fixed token expiration edge case
|
||||
> Handled race condition in refresh flow
|
||||
> Nov 9, 2024 • api-server
|
||||
|
||||
[Click for full details on #1235]
|
||||
```
|
||||
|
||||
**Why this workflow:**
|
||||
- Progressive disclosure: index first, full details selectively
|
||||
- Type-specific search is more efficient than generic search
|
||||
- ~250-500 tokens for index, ~750-1000 per full detail
|
||||
|
||||
---
|
||||
|
||||
## Workflow 3: Understanding File History
|
||||
|
||||
**User asks:** "What changes to auth/login.ts?" or "Show me work on this file"
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Search by file** (index format):
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/by-file?filePath=auth/login.ts&format=index&limit=10"
|
||||
```
|
||||
|
||||
2. **Review chronological changes**
|
||||
|
||||
3. **Get full details** for specific changes:
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/by-file?filePath=auth/login.ts&format=full&limit=1&offset=3"
|
||||
```
|
||||
|
||||
4. **Present as file timeline:**
|
||||
```markdown
|
||||
## History of auth/login.ts
|
||||
|
||||
🟣 **#1230** Added JWT authentication (Nov 9)
|
||||
🔴 **#1235** Fixed token expiration bug (Nov 9)
|
||||
🔄 **#1240** Refactored auth flow (Nov 8)
|
||||
```
|
||||
|
||||
**Why this workflow:**
|
||||
- File-specific search finds all related work
|
||||
- Index format shows chronological overview
|
||||
- Selective full details for deep dives
|
||||
|
||||
---
|
||||
|
||||
## Workflow 4: Timeline Investigation
|
||||
|
||||
**User asks:** "What was happening when we deployed?" or "Show me context around that bug fix"
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Find the event** using search:
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/observations?query=deployment&format=index&limit=5"
|
||||
```
|
||||
|
||||
2. **Note observation ID** (e.g., #1234)
|
||||
|
||||
3. **Get timeline context**:
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/timeline/context?anchor=1234&depth_before=10&depth_after=10"
|
||||
```
|
||||
|
||||
4. **Present as chronological narrative:**
|
||||
```markdown
|
||||
## Timeline: Deployment
|
||||
|
||||
### Before (10 records)
|
||||
**2:45 PM** - 🟣 Prepared deployment scripts
|
||||
**2:50 PM** - 💬 User asked: "Are we ready to deploy?"
|
||||
|
||||
### ⭐ Anchor Point (2:55 PM)
|
||||
🎯 **Observation #1234**: Deployed to production
|
||||
|
||||
### After (10 records)
|
||||
**3:00 PM** - 🔴 Fixed post-deployment routing issue
|
||||
```
|
||||
|
||||
**Why this workflow:**
|
||||
- Timeline shows temporal context (what happened before/after)
|
||||
- Captures causality between events
|
||||
- All record types (observations, sessions, prompts) interleaved
|
||||
|
||||
---
|
||||
|
||||
## Workflow 5: Quick Timeline (One Request)
|
||||
|
||||
**User asks:** "Timeline of authentication work"
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Use timeline-by-query** (auto mode):
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/timeline/by-query?query=authentication&mode=auto&depth_before=10&depth_after=10"
|
||||
```
|
||||
|
||||
2. **Present timeline directly:**
|
||||
```markdown
|
||||
## Timeline: Authentication
|
||||
|
||||
**Best Match:** 🟣 Observation #1234 - Implemented JWT authentication
|
||||
|
||||
### Context (21 records)
|
||||
[... timeline around best match ...]
|
||||
```
|
||||
|
||||
**Why this workflow:**
|
||||
- Single request combines search + timeline
|
||||
- Fastest path when query is specific
|
||||
- Auto mode uses top result as anchor
|
||||
|
||||
**Alternative:** Use interactive mode for broad queries:
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/timeline/by-query?query=auth&mode=interactive&limit=5"
|
||||
```
|
||||
Then choose anchor manually.
|
||||
|
||||
---
|
||||
|
||||
## Workflow 6: Search Composition
|
||||
|
||||
**User asks:** "What features did we add to the authentication system recently?"
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Combine filters** for precision:
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/observations?query=authentication&type=feature&dateStart=2024-11-01&format=index&limit=10"
|
||||
```
|
||||
|
||||
2. **Review filtered results**
|
||||
|
||||
3. **Get full details** for relevant features:
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/observations?query=authentication&type=feature&format=full&limit=1&offset=2"
|
||||
```
|
||||
|
||||
4. **Present findings:**
|
||||
```markdown
|
||||
Found 10 authentication features added in November:
|
||||
|
||||
🟣 **#1234** Implemented JWT authentication (Nov 9)
|
||||
🟣 **#1236** Added refresh token rotation (Nov 9)
|
||||
🟣 **#1238** Implemented OAuth2 flow (Nov 7)
|
||||
```
|
||||
|
||||
**Why this workflow:**
|
||||
- Multiple filters narrow results before requesting full details
|
||||
- Type + query + dateStart/dateEnd = precise targeting
|
||||
- Progressive disclosure: index first, full details selectively
|
||||
|
||||
---
|
||||
|
||||
## Workflow Selection Guide
|
||||
|
||||
| User Request | Workflow | Operation | Token Cost |
|
||||
|--------------|----------|-----------|------------|
|
||||
| "What did we do last session?" | #1 | recent-context | 1,500-2,500 |
|
||||
| "What bugs did we fix?" | #2 | by-type | 500-3,000 |
|
||||
| "What changes to file.ts?" | #3 | by-file | 500-3,000 |
|
||||
| "What was happening then?" | #4 | search → timeline | 3,500-6,000 |
|
||||
| "Timeline of X work" | #5 | timeline-by-query | 3,000-4,000 |
|
||||
| "Recent features added?" | #6 | observations + filters | 500-3,000 |
|
||||
|
||||
## General Principles
|
||||
|
||||
1. **Start with index format** - Always use `format=index` first
|
||||
2. **Use specialized tools** - by-type, by-file, by-concept when applicable
|
||||
3. **Compose operations** - Combine search + timeline for investigations
|
||||
4. **Filter early** - Use type, dateStart/dateEnd, project to narrow before expanding
|
||||
5. **Progressive disclosure** - Load full details only for relevant items
|
||||
|
||||
## Token Budget Awareness
|
||||
|
||||
**Quick queries** (500-1,500 tokens):
|
||||
- Recent context (limit=3)
|
||||
- Index search (limit=5-10)
|
||||
- Filtered searches
|
||||
|
||||
**Medium queries** (1,500-4,000 tokens):
|
||||
- Recent context (limit=5-10)
|
||||
- Full details (3-5 items)
|
||||
- Timeline (depth 10/10)
|
||||
|
||||
**Deep queries** (4,000-8,000 tokens):
|
||||
- Timeline (depth 20/20)
|
||||
- Full details (10+ items)
|
||||
- Multiple composed operations
|
||||
|
||||
Always start with minimal token investment, expand only when needed.
|
||||
@@ -0,0 +1,403 @@
|
||||
# Response Formatting Guidelines
|
||||
|
||||
How to present search results to users for maximum clarity and usefulness.
|
||||
|
||||
## General Principles
|
||||
|
||||
1. **Progressive disclosure** - Show index results first, full details on demand
|
||||
2. **Visual hierarchy** - Use emojis, bold, and structure for scannability
|
||||
3. **Context-aware** - Tailor presentation to user's question
|
||||
4. **Actionable** - Include IDs for follow-up queries
|
||||
5. **Token-efficient** - Balance detail with token budget
|
||||
|
||||
---
|
||||
|
||||
## Format: Index Results
|
||||
|
||||
**When to use:** First response to searches, overviews, multiple results
|
||||
|
||||
**Structure:**
|
||||
```markdown
|
||||
Found {count} results for "{query}":
|
||||
|
||||
{emoji} **#{id}** {title}
|
||||
> {subtitle}
|
||||
> {date} • {project}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```markdown
|
||||
Found 5 results for "authentication":
|
||||
|
||||
🟣 **#1234** Implemented JWT authentication
|
||||
> Added token-based auth with refresh tokens
|
||||
> Nov 9, 2024 • api-server
|
||||
|
||||
🔴 **#1235** Fixed token expiration edge case
|
||||
> Handled race condition in refresh flow
|
||||
> Nov 9, 2024 • api-server
|
||||
```
|
||||
|
||||
**Type Emojis:**
|
||||
- 🔴 bugfix
|
||||
- 🟣 feature
|
||||
- 🔄 refactor
|
||||
- 🔵 discovery
|
||||
- 🧠 decision
|
||||
- ✅ change
|
||||
- 🎯 session
|
||||
- 💬 prompt
|
||||
|
||||
**What to include:**
|
||||
- ✅ ID (for follow-up)
|
||||
- ✅ Type emoji
|
||||
- ✅ Title
|
||||
- ✅ Subtitle (if available)
|
||||
- ✅ Date (human-readable)
|
||||
- ✅ Project name
|
||||
- ❌ Don't include full narrative/facts/files in index format
|
||||
|
||||
---
|
||||
|
||||
## Format: Full Results
|
||||
|
||||
**When to use:** User requests details, specific items selected from index
|
||||
|
||||
**Structure:**
|
||||
```markdown
|
||||
## {emoji} {type} #{id}: {title}
|
||||
|
||||
**Summary:** {subtitle}
|
||||
|
||||
**What happened:**
|
||||
{narrative}
|
||||
|
||||
**Key Facts:**
|
||||
- {fact1}
|
||||
- {fact2}
|
||||
|
||||
**Files modified:**
|
||||
- {file1}
|
||||
- {file2}
|
||||
|
||||
**Concepts:** {concepts}
|
||||
|
||||
**Date:** {human_readable_date}
|
||||
**Project:** {project}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```markdown
|
||||
## 🟣 Feature #1234: Implemented JWT authentication
|
||||
|
||||
**Summary:** Added token-based auth with refresh tokens
|
||||
|
||||
**What happened:**
|
||||
Implemented a complete JWT authentication system with access and refresh tokens. Access tokens expire after 15 minutes, refresh tokens after 7 days. Added token signing with RS256 algorithm and proper key rotation infrastructure.
|
||||
|
||||
**Key Facts:**
|
||||
- Access tokens use 15-minute expiration
|
||||
- Refresh tokens stored in httpOnly cookies
|
||||
- RS256 algorithm with key rotation support
|
||||
- Token refresh endpoint handles race conditions gracefully
|
||||
|
||||
**Files modified:**
|
||||
- src/auth/jwt.ts (created)
|
||||
- src/auth/refresh.ts (created)
|
||||
- src/middleware/auth.ts (modified)
|
||||
|
||||
**Concepts:** how-it-works, pattern
|
||||
|
||||
**Date:** November 9, 2024 at 2:55 PM
|
||||
**Project:** api-server
|
||||
```
|
||||
|
||||
**What to include:**
|
||||
- ✅ Full title with emoji and ID
|
||||
- ✅ Summary/subtitle
|
||||
- ✅ Complete narrative
|
||||
- ✅ All key facts
|
||||
- ✅ All files (with status: created/modified/deleted)
|
||||
- ✅ Concepts/tags
|
||||
- ✅ Precise timestamp
|
||||
- ✅ Project name
|
||||
|
||||
---
|
||||
|
||||
## Format: Timeline Results
|
||||
|
||||
**When to use:** Temporal investigations, "what was happening" questions
|
||||
|
||||
**Structure:**
|
||||
```markdown
|
||||
## Timeline: {anchor_description}
|
||||
|
||||
### Before ({count} records)
|
||||
|
||||
**{time}** - {emoji} {type} #{id}: {title}
|
||||
**{time}** - {emoji} {type} #{id}: {title}
|
||||
|
||||
### ⭐ Anchor Point ({time})
|
||||
{emoji} **{type} #{id}**: {title}
|
||||
|
||||
### After ({count} records)
|
||||
|
||||
**{time}** - {emoji} {type} #{id}: {title}
|
||||
**{time}** - {emoji} {type} #{id}: {title}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```markdown
|
||||
## Timeline: Deployment
|
||||
|
||||
### Before (10 records)
|
||||
|
||||
**2:30 PM** - 🟣 #1230: Prepared deployment scripts
|
||||
**2:45 PM** - 🔄 #1232: Updated configuration files
|
||||
**2:50 PM** - 💬 User asked: "Are we ready to deploy?"
|
||||
|
||||
### ⭐ Anchor Point (2:55 PM)
|
||||
🎯 **Session #545**: Deployed to production
|
||||
|
||||
### After (10 records)
|
||||
|
||||
**3:00 PM** - 🔴 #1235: Fixed post-deployment routing issue
|
||||
**3:10 PM** - 🔵 #1236: Discovered caching behavior in production
|
||||
**3:15 PM** - 🧠 #1237: Decided to add health check endpoint
|
||||
```
|
||||
|
||||
**What to include:**
|
||||
- ✅ Chronological ordering (oldest to newest)
|
||||
- ✅ Human-readable times (not epochs)
|
||||
- ✅ Clear anchor point marker (⭐)
|
||||
- ✅ Mix of all record types (observations, sessions, prompts)
|
||||
- ✅ Concise titles (not full narratives)
|
||||
- ✅ Type emojis for quick scanning
|
||||
|
||||
---
|
||||
|
||||
## Format: Session Summaries
|
||||
|
||||
**When to use:** Recent context, "what did we do" questions
|
||||
|
||||
**Structure:**
|
||||
```markdown
|
||||
## Recent Work on {project}
|
||||
|
||||
### 🎯 Session #{id} - {date}
|
||||
|
||||
**Request:** {user_request}
|
||||
|
||||
**Completed:**
|
||||
- {completion1}
|
||||
- {completion2}
|
||||
|
||||
**Key Learning:** {learning}
|
||||
|
||||
**Observations:**
|
||||
- {emoji} **#{obs_id}** {obs_title}
|
||||
- Files: {file_list}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```markdown
|
||||
## Recent Work on api-server
|
||||
|
||||
### 🎯 Session #545 - November 9, 2024
|
||||
|
||||
**Request:** Add JWT authentication with refresh tokens
|
||||
|
||||
**Completed:**
|
||||
- Implemented token-based auth with refresh logic
|
||||
- Added JWT signing and verification
|
||||
- Created refresh token rotation
|
||||
|
||||
**Key Learning:** JWT expiration requires careful handling of refresh race conditions
|
||||
|
||||
**Observations:**
|
||||
- 🟣 **#1234** Implemented JWT authentication
|
||||
- Files: jwt.ts, refresh.ts, middleware/auth.ts
|
||||
- 🔴 **#1235** Fixed token expiration edge case
|
||||
- Files: refresh.ts
|
||||
```
|
||||
|
||||
**What to include:**
|
||||
- ✅ Session ID and date
|
||||
- ✅ Original user request
|
||||
- ✅ What was completed (bulleted list)
|
||||
- ✅ Key learnings/insights
|
||||
- ✅ Linked observations with file lists
|
||||
- ✅ Clear hierarchy (session → observations)
|
||||
|
||||
---
|
||||
|
||||
## Format: User Prompts
|
||||
|
||||
**When to use:** "What did I ask" questions, prompt searches
|
||||
|
||||
**Structure:**
|
||||
```markdown
|
||||
Found {count} user prompts:
|
||||
|
||||
💬 **Prompt #{id}** (Session #{session_id})
|
||||
> "{preview_text}"
|
||||
> {date} • {project}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```markdown
|
||||
Found 5 user prompts about "authentication":
|
||||
|
||||
💬 **Prompt #1250** (Session #545)
|
||||
> "How do I implement JWT authentication with refresh tokens? I need to handle token expiration..."
|
||||
> Nov 9, 2024 • api-server
|
||||
|
||||
💬 **Prompt #1251** (Session #546)
|
||||
> "The auth tokens are expiring too quickly. Can you help debug the refresh flow?"
|
||||
> Nov 8, 2024 • api-server
|
||||
```
|
||||
|
||||
**What to include:**
|
||||
- ✅ Prompt ID
|
||||
- ✅ Session ID (for context linking)
|
||||
- ✅ Preview text (200 chars for index, full text for full format)
|
||||
- ✅ Date and project
|
||||
- ✅ Quote formatting for prompt text
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
**No results found:**
|
||||
```markdown
|
||||
No results found for "{query}". Try:
|
||||
- Different search terms
|
||||
- Broader keywords
|
||||
- Checking spelling
|
||||
- Using partial paths (for file searches)
|
||||
```
|
||||
|
||||
**Service unavailable:**
|
||||
```markdown
|
||||
The search service isn't available. Check if the worker is running:
|
||||
|
||||
```bash
|
||||
pm2 list
|
||||
```
|
||||
|
||||
If the worker is stopped, restart it:
|
||||
|
||||
```bash
|
||||
npm run worker:restart
|
||||
```
|
||||
```
|
||||
|
||||
**Invalid parameters:**
|
||||
```markdown
|
||||
Invalid search parameters:
|
||||
- {parameter}: {error_message}
|
||||
|
||||
See the [API help](help.md) for valid parameter options.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Context-Aware Presentation
|
||||
|
||||
Tailor formatting to user's question:
|
||||
|
||||
**"What bugs did we fix?"**
|
||||
→ Use index format, emphasize date/type, group by recency
|
||||
|
||||
**"How did we implement X?"**
|
||||
→ Use full format for best match, include complete narrative and files
|
||||
|
||||
**"What was happening when..."**
|
||||
→ Use timeline format, emphasize chronology and causality
|
||||
|
||||
**"Catch me up on recent work"**
|
||||
→ Use session summary format, focus on high-level accomplishments
|
||||
|
||||
---
|
||||
|
||||
## Token Budget Guidelines
|
||||
|
||||
**Minimal presentation (~100-200 tokens):**
|
||||
- Index format with 3-5 results
|
||||
- Compact list structure
|
||||
- Essential metadata only
|
||||
|
||||
**Standard presentation (~500-1,000 tokens):**
|
||||
- Index format with 10-15 results
|
||||
- Include subtitles and context
|
||||
- Clear formatting and emojis
|
||||
|
||||
**Detailed presentation (~1,500-3,000 tokens):**
|
||||
- Full format for 2-3 items
|
||||
- Complete narratives and facts
|
||||
- Timeline with 20-30 records
|
||||
|
||||
**Comprehensive presentation (~5,000+ tokens):**
|
||||
- Multiple full results
|
||||
- Deep timelines (40+ records)
|
||||
- Session summaries with observations
|
||||
|
||||
Always start minimal, expand only when needed.
|
||||
|
||||
---
|
||||
|
||||
## Markdown Best Practices
|
||||
|
||||
1. **Use headers (##, ###)** for hierarchy
|
||||
2. **Bold important elements** (IDs, titles, dates)
|
||||
3. **Quote user text** (prompts, questions)
|
||||
4. **Bullet lists** for facts and files
|
||||
5. **Code blocks** for commands and examples
|
||||
6. **Emojis** for type indicators
|
||||
7. **Horizontal rules (---)** for section breaks
|
||||
8. **Blockquotes (>)** for subtitles and previews
|
||||
|
||||
---
|
||||
|
||||
## Examples by Use Case
|
||||
|
||||
### Use Case 1: Quick Overview
|
||||
User: "What did we do last session?"
|
||||
|
||||
```markdown
|
||||
## Recent Work
|
||||
|
||||
### 🎯 Session #545 - November 9, 2024
|
||||
Implemented JWT authentication system
|
||||
|
||||
**Key accomplishment:** Added token-based auth with refresh tokens
|
||||
**Key learning:** JWT expiration requires careful handling of refresh race conditions
|
||||
```
|
||||
|
||||
### Use Case 2: Specific Investigation
|
||||
User: "How did we implement JWT authentication?"
|
||||
|
||||
```markdown
|
||||
## 🟣 Feature #1234: Implemented JWT authentication
|
||||
|
||||
**What happened:**
|
||||
Implemented a complete JWT authentication system with access and refresh tokens. Access tokens expire after 15 minutes, refresh tokens after 7 days. Added token signing with RS256 algorithm.
|
||||
|
||||
**Files:**
|
||||
- src/auth/jwt.ts (created)
|
||||
- src/auth/refresh.ts (created)
|
||||
- src/middleware/auth.ts (modified)
|
||||
|
||||
**Key insight:** Refresh race conditions require atomic token exchange logic.
|
||||
```
|
||||
|
||||
### Use Case 3: Timeline Investigation
|
||||
User: "What was happening around the deployment?"
|
||||
|
||||
```markdown
|
||||
## Timeline: Deployment
|
||||
|
||||
[... chronological timeline with before/after context ...]
|
||||
```
|
||||
|
||||
Choose presentation style based on user's question and information needs.
|
||||
@@ -0,0 +1,171 @@
|
||||
# API Help
|
||||
|
||||
Get comprehensive API documentation for all search endpoints.
|
||||
|
||||
## When to Use
|
||||
|
||||
- User asks: "What search operations are available?"
|
||||
- User asks: "How do I use the search API?"
|
||||
- Need reference documentation for endpoints
|
||||
- Want to see all available parameters
|
||||
|
||||
## Command
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/help"
|
||||
```
|
||||
|
||||
## Response Structure
|
||||
|
||||
Returns complete API documentation:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "6.5.0",
|
||||
"base_url": "http://localhost:37777/api",
|
||||
"endpoints": [
|
||||
{
|
||||
"path": "/search/observations",
|
||||
"method": "GET",
|
||||
"description": "Search observations using full-text search",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "query",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": "Search terms"
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"default": "full",
|
||||
"options": ["index", "full"],
|
||||
"description": "Response format"
|
||||
}
|
||||
],
|
||||
"example": "curl -s \"http://localhost:37777/api/search/observations?query=authentication&format=index&limit=5\""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## How to Present Results
|
||||
|
||||
Present as reference documentation:
|
||||
|
||||
```markdown
|
||||
## claude-mem Search API Reference
|
||||
|
||||
Base URL: `http://localhost:37777/api`
|
||||
|
||||
### Search Operations
|
||||
|
||||
**1. Search Observations**
|
||||
- **Endpoint:** `GET /search/observations`
|
||||
- **Description:** Search observations using full-text search
|
||||
- **Parameters:**
|
||||
- `query` (required, string): Search terms
|
||||
- `format` (optional, string): "index" or "full" (default: "full")
|
||||
- `limit` (optional, number): Max results (default: 20, max: 100)
|
||||
- **Example:**
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/observations?query=authentication&format=index&limit=5"
|
||||
```
|
||||
|
||||
[... continue for all endpoints ...]
|
||||
```
|
||||
|
||||
## Endpoint Categories
|
||||
|
||||
The API help response organizes endpoints by category:
|
||||
|
||||
1. **Full-Text Search**
|
||||
- `/search/observations`
|
||||
- `/search/sessions`
|
||||
- `/search/prompts`
|
||||
|
||||
2. **Filtered Search**
|
||||
- `/search/by-type`
|
||||
- `/search/by-concept`
|
||||
- `/search/by-file`
|
||||
|
||||
3. **Context Retrieval**
|
||||
- `/context/recent`
|
||||
- `/timeline/context`
|
||||
- `/timeline/by-query`
|
||||
|
||||
4. **Utilities**
|
||||
- `/help`
|
||||
|
||||
## Common Parameters
|
||||
|
||||
Many endpoints share these parameters:
|
||||
|
||||
- **format**: "index" (summary) or "full" (complete details)
|
||||
- **limit**: Number of results to return
|
||||
- **offset**: Number of results to skip (for pagination)
|
||||
- **project**: Filter by project name
|
||||
- **dateStart/dateEnd**: Filter by date range
|
||||
- `dateStart`: Start date (YYYY-MM-DD or epoch timestamp)
|
||||
- `dateEnd`: End date (YYYY-MM-DD or epoch timestamp)
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Worker not running:**
|
||||
Connection refused error. Response: "The search API isn't available. Check if worker is running: `pm2 list`"
|
||||
|
||||
**Invalid endpoint:**
|
||||
```json
|
||||
{"error": "Not found"}
|
||||
```
|
||||
Response: "Invalid API endpoint. Use /api/help to see available endpoints."
|
||||
|
||||
## Tips
|
||||
|
||||
1. Save help response for reference during investigation
|
||||
2. Use examples as starting point for your queries
|
||||
3. Check required parameters before making requests
|
||||
4. Refer to format options for each endpoint
|
||||
5. All endpoints use GET method with query parameters
|
||||
|
||||
**Token Efficiency:**
|
||||
- Help response: ~2,000-3,000 tokens (complete API reference)
|
||||
- Use sparingly - refer to operation-specific docs instead
|
||||
- Keep help response cached for repeated reference
|
||||
|
||||
## When to Use Help
|
||||
|
||||
**Use help when:**
|
||||
- Starting to use the search API
|
||||
- Need complete parameter reference
|
||||
- Forgot which endpoints are available
|
||||
- Want to see all options at once
|
||||
|
||||
**Don't use help when:**
|
||||
- You know which operation you need (use operation-specific docs)
|
||||
- Just need examples (use common-workflows.md)
|
||||
- Token budget is limited (help is comprehensive)
|
||||
|
||||
## Alternative to Help Endpoint
|
||||
|
||||
Instead of calling `/api/help`, you can:
|
||||
|
||||
1. **Use SKILL.md** - Quick decision guide with operation links
|
||||
2. **Use operation docs** - Detailed guides for specific endpoints
|
||||
3. **Use common-workflows.md** - Step-by-step examples
|
||||
4. **Use formatting.md** - Response presentation templates
|
||||
|
||||
The help endpoint is most useful when you need complete API reference in one response.
|
||||
|
||||
## API Versioning
|
||||
|
||||
The help response includes version information:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "6.5.0"
|
||||
}
|
||||
```
|
||||
|
||||
Check version to ensure compatibility with documentation.
|
||||
@@ -0,0 +1,124 @@
|
||||
# Search Observations (Semantic + Full-Text Hybrid)
|
||||
|
||||
Search all observations using natural language queries.
|
||||
|
||||
## When to Use
|
||||
|
||||
- User asks: "How did we implement authentication?"
|
||||
- User asks: "What bugs did we fix?"
|
||||
- User asks: "What features did we add?"
|
||||
- Looking for past work by keyword or topic
|
||||
|
||||
## Command
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/observations?query=authentication&format=index&limit=5"
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- **query** (optional): Natural language search query - uses semantic search (ChromaDB) for ranking with SQLite FTS5 fallback (e.g., "authentication", "bug fix", "database migration"). Can be omitted for filter-only searches.
|
||||
- **format**: "index" (summary) or "full" (complete details). Default: "full"
|
||||
- **limit**: Number of results (default: 20, max: 100)
|
||||
- **project**: Filter by project name (optional)
|
||||
- **dateStart/dateEnd**: Filter by date range (optional) - `dateStart` and/or `dateEnd` (YYYY-MM-DD format or epoch timestamp)
|
||||
- **obs_type**: Filter by observation type (comma-separated): bugfix, feature, refactor, decision, discovery, change (optional)
|
||||
- **concepts**: Filter by concept tags (comma-separated, optional)
|
||||
- **files**: Filter by file paths (comma-separated, optional)
|
||||
|
||||
**Important**: When omitting `query`, you MUST provide at least one filter (project, dateStart/dateEnd, obs_type, concepts, or files)
|
||||
|
||||
## When to Use Each Format
|
||||
|
||||
**Use format=index for:**
|
||||
- Quick overviews
|
||||
- Finding IDs for deeper investigation
|
||||
- Listing multiple results
|
||||
- **Token cost: ~50-100 per result**
|
||||
|
||||
**Use format=full for:**
|
||||
- Complete details including narrative, facts, files, concepts
|
||||
- Understanding the full context of specific observations
|
||||
- **Token cost: ~500-1000 per result**
|
||||
|
||||
## Example Response (format=index)
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "authentication",
|
||||
"count": 5,
|
||||
"format": "index",
|
||||
"results": [
|
||||
{
|
||||
"id": 1234,
|
||||
"type": "feature",
|
||||
"title": "Implemented JWT authentication",
|
||||
"subtitle": "Added token-based auth with refresh tokens",
|
||||
"created_at_epoch": 1699564800000,
|
||||
"project": "api-server",
|
||||
"score": 0.95
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## How to Present Results
|
||||
|
||||
For format=index, present as a compact list:
|
||||
|
||||
```markdown
|
||||
Found 5 results for "authentication":
|
||||
|
||||
1. **#1234** [feature] Implemented JWT authentication
|
||||
> Added token-based auth with refresh tokens
|
||||
> Nov 9, 2024 • api-server
|
||||
|
||||
2. **#1235** [bugfix] Fixed token expiration edge case
|
||||
> Handled race condition in refresh flow
|
||||
> Nov 9, 2024 • api-server
|
||||
```
|
||||
|
||||
**Include:** ID (for follow-up), type emoji (🔴 bugfix, 🟣 feature, 🔄 refactor, 🔵 discovery, 🧠 decision, ✅ change), title, subtitle, date, project.
|
||||
|
||||
For complete formatting guidelines, see formatting.md (documentation coming soon).
|
||||
|
||||
## Filter-Only Examples
|
||||
|
||||
Search without query text (direct SQLite filtering):
|
||||
|
||||
```bash
|
||||
# Get all observations from November 2025
|
||||
curl -s "http://localhost:37777/api/search?type=observations&dateStart=2025-11-01&format=index"
|
||||
|
||||
# Get all bug fixes from a specific project
|
||||
curl -s "http://localhost:37777/api/search?type=observations&obs_type=bugfix&project=api-server&format=index"
|
||||
|
||||
# Get all observations from last 7 days
|
||||
curl -s "http://localhost:37777/api/search?type=observations&dateStart=2025-11-11&format=index"
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Missing query and filters:**
|
||||
```json
|
||||
{"error": "Either query or filters required for search"}
|
||||
```
|
||||
Fix: Provide either a query parameter OR at least one filter (project, dateStart/dateEnd, obs_type, concepts, files)
|
||||
|
||||
**No results found:**
|
||||
```json
|
||||
{"query": "foobar", "count": 0, "results": []}
|
||||
```
|
||||
Response: "No results found for 'foobar'. Try different search terms."
|
||||
|
||||
## Tips
|
||||
|
||||
1. Be specific: "authentication JWT" > "auth"
|
||||
2. Start with format=index and limit=5-10
|
||||
3. Use project filtering when working on one codebase
|
||||
4. If no results, try broader terms or check spelling
|
||||
|
||||
**Token Efficiency:**
|
||||
- Start with format=index (~50-100 tokens per result)
|
||||
- Use format=full only for relevant items (~500-1000 tokens per result)
|
||||
- See [../principles/progressive-disclosure.md](../principles/progressive-disclosure.md)
|
||||
@@ -0,0 +1,125 @@
|
||||
# Search User Prompts (Full-Text)
|
||||
|
||||
Search raw user prompts to find what was actually asked across all sessions.
|
||||
|
||||
## When to Use
|
||||
|
||||
- User asks: "What did I ask about authentication?"
|
||||
- User asks: "Find my question about database migrations"
|
||||
- User asks: "When did I ask about testing?"
|
||||
- Looking for specific user questions or requests
|
||||
|
||||
## Command
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/prompts?query=authentication&format=index&limit=5"
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- **query** (required): Search terms (e.g., "authentication", "how do I", "bug fix")
|
||||
- **format**: "index" (truncated prompts) or "full" (complete prompt text). Default: "full"
|
||||
- **limit**: Number of results (default: 20, max: 100)
|
||||
- **project**: Filter by project name (optional)
|
||||
- **dateStart/dateEnd**: Filter by date range (optional)
|
||||
|
||||
## When to Use Each Format
|
||||
|
||||
**Use format=index for:**
|
||||
- Quick overviews of what was asked
|
||||
- Finding prompt IDs for full text
|
||||
- Listing multiple prompts
|
||||
- **Token cost: ~50-100 per result (truncated to 200 chars)**
|
||||
|
||||
**Use format=full for:**
|
||||
- Complete prompt text
|
||||
- Understanding the full user request
|
||||
- **Token cost: Variable (depends on prompt length, typically 100-300 tokens)**
|
||||
|
||||
## Example Response (format=index)
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "authentication",
|
||||
"count": 5,
|
||||
"format": "index",
|
||||
"results": [
|
||||
{
|
||||
"id": 1250,
|
||||
"session_id": "S545",
|
||||
"prompt_preview": "How do I implement JWT authentication with refresh tokens? I need to handle token expiration...",
|
||||
"created_at_epoch": 1699564800000,
|
||||
"project": "api-server"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## How to Present Results
|
||||
|
||||
For format=index, present as a compact list:
|
||||
|
||||
```markdown
|
||||
Found 5 user prompts about "authentication":
|
||||
|
||||
💬 **Prompt #1250** (Session #545)
|
||||
> "How do I implement JWT authentication with refresh tokens? I need to handle token expiration..."
|
||||
> Nov 9, 2024 • api-server
|
||||
|
||||
💬 **Prompt #1251** (Session #546)
|
||||
> "The auth tokens are expiring too quickly. Can you help debug the refresh flow?"
|
||||
> Nov 8, 2024 • api-server
|
||||
```
|
||||
|
||||
For complete formatting guidelines, see [formatting.md](formatting.md).
|
||||
|
||||
## What Gets Searched
|
||||
|
||||
User prompts search covers:
|
||||
- All user messages sent to Claude Code
|
||||
- Raw text as typed by the user
|
||||
- Multi-turn conversations (each message is a separate prompt)
|
||||
- Questions, requests, commands, and clarifications
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Missing query parameter:**
|
||||
```json
|
||||
{"error": "Missing required parameter: query"}
|
||||
```
|
||||
Fix: Add the query parameter
|
||||
|
||||
**No results found:**
|
||||
```json
|
||||
{"query": "foobar", "count": 0, "results": []}
|
||||
```
|
||||
Response: "No user prompts found for 'foobar'. Try different search terms."
|
||||
|
||||
## Tips
|
||||
|
||||
1. Use exact phrases in quotes: `?query="how do I"` for precise matches
|
||||
2. Start with format=index to see preview, then get full text if needed
|
||||
3. Use dateStart to find recent questions: `?query=bug&dateStart=2024-11-01`
|
||||
4. Prompts show what was asked, sessions/observations show what was done
|
||||
5. Combine with session search to see both question and answer
|
||||
|
||||
**Token Efficiency:**
|
||||
- Start with format=index (~50-100 tokens per result, prompt truncated to 200 chars)
|
||||
- Use format=full only for relevant items (100-300 tokens per result)
|
||||
- See [../principles/progressive-disclosure.md](../principles/progressive-disclosure.md)
|
||||
|
||||
## When to Use Prompts vs Sessions
|
||||
|
||||
**Use prompts search when:**
|
||||
- Looking for specific user questions
|
||||
- Trying to remember what was asked
|
||||
- Finding original request wording
|
||||
|
||||
**Use sessions search when:**
|
||||
- Looking for what was accomplished
|
||||
- Understanding work summaries
|
||||
- Getting high-level context
|
||||
|
||||
**Combine both when:**
|
||||
- Understanding the full conversation (what was asked + what was done)
|
||||
- Investigating how a request was interpreted
|
||||
@@ -0,0 +1,134 @@
|
||||
# Get Recent Context
|
||||
|
||||
Get recent session summaries and observations for a project.
|
||||
|
||||
## When to Use
|
||||
|
||||
- User asks: "What did we do last session?"
|
||||
- User asks: "What have we been working on recently?"
|
||||
- User asks: "Catch me up on recent work"
|
||||
- Starting a new session and need context
|
||||
|
||||
## Command
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/context/recent?project=api-server&limit=3"
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- **project**: Project name (defaults to current working directory basename)
|
||||
- **limit**: Number of recent sessions to retrieve (default: 3, max: 10)
|
||||
|
||||
## Response Structure
|
||||
|
||||
Returns combined context from recent sessions:
|
||||
|
||||
```json
|
||||
{
|
||||
"project": "api-server",
|
||||
"limit": 3,
|
||||
"sessions": [
|
||||
{
|
||||
"id": 545,
|
||||
"session_id": "S545",
|
||||
"title": "Implemented JWT authentication system",
|
||||
"request": "Add JWT authentication with refresh tokens",
|
||||
"completion": "Implemented token-based auth with refresh logic",
|
||||
"learnings": "JWT expiration requires careful handling of refresh race conditions",
|
||||
"created_at_epoch": 1699564800000,
|
||||
"observations": [
|
||||
{
|
||||
"id": 1234,
|
||||
"type": "feature",
|
||||
"title": "Implemented JWT authentication",
|
||||
"subtitle": "Added token-based auth with refresh tokens",
|
||||
"files": ["src/auth/jwt.ts", "src/auth/refresh.ts"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## How to Present Results
|
||||
|
||||
Present as a chronological narrative:
|
||||
|
||||
```markdown
|
||||
## Recent Work on api-server
|
||||
|
||||
### Session #545 - Nov 9, 2024
|
||||
**Request:** Add JWT authentication with refresh tokens
|
||||
|
||||
**Completed:**
|
||||
- Implemented token-based auth with refresh logic
|
||||
- Added JWT signing and verification
|
||||
- Created refresh token rotation
|
||||
|
||||
**Key Learning:** JWT expiration requires careful handling of refresh race conditions
|
||||
|
||||
**Observations:**
|
||||
- 🟣 **#1234** Implemented JWT authentication
|
||||
- Files: jwt.ts, refresh.ts
|
||||
```
|
||||
|
||||
For complete formatting guidelines, see [formatting.md](formatting.md).
|
||||
|
||||
## Default Project Detection
|
||||
|
||||
If no project parameter is provided, uses current working directory:
|
||||
|
||||
```bash
|
||||
# Auto-detects project from current directory
|
||||
curl -s "http://localhost:37777/api/context/recent?limit=3"
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
**No sessions found:**
|
||||
```json
|
||||
{"project": "new-project", "sessions": []}
|
||||
```
|
||||
Response: "No recent sessions found for 'new-project'. This might be a new project."
|
||||
|
||||
**Worker not running:**
|
||||
Connection refused error. Inform user to check if worker is running: `pm2 list`
|
||||
|
||||
## Tips
|
||||
|
||||
1. Start with limit=3 for quick overview (default)
|
||||
2. Increase to limit=5-10 for deeper context
|
||||
3. Recent context is perfect for session start
|
||||
4. Combines both sessions and observations in one request
|
||||
5. Use this when user asks "what did we do last time?"
|
||||
|
||||
**Token Efficiency:**
|
||||
- limit=3 sessions: ~1,500-2,500 tokens (includes observations)
|
||||
- limit=5 sessions: ~2,500-4,000 tokens
|
||||
- limit=10 sessions: ~5,000-8,000 tokens
|
||||
- See [../principles/progressive-disclosure.md](../principles/progressive-disclosure.md)
|
||||
|
||||
## When to Use Recent Context
|
||||
|
||||
**Use recent-context when:**
|
||||
- Starting a new session
|
||||
- User asks about recent work
|
||||
- Need quick catch-up on project activity
|
||||
- Want both sessions and observations together
|
||||
|
||||
**Don't use recent-context when:**
|
||||
- Looking for specific topics (use search instead)
|
||||
- Need timeline around specific event (use timeline instead)
|
||||
- Want only observations or only sessions (use search operations)
|
||||
|
||||
## Comparison with Other Operations
|
||||
|
||||
| Operation | Use Case | Token Cost |
|
||||
|-----------|----------|------------|
|
||||
| recent-context | Quick catch-up on recent work | 1,500-4,000 |
|
||||
| sessions search | Find sessions by topic | 50-100 per result (index) |
|
||||
| observations search | Find specific implementations | 50-100 per result (index) |
|
||||
| timeline | Context around specific point | 3,000-6,000 |
|
||||
|
||||
Recent context is optimized for "what happened recently?" questions with minimal token usage.
|
||||
@@ -0,0 +1,124 @@
|
||||
# Search Sessions (Full-Text)
|
||||
|
||||
Search session summaries using natural language queries.
|
||||
|
||||
## When to Use
|
||||
|
||||
- User asks: "What did we work on last week?"
|
||||
- User asks: "What sessions involved database work?"
|
||||
- User asks: "Show me sessions where we fixed bugs"
|
||||
- Looking for past sessions by topic or theme
|
||||
|
||||
## Command
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/sessions?query=authentication&format=index&limit=5"
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- **query** (required): Search terms (e.g., "authentication", "database migration", "bug fixes")
|
||||
- **format**: "index" (summary) or "full" (complete details). Default: "full"
|
||||
- **limit**: Number of results (default: 20, max: 100)
|
||||
- **project**: Filter by project name (optional)
|
||||
- **dateStart/dateEnd**: Filter by date range (optional)
|
||||
|
||||
## When to Use Each Format
|
||||
|
||||
**Use format=index for:**
|
||||
- Quick overviews of past sessions
|
||||
- Finding session IDs for deeper investigation
|
||||
- Listing multiple sessions
|
||||
- **Token cost: ~50-100 per result**
|
||||
|
||||
**Use format=full for:**
|
||||
- Complete session summaries with requests, completions, learnings
|
||||
- Understanding the full context of a session
|
||||
- **Token cost: ~500-1000 per result**
|
||||
|
||||
## Example Response (format=index)
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "authentication",
|
||||
"count": 3,
|
||||
"format": "index",
|
||||
"results": [
|
||||
{
|
||||
"id": 545,
|
||||
"session_id": "S545",
|
||||
"title": "Implemented JWT authentication system",
|
||||
"subtitle": "Added token-based auth with refresh tokens",
|
||||
"created_at_epoch": 1699564800000,
|
||||
"project": "api-server"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## How to Present Results
|
||||
|
||||
For format=index, present as a compact list:
|
||||
|
||||
```markdown
|
||||
Found 3 sessions about "authentication":
|
||||
|
||||
🎯 **Session #545** Implemented JWT authentication system
|
||||
> Added token-based auth with refresh tokens
|
||||
> Nov 9, 2024 • api-server
|
||||
|
||||
🎯 **Session #546** Fixed authentication token expiration
|
||||
> Resolved race condition in token refresh flow
|
||||
> Nov 8, 2024 • api-server
|
||||
```
|
||||
|
||||
For complete formatting guidelines, see [formatting.md](formatting.md).
|
||||
|
||||
## Session Summary Structure
|
||||
|
||||
Full session summaries include:
|
||||
|
||||
- **Session request**: What the user asked for
|
||||
- **What was completed**: Summary of work done
|
||||
- **Key learnings**: Important insights and discoveries
|
||||
- **Files modified**: List of changed files
|
||||
- **Observations**: Links to detailed observations
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Missing query parameter:**
|
||||
```json
|
||||
{"error": "Missing required parameter: query"}
|
||||
```
|
||||
Fix: Add the query parameter
|
||||
|
||||
**No results found:**
|
||||
```json
|
||||
{"query": "foobar", "count": 0, "results": []}
|
||||
```
|
||||
Response: "No sessions found for 'foobar'. Try different search terms."
|
||||
|
||||
## Tips
|
||||
|
||||
1. Be specific: "JWT authentication implementation" > "auth"
|
||||
2. Start with format=index and limit=5-10
|
||||
3. Use dateStart for recent sessions: `?query=auth&dateStart=2024-11-01`
|
||||
4. Sessions provide high-level overview, observations provide details
|
||||
5. Use project filtering when working on one codebase
|
||||
|
||||
**Token Efficiency:**
|
||||
- Start with format=index (~50-100 tokens per result)
|
||||
- Use format=full only for relevant items (~500-1000 tokens per result)
|
||||
- See [../principles/progressive-disclosure.md](../principles/progressive-disclosure.md)
|
||||
|
||||
## When to Use Sessions vs Observations
|
||||
|
||||
**Use sessions search when:**
|
||||
- Looking for high-level work summaries
|
||||
- Understanding what was done in past sessions
|
||||
- Getting overview of recent activity
|
||||
|
||||
**Use observations search when:**
|
||||
- Looking for specific implementation details
|
||||
- Finding bugs, features, or decisions
|
||||
- Need fine-grained context about code changes
|
||||
@@ -0,0 +1,194 @@
|
||||
# Timeline by Query
|
||||
|
||||
Search for observations and get timeline context in a single request. Combines search + timeline into one operation.
|
||||
|
||||
## When to Use
|
||||
|
||||
- User asks: "What was happening when we worked on authentication?"
|
||||
- User asks: "Show me context around bug fixes"
|
||||
- User asks: "Timeline of database work"
|
||||
- Need to find something then see temporal context
|
||||
|
||||
## MCP Tool
|
||||
|
||||
Use the `get_timeline_by_query` MCP tool:
|
||||
|
||||
```
|
||||
# Auto mode: Uses top search result as timeline anchor
|
||||
get_timeline_by_query(query="authentication", mode="auto", depth_before=10, depth_after=10)
|
||||
|
||||
# Interactive mode: Shows top N search results for manual selection
|
||||
get_timeline_by_query(query="authentication", mode="interactive", limit=5)
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- **query** (required): Search terms (e.g., "authentication", "bug fix", "database")
|
||||
- **mode**: Search mode
|
||||
- `auto` (default): Automatically uses top search result as timeline anchor
|
||||
- `interactive`: Returns top N search results for manual anchor selection
|
||||
- **depth_before**: Records before anchor (default: 10, max: 50) - for auto mode
|
||||
- **depth_after**: Records after anchor (default: 10, max: 50) - for auto mode
|
||||
- **limit**: Number of search results (default: 5, max: 20) - for interactive mode
|
||||
- **project**: Filter by project name (optional)
|
||||
|
||||
## Auto Mode (Recommended)
|
||||
|
||||
Automatically gets timeline around best match:
|
||||
|
||||
```
|
||||
get_timeline_by_query(query="JWT authentication", mode="auto", depth_before=10, depth_after=10)
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"query": "JWT authentication",
|
||||
"mode": "auto",
|
||||
"best_match": {
|
||||
"id": 1234,
|
||||
"type": "feature",
|
||||
"title": "Implemented JWT authentication",
|
||||
"score": 0.95
|
||||
},
|
||||
"timeline": [
|
||||
// ... timeline records around observation #1234
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**When to use auto mode:**
|
||||
- You're confident the top result is what you want
|
||||
- Want fastest path to timeline context
|
||||
- Query is specific enough for accurate top result
|
||||
|
||||
## Interactive Mode
|
||||
|
||||
Shows top search results for manual review:
|
||||
|
||||
```
|
||||
get_timeline_by_query(query="authentication", mode="interactive", limit=5)
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"query": "authentication",
|
||||
"mode": "interactive",
|
||||
"top_matches": [
|
||||
{
|
||||
"id": 1234,
|
||||
"type": "feature",
|
||||
"title": "Implemented JWT authentication",
|
||||
"subtitle": "Added token-based auth with refresh tokens",
|
||||
"score": 0.95
|
||||
},
|
||||
{
|
||||
"id": 1240,
|
||||
"type": "bugfix",
|
||||
"title": "Fixed authentication token expiration",
|
||||
"subtitle": "Resolved race condition in refresh flow",
|
||||
"score": 0.87
|
||||
}
|
||||
],
|
||||
"next_step": "Use get_context_timeline(anchor=<id>, depth_before=10, depth_after=10)"
|
||||
}
|
||||
```
|
||||
|
||||
**When to use interactive mode:**
|
||||
- Query is broad and may have multiple relevant results
|
||||
- Want to review options before getting timeline
|
||||
- Not sure which result is most relevant
|
||||
|
||||
## How to Present Results
|
||||
|
||||
**For auto mode:**
|
||||
|
||||
```markdown
|
||||
## Timeline: JWT authentication
|
||||
|
||||
**Best Match:** 🟣 Observation #1234 - Implemented JWT authentication (score: 0.95)
|
||||
|
||||
### Before (10 records)
|
||||
**2:45 PM** - 🟣 Added authentication middleware
|
||||
|
||||
### ⭐ Anchor Point (2:55 PM)
|
||||
🟣 **Observation #1234**: Implemented JWT authentication
|
||||
|
||||
### After (10 records)
|
||||
**3:00 PM** - 🎯 Session completed: JWT authentication system
|
||||
```
|
||||
|
||||
**For interactive mode:**
|
||||
|
||||
```markdown
|
||||
Found 5 matches for "authentication":
|
||||
|
||||
1. 🟣 **#1234** Implemented JWT authentication (score: 0.95)
|
||||
> Added token-based auth with refresh tokens
|
||||
|
||||
2. 🔴 **#1240** Fixed authentication token expiration (score: 0.87)
|
||||
> Resolved race condition in refresh flow
|
||||
|
||||
To see timeline context, use observation ID with timeline operation.
|
||||
```
|
||||
|
||||
For complete formatting guidelines, see [formatting.md](formatting.md).
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Missing query parameter:**
|
||||
```json
|
||||
{"error": "Missing required parameter: query"}
|
||||
```
|
||||
Fix: Add the query parameter
|
||||
|
||||
**No results found:**
|
||||
```json
|
||||
{"query": "foobar", "top_matches": []}
|
||||
```
|
||||
Response: "No results found for 'foobar'. Try different search terms."
|
||||
|
||||
## Tips
|
||||
|
||||
1. **Use auto mode** for specific queries: "JWT authentication implementation"
|
||||
2. **Use interactive mode** for broad queries: "authentication"
|
||||
3. Start with depth 10/10 for balanced context
|
||||
4. Be specific in queries for better auto mode accuracy
|
||||
5. This is fastest way to find + explore context in one request
|
||||
|
||||
**Token Efficiency:**
|
||||
- Auto mode: ~3,000-4,000 tokens (search + timeline)
|
||||
- Interactive mode: ~500-1,000 tokens (search results only)
|
||||
- See [../principles/progressive-disclosure.md](../principles/progressive-disclosure.md)
|
||||
|
||||
## Workflow Comparison
|
||||
|
||||
**timeline-by-query (auto):**
|
||||
1. One request → get timeline around best match
|
||||
2. ~3,000 tokens
|
||||
|
||||
**timeline-by-query (interactive) → timeline:**
|
||||
1. First request → see top matches (~500 tokens)
|
||||
2. Second request → get timeline for chosen match (~3,000 tokens)
|
||||
3. Total: ~3,500 tokens
|
||||
|
||||
**observations search → timeline:**
|
||||
1. Search observations (~500 tokens)
|
||||
2. Get timeline for chosen result (~3,000 tokens)
|
||||
3. Total: ~3,500 tokens
|
||||
|
||||
Use auto mode when you're confident about the query. Use interactive mode or separate search when you want more control.
|
||||
|
||||
## When to Use Timeline-by-Query
|
||||
|
||||
**Use timeline-by-query when:**
|
||||
- Need to find something AND see temporal context
|
||||
- Want one-request convenience (auto mode)
|
||||
- Investigating "what was happening when we worked on X?"
|
||||
- Don't have observation ID already
|
||||
|
||||
**Don't use timeline-by-query when:**
|
||||
- Already have observation ID (use timeline instead)
|
||||
- Just need search results (use observations search)
|
||||
- Need recent work overview (use recent-context)
|
||||
@@ -0,0 +1,171 @@
|
||||
# Get Context Timeline
|
||||
|
||||
Get a chronological timeline of observations, sessions, and prompts around a specific point in time.
|
||||
|
||||
## When to Use
|
||||
|
||||
- User asks: "What was happening when we deployed?"
|
||||
- User asks: "Show me context around that bug fix"
|
||||
- User asks: "What happened before and after that change?"
|
||||
- Need temporal context around an event
|
||||
|
||||
## MCP Tool
|
||||
|
||||
Use the `get_context_timeline` MCP tool:
|
||||
|
||||
```
|
||||
get_context_timeline(anchor=1234, depth_before=10, depth_after=10)
|
||||
get_context_timeline(anchor="S545", depth_before=10, depth_after=10)
|
||||
get_context_timeline(anchor="2024-11-09T12:00:00Z", depth_before=10, depth_after=10)
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- **anchor** (required): Point in time to center timeline
|
||||
- Observation ID: `1234`
|
||||
- Session ID: `S545`
|
||||
- ISO timestamp: `2024-11-09T12:00:00Z`
|
||||
- **depth_before**: Number of records before anchor (default: 10, max: 50)
|
||||
- **depth_after**: Number of records after anchor (default: 10, max: 50)
|
||||
- **project**: Filter by project name (optional)
|
||||
|
||||
## Response Structure
|
||||
|
||||
Returns unified chronological timeline:
|
||||
|
||||
```json
|
||||
{
|
||||
"anchor": 1234,
|
||||
"depth_before": 10,
|
||||
"depth_after": 10,
|
||||
"total_records": 21,
|
||||
"timeline": [
|
||||
{
|
||||
"record_type": "observation",
|
||||
"id": 1230,
|
||||
"type": "feature",
|
||||
"title": "Added authentication middleware",
|
||||
"created_at_epoch": 1699564700000
|
||||
},
|
||||
{
|
||||
"record_type": "prompt",
|
||||
"id": 1250,
|
||||
"session_id": "S545",
|
||||
"prompt_preview": "How do I add JWT authentication?",
|
||||
"created_at_epoch": 1699564750000
|
||||
},
|
||||
{
|
||||
"record_type": "observation",
|
||||
"id": 1234,
|
||||
"type": "feature",
|
||||
"title": "Implemented JWT authentication",
|
||||
"created_at_epoch": 1699564800000,
|
||||
"is_anchor": true
|
||||
},
|
||||
{
|
||||
"record_type": "session",
|
||||
"id": 545,
|
||||
"session_id": "S545",
|
||||
"title": "Implemented JWT authentication system",
|
||||
"created_at_epoch": 1699564900000
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## How to Present Results
|
||||
|
||||
Present as chronological narrative with anchor highlighted:
|
||||
|
||||
```markdown
|
||||
## Timeline around Observation #1234
|
||||
|
||||
### Before (10 records)
|
||||
|
||||
**2:45 PM** - 🟣 Observation #1230: Added authentication middleware
|
||||
|
||||
**2:50 PM** - 💬 User asked: "How do I add JWT authentication?"
|
||||
|
||||
### ⭐ Anchor Point (2:55 PM)
|
||||
🟣 **Observation #1234**: Implemented JWT authentication
|
||||
|
||||
### After (10 records)
|
||||
|
||||
**3:00 PM** - 🎯 Session #545 completed: Implemented JWT authentication system
|
||||
|
||||
**3:05 PM** - 🔴 Observation #1235: Fixed token expiration edge case
|
||||
```
|
||||
|
||||
For complete formatting guidelines, see [formatting.md](formatting.md).
|
||||
|
||||
## Anchor Types
|
||||
|
||||
**Observation ID:**
|
||||
- Use when you know the specific observation ID
|
||||
- Example: `anchor=1234`
|
||||
|
||||
**Session ID:**
|
||||
- Use when you want context around a session
|
||||
- Example: `anchor=S545`
|
||||
|
||||
**ISO Timestamp:**
|
||||
- Use when you know approximate time
|
||||
- Example: `anchor=2024-11-09T14:30:00Z`
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Missing anchor parameter:**
|
||||
```json
|
||||
{"error": "Missing required parameter: anchor"}
|
||||
```
|
||||
Fix: Add the anchor parameter
|
||||
|
||||
**Anchor not found:**
|
||||
```json
|
||||
{"error": "Anchor not found: 9999"}
|
||||
```
|
||||
Response: "Observation #9999 not found. Check the ID or try a different anchor."
|
||||
|
||||
**Invalid timestamp:**
|
||||
```json
|
||||
{"error": "Invalid timestamp format"}
|
||||
```
|
||||
Fix: Use ISO 8601 format: `2024-11-09T14:30:00Z`
|
||||
|
||||
## Tips
|
||||
|
||||
1. Start with depth_before=10, depth_after=10 for balanced context
|
||||
2. Increase depth for broader investigation (max: 50 each)
|
||||
3. Use observation IDs from search results as anchors
|
||||
4. Timelines show all record types interleaved chronologically
|
||||
5. Perfect for understanding "what was happening when X occurred"
|
||||
|
||||
**Token Efficiency:**
|
||||
- depth 10/10: ~3,000-4,000 tokens (21 records)
|
||||
- depth 20/20: ~6,000-8,000 tokens (41 records)
|
||||
- depth 50/50: ~15,000-20,000 tokens (101 records)
|
||||
- See [../principles/progressive-disclosure.md](../principles/progressive-disclosure.md)
|
||||
|
||||
## When to Use Timeline
|
||||
|
||||
**Use timeline when:**
|
||||
- Need context around specific event
|
||||
- Understanding sequence of events
|
||||
- Investigating "what was happening then?"
|
||||
- Want all record types (observations, sessions, prompts) together
|
||||
|
||||
**Don't use timeline when:**
|
||||
- Just need recent work (use recent-context)
|
||||
- Looking for specific topics (use search)
|
||||
- Don't have an anchor point (use timeline-by-query)
|
||||
|
||||
## Comparison with Timeline-by-Query
|
||||
|
||||
| Feature | timeline | timeline-by-query |
|
||||
|---------|----------|-------------------|
|
||||
| Requires anchor | Yes (ID or timestamp) | No (uses search query) |
|
||||
| Best for | Known event investigation | Finding then exploring context |
|
||||
| Steps | 1 (direct timeline) | 2 (search + timeline) |
|
||||
| Use when | You have observation ID | You have search term |
|
||||
|
||||
Timeline is faster when you already know the anchor point.
|
||||
@@ -0,0 +1,176 @@
|
||||
# Anti-Pattern Catalogue
|
||||
|
||||
Common mistakes to avoid when using the HTTP search API. These anti-patterns address LLM training biases and prevent token-wasting behaviors.
|
||||
|
||||
## Anti-Pattern 1: Skipping Index Format
|
||||
|
||||
**The Mistake:**
|
||||
```bash
|
||||
# ❌ Bad: Jump straight to full format
|
||||
curl -s "http://localhost:37777/api/search/observations?query=authentication&format=full&limit=20"
|
||||
```
|
||||
|
||||
**Why It's Wrong:**
|
||||
- 20 × 750 tokens = 15,000 tokens
|
||||
- May hit MCP token limits
|
||||
- 99% wasted on irrelevant results
|
||||
|
||||
**The Correction:**
|
||||
```bash
|
||||
# ✅ Good: Start with index, review, then request full selectively
|
||||
curl -s "http://localhost:37777/api/search/observations?query=authentication&format=index&limit=5"
|
||||
# Review results, identify relevant items
|
||||
curl -s "http://localhost:37777/api/search/observations?query=authentication&format=full&limit=1&offset=2"
|
||||
```
|
||||
|
||||
**What It Teaches:**
|
||||
Progressive disclosure isn't optional - it's essential for scale.
|
||||
|
||||
**LLM Behavior Insight:**
|
||||
LLMs trained on code examples may have seen `format=full` as "more complete" and default to it.
|
||||
|
||||
---
|
||||
|
||||
## Anti-Pattern 2: Over-Requesting Results
|
||||
|
||||
**The Mistake:**
|
||||
```bash
|
||||
# ❌ Bad: Request limit=20 without reviewing index first
|
||||
curl -s "http://localhost:37777/api/search/observations?query=auth&format=index&limit=20"
|
||||
```
|
||||
|
||||
**Why It's Wrong:**
|
||||
- Most of 20 results will be irrelevant
|
||||
- Wastes tokens and time
|
||||
- Overwhelms review process
|
||||
|
||||
**The Correction:**
|
||||
```bash
|
||||
# ✅ Good: Start small, paginate if needed
|
||||
curl -s "http://localhost:37777/api/search/observations?query=auth&format=index&limit=5"
|
||||
# If needed, paginate:
|
||||
curl -s "http://localhost:37777/api/search/observations?query=auth&format=index&limit=5&offset=5"
|
||||
```
|
||||
|
||||
**What It Teaches:**
|
||||
Start small (limit=3-5), review, paginate if needed.
|
||||
|
||||
**LLM Behavior Insight:**
|
||||
LLMs may think "more results = more thorough" without considering relevance.
|
||||
|
||||
---
|
||||
|
||||
## Anti-Pattern 3: Ignoring Tool Specialization
|
||||
|
||||
**The Mistake:**
|
||||
```bash
|
||||
# ❌ Bad: Use generic search for everything
|
||||
curl -s "http://localhost:37777/api/search/observations?query=bugfix&format=index&limit=10"
|
||||
```
|
||||
|
||||
**Why It's Wrong:**
|
||||
- Specialized tools (by-type, by-concept, by-file) are more efficient
|
||||
- Generic search mixes all result types
|
||||
- Misses filtering optimization
|
||||
|
||||
**The Correction:**
|
||||
```bash
|
||||
# ✅ Good: Use specialized endpoint when applicable
|
||||
curl -s "http://localhost:37777/api/search/by-type?type=bugfix&format=index&limit=10"
|
||||
```
|
||||
|
||||
**What It Teaches:**
|
||||
The decision tree exists for a reason - follow it.
|
||||
|
||||
**LLM Behavior Insight:**
|
||||
LLMs may gravitate toward "general purpose" tools to avoid decision-making.
|
||||
|
||||
---
|
||||
|
||||
## Anti-Pattern 4: Loading Full Context Prematurely
|
||||
|
||||
**The Mistake:**
|
||||
```bash
|
||||
# ❌ Bad: Request full format before understanding what's relevant
|
||||
curl -s "http://localhost:37777/api/search/observations?query=database&format=full&limit=10"
|
||||
```
|
||||
|
||||
**Why It's Wrong:**
|
||||
- Can't filter relevance without seeing index first
|
||||
- Wastes tokens on irrelevant full details
|
||||
- 10 × 750 = 7,500 tokens for potentially zero useful results
|
||||
|
||||
**The Correction:**
|
||||
```bash
|
||||
# ✅ Good: Index first to identify relevance
|
||||
curl -s "http://localhost:37777/api/search/observations?query=database&format=index&limit=10"
|
||||
# Identify relevant: #1234 and #1250
|
||||
curl -s "http://localhost:37777/api/search/observations?query=database+1234&format=full&limit=1"
|
||||
curl -s "http://localhost:37777/api/search/observations?query=database+1250&format=full&limit=1"
|
||||
```
|
||||
|
||||
**What It Teaches:**
|
||||
Filtering is a prerequisite for expansion.
|
||||
|
||||
**LLM Behavior Insight:**
|
||||
LLMs may try to "get everything at once" to avoid multiple tool calls.
|
||||
|
||||
---
|
||||
|
||||
## Anti-Pattern 5: Not Using Timeline Tools
|
||||
|
||||
**The Mistake:**
|
||||
```bash
|
||||
# ❌ Bad: Search for individual observations separately
|
||||
curl -s "http://localhost:37777/api/search/observations?query=before+deployment"
|
||||
curl -s "http://localhost:37777/api/search/observations?query=during+deployment"
|
||||
curl -s "http://localhost:37777/api/search/observations?query=after+deployment"
|
||||
```
|
||||
|
||||
**Why It's Wrong:**
|
||||
- Misses context around events
|
||||
- Inefficient (N searches vs 1 timeline)
|
||||
- Temporal relationships lost
|
||||
|
||||
**The Correction:**
|
||||
```bash
|
||||
# ✅ Good: Use timeline tool for contextual investigation
|
||||
curl -s "http://localhost:37777/api/timeline/by-query?query=deployment&depth_before=10&depth_after=10"
|
||||
```
|
||||
|
||||
**What It Teaches:**
|
||||
Tool composition - some tools are designed to work together.
|
||||
|
||||
**LLM Behavior Insight:**
|
||||
LLMs may not naturally discover tool composition patterns.
|
||||
|
||||
---
|
||||
|
||||
## Why These Anti-Patterns Matter
|
||||
|
||||
**Addresses LLM Training Bias:**
|
||||
LLMs default to "load everything" behavior from web scraping training data where thoroughness was rewarded.
|
||||
|
||||
**Teaches Protocol Awareness:**
|
||||
HTTP APIs and MCP have real token limits that can break the system.
|
||||
|
||||
**Prevents User Frustration:**
|
||||
Token limit errors confuse users and break workflows.
|
||||
|
||||
**Builds Good Habits:**
|
||||
Anti-patterns teach the "why" behind best practices.
|
||||
|
||||
**Makes Implicit Explicit:**
|
||||
Surfaces mental models that experienced users internalize but novices miss.
|
||||
|
||||
---
|
||||
|
||||
## What Happens If These Are Ignored
|
||||
|
||||
- **No progressive disclosure**: Every search loads limit=20 in full format → token exhaustion
|
||||
- **Over-requesting**: 15,000 token searches for 2 relevant results
|
||||
- **Wrong tool**: Generic search when specialized filters would be 10x faster
|
||||
- **Premature expansion**: Load full details before knowing relevance
|
||||
- **Missing composition**: Single-tool thinking, missing powerful multi-step workflows
|
||||
|
||||
**Bottom Line:** These anti-patterns waste 5-10x more tokens than necessary and frequently cause system failures.
|
||||
@@ -0,0 +1,120 @@
|
||||
# Progressive Disclosure Pattern (MANDATORY)
|
||||
|
||||
**Core Principle**: Find the smallest set of high-signal tokens first (index format), then drill down to full details only for relevant items.
|
||||
|
||||
## The 4-Step Workflow
|
||||
|
||||
### Step 1: Start with Index Format
|
||||
|
||||
**Action:**
|
||||
- Use `format=index` (default in most operations)
|
||||
- Set `limit=3-5` (not 20)
|
||||
- Review titles and dates ONLY
|
||||
|
||||
**Token Cost:** ~50-100 tokens per result
|
||||
|
||||
**Why:** Minimal token investment for maximum signal. Get overview before committing to full details.
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -s "http://localhost:37777/api/search/observations?query=authentication&format=index&limit=5"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"query": "authentication",
|
||||
"count": 5,
|
||||
"format": "index",
|
||||
"results": [
|
||||
{
|
||||
"id": 1234,
|
||||
"type": "feature",
|
||||
"title": "Implemented JWT authentication",
|
||||
"subtitle": "Added token-based auth with refresh tokens",
|
||||
"created_at_epoch": 1699564800000,
|
||||
"project": "api-server"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Identify Relevant Items
|
||||
|
||||
**Cognitive Task:**
|
||||
- Scan index results for relevance
|
||||
- Note which items need full details
|
||||
- Discard irrelevant items
|
||||
|
||||
**Why:** Human-in-the-loop filtering before expensive operations. Don't load full details for items you'll ignore.
|
||||
|
||||
### Step 3: Request Full Details (Selectively)
|
||||
|
||||
**Action:**
|
||||
- Use `format=full` ONLY for specific items of interest
|
||||
- Target by ID or use refined search query
|
||||
|
||||
**Token Cost:** ~500-1000 tokens per result
|
||||
|
||||
**Principle:** Load only what you need
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
# After reviewing index, get full details for observation #1234
|
||||
curl -s "http://localhost:37777/api/search/observations?query=authentication&format=full&limit=1&offset=2"
|
||||
```
|
||||
|
||||
**Why:** Targeted token expenditure with high ROI. 10x cost difference means selectivity matters.
|
||||
|
||||
### Step 4: Refine with Filters (If Needed)
|
||||
|
||||
**Techniques:**
|
||||
- Use `type`, `dateStart`/`dateEnd`, `concepts`, `files` filters
|
||||
- Narrow scope BEFORE requesting more results
|
||||
- Use `offset` for pagination instead of large limits
|
||||
|
||||
**Why:** Reduce result set first, then expand selectively. Don't load 20 results when filters could narrow to 3.
|
||||
|
||||
## Token Budget Awareness
|
||||
|
||||
**Costs:**
|
||||
- Index result: ~50-100 tokens
|
||||
- Full result: ~500-1000 tokens
|
||||
- 10x cost difference
|
||||
|
||||
**Starting Points:**
|
||||
- Start with `limit=3-5` (not 20)
|
||||
- Reduce limit if hitting token errors
|
||||
|
||||
**Savings Example:**
|
||||
- Naive: 10 items × 750 tokens (avg full) = 7,500 tokens
|
||||
- Progressive: (5 items × 75 tokens index) + (2 items × 750 tokens full) = 1,875 tokens
|
||||
- **Savings: 5,625 tokens (75% reduction)**
|
||||
|
||||
## What Problems This Solves
|
||||
|
||||
1. **Token exhaustion**: Without this, LLMs load everything in full format (9,000+ tokens for 10 items)
|
||||
2. **Poor signal-to-noise**: Loading full details for irrelevant items wastes tokens
|
||||
3. **MCP limits**: Large payloads hit protocol limits (system failures)
|
||||
4. **Inefficiency**: Loading 20 full results when only 2 are relevant
|
||||
|
||||
## How It Scales
|
||||
|
||||
**With 10 records:**
|
||||
- Index (500 tokens) → Full (2,000 tokens for 2 relevant) = 2,500 tokens
|
||||
- Without pattern: Full (10,000 tokens for all 10) = 4x more expensive
|
||||
|
||||
**With 1,000 records:**
|
||||
- Index (500 tokens for top 5) → Full (1,000 tokens for 1 relevant) = 1,500 tokens
|
||||
- Without pattern: Would hit MCP limits before seeing relevant data
|
||||
|
||||
## Context Engineering Alignment
|
||||
|
||||
This pattern implements core context engineering principles:
|
||||
|
||||
- **Just-in-time context**: Load data dynamically at runtime
|
||||
- **Progressive disclosure**: Lightweight identifiers (index) → full details as needed
|
||||
- **Token efficiency**: Minimal high-signal tokens first, expand selectively
|
||||
- **Attention budget**: Treat context as finite resource with diminishing returns
|
||||
|
||||
Always start with the smallest set of high-signal tokens that maximize likelihood of desired outcome.
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
name: troubleshoot
|
||||
description: Diagnose and fix claude-mem installation issues. Checks PM2 worker status, database integrity, service health, dependencies, and provides automated fixes for common problems.
|
||||
---
|
||||
|
||||
# Claude-Mem Troubleshooting Skill
|
||||
|
||||
Diagnose and resolve installation and operational issues with the claude-mem plugin.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
**Invoke this skill when:**
|
||||
- Memory not persisting after `/clear`
|
||||
- Viewer UI empty or not loading
|
||||
- Worker service not running
|
||||
- Database missing or corrupted
|
||||
- Port conflicts
|
||||
- Missing dependencies
|
||||
- "Nothing is remembered" complaints
|
||||
- Search results empty when they shouldn't be
|
||||
|
||||
**Do NOT invoke** for feature requests or usage questions (use regular documentation for that).
|
||||
|
||||
## Quick Decision Guide
|
||||
|
||||
Once the skill is loaded, choose the appropriate operation:
|
||||
|
||||
**What's the problem?**
|
||||
|
||||
- "Nothing is being remembered" → [operations/common-issues.md](operations/common-issues.md#nothing-remembered)
|
||||
- "Viewer is empty" → [operations/common-issues.md](operations/common-issues.md#viewer-empty)
|
||||
- "Worker won't start" → [operations/common-issues.md](operations/common-issues.md#worker-not-starting)
|
||||
- "Want to run full diagnostics" → [operations/diagnostics.md](operations/diagnostics.md)
|
||||
- "Need automated fix" → [operations/automated-fixes.md](operations/automated-fixes.md)
|
||||
|
||||
## Available Operations
|
||||
|
||||
Choose the appropriate operation file for detailed instructions:
|
||||
|
||||
### Diagnostic Workflows
|
||||
1. **[Full System Diagnostics](operations/diagnostics.md)** - Comprehensive step-by-step diagnostic workflow
|
||||
2. **[Worker Diagnostics](operations/worker.md)** - PM2 worker-specific troubleshooting
|
||||
3. **[Database Diagnostics](operations/database.md)** - Database integrity and data checks
|
||||
|
||||
### Issue Resolution
|
||||
4. **[Common Issues](operations/common-issues.md)** - Quick fixes for frequently encountered problems
|
||||
5. **[Automated Fixes](operations/automated-fixes.md)** - One-command fix sequences
|
||||
|
||||
### Reference
|
||||
6. **[Quick Commands](operations/reference.md)** - Essential commands for troubleshooting
|
||||
|
||||
## Quick Start
|
||||
|
||||
**Fast automated fix (try this first):**
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
|
||||
pm2 delete claude-mem-worker 2>/dev/null; \
|
||||
npm install && \
|
||||
node_modules/.bin/pm2 start ecosystem.config.cjs && \
|
||||
sleep 3 && \
|
||||
curl -s http://127.0.0.1:37777/health
|
||||
```
|
||||
|
||||
Expected output: `{"status":"ok"}`
|
||||
|
||||
If that doesn't work, proceed to detailed diagnostics.
|
||||
|
||||
## Response Format
|
||||
|
||||
When troubleshooting:
|
||||
1. **Identify the symptom** - What's the user reporting?
|
||||
2. **Choose operation file** - Use the decision guide above
|
||||
3. **Follow steps systematically** - Don't skip diagnostic steps
|
||||
4. **Report findings** - Tell user what you found and what was fixed
|
||||
5. **Verify resolution** - Confirm the issue is resolved
|
||||
|
||||
## Technical Notes
|
||||
|
||||
- **Worker port:** Default 37777 (configurable via `CLAUDE_MEM_WORKER_PORT`)
|
||||
- **Database location:** `~/.claude-mem/claude-mem.db`
|
||||
- **Plugin location:** `~/.claude/plugins/marketplaces/thedotmack/`
|
||||
- **PM2 process name:** `claude-mem-worker`
|
||||
|
||||
## Error Reporting
|
||||
|
||||
If troubleshooting doesn't resolve the issue, collect diagnostic data and direct user to:
|
||||
https://github.com/thedotmack/claude-mem/issues
|
||||
|
||||
See [operations/diagnostics.md](operations/diagnostics.md#reporting-issues) for details on what to collect.
|
||||
@@ -0,0 +1,151 @@
|
||||
# Automated Fix Sequences
|
||||
|
||||
One-command fix sequences for common claude-mem issues.
|
||||
|
||||
## Quick Fix: Complete Reset and Restart
|
||||
|
||||
**Use when:** General issues, worker not responding, after updates
|
||||
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
|
||||
pm2 delete claude-mem-worker 2>/dev/null; \
|
||||
npm install && \
|
||||
node_modules/.bin/pm2 start ecosystem.config.cjs && \
|
||||
sleep 3 && \
|
||||
curl -s http://127.0.0.1:37777/health
|
||||
```
|
||||
|
||||
**Expected output:** `{"status":"ok"}`
|
||||
|
||||
**What it does:**
|
||||
1. Stops the worker (if running)
|
||||
2. Ensures dependencies are installed
|
||||
3. Starts worker with local PM2
|
||||
4. Waits for startup
|
||||
5. Verifies health
|
||||
|
||||
## Fix: Worker Not Running
|
||||
|
||||
**Use when:** PM2 shows worker as stopped or not listed
|
||||
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
|
||||
node_modules/.bin/pm2 start ecosystem.config.cjs && \
|
||||
sleep 2 && \
|
||||
pm2 status
|
||||
```
|
||||
|
||||
**Expected output:** Worker shows as "online"
|
||||
|
||||
## Fix: Dependencies Missing
|
||||
|
||||
**Use when:** Worker won't start due to missing packages
|
||||
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
|
||||
npm install && \
|
||||
pm2 restart claude-mem-worker
|
||||
```
|
||||
|
||||
## Fix: Port Conflict
|
||||
|
||||
**Use when:** Error shows port already in use
|
||||
|
||||
```bash
|
||||
# Change to port 37778
|
||||
mkdir -p ~/.claude-mem && \
|
||||
echo '{"env":{"CLAUDE_MEM_WORKER_PORT":"37778"}}' > ~/.claude-mem/settings.json && \
|
||||
pm2 restart claude-mem-worker && \
|
||||
sleep 2 && \
|
||||
curl -s http://127.0.0.1:37778/health
|
||||
```
|
||||
|
||||
**Expected output:** `{"status":"ok"}`
|
||||
|
||||
## Fix: Database Issues
|
||||
|
||||
**Use when:** Database appears corrupted or out of sync
|
||||
|
||||
```bash
|
||||
# Backup and test integrity
|
||||
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup && \
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;" && \
|
||||
pm2 restart claude-mem-worker
|
||||
```
|
||||
|
||||
**If integrity check fails, recreate database:**
|
||||
```bash
|
||||
# WARNING: This deletes all memory data
|
||||
mv ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.old && \
|
||||
pm2 restart claude-mem-worker
|
||||
```
|
||||
|
||||
## Fix: Clean Reinstall
|
||||
|
||||
**Use when:** All else fails, nuclear option
|
||||
|
||||
```bash
|
||||
# Backup data first
|
||||
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup 2>/dev/null
|
||||
|
||||
# Stop and remove worker
|
||||
pm2 delete claude-mem-worker 2>/dev/null
|
||||
|
||||
# Reinstall dependencies
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
|
||||
rm -rf node_modules && \
|
||||
npm install
|
||||
|
||||
# Start worker
|
||||
node_modules/.bin/pm2 start ecosystem.config.cjs && \
|
||||
sleep 3 && \
|
||||
curl -s http://127.0.0.1:37777/health
|
||||
```
|
||||
|
||||
## Fix: Clear PM2 Logs
|
||||
|
||||
**Use when:** Logs are too large, want fresh start
|
||||
|
||||
```bash
|
||||
pm2 flush claude-mem-worker && \
|
||||
pm2 restart claude-mem-worker
|
||||
```
|
||||
|
||||
## Verification Commands
|
||||
|
||||
**After running any fix, verify with these:**
|
||||
|
||||
```bash
|
||||
# Check worker status
|
||||
pm2 status | grep claude-mem-worker
|
||||
|
||||
# Check health
|
||||
curl -s http://127.0.0.1:37777/health
|
||||
|
||||
# Check database
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations;"
|
||||
|
||||
# Check viewer
|
||||
curl -s http://127.0.0.1:37777/api/stats
|
||||
|
||||
# Check logs for errors
|
||||
pm2 logs claude-mem-worker --lines 20 --nostream | grep -i error
|
||||
```
|
||||
|
||||
**All checks should pass:**
|
||||
- Worker status: "online"
|
||||
- Health: `{"status":"ok"}`
|
||||
- Database: Shows count (may be 0 if new)
|
||||
- Stats: Returns JSON with counts
|
||||
- Logs: No recent errors
|
||||
|
||||
## Troubleshooting the Fixes
|
||||
|
||||
**If automated fix fails:**
|
||||
1. Run the diagnostic script from [diagnostics.md](diagnostics.md)
|
||||
2. Check specific error in PM2 logs
|
||||
3. Try manual worker start to see detailed error:
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
node plugin/scripts/worker-service.cjs
|
||||
```
|
||||
@@ -0,0 +1,232 @@
|
||||
# Common Issue Resolutions
|
||||
|
||||
Quick fixes for frequently encountered claude-mem problems.
|
||||
|
||||
## Issue: Nothing is Remembered After `/clear` {#nothing-remembered}
|
||||
|
||||
**Symptoms:**
|
||||
- Data doesn't persist across sessions
|
||||
- Context is empty after `/clear`
|
||||
- Search returns no results for past work
|
||||
|
||||
**Root cause:** Sessions are marked complete but data should persist. This suggests:
|
||||
- Worker not processing observations
|
||||
- Database not being written to
|
||||
- Context hook not reading from database
|
||||
|
||||
**Fix:**
|
||||
1. Verify worker is running:
|
||||
```bash
|
||||
pm2 jlist | grep claude-mem-worker
|
||||
```
|
||||
|
||||
2. Check database has recent observations:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations WHERE created_at > datetime('now', '-1 day');"
|
||||
```
|
||||
|
||||
3. Restart worker and start new session:
|
||||
```bash
|
||||
pm2 restart claude-mem-worker
|
||||
```
|
||||
|
||||
4. Create a test observation: `/skill version-bump` then cancel
|
||||
|
||||
5. Check if observation appears in viewer:
|
||||
```bash
|
||||
open http://127.0.0.1:37777
|
||||
# Or manually check database:
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT * FROM observations ORDER BY created_at DESC LIMIT 1;"
|
||||
```
|
||||
|
||||
## Issue: Viewer Empty After Every Claude Restart {#viewer-empty}
|
||||
|
||||
**Symptoms:**
|
||||
- Viewer shows no data at http://127.0.0.1:37777
|
||||
- Stats endpoint returns all zeros
|
||||
- Database appears empty in UI
|
||||
|
||||
**Root cause:**
|
||||
- Database being recreated on startup (shouldn't happen)
|
||||
- Worker reading from wrong database location
|
||||
- Database permissions issue
|
||||
|
||||
**Fix:**
|
||||
1. Check database file exists and has data:
|
||||
```bash
|
||||
ls -lh ~/.claude-mem/claude-mem.db
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations;"
|
||||
```
|
||||
|
||||
2. Check file permissions:
|
||||
```bash
|
||||
ls -la ~/.claude-mem/claude-mem.db
|
||||
# Should be readable/writable by your user
|
||||
```
|
||||
|
||||
3. Verify worker is using correct database path in logs:
|
||||
```bash
|
||||
pm2 logs claude-mem-worker --lines 50 --nostream | grep "Database"
|
||||
```
|
||||
|
||||
4. Test viewer connection manually:
|
||||
```bash
|
||||
curl -s http://127.0.0.1:37777/api/stats
|
||||
# Should show non-zero counts if data exists
|
||||
```
|
||||
|
||||
## Issue: Old Memory in Claude {#old-memory}
|
||||
|
||||
**Symptoms:**
|
||||
- Context contains outdated observations
|
||||
- Irrelevant past work appearing in sessions
|
||||
- Context feels stale
|
||||
|
||||
**Root cause:** Context hook injecting stale observations
|
||||
|
||||
**Fix:**
|
||||
1. Check the observation count setting:
|
||||
```bash
|
||||
grep CLAUDE_MEM_CONTEXT_OBSERVATIONS ~/.claude/settings.json
|
||||
```
|
||||
|
||||
2. Default is 50 observations - you can adjust this:
|
||||
```json
|
||||
{
|
||||
"env": {
|
||||
"CLAUDE_MEM_CONTEXT_OBSERVATIONS": "25"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Check database for actual observation dates:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT created_at, project, title FROM observations ORDER BY created_at DESC LIMIT 10;"
|
||||
```
|
||||
|
||||
4. Consider filtering by project if working on multiple codebases
|
||||
|
||||
## Issue: Worker Not Starting {#worker-not-starting}
|
||||
|
||||
**Symptoms:**
|
||||
- PM2 shows worker as "stopped" or "errored"
|
||||
- Health check fails
|
||||
- Viewer not accessible
|
||||
|
||||
**Root cause:**
|
||||
- Port already in use
|
||||
- PM2 not installed or not in PATH
|
||||
- Missing dependencies
|
||||
|
||||
**Fix:**
|
||||
1. Try manual worker start to see error:
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
node plugin/scripts/worker-service.cjs
|
||||
# Should start server on port 37777 or show error
|
||||
```
|
||||
|
||||
2. If port in use, change it:
|
||||
```bash
|
||||
mkdir -p ~/.claude-mem
|
||||
echo '{"env":{"CLAUDE_MEM_WORKER_PORT":"37778"}}' > ~/.claude-mem/settings.json
|
||||
```
|
||||
|
||||
3. If dependencies missing:
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
npm install
|
||||
pm2 start ecosystem.config.cjs
|
||||
```
|
||||
|
||||
## Issue: Search Results Empty
|
||||
|
||||
**Symptoms:**
|
||||
- Search skill returns no results
|
||||
- API endpoints return empty arrays
|
||||
- Know there's data but can't find it
|
||||
|
||||
**Root cause:**
|
||||
- FTS5 tables not synchronized
|
||||
- Wrong project filter
|
||||
- Database not being queried correctly
|
||||
|
||||
**Fix:**
|
||||
1. Check if observations exist in database:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations;"
|
||||
```
|
||||
|
||||
2. Check FTS5 table sync:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations_fts;"
|
||||
# Should match observation count
|
||||
```
|
||||
|
||||
3. Try search via API directly:
|
||||
```bash
|
||||
curl "http://127.0.0.1:37777/api/search/observations?q=test&format=index"
|
||||
```
|
||||
|
||||
4. If FTS5 out of sync, restart worker (triggers reindex):
|
||||
```bash
|
||||
pm2 restart claude-mem-worker
|
||||
```
|
||||
|
||||
## Issue: Port Conflicts
|
||||
|
||||
**Symptoms:**
|
||||
- Worker won't start
|
||||
- Error: "EADDRINUSE: address already in use"
|
||||
- Health check fails
|
||||
|
||||
**Fix:**
|
||||
1. Check what's using port 37777:
|
||||
```bash
|
||||
lsof -i :37777
|
||||
```
|
||||
|
||||
2. Either kill the conflicting process or change claude-mem port:
|
||||
```bash
|
||||
mkdir -p ~/.claude-mem
|
||||
echo '{"env":{"CLAUDE_MEM_WORKER_PORT":"37778"}}' > ~/.claude-mem/settings.json
|
||||
pm2 restart claude-mem-worker
|
||||
```
|
||||
|
||||
## Issue: Database Corrupted
|
||||
|
||||
**Symptoms:**
|
||||
- SQLite errors in logs
|
||||
- Worker crashes on startup
|
||||
- Queries fail
|
||||
|
||||
**Fix:**
|
||||
1. Backup the database:
|
||||
```bash
|
||||
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup
|
||||
```
|
||||
|
||||
2. Try to repair:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;"
|
||||
```
|
||||
|
||||
3. If repair fails, recreate (loses data):
|
||||
```bash
|
||||
rm ~/.claude-mem/claude-mem.db
|
||||
pm2 restart claude-mem-worker
|
||||
# Worker will create new database
|
||||
```
|
||||
|
||||
## Prevention Tips
|
||||
|
||||
**Keep claude-mem healthy:**
|
||||
- Regularly check viewer UI to see if observations are being captured
|
||||
- Monitor database size (shouldn't grow unbounded)
|
||||
- Update plugin when new versions are released
|
||||
- Keep Claude Code updated
|
||||
|
||||
**Performance tuning:**
|
||||
- Adjust `CLAUDE_MEM_CONTEXT_OBSERVATIONS` if context is too large/small
|
||||
- Use `/clear` to mark sessions complete and start fresh
|
||||
- Use search skill to query specific memories instead of loading everything
|
||||
@@ -0,0 +1,403 @@
|
||||
# Database Diagnostics
|
||||
|
||||
SQLite database troubleshooting for claude-mem.
|
||||
|
||||
## Database Overview
|
||||
|
||||
Claude-mem uses SQLite3 for persistent storage:
|
||||
- **Location:** `~/.claude-mem/claude-mem.db`
|
||||
- **Library:** better-sqlite3 (synchronous, not bun:sqlite)
|
||||
- **Features:** FTS5 full-text search, triggers, indexes
|
||||
- **Tables:** observations, sessions, user_prompts, observations_fts, sessions_fts, prompts_fts
|
||||
|
||||
## Basic Database Checks
|
||||
|
||||
### Check Database Exists
|
||||
|
||||
```bash
|
||||
# Check file exists
|
||||
ls -lh ~/.claude-mem/claude-mem.db
|
||||
|
||||
# Check file size
|
||||
du -h ~/.claude-mem/claude-mem.db
|
||||
|
||||
# Check permissions
|
||||
ls -la ~/.claude-mem/claude-mem.db
|
||||
```
|
||||
|
||||
**Expected:**
|
||||
- File exists
|
||||
- Size: 100KB - 10MB+ (depends on usage)
|
||||
- Permissions: Readable/writable by your user
|
||||
|
||||
### Check Database Integrity
|
||||
|
||||
```bash
|
||||
# Run integrity check
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;"
|
||||
```
|
||||
|
||||
**Expected output:** `ok`
|
||||
|
||||
**If errors appear:**
|
||||
- Database corrupted
|
||||
- Backup immediately: `cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup`
|
||||
- Consider recreating (data loss)
|
||||
|
||||
## Data Inspection
|
||||
|
||||
### Count Records
|
||||
|
||||
```bash
|
||||
# Observation count
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations;"
|
||||
|
||||
# Session count
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM sessions;"
|
||||
|
||||
# User prompt count
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM user_prompts;"
|
||||
|
||||
# FTS5 table counts (should match main tables)
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations_fts;"
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM sessions_fts;"
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM prompts_fts;"
|
||||
```
|
||||
|
||||
### View Recent Records
|
||||
|
||||
```bash
|
||||
# Recent observations
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
SELECT
|
||||
created_at,
|
||||
type,
|
||||
title,
|
||||
project
|
||||
FROM observations
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 10;
|
||||
"
|
||||
|
||||
# Recent sessions
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
SELECT
|
||||
created_at,
|
||||
request,
|
||||
project
|
||||
FROM sessions
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 5;
|
||||
"
|
||||
|
||||
# Recent user prompts
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
SELECT
|
||||
created_at,
|
||||
prompt
|
||||
FROM user_prompts
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 10;
|
||||
"
|
||||
```
|
||||
|
||||
### Check Projects
|
||||
|
||||
```bash
|
||||
# List all projects
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
SELECT DISTINCT project
|
||||
FROM observations
|
||||
ORDER BY project;
|
||||
"
|
||||
|
||||
# Count observations per project
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
SELECT
|
||||
project,
|
||||
COUNT(*) as count
|
||||
FROM observations
|
||||
GROUP BY project
|
||||
ORDER BY count DESC;
|
||||
"
|
||||
```
|
||||
|
||||
## Database Schema
|
||||
|
||||
### View Table Structure
|
||||
|
||||
```bash
|
||||
# List all tables
|
||||
sqlite3 ~/.claude-mem/claude-mem.db ".tables"
|
||||
|
||||
# Show observations table schema
|
||||
sqlite3 ~/.claude-mem/claude-mem.db ".schema observations"
|
||||
|
||||
# Show all schemas
|
||||
sqlite3 ~/.claude-mem/claude-mem.db ".schema"
|
||||
```
|
||||
|
||||
### Expected Tables
|
||||
|
||||
- `observations` - Main observation records
|
||||
- `observations_fts` - FTS5 virtual table for full-text search
|
||||
- `sessions` - Session summary records
|
||||
- `sessions_fts` - FTS5 virtual table for session search
|
||||
- `user_prompts` - User prompt records
|
||||
- `prompts_fts` - FTS5 virtual table for prompt search
|
||||
|
||||
## FTS5 Synchronization
|
||||
|
||||
The FTS5 tables should stay synchronized with main tables via triggers.
|
||||
|
||||
### Check FTS5 Sync
|
||||
|
||||
```bash
|
||||
# Compare counts
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM observations) as observations,
|
||||
(SELECT COUNT(*) FROM observations_fts) as observations_fts,
|
||||
(SELECT COUNT(*) FROM sessions) as sessions,
|
||||
(SELECT COUNT(*) FROM sessions_fts) as sessions_fts,
|
||||
(SELECT COUNT(*) FROM user_prompts) as prompts,
|
||||
(SELECT COUNT(*) FROM prompts_fts) as prompts_fts;
|
||||
"
|
||||
```
|
||||
|
||||
**Expected:** All pairs should match (observations = observations_fts, etc.)
|
||||
|
||||
### Fix FTS5 Desync
|
||||
|
||||
If FTS5 counts don't match, triggers may have failed. Restart worker to rebuild:
|
||||
|
||||
```bash
|
||||
pm2 restart claude-mem-worker
|
||||
```
|
||||
|
||||
The worker will rebuild FTS5 indexes on startup if they're out of sync.
|
||||
|
||||
## Common Database Issues
|
||||
|
||||
### Issue: Database Doesn't Exist
|
||||
|
||||
**Cause:** First run, or database was deleted
|
||||
|
||||
**Fix:** Database will be created automatically on first observation. No action needed.
|
||||
|
||||
### Issue: Database is Empty (0 Records)
|
||||
|
||||
**Cause:**
|
||||
- New installation (normal)
|
||||
- Data was deleted
|
||||
- Worker not processing observations
|
||||
|
||||
**Fix:**
|
||||
1. Create test observation (use any skill and cancel)
|
||||
2. Check worker logs for errors:
|
||||
```bash
|
||||
pm2 logs claude-mem-worker --lines 50 --nostream
|
||||
```
|
||||
3. Verify observation appears in database
|
||||
|
||||
### Issue: Database Permission Denied
|
||||
|
||||
**Cause:** File permissions wrong, database owned by different user
|
||||
|
||||
**Fix:**
|
||||
```bash
|
||||
# Check ownership
|
||||
ls -la ~/.claude-mem/claude-mem.db
|
||||
|
||||
# Fix permissions (if needed)
|
||||
chmod 644 ~/.claude-mem/claude-mem.db
|
||||
chown $USER ~/.claude-mem/claude-mem.db
|
||||
```
|
||||
|
||||
### Issue: Database Locked
|
||||
|
||||
**Cause:**
|
||||
- Multiple processes accessing database
|
||||
- Crash left lock file
|
||||
- Long-running transaction
|
||||
|
||||
**Fix:**
|
||||
```bash
|
||||
# Check for lock file
|
||||
ls -la ~/.claude-mem/claude-mem.db-wal
|
||||
ls -la ~/.claude-mem/claude-mem.db-shm
|
||||
|
||||
# Remove lock files (only if worker is stopped!)
|
||||
pm2 stop claude-mem-worker
|
||||
rm ~/.claude-mem/claude-mem.db-wal ~/.claude-mem/claude-mem.db-shm
|
||||
pm2 start claude-mem-worker
|
||||
```
|
||||
|
||||
### Issue: Database Growing Too Large
|
||||
|
||||
**Cause:** Too many observations accumulated
|
||||
|
||||
**Check size:**
|
||||
```bash
|
||||
du -h ~/.claude-mem/claude-mem.db
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations;"
|
||||
```
|
||||
|
||||
**Options:**
|
||||
1. Delete old observations (manual cleanup):
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
DELETE FROM observations
|
||||
WHERE created_at < datetime('now', '-90 days');
|
||||
"
|
||||
```
|
||||
|
||||
2. Vacuum to reclaim space:
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "VACUUM;"
|
||||
```
|
||||
|
||||
3. Archive and start fresh:
|
||||
```bash
|
||||
mv ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.archive
|
||||
pm2 restart claude-mem-worker
|
||||
```
|
||||
|
||||
## Database Recovery
|
||||
|
||||
### Backup Database
|
||||
|
||||
**Before any destructive operations:**
|
||||
```bash
|
||||
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup
|
||||
```
|
||||
|
||||
### Restore from Backup
|
||||
|
||||
```bash
|
||||
pm2 stop claude-mem-worker
|
||||
cp ~/.claude-mem/claude-mem.db.backup ~/.claude-mem/claude-mem.db
|
||||
pm2 start claude-mem-worker
|
||||
```
|
||||
|
||||
### Export Data
|
||||
|
||||
Export to JSON for safekeeping:
|
||||
|
||||
```bash
|
||||
# Export observations
|
||||
sqlite3 ~/.claude-mem/claude-mem.db -json "SELECT * FROM observations;" > observations.json
|
||||
|
||||
# Export sessions
|
||||
sqlite3 ~/.claude-mem/claude-mem.db -json "SELECT * FROM sessions;" > sessions.json
|
||||
|
||||
# Export prompts
|
||||
sqlite3 ~/.claude-mem/claude-mem.db -json "SELECT * FROM user_prompts;" > prompts.json
|
||||
```
|
||||
|
||||
### Recreate Database
|
||||
|
||||
**WARNING: Data loss. Backup first!**
|
||||
|
||||
```bash
|
||||
# Stop worker
|
||||
pm2 stop claude-mem-worker
|
||||
|
||||
# Backup current database
|
||||
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.old
|
||||
|
||||
# Delete database
|
||||
rm ~/.claude-mem/claude-mem.db
|
||||
|
||||
# Start worker (creates new database)
|
||||
pm2 start claude-mem-worker
|
||||
```
|
||||
|
||||
## Database Statistics
|
||||
|
||||
### Storage Analysis
|
||||
|
||||
```bash
|
||||
# Database file size
|
||||
du -h ~/.claude-mem/claude-mem.db
|
||||
|
||||
# Record counts by type
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
SELECT
|
||||
type,
|
||||
COUNT(*) as count
|
||||
FROM observations
|
||||
GROUP BY type
|
||||
ORDER BY count DESC;
|
||||
"
|
||||
|
||||
# Observations per month
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
SELECT
|
||||
strftime('%Y-%m', created_at) as month,
|
||||
COUNT(*) as count
|
||||
FROM observations
|
||||
GROUP BY month
|
||||
ORDER BY month DESC;
|
||||
"
|
||||
|
||||
# Average observation size (characters)
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
SELECT
|
||||
AVG(LENGTH(content)) as avg_content_length,
|
||||
MAX(LENGTH(content)) as max_content_length
|
||||
FROM observations;
|
||||
"
|
||||
```
|
||||
|
||||
## Advanced Queries
|
||||
|
||||
### Find Specific Observations
|
||||
|
||||
```bash
|
||||
# Search by keyword (FTS5)
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
SELECT title, created_at
|
||||
FROM observations_fts
|
||||
WHERE observations_fts MATCH 'authentication'
|
||||
ORDER BY created_at DESC;
|
||||
"
|
||||
|
||||
# Find by type
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
SELECT title, created_at
|
||||
FROM observations
|
||||
WHERE type = 'bugfix'
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 10;
|
||||
"
|
||||
|
||||
# Find by file path
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
SELECT title, created_at
|
||||
FROM observations
|
||||
WHERE file_path LIKE '%auth%'
|
||||
ORDER BY created_at DESC;
|
||||
"
|
||||
```
|
||||
|
||||
## Database Maintenance
|
||||
|
||||
### Regular Maintenance Tasks
|
||||
|
||||
```bash
|
||||
# Analyze for query optimization
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "ANALYZE;"
|
||||
|
||||
# Rebuild FTS5 indexes
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "
|
||||
INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
|
||||
INSERT INTO sessions_fts(sessions_fts) VALUES('rebuild');
|
||||
INSERT INTO prompts_fts(prompts_fts) VALUES('rebuild');
|
||||
"
|
||||
|
||||
# Vacuum to reclaim space
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "VACUUM;"
|
||||
```
|
||||
|
||||
**Run monthly to keep database healthy.**
|
||||
@@ -0,0 +1,219 @@
|
||||
# Full System Diagnostics
|
||||
|
||||
Comprehensive step-by-step diagnostic workflow for claude-mem issues.
|
||||
|
||||
## Diagnostic Workflow
|
||||
|
||||
Run these checks systematically to identify the root cause:
|
||||
|
||||
### 1. Check PM2 Worker Status
|
||||
|
||||
First, verify if the worker service is running:
|
||||
|
||||
```bash
|
||||
# Check if PM2 is available
|
||||
which pm2 || echo "PM2 not found in PATH"
|
||||
|
||||
# List PM2 processes
|
||||
pm2 jlist 2>&1
|
||||
|
||||
# If pm2 is not found, try the local installation
|
||||
~/.claude/plugins/marketplaces/thedotmack/node_modules/.bin/pm2 jlist 2>&1
|
||||
```
|
||||
|
||||
**Expected output:** JSON array with `claude-mem-worker` process showing `"status": "online"`
|
||||
|
||||
**If worker not running or status is not "online":**
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
pm2 start ecosystem.config.cjs
|
||||
# Or use local pm2:
|
||||
node_modules/.bin/pm2 start ecosystem.config.cjs
|
||||
```
|
||||
|
||||
### 2. Check Worker Service Health
|
||||
|
||||
Test if the worker service responds to HTTP requests:
|
||||
|
||||
```bash
|
||||
# Default port is 37777
|
||||
curl -s http://127.0.0.1:37777/health
|
||||
|
||||
# Check custom port from settings
|
||||
PORT=$(cat ~/.claude-mem/settings.json 2>/dev/null | grep CLAUDE_MEM_WORKER_PORT | grep -o '[0-9]\+' || echo "37777")
|
||||
curl -s http://127.0.0.1:$PORT/health
|
||||
```
|
||||
|
||||
**Expected output:** `{"status":"ok"}`
|
||||
|
||||
**If connection refused:**
|
||||
- Worker not running → Go back to step 1
|
||||
- Port conflict → Check what's using the port:
|
||||
```bash
|
||||
lsof -i :37777 || netstat -tlnp | grep 37777
|
||||
```
|
||||
|
||||
### 3. Check Database
|
||||
|
||||
Verify the database exists and contains data:
|
||||
|
||||
```bash
|
||||
# Check if database file exists
|
||||
ls -lh ~/.claude-mem/claude-mem.db
|
||||
|
||||
# Check database size (should be > 0 bytes)
|
||||
du -h ~/.claude-mem/claude-mem.db
|
||||
|
||||
# Query database for observation count (requires sqlite3)
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) as observation_count FROM observations;" 2>&1
|
||||
|
||||
# Query for session count
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) as session_count FROM sessions;" 2>&1
|
||||
|
||||
# Check recent observations
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT created_at, type, title FROM observations ORDER BY created_at DESC LIMIT 5;" 2>&1
|
||||
```
|
||||
|
||||
**Expected:**
|
||||
- Database file exists (typically 100KB - 10MB+)
|
||||
- Contains observations and sessions
|
||||
- Recent observations visible
|
||||
|
||||
**If database missing or empty:**
|
||||
- New installation - this is normal, database will populate as you work
|
||||
- After `/clear` - sessions are marked complete but not deleted, data should persist
|
||||
- Corrupted database - backup and recreate:
|
||||
```bash
|
||||
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup
|
||||
# Worker will recreate on next observation
|
||||
```
|
||||
|
||||
### 4. Check Dependencies Installation
|
||||
|
||||
Verify all required npm packages are installed:
|
||||
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
|
||||
# Check for critical packages
|
||||
ls node_modules/@anthropic-ai/claude-agent-sdk 2>&1 | head -1
|
||||
ls node_modules/better-sqlite3 2>&1 | head -1
|
||||
ls node_modules/express 2>&1 | head -1
|
||||
ls node_modules/pm2 2>&1 | head -1
|
||||
```
|
||||
|
||||
**Expected:** All critical packages present
|
||||
|
||||
**If dependencies missing:**
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
npm install
|
||||
```
|
||||
|
||||
### 5. Check Worker Logs
|
||||
|
||||
Review recent worker logs for errors:
|
||||
|
||||
```bash
|
||||
# View last 50 lines of worker logs
|
||||
pm2 logs claude-mem-worker --lines 50 --nostream
|
||||
|
||||
# Or use local pm2:
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
node_modules/.bin/pm2 logs claude-mem-worker --lines 50 --nostream
|
||||
|
||||
# Check for specific errors
|
||||
pm2 logs claude-mem-worker --lines 100 --nostream | grep -i "error\|exception\|failed"
|
||||
```
|
||||
|
||||
### 6. Test Viewer UI
|
||||
|
||||
Check if the web viewer is accessible:
|
||||
|
||||
```bash
|
||||
# Test viewer endpoint
|
||||
curl -s http://127.0.0.1:37777/ | head -20
|
||||
|
||||
# Test stats endpoint
|
||||
curl -s http://127.0.0.1:37777/api/stats
|
||||
```
|
||||
|
||||
**Expected:**
|
||||
- `/` returns HTML page with React viewer
|
||||
- `/api/stats` returns JSON with database counts
|
||||
|
||||
### 7. Check Port Configuration
|
||||
|
||||
Verify port settings and availability:
|
||||
|
||||
```bash
|
||||
# Check if custom port is configured
|
||||
cat ~/.claude-mem/settings.json 2>/dev/null
|
||||
cat ~/.claude/settings.json 2>/dev/null
|
||||
|
||||
# Check what's listening on default port
|
||||
lsof -i :37777 2>&1 || netstat -tlnp 2>&1 | grep 37777
|
||||
|
||||
# Test connectivity
|
||||
nc -zv 127.0.0.1 37777 2>&1
|
||||
```
|
||||
|
||||
## Full System Diagnosis Script
|
||||
|
||||
Run this comprehensive diagnostic script to collect all information:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
echo "=== Claude-Mem Troubleshooting Report ==="
|
||||
echo ""
|
||||
echo "1. Environment"
|
||||
echo " OS: $(uname -s)"
|
||||
echo ""
|
||||
echo "2. Plugin Installation"
|
||||
echo " Plugin directory exists: $([ -d ~/.claude/plugins/marketplaces/thedotmack ] && echo 'YES' || echo 'NO')"
|
||||
echo " Package version: $(grep '"version"' ~/.claude/plugins/marketplaces/thedotmack/package.json 2>/dev/null | head -1)"
|
||||
echo ""
|
||||
echo "3. Database"
|
||||
echo " Database exists: $([ -f ~/.claude-mem/claude-mem.db ] && echo 'YES' || echo 'NO')"
|
||||
echo " Database size: $(du -h ~/.claude-mem/claude-mem.db 2>/dev/null | cut -f1)"
|
||||
echo " Observation count: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT COUNT(*) FROM observations;' 2>/dev/null || echo 'N/A')"
|
||||
echo " Session count: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT COUNT(*) FROM sessions;' 2>/dev/null || echo 'N/A')"
|
||||
echo ""
|
||||
echo "4. Worker Service"
|
||||
PM2_PATH=$(which pm2 2>/dev/null || echo "~/.claude/plugins/marketplaces/thedotmack/node_modules/.bin/pm2")
|
||||
echo " PM2 path: $PM2_PATH"
|
||||
WORKER_STATUS=$($PM2_PATH jlist 2>/dev/null | grep -o '"name":"claude-mem-worker".*"status":"[^"]*"' | grep -o 'status":"[^"]*"' | cut -d'"' -f3 || echo 'not running')
|
||||
echo " Worker status: $WORKER_STATUS"
|
||||
echo " Health check: $(curl -s http://127.0.0.1:37777/health 2>/dev/null || echo 'FAILED')"
|
||||
echo ""
|
||||
echo "5. Configuration"
|
||||
echo " Port setting: $(cat ~/.claude-mem/settings.json 2>/dev/null | grep CLAUDE_MEM_WORKER_PORT || echo 'default (37777)')"
|
||||
echo " Observation count: $(cat ~/.claude/settings.json 2>/dev/null | grep CLAUDE_MEM_CONTEXT_OBSERVATIONS || echo 'default (50)')"
|
||||
echo ""
|
||||
echo "6. Recent Activity"
|
||||
echo " Latest observation: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT created_at FROM observations ORDER BY created_at DESC LIMIT 1;' 2>/dev/null || echo 'N/A')"
|
||||
echo " Latest session: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT created_at FROM sessions ORDER BY created_at DESC LIMIT 1;' 2>/dev/null || echo 'N/A')"
|
||||
echo ""
|
||||
echo "=== End Report ==="
|
||||
```
|
||||
|
||||
Save this as `/tmp/claude-mem-diagnostics.sh` and run:
|
||||
```bash
|
||||
bash /tmp/claude-mem-diagnostics.sh
|
||||
```
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
If troubleshooting doesn't resolve the issue, collect this information for a bug report:
|
||||
|
||||
1. Full diagnostic report (run script above)
|
||||
2. Worker logs: `pm2 logs claude-mem-worker --lines 100 --nostream`
|
||||
3. Your setup:
|
||||
- Claude version: Check with Claude
|
||||
- OS: `uname -a`
|
||||
- Node version: `node --version`
|
||||
- Plugin version: In package.json
|
||||
4. Steps to reproduce the issue
|
||||
5. Expected vs actual behavior
|
||||
|
||||
Post to: https://github.com/thedotmack/claude-mem/issues
|
||||
@@ -0,0 +1,190 @@
|
||||
# Quick Commands Reference
|
||||
|
||||
Essential commands for troubleshooting claude-mem.
|
||||
|
||||
## Worker Management
|
||||
|
||||
```bash
|
||||
# Check worker status
|
||||
pm2 status | grep claude-mem-worker
|
||||
pm2 jlist | grep claude-mem-worker # JSON format
|
||||
|
||||
# Start worker
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
pm2 start ecosystem.config.cjs
|
||||
|
||||
# Restart worker
|
||||
pm2 restart claude-mem-worker
|
||||
|
||||
# Stop worker
|
||||
pm2 stop claude-mem-worker
|
||||
|
||||
# Delete worker (for clean restart)
|
||||
pm2 delete claude-mem-worker
|
||||
|
||||
# View logs
|
||||
pm2 logs claude-mem-worker
|
||||
|
||||
# View last N lines
|
||||
pm2 logs claude-mem-worker --lines 50 --nostream
|
||||
|
||||
# Clear logs
|
||||
pm2 flush claude-mem-worker
|
||||
```
|
||||
|
||||
## Health Checks
|
||||
|
||||
```bash
|
||||
# Check worker health (default port)
|
||||
curl -s http://127.0.0.1:37777/health
|
||||
|
||||
# Check viewer stats
|
||||
curl -s http://127.0.0.1:37777/api/stats
|
||||
|
||||
# Open viewer in browser
|
||||
open http://127.0.0.1:37777
|
||||
|
||||
# Test custom port
|
||||
PORT=37778
|
||||
curl -s http://127.0.0.1:$PORT/health
|
||||
```
|
||||
|
||||
## Database Queries
|
||||
|
||||
```bash
|
||||
# Observation count
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations;"
|
||||
|
||||
# Session count
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM sessions;"
|
||||
|
||||
# Recent observations
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT created_at, type, title FROM observations ORDER BY created_at DESC LIMIT 10;"
|
||||
|
||||
# Recent sessions
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT created_at, request FROM sessions ORDER BY created_at DESC LIMIT 5;"
|
||||
|
||||
# Database size
|
||||
du -h ~/.claude-mem/claude-mem.db
|
||||
|
||||
# Database integrity check
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;"
|
||||
|
||||
# Projects in database
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "SELECT DISTINCT project FROM observations ORDER BY project;"
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# View current settings
|
||||
cat ~/.claude-mem/settings.json
|
||||
cat ~/.claude/settings.json
|
||||
|
||||
# Change worker port
|
||||
echo '{"env":{"CLAUDE_MEM_WORKER_PORT":"37778"}}' > ~/.claude-mem/settings.json
|
||||
|
||||
# Change context observation count
|
||||
# Edit ~/.claude/settings.json and add:
|
||||
{
|
||||
"env": {
|
||||
"CLAUDE_MEM_CONTEXT_OBSERVATIONS": "25"
|
||||
}
|
||||
}
|
||||
|
||||
# Change AI model
|
||||
{
|
||||
"env": {
|
||||
"CLAUDE_MEM_MODEL": "claude-haiku-4-5"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Plugin Management
|
||||
|
||||
```bash
|
||||
# Navigate to plugin directory
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
|
||||
# Check plugin version
|
||||
grep '"version"' package.json
|
||||
|
||||
# Reinstall dependencies
|
||||
npm install
|
||||
|
||||
# View package.json
|
||||
cat package.json
|
||||
```
|
||||
|
||||
## Port Diagnostics
|
||||
|
||||
```bash
|
||||
# Check what's using port 37777
|
||||
lsof -i :37777
|
||||
netstat -tlnp | grep 37777
|
||||
|
||||
# Test port connectivity
|
||||
nc -zv 127.0.0.1 37777
|
||||
curl -v http://127.0.0.1:37777/health
|
||||
```
|
||||
|
||||
## Log Analysis
|
||||
|
||||
```bash
|
||||
# Search logs for errors
|
||||
pm2 logs claude-mem-worker --lines 100 --nostream | grep -i "error"
|
||||
|
||||
# Search for specific keyword
|
||||
pm2 logs claude-mem-worker --lines 100 --nostream | grep "keyword"
|
||||
|
||||
# Follow logs in real-time
|
||||
pm2 logs claude-mem-worker
|
||||
|
||||
# Show only error logs
|
||||
pm2 logs claude-mem-worker --err
|
||||
```
|
||||
|
||||
## File Locations
|
||||
|
||||
```bash
|
||||
# Plugin directory
|
||||
~/.claude/plugins/marketplaces/thedotmack/
|
||||
|
||||
# Database
|
||||
~/.claude-mem/claude-mem.db
|
||||
|
||||
# Settings
|
||||
~/.claude-mem/settings.json
|
||||
~/.claude/settings.json
|
||||
|
||||
# Chroma vector database
|
||||
~/.claude-mem/chroma/
|
||||
|
||||
# Usage logs
|
||||
~/.claude-mem/usage-logs/
|
||||
|
||||
# PM2 logs
|
||||
~/.pm2/logs/
|
||||
```
|
||||
|
||||
## System Information
|
||||
|
||||
```bash
|
||||
# OS version
|
||||
uname -a
|
||||
|
||||
# Node version
|
||||
node --version
|
||||
|
||||
# NPM version
|
||||
npm --version
|
||||
|
||||
# PM2 version
|
||||
pm2 --version
|
||||
|
||||
# SQLite version
|
||||
sqlite3 --version
|
||||
|
||||
# Check disk space
|
||||
df -h ~/.claude-mem/
|
||||
```
|
||||
@@ -0,0 +1,308 @@
|
||||
# Worker Service Diagnostics
|
||||
|
||||
PM2 worker-specific troubleshooting for claude-mem.
|
||||
|
||||
## PM2 Worker Overview
|
||||
|
||||
The claude-mem worker is a persistent background service managed by PM2. It:
|
||||
- Runs Express.js server on port 37777 (default)
|
||||
- Processes observations asynchronously
|
||||
- Serves the viewer UI
|
||||
- Provides search API endpoints
|
||||
|
||||
## Check Worker Status
|
||||
|
||||
### Basic Status Check
|
||||
|
||||
```bash
|
||||
# List all PM2 processes
|
||||
pm2 list
|
||||
|
||||
# JSON format (parseable)
|
||||
pm2 jlist
|
||||
|
||||
# Filter for claude-mem-worker
|
||||
pm2 status | grep claude-mem-worker
|
||||
```
|
||||
|
||||
**Expected output:**
|
||||
```
|
||||
│ claude-mem-worker │ online │ 12345 │ 0 │ 45m │ 0% │ 85.6mb │
|
||||
```
|
||||
|
||||
**Status meanings:**
|
||||
- `online` - Worker running correctly
|
||||
- `stopped` - Worker stopped (normal shutdown)
|
||||
- `errored` - Worker crashed (check logs)
|
||||
- `stopping` - Worker shutting down
|
||||
- Not listed - Worker never started
|
||||
|
||||
### Detailed Worker Info
|
||||
|
||||
```bash
|
||||
# Show detailed information
|
||||
pm2 show claude-mem-worker
|
||||
|
||||
# JSON format
|
||||
pm2 jlist | grep -A 20 '"name":"claude-mem-worker"'
|
||||
```
|
||||
|
||||
## Worker Health Endpoint
|
||||
|
||||
The worker exposes a health endpoint at `/health`:
|
||||
|
||||
```bash
|
||||
# Check health (default port)
|
||||
curl -s http://127.0.0.1:37777/health
|
||||
|
||||
# With custom port
|
||||
PORT=$(grep CLAUDE_MEM_WORKER_PORT ~/.claude-mem/settings.json | grep -o '[0-9]\+' || echo "37777")
|
||||
curl -s http://127.0.0.1:$PORT/health
|
||||
```
|
||||
|
||||
**Expected response:** `{"status":"ok"}`
|
||||
|
||||
**Error responses:**
|
||||
- Connection refused - Worker not running
|
||||
- Timeout - Worker hung (restart needed)
|
||||
- Empty response - Worker crashed mid-request
|
||||
|
||||
## Worker Logs
|
||||
|
||||
### View Recent Logs
|
||||
|
||||
```bash
|
||||
# Last 50 lines
|
||||
pm2 logs claude-mem-worker --lines 50 --nostream
|
||||
|
||||
# Last 200 lines
|
||||
pm2 logs claude-mem-worker --lines 200 --nostream
|
||||
|
||||
# Follow logs in real-time
|
||||
pm2 logs claude-mem-worker
|
||||
```
|
||||
|
||||
### Search Logs for Errors
|
||||
|
||||
```bash
|
||||
# Find errors
|
||||
pm2 logs claude-mem-worker --lines 500 --nostream | grep -i "error"
|
||||
|
||||
# Find exceptions
|
||||
pm2 logs claude-mem-worker --lines 500 --nostream | grep -i "exception"
|
||||
|
||||
# Find failed requests
|
||||
pm2 logs claude-mem-worker --lines 500 --nostream | grep -i "failed"
|
||||
|
||||
# All error patterns
|
||||
pm2 logs claude-mem-worker --lines 500 --nostream | grep -iE "error|exception|failed|crash"
|
||||
```
|
||||
|
||||
### Common Log Patterns
|
||||
|
||||
**Good startup:**
|
||||
```
|
||||
Worker service started on port 37777
|
||||
Database initialized
|
||||
Express server listening
|
||||
```
|
||||
|
||||
**Database errors:**
|
||||
```
|
||||
Error: SQLITE_ERROR
|
||||
Error initializing database
|
||||
Database locked
|
||||
```
|
||||
|
||||
**Port conflicts:**
|
||||
```
|
||||
Error: listen EADDRINUSE
|
||||
Port 37777 already in use
|
||||
```
|
||||
|
||||
**Crashes:**
|
||||
```
|
||||
PM2 | App [claude-mem-worker] exited with code [1]
|
||||
PM2 | App [claude-mem-worker] will restart in 100ms
|
||||
```
|
||||
|
||||
## Starting the Worker
|
||||
|
||||
### Basic Start
|
||||
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
pm2 start ecosystem.config.cjs
|
||||
```
|
||||
|
||||
### Start with Local PM2
|
||||
|
||||
If `pm2` command not in PATH:
|
||||
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
node_modules/.bin/pm2 start ecosystem.config.cjs
|
||||
```
|
||||
|
||||
### Force Restart
|
||||
|
||||
```bash
|
||||
# Restart if already running
|
||||
pm2 restart claude-mem-worker
|
||||
|
||||
# Delete and start fresh
|
||||
pm2 delete claude-mem-worker
|
||||
pm2 start ecosystem.config.cjs
|
||||
```
|
||||
|
||||
## Stopping the Worker
|
||||
|
||||
```bash
|
||||
# Graceful stop
|
||||
pm2 stop claude-mem-worker
|
||||
|
||||
# Delete completely (also removes from PM2 list)
|
||||
pm2 delete claude-mem-worker
|
||||
```
|
||||
|
||||
## Worker Not Starting
|
||||
|
||||
### Diagnostic Steps
|
||||
|
||||
1. **Try manual start to see error:**
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
node plugin/scripts/worker-service.cjs
|
||||
```
|
||||
This runs the worker directly without PM2, showing full error output.
|
||||
|
||||
2. **Check PM2 itself:**
|
||||
```bash
|
||||
which pm2
|
||||
pm2 --version
|
||||
```
|
||||
If PM2 not found, dependencies not installed.
|
||||
|
||||
3. **Check dependencies:**
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
ls node_modules/@anthropic-ai/claude-agent-sdk
|
||||
ls node_modules/better-sqlite3
|
||||
ls node_modules/express
|
||||
ls node_modules/pm2
|
||||
```
|
||||
|
||||
4. **Check port availability:**
|
||||
```bash
|
||||
lsof -i :37777
|
||||
```
|
||||
If port in use, either kill that process or change claude-mem port.
|
||||
|
||||
### Common Fixes
|
||||
|
||||
**Dependencies missing:**
|
||||
```bash
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
npm install
|
||||
pm2 start ecosystem.config.cjs
|
||||
```
|
||||
|
||||
**Port conflict:**
|
||||
```bash
|
||||
echo '{"env":{"CLAUDE_MEM_WORKER_PORT":"37778"}}' > ~/.claude-mem/settings.json
|
||||
pm2 restart claude-mem-worker
|
||||
```
|
||||
|
||||
**Corrupted PM2:**
|
||||
```bash
|
||||
pm2 kill # Stop PM2 daemon
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack/
|
||||
pm2 start ecosystem.config.cjs
|
||||
```
|
||||
|
||||
## Worker Crashing Repeatedly
|
||||
|
||||
If worker keeps restarting (check with `pm2 status` showing high restart count):
|
||||
|
||||
### Find the Cause
|
||||
|
||||
1. **Check error logs:**
|
||||
```bash
|
||||
pm2 logs claude-mem-worker --err --lines 100 --nostream
|
||||
```
|
||||
|
||||
2. **Look for crash pattern:**
|
||||
```bash
|
||||
pm2 logs claude-mem-worker --lines 200 --nostream | grep -A 5 "exited with code"
|
||||
```
|
||||
|
||||
### Common Crash Causes
|
||||
|
||||
**Database corruption:**
|
||||
```bash
|
||||
sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;"
|
||||
```
|
||||
If fails, backup and recreate database.
|
||||
|
||||
**Out of memory:**
|
||||
Check if database is too large or memory leak. Restart:
|
||||
```bash
|
||||
pm2 restart claude-mem-worker
|
||||
```
|
||||
|
||||
**Port conflict race condition:**
|
||||
Another process grabbing port intermittently. Change port:
|
||||
```bash
|
||||
echo '{"env":{"CLAUDE_MEM_WORKER_PORT":"37778"}}' > ~/.claude-mem/settings.json
|
||||
pm2 restart claude-mem-worker
|
||||
```
|
||||
|
||||
## PM2 Management Commands
|
||||
|
||||
```bash
|
||||
# List processes
|
||||
pm2 list
|
||||
pm2 jlist # JSON format
|
||||
|
||||
# Show detailed info
|
||||
pm2 show claude-mem-worker
|
||||
|
||||
# Monitor resources
|
||||
pm2 monit
|
||||
|
||||
# Clear logs
|
||||
pm2 flush claude-mem-worker
|
||||
|
||||
# Restart PM2 daemon
|
||||
pm2 kill
|
||||
pm2 resurrect # Restore saved processes
|
||||
|
||||
# Save current process list
|
||||
pm2 save
|
||||
|
||||
# Update PM2
|
||||
npm install -g pm2
|
||||
```
|
||||
|
||||
## Testing Worker Endpoints
|
||||
|
||||
Once worker is running, test all endpoints:
|
||||
|
||||
```bash
|
||||
# Health check
|
||||
curl -s http://127.0.0.1:37777/health
|
||||
|
||||
# Viewer HTML
|
||||
curl -s http://127.0.0.1:37777/ | head -20
|
||||
|
||||
# Stats API
|
||||
curl -s http://127.0.0.1:37777/api/stats
|
||||
|
||||
# Search API
|
||||
curl -s "http://127.0.0.1:37777/api/search/observations?q=test&format=index"
|
||||
|
||||
# Prompts API
|
||||
curl -s "http://127.0.0.1:37777/api/prompts?limit=5"
|
||||
```
|
||||
|
||||
All should return appropriate responses (HTML for viewer, JSON for APIs).
|
||||
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 42 KiB |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated by Pixelmator Pro 3.6.17 -->
|
||||
<svg width="328" height="327" viewBox="0 0 328 327" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon-thick-completed">
|
||||
<path id="Path" fill="#ee9443" stroke="none" d="M 102.143021 15.109985 C 124.399521 6.597839 146.838608 0.814148 170.500641 1.142517 C 178.716675 1.256592 184.928009 4.778809 188.221222 12.564148 C 192.131836 21.809204 187.675385 32.968201 178.308319 36.536194 C 174.499176 37.987122 170.210297 39.14447 166.207672 38.960144 C 145.084 37.987 126.251495 45.816895 108.085022 54.783447 C 101.013763 58.273621 94.376038 64.903625 90.303864 71.747437 C 81.249969 86.963745 68.683197 98.688721 56.224823 110.687927 C 45.194839 121.311401 37.752762 133.868042 36.270981 149.298157 C 35.333755 159.057739 38.918953 168.018066 43.269821 176.487732 C 48.642563 186.946594 49.607574 198.020325 49.348831 209.462341 C 49.229034 214.759338 49.297974 220.136719 50.076202 225.359192 C 51.13385 232.456848 55.144928 237.91803 61.378815 241.596008 C 63.383698 242.778992 65.430359 243.940247 67.574722 244.829956 C 83.763855 251.546753 97.238754 261.650269 108.032684 275.601196 C 114.748367 284.281067 123.325409 290.831177 135.135101 289.901917 C 140.699997 289.464111 146.271561 287.914001 151.622803 286.192444 C 165.21907 281.818298 178.989075 280.611023 193.001343 283.258057 C 210.095184 286.487305 222.947113 280.610168 232.088593 265.940796 C 239.656372 253.796753 250.168091 244.837585 262.412537 237.534668 C 267.629059 234.42334 272.271271 230.168091 276.676208 225.916443 C 284.850586 218.026672 283.26178 207.977478 281.815948 198.196411 C 280.216736 187.377869 282.084747 177.171692 285.330505 166.805542 C 287.769135 159.01709 289.746948 150.571228 289.354858 142.543823 C 288.898254 133.196045 281.958679 126.555603 274.813629 120.722412 C 262.907257 111.002075 253.400421 99.691772 251.609131 83.625122 C 251.074951 78.833923 250.541107 74.030701 250.329742 69.21875 C 250.08316 63.604675 247.759796 59.19043 243.079437 56.287903 C 238.852234 53.666321 234.389679 51.398193 229.921539 49.196228 C 216.848724 42.753784 213.950043 27.349976 224.305084 18.449036 C 229.938202 13.606995 236.862122 13.132141 243.201904 15.625 C 262.293976 23.132446 277.067566 35.448792 283.134338 55.910461 C 284.725006 61.275391 285.178131 67.024841 285.728149 72.639404 C 286.461243 80.122681 289.553528 86.273743 294.768616 91.602539 C 301.394287 98.372742 308.7612 104.675049 314.152252 112.343567 C 326.11319 129.357178 329.543671 148.192627 322.225342 168.220764 C 318.505066 178.402039 316.802551 188.827332 317.877289 199.604309 C 320.515808 226.062927 309.154083 246.001892 289.051666 261.865967 C 283.568481 266.193054 277.323334 269.67511 272.349426 274.495911 C 266.423248 280.239441 261.273407 286.837769 256.165253 293.361694 C 240.566223 313.284546 220.852509 323.559692 195.203339 318.922852 C 181.849915 316.508789 169.312592 318.014526 156.549927 322.340332 C 128.791275 331.749023 105.282944 322.742676 84.499405 304.061523 C 77.946136 298.171021 72.037766 291.539246 65.29071 285.896729 C 61.008255 282.315308 55.838928 279.71698 50.867279 277.050842 C 31.686432 266.764404 18.30217 251.817322 13.808723 230.139771 C 12.550957 224.072021 12.645752 217.592896 13.001595 211.352783 C 13.513565 202.374756 12.749832 193.795959 9.017151 185.506409 C 4.141502 174.678528 0.361565 163.461487 0.147591 151.46405 C -0.268478 128.135742 8.54351 108.351929 24.183197 91.418823 C 28.588943 86.648743 33.638489 82.456482 37.878265 77.55426 C 43.191925 71.4104 48.86821 65.311707 52.818863 58.30835 C 64.201996 38.129456 80.781326 24.276245 102.143021 15.109985 Z"/>
|
||||
<path id="path1" fill="#ee9443" stroke="none" d="M 177.816132 152.815186 C 191.282013 135.84552 204.424072 119.072571 217.830627 102.513794 C 222.283875 97.013489 228.385834 94.391418 235.566132 95.916077 C 243.398834 97.579224 248.658691 102.725464 249.203705 110.510254 C 249.534729 115.238892 248.017639 120.987549 245.313232 124.874084 C 236.577667 137.427856 227.077362 149.466003 217.560394 161.455688 C 201.420685 181.788879 185.291229 202.139771 168.680969 222.086487 C 157.115097 235.975647 140.902039 236.160095 129.093536 222.542542 C 123.268402 215.825012 118.55864 208.046204 113.969971 200.373413 C 107.730713 189.940674 101.93927 179.349426 93.154266 170.614319 C 84.420303 161.930115 82.955887 151.667053 88.21373 143.463806 C 94.485153 133.679199 108.943008 130.570801 117.990814 138.286194 C 123.506119 142.989258 127.616531 149.584167 131.544327 155.840088 C 136.043503 163.006226 139.188934 171.028992 143.759659 178.141602 C 148.237061 185.108887 151.258911 185.276123 156.6017 179.125671 C 163.907013 170.715942 170.625488 161.796448 177.816132 152.815186 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated by Pixelmator Pro 3.6.17 -->
|
||||
<svg width="295" height="339" viewBox="0 0 295 339" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon-thick-investigated">
|
||||
<path id="Path" fill="#ee9443" stroke="none" d="M 229.062531 225.062622 C 215.534576 231.309692 201.815216 234.810913 187.17041 233.883606 C 178.072418 233.307495 169.25061 234.487427 160.747803 237.888306 C 145.74324 243.889832 130.520584 242.407349 115.103485 239.659485 C 109.520187 238.664368 103.463379 238.559448 97.913162 239.599182 C 91.193512 240.858032 89.857742 246.409546 94.159653 251.608093 C 108.427109 268.849121 103.655029 289.726685 82.846451 298.619263 C 75.620636 301.707153 67.625809 303.104218 59.888016 304.85791 C 50.282639 307.034882 42.764755 311.295197 38.593872 320.941223 C 36.690849 325.342377 33.301422 329.377228 29.783783 332.738495 C 24.606377 337.685913 18.155121 338.136719 11.659698 335.23761 C 5.209885 332.358826 1.559677 327.350647 0.947311 320.210785 C 0.345543 313.194427 1.801857 306.664551 5.316139 300.567902 C 15.429329 283.023499 30.555946 273.203247 50.673309 270.968628 C 53.301224 270.676636 55.950958 270.209106 58.475998 269.444458 C 62.971725 268.08313 63.602158 266.009277 60.836716 262.152527 C 58.420456 258.782776 55.391068 255.764587 53.461945 252.14856 C 44.128372 234.653442 57.2845 217.667847 72.544739 212.954956 C 79.150009 210.915039 86.189026 210.282532 93.028107 208.995117 C 95.427124 208.543579 97.817413 208.045532 101.645859 207.283081 C 99.327225 204.565735 97.793701 202.596069 96.082932 200.794678 C 90.028107 194.41925 83.471466 188.463745 77.908737 181.689087 C 64.844833 165.779236 59.591553 146.643127 58.248764 126.666382 C 56.229111 96.620117 63.289093 68.849243 82.57254 44.979126 C 101.736938 21.256409 126.399551 6.920532 156.699341 3.060181 C 182.104431 -0.176514 207.001221 1.754333 230.040924 14.175964 C 263.505798 32.21814 284.351563 60.22937 290.406555 97.555054 C 294.291138 121.501221 294.880157 146.098694 284.244659 169.16748 C 272.668884 194.275696 254.271667 212.81134 229.062531 225.062622 M 244.872559 167.361755 C 246.057373 165.964722 247.295471 164.60907 248.418304 163.163879 C 255.00592 154.684448 259.361908 145.55304 258.956238 134.328857 C 258.616425 124.927002 259.09082 115.463867 259.744476 106.06543 C 260.732086 91.865112 257.144501 79.826782 245.442902 70.771606 C 239.933441 66.508179 235.032104 61.463989 229.57312 57.128784 C 223.865021 52.595825 218.260376 47.610107 211.829315 44.375488 C 197.484528 37.160461 182.032745 35.63031 166.134003 37.822998 C 133.72908 42.292175 111.445908 59.614502 99.478043 90.057617 C 88.486908 118.016174 92.658325 152.151917 119.026199 172.237122 C 128.423401 179.395264 135.325684 188.393066 138.320084 199.993469 C 139.583832 204.889343 142.883499 205.848633 147.055222 204.817383 C 150.593048 203.942871 153.99028 202.495239 157.443268 201.282898 C 166.797211 197.998596 176.204132 196.039429 186.293915 197.625366 C 200.123383 199.799133 212.669403 197.077393 223.127808 186.812866 C 229.893097 180.172974 237.269012 174.155273 244.872559 167.361755 Z"/>
|
||||
<path id="path1" fill="#ee9544" stroke="none" d="M 194.897339 64.336914 C 206.583221 62.468262 215.635437 67.409302 224.302734 73.749695 C 238.771729 84.334229 245.725098 99.028931 246.693542 116.427734 C 247.490143 130.739807 244.072723 144.22644 234.096741 155.157959 C 231.991882 157.464417 228.721863 159.032837 225.692261 160.098389 C 220.342163 161.980103 215.459534 160.512878 211.675232 156.238647 C 208.011444 152.100525 207.832611 147.407654 209.515137 142.262695 C 211.309143 136.776733 213.460236 131.214111 213.99173 125.547668 C 215.227661 112.370483 209.723846 103.750244 197.700653 98.094055 C 196.345306 97.456421 195.014587 96.766052 193.65506 96.137695 C 186.781982 92.961365 182.817017 87.529785 183.136108 80.035217 C 183.449127 72.68335 187.378235 67.224609 194.897339 64.336914 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated by Pixelmator Pro 3.6.17 -->
|
||||
<svg width="353" height="364" viewBox="0 0 353 364" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon-thick-learned">
|
||||
<path id="Path" fill="#ee9443" stroke="none" d="M 271.450684 172.584045 C 273.343445 194.098389 270.338623 213.959534 256.262817 230.760376 C 252.104553 235.723694 247.645813 240.591919 242.633789 244.650269 C 232.838013 252.582214 229.652893 262.937012 229.050354 274.968018 C 228.661987 282.721924 226.516724 290.377747 225.94397 298.135498 C 225.449341 304.836121 225.212036 311.767395 226.361206 318.334229 C 228.871155 332.677612 225.07959 343.24469 212.557495 350.703888 C 207.639648 353.633392 202.04071 356.192383 196.458435 357.085266 C 181.075195 359.545837 165.563293 361.335999 150.04187 362.707977 C 145.997742 363.06546 141.407715 361.596954 137.642151 359.778809 C 131.365173 356.747955 128.71936 350.93808 129.12146 344.070404 C 129.577759 336.276611 133.243103 330.086884 140.756226 327.561218 C 146.495178 325.631989 152.669922 324.627563 158.727783 324.105865 C 167.84021 323.321045 177.024475 323.407349 186.169434 322.949463 C 191.91217 322.661987 195.344666 320.123352 194.87439 316.190308 C 194.651306 314.324951 192.947021 311.528442 191.41864 311.149231 C 187.805725 310.252808 183.879272 310.225464 180.109924 310.423889 C 164.817383 311.229126 149.53717 312.139893 134.361572 309.125916 C 126.080383 307.481201 121.892212 303.087891 122.043091 294.73584 C 122.186279 286.804382 123.350037 278.892029 124.047974 270.969788 C 125.085876 259.190063 119.776184 250.632141 110.74762 243.601074 C 87.049316 225.145813 79.709412 200.221619 82.73822 171.335754 C 84.429932 155.201782 90.26416 140.769165 100.286255 127.994385 C 116.374084 107.487671 136.94043 94.488403 162.88501 90.460144 C 210.062683 83.135254 254.294556 110.859192 268.11322 156.55188 C 269.643311 161.611084 270.288879 166.937805 271.450684 172.584045 M 229.007874 152.54834 C 226.673096 149.528137 224.618286 146.232605 221.960144 143.530518 C 196.655273 117.807373 141.948364 118.612122 121.780273 161.978516 C 113.200989 180.426147 114.518372 198.947449 131.426208 214.280396 C 135.984802 218.414429 140.783691 222.509155 144.377563 227.422607 C 148.632446 233.239502 152.880615 239.557251 154.981812 246.325378 C 157.495972 254.423828 157.874573 263.211731 158.882996 271.735962 C 159.210083 274.501221 159.49585 276.862671 162.831787 276.897949 C 170.791077 276.981995 178.7771 277.295837 186.704102 276.778931 C 192.626343 276.3927 193.906555 274.412476 194.184692 268.340576 C 195.02771 249.935364 201.677063 234.233765 215.025269 221.240723 C 221.435242 215.001343 228.310364 208.625122 232.63623 200.98645 C 241.793823 184.815918 238.45636 168.581177 229.007874 152.54834 Z"/>
|
||||
<path id="path1" fill="#ee9544" stroke="none" d="M 158.952759 45.972473 C 158.945435 36.99884 158.837341 28.518555 158.958923 20.041565 C 159.11438 9.20166 166.629822 1.291138 176.675232 1.163452 C 187.241089 1.029175 194.853088 8.555664 195.05896 19.830017 C 195.268127 31.288025 195.33667 42.761047 194.987427 54.212891 C 194.707092 63.401917 187.224182 69.711426 177.172668 69.998657 C 167.616699 70.271729 160.188049 64.1026 159.223083 54.933838 C 158.928345 52.13324 159.032532 49.290649 158.952759 45.972473 Z"/>
|
||||
<path id="path2" fill="#ee9545" stroke="none" d="M 240.151184 71.347107 C 246.59021 62.467346 252.821167 53.886292 259.300049 45.496765 C 262.225586 41.708618 265.23053 37.781555 268.930237 34.835205 C 275.682922 29.457458 285.254028 30.138916 290.921997 35.658813 C 297.144104 41.718323 298.258545 50.611267 292.924316 57.851807 C 285.630554 67.752258 278.067322 77.481018 270.14032 86.879028 C 264.368225 93.722168 255.518311 94.755554 247.930908 90.422729 C 241.237549 86.600525 238.141602 79.260254 240.151184 71.347107 Z"/>
|
||||
<path id="path3" fill="#ee9545" stroke="none" d="M 94.416748 92.876099 C 90.227112 90.332947 85.729736 88.465088 82.824646 85.186401 C 75.340088 76.739258 68.420776 67.77478 61.542114 58.812439 C 55.871338 51.42395 56.928345 40.84552 63.497803 35.31958 C 70.645935 29.306885 81.690857 29.472839 87.88501 36.621399 C 96.041443 46.034546 103.694641 55.912842 111.109131 65.92804 C 115.180664 71.427734 115.128906 77.757874 111.843018 83.856567 C 108.197205 90.623291 102.277527 93.223328 94.416748 92.876099 Z"/>
|
||||
<path id="path4" fill="#ee9545" stroke="none" d="M 327.237671 111.158447 C 339.058167 106.002991 350.032959 112.776855 351.524292 123.386963 C 352.453918 130.000122 348.8396 138.183655 342.590576 140.844788 C 331.583923 145.53186 320.502625 150.078064 309.278198 154.209351 C 298.968567 158.004028 290.227539 154.146057 286.725098 144.919678 C 283.136841 135.467163 287.600891 126.236633 297.930237 122.201233 C 307.530823 118.450562 317.217163 114.919128 327.237671 111.158447 Z"/>
|
||||
<path id="path5" fill="#ee9545" stroke="none" d="M 60.526978 150.6521 C 54.4599 155.095764 48.345764 154.328491 42.21698 151.939087 C 32.62677 148.200134 23.100098 144.298462 13.517639 140.539246 C 3.66449 136.673767 -0.680115 127.512268 2.923706 118.00708 C 6.397583 108.844604 15.713013 104.776245 25.429688 108.514954 C 36.124695 112.630127 46.721008 117.005798 57.320618 121.364258 C 68.805969 126.086914 70.452515 143.709595 60.526978 150.6521 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.2 KiB |