Skip to content

feat: native Windows support (detection, spawning, scheduling)#18

Open
9thLevelSoftware wants to merge 5 commits into
AlmanacCode:mainfrom
9thLevelSoftware:feat/windows-support
Open

feat: native Windows support (detection, spawning, scheduling)#18
9thLevelSoftware wants to merge 5 commits into
AlmanacCode:mainfrom
9thLevelSoftware:feat/windows-support

Conversation

@9thLevelSoftware

Copy link
Copy Markdown

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 main and fixes the paths #2 never touched (the live agent/readiness detection path, agent/auth/claude.ts, and process-group termination).

Root causes fixed

  • Detection (Not Detecting Codex CLI #1): the code shelled out to sh -lc 'command -v X' — there is no sh on native Windows, so every provider reported "not found". Replaced with a pure-Node PATH/PATHEXT scan in src/process/exec.ts; three duplicate commandExists copies collapsed into one.
  • Spawning: spawn() couldn't launch npm's .cmd/.ps1 shims. crossSpawn runs shims through cmd.exe /d /s /c with windowsVerbatimArguments and hand-quoted args (avoids shell:true, which is deprecated under DEP0190 and doesn't escape args).
  • Termination: process.kill(-pgid) is POSIX-only → taskkill /T /F on Windows; detached is disabled there (a new console breaks piped stdio; the tree is killed by pid).
  • Scheduling: launchd-only → new src/commands/automation/windows.ts using schtasks + per-task JSON manifests, behind a platform switch across install/status/uninstall/setup/uninstall/doctor.
  • Walk-up isolation: findNearestAlmanacDir no longer ascends above the home dir (the global ~/.almanac is already skipped); also keeps tests hermetic on Windows, where the OS temp dir lives under home.

Verification

  • npm run lint, npm run build, and npm test (548 tests) all green on native Windows; CI now runs ubuntu-latest + windows-latest across Node 20 and 22.
  • Verified on a real Windows machine: almanac doctor detects Codex ("Logged in using ChatGPT") and Claude, and an almanac init completed a full bootstrap run.
  • Automated review on the fork PR (gemini-code-assist, chatgpt-codex-connector) flagged 6 Windows-correctness issues — cmd.exe backslash quoting, quoted PATH entries, case-insensitive home compare, Windows npx-cache detection, and orphaned-task uninstall — all fixed with tests.

Implementation plan: docs/plans/2026-06-21-windows-support.md.

🤖 Generated with Claude Code

9thLevelSoftware and others added 5 commits June 21, 2026 14:31
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>
Copilot AI review requested due to automatic review settings June 21, 2026 20:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +172 to +176
} 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 });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

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.

Not Detecting Codex CLI

2 participants