claude_memory 0.9.1 → 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.
- checksums.yaml +4 -4
- data/.claude/memory.sqlite3 +0 -0
- data/.claude/skills/dashboard/SKILL.md +42 -0
- data/.claude-plugin/marketplace.json +1 -1
- data/.claude-plugin/plugin.json +1 -1
- data/CHANGELOG.md +86 -0
- data/CLAUDE.md +21 -5
- data/README.md +32 -2
- data/db/migrations/015_add_activity_events.rb +26 -0
- data/db/migrations/016_add_moment_feedback.rb +22 -0
- data/db/migrations/017_add_last_recalled_at.rb +15 -0
- data/docs/1_0_punchlist.md +190 -0
- data/docs/EXAMPLES.md +41 -2
- data/docs/GETTING_STARTED.md +31 -4
- data/docs/architecture.md +22 -7
- data/docs/audit-queries.md +131 -0
- data/docs/dashboard.md +172 -0
- data/docs/improvements.md +465 -9
- data/docs/influence/cq.md +187 -0
- data/docs/plugin.md +13 -6
- data/docs/quality_review.md +489 -172
- data/docs/reflection_memory_as_accumulating_judgment.md +67 -0
- data/lib/claude_memory/activity_log.rb +86 -0
- data/lib/claude_memory/commands/census_command.rb +210 -0
- data/lib/claude_memory/commands/completion_command.rb +3 -0
- data/lib/claude_memory/commands/dashboard_command.rb +54 -0
- data/lib/claude_memory/commands/dedupe_conflicts_command.rb +55 -0
- data/lib/claude_memory/commands/digest_command.rb +181 -0
- data/lib/claude_memory/commands/hook_command.rb +34 -0
- data/lib/claude_memory/commands/reclassify_references_command.rb +56 -0
- data/lib/claude_memory/commands/registry.rb +6 -1
- data/lib/claude_memory/commands/skills/distill-transcripts.md +13 -1
- data/lib/claude_memory/commands/stats_command.rb +38 -1
- data/lib/claude_memory/commands/sweep_command.rb +2 -0
- data/lib/claude_memory/configuration.rb +16 -0
- data/lib/claude_memory/core/relative_time.rb +9 -0
- data/lib/claude_memory/dashboard/api.rb +610 -0
- data/lib/claude_memory/dashboard/conflicts.rb +279 -0
- data/lib/claude_memory/dashboard/efficacy.rb +127 -0
- data/lib/claude_memory/dashboard/fact_presenter.rb +109 -0
- data/lib/claude_memory/dashboard/health.rb +175 -0
- data/lib/claude_memory/dashboard/index.html +2707 -0
- data/lib/claude_memory/dashboard/knowledge.rb +136 -0
- data/lib/claude_memory/dashboard/moments.rb +244 -0
- data/lib/claude_memory/dashboard/reuse.rb +97 -0
- data/lib/claude_memory/dashboard/scoped_fact_resolver.rb +95 -0
- data/lib/claude_memory/dashboard/server.rb +211 -0
- data/lib/claude_memory/dashboard/timeline.rb +68 -0
- data/lib/claude_memory/dashboard/trust.rb +285 -0
- data/lib/claude_memory/distill/reference_material_detector.rb +78 -0
- data/lib/claude_memory/hook/auto_memory_mirror.rb +112 -0
- data/lib/claude_memory/hook/context_injector.rb +97 -3
- data/lib/claude_memory/hook/handler.rb +50 -3
- data/lib/claude_memory/mcp/handlers/management_handlers.rb +8 -0
- data/lib/claude_memory/mcp/query_guide.rb +11 -0
- data/lib/claude_memory/mcp/text_summary.rb +29 -0
- data/lib/claude_memory/mcp/tool_definitions.rb +13 -0
- data/lib/claude_memory/mcp/tools.rb +148 -0
- data/lib/claude_memory/publish.rb +13 -21
- data/lib/claude_memory/recall/stale_detector.rb +67 -0
- data/lib/claude_memory/resolve/predicate_policy.rb +2 -0
- data/lib/claude_memory/resolve/resolver.rb +41 -11
- data/lib/claude_memory/store/llm_cache.rb +68 -0
- data/lib/claude_memory/store/metrics_aggregator.rb +96 -0
- data/lib/claude_memory/store/schema_manager.rb +1 -1
- data/lib/claude_memory/store/sqlite_store.rb +47 -143
- data/lib/claude_memory/store/store_manager.rb +29 -0
- data/lib/claude_memory/sweep/maintenance.rb +216 -0
- data/lib/claude_memory/sweep/recall_timestamp_refresher.rb +83 -0
- data/lib/claude_memory/sweep/sweeper.rb +2 -0
- data/lib/claude_memory/version.rb +1 -1
- data/lib/claude_memory.rb +22 -0
- metadata +49 -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 }
|
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.
|
|
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,6 +97,7 @@ 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"
|
|
@@ -126,14 +141,20 @@ files:
|
|
|
126
141
|
- db/migrations/012_add_vec_indexing_support.rb
|
|
127
142
|
- db/migrations/013_add_mcp_tool_calls.rb
|
|
128
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
|
|
129
148
|
- docs/EXAMPLES.md
|
|
130
149
|
- docs/GETTING_STARTED.md
|
|
131
150
|
- docs/RELEASE_NOTES_v0.2.0.md
|
|
132
151
|
- docs/RUBY_COMMUNITY_POST_v0.2.0.md
|
|
133
152
|
- docs/SOCIAL_MEDIA_v0.2.0.md
|
|
134
153
|
- docs/architecture.md
|
|
154
|
+
- docs/audit-queries.md
|
|
135
155
|
- docs/auto_init_design.md
|
|
136
156
|
- docs/ci_integration.md
|
|
157
|
+
- docs/dashboard.md
|
|
137
158
|
- docs/demo.md
|
|
138
159
|
- docs/eval_week1_summary.md
|
|
139
160
|
- docs/eval_week2_summary.md
|
|
@@ -143,6 +164,7 @@ files:
|
|
|
143
164
|
- docs/influence/.gitkeep
|
|
144
165
|
- docs/influence/claude-mem.md
|
|
145
166
|
- docs/influence/claude-supermemory.md
|
|
167
|
+
- docs/influence/cq.md
|
|
146
168
|
- docs/influence/episodic-memory.md
|
|
147
169
|
- docs/influence/grepai.md
|
|
148
170
|
- docs/influence/kbs.md
|
|
@@ -155,13 +177,16 @@ files:
|
|
|
155
177
|
- docs/plans/updated_plan.md
|
|
156
178
|
- docs/plugin.md
|
|
157
179
|
- docs/quality_review.md
|
|
180
|
+
- docs/reflection_memory_as_accumulating_judgment.md
|
|
158
181
|
- docs/review_summary.md
|
|
159
182
|
- exe/claude-memory
|
|
160
183
|
- hooks/hooks.json
|
|
161
184
|
- lefthook.yml
|
|
162
185
|
- lib/claude_memory.rb
|
|
186
|
+
- lib/claude_memory/activity_log.rb
|
|
163
187
|
- lib/claude_memory/cli.rb
|
|
164
188
|
- lib/claude_memory/commands/base_command.rb
|
|
189
|
+
- lib/claude_memory/commands/census_command.rb
|
|
165
190
|
- lib/claude_memory/commands/changes_command.rb
|
|
166
191
|
- lib/claude_memory/commands/checks/claude_md_check.rb
|
|
167
192
|
- lib/claude_memory/commands/checks/database_check.rb
|
|
@@ -173,7 +198,10 @@ files:
|
|
|
173
198
|
- lib/claude_memory/commands/compact_command.rb
|
|
174
199
|
- lib/claude_memory/commands/completion_command.rb
|
|
175
200
|
- lib/claude_memory/commands/conflicts_command.rb
|
|
201
|
+
- lib/claude_memory/commands/dashboard_command.rb
|
|
176
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
|
|
177
205
|
- lib/claude_memory/commands/doctor_command.rb
|
|
178
206
|
- lib/claude_memory/commands/embeddings_command.rb
|
|
179
207
|
- lib/claude_memory/commands/explain_command.rb
|
|
@@ -194,6 +222,7 @@ files:
|
|
|
194
222
|
- lib/claude_memory/commands/promote_command.rb
|
|
195
223
|
- lib/claude_memory/commands/publish_command.rb
|
|
196
224
|
- lib/claude_memory/commands/recall_command.rb
|
|
225
|
+
- lib/claude_memory/commands/reclassify_references_command.rb
|
|
197
226
|
- lib/claude_memory/commands/recover_command.rb
|
|
198
227
|
- lib/claude_memory/commands/registry.rb
|
|
199
228
|
- lib/claude_memory/commands/reject_command.rb
|
|
@@ -228,10 +257,24 @@ files:
|
|
|
228
257
|
- lib/claude_memory/core/text_builder.rb
|
|
229
258
|
- lib/claude_memory/core/token_estimator.rb
|
|
230
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
|
|
231
273
|
- lib/claude_memory/distill/distiller.rb
|
|
232
274
|
- lib/claude_memory/distill/extraction.rb
|
|
233
275
|
- lib/claude_memory/distill/json_schema.md
|
|
234
276
|
- lib/claude_memory/distill/null_distiller.rb
|
|
277
|
+
- lib/claude_memory/distill/reference_material_detector.rb
|
|
235
278
|
- lib/claude_memory/domain/conflict.rb
|
|
236
279
|
- lib/claude_memory/domain/entity.rb
|
|
237
280
|
- lib/claude_memory/domain/fact.rb
|
|
@@ -244,6 +287,7 @@ files:
|
|
|
244
287
|
- lib/claude_memory/embeddings/model_registry.rb
|
|
245
288
|
- lib/claude_memory/embeddings/resolver.rb
|
|
246
289
|
- lib/claude_memory/embeddings/similarity.rb
|
|
290
|
+
- lib/claude_memory/hook/auto_memory_mirror.rb
|
|
247
291
|
- lib/claude_memory/hook/context_injector.rb
|
|
248
292
|
- lib/claude_memory/hook/distillation_runner.rb
|
|
249
293
|
- lib/claude_memory/hook/error_classifier.rb
|
|
@@ -291,14 +335,18 @@ files:
|
|
|
291
335
|
- lib/claude_memory/recall/expansion_detector.rb
|
|
292
336
|
- lib/claude_memory/recall/legacy_engine.rb
|
|
293
337
|
- lib/claude_memory/recall/query_core.rb
|
|
338
|
+
- lib/claude_memory/recall/stale_detector.rb
|
|
294
339
|
- lib/claude_memory/resolve/predicate_policy.rb
|
|
295
340
|
- lib/claude_memory/resolve/resolver.rb
|
|
296
341
|
- lib/claude_memory/shortcuts.rb
|
|
342
|
+
- lib/claude_memory/store/llm_cache.rb
|
|
343
|
+
- lib/claude_memory/store/metrics_aggregator.rb
|
|
297
344
|
- lib/claude_memory/store/retry_handler.rb
|
|
298
345
|
- lib/claude_memory/store/schema_manager.rb
|
|
299
346
|
- lib/claude_memory/store/sqlite_store.rb
|
|
300
347
|
- lib/claude_memory/store/store_manager.rb
|
|
301
348
|
- lib/claude_memory/sweep/maintenance.rb
|
|
349
|
+
- lib/claude_memory/sweep/recall_timestamp_refresher.rb
|
|
302
350
|
- lib/claude_memory/sweep/sweeper.rb
|
|
303
351
|
- lib/claude_memory/templates/hooks.example.json
|
|
304
352
|
- lib/claude_memory/templates/output-styles/memory-aware.md
|