gooseworks 0.3.15 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +58 -8
  2. package/dist/agents/claude.js +1 -1
  3. package/dist/agents/claude.js.map +1 -1
  4. package/dist/agents/codex.js +1 -1
  5. package/dist/agents/codex.js.map +1 -1
  6. package/dist/agents/skill-links.d.ts +7 -2
  7. package/dist/agents/skill-links.d.ts.map +1 -1
  8. package/dist/agents/skill-links.js +10 -5
  9. package/dist/agents/skill-links.js.map +1 -1
  10. package/dist/auth/attribution.d.ts +14 -0
  11. package/dist/auth/attribution.d.ts.map +1 -0
  12. package/dist/auth/attribution.js +37 -0
  13. package/dist/auth/attribution.js.map +1 -0
  14. package/dist/auth/oauth-server.d.ts +4 -4
  15. package/dist/auth/oauth-server.d.ts.map +1 -1
  16. package/dist/auth/oauth-server.js +8 -8
  17. package/dist/auth/oauth-server.js.map +1 -1
  18. package/dist/commands/call.d.ts.map +1 -1
  19. package/dist/commands/call.js +6 -5
  20. package/dist/commands/call.js.map +1 -1
  21. package/dist/commands/install.d.ts.map +1 -1
  22. package/dist/commands/install.js +11 -7
  23. package/dist/commands/install.js.map +1 -1
  24. package/dist/commands/login.d.ts +1 -1
  25. package/dist/commands/login.d.ts.map +1 -1
  26. package/dist/commands/login.js +13 -27
  27. package/dist/commands/login.js.map +1 -1
  28. package/dist/config.d.ts +1 -1
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +6 -2
  31. package/dist/config.js.map +1 -1
  32. package/dist/skills/installer.d.ts +9 -0
  33. package/dist/skills/installer.d.ts.map +1 -1
  34. package/dist/skills/installer.js +20 -6
  35. package/dist/skills/installer.js.map +1 -1
  36. package/dist/skills/master-skill.d.ts +24 -22
  37. package/dist/skills/master-skill.d.ts.map +1 -1
  38. package/dist/skills/master-skill.js +392 -149
  39. package/dist/skills/master-skill.js.map +1 -1
  40. package/dist/skills/names.d.ts +53 -1
  41. package/dist/skills/names.d.ts.map +1 -1
  42. package/dist/skills/names.js +107 -7
  43. package/dist/skills/names.js.map +1 -1
  44. package/dist/skills/routes.d.ts +68 -0
  45. package/dist/skills/routes.d.ts.map +1 -0
  46. package/dist/skills/routes.js +124 -0
  47. package/dist/skills/routes.js.map +1 -0
  48. package/package.json +1 -1
  49. package/skills/goose-ads/SKILL.md +124 -104
  50. package/skills/goose-product-photos/SKILL.md +129 -0
  51. package/skills/gooseworks/SKILL.md +100 -41
  52. package/skills/routes.json +157 -0
@@ -2,8 +2,8 @@
2
2
  name: goose-ads
3
3
  slug: goose-ads
4
4
  description: >
5
- GooseWorks ads skill — create, edit, AND analyze ad creative. Remix a static (image) ad
6
- template into a branded ad for the user's product, edit/re-roll an existing creative,
5
+ GooseWorks ads skill — create, edit, AND analyze ad creative. Turn an approved source ad
6
+ into a branded ad for the user's product, edit/re-roll an existing creative,
7
7
  research a brand for ads, OR analyze ad performance (Meta/Google campaign diagnostics,
8
8
  creative fatigue, CAC & lead quality, competitor ad intelligence, ad angles & hooks). Use
9
9
  when the user says "remix this ad", references a static ad template id/slug, asks to "make
@@ -12,7 +12,7 @@ description: >
12
12
  app uses) — credits are reserved and billed server-side. Analytics recipes are fetched from
13
13
  goose-skills on demand.
14
14
  category: ads
15
- version: 2.2.0
15
+ version: 2.4.0
16
16
  author: GooseWorks
17
17
  tags: [gooseworks, ads, remix, static-ad, brand, creative, image, analytics, meta-ads, performance]
18
18
  ---
