brand-kit — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited brand-kit (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Build a brand-kit.json describing the user's product in a way other skills can consume — colors, type, voice, do/don't language, optional logo. Other Cogny skills (/tiktok-launch-video, /reddit-launch-video, /linkedin-launch-video, /landing-page-review, /ad-copy-writer) read this file when present.
The skill is built around the reality that users don't always have access to their site's source code. There are six paths to a kit, in priority order. Stop at the first one that yields enough.
/brand-kit https://example.com — extract from a live site /brand-kit ~/git/our-site — extract from a local repo (Tailwind / CSS vars) /brand-kit ~/Desktop/logo.png — extract palette from a logo or screenshot /brand-kit ~/Desktop/homepage.png logo.png — combine site screenshot + logo /brand-kit — interview the user
Paths 1–5 work with zero accounts (Path 3 needs ffmpeg, ImageMagick or Node, or falls back to reading the image by eye; see below). Cogny adds two shortcuts:
| Job | Primary | Also answers it | No account |
|---|---|---|---|
| Documented brand (voice, palette) | Context tree (search_context), Solo and Cloud | — | Paths 1-4 |
| Logo / brand images already uploaded | cogny-image-gen tool_list_brand_assets (Cloud only) | — | User gives a file path |
| Palette from an image URL | cogny-image-gen tool_analyze_media (Cloud only) | — | extract-palette.sh (Path 3) |
| Voice samples beyond the homepage | LinkedIn_Organic tool_get_organization_posts | X_Organic tool_get_user_posts | Homepage + /about (Path 5) |
On Solo, cogny-image-gen isn't available: skip Path 0b and use Path 3.
A brand-kit.json written to (in priority order, whichever path makes sense for the user's setup):
.agents/brand-kit.json (if .agents/ exists).claude/brand-kit.json (if .claude/ exists)./brand-kit.json (otherwise — current working directory)Always print the path you wrote to so the user can move it.
{
"name": "cogny",
"site": "https://cogny.com",
"logo": {
"path": "./brand/logo-square.png",
"use_in_video": false
},
"colors": {
"background": "#1f1d1d",
"foreground": "#ede8e3",
"primary": "#b09acd",
"accent": "#c9badf",
"muted": "#9a9494",
"card": "#282525",
"fuchsia": "#d4848a",
"success": "#7ec8a4",
"amber": "#d4a857"
},
"type": {
"family": "'JetBrains Mono', 'Fira Code', ui-monospace, monospace",
"weight_display": 700,
"weight_body": 500,
"letter_spacing_display": "-0.02em"
},
"voice": {
"register": "technical, direct, anti-jargon, CLI-aesthetic",
"person": "first-person plural (we / your)",
"do": ["concrete numbers", "name the tradeoff", "name the tool"],
"dont": ["leverage", "unlock", "revolutionary", "next-gen"]
},
"source": "repo: ~/git/cogny/tailwind.config.ts + src/index.css",
"captured_at": "2026-04-25"
}Minimum viable kit: colors.background, colors.foreground, colors.primary, type.family, voice.register. Don't ship without all five.
If Cogny isn't connected, skip this and note what connecting adds (a brand voice and palette the team already documented, so nothing needs scraping).
Use when: the cogny MCP server is in .mcp.json. The user may have already documented their brand — voice, palette, tone — in their context tree, in which case scraping is redundant.
mcp__cogny__get_context_tree_overview # see what's documented
mcp__cogny__search_context query="brand" # palette, voice, tone, design tokens
mcp__cogny__search_context query="positioning" # voice + register often live here
mcp__cogny__read_context_node node_id="…"If the tree returns brand / palette / voice nodes, hydrate the kit from those values. Cross-check against Path 1 or 3 only if something looks stale or contradictory.
If the cogny MCP isn't connected, or the tree has nothing brand-related, fall through to Path 0b (Cloud) or Path 1.
If Cogny isn't connected, skip this and note what connecting adds (uploaded logos and product shots reused directly). Use when: mcp__cogny__mcp__cogny-image-gen__tool_list_brand_assets exists (Cloud workspaces). Solo workspaces don't have it; skip.
tool_list_brand_assets with asset_type: "logo" (also product, lifestyle). Each row has id, name, description and public_url.logo.path to the logo's public_url and add "brand_asset_id": "<id>" so image-generating skills can pass it as brand_asset_ids.mcp__cogny__mcp__cogny-image-gen__tool_analyze_media with media_url = the logo's public_url and prompt: "List the 3-6 distinct brand colors as hex codes, ordered by area, and say which is background". Treat the answer as a candidate palette and cross-check it with Path 2 or 3: vision models round hex values.Use when: the user has access to the site's source.
Look for, in order. Check which tools exist first: command -v ffmpeg fd convert magick node prints only the ones installed. fd is often missing; use the find lines then.
# Tailwind config — custom color namespaces
fd -t f 'tailwind\.config\.(ts|js|mjs|cjs)$' <repo>
# CSS custom properties (most modern stacks)
fd -t f '(globals|index|app|theme|variables)\.css$' <repo>
# Design tokens
fd -t f '(design-tokens|tokens|theme)\.json$' <repo>
# Same, without fd
find <repo> -path '*/node_modules' -prune -o -type f \( -name 'tailwind.config.*' -o -name 'globals.css' -o -name 'index.css' -o -name 'app.css' -o -name 'theme.css' -o -name 'variables.css' -o -name 'tokens.json' -o -name 'design-tokens.json' -o -name 'theme.json' \) -printWhen you find a Tailwind config, look for a custom color namespace (commonly the brand name, e.g. cogny: {...}):
// tailwind.config.ts (cogny example)
colors: {
cogny: { dark: "#1f1d1d", purple: "#b09acd", secondary: "#7E69AB", light: "#c9badf" }
}When you find a CSS file, grep for the canonical custom-property names:
grep -E '^\s*--(background|foreground|primary|accent|muted|card)' <css-file>Map the values into the kit schema. If you find both --primary (HSL or hex) and a Tailwind namespace, prefer the Tailwind namespace — it's usually the explicit brand intent rather than a shadcn default.
Type detection in the same files:
grep -rE --include='*.css' --include='*.ts' --include='*.tsx' --include='*.js' --exclude-dir=node_modules "(font-family|fontFamily|--font-)" <repo>/src 2>/dev/null | headUse when: the user only has a public URL.
Steps:
<head>, <style> or <link> tags. curl -sL -A "Mozilla/5.0" "<url>" -o /tmp/home.html
grep -oiE '<link[^>]+rel="?(stylesheet|icon|apple-touch-icon|manifest)[^>]*>' /tmp/home.html
grep -oiE '<meta[^>]+(theme-color|og:image)[^>]*>' /tmp/home.html
sed -n '/<style/,/<\/style>/p' /tmp/home.html | head -c 50000curl -sL <css-url> | head -c 200000 is enough — look for CSS custom properties (--primary, --background, etc.) and @font-face rules./site.webmanifest, /manifest.json, or /manifest.webmanifest. Manifests contain theme_color, background_color, and an icon list — gold for brand kits. curl -sL "<site>/favicon.ico" -o /tmp/favicon.ico
curl -sL "<site>/apple-touch-icon.png" -o /tmp/touch.png
# Or extract from <link rel="icon"> in the homepage HTMLThe largest PNG icon on the site (Apple touch icon, manifest icon, og:image) is the brand mark. Check each with curl -sI (status 200 and an image/* content type) before using it.
If only a favicon exists (a 16–48px .ico/PNG, no touch icon, manifest icon or usable SVG logo): don't build a palette from it alone. Small icons are anti-aliased and often simplified, so a palette from them gives one reliable primary at best. Take the dominant color as a candidate primary, keep logo.path empty (or set use_in_video: false), and get the rest from the CSS (step 2), an inline SVG logo in the HTML (grep -oiE '<svg[^>]*>.*?</svg>' on the header, fill colors are exact), or by asking the user for a logo file or a homepage screenshot for Path 3.
font-family declarations on body, html, or h1. If you see Google Fonts links in <head>, the family name is right there in the URL.If the site ships a static frontend (Next.js, etc.), the bundled CSS often contains the same custom properties as the dev source — so you might still recover the full brand palette without repo access.
If you only get partial info (e.g. you got theme_color from the manifest but no full palette), proceed to Path 3 to fill in gaps from the logo / a screenshot.
Use when: the user uploads a logo image, a screenshot of their homepage, or a brand asset PNG/JPG.
The user usually has some image — a logo on Desktop, a slack avatar, a website screenshot. Check what's installed first (command -v ffmpeg convert magick node) and use the first available option. With FFmpeg:
# Generate a small palette image (max 6 distinct colors) from any image.
ffmpeg -y -i <input.png> \
-vf "palettegen=max_colors=6:reserve_transparent=0:stats_mode=single" \
-frames:v 1 -update 1 /tmp/_palette.png
# Read those palette colors back as hex. The PPM header is
# P6\n<W> <H>\n255\n → 13 bytes for the standard 16x16 palette.
ffmpeg -y -i /tmp/_palette.png -c:v ppm -f image2 -update 1 - 2>/dev/null \
| tail -c +14 \
| xxd -c 3 -p \
| awk '!seen[$0]++' \
| head -8 \
| awk '{print "#" toupper($1)}'Output for the cogny logo (gradient C on dark warm bg):
#1D1B1B ← background
#5F4953 ← deep plum
#BB93B9 ← lavender (primary brand)
#CA8B9D ← coral-pink
#D2858C ← coral (accent)Mapping rules — the palette is sorted by frequency, but the role assignment is yours to make:
colors.background (if it covers >40% of pixels)colors.foreground (if there's no off-white, default #fafafa)colors.primarycolors.accentEyeball the original image once and confirm — algorithmic role assignment gets it right ~70% of the time.
For typography from a screenshot: image-based font detection is unreliable. Either:
Optional: semantic palette via node-vibrant (one npm install, gives vibrant/muted/dark-vibrant/light-vibrant categories — more useful for primary/accent role mapping):
npm install -g node-vibrant
node -e "
const Vibrant = require('node-vibrant');
Vibrant.from(process.argv[1]).getPalette().then(p => {
const out = {};
for (const k of ['Vibrant','Muted','DarkVibrant','LightVibrant','DarkMuted','LightMuted']) {
if (p[k]) out[k] = p[k].hex;
}
console.log(JSON.stringify(out, null, 2));
});
" <input.png>With ImageMagick instead (convert, or magick on v7):
convert <input.png> -resize 200x200 -colors 6 -format %c histogram:info:- | sort -rn | head -6Use whichever is available. If none of ffmpeg, ImageMagick or Node is installed, open the image with the Read tool (it displays images), name the 3–5 dominant colors by eye as approximate hex values, and mark them "estimated, please confirm" in the kit; or go to Path 4.
Use when: nothing else worked, or the user wants to override the kit explicitly.
Ask, in this order, and accept partial answers:
Three colors and a font is enough to ship.
Voice is the part of a brand kit that copy-paste tooling forgets. It changes which beats a video would write or which headlines an ad-copy skill would produce.
Source for voice: the homepage hero + 1–2 deeper pages (about, pricing, blog).
Enhanced when connected: the brand's own social posts show how it writes when it isn't selling. mcp__cogny__mcp__LinkedIn_Organic__tool_list_organizations → tool_get_organization_posts (organization, count: 20), or mcp__cogny__mcp__X_Organic__tool_get_me → tool_get_user_posts (max_results: 20). Use the top posts by engagement as voice samples. If site copy and social voice disagree, show both and ask which one the kit should follow.
Steps:
/about or /pricing typically)."voice": {
"register": "<one-line description>",
"do": ["pattern 1", "pattern 2", "pattern 3"],
"dont": ["banned word 1", "banned word 2"]
}Examples (these are real, derived from each company's homepage):
| Brand | register | do | dont |
|---|---|---|---|
| cogny | "technical, direct, anti-jargon, CLI-aesthetic" | concrete numbers, name the tradeoff, name the tool | leverage, unlock, revolutionary, next-gen |
| Linear | "operator, opinionated, fast" | second-person, terse, build-mode language | enterprise, synergy, robust |
| Mailchimp | "warm, conversational, plain-English" | first-person, friendly contractions, clear CTAs | utilize, paradigm, leverage |
If you can't infer voice from the page (e.g. very thin homepage), ask the user for one sentence describing how they want to sound. Don't make it up.
◆ Brand kit (cogny)
✓ colors bg=#1f1d1d fg=#ede8e3 primary=#b09acd accent=#c9badf
✓ type JetBrains Mono / mono
◇ voice pending — fetching /about for analysis… Saved → .agents/brand-kit.json
Used by: /tiktok-launch-video, /reddit-launch-video, /linkedin-launch-video,
/ad-copy-writer, /landing-page-review--primary: 240 5.9% 10%, that's a placeholder not a brand. Cross-check with the homepage screenshot.theme-color to match the address bar of mobile Safari, which is decorative and may not match the actual brand primary./tiktok-launch-video, /reddit-launch-video, /linkedin-launch-video — consume the kit/ad-copy-writer — uses voice rules to write platform-appropriate copy/landing-page-review — checks consistency between a landing page and the brand kit/non-commodity-content — voice extraction overlaps with this skill's interview step~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.