Works with the AI Agents you already use

    Claude CodeClaude CodeCursorCursorCodex CLICodex CLIMuseMuseDotsDots+22 more

    Cross-Platform Code — Linux, macOS, Windows & Docker

    1

    Makes code, tests and Docker builds behave the same on Linux, macOS and native Windows, and catches POSIX-only assumptions.

    Free

    Security scanned
    Cross-Platform Code — Linux, macOS, Windows & Docker

    Cross-Platform Code — Linux, macOS, Windows & Docker

    Example session with this skill installed

    Our CLI is getting Windows users and this export function fails for them. Make it cross-platform. Supported targets: Linux, macOS, Windows 11 (native, not WSL).

    
    # reportr/export.py
    import os, subprocess, datetime
    
    def export_report(user_name, rows):
        out_dir = os.path.expanduser("~/.config/reportr/exports")
        os.makedirs(out_dir, exist_ok=True)
        stamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
        path = out_dir + "/" + user_name + "-" + stamp + ".csv"
        with open(path, "w") as f:
            f.write("\n".join(",".join(r) for r in rows))
        subprocess.run(f"gzip {path}", shell=True, check=True)
        os.system("rm -rf /tmp/reportr-cache")
        return path + ".gz"
    
    • Read your context and instructions
    • Compiled the cross-platform code

    This function can't work on native Windows as written. The filename contains : (from %H:%M), and it shells out to gzip and rm, which Windows doesn't have. Below are 8 findings, a portable rewrite and tests. The tests pass on Linux. macOS and Windows still need CI runs.

    Platform check

    1. Platforms: Linux, macOS, Windows 11 native (as you specified).

    POSIX assumptions: path separator, : in a filename, /tmp, ~/.config, locale-dependent encoding, line endings.
    3. Undeclared external tools: gzip and rm are called through the shell.
    4.

    Failure on Windows: open() raises OSError on the : in the filename. If it got past that, gzip isn't on PATH.
    5. OS-specific hot path: none. The standard library covers everything here, so no platform branch is needed.

    Findings

    #LineAnti-patternWhy it failsPortable fix
    1stamp = ...%H:%M: in filenameForbidden on Windows; the space is also awkward in shellsUTC ISO-like stamp without : (%Y-%m-%dT%H%MZ)
    2os.path.expanduser("~/.config/...")Hardcoded XDG pathWrong location on Windows and macOSplatformdirs.user_data_dir("reportr")
    3out_dir + "/" + ...String path concatenationMixes separatorspathlib.Path and the / operator
    4user_name in the filenameUnvalidated external inputCON, a:b, ../x break or escape the directory on Windowsis_safe_filename() check, rejected with a clear error
    5open(path, "w")No encodingUses the Windows locale code page, so non-ASCII names get corruptedencoding="utf-8"
    6"\n".join(...) in text modeLine endings vary by OSText mode turns \n into \r\n on Windows only, so the bytes differ per OScsv module with newline="", which writes identical bytes on every OS
    7subprocess.run(f"gzip {path}", shell=True)Shell command plus shell interpolationNo gzip on Windows; a crafted user_name would be shell injectionstdlib gzip.open() in-process
    8os.system("rm -rf /tmp/reportr-cache")POSIX utility and hardcoded /tmpNo rm or /tmp on Windows, and errors are silently ignoredshutil.rmtree(user_cache_dir("reportr"))

    Also, datetime.now() is local time. It is now UTC, as the "UTC at boundaries" rule requires. One finding is outside this skill's scope but worth fixing: ",".join(r) doesn't quote fields that contain commas. The csv module in the rewrite fixes that too.

    Portable rewrite

    
    # reportr/export.py
    import csv
    import gzip
    import shutil
    from datetime import datetime, timezone
    from pathlib import Path
    
    from platformdirs import user_cache_dir, user_data_dir  # new dependency: add to pyproject
    
    APP = "reportr"
    WINDOWS_RESERVED = (
        {"CON", "PRN", "AUX", "NUL"}
    
        | {f"COM{i}" for i in range(1, 10)}
        | {f"LPT{i}" for i in range(1, 10)}
    
    )
    FORBIDDEN_CHARS = set('<>:"/\\|?*') | {chr(c) for c in range(32)}
    
    def is_safe_filename(name: str) -> bool:
        if not name or name in {".", ".."}:
            return False
        if name.upper().split(".")[0] in WINDOWS_RESERVED:
            return False
        if any(c in FORBIDDEN_CHARS for c in name):
            return False
        if name.endswith((" ", ".")):
            return False
        return True
    
    def export_report(user_name: str, rows: list[list[str]], out_dir: Path | None = None) -> Path:
        if not is_safe_filename(user_name):
            raise ValueError(f"user_name is not a safe filename on every OS: {user_name!r}")
        out_dir = out_dir or Path(user_data_dir(APP)) / "exports"
        out_dir.mkdir(parents=True, exist_ok=True)
    
        stamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H%MZ")  # no ':' (forbidden on Windows)
        path = out_dir / f"{user_name}-{stamp}.csv.gz"
    
        # gzip in-process instead of shelling out; newline="" so the csv module
        # writes the same bytes (\r\n per RFC 4180) on every OS
        with gzip.open(path, "wt", encoding="utf-8", newline="") as f:
            csv.writer(f).writerows(rows)
    
        shutil.rmtree(Path(user_cache_dir(APP)), ignore_errors=True)
        return path
    

    Behavior changes to note in the changelog: exports now go to the OS data directory (~/.local/share/reportr on Linux, ~/Library/Application Support/reportr on macOS, %LOCALAPPDATA%\reportr\reportr on Windows, all under exports/) instead of ~/.config. Timestamps are UTC. The return type is now Path.

    Tests

    
    # tests/test_export.py
    import csv, gzip
    import pytest
    from reportr.export import export_report, is_safe_filename
    
    @pytest.mark.parametrize("name", ["CON", "nul.txt", "a:b", "x/y", "..", "trail.", "trail ", ""])
    def test_should_reject_name_when_unsafe_on_windows(name):
        assert not is_safe_filename(name)
    
    def test_should_write_portable_filename_and_roundtrip_utf8(tmp_path):
        rows = [["name", "city"], ["Zoë", "São Paulo"], ["a,b", "x"]]
        path = export_report("maria", rows, out_dir=tmp_path)
        assert ":" not in path.name
        with gzip.open(path, "rt", encoding="utf-8", newline="") as f:
            assert list(csv.reader(f)) == rows
    

    Ran on

    Linux only (Python 3.14, pytest 9.1.1, platformdirs 4.12.4): 9 passed. These tests have

    not run on macOS or Windows yet. Finding #1 is the Windows failure, and it is only proven fixed once the Windows lane is green.

    CI matrix (required)

    
    # .github/workflows/test.yml
    jobs:
      test:
        strategy:
          fail-fast: false
          matrix:
            os: [ubuntu-latest, macos-latest, windows-latest]
        runs-on: ${{ matrix.os }}
        steps:
          - uses: actions/checkout@v4
          - uses: actions/setup-python@v5
            with: { python-version: "3.12" }
          - run: pip install -e . pytest
          - run: pytest -q
    

    Validation checklist

    • Path API only, no hardcoded separators
    • Explicit UTF-8 on all file I/O
    • No shell commands or POSIX utilities called from code
    • No hardcoded /tmp or ~/.config
    • UTC at the boundary
    • Filename validation for external input
    • Tests green on macOS and Windows: pending CI
    • README lists supported OS versions and the new export location

    Next steps

    1. Add platformdirs to your dependencies and merge the matrix workflow. Treat a red Windows lane as a blocker.
    2. Search the rest of the CLI for the same patterns (shell=True, os.system, expanduser("~/., open( without encoding=) before declaring Windows supported.
    3. Tell existing users that the export folder has moved, or keep reading the old path for one release.

    Connects securely to your tools. The creator never sees your data.

    About this skill

    Ensuring Cross-Platform treats "it works on my machine" as unfinished work. Code is done when it behaves the same on every supported platform and tests on every one of those platforms prove it.

    The problems it solves

    • Paths built with "/" string concatenation, open() with no encoding, and shell=True calls to rm, gzip or grep
    • Hardcoded /tmp and ~/.config, and timestamps or user input that put : or CON into Windows filenames
    • CI that only runs on Linux, so Windows bugs ship
    • Docker images that build differently depending on the host OS

    How it works: before any filesystem, shell, environment or subprocess code is written, the agent answers a five-question platform check. It then applies the required patterns: path APIs, explicit UTF-8, no shell, tolerant line-ending reads, OS-aware temp/cache/config dirs, filename validation, UTC at boundaries, and prebuilt native dependencies. When code is genuinely OS-specific, it goes behind one protocol with a runtime factory and fails loudly on unsupported platforms instead of falling back silently. Work is checked against a checklist covering code, tests, Docker and docs before delivery, and a CI matrix with fail-fast: false is required.

    Default targets are Linux, macOS, native Windows (WSL doesn't count) and Docker amd64/arm64. You can narrow them per project.

    What's inside

    • references/patterns.md: the required patterns in Python, TypeScript, Go and Java
    • references/anti-patterns.md: a table of POSIX-only mistakes and their portable fixes
    • references/platform-specific-code.md: the protocol + factory pattern, runtime detection, the branch map
    • references/docker.md: image hygiene, a .dockerignore baseline, volume mounts, multi-arch builds

    When it triggers: code that touches files, shell, subprocesses, encoding, time, paths, native dependencies or CI, or any mention of Windows, macOS, portability or Docker.

    Free and MIT-licensed, part of the claude-skills-czl collection.

    Source on GitHub: this skill · full repository

    How to use

    Skills on Agensi run inside your AI agent. Copy the prompt, paste it into your agent, and it connects to Agensi and installs this skill for you. If a payment is needed, your agent shows it to you first.

    Reviews

    No reviews yet

    Be one of the first to try it. Every listed skill passes our trust checks below.

    Security scanned

    Passed our 8-point scan before listing

    Fresh listing

    Recently published to Agensi

    Free forever

    No account required to browse

    Trust & safety

    Security scanned

    Verified clean 11 days ago

    • Free to download with an account

    Requires

    macOS

    Listed11 days ago
    Updated4 days ago

    What's inside

    Frequently Asked Questions