design-brain-memory 0.8.2 → 0.9.0

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.
Files changed (126) hide show
  1. package/dist/agentBrowser.d.ts +1 -0
  2. package/dist/agentBrowser.js +1 -0
  3. package/dist/agentBrowser.js.map +1 -1
  4. package/dist/classify.d.ts +21 -0
  5. package/dist/classify.js +205 -0
  6. package/dist/classify.js.map +1 -0
  7. package/dist/cli.js +216 -219
  8. package/dist/cli.js.map +1 -1
  9. package/dist/commands.d.ts +1 -60
  10. package/dist/commands.js +10 -322
  11. package/dist/commands.js.map +1 -1
  12. package/dist/extractFromUrl.d.ts +0 -7
  13. package/dist/extractFromUrl.js +353 -82
  14. package/dist/extractFromUrl.js.map +1 -1
  15. package/dist/index.d.ts +12 -24
  16. package/dist/index.js +11 -16
  17. package/dist/index.js.map +1 -1
  18. package/dist/llm.d.ts +6 -0
  19. package/dist/llm.js +12 -1
  20. package/dist/llm.js.map +1 -1
  21. package/dist/persona.d.ts +2 -0
  22. package/dist/persona.js +234 -0
  23. package/dist/persona.js.map +1 -0
  24. package/dist/query.js +6 -1
  25. package/dist/query.js.map +1 -1
  26. package/dist/render.d.ts +10 -2
  27. package/dist/render.js +175 -80
  28. package/dist/render.js.map +1 -1
  29. package/dist/scan.d.ts +10 -0
  30. package/dist/scan.js +393 -0
  31. package/dist/scan.js.map +1 -0
  32. package/dist/scanRenderer.d.ts +2 -0
  33. package/dist/scanRenderer.js +155 -0
  34. package/dist/scanRenderer.js.map +1 -0
  35. package/dist/skillPrompt.d.ts +3 -1
  36. package/dist/skillPrompt.js +148 -22
  37. package/dist/skillPrompt.js.map +1 -1
  38. package/dist/store.d.ts +5 -1
  39. package/dist/store.js +19 -0
  40. package/dist/store.js.map +1 -1
  41. package/dist/taste.d.ts +9 -0
  42. package/dist/taste.js +598 -0
  43. package/dist/taste.js.map +1 -0
  44. package/dist/tasteDiff.d.ts +19 -0
  45. package/dist/tasteDiff.js +340 -0
  46. package/dist/tasteDiff.js.map +1 -0
  47. package/dist/tasteGenerate.d.ts +12 -0
  48. package/dist/tasteGenerate.js +140 -0
  49. package/dist/tasteGenerate.js.map +1 -0
  50. package/dist/tasteRefine.d.ts +13 -0
  51. package/dist/tasteRefine.js +351 -0
  52. package/dist/tasteRefine.js.map +1 -0
  53. package/dist/tasteRenderer.d.ts +4 -0
  54. package/dist/tasteRenderer.js +133 -0
  55. package/dist/tasteRenderer.js.map +1 -0
  56. package/dist/theatrical.d.ts +5 -0
  57. package/dist/theatrical.js +258 -0
  58. package/dist/theatrical.js.map +1 -0
  59. package/dist/types.d.ts +196 -27
  60. package/package.json +4 -3
  61. package/skills/SKILL.md +36 -0
  62. package/skills/design-brain/SKILL.md +77 -0
  63. package/README.md +0 -242
  64. package/dist/aggregate.d.ts +0 -9
  65. package/dist/aggregate.js +0 -53
  66. package/dist/aggregate.js.map +0 -1
  67. package/dist/batch.d.ts +0 -16
  68. package/dist/batch.js +0 -44
  69. package/dist/batch.js.map +0 -1
  70. package/dist/compare.d.ts +0 -33
  71. package/dist/compare.js +0 -83
  72. package/dist/compare.js.map +0 -1
  73. package/dist/componentGraph.d.ts +0 -22
  74. package/dist/componentGraph.js +0 -106
  75. package/dist/componentGraph.js.map +0 -1
  76. package/dist/contextLayer.d.ts +0 -12
  77. package/dist/contextLayer.js +0 -263
  78. package/dist/contextLayer.js.map +0 -1
  79. package/dist/cssInJs.d.ts +0 -9
  80. package/dist/cssInJs.js +0 -124
  81. package/dist/cssInJs.js.map +0 -1
  82. package/dist/graphView.d.ts +0 -21
  83. package/dist/graphView.js +0 -492
  84. package/dist/graphView.js.map +0 -1
  85. package/dist/knowledge.d.ts +0 -20
  86. package/dist/knowledge.js +0 -208
  87. package/dist/knowledge.js.map +0 -1
  88. package/dist/liveView.d.ts +0 -15
  89. package/dist/liveView.js +0 -476
  90. package/dist/liveView.js.map +0 -1
  91. package/dist/moodboard.d.ts +0 -3
  92. package/dist/moodboard.js +0 -139
  93. package/dist/moodboard.js.map +0 -1
  94. package/dist/reviewChecklist.d.ts +0 -17
  95. package/dist/reviewChecklist.js +0 -126
  96. package/dist/reviewChecklist.js.map +0 -1
  97. package/dist/scorecard.d.ts +0 -48
  98. package/dist/scorecard.js +0 -201
  99. package/dist/scorecard.js.map +0 -1
  100. package/dist/styleDictionary.d.ts +0 -16
  101. package/dist/styleDictionary.js +0 -89
  102. package/dist/styleDictionary.js.map +0 -1
  103. package/dist/svg.d.ts +0 -5
  104. package/dist/svg.js +0 -162
  105. package/dist/svg.js.map +0 -1
  106. package/dist/systemDiff.d.ts +0 -28
  107. package/dist/systemDiff.js +0 -107
  108. package/dist/systemDiff.js.map +0 -1
  109. package/dist/tailwind.d.ts +0 -2
  110. package/dist/tailwind.js +0 -122
  111. package/dist/tailwind.js.map +0 -1
  112. package/dist/tokenNaming.d.ts +0 -5
  113. package/dist/tokenNaming.js +0 -229
  114. package/dist/tokenNaming.js.map +0 -1
  115. package/dist/tokens.d.ts +0 -17
  116. package/dist/tokens.js +0 -44
  117. package/dist/tokens.js.map +0 -1
  118. package/dist/trends.d.ts +0 -12
  119. package/dist/trends.js +0 -178
  120. package/dist/trends.js.map +0 -1
  121. package/dist/wiki.d.ts +0 -10
  122. package/dist/wiki.js +0 -346
  123. package/dist/wiki.js.map +0 -1
  124. package/dist/writingStyle.d.ts +0 -38
  125. package/dist/writingStyle.js +0 -224
  126. package/dist/writingStyle.js.map +0 -1
