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.
Files changed (29) hide show
  1. openlynx-0.2.0/.gitignore +22 -0
  2. openlynx-0.2.0/LICENSE +21 -0
  3. openlynx-0.2.0/PKG-INFO +277 -0
  4. openlynx-0.2.0/README.md +252 -0
  5. openlynx-0.2.0/README.zh-CN.md +230 -0
  6. openlynx-0.2.0/pyproject.toml +64 -0
  7. openlynx-0.2.0/src/lynx_memory/__init__.py +1 -0
  8. openlynx-0.2.0/src/lynx_memory/assets/__init__.py +0 -0
  9. openlynx-0.2.0/src/lynx_memory/assets/commands/lynx-memory-delete.md +56 -0
  10. openlynx-0.2.0/src/lynx_memory/assets/commands/lynx-memory-history.md +31 -0
  11. openlynx-0.2.0/src/lynx_memory/assets/commands/lynx-memory-pull-global.md +40 -0
  12. openlynx-0.2.0/src/lynx_memory/assets/commands/lynx-memory-push-global.md +40 -0
  13. openlynx-0.2.0/src/lynx_memory/assets/commands/lynx-memory-status.md +20 -0
  14. openlynx-0.2.0/src/lynx_memory/assets/web/assets/index-B0j42wWr.js +68 -0
  15. openlynx-0.2.0/src/lynx_memory/assets/web/assets/index-ChnC4PlG.css +1 -0
  16. openlynx-0.2.0/src/lynx_memory/assets/web/index.html +13 -0
  17. openlynx-0.2.0/src/lynx_memory/cli.py +935 -0
  18. openlynx-0.2.0/src/lynx_memory/config.py +108 -0
  19. openlynx-0.2.0/src/lynx_memory/embeddings.py +31 -0
  20. openlynx-0.2.0/src/lynx_memory/hooks/__init__.py +0 -0
  21. openlynx-0.2.0/src/lynx_memory/hooks/_log.py +11 -0
  22. openlynx-0.2.0/src/lynx_memory/hooks/on_prompt.py +135 -0
  23. openlynx-0.2.0/src/lynx_memory/hooks/on_session_end.py +164 -0
  24. openlynx-0.2.0/src/lynx_memory/hooks/on_stop.py +35 -0
  25. openlynx-0.2.0/src/lynx_memory/server.py +108 -0
  26. openlynx-0.2.0/src/lynx_memory/storage.py +660 -0
  27. openlynx-0.2.0/src/lynx_memory/summarizer.py +209 -0
  28. openlynx-0.2.0/src/lynx_memory/transcript.py +281 -0
  29. 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.
@@ -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).
@@ -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).