Module: ClaudeMemory::MCP::TextSummary
- Defined in:
- lib/claude_memory/mcp/text_summary.rb
Overview
Generates human-readable text summaries from MCP tool results. Used alongside structuredContent for dual content/structuredContent pattern.
Class Method Summary collapse
-
.fact_label(fact) ⇒ Object
Format fact identifier: prefer docid if available, fall back to integer id.
- .for_tool(name, result) ⇒ Object
- .summarize_activity(result) ⇒ Object
- .summarize_changes(result) ⇒ Object
- .summarize_check_setup(result) ⇒ Object
- .summarize_concepts(result) ⇒ Object
- .summarize_conflicts(result) ⇒ Object
- .summarize_context_facts(result) ⇒ Object
- .summarize_explain(result) ⇒ Object
- .summarize_extraction(result) ⇒ Object
- .summarize_fact_graph(result) ⇒ Object
- .summarize_list_projects(result) ⇒ Object
- .summarize_mark_distilled(result) ⇒ Object
- .summarize_promote(result) ⇒ Object
- .summarize_recall(result) ⇒ Object
- .summarize_recall_details(result) ⇒ Object
- .summarize_recall_index(result) ⇒ Object
- .summarize_semantic(result) ⇒ Object
- .summarize_shortcut(result) ⇒ Object
- .summarize_stats(result) ⇒ Object
- .summarize_status(result) ⇒ Object
- .summarize_sweep(result) ⇒ Object
- .summarize_tool_facts(result) ⇒ Object
- .summarize_undistilled(result) ⇒ Object
Class Method Details
.fact_label(fact) ⇒ Object
Format fact identifier: prefer docid if available, fall back to integer id
308 309 310 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 308 def self.fact_label(fact) fact[:docid] || fact[:id] end |
.for_tool(name, result) ⇒ Object
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 8 def self.for_tool(name, result) return result[:error] if result[:error] case name when "memory.recall" then summarize_recall(result) when "memory.recall_index" then summarize_recall_index(result) when "memory.recall_details" then summarize_recall_details(result) when "memory.explain" then summarize_explain(result) when "memory.changes" then summarize_changes(result) when "memory.conflicts" then summarize_conflicts(result) when "memory.sweep_now" then summarize_sweep(result) when "memory.status" then summarize_status(result) when "memory.stats" then summarize_stats(result) when "memory.promote" then summarize_promote(result) when "memory.store_extraction" then summarize_extraction(result) when "memory.decisions" then summarize_shortcut(result) when "memory.conventions" then summarize_shortcut(result) when "memory.architecture" then summarize_shortcut(result) when "memory.facts_by_tool" then summarize_tool_facts(result) when "memory.facts_by_context" then summarize_context_facts(result) when "memory.recall_semantic" then summarize_semantic(result) when "memory.search_concepts" then summarize_concepts(result) when "memory.fact_graph" then summarize_fact_graph(result) when "memory.undistilled" then summarize_undistilled(result) when "memory.mark_distilled" then summarize_mark_distilled(result) when "memory.check_setup" then summarize_check_setup(result) when "memory.activity" then summarize_activity(result) when "memory.list_projects" then summarize_list_projects(result) else JSON.generate(result) end end |
.summarize_activity(result) ⇒ Object
280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 280 def self.summarize_activity(result) events = result[:events] || [] return "No activity events recorded." if events.empty? summary = result[:summary] || {} lines = ["#{result[:event_count]} event(s):"] summary.each do |type, counts| parts = counts.map { |status, count| "#{count} #{status}" } lines << " #{type}: #{parts.join(", ")}" end lines.join("\n") end |
.summarize_changes(result) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 88 def self.summarize_changes(result) changes = result[:changes] || [] return "No changes since #{result[:since]}." if changes.empty? lines = ["#{changes.size} change(s) since #{result[:since]}:"] changes.each do |c| ago = c[:created_ago] ? " (#{c[:created_ago]})" : "" lines << "- [#{fact_label(c)}] #{c[:predicate]}: #{c[:object]} [#{c[:status]}]#{ago}" end lines.join("\n") end |
.summarize_check_setup(result) ⇒ Object
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 264 def self.summarize_check_setup(result) lines = ["Setup status: #{result[:status]}"] lines << "Version: #{result[:version][:current]} (latest: #{result[:version][:latest]})" components = result[:components] || {} lines << "Components: global_db=#{components[:global_database]}, project_db=#{components[:project_database]}, hooks=#{components[:hooks_configured]}" issues = result[:issues] || [] issues.each { |i| lines << "Issue: #{i}" } warnings = result[:warnings] || [] warnings.each { |w| lines << "Warning: #{w}" } lines.join("\n") end |
.summarize_concepts(result) ⇒ Object
212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 212 def self.summarize_concepts(result) facts = result[:facts] || [] concepts_str = (result[:concepts] || []).join(" + ") return "No facts matching all concepts: #{concepts_str}." if facts.empty? lines = ["#{result[:count]} fact(s) matching #{concepts_str}:"] facts.each do |f| sim = f[:average_similarity] ? " (#{(f[:average_similarity] * 100).round}%)" : "" lines << "- [#{fact_label(f)}] #{f[:subject]}.#{f[:predicate]} = #{f[:object]}#{sim}" end lines.join("\n") end |
.summarize_conflicts(result) ⇒ Object
100 101 102 103 104 105 106 107 108 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 100 def self.summarize_conflicts(result) return "No open conflicts." if result[:count] == 0 lines = ["#{result[:count]} conflict(s):"] (result[:conflicts] || []).each do |c| lines << "- Conflict ##{c[:id]}: fact #{c[:fact_a]} vs fact #{c[:fact_b]} (#{c[:status]})" end lines.join("\n") end |
.summarize_context_facts(result) ⇒ Object
189 190 191 192 193 194 195 196 197 198 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 189 def self.summarize_context_facts(result) facts = result[:facts] || [] return "No facts for #{result[:context_type]}=#{result[:context_value]}." if facts.empty? lines = ["#{result[:count]} fact(s) for #{result[:context_type]}=#{result[:context_value]}:"] facts.each do |f| lines << "- [#{fact_label(f)}] #{f[:subject]}.#{f[:predicate]} = #{f[:object]}" end lines.join("\n") end |
.summarize_explain(result) ⇒ Object
74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 74 def self.summarize_explain(result) f = result[:fact] lines = ["Fact [#{fact_label(f)}]: #{f[:subject]}.#{f[:predicate]} = #{f[:object]}"] lines << "Status: #{f[:status]}, valid from: #{f[:valid_from_ago] || f[:valid_from] || "unknown"}" lines << "Source: #{result[:source]}" receipts = result[:receipts] || [] if receipts.any? lines << "Evidence: #{receipts.map { |r| r[:quote] }.join("; ")}" end lines.join("\n") end |
.summarize_extraction(result) ⇒ Object
158 159 160 161 162 163 164 165 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 158 def self.summarize_extraction(result) if result[:success] "Stored: #{result[:facts_created]} facts, #{result[:entities_created]} entities, " \ "#{result[:facts_superseded]} superseded, #{result[:conflicts_created]} conflicts" else result[:error] end end |
.summarize_fact_graph(result) ⇒ Object
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 225 def self.summarize_fact_graph(result) nodes = result[:nodes] || [] edges = result[:edges] || [] return "Fact #{result[:root_fact_id]} not found." if nodes.empty? lines = ["Graph for fact ##{result[:root_fact_id]} (depth #{result[:depth]}): #{result[:node_count]} nodes, #{result[:edge_count]} edges"] nodes.each do |n| label = n[:docid] || n[:id] lines << "- [#{label}] #{n[:subject]}.#{n[:predicate]} = #{n[:object]} (#{n[:status]})" end if edges.any? lines << "Edges:" edges.each do |e| lines << " #{e[:from]} --#{e[:type]}--> #{e[:to]}" end end lines.join("\n") end |
.summarize_list_projects(result) ⇒ Object
293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 293 def self.summarize_list_projects(result) lines = ["Projects:"] if result[:global] lines << "- Global: #{result[:global][:facts_active] || 0} active facts" end if result[:current_project] lines << "- Current: #{result[:current_project][:facts_active] || 0} active facts" end (result[:other_projects] || []).each do |p| lines << "- #{p[:path]}: #{p[:facts_active] || 0} active facts" end lines.join("\n") end |
.summarize_mark_distilled(result) ⇒ Object
256 257 258 259 260 261 262 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 256 def self.summarize_mark_distilled(result) if result[:success] "Marked content item ##{result[:content_item_id]} as distilled (#{result[:facts_extracted]} facts extracted)" else result[:error] end end |
.summarize_promote(result) ⇒ Object
150 151 152 153 154 155 156 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 150 def self.summarize_promote(result) if result[:success] "Fact #{result[:project_fact_id]} promoted to global (new ID: #{result[:global_fact_id]})" else result[:error] end end |
.summarize_recall(result) ⇒ Object
40 41 42 43 44 45 46 47 48 49 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 40 def self.summarize_recall(result) facts = result[:facts] || [] return "No facts found." if facts.empty? lines = ["Found #{facts.size} fact(s):"] facts.each do |f| lines << "- [#{fact_label(f)}] #{f[:subject]}.#{f[:predicate]} = #{f[:object]}" end lines.join("\n") end |
.summarize_recall_details(result) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 62 def self.summarize_recall_details(result) facts = result[:facts] || [] return "No facts found for given IDs." if facts.empty? lines = ["#{result[:fact_count]} fact(s):"] facts.each do |f| fact = f[:fact] lines << "- [#{fact_label(fact)}] #{fact[:subject]}.#{fact[:predicate]} = #{fact[:object]} (#{fact[:status]}, #{fact[:scope]})" end lines.join("\n") end |
.summarize_recall_index(result) ⇒ Object
51 52 53 54 55 56 57 58 59 60 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 51 def self.summarize_recall_index(result) facts = result[:facts] || [] return "No matching facts for '#{result[:query]}'." if facts.empty? lines = ["#{result[:result_count]} fact(s) matching '#{result[:query]}' (~#{result[:total_estimated_tokens]} tokens):"] facts.each do |f| lines << "- [#{fact_label(f)}] #{f[:subject]}.#{f[:predicate]}: #{f[:object_preview]} (#{f[:tokens]}t)" end lines.join("\n") end |
.summarize_semantic(result) ⇒ Object
200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 200 def self.summarize_semantic(result) facts = result[:facts] || [] return "No semantic matches for '#{result[:query]}'." if facts.empty? lines = ["#{result[:count]} match(es) for '#{result[:query]}' (#{result[:mode]}):"] facts.each do |f| sim = f[:similarity] ? " (#{(f[:similarity] * 100).round}%)" : "" lines << "- [#{fact_label(f)}] #{f[:subject]}.#{f[:predicate]} = #{f[:object]}#{sim}" end lines.join("\n") end |
.summarize_shortcut(result) ⇒ Object
167 168 169 170 171 172 173 174 175 176 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 167 def self.summarize_shortcut(result) facts = result[:facts] || [] return "No #{result[:category]} found." if facts.empty? lines = ["#{result[:count]} #{result[:category]}:"] facts.each do |f| lines << "- [#{fact_label(f)}] #{f[:object]}" end lines.join("\n") end |
.summarize_stats(result) ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 136 def self.summarize_stats(result) dbs = result[:databases] || {} lines = ["Stats (scope: #{result[:scope]}):"] dbs.each do |name, info| if info[:exists] == false lines << "- #{name}: not initialized" else facts = info[:facts] || {} lines << "- #{name}: #{facts[:active]}/#{facts[:total]} active facts, #{info[:entities]&.dig(:total) || 0} entities" end end lines.join("\n") end |
.summarize_status(result) ⇒ Object
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 119 def self.summarize_status(result) dbs = result[:databases] || {} lines = ["Memory status:"] dbs.each do |name, info| lines << if info[:exists] == false "- #{name}: not initialized" else "- #{name}: #{info[:facts_active]} active facts, #{info[:open_conflicts]} conflicts (schema v#{info[:schema_version]})" end end pending = result[:pending_distillation] || 0 lines << "Pending distillation: #{pending}" if pending > 0 lines.join("\n") end |
.summarize_sweep(result) ⇒ Object
110 111 112 113 114 115 116 117 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 110 def self.summarize_sweep(result) escalation = result[:escalation_level] ? " [#{result[:escalation_level]}]" : "" "Sweep (#{result[:scope]})#{escalation}: #{result[:proposed_expired]} proposed expired, " \ "#{result[:disputed_expired]} disputed expired, " \ "#{result[:orphaned_deleted]} orphaned deleted, " \ "#{result[:content_pruned]} content pruned " \ "(#{result[:elapsed_seconds]}s)" end |
.summarize_tool_facts(result) ⇒ Object
178 179 180 181 182 183 184 185 186 187 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 178 def self.summarize_tool_facts(result) facts = result[:facts] || [] return "No facts discovered via #{result[:tool_name]}." if facts.empty? lines = ["#{result[:count]} fact(s) from #{result[:tool_name]}:"] facts.each do |f| lines << "- [#{fact_label(f)}] #{f[:subject]}.#{f[:predicate]} = #{f[:object]}" end lines.join("\n") end |
.summarize_undistilled(result) ⇒ Object
244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 244 def self.summarize_undistilled(result) items = result[:items] || [] return "No undistilled content items." if items.empty? lines = ["#{result[:count]} undistilled content item(s):"] items.each do |i| ago = i[:occurred_ago] || "unknown" lines << "- Item ##{i[:content_item_id]} (#{ago}): #{(i[:raw_text] || "")[0, 80]}..." end lines.join("\n") end |