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>
190 lines
6.7 KiB
Python
190 lines
6.7 KiB
Python
"""
|
|
Analyzer de documentacao.
|
|
|
|
Verifica completude do SKILL.md, secoes obrigatorias, frontmatter,
|
|
triggers, exemplos e cobertura de reference files.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List, Tuple
|
|
|
|
from config import SKILL_MD_RECOMMENDED_SECTIONS, SKILL_MD_REQUIRED_SECTIONS
|
|
|
|
|
|
def _check_frontmatter(content: str) -> Dict[str, bool]:
|
|
"""Verifica presenca de campos no frontmatter YAML."""
|
|
has = {}
|
|
if content.startswith("---"):
|
|
end = content.find("---", 3)
|
|
if end > 0:
|
|
fm = content[3:end].lower()
|
|
has["name"] = "name:" in fm
|
|
has["description"] = "description:" in fm or "description:" in fm
|
|
has["version"] = "version:" in fm
|
|
return has
|
|
|
|
|
|
def _check_sections(content: str) -> Dict[str, bool]:
|
|
"""Verifica presenca de secoes recomendadas no corpo."""
|
|
lower = content.lower()
|
|
sections = {}
|
|
section_keywords = {
|
|
"instalacao": ["instalacao", "installation", "instalação", "setup", "pip install"],
|
|
"comandos": ["comandos", "commands", "uso", "usage", "cli", "como usar"],
|
|
"governanca": ["governanca", "governance", "governança", "rate limit", "audit"],
|
|
"referencias": ["referencias", "references", "referências", "reference"],
|
|
}
|
|
for key, keywords in section_keywords.items():
|
|
sections[key] = any(kw in lower for kw in keywords)
|
|
return sections
|
|
|
|
|
|
def _check_triggers(description: str) -> Dict[str, Any]:
|
|
"""Verifica se description tem triggers adequados."""
|
|
if not description:
|
|
return {"has_triggers": False, "trigger_count": 0}
|
|
# Contar palavras-chave de trigger
|
|
words = re.findall(r'\b\w+\b', description.lower())
|
|
return {
|
|
"has_triggers": len(words) > 10,
|
|
"trigger_count": len(words),
|
|
"has_bilingual": any(w in description.lower() for w in ["use when", "use esta", "triggers on"]),
|
|
}
|
|
|
|
|
|
def analyze(skill_data: Dict[str, Any]) -> Tuple[float, List[Dict[str, Any]]]:
|
|
"""Analisa documentacao 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"])
|
|
|
|
skill_md = skill_path / "SKILL.md"
|
|
if not skill_md.exists():
|
|
findings.append({
|
|
"skill_name": skill_name,
|
|
"dimension": "documentation",
|
|
"severity": "critical",
|
|
"category": "missing_skill_md",
|
|
"title": "SKILL.md nao encontrado",
|
|
"recommendation": "Criar SKILL.md com frontmatter YAML e documentacao completa",
|
|
"effort": "medium",
|
|
"impact": "high",
|
|
})
|
|
return 0.0, findings
|
|
|
|
try:
|
|
content = skill_md.read_text(encoding="utf-8", errors="replace")
|
|
except OSError:
|
|
return 0.0, findings
|
|
|
|
lines = len(content.splitlines())
|
|
|
|
# Frontmatter
|
|
fm = _check_frontmatter(content)
|
|
if not fm.get("name"):
|
|
findings.append({
|
|
"skill_name": skill_name,
|
|
"dimension": "documentation",
|
|
"severity": "high",
|
|
"category": "missing_frontmatter_name",
|
|
"title": "Frontmatter sem campo 'name'",
|
|
"recommendation": "Adicionar 'name: skill-name' no frontmatter YAML",
|
|
"effort": "low",
|
|
"impact": "high",
|
|
})
|
|
score -= 20
|
|
|
|
if not fm.get("description"):
|
|
findings.append({
|
|
"skill_name": skill_name,
|
|
"dimension": "documentation",
|
|
"severity": "high",
|
|
"category": "missing_frontmatter_description",
|
|
"title": "Frontmatter sem campo 'description'",
|
|
"recommendation": "Adicionar 'description:' com trigger keywords no frontmatter YAML",
|
|
"effort": "medium",
|
|
"impact": "high",
|
|
})
|
|
score -= 20
|
|
|
|
if not fm.get("version"):
|
|
findings.append({
|
|
"skill_name": skill_name,
|
|
"dimension": "documentation",
|
|
"severity": "low",
|
|
"category": "missing_version",
|
|
"title": "Sem versao no frontmatter",
|
|
"recommendation": "Adicionar 'version: 1.0.0' no frontmatter YAML",
|
|
"effort": "low",
|
|
"impact": "low",
|
|
})
|
|
score -= 3
|
|
|
|
# Triggers
|
|
desc = skill_data.get("description", "")
|
|
trigger_info = _check_triggers(desc)
|
|
if not trigger_info["has_triggers"]:
|
|
findings.append({
|
|
"skill_name": skill_name,
|
|
"dimension": "documentation",
|
|
"severity": "medium",
|
|
"category": "weak_triggers",
|
|
"title": "Description com poucas palavras-chave de trigger",
|
|
"description": f"Apenas {trigger_info['trigger_count']} palavras. "
|
|
"Mais keywords = mais chance de ativacao correta pelo Claude.",
|
|
"recommendation": "Expandir description com mais trigger keywords em PT-BR e EN",
|
|
"effort": "low",
|
|
"impact": "high",
|
|
})
|
|
score -= 10
|
|
|
|
# Secoes do corpo
|
|
sections = _check_sections(content)
|
|
for section_key in ["instalacao", "comandos", "governanca", "referencias"]:
|
|
if not sections.get(section_key):
|
|
findings.append({
|
|
"skill_name": skill_name,
|
|
"dimension": "documentation",
|
|
"severity": "low",
|
|
"category": f"missing_section_{section_key}",
|
|
"title": f"Secao '{section_key}' nao encontrada no SKILL.md",
|
|
"recommendation": f"Adicionar secao de {section_key} no SKILL.md",
|
|
"effort": "low",
|
|
"impact": "low",
|
|
})
|
|
score -= 3
|
|
|
|
# Reference files
|
|
ref_files = skill_data.get("reference_files", [])
|
|
if not ref_files and skill_data.get("has_references_dir"):
|
|
findings.append({
|
|
"skill_name": skill_name,
|
|
"dimension": "documentation",
|
|
"severity": "low",
|
|
"category": "empty_references",
|
|
"title": "Diretorio references/ vazio",
|
|
"recommendation": "Adicionar documentacao de referencia em references/",
|
|
"effort": "medium",
|
|
"impact": "low",
|
|
})
|
|
score -= 5
|
|
|
|
# SKILL.md tamanho: muito curto eh ruim
|
|
if lines < 20:
|
|
findings.append({
|
|
"skill_name": skill_name,
|
|
"dimension": "documentation",
|
|
"severity": "medium",
|
|
"category": "short_skill_md",
|
|
"title": f"SKILL.md muito curto ({lines} linhas)",
|
|
"recommendation": "Expandir SKILL.md com exemplos, workflows e detalhes de uso",
|
|
"effort": "medium",
|
|
"impact": "medium",
|
|
})
|
|
score -= 10
|
|
|
|
return max(0.0, min(100.0, score)), findings
|