Windows SKILL.md field review (v0.9.5): broken interpreter guard, backslash/encoding corruption, ghost flags — with verified fixes
Environment: Windows 11 Pro, Claude Code (Git Bash + PowerShell 7 available), graphifyy 0.9.5 installed via uv tool install, skill at ~/.claude/skills/graphify/ (name: graphify-windows).
I did a line-by-line review of the Windows skill template (SKILL.md + references/) and verified the riskiest findings on a real machine. Two failures reproduce today on a stock Windows + uv setup; the rest are latent Windows bugs, doc/spec mismatches, and two ghost flags the skill references but never implements. I've patched all of these locally and validated the fixes — happy to turn this into a PR if you want it.
Findings are grouped by severity. Line numbers refer to the shipped v0.9.5 SKILL.md (712 lines) and reference files.
A. Reproduced on a stock Windows install
A1. Interpreter guard is broken on Windows (SKILL.md:649-661)
The subcommand guard recovers a lost graphify-out/.graphify_python by parsing a shebang:
GRAPHIFY_BIN=$(which graphify 2>/dev/null)
PYTHON=$(head -1 "$GRAPHIFY_BIN" | tr -d '#!')
On Windows the entry point is graphify.exe — a PE32+ binary (verified with file). head -1 reads MZx... garbage, the character-class guard catches it, and the fallback writes python3 — which on a typical Windows box resolves to the Microsoft Store WindowsApps stub, not a real interpreter (verified: which python3 → .../WindowsApps/python3). Net effect: delete graphify-out/ once and every query / path / explain / --update is broken until a full rebuild, with a confusing Store-stub error.
Fix (validated): don't parse shebangs; probe real interpreters the same way Step 1 does — uv tool dir → pipx environment → active env, each gated on "$PY" -c "import graphify" (the import check also filters out the Store stub). Bash version below works on Git Bash and Linux/macOS:
find_graphify_python() {
if command -v uv >/dev/null 2>&1; then
UV_DIR=$(uv tool dir 2>/dev/null | tr -d '\r' | tr '\\' '/')
for PY in "$UV_DIR/graphifyy/Scripts/python.exe" "$UV_DIR/graphifyy/bin/python"; do
if [ -x "$PY" ] && "$PY" -c "import graphify" 2>/dev/null; then printf '%s' "$PY"; return 0; fi
done
fi
if command -v pipx >/dev/null 2>&1; then
PIPX_VENVS=$(pipx environment --value PIPX_LOCAL_VENVS 2>/dev/null | tr -d '\r' | tr '\\' '/')
for PY in "$PIPX_VENVS/graphifyy/Scripts/python.exe" "$PIPX_VENVS/graphifyy/bin/python"; do
if [ -x "$PY" ] && "$PY" -c "import graphify" 2>/dev/null; then printf '%s' "$PY"; return 0; fi
done
fi
for PY in python3 python; do
if command -v "$PY" >/dev/null 2>&1 && "$PY" -c "import graphify" 2>/dev/null; then
"$PY" -c "import sys; print(sys.executable.replace(chr(92), '/'))" | tr -d '\r'
return 0
fi
done
return 1
}
A2. cp1252 crashes on non-ASCII output; references/query.md lacks encodings
query.md gives Cyrillic examples ("обработчик" → handler) but its Python blocks use bare read_text() / write_text() (lines 34, 43, 86, 203, 271) — unlike SKILL.md's blocks, which consistently pass encoding="utf-8". On Windows the default is cp1252, so the vocab writer crashes on exactly the cross-language corpora the doc describes. Printing non-ASCII node labels through a pipe fails the same way — reproduced:
> python -c "print('обработчик')" | cat
UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-9
Fix (validated): prefix interpreter invocations with PYTHONUTF8=1 (covers file I/O and piped stdout in one move), plus add the missing encoding='utf-8' args in query.md for belt-and-braces.
B. Latent Windows bugs (will fire under documented configurations)
B1. Backslash INPUT_PATH corrupts every Python one-liner
Nearly every block says "Replace INPUT_PATH with the actual path" and splices it into a Python string literal (Path('INPUT_PATH'), root='INPUT_PATH'). A natural Windows substitution corrupts silently or loudly:
'C:\temp\proj'→\tbecomes a TAB — wrong path, no error'C:\Users\me'→\UraisesSyntaxError: (unicode error) truncated \UXXXXXXXX escape
There is no warning anywhere in the skill. Fix: one rule near the top — substitute INPUT_PATH with forward slashes (C:/Users/me/project). Also (Resolve-Path INPUT_PATH) at SKILL.md:122 is unquoted and breaks on paths containing spaces (Resolve-Path 'INPUT_PATH').
B2. BOM poisoning under Windows PowerShell 5.1 (SKILL.md:120-122)
Step 1 saves the interpreter path with Out-File -Encoding utf8. Under PS 5.1 — which the skill explicitly supports (the Troubleshooting section is about the 5.1 console) — that writes a UTF-8 BOM, so every later $(cat graphify-out/.graphify_python) produces a \xef\xbb\xbf-prefixed path that fails to exec. Fix (validated): write the file from Python (open(...,'w',encoding='utf-8').write(sys.executable)) — no BOM, no trailing newline, and it also normalizes to forward slashes.
B3. Shell contradiction: bolded instruction says PowerShell, every block is bash (SKILL.md:127)
"In every subsequent block, run Python through the saved interpreter —
& (Get-Content graphify-out\.graphify_python)in place of a barepython3"
…but all subsequent blocks are bash ($(cat ...)), where the PS form is a parse error — and the bash blocks' \"-escaping breaks if an agent obediently transcribes them into PowerShell. Step B2's CHUNK_PATH derivation (SKILL.md:287-290) is also PowerShell inside an otherwise-bash step. Fix: declare one shell ("Step 1 is PowerShell; run everything else with bash") or port Step 1 to bash too (my local patch does the latter — the uv/pipx detection from #831 ports cleanly, see A1).
B4. Step 1 has no failure gate
If install fails, $GRAPHIFY_PYTHON stays $null, an empty .graphify_python is written, and every later step degenerates into cryptic -c: command not found errors far from the root cause. Fix: after re-detect, if still empty → print an explicit ERROR with install commands and stop. Related: the pip install graphifyy fallback assumes pip is on PATH — python -m pip install graphifyy is the reliable form on Windows.
B5. Unquoted $(cat graphify-out/.graphify_python) throughout
Interpreter paths containing spaces (e.g. a venv under C:\Users\First Last\...) word-split. Quoting it — "$(cat graphify-out/.graphify_python)" — costs nothing.
C. Spec/doc mismatches
C1. Ghost flag --no-cluster (SKILL.md:162)
The large-flat-corpus gate instructs the agent to suggest --no-cluster, but the flag isn't in Usage and no step implements it. Since cluster() returns {community_id: [node_ids]}, the implementation is two lines in Step 4: communities = {0: list(G.nodes())} with a "Full Corpus" label, skipping Step 5. (Validated locally.)
C2. Ghost flag --force (SKILL.md:452)
The #479 shrink-guard error says "re-run a full build with --force", but --force isn't in Usage and Step 4 never passes it. export.to_json() already accepts force= — it just needs wiring: to_json(..., force=IS_FORCE).
C3. Watch-flag filename mismatch
watch.py writes graphify-out/needs_update (no dot — watch.py:749/894/920), and add-watch.md:50 documents it that way, but Step 9's cleanup removes .needs_update (SKILL.md:609). The dotted file never exists, so the real flag survives cleanup.
C4. update.md: backup instruction placed after the step it must precede
"Before the merge step, save the old graph: cp graphify-out/graph.json graphify-out/.graphify_old.json" appears at update.md:179 — after the merge block (line ~85) and the diff block that consumes the backup. An agent executing top-to-bottom reaches it too late and the post-update diff silently no-ops (the diff block's if old_data: swallows it).
C5. Small ones
- Frontmatter
name: graphify-windowsdoesn't match the directory/command namegraphify(hosts register it asgraphifyanyway). - Usage line 13's comment says the default run produces an "Obsidian vault", but Step 6 makes obsidian opt-in (
--obsidian); HTML is the default. - Usage documents
--htmlas "(this flag is a no-op)" — probably better dropped. - Plain
--obsidian(without--obsidian-dir) is implemented in Step 6 but missing from Usage. - Chunk file naming is inconsistent:
_0N(B2 snippet) vsNN(B3) — worth standardizing on zero-padded two digits. - The two "no API key" callouts (SKILL.md:176 and :183) largely duplicate each other and could merge.
D. Suggestion: dispatcher structure for token economy
SKILL.md is 712 lines and all of it loads on every invocation, but the most common flow on an existing graph (query) needs ~30 of them. The references/ pattern is already established (query/update/exports/…); moving the build pipeline (Steps 1–9, ~550 lines) into references/build.md and keeping SKILL.md as a ~145-line dispatcher (Usage, fast path, global execution rules, routing table, interpreter guard) cuts the common case's context cost by ~75-80%. I've run this restructure locally with all of the above folded in and it works well — glad to PR it, either as one change or with section A/B/C fixes split out first.
Verification notes: A1 (PE entry point, Store-stub python3), A2 (UnicodeEncodeError repro + PYTHONUTF8=1 fix), B2 (BOM-free write), and the rewritten interpreter guard were all tested on Windows 11 + Git Bash + uv-installed graphifyy 0.9.5. C1/C2/C3 were verified against the installed package source (cluster() return shape, to_json(force=) signature, watch.py flag name).
Source: Graphify-Labs/graphify