@@ -22,7 +22,7 @@ tags: [gooseworks, ads, remix, static-ad, brand, creative, image, analytics, met
22
22
  The GooseWorks ads skill. Two jobs:
23
23
 
24
24
  1. **Create / edit ad creative** — a **thin wrapper** over the backend's single generation
25
- workflow. You pick the brand + template(s) and submit ONE batch; the **backend** runs the
25
+ workflow. You pick the brand + approved source ad(s) and submit ONE batch; the **backend** runs the
26
26
  whole pipeline (compose → generate → persist → judge), reserves and bills credits, and
27
27
  stores the renders. You do NOT generate images, call FAL, manage render rows, or upload
28
28
  files — those are gone. This is the exact same workflow the GooseWorks ads app uses, so the
@@ -36,6 +36,24 @@ Everything goes through the `mcp__gooseworks__*` tools. If they are not availabl
36
36
  tell the user to run `gooseworks install --claude --mcp`** (and restart Claude Code). There is
37
37
  no HTTP/file fallback — the REST ad endpoints are session-cookie-only and reject your token.
38
38
 
39
+ ## Start from the brand context — don't re-ask what it already answers
40
+
41
+ If the `gooseworks` router handed you brand context, USE IT. If you were invoked directly, call
42
+ `brand_get_context` first (falling back to `get_brand_kit` for the selected brand). It already
43
+ answers most of what the flows below would otherwise ask the user:
44
+
45
+ - **Which product to feature** → `products[]`. Offer the real catalog entries; never guess a
46
+ product name and never ask the user to list their products.
47
+ - **The vibe / tone of the copy** → the brand's **voice**. Use it; don't ask "what tone?".
48
+ - **Who the ad is for** → the brand's **audience**. Don't ask "who's the target?".
49
+ - **The angle, offer framing, and what to claim** → **positioning**, value props, proof points.
50
+ - **Logo, colors, fonts** → owned by the backend research pass. **Never re-derive them.**
51
+ - **Whether the facts are trustworthy yet** → **research status**. If it isn't complete, say so in
52
+ one line and continue; the batch queues and runs when research finishes.
53
+
54
+ Ask only for what the context genuinely doesn't answer: the specific campaign intent (season,
55
+ promo, which of several angles), the source ad, and anything the user must consent to.
56
+
39
57
  ## Identity & credits
40
58
 
41
59
  - One agent-scoped token authenticates the `gooseworks` MCP tools. Never print it. The tools
@@ -46,74 +64,63 @@ no HTTP/file fallback — the REST ad endpoints are session-cookie-only and reje
46
64
  message and stop) and bills only the images that actually complete. Call
47
65
  `estimate_remix_batch` first to tell the user the cost; `gooseworks credits` shows balance.
48
66
 
49
- ## Defaults — match the app (priority: frontend, then backend)
67
+ ## Live MCP contract — inspect it before asking
50
68
 
51
- When the user doesn't specify, submit with the **ads app's** defaults so skill output matches
52
- what they'd get in the UI. **Pass these explicitly:**
69
+ The currently registered MCP tool schemas are the source of truth for inputs, supported choices,
70
+ and defaults. Do not copy an exhaustive input list from this skill or rely on remembered fields.
53
71
 
54
- - `variants`: **1** per template
55
- - `ratios`: **["4:5"]** (Meta feed vertical)
56
- - `engine`: **"gpt_image_2"**
57
- - `quality`: **"medium"**
58
- - `preserve_source_styling`: **ASK the user** — "Keep original" (the template's own
59
- colours/fonts → `preserve_source_styling: true`) vs "Match brand" (restyle to the brand
60
- palette/fonts → `preserve_source_styling: false`). This mirrors the app's Styling control.
61
- **The default is "Keep original"** — if the user doesn't answer or doesn't care, send `true`.
72
+ Before each tool call:
62
73
 
63
- If the user asks for something the app exposes (more variants, a different ratio like 1:1 or
64
- 9:16, a faster engine, higher quality), pass that instead. Omitting a field lets backend policy
65
- decide — fine, but prefer sending the app defaults for predictable parity.
74
+ 1. Inspect the live schema for the tool you are about to use.
75
+ 2. Fill required inputs already known from the Brand Kit, selected source, or conversation.
76
+ 3. Ask the user only for required inputs that cannot be inferred and for choices that materially
77
+ change the result. Do not turn every optional field into a questionnaire.
78
+ 4. Omit unspecified optional settings so the backend applies its current app defaults.
79
+ 5. If the live schema conflicts with this workflow, follow the live schema and report the drift
80
+ with `log_cli_event`.
66
81
 
67
82
  ## The generation tools (the new, single-workflow surface)
68
83
 
69
- - `submit_remix_batch { brand_id, items, prompt?, product_name?, preserve_source_styling?,
70
- reference_image_urls?, allow_without_product_image?, engine?, quality? }` — **the one call
71
- that makes ads.** `items` is `[{ template_id, variants?, ratios? }]` (≤20 templates).
84
+ - `submit_remix_batch` — **the one call that makes ads.** Inspect its live schema and supply
85
+ the required brand/source inputs plus any choices the user explicitly made.
72
86
  Returns the batch with a `links` block (`brand_url` + per-creative `app_url`). If the brand's
73
87
  research isn't finished yet the batch comes back `status: "queued"` — it auto-runs the moment
74
88
  research completes; tell the user it'll appear shortly, don't error.
75
- - `estimate_remix_batch { items, engine?, quality? }` — cost preview (images, credits_per_image,
76
- total_credits, available_credits). `template_id` accepts a uuid OR a slug. Reserves nothing. Use
77
- to quote the cost first. Check `unknown_template_ids` in the response — any token there didn't
78
- resolve (submit would 404 on it); don't quote a cost that silently dropped a bad id.
79
- - `get_remix_batch { batch_id }` — poll status. Returns each creative with its renders and
89
+ - `estimate_remix_batch` — cost preview. Reserves nothing. Use it to quote the cost first and
90
+ check whether every selected source resolved before submitting.
91
+ - `get_remix_batch` — poll status. Returns each creative with its renders and
80
92
  `completed`/`failed`/`pending` counts, plus `links`. A creative is done when its `pending` is 0
