Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
[![Sponsor](https://img.shields.io/badge/sponsor-safishamsi-ea4aaa?logo=github-sponsors)](https://github.com/sponsors/safishamsi)
[![LinkedIn](https://img.shields.io/badge/LinkedIn-Safi%20Shamsi-0077B5?logo=linkedin)](https://www.linkedin.com/in/safi-shamsi)

**An AI coding assistant skill.** Type `/graphify` in Claude Code, Codex, OpenCode, Cursor, Gemini CLI, GitHub Copilot CLI, VS Code Copilot Chat, Aider, OpenClaw, Factory Droid, Trae, Hermes, Kiro, or Google Antigravity - it reads your files, builds a knowledge graph, and gives you back structure you didn't know was there. Understand a codebase faster. Find the "why" behind architectural decisions.
**An AI coding assistant skill.** Type `/graphify` in Claude Code, Codex, OpenCode, Cursor, Gemini CLI, GitHub Copilot CLI, VS Code Copilot Chat, Aider, OpenClaw, Factory Droid, Trae, Hermes, Kiro, Google Antigravity, or Pi (via `graphify pi install`) - it reads your files, builds a knowledge graph, and gives you back structure you didn't know was there. Understand a codebase faster. Find the "why" behind architectural decisions.

Fully multimodal. Drop in code, PDFs, markdown, screenshots, diagrams, whiteboard photos, images in other languages, or video and audio files - graphify extracts concepts and relationships from all of it and connects them into one graph. Videos are transcribed with Whisper using a domain-aware prompt derived from your corpus. 25 languages supported via tree-sitter AST (Python, JS, TS, Go, Rust, Java, C, C++, Ruby, C#, Kotlin, Scala, PHP, Swift, Lua, Zig, PowerShell, Elixir, Objective-C, Julia, Verilog, SystemVerilog, Vue, Svelte, Dart).

Expand Down Expand Up @@ -48,7 +48,7 @@ Every relationship is tagged `EXTRACTED` (found directly in source), `INFERRED`

## Install

**Requires:** Python 3.10+ and one of: [Claude Code](https://claude.ai/code), [Codex](https://openai.com/codex), [OpenCode](https://opencode.ai), [Cursor](https://cursor.com), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [GitHub Copilot CLI](https://docs.github.com/en/copilot/how-tos/copilot-cli), [VS Code Copilot Chat](https://code.visualstudio.com/docs/copilot/overview), [Aider](https://aider.chat), [OpenClaw](https://openclaw.ai), [Factory Droid](https://factory.ai), [Trae](https://trae.ai), [Kiro](https://kiro.dev), Hermes, or [Google Antigravity](https://antigravity.google)
**Requires:** Python 3.10+ and one of: [Claude Code](https://claude.ai/code), [Codex](https://openai.com/codex), [OpenCode](https://opencode.ai), [Cursor](https://cursor.com), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [GitHub Copilot CLI](https://docs.github.com/en/copilot/how-tos/copilot-cli), [VS Code Copilot Chat](https://code.visualstudio.com/docs/copilot/overview), [Aider](https://aider.chat), [OpenClaw](https://openclaw.ai), [Factory Droid](https://factory.ai), [Trae](https://trae.ai), [Kiro](https://kiro.dev), [Pi](https://www.npmjs.com/package/@mariozechner/pi-coding-agent), Hermes, or [Google Antigravity](https://antigravity.google)

```bash
pip install graphifyy && graphify install
Expand All @@ -74,11 +74,14 @@ pip install graphifyy && graphify install
| Gemini CLI | `graphify install --platform gemini` |
| Hermes | `graphify install --platform hermes` |
| Kiro IDE/CLI | `graphify kiro install` |
| Pi | `graphify install --platform pi` |
| Cursor | `graphify cursor install` |
| Google Antigravity | `graphify antigravity install` |

Codex users also need `multi_agent = true` under `[features]` in `~/.codex/config.toml` for parallel extraction. Factory Droid uses the `Task` tool for parallel subagent dispatch. OpenClaw and Aider use sequential extraction (parallel agent support is still early on those platforms). Trae uses the Agent tool for parallel subagent dispatch and does **not** support PreToolUse hooks — AGENTS.md is the always-on mechanism. Codex supports PreToolUse hooks — `graphify codex install` installs one in `.codex/hooks.json` in addition to writing AGENTS.md.

Pi loads skills from `~/.pi/agent/skills/`. After `graphify install --platform pi`, invoke graphify with `/skill:graphify`. If you want a native `/graphify` slash command inside a project, run `graphify pi install` to add a Pi prompt template alias in `.pi/prompts/graphify.md`.

Then open your AI coding assistant and type:

```
Expand Down Expand Up @@ -107,6 +110,7 @@ After building a graph, run this once in your project:
| Gemini CLI | `graphify gemini install` |
| Hermes | `graphify hermes install` |
| Kiro IDE/CLI | `graphify kiro install` |
| Pi | `graphify pi install` |
| Google Antigravity | `graphify antigravity install` |

**Claude Code** does two things: writes a `CLAUDE.md` section telling Claude to read `graphify-out/GRAPH_REPORT.md` before answering architecture questions, and installs a **PreToolUse hook** (`settings.json`) that fires before every Glob and Grep call. If a knowledge graph exists, Claude sees: _"graphify: Knowledge graph exists. Read GRAPH_REPORT.md for god nodes and community structure before searching raw files."_ — so Claude navigates via the graph instead of grepping through every file.
Expand All @@ -123,6 +127,8 @@ After building a graph, run this once in your project:

**Kiro IDE/CLI** writes the skill to `.kiro/skills/graphify/SKILL.md` (invoked via `/graphify`) and a steering file to `.kiro/steering/graphify.md` with `inclusion: always` — Kiro injects this into every conversation automatically, no hook needed.

**Pi** installs the skill to `~/.pi/agent/skills/graphify/SKILL.md`, writes `AGENTS.md` in your project root, and adds `.pi/prompts/graphify.md` so `/graphify` works as a Pi prompt-template alias. Pi also supports `/skill:graphify` directly, and like Aider/OpenClaw it relies on `AGENTS.md` rather than hooks.

**Google Antigravity** writes `.agent/rules/graphify.md` (always-on rules) and `.agent/workflows/graphify.md` (registers `/graphify` as a slash command). No hook equivalent exists in Antigravity — rules are the always-on mechanism.

**GitHub Copilot CLI** copies the skill to `~/.copilot/skills/graphify/SKILL.md`. Run `graphify copilot install` to set it up.
Expand Down Expand Up @@ -263,6 +269,8 @@ graphify hermes install # AGENTS.md + ~/.hermes/skills/ (Hermes)
graphify hermes uninstall
graphify kiro install # .kiro/skills/ + .kiro/steering/graphify.md (Kiro IDE/CLI)
graphify kiro uninstall
graphify pi install # Pi skill + AGENTS.md + .pi/prompts/graphify.md
graphify pi uninstall
graphify antigravity install # .agent/rules + .agent/workflows (Google Antigravity)
graphify antigravity uninstall

Expand Down
118 changes: 102 additions & 16 deletions graphify/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ def _check_skill_version(skill_dst: Path) -> None:
"skill_dst": Path(".kiro") / "skills" / "graphify" / "SKILL.md",
"claude_md": False,
},
"pi": {
"skill_file": "skill-pi.md",
"skill_dst": Path(".pi") / "agent" / "skills" / "graphify" / "SKILL.md",
"claude_md": False,
},
"antigravity": {
"skill_file": "skill.md",
"skill_dst": Path(".agent") / "skills" / "graphify" / "SKILL.md",
Expand Down Expand Up @@ -162,7 +167,14 @@ def install(platform: str = "claude") -> None:
print()
print("Done. Open your AI coding assistant and type:")
print()
print(" /graphify .")
if platform == "pi":
print(" /skill:graphify .")
print()
print("For a native /graphify alias inside a project, run:")
print()
print(" graphify pi install")
else:
print(" /graphify .")
print()


Expand Down Expand Up @@ -594,6 +606,24 @@ def _cursor_uninstall(project_dir: Path) -> None:
print(f"graphify Cursor rule removed from {rule_path.resolve()}")


_PI_PROMPT_PATH = Path(".pi") / "prompts" / "graphify.md"
_PI_PROMPT_MARKER = "<!-- graphify pi prompt alias -->"
_PI_PROMPT = """\
---
description: Build, update, or query the graphify knowledge graph for this project
---
<!-- graphify pi prompt alias -->
Use the installed `graphify` skill now.

Original `/graphify` arguments: $ARGUMENTS

Rules:
- If no arguments were provided, use `.`
- Treat the arguments exactly like the graphify CLI syntax
- Follow the graphify skill instructions rather than improvising a different workflow
"""


# OpenCode tool.execute.before plugin — fires before every tool call.
# Injects a graph reminder into bash command output when graph.json exists.
_OPENCODE_PLUGIN_JS = """\
Expand Down Expand Up @@ -733,7 +763,7 @@ def _uninstall_codex_hook(project_dir: Path) -> None:


def _agents_install(project_dir: Path, platform: str) -> None:
"""Write the graphify section to the local AGENTS.md (Codex/OpenCode/OpenClaw)."""
"""Write the graphify section to the local AGENTS.md for harnesses that read it."""
target = (project_dir or Path(".")) / "AGENTS.md"

if target.exists():
Expand Down Expand Up @@ -885,6 +915,63 @@ def claude_uninstall(project_dir: Path | None = None) -> None:
_uninstall_claude_hook(project_dir or Path("."))


def _remove_platform_skill(platform_name: str) -> list[str]:
cfg = _PLATFORM_CONFIG[platform_name]
skill_dst = Path.home() / cfg["skill_dst"]
removed = []
if skill_dst.exists():
skill_dst.unlink()
removed.append(f"skill removed: {skill_dst}")
version_file = skill_dst.parent / ".graphify_version"
if version_file.exists():
version_file.unlink()
for d in (skill_dst.parent, skill_dst.parent.parent, skill_dst.parent.parent.parent):
try:
d.rmdir()
except OSError:
break
return removed


def _pi_install(project_dir: Path | None = None) -> None:
"""Install graphify for Pi: global skill + local AGENTS + /graphify prompt alias."""
install(platform="pi")
_agents_install(project_dir or Path("."), "pi")

Comment on lines +936 to +940
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_pi_install() calls install(platform="pi"), which prints the post-install hint "For a native /graphify alias inside a project, run: graphify pi install". When the user is already running graphify pi install, this becomes confusing/self-referential output. Consider refactoring install() to allow suppressing the Pi-specific hint when invoked from _pi_install(), or extract a lower-level "copy skill file" helper that _pi_install() can call without the interactive guidance prints.

Copilot uses AI. Check for mistakes.
prompt_path = (project_dir or Path(".")) / _PI_PROMPT_PATH
prompt_path.parent.mkdir(parents=True, exist_ok=True)
if prompt_path.exists():
content = prompt_path.read_text(encoding="utf-8")
if _PI_PROMPT_MARKER in content:
print(f" {_PI_PROMPT_PATH} -> already configured")
else:
print(f" {_PI_PROMPT_PATH} -> already exists, preserving existing prompt")
else:
prompt_path.write_text(_PI_PROMPT, encoding="utf-8")
print(f" {_PI_PROMPT_PATH} -> /graphify prompt alias written")

print()
print("Pi will now read the knowledge graph before answering codebase questions.")
print("Use /graphify inside this project, or /skill:graphify anywhere the skill is installed.")


def _pi_uninstall(project_dir: Path | None = None) -> None:
"""Remove graphify Pi integration: skill, AGENTS.md section, and prompt alias."""
removed = _remove_platform_skill("pi")
print("; ".join(removed) if removed else "nothing to remove from global Pi skill install")

prompt_path = (project_dir or Path(".")) / _PI_PROMPT_PATH
if prompt_path.exists():
content = prompt_path.read_text(encoding="utf-8")
if _PI_PROMPT_MARKER in content:
prompt_path.unlink()
print(f" {_PI_PROMPT_PATH} -> removed")
else:
print(f" {_PI_PROMPT_PATH} -> preserved (not managed by graphify)")

_agents_uninstall(project_dir or Path("."), platform="pi")


def main() -> None:
# Check all known skill install locations for a stale version stamp.
# Skip during install/uninstall (hook writes trigger a fresh check anyway).
Expand All @@ -897,7 +984,7 @@ def main() -> None:
print("Usage: graphify <command>")
print()
print("Commands:")
print(" install [--platform P] copy skill to platform config dir (claude|windows|codex|opencode|aider|claw|droid|trae|trae-cn|gemini|cursor|antigravity|hermes|kiro)")
print(" install [--platform P] copy skill to platform config dir (claude|windows|codex|opencode|aider|claw|droid|trae|trae-cn|gemini|cursor|antigravity|hermes|kiro|pi)")
print(" path \"A\" \"B\" shortest path between two nodes in graph.json")
print(" --graph <path> path to graph.json (default graphify-out/graph.json)")
print(" explain \"X\" plain-language explanation of a node and its neighbors")
Expand Down Expand Up @@ -953,6 +1040,8 @@ def main() -> None:
print(" hermes uninstall remove skill from ~/.hermes/skills/graphify/")
print(" kiro install write skill to .kiro/skills/graphify/ + steering file (Kiro IDE/CLI)")
print(" kiro uninstall remove skill + steering file")
print(" pi install install Pi skill + write AGENTS.md + .pi/prompts/graphify.md")
print(" pi uninstall remove Pi skill + AGENTS.md section + /graphify prompt alias")
print()
return

Expand Down Expand Up @@ -1014,19 +1103,7 @@ def main() -> None:
if subcmd == "install":
install(platform="copilot")
elif subcmd == "uninstall":
skill_dst = Path.home() / _PLATFORM_CONFIG["copilot"]["skill_dst"]
removed = []
if skill_dst.exists():
skill_dst.unlink()
removed.append(f"skill removed: {skill_dst}")
version_file = skill_dst.parent / ".graphify_version"
if version_file.exists():
version_file.unlink()
for d in (skill_dst.parent, skill_dst.parent.parent, skill_dst.parent.parent.parent):
try:
d.rmdir()
except OSError:
break
removed = _remove_platform_skill("copilot")
print("; ".join(removed) if removed else "nothing to remove")
else:
print("Usage: graphify copilot [install|uninstall]", file=sys.stderr)
Expand All @@ -1040,6 +1117,15 @@ def main() -> None:
else:
print("Usage: graphify kiro [install|uninstall]", file=sys.stderr)
sys.exit(1)
elif cmd == "pi":
subcmd = sys.argv[2] if len(sys.argv) > 2 else ""
if subcmd == "install":
_pi_install(Path("."))
elif subcmd == "uninstall":
_pi_uninstall(Path("."))
else:
print("Usage: graphify pi [install|uninstall]", file=sys.stderr)
sys.exit(1)
elif cmd in ("aider", "codex", "opencode", "claw", "droid", "trae", "trae-cn", "hermes"):
subcmd = sys.argv[2] if len(sys.argv) > 2 else ""
if subcmd == "install":
Expand Down
Loading
Loading