Skill: fantox/graphify (version 2.1.0)

--- name: graphify-skill description: "AI coding assistant skill for building and querying knowledge graphs from codebases, docs, and media. Use for: understanding complex codebases, architectural analysis, context sharing with OpenClaw/Claude Code, and extracting design rationale from multimodal sources. Always check graphify-out/GRAPH_REPORT.md before grepping or globbing to find relevant files." metadata: openclaw: requires: bins: - python3 - pip env: - name: ANTHROPIC_API_KEY description: "Required for semantic extraction (docs, PDFs, images). Not needed for AST-only code extraction." required: false - name: OPENAI_API_KEY description: "Alternative LLM backend for semantic extraction." required: false install: - kind: uv package: graphify bins: [graphify] primaryEnv: "" emoji: "🕸️" homepage: https://github.com/safishamsi/graphify os: - linux - macos - windows --- # Graphify Skill Graphify turns a folder of code, docs, images, and videos into a **queryable knowledge graph**. After running `graphify .`, the generated `GRAPH_REPORT.md` is referenced by the assistant when you ask it to explore the codebase. Use it to navigate unfamiliar codebases, trace architectural intent, and share context efficiently. **Credential note:** Semantic extraction (docs, PDFs, images) calls an external LLM using your own API key (`ANTHROPIC_API_KEY` or `OPENAI_API_KEY`). AST code extraction and Whisper transcription run fully locally with no API key required. > **Token efficiency:** Reading `GRAPH_REPORT.md` is ~71.5× cheaper than reading raw source files. Always check it first. --- ## Installation ### Minimum install ```bash pip install graphify ``` ### Recommended install with all extras ```bash pip install "graphify[pdf,video,watch,svg]" ``` | Extra | Adds support for | |-------|-----------------| | `pdf` | PDF papers and documents | | `video` | Video/audio transcription via faster-whisper | | `watch` | `--watch` file monitoring | | `svg` | `--svg` export | | `mcp` | Model Context Protocol server | | `neo4j` | Neo4j graph database integration | | `office` | Word/Excel/PowerPoint documents | ### Register with OpenClaw ```bash graphify claw install ``` This copies the skill into OpenClaw's global skill directory and writes an `AGENTS.md` to the project root so the graph is consulted on every tool call. ### Register with other platforms ```bash graphify claude install # Claude Code (CLAUDE.md + PreToolUse hook) graphify cursor install # Cursor (.cursorrules) graphify codex install # Codex (AGENTS.md) graphify copilot install # GitHub Copilot CLI graphify gemini install # Gemini CLI graphify aider install # Aider ``` ### Git post-commit hook (optional, local project only) Installs a hook in the current project's `.git/hooks/` — no global changes. ```bash graphify hook install ``` --- ## Building a Knowledge Graph ### First run ```bash graphify . ``` Graphify runs a **three-pass pipeline**: 1. **AST extraction** — deterministic tree-sitter parsing of all code files (no LLM calls) 2. **Transcription** — local Whisper processing of any video/audio 3. **Semantic extraction** — parallel LLM calls (using your configured API key) analyze docs, papers, and images ### Incremental update (changed files only) ```bash graphify . --update ``` Uses a SHA-256 cache in `graphify-out/cache/` — safe to run after every save. ### Watch mode (auto-sync as files change) ```bash graphify . --watch ``` ### Deep mode (aggressive inferred-edge extraction) ```bash graphify . --mode deep ``` Adds `INFERRED` edges with confidence scores (0.0–1.0) and `AMBIGUOUS` edges flagged for review. ### Preserve edge directionality ```bash graphify . --directed ``` ### Re-cluster without re-extracting ```bash graphify . --cluster-only ``` ### Skip HTML visualization (faster CI runs) ```bash graphify . --no-viz ``` --- ## Output Artifacts All artifacts land in `graphify-out/`: | File | Purpose | |------|---------| | `GRAPH_REPORT.md` | **Read this first.** God nodes, community structure, surprising connections, suggested questions. | | `graph.html` | Interactive browser visualization — open for human review. | | `graph.json` | Raw graph data for programmatic querying via CLI or script. | | `cache/` | SHA-256 incremental cache — commit everything *except* this directory. | ### Additional export formats ```bash graphify . --svg # SVG visualization graphify . --graphml # Gephi / yEd compatible export graphify . --wiki # Wikipedia-style article per node ``` --- ## Querying the Graph ```bash # Natural-language semantic search graphify query "where is authentication handled?" # Trace a specific path (DFS traversal) graphify query "how does the request reach the database?" --dfs # Shortest path between two nodes graphify path "AuthMiddleware" "PostgresAdapter" # Plain-language explanation of a node graphify explain "UserSessionManager" ``` --- ## Adding External Content ```bash graphify add <arxiv-url> # Fetch and index a research paper graphify add <x.com-url> # Fetch and index a tweet graphify add <video-url> # Download and transcribe video/audio ``` After adding, run `graphify . --update` to integrate the new nodes into the graph. --- ## Ignoring Files Create `.graphifyignore` in the project root (same syntax as `.gitignore`): ``` node_modules/ dist/ build/ .next/ vendor/ *.generated.* *.min.js *.lock graphify-out/cache/ ``` See `templates/graphifyignore.txt` in this skill for a comprehensive starter. --- ## Relationship Types | Tag | Meaning | |-----|---------| | `EXTRACTED` | Found directly in source (AST, explicit reference) | | `INFERRED` | Reasonable inference; includes `confidence` score 0.0–1.0 | | `AMBIGUOUS` | Low-confidence; flagged for manual review | Use `--mode deep` to maximize `INFERRED` coverage. Filter `AMBIGUOUS` edges when precision matters. --- ## Best Practices for OpenClaw / Claude Code ### 1. Always read the report before exploring ``` Read graphify-out/GRAPH_REPORT.md ``` The report identifies "god nodes" (highest-degree concepts), community clusters, and suggested entry-point questions. Use these to focus subsequent searches. ### 2. Map communities to features Each community in the report corresponds to a functional area of the codebase. When fixing a bug or adding a feature, identify the relevant community first, then limit file reads to that cluster. ### 3. Use `graphify query` before `grep` For open-ended questions ("where is X handled?"), prefer `graphify query` — it searches the semantic graph and returns ranked node paths rather than raw text matches. ### 4. Use `graphify path` for impact analysis Before editing a node, run `graphify path "NodeA" "NodeB"` to find coupling chains and assess blast radius. ### 5. Use `--update` aggressively After any significant edit, run `graphify . --update` to keep the graph current. The cache makes this cheap. ### 6. Team workflows - Commit `graphify-out/` (excluding `graphify-out/cache/`) so teammates get graph context immediately on checkout. - One team member builds the initial graph; all others benefit without LLM cost. - Install `graphify hook install` to auto-rebuild on every commit. ### 7. Multimodal context Place architecture diagrams, whiteboard photos, or design mockups in the project directory before running `graphify .`. Claude vision will link visual concepts to code nodes. --- ## Supported Languages Python, JavaScript, TypeScript, Go, Rust, Java, C, C++, Ruby, C#, Kotlin, Scala, PHP, Swift, Lua, Zig, PowerShell, Elixir, Objective-C, Julia, Verilog, SystemVerilog, Vue, Svelte, Dart Plus: Markdown, MDX, HTML, plain text, RST, PDF, PNG/JPG/WebP/GIF images, MP4/MOV/MKV/WebM/MP3/WAV/M4A/OGG media. --- ## Troubleshooting | Symptom | Fix | |---------|-----| | `graphify: command not found` | Run `pip install graphify` and ensure pip's bin directory is on PATH | | Graph is stale after edits | Run `graphify . --update` | | Missing nodes for a language | Confirm tree-sitter grammar is installed; run `graphify . --update` | | Video transcription slow | Expected — Whisper runs locally. Add GPU acceleration or use `--no-viz` to skip unrelated steps | | `AMBIGUOUS` edges dominating | Switch from `--mode deep` to default mode, or filter by `confidence > 0.7` in `graph.json` | | `AGENTS.md` not picked up | Re-run `graphify claw install` from the project root |

