claude_memory 0.9.0 → 0.10.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/memory.sqlite3 +0 -0
  3. data/.claude/rules/claude_memory.generated.md +63 -1
  4. data/.claude/skills/dashboard/SKILL.md +42 -0
  5. data/.claude/skills/release/SKILL.md +168 -0
  6. data/.claude-plugin/marketplace.json +1 -1
  7. data/.claude-plugin/plugin.json +1 -1
  8. data/CHANGELOG.md +92 -0
  9. data/CLAUDE.md +21 -5
  10. data/README.md +32 -2
  11. data/db/migrations/015_add_activity_events.rb +26 -0
  12. data/db/migrations/016_add_moment_feedback.rb +22 -0
  13. data/db/migrations/017_add_last_recalled_at.rb +15 -0
  14. data/docs/1_0_punchlist.md +190 -0
  15. data/docs/EXAMPLES.md +41 -2
  16. data/docs/GETTING_STARTED.md +31 -4
  17. data/docs/architecture.md +22 -7
  18. data/docs/audit-queries.md +131 -0
  19. data/docs/dashboard.md +172 -0
  20. data/docs/improvements.md +465 -9
  21. data/docs/influence/cq.md +187 -0
  22. data/docs/plugin.md +13 -6
  23. data/docs/quality_review.md +489 -172
  24. data/docs/reflection_memory_as_accumulating_judgment.md +67 -0
  25. data/lib/claude_memory/activity_log.rb +86 -0
  26. data/lib/claude_memory/commands/census_command.rb +210 -0
  27. data/lib/claude_memory/commands/completion_command.rb +3 -0
  28. data/lib/claude_memory/commands/dashboard_command.rb +54 -0
  29. data/lib/claude_memory/commands/dedupe_conflicts_command.rb +55 -0
  30. data/lib/claude_memory/commands/digest_command.rb +181 -0
  31. data/lib/claude_memory/commands/hook_command.rb +34 -0
  32. data/lib/claude_memory/commands/reclassify_references_command.rb +56 -0
  33. data/lib/claude_memory/commands/registry.rb +6 -1
  34. data/lib/claude_memory/commands/skills/distill-transcripts.md +13 -1
  35. data/lib/claude_memory/commands/stats_command.rb +38 -1
  36. data/lib/claude_memory/commands/sweep_command.rb +2 -0
  37. data/lib/claude_memory/configuration.rb +16 -0
  38. data/lib/claude_memory/core/relative_time.rb +9 -0
  39. data/lib/claude_memory/dashboard/api.rb +610 -0
  40. data/lib/claude_memory/dashboard/conflicts.rb +279 -0
  41. data/lib/claude_memory/dashboard/efficacy.rb +127 -0
  42. data/lib/claude_memory/dashboard/fact_presenter.rb +109 -0
  43. data/lib/claude_memory/dashboard/health.rb +175 -0
  44. data/lib/claude_memory/dashboard/index.html +2707 -0
  45. data/lib/claude_memory/dashboard/knowledge.rb +136 -0
  46. data/lib/claude_memory/dashboard/moments.rb +244 -0
  47. data/lib/claude_memory/dashboard/reuse.rb +97 -0
  48. data/lib/claude_memory/dashboard/scoped_fact_resolver.rb +95 -0
  49. data/lib/claude_memory/dashboard/server.rb +211 -0
  50. data/lib/claude_memory/dashboard/timeline.rb +68 -0
  51. data/lib/claude_memory/dashboard/trust.rb +285 -0
  52. data/lib/claude_memory/distill/reference_material_detector.rb +78 -0
  53. data/lib/claude_memory/hook/auto_memory_mirror.rb +112 -0
  54. data/lib/claude_memory/hook/context_injector.rb +97 -3
  55. data/lib/claude_memory/hook/handler.rb +50 -3
  56. data/lib/claude_memory/mcp/handlers/management_handlers.rb +8 -0
  57. data/lib/claude_memory/mcp/query_guide.rb +11 -0
  58. data/lib/claude_memory/mcp/server.rb +8 -2
  59. data/lib/claude_memory/mcp/text_summary.rb +29 -0
  60. data/lib/claude_memory/mcp/tool_definitions.rb +13 -0
  61. data/lib/claude_memory/mcp/tools.rb +148 -0
  62. data/lib/claude_memory/publish.rb +13 -21
  63. data/lib/claude_memory/recall/stale_detector.rb +67 -0
  64. data/lib/claude_memory/resolve/predicate_policy.rb +2 -0
  65. data/lib/claude_memory/resolve/resolver.rb +41 -11
  66. data/lib/claude_memory/store/llm_cache.rb +68 -0
  67. data/lib/claude_memory/store/metrics_aggregator.rb +96 -0
  68. data/lib/claude_memory/store/schema_manager.rb +1 -1
  69. data/lib/claude_memory/store/sqlite_store.rb +47 -143
  70. data/lib/claude_memory/store/store_manager.rb +29 -0
  71. data/lib/claude_memory/sweep/maintenance.rb +216 -0
  72. data/lib/claude_memory/sweep/recall_timestamp_refresher.rb +83 -0
  73. data/lib/claude_memory/sweep/sweeper.rb +2 -0
  74. data/lib/claude_memory/version.rb +1 -1
  75. data/lib/claude_memory.rb +22 -0
  76. metadata +50 -1
