Files

119 lines
3.9 KiB
Python

# git hook integration - install/uninstall graphify post-commit hook
from __future__ import annotations
from pathlib import Path
_HOOK_MARKER = "# graphify-hook"
_HOOK_SCRIPT = """\
#!/bin/bash
# graphify-hook
# Auto-rebuilds the knowledge graph after each commit (code files only, no LLM needed).
# Installed by: graphify hook install
CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || git diff --name-only HEAD 2>/dev/null)
if [ -z "$CHANGED" ]; then
exit 0
fi
export GRAPHIFY_CHANGED="$CHANGED"
python3 -c "
import os, sys
from pathlib import Path
CODE_EXTS = {
'.py', '.ts', '.js', '.go', '.rs', '.java', '.cpp', '.c', '.rb', '.swift',
'.kt', '.cs', '.scala', '.php', '.cc', '.cxx', '.hpp', '.h', '.kts',
}
changed_raw = os.environ.get('GRAPHIFY_CHANGED', '')
changed = [Path(f.strip()) for f in changed_raw.strip().splitlines() if f.strip()]
code_changed = [f for f in changed if f.suffix.lower() in CODE_EXTS and f.exists()]
if not code_changed:
sys.exit(0)
print(f'[graphify hook] {len(code_changed)} code file(s) changed - rebuilding graph...')
try:
from graphify.watch import _rebuild_code
_rebuild_code(Path('.'))
except Exception as exc:
print(f'[graphify hook] Rebuild failed: {exc}')
sys.exit(0)
"
"""
def _git_root(path: Path) -> Path | None:
"""Walk up to find .git directory."""
current = path.resolve()
for parent in [current, *current.parents]:
if (parent / ".git").exists():
return parent
return None
def install(path: Path = Path(".")) -> str:
"""Install graphify post-commit hook in the nearest git repo.
Returns a message describing what was done.
"""
root = _git_root(path)
if root is None:
raise RuntimeError(f"No git repository found at or above {path.resolve()}")
hooks_dir = root / ".git" / "hooks"
hooks_dir.mkdir(exist_ok=True)
hook_path = hooks_dir / "post-commit"
if hook_path.exists():
content = hook_path.read_text()
if _HOOK_MARKER in content:
return f"graphify hook already installed at {hook_path}"
# Append to existing hook
hook_path.write_text(content.rstrip() + "\n\n" + _HOOK_SCRIPT)
return f"graphify hook appended to existing post-commit hook at {hook_path}"
hook_path.write_text(_HOOK_SCRIPT)
hook_path.chmod(0o755)
return f"graphify hook installed at {hook_path}"
def uninstall(path: Path = Path(".")) -> str:
"""Remove graphify post-commit hook."""
root = _git_root(path)
if root is None:
raise RuntimeError(f"No git repository found at or above {path.resolve()}")
hook_path = root / ".git" / "hooks" / "post-commit"
if not hook_path.exists():
return "No post-commit hook found - nothing to remove."
content = hook_path.read_text()
if _HOOK_MARKER not in content:
return "graphify hook not found in post-commit - nothing to remove."
# Strip everything from our marker onwards
before = content.split(_HOOK_MARKER)[0].rstrip()
# 'before' is empty or just a shebang line if the whole file was ours
non_empty = [l for l in before.splitlines() if l.strip() and not l.startswith("#!")]
if not non_empty:
hook_path.unlink()
return f"Removed post-commit hook at {hook_path}"
else:
hook_path.write_text(before + "\n")
return f"graphify hook removed from {hook_path} (other hook content preserved)"
def status(path: Path = Path(".")) -> str:
"""Check if graphify hook is installed."""
root = _git_root(path)
if root is None:
return "Not in a git repository."
hook_path = root / ".git" / "hooks" / "post-commit"
if not hook_path.exists():
return "graphify hook: not installed"
if _HOOK_MARKER in hook_path.read_text():
return f"graphify hook: installed at {hook_path}"
return "graphify hook: not installed (post-commit exists but graphify hook not found)"