claude-mpm 5.0.2__py3-none-any.whl → 5.4.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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
- claude_mpm/agents/PM_INSTRUCTIONS.md +1218 -905
- claude_mpm/agents/agent_loader.py +10 -17
- claude_mpm/agents/base_agent_loader.py +10 -35
- claude_mpm/agents/frontmatter_validator.py +68 -0
- claude_mpm/agents/templates/circuit-breakers.md +431 -45
- claude_mpm/cli/__init__.py +0 -1
- claude_mpm/cli/commands/__init__.py +2 -0
- claude_mpm/cli/commands/agent_state_manager.py +67 -23
- claude_mpm/cli/commands/agents.py +446 -25
- claude_mpm/cli/commands/auto_configure.py +535 -233
- claude_mpm/cli/commands/configure.py +1500 -147
- claude_mpm/cli/commands/configure_agent_display.py +13 -6
- claude_mpm/cli/commands/mpm_init/core.py +158 -1
- claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
- claude_mpm/cli/commands/postmortem.py +401 -0
- claude_mpm/cli/commands/run.py +1 -39
- claude_mpm/cli/commands/skills.py +322 -19
- claude_mpm/cli/commands/summarize.py +413 -0
- claude_mpm/cli/executor.py +8 -0
- claude_mpm/cli/interactive/agent_wizard.py +302 -195
- claude_mpm/cli/parsers/agents_parser.py +137 -0
- claude_mpm/cli/parsers/auto_configure_parser.py +13 -0
- claude_mpm/cli/parsers/base_parser.py +9 -0
- claude_mpm/cli/parsers/skills_parser.py +7 -0
- claude_mpm/cli/startup.py +133 -85
- claude_mpm/commands/mpm-agents-auto-configure.md +2 -2
- claude_mpm/commands/mpm-agents-list.md +2 -2
- claude_mpm/commands/mpm-config-view.md +2 -2
- claude_mpm/commands/mpm-help.md +3 -0
- claude_mpm/commands/{mpm-ticket-organize.md → mpm-organize.md} +4 -5
- claude_mpm/commands/mpm-postmortem.md +123 -0
- claude_mpm/commands/mpm-session-resume.md +2 -2
- claude_mpm/commands/mpm-ticket-view.md +2 -2
- claude_mpm/config/agent_presets.py +312 -82
- claude_mpm/config/agent_sources.py +27 -0
- claude_mpm/config/skill_presets.py +392 -0
- claude_mpm/constants.py +1 -0
- claude_mpm/core/claude_runner.py +2 -25
- claude_mpm/core/framework/loaders/agent_loader.py +8 -5
- claude_mpm/core/framework/loaders/file_loader.py +54 -101
- claude_mpm/core/interactive_session.py +19 -5
- claude_mpm/core/oneshot_session.py +16 -4
- claude_mpm/core/output_style_manager.py +173 -43
- claude_mpm/core/protocols/__init__.py +23 -0
- claude_mpm/core/protocols/runner_protocol.py +103 -0
- claude_mpm/core/protocols/session_protocol.py +131 -0
- claude_mpm/core/shared/singleton_manager.py +11 -4
- claude_mpm/core/socketio_pool.py +3 -3
- claude_mpm/core/system_context.py +38 -0
- claude_mpm/core/unified_agent_registry.py +134 -16
- claude_mpm/core/unified_config.py +22 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +35 -2
- claude_mpm/hooks/claude_hooks/hook_handler.py +4 -0
- claude_mpm/hooks/claude_hooks/memory_integration.py +12 -1
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
- claude_mpm/models/agent_definition.py +7 -0
- claude_mpm/scripts/launch_monitor.py +93 -13
- claude_mpm/services/agents/agent_recommendation_service.py +279 -0
- claude_mpm/services/agents/cache_git_manager.py +621 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +3 -2
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +110 -3
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +518 -55
- claude_mpm/services/agents/git_source_manager.py +20 -0
- claude_mpm/services/agents/sources/git_source_sync_service.py +45 -6
- claude_mpm/services/agents/toolchain_detector.py +6 -5
- claude_mpm/services/analysis/__init__.py +35 -0
- claude_mpm/services/analysis/clone_detector.py +1030 -0
- claude_mpm/services/analysis/postmortem_reporter.py +474 -0
- claude_mpm/services/analysis/postmortem_service.py +765 -0
- claude_mpm/services/command_deployment_service.py +106 -5
- claude_mpm/services/core/base.py +7 -2
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +7 -15
- claude_mpm/services/event_bus/config.py +3 -1
- claude_mpm/services/git/git_operations_service.py +8 -8
- claude_mpm/services/mcp_config_manager.py +75 -145
- claude_mpm/services/mcp_service_verifier.py +6 -3
- claude_mpm/services/monitor/daemon.py +37 -10
- claude_mpm/services/monitor/daemon_manager.py +134 -21
- claude_mpm/services/monitor/server.py +225 -19
- claude_mpm/services/project/project_organizer.py +4 -0
- claude_mpm/services/runner_configuration_service.py +16 -3
- claude_mpm/services/session_management_service.py +16 -4
- claude_mpm/services/socketio/event_normalizer.py +15 -1
- claude_mpm/services/socketio/server/core.py +160 -21
- claude_mpm/services/version_control/git_operations.py +103 -0
- claude_mpm/utils/agent_filters.py +261 -0
- claude_mpm/utils/gitignore.py +3 -0
- claude_mpm/utils/migration.py +372 -0
- claude_mpm/utils/progress.py +5 -1
- {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/METADATA +69 -84
- {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/RECORD +112 -153
- {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/entry_points.txt +0 -2
- claude_mpm/dashboard/analysis_runner.py +0 -455
- claude_mpm/dashboard/index.html +0 -13
- claude_mpm/dashboard/open_dashboard.py +0 -66
- claude_mpm/dashboard/static/css/activity.css +0 -1958
- claude_mpm/dashboard/static/css/connection-status.css +0 -370
- claude_mpm/dashboard/static/css/dashboard.css +0 -4701
- claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
- claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
- claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
- claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
- claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
- claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
- claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
- claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
- claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
- claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
- claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
- claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
- claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
- claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
- claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
- claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
- claude_mpm/dashboard/static/js/connection-manager.js +0 -536
- claude_mpm/dashboard/static/js/dashboard.js +0 -1914
- claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
- claude_mpm/dashboard/static/js/socket-client.js +0 -1474
- claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
- claude_mpm/dashboard/static/socket.io.min.js +0 -7
- claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
- claude_mpm/dashboard/templates/code_simple.html +0 -153
- claude_mpm/dashboard/templates/index.html +0 -606
- claude_mpm/dashboard/test_dashboard.html +0 -372
- claude_mpm/scripts/mcp_server.py +0 -75
- claude_mpm/scripts/mcp_wrapper.py +0 -39
- claude_mpm/services/mcp_gateway/__init__.py +0 -159
- claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
- claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
- claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
- claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
- claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
- claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
- claude_mpm/services/mcp_gateway/core/base.py +0 -312
- claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
- claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
- claude_mpm/services/mcp_gateway/core/process_pool.py +0 -971
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
- claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
- claude_mpm/services/mcp_gateway/main.py +0 -589
- claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
- claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
- claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
- claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
- claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
- claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
- claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
- claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
- claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
- /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
- {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/WHEEL +0 -0
- {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.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)
|