$ inkvoke

Generate and edit images from the terminal with OpenAI's image models. One Go binary (v1.0.0) for a single prompt, a photo edit, a JSON batch - and machine-readable --json output for agents.

tool.png · inkvoke as a product (Go) click to swap · 1 / 2
Product illustration of the inkvoke Go CLI: a dark terminal generating a lighthouse, a portrait edit, and a batch grid of images

Illustration of the tool itself - terminal in, images out (generate, edit, batch). Also made with inkvoke generate (Go binary, v1.0.0). Click the image for the original Python-era product shot, then community samples.

generate · gpt-image-2 · high · 1536x1024
$ inkvoke generate "a lighthouse in a storm, gouache" \
    --output lighthouse.png --quality high --size 1536x1024
Wrote lighthouse.png · ~$0.0531 est. · tokens in=18 out=1760 · 41s

$ inkvoke version
inkvoke 1.0.0 (default model: gpt-image-2)

Defaults to gpt-image-2. Started as a hair-color photo-edit workaround against Arena's image edit leaderboard; grew into a general Go CLI with agent-friendly --json and exit codes. The original hair-color path is inkvoke hair-color.

Try it live

Type or paste a multi-paragraph prompt the way you would in the CLI. Generating an image requires joining Jacob Stephens' blog newsletter (Cloudflare Turnstile; free trial limits apply).

