diff --git a/root/wrapper.go b/root/wrapper.go index e8ad519..cdcbf61 100644 --- a/root/wrapper.go +++ b/root/wrapper.go @@ -117,6 +117,13 @@ func restoreTerminal() { } } +func syncProcessCWD(cwd string) { + if !filepath.IsAbs(cwd) { + return + } + _ = os.Chdir(cwd) +} + // runWrapper sets up the pty environment, launches the shell, // and manages the main input loop to provide real-time suggestions // it handles raw terminal mode to intercept keystrokes and @@ -494,6 +501,7 @@ func runWrapper() { if cwd, ok := strings.CutPrefix(query, "IRIS_CWD:"); ok { spec.SetCWD(cwd) + syncProcessCWD(cwd) continue } diff --git a/root/wrapper_test.go b/root/wrapper_test.go new file mode 100644 index 0000000..01bffa4 --- /dev/null +++ b/root/wrapper_test.go @@ -0,0 +1,49 @@ +package root + +import ( + "os" + "path/filepath" + "testing" +) + +func wantDir(t *testing.T, path string) string { + t.Helper() + resolved, err := filepath.EvalSymlinks(path) + if err != nil { + t.Fatal(err) + } + return resolved +} + +func currentDir(t *testing.T) string { + t.Helper() + cwd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + return wantDir(t, cwd) +} + +func TestSyncProcessCWDFollowsShell(t *testing.T) { + shellDir := t.TempDir() + t.Chdir(t.TempDir()) + + syncProcessCWD(shellDir) + + if got := currentDir(t); got != wantDir(t, shellDir) { + t.Fatalf("os.Getwd() = %q, want %q", got, wantDir(t, shellDir)) + } +} + +func TestSyncProcessCWDKeepsDirectoryOnBadPath(t *testing.T) { + launcherDir := t.TempDir() + t.Chdir(launcherDir) + + for _, cwd := range []string{"", "relative/path", filepath.Join(launcherDir, "does-not-exist")} { + syncProcessCWD(cwd) + + if got := currentDir(t); got != wantDir(t, launcherDir) { + t.Fatalf("syncProcessCWD(%q) moved the process to %q, want %q", cwd, got, wantDir(t, launcherDir)) + } + } +}