mirror of
https://github.com/safishamsi/graphify.git
synced 2026-09-22 21:45:58 +00:00
make tree-sitter-dm an optional extra to fix default install (fixes #1104)
tree-sitter-dm (BYOND DreamMaker) publishes only a Windows wheel, so on Linux/macOS uv/pip compiled it from source and aborted the entire `uv tool install graphifyy` when a C toolchain or python3-dev was missing. It is the only core grammar lacking Linux/Mac wheels. Move it from core dependencies to an optional `dm` extra (also in `all`); the default install now needs no compiler. extract_dm already imports the grammar lazily with a graceful fallback, and .dmi/.dmm/.dmf use no tree-sitter, so only .dm/.dme AST parsing is gated behind the extra. Also: guard the .dm grammar tests with pytest.importorskip so a plain `uv sync` (no extras) doesn't hard-fail; bump to 0.8.28 with a changelog upgrade note; switch the README extras table + type notes from pip to `uv tool install` (uv is the recommended installer, and pip-into-a-uv-tool-env is the exact ModuleNotFoundError the README already warns about). 0.8.28 also ships Kilo Code (#512) and the Dart parser modernization (#1098). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,13 @@
|
||||
|
||||
Full release notes with details on each version: [GitHub Releases](https://github.com/safishamsi/graphify/releases)
|
||||
|
||||
## 0.8.28 (2026-06-01)
|
||||
|
||||
- Feat: Kilo Code support — `graphify install --platform kilo` installs a native skill (`~/.config/kilo/skills/graphify/SKILL.md`) and `/graphify` command, plus a `.kilo` `tool.execute.before` plugin (mirroring the OpenCode integration). Existing `.kilo/kilo.jsonc` config is read but never rewritten — plugin registration goes to `kilo.json` so user comments are preserved (#512)
|
||||
- Feat: modernized Dart extractor — comment stripping, `part of` redirection, nested-generic-aware `extends`/`with`/`implements` parsing, generic type-argument mapping, and generic call detection (#1098)
|
||||
- Fix: `uv tool install graphifyy` / `pip install graphifyy` no longer fails to build on Linux/macOS — `tree-sitter-dm` (BYOND DreamMaker) ships only a Windows wheel, so on other platforms it compiled from source and aborted the entire install when a C toolchain or `python3-dev` headers were missing. It is now an optional extra (`graphifyy[dm]`, also in `[all]`) instead of a core dependency, so the default install needs no compiler (#1104).
|
||||
- **Upgrade note:** DreamMaker `.dm`/`.dme` users must reinstall with `graphifyy[dm]` (or `[all]`) to keep AST extraction — on `uv tool upgrade` the now-optional grammar is removed. `.dmi`/`.dmm`/`.dmf` parsing is unaffected (no tree-sitter dependency).
|
||||
|
||||
## 0.8.27 (2026-05-31)
|
||||
|
||||
- Feat: standalone CLI now auto-names communities with the configured backend instead of leaving `Community N` placeholders — community labeling was previously an agent-only step (skill.md Step 5), so bare-CLI runs never got semantic names; `cluster-only` now auto-labels when no `.graphify_labels.json` exists, new `graphify label <path>` subcommand (re)generates names on demand, `--no-label` opts out, `--backend=<name>` overrides auto-detection; one batched LLM call with per-community placeholder fallback and graceful degradation on missing backend/API error; works with all built-in and custom OpenAI-compatible backends (#1097)
|
||||
|
||||
@@ -153,21 +153,22 @@ Install only what you need:
|
||||
|
||||
| Extra | What it adds | Install |
|
||||
|---|---|---|
|
||||
| `pdf` | PDF extraction | `pip install "graphifyy[pdf]"` |
|
||||
| `office` | `.docx` and `.xlsx` support | `pip install "graphifyy[office]"` |
|
||||
| `google` | Google Sheets rendering | `pip install "graphifyy[google]"` |
|
||||
| `video` | Video/audio transcription (faster-whisper + yt-dlp) | `pip install "graphifyy[video]"` |
|
||||
| `mcp` | MCP stdio server | `pip install "graphifyy[mcp]"` |
|
||||
| `neo4j` | Neo4j push support | `pip install "graphifyy[neo4j]"` |
|
||||
| `svg` | SVG graph export | `pip install "graphifyy[svg]"` |
|
||||
| `leiden` | Leiden community detection (Python < 3.13 only) | `pip install "graphifyy[leiden]"` |
|
||||
| `ollama` | Ollama local inference | `pip install "graphifyy[ollama]"` |
|
||||
| `openai` | OpenAI / OpenAI-compatible APIs | `pip install "graphifyy[openai]"` |
|
||||
| `gemini` | Google Gemini API | `pip install "graphifyy[gemini]"` |
|
||||
| `bedrock` | AWS Bedrock (uses IAM, no API key) | `pip install "graphifyy[bedrock]"` |
|
||||
| `sql` | SQL schema extraction | `pip install "graphifyy[sql]"` |
|
||||
| `chinese` | Chinese query segmentation (jieba) | `pip install "graphifyy[chinese]"` |
|
||||
| `all` | Everything above | `pip install "graphifyy[all]"` |
|
||||
| `pdf` | PDF extraction | `uv tool install "graphifyy[pdf]"` |
|
||||
| `office` | `.docx` and `.xlsx` support | `uv tool install "graphifyy[office]"` |
|
||||
| `google` | Google Sheets rendering | `uv tool install "graphifyy[google]"` |
|
||||
| `video` | Video/audio transcription (faster-whisper + yt-dlp) | `uv tool install "graphifyy[video]"` |
|
||||
| `mcp` | MCP stdio server | `uv tool install "graphifyy[mcp]"` |
|
||||
| `neo4j` | Neo4j push support | `uv tool install "graphifyy[neo4j]"` |
|
||||
| `svg` | SVG graph export | `uv tool install "graphifyy[svg]"` |
|
||||
| `leiden` | Leiden community detection (Python < 3.13 only) | `uv tool install "graphifyy[leiden]"` |
|
||||
| `ollama` | Ollama local inference | `uv tool install "graphifyy[ollama]"` |
|
||||
| `openai` | OpenAI / OpenAI-compatible APIs | `uv tool install "graphifyy[openai]"` |
|
||||
| `gemini` | Google Gemini API | `uv tool install "graphifyy[gemini]"` |
|
||||
| `bedrock` | AWS Bedrock (uses IAM, no API key) | `uv tool install "graphifyy[bedrock]"` |
|
||||
| `sql` | SQL schema extraction | `uv tool install "graphifyy[sql]"` |
|
||||
| `dm` | BYOND DreamMaker `.dm`/`.dme` AST extraction (may need a C compiler + `python3-dev` if no wheel matches your platform) | `uv tool install "graphifyy[dm]"` |
|
||||
| `chinese` | Chinese query segmentation (jieba) | `uv tool install "graphifyy[chinese]"` |
|
||||
| `all` | Everything above | `uv tool install "graphifyy[all]"` |
|
||||
|
||||
---
|
||||
|
||||
@@ -222,15 +223,15 @@ To remove graphify from all platforms at once: `graphify uninstall` (add `--purg
|
||||
|
||||
| Type | Extensions |
|
||||
|------|-----------|
|
||||
| Code (33 languages) | `.py .ts .js .jsx .tsx .mjs .go .rs .java .c .cpp .h .hpp .rb .cs .kt .scala .php .swift .lua .luau .zig .ps1 .ex .exs .m .mm .jl .vue .svelte .astro .groovy .gradle .dart .v .sv .svh .sql .f .f90 .f95 .f03 .f08 .pas .pp .dpr .dpk .lpr .inc .dfm .lfm .lpk .sh .bash .json .dm .dme .dmi .dmm .dmf .sln .csproj .fsproj .vbproj .razor .cshtml` |
|
||||
| Code (33 languages) | `.py .ts .js .jsx .tsx .mjs .go .rs .java .c .cpp .h .hpp .rb .cs .kt .scala .php .swift .lua .luau .zig .ps1 .ex .exs .m .mm .jl .vue .svelte .astro .groovy .gradle .dart .v .sv .svh .sql .f .f90 .f95 .f03 .f08 .pas .pp .dpr .dpk .lpr .inc .dfm .lfm .lpk .sh .bash .json .dm .dme .dmi .dmm .dmf .sln .csproj .fsproj .vbproj .razor .cshtml` (`.dm`/`.dme` AST extraction requires `uv tool install graphifyy[dm]`) |
|
||||
| MCP configs | `.mcp.json` `mcp.json` `mcp_servers.json` `claude_desktop_config.json` — extracts server nodes, package refs, env var requirements |
|
||||
| Docs | `.md .mdx .qmd .html .txt .rst .yaml .yml` |
|
||||
| Office | `.docx .xlsx` (requires `pip install graphifyy[office]`) |
|
||||
| Google Workspace | `.gdoc .gsheet .gslides` (opt-in; requires `gws` auth and `--google-workspace`; Sheets need `pip install graphifyy[google]`) |
|
||||
| Office | `.docx .xlsx` (requires `uv tool install graphifyy[office]`) |
|
||||
| Google Workspace | `.gdoc .gsheet .gslides` (opt-in; requires `gws` auth and `--google-workspace`; Sheets need `uv tool install graphifyy[google]`) |
|
||||
| PDFs | `.pdf` |
|
||||
| Images | `.png .jpg .webp .gif` |
|
||||
| Video / Audio | `.mp4 .mov .mp3 .wav` and more (requires `pip install graphifyy[video]`) |
|
||||
| YouTube / URLs | any video URL (requires `pip install graphifyy[video]`) |
|
||||
| Video / Audio | `.mp4 .mov .mp3 .wav` and more (requires `uv tool install graphifyy[video]`) |
|
||||
| YouTube / URLs | any video URL (requires `uv tool install graphifyy[video]`) |
|
||||
|
||||
Code is extracted locally with no API calls (AST via tree-sitter). Everything else goes through your AI assistant's model API.
|
||||
|
||||
@@ -240,7 +241,7 @@ in a headless extraction, install and authenticate the
|
||||
[`gws` CLI](https://github.com/googleworkspace/cli), then run:
|
||||
|
||||
```bash
|
||||
pip install "graphifyy[google]" # needed for Google Sheets table rendering
|
||||
uv tool install "graphifyy[google]" # needed for Google Sheets table rendering
|
||||
gws auth login -s drive
|
||||
graphify extract ./docs --google-workspace
|
||||
```
|
||||
|
||||
+6
-3
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "graphifyy"
|
||||
version = "0.8.27"
|
||||
version = "0.8.28"
|
||||
description = "AI coding assistant skill (Claude Code, Codex, OpenCode, Kilo Code, Cursor, Gemini CLI, Aider, OpenClaw, Factory Droid, Trae, Hermes, Kiro, Pi, Devin CLI, Google Antigravity) - turn any folder of code, docs, papers, images, or videos into a queryable knowledge graph"
|
||||
readme = "README.md"
|
||||
license = { file = "LICENSE" }
|
||||
@@ -40,7 +40,6 @@ dependencies = [
|
||||
"tree-sitter-fortran",
|
||||
"tree-sitter-bash",
|
||||
"tree-sitter-json",
|
||||
"tree-sitter-dm",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
@@ -65,7 +64,11 @@ gemini = ["openai", "tiktoken"]
|
||||
openai = ["openai", "tiktoken"]
|
||||
chinese = ["jieba"]
|
||||
sql = ["tree-sitter-sql"]
|
||||
all = ["mcp", "neo4j", "pypdf", "markdownify", "watchdog", "graspologic; python_version < '3.13'", "python-docx", "openpyxl", "faster-whisper; python_version >= '3.11'", "yt-dlp", "matplotlib", "openai", "tiktoken", "boto3", "tree-sitter-sql", "jieba"]
|
||||
# tree-sitter-dm (BYOND DreamMaker) ships only a Windows wheel, so on Linux/Mac it
|
||||
# must compile from source (needs a C toolchain + python3-dev). Keeping it optional
|
||||
# avoids breaking the default `uv tool install graphifyy` for everyone (#1104).
|
||||
dm = ["tree-sitter-dm"]
|
||||
all = ["mcp", "neo4j", "pypdf", "markdownify", "watchdog", "graspologic; python_version < '3.13'", "python-docx", "openpyxl", "faster-whisper; python_version >= '3.11'", "yt-dlp", "matplotlib", "openai", "tiktoken", "boto3", "tree-sitter-sql", "jieba", "tree-sitter-dm"]
|
||||
|
||||
[project.scripts]
|
||||
graphify = "graphify.__main__:main"
|
||||
|
||||
@@ -13,6 +13,15 @@ from graphify.extract import (
|
||||
|
||||
FIXTURES = Path(__file__).parent / "fixtures"
|
||||
|
||||
# tree-sitter-dm is an optional extra (#1104) - it ships no Linux/Mac wheel, so it
|
||||
# is not installed by a default `uv sync`. Skip the .dm/.dme grammar tests when the
|
||||
# grammar is absent (.dmi/.dmm/.dmf use no tree-sitter and are always tested).
|
||||
import importlib.util as _ilu
|
||||
_needs_dm = pytest.mark.skipif(
|
||||
_ilu.find_spec("tree_sitter_dm") is None,
|
||||
reason="tree-sitter-dm not installed (optional [dm] extra)",
|
||||
)
|
||||
|
||||
|
||||
def _labels(r):
|
||||
return [n["label"] for n in r["nodes"]]
|
||||
@@ -1301,38 +1310,45 @@ def test_groovy_spock_no_dangling_edges():
|
||||
|
||||
# ── DM (BYOND DreamMaker) ────────────────────────────────────────────────────
|
||||
|
||||
@_needs_dm
|
||||
def test_dm_no_error():
|
||||
r = extract_dm(FIXTURES / "sample.dm")
|
||||
assert "error" not in r
|
||||
|
||||
@_needs_dm
|
||||
def test_dm_finds_global_proc():
|
||||
r = extract_dm(FIXTURES / "sample.dm")
|
||||
labels = _labels(r)
|
||||
assert any(l == "log_event()" for l in labels)
|
||||
assert any(l == "RunTest()" for l in labels)
|
||||
|
||||
@_needs_dm
|
||||
def test_dm_finds_type_definition():
|
||||
r = extract_dm(FIXTURES / "sample.dm")
|
||||
labels = _labels(r)
|
||||
assert "/datum/weapon" in labels
|
||||
assert "/datum/weapon/sword" in labels
|
||||
|
||||
@_needs_dm
|
||||
def test_dm_qualifies_proc_with_type_path():
|
||||
r = extract_dm(FIXTURES / "sample.dm")
|
||||
labels = _labels(r)
|
||||
assert "/datum/weapon/attack()" in labels
|
||||
assert "/datum/weapon/sword/attack()" in labels
|
||||
|
||||
@_needs_dm
|
||||
def test_dm_finds_path_form_proc_definition():
|
||||
r = extract_dm(FIXTURES / "sample.dm")
|
||||
assert "/datum/weapon/sword/sharpen()" in _labels(r)
|
||||
|
||||
@_needs_dm
|
||||
def test_dm_emits_include_edge():
|
||||
r = extract_dm(FIXTURES / "sample.dm")
|
||||
import_edges = _edges_with_relation(r, "imports", "imports_from")
|
||||
assert import_edges
|
||||
assert all(e.get("context") == "import" for e in import_edges)
|
||||
|
||||
@_needs_dm
|
||||
def test_dm_unresolved_include_flagged_external():
|
||||
r = extract_dm(FIXTURES / "sample.dm")
|
||||
import_edges = _edges_with_relation(r, "imports", "imports_from")
|
||||
@@ -1340,12 +1356,14 @@ def test_dm_unresolved_include_flagged_external():
|
||||
assert helpers
|
||||
assert all(e.get("external") is True for e in helpers)
|
||||
|
||||
@_needs_dm
|
||||
def test_dm_resolves_in_file_calls():
|
||||
r = extract_dm(FIXTURES / "sample.dm")
|
||||
calls = _calls(r)
|
||||
assert any(callee == "log_event()" for _, callee in calls)
|
||||
assert ("/datum/weapon/sword/attack()", "/datum/weapon/sword/sharpen()") in calls
|
||||
|
||||
@_needs_dm
|
||||
def test_dm_ambiguous_member_call_left_unresolved():
|
||||
r = extract_dm(FIXTURES / "sample.dm")
|
||||
calls = _calls(r)
|
||||
@@ -1354,6 +1372,7 @@ def test_dm_ambiguous_member_call_left_unresolved():
|
||||
assert not runtest_to_attack
|
||||
assert any(rc["callee"] == "attack" for rc in r.get("raw_calls", []))
|
||||
|
||||
@_needs_dm
|
||||
def test_dm_emits_new_as_instantiates():
|
||||
r = extract_dm(FIXTURES / "sample.dm")
|
||||
node_by_id = {n["id"]: n["label"] for n in r["nodes"]}
|
||||
@@ -1361,18 +1380,21 @@ def test_dm_emits_new_as_instantiates():
|
||||
for e in r["edges"] if e["relation"] == "instantiates"]
|
||||
assert ("RunTest()", "/datum/weapon/sword") in inst
|
||||
|
||||
@_needs_dm
|
||||
def test_dm_call_edges_have_call_context():
|
||||
r = extract_dm(FIXTURES / "sample.dm")
|
||||
call_edges = _edges_with_relation(r, "calls", "instantiates")
|
||||
assert call_edges
|
||||
assert all(e.get("context") == "call" for e in call_edges)
|
||||
|
||||
@_needs_dm
|
||||
def test_dm_no_dangling_edges():
|
||||
r = extract_dm(FIXTURES / "sample.dm")
|
||||
node_ids = {n["id"] for n in r["nodes"]}
|
||||
for e in r["edges"]:
|
||||
assert e["source"] in node_ids
|
||||
|
||||
@_needs_dm
|
||||
def test_dm_super_call_not_emitted():
|
||||
r = extract_dm(FIXTURES / "sample.dm")
|
||||
calls = _calls(r)
|
||||
|
||||
Reference in New Issue
Block a user