Summary
On a stock Windows machine (Git for Windows only — no jq, no real Python on PATH), every bash-variant script that reads .specify/feature.json fails with ERROR: Feature directory not found ... immediately after a successful specify init --script sh. The cause is that the JSON parser fallback chain in common.sh selects python3 based on existence (command -v), but on Windows python3 resolves to the Microsoft Store App Execution Alias stub, which exists on PATH yet fails at runtime. The chain then swallows the runtime failure and returns an empty value, and the grep/sed last-resort parser is unreachable because the chain is if/elif/else on availability, not on parse success.
Environment
- Spec Kit
v0.12.3 (installed via uv tool/uvx from the git tag)
- Windows 11, Git for Windows (bash 5.2.37, MSYS), run under Git Bash
- No
jq installed (Git for Windows does not ship one)
- No real Python on
PATH; C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\python3.exe stub present (Windows default)
Reproduction
# on the machine described above, in Git Bash
uvx --from "git+https://github.com/github/spec-kit.git@v0.12.3" \
specify init target --integration generic \
--integration-options "--commands-dir .agent/commands" \
--script sh --ignore-agent-tools
cd target && git init -b main . && git add -A && git commit -m scaffold
bash .specify/scripts/bash/create-new-feature.sh --json --short-name demo "demo feature"
# ^ succeeds, persists .specify/feature.json = {"feature_directory":"specs/001-demo"}
bash .specify/scripts/bash/setup-plan.sh --json
# ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRECTORY or ensure
# .specify/feature.json contains feature_directory.
feature.json exists and contains the key — it just can't be read:
$ command -v python3
/c/Users/<user>/AppData/Local/Microsoft/WindowsApps/python3
$ python3 -c "print(42)"; echo exit=$?
Python was not found; run without arguments to install from the Microsoft Store, ...
exit=49
Root cause
scripts/bash/common.sh, read_feature_json_feature_directory() (v0.12.3, ~lines 95–120):
if command -v jq >/dev/null 2>&1; then
... jq ...
elif command -v python3 >/dev/null 2>&1; then # <- stub passes this test
if ! _fd=$(python3 -c "..." 2>/dev/null); then # <- fails at runtime (exit 49)
_fd='' # <- failure swallowed
fi
else
... grep/sed fallback ... # <- unreachable
fi
create-new-feature.sh masks the problem on its own persist path (it falls back to printf for writing), so the failure only appears one script later, which makes it look like state corruption rather than a parser issue.
Notably, CI runners (GitHub Actions windows-latest etc.) never see this: they ship both jq and a real python3. It only bites real end-user Windows machines — which are exactly the machines the sh-variant + Git Bash path is meant to serve.
Suggested fix
Any of these would do (first is smallest):
- Runtime-probe instead of existence-probe:
python3 -c 'print(0)' >/dev/null 2>&1 before committing to the python3 branch (the stub also fails this, exit 49).
- Fall through the chain on parse failure rather than returning empty (
jq → python3 → grep/sed as successive attempts, not exclusive branches).
- Prefer
python over python3 on MSYS/Windows, or check both.
The same existence-vs-runtime pattern may exist in other scripts that shell out to python3; worth a quick grep.
Workaround (for anyone else hitting this)
Install jq, disable the python3 App Execution Alias, or expose a real interpreter as python3 (e.g. one managed by uv python install).
Summary
On a stock Windows machine (Git for Windows only — no jq, no real Python on
PATH), every bash-variant script that reads.specify/feature.jsonfails withERROR: Feature directory not found ...immediately after a successfulspecify init --script sh. The cause is that the JSON parser fallback chain incommon.shselectspython3based on existence (command -v), but on Windowspython3resolves to the Microsoft Store App Execution Alias stub, which exists onPATHyet fails at runtime. The chain then swallows the runtime failure and returns an empty value, and thegrep/sedlast-resort parser is unreachable because the chain isif/elif/elseon availability, not on parse success.Environment
v0.12.3(installed viauv tool/uvxfrom the git tag)jqinstalled (Git for Windows does not ship one)PATH;C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\python3.exestub present (Windows default)Reproduction
feature.jsonexists and contains the key — it just can't be read:Root cause
scripts/bash/common.sh,read_feature_json_feature_directory()(v0.12.3, ~lines 95–120):create-new-feature.shmasks the problem on its own persist path (it falls back toprintffor writing), so the failure only appears one script later, which makes it look like state corruption rather than a parser issue.Notably, CI runners (GitHub Actions
windows-latestetc.) never see this: they ship bothjqand a realpython3. It only bites real end-user Windows machines — which are exactly the machines the sh-variant + Git Bash path is meant to serve.Suggested fix
Any of these would do (first is smallest):
python3 -c 'print(0)' >/dev/null 2>&1before committing to the python3 branch (the stub also fails this, exit 49).jq→python3→grep/sedas successive attempts, not exclusive branches).pythonoverpython3on MSYS/Windows, or check both.The same existence-vs-runtime pattern may exist in other scripts that shell out to
python3; worth a quick grep.Workaround (for anyone else hitting this)
Install jq, disable the
python3App Execution Alias, or expose a real interpreter aspython3(e.g. one managed byuv python install).