81
93
  — NOT when `current_render_url` is set (during a regenerate that field still points at the prior
82
94
  image). Each render carries `age_seconds` (since queued) and `elapsed_seconds` (time generating):
83
95
  use them to tell a slow-but-healthy render from a stuck one. A render only failed when its
84
96
  `status` is `"failed"` — never assume a stall and re-submit, that double-bills.
85
- - `list_brand_creatives { brand_id, limit?, offset? }` — the brand's gallery feed (newest
97
+ - `list_brand_creatives` — the brand's gallery feed (newest
86
98
  first) + `brand_url`. Alternative poll target; also use to show everything made for a brand.
87
- - `surprise_me_templates { brand_id, count? }` — the **"Surprise me" recommender**. Picks
88
- brand-relevant templates (SAME logic as the web /create "Surprise me" button — templates
89
- whose category overlaps the brand float to the top, bucketed + shuffled so picks stay fresh).
99
+ - `surprise_me_templates` — the **"Surprise me" recommender**. Picks
100
+ remixable Community creations (SAME logic as the web /create "Surprise me" button), shuffled
101
+ so picks stay fresh. It does not use the retired curated third-party catalog.
90
102
  Returns the picked templates (id, slug, title, image, ratio) AND a ready-to-open `create_url`
91
103
  (the /create page with `cli=true` and the picks pre-selected). This is how you recommend
92
104
  templates — do NOT hand-pick from the raw catalog yourself (see "Picking templates" below).
93
- - `regenerate_creative { project_id, mode?, prompt?, source_render_id?, ... }` — **edit / re-roll
94
- one existing creative** through the same pipeline. `mode: "variation"` (default) re-rolls from
95
- the template; `"edit"` makes a targeted change to a specific render (`prompt` + `source_render_id`
96
- required); `"exact"` runs `prompt` verbatim against that render's references. Returns a
97
- single-item batch — poll it with `get_remix_batch`.
98
- - `set_creative_feedback { render_id, rating?, comment?, reasons? }` — record the user's reaction
99
- to a generated image (the SAME happy/neutral/sad + comment + reason chips the app captures). Use
100
- it whenever the user reacts ("love this one" / "the logo is wrong"). `render_id` is a RENDER id
101
- from `get_remix_batch` / `list_brand_creatives`, not a project/batch id. `reasons` are quick
102
- chips (wrong_product, brand_or_logo_wrong, off_brand, text_garbled, weak_copy, ai_or_distorted).
105
+ - `regenerate_creative` — edit or re-roll one existing creative through the same pipeline.
106
+ Inspect the live schema to select the supported mode and required source inputs. Returns a
107
+ single-item batch; poll it with `get_remix_batch`.
108
+ - `set_creative_feedback` — record the user's reaction to a generated image. Use it whenever
109
+ the user reacts; inspect the schema for the current rating and reason choices.
103
110
 
104
111
  ### Plan mode — review the plan BEFORE generating (optional)
105
112
 
106
113
  For users who want to approve each ad's plan before spending credits (the app's "Plan it" flow):
107
114
 
108
- - `submit_remix_batch { ..., requires_approval: true }` — composes each creative's plan and PAUSES.
115
+ - Use the approval option exposed by `submit_remix_batch` — it composes each creative's plan and PAUSES.
109
116
  **No credits are reserved and no image renders** until you approve.
110
- - `list_ad_approvals { brand_id? }` — poll this; returns `{ items, counts }`. While a creative is
117
+ - `list_ad_approvals` — poll this. While a creative is
111
118
  `composing`, wait; once `awaiting_approval`, show its `plan` (composed prompt + refs + quality)
112
119
  to the user.
113
- - `revise_ad_plan { project_id, message?, variant_label? }` — recompose from a chat steer, still
120
+ - `revise_ad_plan` — recompose from a chat steer, still
114
121
  free. Poll `list_ad_approvals` until it's `awaiting_approval` again.
115
- - `approve_ad_plan { project_id | batch_id }` — approve ONE creative (`project_id`) or the whole
116
- batch (`batch_id`). **This is the step that reserves credits and renders.** Then poll
122
+ - `approve_ad_plan` — approve one creative or the whole batch using the live schema.
123
+ **This is the step that reserves credits and renders.** Then poll
117
124
  `get_remix_batch` and hand back links as usual.
118
125
 
119
126
  Only offer plan mode when the user asks to review/approve first — the default path generates
@@ -121,19 +128,23 @@ immediately.
121
128
 
122
129
  ## Reading the brand & picking inputs (still MCP, read-only)
123
130
 
