Initial commit
This commit is contained in:
116
skills/project-status-report/scripts/health_check.py
Executable file
116
skills/project-status-report/scripts/health_check.py
Executable file
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Health Check Module
|
||||
|
||||
Checks project health: tests, linting, coverage, build status.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
||||
class HealthChecker:
|
||||
"""Check project health indicators"""
|
||||
|
||||
def __init__(self, project_path: str = "."):
|
||||
"""Initialize checker with project path"""
|
||||
self.project_path = Path(project_path)
|
||||
|
||||
def _run_command(self, cmd: List[str]) -> Optional[subprocess.CompletedProcess]:
|
||||
"""Run command and return result"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
cwd=self.project_path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=30
|
||||
)
|
||||
return result
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
return None
|
||||
|
||||
def check_tests(self) -> Dict[str, Any]:
|
||||
"""Check test status (pytest)"""
|
||||
result = self._run_command(["pytest", "--collect-only", "-q"])
|
||||
|
||||
if not result:
|
||||
return {"status": "unknown", "reason": "pytest not found"}
|
||||
|
||||
# Check if pytest runs successfully
|
||||
if result.returncode == 0:
|
||||
# Parse output for test count
|
||||
lines = result.stdout.split("\n")
|
||||
for line in lines:
|
||||
if "test" in line.lower():
|
||||
return {"status": "pass", "message": line.strip()}
|
||||
return {"status": "pass"}
|
||||
else:
|
||||
return {"status": "fail", "message": result.stderr[:200]}
|
||||
|
||||
def check_ccmp_context_health(self) -> Optional[Dict[str, Any]]:
|
||||
"""Check context health from .ccmp/state.json"""
|
||||
state_file = self.project_path / ".ccmp" / "state.json"
|
||||
|
||||
if not state_file.exists():
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(state_file) as f:
|
||||
state = json.load(f)
|
||||
|
||||
context_state = state.get("claude-context-manager", {})
|
||||
if context_state:
|
||||
return {
|
||||
"health_score": context_state.get("health_score"),
|
||||
"critical_files": context_state.get("critical_files", [])
|
||||
}
|
||||
except (json.JSONDecodeError, IOError):
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
def generate_report(self) -> str:
|
||||
"""Generate health indicators section"""
|
||||
lines = []
|
||||
lines.append("## 🏥 Health Indicators")
|
||||
lines.append("")
|
||||
|
||||
# Test status
|
||||
test_result = self.check_tests()
|
||||
if test_result["status"] == "pass":
|
||||
lines.append(f"✅ Tests: {test_result.get('message', 'Passing')}")
|
||||
elif test_result["status"] == "fail":
|
||||
lines.append(f"❌ Tests: {test_result.get('message', 'Failing')}")
|
||||
else:
|
||||
lines.append("⚠️ Tests: Status unknown")
|
||||
|
||||
# Context health (if available)
|
||||
context_health = self.check_ccmp_context_health()
|
||||
if context_health:
|
||||
score = context_health.get("health_score")
|
||||
critical = context_health.get("critical_files", [])
|
||||
if score is not None:
|
||||
if score >= 80:
|
||||
lines.append(f"✅ Context Health: {score}/100")
|
||||
elif score >= 60:
|
||||
lines.append(f"⚠️ Context Health: {score}/100")
|
||||
else:
|
||||
lines.append(f"❌ Context Health: {score}/100")
|
||||
|
||||
if critical:
|
||||
lines.append(f"⚠️ Context: {len(critical)} files need attention")
|
||||
|
||||
# Summary
|
||||
lines.append("")
|
||||
critical_issues = [line for line in lines if "❌" in line]
|
||||
warnings = [line for line in lines if "⚠️" in line]
|
||||
|
||||
if critical_issues:
|
||||
lines.append(f"**Critical Issues**: {len(critical_issues)}")
|
||||
if warnings:
|
||||
lines.append(f"**Warnings**: {len(warnings)}")
|
||||
|
||||
return "\n".join(lines)
|
||||
Reference in New Issue
Block a user