microsoft/cross-platform-paths
Critical patterns for cross-platform path handling in this VS Code extension. Windows vs POSIX path bugs are the #1 source of issues. Use this skill when reviewing or writing path-related code.
npx skills add https://github.com/microsoft/vscode-python-environments --skill cross-platform-paths
CRITICAL: This extension runs on Windows, macOS, and Linux. Path bugs are the #1 source of issues.
/// ❌ WRONG: POSIX-style path concatenation
const envPath = homeDir + '/.venv/bin/python';
// ✅ RIGHT: Use path.join()
const envPath = path.join(homeDir, '.venv', 'bin', 'python');
// ❌ WRONG: path.normalize keeps relative paths relative on Windows
const normalized = path.normalize(fsPath);
// path.normalize('\test') → '\test' (still relative!)
// ✅ RIGHT: path.resolve adds drive letter on Windows
const normalized = path.resolve(fsPath);
// path.resolve('\test') → 'C:\test' (absolute!)
// When comparing paths, use resolve() on BOTH sides:
const pathA = path.resolve(fsPath);
const pathB = path.resolve(e.environmentPath.fsPath);
return pathA === pathB;
// ❌ WRONG: Raw string comparison
if (filePath === otherPath) {
}
// ✅ RIGHT: Compare fsPath to fsPath
import { Uri } from 'vscode';
const fsPathA = Uri.file(pathA).fsPath;
const fsPathB = Uri.file(pathB).fsPath;
if (fsPathA === fsPathB) {
}
| Issue | Details |
| ------------------ | -------------------------------------------- |
| Drive letters | Paths start with C:\, D:\, etc. |
| Backslashes | Separator is \, not / |
| Case insensitivity | C:\Test equals c:\test |
| Long paths | Paths >260 chars may fail |
| Mapped drives | Z:\ may not be accessible |
| pyenv-win | Uses pyenv.bat, not pyenv or pyenv.exe |
| Poetry cache | %LOCALAPPDATA%\pypoetry\Cache\virtualenvs |
| UNC paths | \\server\share\ format |
| Issue | Details |
| ----------------- | ------------------------------------------- |
| Case sensitivity | Depends on filesystem (usually insensitive) |
| Homebrew symlinks | Complex symlink chains in /opt/homebrew/ |
| Poetry cache | ~/Library/Caches/pypoetry/virtualenvs |
| XCode Python | Different from Command Line Tools Python |
| Issue | Details |
| ---------------- | --------------------------------------- |
| Case sensitivity | Paths ARE case-sensitive |
| /bin symlinks | /bin may be symlink to /usr/bin |
| XDG directories | ~/.local/share/virtualenvs for pipenv |
| Poetry cache | ~/.cache/pypoetry/virtualenvs |
| Hidden files | Dot-prefixed files are hidden |
import * as os from 'os';
import * as path from 'path';
// Home directory
const home = os.homedir(); // Works cross-platform
// Construct paths correctly
const venvPath = path.join(home, '.venv', 'bin', 'python');
// Windows: C:\Users\name\.venv\bin\python
// macOS: /Users/name/.venv/bin/python
// Linux: /home/name/.venv/bin/python
const isWindows = process.platform === 'win32';
// Python executable
const pythonExe = isWindows ? 'python.exe' : 'python';
// Activate script
const activateScript = isWindows
? path.join(venvPath, 'Scripts', 'activate.bat')
: path.join(venvPath, 'bin', 'activate');
// pyenv command
const pyenvCmd = isWindows ? 'pyenv.bat' : 'pyenv';
import { normalizePath } from './common/utils/pathUtils';
// Use normalizePath() for map keys and comparisons
const key = normalizePath(filePath);
cache.set(key, value);
// But preserve original for user display
traceLog(`Discovered: ${filePath}`); // Keep original
// ❌ WRONG: Assuming Uri
function process(locator: Uri | string) {
const fsPath = locator.fsPath; // Crashes if string!
}
// ✅ RIGHT: Handle both types
function process(locator: Uri | string) {
const fsPath = locator instanceof Uri ? locator.fsPath : locator;
// Now normalize for comparisons
const normalized = path.resolve(fsPath);
}
import * as fs from 'fs';
import * as path from 'path';
// Check file exists (cross-platform)
const configPath = path.join(projectRoot, 'pyproject.toml');
if (fs.existsSync(configPath)) {
// File exists
}
// Use async version when possible
import { promises as fsPromises } from 'fs';
try {
await fsPromises.access(configPath);
// File exists
} catch {
// File does not exist
}
// ❌ WRONG: Unescaped paths in shell commands
terminal.sendText(`python ${filePath}`);
// D:\path\file.py becomes "D:pathfile.py" in some shells!
// ✅ RIGHT: Quote paths
terminal.sendText(`python "${filePath}"`);
// For Git Bash on Windows, escape backslashes
const shellPath = isGitBash ? filePath.replace(/\\/g, '/') : filePath;
When testing path-related code:
Pay special attention to:
C:\Program Files\Python~/проекты/$, &, (, )Take microsoft/cross-platform-paths from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.