zclDragon/dsh-tool-generate-image

zclDragon/dsh-tool-generate-image

给纯文本模型的 `generate_image` 工具:模型用自然语言要一张图,经 Antigravity CLI 让 Gemini 画出,保存到可配置输出目录并返回文件路径,供模型在任务中使用。

工作流与自动化deepseek-harnessdshgemini

dsh-tool-generate-image

English | 中文

A hot-pluggable DeepSeek Harness (dsh) plugin that gives a text-only model a generate_image tool: describe an image in natural language and the model calls the tool, which asks Antigravity CLI (agy) / Google Gemini to draw it, saves it to an output directory, and returns the file path.

Generated images render inline in the chat AND are persisted to disk — the files survive restarts (unlike pure in-memory solutions where old links 404).

Example output: an orange cat on a blue sofa

What it is

This is a model-facing tool — the model calls it on demand while working ("draw a diagram for section 3, save it, I'll reference the path") rather than a chat-side auto-generate-and-display feature.

  • Generation is routed through the local agy generate_image tool + the gemini-3.1-flash-image image model (free Google account, no API key).
  • Generated images are harvested recursively from ~/.gemini/antigravity-cli (agy's write location is not stable across runs: scratch/, brain/<conversation-id>/, .tempmediaStorage/ all appear; SVG vectors are possible too) and copied into a configurable output directory.
  • Inline chat display: image bytes are held in process memory and served through a loopback-only route on the host's web server (default /generate-image) as a markdown reference — the model copies that line into its reply and the client renders it. In-memory cap 200 images / 128MB, cleared on restart (the disk copy is unaffected).
  • Zero runtime dependencies (Node built-ins only); no dsh source changes.

Install

dsh plugin --profile web add dsh-tool-generate-image

Restart the web app after install and the model can call the generate_image tool.

Usage

No commands to remember. Just ask the model to draw something, e.g.:

Draw a shiba inu wearing an engineer cap, coding at a desk, cartoon style, 16:9

The model calls generate_image, saves the image to the output directory (default ~/.dsh/generated-images), and returns the file paths plus an inline-display markdown reference — the model copies that line into its reply and the image renders right in the chat:

Example output: a shiba inu developer coding

Two layers: the inline chat display is in-memory (old references 404 after a restart — fine), while the image is durably saved to disk, so the file paths keep working after a restart.

Configuration

Read by the plugin's apply(ctx, config) (set it in the web profile's cordis.patch.yml / settings layer):

KeyDefaultMeaning
modelgemini-3.7-flash-mediumChat model driving the generation (the image is rendered by the backend gemini-3.1-flash-image model)
outputDir~/.dsh/generated-imagesWhere generated images are saved
timeoutMs300000Per-generation timeout
toolNamegenerate_imageRegistered tool name (change if it collides with a host tool)
agyagyThe agy executable
effort—Optional low / medium / high, forwarded as agy --effort
inlinePath/generate-imageInline-display route prefix (mounted on the host web server, loopback-only)
displayHostauto http://127.0.0.1:<webServer.port>Base URL used in the inline markdown; set only if it must differ

Tool arguments: prompt (required, natural-language description), fileName (optional, output base name; defaults to a timestamp).

Returned: ok, images (durable disk paths), markdown (in-memory inline reference; may be empty), message.

How it works

model: "draw a shiba inu"
  └─ generate_image tool execute()
       └─ spawn agy -p "<prompt>" --dangerously-skip-permissions \
                 --model <model> --print-timeout <Ns>
            └─ agy calls its generate_image tool → gemini-3.1-flash-image renders
       └─ recursive mtime snapshot of ~/.gemini/antigravity-cli finds the new images
       └─ copy to outputDir (durable)
       └─ bytes → in-memory store → markdown ![img](http://127.0.0.1:<port>/generate-image/raw/<id>)
  └─ return { ok, images: [paths], markdown, message }
       └─ model copies the markdown line into its reply → DSH client renders → image shows in chat

--dangerously-skip-permissions is required: in agy's print mode, tool calls are silently skipped without it, so the image would never be drawn.

⚠️ Security note: this flag means a full agent CLI (agy) runs with its permission prompts disabled. The prompt is passed as a spawn argument array (never through a shell), so there is no injection surface from the prompt text — but be aware that agy will execute tool calls directly under the signed-in account's permissions.

Notes

  • Prerequisite: install and sign in to the Antigravity CLI (curl -fsSL https://antigravity.google/cli/install.sh | bash, then agy and sign in once).
  • Quota variance: the free image service occasionally returns 503 MODEL_CAPACITY_EXHAUSTED / 429 RESOURCE_EXHAUSTED — transient, retry shortly.
  • Tested on the Web profile only.
  • Restart required: install/uninstall takes effect after restarting the web app.

Development / testing

node test/harvest.mjs                                  # harvest-logic unit test
node test/inline.mjs                                   # inline route unit test (incl. real-http round trip)
node test/standalone.mjs "a shiba inu" /tmp/out        # end-to-end (real generation)

License

Apache-2.0