claude_memory 0.4.0 → 0.5.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/CLAUDE.md +1 -1
  3. data/.claude/rules/claude_memory.generated.md +14 -1
  4. data/.claude/skills/check-memory/SKILL.md +10 -0
  5. data/.claude/skills/improve/SKILL.md +12 -1
  6. data/.claude-plugin/plugin.json +1 -1
  7. data/CHANGELOG.md +70 -0
  8. data/db/migrations/008_add_provenance_line_range.rb +21 -0
  9. data/db/migrations/009_add_docid.rb +39 -0
  10. data/db/migrations/010_add_llm_cache.rb +30 -0
  11. data/docs/improvements.md +72 -1084
  12. data/docs/influence/claude-supermemory.md +498 -0
  13. data/docs/influence/qmd.md +424 -2022
  14. data/docs/quality_review.md +64 -705
  15. data/lib/claude_memory/commands/doctor_command.rb +45 -4
  16. data/lib/claude_memory/commands/explain_command.rb +11 -6
  17. data/lib/claude_memory/commands/stats_command.rb +1 -1
  18. data/lib/claude_memory/core/fact_graph.rb +122 -0
  19. data/lib/claude_memory/core/fact_query_builder.rb +34 -14
  20. data/lib/claude_memory/core/fact_ranker.rb +3 -20
  21. data/lib/claude_memory/core/relative_time.rb +45 -0
  22. data/lib/claude_memory/core/result_sorter.rb +2 -2
  23. data/lib/claude_memory/core/rr_fusion.rb +57 -0
  24. data/lib/claude_memory/core/snippet_extractor.rb +97 -0
  25. data/lib/claude_memory/domain/fact.rb +3 -1
  26. data/lib/claude_memory/index/index_query.rb +2 -0
  27. data/lib/claude_memory/index/lexical_fts.rb +18 -0
  28. data/lib/claude_memory/infrastructure/operation_tracker.rb +7 -21
  29. data/lib/claude_memory/infrastructure/schema_validator.rb +30 -25
  30. data/lib/claude_memory/ingest/content_sanitizer.rb +8 -1
  31. data/lib/claude_memory/ingest/ingester.rb +67 -56
  32. data/lib/claude_memory/ingest/tool_extractor.rb +1 -1
  33. data/lib/claude_memory/ingest/tool_filter.rb +55 -0
  34. data/lib/claude_memory/logging/logger.rb +112 -0
  35. data/lib/claude_memory/mcp/query_guide.rb +96 -0
  36. data/lib/claude_memory/mcp/response_formatter.rb +86 -23
  37. data/lib/claude_memory/mcp/server.rb +34 -4
  38. data/lib/claude_memory/mcp/text_summary.rb +257 -0
  39. data/lib/claude_memory/mcp/tool_definitions.rb +20 -4
  40. data/lib/claude_memory/mcp/tools.rb +133 -120
  41. data/lib/claude_memory/publish.rb +12 -2
  42. data/lib/claude_memory/recall/expansion_detector.rb +44 -0
  43. data/lib/claude_memory/recall.rb +93 -41
  44. data/lib/claude_memory/resolve/resolver.rb +72 -40
  45. data/lib/claude_memory/store/sqlite_store.rb +99 -24
  46. data/lib/claude_memory/sweep/sweeper.rb +6 -0
  47. data/lib/claude_memory/version.rb +1 -1
  48. data/lib/claude_memory.rb +21 -0
  49. metadata +14 -2
  50. data/docs/remaining_improvements.md +0 -330
@@ -50,61 +50,91 @@ module ClaudeMemory
50
50
  end
51
51
 
52
52
  def resolve_fact(fact_data, entity_ids, content_item_id, occurred_at)
53
- subject_id = entity_ids[fact_data[:subject]] ||
53
+ subject_id = resolve_subject(fact_data, entity_ids)
54
+ existing_facts = @store.facts_for_slot(subject_id, fact_data[:predicate])
55
+ resolution = determine_resolution(existing_facts, fact_data, entity_ids)
56
+
57
+ apply_resolution(resolution, fact_data, subject_id, entity_ids, content_item_id, occurred_at, existing_facts)
58
+ end
59
+
60
+ def resolve_subject(fact_data, entity_ids)
61
+ entity_ids[fact_data[:subject]] ||
54
62
  @store.find_or_create_entity(type: "repo", name: fact_data[:subject])
63
+ end
55
64
 
