Repository navigation
Porting with an AI Tool
parityrun port --to ts # TypeScript; or js (JavaScript), rs (Rust)
parityrun writes the new app's starting point beside yours (../my-app-ts), adds it to impls.json and gives your
AI tool the task in PORTING.md. Then it goes turn by turn: the tool works, parityrun checks, and the differences of
one scenario at a time become the next turn's task, until parity is 100 %. A scenario that stays red is reported,
never hidden.
parityrun port --to typescript|javascript|rust|ts|js|rs [--into FOLDER] [--name NAME] [--agent claude|"COMMAND"]
[--turns 20] [--patience 3] [--turn-minutes 60] [--setup-only]
The recordings are what the port is held to, so port first records any scenario that has no recording yet. Then it
-
writes the port's starting point, an app that runs and answers 501 everywhere, in
--into(default: beside the app,<app>-ts,<app>-jsor<app>-rs):- JavaScript is
server.js(CommonJS), - TypeScript is
server.ts, run by node 23.6 or newer as it is (type stripping, no build), - Rust is a Cargo package of its own, built with
cargo build --release.
A file that exists is never written over, so
portgoes on from what is there; - JavaScript is
-
adds the port to
impls.json(default namejs,tsorrust), with its port inPORT; -
writes
PORTING.mdin the port's folder, the task: how the port is started, the environment every run gives it (from yourenv(), with sample values), what is compared, every scenario with its recording, how to check, and the rules; -
loops. Each turn runs your AI tool in the port's folder. The first turn ports the whole app; then parityrun builds the port, sees that it starts, runs
check, and the next turn gets the failing checks of one red scenario (path: expected != yours, with the paths of the scenario, its recording and the port's own recording inout/<name>/<scenario>/recording.json). A crash comes with the port's own output; a build that fails, with the compiler's.
It stops at 100 % parity (exit 0), after --turns turns, or when every red scenario has been worked on for
--patience turns without getting better, or the port hasn't built or started for that many turns (exit 1: what is
still red is printed). Run the same command again to go on.
--agent claude (the default) runs Claude Code as
claude -p --permission-mode acceptEdits with the task on stdin: it may read the app and the parityrun folder
(--add-dir), edit files in the port's folder and run parityrun check, node/npm or cargo, nothing else. It
uses your own Claude Code login and plan.
Any other tool: give its command line, with {prompt_file} (the turn's task as a file) and {folder} (the port's
folder) filled in; it runs in the port's folder and gets the task on stdin too (write paths in the command with
forward slashes):
parityrun port --to rs --agent "yourtool --message-file {prompt_file}"
PARITYRUN_PROJECT points the tool's own parityrun check at the project.
The parityrun folder (scenarios, recordings, fixtures, settings) is kept aside and put back after every turn; the
next turn is told what was put back. The original app's files are watched: when one changes, port stops (exit 2),
as the recordings may no longer be the original's.
Each turn's task and the tool's output are in out/port/turn-<n>.prompt.md and turn-<n>.log; each turn's result
(parity, every scenario's passing checks, the time the tool took) is a line of out/port-log.jsonl.
--setup-only writes the folder, the impls.json entry and PORTING.md and stops, for porting by hand or in your
own tool's chat: parityrun check --impl ts checks the port at any time.
A library is ported the same way, with a shim to fill in: see Libraries.
parityrun is MIT-licensed. Install: pip install parityrun. Issues and source: github.com/arttujan/parityrun.
Start
Porting
Reference
Links