mirror of
https://github.com/safishamsi/graphify.git
synced 2026-09-06 21:55:47 +00:00
This commit is contained in:
@@ -7,6 +7,8 @@ Full release notes with details on each version: [GitHub Releases](https://githu
|
||||
- Feat: `graphify extract` now runs incrementally - auto-detects prior `manifest.json` and re-extracts only changed/new files; semantic results cached by content hash so unchanged docs cost zero LLM tokens on repeat runs (#698)
|
||||
- Feat: Entity deduplication pipeline runs on every build - entropy gate + MinHash/LSH blocking + Jaro-Winkler verification + same-community boost collapses near-duplicate entities (typos, spacing, plurals) before clustering
|
||||
- Feat: `--dedup-llm` flag for `graphify extract` - optional LLM tiebreaker for ambiguous entity pairs (~$0.01 for 10k-node graphs), off by default
|
||||
- Fix: `graphify hook install` rebuild now preserves human-readable community labels from `.graphify_labels.json` instead of resetting to generic "Community N" names on every commit (#705)
|
||||
- Fix: `graphify install --platform gemini` now works correctly (#706)
|
||||
- Deps: `datasketch` and `rapidfuzz` added as base dependencies
|
||||
|
||||
## 0.7.4 (2026-05-04)
|
||||
|
||||
+29
-6
@@ -9,6 +9,16 @@ from pathlib import Path
|
||||
_GRAPHIFY_OUT = os.environ.get("GRAPHIFY_OUT", "graphify-out")
|
||||
|
||||
|
||||
def _git_head() -> str | None:
|
||||
"""Return current git HEAD commit hash, or None outside a repo."""
|
||||
import subprocess as _sp
|
||||
try:
|
||||
r = _sp.run(["git", "rev-parse", "HEAD"], capture_output=True, text=True, timeout=3)
|
||||
return r.stdout.strip() if r.returncode == 0 else None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
from graphify.detect import CODE_EXTENSIONS, DOC_EXTENSIONS, PAPER_EXTENSIONS, IMAGE_EXTENSIONS
|
||||
|
||||
_WATCHED_EXTENSIONS = CODE_EXTENSIONS | DOC_EXTENSIONS | PAPER_EXTENSIONS | IMAGE_EXTENSIONS
|
||||
@@ -64,6 +74,7 @@ def _rebuild_code(watch_path: Path, *, follow_symlinks: bool = False, force: boo
|
||||
print("[graphify watch] No code files found - nothing to rebuild.")
|
||||
return False
|
||||
|
||||
commit = _git_head()
|
||||
result = extract(code_files, cache_root=watch_root)
|
||||
|
||||
# Preserve semantic nodes/edges from a previous full run.
|
||||
@@ -106,13 +117,22 @@ def _rebuild_code(watch_path: Path, *, follow_symlinks: bool = False, force: boo
|
||||
cohesion = score_all(G, communities)
|
||||
gods = god_nodes(G)
|
||||
surprises = surprising_connections(G, communities)
|
||||
labels = {cid: "Community " + str(cid) for cid in communities}
|
||||
labels_file = out / ".graphify_labels.json"
|
||||
try:
|
||||
raw = json.loads(labels_file.read_text(encoding="utf-8")) if labels_file.exists() else {}
|
||||
labels = {int(k): v for k, v in raw.items() if int(k) in communities}
|
||||
except Exception:
|
||||
raw = {}
|
||||
labels = {}
|
||||
for cid in communities:
|
||||
if cid not in labels:
|
||||
labels[cid] = "Community " + str(cid)
|
||||
questions = suggest_questions(G, communities, labels)
|
||||
|
||||
out.mkdir(exist_ok=True)
|
||||
(out / ".graphify_root").write_text(str(watch_root), encoding="utf-8")
|
||||
|
||||
json_written = to_json(G, communities, str(out / "graph.json"), force=force)
|
||||
json_written = to_json(G, communities, str(out / "graph.json"), force=force, built_at_commit=commit)
|
||||
if not json_written:
|
||||
return False
|
||||
|
||||
@@ -123,7 +143,8 @@ def _rebuild_code(watch_path: Path, *, follow_symlinks: bool = False, force: boo
|
||||
pass
|
||||
|
||||
report = generate(G, communities, cohesion, labels, gods, surprises, detection,
|
||||
{"input": 0, "output": 0}, report_root, suggested_questions=questions)
|
||||
{"input": 0, "output": 0}, report_root, suggested_questions=questions,
|
||||
built_at_commit=commit)
|
||||
(out / "GRAPH_REPORT.md").write_text(report, encoding="utf-8")
|
||||
|
||||
# to_html raises ValueError for graphs > MAX_NODES_FOR_VIZ (5000).
|
||||
@@ -241,10 +262,12 @@ def watch(watch_path: Path, debounce: float = 3.0) -> None:
|
||||
batch = list(changed)
|
||||
changed.clear()
|
||||
print(f"\n[graphify watch] {len(batch)} file(s) changed")
|
||||
if _has_non_code(batch):
|
||||
_notify_only(watch_path)
|
||||
else:
|
||||
has_non_code = _has_non_code(batch)
|
||||
has_code = any(p.suffix.lower() in _CODE_EXTENSIONS for p in batch)
|
||||
if has_code:
|
||||
_rebuild_code(watch_path)
|
||||
if has_non_code:
|
||||
_notify_only(watch_path)
|
||||
except KeyboardInterrupt:
|
||||
print("\n[graphify watch] Stopped.")
|
||||
finally:
|
||||
|
||||
Reference in New Issue
Block a user