path: templates/graphifyignore.txt
content: # .graphifyignore — copy this to your project root and rename to .graphifyignore
# Uses .gitignore syntax. Paths are relative to the project root.
# Graphify will skip any file or directory matched below.

# ── Package managers / dependency trees ──────────────────────────────────────
node_modules/
vendor/
.venv/
venv/
env/
.env/
__pycache__/
*.pyc
*.pyo
.tox/
.nox/
bower_components/
jspm_packages/
.yarn/
.pnp.*

# ── Build and distribution outputs ───────────────────────────────────────────
dist/
build/
out/
.next/
.nuxt/
.output/
.svelte-kit/
target/
bin/
obj/
*.egg-info/
*.dist-info/
site/

# ── Generated / compiled files ────────────────────────────────────────────────
*.min.js
*.min.css
*.bundle.js
*.chunk.js
*.generated.*
*.pb.go
*_pb2.py
*_pb2_grpc.py
*.g.dart

# ── Lock files (high noise, low signal) ──────────────────────────────────────
package-lock.json
yarn.lock
pnpm-lock.yaml
poetry.lock
Cargo.lock
Gemfile.lock
composer.lock
go.sum

# ── Test fixtures, snapshots, and large datasets ─────────────────────────────
__snapshots__/
testdata/
fixtures/
*.snap
*.golden

