UPD: looks like amp has now built a version of this into their product
agentic engineering with wings
flamp runs an Amp prompt on a disposable Fly Machine, commits the result, and pushes it back to your GitHub branch. Launch your agent, close your laptop, get a push notification to your phone when it's done.
- A Git repository with an
originremote, preferably on GitHub. - The Fly CLI installed and authenticated.
- The Amp CLI installed locally.
flampuses it to generateDockerfile.amp(the project's base image + dependency-install steps, plus flamp's scaffolding) when one does not already exist. - An Amp API key in
AMP_API_KEY. - An SSH private key with push access to the repo in
CLOUD_SSH_KEY_PATH.
export AMP_API_KEY=...
export CLOUD_SSH_KEY_PATH=~/.ssh/id_ed25519
./flamp <branch> "your prompt here" [path-or-glob ...]
./flamp <branch> @prompt.md [path-or-glob ...]
./flamp <branch> - [path-or-glob ...]
cat prompt.md | ./flamp <branch>Examples:
./flamp fix/readme "Add a README for this project"
./flamp debug-run @prompt.md logs/latest.log .env.local
cat prompt.md | ./flamp feature/new-apiPass HEAD as the branch to use your currently checked-out branch:
./flamp HEAD @prompt.mdOn the first run, flamp generates Dockerfile.amp — the complete build recipe (base image + dependency installs, authored by Amp from your repo, plus flamp's scaffolding) — and persists it. On later runs any existing Dockerfile.amp, whether flamp-generated or hand-written, is built verbatim and never overwritten. The generated file carries a # Generated by flamp header purely as a human-facing note that it's safe to delete and regenerate; flamp itself doesn't read it back.
The Docker image is built from a clean checkout of a sticky pinned commit (not your local worktree, and not origin's default-branch tip). The pin is recorded in flamp-deps.commit and deliberately does not move every time you commit — so ordinary local commits no longer change the build context and therefore no longer bust Docker's dependency-layer cache. The pin only moves when the recipe is (re)generated or when you reset it (see below). The baked checkout only seeds the image's dependency layers; at runtime the Fly Machine fetches and checks out the requested branch, so the branch (including HEAD) must already be pushed with the commits you want Amp to work from — uncommitted local changes are not picked up.
Refreshing the cache:
- Delete
Dockerfile.ampto regenerate the whole recipe (new dependency steps + current flamp scaffolding) and re-pin to your currentHEAD. - Delete
flamp-deps.committo rebuild the dependencies against your currentHEADwithout changing the recipe.
Otherwise the dependency layers are reused indefinitely, which is what you want for a dev container whose dependencies rarely change.
Extra path or glob arguments are copied into the checked-out repository on the Fly Machine at the same relative path. They are added to .git/info/exclude there, so they are available to Amp but are not committed.
| Environment variable | Default | Description |
|---|---|---|
AMP_API_KEY |
required | Amp API key from ampcode.com/settings. |
CLOUD_SSH_KEY_PATH |
required | SSH private key with push access to the repository. |
FLY_APP |
amp-runner |
Fly app used to launch the Machine. |
FLY_REGION |
ord |
Fly region for the Machine. |
FLY_VM_SIZE |
shared-cpu-2x |
Fly Machine CPU size. |
FLY_VM_MEMORY |
2048 |
Fly Machine memory in MB. |
FLAMP_SIMPLEPUSH_KEY |
unset | Optional Simplepush key for Amp's final output. |
(ord is the closest fly region to ampcode.com)
- Reads the prompt from an argument,
@file,-, or stdin. - Builds a Docker context from a clean checkout of the sticky pinned commit recorded in
flamp-deps.commit(not your local worktree and notorigin's latest default-branch tip), excluding local worktree files. The pin stays put across ordinary local commits so the dependency-layer cache survives; it is re-pinned toHEADonly when the recipe is (re)generated or when you deleteflamp-deps.commit. - If
Dockerfile.ampis absent, generates it: Amp authors a single-stage fragment (base image + system/dependency steps, reusing the repo's existing Dockerfile where possible) as a transient intermediate, and flamp assembles it intoDockerfile.amp— injecting the repo worktree (WORKDIR /tmp/home/work+COPY . /tmp/home/work, without.git) right after the fragment'sFROM, then appending git/ssh/ripgrep and the Amp CLI install. The assembled file is persisted and built verbatim on subsequent runs..gitis deliberately excluded: a fresh clone's.gitis not byte-stable, so baking it in would bust the Docker layer cache on every build (the runtime fetch recreates.giton the Machine). - Starts a disposable Fly Machine with the baked-in repo, prompt, SSH key, and optional extra files.
- Fetches and checks out or creates the requested branch in the baked-in repo, then runs Amp in deep mode.
- Commits any changes, pulls the latest branch state, asks Amp to resolve merge conflicts if needed, and pushes back to
origin.
- The Fly Machine receives the SSH key as a mounted file and uses it only for Git operations.
- The generated
Dockerfile.ampis assembled from a single-stage fragment Amp authors (itsFROMplus system/dependencyRUN/ENVlines, ideally lifted from the repo's own Dockerfile) with flamp's scaffolding injected: the repo checkout right afterFROM, and the tooling/Amp install appended. The fragment is transient — only the assembled result is stored. It must contain exactly oneFROM; if Amp's fragment has more (or you need a multi-stage / fully custom build), writeDockerfile.ampby hand instead. - For full control over the image, commit your own
Dockerfile.amp. When present, anyDockerfile.amp— generated or hand-written — is built verbatim and flamp will not overwrite it (flamp never inspects its contents to decide this; it only checks whether the file exists). A hand-written one must copy the build context into/tmp/home/work, install dependencies, and install the Amp CLI itself. The build context no longer contains.git; the runner initializes git at runtime, so you don't need to bake it in (if you do, the runner reuses it). - Extra local files are intentionally excluded from commits on the remote Machine.