124
- - `get_brand_kit { brand_id }` — the CANONICAL brand context (name, description, audience,
125
- voice, brandType, valueProps, colors, typography, logoUrl, `products[]`, presigned
126
- `referenceImages[]`). Read this to choose `product_name` and any `reference_image_urls`.
127
- - `list_ad_brands { query? }` / `get_ad_brand { brand_id }` — find/fetch a brand. Pass `query` to
128
- filter by name (case-insensitive) instead of listing every brand; rows are lean (no `brand_kit` —
129
- read `get_brand_kit` for the full kit).
130
- - `get_static_ad_template { template_id }` — resolve a template (slug OR uuid; public catalog
131
- AND your org's private templates). Confirms it exists before you submit.
132
- - `remix_community_ad { community_id }` — a **Community** ad id is an `ad_project` id, not a
131
+ - `get_brand_kit` — read the canonical brand context and available products/assets.
132
+ - `list_ad_brands` / `get_ad_brand` — find and fetch the active brand.
133
+ - `list_user_ad_templates` — list the org's own uploads and
134
+ imported ads. Prefer `relationship: "self"` when the user wants to reuse their own ads;
135
+ `relationship: "competitor"` is research/inspiration, never proof that the user owns the ad.
136
+ - `search_ad_templates` — search remixable Community generations. The
137
+ retired curated third-party catalog is not returned.
138
+ - `get_static_ad_template` — resolve a source already owned by
139
+ the org, including an own upload or a snapshotted Community creative. It does not resolve the
140
+ retired curated third-party catalog.
141
+ - `remix_community_ad` — turn a selected Community creative into a private remix source before
142
+ submitting it. A Community ad id is an `ad_project` id, not a
133
143
  template id. Call this FIRST to snapshot it into a private template, then use the returned
134
144
  template `id` in `items`.
135
- - `create_user_ad_template { workspace_path }` — "bring your own ad": upload the user's own
136
- image as a private template, then remix it like any other.
145
+ - `create_user_ad_template` — upload a source image as a private template. Answer any
146
+ ownership/rights input only from the user's explicit confirmation. Never claim rights for a
147
+ competitor ad or an image found online.
137
148
  - `get_ad_project` / `append_project_message` — inspect a creative / leave a note on its thread.
138
149
 
139
150
  ## Keep the brand kit in sync — reconcile, then update (ASK first)
@@ -144,7 +155,7 @@ tagline, audience, voice, a product's name/price/description, "our logo is X", "
144
155
  anymore", a new product photo — treat it as a possible kit update, don't just use it for this one
145
156
  ad and forget it:
146
157
 
147
- 1. **Check it against the kit.** `get_brand_kit { brand_id }` and see whether what the user said
158
+ 1. **Check it against the kit.** Call `get_brand_kit` for the active brand and see whether what the user said
148
159
  matches, is missing from, or contradicts the kit.
149
160
  2. **If it's already in the kit and matches** — nothing to do; proceed.
150
161
  3. **If it's new or different — ASK before writing.** Confirm in one line: *"Want me to update
@@ -152,65 +163,74 @@ ad and forget it:
152
163
  asked you to change the brand). Don't silently mutate the kit, and don't nag on trivia.
