claude_memory 0.8.0 → 0.9.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/memory.sqlite3 +0 -0
  3. data/.claude/rules/claude_memory.generated.md +32 -2
  4. data/.claude/settings.json +30 -52
  5. data/.claude/settings.local.json +3 -1
  6. data/.claude/skills/upgrade-dependencies/SKILL.md +154 -0
  7. data/.claude-plugin/marketplace.json +2 -2
  8. data/.claude-plugin/plugin.json +3 -3
  9. data/.claude-plugin/scripts/hook-runner.sh +14 -0
  10. data/.claude-plugin/scripts/serve-mcp.sh +14 -0
  11. data/.ruby-version +1 -1
  12. data/CHANGELOG.md +41 -0
  13. data/CLAUDE.md +31 -17
  14. data/README.md +35 -0
  15. data/db/migrations/013_add_mcp_tool_calls.rb +26 -0
  16. data/db/migrations/014_canonicalize_predicates.rb +30 -0
  17. data/docs/improvements.md +58 -20
  18. data/docs/influence/claude-mem.md +1 -0
  19. data/docs/influence/claude-supermemory.md +1 -0
  20. data/docs/influence/episodic-memory.md +1 -0
  21. data/docs/influence/grepai.md +1 -0
  22. data/docs/influence/kbs.md +1 -0
  23. data/docs/influence/lossless-claw.md +1 -0
  24. data/docs/influence/qmd.md +1 -0
  25. data/lib/claude_memory/commands/completion_command.rb +1 -31
  26. data/lib/claude_memory/commands/embeddings_command.rb +198 -0
  27. data/lib/claude_memory/commands/help_command.rb +8 -1
  28. data/lib/claude_memory/commands/registry.rb +47 -34
  29. data/lib/claude_memory/commands/reject_command.rb +62 -0
  30. data/lib/claude_memory/commands/restore_command.rb +77 -0
  31. data/lib/claude_memory/commands/skills/distill-transcripts.md +5 -1
  32. data/lib/claude_memory/commands/stats_command.rb +98 -2
  33. data/lib/claude_memory/configuration.rb +14 -1
  34. data/lib/claude_memory/distill/json_schema.md +8 -4
  35. data/lib/claude_memory/distill/null_distiller.rb +2 -0
  36. data/lib/claude_memory/domain/entity.rb +13 -1
  37. data/lib/claude_memory/domain/fact.rb +26 -2
  38. data/lib/claude_memory/embeddings/api_adapter.rb +5 -4
  39. data/lib/claude_memory/embeddings/fastembed_adapter.rb +43 -13
  40. data/lib/claude_memory/embeddings/inspector.rb +91 -0
  41. data/lib/claude_memory/embeddings/model_registry.rb +210 -0
  42. data/lib/claude_memory/embeddings/resolver.rb +32 -6
  43. data/lib/claude_memory/ingest/ingester.rb +17 -0
  44. data/lib/claude_memory/mcp/handlers/management_handlers.rb +24 -0
  45. data/lib/claude_memory/mcp/handlers/stats_handlers.rb +5 -2
  46. data/lib/claude_memory/mcp/instructions_builder.rb +17 -0
  47. data/lib/claude_memory/mcp/server.rb +22 -1
  48. data/lib/claude_memory/mcp/telemetry.rb +86 -0
  49. data/lib/claude_memory/mcp/tool_definitions.rb +86 -3
  50. data/lib/claude_memory/mcp/tools.rb +10 -0
  51. data/lib/claude_memory/publish.rb +40 -5
  52. data/lib/claude_memory/recall.rb +81 -0
  53. data/lib/claude_memory/resolve/predicate_policy.rb +63 -3
  54. data/lib/claude_memory/resolve/resolver.rb +43 -0
  55. data/lib/claude_memory/store/schema_manager.rb +1 -1
  56. data/lib/claude_memory/store/sqlite_store.rb +250 -1
  57. data/lib/claude_memory/store/store_manager.rb +50 -1
  58. data/lib/claude_memory/sweep/maintenance.rb +115 -1
  59. data/lib/claude_memory/sweep/sweeper.rb +3 -0
  60. data/lib/claude_memory/version.rb +1 -1
  61. data/lib/claude_memory.rb +5 -0
  62. metadata +26 -8
  63. data/.claude/memory.sqlite3-shm +0 -0
  64. data/.claude/memory.sqlite3-wal +0 -0
@@ -8,10 +8,16 @@ module ClaudeMemory
8
8
  #
9
9
  # Source: QMD v2.0.1 Maintenance class pattern
10
10
  class Maintenance