# ── IDE and editor artifacts ──────────────────────────────────────────────────
.idea/
.vscode/
*.swp
*.swo
.DS_Store
Thumbs.db

# ── Version control internals ─────────────────────────────────────────────────
.git/
.svn/
.hg/

# ── CI and infrastructure ─────────────────────────────────────────────────────
.github/
.circleci/
.gitlab/
.terraform/
*.tfstate
*.tfstate.backup

# ── Logs, coverage, and profiling ─────────────────────────────────────────────
*.log
logs/
coverage/
.nyc_output/
htmlcov/
*.prof
*.trace

# ── Secrets and credentials (never index) ─────────────────────────────────────
.env
.env.*
*.pem
*.key
*.p12
*.pfx
secrets/
credentials/

# ── Graphify cache (never commit) ─────────────────────────────────────────────
graphify-out/cache/

# ── Large binary assets (index if you want vision analysis; exclude otherwise) ─
# *.png
# *.jpg
# *.gif
# *.mp4
# *.mov


path: scripts/query_graph.py
content: #!/usr/bin/env python3
"""
Programmatic query helper for graphify-out/graph.json.

Usage:
    python query_graph.py                      # print full summary
    python query_graph.py --god-nodes          # list highest-degree nodes
    python query_graph.py --communities        # list community clusters
    python query_graph.py --neighbors NodeId   # show direct neighbours
    python query_graph.py --path NodeA NodeB   # shortest path
    python query_graph.py --search keyword     # fuzzy node search
    python query_graph.py --inferred           # show inferred edges (confidence > threshold)
    python query_graph.py --export-context     # emit AI context block for graph content
"""

import json
import argparse
import sys
from pathlib import Path
from collections import defaultdict, deque


def load_graph(graph_path: Path) -> dict:
    if not graph_path.exists():
        sys.exit(f"ERROR: graph.json not found at {graph_path}\nRun: graphify .")
    with open(graph_path) as f:
        return json.load(f)


def build_adjacency(graph: dict, directed: bool = False) -> dict[str, list[dict]]:
    adj: dict[str, list[dict]] = defaultdict(list)
    for edge in graph.get("edges", []):
        adj[edge["source"]].append(edge)
        if not directed:
            reverse = {**edge, "source": edge["target"], "target": edge["source"]}
            adj[edge["target"]].append(reverse)
    return adj


def node_by_id(graph: dict) -> dict[str, dict]:
    return {n["id"]: n for n in graph.get("nodes", [])}


def god_nodes(graph: dict, top_n: int = 10) -> list[dict]:
    nodes = graph.get("nodes", [])
    return sorted(nodes, key=lambda n: n.get("degree", 0), reverse=True)[:top_n]


