diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 8753d3388..a6bcd3e60 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,6 +1,6 @@ # GitNexus Devcontainer -A cross-platform Dev Container that pre-installs Claude Code, OpenAI Codex CLI, and Cursor CLI alongside the GitNexus native build chain. Supported hosts: **macOS, Linux, and Windows 11 via WSL2** (Windows-native is unsupported — see below). +A cross-platform Dev Container that pre-installs Claude Code, OpenAI Codex CLI, and Cursor CLI alongside the GitNexus native build chain. Supported hosts: **macOS, Linux, Windows 11 via WSL2 (strongly recommended), and Windows 11 native (works but slower, with more bind-mount edge cases — see below).** ## Quick start @@ -10,9 +10,15 @@ A cross-platform Dev Container that pre-installs Claude Code, OpenAI Codex CLI, 4. Wait for the first build (~3–6 minutes) and `postCreateCommand` to finish installing workspace dependencies. 5. Authenticate the three CLIs once — see [First-time CLI authentication](#first-time-cli-authentication) below. -## Windows 11 — WSL2 is required +## Windows 11 — WSL2 is strongly recommended -**Windows-native is unsupported.** The devcontainer bind-mounts host config dirs via `${localEnv:HOME}/.claude` (and `.codex`, `.cursor`, `.gitconfig`, `.config/gh`). On Windows-native, the host has `USERPROFILE` set but no `HOME` — VS Code resolves the missing `HOME` to an empty string and Docker tries to bind-mount paths from filesystem root, which silently breaks the host-sync feature. The same checkout-from-Windows-side path also has poor IO and unreliable file watchers (Vite/jest `--watch` will miss changes). The fix is to clone and open the repo inside WSL2: +Both WSL2 and Windows-native checkouts work. WSL2 is meaningfully better: + +- **File-watcher reliability.** Vite/jest `--watch` see file changes reliably on the WSL2 filesystem; Windows-native bind-mounts miss events intermittently. +- **`npm install` performance.** WSL2-side IO is ~3-5× faster for the heavy `gitnexus` install (tree-sitter native bindings, onnxruntime, vendored grammars). +- **No bind-mount permission edge cases.** Docker Desktop's Windows bind-mount permission translation can EPERM when a previous container created a file (e.g., `.husky/_/h`) and a new container with a different effective UID tries to overwrite it. WSL2-side filesystems don't have this class of issue (the `post-create.sh` defensive `rm -rf .husky/_` mitigates the most common case but isn't a full fix). + +To clone and open the repo inside WSL2: ```bash # 1. Install WSL2 and a Linux distro if you haven't already. @@ -179,7 +185,7 @@ Bump `CLAUDE_CODE_VERSION` and `CODEX_VERSION` in `.devcontainer/devcontainer.js | Symptom | Likely cause | Fix | |---------|--------------|-----| -| `EACCES` / `EPERM` writing into `~/.claude`, `~/.codex`, or `~/.cursor` inside the container | Windows-side bind-mount permission translation got out of sync after a UID change between rebuilds | Move to WSL2 — Windows-native isn't supported. See [Windows 11 — WSL2 is required](#windows-11--wsl2-is-required) | +| `EACCES` / `EPERM` writing into `~/.claude`, `~/.codex`, or `~/.cursor` inside the container | Windows-side bind-mount permission translation got out of sync after a UID change between rebuilds | Move the affected dir aside and let the CLI rebuild it (`mv ~/.claude ~/.claude.bak` and log in again). Long-term: clone in WSL2 — that filesystem doesn't hit this class of issue. See [Windows 11 — WSL2 is strongly recommended](#windows-11--wsl2-is-strongly-recommended) | | `EPERM: operation not permitted, copyfile ... '.husky/_/h'` in `postCreateCommand` | Leftover `.husky/_/` from a previous container run on a Windows-side bind mount | `post-create.sh` already runs `rm -rf .husky/_` defensively. If you hit this on an older config, delete `.husky/_/` on the host and rebuild. Long-term: clone in WSL2 | | Vite never hot-reloads | Repo cloned on Windows side, not WSL2 | Re-clone inside WSL2 | | `gitnexus-web` can't reach the backend | `4747` was remapped or backend isn't running | Verify the Ports panel shows `4747` forwarded with no remap; start the backend with `cd gitnexus && npx gitnexus serve` | diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 388dfc81e..cae21a5ed 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -21,10 +21,17 @@ // Runs on the HOST shell before the container is created. Guarantees the // bind-mount source directories below exist so Docker doesn't reject the - // mount when a CLI has never been used on this host. Idempotent; safe to - // re-run. POSIX-shell only (Linux / macOS / WSL2) — Windows-native is - // out of scope per README's WSL2-required posture. - "initializeCommand": "mkdir -p $HOME/.claude $HOME/.codex $HOME/.cursor $HOME/.config/gh && touch $HOME/.gitconfig", + // mount when a CLI has never been used on this host. OS-keyed because + // VS Code runs the host shell in its native form (POSIX on Linux/macOS, + // cmd.exe on Windows — which means `mkdir -p` + `$HOME` won't work on + // Win32). All three branches are idempotent. WSL is covered by the + // linux branch because VS Code's WSL extension runs initializeCommand + // in the WSL shell. + "initializeCommand": { + "linux": "mkdir -p $HOME/.claude $HOME/.codex $HOME/.cursor $HOME/.config/gh && touch $HOME/.gitconfig", + "darwin": "mkdir -p $HOME/.claude $HOME/.codex $HOME/.cursor $HOME/.config/gh && touch $HOME/.gitconfig", + "win32": "powershell -NoProfile -Command \"$d=$env:USERPROFILE; foreach ($p in '.claude','.codex','.cursor','.config\\gh') { $f=Join-Path $d $p; if (-not (Test-Path $f)) { New-Item -ItemType Directory -Force -Path $f | Out-Null } }; if (-not (Test-Path (Join-Path $d '.gitconfig'))) { New-Item -ItemType File -Path (Join-Path $d '.gitconfig') | Out-Null }\"" + }, "features": { "ghcr.io/devcontainers/features/github-cli:1": {}