11
+ # Short / noise tokens dropped before Jaccard comparison.
12
+ # Intentionally minimal — we want conservative token extraction that
13
+ # still treats "Rails 8.0" and "Rails 8.1" as overlapping.
14
+ RESTORE_STOPWORDS = %w[for the and with via of in on to by is are].to_set.freeze
15
+ RESTORE_JACCARD_THRESHOLD = 0.5
11
16
  DEFAULT_CONFIG = {
12
17
  proposed_fact_ttl_days: 14,
13
18
  disputed_fact_ttl_days: 30,
14
- content_retention_days: 30
19
+ content_retention_days: 30,
20
+ mcp_tool_call_retention_days: 90
15
21
  }.freeze
16
22
 
17
23
  attr_reader :store
@@ -96,6 +102,85 @@ module ClaudeMemory
96
102
  0
97
103
  end
98
104
 
105
+ # Restore superseded facts in a (subject, predicate) slot that were
106
+ # only superseded because of an obsolete single-value classification.
107
+ # Uses Jaccard-based token overlap to distinguish bug-superseded facts
108
+ # (token-disjoint siblings) from legitimate corrections (overlapping
109
+ # siblings).
110
+ #
111
+ # Refuses to run on predicates still classified as single-value — they
112
+ # should stay superseded by design.
113
+ #
114
+ # Never touches status: "rejected" facts (explicit user decisions).
115
+ #
116
+ # @return [Hash] {inspected, restored, skipped_ambiguous, skipped_rejected, decisions}
117
+ def restore_multi_value_supersessions(predicate:, dry_run: false)
118
+ if ClaudeMemory::Resolve::PredicatePolicy.single?(predicate)
119
+ raise ArgumentError, "Predicate '#{predicate}' is still classified single-value; refusing to restore"
120
+ end
121
+
122
+ result = {inspected: 0, restored: 0, skipped_ambiguous: 0, skipped_rejected: 0, decisions: []}
123
+
124
+ rows_by_subject = @store.facts
125
+ .where(predicate: predicate)
126
+ .exclude(status: "rejected")
127
+ .select(:id, :subject_entity_id, :object_literal, :status)
128
+ .all
129
+ .group_by { |r| r[:subject_entity_id] }
130
+
131
+ rejected_by_subject = @store.facts
132
+ .where(predicate: predicate, status: "rejected")
133
+ .select(:id, :subject_entity_id, :object_literal)
134
+ .all
135
+ .group_by { |r| r[:subject_entity_id] }
136
+
137
+ @store.db.transaction do
138
+ rows_by_subject.each do |subject_id, rows|
139
+ rejected_rows = rejected_by_subject[subject_id] || []
140
+ siblings = rows + rejected_rows
141
+
142
+ rows.each do |candidate|
143
+ next unless candidate[:status] == "superseded"
144
+ result[:inspected] += 1
145
+
146
+ candidate_tokens = restore_tokenize(candidate[:object_literal])
147
+ ambiguous_against = find_overlapping_siblings(candidate, siblings, candidate_tokens)
148
+
149
+ if ambiguous_against.empty?
150
+ result[:restored] += 1
151
+ result[:decisions] << {
152
+ subject_entity_id: subject_id,
153
+ fact_id: candidate[:id],
154
+ object: candidate[:object_literal],
155
+ action: :restore
156
+ }
157
+ restore_fact!(candidate[:id]) unless dry_run
158
+ else
159
+ result[:skipped_ambiguous] += 1
160
+ result[:decisions] << {
161
+ subject_entity_id: subject_id,
162
+ fact_id: candidate[:id],
163
+ object: candidate[:object_literal],
164
+ action: :skip_ambiguous,
165
+ overlaps_with: ambiguous_against.map { |s| s[:object_literal] }
166
+ }
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ result
173
+ end
174
+
175
+ # Delete MCP tool-call telemetry rows older than retention window.
176
+ # Returns: Integer count of deleted rows (0 if table missing).
177
+ def prune_old_mcp_tool_calls
178
+ return 0 unless @store.db.table_exists?(:mcp_tool_calls)
179
+
180
+ cutoff = cutoff_time(@config[:mcp_tool_call_retention_days])
181
+ @store.mcp_tool_calls.where { called_at < cutoff }.delete
182
+ end
183
+
99
184
  # Checkpoint the SQLite WAL file for compaction.
100
185
  # Returns: true
101
186
  def checkpoint_wal
@@ -112,6 +197,35 @@ module ClaudeMemory
112
197
 
113
198
  private
114
199
 