@@ -47,6 +47,74 @@ module ClaudeMemory
47
47
  .update(status: "expired")
48
48
  end
49
49
 
50
+ # Collapse duplicate multi-value facts. Before the resolver-level
51
+ # dedup fix (2026-04-17), multi-value predicates like uses_language
52
+ # and uses_framework accumulated identical rows every ingest cycle.
53
+ # For each (subject_entity_id, predicate, object_literal, scope) group
54
+ # with more than one active fact, keep the oldest row, copy the
55
+ # duplicates' provenance onto the keeper (so we retain source
56
+ # signal), and mark the duplicates superseded. Returns the count of
57
+ # fact rows merged into their keeper.
58
+ def dedupe_multi_value_facts
59
+ merged = 0
60
+ @store.db.transaction do
61
+ # Pull every active fact with a literal object and group in Ruby.
62
+ # Facts tables stay small (< 10k typical); Sequel's HAVING COUNT(*)
63
+ # path hits adapter quoting bugs on some Extralite versions.
64
+ active = @store.facts
65
+ .where(status: "active")
66
+ .exclude(subject_entity_id: nil)
67
+ .exclude(object_literal: nil)
68
+ .order(:id)
69
+ .all
70
+
71
+ groups = active.group_by { |f|
72
+ [f[:subject_entity_id], f[:predicate], f[:object_literal]&.downcase, f[:scope]]
73
+ }
74
+
75
+ groups.each_value do |rows|
76
+ next if rows.size < 2
77
+
78
+ keeper = rows.first
79
+ rows[1..].each do |loser|
80
+ @store.provenance.where(fact_id: loser[:id]).update(fact_id: keeper[:id])
81
+ @store.facts.where(id: loser[:id]).update(
82
+ status: "superseded",
83
+ valid_to: Time.now.utc.iso8601
84
+ )
85
+ @store.insert_fact_link(from_fact_id: keeper[:id], to_fact_id: loser[:id], link_type: "supersedes")
86
+ merged += 1
87
+ end
88
+ end
89
+ end
90
+ merged
91
+ end
92
+
93
+ # Fix scope leakage: facts whose `scope` column disagrees with the
94
+ # store they live in. Pre-2026-04-20, the resolver treated
95
+ # scope_hint from the distiller as a scope override — so when the
96
+ # NullDistiller detected global-scope language ("always", "my
97
+ # preference"), it stamped scope: "global" on facts that still
98
+ # ended up written to the project DB. The result was invisible
99
+ # orphaned rows: not in the global DB so global recall never saw
100
+ # them, but labeled global inside the project DB.
101
+ #
102
+ # This pass detects those rows by comparing `scope` to the
103
+ # expected value derived from which DB this Maintenance instance
104
+ # is running against, and rewrites scope + project_path to match.
105
+ # Does not move facts between DBs — users can `claude-memory
106
+ # promote <id>` to do a proper cross-store copy.
107
+ # Returns: Integer count of facts whose scope was corrected.
108
+ def fix_scope_leakage
109
+ expected = expected_scope_for_store
110
+ return 0 unless expected
111
+
112
+ project_path_for_scope = (expected == "global") ? nil : detect_project_path
113
+ @store.facts
114
+ .exclude(scope: expected)
115
+ .update(scope: expected, project_path: project_path_for_scope)
116
+ end
117
+
50
118
  # Delete provenance records referencing non-existent facts.