153
164
  4. **Persist with the write tools** (partial — only the fields you pass are touched; each edit is
154
165
  recorded as a user override that later re-research won't clobber):
155
- - `update_brand_kit { brand_id, description?, audience?, voice?, instructions?, brand_type?,
156
- value_props?, primary_color?, accent_color? }` — the structured kit fields.
157
- - `upsert_brand_product { brand_id, ... }` / `delete_brand_product` — manage products.
158
- - `add_brand_product_image { brand_id, ... }` / `remove_brand_reference_image` — product /
159
- reference photos.
166
+ - `update_brand_kit` — structured brand fields.
167
+ - `upsert_brand_product` / `delete_brand_product` — products.
168
+ - `add_brand_product_image` / `remove_brand_reference_image` — product and reference photos.
169
+ Inspect each live schema and send only the fields needed for the confirmed change.
160
170
  5. **Confirm what changed** and continue the task. (Logo, colors, and fonts are owned by the
161
171
  backend research pass — prefer `update_ad_brand` / the research flow for those, not free text.)
162
172
 
163
173
  This is the parity gap the app closes in-product: a brand fact the user gives mid-task should be
164
174
  able to flow back into the kit — with their ok — instead of being lost.
165
175
 
166
- ## Picking templates — ASK the user; don't freelance from the catalog
176
+ ## Picking source ads — use approved sources, not the retired catalog
167
177
 
168
178
  When the user wants to make ads but has NOT named a specific template (id/slug/Community
169
179
  ad/upload), do NOT silently browse the raw catalog and hand-pick for them. Instead run this
170
180
  short ask flow — it mirrors the web app and keeps the human in the loop:
171
181
 
172
- 1. **Ask what kind of ads they want** — the angle/offer/theme/season, the vibe, and which
173
- product from the brand kit to feature. This shapes both the template choice and your steering
174
- `prompt`. Keep it to one or two quick questions.
175
- 2. **Ask how to pick templates: "Choose explicitly" or "Surprise me".**
176
- - **Surprise me** (they want you/the app to pick) → call
177
- `surprise_me_templates { brand_id, count }` and hand the user the returned `create_url`.
182
+ 1. **Ask what kind of ads they want** — the angle/offer/theme/season. **The brand context already
183
+ gives you the vibe (voice), the audience, and the product catalog — do NOT ask for those.**
184
+ Offer the real `products[]` to pick from rather than asking "which product?", and derive the
185
+ tone from the brand's voice. This shapes both the source choice and your steering `prompt`.
186
+ Keep it to one quick question about campaign intent.
187
+ 2. **Ask how to pick a source: their own ads, Community, upload, or "Surprise me".**
188
+ - **Their own ads** → use `list_user_ad_templates` to load the active brand's own sources and
189
+ let them choose from the results.
190
+ - **Community** → `search_ad_templates`, let them choose, then call `remix_community_ad`
191
+ before submitting.
192
+ - **Upload** → upload through the workspace and call `create_user_ad_template`. If its live
193
+ schema requires an ownership or permission answer, only supply it after explicit confirmation.
194
+ - **Surprise me** (they want you/the app to pick) → call `surprise_me_templates` for the active
195
+ brand and hand the user the returned `create_url`.
178
196
  It opens /create in **CLI mode** with the picks pre-selected, a preview modal, and the
179
197
  **copyable remix prompt at the bottom** (in place of the Generate input). They can swap
180
198
  picks and copy that prompt. If they'd rather you "just make them" without reviewing in the
181
199
  app, you MAY submit the `surprise_me_templates` picks directly (skip to submit).
182
- - **Choose explicitly** (they want to browse and select) → hand the user this URL, with the
200
+ - **Browse in the app** → hand the user this URL, with the
183
201
  active brand's slug filled in:
184
202
  `https://make.gooseworks.ai/create?brand=<brand-slug>&cli=true`
185
203
  In CLI mode the app shows the copyable remix prompt at the bottom (dismissable / switchable
186
- back to the UI composer). They browse, select templates, and copy the prompt.
187
- 3. **Ask the styling** — "Keep original" (default) vs "Match brand" — per the Defaults section.
188
- 4. **Close the loop.** When the user **pastes back the copyable remix prompt** from the app
204
+ back to the UI composer). They browse the available own/Community sources and copy the prompt.
205
+ 3. **Close the loop.** When the user **pastes back the copyable remix prompt** from the app
189
206
  (it names the brand + the templates they chose), THAT is your cue to generate: resolve the
190
- named template(s), then `submit_remix_batch` with the app defaults + the styling they chose.
207
+ named source(s), inspect `submit_remix_batch`, and collect only its unresolved required inputs.
191
208
 
192
- If the user already named a template (id/slug), a Community ad, or an upload, skip the ask flow
193
- for template choice — they've chosen — but still confirm the styling default and steer the prompt.
209
+ If the user already named an owned source (id/slug), a Community ad, or an upload, skip the source
210
+ choice. Competitor ads may inform the angle or structure, but describe them as inspiration, never
211
+ claim ownership, and never attest rights for the user.
194
212
 
195
213
  ## Workflow — make ads from a template
196
214
 
197
- 1. **Resolve the brand.** `list_ad_brands` by name/site → `get_brand_kit { brand_id }`. If the
215
+ 1. **Resolve the brand.** Use `list_ad_brands` by name/site, then call `get_brand_kit` for the
216
+ selected brand. If the
198
217
  kit's `researchStatus` isn't `complete`, you can still submit (the batch queues and runs when
199
218
  research finishes) — just tell the user. Use the kit to pick `product_name` (a real entry from
200
219
  `products[]`, not a guess) and, if the user supplied product photos, `reference_image_urls`.
201
- 2. **Pick the template(s) via the ask flow above** (kind of ads → Choose explicitly vs Surprise
202
- me → styling). Once you have concrete ids: `get_static_ad_template { template_id }` for each.
220
+ 2. **Pick the source ad(s) via the ask flow above.** Once you have concrete ids:
221
+ call `get_static_ad_template` for each.
203
222
  For a Community ad, `remix_community_ad` first; for an uploaded image, `create_user_ad_template`
204
223
  first.
205
224
  3. **(Optional) Craft the steering prompt.** The `prompt` is OPTIONAL — this is where the skill
206
225
  adds value: turn the user's intent (from step 1) into a concise steering note (e.g. tone,
207
226
  season, emphasis). Don't over-specify; the backend pipeline + brand kit handle palette, fonts,
208
227
  product swap.
209
- 4. **(Optional) Quote the cost.** `estimate_remix_batch { items, engine, quality }` → tell the user.
210
- 5. **Submit ONE batch.** `submit_remix_batch { brand_id, items, prompt?, product_name?, engine,
211
- quality, preserve_source_styling }` using the app defaults above and the styling the user chose.
212
- Keep the returned `batch_id` and `links`.
213
- 6. **Poll until done.** `get_remix_batch { batch_id }` (or `list_brand_creatives`) every ~20-30s
228
+ 4. **Quote the cost.** Inspect and call `estimate_remix_batch`, then tell the user.
229
+ 5. **Submit ONE batch.** Inspect the current `submit_remix_batch` schema, fill known required
230
+ inputs, ask only for unresolved user decisions, and omit unspecified optional settings. Keep
231
+ the returned `batch_id` and `links`.
232
+ 6. **Poll until done.** Call `get_remix_batch` for the returned batch (or use
233
+ `list_brand_creatives`) every ~20-30s
214
234
  until every creative's `pending` is 0. Most images finish in a few minutes; text-heavy templates