def communities(graph: dict) -> list[dict]:
    return graph.get("communities", [])


def neighbours(graph: dict, node_id: str) -> tuple[dict | None, list[dict]]:
    nmap = node_by_id(graph)
    node = nmap.get(node_id)
    edges = [e for e in graph.get("edges", []) if node_id in (e["source"], e["target"])]
    return node, edges


def bfs_path(graph: dict, start: str, end: str) -> list[str] | None:
    adj = build_adjacency(graph, directed=False)
    visited = {start}
    queue: deque[list[str]] = deque([[start]])
    while queue:
        path = queue.popleft()
        current = path[-1]
        if current == end:
            return path
        for edge in adj.get(current, []):
            nxt = edge["target"]
            if nxt not in visited:
                visited.add(nxt)
                queue.append(path + [nxt])
    return None


def search_nodes(graph: dict, keyword: str) -> list[dict]:
    kw = keyword.lower()
    return [
        n for n in graph.get("nodes", [])
        if kw in n.get("label", "").lower()
        or kw in n.get("summary", "").lower()
        or kw in n.get("file", "").lower()
    ]


def inferred_edges(graph: dict, min_confidence: float = 0.7) -> list[dict]:
    return [
        e for e in graph.get("edges", [])
        if e.get("relation") == "INFERRED"
        and e.get("confidence", 0.0) >= min_confidence
    ]


def print_summary(graph: dict) -> None:
    meta = graph.get("metadata", {})
    nodes = graph.get("nodes", [])
    edges = graph.get("edges", [])
    comms = graph.get("communities", [])
    print(f"graphify knowledge graph summary")
    print(f"  version   : {meta.get('graphify_version', 'unknown')}")
    print(f"  generated : {meta.get('generated_at', 'unknown')}")
    print(f"  nodes     : {len(nodes)}")
    print(f"  edges     : {len(edges)}")
    print(f"  communities: {len(comms)}")
    print()
    print("Top god nodes:")
    for n in god_nodes(graph, top_n=5):
        print(f"  [{n.get('degree', 0):>4d}] {n['label']}  ({n.get('file', '')})")
    print()
    print("Communities:")
    for c in comms[:8]:
        gods = ", ".join(c.get("god_nodes", []))
        print(f"  [{c['id']}] {c.get('label', 'unnamed')}  ({len(c.get('members', []))} members)  gods: {gods}")


def export_context_block(graph: dict) -> str:
    lines = ["<graphify_context>"]
    meta = graph.get("metadata", {})
    lines.append(f"nodes={len(graph.get('nodes', []))} edges={len(graph.get('edges', []))} generated={meta.get('generated_at', '')}")
    lines.append("")
    lines.append("GOD NODES (highest-degree concepts):")
    for n in god_nodes(graph, top_n=10):
        lines.append(f"  {n['label']} (degree={n.get('degree', 0)}, file={n.get('file', '')}, community={n.get('community', '?')})")
    lines.append("")
    lines.append("COMMUNITIES:")
    for c in graph.get("communities", []):
        members_preview = ", ".join(c.get("members", [])[:5])
        lines.append(f"  [{c['id']}] {c.get('label', 'unnamed')}: {members_preview}{'...' if len(c.get('members', [])) > 5 else ''}")
    lines.append("</graphify_context>")
    return "\n".join(lines)


