claude_memory 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/memory.sqlite3 +0 -0
- data/.claude/rules/claude_memory.generated.md +32 -2
- data/.claude/settings.json +65 -15
- data/.claude/settings.local.json +5 -2
- data/.claude/skills/improve/SKILL.md +113 -25
- data/.claude/skills/upgrade-dependencies/SKILL.md +154 -0
- data/.claude-plugin/commands/distill-transcripts.md +98 -0
- data/.claude-plugin/commands/memory-recall.md +67 -0
- data/.claude-plugin/marketplace.json +2 -2
- data/.claude-plugin/plugin.json +3 -3
- data/.claude-plugin/scripts/hook-runner.sh +14 -0
- data/.claude-plugin/scripts/serve-mcp.sh +14 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +90 -1
- data/CLAUDE.md +56 -18
- data/README.md +35 -0
- data/db/migrations/013_add_mcp_tool_calls.rb +26 -0
- data/db/migrations/014_canonicalize_predicates.rb +30 -0
- data/docs/improvements.md +74 -74
- data/docs/influence/claude-mem.md +1 -0
- data/docs/influence/claude-supermemory.md +1 -0
- data/docs/influence/episodic-memory.md +1 -0
- data/docs/influence/grepai.md +1 -0
- data/docs/influence/kbs.md +1 -0
- data/docs/influence/lossless-claw.md +1 -0
- data/docs/influence/qmd.md +1 -0
- data/docs/quality_review.md +119 -224
- data/hooks/hooks.json +39 -7
- data/lib/claude_memory/commands/checks/distill_check.rb +61 -0
- data/lib/claude_memory/commands/checks/hooks_check.rb +2 -2
- data/lib/claude_memory/commands/checks/vec_check.rb +2 -1
- data/lib/claude_memory/commands/completion_command.rb +149 -0
- data/lib/claude_memory/commands/doctor_command.rb +2 -0
- data/lib/claude_memory/commands/embeddings_command.rb +198 -0
- data/lib/claude_memory/commands/help_command.rb +12 -1
- data/lib/claude_memory/commands/hook_command.rb +2 -1
- data/lib/claude_memory/commands/index_command.rb +85 -78
- data/lib/claude_memory/commands/initializers/database_ensurer.rb +16 -0
- data/lib/claude_memory/commands/initializers/global_initializer.rb +2 -1
- data/lib/claude_memory/commands/initializers/hooks_configurator.rb +55 -11
- data/lib/claude_memory/commands/initializers/project_initializer.rb +2 -1
- data/lib/claude_memory/commands/install_skill_command.rb +78 -0
- data/lib/claude_memory/commands/registry.rb +47 -32
- data/lib/claude_memory/commands/reject_command.rb +62 -0
- data/lib/claude_memory/commands/restore_command.rb +77 -0
- data/lib/claude_memory/commands/skills/distill-transcripts.md +102 -0
- data/lib/claude_memory/commands/skills/memory-recall.md +67 -0
- data/lib/claude_memory/commands/stats_command.rb +98 -2
- data/lib/claude_memory/configuration.rb +14 -1
- data/lib/claude_memory/core/fact_ranker.rb +2 -2
- data/lib/claude_memory/core/rr_fusion.rb +23 -6
- data/lib/claude_memory/core/snippet_extractor.rb +7 -3
- data/lib/claude_memory/core/text_builder.rb +11 -0
- data/lib/claude_memory/distill/json_schema.md +8 -4
- data/lib/claude_memory/distill/null_distiller.rb +2 -0
- data/lib/claude_memory/domain/entity.rb +13 -1
- data/lib/claude_memory/domain/fact.rb +26 -2
- data/lib/claude_memory/domain/provenance.rb +0 -1
- data/lib/claude_memory/embeddings/api_adapter.rb +97 -0
- data/lib/claude_memory/embeddings/dimension_check.rb +23 -0
- data/lib/claude_memory/embeddings/fastembed_adapter.rb +46 -12
- data/lib/claude_memory/embeddings/generator.rb +4 -0
- data/lib/claude_memory/embeddings/inspector.rb +91 -0
- data/lib/claude_memory/embeddings/model_registry.rb +210 -0
- data/lib/claude_memory/embeddings/resolver.rb +44 -0
- data/lib/claude_memory/hook/context_injector.rb +58 -2
- data/lib/claude_memory/hook/distillation_runner.rb +46 -0
- data/lib/claude_memory/hook/handler.rb +11 -2
- data/lib/claude_memory/index/vector_index.rb +15 -2
- data/lib/claude_memory/infrastructure/schema_validator.rb +3 -3
- data/lib/claude_memory/ingest/ingester.rb +17 -0
- data/lib/claude_memory/mcp/handlers/context_handlers.rb +38 -0
- data/lib/claude_memory/mcp/handlers/management_handlers.rb +169 -0
- data/lib/claude_memory/mcp/handlers/query_handlers.rb +115 -0
- data/lib/claude_memory/mcp/handlers/setup_handlers.rb +211 -0
- data/lib/claude_memory/mcp/handlers/shortcut_handlers.rb +37 -0
- data/lib/claude_memory/mcp/handlers/stats_handlers.rb +205 -0
- data/lib/claude_memory/mcp/instructions_builder.rb +19 -1
- data/lib/claude_memory/mcp/query_guide.rb +10 -0
- data/lib/claude_memory/mcp/response_formatter.rb +1 -0
- data/lib/claude_memory/mcp/server.rb +22 -1
- data/lib/claude_memory/mcp/telemetry.rb +86 -0
- data/lib/claude_memory/mcp/text_summary.rb +26 -0
- data/lib/claude_memory/mcp/tool_definitions.rb +116 -4
- data/lib/claude_memory/mcp/tool_helpers.rb +43 -0
- data/lib/claude_memory/mcp/tools.rb +50 -679
- data/lib/claude_memory/publish.rb +40 -5
- data/lib/claude_memory/recall/dual_engine.rb +105 -0
- data/lib/claude_memory/recall/legacy_engine.rb +138 -0
- data/lib/claude_memory/recall/query_core.rb +371 -0
- data/lib/claude_memory/recall.rb +121 -673
- data/lib/claude_memory/resolve/predicate_policy.rb +63 -3
- data/lib/claude_memory/resolve/resolver.rb +43 -0
- data/lib/claude_memory/shortcuts.rb +4 -4
- data/lib/claude_memory/store/retry_handler.rb +61 -0
- data/lib/claude_memory/store/schema_manager.rb +68 -0
- data/lib/claude_memory/store/sqlite_store.rb +334 -201
- data/lib/claude_memory/store/store_manager.rb +50 -1
- data/lib/claude_memory/sweep/maintenance.rb +115 -1
- data/lib/claude_memory/sweep/sweeper.rb +3 -0
- data/lib/claude_memory/templates/hooks.example.json +26 -7
- data/lib/claude_memory/version.rb +1 -1
- data/lib/claude_memory.rb +16 -0
- metadata +48 -8
- data/.claude/memory.sqlite3-shm +0 -0
- data/.claude/memory.sqlite3-wal +0 -0
|
@@ -14,6 +14,59 @@ module ClaudeMemory
|
|
|
14
14
|
# Annotations for idempotent writes (safe to retry)
|
|
15
15
|
WRITE_IDEMPOTENT = {readOnlyHint: false, idempotentHint: true, destructiveHint: false}.freeze
|
|
16
16
|
|
|
17
|
+
# Schema for {predicate, count} entries
|
|
18
|
+
PREDICATE_COUNT_SCHEMA = {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
predicate: {type: "string"},
|
|
22
|
+
count: {type: "integer"}
|
|
23
|
+
},
|
|
24
|
+
required: ["predicate", "count"]
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
# Schema for per-database stats block returned by memory.stats
|
|
28
|
+
DATABASE_STATS_SCHEMA = {
|
|
29
|
+
type: "object",
|
|
30
|
+
properties: {
|
|
31
|
+
exists: {type: "boolean"},
|
|
32
|
+
schema_version: {type: "integer"},
|
|
33
|
+
facts: {
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: {
|
|
36
|
+
total: {type: "integer"},
|
|
37
|
+
active: {type: "integer"},
|
|
38
|
+
superseded: {type: "integer"},
|
|
39
|
+
top_predicates: {
|
|
40
|
+
type: "array",
|
|
41
|
+
description: "Top 10 predicates by count (known + novel combined)",
|
|
42
|
+
items: PREDICATE_COUNT_SCHEMA
|
|
43
|
+
},
|
|
44
|
+
predicates_known: {
|
|
45
|
+
type: "array",
|
|
46
|
+
description: "Predicates with explicit cardinality policies in PredicatePolicy::POLICIES, sorted by count desc",
|
|
47
|
+
items: PREDICATE_COUNT_SCHEMA
|
|
48
|
+
},
|
|
49
|
+
predicates_novel: {
|
|
50
|
+
type: "array",
|
|
51
|
+
description: "Predicates not in PredicatePolicy::POLICIES, sorted by count desc. Novel predicates with high counts are candidates for promotion to known status with explicit cardinality policies (canonicalization signal).",
|
|
52
|
+
items: PREDICATE_COUNT_SCHEMA
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
entities: {
|
|
57
|
+
type: "object",
|
|
58
|
+
properties: {
|
|
59
|
+
total: {type: "integer"},
|
|
60
|
+
by_type: {type: "array", items: {type: "object"}}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
content_items: {type: "object"},
|
|
64
|
+
provenance: {type: "object"},
|
|
65
|
+
conflicts: {type: "object"},
|
|
66
|
+
vec: {type: "object"}
|
|
67
|
+
}
|
|
68
|
+
}.freeze
|
|
69
|
+
|
|
17
70
|
# Returns array of tool definitions for MCP protocol
|
|
18
71
|
# @return [Array<Hash>] Tool definitions with name, description, and inputSchema
|
|
19
72
|
def self.all
|
|
@@ -25,6 +78,7 @@ module ClaudeMemory
|
|
|
25
78
|
type: "object",
|
|
26
79
|
properties: {
|
|
27
80
|
query: {type: "string", description: "Search query for existing knowledge (e.g., 'authentication flow', 'error handling', 'database setup')"},
|
|
81
|
+
intent: {type: "string", description: "Optional intent to disambiguate the query (e.g., 'migration' or 'performance' when query is 'database'). Steers search without replacing the query."},
|
|
28
82
|
limit: {type: "integer", description: "Max results", default: 10},
|
|
29
83
|
scope: {type: "string", enum: ["all", "global", "project"], description: "Filter by scope: 'all' (default), 'global', or 'project'", default: "all"},
|
|
30
84
|
compact: {type: "boolean", description: "Omit provenance receipts for ~60% smaller responses (~800 → ~300 tokens/result)", default: false}
|
|
@@ -40,6 +94,7 @@ module ClaudeMemory
|
|
|
40
94
|
type: "object",
|
|
41
95
|
properties: {
|
|
42
96
|
query: {type: "string", description: "Search query for existing knowledge (e.g., 'client errors', 'database choice')"},
|
|
97
|
+
intent: {type: "string", description: "Optional intent to disambiguate the query (e.g., 'schema' or 'optimization' when query is 'database'). Steers search without replacing the query."},
|
|
43
98
|
limit: {type: "integer", description: "Maximum results to return", default: 20},
|
|
44
99
|
scope: {type: "string", enum: ["all", "global", "project"], description: "Scope: 'all' (both), 'global' (user-wide), 'project' (current only)", default: "all"}
|
|
45
100
|
},
|
|
@@ -121,13 +176,29 @@ module ClaudeMemory
|
|
|
121
176
|
},
|
|
122
177
|
{
|
|
123
178
|
name: "memory.stats",
|
|
124
|
-
description: "Get detailed statistics about the memory system (facts by predicate, entities by type, provenance coverage, conflicts, database sizes)",
|
|
179
|
+
description: "Get detailed statistics about the memory system (facts by predicate, entities by type, provenance coverage, conflicts, database sizes).",
|
|
125
180
|
inputSchema: {
|
|
126
181
|
type: "object",
|
|
127
182
|
properties: {
|
|
128
183
|
scope: {type: "string", enum: ["all", "global", "project"], description: "Show stats for: all (default), global, or project", default: "all"}
|
|
129
184
|
}
|
|
130
185
|
},
|
|
186
|
+
outputSchema: {
|
|
187
|
+
type: "object",
|
|
188
|
+
properties: {
|
|
189
|
+
scope: {type: "string", enum: ["all", "global", "project"]},
|
|
190
|
+
databases: {
|
|
191
|
+
type: "object",
|
|
192
|
+
description: "Per-database stats. Keys are 'global', 'project', or 'legacy' depending on connection mode.",
|
|
193
|
+
properties: {
|
|
194
|
+
global: DATABASE_STATS_SCHEMA,
|
|
195
|
+
project: DATABASE_STATS_SCHEMA,
|
|
196
|
+
legacy: DATABASE_STATS_SCHEMA
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
required: ["scope", "databases"]
|
|
201
|
+
},
|
|
131
202
|
annotations: READ_ONLY
|
|
132
203
|
},
|
|
133
204
|
{
|
|
@@ -142,6 +213,20 @@ module ClaudeMemory
|
|
|
142
213
|
},
|
|
143
214
|
annotations: WRITE_IDEMPOTENT
|
|
144
215
|
},
|
|
216
|
+
{
|
|
217
|
+
name: "memory.reject_fact",
|
|
218
|
+
description: "Mark a fact as rejected (e.g. a distiller hallucination). Sets status to 'rejected' and closes any open conflicts involving the fact. Use when the user confirms a fact is wrong.",
|
|
219
|
+
inputSchema: {
|
|
220
|
+
type: "object",
|
|
221
|
+
properties: {
|
|
222
|
+
fact_id: {type: "integer", description: "Fact ID to reject"},
|
|
223
|
+
docid: {type: "string", description: "8-char docid (alternative to fact_id)"},
|
|
224
|
+
reason: {type: "string", description: "Why the fact is wrong (recorded in conflict notes)"},
|
|
225
|
+
scope: {type: "string", enum: ["project", "global"], description: "Database scope", default: "project"}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
annotations: WRITE_IDEMPOTENT
|
|
229
|
+
},
|
|
145
230
|
{
|
|
146
231
|
name: "memory.store_extraction",
|
|
147
232
|
description: "Store extracted facts, entities, and decisions from a conversation. Call this to persist knowledge you've learned during the session.",
|
|
@@ -154,7 +239,7 @@ module ClaudeMemory
|
|
|
154
239
|
items: {
|
|
155
240
|
type: "object",
|
|
156
241
|
properties: {
|
|
157
|
-
type: {type: "string", description: "Entity type: database, framework, language, platform, repo, module, person, service"},
|
|
242
|
+
type: {type: "string", description: "Entity type. Common types: database, framework, language, platform, repo, module, person, service, tool, library, concept. You may use other types if needed."},
|
|
158
243
|
name: {type: "string", description: "Canonical name"},
|
|
159
244
|
confidence: {type: "number", description: "0.0-1.0 extraction confidence"}
|
|
160
245
|
},
|
|
@@ -168,7 +253,7 @@ module ClaudeMemory
|
|
|
168
253
|
type: "object",
|
|
169
254
|
properties: {
|
|
170
255
|
subject: {type: "string", description: "Entity name or 'repo' for project-level facts"},
|
|
171
|
-
predicate: {type: "string", description: "Relationship type:
|
|
256
|
+
predicate: {type: "string", description: "Relationship type. Known predicates: #{ClaudeMemory::Resolve::PredicatePolicy.known_predicates.join(", ")}. You may use other snake_case predicates for relations that don't fit these — be specific and reuse existing predicates when possible."},
|
|
172
257
|
object: {type: "string", description: "The value or target entity"},
|
|
173
258
|
confidence: {type: "number", description: "0.0-1.0 how confident"},
|
|
174
259
|
quote: {type: "string", description: "Source text excerpt (max 200 chars)"},
|
|
@@ -265,10 +350,12 @@ module ClaudeMemory
|
|
|
265
350
|
type: "object",
|
|
266
351
|
properties: {
|
|
267
352
|
query: {type: "string", description: "Search query"},
|
|
353
|
+
intent: {type: "string", description: "Optional intent to disambiguate the query (e.g., 'security' when query is 'authentication'). Disables BM25 shortcut to ensure vector search runs."},
|
|
268
354
|
mode: {type: "string", enum: ["vector", "text", "both"], default: "both", description: "Search mode: vector (embeddings), text (FTS), or both (hybrid)"},
|
|
269
355
|
limit: {type: "integer", default: 10, description: "Maximum results to return"},
|
|
270
356
|
scope: {type: "string", enum: ["all", "global", "project"], default: "all", description: "Filter by scope"},
|
|
271
|
-
compact: {type: "boolean", description: "Omit provenance receipts for ~60% smaller responses (~800 → ~300 tokens/result)", default: false}
|
|
357
|
+
compact: {type: "boolean", description: "Omit provenance receipts for ~60% smaller responses (~800 → ~300 tokens/result)", default: false},
|
|
358
|
+
explain: {type: "boolean", description: "Include per-result score traces showing FTS rank, vector similarity, and RRF contribution", default: false}
|
|
272
359
|
},
|
|
273
360
|
required: ["query"]
|
|
274
361
|
},
|
|
@@ -309,6 +396,31 @@ module ClaudeMemory
|
|
|
309
396
|
},
|
|
310
397
|
annotations: READ_ONLY
|
|
311
398
|
},
|
|
399
|
+
{
|
|
400
|
+
name: "memory.undistilled",
|
|
401
|
+
description: "List content items not yet deeply distilled. Returns raw transcript text for knowledge extraction.",
|
|
402
|
+
inputSchema: {
|
|
403
|
+
type: "object",
|
|
404
|
+
properties: {
|
|
405
|
+
limit: {type: "integer", default: 3, description: "Max items to return"},
|
|
406
|
+
min_length: {type: "integer", default: 200, description: "Min text length (skip tiny deltas)"}
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
annotations: READ_ONLY
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
name: "memory.mark_distilled",
|
|
413
|
+
description: "Mark a content item as distilled after extracting facts from it.",
|
|
414
|
+
inputSchema: {
|
|
415
|
+
type: "object",
|
|
416
|
+
properties: {
|
|
417
|
+
content_item_id: {type: "integer", description: "ID of the distilled content item"},
|
|
418
|
+
facts_extracted: {type: "integer", default: 0, description: "Number of facts extracted"}
|
|
419
|
+
},
|
|
420
|
+
required: ["content_item_id"]
|
|
421
|
+
},
|
|
422
|
+
annotations: WRITE_IDEMPOTENT
|
|
423
|
+
},
|
|
312
424
|
{
|
|
313
425
|
name: "memory.check_setup",
|
|
314
426
|
description: "Check ClaudeMemory initialization status. Returns version info, issues found, and recommendations.",
|
|
@@ -75,6 +75,49 @@ module ClaudeMemory
|
|
|
75
75
|
def extract_limit(args, default: 10)
|
|
76
76
|
args["limit"] || default
|
|
77
77
|
end
|
|
78
|
+
|
|
79
|
+
# Extract optional intent parameter for query disambiguation
|
|
80
|
+
# @param args [Hash] Tool arguments
|
|
81
|
+
# @return [String, nil] Intent string or nil if not provided/blank
|
|
82
|
+
def extract_intent(args)
|
|
83
|
+
intent = args["intent"]
|
|
84
|
+
(intent.nil? || intent.to_s.strip.empty?) ? nil : intent.to_s.strip
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Collect undistilled content items from both stores (or legacy store)
|
|
88
|
+
# @param limit [Integer] Maximum items to return
|
|
89
|
+
# @param min_length [Integer] Minimum byte_len to include
|
|
90
|
+
# @return [Array<Hash>] Undistilled items sorted by recency
|
|
91
|
+
def collect_undistilled_items(limit:, min_length: 200)
|
|
92
|
+
if @manager
|
|
93
|
+
stores = []
|
|
94
|
+
stores << @manager.project_store if @manager.project_exists?
|
|
95
|
+
stores << @manager.global_store if @manager.global_exists?
|
|
96
|
+
items = stores.flat_map { |s| s.undistilled_content_items(limit: limit, min_length: min_length) }
|
|
97
|
+
items.sort_by { |i| i[:occurred_at] || "" }.reverse.first(limit)
|
|
98
|
+
elsif @legacy_store
|
|
99
|
+
@legacy_store.undistilled_content_items(limit: limit, min_length: min_length)
|
|
100
|
+
else
|
|
101
|
+
[]
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Find the store containing a given content item
|
|
106
|
+
# @param content_item_id [Integer] Content item ID to locate
|
|
107
|
+
# @return [Store::SQLiteStore, nil] The store containing the item, or nil
|
|
108
|
+
def find_store_for_content_item(content_item_id)
|
|
109
|
+
if @manager
|
|
110
|
+
if @manager.project_store&.content_items&.where(id: content_item_id)&.any?
|
|
111
|
+
@manager.project_store
|
|
112
|
+
elsif @manager.global_store&.content_items&.where(id: content_item_id)&.any?
|
|
113
|
+
@manager.global_store
|
|
114
|
+
end
|
|
115
|
+
elsif @legacy_store
|
|
116
|
+
if @legacy_store.content_items.where(id: content_item_id).any?
|
|
117
|
+
@legacy_store
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
78
121
|
end
|
|
79
122
|
end
|
|
80
123
|
end
|