@aicopycoders/exodus 2026.9.1100-beta.2 → 2026.9.1400-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/assets/docs/CLAUDE.md +22 -1
  2. package/assets/docs/PIPELINES.md +20 -0
  3. package/assets/skills/anatomy-of-ads/SKILL.md +138 -0
  4. package/assets/skills/anatomy-of-ads/references/beat-map.md +69 -0
  5. package/assets/skills/anatomy-of-ads/references/concept-angle.md +50 -0
  6. package/assets/skills/anatomy-of-ads/references/hook-and-structure.md +69 -0
  7. package/assets/skills/bot-builder/SKILL.md +147 -0
  8. package/assets/skills/bot-builder/references/exodus-prompt-node.md +113 -0
  9. package/assets/skills/bot-builder/references/prompt-blocks.md +169 -0
  10. package/assets/skills/copy-instincts/SKILL.md +154 -0
  11. package/assets/skills/copy-instincts/references/instincts-catalog.md +131 -0
  12. package/assets/skills/copy-instincts/references/locked-examples.md +104 -0
  13. package/assets/skills/copy-instincts/references/worked-runs.md +148 -0
  14. package/assets/skills/exodus/SKILL.md +10 -1
  15. package/assets/skills/exodus-video/SKILL.md +12 -4
  16. package/assets/skills/exodus-video/scripts/first-cut.mjs +129 -32
  17. package/assets/skills/find-competitors/SKILL.md +148 -0
  18. package/assets/skills/find-competitors/references/query-grid.md +78 -0
  19. package/assets/skills/find-competitors/scripts/__pycache__/adlib_sweep.cpython-314.pyc +0 -0
  20. package/assets/skills/find-competitors/scripts/__pycache__/competitor_signals.cpython-314.pyc +0 -0
  21. package/assets/skills/find-competitors/scripts/adlib_sweep.py +100 -0
  22. package/assets/skills/find-competitors/scripts/competitor_signals.py +168 -0
  23. package/assets/skills/handoff/SKILL.md +81 -0
  24. package/assets/skills/mechanism/SKILL.md +130 -0
  25. package/assets/skills/mechanism/references/bot-prompts.md +94 -0
  26. package/assets/skills/mechanism/references/competitor-mechanisms.md +39 -0
  27. package/assets/skills/odf/SKILL.md +167 -0
  28. package/assets/skills/patterns-and-gaps/SKILL.md +120 -0
  29. package/assets/skills/positioning-spiral/SKILL.md +166 -0
  30. package/assets/skills/remember-that/SKILL.md +56 -0
  31. package/assets/skills/segment-grid/README.md +45 -0
  32. package/assets/skills/segment-grid/SKILL.md +84 -0
  33. package/assets/skills/thinking-vs-build/SKILL.md +120 -0
  34. package/assets/templates/STANDARDS.md +49 -0
  35. package/dist/commands/init.d.ts +1 -0
  36. package/dist/commands/init.js +10 -5
  37. package/dist/commands/update.js +1 -1
  38. package/dist/commands/video.d.ts +6 -0
  39. package/dist/commands/video.js +30 -1
  40. package/dist/lib/assets.d.ts +1 -0
  41. package/dist/lib/assets.js +3 -0
  42. package/dist/lib/scaffold.d.ts +3 -0
  43. package/dist/lib/scaffold.js +18 -1
  44. package/package.json +1 -1
