You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`main/src/ipc/file.ts` currently imports `fs/promises` directly and uses `path.join` in ~20 places. This causes two problems:
Cross-platform path bug: On Windows, `path.join` produces backslash-separated paths. The existing WSL code path already has subtle issues here, and any future cross-system file backend (SSH, Docker, Codespaces) would inherit this latent bug.
No seam for future remote backends: Every file handler hard-codes `fs.readFile`, `fs.writeFile`, etc. There's no place to add an `if (remote) { ... }` branch without touching every handler individually.
This issue introduces a single, narrow `RemoteFs` interface and migrates every file handler to use it. The interface starts with only one implementation (`LocalRemoteFs`) that wraps the same `fs/promises` calls used today. This is a pure refactor — zero behavior change.
The goal is not to add abstraction for its own sake. It's to give us one place to slot in remote file backends later, and to fix the `path.join` bug along the way.
`LocalRemoteFs` wraps `fs/promises` for everything and `node-glob` for `search`. No new logic — it's a thin pass-through.
Files to migrate
`main/src/ipc/file.ts` — every handler pulls `remoteFs` from `sessionManager.getProjectContext` and uses it instead of direct `fs.*` calls. Specific call sites to migrate (line numbers from current main):
Line 102: `fs.readFile` → `remoteFs.readFile`
Line 138: `fs.readFile` (binary) → `remoteFs.readFileBuffer`
`main/src/services/worktreeManager.ts` — `initializeProject` accepts `remoteFs: RemoteFs` parameter; the one direct `mkdir` call is replaced with `remoteFs.mkdir(...)`
All `path.join` calls in `ipc/file.ts` are replaced with `pathResolver.join` (which already handles WSL correctly today). `path.basename` and `path.dirname` stay — those are safe for both local and remote paths.
Audit task
After migration, `grep -n 'from .fs/promises.|require..fs/promises.' main/src/ipc/file.ts` should return zero matches. The only remaining `path.*` usages should be `path.basename`, `path.dirname`, `path.extname`, or `path.relative` — never `path.join` for paths constructed from session/worktree context.
Validation
Automated
`pnpm typecheck` — clean
`pnpm lint` — clean
No new `any` introduced
Manual regression smoke (REQUIRED)
Test on a real local project:
File tree: open editor view, navigate directories, see file listings with sizes and timestamps
Open file: click a file in the tree, content loads in the editor
Edit and save file: modify a file, save, reopen — changes persist
Create new file via the file tree
Delete file: delete a single file, then delete a directory recursively
Rename file: works
File search: open the search panel, type a query, see results
Binary file read: open an image file in the editor (uses `readFileBuffer`)
Permission errors: try to read a file with no read permission, see a clean error (not a crash)
Large file: open a several-MB file, see it load
WSL projects (if applicable): file operations on WSL paths still work
Worktree creation: create a new session, verify the worktree directory is created
The Remote SSH effort needs to add a `SftpRemoteFs implements RemoteFs` that routes file operations through SFTP and `find` over SSH. With this issue landed, that addition is purely additive — drop in a new class, branch in `getOrCreateContext` based on `project.ssh_enabled`, no other code in `ipc/file.ts` or `worktreeManager.ts` needs to change.
Doing this introduction as part of the SSH PR would couple a refactor (path bug fix, FS abstraction) with a feature (remote support) and make regressions impossible to bisect.
The Remote SSH plan is at `tmp/ready-plans/2026-04-10-remote-ssh-support.md` in the `remote-ssh-like-vs-code` branch.
Why
`main/src/ipc/file.ts` currently imports `fs/promises` directly and uses `path.join` in ~20 places. This causes two problems:
This issue introduces a single, narrow `RemoteFs` interface and migrates every file handler to use it. The interface starts with only one implementation (`LocalRemoteFs`) that wraps the same `fs/promises` calls used today. This is a pure refactor — zero behavior change.
The goal is not to add abstraction for its own sake. It's to give us one place to slot in remote file backends later, and to fix the `path.join` bug along the way.
Scope
New files
Interface
```typescript
export interface RemoteDirent {
name: string;
isDirectory: boolean;
isFile: boolean;
size: number;
mtime: Date;
}
export interface RemoteStat {
isDirectory: boolean;
isFile: boolean;
size: number;
mtime: Date;
}
export interface RemoteFs {
readFile(p: string): Promise;
readFileBuffer(p: string): Promise;
writeFile(p: string, data: string | Buffer): Promise;
mkdir(p: string, opts?: { recursive?: boolean }): Promise;
exists(p: string): Promise;
stat(p: string): Promise;
readdir(p: string): Promise<RemoteDirent[]>;
unlink(p: string): Promise;
rm(p: string, opts: { recursive: boolean }): Promise;
rename(from: string, to: string): Promise;
search(root: string, pattern: string, opts?: { ignoreCase?: boolean; maxResults?: number }): Promise<string[]>;
}
```
`LocalRemoteFs` wraps `fs/promises` for everything and `node-glob` for `search`. No new logic — it's a thin pass-through.
Files to migrate
Audit task
After migration, `grep -n 'from .fs/promises.|require..fs/promises.' main/src/ipc/file.ts` should return zero matches. The only remaining `path.*` usages should be `path.basename`, `path.dirname`, `path.extname`, or `path.relative` — never `path.join` for paths constructed from session/worktree context.
Validation
Automated
Manual regression smoke (REQUIRED)
Test on a real local project:
Out of Scope
Success Criteria
Dependencies
Why This Is a Prerequisite for Remote SSH
The Remote SSH effort needs to add a `SftpRemoteFs implements RemoteFs` that routes file operations through SFTP and `find` over SSH. With this issue landed, that addition is purely additive — drop in a new class, branch in `getOrCreateContext` based on `project.ssh_enabled`, no other code in `ipc/file.ts` or `worktreeManager.ts` needs to change.
Doing this introduction as part of the SSH PR would couple a refactor (path bug fix, FS abstraction) with a feature (remote support) and make regressions impossible to bisect.
The Remote SSH plan is at `tmp/ready-plans/2026-04-10-remote-ssh-support.md` in the `remote-ssh-like-vs-code` branch.