51
119
  # Returns: Integer count of deleted provenance rows
52
120
  def prune_orphaned_provenance
@@ -188,6 +256,114 @@ module ClaudeMemory
188
256
  true
189
257
  end
190
258
 
259
+ # Deduplicate open conflicts that describe the same contradiction.
260
+ # Before the Resolver#apply_conflict dedupe fix (2026-04-24), each
261
+ # re-extraction of the losing value in a single-value slot produced
262
+ # a new disputed fact + conflict row — production DBs accumulated 11
263
+ # open conflicts for "sqlite vs postgresql" referencing 11 different
264
+ # disputed facts. This pass keeps the earliest conflict per logical
265
+ # pair and marks the rest resolved, reinforcing the keeper's
266
+ # provenance chain with the duplicates' provenance.
267
+ #
268
+ # Pair key: (subject_entity_id, predicate, normalized(object_a), normalized(object_b))
269
+ # with object order sorted so A-vs-B == B-vs-A.
270
+ #
271
+ # @param dry_run [Boolean] when true, decide but don't write
272
+ # @return [Hash] {inspected:, resolved:, decisions: [{conflict_id:, action:, keeper_id:}]}
273
+ def dedupe_open_conflicts(dry_run: false)
274
+ result = {inspected: 0, resolved: 0, decisions: []}
275
+
276
+ open_rows = @store.conflicts
277
+ .where(status: "open")
278
+ .order(:id)
279
+ .all
280
+ return result if open_rows.empty?
281
+
282
+ fact_ids = open_rows.flat_map { |r| [r[:fact_a_id], r[:fact_b_id]] }.uniq
283
+ facts = @store.facts
284
+ .where(id: fact_ids)
285
+ .select(:id, :subject_entity_id, :predicate, :object_literal, :status)
286
+ .all
287
+ .to_h { |f| [f[:id], f] }
288
+
289
+ @store.db.transaction do
290
+ groups = open_rows.group_by { |row| pair_key(row, facts) }.reject { |key, _| key.nil? }
291
+ groups.each_value do |rows_in_group|
292
+ result[:inspected] += rows_in_group.size
293
+ next if rows_in_group.size < 2
294
+
295
+ keeper = rows_in_group.first
296
+ duplicates = rows_in_group[1..]
297
+ duplicates.each do |dup|
298
+ result[:decisions] << {
299
+ conflict_id: dup[:id],
300
+ action: :resolve_duplicate,
301
+ keeper_id: keeper[:id],
302
+ duplicate_fact_id: dup[:fact_b_id]
303
+ }
304
+ # Counted whether or not we actually write, so dry-run output
305
+ # matches real-run output and callers can compare plans.
306
+ result[:resolved] += 1
307
+ next if dry_run
308
+
309
+ # Resolve the duplicate conflict. Also reject its disputed
310
+ # side (fact_b_id is always the newer inserted-as-disputed
311
+ # fact per Resolver convention), and shift its provenance
312
+ # onto the keeper's fact_b so the evidence isn't lost.
313
+ keeper_fact_b_id = keeper[:fact_b_id]
314
+ if dup[:fact_b_id] != keeper_fact_b_id
315
+ @store.provenance.where(fact_id: dup[:fact_b_id]).update(fact_id: keeper_fact_b_id)
316
+ @store.facts.where(id: dup[:fact_b_id]).update(
317
+ status: "rejected",
318
+ valid_to: Time.now.utc.iso8601
319
+ )
320
+ end
321
+ @store.conflicts.where(id: dup[:id]).update(
322
+ status: "resolved",
323
+ notes: "Deduplicated into conflict ##{keeper[:id]}"
324
+ )
325
+ end
326
+ end
327
+ end
328
+
329
+ result
330
+ end
331
+
332
+ # Reclassify active facts currently labeled `convention` whose object
333
+ # text matches the ReferenceMaterialDetector heuristics. Fixes the
334
+ # historical data tail from before the detector was wired into
335
+ # `store_extraction` on 2026-04-24. Current writes can't create this
336
+ # pattern — this pass only cleans up what already exists.
337
+ #
338
+ # @param dry_run [Boolean] when true, decide but don't write
339
+ # @return [Hash] {inspected:, reclassified:, decisions: [{fact_id:, object:}]}
340
+ def reclassify_references(dry_run: false)
341
+ detector = ClaudeMemory::Distill::ReferenceMaterialDetector.new
342
+ result = {inspected: 0, reclassified: 0, decisions: []}
343
+
344
+ candidates = @store.facts
345
+ .where(status: "active", predicate: "convention")
346
+ .select(:id, :object_literal)
347
+ .all
348
+
349
+ @store.db.transaction do
350
+ candidates.each do |row|
351
+ result[:inspected] += 1
352
+ fact = {predicate: "convention", object: row[:object_literal]}
353
+ next unless detector.reference_material?(fact)
354
+
355
+ result[:decisions] << {fact_id: row[:id], object: row[:object_literal]}
356
+ result[:reclassified] += 1
357
+
358
+ unless dry_run
359
+ @store.facts.where(id: row[:id]).update(predicate: "reference")
360
+ end
361
+ end
362
+ end
363
+
364
+ result
365
+ end
366
+
191
367
  # Run SQLite VACUUM to reclaim space.