@@ -0,0 +1,258 @@
1
+ import path from 'node:path';
2
+ import os from 'node:os';
3
+ import fs from 'fs-extra';
4
+ import { runAgentBrowserJson } from './agentBrowser.js';
5
+ const SESSION_NAME = 'design-brain-demo';
6
+ /* ─── Highlight injection script ─── */
7
+ const HIGHLIGHT_SCRIPT = String.raw `(() => {
8
+ const style = document.createElement('style');
9
+ style.id = 'design-brain-highlights';
10
+ style.textContent = [
11
+ '@keyframes db-pulse { 0%,100% { opacity: 0.7; } 50% { opacity: 1; } }',
12
+ '.db-highlight-color { outline: 3px solid #00ff88 !important; animation: db-pulse 1s infinite; }',
13
+ '.db-highlight-typo { text-decoration: underline wavy #ff6b00 !important; text-underline-offset: 4px; }',
14
+ '.db-highlight-component { outline: 3px solid #6366f1 !important; outline-offset: 2px; box-shadow: 0 0 12px rgba(99,102,241,0.4) !important; }',
15
+ ].join('\n');
16
+ document.head.appendChild(style);
17
+
18
+ const nodes = Array.from(document.querySelectorAll('body *')).slice(0, 500);
19
+ let colorCount = 0, typoCount = 0, compCount = 0;
20
+
21
+ nodes.forEach(el => {
22
+ const s = getComputedStyle(el);
23
+ const bg = s.backgroundColor;
24
+ if (bg && bg !== 'rgba(0, 0, 0, 0)' && bg !== 'transparent' && colorCount < 15) {
25
+ el.classList.add('db-highlight-color');
26
+ colorCount++;
27
+ }
28
+ if (el.textContent?.trim().length > 3 && s.fontSize && parseInt(s.fontSize) >= 20 && typoCount < 10) {
29
+ el.classList.add('db-highlight-typo');
30
+ typoCount++;
31
+ }
32
+ const tag = el.tagName.toLowerCase();
33
+ if ((tag === 'button' || tag === 'a' || el.getAttribute('role') === 'button') && compCount < 10) {
34
+ el.classList.add('db-highlight-component');
35
+ compCount++;
36
+ }
37
+ });
38
+
39
+ return { colorCount, typoCount, compCount };
40
+ })()`;
41
+ /* ─── Cleanup script ─── */
42
+ const CLEANUP_SCRIPT = String.raw `(() => {
43
+ document.getElementById('design-brain-highlights')?.remove();
44
+ document.querySelectorAll('.db-highlight-color,.db-highlight-typo,.db-highlight-component')
45
+ .forEach(el => el.classList.remove('db-highlight-color','db-highlight-typo','db-highlight-component'));
46
+ })()`;
47
+ /* ─── Scroll script ─── */
48
+ const SCROLL_SCRIPT = String.raw `(() => {
49
+ window.scrollBy({ top: 400, behavior: 'smooth' });
50
+ return { scrollY: window.scrollY };
51
+ })()`;
52
+ /* ─── Extraction script (same as extractFromUrl.ts) ─── */
53
+ const EXTRACTION_SCRIPT = String.raw `(() => {
54
+ const maxNodes = 2000;
55
+ const nodes = Array.from(document.querySelectorAll('body *')).slice(0, maxNodes);
56
+
57
+ const colorMap = new Map();
58
+ const typographyMap = new Map();
59
+ const componentList = [];
60
+ const motionList = [];
61
+ const layoutList = [];
62
+ const variableMap = {};
63
+
64
+ const componentTags = new Set(['button', 'a', 'input', 'select', 'textarea', 'nav', 'header', 'footer', 'form']);
65
+ const maxComponents = 220;
66
+ const maxMotion = 220;
67
+
68
+ function normalizeWhitespace(text) {
69
+ return (text || '').replace(/\s+/g, ' ').trim();
70
+ }
71
+
72
+ function pushColor(value, sample) {
73
+ if (!value || value === 'transparent' || value === 'rgba(0, 0, 0, 0)') return;
74
+ const parsed = parseCssColor(value);
75
+ if (!parsed) return;
76
+ const key = parsed.toUpperCase();
77
+ const entry = colorMap.get(key) || { hex: key, count: 0, samples: [] };
78
+ entry.count += 1;
79
+ if (sample && entry.samples.length < 5 && !entry.samples.includes(sample)) {
80
+ entry.samples.push(sample);
81
+ }
82
+ colorMap.set(key, entry);
83
+ }
84
+
85
+ function parseCssColor(input) {
86
+ if (!input) return null;
87
+ const value = input.toString().trim().toLowerCase();
88
+ if (value.startsWith('#')) {
89
+ if (value.length === 4) return '#' + value[1] + value[1] + value[2] + value[2] + value[3] + value[3];
90
+ if (value.length === 7) return value;
91
+ return null;
92
+ }
93
+ const rgbMatch = value.match(/rgba?\(([^)]+)\)/);
94
+ if (rgbMatch) {
95
+ const parts = rgbMatch[1].split(',').map(p => p.trim());
96
+ const r = Number(parts[0]), g = Number(parts[1]), b = Number(parts[2]);
97
+ if ([r, g, b].some(n => Number.isNaN(n))) return null;
98
+ const toHex = n => Math.max(0, Math.min(255, n)).toString(16).padStart(2, '0');
99
+ return '#' + toHex(r) + toHex(g) + toHex(b);
100
+ }
101
+ return null;
102
+ }
103
+
104
+ function buildSelector(el) {
105
+ const tag = el.tagName.toLowerCase();
106
+ if (el.id) return tag + '#' + el.id;
107
+ if (typeof el.className === 'string' && el.className.trim()) {
108
+ const firstClass = el.className.trim().split(/\s+/).slice(0, 2).join('.');
109
+ if (firstClass) return tag + '.' + firstClass;
110
+ }
111
+ return tag;
112
+ }
113
+
114
+ function addTypography(style) {
115
+ const key = [style.fontFamily, style.fontSize, style.fontWeight, style.lineHeight].join('|');
116
+ const existing = typographyMap.get(key) || {
117
+ fontFamily: style.fontFamily, fontSize: style.fontSize,
118
+ fontWeight: style.fontWeight, lineHeight: style.lineHeight, count: 0,
119
+ };
120
+ existing.count += 1;
121
+ typographyMap.set(key, existing);
122
+ }
123
+
124
+ function shouldCaptureAsComponent(el, selector) {
125
+ const tag = el.tagName.toLowerCase();
126
+ if (componentTags.has(tag)) return true;
127
+ const className = typeof el.className === 'string' ? el.className.toLowerCase() : '';
128
+ if (className.includes('btn') || className.includes('button') || className.includes('card') || className.includes('hero')) return true;
129
+ if (el.getAttribute('role') === 'button' || el.getAttribute('role') === 'navigation') return true;
130
+ return selector.startsWith('section.') || selector.startsWith('div.card');
131
+ }
132
+
133
+ function inferComponentKind(el) {
134
+ const tag = el.tagName.toLowerCase();
135
+ const className = typeof el.className === 'string' ? el.className.toLowerCase() : '';
136
+ if (tag === 'button' || className.includes('btn')) return 'button';
137
+ if (tag === 'a') return 'link';
138
+ if (tag === 'input' || tag === 'textarea' || tag === 'select') return 'form-control';
139
+ if (tag === 'nav' || className.includes('nav')) return 'navigation';
140
+ if (className.includes('card')) return 'card';
141
+ if (tag === 'header') return 'header';
142
+ if (tag === 'footer') return 'footer';
143
+ if (className.includes('hero')) return 'hero';
144
+ return tag;
145
+ }
146
+
147
+ nodes.forEach(el => {
148
+ const style = window.getComputedStyle(el);
149
+ if (style.display === 'none' || style.visibility === 'hidden' || Number(style.opacity) === 0) return;
150
+ const selector = buildSelector(el);
151
+ pushColor(style.color, selector + ':color');
152
+ pushColor(style.backgroundColor, selector + ':background');
153
+ if (style.borderColor && style.borderColor !== 'rgba(0, 0, 0, 0)') {
154
+ pushColor(style.borderColor, selector + ':border');
155
+ }
156
+ const hasText = normalizeWhitespace(el.textContent || '').length > 0;
157
+ if (hasText) addTypography(style);
158
+ if (shouldCaptureAsComponent(el, selector) && componentList.length < maxComponents) {
159
+ componentList.push({
160
+ kind: inferComponentKind(el), tag: el.tagName.toLowerCase(), selector,
161
+ text: normalizeWhitespace(el.textContent || '').slice(0, 90),
162
+ className: typeof el.className === 'string' ? normalizeWhitespace(el.className) : '',
163
+ styles: {
164
+ color: style.color, backgroundColor: style.backgroundColor,
165
+ borderRadius: style.borderRadius, border: style.border,
166
+ padding: style.padding, margin: style.margin,
167
+ fontSize: style.fontSize, fontWeight: style.fontWeight, boxShadow: style.boxShadow,
168
+ },
169
+ });
170
+ }
171
+ const hasTransition = style.transitionDuration && style.transitionDuration !== '0s';
172
+ const hasAnimation = style.animationName && style.animationName !== 'none';
173
+ const hasTransform = style.transform && style.transform !== 'none';
174
+ if ((hasTransition || hasAnimation || hasTransform) && motionList.length < maxMotion) {
175
+ motionList.push({ selector, transition: style.transition, animation: style.animation, transform: style.transform });
176
+ }
177
+ });
178
+
179
+ const layoutSelectors = Array.from(document.querySelectorAll('header, nav, main, section, article, aside, footer')).slice(0, 160);
180
+ layoutSelectors.forEach(el => {
181
+ layoutList.push({ tag: el.tagName.toLowerCase(), selector: buildSelector(el), role: el.getAttribute('role') || '', children: el.children.length });
182
+ });
183
+
184
+ [document.documentElement, document.body].forEach(root => {
185
+ if (!root) return;
186
+ const style = window.getComputedStyle(root);
187
+ for (const propertyName of style) {
188
+ if (!propertyName.startsWith('--')) continue;
189
+ const value = style.getPropertyValue(propertyName).trim();
190
+ if (value) variableMap[propertyName] = value;
191
+ }
192
+ });
193
+
194
+ const colors = Array.from(colorMap.values()).sort((a, b) => b.count - a.count).slice(0, 70);
195
+ const typography = Array.from(typographyMap.values()).sort((a, b) => b.count - a.count).slice(0, 90);
196
+
197
+ return {
198
+ pageTitle: document.title, pageUrl: window.location.href,
199
+ viewport: { width: window.innerWidth, height: window.innerHeight },
200
+ colors, typography, components: componentList, motion: motionList,
201
+ layout: layoutList, cssVariables: variableMap,
202
+ };
203
+ })()`;
204
+ /* ─── Normalize extraction result ─── */
205
+ function normalizeResult(result) {
206
+ return {
207
+ pageTitle: result.pageTitle ?? undefined,
208
+ pageUrl: result.pageUrl ?? undefined,
209
+ viewport: result.viewport ?? undefined,
210
+ colors: result.colors ?? [],
211
+ typography: result.typography ?? [],
212
+ components: result.components ?? [],
213
+ motion: result.motion ?? [],
214
+ layout: result.layout ?? [],
215
+ cssVariables: result.cssVariables ?? {},
216
+ };
217
+ }
218
+ /* ─── Theatrical scan sequence ─── */
219
+ export async function theatricalScan(url) {
220
+ const screenshotDir = path.join(os.tmpdir(), 'design-brain-theatrical');
221
+ await fs.ensureDir(screenshotDir);
222
+ const screenshotPath = path.join(screenshotDir, `scan-${Date.now()}.png`);
223
+ const browserOpts = { session: SESSION_NAME, headed: true };
224
+ // 1. Open the URL in a visible browser
225
+ await runAgentBrowserJson(['open', url], browserOpts);
226
+ try {
227
+ // 2. Wait for page to settle
228
+ await runAgentBrowserJson(['wait', '1500'], browserOpts);
229
+ // 3. Slow scroll — 4 steps, ~600ms between each
230
+ for (let i = 0; i < 4; i++) {
231
+ await runAgentBrowserJson(['eval', SCROLL_SCRIPT], browserOpts);
232
+ await runAgentBrowserJson(['wait', '600'], browserOpts);
233
+ }
234
+ // Scroll back to top for highlight injection
235
+ await runAgentBrowserJson(['eval', 'window.scrollTo({ top: 0, behavior: "smooth" })'], browserOpts);
236
+ await runAgentBrowserJson(['wait', '800'], browserOpts);
237
+ // 4. Inject highlight overlays
238
+ await runAgentBrowserJson(['eval', HIGHLIGHT_SCRIPT], browserOpts);
239
+ // 5. Pause for visual impact
240
+ await runAgentBrowserJson(['wait', '2000'], browserOpts);
241
+ // 6. Take screenshot with overlays visible
242
+ await runAgentBrowserJson(['screenshot', screenshotPath, '--full'], browserOpts);
243
+ // 7. Clean overlays
244
+ await runAgentBrowserJson(['eval', CLEANUP_SCRIPT], browserOpts);
245
+ await runAgentBrowserJson(['wait', '300'], browserOpts);
246
+ // 8. Extract design tokens (real extraction on clean page)
247
+ const extraction = await runAgentBrowserJson(['eval', EXTRACTION_SCRIPT], browserOpts);
248
+ if (!extraction.success) {
249
+ throw new Error(`Extraction failed: ${extraction.error ?? 'Unknown error'}`);
250
+ }
251
+ const analysis = normalizeResult(extraction.data.result ?? {});
252
+ return { analysis, screenshotPath };
253
+ }
254
+ finally {
255
+ await runAgentBrowserJson(['close'], browserOpts).catch(() => undefined);
256
+ }
257
+ }
258
+ //# sourceMappingURL=theatrical.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theatrical.js","sourceRoot":"","sources":["../src/theatrical.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -18,37 +18,68 @@ export interface ComponentToken {
18
18
  text: string;