@@ -0,0 +1,104 @@
1
+ # What "locked" looks like
2
+
3
+ Locked (coherence, resonance, the click) is when all the dimensions rhyme. The product stops being a product and becomes the inevitable conclusion of a worldview. The audience does not feel sold to; they feel shown how the world works. The test: say the whole thing, product, mechanism, proof, enemy, solution, in one breath, and the listener nods before you finish. Not because it is vague. Because every piece implies the next.
4
+
5
+ Not locked: you keep adding explanations. "Well, the mechanism is this, but the spokesperson is more like… and the history is kind of separate but…". Every "but" and "kind of" is a dimension not rhyming.
6
+
7
+ ## Five full locks
8
+
9
+ ### Olive oil (lock through place)
10
+
11
+ Premium olive oil from Cyprus. Everything in the category is grey: cold-pressed, high polyphenol, extra virgin. Then the student mentioned the land: the trees grow on a former UN buffer zone untouched for 35 years. No pesticides, no agriculture, nothing.
12
+
13
+ - Mechanism: trees fight harder in undisturbed soil → more polyphenols as a stress response → the oil is more potent because of the adversity. The mechanism rhymes with the origin.
14
+ - Visual: untouched earth, ancient trees, a no-man's land nature reclaimed.
15
+ - Narrative: protected for 3,000 years, a conflict zone, then a buffer zone, then someone planted olives in the most pristine soil on earth.
16
+ - Binary: "Most premium olive oil is dead. This is the world's only living olive oil."
17
+ - Proof: the polyphenol count is higher, and the reason it is higher is the story. Proof and story are the same thing.
18
+ - Moat: a place with an irreproducible history. Nobody can manufacture 35 untouched years.
19
+
20
+ One breath: "The only olive oil in the world grown on land untouched by a single pesticide in 35 years, a former UN no-man's land in Cyprus where the trees fight so hard to survive they produce the highest polyphenol levels ever measured. Most premium olive oil is dead. This is the world's only living olive oil."
21
+
22
+ Notice what disappeared: cold-pressed, extraction, extra virgin. The grey claims became supporting details.
23
+
24
+ ### Volcanic shower head (lock through ingredient origin)
25
+
26
+ A filtered shower head. Grey claims: filters chlorine, improves skin, better pressure. Colour: one filtration layer contains volcanic rock from the Faroe Islands.
27
+
28
+ - Mechanism: rock forged under extreme heat and pressure naturally purifies through mineral filtration. The rock's violent creation is the reason it works.
29
+ - Visual: "I got a volcano shower."
30
+ - Cultural: the Faroes were settled by Vikings; Vikings have legendary hair. Association, not logic. Let the imagination connect it.
31
+ - Binary: volcanic rock (ancient, forged by earth) versus activated carbon (industrial, manufactured).
32
+ - Sensory: "secret minerals flooding my skin". It does not just filter, it gives.
33
+ - Conspiracy: fluoride bans in two US states and Europe. "What's in your water that governments are banning?"
34
+
35
+ One breath: "This shower head filters your water through volcanic rock from the Faroe Islands, the same mineral-rich stone the Vikings bathed in, forged under extreme pressure deep in the earth, purifying and mineralising every drop. It's not a filter. It's a volcano shower."
36
+
37
+ ### Melatonin strip (lock through conspiracy)
38
+
39
+ The most commoditised supplement in America. Colour: MIT patented melatonin at 0.3 to 1mg. The industry mega-dosed to 5 to 10mg to avoid paying MIT.
40
+
41
+ - Mechanism: the effective dose is tiny; the common dose is 10 to 100x too high and trains the body to stop making its own. The trusted solution is the enemy. An inversion.
42
+ - Conspiracy: documented patent strategy, not speculation.
43
+ - Binary: horse pills versus targeted micro-strip.
44
+ - Proof: "Melatonin is the only hormone you can buy over the counter, banned in Europe and Japan." Two facts side by side; the reader builds the implication.
45
+ - Names: melatonin overdose, melatonin hangover, melatonin resistance, targeted melatonin. Every name implies the same enemy and the same answer.
46
+
47
+ One breath: "MIT patented the effective melatonin dose at under 1mg. The supplement industry didn't want to pay, so they mega-dosed to 5 to 10mg, cut it with fillers, and sold it as sleep support. Those horse pills aren't helping you sleep. They're training your brain to stop making melatonin. This strip delivers the exact dose MIT patented."
48
+
49
+ ### Norwegian fitness coach (lock through archetype)
50
+
51
+ A fitness program for men over 35 from a 26-year-old Norwegian. Original pitch: three-pillar system, metabolic reset. Every coach says that, and a 26-year-old telling 45-year-olds is a credibility gap.
52
+
53
+ - Archetype: Rolex, polo, travel, chill, ex-footballer. Not "become Wolverine". Nordic mystique: scientific, elite, measured, old money, enjoys luxurious food.
54
+ - Mechanism: cortisol. Workouts are short because long workouts spike cortisol. Food is flexible because restriction spikes cortisol. Results come fast because when cortisol is fixed the rest follows.
55
+ - The rhyme: the archetype is the mechanism expressed as a lifestyle. His entire being is the proof. He is not stressed, not grinding, training three times a week.
56
+ - Case study: the yacht owner who travels three weeks a month, eats out constantly, dropped 60 pounds. A lifestyle demonstration, not a boot camp before-and-after.
57
+ - Cultural positioning: "Why are you Americans going so crazy?" Playful superiority that rhymes with the mechanism (overcomplication is the problem).
58
+ - Binary: overcomplicated American bro-science versus measured Scandinavian science.
59
+
60
+ One breath: "A 26-year-old Norwegian sports scientist figured out what American fitness gets wrong: you're working too hard, stressing too much, and spiking the one hormone that makes fat loss impossible. His clients, CEOs who travel three weeks a month and eat out every night, drop 40 to 60 pounds training three times a week and eating what they want, because when you manage cortisol the way Scandinavian sports science has for decades, everything else fixes itself."
61
+
62
+ ### CIA self-defence (lock through exclusivity)
63
+
64
+ - Archetype: he was in the CIA. Everything radiates from that. "They're declassifying stuff from 50 years ago that's cutting edge today. Imagine what's still classified."
65
+ - Mechanism: every martial art is trained for tournaments and cardio classes. Real combat has no referee, no gloves, no timer. CIA training is the only training built for that reality. The mechanism is the archetype.
66
+ - Exclusivity: "I can't teach you everything. What I can show you is how they think." More exists that you cannot access, and what is shared is the distilled essence.
67
+ - Audience: 50-year-old dads. Three moves, simple. The simplicity is the proof: if it is distilled for operatives with two seconds, it must be the most effective.
68
+ - Binary: tournament fighters in Lululemon versus operatives with no rules.
69
+
70
+ One breath: "A former CIA operative is sharing the three pressure-point techniques agents learn for when there are no rules, no referees, and no second chances. He can't teach you everything; most of it is still classified. But what he can share is simple enough for any 50-year-old dad to learn in an afternoon, and powerful enough he has to warn you not to use it unless your family's safety is at stake."
71
+
72
+ ## Almost locked, and where the rhyme broke
73
+
74
+ **Copper pillowcase.** The copy built desire around pressure on the face during sleep. The product's mechanism was copper ion transfer. Pain said "your pillow is crushing your face"; mechanism said "copper seeps into your skin". Two stories. Locked version: your skin is starving while you sleep, cells trying to repair without the one mineral that activates regeneration; this pillowcase transfers copper ions for the eight hours you are asleep. "It's not a pillow. It's an 8-hour spa treatment." Now pain (starving cells) rhymes with mechanism (copper feeds cells) rhymes with time (eight hours) rhymes with visual (ions flowing from fabric to skin).
75
+
76
+ **Menopause joint supplement.** Mechanism: "changing hormones have hijacked how your body uses the nutrients it needs to repair your joints." Six moving parts. Too complex to picture, so no visual, so no binary, so nowhere for the pain and benefit cascades to anchor. Locked version collapsed to two links: hormones deplete, so cells cannot absorb. Now you can draw it (sealed walls versus open walls), name it (nutrient lockout), make the binary (absorbing versus starving), and the star ingredient rhymes (bee propolis penetrates the cell wall). Simplification did not dumb it down; it enabled the lock.
77
+
78
+ **Cannibal brain.** A memory supplement scaled to $9M on "your brain is eating itself". Vivid, novel, and it rhymed within the pain dimension: eating itself → irreversible → despair. But despair has no product at the end. "Nobody believes a drop is gonna make your brain rebuild itself." Compare "dirty brain": dirty implies cleanable, gunk implies flushable. The pain rhyme and the solution rhyme converge on the product.
79
+
80
+ **Fire blanket.** Strong mechanism (extinguishers are the dangerous thing), strong conspiracy (most are expired), strong binary (chemical spray versus clean blanket). Then the story ad: she left oil on the stove while chatting with her mom. Gut reaction: "this bitch is kind of dumb, it's her fault." The character broke the worldview. The worldview said the system failed you; the character said user error. Locked version: she does everything right, has the extinguisher because she is a good mom, grabs it, and nothing happens because it expired and nobody told her. Or it works and the residue wrecks the kitchen worse than the fire. Better still, a firefighter spokesperson: "I've seen extinguishers fail hundreds of times. Here's what I make my family use."
81
+
82
+ ## The diagnostic
83
+
84
+ - You keep needing "and also". Locked copy says "which means", "because", "which is why".
85
+ - Mechanism and star ingredient were chosen in separate sessions and are now being connected. Locked: they are the same discovery.
86
+ - The spokesperson could be swapped. Locked: their background is what makes the discovery possible.
87
+ - You are defending the mechanism ("I know this sounds crazy, but…", "studies show…"). Locked: proof is a chapter of the story.
88
+ - Pain and solution use different metaphors. Locked: invert the pain metaphor and you get the solution metaphor.
89
+
90
+ ## The feel of it
91
+
92
+ It feels like a slope, not a staircase. Each sentence tilts you into the next. It feels like one idea expressed seven ways: purity through adversity as land, chemistry, history, binary, moat, sensation, visual. It feels inevitable rather than clever. You can hand it to anyone at dinner and they get it in fifteen seconds. And the writer feels the product deserves to exist: the conviction is real, so the writing reads as advocacy instead of marketing.
93
+
94
+ ## Getting there: the practical seven
95
+
96
+ 1. **Load everything before committing to anything.** Every ingredient's origin, history, natural behaviour, science. The spokesperson's full backstory. The market's current arguments and charged words. Suppressions, patents, bans. Competitors' exact claims. One interesting thing gives you one dial position; you need five or six dials with several positions each.
97
+ 2. **Find the single strongest shade of colour.** The one fact that makes your gut tighten and that no competitor has. If you could paste it into a competitor's ad, it is grey.
98
+ 3. **Ask what that detail's natural metaphor is, and whether it rhymes with a mechanism.** Volcanic rock purifies. Bees penetrate. Untouched land makes hardier plants. If it does not rhyme, change the detail or the mechanism; do not force it.
99
+ 4. **Check the spokesperson.** Does their specific history make them the natural discoverer of this? If not, pick a different aspect of their background to emphasise.
100
+ 5. **Generate the binary.** The bad side is life without the mechanism; the good side is life with it.
101
+ 6. **Run the one-breath test.** Three or four sentences a stranger finds compelling. If a listener can predict the ending after the first sentence, that is inevitability, not boredom.
102
+ 7. **Check the cascades against the root metaphor.** Pain drilled to bedrock and benefit cascaded upward must both stay inside the same metaphorical universe.
103
+
104
+ The deepest reason this is hard: you cannot fake the lock. You can fake a hook, a mechanism name, a backstory. The convergence depends on finding something true about the product that naturally generates the rest. Students assemble from the outside. The lock is discovered from the inside. The skill is perception: looking at a product deeply enough to find the one true thing that makes everything else obvious.
@@ -0,0 +1,148 @@
1
+ # Worked runs
2
+
3
+ Five runs of the process at different levels. The first four are Luke and Mario's public teaching examples. The fifth is a fictional cortisol-style supplement so the shape is visible on a category like the ones this workspace handles.
4
+
5
+ ---
6
+
7
+ ## Run 1. The beard oil ad (ad level → hook)
8
+
9
+ **Draft.** "If you oil your beard daily and it's still dry, you're not doing it wrong, you're using the wrong product entirely. And here's why it's not your fault. The oil you're using, whether it's from the drugstore or Amazon, was formulated for straight hair. Different texture. Different biology. On your hair, it just sits on top and does nothing… Your hair, 4b, 4c texture, curls so tight that the natural oils your skin produces get trapped at the root… So your beard is starving on the outside while your skin is drowning in oil underneath… That's exactly why two brothers from Atlanta built this kit…"
10
+
11
+ **Frame.** Ad. Black men with beards. Money symptom: patches (not dryness, not acne).
12
+
13
+ **See.**
14
+ - Body scan: reads AI, dashes, mildly bored, one line woke me up.
15
+ - Copy blocks: pain present and long, promise thin, proof at the end (100,000 kings, 4,000 reviews), curiosity is the straight-hair mechanism, no conditions beyond "first-time discount".
16
+ - Colour: formulated for straight hair · sits on top and does nothing · 4b/4c curls so tight oil can't move · starving outside, drowning underneath · two brothers from Atlanta. Grey: "you've tried everything", "here's why it's not your fault", the cycle paragraph.
17
+ - Title: "Your beard oil isn't working." Rated 3 to 4 out of 10.
18
+ - Ideas: one, but the best lines are on lines 6 to 12, not line 1.
19
+
20
+ **Attract (light).** Unique: built for 4c texture by two brothers from Atlanta; no water, no sulfates. Thesis underneath: the beard companies made oil for straight hair and sold it in every Black neighbourhood in America. They don't care about Black men.
21
+
22
+ **Make.**
23
+ - Why the colour line is interesting: strong mental image, contrarian, "starving" and "drowning".
24
+ - Relevance: do I care that my skin is drowning in oil? Acne, maybe. If I'm a beard person I care about my beard. So: the oil clogs the follicles that cause growth. Now it hits patches.
25
+ - Contrast: "sits on top and does nothing" is neutral. "Clogs the follicles" is good thing bad. Then: "most oil" → "white beard oil". Loaded phrase.
26
+ - Claim ladder: most oil is formulated for straight hair and not designed for your hair → most oil doesn't work for your hair → most oil doesn't work for Black hair → most oil hurts Black hair → white beard oil hurts Black hair.
27
+ - Visual: two images. Oil moving down a straight shaft versus oil trapped at a coiled root. Then metaphor, specific: curly hair strangles the root like a boa constrictor. Name it: follicle strangulation.
28
+ - Blame placement (Mario): blame what the oil does, not their curly hair. The oil turns into the snake.
29
+ - Intensification and ratchet: drowning your follicles, making it impossible to grow a beard.
30
+
31
+ **Rewrite (hook).** "If you're using white beard oil as a Black man, you're probably drowning your follicles and making it impossible to grow a beard."
32
+
33
+ Alternates from the same idea: "Most beard oil is built for white beards. It feeds straight hair and strangles curly hair. That's why your beard stays patchy." · "The beard companies made beard oil for straight hair and sold it in every Black neighbourhood in America. It nourishes straight beards while choking ours." · "Your genes are fine. Beard oil made for straight hair suffocates curly hair. That's why your beard grows in patches."
34
+
35
+ **Protect.** Redditor: "racist?" is the bail risk; the payoff is that it is true of the formulation, and the body walks it back with the texture biology. Cold scroll: line 1 stands alone. Congruence: the enemy is the oil, the prospect is absolved.
36
+
37
+ **Probability.** Hook 2 is more likely than hook 1. Nobody knows until the test.
38
+
39
+ ---
40
+
41
+ ## Run 2. PDRN micro-infusion (mechanism / big idea level)
42
+
43
+ **Draft.** A product page: PDRN (salmon DNA), hyaluronic acid, peptides, growth factors, delivered through 24k gold micro-infusion stamps. Brief: "market it."
44
+
45
+ **Frame.** Mechanism. Women buying anti-aging. Money symptom: visible aging the mirror shows.
46
+
47
+ **See.** Grey: hyaluronic acid, peptides, growth factors. All good, all sayable by everyone. Colour: PDRN itself (early majority; known but still novel) and the gold micro-infusion delivery. No spokesperson with a story.
48
+
49
+ **Attract (full).**
50
+ - North star: I need a worldview where PDRN plus gold micro-infusion is the absolute best form of skincare.
51
+ - Two mechanisms exist for PDRN: receptor signalling, or building block that fills the cell. Building block felt more substantial, we are adding something lost to your cells. Chosen.
52
+ - Preeminent: if PDRN must be the perfect solution, what must be true about salmon? Salmon swim upstream, get battered, their skin gets wrecked, and they regenerate. Fish skin heals faster than mammal skin. PDRN is used in wound care. So: salmon have the most regenerative skin of any animal. Plausible argument, not a lie; if it is not defensible, move on.
53
+ - Association: farm-raised salmon from the Chinese sea, or Norwegian, or Arctic? There is a cherry salmon grown in Korea. Korean cherry salmon. Cherry reads sweet and beautiful, Korean opens the K-beauty file, salmon keeps the mechanism. Nothing about the fish changed. Ask the brand whether it is true before you say it.
54
+ - Delivery: why micro-infusion? The capillaries are stuck; nothing taken internally reaches that layer. Why gold? Most valuable metal for its properties; any other metal would degrade PDRN. So it has to be gold, and it has to be infused.
55
+ - Coherence: beauty, gold, luxury, Korea, cherry salmon, regenerative skin, DNA, rejuvenating your cells. Cleopatra. Now there is a movie.
56
+
57
+ **One breath.** "PDRN is DNA from the most regenerative animal on earth, salmon that tear their skin apart swimming upstream and rebuild it. This one comes from Korean cherry salmon, and it's delivered through 24k gold micro-infusion because gold is the only metal that doesn't destroy it and micro-infusion is the only way past your stuck capillaries into the cell."
58
+
59
+ **Protect.** Cold scroll: "PDRN" needs one clause of definition on line 1. Compliance: "most regenerative animal on earth" is the rung to check; "fish skin heals faster than mammal skin" is the walk-back.
60
+
61
+ ---
62
+
63
+ ## Run 3. The freelancer headline (hook / headline level)
64
+
65
+ **Draft.** "The 3-Pillar System That's Helped 1,000+ Freelancers Go From Struggling to Building Predictable Revenue in 6 Weeks (Not Years). Only 15 Slots Left."
66
+
67
+ **See.**
68
+ - Copy blocks: curiosity = 3-pillar system (rated 3), promise = predictable revenue (4), proof = 1,000+ freelancers (7), pain = struggling (vague), constraint = 6 weeks not years (6), condition = 15 slots. All six present. Not missing blocks; each block is weak.
69
+ - Title: "A system helps freelancers earn predictably." Rated 3 to 4.
70
+
71
+ **Make.**
72
+ - "3-pillar" feels like three steps. Simplicity: one step. "Client Domino System" is one specific action that creates inevitable momentum, and domino carries associations of chain reaction, gambling, money.
73
+ - "Struggling" → specific: feast-or-famine, cold pitching, dry spells, held hostage by referrals.
74
+ - Ratchet on the pain: "Every month you spend surviving feast-or-famine is another month you're falling further behind."
75
+ - Proof made specific: 16-year pro; worked with Netflix, Google, Lionsgate.
76
+ - Promise made specific: land premium clients, add six figures, predictable $10K+ months.
77
+
78
+ **Rewrites.**
79
+ - "Every Month You Spend Surviving Feast-or-Famine Is Another Month You're Falling Further Behind… The 'Predictable Freelance Business' Blueprint That 1,000+ Members Used to Finally Escape the Panic, Land Premium Clients, and Add Six Figures to Their Income."
80
+ - "7-Figure Freelancing Expert Who Worked With Netflix, Google and Lionsgate Reveals… New 'Client Domino System' That's Helping THOUSANDS of Freelancers Go From Feast-Or-Famine To Predictable $10K+ Months (Without Cold Pitching, Job Boards, Or Begging For Referrals)."
81
+
82
+ Not more blocks; each block stronger.
83
+
84
+ ---
85
+
86
+ ## Run 4. The melatonin claim ladder (advertorial lead)
87
+
88
+ **Draft idea.** "Melatonin doesn't always work for some people." True, hedged, boring. Nobody stops for it.
89
+
90
+ **Ladder.**
91
+ 1. Melatonin doesn't always work for some people
92
+ 2. Melatonin doesn't always work
93
+ 3. Melatonin doesn't work
94
+ 4. Melatonin hurts sleep
95
+ 5. Melatonin hurts sleep by making you pee more
96
+
97
+ Rung 5 is the one you would look at. You will never find it by staring at facts about melatonin. You find it by knowing rung 5 is psychologically stronger, then working back: over-the-counter doses are 10 to 100x the effective amount, the surge wears off mid-night, you wake, you pee, you cannot get back down, and your receptors downregulate over time. Walk it back in the body, not in the hook. If it cannot be made true, kill it and take rung 4.
98
+
99
+ ---
100
+
101
+ ## Run 5. Evenkeel Reset Gummies (fictional, cortisol-style supplement, ad level)
102
+
103
+ Fictional brand, fictional competitor, fictional facts. Saffron extract, magnesium glycinate, rhodiola, evening gummy. Founder is a former ER nurse. Sister SKUs: Night Tea and Glow Collagen (which already claims cortisol belly).
104
+
105
+ **Draft.** "Feeling stressed and wired at night? Evenkeel Reset Gummies combine saffron, magnesium and rhodiola to support healthy cortisol levels so you can relax, sleep better and wake up refreshed. Formulated by a nurse. Clinically studied ingredients. Try it risk-free."
106
+
107
+ **Frame.** Ad. Women 40 to 55 who fall asleep fine and wake at 3AM. Money symptom: the 3AM wake with a racing mind. Not "stress".
108
+
109
+ **See.**
110
+ - Body scan: nothing. Grey from the first word. "Support healthy cortisol levels" is a phrase from every label in the category.
111
+ - Copy blocks: promise (relax, sleep, refreshed, rated 2), curiosity (none; the ingredients are listed, not explained), proof (clinically studied, 3), pain (stressed and wired, 2), constraint none, condition (risk-free, 4).
112
+ - Colour: "formulated by a nurse" is the only owned line, and it is buried. Everything else is grey.
113
+ - Title: "A calming gummy." Rated 2.
114
+ - Ideas: several benefits, no idea.
115
+
116
+ **Attract (full, because the idea is under 6).**
117
+ - Unique candidates: an ER nurse founder; the evening timing; saffron as the star (magnesium is everywhere, rhodiola is a morning herb).
118
+ - North star: what would have to be true for an evening saffron gummy made by an ER nurse to be the best possible answer to the 3AM wake? Cortisol is supposed to fall at night. In these women it does not; it spikes around 3AM (a second surge), and that surge is what opens their eyes. Falling asleep was never the problem. Staying asleep is. So the whole category, built on falling asleep (melatonin, Night Tea, sleepy teas), is aimed at the wrong hour.
119
+ - Spokesperson: an ER nurse works nights, lives on cortisol, and sees what a 3AM surge does to a body every shift. She would notice the second surge before a researcher would. Irreplaceable, because her shift is the mechanism.
120
+ - Sympathetic magic: saffron is harvested at dawn, by hand, from a flower that opens for one day. The most expensive spice on earth, calming by tradition. Weaker than salmon, but it carries "rare, hand-picked, precious". Magnesium glycinate rhymes with "the mineral the night spends".
121
+ - Binary: fall-asleep products versus stay-asleep products. Or: night cortisol dropping versus night cortisol spiking.
122
+ - Name: the 3AM surge. Night cortisol. Check: "surge" implies it can be flattened. Good.
123
+ - Catalog check: Glow Collagen owns cortisol belly and face. Reset owns night cortisol and the 3AM wake. Night Tea owns falling asleep. No collision once the lane is "stay asleep".
124
+
125
+ **One breath.** "You don't have a falling-asleep problem, you have a 3AM problem. Your cortisol is supposed to fall all night; yours spikes at 3AM and that surge is what opens your eyes. An ER nurse who lived that surge every night shift built an evening gummy with saffron and magnesium to flatten it, so you sleep through instead of waking up wired."
126
+
127
+ **Make.**
128
+ - Relevance: 3AM by name, racing mind, ceiling-staring. Not "sleep better".
129
+ - Contrast, good thing bad: "Your sleep tea is why you wake at 3AM." (Fall-asleep products push you down hard and let cortisol bounce back.) Bad thing good: "Skip the bedtime routine. Fix 3AM instead."
130
+ - Visual: a cortisol line that should slide downhill all night and instead jumps at 3 like a spike on a heart monitor. The nurse has seen that spike on a screen.
131
+ - Claim ladder: sleep aids don't help everyone stay asleep → sleep aids don't fix 3AM → sleep aids don't work after midnight → sleep aids cause the 3AM wake → your sleep tea is causing the 3AM surge.
132
+ - Thesis: the sleep industry sold you falling asleep because it is easy to promise, and left you alone at 3AM.
133
+ - Permission slip: keep your wine, keep your late dinner; the surge is the target.
134
+ - Name and reify: "the 3AM surge".
135
+ - Ratchet: every 3AM wake trains the surge to come earlier.
136
+ - Constraint: one gummy at 8PM.
137
+
138
+ **Rewrites (hooks).**
139
+ - "If you fall asleep fine but wake up at 3AM wired, your cortisol is spiking in the middle of the night, and your sleep tea is making it worse."
140
+ - "An ER nurse figured out why women over 40 wake at 3AM. It isn't stress. It's a cortisol surge that hits while you sleep."
141
+ - "Stop trying to fall asleep faster. You already do. Fix the 3AM surge instead."
142
+
143
+ **Protect.**
144
+ - Redditor: "cortisol at 3AM, is that real?" The body has to carry the walk-back (the normal overnight cortisol curve and the early-morning rise). Rung 5 of the ladder ("your sleep tea is causing it") is the one compliance will pull; rung 3 or 4 survives.
145
+ - Cold scroll: hook 1 stands alone. Hook 3 needs "3AM" to be understood as a wake, which most of this market will get.
146
+ - Congruence: enemy is the category and the hour, not the woman. Nurse founder rhymes with the mechanism. Glow Collagen keeps belly and face. Night Tea keeps falling asleep. Nothing points the other way.
147
+
148
+ **Probability.** Hook 1 is the highest-probability opener because it names the symptom, the hour, and the enemy. Hook 2 tests the spokesperson lane. Only the account decides.
@@ -30,9 +30,18 @@ This skill ROUTES. Read what the user wants, name the destination skill, and han
30
30
  | Open or create Google Docs/Sheets | `exodus-drive` |
