Codex CLI on Windows: neo-tree markers, path handling, and shell-create detection#80
Merged
Merged
Conversation
… for Codex (#46) On Windows, Codex routes file edits through apply_patch with relative paths, which apply/patch.lua joins as `cwd .. "/" .. relpath`. cwd is backslashed, so the result has mixed separators (e.g. D:\proj\sub/file.txt). That string became the change-registry key, but neo-tree keys its tree nodes by native backslash paths, so the lookup missed and no created/modified/deleted marker rendered — even though the diff itself opened correctly (nvim accepts mixed separators when opening a file). Claude Code was unaffected because it sends fully-native paths. - changes.normalize(): fold to the OS-native separator on Windows. No-op on Unix, and a no-op for the already-native paths Claude Code sends. - neo_tree resolve_status(): the ancestor-deletion check hardcoded "/", which wouldn't match native backslash keys; use the native separator. - Add a Windows-gated regression spec for the mixed-separator key. - Ignore .codex/ (local hook install written by :CodePreviewInstallCodexCliHooks). Validated end-to-end on Windows 11 with codex-cli 0.137 (gpt-5.5): apply_patch update/create/delete and shell ops now mark correctly in neo-tree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The trailing-strip had broadened to [/\\]$ for the Windows separator fold, but that also stripped a trailing backslash on Unix — where "\" is a legal filename character, not a separator. Since neo_tree looks up the raw node.path against the normalized registry keys (asymmetric), a Unix file named `foo\` would key as `foo` and silently lose its marker. Branch the strip: fold + strip "\" on Windows, strip "/" only on Unix. Restores the true "no-op on Unix" property. Windows regression test still passes; registry spec + full suite green on macOS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#46) Codex emits absolute paths in apply_patch directives for some projects (`*** Update File: D:\proj\sub\file`). apply/patch.lua's resolve_path only treated a leading "/" as absolute, so a Windows-absolute path was joined onto cwd: `D:\proj` + "/" + `D:\proj\sub\file` = `D:\proj/D:\proj\sub\file`. That doubled path fs_stats as missing, so an existing file was mis-marked "created", the diff opened at a bogus path (wrong position, shown as a new file), and a junk neo-tree node was injected with the full doubled path. Recognize Windows drive-letter (`X:\` / `X:/`) and UNC (`\`) absolutes in resolve_path so they pass through unchanged. Adds a cross-platform regression spec. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n path (#46) post_tool.patch_paths kept a private copy of the cwd-join whose absolute check was Unix-only, so a Windows-absolute apply_patch path got doubled onto cwd on close. The diff opened under the clean path (apply/patch.lua resolve_path, already fixed) but PostToolUse tried to close a doubled one -> "no active diff, skipping" -> the neo-tree marker and diff never cleared after the user accepted the change. Export apply.patch.resolve_path and use it from post_tool so the close path is resolved identically to the open path (Windows drive-letter + UNC aware). One shared resolver means open/close can't diverge again. Adds a regression test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sitive) (#46) present_single_file initialised the diff-tab label to the absolute file path and only shortened it to a cwd-relative path when it could strip a `cwd .. "/"` prefix. On Windows file_path is backslashed while that prefix is forward-slashed, so the strip never fired and the tab showed the full absolute path (D:\...) instead of the relative one (macOS was unaffected). Extract display_path() with a separator-insensitive prefix test (fold both sides to "/" on Windows; slice the original file_path so the relative result keeps its native separators). Byte-identical on Unix. Adds a unit spec. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
handle_apply_patch labelled the diff tab with file.rel_path — the literal path from the `*** Update File:` directive. Some codex models (GPT 5.3) write an absolute path there, so the tab rendered `D:\...` instead of a cwd-relative label (the diff itself was always correct). Derive the label from the resolved file.path via display_path so it is relative regardless of what the model emits, matching the Edit/Write path. display_path is byte-identical on Unix and the label is unchanged for a relative-emitting model. Adds an integration regression. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#46) Codex/GPT on Windows creates files via `New-Item -ItemType File`, which the PowerShell shell-detect grammar didn't recognise, so the new file was never marked in neo-tree. Add `new-item`/`ni` as a create category routed through the existing write path (so a brand-new file becomes bash_created, like Out-File to a new file). `-ItemType Directory` is deliberately skipped (parity with mkdir and the locked `New-Item -ItemType Directory … | Out-Null → {}` spec row), and POSIX `touch` is left untouched. Adds spec rows incl. the two-file `; … | Out-Null` form codex emitted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#46) Codex on Windows is now validated end-to-end, so :checkhealth no longer needs to warn "Codex CLI on Windows is not yet validated … use Claude Code on Windows". Remove that Windows-only warning (its PowerShell shim path is the same one Claude Code uses) and refresh the adjacent comment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes the Codex CLI backend work correctly on Windows (validated end-to-end with
codex-cli 0.137, GPT 5.5 and GPT 5.3). The integration was already wired for Windows; this PR fixes the Windows-specific path/marker bugs that surfaced under real use, plus a couple of related quality/consistency fixes. Claude Code on Windows is unaffected, and Unix behaviour is preserved (see Unix safety below).What was broken & fixed
1. neo-tree markers silently dropped (path separators)
Codex routes edits through
apply_patchwith relative paths;apply/patch.luajoined them ascwd .. "/" .. relpath, and on Windowscwdis backslashed → a mixed-separator key (D:\proj\sub/file.txt). neo-tree keys nodes by native backslash paths, so the lookup missed and no created/modified/deleted marker rendered (the diff still opened). Claude Code was immune (it sends native paths).changes.lua—normalize()folds separators to the OS-native one on Windows (central key canonicalization).neo_tree.lua— the ancestor-deletion check used a hardcoded"/"; now uses the native separator.2. Existing file shown as "new file" (absolute-path doubling — open)
For some projects Codex emits absolute paths in
apply_patchdirectives (*** Update File: D:\proj\sub\file).resolve_pathonly treated a leading/as absolute, so a Windows-absolute path was doubled onto cwd (D:\proj/D:\proj\sub\file). That bogus pathfs_stats as missing → file mis-markedcreated, diff opened at the wrong position (rendered as a new file), and a junk neo-tree node appeared.apply/patch.lua—resolve_pathnow recognizes Windows drive-letter (X:\/X:/) and UNC (\) absolutes and passes them through.3. PostToolUse didn't clear the diff/marker after accept (open/close parity)
post_tool.luahad its own copy of the path-join with the same Unix-only absolute check, so the close path was still doubled while the open path was fixed →close_for_file: no active diff … skipping→ marker/diff never cleared.post_tool.lua— now reusesapply.patch.resolve_path(exported), so open and close resolve identically and can't diverge again.4. Diff-tab label showed the absolute path
On Windows the cwd-prefix strip compared
D:\proj\…againstD:\proj/(forward slash) and never matched, so the tab title fell back to the full absolute path. Additionally,apply_patchlabelled the tab withfile.rel_path— the literal directive path — which GPT 5.3 writes as absolute.pre_tool/init.lua— extracteddisplay_path()with a separator-insensitive cwd-prefix test (relative result keeps native separators); used for Edit/Write and apply_patch, so the label is cwd-relative regardless of OS or what the model emits.5.
New-Itemfile creation not detected (shell-create)Codex/GPT creates files on Windows via PowerShell
New-Item -ItemType File, which the shell-detect grammar didn't recognize → no neo-tree mark.shell_detect.lua— addednew-item/nias a create category routed through the existing write path (a brand-new file becomesbash_created, likeOut-Fileto a new file).-ItemType Directoryis deliberately skipped (parity withmkdirand the lockedNew-Item -ItemType Directory … | Out-Null → {}spec row); POSIXtouchis left untouched.6. Healthcheck no longer steers Codex users away
health.lua— removed the stale Windows-only warning "Codex CLI on Windows is not yet validated … use Claude Code on Windows" now that Codex is supported on Windows. (Copilot-on-Windows remains correctly deferred.)7. Housekeeping
.gitignore— ignore.codex/(local hook install written by:CodePreviewInstallCodexCliHooks), matching the existing opencode/copilot entries.Unix safety
Windows path transforms are either gated on
package.config:sub(1,1) == "\\"(changes.normalize,display_path) or use the native separatorpackage.config:sub(1,1)(neo_tree), so the Unix paths are byte-identical to before. Theresolve_pathabsolute-detection and theNew-Itemgrammar are not OS-gated but only match Windows-shaped input (X:\paths / a PowerShell cmdlet) that doesn't occur in real Unix workflows. No Unix workflow changes.Tests
Regression specs added/extended:
changes_registry_spec— mixed-separator key canonicalization (Windows-gated)apply_patch_spec— relative / Unix-absolute / Windows-absolute / UNC resolution (cross-platform)post_tool_handle_spec— Windows-absolute patch path not doubled on closedisplay_path_spec— separator-insensitive label + an integration test drivingpre_tool.handlewith an absolute-path codex patchpre_tool_shell_detect_spec—New-Itemfile create (incl. the exact; … | Out-Nulltwo-file command),nialias, and directory-create still{}Full plugin spec suite passes on Windows (per-file;
PlenaryBustedDirectoryhangs headless on Windows).diff_lifecycle_specfails 13/14 headless on Windows onorigin/maintoo — a pre-existing UI limitation, not introduced here.Validated end-to-end on Windows 11 with
codex-cli 0.137(GPT 5.5 / 5.3) and Claude Code:apply_patch(relative & absolute) and PowerShell shell ops mark correctly, open the diff at the right position with a relative tab label, and clear on accept.Relates to #46.