Files
antigravity-skills-reference/skills/skill-sentinel/scripts/analyzers/code_quality.py
ProgramadorBrasil 61ec71c5c7 feat: add 52 specialized AI agent skills (#217)
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>
2026-03-07 10:04:07 +01:00

248 lines
9.6 KiB
Python

"""
Analyzer de qualidade de codigo.
Usa AST (stdlib) para medir complexidade ciclomatica, tamanho de funcoes,
cobertura de docstrings e padroes de error handling.
"""
from __future__ import annotations
import ast
from pathlib import Path
from typing import Any, Dict, List, Tuple
from config import (
MAX_CYCLOMATIC_COMPLEXITY,
MAX_FILE_LINES,
MAX_FUNCTION_LINES,
PENALTY_BARE_EXCEPT,
PENALTY_BROAD_EXCEPT,
PENALTY_HIGH_COMPLEXITY,
PENALTY_LONG_FILE,
PENALTY_LONG_FUNCTION,
PENALTY_NO_DOCSTRING,
)
def _cyclomatic_complexity(node: ast.AST) -> int:
"""Calcula complexidade ciclomatica de uma funcao/metodo."""
complexity = 1 # base
for child in ast.walk(node):
if isinstance(child, (ast.If, ast.IfExp)):
complexity += 1
elif isinstance(child, (ast.For, ast.AsyncFor, ast.While)):
complexity += 1
elif isinstance(child, ast.ExceptHandler):
complexity += 1
elif isinstance(child, (ast.With, ast.AsyncWith)):
complexity += 1
elif isinstance(child, ast.BoolOp):
# cada and/or adiciona um path
complexity += len(child.values) - 1
elif isinstance(child, ast.Assert):
complexity += 1
return complexity
def _check_except_patterns(node: ast.AST) -> List[Dict[str, Any]]:
"""Verifica padroes de except (bare except, broad except)."""
issues = []
for child in ast.walk(node):
if isinstance(child, ast.ExceptHandler):
if child.type is None:
issues.append({
"type": "bare_except",
"line": child.lineno,
"severity": "high",
})
elif isinstance(child.type, ast.Name) and child.type.id == "Exception":
# Verificar se tem logging no corpo
has_log = False
for stmt in ast.walk(child):
if isinstance(stmt, ast.Call):
func = stmt.func
if isinstance(func, ast.Attribute) and func.attr in (
"error", "warning", "exception", "critical"
):
has_log = True
break
if isinstance(func, ast.Name) and func.id == "print":
has_log = True
break
if not has_log:
issues.append({
"type": "broad_except_no_log",
"line": child.lineno,
"severity": "medium",
})
return issues
def analyze(skill_data: Dict[str, Any]) -> Tuple[float, List[Dict[str, Any]]]:
"""
Analisa qualidade de codigo de uma skill.
Retorna (score, findings).
"""
score = 100.0
findings: List[Dict[str, Any]] = []
skill_name = skill_data["name"]
skill_path = Path(skill_data["path"])
total_functions = 0
functions_with_docs = 0
for rel_path in skill_data.get("python_files", []):
filepath = skill_path / rel_path
if not filepath.exists():
continue
try:
source = filepath.read_text(encoding="utf-8", errors="replace")
tree = ast.parse(source, filename=str(filepath))
except SyntaxError as e:
findings.append({
"skill_name": skill_name,
"dimension": "code_quality",
"severity": "high",
"category": "syntax_error",
"title": f"Erro de sintaxe em {rel_path}",
"description": str(e),
"file_path": rel_path,
"line_number": getattr(e, "lineno", None),
"recommendation": "Corrigir o erro de sintaxe",
"effort": "low",
"impact": "high",
})
score -= 15
continue
lines = source.splitlines()
file_lines = len(lines)
# Arquivo muito longo
if file_lines > MAX_FILE_LINES:
findings.append({
"skill_name": skill_name,
"dimension": "code_quality",
"severity": "medium",
"category": "long_file",
"title": f"Arquivo longo: {rel_path} ({file_lines} linhas)",
"description": f"Arquivo excede {MAX_FILE_LINES} linhas. Considerar dividir em modulos menores.",
"file_path": rel_path,
"recommendation": "Extrair funcionalidades em modulos separados",
"effort": "medium",
"impact": "medium",
})
score -= PENALTY_LONG_FILE
# Analisar funcoes e classes
for node in ast.walk(tree):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
total_functions += 1
func_name = node.name
end_line = getattr(node, "end_lineno", node.lineno)
func_lines = end_line - node.lineno + 1
# Docstring
has_doc = (
node.body
and isinstance(node.body[0], ast.Expr)
and isinstance(node.body[0].value, (ast.Constant, ast.Str))
)
if has_doc:
functions_with_docs += 1
else:
if not func_name.startswith("_"):
score -= PENALTY_NO_DOCSTRING
# Funcao longa
if func_lines > MAX_FUNCTION_LINES:
findings.append({
"skill_name": skill_name,
"dimension": "code_quality",
"severity": "medium",
"category": "long_function",
"title": f"Funcao longa: {func_name} ({func_lines} linhas) em {rel_path}",
"file_path": rel_path,
"line_number": node.lineno,
"recommendation": f"Reduzir {func_name} para < {MAX_FUNCTION_LINES} linhas extraindo sub-funcoes",
"effort": "medium",
"impact": "medium",
})
score -= PENALTY_LONG_FUNCTION
# Complexidade ciclomatica
complexity = _cyclomatic_complexity(node)
if complexity > MAX_CYCLOMATIC_COMPLEXITY:
findings.append({
"skill_name": skill_name,
"dimension": "code_quality",
"severity": "medium",
"category": "high_complexity",
"title": f"Alta complexidade: {func_name} (CC={complexity}) em {rel_path}",
"file_path": rel_path,
"line_number": node.lineno,
"recommendation": f"Simplificar {func_name} (CC={complexity} > {MAX_CYCLOMATIC_COMPLEXITY})",
"effort": "medium",
"impact": "medium",
})
score -= PENALTY_HIGH_COMPLEXITY
elif isinstance(node, ast.ClassDef):
total_functions += 1
has_doc = (
node.body
and isinstance(node.body[0], ast.Expr)
and isinstance(node.body[0].value, (ast.Constant, ast.Str))
)
if has_doc:
functions_with_docs += 1
# Padroes de except
except_issues = _check_except_patterns(tree)
for issue in except_issues:
if issue["type"] == "bare_except":
findings.append({
"skill_name": skill_name,
"dimension": "code_quality",
"severity": "high",
"category": "bare_except",
"title": f"Bare except em {rel_path}:{issue['line']}",
"file_path": rel_path,
"line_number": issue["line"],
"recommendation": "Usar except especifico (ex: except ValueError) em vez de bare except",
"effort": "low",
"impact": "high",
})
score -= PENALTY_BARE_EXCEPT
elif issue["type"] == "broad_except_no_log":
findings.append({
"skill_name": skill_name,
"dimension": "code_quality",
"severity": "medium",
"category": "broad_except",
"title": f"except Exception sem logging em {rel_path}:{issue['line']}",
"file_path": rel_path,
"line_number": issue["line"],
"recommendation": "Adicionar logging.error() ou re-raise dentro do except Exception",
"effort": "low",
"impact": "medium",
})
score -= PENALTY_BROAD_EXCEPT
# Bonus/penalidade por cobertura de docstrings
if total_functions > 0:
doc_coverage = functions_with_docs / total_functions
if doc_coverage < 0.3:
findings.append({
"skill_name": skill_name,
"dimension": "code_quality",
"severity": "low",
"category": "low_docstring_coverage",
"title": f"Baixa cobertura de docstrings ({doc_coverage:.0%})",
"recommendation": "Adicionar docstrings em funcoes publicas para melhorar manutenibilidade",
"effort": "low",
"impact": "low",
})
return max(0.0, min(100.0, score)), findings