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_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_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
279 280 281 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 279 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 |
# 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) else JSON.generate(result) end end |
.summarize_changes(result) ⇒ Object
86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 86 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
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 262 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
210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 210 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
98 99 100 101 102 103 104 105 106 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 98 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
187 188 189 190 191 192 193 194 195 196 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 187 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
72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 72 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
156 157 158 159 160 161 162 163 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 156 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
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 223 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_mark_distilled(result) ⇒ Object
254 255 256 257 258 259 260 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 254 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
148 149 150 151 152 153 154 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 148 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
38 39 40 41 42 43 44 45 46 47 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 38 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
60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 60 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
49 50 51 52 53 54 55 56 57 58 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 49 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
198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 198 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
165 166 167 168 169 170 171 172 173 174 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 165 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
134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 134 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
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 117 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
108 109 110 111 112 113 114 115 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 108 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
176 177 178 179 180 181 182 183 184 185 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 176 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
242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/claude_memory/mcp/text_summary.rb', line 242 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 |