56
- predicate = fact_data[:predicate]
57
- object_val = fact_data[:object]
58
- object_entity_id = entity_ids[object_val]
59
-
60
- outcome = {created: 0, superseded: 0, conflicts: 0, provenance: 0}
61
-
62
- existing_facts = @store.facts_for_slot(subject_id, predicate)
63
-
64
- # No transaction wrapper needed - handled by apply method
65
- # This allows all facts to be processed in a single transaction
66
- if PredicatePolicy.single?(predicate) && existing_facts.any?
67
- matching = existing_facts.find { |f| values_match?(f, object_val, object_entity_id) }
68
- if matching
69
- add_provenance(matching[:id], content_item_id, fact_data)
70
- outcome[:provenance] = 1
71
- return outcome
72
- elsif supersession_signal?(fact_data)
73
- supersede_facts(existing_facts, occurred_at)
74
- outcome[:superseded] = existing_facts.size
75
- else
76
- create_conflict(existing_facts.first[:id], fact_data, subject_id, content_item_id, occurred_at)
77
- outcome[:conflicts] = 1
78
- return outcome
79
- end
65
+ def determine_resolution(existing_facts, fact_data, entity_ids)
66
+ return :insert unless PredicatePolicy.single?(fact_data[:predicate]) && existing_facts.any?
67
+
68
+ object_entity_id = entity_ids[fact_data[:object]]
69
+ matching = existing_facts.find { |f| values_match?(f, fact_data[:object], object_entity_id) }
70
+
71
+ if matching
72
+ :reinforce
73
+ elsif supersession_signal?(fact_data)
74
+ :supersede
75
+ else
76
+ :conflict
77
+ end
78
+ end
79
+
80
+ def apply_resolution(resolution, fact_data, subject_id, entity_ids, content_item_id, occurred_at, existing_facts)
81
+ case resolution
82
+ when :reinforce
83
+ apply_reinforcement(existing_facts, fact_data, entity_ids, content_item_id)
84
+ when :conflict
85
+ apply_conflict(existing_facts, fact_data, subject_id, content_item_id, occurred_at)
86
+ else
87
+ apply_insert(fact_data, subject_id, entity_ids, content_item_id, occurred_at, existing_facts, resolution)
88
+ end
89
+ end
90
+
91
+ def apply_reinforcement(existing_facts, fact_data, entity_ids, content_item_id)
92
+ object_entity_id = entity_ids[fact_data[:object]]
93
+ matching = existing_facts.find { |f| values_match?(f, fact_data[:object], object_entity_id) }
94
+ add_provenance(matching[:id], content_item_id, fact_data)
95
+ {created: 0, superseded: 0, conflicts: 0, provenance: 1}
96
+ end
97
+
98
+ def apply_conflict(existing_facts, fact_data, subject_id, content_item_id, occurred_at)
99
+ create_conflict(existing_facts.first[:id], fact_data, subject_id, content_item_id, occurred_at)
100
+ {created: 0, superseded: 0, conflicts: 1, provenance: 0}
101
+ end
102
+
103
+ def apply_insert(fact_data, subject_id, entity_ids, content_item_id, occurred_at, existing_facts, resolution)
104
+ superseded_count = 0
105
+ if resolution == :supersede
106
+ supersede_facts(existing_facts, occurred_at)
107
+ superseded_count = existing_facts.size
80
108
  end
81
109
 
110
+ fact_id = insert_new_fact(fact_data, subject_id, entity_ids, occurred_at)
111
+ link_superseded_facts(fact_id, existing_facts) if superseded_count > 0
112
+ add_provenance(fact_id, content_item_id, fact_data)
113
+
114
+ {created: 1, superseded: superseded_count, conflicts: 0, provenance: 1}
115
+ end
116
+
117
+ def insert_new_fact(fact_data, subject_id, entity_ids, occurred_at)
82
118
  fact_scope = fact_data[:scope_hint] || @current_scope
83
119
  fact_project = (fact_scope == "global") ? nil : @current_project_path
84
120
 
