brand-intent 0.1.3 → 0.1.5

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 CHANGED
@@ -17,9 +17,9 @@ Brand Intent is a set of five composable file types that encode brand knowledge
17
17
  | Layer | File | Question |
18
18
  |-------|------|----------|
19
19
  | Identity | `.identity` | Who is this brand? |
20
- | Brand | `.brand` | What expression intent does it derive? |
21
- | Format | `.format` | Where does this content appear? |
22
- | Purpose | `.purpose` | What is this content trying to do? |
20
+ | Brand | `.brand` | How is it expressed? |
21
+ | Format | `.format` | Where does it appear? |
22
+ | Purpose | `.purpose` | What is this content for? |
23
23
  | Composition | `.composition` | How are elements composed? |
24
24
 
25
25
  ## Get started
@@ -29,23 +29,15 @@ Brand Intent is a set of five composable file types that encode brand knowledge
29
29
  Syntax highlighting, autocomplete, and validation for all five file types.
30
30
 
31
31
  ```
32
- ext install stoll.brand-intent-lang
32
+ ext install studiostoll.brand-intent-lang
33
33
  ```
34
34
 
35
- ### 2. Install skills for your AI agent
35
+ ### 2. Run init
36
36
 
37
- ```bash
38
- npx brand-intent install-skills
39
- ```
40
-
41
- Works with Claude Code, Cursor, Gemini CLI, VS Code Copilot, Codex CLI, and any MCP-compatible agent.
42
-
43
- ### 3. Author your brand
44
-
45
- Start with `.identity` (strategy, voice, audiences), then derive `.brand` (expression intent). Add formats, purposes, and compositions as needed. The Krume bakery brand in `brands/krume/` is a fully commented reference implementation.
37
+ Installs the Brand Intent skill for your AI agent. The skill handles everything else: onboard your brand through a guided interview, explore the Krume bakery reference, generate and validate on-brand content.
46
38
 
47
39
  ```bash
48
- npx brand-intent init --from krume
40
+ npx brand-intent init
49
41
  ```
50
42
 
51
43
  ## Documentation
@@ -54,10 +46,6 @@ npx brand-intent init --from krume
54
46
  - [Design Decisions](DESIGN.md): reasoning behind the framework
