agentleFS
sebastienrousseausebastienrousseau/agtmls/skills/cross-language-port/SKILL.md

cross-language-port

Skill1 starsChanged 3 months agoApache-2.0View on GitHub

Port a module, CLI, or function between Rust, Python, Go, C++, Swift, TypeScript, JavaScript, Ruby, and Bash - preserving logic, adopting target idioms, and proving equivalence with a differential golden-I/O harness. Use for 'port/rewrite this in <lang>'. Not micro-snippets or redesigns.

The file

We split this skill into the rules it gives an agent

7 rules. About 0 are common, 4 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Every one is a first-class source and target.

    line 46
  2. Very specific:

    Do not invent a target outside this set without confirming it exists in the fleet (github.com/sebastienrousseau).

    line 62
  3. Distinctive:

    Scope & contract: Read the source in full. Write down, in prose, the *observable contract*: inputs (types, ranges, encodings), outputs, …

    line 70
  4. Distinctive:

    Translate: Write the target code from the *contract*, in the target's idiom.

    line 96
  5. Very specific:

    Land it green: Run harness/port-check.sh <target-lang> <files> — it formats-checks, lints/typechecks, and compiles/syntax-gates with the …

    line 116
  6. Distinctive:

    When the port keeps a foot in both languages (a Python service shelling out to a new Rust binary, a Go caller of a C++ FFI shim, a TS …

    line 123
  7. Distinctive:

    Secrets never move into source: If the original read a key from env or a vault, the port does too — don't inline a value you found in a …

    line 133

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Skill: cross-language porting

Trigger. The user asks to port this, translate to [Language], rewrite in [Language], convert this to [Language], reimplement X in [Language], or give me the [Language] equivalent.

Reference material (the full cross-language idiom-mapping matrix, per-language porting notes + landmines, the equivalence-harness recipes, and the data-boundary/FFI contracts): see reference.md in this directory. This file is the doctrine + the execution loop; reference.md is the lookup and the recipes.

Objective

Port a specific module, script, CLI, or function from a source language to a target language so that:

  1. Behaviour is preserved exactly — same inputs produce same outputs, same errors, same side-effect ordering. This is proven, not asserted (see §4).
  2. The output is idiomatic — it reads as if a senior engineer native to the target language wrote it from the spec, not transliterated line-for-line from the source.
  3. It lands green — formatted, linted, and tested with the target language's own toolchain before you call it done.

Supported languages (the fleet)

Every one is a first-class source and target. Anchor per language — full toolchain rows (formatter / linter / test runner / package manager / error model / async model / HTTP / JSON) are in reference.md §R1.

LanguageIdiom anchorFormat · Lint · Test
Rustownership, Result<T,E> + ?, traits over inheritancerustfmt · clippy -D warnings · cargo test
Pythonduck typing, EAFP, type hints + mypy, context managersruff format · ruff + mypy · pytest
Go(T, error) returns, goroutines + channels, small interfacesgofmt · go vet + golangci-lint · go test
C++RAII, std::expected/exceptions, smart pointers, no raw newclang-format · clang-tidy · GoogleTest/Catch2
Swiftthrows/Result, async/await + actors, value types, Codableswift-format · SwiftLint · swift-testing/XCTest
TypeScriptdiscriminated unions, strict mode, async/await, no anyprettier · eslint + tsc --noEmit · vitest
JavaScriptasync/await, modules, immutability by defaultprettier · eslint · vitest/node:test
Rubyblocks/enumerables, raise/rescue, duck typingrubocop -a · rubocop · RSpec/minitest
Bash/Shellset -euo pipefail, exit codes, pipes, trapshfmt · shellcheck · bats

Do not invent a target outside this set without confirming it exists in the fleet (github.com/sebastienrousseau). If the user names one that isn't here, port it anyway but say the target is outside the standard fleet.

Execution plan

Six phases. Do not skip §4 — it is what separates a port from a guess.

  1. Scope & contract. Read the source in full. Write down, in prose, the observable contract: inputs (types, ranges, encodings), outputs, error conditions, side effects and their ordering, and any concurrency. This contract — not the source lines — is what you port. If the contract is ambiguous, that ambiguity is the first thing to resolve.
  2. Source analysis. Identify the core algorithm, external dependencies (I/O, network, filesystem, env), edge-case handling, and the concurrency model. Flag anything source-language-specific that has no direct target analogue (GIL assumptions, Go's zero values, Rust lifetimes, Ruby monkey-patching, Bash word-splitting) — these are the porting risks.
  3. Idiom mapping. Map each construct to its idiomatic target equivalent, not its literal one. Use the matrix in reference.md §R1. The load-bearing axes:
    • Error model — source exceptions → target's Result/error/ throws/exit-code convention. Never smuggle try/except into Rust or if err != nil into Python.
    • Concurrency — asyncio.gather↔Promise.all↔tokio::join!↔ goroutines+WaitGroup↔Swift TaskGroup↔Ruby threads↔std::async↔ Bash &+wait.
    • Collections & iteration — comprehensions ↔ iterator combinators ↔ range-for ↔ enumerables ↔ streams. Prefer the target's expression form when it's clearer, not shorter for its own sake.
    • Data boundaries — see §3 boundary rules.
  4. Translate. Write the target code from the contract, in the target's idiom. Rules:
    • Not a 1:1 line translation. Restructure so it reads native.
    • Adopt the target error model fully.
    • Use vetted standard libraries, named in reference.md §R1/§R2 (e.g. Python requests→Rust reqwest, C++ std::thread→Go goroutines, JS Promise.all→Python asyncio.gather).
    • Respect the target's memory/ownership model — RAII in C++, borrow discipline in Rust, ARC in Swift; do not paper over it with clones or unsafe/any.
  5. Prove behavioural equivalence (the load-bearing phase). Build a golden-I/O corpus from the source — representative inputs paired with the source's actual outputs — then run the port against the same corpus and diff. Any divergence is a port bug until explained. For pure functions, add a property test asserting port(x) == reference(x) over generated inputs. The full differential-harness recipe (per language pair) is in reference.md §R3. "It compiles and looks right" is not equivalence.
  6. Land it green. Run harness/port-check.sh <target-lang> <files> — it formats-checks, lints/typechecks, and compiles/syntax-gates with the target's toolchain, and must print GREEN before you're done. Deliver the ported file(s) + the equivalence tests + the golden corpus.

Data-boundary & interop rules

When the port keeps a foot in both languages (a Python service shelling out to a new Rust binary, a Go caller of a C++ FFI shim, a TS frontend hitting a new Ruby endpoint), enforce a structured contract at the boundary — JSON, Protocol Buffers, or a strict CLI stdout/exit-code contract. Never ad-hoc string-parse across a language boundary. Boundary specifics (serialization parity, integer-width and float-format traps, error propagation across the seam) are in reference.md §R4.

Safety at the port boundary

  • Secrets never move into source. If the original read a key from env or a vault, the port does too — don't inline a value you found in a fixture.
  • Re-validate all input in the target. A port is a fresh attack surface; the source's validation assumptions may not survive the idiom change (Bash word-splitting → argv, Python int bignum → Rust i64 overflow, JS number precision → other languages' integer types).
  • No new unsafe (Rust) / any (TS) / raw new (C++) / eval to force a shortcut. If the idiomatic path needs one, that's a design question to surface, not a silent choice.

Worked examples

Runnable, verified ports live in examples/ — each a reference.<ext> (source) + port.<ext> (idiomatic target) + golden corpus/ + verify.sh that proves equivalence. Study the one closest to your port before starting; copy its shape. Roster (examples/README.md indexes them), each teaching a distinct landmine:

  • py-to-rust-iban/ — Python → Rust; integer width (bignum % 97 → incremental mod, because the value overflows u128).
  • bash-to-python-pathdedupe/ — Bash → Python; word-splitting + glob expansion of unquoted $var (a /tmp/* segment must stay literal).
  • js-to-ts-configvalidate/ — JavaScript → TypeScript; runtime type erasure (validate unknown at the boundary; a type cast validates nothing).
  • go-to-rust-fraction/ — Go → Rust; (T, error) + zero values → Result + ?, and matching Go's split semantics exactly.
  • rust-to-swift-op/ — Rust → Swift; enum associated values map 1:1, but Result → throws (adopt the target's error channel).
  • py-to-go-duration/ — Python → Go; exceptions → if err != nil, no sum types (control flow inverts from throw/catch to return/check).
  • py-to-cpp-hexcolor/ — Python → C++; exceptions → std::expected (C++23) and RAII (no new/delete).
  • ruby-to-python-histogram/ — Ruby → Python; Enumerable/tally/blocks → Counter + comprehensions (watch the codepoint sort order).

Eight directions covering all nine fleet languages. Every verify.sh is a thin wrapper over the shared runner in harness/ (golden-diff.sh + differential property-test templates). Run every proof at once:

for v in examples/*/verify.sh; do "$v"; done

When NOT to use this skill

  • Micro-porting (a single function of a few lines) — do it inline in the conversation without invoking this skill.
  • Behaviour-changing rewrites — that's a redesign, not a port. Clarify the intended new behaviour with the user first; the equivalence harness in §5 assumes the contract is preserved.
  • Porting within a language (2→3, ES5→ESNext, C++17→23) — that's a migration/modernization, not a cross-language port.

Provenance & maintenance

  • Fleet languages are derived from the active repos at github.com/sebastienrousseau (Rust, Python, Go, C++, Swift, TypeScript, JavaScript, Ruby, Shell). Re-derive with: gh api 'users/sebastienrousseau/repos?per_page=100&type=owner' --jq '.[]|select(.fork==false)|.language' | sort | uniq -c | sort -rn
  • Toolchain rows in reference.md §R1 name current-stable tools; re-verify a formatter/linter/test-runner exists before scripting it into a port.
  • Last reviewed against the fleet: 2026-07-08.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.