192
368
  # Returns: true
193
369
  def vacuum
@@ -197,6 +373,20 @@ module ClaudeMemory
197
373
 
198
374
  private
199
375
 
376
+ # Canonical key for grouping open conflicts. Two conflicts are the
377
+ # "same" when they involve the same subject, predicate, and set of
378
+ # objects (A-vs-B == B-vs-A). Missing-fact conflicts (either side
379
+ # deleted) get a nil key and are skipped by the caller.
380
+ def pair_key(conflict_row, facts_by_id)
381
+ a = facts_by_id[conflict_row[:fact_a_id]]
382
+ b = facts_by_id[conflict_row[:fact_b_id]]
383
+ return nil unless a && b
384
+ return nil unless a[:subject_entity_id] == b[:subject_entity_id]
385
+ return nil unless a[:predicate] == b[:predicate]
386
+ objects = [a[:object_literal].to_s.downcase.strip, b[:object_literal].to_s.downcase.strip].sort
387
+ [a[:subject_entity_id], a[:predicate], objects]
388
+ end
389
+
200
390
  def restore_tokenize(text)
201
391
  return Set.new if text.nil?
202
392
  text.downcase
@@ -230,6 +420,32 @@ module ClaudeMemory
230
420
  (Time.now - days * 86400).utc.iso8601
231
421
  end
232
422
 
423
+ # Infer the scope each store is supposed to carry by comparing its
424
+ # DB path to the canonical Configuration paths. Returns "global" for
425
+ # the user-wide DB, "project" for the per-project DB, or nil when
426
+ # the path doesn't match either (custom test paths, etc. — in which
427
+ # case fix_scope_leakage is a no-op).
428
+ def expected_scope_for_store
429
+ path = @store.db.opts[:database].to_s
430
+ return nil if path.empty?
431
+ config = ClaudeMemory::Configuration.new
432
+ return "global" if File.expand_path(path) == File.expand_path(config.global_db_path)
433
+
434
+ # Project DB lives at <project>/.claude/memory.sqlite3 — we can
435
+ # always check whether the DB path sits under a .claude directory
436
+ # to classify it as project-scoped regardless of which project.
437
+ File.dirname(File.expand_path(path)).end_with?("/.claude") ? "project" : nil
438
+ end
439
+
440
+ def detect_project_path
441
+ path = @store.db.opts[:database].to_s
442
+ return nil if path.empty?
443
+ # The canonical project DB lives at <project>/.claude/memory.sqlite3
444
+ # so the project path is two levels up.
445
+ project = File.dirname(File.expand_path(path), 2)
446
+ Dir.exist?(project) ? project : nil
447
+ end
448
+
233
449
  def with_vec_index
234
450
  vec_index = @store.vector_index