def main() -> None:
    parser = argparse.ArgumentParser(description="Query graphify-out/graph.json")
    parser.add_argument("--graph", default="graphify-out/graph.json", help="Path to graph.json")
    parser.add_argument("--god-nodes", action="store_true")
    parser.add_argument("--communities", action="store_true")
    parser.add_argument("--neighbors", metavar="NODE_ID")
    parser.add_argument("--path", nargs=2, metavar=("FROM", "TO"))
    parser.add_argument("--search", metavar="KEYWORD")
    parser.add_argument("--inferred", action="store_true")
    parser.add_argument("--min-confidence", type=float, default=0.7)
    parser.add_argument("--export-context", action="store_true")
    parser.add_argument("--top", type=int, default=10)
    args = parser.parse_args()

    graph = load_graph(Path(args.graph))
    nmap = node_by_id(graph)

    if args.god_nodes:
        print(f"Top {args.top} god nodes:")
        for n in god_nodes(graph, top_n=args.top):
            print(f"  [{n.get('degree', 0):>4d}] {n['label']}  ({n.get('file', '')}:{n.get('line', '')})")

    elif args.communities:
        for c in communities(graph):
            print(f"\n[Community {c['id']}] {c.get('label', 'unnamed')}  ({len(c.get('members', []))} members)")
            for m in c.get("members", []):
                node = nmap.get(m, {})
                print(f"  - {node.get('label', m)}  ({node.get('file', '')})")

    elif args.neighbors:
        node, edges = neighbours(graph, args.neighbors)
        if not node:
            sys.exit(f"Node '{args.neighbors}' not found. Use --search to find node IDs.")
        print(f"Node: {node['label']}  ({node.get('file', '')})\n")
        for e in edges:
            other_id = e["target"] if e["source"] == args.neighbors else e["source"]
            other = nmap.get(other_id, {})
            conf = f"  conf={e['confidence']:.2f}" if "confidence" in e else ""
            direction = "→" if e["source"] == args.neighbors else "←"
            print(f"  {direction} {other.get('label', other_id)}  [{e.get('relation', '')} {e.get('label', '')}]{conf}")

    elif args.path:
        start, end = args.path
        if start not in nmap or end not in nmap:
            matches_start = search_nodes(graph, start)
            matches_end = search_nodes(graph, end)
            if not matches_start:
                sys.exit(f"Node '{start}' not found.")
            if not matches_end:
                sys.exit(f"Node '{end}' not found.")
            start = matches_start[0]["id"]
            end = matches_end[0]["id"]
            print(f"Resolved: {start} → {end}")
        path = bfs_path(graph, start, end)
        if path is None:
            print(f"No path found between '{start}' and '{end}'.")
        else:
            print(f"Shortest path ({len(path) - 1} hops):")
            for node_id in path:
                n = nmap.get(node_id, {})
                print(f"  {n.get('label', node_id)}  ({n.get('file', '')})")

    elif args.search:
        results = search_nodes(graph, args.search)
        if not results:
            print(f"No nodes matching '{args.search}'.")
        else:
            print(f"{len(results)} result(s) for '{args.search}':")
            for n in results[:args.top]:
                print(f"  {n['id']}  {n['label']}  ({n.get('file', '')})  community={n.get('community', '?')}")

    elif args.inferred:
        edges = inferred_edges(graph, min_confidence=args.min_confidence)
        print(f"{len(edges)} inferred edges with confidence >= {args.min_confidence}:")
        for e in sorted(edges, key=lambda x: x.get("confidence", 0), reverse=True)[:args.top]:
            src = nmap.get(e["source"], {}).get("label", e["source"])
            tgt = nmap.get(e["target"], {}).get("label", e["target"])
            print(f"  {src} --[{e.get('label', '')} conf={e.get('confidence', 0):.2f}]--> {tgt}")

    elif args.export_context:
        print(export_context_block(graph))

    else:
        print_summary(graph)


if __name__ == "__main__":
    main()


path: references/api_reference.md
content: # Graphify API Reference

Complete reference for the `graphify` CLI (package `graphify`, v0.4.23+).

---

## Command Overview

```
graphify <target> [options]       Build or update a knowledge graph
graphify query "<question>"       Semantic search on the graph
graphify path "NodeA" "NodeB"     Shortest path between two nodes
graphify explain "NodeName"       Plain-language node explanation
graphify add <url>                Add external content to the graph
graphify <platform> install       Register with an AI assistant platform
graphify hook install             Install git post-commit rebuild hook
```

---

## Build Commands

### `graphify <target>`

Build a knowledge graph from a directory.

```bash
graphify .                    # current directory
graphify ./src                # specific subdirectory
graphify /absolute/path       # absolute path
```

**Flags:**

