Alembic/Flask-Migrate revisions, Django migrations, and protobuf/OpenAPI
generated files produce hundreds of degree-1 rationale nodes labeled as
'possible documentation gaps'. Their module docstrings are revision
annotations or boilerplate, not architectural rationale.
- Add _is_autogenerated_python() in extract.py detecting Alembic, Django
migrations, and generic DO-NOT-EDIT markers; skip module docstring only
- Function/class docstrings inside those files still extracted as normal
- report.py: exclude file_type=rationale nodes from isolated-node gaps
section — rationale nodes are degree-1 by construction; flagging them
as missing edges was always wrong
- 5 new tests covering Alembic, Django, protobuf, false-positive guard,
and function-docstring passthrough
Move all intermediate data files and temp step scripts from the
project root into graphify-out/ to match the bash skill behaviour.
Also fix the > redirect on detect.json which wrote UTF-16LE in
PowerShell 5.1 - now uses Out-File -Encoding utf8 consistently.
Files moved: .graphify_detect.json, .graphify_ast.json,
.graphify_extract.json, .graphify_semantic.json,
.graphify_analysis.json, .graphify_labels.json,
.graphify_incremental.json, .graphify_old.json,
all .graphify_step_*.py temp scripts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
antigravity install on Windows now installs skill-windows.md instead
of skill.md by redirecting to a new antigravity-windows platform config
at install time via sys.platform check.
Python detection in Find-GraphifyPython now uses uv tool dir (respects
UV_TOOL_DIR) and pipx environment --value PIPX_LOCAL_VENVS (respects
PIPX_HOME) instead of guessing from the shim location. The graphify.exe
shim and python.exe live in different directories for both uv and pipx
on Windows so the previous Get-Command approach was wrong.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
graphify.exe and python.exe are co-located in the same Scripts\
directory for both uv tool and pipx installs. Use Get-Command graphify
to find that directory and pick up the correct Python instead of
falling through to bare python (which is a different interpreter
that doesn't have graphify).
Also replace all bare `python .graphify_*` invocations throughout
the skill with `& (Get-Content graphify-out\.graphify_python)` so
every step uses the saved interpreter, and fix the bash $(cat ...)
syntax to PowerShell & (Get-Content ...).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
#873: Remove blanket dot-prefix exclusion from detect.py and
extract.py collect_files(). Add framework caches (.next, .nuxt,
.turbo, .angular, .idea, .cache, .parcel-cache, .svelte-kit,
.terraform, .serverless, .graphify) to _SKIP_DIRS so they stay
blocked. Meaningful dot dirs (.github, .claude, etc.) are now
indexed.
#874: Add _maybe_reload() with mtime+size stat key and threading.Lock
to serve.py. call_tool and read_resource call _maybe_reload() on
every request; the graph reloads automatically when graph.json changes
without restarting the MCP server.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add .sh, .bash, .json to CODE_EXTENSIONS in detect.py so files reach extractor
- Fix test_detect_incremental manifest path collision with new .json extension
- Update test_watch to reflect .json/.sh are now watched extensions
- B-1: only emit source imports for paths that exist on disk
- J-1: replace stat()+read() with bounded read to eliminate TOCTOU
- J-3: move pair_count cap inside loop so it is honoured exactly
- J-4: namespace $ref/extends refs with "ref_" prefix to prevent ID collision
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In a linked worktree .git is a file not a directory, so root/.git/hooks
fails with NotADirectoryError. Fall back to git rev-parse --path-format=absolute
--git-path hooks which resolves the correct hooks dir from any worktree.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- skill-windows.md: add graphify-out/ prefix to 5 paths in Step B3 that
were being read/written without prefix (lines 244,245,373,387,388,407,413)
causing semantic cache to always appear empty on Windows (#862)
- detect.py: gate converted office/Google Workspace sidecar paths through
_is_ignored() before appending to files list so .graphifyignore rules
apply to graphify-out/converted/ sidecars (#861)
- detect.py: split manifest entries from {mtime,hash} to
{mtime,ast_hash,semantic_hash}; save_manifest() accepts kind="ast"|
"semantic"|"both"; detect_incremental() accepts kind= and checks the
right hash field so graphify update (kind=ast) no longer prevents
graphify extract from re-running semantic extraction (#857)
- watch.py: all three save_manifest() calls use kind="ast" (AST-only rebuild)
- __main__.py: both extract save_manifest() calls use kind="both"
- backwards compatible: legacy {mtime,hash} entries treated as ast_hash only
775 tests passing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Several host agents (Claude Code in particular) misread the current
phrasing and prompt the user for `ANTHROPIC_API_KEY` when no Gemini
key is set, instead of falling through to subagent dispatch. The
Gemini tip stays as-is; this just adds an explicit one-line callout
that no other provider keys are consulted, with `ANTHROPIC_API_KEY`
called out by name since that is the specific misread we hit in the
wild.
No behavior change — pure documentation.
The rebuild lock file accumulated concatenated PIDs across post-commit
rebuilds without a separator, and was never removed when the rebuild
finished. Two practical consequences for users:
1. Downstream tooling that polls for `.rebuild.lock` to disappear before
doing post-rebuild work (publish scripts copying graph.html to a web
root, etc.) blocked forever / until its own timeout.
2. The accumulated digit string could not be parsed by humans or tooling
to find the owning PID.
The `_rebuild_lock` context manager now:
- Opens the lock file with `a+` so a non-acquiring caller does not
truncate the existing holder's PID.
- After flock acquisition, truncates and writes a single `<pid>\n` line
so external readers can `kill -0 $(cat .rebuild.lock)` to check
liveness.
- Unlinks the lock file in the finally block (only when *we* held the
lock), restoring the "signal-by-absence" convention users rely on.
Four regression tests added under `tests/test_watch.py` covering the
PID-with-newline payload, post-release unlink, no-accumulation across
sequential acquisitions, and the non-blocking-caller-does-not-clobber
invariant.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Astro files have a `---...---` TypeScript frontmatter block at the top
containing nearly all imports, followed by an HTML-with-expressions
template body, and optionally `<script>` blocks for client-side JS.
Feeding the whole file to the JS tree-sitter parser produces a top-level
ERROR node because the template isn't valid JS, so `import_statement`
nodes are never reached.
Mirrors `extract_svelte` (#713) — same regex-rescue strategy:
- Dynamic `import('...')` pass over the full source.
- Static `import X from '...'` pass over the frontmatter block plus any
`<script>` blocks.
Both static and dynamic imports honor tsconfig.json path aliases and
resolve relative paths through `_resolve_js_module_path` so node ids
match the targets that `_extract_generic` produces for sibling files.
Wired in:
- `detect.py:27` — `.astro` added to `CODE_EXTENSIONS`.
- `extract.py:5492` — `".astro": extract_astro` in the dispatch table.
Tests in `tests/test_astro_extraction.py` cover: extension registration,
frontmatter static imports, dynamic import in frontmatter, client-side
`<script>` block imports, frontmatter-less pages (must not crash), and
tsconfig.json path-alias resolution.
- skill.md + skill-windows.md: add encoding="utf-8" to all read_text()/write_text()
calls and ensure_ascii=False to json.dumps — bare calls defaulted to system
codepage on Chinese-locale Windows, mojibaking non-ASCII content (#832)
- skill.md + skill-windows.md: prefer uv tool install --upgrade graphifyy over
pip in the Step 1 install fallback — pip installs to the wrong env when
graphify was installed via uv tool (#831)
- serve.py + __main__.py: replace flat substring scoring in _score_nodes with
three-tier precedence (exact 1000 / prefix 100 / substring 1); _find_node
returns results ordered exact→prefix→substring; both path CLI and MCP now
emit a clear error when src and tgt resolve to the same node (#828)
- cache.py: normalize path key via .as_posix().lower() in file_hash so Windows
junction/case variants hash identically; mirror abs-path normalization from
save_semantic_cache into check_semantic_cache so relative source_file paths
resolve the same way on both sides (#826)
- __main__.py: add /graphify skill trigger line to _AGENTS_MD_SECTION — affects
all 7 AGENTS.md platforms (OpenCode, Codex, Aider, Trae, Hermes, Claw, Droid)
so typing /graphify actually invokes the skill tool (#827)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a pre-cluster topology comparison fast path in update rebuilds so unchanged graphs short-circuit before clustering and report generation, preventing residual run-to-run community-count drift.
Co-authored-by: Cursor <cursoragent@cursor.com>