[PR #6082] fix(windows): better support MSYS/Git Bash path handling #11727

Closed
opened 2026-02-16 18:16:40 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/anomalyco/opencode/pull/6082

State: closed
Merged: No


Fix MSYS/Git Bash path handling on Windows

Summary

  • Add toNativePath() to convert MSYS/Git Bash/Cygwin paths (/c/Users/...) to Windows paths (C:\Users\...)
  • Add safeRelative() to handle cross-drive paths and deep parent traversals
  • Fix TUI path display with Locale.truncateMiddle for long paths

Details

When running opencode in Git Bash on Windows, the shell uses POSIX-style paths (/f/test/file.txt) but Node.js/Bun APIs expect Windows paths (F:\test\file.txt). This caused:

  1. File operations failing - ENOENT: no such file or directory because Node can't resolve /f/... paths
  2. Garbage display - path.relative() across different drives produced ..\..\..\..\..\..\f\test
  3. Long paths unreadable - No truncation for deeply nested paths

Before

→ Read ..\..\..\..\..\..\..\f\test\file.txt
Error: ENOENT: no such file or directory

After

→ Read F:\test\file.txt

Files Changed

  • packages/opencode/src/util/filesystem.ts - Added toNativePath() and safeRelative()
  • packages/opencode/src/tool/*.ts - Applied path conversion at entry points (read, write, edit, glob, grep, ls, patch)
  • packages/opencode/src/cli/cmd/tui/routes/session/index.tsx - Fixed TUI display with path abbreviation

Test Cases

Setup (run once in Git Bash)

# Cross-drive test files (F: drive)
mkdir -p /f/test
echo "hello world foo bar" > /f/test/test.txt
echo "another hello" > /f/test/test2.txt

# Long nested path on different drive
mkdir -p /f/test/very/deeply/nested/folder/structure/that/is/really/long
echo "deeply nested content" > /f/test/very/deeply/nested/folder/structure/that/is/really/long/file.txt

# Parent traversal test files (same drive, different depths)
mkdir -p /c/Users/<user_name>/repo/sst/testdir
echo "2 levels up" > /c/Users/<user_name>/repo/sst/testdir/two-up.txt

mkdir -p /c/Users/<user_name>/repo/testdir
echo "3 levels up" > /c/Users/<user_name>/repo/testdir/three-up.txt

# Long nested path inside worktree
mkdir -p /c/Users/<user_name>/repo/sst/opencode/very/deeply/nested/folder/structure/that/is/really/long
echo "deep test" > /c/Users/<user_name>/repo/sst/opencode/very/deeply/nested/folder/structure/that/is/really/long/file.txt

I don't touch LSP or MultipleEdit tools because unsure about those. (But it's working by default)

Feature 1: MSYS/Git Bash Path Conversion

Tests that /c/... and /f/... paths are converted to C:\... and F:\....

LIST Tool

Use the LIST tool to show all files in /f/test/

Expected: → List F:\test (not ..\..\..\..\f\test)

READ Tool

Use the READ tool to read /f/test/test.txt

Expected: → Read F:\test\test.txt

WRITE Tool

Use the WRITE tool to create /f/test/write-test.txt with content "written by opencode"

Expected: → Wrote F:\test\write-test.txt

EDIT Tool

Use the EDIT tool to replace "hello" with "goodbye" in /f/test/test.txt

Expected: → Edit F:\test\test.txt

GLOB Tool

Use the GLOB tool to find all *.txt files in /f/test/

Expected: → ✱ Glob "*.txt" in F:\test\ (4 matches)

GREP Tool

Use the GREP tool to search for "hello" in /f/test/

Expected: → ✱ Grep "hello" in F:\test\ (1 matches)


Feature 2: Parent Traversal Threshold (3+)

Paths with 3+ parent traversals (../../..) show absolute path. Paths with 0-2 show relative.

2 levels up - shows RELATIVE

Use the READ tool to read /c/Users/<user_name>/repo/sst/opencode/PR.md

Expected: → Read ..\..\.PR.md (relative path)

3+ levels up - shows ABSOLUTE

Use the READ tool to read /c/Users/<user_name>/repo/testdir/three-up.txt

Expected: → Read C:\Users\<user_name>\repo\testdir\three-up.txt (absolute path)

Use the READ tool to read /c/Users/<user_name>/.config/opencode/opencode.jsonc

Expected: → Read C:\Users\<user_name>\.config\opencode\opencode.jsonc (absolute path)


Feature 3: Long Path Truncation

Long paths are truncated with ... in the middle, using Locale.truncateMiddle.

Long path on different drive

Use the READ tool to read /f/test/very/deeply/nested/folder/structure/that/is/really/long/file.txt

Expected: → Read F:\test\very\deeply\…\that\is\really\long\file.txt (truncated)

Long path inside worktree

Use the READ tool to read /c/Users/<user_name>/repo/sst/opencode/very/deeply/nested/folder/structure/that/is/really/long/file.txt

Expected: → Read ..\..\very\deeply\ne…\that\is\really\long\file.txt (truncated relative path)


Checklist

  • No-op on non-Windows platforms for toNativePath() (gated by process.platform === "win32")
  • Parent traversal check (3+) applies to ALL platforms
  • Uses existing Locale.truncateMiddle for path abbreviation (consistent with sidebar)
  • No breaking changes for normal Windows or Unix paths
**Original Pull Request:** https://github.com/anomalyco/opencode/pull/6082 **State:** closed **Merged:** No --- # Fix MSYS/Git Bash path handling on Windows ## Summary - Add `toNativePath()` to convert MSYS/Git Bash/Cygwin paths (`/c/Users/...`) to Windows paths (`C:\Users\...`) - Add `safeRelative()` to handle cross-drive paths and deep parent traversals - Fix TUI path display with `Locale.truncateMiddle` for long paths ## Details When running opencode in Git Bash on Windows, the shell uses POSIX-style paths (`/f/test/file.txt`) but Node.js/Bun APIs expect Windows paths (`F:\test\file.txt`). This caused: 1. **File operations failing** - `ENOENT: no such file or directory` because Node can't resolve `/f/...` paths 2. **Garbage display** - `path.relative()` across different drives produced `..\..\..\..\..\..\f\test` 3. **Long paths unreadable** - No truncation for deeply nested paths ### Before ``` → Read ..\..\..\..\..\..\..\f\test\file.txt Error: ENOENT: no such file or directory ``` ### After ``` → Read F:\test\file.txt ``` ## Files Changed - `packages/opencode/src/util/filesystem.ts` - Added `toNativePath()` and `safeRelative()` - `packages/opencode/src/tool/*.ts` - Applied path conversion at entry points (read, write, edit, glob, grep, ls, patch) - `packages/opencode/src/cli/cmd/tui/routes/session/index.tsx` - Fixed TUI display with path abbreviation ## Test Cases ### Setup (run once in Git Bash) ```bash # Cross-drive test files (F: drive) mkdir -p /f/test echo "hello world foo bar" > /f/test/test.txt echo "another hello" > /f/test/test2.txt # Long nested path on different drive mkdir -p /f/test/very/deeply/nested/folder/structure/that/is/really/long echo "deeply nested content" > /f/test/very/deeply/nested/folder/structure/that/is/really/long/file.txt # Parent traversal test files (same drive, different depths) mkdir -p /c/Users/<user_name>/repo/sst/testdir echo "2 levels up" > /c/Users/<user_name>/repo/sst/testdir/two-up.txt mkdir -p /c/Users/<user_name>/repo/testdir echo "3 levels up" > /c/Users/<user_name>/repo/testdir/three-up.txt # Long nested path inside worktree mkdir -p /c/Users/<user_name>/repo/sst/opencode/very/deeply/nested/folder/structure/that/is/really/long echo "deep test" > /c/Users/<user_name>/repo/sst/opencode/very/deeply/nested/folder/structure/that/is/really/long/file.txt ``` --- ### I don't touch LSP or MultipleEdit tools because unsure about those. (But it's working by default) ### Feature 1: MSYS/Git Bash Path Conversion Tests that `/c/...` and `/f/...` paths are converted to `C:\...` and `F:\...`. #### LIST Tool ``` Use the LIST tool to show all files in /f/test/ ``` Expected: `→ List F:\test` (not `..\..\..\..\f\test`) #### READ Tool ``` Use the READ tool to read /f/test/test.txt ``` Expected: `→ Read F:\test\test.txt ` #### WRITE Tool ``` Use the WRITE tool to create /f/test/write-test.txt with content "written by opencode" ``` Expected: `→ Wrote F:\test\write-test.txt` #### EDIT Tool ``` Use the EDIT tool to replace "hello" with "goodbye" in /f/test/test.txt ``` Expected: `→ Edit F:\test\test.txt` #### GLOB Tool ``` Use the GLOB tool to find all *.txt files in /f/test/ ``` Expected: `→ ✱ Glob "*.txt" in F:\test\ (4 matches)` #### GREP Tool ``` Use the GREP tool to search for "hello" in /f/test/ ``` Expected: `→ ✱ Grep "hello" in F:\test\ (1 matches)` --- ### Feature 2: Parent Traversal Threshold (3+) Paths with 3+ parent traversals (`../../..`) show absolute path. Paths with 0-2 show relative. #### 2 levels up - shows RELATIVE ``` Use the READ tool to read /c/Users/<user_name>/repo/sst/opencode/PR.md ``` Expected: `→ Read ..\..\.PR.md` (relative path) #### 3+ levels up - shows ABSOLUTE ``` Use the READ tool to read /c/Users/<user_name>/repo/testdir/three-up.txt ``` Expected: `→ Read C:\Users\<user_name>\repo\testdir\three-up.txt` (absolute path) ``` Use the READ tool to read /c/Users/<user_name>/.config/opencode/opencode.jsonc ``` Expected: `→ Read C:\Users\<user_name>\.config\opencode\opencode.jsonc` (absolute path) --- ### Feature 3: Long Path Truncation Long paths are truncated with `...` in the middle, using `Locale.truncateMiddle`. #### Long path on different drive ``` Use the READ tool to read /f/test/very/deeply/nested/folder/structure/that/is/really/long/file.txt ``` Expected: `→ Read F:\test\very\deeply\…\that\is\really\long\file.txt` (truncated) #### Long path inside worktree ``` Use the READ tool to read /c/Users/<user_name>/repo/sst/opencode/very/deeply/nested/folder/structure/that/is/really/long/file.txt ``` Expected: `→ Read ..\..\very\deeply\ne…\that\is\really\long\file.txt ` (truncated relative path) --- ## Checklist - [x] No-op on non-Windows platforms for `toNativePath()` (gated by `process.platform === "win32"`) - [x] Parent traversal check (3+) applies to ALL platforms - [x] Uses existing `Locale.truncateMiddle` for path abbreviation (consistent with sidebar) - [x] No breaking changes for normal Windows or Unix paths
yindo added the pull-request label 2026-02-16 18:16:40 -05:00
yindo closed this issue 2026-02-16 18:16:40 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#11727