215
235
  and `quality: high` take longer. Read each render's `elapsed_seconds` rather than guessing — a
216
236
  render that's still `running` is healthy; do NOT re-submit thinking it stalled (that double-bills).
@@ -219,17 +239,14 @@ for template choice — they've chosen — but still confirm the styling default
219
239
 
220
240
  ## Workflow — edit an existing ad
221
241
 
222
- User wants to tweak a creative they already made → `regenerate_creative`:
223
- - "make another version / different take" → `mode: "variation"` (optionally new `prompt`,
224
- `product_name`, `ratios`).
225
- - "change X in this exact image" → `mode: "edit"`, `source_render_id` = the render to edit,
226
- `prompt` = the change.
227
- - "run exactly this prompt on the product" → `mode: "exact"`, `source_render_id` + `prompt`.
228
- Then poll with `get_remix_batch` and hand back the links, same as above.
242
+ User wants to tweak a creative they already made → use `regenerate_creative`. Infer whether they
243
+ want another take, a targeted edit, or an exact instructed change from their request. Then inspect
244
+ the live schema, ask only for any required source or instruction that is still missing, submit,
245
+ poll with `get_remix_batch`, and hand back the links.
229
246
 
230
247
  ## Brand research
231
248
 
232
- Prefer the backend's result: `get_brand_kit { brand_id }`. If `researchStatus` is
249
+ Prefer the backend's result: call `get_brand_kit` for the selected brand. If `researchStatus` is
233
250
  `complete`, REUSE it — never re-research.
234
251
 
235
252
  **The split — backend owns visuals, you own the qualitative depth:**
@@ -246,9 +263,10 @@ Prefer the backend's result: `get_brand_kit { brand_id }`. If `researchStatus` i
246
263
 
247
264
  **CLI brand-research flow:**
248
265
 
249
- 1. `create_ad_brand { name, website_url }` → keep `brand_id` + `slug`. The brand comes back with
266
+ 1. Inspect and call `create_ad_brand` with the known brand identity and website, then keep its id
267
+ and slug. The brand comes back with
250
268
  `research_status: "pending"` (light pass in flight).
251
- 2. **Wait for the backend light pass:** poll `get_brand_kit { brand_id }` until `researchStatus`
269
+ 2. **Wait for the backend light pass:** poll `get_brand_kit` for that brand until `researchStatus`
252
270
  is `complete` (usually <60s). Now the kit has authoritative logo/colors/fonts + a baseline.
253
271
  At this point generation is already unblocked — but do the deep pass to make it good.
254
272
  3. **Deep research locally:** `gooseworks fetch brand-research` and follow its phases. **Ground
@@ -262,10 +280,10 @@ Prefer the backend's result: `get_brand_kit { brand_id }`. If `researchStatus` i
262
280
  (`brandType` ∈ product | saas | service | agency | restaurant | fashion | beauty | fitness |
263
281
  finance | education | health). Only URLs already in our storage for product images.
264
282
  - **Do NOT set logo / colors / fonts here** — the backend light pass already owns those.
265
- 5. **Persist it:** `finalize_brand_research { brand_id }` merges `kit-patch.json` into the kit
283
+ 5. **Persist it:** call `finalize_brand_research` for the brand. It merges `kit-patch.json` into the kit
266
284
  NON-CLOBBERINGLY (it will NOT overwrite the backend's visuals or any user edit), then re-confirms
267
285
  `research_status: complete`.
268
- 6. **Verify:** `get_brand_kit { brand_id }` — confirm the qualitative fields you wrote are present
286
+ 6. **Verify:** call `get_brand_kit` again and confirm the qualitative fields you wrote are present
269
287
  before generating.
270
288
 
271
289
  **If the brand has NO website**, the backend light pass can't run (nothing to fetch) — do the whole
@@ -305,20 +323,22 @@ run through the `gooseworks` CLI (`gooseworks fetch` / `gooseworks call`), like
305
323
  each creative's `app_url`), copied verbatim. Never end on just "done" or a file path.
306
324
  - **Quote cost before generating** when it's non-trivial (use `estimate_remix_batch`), and
307
325
  relay `insufficient_credits` plainly if the submit is rejected — don't retry blindly.
308
- - **Don't hand-pick templates silently.** If the user didn't name a template, run the ask flow
309
- (kind of ads → Choose explicitly vs Surprise me → styling). "Surprise me" goes through
310
- `surprise_me_templates`; "Choose explicitly" sends them to `/create?brand=<slug>&cli=true`.
326
+ - **Use approved source paths.** If the user didn't name a source, run the ask flow (own ads,
327
+ Community, upload, Surprise me, or browse in the app). "Surprise me" goes through
328
+ `surprise_me_templates`; browsing uses `/create?brand=<slug>&cli=true`. Never use the retired
329
+ curated third-party catalog.
311
330
  Generate when they paste the app's copyable remix prompt back (or submit the surprise picks
