openlynx 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openlynx-0.2.0/.gitignore +22 -0
- openlynx-0.2.0/LICENSE +21 -0
- openlynx-0.2.0/PKG-INFO +277 -0
- openlynx-0.2.0/README.md +252 -0
- openlynx-0.2.0/README.zh-CN.md +230 -0
- openlynx-0.2.0/pyproject.toml +64 -0
- openlynx-0.2.0/src/lynx_memory/__init__.py +1 -0
- openlynx-0.2.0/src/lynx_memory/assets/__init__.py +0 -0
- openlynx-0.2.0/src/lynx_memory/assets/commands/lynx-memory-delete.md +56 -0
- openlynx-0.2.0/src/lynx_memory/assets/commands/lynx-memory-history.md +31 -0
- openlynx-0.2.0/src/lynx_memory/assets/commands/lynx-memory-pull-global.md +40 -0
- openlynx-0.2.0/src/lynx_memory/assets/commands/lynx-memory-push-global.md +40 -0
- openlynx-0.2.0/src/lynx_memory/assets/commands/lynx-memory-status.md +20 -0
- openlynx-0.2.0/src/lynx_memory/assets/web/assets/index-B0j42wWr.js +68 -0
- openlynx-0.2.0/src/lynx_memory/assets/web/assets/index-ChnC4PlG.css +1 -0
- openlynx-0.2.0/src/lynx_memory/assets/web/index.html +13 -0
- openlynx-0.2.0/src/lynx_memory/cli.py +935 -0
- openlynx-0.2.0/src/lynx_memory/config.py +108 -0
- openlynx-0.2.0/src/lynx_memory/embeddings.py +31 -0
- openlynx-0.2.0/src/lynx_memory/hooks/__init__.py +0 -0
- openlynx-0.2.0/src/lynx_memory/hooks/_log.py +11 -0
- openlynx-0.2.0/src/lynx_memory/hooks/on_prompt.py +135 -0
- openlynx-0.2.0/src/lynx_memory/hooks/on_session_end.py +164 -0
- openlynx-0.2.0/src/lynx_memory/hooks/on_stop.py +35 -0
- openlynx-0.2.0/src/lynx_memory/server.py +108 -0
- openlynx-0.2.0/src/lynx_memory/storage.py +660 -0
- openlynx-0.2.0/src/lynx_memory/summarizer.py +209 -0
- openlynx-0.2.0/src/lynx_memory/transcript.py +281 -0
- openlynx-0.2.0/src/lynx_memory/web.py +258 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.eggs/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
.env
|
|
11
|
+
db/
|
|
12
|
+
.DS_Store
|
|
13
|
+
.idea/
|
|
14
|
+
.vscode/
|
|
15
|
+
web/node_modules/
|
|
16
|
+
web/dist/
|
|
17
|
+
src/lynx_memory/assets/web/
|
|
18
|
+
.playwright-mcp/
|
|
19
|
+
lynx-memory-webui*.png
|
|
20
|
+
.lynx-memory/
|
|
21
|
+
.claude/
|
|
22
|
+
CLAUDE.md
|
openlynx-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 lynx-memory contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
openlynx-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openlynx
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: OpenLynx — persistent semantic memory for Claude Code, Codex CLI and other coding agents. Auto-saves and recalls conversation history across sessions.
|
|
5
|
+
Project-URL: Homepage, https://github.com/by123/lynx-memory
|
|
6
|
+
Project-URL: Issues, https://github.com/by123/lynx-memory/issues
|
|
7
|
+
Author: OpenLynx contributors
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: claude,claude-code,codex,embeddings,lynx-memory,mcp,memory,openlynx,rag
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: anthropic>=0.40.0
|
|
18
|
+
Requires-Dist: chromadb>=0.5.0
|
|
19
|
+
Requires-Dist: fastapi>=0.110.0
|
|
20
|
+
Requires-Dist: mcp>=1.0.0
|
|
21
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
22
|
+
Requires-Dist: uvicorn>=0.27.0
|
|
23
|
+
Requires-Dist: voyageai>=0.3.0
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# lynx-memory
|
|
27
|
+
|
|
28
|
+
[中文 README](./README.zh-CN.md)
|
|
29
|
+
|
|
30
|
+
Persistent, semantic, long-term memory for [Claude Code](https://claude.com/claude-code).
|
|
31
|
+
Conversations are auto-saved across sessions and the most relevant snippets are
|
|
32
|
+
injected into context whenever you start a new prompt — no special syntax,
|
|
33
|
+
no "remember this" phrasing required.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
You : What can I do tomorrow if the weather's nice — maybe walk the dog?
|
|
37
|
+
Claude : Since you've got Dandan (your golden Border Collie) who needs a lot
|
|
38
|
+
of exercise, try a long walk, frisbee, or a bike ride with him
|
|
39
|
+
tagging along… 🐶
|
|
40
|
+
(you never mentioned Dandan or owning a dog — memory recalled it
|
|
41
|
+
from a past chat)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## How it works
|
|
45
|
+
|
|
46
|
+
Three Claude Code [hooks](https://docs.claude.com/en/docs/claude-code/hooks) +
|
|
47
|
+
a small Python service:
|
|
48
|
+
|
|
49
|
+
| Hook | What it does |
|
|
50
|
+
| ----------------- | ------------------------------------------------------------------------- |
|
|
51
|
+
| `UserPromptSubmit` | Embeds your prompt and injects the top-K most similar prior turns. When a turn has a Haiku-generated summary, the **summary** is injected instead of the raw prose. |
|
|
52
|
+
| `Stop` | Persists the current user/assistant turn into SQLite + Chroma, then spawns a detached background process that asks Haiku to summarize the turn (no extra API key needed — reuses your `claude` CLI session). |
|
|
53
|
+
| `SessionEnd` | Asks Claude Haiku to produce a coarse summary of the whole session. |
|
|
54
|
+
|
|
55
|
+
Storage:
|
|
56
|
+
|
|
57
|
+
- **SQLite** — source of truth for raw turns, per-turn Haiku summaries, and session summaries
|
|
58
|
+
- **Chroma** — local vector index over turns + summaries
|
|
59
|
+
- **Voyage AI** (`voyage-3`) — embeddings
|
|
60
|
+
- **Claude Haiku** (`claude-haiku-4-5-20251001`) — per-turn summarization, called via `claude -p` so no extra `ANTHROPIC_API_KEY` is required
|
|
61
|
+
|
|
62
|
+
## Install
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install lynx-memory
|
|
66
|
+
lynx-memory init
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`init` will:
|
|
70
|
+
|
|
71
|
+
1. Create `~/.claude/lynx-memory/` (data directory)
|
|
72
|
+
2. Prompt for your `VOYAGE_API_KEY` (get one free at https://www.voyageai.com/)
|
|
73
|
+
3. Write the default `.env` (`MIN_SCORE=0.7`, `SUMMARY_ENABLED=1`,
|
|
74
|
+
`SUMMARY_MODEL=claude-haiku-4-5-20251001`, `SUMMARY_BACKEND=auto`) — the
|
|
75
|
+
per-turn Haiku summarizer reuses your existing `claude` CLI session, so
|
|
76
|
+
no extra `ANTHROPIC_API_KEY` is required by default
|
|
77
|
+
4. Back up your existing `~/.claude/settings.json` and add the three hooks
|
|
78
|
+
5. Print verification steps
|
|
79
|
+
|
|
80
|
+
Then open a fresh Claude Code session, chat for a few turns, and run:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
lynx-memory status
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
You should see `turns` and `chroma_turns` counters going up.
|
|
87
|
+
|
|
88
|
+
## Codex CLI (cross-host memory)
|
|
89
|
+
|
|
90
|
+
Same memory store, also wired into [Codex CLI](https://developers.openai.com/codex/cli):
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
lynx-memory init --target codex # or --target all to install both
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
This writes `~/.codex/hooks.json`, sets `[features] codex_hooks = true` in
|
|
97
|
+
`~/.codex/config.toml`, and registers three hooks (`UserPromptSubmit` →
|
|
98
|
+
inject, `Stop` → persist, `SessionStart` → summarize the previous session
|
|
99
|
+
since Codex has no `SessionEnd` event).
|
|
100
|
+
|
|
101
|
+
Codex's `additionalContext` field is fully respected, so retrieved memory
|
|
102
|
+
is injected exactly like in Claude Code. **Restart any running `codex`
|
|
103
|
+
process for hooks to take effect** — they're loaded at session start.
|
|
104
|
+
|
|
105
|
+
A turn typed in Claude Code can be recalled inside Codex (and vice versa)
|
|
106
|
+
because both write to the same SQLite + Chroma store at
|
|
107
|
+
`~/.claude/lynx-memory/`.
|
|
108
|
+
|
|
109
|
+
## CLI
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
lynx-memory init Install hooks and slash commands
|
|
113
|
+
lynx-memory init-project Create a .lynx-memory/ marker in cwd to enable
|
|
114
|
+
project-level storage
|
|
115
|
+
lynx-memory status Show data dir, hook registration, DB stats
|
|
116
|
+
lynx-memory doctor Verify Python, deps, API key, settings.json
|
|
117
|
+
lynx-memory merge Merge memory between the project and global stores
|
|
118
|
+
(--from / --to is project|global, with --dry-run)
|
|
119
|
+
lynx-memory delete Permanently delete memory for a scope
|
|
120
|
+
(--scope project|global|both, with double confirm)
|
|
121
|
+
lynx-memory uninstall Remove hooks and slash commands (keeps your data)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Slash commands
|
|
125
|
+
|
|
126
|
+
`lynx-memory init` also installs five global slash commands into
|
|
127
|
+
`~/.claude/commands/`, callable from any Claude Code session:
|
|
128
|
+
|
|
129
|
+
| Command | What it does |
|
|
130
|
+
| ------------------------------- | ----------------------------------------------------------- |
|
|
131
|
+
| `/lynx-memory-status` | Show current scope (project vs global) with stats for both |
|
|
132
|
+
| `/lynx-memory-pull-global` | Merge global memory into the current project (global → proj)|
|
|
133
|
+
| `/lynx-memory-push-global` | Merge current project memory into global (proj → global) |
|
|
134
|
+
| `/lynx-memory-delete` | Delete memory with mandatory double confirm (`DELETE` + `y`)|
|
|
135
|
+
| `/lynx-memory-history` | Open a local Web UI to browse, search, tag, and delete turns|
|
|
136
|
+
|
|
137
|
+
Each of these runs `lynx-memory status` / `merge --dry-run` first and asks
|
|
138
|
+
for your approval before any write or destructive action.
|
|
139
|
+
|
|
140
|
+
## Web UI
|
|
141
|
+
|
|
142
|
+
Type `/lynx-memory-history` in Claude Code (or run `lynx-memory web`) to
|
|
143
|
+
launch a local FastAPI + React UI on `127.0.0.1`. The page opens automatically
|
|
144
|
+
in your browser and lets you:
|
|
145
|
+
|
|
146
|
+
- Switch between **project** and **global** scopes
|
|
147
|
+
- Page through every saved turn
|
|
148
|
+
- Search by **keyword** (SQL `LIKE`) or **semantic** similarity (Voyage embeddings)
|
|
149
|
+
- Tag turns (e.g. `#work`, `#personal`) and filter by tag
|
|
150
|
+
- Delete a single turn (also clears its embedding from Chroma)
|
|
151
|
+
- See the per-turn **Haiku summary** above each turn, with a one-click button to (re)generate it on demand
|
|
152
|
+
|
|
153
|
+
### Usage
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# default — listens on http://127.0.0.1:9527 and opens your browser
|
|
157
|
+
lynx-memory web
|
|
158
|
+
|
|
159
|
+
# pick a different port
|
|
160
|
+
lynx-memory web --port 8080
|
|
161
|
+
|
|
162
|
+
# or let the OS assign a free port
|
|
163
|
+
lynx-memory web --port 0
|
|
164
|
+
|
|
165
|
+
# don't auto-open the browser (useful in headless / SSH sessions)
|
|
166
|
+
lynx-memory web --no-open
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
| Action | What happens on disk |
|
|
170
|
+
| -------------------- | --------------------------------------------------------------------------- |
|
|
171
|
+
| **Delete a turn** | Row removed from SQLite `turns` and `turn_tags`; embedding removed from Chroma |
|
|
172
|
+
| **Add a tag** | Inserted into SQLite `tags` (created on demand) and `turn_tags` |
|
|
173
|
+
| **Remove a tag** | Row removed from `turn_tags`; orphaned tag is GC'd from `tags` |
|
|
174
|
+
| **Search (keyword)** | SQL `LIKE` over `user_msg` and `assistant_msg` — no embedding call |
|
|
175
|
+
| **Search (semantic)**| One Voyage embedding per query, then top-K from Chroma |
|
|
176
|
+
| **Regenerate summary** | One `claude -p` call (Haiku); writes `summary` / `summary_model` / `summary_ts` back into the `turns` row |
|
|
177
|
+
|
|
178
|
+
The server only binds to `127.0.0.1`. Press `Ctrl+C` to stop it.
|
|
179
|
+
|
|
180
|
+
## Project-level vs global
|
|
181
|
+
|
|
182
|
+
Memory is global by default. Run this in a project root:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
cd ~/code/my-project
|
|
186
|
+
lynx-memory init-project
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
It creates a `.lynx-memory/` marker. As long as your cwd is inside that
|
|
190
|
+
project, memory transparently switches to the project-level store at
|
|
191
|
+
`<project>/.lynx-memory/db/`, isolated from the global one at
|
|
192
|
+
`~/.claude/lynx-memory/`.
|
|
193
|
+
|
|
194
|
+
Use `/lynx-memory-status` to inspect the active scope, and
|
|
195
|
+
`/lynx-memory-pull-global` / `/lynx-memory-push-global` to move history
|
|
196
|
+
between the two layers.
|
|
197
|
+
|
|
198
|
+
## Configuration
|
|
199
|
+
|
|
200
|
+
All optional, set in `~/.claude/lynx-memory/.env`:
|
|
201
|
+
|
|
202
|
+
| Variable | Default | Purpose |
|
|
203
|
+
| ------------------------------ | ------------------------------------ | ------------------------------------------ |
|
|
204
|
+
| `VOYAGE_API_KEY` | — | Required for embeddings |
|
|
205
|
+
| `TOP_K` | `5` | Max memories injected per prompt |
|
|
206
|
+
| `MIN_SCORE` | `0.7` | Cosine similarity floor (0–1) |
|
|
207
|
+
| `SUMMARY_ENABLED` | `1` | Set `0`/`false` to disable per-turn Haiku summarization |
|
|
208
|
+
| `SUMMARY_MODEL` | `claude-haiku-4-5-20251001` | Model used for per-turn summaries |
|
|
209
|
+
| `SUMMARY_BACKEND` | `auto` | `auto` → CLI when `claude` is on PATH, else SDK; force with `cli` or `sdk` |
|
|
210
|
+
| `SUMMARY_TIMEOUT` | `60` | Seconds before the `claude -p` subprocess is killed |
|
|
211
|
+
| `ANTHROPIC_API_KEY` | — | Only needed when `SUMMARY_BACKEND=sdk` (CLI backend reuses your existing `claude` auth) |
|
|
212
|
+
| `LYNX_MEMORY_DIR` | `~/.claude/lynx-memory` | Where SQLite + Chroma live |
|
|
213
|
+
| `LYNX_MEMORY_SUMMARY_MODEL` | `claude-haiku-4-5-20251001` | Model used by `SessionEnd` |
|
|
214
|
+
|
|
215
|
+
## Optional: MCP server
|
|
216
|
+
|
|
217
|
+
You can also expose memory as MCP tools for Claude Code (`search_memory`,
|
|
218
|
+
`list_recent`, `stats`, `forget`). Add to `~/.claude.json` or `.mcp.json`:
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"mcpServers": {
|
|
223
|
+
"lynx-memory": {
|
|
224
|
+
"command": "lynx-memory-mcp"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Uninstall
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
lynx-memory uninstall # remove hooks + slash commands
|
|
234
|
+
lynx-memory delete --scope global # delete the global store (confirms)
|
|
235
|
+
# or
|
|
236
|
+
rm -rf ~/.claude/lynx-memory # nuke directly (irreversible)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Privacy
|
|
240
|
+
|
|
241
|
+
- All data stays on your machine in `~/.claude/lynx-memory/`.
|
|
242
|
+
- Outbound calls: **Voyage AI** for embeddings (your prompt text), **Anthropic**
|
|
243
|
+
for per-turn Haiku summaries (default; goes through your existing `claude`
|
|
244
|
+
CLI session — no extra key) and end-of-session summaries.
|
|
245
|
+
- Set `SUMMARY_ENABLED=0` if you don't want per-turn summaries to leave the box.
|
|
246
|
+
- Set `LYNX_MEMORY_DIR` to encrypt at rest with whatever filesystem-level
|
|
247
|
+
encryption your OS provides.
|
|
248
|
+
|
|
249
|
+
## Roadmap
|
|
250
|
+
|
|
251
|
+
- [x] **Project-level / global dual-layer storage**
|
|
252
|
+
Global by default; auto-switches to project-level when a `.lynx-memory/`
|
|
253
|
+
marker is found by walking up from cwd, so histories from different
|
|
254
|
+
projects don't bleed into each other. Run `lynx-memory init-project`
|
|
255
|
+
in a project root to create the marker. Search supports
|
|
256
|
+
`scope=auto|project|global|merged` (hooks via `LYNX_MEMORY_SCOPE` env;
|
|
257
|
+
MCP tools accept a `scope` argument).
|
|
258
|
+
|
|
259
|
+
- [ ] **Multi-CLI client support**
|
|
260
|
+
Extend beyond Claude Code to **Cursor CLI, Codex CLI, Gemini CLI**. Provide
|
|
261
|
+
`lynx-memory install --client <name>` to write MCP configs in one shot,
|
|
262
|
+
with rules templates that force consistent recall on each client.
|
|
263
|
+
|
|
264
|
+
- [ ] **Import / export & cross-device sync**
|
|
265
|
+
`lynx-memory export` / `import` for JSONL backup and restore; place `db/`
|
|
266
|
+
in iCloud / Dropbox / a Git repo, or use a built-in `lynx-memory sync`
|
|
267
|
+
subcommand to share memory across machines.
|
|
268
|
+
|
|
269
|
+
- [x] **Local Web UI memory browser**
|
|
270
|
+
A local FastAPI + React UI with paging, keyword / semantic search,
|
|
271
|
+
single-turn deletion, and tagging (e.g. `#work`, `#personal`). Launch with
|
|
272
|
+
`/lynx-memory-history` (or `lynx-memory web`); the page exposes both
|
|
273
|
+
project-level and global histories with a one-click scope toggle.
|
|
274
|
+
|
|
275
|
+
## License
|
|
276
|
+
|
|
277
|
+
MIT — see [LICENSE](./LICENSE).
|
openlynx-0.2.0/README.md
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# lynx-memory
|
|
2
|
+
|
|
3
|
+
[中文 README](./README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
Persistent, semantic, long-term memory for [Claude Code](https://claude.com/claude-code).
|
|
6
|
+
Conversations are auto-saved across sessions and the most relevant snippets are
|
|
7
|
+
injected into context whenever you start a new prompt — no special syntax,
|
|
8
|
+
no "remember this" phrasing required.
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
You : What can I do tomorrow if the weather's nice — maybe walk the dog?
|
|
12
|
+
Claude : Since you've got Dandan (your golden Border Collie) who needs a lot
|
|
13
|
+
of exercise, try a long walk, frisbee, or a bike ride with him
|
|
14
|
+
tagging along… 🐶
|
|
15
|
+
(you never mentioned Dandan or owning a dog — memory recalled it
|
|
16
|
+
from a past chat)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## How it works
|
|
20
|
+
|
|
21
|
+
Three Claude Code [hooks](https://docs.claude.com/en/docs/claude-code/hooks) +
|
|
22
|
+
a small Python service:
|
|
23
|
+
|
|
24
|
+
| Hook | What it does |
|
|
25
|
+
| ----------------- | ------------------------------------------------------------------------- |
|
|
26
|
+
| `UserPromptSubmit` | Embeds your prompt and injects the top-K most similar prior turns. When a turn has a Haiku-generated summary, the **summary** is injected instead of the raw prose. |
|
|
27
|
+
| `Stop` | Persists the current user/assistant turn into SQLite + Chroma, then spawns a detached background process that asks Haiku to summarize the turn (no extra API key needed — reuses your `claude` CLI session). |
|
|
28
|
+
| `SessionEnd` | Asks Claude Haiku to produce a coarse summary of the whole session. |
|
|
29
|
+
|
|
30
|
+
Storage:
|
|
31
|
+
|
|
32
|
+
- **SQLite** — source of truth for raw turns, per-turn Haiku summaries, and session summaries
|
|
33
|
+
- **Chroma** — local vector index over turns + summaries
|
|
34
|
+
- **Voyage AI** (`voyage-3`) — embeddings
|
|
35
|
+
- **Claude Haiku** (`claude-haiku-4-5-20251001`) — per-turn summarization, called via `claude -p` so no extra `ANTHROPIC_API_KEY` is required
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install lynx-memory
|
|
41
|
+
lynx-memory init
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`init` will:
|
|
45
|
+
|
|
46
|
+
1. Create `~/.claude/lynx-memory/` (data directory)
|
|
47
|
+
2. Prompt for your `VOYAGE_API_KEY` (get one free at https://www.voyageai.com/)
|
|
48
|
+
3. Write the default `.env` (`MIN_SCORE=0.7`, `SUMMARY_ENABLED=1`,
|
|
49
|
+
`SUMMARY_MODEL=claude-haiku-4-5-20251001`, `SUMMARY_BACKEND=auto`) — the
|
|
50
|
+
per-turn Haiku summarizer reuses your existing `claude` CLI session, so
|
|
51
|
+
no extra `ANTHROPIC_API_KEY` is required by default
|
|
52
|
+
4. Back up your existing `~/.claude/settings.json` and add the three hooks
|
|
53
|
+
5. Print verification steps
|
|
54
|
+
|
|
55
|
+
Then open a fresh Claude Code session, chat for a few turns, and run:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
lynx-memory status
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
You should see `turns` and `chroma_turns` counters going up.
|
|
62
|
+
|
|
63
|
+
## Codex CLI (cross-host memory)
|
|
64
|
+
|
|
65
|
+
Same memory store, also wired into [Codex CLI](https://developers.openai.com/codex/cli):
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
lynx-memory init --target codex # or --target all to install both
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This writes `~/.codex/hooks.json`, sets `[features] codex_hooks = true` in
|
|
72
|
+
`~/.codex/config.toml`, and registers three hooks (`UserPromptSubmit` →
|
|
73
|
+
inject, `Stop` → persist, `SessionStart` → summarize the previous session
|
|
74
|
+
since Codex has no `SessionEnd` event).
|
|
75
|
+
|
|
76
|
+
Codex's `additionalContext` field is fully respected, so retrieved memory
|
|
77
|
+
is injected exactly like in Claude Code. **Restart any running `codex`
|
|
78
|
+
process for hooks to take effect** — they're loaded at session start.
|
|
79
|
+
|
|
80
|
+
A turn typed in Claude Code can be recalled inside Codex (and vice versa)
|
|
81
|
+
because both write to the same SQLite + Chroma store at
|
|
82
|
+
`~/.claude/lynx-memory/`.
|
|
83
|
+
|
|
84
|
+
## CLI
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
lynx-memory init Install hooks and slash commands
|
|
88
|
+
lynx-memory init-project Create a .lynx-memory/ marker in cwd to enable
|
|
89
|
+
project-level storage
|
|
90
|
+
lynx-memory status Show data dir, hook registration, DB stats
|
|
91
|
+
lynx-memory doctor Verify Python, deps, API key, settings.json
|
|
92
|
+
lynx-memory merge Merge memory between the project and global stores
|
|
93
|
+
(--from / --to is project|global, with --dry-run)
|
|
94
|
+
lynx-memory delete Permanently delete memory for a scope
|
|
95
|
+
(--scope project|global|both, with double confirm)
|
|
96
|
+
lynx-memory uninstall Remove hooks and slash commands (keeps your data)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Slash commands
|
|
100
|
+
|
|
101
|
+
`lynx-memory init` also installs five global slash commands into
|
|
102
|
+
`~/.claude/commands/`, callable from any Claude Code session:
|
|
103
|
+
|
|
104
|
+
| Command | What it does |
|
|
105
|
+
| ------------------------------- | ----------------------------------------------------------- |
|
|
106
|
+
| `/lynx-memory-status` | Show current scope (project vs global) with stats for both |
|
|
107
|
+
| `/lynx-memory-pull-global` | Merge global memory into the current project (global → proj)|
|
|
108
|
+
| `/lynx-memory-push-global` | Merge current project memory into global (proj → global) |
|
|
109
|
+
| `/lynx-memory-delete` | Delete memory with mandatory double confirm (`DELETE` + `y`)|
|
|
110
|
+
| `/lynx-memory-history` | Open a local Web UI to browse, search, tag, and delete turns|
|
|
111
|
+
|
|
112
|
+
Each of these runs `lynx-memory status` / `merge --dry-run` first and asks
|
|
113
|
+
for your approval before any write or destructive action.
|
|
114
|
+
|
|
115
|
+
## Web UI
|
|
116
|
+
|
|
117
|
+
Type `/lynx-memory-history` in Claude Code (or run `lynx-memory web`) to
|
|
118
|
+
launch a local FastAPI + React UI on `127.0.0.1`. The page opens automatically
|
|
119
|
+
in your browser and lets you:
|
|
120
|
+
|
|
121
|
+
- Switch between **project** and **global** scopes
|
|
122
|
+
- Page through every saved turn
|
|
123
|
+
- Search by **keyword** (SQL `LIKE`) or **semantic** similarity (Voyage embeddings)
|
|
124
|
+
- Tag turns (e.g. `#work`, `#personal`) and filter by tag
|
|
125
|
+
- Delete a single turn (also clears its embedding from Chroma)
|
|
126
|
+
- See the per-turn **Haiku summary** above each turn, with a one-click button to (re)generate it on demand
|
|
127
|
+
|
|
128
|
+
### Usage
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# default — listens on http://127.0.0.1:9527 and opens your browser
|
|
132
|
+
lynx-memory web
|
|
133
|
+
|
|
134
|
+
# pick a different port
|
|
135
|
+
lynx-memory web --port 8080
|
|
136
|
+
|
|
137
|
+
# or let the OS assign a free port
|
|
138
|
+
lynx-memory web --port 0
|
|
139
|
+
|
|
140
|
+
# don't auto-open the browser (useful in headless / SSH sessions)
|
|
141
|
+
lynx-memory web --no-open
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
| Action | What happens on disk |
|
|
145
|
+
| -------------------- | --------------------------------------------------------------------------- |
|
|
146
|
+
| **Delete a turn** | Row removed from SQLite `turns` and `turn_tags`; embedding removed from Chroma |
|
|
147
|
+
| **Add a tag** | Inserted into SQLite `tags` (created on demand) and `turn_tags` |
|
|
148
|
+
| **Remove a tag** | Row removed from `turn_tags`; orphaned tag is GC'd from `tags` |
|
|
149
|
+
| **Search (keyword)** | SQL `LIKE` over `user_msg` and `assistant_msg` — no embedding call |
|
|
150
|
+
| **Search (semantic)**| One Voyage embedding per query, then top-K from Chroma |
|
|
151
|
+
| **Regenerate summary** | One `claude -p` call (Haiku); writes `summary` / `summary_model` / `summary_ts` back into the `turns` row |
|
|
152
|
+
|
|
153
|
+
The server only binds to `127.0.0.1`. Press `Ctrl+C` to stop it.
|
|
154
|
+
|
|
155
|
+
## Project-level vs global
|
|
156
|
+
|
|
157
|
+
Memory is global by default. Run this in a project root:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
cd ~/code/my-project
|
|
161
|
+
lynx-memory init-project
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
It creates a `.lynx-memory/` marker. As long as your cwd is inside that
|
|
165
|
+
project, memory transparently switches to the project-level store at
|
|
166
|
+
`<project>/.lynx-memory/db/`, isolated from the global one at
|
|
167
|
+
`~/.claude/lynx-memory/`.
|
|
168
|
+
|
|
169
|
+
Use `/lynx-memory-status` to inspect the active scope, and
|
|
170
|
+
`/lynx-memory-pull-global` / `/lynx-memory-push-global` to move history
|
|
171
|
+
between the two layers.
|
|
172
|
+
|
|
173
|
+
## Configuration
|
|
174
|
+
|
|
175
|
+
All optional, set in `~/.claude/lynx-memory/.env`:
|
|
176
|
+
|
|
177
|
+
| Variable | Default | Purpose |
|
|
178
|
+
| ------------------------------ | ------------------------------------ | ------------------------------------------ |
|
|
179
|
+
| `VOYAGE_API_KEY` | — | Required for embeddings |
|
|
180
|
+
| `TOP_K` | `5` | Max memories injected per prompt |
|
|
181
|
+
| `MIN_SCORE` | `0.7` | Cosine similarity floor (0–1) |
|
|
182
|
+
| `SUMMARY_ENABLED` | `1` | Set `0`/`false` to disable per-turn Haiku summarization |
|
|
183
|
+
| `SUMMARY_MODEL` | `claude-haiku-4-5-20251001` | Model used for per-turn summaries |
|
|
184
|
+
| `SUMMARY_BACKEND` | `auto` | `auto` → CLI when `claude` is on PATH, else SDK; force with `cli` or `sdk` |
|
|
185
|
+
| `SUMMARY_TIMEOUT` | `60` | Seconds before the `claude -p` subprocess is killed |
|
|
186
|
+
| `ANTHROPIC_API_KEY` | — | Only needed when `SUMMARY_BACKEND=sdk` (CLI backend reuses your existing `claude` auth) |
|
|
187
|
+
| `LYNX_MEMORY_DIR` | `~/.claude/lynx-memory` | Where SQLite + Chroma live |
|
|
188
|
+
| `LYNX_MEMORY_SUMMARY_MODEL` | `claude-haiku-4-5-20251001` | Model used by `SessionEnd` |
|
|
189
|
+
|
|
190
|
+
## Optional: MCP server
|
|
191
|
+
|
|
192
|
+
You can also expose memory as MCP tools for Claude Code (`search_memory`,
|
|
193
|
+
`list_recent`, `stats`, `forget`). Add to `~/.claude.json` or `.mcp.json`:
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"mcpServers": {
|
|
198
|
+
"lynx-memory": {
|
|
199
|
+
"command": "lynx-memory-mcp"
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Uninstall
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
lynx-memory uninstall # remove hooks + slash commands
|
|
209
|
+
lynx-memory delete --scope global # delete the global store (confirms)
|
|
210
|
+
# or
|
|
211
|
+
rm -rf ~/.claude/lynx-memory # nuke directly (irreversible)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Privacy
|
|
215
|
+
|
|
216
|
+
- All data stays on your machine in `~/.claude/lynx-memory/`.
|
|
217
|
+
- Outbound calls: **Voyage AI** for embeddings (your prompt text), **Anthropic**
|
|
218
|
+
for per-turn Haiku summaries (default; goes through your existing `claude`
|
|
219
|
+
CLI session — no extra key) and end-of-session summaries.
|
|
220
|
+
- Set `SUMMARY_ENABLED=0` if you don't want per-turn summaries to leave the box.
|
|
221
|
+
- Set `LYNX_MEMORY_DIR` to encrypt at rest with whatever filesystem-level
|
|
222
|
+
encryption your OS provides.
|
|
223
|
+
|
|
224
|
+
## Roadmap
|
|
225
|
+
|
|
226
|
+
- [x] **Project-level / global dual-layer storage**
|
|
227
|
+
Global by default; auto-switches to project-level when a `.lynx-memory/`
|
|
228
|
+
marker is found by walking up from cwd, so histories from different
|
|
229
|
+
projects don't bleed into each other. Run `lynx-memory init-project`
|
|
230
|
+
in a project root to create the marker. Search supports
|
|
231
|
+
`scope=auto|project|global|merged` (hooks via `LYNX_MEMORY_SCOPE` env;
|
|
232
|
+
MCP tools accept a `scope` argument).
|
|
233
|
+
|
|
234
|
+
- [ ] **Multi-CLI client support**
|
|
235
|
+
Extend beyond Claude Code to **Cursor CLI, Codex CLI, Gemini CLI**. Provide
|
|
236
|
+
`lynx-memory install --client <name>` to write MCP configs in one shot,
|
|
237
|
+
with rules templates that force consistent recall on each client.
|
|
238
|
+
|
|
239
|
+
- [ ] **Import / export & cross-device sync**
|
|
240
|
+
`lynx-memory export` / `import` for JSONL backup and restore; place `db/`
|
|
241
|
+
in iCloud / Dropbox / a Git repo, or use a built-in `lynx-memory sync`
|
|
242
|
+
subcommand to share memory across machines.
|
|
243
|
+
|
|
244
|
+
- [x] **Local Web UI memory browser**
|
|
245
|
+
A local FastAPI + React UI with paging, keyword / semantic search,
|
|
246
|
+
single-turn deletion, and tagging (e.g. `#work`, `#personal`). Launch with
|
|
247
|
+
`/lynx-memory-history` (or `lynx-memory web`); the page exposes both
|
|
248
|
+
project-level and global histories with a one-click scope toggle.
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT — see [LICENSE](./LICENSE).
|