19
19
  className: string;
20
20
  styles: Record<string, string>;
21
- html?: string;
22
21
  }
23
- export interface TransitionDetail {
24
- property: string;
25
- duration: string;
26
- timingFunction: string;
27
- delay: string;
22
+ export type AnimationLibrary = 'css' | 'gsap' | 'lottie' | 'framer-motion' | 'react-spring' | 'motion-one' | 'unknown';
23
+ export type MotionIntent = 'fade' | 'slide' | 'scale' | 'rotate' | 'color-shift' | 'spring' | 'bounce' | 'morph' | 'reveal' | 'parallax' | 'complex';
24
+ export type TriggerEvent = 'load' | 'hover' | 'focus' | 'click' | 'scroll' | 'viewport-enter' | 'media-query' | 'unknown';
25
+ export interface KeyframeStop {
26
+ offset: number;
27
+ properties: Record<string, string>;
28
+ easing?: string;
28
29
  }
29
- export interface AnimationDetail {
30
- name: string;
31
- duration: string;
32
- timingFunction: string;
33
- delay: string;
34
- iterationCount: string;
35
- direction: string;
36
- fillMode: string;
37
- }
38
- export interface KeyframeRule {
39
- name: string;
40
- steps: Array<{
41
- offset: string;
42
- declarations: Record<string, string>;
43
- }>;
30
+ export interface AnimationTiming {
31
+ duration: number;
32
+ delay: number;
33
+ easing: string;
34
+ iterations: number;
35
+ direction: 'normal' | 'reverse' | 'alternate' | 'alternate-reverse';
36
+ fillMode: 'none' | 'forwards' | 'backwards' | 'both';
37
+ }
38
+ export interface ScrollBinding {
39
+ triggerSelector?: string;
40
+ hasScrollTrigger: boolean;
41
+ hasIntersectionObserver: boolean;
42
+ scrollTimelineAxis?: 'block' | 'inline';
43
+ }
44
+ export interface PhysicsParams {
45
+ type: 'spring' | 'bounce' | 'inertia';
46
+ mass?: number;
47
+ stiffness?: number;
48
+ damping?: number;
49
+ oscillationCount?: number;
50
+ overshootPercent?: number;
51
+ }
52
+ export interface AnimationGroup {
53
+ groupId: string;
54
+ role: 'lead' | 'follower';
55
+ staggerDelay?: number;
44
56
  }
57
+ export interface AnimationToken {
58
+ selector: string;
59
+ library: AnimationLibrary;
60
+ motionIntent: MotionIntent;
61
+ timing?: AnimationTiming;
62
+ keyframes?: KeyframeStop[];
63
+ trigger: TriggerEvent;
64
+ scrollBinding?: ScrollBinding;
65
+ physics?: PhysicsParams;
66
+ group?: AnimationGroup;
67
+ gsapVars?: Record<string, unknown>;
68
+ lottieMetadata?: {
69
+ frameRate: number;
70
+ totalFrames: number;
71
+ duration: number;
72
+ };
73
+ rawTransition?: string;
74
+ rawAnimation?: string;
75
+ rawTransform?: string;
76
+ }
77
+ /** @deprecated Use AnimationToken instead */
45
78
  export interface MotionToken {
46
79
  selector: string;
47
80
  transition: string;
48
81
  animation: string;
49
82
  transform: string;
50
- transitions?: TransitionDetail[];
51
- animations?: AnimationDetail[];
52
83
  }
53
84
  export interface LayoutToken {
54
85
  tag: string;
@@ -91,8 +122,7 @@ export interface DesignAnalysis {
91
122
  colors: ColorToken[];
92
123
  typography: TypographyToken[];
93
124
  components: ComponentToken[];
94
- motion: MotionToken[];
95
- keyframes?: KeyframeRule[];
125
+ motion: (MotionToken | AnimationToken)[];
96
126
  layout: LayoutToken[];
97
127
  cssVariables: Record<string, string>;
98
128
  accessibilitySnapshot?: string;
@@ -188,8 +218,6 @@ export interface IngestOptions {
188
218
  width: number;
189
219
  height: number;
190
220
  }>;
191
- skipVisuals?: boolean;
192
- live?: boolean;
193
221
  }
194
222
  export interface OutcomeOptions {
195
223
  rootDir: string;
@@ -200,3 +228,144 @@ export interface OutcomeOptions {
200
228
  artifactUrl?: string;
201
229
  tags: string[];
202
230
  }
231
+ export interface ScanTokens {
232
+ colors: string[];
233
+ fontFamilies: string[];
234
+ fontSizes: string[];
235
+ transitions: string[];
236
+ spacingValues: string[];
237
+ cssVariableCount: number;
238
+ framework: string | null;
239
+ }
240
+ export interface ScanScore {
241
+ colorDiscipline: number;
242
+ typographySystem: number;
243
+ spacingLayout: number;
244
+ motionPolish: number;
245
+ total: number;
246
+ }
247
+ export interface PersonaMatch {
248
+ name: string;
249
+ tagline: string;
250
+ score: number;
251
+ reasoning: string;
252
+ }
253
+ export interface ScanResult {
254
+ path: string;
255
+ filesScanned: number;
256
+ tokens: ScanTokens;
257
+ score: ScanScore;
258
+ persona: PersonaMatch;
259
+ framework: string | null;
260
+ }
261
+ export interface AgentInfo {
262
+ name: string;
263
+ configDir: string;
264
+ skillDir: string;
265
+ }
266
+ export interface InstallResult {
267
+ agent: AgentInfo;
268
+ success: boolean;
269
+ error?: string;
270
+ }
271
+ export interface TasteColorPreference {
272
+ palette: Array<{
273
+ hex: string;
274
+ role: string;
275
+ source: string;
276
+ }>;
277
+ harmony: string;
278
+ hueRange: {
279
+ min: number;
280
+ max: number;
281
+ };
282
+ saturationBias: 'muted' | 'vibrant' | 'neutral';
283
+ lightnessBias: 'light' | 'dark' | 'balanced';
284
+ }
285
+ export interface TasteTypographyPreference {
286
+ primaryFont: string;
287
+ secondaryFont: string | null;
288
+ scaleType: string;
289
+ sizes: string[];
290
+ weightRange: {
291
+ min: string;
292
+ max: string;
293
+ };
294
+ }
295
+ export interface TasteSpacingPreference {
296
+ baseUnit: number;
297
+ scale: number[];
298
+ gridAlignmentRatio: number;
299
+ }
300
+ export interface TasteMotionPreference {
301
+ easing: string;
302
+ durations: string[];
303
+ intensity: 'none' | 'subtle' | 'moderate' | 'expressive';
304
+ }
305
+ export interface TasteComponentPreference {
306
+ cherryPicks: ComponentCherryPick[];
307
+ borderRadius: string;
308
+ shadowStyle: 'none' | 'subtle' | 'elevated' | 'dramatic';
309
+ }
310
+ export interface ComponentCherryPick {
311
+ componentKind: string;
312
+ sourceInspirationId: string;
313
+ sourceUrl: string;
314
+ tokens: ComponentToken[];
315
+ styles: Record<string, string>;
316
+ note?: string;
317
+ }
318
+ export interface TasteDecision {
319
+ id: string;
320
+ question: string;
321
+ answer: string;
322
+ dimension: 'color' | 'typography' | 'spacing' | 'motion' | 'component' | 'layout' | 'general';
323
+ decidedAt: string;
324
+ }
325
+ export interface TasteConflict {
326
+ dimension: string;
327
+ description: string;
328
+ options: string[];
329
+ resolved: boolean;
330
+ resolvedByDecisionId?: string;
331
+ }
332
+ export interface TasteProfile {
333
+ id: string;
334
+ name: string;
335
+ version: number;
336
+ sourceInspirationIds: string[];
337
+ sourceUrls: string[];
338
+ persona: PersonaMatch;
339
+ aggregateScore: ScanScore;
340
+ color: TasteColorPreference;
341
+ typography: TasteTypographyPreference;
342
+ spacing: TasteSpacingPreference;
343
+ motion: TasteMotionPreference;
344
+ components: TasteComponentPreference;
345
+ decisions: TasteDecision[];
346
+ conflicts: TasteConflict[];
347
+ narrative?: string;
348
+ principles?: string[];
349
+ createdAt: string;
350
+ updatedAt: string;
351
+ }
352
+ export interface TasteDiffResult {
353
+ alignment: number;
354
+ dimensions: {
355
+ color: TasteDimensionDiff;
356
+ typography: TasteDimensionDiff;
357
+ spacing: TasteDimensionDiff;
358
+ motion: TasteDimensionDiff;
359
+ };
360
+ deltas: TasteDelta[];
361
+ }
362
+ export interface TasteDimensionDiff {
363
+ alignment: number;
364
+ summary: string;
365
+ }
366
+ export interface TasteDelta {
367
+ dimension: string;
368
+ issue: string;
369
+ suggestion: string;
370
+ severity: 'info' | 'warning' | 'mismatch';
371
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "design-brain-memory",
3
- "version": "0.8.2",
3
+ "version": "0.9.0",
4
4
  "description": "Relational markdown design memory powered by Agent Browser CLI",
5
5
  "repository": {
6
6
  "type": "git",
@@ -13,10 +13,11 @@
13
13
  "type": "module",
14
14
  "main": "./dist/index.js",
15
15
  "bin": {
16
- "design-brain-memory": "dist/cli.js"
16
+ "design-brain-memory": "./dist/cli.js"
17
17
  },
18
18
  "files": [
19
- "dist"
19
+ "dist",
20
+ "skills"
20
21
  ],
21
22
  "scripts": {
22
23
  "build": "tsc -p tsconfig.json",
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: design-brain
3
+ description: Design system awareness for AI coding agents. Provides design token guidance, component patterns, and design principles extracted from your codebase.
4
+ ---
5
+
6
+ # Design Brain Skill
7
+
8
+ You have access to design system knowledge extracted by design-brain-memory.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - When writing UI components, reference the color palette and typography system
13
+ - When adding spacing/layout, follow the established spacing scale
14
+ - When creating new components, follow existing component patterns
15
+ - When adding transitions/animations, match existing motion patterns
16
+
17
+ ## Design Principles
18
+
19
+ 1. **Consistency** — Use existing design tokens rather than ad-hoc values
20
+ 2. **Systematic spacing** — Prefer multiples of 4px (4, 8, 12, 16, 24, 32, 48, 64)
21
+ 3. **Color discipline** — Use palette colors from CSS variables or design tokens
22
+ 4. **Typography scale** — Stick to the established font size scale
23
+ 5. **Motion restraint** — Use consistent transition timing across the UI
24
+
25
+ ## Quick Reference
26
+
27
+ - Run `npx design-brain-memory scan` to see your design health score
28
+ - Run `npx design-brain-memory scan .` to scan the current directory
29
+ - Design tokens are extracted from CSS, SCSS, and component files
30
+
31
+ ## Tips for Code Generation
32
+
33
+ - Prefer CSS custom properties (--var-name) over hardcoded values
34
+ - Use the project's existing color palette — don't introduce new colors without reason
35
+ - Match existing component patterns before creating new abstractions
36
+ - Keep transition durations consistent (prefer 150ms, 200ms, or 300ms)
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: design-brain
3
+ description: Build and query a relational markdown design memory using design-brain-memory and Agent Browser CLI.
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # Design Brain
8
+
9
+ Capture inspiration from websites/screenshots and turn it into a searchable design memory.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ design-brain-memory install-skill
15
+ ```
16
+
17
+ or
18
+
19
+ ```bash
20
+ npx -y skills add design-brain/design-brain
21
+ ```
22
+
23
+ ## Workflow
24
+
25
+ 1. Initialize:
26
+
27
+ ```bash
28
+ design-brain-memory init --root <workspace>
29
+ ```
30
+
31
+ 2. Ingest inspiration:
32
+
33
+ ```bash
34
+ design-brain-memory ingest --project <id> --url <url> --name "<name>" --tags <tags>
35
+ ```
36
+
37
+ or
38
+
39
+ ```bash
40
+ design-brain-memory ingest --project <id> --screenshot <path> --name "<name>" --tags <tags>
41
+ ```
42
+
43
+ 3. Record outcome:
44
+
45
+ ```bash
46
+ design-brain-memory outcome --project <id> --title "<title>" --description "<what was built>" --inspired-by <inspiration-id>
47
+ ```
48
+
49
+ 4. Query:
50
+
51
+ ```bash
52
+ design-brain-memory search --query "<keywords>"
53
+ ```
54
+
55
+ ```bash
56
+ design-brain-memory ask --query "<question>" --llm-base-url <url> --llm-api-key <key> --llm-model <model>
57
+ ```
58
+
59
+ ## Taste Profile Context
60
+
61
+ Before generating UI code, check for a taste profile:
62
+
63
+ ```bash
64
+ cat .design-brain/taste/*.json 2>/dev/null
65
+ ```
66
+
67
+ If a taste profile exists, all generated UI code should use:
68
+ - Colors from the palette (exact hex values)
69
+ - The specified font families
70
+ - Spacing values from the scale
71
+ - Motion parameters (durations, easing)
72
+ - Cherry-picked component styles where applicable
73
+
74
+ Commands:
75
+ - `design-brain-memory taste show --project <id> --json` to read taste JSON
76
+ - `design-brain-memory taste score . --project <id>` to check alignment
77
+ - `design-brain-memory taste generate --target tokens --project <id>` to emit CSS variables