From productivity-skills
Govern the BRAND-VOICE.md — extract a brand's writing voice from URL, Notion page, MD file/directory, or interview into a structured executable doc; update incrementally; diff versions; validate the canonical format; show testable rules. Supports multi-voice via `voice.extends` (founder on corporate, persona on institutional, multi-host) with `_replace`/`_remove` overrides. Consumed by writing skills via `-f` (e.g. `humanize-en`). Use when defining, extracting, refreshing, validating, or inspecting a brand's writing voice — "create a brand voice doc", "extract voice from this site", "tone of voice", "writing style guide", "BRAND-VOICE.md", "founder voice", "persona voice", "multi-voice".
How this skill is triggered — by the user, by Claude, or both
Slash command
/productivity-skills:brand-voice [extract|update|diff|validate|show] [-s] [-o <path>] [-u <url>] [-n <id|url>] [-d <dir>] [-f <file>] [refs|paths]When to use
Routes via `$ARGUMENTS` first token — `extract` (sources → BRAND-VOICE.md; `--extends <parent>` scaffolds a child), `update` (refresh from new sources), `diff` (regression check; single-arg form when child has `voice.extends`), `validate` / `lint` / `check` (walks chain), `show` (testable rules; `--chain`/`--explain`/`--raw` for inheritance). Skip when the user wants to apply or check prose against an existing voice (rewrite, humanize, "does this match") — invoke `/humanize-en -f BRAND-VOICE.md` instead.
[extract|update|diff|validate|show] [-s] [-o <path>] [-u <url>] [-n <id|url>] [-d <dir>] [-f <file>] [refs|paths]This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
<!-- canonical:writing-rules:start -->
references/canonical-format.mdreferences/example-chanel.mdreferences/example-multi-voice.mdreferences/interview-questions.mdreferences/schemas.mdreferences/source-resolution.mdscripts/extract_rules.pyscripts/lint_all.pyscripts/measure_corpus.pyscripts/utils.pyscripts/voice_lint.pysteps/diff.mdsteps/extract.mdsteps/show.mdsteps/update.mdsteps/validate.mdThese rules govern every prose artifact this skill emits — READMEs, CHANGELOGs, commit messages, PR bodies, release notes, doc paragraphs, non-trivial comments. Apply them at draft time, verify before output.
NEVER commit secrets)./humanize-en if installed.Govern BRAND-VOICE.md — the canonical writing voice document for a brand. Two layers: YAML frontmatter (machine-readable normative rules consumed by writing skills) plus eleven prose sections (human-readable rationale). Same split as DESIGN.md and the same design-system skill pattern: a canonical file at the project root, CLI-style subcommands for the lifecycle.
Additional context from the user: $ARGUMENTS
Parse the first positional token of $ARGUMENTS. If it matches a verb below, load the referenced file and follow its workflow. Otherwise fall through to the default workflow at the end of this document.
| First token | Mode | Reference |
|---|---|---|
extract | Ingest sources, synthesise canonical voice doc, write to ./BRAND-VOICE.md | steps/extract.md |
update | Refresh an existing voice doc from new sources, preserve manual sections | steps/update.md |
diff | Show what changed between two versions of the voice doc (git-aware) | steps/diff.md |
validate (aliases: lint, check) | Lint a voice doc against canonical-format.md — verdict + errors + warnings + fix suggestions, CI-friendly exit codes | steps/validate.md |
show | Print the flat list of testable rules from the voice doc | steps/show.md |
| (none) | See Default workflow below | (this file) |
There is no apply subcommand. Application of the voice — rewriting prose to match it — is the consumer skill's job. The current consumer is humanize-en -f BRAND-VOICE.md, which calls scripts/extract_rules.py --full for chain-resolved flat rules. Any other skill that needs the voice contract follows the same path; alternative consumers can read the YAML frontmatter directly when their allowed-tools excludes Bash.
The voice doc lives at ./BRAND-VOICE.md by default — at the project root, versioned in git, alongside DESIGN.md, README.md, LICENSE.md. Override the path with -o <path> when the voice is multi-project.
extract refuses to overwrite an existing file. To refresh, use update. To replace, delete first.
When -s is passed alongside extract, the skill also writes a copy to ~/.claude/output/{project}/brand-voice/brand-voice-{slug}.md for pipeline-history consumers ({slug} = kebab of voice.name; {project} = kebab-cased basename of the git toplevel, else cwd) and reports its fully-expanded absolute path (no tilde, no magic). The canonical file at ./BRAND-VOICE.md remains the single source of truth.
When a brand spans multiple repositories (a www repo, an iOS repo, a docs repo, a marketing site), the same BRAND-VOICE.md should govern all of them. Pick one pattern and document it in each project's CLAUDE.md:
BRAND-VOICE.md at the brand workspace root (e.g. ~/<brand>/BRAND-VOICE.md). Each subproject references it via absolute path: /humanize-en -f ~/<brand>/BRAND-VOICE.md draft.md. Simplest. Best when subprojects share a local workspace.packages/brand/BRAND-VOICE.md consumed by every app in the monorepo. Single PR for cross-cutting voice changes.@<org>/brand-voice on npm with BRAND-VOICE.md plus the bundled scripts (extract_rules.py, voice_lint.py). Versioned, works cross-repo without a shared filesystem./brand-voice diff — a copy in each repo; periodic diff <canonical> <local> catches drift. Simplest tooling, highest drift risk. Pair with a CI check.Notion-as-source-of-truth is its own pattern: keep the spec in Notion, refresh local BRAND-VOICE.md periodically via /brand-voice update -n <page-id>. Notion stays the editorial surface; the local file is the executable artifact.
The default and recommended pattern is one BRAND-VOICE.md per brand. Within that file, contexts: handles register variation across document types (RFC vs landing page vs press release), audience segments (B2B vs consumer, technical vs lay), or channels (long-form vs social vs email):
contexts:
rfc: { density: max, numbered_sections: true }
landing: { sentence_count: 1 }
social: { shorter_form: true, formality_preserved: true }
Different contexts share the same lexicon, the same forbidden patterns, the same pronouns — what changes is the register, the sentence rhythm, the example openers.
Multiple voice files are warranted only when the brand has genuinely separate sub-brands with separate voices: a luxury group that owns Maison X Couture (institutional, French-rooted) and Maison X Beauty (more accessible, broader audience). Each sub-brand gets its own BRAND-VOICE.md. The skills consume each independently — humanize-en -f maison-x-couture.md for one, humanize-en -f maison-x-beauty.md for the other.
Inheritance via voice.extends — when sub-voices share a common substrate (founder voice on top of corporate, persona on top of institutional, multi-host media brand), declare voice.extends: ./BRAND-VOICE.md on the child file. The child inherits the parent's rules and overrides only what differs. Per-field merge policy, _replace / _remove overrides, cycle detection, and validation order live in references/canonical-format.md § Inheritance; a worked example sits in references/example-multi-voice.md.
When in doubt, start with one file. Adding contexts.foo later is cheaper than splitting two files later. Adding voice.extends later, when a real second voice emerges, is cheaper than over-engineering inheritance up front.
Sources are combinable — pass any number of -u, -n, -d, -f. The skill aggregates all sources into a working draft, then synthesises the canonical format once.
| Flag | Source | Mechanism |
|---|---|---|
-u <url> | URL | WebFetch (or your harness's web-fetch tool) direct → fallback /markitdown -s <url> if binary/error |
-n <id|url> | Notion page | Notion MCP fetch tool (page + linked sub-pages, depth 1) — no MCP: export to Markdown and use -d |
-d <dir> | Folder of MD/MDX | Glob <dir>/**/*.md → aggregate |
-f <file> | Single MD/MDX/TXT | Read direct |
(none, with extract) | Interview | 8 canonical questions via AskUserQuestion (when unavailable, ask in plain text) |
Full resolution rules — including failure handling, conflicts, MCP unavailability, large-folder fan-out, and contribution summary — live in references/source-resolution.md.
The Notion MCP is authorised through Claude Code's permission layer, not via this skill's allowed-tools. If the MCP is not installed, -n errors with a clear install pointer and the workaround (export Notion → MD, then -d).
BRAND-VOICE.md has two parts:
voice.name, forbidden_lexicon, rewrite_rules, sentence_norms. Optional: core_attributes, required_lexicon, forbidden_patterns, contexts, pronouns, voice.source_urls, voice.last_updated, voice.source.Full schema, field constraints, manual-section markers, and section-heading normalisation rules: references/canonical-format.md. A complete reference example: references/example-chanel.md.
The split is deliberate. Tooling reads YAML; humans read prose. Consumers like humanize-en -f BRAND-VOICE.md load only the rule block (50–150 lines via extract_rules.py --full), not the full doc, so the voice doc can be richly explained without bloating downstream contexts.
Brand voice is consumed by writing skills via -f. The current consumer is humanize-en:
/brand-voice extract -u https://example.com/about
→ ./BRAND-VOICE.md
/humanize-en -f ./BRAND-VOICE.md draft.md
→ draft humanized against universal AI tells + brand-specific rules
$SKILL_DIR = this skill's folder — ${CLAUDE_SKILL_DIR} in Claude Code, the directory containing this SKILL.md elsewhere.
Two ways for a consumer to read the rules:
Invoke extract_rules.py --full — preferred. The script flattens the YAML to plain text, automatically resolves any voice.extends chain, applies _replace and _remove overrides, and emits a 50–150 line block ready for inclusion in an LLM prompt. This is what humanize-en -f does as of the inheritance release.
python3 "$SKILL_DIR"/scripts/extract_rules.py --full ./BRAND-VOICE.md
Read the YAML frontmatter directly — fallback when the consumer's allowed-tools does not include Bash, or when the consumer wants raw structure. The consumer parses the YAML and uses forbidden_lexicon, rewrite_rules, sentence_norms, forbidden_patterns, pronouns, core_attributes, contexts directly. This path does not resolve voice.extends — child files appear as-written.
Both shapes are documented in references/schemas.md § extract_rules.py. The --legacy flag emits the v1 minimal output (byte-identical to the pre-inheritance shape) for any external consumer pinned to it.
When a brand voice rule conflicts with a universal AI-tell pattern (e.g., the voice requires em-dashes vs pattern #14), the brand rule wins — it is the user's contract. Conflicts are logged in the consumer's report.
voice_lint.pyEvery doc the skill writes (or the user authors) is validated by scripts/voice_lint.py:
python3 "$SKILL_DIR"/scripts/voice_lint.py ./BRAND-VOICE.md
Verdicts: GREEN (zero errors, zero warnings), YELLOW (warnings only — acceptable but flagged), RED (errors — block). Output is JSON per references/schemas.md § voice_lint.py.
extract and update lint before writing. RED → fix and re-lint without prompting the user. YELLOW → present warnings to the user and proceed on confirmation.
When the first token of $ARGUMENTS does not match extract|update|diff|validate|lint|check|show, the skill behaves as follows:
BRAND-VOICE.md at the target → suggest /brand-voice extract with the sources the user mentions inline. Do not silently extract.BRAND-VOICE.md exists → run show --rules and print the testable rules. Useful when the user types /brand-voice to glance at the current contract./brand-voice extract -u <url> (or /brand-voice update -u <url> if a doc exists).-f invocation.The default workflow exists to avoid silent state-modifying actions. Every write goes through an explicit subcommand.
./BRAND-VOICE.md as a code asset. Diff before merge. The git history is the audit trail.extract and update runs voice_lint.py on the synthesised content before the user is asked to approve. RED never reaches disk.extract refuses to overwrite an existing file. update always shows a diff and asks for explicit yes. diff is read-only by definition.<!-- manual: true --> is preserved verbatim by update. Do not re-synthesise.AskUserQuestion. When a source contradicts the existing doc during update, surface. No silent picks../BRAND-VOICE.md. Pipeline copies under ~/.claude/output/{project}/brand-voice/brand-voice-{slug}.md only when -s is passed./humanize-en -f BRAND-VOICE.md <draft>. This skill never humanises./markitdown -s <source>, then /brand-voice extract -f <markitdown-output>./award-design and /design-system. Brand voice is prose; brand visuals are tokens. Different docs, different lifecycles.steps/extract.md, steps/update.md, steps/diff.md, steps/validate.md, steps/show.md — per-subcommand workflows, flags, edge cases.references/canonical-format.md — full schema, required vs recommended sections, section ordering, manual-section markers, inheritance via voice.extends. The contract.references/example-chanel.md — complete reference voice doc, anchored on chanel.com primary sources (Métiers d'art savoir-faire page, House of Chanel history, founder page) plus Met Museum and Wikipedia as cross-references. Use as a structural template.references/example-multi-voice.md — worked example of voice.extends: a fictional founder-led startup with parent + child + merged result side-by-side, plus when to use _replace vs _remove vs default merge.references/source-resolution.md — how each -u/-n/-d/-f flag resolves, failure modes, conflict handling.references/interview-questions.md — eight canonical questions for extract with no source flag.references/schemas.md — JSON shape for voice_lint.py, plain-text shape for extract_rules.py. Stable contract for downstream consumers.scripts/voice_lint.py — validates a BRAND-VOICE.md, walks voice.extends chain, emits chain and merged_stats when inheritance applies. Python 3.7+, no third-party deps.scripts/extract_rules.py — emits flat testable rules. Resolves voice.extends chain by default. --full (default) includes core_attributes/contexts/source_urls; --legacy emits the v1 minimal output. Consumed by humanize-en -f.scripts/measure_corpus.py — measures stylometric stats from a prose corpus; --as-sentence-norms emits a sentence_norms dict (or null below the 30-sentence threshold) for extract to use in place of estimated bounds. stdlib only.scripts/lint_all.py — globs every BRAND-VOICE*.md under a root and lints each. Single-command audit for the parent-change blast-radius problem: a parent edit that breaks N children surfaces as N RED verdicts. CI-friendly; recommended in pre-merge hooks.scripts/utils.py — shared I/O helpers, chain resolution (resolve_extends_chain), merge engine (merge_voice_dicts, apply_replace_overrides, apply_remove_overrides). Not invoked directly._replace directives win over parent rules even when --chain shows the parent. scripts/utils.py:resolve_extends_chain walks parent → child; merge applies overrides last. Verify final state with voice_lint.py (chain state is in its JSON output; --chain is a show flag) before merging.scripts/lint_all.py, not just the target; block writes if any ancestor is RED._replace / _remove only apply to fields in REPLACE_ALLOWED_FIELDS / REMOVE_ALLOWED_FIELDS (scripts/utils.py:334-352). Overrides on other fields (e.g., voice, source_urls, signature_traits) no-op at merge and surface only at lint time. Always run voice_lint.py on the child after override edits.extends-depth-exceeded (scripts/utils.py:332: MAX_EXTENDS_DEPTH = 5). Fix: keep chains short (≤3 hops); flatten when a child needs more than 2 ancestors.extract_rules.py path mismatch breaks humanize-en -f silently. Consumers try three candidates and degrade to universal patterns if none resolve. Symlink or copy the script when your install path is non-standard.npx claudepluginhub coroboros/agent-skills --plugin productivity-skillsGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.