312
331
  directly if they'd rather not review).
313
- - **Ask the styling** — Keep original (default) vs Match brand — before you submit.
332
+ - **Treat competitor ads as inspiration** — never attest rights, imply ownership, or promise to
333
+ copy a competitor's distinctive expression.
314
334
  - **Reconcile brand facts into the kit** — when the user states or changes something brand-level
315
335
  mid-task, check it against `get_brand_kit` and, with their ok, persist it via `update_brand_kit`
316
336
  / `upsert_brand_product` / `add_brand_product_image` so it sticks for future ads. Ask first;
317
337
  never silently mutate the kit.
318
- - **Record feedback** — when the user reacts to a generated image, capture it with
319
- `set_creative_feedback { render_id, rating/comment/reasons }` so the quality loop learns.
320
- - **Plan mode is opt-in** — only compose-and-pause (`requires_approval: true` → `list_ad_approvals`
321
- → `approve_ad_plan`) when the user wants to review before spending credits; otherwise generate
338
+ - **Record feedback** — when the user reacts to a generated image, inspect and call
339
+ `set_creative_feedback` so the quality loop learns.
340
+ - **Plan mode is opt-in** — only use the live approval option, then `list_ad_approvals` and
341
+ `approve_ad_plan`, when the user wants to review before spending credits; otherwise generate
322
342
  immediately.
323
343
  - **Don't busy-loop** — poll `get_remix_batch` on a sensible interval (~20-30s); a `queued`
