graphifyy 0.3.0__tar.gz → 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. {graphifyy-0.3.0 → graphifyy-0.3.2}/PKG-INFO +65 -19
  2. {graphifyy-0.3.0 → graphifyy-0.3.2}/README.md +60 -18
  3. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/__main__.py +93 -6
  4. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/analyze.py +9 -5
  5. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/cache.py +15 -3
  6. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/detect.py +64 -2
  7. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/export.py +22 -13
  8. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/extract.py +463 -5
  9. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/hooks.py +25 -16
  10. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/ingest.py +8 -5
  11. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/security.py +34 -2
  12. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/serve.py +2 -2
  13. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/skill.md +1 -1
  14. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/watch.py +3 -4
  15. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphifyy.egg-info/PKG-INFO +65 -19
  16. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphifyy.egg-info/requires.txt +4 -0
  17. {graphifyy-0.3.0 → graphifyy-0.3.2}/pyproject.toml +5 -1
  18. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_detect.py +35 -1
  19. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_extract.py +2 -1
  20. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_install.py +74 -0
  21. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_languages.py +121 -1
  22. {graphifyy-0.3.0 → graphifyy-0.3.2}/LICENSE +0 -0
  23. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/__init__.py +0 -0
  24. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/benchmark.py +0 -0
  25. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/build.py +0 -0
  26. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/cluster.py +0 -0
  27. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/manifest.py +0 -0
  28. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/report.py +0 -0
  29. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/skill-claw.md +0 -0
  30. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/skill-codex.md +0 -0
  31. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/skill-opencode.md +0 -0
  32. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/validate.py +0 -0
  33. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphify/wiki.py +0 -0
  34. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphifyy.egg-info/SOURCES.txt +0 -0
  35. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphifyy.egg-info/dependency_links.txt +0 -0
  36. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphifyy.egg-info/entry_points.txt +0 -0
  37. {graphifyy-0.3.0 → graphifyy-0.3.2}/graphifyy.egg-info/top_level.txt +0 -0
  38. {graphifyy-0.3.0 → graphifyy-0.3.2}/setup.cfg +0 -0
  39. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_analyze.py +0 -0
  40. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_benchmark.py +0 -0
  41. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_build.py +0 -0
  42. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_cache.py +0 -0
  43. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_claude_md.py +0 -0
  44. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_cluster.py +0 -0
  45. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_confidence.py +0 -0
  46. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_export.py +0 -0
  47. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_hooks.py +0 -0
  48. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_hypergraph.py +0 -0
  49. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_ingest.py +0 -0
  50. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_multilang.py +0 -0
  51. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_pipeline.py +0 -0
  52. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_rationale.py +0 -0
  53. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_report.py +0 -0
  54. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_security.py +0 -0
  55. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_semantic_similarity.py +0 -0
  56. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_serve.py +0 -0
  57. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_validate.py +0 -0
  58. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_watch.py +0 -0
  59. {graphifyy-0.3.0 → graphifyy-0.3.2}/tests/test_wiki.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graphifyy
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: AI coding assistant skill (Claude Code, Codex, OpenCode, OpenClaw) - turn any folder of code, docs, papers, or images into a queryable knowledge graph
5
5
  License: MIT License
6
6
 
@@ -46,6 +46,10 @@ Requires-Dist: tree-sitter-c-sharp
46
46
  Requires-Dist: tree-sitter-kotlin
47
47
  Requires-Dist: tree-sitter-scala
48
48
  Requires-Dist: tree-sitter-php
49
+ Requires-Dist: tree-sitter-swift
50
+ Requires-Dist: tree-sitter-lua
51
+ Requires-Dist: tree-sitter-zig
52
+ Requires-Dist: tree-sitter-powershell
49
53
  Provides-Extra: mcp
50
54
  Requires-Dist: mcp; extra == "mcp"
51
55
  Provides-Extra: neo4j
@@ -68,10 +72,12 @@ Dynamic: license-file
68
72
 
69
73
  # graphify
70
74
 
