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.
- checksums.yaml +4 -4
- data/.claude/memory.sqlite3 +0 -0
- data/.claude/rules/claude_memory.generated.md +63 -1
- data/.claude/skills/dashboard/SKILL.md +42 -0
- data/.claude/skills/release/SKILL.md +168 -0
- data/.claude-plugin/marketplace.json +1 -1
- data/.claude-plugin/plugin.json +1 -1
- data/CHANGELOG.md +92 -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/server.rb +8 -2
- 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 +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 }
|
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,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
|