# Show HN: Scriptable CLI tool for generating images using popular providers

> Source: <https://github.com/michaeldmueller/etch-image>
> Published: 2026-11-04 14:42:54+00:00

Scriptable CLI tool for generating and editing images with OpenAI, Gemini, and FLUX.

```
npm install -g etch-image
etch "a polished app icon for a CLI image generation tool called etch"
```

You'll need an API key for the provider you want to use in `~/.config/etch/.env` See [Setup](#setup)

| Provider | Models | 
|---|---|
| OpenAI |  | 
| Gemini |  | 
| FLUX (Black Forest Labs) |  | 

```
npm install -g etch-image
```

Etch is bring-your-own-token. You provide API keys for the providers you want to use. Put them in `~/.config/etch/.env`:

```
OPENAI_API_KEY=your_key_here
GEMINI_API_KEY=your_key_here
BFL_API_KEY=your_key_here
```

You only need keys for the providers you plan to use. Keys are read from the shell environment first, then `~/.config/etch/.env`.

```
etch [input_file ...] "prompt" [options]
```

The last positional argument is always the prompt. Anything before it is treated as an input image:

- **No input files** → generate a new image from the prompt.
- **One or more input files** → edit those images using the prompt.

The result is saved as a PNG. Pass `-o <path>` to choose the file, or leave it out and Etch generates a name and outputs it in the current directory (or in `output_dir` from your config).

| Flag | Description | 
|---|---|
| `-o, --output <path>` | Where to save the image (default: an auto-named PNG in the current directory). Never overwrites an existing file unless `--force` is given | 
| `-n, --count <n>` | Number of images to generate, 1 to 10 (default: 1). With `-o` , files get a`-1` ,`-2` , ... suffix | 
| `--model <model>` | Model to use (default: `gpt-image-2.5-sunburst` ) | 
| `--size <size>` | Output size; depends on the provider, see [Sizes and quality](#sizes-and-quality) | 
| `--quality <quality>` | Quality level (OpenAI) or resolution tier (FLUX 3), see [Sizes and quality](#sizes-and-quality) | 
| `--background <background>` | Background style (OpenAI only) | 
| `--open` | Open the image after it is saved (default when run from a terminal) | 
| `--dont-open` | Don't open the image after it is saved | 
| `--json` | Print the result as a JSON object instead of text | 
| `--force` | Overwrite the output file if it already exists | 
| `--models` | List available models and exit | 
| `--sizes` | List available sizes by provider and exit | 
| `--config` | Show the config file path and effective defaults, then exit | 

What `--size` and `--quality` accept depends on the model. Run `etch --sizes` for the list.

**OpenAI**

- Size: `auto` ,`1024x1024` ,`1536x1024` ,`1024x1536` .`gpt-image-2` and later also accept any`WIDTHxHEIGHT` (multiples of 16, aspect ratio 1:3 to 3:1, up to`3840x2160` ).
- Quality: `auto` ,`low` ,`medium` ,`high` .`gpt-image-2.5` models also accept`xhigh` and`max` .

**Gemini**

- Size: `1K` ,`2K` ,`4K` .`gemini-3.1-flash-image` also accepts`512px` ,`gemini-3.1-flash-lite-image` only`1K` , and`gemini-2.5-flash-image` takes no size.
- Quality: not supported.

**FLUX**

- Size: an aspect ratio like `16:9` , from 21:9 to 9:21.`flux-3` models take a fixed list of ratios (see`--sizes` ). FLUX 2, 1.1 and dev also accept`WIDTHxHEIGHT` ; a ratio is converted to about 1 megapixel for these models.
- Quality: `flux-3` models only, as a resolution tier:`768sq` ,`1k` ,`1.5k` ,`2k` ,`4k` .

Any option's default can be changed in `~/.config/etch/config.json` (or `$XDG_CONFIG_HOME/etch/config.json` if that variable is set). A flag on the command line always wins over the config file.

```
{
  "open": false,
  "model": "gpt-image-2.5-flare",
  "quality": "high",
  "output_dir": "~/Pictures/etch"
}
```

Supported settings: `model`, `size`, `quality`, `background`, `count`, `open`, `output_dir`. The `output_dir` setting only applies when `-o` is not given. Run `etch --config` to see the effective defaults.

Only the saved paths are written to stdout, one per line. Progress and the model details go to stderr, and when stdout is not a terminal the spinner and auto-open are skipped. So the output can be captured or piped directly:

```
file=$(etch "a red cube")
etch "a red cube" | xargs open
```

Pass `--open` to open the image anyway.

With `--json`, stdout is a single JSON object and errors on stderr are JSON too. `path` is the first image and `paths` lists all of them:

```
etch --json "a red cube" | jq -r .path
{"path":"/Users/you/etch-20261021-143210-k8v1.png","paths":["/Users/you/etch-20261021-143210-k8v1.png"],"mode":"generate","provider":"openai","model":"gpt-image-2.5-sunburst","size":null,"quality":null,"background":null,"inputs":[],"elapsed_ms":11840,"cost_usd":0.0412,"cost_estimated":true}
```

Exit codes:

| Code | Meaning | 
|---|---|
| `0` | Image saved | 
| `1` | Usage or config error (bad flag, invalid size, missing input file) | 
| `2` | Provider error (bad API key, network, server error) | 
| `3` | Request refused by the provider's content moderation | 

Generate an image from a prompt:

```
etch "a dramatic sunset over the ocean"
```

Save to a specific file:

```
etch "a dramatic sunset over the ocean" -o output/sunset.png
```

Generate four variations at once:

```
etch "a dramatic sunset over the ocean" -n 4
```

Edit an image:

```
etch source.png "make the sky purple"
```

Use a specific model:

```
etch source.png "make the sky purple" --model gemini-nano-banana-2.1
```

Specify size and quality:

```
etch source.png "add a sunset" --size 1536x1024 --quality high
```

Generate a 4K widescreen image with the top quality setting (`gpt-image-2.5` models only):

```
etch "a city skyline at dusk" --size 3840x2160 --quality max -o output/skyline.png
```

Use a FLUX model:

```
etch "cyberpunk cityscape at night" --model flux-2-pro --size 16:9
```

Generate a 4K image with FLUX 3:

```
etch "cyberpunk cityscape at night" --model flux-3-image --size 21:9 --quality 4k
```

Use multiple input images:

```
etch room.jpg reference.jpg "restyle the first room to match the second" -o output/restyled.png
```

Save without opening the result:

```
etch "a minimal app icon" --dont-open
```

The repo ships a plugin that teaches Claude Code and Codex how to use Etch: ask for an image in a session and the agent runs `etch` for you, names the file sensibly, and reports the cost. It needs `etch` installed and an API key configured as described in Setup.

Claude Code:

```
/plugin install etch --marketplace michaeldmueller/etch-image
```

Codex:

```
codex plugin marketplace add michaeldmueller/etch-image
codex plugin add etch@etch-image
```

Both read the same skill, in `plugins/etch/skills/etch/SKILL.md`. To use it as a standalone skill instead, copy that folder to `~/.claude/skills/etch` or `~/.agents/skills/etch`.