324
344
  batch is waiting on research and will start on its own.
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: goose-product-photos
3
+ slug: goose-product-photos
4
+ description: >
5
+ GooseWorks Product Photos — turn a brand's product images into publish-ready photography
6
+ (clean studio shots, lifestyle scenes, on-model looks) while keeping the product faithful
7
+ (silhouette, materials, logo, colorway). You pick a brand + product and submit; the GooseWorks
8
+ backend runs the SAME server-side pipeline the Product Photos studio uses (compose → generate →
9
+ judge → auto-retry) and bills credits. Use when the user says "make product photos", "shoot my
10
+ product", "studio/lifestyle/on-model photo of <product>", "generate product photography", or
11
+ references a product to photograph. Unlike goose-ads (ad creative) this produces clean PRODUCT
12
+ photos that can then feed the ad workflow.
13
+ category: ads
14
+ version: 0.2.0
15
+ author: GooseWorks
16
+ tags: [gooseworks, ads, product-photos, photoshoot, product, ecommerce, studio, lifestyle, on-model]
17
+ ---
18
+
19
+ # GooseWorks Product Photos — branded product photography
20
+
21
+ The GooseWorks Product Photos skill. You **pick a brand + product and submit one generation**;
22
+ the **backend** runs the whole pipeline (compose the shot prompt → generate on `gpt_image_2` →
23
+ judge for product fidelity → auto-retry a few times for free) and stores the results. You do NOT
24
+ generate images, call a model, or manage files — this is the exact same workflow the Product
25
+ Photos studio uses, so the skill and the app can never drift. The point is to **enrich a brand's
26
+ usable product imagery** — approved photos join the brand kit and can then feed the ad workflow
27
+ (`goose-ads`).
28
+
29
+ ## Prerequisite — the GooseWorks MCP server is REQUIRED
30
+
31
+ Everything goes through the `mcp__gooseworks__*` tools. If they are not available, **stop and
32
+ tell the user to run `gooseworks install --claude --mcp`** (and restart Claude Code). There is no
33
+ HTTP/file fallback.
34
+
35
+ ## Start from the brand context — don't re-ask what it already answers
36
+
37
+ If the `gooseworks` router handed you brand context, USE IT. If you were invoked directly, call
38
+ `brand_get_context` yourself first. It answers most of the setup questions below, so **do not ask
39
+ the user for them**:
40
+
41
+ - **Which product?** — the context's `products[]` are the real catalog entries. Offer them; never
42
+ invent a product or ask the user to describe one you can already see.
43
+ - **What does it look like / what is it made of?** — grounded in the product's stored images and
44
+ description. Never guess a material, colorway, or silhouette.
45
+ - **What vibe / who is it for?** — the context's voice, positioning, and audience already say. Let
46
+ them shape the scene and styling instead of asking "what mood do you want?".
47
+ - **Brand look** — logo, colors, and fonts are owned by the backend research pass. Read them, never
48
+ re-derive them.
49
+
50
+ Ask only for the genuinely open choices: the shot `category`, how many photos, quality, and
51
+ whether a human model is wanted (which needs explicit consent — see the rules).
52
+
53
+ ## Identity & credits
54
+
55
+ - One agent-scoped token authenticates the tools; they resolve your org automatically. Never
56
+ print the token. (You may pass an optional `target` to operate on a specific agent/org, exactly
57
+ as the other GooseWorks tools; omit it to use your pinned scope.)
58
+ - **Credits are handled by the backend.** `generate_product_photos` reserves the estimated cost up
59
+ front and bills only the photos that pass the judge — **automatic retries are free**, and a photo
60
+ the judge can't get right (`flagged`) is shown but **never billed**. Call
61
+ `estimate_product_photos` first to quote the cost; `get_ad_credits` shows the balance.
62
+
63
+ ## The tools
64
+
65
+ **Pick the brand + product**
66
+ - `list_ad_brands` — the user's ad brands (get a `brand_id`; also carries `slug`).
67
+ - `list_brand_products { brand_id, search?, page?, page_size? }` — the brand's imported products.
68
+ Pick a `product_id` to shoot. `search` matches name / type / variant / SKU.
69
+ - `import_product { brand_id, kind, url, product_name? }` — import a product if it isn't in the
70
+ catalog yet. `kind` is `product_url` (a single product page), `shopify_store` (a store URL →
71
+ imports the catalog), or `image_url` (a direct image; requires `product_name`). Returns an import
72
+ row with an `id`; if its `status` isn't `complete`, poll `get_product_import` until it is, then
73
+ `list_brand_products` to find the new product. (File uploads aren't available over MCP — use a URL.)
74
+ - `get_product_import { import_id }` — poll an import until `status` is `complete` or `failed`.
75
+
76
+ **Generate**
77
+ - `estimate_product_photos { count, quality? }` — cost preview (per-photo + total credits). `count`
78
+ is 1, 2, 4, or 8; `quality` is `low` | `medium` | `high` (default `medium`). Reserves nothing.
79
+ - `generate_product_photos { brand_id, product_id, variant_id?, category, controls?, prompt?,
80
+ count?, quality?, reference_image_urls?, attestation_accepted? }` — **the one call that makes
81
+ photos.** `category` is `apparel` | `beauty` | `cpg` (seeds sensible scene/framing defaults).
82
+ Omit `controls` to use the category preset; pass `prompt` as free-text steering **added on top of**
83
+ the settings (it doesn't replace them). Returns a generation with an `id` **immediately** — poll
84
+ `get_product_photo_generation` until done, then read each `outputs[].final_image_url`.
85
+ **If you request a human model** (`controls.model.presence` is not `none`) you MUST pass
86
+ `attestation_accepted: true` to confirm the user has the rights for model imagery.
87
+ - `get_product_photo_generation { generation_id }` — poll until `status` is `complete`,
88
+ `partial_failure`, or `failed`. Each `outputs[]` entry has its own `status` and, once ready, a
89
+ `final_image_url`. A `flagged` output is the best attempt but wasn't billed.
90
+
91
+ **Use the results**
92
+ - `list_product_photos { brand_id, archived? }` — the brand's generated photos (`archived: false`
93
+ = active, `true` = archived).
94
+ - `approve_product_photo { output_id }` — approve a photo: links it to the product and makes it
95
+ available in the **brand kit**, so `goose-ads` can use it. **Photos are not used anywhere until
96
+ approved.**
97
+ - `archive_product_photo { output_id, reason? }` — archive a photo; archived photos are **excluded**
98
+ from ad generation.
99
+
100
+ ## Workflow — shoot a product
101
+
102
+ 1. **Load the brand context** (`brand_get_context`, or reuse what the router passed you) and
103
+ **resolve the brand + product.** `list_ad_brands` → `brand_id`. `list_brand_products` → pick a
104
+ `product_id` from the catalog you already know about. If the product genuinely isn't there,
105
+ `import_product` (poll `get_product_import`).
106
+ 2. **Quote the cost.** `estimate_product_photos { count, quality }` → tell the user credits.
107
+ 3. **Generate.** `generate_product_photos { brand_id, product_id, category, count, quality, prompt? }`.
108
+ Build `prompt` from the brand's voice/positioning you already have — don't interview the user for it.
109
+ Returns a generation `id` right away.
110
+ 4. **Poll.** `get_product_photo_generation { generation_id }` until terminal; hand back each
111
+ `final_image_url`.
112
+ 5. **Approve the keepers.** Show the results and let the user pick; `approve_product_photo` the ones
113
+ they'd publish (that's what puts them in the brand kit for ads), `archive_product_photo` the rest.
114
+
115
+ ## Rules
116
+
117
+ - **Never invent product facts.** The backend grounds the shot on the product's real images; don't
118
+ describe a product you can't see.
119
+ - **Use the brand context instead of interviewing the user.** Product, audience, voice, positioning,
120
+ logo/colors/fonts all come from `brand_get_context` / the brand kit. Ask only for the shot
121
+ category, count, quality, and model consent.
122
+ - **Ask before spending.** Quote the estimate and confirm `count` / `quality` before
123
+ `generate_product_photos` — it reserves credits.
124
+ - **Poll, don't re-submit.** A generation that's still `running` is not stuck; re-submitting
125
+ double-bills. Only a `failed` generation should be retried.
126
+ - **Model imagery needs consent.** Only set a human model when the user asks, and pass
127
+ `attestation_accepted: true`.
128
+ - **Approval is the hand-off to ads.** Remind the user that only **approved** photos reach the brand
129
+ kit / ad workflow; archived ones never do.