Class: ClaudeMemory::Hook::AutoMemoryMirror
- Inherits:
-
Object
- Object
- ClaudeMemory::Hook::AutoMemoryMirror
- Defined in:
- lib/claude_memory/hook/auto_memory_mirror.rb
Overview
Mirrors Claude Code auto-memory (~/.claude/projects/<slug>/memory/*.md) into extraction candidates surfaced alongside the SessionStart distillation prompt. Diffs files against a per-project state file (mtime+md5) so only new or changed entries are emitted. Idempotent — unchanged files are skipped on re-run.
The emission is a hint to Claude that auto-memory has content worth mirroring into claude_memory via ‘memory.store_extraction`. The mirror never writes facts itself; the normal extraction review flow still applies.
Constant Summary collapse
- MAX_CANDIDATES =
5- MAX_TEXT_PER_ITEM =
1500- STATE_FILENAME =
"auto_memory_mirror.json"
Class Method Summary collapse
-
.default_dir(project_path, claude_config_dir) ⇒ Object
Derive auto-memory directory from a project path using Claude Code’s slug convention (path separators → hyphens).
- .default_state_file(project_path) ⇒ Object
Instance Method Summary collapse
-
#commit(candidates) ⇒ Object
Record the given candidates as the new baseline so they won’t be re-emitted until their content changes.
-
#initialize(auto_memory_dir:, state_file:) ⇒ AutoMemoryMirror
constructor
A new instance of AutoMemoryMirror.
-
#pending_candidates(limit: MAX_CANDIDATES) ⇒ Array<Hash>
Candidate entries — each path:, content:, signature:.
Constructor Details
#initialize(auto_memory_dir:, state_file:) ⇒ AutoMemoryMirror
Returns a new instance of AutoMemoryMirror.
35 36 37 38 |
# File 'lib/claude_memory/hook/auto_memory_mirror.rb', line 35 def initialize(auto_memory_dir:, state_file:) @auto_memory_dir = auto_memory_dir @state_file = state_file end |
Class Method Details
.default_dir(project_path, claude_config_dir) ⇒ Object
Derive auto-memory directory from a project path using Claude Code’s slug convention (path separators → hyphens). E.g. ‘/Users/me/src/app` → `~/.claude/projects/-Users-me-src-app/memory`.
26 27 28 29 |
# File 'lib/claude_memory/hook/auto_memory_mirror.rb', line 26 def self.default_dir(project_path, claude_config_dir) slug = project_path.to_s.tr("/", "-") File.join(claude_config_dir, "projects", slug, "memory") end |
.default_state_file(project_path) ⇒ Object
31 32 33 |
# File 'lib/claude_memory/hook/auto_memory_mirror.rb', line 31 def self.default_state_file(project_path) File.join(project_path, ".claude", STATE_FILENAME) end |
Instance Method Details
#commit(candidates) ⇒ Object
Record the given candidates as the new baseline so they won’t be re-emitted until their content changes. Call only after the candidates have actually been surfaced to the user.
69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/claude_memory/hook/auto_memory_mirror.rb', line 69 def commit(candidates) return if candidates.empty? state = load_state candidates.each do |c| state[c[:name]] = {"md5" => c[:signature][:md5], "mtime" => c[:signature][:mtime]} end write_state(state) rescue => e ClaudeMemory.logger.warn("AutoMemoryMirror#commit failed: #{e.}") end |
#pending_candidates(limit: MAX_CANDIDATES) ⇒ Array<Hash>
Returns candidate entries — each path:, content:, signature:.
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/claude_memory/hook/auto_memory_mirror.rb', line 41 def pending_candidates(limit: MAX_CANDIDATES) return [] unless Dir.exist?(@auto_memory_dir) state = load_state files = Dir.glob(File.join(@auto_memory_dir, "*.md")).sort_by { |p| -File.mtime(p).to_i } files.each_with_object([]) do |path, candidates| break candidates if candidates.size >= limit name = File.basename(path) signature = file_signature(path) prior = state[name] next if prior.is_a?(Hash) && prior["md5"] == signature[:md5] candidates << { name: name, path: path, content: Core::TextBuilder.truncate(safe_read(path), MAX_TEXT_PER_ITEM), signature: signature } end rescue => e ClaudeMemory.logger.warn("AutoMemoryMirror#pending_candidates failed: #{e.}") [] end |