New skills covering 10 categories: **Security & Audit**: 007 (STRIDE/PASTA/OWASP), cred-omega (secrets management) **AI Personas**: Karpathy, Hinton, Sutskever, LeCun (4 sub-skills), Altman, Musk, Gates, Jobs, Buffett **Multi-agent Orchestration**: agent-orchestrator, task-intelligence, multi-advisor **Code Analysis**: matematico-tao (Terence Tao-inspired mathematical code analysis) **Social & Messaging**: Instagram Graph API, Telegram Bot, WhatsApp Cloud API, social-orchestrator **Image Generation**: AI Studio (Gemini), Stability AI, ComfyUI Gateway, image-studio router **Brazilian Domain**: 6 auction specialist modules, 2 legal advisors, auctioneers data scraper **Product & Growth**: design, invention, monetization, analytics, growth engine **DevOps & LLM Ops**: Docker/CI-CD/AWS, RAG/embeddings/fine-tuning **Skill Governance**: installer, sentinel auditor, context management Each skill includes: - Standardized YAML frontmatter (name, description, risk, source, tags, tools) - Structured sections (Overview, When to Use, How it Works, Best Practices) - Python scripts and reference documentation where applicable - Cross-platform compatibility (Claude Code, Antigravity, Cursor, Gemini CLI, Codex CLI) Co-authored-by: ProgramadorBrasil <214873561+ProgramadorBrasil@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
225 lines
8.6 KiB
Python
225 lines
8.6 KiB
Python
"""
|
|
Gerador de relatorios Markdown.
|
|
|
|
Produz relatorio estruturado com resumo executivo, scores por skill,
|
|
findings por severidade, recomendacoes e plano de acao.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
from config import DIMENSION_WEIGHTS, REPORTS_DIR, SEVERITY_ORDER, get_score_label
|
|
|
|
|
|
def _severity_icon(severity: str) -> str:
|
|
"""Retorna indicador textual de severidade."""
|
|
icons = {
|
|
"critical": "[CRITICO]",
|
|
"high": "[ALTO]",
|
|
"medium": "[MEDIO]",
|
|
"low": "[BAIXO]",
|
|
"info": "[INFO]",
|
|
}
|
|
return icons.get(severity, "[?]")
|
|
|
|
|
|
def _format_score(score: Optional[float]) -> str:
|
|
"""Formata score como string."""
|
|
if score is None:
|
|
return "N/A"
|
|
return f"{score:.0f}"
|
|
|
|
|
|
def generate_report(
|
|
snapshots: List[Dict[str, Any]],
|
|
findings: List[Dict[str, Any]],
|
|
recommendations: List[Dict[str, Any]],
|
|
overall_score: float,
|
|
previous_snapshots: Optional[List[Dict[str, Any]]] = None,
|
|
) -> str:
|
|
"""Gera relatorio Markdown completo."""
|
|
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
|
|
lines: List[str] = []
|
|
|
|
# -- Header ----------------------------------------------------------------
|
|
lines.append("# Relatorio Sentinel - Auditoria do Ecossistema de Skills")
|
|
lines.append("")
|
|
lines.append(f"**Data:** {now}")
|
|
lines.append(f"**Skills Analisadas:** {len(snapshots)}")
|
|
lines.append(f"**Score Geral:** {overall_score:.0f}/100 ({get_score_label(overall_score)})")
|
|
lines.append("")
|
|
lines.append("---")
|
|
lines.append("")
|
|
|
|
# -- Resumo Executivo (tabela) ---------------------------------------------
|
|
lines.append("## Resumo Executivo")
|
|
lines.append("")
|
|
lines.append("| Skill | Score | Qualidade | Seguranca | Performance | Governanca | Docs | Deps |")
|
|
lines.append("|-------|-------|-----------|-----------|-------------|------------|------|------|")
|
|
|
|
for snap in sorted(snapshots, key=lambda s: -(s.get("overall_score") or 0)):
|
|
name = snap.get("skill_name", "?")
|
|
overall = _format_score(snap.get("overall_score"))
|
|
cq = _format_score(snap.get("code_quality"))
|
|
sec = _format_score(snap.get("security"))
|
|
perf = _format_score(snap.get("performance"))
|
|
gov = _format_score(snap.get("governance"))
|
|
doc = _format_score(snap.get("documentation"))
|
|
deps = _format_score(snap.get("dependencies"))
|
|
lines.append(f"| {name} | {overall} | {cq} | {sec} | {perf} | {gov} | {doc} | {deps} |")
|
|
|
|
lines.append("")
|
|
|
|
# -- Tendencias (se houver dados anteriores) --------------------------------
|
|
if previous_snapshots:
|
|
lines.append("## Tendencias")
|
|
lines.append("")
|
|
prev_map = {s["skill_name"]: s for s in previous_snapshots}
|
|
for snap in snapshots:
|
|
name = snap.get("skill_name", "?")
|
|
prev = prev_map.get(name)
|
|
if prev:
|
|
curr_score = snap.get("overall_score", 0) or 0
|
|
prev_score = prev.get("overall_score", 0) or 0
|
|
delta = curr_score - prev_score
|
|
if delta > 0:
|
|
trend = f"+{delta:.0f} pts"
|
|
elif delta < 0:
|
|
trend = f"{delta:.0f} pts"
|
|
else:
|
|
trend = "sem alteracao"
|
|
lines.append(f"- **{name}**: {prev_score:.0f} -> {curr_score:.0f} ({trend})")
|
|
lines.append("")
|
|
|
|
# -- Findings por Severidade ------------------------------------------------
|
|
lines.append("## Findings por Severidade")
|
|
lines.append("")
|
|
|
|
# Agrupar
|
|
by_severity: Dict[str, List[Dict[str, Any]]] = {}
|
|
for f in findings:
|
|
sev = f.get("severity", "info")
|
|
by_severity.setdefault(sev, []).append(f)
|
|
|
|
severity_labels = {
|
|
"critical": "Criticos",
|
|
"high": "Altos",
|
|
"medium": "Medios",
|
|
"low": "Baixos",
|
|
"info": "Informativos",
|
|
}
|
|
|
|
for sev in ["critical", "high", "medium", "low", "info"]:
|
|
items = by_severity.get(sev, [])
|
|
label = severity_labels.get(sev, sev)
|
|
lines.append(f"### {label} ({len(items)})")
|
|
lines.append("")
|
|
if not items:
|
|
lines.append("Nenhum.")
|
|
lines.append("")
|
|
continue
|
|
for f in items:
|
|
skill = f.get("skill_name", "?")
|
|
title = f.get("title", "?")
|
|
lines.append(f"- {_severity_icon(sev)} **[{skill}]** {title}")
|
|
if f.get("recommendation"):
|
|
lines.append(f" - Recomendacao: {f['recommendation']}")
|
|
if f.get("file_path"):
|
|
loc = f["file_path"]
|
|
if f.get("line_number"):
|
|
loc += f":{f['line_number']}"
|
|
lines.append(f" - Local: `{loc}`")
|
|
lines.append("")
|
|
|
|
# -- Analise por Skill -----------------------------------------------------
|
|
lines.append("## Analise por Skill")
|
|
lines.append("")
|
|
|
|
for snap in sorted(snapshots, key=lambda s: s.get("skill_name", "")):
|
|
name = snap.get("skill_name", "?")
|
|
overall = snap.get("overall_score", 0)
|
|
lines.append(f"### {name} ({_format_score(overall)}/100 - {get_score_label(overall or 0)})")
|
|
lines.append("")
|
|
lines.append(f"- Arquivos Python: {snap.get('file_count', 0)}")
|
|
lines.append(f"- Linhas de codigo: {snap.get('line_count', 0)}")
|
|
lines.append(f"- Qualidade: {_format_score(snap.get('code_quality'))} | "
|
|
f"Seguranca: {_format_score(snap.get('security'))} | "
|
|
f"Performance: {_format_score(snap.get('performance'))}")
|
|
lines.append(f"- Governanca: {_format_score(snap.get('governance'))} | "
|
|
f"Docs: {_format_score(snap.get('documentation'))} | "
|
|
f"Deps: {_format_score(snap.get('dependencies'))}")
|
|
lines.append("")
|
|
|
|
# Findings desta skill
|
|
skill_findings = [f for f in findings if f.get("skill_name") == name and f.get("severity") != "info"]
|
|
if skill_findings:
|
|
for f in sorted(skill_findings, key=lambda x: SEVERITY_ORDER.get(x.get("severity", "info"), 9)):
|
|
lines.append(f" - {_severity_icon(f['severity'])} {f['title']}")
|
|
else:
|
|
lines.append(" Nenhum finding significativo.")
|
|
lines.append("")
|
|
|
|
# -- Recomendacoes de Novas Skills -----------------------------------------
|
|
if recommendations:
|
|
lines.append("## Recomendacoes de Novas Skills")
|
|
lines.append("")
|
|
for i, rec in enumerate(recommendations, 1):
|
|
name = rec.get("suggested_name", "?")
|
|
priority = rec.get("priority", "?")
|
|
rationale = rec.get("rationale", "")
|
|
caps = rec.get("capabilities", [])
|
|
if isinstance(caps, str):
|
|
caps = [caps]
|
|
lines.append(f"### {i}. {name} (prioridade: {priority})")
|
|
lines.append("")
|
|
lines.append(f"**Razao:** {rationale}")
|
|
lines.append("")
|
|
if caps:
|
|
lines.append(f"**Capacidades:** {', '.join(caps)}")
|
|
lines.append("")
|
|
|
|
# -- Plano de Acao Priorizado ----------------------------------------------
|
|
lines.append("## Plano de Acao Priorizado")
|
|
lines.append("")
|
|
|
|
actionable = [
|
|
f for f in findings
|
|
if f.get("severity") in ("critical", "high", "medium") and f.get("recommendation")
|
|
]
|
|
actionable.sort(key=lambda x: (
|
|
SEVERITY_ORDER.get(x.get("severity", "info"), 9),
|
|
{"low": 0, "medium": 1, "high": 2}.get(x.get("effort", "medium"), 1),
|
|
))
|
|
|
|
if actionable:
|
|
lines.append("| # | Severidade | Skill | Acao | Esforco |")
|
|
lines.append("|---|-----------|-------|------|---------|")
|
|
for i, f in enumerate(actionable[:20], 1):
|
|
sev = f.get("severity", "?")
|
|
skill = f.get("skill_name", "?")
|
|
rec = f.get("recommendation", "?")[:80]
|
|
effort = f.get("effort", "?")
|
|
lines.append(f"| {i} | {sev} | {skill} | {rec} | {effort} |")
|
|
else:
|
|
lines.append("Nenhuma acao prioritaria identificada.")
|
|
|
|
lines.append("")
|
|
lines.append("---")
|
|
lines.append(f"*Gerado por skill-sentinel em {now}*")
|
|
|
|
return "\n".join(lines)
|
|
|
|
|
|
def save_report(content: str, filename: Optional[str] = None) -> str:
|
|
"""Salva relatorio em arquivo e retorna o path."""
|
|
if not filename:
|
|
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
filename = f"audit_{timestamp}.md"
|
|
|
|
filepath = REPORTS_DIR / filename
|
|
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
filepath.write_text(content, encoding="utf-8")
|
|
return str(filepath)
|