gooseworks 0.4.4 → 0.4.5-dev.15.facf60fb
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -11
- package/dist/agents/staging.d.ts +38 -0
- package/dist/agents/staging.d.ts.map +1 -0
- package/dist/agents/staging.js +309 -0
- package/dist/agents/staging.js.map +1 -0
- package/dist/auth/credentials.d.ts +1 -0
- package/dist/auth/credentials.d.ts.map +1 -1
- package/dist/auth/credentials.js +26 -8
- package/dist/auth/credentials.js.map +1 -1
- package/dist/auth/device-flow.d.ts +79 -0
- package/dist/auth/device-flow.d.ts.map +1 -0
- package/dist/auth/device-flow.js +411 -0
- package/dist/auth/device-flow.js.map +1 -0
- package/dist/auth/login-mode.d.ts +25 -0
- package/dist/auth/login-mode.d.ts.map +1 -0
- package/dist/auth/login-mode.js +87 -0
- package/dist/auth/login-mode.js.map +1 -0
- package/dist/auth/oauth-server.d.ts +2 -2
- package/dist/auth/oauth-server.d.ts.map +1 -1
- package/dist/auth/oauth-server.js +28 -5
- package/dist/auth/oauth-server.js.map +1 -1
- package/dist/auth/paste-login.d.ts +27 -0
- package/dist/auth/paste-login.d.ts.map +1 -0
- package/dist/auth/paste-login.js +203 -0
- package/dist/auth/paste-login.js.map +1 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +17 -8
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/formats.js +2 -2
- package/dist/commands/formats.js.map +1 -1
- package/dist/commands/graphics/publish-flow.js +1 -1
- package/dist/commands/graphics/publish-flow.js.map +1 -1
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +101 -34
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/launch.d.ts +3 -0
- package/dist/commands/launch.d.ts.map +1 -0
- package/dist/commands/launch.js +35 -0
- package/dist/commands/launch.js.map +1 -0
- package/dist/commands/login.d.ts +21 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +106 -29
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +7 -1
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/skills.d.ts +16 -0
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +52 -1
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/styles.js +2 -2
- package/dist/commands/styles.js.map +1 -1
- package/dist/commands/tool.d.ts +17 -0
- package/dist/commands/tool.d.ts.map +1 -0
- package/dist/commands/tool.js +239 -0
- package/dist/commands/tool.js.map +1 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +20 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/video-local.d.ts.map +1 -1
- package/dist/commands/video-local.js +2 -2
- package/dist/commands/video-local.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +14 -3
- package/dist/config.js.map +1 -1
- package/dist/environment.d.ts +9 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +105 -0
- package/dist/environment.js.map +1 -0
- package/dist/index.js +12 -45
- package/dist/index.js.map +1 -1
- package/dist/lib/graphics-cache.d.ts.map +1 -1
- package/dist/lib/graphics-cache.js +2 -2
- package/dist/lib/graphics-cache.js.map +1 -1
- package/dist/lib/mcp-client.d.ts +53 -0
- package/dist/lib/mcp-client.d.ts.map +1 -0
- package/dist/lib/mcp-client.js +196 -0
- package/dist/lib/mcp-client.js.map +1 -0
- package/dist/program.d.ts +2 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +56 -0
- package/dist/program.js.map +1 -0
- package/dist/skills/custom-video-skill.d.ts +1 -1
- package/dist/skills/custom-video-skill.d.ts.map +1 -1
- package/dist/skills/custom-video-skill.js +1 -1
- package/dist/skills/custom-video-skill.js.map +1 -1
- package/dist/skills/installer.d.ts.map +1 -1
- package/dist/skills/installer.js +42 -24
- package/dist/skills/installer.js.map +1 -1
- package/dist/skills/master-skill.d.ts +12 -9
- package/dist/skills/master-skill.d.ts.map +1 -1
- package/dist/skills/master-skill.js +857 -372
- package/dist/skills/master-skill.js.map +1 -1
- package/dist/skills/releases.d.ts +1 -1
- package/dist/skills/releases.d.ts.map +1 -1
- package/dist/skills/releases.js +8 -5
- package/dist/skills/releases.js.map +1 -1
- package/dist/skills/routes.js +8 -8
- package/dist/skills/routes.js.map +1 -1
- package/dist/skills/staging-content.d.ts +3 -0
- package/dist/skills/staging-content.d.ts.map +1 -0
- package/dist/skills/staging-content.js +12 -0
- package/dist/skills/staging-content.js.map +1 -0
- package/dist/utils/http.d.ts +7 -1
- package/dist/utils/http.d.ts.map +1 -1
- package/dist/utils/http.js +5 -1
- package/dist/utils/http.js.map +1 -1
- package/package.json +12 -4
- package/skills/goose-ads/SKILL.md +386 -224
- package/skills/goose-product-photos/SKILL.md +107 -58
- package/skills/goose-video/SKILL.md +300 -36
- package/skills/goose-video-local/SKILL.md +239 -31
- package/skills/gooseworks/SKILL.md +133 -26
- package/skills/make-custom-video/SKILL.md +256 -11
- package/skills/manifest.json +7 -7
- package/skills/routes.json +8 -8
- package/dist/skills/fetcher.d.ts +0 -18
- package/dist/skills/fetcher.d.ts.map +0 -1
- package/dist/skills/fetcher.js +0 -118
- package/dist/skills/fetcher.js.map +0 -1
|
@@ -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.
|
|
15
|
+
version: 2.6.1
|
|
16
16
|
author: GooseWorks
|
|
17
17
|
tags: [gooseworks, ads, remix, static-ad, brand, creative, image, analytics, meta-ads, performance]
|
|
18
18
|
---
|
|
@@ -30,6 +30,9 @@ The GooseWorks ads skill. Two jobs:
|
|
|
30
30
|
2. **Analyze ad performance** — fetch ad-analytics recipes from goose-skills on demand
|
|
31
31
|
(these are unrelated to generation; see "Analyze / intelligence" below).
|
|
32
32
|
|
|
33
|
+
It works the same in a chat app (ChatGPT, claude.ai, Cowork) and in a terminal coding agent:
|
|
34
|
+
everything except the few steps labelled terminal-only goes through the connector's tools.
|
|
35
|
+
|
|
33
36
|
## How to talk to the customer (applies to every message you send them)
|
|
34
37
|
|
|
35
38
|
The customer is a marketer or founder, not an engineer. Everything in this skill about tools,
|
|
@@ -82,39 +85,147 @@ and approval before spending. Hosted installed snapshots use the existing Skills
|
|
|
82
85
|
Skill content and a host's cached MCP tool schemas are separate: refreshing one does not refresh
|
|
83
86
|
the other. Check the actual advertised tools before using new fields.
|
|
84
87
|
|
|
85
|
-
##
|
|
88
|
+
## No GooseWorks tools in this session? Call them through the CLI
|
|
89
|
+
|
|
90
|
+
The GooseWorks tools named here (for example `account_whoami`, `brand_onboarding`,
|
|
91
|
+
`brand_read`) normally come from the GooseWorks connector. If they are not registered in this
|
|
92
|
+
session but you have a shell with the `gooseworks` CLI signed in (a cloud sandbox such as
|
|
93
|
+
ChatGPT agent, Meta AI or Grok), call the same tool from the shell:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npx gooseworks tool <name> '<arguments as a JSON object>'
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- Run `npx gooseworks tool --list` once first. It prints the server's rules for using the tools
|
|
100
|
+
and every tool name; `npx gooseworks tool <name> --schema` shows one tool's arguments.
|
|
101
|
+
- The result prints as JSON. When a tool answers with a one-line summary, the fields you act on
|
|
102
|
+
(such as `next_step` and ids) follow under "Data:". It exits 1 when the tool reports an error.
|
|
103
|
+
- Nothing renders as a widget here, so tell the user the result in plain words.
|
|
104
|
+
- The same rules apply as over the connector: state the credit total and get the user's yes
|
|
105
|
+
before paid work.
|
|
106
|
+
- If it says you are not logged in, run `npx gooseworks login --device --no-wait`, show the
|
|
107
|
+
user the link and code it prints, and run `npx gooseworks login --device` once they approve.
|
|
108
|
+
|
|
109
|
+
When the tools are registered in this session, call them directly instead.
|
|
110
|
+
|
|
111
|
+
## Prerequisite — the GooseWorks connector tools
|
|
86
112
|
|
|
87
|
-
Everything
|
|
88
|
-
|
|
89
|
-
|
|
113
|
+
Everything below runs through the GooseWorks connector's tools, by the names this skill uses.
|
|
114
|
+
Match on the tool name: a coding agent may show a server prefix (for example
|
|
115
|
+
`mcp__gooseworks__ads_generate`), a chat app may not. There is no HTTP or file fallback: the REST ad
|
|
116
|
+
endpoints are session-cookie-only and reject your token.
|
|
117
|
+
|
|
118
|
+
If a tool named here is missing, the GooseWorks connection or its tool list is stale: ask the
|
|
119
|
+
user to reconnect or refresh GooseWorks in their app's connector settings. Installing or
|
|
120
|
+
updating the `gooseworks` CLI never fixes a missing connector tool, so never send a chat-app
|
|
121
|
+
user to a terminal for it. Only a terminal coding agent (Claude Code, Codex, Cursor) that has no
|
|
122
|
+
GooseWorks tools at all connects them, in that terminal, with `gooseworks install --mcp` plus
|
|
123
|
+
`--claude`, `--codex` or `--cursor`, then a restart.
|
|
124
|
+
|
|
125
|
+
Older notes or skill copies may name tools the connector no longer lists. Use the tool this skill
|
|
126
|
+
names instead; `catalog_fetch { type: "skill", slug: "gooseworks-guide" }` maps every old name.
|
|
90
127
|
|
|
91
128
|
## Start from the brand context — don't re-ask what it already answers
|
|
92
129
|
|
|
93
|
-
If the `gooseworks` router handed you brand context, USE
|
|
94
|
-
`brand_read { brand_id, sections: ["summary","kit","products","learnings"] }`
|
|
95
|
-
|
|
130
|
+
If the `gooseworks` router handed you brand context and an evidence brief, USE THEM. If you were
|
|
131
|
+
invoked directly, call `brand_read { brand_id, sections: ["summary","kit","products","learnings"] }`
|
|
132
|
+
first, then search the Brand Brain (next section). Together they answer most of what the flows
|
|
133
|
+
below would otherwise ask the user:
|
|
96
134
|
|
|
97
|
-
- **Which product to feature** → `products
|
|
135
|
+
- **Which product to feature** → `products.items`. Recommend one real catalog entry; never guess a
|
|
98
136
|
product name and never ask the user to list their products.
|
|
99
137
|
- **The vibe / tone of the copy** → the brand's **voice**. Use it; don't ask "what tone?".
|
|
100
138
|
- **Who the ad is for** → the brand's **audience**. Don't ask "who's the target?".
|
|
101
|
-
- **The angle
|
|
139
|
+
- **The angle and offer framing** → **positioning** and value props, sharpened by the Brain:
|
|
140
|
+
lead with an angle past approved creatives proved, and drop anything a saved rule forbids.
|
|
141
|
+
- **What to claim** → only claim-grade proof: a search result with `approved_ad_claim: true`, the
|
|
142
|
+
kit's approved claims, or its proof points as written. Other Kit text, documents and
|
|
143
|
+
performance numbers are context.
|
|
102
144
|
- **Logo, colors, fonts** → owned by the backend research pass. **Never re-derive them.**
|
|
103
145
|
- **Whether the facts are trustworthy yet** → **research status**. If it isn't complete, say so in
|
|
104
146
|
one line and continue; the batch queues and runs when research finishes.
|
|
105
147
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
148
|
+
Don't ask for the angle, product or tone: recommend them with a one-line reason. Ask only for a
|
|
149
|
+
decision the brand read and the Brain search cannot settle (for example an offer or season the
|
|
150
|
+
user hasn't mentioned) and for anything the user must consent to (rights, spend).
|
|
151
|
+
|
|
152
|
+
## Search the Brand Brain, then propose — before any creative choice or question
|
|
153
|
+
|
|
154
|
+
The Brand Kit is a summary. The brand's saved knowledge (its Brain) holds what the Kit does not:
|
|
155
|
+
rules from past feedback, approved and rejected creatives, customer evidence, approved claims,
|
|
156
|
+
reports and documents. **After `brand_read`, and before you choose an angle, claim, hook,
|
|
157
|
+
product emphasis, format or source ad — and before you ask the user for any brand fact — call
|
|
158
|
+
`knowledge_search { brand_id, query }`** when it is registered. It is free and read-only: no
|
|
159
|
+
approval, no announcement to the customer and no questionnaire.
|
|
160
|
+
|
|
161
|
+
1. **Search for this task, not the whole Brain.** Run one short query (under 500 characters) in
|
|
162
|
+
the user's own words plus the product (for example "ads for <product>: what worked, what to
|
|
163
|
+
avoid") and one with
|
|
164
|
+
`source_types: ["evidence", "claim", "learning", "creative", "document"]` for proof and past
|
|
165
|
+
creative results. Add a query only for a specific open question. Reuse results from this run.
|
|
166
|
+
2. **Keep these states distinct** and record which one each query returned:
|
|
167
|
+
|
|
168
|
+
| Result | Means | Do |
|
|
169
|
+
| --- | --- | --- |
|
|
170
|
+
| `status: "ok"` with matches | Saved knowledge exists | Use it; keep each fact's citation in your working brief |
|
|
171
|
+
| `status: "empty"` | Nothing saved matches this query | Say "no saved evidence for <topic>", never "the brand has no proof" |
|
|
172
|
+
| `refresh_required` | The results shown are current; some changed sources were left out | Use them; search again shortly for anything missing |
|
|
173
|
+
| `building` | The index is not ready | Retry once shortly, then continue with the gap stated |
|
|
174
|
+
| An error, or the tool is not registered | Retrieval failed or is unavailable | Retry an error once, then continue from `brand_read` and treat evidence as unchecked |
|
|
175
|
+
| An empty Kit field | Only that field is blank | Not a search result: still search before asking |
|
|
176
|
+
|
|
177
|
+
3. **Let the findings change the plan.** A `dont`/`must` learning or a rejected creative rules
|
|
178
|
+
options out; an approved or well-rated past creative is a proven angle to lead with; a report
|
|
179
|
+
shows what worked. Claim-grade proof is a result with `approved_ad_claim: true` or the kit's
|
|
180
|
+
`approvedClaims` within their stated applicability; the kit's `proofPoints` are what the
|
|
181
|
+
backend allows an ad to state, used as written. Other Kit text, documents and performance
|
|
182
|
+
numbers are context, never public claims. Never invent an offer, price or result. Judge
|
|
183
|
+
relevance and skip results about another product or business.
|
|
184
|
+
4. **Propose; don't interview.** Lead with one recommended direction (angle, product, source or
|
|
185
|
+
format) and a one-line reason naming what you found, with up to two alternatives. When several
|
|
186
|
+
directions fit (two audiences, products or campaigns), pick the one the evidence favours, such
|
|
187
|
+
as an active campaign or approved past creatives, and name the other as an alternative instead
|
|
188
|
+
of asking. Ask only for a decision the brand read and the search cannot settle, or for spend
|
|
189
|
+
approval. Never ask the user for a fact the Brain already answered.
|
|
190
|
+
Keep directions already chosen (the user's words, a project's creative brief, handed-off
|
|
191
|
+
defaults, approved plans): search only to fill empty fields and to apply saved rules. When a
|
|
192
|
+
returned card or picker presents the choice, your recommendation is its one acknowledgement
|
|
193
|
+
line and the saved plan's defaults, not a separate list.
|
|
194
|
+
5. **Carry an evidence brief** into the routed skill, writer or plan: findings with citations,
|
|
195
|
+
the state of each query, what it ruled out and the open gaps. Tell the customer the findings
|
|
196
|
+
in plain words; the citations stay in the brief.
|
|
197
|
+
|
|
198
|
+
On an approved resume, keep the saved brief and evidence; search again only for a new decision.
|
|
199
|
+
|
|
200
|
+
## Credits — state the total, then get a yes
|
|
201
|
+
|
|
202
|
+
- One token authenticates the GooseWorks tools and resolves your org; never print it. The
|
|
203
|
+
generation tools need no `target`.
|
|
204
|
+
- **Nothing paid runs without the user's explicit yes in this chat, given after you state the
|
|
205
|
+
credit total.** The paid calls are `ads_generate` (without `dry_run`), every
|
|
206
|
+
`ads_creative_edit` action, `ads_approval_decide` with `decision: "approve"`, and every
|
|
207
|
+
`data_call_provider` / `data_post_provider` call (the data calls an analysis recipe or brand
|
|
208
|
+
research makes; `gooseworks call` in a terminal). Reads, dry runs, `ads_creative_update` and
|
|
209
|
+
`request_campaign_generation` (it only composes plans) are free.
|
|
210
|
+
- The balance is `credits.available_credits` from `account_whoami`; a dry-run `estimate` also
|
|
211
|
+
carries `available_credits`.
|
|
212
|
+
- The backend reserves the quoted credits when a paid call starts and bills only the images
|
|
213
|
+
that complete. A rejection with `insufficient_credits` means the wallet is short: tell the
|
|
214
|
+
user the total and their balance in plain words, offer fewer images or a top-up, and stop.
|
|
215
|
+
Never retry blindly.
|
|
216
|
+
|
|
217
|
+
How to get the total for each paid call:
|
|
218
|
+
|
|
219
|
+
| Paid call | Credit total to state |
|
|
220
|
+
| --- | --- |
|
|
221
|
+
| `ads_generate` with `source.template_ids` | The same call with `dry_run: true` returns `estimate`: `total_credits` for `images` images (plus `credits_per_image`, `rates` and `unknown_template_ids`). |
|
|
222
|
+
| `ads_generate` with `source.community_ad_ids` or `source.creative_ids` | No dry run (it returns `not_available`; these are priced when submitted). Pass `quality` explicitly (the user's choice, else `high`, the app default) and quote the images (each source's `variants` × its `ratios`) × `estimate.rates.<quality>` as the most it will cost (an engine without quality tiers costs the lower `rates.low`). `rates` comes with any template dry run; with no template at hand, dry-run one Surprise-me pick. |
|
|
223
|
+
| `ads_creative_edit` `animate` | The same call with `dry_run: true` returns its `estimate`. |
|
|
224
|
+
| `ads_creative_edit` `regenerate` / `precision_edit` | No dry run. Pass `quality` in the action's payload (without it `precision_edit` copies the source render's tier) and quote the images × `estimate.rates.<quality>` as the most it will cost: `regenerate` with `mode: "variation"` makes one per ratio (omitted `ratios` make three); `edit`, `exact` and `precision_edit` make one. |
|
|
225
|
+
| `ads_creative_edit` `resize` | No dry run and no quality setting: it renders at the server's default tier (`high` today). Quote one image per placement × the larger of `estimate.rates.high` and `credits_per_image` from a template dry run without `quality` (that dry run uses the server's default tier) as the most it will cost. `platforms` expands to every placement: meta 4, google 4, tiktok 2, linkedin 3, reddit 3, x 2; pass `targets` for fewer. |
|
|
226
|
+
| `ads_creative_edit` `layerize` | No dry run. It holds about 80 credits while it runs and charges the actual cost of the split (usually less). Say so and get the yes before sending `layerize: { confirmed: true }`. |
|
|
227
|
+
| `ads_approval_decide` approve | The `credits` that `request_campaign_generation` and `ads_creative_read { brand_id, view: "approvals", batch_id }` return for those plans. |
|
|
228
|
+
| `data_call_provider` / `data_post_provider` | The calls a recipe will make × their price: a ScrapeCreators call costs 1 credit today, and each result reports what it charged. A fal or ElevenLabs POST quotes free with the same call plus `query: { quote_only: true }`. State the rough total for the whole recipe once, before its first paid call. |
|
|
118
229
|
|
|
119
230
|
## Live MCP contract — inspect it before asking
|
|
120
231
|
|
|
@@ -127,77 +238,82 @@ Before each tool call:
|
|
|
127
238
|
2. Fill required inputs already known from the Brand Kit, selected source, or conversation.
|
|
128
239
|
3. Ask the user only for required inputs that cannot be inferred and for choices that materially
|
|
129
240
|
change the result. Do not turn every optional field into a questionnaire.
|
|
130
|
-
4. Omit unspecified optional settings so the backend applies its current app defaults.
|
|
241
|
+
4. Omit unspecified optional settings so the backend applies its current app defaults. Two
|
|
242
|
+
exceptions: always pass `ratios` on every source (omitted ratios make three images per
|
|
243
|
+
variant), and keep the same `quality` setting in the quote and the paid call (both omitted
|
|
244
|
+
for a template dry run, or the explicit `quality` the credits table asks for).
|
|
131
245
|
5. If the live schema conflicts with this workflow, follow the live schema and report the drift
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
## The
|
|
135
|
-
|
|
136
|
-
- `
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
`
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
`
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
`
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
246
|
+
(see "Report problems" in the rules).
|
|
247
|
+
|
|
248
|
+
## The ad tools
|
|
249
|
+
|
|
250
|
+
- `ads_generate` — **the one call that makes ads.** Needs `brand_id` and exactly one `source`:
|
|
251
|
+
`template_ids: [{ template_id, variants?, ratios }]` (the brand's own templates, Surprise-me
|
|
252
|
+
picks, Community rows whose `item_type` is `template`), `community_ad_ids: [{ community_id,
|
|
253
|
+
variants?, ratios }]` (Community rows whose `item_type` is `creative`; the backend snapshots
|
|
254
|
+
them), or `creative_ids: [{ project_id, render_id?, variants?, ratios }]` (remix the user's own
|
|
255
|
+
finished ads). Optional: `product_name` (a real product), `prompt` (a short steering note),
|
|
256
|
+
`reference_image_urls`, `quality`. `dry_run: true` quotes template sources and reserves
|
|
257
|
+
nothing. A real call GENERATES at once and returns `{ job_id }` (`kind: "ads_batch"`) and the
|
|
258
|
+
`batch`, which already carries its `links` (below). If the
|
|
259
|
+
brand's research is still running the batch is `queued` and starts on its own when research
|
|
260
|
+
finishes: tell the user it'll appear shortly, don't error.
|
|
261
|
+
- `job_get { job_id, kind: "ads_batch" }` — poll a batch. `status` is `queued`, `running`,
|
|
262
|
+
`complete`, `partial_failure` or `failed`; `progress` counts `completed` / `failed` /
|
|
263
|
+
`pending` images. `result.creatives[]` carry each creative's `renders[]` (`id`, `status`,
|
|
264
|
+
`ratio`, `output_url`, `age_seconds` since queued, `elapsed_seconds` generating). A creative
|
|
265
|
+
is done when its `pending` is 0, NOT when `current_render_url` is set (during a regenerate it
|
|
266
|
+
still points at the prior image). A render only failed when its `status` is `"failed"`: a slow
|
|
267
|
+
render is healthy, and re-submitting it double-bills. `result.links` holds the app links you end
|
|
268
|
+
the run with: `brand_url` (the brand's page, with all its ads) and `creative_links: [{
|
|
269
|
+
project_id, app_url }]` (each creative's page). `brand_url` is `null` and the list empty only
|
|
270
|
+
if the link lookup failed; the ads are still made. A server older than this skill returns no
|
|
271
|
+
`links` (and no `app_url` / `brand_url` on creative reads): then hand back the images
|
|
272
|
+
only and don't mention links.
|
|
273
|
+
- `ads_creative_read { brand_id }` — the brand's generated creatives, newest first (filter with
|
|
274
|
+
`batch_id`, `tags`, `approved_only`); each row has its `app_url` and the list has the
|
|
275
|
+
`brand_url`. `creative_id` reads one with its `renders`, plus `creative.app_url` and
|
|
276
|
+
`creative.brand_url`.
|
|
277
|
+
- `ads_template_read` — find or inspect a source (see "Picking source ads"). `template_id` reads one.
|
|
278
|
+
- `ads_creative_edit { brand_id, creative_id, action }` — every paid edit of one creative, one
|
|
279
|
+
`action` per call: `regenerate` (`regenerate: { mode: "variation" }` for another take;
|
|
280
|
+
`mode: "edit"` or `"exact"` with `source_render_id` and `prompt`), `precision_edit` (`source_render_id`
|
|
281
|
+
plus a `note` or region `annotations`), `resize` (`source_render_id` plus `platforms` or
|
|
282
|
+
`targets`), `layerize` (`confirmed: true`), `animate` (`source_image_url`; only when
|
|
283
|
+
`account_whoami` shows the Animate Images feature; for the full flow fetch the `animate-image`
|
|
284
|
+
skill). Returns `{ job_id }`: poll `job_get` with `kind: "ads_batch"` (regenerate, resize,
|
|
285
|
+
precision_edit) or `kind: "animate"`; for layerize read
|
|
286
|
+
`ads_creative_read { brand_id, creative_id, include: ["layers"] }`. While the brand's research is
|
|
287
|
+
still running, `regenerate`, `resize` and `precision_edit` refuse with
|
|
288
|
+
`brand_research_in_progress`: tell the user and try again when it finishes.
|
|
289
|
+
- `ads_creative_update { brand_id, creative_id, patch }` — free. `patch.feedback: { render_id, rating:
|
|
290
|
+
"happy" | "neutral" | "sad", comment?, reasons? }` records the user's reaction (it feeds the
|
|
291
|
+
quality loop); `patch.tags` replaces the creative's tags.
|
|
292
|
+
- `ads_template_create { brand_id, source, rights_attested? }` — register the user's own image as a
|
|
293
|
+
private source template (see "Upload" below).
|
|
294
|
+
|
|
295
|
+
### Campaigns — the only plan-and-approve path
|
|
296
|
+
|
|
297
|
+
`ads_generate` has no plan step: the dry-run quote and the user's yes are the checkpoint, and a
|
|
298
|
+
submit generates at once. Never promise a review step before the images render. Plans exist only
|
|
299
|
+
for a campaign's concepts (planned with `campaign_read`, `campaign_upsert` and
|
|
300
|
+
`add_campaign_concept`; follow the connector guide for those):
|
|
301
|
+
|
|
302
|
+
1. `request_campaign_generation { campaign_id }` composes the plans for the concepts with no
|
|
303
|
+
creatives yet (`concept_ids` re-runs chosen ones; `count` only for a number the user named). It
|
|
304
|
+
spends nothing and returns `batch_ids` and `credits`. Never say generation has started.
|
|
305
|
+
2. Tell the user what will be made and the credit total, and wait for their explicit yes in this
|
|
306
|
+
chat. Never send them to a button in the app; a campaign link is only a place to look.
|
|
307
|
+
3. Read `ads_creative_read { brand_id, view: "approvals", batch_id }` until that batch's plans are
|
|
308
|
+
`awaiting_approval` and none is `composing`. If its credit total differs from what they
|
|
309
|
+
agreed to, state the new total and ask again.
|
|
310
|
+
4. `ads_approval_decide { brand_id, decision: "approve", batch_id, user_quote: "<their exact words>" }`
|
|
311
|
+
for each batch, then poll the batch ids it returns with `job_get` (`kind: "ads_batch"`) and
|
|
312
|
+
end with each batch's `result.links`, as in step 7 of the workflow below.
|
|
313
|
+
|
|
314
|
+
Before approving, a steer is free: `ads_approval_decide { brand_id, creative_id, decision: "revise",
|
|
315
|
+
revise: { message } }` recomposes that plan (read the approvals view again), and
|
|
316
|
+
`decision: "edit_plan"` patches it directly. Editing the campaign in between discards these plans.
|
|
201
317
|
|
|
202
318
|
## Keep the brand kit in sync — reconcile, then save or propose
|
|
203
319
|
|
|
@@ -228,8 +344,9 @@ able to flow back into the kit — with their ok — instead of being lost.
|
|
|
228
344
|
## Save durable brand answers, then verify them
|
|
229
345
|
|
|
230
346
|
Read the selected brand with `brand_read { brand_id, sections: ["summary", "kit", "products", "learnings", "onboarding"] }`
|
|
231
|
-
(
|
|
232
|
-
research and your own hypotheses distinct. Reuse
|
|
347
|
+
(older clients: `brand_get_context` with the same sections, only when that tool is advertised).
|
|
348
|
+
Keep founder answers, user corrections, research and your own hypotheses distinct. Reuse
|
|
349
|
+
matching saved answers; ask only about gaps.
|
|
233
350
|
|
|
234
351
|
When the user asks to remember a rule, answers a brand interview, or explicitly corrects a
|
|
235
352
|
standing fact, save that answer in the same turn. The capture request authorizes those answers;
|
|
@@ -283,159 +400,206 @@ routed skill; claims still pass their own safety gate.
|
|
|
283
400
|
## Picking source ads — use approved sources, not the retired catalog
|
|
284
401
|
|
|
285
402
|
When the user wants to make ads but has NOT named a specific template (id/slug/Community
|
|
286
|
-
ad/upload), do NOT silently browse the raw catalog and hand-pick for them. Instead
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
1. **
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
2. **
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
- **
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
`
|
|
310
|
-
|
|
311
|
-
|
|
403
|
+
ad/upload), do NOT silently browse the raw catalog and hand-pick for them. Instead send **one
|
|
404
|
+
proposal** — it mirrors the web app and keeps the human in the loop without an interview:
|
|
405
|
+
|
|
406
|
+
1. **Propose the direction yourself.** From the brand read and the evidence brief, recommend the
|
|
407
|
+
product, angle/offer and tone, with a one-line reason naming what the Brain showed (a past
|
|
408
|
+
approved angle, a rule it respects). **Do NOT ask what kind of ads they want, which product,
|
|
409
|
+
or the vibe.** Keep any direction the user already gave. This shapes both the source choice
|
|
410
|
+
and your steering `prompt`.
|
|
411
|
+
2. **Recommend a source in the same message: their own ads, Community, upload, or "Surprise me".**
|
|
412
|
+
Default to their own approved ads when suitable ones exist, otherwise Surprise-me picks for
|
|
413
|
+
the brand; list the other paths as one-line alternatives. You may resolve the picks and run the
|
|
414
|
+
free dry run first so the proposal already carries the credit total. Show a list of sources as
|
|
415
|
+
one table with every row and its image link, and mark your one suggestion.
|
|
416
|
+
- **Their own ads** → `ads_template_read { brand_id, mode: "mine", filters: { relationship: "self" } }`
|
|
417
|
+
and let them choose. `mode: "competitor"` rows are research and inspiration, never proof that
|
|
418
|
+
the user owns the ad.
|
|
419
|
+
- **Community** → `ads_template_read { brand_id, mode: "query", query: "<the angle and look>" }`
|
|
420
|
+
ranks Community ads by meaning; each row's `sourceId` (with `title` and `thumbnailUrl`) goes
|
|
421
|
+
in `source.community_ad_ids[].community_id`. To browse the feed instead, use `mode:
|
|
422
|
+
"community"`: its rows carry `item_type`, and a `template` id goes in `source.template_ids`,
|
|
423
|
+
a `creative` id in `source.community_ad_ids`. The backend snapshots a Community creative
|
|
424
|
+
itself; there is no separate step. Let the user choose.
|
|
425
|
+
- **Upload** → the user's own image becomes a private template: `media_upload` it (`scope:
|
|
426
|
+
"brand"`, `scope_id: <brand_id>`, `kind: "image"`; in a chat app `source.type: "bytes"`), then
|
|
427
|
+
`ads_template_create { brand_id, source: { type: "media", media_id } }` (or `source: { type:
|
|
428
|
+
"url", url }` for a public image). Set `rights_attested: true` only after the user explicitly
|
|
429
|
+
confirms they own it; this is the ownership/rights input. Never claim rights for a competitor
|
|
430
|
+
ad or an image found online.
|
|
431
|
+
- **Surprise me** (they want you/the app to pick) → `ads_template_read { brand_id, mode:
|
|
432
|
+
"surprise", count: 4 }` picks remixable Community ads for the brand, shuffled so picks stay
|
|
433
|
+
fresh. Every pick is a template: show them, and generate
|
|
434
|
+
from the ones the user keeps (or from all of them if they said "just make them").
|
|
435
|
+
- **Browse in the app** → the one link you build yourself: `<app>/create?brand=<brand-slug>&cli=true`,
|
|
436
|
+
with the active brand's slug, where `<app>` is the origin of a `brand_url` a tool returned
|
|
437
|
+
(the part before `/?brand=`: `https://ads-staging.gooseworks.ai/?brand=acme` gives
|
|
438
|
+
`https://ads-staging.gooseworks.ai`), so it opens on the user's own environment. Reuse a
|
|
439
|
+
`brand_url` you already have from this run, or read one cheaply with
|
|
440
|
+
`ads_creative_read { brand_id, limit: 1 }`; if none comes back, use `https://make.gooseworks.ai`.
|
|
441
|
+
In this mode the app shows a copyable remix prompt at the bottom (dismissable / switchable
|
|
442
|
+
back to the UI composer). They browse their own and Community sources and copy the prompt.
|
|
312
443
|
3. **Close the loop.** When the user **pastes back the copyable remix prompt** from the app
|
|
313
444
|
(it names the brand + the templates they chose), THAT is your cue to generate: resolve the
|
|
314
|
-
named source(s)
|
|
445
|
+
named source(s) with `ads_template_read`, quote, and collect only the unresolved required inputs.
|
|
315
446
|
|
|
316
447
|
If the user already named an owned source (id/slug), a Community ad, or an upload, skip the source
|
|
317
448
|
choice. Competitor ads may inform the angle or structure, but describe them as inspiration, never
|
|
318
|
-
claim ownership, and never attest rights for the user.
|
|
449
|
+
claim ownership, and never attest rights for the user. Never use the retired curated third-party
|
|
450
|
+
catalog.
|
|
319
451
|
|
|
320
452
|
## Workflow — make ads from a template
|
|
321
453
|
|
|
322
|
-
1. **Resolve the brand
|
|
323
|
-
selected brand
|
|
324
|
-
|
|
325
|
-
research finishes) — just tell the user. Use the
|
|
326
|
-
`products
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
454
|
+
1. **Resolve the brand and its evidence.** List brands with `brand_read` (no `brand_id`), then
|
|
455
|
+
read the selected brand's summary, kit, products and
|
|
456
|
+
learnings and search the Brand Brain as above. If research isn't `complete`, you can still
|
|
457
|
+
submit (the batch queues and runs when research finishes) — just tell the user. Use the read to
|
|
458
|
+
pick `product_name` (a real entry from `products.items`, not a guess) and, if the user supplied
|
|
459
|
+
product photos, `reference_image_urls` (public URLs; `media_upload` a local file first).
|
|
460
|
+
2. **Pick the source ad(s) via the proposal above.** Read each chosen template with
|
|
461
|
+
`ads_template_read { brand_id, template_id }`. Community creatives (a query row's `sourceId`,
|
|
462
|
+
a feed row with `item_type: "creative"`) and the user's own finished ads go straight into
|
|
463
|
+
`source.community_ad_ids` / `source.creative_ids`.
|
|
331
464
|
3. **(Optional) Craft the steering prompt.** The `prompt` is OPTIONAL — this is where the skill
|
|
332
465
|
adds value: turn the user's intent (from step 1) into a concise steering note (e.g. tone,
|
|
333
466
|
season, emphasis). Don't over-specify; the backend pipeline + brand kit handle palette, fonts,
|
|
334
467
|
product swap.
|
|
335
|
-
4. **Quote the cost.**
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
the
|
|
339
|
-
|
|
340
|
-
`
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
render
|
|
344
|
-
|
|
345
|
-
|
|
468
|
+
4. **Quote the cost.** Call `ads_generate` with the exact arguments you will submit plus
|
|
469
|
+
`dry_run: true` (template sources), or work out the total as in the credits table. Drop or
|
|
470
|
+
replace anything in `unknown_template_ids`. State the image count and credit total in one line
|
|
471
|
+
and wait for the user's explicit yes.
|
|
472
|
+
5. **Submit ONE batch** after that yes: the same `ads_generate` call without `dry_run`. Keep the
|
|
473
|
+
returned `job_id`.
|
|
474
|
+
6. **Poll until done.** `job_get { job_id, kind: "ads_batch" }` every ~20-30s until every creative's
|
|
475
|
+
`pending` is 0. Most images finish in a few minutes; text-heavy templates and `quality: high`
|
|
476
|
+
take longer. Read each render's `elapsed_seconds` rather than guessing; do NOT re-submit
|
|
477
|
+
thinking it stalled (that double-bills). If `ads_generate` refused with
|
|
478
|
+
`brand_research_required`, the brand was never researched: run "Brand research" below, then
|
|
479
|
+
quote and submit again.
|
|
480
|
+
7. **Hand back the ads with their links.** Show every finished image (`renders[].output_url` of
|
|
481
|
+
completed renders) and say in one line what failed, if anything. Then give the links from the
|
|
482
|
+
last `job_get`'s `result.links`, copied verbatim: the `brand_url` where all the brand's
|
|
483
|
+
ads are, and the `app_url` of each creative with a finished image (match
|
|
484
|
+
`creative_links[].project_id` to `result.creatives[].id`; skip a creative whose renders all
|
|
485
|
+
failed). For a big batch (more than about 8 creatives) give the `brand_url` and offer the
|
|
486
|
+
rest. Skip a missing or `null` link; never build an app URL yourself. Never end on just
|
|
487
|
+
"done" or a file path.
|
|
346
488
|
|
|
347
489
|
## Workflow — edit an existing ad
|
|
348
490
|
|
|
349
|
-
User wants to tweak a creative they already made →
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
491
|
+
User wants to tweak a creative they already made → `ads_creative_edit`. Read the creative first
|
|
492
|
+
(`ads_creative_read { brand_id, creative_id }`) to get the render they mean. Infer the action
|
|
493
|
+
from their request: another take (`regenerate`, `mode: "variation"`), a targeted change
|
|
494
|
+
(`regenerate` with `mode: "edit"`, or `precision_edit` when they point at a region), an exact
|
|
495
|
+
instructed change (`mode: "exact"`), new placements (`resize`), editable layers (`layerize`) or a
|
|
496
|
+
short video (`animate`). Ask only for a required source or instruction that is still missing,
|
|
497
|
+
state the credit total from the credits table, get the yes, submit, poll as above, and hand back
|
|
498
|
+
the new images with their links: `result.links` from `job_get` for `regenerate`, `resize` and
|
|
499
|
+
`precision_edit`; `creative.app_url` and `creative.brand_url` from `ads_creative_read { brand_id,
|
|
500
|
+
creative_id }` for `animate` and `layerize`.
|
|
353
501
|
|
|
354
502
|
## Brand research
|
|
355
503
|
|
|
356
|
-
Prefer the backend's result
|
|
357
|
-
`complete`, REUSE it — never re-research.
|
|
504
|
+
Prefer the backend's result. `brand_read { brand_id, sections: ["summary"] }`: if
|
|
505
|
+
`research_status` is `complete`, REUSE it — never re-research. Proof points and the full product
|
|
506
|
+
catalog keep arriving until `enrichment.settled` is true.
|
|
358
507
|
|
|
359
508
|
**The split — backend owns visuals, you own the qualitative depth:**
|
|
360
509
|
|
|
361
|
-
- **Backend LIGHT pass (automatic).**
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
every fact on the fetched site** — if the site can't be read, say so and ask the user; never
|
|
510
|
+
- **Backend LIGHT pass (automatic).** A new brand comes from the connector's setup flow
|
|
511
|
+
(`brand_onboarding { action: "status" }`, then its `next_step`). For an additional brand, check
|
|
512
|
+
it is absent (`brand_read` with no `brand_id`), then `brand_create { name, website_url }` (free).
|
|
513
|
+
The website starts the same backend research the web app uses: it resolves the
|
|
514
|
+
**authoritative logo, colors, and fonts** plus a baseline kit and returns `research_job_id`; poll
|
|
515
|
+
`job_get { job_id: research_job_id, kind: "brand_research" }` (or `brand_read`) until
|
|
516
|
+
`research_status` is `complete` — usually under a minute. If it returns `reused_existing: true`,
|
|
517
|
+
tell the user and use that brand. You can't reproduce those visual signals, so **never
|
|
518
|
+
re-derive logo/colors/fonts.**
|
|
519
|
+
- **Your DEEP pass (optional).** You add the qualitative depth the light pass leaves thin —
|
|
520
|
+
positioning, audience, voice, brand type, value props, proof points, products — grounded on
|
|
521
|
+
the actual site.
|
|
522
|
+
|
|
523
|
+
**Deep research flow:**
|
|
524
|
+
|
|
525
|
+
1. Fetch the `brand-research` skill (`catalog_fetch { type: "skill", slug: "brand-research" }` in a
|
|
526
|
+
chat app; `gooseworks fetch brand-research` in a terminal) and follow its phases. Its data
|
|
527
|
+
calls are paid: state the credit total (credits table) and get the yes first. **Ground every
|
|
528
|
+
fact on the brand's own site** — if the site can't be read, say so and ask the user; never
|
|
381
529
|
guess a category from the brand name alone.
|
|
382
|
-
|
|
383
|
-
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
530
|
+
2. **Save what you improved.**
|
|
531
|
+
- **As proposals (the default):** `brand_update { brand_id,
|
|
532
|
+
knowledge_intent: "agent_proposal", rationale, patch: { knowledge: { positioning?, audience?,
|
|
533
|
+
voice?, brandType?, tagline?, valueProps? } } }`. They stay pending until the user accepts
|
|
534
|
+
them in the app.
|
|
535
|
+
- **As the full research pack (only when you wrote all of it; in a chat app, only for a brand
|
|
536
|
+
with no website, see below):** with `file_write`, under
|
|
537
|
+
`agent-config/brands/<slug>/brand-research/`, write all four docs `brand-summary.md`,
|
|
538
|
+
`visual-identity.md`, `audience.md` and `competitors.md` (real content, at least a few
|
|
539
|
+
sentences each) plus `kit-patch.json`: `{ positioning?, audience?, voice?, brandType?,
|
|
540
|
+
tagline?, valueProps?: string[], proofPoints?: [{ text, source_url }], products?: [{ name,
|
|
541
|
+
description?, link?, pricing?, imageUrls?: string[] }] }` with only the fields you improved;
|
|
542
|
+
every proof point copied word for word from the brand's own page at `source_url`; product
|
|
543
|
+
images only from URLs already in GooseWorks storage; **no logo / colors / fonts**. Then
|
|
544
|
+
`brand_update { brand_id, patch: { finalize_research: true } }` merges it NON-CLOBBERINGLY
|
|
545
|
+
(never over the backend's visuals or a user edit). **If any of the four docs is missing or
|
|
546
|
+
nearly empty, finalize fails and marks the brand's research as failed**, so never finalize a
|
|
547
|
+
partial pack.
|
|
548
|
+
3. **Verify:** `brand_read` again (`summary`, `kit`) and confirm what you saved before generating.
|
|
549
|
+
|
|
550
|
+
**If the brand has NO website**, the backend light pass can't run and generation goes ahead with
|
|
551
|
+
an empty kit, so ads come out off-brand. Do the deep pass from what the user tells you and save the
|
|
552
|
+
full research pack, so the brand has a kit and a record to debug a wrong run (this is how a bad
|
|
553
|
+
classification, e.g. mislabelling a SaaS as a "drink company", used to vanish).
|
|
400
554
|
|
|
401
555
|
## Analyze / intelligence (fetched recipes — NOT generation)
|
|
402
556
|
|
|
403
|
-
These are analysis recipes you fetch
|
|
404
|
-
|
|
405
|
-
|
|
557
|
+
These are analysis recipes you fetch and follow; they don't use the generation tools, but their
|
|
558
|
+
data calls are paid (credits table: state the total and get the yes before the first one). Fetch with `catalog_fetch { type: "skill", slug: "<slug>" }` in a chat app or
|
|
559
|
+
`gooseworks fetch <slug>` in a terminal. Pick the closest match; if unsure, search first with
|
|
560
|
+
`catalog_search { type: "skill", query: "<what the user wants>" }` (terminal:
|
|
561
|
+
`gooseworks search "<what the user wants>"`):
|
|
406
562
|
- **Campaign performance diagnosis** ("why is my Meta/Google campaign underperforming",
|
|
407
|
-
creative fatigue, learning phase, pacing, auction overlap) → `
|
|
563
|
+
creative fatigue, learning phase, pacing, auction overlap) → `meta-ads-analyzer`
|
|
408
564
|
(or `ad-campaign-analyzer` for cross-platform).
|
|
409
565
|
- **Lead/CAC quality** ("are these ads driving qualified leads", true CAC vs vanity CPA,
|
|
410
|
-
Scale/Keep/Investigate/Cut) → `
|
|
566
|
+
Scale/Keep/Investigate/Cut) → `ad-lead-quality-analyzer`.
|
|
411
567
|
- **Competitor ad intelligence** ("what ads are competitors running") →
|
|
412
|
-
`
|
|
413
|
-
|
|
414
|
-
- **
|
|
415
|
-
`gooseworks fetch trending-ad-hook-spotter`.
|
|
416
|
-
- **Policy / landing-page checks** → `gooseworks fetch meta-ad-policy-checker` /
|
|
417
|
-
`gooseworks fetch ad-to-landing-page-auditor`.
|
|
568
|
+
`competitor-ad-intelligence` (Meta Ad Library: `meta-ad-scraper`; Google: `google-ad-scraper`).
|
|
569
|
+
- **Creative ideation** (ad angles, winning hooks) → `ad-angle-miner` / `trending-ad-hook-spotter`.
|
|
570
|
+
- **Policy / landing-page checks** → `meta-ad-policy-checker` / `ad-to-landing-page-auditor`.
|
|
418
571
|
|
|
419
|
-
|
|
420
|
-
|
|
572
|
+
A recipe's provider calls go through the connector in a chat app (`data_call_provider` for GET,
|
|
573
|
+
`data_post_provider` for POST) and through `gooseworks call` in a terminal; both are billed. Saving and running
|
|
574
|
+
its scripts (under `/tmp/gooseworks-scripts/<slug>/`) is terminal-only: without a terminal, do
|
|
575
|
+
that step's analysis yourself from the data the tools return, and never ask the user to run a
|
|
576
|
+
command.
|
|
421
577
|
|
|
422
578
|
## Rules
|
|
423
579
|
|
|
424
|
-
- **
|
|
425
|
-
|
|
426
|
-
-
|
|
580
|
+
- **Connector tools only** — every step uses the tool names above. A missing tool means the
|
|
581
|
+
GooseWorks connection is stale: ask the user to reconnect or refresh GooseWorks. Never send a
|
|
582
|
+
chat-app user to a terminal or a CLI install.
|
|
583
|
+
- **One backend workflow** — generation is `ads_generate` / `ads_creative_edit` ONLY.
|
|
427
584
|
Do NOT call FAL, the media proxy, `submit_render`, `update_render_status`, or upload render
|
|
428
|
-
files yourself; do NOT
|
|
429
|
-
- **
|
|
430
|
-
|
|
431
|
-
- **
|
|
432
|
-
|
|
433
|
-
- **
|
|
434
|
-
|
|
435
|
-
`
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
585
|
+
files yourself; do NOT fetch a local remix recipe to generate. The backend owns it.
|
|
586
|
+
- **State the credit total and get an explicit yes before every paid call** (credits table above).
|
|
587
|
+
Relay `insufficient_credits` plainly if the submit is rejected — don't retry blindly.
|
|
588
|
+
- **No plan step for one-off ads.** Only a campaign's plans wait for approval
|
|
589
|
+
(`request_campaign_generation` → `ads_approval_decide` with the user's words as `user_quote`).
|
|
590
|
+
- **Always end a successful run with the finished images and their links**, copied verbatim:
|
|
591
|
+
each creative's `app_url` and the `brand_url` from `result.links`, or from the creative
|
|
592
|
+
read (`creative.app_url`, `creative.brand_url`) after `animate` or `layerize`. When the
|
|
593
|
+
server returns no links, end with the images only. Never end on just "done" or a file path, and
|
|
594
|
+
never build an app URL yourself (the one exception is the browse link below).
|
|
595
|
+
- **Search the Brain before proposing.** After the brand read and before choosing an angle,
|
|
596
|
+
claim or source — or asking for a brand fact — run the task's `knowledge_search` and carry
|
|
597
|
+
its evidence brief. A failed or empty search is stated as such, never as "no evidence exists".
|
|
598
|
+
- **Use approved source paths.** If the user didn't name a source, recommend one in the single
|
|
599
|
+
proposal (own ads, Community, upload, Surprise me, or browse in the app). Surprise me is
|
|
600
|
+
`ads_template_read` with `mode: "surprise"`; browsing uses `/create?brand=<slug>&cli=true`.
|
|
601
|
+
Never use the retired curated third-party catalog. Generate when they paste the app's copyable
|
|
602
|
+
remix prompt back, or when they accept the surprise picks.
|
|
439
603
|
- **Treat competitor ads as inspiration** — never attest rights, imply ownership, or promise to
|
|
440
604
|
copy a competitor's distinctive expression.
|
|
441
605
|
- **Reconcile brand facts into the kit** — when the user states or changes something brand-level
|
|
@@ -443,14 +607,12 @@ run through the `gooseworks` CLI (`gooseworks fetch` / `gooseworks call`), like
|
|
|
443
607
|
`brand_update` with correction intent and the user's exact statement, or the user's own
|
|
444
608
|
images through `media_upload`; read back before saying saved. Proposed improvements stay
|
|
445
609
|
pending. Ask only when it is unclear whether a one-ad direction should apply to future ads.
|
|
446
|
-
- **Record feedback** — when the user reacts to a generated image,
|
|
447
|
-
`
|
|
448
|
-
- **
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
call the **`log_cli_event`** MCP tool (`event_type`: `error`/`blocker`/`missing_input`/`confusion`,
|
|
456
|
-
with the real error + step in `details`) so the team gets visibility. Still tell the user too.
|
|
610
|
+
- **Record feedback** — when the user reacts to a generated image, call `ads_creative_update`
|
|
611
|
+
with `patch.feedback` so the quality loop learns.
|
|
612
|
+
- **Don't busy-loop** — poll `job_get` on a sensible interval (~20-30s); a `queued` batch is
|
|
613
|
+
waiting on research and will start on its own.
|
|
614
|
+
- **Report problems so we can fix them** — when a batch fails or is rejected and you can't resolve
|
|
615
|
+
it, a required brand input/asset is missing, or an instruction is ambiguous or contradictory:
|
|
616
|
+
in a terminal run `gooseworks log "<what happened>" --event-type <error|blocker|missing_input|confusion> --details '<json with the real error and step>'`,
|
|
617
|
+
or call the `log_cli_event` tool if your connection lists it. A chat app without either skips
|
|
618
|
+
this step. Always tell the user too.
|