ccrecall 0.10.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 (75) hide show
  1. ccrecall-0.10.0/LICENSE +21 -0
  2. ccrecall-0.10.0/PKG-INFO +244 -0
  3. ccrecall-0.10.0/README.md +210 -0
  4. ccrecall-0.10.0/pyproject.toml +113 -0
  5. ccrecall-0.10.0/setup.cfg +4 -0
  6. ccrecall-0.10.0/src/ccrecall/__init__.py +18 -0
  7. ccrecall-0.10.0/src/ccrecall/cli/__init__.py +98 -0
  8. ccrecall-0.10.0/src/ccrecall/cli/commands.py +245 -0
  9. ccrecall-0.10.0/src/ccrecall/cli/context.py +31 -0
  10. ccrecall-0.10.0/src/ccrecall/content.py +152 -0
  11. ccrecall-0.10.0/src/ccrecall/db.py +306 -0
  12. ccrecall-0.10.0/src/ccrecall/embeddings.py +125 -0
  13. ccrecall-0.10.0/src/ccrecall/formatting.py +144 -0
  14. ccrecall-0.10.0/src/ccrecall/fusion.py +17 -0
  15. ccrecall-0.10.0/src/ccrecall/hooks/__init__.py +0 -0
  16. ccrecall-0.10.0/src/ccrecall/hooks/backfill_embeddings.py +374 -0
  17. ccrecall-0.10.0/src/ccrecall/hooks/backfill_summaries.py +99 -0
  18. ccrecall-0.10.0/src/ccrecall/hooks/clear_handoff.py +49 -0
  19. ccrecall-0.10.0/src/ccrecall/hooks/import_conversations.py +271 -0
  20. ccrecall-0.10.0/src/ccrecall/hooks/memory_context.py +539 -0
  21. ccrecall-0.10.0/src/ccrecall/hooks/memory_setup.py +162 -0
  22. ccrecall-0.10.0/src/ccrecall/hooks/memory_sync.py +57 -0
  23. ccrecall-0.10.0/src/ccrecall/hooks/onboarding.py +83 -0
  24. ccrecall-0.10.0/src/ccrecall/hooks/sync_current.py +154 -0
  25. ccrecall-0.10.0/src/ccrecall/hooks/write_config.py +51 -0
  26. ccrecall-0.10.0/src/ccrecall/models.py +121 -0
  27. ccrecall-0.10.0/src/ccrecall/parsing.py +314 -0
  28. ccrecall-0.10.0/src/ccrecall/project_ops.py +92 -0
  29. ccrecall-0.10.0/src/ccrecall/recent_chats.py +204 -0
  30. ccrecall-0.10.0/src/ccrecall/schema.py +192 -0
  31. ccrecall-0.10.0/src/ccrecall/search_conversations.py +500 -0
  32. ccrecall-0.10.0/src/ccrecall/serialization.py +41 -0
  33. ccrecall-0.10.0/src/ccrecall/session_ops.py +563 -0
  34. ccrecall-0.10.0/src/ccrecall/session_tail.py +384 -0
  35. ccrecall-0.10.0/src/ccrecall/summarizer.py +427 -0
  36. ccrecall-0.10.0/src/ccrecall/token_analytics.py +212 -0
  37. ccrecall-0.10.0/src/ccrecall/token_dashboard.py +119 -0
  38. ccrecall-0.10.0/src/ccrecall/token_insights.py +862 -0
  39. ccrecall-0.10.0/src/ccrecall/token_output.py +867 -0
  40. ccrecall-0.10.0/src/ccrecall/token_parser.py +661 -0
  41. ccrecall-0.10.0/src/ccrecall/token_schema.py +206 -0
  42. ccrecall-0.10.0/src/ccrecall.egg-info/PKG-INFO +244 -0
  43. ccrecall-0.10.0/src/ccrecall.egg-info/SOURCES.txt +73 -0
  44. ccrecall-0.10.0/src/ccrecall.egg-info/dependency_links.txt +1 -0
  45. ccrecall-0.10.0/src/ccrecall.egg-info/entry_points.txt +7 -0
  46. ccrecall-0.10.0/src/ccrecall.egg-info/requires.txt +11 -0
  47. ccrecall-0.10.0/src/ccrecall.egg-info/top_level.txt +1 -0
  48. ccrecall-0.10.0/tests/test_backfill_embeddings.py +719 -0
  49. ccrecall-0.10.0/tests/test_boundary_validation.py +149 -0
  50. ccrecall-0.10.0/tests/test_clear_handoff_contract.py +233 -0
  51. ccrecall-0.10.0/tests/test_cli_context.py +23 -0
  52. ccrecall-0.10.0/tests/test_content.py +377 -0
  53. ccrecall-0.10.0/tests/test_context_injection.py +738 -0
  54. ccrecall-0.10.0/tests/test_db.py +826 -0
  55. ccrecall-0.10.0/tests/test_embeddings.py +110 -0
  56. ccrecall-0.10.0/tests/test_formatting.py +283 -0
  57. ccrecall-0.10.0/tests/test_fusion.py +62 -0
  58. ccrecall-0.10.0/tests/test_import_pipeline.py +699 -0
  59. ccrecall-0.10.0/tests/test_ingest_token_data.py +620 -0
  60. ccrecall-0.10.0/tests/test_integration.py +267 -0
  61. ccrecall-0.10.0/tests/test_onboarding.py +164 -0
  62. ccrecall-0.10.0/tests/test_parsing.py +383 -0
  63. ccrecall-0.10.0/tests/test_project_ops.py +132 -0
  64. ccrecall-0.10.0/tests/test_recent_chats.py +158 -0
  65. ccrecall-0.10.0/tests/test_search.py +909 -0
  66. ccrecall-0.10.0/tests/test_security.py +92 -0
  67. ccrecall-0.10.0/tests/test_serialization.py +58 -0
  68. ccrecall-0.10.0/tests/test_session_ops.py +690 -0
  69. ccrecall-0.10.0/tests/test_session_tail.py +276 -0
  70. ccrecall-0.10.0/tests/test_summarizer.py +667 -0
  71. ccrecall-0.10.0/tests/test_sync_hook.py +641 -0
  72. ccrecall-0.10.0/tests/test_token_insights.py +530 -0
  73. ccrecall-0.10.0/tests/test_token_output.py +211 -0
  74. ccrecall-0.10.0/tests/test_token_parser.py +280 -0
  75. ccrecall-0.10.0/tests/test_write_config.py +186 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jessica Smith
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,244 @@
1
+ Metadata-Version: 2.4
2
+ Name: ccrecall
3
+ Version: 0.10.0
4
+ Summary: Conversation history and semantic search for Claude Code
5
+ Author-email: Jessica Smith <12jessicasmith34@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/NodeJSmith/claude-code-recall
8
+ Project-URL: Issues, https://github.com/NodeJSmith/claude-code-recall/issues
9
+ Project-URL: Changelog, https://github.com/NodeJSmith/claude-code-recall/blob/main/CHANGELOG.md
10
+ Keywords: claude-code,conversation-history,search,recall,transcripts,semantic-search
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Utilities
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: sqlite-vec==0.1.9
27
+ Requires-Dist: fastembed==0.8.0
28
+ Requires-Dist: numpy==2.2.6; python_version < "3.11"
29
+ Requires-Dist: numpy==2.4.6; python_version >= "3.11"
30
+ Requires-Dist: whenever==0.10.0
31
+ Requires-Dist: pydantic==2.13.4
32
+ Requires-Dist: cyclopts>=4.16
33
+ Dynamic: license-file
34
+
35
+ # ccrecall
36
+
37
+ **Conversation history and semantic search for Claude Code.**
38
+
39
+ ccrecall stores your Claude Code sessions in a local SQLite database so you can recall past conversations, search across them by keyword and meaning, and get automatic context on session start. Everything runs on your machine — no data leaves it.
40
+
41
+ > ccrecall is an independent, community project for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). It is not affiliated with, endorsed by, or sponsored by Anthropic.
42
+
43
+ ## What it does
44
+
45
+ Every time a Claude Code session ends, the conversation is synced to `~/.ccrecall/conversations.db`. On your next session start, Claude automatically gets a summary of what you were last working on. You can also search past sessions by keyword or pull recent ones at any time.
46
+
47
+ ## Install
48
+
49
+ ccrecall has two parts: a **Python package** (the `ccrecall` CLI plus the hook binaries) and a **Claude Code plugin** (the `/ccr-*` skills and the hook wiring). Install both.
50
+
51
+ **1. Install the package** — puts `ccrecall` and the hook commands on your PATH:
52
+
53
+ ```bash
54
+ uv tool install ccrecall
55
+ ```
56
+
57
+ (`pipx install ccrecall` or `pip install ccrecall` work too.)
58
+
59
+ **2. Enable the plugin** — ccrecall ships as a Claude Code plugin. The repo doubles as a single-plugin marketplace, so from inside Claude Code:
60
+
61
+ ```
62
+ /plugin marketplace add NodeJSmith/claude-code-recall
63
+ /plugin install ccrecall@claude-code-recall
64
+ ```
65
+
66
+ That registers the skills and wires the SessionStart / Stop / SessionEnd hooks (`hooks/hooks.json`) — both are auto-discovered from the plugin's directory layout. Reload with `/reload-plugins` if they don't appear immediately.
67
+
68
+ > Plugin skills are namespaced under the plugin name, so the skills below are invoked as `/ccrecall:ccr-recall`, `/ccrecall:ccr-resume`, and `/ccrecall:ccr-tokens`. The hook commands degrade gracefully — each is guarded by `command -v … || true`, so if the package isn't installed (or isn't yet on PATH) the hook is a silent no-op rather than a broken session.
69
+
70
+ ## First-run setup
71
+
72
+ On your first session after installing, Claude will notice that `~/.ccrecall/config.json` doesn't exist and walk you through a brief onboarding. It asks a single question — **session context injection**: should Claude automatically recall what you were working on last session?
73
+
74
+ Your choice gets written to `~/.ccrecall/config.json`. You can edit that file directly at any time to change settings.
75
+
76
+ To skip the walkthrough and use recommended defaults immediately:
77
+
78
+ ```bash
79
+ ccrecall write-config --defaults
80
+ ```
81
+
82
+ ## Semantic search
83
+
84
+ Search results are fused from two signals: keyword full-text search (FTS5 → FTS4 → LIKE fallback) and vector similarity from a locally-running embedding model. The two ranked lists are merged with Reciprocal Rank Fusion (RRF), so results that rank well in both signals appear first.
85
+
86
+ The embedding model is [jina-embeddings-v2-small-en](https://huggingface.co/jinaai/jina-embeddings-v2-small-en) (512-dim), running entirely on your machine via [fastembed](https://github.com/qdrant/fastembed). No data leaves your machine.
87
+
88
+ ### Coverage
89
+
90
+ New sessions are embedded automatically as they sync (embed-on-write), so coverage builds forward on its own. Only **active-leaf** branches are embedded — at most one active leaf per session (maintained by sync/import), not its abandoned forks/retries. The flag isn't DB-enforced, but sync/import marks exactly one branch `is_active=1` per session. The search path only ever returns active leaves, so embedding inactive forks would just produce vectors that can never surface.
91
+
92
+ ### Optional: seed historical conversations
93
+
94
+ Embedding runs on CPU via fastembed. jina-v2-small-en is light — a few milliseconds for a short summary, up to ~400ms for a long one — but seeding a large history (~2k active leaves) is still a bounded chunk of work, and a parallel run can thrash a small or shared box. It is therefore **opt-in** — it is *not* auto-spawned on SessionStart — so it never fires unbidden. Run it yourself when you want to seed:
95
+
96
+ ```bash
97
+ ccrecall backfill embeddings # all active leaves, all history
98
+ ccrecall backfill embeddings --days 14 # only the last 14 days
99
+ ccrecall backfill embeddings --limit 500 # cap this run at 500 branches
100
+ ccrecall backfill embeddings --threads 4 # use 4 inference threads (idle machine)
101
+ ```
102
+
103
+ It runs at low scheduling priority (`nice`) and a single inference thread by default so it yields to interactive work. Tune the thread count with `--threads` (e.g. `--threads 4` on an idle workstation to finish faster). Progress prints to stderr (one line per batch); the run is resumable — re-running skips already-embedded branches.
104
+
105
+ ### Flags
106
+
107
+ | Flag | Effect |
108
+ |------|--------|
109
+ | `--keyword-only` | Skip the embedding step entirely, use keyword search only |
110
+ | `--status` | Print diagnostic info (vec extension loaded, model name, embedded vs. total summarized (embeddable) branch count) and exit 0 |
111
+
112
+ ### Runtime deps
113
+
114
+ The semantic search path requires three extra packages beyond the base install:
115
+
116
+ - `sqlite-vec` — SQLite extension for vector KNN queries
117
+ - `fastembed` — downloads and runs the embedding model (manages onnxruntime + tokenization)
118
+ - `numpy` — vector math (normalization)
119
+
120
+ These are included in the package dependencies. If fastembed fails to import (e.g. ABI mismatch on an unusual platform), search falls back silently to keyword-only mode.
121
+
122
+ ### Degradation
123
+
124
+ Semantic fusion is automatically disabled when:
125
+ - The embedding model can't be loaded (e.g. a first-run download failed and no cached copy exists)
126
+ - `fastembed` cannot be imported
127
+ - `sqlite-vec` cannot be loaded on the connection (e.g. Python built without loadable extensions)
128
+
129
+ In all cases, search falls back to keyword-only and returns results normally. Use `ccrecall search --status` to check which path is active.
130
+
131
+ ## Skills
132
+
133
+ | Skill | Trigger | What it does |
134
+ |---|---|---|
135
+ | `/ccr-recall` | "what did we discuss", "continue where we left off", "search my conversations" | Lets Claude search or browse your past sessions on demand |
136
+ | `/ccr-resume` | "pick up where we left off after /clear", a stop, or an unanswered question | Reconstructs the prior session's intent from its transcript tail and surfaces any unresolved decision |
137
+ | `/ccr-tokens` | "analyze Claude token usage", "how much am I spending on Claude" | Full cost + workflow analytics report with an interactive HTML dashboard |
138
+
139
+ ## Entry points
140
+
141
+ ### Hooks (run automatically — don't call these manually)
142
+
143
+ These are wired by the plugin's `hooks/hooks.json` and fire on their respective Claude Code events.
144
+
145
+ | Entry point | Event | What it does |
146
+ |---|---|---|
147
+ | `ccrecall-setup` | SessionStart | Creates `~/.ccrecall/` if needed, opens the DB to apply any pending migrations, then spawns `ccrecall import` and `ccrecall backfill summaries` as background processes |
148
+ | `ccrecall-onboarding` | SessionStart (startup only) | One-time first-run onboarding. Injects setup instructions into Claude's context if `config.json` is missing or onboarding hasn't been completed. Silent no-op after that |
149
+ | `ccrecall-context` | SessionStart (startup + clear) | Injects a summary of your most recent session into Claude's context so it knows what you were working on. On `/clear`, reads a handoff file to link directly to the session you just cleared from |
150
+ | `ccrecall-clear-handoff` | SessionEnd (clear only) | Writes a small handoff file so the next session start knows which session to link to after a `/clear`. Without this, context injection falls back to a "most recent session" heuristic |
151
+ | `ccrecall-sync` | Stop | Syncs the current session to the DB in a detached background process. Runs on every session end |
152
+
153
+ > These are kept as separate console scripts (rather than `ccrecall hook …` subcommands) on purpose: hooks fire on every session boundary, and a direct entry point avoids eagerly importing the full CLI command surface on the hot path.
154
+
155
+ ### Internal helpers (spawned by hooks — don't call these manually)
156
+
157
+ | Entry point | What it does |
158
+ |---|---|
159
+ | `ccrecall sync-current` | Syncs a single session file to the DB. Called by `ccrecall-sync` with the session ID from stdin |
160
+ | `ccrecall import` | Full import of all JSONL files in `~/.claude/projects/`. Skips files that haven't changed since last import (file hash check). Run on first install and whenever new sessions need backfilling |
161
+ | `ccrecall backfill summaries` | Generates context summaries for any DB branches that don't have one yet. Runs in the background after `ccrecall-setup` |
162
+ | `ccrecall write-config` | Writes `~/.ccrecall/config.json`. Called by Claude during onboarding to persist your settings choices. You can also call it directly — run `ccrecall write-config --help` for flags |
163
+
164
+ ### Skill CLIs (called from skill files — can also be used directly)
165
+
166
+ These are the `ccrecall` subcommands the `/ccr-*` skills invoke. You can run them from the terminal too.
167
+
168
+ | Entry point | What it does |
169
+ |---|---|
170
+ | `ccrecall recent` | Prints recent sessions from the DB in markdown (default) or JSON. Used by `/ccr-recall` |
171
+ | `ccrecall search` | Searches sessions by keyword fused with vector similarity (FTS5 → FTS4 → LIKE fallback, RRF-fused with jina embeddings when available). Used by `/ccr-recall` |
172
+ | `ccrecall tail` | Reads the tail of a prior session's transcript to recover the last instruction and any unanswered question. Used by `/ccr-resume` |
173
+ | `ccrecall backfill embeddings` | Opt-in seeding of embeddings for historical active-leaf branches (jina-v2-small-en via fastembed). Not auto-spawned. Supports `--days N` / `--limit N` / `--threads N`; throttled via `nice` + a single inference thread by default. Resumable |
174
+ | `ccrecall tokens` | Parses JSONL files for token usage analytics — cost, cache hits, model mix, skill/agent/hook patterns. Populates analytics tables and builds `~/.ccrecall/dashboard.html`. Used by `/ccr-tokens` |
175
+
176
+ ## Data flow
177
+
178
+ ```
179
+ Session ends
180
+ └─ ccrecall-sync (Stop hook)
181
+ └─ ccrecall sync-current (background)
182
+ └─ writes to ~/.ccrecall/conversations.db
183
+ └─ embeds the active leaf via jina if model available (drops silently on failure)
184
+
185
+ /clear (SessionEnd)
186
+ └─ ccrecall-clear-handoff
187
+ └─ writes a handoff file naming the session being cleared
188
+ (so the next SessionStart links to it instead of guessing)
189
+
190
+ Session starts
191
+ └─ ccrecall-setup (SessionStart)
192
+ │ └─ ccrecall import (background, first run / new files)
193
+ │ └─ embeds each new active leaf via jina if model available
194
+ │ └─ ccrecall backfill summaries (background, if summaries missing)
195
+ │ └─ (embedding backfill is NOT auto-spawned — opt-in via ccrecall backfill embeddings)
196
+ ├─ ccrecall-onboarding (SessionStart, startup only — one-time)
197
+ └─ ccrecall-context (SessionStart, startup + clear)
198
+ └─ injects last session summary into Claude's context
199
+ ```
200
+
201
+ ## Config file
202
+
203
+ `~/.ccrecall/config.json` — written by `ccrecall write-config` during onboarding:
204
+
205
+ ```json
206
+ {
207
+ "onboarding_completed": true,
208
+ "onboarding_version": 1,
209
+ "auto_inject_context": true
210
+ }
211
+ ```
212
+
213
+ Onboarding sets `auto_inject_context`. The remaining settings are tunable by editing `config.json` directly:
214
+
215
+ | Key | Type | Default | Effect |
216
+ |---|---|---|---|
217
+ | `auto_inject_context` | bool | `true` | Inject a summary of your previous session at session start. |
218
+ | `max_context_sessions` | int | `2` | How many recent sessions to include in that injected context. |
219
+ | `exclude_projects` | list[str] | `[]` | Project names to skip when **storing** conversations — excluded projects are not imported or synced. Matched against the project's directory name. This is write-side only: it prevents new data from being indexed; it does not remove or hide conversations already stored before the project was excluded. |
220
+ | `logging_enabled` | bool | `false` | Write hook diagnostics (including swallowed hook exceptions) to `~/.ccrecall/ccrecall.log`. Useful for troubleshooting a misbehaving hook. |
221
+
222
+ ## Database
223
+
224
+ `~/.ccrecall/conversations.db` — SQLite, WAL mode. Tables:
225
+
226
+ - `sessions` — one row per conversation session
227
+ - `branches` — one row per conversation branch (rewinding creates new branches)
228
+ - `messages` — all messages, stored once per session regardless of branch
229
+ - `branch_messages` — join table linking messages to branches
230
+ - `import_log` — tracks which JSONL files have been imported and their hashes
231
+ - `branch_vec` — vec0 virtual table (sqlite-vec) storing 512-dim jina embeddings for each branch, used for KNN search
232
+ - `token_snapshots`, `turns`, `turn_tool_calls`, `session_metrics` — analytics tables populated by `ccrecall tokens`
233
+
234
+ ## Development
235
+
236
+ ```bash
237
+ uv sync # install package + dev dependencies
238
+ uv run pytest # run the test suite
239
+ uvx prek run --all-files # run the lint/format/type hooks
240
+ ```
241
+
242
+ ## License
243
+
244
+ [MIT](LICENSE)
@@ -0,0 +1,210 @@
1
+ # ccrecall
2
+
3
+ **Conversation history and semantic search for Claude Code.**
4
+
5
+ ccrecall stores your Claude Code sessions in a local SQLite database so you can recall past conversations, search across them by keyword and meaning, and get automatic context on session start. Everything runs on your machine — no data leaves it.
6
+
7
+ > ccrecall is an independent, community project for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). It is not affiliated with, endorsed by, or sponsored by Anthropic.
8
+
9
+ ## What it does
10
+
11
+ Every time a Claude Code session ends, the conversation is synced to `~/.ccrecall/conversations.db`. On your next session start, Claude automatically gets a summary of what you were last working on. You can also search past sessions by keyword or pull recent ones at any time.
12
+
13
+ ## Install
14
+
15
+ ccrecall has two parts: a **Python package** (the `ccrecall` CLI plus the hook binaries) and a **Claude Code plugin** (the `/ccr-*` skills and the hook wiring). Install both.
16
+
17
+ **1. Install the package** — puts `ccrecall` and the hook commands on your PATH:
18
+
19
+ ```bash
20
+ uv tool install ccrecall
21
+ ```
22
+
23
+ (`pipx install ccrecall` or `pip install ccrecall` work too.)
24
+
25
+ **2. Enable the plugin** — ccrecall ships as a Claude Code plugin. The repo doubles as a single-plugin marketplace, so from inside Claude Code:
26
+
27
+ ```
28
+ /plugin marketplace add NodeJSmith/claude-code-recall
29
+ /plugin install ccrecall@claude-code-recall
30
+ ```
31
+
32
+ That registers the skills and wires the SessionStart / Stop / SessionEnd hooks (`hooks/hooks.json`) — both are auto-discovered from the plugin's directory layout. Reload with `/reload-plugins` if they don't appear immediately.
33
+
34
+ > Plugin skills are namespaced under the plugin name, so the skills below are invoked as `/ccrecall:ccr-recall`, `/ccrecall:ccr-resume`, and `/ccrecall:ccr-tokens`. The hook commands degrade gracefully — each is guarded by `command -v … || true`, so if the package isn't installed (or isn't yet on PATH) the hook is a silent no-op rather than a broken session.
35
+
36
+ ## First-run setup
37
+
38
+ On your first session after installing, Claude will notice that `~/.ccrecall/config.json` doesn't exist and walk you through a brief onboarding. It asks a single question — **session context injection**: should Claude automatically recall what you were working on last session?
39
+
40
+ Your choice gets written to `~/.ccrecall/config.json`. You can edit that file directly at any time to change settings.
41
+
42
+ To skip the walkthrough and use recommended defaults immediately:
43
+
44
+ ```bash
45
+ ccrecall write-config --defaults
46
+ ```
47
+
48
+ ## Semantic search
49
+
50
+ Search results are fused from two signals: keyword full-text search (FTS5 → FTS4 → LIKE fallback) and vector similarity from a locally-running embedding model. The two ranked lists are merged with Reciprocal Rank Fusion (RRF), so results that rank well in both signals appear first.
51
+
52
+ The embedding model is [jina-embeddings-v2-small-en](https://huggingface.co/jinaai/jina-embeddings-v2-small-en) (512-dim), running entirely on your machine via [fastembed](https://github.com/qdrant/fastembed). No data leaves your machine.
53
+
54
+ ### Coverage
55
+
56
+ New sessions are embedded automatically as they sync (embed-on-write), so coverage builds forward on its own. Only **active-leaf** branches are embedded — at most one active leaf per session (maintained by sync/import), not its abandoned forks/retries. The flag isn't DB-enforced, but sync/import marks exactly one branch `is_active=1` per session. The search path only ever returns active leaves, so embedding inactive forks would just produce vectors that can never surface.
57
+
58
+ ### Optional: seed historical conversations
59
+
60
+ Embedding runs on CPU via fastembed. jina-v2-small-en is light — a few milliseconds for a short summary, up to ~400ms for a long one — but seeding a large history (~2k active leaves) is still a bounded chunk of work, and a parallel run can thrash a small or shared box. It is therefore **opt-in** — it is *not* auto-spawned on SessionStart — so it never fires unbidden. Run it yourself when you want to seed:
61
+
62
+ ```bash
63
+ ccrecall backfill embeddings # all active leaves, all history
64
+ ccrecall backfill embeddings --days 14 # only the last 14 days
65
+ ccrecall backfill embeddings --limit 500 # cap this run at 500 branches
66
+ ccrecall backfill embeddings --threads 4 # use 4 inference threads (idle machine)
67
+ ```
68
+
69
+ It runs at low scheduling priority (`nice`) and a single inference thread by default so it yields to interactive work. Tune the thread count with `--threads` (e.g. `--threads 4` on an idle workstation to finish faster). Progress prints to stderr (one line per batch); the run is resumable — re-running skips already-embedded branches.
70
+
71
+ ### Flags
72
+
73
+ | Flag | Effect |
74
+ |------|--------|
75
+ | `--keyword-only` | Skip the embedding step entirely, use keyword search only |
76
+ | `--status` | Print diagnostic info (vec extension loaded, model name, embedded vs. total summarized (embeddable) branch count) and exit 0 |
77
+
78
+ ### Runtime deps
79
+
80
+ The semantic search path requires three extra packages beyond the base install:
81
+
82
+ - `sqlite-vec` — SQLite extension for vector KNN queries
83
+ - `fastembed` — downloads and runs the embedding model (manages onnxruntime + tokenization)
84
+ - `numpy` — vector math (normalization)
85
+
86
+ These are included in the package dependencies. If fastembed fails to import (e.g. ABI mismatch on an unusual platform), search falls back silently to keyword-only mode.
87
+
88
+ ### Degradation
89
+
90
+ Semantic fusion is automatically disabled when:
91
+ - The embedding model can't be loaded (e.g. a first-run download failed and no cached copy exists)
92
+ - `fastembed` cannot be imported
93
+ - `sqlite-vec` cannot be loaded on the connection (e.g. Python built without loadable extensions)
94
+
95
+ In all cases, search falls back to keyword-only and returns results normally. Use `ccrecall search --status` to check which path is active.
96
+
97
+ ## Skills
98
+
99
+ | Skill | Trigger | What it does |
100
+ |---|---|---|
101
+ | `/ccr-recall` | "what did we discuss", "continue where we left off", "search my conversations" | Lets Claude search or browse your past sessions on demand |
102
+ | `/ccr-resume` | "pick up where we left off after /clear", a stop, or an unanswered question | Reconstructs the prior session's intent from its transcript tail and surfaces any unresolved decision |
103
+ | `/ccr-tokens` | "analyze Claude token usage", "how much am I spending on Claude" | Full cost + workflow analytics report with an interactive HTML dashboard |
104
+
105
+ ## Entry points
106
+
107
+ ### Hooks (run automatically — don't call these manually)
108
+
109
+ These are wired by the plugin's `hooks/hooks.json` and fire on their respective Claude Code events.
110
+
111
+ | Entry point | Event | What it does |
112
+ |---|---|---|
113
+ | `ccrecall-setup` | SessionStart | Creates `~/.ccrecall/` if needed, opens the DB to apply any pending migrations, then spawns `ccrecall import` and `ccrecall backfill summaries` as background processes |
114
+ | `ccrecall-onboarding` | SessionStart (startup only) | One-time first-run onboarding. Injects setup instructions into Claude's context if `config.json` is missing or onboarding hasn't been completed. Silent no-op after that |
115
+ | `ccrecall-context` | SessionStart (startup + clear) | Injects a summary of your most recent session into Claude's context so it knows what you were working on. On `/clear`, reads a handoff file to link directly to the session you just cleared from |
116
+ | `ccrecall-clear-handoff` | SessionEnd (clear only) | Writes a small handoff file so the next session start knows which session to link to after a `/clear`. Without this, context injection falls back to a "most recent session" heuristic |
117
+ | `ccrecall-sync` | Stop | Syncs the current session to the DB in a detached background process. Runs on every session end |
118
+
119
+ > These are kept as separate console scripts (rather than `ccrecall hook …` subcommands) on purpose: hooks fire on every session boundary, and a direct entry point avoids eagerly importing the full CLI command surface on the hot path.
120
+
121
+ ### Internal helpers (spawned by hooks — don't call these manually)
122
+
123
+ | Entry point | What it does |
124
+ |---|---|
125
+ | `ccrecall sync-current` | Syncs a single session file to the DB. Called by `ccrecall-sync` with the session ID from stdin |
126
+ | `ccrecall import` | Full import of all JSONL files in `~/.claude/projects/`. Skips files that haven't changed since last import (file hash check). Run on first install and whenever new sessions need backfilling |
127
+ | `ccrecall backfill summaries` | Generates context summaries for any DB branches that don't have one yet. Runs in the background after `ccrecall-setup` |
128
+ | `ccrecall write-config` | Writes `~/.ccrecall/config.json`. Called by Claude during onboarding to persist your settings choices. You can also call it directly — run `ccrecall write-config --help` for flags |
129
+
130
+ ### Skill CLIs (called from skill files — can also be used directly)
131
+
132
+ These are the `ccrecall` subcommands the `/ccr-*` skills invoke. You can run them from the terminal too.
133
+
134
+ | Entry point | What it does |
135
+ |---|---|
136
+ | `ccrecall recent` | Prints recent sessions from the DB in markdown (default) or JSON. Used by `/ccr-recall` |
137
+ | `ccrecall search` | Searches sessions by keyword fused with vector similarity (FTS5 → FTS4 → LIKE fallback, RRF-fused with jina embeddings when available). Used by `/ccr-recall` |
138
+ | `ccrecall tail` | Reads the tail of a prior session's transcript to recover the last instruction and any unanswered question. Used by `/ccr-resume` |
139
+ | `ccrecall backfill embeddings` | Opt-in seeding of embeddings for historical active-leaf branches (jina-v2-small-en via fastembed). Not auto-spawned. Supports `--days N` / `--limit N` / `--threads N`; throttled via `nice` + a single inference thread by default. Resumable |
140
+ | `ccrecall tokens` | Parses JSONL files for token usage analytics — cost, cache hits, model mix, skill/agent/hook patterns. Populates analytics tables and builds `~/.ccrecall/dashboard.html`. Used by `/ccr-tokens` |
141
+
142
+ ## Data flow
143
+
144
+ ```
145
+ Session ends
146
+ └─ ccrecall-sync (Stop hook)
147
+ └─ ccrecall sync-current (background)
148
+ └─ writes to ~/.ccrecall/conversations.db
149
+ └─ embeds the active leaf via jina if model available (drops silently on failure)
150
+
151
+ /clear (SessionEnd)
152
+ └─ ccrecall-clear-handoff
153
+ └─ writes a handoff file naming the session being cleared
154
+ (so the next SessionStart links to it instead of guessing)
155
+
156
+ Session starts
157
+ └─ ccrecall-setup (SessionStart)
158
+ │ └─ ccrecall import (background, first run / new files)
159
+ │ └─ embeds each new active leaf via jina if model available
160
+ │ └─ ccrecall backfill summaries (background, if summaries missing)
161
+ │ └─ (embedding backfill is NOT auto-spawned — opt-in via ccrecall backfill embeddings)
162
+ ├─ ccrecall-onboarding (SessionStart, startup only — one-time)
163
+ └─ ccrecall-context (SessionStart, startup + clear)
164
+ └─ injects last session summary into Claude's context
165
+ ```
166
+
167
+ ## Config file
168
+
169
+ `~/.ccrecall/config.json` — written by `ccrecall write-config` during onboarding:
170
+
171
+ ```json
172
+ {
173
+ "onboarding_completed": true,
174
+ "onboarding_version": 1,
175
+ "auto_inject_context": true
176
+ }
177
+ ```
178
+
179
+ Onboarding sets `auto_inject_context`. The remaining settings are tunable by editing `config.json` directly:
180
+
181
+ | Key | Type | Default | Effect |
182
+ |---|---|---|---|
183
+ | `auto_inject_context` | bool | `true` | Inject a summary of your previous session at session start. |
184
+ | `max_context_sessions` | int | `2` | How many recent sessions to include in that injected context. |
185
+ | `exclude_projects` | list[str] | `[]` | Project names to skip when **storing** conversations — excluded projects are not imported or synced. Matched against the project's directory name. This is write-side only: it prevents new data from being indexed; it does not remove or hide conversations already stored before the project was excluded. |
186
+ | `logging_enabled` | bool | `false` | Write hook diagnostics (including swallowed hook exceptions) to `~/.ccrecall/ccrecall.log`. Useful for troubleshooting a misbehaving hook. |
187
+
188
+ ## Database
189
+
190
+ `~/.ccrecall/conversations.db` — SQLite, WAL mode. Tables:
191
+
192
+ - `sessions` — one row per conversation session
193
+ - `branches` — one row per conversation branch (rewinding creates new branches)
194
+ - `messages` — all messages, stored once per session regardless of branch
195
+ - `branch_messages` — join table linking messages to branches
196
+ - `import_log` — tracks which JSONL files have been imported and their hashes
197
+ - `branch_vec` — vec0 virtual table (sqlite-vec) storing 512-dim jina embeddings for each branch, used for KNN search
198
+ - `token_snapshots`, `turns`, `turn_tool_calls`, `session_metrics` — analytics tables populated by `ccrecall tokens`
199
+
200
+ ## Development
201
+
202
+ ```bash
203
+ uv sync # install package + dev dependencies
204
+ uv run pytest # run the test suite
205
+ uvx prek run --all-files # run the lint/format/type hooks
206
+ ```
207
+
208
+ ## License
209
+
210
+ [MIT](LICENSE)
@@ -0,0 +1,113 @@
1
+ [build-system]
2
+ requires = ["setuptools>=80", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ccrecall"
7
+ version = "0.10.0"
8
+ description = "Conversation history and semantic search for Claude Code"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Jessica Smith", email = "12jessicasmith34@gmail.com" }]
14
+ keywords = [
15
+ "claude-code",
16
+ "conversation-history",
17
+ "search",
18
+ "recall",
19
+ "transcripts",
20
+ "semantic-search",
21
+ ]
22
+ classifiers = [
23
+ "Development Status :: 4 - Beta",
24
+ "Environment :: Console",
25
+ "Intended Audience :: Developers",
26
+ "Operating System :: OS Independent",
27
+ "Programming Language :: Python :: 3",
28
+ "Programming Language :: Python :: 3.10",
29
+ "Programming Language :: Python :: 3.11",
30
+ "Programming Language :: Python :: 3.12",
31
+ "Programming Language :: Python :: 3.13",
32
+ "Programming Language :: Python :: 3.14",
33
+ "Topic :: Utilities",
34
+ "Topic :: Software Development :: Libraries :: Python Modules",
35
+ ]
36
+ # Pinned to the versions in uv.lock: `uv tool install` without a lockfile
37
+ # ignores uv.lock and resolves fresh, so an unbounded range would pull newer
38
+ # ABI/runtime-breaking releases of these native wheels.
39
+ # fastembed is the embedding engine — it owns the ONNX runtime + tokenizers and
40
+ # the model download, so onnxruntime is left to fastembed's own constraint (an
41
+ # explicit pin here fights its resolver) and reproducibility comes from uv.lock.
42
+ # numpy is a direct dependency and uses Python-version markers. To re-sync after
43
+ # a bump: `uv lock` then mirror the resolved versions here.
44
+ dependencies = [
45
+ "sqlite-vec==0.1.9",
46
+ "fastembed==0.8.0",
47
+ "numpy==2.2.6; python_version < '3.11'",
48
+ "numpy==2.4.6; python_version >= '3.11'",
49
+ "whenever==0.10.0",
50
+ # Pin pydantic exactly so its native (Rust) pydantic-core dep resolves to a
51
+ # known ABI — same rationale as the native wheels above.
52
+ "pydantic==2.13.4",
53
+ "cyclopts>=4.16",
54
+ ]
55
+
56
+ [project.urls]
57
+ Homepage = "https://github.com/NodeJSmith/claude-code-recall"
58
+ Issues = "https://github.com/NodeJSmith/claude-code-recall/issues"
59
+ Changelog = "https://github.com/NodeJSmith/claude-code-recall/blob/main/CHANGELOG.md"
60
+
61
+ [project.scripts]
62
+ # Unified CLI — single entry point. Subcommands replace the former skill-facing
63
+ # and helper cm-* tools.
64
+ ccrecall = "ccrecall.cli:main"
65
+ # Hook entry points (wired by the plugin's hooks/hooks.json). Kept as separate
66
+ # console scripts rather than `ccrecall hook ...` subcommands on purpose: hooks
67
+ # fire on every SessionStart/Stop, and routing through the full cyclopts app
68
+ # eager-imports the whole command surface (fastembed/numpy/onnxruntime, ~1800ms)
69
+ # vs ~440ms for a direct hook import. The no-lazy-imports rule rules out dodging
70
+ # that, so a direct entry point per hook stays the fast path.
71
+ ccrecall-setup = "ccrecall.hooks.memory_setup:main"
72
+ ccrecall-sync = "ccrecall.hooks.memory_sync:main"
73
+ ccrecall-context = "ccrecall.hooks.memory_context:main"
74
+ ccrecall-onboarding = "ccrecall.hooks.onboarding:main"
75
+ ccrecall-clear-handoff = "ccrecall.hooks.clear_handoff:main"
76
+
77
+ [tool.setuptools.packages.find]
78
+ where = ["src"]
79
+
80
+ [dependency-groups]
81
+ dev = [
82
+ "pytest>=8.0",
83
+ "hypothesis>=6.0",
84
+ "pytest-cov>=7.0.0",
85
+ "coverage[toml]>=7.10.7",
86
+ ]
87
+
88
+ [tool.pytest.ini_options]
89
+ testpaths = ["tests"]
90
+
91
+ [tool.coverage.run]
92
+ branch = true # measure branch coverage, not just lines
93
+ source = ["ccrecall"]
94
+ parallel = true # suffix data files so per-Python-version CI shards combine cleanly
95
+ relative_files = true # stable paths across CI runners
96
+ omit = ["*/__init__.py", "*/__main__.py"]
97
+
98
+ [tool.coverage.report]
99
+ show_missing = true
100
+ skip_covered = true
101
+ # fail_under = 80 # enable once a baseline is established; CI can override
102
+ exclude_lines = [
103
+ "pragma: no cover",
104
+ "if TYPE_CHECKING:",
105
+ "if __name__ == .__main__.:",
106
+ "raise NotImplementedError",
107
+ ]
108
+
109
+ [tool.coverage.html]
110
+ directory = "htmlcov"
111
+
112
+ [tool.coverage.xml]
113
+ output = "coverage.xml"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,18 @@
1
+ """
2
+ ccrecall — conversation memory package for Claude Code.
3
+
4
+ Submodules:
5
+ db — Database connection, config/settings, vec operations, logging
6
+ schema — Conversation DB schema constants (SCHEMA_*) and FTS detection
7
+ content — Message content extraction and tool detection
8
+ parsing — JSONL parsing, branch detection, metadata extraction
9
+ formatting — Session formatting, time/path utilities
10
+ project_ops — Shared project upsert logic (cwd strategy + JSONL-probe strategy)
11
+ session_ops — Shared session import logic (used by sync and import pipelines)
12
+ token_schema — Token ingest schema definitions, ensure_schema(), version management
13
+ token_parser — Token JSONL parsing, data classes, session parsing, file discovery
14
+ token_analytics — Session import and token_snapshots backfill
15
+ token_output — Dashboard JSON output assembly (chart queries)
16
+ token_insights — Trend analysis, insight generation, findings/recommendations
17
+ token_dashboard — Token dashboard deployment and main() entry point
18
+ """