235
451
  return unless vec_index.available?
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module ClaudeMemory
6
+ module Sweep
7
+ # Path B for #35 access-based staleness — sweep-derived rather than
8
+ # per-recall written. Scans activity_events from both stores, projects
9
+ # the most recent recall/context-injection touch per (scope, fact_id),
10
+ # and bulk-updates facts.last_recalled_at across both DBs.
11
+ #
12
+ # Cross-DB by design: project DBs record activity_events for both
13
+ # project and global facts (a recall fired from a project context that
14
+ # returns global facts is logged in the project DB), so a per-store
15
+ # refresh would silently miss global facts entirely.
16
+ #
17
+ # Lookback bounds keep the scan O(window), not O(history).
18
+ class RecallTimestampRefresher
19
+ DEFAULT_LOOKBACK_DAYS = 90
20
+ RECALL_EVENT_TYPES = %w[recall hook_context].freeze
21
+
22
+ def initialize(manager, lookback_days: DEFAULT_LOOKBACK_DAYS)
23
+ @manager = manager
24
+ @lookback_days = lookback_days
25
+ end
26
+
27
+ # @return [Hash] {project: Int, global: Int} — count of facts updated per scope.
28
+ def refresh!
29
+ cutoff = (Time.now.utc - @lookback_days * 86_400).iso8601
30
+ latest = collect_latest_per_fact(cutoff)
31
+ apply_to_stores(latest)
32
+ end
33
+
34
+ private
35
+
36
+ # Scans every activity_events table available to the manager and
37
+ # returns {[scope, fact_id] => latest_occurred_at}.
38
+ def collect_latest_per_fact(cutoff)
39
+ latest = {}
40
+ %w[project global].each do |source|
41
+ store = @manager.store_if_exists(source)
42
+ next unless store
43
+ rows = store.activity_events
44
+ .where(event_type: RECALL_EVENT_TYPES)
45
+ .where { occurred_at >= cutoff }
46
+ .select(:occurred_at, :detail_json)
47
+ .all
48
+ rows.each do |row|
49
+ details = parse_details(row[:detail_json])
50
+ scoped = Dashboard::ScopedFactResolver.scoped_ids_from_details(details)
51
+ scoped.each do |scope, ids|
52
+ ids.each do |fact_id|
53
+ key = [scope.to_s, fact_id]
54
+ existing = latest[key]
55
+ latest[key] = row[:occurred_at] if existing.nil? || row[:occurred_at] > existing
56
+ end
57
+ end
58
+ end
59
+ end
60
+ latest
61
+ end
62
+
63
+ def parse_details(detail_json)
64
+ return {} if detail_json.nil? || detail_json.empty?
65
+ JSON.parse(detail_json, symbolize_names: true)
66
+ rescue JSON::ParserError
67
+ {}
68
+ end
69
+
70
+ def apply_to_stores(latest)
71
+ counts = {project: 0, global: 0}
72
+ latest.group_by { |(scope, _id), _ts| scope }.each do |scope, entries|
73
+ store = @manager.store_if_exists(scope)
74
+ next unless store
75
+ entries.each do |((_scope, fact_id), ts)|
76
+ counts[scope.to_sym] += store.facts.where(id: fact_id).update(last_recalled_at: ts)
77
+ end
78
+ end
79
+ counts
80
+ end
81
+ end
82
+ end
83
+ end
@@ -38,6 +38,8 @@ module ClaudeMemory
38
38
 
39
39
  run_if_within_budget { @stats[:proposed_facts_expired] = maintenance.expire_proposed_facts }
40
40
  run_if_within_budget { @stats[:disputed_facts_expired] = maintenance.expire_disputed_facts }
41
+ run_if_within_budget { @stats[:multi_value_facts_merged] = maintenance.dedupe_multi_value_facts }
42
+ run_if_within_budget { @stats[:scope_leakage_fixed] = maintenance.fix_scope_leakage }
41
43
  run_if_within_budget { @stats[:orphaned_provenance_deleted] = maintenance.prune_orphaned_provenance }
42
44
  run_if_within_budget { @stats[:old_content_pruned] = maintenance.prune_old_content }
43
45
  run_if_within_budget { @stats[:mcp_tool_calls_pruned] = maintenance.prune_old_mcp_tool_calls }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeMemory
4
- VERSION = "0.9.0"
4
+ VERSION = "0.10.0"
5
5
  end