85
- fact_id = @store.insert_fact(
121
+ @store.insert_fact(
86
122
  subject_entity_id: subject_id,
87
- predicate: predicate,
88
- object_entity_id: object_entity_id,
89
- object_literal: object_val,
123
+ predicate: fact_data[:predicate],
124
+ object_entity_id: entity_ids[fact_data[:object]],
125
+ object_literal: fact_data[:object],
90
126
  polarity: fact_data[:polarity] || "positive",
91
127
  confidence: fact_data[:confidence] || 1.0,
92
128
  valid_from: occurred_at,
93
129
  scope: fact_scope,
94
130
  project_path: fact_project
95
131
  )
96
- outcome[:created] = 1
132
+ end
97
133
 
98
- if existing_facts.any? && outcome[:superseded] > 0
99
- existing_facts.each do |old_fact|
100
- @store.insert_fact_link(from_fact_id: fact_id, to_fact_id: old_fact[:id], link_type: "supersedes")
101
- end
134
+ def link_superseded_facts(new_fact_id, old_facts)
135
+ old_facts.each do |old_fact|
136
+ @store.insert_fact_link(from_fact_id: new_fact_id, to_fact_id: old_fact[:id], link_type: "supersedes")
102
137
  end
103
-
104
- add_provenance(fact_id, content_item_id, fact_data)
105
- outcome[:provenance] = 1
106
-
107
- outcome
108
138
  end
109
139
 
110
140
  def supersession_signal?(fact_data)
@@ -151,7 +181,9 @@ module ClaudeMemory
151
181
  fact_id: fact_id,
152
182
  content_item_id: content_item_id,
153
183
  quote: fact_data[:quote],
154
- strength: fact_data[:strength] || "stated"
184
+ strength: fact_data[:strength] || "stated",
185
+ line_start: fact_data[:line_start],
186
+ line_end: fact_data[:line_end]
155
187
  )
156
188
  end
157
189
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "sequel"
4
4
  require "sequel/extensions/migration"
5
+ require "digest"
5
6
  require "json"
6
7
  require "extralite"
7
8
  require "sequel/adapters/extralite"
@@ -9,7 +10,7 @@ require "sequel/adapters/extralite"
9
10
  module ClaudeMemory
10
11
  module Store
11
12
  class SQLiteStore
12
- SCHEMA_VERSION = 7
13
+ SCHEMA_VERSION = 10
13
14
 
14
15
  attr_reader :db
15
16
 
@@ -17,32 +18,20 @@ module ClaudeMemory
17
18
  @db_path = db_path
18
19
  @db = connect_database(db_path)
19
20
 
20
- configure_pragmas
21
-
22
21
  ensure_schema!
23
22
  end
24
23
 
25
24
  private
26
25
 
27
26
  def connect_database(db_path)
28
- # Extralite adapter: Better performance and GVL release
29
- Sequel.connect("extralite:#{db_path}")
30
- end
31
-
32
- def configure_pragmas
33
- # Enable WAL mode for better concurrency
34
- # - Multiple readers don't block each other
35
- # - Writers don't block readers
36
- # - Safer concurrent hook execution
37
- @db.run("PRAGMA journal_mode = WAL")
38
- @db.run("PRAGMA synchronous = NORMAL")
39
-
40
- # Set busy timeout to 30 seconds (increased from 5s)
41
- # - Allows much longer wait times before raising BusyException
42
- # - Critical for concurrent hook execution with MCP server
43
- # - Combined with ingester retry logic, provides ~5 minutes total wait
44
- # - Extralite releases GVL for better threading performance
45
- @db.run("PRAGMA busy_timeout = 30000")
27
+ Sequel.connect(
28
+ "extralite:#{db_path}",
29
+ connect_sqls: [
30
+ "PRAGMA journal_mode = WAL",
31
+ "PRAGMA synchronous = NORMAL",
32
+ "PRAGMA busy_timeout = 30000"
33
+ ]
34
+ )
46
35
  end
47
36
 
48
37
  public
@@ -110,6 +99,10 @@ module ClaudeMemory
110
99
  @db[:ingestion_metrics]
111
100
  end
112
101
 
102
+ def llm_cache
103
+ @db[:llm_cache]
104
+ end
105
+
113
106
  def upsert_content_item(source:, text_hash:, byte_len:, session_id: nil, transcript_path: nil,
114
107
  project_path: nil, occurred_at: nil, raw_text: nil, metadata: nil,
115
108
  git_branch: nil, cwd: nil, claude_version: nil, thinking_level: nil, source_mtime: nil)
@@ -194,6 +187,7 @@ module ClaudeMemory
194
187
  datatype: nil, polarity: "positive", valid_from: nil, status: "active",
195
188
  confidence: 1.0, created_from: nil, scope: "project", project_path: nil)
196
189
  now = Time.now.utc.iso8601
190
+ docid = generate_docid(subject_entity_id, predicate, object_literal, now)
197
191
  facts.insert(
198
192
  subject_entity_id: subject_entity_id,
199
193
  predicate: predicate,
@@ -207,10 +201,15 @@ module ClaudeMemory
207
201
  created_from: created_from,
208
202
  created_at: now,
209
203
  scope: scope,
210
- project_path: project_path
204
+ project_path: project_path,
205
+ docid: docid
211
206
  )
