Based on claude-code-tools TmuxCLIController, this refactor: - Added DockerTmuxController class for robust tmux session management - Implements send_keys() with configurable delay_enter - Implements capture_pane() for output retrieval - Implements wait_for_prompt() for pattern-based completion detection - Implements wait_for_idle() for content-hash-based idle detection - Implements wait_for_shell_prompt() for shell prompt detection Also includes workflow improvements: - Pre-task git snapshot before agent execution - Post-task commit protocol in agent guidelines Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
314 lines
10 KiB
Python
314 lines
10 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Health Report Generator
|
|
|
|
Generates formatted health reports with:
|
|
- 0-100 overall scores
|
|
- Component breakdown
|
|
- Specific issue examples
|
|
- Actionable recommendations
|
|
"""
|
|
|
|
import json
|
|
from datetime import datetime
|
|
from typing import List, Dict
|
|
from pathlib import Path
|
|
|
|
|
|
class HealthReportGenerator:
|
|
"""Generate formatted health reports."""
|
|
|
|
def __init__(self):
|
|
"""Initialize report generator."""
|
|
pass
|
|
|
|
def generate_dashboard_report(self, health_data: Dict) -> str:
|
|
"""
|
|
Generate formatted dashboard report.
|
|
|
|
Args:
|
|
health_data: Health data from system orchestrator
|
|
|
|
Returns:
|
|
Formatted dashboard string
|
|
"""
|
|
overall = health_data['overall_score']
|
|
status = health_data['status'].upper()
|
|
timestamp = datetime.fromtimestamp(health_data['timestamp']).strftime('%Y-%m-%d %H:%M UTC')
|
|
|
|
report = f"""
|
|
╔════════════════════════════════════════════════════════════════════╗
|
|
║ LUZIA SYSTEM HEALTH REPORT ║
|
|
║ {timestamp:42} ║
|
|
╚════════════════════════════════════════════════════════════════════╝
|
|
|
|
OVERALL HEALTH SCORE: {overall:3.1f}/100 [{self._status_emoji(status)} {status}]
|
|
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
COMPONENT BREAKDOWN:
|
|
|
|
"""
|
|
components = health_data.get('component_scores', {})
|
|
for name, score in components.items():
|
|
emoji = self._score_emoji(score)
|
|
status_str = 'healthy' if score >= 80 else 'degraded' if score >= 60 else 'critical'
|
|
report += f" {name:15} {score:6.1f}/100 {emoji} {status_str}\n"
|
|
|
|
report += "\n" + "━" * 70 + "\n\n"
|
|
|
|
return report
|
|
|
|
def generate_component_report(self, component_name: str, component_data: Dict) -> str:
|
|
"""
|
|
Generate detailed component report.
|
|
|
|
Args:
|
|
component_name: Name of component (kg, conductor, etc)
|
|
component_data: Component health data
|
|
|
|
Returns:
|
|
Formatted component report
|
|
"""
|
|
report = f"\n{'=' * 70}\n"
|
|
report += f"{component_name.upper()} COMPONENT REPORT\n"
|
|
report += f"{'=' * 70}\n\n"
|
|
|
|
# Score
|
|
score = component_data.get('health_score') or component_data.get('overall_score', 0)
|
|
status = component_data.get('status', 'unknown').upper()
|
|
report += f"Score: {score:3.1f}/100 [{status}]\n\n"
|
|
|
|
# Issues
|
|
issues = component_data.get('issues', [])
|
|
if issues:
|
|
report += f"ISSUES FOUND ({len(issues)}):\n\n"
|
|
for i, issue in enumerate(issues[:10], 1):
|
|
if isinstance(issue, dict):
|
|
report += f" [{i}] {issue.get('severity', 'UNKNOWN').upper()}\n"
|
|
report += f" {issue.get('pattern', 'Unknown pattern')}\n"
|
|
if 'example' in issue:
|
|
example = issue['example']
|
|
if len(example) > 80:
|
|
example = example[:80] + "..."
|
|
report += f" Example: {example}\n\n"
|
|
else:
|
|
report += f" [{i}] {issue}\n\n"
|
|
|
|
# Recommendations
|
|
recommendations = component_data.get('recommendations', [])
|
|
if recommendations:
|
|
report += f"RECOMMENDATIONS:\n\n"
|
|
for i, rec in enumerate(recommendations, 1):
|
|
report += f" {i}. {rec}\n"
|
|
report += "\n"
|
|
|
|
return report
|
|
|
|
def generate_summary_report(self, health_data: Dict) -> str:
|
|
"""
|
|
Generate executive summary report.
|
|
|
|
Args:
|
|
health_data: Health data from system orchestrator
|
|
|
|
Returns:
|
|
Summary report string
|
|
"""
|
|
overall = health_data['overall_score']
|
|
timestamp = datetime.fromtimestamp(health_data['timestamp']).strftime('%Y-%m-%d %H:%M UTC')
|
|
|
|
report = f"""
|
|
╔════════════════════════════════════════════════════════════════════╗
|
|
║ SYSTEM HEALTH SUMMARY ║
|
|
║ {timestamp:42} ║
|
|
╚════════════════════════════════════════════════════════════════════╝
|
|
|
|
OVERALL SCORE: {overall:3.1f}/100
|
|
|
|
COMPONENT STATUS:
|
|
"""
|
|
|
|
components = health_data.get('component_scores', {})
|
|
for name, score in components.items():
|
|
emoji = self._score_emoji(score)
|
|
report += f" ├─ {name:20} {score:6.1f}/100 {emoji}\n"
|
|
|
|
report += """
|
|
NEXT STEPS:
|
|
"""
|
|
|
|
# Provide actionable next steps based on score
|
|
if overall >= 80:
|
|
report += """
|
|
✓ System is healthy - continue normal operations
|
|
- Run weekly full audits for proactive monitoring
|
|
- Review error patterns for systemic improvements
|
|
"""
|
|
elif overall >= 60:
|
|
report += f"""
|
|
⚠ System is degraded - {int(100 - overall)} points below healthy threshold
|
|
- Address component issues in order of severity
|
|
- Run luzia health --full for detailed analysis
|
|
- Implement recommended fixes for each component
|
|
"""
|
|
else:
|
|
report += """
|
|
✗ System is critical - immediate action required
|
|
- Run luzia health --full immediately
|
|
- Address URGENT issues first
|
|
- Contact administrator if problems persist
|
|
"""
|
|
|
|
report += f"\nFor detailed analysis:\n luzia health --full\n\n"
|
|
|
|
return report
|
|
|
|
def generate_full_report(self, all_health_data: Dict) -> str:
|
|
"""
|
|
Generate comprehensive full system report.
|
|
|
|
Args:
|
|
all_health_data: Complete health data dict
|
|
|
|
Returns:
|
|
Full report string
|
|
"""
|
|
report = self.generate_dashboard_report(all_health_data)
|
|
|
|
# Add capacity section
|
|
capacity = all_health_data.get('capacity', {})
|
|
report += f"""
|
|
SYSTEM CAPACITY:
|
|
|
|
Disk Usage: {capacity['disk']['usage_pct']:5.1f}% ({capacity['disk']['status']})
|
|
Memory Usage: {capacity['memory']['usage_pct']:5.1f}% ({capacity['memory']['status']})
|
|
CPU Load: {capacity['cpu']['load_pct']:5.1f}% ({capacity['cpu']['status']})
|
|
Concurrency: {capacity['concurrency']['active_agents']}/{capacity['concurrency']['max_concurrent']} agents
|
|
|
|
"""
|
|
|
|
# Configuration status
|
|
config = all_health_data.get('configuration', {})
|
|
report += f"""
|
|
CONFIGURATION STATUS:
|
|
|
|
Config File: {'✓' if config['config_file_valid'] else '✗'}
|
|
Permissions: {'✓' if config['permissions_valid'] else '✗'}
|
|
Databases: {'✓' if config['databases_accessible'] else '✗'}
|
|
MCP Servers: {'✓' if config['mcp_servers_configured'] else '✗'}
|
|
|
|
"""
|
|
|
|
# Integration tests
|
|
integration = all_health_data.get('integration', {})
|
|
report += f"""
|
|
INTEGRATION TESTS:
|
|
|
|
KG Query: {'✓' if integration['kg_query'] else '✗'}
|
|
Conductor R/W: {'✓' if integration['conductor_rw'] else '✗'}
|
|
Context Retrieval: {'✓' if integration['context_retrieval'] else '✗'}
|
|
Bash Execution: {'✓' if integration['bash_execution'] else '✗'}
|
|
|
|
"""
|
|
|
|
# Issues summary
|
|
all_issues = []
|
|
all_issues.extend(capacity.get('issues', []))
|
|
all_issues.extend(config.get('issues', []))
|
|
all_issues.extend(integration.get('issues', []))
|
|
|
|
if all_issues:
|
|
report += f"""
|
|
ISSUES FOUND ({len(all_issues)}):
|
|
|
|
"""
|
|
for issue in all_issues[:20]:
|
|
report += f" • {issue}\n"
|
|
|
|
if len(all_issues) > 20:
|
|
report += f"\n ... and {len(all_issues) - 20} more issues\n"
|
|
|
|
report += f"\n{'━' * 70}\n"
|
|
|
|
return report
|
|
|
|
def save_report(self, filename: str, content: str) -> Path:
|
|
"""
|
|
Save report to file.
|
|
|
|
Args:
|
|
filename: Filename to save
|
|
content: Report content
|
|
|
|
Returns:
|
|
Path to saved file
|
|
"""
|
|
output_path = Path('/home/admin') / filename
|
|
output_path.write_text(content)
|
|
return output_path
|
|
|
|
def _status_emoji(self, status: str) -> str:
|
|
"""Get emoji for status."""
|
|
emojis = {
|
|
'HEALTHY': '✅',
|
|
'DEGRADED': '⚠️',
|
|
'CRITICAL': '❌',
|
|
'UNKNOWN': '❓'
|
|
}
|
|
return emojis.get(status, '❓')
|
|
|
|
def _score_emoji(self, score: float) -> str:
|
|
"""Get emoji for score."""
|
|
if score >= 80:
|
|
return '✅'
|
|
elif score >= 60:
|
|
return '⚠️'
|
|
else:
|
|
return '❌'
|
|
|
|
|
|
if __name__ == '__main__':
|
|
generator = HealthReportGenerator()
|
|
|
|
# Example health data
|
|
sample_data = {
|
|
'overall_score': 87,
|
|
'status': 'healthy',
|
|
'timestamp': 1704729600,
|
|
'component_scores': {
|
|
'kg': 92,
|
|
'conductor': 84,
|
|
'context': 89,
|
|
'scripts': 95,
|
|
'routines': 88,
|
|
'capacity': 81,
|
|
'configuration': 98,
|
|
'integration': 100
|
|
},
|
|
'capacity': {
|
|
'disk': {'usage_pct': 82, 'status': 'warning'},
|
|
'memory': {'usage_pct': 65, 'status': 'healthy'},
|
|
'cpu': {'load_pct': 45, 'status': 'healthy'},
|
|
'concurrency': {'active_agents': 2, 'max_concurrent': 4},
|
|
'issues': []
|
|
},
|
|
'configuration': {
|
|
'config_file_valid': True,
|
|
'permissions_valid': True,
|
|
'databases_accessible': True,
|
|
'mcp_servers_configured': True,
|
|
'issues': []
|
|
},
|
|
'integration': {
|
|
'kg_query': True,
|
|
'conductor_rw': True,
|
|
'context_retrieval': True,
|
|
'bash_execution': True,
|
|
'issues': []
|
|
}
|
|
}
|
|
|
|
print(generator.generate_dashboard_report(sample_data))
|
|
print(generator.generate_summary_report(sample_data))
|