get-shit-pretty 0.7.4 → 0.8.2
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/bin/theme-css.js +331 -0
- package/gsp/agents/gsp-brand-coherence.md +7 -0
- package/gsp/skills/gsp-brand-brief/SKILL.md +50 -5
- package/gsp/skills/gsp-brand-guidelines/SKILL.md +51 -31
- package/gsp/skills/gsp-brand-guidelines/design-tokens.md +2 -0
- package/gsp/skills/gsp-brand-guidelines/guidelines-structure.md +167 -0
- package/gsp/skills/gsp-brand-guidelines/methodology/gsp-brand-coherence.md +86 -0
- package/gsp/skills/gsp-brand-guidelines/methodology/gsp-brand-engineer.md +13 -5
- package/gsp/skills/gsp-brand-guidelines/token-mapping.md +16 -319
- package/gsp/skills/gsp-brand-identity/SKILL.md +1 -1
- package/gsp/skills/gsp-brand-refine/SKILL.md +5 -6
- package/gsp/skills/gsp-brand-research/SKILL.md +1 -1
- package/gsp/skills/gsp-brand-strategy/SKILL.md +1 -1
- package/gsp/skills/gsp-design-system/SKILL.md +1 -1
- package/gsp/skills/gsp-doctor/SKILL.md +51 -3
- package/gsp/skills/gsp-progress/SKILL.md +1 -1
- package/gsp/skills/gsp-project-brief/SKILL.md +40 -6
- package/gsp/skills/gsp-project-build/SKILL.md +22 -29
- package/gsp/skills/gsp-project-build/flows/figma.md +50 -0
- package/gsp/skills/gsp-project-build/flows/revision.md +64 -0
- package/gsp/skills/gsp-project-build/methodology/gsp-project-builder.md +57 -4
- package/gsp/skills/gsp-project-build/shadcn-composition.md +217 -0
- package/gsp/skills/gsp-project-critique/SKILL.md +3 -1
- package/gsp/skills/gsp-project-design/SKILL.md +3 -1
- package/gsp/skills/gsp-project-research/SKILL.md +3 -1
- package/gsp/skills/gsp-project-review/SKILL.md +10 -1
- package/gsp/skills/gsp-scaffold/SKILL.md +49 -12
- package/gsp/skills/gsp-scaffold/shadcn-rules.md +433 -0
- package/gsp/skills/gsp-scaffold/shadcn-theming.md +310 -0
- package/gsp/skills/gsp-start/SKILL.md +18 -2
- package/gsp/skills/gsp-style/SKILL.md +1 -1
- package/gsp/skills/gsp-style/style-preset-schema.md +59 -14
- package/gsp/skills/gsp-style/styles/academia.yml +58 -8
- package/gsp/skills/gsp-style/styles/art-deco.yml +39 -7
- package/gsp/skills/gsp-style/styles/bauhaus.yml +39 -8
- package/gsp/skills/gsp-style/styles/bold-typography.yml +39 -8
- package/gsp/skills/gsp-style/styles/botanical.yml +39 -9
- package/gsp/skills/gsp-style/styles/claymorphism.yml +39 -9
- package/gsp/skills/gsp-style/styles/cyberpunk.yml +39 -7
- package/gsp/skills/gsp-style/styles/enterprise.yml +39 -10
- package/gsp/skills/gsp-style/styles/flat-design.yml +39 -9
- package/gsp/skills/gsp-style/styles/fluent.yml +39 -10
- package/gsp/skills/gsp-style/styles/glassmorphism.yml +39 -8
- package/gsp/skills/gsp-style/styles/humanist-literary.yml +39 -9
- package/gsp/skills/gsp-style/styles/industrial.yml +59 -9
- package/gsp/skills/gsp-style/styles/kinetic.yml +32 -7
- package/gsp/skills/gsp-style/styles/liquid-glass.yml +59 -9
- package/gsp/skills/gsp-style/styles/luxury.yml +59 -9
- package/gsp/skills/gsp-style/styles/material.yml +59 -9
- package/gsp/skills/gsp-style/styles/maximalism.yml +32 -6
- package/gsp/skills/gsp-style/styles/minimal-dark.yml +32 -7
- package/gsp/skills/gsp-style/styles/modern-dark.yml +32 -7
- package/gsp/skills/gsp-style/styles/monochrome.yml +59 -10
- package/gsp/skills/gsp-style/styles/neubrutalism.yml +59 -9
- package/gsp/skills/gsp-style/styles/neumorphism.yml +32 -7
- package/gsp/skills/gsp-style/styles/newsprint.yml +32 -7
- package/gsp/skills/gsp-style/styles/nothing.yml +44 -13
- package/gsp/skills/gsp-style/styles/organic.yml +42 -9
- package/gsp/skills/gsp-style/styles/playful-geometric.yml +43 -9
- package/gsp/skills/gsp-style/styles/professional.yml +41 -10
- package/gsp/skills/gsp-style/styles/retro.yml +42 -8
- package/gsp/skills/gsp-style/styles/saas.yml +42 -9
- package/gsp/skills/gsp-style/styles/sketch.yml +42 -9
- package/gsp/skills/gsp-style/styles/swiss-minimalist.yml +41 -10
- package/gsp/skills/gsp-style/styles/terminal.yml +42 -8
- package/gsp/skills/gsp-style/styles/vaporwave.yml +42 -7
- package/gsp/skills/gsp-style/styles/web3.yml +42 -9
- package/gsp/templates/branding/brief.md +9 -0
- package/gsp/templates/branding/config.json +1 -1
- package/gsp/templates/design-claude.md +6 -0
- package/gsp/templates/phases/patterns.md +2 -2
- package/gsp/templates/projects/config.json +6 -3
- package/gsp/templates/projects/state.md +1 -1
- package/gsp/templates/system/STACK.md +1 -0
- package/package.json +1 -1
package/bin/theme-css.js
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* theme-css.js — GSP deterministic token-to-CSS generator
|
|
4
|
+
*
|
|
5
|
+
* Reads a GSP style preset `.yml` file and outputs a shadcn/ui-compatible
|
|
6
|
+
* CSS variables block for `:root` and `.dark`.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node bin/theme-css.js <path-to-preset.yml>
|
|
10
|
+
* node bin/theme-css.js <path-to-preset.yml> --output globals.css
|
|
11
|
+
* node bin/theme-css.js <path-to-preset.yml> --stdout
|
|
12
|
+
*
|
|
13
|
+
* Token → CSS var mapping is 1:1. No derivation, no LLM guesswork.
|
|
14
|
+
* Hex values are converted to OKLCH. Alpha values (oklch with /) pass through.
|
|
15
|
+
* Sidebar vars are output verbatim. Extras (success/warning/info) become custom props.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// YAML parser (zero-dependency, handles the subset GSP uses)
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
function parseYaml(text) {
|
|
28
|
+
const lines = text.split('\n');
|
|
29
|
+
const root = {};
|
|
30
|
+
const stack = [{ indent: -1, obj: root }];
|
|
31
|
+
|
|
32
|
+
for (let i = 0; i < lines.length; i++) {
|
|
33
|
+
const raw = lines[i];
|
|
34
|
+
const trimmed = raw.trimEnd();
|
|
35
|
+
if (!trimmed || trimmed.trimStart().startsWith('#')) continue;
|
|
36
|
+
|
|
37
|
+
const indent = raw.length - raw.trimStart().length;
|
|
38
|
+
const content = trimmed.trimStart();
|
|
39
|
+
|
|
40
|
+
// Handle inline arrays: key: [a, b, c]
|
|
41
|
+
const colonIdx = content.indexOf(':');
|
|
42
|
+
if (colonIdx === -1) continue;
|
|
43
|
+
|
|
44
|
+
const key = content.slice(0, colonIdx).trim();
|
|
45
|
+
const rest = content.slice(colonIdx + 1).trim();
|
|
46
|
+
|
|
47
|
+
// Pop stack to correct parent
|
|
48
|
+
while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
|
|
49
|
+
stack.pop();
|
|
50
|
+
}
|
|
51
|
+
const parent = stack[stack.length - 1].obj;
|
|
52
|
+
|
|
53
|
+
if (rest === '' || rest.startsWith('#')) {
|
|
54
|
+
// Nested object
|
|
55
|
+
parent[key] = {};
|
|
56
|
+
stack.push({ indent, obj: parent[key] });
|
|
57
|
+
} else if (rest.startsWith('[')) {
|
|
58
|
+
// Inline array — parse as string, not needed for color extraction
|
|
59
|
+
parent[key] = rest;
|
|
60
|
+
} else {
|
|
61
|
+
// Scalar — strip inline comments and quotes
|
|
62
|
+
let val = rest.split(' #')[0].trim();
|
|
63
|
+
if ((val.startsWith('"') && val.endsWith('"')) ||
|
|
64
|
+
(val.startsWith("'") && val.endsWith("'"))) {
|
|
65
|
+
val = val.slice(1, -1);
|
|
66
|
+
}
|
|
67
|
+
// Numbers
|
|
68
|
+
if (/^-?\d+(\.\d+)?$/.test(val)) {
|
|
69
|
+
parent[key] = parseFloat(val);
|
|
70
|
+
} else if (val === 'true') {
|
|
71
|
+
parent[key] = true;
|
|
72
|
+
} else if (val === 'false') {
|
|
73
|
+
parent[key] = false;
|
|
74
|
+
} else {
|
|
75
|
+
parent[key] = val;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return root;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Color conversion: hex → OKLCH
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
function hexToRgb(hex) {
|
|
88
|
+
const h = hex.replace('#', '');
|
|
89
|
+
const len = h.length;
|
|
90
|
+
if (len === 3) {
|
|
91
|
+
return [
|
|
92
|
+
parseInt(h[0] + h[0], 16),
|
|
93
|
+
parseInt(h[1] + h[1], 16),
|
|
94
|
+
parseInt(h[2] + h[2], 16),
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
return [
|
|
98
|
+
parseInt(h.slice(0, 2), 16),
|
|
99
|
+
parseInt(h.slice(2, 4), 16),
|
|
100
|
+
parseInt(h.slice(4, 6), 16),
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// sRGB → linear
|
|
105
|
+
function toLinear(c) {
|
|
106
|
+
c = c / 255;
|
|
107
|
+
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Linear RGB → XYZ (D65)
|
|
111
|
+
function linearToXyz(r, g, b) {
|
|
112
|
+
return [
|
|
113
|
+
r * 0.4124564 + g * 0.3575761 + b * 0.1804375,
|
|
114
|
+
r * 0.2126729 + g * 0.7151522 + b * 0.0721750,
|
|
115
|
+
r * 0.0193339 + g * 0.1191920 + b * 0.9503041,
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// XYZ → OKLab
|
|
120
|
+
function xyzToOklab(x, y, z) {
|
|
121
|
+
const l_ = Math.cbrt(0.8189330101 * x + 0.3618667424 * y - 0.1288597137 * z);
|
|
122
|
+
const m_ = Math.cbrt(0.0329845436 * x + 0.9293118715 * y + 0.0361456387 * z);
|
|
123
|
+
const s_ = Math.cbrt(0.0482003018 * x + 0.2643662691 * y + 0.6338517070 * z);
|
|
124
|
+
return [
|
|
125
|
+
0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_,
|
|
126
|
+
1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_,
|
|
127
|
+
0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_,
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// OKLab → OKLCH
|
|
132
|
+
function oklabToOklch(L, a, b) {
|
|
133
|
+
const C = Math.sqrt(a * a + b * b);
|
|
134
|
+
let H = Math.atan2(b, a) * (180 / Math.PI);
|
|
135
|
+
if (H < 0) H += 360;
|
|
136
|
+
return [L, C, H];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function hexToOklch(hex) {
|
|
140
|
+
const [r, g, b] = hexToRgb(hex);
|
|
141
|
+
const lr = toLinear(r);
|
|
142
|
+
const lg = toLinear(g);
|
|
143
|
+
const lb = toLinear(b);
|
|
144
|
+
const [x, y, z] = linearToXyz(lr, lg, lb);
|
|
145
|
+
const [L, a, bk] = xyzToOklab(x, y, z);
|
|
146
|
+
const [Lch, C, H] = oklabToOklch(L, a, bk);
|
|
147
|
+
// Format: oklch(L% C H)
|
|
148
|
+
const Lpct = (Lch * 100).toFixed(2);
|
|
149
|
+
const Cfmt = C.toFixed(4);
|
|
150
|
+
const Hfmt = H.toFixed(2);
|
|
151
|
+
return `oklch(${Lpct}% ${Cfmt} ${Hfmt})`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// Value formatter: hex → oklch, alpha values pass through
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
function formatValue(val) {
|
|
159
|
+
if (typeof val !== 'string') return String(val);
|
|
160
|
+
const v = val.trim();
|
|
161
|
+
|
|
162
|
+
// Already oklch (alpha or not) — pass through
|
|
163
|
+
if (v.startsWith('oklch(')) return v;
|
|
164
|
+
|
|
165
|
+
// Hex color
|
|
166
|
+
if (/^#[0-9a-fA-F]{3,6}$/.test(v)) {
|
|
167
|
+
return hexToOklch(v);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Everything else (rgba, named colors, etc.) — pass through
|
|
171
|
+
return v;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
// CSS var name mapping (1:1 to shadcn/ui)
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
|
|
178
|
+
// Core color tokens → CSS var names
|
|
179
|
+
const CORE_VARS = [
|
|
180
|
+
'background', 'foreground',
|
|
181
|
+
'card', 'card-foreground',
|
|
182
|
+
'popover', 'popover-foreground',
|
|
183
|
+
'primary', 'primary-foreground',
|
|
184
|
+
'secondary', 'secondary-foreground',
|
|
185
|
+
'accent', 'accent-foreground',
|
|
186
|
+
'muted', 'muted-foreground',
|
|
187
|
+
'destructive',
|
|
188
|
+
'border', 'input', 'ring',
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
// NOTE: 'sidebar' (not 'sidebar-background') matches shadcn's CSS var --sidebar
|
|
192
|
+
const SIDEBAR_VARS = [
|
|
193
|
+
'sidebar', 'sidebar-foreground',
|
|
194
|
+
'sidebar-primary', 'sidebar-primary-foreground',
|
|
195
|
+
'sidebar-accent', 'sidebar-accent-foreground',
|
|
196
|
+
'sidebar-border', 'sidebar-ring',
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
const EXTRA_VARS = ['success', 'warning', 'info'];
|
|
200
|
+
|
|
201
|
+
// Shape tokens → CSS vars
|
|
202
|
+
const SHAPE_VARS = {
|
|
203
|
+
'border-radius-lg': '--radius',
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// CSS block generator
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
|
|
210
|
+
function generateBlock(colorObj, shapeObj, typographyObj, selector) {
|
|
211
|
+
if (!colorObj) return '';
|
|
212
|
+
const lines = [];
|
|
213
|
+
|
|
214
|
+
// Core vars
|
|
215
|
+
for (const key of CORE_VARS) {
|
|
216
|
+
if (colorObj[key] !== undefined) {
|
|
217
|
+
lines.push(` --${key}: ${formatValue(colorObj[key])};`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Sidebar vars
|
|
222
|
+
for (const key of SIDEBAR_VARS) {
|
|
223
|
+
if (colorObj[key] !== undefined) {
|
|
224
|
+
lines.push(` --${key}: ${formatValue(colorObj[key])};`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Chart vars — emit in both :root and .dark (dark falls back to light values if not set)
|
|
229
|
+
{
|
|
230
|
+
// For :root use light palette; for .dark use dark overrides falling back to light
|
|
231
|
+
const chartColors = [
|
|
232
|
+
colorObj['chart-1'] || colorObj.primary,
|
|
233
|
+
colorObj['chart-2'] || colorObj.secondary,
|
|
234
|
+
colorObj['chart-3'] || colorObj.accent,
|
|
235
|
+
colorObj['chart-4'] || colorObj.info || colorObj.primary,
|
|
236
|
+
colorObj['chart-5'] || colorObj.success || colorObj.secondary || colorObj.primary,
|
|
237
|
+
];
|
|
238
|
+
chartColors.forEach((c, i) => {
|
|
239
|
+
if (c) lines.push(` --chart-${i + 1}: ${formatValue(c)};`);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Shape → radius (only in :root)
|
|
244
|
+
if (selector === ':root' && shapeObj) {
|
|
245
|
+
const lg = shapeObj['border-radius-lg'];
|
|
246
|
+
if (lg !== undefined) {
|
|
247
|
+
lines.push(` --radius: ${lg};`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Typography → font CSS vars (only in :root)
|
|
252
|
+
if (selector === ':root' && typographyObj) {
|
|
253
|
+
const fontMappings = [
|
|
254
|
+
['font-family-primary', '--font-sans'],
|
|
255
|
+
['font-family-mono', '--font-mono'],
|
|
256
|
+
['font-family-display', '--font-display'],
|
|
257
|
+
['font-family-secondary', '--font-secondary'],
|
|
258
|
+
];
|
|
259
|
+
for (const [ymlKey, cssVar] of fontMappings) {
|
|
260
|
+
if (typographyObj[ymlKey] !== undefined) {
|
|
261
|
+
lines.push(` ${cssVar}: ${typographyObj[ymlKey]};`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Extras as custom properties
|
|
267
|
+
for (const key of EXTRA_VARS) {
|
|
268
|
+
if (colorObj[key] !== undefined) {
|
|
269
|
+
lines.push(` --${key}: ${formatValue(colorObj[key])};`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (!lines.length) return '';
|
|
274
|
+
return `${selector} {\n${lines.join('\n')}\n}`;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
// Main
|
|
279
|
+
// ---------------------------------------------------------------------------
|
|
280
|
+
|
|
281
|
+
function main() {
|
|
282
|
+
const args = process.argv.slice(2);
|
|
283
|
+
if (!args.length || args.includes('--help') || args.includes('-h')) {
|
|
284
|
+
console.log(`Usage: node bin/theme-css.js <preset.yml> [--output <file>] [--stdout]`);
|
|
285
|
+
console.log(` node bin/theme-css.js gsp/skills/gsp-style/styles/saas.yml`);
|
|
286
|
+
process.exit(0);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const inputPath = path.resolve(args[0]);
|
|
290
|
+
if (!fs.existsSync(inputPath)) {
|
|
291
|
+
console.error(`Error: File not found: ${inputPath}`);
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const outputIdx = args.indexOf('--output');
|
|
296
|
+
const outputPath = outputIdx !== -1 ? path.resolve(args[outputIdx + 1]) : null;
|
|
297
|
+
const toStdout = args.includes('--stdout') || !outputPath;
|
|
298
|
+
|
|
299
|
+
const raw = fs.readFileSync(inputPath, 'utf8');
|
|
300
|
+
const preset = parseYaml(raw);
|
|
301
|
+
|
|
302
|
+
const colorLight = (preset.tokens && preset.tokens.color) || {};
|
|
303
|
+
const colorDark = (preset.dark_mode && preset.dark_mode.color) || {};
|
|
304
|
+
const shape = (preset.tokens && preset.tokens.shape) || {};
|
|
305
|
+
const typography = (preset.tokens && preset.tokens.typography) || null;
|
|
306
|
+
|
|
307
|
+
const rootBlock = generateBlock(colorLight, shape, typography, ':root');
|
|
308
|
+
const darkBlock = generateBlock(colorDark, null, null, '.dark');
|
|
309
|
+
|
|
310
|
+
const presetName = preset.name || path.basename(inputPath, '.yml');
|
|
311
|
+
const presetDesc = preset.description || '';
|
|
312
|
+
|
|
313
|
+
const header = [
|
|
314
|
+
`/* GSP theme: ${presetName} */`,
|
|
315
|
+
presetDesc ? `/* ${presetDesc} */` : null,
|
|
316
|
+
`/* Generated by bin/theme-css.js from ${path.basename(inputPath)} */`,
|
|
317
|
+
`/* Edit the .yml file, not this output */`,
|
|
318
|
+
'',
|
|
319
|
+
].filter(Boolean).join('\n');
|
|
320
|
+
|
|
321
|
+
const output = [header, rootBlock, darkBlock ? '' : null, darkBlock].filter(s => s !== null).join('\n');
|
|
322
|
+
|
|
323
|
+
if (toStdout) {
|
|
324
|
+
process.stdout.write(output + '\n');
|
|
325
|
+
} else {
|
|
326
|
+
fs.writeFileSync(outputPath, output + '\n', 'utf8');
|
|
327
|
+
console.log(`Written to ${outputPath}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
main();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gsp-brand-coherence
|
|
3
|
+
description: Brand coherence auditor — scores intensity dials against archetype and surfaces tensions between declared values and expressed output
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
---
|
|
7
|
+
Coherence auditor for gsp-brand-guidelines. Spawned by the skill after Pass 1 to evaluate the generated artifacts before user review.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gsp-brand-brief
|
|
3
|
-
description: Define your brand — who, why, and what it should feel like
|
|
3
|
+
description: Define your brand — who, why, and what it should feel like — use when: create a brand, define our brand identity, who are we, what's our brand
|
|
4
4
|
user-invocable: true
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- Read
|
|
@@ -86,23 +86,35 @@ Before presenting personality options, **internally synthesize** promise (what s
|
|
|
86
86
|
- Note: this is a high-level direction only. Brand strategy phase will deepen this into archetype + voice — don't over-refine here.
|
|
87
87
|
8. What should the brand NEVER feel like? (use `AskUserQuestion` with 2-3 anti-directions inferred from their personality pick, plus open-ended option)
|
|
88
88
|
9. Brands admired or styles to avoid? (open-ended `AskUserQuestion`)
|
|
89
|
+
10. Visual direction — raw aesthetic feeling. Use `AskUserQuestion` (open-ended):
|
|
90
|
+
> "What should it look and feel like visually? You can share image or website links, describe a mood ('editorial and dark', 'warm brutalist', 'cinematographic with beautiful stills'), drop adjective clusters ('rounded, clean, airy'), or even describe a scene or texture. The weirder and more specific the better — this is what prevents a bland brand."
|
|
91
|
+
- Synthesize the answer into a `visual_direction` block in the brief: mood words, reference aesthetics, texture/atmosphere descriptors, any specific anti-patterns (e.g., "never stock-photo corporate"). This block directly informs color, typography, and imagery choices downstream.
|
|
89
92
|
|
|
90
93
|
## Step 4: Constraints & confirmation
|
|
91
94
|
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
11. Any non-negotiables or constraints? (timeline, budget, must-haves) — open-ended `AskUserQuestion`
|
|
96
|
+
12. State your understanding back — but lead with *feeling*, not facts. Format:
|
|
97
|
+
|
|
98
|
+
> "Here's what I'm hearing: [2-sentence factual summary].
|
|
99
|
+
> The feeling this brand should leave: **[emotional compass — one evocative sentence capturing the brand's energy, not its category]**."
|
|
100
|
+
|
|
101
|
+
The emotional compass is the hardest line to write and the most important. It should make the user feel something when they read it. Not "a fintech tool that simplifies investing" but "the brand that makes financial confidence feel earned, not given." Synthesize it from the personality direction, the persona aspiration, the brand POV, and the visual direction. It should be specific enough to be wrong — vague sentences aren't compasses.
|
|
102
|
+
|
|
103
|
+
Use `AskUserQuestion`:
|
|
94
104
|
- **Looks good** — "That's accurate, let's go"
|
|
95
|
-
- **Adjust
|
|
105
|
+
- **Adjust the feeling** — "The compass is off — let me reframe it"
|
|
106
|
+
- **Adjust something else** — "Facts are right but I want to change something"
|
|
96
107
|
|
|
97
108
|
If "Adjust" — ask what to change, update your understanding, re-confirm. Don't re-ask everything.
|
|
98
109
|
|
|
99
|
-
## Step 5: Write artifacts
|
|
110
|
+
## Step 5: Write artifacts and register brand
|
|
100
111
|
|
|
101
112
|
Read templates at write time from `${CLAUDE_SKILL_DIR}/../../templates/branding/` and write:
|
|
102
113
|
|
|
103
114
|
1. `.design/branding/{name}/BRIEF.md` from `brief.md` template
|
|
104
115
|
- Populate all sections from conversation answers
|
|
105
116
|
- Synthesize brand promise, POV, and personality (these are inferred, not asked directly)
|
|
117
|
+
- Write the confirmed emotional compass as `brand_heartbeat` in the Emotional Compass section
|
|
106
118
|
- Set `brand_mode` to `new`
|
|
107
119
|
- Set evolve-only sections (Existing Brand State, Evolution Scope) to "N/A — new brand"
|
|
108
120
|
|
|
@@ -117,6 +129,14 @@ Read templates at write time from `${CLAUDE_SKILL_DIR}/../../templates/branding/
|
|
|
117
129
|
|
|
118
130
|
4. `.design/branding/{name}/ROADMAP.md` from `roadmap.md` template
|
|
119
131
|
|
|
132
|
+
5. Write/update `.design/CLAUDE.md` — register the brand as started. If the file doesn't exist, read `${CLAUDE_SKILL_DIR}/../../templates/design-claude.md` first. Append under `## Brands`:
|
|
133
|
+
|
|
134
|
+
```markdown
|
|
135
|
+
### {brand-name} · in progress · {DATE}
|
|
136
|
+
"{brand_heartbeat}"
|
|
137
|
+
next: gsp-brand-research · .design/branding/{brand-name}/
|
|
138
|
+
```
|
|
139
|
+
|
|
120
140
|
## Step 6: Route
|
|
121
141
|
|
|
122
142
|
Use `AskUserQuestion` — always offer Continue / Stop here / What happens next:
|
|
@@ -126,4 +146,29 @@ Use `AskUserQuestion` — always offer Continue / Stop here / What happens next:
|
|
|
126
146
|
- **What happens next?** — "Explain the research phase" → explain what brand-research does (market landscape, competitive audit, trend analysis, mood board direction) and how it uses the brief
|
|
127
147
|
|
|
128
148
|
If `e2e: true`, mention that after the full branding diamond completes, it will auto-transition to project setup.
|
|
149
|
+
|
|
150
|
+
## Step 7: e2e transition (only when `e2e: true` and branding diamond is complete)
|
|
151
|
+
|
|
152
|
+
After all four brand phases complete (brand-research → brand-strategy → brand-identity → brand-guidelines), scaffold the project directory before invoking `/gsp-project-brief`:
|
|
153
|
+
|
|
154
|
+
1. Derive `{project-slug}` from the brand name: lowercase, spaces and underscores replaced with hyphens.
|
|
155
|
+
|
|
156
|
+
2. Create the project directory:
|
|
157
|
+
```bash
|
|
158
|
+
mkdir -p .design/projects/{project-slug}/
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
3. Read templates at write time from `${CLAUDE_SKILL_DIR}/../../templates/projects/` and write:
|
|
162
|
+
- `.design/projects/{project-slug}/config.json` from `config.json` template — set `project.name` (title-cased from project-slug) and `project.created` (ISO date)
|
|
163
|
+
- `.design/projects/{project-slug}/STATE.md` from `state.md` template — fill in project name and brand name
|
|
164
|
+
|
|
165
|
+
4. Write `.design/projects/{project-slug}/brand.ref` containing the brand directory name (e.g. `{brand-name}`), so the project knows which brand it belongs to.
|
|
166
|
+
|
|
167
|
+
5. Display:
|
|
168
|
+
```
|
|
169
|
+
brand complete — {brand-name}
|
|
170
|
+
now let's scope your project.
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
6. Invoke `/gsp-project-brief` via Skill tool, passing `{project-slug}` so Step 0 resolves the existing directory rather than prompting for one.
|
|
129
174
|
</process>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gsp-brand-guidelines
|
|
3
|
-
description:
|
|
3
|
+
description: Build design system tokens and STYLE.md (technical phase — benefits from capable models) — use when: create the design system, generate tokens, finalize brand guidelines, build the component system
|
|
4
4
|
user-invocable: true
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- Read
|
|
@@ -113,6 +113,7 @@ Redesign the system from the ground up, informed by what exists.
|
|
|
113
113
|
Read these files and hold their content for inlining into the agent prompt:
|
|
114
114
|
- `${CLAUDE_SKILL_DIR}/../../templates/phases/patterns.md` — patterns output template
|
|
115
115
|
- `${CLAUDE_SKILL_DIR}/design-tokens.md` — design tokens reference
|
|
116
|
+
- `${CLAUDE_SKILL_DIR}/guidelines-structure.md` — guidelines.html structure spec (shadcn tokens, sections, primitive classes)
|
|
116
117
|
- `${CLAUDE_SKILL_DIR}/methodology/gsp-brand-engineer.md` — agent methodology
|
|
117
118
|
|
|
118
119
|
Spawn the `gsp-brand-engineer` agent. **Inline all content** — the agent should not need to read input files.
|
|
@@ -120,11 +121,12 @@ Spawn the `gsp-brand-engineer` agent. **Inline all content** — the agent shoul
|
|
|
120
121
|
Pass in the agent prompt:
|
|
121
122
|
- **Content of** all identity chunks + palettes.json (loaded in Step 1)
|
|
122
123
|
- **Content of** strategy chunks: voice-and-tone.md, archetype.md, positioning.md (loaded in Step 1)
|
|
123
|
-
- **Content of** BRIEF.md (loaded in Step 1)
|
|
124
|
+
- **Content of** BRIEF.md (loaded in Step 1) — explicitly pass the `brand_heartbeat` field as a named input so the agent uses it in the hero headline if no manifesto line exists yet
|
|
124
125
|
- **Content of** style base preset `.yml` + `.md` (loaded in Step 1) — `.yml` as structural scaffold, `.md` as philosophy + implementation content for STYLE.md
|
|
125
126
|
- **Agent methodology** (loaded above)
|
|
126
127
|
- **Content of** patterns output template (loaded above)
|
|
127
128
|
- **Content of** design tokens reference (loaded above)
|
|
129
|
+
- **Content of** guidelines structure spec (loaded above) — follow this exactly for `guidelines.html`
|
|
128
130
|
- The `system_strategy` and `tech_stack` values
|
|
129
131
|
- **Output path:** `{BRAND_PATH}/patterns/`
|
|
130
132
|
|
|
@@ -136,55 +138,64 @@ Pass in the agent prompt:
|
|
|
136
138
|
>
|
|
137
139
|
> Do NOT produce component artifacts yet (token-mapping, overrides, custom specs). Those come after the user reviews the visual output.
|
|
138
140
|
|
|
139
|
-
## Step 3.5:
|
|
141
|
+
## Step 3.5: Coherence check
|
|
140
142
|
|
|
141
|
-
|
|
143
|
+
Spawn the `gsp-brand-coherence` agent with a fresh context. Load the methodology and inline all inputs — the agent should not need to read files.
|
|
142
144
|
|
|
143
|
-
|
|
145
|
+
### Load
|
|
146
|
+
Read these and hold for inlining:
|
|
147
|
+
- `${CLAUDE_SKILL_DIR}/methodology/gsp-brand-coherence.md` — agent methodology
|
|
148
|
+
- `{BRAND_PATH}/patterns/{brand-name}.yml` — generated preset
|
|
149
|
+
- `{BRAND_PATH}/patterns/guidelines.html` — generated visual guide
|
|
144
150
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
151
|
+
Extract from Step 1 context:
|
|
152
|
+
- `archetype` — from archetype.md
|
|
153
|
+
- `brand_heartbeat` — from BRIEF.md
|
|
154
|
+
|
|
155
|
+
### Spawn `gsp-brand-coherence`
|
|
148
156
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
157
|
+
Pass inline:
|
|
158
|
+
- **Agent methodology** (loaded above)
|
|
159
|
+
- **Content of** `{brand-name}.yml`
|
|
160
|
+
- **Content of** `guidelines.html`
|
|
161
|
+
- `archetype` and `brand_heartbeat`
|
|
153
162
|
|
|
154
|
-
|
|
155
|
-
patterns: {N} components defined
|
|
156
|
-
constraints: {N} never, {N} always rules
|
|
157
|
-
effects: {interaction vocabulary list}
|
|
158
|
-
bold bets: {1-line summary of boldest bet}
|
|
163
|
+
The agent returns a structured coherence report. No back-and-forth — one response.
|
|
159
164
|
|
|
160
|
-
|
|
165
|
+
### Present the report
|
|
161
166
|
|
|
167
|
+
Display the agent's report, then add:
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
→ open guidelines.html in your browser
|
|
162
171
|
─────────────────────────────────────
|
|
163
172
|
```
|
|
164
173
|
|
|
165
174
|
Use `AskUserQuestion`:
|
|
166
|
-
- **Looks
|
|
167
|
-
- **
|
|
168
|
-
- **
|
|
169
|
-
- **Adjust
|
|
175
|
+
- **Looks right** — "Coherent — build components"
|
|
176
|
+
- **Push [tension 1]** — pre-fill with the specific gap from the report
|
|
177
|
+
- **Push [tension 2]** — same
|
|
178
|
+
- **Adjust something else** — "I want to change colors / type / patterns"
|
|
170
179
|
|
|
171
|
-
If
|
|
180
|
+
If refinement needed → invoke `/gsp-brand-refine` with the specific tension. After it completes, re-spawn `gsp-brand-coherence` with the updated `.yml` and `guidelines.html`. Only proceed to Step 3.75 when the archetype tension is present and dials are coherent.
|
|
172
181
|
|
|
173
182
|
## Step 3.75: Perspective check
|
|
174
183
|
|
|
175
|
-
Load persona profiles from `{BRAND_PATH}/BRIEF.md
|
|
184
|
+
Load persona profiles and the `brand_heartbeat` from `{BRAND_PATH}/BRIEF.md`. Present stakeholder reactions framed around the compass:
|
|
176
185
|
|
|
177
|
-
|
|
186
|
+
```
|
|
187
|
+
stress-testing against: "{brand_heartbeat}"
|
|
178
188
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
189
|
+
{primary persona name}: {does this visual language make them feel that sentence?}
|
|
190
|
+
Skeptic: {does the intensity feel calibrated — or is it playing it safe?}
|
|
191
|
+
{top competitor}: {is the brand visually distinct enough to own this feeling?}
|
|
192
|
+
```
|
|
182
193
|
|
|
183
194
|
Use `AskUserQuestion`:
|
|
184
|
-
- **Lock it in** — "The brand
|
|
185
|
-
- **Adjust** — "One of these concerns resonates
|
|
195
|
+
- **Lock it in** — "The brand earns that feeling — build components"
|
|
196
|
+
- **Adjust** — "One of these concerns resonates"
|
|
186
197
|
|
|
187
|
-
If adjust →
|
|
198
|
+
If adjust → invoke `/gsp-brand-refine` with the concern, re-present. If confirmed → proceed to components.
|
|
188
199
|
|
|
189
200
|
## Step 4: Spawn brand engineer — Pass 2: Components
|
|
190
201
|
|
|
@@ -212,6 +223,14 @@ Update `{BRAND_PATH}/STATE.md`:
|
|
|
212
223
|
- Record completion date
|
|
213
224
|
- Set Prettiness Level to 100%
|
|
214
225
|
|
|
226
|
+
Update `.design/CLAUDE.md` — replace the existing `### {brand-name}` entry (written by gsp-brand-brief when started) with the completed entry:
|
|
227
|
+
|
|
228
|
+
```markdown
|
|
229
|
+
### {brand-name} · complete · {DATE}
|
|
230
|
+
"{brand_heartbeat}"
|
|
231
|
+
.design/branding/{brand-name}/patterns/ — guidelines.html · STYLE.md · {brand-name}.yml
|
|
232
|
+
```
|
|
233
|
+
|
|
215
234
|
## Step 5: Phase transition output
|
|
216
235
|
|
|
217
236
|
Invoke `/gsp-phase-transition` with phase `guidelines` and output directory `{BRAND_PATH}/patterns/`.
|
|
@@ -224,6 +243,7 @@ Also display a brand summary after the standard transition — this is the final
|
|
|
224
243
|
|
|
225
244
|
```
|
|
226
245
|
brand complete — {brand-name}
|
|
246
|
+
"{brand_heartbeat}"
|
|
227
247
|
|
|
228
248
|
discover {key finding}
|
|
229
249
|
strategy {archetype}, {positioning}, {top voice attributes}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Design Token Standards
|
|
2
2
|
|
|
3
|
+
> **GSP approach (v0.8.0+):** GSP presets use **shadcn/ui-native token names** directly — keys in `.yml` files map 1:1 to shadcn CSS variables (`background`, `foreground`, `primary`, `accent`, etc.). The W3C format below is background context on token standards in general; it does not reflect GSP's flat, shadcn-native schema. See `bin/theme-css.js` for how GSP converts `.yml` tokens to CSS.
|
|
4
|
+
|
|
3
5
|
**Format:** W3C Design Tokens Community Group specification
|
|
4
6
|
|
|
5
7
|
---
|