Shown in the gallery. Leave blank for anonymous.
live · gpt-image-2 · medium · 1024x1024
$ inkvoke generate "
"
\ --output trial.png --quality medium --size 1024x1024
Ctrl/Cmd+Enter runs · multi-line paste OK · newsletter + Turnstile gate
    Image generated by the live inkvoke trial
    trial.png

    What it does

    Three commands, one API key, no browser, no Python runtime. Download a single binary (or go install) and point it at OpenAI's image models. Humans get a cost line on stdout; agents pass --json and branch on exit codes. Keep inputs, outputs, and keys out of the repo.

    Generate from a prompt

    One string in, one PNG (or JPEG/WebP) out. Quality, size, and model are flags; defaults are gpt-image-2, quality auto, size auto.

    Edit existing images

    Pass one or more input paths and a prompt: golden-hour sky, recolor a door, or the original hair-color path. Same quality/size knobs as generate.

    Batch from a JSON manifest

    A list of prompts (optional edit_from, per-item quality/size/output). Concurrent workers, retries with backoff, --skip-existing.

    🔎

    Dry-run the plan

    --dry-run prints what the batch would do without calling the API. Useful before a paid run of 30 items.

    {}

    Machine-readable for agents

    --json emits one result object on stdout (path, cost, tokens, errors). Exit codes 1–5 separate usage, auth, permanent API, retryable API, and I/O. See AGENTS.md or inkvoke --help-agent.

    🔑

    Key on disk or in env

    OPENAI_API_KEY or --api-key-file ~/path/to/key.txt. The key never has to live inside the clone.

    🔒

    Code-only repo

    .gitignore excludes image formats, inputs/, outputs/, local key files, and response JSON. Do not commit photos or keys.

    Quickstart

    Go binary - no Python, no venv, no pip. Current release: v1.0.0 (linux / macOS / Windows) — includes interactive multi-line paste (end prompt with ---), API-key prompt, and inkvoke --uninstall.

    Binary (recommended)

    Download the asset that matches both your OS and CPU. The wrong file (Linux on macOS, or amd64 on Apple Silicon) fails with exec format error or a wrong-architecture message. All downloads · SHA256SUMS included.

    Which binary? (amd64 vs arm64)

    Suffix CPU Typical machines
    amd64 Intel/AMD 64-bit (x86_64) Most Windows/Linux PCs; older Intel Macs
    arm64 ARM 64-bit (aarch64) Apple Silicon Macs (M1–M4); many Pi / Graviton hosts

    Check if unsure: uname -s (Darwin = macOS, Linux = Linux) · uname -m (arm64/aarch64 → arm64; x86_64 → amd64).

    macOS (Apple Silicon — arm64)

    curl -fsSL -o inkvoke \
      https://github.com/JacobStephens2/inkvoke/releases/latest/download/inkvoke-darwin-arm64
    chmod +x inkvoke
    sudo mv inkvoke /usr/local/bin/inkvoke
    inkvoke version

    Expected: inkvoke 1.0.0 (default model: gpt-image-2) — do not paste this line; it is sample output.

    macOS (Intel — amd64)

    curl -fsSL -o inkvoke \
      https://github.com/JacobStephens2/inkvoke/releases/latest/download/inkvoke-darwin-amd64
    chmod +x inkvoke
    sudo mv inkvoke /usr/local/bin/inkvoke
    inkvoke version

    Linux (amd64)

    curl -fsSL -o inkvoke \
      https://github.com/JacobStephens2/inkvoke/releases/latest/download/inkvoke-linux-amd64
    chmod +x inkvoke
    sudo mv inkvoke /usr/local/bin/inkvoke
    inkvoke version

    Expected: inkvoke 1.0.0 (default model: gpt-image-2) — do not paste this line; it is sample output.

    Linux (arm64)

    curl -fsSL -o inkvoke \
      https://github.com/JacobStephens2/inkvoke/releases/latest/download/inkvoke-linux-arm64
    chmod +x inkvoke
    sudo mv inkvoke /usr/local/bin/inkvoke
    inkvoke version

    Windows (amd64)

    Download inkvoke-windows-amd64.exe, rename to inkvoke.exe if you like, put it on your PATH, then run inkvoke version.

    Uninstall

    inkvoke --uninstall
    # if root-owned (e.g. /usr/local/bin):
    sudo inkvoke --uninstall

    Removes the inkvoke binary you just ran (not API keys or images). Then open a new terminal or run hash -r. Manual fallback: sudo rm /usr/local/bin/inkvoke.

    With Go 1.22+

    go install github.com/JacobStephens2/inkvoke/cmd/inkvoke@v1.0.0

    Or from a clone: go build -o inkvoke ./cmd/inkvoke (go install puts the binary in $(go env GOPATH)/bin - put that on your PATH).

    API key (required)

    Every generate, edit, and batch call needs an OpenAI API key with access to image models. Create one in the OpenAI dashboard, then either export it in your shell or pass a file outside the repo. Without a key the CLI exits with missing API key.

    export OPENAI_API_KEY="<your_openai_api_key>"
    # or: inkvoke generate "…" --api-key-file ~/path/to/key.txt

    Prefer a file on disk if you do not want the key in shell history. Long high-quality runs print stderr heartbeats; see --timeout / --quiet. Agents: read AGENTS.md or run inkvoke --help-agent.

    Usage

    Generate, edit, or batch. Pass --json when a machine must parse the result. Full flags and defaults live in the README; the agent contract is AGENTS.md.

    Generate

    inkvoke generate "a lighthouse in a storm, gouache" \
      --output lighthouse.png --quality high --size 1536x1024

    Generate with --json (agents)

    inkvoke generate "a lighthouse in a storm, gouache" \
      --output lighthouse.jpg --size 1280x648 --output-format jpeg \
      --quality high --json

    Stdout is one JSON object (path, bytes, cost, tokens). Heartbeats stay on stderr. Prefer this over regexing the human cost line. Full contract: AGENTS.md.

    Edit

    inkvoke edit photo.jpg --prompt "make the sky golden hour" --output golden.png

    Batch

    inkvoke batch manifest.json --output-dir outputs \
      --quality high --size 1536x1024 --workers 3 --skip-existing

    Manifest format

    JSON list (or an object with an "images" list). Each item needs a prompt; everything else is optional.

    [
      {"id": "cover", "prompt": "a stone house on a sea cliff at dawn"},
      {"id": "recolor", "prompt": "make the door red", "edit_from": "inputs/house.png",
       "quality": "medium", "size": "1024x1024", "output": "outputs/red-door.png"}
    ]

    id names the output file when output is unset. edit_from (path or list) switches that item to edit. Failed items retry twice with backoff; the process exits non-zero if any still fail (see exit codes 1–5 under Features).

    Hair-color path

    The original Arena hair-color workflow is a first-class subcommand (same API edit under the hood).

    inkvoke hair-color photo.jpg --output edited.png --color "strawberry blonde"