{"_id":"@abhishek944/pi-image-gen","_rev":"4-fecbd440a0e858b42d107afa1d7a1b94","name":"@abhishek944/pi-image-gen","dist-tags":{"latest":"0.4.2"},"versions":{"0.1.1":{"name":"@abhishek944/pi-image-gen","version":"0.1.1","keywords":["pi-package","pi","extension","image-generation","codex","chatgpt","nano-banana","gpt-image","qwen-image","openrouter"],"license":"Apache-2.0","_id":"@abhishek944/pi-image-gen@0.1.1","maintainers":[{"name":"abhishek944","email":"abhishek944cse@gmail.com"}],"homepage":"https://github.com/abhishek944/pi-image-gen#readme","bugs":{"url":"https://github.com/abhishek944/pi-image-gen/issues"},"pi":{"image":"https://raw.githubusercontent.com/abhishek944/pi-image-gen/main/preview.png","skills":["./skills"],"extensions":["./src/index.ts"]},"dist":{"shasum":"0095fe1ae7d8f390824af5b45773593644693818","tarball":"https://registry.npmjs.org/@abhishek944/pi-image-gen/-/pi-image-gen-0.1.1.tgz","fileCount":24,"integrity":"sha512-BSdyH71kPR8WKxVoVe1CZJ6jQVZQ58uNtbteEbG+l/YMiRYRHbAHoaCkZPztqKxYP4IRJIcOr1Y1+M+xslkc6g==","signatures":[{"sig":"MEYCIQDeC8rFMn3I+B/mBJmDfsX9O/vkiQ3W8ut8BwBIYnJxSgIhAP/5WDIM6sdcOqi3OaiTbLjmG23bdpvKLn3z0e0aWZVV","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2102502},"main":"./src/index.ts","type":"module","exports":{".":{"default":"./src/index.ts"},"./package.json":{"default":"./package.json"}},"gitHead":"ad254fe9e0837f2f71263310167ecc472dad1ef5","scripts":{"test":"vitest run src","build":"tsc -b","typecheck":"tsc -b --pretty false"},"_npmUser":{"name":"abhishek944","email":"abhishek944cse@gmail.com"},"repository":{"url":"git+https://github.com/abhishek944/pi-image-gen.git","type":"git"},"_npmVersion":"10.9.4","description":"Image generation for Pi via ChatGPT Codex subscriptions, OpenAI, Gemini, Qwen, Seedream, OpenRouter, and custom providers.","directories":{},"sideEffects":false,"_nodeVersion":"22.21.1","dependencies":{"@amaster.ai/pi-shared":"^0.1.10"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.0.0","typebox":"*","typescript":"^5.9.3","@earendil-works/pi-ai":"0.80.10","@earendil-works/pi-coding-agent":"0.80.10"},"peerDependencies":{"typebox":"*","@earendil-works/pi-ai":">=0.80.10","@earendil-works/pi-coding-agent":">=0.79.1"},"peerDependenciesMeta":{"typebox":{"optional":true},"@earendil-works/pi-ai":{"optional":true},"@earendil-works/pi-coding-agent":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/pi-image-gen_0.1.1_1787830873231_0.888459074226613","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@abhishek944/pi-image-gen","version":"0.4.0","keywords":["pi-package","pi","extension","image-generation","sprite-generation","apng","codex","chatgpt","nano-banana","gpt-image","qwen-image","muse-image","meta-ai","openrouter"],"license":"Apache-2.0","_id":"@abhishek944/pi-image-gen@0.4.0","maintainers":[{"name":"abhishek944","email":"abhishek944cse@gmail.com"}],"homepage":"https://github.com/abhishek944/pi-image-gen#readme","bugs":{"url":"https://github.com/abhishek944/pi-image-gen/issues"},"pi":{"image":"https://raw.githubusercontent.com/abhishek944/pi-image-gen/main/preview.png","skills":["./skills"],"extensions":["./src/index.ts"]},"dist":{"shasum":"ae600426944f3849bd0439ef4eb9bab6b4486047","tarball":"https://registry.npmjs.org/@abhishek944/pi-image-gen/-/pi-image-gen-0.4.0.tgz","fileCount":36,"integrity":"sha512-qO7vByJ9GqpeHIR3rbtfwBVDdvO8JK0XOiBtnUc3V6mA0d7ku21mtj2/MVofqQ4RsrNEz4fMDdbZUY3umkQ99Q==","signatures":[{"sig":"MEUCIQCr7rrVW35x0OG6KpuW2sJH2JpaN9mK3jGPqm3PpQk8lwIgPzoeGbCI7Teiks819e9EnUZUMeo0kiogVORt7a69ens=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQDI/mozx7cFQVGLnTSoVGdASWz/1rwmbu1Qo9Y/2wCoxwIgLgoienPo+mX4EKRNZm43SaWf26yP/XsHO4U1qHnMJxg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2280199},"main":"./src/index.ts","type":"module","exports":{".":{"default":"./src/index.ts"},"./package.json":{"default":"./package.json"}},"gitHead":"d3daf1b1cb35b1f0716fa723d1e15130485d230d","scripts":{"build":"tsc -b","check":"npm run typecheck && npm pack --dry-run --json >/dev/null","typecheck":"tsc -b --pretty false"},"_npmUser":{"name":"abhishek944","email":"abhishek944cse@gmail.com"},"repository":{"url":"git+https://github.com/abhishek944/pi-image-gen.git","type":"git"},"_npmVersion":"10.9.4","description":"Image and optional sprite-sheet generation for Pi via ChatGPT Codex subscriptions, OpenAI, Gemini, Qwen, Seedream, Meta Muse Image, OpenRouter, and custom providers.","directories":{},"sideEffects":false,"_nodeVersion":"22.21.1","dependencies":{"upng-js":"^2.1.0","@amaster.ai/pi-shared":"^0.1.10"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typebox":"*","typescript":"^5.9.3","@earendil-works/pi-ai":"0.80.10","@earendil-works/pi-coding-agent":"0.80.10"},"peerDependencies":{"typebox":"*","@earendil-works/pi-ai":">=0.80.10","@earendil-works/pi-coding-agent":">=0.79.1"},"peerDependenciesMeta":{"typebox":{"optional":true},"@earendil-works/pi-ai":{"optional":true},"@earendil-works/pi-coding-agent":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/pi-image-gen_0.4.0_1789548488739_0.4352596263546349","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@abhishek944/pi-image-gen","version":"0.4.1","keywords":["pi-package","pi","extension","image-generation","sprite-generation","apng","codex","chatgpt","nano-banana","gpt-image","qwen-image","muse-image","meta-ai","openrouter"],"license":"Apache-2.0","_id":"@abhishek944/pi-image-gen@0.4.1","maintainers":[{"name":"abhishek944","email":"abhishek944cse@gmail.com"}],"homepage":"https://github.com/abhishek944/pi-image-gen#readme","bugs":{"url":"https://github.com/abhishek944/pi-image-gen/issues"},"pi":{"image":"https://raw.githubusercontent.com/abhishek944/pi-image-gen/main/preview.png","skills":["./skills"],"extensions":["./dist/extension.js"]},"dist":{"shasum":"fe2238add52ce6af56eea10d08af0fafafedff2a","tarball":"https://registry.npmjs.org/@abhishek944/pi-image-gen/-/pi-image-gen-0.4.1.tgz","fileCount":64,"integrity":"sha512-2lo+mouMzu/HrGqzoZXhoe1K0Pz/jJrcnqUw3pNNV4E2qkl6qGhIcy0VTMIaUMoNyIuC8hE9+btuLcH+GLhtRw==","signatures":[{"sig":"MEUCIQC1AT5iNp71p+ph5MVvvI4Qz/4n4NfS8Dtqt/xcfEj3TQIgOgofpms6JnUDV69Hljc8zRVPMMpN0qZf3Et0x4obEBE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIAkjBKpHZfsnrEab9a8sxRlD/j5bRtcKll3UrgpD3FUoAiEAkkftL0O8gq2c4omWOwBJsEpAfvUmg0l4glMHTl8vD3o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2308345},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./extension":{"types":"./dist/extension.d.ts","import":"./dist/extension.js","default":"./dist/extension.js"},"./package.json":"./package.json"},"gitHead":"03065bf8027b3cea0e8b129a3f98411d3c1f067b","scripts":{"build":"npm run clean && tsc -p tsconfig.build.json","check":"npm run typecheck && npm pack --dry-run --json >/dev/null","clean":"node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"","prepare":"npm run build","typecheck":"tsc -p tsconfig.json --noEmit --pretty false"},"_npmUser":{"name":"abhishek944","email":"abhishek944cse@gmail.com"},"repository":{"url":"git+https://github.com/abhishek944/pi-image-gen.git","type":"git"},"_npmVersion":"10.9.4","description":"Image and optional sprite-sheet generation for Pi via ChatGPT Codex subscriptions, OpenAI, Gemini, Qwen, Seedream, Meta Muse Image, OpenRouter, and custom providers.","directories":{},"sideEffects":false,"_nodeVersion":"22.21.1","dependencies":{"upng-js":"^2.1.0","@amaster.ai/pi-shared":"^0.1.10"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typebox":"*","typescript":"^5.9.3","@earendil-works/pi-ai":"0.80.10","@earendil-works/pi-coding-agent":"0.80.10"},"peerDependencies":{"typebox":"*","@earendil-works/pi-ai":">=0.80.10","@earendil-works/pi-coding-agent":">=0.79.1"},"peerDependenciesMeta":{"typebox":{"optional":true},"@earendil-works/pi-ai":{"optional":true},"@earendil-works/pi-coding-agent":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/pi-image-gen_0.4.1_1789550678001_0.1552010287499228","host":"s3://npm-registry-packages-npm-production"}},"0.4.2":{"pi":{"image":"https://raw.githubusercontent.com/abhishek944/pi-image-gen/main/preview.png","skills":["./skills"],"extensions":["./dist/extension.js"]},"_id":"@abhishek944/pi-image-gen@0.4.2","bugs":{"url":"https://github.com/abhishek944/pi-image-gen/issues"},"dist":{"shasum":"33bdca83cbaa56423cf18d0fee04517fec1d9a5e","tarball":"https://registry.npmjs.org/@abhishek944/pi-image-gen/-/pi-image-gen-0.4.2.tgz","fileCount":64,"integrity":"sha512-QCkf9ejtSfi9BahmsaLEPxWGZcIlVrcGhYscWQpjL/x2E0td8a6LaZWh3XzDdk3V1AQm52cJ/q73VLYL1dyVKg==","signatures":[{"sig":"MEUCIFgp2vKXjt9Ebo8eAvZTo+CpCr9WMxRZWrz9JhSkqDgNAiEA/BzG/nlcEuDFM8bqYd4FMENMZKe+ICqzr10GxLdQMrk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCS3lgnAo+NEC2mEwV7rPm6h0X8i9dLSTB9bDo18Q3OcwIgQEYrmhJHyBJWYTHwAzuOicDjw46lZk60kyxEt9lUKdc="}],"unpackedSize":2308793},"main":"./dist/index.js","name":"@abhishek944/pi-image-gen","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./extension":{"types":"./dist/extension.d.ts","import":"./dist/extension.js","default":"./dist/extension.js"},"./package.json":"./package.json"},"gitHead":"2d08b1d8b3a2c49f4a5b081224cc108e8f24cda2","license":"Apache-2.0","scripts":{"build":"npm run clean && tsc -p tsconfig.build.json","check":"npm run typecheck && npm pack --dry-run --json >/dev/null","clean":"node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"","prepare":"npm run build","typecheck":"tsc -p tsconfig.json --noEmit --pretty false"},"version":"0.4.2","_npmUser":{"name":"abhishek944","email":"abhishek944cse@gmail.com"},"homepage":"https://github.com/abhishek944/pi-image-gen#readme","keywords":["pi-package","pi","extension","image-generation","sprite-generation","apng","codex","chatgpt","nano-banana","gpt-image","qwen-image","muse-image","meta-ai","openrouter"],"repository":{"url":"git+https://github.com/abhishek944/pi-image-gen.git","type":"git"},"_npmVersion":"10.9.4","description":"Image and optional sprite-sheet generation for Pi via ChatGPT Codex subscriptions, OpenAI, Gemini, Qwen, Seedream, Meta Muse Image, OpenRouter, and custom providers.","directories":{},"maintainers":[{"name":"abhishek944","email":"abhishek944cse@gmail.com"}],"sideEffects":false,"_nodeVersion":"22.21.1","dependencies":{"upng-js":"^2.1.0","@amaster.ai/pi-shared":"^0.1.10"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typebox":"*","typescript":"^5.9.3","@earendil-works/pi-ai":"0.80.10","@earendil-works/pi-coding-agent":"0.80.10"},"peerDependencies":{"typebox":"*","@earendil-works/pi-ai":">=0.80.10","@earendil-works/pi-coding-agent":">=0.79.1"},"peerDependenciesMeta":{"typebox":{"optional":true},"@earendil-works/pi-ai":{"optional":true},"@earendil-works/pi-coding-agent":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pi-image-gen_0.4.2_1789557491465_0.42391770836619047"}}},"time":{"created":"2026-08-27T11:41:12.991Z","modified":"2026-09-16T11:18:11.749Z","0.1.1":"2026-08-27T11:41:13.698Z","0.4.0":"2026-09-16T08:48:08.903Z","0.4.1":"2026-09-16T09:24:38.112Z","0.4.2":"2026-09-16T11:18:11.586Z"},"bugs":{"url":"https://github.com/abhishek944/pi-image-gen/issues"},"license":"Apache-2.0","homepage":"https://github.com/abhishek944/pi-image-gen#readme","keywords":["pi-package","pi","extension","image-generation","sprite-generation","apng","codex","chatgpt","nano-banana","gpt-image","qwen-image","muse-image","meta-ai","openrouter"],"repository":{"url":"git+https://github.com/abhishek944/pi-image-gen.git","type":"git"},"description":"Image and optional sprite-sheet generation for Pi via ChatGPT Codex subscriptions, OpenAI, Gemini, Qwen, Seedream, Meta Muse Image, OpenRouter, and custom providers.","maintainers":[{"name":"abhishek944","email":"abhishek944cse@gmail.com"}],"readme":"# pi-image-gen\n\nPi extension that adds the general-purpose `image_generate` tool and an optional, disabled-by-default `sprite_generate` pipeline. Supported providers:\n\n| Provider                       | Model id (alias)                              | Authentication        |\n| ------------------------------ | --------------------------------------------- | --------------------- |\n| ChatGPT Codex                  | `gpt-image-2` on route `codex-subscription` (`gpt-image-2-codex`, `codex`, and `openai-codex` remain legacy aliases) | Pi `/login` → ChatGPT Plus/Pro (Codex) |\n| OpenAI                         | `gpt-image-2`, `gpt-image-2.5-flare`, `gpt-image-2.5-sunburst` | `OPENAI_API_KEY`      |\n| Google Gemini (\"Nano Banana\")  | `gemini-3-pro-image` (alias `nano-banana-pro`), `gemini-3.1-flash-image` (alias `nano-banana-2`), `gemini-3.1-flash-lite-image` (alias `nano-banana-2-lite`), `gemini-2.5-flash-image` (alias `nano-banana`) | `GEMINI_API_KEY` |\n| Alibaba DashScope (Qwen-Image) | `qwen-image-3.0-pro`, `qwen-image-3.0`, `qwen-image-2.0-pro`, `qwen-image-2.0` | `DASHSCOPE_API_KEY`   |\n| Volcengine Ark (ByteDance Seedream) | `doubao-seedream-5-0-pro-260628` (alias `seedream-5-pro`, retired id `doubao-seedream-5-0-pro-260128` still resolves), `doubao-seedream-5-0-260128` (aliases `seedream-5`, `seedream`; the same model also answers to `doubao-seedream-5-0-lite-260128` / `seedream-5-lite`), `doubao-seedream-4-5-251128` (alias `seedream-4-5`), `doubao-seedream-4-0-250828` (alias `seedream-4`) | `ARK_API_KEY`         |\n| Meta Model API (Muse Image) | `muse-image-1.0` (aliases `muse-image`, `meta-muse`) | Pi `/login meta` via `pi-meta-oauth`, or `META_API_KEY` |\n| OpenRouter                     | any (use `openrouter/<vendor>/<id>`)          | `OPENROUTER_API_KEY`  |\n| Custom providers               | whatever you declare in settings              | (your choice, via `$VAR`) |\n\nUpstream API docs (handy when debugging gateway behavior or adding new models):\n\n- OpenAI GPT Image — [generation guide](https://developers.openai.com/api/docs/guides/image-generation), [gpt-image-2](https://developers.openai.com/api/docs/models/gpt-image-2), [2.5 Flare](https://developers.openai.com/api/docs/models/gpt-image-2.5-flare), [2.5 Sunburst](https://developers.openai.com/api/docs/models/gpt-image-2.5-sunburst)\n- Google Gemini image generation — [ai.google.dev/gemini-api/docs/image-generation](https://ai.google.dev/gemini-api/docs/image-generation)\n- Alibaba Qwen-Image 3.0 (generation & editing) — [help.aliyun.com/zh/model-studio/qwen-image-generation-and-editing-api-reference](https://help.aliyun.com/zh/model-studio/qwen-image-generation-and-editing-api-reference)\n- Alibaba DashScope Qwen-Image 2.0 (text-to-image) — [help.aliyun.com/zh/model-studio/qwen-image-api](https://help.aliyun.com/zh/model-studio/qwen-image-api)\n- Alibaba DashScope Qwen-Image-Edit — [help.aliyun.com/zh/model-studio/qwen-image-edit-api](https://help.aliyun.com/zh/model-studio/qwen-image-edit-api)\n- Volcengine Ark Seedream — [volcengine.com/docs/82379/1824121](https://www.volcengine.com/docs/82379/1824121)\n- Meta Muse Image cookbook — [github.com/meta-models/meta-model-cookbook/tree/main/05_muse_image](https://github.com/meta-models/meta-model-cookbook/tree/main/05_muse_image)\n- OpenRouter image API — [openrouter.ai/docs/api/api-reference/images/create-images](https://openrouter.ai/docs/api/api-reference/images/create-images)\n\nBuilt-in API-key routes read the environment variables shown above (or `providers.<id>.apiKey` in `settings.json`); they do not read keys saved by Pi's general `/login` flow. Subscription routes are separate: Meta can use a Pi credential created by the [`pi-meta-oauth`](https://github.com/BlockedPath/pi-meta-oauth) package, while Codex uses Pi's ChatGPT Plus/Pro login. Meta API billing also accepts `META_API_KEY`; legacy `MODEL_API_KEY` remains accepted.\n\nFor subscription-backed generation, first run `/login` in Pi and select **ChatGPT Plus/Pro (Codex)**, then run `/image-gen use codex-subscription gpt-image-2`. No `OPENAI_API_KEY` is required. Codex requests use the ChatGPT-backed image endpoints and count against provider-managed subscription usage and limits. GPT Image 2.5 is intentionally **not** listed on this route: its API model ids have not been verified against the private Codex subscription endpoint.\n\nThe active provider route and model are **fixed in settings.json**. The `image_generate` tool intentionally does **not** take a `model` parameter — point your project at one route/model pair for consistent output. Use `/image-gen use <provider> <model>` to switch both safely; the command persists trusted project settings and refreshes the tool schema immediately.\n\n## Install\n\nFrom npm:\n\n```sh\npi install npm:@abhishek944/pi-image-gen\n```\n\nOr directly from GitHub:\n\n```sh\npi install git:github.com/abhishek944/pi-image-gen@v0.4.1\n```\n\nThe package's `pi.extensions` field auto-registers the compiled `dist/extension.js` entry with the host pi-coding-agent runtime; no extra wiring needed.\n\n### Use as a Node library\n\nThe npm package ships compiled ESM JavaScript and TypeScript declarations, so a normal Node 22 process can import the library without a TypeScript loader:\n\n```js\nimport {\n  generateImage,\n  runSpritePipeline,\n} from '@abhishek944/pi-image-gen';\n```\n\nThe package root is intentionally the lightweight library entry and does not load Pi-only runtime modules. Hosts that manually register the Pi extension instead of using the `pi.extensions` manifest can import its default export explicitly:\n\n```js\nimport piImageGenExtension from '@abhishek944/pi-image-gen/extension';\n```\n\n## Configure\n\nSettings are read by `pi-shared`'s `loadPiSettings`, which merges three files (low-to-high priority):\n\n1. `~/.pi/agent/settings.json` (global)\n2. `$PI_AGENT_HOME/settings.json` (agent dir, if `PI_AGENT_HOME` is set)\n3. `<cwd>/.pi/settings.json` (trusted project)\n\nProject settings are ignored when project trust is declined. `${ENV_VAR}` interpolation is supported in global and agent settings only, so keep environment-backed credentials out of project settings.\n\nAll settings live under the `pi-image-gen` key. New configurations should set an authentication-specific `defaultProvider` together with `defaultModel` (the command below writes this for you):\n\n```text\n/image-gen use gemini-api nano-banana\n```\n\n```json\n{\n  \"pi-image-gen\": {\n    \"defaultProvider\": \"gemini-api\",\n    \"defaultModel\": \"nano-banana\"\n  }\n}\n```\n\nModel-only configurations remain supported and keep the earlier automatic routing behavior.\n\n…and exports the matching env var:\n\n```sh\nexport GEMINI_API_KEY=sk-...\n```\n\nThat's it. From the agent: `image_generate({ prompt: \"a cyberpunk cat\" })`.\n\n### All settings fields\n\n```json\n{\n  \"pi-image-gen\": {\n    \"defaultProvider\": \"gemini-api\",\n    \"defaultModel\": \"nano-banana\",\n    \"outputDir\": \".pi/images\",\n    \"requestTimeoutMs\": 120000,\n    \"openRouterDiscovery\": true,\n    \"spriteGeneration\": {\n      \"enabled\": false,\n      \"defaultRows\": 2,\n      \"defaultColumns\": 3,\n      \"defaultFormat\": \"apng\",\n      \"frameDurationMs\": 120,\n      \"strictValidation\": true,\n      \"outputDir\": \".pi/images/sprites\",\n      \"maxFrames\": 16\n    },\n\n    \"providers\": {\n      \"openai\":     { \"baseUrl\": \"https://my-proxy.example.com/v1\", \"apiKey\": \"${MY_OPENAI_KEY}\" },\n      \"gemini\":     { \"headers\": { \"x-goog-trace\": \"pi-prod\" } },\n      \"dashscope\":  { \"baseUrl\": \"https://dashscope-intl.aliyuncs.com/api/v1\" },\n      \"ark\":        { \"apiKey\": \"$ARK_API_KEY\" },\n      \"meta\":       { \"apiKey\": \"$META_API_KEY\" },\n      \"openrouter\": { \"apiKey\": \"$OPENROUTER_API_KEY\" }\n    },\n\n    \"customProviders\": {\n      \"my-stable-diffusion\": {\n        \"api\": \"openai\",\n        \"baseUrl\": \"https://api.my-sd.example.com/v1\",\n        \"apiKey\": \"${MY_SD_KEY}\",\n        \"headers\": { \"x-tenant\": \"team-a\" },\n        \"models\": [\n          { \"id\": \"sd-3-large\", \"alias\": \"sd3\" },\n          \"sd-3-medium\"\n        ]\n      }\n    }\n  }\n}\n```\n\n| Field             | Purpose                                                                                  |\n| ----------------- | ---------------------------------------------------------------------------------------- |\n| `defaultProvider` | Authentication-specific route (`openai-api`, `codex-subscription`, `gemini-api`, `dashscope-api`, `openrouter-api`, `ark-api`, `meta-api`, `meta-subscription`, or a custom-provider id). Recommended for new configs. |\n| `defaultModel`    | Model id or alias the tool will use. **Required.**                                       |\n| `outputDir`       | Where to write generated images. Relative paths resolve against the session cwd. Default `.pi/images`. |\n| `requestTimeoutMs` | End-to-end deadline including inputs and downloads. `1000`–`900000`; default `120000`. |\n| `openRouterDiscovery` | Best-effort discovery of the selected OpenRouter model's supported parameters. Default `true`; failure falls back safely. |\n| `providers`       | Per-built-in-provider override. Set `apiKey`, `baseUrl`, or `headers` to point at a proxy or non-standard env var. |\n| `customProviders` | User-defined providers — see below. Built-in route ids such as `openai-api` and `meta-subscription` are reserved and cannot be shadowed. |\n| `spriteGeneration` | Optional sprite-sheet processing settings. Disabled by default; see below. Invalid values fail closed to safe defaults. |\n\nIn global and agent settings, `apiKey`, `baseUrl`, and `headers` values support `$VAR` and `${VAR}` environment interpolation. Fallbacks require the braced form (for example, `${FOO:-default}`); `$FOO:-default` is not supported. Project settings keep all of these placeholders literal.\n\n## DeepSeek Harness\n\n| Host | `pi-image-gen` status |\n| --- | --- |\n| [pi2dsh](https://github.com/weijiafu14/pi2dsh) | `@amaster.ai/pi-image-gen@0.1.8` was exercised against a controlled OpenAI-compatible endpoint ([scope and evidence](https://github.com/TGYD-helige/pi/issues/159)). |\n| [dsh-pi-host](https://github.com/TGYD-helige/dsh-pi) | Loads `@amaster.ai/pi-image-gen` when selected in its `extensions` list. |\n\nFollow the selected host's documentation for installation and current compatibility details.\n\n## Built-in setup walkthrough\n\n### 1. OpenAI (`gpt-image-2` and GPT Image 2.5)\n\n```sh\nexport OPENAI_API_KEY=sk-...\n```\n\n```json\n{ \"pi-image-gen\": { \"defaultProvider\": \"openai-api\", \"defaultModel\": \"gpt-image-2.5-flare\" } }\n```\n\nChoose `gpt-image-2.5-flare` for fast everyday generation or `gpt-image-2.5-sunburst` when editing precision matters most. Both expose `low`, `medium`, `high`, `xhigh`, `max`, and `auto` quality. Their `-2026-09-08` snapshots are also built in. The older `gpt-image-2` remains available.\n\n### 2. Google Gemini \"Nano Banana\"\n\n```sh\nexport GEMINI_API_KEY=...\n```\n\n```json\n{ \"pi-image-gen\": { \"defaultModel\": \"nano-banana\" } }\n```\n\n### 3. Alibaba DashScope (Qwen-Image)\n\n```sh\nexport DASHSCOPE_API_KEY=...\n```\n\n```json\n{ \"pi-image-gen\": { \"defaultModel\": \"qwen-image-2.0\" } }\n```\n\nFor the international DashScope endpoint, override the base URL:\n\n```json\n{\n  \"pi-image-gen\": {\n    \"defaultModel\": \"qwen-image-2.0\",\n    \"providers\": {\n      \"dashscope\": { \"baseUrl\": \"https://dashscope-intl.aliyuncs.com/api/v1\" }\n    }\n  }\n}\n```\n\n### 4. Volcengine Ark (ByteDance Seedream)\n\n```sh\nexport ARK_API_KEY=...\n```\n\n```json\n{ \"pi-image-gen\": { \"defaultModel\": \"seedream\" } }\n```\n\n> Supported `size` values are model-dependent, and the tool schema tells the agent the exact form for the active model: a tier token (`1K`/`1.5K`/`2K`/`3K`/`4K` — the list differs per model) or an explicit `\"<w>x<h>\"` pixel string, never mixed. Seedream 5.0 / 4.5 enforce a 2K pixel floor (`1024x1024` fails with `InvalidParameter`); 5.0 pro accepts `1K`/`1.5K`/`2K` down to 921,600 px; 4.0 accepts 1K. Full sizing matrix in the [official docs](https://www.volcengine.com/docs/82379/1824121). Seedream has **no `n` parameter**, so `n` is hidden. Supported non-Pro models instead expose `seriesMaxImages` for the API's related `sequential_image_generation` mode; Seedream 5.0 Pro does not support that mode. The extension sends `watermark: false` by default to avoid Seedream's upstream \"AI 生成\" badge, while an explicit `watermark: true` opts in.\n\nThe default base URL is `https://ark.cn-beijing.volces.com/api/v3`. To use a different region (e.g. `ap-southeast`), override it:\n\n```json\n{\n  \"pi-image-gen\": {\n    \"defaultModel\": \"seedream\",\n    \"providers\": {\n      \"ark\": { \"baseUrl\": \"https://ark.ap-southeast.bytepluses.com/api/v3\" }\n    }\n  }\n}\n```\n\n### 5. Meta Muse Image\n\nMeta works with either subscription login or an API key.\n\n**Subscription login:** install [`pi-meta-oauth`](https://github.com/BlockedPath/pi-meta-oauth), then authenticate in Pi. Check that package's current Pi peer-version range first; OAuth support is optional, and `META_API_KEY` remains available on Pi versions outside it.\n\n```sh\npi install npm:pi-meta-oauth\n```\n\n```text\n/login meta\n```\n\n**API key:** create a key in the [Meta Model API dashboard](https://dev.meta.ai/), then configure:\n\n```sh\nexport META_API_KEY=...\n```\n\nLegacy `MODEL_API_KEY` is also accepted. Select the funding route explicitly:\n\n```text\n/image-gen use meta-subscription muse-image\n/image-gen use meta-api muse-image\n```\n\n```json\n{ \"pi-image-gen\": { \"defaultProvider\": \"meta-subscription\", \"defaultModel\": \"muse-image\" } }\n```\n\nWith a legacy model-only configuration, an active Meta login still takes precedence and an API key remains the fallback. With an explicit route, `meta-subscription` uses only Pi OAuth and `meta-api` uses only the configured API key.\n\nOAuth credentials are resolved and refreshed through Pi at request time. They are used only for the built-in Meta provider at `api.meta.ai`, never for custom providers or overridden endpoints. Muse Image uses Meta's conversational Responses API. This extension sends `store: false`, so calls do not retain server-side conversation state. Generate from text normally; for editing or composition, pass one or more images through the tool's `image` array. Official cookbook size examples include `1024x1024`, `1536x1024`, and `1024x1536`; these are guidance rather than an exhaustive enum, and Meta validates the requested value. Muse Image produces one output per request, so `n` and `quality` are hidden.\n\n### 6. OpenRouter (one key, many models)\n\n```sh\nexport OPENROUTER_API_KEY=...\n```\n\n```json\n{ \"pi-image-gen\": { \"defaultModel\": \"openrouter/bytedance-seed/seedream-4.5\" } }\n```\n\nThe string after `openrouter/` is the OpenRouter model slug; pass any image model OpenRouter supports (`google/gemini-3.1-flash-image`, `openai/gpt-image-2`, `bytedance-seed/seedream-4.5`, …).\n\nOpenRouter's image API is **not** OpenAI-compatible despite the family name — it lives at `POST /api/v1/images` (no `/generations` suffix) and uses JSON `input_references` for image-to-image. The extension targets the right endpoint automatically; no wire-shape config needed.\n\n## Custom providers\n\nUse `customProviders` for anything not built in: a self-hosted Stable Diffusion, an internal corp gateway, a third-party image API. The shape mirrors [pi.dev's custom-provider docs](https://pi.dev/docs/latest/custom-provider).\n\nEach custom provider declares:\n\n| Field      | Required | Notes                                                                                |\n| ---------- | -------- | ------------------------------------------------------------------------------------ |\n| `api` | yes      | One of `openai`, `gemini`, `dashscope`, `openrouter`, `ark`, `meta`. Picks the image-API wire shape. |\n| `baseUrl`  | yes      | API endpoint URL. `$VAR` syntax supported.                                           |\n| `apiKey`   | usually  | Credential string. `$VAR` syntax supported. Required for `bearer` and `header` authentication. |\n| `auth`     | no       | `{ \"type\": \"bearer\" }`, `{ \"type\": \"header\", \"header\": \"x-api-key\" }`, or explicit `{ \"type\": \"none\" }`. Omission preserves the adapter's legacy default (Gemini uses `x-goog-api-key`; the others use bearer). |\n| `name`     | no       | Display name shown in `/image-gen list`.                                             |\n| `headers`  | no       | Extra headers merged into every request.                                             |\n| `models`   | no       | Optional model id/alias list. Omit to make this a **catch-all** — the provider will accept any unknown model id (passed through as the remote id). Provide a list only when you want aliases or want to route specific ids elsewhere. Each entry is a string or `{ id, alias?, name?, capabilities? }`. |\n\nRequests using a named credential header reject redirects so that a gateway cannot forward that credential to another origin.\n\nA custom model whose `id` names a built-in model **inherits that model's capability contract** (size form, `n` ceiling, reference-image rules) so the tool schema stays accurate when you route a known model through your own gateway. Declare `capabilities` on the entry to override individual fields; anything undeclared falls back to the built-in entry, then to a conservative generic contract. Catch-all routes and unknown ids get no contract — the schema stays fully generic, as before.\n\n```json\n{\n  \"pi-image-gen\": {\n    \"defaultModel\": \"qwen-image-3.0\",\n    \"customProviders\": {\n      \"corp-gateway\": {\n        \"api\": \"dashscope\",\n        \"baseUrl\": \"https://gateway.corp.example/api/v1\",\n        \"apiKey\": \"$GW_KEY\",\n        \"auth\": { \"type\": \"bearer\" },\n        \"models\": [\n          \"qwen-image-3.0\",\n          { \"id\": \"my-finetune\", \"capabilities\": { \"nMax\": 4, \"maxReferenceImages\": 2 } }\n        ]\n      }\n    }\n  }\n}\n```\n\n> Note: pi.dev custom providers also have an `api` field, but its values (`openai-completions`, `anthropic-messages`, …) are LLM streaming formats that don't apply to image generation. The values here (`openai`, `gemini`, `dashscope`, `openrouter`, `ark`, `meta`) are image-API wire shapes — same field name, different namespace.\n\n### Example: self-hosted Stable Diffusion (OpenAI-compatible)\n\n```sh\nexport SD_KEY=local-secret\n```\n\n```json\n{\n  \"pi-image-gen\": {\n    \"defaultModel\": \"sd3\",\n    \"customProviders\": {\n      \"my-sd\": {\n        \"api\": \"openai\",\n        \"baseUrl\": \"http://localhost:8000/v1\",\n        \"apiKey\": \"$SD_KEY\",\n        \"models\": [{ \"id\": \"sd-3-large\", \"alias\": \"sd3\" }]\n      }\n    }\n  }\n}\n```\n\nThe agent calls `image_generate({prompt: ...})`; the extension sees `defaultModel: \"sd3\"`, finds it under `my-sd`, and POSTs to `http://localhost:8000/v1/images/generations` with `Bearer $SD_KEY`.\n\n### Example: Volcengine Doubao image API (OpenAI-compatible)\n\n```json\n{\n  \"pi-image-gen\": {\n    \"defaultModel\": \"doubao-seed-image\",\n    \"customProviders\": {\n      \"doubao\": {\n        \"api\": \"openai\",\n        \"baseUrl\": \"https://ark.cn-beijing.volces.com/api/v3\",\n        \"apiKey\": \"${ARK_API_KEY}\",\n        \"models\": [{ \"id\": \"doubao-seedream-4-0-250828\", \"alias\": \"doubao-seed-image\" }]\n      }\n    }\n  }\n}\n```\n\n### Example: a Gemini-shape proxy\n\nIf your provider speaks the Google Generative Language wire format:\n\n```json\n{\n  \"pi-image-gen\": {\n    \"defaultModel\": \"internal-banana\",\n    \"customProviders\": {\n      \"internal\": {\n        \"api\": \"gemini\",\n        \"baseUrl\": \"https://gemini-proxy.corp.example/v1beta\",\n        \"apiKey\": \"$INTERNAL_GEMINI_KEY\",\n        \"models\": [{ \"id\": \"gemini-2.5-flash-image\", \"alias\": \"internal-banana\" }]\n      }\n    }\n  }\n}\n```\n\n### Direct addressing without an alias\n\nIf a custom provider has no `models` list, you can still address it with `<providerName>/<remoteId>`:\n\n```json\n{\n  \"pi-image-gen\": {\n    \"defaultModel\": \"my-sd/sd-3-large\",\n    \"customProviders\": {\n      \"my-sd\": { \"api\": \"openai\", \"baseUrl\": \"http://localhost:8000/v1\", \"apiKey\": \"$SD_KEY\" }\n    }\n  }\n}\n```\n\n## Optional sprite generation\n\n`image_generate` remains the general image tool. Enable the separate `sprite_generate` tool only when you need one coherent action sheet plus deterministic local processing:\n\n```json\n{\n  \"pi-image-gen\": {\n    \"defaultProvider\": \"openai-api\",\n    \"defaultModel\": \"gpt-image-2\",\n    \"spriteGeneration\": {\n      \"enabled\": true,\n      \"defaultRows\": 2,\n      \"defaultColumns\": 3,\n      \"defaultFormat\": \"apng\",\n      \"frameDurationMs\": 120,\n      \"strictValidation\": true,\n      \"outputDir\": \".pi/images/sprites\",\n      \"maxFrames\": 16\n    }\n  }\n}\n```\n\nRun `/image-gen reload` after editing settings. The command reports whether sprite generation is enabled. Toggling it changes only `sprite_generate`; all other active tools are preserved.\n\nA call makes exactly one provider request through the same `generateImage()` service used by `image_generate`. The model creates one full sheet, then local code splits cells in row-major order, segments alpha foreground, applies one shared scale, aligns frames, checks geometry and silhouette motion, and writes transparent PNG assets. No automatic paid retry occurs.\n\n```ts\nsprite_generate({\n  prompt: \"the same red-jacket courier running in place, crisp pixel art\",\n  image: [\"references/courier.png\"],\n  assetType: \"player\",\n  action: \"run\",\n  view: \"side\",\n  rows: 2,\n  columns: 3,\n  frameCount: 6,\n  align: \"feet\",\n  format: \"apng\",\n  frameDurationMs: 120,\n  filename: \"courier-run\"\n})\n```\n\nSix frames default to a 2×3 grid. `frameCount` must equal `rows × columns`, with a hard maximum of 16. Supported output formats are `apng` and `frames`; GIF is intentionally not advertised because it reduces alpha and color fidelity. Model-aware `size`, `aspectRatio`, `imageSize`, and `quality` controls appear only when the configured route supports them.\n\nEach call reserves a new run directory and never overwrites an earlier run:\n\n```text\n.pi/images/sprites/courier-run/\n├── prompt-used.txt\n├── raw-sheet.png\n├── sheet-transparent.png\n├── frames/frame-01.png ... frame-06.png\n├── animation.apng\n└── pipeline-meta.json\n```\n\nStrict validation prevents APNG approval when required checks fail, while preserving the raw sheet and any safe processed artifacts. Advisory mode can accept bounded quality warnings, but structural failures such as empty cells or missing alpha still reject the sequence. Local checks cannot prove identity, costume, anatomy, or acting quality; inspect those visually before shipping.\n\n## Tool: `image_generate`\n\n```ts\nimage_generate({\n  prompt: string,                  // required — what to draw or how to edit\n  image?: string[],                // optional — array of file paths or http(s) URLs\n  n?: number,                      // per-model ceiling (qwen 1–6, gpt-image-2 1–10); hidden for Seedream\n  size?: string,                   // per-model form (see below); hidden for Gemini models\n  aspectRatio?: string,            // Gemini models only — enum from the model's vocabulary\n  imageSize?: string,              // Gemini models only — tier enum (\"1K\"/\"2K\"/\"4K\"), when the model has tiers\n  quality?: 'low'|'medium'|'high'|'xhigh'|'max'|'auto', // exact enum is model-specific\n  outputFormat?: 'png'|'jpeg'|'webp', // verified OpenAI/OpenRouter routes\n  background?: 'auto'|'transparent'|'opaque',\n  outputCompression?: number,        // 0–100; JPEG/WebP only\n  mask?: string,                     // precise OpenAI edit mask; requires image\n  negativePrompt?: string,           // Qwen 3 / discovered OpenRouter support\n  seed?: number,\n  promptEnhance?: boolean,\n  enableThinking?: boolean,\n  watermark?: boolean,\n  seriesMaxImages?: number,          // Seedream related series; distinct from n\n  filename?: string,               // filename prefix (no extension)\n  outputDir?: string,              // override settings.outputDir for this call\n})\n```\n\nReturns the absolute file path(s) of saved images. Files land in `outputDir` (default `<cwd>/.pi/images`), filename pattern `<filename or model-UTC-stamp>.<ext>`. Result details also include elapsed time and, when supplied by the provider, a sanitized request id, numeric usage/cost metadata, and decoded output dimensions.\n\n**The schema is model-aware.** Every built-in model carries a capability contract sourced from official API docs or clearly labeled extension safety limits, and the tool is registered with parameters shaped by that contract (on session start, and again after `/image-gen reload`) — so the agent sees exactly the knobs the active model honors, with documented values in enums and descriptions where available. Provider-specific numeric contracts (size ranges, `n` ceilings, and reference-image counts) are generally **advice, not a gate** because a self-hosted deployment or gateway may legitimately differ. Independently, the extension enforces universal safety ceilings of 16 references, 20MB per input, and 128MB combined, plus rejects parameter combinations an adapter would otherwise silently drop. Providers may enforce stricter limits.\n\n- `size` follows the model's documented form:\n  - **Qwen** (`qwen-image-*`): `\"<width>*<height>\"` (asterisk, e.g. `\"2048*2048\"`), total pixels 512²–2048²; 3.0 models additionally cap aspect ratio at 1:8–8:1. The x-form is normalized automatically as a safety net.\n  - **Seedream** (`doubao-seedream-*`): a tier token from the model's list (`1K`/`1.5K`/`2K`/`3K`/`4K`) **or** an explicit `\"<w>x<h>\"` within the model's pixel window (2K floor on 5.0/4.5).\n  - **gpt-image-2**: `\"auto\"` or `\"<w>x<h>\"` — arbitrary sizes allowed (both edges divisible by 16, ratio ≤ 3:1, 655,360–8,294,400 px, longest edge ≤ 3840), beyond the standard `1024x1024`/`1536x1024`/`1024x1536`.\n  - **GPT Image 2.5 Flare/Sunburst**: the same arbitrary `\"<w>x<h>\"` range as GPT Image 2 (multiples of 16, ratio 1:3–3:1, 655,360–8,294,400 px, longest edge ≤ 3840), plus `\"auto\"`. The standard recommended sizes remain `1024x1024`, `1536x1024`, and `1024x1536`; resolutions above `2560x1440` are experimental.\n  - **Meta Muse Image**: passed through the Responses API's `image_generation` tool; official cookbook examples include `1024x1024`, `1536x1024`, and `1024x1536`, but the field remains free-form for provider validation.\n  - Omit `size` to use the model's own default (qwen-image-3.0 auto-picks from the prompt).\n- `aspectRatio` / `imageSize` replace `size` for **Gemini** models (they have no pixel-size knob): `aspectRatio` is an enum from the model's vocabulary (10–14 values), `imageSize` an enum of the model's tiers (`1K`/`2K`/`4K`; hidden when the model is fixed at one tier, as `gemini-3.1-flash-lite-image` and `gemini-2.5-flash-image` are).\n- `n` is model-specific and carries the model's documented ceiling in its description (Qwen 6, GPT Image 2/2.5 10). The extension enforces a universal maximum of 10 outputs per call. It is **hidden for Seedream and Meta Muse Image** — those APIs expose no count knob here, and direct callers are rejected if they request more than one output.\n- `image` spells out the active model's documented reference-image contract in its description (formats, max count, per-image byte ceiling, dimension advice): qwen documents ≤ 3 images (JPG/JPEG/PNG/BMP/TIFF/WEBP/GIF, ≤ 10MB each), Seedream ≤ 10–14 (incl. HEIC/HEIF, ≤ 30MB), gpt-image-2 ≤ 16 (png/webp/jpg, ≤ 50MB), Gemini ≤ 3–14 (≤ 20MB), Meta Muse Image labels the extension's own recognized formats (PNG/JPEG/GIF/WEBP/BMP/TIFF/HEIC/HEIF) because the cookbook does not publish an exhaustive input contract. Provider-documented contracts remain advisory; the extension-wide 16-reference, 20MB-per-input, and 128MB-combined safety ceilings are enforced locally, and providers may enforce stricter rules.\n- `quality` appears **only** for a **built-in gpt-image** route — the built-in OpenAI provider on `gpt-image-*`, or an OpenRouter route whose model id is gpt-image (e.g. `openrouter/openai/gpt-image-2`). GPT Image 2 and the verified Codex route use `low`/`medium`/`high`/`auto`; GPT Image 2.5 Flare and Sunburst additionally expose `xhigh` and `max`. It is **omitted from the schema entirely** for:\n  - Gemini, DashScope/Qwen, Ark/Seedream, and Meta Muse Image — their image APIs have no `quality` field (Seedream varies quality by `size` resolution tier instead);\n  - **non-gpt-image routes** on the OpenAI/OpenRouter wire — e.g. built-in `openai/dall-e-3` (which uses `standard`/`hd`) or an OpenRouter route to a non-OpenAI model like Seedream — because the enum above is gpt-image's vocabulary, not the wire format's; and\n  - custom providers by default, including OpenAI-*compatible* ones — wire format alone does **not** imply a quality vocabulary. A custom model may opt in by explicitly declaring `capabilities.qualityValues`; this is honored only for the custom OpenAI/OpenRouter adapters that forward `quality`.\n\n  If `defaultModel` is unset or misconfigured, `quality` stays present (the tool remains fully featured and `execute` surfaces a friendly config error). Use `\"low\"` for fast drafts and a higher level for final assets or dense text.\n\nAdvanced controls are also capability-gated:\n\n- OpenAI API GPT Image routes expose `outputFormat`, `background`, `outputCompression`, and `mask`. The private Codex subscription route exposes `background` and forwards `auto`, `transparent`, or `opaque`; it returns PNG images and does not expose the other public-API controls. A mask requires an `image` edit target; transparent output requires PNG or WebP.\n- Qwen Image 3 routes expose `negativePrompt`, `seed`, `promptEnhance`, `enableThinking`, and `watermark`.\n- Seedream exposes `watermark` and `seriesMaxImages`. A series is a related set, not independent `n` variants.\n- OpenRouter capability discovery caches the selected model's endpoint metadata for ten minutes and may add format, background, compression, seed, or negative-prompt controls. Discovery has a five-second deadline and safely falls back to the static/generic schema.\n- Custom models may opt into the same fields through their `capabilities` declaration. Unsupported accepted parameters are rejected before a paid request rather than silently dropped.\n\nBecause the schema is fixed at registration, switching models via `/image-gen reload` re-registers the tool so the parameter set tracks the new provider.\n\n**Non-destructive writes:** a saved file never overwrites an existing one. Two calls with `filename: \"hero\"` produce `hero.png` then `hero-v2.png`, so an earlier result is preserved rather than clobbered. Path reservation is atomic (`O_EXCL`): even many concurrent calls with the same `filename` each claim a distinct path — no two clobber each other.\n\n### Tool result format\n\nThe tool's text result is shaped as ready-to-paste markdown the model can copy verbatim into its reply, so the UI renders the image inline:\n\n```\nGenerated 1 image(s) via amaster (custom) (qwen-image-2.0). Show each one to the user as inline markdown — copy the lines below verbatim into your reply:\n\n![white](/Users/.../white.png)\n```\n\nThe `alt` text is the filename without its extension — i.e. whatever you passed as `filename`, or `<model>-<UTC-stamp>` if you didn't. When OpenAI returns a `revised_prompt`, it appears as a quote line under the image:\n\n```\n![beaver](/Users/.../beaver.png)\n> revised prompt: a cute beaver, photorealistic, water droplets\n```\n\nThe markdown URL is platform-shaped so any CommonMark host (desktop, TUI, CLI) keeps it intact: on macOS/Linux a bare absolute path — byte-identical unless it contains markdown-unsafe characters (whitespace, `#`, `?`, `%`, `()`, `<>`), which are percent-escaped; on Windows a percent-encoded `file:///…` URL (`![result](file:///C:/Users/.../result.png)`) — sanitizers such as react-markdown's `defaultUrlTransform` read `C:` as an unknown URI scheme and strip the img `src`. Independently of the markdown, `details.images[].path` always carries the raw filesystem path.\n\n### Image-to-image / edit\n\n`image` is always an array — pass `[\"path\"]` for a single image, `[\"a\", \"b\"]` for multi-image conditioning. Each entry must be:\n\n- **Local file path** — a regular png/jpeg/gif/webp/bmp/tiff/heic/heif file inside the session cwd (the active model's accepted formats may be narrower — the `image` parameter description lists them). Paths may be absolute or relative, but symlinks and paths outside cwd are rejected.\n- **Public http(s) URL** — private, loopback, link-local, metadata, credentialed, and unsafe redirect destinations are rejected.\n\nBase64 strings and `data:` URIs are intentionally rejected — tool arguments don't survive megabyte-sized strings cleanly. If you have raw image bytes, write them to a file under the session cwd and pass that path.\n\n**Iterating on a previous result:** pass the previous output path back. The next image is conditioned on the last one:\n\n```\nimage_generate({ prompt: \"a beaver chewing wood\", filename: \"beaver\" })\n  → /Users/.../.pi/images/beaver.png\n\nimage_generate({ prompt: \"now in watercolor style\", image: [\"/Users/.../.pi/images/beaver.png\"] })\n  → /Users/.../.pi/images/gpt-image-2-20260605-...png  (edited)\n```\n\nProvider behavior:\n\n| Provider | Image input route |\n|---|---|\n| OpenAI (`gpt-image-2`) | `POST /v1/images/edits` (multipart). Supports multi-image. |\n| Gemini (`gemini-3-pro-image`, `gemini-3.1-flash-image`, `gemini-3.1-flash-lite-image`, `gemini-2.5-flash-image`) | `inline_data` parts prepended to the user message. Supports multi-image. |\n| DashScope (`qwen-image-3.0-pro`, `qwen-image-3.0`, `qwen-image-2.0-pro`, `qwen-image-2.0`) | `image` parts in `messages[].content`. Up to 3 images. |\n| Meta Muse Image (`muse-image-1.0`) | `POST /v1/responses` with `input_image` content parts. Supports multi-image composition. |\n| OpenRouter | `POST /api/v1/images` with `input_references` JSON. Supports multi-image. |\n\nThere is intentionally no `model` parameter on the tool — the active route/model pair is fixed by `pi-image-gen.defaultProvider` and `pi-image-gen.defaultModel` in settings.\n\n## Slash commands\n\n- `/image-gen doctor` — validate the provider/model pair, authentication presence, timeout, custom-provider shapes, and output-directory writability without making a paid generation request or printing credentials.\n- `/image-gen setup` — interactively choose a configured route and compatible model. Non-interactive hosts receive equivalent `/image-gen use` guidance.\n- Command arguments provide completions for subcommands, routes, and known model ids.\n- `/image-gen list` — show output directory, sprite status/defaults, default provider, default model, routes currently configured through API keys/Pi logins/custom settings, and every available provider/model route. OpenAI API vs Codex subscription and Meta API vs Meta subscription are separate entries. Listing login status never refreshes or retrieves an OAuth token.\n- `/image-gen set provider <provider>` — persist only the default provider route. If the existing model is incompatible, the command warns so you can set the model next.\n- `/image-gen set model <model>` — persist the model after validating it against the selected provider.\n- `/image-gen use <provider> <model>` — validate and persist both atomically. This is the recommended switch command.\n- `/image-gen reload` — re-read settings from disk and re-register the tool so its schema (e.g. whether `quality` is exposed) tracks the newly selected model.\n- `/image-gen generate <prompt>` — generate an image directly from the command line using the active model. Reports the saved file path(s) as a plain-text notification (the command uses `ctx.ui.notify`, which shows a status line, not rendered Markdown — so unlike the tool result it does not emit an inline `![](…)` image). Use the `image_generate` tool from the agent when you want the image rendered inline.\n\nGeneration emits safe phase updates while loading inputs, waiting for the provider, and saving output. Escape cancellation and `requestTimeoutMs` propagate through provider requests and downloads. Paid generation POSTs are never retried automatically.\n\nThe three settings commands update `<cwd>/.pi/settings.json` only for a trusted project. They merge the `pi-image-gen` object without replacing unrelated settings and immediately re-register the tool; manual edits still require `/image-gen reload`.\n\n## Bundled skill\n\nThis package ships an `image-gen` skill (`skills/image-gen/SKILL.md`) that Pi loads on demand. It carries the prompting playbook the one-line tool guidance can't hold: when to use raster generation vs repo-native SVG/CSS, generate-vs-edit intent, `n`-is-variants-not-assets, multi-image role labeling, edit invariants, text-in-image handling, and the labeled prompt schema. The tool works without it; the skill makes the model use the tool well.\n\nThe optional `sprite-gen` skill (`skills/sprite-gen/SKILL.md`) explains one-action sheet prompting, reference roles, grid/alpha constraints, validation limits, disabled-state guidance, and explicit retry behavior. The skill may remain discoverable while the tool is disabled; runtime activation is controlled by `spriteGeneration.enabled`.\n\n## Acknowledgements\n\nThis standalone repository is derived from [`packages/pi-image-gen`](https://github.com/TGYD-helige/pi/tree/master/packages/pi-image-gen) in the TGYD-helige Pi extensions monorepo. Codex authentication and transport behavior was informed by [`pi-codex-image-gen`](https://github.com/crazygit/pi-codex-image-gen).\n\nThe optional sprite-generation workflow was inspired by and adapted from concepts in [`agent-sprite-forge`](https://github.com/0x0funky/agent-sprite-forge). Thank you to 0x0funky and its contributors for sharing their work.\n\nSee [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).\n","readmeFilename":"README.md"}