feat: native Windows support (detection, spawning, scheduling)#1
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>
There was a problem hiding this comment.
Code Review
This pull request introduces comprehensive native Windows support for CodeAlmanac, including cross-platform process execution, Windows Task Scheduler integration, and platform-aware setup and doctor checks. The feedback highlights critical Windows-specific edge cases that need to be addressed: stripping double quotes from directories in the PATH environment variable, correctly handling trailing backslashes in Windows argument quoting functions to prevent escaping closing quotes, and performing case-insensitive path comparisons to ensure the home directory boundary check works reliably on Windows.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e5c30bfee4
ℹ️ 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".
Code Review Roast 🔥Verdict: All Issues Resolved | Recommendation: Merge Overview
Resolved Issues
🏆 Best part: The backslash-fix was applied consistently across both 💀 Worst part: There isn't one. I'm almost disappointed. I sharpened my knives for this review. 📊 Overall: Like watching a junior dev grow into a senior — the bugs from the last review are gone, the tests are comprehensive, and the quoting logic is now bulletproof. This PR is clean. Files Reviewed (6 files, incremental)
Fix these issues in Kilo Cloud Previous Review Summary (commit e5c30bf)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit e5c30bf)Verdict: 6 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)
🏆 Best part: The architecture decision to centralize cross-platform process spawning into a single module is solid. Collapsing 3 duplicated 💀 Worst part: The Windows quoting logic has a classic cmd.exe footgun - trailing backslashes in paths silently break argument escaping. This is the kind of bug that only shows up in production when someone's npm bin is under 📊 Overall: Like teaching a fish to ride a bicycle — impressive center of gravity, but those backslash wounds are gonna leave a mark. Files Reviewed (15 files)
Reviewed by step-3.7-flash-20260528 · Input: 124.1K · Output: 4.3K · Cached: 27.2K |
… 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".
| if ((options.platform ?? process.platform) === "win32") { | ||
| return installWindowsAutomation({ | ||
| home: options.homeDir ?? homedir(), | ||
| jobs: plan.value.jobs.map(toWindowsJob), | ||
| disabledTaskIds: plan.value.disabledGardenPlistPath !== null ? ["garden"] : [], | ||
| captureSince, |
There was a problem hiding this comment.
Record capture baseline only after install succeeds
On Windows this branch calls installWindowsAutomation only after ensureAutomationCaptureSince has already written automation.capture_since. If windowsSchedule rejects the interval (for example almanac automation install --every 30s) or schtasks /Create fails, the command exits non-zero but the baseline has advanced, so a later successful install skips transcripts from before the failed attempt. Validate/create the Windows tasks before recording the capture baseline, or roll the baseline back on failure.
Useful? React with 👍 / 👎.
| } 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.
Surface unexpected schtasks delete failures
When schtasks /Delete fails for anything other than an absent task, such as access denied or the scheduler service being unavailable, this catch still removes the manifest below; uninstallWindowsAutomation then reports success whenever the manifest existed, leaving a live task with no manifest for status/doctor. Microsoft documents /f as only suppressing the confirmation message (docs), so preserve the manifest and surface unexpected delete errors instead of swallowing every non-zero exit.
Useful? React with 👍 / 👎.
| @@ -60,6 +75,12 @@ export function findNearestAlmanacDir(startDir: string): string | null { | |||
| if (candidate !== globalDir && existsSync(candidate)) { | |||
There was a problem hiding this comment.
Compare global wiki paths case-insensitively
On Windows this comparison still uses strict string equality for the global ~\.almanac skip. If process.cwd() reaches the home directory with different casing than homedir() (for example c:\users\dev vs C:\Users\Dev), existsSync(candidate) succeeds against the global state directory and the function returns the home directory as a wiki root, causing plain directories under home to be auto-registered as a repo. Use the new samePath(candidate, globalDir) check here too.
Useful? React with 👍 / 👎.
Makes codealmanac work end-to-end on native Windows / PowerShell. Fixes the Codex/Claude "not found on PATH" problem from AlmanacCode#1 and adds the missing platform layers behind it.
Re-applies the approach from the stale draft AlmanacCode#2 onto the current (refactored) codebase, and fixes the layers that PR never touched: the live
agent/readinessdetection path,agent/auth/claude.ts, and process-group termination.Root causes fixed
sh -lc 'command -v X'— noshon native Windows, so every provider read "not found". Replaced with a pure-Node PATH/PATHEXT scan (src/process/exec.ts); 3 duplicate copies collapsed into 1.spawn()couldn't launch npm's.cmd/.ps1shims.crossSpawnnow runs shims viacmd.exe /d /s /cwithwindowsVerbatimArguments(avoidsshell:true, which trips Node's DEP0190 and doesn't escape args).process.kill(-pgid)is POSIX-only →taskkill /T /Fon Windows; nodetached(it broke piped stdio).automation/windows.tsusingschtasks+ per-task JSON manifests, behind aplatformswitch across install/status/uninstall/setup/uninstall/doctor..almanacwalk-up ascended above the home dir; bounded at home (the global~/.almanacwas already skipped). Also makes tests hermetic on Windows, where the OS temp dir lives under home.Verification
npm run lint,npm run build, andnpm test(540/540) all green on native Windows.almanac doctordetects Codex ("Logged in using ChatGPT") and Claude, with no deprecation noise.Implementation plan:
docs/plans/2026-06-21-windows-support.md.🤖 Generated with Claude Code