200
+ def restore_tokenize(text)
201
+ return Set.new if text.nil?
202
+ text.downcase
203
+ .scan(/[a-z0-9]+/)
204
+ .reject { |t| t.length <= 2 || RESTORE_STOPWORDS.include?(t) }
205
+ .to_set
206
+ end
207
+
208
+ def restore_jaccard(a, b)
209
+ return 0.0 if a.empty? && b.empty?
210
+ intersection = (a & b).size
211
+ union = (a | b).size
212
+ return 0.0 if union.zero?
213
+ intersection.to_f / union
214
+ end
215
+
216
+ def find_overlapping_siblings(candidate, siblings, candidate_tokens)
217
+ siblings.select do |other|
218
+ next false if other[:id] == candidate[:id]
219
+ other_tokens = restore_tokenize(other[:object_literal])
220
+ restore_jaccard(candidate_tokens, other_tokens) >= RESTORE_JACCARD_THRESHOLD
221
+ end
222
+ end
223
+
224
+ def restore_fact!(fact_id)
225
+ @store.facts.where(id: fact_id).update(status: "active", valid_to: nil)
226
+ @store.fact_links.where(to_fact_id: fact_id, link_type: "supersedes").delete
227
+ end
228
+
115
229
  def cutoff_time(days)
116
230
  (Time.now - days * 86400).utc.iso8601
117
231
  end
@@ -7,6 +7,7 @@ module ClaudeMemory
7
7
  proposed_fact_ttl_days: 14,
8
8
  disputed_fact_ttl_days: 30,
9
9
  content_retention_days: 30,
10
+ mcp_tool_call_retention_days: 90,
10
11
  default_budget_seconds: 5
11
12
  }.freeze
12
13
 
@@ -29,6 +30,7 @@ module ClaudeMemory
29
30
  disputed_facts_expired: 0,
30
31
  orphaned_provenance_deleted: 0,
31
32
  old_content_pruned: 0,
33
+ mcp_tool_calls_pruned: 0,
32
34
  escalation_level: :normal
33
35
  }
34
36
 
@@ -38,6 +40,7 @@ module ClaudeMemory
38
40
  run_if_within_budget { @stats[:disputed_facts_expired] = maintenance.expire_disputed_facts }
39
41
  run_if_within_budget { @stats[:orphaned_provenance_deleted] = maintenance.prune_orphaned_provenance }
40
42
  run_if_within_budget { @stats[:old_content_pruned] = maintenance.prune_old_content }
43
+ run_if_within_budget { @stats[:mcp_tool_calls_pruned] = maintenance.prune_old_mcp_tool_calls }
41
44
  run_if_within_budget { @stats[:vec_backfilled] = maintenance.backfill_vec_index }
42
45
  run_if_within_budget { @stats[:vec_cleaned] = maintenance.cleanup_vec_expired }
43
46
  run_if_within_budget { @stats[:wal_checkpointed] = maintenance.checkpoint_wal }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeMemory
4
- VERSION = "0.8.0"
4
+ VERSION = "0.9.0"
5
5
  end
data/lib/claude_memory.rb CHANGED
@@ -70,6 +70,9 @@ require_relative "claude_memory/commands/export_command"
70
70
  require_relative "claude_memory/commands/git_lfs_command"
71
71
  require_relative "claude_memory/commands/install_skill_command"
72
72
  require_relative "claude_memory/commands/completion_command"
73
+ require_relative "claude_memory/commands/embeddings_command"
74
+ require_relative "claude_memory/commands/reject_command"
75
+ require_relative "claude_memory/commands/restore_command"
73
76
  require_relative "claude_memory/commands/registry"
74
77
  require_relative "claude_memory/cli"
75
78
  require_relative "claude_memory/configuration"
@@ -80,6 +83,8 @@ require_relative "claude_memory/domain/fact"
80
83
  require_relative "claude_memory/domain/entity"
81
84
  require_relative "claude_memory/domain/provenance"
82
85
  require_relative "claude_memory/domain/conflict"
86
+ require_relative "claude_memory/embeddings/model_registry"
87
+ require_relative "claude_memory/embeddings/inspector"
83
88
  require_relative "claude_memory/embeddings/generator"
84
89
  require_relative "claude_memory/embeddings/fastembed_adapter"
85
90
  require_relative "claude_memory/embeddings/api_adapter"
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.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valentino Stoll
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '5.0'
18
+ version: '5.102'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '5.0'
25
+ version: '5.102'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: extralite
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -44,6 +44,9 @@ dependencies:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0.1'
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 0.1.9
47
50
  type: :runtime
48
51
  prerelease: false
49
52
  version_requirements: !ruby/object:Gem::Requirement
@@ -51,8 +54,13 @@ dependencies:
51
54
  - - "~>"
52
55
  - !ruby/object:Gem::Version
53
56
  version: '0.1'
