claude_memory 0.2.0 → 0.3.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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/.mind.mv2.o2N83S +0 -0
  3. data/.claude/CLAUDE.md +1 -0
  4. data/.claude/rules/claude_memory.generated.md +28 -9
  5. data/.claude/settings.local.json +9 -1
  6. data/.claude/skills/check-memory/SKILL.md +77 -0
  7. data/.claude/skills/improve/SKILL.md +532 -0
  8. data/.claude/skills/improve/feature-patterns.md +1221 -0
  9. data/.claude/skills/quality-update/SKILL.md +229 -0
  10. data/.claude/skills/quality-update/implementation-guide.md +346 -0
  11. data/.claude/skills/review-commit/SKILL.md +199 -0
  12. data/.claude/skills/review-for-quality/SKILL.md +154 -0
  13. data/.claude/skills/review-for-quality/expert-checklists.md +79 -0
  14. data/.claude/skills/setup-memory/SKILL.md +168 -0
  15. data/.claude/skills/study-repo/SKILL.md +307 -0
  16. data/.claude/skills/study-repo/analysis-template.md +323 -0
  17. data/.claude/skills/study-repo/focus-examples.md +327 -0
  18. data/CHANGELOG.md +133 -0
  19. data/CLAUDE.md +130 -11
  20. data/README.md +117 -10
  21. data/db/migrations/001_create_initial_schema.rb +117 -0
  22. data/db/migrations/002_add_project_scoping.rb +33 -0
  23. data/db/migrations/003_add_session_metadata.rb +42 -0
  24. data/db/migrations/004_add_fact_embeddings.rb +20 -0
  25. data/db/migrations/005_add_incremental_sync.rb +21 -0
  26. data/db/migrations/006_add_operation_tracking.rb +40 -0
  27. data/db/migrations/007_add_ingestion_metrics.rb +26 -0
  28. data/docs/.claude/mind.mv2.lock +0 -0
  29. data/docs/GETTING_STARTED.md +587 -0
  30. data/docs/RELEASE_NOTES_v0.2.0.md +0 -1
  31. data/docs/RUBY_COMMUNITY_POST_v0.2.0.md +0 -2
  32. data/docs/architecture.md +9 -8
  33. data/docs/auto_init_design.md +230 -0
  34. data/docs/improvements.md +557 -731
  35. data/docs/influence/.gitkeep +13 -0
  36. data/docs/influence/grepai.md +933 -0
  37. data/docs/influence/qmd.md +2195 -0
  38. data/docs/plugin.md +257 -11
  39. data/docs/quality_review.md +472 -1273
  40. data/docs/remaining_improvements.md +330 -0
  41. data/lefthook.yml +13 -0
  42. data/lib/claude_memory/commands/checks/claude_md_check.rb +41 -0
  43. data/lib/claude_memory/commands/checks/database_check.rb +120 -0
  44. data/lib/claude_memory/commands/checks/hooks_check.rb +112 -0
  45. data/lib/claude_memory/commands/checks/reporter.rb +110 -0
  46. data/lib/claude_memory/commands/checks/snapshot_check.rb +30 -0
  47. data/lib/claude_memory/commands/doctor_command.rb +12 -129
  48. data/lib/claude_memory/commands/help_command.rb +1 -0
  49. data/lib/claude_memory/commands/hook_command.rb +9 -2
  50. data/lib/claude_memory/commands/index_command.rb +169 -0
  51. data/lib/claude_memory/commands/ingest_command.rb +1 -1
  52. data/lib/claude_memory/commands/init_command.rb +5 -197
  53. data/lib/claude_memory/commands/initializers/database_ensurer.rb +30 -0
  54. data/lib/claude_memory/commands/initializers/global_initializer.rb +85 -0
  55. data/lib/claude_memory/commands/initializers/hooks_configurator.rb +156 -0
  56. data/lib/claude_memory/commands/initializers/mcp_configurator.rb +56 -0
  57. data/lib/claude_memory/commands/initializers/memory_instructions_writer.rb +135 -0
  58. data/lib/claude_memory/commands/initializers/project_initializer.rb +111 -0
  59. data/lib/claude_memory/commands/recover_command.rb +75 -0
  60. data/lib/claude_memory/commands/registry.rb +5 -1
  61. data/lib/claude_memory/commands/stats_command.rb +239 -0
  62. data/lib/claude_memory/commands/uninstall_command.rb +226 -0
  63. data/lib/claude_memory/core/batch_loader.rb +32 -0
  64. data/lib/claude_memory/core/concept_ranker.rb +73 -0
  65. data/lib/claude_memory/core/embedding_candidate_builder.rb +37 -0
  66. data/lib/claude_memory/core/fact_collector.rb +51 -0
  67. data/lib/claude_memory/core/fact_query_builder.rb +154 -0
  68. data/lib/claude_memory/core/fact_ranker.rb +113 -0
  69. data/lib/claude_memory/core/result_builder.rb +54 -0
  70. data/lib/claude_memory/core/result_sorter.rb +25 -0
  71. data/lib/claude_memory/core/scope_filter.rb +61 -0
  72. data/lib/claude_memory/core/text_builder.rb +29 -0
  73. data/lib/claude_memory/embeddings/generator.rb +161 -0
  74. data/lib/claude_memory/embeddings/similarity.rb +69 -0
  75. data/lib/claude_memory/hook/handler.rb +4 -3
  76. data/lib/claude_memory/index/lexical_fts.rb +7 -2
  77. data/lib/claude_memory/infrastructure/operation_tracker.rb +158 -0
  78. data/lib/claude_memory/infrastructure/schema_validator.rb +206 -0
  79. data/lib/claude_memory/ingest/content_sanitizer.rb +6 -7
  80. data/lib/claude_memory/ingest/ingester.rb +99 -15
  81. data/lib/claude_memory/ingest/metadata_extractor.rb +57 -0
  82. data/lib/claude_memory/ingest/tool_extractor.rb +71 -0
  83. data/lib/claude_memory/mcp/response_formatter.rb +331 -0
  84. data/lib/claude_memory/mcp/server.rb +19 -0
  85. data/lib/claude_memory/mcp/setup_status_analyzer.rb +73 -0
  86. data/lib/claude_memory/mcp/tool_definitions.rb +279 -0
  87. data/lib/claude_memory/mcp/tool_helpers.rb +80 -0
  88. data/lib/claude_memory/mcp/tools.rb +330 -320
  89. data/lib/claude_memory/recall/dual_query_template.rb +63 -0
  90. data/lib/claude_memory/recall.rb +304 -237
  91. data/lib/claude_memory/resolve/resolver.rb +52 -49
  92. data/lib/claude_memory/store/sqlite_store.rb +210 -144
  93. data/lib/claude_memory/store/store_manager.rb +6 -6
  94. data/lib/claude_memory/sweep/sweeper.rb +6 -0
  95. data/lib/claude_memory/version.rb +1 -1
  96. data/lib/claude_memory.rb +35 -3
  97. metadata +71 -11
  98. data/.claude/.mind.mv2.aLCUZd +0 -0
  99. data/.claude/memory.sqlite3 +0 -0
  100. data/.mcp.json +0 -11
  101. /data/docs/{feature_adoption_plan.md → plans/feature_adoption_plan.md} +0 -0
  102. /data/docs/{feature_adoption_plan_revised.md → plans/feature_adoption_plan_revised.md} +0 -0
  103. /data/docs/{plan.md → plans/plan.md} +0 -0
  104. /data/docs/{updated_plan.md → plans/updated_plan.md} +0 -0
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeMemory
4
+ module MCP
5
+ # Pure logic for analyzing setup status and generating recommendations
6
+ # Follows Functional Core pattern - no I/O, just decision logic
7
+ class SetupStatusAnalyzer
8
+ # Determine overall setup status based on component states
9
+ # @param global_db_exists [Boolean] Global database exists
10
+ # @param claude_md_exists [Boolean] CLAUDE.md file exists
11
+ # @param version_status [String, nil] Version status (up_to_date, outdated, etc.)
12
+ # @return [String] Overall status (healthy, needs_upgrade, partially_initialized, not_initialized)
13
+ def self.determine_status(global_db_exists, claude_md_exists, version_status)
14
+ initialized = global_db_exists && claude_md_exists
15
+
16
+ if initialized && version_status == "up_to_date"
17
+ "healthy"
18
+ elsif initialized && version_status == "outdated"
19
+ "needs_upgrade"
20
+ elsif global_db_exists && !claude_md_exists
21
+ "partially_initialized"
22
+ else
23
+ "not_initialized"
24
+ end
25
+ end
26
+
27
+ # Generate recommendations based on setup status
28
+ # @param initialized [Boolean] Whether system is initialized
29
+ # @param version_status [String, nil] Version status
30
+ # @param has_warnings [Boolean] Whether there are warnings
31
+ # @return [Array<String>] List of recommendations
32
+ def self.generate_recommendations(initialized, version_status, has_warnings)
33
+ recommendations = []
34
+
35
+ if !initialized
36
+ recommendations << "Run: claude-memory init"
37
+ recommendations << "This will create databases, configure hooks, and set up CLAUDE.md"
38
+ elsif version_status == "outdated"
39
+ recommendations << "Run: claude-memory upgrade (when available)"
40
+ recommendations << "Or manually run: claude-memory init to update CLAUDE.md"
41
+ elsif has_warnings
42
+ recommendations << "Run: claude-memory doctor --fix (when available)"
43
+ recommendations << "Or check individual issues and fix manually"
44
+ end
45
+
46
+ recommendations
47
+ end
48
+
49
+ # Extract version from CLAUDE.md content
50
+ # @param content [String] CLAUDE.md file content
51
+ # @return [String, nil] Extracted version or nil
52
+ def self.extract_version(content)
53
+ if content =~ /<!-- ClaudeMemory v([\d.]+) -->/
54
+ $1
55
+ end
56
+ end
57
+
58
+ # Determine version status by comparing current and latest
59
+ # @param current_version [String, nil] Version from config
60
+ # @param latest_version [String] Latest version
61
+ # @return [String] Version status (up_to_date, outdated, no_version_marker, unknown)
62
+ def self.determine_version_status(current_version, latest_version)
63
+ return "unknown" unless current_version
64
+
65
+ if current_version == latest_version
66
+ "up_to_date"
67
+ else
68
+ "outdated"
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,279 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeMemory
4
+ module MCP
5
+ # MCP tool definitions for Claude Memory
6
+ # Pure data structure - no logic, just tool schemas
7
+ module ToolDefinitions
8
+ # Returns array of tool definitions for MCP protocol
9
+ # @return [Array<Hash>] Tool definitions with name, description, and inputSchema
10
+ def self.all
11
+ [
12
+ {
13
+ name: "memory.recall",
14
+ description: "IMPORTANT: Check memory FIRST before reading files or exploring code. Recalls facts matching a query from distilled knowledge in both global and project databases. Use this to find existing knowledge about modules, patterns, decisions, and conventions before resorting to file reads or code searches.",
15
+ inputSchema: {
16
+ type: "object",
17
+ properties: {
18
+ query: {type: "string", description: "Search query for existing knowledge (e.g., 'authentication flow', 'error handling', 'database setup')"},
19
+ limit: {type: "integer", description: "Max results", default: 10},
20
+ scope: {type: "string", enum: ["all", "global", "project"], description: "Filter by scope: 'all' (default), 'global', or 'project'", default: "all"}
21
+ },
22
+ required: ["query"]
23
+ }
24
+ },
25
+ {
26
+ name: "memory.recall_index",
27
+ description: "Layer 1: CHECK MEMORY FIRST with this lightweight search. Returns fact previews, IDs, and token costs without full details. Use before exploring code to see what knowledge already exists. Follow up with memory.recall_details for specific facts.",
28
+ inputSchema: {
29
+ type: "object",
30
+ properties: {
31
+ query: {type: "string", description: "Search query for existing knowledge (e.g., 'client errors', 'database choice')"},
32
+ limit: {type: "integer", description: "Maximum results to return", default: 20},
33
+ scope: {type: "string", enum: ["all", "global", "project"], description: "Scope: 'all' (both), 'global' (user-wide), 'project' (current only)", default: "all"}
34
+ },
35
+ required: ["query"]
36
+ }
37
+ },
38
+ {
39
+ name: "memory.recall_details",
40
+ description: "Layer 2: Fetch full details for specific fact IDs from the index. Use after memory.recall_index to get complete information.",
41
+ inputSchema: {
42
+ type: "object",
43
+ properties: {
44
+ fact_ids: {type: "array", items: {type: "integer"}, description: "Fact IDs from memory.recall_index"},
45
+ scope: {type: "string", enum: ["project", "global"], description: "Database to query", default: "project"}
46
+ },
47
+ required: ["fact_ids"]
48
+ }
49
+ },
50
+ {
51
+ name: "memory.explain",
52
+ description: "Get detailed explanation of a fact with provenance",
53
+ inputSchema: {
54
+ type: "object",
55
+ properties: {
56
+ fact_id: {type: "integer", description: "Fact ID to explain"},
57
+ scope: {type: "string", enum: ["global", "project"], description: "Which database to look in", default: "project"}
58
+ },
59
+ required: ["fact_id"]
60
+ }
61
+ },
62
+ {
63
+ name: "memory.changes",
64
+ description: "List recent fact changes from both databases",
65
+ inputSchema: {
66
+ type: "object",
67
+ properties: {
68
+ since: {type: "string", description: "ISO timestamp"},
69
+ limit: {type: "integer", default: 20},
70
+ scope: {type: "string", enum: ["all", "global", "project"], default: "all"}
71
+ }
72
+ }
73
+ },
74
+ {
75
+ name: "memory.conflicts",
76
+ description: "List open conflicts from both databases",
77
+ inputSchema: {
78
+ type: "object",
79
+ properties: {
80
+ scope: {type: "string", enum: ["all", "global", "project"], default: "all"}
81
+ }
82
+ }
83
+ },
84
+ {
85
+ name: "memory.sweep_now",
86
+ description: "Run maintenance sweep on a database",
87
+ inputSchema: {
88
+ type: "object",
89
+ properties: {
90
+ budget_seconds: {type: "integer", default: 5},
91
+ scope: {type: "string", enum: ["global", "project"], default: "project"}
92
+ }
93
+ }
94
+ },
95
+ {
96
+ name: "memory.status",
97
+ description: "Get memory system status for both databases",
98
+ inputSchema: {
99
+ type: "object",
100
+ properties: {}
101
+ }
102
+ },
103
+ {
104
+ name: "memory.stats",
105
+ description: "Get detailed statistics about the memory system (facts by predicate, entities by type, provenance coverage, conflicts, database sizes)",
106
+ inputSchema: {
107
+ type: "object",
108
+ properties: {
109
+ scope: {type: "string", enum: ["all", "global", "project"], description: "Show stats for: all (default), global, or project", default: "all"}
110
+ }
111
+ }
112
+ },
113
+ {
114
+ name: "memory.promote",
115
+ description: "Promote a project fact to global memory. Use when user says a preference should apply everywhere.",
116
+ inputSchema: {
117
+ type: "object",
118
+ properties: {
119
+ fact_id: {type: "integer", description: "Project fact ID to promote to global"}
120
+ },
121
+ required: ["fact_id"]
122
+ }
123
+ },
124
+ {
125
+ name: "memory.store_extraction",
126
+ description: "Store extracted facts, entities, and decisions from a conversation. Call this to persist knowledge you've learned during the session.",
127
+ inputSchema: {
128
+ type: "object",
129
+ properties: {
130
+ entities: {
131
+ type: "array",
132
+ description: "Entities mentioned (databases, frameworks, services, etc.)",
133
+ items: {
134
+ type: "object",
135
+ properties: {
136
+ type: {type: "string", description: "Entity type: database, framework, language, platform, repo, module, person, service"},
137
+ name: {type: "string", description: "Canonical name"},
138
+ confidence: {type: "number", description: "0.0-1.0 extraction confidence"}
139
+ },
140
+ required: ["type", "name"]
141
+ }
142
+ },
143
+ facts: {
144
+ type: "array",
145
+ description: "Facts learned during the session",
146
+ items: {
147
+ type: "object",
148
+ properties: {
149
+ subject: {type: "string", description: "Entity name or 'repo' for project-level facts"},
150
+ predicate: {type: "string", description: "Relationship type: uses_database, uses_framework, convention, decision, auth_method, deployment_platform"},
151
+ object: {type: "string", description: "The value or target entity"},
152
+ confidence: {type: "number", description: "0.0-1.0 how confident"},
153
+ quote: {type: "string", description: "Source text excerpt (max 200 chars)"},
154
+ strength: {type: "string", enum: ["stated", "inferred"], description: "Was this explicitly stated or inferred?"},
155
+ scope_hint: {type: "string", enum: ["project", "global"], description: "Should this apply to just this project or globally?"}
156
+ },
157
+ required: ["subject", "predicate", "object"]
158
+ }
159
+ },
160
+ decisions: {
161
+ type: "array",
162
+ description: "Decisions made during the session",
163
+ items: {
164
+ type: "object",
165
+ properties: {
166
+ title: {type: "string", description: "Short summary (max 100 chars)"},
167
+ summary: {type: "string", description: "Full description"},
168
+ status_hint: {type: "string", enum: ["accepted", "proposed", "rejected"]}
169
+ },
170
+ required: ["title", "summary"]
171
+ }
172
+ },
173
+ scope: {type: "string", enum: ["global", "project"], description: "Default scope for facts", default: "project"}
174
+ },
175
+ required: ["facts"]
176
+ }
177
+ },
178
+ {
179
+ name: "memory.decisions",
180
+ description: "Quick access to architectural decisions, constraints, and rules. Use BEFORE implementing features to understand existing decisions and constraints.",
181
+ inputSchema: {
182
+ type: "object",
183
+ properties: {
184
+ limit: {type: "integer", default: 10, description: "Maximum results to return"}
185
+ }
186
+ }
187
+ },
188
+ {
189
+ name: "memory.conventions",
190
+ description: "Quick access to coding conventions and style preferences (global scope). Check BEFORE writing code to follow established patterns.",
191
+ inputSchema: {
192
+ type: "object",
193
+ properties: {
194
+ limit: {type: "integer", default: 20, description: "Maximum results to return"}
195
+ }
196
+ }
197
+ },
198
+ {
199
+ name: "memory.architecture",
200
+ description: "Quick access to framework choices and architectural patterns. Check FIRST when working with frameworks or making architectural decisions.",
201
+ inputSchema: {
202
+ type: "object",
203
+ properties: {
204
+ limit: {type: "integer", default: 10, description: "Maximum results to return"}
205
+ }
206
+ }
207
+ },
208
+ {
209
+ name: "memory.facts_by_tool",
210
+ description: "Find facts discovered using a specific tool (Read, Edit, Bash, etc.)",
211
+ inputSchema: {
212
+ type: "object",
213
+ properties: {
214
+ tool_name: {type: "string", description: "Tool name (Read, Edit, Bash, etc.)"},
215
+ limit: {type: "integer", default: 20, description: "Maximum results to return"},
216
+ scope: {type: "string", enum: ["all", "global", "project"], default: "all", description: "Filter by scope"}
217
+ },
218
+ required: ["tool_name"]
219
+ }
220
+ },
221
+ {
222
+ name: "memory.facts_by_context",
223
+ description: "Find facts learned in specific context (branch, directory)",
224
+ inputSchema: {
225
+ type: "object",
226
+ properties: {
227
+ git_branch: {type: "string", description: "Git branch name"},
228
+ cwd: {type: "string", description: "Working directory path"},
229
+ limit: {type: "integer", default: 20, description: "Maximum results to return"},
230
+ scope: {type: "string", enum: ["all", "global", "project"], default: "all", description: "Filter by scope"}
231
+ }
232
+ }
233
+ },
234
+ {
235
+ name: "memory.recall_semantic",
236
+ description: "Search facts using semantic similarity (finds conceptually related facts using vector embeddings)",
237
+ inputSchema: {
238
+ type: "object",
239
+ properties: {
240
+ query: {type: "string", description: "Search query"},
241
+ mode: {type: "string", enum: ["vector", "text", "both"], default: "both", description: "Search mode: vector (embeddings), text (FTS), or both (hybrid)"},
242
+ limit: {type: "integer", default: 10, description: "Maximum results to return"},
243
+ scope: {type: "string", enum: ["all", "global", "project"], default: "all", description: "Filter by scope"}
244
+ },
245
+ required: ["query"]
246
+ }
247
+ },
248
+ {
249
+ name: "memory.search_concepts",
250
+ description: "Search for facts matching ALL of the provided concepts (AND query). Ranks by average similarity across all concepts.",
251
+ inputSchema: {
252
+ type: "object",
253
+ properties: {
254
+ concepts: {
255
+ type: "array",
256
+ items: {type: "string"},
257
+ minItems: 2,
258
+ maxItems: 5,
259
+ description: "2-5 concepts that must all be present"
260
+ },
261
+ limit: {type: "integer", default: 10, description: "Maximum results to return"},
262
+ scope: {type: "string", enum: ["all", "global", "project"], default: "all", description: "Filter by scope"}
263
+ },
264
+ required: ["concepts"]
265
+ }
266
+ },
267
+ {
268
+ name: "memory.check_setup",
269
+ description: "Check if ClaudeMemory is properly initialized. CALL THIS FIRST if memory tools fail or on first use. Returns initialization status, version info, and actionable recommendations.",
270
+ inputSchema: {
271
+ type: "object",
272
+ properties: {}
273
+ }
274
+ }
275
+ ]
276
+ end
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeMemory
4
+ module MCP
5
+ # Shared utility methods for MCP tool implementations
6
+ # Reduces duplication across tool methods
7
+ module ToolHelpers
8
+ # Standard error response when database is not accessible
9
+ # @param error [Exception] The caught database error
10
+ # @return [Hash] Formatted error response with recommendations
11
+ def database_not_found_error(error)
12
+ {
13
+ error: "Database not found or not accessible",
14
+ message: "ClaudeMemory may not be initialized. Run memory.check_setup for detailed status.",
15
+ details: error.message,
16
+ recommendations: [
17
+ "Run memory.check_setup to diagnose the issue",
18
+ "If not initialized, run: claude-memory init",
19
+ "For help: claude-memory doctor"
20
+ ]
21
+ }
22
+ end
23
+
24
+ # Format a fact hash for API response
25
+ # @param fact [Hash] Fact record from database
26
+ # @return [Hash] Formatted fact with standard fields
27
+ def format_fact(fact)
28
+ {
29
+ id: fact[:id],
30
+ subject: fact[:subject_name],
31
+ predicate: fact[:predicate],
32
+ object: fact[:object_literal],
33
+ status: fact[:status],
34
+ scope: fact[:scope]
35
+ }
36
+ end
37
+
38
+ # Format a receipt hash for API response
39
+ # @param receipt [Hash] Provenance record from database
40
+ # @return [Hash] Formatted receipt with quote and strength
41
+ def format_receipt(receipt)
42
+ {
43
+ quote: receipt[:quote],
44
+ strength: receipt[:strength]
45
+ }
46
+ end
47
+
48
+ # Format a result with fact and receipts
49
+ # @param result [Hash] Result hash with :fact and :receipts keys
50
+ # @return [Hash] Formatted result with source
51
+ def format_result(result)
52
+ {
53
+ id: result[:fact][:id],
54
+ subject: result[:fact][:subject_name],
55
+ predicate: result[:fact][:predicate],
56
+ object: result[:fact][:object_literal],
57
+ scope: result[:fact][:scope],
58
+ source: result[:source],
59
+ receipts: result[:receipts].map { |r| format_receipt(r) }
60
+ }
61
+ end
62
+
63
+ # Get default scope from arguments
64
+ # @param args [Hash] Tool arguments
65
+ # @param default [String] Default scope if not specified
66
+ # @return [String] Scope value
67
+ def extract_scope(args, default: "all")
68
+ args["scope"] || default
69
+ end
70
+
71
+ # Get default limit from arguments
72
+ # @param args [Hash] Tool arguments
73
+ # @param default [Integer] Default limit if not specified
74
+ # @return [Integer] Limit value
75
+ def extract_limit(args, default: 10)
76
+ args["limit"] || default
77
+ end
78
+ end
79
+ end
80
+ end