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())