minions-ai-agents/antigravity_brain_export/tools/todo_tracker.py

129 lines
4.4 KiB
Python

import os
import pathspec
import re
def track_todos(
start_path=".",
output_file="docs/manual_desenvolvimento/07_Divida_Tecnica_Backlog.md",
gitignore_file=".gitignore",
):
"""
Extrai TODOs, FIXMEs e HACKs do código e gera um relatório.
"""
# Filtros
ignore_patterns = [".git", ".agent", "node_modules", "venv", "dist", "build"]
if os.path.exists(gitignore_file):
with open(gitignore_file, "r", encoding="utf-8") as f:
ignore_patterns.extend(f.read().splitlines())
spec = pathspec.PathSpec.from_lines("gitwildmatch", ignore_patterns)
# Regex para capturar comentários
todo_pattern = re.compile(r"(TODO|FIXME|HACK|BUG|NOTE):\s*(.+)", re.IGNORECASE)
todos = []
for root, dirs, files in os.walk(start_path):
dirs[:] = [d for d in dirs if not spec.match_file(os.path.join(root, d))]
for file in files:
file_path = os.path.join(root, file)
if spec.match_file(file_path):
continue
# Pula arquivos não-texto
if file.endswith((".png", ".jpg", ".ico", ".pdf", ".exe")):
continue
try:
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
lines = f.readlines()
for i, line in enumerate(lines):
match = todo_pattern.search(line)
if match:
tag = match.group(1).upper()
msg = match.group(2).strip()
# Tenta pegar a próxima linha de interesse (contexto)
context = ""
if i + 1 < len(lines):
context = lines[i + 1].strip()
# Se for apenas um comentário vazio ou fechamento, tenta mais uma
if not context and i + 2 < len(lines):
context = lines[i + 2].strip()
todos.append(
{
"tag": tag,
"msg": msg,
"file": file_path,
"line": i + 1,
"context": context[
:100
], # Limita tamanho do contexto
}
)
except:
pass
# Estatísticas
stats = {}
for item in todos:
stats[item["tag"]] = stats.get(item["tag"], 0) + 1
# Mapeamento de Prioridade e Ícones
priority = {"BUG": 0, "FIXME": 1, "HACK": 2, "TODO": 3, "NOTE": 4}
icons = {
"BUG": "🔴 ERRO (BUG)",
"FIXME": "🟠 CORRIGIR (FIXME)",
"HACK": "🏴‍☠️ GAMBIARRA (HACK)",
"TODO": "🔵 PENDENTE (TODO)",
"NOTE": "📝 NOTA (NOTE)",
}
# Gera Markdown
os.makedirs(os.path.dirname(output_file), exist_ok=True)
md = [
"# 🏚️ Backlog de Dívida Técnica (Gerado Automaticamente)",
f"> Atualizado em: {os.popen('date /t').read().strip()} {os.popen('time /t').read().strip()}",
"",
"## 📊 Resumo de Pendências",
]
for tag, count in sorted(stats.items(), key=lambda x: priority.get(x[0], 99)):
label = icons.get(tag, tag)
md.append(f"- **{label}**: {count}")
md.extend(
[
"",
"## 📋 Lista Detalhada",
"",
"| Tipo | Mensagem | Contexto | Localização |",
"| :--- | :--- | :--- | :--- |",
]
)
# Ordena por tipo (BUG > FIXME > TODO)
todos.sort(key=lambda x: (priority.get(x["tag"], 99), x["file"]))
for item in todos:
label = icons.get(item["tag"], f"**{item['tag']}**")
# Link relativo limpo para VSCode/Editores
relative_path = os.path.relpath(item["file"], ".")
location = (
f"[`{relative_path}:{item['line']}`]({relative_path}#L{item['line']})"
)
ctx = f"`{item['context']}`" if item["context"] else "---"
md.append(f"| {label} | {item['msg']} | {ctx} | {location} |")
with open(output_file, "w", encoding="utf-8") as f:
f.write("\n".join(md))
return f"✅ Backlog de Dívida Técnica atualizado com sucesso: {output_file}"
if __name__ == "__main__":
print(track_todos())