claude_memory 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/.mind.mv2.o2N83S +0 -0
  3. data/.claude/CLAUDE.md +1 -0
  4. data/.claude/rules/claude_memory.generated.md +28 -9
  5. data/.claude/settings.local.json +9 -1
  6. data/.claude/skills/check-memory/SKILL.md +77 -0
  7. data/.claude/skills/improve/SKILL.md +532 -0
  8. data/.claude/skills/improve/feature-patterns.md +1221 -0
  9. data/.claude/skills/quality-update/SKILL.md +229 -0
  10. data/.claude/skills/quality-update/implementation-guide.md +346 -0
  11. data/.claude/skills/review-commit/SKILL.md +199 -0
  12. data/.claude/skills/review-for-quality/SKILL.md +154 -0
  13. data/.claude/skills/review-for-quality/expert-checklists.md +79 -0
  14. data/.claude/skills/setup-memory/SKILL.md +168 -0
  15. data/.claude/skills/study-repo/SKILL.md +307 -0
  16. data/.claude/skills/study-repo/analysis-template.md +323 -0
  17. data/.claude/skills/study-repo/focus-examples.md +327 -0
  18. data/CHANGELOG.md +133 -0
  19. data/CLAUDE.md +130 -11
  20. data/README.md +117 -10
  21. data/db/migrations/001_create_initial_schema.rb +117 -0
  22. data/db/migrations/002_add_project_scoping.rb +33 -0
  23. data/db/migrations/003_add_session_metadata.rb +42 -0
  24. data/db/migrations/004_add_fact_embeddings.rb +20 -0
  25. data/db/migrations/005_add_incremental_sync.rb +21 -0
  26. data/db/migrations/006_add_operation_tracking.rb +40 -0
  27. data/db/migrations/007_add_ingestion_metrics.rb +26 -0
  28. data/docs/.claude/mind.mv2.lock +0 -0
  29. data/docs/GETTING_STARTED.md +587 -0
  30. data/docs/RELEASE_NOTES_v0.2.0.md +0 -1
  31. data/docs/RUBY_COMMUNITY_POST_v0.2.0.md +0 -2
  32. data/docs/architecture.md +9 -8
  33. data/docs/auto_init_design.md +230 -0
  34. data/docs/improvements.md +557 -731
  35. data/docs/influence/.gitkeep +13 -0
  36. data/docs/influence/grepai.md +933 -0
  37. data/docs/influence/qmd.md +2195 -0
  38. data/docs/plugin.md +257 -11
  39. data/docs/quality_review.md +472 -1273
  40. data/docs/remaining_improvements.md +330 -0
  41. data/lefthook.yml +13 -0
  42. data/lib/claude_memory/commands/checks/claude_md_check.rb +41 -0
  43. data/lib/claude_memory/commands/checks/database_check.rb +120 -0
  44. data/lib/claude_memory/commands/checks/hooks_check.rb +112 -0
  45. data/lib/claude_memory/commands/checks/reporter.rb +110 -0
  46. data/lib/claude_memory/commands/checks/snapshot_check.rb +30 -0
  47. data/lib/claude_memory/commands/doctor_command.rb +12 -129
  48. data/lib/claude_memory/commands/help_command.rb +1 -0
  49. data/lib/claude_memory/commands/hook_command.rb +9 -2
  50. data/lib/claude_memory/commands/index_command.rb +169 -0
  51. data/lib/claude_memory/commands/ingest_command.rb +1 -1
  52. data/lib/claude_memory/commands/init_command.rb +5 -197
  53. data/lib/claude_memory/commands/initializers/database_ensurer.rb +30 -0
  54. data/lib/claude_memory/commands/initializers/global_initializer.rb +85 -0
  55. data/lib/claude_memory/commands/initializers/hooks_configurator.rb +156 -0
  56. data/lib/claude_memory/commands/initializers/mcp_configurator.rb +56 -0
  57. data/lib/claude_memory/commands/initializers/memory_instructions_writer.rb +135 -0
  58. data/lib/claude_memory/commands/initializers/project_initializer.rb +111 -0
  59. data/lib/claude_memory/commands/recover_command.rb +75 -0
  60. data/lib/claude_memory/commands/registry.rb +5 -1
  61. data/lib/claude_memory/commands/stats_command.rb +239 -0
  62. data/lib/claude_memory/commands/uninstall_command.rb +226 -0
  63. data/lib/claude_memory/core/batch_loader.rb +32 -0
  64. data/lib/claude_memory/core/concept_ranker.rb +73 -0
  65. data/lib/claude_memory/core/embedding_candidate_builder.rb +37 -0
  66. data/lib/claude_memory/core/fact_collector.rb +51 -0
  67. data/lib/claude_memory/core/fact_query_builder.rb +154 -0
  68. data/lib/claude_memory/core/fact_ranker.rb +113 -0
  69. data/lib/claude_memory/core/result_builder.rb +54 -0
  70. data/lib/claude_memory/core/result_sorter.rb +25 -0
  71. data/lib/claude_memory/core/scope_filter.rb +61 -0
  72. data/lib/claude_memory/core/text_builder.rb +29 -0
  73. data/lib/claude_memory/embeddings/generator.rb +161 -0
  74. data/lib/claude_memory/embeddings/similarity.rb +69 -0
  75. data/lib/claude_memory/hook/handler.rb +4 -3
  76. data/lib/claude_memory/index/lexical_fts.rb +7 -2
  77. data/lib/claude_memory/infrastructure/operation_tracker.rb +158 -0
  78. data/lib/claude_memory/infrastructure/schema_validator.rb +206 -0
  79. data/lib/claude_memory/ingest/content_sanitizer.rb +6 -7
  80. data/lib/claude_memory/ingest/ingester.rb +99 -15
  81. data/lib/claude_memory/ingest/metadata_extractor.rb +57 -0
  82. data/lib/claude_memory/ingest/tool_extractor.rb +71 -0
  83. data/lib/claude_memory/mcp/response_formatter.rb +331 -0
  84. data/lib/claude_memory/mcp/server.rb +19 -0
  85. data/lib/claude_memory/mcp/setup_status_analyzer.rb +73 -0
  86. data/lib/claude_memory/mcp/tool_definitions.rb +279 -0
  87. data/lib/claude_memory/mcp/tool_helpers.rb +80 -0
  88. data/lib/claude_memory/mcp/tools.rb +330 -320
  89. data/lib/claude_memory/recall/dual_query_template.rb +63 -0
  90. data/lib/claude_memory/recall.rb +304 -237
  91. data/lib/claude_memory/resolve/resolver.rb +52 -49
  92. data/lib/claude_memory/store/sqlite_store.rb +210 -144
  93. data/lib/claude_memory/store/store_manager.rb +6 -6
  94. data/lib/claude_memory/sweep/sweeper.rb +6 -0
  95. data/lib/claude_memory/version.rb +1 -1
  96. data/lib/claude_memory.rb +35 -3
  97. metadata +71 -11
  98. data/.claude/.mind.mv2.aLCUZd +0 -0
  99. data/.claude/memory.sqlite3 +0 -0
  100. data/.mcp.json +0 -11
  101. /data/docs/{feature_adoption_plan.md → plans/feature_adoption_plan.md} +0 -0
  102. /data/docs/{feature_adoption_plan_revised.md → plans/feature_adoption_plan_revised.md} +0 -0
  103. /data/docs/{plan.md → plans/plan.md} +0 -0
  104. /data/docs/{updated_plan.md → plans/updated_plan.md} +0 -0
