files in · plain English · files out

Give your app a “describe the change” box.

patchling is a small library that turns a plain-English sentence into an applied, multi-file change. Hand it your app's state as a { path: content } map — code, an SVG scene, a comic's panels, a character's soul — and get the transformed files back. No agent, no server, no filesystem: call it, and control returns to your code.

jsnpm install patchling pypip install patchling

Zero-dependency ESM · runs in the browser and Node 18+ · Python too.

Live playground

Type a change. Watch it land.

This page imports patchling straight from esm.sh and runs the real generateDiff → smartapply pipeline in your browser. Demo mode replays a pre-recorded model response — free, instant, no key — through the real diff parser and apply engine. Switch to Real to transform anything with your own words.

Pre-recorded reply, real engine. The model response below is canned and streamed locally — but the diff parser and smartapply engine you're watching are the actual library running in this page.

or

Keys come from nano-gpt.com → Settings → API. Your key stays in this browser (localStorage) and is sent only to nano-gpt.com. OAuth sign-in requires the page to be served over http(s).

live preview — rendered from the files on the left

Demo mode replays a canned response, so the files and goal were reset to match the scenario. Switch to Real mode to transform your own edits.

starting… · ⏱ 0.0s · streamed 0 chars

Unified diff

The model's response streams here.

Files out

smartapply's returned file map lands here.
Everything you just watched — streamed diff, deterministic patch, scoped LLM repair — was the real library running in your browser. Switch to Real mode to transform your own files with your own words, or npm install patchling to put it in your app.

Built with it

The files don't have to be code.

Everything below is the same three calls pointed at different state. None of these are coding tools — they're products with a text box.

How it works

Three calls. No filesystem. No agent loop.

Your app's state is the input. patchling transforms the map you hand it and hands back a new one — what you do with the result is your code's business.

1

buildEnvironment(files)

Hand it your files as a plain { path: content } object — it becomes the project the model sees. No disk access; runs in the browser and Node 18+.

2

generateDiff(env, goal)

One LLM call returns a unified git diff — minimal, reviewable, streamable. Stream it with onToken, meter it with onUsage, or inject your own client with callLlm.

3

smartapply(diff, files)

Exact context-matched patching first; an LLM rewrite for only the files that fail. Returns a new map — originals are never mutated, and failures never half-apply.

A function call, not an agent. patchling never touches disk, never loops, never decides what to do next — one call in, one new file map out, and control returns to your code. That's what makes it embeddable: the AI-edit feature ships in your UI, on your storage, under your rules. Mock it (callLlm), meter it (onUsage), stream it (onToken), and diff its output byte-for-byte before you accept it.

Trust it the way you'd trust any function: verify the output. The count-to-100 demo wraps patchling in the dumbest possible loop — add 1, apply, assert n+1, a hundred times — and shows exactly what every verified step costs. That's the intended shape: patchling is the transformation inside your loop, not a loop of its own.

Why it doesn't flake

Why diffs — and why yours will apply.

Why a diff, and not “just rewrite the file”? A diff is the safest thing an LLM can say. It's minimal — the model writes what changed, not a fresh copy of your state to subtly corrupt. It's reviewable — show it to your user before you apply it. It streams, it's cheap, and untouched files cost nothing.

The catch: LLM diffs are sloppy. Drifted line numbers, context that doesn't quite match, malformed headers. git apply rejects them. smartapply heals them instead — deterministic patching when the hunks match, a scoped per-file LLM repair only when they don't. The sloppiness becomes patchling's problem instead of yours.

smartapply survives:

drifted @@ line numbers
context that doesn't quite match
malformed --/++ headers
headerless diffs
*** Begin Patch style
new files & deletions
multi-file diffs, applied concurrently
<think> & reasoning preambles

Get it

One primitive, two runtimes.

JavaScript

Zero-dependency ESM. Import from npm or straight off a CDN — this page does.

npm install patchling
# or, in the browser, no build step:
import { generateDiff, smartapply }
  from "https://esm.sh/patchling";

Python

The same bounded primitive, ported to Python.

pip install patchling

from patchling import generate_diff, smartapply