feat: native Windows support (detection, spawning, scheduling)#18
feat: native Windows support (detection, spawning, scheduling)#189thLevelSoftware wants to merge 5 commits into
Conversation
Native Windows/PowerShell could not detect or run the agent CLIs because the codebase shelled out to `sh -lc 'command -v X'` (no `sh` on Windows) and spawned npm `.cmd`/`.ps1` shims without `shell: true` (refused by Node >=20). Fixes AlmanacCode#1. - Add src/process/exec.ts: pure-Node PATH/PATHEXT resolution (commandExists/resolveExecutable) and crossSpawn, which uses a shell only for Windows shims. Removes the `sh` dependency on every platform. - Route claude auth, codex readiness/status, and codex exec spawns through the shared module, collapsing three duplicate `commandExists` copies. - process-group: terminate via `taskkill /T /F` on Windows (POSIX `process.kill(-pgid)` is unavailable) and do not detach (a new console breaks piped stdio; the tree is killed by pid instead). - paths: stop the `.almanac` walk-up at the home directory. The global `~/.almanac` is already skipped; this also keeps tests hermetic on Windows, where the OS temp dir lives under the home dir. - Make the test suite portable on Windows: dirname() instead of slash-only regex, separator-agnostic path assertions, and a `.cmd` shim + path.delimiter for the fake codex CLI fixtures. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
macOS schedules auto-capture/garden/update via launchd plists; Windows had no equivalent, so `almanac setup`/`automation` failed natively. - Add src/commands/automation/windows.ts: install/status/uninstall via `schtasks`, recording a JSON manifest per task under `~/.almanac/automation/` so status/doctor can report what was installed. - Branch runAutomationInstall/Uninstall/Status on a `platform` option (defaults to process.platform), delegating to the Windows adapter. - setup/automation-step: thread `platform`; ephemeral installs use `almanac.cmd` program args on Windows instead of `/usr/bin/env almanac`. - uninstall: thread `platform` to the scheduler uninstall. - Tests pin launchd cases to platform "darwin" (so they keep exercising launchd on any host) and add a Windows Task Scheduler suite. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add src/install/ephemeral.ts (shared by setup and doctor) recognizing npx/dlx caches and OS temp dirs including Windows %TEMP%/%TMP%. - doctor: report the Windows Task Scheduler task (via the JSON manifest + `schtasks /Query`) instead of always probing a launchd plist. - install-path: install globally via `cmd.exe /d /s /c npm.cmd ...` on Windows, since npm is a `.cmd` shim that won't spawn without a shell. - CLI: describe automation as the platform scheduler, not macOS launchd. - CI: run the build/typecheck/test loop on windows-latest as well as ubuntu-latest, across Node 20 and 22. - README: document macOS/Linux/Windows support and the per-OS scheduler. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`shell: true` with an args array triggers Node's DEP0190 deprecation (args are concatenated, not escaped) and printed a warning on every Codex invocation. Launch shims through `cmd.exe /d /s /c` with windowsVerbatimArguments and a hand-quoted command line instead, and spawn the resolved executable directly for non-shim targets. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… tasks Resolves automated review findings on the Windows support PR: - exec.ts: strip surrounding double quotes from PATH entries (Windows can wrap dirs in quotes), so resolveExecutable doesn't build quoted paths. - exec.ts/windows.ts: fix cmd.exe arg quoting — double backslash runs that precede a quote and trailing backslash runs, so a path like `C:\My Projects\` can't escape the closing quote. - paths.ts: compare the home-dir walk-up boundary case-insensitively on Windows (drive-letter/path casing can differ), preserving the isolation. - install/ephemeral.ts: recognize the Windows npm-cache `_npx` location (via npm_config_cache / %LocalAppData%\npm-cache) as ephemeral so npx setups aren't treated as durable. - automation/windows.ts: uninstall attempts deletion of the deterministic task names even when the manifest is missing, so an orphaned schtasks job can't keep running. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 11d880b5a1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } catch { | ||
| // schtasks exits non-zero when the task does not exist; treat as | ||
| // "nothing to delete" rather than an error. | ||
| } | ||
| await rm(windowsManifestPath(taskId, home), { force: true }); |
There was a problem hiding this comment.
Propagate non-missing schtasks delete failures
On Windows, this catch treats every schtasks /Delete failure as “task not found” and then removes the manifest anyway. If deletion fails because Task Scheduler is unavailable, access is denied, or schtasks returns another real error, automation uninstall and gardenOff can report success while the scheduled task keeps running with no manifest left for status/doctor to reconcile; only ignore the specific missing-task case and otherwise surface the error without deleting the manifest.
Useful? React with 👍 / 👎.
Makes codealmanac work end-to-end on native Windows / PowerShell. Fixes #1 (Codex/Claude "not found on PATH") and the platform layers behind it. Supersedes the stale draft #2 — that branch was cut from v0.2.23 and only patched the old single-file codex provider; this re-applies the approach to current
mainand fixes the paths #2 never touched (the liveagent/readinessdetection path,agent/auth/claude.ts, and process-group termination).Root causes fixed
sh -lc 'command -v X'— there is noshon native Windows, so every provider reported "not found". Replaced with a pure-Node PATH/PATHEXT scan insrc/process/exec.ts; three duplicatecommandExistscopies collapsed into one.spawn()couldn't launch npm's.cmd/.ps1shims.crossSpawnruns shims throughcmd.exe /d /s /cwithwindowsVerbatimArgumentsand hand-quoted args (avoidsshell:true, which is deprecated under DEP0190 and doesn't escape args).process.kill(-pgid)is POSIX-only →taskkill /T /Fon Windows;detachedis disabled there (a new console breaks piped stdio; the tree is killed by pid).src/commands/automation/windows.tsusingschtasks+ per-task JSON manifests, behind aplatformswitch across install/status/uninstall/setup/uninstall/doctor.findNearestAlmanacDirno longer ascends above the home dir (the global~/.almanacis already skipped); also keeps tests hermetic on Windows, where the OS temp dir lives under home.Verification
npm run lint,npm run build, andnpm test(548 tests) all green on native Windows; CI now runs ubuntu-latest + windows-latest across Node 20 and 22.almanac doctordetects Codex ("Logged in using ChatGPT") and Claude, and analmanac initcompleted a full bootstrap run.Implementation plan:
docs/plans/2026-06-21-windows-support.md.🤖 Generated with Claude Code