55
47
  - [intent.stoll.studio](https://intent.stoll.studio): project landing page
56
48
 
57
- ## Relationship to Impeccable
58
-
59
- Brand Intent is complementary to [Impeccable](https://impeccable.style). Where Impeccable teaches agents *how to design well* (general craft), Brand Intent teaches them *what this brand's expression intent is* (specific identity). Together: execution quality and brand specificity.
60
-
61
49
  ## License
62
50
 
63
51
  MIT
@@ -27,10 +27,15 @@ theme Laden
27
27
  text-primary: $primary
28
28
  text-secondary: $secondary
29
29
  text-tertiary: $accent1
30
+ text-subtle: #A89282
31
+ text-muted: #C4B5A7
30
32
  logo: $primary
33
+ sticker-bg: $accent1
34
+ sticker-fg: $primary
31
35
  cta: $accent2
32
36
  label-bg: $accent1
33
37
  label-fg: $primary
38
+ icon: $secondary
34
39
  divider: $accent1
35
40
 
36
41
  theme Tafel
@@ -38,33 +43,61 @@ theme Tafel
38
43
  text-primary: $white
39
44
  text-secondary: #D4C4B0
40
45
  text-tertiary: #8A7A68
46
+ text-subtle: #6A5A48
47
+ text-muted: #4A3A28
41
48
  logo: $accent1
49
+ sticker-bg: $accent2
50
+ sticker-fg: $white
42
51
  cta: $accent1
43
52
  label-bg: $accent2
44
53
  label-fg: $white
54
+ icon: #D4C4B0
45
55
  divider: $accent1
46
56
 
47
57
  # ── Typography ──
48
- # Libre Franklin: utilitarian, no-nonsense, legible.
49
- # Not a display typeface. Not decorative. Feels like signage.
58
+ # Hanken Grotesk: clean, geometric, contemporary.
59
+ # ABC Stefan Simple: display face for hero moments.
50
60
 
51
61
  font primary
52
- name: Libre Franklin
62
+ name: Hanken Grotesk
53
63
  fallback: sans-serif
54
64
  source: google
55
65
 
66
+ font secondary
67
+ name: ABC Stefan Simple
68
+ fallback: sans-serif
69
+ source: local
70
+
71
+ typography display
72
+ font: $secondary
73
+ weight: 400
74
+ size: 8
75
+ lineHeight: 1.1
76
+ autofit-min: 5
77
+ autofit-max: 12
78
+
56
79
  typography headline
57
80
  weight: 700
58
81
  strong: 800
59
- size: 6 # cqh - large but not shouting
82
+ size: 6
60
83
  lineHeight: 1.15
61
84
  autofit-min: 4
62
85
  autofit-max: 10
63
86
 
64
87
  typography kicker
65
- weight: 500
66
- size: 3.5
88
+ weight: 400
89
+ size: 2.6
67
90
  lineHeight: 1.3
91
+ autofit-min: 2.5
92
+ autofit-max: 3.5
93
+
94
+ typography quote
95
+ weight: 700
96
+ strong: 800
97
+ size: 5.5
98
+ lineHeight: 1.2
99
+ autofit-min: 4
100
+ autofit-max: 8
68
101
 
69
102
  typography body
70
103
  weight: 400
@@ -76,6 +109,12 @@ typography caption
76
109
  size: 2.2
77
110
  lineHeight: 1.4
78
111
 
112
+ typography attribution
113
+ weight: 300
114
+ size: 2.2
115
+ lineHeight: 1.4
116
+ letterSpacing: 0.08
117
+
79
118
  typography label
80
119
  weight: 600
81
120
  size: 2
@@ -83,10 +122,10 @@ typography label
83
122
  uppercase
84
123
 
85
124
  typography number
86
- weight: 300
87
- size: 14 # intentionally large - display element
88
- lineHeight: 0.9
89
- letterSpacing: -0.03
125
+ weight: 300
126
+ size: 14
127
+ lineHeight: 0.9
128
+ letterSpacing: -0.03
90
129
 
91
130
  # ── Spacing ──
92
131
 
@@ -105,31 +144,12 @@ divider line
105
144
  width: 100%
106
145
  color: divider
107
146
  align: left
108
- spacing: xs
147
+ spacing: s
109
148
 
110
- divider short
111
- thickness: 0.2
112
- width: 25%
149
+ divider ruler
150
+ thickness: 0.8
151
+ width: 20%
113
152
  color: divider
114
153
  align: left
115
- spacing: s
154
+ spacing: m
116
155
 
117
- # ── Voice Constraints ──
118
- # Operationalized from identity voice reasoning.
119
- # These are enforceable limits, not restated strategy.
120
- # Derived from: persona is direct, rhythm is short sentences.
121
-
122
- voice-constraints
123
- register: informal
124
- sentence-max: 8 # words - short, direct, like the baker talks
125
- headline-pattern: noun-first
126
- number-format: cardinal # "drei", not "3"
127
-
128
- # ── Content Defaults ──
129
- # Derived from identity: this is a small bakery, not a media brand.
130
- # Low density. Natural images. Simple dividers.
131
-
132
- content-defaults
133
- density: low
134
- image-treatment: natural
135
- divider: line
package/cli/index.mjs CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, readdirSync, statSync } from 'fs';
4
- import { join, dirname, resolve, basename } from 'path';
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
4
+ import { join, dirname, resolve } from 'path';
5
5
  import { fileURLToPath } from 'url';
6
+ import { createInterface } from 'readline';
6
7
 
7
8
  const __filename = fileURLToPath(import.meta.url);
8
9
  const __dirname = dirname(__filename);
@@ -10,108 +11,77 @@ const PKG_ROOT = resolve(__dirname, '..');
10
11
 
11
12
  const SKILL_FILE = join(PKG_ROOT, 'skills', 'brand-intent.md');
12
13
 
13
- // ── Harness detection and install paths ──
14
+ // ── Harness detection ──
14
15
 
15
- const HARNESS_DIRS = {
16
- claude: '.claude/commands',
17
- cursor: '.cursor/rules',
18
- gemini: '.gemini',
19
- copilot: '.github/copilot-instructions',
20
- };
16
+ const HARNESSES = [
17
+ { id: 'claude', label: 'Claude Code', dir: '.claude/commands' },
18
+ { id: 'cursor', label: 'Cursor', dir: '.cursor/rules' },
19
+ { id: 'gemini', label: 'Gemini CLI', dir: '.gemini' },
20
+ { id: 'copilot', label: 'VS Code Copilot', dir: '.github/copilot-instructions' },
21
+ ];
21
22
 
22
23
  function detectHarnesses(cwd) {
23
- const found = [];
24
- for (const [name, dir] of Object.entries(HARNESS_DIRS)) {
25
- // Check if the harness config directory exists (or parent does)
26
- const parent = dir.split('/')[0];
27
- if (existsSync(join(cwd, parent))) {
28
- found.push({ name, dir });
29
- }
24
+ return HARNESSES.filter(h => {
25
+ const parent = h.dir.split('/')[0];
26
+ return existsSync(join(cwd, parent));
27
+ });
28
+ }
29
+
30
+ // ── Readline ──
31
+
32
+ let _rl;
33
+ function rl() {
34
+ if (!_rl) {
35
+ _rl = createInterface({ input: process.stdin, output: process.stdout });
30
36
  }
31
- return found;
37
+ return _rl;
38
+ }
39
+ function closeRl() {
40
+ if (_rl) { _rl.close(); _rl = null; }
32
41
  }
33
42
 
34
- // ── Commands ──
43
+ function ask(q) {
44
+ return new Promise((res) => rl().question(q, res));
45
+ }
46
+
47
+ // ── Install skill ──
35
48
 
36
- function installSkills(cwd) {
49
+ async function installSkills(cwd) {
37
50
  if (!existsSync(SKILL_FILE)) {
38
51
  console.error('Error: skill file not found at', SKILL_FILE);
39
52
  process.exit(1);
40
53
  }
41
54
 
42
55
  const skill = readFileSync(SKILL_FILE, 'utf-8');
43
- const harnesses = detectHarnesses(cwd);
56
+ let harnesses = detectHarnesses(cwd);
44
57
 
45
58
  if (harnesses.length === 0) {
46
- // No harness detected — install to .claude/ by default
47
- harnesses.push({ name: 'claude', dir: HARNESS_DIRS.claude });
59
+ console.log(' Which AI tools do you use?\n');
60
+ HARNESSES.forEach((h, i) => {
61
+ console.log(` ${i + 1}) ${h.label}`);
62
+ });
63
+ console.log(` ${HARNESSES.length + 1}) all of the above`);
64
+ console.log('');
65
+ const answer = await ask(` Choose [1-${HARNESSES.length + 1}]: `);
66
+ const idx = parseInt(answer, 10) - 1;
67
+ if (idx === HARNESSES.length) {
68
+ harnesses = HARNESSES;
69
+ } else if (idx >= 0 && idx < HARNESSES.length) {
70
+ harnesses = [HARNESSES[idx]];
71
+ } else {
72
+ harnesses = [HARNESSES[0]];
73
+ }
48
74
  }
49
75
 
50
- let installed = 0;
51
- for (const { name, dir } of harnesses) {
76
+ for (const { label, dir } of harnesses) {
52
77
  const target = join(cwd, dir);
53
78
  mkdirSync(target, { recursive: true });
54
79
  const dest = join(target, 'brand-intent.md');
55
80
  writeFileSync(dest, skill, 'utf-8');
56
- console.log(` ✓ ${name} — ${dir}/brand-intent.md`);
57
- installed++;
58
- }
59
-
60
- console.log(`\nInstalled Brand Intent skill to ${installed} harness${installed > 1 ? 'es' : ''}.`);
61
- }
62
-
63
- function copyDirRecursive(src, dest) {
64
- mkdirSync(dest, { recursive: true });
65
- for (const entry of readdirSync(src)) {
66
- const srcPath = join(src, entry);
67
- const destPath = join(dest, entry);
68
- if (statSync(srcPath).isDirectory()) {
69
- copyDirRecursive(srcPath, destPath);
70
- } else {
71
- copyFileSync(srcPath, destPath);
72
- }
73
- }
74
- }
75
-
76
- function initFromBrand(brandName, cwd) {
77
- const brandSrc = join(PKG_ROOT, 'brands', brandName);
78
- if (!existsSync(brandSrc)) {
79
- console.error(`Error: brand "${brandName}" not found.`);
80
- console.error(`Available brands: ${readdirSync(join(PKG_ROOT, 'brands')).join(', ')}`);
81
- process.exit(1);
82
- }
83
-
84
- // Copy brand files
85
- const brandDest = join(cwd, `brands/${brandName}`);
86
- copyDirRecursive(brandSrc, brandDest);
87
- console.log(` ✓ brand files → brands/${brandName}/`);
88
-
89
- // Copy shared layers (formats, purposes, compositions)
90
- for (const layer of ['formats', 'purposes', 'compositions']) {
91
- const layerSrc = join(PKG_ROOT, layer);
92
- if (existsSync(layerSrc) && readdirSync(layerSrc).length > 0) {
93
- const layerDest = join(cwd, layer);
94
- copyDirRecursive(layerSrc, layerDest);
95
- console.log(` ✓ ${layer}/ → ${layer}/`);
96
- }
97
- }
98
-
99
- console.log(`\nScaffolded from "${brandName}". Edit the .identity and .brand files to make it yours.`);
100
- }
101
-
102
- function addBrand(brandRef, cwd) {
103
- // brandRef is "brandName/brand" — extract the brand name
104
- const brandName = brandRef.split('/')[0];
105
- const brandSrc = join(PKG_ROOT, 'brands', brandName);
106
- if (!existsSync(brandSrc)) {
107
- console.error(`Error: brand "${brandName}" not found.`);
108
- process.exit(1);
81
+ console.log(` \u2713 ${label} \u2014 ${dir}/brand-intent.md`);
109
82
  }
110
83
 
111
- const brandDest = join(cwd, `brands/${brandName}`);
112
- copyDirRecursive(brandSrc, brandDest);
113
- console.log(` ✓ brand files → brands/${brandName}/`);
114
- console.log(`\nAdded "${brandName}" brand files to your project.`);
84
+ return harnesses;
115
85
  }
116
86
 
117
87
  // ── CLI entry point ──
@@ -123,41 +93,44 @@ const cwd = process.cwd();
123
93
  const pkg = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf-8'));
124
94
  console.log(`Brand Intent v${pkg.version}\n`);
125
95
 
126
- switch (command) {
127
- case 'install-skills':
128
- installSkills(cwd);
129
- break;
130
-
131
- case 'init': {
132
- const fromIdx = args.indexOf('--from');
133
- if (fromIdx === -1 || !args[fromIdx + 1]) {
134
- console.error('Usage: brand-intent init --from <brand-name>');
135
- console.error('Example: brand-intent init --from krume');
136
- process.exit(1);
137
- }
138
- initFromBrand(args[fromIdx + 1], cwd);
139
- break;
140
- }
96
+ if (command && command !== 'init' && command !== 'install-skills' && command !== '--help' && command !== '-h') {
97
+ console.log(` Unknown command: ${command}\n`);
98
+ }
141
99
 
142
- case 'add': {
143
- if (!args[1]) {
144
- console.error('Usage: brand-intent add <brand-name>/brand');
145
- console.error('Example: brand-intent add krume/brand');
146
- process.exit(1);
147
- }
148
- addBrand(args[1], cwd);
149
- break;
150
- }
100
+ if (!command || command === '--help' || command === '-h') {
101
+ console.log('Usage:');
102
+ console.log(' brand-intent init Install the Brand Intent skill for your AI agent');
103
+ console.log(' brand-intent install-skills Same as init (alias)');
104
+ console.log('');
105
+ console.log('Example:');
106
+ console.log(' npx brand-intent init');
107
+ process.exit(0);
108
+ }
151
109
 
152
- default:
153
- console.log('Usage:');
154
- console.log(' brand-intent install-skills Install Brand Intent skill for your AI harness');
155
- console.log(' brand-intent init --from <brand> Scaffold a project from a reference brand');
156
- console.log(' brand-intent add <brand>/brand Add a brand to an existing project');
157
- console.log('');
158
- console.log('Examples:');
159
- console.log(' npx brand-intent install-skills');
160
- console.log(' npx brand-intent init --from krume');
161
- console.log(' npx brand-intent add krume/brand');
162
- break;
110
+ // Both "init" and "install-skills" do the same thing now
111
+ const installed = await installSkills(cwd);
112
+ closeRl();
113
+
114
+ // Show next steps with skill-specific commands
115
+ const hasClaudeCode = installed.some(h => h.id === 'claude');
116
+ const hasCursor = installed.some(h => h.id === 'cursor');
117
+
118
+ console.log('\n Done. Now open your AI agent and try:\n');
119
+ console.log(' "Onboard my brand"');
120
+ console.log(' "Show me the Krume bakery example"');
121
+
122
+ if (hasClaudeCode || hasCursor) {
123
+ console.log('\n Or use the skill directly:\n');
124
+ if (hasClaudeCode) {
125
+ console.log(' /brand-intent onboard Create your brand through a guided interview');
126
+ console.log(' /brand-intent example Scaffold the Krume reference implementation');
127
+ console.log(' /brand-intent validate Validate content against your brand rules');
128
+ console.log(' /brand-intent Discover brand files and available purposes');
129
+ }
130
+ if (hasCursor) {
131
+ console.log(' @brand-intent onboard Create your brand through a guided interview');
132
+ console.log(' @brand-intent example Scaffold the Krume reference implementation');
133
+ }
163
134
  }
135
+
136
+ console.log('');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brand-intent",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "An open file format specification for making brand intent machine-readable.",
5
5
  "author": "Christophe Stoll <christophe@stoll.studio>",
6
6
  "license": "MIT",
@@ -20,6 +20,9 @@
20
20
  "brand-identity",
21
21
  "design-system"
22
22
  ],
23
+ "scripts": {
24
+ "release": "./scripts/release.sh"
25
+ },
23
26
  "files": [
24
27
  "cli/",
25
28
  "skills/",
@@ -6,6 +6,8 @@ description: Generate and validate on-brand content using Brand Intent files
6
6
 
7
7
  **Scope:** `$ARGUMENTS`
8
8
 
9
+ > If `$ARGUMENTS` is "onboard": run the guided brand onboarding interview (see Onboarding section below).
10
+ > If `$ARGUMENTS` is "example": scaffold the Krume bakery reference implementation into `examples/krume/` and explain what each file does.
9
11
  > If `$ARGUMENTS` names a purpose (e.g. "daily-bread", "event", "quote"): compose context from the five layers and generate content for that purpose.
10
12
  > If `$ARGUMENTS` is "validate": read the most recent generated or provided content and run the validation checklist against the brand files.
11
13
  > If `$ARGUMENTS` is empty: discover all `.identity`, `.brand`, `.purpose` files in the project, summarize the brand voice and list available purposes.
@@ -98,9 +100,6 @@ When generating content for a specific purpose, read all layers silently (do not
98
100
 
99
101
  ### Output format:
100
102
 
101
- For each brand that has the requested purpose:
102
-
103
- - If multiple brands exist, show the **brand name** as a heading
104
103
  - Show each **slot** with its generated content — nothing else
105
104
  - After the content, show a **compact validation summary** (pass/fail per rule, only call out failures in detail)
106
105
 
@@ -132,27 +131,115 @@ After generating or reviewing content, validate against these rules in order:
132
131
  Brand Intent files follow this structure:
133
132
 
134
133
  ```
135
- # Single brand
136
134
  brandname.identity
137
135
  brandname.brand
138
136
  formats/*.format
139
137
  purposes/*.purpose
140
138
  compositions/*.composition
139
+ ```
140
+
141
+ To find the active brand, look for the `.identity` and `.brand` files at the project root. There is exactly one of each per project.
142
+
143
+ ## Onboarding: Create a New Brand
144
+
145
+ When `$ARGUMENTS` is "onboard", guide the user through a structured interview to create their brand files. Do NOT jump to file creation — the interview comes first.
146
+
147
+ ### Interview Flow
148
+
149
+ Have a natural conversation. Don't dump all questions at once — adapt based on answers. Group questions into rounds of 2-4.
150
+
151
+ **Round 1: Identity**
152
+ - Brand name? (and brand ID — lowercase, no spaces, used in filenames)
153
+ - What does this brand do? One sentence.
154
+ - Language? (e.g., de, en)
155
+ - Essence — the irreducible truth in 3-8 words?
156
+
157
+ Listen for tone, confidence, specificity. A founder who says "we make artisan coffee" vs "we roast single-origin in a garage in Kreuzberg" gives you very different brands.
158
+
159
+ **Round 2: Voice & Personality**
160
+ - If this brand were a person talking to a customer, who would they be?
161
+ - Formal or informal?
162
+ - What does this brand ALWAYS do in communication? (2-3 concrete rules)
163
+ - What does this brand NEVER do? (2-3 concrete anti-patterns)
164
+
165
+ The user will often describe what the brand is NOT before what it IS. Capture both.
166
+
167
+ **Round 3: Visual Direction**
168
+ - Describe the color palette. Hex values, or a mood? (e.g., "warm earth tones", "cold Nordic blue")
169
+ - What fonts define this brand? (primary + body. If unknown: serif or sans-serif? Heavy or light?)
170
+
171
+ If the user describes a mood: propose a palette of 4-5 colors (primary, secondary, accent1, accent2, white/background) and confirm before proceeding.
172
+
173
+ **Round 4: Content & Audience**
174
+ - Who are the 1-2 main audiences? For each: who are they, what do they want, how should you talk to them?
175
+ - Anti-audience — someone this brand is explicitly NOT for?
176
+ - What content topics are on-brand? (primary pillars)
177
+ - What topics should the brand avoid?
178
+
179
+ ### Synthesis
141
180
 
142
- # Multi-brand
143
- brands/brandname/brandname.identity
144
- brands/brandname/brandname.brand
145
- formats/*.format ← shared
146
- purposes/*.purpose ← shared
147
- compositions/*.composition ← shared
181
+ After the interview, present a Brand Brief for confirmation:
182
+
183
+ ```
184
+ ## Brand Brief: [Brand Name]
185
+ ID: [brandid] | Language: [language]
186
+ Essence: [essence]
187
+
188
+ Voice: [persona], [register]
189
+ Always: [rules]
190
+ Never: [anti-patterns]
191
+
192
+ Colors: primary [hex], secondary [hex], accent [hex], background [hex]
193
+ Font: [primary font name]
194
+
195
+ Audiences: [summaries]
196
+ Pillars: [topics] | Avoid: [topics]
148
197
  ```
149
198
 
150
- To find the active brand, look for `.identity` and `.brand` files. There is exactly one of each per brand.
199
+ Ask the user to confirm or adjust. Iterate until they're happy.
200
+
201
+ ### File Generation
202
+
203
+ Once confirmed, generate these files at the project root:
204
+
205
+ 1. **`[brandid].identity`** — essence, promise, tagline, archetype, voice (register, persona, rhythm, always/never), pillars, audience blocks, anti-audience blocks. Follow the structure of the Krume reference.
151
206
 
152
- ## Relationship to Impeccable
207
+ 2. **`[brandid].brand`** — id, name, language, locale, brand-colors, at least one theme (mapping semantic slots to color references), font blocks, typography blocks (headline, body, caption, label at minimum), spacing, dividers. Use `$name` references for theme colors.
153
208
 
154
- If this project also uses [Impeccable](https://impeccable.style), the two are complementary:
155
- - **Impeccable** teaches you how to design well (general craft)
156
- - **Brand Intent** teaches you what this brand's expression intent is (specific identity)
209
+ 3. **`purposes/starter.purpose`** — a starter purpose with slots that have on-brand sample content derived from the identity. Samples must be in the brand's language and respect voice rules.
210
+
211
+ ### Rules
212
+
213
+ - **Every voice rule, value, and narrative must come from the user's answers.** Don't invent brand personality.
214
+ - **Sample content in purposes must match the brand's language.**
215
+ - **Don't skip the interview.** Even if the user says "just make a brand called X" — ask at least the essentials (essence, voice, colors, one audience).
216
+ - **Typography sizes use `cqmin` units.** Use these sensible defaults unless the user specified otherwise: headline 6, body 2.8, caption 2.2, label 2.
217
+ - **Spacing defaults:** unit cqmin, xs 1.5, s 2, m 3, l 5, xl 6.5.
218
+
219
+ ## Example: Scaffold the Krume Reference
220
+
221
+ When `$ARGUMENTS` is "example", copy the Krume bakery reference files into `examples/krume/` in the current project. The Krume files are distributed with the npm package.
222
+
223
+ Run this shell command to copy them:
224
+
225
+ ```bash
226
+ node -e "
227
+ const pkg = require.resolve('brand-intent/package.json');
228
+ const root = require('path').dirname(pkg);
229
+ const { cpSync, mkdirSync } = require('fs');
230
+ const dest = 'examples/krume';
231
+ mkdirSync(dest, { recursive: true });
232
+ for (const d of ['brands/krume', 'formats', 'purposes', 'compositions']) {
233
+ const src = require('path').join(root, d);
234
+ try { cpSync(src, require('path').join(dest, d.replace('brands/krume', '')), { recursive: true }); } catch {}
235
+ }
236
+ console.log('Krume example scaffolded into examples/krume/');
237
+ "
238
+ ```
157
239
 
158
- Apply both. Impeccable governs execution quality. Brand Intent governs brand specificity.
240
+ Then explain to the user what each file does:
241
+ - **`.identity`** — the strategic layer: essence, voice, values, audiences. Read this first.
242
+ - **`.brand`** — derived expression: colors, themes, typography, spacing. Every decision traces back to identity.
243
+ - **`.format`** — canvas dimensions, safe zones, grid. Where content appears.
244
+ - **`.purpose`** — content type with semantic slots, samples, constraints. What content does.
245
+ - **`.composition`** — spatial arrangement of slots. How elements are placed.