212
207
  end
213
208
 
209
+ def find_fact_by_docid(docid)
210
+ facts.where(docid: docid).first
211
+ end
212
+
214
213
  def update_fact(fact_id, status: nil, valid_to: nil, scope: nil, project_path: nil, embedding: nil)
215
214
  updates = {}
216
215
  updates[:status] = status if status
@@ -253,13 +252,16 @@ module ClaudeMemory
253
252
  .all
254
253
  end
255
254
 
256
- def insert_provenance(fact_id:, content_item_id: nil, quote: nil, attribution_entity_id: nil, strength: "stated")
255
+ def insert_provenance(fact_id:, content_item_id: nil, quote: nil, attribution_entity_id: nil, strength: "stated",
256
+ line_start: nil, line_end: nil)
257
257
  provenance.insert(
258
258
  fact_id: fact_id,
259
259
  content_item_id: content_item_id,
260
260
  quote: quote,
261
261
  attribution_entity_id: attribution_entity_id,
262
- strength: strength
262
+ strength: strength,
263
+ line_start: line_start,
264
+ line_end: line_end
263
265
  )
264
266
  end
265
267
 
@@ -343,6 +345,65 @@ module ClaudeMemory
343
345
  }
344
346
  end
345
347
 
348
+ # Look up a cached LLM response by cache key
349
+ #
350
+ # @param cache_key [String] SHA256 hex digest of operation+model+input
351
+ # @return [Hash, nil] Cached result row or nil
352
+ def llm_cache_lookup(cache_key)
353
+ llm_cache.where(cache_key: cache_key).first
354
+ end
355
+
356
+ # Store an LLM response in the cache
357
+ #
358
+ # @param operation [String] Operation type (e.g., "distill", "extract")
359
+ # @param model [String] Model identifier
360
+ # @param input_hash [String] SHA256 of input content
361
+ # @param result_json [String] JSON response to cache
362
+ # @param input_tokens [Integer, nil] Tokens in request
363
+ # @param output_tokens [Integer, nil] Tokens in response
364
+ # @return [Integer] The created cache entry ID
365
+ def llm_cache_store(operation:, model:, input_hash:, result_json:, input_tokens: nil, output_tokens: nil)
366
+ cache_key = Digest::SHA256.hexdigest("#{operation}:#{model}:#{input_hash}")
367
+
368
+ llm_cache
369
+ .insert_conflict(target: :cache_key, update: {
370
+ result_json: result_json,
371
+ input_tokens: input_tokens,
372
+ output_tokens: output_tokens,
373
+ created_at: Time.now.utc.iso8601
374
+ })
375
+ .insert(
376
+ cache_key: cache_key,
377
+ operation: operation,
378
+ model: model,
379
+ input_hash: input_hash,
380
+ result_json: result_json,
381
+ input_tokens: input_tokens,
382
+ output_tokens: output_tokens,
383
+ created_at: Time.now.utc.iso8601
384
+ )
385
+ end
386
+
387
+ # Generate a cache key for LLM response lookup
388
+ #
389
+ # @param operation [String] Operation type
390
+ # @param model [String] Model identifier
391
+ # @param input [String] Raw input content
392
+ # @return [String] SHA256 hex digest cache key
393
+ def llm_cache_key(operation, model, input)
394
+ input_hash = Digest::SHA256.hexdigest(input)
395
+ Digest::SHA256.hexdigest("#{operation}:#{model}:#{input_hash}")
396
+ end
397
+
398
+ # Prune cache entries older than the given age
399
+ #
400
+ # @param max_age_seconds [Integer] Maximum age in seconds (default: 7 days)
401
+ # @return [Integer] Number of entries pruned
402
+ def llm_cache_prune(max_age_seconds: 604_800)
403
+ cutoff = (Time.now - max_age_seconds).utc.iso8601
404
+ llm_cache.where { created_at < cutoff }.delete
405
+ end
406
+
346
407
  private
347
408
 
348
409
  def ensure_schema!
@@ -398,6 +459,20 @@ module ClaudeMemory
398
459
  @db[:meta].where(key: key).get(:value)
399
460
  end
400
461
 
