@duyquangnvx/image-gen 0.0.0-stage → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +531 -0
- package/README.md +213 -2
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +4450 -0
- package/dist/codex-C-kLMs4Z.mjs +282 -0
- package/dist/codex.d.mts +60 -0
- package/dist/codex.mjs +3 -0
- package/dist/errors-D6qZIFTT.mjs +210 -0
- package/dist/failure-8WRJSaJj.mjs +382 -0
- package/dist/index.d.mts +222 -0
- package/dist/index.mjs +3 -0
- package/dist/node.d.mts +12 -0
- package/dist/node.mjs +20 -0
- package/dist/options-Cf-GPJ_h.d.mts +238 -0
- package/dist/src-B1rS2YsP.mjs +1325 -0
- package/package.json +70 -4
- package/skills/image-gen/LICENSE.txt +201 -0
- package/skills/image-gen/SKILL.md +99 -0
- package/skills/image-gen/references/alpha.md +41 -0
- package/skills/image-gen/references/art-entries.md +38 -0
- package/skills/image-gen/references/confinement.md +21 -0
- package/skills/image-gen/references/prompting.md +116 -0
- package/skills/image-gen/references/repaint-sheet.md +140 -0
- package/skills/image-gen/references/sample-prompts.md +282 -0
- package/skills/image-gen/references/setup.md +45 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
What changed in each release of image-gen, newest first, for someone using the CLI or the library, in English and in the format of [Keep a Changelog 2.0](https://keepachangelog.com/en/2.0.0/). Each change lands under `## Unreleased` as it merges, grouped under `### Added`, `### Changed`, `### Fixed` or `### Removed`. A breaking change starts with `**Breaking:**` and names what breaks: a library caller, a flag, a `--json` field, an exit code or the skill.
|
|
4
|
+
|
|
5
|
+
A release turns `## Unreleased` into `## vX.Y.Z (YYYY-MM-DD)`, which from the second release on opens with `### Upgrade from v<the release below>`: what someone upgrading must do, as a list, or `- Nothing to do.`. Keep a Changelog 2.0 puts upgrade notes in the entries; this file keeps them in a section of their own because agents and `image-gen update` read it by machine, `update` printing the steps of every release it skips, oldest first.
|
|
6
|
+
|
|
7
|
+
## Unreleased
|
|
8
|
+
|
|
9
|
+
## v0.13.1 (2026-10-12)
|
|
10
|
+
|
|
11
|
+
### Upgrade from v0.13.0
|
|
12
|
+
|
|
13
|
+
- Nothing to do.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- The skill and the README no longer tell an agent to raise the Bash tool's timeout: Claude Code moves a command past it to the background and delivers the output when the command exits.
|
|
18
|
+
- The skill's `E_REGISTRY_AUTH` row for a sandboxed Bash now covers every command, not only `update`: the agent asks the user to type the command with `!` in front.
|
|
19
|
+
|
|
20
|
+
## v0.13.0 (2026-10-09)
|
|
21
|
+
|
|
22
|
+
### Upgrade from v0.12.1
|
|
23
|
+
|
|
24
|
+
- Run `npm rm -g image-gen` first, so the old package and the new one do not fight over the `image-gen` bin.
|
|
25
|
+
- Run `npm i -g @duyquangnvx/image-gen`.
|
|
26
|
+
- Run `image-gen skill install`.
|
|
27
|
+
- Inside Claude Code, type each of the three commands above with `!`, as in `! npm rm -g image-gen`: the sandbox denies npm's global folder and `~/.claude/skills`.
|
|
28
|
+
- A library caller replaces `image-gen` with `@duyquangnvx/image-gen` in its imports, `/node` and `/codex` too, and in its `package.json`.
|
|
29
|
+
- A script that reads the version or the changelog's path from `image-gen --help` reads `image-gen --version` instead: the version from its first line, the path after `changelog ` on its second. One that reads the version from `--version` takes the second word of the first line, or `version` from `image-gen --json doctor`.
|
|
30
|
+
- A script that reads only the first diagnostic of a failed `generate` reads them all: a batch lists one per failed image, and the exit code is that of whoever must act first.
|
|
31
|
+
- A library caller that catches a provider error, such as `ProviderFailedError` or `QuotaExhaustedError`, from a request whose `n` is split over several calls also catches `NoResultError`, and reads every failure in `failures`, or the first in `cause`. Code that catches such a request by its abort reason finds the reason among `failures`.
|
|
32
|
+
- A script or agent that reads the installed skill's folder reads `path` where it read `dir`: `data.skill.path` of `doctor --json` and `update --json`, `data.path` of `skill install --json`.
|
|
33
|
+
- A script that stops on `E_TIMEOUT` because the request may have been billed need not: it now means nothing was sent, and exits 1, since a rerun as typed most likely waits again on a stdin that never ends. A call that may have been billed and ran past its own timeout is `E_NO_ANSWER`, which such a script already stops on.
|
|
34
|
+
- A script that branches on the exit code retries, after waiting, on `75`, where it retried on `2`. `2` now means the operator must act on their machine (a key, the Plan Login, Credit, the sandbox, a call that may have been billed, an `update`) and is no longer worth a retry; `3` means there is no release to update to. A bug still exits `70`.
|
|
35
|
+
- A script that matches `E_HTTP` with `details.status` 401 or 403 matches `E_TOKEN_REJECTED` instead.
|
|
36
|
+
- A script that reads `details.resetsAt` of `E_QUOTA_EXHAUSTED` reads `details.retryAt`. The library's `QuotaExhaustedError.resetsAt` is unchanged.
|
|
37
|
+
- A script that reads `data.releases` of `doctor --json` reads `data.registry`, whose `url` replaces `repo`; `latest`, `standing` and `problem` mean what they did.
|
|
38
|
+
- A script that matches an `update` or `doctor` failure by code matches `E_REGISTRY_UNREACHABLE` (exit 75) where it matched `E_RELEASES_UNREACHABLE`, and `E_REGISTRY_AUTH` or `E_NPM_FAILED` (exit 2) where it matched `E_GH_MISSING`, `E_GH_AUTH` or `E_GH_FAILED`. `E_NO_LATEST` now means npm's registry names no `latest` version. `GH_PATH` is no longer read, and `gh` is no longer needed.
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- `generate` and `edit` take `--resume`: an output whose Provenance Record says this run asked for it (prompt, model, options, each input's hash, size and the rest of `request`) is kept and not sent again, so a rerun after a Partial Result or `E_NO_ANSWER` bills only the images still missing. A run where every output is kept sends nothing and succeeds. An output whose record differs, is missing or came back degraded is still `E_OUTPUT_EXISTS`, its `hint` saying why. `--resume` and `--force` are one choice.
|
|
43
|
+
- Each image in `--json`'s `data.images` of `generate` and `edit` has `kept`: `true` when `--resume` kept it from an earlier run, `false` when this run billed it. The text prints `kept <path>` for such an image, and a run that sent nothing ends with `<model>, nothing sent; the kept images billed <who>`.
|
|
44
|
+
- `PartialResultError` has `failures`: each position in `n` no image came back for, with its failure. `cause` is still the first of them.
|
|
45
|
+
- `RequestDegradedError`, and so `ProviderWarnedError` and `WrongSizeError`, has `failures` too: each position of the batch no image came back for, empty when every call answered. A degraded batch used to drop them.
|
|
46
|
+
- A Provenance Record's `request` names the model the run asked for, as `--model` takes it, beside the top-level `model` that answered. `--resume` compares it.
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
|
|
50
|
+
- **Breaking:** the package is `@duyquangnvx/image-gen` on the public npm registry, no longer `image-gen` in a tarball on a GitHub release. The CLI installs with `npm i -g @duyquangnvx/image-gen && image-gen skill install`, needing neither `gh` nor access to the repository; the library installs with `pnpm add @duyquangnvx/image-gen ai`, and a library caller imports `@duyquangnvx/image-gen`, `@duyquangnvx/image-gen/node` and `@duyquangnvx/image-gen/codex`. The command is still `image-gen`.
|
|
51
|
+
- The `hint` of `E_NO_ANSWER`, of an image billed but left unwritten, and of a call that failed beside images that were billed names the exact rerun: the command as typed, with `--resume` in place of any `--force`. A failed call's `hint` no longer says that call was billed: it says the other images were, and written too only when none of them failed to write or finish.
|
|
52
|
+
- **Breaking:** `generate` and `edit` tell everything a run found wrong, each as its own diagnostic, and exit by whoever must act first (1, then 2, then 3, then 75) rather than by whichever failed first: each image a batch did not get, its `message` starting `image <i>: `, whether some calls or all of them failed; a degraded result beside them; each write that failed; each image that could not be finished. A 503 beside a 401 now exits 2, and a call its own timeout cut off is its own `E_NO_ANSWER` among them. A batch with one failure names its image the same way.
|
|
53
|
+
- **Breaking:** a request whose `n` is split over several calls, every one of which failed or was aborted, throws `NoResultError`, carrying each position's failure or the abort in `failures` and the first in `cause`, where it threw the first call's error, or the abort. A request sent as one call still throws that call's own error.
|
|
54
|
+
- The skill ships under `skills/image-gen/` in the package, no longer `skill/`. `image-gen skill install` still copies it to `~/.claude/skills/image-gen`.
|
|
55
|
+
- **Breaking:** `--version` prints two lines, `image-gen 0.13.0 (a1b2c3d) built <time>` then `changelog <path of the installed CHANGELOG.md>`, where it printed only `0.12.1 (a1b2c3d)`. `doctor`'s `build` row prints the same two lines, and `data.build` of `doctor --json` gains `changelog`.
|
|
56
|
+
- **Breaking:** the root `--help` ends with `Exit codes:` then `Examples:`, the last one `image-gen doctor`; the block that named the installed version, the changelog, `image-gen update` and the `gh release list` / `gh release view` lines is gone.
|
|
57
|
+
- **Breaking:** the installed skill's folder is `path`, no longer `dir`, in `--json`'s `data.skill` of `doctor` and `update` and in `data` of `skill install`.
|
|
58
|
+
- `doctor`'s `skill` row always prints the skill's state after its path, `same as this version` too, so a match is never read as unchecked; `not installed` is now `missing`, as `data.skill.state` says. `doctor --json` lists its rows in the order the text prints them, `skill` after `registry`.
|
|
59
|
+
- Ctrl-C and SIGTERM no longer cut short a call `generate` or `edit` already sent: the run sends nothing more, prints `finishing the billed calls; Ctrl-C again to quit now`, lets the calls end, writes every image they bring back, then exits 130 or 143 with them in `data.images`. A second Ctrl-C still quits at once.
|
|
60
|
+
- **Breaking:** `E_TIMEOUT` now means the run used up its 9 minutes before sending anything, or had under 10 s of them left for its call and so did not send it, so nothing was billed, and exits 1: only local work comes before sending, such as a `-` reading a stdin that never ends, which the caller fixes. Its `hint` says so. The deadline no longer cuts a call already sent: each call has its own timeout instead, 270 s or what the 9 minutes have left once 30 s are kept for the writes, whichever is less, and one that runs past it is `E_NO_ANSWER`, after the images that did come back are written.
|
|
61
|
+
- **Breaking:** exit codes say who must act, as in every CLI built from `cli-design`: `1` the caller fixes its command, `2` the operator acts on their machine, `3` someone at the source acts, `75` nobody acts (wait, then run it again), `70` a bug. `E_HTTP_TRANSIENT`, `E_NETWORK` and `E_LOGIN_NO_ANSWER` exit `75`, not `2`. `E_NODE_VERSION`, `E_CONFIG`, `E_FILE_DENIED`, `E_DISK_FULL`, `E_MISSING_API_KEY`, `E_LOGIN_REQUIRED`, `E_LOGIN_DENIED`, `E_CODEX_MISSING`, `E_NO_TTY`, `E_SKILL_DENIED`, `E_OUT_OF_CREDIT`, `E_NO_ANSWER`, `E_NOT_NPM_INSTALL`, `E_INSTALL_FAILED` and `E_SKILL_REINSTALL_FAILED` exit `2`, not `1`. `E_NO_LATEST` exits `3`, not `1`. The root help's exit table lists only the classes image-gen produces, each with its examples.
|
|
62
|
+
- **Breaking:** a provider's 401 or 403 is `E_TOKEN_REJECTED` (exit 2), no longer `E_HTTP`, with the same `details.status` and a `hint` naming `image-gen doctor`.
|
|
63
|
+
- **Breaking:** `E_QUOTA_EXHAUSTED`'s `--json` field `details.resetsAt` is now `details.retryAt`, still set only when the plan said when. Any exit-75 diagnostic may carry `details.retryAt`.
|
|
64
|
+
- The `hint` of `E_NETWORK` and of `E_HTTP_TRANSIENT` says whether a rerun may pass.
|
|
65
|
+
- **Breaking:** the skill takes the layout and words every `cli-design` CLI's skill shares. `references/setup.md` opens with `Check`, `Install`, `Update` and `Sandbox`; its anchors `#checking`, `#installing` and `#updating` are gone. Failures lists image-gen's own rows, then the shared rows, then a paragraph on a skill and CLI that differ: the rows the shared ones now cover go (`E_OPTION`, `E_TIMEOUT`, the exit 75 codes, the exit 2 and exit 1 catch-alls), exit `3` gets a row, and exit `75` waits for `details.retryAt`. `E_REGISTRY_AUTH` gets a row for the sandbox, whose proxy refuses the registry with a 403 that npm reports as that code. `setup.md` says how to install again past a stale npm cache. Feedback has the user write to image-gen's author at duyquangnvx@gmail.com.
|
|
66
|
+
- A bug's message names image-gen's author at duyquangnvx@gmail.com, as the skill's Feedback does, not the GitHub new-issue page.
|
|
67
|
+
- **Breaking:** `update` and `doctor` ask npm's registry for the version tagged `latest`, and `update` installs it with the npm beside the running Node, each npm call with a new empty cache so a stale one cannot hide a release; neither runs `gh` any more. `doctor`'s `releases` row is `registry`: `--json` field `data.releases` is `data.registry`, with `url` (the registry), `latest`, `standing` and `problem`, and the text prints `registry <url>: latest <version>, this build is current`. Codes `E_GH_MISSING`, `E_GH_AUTH`, `E_GH_FAILED` and `E_RELEASES_UNREACHABLE` are gone: npm that cannot reach the registry is `E_REGISTRY_UNREACHABLE` (exit 75), the registry refusing npm's credentials `E_REGISTRY_AUTH` (exit 2), any other npm failure `E_NPM_FAILED` (exit 2). `E_NO_LATEST` (exit 3) means npm named no `latest` version, as before the first release on the registry.
|
|
68
|
+
- `E_NOT_NPM_INSTALL`'s `hint` is the install line, `npm i -g @duyquangnvx/image-gen`, where it was the `gh release download` lines.
|
|
69
|
+
- No version prints with a `v` any more, as in `0.12.1 (a1b2c3d)`: `skill install`, `update`, and `doctor`'s `node` and `registry` rows. Git tags and changelog headings keep it.
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
- `update` no longer fails after a finished install when a temporary folder cannot be removed.
|
|
74
|
+
- A `PartialResultError` keeps the failure of a call that failed before `abortSignal` aborted, such as a 401, where every failed position used to carry the abort.
|
|
75
|
+
|
|
76
|
+
## v0.12.1 (2026-10-07)
|
|
77
|
+
|
|
78
|
+
### Upgrade from v0.12.0
|
|
79
|
+
|
|
80
|
+
- A script that reads `doctor`'s text reads `doctor --json` instead, whose rows did not change.
|
|
81
|
+
|
|
82
|
+
### Changed
|
|
83
|
+
|
|
84
|
+
- `doctor`'s text prints one row per check, a padded label then its facts, in the order of `doctor --json`'s rows: `build` (with the changelog's path), `node`, `pin`, one row per provider, `install`, `releases`, `skill`, then one `next` row per command. The `ok` / `—` / `fail` column is gone: a problem prints in red, with its hint on the next line when doctor does not fail on it, and an unset provider a run can do without prints dim.
|
|
85
|
+
|
|
86
|
+
## v0.12.0 (2026-10-07)
|
|
87
|
+
|
|
88
|
+
### Upgrade from v0.11.0
|
|
89
|
+
|
|
90
|
+
- A script that retries `image-gen update` on `E_NETWORK` matches `E_RELEASES_UNREACHABLE` instead.
|
|
91
|
+
- A script or agent that reads `image-gen --json doctor` reads the build from `data.build.version`, `data.build.commit` and `data.build.builtAt` instead of `data.version`, `data.commit` and `data.builtAt`.
|
|
92
|
+
- It tells a failing row by its `problem` instead of `ok`: `data.node.problem`, `data.pin.problem` and `data.providers.<name>.problem` are `null` when the row is fine, and `ok` is gone from all three.
|
|
93
|
+
- It tells an install `update` can replace by `data.install.problem` being `null`; `data.install.prefix` is now always there, `null` when this is not npm's global install.
|
|
94
|
+
|
|
95
|
+
### Added
|
|
96
|
+
|
|
97
|
+
- When an agent runs it (`CLAUDECODE`, `AI_AGENT`, `CODEX_SANDBOX` and the other markers `gh` reads), a mistyped command, an unknown flag or a missing argument prints that command's full help on stderr after the error, so the agent needs no `--help` round trip.
|
|
98
|
+
- Every `--help`, the program's and each command's, ends with one to three examples of how to run it, their comments dimmed as `--color` says. The program's `--help` now prints the version lines first, then the exit codes, then its examples.
|
|
99
|
+
- `split` and `generate --n` end their text output with a count on stderr, such as `2 pieces` or `3 images`, also after a failure or Ctrl-C, so an agent reading the result knows how many files were written without counting lines.
|
|
100
|
+
- `doctor` lists the commands that fix what it found and does not fail on, in `data.next` of `doctor --json` and as one `next:` line each, as `update` does: `image-gen skill install` when the installed skill is not this version's.
|
|
101
|
+
- `doctor` asks GitHub, through `gh` and for at most 10 s, for the latest release, and reports how this version stands against it in a `releases` row (`data.releases.standing`: `behind`, `current` or `ahead`); `next` then names `image-gen update` when a newer release is out and `update` can replace this install. A `gh` that fails, is missing (`E_GH_MISSING`, also for a `GH_PATH` that names no `gh`), or does not answer in time (`E_RELEASES_UNREACHABLE`) is the row's `problem` and never fails doctor. In Claude Code's sandbox the row needs `api.github.com`. The `gh release list` and `gh release view` lines it replaces stay in `--help`.
|
|
102
|
+
- `skill install --json` reports the build whose skill it installed in `data.build`, and its text names the commit beside the version.
|
|
103
|
+
|
|
104
|
+
### Changed
|
|
105
|
+
|
|
106
|
+
- Breaking: `image-gen update` that cannot reach GitHub through `gh` fails `E_RELEASES_UNREACHABLE` (exit 2), no longer `E_NETWORK`, which now means only a provider never reached.
|
|
107
|
+
- Under `--json`, a parse failure's hint names the command's `--help`, as in ``see `image-gen doctor --help` ``, after commander's guess when there is one. A prompt that reads as a flag keeps its hint the command to copy.
|
|
108
|
+
- `E_NODE_VERSION`'s hint says how to make the newer Node the default under nvm, as in `nvm install 22 && nvm alias default 22`.
|
|
109
|
+
- The shipped skill hands the user `doctor`'s `data.next` in order and does `update`'s `data.upgrade` steps oldest first, looks for an install under another nvm Node with `ls`, has rows for `E_SKILL_REINSTALL_FAILED`, `E_SKILL_DENIED` and `E_NODE_VERSION`, and on a bug offers a message for the user to send to the author.
|
|
110
|
+
- Breaking: every row of `doctor --json` is its facts plus `problem`, a diagnostic or `null`, with no `ok`, as in every CLI scaffolded from the same template: the build is `data.build`, `data.node` and `data.pin` gain `problem`, `data.skill` gains `problem: null`, and `data.install` is `{prefix, problem}` with one of them `null`.
|
|
111
|
+
|
|
112
|
+
### Fixed
|
|
113
|
+
|
|
114
|
+
- `image-gen update` whose `gh` names no latest release tagged `vX.Y.Z` fails `E_NO_LATEST` (exit 1), quoting `gh`, instead of exiting 70 as a bug.
|
|
115
|
+
- `image-gen update` on npm 9 or older that is denied a write fails `E_FILE_DENIED`, naming the path, as on newer npm, instead of `E_INSTALL_FAILED`.
|
|
116
|
+
- When `image-gen update` fails both to reinstall the skill and to read the new version's changelog, it reports both failures instead of only the changelog's, and the second Ctrl-C message names the changelog read while it runs.
|
|
117
|
+
- `skill install` that runs out of space is `E_DISK_FULL`, exit 1, instead of exiting 70 as a bug. A denied write is `E_SKILL_DENIED` when only the user can lift it, with a hint that says how: the `!` command where Claude Code's sandbox denies it (on Linux also `EROFS`, since the sandbox mounts what it denies read-only), and on Windows the command to run again once the program holding a file closes it. Any other denied write, such as a read-only disk on macOS, is `E_FILE_DENIED`, exit 1, with no hint, instead of a bug.
|
|
118
|
+
- `image-gen update` gives the `!` hint only when npm is denied a write where Claude Code's sandbox is the cause, in the same words as `skill install`; on Windows the hint names the program holding a file instead. A denied write in its temp directory, which the sandbox allows, no longer suggests the sandbox. npm denied a write with `EROFS` or `EBUSY` is `E_FILE_DENIED`, naming the path, instead of `E_INSTALL_FAILED`.
|
|
119
|
+
|
|
120
|
+
## v0.11.0 (2026-10-07)
|
|
121
|
+
|
|
122
|
+
### Upgrade from v0.10.3
|
|
123
|
+
|
|
124
|
+
- A script that reads the installed skill from `image-gen --json doctor` or `image-gen --json update` reads `data.skill.state` and `data.skill.dir` instead of `data.skill.installed.state` and `data.skill.installed.dir`; `data.skill.path`, the skill shipped in the package, is gone.
|
|
125
|
+
- A script that reads the build time from `image-gen --version` reads `builtAt` from `image-gen --json doctor`.
|
|
126
|
+
|
|
127
|
+
### Changed
|
|
128
|
+
|
|
129
|
+
- Breaking: `skill` in `doctor --json` and `update --json` is the installed skill's `dir` and `state` (`same`, `differs` or `missing`), as every CLI scaffolded from the same template reports it, no longer wrapped in `installed` beside the shipped skill's `path`.
|
|
130
|
+
- Breaking: `--version` prints only the version and the commit, as in `0.11.0 (f7beab4)`, which is what the release checks. The build time stays in the version line at the end of `--help` and `doctor`, and in `builtAt` of `doctor --json`.
|
|
131
|
+
|
|
132
|
+
## v0.10.3 (2026-10-07)
|
|
133
|
+
|
|
134
|
+
### Upgrade from v0.10.2
|
|
135
|
+
|
|
136
|
+
- Nothing to do.
|
|
137
|
+
|
|
138
|
+
### Added
|
|
139
|
+
|
|
140
|
+
- `image-gen split <png> --out-dir <dir>` writes each Piece of an image with alpha, such as a kit of UI parts drawn in one call, to its own png in reading order, locally with no model call. Parts `--gap` px apart (8 by default) are one Piece; Pieces under `--min-area` visible pixels (64) are listed in `dropped`, not written. The library exports it as `splitPieces()`.
|
|
141
|
+
|
|
142
|
+
### Changed
|
|
143
|
+
|
|
144
|
+
- The shipped skill makes `--transparent` the route on every model that takes it and `--cutout` the fallback for those that refuse it, and says to fit again after a game's entry takes the art's aspect.
|
|
145
|
+
|
|
146
|
+
### Fixed
|
|
147
|
+
|
|
148
|
+
- The shipped skill names the plate `--cutout` keeps as `fox.plate.png` for `fox.png`, as the CLI writes it, and says how to make a variant of a cutout: `edit` its plate, then key it with `cutout`.
|
|
149
|
+
|
|
150
|
+
## v0.10.2 (2026-10-07)
|
|
151
|
+
|
|
152
|
+
### Upgrade from v0.10.1
|
|
153
|
+
|
|
154
|
+
- Nothing to do.
|
|
155
|
+
|
|
156
|
+
### Added
|
|
157
|
+
|
|
158
|
+
- The shipped skill's `references/art-entries.md` turns one entry of a game's art manifest, as the `game-ui-sheets` skill plans them, into prompt, flags and `fit`, and checks the game's art gate against the `alpha` and `deviation` the CLI reports.
|
|
159
|
+
|
|
160
|
+
## v0.10.1 (2026-10-06)
|
|
161
|
+
|
|
162
|
+
### Upgrade from v0.10.0
|
|
163
|
+
|
|
164
|
+
- Nothing to do.
|
|
165
|
+
|
|
166
|
+
### Changed
|
|
167
|
+
|
|
168
|
+
- The shipped skill keeps checking, installing, updating and the sandbox settings in `references/setup.md`, with a settings block per provider, and leaves to `--help` what it already says. Its description now also fires when image-gen has to be installed, updated or checked, or when a command fails, and it ends a task by telling the user what image-gen got in the way of.
|
|
169
|
+
|
|
170
|
+
### Fixed
|
|
171
|
+
|
|
172
|
+
- The install lines download the tarball into `/tmp/image-gen`, emptied first, so a second install no longer meets the tarball of the first.
|
|
173
|
+
- The README says that only `openai` defaults to `quality=medium`; the plan picks its own.
|
|
174
|
+
|
|
175
|
+
## v0.10.0 (2026-10-06)
|
|
176
|
+
|
|
177
|
+
### Upgrade from v0.9.0
|
|
178
|
+
|
|
179
|
+
- Install this release by hand, as the README's install section says, since v0.9.0 has no `image-gen update`; from then on, `! image-gen update` upgrades.
|
|
180
|
+
- A script that reads `model` from a `generate` or `edit` result or a Provenance Record now gets `provider:id`, such as `openai:gpt-image-2.5-flare`: pass it to `--model` as it is, and drop any code that joins `provider` and `model` with a colon.
|
|
181
|
+
- A script that ran `image-gen --json login`, or `login` with stdin or stdout redirected, now gets `E_NO_TTY`: run `image-gen login` yourself in a terminal (inside Claude Code, `! image-gen login`).
|
|
182
|
+
- A script that compares `image-gen --version` with a version number: read the first word of the line, or `version` from `image-gen --json doctor`.
|
|
183
|
+
- Code that reads `loginStatus().error.command` narrows the error with `instanceof LoginRequiredError` first: it can now be a `LoginDeniedError`.
|
|
184
|
+
- Code that catches an aborted `generate` or `generateCutout` by its abort reason also checks for a `PartialResultError` whose `cause` is that reason: its `result` holds the billed images that came back before the abort.
|
|
185
|
+
- A script that retries on exit 2 no longer sees a request that may have been billed: `E_NO_ANSWER` and `E_TIMEOUT` now exit 1. Put them to a person instead of rerunning them, and stop matching `E_NETWORK` for a provider that was reached but never answered.
|
|
186
|
+
- Code that reads `status` or `retryable` from a `ProviderFailedError` switches on `reach.kind` instead: only `answered` carries `status` and `retryable`, and a request that got no status is `unreached`, `unanswered` or `refreshUnanswered`.
|
|
187
|
+
- A script that reads `data.images` tests `provenance`, `path`, `width`, `height` and `mediaType` for `null` instead of for absence: every entry now has every key, and a `--cutout` plate whose keyed image was not written is no longer `{ plate }` alone.
|
|
188
|
+
- A script that reads `details.provider` from `E_QUOTA_EXHAUSTED` or `E_OUT_OF_CREDIT` gets the CLI's name for the provider, such as `codex` or `openai`, as `data.provider` and `--model` spell it, instead of the library's `codex.image` or `openai.image`.
|
|
189
|
+
|
|
190
|
+
### Added
|
|
191
|
+
|
|
192
|
+
- `image-gen update` upgrades a CLI npm installed globally to the latest GitHub release, found and downloaded through `gh` (`GH_PATH` pins it): it installs the tarball with the npm beside the running Node, has the new CLI reinstall the skill when it was installed, prints the "Upgrade from" steps of every version in between, oldest first, and ends with the hint `image-gen doctor`. Already on the latest release, or ahead of it, it installs nothing. Any other install (linked from a clone, `npx`, a clone's `node dist/…`, pnpm) is refused with `E_NOT_NPM_INSTALL`, whose hint gives the manual install lines. `--json` gives `from`, `latest`, `installed`, `skill` (as `doctor` reports it), `skillReinstalled`, `upgrade` and `next`. Its failures exit 1 with `E_GH_MISSING`, `E_GH_AUTH`, `E_GH_FAILED`, `E_INSTALL_FAILED` (with npm's last lines), `E_SKILL_REINSTALL_FAILED` (the new CLI's code in `details.code`) or `E_FILE_DENIED` (hinting `! image-gen update`), and exit 2 with `E_NETWORK` or `E_HTTP_TRANSIENT` for an unreachable GitHub or a GitHub 5xx. Ctrl-C stops it at once until npm starts; after that, npm and the skill reinstall finish however long they take, the upgrade steps are printed, and it exits 130, unless a second Ctrl-C quits at once. Not tested on Windows.
|
|
193
|
+
- `doctor` gains an `install` row, and `install` in `--json` (`{ prefix }` or `{ problem }`), saying from which npm prefix `image-gen update` upgrades this install or why it cannot; the row never fails `doctor`. The version block of `--help` and `doctor` names `image-gen update`.
|
|
194
|
+
- An unknown command whose name exists further down the tree is hinted by its full path: `image-gen install` hints `image-gen skill install`.
|
|
195
|
+
- While `generate` or `edit` waits on the provider, it prints `waiting on <provider:id>, <N>s` on stderr about every 15 seconds, and `resending to <provider:id> (attempt <N>)` each time the AI SDK resends a failed call, so a long call no longer looks hung. `--json` silences both.
|
|
196
|
+
- The library's `generate`, `edit`, `generateCutout` and `removeBackground` take an `onResend` callback that reports each resend with its attempt number. Its option type is exported as `CallHooks`, and its argument as `Resend`.
|
|
197
|
+
- The skill has a row for `command not found`, which sends the user to reinstall into the shell's own Node through `!` (a global install is per Node version under nvm and nvm-windows) and never to `npx image-gen`, and a row for Claude Code's sandbox, naming the domains each provider needs and the Plan Login directory to allow. It names `E_LOGIN_DENIED` and `E_NO_TTY`.
|
|
198
|
+
- An image written at a size other than the one asked for, by `--size` or by an edit's target, carries `askedFor: { width, height }` in `data.images` and in its Provenance Record, and its text line ends `, asked WxH`, so a Degraded Result accepted with `--allow-warnings` stays visible. Images at the size asked for carry no such field.
|
|
199
|
+
- Each `doctor` provider row carries `problem`: the diagnostic that keeps the provider from being ready, or `null`.
|
|
200
|
+
|
|
201
|
+
### Changed
|
|
202
|
+
|
|
203
|
+
- Breaking: `model` in `generate` and `edit` results and in Provenance Records is `provider:id`, as `--model` takes it and as `doctor` and `E_NO_MODEL` already spell it; `provider` stays its own key.
|
|
204
|
+
- Breaking: `login` runs only in a terminal and without `--json`. Elsewhere it exits 1 with the new `E_NO_TTY`, whose hint asks the operator to type `! image-gen login`, so an agent no longer hangs on a browser sign-in.
|
|
205
|
+
- Breaking: `--version` prints the commit (with `+dirty` when the build had uncommitted changes) and the build time after the version, as in `0.9.0 (f7beab4, built 2026-10-05T15:00:24.797Z)`, so a linked dev build is told apart from a release. The version block at the end of `--help` and `doctor` shows the same, and `doctor --json` gains `commit` and `builtAt`.
|
|
206
|
+
- Breaking: a request that may have reached the provider and brought no answer back, such as a timeout waiting for the answer, a connection dropped mid-request or a body lost after a 200, is never resent, by the library or the CLI. The CLI reports it as the new `E_NO_ANSWER`, exit 1, naming the provider and saying it may have been billed. `E_NETWORK`, exit 2, now means only that the request never reached the provider (refused, unresolvable, unreachable), which is still resent. `E_TIMEOUT` now exits 1, with the same hint: rerun only if paying again is acceptable. A Plan Login token refresh that got no answer is not resent either, since it may have spent the refresh token. It is the new `E_LOGIN_NO_ANSWER`, exit 2: nothing was billed, and if the token was spent the rerun fails `E_LOGIN_REQUIRED`, so a retrying script stops.
|
|
207
|
+
- Breaking: in the library, `ProviderFailedError` says how far the request got in one field, `reach`: `answered` (with `status` and `retryable`), `unreached`, `unanswered` (may have been billed), or `refreshUnanswered` (the plan's token refresh got no answer). It replaces `status` and `retryable` on the error.
|
|
208
|
+
- Breaking: every entry of `data.images` has the same keys in every command. `provenance` is `null` for an image whose Provenance Record could not be written, and a `--cutout` plate whose keyed image could not be written has `path`, `width`, `height`, `mediaType` and `provenance` `null` beside its `plate`.
|
|
209
|
+
- Breaking: `details.provider` in `E_QUOTA_EXHAUSTED`, `E_OUT_OF_CREDIT`, `E_NO_ANSWER` and `E_LOGIN_NO_ANSWER` is the CLI's name for the provider (`codex`, `openai`, `google`), the same as `data.provider`.
|
|
210
|
+
- `E_FILE_DENIED` names the errno the file system answered, as in `cannot read '<path>' (EACCES)`.
|
|
211
|
+
- Breaking: in the library, a request for several images aborted after some came back throws a `PartialResultError` holding them, with the abort's reason as `cause`, instead of throwing the abort itself. A request aborted before any came back still throws the abort.
|
|
212
|
+
- `fit`'s images carry `width` and `height`, like every other command's.
|
|
213
|
+
- The shipped skill sends previews and contact sheets to a directory inside the project, where Claude Code's sandbox lets a command write, instead of a temp directory.
|
|
214
|
+
- Breaking: on the plan, a Plan Login the file system will not let the library read, or a lapsed one whose directory it cannot write, is the new `E_LOGIN_DENIED` (exit 1), not `E_LOGIN_REQUIRED` or a bug, and the lapsed one is refused before its single-use refresh token is spent; `loginStatus()` can now answer with a `LoginDeniedError`. A login that has not lapsed still runs from a directory that refuses saves. The hint names the directory, says a sandbox may be denying it, and gives the `sandbox.filesystem.allowWrite` entry. `doctor` checks that the directory takes a save and fails the plan's row when it does not, with the reason; it fails `doctor` itself only when the plan is the pinned model. The library throws `LoginDeniedError` from `image-gen/codex`, and `loginStatus(true)` checks the save.
|
|
215
|
+
|
|
216
|
+
### Fixed
|
|
217
|
+
|
|
218
|
+
- An `--out` the sandbox or file system will not let `generate` or `edit` write no longer bills a call whose images are then lost: before calling the model, they create and remove a temp file beside the first output, and a refusal there is the write's own diagnostic, such as `E_FILE_DENIED`. Every error raised after the provider answered, such as `E_DISK_FULL`, `E_PROVIDER_WARNED` or the failed part of a Partial Result, adds to its hint that the call was billed and to ask before rerunning. An output another program took during the run is still `E_OUTPUT_EXISTS`, without the `--force` hint that would bill again.
|
|
219
|
+
- A run stopped by Ctrl-C, SIGTERM or its 9-minute deadline keeps the images that already came back: with `--n 4`, three finished images are written with their Provenance Records and listed in `data.images` when the fourth hangs, instead of being thrown away while the `E_TIMEOUT` hint said to rerun all four. The exit is unchanged: 130 or 143 for a signal, `E_TIMEOUT` (exit 1) for the deadline. A second Ctrl-C still quits at once.
|
|
220
|
+
- A write that fails mid-image no longer hides the files already on disk, or stops the images after it: every billed image is still written and reported, then the run fails with the first write's error. An image whose Provenance Record could not be written has `provenance: null`, and under `--cutout` a Chroma Plate whose keyed image could not be written is listed with its `plate`, ready to re-key with `cutout`.
|
|
221
|
+
- One billed image that cannot be finished, such as a plate that cannot be keyed, no longer loses the others: they are written and reported first, and the failure says the call was billed. A failure inside image-gen there is still a bug (exit 70), whose message says the image was billed; without `--json`, the images written before it are listed first.
|
|
222
|
+
- When two refusals apply, the one whose fix comes first is reported: `generate --transparent --backdrop <hex>` is refused as two routes to alpha instead of being told to add `--cutout`, and a missing `--ref`, Edit Target or `--mask` is `E_FILE_NOT_FOUND` before an existing output's `E_OUTPUT_EXISTS`, whose `--force` hint would only lead to it.
|
|
223
|
+
- A command line that fails to parse, such as an unknown command or flag, now prints its error in the colour `--color` or `--no-color` asked for; before, only the environment decided, so `FORCE_COLOR` coloured it even under `--no-color`.
|
|
224
|
+
- A new output appears whole or not at all: a run killed mid-write no longer leaves a truncated image under the output name, so `E_OUTPUT_EXISTS` means the file is finished. On a file system without hard links, such as exFAT, new files are written directly as before. A file another program holds (Windows `EBUSY`) is `E_FILE_DENIED`, and a Windows rename that races another writer is retried briefly.
|
|
225
|
+
- `doctor` no longer loses its whole report when the env file cannot be read (a sandbox denying `~/.config`, a file without permission): it reports every row, fails each provider whose key the file might hold with the file's path and errno as its `problem` and its line, and fails with the file's error (`E_FILE_DENIED`) among its other findings only when the pinned model's provider needed that file.
|
|
226
|
+
|
|
227
|
+
## v0.9.0 (2026-10-04)
|
|
228
|
+
|
|
229
|
+
### Upgrade from v0.8.1
|
|
230
|
+
|
|
231
|
+
- A Region padded for the old inward fade now changes more than before: give `--region`, or the library's `confineTo(target, { region })`, the box around what should change.
|
|
232
|
+
|
|
233
|
+
### Changed
|
|
234
|
+
|
|
235
|
+
- `--region` and the library's `confineTo(target, { region })` now name the rectangle the edit changes. The whole rectangle takes the edit, and the fade lies in a band outside it, an eighth of its shorter side wide and ending at the target's border; past the band the target's pixels are kept. A Region padded for the old inward fade now changes more than before: pass the box around what should change.
|
|
236
|
+
- The shipped skill reads alpha and confinement details from `references/alpha.md` and `references/confinement.md` only when a run needs them, keeps a set's commands in `.image-gen/<set>/` so a later session can finish it, steers keying away from soft fur, and splits a large repaint set across sheets.
|
|
237
|
+
|
|
238
|
+
### Fixed
|
|
239
|
+
|
|
240
|
+
- The shipped skill routes a refusal before billing (`E_UNSUPPORTED_REQUEST`) to its `hint` instead of a skill-version check, and no longer renames a finished asset when a set is rerun (`E_OUTPUT_EXISTS`).
|
|
241
|
+
- The shipped skill now says correctly where `--size` is checked, that `openai:gpt-image-2` refuses `--transparent`, what an edit in other proportions writes, and that only `openai:` defaults to `quality=medium`.
|
|
242
|
+
|
|
243
|
+
## v0.8.1 (2026-10-04)
|
|
244
|
+
|
|
245
|
+
### Upgrade from v0.8.0
|
|
246
|
+
|
|
247
|
+
- If you linked the skill with `ln -s`, run `image-gen skill install` once (inside Claude Code, `! image-gen skill install`): it replaces the link with a copy that `doctor` compares with the CLI.
|
|
248
|
+
|
|
249
|
+
### Added
|
|
250
|
+
|
|
251
|
+
- `image-gen skill install` replaces `~/.claude/skills/image-gen` with the Claude Code skill shipped beside the CLI, whole, and says what was there before (`data.was`: `missing`, `differs` or `same`). Inside Claude Code, type it as `! image-gen skill install`: the sandbox denies that write, and a denied write is the new `E_SKILL_DENIED`, exit 1. If you linked the skill with `ln -s`, run it once: it replaces the link with a copy and leaves the linked directory alone.
|
|
252
|
+
- `doctor` says whether the installed skill matches this CLI, comparing contents: `same`, `differs` or `missing`, in `data.skill.installed`. It never changes doctor's exit code.
|
|
253
|
+
|
|
254
|
+
### Changed
|
|
255
|
+
|
|
256
|
+
- When a flag or code the shipped skill names does not behave as described, the skill tells the agent to check `doctor` and, if the installed skill differs, ask you to run `! image-gen skill install` instead of running it itself.
|
|
257
|
+
|
|
258
|
+
## v0.8.0 (2026-10-04)
|
|
259
|
+
|
|
260
|
+
### Upgrade from v0.7.2
|
|
261
|
+
|
|
262
|
+
- On `codex:gpt-image-2`, drop `--option quality=…` or pass `quality=auto`: any other level is now refused.
|
|
263
|
+
|
|
264
|
+
### Changed
|
|
265
|
+
|
|
266
|
+
- A run on `codex:gpt-image-2` asks for `quality=auto` instead of `medium`, and `--option quality=…` on the plan takes only `auto`; any other level is `E_UNSUPPORTED_REQUEST` before anything is sent. The plan picks its own quality whatever is asked (`medium` on 2026-09-29, `low` on 2026-10-04), so asking for `medium` failed every run with `E_PROVIDER_WARNED`. Each image's `quality` still reports what the plan produced. The library's prescription for the codex model takes only `auto` too.
|
|
267
|
+
|
|
268
|
+
### Fixed
|
|
269
|
+
|
|
270
|
+
- `image-gen login` works on Windows, where npm installs the Codex CLI as `codex.cmd`: it used to report `E_CODEX_MISSING` with Codex installed. Ctrl-C during the sign-in now stops Codex there too, so nothing keeps holding port 1455.
|
|
271
|
+
|
|
272
|
+
## v0.7.2 (2026-10-04)
|
|
273
|
+
|
|
274
|
+
### Upgrade from v0.7.1
|
|
275
|
+
|
|
276
|
+
- Nothing to do.
|
|
277
|
+
|
|
278
|
+
### Added
|
|
279
|
+
|
|
280
|
+
- `image-gen login` signs in to a ChatGPT plan for `--model codex:gpt-image-2`. It runs the Codex CLI's own `codex login` against `~/.codex-imagegen`, apart from your interactive Codex login, creating that directory readable by you alone, and replaces any login already there. It succeeds only once the saved login holds ChatGPT tokens, and reports where it lives (`data.login.path`). The Codex CLI's own output goes to stderr, so `--json` still prints one envelope. A sign-in that fails or saves no ChatGPT tokens is `E_LOGIN_REQUIRED`; without the Codex CLI on `PATH` it is the new `E_CODEX_MISSING`, with the install command as the hint. Both exit 1.
|
|
281
|
+
|
|
282
|
+
### Changed
|
|
283
|
+
|
|
284
|
+
- `E_LOGIN_REQUIRED`'s hint, from `generate`, `edit` and `doctor`, is now `image-gen login` instead of the `mkdir … && CODEX_HOME=… codex login` line. The library's `LoginRequiredError` still names that line, and `createCodex()` now exposes the directory it reads as `loginHome`.
|
|
285
|
+
|
|
286
|
+
## v0.7.1 (2026-10-04)
|
|
287
|
+
|
|
288
|
+
### Upgrade from v0.7.0
|
|
289
|
+
|
|
290
|
+
- Nothing to do.
|
|
291
|
+
|
|
292
|
+
### Changed
|
|
293
|
+
|
|
294
|
+
- `image-gen --help` describes the CLI as generating and editing images, billed to a ChatGPT plan, an OpenAI API key or a Google API key; it used to leave out editing and Google.
|
|
295
|
+
- The README is an overview: install, the library and CLI at a glance, who pays, the errors and the exit codes. Each flag's behaviour is in `image-gen <command> --help` and the shipped skill.
|
|
296
|
+
|
|
297
|
+
## v0.7.0 (2026-10-04)
|
|
298
|
+
|
|
299
|
+
### Upgrade from v0.6.0
|
|
300
|
+
|
|
301
|
+
- Update a script that branches on codes or exit codes: an account out of credit is `E_OUT_OF_CREDIT`, exit 1, not `E_QUOTA_EXHAUSTED`, exit 75; a provider answer a retry may pass is `E_HTTP_TRANSIENT`, and `E_HTTP` now exits 1; a bug exits 70 instead of 1.
|
|
302
|
+
- Have a script reading `contact-sheet --json` read the sheet from `data.images[0]` instead of `data.path`, `data.width` and `data.height`.
|
|
303
|
+
|
|
304
|
+
### Changed
|
|
305
|
+
|
|
306
|
+
- Each exit code says who or what fixes the failure, and each diagnostic code always exits the same way: 0 ok, 1 refused (fix the command, or a person must act), 2 a retry may pass, 70 a bug, 75 retry only after waiting, 130 and 143 interrupted. `image-gen --help` ends with this table, and `docs/adr/0009-exit-codes-say-whether-retrying-waiting-or-a-person-fixes-it.md` gives the reasons.
|
|
307
|
+
- A bug in the CLI, anything it did not foresee, exits 70 instead of 1, so a script or agent can tell it from a mistake in the command; its text is unchanged.
|
|
308
|
+
- An OpenAI account out of credit fails with the new code `E_OUT_OF_CREDIT` and exits 1, instead of `E_QUOTA_EXHAUSTED` with exit 75: waiting never refills Credit, so a script that retries on 75 no longer waits forever. `E_QUOTA_EXHAUSTED` (exit 75) now means only the ChatGPT plan's quota, with `details.resetsAt` when known. The message and hint are unchanged, and the library still throws one `QuotaExhaustedError`.
|
|
309
|
+
- A provider answer a retry may pass (5xx, 408, or a 429 that is not a used-up quota) is the new `E_HTTP_TRANSIENT` with exit 2, where it used to be `E_HTTP` with exit 2; `E_HTTP` now always exits 1, a refusal no retry fixes. Both keep `details.status`. `E_NETWORK`, when no answer came back, always exits 2. `E_TIMEOUT`'s hint now says the call may have been billed even though nothing came back, and to rerun once.
|
|
310
|
+
- In text mode a parse failure prints as a diagnostic, such as `error [E_ARGS]: unknown command 'genrate'` with `hint: Did you mean generate?`, instead of commander's own `error:` line; with no subcommand the help is followed by the `needs a subcommand` diagnostic. Its exit comes from its code, `E_ARGS` or `E_OPTION`, both 1. With `--debug`, a text-mode parse failure is followed by a stack trace, as it already was under `--json`.
|
|
311
|
+
- A prompt or instruction that starts with `-` and holds whitespace is `E_ARGS`, saying it reads as a flag, with a hint giving the same command with `-` in its place and a heredoc, instead of `E_OPTION` for an unknown option.
|
|
312
|
+
- `--region` with `--mask` is `E_OPTION`, like every other pair of conflicting flags, instead of `E_ARGS`.
|
|
313
|
+
- A run stopped by Ctrl-C or SIGTERM after its work finished exits 130 or 143 instead of 0, with `E_INTERRUPTED` and the delivered `data` under `--json`, and the result text with no diagnostic in text mode, as a run stopped mid-way does. `E_INTERRUPTED`'s message is now "stopped by a signal" instead of "interrupted before it finished", since the run may have finished. The shipped skill's `E_INTERRUPTED` row now says `data.images` lists what was written, and billed, before the stop, so only the missing images are rerun.
|
|
314
|
+
- `image-gen contact-sheet --json` reports the sheet it wrote in `images`, `[{ path, width, height }]`, as every other command that writes an image does, so a script reads `data.images[].path` after any command. It used to report `path`, `width` and `height` at the top of `data`; `cell` and `inputs` are unchanged.
|
|
315
|
+
|
|
316
|
+
## v0.6.0 (2026-10-04)
|
|
317
|
+
|
|
318
|
+
### Upgrade from v0.5.1
|
|
319
|
+
|
|
320
|
+
- Pass `--model` on every `generate` and `edit`, or pin a model in an `image-gen.json`, such as `{ "model": "codex:gpt-image-2" }`: there is no default model any more.
|
|
321
|
+
- Library: construct `ProviderWarnedError` and `WrongSizeError` with the result and its positions in place of the warnings or sizes.
|
|
322
|
+
|
|
323
|
+
### Changed
|
|
324
|
+
|
|
325
|
+
- Nothing is billed unless someone chose who pays, so there is no default model: without `--model`, `generate` and `edit` take the Pin from the nearest `image-gen.json` from the working directory up, a project file holding only `{ "model": "codex:gpt-image-2" }`. With neither, the run is refused before anything is sent, `E_NO_MODEL`, naming each choice and who it bills in `details.choices`. It used to run `openai:gpt-image-2.5-flare`. `--model` wins over the Pin, and a malformed `image-gen.json` is `E_CONFIG`, naming the file.
|
|
326
|
+
- `image-gen doctor` reports the Pin and who it bills, and fails for a malformed `image-gen.json` or when the pinned model's provider has no key or login. With no Pin, a missing `OPENAI_API_KEY` no longer fails it: a provider no Pin names is only reported. `--json` gives the Pin as `pin`, `null` when there is none.
|
|
327
|
+
- An `edit` with no `--aspect` or `--size` is written at its Edit Target's size: the models paint their own canvas, so an answer in the target's proportions is rescaled, its line says `rescaled from <size>`, and `--json` reports `rescaledFrom`. An answer in other proportions is written as it came back and fails `E_WRONG_SIZE`, unless `--allow-warnings`. The library's `edit()` still returns the model's pixels unchanged.
|
|
328
|
+
- A Degraded Result, `E_PROVIDER_WARNED` or `E_WRONG_SIZE`, writes its billed images before the run fails, instead of throwing them away; `--allow-warnings` now only turns that into a success. In the library, `RequestDegradedError` (`ProviderWarnedError`, `WrongSizeError`) carries them in `result` and `positions`, as `PartialResultError` does, and the two subclasses' constructors take that result and its positions in place of the warnings or sizes.
|
|
329
|
+
- The library refuses an `edit()` with a `mask` on a Gemini model before sending, as it already did on the plan. The Refusal `no-mask` now carries the `modelId`, and the CLI's `E_UNSUPPORTED_REQUEST` names the model and offers `--region` or an `openai:` model.
|
|
330
|
+
|
|
331
|
+
### Added
|
|
332
|
+
|
|
333
|
+
- `image-gen edit --mask <png>` sends a Mask, a png the Edit Target's size, transparent where the target may change, and composites the answer through it locally, so outside the Mask the written image is the target's even where the model repainted. It takes no `--aspect` or `--size` (`E_OPTION`). A Mask of another size, without alpha, or with no transparent pixel is `E_ARGS` before anything is billed; Gemini and the plan cannot take one and refuse it with `E_UNSUPPORTED_REQUEST`.
|
|
334
|
+
- `image-gen edit --region x,y,w,h` keeps an edit to a rectangle of the Edit Target on any model, the plan included: the Region is never sent, and outside it the written image is the target's, byte for byte, with the edit fading in from the Region's edge. It takes no `--aspect` or `--size` (`E_OPTION`), and a Region reaching past the target, or `--region` with `--mask`, is `E_ARGS` before anything is billed.
|
|
335
|
+
- Under `--mask` or `--region`, an Edit Target that is not 8-bit sRGB, such as a 16-bit png or one tagged Display P3, is `E_ARGS` before anything is billed, since the composite would change every pixel of it; the hint gives the `magick` command that converts it.
|
|
336
|
+
- Every image `generate` and `edit` write has a Provenance Record beside it, `knight.png.json`: the CLI version, the model and who it billed, the full prompt, each input file by path and sha256, the Region and flags, the options sent, the image as `--json` describes it, the provider's `warnings`, and the elapsed time. Images written beside a failure get one too. `--json` gives each image's record as `provenance`, and an existing record is refused before anything is billed unless `--force`, as an existing `--out` is.
|
|
337
|
+
- `image-gen fit <png> --frame WxH --out <file>` is a Fit, locally, with no model call: the subject trimmed to its Subject Bounds, scaled whole to the largest size the Target Frame holds, and centred in a png at exactly that size, never cropped or stretched. The line and `--json` report the input's `subject`, the `frame`, each with its `aspect`, the `deviation` between the two, and the `scale`. An opaque input, or one with nothing visible, is `E_ARGS`, and an `--out` naming the input is refused even with `--force`.
|
|
338
|
+
- `image-gen contact-sheet <image>... --out <file>` lays images out on one numbered Contact Sheet, locally, with no model call: equal cells on a dark ground, each input over a checkerboard so its canvas edge and alpha show, numbered from 1 in argument order without needing any installed font. `--json` reports `path`, `width`, `height`, `cell`, and `inputs` as `[{ number, path }]`. An `--out` naming an input is refused even with `--force`.
|
|
339
|
+
- Every image with alpha, from `--transparent`, `--cutout` or `cutout`, reports its Subject Bounds: a `subject:` line before its `alpha:` line, such as `subject: 1887x492 at 12,40, aspect 3.84`, and `alpha.subject` in `--json`, `{ x, y, width, height, aspect }` or `null`. The library's `inspectCutout()` returns it as `subject`, counting every pixel with alpha 8 or more.
|
|
340
|
+
- Every run's last line says who was billed, and `--json` reports it as `billing`, with `modelSource` saying where the model came from: `{ "from": "flag" }`, or `{ "from": "project", "path" }`.
|
|
341
|
+
- The Claude Code skill ships in the tarball, so the installed copy describes the installed CLI, and `image-gen doctor` names its directory (`skill.path` in `--json`) to link from `~/.claude/skills/image-gen`.
|
|
342
|
+
- The library exports `confineTo(target, { mask } | { region })`, which refuses with `UnconfinableEditError` (its `reason` an `Unconfinable`) an Edit Target that is not 8-bit sRGB, a Mask that could not mean what it says, or a Region reaching past the target, before anything is sent, and returns the function that confines each edited image; with it the types `Confinement` and `Region`.
|
|
343
|
+
- The library exports `fitToFrame(image, { width, height })`, returning `Fitted` (`image`, `subject`, `scale`) and throwing `UnfittableImageError` with `reason` `opaque` or `empty`, the `SubjectBounds` type, and `pixelSize`, `sameSize` and `sameProportions`.
|
|
344
|
+
|
|
345
|
+
## v0.5.1 (2026-10-03)
|
|
346
|
+
|
|
347
|
+
### Upgrade from v0.5.0
|
|
348
|
+
|
|
349
|
+
- Nothing to do.
|
|
350
|
+
|
|
351
|
+
### Fixed
|
|
352
|
+
|
|
353
|
+
- A run that reports several problems at once, as `image-gen doctor` can, exits with the least hopeful of their codes: 1 if any, else 75, else 2. It used to take the highest number, so a problem no retry can fix beside a retryable one exited 2 and a looping script retried forever.
|
|
354
|
+
|
|
355
|
+
## v0.5.0 (2026-10-01)
|
|
356
|
+
|
|
357
|
+
### Upgrade from v0.4.1
|
|
358
|
+
|
|
359
|
+
- Library: read `generateCutout()`'s `plate` in place of `source`.
|
|
360
|
+
|
|
361
|
+
### Added
|
|
362
|
+
|
|
363
|
+
- `image-gen cutout <plate> --out <file>` keys a Chroma Plate, an image whose subject sits on one flat backdrop colour, into a PNG with alpha, locally: no model call, no key, nothing billed. `--backdrop <hex>` names the colour the plate was asked for, `--contract <px>` shrinks the matte, `--feather <sigma>` softens its edge, and `--json` reports the cutout's `alpha` as `--transparent` does. An `--out` that is the plate itself is refused even with `--force`.
|
|
364
|
+
- `image-gen generate --cutout` asks the model for a flat Chroma Backdrop (`#00ff00`, or `--backdrop <hex>`) and keys it out locally, so models that refuse `--transparent`, such as Gemini and `openai:gpt-image-2`, can produce alpha. Each cutout is written with its billed plate beside it as `<file>.plate.png`, and `--json` gives each image `alpha` and `plate`. It cannot be combined with `--transparent`.
|
|
365
|
+
- The library exports `isHexColour`.
|
|
366
|
+
|
|
367
|
+
### Changed
|
|
368
|
+
|
|
369
|
+
- `CutoutResult.source` from `generateCutout()` is renamed `plate`.
|
|
370
|
+
- When `generate --transparent` is refused for a model without a transparent background, the hint now points at `--cutout`.
|
|
371
|
+
|
|
372
|
+
## v0.4.1 (2026-10-01)
|
|
373
|
+
|
|
374
|
+
### Upgrade from v0.4.0
|
|
375
|
+
|
|
376
|
+
- Nothing to do.
|
|
377
|
+
|
|
378
|
+
### Changed
|
|
379
|
+
|
|
380
|
+
- A call the model answers without an image is sent once and never retried, by the CLI and the library alike, since nobody says whether it is billed. The AI SDK used to resend it up to twice unless the provider marked it content-filtered; `E_NO_IMAGE` (`NoImageGeneratedError` in the library) now arrives after the first.
|
|
381
|
+
|
|
382
|
+
### Fixed
|
|
383
|
+
|
|
384
|
+
- A provider option the AI SDK refuses before sending, such as `--aspect 7:5` or an unknown `imageConfig.imageSize` on Gemini, is `E_UNSUPPORTED_REQUEST` naming the field and the values it takes, instead of an exit with no envelope.
|
|
385
|
+
- `--model google:imagen-*` is `E_OPTION` before anything is built: the AI SDK runs only `gemini-*` models on google.
|
|
386
|
+
|
|
387
|
+
## v0.4.0 (2026-10-01)
|
|
388
|
+
|
|
389
|
+
### Upgrade from v0.3.0
|
|
390
|
+
|
|
391
|
+
- Replace `--provider codex` with `--model codex:gpt-image-2`, and `--provider openai` with `--model openai:<id>`. A run with neither now bills `OPENAI_API_KEY`, where it used to bill the ChatGPT plan.
|
|
392
|
+
|
|
393
|
+
### Changed
|
|
394
|
+
|
|
395
|
+
- `--model provider:modelId` picks the model and who is billed, and `--provider` is gone. With no `--model` a run is `openai:gpt-image-2.5-flare` on `OPENAI_API_KEY`; the ChatGPT plan is no longer the default and is asked for with `--model codex:gpt-image-2`, the only id its route runs.
|
|
396
|
+
- `image-gen doctor` lists every provider and fails only when the default model's provider is not ready; one a run can do without is marked as not set up.
|
|
397
|
+
- Every file written at `--out` is a PNG whatever the model answered: a JPEG or WebP answer is converted, its line says `converted from <type>`, and `--json` names each image's `mediaType`.
|
|
398
|
+
|
|
399
|
+
### Added
|
|
400
|
+
|
|
401
|
+
- `google:<gemini-id>` runs Gemini image models, such as `gemini-3.1-flash-image`, billed to `GOOGLE_GENERATIVE_AI_API_KEY`, read from the shell or the env file as OpenAI's key is.
|
|
402
|
+
- `--option key=value` sends any option the provider takes under its own `providerOptions` key; a dotted key nests, a JSON value parses, a later option wins, and `null` removes one. `openai` and `codex` send `quality=medium` unless an option says otherwise, and `--json` reports the `options` sent and the quality each image was produced at.
|
|
403
|
+
- `E_NO_IMAGE` when the model answers without an image, as Gemini does when it declines a prompt.
|
|
404
|
+
- The library refuses a `quality` a model does not honour before anything is sent (`reason.kind` `"unsupported-quality"`); the plan takes only `medium` or `auto`, and `codex.image()` reports the quality it produced in `providerMetadata`.
|
|
405
|
+
|
|
406
|
+
## v0.3.0 (2026-09-30)
|
|
407
|
+
|
|
408
|
+
### Upgrade from v0.2.0
|
|
409
|
+
|
|
410
|
+
- Library: `ImageResult.raw` is a list; read `raw[0]` where you read `raw`.
|
|
411
|
+
|
|
412
|
+
### Changed
|
|
413
|
+
|
|
414
|
+
- `ImageResult.raw` is a list: one AI SDK result per call the request was split into, a single entry when it was not split.
|
|
415
|
+
|
|
416
|
+
### Added
|
|
417
|
+
|
|
418
|
+
- `PartialResultError`: when some calls of a request for several images fail, the images the other calls returned, and were billed for, come back in `result`, with `positions` (where each sits in `n`), `asked`, and the first failure as `cause`. Before, one failed call threw every image away.
|
|
419
|
+
- `image-gen generate --n` writes the images that came back under their own numbers when another fails (`hero-1.png`, `hero-3.png`), lists them in `data.images`, and exits with that failure's code.
|
|
420
|
+
|
|
421
|
+
### Fixed
|
|
422
|
+
|
|
423
|
+
- An `--out` in a directory that does not exist is refused with `E_FILE_NOT_FOUND` before anything is billed, instead of failing after the image was paid for.
|
|
424
|
+
|
|
425
|
+
## v0.2.0 (2026-09-30)
|
|
426
|
+
|
|
427
|
+
### Upgrade from v0.1.6
|
|
428
|
+
|
|
429
|
+
- Library: catch `ProviderFailedError` in place of `RequestRejectedError` and the plan's `RouteFailedError`, and import `QuotaExhaustedError` from `image-gen` instead of `image-gen/codex`.
|
|
430
|
+
|
|
431
|
+
### Changed
|
|
432
|
+
|
|
433
|
+
- Library errors carry facts, not advice. Every refusal made before a call is a `RefusalError` (the parent of `WrongModelKindError` and `UnsupportedRequestError`) whose `reason` names the cause and its facts.
|
|
434
|
+
- A provider's failure is a `ProviderFailedError` with `provider`, `status`, `retryable` and `answer`; `RequestRejectedError` and the plan's `RouteFailedError` are gone.
|
|
435
|
+
- `QuotaExhaustedError` moves from `image-gen/codex` to `image-gen`, carries `provider`, `limit` and `resetsAt`, and is thrown for an OpenAI account out of credit too, so the CLI exits 75 for it as for the plan's quota.
|
|
436
|
+
- The AI SDK's retries follow the provider's own verdict: a 409 is retried, an out-of-credit answer is sent once, and the CLI exits 2 only when the provider says a retry may help.
|
|
437
|
+
- `image-gen` on the plan without a login fails before it reads any input.
|
|
438
|
+
|
|
439
|
+
### Added
|
|
440
|
+
|
|
441
|
+
- `generateCutout` returns `images`, every image the model returned, each keyed against its own backdrop.
|
|
442
|
+
- Results name the `modelId` that answered.
|
|
443
|
+
- `isSize`, `isAspectRatio` and `sizeOf` check and write the `WxH` and `W:H` forms; `describeWarning` words an SDK warning.
|
|
444
|
+
- `codex.loginStatus()` reports whether the plan's login is usable, and `CODEX_PROVIDER` names the plan model's provider.
|
|
445
|
+
|
|
446
|
+
### Fixed
|
|
447
|
+
|
|
448
|
+
- `generateCutout` with `n` above 1 keyed only the first image; the rest came back on the backdrop.
|
|
449
|
+
- An Edit on fal with one Reference Image sent only the Edit Target.
|
|
450
|
+
- Several plan images in one call no longer spend the single-use refresh token more than once, and a call aborted mid-refresh keeps the login.
|
|
451
|
+
|
|
452
|
+
## v0.1.6 (2026-09-29)
|
|
453
|
+
|
|
454
|
+
### Upgrade from v0.1.5
|
|
455
|
+
|
|
456
|
+
- Nothing to do.
|
|
457
|
+
|
|
458
|
+
### Added
|
|
459
|
+
|
|
460
|
+
- `CHANGELOG.md` now ships in the package: what changed in every version up to the installed one, newest first. `image-gen --help` and `image-gen doctor` print its installed path, above the `gh` commands that find newer releases.
|
|
461
|
+
|
|
462
|
+
## v0.1.5 (2026-09-29)
|
|
463
|
+
|
|
464
|
+
### Upgrade from v0.1.4
|
|
465
|
+
|
|
466
|
+
- Nothing to do.
|
|
467
|
+
|
|
468
|
+
### Added
|
|
469
|
+
|
|
470
|
+
- `image-gen doctor` checks, without any network call, what a run needs: the Node version against `engines`, the plan's login at `~/.codex-imagegen/auth.json`, and where `OPENAI_API_KEY` comes from (the shell or the env file, never its value). It exits 1 only when Node is too old (`E_NODE_VERSION`) or neither provider is ready (`E_LOGIN_REQUIRED` and `E_MISSING_API_KEY` together); `--json` carries the full report in `data` either way.
|
|
471
|
+
- `image-gen --help` ends with the installed version and the `gh release list` / `gh release view` commands for reading release notes.
|
|
472
|
+
|
|
473
|
+
## v0.1.4 (2026-09-29)
|
|
474
|
+
|
|
475
|
+
### Upgrade from v0.1.3
|
|
476
|
+
|
|
477
|
+
- Nothing to do.
|
|
478
|
+
|
|
479
|
+
### Fixed
|
|
480
|
+
|
|
481
|
+
- When the plan refuses a `--size` off its pixel budget, the `hint` now names the flag to use, such as `rerun with --aspect 16:9 in place of --size`, instead of the library's `aspectRatio`.
|
|
482
|
+
|
|
483
|
+
### Added
|
|
484
|
+
|
|
485
|
+
- The library throws `SizeOffBudgetError` (an `UnsupportedRequestError`, exported from `image-gen/codex`) for that refusal, carrying `requested` and the `aspectRatio` to pass instead.
|
|
486
|
+
|
|
487
|
+
## v0.1.3 (2026-09-29)
|
|
488
|
+
|
|
489
|
+
### Upgrade from v0.1.2
|
|
490
|
+
|
|
491
|
+
- Nothing to do.
|
|
492
|
+
|
|
493
|
+
### Changed
|
|
494
|
+
|
|
495
|
+
- On the ChatGPT plan, `--aspect` (library: `aspectRatio`) now sets the image's shape for `generate` and `edit`, instead of being refused. The plan paints about 1536×1024 pixels at that ratio; an image that misses the ratio is a warning.
|
|
496
|
+
- On the ChatGPT plan, `--size` (library: `size`) is now taken when its area is the plan's own, such as `1536x1024` or `1024x1536`, and the result is checked pixel for pixel. Any other size is still refused before anything is sent, with the aspect ratio to pass instead.
|
|
497
|
+
- An `edit` on the plan with `--aspect` or `--size` reframes its target to that shape.
|
|
498
|
+
|
|
499
|
+
## v0.1.2 (2026-09-29)
|
|
500
|
+
|
|
501
|
+
### Upgrade from v0.1.1
|
|
502
|
+
|
|
503
|
+
- Nothing to do.
|
|
504
|
+
|
|
505
|
+
### Added
|
|
506
|
+
|
|
507
|
+
- `--provider openai` reads `OPENAI_API_KEY` from `~/.config/image-gen/.env` (or `$XDG_CONFIG_HOME/image-gen/.env`) when the shell has none, so an agent's shell needs no `export`. The shell's key still wins, and plan runs never read the file.
|
|
508
|
+
|
|
509
|
+
### Changed
|
|
510
|
+
|
|
511
|
+
- The CLI gives up on a run after 9 minutes instead of 5, staying under the Bash tool's 10-minute cap; the skill now runs it with `timeout: 600000`.
|
|
512
|
+
- `E_MISSING_API_KEY` names the env file in its hint.
|
|
513
|
+
|
|
514
|
+
## v0.1.1 (2026-09-29)
|
|
515
|
+
|
|
516
|
+
### Upgrade from v0.1.0
|
|
517
|
+
|
|
518
|
+
- Nothing to do.
|
|
519
|
+
|
|
520
|
+
### Added
|
|
521
|
+
|
|
522
|
+
- The codex login refreshes an expired access token by itself and saves the new tokens, so a login that sat idle for weeks still works. Parallel processes that refresh at the same moment do not log each other out.
|
|
523
|
+
- `transparent: true` (and `--transparent` with `--provider openai`) works on `gpt-image-2.5-flare` and `gpt-image-2.5-sunburst`, including their dated snapshots, with the same size rules as `gpt-image-2`.
|
|
524
|
+
- `image-gen/codex` exports `DEFAULT_CODEX_TOKEN_URL`, and `createCodex` takes a `tokenURL` setting.
|
|
525
|
+
|
|
526
|
+
## v0.1.0 (2026-09-29)
|
|
527
|
+
|
|
528
|
+
First tagged build of `image-gen`: the library and the CLI.
|
|
529
|
+
|
|
530
|
+
- **Library:** an AI SDK v7 image model that bills the ChatGPT plan (`codex`), next to the OpenAI API-key provider.
|
|
531
|
+
- **CLI:** `image-gen generate` and `image-gen edit`, with reference images, `--transparent` (real alpha, reported per image), `--n` variants, and a JSON envelope under `--json`.
|