| Flag | Description |
|------|-------------|
| `--mode deep` | Aggressive INFERRED edge extraction with confidence scores |
| `--update` | Reprocess only files changed since last run (uses SHA-256 cache) |
| `--directed` | Preserve edge directionality in the output graph |
| `--cluster-only` | Re-run Leiden community detection without re-extracting nodes/edges |
| `--no-viz` | Skip generating `graph.html` (faster; useful in CI) |
| `--watch` | Auto-sync graph as files change (requires `graphify[watch]`) |
| `--wiki` | Generate Wikipedia-style article per node |
| `--svg` | Export SVG visualization (requires `graphify[svg]`) |
| `--graphml` | Export GraphML file for Gephi / yEd |

**Extraction pipeline (in order):**
1. **AST extraction** — tree-sitter deterministic parsing; no LLM calls; produces `EXTRACTED` edges
2. **Transcription** — faster-whisper local audio/video processing; domain-aware prompts
3. **Semantic extraction** — parallel LLM subagents for docs, PDFs, images; produces `EXTRACTED` and `INFERRED` edges

---

## Query Commands

### `graphify query "<question>"`

Semantic natural-language search across the knowledge graph.

```bash
graphify query "where is rate limiting enforced?"
graphify query "what calls the payment processor?" --dfs
```

| Flag | Description |
|------|-------------|
| `--dfs` | Depth-first traversal; traces a specific execution path instead of ranking |

**Returns:** Ranked list of matching nodes with relationship context and source file references.

---

### `graphify path "NodeA" "NodeB"`

Find the shortest path between two named nodes in the graph.

```bash
graphify path "AuthMiddleware" "PostgresAdapter"
graphify path "UserController" "EmailService"
```

**Use for:** Impact analysis before editing a module, tracing coupling chains, verifying isolation.

---

### `graphify explain "NodeName"`

Generate a plain-language explanation of a node and its relationships.

```bash
graphify explain "UserSessionManager"
graphify explain "RateLimiter"
```

**Returns:** What the node does, what it connects to, and which community it belongs to.

---

## Content Addition

### `graphify add <url>`

Fetch and index external content into the existing graph.

```bash
graphify add https://arxiv.org/abs/2305.12345     # research paper
graphify add https://x.com/user/status/123456     # tweet
graphify add https://youtube.com/watch?v=abc123   # video (transcribed locally)
```

After adding, run `graphify . --update` to integrate new nodes into the graph.

---

## Platform Installation

### `graphify <platform> install`

Registers graphify with an AI assistant platform by writing steering files and (where supported) hooks.

| Command | Platform | Mechanism |
|---------|----------|-----------|
| `graphify claw install` | OpenClaw | Copies skill + writes `AGENTS.md` |
| `graphify claude install` | Claude Code | Writes `CLAUDE.md` entry + PreToolUse hook |
| `graphify codex install` | OpenAI Codex | Writes `AGENTS.md` |
| `graphify cursor install` | Cursor | Writes `.cursorrules` |
| `graphify copilot install` | GitHub Copilot CLI | Writes steering config |
| `graphify gemini install` | Gemini CLI | Writes steering config |
| `graphify aider install` | Aider | Writes `.aider.conf.yml` entry |

Run from the **project root**. Re-run if you move the project or reinstall the assistant.

---

## Git Hook

### `graphify hook install`

Installs a git `post-commit` hook **locally** in the current project's `.git/hooks/` directory. Runs `graphify . --update` automatically after every commit. No global or system-level changes are made.

```bash
graphify hook install
```

This keeps the graph current without manual intervention. No LLM calls are made for files that haven't changed.

---

## Output Files

All output lands in `graphify-out/` in the target directory.

| File | Format | Description |
|------|--------|-------------|
| `GRAPH_REPORT.md` | Markdown | **Primary AI context file.** God nodes, communities, surprising connections, suggested questions. Read before grepping. |
| `graph.html` | HTML | Interactive vis.js visualization. Open in browser. |
| `graph.json` | JSON | Raw graph data for programmatic querying. |
| `cache/` | Directory | SHA-256 incremental cache. **Do not commit this directory.** |
| `graph.svg` | SVG | Static visualization (only with `--svg`) |
| `graph.graphml` | GraphML | Gephi/yEd export (only with `--graphml`) |