54
- description: Turn-key Ruby gem providing Claude Code with instant, high-quality, long-term,
55
- self-managed memory using Claude Code Hooks + MCP + Output Style.
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 0.1.9
60
+ description: Gives Claude Code persistent memory across sessions. Claude recalls your
61
+ codebase architecture without file traversal, enforces project conventions during
62
+ code generation, tracks decisions with rationale, and remembers your preferences
63
+ across all projects. Zero-config via Hooks + MCP.
56
64
  email:
57
65
  - v@codenamev.com
58
66
  executables:
@@ -64,11 +72,11 @@ files:
64
72
  - ".claude-plugin/commands/memory-recall.md"
65
73
  - ".claude-plugin/marketplace.json"
66
74
  - ".claude-plugin/plugin.json"
75
+ - ".claude-plugin/scripts/hook-runner.sh"
76
+ - ".claude-plugin/scripts/serve-mcp.sh"
67
77
  - ".claude.json"
68
78
  - ".claude/CLAUDE.md"
69
79
  - ".claude/memory.sqlite3"
70
- - ".claude/memory.sqlite3-shm"
71
- - ".claude/memory.sqlite3-wal"
72
80
  - ".claude/output-styles/memory-aware.md"
73
81
  - ".claude/rules/claude_memory.generated.md"
74
82
  - ".claude/settings.json"
@@ -88,6 +96,7 @@ files:
88
96
  - ".claude/skills/study-repo/SKILL.md"
89
97
  - ".claude/skills/study-repo/analysis-template.md"
90
98
  - ".claude/skills/study-repo/focus-examples.md"
99
+ - ".claude/skills/upgrade-dependencies/SKILL.md"
91
100
  - ".gitattributes"
92
101
  - ".lefthook/map_specs.rb"
93
102
  - ".mcp.json"
@@ -114,6 +123,8 @@ files:
114
123
  - db/migrations/010_add_llm_cache.rb
115
124
  - db/migrations/011_add_tool_call_summaries.rb
116
125
  - db/migrations/012_add_vec_indexing_support.rb
126
+ - db/migrations/013_add_mcp_tool_calls.rb
127
+ - db/migrations/014_canonicalize_predicates.rb
117
128
  - docs/EXAMPLES.md
118
129
  - docs/GETTING_STARTED.md
119
130
  - docs/RELEASE_NOTES_v0.2.0.md
@@ -163,6 +174,7 @@ files:
163
174
  - lib/claude_memory/commands/conflicts_command.rb
164
175
  - lib/claude_memory/commands/db_init_command.rb
165
176
  - lib/claude_memory/commands/doctor_command.rb
177
+ - lib/claude_memory/commands/embeddings_command.rb
166
178
  - lib/claude_memory/commands/explain_command.rb
167
179
  - lib/claude_memory/commands/export_command.rb
168
180
  - lib/claude_memory/commands/git_lfs_command.rb
@@ -183,6 +195,8 @@ files:
183
195
  - lib/claude_memory/commands/recall_command.rb
184
196
  - lib/claude_memory/commands/recover_command.rb
185
197
  - lib/claude_memory/commands/registry.rb
198
+ - lib/claude_memory/commands/reject_command.rb
199
+ - lib/claude_memory/commands/restore_command.rb
186
200
  - lib/claude_memory/commands/search_command.rb
187
201
  - lib/claude_memory/commands/serve_mcp_command.rb
188
202
  - lib/claude_memory/commands/skills/distill-transcripts.md
@@ -225,6 +239,8 @@ files:
225
239
  - lib/claude_memory/embeddings/dimension_check.rb
226
240
  - lib/claude_memory/embeddings/fastembed_adapter.rb
227
241
  - lib/claude_memory/embeddings/generator.rb
242
+ - lib/claude_memory/embeddings/inspector.rb
243
+ - lib/claude_memory/embeddings/model_registry.rb
228
244
  - lib/claude_memory/embeddings/resolver.rb
229
245
  - lib/claude_memory/embeddings/similarity.rb
230
246
  - lib/claude_memory/hook/context_injector.rb
@@ -262,6 +278,7 @@ files:
262
278
  - lib/claude_memory/mcp/response_formatter.rb
263
279
  - lib/claude_memory/mcp/server.rb
264
280
  - lib/claude_memory/mcp/setup_status_analyzer.rb
281
+ - lib/claude_memory/mcp/telemetry.rb
265
282
  - lib/claude_memory/mcp/text_summary.rb
266
283
  - lib/claude_memory/mcp/tool_definitions.rb
267
284
  - lib/claude_memory/mcp/tool_helpers.rb
@@ -318,5 +335,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
318
335
  requirements: []
319
336
  rubygems_version: 4.0.6
320
337
  specification_version: 4
321
- summary: Long-term, self-managed memory for Claude Code
338
+ summary: Long-term memory for Claude Code — architecture recall, convention enforcement,
339
+ decision tracking
322
340
  test_files: []
Binary file
Binary file