agentskills.codes
CR

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.

Install

mkdir -p .claude/skills/cross-platform-paths && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/15874" && unzip -o skill.zip -d .claude/skills/cross-platform-paths && rm skill.zip

Installs to .claude/skills/cross-platform-paths

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

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.
193 chars✓ has a “when” trigger

About this skill

Cross-Platform Path Handling

CRITICAL: This extension runs on Windows, macOS, and Linux. Path bugs are the #1 source of issues.

Core Rules

Rule 1: Never Concatenate Paths with /

// ❌ WRONG: POSIX-style path concatenation
const envPath = homeDir + '/.venv/bin/python';

// ✅ RIGHT: Use path.join()
const envPath = path.join(homeDir, '.venv', 'bin', 'python');

Rule 2: Use path.resolve() for Comparisons, Not path.normalize()

// ❌ 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;

Rule 3: Use Uri.file().fsPath for VS Code Paths

// ❌ 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) {
}

Platform-Specific Gotchas

Windows

IssueDetails
Drive lettersPaths start with C:\, D:\, etc.
BackslashesSeparator is \, not /
Case insensitivityC:\Test equals c:\test
Long pathsPaths >260 chars may fail
Mapped drivesZ:\ may not be accessible
pyenv-winUses pyenv.bat, not pyenv or pyenv.exe
Poetry cache%LOCALAPPDATA%\pypoetry\Cache\virtualenvs
UNC paths\\server\share\ format

macOS

IssueDetails
Case sensitivityDepends on filesystem (usually insensitive)
Homebrew symlinksComplex symlink chains in /opt/homebrew/
Poetry cache~/Library/Caches/pypoetry/virtualenvs
XCode PythonDifferent from Command Line Tools Python

Linux

IssueDetails
Case sensitivityPaths 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 filesDot-prefixed files are hidden

Common Patterns

Getting Platform-Specific Paths

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

Environment-Specific Executable Names

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';

Normalizing Paths for Comparison

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

Handling Uri | string Union Types

// ❌ 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);
}

File Existence Checks

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
}

Shell Path Escaping

// ❌ 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;

Testing Cross-Platform Code

When testing path-related code:

  1. Test on Windows (cmd, PowerShell, Git Bash)
  2. Test on macOS (zsh, bash)
  3. Test on Linux (bash, fish)

Pay special attention to:

  • Paths with spaces: C:\Program Files\Python
  • Paths with Unicode: ~/проекты/
  • Very long paths (>260 chars on Windows)
  • Paths with special characters: $, &, (, )

More by microsoft

View all by microsoft

Search skills

Search the agent skills registry