Skip to content

Commit ae706eb

Browse files
authored
Merge pull request #650 from rajbos/rajbos/fix-wsl-windows-path-discovery
fix: discover Windows-side VS Code sessions when running inside WSL
2 parents 1f136ff + d79b101 commit ae706eb

2 files changed

Lines changed: 124 additions & 0 deletions

File tree

vscode-extension/src/extension.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6399,6 +6399,13 @@ ${hashtag}`;
63996399
report.push(" - No Copilot Chat conversations have been initiated");
64006400
report.push(" - Sessions stored in unsupported location");
64016401
report.push(" - Authentication required with GitHub Copilot");
6402+
if (vscode.env.remoteName === "wsl") {
6403+
report.push("");
6404+
report.push("WSL note: the extension host runs inside WSL and scans both the");
6405+
report.push(" Linux-side ~/.vscode-server paths and the Windows-side");
6406+
report.push(" /mnt/c/Users/<you>/AppData/Roaming/Code paths.");
6407+
report.push(" If /mnt/c is not mounted, Windows-side sessions cannot be read.");
6408+
}
64026409
}
64036410
report.push("");
64046411
} catch (error) {
@@ -6478,6 +6485,13 @@ ${hashtag}`;
64786485
report.push(" - No Copilot Chat conversations have been initiated");
64796486
report.push(" - Sessions stored in unsupported location");
64806487
report.push(" - Authentication required with GitHub Copilot");
6488+
if (vscode.env.remoteName === "wsl") {
6489+
report.push("");
6490+
report.push("WSL note: the extension host runs inside WSL and scans both the");
6491+
report.push(" Linux-side ~/.vscode-server paths and the Windows-side");
6492+
report.push(" /mnt/c/Users/<you>/AppData/Roaming/Code paths.");
6493+
report.push(" If /mnt/c is not mounted, Windows-side sessions cannot be read.");
6494+
}
64816495
}
64826496
report.push("");
64836497
} catch (error) {

vscode-extension/src/sessionDiscovery.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,82 @@ export class SessionDiscovery {
117117
return paths;
118118
}
119119

120+
/** Returns true when the extension host is running inside WSL. */
121+
isWSL(): boolean {
122+
return os.platform() === 'linux' && (
123+
typeof process.env.WSL_DISTRO_NAME === 'string' ||
124+
typeof process.env.WSL_INTEROP === 'string'
125+
);
126+
}
127+
128+
/**
129+
* When running inside WSL, probes the Windows-side VS Code user paths
130+
* (mounted at /mnt/c/...) so sessions created in a native Windows VS Code
131+
* window are also discovered.
132+
*
133+
* This is intentionally async and non-blocking: all path existence checks
134+
* use async fs.promises.access; errors at any level are silently ignored so
135+
* a missing or inaccessible /mnt/c mount never throws.
136+
*/
137+
async getWSLWindowsPaths(): Promise<string[]> {
138+
if (!this.isWSL()) {
139+
return [];
140+
}
141+
142+
const wslPaths: string[] = [];
143+
144+
const vscodeVariants = [
145+
'Code',
146+
'Code - Insiders',
147+
'Code - Exploration',
148+
'VSCodium',
149+
'Cursor'
150+
];
151+
152+
// Derive candidate Windows usernames (safe, multiple fallbacks).
153+
const windowsUsernames: string[] = [];
154+
155+
// USERPROFILE in WSL is sometimes set to the Windows path e.g. /mnt/c/Users/alice
156+
const userprofile = process.env.USERPROFILE;
157+
if (userprofile) {
158+
const match = userprofile.match(/^\/mnt\/[a-z]\/Users\/([^/]+)/);
159+
if (match) {
160+
windowsUsernames.push(match[1]);
161+
}
162+
}
163+
164+
// Enumerate /mnt/c/Users/ if accessible — gives us every Windows profile
165+
// without guessing. We read only the top-level directory names.
166+
const windowsUsersDir = '/mnt/c/Users';
167+
try {
168+
const entries = await fs.promises.readdir(windowsUsersDir, { withFileTypes: true });
169+
for (const entry of entries) {
170+
// Skip system pseudo-folders
171+
if (!entry.isDirectory()) { continue; }
172+
const name = entry.name;
173+
if (name === 'Public' || name === 'Default' || name === 'Default User' ||
174+
name === 'All Users' || name.startsWith('.')) {
175+
continue;
176+
}
177+
if (!windowsUsernames.includes(name)) {
178+
windowsUsernames.push(name);
179+
}
180+
}
181+
} catch {
182+
// /mnt/c/Users is not accessible — WSL drive not mounted or no Windows partition
183+
return [];
184+
}
185+
186+
for (const winUser of windowsUsernames) {
187+
const appData = path.join(windowsUsersDir, winUser, 'AppData', 'Roaming');
188+
for (const variant of vscodeVariants) {
189+
wslPaths.push(path.join(appData, variant, 'User'));
190+
}
191+
}
192+
193+
return wslPaths;
194+
}
195+
120196
/**
121197
* Returns all candidate paths the extension considers when scanning for session files,
122198
* along with whether each path exists on disk. Used for diagnostics display.
@@ -132,6 +208,26 @@ export class SessionDiscovery {
132208
candidates.push({ path: p, exists, source: 'VS Code' });
133209
}
134210

211+
// When in WSL, synchronously check the top-level Windows users dir and add
212+
// candidate paths so they appear in the diagnostics panel.
213+
if (this.isWSL()) {
214+
const vscodeVariants = ['Code', 'Code - Insiders', 'Code - Exploration', 'VSCodium', 'Cursor'];
215+
const windowsUsersDir = '/mnt/c/Users';
216+
try {
217+
const entries = fs.readdirSync(windowsUsersDir, { withFileTypes: true });
218+
const systemNames = new Set(['Public', 'Default', 'Default User', 'All Users']);
219+
for (const entry of entries) {
220+
if (!entry.isDirectory() || entry.name.startsWith('.') || systemNames.has(entry.name)) { continue; }
221+
for (const variant of vscodeVariants) {
222+
const p = path.join(windowsUsersDir, entry.name, 'AppData', 'Roaming', variant, 'User');
223+
let exists = false;
224+
try { exists = fs.existsSync(p); } catch { /* ignore */ }
225+
candidates.push({ path: p, exists, source: 'VS Code (Windows via WSL)' });
226+
}
227+
}
228+
} catch { /* /mnt/c not accessible — skip */ }
229+
}
230+
135231
// Copilot CLI
136232
const copilotCliPath = path.join(os.homedir(), '.copilot', 'session-state');
137233
let copilotCliExists = false;
@@ -218,6 +314,20 @@ export class SessionDiscovery {
218314

219315
// Get all possible VS Code user paths (stable, insiders, remote, etc.)
220316
const allVSCodePaths = this.getVSCodeUserPaths();
317+
318+
// When running inside WSL also probe the Windows-side paths so sessions
319+
// created in a native Windows VS Code window are not missed.
320+
if (this.isWSL()) {
321+
this.deps.log(`🪟 WSL environment detected — probing Windows-side VS Code paths`);
322+
const wslWinPaths = await this.getWSLWindowsPaths();
323+
if (wslWinPaths.length > 0) {
324+
this.deps.log(`🪟 Adding ${wslWinPaths.length} Windows-side candidate paths from WSL`);
325+
allVSCodePaths.push(...wslWinPaths);
326+
} else {
327+
this.deps.log(`🪟 No Windows-side paths found (Windows drive may not be mounted)`);
328+
}
329+
}
330+
221331
this.deps.log(`📂 Considering ${allVSCodePaths.length} candidate VS Code paths:`);
222332
for (const candidatePath of allVSCodePaths) {
223333
this.deps.log(` 📁 ${candidatePath}`);

0 commit comments

Comments
 (0)