data/lib/claude_memory.rb CHANGED
@@ -73,12 +73,30 @@ require_relative "claude_memory/commands/completion_command"
73
73
  require_relative "claude_memory/commands/embeddings_command"
74
74
  require_relative "claude_memory/commands/reject_command"
75
75
  require_relative "claude_memory/commands/restore_command"
76
+ require_relative "claude_memory/commands/dedupe_conflicts_command"
77
+ require_relative "claude_memory/commands/reclassify_references_command"
78
+ require_relative "claude_memory/commands/census_command"
79
+ require_relative "claude_memory/commands/dashboard_command"
80
+ require_relative "claude_memory/dashboard/fact_presenter"
81
+ require_relative "claude_memory/dashboard/scoped_fact_resolver"
82
+ require_relative "claude_memory/dashboard/conflicts"
83
+ require_relative "claude_memory/dashboard/efficacy"
84
+ require_relative "claude_memory/dashboard/moments"
85
+ require_relative "claude_memory/dashboard/trust"
86
+ require_relative "claude_memory/dashboard/knowledge"
87
+ require_relative "claude_memory/dashboard/reuse"
88
+ require_relative "claude_memory/dashboard/timeline"
89
+ require_relative "claude_memory/dashboard/health"
90
+ require_relative "claude_memory/dashboard/api"
91
+ require_relative "claude_memory/dashboard/server"
92
+ require_relative "claude_memory/commands/digest_command"
76
93
  require_relative "claude_memory/commands/registry"
77
94
  require_relative "claude_memory/cli"
78
95
  require_relative "claude_memory/configuration"
79
96
  require_relative "claude_memory/distill/distiller"
80
97
  require_relative "claude_memory/distill/extraction"
81
98
  require_relative "claude_memory/distill/null_distiller"
99
+ require_relative "claude_memory/distill/reference_material_detector"
82
100
  require_relative "claude_memory/domain/fact"
83
101
  require_relative "claude_memory/domain/entity"
84
102
  require_relative "claude_memory/domain/provenance"
@@ -91,6 +109,7 @@ require_relative "claude_memory/embeddings/api_adapter"
91
109
  require_relative "claude_memory/embeddings/dimension_check"
92
110
  require_relative "claude_memory/embeddings/resolver"
93
111
  require_relative "claude_memory/embeddings/similarity"
112
+ require_relative "claude_memory/hook/auto_memory_mirror"
94
113
  require_relative "claude_memory/hook/context_injector"
95
114
  require_relative "claude_memory/hook/distillation_runner"
96
115
  require_relative "claude_memory/hook/exit_codes"
@@ -109,6 +128,7 @@ require_relative "claude_memory/ingest/tool_extractor"
109
128
  require_relative "claude_memory/ingest/tool_filter"
110
129
  require_relative "claude_memory/ingest/ingester"
111
130
  require_relative "claude_memory/ingest/transcript_reader"
131
+ require_relative "claude_memory/activity_log"
112
132
  require_relative "claude_memory/logging/logger"
113
133
  require_relative "claude_memory/infrastructure/file_system"
114
134
  require_relative "claude_memory/infrastructure/in_memory_file_system"
@@ -126,6 +146,7 @@ require_relative "claude_memory/recall/expansion_detector"
126
146
  require_relative "claude_memory/recall/query_core"
127
147
  require_relative "claude_memory/recall/legacy_engine"
128
148
  require_relative "claude_memory/recall/dual_engine"
149
+ require_relative "claude_memory/recall/stale_detector"
129
150
  require_relative "claude_memory/recall"
130
151
  require_relative "claude_memory/shortcuts"
131
152
  require_relative "claude_memory/resolve/predicate_policy"
@@ -134,6 +155,7 @@ require_relative "claude_memory/store/sqlite_store"
134
155
  require_relative "claude_memory/store/store_manager"
135
156
  require_relative "claude_memory/sweep/maintenance"
136
157
  require_relative "claude_memory/sweep/sweeper"
158
+ require_relative "claude_memory/sweep/recall_timestamp_refresher"
137
159
  require_relative "claude_memory/version"
138
160
 
139
161
  module ClaudeMemory
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.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valentino Stoll
@@ -57,6 +57,20 @@ dependencies:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
59
  version: 0.1.9
