本文へ移動
cccskills
無料GitHub で公開

product-image

Requires OFOX_API_KEY — create one at https://app.ofox.ai. Produce a set of product images to choose between — several styles, backgrounds or treatments of one product, for a listing, a store page or an ad — and quote the whole set's cost before any of it is spent. From a real product photo it edits that photo once per style, so the item stays identical across the set; for a fictional or prototype product with no photo it generates from text instead, and says what that costs in accuracy. Delegates to ofox-image-core. Use when a user asks for several product images at once, e.g. "give me 4 main images in different styles for this product", "a few background options for this photo", "some listing images for my shop", or "show this product on white, on wood, and in a lifestyle scene". Do not use for a single change to a single image (see image-edit), for video from a product photo (see seedance-product-video), or for one plain text-to-image render (that is ofox-image-core's `generate`).

インストール方法を見る

含まれるファイル(2)

  • SKILL.md47.8 KB
  • CHANGELOG.md11.4 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

product-image: a set of product images to choose between

"Four main images in different styles" is a set, and a set is a different job from one picture. It has a shape nobody asked about out loud — how many, varying what, and does the product stay the same across them — and it has a price that is the total, not the per-image figure.

This skill is a thin, scenario-specific layer over ofox-image-core. It owns the set's composition, the per-style prompt craft, the consistency requirement and the set-total pricing; ofox-image-core owns talking to the Ofox image API correctly and safely — the OFOX_API_KEY handling, model resolution, requests, decoding, cost arithmetic and absolute paths. Read that skill's safety contract before using this one — it is not restated here.

This skill has produced one set of its own. 2026-09-17: three background variants of a single real product photograph, on the edit route this file recommends, one axis varied, one image plainly on white, about 1.6 cents each. It settled the claim the whole skill turns on — the product is the same product in all three — and it exposed one this file had not said: the product's size within the frame is not held. Both are in "Why the 'keep' sentence is worth its length". Three images is not four and one product is not a catalogue; the generate route still has no set of its own.

The other measured figures below come from ofox-image-core's work building the edit subcommand, and from two probe calls made directly against ofox-image.sh while this file was being written. Every one of them names the run it came from.

Where the core skill lives

Resolve once, before the first call:

for d in ../ofox-image-core \
         ../ofoxai-skills-ofox-image-core \
         ~/.agents/skills/ofox-image-core \
         ~/.agents/skills/ofoxai-skills-ofox-image-core \
         ~/.claude/skills/ofox-image-core; do
  [ -f "$d/references/ofox-image.sh" ] && echo "$d" && break
done

