Skip to content

fix(tools): resolve external tools on Windows - #175

Closed
jz-wilson wants to merge 1 commit into
AltanS:mainfrom
jz-wilson:fix/windows-tool-resolution
Closed

fix(tools): resolve external tools on Windows#175
jz-wilson wants to merge 1 commit into
AltanS:mainfrom
jz-wilson:fix/windows-tool-resolution

Conversation

@jz-wilson

Copy link
Copy Markdown
Contributor

What

bridge/tools.ts assumes a POSIX environment in three ways. On Windows all three fail closed, so every external tool resolves as "not installed on this host".

The user-visible symptom is on the phone: "The preflight couldn't be run on this machine." The chain behind it is findTool("git") → nullisGitCheckout false → install kind unknowncollie update refuses. collie doctor reports the same machine as having no herdr and no python3 while both sit on PATH, and collie update's own build step fails with bun not found.

The three

  1. PATH split on ":". Windows separates with ;, so the split does not merely miss entries — it shreds each one at its drive letter (C:\Program Files\Git\cmd becomes C and \Program Files\Git\cmd). searchDirs' absolute-only filter then drops every fragment and PATH contributes nothing at all. Now node:path's delimiter.

  2. No PATHEXT. The bare name was joined to each directory as-is, but the executable is git.exe, herdr.exe, python3.exe. findIn now takes an exts list and tries each suffix within a directory before moving on, bare name first, so PATH order still decides. The parameter defaults to [""] and toolExts returns [""] off win32 — the search is byte-for-byte unchanged on Linux and macOS.

  3. env.PATH misses Windows' Path spelling. Case-insensitivity survives only while the environment is the live process.env proxy; this module is handed plain copies of it (the CLI merges .env over the ambient environment before building Exec). So PATH and PATHEXT are read case-insensitively on win32.

The third one is why this was not caught earlier by anyone poking at it from a terminal: the same binary passes under Git Bash, which exports the name uppercase, and fails under PowerShell — which is what a Windows bridge actually runs under.

Verification

On Windows 11, against a Herdr-managed checkout at v1.5.3, collie doctor goes from

error: herdr-version   no `herdr` on this host
error: hook-python3    no `python3` on PATH
warn:  install         cannot tell how this Collie was installed (… has no .git of its own …)

to herdr 0.8.2, python3.EXE, and Herdr-managed checkout … (detached at 1.5.3). collie update --check --local --json then produces a real preflight (bun green, tree green, upstream green) instead of update.preflight_unavailable, and the Updates page on the phone works.

Checks run on Linux, all green: bun run typecheck, cd web && bun run typecheck, bun run lint (0 warnings, 0 errors), bun test ./cli/tools.test.ts ./bridge/dial.test.ts (23 pass, 3 skip), and cd web && bun run test (181 files, 5050 pass).

Notes

  • No version bump, per CONTRIBUTING's fork rule. Suggested ## [Unreleased] entry:
    - Fixed: external tools (git, herdr, python3, bun) now resolve on Windows, so doctor and update work there.
  • New tests cover the suffix search cross-platform and toolExts on both sides of the platform check; the three win32-only assertions skipIf off Windows.
  • Two things remain Windows-rough and are deliberately not in this PR, since each is a separate decision: the manifest skips build on win32 so there is no bin/collie for canRunUpdate to find (and a bun build --compile there emits bin/collie.exe, not the bare name the bridge checks), and doctor's disk check cannot read free space on Windows. Happy to follow up on either.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UDQT5GAR9qRs6ukXrmDzhL

The tool search assumes a POSIX environment in three ways, and on Windows all
three fail closed — every tool reads as "not installed on this host", so
`doctor` reports no herdr and no python3 on a machine that has both on PATH,
and `update` cannot classify the install because it cannot find `git` to ask.
Downstream of that, the phone is told the update preflight cannot be run here.

- PATH was split on ":", which on Windows does not merely miss entries but
  shreds each one at its drive letter (`C:\Program Files\Git\cmd` becomes `C`
  and `\Program Files\Git\cmd`), so the absolute-only filter drops the lot and
  PATH contributes nothing at all. Use node:path's `delimiter`.
- A bare name was joined to each directory as-is, but on Windows the
  executable is `git.exe`, `herdr.exe`, `python3.exe`. `findIn` now tries each
  suffix within a directory, bare name first, so PATH order still decides.
  Off win32 the suffix list is a lone "" and the search is unchanged.
- `env.PATH` misses Windows' `Path` spelling. Case-insensitivity survives only
  while the environment is the live `process.env` proxy, and this module is
  handed plain copies of it — so PATH and PATHEXT are read case-insensitively
  on win32. This is the one that hid the other two: the same binary passed
  under Git Bash, which exports the name uppercase, and failed under
  PowerShell.

Verified on Windows 11 against a Herdr-managed checkout: `collie doctor` now
resolves herdr 0.8.2, python3 and git and classifies the install, and
`collie update --check` produces a real preflight. `bun test ./cli/tools.test.ts`
is green on Linux, where the three win32-only assertions skip.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UDQT5GAR9qRs6ukXrmDzhL
@github-actions github-actions Bot added area: bridge Bun/TS bridge — Herdr socket, state engine, polling, REST area: install bin/collie verbs, Herdr plugin lifecycle, update, tailscale serve platform: windows Windows host — named-pipe bridge, contrib/windows labels Sep 6, 2026
AltanS added a commit that referenced this pull request Sep 7, 2026
The win32 branches landed in #175 read `process.platform` directly, so the
only host that could exercise them was a Windows host, which we do not have.
`searchDirs`, `toolExts` and `findTool` now take a trailing
`platform: NodeJS.Platform = process.platform` argument, the way
`bridge/config.ts`'s `defaultSocketPath` already takes its own, and the
internal `envGet` takes it as a required third argument. Callers pass nothing
and behave as before; off win32 every path is byte-identical.

`cli/tools.test.ts` drops its four `skipIf` guards and passes the platform
instead, and two new cases pin `findTool`: a `.cmd` shim on PATH resolves
under `"win32"` and is not matched under `"linux"`.

CHANGELOG credits @jz-wilson (#175) for the Windows tool resolution.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@AltanS

AltanS commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Thanks Johnzell, this lands. Your commit is on main as d80ebbe with your authorship, plus one of mine that makes the platform an injected parameter so the Windows cases run on our Linux CI instead of being skipped, and the changelog names the .cmd shim limit. It ships in 1.6.0.

@AltanS AltanS closed this Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: bridge Bun/TS bridge — Herdr socket, state engine, polling, REST area: install bin/collie verbs, Herdr plugin lifecycle, update, tailscale serve platform: windows Windows host — named-pipe bridge, contrib/windows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants