ChatGPT Pro models are seriously good at coding, and for a long stretch the strongest of them simply weren’t available in Codex. The only place OpenAI would let them write code was the ChatGPT sandbox. The sandbox has Git, but it’s locked down: no network, no pushing to your remote. So everyone using Pro models for real work was doing some version of the same sad ritual: copy-pasting diffs out of a chat window, or downloading a zip of the whole repo and eyeballing what changed. I wanted the thing that ritual was imitating: ChatGPT makes actual commits, and those commits land in my actual repository. procoder is that round trip, and the reason it works is that it refuses to move anything except Git history itself.
Three tools, one exchange
The workflow is deliberately small. procoder prepare runs locally in a clean repo: it creates a dedicated task branch, builds a sanitized export of tracked files plus local branches and tags for read-only context, with remotes, credentials, hooks, and reflogs stripped out, drops a Linux helper binary called procoder-return at the repo root, and zips it all up. You upload that to ChatGPT with a prompt that ends “run ./procoder-return and give me the path to the zip.” The model codes, commits on the prepared branch, runs the helper, and hands back a small return package. procoder apply imports it locally. That’s the whole surface.
The return helper is worth a note on its own: it takes no arguments and has no subcommands, on purpose. It’s a binary designed for an agent to run, and every flag you add to a tool an LLM operates is another opportunity for it to hallucinate an invocation. One command, one behavior, one output path to report back.
git bundle is the whole trick
The primitive underneath is git bundle, Git’s own mechanism for moving commits, trees, blobs, and refs as a portable file, no server or network required. The prepared zip is a real Git repository with real history, so the sandbox can work offline exactly the way Git expects. And the return trip is an incremental bundle: procoder-return packages only the objects created since prepare, plus a small JSON manifest naming the returned refs and commit IDs. You ship the repo in once; only the new history comes back. No re-uploading, no whole-repo zips, no diffs mangled by a chat renderer.
apply treats the return package as untrusted input, because it is one: a file a language model produced in a sandbox. It verifies the bundle, imports it into a temporary ref namespace, checks the imported refs against the manifest, and only then fast-forwards your local task branch, and only if it’s still safe. If your branch moved locally in the meantime, or the package tries to touch refs outside the prepared exchange-branch family, apply fails loudly instead of guessing. There’s a --dry-run for inspecting the import first. The feeling you get is “ChatGPT committed to my repo”; the reality is that nothing updated until every check passed on your machine.
The narrow happy path is a feature
V1’s constraints are stated instead of hidden: clean repo required at prepare, no LFS, no submodules, returned work must stay inside the exchange branch family, no tag changes in the return flow. Each of those is a class of edge case where “update when safe, otherwise fail clearly” would degrade into “probably fine, usually.” I’d rather ship a tool with a narrow path that never lies than a wide one that occasionally imports something surprising into your repository.
procoder is one piece of a running theme in my tooling: treating hosted AI products as agents in my system rather than destinations I visit. Agentbox moves context and files between ChatGPT and my local agents; procoder moves Git history. Both exist because the interesting models don’t always live where your repo does, and being the human clipboard between them is the job most worth automating.