31
31
  | Read their OWN live Meta ads and their numbers — "what are my cheapest ads", "where is my spend going", "show me that ad", "what are people saying in the comments on my ads" (`exodus ads` / `exodus comments`) | `exodus-ads` (read-only; already synced by the dashboard, no Meta MCP needed) |
32
32
  | Import their OWN winning ads from their Meta ad account ("import my winners", "mine my ad account for winners", "exodus winners") | `exodus-winners` (it checks first whether the brand's Meta account is connected — if it is, it reads `exodus ads` and `exodus winners definition` instead of a Meta MCP) |
33
+ | Figure out WHO to sell to, segments, personas, "map the market" | `odf` (or `positioning-spiral` when no coherent positioning exists yet) |
34
+ | The product's mechanism, "why does this work", "which ingredient leads" | `mechanism` |
35
+ | Who the real competitors are, a watch list | `find-competitors` |
36
+ | What patterns the ads show, what competitors do that we don't | `patterns-and-gaps` |
37
+ | Break an ad down, tag a batch, write a brief | `anatomy-of-ads` |
38
+ | Make an existing draft hit harder, "what's wrong with this ad" | `copy-instincts` |
39
+ | Build a bot or a workflow prompt node | `bot-builder` |
40
+ | "remember that", "never do that again" | `remember-that` |
41
+ | "handoff", "wrap up", "what did we do today" | `handoff` |
33
42
  | Update the CLI + skills ("update exodus", "run an exodus update") | run `npx @aicopycoders/exodus@latest update` — the ONLY command that refreshes skills; `doctor` checks health but never rewrites them |
34
43
 
35
- If a custom pipeline skill is installed in this folder (any additional `exodus-*` directory under `.claude/skills/`), it routes the same way — match the user's words to that skill's description.
44
+ The method skills above are Luke's thinking as skills; they run as a conversation and are listed in `CLAUDE.md` under **Method skills**. If a custom pipeline skill is installed in this folder (any additional `exodus-*` directory under `.claude/skills/`), it routes the same way — match the user's words to that skill's description.
36
45
 
37
46
  For any creative judgment along the way (awareness calls, hook critique, which pipeline fits), `exodus-strategist` is the operating persona — it activates with the work.
38
47
 
@@ -144,6 +144,9 @@ failed[] files that did not download: {file, url, er
144
144
  How to read it before cutting:
145
145
 
146
146
  - `clipStatus: "done"` and a `clip` filename: the scene is cuttable.
147
+ - `wordsFrom: "voice"` (or narration in the storyboard with no on-camera line):
148
+ a narrated scene. Its clip is picture only; the voice track is the sound.
149
+ Never cut a narrated scene with the clip's own audio.
147
150
  - `flagged: true` is a warning, not a block. The clip was delivered anyway; the
148
151
  findings say what the QC model saw (`wrong-character`, `eyeline-off`,
149
152
  `set-drift`, `speech-cutoff`, `framing-off`). Tell the user which scenes are
@@ -163,10 +166,15 @@ How to read it before cutting:
163
166
 
164
167
  Use the bundled script. It reads the manifest, puts every A-roll scene in order
165
168
  as the spine, lays each cutaway over the spine at the moment its line is spoken,
166
- normalizes everything to 1080x1920 at 24 fps, fills a silent clip with the
167
- scene's voice track, lays the music bed underneath at a low level, and writes one
168
- MP4 with faststart for upload. The path is from the workspace root; from a brand
169
- subfolder, prefix `../`:
169
+ normalizes everything to 1080x1920 at 24 fps, and writes one MP4 with faststart
170
+ for upload. The audio track comes first: a narrated scene (narration in the
171
+ storyboard, no on-camera line) plays its voice track and nothing the clip
172
+ recorded, and the picture is fitted to the voice (a longer clip is trimmed, a
173
+ longer voice is sped up to at most 1.26x and then the last frame holds). A
174
+ dialogue scene keeps the line it performed. Every segment is loudness-normalized
175
+ to -16 LUFS, a continuous room-tone bed runs under the whole ad so the joins do
176
+ not read as dead air, and the music bed sits underneath at a low level. The path
177
+ is from the workspace root; from a brand subfolder, prefix `../`:
170
178
 
171
179
  ```
172
180
  node .claude/skills/exodus-video/scripts/first-cut.mjs ./ad-<runId>
@@ -1,15 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  // A plain first cut from a folder written by `exodus video pull`: the A-roll
3
3
  // scenes in the manifest's order make the spine, each cutaway lays over the
4
- // spine at the moment its cued line is spoken, the music bed goes underneath,
5
- // and one MP4 comes out ready to upload.
4
+ // spine at the moment its cued line is spoken, a room-tone bed and the music
5
+ // bed go underneath, and one MP4 comes out ready to upload.
6
6
  //
7
7
  // node first-cut.mjs <pulled-dir> [--out cut.mp4] [--skip 2,5] [--no-music]
8
8
  //
9
- // A scene with a clip contributes the clip. A clip with no audio stream gets
10
- // the scene's voice track, or silence. A scene with no clip but a keyframe
11
- // becomes a still for the length of its voice track, else the storyboard's
12
- // planned duration, with the voice if one was delivered. --skip drops scenes.
9
+ // The audio track comes first. A narrated scene (the storyboard gives it
10
+ // narration and no on-camera line) plays its voice track and NOTHING the clip
11
+ // recorded: the voice replaces the clip's audio, and the picture is fitted to
12
+ // the voice, trimmed when the clip runs longer, sped up to at most 1.26x and
13
+ // then held on its last frame when the voice runs longer. A dialogue scene
14
+ // keeps the audio it performed. A scene with no clip but a keyframe becomes a
15
+ // still for the length of its voice track, else the storyboard's planned
16
+ // duration. Every segment is loudness-normalized to -16 LUFS before the join.
17
+ // --skip drops scenes.
13
18
  //
14
19
  // After the render it writes the whole ad's word times beside the MP4, as
15
20
  // <out>.words.json and <out>.srt, for captioning in post.
@@ -26,6 +31,16 @@ const norm = (label) =>
26
31
  `[${label}]scale=${WIDTH}:${HEIGHT}:force_original_aspect_ratio=decrease,` +
27
32
  `pad=${WIDTH}:${HEIGHT}:(ow-iw)/2:(oh-ih)/2,setsar=1,fps=${FPS},format=yuv420p`;
28
33
  const stereo = "aresample=48000,aformat=channel_layouts=stereo";
34
+ /** Per-segment loudness, the level every clip was normalized to before the join. */
35
+ const LOUDNORM = "loudnorm=I=-16:TP=-1.5:LRA=11,aresample=48000";
36
+ /** Air after the last spoken word of a narrated scene, so the line lands. */
37
+ export const TAIL_AIR_SEC = 0.8;
38
+ /** The most a voice track may be sped up to fit its clip. */
39
+ export const ATEMPO_CAP = 1.26;
40
+ /** A continuous low bed under the whole ad, so the joins between clips do not read as dead air. */
41
+ const ROOM_TONE =
42
+ "anoisesrc=color=brown:amplitude=0.0035:r=48000,highpass=f=60,lowpass=f=900,aformat=channel_layouts=stereo";
43
+ const MUSIC_VOLUME = 0.18;
29
44
 
30
45
  /** The word rule the planner sized every line with (scout models.ts wordCount). */
31
46
  export function wordCount(text) {
@@ -43,12 +58,30 @@ export function lineWordCounts(storyboard) {
43
58
 
44
59
  const CUTAWAY_KINDS = new Set(["cutaway", "insert"]);
45
60
 
61
+ const hasText = (value) => typeof value === "string" && value.trim().length > 0;
62
+
63
+ /**
64
+ * A narrated scene: on a script-bound storyboard (one that carries the member's
65
+ * script lines) the scene has narration and no on-camera line. That is the same
66
+ * fact the server uses to leave a narrator scene's clip mute and its voice track
67
+ * separate. A storyboard without script lines predates that rule: its voiced
68
+ * clips were lip-synced to the voice on the server, so they keep their own audio.
69
+ */
70
+ export function narratedScene(scene, storyboard) {
71
+ const scriptBound = Array.isArray(storyboard?.script) && storyboard.script.length > 0;
72
+ const spoken = (Array.isArray(scene?.dialogue) ? scene.dialogue : []).some(
73
+ (turn) => hasText(turn?.line) || hasText(turn?.text),
74
+ );
75
+ return scriptBound && hasText(scene?.voText) && !spoken;
76
+ }
77
+
46
78
  export function scenePlans(storyboard) {
47
79
  const plans = new Map();
48
80
  for (const scene of storyboard?.scenes ?? []) {
49
81
  if (typeof scene?.sceneIndex !== "number") continue;
50
82
  plans.set(scene.sceneIndex, {
51
83
  cutaway: CUTAWAY_KINDS.has(scene.kind),
84
+ narrated: narratedScene(scene, storyboard),
52
85
  lineIds: Array.isArray(scene.lineIds) ? scene.lineIds : [],
53
86
  cueLineId: typeof scene.cueLineId === "string" ? scene.cueLineId : null,
54
87
  durationSec: typeof scene.durationSec === "number" ? scene.durationSec : null,
@@ -57,6 +90,30 @@ export function scenePlans(storyboard) {
57
90
  return plans;
58
91
  }
59
92
 
93
+ /**
94
+ * Fit a picture to its voice track. The segment runs as long as the voice plus
95
+ * a beat of air. A clip that runs longer is trimmed; a voice that runs longer
96
+ * is sped up to at most ATEMPO_CAP and the clip's last frame is held for
97
+ * whatever is still left. A still (no clip) simply runs the voice's length.
98
+ */
99
+ export function fitNarration({ clipSeconds, voiceSeconds }) {
100
+ const target = voiceSeconds + TAIL_AIR_SEC;
101
+ if (clipSeconds === null) return { seconds: target, atempo: 1, holdSec: 0, trimmedBy: 0 };
102
+ if (target <= clipSeconds) {
103
+ return { seconds: target, atempo: 1, holdSec: 0, trimmedBy: clipSeconds - target };
104
+ }
105
+ const room = Math.max(clipSeconds - TAIL_AIR_SEC, 0.1);
106
+ const atempo = Math.min(ATEMPO_CAP, voiceSeconds / room);
107
+ const fitted = voiceSeconds / atempo + TAIL_AIR_SEC;
108
+ if (fitted <= clipSeconds) return { seconds: fitted, atempo, holdSec: 0, trimmedBy: 0 };
109
+ return { seconds: fitted, atempo, holdSec: fitted - clipSeconds, trimmedBy: 0 };
110
+ }
111
+
112
+ function scaleWords(words, atempo) {
113
+ if (!words || atempo === 1) return words;
114
+ return words.map((w) => ({ ...w, s: w.s / atempo, e: w.e / atempo }));
115
+ }
116
+
60
117
  export function cueOffsetOnSpine(spine, counts, cueLineId) {
61
118
  for (const seg of spine) {
62
119
  const idx = seg.lineIds.indexOf(cueLineId);
@@ -115,31 +172,61 @@ export function buildTimeline({ manifest, storyboard, skip, probe, exists, readW
115
172
  continue;
116
173
  }
117
174
 
175
+ const narrated = plan.narrated || scene.wordsFrom === "voice";
176
+ const voice = exists(scene.voice) ? scene.voice : null;
177
+ const flag = scene.flagged ? " (flagged)" : "";
178
+
118
179
  if (exists(scene.clip)) {
119
180
  const clip = probe(scene.clip);
120
- const voice = clip.hasAudio || !exists(scene.voice) ? null : scene.voice;
121
- const flag = scene.flagged ? " (flagged)" : "";
122
- const silent = clip.hasAudio ? "" : voice ? `, silent clip + ${voice}` : ", silent clip";
123
- spine.push({
124
- sceneIndex: n, source: "clip", file: scene.clip, seconds: clip.seconds,
125
- hasAudio: clip.hasAudio, voice, startSec, lineIds: plan.lineIds,
126
- words: readWords(scene.words), note: `${flag}${silent}`,
127
- });
128
- startSec += clip.seconds;
181
+ const base = {
182
+ sceneIndex: n, source: "clip", file: scene.clip, clipSeconds: clip.seconds,
183
+ startSec, lineIds: plan.lineIds,
184
+ };
185
+ if (voice && (narrated || !clip.hasAudio)) {
186
+ const fit = fitNarration({ clipSeconds: clip.seconds, voiceSeconds: probe(voice).seconds });
187
+ const how = [];
188
+ if (fit.trimmedBy > 0.05) how.push(`clip trimmed ${clip.seconds.toFixed(1)}s -> ${fit.seconds.toFixed(1)}s`);
189
+ if (fit.atempo > 1.001) how.push(`voice sped up ${fit.atempo.toFixed(2)}x`);
190
+ if (fit.holdSec > 0.05) how.push(`last frame held ${fit.holdSec.toFixed(1)}s`);
191
+ spine.push({
192
+ ...base, audio: "narration", voice, seconds: fit.seconds, atempo: fit.atempo,
193
+ holdSec: fit.holdSec, words: scaleWords(readWords(scene.words), fit.atempo),
194
+ note: `${flag}, ${voice} replaces the clip's audio${how.length ? ` (${how.join(", ")})` : ""}`,
195
+ });
196
+ } else if (narrated) {
197
+ spine.push({
198
+ ...base, audio: "silence", voice: null, seconds: clip.seconds, atempo: 1, holdSec: 0,
199
+ words: null,
200
+ note: `${flag}, narrated scene without its voice track: the clip's own audio is muted`,
201
+ });
202
+ } else if (clip.hasAudio) {
203
+ spine.push({
204
+ ...base, audio: "clip", voice: null, seconds: clip.seconds, atempo: 1, holdSec: 0,
205
+ words: readWords(scene.words), note: `${flag}, its own dialogue`,
206
+ });
207
+ } else {
208
+ spine.push({
209
+ ...base, audio: "silence", voice: null, seconds: clip.seconds, atempo: 1, holdSec: 0,
210
+ words: null, note: `${flag}, silent clip`,
211
+ });
212
+ }
213
+ startSec += spine[spine.length - 1].seconds;
129
214
  continue;
130
215
  }
131
216
  if (exists(scene.keyframe)) {
132
- const voice = exists(scene.voice) ? scene.voice : null;
133
- const seconds = voice ? probe(voice).seconds : (plan.durationSec ?? 4);
217
+ const fit = voice
218
+ ? fitNarration({ clipSeconds: null, voiceSeconds: probe(voice).seconds })
219
+ : { seconds: plan.durationSec ?? 4, atempo: 1, holdSec: 0 };
134
220
  spine.push({
135
- sceneIndex: n, source: "still", file: scene.keyframe, seconds,
136
- hasAudio: false, voice, startSec, lineIds: plan.lineIds,
221
+ sceneIndex: n, source: "still", file: scene.keyframe, clipSeconds: null,
222
+ seconds: fit.seconds, audio: voice ? "narration" : "silence", voice, atempo: 1, holdSec: 0,
223
+ startSec, lineIds: plan.lineIds,
137
224
  // #1689: on a narrated scene the words are the voice track's, and the
138
225
  // voice starts where the segment does, so they need no offset here.
139
226
  words: voice ? readWords(scene.words) : null,
140
- note: `${voice ? ` + ${voice}` : ", silent"} (${seconds.toFixed(1)}s, clip ${scene.clipStatus})`,
227
+ note: `${flag}${voice ? ` + ${voice}` : ", silent"} (${fit.seconds.toFixed(1)}s, clip ${scene.clipStatus})`,
141
228
  });
142
- startSec += seconds;
229
+ startSec += fit.seconds;
143
230
  continue;
144
231
  }
145
232
  dropped.push({ n, reason: `scene ${n}: ${nothing}` });
@@ -193,14 +280,20 @@ export function buildFfmpegArgs(timeline, { resolve, music, out }) {
193
280
  seg.source === "clip"
194
281
  ? addInput("-i", resolve(seg.file))
195
282
  : addInput("-loop", "1", "-t", String(seg.seconds), "-i", resolve(seg.file));
196
- filters.push(`${norm(`${i}:v`)}[v${i}]`);
197
- if (seg.hasAudio) {
198
- filters.push(`[${i}:a]${stereo}[a${i}]`);
199
- } else if (seg.voice) {
283
+ const fit = [];
284
+ if (seg.source === "clip" && seg.clipSeconds !== null && seg.seconds < seg.clipSeconds - 0.01) {
285
+ fit.push(`trim=0:${sec(seg.seconds)},setpts=PTS-STARTPTS`);
286
+ }
287
+ if (seg.holdSec > 0.01) fit.push(`tpad=stop_mode=clone:stop_duration=${sec(seg.holdSec)}`);
288
+ filters.push(`${norm(`${i}:v`)}${fit.length ? `,${fit.join(",")}` : ""}[v${i}]`);
289
+ if (seg.audio === "narration") {
200
290
  const v = addInput("-i", resolve(seg.voice));
201
- filters.push(`[${v}:a]${stereo},apad,atrim=0:${seg.seconds}[a${i}]`);
291
+ const tempo = seg.atempo > 1.001 ? `atempo=${seg.atempo.toFixed(4)},` : "";
292
+ filters.push(`[${v}:a]${stereo},${tempo}${LOUDNORM},apad,atrim=0:${sec(seg.seconds)}[a${i}]`);
293
+ } else if (seg.audio === "clip") {
294
+ filters.push(`[${i}:a]${stereo},${LOUDNORM},apad,atrim=0:${sec(seg.seconds)}[a${i}]`);
202
295
  } else {
203
- filters.push(`anullsrc=r=48000:cl=stereo,atrim=0:${seg.seconds}[a${i}]`);
296
+ filters.push(`anullsrc=r=48000:cl=stereo,atrim=0:${sec(seg.seconds)}[a${i}]`);
204
297
  }
205
298
  pairs.push(`[v${i}][a${i}]`);
206
299
  }
@@ -227,13 +320,17 @@ export function buildFfmpegArgs(timeline, { resolve, music, out }) {
227
320
  videoLabel = `ov${j}`;
228
321
  });
229
322
 
230
- let audioLabel = "voice";
323
+ filters.push(`${ROOM_TONE}[room]`);
324
+ const beds = ["[room]"];
231
325
  if (music) {
232
326
  const m = addInput("-stream_loop", "-1", "-i", music);
233
- filters.push(`[${m}:a]${stereo},volume=0.18[bed]`);
234
- filters.push(`[voice][bed]amix=inputs=2:duration=first:dropout_transition=0:normalize=0[mix]`);
235
- audioLabel = "mix";
327
+ filters.push(`[${m}:a]${stereo},volume=${MUSIC_VOLUME}[bed]`);
328
+ beds.push("[bed]");
236
329
  }
330
+ filters.push(
331
+ `[voice]${beds.join("")}amix=inputs=${beds.length + 1}:duration=first:dropout_transition=0:normalize=0[mix]`,
332
+ );
333
+ const audioLabel = "mix";
237
334
 
238
335
  return [
239
336
  "-y", "-hide_banner", "-loglevel", "error",
@@ -464,7 +561,7 @@ function main() {
464
561
  }
465
562
  const silent = noWordTimes(timeline);
466
563
  if (silent.length > 0) console.log(`No word times: ${silent.join(", ")}`);
467
- console.log(`Music bed: ${music ?? "none"}`);
564
+ console.log(`Room tone: under the whole ad. Music bed: ${music ?? "none"}`);
468
565
  console.log(`Upload it: exodus video upload ${manifest.runId} --file ${out}`);
469
566
  }
470
567