60
+ - !ruby/object:Gem::Dependency
61
+ name: webrick
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '1.8'
67
+ type: :runtime
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '1.8'
60
74
  description: Gives Claude Code persistent memory across sessions. Claude recalls your
61
75
  codebase architecture without file traversal, enforces project conventions during
62
76
  code generation, tracks decisions with rationale, and remembers your preferences
@@ -83,12 +97,14 @@ files:
83
97
  - ".claude/settings.local.json"
84
98
  - ".claude/skills/check-memory/DEPRECATED.md"
85
99
  - ".claude/skills/check-memory/SKILL.md"
100
+ - ".claude/skills/dashboard/SKILL.md"
86
101
  - ".claude/skills/debug-memory"
87
102
  - ".claude/skills/improve/SKILL.md"
88
103
  - ".claude/skills/improve/feature-patterns.md"
89
104
  - ".claude/skills/memory-first-workflow"
90
105
  - ".claude/skills/quality-update/SKILL.md"
91
106
  - ".claude/skills/quality-update/implementation-guide.md"
107
+ - ".claude/skills/release/SKILL.md"
92
108
  - ".claude/skills/review-commit/SKILL.md"
93
109
  - ".claude/skills/review-for-quality/SKILL.md"
94
110
  - ".claude/skills/review-for-quality/expert-checklists.md"
@@ -125,14 +141,20 @@ files:
125
141
  - db/migrations/012_add_vec_indexing_support.rb
126
142
  - db/migrations/013_add_mcp_tool_calls.rb
127
143
  - db/migrations/014_canonicalize_predicates.rb
144
+ - db/migrations/015_add_activity_events.rb
145
+ - db/migrations/016_add_moment_feedback.rb
146
+ - db/migrations/017_add_last_recalled_at.rb
147
+ - docs/1_0_punchlist.md
128
148
  - docs/EXAMPLES.md
129
149
  - docs/GETTING_STARTED.md
130
150
  - docs/RELEASE_NOTES_v0.2.0.md
131
151
  - docs/RUBY_COMMUNITY_POST_v0.2.0.md
132
152
  - docs/SOCIAL_MEDIA_v0.2.0.md
133
153
  - docs/architecture.md
154
+ - docs/audit-queries.md
134
155
  - docs/auto_init_design.md
135
156
  - docs/ci_integration.md
157
+ - docs/dashboard.md
136
158
  - docs/demo.md
137
159
  - docs/eval_week1_summary.md
138
160
  - docs/eval_week2_summary.md
@@ -142,6 +164,7 @@ files:
142
164
  - docs/influence/.gitkeep
143
165
  - docs/influence/claude-mem.md
144
166
  - docs/influence/claude-supermemory.md
167
+ - docs/influence/cq.md
145
168
  - docs/influence/episodic-memory.md
146
169
  - docs/influence/grepai.md
147
170
  - docs/influence/kbs.md
@@ -154,13 +177,16 @@ files:
154
177
  - docs/plans/updated_plan.md
155
178
  - docs/plugin.md
156
179
  - docs/quality_review.md
180
+ - docs/reflection_memory_as_accumulating_judgment.md
157
181
  - docs/review_summary.md
158
182
  - exe/claude-memory
159
183
  - hooks/hooks.json
160
184
  - lefthook.yml
161
185
  - lib/claude_memory.rb
186
+ - lib/claude_memory/activity_log.rb
162
187
  - lib/claude_memory/cli.rb
163
188
  - lib/claude_memory/commands/base_command.rb
189
+ - lib/claude_memory/commands/census_command.rb
164
190
  - lib/claude_memory/commands/changes_command.rb
165
191
  - lib/claude_memory/commands/checks/claude_md_check.rb
166
192
  - lib/claude_memory/commands/checks/database_check.rb
@@ -172,7 +198,10 @@ files:
172
198
  - lib/claude_memory/commands/compact_command.rb
173
199
  - lib/claude_memory/commands/completion_command.rb
174
200
  - lib/claude_memory/commands/conflicts_command.rb
201
+ - lib/claude_memory/commands/dashboard_command.rb
175
202
  - lib/claude_memory/commands/db_init_command.rb
