Cross-platform path handling and command patterns
48
51%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./.copilot/skills/windows-compatibility/SKILL.mdSquad runs on Windows, macOS, and Linux. Several bugs have been traced to platform-specific assumptions: ISO timestamps with colons (illegal on Windows), git -C with Windows paths (unreliable), forward-slash paths in Node.js on Windows.
2026-03-15T05:30:00Z is illegal on WindowssafeTimestamp() utility: Replaces colons with hyphens → 2026-03-15T05-30-00Z.toISOString().replace(/:/g, '-') — use the utilitygit -C {path}: Unreliable with Windows paths (backslashes, spaces, drive letters)cd first: Change directory, then run git commandsgit diff --cached --quiet (exit 0 = no changes)-m flag: Backtick-n (\n) fails silently in PowerShell-F flag: Write message to file, commit with git commit -F $msgFileTEAM ROOT from spawn prompt or run git rev-parse --show-toplevel/ or \process.platform alone: Windows is case-insensitive, but Darwin volumes are not all case-insensitive. APFS and HFS+ can be case-sensitive; a blanket lowercase conversion on macOS can conflate distinct sibling directories.FSStorageProvider currently folds case for all Darwin paths, so its root confinement is not volume-aware on case-sensitive macOS filesystems.rootDir only when it equals the normalized root exactly or starts with rootDir + path.sep; a bare substring/prefix match lets /root-escape slip past /root. Filesystem roots are valid roots and must remain exact-match-or-separator checks.rootDir confinement, and any validation that a resolved path stays under an allowed directoryimport path from 'node:path';
function normalizeForRootComparison(value: string, volumeIsCaseInsensitive = false): string {
return volumeIsCaseInsensitive ? value.toLowerCase() : value;
}
function isPathWithin(candidate: string, rootDir: string, volumeIsCaseInsensitive = false): boolean {
const a = normalizeForRootComparison(path.resolve(candidate), volumeIsCaseInsensitive);
const b = normalizeForRootComparison(path.resolve(rootDir), volumeIsCaseInsensitive);
const boundary = b.replace(/[\\/]+$/, '') + path.sep;
return a === b || a.startsWith(boundary);
}An eol=lf attribute affects checkout behavior; it does not repair files that were already
materialized with CRLF. For the known shebang failure mode, use the repository's
scripts/fix-crlf-worktree.mjs rather than a broad renormalization:
git checkout-index -f.This is a local repair, not a source rewrite. Do not use git add --renormalize .; it rewrites
the index and creates unrelated line-ending churn. Do not force-checkout a locally modified file.
✓ Correct:
# Timestamp utility
$safeTimestamp = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH-mm-ssZ')
# Git workflow (PowerShell)
cd $teamRoot
# NEVER use `git add .squad/` or broad globs — only stage files you intentionally changed
# Stage only files you actually modified — use git status to build explicit list
$filesToStage = git status --porcelain | Where-Object { $_.Length -gt 3 } | ForEach-Object { $_.Substring(3) -replace '^.* -> ','' } | Where-Object {
$_ -eq '.squad/decisions.md' -or
$_ -eq '.squad/decisions-archive.md' -or
$_ -like '.squad/agents/*/history.md' -or
$_ -like '.squad/agents/*/history-archive.md'
}
if ($filesToStage) { $filesToStage | Where-Object { $_ } | ForEach-Object { git add -- $_ } }
git diff --cached --quiet
if ($LASTEXITCODE -ne 0) {
$msg = @"
docs(ai-team): session log
Changes:
- Added decisions
"@
$msgFile = [System.IO.Path]::GetTempFileName()
Set-Content -Path $msgFile -Value $msg -Encoding utf8
git commit -F $msgFile
Remove-Item $msgFile
}✗ Incorrect:
// Colon in filename
const logPath = `.squad/log/${new Date().toISOString()}.md`; // ILLEGAL on Windows
// git -C with Windows path
exec('git -C C:\\src\\squad add .squad/'); // UNRELIABLE
// Inline newlines in commit message
exec('git commit -m "First line\nSecond line"'); // FAILS silently in PowerShellgit -C because it "looks cleaner" (it doesn't work)git diff --cached --quiet check (creates empty commits)29e69f5
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.