#!/bin/bash # # graphify.sh — Roda o graphify sobre o código-fonte deste projeto (G-ART / fcp-mcp-server). # # Uso: # ./admin/graphify.sh # analisa o código real (server.py + fcpxml/) # ./admin/graphify.sh # analisa outro caminho # # Requer: graphify instalado (pip install graphifyy / uv tool install graphifyy) set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT_DIR" # Caminho padrão: pasta code/ (analisa server.py + fcpxml/). Pode ser sobrescrito por $1. INPUT_PATH="${1:-$ROOT_DIR/code}" if [ ! -e "$INPUT_PATH" ]; then echo "ERRO: caminho não encontrado: $INPUT_PATH" >&2 exit 1 fi echo "==> Graphify em: $INPUT_PATH" # Detecta interpretador Python que possui o pacote graphify. PYTHON="" if command -v uv >/dev/null 2>&1; then PYTHON="$(uv tool run --from graphifyy python -c 'import sys; print(sys.executable)' 2>/dev/null || true)" fi if [ -z "$PYTHON" ]; then PYTHON="$(command -v python3 || command -v python)" fi if ! "$PYTHON" -c "import graphify" 2>/dev/null; then echo "==> Instalando graphifyy..." if command -v uv >/dev/null 2>&1; then uv tool install --upgrade graphifyy PYTHON="$(uv tool run --from graphifyy python -c 'import sys; print(sys.executable)')" else "$PYTHON" -m pip install graphifyy fi fi mkdir -p graphify-out "$PYTHON" -c "import sys; open('graphify-out/.graphify_python','w').write(sys.executable)" # Detecta arquivos do corpus. echo "==> Detectando arquivos..." "$PYTHON" -c " import json, sys from graphify.detect import detect from pathlib import Path print(json.dumps(detect(Path('$INPUT_PATH')), ensure_ascii=False)) " > graphify-out/.graphify_detect.json # Extração estrutural (AST) — código, sem LLM. echo "==> Extração estrutural (AST)..." "$PYTHON" -c " import json, sys from pathlib import Path from graphify.extract import collect_files, extract detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding='utf-8')) code_files = [] for f in detect.get('files', {}).get('code', []): p = Path(f) code_files.extend(collect_files(p) if p.is_dir() else [p]) if code_files: result = extract(code_files, cache_root=Path('$INPUT_PATH')) else: result = {'nodes': [], 'edges': [], 'input_tokens': 0, 'output_tokens': 0} Path('graphify-out/.graphify_ast.json').write_text(json.dumps(result, indent=2, ensure_ascii=False), encoding='utf-8') print(f'AST: {len(result[\"nodes\"])} nodes, {len(result[\"edges\"])} edges') " # Corpus só de código -> semântica vazia; caso contrário o usuário roda via /graphify. "$PYTHON" -c " import json from pathlib import Path Path('graphify-out/.graphify_semantic.json').write_text(json.dumps({'nodes': [], 'edges': [], 'hyperedges': [], 'input_tokens': 0, 'output_tokens': 0}), encoding='utf-8') " # Merge AST + semântica. "$PYTHON" -c " import json from pathlib import Path ast = json.loads(Path('graphify-out/.graphify_ast.json').read_text(encoding='utf-8')) sem = json.loads(Path('graphify-out/.graphify_semantic.json').read_text(encoding='utf-8')) seen = {n['id'] for n in ast['nodes']} merged_nodes = list(ast['nodes']) for n in sem['nodes']: if n['id'] not in seen: merged_nodes.append(n); seen.add(n['id']) merged = {'nodes': merged_nodes, 'edges': ast['edges'] + sem['edges'], 'hyperedges': sem.get('hyperedges', []), 'input_tokens': sem.get('input_tokens', 0), 'output_tokens': sem.get('output_tokens', 0)} Path('graphify-out/.graphify_extract.json').write_text(json.dumps(merged, indent=2, ensure_ascii=False), encoding='utf-8') print(f'Merged: {len(merged_nodes)} nodes, {len(merged[\"edges\"])} edges') " # Build + cluster + report. echo "==> Construindo grafo e clusters..." "$PYTHON" -c " import json from pathlib import Path from graphify.build import build_from_json from graphify.cluster import cluster, score_all from graphify.analyze import god_nodes, surprising_connections, suggest_questions from graphify.report import generate from graphify.export import to_json extraction = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding='utf-8')) detection = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding='utf-8')) G = build_from_json(extraction, root='$INPUT_PATH', directed=False) if G.number_of_nodes() == 0: print('ERRO: grafo vazio - nenhum nó extraído.'); raise SystemExit(1) communities = cluster(G) cohesion = score_all(G, communities) gods = god_nodes(G) surprises = surprising_connections(G, communities) labels = {cid: 'Community ' + str(cid) for cid in communities} questions = suggest_questions(G, communities, labels) tokens = {'input': extraction.get('input_tokens', 0), 'output': extraction.get('output_tokens', 0)} wrote = to_json(G, communities, 'graphify-out/graph.json') if not wrote: print('ERRO: recusou encolher graph.json (#479).'); raise SystemExit(1) report = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, '$INPUT_PATH', suggested_questions=questions) Path('graphify-out/GRAPH_REPORT.md').write_text(report, encoding='utf-8') Path('graphify-out/.graphify_labels.json').write_text(json.dumps({str(k): v for k, v in labels.items()}), encoding='utf-8') print(f'Grafo: {G.number_of_nodes()} nós, {G.number_of_edges()} arestas, {len(communities)} comunidades') " # HTML interativo. echo "==> Exportando HTML..." "$PYTHON" -m graphify export html || graphify export html 2>/dev/null || true echo echo "Concluído. Saídas em:" echo " $(pwd)/graphify-out/graph.html" echo " $(pwd)/graphify-out/GRAPH_REPORT.md" echo " $(pwd)/graphify-out/graph.json" echo read -n 1 -s -r -p "Pressione qualquer tecla para fechar..." || true