@@ -0,0 +1,933 @@
1
+ # grepai Analysis
2
+
3
+ *Analysis Date: 2026-01-29*
4
+ *Repository: https://github.com/yoanbernabeu/grepai*
5
+ *Version/Commit: HEAD (main branch)*
6
+
7
+ ---
8
+
9
+ ## Executive Summary
10
+
11
+ ### Project Purpose
12
+ grepai is a privacy-first CLI tool for semantic code search using vector embeddings, enabling natural language queries that find relevant code based on intent rather than exact text matches.
13
+
14
+ ### Key Innovation
15
+ **Semantic search with zero cloud dependency** - Combines local vector embeddings (via Ollama) with file-watching for real-time index updates, drastically reducing AI agent token usage by ~80% through intelligent context retrieval instead of full codebase scans.
16
+
17
+ ### Technology Stack
18
+ | Component | Technology |
19
+ |-----------|-----------|
20
+ | **Language** | Go 1.22+ |
21
+ | **Vector Store** | PostgreSQL with pgvector, Qdrant, or GOB (file-based) |
22
+ | **Embedding** | Ollama (local), OpenAI, LM Studio |
23
+ | **File Watching** | fsnotify |
24
+ | **CLI Framework** | cobra |
25
+ | **MCP Integration** | mark3labs/mcp-go |
26
+ | **Call Graph** | tree-sitter for AST parsing |
27
+ | **Testing** | Go stdlib testing with race detection |
28
+ | **CI/CD** | GitHub Actions (multi-OS, cross-compile) |
29
+
30
+ ### Production Readiness
31
+ - **Maturity**: Production-ready (actively developed, popular on ProductHunt)
32
+ - **Test Coverage**: Comprehensive with race detection enabled
33
+ - **Documentation**: Excellent (dedicated docs site, blog, examples)
34
+ - **Distribution**: Homebrew, shell installers, multi-platform binaries
35
+ - **Community**: Active development, ~280K views on Reddit
36
+ - **Performance**: Designed for efficiency (compact JSON, batching, debouncing)
37
+
38
+ ---
39
+
40
+ ## Architecture Overview
41
+
42
+ ### Data Model
43
+
44
+ **Two-Phase Indexing**:
45
+
46
+ 1. **Vector Index** (semantic search)
47
+ - **Chunk**: Code segment with vector embedding
48
+ - Fields: ID, FilePath, StartLine, EndLine, Content, Vector, Hash, UpdatedAt
49
+ - Chunk size: 512 tokens with 50-token overlap
50
+ - Character-based chunking (handles minified files)
51
+ - **Document**: File metadata tracking chunks
52
+ - Fields: Path, Hash, ModTime, ChunkIDs
53
+
54
+ 2. **Symbol Index** (call graph tracing)
55
+ - **Symbol**: Function/method/class definitions
56
+ - Fields: Name, Kind, File, Line, Signature, Receiver, Package, Exported
57
+ - **Reference**: Symbol usage/call sites
58
+ - Fields: SymbolName, File, Line, Context, CallerName, CallerFile
59
+ - **CallEdge**: Caller → Callee relationships for graph traversal
60
+
61
+ **Storage Backends** (pluggable via `VectorStore` interface):
62
+ - GOB: File-based (`.grepai/index.gob`)
63
+ - PostgreSQL: pgvector extension for similarity search
64
+ - Qdrant: Dedicated vector database
65
+
66
+ ### Design Patterns
67
+
68
+ 1. **Interface-Based Extensibility**
69
+ - `Embedder` interface (embedder/embedder.go:6): Pluggable embedding providers
70
+ - `VectorStore` interface (store/store.go:50): Pluggable storage backends
71
+ - `SymbolExtractor` interface (trace/trace.go:113): Pluggable language parsers
72
+
73
+ 2. **Context-Aware Operations**
74
+ - All I/O operations accept `context.Context` for cancellation
75
+ - Example: `SaveChunks(ctx context.Context, chunks []Chunk) error`
76
+
77
+ 3. **Batch Processing with Progress Callbacks**
78
+ - `BatchEmbedder` interface (embedder/embedder.go:29): Parallel batch embedding
79
+ - Progress callback: `func(batchIndex, totalBatches, completedChunks, totalChunks, retrying, attempt, statusCode)`
80
+
81
+ 4. **Debounced File Watching**
82
+ - Aggregates rapid file changes before triggering re-indexing
83
+ - Prevents index thrashing during bulk operations (git checkout, etc.)
84
+
85
+ 5. **Compact JSON Mode**
86
+ - MCP tools support `--compact` flag to omit content (~80% token reduction)
87
+ - Returns only file:line references, not full code chunks
88
+
89
+ ### Module Organization
90
+
91
+ ```
92
+ grepai/
93
+ ├── cmd/
94
+ │ └── grepai/ # CLI entry point
95
+ ├── cli/ # Command implementations (init, watch, search, trace)
96
+ ├── embedder/ # Embedding providers (Ollama, OpenAI, LMStudio)
97
+ ├── indexer/ # Scanner, chunker, indexer orchestration
98
+ ├── store/ # Vector storage backends (GOB, Postgres, Qdrant)
99
+ ├── search/ # Semantic + hybrid search
100
+ ├── trace/ # Symbol extraction, call graph (tree-sitter based)
101
+ ├── watcher/ # File watching with debouncing
102
+ ├── daemon/ # Background indexing daemon
103
+ ├── config/ # YAML configuration management
104
+ ├── updater/ # Self-update mechanism
105
+ └── mcp/ # MCP server (5 tools)
106
+ ```
107
+
108
+ **Entry Point Flow**:
109
+ ```
110
+ cmd/grepai/main.go → cli/root.go → cli/{init,watch,search,trace}.go
111
+ ```
112
+
113
+ **Data Flow**:
114
+ ```
115
+ FileSystem → Scanner (respects .gitignore)
116
+ → Chunker (overlapping chunks)
117
+ → Embedder (batch API calls)
118
+ → VectorStore (persist with hash-based deduplication)
119
+
120
+ Query → Embedder (query vector)
121
+ → VectorStore.Search (cosine similarity)
122
+ → Results (sorted by score)
123
+ ```
124
+
125
+ ### Comparison with ClaudeMemory
126
+
127
+ | Aspect | grepai | ClaudeMemory |
128
+ |--------|--------|--------------|
129
+ | **Language** | Go (compiled, fast) | Ruby (interpreted) |
130
+ | **Primary Use** | Semantic code search | Long-term AI memory |
131
+ | **Data Model** | Chunks + Symbols | Facts + Provenance |
132
+ | **Storage** | Vector DB (pgvector/Qdrant/GOB) | SQLite (facts, no vectors) |
133
+ | **Search** | Vector similarity + hybrid | Full-text search (FTS5) |
134
+ | **Indexing** | Automatic (file watcher) | Manual (transcript ingest) |
135
+ | **MCP Tools** | 5 (search, trace, status) | 8+ (recall, explain, promote) |
136
+ | **Scope** | Project-local or workspace | Global + Project dual-DB |
137
+ | **Truth Maintenance** | Hash-based deduplication | Supersession + conflict resolution |
138
+ | **Performance** | Optimized for speed (Go, batching) | Optimized for accuracy (resolution) |
139
+ | **Distribution** | Standalone CLI (Homebrew) | Ruby gem |
140
+ | **Testing** | Go stdlib + race detection | RSpec with mocks |
141
+ | **UI** | CLI with bubbletea (TUI) | Pure CLI |
142
+ | **Call Graph** | Yes (tree-sitter AST) | No |
143
+ | **File Watching** | Yes (fsnotify) | No (transcript-driven) |
144
+
145
+ **Key Architectural Difference**: grepai is **proactive** (watches files, updates index automatically) while ClaudeMemory is **reactive** (responds to transcript events via hooks).
146
+
147
+ ---
148
+
149
+ ## Key Components Deep-Dive
150
+
151
+ ### Component 1: Chunking Strategy
152
+
153
+ **Purpose**: Split code into overlapping chunks optimized for embedding
154
+
155
+ **Implementation** (indexer/chunker.go:47-100):
156
+ ```go
157
+ // Character-based chunking (not line-based) handles minified files
158
+ maxChars := c.chunkSize * CharsPerToken // 512 tokens * 4 chars = 2048 chars
159
+ overlapChars := c.overlap * CharsPerToken // 50 tokens * 4 chars = 200 chars
160
+
161
+ for pos < len(content) {
162
+ end := pos + maxChars
163
+ // Try to break at newline for cleaner chunks
164
+ if end < len(content) {
165
+ lastNewline := strings.LastIndex(content[pos:end], "\n")
166
+ if lastNewline > 0 {
167
+ end = pos + lastNewline + 1
168
+ }
169
+ }
170
+
171
+ // Calculate line numbers using pre-built index
172
+ startLine := getLineNumber(lineStarts, pos)
173
+ endLine := getLineNumber(lineStarts, end-1)
174
+
175
+ chunks = append(chunks, ChunkInfo{
176
+ ID: fmt.Sprintf("%s_%d", filePath, chunkIndex),
177
+ FilePath: filePath,
178
+ StartLine: startLine,
179
+ EndLine: endLine,
180
+ Content: chunkContent,
181
+ Hash: sha256Hash, // For deduplication
182
+ })
183
+
184
+ pos = end - overlapChars // Overlap for context continuity
185
+ }
186
+ ```
187
+
188
+ **Design Decisions**:
189
+ - **Character-based**: Handles minified files with very long lines
190
+ - **Overlap**: 50 tokens ensure context continuity across chunks
191
+ - **Newline breaking**: Prefers natural boundaries when possible
192
+ - **Line number mapping**: Pre-build index for O(log n) lookups
193
+ - **Hash-based deduplication**: Skip re-embedding unchanged chunks
194
+
195
+ **Performance**: ~4 chars per token is empirically validated for code.
196
+
197
+ ---
198
+
199
+ ### Component 2: File Watcher with Debouncing
200
+
201
+ **Purpose**: Incrementally update index on file changes without thrashing
202
+
203
+ **Implementation** (watcher/watcher.go:30-100):
204
+ ```go
205
+ type Watcher struct {
206
+ pending map[string]FileEvent // Aggregates rapid changes
207
+ pendingMu sync.Mutex // Thread-safe updates
208
+ timer *time.Timer // Debounce timer
209
+ }
210
+
211
+ func (w *Watcher) processEvents(ctx context.Context) {
212
+ for {
213
+ select {
214
+ case event := <-w.watcher.Events:
215
+ w.pendingMu.Lock()
216
+
217
+ // Aggregate events (overwrites older events for same file)
218
+ w.pending[relPath] = FileEvent{Type: eventType, Path: relPath}
219
+
220
+ // Reset debounce timer
221
+ if w.timer != nil {
222
+ w.timer.Stop()
223
+ }
224
+ w.timer = time.AfterFunc(time.Duration(w.debounceMs)*time.Millisecond,
225
+ w.flushPending)
226
+
227
+ w.pendingMu.Unlock()
228
+ }
229
+ }
230
+ }
231
+ ```
232
+
233
+ **Design Decisions**:
234
+ - **Debouncing**: Default 300ms prevents index thrashing during bulk operations
235
+ - **Event aggregation**: Multiple edits to same file collapse to single update
236
+ - **Recursive watching**: Watches all subdirectories automatically
237
+ - **gitignore respect**: Uses `sabhiram/go-gitignore` for filtering
238
+
239
+ **Use Case**: During `git checkout`, hundreds of files change simultaneously. Debouncing waits for changes to settle before re-indexing.
240
+
241
+ ---
242
+
243
+ ### Component 3: MCP Server Design
244
+
245
+ **Purpose**: Expose grepai as native tool for AI agents (Claude Code, Cursor, Windsurf)
246
+
247
+ **Implementation** (mcp/server.go:100-168):
248
+ ```go
249
+ func (s *Server) registerTools() {
250
+ // 1. grepai_search
251
+ searchTool := mcp.NewTool("grepai_search",
252
+ mcp.WithDescription("Semantic code search..."),
253
+ mcp.WithString("query", mcp.Required(), mcp.Description("Natural language query")),
254
+ mcp.WithNumber("limit", mcp.Description("Max results (default: 10)")),
255
+ mcp.WithBoolean("compact", mcp.Description("Omit content (~80% token savings)")),
256
+ mcp.WithString("workspace", mcp.Description("Cross-project search")),
257
+ mcp.WithString("projects", mcp.Description("Filter by project names")),
258
+ )
259
+ s.mcpServer.AddTool(searchTool, s.handleSearch)
260
+
261
+ // 2. grepai_trace_callers - Find who calls a function
262
+ // 3. grepai_trace_callees - Find what a function calls
263
+ // 4. grepai_trace_graph - Build full call graph
264
+ // 5. grepai_index_status - Health check
265
+ }
266
+
267
+ func (s *Server) handleSearch(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
268
+ query := request.RequireString("query")
269
+ limit := request.GetInt("limit", 10)
270
+ compact := request.GetBool("compact", false)
271
+
272
+ // Initialize embedder + store
273
+ emb := s.createEmbedder(cfg)
274
+ st := s.createStore(ctx, cfg)
275
+ defer emb.Close(); defer st.Close()
276
+
277
+ // Search
278
+ searcher := search.NewSearcher(st, emb, cfg.Search)
279
+ results := searcher.Search(ctx, query, limit)
280
+
281
+ // Return compact or full results
282
+ if compact {
283
+ return SearchResultCompact{FilePath, StartLine, EndLine, Score}
284
+ } else {
285
+ return SearchResult{FilePath, StartLine, EndLine, Score, Content}
286
+ }
287
+ }
288
+ ```
289
+
290
+ **Design Decisions**:
291
+ - **5 focused tools**: Search, trace callers, trace callees, trace graph, status
292
+ - **Compact mode**: Critical for token efficiency (~80% reduction)
293
+ - **Workspace support**: Search across multiple projects with shared embedder/store
294
+ - **Project filtering**: Comma-separated list filters workspace results
295
+ - **Error handling**: Returns `ToolResultError` instead of throwing exceptions
296
+ - **Resource cleanup**: Explicit `defer` for embedder/store cleanup
297
+
298
+ **Integration**: Works out-of-box with Claude Code, Cursor, Windsurf via stdio transport.
299
+
300
+ ---
301
+
302
+ ### Component 4: Call Graph via tree-sitter
303
+
304
+ **Purpose**: Static analysis for "find callers" and "find callees" without executing code
305
+
306
+ **Implementation** (trace/extractor.go):
307
+ - Uses tree-sitter parsers for Go, TypeScript, Python, JavaScript, C#, Java, Ruby, Rust
308
+ - Extracts symbols (function definitions) and references (function calls)
309
+ - Builds call graph: `Symbol → References → Callers/Callees`
310
+
311
+ **Symbol Extraction** (trace/extractor.go:115):
312
+ ```go
313
+ type Symbol struct {
314
+ Name string // Function/method name
315
+ Kind SymbolKind // function, method, class, interface
316
+ File string
317
+ Line int
318
+ Signature string // Full signature for disambiguation
319
+ Receiver string // For methods (e.g., "MyStruct")
320
+ Package string
321
+ Exported bool // Public vs private
322
+ Language string
323
+ }
324
+ ```
325
+
326
+ **Reference Extraction** (trace/trace.go:36):
327
+ ```go
328
+ type Reference struct {
329
+ SymbolName string // What's being called
330
+ File string // Where the call happens
331
+ Line int
332
+ Context string // Surrounding code for display
333
+ CallerName string // Who's making the call
334
+ CallerFile string // Where caller is defined
335
+ CallerLine int
336
+ }
337
+ ```
338
+
339
+ **Call Graph Query** (trace/store.go):
340
+ - `LookupCallers(symbolName)` → All references where symbol is called
341
+ - `LookupCallees(symbolName, file)` → All references within symbol's body
342
+ - `GetCallGraph(symbolName, depth)` → Multi-level traversal (BFS)
343
+
344
+ **Design Decisions**:
345
+ - **Fast mode**: tree-sitter AST parsing (no execution)
346
+ - **Language-agnostic**: Interface-based extractor per language
347
+ - **Context preservation**: Stores surrounding code for display
348
+ - **Disambiguation**: Uses signature + file for overloaded functions
349
+ - **Depth-limited**: Prevents exponential explosion in call graphs
350
+
351
+ ---
352
+
353
+ ### Component 5: Hybrid Search
354
+
355
+ **Purpose**: Combine vector similarity with keyword matching for better relevance
356
+
357
+ **Implementation** (search/search.go):
358
+ ```go
359
+ type HybridConfig struct {
360
+ Enabled bool // Enable hybrid search
361
+ K int // RRF constant (default: 60)
362
+ }
363
+
364
+ func (s *Searcher) Search(ctx context.Context, query string, limit int) ([]SearchResult, error) {
365
+ if !s.cfg.Hybrid.Enabled {
366
+ return s.vectorSearch(ctx, query, limit)
367
+ }
368
+
369
+ // 1. Vector search (semantic)
370
+ vectorResults := s.vectorSearch(ctx, query, limit*2)
371
+
372
+ // 2. Text search (keyword)
373
+ textResults := s.textSearch(ctx, query, limit*2)
374
+
375
+ // 3. Reciprocal Rank Fusion (RRF)
376
+ fusedResults := s.fuseResults(vectorResults, textResults)
377
+
378
+ return fusedResults[:limit]
379
+ }
380
+
381
+ func (s *Searcher) fuseResults(vec, text []Result) []Result {
382
+ scores := make(map[string]float32)
383
+ for rank, r := range vec {
384
+ scores[r.Chunk.ID] += 1.0 / float32(rank + s.cfg.Hybrid.K)
385
+ }
386
+ for rank, r := range text {
387
+ scores[r.Chunk.ID] += 1.0 / float32(rank + s.cfg.Hybrid.K)
388
+ }
389
+ // Sort by fused score
390
+ }
391
+ ```
392
+
393
+ **Design Decisions**:
394
+ - **RRF (Reciprocal Rank Fusion)**: Proven effective for combining ranked lists
395
+ - **K=60 default**: Standard RRF constant balancing vector vs text
396
+ - **2x over-fetch**: Retrieve more results before fusion to avoid missing relevant items
397
+ - **Configurable**: Can disable hybrid for pure semantic search
398
+
399
+ **Trade-off**: Slightly slower (2 searches + fusion) but significantly better relevance for queries with specific keywords.
400
+
401
+ ---
402
+
403
+ ### Component 6: Pluggable Storage Backends
404
+
405
+ **Purpose**: Support different deployment scenarios (local, team, cloud)
406
+
407
+ **Interface** (store/store.go:50):
408
+ ```go
409
+ type VectorStore interface {
410
+ SaveChunks(ctx context.Context, chunks []Chunk) error
411
+ DeleteByFile(ctx context.Context, filePath string) error
412
+ Search(ctx context.Context, queryVector []float32, limit int) ([]SearchResult, error)
413
+ GetDocument(ctx context.Context, filePath string) (*Document, error)
414
+ Load(ctx context.Context) error
415
+ Persist(ctx context.Context) error
416
+ Close() error
417
+ GetStats(ctx context.Context) (*IndexStats, error)
418
+ }
419
+ ```
420
+
421
+ **Implementations**:
422
+
423
+ 1. **GOB Store** (store/gob.go): File-based, single-project
424
+ - Storage: `.grepai/index.gob` (binary format)
425
+ - Search: In-memory cosine similarity (brute force)
426
+ - Use case: Individual developers, local projects
427
+ - Pros: Zero dependencies, fast for small codebases (<10K files)
428
+ - Cons: Memory usage grows with index size, no concurrent access
429
+
430
+ 2. **PostgreSQL Store** (store/postgres.go): Database-backed, multi-project
431
+ - Storage: Postgres with pgvector extension
432
+ - Search: `<=>` operator (optimized with IVFFlat index)
433
+ - Use case: Teams, shared index across projects
434
+ - Pros: Concurrent access, persistence, scalable
435
+ - Cons: Requires Postgres + pgvector setup
436
+
437
+ 3. **Qdrant Store** (store/qdrant.go): Dedicated vector DB
438
+ - Storage: Qdrant server (gRPC API)
439
+ - Search: Native vector search with HNSW indexing
440
+ - Use case: Large codebases (>50K files), production deployments
441
+ - Pros: Fastest search, advanced filtering, scalable
442
+ - Cons: Additional service to run
443
+
444
+ **Configuration** (.grepai/config.yaml):
445
+ ```yaml
446
+ store:
447
+ backend: "gob" # or "postgres" or "qdrant"
448
+ postgres:
449
+ dsn: "postgresql://user:pass@localhost/grepai"
450
+ qdrant:
451
+ endpoint: "localhost"
452
+ port: 6334
453
+ collection: "my-codebase"
454
+ ```
455
+
456
+ **Design Decisions**:
457
+ - **Same interface**: Swap backends without changing application code
458
+ - **Project ID scoping**: Multi-project support in Postgres/Qdrant
459
+ - **Hash-based deduplication**: All backends check chunk hash before embedding
460
+ - **Graceful degradation**: GOB works offline, Postgres/Qdrant require connectivity
461
+
462
+ ---
463
+
464
+ ## Comparative Analysis
465
+
466
+ ### What They Do Well
467
+
468
+ #### 1. **Incremental Indexing with File Watching**
469
+ - **Value**: Index stays fresh automatically without manual triggers
470
+ - **Evidence**: watcher/watcher.go:61 - `fsnotify` integration with debouncing
471
+ - **How**: Watches all directories recursively, respects gitignore, debounces rapid changes
472
+ - **Result**: Zero maintenance overhead for users, always up-to-date search results
473
+
474
+ #### 2. **Compact JSON Mode for Token Efficiency**
475
+ - **Value**: ~80% reduction in AI agent token usage
476
+ - **Evidence**: mcp/server.go:36 - `SearchResultCompact` omits content field
477
+ - **How**: MCP tools support `--compact` flag returning only file:line:score, not full chunks
478
+ - **Result**: Enables AI agents to search larger codebases without hitting context limits
479
+
480
+ #### 3. **Tree-sitter for Call Graph Analysis**
481
+ - **Value**: Language-agnostic static analysis without code execution
482
+ - **Evidence**: trace/extractor.go - Parsers for 8+ languages
483
+ - **How**: AST parsing extracts symbols + references, builds call graph
484
+ - **Result**: "Find callers" feature shows impact before refactoring
485
+
486
+ #### 4. **Interface-Based Extensibility**
487
+ - **Value**: Easy to add new embedders or storage backends
488
+ - **Evidence**: embedder/embedder.go:6, store/store.go:50
489
+ - **How**: Clean interfaces with multiple implementations (Ollama, OpenAI, LMStudio | GOB, Postgres, Qdrant)
490
+ - **Result**: Users choose deployment model (local, team, cloud) without code changes
491
+
492
+ #### 5. **Character-Based Chunking**
493
+ - **Value**: Handles minified files and long lines gracefully
494
+ - **Evidence**: indexer/chunker.go:54 - `maxChars := c.chunkSize * CharsPerToken`
495
+ - **How**: Splits by characters with newline preference, not by lines
496
+ - **Result**: No failures on minified JS/CSS, consistent chunk sizes
497
+
498
+ #### 6. **Hybrid Search (Vector + Text)**
499
+ - **Value**: Better relevance for queries with specific keywords
500
+ - **Evidence**: search/search.go - Reciprocal Rank Fusion (RRF)
501
+ - **How**: Combines cosine similarity with full-text search using RRF
502
+ - **Result**: Finds both semantically related and keyword-matched code
503
+
504
+ #### 7. **Multi-Platform Distribution**
505
+ - **Value**: Easy installation on Mac/Linux/Windows
506
+ - **Evidence**: .goreleaser.yml, install.sh, install.ps1, Homebrew tap
507
+ - **How**: GoReleaser builds cross-platform binaries, shell installers
508
+ - **Result**: Friction-free adoption (`brew install grepai`)
509
+
510
+ #### 8. **Workspace Mode**
511
+ - **Value**: Search across multiple projects simultaneously
512
+ - **Evidence**: mcp/server.go:252 - `handleWorkspaceSearch`
513
+ - **How**: Shared embedder + store with project filtering
514
+ - **Result**: Reuse patterns across related projects (microservices, monorepos)
515
+
516
+ ---
517
+
518
+ ### What We Do Well
519
+
520
+ #### 1. **Truth Maintenance with Conflict Resolution**
521
+ - **Our Advantage**: Fact supersession and conflict detection
522
+ - **Evidence**: resolve/resolver.rb - Determines equivalence, supersession, or conflicts
523
+ - **Value**: Maintains consistent knowledge base even with contradictory information
524
+ - **Why**: Memory requires long-term coherence; search can tolerate stale chunks
525
+
526
+ #### 2. **Dual-Database Scoping (Global + Project)**
527
+ - **Our Advantage**: User preferences (global) vs project-specific facts
528
+ - **Evidence**: store/store_manager.rb - Manages two SQLite connections
529
+ - **Value**: Facts apply at correct granularity (always vs this-project-only)
530
+ - **Why**: Memory has semantic scope; search is purely location-based
531
+
532
+ #### 3. **Provenance Tracking**
533
+ - **Our Advantage**: Every fact links to source transcript content
534
+ - **Evidence**: domain/provenance.rb - Links facts to content_items
535
+ - **Value**: Users can verify where facts came from, assess confidence
536
+ - **Why**: Memory requires trustworthiness; search assumes correctness
537
+
538
+ #### 4. **Pluggable Distiller Interface**
539
+ - **Our Advantage**: AI-powered fact extraction (future: Claude API)
540
+ - **Evidence**: distill/distiller.rb - Extracts entities, facts, scope hints
541
+ - **Value**: Understands context and intent, not just code structure
542
+ - **Why**: Memory extracts meaning; search indexes literal content
543
+
544
+ #### 5. **Hook-Based Integration**
545
+ - **Our Advantage**: Seamless integration with Claude Code events
546
+ - **Evidence**: hook/ - Reads stdin JSON from Claude Code hooks
547
+ - **Value**: Zero-effort ingestion, automatic sweeping
548
+ - **Why**: Memory is reactive to AI sessions; search is proactive (file watcher)
549
+
550
+ ---
551
+
552
+ ### Trade-offs
553
+
554
+ | Approach | Pros | Cons |
555
+ |----------|------|------|
556
+ | **Their: Proactive file watching** | Always up-to-date, zero manual work | CPU/disk overhead, battery drain on laptops, irrelevant for non-code files |
557
+ | **Ours: Reactive transcript ingest** | Only processes meaningful interactions | Requires hook setup, delayed until session ends |
558
+ | **Their: Vector embeddings** | Semantic understanding, fuzzy matching | Requires embedding provider, slower than text search, cost (if OpenAI) |
559
+ | **Ours: FTS5 full-text search** | Fast, zero dependencies, no cost | Exact/substring matching only, no semantic understanding |
560
+ | **Their: Go (compiled)** | Fast startup, low memory, easy distribution | Harder to extend (compile step), less metaprogramming |
561
+ | **Ours: Ruby (interpreted)** | Easy to extend, rich metaprogramming | Slower, requires Ruby runtime, harder to distribute |
562
+ | **Their: Chunk-based storage** | Optimized for retrieval granularity | Redundancy across chunks, harder to track changes |
563
+ | **Ours: Fact-based storage** | Deduplicated, structured, queryable | Requires distillation step, more complex schema |
564
+ | **Their: Interface-based backends** | Pluggable storage (GOB/Postgres/Qdrant) | Complexity in maintaining multiple implementations |
565
+ | **Ours: SQLite-only** | Simple, zero config, portable | Limited scalability, no built-in vector search |
566
+ | **Their: Tree-sitter call graph** | Static analysis, language-agnostic | Setup cost (parsers), limited to supported languages |
567
+ | **Ours: No call graph** | Simpler architecture | Can't answer "who calls this?" questions |
568
+ | **Their: Compact JSON mode** | Token-efficient for AI agents | Requires Read tool for full content (two-step) |
569
+ | **Ours: Full content in recall** | One-step retrieval | Higher token usage per query |
570
+
571
+ **Key Trade-off**: grepai prioritizes **search speed and semantic understanding** at the cost of setup complexity. ClaudeMemory prioritizes **truth maintenance and provenance** at the cost of search sophistication.
572
+
573
+ ---
574
+
575
+ ## Adoption Opportunities
576
+
577
+ ### High Priority ⭐
578
+
579
+ #### 1. Incremental Indexing with File Watching
580
+ - **Value**: ClaudeMemory index could stay fresh automatically during coding sessions
581
+ - **Evidence**: watcher/watcher.go:44 - `fsnotify` with debouncing, gitignore respect
582
+ - **Implementation**:
583
+ 1. Add `fsnotify` to Gemfile
584
+ 2. Create `ClaudeMemory::Watcher` class wrapping `Listen` gem (Ruby equivalent of fsnotify)
585
+ 3. Watch `.claude/projects/*/transcripts/*.jsonl` for new lines (tail-like behavior)
586
+ 4. Debounce events (default 500ms to avoid thrashing during bulk writes)
587
+ 5. Trigger `IngestCommand` automatically when new transcript data appears
588
+ 6. Optional: Watch `.claude/rules/` for manual fact additions
589
+ - **Effort**: 2-3 days (watcher class, integration with ingest, testing)
590
+ - **Trade-off**: Adds background process (memory overhead ~10MB), may complicate testing
591
+ - **Recommendation**: **ADOPT** - Eliminates manual `claude-memory ingest` calls, huge UX win
592
+
593
+ #### 2. Compact Response Format for MCP Tools
594
+ - **Value**: Reduce token usage by ~60% in MCP responses by omitting verbose content
595
+ - **Evidence**: mcp/server.go:219 - `SearchResultCompact` omits content field, returns only metadata
596
+ - **Implementation**:
597
+ 1. Add `compact` boolean parameter to `memory.recall` and `memory.search_*` tools
598
+ 2. Create `CompactFormatter` in `MCP::ResponseFormatter`:
599
+ ```ruby
600
+ def format_fact_compact(fact)
601
+ {
602
+ id: fact.id,
603
+ subject: fact.subject,
604
+ predicate: fact.predicate,
605
+ object: fact.object,
606
+ scope: fact.scope,
607
+ confidence: fact.confidence
608
+ }
609
+ # Omit: provenance, supersession_chain, context excerpts
610
+ end
611
+ ```
612
+ 3. Default to `compact: true` for all MCP tools (user can override with `compact: false`)
613
+ 4. Update tool descriptions to explain compact mode
614
+ - **Effort**: 4-6 hours (add parameter, update formatters, tests)
615
+ - **Trade-off**: User needs follow-up `memory.explain <fact_id>` for full context (two-step interaction)
616
+ - **Recommendation**: **ADOPT** - Critical for scaling to large fact databases (1000+ facts)
617
+
618
+ #### 3. Hybrid Search (Vector + Text)
619
+ - **Value**: Better relevance when users search for specific terms (e.g., "uses_database") while preserving semantic matching
620
+ - **Evidence**: search/search.go - Reciprocal Rank Fusion (RRF) with K=60
621
+ - **Implementation**:
622
+ 1. Add `sqlite-vec` extension to Gemfile for vector similarity in SQLite
623
+ 2. Add `embeddings` column to `facts` table (BLOB storing float32 array)
624
+ 3. Create `ClaudeMemory::Embedder` interface:
625
+ - Implementation: Call Anthropic API for embeddings (free with Claude usage)
626
+ - Cache embeddings per fact (regenerate only when fact changes)
627
+ 4. Implement RRF in `Recall#query`:
628
+ ```ruby
629
+ vector_results = vector_search(query, limit * 2) # Cosine similarity
630
+ text_results = fts_search(query, limit * 2) # Existing FTS5
631
+ fuse_with_rrf(vector_results, text_results, k: 60)
632
+ ```
633
+ 5. Make hybrid search optional via `.grepai/config.yaml`:
634
+ ```yaml
635
+ search:
636
+ hybrid:
637
+ enabled: true
638
+ k: 60
639
+ ```
640
+ - **Effort**: 5-7 days (embedder setup, schema migration, RRF implementation, testing)
641
+ - **Trade-off**: Requires API calls for embedding (cost ~$0.00001/fact), slower queries (2x search + fusion)
642
+ - **Recommendation**: **CONSIDER** - High value but significant implementation effort. Start with FTS5, add vectors later if search quality issues arise.
643
+
644
+ #### 4. Call Graph for Fact Dependencies
645
+ - **Value**: Show which facts depend on others (supersession chains, conflict relationships) visually
646
+ - **Evidence**: trace/trace.go:95 - `CallGraph` struct with nodes and edges
647
+ - **Implementation**:
648
+ 1. Create `memory.fact_graph <fact_id> --depth 2` MCP tool
649
+ 2. Query `fact_links` table to build graph:
650
+ - Nodes: Facts (subject/predicate/object)
651
+ - Edges: Supersedes, Conflicts, Supports
652
+ 3. Return JSON matching grepai's format:
653
+ ```json
654
+ {
655
+ "root": "fact_123",
656
+ "nodes": {"fact_123": {...}, "fact_456": {...}},
657
+ "edges": [
658
+ {"from": "fact_123", "to": "fact_456", "type": "supersedes"},
659
+ {"from": "fact_123", "to": "fact_789", "type": "conflicts"}
660
+ ],
661
+ "depth": 2
662
+ }
663
+ ```
664
+ 4. Depth-limited BFS traversal (avoid exponential explosion)
665
+ - **Effort**: 2-3 days (graph builder, MCP tool, tests)
666
+ - **Trade-off**: Adds complexity for a feature used mainly for debugging/exploration
667
+ - **Recommendation**: **ADOPT** - Invaluable for understanding why facts were superseded or conflicted
668
+
669
+ #### 5. Multi-Project Workspace Mode
670
+ - **Value**: Search facts across multiple projects simultaneously (e.g., all Ruby projects)
671
+ - **Evidence**: mcp/server.go:252 - `handleWorkspaceSearch` with project filtering
672
+ - **Implementation**:
673
+ 1. Extend `.claude/settings.json` with workspace config:
674
+ ```json
675
+ {
676
+ "workspaces": {
677
+ "ruby-projects": {
678
+ "projects": [
679
+ "/Users/me/project1",
680
+ "/Users/me/project2"
681
+ ],
682
+ "scope": "project" // Only search project-scoped facts
683
+ }
684
+ }
685
+ }
686
+ ```
687
+ 2. Add `workspace` parameter to `memory.recall`:
688
+ ```ruby
689
+ memory.recall(query: "authentication", workspace: "ruby-projects")
690
+ ```
691
+ 3. StoreManager opens all project databases, merges results
692
+ 4. Filter by `project_path` matching workspace projects
693
+ - **Effort**: 3-4 days (workspace config, multi-DB queries, result merging, tests)
694
+ - **Trade-off**: Complexity in managing multiple DB connections, potential for confusion (which project is this fact from?)
695
+ - **Recommendation**: **DEFER** - Nice-to-have but low ROI for current use case (most users work on one project at a time)
696
+
697
+ ---
698
+
699
+ ### Medium Priority
700
+
701
+ #### 6. Self-Update Mechanism
702
+ - **Value**: Users get bug fixes and new features automatically without reinstalling gem
703
+ - **Evidence**: updater/updater.go - Checks GitHub releases, downloads binary, replaces self
704
+ - **Implementation**:
705
+ 1. Add `claude-memory update` command
706
+ 2. Check GitHub releases API for `anthropics/claude-memory` (or RubyGems API)
707
+ 3. Compare current version (`ClaudeMemory::VERSION`) with latest
708
+ 4. Download gem, extract, replace files in-place
709
+ 5. Display changelog and prompt to confirm
710
+ - **Effort**: 2-3 days (update logic, safe file replacement, testing)
711
+ - **Trade-off**: Requires write permissions to gem directory (may fail in system-wide installs)
712
+ - **Recommendation**: **CONSIDER** - Nice UX, but users can `gem update` manually
713
+
714
+ #### 7. Configuration via YAML
715
+ - **Value**: Easier configuration than editing JSON or ENV vars
716
+ - **Evidence**: config/config.go - `.grepai/config.yaml` with typed structs
717
+ - **Implementation**:
718
+ 1. Add `.claude/memory.yaml` support:
719
+ ```yaml
720
+ ingest:
721
+ auto_sweep: true
722
+ sweep_budget_seconds: 5
723
+ publish:
724
+ mode: shared
725
+ include_provenance: false
726
+ recall:
727
+ default_limit: 10
728
+ scope: all
729
+ ```
730
+ 2. Load YAML in `Configuration` class, merge with ENV vars (ENV takes precedence)
731
+ 3. Validate config on load (raise clear errors for invalid values)
732
+ - **Effort**: 1-2 days (YAML parsing, validation, tests)
733
+ - **Trade-off**: Another configuration method to document and support
734
+ - **Recommendation**: **CONSIDER** - Better UX, but JSON in `.claude/settings.json` works fine for now
735
+
736
+ #### 8. Batch MCP Tool Operations
737
+ - **Value**: Reduce round-trips when agent needs to recall multiple facts
738
+ - **Evidence**: embedder/embedder.go:10 - `EmbedBatch` for parallel processing
739
+ - **Implementation**:
740
+ 1. Add `memory.recall_batch` tool accepting array of queries:
741
+ ```json
742
+ {
743
+ "queries": [
744
+ {"query": "authentication", "limit": 5},
745
+ {"query": "database schema", "limit": 3}
746
+ ]
747
+ }
748
+ ```
749
+ 2. Execute queries in parallel (use `Concurrent::Future` from concurrent-ruby gem)
750
+ 3. Return merged results with query labels:
751
+ ```json
752
+ [
753
+ {"query": "authentication", "results": [...]},
754
+ {"query": "database schema", "results": [...]}
755
+ ]
756
+ ```
757
+ - **Effort**: 1-2 days (batch tool, parallel execution, tests)
758
+ - **Trade-off**: More complex error handling (what if one query fails?)
759
+ - **Recommendation**: **CONSIDER** - Useful if agents frequently need multiple searches, but current single-query API is simpler
760
+
761
+ ---
762
+
763
+ ### Low Priority
764
+
765
+ #### 9. TUI (Terminal UI) for Interactive Exploration
766
+ - **Value**: Visual interface for browsing facts, conflicts, provenance
767
+ - **Evidence**: cli/ uses `charmbracelet/bubbletea` for rich TUI
768
+ - **Implementation**:
769
+ 1. Add `claude-memory explore` command launching TUI
770
+ 2. Use `tty-prompt` gem for interactive menus:
771
+ - Browse facts by predicate
772
+ - Explore supersession chains
773
+ - View conflict details
774
+ - Search facts interactively
775
+ 3. Display provenance excerpts inline
776
+ - **Effort**: 3-5 days (TUI design, navigation, rendering)
777
+ - **Trade-off**: Adds dependency and complexity for limited use case (most interaction via MCP tools)
778
+ - **Recommendation**: **DEFER** - Nice for power users, but MCP tools + `memory.explain` cover 90% of needs
779
+
780
+ #### 10. Prometheus Metrics Endpoint
781
+ - **Value**: Monitor memory system health (fact count, sweep duration, query latency)
782
+ - **Evidence**: grepai exposes metrics via `grepai_index_status` tool
783
+ - **Implementation**:
784
+ 1. Add `claude-memory serve-metrics` command (HTTP server on `:9090/metrics`)
785
+ 2. Expose Prometheus-format metrics:
786
+ - `claude_memory_facts_total{scope="global|project"}`
787
+ - `claude_memory_sweep_duration_seconds`
788
+ - `claude_memory_recall_latency_seconds`
789
+ 3. Optional: Export to StatsD, DataDog, etc.
790
+ - **Effort**: 2-3 days (metrics collection, HTTP server, testing)
791
+ - **Trade-off**: Requires running separate metrics server, overkill for most users
792
+ - **Recommendation**: **DEFER** - Only needed for production deployments with SLAs
793
+
794
+ ---
795
+
796
+ ### Features to Avoid
797
+
798
+ #### 1. Cloud-Based Embedding Service
799
+ - **Why Avoid**: grepai's privacy-first approach (Ollama local embeddings) is a key selling point, but ClaudeMemory's use case (AI memory, not code search) doesn't require embeddings yet
800
+ - **Our Alternative**: Stick with FTS5 full-text search until we need semantic matching. If we add embeddings, use Anthropic API (already authenticated) rather than separate embedding service
801
+ - **Reasoning**: Adding embeddings adds cost, latency, and complexity. FTS5 is sufficient for fact recall (structured data) vs code search (unstructured data)
802
+
803
+ #### 2. Multiple Storage Backends (Postgres, Qdrant)
804
+ - **Why Avoid**: Increases maintenance burden (test matrix, docs, support) for unclear benefit
805
+ - **Our Alternative**: SQLite is perfect for local storage, portable, and sufficient for fact databases (<100K facts). If we need remote storage, use libSQL (SQLite over HTTP) or Turso
806
+ - **Reasoning**: grepai needs backends for team collaboration (shared index). ClaudeMemory is single-user by design (global + project scoping)
807
+
808
+ #### 3. Daemon Mode with Background Indexing
809
+ - **Why Avoid**: Adds complexity (process management, logging, crash recovery) and battery drain
810
+ - **Our Alternative**: Hook-based reactive ingestion (current approach) is elegant and efficient. Only process transcripts when meaningful work happens (AI sessions)
811
+ - **Reasoning**: grepai needs daemon for real-time file watching. ClaudeMemory doesn't need to watch files (transcripts are append-only, hooks trigger ingestion)
812
+
813
+ ---
814
+
815
+ ## Implementation Recommendations
816
+
817
+ ### Phase 1: Quick Wins (1-2 weeks)
818
+
819
+ **Goal**: Low-effort, high-value improvements
820
+
821
+ - [ ] **Compact MCP responses** (4-6 hours)
822
+ - Add `compact: true` parameter to all recall tools
823
+ - Omit provenance and context excerpts by default
824
+ - Test token reduction with realistic queries
825
+ - Success criteria: 60% token reduction in MCP responses
826
+
827
+ - [ ] **Fact dependency graph** (2-3 days)
828
+ - Implement `memory.fact_graph <fact_id> --depth 2` tool
829
+ - BFS traversal of fact_links table
830
+ - Return JSON with nodes and edges
831
+ - Success criteria: Visualize supersession chains and conflicts
832
+
833
+ ### Phase 2: Incremental Indexing (2-3 weeks)
834
+
835
+ **Goal**: Auto-update index during coding sessions
836
+
837
+ - [ ] **File watcher for transcripts** (2-3 days)
838
+ - Add `Listen` gem (Ruby equivalent of fsnotify)
839
+ - Watch `.claude/projects/*/transcripts/*.jsonl` for changes
840
+ - Debounce rapid changes (500ms)
841
+ - Trigger `IngestCommand` automatically
842
+ - Success criteria: Index updates within 1 second of transcript write
843
+
844
+ - [ ] **Optional daemon mode** (2-3 days)
845
+ - Add `claude-memory watch` command (background process)
846
+ - Fork and daemonize, write PID file
847
+ - Graceful shutdown on SIGTERM
848
+ - Success criteria: Run in background, no manual ingest needed
849
+
850
+ - [ ] **Integration with existing hooks** (1 day)
851
+ - Keep existing hooks as fallback (if watcher not running)
852
+ - Add `auto_watch: true` setting to enable watcher
853
+ - Success criteria: Works with or without daemon
854
+
855
+ ### Phase 3: Hybrid Search (4-6 weeks)
856
+
857
+ **Goal**: Add semantic search for better relevance
858
+
859
+ - [ ] **Embedder interface** (1 week)
860
+ - Create `ClaudeMemory::Embedder` module
861
+ - Implement Anthropic API embedder (call `/v1/embeddings`)
862
+ - Cache embeddings in `fact_embeddings` table
863
+ - Success criteria: Generate embeddings for facts
864
+
865
+ - [ ] **Vector storage** (1 week)
866
+ - Add `sqlite-vec` extension to dependencies
867
+ - Migrate schema: add `embedding` BLOB column to `facts`
868
+ - Implement cosine similarity search
869
+ - Success criteria: Vector search returns similar facts
870
+
871
+ - [ ] **RRF implementation** (1 week)
872
+ - Implement Reciprocal Rank Fusion in `Recall#query`
873
+ - Combine FTS5 results with vector results
874
+ - Make hybrid search optional via config
875
+ - Success criteria: Better relevance than FTS5 alone
876
+
877
+ - [ ] **Performance tuning** (1 week)
878
+ - Benchmark vector search vs FTS5
879
+ - Add caching for frequently-queried embeddings
880
+ - Optimize batch embedding (parallel API calls)
881
+ - Success criteria: Hybrid search <500ms for typical queries
882
+
883
+ ---
884
+
885
+ ## Architecture Decisions
886
+
887
+ ### What to Preserve
888
+
889
+ - **SQLite-only storage**: Simple, portable, fast enough for fact databases
890
+ - **Hook-based integration**: Elegant reactive model (no polling, no daemons unless opted-in)
891
+ - **Fact-based data model**: Structured triples with provenance vs unstructured chunks
892
+ - **Truth maintenance**: Supersession and conflict resolution (grepai has no equivalent)
893
+ - **Dual-database scoping**: Global vs project facts (grepai has only project-local or workspace)
894
+
895
+ ### What to Adopt
896
+
897
+ - **File watcher for incremental indexing**: Huge UX win, eliminates manual `ingest` calls
898
+ - **Compact JSON for MCP tools**: Critical for scaling to large fact databases
899
+ - **Fact dependency graph visualization**: Invaluable for debugging supersession/conflicts
900
+ - **Interface-based extensibility**: If we add embeddings, use pluggable `Embedder` interface
901
+ - **Hybrid search (RRF)**: Better relevance, proven technique
902
+
903
+ ### What to Reject
904
+
905
+ - **Cloud-based embeddings**: Privacy concerns, cost, latency (use Anthropic API if needed)
906
+ - **Multiple storage backends**: Adds complexity without clear benefit for single-user tool
907
+ - **Daemon mode by default**: Optional is fine, but hooks are simpler and more efficient
908
+ - **Tree-sitter call graphs**: Out of scope for memory system (we track fact dependencies, not code dependencies)
909
+ - **Workspace mode**: Defer until multi-project use case is validated
910
+
911
+ ---
912
+
913
+ ## Key Takeaways
914
+
915
+ 1. **grepai excels at real-time semantic search** via file watching + vector embeddings. We should adopt file watching for transcript indexing but defer vector embeddings until FTS5 proves insufficient.
916
+
917
+ 2. **Compact JSON mode is critical for token efficiency**. We should implement this immediately in all MCP tools (60% token reduction with minimal effort).
918
+
919
+ 3. **Fact dependency graphs** (supersession chains, conflicts) are analogous to grepai's call graphs. Implementing `memory.fact_graph` would be highly valuable for understanding fact relationships.
920
+
921
+ 4. **Interface-based extensibility** is a proven pattern. If we add embeddings, follow grepai's `Embedder` interface design for pluggability.
922
+
923
+ 5. **Hybrid search (vector + text)** is more sophisticated than our FTS5-only approach, but adds significant complexity. Defer until search quality becomes a bottleneck.
924
+
925
+ 6. **Go's performance and distribution advantages** are appealing, but Ruby is fine for our use case (not latency-critical, single-user tool). Don't rewrite unless clear performance issues arise.
926
+
927
+ 7. **Recommended adoption order**:
928
+ - **Immediate**: Compact JSON, fact dependency graph
929
+ - **Short-term**: File watcher for transcripts
930
+ - **Medium-term**: Hybrid search (if needed)
931
+ - **Defer**: Workspace mode, cloud backends, TUI
932
+
933
+ **Expected impact**: Adopting file watching + compact JSON would eliminate manual `ingest` calls and reduce token usage by 60%, dramatically improving UX without major architectural changes.