skilldrop-cli 0.13.4 → 0.13.6
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 +32 -555
- package/catalogue.json +19 -1
- package/contracts/catalogue.schema.json +1 -0
- package/model-routing.json +8 -0
- package/package.json +1 -1
- package/packs/design/pack.json +22 -0
- package/packs/design/skills/brand-kit/SKILL.md +105 -0
- package/packs/design/skills/brand-kit/evals/eval_queries.json +30 -0
- package/packs/design/skills/brand-kit/evals/evals.json +18 -0
- package/packs/design/skills/brand-kit/examples/northwind-brand.md +48 -0
- package/packs/design/skills/brand-kit/manifest.json +44 -0
- package/packs/design/skills/brand-kit/scripts/check_brand.py +114 -0
- package/packs/design/skills/brand-kit/templates/brand.json +54 -0
- package/packs/{stakeholder-comms → design}/skills/deck-builder/SKILL.md +27 -9
- package/packs/{stakeholder-comms → design}/skills/deck-builder/manifest.json +2 -2
- package/packs/{stakeholder-comms → design}/skills/deck-builder/scripts/build_deck.py +105 -3
- package/packs/{stakeholder-comms → design}/skills/deck-builder/templates/deck-spec.json +4 -1
- package/packs/design/skills/marketing-flyer/SKILL.md +113 -0
- package/packs/design/skills/marketing-flyer/evals/eval_queries.json +30 -0
- package/packs/design/skills/marketing-flyer/evals/evals.json +18 -0
- package/packs/design/skills/marketing-flyer/examples/community-health-fair.md +68 -0
- package/packs/design/skills/marketing-flyer/manifest.json +39 -0
- package/packs/design/skills/marketing-flyer/reference.md +54 -0
- package/packs/design/skills/marketing-flyer/scripts/build_flyer.py +323 -0
- package/packs/design/skills/marketing-flyer/templates/flyer-spec.json +26 -0
- package/packs/dev-team/skills/contribution-wizard/SKILL.md +6 -6
- package/packs/dev-team/skills/contribution-wizard/manifest.json +2 -2
- package/packs/stakeholder-comms/pack.json +1 -1
- package/profiles.json +1 -1
- /package/packs/{stakeholder-comms → design}/skills/deck-builder/evals/eval_queries.json +0 -0
- /package/packs/{stakeholder-comms → design}/skills/deck-builder/evals/evals.json +0 -0
- /package/packs/{stakeholder-comms → design}/skills/deck-builder/examples/exec-board-update.md +0 -0
- /package/packs/{stakeholder-comms → design}/skills/deck-builder/examples/templated-brand-deck.md +0 -0
- /package/packs/{stakeholder-comms → design}/skills/deck-builder/reference.md +0 -0
- /package/packs/{stakeholder-comms → design}/skills/deck-builder/requirements.txt +0 -0
- /package/packs/{stakeholder-comms → design}/skills/deck-builder/templates/palettes.json +0 -0
- /package/packs/{stakeholder-comms → design}/skills/slide-outliner/SKILL.md +0 -0
- /package/packs/{stakeholder-comms → design}/skills/slide-outliner/evals/eval_queries.json +0 -0
- /package/packs/{stakeholder-comms → design}/skills/slide-outliner/evals/evals.json +0 -0
- /package/packs/{stakeholder-comms → design}/skills/slide-outliner/manifest.json +0 -0
- /package/packs/{stakeholder-comms → design}/skills/slide-outliner/templates/deck-outline.md +0 -0
package/catalogue.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "RFC-0034. The catalogue index: pack display order and the outcome browse axis (RFC-0026). Pack metadata lives in packs/<pack>/pack.json; membership is the folders — a skill is in a pack because it sits at packs/<pack>/skills/<name>/.",
|
|
3
3
|
"version": "1.0.0",
|
|
4
|
-
"packs": ["core", "solution-architect", "product-manager", "dev-team", "sre-oncall", "stakeholder-comms", "ai-engineering", "claude-api"],
|
|
4
|
+
"packs": ["core", "solution-architect", "product-manager", "dev-team", "sre-oncall", "stakeholder-comms", "design", "ai-engineering", "claude-api"],
|
|
5
5
|
"outcomes": {
|
|
6
6
|
"decide-what-to-build": {
|
|
7
|
+
"for": "Product managers · founders · strategists",
|
|
7
8
|
"description": "Turn a direction into a committed, measurable plan: press releases, strategy frameworks, OKRs, business cases, requirements, PRDs, and the metrics that will say whether it worked.",
|
|
8
9
|
"skills": [
|
|
9
10
|
"prfaq",
|
|
@@ -20,6 +21,7 @@
|
|
|
20
21
|
]
|
|
21
22
|
},
|
|
22
23
|
"design-the-system": {
|
|
24
|
+
"for": "Architects · tech leads",
|
|
23
25
|
"description": "Commit the shape before the code: decision records, design docs, diagrams, contracts, schemas, migration and threat models, and the design of any agentic parts.",
|
|
24
26
|
"skills": [
|
|
25
27
|
"adr-generator",
|
|
@@ -40,6 +42,7 @@
|
|
|
40
42
|
]
|
|
41
43
|
},
|
|
42
44
|
"build-and-review-software": {
|
|
45
|
+
"for": "Software teams · coding agents",
|
|
43
46
|
"description": "The loop around the code itself: supervised implementation, adversarial review panels, test plans, triage, accessibility and static-analysis passes, and the policy files agents read.",
|
|
44
47
|
"skills": [
|
|
45
48
|
"feature-implement-loop",
|
|
@@ -58,6 +61,7 @@
|
|
|
58
61
|
]
|
|
59
62
|
},
|
|
60
63
|
"build-on-the-claude-api": {
|
|
64
|
+
"for": "Teams building on the Anthropic API",
|
|
61
65
|
"description": "Skills for teams building directly on the Anthropic API — prompt caching strategy, token budgeting, eval generation, and tool-use schema writing.",
|
|
62
66
|
"skills": [
|
|
63
67
|
"prompt-caching-advisor",
|
|
@@ -67,6 +71,7 @@
|
|
|
67
71
|
]
|
|
68
72
|
},
|
|
69
73
|
"run-and-recover-the-service": {
|
|
74
|
+
"for": "SRE · on-call · platform teams",
|
|
70
75
|
"description": "Operate what shipped: runbooks, observability design, release notes, incident communication, and the postmortem that closes the loop.",
|
|
71
76
|
"skills": [
|
|
72
77
|
"runbook-generator",
|
|
@@ -78,6 +83,7 @@
|
|
|
78
83
|
]
|
|
79
84
|
},
|
|
80
85
|
"explain-it-to-decision-makers": {
|
|
86
|
+
"for": "Anyone presenting to leadership",
|
|
81
87
|
"description": "Get a non-technical audience to a decision: audience profiling, deck outlines and real decks, exec summaries, decision logs, and how-to guides.",
|
|
82
88
|
"skills": [
|
|
83
89
|
"audience-profile",
|
|
@@ -89,6 +95,7 @@
|
|
|
89
95
|
]
|
|
90
96
|
},
|
|
91
97
|
"govern-ai-use": {
|
|
98
|
+
"for": "AI enablement · engineering leaders · risk owners",
|
|
92
99
|
"description": "Adopt AI deliberately and prove what it is doing: readiness assessment, use-case triage, rollout plans, acceptable-use policy, adoption staging, and usage reporting.",
|
|
93
100
|
"skills": [
|
|
94
101
|
"ai-readiness-assessment",
|
|
@@ -99,7 +106,18 @@
|
|
|
99
106
|
"ai-usage-report"
|
|
100
107
|
]
|
|
101
108
|
},
|
|
109
|
+
"create-on-brand-collateral": {
|
|
110
|
+
"for": "Marketers · designers · anyone making a deck or flyer",
|
|
111
|
+
"description": "Make collateral that looks like it came from your team: capture the brand once, then build decks and print-ready flyers with your logo, colours and fonts.",
|
|
112
|
+
"skills": [
|
|
113
|
+
"brand-kit",
|
|
114
|
+
"deck-builder",
|
|
115
|
+
"marketing-flyer",
|
|
116
|
+
"slide-outliner"
|
|
117
|
+
]
|
|
118
|
+
},
|
|
102
119
|
"get-a-draft-ready-to-ship": {
|
|
120
|
+
"for": "Anyone shipping a document",
|
|
103
121
|
"description": "The passes either side of a generator: collect a messy input into a structured brief, critique the argument, and strip the machine artifacts before it leaves the building.",
|
|
104
122
|
"skills": [
|
|
105
123
|
"brief-intake",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"additionalProperties": false,
|
|
24
24
|
"required": ["description", "skills"],
|
|
25
25
|
"properties": {
|
|
26
|
+
"for": { "type": "string", "description": "Who this job belongs to, shown on the home page's outcome cards." },
|
|
26
27
|
"description": { "type": "string", "description": "The chip's tooltip on the catalogue site." },
|
|
27
28
|
"skills": { "type": "array", "items": { "type": "string" }, "minItems": 1 }
|
|
28
29
|
}
|
package/model-routing.json
CHANGED
|
@@ -150,6 +150,14 @@
|
|
|
150
150
|
"tier": "standard",
|
|
151
151
|
"rationale": "Checks evidence against a fixed checklist. Bounded synthesis, not adversarial review."
|
|
152
152
|
},
|
|
153
|
+
"brand-kit": {
|
|
154
|
+
"tier": "standard",
|
|
155
|
+
"rationale": "Extracts and normalises brand facts from files and pages, then runs a checker. Bounded synthesis."
|
|
156
|
+
},
|
|
157
|
+
"marketing-flyer": {
|
|
158
|
+
"tier": "standard",
|
|
159
|
+
"rationale": "Writes short copy to fit and fills a spec for a deterministic builder. Bounded generation."
|
|
160
|
+
},
|
|
153
161
|
"runbook-generator": {
|
|
154
162
|
"tier": "standard",
|
|
155
163
|
"rationale": "Operational doc from service facts. Bounded synthesis."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skilldrop-cli",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.6",
|
|
4
4
|
"description": "Portable AI-agent skills for the deliverables knowledge workers ship \u2014 diagrams, ADRs, PRDs, runbooks, decks, reviews \u2014 installable into Claude Code, Cursor, Kiro, and any IDE that takes custom instructions. One command per skill or per role-based pack.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Sanjay Ananthanarayan",
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "On-brand visual work: capture a brand once, then build real PowerPoint decks and print-ready marketing flyers in that brand, with logo, colours and fonts applied.",
|
|
3
|
+
"display_name": "Design",
|
|
4
|
+
"keywords": ["brand", "deck", "pptx", "flyer", "marketing", "logo"],
|
|
5
|
+
"links": {
|
|
6
|
+
"documentation": "https://sananthanarayan.github.io/skilldrop/packs/design/"
|
|
7
|
+
},
|
|
8
|
+
"maintainers": [
|
|
9
|
+
{
|
|
10
|
+
"name": "sananthanarayan"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"first-value": {
|
|
14
|
+
"audience-posture": "non-technical",
|
|
15
|
+
"prerequisites": ["Your logo as a PNG file, and your brand colours if you know them"],
|
|
16
|
+
"starter-task": "Capture your brand once, then make a flyer in it",
|
|
17
|
+
"starter-prompt": "Set up our brand kit from this logo (./brand/logo.png) and these colours: #0E5A46, #F2A541. Then make a Letter-size flyer for our community health fair on Saturday 14 November, 10am–2pm, at Riverside Park.",
|
|
18
|
+
"verification": "brand-kit writes brand.json and a one-page BRAND.md with contrast checked, and marketing-flyer writes flyer.html in your colours with your logo, one call to action, and only the facts you gave it.",
|
|
19
|
+
"recovery": "If the flyer comes out in default colours, check that marketing-flyer was given the brand.json path. If brand-kit does not activate, ask for it by name (\"use brand-kit\")."
|
|
20
|
+
},
|
|
21
|
+
"requires": ["core"]
|
|
22
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: brand-kit
|
|
3
|
+
description: Capture a brand once as a reusable brand.json — logo files (and an on-dark version), colour roles as hex codes, heading and body fonts, voice, imagery rules, a PowerPoint template, contact details and the legal footer — plus a one-page BRAND.md, with text contrast checked and every value traced to where it came from. Use when the user wants decks, flyers or documents in their branding, says "use our brand", "here's our logo and colours", "set up our brand kit", or hands over brand guidelines.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# brand-kit
|
|
7
|
+
|
|
8
|
+
You turn whatever the user has about their brand (logo files, a guidelines PDF, a website, a
|
|
9
|
+
few hex codes, "we use green") into one `brand.json` that other skills build from:
|
|
10
|
+
[`deck-builder`](../deck-builder/SKILL.md) for PowerPoint decks and
|
|
11
|
+
[`marketing-flyer`](../marketing-flyer/SKILL.md) for flyers. Capturing the brand once means
|
|
12
|
+
nobody answers the same questions for every deck, and the deck and the flyer match.
|
|
13
|
+
|
|
14
|
+
## How to respond
|
|
15
|
+
|
|
16
|
+
1. **Ask for everything in one message, and take what they already gave.** Never ask for
|
|
17
|
+
something the user already supplied.
|
|
18
|
+
|
|
19
|
+
> To set up your brand kit, send whatever you have. Anything you skip I'll either take from
|
|
20
|
+
> what you sent or leave for you to confirm:
|
|
21
|
+
> 1. **Brand name**, and a tagline if you use one
|
|
22
|
+
> 2. **Logo files.** The main logo, plus a white or light version for dark backgrounds if
|
|
23
|
+
> you have one. PNG is best; SVG works for flyers, but PowerPoint needs PNG.
|
|
24
|
+
> 3. **Colours** as hex codes (`#0E5A46`), or a guidelines PDF or website I can read them from
|
|
25
|
+
> 4. **Fonts** for headings and body text
|
|
26
|
+
> 5. **Voice:** how you sound, and words you always or never use
|
|
27
|
+
> 6. **Imagery:** the kind of photos or illustrations you use, and what to avoid
|
|
28
|
+
> 7. **A PowerPoint template** (`.potx` or `.pptx`) if your company has one
|
|
29
|
+
> 8. **Contact and legal:** website, email, phone, and any footer line such as "© 2026 …"
|
|
30
|
+
|
|
31
|
+
2. **Read the sources the user points to.** From a guidelines PDF or website, extract colour
|
|
32
|
+
hex codes, font names, logo usage rules and voice. Tag every value with where it came from
|
|
33
|
+
in `sources`: `[explicit]` when the source states it, `[inferred]` when you derived it (a
|
|
34
|
+
colour sampled from the logo, a tone read from the About page). Never present an inferred
|
|
35
|
+
value as the brand's own rule.
|
|
36
|
+
|
|
37
|
+
3. **Fill the five colour roles.** The roles are `primary` (main brand colour; slide title
|
|
38
|
+
bars, flyer bands), `secondary` (supporting), `accent` (highlights, calls to action),
|
|
39
|
+
`background` and `text`. If the user gave one or two colours, derive the rest and say which
|
|
40
|
+
roles you filled. Usually `background` is white or a near-white tint of the primary,
|
|
41
|
+
`text` is a near-black, and `secondary` is a lighter or darker step of the primary.
|
|
42
|
+
|
|
43
|
+
4. **Write the files** next to the logo files, so relative paths resolve:
|
|
44
|
+
- `brand.json`, in the shape of [`templates/brand.json`](templates/brand.json). Leave a
|
|
45
|
+
field out instead of guessing it.
|
|
46
|
+
- `BRAND.md`: one page with the name, colour swatches as a table (role, hex, use), fonts,
|
|
47
|
+
voice, imagery rules and logo rules, readable by a person who never opens the JSON.
|
|
48
|
+
|
|
49
|
+
5. **Check it.**
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Claude Code
|
|
53
|
+
python3 "${CLAUDE_SKILL_DIR}/scripts/check_brand.py" ./brand/brand.json
|
|
54
|
+
# Other IDEs (from the skill folder)
|
|
55
|
+
python3 scripts/check_brand.py ./brand/brand.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
It fails on a malformed hex code, text-on-background contrast below 4.5:1, a missing logo
|
|
59
|
+
file or template. It warns when white text on the primary is hard to read, when the accent
|
|
60
|
+
is too light for text, when a logo is SVG or EPS, or when fonts aren't set. Fix the
|
|
61
|
+
failures. Report each warning with what it means for the deck or flyer.
|
|
62
|
+
|
|
63
|
+
6. **Hand off.** Tell the user the path to `brand.json` and that `deck-builder` and
|
|
64
|
+
`marketing-flyer` will use it: `build_deck.py --brand brand.json`, or the flyer spec's
|
|
65
|
+
`brand` field.
|
|
66
|
+
|
|
67
|
+
**Non-interactive runs** (subagent, CI, headless): never invent a brand colour, font or logo.
|
|
68
|
+
Use only what the input supplies, tag derived colours `[inferred]`, and list missing fields
|
|
69
|
+
at the top of the response. With no brand material at all, emit `BLOCKED: need at least a
|
|
70
|
+
brand name and either a logo file or one brand colour`.
|
|
71
|
+
|
|
72
|
+
## Useful references in this skill
|
|
73
|
+
|
|
74
|
+
- [`templates/brand.json`](templates/brand.json) — the full shape, with every field filled in for a sample brand
|
|
75
|
+
- [`scripts/check_brand.py`](scripts/check_brand.py) — the checker: hex format, contrast, logo files and formats, template
|
|
76
|
+
- [`examples/northwind-brand.md`](examples/northwind-brand.md) — worked example: a logo, two hex codes and a website become a brand kit
|
|
77
|
+
|
|
78
|
+
## Quality bar
|
|
79
|
+
|
|
80
|
+
- **Every colour is a hex code with a role.** "Our green" is not a brand colour; `#0E5A46` used for title bars is.
|
|
81
|
+
- **Text on background passes 4.5:1,** checked by the script rather than by eye.
|
|
82
|
+
- **Every value says where it came from,** and inferred values are marked as inferred.
|
|
83
|
+
- **Logo files are real paths that exist,** with a PNG for PowerPoint and an on-dark version when the brand has one.
|
|
84
|
+
- **Nothing is invented.** A field the user didn't supply and couldn't be read from their sources is left out and listed, not guessed.
|
|
85
|
+
- **BRAND.md fits on one page.** A person checks the brand from it without opening the JSON.
|
|
86
|
+
|
|
87
|
+
## When to use this skill
|
|
88
|
+
|
|
89
|
+
- ✅ Before the first branded deck or flyer, so both come out in the same brand
|
|
90
|
+
- ✅ The user hands over brand guidelines, a logo, or a website and asks for things "in our style"
|
|
91
|
+
- ✅ A brand has changed and the old `brand.json` needs updating
|
|
92
|
+
|
|
93
|
+
## When NOT to use this skill
|
|
94
|
+
|
|
95
|
+
- ❌ Designing a new brand from scratch: this captures a brand, it doesn't invent one
|
|
96
|
+
- ❌ Building the deck or flyer itself; use `deck-builder` or `marketing-flyer`
|
|
97
|
+
- ❌ A one-off document where the user only wants the default look
|
|
98
|
+
|
|
99
|
+
## Anti-patterns to avoid
|
|
100
|
+
|
|
101
|
+
- ❌ **Asking eight separate questions** instead of one message with everything.
|
|
102
|
+
- ❌ **Sampling a colour from a JPEG** and recording it as the official hex without the `[inferred]` tag.
|
|
103
|
+
- ❌ **Accepting an SVG logo for PowerPoint use** without saying a PNG is needed.
|
|
104
|
+
- ❌ **Picking a pale accent as the text colour** because it is "on brand". It fails contrast and nobody can read it.
|
|
105
|
+
- ❌ **Filling the voice section with adjectives** ("innovative, passionate"). Words to use and words to avoid are what a writer can act on.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Here's our logo and brand colours \u2014 set up a brand kit we can reuse",
|
|
4
|
+
"should_trigger": true
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"query": "Use our brand guidelines PDF so everything matches our style",
|
|
8
|
+
"should_trigger": true
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"query": "Capture our fonts, colours and logo rules in one place",
|
|
12
|
+
"should_trigger": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"query": "Make our decks and flyers on-brand \u2014 start with the brand settings",
|
|
16
|
+
"should_trigger": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"query": "Build a PowerPoint deck for the board meeting",
|
|
20
|
+
"should_trigger": false
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"query": "Make a flyer for our open day",
|
|
24
|
+
"should_trigger": false
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"query": "Design a new logo for my startup",
|
|
28
|
+
"should_trigger": false
|
|
29
|
+
}
|
|
30
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "brand-kit",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "Set up our brand kit. Logo is ./brand/northwind-logo.png and there's a white version ./brand/northwind-logo-white.png. Our colours are #0E5A46 and #F2A541. We use Georgia for headings. Website is northwindhealth.example.",
|
|
7
|
+
"assertions": [
|
|
8
|
+
"Writes brand.json next to the logo files, with logo.primary and logo.on_dark set to the two supplied paths",
|
|
9
|
+
"colors.primary is #0E5A46 and colors.accent is #F2A541; secondary, background and text are filled and listed as derived",
|
|
10
|
+
"fonts.heading.family is Georgia; the body font is either asked for or marked as a default, not presented as the brand's",
|
|
11
|
+
"Every value has a source, with derived values tagged [inferred]",
|
|
12
|
+
"Runs check_brand.py and reports its contrast figures",
|
|
13
|
+
"Writes a one-page BRAND.md with a colour table (role, hex, use)",
|
|
14
|
+
"Invents no tagline, phone number, address or legal footer"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Worked example — a logo, two colours and a website become a brand kit
|
|
2
|
+
|
|
3
|
+
## Input given to the skill
|
|
4
|
+
|
|
5
|
+
> Set up our brand kit. Logo is `./brand/northwind-logo.png` and there's a white version
|
|
6
|
+
> `./brand/northwind-logo-white.png`. Our colours are `#0E5A46` and `#F2A541`. We use Georgia
|
|
7
|
+
> for headings. Website is northwindhealth.example.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
**Wrote** `./brand/brand.json` and `./brand/BRAND.md`.
|
|
12
|
+
|
|
13
|
+
**Filled for you, please confirm:** `secondary` `#3D7F6B` (a lighter step of your green),
|
|
14
|
+
`background` `#FFFFFF` and `text` `#1F2A28` (near-black with a green cast). Voice is read
|
|
15
|
+
from your About page and tagged `[inferred]`.
|
|
16
|
+
|
|
17
|
+
**Not set (nothing supplied):** body font, tagline, phone, address, legal footer, PowerPoint
|
|
18
|
+
template. Tell me your body font; until then decks and flyers use their defaults.
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
$ python3 scripts/check_brand.py ./brand/brand.json
|
|
22
|
+
text on background 14.79:1
|
|
23
|
+
white on primary 8.17:1
|
|
24
|
+
WARN accent on background is 2.05:1 — use the accent for shapes and rules, not text
|
|
25
|
+
WARN fonts.body is not set; generators fall back to their defaults
|
|
26
|
+
OK: brand.json — 0 failure(s), 2 warning(s)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### BRAND.md
|
|
30
|
+
|
|
31
|
+
| Role | Hex | Use |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| primary | `#0E5A46` | Slide title bars, flyer header band, buttons |
|
|
34
|
+
| secondary | `#3D7F6B` | Charts, secondary panels |
|
|
35
|
+
| accent | `#F2A541` | Call to action, highlights; shapes and rules, never body text |
|
|
36
|
+
| background | `#FFFFFF` | Page and slide background |
|
|
37
|
+
| text | `#1F2A28` | Body text |
|
|
38
|
+
|
|
39
|
+
- **Fonts:** headings Georgia (from you); body font not set yet
|
|
40
|
+
- **Logo:** full colour on white; the white version on the green. Never the full-colour logo on the green.
|
|
41
|
+
- **Voice** `[inferred]`: warm and plain, short sentences. Says "members", not "patients".
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
**Why this is a good output:** it uses every fact the user gave and invents none. It fills only
|
|
46
|
+
the colour roles a deck needs, says which ones it filled, and leaves the tagline and legal
|
|
47
|
+
footer empty rather than making them up. The checker's figures show white titles on the
|
|
48
|
+
green will read, and its accent warning is carried into BRAND.md as "never body text".
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "brand-kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Capture a brand once as a reusable brand.json — logo files (and an on-dark version), colour roles as hex codes, heading and body fonts, voice, imagery rules, a PowerPoint template, contact details and the legal footer — plus a one-page BRAND.md, with text contrast checked and every value traced to where it came from. Use when the user wants decks, flyers or documents in their branding, says \"use our brand\", \"here's our logo and colours\", \"set up our brand kit\", or hands over brand guidelines.",
|
|
5
|
+
"entrypoint": "SKILL.md",
|
|
6
|
+
"deps": {
|
|
7
|
+
"npm": [],
|
|
8
|
+
"pip": []
|
|
9
|
+
},
|
|
10
|
+
"env": {
|
|
11
|
+
"required": [],
|
|
12
|
+
"optional": []
|
|
13
|
+
},
|
|
14
|
+
"related": [
|
|
15
|
+
"deck-builder",
|
|
16
|
+
"marketing-flyer"
|
|
17
|
+
],
|
|
18
|
+
"handoff": [
|
|
19
|
+
{
|
|
20
|
+
"to": "deck-builder",
|
|
21
|
+
"when": "the brand kit is written and the user wants a deck",
|
|
22
|
+
"purpose": "Build the PowerPoint deck in this brand: build_deck.py --brand brand.json.",
|
|
23
|
+
"fallback": "If deck-builder is absent, give the user the brand.json path and the five hex codes to paste into whatever deck tool they use."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"to": "marketing-flyer",
|
|
27
|
+
"when": "the brand kit is written and the user wants a flyer or one-page sheet",
|
|
28
|
+
"purpose": "Build a print-ready flyer in this brand from the flyer spec's brand field.",
|
|
29
|
+
"fallback": "If marketing-flyer is absent, give the user BRAND.md so a designer or tool can apply it by hand."
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"tags": [
|
|
33
|
+
"brand",
|
|
34
|
+
"logo",
|
|
35
|
+
"colours",
|
|
36
|
+
"fonts",
|
|
37
|
+
"design",
|
|
38
|
+
"style-guide"
|
|
39
|
+
],
|
|
40
|
+
"model": {
|
|
41
|
+
"tier": "standard",
|
|
42
|
+
"rationale": "Extracts and normalises brand facts from files and pages, then runs a checker. Bounded synthesis."
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Check a brand.json before anything is generated from it. Stdlib only.
|
|
3
|
+
|
|
4
|
+
Checks:
|
|
5
|
+
- every colour role is a 6-digit hex code
|
|
6
|
+
- text on background, and white on primary, reach WCAG AA contrast (4.5:1)
|
|
7
|
+
- each logo file exists, and is PNG or JPG (PowerPoint cannot place SVG or EPS)
|
|
8
|
+
- a referenced .pptx/.potx template exists
|
|
9
|
+
- heading and body fonts are named
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
python3 check_brand.py path/to/brand.json
|
|
13
|
+
Exit code 1 if any check fails; warnings alone exit 0.
|
|
14
|
+
"""
|
|
15
|
+
import json
|
|
16
|
+
import re
|
|
17
|
+
import sys
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
HEX = re.compile(r"^#[0-9A-Fa-f]{6}$")
|
|
21
|
+
ROLES = ("primary", "secondary", "accent", "background", "text")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def luminance(hex_str):
|
|
25
|
+
h = hex_str.lstrip("#")
|
|
26
|
+
def ch(v):
|
|
27
|
+
v = int(v, 16) / 255
|
|
28
|
+
return v / 12.92 if v <= 0.03928 else ((v + 0.055) / 1.055) ** 2.4
|
|
29
|
+
r, g, b = (ch(h[i:i + 2]) for i in (0, 2, 4))
|
|
30
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def contrast(a, b):
|
|
34
|
+
la, lb = sorted((luminance(a), luminance(b)), reverse=True)
|
|
35
|
+
return (la + 0.05) / (lb + 0.05)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def main(argv):
|
|
39
|
+
if len(argv) != 1:
|
|
40
|
+
print(__doc__)
|
|
41
|
+
return 2
|
|
42
|
+
path = Path(argv[0]).expanduser().resolve()
|
|
43
|
+
base = path.parent
|
|
44
|
+
try:
|
|
45
|
+
brand = json.loads(path.read_text())
|
|
46
|
+
except (OSError, json.JSONDecodeError) as e:
|
|
47
|
+
print(f"FAIL cannot read {path}: {e}")
|
|
48
|
+
return 1
|
|
49
|
+
fails, warns = [], []
|
|
50
|
+
|
|
51
|
+
colors = brand.get("colors") or {}
|
|
52
|
+
for role in ROLES:
|
|
53
|
+
v = colors.get(role)
|
|
54
|
+
if not v:
|
|
55
|
+
warns.append(f"colors.{role} is missing; the generators will derive it from the others")
|
|
56
|
+
elif not HEX.match(v):
|
|
57
|
+
fails.append(f"colors.{role} is {v!r}, not a 6-digit hex code like #1A2A6C")
|
|
58
|
+
for x in colors.get("extra") or []:
|
|
59
|
+
if not HEX.match(str(x.get("hex", ""))):
|
|
60
|
+
fails.append(f"colors.extra {x.get('name', '?')!r} has hex {x.get('hex')!r}")
|
|
61
|
+
if HEX.match(colors.get("text", "")) and HEX.match(colors.get("background", "")):
|
|
62
|
+
r = contrast(colors["text"], colors["background"])
|
|
63
|
+
if r < 4.5:
|
|
64
|
+
fails.append(f"text on background contrast is {r:.2f}:1, below 4.5:1")
|
|
65
|
+
print(f" text on background {r:5.2f}:1")
|
|
66
|
+
if HEX.match(colors.get("primary", "")):
|
|
67
|
+
r = contrast("#FFFFFF", colors["primary"])
|
|
68
|
+
msg = f"white text on primary is {r:.2f}:1"
|
|
69
|
+
if r < 4.5:
|
|
70
|
+
warns.append(msg + " — slide title bars and flyer bands will use dark text instead")
|
|
71
|
+
print(f" white on primary {r:5.2f}:1")
|
|
72
|
+
if HEX.match(colors.get("accent", "")) and HEX.match(colors.get("background", "")):
|
|
73
|
+
r = contrast(colors["accent"], colors["background"])
|
|
74
|
+
if r < 3:
|
|
75
|
+
warns.append(f"accent on background is {r:.2f}:1 — use the accent for shapes and rules, not text")
|
|
76
|
+
|
|
77
|
+
logo = brand.get("logo") or {}
|
|
78
|
+
if isinstance(logo, str):
|
|
79
|
+
logo = {"primary": logo}
|
|
80
|
+
if not logo.get("primary") and not logo.get("on_dark"):
|
|
81
|
+
warns.append("no logo file — decks and flyers will carry the brand name as text instead")
|
|
82
|
+
for field in ("primary", "on_dark", "mark"):
|
|
83
|
+
ref = logo.get(field)
|
|
84
|
+
if not ref:
|
|
85
|
+
continue
|
|
86
|
+
lp = (base / ref) if not Path(ref).is_absolute() else Path(ref)
|
|
87
|
+
if not lp.exists():
|
|
88
|
+
fails.append(f"logo.{field} {ref!r} does not exist (looked in {lp.parent})")
|
|
89
|
+
elif lp.suffix.lower() not in (".png", ".jpg", ".jpeg"):
|
|
90
|
+
warns.append(f"logo.{field} is {lp.suffix}: fine for the flyer, but PowerPoint needs PNG or JPG — export one")
|
|
91
|
+
|
|
92
|
+
tpl = (brand.get("templates") or {}).get("pptx")
|
|
93
|
+
if tpl:
|
|
94
|
+
tp = (base / tpl) if not Path(tpl).is_absolute() else Path(tpl)
|
|
95
|
+
if not tp.exists():
|
|
96
|
+
fails.append(f"templates.pptx {tpl!r} does not exist")
|
|
97
|
+
|
|
98
|
+
fonts = brand.get("fonts") or {}
|
|
99
|
+
for role in ("heading", "body"):
|
|
100
|
+
f = fonts.get(role)
|
|
101
|
+
fam = f.get("family") if isinstance(f, dict) else f
|
|
102
|
+
if not fam:
|
|
103
|
+
warns.append(f"fonts.{role} is not set; generators fall back to their defaults")
|
|
104
|
+
|
|
105
|
+
for w in warns:
|
|
106
|
+
print(f"WARN {w}")
|
|
107
|
+
for f in fails:
|
|
108
|
+
print(f"FAIL {f}")
|
|
109
|
+
print(f"{'FAILED' if fails else 'OK'}: {path.name} — {len(fails)} failure(s), {len(warns)} warning(s)")
|
|
110
|
+
return 1 if fails else 0
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == "__main__":
|
|
114
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "brand-kit output. deck-builder and marketing-flyer read this file; any field left out is asked for or defaulted by them. Paths are relative to this file. Every value carries where it came from in `sources`.",
|
|
3
|
+
"name": "Northwind Health",
|
|
4
|
+
"tagline": "Care that comes to you",
|
|
5
|
+
"logo": {
|
|
6
|
+
"primary": "logo.png",
|
|
7
|
+
"on_dark": "logo-white.png",
|
|
8
|
+
"mark": "mark.png",
|
|
9
|
+
"notes": "Keep clear space equal to the height of the N on every side. Never place the full-colour logo on the green."
|
|
10
|
+
},
|
|
11
|
+
"colors": {
|
|
12
|
+
"primary": "#0E5A46",
|
|
13
|
+
"secondary": "#3D7F6B",
|
|
14
|
+
"accent": "#F2A541",
|
|
15
|
+
"background": "#FFFFFF",
|
|
16
|
+
"text": "#1F2A28",
|
|
17
|
+
"extra": [
|
|
18
|
+
{ "name": "Mist", "hex": "#E8F1EE", "use": "panel backgrounds" }
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"fonts": {
|
|
22
|
+
"heading": { "family": "Georgia", "fallback": "serif", "weight": 700 },
|
|
23
|
+
"body": { "family": "Arial", "fallback": "sans-serif", "weight": 400 }
|
|
24
|
+
},
|
|
25
|
+
"voice": {
|
|
26
|
+
"tone": "Warm, plain, confident. Short sentences.",
|
|
27
|
+
"use": ["members", "care team", "near you"],
|
|
28
|
+
"avoid": ["patients", "cutting-edge", "solutions"]
|
|
29
|
+
},
|
|
30
|
+
"imagery": {
|
|
31
|
+
"style": "Real people in daylight, community settings, no stock handshakes.",
|
|
32
|
+
"do": ["natural light", "diverse ages"],
|
|
33
|
+
"dont": ["clinical close-ups", "illustrated icons as hero images"]
|
|
34
|
+
},
|
|
35
|
+
"templates": {
|
|
36
|
+
"pptx": "northwind-master.potx"
|
|
37
|
+
},
|
|
38
|
+
"contact": {
|
|
39
|
+
"website": "northwindhealth.example",
|
|
40
|
+
"email": "hello@northwindhealth.example",
|
|
41
|
+
"phone": "",
|
|
42
|
+
"address": ""
|
|
43
|
+
},
|
|
44
|
+
"legal": {
|
|
45
|
+
"footer": "© 2026 Northwind Health",
|
|
46
|
+
"disclaimer": ""
|
|
47
|
+
},
|
|
48
|
+
"sources": {
|
|
49
|
+
"colors": "brand guidelines PDF, page 4 [explicit]",
|
|
50
|
+
"fonts": "brand guidelines PDF, page 6 [explicit]",
|
|
51
|
+
"voice": "website About page [inferred]",
|
|
52
|
+
"logo": "files supplied by the user [explicit]"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -7,10 +7,11 @@ description: Generate a real PowerPoint (.pptx) file from content, audience, and
|
|
|
7
7
|
|
|
8
8
|
This skill produces an **actual editable `.pptx` file**, not an outline. It runs `python-pptx`
|
|
9
9
|
to materialise slides — either on a brand template's own masters and layouts, or on the skill's
|
|
10
|
-
built-in design
|
|
10
|
+
built-in design in the user's brand: their colours, logo and fonts.
|
|
11
11
|
|
|
12
|
-
It pairs with
|
|
13
|
-
- **[`
|
|
12
|
+
It pairs with three others:
|
|
13
|
+
- **[`brand-kit`](../brand-kit/SKILL.md)** — captures the brand once as a `brand.json` this skill reads, so nobody answers the brand questions for every deck
|
|
14
|
+
- **[`audience-profile`](../../../stakeholder-comms/skills/audience-profile/SKILL.md)** — decides how dense, how many, which sections
|
|
14
15
|
- **[`slide-outliner`](../slide-outliner/SKILL.md)** — drafts per-slide content before this skill builds it
|
|
15
16
|
|
|
16
17
|
For a single fast pipeline, run all three in order: profile → outline → build.
|
|
@@ -26,9 +27,11 @@ with the design silently defaulted.
|
|
|
26
27
|
> Before I build, confirm these — reply "go" to take the defaults:
|
|
27
28
|
> 1. **Audience** — `exec` · `board` · `technical` · `sales` · `internal` · `investor` · `partner` · `customer`
|
|
28
29
|
> 2. **Format** — live presentation / read-ahead / async share, **how many minutes**, and **16:9** (default) or **4:3**
|
|
29
|
-
> 3. **
|
|
30
|
-
> - a
|
|
31
|
-
> - a **
|
|
30
|
+
> 3. **Brand** — the deck should look like it came from your team. Send any of:
|
|
31
|
+
> - a **`brand.json`** from brand-kit, which covers everything below in one file;
|
|
32
|
+
> - a **template**: path to a corporate `.pptx` / `.potx`, and the deck inherits its masters, fonts, colours, logo and slide size;
|
|
33
|
+
> - your **logo** as a PNG (and a white version for dark slides), **brand colours** as hex values for `primary` / `secondary` / `accent` / `background` / `text`, and your **heading and body fonts**;
|
|
34
|
+
> - or, with no brand, a named **palette**: `corporate-blue` (default for B2B) · `monochrome` · `vibrant` · `dark-mode` · `editorial` · `forest` · `sunset`
|
|
32
35
|
> 4. **Anything to embed** — numbers to chart, a table, diagram PNGs
|
|
33
36
|
|
|
34
37
|
Rules for reading the answers:
|
|
@@ -44,10 +47,15 @@ Rules for reading the answers:
|
|
|
44
47
|
[`reference.md`](reference.md#color-palette-principles) — say which roles were filled in.
|
|
45
48
|
- **Template beats palette** for chrome; the palette still colours charts, tables, big numbers
|
|
46
49
|
and quote bars, so ask for brand hexes even when a template is supplied.
|
|
50
|
+
- **Brand material beats a named palette.** If the user sent a logo or brand colours, never fall
|
|
51
|
+
back to `corporate-blue`. If they have brand material but no `brand.json`, suggest running
|
|
52
|
+
`brand-kit` first, so the next deck and any flyer match this one.
|
|
53
|
+
- **Logos must be PNG or JPG.** PowerPoint can't place SVG or EPS: ask for a PNG export, and
|
|
54
|
+
never build with the logo silently missing.
|
|
47
55
|
|
|
48
56
|
**Non-interactive runs** (subagent, CI, headless): audience, format, time and palette degrade to
|
|
49
57
|
`[assumption]` lines stated at the top of the response — default `exec`, live, 16:9,
|
|
50
|
-
`corporate-blue
|
|
58
|
+
`corporate-blue`, or the `brand.json` when one is in the input. Never invent a logo or brand colour. Missing *content* does not degrade: emit `BLOCKED: need deck content
|
|
51
59
|
(outline, doc, or point list)` and build nothing.
|
|
52
60
|
|
|
53
61
|
### 2. Plan the slide list against the audience archetype
|
|
@@ -61,7 +69,7 @@ Rules for reading the answers:
|
|
|
61
69
|
| `internal` | 5–10 | Mixed | Context • Proposal • Discussion topics • Action items |
|
|
62
70
|
| `investor` | 10–15 | Polished, narrative | Problem • Market • Product • Traction • Team • Ask |
|
|
63
71
|
|
|
64
|
-
Full archetype spec: [`audience-profile`](
|
|
72
|
+
Full archetype spec: [`audience-profile`](../../../stakeholder-comms/skills/audience-profile/SKILL.md). Slide-count budgets per
|
|
65
73
|
format and time: [`reference.md`](reference.md#slide-count-budgets).
|
|
66
74
|
|
|
67
75
|
### 3. Build a deck spec
|
|
@@ -77,6 +85,7 @@ A JSON document — full schema, with every layout and the template block, in
|
|
|
77
85
|
"template": "./brand/acme-master.potx",
|
|
78
86
|
"layout_map": { "title": "Title Slide", "content": "Title and Content" },
|
|
79
87
|
"palette": { "primary": "#1A2A6C", "accent": "#FDBB2D" },
|
|
88
|
+
"brand": "./brand/brand.json",
|
|
80
89
|
"slides": [
|
|
81
90
|
{ "layout": "title", "title": "...", "subtitle": "...", "presenter": "...", "date": "..." },
|
|
82
91
|
{ "layout": "content", "title": "...", "bullets": ["..."], "notes": "..." },
|
|
@@ -129,6 +138,13 @@ python3 "${CLAUDE_SKILL_DIR}/scripts/build_deck.py" /tmp/deck-spec.json -o ./out
|
|
|
129
138
|
cd path/to/deck-builder && python3 scripts/build_deck.py /tmp/deck-spec.json -o ./out/deck.pptx
|
|
130
139
|
```
|
|
131
140
|
|
|
141
|
+
Add `--brand ./brand/brand.json` to build in the user's brand (or set `"brand"` in the spec).
|
|
142
|
+
The brand fills whatever the spec leaves out: palette colours, the template, logo files and
|
|
143
|
+
fonts. On the built-in design the logo goes on every slide: large on the cover, small in the
|
|
144
|
+
lower-left corner elsewhere, and the on-dark version on dark section and closing slides. The
|
|
145
|
+
brand's heading and body fonts are applied to titles, text, tables and charts. A template
|
|
146
|
+
already carries its own logo and fonts, so they come from the template instead.
|
|
147
|
+
|
|
132
148
|
Add `--strict` to fail the build on a missing or unreadable image instead of drawing a
|
|
133
149
|
placeholder box — use it in CI, not in a conversation.
|
|
134
150
|
|
|
@@ -157,7 +173,8 @@ A generated deck is a strong first draft. Always tell the user to:
|
|
|
157
173
|
|
|
158
174
|
## Quality bar
|
|
159
175
|
|
|
160
|
-
- **Design is chosen by the user, never silently defaulted** — a template
|
|
176
|
+
- **Design is chosen by the user, never silently defaulted** — a `brand.json`, a template, brand colours and logo, or a named palette, confirmed in the setup block.
|
|
177
|
+
- **The user's brand shows on every slide** — their logo (PNG) and colours, and their fonts when they gave them. A deck built from brand material that comes out in `corporate-blue` has failed.
|
|
161
178
|
- **A supplied template is actually inherited** — `layout_map` covers `title`, `section`, `content` and `closing` at minimum. A branded deck rendered on the blank layout has thrown away the branding.
|
|
162
179
|
- **Title slide isn't blank** — real title, subtitle, presenter, date.
|
|
163
180
|
- **Numbers are charted, not bulleted** — three or more comparable figures belong in a `chart` or a `table`, not a bullet list.
|
|
@@ -171,6 +188,7 @@ A generated deck is a strong first draft. Always tell the user to:
|
|
|
171
188
|
## Anti-patterns to avoid
|
|
172
189
|
|
|
173
190
|
- ❌ Ignoring a template the user already named, or asking for a palette after they supplied one. The template is the answer to "what should this look like".
|
|
191
|
+
- ❌ Asking for brand colours when a `brand.json` was supplied, or building without the logo because it was an SVG. Ask for a PNG export instead.
|
|
174
192
|
- ❌ Mapping every logical layout onto the template's one title-and-bullets layout. That is a branded deck that still looks generated.
|
|
175
193
|
- ❌ Generating without confirming audience. The same content for execs and engineers is a different deck.
|
|
176
194
|
- ❌ Inventing a statistic to fill a `big_number`, or a series to fill a chart. No source, no chart.
|