Skip to content

Make the repo work on Windows without WSL (and fix a <total_tokens> cache invalidation bug) - #152

Open
SiNaPsEr0x wants to merge 5 commits into
teamchong:mainfrom
SiNaPsEr0x:fix/windows-cross-platform
Open

Make the repo work on Windows without WSL (and fix a <total_tokens> cache invalidation bug)#152
SiNaPsEr0x wants to merge 5 commits into
teamchong:mainfrom
SiNaPsEr0x:fix/windows-cross-platform

Conversation

@SiNaPsEr0x

Copy link
Copy Markdown

Summary

Makes the repo usable on Windows without WSL, and fixes one platform-independent
cache bug found along the way.

On a clean Windows clone, pnpm run restart failed immediately:

scripts/restart.sh: riga 27: $'\r': comando non trovato
scripts/restart.sh: riga 115: exec: node: non trovato

Two separate causes: the repo had no .gitattributes, so shell scripts were
checked out with CRLF; and restart.sh relied on pgrep/kill/exec plus a
POSIX-only PATH lookup.

Changes

.gitattributes (new) — normalizes line endings so checkouts are consistent
across platforms, and forces LF on files carrying a shebang so they stay
executable when cloned from Windows. Binary assets (.png, .ico, .woff2, …)
are marked explicitly to avoid corruption.

scripts/restart.shscripts/restart.mjs — reimplemented in Node, no
shell dependency. Process discovery and termination now go through
platform-appropriate paths (ps/kill on POSIX, Get-CimInstance +
Get-NetTCPConnection on Windows), with the same graceful-then-forceful
shutdown sequence and the same port-holder diagnostics as before. Behaviour on
Linux/macOS is unchanged.

tests/restart.test.shtests/restart.test.ts — the restart suite now
runs under vitest on every platform instead of requiring bash. This also drops
the separate test:restart script, since pnpm test covers it.

tests/node-security.test.ts — POSIX permission-bit assertions are skipped
on Windows, where the filesystem has no equivalent concept. The assertions still
run everywhere else.

src/core/transform.ts<total_tokens> added to DYNAMIC_BLOCK_TAGS.

The cache bug (not Windows-specific)

<total_tokens> was being treated as static content. It changes on every single
turn, so it was landing in the static slab and rewriting the cached image each
turn — dropping the cache hit rate to zero for any conversation long enough to
reach the image path. It also surfaced as recurring unknownStaticTags noise in
the logs, which is what led me to it.

This one is worth reviewing independently of the Windows work: it affects every
platform.

Testing

Full suite green on Windows: 923 tests across 51 files. The restart suite is
included in pnpm test now, so it runs in CI on all platforms rather than only
where bash is available.

I don't have a macOS or Linux box to verify on, so a second pair of eyes on the
POSIX branch of restart.mjs would be welcome — the logic is a direct port, but
it is the part I could not exercise myself.

claude added 5 commits July 26, 2026 20:57
Cloning on Windows with core.autocrlf=true rewrote every shell script with
CRLF, so bash refused to run them:

    scripts/restart.sh: line 27: $'\r': command not found

Pin *.sh and *.bash to LF, let the rest use text=auto, and mark the binary
asset types so they are never converted. No tracked file is renormalized
here — the attributes take effect on the next checkout.
scripts/restart.sh only worked on macOS/Linux and broke on Windows:
it relied on pgrep/lsof/kill and on `exec node`, which are unavailable
or behave differently under Git Bash / PowerShell.

- Rewrite it as scripts/restart.mjs, a plain Node ESM script with no
  external shell dependencies.
- Process discovery: `ps` + pgrep-style matching on POSIX, and
  Get-CimInstance Win32_Process on Windows (its ConvertTo-Json output is
  normalized by hand, since PowerShell emits a bare object for a single
  match and nothing at all for none), matching the proxy by its actual
  command line so unrelated `node` processes are never touched.
- Termination: SIGTERM then SIGKILL escalation on POSIX, taskkill /T then
  taskkill /T /F on Windows, both with the same 5s grace period as before.
- Port ownership diagnostics use lsof on POSIX and
  Get-NetTCPConnection on Windows.
- Port the shell test suite to tests/restart.test.ts (vitest), so it runs
  on every platform instead of requiring bash.
- package.json: `restart` now runs `node scripts/restart.mjs`, and the
  `test:restart` entry is dropped because the ported suite is picked up
  by the regular vitest run.
NTFS has no POSIX mode bits: fs.statSync().mode reports 0o666 for every
file and directory, and chmodSync() only toggles the read-only flag, so
node-security.test.ts failed on Windows even though the hardening code
in src/node.ts is correct and still runs there.

Route the mode checks through an expectMode() helper that asserts the
exact 0o600/0o700 bits only on platforms that can express them. The rest
of each test — the request outcome, the file being created at all — keeps
running everywhere.
`total_tokens` changes on every single turn, but it was listed in neither
DYNAMIC_BLOCK_TAGS nor the known-static set, so it stayed in the static
slab. That rewrote the cached image on each turn and reduced the prefix
cache hit rate to zero for long sessions (it also showed up as a
recurring "unknown static tag" churn warning at runtime).

Add it to DYNAMIC_BLOCK_TAGS and add a cache-stability e2e case that
fails if a changing token counter ever leaks back into the static slab.
The previous .gitattributes only pinned *.sh/*.bash to LF. With
core.autocrlf=true a Windows clone still checked out bin/cli.js and
scripts/*.mjs with CRLF, so the '#!/usr/bin/env node' line kept a
trailing \r: Vite's shebang strip missed it and importing
scripts/restart.mjs from a test failed with 'Invalid or unexpected
token'. Pin *.js/*.mjs/*.cjs/*.py to LF too.
@SiNaPsEr0x
SiNaPsEr0x force-pushed the fix/windows-cross-platform branch from 2ab90e5 to c6189b1 Compare July 26, 2026 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants