Skip to content

Commit b2d2806

Browse files
🤖 fix: expand tilde in plan file paths for editor deep links (#1090)
## Problem When clicking the Edit button on a plan (Electron app, local worktree), VSCode opens an empty file in the current working directory instead of the actual plan file. **Root cause:** Plan file paths contain tilde prefix (`~/.mux/plans/...`). The backend `editorService` uses `shellQuote()` which wraps paths in single quotes. In bash, tilde expansion doesn't happen inside single quotes, so VSCode receives the literal `~/.mux/...` and interprets it as a relative path. ## Solution `readPlanFile` now uses `runtime.resolvePath()` to return absolute paths. This works for both local (expands via `os.homedir()`) and SSH (resolves remotely). ## Testing - Updated integration test to assert returned paths are absolute (no tilde) - Manually verified Edit button works on local worktrees - SSH workspaces continue to work (resolvePath handles remote expansion) _Generated with mux_
1 parent 6cd905d commit b2d2806

File tree

2 files changed

+10
-4
lines changed

2 files changed

+10
-4
lines changed

src/node/utils/runtime/helpers.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,14 @@ export async function readPlanFile(
145145
const planPath = getPlanFilePath(workspaceName, projectName);
146146
const legacyPath = getLegacyPlanFilePath(workspaceId);
147147

148+
// Resolve tilde to absolute path for client use (editor deep links, etc.)
149+
// For local runtimes this expands ~ to /home/user; for SSH it resolves remotely
150+
const resolvedPath = await runtime.resolvePath(planPath);
151+
148152
// Try new path first
149153
try {
150154
const content = await readFileString(runtime, planPath);
151-
return { content, exists: true, path: planPath };
155+
return { content, exists: true, path: resolvedPath };
152156
} catch {
153157
// Fall back to legacy path
154158
try {
@@ -163,10 +167,10 @@ export async function readPlanFile(
163167
} catch {
164168
// Migration failed, but we have the content
165169
}
166-
return { content, exists: true, path: planPath };
170+
return { content, exists: true, path: resolvedPath };
167171
} catch {
168172
// File doesn't exist at either location
169-
return { content: "", exists: false, path: planPath };
173+
return { content: "", exists: false, path: resolvedPath };
170174
}
171175
}
172176
}

tests/ipc/planCommands.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ describeIntegration("Plan Commands Integration", () => {
9797
expect(result.success).toBe(true);
9898
if (result.success) {
9999
expect(result.data.content).toBe(planContent);
100-
expect(result.data.path).toBe(planPath);
100+
// Path should be expanded (no tilde) so it works with editor deep links
101+
expect(result.data.path).toBe(expandedPlanPath);
102+
expect(result.data.path).not.toContain("~");
101103
}
102104
} finally {
103105
await env.orpc.workspace.remove({ workspaceId });

0 commit comments

Comments
 (0)