203
+ - lib/claude_memory/commands/dedupe_conflicts_command.rb
204
+ - lib/claude_memory/commands/digest_command.rb
176
205
  - lib/claude_memory/commands/doctor_command.rb
177
206
  - lib/claude_memory/commands/embeddings_command.rb
178
207
  - lib/claude_memory/commands/explain_command.rb
@@ -193,6 +222,7 @@ files:
193
222
  - lib/claude_memory/commands/promote_command.rb
194
223
  - lib/claude_memory/commands/publish_command.rb
195
224
  - lib/claude_memory/commands/recall_command.rb
225
+ - lib/claude_memory/commands/reclassify_references_command.rb
196
226
  - lib/claude_memory/commands/recover_command.rb
197
227
  - lib/claude_memory/commands/registry.rb
198
228
  - lib/claude_memory/commands/reject_command.rb
@@ -227,10 +257,24 @@ files:
227
257
  - lib/claude_memory/core/text_builder.rb
228
258
  - lib/claude_memory/core/token_estimator.rb
229
259
  - lib/claude_memory/core/transcript_path.rb
260
+ - lib/claude_memory/dashboard/api.rb
261
+ - lib/claude_memory/dashboard/conflicts.rb
262
+ - lib/claude_memory/dashboard/efficacy.rb
263
+ - lib/claude_memory/dashboard/fact_presenter.rb
264
+ - lib/claude_memory/dashboard/health.rb
265
+ - lib/claude_memory/dashboard/index.html
266
+ - lib/claude_memory/dashboard/knowledge.rb
267
+ - lib/claude_memory/dashboard/moments.rb
268
+ - lib/claude_memory/dashboard/reuse.rb
269
+ - lib/claude_memory/dashboard/scoped_fact_resolver.rb
270
+ - lib/claude_memory/dashboard/server.rb
271
+ - lib/claude_memory/dashboard/timeline.rb
272
+ - lib/claude_memory/dashboard/trust.rb
230
273
  - lib/claude_memory/distill/distiller.rb
231
274
  - lib/claude_memory/distill/extraction.rb
232
275
  - lib/claude_memory/distill/json_schema.md
233
276
  - lib/claude_memory/distill/null_distiller.rb
277
+ - lib/claude_memory/distill/reference_material_detector.rb
234
278
  - lib/claude_memory/domain/conflict.rb
235
279
  - lib/claude_memory/domain/entity.rb
236
280
  - lib/claude_memory/domain/fact.rb
@@ -243,6 +287,7 @@ files:
243
287
  - lib/claude_memory/embeddings/model_registry.rb
244
288
  - lib/claude_memory/embeddings/resolver.rb
245
289
  - lib/claude_memory/embeddings/similarity.rb
290
+ - lib/claude_memory/hook/auto_memory_mirror.rb
246
291
  - lib/claude_memory/hook/context_injector.rb
247
292
  - lib/claude_memory/hook/distillation_runner.rb
248
293
  - lib/claude_memory/hook/error_classifier.rb
@@ -290,14 +335,18 @@ files:
290
335
  - lib/claude_memory/recall/expansion_detector.rb
291
336
  - lib/claude_memory/recall/legacy_engine.rb
292
337
  - lib/claude_memory/recall/query_core.rb
338
+ - lib/claude_memory/recall/stale_detector.rb
293
339
  - lib/claude_memory/resolve/predicate_policy.rb
294
340
  - lib/claude_memory/resolve/resolver.rb
295
341
  - lib/claude_memory/shortcuts.rb
342
+ - lib/claude_memory/store/llm_cache.rb
343
+ - lib/claude_memory/store/metrics_aggregator.rb
296
344
  - lib/claude_memory/store/retry_handler.rb
297
345
  - lib/claude_memory/store/schema_manager.rb
298
346
  - lib/claude_memory/store/sqlite_store.rb
299
347
  - lib/claude_memory/store/store_manager.rb
300
348
  - lib/claude_memory/sweep/maintenance.rb
349
+ - lib/claude_memory/sweep/recall_timestamp_refresher.rb
301
350
  - lib/claude_memory/sweep/sweeper.rb
302
351
  - lib/claude_memory/templates/hooks.example.json
303
352
  - lib/claude_memory/templates/output-styles/memory-aware.md