462
+ def generate_docid(subject_entity_id, predicate, object_literal, created_at)
463
+ input = "#{subject_entity_id}:#{predicate}:#{object_literal}:#{created_at}"
464
+ docid = Digest::SHA256.hexdigest(input)[0, 8]
465
+
466
+ # Handle unlikely collisions by rehashing with a counter
467
+ counter = 0
468
+ while facts.where(docid: docid).any?
469
+ counter += 1
470
+ docid = Digest::SHA256.hexdigest("#{input}:#{counter}")[0, 8]
471
+ end
472
+
473
+ docid
474
+ end
475
+
401
476
  def slugify(type, name)
402
477
  "#{type}:#{name.downcase.gsub(/[^a-z0-9]+/, "_").gsub(/^_|_$/, "")}"
403
478
  end
@@ -35,6 +35,12 @@ module ClaudeMemory
35
35
 
36
36
  @stats[:elapsed_seconds] = Time.now - @start_time
37
37
  @stats[:budget_honored] = @stats[:elapsed_seconds] <= budget
38
+ ClaudeMemory.logger.info("sweep",
39
+ message: "Sweep complete",
40
+ elapsed_seconds: @stats[:elapsed_seconds].round(3),
41
+ budget_honored: @stats[:budget_honored],
42
+ proposed_expired: @stats[:proposed_facts_expired],
43
+ disputed_expired: @stats[:disputed_facts_expired])
38
44
  @stats
39
45
  end
40
46
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeMemory
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/claude_memory.rb CHANGED
@@ -13,6 +13,7 @@ require_relative "claude_memory/core/null_explanation"
13
13
  require_relative "claude_memory/core/token_estimator"
14
14
  require_relative "claude_memory/core/batch_loader"
15
15
  require_relative "claude_memory/core/fact_ranker"
16
+ require_relative "claude_memory/core/rr_fusion"
16
17
  require_relative "claude_memory/core/concept_ranker"
17
18
  require_relative "claude_memory/core/scope_filter"
18
19
  require_relative "claude_memory/core/result_builder"
@@ -20,7 +21,10 @@ require_relative "claude_memory/core/fact_collector"
20
21
  require_relative "claude_memory/core/embedding_candidate_builder"
21
22
  require_relative "claude_memory/core/fact_query_builder"
22
23
  require_relative "claude_memory/core/result_sorter"
24
+ require_relative "claude_memory/core/relative_time"
23
25
  require_relative "claude_memory/core/text_builder"
26
+ require_relative "claude_memory/core/snippet_extractor"
27
+ require_relative "claude_memory/core/fact_graph"
24
28
  require_relative "claude_memory/commands/base_command"
25
29
  require_relative "claude_memory/commands/checks/database_check"
26
30
  require_relative "claude_memory/commands/checks/snapshot_check"
@@ -75,17 +79,22 @@ require_relative "claude_memory/ingest/privacy_tag"
75
79
  require_relative "claude_memory/ingest/content_sanitizer"
76
80
  require_relative "claude_memory/ingest/metadata_extractor"
77
81
  require_relative "claude_memory/ingest/tool_extractor"
82
+ require_relative "claude_memory/ingest/tool_filter"
78
83
  require_relative "claude_memory/ingest/ingester"
79
84
  require_relative "claude_memory/ingest/transcript_reader"
85
+ require_relative "claude_memory/logging/logger"
80
86
  require_relative "claude_memory/infrastructure/file_system"
81
87
  require_relative "claude_memory/infrastructure/in_memory_file_system"
82
88
  require_relative "claude_memory/infrastructure/operation_tracker"
83
89
  require_relative "claude_memory/infrastructure/schema_validator"
90
+ require_relative "claude_memory/mcp/query_guide"
91
+ require_relative "claude_memory/mcp/text_summary"
84
92
  require_relative "claude_memory/mcp/tool_helpers"
85
93
  require_relative "claude_memory/mcp/server"
86
94
  require_relative "claude_memory/mcp/tools"
87
95
  require_relative "claude_memory/publish"
88
96
  require_relative "claude_memory/recall/dual_query_template"
97
+ require_relative "claude_memory/recall/expansion_detector"
89
98
  require_relative "claude_memory/recall"
90
99
  require_relative "claude_memory/shortcuts"
91
100
  require_relative "claude_memory/resolve/predicate_policy"
@@ -103,4 +112,16 @@ module ClaudeMemory
103
112
  def self.project_db_path(project_path = Dir.pwd)
104
113
  Configuration.new.project_db_path(project_path)
105
114
  end