Examples below are written as ../ofox-image-core/... (the skills.sh / ClawHub / npx ofox-skills layout, where a skill's directory is named after the skill). If the probe found a different directory — LobeHub unpacks each skill as ofoxai-skills-<name>, so the sibling there is ofoxai-skills-ofox-image-core — substitute it, in the ofox-image.sh commands and in the references/*.md links alike.

That ../ is relative to this skill's own directory, which is also where the probe has to run. From anywhere else nothing resolves — use the absolute path the probe printed (candidates 3–5 are absolute already), or, in a clone of this repo, skills/ofox-image-core/references/ofox-image.sh from the repo root.

Nothing found → the core skill isn't installed; see "If the script isn't found".

The spend gate's spec lives in a different core skill, because it is shared by every Ofox skill in this repo, video or not. It is a document rather than an execution dependency — nothing here stops working without it, and the rule it carries still applies:

for d in ../ofox-video-core \
         ../ofoxai-skills-ofox-video-core \
         ~/.agents/skills/ofox-video-core \
         ~/.agents/skills/ofoxai-skills-ofox-video-core \
         ~/.claude/skills/ofox-video-core; do
  [ -f "$d/references/approval-gate.md" ] && echo "$d" && break
done

Before generating: the availability check

Run this once per session (not on every request):

bash ../ofox-image-core/references/ofox-image.sh check

It checks curl, jq and OFOX_API_KEY and makes no network call. If it fails, follow ofox-image-core's guidance (install the missing tool, or get a key at https://app.ofox.ai) — don't dead-end the conversation, and don't re-run the check on every subsequent request once it has passed.

A failing check never blocks the quote: models and both --dry-run forms work with OFOX_API_KEY unset, so a whole set can be priced for someone who has not signed up.

First: is there a photo of the real product?

Settle this before anything else. It decides the command, the price and how much of the result you can promise.

The product is…RouteWhat you get
a real item the user has a photo of — a real SKU, a logo, a printed label, particular geometryedit, once per style. Every image in the set starts from the same source filethe product stays itself across the set, because every image is an edit of the same picture rather than an independent invention of it
a fictional, prototype or purely generic product — "a matte white ceramic mug", a brand that does not exist yetgenerate, once per stylea set of plausible product images that are not the same object — see the measurement below. Say this before spending. This route is also the one where --quality becomes required and decides the set's whole cost profile (⚠️ section under the approval gate)

The file being there is not evidence it shows the product

Open the source image and confirm it is the product before treating it as one. A path in a request is a claim, and the claim is wrong often enough to be worth ten seconds: wrong file picked from a folder, a screenshot instead of a photo, the packaging instead of the item, last week's SKU.

This is cheap to check and expensive to skip, because the edit route does not fail on a wrong source — it succeeds. Four calls against the wrong picture return four well-executed, correctly-billed images of the wrong thing, and the mistake is only visible when someone opens them.

If the file is not the product the user described, stop and say what you actually see, naming both — "this looks like a screenshot of a billing settings page, not a thermos". Do not:

  • proceed anyway on the theory that the user knows their own files;
  • silently switch to the generate route, which produces a different wrong answer — four images of a plausible invented item — at the same cost;
  • generate a reference image and edit that. See the prohibition below; the wrong-file case is exactly where it is most tempting.

The three legitimate exits are: ask for the right file, proceed on the generate route only if the user confirms there is no photo and accept what that costs in accuracy, or stop.

The measurement that decides it. One probe call, 2026-09-15, openai/gpt-image-2, generate --n 2 on a single mug prompt at --quality low --size 1024x1024: two files came back, and they are visibly different mugs — different proportions, different camera scale, different light. For "give me two options", that variety is the product. For a listing, it is the defect, because a shopper compares every image in a set against the same physical item and against each other.

That is one probe on one model, not a law. But it lines up exactly with seedance-product-video's own rule, arrived at independently: a real SKU needs the photo, a category prototype can go text-only with the expectation set. This skill follows the same split, for the same reason.

⚠️ Do not offer to generate a reference image first and then edit that. seedance-product-video records the reasoning and it holds here identically: a generated image can be wrong about the label, the cap and the proportions in exactly the ways the set can — and then locks those errors into every image in it. If the user has no photo of a real SKU, say the results may not match the item and offer to proceed as a category prototype instead.

How a set is actually produced — asked, not assumed

This is the question this skill exists to have already answered, so read it before reaching for --n.

The request has one prompt field, so a style set is N calls

Four styles means four different instructions. The request body carries exactly one prompt, and there is no per-image prompt field on either endpoint. So a four-style set is four calls whatever else is true — that is not a guess about how the model behaves, it is the field list.

--n is a count on a single prompt. It asks for N renders of the same instruction. Useful, and a different thing.

What --n does buy, measured

Two probe calls, 2026-09-15, run directly against ofox-image.sh while this file was being written — not through this skill's flow, and not deliverables. Both on openai/gpt-image-2; together about 3 cents.

ProbeOutput filesOutput tokensThe n=1 anchor for the same conditionsBilled vs estimated
generate --n 2, --quality low --size 1024x10242, suffixed _0 and _1391196 (2x = 392)1.19 cents against a 1.18-cent estimate
edit --n 2, a 256x256 input2, suffixed _0 and _1458229 (2x = 458, exactly)1.81 cents against a 1.84-cent estimate

Three things follow, and the third is the one that settles the route:

  1. --n really does return that many distinct results. Two files each time, visibly different from each other, and the edit pair both honoured the instruction's "leave every other pixel exactly as it is" half.
  2. Output tokens scale linearly with n — 391 against 2x196, and 458 against exactly 2x229. Before these probes the script's own dry run said this "has never been measured on either endpoint"; now it has been, at n=2, on one model, on both endpoints.
  3. --n is not a discount. On the edit probe the input image tokens came back as 512 = exactly 2 x 256, so the uploaded picture is billed once per output image rather than once per call. On the generate probe the input text tokens scaled the same way. One call at --n 4 and four separate calls cost the same, to within the measurement.

Therefore: the default is N separate calls

Since the price is the same either way, choose on what the set needs — and a product set needs three things --n cannot give:

  • different instructions per image, which is what "different styles" means;
  • the ability to stop after the first one is wrong, instead of paying for four renders of a misunderstanding;
  • independent re-rolls — replace image 3 without touching 1, 2 and 4.

So: one call per image, one prompt each. Reach for --n only when the user wants several rolls of one style to pick from — and price that as part of the same set total.

Which models take n at all: ask the catalog

There is no support table in this file. The Ofox catalog carries it, free and keyless, in each model's image_attributes.supported_params:

curl -s https://api.ofox.ai/v1/models | jq -r '
  .data[]
  | select((.supported_endpoints // []) | index("/v1/images/generations"))
  | [.id,
     (if .image_attributes.supported_params == null then "unknown — no image_attributes"
      elif (.image_attributes.supported_params | index("n")) then "n advertised"
      else "n absent from the list" end)]
  | @tsv'

Three states, not two — which is why the query is written this long. A model that carries the list and leaves n out of it is saying something; a model with no image_attributes block at all is saying nothing.

Read 2026-09-15: n was advertised by exactly four of the sixteen image models, all OpenAI ones; the four Gemini models carry the list and omit n; the other eight (Microsoft, Qwen, Volcengine) have no image_attributes block at all. Quote what the command prints today, not that sentence.

Two limits worth carrying:

  • google/gemini-3.1-flash-image refuses n outright, even at n: 1. ofox-image-core rejects it client-side on both subcommands — exit 1, free, nothing submitted.
  • supported_params is a generations-shaped list and never mentions the edits endpoint. The edit probe above shows n working on the edits endpoint for a model that advertises it, which is one model in one direction. Do not read the field as an edits contract.

None of this changes the default, which is why it is safe to leave as a lookup: N separate calls need no n support from anybody.

Composing the set

A set is only worth paying for if the images differ in a way a buyer would notice. Four renders of "a nice photo of this product" is one image billed four times.

Name each image's job before writing any prompt. The usual axes, and a request usually picks one of them rather than mixing:

AxisA four-image set looks like
Background / setting (the commonest reading of "different styles")pure white catalog; a warm wooden surface; a soft grey studio sweep; a lifestyle scene where the product is in use
Lighting treatmenteven softbox; hard directional with a defined shadow; backlit rim; moody low-key
Framingfull product on white; three-quarter hero; a macro of one feature; a scale shot next to something familiar
Seasonal / campaignplain evergreen; summer; winter; gift

Two rules for the list you settle on:

  • Keep one image plainly on white, whatever else the set does, unless the user has ruled it out. Every marketplace wants one, and it is the image the others are judged against.
  • Vary one axis, not three. A set that changes background and framing and light gives the user four unrelated pictures instead of four options.

Writing each image's prompt

On the edit route — the shape that keeps the product itself

Same two-sentence discipline image-edit is built on, and here the second sentence is what makes the set a set:

<The new setting, lighting and surface, described concretely.> Keep the product itself exactly as it is — shape, proportions, colour, material, and every printed word and logo on it — and do not move or re-frame it.

Four prompts for a four-image set differ only in the first sentence. The second is byte-identical across all four, and it is the thing standing between a set of options and four pictures of four slightly different products.

Worked, the background axis:

1. Place the product on a pure white seamless background with even studio softbox light, a soft contact shadow directly beneath it and nothing else in frame. Keep the product itself exactly as it is — shape, proportions, colour, material, and every printed word and logo on it — and do not move or re-frame it.
2. Place the product on a warm oak surface against a softly blurred neutral wall, with soft late-afternoon light from the left. Keep the product itself exactly as it is — ...
3. Place the product on a light grey studio sweep with a gentle gradient behind it and a soft true reflection beneath it. Keep the product itself exactly as it is — ...
4. Place the product on a kitchen counter beside a linen cloth and a small plant, in soft daylight from a window out of frame. Keep the product itself exactly as it is — ...

Why the "keep" sentence is worth its length, and where the evidence stops: ofox-image-core measured this endpoint genuinely editing rather than redrawing — a UI screenshot asked for one button colour change returned every string verbatim, with mean absolute difference 5.27/255 overall against 84.27 inside the button, a region that is 1.1% of the frame carrying 58% of all substantially-changed pixels. Replicated three times, on one model, on inputs with hard edges and flat colour. That is what makes "leave the product alone" a reasonable thing to ask for. It is not a promise that your product survives, which is why the set gets checked below rather than trusted.

And the set claim itself has now been run — this skill's core sentence held. 2026-09-17, one real 1792x1008 product photograph, three edits differing only in the first sentence (pure white; warm oak; grey sweep), the "keep" sentence byte-identical across all three, about 1.6 cents each. Put side by side, the wordmark's glyph shapes, letter spacing, position and relative size; the copper knurl and the two polished rings; the brushed vertical grain; and the waist taper are the same in all three. That is the thing this file promises — every image is an edit of one photograph, not three independent re-inventions of it — and it is a measurement now rather than an inference from a UI screenshot.

⚠️ What the same three images exposed, which this file had not said: the framing is not locked. The "keep" sentence protects the product's identity; it does not hold the product's size within the frame. The oak image came back visibly larger in frame than the white one, and the grey-sweep image slightly narrower. Nothing was cropped or distorted — the subject simply sits at a different scale. --target-aspect governs the frame's shape and has nothing to say about this.

So for a marketplace set that has to line up — a grid where the product must appear the same size in every tile — treat scale consistency as unsolved here, tell the user before they pay, and plan to normalise it in an image editor afterwards. Adding "do not move or re-frame it" to the prompt (this file already does) did not deliver it. One run of three images, one model, one product.

⚠️ Printed text is the part to check hardest. What is measured is that existing strings survive an edit aimed elsewhere. Nothing measures the endpoint preserving a label through a change to the whole scene around it, and nothing at all measures it writing new text correctly. If the label has to read right, that is a check on every image in the set, not a hope.

On the generate route — the shape for a product that does not exist yet

No input image, so every image is an independent roll and the product will drift between them. Write the product block identically in all four prompts and vary only the scene, which narrows the drift without removing it:

<Product, in physical words: material, finish, colour, geometry, any structural points.> <The setting, lighting and surface.> No printed text, no logo, no engraving and no marking anywhere on it.

Three notes, all borrowed rather than measured here:

  • Physical words, not adjectives — matte off-white glazed ceramic, straight-sided cylinder, curved handle, not "an elegant mug". This is seedance-product-video's PRODUCT-block rule and it is the same problem.
  • A description is honoured; a number attached to it is not. That skill measured three interleaved knuckles rendering as four. If a countable feature matters, check it rather than writing it harder.
  • The no-text declaration plus a per-carrier negative list is what has actually produced text-free product renders in this repo — on video, three times. It is an inference here, not a measurement, and it only covers the surfaces you name.

The shape of the set, and where it comes from

On the edit route the output's shape follows the input's, measured on one model over four runs: 854x480, 320x180 and 1792x1008 all returned 1672x941, and 256x256 returned 1254x1254 — a near-constant ~1.57 MP spent on whatever shape it was handed, and spent in both directions, since the 1792x1008 input is larger than the output it came back as. Whether --size is honoured on this endpoint is untested.

So for a marketplace set that has to be square:

  1. Crop the source photo to the target ratio before the first call. Every image in the set then arrives at that ratio, and the crop also cuts the bill — the uploaded picture is billed.
  2. Pass --target-aspect 1:1 (or whatever the platform wants) as the guarantee: the script measures the written file and centre-crops it, or fails loudly rather than handing back an almost-right one. It needs ffmpeg/ffprobe, and fails as exit 2 before anything is spent if they are missing.

Do both when the shape is load-bearing — the crop gets you there, the flag proves it, on every image in the set rather than on the one you happened to look at.

On the generate route the shape is --size, whose enum has no 16:9 or 9:16 entry at all; --target-aspect covers that gap the same way. Detail: ofox-image-core's "The size enum cannot express 16:9 or 9:16".

Before writing anything: the brief

The shared rules for asking — the three tiers, one round of at most four questions, the "Let the AI decide" discipline, the order with the approval gate, the fallback for a runtime without AskUserQuestion — are in ../ofox-video-core/references/creative-brief.md. Every rule there about how to ask carries over; its question content does not. This is this scenario's own set.

Tierproduct-image axes
must-askis there a photo of the real product (it decides the route and what can be promised), and what varies across the set
ask-if-openhow many images; the delivery shape
never-askthe model, --quality, --output-format, --background, whether to use --n. The model comes from the chain and is named in the cost table; the rest are settled below. One exception: on the generate route --quality defaults to low, but if the user says the renders are the finished artwork rather than proposals, it stops being a default and becomes two priced rows in the cost table — see the ⚠️ section under the approval gate

The question set

#TierheaderQuestionOptions (1 = recommended)Ask when
1must-askPhotoDo you have a photo of the actual product? With one, every image in the set is an edit of that same photo, so the item stays itself.Yes — a local path (recommended): the edit route. / No — describe it in text: fine for a generic or fictional product; for a real item the set will not be four pictures of the same object, and I will say so. No AI option — a photo that does not exist cannot be invented.No image attached and the user did not say there is none.
2must-askVaryWhat should differ between the images?Background and setting (recommended): white, wood, studio sweep, lifestyle — the usual reading of "different styles". / Lighting treatment: same scene, four looks. / Framing: full shot, hero, macro detail, scale. / Seasonal or campaign. No AI option — this is what the set is.The request says "different styles" or "a few options" without saying of what.
3ask-if-openCountHow many?4 (recommended): enough to choose from without paying for a crowd. / 2 — a straight comparison. / 6 — a spread. / Let the AI decide — 4. Every image is one more line on the bill, linearly.The user said "a few" or "some" rather than a number.
4ask-if-openShapeWhat shape do these need to be?Square 1:1 (recommended): what most marketplace grids want. / 4:5 for a social feed. / 16:9 for a site banner. / Same as the source photo. / Let the AI decide — 1:1.The destination is not stated and the source is not already the right shape.

Skip rows specific to this scenario

On top of the generic rows in creative-brief.md:

Input says…AxisValue
a photo is attached, or a path is givenPhotoedit route; do not ask whether there is one — but open it and confirm it shows the product before using it. The path is the answer to "is there a file"; only looking answers "is it the product"
"white, wood, and in a kitchen", any list of settingsVarybackground — and the list is the set
"different lighting", "brighter / moodier versions"Varylighting
"close-up too", "one showing the detail"Varyframing
a number ("4 images", "a couple")Countthat number
Amazon, Etsy, Shopify, eBay, "listing", "main image"Shape1:1
Instagram, "feed post"Shape4:5
"banner", "hero", "site header"Shape16:9

The recap for this scenario

Brief:
- Photo: /Users/me/shop/kettle.jpg (given), 2000x2000 — I will crop to 1:1 first (already square, no crop)
- Vary: background — white / oak / grey sweep / kitchen scene (you listed three, I added the grey sweep)
- Count: 4 (you said)
- Shape: 1:1 (inferred from "Etsy")
- Route: edit, four calls, one per background — the product stays the same photo in all four

Then all four prompts in full, then the cost table with its total, all in one message. The user approves the set, the prompts and the price together.

Before you spend: the approval gate, and the total

Never send the first image until the user has seen a cost table for the whole set and said yes. The rule, the required columns and where the numbers come from are written down once for every Ofox skill in this repo: ../ofox-video-core/references/approval-gate.md (directory-name caveat and probe: "Where the core skill lives" above). Its Batches get an itemised table, not one total section is the one that governs this skill.

Four things this scenario has to get right:

  1. Quote the set's total, itemised one row per image. Never the per-image figure on its own. If one image in four is usable, that image cost the whole total; the per-image number understates it by 4x. This is the same rule shorts-reels follows for video batches, for the same reason.
  2. The whole set commits at the yes. These calls take seconds each, so all four bills land together. The cost table is the only place the set can be stopped.
  3. A re-roll of one image is a second approval, with its own row. Don't fold "and we'll redo any that miss" into the first table as though it were priced.
  4. On the generate route, show both --quality rows — see the ⚠️ subsection that closes this one. It is the one flag in this skill that moves the set's total by more than an order of magnitude, and it must not be picked silently when the renders are the deliverable.

Get every number from --dry-run, which validates, resolves the model, builds the request and prints an estimate — then returns, before anything is sent. No key needed:

bash ../ofox-image-core/references/ofox-image.sh edit --dry-run \
  --image /absolute/path/to/kettle-square.jpg \
  --prompt "<style 1's prompt>" \
  --target-aspect 1:1 \
  --out-dir /absolute/path/to/out --out-name kettle-white

Dry-run each image, not one of them. On the edit route all four share the same input file and near-identical prompt length, so the four estimates are usually the same figure — but "usually" is not a basis for a price a user is approving, and a per-image estimate is the only thing that lets the table be itemised honestly. It costs nothing.

Then sum them into the table's total, relaying each Estimated cost: line as it prints — never a figure of your own, never a cents-per-image number copied out of this file. The line carries the conditions it was matched on and how rough it is; a number lifted out of it loses both. Alongside the total, the table needs the resolved MODEL, the input's measured size, and all four prompts in full.

⚠️ On the edit route the source photo's size is the set's cost lever, and a set multiplies it by N. The uploaded picture is billed per image, and on a large source it is most of the bill: a 1792x1008 input bills 1508 image tokens, 1508 * 0.000008 = 0.012064, which is 74% of that edit's 0.016249. Four images at that size is ~6.5 cents against ~2.4 for four 854x480 ones. Cropping the source to the target ratio before the first call — which this file already tells you to do — cuts every image in the set, not just the first. ofox-image-core's references/pricing.md → "Measured edits" has the four measured points.

Afterwards the actual bill is the sum of the EDIT_COST (or IMAGE_COST) lines the real runs print, computed from each response's own token counts. Report the total as money, say how many images it covers, and note the script's own caveat that EDIT_COST is the dearer of two readings of the same token counts, because no invoice has settled which is right.

⚠️ On the generate route, --quality decides the set's whole cost profile

--quality is required on generate (and only there — omit it on edit). The skill cannot leave that to chance, because the two values a caller would reach for first are not in the same price bracket. Both rows below are what --dry-run printed on 2026-09-15 for a four-image set at --size 1024x1024, on the chain's resolved head openai/gpt-image-2 — free, no key, no request sent:

--qualityPer image, as the gate quotes itA set of fourWhat the number is
lowROUGH, ~0.59 cents~2.4 centsAn anchor measured at this request's own pair (2026-09-02, low + 1024x1024), which is the only condition under which an image estimate means much
medium or highROUGH UPPER BOUND, ~15.19 cents~61 centsNo anchor exists at either pair. The gate falls back to the dearest of the model's two measured points (2026-09-04, high + 1792x1024) and quotes it as a ceiling, deliberately erring high

⚠️ That table is for choosing, not for quoting. It is a snapshot of one model on one day, and the chain's head moves. The figures a user approves come from --dry-run on the actual request, every time — the rule above ("never a cents-per-image number copied out of this file") applies to these two numbers as hard as to any other.

Read the 26x honestly — it is a spread in the quote, not a measured spread in the bill. The model's two measured points really are about 26x apart in output tokens (196 against 5063), and the script says so itself. But nothing has been measured at medium/1024x1024 or high/1024x1024, so the 15.19-cent figure is a bound, and the actual bill could land well under it. Say it that way to the user: "at high I can only quote you a ceiling, and the ceiling for four is 61 cents; at low I have a real measurement and it is about 2.4 cents." Never present the ceiling as a price, and never quietly substitute a cheaper guess for it.

The default is low, and it is a default rather than a question because of what the generate route is for: this route exists only for a product that has no photo (see the first section), so the set is a batch of style proposals to choose between, not final listing artwork. low is the value with a real measurement behind it and the one the cheap-first discipline in this repo points at.

Override it when the user says the renders are the deliverable — final artwork rather than proposals. Then the choice belongs to them, so put both rows in the cost table rather than picking: the ceiling for the set at the dearer value, the measured total at low, and the sentence that the dearer one is a bound. That is the only place in this skill where the cost table carries two options instead of one total.

Two things that follow and are easy to get wrong:

  • --quality is a per-model enum, validated by the dry run against the model the request will actually use (since ofox-image-core 1.7.0). Do not hardcode a value on the strength of one model accepting it — five copy-pasteable commands in this repo said --quality standard and broke the day the model chain's head changed. low is a recommendation to pass, not a value to assume is accepted; let the dry run confirm it.
  • None of this applies to the edit route, which is the common one. There --quality is not required, it is not validated by the endpoint, and a typo in it renders and bills anyway — so omit it entirely.

Running the set

One call per image. Same input file, same --out-dir, a distinct --out-name each time so the results are legible later:

IMG=/absolute/path/to/kettle-square.jpg
OUT=/absolute/path/to/out

bash ../ofox-image-core/references/ofox-image.sh edit \
  --image "$IMG" --prompt "<style 1's prompt>" \
  --target-aspect 1:1 --out-dir "$OUT" --out-name kettle-white

bash ../ofox-image-core/references/ofox-image.sh edit \
  --image "$IMG" --prompt "<style 2's prompt>" \
  --target-aspect 1:1 --out-dir "$OUT" --out-name kettle-oak
# ...one per style...

Stop after the first one and look at it. It is one image's worth of money to find out that the instruction was misread, and three images' worth to find out afterwards. If image 1 is right, run the rest; if it is not, fix the prompt shape and re-price.

For the generate route the shape is the same, one call per style:

bash ../ofox-image-core/references/ofox-image.sh generate \
  --prompt "<style 1's prompt>" --quality low --target-aspect 1:1 \
  --out-dir "$OUT" --out-name mug-white

--quality low is not decoration in that command — it is required on generate, and it is the difference between a four-image set quoted at ~2.4 cents and one quoted at a ~61-cent ceiling. The reasoning, both priced rows, and when to hand the choice back to the user are in "⚠️ On the generate route, --quality decides the set's whole cost profile" above. Pass it explicitly and let the dry run validate it against the resolved model, which since ofox-image-core 1.7.0 it does for free.

Reporting the set

  • Every IMAGE_PATH on its own line, absolute, grouped as a list with the style each one is. The paths are the deliverable; the user should not have to re-derive your working directory.
  • The total cost, and how many images it covers.
  • Any MODEL_FALLBACK_* or MODEL_SOURCE request line, relayed rather than swallowed — the first means something dearer may have run, the second means the API did not confirm the model the price is computed at.
  • SIZE_ACTUAL, not SIZE. SIZE is printed straight from the response and this API has a recorded history of that field disagreeing with the file on disk.

Check the set before you hand it over

STATUS completed means each request was accepted and a file was written. For a set it does not answer the question that matters, which is not "did four images arrive" but "are these four images of the same product".

Check, in this order:

  1. Product identity across the set. Put all four next to the source and compare the same features in each: shape and proportions, colour and material, every printed word and logo, any distinctive part (a handle, a hinge, a cap). Pick the features before looking, so you are comparing rather than being reassured.
  2. The white image, hardest. It is the one a marketplace will actually reject, and a plain background makes a wrong shadow or a cut-out edge obvious.
  3. The labels at magnification. Text is the first thing to go soft, and a listing image with a plausible-but-wrong wordmark is worse than one with none.
  4. The shape, on every file. SIZE_ACTUAL on each, not the one you happened to open. With --target-aspect the script has already guaranteed it; without it, this is the check that was skipped.
  5. How big the product sits in each frame — and expect this one to fail. Measured (see "Why the 'keep' sentence is worth its length"): identity held across three edits and scale did not. Nothing in the prompt or the flags controls it, so this is a check that ends in an editor rather than a re-roll. Say it at the gate, not at hand-over.

A wrong image is a spent call. There is no job id and no poll on this API, so the repair is a new call with a better prompt, priced again as its own row. That is the argument for stopping after image 1, not an argument for accepting image 3.

Recommended defaults

ParameterDefaultWhy
Routeedit when there is a real photo; generate only for a fictional or generic productThe only thing that keeps one product identical across a set is starting every image from the same picture
Callsone per imageThe price is the same as --n (measured), and separate calls buy per-image prompts, an early stop, and independent re-rolls
--nomit itReach for it only when the user wants several rolls of one style. Not a discount; refused outright by at least one model
--modelomit itofox-image-core resolves its cheapest-first chain against the endpoint being used and reports any fallback. Pass one only when the user named one
--qualityomit on edit; on generate it is required — lowNot required on edits, and since that endpoint does not validate the value, an omitted flag cannot be a typo. On generations it is the single flag that moves the set's total by more than 10x: a four-image set quotes ~2.4 cents at low (a measured anchor) against a ~61-cent ceiling at medium/high (no anchor at that pair). See "⚠️ On the generate route, --quality decides the set's whole cost profile" — and let the dry run validate the value against the resolved model either way
--target-aspectthe platform's ratio, on every imageA set whose images are not all the same shape is not a set. The script guarantees it or fails; --size is untested on edits
--sizeomit on edit; on generate, the enum value that survives the crop with the most pixelsThe edit output's shape follows the input's. On generations --target-aspect picks the size for you
--out-diralways, absoluteWithout it the files land in whatever directory you happened to be in — which, given the examples run from this skill's own directory, means inside an installed skill
--out-nameone per image, naming the stylekettle-white, kettle-oak. A set of timestamp-named files is unusable a day later

Pricing a set with no API key

All of these work with OFOX_API_KEY unset:

bash ../ofox-image-core/references/ofox-image.sh models --endpoint edits
bash ../ofox-image-core/references/ofox-image.sh edit --dry-run --image FILE --prompt "..." --out-dir DIR
bash ../ofox-image-core/references/ofox-image.sh generate --dry-run --prompt "..." --quality low

So a user who has not signed up can see the whole set priced before deciding whether to register. Quote it first; don't open with a signup link.

If the script isn't found

bash: ../ofox-image-core/references/ofox-image.sh: No such file or directory

Nothing is broken — this skill delegates all execution to ofox-image-core and reaches it by relative path, and that path just missed. Two different situations wear this same message, so run the probe in "Where the core skill lives" before deciding which one it is:

  • The probe printed a directory — the core is installed and only the directory name was wrong, which is the normal LobeHub case (ofoxai-skills-ofox-image-core). Re-run against what the probe printed. Nothing needs installing.
  • The probe printed nothing — ofox-image-core really is absent, and installing it is the user's call to make, not yours: an install writes outside this working directory, so hand over the command and let them run it rather than running it for them. Which command depends on the installer they already have — skills.sh is npx skills add ofoxai/skills --skill ofox-image-core, which asks for that one skill and answers none of the agent, scope or confirmation questions on the user's behalf; this repo's own wrapper is npx ofox-skills ofox-image-core, the same install with all three answered in advance (every agent, user-level, no prompts); on LobeHub or ClawHub, install ofox-image-core from the same publisher. Ask for the one skill that is missing rather than the whole repo, and give all three routes — pointing a LobeHub user at the skills.sh line alone reads as "abandon your installer", which isn't the advice.

Either way, name the missing skill and where it was expected rather than relaying the raw path error, which names neither.

A broken link to approval-gate.md has the same two causes and its own probe above. This skill packages only its SKILL.md and CHANGELOG.md, so nothing here becomes unusable when a link misses — the routes, the question set, the prompt shapes and the defaults are all written out in this file. What is lost is the gate's exact wording, which still applies, and ofox-image-core's depth on the API itself.

Exit codes worth knowing

Full table in ../ofox-image-core/SKILL.md. The ones that come up:

CodeMeaningWhat to do
0Success, or a --dry-run that spent nothingRead STATUS to tell them apart
1Parameter rejected locally, no network call, nothing billedFix the flag and retry freely. A source file that is not jpeg/png/webp, a --quality typo, --n on a model that refuses it, and a --model that does not serve the endpoint all land here
2Environment problem — curl/jq/OFOX_API_KEY missing, or ffmpeg/ffprobe missing while --target-aspect was passedAsk the user to fix it; check reports the same. Nothing was attempted, so the set has not started
3The API rejected it, the response couldn't be parsed, or a --target-aspect target couldn't be metRead the upstream message. An upstream rejection (endpoint_not_supported, model_not_found) is free and only reaches here when the local catalog check was skipped or fell back to a snapshot; a crop that couldn't be met means that image was generated and billed and is on disk at the -uncropped path
4--out-dir could not be created or enteredCaught before any network call. Fix the path and retry — no money spent finding out. Worth hitting on image 1 rather than image 4, which is another reason to dry-run all of them first
5Ambiguous network failure — no HTTP response at allThere is no job id to look up. Do not auto-retry that image; tell the user to check https://app.ofox.ai's usage history, and do not start the rest of the set until they have

Common failure modes and fixes

SymptomCauseFix
The four images are four different-looking productsThe generate route was used for a real item. Measured: two renders of one mug prompt came back with different proportions, scale and lightSwitch to the edit route with a photo of the real product. For a genuinely fictional product this is expected — say so before spending, not after
The product changed inside one image of an edited setOne roll. The endpoint edits rather than redraws on the runs measured, but that is four runs on one model and is not a guaranteeRe-run that one image with the keep sentence made more specific (name the features, not just "the product"). A new call, so a new row on a new table
A label or logo went soft or wrongText is what degrades first, and nothing here measures a label surviving a whole-scene changeCheck every image at magnification before delivering. If the label has to read exactly right, composite it back in an editor — hal-image does that locally and free
The images are not all the same shape--target-aspect was passed on some calls and not others, or the source was not cropped and --size was relied onPass --target-aspect on every call in the set. --size is untested on the edits endpoint; the output's shape otherwise follows the input's
Exit 1 on --nThe chosen model refuses n outright — google/gemini-3.1-flash-image does, even at n: 1Omit --n. This skill's default route doesn't use it; nothing was submitted, so it is free to fix
Exit 1, "exists but does not serve /v1/images/edits"A model that cannot edit was pinned, caught locally against the catalog's supported_endpoints. Nothing submittedmodels --endpoint edits lists the ones that can, read live. Usually the fix is to stop passing --model at all. Reaching the API instead returns endpoint_not_supported, also free
The bill was bigger than expectedThe set is N bills, and on the edit route each one includes the uploaded picture. Input tokens are not linear in pixels — 854x480 bills 576 image tokens where 256x256 bills 256Nothing to fix after the fact. Next time: dry-run every image with the real file, show the total, and crop the source smaller if the deliverable is small
A --quality typo on an edit rendered and billed anywayThe edits endpoint does not validate the value; measured on six modelsPrevention only: --dry-run first, which rejects it locally, and omit --quality on edits entirely
Half the set is goodExpected — that is what a set is forRe-roll only the misses, each as its own row on a second cost table. Do not re-run the whole set
Exit 5 partway through a setNo HTTP response at all on one image; there is no job id to checkStop the set. Have the user check https://app.ofox.ai's usage history before any further calls — the ambiguous one may or may not have billed

When NOT to use

  • One change to one image. "Change this photo's background to a beach" is image-edit — one in, one out, with the craft for what survives an edit. Come here when the answer is a set.
  • The deliverable is video. A product photo becoming catalog footage is seedance-product-video, which owns the orbit, the turntable and the literal-accuracy prompt craft, and which has its own documented text-only route for fictional products. A cinematic brand spot is seedance-ad-creative. If the user wants both a set of stills and a clip, they are two jobs with two cost tables — and note that an image attached to a video job forces the clip's ratio to follow the image's, so the video skill owns the crop decision rather than inheriting one made here.
  • One plain render from text, with no set and no product to keep consistent. That is generate and belongs to ofox-image-core directly, as that skill's own description says.
  • Character images for an anime video sequence. seedance-anime-drama generates those itself and carries the machinery that keeps one character consistent across shots; images made outside that flow are not tracked by it.
  • The user is driving the API directly — naming low-level parameters, debugging a failed request. ofox-image-core is the skill for that; this one adds set composition, not API surface.
  • The change has to be exact — a supplied logo dropped in, a specific hex background, a pixel-accurate crop, text that must read correctly. A generative render is not a compositing operation. hal-image does that locally, deterministically and for free.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Publish a static site (a folder of HTML/CSS/JS/images/fonts) to Cloudflare and get back a live, shareable URL in seconds. Use when you have a finished static page or site and need to hand someone a link they can open on any device — reports, mockups, one-off landing pages, AI-generated HTML, "give me a link I can share". Runs one packaged command (references/deploy.mjs) built on the Wrangler CLI, which is what Cloudflare's own agent guidance tells agents to use. Deploys permanently to your Cloudflare account when credentials are present, or as a 60-minute claimable preview when they are not — and says plainly which one you got. Bakes an expiry countdown into previews (matched to the real 60-minute limit; --ttl to shorten it), and supports optional server-side six-digit access-code protection with -otp for permanent links only. Unprotected delivery may fall back to a file; protected delivery fails closed.

日本語の概要は準備中です。原文の説明を表示しています。

ofoxai/skills22026年9月22日 更新

explainer

無料

Requires OFOX_API_KEY — create one at https://app.ofox.ai. Turn an article, doc or release note into a short explainer clip — one person to camera, or a voiceover over illustrative footage. The user supplies the source text and the model generates the speech; audio cannot be uploaded, measured. A 30-second clip holds about eighty spoken words in eight sentences — measured, and well under a tenth of a 1,200-word post — so this skill does not summarise an article, it picks the single idea worth saying and helps choose which one. Use when a user asks to turn writing into a short spoken video, e.g. "make a 30-second explainer from this blog post", "explain this feature in a short video", "turn our changelog into a clip", "a quick video explaining what this paper found". Do not use for a scene between people (see seedance-short-drama), a brand or product ad (see seedance-ad-creative), a handheld creator clip (see ugc-ads), or when the user already has both a portrait and the finished words (see talking-head). Budget sentences as well as words — each sentence boundary costs about 0.7 seconds of silence, so a script with more sentences runs longer at the same word count.

日本語の概要は準備中です。原文の説明を表示しています。

ofoxai/skills22026年9月22日 更新

hal-image

無料

Handle images with ImageMagick (magick) — read metadata, resize, crop, rotate, convert format, combine images (side-by-side or grids with montage), overlay logos/watermarks, and losslessly compress with oxipng before sending. Use when the user sends an image to process, when you produce an image to deliver, or before attaching any image to a message or upload — compressing first keeps transfers fast and tokens low. Core discipline - always compress before delivering, never expose local file paths to the user, and fail open (if a tool is missing or a step errors, pass the original through unchanged; never block the task).

日本語の概要は準備中です。原文の説明を表示しています。

ofoxai/skills22026年9月22日 更新

hal-vault

無料

Securely store, search, and use secrets (API keys, tokens, passwords, SSH keys) with hal-vault, an SSH-key encrypted local secret store. Use when the user shares a credential that should be saved, asks what secrets are stored or where a key is, or when a command/workflow needs a secret injected. Core discipline - never print raw secret values into chat, logs, or files; reference secrets only by their masked form, and use --reveal only to feed another process, never into a command argument (where it lands in the machine's process table) or into text you write down.

日本語の概要は準備中です。原文の説明を表示しています。

ofoxai/skills22026年9月22日 更新

Requires OFOX_API_KEY — create one at https://app.ofox.ai. Change one thing in an image you already have and leave the rest of the picture alone — swap the background, recolour a part, remove or add an object, clean up a photo — from a local jpeg/png/webp file. Delegates to ofox-image-core's `edit` subcommand (POST /v1/images/edits, one synchronous request), prices the job with --dry-run before spending, and reports the real token cost including the uploaded picture, which is billed. Use when a user hands over an image and asks for a change to it, e.g. "change the background of this photo to a beach and keep the person unchanged", "make this button green", "remove the car in the background", or "put this product on a plain white background". Do not use to draw a new image from a text description with no input picture (that is ofox-image-core's `generate`), to produce a set of several images to choose between (see product-image), or to turn a photo into video (see seedance-product-video or seedance-ad-creative). Editing the content of an existing video — "change the background of my clip, keep the product" — has no route here and none in the video API either (measured; the mode field an edit would use is accepted and silently ignored, so nothing errors); this skill edits a single still, and routing such a request to a video skill generates brand-new footage instead of changing theirs.

日本語の概要は準備中です。原文の説明を表示しています。

ofoxai/skills22026年9月22日 更新

Requires OFOX_API_KEY — create one at https://app.ofox.ai, plus two images you already have, a start frame and an end frame. Animates the motion between them as one video — both frames go into a single Ofox video job, the clip opens on A, closes on B, and the model fills the middle. Use when a user has two stills and wants the in-between animated, e.g. "here is the before and the after, animate the transition", "make a video that starts on this image and ends on that one", "tween these two frames", or "move the object from where it sits in the first picture to where it sits in the second". Do not use when only one image exists (animating a single frame is seedance-ad-creative or seedance-product-video), or when the pair is two states of a user interface (see product-demo).

日本語の概要は準備中です。原文の説明を表示しています。

ofoxai/skills22026年9月22日 更新

ofoxai のスキルをすべて見る

このスキルの問題を報告する