claude-mpm 4.1.2__py3-none-any.whl → 4.1.3__py3-none-any.whl

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 (53) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/templates/engineer.json +33 -11
  3. claude_mpm/cli/commands/agents.py +556 -1009
  4. claude_mpm/cli/commands/memory.py +248 -927
  5. claude_mpm/cli/commands/run.py +139 -484
  6. claude_mpm/cli/startup_logging.py +76 -0
  7. claude_mpm/core/agent_registry.py +6 -10
  8. claude_mpm/core/framework_loader.py +114 -595
  9. claude_mpm/core/logging_config.py +2 -4
  10. claude_mpm/hooks/claude_hooks/event_handlers.py +7 -117
  11. claude_mpm/hooks/claude_hooks/hook_handler.py +91 -755
  12. claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
  13. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
  14. claude_mpm/hooks/claude_hooks/services/__init__.py +13 -0
  15. claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
  16. claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
  17. claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
  18. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
  19. claude_mpm/services/agents/deployment/agent_deployment.py +42 -454
  20. claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
  21. claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
  22. claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
  23. claude_mpm/services/agents/memory/agent_memory_manager.py +42 -508
  24. claude_mpm/services/agents/memory/memory_categorization_service.py +165 -0
  25. claude_mpm/services/agents/memory/memory_file_service.py +103 -0
  26. claude_mpm/services/agents/memory/memory_format_service.py +201 -0
  27. claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
  28. claude_mpm/services/agents/registry/__init__.py +1 -1
  29. claude_mpm/services/cli/__init__.py +18 -0
  30. claude_mpm/services/cli/agent_cleanup_service.py +407 -0
  31. claude_mpm/services/cli/agent_dependency_service.py +395 -0
  32. claude_mpm/services/cli/agent_listing_service.py +463 -0
  33. claude_mpm/services/cli/agent_output_formatter.py +605 -0
  34. claude_mpm/services/cli/agent_validation_service.py +589 -0
  35. claude_mpm/services/cli/dashboard_launcher.py +424 -0
  36. claude_mpm/services/cli/memory_crud_service.py +617 -0
  37. claude_mpm/services/cli/memory_output_formatter.py +604 -0
  38. claude_mpm/services/cli/session_manager.py +513 -0
  39. claude_mpm/services/cli/socketio_manager.py +498 -0
  40. claude_mpm/services/cli/startup_checker.py +370 -0
  41. claude_mpm/services/core/cache_manager.py +311 -0
  42. claude_mpm/services/core/memory_manager.py +637 -0
  43. claude_mpm/services/core/path_resolver.py +498 -0
  44. claude_mpm/services/core/service_container.py +520 -0
  45. claude_mpm/services/core/service_interfaces.py +436 -0
  46. claude_mpm/services/diagnostics/checks/agent_check.py +65 -19
  47. {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/METADATA +1 -1
  48. {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/RECORD +52 -22
  49. claude_mpm/cli/commands/run_config_checker.py +0 -159
  50. {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/WHEEL +0 -0
  51. {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/entry_points.txt +0 -0
  52. {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/licenses/LICENSE +0 -0
  53. {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,604 @@
1
+ """
2
+ Memory Output Formatter Service
3
+ ===============================
4
+
5
+ WHY: This service consolidates all memory display and formatting logic that was
6
+ previously duplicated throughout the memory.py command file, reducing code by ~250 lines
7
+ and providing consistent, reusable formatting across the entire memory subsystem.
8
+
9
+ DESIGN DECISIONS:
10
+ - Extract all formatting methods from memory.py into a reusable service
11
+ - Support multiple output formats (text, json, yaml, table)
12
+ - Implement quiet and verbose modes for flexible output control
13
+ - Maintain consistent emoji usage across all memory displays
14
+ - Follow established service patterns with interface-based design
15
+ """
16
+
17
+ import json
18
+ from abc import ABC, abstractmethod
19
+ from datetime import datetime
20
+ from typing import Any, Dict, List, Optional, Tuple
21
+
22
+ import yaml
23
+
24
+ from ...core.logger import get_logger
25
+
26
+
27
+ class IMemoryOutputFormatter(ABC):
28
+ """Interface for memory output formatting service."""
29
+
30
+ @abstractmethod
31
+ def format_status(self, status_data: Dict[str, Any], verbose: bool = False) -> str:
32
+ """Format memory status display."""
33
+
34
+ @abstractmethod
35
+ def format_memory_view(
36
+ self, agent_id: str, memory_content: str, format_type: str = "detailed"
37
+ ) -> str:
38
+ """Format memory viewing output."""
39
+
40
+ @abstractmethod
41
+ def format_optimization_results(
42
+ self, results: Dict[str, Any], is_single_agent: bool = True
43
+ ) -> str:
44
+ """Format optimization output."""
45
+
46
+ @abstractmethod
47
+ def format_cross_reference(
48
+ self, cross_ref_data: Dict[str, Any], query: Optional[str] = None
49
+ ) -> str:
50
+ """Format pattern analysis output."""
51
+
52
+ @abstractmethod
53
+ def format_as_json(self, data: Dict[str, Any], pretty: bool = True) -> str:
54
+ """Format data as JSON."""
55
+
56
+ @abstractmethod
57
+ def format_as_yaml(self, data: Dict[str, Any]) -> str:
58
+ """Format data as YAML."""
59
+
60
+ @abstractmethod
61
+ def format_as_table(self, headers: List[str], rows: List[List[Any]]) -> str:
62
+ """Format data as a table."""
63
+
64
+ @abstractmethod
65
+ def format_build_results(self, results: Dict[str, Any]) -> str:
66
+ """Format memory build results."""
67
+
68
+ @abstractmethod
69
+ def format_agent_memories_summary(
70
+ self, agent_memories: Dict[str, Dict], format_type: str = "summary"
71
+ ) -> str:
72
+ """Format summary of all agent memories."""
73
+
74
+
75
+ class MemoryOutputFormatter(IMemoryOutputFormatter):
76
+ """Implementation of memory output formatting service."""
77
+
78
+ def __init__(self, quiet: bool = False):
79
+ """
80
+ Initialize the memory output formatter.
81
+
82
+ Args:
83
+ quiet: If True, minimize output (no emojis, minimal formatting)
84
+ """
85
+ self.quiet = quiet
86
+ self.logger = get_logger(__name__)
87
+
88
+ # Emoji mappings for consistent usage
89
+ self.emojis = {
90
+ "success": "✅" if not quiet else "[OK]",
91
+ "error": "❌" if not quiet else "[ERROR]",
92
+ "warning": "⚠️" if not quiet else "[WARN]",
93
+ "info": "ℹ️" if not quiet else "[INFO]",
94
+ "memory": "🧠" if not quiet else "[MEMORY]",
95
+ "file": "📁" if not quiet else "[FILE]",
96
+ "agent": "🤖" if not quiet else "[AGENT]",
97
+ "stats": "📊" if not quiet else "[STATS]",
98
+ "clean": "🧹" if not quiet else "[CLEAN]",
99
+ "optimize": "🔧" if not quiet else "[OPTIMIZE]",
100
+ "build": "📚" if not quiet else "[BUILD]",
101
+ "link": "🔗" if not quiet else "[LINK]",
102
+ "search": "🔍" if not quiet else "[SEARCH]",
103
+ "target": "🎯" if not quiet else "[TARGET]",
104
+ "book": "📖" if not quiet else "[SECTION]",
105
+ "page": "📋" if not quiet else "[PAGE]",
106
+ "handshake": "🤝" if not quiet else "[CORRELATION]",
107
+ "cycle": "🔄" if not quiet else "[PATTERN]",
108
+ "rocket": "🚀" if not quiet else "[INIT]",
109
+ "disk": "💾" if not quiet else "[SIZE]",
110
+ "green": "🟢" if not quiet else "[LOW]",
111
+ "yellow": "🟡" if not quiet else "[MED]",
112
+ "red": "🔴" if not quiet else "[HIGH]",
113
+ "empty": "📭" if not quiet else "[EMPTY]",
114
+ "note": "📝" if not quiet else "[NOTE]",
115
+ }
116
+
117
+ def format_status(self, status_data: Dict[str, Any], verbose: bool = False) -> str:
118
+ """Format memory status display."""
119
+ lines = []
120
+ lines.append("Agent Memory System Status")
121
+ lines.append("-" * 80)
122
+
123
+ if not status_data.get("success", True):
124
+ lines.append(
125
+ f"{self.emojis['error']} Error getting status: {status_data.get('error', 'Unknown error')}"
126
+ )
127
+ return "\n".join(lines)
128
+
129
+ # System overview
130
+ system_health = status_data.get("system_health")
131
+ if system_health is None:
132
+ system_health = "unknown"
133
+ health_emoji = {
134
+ "healthy": self.emojis["success"],
135
+ "needs_optimization": self.emojis["warning"],
136
+ "high_usage": self.emojis["stats"],
137
+ "no_memory_dir": self.emojis["file"],
138
+ }.get(system_health, self.emojis["info"])
139
+
140
+ lines.append(
141
+ f"{self.emojis['memory']} Memory System Health: {health_emoji} {system_health}"
142
+ )
143
+
144
+ # Handle null/None values properly
145
+ memory_dir = status_data.get("memory_directory")
146
+ lines.append(
147
+ f"{self.emojis['file']} Memory Directory: {memory_dir if memory_dir is not None else 'Unknown'}"
148
+ )
149
+
150
+ lines.append(
151
+ f"{self.emojis['optimize']} System Enabled: {'Yes' if status_data.get('system_enabled', True) else 'No'}"
152
+ )
153
+ lines.append(
154
+ f"{self.emojis['build']} Auto Learning: {'Yes' if status_data.get('auto_learning', True) else 'No'}"
155
+ )
156
+
157
+ total_agents = status_data.get("total_agents")
158
+ lines.append(
159
+ f"{self.emojis['stats']} Total Agents: {total_agents if total_agents is not None else 'Unknown'}"
160
+ )
161
+
162
+ lines.append(
163
+ f"{self.emojis['disk']} Total Size: {status_data.get('total_size_kb', 0):.1f} KB"
164
+ )
165
+ lines.append("")
166
+
167
+ # Optimization opportunities
168
+ opportunities = status_data.get("optimization_opportunities", [])
169
+ if opportunities:
170
+ lines.append(
171
+ f"{self.emojis['warning']} Optimization Opportunities ({len(opportunities)}):"
172
+ )
173
+ limit = 10 if verbose else 5
174
+ for opportunity in opportunities[:limit]:
175
+ lines.append(f" • {opportunity}")
176
+ if len(opportunities) > limit:
177
+ lines.append(f" ... and {len(opportunities) - limit} more")
178
+ lines.append("")
179
+
180
+ # Per-agent details
181
+ agents = status_data.get("agents", {})
182
+ if agents:
183
+ lines.append(f"{self.emojis['page']} Agent Memory Details:")
184
+ for agent_id, agent_info in sorted(agents.items()):
185
+ if "error" in agent_info:
186
+ lines.append(
187
+ f" {self.emojis['error']} {agent_id}: Error - {agent_info['error']}"
188
+ )
189
+ continue
190
+
191
+ size_kb = agent_info.get("size_kb", 0)
192
+ size_limit = agent_info.get("size_limit_kb", 8)
193
+ utilization = agent_info.get("size_utilization", 0)
194
+ sections = agent_info.get("sections", 0)
195
+ items = agent_info.get("items", 0)
196
+ last_modified = agent_info.get("last_modified", "Unknown")
197
+ auto_learning = agent_info.get("auto_learning", True)
198
+
199
+ # Format last modified time
200
+ try:
201
+ dt = datetime.fromisoformat(last_modified.replace("Z", "+00:00"))
202
+ last_modified_str = dt.strftime("%Y-%m-%d %H:%M:%S")
203
+ except:
204
+ last_modified_str = last_modified
205
+
206
+ # Status indicator based on usage
207
+ if utilization > 90:
208
+ status_emoji = self.emojis["red"]
209
+ elif utilization > 70:
210
+ status_emoji = self.emojis["yellow"]
211
+ else:
212
+ status_emoji = self.emojis["green"]
213
+
214
+ lines.append(f" {status_emoji} {agent_id}")
215
+ lines.append(
216
+ f" Size: {size_kb:.1f} KB / {size_limit} KB ({utilization:.1f}%)"
217
+ )
218
+ lines.append(f" Content: {sections} sections, {items} items")
219
+ lines.append(f" Auto-learning: {'On' if auto_learning else 'Off'}")
220
+ lines.append(f" Last modified: {last_modified_str}")
221
+
222
+ if verbose and agent_info.get("recent_items"):
223
+ lines.append(" Recent items:")
224
+ for item in agent_info["recent_items"][:3]:
225
+ lines.append(f" - {item}")
226
+ else:
227
+ lines.append(f"{self.emojis['empty']} No agent memories found")
228
+
229
+ return "\n".join(lines)
230
+
231
+ def format_memory_view(
232
+ self, agent_id: str, memory_content: str, format_type: str = "detailed"
233
+ ) -> str:
234
+ """Format memory viewing output."""
235
+ lines = []
236
+
237
+ if not memory_content:
238
+ lines.append(
239
+ f"{self.emojis['empty']} No memory found for agent: {agent_id}"
240
+ )
241
+ return "\n".join(lines)
242
+
243
+ lines.append(f"{self.emojis['agent']} Agent: {agent_id}")
244
+ lines.append("-" * 40)
245
+
246
+ if format_type == "full":
247
+ lines.append(memory_content)
248
+ else:
249
+ # Parse and display memory sections
250
+ sections = self._parse_memory_content(memory_content)
251
+
252
+ for section_name, items in sections.items():
253
+ if items:
254
+ lines.append(
255
+ f"\n{self.emojis['build']} {section_name} ({len(items)} items):"
256
+ )
257
+ limit = 10 if format_type == "detailed" else 5
258
+ for i, item in enumerate(items[:limit], 1):
259
+ lines.append(f" {i}. {item}")
260
+ if len(items) > limit:
261
+ lines.append(f" ... and {len(items) - limit} more")
262
+
263
+ return "\n".join(lines)
264
+
265
+ def format_optimization_results(
266
+ self, results: Dict[str, Any], is_single_agent: bool = True
267
+ ) -> str:
268
+ """Format optimization output."""
269
+ lines = []
270
+
271
+ if is_single_agent:
272
+ agent_id = results.get("agent_id", "unknown")
273
+ original_size = results.get("original_size", 0)
274
+ optimized_size = results.get("optimized_size", 0)
275
+ size_reduction = results.get("size_reduction", 0)
276
+ size_reduction_percent = results.get("size_reduction_percent", 0)
277
+
278
+ lines.append(
279
+ f"{self.emojis['success']} Optimization completed for {agent_id}"
280
+ )
281
+ lines.append(f" Original size: {original_size:,} bytes")
282
+ lines.append(f" Optimized size: {optimized_size:,} bytes")
283
+ lines.append(
284
+ f" Size reduction: {size_reduction:,} bytes ({size_reduction_percent}%)"
285
+ )
286
+
287
+ duplicates = results.get("duplicates_removed", 0)
288
+ consolidated = results.get("items_consolidated", 0)
289
+ reordered = results.get("items_reordered", 0)
290
+
291
+ if duplicates > 0:
292
+ lines.append(f" Duplicates removed: {duplicates}")
293
+ if consolidated > 0:
294
+ lines.append(f" Items consolidated: {consolidated}")
295
+ if reordered > 0:
296
+ lines.append(f" Sections reordered: {reordered}")
297
+
298
+ backup_path = results.get("backup_created")
299
+ if backup_path:
300
+ lines.append(f" Backup created: {backup_path}")
301
+ else:
302
+ # Bulk optimization results
303
+ summary = results.get("summary", {})
304
+
305
+ lines.append(f"{self.emojis['success']} Bulk optimization completed")
306
+ lines.append(f" Agents processed: {summary.get('agents_processed', 0)}")
307
+ lines.append(f" Agents optimized: {summary.get('agents_optimized', 0)}")
308
+ lines.append(
309
+ f" Total size before: {summary.get('total_size_before', 0):,} bytes"
310
+ )
311
+ lines.append(
312
+ f" Total size after: {summary.get('total_size_after', 0):,} bytes"
313
+ )
314
+ lines.append(
315
+ f" Total reduction: {summary.get('total_size_reduction', 0):,} bytes ({summary.get('total_size_reduction_percent', 0)}%)"
316
+ )
317
+ lines.append(
318
+ f" Total duplicates removed: {summary.get('total_duplicates_removed', 0)}"
319
+ )
320
+ lines.append(
321
+ f" Total items consolidated: {summary.get('total_items_consolidated', 0)}"
322
+ )
323
+
324
+ # Per-agent summary
325
+ agents_results = results.get("agents", {})
326
+ if agents_results:
327
+ lines.append(f"\n{self.emojis['stats']} Per-agent results:")
328
+ for agent_id, agent_result in agents_results.items():
329
+ if agent_result.get("success"):
330
+ reduction = agent_result.get("size_reduction_percent", 0)
331
+ duplicates = agent_result.get("duplicates_removed", 0)
332
+ consolidated = agent_result.get("items_consolidated", 0)
333
+
334
+ status_parts = []
335
+ if duplicates > 0:
336
+ status_parts.append(f"{duplicates} dupes")
337
+ if consolidated > 0:
338
+ status_parts.append(f"{consolidated} consolidated")
339
+
340
+ status = f" ({', '.join(status_parts)})" if status_parts else ""
341
+ lines.append(f" {agent_id}: {reduction}% reduction{status}")
342
+ else:
343
+ error = agent_result.get("error", "Unknown error")
344
+ lines.append(f" {agent_id}: {self.emojis['error']} {error}")
345
+
346
+ return "\n".join(lines)
347
+
348
+ def format_cross_reference(
349
+ self, cross_ref_data: Dict[str, Any], query: Optional[str] = None
350
+ ) -> str:
351
+ """Format pattern analysis output."""
352
+ lines = []
353
+ lines.append(f"{self.emojis['link']} Memory Cross-Reference Analysis")
354
+ lines.append("-" * 80)
355
+
356
+ if query:
357
+ lines.append(f"{self.emojis['search']} Searching for: '{query}'")
358
+ else:
359
+ lines.append(
360
+ f"{self.emojis['search']} Analyzing all agent memories for patterns..."
361
+ )
362
+
363
+ if cross_ref_data.get("success") is False:
364
+ lines.append(
365
+ f"{self.emojis['error']} Analysis failed: {cross_ref_data.get('error', 'Unknown error')}"
366
+ )
367
+ return "\n".join(lines)
368
+
369
+ # Display common patterns
370
+ common_patterns = cross_ref_data.get("common_patterns", [])
371
+ if common_patterns:
372
+ lines.append(
373
+ f"\n{self.emojis['cycle']} Common patterns found ({len(common_patterns)}):"
374
+ )
375
+ for pattern in common_patterns[:10]:
376
+ agents = ", ".join(pattern["agents"])
377
+ lines.append(f" • {pattern['pattern']}")
378
+ lines.append(f" Found in: {agents} ({pattern['count']} instances)")
379
+ else:
380
+ lines.append(f"\n{self.emojis['cycle']} No common patterns found")
381
+
382
+ # Display query matches if query was provided
383
+ if query and cross_ref_data.get("query_matches"):
384
+ lines.append(f"\n{self.emojis['target']} Query matches for '{query}':")
385
+ for match in cross_ref_data["query_matches"]:
386
+ lines.append(f" {self.emojis['page']} {match['agent']}:")
387
+ for line in match["matches"][:3]:
388
+ lines.append(f" • {line}")
389
+
390
+ # Display agent correlations
391
+ correlations = cross_ref_data.get("agent_correlations", {})
392
+ if correlations:
393
+ lines.append(f"\n{self.emojis['handshake']} Agent knowledge correlations:")
394
+ sorted_correlations = sorted(
395
+ correlations.items(), key=lambda x: x[1], reverse=True
396
+ )
397
+ for agents, count in sorted_correlations[:5]:
398
+ lines.append(f" {agents}: {count} common items")
399
+ else:
400
+ lines.append(
401
+ f"\n{self.emojis['handshake']} No significant correlations found"
402
+ )
403
+
404
+ return "\n".join(lines)
405
+
406
+ def format_as_json(self, data: Dict[str, Any], pretty: bool = True) -> str:
407
+ """Format data as JSON."""
408
+ if pretty:
409
+ return json.dumps(data, indent=2, ensure_ascii=False)
410
+ return json.dumps(data, ensure_ascii=False)
411
+
412
+ def format_as_yaml(self, data: Dict[str, Any]) -> str:
413
+ """Format data as YAML."""
414
+ return yaml.dump(data, default_flow_style=False, allow_unicode=True)
415
+
416
+ def format_as_table(self, headers: List[str], rows: List[List[Any]]) -> str:
417
+ """Format data as a table."""
418
+ lines = []
419
+
420
+ # Calculate column widths
421
+ col_widths = [len(str(h)) for h in headers]
422
+ for row in rows:
423
+ for i, cell in enumerate(row):
424
+ col_widths[i] = max(col_widths[i], len(str(cell)))
425
+
426
+ # Create header
427
+ header_line = " | ".join(str(h).ljust(w) for h, w in zip(headers, col_widths))
428
+ lines.append(header_line)
429
+ lines.append("-" * len(header_line))
430
+
431
+ # Add rows
432
+ for row in rows:
433
+ row_line = " | ".join(
434
+ str(cell).ljust(w) for cell, w in zip(row, col_widths)
435
+ )
436
+ lines.append(row_line)
437
+
438
+ return "\n".join(lines)
439
+
440
+ def format_build_results(self, results: Dict[str, Any]) -> str:
441
+ """Format memory build results."""
442
+ lines = []
443
+ lines.append(f"{self.emojis['build']} Memory Building from Documentation")
444
+ lines.append("-" * 80)
445
+
446
+ if results.get("success"):
447
+ lines.append(
448
+ f"{self.emojis['success']} Successfully processed documentation"
449
+ )
450
+ lines.append(f" Files processed: {results.get('files_processed', 0)}")
451
+ lines.append(f" Memories created: {results.get('memories_created', 0)}")
452
+ lines.append(f" Memories updated: {results.get('memories_updated', 0)}")
453
+ lines.append(
454
+ f" Agents affected: {results.get('total_agents_affected', 0)}"
455
+ )
456
+
457
+ if results.get("agents_affected"):
458
+ lines.append(
459
+ f" Affected agents: {', '.join(results['agents_affected'])}"
460
+ )
461
+
462
+ # Show file-specific results
463
+ files_results = results.get("files", {})
464
+ if files_results:
465
+ lines.append(f"\n{self.emojis['page']} File processing details:")
466
+ for file_path, file_result in files_results.items():
467
+ if file_result.get("success"):
468
+ extracted = file_result.get("items_extracted", 0)
469
+ created = file_result.get("memories_created", 0)
470
+ lines.append(
471
+ f" {file_path}: {extracted} items extracted, {created} memories created"
472
+ )
473
+
474
+ if results.get("errors"):
475
+ lines.append(f"\n{self.emojis['warning']} Errors encountered:")
476
+ for error in results["errors"]:
477
+ lines.append(f" {error}")
478
+ else:
479
+ lines.append(
480
+ f"{self.emojis['error']} Build failed: {results.get('error', 'Unknown error')}"
481
+ )
482
+
483
+ return "\n".join(lines)
484
+
485
+ def format_agent_memories_summary(
486
+ self, agent_memories: Dict[str, Dict], format_type: str = "summary"
487
+ ) -> str:
488
+ """Format summary of all agent memories."""
489
+ lines = []
490
+
491
+ if not agent_memories:
492
+ lines.append(f"{self.emojis['empty']} No agent memories found")
493
+ return "\n".join(lines)
494
+
495
+ lines.append(
496
+ f"{self.emojis['stats']} Found memories for {len(agent_memories)} agents"
497
+ )
498
+ lines.append("")
499
+
500
+ total_items = 0
501
+ for agent_id, sections in sorted(agent_memories.items()):
502
+ item_count = sum(len(items) for items in sections.values())
503
+ total_items += item_count
504
+
505
+ lines.append(f"{self.emojis['agent']} {agent_id}")
506
+ lines.append(
507
+ f" {self.emojis['build']} {len(sections)} sections, {item_count} total items"
508
+ )
509
+
510
+ if format_type == "summary":
511
+ # Show section summary
512
+ for section_name, items in sections.items():
513
+ if items:
514
+ lines.append(f" • {section_name}: {len(items)} items")
515
+ elif format_type == "detailed":
516
+ for section_name, items in sections.items():
517
+ if items:
518
+ lines.append(f"\n {self.emojis['book']} {section_name}:")
519
+ for item in items[:3]:
520
+ lines.append(f" • {item}")
521
+ if len(items) > 3:
522
+ lines.append(f" ... and {len(items) - 3} more")
523
+ lines.append("")
524
+
525
+ lines.append(
526
+ f"{self.emojis['stats']} Total: {total_items} memory items across {len(agent_memories)} agents"
527
+ )
528
+
529
+ # Show cross-references if we have multiple agents
530
+ if len(agent_memories) > 1:
531
+ lines.append(
532
+ f"\n{self.emojis['link']} Cross-References and Common Patterns:"
533
+ )
534
+ common_patterns = self._find_common_patterns(agent_memories)
535
+ if common_patterns:
536
+ lines.append(f"\n{self.emojis['cycle']} Most Common Patterns:")
537
+ for pattern, count, agents in common_patterns[:5]:
538
+ lines.append(
539
+ f" • {pattern[:80]}{'...' if len(pattern) > 80 else ''}"
540
+ )
541
+ lines.append(f" Found in: {', '.join(agents)} ({count} agents)")
542
+ else:
543
+ lines.append(" No common patterns found across agents")
544
+
545
+ return "\n".join(lines)
546
+
547
+ def _parse_memory_content(self, content: str) -> Dict[str, List[str]]:
548
+ """Parse memory content into sections and items."""
549
+ sections = {}
550
+ current_section = None
551
+ current_items = []
552
+
553
+ for line in content.split("\n"):
554
+ line = line.strip()
555
+
556
+ if line.startswith("## ") and not line.startswith("## Memory Usage"):
557
+ # New section
558
+ if current_section and current_items:
559
+ sections[current_section] = current_items.copy()
560
+
561
+ current_section = line[3:].strip()
562
+ current_items = []
563
+ elif line.startswith("- ") and current_section:
564
+ # Item in current section
565
+ item = line[2:].strip()
566
+ if item and len(item) > 5: # Filter out very short items
567
+ current_items.append(item)
568
+
569
+ # Add final section
570
+ if current_section and current_items:
571
+ sections[current_section] = current_items
572
+
573
+ return sections
574
+
575
+ def _find_common_patterns(
576
+ self, agent_memories: Dict[str, Dict]
577
+ ) -> List[Tuple[str, int, List[str]]]:
578
+ """Find common patterns across agent memories."""
579
+ pattern_agents = {}
580
+
581
+ # Collect all patterns and which agents have them
582
+ for agent_id, sections in agent_memories.items():
583
+ seen_patterns = set()
584
+
585
+ for _section_name, items in sections.items():
586
+ for item in items:
587
+ # Normalize item for comparison (lowercase, basic cleanup)
588
+ normalized = item.lower().strip()
589
+ if len(normalized) > 10 and normalized not in seen_patterns:
590
+ if normalized not in pattern_agents:
591
+ pattern_agents[normalized] = []
592
+ pattern_agents[normalized].append(agent_id)
593
+ seen_patterns.add(normalized)
594
+
595
+ # Find patterns that appear in multiple agents
596
+ common_patterns = []
597
+ for pattern, agents in pattern_agents.items():
598
+ if len(agents) > 1:
599
+ common_patterns.append((pattern, len(agents), agents))
600
+
601
+ # Sort by number of agents
602
+ common_patterns.sort(key=lambda x: x[1], reverse=True)
603
+
604
+ return common_patterns