gooseworks 0.3.9 → 0.3.11
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"master-skill.d.ts","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,oDAAoD;AACpD,wBAAgB,cAAc,IAAI,UAAU,EAAE,CAM7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"master-skill.d.ts","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,oDAAoD;AACpD,wBAAgB,cAAc,IAAI,UAAU,EAAE,CAM7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CA0L9C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAsUhD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAmTlD"}
|
|
@@ -61,6 +61,18 @@ Examples — all of these route to \`goose-ads\`, not the data flow: "remix this
|
|
|
61
61
|
|
|
62
62
|
All commands below auto-load credentials from \`~/.gooseworks/credentials.json\`. If a command exits with "Not logged in", tell the user to run: \`npx gooseworks login\`. To log out: \`npx gooseworks logout\`.
|
|
63
63
|
|
|
64
|
+
### CLI-free environments (cowork / headless)
|
|
65
|
+
|
|
66
|
+
If the \`gooseworks\` CLI binary isn't available (e.g. Anthropic cowork) but the
|
|
67
|
+
\`mcp__gooseworks__*\` tools are connected, use the MCP equivalents instead of shelling out:
|
|
68
|
+
- \`gooseworks search <q>\` → the **\`search_skills\`** MCP tool.
|
|
69
|
+
- \`gooseworks fetch <slug>\` → the **\`fetch_skill\`** MCP tool (same content/scripts/files/deps).
|
|
70
|
+
- \`gooseworks credits\` → the **\`get_ad_credits\`** MCP tool.
|
|
71
|
+
|
|
72
|
+
Discovery and fetching a skill's instructions work fully CLI-free this way. Note: the paid data
|
|
73
|
+
proxy (\`gooseworks call <provider> <path>\`) still requires the CLI for now — if a task needs it
|
|
74
|
+
and no CLI is present, tell the user that step must run where the \`gooseworks\` CLI is installed.
|
|
75
|
+
|
|
64
76
|
To check credit balance:
|
|
65
77
|
\`\`\`bash
|
|
66
78
|
gooseworks credits
|
|
@@ -577,14 +589,43 @@ you *what to make*; read both, and this doc wins on any conflict about the envir
|
|
|
577
589
|
You run inside the user's own Claude Code session (they pasted an instruction with a project
|
|
578
590
|
id). The app NEVER runs you — it is the viewer + review surface; you are the renderer.
|
|
579
591
|
|
|
580
|
-
##
|
|
592
|
+
## CLI-free environments (cowork / headless)
|
|
593
|
+
|
|
594
|
+
You may be running WITHOUT the \`gooseworks\` CLI binary (e.g. Anthropic cowork). The
|
|
595
|
+
\`mcp__gooseworks__*\` tools work over the MCP connection regardless, so wherever this skill
|
|
596
|
+
says to shell out, use the MCP equivalent:
|
|
597
|
+
|
|
598
|
+
- \`gooseworks fetch <slug>\` → the **\`fetch_skill\`** MCP tool (returns the same content/scripts/
|
|
599
|
+
files/dependencySkills). \`gooseworks search <q>\` → **\`search_skills\`**.
|
|
600
|
+
- \`gooseworks credits\` → the **\`get_ad_credits\`** MCP tool.
|
|
601
|
+
- \`gooseworks doctor\` → do the manual toolchain check in the preflight below.
|
|
602
|
+
|
|
603
|
+
## Prerequisite — MCP + a render toolchain (Phase 0 preflight)
|
|
581
604
|
|
|
582
605
|
- The \`mcp__gooseworks__*\` tools are REQUIRED. If they're unavailable, stop and tell the user
|
|
583
|
-
to run \`gooseworks install --claude --mcp\`
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
606
|
+
to connect the GooseWorks MCP server (or run \`gooseworks install --claude --mcp\` on the CLI)
|
|
607
|
+
and restart. There is no REST fallback.
|
|
608
|
+
- **The render runs wherever THIS agent runs, and it needs a real toolchain: \`ffmpeg\` +
|
|
609
|
+
\`ffprobe\` + a Playwright **Chromium**.** Establish it in this priority order, and do NOT start
|
|
610
|
+
rendering until one is confirmed:
|
|
611
|
+
1. **CLI present →** run \`gooseworks doctor\` (checks login, MCP, ffmpeg/ffprobe, Playwright
|
|
612
|
+
Chromium in one shot). Fix any ✗ with the command it prints, then continue.
|
|
613
|
+
2. **No CLI →** check the toolchain yourself: \`ffmpeg -version\`, \`ffprobe -version\`, and a
|
|
614
|
+
Playwright Chromium probe (\`npx playwright --version\` and, if needed, \`npx playwright install
|
|
615
|
+
chromium\`). If all resolve, continue.
|
|
616
|
+
3. **Docker available →** this is the most reliable way to get the toolchain in a sandbox that
|
|
617
|
+
lacks it: run the render steps inside the prebuilt image
|
|
618
|
+
**\`ghcr.io/gooseworks-ai/goose-video-render\`** (ffmpeg + ffprobe + Playwright Chromium baked
|
|
619
|
+
in), mounting the project working directory. Use Docker whenever the host is missing ffmpeg or
|
|
620
|
+
Chromium and \`docker\` is on PATH. (Note: nested Docker is usually disabled inside managed
|
|
621
|
+
sandboxes like cowork — treat this as an option, not a guarantee.)
|
|
622
|
+
4. **None of the above works →** STOP and tell the user plainly, e.g.: *"Video rendering needs
|
|
623
|
+
ffmpeg + a Playwright Chromium (or Docker) on the machine running this agent. This environment
|
|
624
|
+
doesn't have them and I can't install them here. Options: (a) enable/allow Docker so I can use
|
|
625
|
+
the goose-video-render image, (b) install ffmpeg + \`npx playwright install chromium\`, or
|
|
626
|
+
(c) run this skill locally in your own Claude Code where the toolchain is available."* Do not
|
|
627
|
+
half-render or fake a result. Static image ads (the \`goose-ads\` skill) do NOT need any of this
|
|
628
|
+
and work anywhere — offer that as the fallback if they just want an ad now.
|
|
588
629
|
|
|
589
630
|
## Identity, token, credits
|
|
590
631
|
|
|
@@ -661,6 +702,16 @@ This is a **review-once** flow: prepare every ingredient the video needs, show t
|
|
|
661
702
|
the user in the app, get ONE approval, then render. Never render before approval, and don't drip
|
|
662
703
|
ingredients out one at a time.
|
|
663
704
|
|
|
705
|
+
**The review is of the individual PIECES, never the finished cut.** Prepare and mirror only the
|
|
706
|
+
*ingredients* — the script, each conversation image, the end card, the voice/music plan. Do NOT
|
|
707
|
+
stitch, composite, or render the assembled video (no "full cascade" / "approved cut" clip) during
|
|
708
|
+
ingredient prep, and never add such a clip as a review ingredient. Assembling the video before
|
|
709
|
+
approval defeats this gate — the user opens "Review before rendering" and sees an already-finished
|
|
710
|
+
video, then rightly asks why it was made before they approved (GOOSE-2542) — and it wastes the
|
|
711
|
+
render. The full video is stitched ONLY in Step 4, after approval. A \`video\` ingredient here is
|
|
712
|
+
only for a genuinely separate SOURCE clip the format needs (e.g. supplied b-roll), never the
|
|
713
|
+
assembled output.
|
|
714
|
+
|
|
664
715
|
1. **Generate every ingredient the format needs — not just the script.** For an iMessage video
|
|
665
716
|
that's typically: the **script** (the bubble thread), the **image(s)** shown in the conversation
|
|
666
717
|
(one or more), and the **end card**. Richer templates add more (hook frame, background, product
|
|
@@ -806,6 +857,9 @@ path. (\`fal-storage-proxy\` may 404 depending on the install; don't block on it
|
|
|
806
857
|
- **Prepare ALL ingredients first** (script + every visual: image(s) + end card + whatever else
|
|
807
858
|
the template needs), mirror the whole set with \`update_ad_project_script\`, and get the user's
|
|
808
859
|
approval in-session BEFORE rendering — always (review-once).
|
|
860
|
+
- **Never assemble the full video before approval.** The review shows the individual pieces, not a
|
|
861
|
+
stitched/composited cut; do not add a "full cascade" / finished-video clip as a review ingredient
|
|
862
|
+
(GOOSE-2542). The assembled video is produced only in Step 4, after the user approves.
|
|
809
863
|
- **submit_render LAST**; \`output_url\` = the durable render-file URL, never a CDN URL.
|
|
810
864
|
- **Always pass \`project_id\` on media-proxy calls** (fal / ElevenLabs) so the credits attribute
|
|
811
865
|
to this ad project — that's what lets the user see per-project spend in the app.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"master-skill.js","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":";;AA4BA,wCAMC;AAWD,
|
|
1
|
+
{"version":3,"file":"master-skill.js","sourceRoot":"","sources":["../../src/skills/master-skill.ts"],"names":[],"mappings":";;AA4BA,wCAMC;AAWD,sDA0LC;AAiBD,0DAsUC;AAcD,8DAmTC;AAp2BD,oDAAoD;AACpD,SAAgB,cAAc;IAC5B,OAAO;QACL,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,qBAAqB,EAAE,EAAE;QACxD,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE;QACzD,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,yBAAyB,EAAE,EAAE;KAC9D,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,qBAAqB;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwLR,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,uBAAuB;IACrC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoUR,CAAC;AACF,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAgB,yBAAyB;IACvC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiTR,CAAC;AACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gooseworks",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"description": "GooseWorks CLI — give your coding agent real data tools",
|
|
5
5
|
"bin": {
|
|
6
6
|
"gooseworks": "./dist/index.js"
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"build": "tsc",
|
|
11
11
|
"dev": "tsx src/index.ts",
|
|
12
12
|
"test": "jest",
|
|
13
|
-
"
|
|
13
|
+
"generate:skills": "tsx scripts/generate-skill-files.ts",
|
|
14
|
+
"prepublishOnly": "npm run generate:skills && npm run build && npm test"
|
|
14
15
|
},
|
|
15
16
|
"dependencies": {
|
|
16
17
|
"chalk": "^4.1.2",
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: goose-ads
|
|
3
|
+
slug: goose-ads
|
|
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,
|
|
7
|
+
research a brand for ads, OR analyze ad performance (Meta/Google campaign diagnostics,
|
|
8
|
+
creative fatigue, CAC & lead quality, competitor ad intelligence, ad angles & hooks). Use
|
|
9
|
+
when the user says "remix this ad", references a static ad template id/slug, asks to "make
|
|
10
|
+
an ad", "edit this ad", "research my brand", or asks to analyze/diagnose ad campaigns.
|
|
11
|
+
Generation runs through the GooseWorks backend's single cloud workflow (the same one the ads
|
|
12
|
+
app uses) — credits are reserved and billed server-side. Analytics recipes are fetched from
|
|
13
|
+
goose-skills on demand.
|
|
14
|
+
category: ads
|
|
15
|
+
version: 2.2.0
|
|
16
|
+
author: GooseWorks
|
|
17
|
+
tags: [gooseworks, ads, remix, static-ad, brand, creative, image, analytics, meta-ads, performance]
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# GooseWorks Ads — create, edit & analyze
|
|
21
|
+
|
|
22
|
+
The GooseWorks ads skill. Two jobs:
|
|
23
|
+
|
|
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
|
|
26
|
+
whole pipeline (compose → generate → persist → judge), reserves and bills credits, and
|
|
27
|
+
stores the renders. You do NOT generate images, call FAL, manage render rows, or upload
|
|
28
|
+
files — those are gone. This is the exact same workflow the GooseWorks ads app uses, so the
|
|
29
|
+
skill and the app can never drift.
|
|
30
|
+
2. **Analyze ad performance** — fetch ad-analytics recipes from goose-skills on demand
|
|
31
|
+
(these are unrelated to generation; see "Analyze / intelligence" below).
|
|
32
|
+
|
|
33
|
+
## Prerequisite — the GooseWorks MCP server is REQUIRED
|
|
34
|
+
|
|
35
|
+
Everything goes through the `mcp__gooseworks__*` tools. If they are not available, **stop and
|
|
36
|
+
tell the user to run `gooseworks install --claude --mcp`** (and restart Claude Code). There is
|
|
37
|
+
no HTTP/file fallback — the REST ad endpoints are session-cookie-only and reject your token.
|
|
38
|
+
|
|
39
|
+
## Identity & credits
|
|
40
|
+
|
|
41
|
+
- One agent-scoped token authenticates the `gooseworks` MCP tools. Never print it. The tools
|
|
42
|
+
resolve your org automatically — you do NOT resolve an "Ads agent" or pass `target` for the
|
|
43
|
+
generation tools.
|
|
44
|
+
- **Credits are handled entirely by the backend.** `submit_remix_batch` reserves the estimated
|
|
45
|
+
cost up front (it errors with `insufficient_credits` if the wallet is short — relay the
|
|
46
|
+
message and stop) and bills only the images that actually complete. Call
|
|
47
|
+
`estimate_remix_batch` first to tell the user the cost; `gooseworks credits` shows balance.
|
|
48
|
+
|
|
49
|
+
## Defaults — match the app (priority: frontend, then backend)
|
|
50
|
+
|
|
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:**
|
|
53
|
+
|
|
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`.
|
|
62
|
+
|
|
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.
|
|
66
|
+
|
|
67
|
+
## The generation tools (the new, single-workflow surface)
|
|
68
|
+
|
|
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).
|
|
72
|
+
Returns the batch with a `links` block (`brand_url` + per-creative `app_url`). If the brand's
|
|
73
|
+
research isn't finished yet the batch comes back `status: "queued"` — it auto-runs the moment
|
|
74
|
+
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
|
|
80
|
+
`completed`/`failed`/`pending` counts, plus `links`. A creative is done when its `pending` is 0
|
|
81
|
+
— NOT when `current_render_url` is set (during a regenerate that field still points at the prior
|
|
82
|
+
image). Each render carries `age_seconds` (since queued) and `elapsed_seconds` (time generating):
|
|
83
|
+
use them to tell a slow-but-healthy render from a stuck one. A render only failed when its
|
|
84
|
+
`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
|
|
86
|
+
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).
|
|
90
|
+
Returns the picked templates (id, slug, title, image, ratio) AND a ready-to-open `create_url`
|
|
91
|
+
(the /create page with `cli=true` and the picks pre-selected). This is how you recommend
|
|
92
|
+
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).
|
|
103
|
+
|
|
104
|
+
### Plan mode — review the plan BEFORE generating (optional)
|
|
105
|
+
|
|
106
|
+
For users who want to approve each ad's plan before spending credits (the app's "Plan it" flow):
|
|
107
|
+
|
|
108
|
+
- `submit_remix_batch { ..., requires_approval: true }` — composes each creative's plan and PAUSES.
|
|
109
|
+
**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
|
|
111
|
+
`composing`, wait; once `awaiting_approval`, show its `plan` (composed prompt + refs + quality)
|
|
112
|
+
to the user.
|
|
113
|
+
- `revise_ad_plan { project_id, message?, variant_label? }` — recompose from a chat steer, still
|
|
114
|
+
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
|
|
117
|
+
`get_remix_batch` and hand back links as usual.
|
|
118
|
+
|
|
119
|
+
Only offer plan mode when the user asks to review/approve first — the default path generates
|
|
120
|
+
immediately.
|
|
121
|
+
|
|
122
|
+
## Reading the brand & picking inputs (still MCP, read-only)
|
|
123
|
+
|
|
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
|
|
133
|
+
template id. Call this FIRST to snapshot it into a private template, then use the returned
|
|
134
|
+
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.
|
|
137
|
+
- `get_ad_project` / `append_project_message` — inspect a creative / leave a note on its thread.
|
|
138
|
+
|
|
139
|
+
## Keep the brand kit in sync — reconcile, then update (ASK first)
|
|
140
|
+
|
|
141
|
+
The brand kit is the source of truth every generation reads. During ANY task, when the user
|
|
142
|
+
**tells you something about the brand or asks to change something brand-level** — a different
|
|
143
|
+
tagline, audience, voice, a product's name/price/description, "our logo is X", "we don't sell Y
|
|
144
|
+
anymore", a new product photo — treat it as a possible kit update, don't just use it for this one
|
|
145
|
+
ad and forget it:
|
|
146
|
+
|
|
147
|
+
1. **Check it against the kit.** `get_brand_kit { brand_id }` and see whether what the user said
|
|
148
|
+
matches, is missing from, or contradicts the kit.
|
|
149
|
+
2. **If it's already in the kit and matches** — nothing to do; proceed.
|
|
150
|
+
3. **If it's new or different — ASK before writing.** Confirm in one line: *"Want me to update
|
|
151
|
+
the brand kit so this sticks for future ads?"* Only persist on a yes (or when the user clearly
|
|
152
|
+
asked you to change the brand). Don't silently mutate the kit, and don't nag on trivia.
|
|
153
|
+
4. **Persist with the write tools** (partial — only the fields you pass are touched; each edit is
|
|
154
|
+
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.
|
|
160
|
+
5. **Confirm what changed** and continue the task. (Logo, colors, and fonts are owned by the
|
|
161
|
+
backend research pass — prefer `update_ad_brand` / the research flow for those, not free text.)
|
|
162
|
+
|
|
163
|
+
This is the parity gap the app closes in-product: a brand fact the user gives mid-task should be
|
|
164
|
+
able to flow back into the kit — with their ok — instead of being lost.
|
|
165
|
+
|
|
166
|
+
## Picking templates — ASK the user; don't freelance from the catalog
|
|
167
|
+
|
|
168
|
+
When the user wants to make ads but has NOT named a specific template (id/slug/Community
|
|
169
|
+
ad/upload), do NOT silently browse the raw catalog and hand-pick for them. Instead run this
|
|
170
|
+
short ask flow — it mirrors the web app and keeps the human in the loop:
|
|
171
|
+
|
|
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`.
|
|
178
|
+
It opens /create in **CLI mode** with the picks pre-selected, a preview modal, and the
|
|
179
|
+
**copyable remix prompt at the bottom** (in place of the Generate input). They can swap
|
|
180
|
+
picks and copy that prompt. If they'd rather you "just make them" without reviewing in the
|
|
181
|
+
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
|
|
183
|
+
active brand's slug filled in:
|
|
184
|
+
`https://make.gooseworks.ai/create?brand=<brand-slug>&cli=true`
|
|
185
|
+
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
|
|
189
|
+
(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.
|
|
191
|
+
|
|
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.
|
|
194
|
+
|
|
195
|
+
## Workflow — make ads from a template
|
|
196
|
+
|
|
197
|
+
1. **Resolve the brand.** `list_ad_brands` by name/site → `get_brand_kit { brand_id }`. If the
|
|
198
|
+
kit's `researchStatus` isn't `complete`, you can still submit (the batch queues and runs when
|
|
199
|
+
research finishes) — just tell the user. Use the kit to pick `product_name` (a real entry from
|
|
200
|
+
`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.
|
|
203
|
+
For a Community ad, `remix_community_ad` first; for an uploaded image, `create_user_ad_template`
|
|
204
|
+
first.
|
|
205
|
+
3. **(Optional) Craft the steering prompt.** The `prompt` is OPTIONAL — this is where the skill
|
|
206
|
+
adds value: turn the user's intent (from step 1) into a concise steering note (e.g. tone,
|
|
207
|
+
season, emphasis). Don't over-specify; the backend pipeline + brand kit handle palette, fonts,
|
|
208
|
+
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
|
|
214
|
+
until every creative's `pending` is 0. Most images finish in a few minutes; text-heavy templates
|
|
215
|
+
and `quality: high` take longer. Read each render's `elapsed_seconds` rather than guessing — a
|
|
216
|
+
render that's still `running` is healthy; do NOT re-submit thinking it stalled (that double-bills).
|
|
217
|
+
7. **Hand back the links** from the batch's `links` block — `brand_url` (gallery) and each
|
|
218
|
+
creative's `app_url` — copied verbatim. Never end on just "done" or a file path.
|
|
219
|
+
|
|
220
|
+
## Workflow — edit an existing ad
|
|
221
|
+
|
|
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.
|
|
229
|
+
|
|
230
|
+
## Brand research
|
|
231
|
+
|
|
232
|
+
Prefer the backend's result: `get_brand_kit { brand_id }`. If `researchStatus` is
|
|
233
|
+
`complete`, REUSE it — never re-research.
|
|
234
|
+
|
|
235
|
+
**The split — backend owns visuals, you own the qualitative depth:**
|
|
236
|
+
|
|
237
|
+
- **Backend LIGHT pass (automatic).** `create_ad_brand` with a `website_url` kicks off the same
|
|
238
|
+
backend research the web app uses, in `mode: "light"`: it resolves the **authoritative logo,
|
|
239
|
+
colors, and fonts** (Brandfetch + context.dev) plus a baseline kit, then flips
|
|
240
|
+
`research_status` to `complete` — usually under a minute. You can't reproduce those visual
|
|
241
|
+
signals locally, so **never re-derive logo/colors/fonts.** (Web onboarding via `/api/ads/onboard`
|
|
242
|
+
runs the full thing; nothing to do but read it.)
|
|
243
|
+
- **Your DEEP pass (local, agentic).** You add the qualitative depth the light pass leaves thin —
|
|
244
|
+
positioning, audience segments, voice, brandType, value props, proof points, products — grounded
|
|
245
|
+
on the actual site.
|
|
246
|
+
|
|
247
|
+
**CLI brand-research flow:**
|
|
248
|
+
|
|
249
|
+
1. `create_ad_brand { name, website_url }` → keep `brand_id` + `slug`. The brand comes back with
|
|
250
|
+
`research_status: "pending"` (light pass in flight).
|
|
251
|
+
2. **Wait for the backend light pass:** poll `get_brand_kit { brand_id }` until `researchStatus`
|
|
252
|
+
is `complete` (usually <60s). Now the kit has authoritative logo/colors/fonts + a baseline.
|
|
253
|
+
At this point generation is already unblocked — but do the deep pass to make it good.
|
|
254
|
+
3. **Deep research locally:** `gooseworks fetch brand-research` and follow its phases. **Ground
|
|
255
|
+
every fact on the fetched site** — if the site can't be read, say so and ask the user; never
|
|
256
|
+
guess a category from the brand name alone.
|
|
257
|
+
4. **Write the pack** with `write_file` under `agent-config/brands/<slug>/`:
|
|
258
|
+
- the `brand-research/*.md` docs + `brand-assets/manifest.json` (human-readable pack), AND
|
|
259
|
+
- `brand-research/kit-patch.json` — the STRUCTURED fields the web UI renders. Field-for-field
|
|
260
|
+
contract; only what you put here reaches the kit. Shape:
|
|
261
|
+
`{ positioning?: string, audience?: string, voice?: string, brandType?: string, tagline?: string, valueProps?: string[], proofPoints?: string[], products?: [{ name, description?, link?, pricing?, imageUrls?: string[] }] }`
|
|
262
|
+
(`brandType` ∈ product | saas | service | agency | restaurant | fashion | beauty | fitness |
|
|
263
|
+
finance | education | health). Only URLs already in our storage for product images.
|
|
264
|
+
- **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
|
|
266
|
+
NON-CLOBBERINGLY (it will NOT overwrite the backend's visuals or any user edit), then re-confirms
|
|
267
|
+
`research_status: complete`.
|
|
268
|
+
6. **Verify:** `get_brand_kit { brand_id }` — confirm the qualitative fields you wrote are present
|
|
269
|
+
before generating.
|
|
270
|
+
|
|
271
|
+
**If the brand has NO website**, the backend light pass can't run (nothing to fetch) — do the whole
|
|
272
|
+
thing locally (steps 3–6) and finalize; an un-finalized brand has no kit for generation and leaves
|
|
273
|
+
no artifact to debug a wrong run (this is how a bad local classification, e.g. mislabelling a SaaS
|
|
274
|
+
as a "drink company", used to vanish without a trace).
|
|
275
|
+
|
|
276
|
+
## Analyze / intelligence (fetched recipes — NOT generation)
|
|
277
|
+
|
|
278
|
+
These are analysis recipes you fetch from goose-skills with `gooseworks fetch <slug>` and
|
|
279
|
+
follow; they do NOT touch the generation tools or credits-for-images. Pick the closest match;
|
|
280
|
+
if unsure, `gooseworks search "<what the user wants>"` first:
|
|
281
|
+
- **Campaign performance diagnosis** ("why is my Meta/Google campaign underperforming",
|
|
282
|
+
creative fatigue, learning phase, pacing, auction overlap) → `gooseworks fetch meta-ads-analyzer`
|
|
283
|
+
(or `ad-campaign-analyzer` for cross-platform).
|
|
284
|
+
- **Lead/CAC quality** ("are these ads driving qualified leads", true CAC vs vanity CPA,
|
|
285
|
+
Scale/Keep/Investigate/Cut) → `gooseworks fetch ad-lead-quality-analyzer`.
|
|
286
|
+
- **Competitor ad intelligence** ("what ads are competitors running") →
|
|
287
|
+
`gooseworks fetch competitor-ad-intelligence` (Meta Ad Library: `meta-ad-scraper`;
|
|
288
|
+
Google: `google-ad-scraper`).
|
|
289
|
+
- **Creative ideation** (ad angles, winning hooks) → `gooseworks fetch ad-angle-miner` /
|
|
290
|
+
`gooseworks fetch trending-ad-hook-spotter`.
|
|
291
|
+
- **Policy / landing-page checks** → `gooseworks fetch meta-ad-policy-checker` /
|
|
292
|
+
`gooseworks fetch ad-to-landing-page-auditor`.
|
|
293
|
+
|
|
294
|
+
Save their scripts to `/tmp/gooseworks-scripts/<slug>/` and follow their instructions. These
|
|
295
|
+
run through the `gooseworks` CLI (`gooseworks fetch` / `gooseworks call`), like the GTM skills.
|
|
296
|
+
|
|
297
|
+
## Rules
|
|
298
|
+
|
|
299
|
+
- **MCP required** — if `mcp__gooseworks__*` is unavailable, stop and tell the user to run
|
|
300
|
+
`gooseworks install --claude --mcp`.
|
|
301
|
+
- **One backend workflow** — generation is `submit_remix_batch` / `regenerate_creative` ONLY.
|
|
302
|
+
Do NOT call FAL, the media proxy, `submit_render`, `update_render_status`, or upload render
|
|
303
|
+
files yourself; do NOT `gooseworks fetch` a local remix recipe to generate. The backend owns it.
|
|
304
|
+
- **Always end a successful run with the links** from the batch's `links` block (`brand_url` +
|
|
305
|
+
each creative's `app_url`), copied verbatim. Never end on just "done" or a file path.
|
|
306
|
+
- **Quote cost before generating** when it's non-trivial (use `estimate_remix_batch`), and
|
|
307
|
+
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`.
|
|
311
|
+
Generate when they paste the app's copyable remix prompt back (or submit the surprise picks
|
|
312
|
+
directly if they'd rather not review).
|
|
313
|
+
- **Ask the styling** — Keep original (default) vs Match brand — before you submit.
|
|
314
|
+
- **Reconcile brand facts into the kit** — when the user states or changes something brand-level
|
|
315
|
+
mid-task, check it against `get_brand_kit` and, with their ok, persist it via `update_brand_kit`
|
|
316
|
+
/ `upsert_brand_product` / `add_brand_product_image` so it sticks for future ads. Ask first;
|
|
317
|
+
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
|
|
322
|
+
immediately.
|
|
323
|
+
- **Don't busy-loop** — poll `get_remix_batch` on a sensible interval (~20-30s); a `queued`
|
|
324
|
+
batch is waiting on research and will start on its own.
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: goose-video
|
|
3
|
+
slug: goose-video
|
|
4
|
+
description: >
|
|
5
|
+
GooseWorks video ads — remix a video ad template (iMessage chat-reveal, more coming) into a
|
|
6
|
+
branded video ad for the user's product. Renders LOCALLY on the user's machine (Playwright +
|
|
7
|
+
ffmpeg + GooseWorks media proxies) and saves the finished MP4 back to the project over MCP.
|
|
8
|
+
Use when the user says "make the video for project <id>", references a video ad project or
|
|
9
|
+
template, or asks to remix a video ad. Unlike goose-ads (static images, generated server-side),
|
|
10
|
+
video renders locally and reports progress + the result back through the gooseworks MCP tools.
|
|
11
|
+
category: ads
|
|
12
|
+
version: 0.2.0
|
|
13
|
+
author: GooseWorks
|
|
14
|
+
tags: [gooseworks, ads, video, remix, imessage, local-render, byoa]
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# GooseWorks Video Ads — local remix runtime
|
|
18
|
+
|
|
19
|
+
You produce **video** ad creative on the user's OWN machine and sync the result back to the
|
|
20
|
+
GooseWorks app over MCP. This document is the **runtime contract** (auth, credits, the media
|
|
21
|
+
proxies, data I/O, the review gate). A separate **recipe skill** — fetched per format — tells
|
|
22
|
+
you *what to make*; read both, and this doc wins on any conflict about the environment.
|
|
23
|
+
|
|
24
|
+
You run inside the user's own Claude Code session (they pasted an instruction with a project
|
|
25
|
+
id). The app NEVER runs you — it is the viewer + review surface; you are the renderer.
|
|
26
|
+
|
|
27
|
+
## CLI-free environments (cowork / headless)
|
|
28
|
+
|
|
29
|
+
You may be running WITHOUT the `gooseworks` CLI binary (e.g. Anthropic cowork). The
|
|
30
|
+
`mcp__gooseworks__*` tools work over the MCP connection regardless, so wherever this skill
|
|
31
|
+
says to shell out, use the MCP equivalent:
|
|
32
|
+
|
|
33
|
+
- `gooseworks fetch <slug>` → the **`fetch_skill`** MCP tool (returns the same content/scripts/
|
|
34
|
+
files/dependencySkills). `gooseworks search <q>` → **`search_skills`**.
|
|
35
|
+
- `gooseworks credits` → the **`get_ad_credits`** MCP tool.
|
|
36
|
+
- `gooseworks doctor` → do the manual toolchain check in the preflight below.
|
|
37
|
+
|
|
38
|
+
## Prerequisite — MCP + a render toolchain (Phase 0 preflight)
|
|
39
|
+
|
|
40
|
+
- The `mcp__gooseworks__*` tools are REQUIRED. If they're unavailable, stop and tell the user
|
|
41
|
+
to connect the GooseWorks MCP server (or run `gooseworks install --claude --mcp` on the CLI)
|
|
42
|
+
and restart. There is no REST fallback.
|
|
43
|
+
- **The render runs wherever THIS agent runs, and it needs a real toolchain: `ffmpeg` +
|
|
44
|
+
`ffprobe` + a Playwright **Chromium**.** Establish it in this priority order, and do NOT start
|
|
45
|
+
rendering until one is confirmed:
|
|
46
|
+
1. **CLI present →** run `gooseworks doctor` (checks login, MCP, ffmpeg/ffprobe, Playwright
|
|
47
|
+
Chromium in one shot). Fix any ✗ with the command it prints, then continue.
|
|
48
|
+
2. **No CLI →** check the toolchain yourself: `ffmpeg -version`, `ffprobe -version`, and a
|
|
49
|
+
Playwright Chromium probe (`npx playwright --version` and, if needed, `npx playwright install
|
|
50
|
+
chromium`). If all resolve, continue.
|
|
51
|
+
3. **Docker available →** this is the most reliable way to get the toolchain in a sandbox that
|
|
52
|
+
lacks it: run the render steps inside the prebuilt image
|
|
53
|
+
**`ghcr.io/gooseworks-ai/goose-video-render`** (ffmpeg + ffprobe + Playwright Chromium baked
|
|
54
|
+
in), mounting the project working directory. Use Docker whenever the host is missing ffmpeg or
|
|
55
|
+
Chromium and `docker` is on PATH. (Note: nested Docker is usually disabled inside managed
|
|
56
|
+
sandboxes like cowork — treat this as an option, not a guarantee.)
|
|
57
|
+
4. **None of the above works →** STOP and tell the user plainly, e.g.: *"Video rendering needs
|
|
58
|
+
ffmpeg + a Playwright Chromium (or Docker) on the machine running this agent. This environment
|
|
59
|
+
doesn't have them and I can't install them here. Options: (a) enable/allow Docker so I can use
|
|
60
|
+
the goose-video-render image, (b) install ffmpeg + `npx playwright install chromium`, or
|
|
61
|
+
(c) run this skill locally in your own Claude Code where the toolchain is available."* Do not
|
|
62
|
+
half-render or fake a result. Static image ads (the `goose-ads` skill) do NOT need any of this
|
|
63
|
+
and work anywhere — offer that as the fallback if they just want an ad now.
|
|
64
|
+
|
|
65
|
+
## Identity, token, credits
|
|
66
|
+
|
|
67
|
+
- Read `~/.gooseworks/credentials.json` → `api_key` (your agent token), `api_base`, `agent_id`.
|
|
68
|
+
Never print the token.
|
|
69
|
+
- **CRITICAL — target the org-default Ads agent on EVERY file op.** The app serves project files
|
|
70
|
+
(the render-file route) from the org's DEFAULT agent, but MCP file writes default to your
|
|
71
|
+
token's pinned agent — which can be a DIFFERENT agent, so a render written with the default
|
|
72
|
+
scope is **invisible in the app**. First resolve the Ads agent: `list_accessible_scopes` → the
|
|
73
|
+
scope with `is_org_default: true` (the ORG default — NOT the `is_default` / `default_agent_id`
|
|
74
|
+
fields, which are the *user's* default agent and are often a DIFFERENT agent). Its `agent_id` is
|
|
75
|
+
`ADS_AGENT` (name "Ads agent", slug `org-default`; usually also the `agent_id` in
|
|
76
|
+
credentials.json). Then pass `target: { type: "agent", agent_id: ADS_AGENT }` on EVERY
|
|
77
|
+
`get_upload_url` / `get_download_url` / `write_file` / `list_directory` / `read_file` — NEVER
|
|
78
|
+
omit `target`.
|
|
79
|
+
- **CRITICAL — publish under the PROJECT FOLDER, not the workspace root (the #1 "video renders but
|
|
80
|
+
is invisible" bug).** `get_upload_url` stores at `<ADS_AGENT>/files/<path>` verbatim, but the
|
|
81
|
+
render-file route reads from
|
|
82
|
+
`<ADS_AGENT>/files/agent-config/brands/<brand_slug>/projects/<project_id>/<path>`
|
|
83
|
+
(see backend `resolveProjectFileKey`). So EVERY publish/preview `path` MUST be prefixed with
|
|
84
|
+
`agent-config/brands/<brand_slug>/projects/<project_id>/` — e.g. upload to
|
|
85
|
+
`agent-config/brands/<brand_slug>/projects/<project_id>/working/final.mp4`, NEVER bare
|
|
86
|
+
`working/final.mp4`. A bare path 404s in the app even though the render row AND a bare-path
|
|
87
|
+
`get_download_url` both "succeed" (they resolve the wrong key). The render `output_url` still
|
|
88
|
+
stays the project-relative `...render-file?path=working/final.mp4` — the route re-prepends the
|
|
89
|
+
prefix itself. Always verify with `get_download_url` on the FULL `agent-config/...` path (must
|
|
90
|
+
be non-empty; curl it for HTTP 200) BEFORE marking the render complete.
|
|
91
|
+
- Media generation (FAL / ElevenLabs) is billed to the agent through the GooseWorks proxies.
|
|
92
|
+
`submit_render { kind: "full" }` debits **1 ad credit at row creation** — so sequence it LAST
|
|
93
|
+
(render + verify a good MP4 first), and never re-submit on a guess (that double-bills). Call
|
|
94
|
+
`get_ad_credits` first; the user can check `gooseworks credits`.
|
|
95
|
+
|
|
96
|
+
## Step 1 — resolve the project, source, brand
|
|
97
|
+
|
|
98
|
+
1. `get_ad_project { project_id }` → keep `brand_id`, `source_sample_id`, `name`, `status`.
|
|
99
|
+
2. `get_ad_template { template_id: source_sample_id }` → the source video: `media_url`,
|
|
100
|
+
`recipe`, `format` (e.g. "imessage"), `extracted_script`, `how_to`, `remix_spec`.
|
|
101
|
+
3. Brand gate: `get_brand_kit { brand_id }`. If `researchStatus` is `complete`, REUSE it —
|
|
102
|
+
never re-research. If not, run brand research first (`gooseworks fetch brand-research`,
|
|
103
|
+
follow it, then `finalize_brand_research { brand_id }`) before continuing.
|
|
104
|
+
|
|
105
|
+
## Step 2 — read the template's recipe (it carries everything; NO hardcoded format map)
|
|
106
|
+
|
|
107
|
+
The ad format is a **template (data) in the ad_sample DB**, not a per-format skill.
|
|
108
|
+
`get_ad_template(source_sample_id)` returns the template's `recipe` — a self-contained brief you
|
|
109
|
+
read and execute. **Do NOT map `format` to a hardcoded recipe slug** (there is no such table):
|
|
110
|
+
|
|
111
|
+
- `recipe.format` — the format label (e.g. `vignette`), for display only.
|
|
112
|
+
- `recipe.atoms` — the **capabilities** this template composes (e.g. `create-video-seedance-2-fal`,
|
|
113
|
+
`create-image-gpt-image-fal`, `review-ugc-render`, `watch`). `gooseworks fetch <name>` each — they
|
|
114
|
+
live in `skills/ads/capabilities/` and are reused across templates (so they cache).
|
|
115
|
+
- `recipe.instructions` — the **playbook** to follow: `instructions.inline` prose, or
|
|
116
|
+
`instructions.doc_url` (an S3 markdown doc — fetch it).
|
|
117
|
+
- `recipe.config` — every param (prompts, layout, timings, palette, model choices).
|
|
118
|
+
- `recipe.inputs` — the brand-asset contract (which product / logo / offer this template needs).
|
|
119
|
+
- `recipe.assets` — reference material as S3 links (reference render, style guide, example frames) —
|
|
120
|
+
fetch as needed.
|
|
121
|
+
|
|
122
|
+
Runtime: **read the recipe → `gooseworks fetch` each capability in `recipe.atoms` → follow
|
|
123
|
+
`recipe.instructions` with `recipe.config` + the brand's bound `inputs`.** The template IS the recipe;
|
|
124
|
+
there is no `format → recipe-slug` table and no per-format skill to fetch.
|
|
125
|
+
|
|
126
|
+
Save each fetched capability's scripts + files under `/tmp/gooseworks-scripts/<name>/`. If a capability
|
|
127
|
+
is a Node package (a phone-mockup renderer), `npm install` in its folder so its `generate.js` +
|
|
128
|
+
Playwright resolve, and point the recorder's `NODE_PATH` at it.
|
|
129
|
+
|
|
130
|
+
> **Migration note:** older phone-mockup formats (`imessage` / `chatgpt` / `apple-notes`) whose DB
|
|
131
|
+
> recipe does not yet carry `atoms` / `instructions` still hold the legacy `recipe.thread` payload;
|
|
132
|
+
> migrate them to this shape (capabilities + instructions in the DB) — do not reintroduce a CLI map.
|
|
133
|
+
|
|
134
|
+
## Step 3 — prepare ALL the ingredients, then review ONCE (always, before any paid render)
|
|
135
|
+
|
|
136
|
+
This is a **review-once** flow: prepare every ingredient the video needs, show the whole set to
|
|
137
|
+
the user in the app, get ONE approval, then render. Never render before approval, and don't drip
|
|
138
|
+
ingredients out one at a time.
|
|
139
|
+
|
|
140
|
+
**The review is of the individual PIECES, never the finished cut.** Prepare and mirror only the
|
|
141
|
+
*ingredients* — the script, each conversation image, the end card, the voice/music plan. Do NOT
|
|
142
|
+
stitch, composite, or render the assembled video (no "full cascade" / "approved cut" clip) during
|
|
143
|
+
ingredient prep, and never add such a clip as a review ingredient. Assembling the video before
|
|
144
|
+
approval defeats this gate — the user opens "Review before rendering" and sees an already-finished
|
|
145
|
+
video, then rightly asks why it was made before they approved (GOOSE-2542) — and it wastes the
|
|
146
|
+
render. The full video is stitched ONLY in Step 4, after approval. A `video` ingredient here is
|
|
147
|
+
only for a genuinely separate SOURCE clip the format needs (e.g. supplied b-roll), never the
|
|
148
|
+
assembled output.
|
|
149
|
+
|
|
150
|
+
1. **Generate every ingredient the format needs — not just the script.** For an iMessage video
|
|
151
|
+
that's typically: the **script** (the bubble thread), the **image(s)** shown in the conversation
|
|
152
|
+
(one or more), and the **end card**. Richer templates add more (hook frame, background, product
|
|
153
|
+
shots, music bed…). Read the recipe for the exact ingredient list. Generate the visuals NOW
|
|
154
|
+
(media proxies / recipe), and `get_upload_url` each preview asset to the project folder
|
|
155
|
+
`agent-config/brands/<brand_slug>/projects/<project_id>/working/review/<name>` (the same
|
|
156
|
+
path-prefix rule as final publish — a bare `working/review/<name>` won't render in the panel).
|
|
157
|
+
In `script_drafts`, set each ingredient's `path` to the project-relative `working/review/<name>`.
|
|
158
|
+
You may ask the user a couple of clarifying questions about the generation first if the recipe
|
|
159
|
+
calls for it (angle, which product, offer/code) — batch them, then prepare everything.
|
|
160
|
+
2. **Mirror the whole ingredient set for review** — `update_ad_project_script { project_id,
|
|
161
|
+
script_drafts, script }`. `script_drafts` is a structured payload of **container-tagged
|
|
162
|
+
ingredients** so the app renders each piece the right way:
|
|
163
|
+
`{ format, scenes?, ingredients: [{ container, label, subtitle?, path?, text? }] }`. Each
|
|
164
|
+
ingredient's `container` tells the app HOW to show it:
|
|
165
|
+
- `image` (a frame shown in the video), `endcard` (the end card), `avatar` (a character
|
|
166
|
+
headshot), `background` → rendered as an image tile.
|
|
167
|
+
- `voice` (a voiceover clip — put the voice NAME in `subtitle`), `music` (the bed),
|
|
168
|
+
`audio` → rendered as an audio player.
|
|
169
|
+
- `video` (a clip) → a video player. `text` (a copy line like the CTA) → a text tile.
|
|
170
|
+
- `script` / `thread` / `note` / `conversation` → the written script (or set `scenes[]`
|
|
171
|
+
for the podcast shape, or pass the readable `script` string).
|
|
172
|
+
`path` = `working/review/<name>` (upload the preview asset first via `get_upload_url`); `url`
|
|
173
|
+
works too. **Label every ingredient** ("Hook image", "End card", "Voiceover", "Background
|
|
174
|
+
music", "HER"). This writes NO render and costs NO credits — it populates the review panel.
|
|
175
|
+
3. **STOP and ask the user to approve the ingredients in THIS Claude Code session.** Do not render
|
|
176
|
+
until they say go. If they want changes, regenerate the affected ingredient, call
|
|
177
|
+
`update_ad_project_script` again, and re-ask. Only AFTER approval do Step 4.
|
|
178
|
+
|
|
179
|
+
## Step 4 — render locally, report stages, publish
|
|
180
|
+
|
|
181
|
+
1. Render per the recipe (Playwright record → ffmpeg stitch → `mix-master` audio). Generate any
|
|
182
|
+
hook / background / end-card assets through the media proxies (below).
|
|
183
|
+
2. Open the row LAST: `submit_render { project_id, kind: "full" }` → keep `render_id`, then
|
|
184
|
+
`update_render_status { render_id, status: "running" }`. The render row tracks status only
|
|
185
|
+
(queued / running / complete / failed) — narrate fine-grained progress with
|
|
186
|
+
`append_project_message` instead.
|
|
187
|
+
3. **MANDATORY final-video review gate — review EVERY finished master before `set_final_render`,
|
|
188
|
+
whatever the format (UGC or not).** The render credit is already spent (`submit_render` in 4.2);
|
|
189
|
+
this gate stands between a rendered master and PINNING/publishing it, so a bad render never gets
|
|
190
|
+
set as final. A master that looks fine on a still can still have a mis-voiced word, a caption
|
|
191
|
+
drifting off its line, a beat out of order, or a deformation — review the actual VIDEO, not
|
|
192
|
+
stills. Run the passes that APPLY to this format:
|
|
193
|
+
- **Audio ↔ script** — any master with SPEECH (VO or native/Seedance voice); **skip for
|
|
194
|
+
music-only / no-speech formats.** `review-ugc-render` is format-agnostic despite the name —
|
|
195
|
+
a deterministic Whisper transcript-vs-script diff, not UGC-specific: persist the approved
|
|
196
|
+
spoken lines to `working/approved-script.txt`, then `gooseworks fetch review-ugc-render` and
|
|
197
|
+
run `review_render.py --video <master>.mp4 --script-file working/approved-script.txt --json
|
|
198
|
+
working/review-verdict.json` (exit 0 PASS / 2 FAIL / 3 ERROR). It blocks a mis-voiced word
|
|
199
|
+
(approved "human-vetted" → "human witted"), a dropped phrase, or silence. It routes Whisper
|
|
200
|
+
through the gooseworks proxy when `OPENAI_BASE_URL` is set; with no backend at all, run
|
|
201
|
+
`fal-ai/whisper` via `fal-proxy` (upload the audio, pass its `get_download_url` as `audio_url`)
|
|
202
|
+
and diff the transcript yourself.
|
|
203
|
+
- **Captions / subtitles** — ANY captioned format (the most common non-UGC defect); **skip for
|
|
204
|
+
UGC/Seedance masters, which carry no subtitle track.** Concrete check: diff the caption file
|
|
205
|
+
you burned (SRT/ASS) against the SAME Whisper transcript + word timings from the audio pass —
|
|
206
|
+
every caption line must match the heard/scripted words and sit within ~0.3s of when they're
|
|
207
|
+
spoken; then in the visual pass below, OCR-read the burned caption off 4–5 sampled frames to
|
|
208
|
+
confirm it's actually on screen at that time and not colliding with a hyperframe or the end
|
|
209
|
+
card. Mismatched text or >0.3s drift fails the gate.
|
|
210
|
+
- **Visual + structure** — always: run the `watch` skill on the master — beat/scene order + SFX,
|
|
211
|
+
the brand's product (not the source's) is shown, the end card has the real wordmark + code, no
|
|
212
|
+
deformation/artifact, duration within ~20% of the source.
|
|
213
|
+
If ANY applicable pass fails, FIX it (regenerate/stitch the offending window, rebuild captions)
|
|
214
|
+
and re-review — only a clean pass proceeds to `set_final_render`. **This gate is universal: it
|
|
215
|
+
runs from the master skill for every format, so a recipe never has to opt in.**
|
|
216
|
+
4. Publish: `get_upload_url { target: { type: "agent", agent_id: ADS_AGENT } }` → PUT the master
|
|
217
|
+
and poster **under the project folder** (see Identity's path-prefix rule) — to
|
|
218
|
+
`agent-config/brands/<brand_slug>/projects/<project_id>/working/final.mp4` and
|
|
219
|
+
`.../working/final-thumb.jpg`. **Always target ADS_AGENT AND use the full project-folder path**
|
|
220
|
+
— a bare `working/final.mp4`, even on the right agent, 404s in the app. Verify servable:
|
|
221
|
+
`get_download_url { target: ADS_AGENT, path: "agent-config/brands/<brand_slug>/projects/<project_id>/working/final.mp4" }`
|
|
222
|
+
must return a non-empty URL (curl it for HTTP 200).
|
|
223
|
+
Then `update_render_status { render_id, status: "complete", output_url, thumbnail_url }` where
|
|
224
|
+
**output_url MUST be the durable render-file URL**
|
|
225
|
+
`/api/ads/projects/<project_id>/render-file?path=working/final.mp4` (the app re-presigns it on
|
|
226
|
+
every view) — NEVER a raw proxy/CDN URL (those expire). Same for `thumbnail_url`.
|
|
227
|
+
5. `set_final_render { project_id, render_id }` to pin it, then return the `app_url` +
|
|
228
|
+
`brand_url` (from the project/links) verbatim. Never end on just "done" or a file path.
|
|
229
|
+
|
|
230
|
+
Narrate each long step in one line via `append_project_message { project_id, role: "agent",
|
|
231
|
+
content }` — never sit silent on a queue > 90s.
|
|
232
|
+
|
|
233
|
+
## Media generation — the GooseWorks proxies (queue loop)
|
|
234
|
+
|
|
235
|
+
Media APIs go through GooseWorks proxies with your agent token; do NOT use an SDK's default host
|
|
236
|
+
(your token isn't a FAL/ElevenLabs token → 401). Base = `<api_base>/api/internal/<proxy>`; pass
|
|
237
|
+
`?token=<api_key>&agent_id=<agent_id>&project_id=<project_id>` (agent_id bills the Ads agent;
|
|
238
|
+
`project_id` = the id of the project you're rendering — it attributes this generation's credits to
|
|
239
|
+
that ad project so the user sees per-project spend in the app. ALWAYS pass it). FAL = `fal-proxy`
|
|
240
|
+
(+ `fal-storage-proxy` to host a local image and get a CDN URL); ElevenLabs = `elevenlabs-proxy`
|
|
241
|
+
(VO / music bed).
|
|
242
|
+
|
|
243
|
+
**FAL queue gotcha** (#1 waste of generations): submit returns `status_url`/`response_url` on
|
|
244
|
+
`queue.fal.run` (the real host, not the proxy). Polling those 401s forever — rewrite their host
|
|
245
|
+
to the proxy base (keep the path), re-add `?token=&agent_id=`. Only the final `*.fal.media`
|
|
246
|
+
image is a real public URL. Helper:
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
import json, os, pathlib, time, requests
|
|
250
|
+
from urllib.parse import urlparse
|
|
251
|
+
|
|
252
|
+
def _cfg():
|
|
253
|
+
c = json.loads(pathlib.Path(os.path.expanduser("~/.gooseworks/credentials.json")).read_text())
|
|
254
|
+
return c["api_base"].rstrip("/"), c["api_key"], c.get("agent_id")
|
|
255
|
+
|
|
256
|
+
def _params(tok, agent, project_id=None):
|
|
257
|
+
p = {"token": tok}
|
|
258
|
+
if agent: p["agent_id"] = agent
|
|
259
|
+
if project_id: p["project_id"] = project_id # attributes the spend to this ad project
|
|
260
|
+
return p
|
|
261
|
+
|
|
262
|
+
def fal_generate(model_path, payload, project_id=None, timeout_s=180, poll_s=3):
|
|
263
|
+
"""model_path e.g. 'fal-ai/nano-banana-2/edit' (the recipe names the model).
|
|
264
|
+
Pass project_id = the ad project you're rendering so credits attribute to it.
|
|
265
|
+
Returns the result image URL (a public *.fal.media CDN URL)."""
|
|
266
|
+
api_base, tok, agent = _cfg()
|
|
267
|
+
base = api_base + "/api/internal/fal-proxy"
|
|
268
|
+
sub = requests.post(f"{base}/{model_path}", params=_params(tok, agent, project_id), json=payload).json()
|
|
269
|
+
to_proxy = lambda u: base + urlparse(u).path
|
|
270
|
+
status_url, response_url = to_proxy(sub["status_url"]), to_proxy(sub["response_url"])
|
|
271
|
+
deadline = time.time() + timeout_s
|
|
272
|
+
while time.time() < deadline:
|
|
273
|
+
st = requests.get(status_url, params=_params(tok, agent, project_id)).json()
|
|
274
|
+
if st.get("status") == "COMPLETED":
|
|
275
|
+
return requests.get(response_url, params=_params(tok, agent, project_id)).json()["images"][0]["url"]
|
|
276
|
+
if st.get("status") in ("FAILED", "ERROR"):
|
|
277
|
+
raise RuntimeError(f"FAL failed: {st}")
|
|
278
|
+
time.sleep(poll_s)
|
|
279
|
+
raise TimeoutError("FAL polling exceeded timeout")
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
ElevenLabs (VO / music) is the same shape against `<api_base>/api/internal/elevenlabs-proxy`
|
|
283
|
+
with `?token=&agent_id=&project_id=`. Feed FAL a local image by storing it (`get_upload_url`) and passing its
|
|
284
|
+
`get_download_url` presigned URL as an `image_urls` / `audio_url` entry — this is the reliable
|
|
285
|
+
path. (`fal-storage-proxy` may 404 depending on the install; don't block on it — prefer the
|
|
286
|
+
`get_download_url` presigned URL.)
|
|
287
|
+
|
|
288
|
+
## Rules
|
|
289
|
+
|
|
290
|
+
- **MCP + ffmpeg + Playwright required** — run `gooseworks doctor` in Phase 0; stop with the
|
|
291
|
+
exact fix it prints if anything is ✗.
|
|
292
|
+
- **Prepare ALL ingredients first** (script + every visual: image(s) + end card + whatever else
|
|
293
|
+
the template needs), mirror the whole set with `update_ad_project_script`, and get the user's
|
|
294
|
+
approval in-session BEFORE rendering — always (review-once).
|
|
295
|
+
- **Never assemble the full video before approval.** The review shows the individual pieces, not a
|
|
296
|
+
stitched/composited cut; do not add a "full cascade" / finished-video clip as a review ingredient
|
|
297
|
+
(GOOSE-2542). The assembled video is produced only in Step 4, after the user approves.
|
|
298
|
+
- **submit_render LAST**; `output_url` = the durable render-file URL, never a CDN URL.
|
|
299
|
+
- **Always pass `project_id` on media-proxy calls** (fal / ElevenLabs) so the credits attribute
|
|
300
|
+
to this ad project — that's what lets the user see per-project spend in the app.
|
|
301
|
+
- **Verify a real, non-empty MP4** (watch it) before marking the render complete.
|
|
302
|
+
- **Reuse the brand** when its research is complete; never re-research.
|
|
303
|
+
- On a hard error (auth/quota/model/timeout) set the render `failed` with a short
|
|
304
|
+
`error_message` and stop — don't ship the source unchanged.
|
|
305
|
+
- Always end a successful run with `app_url` + `brand_url`, verbatim.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gooseworks
|
|
3
|
+
slug: gooseworks
|
|
3
4
|
description: >
|
|
4
5
|
GooseWorks data toolkit. Search and scrape Twitter/X, Reddit, LinkedIn, websites, and the web.
|
|
5
6
|
Find people, emails, and company info. Enrich contacts and companies.
|
|
@@ -7,16 +8,10 @@ description: >
|
|
|
7
8
|
LinkedIn scraping: extract post engagers, commenters, profile data, and job postings.
|
|
8
9
|
Reach for it when you need data at scale, sources behind auth, or a specific provider — not as
|
|
9
10
|
a replacement for your built-in web search/fetch on quick, one-off lookups.
|
|
11
|
+
category: general
|
|
10
12
|
version: 1.0.0
|
|
11
13
|
author: GooseWorks
|
|
12
14
|
tags: [gooseworks, data, scraping, search, reddit, twitter, linkedin, email, people, research, gtm, leads, prospecting]
|
|
13
|
-
homepage: https://github.com/gooseworks-ai/gooseworks
|
|
14
|
-
metadata:
|
|
15
|
-
clawdbot:
|
|
16
|
-
emoji: "\U0001F9AE"
|
|
17
|
-
primaryEnv: GOOSEWORKS_API_KEY
|
|
18
|
-
requires:
|
|
19
|
-
env: [GOOSEWORKS_API_KEY]
|
|
20
15
|
---
|
|
21
16
|
|
|
22
17
|
# GooseWorks
|
|
@@ -33,7 +28,7 @@ Before anything else, check whether the request belongs to a specialized domain.
|
|
|
33
28
|
| --- | --- | --- |
|
|
34
29
|
| Remix/make an ad, research a brand for ads, OR analyze ad performance — Meta/Google ad campaigns, creative fatigue, CAC/lead quality, competitor ad intel, ad angles & hooks | **`goose-ads`** | Installed locally as an entry skill. Just use it. If unavailable, run `gooseworks install --claude`. |
|
|
35
30
|
| Charts, infographics, slides, social graphics, branded visual designs from a style/format | **`goose-graphics`** | If installed locally, use it. Otherwise `gooseworks fetch goose-graphics` (or `gooseworks install --claude --with goose-graphics`). |
|
|
36
|
-
|
|
|
31
|
+
| Make a **video** ad — remix a video ad template (e.g. iMessage chat-reveal), or "make the video for project <id>" | **`goose-video`** | Installed locally as an entry skill. Just use it. If unavailable, run `gooseworks install --claude`. |
|
|
37
32
|
| Anything else — scraping, research, lead gen, enrichment, any data lookup | (stay here) | Follow "How to Use" below. |
|
|
38
33
|
|
|
39
34
|
Examples — all of these route to `goose-ads`, not the data flow: "remix this ad with project id 123", "make an ad for my product", "research my brand", "why is my Meta campaign underperforming", "which creatives should I cut".
|
|
@@ -42,6 +37,18 @@ Examples — all of these route to `goose-ads`, not the data flow: "remix this a
|
|
|
42
37
|
|
|
43
38
|
All commands below auto-load credentials from `~/.gooseworks/credentials.json`. If a command exits with "Not logged in", tell the user to run: `npx gooseworks login`. To log out: `npx gooseworks logout`.
|
|
44
39
|
|
|
40
|
+
### CLI-free environments (cowork / headless)
|
|
41
|
+
|
|
42
|
+
If the `gooseworks` CLI binary isn't available (e.g. Anthropic cowork) but the
|
|
43
|
+
`mcp__gooseworks__*` tools are connected, use the MCP equivalents instead of shelling out:
|
|
44
|
+
- `gooseworks search <q>` → the **`search_skills`** MCP tool.
|
|
45
|
+
- `gooseworks fetch <slug>` → the **`fetch_skill`** MCP tool (same content/scripts/files/deps).
|
|
46
|
+
- `gooseworks credits` → the **`get_ad_credits`** MCP tool.
|
|
47
|
+
|
|
48
|
+
Discovery and fetching a skill's instructions work fully CLI-free this way. Note: the paid data
|
|
49
|
+
proxy (`gooseworks call <provider> <path>`) still requires the CLI for now — if a task needs it
|
|
50
|
+
and no CLI is present, tell the user that step must run where the `gooseworks` CLI is installed.
|
|
51
|
+
|
|
45
52
|
To check credit balance:
|
|
46
53
|
```bash
|
|
47
54
|
gooseworks credits
|
|
@@ -81,11 +88,11 @@ If the response includes `dependencySkills` (non-empty array), set up each depen
|
|
|
81
88
|
### Step 4: Set up and run the skill
|
|
82
89
|
Follow the instructions in the skill's `content` field. **Save ALL files from both `scripts` AND `files` before running anything:**
|
|
83
90
|
|
|
84
|
-
> **Credential translation rule:** Individual skill instructions may
|
|
85
|
-
> -
|
|
86
|
-
> -
|
|
87
|
-
> -
|
|
88
|
-
> -
|
|
91
|
+
> **Credential translation rule:** Individual skill instructions may contain a legacy `## Setup` block with `export GOOSEWORKS_API_KEY=$(python3 ...)` and raw `curl` commands. **Replace those with the clean equivalents below.**
|
|
92
|
+
> - **Credentials (only needed before running Python scripts, NOT before gooseworks commands):** replace the python one-liner exports with `eval $(gooseworks env)`. Skip entirely if you are only using `gooseworks call` — it loads credentials automatically.
|
|
93
|
+
> - **Orthogonal run:** replace `curl ... /v1/proxy/orthogonal/run ... -d '{"api":"X","path":"/Y","body":{...}}'` with `gooseworks call X /Y --body='{...}'`
|
|
94
|
+
> - **Direct proxy:** replace `curl ... /v1/proxy/<provider>/<path> ... -d '{...}'` with `gooseworks call <provider> <path> --body='{...}'`
|
|
95
|
+
> - **Orthogonal search:** replace `curl ... /v1/proxy/orthogonal/search ... -d '{"prompt":"..."}'` with `gooseworks orthogonal find "..."`
|
|
89
96
|
|
|
90
97
|
1. Save each script from `scripts` to `/tmp/gooseworks-scripts/<slug>/scripts/` — **NEVER save scripts into the user's project directory**
|
|
91
98
|
2. **IMPORTANT: Also save everything from `files`** — these contain required modules (like `tools/apify_guard.py`) that scripts import at runtime:
|
|
@@ -174,3 +181,4 @@ The `gooseworks` CLI sends authenticated requests (Bearer `GOOSEWORKS_API_KEY`)
|
|
|
174
181
|
4. **Parse JSON responses** and present data in a readable format to the user
|
|
175
182
|
5. **When running scripts**: save to `/tmp/gooseworks-scripts/`, install pip deps, then execute. NEVER pollute the user's project directory
|
|
176
183
|
6. **Output files default to `~/Gooseworks/`** — always confirm with the user before saving
|
|
184
|
+
7. **Prefer `gooseworks call` over raw curl** — if it returns an error, first fix the parameters (check types, required fields, format) and retry. Only fall back to raw curl if you have strong reason to believe it is a CLI bug, not a parameter issue.
|