claude-mpm 5.0.2__py3-none-any.whl → 5.1.9__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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (76) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
  3. claude_mpm/agents/PM_INSTRUCTIONS.md +1176 -909
  4. claude_mpm/agents/base_agent_loader.py +10 -35
  5. claude_mpm/agents/frontmatter_validator.py +68 -0
  6. claude_mpm/agents/templates/circuit-breakers.md +293 -44
  7. claude_mpm/cli/__init__.py +0 -1
  8. claude_mpm/cli/commands/__init__.py +2 -0
  9. claude_mpm/cli/commands/agent_state_manager.py +64 -11
  10. claude_mpm/cli/commands/agents.py +446 -25
  11. claude_mpm/cli/commands/auto_configure.py +535 -233
  12. claude_mpm/cli/commands/configure.py +545 -89
  13. claude_mpm/cli/commands/postmortem.py +401 -0
  14. claude_mpm/cli/commands/run.py +1 -39
  15. claude_mpm/cli/commands/skills.py +322 -19
  16. claude_mpm/cli/interactive/agent_wizard.py +302 -195
  17. claude_mpm/cli/parsers/agents_parser.py +137 -0
  18. claude_mpm/cli/parsers/auto_configure_parser.py +13 -0
  19. claude_mpm/cli/parsers/base_parser.py +4 -0
  20. claude_mpm/cli/parsers/skills_parser.py +7 -0
  21. claude_mpm/cli/startup.py +73 -32
  22. claude_mpm/commands/mpm-agents-auto-configure.md +2 -2
  23. claude_mpm/commands/mpm-agents-list.md +2 -2
  24. claude_mpm/commands/mpm-config-view.md +2 -2
  25. claude_mpm/commands/mpm-help.md +3 -0
  26. claude_mpm/commands/mpm-postmortem.md +123 -0
  27. claude_mpm/commands/mpm-session-resume.md +2 -2
  28. claude_mpm/commands/mpm-ticket-organize.md +2 -2
  29. claude_mpm/commands/mpm-ticket-view.md +2 -2
  30. claude_mpm/config/agent_presets.py +312 -82
  31. claude_mpm/config/skill_presets.py +392 -0
  32. claude_mpm/constants.py +1 -0
  33. claude_mpm/core/claude_runner.py +2 -25
  34. claude_mpm/core/framework/loaders/file_loader.py +54 -101
  35. claude_mpm/core/interactive_session.py +19 -5
  36. claude_mpm/core/oneshot_session.py +16 -4
  37. claude_mpm/core/output_style_manager.py +173 -43
  38. claude_mpm/core/protocols/__init__.py +23 -0
  39. claude_mpm/core/protocols/runner_protocol.py +103 -0
  40. claude_mpm/core/protocols/session_protocol.py +131 -0
  41. claude_mpm/core/shared/singleton_manager.py +11 -4
  42. claude_mpm/core/system_context.py +38 -0
  43. claude_mpm/core/unified_agent_registry.py +129 -1
  44. claude_mpm/core/unified_config.py +22 -0
  45. claude_mpm/hooks/claude_hooks/memory_integration.py +12 -1
  46. claude_mpm/models/agent_definition.py +7 -0
  47. claude_mpm/services/agents/cache_git_manager.py +621 -0
  48. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +110 -3
  49. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +195 -1
  50. claude_mpm/services/agents/sources/git_source_sync_service.py +37 -5
  51. claude_mpm/services/analysis/__init__.py +25 -0
  52. claude_mpm/services/analysis/postmortem_reporter.py +474 -0
  53. claude_mpm/services/analysis/postmortem_service.py +765 -0
  54. claude_mpm/services/command_deployment_service.py +108 -5
  55. claude_mpm/services/core/base.py +7 -2
  56. claude_mpm/services/diagnostics/checks/mcp_services_check.py +7 -15
  57. claude_mpm/services/git/git_operations_service.py +8 -8
  58. claude_mpm/services/mcp_config_manager.py +75 -145
  59. claude_mpm/services/mcp_gateway/core/process_pool.py +22 -16
  60. claude_mpm/services/mcp_service_verifier.py +6 -3
  61. claude_mpm/services/monitor/daemon.py +28 -8
  62. claude_mpm/services/monitor/daemon_manager.py +96 -19
  63. claude_mpm/services/project/project_organizer.py +4 -0
  64. claude_mpm/services/runner_configuration_service.py +16 -3
  65. claude_mpm/services/session_management_service.py +16 -4
  66. claude_mpm/utils/agent_filters.py +288 -0
  67. claude_mpm/utils/gitignore.py +3 -0
  68. claude_mpm/utils/migration.py +372 -0
  69. claude_mpm/utils/progress.py +5 -1
  70. {claude_mpm-5.0.2.dist-info → claude_mpm-5.1.9.dist-info}/METADATA +69 -8
  71. {claude_mpm-5.0.2.dist-info → claude_mpm-5.1.9.dist-info}/RECORD +76 -62
  72. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  73. {claude_mpm-5.0.2.dist-info → claude_mpm-5.1.9.dist-info}/WHEEL +0 -0
  74. {claude_mpm-5.0.2.dist-info → claude_mpm-5.1.9.dist-info}/entry_points.txt +0 -0
  75. {claude_mpm-5.0.2.dist-info → claude_mpm-5.1.9.dist-info}/licenses/LICENSE +0 -0
  76. {claude_mpm-5.0.2.dist-info → claude_mpm-5.1.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,474 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Postmortem Reporter
4
+ ===================
5
+
6
+ Formats postmortem analysis reports in various output formats (terminal, JSON, markdown).
7
+
8
+ WHY: Different consumption contexts require different formats. Terminal output
9
+ needs to be concise and visually organized, JSON is for machine processing,
10
+ and markdown is for documentation.
11
+
12
+ DESIGN DECISION: Follows the same pattern as DoctorReporter for consistency.
13
+ """
14
+
15
+ import json
16
+ import sys
17
+ from typing import Optional, TextIO
18
+
19
+ from claude_mpm.core.logging_utils import get_logger
20
+
21
+ from .postmortem_service import (
22
+ ActionType,
23
+ ErrorAnalysis,
24
+ ErrorCategory,
25
+ ImprovementAction,
26
+ PostmortemReport,
27
+ )
28
+
29
+ logger = get_logger(__name__)
30
+
31
+
32
+ class PostmortemReporter:
33
+ """Reporter for postmortem analysis results.
34
+
35
+ WHY: Provides consistent, well-formatted output across different formats
36
+ with color support and clear visual hierarchy.
37
+ """
38
+
39
+ # ANSI color codes
40
+ COLORS = {
41
+ "reset": "\033[0m",
42
+ "bold": "\033[1m",
43
+ "red": "\033[91m",
44
+ "green": "\033[92m",
45
+ "yellow": "\033[93m",
46
+ "blue": "\033[94m",
47
+ "magenta": "\033[95m",
48
+ "cyan": "\033[96m",
49
+ }
50
+
51
+ # Emoji/symbol mapping
52
+ SYMBOLS = {
53
+ "error": "❌",
54
+ "success": "✓",
55
+ "warning": "⚠️",
56
+ "info": "ℹ️",
57
+ "script": "🔧",
58
+ "skill": "📚",
59
+ "agent": "🤖",
60
+ "user": "💡",
61
+ "chart": "📊",
62
+ "summary": "📈",
63
+ }
64
+
65
+ def __init__(
66
+ self,
67
+ use_color: bool = True,
68
+ verbose: bool = False,
69
+ output: Optional[TextIO] = None,
70
+ ):
71
+ """Initialize reporter.
72
+
73
+ Args:
74
+ use_color: Enable colored output
75
+ verbose: Include detailed information
76
+ output: Output stream (defaults to stdout)
77
+ """
78
+ self.use_color = use_color and sys.stdout.isatty()
79
+ self.verbose = verbose
80
+ self.output = output or sys.stdout
81
+
82
+ def report(self, report: PostmortemReport, format: str = "terminal") -> None:
83
+ """Generate report in specified format.
84
+
85
+ Args:
86
+ report: Postmortem report to format
87
+ format: Output format (terminal/json/markdown)
88
+ """
89
+ if format == "json":
90
+ self._report_json(report)
91
+ elif format == "markdown":
92
+ self._report_markdown(report)
93
+ else:
94
+ self._report_terminal(report)
95
+
96
+ def _report_terminal(self, report: PostmortemReport) -> None:
97
+ """Generate terminal-formatted report.
98
+
99
+ Args:
100
+ report: Postmortem report
101
+ """
102
+ # Header
103
+ self._print_header(report)
104
+
105
+ # Error breakdown by category
106
+ self._print_error_categories(report)
107
+
108
+ # Actions by type
109
+ self._print_actions(report)
110
+
111
+ # Summary
112
+ self._print_summary(report)
113
+
114
+ def _print_header(self, report: PostmortemReport) -> None:
115
+ """Print report header.
116
+
117
+ Args:
118
+ report: Postmortem report
119
+ """
120
+ self._print_colored(
121
+ f"\n{self.SYMBOLS['chart']} Session Postmortem Analysis",
122
+ "bold",
123
+ )
124
+ self._print("═" * 50)
125
+ self._print()
126
+
127
+ # Session info
128
+ self._print(f"Session: {report.session_id}")
129
+
130
+ # Duration
131
+ minutes = int(report.duration_seconds // 60)
132
+ seconds = int(report.duration_seconds % 60)
133
+ duration_str = f"{minutes} minute{'s' if minutes != 1 else ''}"
134
+ if seconds > 0:
135
+ duration_str += f", {seconds} second{'s' if seconds != 1 else ''}"
136
+ self._print(f"Duration: {duration_str}")
137
+
138
+ # Error count with color
139
+ error_color = "red" if report.total_errors > 0 else "green"
140
+ self._print_colored(
141
+ f"Errors Found: {report.total_errors}",
142
+ error_color if report.total_errors > 0 else "green",
143
+ )
144
+ self._print()
145
+
146
+ def _print_error_categories(self, report: PostmortemReport) -> None:
147
+ """Print errors grouped by category.
148
+
149
+ Args:
150
+ report: Postmortem report
151
+ """
152
+ categories = [
153
+ (ErrorCategory.SCRIPT, "script", "Script Errors"),
154
+ (ErrorCategory.SKILL, "skill", "Skill Errors"),
155
+ (ErrorCategory.AGENT, "agent", "Agent Improvements"),
156
+ (ErrorCategory.USER_CODE, "user", "User Code Suggestions"),
157
+ ]
158
+
159
+ for category, symbol_key, title in categories:
160
+ analyses = report.get_analyses_by_category(category)
161
+ count = len(analyses)
162
+
163
+ # Section header
164
+ self._print_colored(
165
+ f"{self.SYMBOLS[symbol_key]} {title} ({count})",
166
+ "bold",
167
+ )
168
+ self._print("─" * 50)
169
+
170
+ if count == 0:
171
+ self._print_colored("No issues detected\n", "green")
172
+ continue
173
+
174
+ # Print each error
175
+ for i, analysis in enumerate(analyses, 1):
176
+ self._print_error_analysis(analysis, i)
177
+
178
+ self._print()
179
+
180
+ def _print_error_analysis(self, analysis: ErrorAnalysis, index: int) -> None:
181
+ """Print single error analysis.
182
+
183
+ Args:
184
+ analysis: Error analysis
185
+ index: Error index number
186
+ """
187
+ # Error header with file
188
+ file_str = str(analysis.affected_file) if analysis.affected_file else "Unknown"
189
+ self._print(f"{index}. {file_str}")
190
+
191
+ # Root cause
192
+ self._print(f" Root Cause: {analysis.root_cause}")
193
+
194
+ # Fix suggestion
195
+ self._print(f" Fix: {analysis.fix_suggestion}")
196
+
197
+ # Priority indicator
198
+ priority_colors = {
199
+ "critical": "red",
200
+ "high": "yellow",
201
+ "medium": "cyan",
202
+ "low": "reset",
203
+ }
204
+ priority_color = priority_colors.get(analysis.priority, "reset")
205
+ self._print_colored(f" Priority: {analysis.priority}", priority_color)
206
+
207
+ # Status
208
+ if analysis.failure_event.fixed:
209
+ self._print_colored(f" Status: Fixed {self.SYMBOLS['success']}", "green")
210
+ else:
211
+ self._print(" Status: Unfixed")
212
+
213
+ # Verbose details
214
+ if self.verbose:
215
+ self._print(
216
+ f" Error Type: {analysis.metadata.get('error_type', 'unknown')}"
217
+ )
218
+ self._print(f" Tool: {analysis.metadata.get('tool', 'unknown')}")
219
+
220
+ self._print()
221
+
222
+ def _print_actions(self, report: PostmortemReport) -> None:
223
+ """Print improvement actions grouped by type.
224
+
225
+ Args:
226
+ report: Postmortem report
227
+ """
228
+ self._print_colored(
229
+ f"{self.SYMBOLS['info']} Improvement Actions",
230
+ "bold",
231
+ )
232
+ self._print("═" * 50)
233
+ self._print()
234
+
235
+ # Group actions by type
236
+ action_groups = [
237
+ (ActionType.AUTO_FIX, "Auto-Fix Actions"),
238
+ (ActionType.UPDATE_FILE, "File Update Actions"),
239
+ (ActionType.CREATE_PR, "PR Creation Actions"),
240
+ (ActionType.SUGGEST, "Suggestions"),
241
+ ]
242
+
243
+ for action_type, title in action_groups:
244
+ actions = report.get_actions_by_type(action_type)
245
+ if not actions:
246
+ continue
247
+
248
+ self._print_colored(f"{title} ({len(actions)})", "cyan")
249
+ self._print()
250
+
251
+ for i, action in enumerate(actions, 1):
252
+ self._print_action(action, i)
253
+
254
+ self._print()
255
+
256
+ def _print_action(self, action: ImprovementAction, index: int) -> None:
257
+ """Print single improvement action.
258
+
259
+ Args:
260
+ action: Improvement action
261
+ index: Action index number
262
+ """
263
+ # Status indicator
264
+ status_indicators = {
265
+ "pending": f"{self.SYMBOLS['info']} Pending",
266
+ "completed": f"{self.SYMBOLS['success']} Completed",
267
+ "failed": f"{self.SYMBOLS['error']} Failed",
268
+ }
269
+ status_str = status_indicators.get(action.status, action.status)
270
+
271
+ self._print(f"{index}. {action.description}")
272
+ self._print(f" Status: {status_str}")
273
+
274
+ # Action-specific details
275
+ if action.action_type == ActionType.AUTO_FIX and action.commands:
276
+ self._print(" Commands:")
277
+ for cmd in action.commands:
278
+ self._print(f" • {cmd}")
279
+
280
+ if action.action_type == ActionType.CREATE_PR:
281
+ if action.pr_branch:
282
+ self._print(f" Branch: {action.pr_branch}")
283
+ if self.verbose and action.pr_title:
284
+ self._print(f" PR Title: {action.pr_title}")
285
+
286
+ if action.error_message:
287
+ self._print_colored(f" Error: {action.error_message}", "red")
288
+
289
+ self._print()
290
+
291
+ def _print_summary(self, report: PostmortemReport) -> None:
292
+ """Print summary statistics.
293
+
294
+ Args:
295
+ report: Postmortem report
296
+ """
297
+ self._print_colored(
298
+ f"{self.SYMBOLS['summary']} Summary",
299
+ "bold",
300
+ )
301
+ self._print("─" * 50)
302
+
303
+ stats = report.stats
304
+
305
+ # Error breakdown
306
+ self._print(f"Total Errors: {stats['total_errors']}")
307
+ self._print(f" • Script Errors: {stats['script_errors']}")
308
+ self._print(f" • Skill Errors: {stats['skill_errors']}")
309
+ self._print(f" • Agent Issues: {stats['agent_errors']}")
310
+ self._print(f" • User Code Issues: {stats['user_code_errors']}")
311
+ self._print()
312
+
313
+ # Priority breakdown
314
+ self._print("Priority Breakdown:")
315
+ self._print_colored(f" • Critical: {stats['critical_priority']}", "red")
316
+ self._print_colored(f" • High: {stats['high_priority']}", "yellow")
317
+ self._print()
318
+
319
+ # Actions
320
+ self._print(f"Total Actions: {stats['total_actions']}")
321
+ self._print(f" • Auto-fixable: {stats['auto_fixable']}")
322
+ self._print(f" • PR Actions: {stats['pr_actions']}")
323
+ self._print()
324
+
325
+ def _report_json(self, report: PostmortemReport) -> None:
326
+ """Generate JSON-formatted report.
327
+
328
+ Args:
329
+ report: Postmortem report
330
+ """
331
+ # Convert report to dict
332
+ report_dict = {
333
+ "session_id": report.session_id,
334
+ "start_time": report.start_time.isoformat(),
335
+ "duration_seconds": report.duration_seconds,
336
+ "total_errors": report.total_errors,
337
+ "stats": report.stats,
338
+ "analyses": [
339
+ {
340
+ "category": a.category.value,
341
+ "root_cause": a.root_cause,
342
+ "affected_file": str(a.affected_file) if a.affected_file else None,
343
+ "action_type": a.action_type.value,
344
+ "fix_suggestion": a.fix_suggestion,
345
+ "priority": a.priority,
346
+ "auto_fixable": a.auto_fixable,
347
+ "fixed": a.failure_event.fixed,
348
+ "error_message": a.failure_event.error_message,
349
+ "metadata": a.metadata,
350
+ }
351
+ for a in report.analyses
352
+ ],
353
+ "actions": [
354
+ {
355
+ "action_type": a.action_type.value,
356
+ "description": a.description,
357
+ "status": a.status,
358
+ "commands": a.commands,
359
+ "pr_branch": a.pr_branch,
360
+ "pr_title": a.pr_title,
361
+ "error_message": a.error_message,
362
+ }
363
+ for a in report.actions
364
+ ],
365
+ }
366
+
367
+ json.dump(report_dict, self.output, indent=2)
368
+ self.output.write("\n")
369
+
370
+ def _report_markdown(self, report: PostmortemReport) -> None:
371
+ """Generate markdown-formatted report.
372
+
373
+ Args:
374
+ report: Postmortem report
375
+ """
376
+ # Header
377
+ self._print("# Session Postmortem Analysis\n")
378
+ self._print(f"**Session:** {report.session_id} ")
379
+
380
+ minutes = int(report.duration_seconds // 60)
381
+ self._print(f"**Duration:** {minutes} minutes ")
382
+ self._print(f"**Errors Found:** {report.total_errors}\n")
383
+
384
+ # Error categories
385
+ self._print("## Error Analysis\n")
386
+
387
+ categories = [
388
+ (ErrorCategory.SCRIPT, "Script Errors"),
389
+ (ErrorCategory.SKILL, "Skill Errors"),
390
+ (ErrorCategory.AGENT, "Agent Improvements"),
391
+ (ErrorCategory.USER_CODE, "User Code Suggestions"),
392
+ ]
393
+
394
+ for category, title in categories:
395
+ analyses = report.get_analyses_by_category(category)
396
+ self._print(f"### {title} ({len(analyses)})\n")
397
+
398
+ if not analyses:
399
+ self._print("No issues detected.\n")
400
+ continue
401
+
402
+ for i, analysis in enumerate(analyses, 1):
403
+ self._print(f"{i}. **{analysis.affected_file or 'Unknown'}**")
404
+ self._print(f" - **Root Cause:** {analysis.root_cause}")
405
+ self._print(f" - **Fix:** {analysis.fix_suggestion}")
406
+ self._print(f" - **Priority:** {analysis.priority}")
407
+ status = "Fixed ✓" if analysis.failure_event.fixed else "Unfixed"
408
+ self._print(f" - **Status:** {status}\n")
409
+
410
+ # Actions
411
+ self._print("## Improvement Actions\n")
412
+
413
+ action_groups = [
414
+ (ActionType.AUTO_FIX, "Auto-Fix Actions"),
415
+ (ActionType.UPDATE_FILE, "File Update Actions"),
416
+ (ActionType.CREATE_PR, "PR Creation Actions"),
417
+ (ActionType.SUGGEST, "Suggestions"),
418
+ ]
419
+
420
+ for action_type, title in action_groups:
421
+ actions = report.get_actions_by_type(action_type)
422
+ if not actions:
423
+ continue
424
+
425
+ self._print(f"### {title} ({len(actions)})\n")
426
+
427
+ for i, action in enumerate(actions, 1):
428
+ self._print(f"{i}. {action.description}")
429
+ self._print(f" - **Status:** {action.status}")
430
+
431
+ if action.commands:
432
+ self._print(" - **Commands:**")
433
+ for cmd in action.commands:
434
+ self._print(f" - `{cmd}`")
435
+
436
+ if action.pr_branch:
437
+ self._print(f" - **Branch:** `{action.pr_branch}`")
438
+
439
+ self._print()
440
+
441
+ # Summary
442
+ self._print("## Summary\n")
443
+ stats = report.stats
444
+
445
+ self._print(f"- **Total Errors:** {stats['total_errors']}")
446
+ self._print(f" - Script Errors: {stats['script_errors']}")
447
+ self._print(f" - Skill Errors: {stats['skill_errors']}")
448
+ self._print(f" - Agent Issues: {stats['agent_errors']}")
449
+ self._print(f" - User Code Issues: {stats['user_code_errors']}")
450
+ self._print(f"- **Critical Priority:** {stats['critical_priority']}")
451
+ self._print(f"- **High Priority:** {stats['high_priority']}")
452
+ self._print(f"- **Total Actions:** {stats['total_actions']}")
453
+ self._print(f"- **Auto-fixable:** {stats['auto_fixable']}")
454
+ self._print(f"- **PR Actions:** {stats['pr_actions']}\n")
455
+
456
+ def _print(self, text: str = "") -> None:
457
+ """Print text to output.
458
+
459
+ Args:
460
+ text: Text to print
461
+ """
462
+ print(text, file=self.output)
463
+
464
+ def _print_colored(self, text: str, color: str) -> None:
465
+ """Print colored text (if colors enabled).
466
+
467
+ Args:
468
+ text: Text to print
469
+ color: Color name
470
+ """
471
+ if self.use_color and color in self.COLORS:
472
+ print(f"{self.COLORS[color]}{text}{self.COLORS['reset']}", file=self.output)
473
+ else:
474
+ print(text, file=self.output)