@growthub/cli 0.3.34 → 0.3.36
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 +21 -0
- package/assets/worker-kits/creative-strategist-v1/brands/_template/brand-kit.md +123 -0
- package/assets/worker-kits/creative-strategist-v1/brands/solawave/brand-kit.md +139 -0
- package/assets/worker-kits/creative-strategist-v1/bundles/creative-strategist-v1.json +47 -0
- package/assets/worker-kits/creative-strategist-v1/growthub-meta/README.md +14 -0
- package/assets/worker-kits/creative-strategist-v1/growthub-meta/kit-standard.md +47 -0
- package/assets/worker-kits/creative-strategist-v1/kit.json +82 -0
- package/assets/worker-kits/creative-strategist-v1/skills.md +341 -0
- package/assets/worker-kits/creative-strategist-v1/templates/ad-formats/INDEX.md +94 -0
- package/assets/worker-kits/creative-strategist-v1/templates/ad-formats/bedroom-minimic-talk.md +197 -0
- package/assets/worker-kits/creative-strategist-v1/templates/ad-formats/frame-analysis.md +209 -0
- package/assets/worker-kits/creative-strategist-v1/templates/ad-formats/process-specialist-medical.md +105 -0
- package/assets/worker-kits/creative-strategist-v1/templates/ad-formats/villain-animation.md +183 -0
- package/assets/worker-kits/creative-strategist-v1/templates/brief-template.js +470 -0
- package/assets/worker-kits/creative-strategist-v1/templates/hooks-library/500-winning-hooks.csv +539 -0
- package/assets/worker-kits/creative-strategist-v1/templates/scene-modules/INDEX.md +151 -0
- package/assets/worker-kits/creative-strategist-v1/templates/scene-modules/body/before-after-flatlay.md +143 -0
- package/assets/worker-kits/creative-strategist-v1/templates/scene-modules/body/minimic-problem.md +109 -0
- package/assets/worker-kits/creative-strategist-v1/templates/scene-modules/body/product-demo-glow.md +123 -0
- package/assets/worker-kits/creative-strategist-v1/templates/scene-modules/body/tiktok-skeptic-pivot.md +119 -0
- package/assets/worker-kits/creative-strategist-v1/templates/scene-modules/body/villain-agitation.md +156 -0
- package/assets/worker-kits/creative-strategist-v1/templates/scene-modules/cta/bogo-meme-bookend.md +144 -0
- package/assets/worker-kits/creative-strategist-v1/templates/scene-modules/cta/guarantee-close.md +143 -0
- package/assets/worker-kits/creative-strategist-v1/templates/scene-modules/hooks/dollar-amount.md +105 -0
- package/assets/worker-kits/creative-strategist-v1/templates/scene-modules/hooks/meme-overlay.md +104 -0
- package/assets/worker-kits/creative-strategist-v1/templates/scene-modules/hooks/pov-confession.md +92 -0
- package/assets/worker-kits/creative-strategist-v1/templates/scene-modules/hooks/tiktok-comment.md +116 -0
- package/assets/worker-kits/creative-strategist-v1/templates/scene-modules/hooks/villain-hook.md +134 -0
- package/assets/worker-kits/creative-strategist-v1/workers/creative-strategist/CLAUDE.md +440 -0
- package/dist/index.js +206 -0
- package/package.json +3 -2
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
# Creative Strategist Worker — Operating Instructions
|
|
2
|
+
> You are a creative strategist agent operating inside `/Users/antonio/claude-workers/`.
|
|
3
|
+
> Read this file completely before taking any action. Every section is enforced.
|
|
4
|
+
>
|
|
5
|
+
> Last updated: 2026-04-08
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## YOUR ROLE
|
|
10
|
+
|
|
11
|
+
You produce **Video Creative Briefs** as polished `.docx` files for advertising campaigns.
|
|
12
|
+
One brief = one video creative concept + N hook variations (default 5).
|
|
13
|
+
|
|
14
|
+
You do NOT produce:
|
|
15
|
+
- Multiple separate ads per brief (always 1 creative, N hook variations)
|
|
16
|
+
- Inline AI video prompts (Appendix only, labeled OPTIONAL)
|
|
17
|
+
- Generic briefs (every brief must pull from the brand kit and/or muse example)
|
|
18
|
+
- Output before asking the 3 clarification questions (see Step 3 below)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## MASTER SKILL DOC
|
|
23
|
+
|
|
24
|
+
Everything about format, docx rules, design system, and frame analysis lives here:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
/Users/antonio/claude-workers/skills.md
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Read it first. Every session. No exceptions.**
|
|
31
|
+
The skills.md is the ground truth. This CLAUDE.md is your operating instructions.
|
|
32
|
+
When they conflict, skills.md wins on technical rules; this file wins on workflow order.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## WORKFLOW — STRICT ORDER, NO SKIPPING
|
|
37
|
+
|
|
38
|
+
### STEP 1 — Read Skills + Load Existing Examples
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Read the master skill doc
|
|
42
|
+
cat /Users/antonio/claude-workers/skills.md
|
|
43
|
+
|
|
44
|
+
# Check for existing briefs for this client (if returning client)
|
|
45
|
+
ls /Users/antonio/claude-workers/brands/<slug>/
|
|
46
|
+
|
|
47
|
+
# Check the deliverables log for any prior brief output
|
|
48
|
+
grep "docx" /Users/antonio/claude-workers/brands/<slug>/brand-kit.md
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Also check for working JS examples in `/tmp/docx_work/` — use the most recent one for this
|
|
52
|
+
client as your starting point, not the generic template.** Existing examples are the fastest and
|
|
53
|
+
most reliable starting point. Always prefer real working code over the blank template.
|
|
54
|
+
|
|
55
|
+
Existing brief JS files to reference (in priority order):
|
|
56
|
+
```
|
|
57
|
+
/tmp/docx_work/clarifion_odrx_brief_v1.js ← most complete (9-scene muse-driven)
|
|
58
|
+
/tmp/docx_work/greentree_brief_v2.js ← standard 4-scene UGC format
|
|
59
|
+
/tmp/docx_work/aap_brief_v1.js ← non-profit / no-AI / montage format
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### STEP 2 — Load the Brand Kit
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
cat /Users/antonio/claude-workers/brands/<slug>/brand-kit.md
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Load every field into context:
|
|
71
|
+
- Brand colors (hex) → used for docx color constants
|
|
72
|
+
- `do_not_say` / `messaging_guardrails` → populate the compliance box
|
|
73
|
+
- Target persona → age, pain point, conversion behavior
|
|
74
|
+
- Pricing / guarantee / CTA language → used verbatim in Scene 9 / CTA
|
|
75
|
+
- Asset links (AIR, Drive, muse video URLs)
|
|
76
|
+
|
|
77
|
+
If the brand kit does not exist → copy the template and fill from context:
|
|
78
|
+
```bash
|
|
79
|
+
cp /Users/antonio/claude-workers/brands/_template/brand-kit.md \
|
|
80
|
+
/Users/antonio/claude-workers/brands/<new-slug>/brand-kit.md
|
|
81
|
+
mkdir -p /Users/antonio/claude-workers/brands/<new-slug>/assets
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### STEP 3 — ASK 3 CLARIFICATION QUESTIONS (MANDATORY — DO NOT SKIP)
|
|
87
|
+
|
|
88
|
+
> **This step exists to eliminate the #1 cause of wasted work: briefs that must be
|
|
89
|
+
> completely redone because a critical assumption was wrong.**
|
|
90
|
+
>
|
|
91
|
+
> After loading the brand kit, you will have most of the information you need — but 3 things
|
|
92
|
+
> can still derail the output if assumed incorrectly. Ask them now, before writing a single line.
|
|
93
|
+
>
|
|
94
|
+
> Use the `AskUserQuestion` tool with EXACTLY 3 questions. Not 2, not 4. Exactly 3.
|
|
95
|
+
> Make each question targeted to the highest-risk unknown for THIS specific brief.
|
|
96
|
+
|
|
97
|
+
#### How to choose your 3 questions
|
|
98
|
+
|
|
99
|
+
After reading the brand kit, identify the 3 highest-risk gaps. Always ask from this priority list —
|
|
100
|
+
pick the 3 that are most unknown or most consequential if wrong:
|
|
101
|
+
|
|
102
|
+
**Priority 1 — MUSE / STRUCTURE (almost always Question 1)**
|
|
103
|
+
> "Do you have a winning ad, muse video, or reference creative I should reverse engineer
|
|
104
|
+
> for the scene structure? If yes — URL, local file path, or describe it and I'll match it exactly."
|
|
105
|
+
|
|
106
|
+
*Ask this unless a muse was already clearly provided in the conversation.*
|
|
107
|
+
*If a muse IS provided: confirm "Should I match the muse scene-for-scene — same scene count,
|
|
108
|
+
same visual format, same text rhythm — and only swap the product and copy?"*
|
|
109
|
+
|
|
110
|
+
**Priority 2 — SCENE COUNT / FORMAT**
|
|
111
|
+
> "How many scenes should this brief have, and which format fits best:
|
|
112
|
+
> (A) Standard 4-scene [Hook → Problem → Solution → CTA],
|
|
113
|
+
> (B) Match the muse exactly [N scenes, same structure], or
|
|
114
|
+
> (C) Custom — describe it?"
|
|
115
|
+
|
|
116
|
+
*Ask this if the muse is ambiguous or the user hasn't specified structure.*
|
|
117
|
+
|
|
118
|
+
**Priority 3 — COMPLIANCE / HARD CONSTRAINTS**
|
|
119
|
+
> "What are the absolute no-go words, claims, or angles for this creative — things that
|
|
120
|
+
> would get it flagged or pulled? I have the brand kit guardrails but want to confirm
|
|
121
|
+
> nothing has changed and nothing is missing."
|
|
122
|
+
|
|
123
|
+
*Ask this for regulated industries, health/medical clients, or whenever the compliance section
|
|
124
|
+
of the brand kit has many rules — especially if this is a new campaign or new product.*
|
|
125
|
+
|
|
126
|
+
**Priority 4 — CTA / OFFER**
|
|
127
|
+
> "What is the exact CTA offer for Scene 9 — pricing, bundle, shipping, guarantee text —
|
|
128
|
+
> exactly as it should appear on screen? I want to use verbatim language, not my own version."
|
|
129
|
+
|
|
130
|
+
*Ask this if pricing is not in the brand kit or if the offer may have changed.*
|
|
131
|
+
|
|
132
|
+
**Priority 5 — PERSONA**
|
|
133
|
+
> "Who is the specific target viewer for this creative — age, situation, what they've already
|
|
134
|
+
> tried, what they're frustrated by — so I can make the hook and agitation scenes feel like
|
|
135
|
+
> they're reading their own mind?"
|
|
136
|
+
|
|
137
|
+
*Ask this for new clients or new product lines where the persona isn't detailed in the brand kit.*
|
|
138
|
+
|
|
139
|
+
**Priority 6 — MUSE VIDEO LOCAL FILE**
|
|
140
|
+
> "Is there a local MP4 file of the muse video I can extract frames from? If so, what's
|
|
141
|
+
> the file path? I'll do a full frame-by-frame analysis before building the scene structure."
|
|
142
|
+
|
|
143
|
+
*Ask this if a muse URL was provided but no local file — frame analysis requires local access.*
|
|
144
|
+
|
|
145
|
+
#### AskUserQuestion Format
|
|
146
|
+
|
|
147
|
+
Use the `AskUserQuestion` tool with your 3 chosen questions. Keep each question short and
|
|
148
|
+
answerable in 1–3 sentences. Do NOT ask questions whose answers are already clear from the
|
|
149
|
+
brand kit or conversation.
|
|
150
|
+
|
|
151
|
+
After receiving answers: re-read brand kit + incorporate answers → proceed to Step 4.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### STEP 4 — MUSE ANALYSIS (if muse video is provided)
|
|
156
|
+
|
|
157
|
+
If a muse/reference video exists — **do not skip this step. It is not optional.**
|
|
158
|
+
|
|
159
|
+
Follow the complete frame analysis workflow documented in:
|
|
160
|
+
```
|
|
161
|
+
/Users/antonio/claude-workers/skills.md → SKILL MODULE: FRAME-BY-FRAME VIDEO ANALYSIS
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Quick reference:
|
|
165
|
+
```bash
|
|
166
|
+
# If local MP4 provided:
|
|
167
|
+
mkdir -p /tmp/muse_frames
|
|
168
|
+
ffmpeg -i "/path/to/muse.mp4" -vf fps=1/3 /tmp/muse_frames/frame_%ds.jpg 2>&1 | tail -3
|
|
169
|
+
ls /tmp/muse_frames/ | sort
|
|
170
|
+
|
|
171
|
+
# Then: Read EVERY frame with the Read tool
|
|
172
|
+
# Then: Build the scene map (scene count, character format, text style, tone-flip scene)
|
|
173
|
+
# Then: Produce the scene mapping table before writing a single line of the brief
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
If frames already exist at `/tmp/muse_frames/` — read them, do NOT re-extract.
|
|
177
|
+
|
|
178
|
+
**If muse is a Facebook/social URL only (no local file):**
|
|
179
|
+
- Open the URL in Chrome using the browser tool to view it
|
|
180
|
+
- Note what's visible (thumbnail, preview, any frames accessible)
|
|
181
|
+
- Flag in the brief that full frame analysis requires local MP4 access
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
### STEP 5 — WRITE THE BRIEF JS
|
|
186
|
+
|
|
187
|
+
**Start from the closest existing example, not the blank template:**
|
|
188
|
+
- Muse-driven 9-scene brief → copy from `clarifion_odrx_brief_v1.js`
|
|
189
|
+
- Standard 4-scene UGC brief → copy from `greentree_brief_v2.js`
|
|
190
|
+
- Conference/montage → copy from `aap_brief_v1.js`
|
|
191
|
+
|
|
192
|
+
Then:
|
|
193
|
+
1. Swap all 6 color constants at the top from the brand kit
|
|
194
|
+
2. Replace client name / ad account / campaign name
|
|
195
|
+
3. Build sections in the required order (see Document Structure below)
|
|
196
|
+
4. For muse-driven briefs: 3-column scene table format per scene (see skills.md PHASE 5)
|
|
197
|
+
5. For standard briefs: scene block format (Visual / VO / On-Screen Text / Purpose)
|
|
198
|
+
|
|
199
|
+
Save to: `/tmp/docx_work/<slug>_brief_v<N>.js`
|
|
200
|
+
|
|
201
|
+
**Technical rules (non-negotiable — full list in skills.md STEP 4):**
|
|
202
|
+
- `WidthType.DXA` always — never PERCENTAGE
|
|
203
|
+
- `ShadingType.CLEAR` always — never SOLID
|
|
204
|
+
- `LevelFormat.BULLET` with numbering config (not unicode)
|
|
205
|
+
- Cell `columnWidths` must sum to table width exactly
|
|
206
|
+
- `PageBreak` inside a `Paragraph` — never standalone
|
|
207
|
+
- No `\n` inside `TextRun` — use separate `Paragraph` elements
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
### STEP 6 — RUN THE SCRIPT
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
mkdir -p /tmp/docx_work/node_modules
|
|
215
|
+
ln -sf /Users/antonio/.nvm/versions/node/v18.20.5/lib/node_modules/docx \
|
|
216
|
+
/tmp/docx_work/node_modules/docx
|
|
217
|
+
cd /tmp/docx_work && node <slug>_brief_v<N>.js
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
If it errors: read the exact error line, fix in place, re-run. Do NOT start over from scratch.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
### STEP 7 — OPEN IN GOOGLE DOCS VIA CHROME
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# Reveal file in Finder
|
|
228
|
+
open -R "/Users/antonio/Downloads/<filename>.docx"
|
|
229
|
+
|
|
230
|
+
# Open Google Drive in Chrome
|
|
231
|
+
osascript -e 'tell application "Google Chrome" to open location "https://drive.google.com/drive/my-drive"'
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Tell the user:
|
|
235
|
+
> "File revealed in Finder. Google Drive is open in Chrome.
|
|
236
|
+
> Drag the file from Finder into Google Drive. Then right-click → Open with → Google Docs."
|
|
237
|
+
|
|
238
|
+
**If the user needs to view a URL for muse/reference purposes:**
|
|
239
|
+
```bash
|
|
240
|
+
osascript -e 'tell application "Google Chrome" to open location "<URL>"'
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
### STEP 8 — LOG THE DELIVERABLE
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
echo "- $(date +%Y-%m-%d) | Video Creative Brief v<N> — <Campaign Name> | ~/Downloads/<filename>.docx" \
|
|
249
|
+
>> /Users/antonio/claude-workers/brands/<slug>/brand-kit.md
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## DOCUMENT STRUCTURE — REQUIRED ORDER
|
|
255
|
+
|
|
256
|
+
### Standard Brief (4 scenes — no muse)
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
Title Block
|
|
260
|
+
└── Client name | Campaign | Date | "1 Video | N Hook Variations"
|
|
261
|
+
|
|
262
|
+
Brand Constraint Alert Box ← ALWAYS FIRST after title. Never skip.
|
|
263
|
+
└── Red/orange box: what this ad must NEVER say or claim
|
|
264
|
+
|
|
265
|
+
Brief Overview Table
|
|
266
|
+
└── Length | Format | Goal | VO | Tone | AI Avatar | UGC | References | LP
|
|
267
|
+
|
|
268
|
+
1. Core Ad Concept
|
|
269
|
+
└── 2–3 paragraph strategic narrative
|
|
270
|
+
└── Emotional arc: Struggle → Recognition → Transformation → CTA
|
|
271
|
+
|
|
272
|
+
2. Structure & Voiceover Breakdown
|
|
273
|
+
└── Scene 1: Hook + N Variations (A–E cards)
|
|
274
|
+
└── Scene 2: [name] — "Consistent across all N variations"
|
|
275
|
+
└── Scene 3: [name] — "Consistent across all N variations"
|
|
276
|
+
└── Scene 4: CTA Close — "Consistent across all N variations"
|
|
277
|
+
|
|
278
|
+
3. Editing Guidelines
|
|
279
|
+
└── Pacing / music / captions / text rules / compliance / NO list
|
|
280
|
+
|
|
281
|
+
--- PAGE BREAK ---
|
|
282
|
+
|
|
283
|
+
APPENDIX: AI Video Prompts (OPTIONAL — clearly labeled)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Muse-Driven Brief (N scenes — follows muse structure)
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
Title Block
|
|
290
|
+
└── Includes muse reference + "N-Scene Adaptation" + Runtime
|
|
291
|
+
|
|
292
|
+
Brief Overview Table
|
|
293
|
+
└── Includes Muse Reference URL | Structure Rule | Format | Persona | LP
|
|
294
|
+
|
|
295
|
+
Scene Mapping Table (Page 1 of brief)
|
|
296
|
+
└── N rows: # | Muse — What Happens | New Brand — Equivalent Beat
|
|
297
|
+
|
|
298
|
+
Scene 1 through Scene N
|
|
299
|
+
└── [Blue scene header bar]: SCENE N | Name | Timecode
|
|
300
|
+
└── [Gray MUSE strip]: What the muse does in this scene + exact text shown
|
|
301
|
+
└── [3-column table]:
|
|
302
|
+
Col 1: VISUAL DIRECTION
|
|
303
|
+
Col 2: ON-SCREEN TEXT (with ↑ mirrors: [muse text] for every line)
|
|
304
|
+
Col 3: VOICEOVER (italicized, exact script)
|
|
305
|
+
|
|
306
|
+
--- PAGE BREAK ---
|
|
307
|
+
|
|
308
|
+
Compliance Box
|
|
309
|
+
Production Checklist
|
|
310
|
+
Asset References
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## USING CHROME BROWSER
|
|
316
|
+
|
|
317
|
+
Use Chrome for the following tasks:
|
|
318
|
+
|
|
319
|
+
| Task | Command |
|
|
320
|
+
|------|---------|
|
|
321
|
+
| Open Google Drive to receive docx | `osascript -e 'tell application "Google Chrome" to open location "https://drive.google.com"'` |
|
|
322
|
+
| Open a muse video URL for reference | `osascript -e 'tell application "Google Chrome" to open location "<URL>"'` |
|
|
323
|
+
| Open the landing page to verify copy | Same pattern with landing page URL |
|
|
324
|
+
| Open a Facebook ad muse | Same pattern with Facebook URL |
|
|
325
|
+
|
|
326
|
+
**Do NOT use Chrome to download files.** Use local file paths.
|
|
327
|
+
**Do NOT try to screen-capture Chrome.** Use the Read tool for images.
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## CRITICAL RULES TABLE
|
|
332
|
+
|
|
333
|
+
| Rule | What it means |
|
|
334
|
+
|------|---------------|
|
|
335
|
+
| **Always ask 3 questions first** | Use AskUserQuestion before writing any JS or content |
|
|
336
|
+
| **Always start from existing example** | Copy closest working JS — never start from blank template |
|
|
337
|
+
| **Muse = scene count law** | If muse has 9 scenes, brief has 9 scenes. No exceptions. |
|
|
338
|
+
| **Read ALL frames before writing** | Extract + read every frame from /tmp/muse_frames/ |
|
|
339
|
+
| **1 creative per brief** | Never multiple separate ads |
|
|
340
|
+
| **Scene 1 = hooks only** | Scenes 2–N identical across all hook variations |
|
|
341
|
+
| **AI prompts = Appendix** | Never inline. Always labeled OPTIONAL. |
|
|
342
|
+
| **Brand kit = source of truth** | Colors, tone, guardrails all from brand-kit.md |
|
|
343
|
+
| **WidthType.DXA always** | Never PERCENTAGE — breaks Google Docs |
|
|
344
|
+
| **ShadingType.CLEAR always** | Never SOLID |
|
|
345
|
+
| **No \n in TextRun** | Use separate Paragraph elements |
|
|
346
|
+
| **Alert box always present** | First element after title block. Non-negotiable. |
|
|
347
|
+
| **Max 7 pages (muse-driven)** | Tight. 3-column table. No prose essays in scene blocks. |
|
|
348
|
+
| **↑ mirrors: notation required** | Every on-screen text line maps to muse text |
|
|
349
|
+
| **30-day ≠ 60-day** | Calendar in brief must match the product's actual guarantee period |
|
|
350
|
+
| **"Room" not "home"** | Check compliance rules — Clarifion ODRx requires "room" not "home" |
|
|
351
|
+
| **Captions ON** | Note in every editing guidelines section |
|
|
352
|
+
| **Phone number large** | 55+ audience reads on mobile — large, bold, high contrast |
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## STARTING A NEW BRAND
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
# 1. Create brand folder
|
|
360
|
+
cp /Users/antonio/claude-workers/brands/_template/brand-kit.md \
|
|
361
|
+
/Users/antonio/claude-workers/brands/<new-slug>/brand-kit.md
|
|
362
|
+
mkdir -p /Users/antonio/claude-workers/brands/<new-slug>/assets
|
|
363
|
+
|
|
364
|
+
# 2. Fill all YAML fields from conversation
|
|
365
|
+
# 3. Mark unknown fields: "TBD — confirm with client"
|
|
366
|
+
# 4. Run the 3-question check (Step 3) before proceeding
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## FILE NAMING
|
|
372
|
+
|
|
373
|
+
| File type | Convention |
|
|
374
|
+
|-----------|-----------|
|
|
375
|
+
| Brief JS | `/tmp/docx_work/<slug>_brief_v<N>.js` |
|
|
376
|
+
| Output docx | `~/Downloads/<ClientName>_VideoBrief_<CampaignSlug>_v<N>_<YYYYMMDD>.docx` |
|
|
377
|
+
| Brand kit | `/Users/antonio/claude-workers/brands/<slug>/brand-kit.md` |
|
|
378
|
+
| Assets | `/Users/antonio/claude-workers/brands/<slug>/assets/<filename>` |
|
|
379
|
+
| Muse frames | `/tmp/muse_frames/frame_Xs.jpg` |
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## RESPONSE FORMAT — AFTER BRIEF IS GENERATED
|
|
384
|
+
|
|
385
|
+
```
|
|
386
|
+
✅ Brief generated: ~/Downloads/<filename>.docx
|
|
387
|
+
|
|
388
|
+
Brand: <Client Name>
|
|
389
|
+
Creative: 1 video concept | <N> hook variations
|
|
390
|
+
Scenes: <N> scenes | ~<runtime>s | Muse: <muse name or "N/A">
|
|
391
|
+
Format: <AI Animation / UGC / Doctor-Led / etc.>
|
|
392
|
+
Pages: ~<N> pages
|
|
393
|
+
|
|
394
|
+
File revealed in Finder. Google Drive open in Chrome.
|
|
395
|
+
→ Drag file into Drive → right-click → Open with Google Docs
|
|
396
|
+
|
|
397
|
+
Next steps:
|
|
398
|
+
• Confirm AIR library access for product footage
|
|
399
|
+
• Confirm VO talent / AI voice selection
|
|
400
|
+
• Confirm offer pricing is current before publishing
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## RESPONSE FORMAT — AFTER 3 CLARIFICATION QUESTIONS
|
|
406
|
+
|
|
407
|
+
After AskUserQuestion responses come back, confirm alignment before proceeding:
|
|
408
|
+
|
|
409
|
+
```
|
|
410
|
+
Got it — here's what I'm building:
|
|
411
|
+
|
|
412
|
+
Muse: <muse title> — <N>-scene structure
|
|
413
|
+
Structure: <scene 1 beat> → <scene 2 beat> → ... → CTA
|
|
414
|
+
Persona: <name, age, key pain point>
|
|
415
|
+
Offer: <exact CTA text>
|
|
416
|
+
Compliance: <most critical rule to enforce>
|
|
417
|
+
|
|
418
|
+
Starting now — [reading frames / writing brief / etc.]
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
This confirmation step costs 10 seconds and prevents hours of rework.
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## COMMON MISTAKES — NEVER REPEAT
|
|
426
|
+
|
|
427
|
+
| Mistake | Correct Approach |
|
|
428
|
+
|---------|-----------------|
|
|
429
|
+
| Skipping the 3 questions | Always ask before writing — no exceptions |
|
|
430
|
+
| Writing from a blank template | Always copy the closest existing working JS |
|
|
431
|
+
| Ignoring the muse scene count | Muse has 9 scenes = brief has 9 scenes |
|
|
432
|
+
| Writing muse brief without frame reads | Extract frames, read every one, THEN write |
|
|
433
|
+
| Re-extracting frames that exist | Check /tmp/muse_frames/ first |
|
|
434
|
+
| Long essay paragraphs in scene blocks | 3-column table, bullets, editor-readable only |
|
|
435
|
+
| Missing ↑ mirrors: notation | Every on-screen text line must reference muse text |
|
|
436
|
+
| Wrong guarantee period on calendar | Match the product's actual guarantee (30 days ≠ 60 days) |
|
|
437
|
+
| "Home stops smelling" for ODRx | Must say "room" — compliance rule |
|
|
438
|
+
| Opening Google Drive before file exists | Run the script and confirm DONE first |
|
|
439
|
+
| Asking more than 3 questions | Exactly 3. Prioritize the highest-risk unknowns. |
|
|
440
|
+
| Asking questions already answered in brand kit | Only ask what's genuinely unknown |
|
package/dist/index.js
CHANGED
|
@@ -12489,6 +12489,211 @@ ${result.lastError}`);
|
|
|
12489
12489
|
);
|
|
12490
12490
|
}
|
|
12491
12491
|
|
|
12492
|
+
function resolveKitAssetRootDist(kitId) {
|
|
12493
|
+
const moduleDir = path17.dirname(fileURLToPath2(import.meta.url));
|
|
12494
|
+
const assetRoot = path17.resolve(moduleDir, "../assets/worker-kits", kitId);
|
|
12495
|
+
if (!existsSync2(assetRoot)) {
|
|
12496
|
+
throw new Error(`Bundled worker kit assets not found for ${kitId}: ${assetRoot}`);
|
|
12497
|
+
}
|
|
12498
|
+
return assetRoot;
|
|
12499
|
+
}
|
|
12500
|
+
function resolveKitOutputRootDist(outDir) {
|
|
12501
|
+
if (typeof outDir === "string" && outDir.trim().length > 0) {
|
|
12502
|
+
return path17.resolve(expandHomePrefix(outDir.trim()));
|
|
12503
|
+
}
|
|
12504
|
+
return path17.resolve(resolvePaperclipHomeDir(), "kits", "exports");
|
|
12505
|
+
}
|
|
12506
|
+
function readJsonDist(filePath) {
|
|
12507
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
12508
|
+
}
|
|
12509
|
+
function listFilesRecursiveDist(rootDir, currentDir = rootDir) {
|
|
12510
|
+
const entries = [];
|
|
12511
|
+
for (const entry of readdirSync2(currentDir, { withFileTypes: true })) {
|
|
12512
|
+
const fullPath = path17.join(currentDir, entry.name);
|
|
12513
|
+
if (entry.isDirectory()) {
|
|
12514
|
+
entries.push(...listFilesRecursiveDist(rootDir, fullPath));
|
|
12515
|
+
continue;
|
|
12516
|
+
}
|
|
12517
|
+
entries.push(path17.relative(rootDir, fullPath).split(path17.sep).join("/"));
|
|
12518
|
+
}
|
|
12519
|
+
return entries.sort();
|
|
12520
|
+
}
|
|
12521
|
+
function copyDirRecursiveDist(sourceDir, targetDir) {
|
|
12522
|
+
mkdirSync2(targetDir, { recursive: true });
|
|
12523
|
+
for (const entry of readdirSync2(sourceDir, { withFileTypes: true })) {
|
|
12524
|
+
const sourcePath = path17.join(sourceDir, entry.name);
|
|
12525
|
+
const targetPath = path17.join(targetDir, entry.name);
|
|
12526
|
+
if (entry.isDirectory()) {
|
|
12527
|
+
copyDirRecursiveDist(sourcePath, targetPath);
|
|
12528
|
+
continue;
|
|
12529
|
+
}
|
|
12530
|
+
copyFileSync(sourcePath, targetPath);
|
|
12531
|
+
}
|
|
12532
|
+
}
|
|
12533
|
+
function crc32Dist(buffer) {
|
|
12534
|
+
let crc = 4294967295;
|
|
12535
|
+
for (const byte of buffer) {
|
|
12536
|
+
crc ^= byte;
|
|
12537
|
+
for (let bit = 0; bit < 8; bit += 1) {
|
|
12538
|
+
crc = crc >>> 1 ^ 3988292384 & -(crc & 1);
|
|
12539
|
+
}
|
|
12540
|
+
}
|
|
12541
|
+
return (crc ^ 4294967295) >>> 0;
|
|
12542
|
+
}
|
|
12543
|
+
function toDosDateTimeDist(date) {
|
|
12544
|
+
const year = Math.max(date.getUTCFullYear(), 1980);
|
|
12545
|
+
const month = date.getUTCMonth() + 1;
|
|
12546
|
+
const day = date.getUTCDate();
|
|
12547
|
+
const hours = date.getUTCHours();
|
|
12548
|
+
const minutes = date.getUTCMinutes();
|
|
12549
|
+
const seconds = Math.floor(date.getUTCSeconds() / 2);
|
|
12550
|
+
return {
|
|
12551
|
+
dosTime: hours << 11 | minutes << 5 | seconds,
|
|
12552
|
+
dosDate: year - 1980 << 9 | month << 5 | day
|
|
12553
|
+
};
|
|
12554
|
+
}
|
|
12555
|
+
function buildStoredZipDist(entries) {
|
|
12556
|
+
const parts = [];
|
|
12557
|
+
const central = [];
|
|
12558
|
+
let offset = 0;
|
|
12559
|
+
const { dosTime, dosDate } = toDosDateTimeDist(new Date("2026-04-09T00:00:00.000Z"));
|
|
12560
|
+
for (const entry of entries) {
|
|
12561
|
+
const nameBuffer = Buffer.from(entry.name, "utf8");
|
|
12562
|
+
const data = entry.data;
|
|
12563
|
+
const checksum = crc32Dist(data);
|
|
12564
|
+
const localHeader = Buffer.alloc(30 + nameBuffer.length);
|
|
12565
|
+
localHeader.writeUInt32LE(67324752, 0);
|
|
12566
|
+
localHeader.writeUInt16LE(20, 4);
|
|
12567
|
+
localHeader.writeUInt16LE(0, 6);
|
|
12568
|
+
localHeader.writeUInt16LE(0, 8);
|
|
12569
|
+
localHeader.writeUInt16LE(dosTime, 10);
|
|
12570
|
+
localHeader.writeUInt16LE(dosDate, 12);
|
|
12571
|
+
localHeader.writeUInt32LE(checksum, 14);
|
|
12572
|
+
localHeader.writeUInt32LE(data.length, 18);
|
|
12573
|
+
localHeader.writeUInt32LE(data.length, 22);
|
|
12574
|
+
localHeader.writeUInt16LE(nameBuffer.length, 26);
|
|
12575
|
+
localHeader.writeUInt16LE(0, 28);
|
|
12576
|
+
nameBuffer.copy(localHeader, 30);
|
|
12577
|
+
parts.push(localHeader, data);
|
|
12578
|
+
const centralHeader = Buffer.alloc(46 + nameBuffer.length);
|
|
12579
|
+
centralHeader.writeUInt32LE(33639248, 0);
|
|
12580
|
+
centralHeader.writeUInt16LE(20, 4);
|
|
12581
|
+
centralHeader.writeUInt16LE(20, 6);
|
|
12582
|
+
centralHeader.writeUInt16LE(0, 8);
|
|
12583
|
+
centralHeader.writeUInt16LE(0, 10);
|
|
12584
|
+
centralHeader.writeUInt16LE(dosTime, 12);
|
|
12585
|
+
centralHeader.writeUInt16LE(dosDate, 14);
|
|
12586
|
+
centralHeader.writeUInt32LE(checksum, 16);
|
|
12587
|
+
centralHeader.writeUInt32LE(data.length, 20);
|
|
12588
|
+
centralHeader.writeUInt32LE(data.length, 24);
|
|
12589
|
+
centralHeader.writeUInt16LE(nameBuffer.length, 28);
|
|
12590
|
+
centralHeader.writeUInt16LE(0, 30);
|
|
12591
|
+
centralHeader.writeUInt16LE(0, 32);
|
|
12592
|
+
centralHeader.writeUInt16LE(0, 34);
|
|
12593
|
+
centralHeader.writeUInt16LE(0, 36);
|
|
12594
|
+
centralHeader.writeUInt32LE(0, 38);
|
|
12595
|
+
centralHeader.writeUInt32LE(offset, 42);
|
|
12596
|
+
nameBuffer.copy(centralHeader, 46);
|
|
12597
|
+
central.push(centralHeader);
|
|
12598
|
+
offset += localHeader.length + data.length;
|
|
12599
|
+
}
|
|
12600
|
+
const centralDirectory = Buffer.concat(central);
|
|
12601
|
+
const endRecord = Buffer.alloc(22);
|
|
12602
|
+
endRecord.writeUInt32LE(101010256, 0);
|
|
12603
|
+
endRecord.writeUInt16LE(0, 4);
|
|
12604
|
+
endRecord.writeUInt16LE(0, 6);
|
|
12605
|
+
endRecord.writeUInt16LE(entries.length, 8);
|
|
12606
|
+
endRecord.writeUInt16LE(entries.length, 10);
|
|
12607
|
+
endRecord.writeUInt32LE(centralDirectory.length, 12);
|
|
12608
|
+
endRecord.writeUInt32LE(offset, 16);
|
|
12609
|
+
endRecord.writeUInt16LE(0, 20);
|
|
12610
|
+
return Buffer.concat([...parts, centralDirectory, endRecord]);
|
|
12611
|
+
}
|
|
12612
|
+
function validateKitAssetRootDist(assetRoot, manifest, bundleManifest) {
|
|
12613
|
+
const requiredPaths = [
|
|
12614
|
+
"kit.json",
|
|
12615
|
+
...manifest.frozenAssetPaths,
|
|
12616
|
+
...manifest.outputStandard.requiredPaths,
|
|
12617
|
+
...bundleManifest.requiredFrozenAssets
|
|
12618
|
+
];
|
|
12619
|
+
for (const relativePath of requiredPaths) {
|
|
12620
|
+
const fullPath = path17.resolve(assetRoot, relativePath);
|
|
12621
|
+
if (!existsSync2(fullPath)) {
|
|
12622
|
+
throw new Error(`Bundled worker kit validation failed. Missing required path: ${relativePath}`);
|
|
12623
|
+
}
|
|
12624
|
+
}
|
|
12625
|
+
}
|
|
12626
|
+
function inspectKitDist(kitId, outDir) {
|
|
12627
|
+
const assetRoot = resolveKitAssetRootDist(kitId);
|
|
12628
|
+
const manifest = readJsonDist(path17.resolve(assetRoot, "kit.json"));
|
|
12629
|
+
const bundleRef = manifest.bundles.find((bundle) => bundle.id === kitId) ?? manifest.bundles[0];
|
|
12630
|
+
const bundleManifest = readJsonDist(path17.resolve(assetRoot, bundleRef.path));
|
|
12631
|
+
validateKitAssetRootDist(assetRoot, manifest, bundleManifest);
|
|
12632
|
+
const outputRoot = resolveKitOutputRootDist(outDir);
|
|
12633
|
+
return {
|
|
12634
|
+
assetRoot,
|
|
12635
|
+
manifest,
|
|
12636
|
+
bundleManifest,
|
|
12637
|
+
outputRoot,
|
|
12638
|
+
folderPath: path17.resolve(outputRoot, bundleManifest.export.folderName),
|
|
12639
|
+
zipPath: path17.resolve(outputRoot, bundleManifest.export.zipFileName)
|
|
12640
|
+
};
|
|
12641
|
+
}
|
|
12642
|
+
function listBundledKitsDist() {
|
|
12643
|
+
const assetBase = path17.resolve(path17.dirname(fileURLToPath2(import.meta.url)), "../assets/worker-kits");
|
|
12644
|
+
if (!existsSync2(assetBase)) return [];
|
|
12645
|
+
return readdirSync2(assetBase, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => inspectKitDist(entry.name)).sort((a, b) => a.manifest.kit.id.localeCompare(b.manifest.kit.id));
|
|
12646
|
+
}
|
|
12647
|
+
function downloadKitDist(kitId, outDir) {
|
|
12648
|
+
const info = inspectKitDist(kitId, outDir);
|
|
12649
|
+
mkdirSync2(info.outputRoot, { recursive: true });
|
|
12650
|
+
rmSync(info.folderPath, { recursive: true, force: true });
|
|
12651
|
+
copyDirRecursiveDist(info.assetRoot, info.folderPath);
|
|
12652
|
+
const zipEntries = listFilesRecursiveDist(info.folderPath).map((relativePath) => ({
|
|
12653
|
+
name: path17.posix.join(info.bundleManifest.export.folderName, relativePath),
|
|
12654
|
+
data: readFileSync(path17.resolve(info.folderPath, relativePath))
|
|
12655
|
+
}));
|
|
12656
|
+
writeFileSync(info.zipPath, buildStoredZipDist(zipEntries));
|
|
12657
|
+
return info;
|
|
12658
|
+
}
|
|
12659
|
+
function registerKitCommandsDist(target) {
|
|
12660
|
+
const kit = target.command("kit").description("Bundled Growthub Agent Worker Kit export utilities");
|
|
12661
|
+
kit.command("list").description("List the bundled worker kits available in this CLI build").action(() => {
|
|
12662
|
+
const kits = listBundledKitsDist();
|
|
12663
|
+
if (kits.length === 0) {
|
|
12664
|
+
console.log(pc21.dim("No bundled worker kits are available in this CLI build."));
|
|
12665
|
+
return;
|
|
12666
|
+
}
|
|
12667
|
+
for (const item of kits) {
|
|
12668
|
+
console.log([
|
|
12669
|
+
pc21.bold(item.manifest.kit.id),
|
|
12670
|
+
`version=${item.manifest.kit.version}`,
|
|
12671
|
+
`bundle=${item.bundleManifest.bundle.id}@${item.bundleManifest.bundle.version}`,
|
|
12672
|
+
`briefType=${item.bundleManifest.briefType}`,
|
|
12673
|
+
`name=${item.manifest.kit.name}`
|
|
12674
|
+
].join(" "));
|
|
12675
|
+
}
|
|
12676
|
+
});
|
|
12677
|
+
kit.command("inspect").description("Inspect a bundled worker kit manifest and export metadata").argument("<kit-id>", "Bundled worker kit id").option("--out <path>", "Override the export root used for resolved output paths").action((kitId, opts) => {
|
|
12678
|
+
const info = inspectKitDist(kitId, opts.out);
|
|
12679
|
+
console.log(`Kit: ${info.manifest.kit.id} @ ${info.manifest.kit.version}`);
|
|
12680
|
+
console.log(`Name: ${info.manifest.kit.name}`);
|
|
12681
|
+
console.log(`Bundle: ${info.bundleManifest.bundle.id} @ ${info.bundleManifest.bundle.version}`);
|
|
12682
|
+
console.log(`Entrypoint: ${info.manifest.entrypoint.path}`);
|
|
12683
|
+
console.log(`Export Folder: ${info.folderPath}`);
|
|
12684
|
+
console.log(`Export Zip: ${info.zipPath}`);
|
|
12685
|
+
});
|
|
12686
|
+
kit.command("download").description("Export a bundled worker kit as both a zip file and expanded folder").argument("<kit-id>", "Bundled worker kit id").option("--out <path>", "Output directory for the generated artifacts").action((kitId, opts) => {
|
|
12687
|
+
const info = downloadKitDist(kitId, opts.out);
|
|
12688
|
+
console.log(`Expanded Folder: ${info.folderPath}`);
|
|
12689
|
+
console.log(`Zip File: ${info.zipPath}`);
|
|
12690
|
+
});
|
|
12691
|
+
kit.command("path").description("Resolve the expected expanded export folder path without exporting").argument("<kit-id>", "Bundled worker kit id").option("--out <path>", "Output directory for the generated artifacts").action((kitId, opts) => {
|
|
12692
|
+
const info = inspectKitDist(kitId, opts.out);
|
|
12693
|
+
console.log(info.folderPath);
|
|
12694
|
+
});
|
|
12695
|
+
}
|
|
12696
|
+
|
|
12492
12697
|
// src/index.ts
|
|
12493
12698
|
var program = new Command();
|
|
12494
12699
|
var DATA_DIR_OPTION_HELP = "Growthub data directory root (isolates local instance state)";
|
|
@@ -12520,6 +12725,7 @@ function registerSharedCommands(target) {
|
|
|
12520
12725
|
});
|
|
12521
12726
|
target.command("allowed-hostname").description("Allow a hostname for authenticated/private mode access").argument("<host>", "Hostname to allow (for example dotta-macbook-pro)").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).action(addAllowedHostname);
|
|
12522
12727
|
target.command("run").description("Bootstrap local setup (onboard + doctor) and run Growthub").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).option("-i, --instance <id>", "Local instance id (default: default)").option("--repair", "Attempt automatic repairs during doctor", true).option("--no-repair", "Disable automatic repairs during doctor").action(runCommand);
|
|
12728
|
+
registerKitCommandsDist(target);
|
|
12523
12729
|
const auth = target.command("auth").description("Authentication and bootstrap utilities");
|
|
12524
12730
|
auth.command("bootstrap-ceo").description("Create a one-time bootstrap invite URL for first instance admin").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).option("--force", "Create new invite even if admin already exists", false).option("--expires-hours <hours>", "Invite expiration window in hours", (value) => Number(value)).option("--base-url <url>", "Public base URL used to print invite link").action(bootstrapCeoInvite);
|
|
12525
12731
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@growthub/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.36",
|
|
4
4
|
"description": "Growthub CLI — orchestrate AI agent teams to run a business",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"url": "https://github.com/antonioromero1220/growthub-local/issues"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
-
"dist"
|
|
28
|
+
"dist",
|
|
29
|
+
"assets"
|
|
29
30
|
],
|
|
30
31
|
"engines": {
|
|
31
32
|
"node": ">=20"
|