### `graph.json` schema

```json
{
  "nodes": [
    {
      "id": "string",
      "label": "string",
      "type": "module|class|function|concept|document|media",
      "file": "relative/path/to/source.py",
      "line": 42,
      "community": 3,
      "degree": 17,
      "summary": "One-line description"
    }
  ],
  "edges": [
    {
      "source": "NodeId",
      "target": "NodeId",
      "relation": "EXTRACTED|INFERRED|AMBIGUOUS",
      "confidence": 0.85,
      "label": "calls|imports|implements|references|extends"
    }
  ],
  "communities": [
    {
      "id": 3,
      "label": "Authentication Layer",
      "members": ["NodeId1", "NodeId2"],
      "god_nodes": ["NodeId1"]
    }
  ],
  "metadata": {
    "graphify_version": "0.4.23",
    "generated_at": "2026-04-19T10:00:00Z",
    "target_dir": ".",
    "total_nodes": 248,
    "total_edges": 891
  }
}
```

---

## Relationship Tags

| Tag | Source | Meaning |
|-----|--------|---------|
| `EXTRACTED` | AST / explicit reference | Found directly in source code |
| `INFERRED` | LLM semantic analysis | Reasonable inference; has `confidence` (0.0–1.0) |
| `AMBIGUOUS` | LLM with low confidence | Flagged for manual review |

**Edge labels (non-exhaustive):** `calls`, `imports`, `implements`, `extends`, `references`, `uses`, `defines`, `depends_on`, `documents`, `transcribed_from`

---

## .graphifyignore

Uses `.gitignore` syntax. Place in the project root.

```
# Dependencies
node_modules/
vendor/
.venv/
__pycache__/

# Build outputs
dist/
build/
.next/
out/
target/

# Generated files
*.min.js
*.min.css
*.generated.*
*.lock
*.sum

# Test fixtures / snapshots
__snapshots__/
testdata/

# Graphify cache
graphify-out/cache/
```

See `templates/graphifyignore.txt` for a comprehensive starter.

---

## Supported File Types

### Code (AST extraction via tree-sitter)

Python, JavaScript, TypeScript, Go, Rust, Java, C, C++, Ruby, C#, Kotlin, Scala, PHP, Swift, Lua, Zig, PowerShell, Elixir, Objective-C, Julia, Verilog, SystemVerilog, Vue, Svelte, Dart

### Documents (LLM semantic extraction)

Markdown (`.md`, `.mdx`), HTML, plain text (`.txt`), RST, PDF (requires `graphify[pdf]`)

### Images (Claude vision)

`.png`, `.jpg`, `.jpeg`, `.webp`, `.gif`

### Video / Audio (local Whisper transcription; requires `graphify[video]`)

`.mp4`, `.mov`, `.mkv`, `.webm`, `.mp3`, `.wav`, `.m4a`, `.ogg`

---

## Python Requirements

- Python 3.10 or higher
- For video/audio: significant CPU/GPU resources; Whisper model weights (~150 MB–1.5 GB) downloaded on first run

---

## Common Issues

| Symptom | Cause | Fix |
|---------|-------|-----|
| `command not found: graphify` | pip bin not on PATH | Add `$(python -m site --user-base)/bin` to PATH |
| Nodes missing for a language | tree-sitter grammar not installed | Run `pip install "graphify" --upgrade` |
| Stale graph after edits | Graph not rebuilt | Run `graphify . --update` |
| `AMBIGUOUS` edges dominating | `--mode deep` too aggressive | Remove `--mode deep` or filter `confidence > 0.7` |
| Video transcription very slow | Running on CPU only | Install CUDA-enabled torch for GPU acceleration |
| `AGENTS.md` not respected by OpenClaw | Wrong working directory | Re-run `graphify claw install` from the project root |
| PDF nodes empty | Missing pdf extra | `pip install "graphify[pdf]"` |


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)