Skip to content

Commit b5d9584

Browse files
authored
Merge pull request #9 from jongio/feature/wsl-windows-terminal
feat: add WSL Windows Terminal support
2 parents 44075ae + 2c8d878 commit b5d9584

7 files changed

Lines changed: 649 additions & 268 deletions

File tree

KEYBOARD_BINDINGS_AUDIT.txt

Lines changed: 0 additions & 254 deletions
This file was deleted.
File renamed without changes.

internal/data/chronicle_windows.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package data
55
import (
66
"io"
77
"os"
8+
"os/exec"
89
"path/filepath"
910
"sync"
1011

@@ -51,6 +52,11 @@ func startPTY(binary string) (io.ReadWriteCloser, error) {
5152

5253
// findCopilotBinary returns the path to copilot.exe on Windows.
5354
func findCopilotBinary() string {
55+
// Try PATH first (matches Unix behavior).
56+
if p, err := exec.LookPath("copilot"); err == nil {
57+
return p
58+
}
59+
5460
candidates := []string{
5561
filepath.Join(os.Getenv("ProgramFiles"), "nodejs", "node_modules",
5662
"@github", "copilot", "node_modules", "@github", "copilot-win32-x64", "copilot.exe"),

internal/data/mq_coverage_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,10 @@ func TestLastReindexTime_EmptyWAL(t *testing.T) {
166166
// ---------------------------------------------------------------------------
167167

168168
func TestFindCopilotBinary_NoCandidates(t *testing.T) {
169-
// Set environment to non-existent paths.
169+
// Set environment to non-existent paths and isolate PATH.
170170
t.Setenv("ProgramFiles", filepath.Join(t.TempDir(), "nonexistent"))
171171
t.Setenv("APPDATA", "")
172+
t.Setenv("PATH", t.TempDir())
172173

173174
got := findCopilotBinary()
174175
if got != "" {
@@ -179,6 +180,7 @@ func TestFindCopilotBinary_NoCandidates(t *testing.T) {
179180
func TestFindCopilotBinary_WithAPPDATA(t *testing.T) {
180181
tmp := t.TempDir()
181182
t.Setenv("ProgramFiles", filepath.Join(tmp, "nonexistent"))
183+
t.Setenv("PATH", filepath.Join(tmp, "emptypath"))
182184

183185
// Create the APPDATA candidate path.
184186
appdata := filepath.Join(tmp, "appdata")
@@ -206,6 +208,7 @@ func TestFindCopilotBinary_WithProgramFiles(t *testing.T) {
206208
progFiles := filepath.Join(tmp, "Program Files")
207209
t.Setenv("ProgramFiles", progFiles)
208210
t.Setenv("APPDATA", "")
211+
t.Setenv("PATH", filepath.Join(tmp, "emptypath"))
209212

210213
candidatePath := filepath.Join(progFiles, "nodejs", "node_modules",
211214
"@github", "copilot", "node_modules", "@github", "copilot-win32-x64")
@@ -223,6 +226,28 @@ func TestFindCopilotBinary_WithProgramFiles(t *testing.T) {
223226
}
224227
}
225228

229+
func TestFindCopilotBinary_ViaPath(t *testing.T) {
230+
tmp := t.TempDir()
231+
t.Setenv("ProgramFiles", filepath.Join(tmp, "nonexistent"))
232+
t.Setenv("APPDATA", "")
233+
234+
// Place a copilot.exe on PATH.
235+
binDir := filepath.Join(tmp, "bin")
236+
if err := os.MkdirAll(binDir, 0o755); err != nil {
237+
t.Fatalf("creating bin dir: %v", err)
238+
}
239+
fakeBinary := filepath.Join(binDir, "copilot.exe")
240+
if err := os.WriteFile(fakeBinary, []byte("fake"), 0o755); err != nil {
241+
t.Fatalf("writing fake binary: %v", err)
242+
}
243+
t.Setenv("PATH", binDir)
244+
245+
got := findCopilotBinary()
246+
if got != fakeBinary {
247+
t.Errorf("findCopilotBinary() = %q, want %q", got, fakeBinary)
248+
}
249+
}
250+
226251
// ---------------------------------------------------------------------------
227252
// Maintain — 56% coverage, test error branches
228253
// ---------------------------------------------------------------------------

internal/platform/paths.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"path/filepath"
99
"runtime"
1010
"strings"
11+
"sync"
1112
)
1213

1314
const (
@@ -58,20 +59,30 @@ func SessionStorePath() (string, error) {
5859
const wslMountRoot = "/mnt/c/Users"
5960

6061
// isWSL reports whether the current process is running inside Windows
61-
// Subsystem for Linux.
62+
// Subsystem for Linux. The result is cached after the first call since
63+
// the WSL status cannot change during a process lifetime.
6264
func isWSL() bool {
63-
// WSL2 (and recent WSL1) always set WSL_DISTRO_NAME.
64-
if os.Getenv("WSL_DISTRO_NAME") != "" {
65-
return true
66-
}
67-
// Older WSL1 may not set the env var; fall back to /proc/version.
68-
data, err := os.ReadFile("/proc/version")
69-
if err != nil {
70-
return false
71-
}
72-
return strings.Contains(strings.ToLower(string(data)), "microsoft")
65+
wslOnce.Do(func() {
66+
// WSL2 (and recent WSL1) always set WSL_DISTRO_NAME.
67+
if os.Getenv("WSL_DISTRO_NAME") != "" {
68+
wslCached = true
69+
return
70+
}
71+
// Older WSL1 may not set the env var; fall back to /proc/version.
72+
data, err := os.ReadFile("/proc/version")
73+
if err != nil {
74+
return
75+
}
76+
wslCached = strings.Contains(strings.ToLower(string(data)), "microsoft")
77+
})
78+
return wslCached
7379
}
7480

81+
var (
82+
wslOnce sync.Once
83+
wslCached bool
84+
)
85+
7586
// findWindowsSessionStore scans Windows user-profile directories under the
7687
// default WSL mount for a Copilot session store database.
7788
func findWindowsSessionStore() string {

0 commit comments

Comments
 (0)