115
+
116
+ # Module-level logger instance, shared across components
117
+ # @return [Logging::Logger, Logging::NullLogger]
118
+ def self.logger
119
+ @logger ||= Logging::Logger.new
120
+ end
121
+
122
+ # Replace the module-level logger (useful for testing)
123
+ # @param logger [Logging::Logger, Logging::NullLogger]
124
+ def self.logger=(logger)
125
+ @logger = logger
126
+ end
106
127
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: claude_memory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valentino Stoll
@@ -88,6 +88,9 @@ files:
88
88
  - db/migrations/005_add_incremental_sync.rb
89
89
  - db/migrations/006_add_operation_tracking.rb
90
90
  - db/migrations/007_add_ingestion_metrics.rb
91
+ - db/migrations/008_add_provenance_line_range.rb
92
+ - db/migrations/009_add_docid.rb
93
+ - db/migrations/010_add_llm_cache.rb
91
94
  - docs/EXAMPLES.md
92
95
  - docs/GETTING_STARTED.md
93
96
  - docs/RELEASE_NOTES_v0.2.0.md
@@ -103,6 +106,7 @@ files:
103
106
  - docs/expert_review.md
104
107
  - docs/improvements.md
105
108
  - docs/influence/.gitkeep
109
+ - docs/influence/claude-supermemory.md
106
110
  - docs/influence/grepai.md
107
111
  - docs/influence/qmd.md
108
112
  - docs/organizational_memory_playbook.md
@@ -112,7 +116,6 @@ files:
112
116
  - docs/plans/updated_plan.md
113
117
  - docs/plugin.md
114
118
  - docs/quality_review.md
115
- - docs/remaining_improvements.md
116
119
  - docs/review_summary.md
117
120
  - exe/claude-memory
118
121
  - hooks/hooks.json
@@ -157,16 +160,20 @@ files:
157
160
  - lib/claude_memory/core/concept_ranker.rb
158
161
  - lib/claude_memory/core/embedding_candidate_builder.rb
159
162
  - lib/claude_memory/core/fact_collector.rb
163
+ - lib/claude_memory/core/fact_graph.rb
160
164
  - lib/claude_memory/core/fact_id.rb
161
165
  - lib/claude_memory/core/fact_query_builder.rb
162
166
  - lib/claude_memory/core/fact_ranker.rb
163
167
  - lib/claude_memory/core/null_explanation.rb
164
168
  - lib/claude_memory/core/null_fact.rb
169
+ - lib/claude_memory/core/relative_time.rb
165
170
  - lib/claude_memory/core/result.rb
166
171
  - lib/claude_memory/core/result_builder.rb
167
172
  - lib/claude_memory/core/result_sorter.rb
173
+ - lib/claude_memory/core/rr_fusion.rb
168
174
  - lib/claude_memory/core/scope_filter.rb
169
175
  - lib/claude_memory/core/session_id.rb
176
+ - lib/claude_memory/core/snippet_extractor.rb
170
177
  - lib/claude_memory/core/text_builder.rb
171
178
  - lib/claude_memory/core/token_estimator.rb
172
179
  - lib/claude_memory/core/transcript_path.rb
@@ -196,16 +203,21 @@ files:
196
203
  - lib/claude_memory/ingest/metadata_extractor.rb
197
204
  - lib/claude_memory/ingest/privacy_tag.rb
198
205
  - lib/claude_memory/ingest/tool_extractor.rb
206
+ - lib/claude_memory/ingest/tool_filter.rb
199
207
  - lib/claude_memory/ingest/transcript_reader.rb
208
+ - lib/claude_memory/logging/logger.rb
209
+ - lib/claude_memory/mcp/query_guide.rb
200
210
  - lib/claude_memory/mcp/response_formatter.rb
201
211
  - lib/claude_memory/mcp/server.rb
202
212
  - lib/claude_memory/mcp/setup_status_analyzer.rb
213
+ - lib/claude_memory/mcp/text_summary.rb
203
214
  - lib/claude_memory/mcp/tool_definitions.rb
204
215
  - lib/claude_memory/mcp/tool_helpers.rb
205
216
  - lib/claude_memory/mcp/tools.rb
206
217
  - lib/claude_memory/publish.rb
207
218
  - lib/claude_memory/recall.rb
208
219
  - lib/claude_memory/recall/dual_query_template.rb
220
+ - lib/claude_memory/recall/expansion_detector.rb
209
221
  - lib/claude_memory/resolve/predicate_policy.rb
210
222
  - lib/claude_memory/resolve/resolver.rb
211
223
  - lib/claude_memory/shortcuts.rb