Skip to content

Codex CLI on Windows: neo-tree markers, path handling, and shell-create detection#80

Merged
Cannon07 merged 8 commits into
mainfrom
fix/windows-codex-neotree-markers
Jun 8, 2026
Merged

Codex CLI on Windows: neo-tree markers, path handling, and shell-create detection#80
Cannon07 merged 8 commits into
mainfrom
fix/windows-codex-neotree-markers

Conversation

@Cannon07

@Cannon07 Cannon07 commented Jun 7, 2026

Copy link
Copy Markdown
Owner

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_patch with relative paths; apply/patch.lua joined them as cwd .. "/" .. relpath, and on Windows cwd is 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.luanormalize() 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_patch directives (*** Update File: D:\proj\sub\file). resolve_path only treated a leading / as absolute, so a Windows-absolute path was doubled onto cwd (D:\proj/D:\proj\sub\file). That bogus path fs_stats as missing → file mis-marked created, diff opened at the wrong position (rendered as a new file), and a junk neo-tree node appeared.

  • apply/patch.luaresolve_path now 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.lua had 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 reuses apply.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\… against D:\proj/ (forward slash) and never matched, so the tab title fell back to the full absolute path. Additionally, apply_patch labelled the tab with file.rel_path — the literal directive path — which GPT 5.3 writes as absolute.

  • pre_tool/init.lua — extracted display_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-Item file 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 — added new-item/ni as a create category routed through the existing write path (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); POSIX touch is 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 separator package.config:sub(1,1) (neo_tree), so the Unix paths are byte-identical to before. The resolve_path absolute-detection and the New-Item grammar 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 close
  • display_path_spec — separator-insensitive label + an integration test driving pre_tool.handle with an absolute-path codex patch
  • pre_tool_shell_detect_specNew-Item file create (incl. the exact ; … | Out-Null two-file command), ni alias, and directory-create still {}

Full plugin spec suite passes on Windows (per-file; PlenaryBustedDirectory hangs headless on Windows). diff_lifecycle_spec fails 13/14 headless on Windows on origin/main too — 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.

Cannon07 and others added 3 commits June 8, 2026 00:38
… 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>
@Cannon07 Cannon07 changed the title fix(windows): neo-tree markers for Codex apply_patch (path separator canonicalization) fix(windows): Codex apply_patch path handling (separators + absolute paths) Jun 8, 2026
Cannon07 and others added 5 commits June 8, 2026 14:20
…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>
@Cannon07 Cannon07 changed the title fix(windows): Codex apply_patch path handling (separators + absolute paths) Codex CLI on Windows: neo-tree markers, path handling, and shell-create detection Jun 8, 2026
@Cannon07
Cannon07 merged commit a5e9be3 into main Jun 8, 2026
3 checks passed
@Cannon07
Cannon07 deleted the fix/windows-codex-neotree-markers branch June 9, 2026 06:40
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.

1 participant