Skip to content
Back to skills

Graphify

BSecurity

Build and query knowledge graphs from any folder (code + docs + PDFs + images). MCP-backed. Activates on /graphify and on natural-language requests to graph a project.

  • 25 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 18, 2026
ai-agentspythonrustgojavabashsqlnodetestinggitbackend

Works with

  • cli
  • mcp

Security analysis

B75/100
  • criticalAccesses sensitive system or user directories

Pro scans all 5 files and shows the line behind each finding

Scanned October 18, 2026

npx -y skills add nikolanovoselec/codeflare --skill graphify --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Graphify?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Graphify
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/nikolanovoselec-graphify/badge)](https://www.skillsdirectory.com/skills/nikolanovoselec-graphify)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: graphify
description: Build and query knowledge graphs from any folder (code + docs + PDFs + images). MCP-backed. Activates on /graphify and on natural-language requests to graph a project.
trigger: /graphify
---

# /graphify (Codeflare-managed skill)

This skill drives `/graphify` knowledge-graph extraction inside the Codeflare container. The `graphifyy` Python tool is pre-installed at build time (`uv tool install graphifyy[mcp,sql,pdf]`); the `graphify` CLI is on PATH at `/root/.local/bin/graphify` and the MCP server is pre-registered in `~/.claude.json` as `/root/.local/share/uv/tools/graphifyy/bin/python -m graphify.serve`. You do not need to install or wire anything.

## Graphify 0.9.73/0.9.74 compatibility

The image and managed update wrappers set `GRAPHIFY_NO_AUTO_REFRESH=1`: signed curation, not an upstream CLI refresh, owns these skill files. Do not unset the guard to resolve a version warning.

Verify the installed CLI version independently of plugin metadata. The 0.9.73 enum `case_of` edges, inherited Java-call resolution and improved Astro parsing are additive extraction changes; preserve the official graph schema and existing consent requirements. A warning about the 20,000-character semantic file cap means incomplete coverage: read the remaining source in bounded chunks rather than treating the warning as successful full-file analysis. If `graphify hook status` reports older hooks, do not overwrite Codeflare-managed hooks with an upstream installer without an explicitly requested source change.

Graphify 0.9.74 prefers authored workspace sources, filters Rust builtins and reclassifies Rust constructor edges as `references` with `context: "constructor"`. Callflow, hubs and counts can change on an explicitly authorized refresh; this is not permission to rebuild, normalize graphs or replace managed skills.

## Graphify 0.9.92 compatibility

The [reviewed release](https://github.com/Graphify-Labs/graphify/blob/86c57be8204673c2a1b9849a3292a7dd3ab373d8/CHANGELOG.md) changes stable IDs and grammar-versioned AST caches; an authorized update may re-extract code and change counts or callflow. Preserve optional `schema_version`/`graphify_version` metadata and parallel relations; do not normalize existing graphs. Qualify ambiguous path endpoints rather than guessing. `.graphify_labels.pending.json` is a regenerable worklist, not the curated labels file. Upstream hook installation now adds post-merge/post-rewrite rebuilds: do not install these hooks or refresh graphs without explicit authorization. Retain `GRAPHIFY_NO_AUTO_REFRESH=1`, local optional labels and the external-provider prohibition; no extra Pi guard or backend setting is required.

## Quick Reference

**Python interpreter:** All `python3` code blocks in this skill must use graphify's own interpreter: `/root/.local/share/uv/tools/graphifyy/bin/python`. System `python3` cannot import graphify. The CLI at `/root/.local/bin/graphify` is on PATH.

### Recipe 1: AST-only update (free, no tokens)
```
bash /home/user/.claude/plugins/graphify/scripts/safe-graphify-update.sh .
```
Re-extracts code structure only. Use after source code changes. Memory-safe (OOM-guarded).

### Recipe 2: Full semantic update (existing repo)
1. Detect files (Step 1 below)
2. Check semantic cache (Step B0) - note cached vs uncached counts
3. Present AskUserQuestion: AST-only vs Full (Note 8). Include subagent count: `ceil(uncached_doc_paper_files / 22) + uncached_image_count` (images get own chunk)
4. If Full chosen: start AST (Part A) in background, dispatch first semantic wave in parallel
5. Split uncached non-code files into chunks of 22 (images get own chunk)
6. Dispatch waves of at most 10 Sonnet subagents (Note 9: `model: "sonnet"`). All agents in one wave go in a SINGLE message. Wait for wave completion before next wave.
7. Collect results, save to cache, merge cached + new (Step B3)
8. Merge AST + semantic (Part C)
9. Build, cluster, and optionally label communities (Steps 3-4)
10. Generate HTML + Obsidian vault (Step 5)
11. Benchmark + manifest + cleanup (Steps 6-7)
12. Commit `graphify-out/graph.json`, `GRAPH_REPORT.md`, `graph.html` + merge into global graph (Note 3)

**If interrupted mid-wave:** re-run from step 2. Cache (Step B0) skips already-extracted files. Only uncached files get re-dispatched.

### Recipe 3: First-time build on new repo
Follow the full canonical protocol below (Steps 1-7). Note 8 fires to ask build mode. After build, set up `.gitignore` and `.gitattributes` per Note 3.

### Recipe 4: Re-cluster existing graph
```
graphify cluster-only .
```
Reruns community detection on existing `graph.json`. No extraction, no tokens.

### Recipe 5: Optionally name/relabel communities in-session (NO backend)
Community labels are optional and never block graph publication. **NEVER run `graphify label` and NEVER pass `--backend`.** That command calls an external LLM provider and re-clusters, which renumbers communities and wipes existing labels. When the user requests labels, community naming is done by THIS session reading the member nodes:

1. **Prepare** a worklist from the graph's existing community assignments (no recluster, no LLM):
   ```
   bash /home/user/.claude/plugins/graphify/scripts/local-graphify-labels.sh prepare .
   ```
   Writes `graphify-out/.graphify_community_label_worklist.json` and `graphify-out/.graphify_community_label_batches/batch_*.md` (40 communities/batch), each community listed with its top member node labels + source files.
2. **Name** every community in `graphify-out/.graphify_labels.json` as `{"<id>":"<Name>"}`. Infer each name from that community's top nodes/sources. For a large graph, fan out one subagent per `batch_*.md` (Agent tool, `run_in_background: true`) and merge their `{id:name}` maps. Rules: unique, specific, 2-6 words, Title Case; NO placeholders (`Community 12`), NO numeric suffixes (`Auth 2`) - qualify by source/domain instead (`Vault Crypto`, `Vault Proxy Routing`). Every current community id must be present.
3. **Apply** (validates uniqueness, regenerates `GRAPH_REPORT.md` + `graph.html` with `community_labels` + `callflow.html` from existing communities - no recluster, no backend):
   ```
   bash /home/user/.claude/plugins/graphify/scripts/local-graphify-labels.sh apply .
   ```
   The labeled `graph.html` is where names appear in the viz. If apply reports `duplicate_exact` / `numbered_duplicate` / `duplicate_base` / `placeholder`, fix those ids in `.graphify_labels.json` and re-run apply.
4. **Merge into the global graph** (so cross-repo MCP queries see this repo's nodes/edges), then commit:
   ```
   flock -w 5 /run/codeflare/locks/graphify-global.lock graphify global add graphify-out/graph.json --as "$(basename "$PWD")"
   ```
   `global add` is hash-keyed on node/edge content, so it no-ops when only labels changed - that is expected, not a failure. Community **names** live in `graphify-out/.graphify_labels.json` and the regenerated `graph.html`, NOT in the global graph: `graph_stats` reporting `Communities: 0` for the global graph is its normal state and is not "fixed" by labeling. Commit `graph.json`, `GRAPH_REPORT.md`, `graph.html`, and `callflow.html`; include `.graphify_labels.json` only when optional labeling was requested.

When labeling is skipped, do not run prepare/apply. Keep Graphify's official report/html, remove `.graphify_labels.json` from the committed update, and generate `callflow.html` directly with `graphify export callflow-html --graph graphify-out/graph.json --output graphify-out/callflow.html`.

## Codeflare-specific operational notes

1. **MCP query tools are always available.** Even before any graph is built, you can call `mcp__graphify__query_graph`, `mcp__graphify__get_node`, `mcp__graphify__get_neighbors`, and `mcp__graphify__shortest_path`. They return useful errors when no graph is present. After a build, point them at `graphify-out/graph.json` in the current cwd.

2. **Use only local or in-session Graphify workflows; never run `graphify label`.** Do not pass `--backend` to any command. Provider-backed labeling also re-clusters, which renumbers communities and discards existing labels. Semantic extraction uses in-session Claude subagents. Optional community naming uses the in-session `local-graphify-labels.sh` flow (Recipe 5); unlabeled graphs keep official Graphify report/html output.

3. **Persistence lives in git, not R2.** The graph travels with the repo. After your first `/graphify` build in a repo the user has push permission to:
   - Add to the repo's `.gitignore` (create if absent):
     ```
     graphify-out/cache/
     graphify-out/.cache/
     graphify-out/.chunks/
     graphify-out/manifest.json
     graphify-out/obsidian/
     .graphify_ast.json
     .graphify_semantic.json
     .graphify_semantic_new.json
     .graphify_extract.json
     .graphify_detect.json
     .graphify_analysis.json
     .graphify_cached.json
     .graphify_uncached.txt
     .graphify_chunk_*.txt
     .graphify_old.json
     .graphify_labels.pending.json
     .graphify_community_label_worklist.json
     .graphify_community_label_batches/
     .graphify_root
     /.graphify_labels.json
     ```
     All patterns are regenerable; only `graph.json`, `GRAPH_REPORT.md`, `graph.html`, and `callflow.html` are always committed (plus optional `wiki/`). `.graphify_labels.json` is committed only when optional community labeling was requested. The leading `/` on `/.graphify_labels.json` ignores only a stray root-level marker. The `local-graphify-labels.sh prepare` worklist + batches are working intermediates - never commit them.
   - Add to the repo's `.gitattributes` (create if absent):
     ```
     graphify-out/graph.json merge=graphify
     ```
     This wires the graphify semantic merge driver for `graph.json`. The driver itself is registered globally in the container image, so this `.gitattributes` line is the only per-repo setup needed. Without it, concurrent edits produce corrupt JSON on merge.
   - Stage and commit `graphify-out/graph.json`, `GRAPH_REPORT.md`, `graph.html`, `callflow.html`, optional `.graphify_labels.json` when labeling was requested, and optional `wiki/`.
   - For repos the user does NOT have push permission to (cloned open-source projects, read-only forks): graphify-out/ stays in the working tree only, ephemeral, no R2 fallback. Do not try to persist via bisync.
   - **Before the commit step, merge this repo's graph into the unified global graph** so `mcp__graphify__*` tool calls see it alongside the vault and any other active repos: `flock -w 5 /run/codeflare/locks/graphify-global.lock graphify global add graphify-out/graph.json --as <repo-basename>`. Hash-keyed and idempotent. The `flock -w 5` serialises against the capture agent and the vault-extract agent; the 5s timeout prevents a wedged writer from blocking the queue.

4. **Bias toward `--update` and `cluster-only` for repeat runs.** Full LLM extraction is expensive. After the first build:
 - For source changes: `bash /home/user/.claude/plugins/graphify/scripts/safe-graphify-update.sh .` (AST-only, free, no token cost; wraps `graphify update` with `GRAPHIFY_MAX_WORKERS=1` + `ulimit -v 1500000` so a runaway rebuild on a large repo cannot OOM-kill the codeflare session).
 - For repos larger than 2000 files: `graphify cluster-only . --no-viz` (AST-only first build).

5. **Context boundedness.** Graphify's own subagent-chunking model bounds the main session context, so extraction works without context-mode or `ctx_*` tools. No per-tier branching is needed in this skill.

6. **AskUserQuestion on clone - never auto-update.** A PostToolUse hook (`graphify-clone-prompt.sh`) injects a directive after `git clone` / `gh repo clone`. At clone time, if no graph exists, ask the user which graph action they want before running any build: **AST-only**, **Full semantic**, or **No graph action**. If an existing graph is stale/unknown, ask before running any update and offer **Use existing graph as-is**, **AST-only update**, or **Full semantic refresh**. If an existing graph is fresh, only print an informational note and use it. A clone-time AST-only choice is a final build-mode choice for this skill after detection. A clone-time Full semantic choice is intent only: after detection, show the actual uncached file/subagent counts and ask for confirmation before dispatching semantic subagents. Respect a NO without arguing.

7. **Discipline rule.** When `graphify-out/graph.json` exists, the "Graph first" section of `~/.claude/rules/engineering-constitution.md` applies: prefer focused MCP queries over Grep for architecture, dependency, and call-flow questions.

8. **Mandatory build-mode choice before any extraction.** Before dispatching Part B subagents (Step B2 of the upstream protocol), ALWAYS present the user with an `AskUserQuestion` offering exactly two modes unless the user already chose AST-only in the current clone-time triage prompt. If the clone-time triage captured Full semantic intent, do not dispatch semantic subagents yet; after detection, present a confirmation with the same Full-mode cost/count details below:
   - **AST-only** - free, no token cost; code structure + call/import/contains edges only; no semantic concepts from docs / papers / images.
   - **Full (AST + semantic)** - AST plus N parallel Sonnet subagents extracting concepts from docs / papers / images. Include the actual subagent count (`ceil(uncached_doc_paper_files / 22) + uncached_image_count`) and a wall-time estimate (~45s per parallel batch).

   Choose by intent, not size. **AST-only** when testing the pipeline, exploring for a one-off question, or cost-capping. **Full** when this is a long-term project and the user wants semantic concepts from docs/images in MCP queries.

   Skip the mode question only when (a) the corpus has zero docs / papers / images (code-only fast path makes the choice moot), (b) `--no-semantic` was passed explicitly, or (c) the user already chose AST-only in the current clone-time triage prompt. If AST-only is chosen, skip Part B entirely and treat AST as the full extraction (same flow as the code-only fast path). If Full semantic was chosen at clone time, ask only the post-detection cost/count confirmation; a Yes proceeds to Part B, and a No falls back to AST-only/no graph per the user's answer.

   This choice is separate from the "split by subfolder" question the upstream protocol asks on > 200 files - ask both in sequence (subfolder first, then build mode against the chosen scope).

9. **Spawn Part B semantic subagents with `model: "sonnet"`.** Graphify semantic extraction requires reliable schema compliance - each subagent must emit valid JSON with correct `id`, `source_file`, and `confidence_score` fields. Haiku produced 57% malformed nodes on the codeflare corpus (288/504 dropped during post-filter); Sonnet's structured-output fidelity eliminates this waste. The `Task` calls in Step B2 must include `model: "sonnet"`. Never escalate to Opus from this skill.

10. **The on-disk edge key is `links`, not `edges`.** Every graph graphify writes - `graphify-out/graph.json`, `~/Vault/graphify-out/vault-graph.json`, and `~/.graphify/global-graph.json` - is NetworkX node-link JSON in this shape:

    ```json
    {"directed": true, "multigraph": false, "graph": {},
     "nodes": [{"id": "...", "type": "...", "repo": "<tag>", "...": "..."}],
     "links": [{"source": "<node id>", "target": "<node id>", "type": "..."}]}
    ```

    A script that reads `data["edges"]` gets nothing and reports an edgeless graph, which is a lookup bug and never a real finding. NetworkX 3.6 flipped the default key from `links` to `edges`, so anything calling `node_link_data` / `node_link_graph` directly must pass `edges="links"` to stay compatible with what is already on disk; graphify pins that explicitly. Nodes in the global graph carry a `repo` attribute holding the tag they were added under (`user_vault`, `codeflare`, ...), and that attribute is what `graphify global remove <tag>` prunes on. Prefer the `mcp__graphify__*` tools over parsing these files by hand; they answer against the global graph and cannot get the key wrong.

---

The upstream graphify extraction pipeline is reproduced below in full. It is the canonical algorithm; do not improvise on it. The two operational notes above (#8 mandatory build-mode question, #9 Sonnet subagents) are codeflare-specific overrides that bind on top of the upstream Step 1 + Step B2 below - apply them even where the upstream text does not mention them.

---

graphify turns any folder of files into a navigable knowledge graph with community detection, an honest audit trail, and three outputs: interactive HTML, GraphRAG-ready JSON, and a plain-language GRAPH_REPORT.md.

## What You Must Do When Invoked

If no path was given, use `.` (current directory). Do not ask the user for a path.

Follow these steps in order. Do not skip steps.

### Step 1 - Detect files

```bash
/root/.local/share/uv/tools/graphifyy/bin/python -c "
import json
from graphify.detect import detect
from pathlib import Path
result = detect(Path('INPUT_PATH'))
print(json.dumps(result))
" > .graphify_detect.json
```

Replace INPUT_PATH with the actual path the user provided. Do NOT cat or print the JSON - read it silently and present a clean summary instead:

```
Corpus: X files ~ ~Y words
 code: N files (.py .ts .go ...)
 document: N files (.md .txt ...)
 paper: N files (.pdf ...)
 image: N files
```

Then act on it:
- If `total_files` is 0: stop with "No supported files found in [path]."
- If `skipped_sensitive` is non-empty: mention file count skipped, not the file names.
- If `total_words` > 2,000,000 OR `total_files` > 200: show the warning and the top 5 subdirectories by file count, then ask which subfolder to run on. Wait for the user's answer before proceeding.
- Otherwise: proceed directly to Step 2 - no need to ask anything.

### Step 2 - Extract entities and relationships

This step runs structural (AST, free) and semantic (Claude subagents, costs tokens) extraction in parallel, then merges them. The two codeflare overrides bind here: the mandatory build-mode question (operational note 8) before Part B, and `model: "sonnet"` subagents (operational note 9). For the full Part A / Part B / Part C pipeline, the wave structure, the exact subagent prompt, and the extraction JSON schema, see `references/extraction-spec.md`.

### Steps 3-7 - Build, cluster, label, visualize, report

After extraction produces `.graphify_extract.json`, build the graph, cluster it, optionally label communities, generate the Obsidian vault + HTML + callflow, benchmark (only if `total_words > 5000`), save the manifest, clean up, and report. See `references/build.md` for all five steps.

## Incremental Updates and --cluster-only

For repeat runs, bias toward `--update` (re-extracts only new/changed files) and `cluster-only` (reruns clustering on the existing graph) over a full LLM rebuild (operational note 4). The cheap AST-only refresh is `bash /home/user/.claude/plugins/graphify/scripts/safe-graphify-update.sh .` (wraps `graphify update`). For the full `--update` procedure (code-only fast path, merge, graph diff) and the `--cluster-only` flow, see `references/update.md`.

## Query, Path, Explain

For graph queries, prefer the MCP tools directly (`mcp__graphify__query_graph`, `mcp__graphify__get_node`, `mcp__graphify__get_neighbors`, `mcp__graphify__shortest_path`, `mcp__graphify__get_community`, `mcp__graphify__god_nodes`) - always available when a graph exists. After answering a query, path, or explain, persist the Q&A back into the graph with `graphify save-result` so the next update extracts it as a node. For the save-result feedback loop (`--type query` / `path_query` / `explain`), the CLI/NetworkX fallback, and the `/graphify add`, `--watch`, and export flows, see `references/query.md`.


## Honesty Rules

- Never invent an edge. If unsure, use AMBIGUOUS.
- Never skip the corpus check warning.
- Always show token cost in the report.
- Never hide cohesion scores behind symbols - show the raw number.
- Never skip HTML viz. Codeflare sets `GRAPHIFY_VIZ_NODE_LIMIT=100000` globally in entrypoint.sh; if a build ever logs `Skipped graph.html`, re-export the limit and rebuild before reporting done.

Files in this skill

  • SKILL.md19.9 KB
  • references/build.md10.2 KB
  • references/extraction-spec.md14 KB
  • references/query.md3.8 KB
  • references/update.md5.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.