Files
gart/admin/graphify.command

143 lines
5.6 KiB
Bash
Executable File

#!/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 <caminho> # 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