75
+ [English](README.md) | [简体中文](README.zh-CN.md)
76
+
71
77
  [![CI](https://github.com/safishamsi/graphify/actions/workflows/ci.yml/badge.svg?branch=v3)](https://github.com/safishamsi/graphify/actions/workflows/ci.yml)
72
78
  [![PyPI](https://img.shields.io/pypi/v/graphifyy)](https://pypi.org/project/graphifyy/)
73
79
 
74
- **An AI coding assistant skill.** Type `/graphify` in Claude Code, Codex, OpenCode, or OpenClaw - 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.
80
+ **An AI coding assistant skill.** Type `/graphify` in Claude Code, Codex, OpenCode, OpenClaw, or Factory Droid - 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.
75
81
 
76
82
  Fully multimodal. Drop in code, PDFs, markdown, screenshots, diagrams, whiteboard photos, even images in other languages - graphify uses Claude vision to extract concepts and relationships from all of it and connects them into one graph.
77
83
 
@@ -89,15 +95,29 @@ graphify-out/
89
95
  └── cache/ SHA256 cache - re-runs only process changed files
90
96
  ```
91
97
 
98
+ Add a `.graphifyignore` file to exclude folders you don't want in the graph:
99
+
100
+ ```
101
+ # .graphifyignore
102
+ vendor/
103
+ node_modules/
104
+ dist/
105
+ *.generated.py
106
+ ```
107
+
108
+ Same syntax as `.gitignore`. Patterns match against file paths relative to the folder you run graphify on.
109
+
92
110
  ## How it works
93
111
 
94
112
  graphify runs in two passes. First, a deterministic AST pass extracts structure from code files (classes, functions, imports, call graphs, docstrings, rationale comments) with no LLM needed. Second, Claude subagents run in parallel over docs, papers, and images to extract concepts, relationships, and design rationale. The results are merged into a NetworkX graph, clustered with Leiden community detection, and exported as interactive HTML, queryable JSON, and a plain-language audit report.
95
113
 
114
+ **Clustering is graph-topology-based — no embeddings.** Leiden finds communities by edge density. The semantic similarity edges that Claude extracts (`semantically_similar_to`, marked INFERRED) are already in the graph, so they influence community detection directly. The graph structure is the similarity signal — no separate embedding step or vector database needed.
115
+
96
116
  Every relationship is tagged `EXTRACTED` (found directly in source), `INFERRED` (reasonable inference, with a confidence score), or `AMBIGUOUS` (flagged for review). You always know what was found vs guessed.
97
117
 
98
118
  ## Install
99
119
 
100
- **Requires:** [Claude Code](https://claude.ai/code) and Python 3.10+
120
+ **Requires:** Python 3.10+ and one of: [Claude Code](https://claude.ai/code), [Codex](https://openai.com/codex), [OpenCode](https://opencode.ai), [OpenClaw](https://openclaw.ai), or [Factory Droid](https://factory.ai)
101
121
 
102
122
  ```bash
103
123
  pip install graphifyy && graphify install
@@ -113,8 +133,9 @@ pip install graphifyy && graphify install
113
133
  | Codex | `graphify install --platform codex` |
114
134
  | OpenCode | `graphify install --platform opencode` |
115
135
  | OpenClaw | `graphify install --platform claw` |
136
+ | Factory Droid | `graphify install --platform droid` |
116
137
 
117
- Codex users also need `multi_agent = true` under `[features]` in `~/.codex/config.toml` for parallel extraction. OpenClaw uses sequential extraction (parallel agent support is still early on that platform).
138
+ 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 uses sequential extraction (parallel agent support is still early on that platform).
118
139
 
119
140
  Then open your AI coding assistant and type:
120
141
 
@@ -122,23 +143,31 @@ Then open your AI coding assistant and type:
122
143
  /graphify .
123
144
  ```
124
145
 
125
- ### Make Claude always use the graph (recommended)
146
+ ### Make your assistant always use the graph (recommended)
126
147
 
127
148
  After building a graph, run this once in your project:
128
149
 
129
- ```bash
130
- graphify claude install
131
- ```
150
+ | Platform | Command |
151
+ |----------|---------|
152
+ | Claude Code | `graphify claude install` |
153
+ | Codex | `graphify codex install` |
154
+ | OpenCode | `graphify opencode install` |
155
+ | OpenClaw | `graphify claw install` |
156
+ | Factory Droid | `graphify droid install` |
157
+
158
+ **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.
159
+
160
+ **Codex, OpenCode, OpenClaw, Factory Droid** write the same rules to `AGENTS.md` in your project root. These platforms don't support PreToolUse hooks, so AGENTS.md is the always-on mechanism.
132
161
 
133
- This does two things:
162
+ Uninstall with the matching uninstall command (e.g. `graphify claude uninstall`).
134
163
 
135
- 1. **CLAUDE.md rules** - tells Claude to read `graphify-out/GRAPH_REPORT.md` before answering architecture questions, and to rebuild the graph after editing code files.
164
+ **Always-on vs explicit trigger what's the difference?**
136
165
 
137
- 2. **PreToolUse hook** (`settings.json`) - fires automatically 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."_ This means Claude navigates via the graph instead of grepping through every file - faster answers, fewer wasted tool calls, and responses grounded in the actual structure of your codebase rather than keyword matches.
166
+ The always-on hook surfaces `GRAPH_REPORT.md` a one-page summary of god nodes, communities, and surprising connections. Your assistant reads this before searching files, so it navigates by structure instead of keyword matching. That covers most everyday questions.
138
167
 
139
- Without this, Claude will grep raw files by default even when a graph exists. With it, the graph becomes the first thing Claude reaches for.
168
+ `/graphify query`, `/graphify path`, and `/graphify explain` go deeper: they traverse the raw `graph.json` hop by hop, trace exact paths between nodes, and surface edge-level detail (relation type, confidence score, source location). Use them when you want a specific question answered from the graph rather than a general orientation.
140
169
 
141
- Uninstall with `graphify claude uninstall`.
170
+ Think of it this way: the always-on hook gives your assistant a map. The `/graphify` commands let it navigate the map precisely.
142
171
 
143
172
  <details>
144
173
  <summary>Manual install (curl)</summary>
@@ -165,12 +194,18 @@ When the user types `/graphify`, invoke the Skill tool with `skill: "graphify"`
165
194
  /graphify ./raw # run on a specific folder
166
195
  /graphify ./raw --mode deep # more aggressive INFERRED edge extraction
167
196
  /graphify ./raw --update # re-extract only changed files, merge into existing graph
197
+ /graphify ./raw --cluster-only # rerun clustering on existing graph, no re-extraction
198
+ /graphify ./raw --no-viz # skip HTML, just produce report + JSON
168
199
  /graphify ./raw --obsidian # also generate Obsidian vault (opt-in)
169
200
 
170
201
  /graphify add https://arxiv.org/abs/1706.03762 # fetch a paper, save, update graph
171
202
  /graphify add https://x.com/karpathy/status/... # fetch a tweet
203
+ /graphify add https://... --author "Name" # tag the original author
204
+ /graphify add https://... --contributor "Name" # tag who added it to the corpus
172
205
 
173
206
  /graphify query "what connects attention to the optimizer?"
207
+ /graphify query "what connects attention to the optimizer?" --dfs # trace a specific path
208
+ /graphify query "what connects attention to the optimizer?" --budget 1500 # cap at N tokens
174
209
  /graphify path "DigestAuth" "Response"
175
210
  /graphify explain "SwinTransformer"
176
211
 
@@ -179,17 +214,28 @@ When the user types `/graphify`, invoke the Skill tool with `skill: "graphify"`
179
214
  /graphify ./raw --svg # export graph.svg
180
215
  /graphify ./raw --graphml # export graph.graphml (Gephi, yEd)
181
216
  /graphify ./raw --neo4j # generate cypher.txt for Neo4j
217
+ /graphify ./raw --neo4j-push bolt://localhost:7687 # push directly to a running Neo4j instance
182
218
  /graphify ./raw --mcp # start MCP stdio server
183
219
 
184
- graphify hook install # git hooks - rebuilds graph on commit and branch switch
185
- graphify claude install # always-on: CLAUDE.md + PreToolUse hook for this project
220
+ # git hooks - platform-agnostic, rebuild graph on commit and branch switch
221
+ graphify hook install
222
+ graphify hook uninstall
223
+ graphify hook status
224
+
225
+ # always-on assistant instructions - platform-specific
226
+ graphify claude install # CLAUDE.md + PreToolUse hook (Claude Code)
227
+ graphify claude uninstall
228
+ graphify codex install # AGENTS.md (Codex)
229
+ graphify opencode install # AGENTS.md (OpenCode)
230
+ graphify claw install # AGENTS.md (OpenClaw)
231
+ graphify droid install # AGENTS.md (Factory Droid)
186
232
  ```
187
233
 
188
234
  Works with any mix of file types:
189
235
 
190
236
  | Type | Extensions | Extraction |
191
237
  |------|-----------|------------|
192
- | Code | `.py .ts .js .go .rs .java .c .cpp .rb .cs .kt .scala .php` | AST via tree-sitter + call-graph + docstring/comment rationale |
238
+ | Code | `.py .ts .js .go .rs .java .c .cpp .rb .cs .kt .scala .php .swift .lua .zig .ps1` | AST via tree-sitter + call-graph + docstring/comment rationale |
193
239
  | Docs | `.md .txt .rst` | Concepts + relationships + design rationale via Claude |
194
240
  | Papers | `.pdf` | Citation mining + concept extraction |
195
241
  | Images | `.png .jpg .webp .gif` | Claude vision - screenshots, diagrams, any language |
@@ -214,7 +260,7 @@ Works with any mix of file types:
214
260
 
215
261
  **Auto-sync** (`--watch`) - run in a background terminal and the graph updates itself as your codebase changes. Code file saves trigger an instant rebuild (AST only, no LLM). Doc/image changes notify you to run `--update` for the LLM re-pass.
216
262
 
217
- **Git hooks** (`graphify hook install`) - installs post-commit and post-checkout hooks. Graph rebuilds automatically after every commit and every branch switch. No background process needed.
263
+ **Git hooks** (`graphify hook install`) - installs post-commit and post-checkout hooks. Graph rebuilds automatically after every commit and every branch switch. If a rebuild fails, the hook exits with a non-zero code so git surfaces the error instead of silently continuing. No background process needed.
218
264
 
219
265
  **Wiki** (`--wiki`) - Wikipedia-style markdown articles per community and god node, with an `index.md` entry point. Point any agent at `index.md` and it can navigate the knowledge base by reading files instead of parsing JSON.
220
266
 
@@ -230,11 +276,11 @@ Token reduction scales with corpus size. 6 files fits in a context window anyway
230
276
 
231
277
  ## Privacy
232
278
 
233
- graphify sends file contents to the Claude API (Anthropic) for semantic extraction of docs, papers, and images. Code files are processed locally via tree-sitter AST — no file contents leave your machine for code. No telemetry, usage tracking, or analytics of any kind. The only network calls are to Anthropic's API during extraction, using your own API key via Claude Code.
279
+ graphify sends file contents to your AI coding assistant's underlying model API for semantic extraction of docs, papers, and images — Anthropic (Claude Code), OpenAI (Codex), or whichever provider your platform uses. Code files are processed locally via tree-sitter AST — no file contents leave your machine for code. No telemetry, usage tracking, or analytics of any kind. The only network calls are to your platform's model API during extraction, using your own API key.
234
280
 
235
281
  ## Tech stack
236
282
 
237
- NetworkX + Leiden (graspologic) + tree-sitter + Claude + vis.js. No Neo4j required, no server, runs entirely locally.
283
+ NetworkX + Leiden (graspologic) + tree-sitter + vis.js. Semantic extraction via Claude (Claude Code), GPT-4 (Codex), or whichever model your platform runs. No Neo4j required, no server, runs entirely locally.
238
284
 
239
285
  <details>
240
286
  <summary>Contributing</summary>
@@ -1,9 +1,11 @@
1
1
  # graphify
2
2
 
3
+ [English](README.md) | [简体中文](README.zh-CN.md)
4
+
3
5
  [![CI](https://github.com/safishamsi/graphify/actions/workflows/ci.yml/badge.svg?branch=v3)](https://github.com/safishamsi/graphify/actions/workflows/ci.yml)
4
6
  [![PyPI](https://img.shields.io/pypi/v/graphifyy)](https://pypi.org/project/graphifyy/)
5
7
 
6
- **An AI coding assistant skill.** Type `/graphify` in Claude Code, Codex, OpenCode, or OpenClaw - 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.
8
+ **An AI coding assistant skill.** Type `/graphify` in Claude Code, Codex, OpenCode, OpenClaw, or Factory Droid - 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.
7
9
 
8
10
  Fully multimodal. Drop in code, PDFs, markdown, screenshots, diagrams, whiteboard photos, even images in other languages - graphify uses Claude vision to extract concepts and relationships from all of it and connects them into one graph.
9
11
 
@@ -21,15 +23,29 @@ graphify-out/
21
23
  └── cache/ SHA256 cache - re-runs only process changed files
22
24
  ```
23
25
 
26
+ Add a `.graphifyignore` file to exclude folders you don't want in the graph:
27
+
28
+ ```
29
+ # .graphifyignore
30
+ vendor/
31
+ node_modules/
32
+ dist/
33
+ *.generated.py
34
+ ```
35
+
36
+ Same syntax as `.gitignore`. Patterns match against file paths relative to the folder you run graphify on.
37
+
24
38
  ## How it works
25
39
 
26
40
  graphify runs in two passes. First, a deterministic AST pass extracts structure from code files (classes, functions, imports, call graphs, docstrings, rationale comments) with no LLM needed. Second, Claude subagents run in parallel over docs, papers, and images to extract concepts, relationships, and design rationale. The results are merged into a NetworkX graph, clustered with Leiden community detection, and exported as interactive HTML, queryable JSON, and a plain-language audit report.
27
41
 
42
+ **Clustering is graph-topology-based — no embeddings.** Leiden finds communities by edge density. The semantic similarity edges that Claude extracts (`semantically_similar_to`, marked INFERRED) are already in the graph, so they influence community detection directly. The graph structure is the similarity signal — no separate embedding step or vector database needed.
43
+
28
44
  Every relationship is tagged `EXTRACTED` (found directly in source), `INFERRED` (reasonable inference, with a confidence score), or `AMBIGUOUS` (flagged for review). You always know what was found vs guessed.
29
45
 
30
46
  ## Install
31
47
 
32
- **Requires:** [Claude Code](https://claude.ai/code) and Python 3.10+
48
+ **Requires:** Python 3.10+ and one of: [Claude Code](https://claude.ai/code), [Codex](https://openai.com/codex), [OpenCode](https://opencode.ai), [OpenClaw](https://openclaw.ai), or [Factory Droid](https://factory.ai)
33
49
 
34
50
  ```bash
35
51
  pip install graphifyy && graphify install
@@ -45,8 +61,9 @@ pip install graphifyy && graphify install
45
61
  | Codex | `graphify install --platform codex` |
46
62
  | OpenCode | `graphify install --platform opencode` |
47
63
  | OpenClaw | `graphify install --platform claw` |
64
+ | Factory Droid | `graphify install --platform droid` |
48
65
 
49
- Codex users also need `multi_agent = true` under `[features]` in `~/.codex/config.toml` for parallel extraction. OpenClaw uses sequential extraction (parallel agent support is still early on that platform).
66
+ 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 uses sequential extraction (parallel agent support is still early on that platform).
50
67
 
51
68
  Then open your AI coding assistant and type:
52
69
 
@@ -54,23 +71,31 @@ Then open your AI coding assistant and type:
54
71
  /graphify .
55
72
  ```
56
73
 
57
- ### Make Claude always use the graph (recommended)
74
+ ### Make your assistant always use the graph (recommended)
58
75
 
59
76
  After building a graph, run this once in your project:
60
77
 
61
- ```bash
62
- graphify claude install
63
- ```
78
+ | Platform | Command |
79
+ |----------|---------|
80
+ | Claude Code | `graphify claude install` |
81
+ | Codex | `graphify codex install` |
82
+ | OpenCode | `graphify opencode install` |
83
+ | OpenClaw | `graphify claw install` |
84
+ | Factory Droid | `graphify droid install` |
85
+
86
+ **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.
87
+
88
+ **Codex, OpenCode, OpenClaw, Factory Droid** write the same rules to `AGENTS.md` in your project root. These platforms don't support PreToolUse hooks, so AGENTS.md is the always-on mechanism.
64
89
 
65
- This does two things:
90
+ Uninstall with the matching uninstall command (e.g. `graphify claude uninstall`).
66
91
 
67
- 1. **CLAUDE.md rules** - tells Claude to read `graphify-out/GRAPH_REPORT.md` before answering architecture questions, and to rebuild the graph after editing code files.
92
+ **Always-on vs explicit trigger what's the difference?**
68
93
 
69
- 2. **PreToolUse hook** (`settings.json`) - fires automatically 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."_ This means Claude navigates via the graph instead of grepping through every file - faster answers, fewer wasted tool calls, and responses grounded in the actual structure of your codebase rather than keyword matches.
94
+ The always-on hook surfaces `GRAPH_REPORT.md` a one-page summary of god nodes, communities, and surprising connections. Your assistant reads this before searching files, so it navigates by structure instead of keyword matching. That covers most everyday questions.
70
95
 
71
- Without this, Claude will grep raw files by default even when a graph exists. With it, the graph becomes the first thing Claude reaches for.
96
+ `/graphify query`, `/graphify path`, and `/graphify explain` go deeper: they traverse the raw `graph.json` hop by hop, trace exact paths between nodes, and surface edge-level detail (relation type, confidence score, source location). Use them when you want a specific question answered from the graph rather than a general orientation.
72
97
 
73
- Uninstall with `graphify claude uninstall`.
98
+ Think of it this way: the always-on hook gives your assistant a map. The `/graphify` commands let it navigate the map precisely.
74
99
 
75
100
  <details>
76
101
  <summary>Manual install (curl)</summary>
@@ -97,12 +122,18 @@ When the user types `/graphify`, invoke the Skill tool with `skill: "graphify"`
97
122
  /graphify ./raw # run on a specific folder
98
123
  /graphify ./raw --mode deep # more aggressive INFERRED edge extraction
99
124
  /graphify ./raw --update # re-extract only changed files, merge into existing graph
125
+ /graphify ./raw --cluster-only # rerun clustering on existing graph, no re-extraction
126
+ /graphify ./raw --no-viz # skip HTML, just produce report + JSON
100
127
  /graphify ./raw --obsidian # also generate Obsidian vault (opt-in)
101
128
 
102
129
  /graphify add https://arxiv.org/abs/1706.03762 # fetch a paper, save, update graph
103
130
  /graphify add https://x.com/karpathy/status/... # fetch a tweet
131
+ /graphify add https://... --author "Name" # tag the original author
132
+ /graphify add https://... --contributor "Name" # tag who added it to the corpus
104
133
 
105
134
  /graphify query "what connects attention to the optimizer?"
135
+ /graphify query "what connects attention to the optimizer?" --dfs # trace a specific path
136
+ /graphify query "what connects attention to the optimizer?" --budget 1500 # cap at N tokens
106
137
  /graphify path "DigestAuth" "Response"
107
138
  /graphify explain "SwinTransformer"
108
139
 
@@ -111,17 +142,28 @@ When the user types `/graphify`, invoke the Skill tool with `skill: "graphify"`
111
142
  /graphify ./raw --svg # export graph.svg
112
143
  /graphify ./raw --graphml # export graph.graphml (Gephi, yEd)
113
144
  /graphify ./raw --neo4j # generate cypher.txt for Neo4j
145
+ /graphify ./raw --neo4j-push bolt://localhost:7687 # push directly to a running Neo4j instance
114
146
  /graphify ./raw --mcp # start MCP stdio server
115
147
 
116
- graphify hook install # git hooks - rebuilds graph on commit and branch switch
117
- graphify claude install # always-on: CLAUDE.md + PreToolUse hook for this project
148
+ # git hooks - platform-agnostic, rebuild graph on commit and branch switch
149
+ graphify hook install
150
+ graphify hook uninstall
151
+ graphify hook status
152
+
153
+ # always-on assistant instructions - platform-specific
154
+ graphify claude install # CLAUDE.md + PreToolUse hook (Claude Code)
155
+ graphify claude uninstall
156
+ graphify codex install # AGENTS.md (Codex)
157
+ graphify opencode install # AGENTS.md (OpenCode)
158
+ graphify claw install # AGENTS.md (OpenClaw)
159
+ graphify droid install # AGENTS.md (Factory Droid)
118
160
  ```
119
161
 
120
162
  Works with any mix of file types:
121
163
 
122
164
  | Type | Extensions | Extraction |
123
165
  |------|-----------|------------|
124
- | Code | `.py .ts .js .go .rs .java .c .cpp .rb .cs .kt .scala .php` | AST via tree-sitter + call-graph + docstring/comment rationale |
166
+ | Code | `.py .ts .js .go .rs .java .c .cpp .rb .cs .kt .scala .php .swift .lua .zig .ps1` | AST via tree-sitter + call-graph + docstring/comment rationale |
125
167
  | Docs | `.md .txt .rst` | Concepts + relationships + design rationale via Claude |
126
168
  | Papers | `.pdf` | Citation mining + concept extraction |
127
169
  | Images | `.png .jpg .webp .gif` | Claude vision - screenshots, diagrams, any language |
@@ -146,7 +188,7 @@ Works with any mix of file types:
146
188
 
147
189
  **Auto-sync** (`--watch`) - run in a background terminal and the graph updates itself as your codebase changes. Code file saves trigger an instant rebuild (AST only, no LLM). Doc/image changes notify you to run `--update` for the LLM re-pass.
148
190
 
149
- **Git hooks** (`graphify hook install`) - installs post-commit and post-checkout hooks. Graph rebuilds automatically after every commit and every branch switch. No background process needed.
191
+ **Git hooks** (`graphify hook install`) - installs post-commit and post-checkout hooks. Graph rebuilds automatically after every commit and every branch switch. If a rebuild fails, the hook exits with a non-zero code so git surfaces the error instead of silently continuing. No background process needed.
150
192
 
151
193
  **Wiki** (`--wiki`) - Wikipedia-style markdown articles per community and god node, with an `index.md` entry point. Point any agent at `index.md` and it can navigate the knowledge base by reading files instead of parsing JSON.
152
194
 
@@ -162,11 +204,11 @@ Token reduction scales with corpus size. 6 files fits in a context window anyway
162
204
 
163
205
  ## Privacy
164
206
 
165
- graphify sends file contents to the Claude API (Anthropic) for semantic extraction of docs, papers, and images. Code files are processed locally via tree-sitter AST — no file contents leave your machine for code. No telemetry, usage tracking, or analytics of any kind. The only network calls are to Anthropic's API during extraction, using your own API key via Claude Code.
207
+ graphify sends file contents to your AI coding assistant's underlying model API for semantic extraction of docs, papers, and images — Anthropic (Claude Code), OpenAI (Codex), or whichever provider your platform uses. Code files are processed locally via tree-sitter AST — no file contents leave your machine for code. No telemetry, usage tracking, or analytics of any kind. The only network calls are to your platform's model API during extraction, using your own API key.
166
208
 
167
209
  ## Tech stack
168
210
 
169
- NetworkX + Leiden (graspologic) + tree-sitter + Claude + vis.js. No Neo4j required, no server, runs entirely locally.
211
+ NetworkX + Leiden (graspologic) + tree-sitter + vis.js. Semantic extraction via Claude (Claude Code), GPT-4 (Codex), or whichever model your platform runs. No Neo4j required, no server, runs entirely locally.
170
212
 
171
213
  <details>
172
214
  <summary>Contributing</summary>
@@ -50,6 +50,11 @@ _PLATFORM_CONFIG: dict[str, dict] = {
50
50
  "skill_dst": Path(".claw") / "skills" / "graphify" / "SKILL.md",
51
51
  "claude_md": False,
52
52
  },
53
+ "droid": {
54
+ "skill_file": "skill-droid.md",
55
+ "skill_dst": Path(".factory") / "skills" / "graphify" / "SKILL.md",
56
+ "claude_md": False,
57
+ },
53
58
  }
54
59
 
55
60
 
@@ -107,6 +112,71 @@ Rules:
107
112
 
108
113
  _CLAUDE_MD_MARKER = "## graphify"
109
114
 
115
+ # AGENTS.md section for Codex, OpenCode, and OpenClaw.
116
+ # All three platforms read AGENTS.md in the project root for persistent instructions.
117
+ _AGENTS_MD_SECTION = """\
118
+ ## graphify
119
+
120
+ This project has a graphify knowledge graph at graphify-out/.
121
+
122
+ Rules:
123
+ - Before answering architecture or codebase questions, read graphify-out/GRAPH_REPORT.md for god nodes and community structure
124
+ - If graphify-out/wiki/index.md exists, navigate it instead of reading raw files
125
+ - After modifying code files in this session, run `python3 -c "from graphify.watch import _rebuild_code; from pathlib import Path; _rebuild_code(Path('.'))"` to keep the graph current
126
+ """
127
+
128
+ _AGENTS_MD_MARKER = "## graphify"
129
+
130
+
131
+ def _agents_install(project_dir: Path, platform: str) -> None:
132
+ """Write the graphify section to the local AGENTS.md (Codex/OpenCode/OpenClaw)."""
133
+ target = (project_dir or Path(".")) / "AGENTS.md"
134
+
135
+ if target.exists():
136
+ content = target.read_text()
137
+ if _AGENTS_MD_MARKER in content:
138
+ print(f"graphify already configured in AGENTS.md")
139
+ return
140
+ new_content = content.rstrip() + "\n\n" + _AGENTS_MD_SECTION
141
+ else:
142
+ new_content = _AGENTS_MD_SECTION
143
+
144
+ target.write_text(new_content)
145
+ print(f"graphify section written to {target.resolve()}")
146
+ print()
147
+ print(f"{platform.capitalize()} will now check the knowledge graph before answering")
148
+ print("codebase questions and rebuild it after code changes.")
149
+ print()
150
+ print("Note: unlike Claude Code, there is no PreToolUse hook equivalent for")
151
+ print(f"{platform.capitalize()} — the AGENTS.md rules are the always-on mechanism.")
152
+
153
+
154
+ def _agents_uninstall(project_dir: Path) -> None:
155
+ """Remove the graphify section from the local AGENTS.md."""
156
+ target = (project_dir or Path(".")) / "AGENTS.md"
157
+
158
+ if not target.exists():
159
+ print("No AGENTS.md found in current directory - nothing to do")
160
+ return
161
+
162
+ content = target.read_text()
163
+ if _AGENTS_MD_MARKER not in content:
164
+ print("graphify section not found in AGENTS.md - nothing to do")
165
+ return
166
+
167
+ cleaned = re.sub(
168
+ r"\n*## graphify\n.*?(?=\n## |\Z)",
169
+ "",
170
+ content,
171
+ flags=re.DOTALL,
172
+ ).rstrip()
173
+ if cleaned:
174
+ target.write_text(cleaned + "\n")
175
+ print(f"graphify section removed from {target.resolve()}")
176
+ else:
177
+ target.unlink()
178
+ print(f"AGENTS.md was empty after removal - deleted {target.resolve()}")
179
+
110
180
 
111
181
  def claude_install(project_dir: Path | None = None) -> None:
112
182
  """Write the graphify section to the local CLAUDE.md."""
@@ -211,13 +281,21 @@ def main() -> None:
211
281
  print("Usage: graphify <command>")
212
282
  print()
213
283
  print("Commands:")
214
- print(" install [--platform P] copy skill to platform config dir (claude|codex|opencode|claw)")
284
+ print(" install [--platform P] copy skill to platform config dir (claude|codex|opencode|claw|droid)")
215
285
  print(" benchmark [graph.json] measure token reduction vs naive full-corpus approach")
216
- print(" hook install install post-commit git hook (auto-rebuilds graph on commit)")
217
- print(" hook uninstall remove post-commit git hook")
218
- print(" hook status check if hook is installed")
219
- print(" claude install write graphify section to local CLAUDE.md")
220
- print(" claude uninstall remove graphify section from local CLAUDE.md")
286
+ print(" hook install install post-commit/post-checkout git hooks (all platforms)")
287
+ print(" hook uninstall remove git hooks")
288
+ print(" hook status check if git hooks are installed")
289
+ print(" claude install write graphify section to CLAUDE.md + PreToolUse hook (Claude Code)")
290
+ print(" claude uninstall remove graphify section from CLAUDE.md + PreToolUse hook")
291
+ print(" codex install write graphify section to AGENTS.md (Codex)")
292
+ print(" codex uninstall remove graphify section from AGENTS.md")
293
+ print(" opencode install write graphify section to AGENTS.md (OpenCode)")
294
+ print(" opencode uninstall remove graphify section from AGENTS.md")
295
+ print(" claw install write graphify section to AGENTS.md (OpenClaw)")
296
+ print(" claw uninstall remove graphify section from AGENTS.md")
297
+ print(" droid install write graphify section to AGENTS.md (Factory Droid)")
298
+ print(" droid uninstall remove graphify section from AGENTS.md")
221
299
  print()
222
300
  return
223
301
 
@@ -245,6 +323,15 @@ def main() -> None:
245
323
  else:
246
324
  print("Usage: graphify claude [install|uninstall]", file=sys.stderr)
247
325
  sys.exit(1)
326
+ elif cmd in ("codex", "opencode", "claw", "droid"):
327
+ subcmd = sys.argv[2] if len(sys.argv) > 2 else ""
328
+ if subcmd == "install":
329
+ _agents_install(Path("."), cmd)
330
+ elif subcmd == "uninstall":
331
+ _agents_uninstall(Path("."))
332
+ else:
333
+ print(f"Usage: graphify {cmd} [install|uninstall]", file=sys.stderr)
334
+ sys.exit(1)
248
335
  elif cmd == "hook":
249
336
  from graphify.hooks import install as hook_install, uninstall as hook_uninstall, status as hook_status
250
337
  subcmd = sys.argv[2] if len(sys.argv) > 2 else ""
@@ -16,12 +16,16 @@ def _is_file_node(G: nx.Graph, node_id: str) -> bool:
16
16
  These are synthetic nodes created by the AST extractor and should be excluded
17
17
  from god nodes, surprising connections, and knowledge gap reporting.
18
18
  """
19
- label = G.nodes[node_id].get("label", "")
19
+ attrs = G.nodes[node_id]
20
+ label = attrs.get("label", "")
20
21
  if not label:
21
22
  return False
22
- # File-level hub: label is a filename with a code extension
23
- if label.split(".")[-1] in ("py", "ts", "js", "go", "rs", "java", "rb", "cpp", "c", "h"):
24
- return True
23
+ # File-level hub: label matches the actual source filename (not just any label ending in .py)
24
+ source_file = attrs.get("source_file", "")
25
+ if source_file:
26
+ from pathlib import Path as _Path
27
+ if label == _Path(source_file).name:
28
+ return True
25
29
  # Method stub: AST extractor labels methods as '.method_name()'
26
30
  if label.startswith(".") and label.endswith("()"):
27
31
  return True
@@ -468,7 +472,7 @@ def graph_diff(G_old: nx.Graph, G_new: nx.Graph) -> dict:
468
472
  ]
469
473
 
470
474
  def edge_key(G: nx.Graph, u: str, v: str, data: dict) -> tuple:
471
- return (u, v, data.get("relation", ""))
475
+ return (min(u, v), max(u, v), data.get("relation", ""))
472
476
 
473
477
  old_edge_keys = {
474
478
  edge_key(G_old, u, v, d)
@@ -3,12 +3,18 @@ from __future__ import annotations
3
3
 
4
4
  import hashlib
5
5
  import json
6
+ import os
6
7
  from pathlib import Path
7
8
 
8
9
 
9
10
  def file_hash(path: Path) -> str:
10
- """SHA256 of file contents, hex digest."""
11
- return hashlib.sha256(Path(path).read_bytes()).hexdigest()
11
+ """SHA256 of file contents + resolved path. Prevents cache collisions on identical content."""
12
+ p = Path(path)
13
+ h = hashlib.sha256()
14
+ h.update(p.read_bytes())
15
+ h.update(b"\x00")
16
+ h.update(str(p.resolve()).encode())
17
+ return h.hexdigest()
12
18
 
13
19
 
14
20
  def cache_dir(root: Path = Path(".")) -> Path:
@@ -46,7 +52,13 @@ def save_cached(path: Path, result: dict, root: Path = Path(".")) -> None:
46
52
  """
47
53
  h = file_hash(path)
48
54
  entry = cache_dir(root) / f"{h}.json"
49
- entry.write_text(json.dumps(result))
55
+ tmp = entry.with_suffix(".tmp")
56
+ try:
57
+ tmp.write_text(json.dumps(result))
58
+ os.replace(tmp, entry)
59
+ except Exception:
60
+ tmp.unlink(missing_ok=True)
61
+ raise
50
62
 
51
63
 
52
64
  def cached_files(root: Path = Path(".")) -> set[str]: