@cliangdev/conductor 0.25.0 → 0.27.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.
- package/assets/claude/agents/conductor-coder.md +25 -0
- package/assets/claude/agents/conductor-content-art-director.md +68 -0
- package/assets/claude/agents/conductor-content-creative-director.md +56 -0
- package/assets/claude/agents/conductor-content-ideator.md +49 -0
- package/assets/claude/agents/conductor-content-scriptwriter.md +42 -0
- package/assets/claude/agents/conductor-content-strategist.md +47 -0
- package/assets/claude/commands/conductor/content.md +64 -0
- package/assets/claude/commands/conductor/creative.md +46 -46
- package/assets/claude/commands/conductor/implement.md +1 -1
- package/assets/claude/skills/conductor-content-studio/SKILL.md +323 -0
- package/assets/claude/skills/conductor-content-studio/references/capabilities.md +61 -0
- package/assets/claude/skills/conductor-content-studio/references/compliance-checklist.md +120 -0
- package/assets/claude/skills/conductor-content-studio/references/idea-template.md +54 -0
- package/assets/claude/skills/conductor-content-studio/references/platform-specs.md +59 -0
- package/assets/claude/skills/conductor-content-studio/references/publish-pack-template.md +121 -0
- package/assets/claude/skills/conductor-content-studio/references/rubric.md +99 -0
- package/assets/claude/skills/conductor-content-studio/references/script-template.md +53 -0
- package/assets/claude/skills/conductor-content-studio/references/shotlist-template.md +44 -0
- package/assets/claude/skills/conductor-creative/SKILL.md +111 -45
- package/assets/claude/skills/conductor-creative/references/brand-kit-setup.md +41 -0
- package/assets/claude/skills/conductor-publisher/SKILL.md +26 -2
- package/assets/cli-manifest.json +48 -3
- package/dist/creative/assertions.js +22 -11
- package/dist/creative/frame.js +3 -3
- package/dist/creative/job/file-transport.mjs +211 -0
- package/dist/creative/job/render.mjs +84 -2
- package/dist/creative/job/server.mjs +70 -13
- package/dist/lib/creative-render.d.ts +62 -0
- package/dist/lib/creative-render.d.ts.map +1 -1
- package/dist/lib/creative-render.js +51 -0
- package/dist/lib/creative-render.js.map +1 -1
- package/dist/lib/plugin-assets.d.ts.map +1 -1
- package/dist/lib/plugin-assets.js +17 -0
- package/dist/lib/plugin-assets.js.map +1 -1
- package/dist/mcp/index.d.ts +856 -261
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +164 -75
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/tools/creative-drafts.d.ts +134 -0
- package/dist/mcp/tools/creative-drafts.d.ts.map +1 -0
- package/dist/mcp/tools/creative-drafts.js +405 -0
- package/dist/mcp/tools/creative-drafts.js.map +1 -0
- package/dist/mcp/tools/creatives.d.ts +3 -0
- package/dist/mcp/tools/creatives.d.ts.map +1 -1
- package/dist/mcp/tools/creatives.js.map +1 -1
- package/dist/mcp/tools/issues.d.ts.map +1 -1
- package/dist/mcp/tools/issues.js +6 -1
- package/dist/mcp/tools/issues.js.map +1 -1
- package/package.json +1 -1
- package/scripts/build-creative.mjs +1 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Publish pack
|
|
2
|
+
|
|
3
|
+
Two files in the run folder, written by the orchestrator in stage S5b from the scriptwriter's
|
|
4
|
+
`## Publish copy` section in `script.md`, the art director's `## Publish assets` section in
|
|
5
|
+
`direction.md`, and the compliance results (`compliance-checklist.md`). The pack is what the
|
|
6
|
+
publisher uses later to pre-fill the Post, so it holds only what the Post needs. A run never
|
|
7
|
+
creates a Post.
|
|
8
|
+
|
|
9
|
+
Short-form craft this encodes: the search keyword sits in the caption's opening words, on-screen
|
|
10
|
+
text and speech (TikTok search reads all three); a hook swap is the standard creative iteration;
|
|
11
|
+
hook rate is 3-second plays over impressions and hold rate is completions over 3-second plays;
|
|
12
|
+
posts work with the sound off.
|
|
13
|
+
|
|
14
|
+
## Hook types
|
|
15
|
+
|
|
16
|
+
Tag every hook, in ideas and in the pack, with one of: `question`, `bold-claim`,
|
|
17
|
+
`pattern-interrupt`, `relatable-pain`, `how-to`, `number`, `curiosity-gap`, `story`,
|
|
18
|
+
`social-proof`. Use another short kebab-case word only when none fits.
|
|
19
|
+
|
|
20
|
+
## publish-pack.md (for people)
|
|
21
|
+
|
|
22
|
+
One section per platform the brief asks for. Omit a platform it does not.
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
# Publish pack: <title>
|
|
26
|
+
|
|
27
|
+
Run: <runId> Creative: <display id, or "not yet committed"> Tags: hook <type>, angle <angle>, format <format>
|
|
28
|
+
|
|
29
|
+
## <Platform> (<feed | reel | story>, as the destination's formats allow)
|
|
30
|
+
- **Caption:** <final text; the search keyword is in the first line, within the first ~125
|
|
31
|
+
characters; within the platform's limit>
|
|
32
|
+
- **Hashtags:** <3 to 5>
|
|
33
|
+
- **First comment (post manually):** <text, or "none">. Conductor cannot post comments.
|
|
34
|
+
- **Cover frame:** <video: timestamp in seconds and what is on it; carousel: card 1; still: n/a>
|
|
35
|
+
- **Alt text:** <one line per image or card, in order>
|
|
36
|
+
- **On-screen text placement:** <where each line sits, inside the safe zone>
|
|
37
|
+
- **Sound:** <source and licence: original audio | Commercial Music Library track "<name>" |
|
|
38
|
+
library track id and its licence | clip's own sound | none>
|
|
39
|
+
- **Posting window:** <best hour and weekday from get_marketing_insights byHour/byWeekday for this
|
|
40
|
+
platform, with the window it came from, or "no data yet">
|
|
41
|
+
- **Options:** <the option keys and values this platform gets, from publish-pack.json>
|
|
42
|
+
|
|
43
|
+
## Alternate hooks (for variantOf hook experiments)
|
|
44
|
+
1. <hook line> (<hookType>)
|
|
45
|
+
2. <hook line> (<hookType>)
|
|
46
|
+
3. <optional>
|
|
47
|
+
|
|
48
|
+
## Compliance
|
|
49
|
+
<the table from compliance-checklist.md: check, answer, what was done, option set>
|
|
50
|
+
|
|
51
|
+
## Third-party assets
|
|
52
|
+
| Asset | Whose | Permission or licence |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| <music, footage, logo, meme, likeness, trademark, parody> | ... | ... |
|
|
55
|
+
(Write "none" if the post uses only the brand's own and generated-by-Conductor material.)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## publish-pack.json (for the publisher)
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"creativeId": null,
|
|
63
|
+
"platforms": [
|
|
64
|
+
{
|
|
65
|
+
"platform": "tiktok",
|
|
66
|
+
"format": "feed",
|
|
67
|
+
"captionOverride": "<final caption>",
|
|
68
|
+
"options": { "brandOrganicToggle": true, "isAigc": false, "videoCoverTimestampMs": 1500 }
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"platform": "instagram",
|
|
72
|
+
"format": "feed",
|
|
73
|
+
"captionOverride": "<final caption>",
|
|
74
|
+
"options": { "altText": "<alt text for a single feed image>" }
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"altText": ["<card 1 alt text>", "<card 2 alt text>"],
|
|
78
|
+
"alternateHooks": [{ "hook": "<line>", "hookType": "question" }],
|
|
79
|
+
"tags": { "hookType": "bold-claim", "angle": "utility-education", "format": "carousel" }
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Rules:
|
|
84
|
+
|
|
85
|
+
- `creativeId` is null until the person approves the commit (S6, On Approve), then it is the
|
|
86
|
+
Creative's id. A `FILM` run leaves it null: the publisher is pointed at the pack by the person.
|
|
87
|
+
- `platforms[]` items are shaped to drop straight into `create_post` targets or
|
|
88
|
+
`set_publish_targets` targets: `platform`, `format` (`feed`, `reel` or `story`, and only one the destination's `formats` offers: at the time
|
|
89
|
+
of writing TikTok and YouTube are feed only, Facebook and Instagram offer all three),
|
|
90
|
+
`captionOverride`, and `options`. Name `options` `publishOptions` for `set_publish_targets`. They
|
|
91
|
+
carry no account: the publisher resolves the account (`account` or `connectionId`) from
|
|
92
|
+
`list_publish_targets`. `captionOverride` is ignored on a story.
|
|
93
|
+
- `captionOverride` is the **complete text that gets posted**: the caption, then a blank line, then that
|
|
94
|
+
platform's hashtags. The publisher posts it as-is, so hashtags listed only in `publish-pack.md` never
|
|
95
|
+
reach the platform. The first comment is the one thing that stays out: it is posted by hand.
|
|
96
|
+
- `options` holds real option keys only, in the camelCase the destination reports under
|
|
97
|
+
`optionKeys`. Never add a key that is not in this table, and never `privacyLevel` (the person
|
|
98
|
+
picks it from the account's allowed levels at publish time):
|
|
99
|
+
|
|
100
|
+
| Platform | Keys the pack may set | Applies to |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| TikTok | `brandContentToggle`, `brandOrganicToggle` | video and photo posts |
|
|
103
|
+
| TikTok | `isAigc` | video posts only |
|
|
104
|
+
| TikTok | `videoCoverTimestampMs` (milliseconds, from the cover frame) | video posts only |
|
|
105
|
+
| TikTok | `disableComment` | video and photo posts |
|
|
106
|
+
| TikTok | `disableDuet`, `disableStitch` | video posts only |
|
|
107
|
+
| Instagram | `altText` (at most 1000 characters) | a single feed image only |
|
|
108
|
+
| Instagram | `audioName`, `shareToFeed`, `collaborators` (at most 3, not on a carousel) | reels only |
|
|
109
|
+
| YouTube | `containsSyntheticMedia` | video |
|
|
110
|
+
|
|
111
|
+
Set only what the compliance check or the plan decided: leave a key out rather than writing a
|
|
112
|
+
default. Keys that need a Post's own asset id (`coverAssetId`, `thumbnailAssetId`,
|
|
113
|
+
`photoCoverIndex`) are not known until media is attached to a Post, so the pack does not carry
|
|
114
|
+
them; the cover frame is in `publish-pack.md` for the publisher to act on.
|
|
115
|
+
- `altText` is one string per image or card, in order. It is also the source for the Creative's
|
|
116
|
+
own `altText` field.
|
|
117
|
+
- `alternateHooks` and `tags` are for learning and experiments, not for publishing. `tags` is also
|
|
118
|
+
copied into `run.json`.
|
|
119
|
+
- `tags.angle` is the ideator's angle: `story-emotion`, `utility-education` or `trend-humor`.
|
|
120
|
+
`tags.format` is the idea's format (`still`, `carousel`, `story`, `motion`, `clip` or `film`).
|
|
121
|
+
- Valid JSON, no comments.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Judging rubric
|
|
2
|
+
|
|
3
|
+
Used by the `conductor-content-creative-director` agent in `text-round` and `visual-round`, and by
|
|
4
|
+
the orchestrator when it needs to break a tie. Everything here exists to counter a known judge
|
|
5
|
+
failure mode, so follow it as written.
|
|
6
|
+
|
|
7
|
+
## Why it is shaped this way
|
|
8
|
+
|
|
9
|
+
- LLM judges favour longer answers, the first-listed answer and the answer written in their own
|
|
10
|
+
style (Zheng et al. 2023, MT-Bench). So ideas arrive at the same length, under shuffled neutral
|
|
11
|
+
IDs, and finalists are compared in both orders.
|
|
12
|
+
- A judge that scores before it reasons anchors on its first impression, and a judge asked to
|
|
13
|
+
confirm a leader mostly confirms it (Verga et al. 2024 on panels of different models). So:
|
|
14
|
+
reasoning is written first, the judge is a different model tier from the ideators, and a
|
|
15
|
+
devil's-advocate pass runs against the leader.
|
|
16
|
+
- Ideas generated by one assisted process converge on the same few mechanics (Doshi & Hauser 2024),
|
|
17
|
+
and a model asked to debate itself repeats its first stance (Liang et al., degeneration of
|
|
18
|
+
thought). So novelty is scored explicitly, and the leader must be argued against.
|
|
19
|
+
|
|
20
|
+
## Criteria and weights
|
|
21
|
+
|
|
22
|
+
Score each 1 to 5. Weighted total = sum(score x weight) / 5, so the maximum is 100.
|
|
23
|
+
|
|
24
|
+
| Criterion | Weight | 1 | 3 | 5 |
|
|
25
|
+
|---|---|---|---|---|
|
|
26
|
+
| Hook strength | 25 | Opens on a greeting, a logo or context; nothing makes a stranger stay | A clear promise or question, but generic; could open any account's post | A specific, surprising or tension-filled claim a stranger understands in 1s, spoken and on screen |
|
|
27
|
+
| Audience relevance | 20 | Aimed at "everyone"; ignores the brief's tension | Names the audience's topic but not their actual friction | Opens on a friction the brief names and the audience would recognise as theirs |
|
|
28
|
+
| Brand fit | 15 | Breaks a copy rule or approved-line, or reads off-voice | Within the rules but could be any brand | Uses an approved line or the knowledge page's voice naturally; no rule risk |
|
|
29
|
+
| Feasibility with assets on hand | 15 | Needs assets that do not exist and cannot be made here (and is tagged `RENDER`) | Needs one new photo or clip the user can supply easily | Fully buildable from `media.json` or a render with no photo; or an honest `FILM` with a simple shoot |
|
|
30
|
+
| Novelty | 15 | The mechanic is the most common one for this topic (listicle, "5 tips", before/after) | A familiar mechanic with a fresh angle | A mechanic not seen elsewhere in the pool or in the performance data, still easy to grasp |
|
|
31
|
+
| Shareability | 10 | Nothing to send to a friend; purely informational | Useful or pleasant, but no reason to send | A person would send it to a named other person ("this is you") or save it for later |
|
|
32
|
+
|
|
33
|
+
## Scoring protocol
|
|
34
|
+
|
|
35
|
+
1. Read `brief.md`, then every idea in `ideas/pool.md`. Ideas are identified by ID only; ignore the
|
|
36
|
+
order they appear in and any difference in polish.
|
|
37
|
+
2. For each idea, per criterion, write one or two sentences of reasoning FIRST, citing the idea's
|
|
38
|
+
own words. Then write the score. Never write a score before its reasoning.
|
|
39
|
+
3. Penalise nothing for length (ideas are length-matched) and reward nothing for confident tone.
|
|
40
|
+
4. Compute the weighted total. Shortlist the top 3. If ties, apply the tie-break rule below.
|
|
41
|
+
5. Any idea that breaks a copy rule or contradicts an approved line is capped at 2 on brand fit
|
|
42
|
+
and cannot be a finalist unless the fix is a one-word edit you state.
|
|
43
|
+
|
|
44
|
+
## Pairwise protocol (visual round)
|
|
45
|
+
|
|
46
|
+
For each pair of finalists (A,B), (A,C), (B,C):
|
|
47
|
+
|
|
48
|
+
1. Judge the pair twice, once with each order, as two independent comparisons. Write reasoning
|
|
49
|
+
first, then the verdict "first" or "second", then say which idea won.
|
|
50
|
+
2. If the two orders name different winners (position flipped the result), the pair is a tie.
|
|
51
|
+
3. Tally wins: win = 1, tie = 0.5 each. The leader has the most points.
|
|
52
|
+
|
|
53
|
+
## Judging a rendered draft
|
|
54
|
+
|
|
55
|
+
Look at the actual image file, not the text description. Check:
|
|
56
|
+
|
|
57
|
+
- Readability at phone size: shrink it mentally to a thumbnail. Can the headline be read in a glance?
|
|
58
|
+
- Hook legibility in 1 second: is the first thing seen the hook, in high contrast?
|
|
59
|
+
- Safe zones: text out of the platform UI (see `platform-specs.md`), nothing sitting on a face.
|
|
60
|
+
- Brand look: tokens, font, logo lockup, accent phrase on the words that carry the meaning.
|
|
61
|
+
- Whether the picture and the words do different jobs (photo shows, words claim) rather than
|
|
62
|
+
repeating each other.
|
|
63
|
+
|
|
64
|
+
A finalist that could not be rendered keeps its text-round score; say so, and do not treat the
|
|
65
|
+
missing image as a defect of the idea, except when the failure came from copy the validator refused.
|
|
66
|
+
A `FILM` finalist has no image: judge its script and shot description against the same criteria and
|
|
67
|
+
note that the visual round could not test it.
|
|
68
|
+
|
|
69
|
+
## Devil's-advocate pass
|
|
70
|
+
|
|
71
|
+
After the tally, and before naming a winner, write the strongest case against the leader in at
|
|
72
|
+
least five sentences: the likeliest way it fails with this audience, the platform risk, the
|
|
73
|
+
brand risk, and what the runner-up does better. Then decide whether the case is strong enough to
|
|
74
|
+
change the winner. State the answer plainly. Do not soften the case to protect the leader.
|
|
75
|
+
|
|
76
|
+
### Veto questions
|
|
77
|
+
|
|
78
|
+
The pass must also answer these four about the leader, each yes or no with one line of reasoning.
|
|
79
|
+
They come from `compliance-checklist.md`.
|
|
80
|
+
|
|
81
|
+
1. Does it rely on an **unsubstantiated claim** (nothing in the brief, knowledge or approved lines
|
|
82
|
+
confirms it)?
|
|
83
|
+
2. Is there a **trademark or parody risk** (another company's product, brand, logo or likeness)?
|
|
84
|
+
3. Is there a **representation or sensitivity problem**?
|
|
85
|
+
4. Is **disclosure needed** (anyone paid, gifted, an employee or an affiliate) that the brief does
|
|
86
|
+
not already handle?
|
|
87
|
+
|
|
88
|
+
A "yes" that a copy edit can fix is fixed or noted for the scriptwriter. A "yes" that cannot be
|
|
89
|
+
fixed in copy disqualifies the idea: record it in `decision.md` under **Veto**, take the next
|
|
90
|
+
leader on the tally and run the same questions on it. Disclosure that only needs a toggle and a
|
|
91
|
+
caption line is not a disqualifier; note it.
|
|
92
|
+
|
|
93
|
+
## Tie-break rule
|
|
94
|
+
|
|
95
|
+
If two ideas are within 3 points of weighted total, or tied on the pairwise tally, prefer in order:
|
|
96
|
+
1. the higher hook-strength score;
|
|
97
|
+
2. the idea buildable entirely from assets on hand (`RENDER` over `FILM`);
|
|
98
|
+
3. the higher novelty score;
|
|
99
|
+
4. the lower ID number (an arbitrary but fixed choice, so no one can say the order was chosen).
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Script template
|
|
2
|
+
|
|
3
|
+
`script.md` for the winning idea. Short-form craft rules this encodes: the hook lands in 0 to 3
|
|
4
|
+
seconds, spoken and on screen; something new happens every 3 to 5 seconds; there is a payoff, then
|
|
5
|
+
a call to action; the piece works with the sound off.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
# Script: <title>
|
|
9
|
+
|
|
10
|
+
Platform: <platform(s)> Length: <seconds> Tag: RENDER | FILM
|
|
11
|
+
Idea: <id> Source decision: decision.md
|
|
12
|
+
|
|
13
|
+
## Hook (0-3s)
|
|
14
|
+
- Spoken: <line, max 12 words, or "none">
|
|
15
|
+
- On screen: <text, max 12 words>
|
|
16
|
+
- Visual: <what is shown>
|
|
17
|
+
|
|
18
|
+
## Beats
|
|
19
|
+
| # | Time | Visual | On-screen text | Spoken / audio | What changes |
|
|
20
|
+
|---|------|--------|----------------|----------------|--------------|
|
|
21
|
+
| 1 | 3-6s | ... | ... | ... | <the new beat or pattern interrupt> |
|
|
22
|
+
| 2 | 6-10s | ... | ... | ... | ... |
|
|
23
|
+
|
|
24
|
+
## Payoff
|
|
25
|
+
<the line or moment that delivers the hook's promise, with time>
|
|
26
|
+
|
|
27
|
+
## CTA
|
|
28
|
+
<one action; use the kit's CTA claim or an approved line where there is one>
|
|
29
|
+
|
|
30
|
+
## Publish copy
|
|
31
|
+
Per platform the brief asks for. The keyword the audience would search for sits in the caption's
|
|
32
|
+
opening words (and in the on-screen text and speech); the first line carries the hook.
|
|
33
|
+
- TikTok: <caption; keep the visible part to about 150 characters>
|
|
34
|
+
- Instagram: <caption; first line carries the hook, max 125 characters shown before "more">
|
|
35
|
+
- YouTube Shorts: <title max 100 characters>
|
|
36
|
+
- Facebook: <caption>
|
|
37
|
+
- Hashtags: <3 to 5, no more>
|
|
38
|
+
- First comment: <text, or "none"; posted by hand, because Conductor cannot post comments>
|
|
39
|
+
- Alternate hooks (hook swaps for variantOf experiments):
|
|
40
|
+
1. <hook line> (<hookType>)
|
|
41
|
+
2. <hook line> (<hookType>)
|
|
42
|
+
3. <optional>
|
|
43
|
+
|
|
44
|
+
Hook types: question, bold-claim, pattern-interrupt, relatable-pain, how-to, number,
|
|
45
|
+
curiosity-gap, story, social-proof.
|
|
46
|
+
|
|
47
|
+
## Copy-rule check
|
|
48
|
+
<each must-say / must-avoid item from brief.md, and ok or the fix>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Rules: a beat every 3 to 5 seconds; no beat longer than 5 seconds without a visual or text change;
|
|
52
|
+
caption limits are in `platform-specs.md`; alternates differ from the main hook in hook type, not just wording (check current platform guidance); approved lines run
|
|
53
|
+
verbatim; leave out platforms the brief did not ask for.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Shot list and direction template
|
|
2
|
+
|
|
3
|
+
`direction.md` (RENDER) and `shoot/shoot-script.md` (FILM) both use this table. A RENDER direction
|
|
4
|
+
has no camera, so "visual" is the layout or frame, and the final section maps the direction onto
|
|
5
|
+
the Creative's own fields.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
# Direction: <title>
|
|
9
|
+
|
|
10
|
+
## Shot list
|
|
11
|
+
| Time | Visual | On-screen text | VO / audio | Asset |
|
|
12
|
+
|------|--------|----------------|------------|-------|
|
|
13
|
+
| 0-3s | ... | ... | ... | <media id, "to film", or none> |
|
|
14
|
+
|
|
15
|
+
## Safe zones
|
|
16
|
+
- Keep text out of the top ~14%, the bottom ~35% and ~6% at each side (9:16; check current
|
|
17
|
+
platform guidance).
|
|
18
|
+
- Headline and CTA sit in the middle band. Nothing on a face.
|
|
19
|
+
|
|
20
|
+
## Sound
|
|
21
|
+
<sound on or off by design; music track id if any; voice-over; captions are always on screen>
|
|
22
|
+
|
|
23
|
+
## Publish assets
|
|
24
|
+
Per platform the script names:
|
|
25
|
+
- Cover frame: <video: timestamp in seconds and what is on it; carousel: card 1; still: n/a>
|
|
26
|
+
- Alt text: <one line per image or card, in order>
|
|
27
|
+
- On-screen text placement: <where each line sits, inside the safe zone for that platform>
|
|
28
|
+
- Sound source and licence: <original audio | Commercial Music Library track | library track id
|
|
29
|
+
and licence | clip's own sound | none>. Never a trending track a business account cannot license.
|
|
30
|
+
|
|
31
|
+
## Capture settings (FILM only)
|
|
32
|
+
9:16 vertical, 1080x1920, 30 fps, sound on, lit from the front, phone steady, shoot extra B-roll.
|
|
33
|
+
|
|
34
|
+
## Ready-to-paste text (FILM only)
|
|
35
|
+
<on-screen text lines in order, and the captions per platform>
|
|
36
|
+
|
|
37
|
+
## Mapping to the Creative (RENDER only)
|
|
38
|
+
- kind: STILL | MOTION
|
|
39
|
+
- layout / theme: <from the kit and registry>
|
|
40
|
+
- sequenceKind: story | carousel | none (carouselRatio for a carousel)
|
|
41
|
+
- motion: preset (fade-up | word-by-word | accent-pop | none), durationSec, background (photo or
|
|
42
|
+
clip, with its motion)
|
|
43
|
+
- audio: clip | track | none
|
|
44
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: conductor-creative
|
|
3
|
-
description: Creates on-brand marketing creative through the Conductor MCP tools — static ads and short videos alike, rendered locally on this machine
|
|
3
|
+
description: Creates on-brand marketing creative through the Conductor MCP tools — static ads and short videos alike, rendered locally on this machine as a draft, checked by eye and approved before anything is saved to Conductor. Covers a photo-plus-headline ad at every enabled placement (e.g. TikTok/Reels 9:16, Instagram feed 4:5, Facebook 1:1); a short video made from a still photo or an existing clip (animated text, zoom/pan, background music, a TikTok/Reels/Shorts-ready cut); posting a finished video as-is; a story or carousel sequence; a lettered headline/hook variant for testing; and, when a workspace's Brand Kit has never been set up, setting one up first (colours, font, logo, copy rules) from a website, a brand doc, or a few direct answers. Use when asked to make, design, write or export a social ad, a promo image, a short-form video, an animated ad, or to configure/update a project's brand.
|
|
4
4
|
user-invocable: true
|
|
5
5
|
allowed-tools: mcp__conductor__*, AskUserQuestion, Read, Glob, Grep, WebFetch
|
|
6
6
|
---
|
|
@@ -17,7 +17,13 @@ things that already exist for this workspace — a Brand Kit (data: tokens, copy
|
|
|
17
17
|
lines) and a Knowledge page (prose: positioning, voice, facts). Read them rather than inventing.
|
|
18
18
|
|
|
19
19
|
Rendering happens locally, through this MCP server, with a browser on this machine — there is no
|
|
20
|
-
separate render service to wait on.
|
|
20
|
+
separate render service to wait on. A preview typically finishes in well under a minute.
|
|
21
|
+
|
|
22
|
+
**Draft first, save after approval.** Every Creative here goes draft, look, approve, commit:
|
|
23
|
+
`preview_creative_draft` renders the artwork on this machine and writes **nothing** to Conductor — no
|
|
24
|
+
Creative, no upload — then the person looks at it, and only on their approval does
|
|
25
|
+
`commit_creative_draft` upload the media, save the Creative and do the full render. Never commit a
|
|
26
|
+
draft the person has not seen and approved.
|
|
21
27
|
|
|
22
28
|
## How to make a Creative
|
|
23
29
|
|
|
@@ -50,8 +56,8 @@ Prefer an approved line from the Knowledge page verbatim. Otherwise write one th
|
|
|
50
56
|
real and specific, not a generic claim.
|
|
51
57
|
|
|
52
58
|
If the Brand Kit's `accentPhraseRequired` is true, mark exactly one phrase per headline with
|
|
53
|
-
asterisks: `Every saved link, *finally usable*.` Never zero, never two — `
|
|
54
|
-
`update_creative` refuse a headline that gets this wrong, naming the rule.
|
|
59
|
+
asterisks: `Every saved link, *finally usable*.` Never zero, never two — `preview_creative_draft`,
|
|
60
|
+
`create_creative` and `update_creative` refuse a headline that gets this wrong, naming the rule.
|
|
55
61
|
|
|
56
62
|
Keep it short — checked at thumbnail size, not read up close.
|
|
57
63
|
|
|
@@ -69,10 +75,13 @@ uploading a new one — id, dimensions, checked/blocked state and provenance for
|
|
|
69
75
|
the project's media library. A photo needs `checked: true` and not `blocked` before a Creative can
|
|
70
76
|
go to `READY`.
|
|
71
77
|
|
|
72
|
-
A
|
|
73
|
-
|
|
74
|
-
(
|
|
75
|
-
|
|
78
|
+
A photo that is not in the library yet is previewed straight from its local file (`photoPath` on
|
|
79
|
+
`preview_creative_draft`; a URL is downloaded to a local file first) and is only uploaded when the
|
|
80
|
+
draft is committed — pass `source` (a URL or a short provenance note, e.g. "Generated with a named
|
|
81
|
+
model on a given date") and `licence` (e.g. "Own work", "Unsplash Licence", "Generated, house use")
|
|
82
|
+
to `commit_creative_draft`, which records them on every file it uploads. This closes a real legal
|
|
83
|
+
exposure: an unattributed or unlicensed photo running as paid or organic media. A photo already in
|
|
84
|
+
the library is used by its id (`photoId`) with no upload at all.
|
|
76
85
|
|
|
77
86
|
**Open the photo and look at it before using it.** A picture can break every copy rule with text,
|
|
78
87
|
a logo or a claim burned into its pixels, and no automated check can see that — this is the first
|
|
@@ -83,48 +92,98 @@ Match the crop to the subject: a layout that lays copy over the photo (e.g. `ble
|
|
|
83
92
|
the top third in collision with the headline — prefer a layout that keeps the photo and copy on
|
|
84
93
|
separate panels (e.g. `stacked`) for those.
|
|
85
94
|
|
|
86
|
-
### 6.
|
|
87
|
-
|
|
88
|
-
`
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
95
|
+
### 6. Preview the draft
|
|
96
|
+
|
|
97
|
+
`preview_creative_draft` with the Brand Kit (if not the default), layout, theme, photo (`photoPath`
|
|
98
|
+
for a local file, `photoId` for one already in the library), headline, body, caption and alt text.
|
|
99
|
+
It renders a contact sheet on this machine and returns it as an image, with the `draftDir` it wrote
|
|
100
|
+
to (`.conductor/drafts/<timestamp>-<slug>/`), the readiness gaps and any render warnings. **Nothing
|
|
101
|
+
has been saved to Conductor.** A copy-rule or accent-phrase violation comes back as an error naming
|
|
102
|
+
the rule and field, without rendering — fix the copy and preview again.
|
|
103
|
+
|
|
104
|
+
To change an existing Creative's copy, layout or photo, preview the edit first with `baseCreativeId`:
|
|
105
|
+
only the fields you pass change, and the commit then updates that Creative instead of creating one.
|
|
106
|
+
|
|
107
|
+
To test a headline variant of an existing concept, preview the new headline the same way
|
|
108
|
+
(`baseCreativeId` plus `headline`) so the person sees it on the artwork — but do **not** commit that
|
|
109
|
+
draft, since a commit would update the original. Once they approve, call `create_creative` with
|
|
110
|
+
`variantOf` and the headline instead: it inherits everything else and gets the next letter.
|
|
111
|
+
|
|
112
|
+
Pass `full: true` to render every placement at full size instead of the contact sheet (slower; the
|
|
113
|
+
image returned is the first placement).
|
|
114
|
+
|
|
115
|
+
**Read `checks` and `passed` before you show anything.** The contact sheet never runs the render's
|
|
116
|
+
safety checks, so the preview also runs them for every placement (and, for a video, its end card)
|
|
117
|
+
and returns them as `checks: [{placementKey, index?, rule, message, severity}]` plus `passed`. An
|
|
118
|
+
`error` check (`safeZone`, `spill`, `contrast`, `font`, `image`, ...) is exactly what would fail the
|
|
119
|
+
full render after the person approved: e.g. an App Store badge intruding into the band TikTok/Reels
|
|
120
|
+
reserve on 9:16. A `warning` (a soft, upscaled photo) does not block, but mention it.
|
|
121
|
+
|
|
122
|
+
When `passed` is false, **do not show the image or ask for approval yet.** Fix the design and
|
|
123
|
+
preview again, repeating until `passed` is true:
|
|
124
|
+
|
|
125
|
+
- the copy collides with a reserved zone: shorten the headline or drop the body;
|
|
126
|
+
- the photo takes too much of a placement: lower its height there with `layoutOverrides.band`
|
|
127
|
+
(e.g. `{"band": {"9x16": 1100}}`);
|
|
128
|
+
- the bottom row (CTA/badge) does not clear the safe zone: raise the clearance with
|
|
129
|
+
`layoutOverrides.padBottom`, or pick a layout that keeps it clear;
|
|
130
|
+
- the text is too large or too small for a placement: set it with `typeOverrides` (below).
|
|
131
|
+
|
|
132
|
+
Tell the person what you changed and why when you do show it. Never offer **Approve & upload**
|
|
133
|
+
while a blocking check fails.
|
|
134
|
+
|
|
135
|
+
**Bigger or smaller text on one placement: use `typeOverrides`, not a different layout.** It pins
|
|
136
|
+
the headline per placement as `{"<placementKey>": [fontSize, lineHeight?, letterSpacing?]}`, e.g.
|
|
137
|
+
`{"9x16": [96], "1x1": [88]}` for a bigger headline on 9:16 and 1:1. `fontSize` is in artboard px
|
|
138
|
+
(a 9:16 board is 1080 wide), `lineHeight` a multiplier (e.g. `1.0`) and `letterSpacing` in px
|
|
139
|
+
(usually negative); leave the last two out to take the engine's defaults for that size. A placement
|
|
140
|
+
you do not name stays auto-fitted. A pinned size is not shrunk to fit, so re-check `checks` after
|
|
141
|
+
setting one. It works on `preview_creative_draft` and `create_creative`/`update_creative`; changing
|
|
142
|
+
the headline or layout of a saved Creative clears pinned sizes unless you send them again.
|
|
96
143
|
|
|
97
144
|
### 7. Look at the result honestly
|
|
98
145
|
|
|
99
|
-
**
|
|
100
|
-
|
|
146
|
+
**Passing checks prove the frame is structurally correct, not that the ad is good.** The checks
|
|
147
|
+
(`passed: true`) catch text spilling out of a safe zone, a font that failed to load, a photo that
|
|
101
148
|
never decoded, or contrast below the accessible minimum — not whether the headline reads at a
|
|
102
149
|
glance, whether the accent phrase falls on the words that carry the meaning, or whether the copy is
|
|
103
|
-
sitting on the subject's face. Judge those yourself from the image `
|
|
104
|
-
the copy collides with the photo, switch layout (see step 5) and
|
|
105
|
-
text until it technically fits.
|
|
150
|
+
sitting on the subject's face. Judge those yourself from the image `preview_creative_draft` hands
|
|
151
|
+
back. If the copy collides with the photo, switch layout (see step 5) and preview again rather than
|
|
152
|
+
shrinking text until it technically fits.
|
|
153
|
+
|
|
154
|
+
Only once `passed` is true, show the image to the person and ask with `AskUserQuestion` —
|
|
155
|
+
**Approve & upload**, **Request changes**, **Stop**. If `passed` is false you are not at this step
|
|
156
|
+
yet: fix it and re-preview first (step 6). On changes, preview again with the edits (another draft; nothing was saved). On
|
|
157
|
+
Stop, leave it: the draft stays on this machine under `.conductor/drafts/` and is cleaned up
|
|
158
|
+
automatically once it is committed or two weeks old. Only on **Approve & upload**, call
|
|
159
|
+
`commit_creative_draft({draftDir})` with the `draftDir` the approved preview returned.
|
|
106
160
|
|
|
107
161
|
### 8. Check readiness before calling it done
|
|
108
162
|
|
|
109
|
-
|
|
163
|
+
The preview already lists the readiness gaps, so fix what you can (caption, alt text, a checked
|
|
164
|
+
photo) before asking for approval. After the commit, `get_creative` includes a `readiness` block: caption, alt text, the photo's checked/blocked state
|
|
110
165
|
and provenance, and the Creative's own state (not `DRAFT`) are blocking; an AI-generation disclosure
|
|
111
166
|
is informational only. Do not call a Creative done while `ready` is false.
|
|
112
167
|
|
|
113
|
-
### 9.
|
|
168
|
+
### 9. Commit, then hand off
|
|
114
169
|
|
|
115
|
-
|
|
116
|
-
full-size
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
170
|
+
`commit_creative_draft` uploads the draft's local files, creates the Creative (or updates
|
|
171
|
+
`baseCreativeId`'s), and runs the full-size render in one call, then returns the Creative id and
|
|
172
|
+
display id, the frames and the readiness. It can be run once per draft; if its render fails the
|
|
173
|
+
Creative is already saved — call `render_creative`, don't commit again. Verify with `get_creative`.
|
|
174
|
+
Then `attach_creative_to_post` with the Post's Work Item id — it maps each placement to the right
|
|
175
|
+
destinations (9:16 to TikTok/Reels, 4:5 to Instagram feed, 1:1 to Facebook, story to story targets)
|
|
176
|
+
for every destination that has not set its own custom media, and never touches one that has. Verify
|
|
177
|
+
with `get_post_status`. From here, use the `conductor-publisher` skill for scheduling, review and
|
|
178
|
+
publishing — this skill's job ends at attaching the artwork.
|
|
121
179
|
|
|
122
180
|
## Making a sequence instead of a single ad
|
|
123
181
|
|
|
124
182
|
For a platform surface that's tapped or swiped through, set `sequenceKind` to `story` (2 to 7
|
|
125
|
-
beats, aim for 3 to 5) or `carousel` (2 to 10 cards, 5 to 7 performs best) on
|
|
126
|
-
`
|
|
127
|
-
own `headline`, and optionally `body`, `
|
|
183
|
+
beats, aim for 3 to 5) or `carousel` (2 to 10 cards, 5 to 7 performs best) on
|
|
184
|
+
`preview_creative_draft` (then `commit_creative_draft`), with a `sequence` array. Each beat inherits
|
|
185
|
+
the Creative and overrides only its own `headline`, and optionally `body`, `photoPath` (a local
|
|
186
|
+
photo, uploaded on commit) or `photoId`, or `cta`. Structure a story hook, mechanism,
|
|
128
187
|
payoff — the first beat has to earn the next tap on its own, since that's where the drop-off is
|
|
129
188
|
worst. A logo lockup and CTA claim land on the last beat only by default; `cta: true` on an earlier
|
|
130
189
|
beat overrides that. A carousel renders every card at one shared aspect ratio (`carouselRatio`),
|
|
@@ -136,6 +195,9 @@ Not every video needs the brand layout above — a finished clip (a demo recordi
|
|
|
136
195
|
something a person or another tool already produced) can go up as-is. That's a CLIP Creative: no
|
|
137
196
|
photo, headline or layout, just the video.
|
|
138
197
|
|
|
198
|
+
A CLIP has no artwork to render, so it is the one flow without a draft preview: open the video
|
|
199
|
+
itself and check it with the person before uploading.
|
|
200
|
+
|
|
139
201
|
1. Check `list_creative_media({kind: "VIDEO"})` for a clip already in the project before uploading a
|
|
140
202
|
new one. A new one goes through `upload_creative_media` (local path or URL) — same provenance
|
|
141
203
|
fields as a photo (`source`, `licence`, `aiGenerated`). It detects video vs. audio vs. photo
|
|
@@ -162,10 +224,11 @@ of a static frame — no separate video tool needed. Use it when a still doesn't
|
|
|
162
224
|
destination rewards video over an image.
|
|
163
225
|
|
|
164
226
|
1. Pick a photo (step 5) or, for a moving background, a video — check `list_creative_media({kind:
|
|
165
|
-
"VIDEO"})` first
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
2. `
|
|
227
|
+
"VIDEO"})` first and use its media id if something fits; otherwise pass the local file as
|
|
228
|
+
`clipPath` (previewed from disk, uploaded on commit). For background music,
|
|
229
|
+
`list_creative_media({kind: "AUDIO"})` the same way, else `audioPath`.
|
|
230
|
+
2. `preview_creative_draft` with `kind: "MOTION"` (to change a saved MOTION Creative, `baseCreativeId`
|
|
231
|
+
instead) plus:
|
|
169
232
|
- `motion.preset` — how the copy enters: `fade-up` (default), `word-by-word`, `accent-pop`, or
|
|
170
233
|
`none` (everything visible from the first frame).
|
|
171
234
|
- `motion.durationSec` — 3 to 60, default 8.
|
|
@@ -177,13 +240,13 @@ destination rewards video over an image.
|
|
|
177
240
|
- `audio` — `source: "clip"` (the background clip's own recorded sound — the default when there
|
|
178
241
|
is one), `"track"` (a separate library track: `trackId` from `upload_creative_media`, plus
|
|
179
242
|
`volume` and `fadeOutSec`), or `"none"`.
|
|
180
|
-
3.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
243
|
+
3. The preview renders three key moments across the timeline into one sheet image (not the finished
|
|
244
|
+
video). Judge it the same honest way as step 7 above — does the copy's entrance finish before the
|
|
245
|
+
next beat, does the end card actually hold long enough to read — and get approval before
|
|
246
|
+
spending a full render on it.
|
|
247
|
+
4. `commit_creative_draft` then uploads the media, saves the Creative and runs the local,
|
|
248
|
+
ffmpeg-driven encode — roughly 20 seconds per placement for an 8-second video. No ffmpeg on this
|
|
249
|
+
machine fails with a clear message naming how to get one rather than silently producing nothing.
|
|
187
250
|
5. `attach_creative_to_post`, same as any Creative — one MP4 per placement.
|
|
188
251
|
|
|
189
252
|
A library music track's `licence` has to be recorded on upload (same as a photo's provenance) — the
|
|
@@ -203,6 +266,9 @@ Automated checks are real but incomplete. These are the ones worth remembering t
|
|
|
203
266
|
- **A Brand Kit with no copy rules, or no accent-phrase requirement, never fails a save on that
|
|
204
267
|
account.** That is permissive by design, not a bug — but it means the model, not the platform, is
|
|
205
268
|
the only check on brand voice for that workspace until someone configures rules.
|
|
269
|
+
- **A draft is not a saved Creative.** `preview_creative_draft` writes nothing to Conductor, so a
|
|
270
|
+
Creative or media id does not exist until `commit_creative_draft` runs — don't hand a draft's
|
|
271
|
+
folder to `attach_creative_to_post` or a Post.
|
|
206
272
|
- **A stale write is refused, not silently overwritten.** `update_creative` requires the Creative's
|
|
207
273
|
current `version`; a mismatch comes back as a 409 naming the conflict rather than clobbering a
|
|
208
274
|
concurrent edit — reread with `get_creative` and reapply, don't retry blind.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Brand Kit check and setup
|
|
2
|
+
|
|
3
|
+
Shared by `/conductor:creative` and the `conductor-creative` skill: check whether this workspace's
|
|
4
|
+
Brand Kit has been set up, and offer to set it up if not. The text below is the command's Step 1,
|
|
5
|
+
unchanged.
|
|
6
|
+
|
|
7
|
+
## Step 1 — Check the brand
|
|
8
|
+
|
|
9
|
+
Call `get_brand_kit` (pass `kitId` only if the person already named a specific brand).
|
|
10
|
+
|
|
11
|
+
**If `configured` is `false`**, this workspace has no brand set up yet — offer to fix that before
|
|
12
|
+
making anything:
|
|
13
|
+
|
|
14
|
+
```json
|
|
15
|
+
{
|
|
16
|
+
"questions": [{
|
|
17
|
+
"question": "This workspace's Brand Kit hasn't been set up yet (default colours, no font, no logo). Set it up now?",
|
|
18
|
+
"header": "Brand Kit",
|
|
19
|
+
"options": [
|
|
20
|
+
{"label": "From a website", "description": "Fetch a URL and infer colours, voice and name"},
|
|
21
|
+
{"label": "From a brand doc", "description": "Read a local file (guidelines, style doc)"},
|
|
22
|
+
{"label": "Answer a few questions", "description": "Name, colours, font, CTA line, approved lines"},
|
|
23
|
+
{"label": "Skip for now", "description": "Proceed with the default kit as-is"}
|
|
24
|
+
],
|
|
25
|
+
"multiSelect": false
|
|
26
|
+
}]
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- **From a website**: ask for the URL, `WebFetch` it, infer a plausible accent colour, name, CTA
|
|
31
|
+
claim and a couple of approved lines from the copy on the page. Show what you inferred and confirm
|
|
32
|
+
before writing.
|
|
33
|
+
- **From a brand doc**: ask for the file path, `Read` it, extract the same fields, confirm before
|
|
34
|
+
writing.
|
|
35
|
+
- **Answer a few questions**: ask for name, primary/accent colour (hex or a description you convert),
|
|
36
|
+
font (if any), the CTA claim, and any lines that must run verbatim. Keep it to one AskUserQuestion
|
|
37
|
+
batch, not a long interview.
|
|
38
|
+
- Once confirmed, call `update_brand_kit` with whatever was gathered. If a logo file or URL is
|
|
39
|
+
available, `upload_brand_image` for the `mark` slot at least. Verify with `get_brand_kit` —
|
|
40
|
+
`configured` should now read `true`.
|
|
41
|
+
- **Skip for now**: proceed; the skill below will render against the default kit as given.
|