@brandsystem/mcp 0.3.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 (223) hide show
  1. package/README.md +515 -0
  2. package/bin/brandsystem-mcp.mjs +2 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +20 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/brand-dir.d.ts +56 -0
  8. package/dist/lib/brand-dir.d.ts.map +1 -0
  9. package/dist/lib/brand-dir.js +270 -0
  10. package/dist/lib/brand-dir.js.map +1 -0
  11. package/dist/lib/color-namer.d.ts +28 -0
  12. package/dist/lib/color-namer.d.ts.map +1 -0
  13. package/dist/lib/color-namer.js +155 -0
  14. package/dist/lib/color-namer.js.map +1 -0
  15. package/dist/lib/confidence.d.ts +19 -0
  16. package/dist/lib/confidence.d.ts.map +1 -0
  17. package/dist/lib/confidence.js +66 -0
  18. package/dist/lib/confidence.js.map +1 -0
  19. package/dist/lib/content-scorer.d.ts +38 -0
  20. package/dist/lib/content-scorer.d.ts.map +1 -0
  21. package/dist/lib/content-scorer.js +571 -0
  22. package/dist/lib/content-scorer.js.map +1 -0
  23. package/dist/lib/css-parser.d.ts +45 -0
  24. package/dist/lib/css-parser.d.ts.map +1 -0
  25. package/dist/lib/css-parser.js +330 -0
  26. package/dist/lib/css-parser.js.map +1 -0
  27. package/dist/lib/dtcg-compiler.d.ts +7 -0
  28. package/dist/lib/dtcg-compiler.d.ts.map +1 -0
  29. package/dist/lib/dtcg-compiler.js +89 -0
  30. package/dist/lib/dtcg-compiler.js.map +1 -0
  31. package/dist/lib/interaction-policy-compiler.d.ts +40 -0
  32. package/dist/lib/interaction-policy-compiler.d.ts.map +1 -0
  33. package/dist/lib/interaction-policy-compiler.js +60 -0
  34. package/dist/lib/interaction-policy-compiler.js.map +1 -0
  35. package/dist/lib/logo-extractor.d.ts +49 -0
  36. package/dist/lib/logo-extractor.d.ts.map +1 -0
  37. package/dist/lib/logo-extractor.js +384 -0
  38. package/dist/lib/logo-extractor.js.map +1 -0
  39. package/dist/lib/report-html.d.ts +20 -0
  40. package/dist/lib/report-html.d.ts.map +1 -0
  41. package/dist/lib/report-html.js +938 -0
  42. package/dist/lib/report-html.js.map +1 -0
  43. package/dist/lib/response.d.ts +20 -0
  44. package/dist/lib/response.d.ts.map +1 -0
  45. package/dist/lib/response.js +54 -0
  46. package/dist/lib/response.js.map +1 -0
  47. package/dist/lib/runtime-compiler.d.ts +60 -0
  48. package/dist/lib/runtime-compiler.d.ts.map +1 -0
  49. package/dist/lib/runtime-compiler.js +96 -0
  50. package/dist/lib/runtime-compiler.js.map +1 -0
  51. package/dist/lib/svg-resolver.d.ts +21 -0
  52. package/dist/lib/svg-resolver.d.ts.map +1 -0
  53. package/dist/lib/svg-resolver.js +115 -0
  54. package/dist/lib/svg-resolver.js.map +1 -0
  55. package/dist/lib/url-validator.d.ts +11 -0
  56. package/dist/lib/url-validator.d.ts.map +1 -0
  57. package/dist/lib/url-validator.js +93 -0
  58. package/dist/lib/url-validator.js.map +1 -0
  59. package/dist/lib/version.d.ts +2 -0
  60. package/dist/lib/version.d.ts.map +1 -0
  61. package/dist/lib/version.js +19 -0
  62. package/dist/lib/version.js.map +1 -0
  63. package/dist/lib/vim-generator.d.ts +13 -0
  64. package/dist/lib/vim-generator.d.ts.map +1 -0
  65. package/dist/lib/vim-generator.js +718 -0
  66. package/dist/lib/vim-generator.js.map +1 -0
  67. package/dist/resources/brand-resources.d.ts +4 -0
  68. package/dist/resources/brand-resources.d.ts.map +1 -0
  69. package/dist/resources/brand-resources.js +34 -0
  70. package/dist/resources/brand-resources.js.map +1 -0
  71. package/dist/schemas/brand-config.d.ts +28 -0
  72. package/dist/schemas/brand-config.d.ts.map +1 -0
  73. package/dist/schemas/brand-config.js +11 -0
  74. package/dist/schemas/brand-config.js.map +1 -0
  75. package/dist/schemas/brand-runtime.d.ts +251 -0
  76. package/dist/schemas/brand-runtime.d.ts.map +1 -0
  77. package/dist/schemas/brand-runtime.js +54 -0
  78. package/dist/schemas/brand-runtime.js.map +1 -0
  79. package/dist/schemas/core-identity.d.ts +302 -0
  80. package/dist/schemas/core-identity.d.ts.map +1 -0
  81. package/dist/schemas/core-identity.js +51 -0
  82. package/dist/schemas/core-identity.js.map +1 -0
  83. package/dist/schemas/index.d.ts +11 -0
  84. package/dist/schemas/index.d.ts.map +1 -0
  85. package/dist/schemas/index.js +11 -0
  86. package/dist/schemas/index.js.map +1 -0
  87. package/dist/schemas/interaction-policy.d.ts +150 -0
  88. package/dist/schemas/interaction-policy.d.ts.map +1 -0
  89. package/dist/schemas/interaction-policy.js +34 -0
  90. package/dist/schemas/interaction-policy.js.map +1 -0
  91. package/dist/schemas/messaging.d.ts +776 -0
  92. package/dist/schemas/messaging.d.ts.map +1 -0
  93. package/dist/schemas/messaging.js +68 -0
  94. package/dist/schemas/messaging.js.map +1 -0
  95. package/dist/schemas/needs-clarification.d.ts +62 -0
  96. package/dist/schemas/needs-clarification.d.ts.map +1 -0
  97. package/dist/schemas/needs-clarification.js +13 -0
  98. package/dist/schemas/needs-clarification.js.map +1 -0
  99. package/dist/schemas/strategy.d.ts +537 -0
  100. package/dist/schemas/strategy.d.ts.map +1 -0
  101. package/dist/schemas/strategy.js +71 -0
  102. package/dist/schemas/strategy.js.map +1 -0
  103. package/dist/schemas/tokens.d.ts +35 -0
  104. package/dist/schemas/tokens.d.ts.map +1 -0
  105. package/dist/schemas/tokens.js +15 -0
  106. package/dist/schemas/tokens.js.map +1 -0
  107. package/dist/schemas/visual-identity.d.ts +224 -0
  108. package/dist/schemas/visual-identity.d.ts.map +1 -0
  109. package/dist/schemas/visual-identity.js +42 -0
  110. package/dist/schemas/visual-identity.js.map +1 -0
  111. package/dist/server.d.ts +3 -0
  112. package/dist/server.d.ts.map +1 -0
  113. package/dist/server.js +75 -0
  114. package/dist/server.js.map +1 -0
  115. package/dist/tools/brand-audit-content.d.ts +3 -0
  116. package/dist/tools/brand-audit-content.d.ts.map +1 -0
  117. package/dist/tools/brand-audit-content.js +116 -0
  118. package/dist/tools/brand-audit-content.js.map +1 -0
  119. package/dist/tools/brand-audit-drift.d.ts +3 -0
  120. package/dist/tools/brand-audit-drift.d.ts.map +1 -0
  121. package/dist/tools/brand-audit-drift.js +301 -0
  122. package/dist/tools/brand-audit-drift.js.map +1 -0
  123. package/dist/tools/brand-audit.d.ts +3 -0
  124. package/dist/tools/brand-audit.d.ts.map +1 -0
  125. package/dist/tools/brand-audit.js +129 -0
  126. package/dist/tools/brand-audit.js.map +1 -0
  127. package/dist/tools/brand-build-journey.d.ts +3 -0
  128. package/dist/tools/brand-build-journey.d.ts.map +1 -0
  129. package/dist/tools/brand-build-journey.js +312 -0
  130. package/dist/tools/brand-build-journey.js.map +1 -0
  131. package/dist/tools/brand-build-matrix.d.ts +3 -0
  132. package/dist/tools/brand-build-matrix.d.ts.map +1 -0
  133. package/dist/tools/brand-build-matrix.js +525 -0
  134. package/dist/tools/brand-build-matrix.js.map +1 -0
  135. package/dist/tools/brand-build-personas.d.ts +3 -0
  136. package/dist/tools/brand-build-personas.d.ts.map +1 -0
  137. package/dist/tools/brand-build-personas.js +436 -0
  138. package/dist/tools/brand-build-personas.js.map +1 -0
  139. package/dist/tools/brand-build-themes.d.ts +3 -0
  140. package/dist/tools/brand-build-themes.d.ts.map +1 -0
  141. package/dist/tools/brand-build-themes.js +476 -0
  142. package/dist/tools/brand-build-themes.js.map +1 -0
  143. package/dist/tools/brand-check-compliance.d.ts +3 -0
  144. package/dist/tools/brand-check-compliance.d.ts.map +1 -0
  145. package/dist/tools/brand-check-compliance.js +243 -0
  146. package/dist/tools/brand-check-compliance.js.map +1 -0
  147. package/dist/tools/brand-clarify.d.ts +21 -0
  148. package/dist/tools/brand-clarify.d.ts.map +1 -0
  149. package/dist/tools/brand-clarify.js +497 -0
  150. package/dist/tools/brand-clarify.js.map +1 -0
  151. package/dist/tools/brand-compile-messaging.d.ts +3 -0
  152. package/dist/tools/brand-compile-messaging.d.ts.map +1 -0
  153. package/dist/tools/brand-compile-messaging.js +759 -0
  154. package/dist/tools/brand-compile-messaging.js.map +1 -0
  155. package/dist/tools/brand-compile.d.ts +3 -0
  156. package/dist/tools/brand-compile.d.ts.map +1 -0
  157. package/dist/tools/brand-compile.js +182 -0
  158. package/dist/tools/brand-compile.js.map +1 -0
  159. package/dist/tools/brand-deepen-identity.d.ts +3 -0
  160. package/dist/tools/brand-deepen-identity.d.ts.map +1 -0
  161. package/dist/tools/brand-deepen-identity.js +483 -0
  162. package/dist/tools/brand-deepen-identity.js.map +1 -0
  163. package/dist/tools/brand-export.d.ts +17 -0
  164. package/dist/tools/brand-export.d.ts.map +1 -0
  165. package/dist/tools/brand-export.js +730 -0
  166. package/dist/tools/brand-export.js.map +1 -0
  167. package/dist/tools/brand-extract-figma.d.ts +3 -0
  168. package/dist/tools/brand-extract-figma.d.ts.map +1 -0
  169. package/dist/tools/brand-extract-figma.js +174 -0
  170. package/dist/tools/brand-extract-figma.js.map +1 -0
  171. package/dist/tools/brand-extract-messaging.d.ts +3 -0
  172. package/dist/tools/brand-extract-messaging.d.ts.map +1 -0
  173. package/dist/tools/brand-extract-messaging.js +620 -0
  174. package/dist/tools/brand-extract-messaging.js.map +1 -0
  175. package/dist/tools/brand-extract-web.d.ts +3 -0
  176. package/dist/tools/brand-extract-web.d.ts.map +1 -0
  177. package/dist/tools/brand-extract-web.js +477 -0
  178. package/dist/tools/brand-extract-web.js.map +1 -0
  179. package/dist/tools/brand-feedback.d.ts +3 -0
  180. package/dist/tools/brand-feedback.d.ts.map +1 -0
  181. package/dist/tools/brand-feedback.js +366 -0
  182. package/dist/tools/brand-feedback.js.map +1 -0
  183. package/dist/tools/brand-ingest-assets.d.ts +3 -0
  184. package/dist/tools/brand-ingest-assets.d.ts.map +1 -0
  185. package/dist/tools/brand-ingest-assets.js +233 -0
  186. package/dist/tools/brand-ingest-assets.js.map +1 -0
  187. package/dist/tools/brand-init.d.ts +3 -0
  188. package/dist/tools/brand-init.d.ts.map +1 -0
  189. package/dist/tools/brand-init.js +66 -0
  190. package/dist/tools/brand-init.js.map +1 -0
  191. package/dist/tools/brand-preflight.d.ts +3 -0
  192. package/dist/tools/brand-preflight.d.ts.map +1 -0
  193. package/dist/tools/brand-preflight.js +608 -0
  194. package/dist/tools/brand-preflight.js.map +1 -0
  195. package/dist/tools/brand-report.d.ts +3 -0
  196. package/dist/tools/brand-report.d.ts.map +1 -0
  197. package/dist/tools/brand-report.js +154 -0
  198. package/dist/tools/brand-report.js.map +1 -0
  199. package/dist/tools/brand-runtime.d.ts +3 -0
  200. package/dist/tools/brand-runtime.d.ts.map +1 -0
  201. package/dist/tools/brand-runtime.js +37 -0
  202. package/dist/tools/brand-runtime.js.map +1 -0
  203. package/dist/tools/brand-set-logo.d.ts +3 -0
  204. package/dist/tools/brand-set-logo.d.ts.map +1 -0
  205. package/dist/tools/brand-set-logo.js +170 -0
  206. package/dist/tools/brand-set-logo.js.map +1 -0
  207. package/dist/tools/brand-start.d.ts +3 -0
  208. package/dist/tools/brand-start.d.ts.map +1 -0
  209. package/dist/tools/brand-start.js +686 -0
  210. package/dist/tools/brand-start.js.map +1 -0
  211. package/dist/tools/brand-status.d.ts +3 -0
  212. package/dist/tools/brand-status.d.ts.map +1 -0
  213. package/dist/tools/brand-status.js +175 -0
  214. package/dist/tools/brand-status.js.map +1 -0
  215. package/dist/tools/brand-write.d.ts +3 -0
  216. package/dist/tools/brand-write.d.ts.map +1 -0
  217. package/dist/tools/brand-write.js +442 -0
  218. package/dist/tools/brand-write.js.map +1 -0
  219. package/dist/types/index.d.ts +331 -0
  220. package/dist/types/index.d.ts.map +1 -0
  221. package/dist/types/index.js +52 -0
  222. package/dist/types/index.js.map +1 -0
  223. package/package.json +60 -0
@@ -0,0 +1,730 @@
1
+ import { z } from "zod";
2
+ import { mkdir } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { BrandDir } from "../lib/brand-dir.js";
5
+ import { buildResponse, safeParseParams } from "../lib/response.js";
6
+ import { ERROR_CODES } from "../types/index.js";
7
+ import { cleanColorName } from "../lib/color-namer.js";
8
+ async function loadBrandData(brandDir) {
9
+ const config = await brandDir.readConfig();
10
+ let identity;
11
+ try {
12
+ identity = await brandDir.readCoreIdentity();
13
+ }
14
+ catch {
15
+ identity = { schema_version: "0.1.0", colors: [], typography: [], logo: [], spacing: null };
16
+ }
17
+ let visual = null;
18
+ if (await brandDir.hasVisualIdentity()) {
19
+ try {
20
+ visual = await brandDir.readVisualIdentity();
21
+ }
22
+ catch {
23
+ // Degrade gracefully
24
+ }
25
+ }
26
+ let messaging = null;
27
+ if (await brandDir.hasMessaging()) {
28
+ try {
29
+ messaging = await brandDir.readMessaging();
30
+ }
31
+ catch {
32
+ // Degrade gracefully
33
+ }
34
+ }
35
+ return { config, identity, visual, messaging };
36
+ }
37
+ // ---------------------------------------------------------------------------
38
+ // Shared helpers
39
+ // ---------------------------------------------------------------------------
40
+ function colorTable(colors) {
41
+ if (colors.length === 0)
42
+ return "*No colors extracted yet.*\n";
43
+ const lines = [];
44
+ lines.push("| Name | Hex | Role |");
45
+ lines.push("|------|-----|------|");
46
+ for (const c of colors) {
47
+ lines.push(`| ${cleanColorName(c)} | \`${c.value}\` | ${c.role} |`);
48
+ }
49
+ return lines.join("\n") + "\n";
50
+ }
51
+ function typographyList(typo) {
52
+ if (typo.length === 0)
53
+ return "*No typography extracted yet.*\n";
54
+ return typo
55
+ .map((t) => {
56
+ const parts = [`**${t.name}**: \`${t.family}\``];
57
+ if (t.weight)
58
+ parts.push(`weight ${t.weight}`);
59
+ if (t.size)
60
+ parts.push(`size ${t.size}`);
61
+ return `- ${parts.join(", ")}`;
62
+ })
63
+ .join("\n") + "\n";
64
+ }
65
+ function logoBlock(identity, includeLogo) {
66
+ if (!includeLogo || identity.logo.length === 0)
67
+ return "";
68
+ const lines = ["## Logo\n"];
69
+ for (const logo of identity.logo) {
70
+ lines.push(`**Type**: ${logo.type}\n`);
71
+ for (const v of logo.variants) {
72
+ if (v.inline_svg) {
73
+ lines.push(`### ${v.name} variant\n`);
74
+ lines.push("```svg");
75
+ lines.push(v.inline_svg.trim());
76
+ lines.push("```\n");
77
+ }
78
+ if (v.data_uri) {
79
+ lines.push("Data URI for `<img>` tags:\n");
80
+ lines.push("```");
81
+ lines.push(v.data_uri);
82
+ lines.push("```\n");
83
+ }
84
+ }
85
+ }
86
+ return lines.join("\n") + "\n";
87
+ }
88
+ function antiPatternsSection(visual, style) {
89
+ if (!visual || visual.anti_patterns.length === 0)
90
+ return "";
91
+ const lines = [];
92
+ if (style === "hard-rules") {
93
+ lines.push("## HARD RULES (Anti-Patterns)\n");
94
+ lines.push("These are absolute constraints. Never violate them.\n");
95
+ for (const ap of visual.anti_patterns) {
96
+ const severity = ap.severity === "hard" ? "NEVER" : "AVOID";
97
+ lines.push(`- **${severity}**: ${ap.rule}`);
98
+ }
99
+ }
100
+ else {
101
+ lines.push("## What NOT To Do\n");
102
+ for (const ap of visual.anti_patterns) {
103
+ const prefix = ap.severity === "hard" ? "Never" : "Try to avoid";
104
+ lines.push(`- ${prefix}: ${ap.rule}`);
105
+ }
106
+ }
107
+ return lines.join("\n") + "\n";
108
+ }
109
+ function compositionSection(visual) {
110
+ if (!visual?.composition)
111
+ return "";
112
+ const c = visual.composition;
113
+ const lines = [
114
+ "## Composition Rules\n",
115
+ `- **Energy**: ${c.energy}`,
116
+ `- **Negative Space**: ${c.negative_space}`,
117
+ `- **Grid**: ${c.grid}`,
118
+ `- **Layout Preference**: ${c.layout_preference}`,
119
+ ];
120
+ return lines.join("\n") + "\n";
121
+ }
122
+ function signatureSection(visual) {
123
+ if (!visual?.signature)
124
+ return "";
125
+ const s = visual.signature;
126
+ const lines = [
127
+ "## Signature Moves\n",
128
+ s.description + "\n",
129
+ ...s.elements.map((e) => `- ${e}`),
130
+ ];
131
+ return lines.join("\n") + "\n";
132
+ }
133
+ function voiceSection(messaging) {
134
+ if (!messaging?.voice)
135
+ return "";
136
+ const v = messaging.voice;
137
+ const lines = ["## Voice\n"];
138
+ // Tone
139
+ lines.push(`**Tone**: ${v.tone.descriptors.join(", ")}`);
140
+ lines.push(`**Register**: ${v.tone.register}`);
141
+ lines.push(`**Never sounds like**: ${v.tone.never_sounds_like}\n`);
142
+ // Anchor vocabulary
143
+ if (v.vocabulary.anchor.length > 0) {
144
+ lines.push("### Anchor Vocabulary\n");
145
+ lines.push("| Use | Instead of | Why |");
146
+ lines.push("|-----|-----------|-----|");
147
+ for (const a of v.vocabulary.anchor) {
148
+ lines.push(`| ${a.use} | ${a.not} | ${a.reason} |`);
149
+ }
150
+ lines.push("");
151
+ }
152
+ // Never-say
153
+ if (v.vocabulary.never_say.length > 0) {
154
+ lines.push("### Never Say\n");
155
+ for (const ns of v.vocabulary.never_say) {
156
+ lines.push(`- **${ns.word}** — ${ns.reason}`);
157
+ }
158
+ lines.push("");
159
+ }
160
+ return lines.join("\n") + "\n";
161
+ }
162
+ function aiIsmSection(messaging) {
163
+ if (!messaging?.voice?.ai_ism_detection)
164
+ return "";
165
+ const ai = messaging.voice.ai_ism_detection;
166
+ const lines = [
167
+ "## AI-ism Detection\n",
168
+ ai.instruction + "\n",
169
+ "Patterns to avoid:\n",
170
+ ...ai.patterns.map((p) => `- ${p}`),
171
+ ];
172
+ return lines.join("\n") + "\n";
173
+ }
174
+ function perspectiveSection(messaging) {
175
+ if (!messaging?.perspective)
176
+ return "";
177
+ const p = messaging.perspective;
178
+ const lines = [
179
+ "## Perspective\n",
180
+ `**One-liner**: ${p.one_liner}\n`,
181
+ `- **Worldview**: ${p.worldview}`,
182
+ `- **Tension**: ${p.tension}`,
183
+ `- **Resolution**: ${p.resolution}`,
184
+ `- **Audience**: ${p.audience}`,
185
+ `- **Positioning**: ${p.positioning}`,
186
+ ];
187
+ return lines.join("\n") + "\n";
188
+ }
189
+ function brandStorySection(messaging) {
190
+ if (!messaging?.brand_story)
191
+ return "";
192
+ const s = messaging.brand_story;
193
+ const lines = [
194
+ "## Brand Story\n",
195
+ `**Tagline**: ${s.tagline}\n`,
196
+ `- **Origin**: ${s.origin}`,
197
+ `- **Tension**: ${s.tension}`,
198
+ `- **Resolution**: ${s.resolution}`,
199
+ `- **Vision**: ${s.vision}`,
200
+ ];
201
+ return lines.join("\n") + "\n";
202
+ }
203
+ export function generateChat(data, includeLogo) {
204
+ const { config, identity, visual, messaging } = data;
205
+ const lines = [];
206
+ lines.push(`# ${config.client_name} — Brand System`);
207
+ lines.push("");
208
+ lines.push("> **Portability notice:** This file is your complete brand system. Upload it to any AI conversation (Claude, ChatGPT, Gemini, etc.) and the AI will produce on-brand output.");
209
+ if (config.industry) {
210
+ lines.push(`>\n> **Industry**: ${config.industry}`);
211
+ }
212
+ lines.push("");
213
+ // Logo
214
+ lines.push(logoBlock(identity, includeLogo));
215
+ // Colors
216
+ lines.push("## Colors\n");
217
+ lines.push(colorTable(identity.colors));
218
+ // Typography
219
+ lines.push("## Typography\n");
220
+ lines.push(typographyList(identity.typography));
221
+ // Anti-patterns as HARD RULES
222
+ lines.push(antiPatternsSection(visual, "hard-rules"));
223
+ // Composition rules
224
+ lines.push(compositionSection(visual));
225
+ // Signature moves
226
+ lines.push(signatureSection(visual));
227
+ // Voice (Session 3)
228
+ lines.push(voiceSection(messaging));
229
+ // AI-ism detection
230
+ lines.push(aiIsmSection(messaging));
231
+ // Perspective
232
+ lines.push(perspectiveSection(messaging));
233
+ // Brand story tagline
234
+ if (messaging?.brand_story?.tagline) {
235
+ lines.push(`## Brand Tagline\n`);
236
+ lines.push(`> ${messaging.brand_story.tagline}\n`);
237
+ }
238
+ // Strip excessive blank lines
239
+ return lines
240
+ .join("\n")
241
+ .replace(/\n{4,}/g, "\n\n\n")
242
+ .trim() + "\n";
243
+ }
244
+ export function generateCode(data) {
245
+ const { config } = data;
246
+ const lines = [];
247
+ lines.push(`# ${config.client_name} — Brand System (Code Integration)`);
248
+ lines.push("");
249
+ lines.push("Two things to set up: (1) MCP server config so your AI coding tool can call brand tools, and (2) an instruction snippet for your project.");
250
+ lines.push("");
251
+ // MCP config
252
+ lines.push("## 1. MCP Server Config (`.mcp.json`)\n");
253
+ lines.push("Add this to your project's `.mcp.json` (Claude Code) or equivalent:\n");
254
+ lines.push("```json");
255
+ lines.push(JSON.stringify({
256
+ mcpServers: {
257
+ brandsystem: {
258
+ command: "npx",
259
+ args: ["-y", "@brandsystem/mcp"],
260
+ },
261
+ },
262
+ }, null, 2));
263
+ lines.push("```\n");
264
+ // Instruction snippet
265
+ lines.push("## 2. Project Instructions (`CLAUDE.md` / `.cursorrules`)\n");
266
+ lines.push("Paste this into your CLAUDE.md, .cursorrules, or project instructions:\n");
267
+ lines.push("```markdown");
268
+ lines.push(`# Brand System: ${config.client_name}`);
269
+ lines.push("");
270
+ lines.push("This project uses a machine-readable brand system in `.brand/`.");
271
+ lines.push("");
272
+ lines.push("## Before creating any visual output:");
273
+ lines.push("1. Run `brand_write` with the content type to load the full brand brief");
274
+ lines.push("2. Apply ALL rules from the creation brief (colors, typography, logo, anti-patterns)");
275
+ lines.push("3. Run `brand_preflight` on the output to validate compliance");
276
+ lines.push("");
277
+ lines.push("## Key files:");
278
+ lines.push("- `.brand/core-identity.yaml` — colors, fonts, logo");
279
+ lines.push("- `.brand/visual-identity.yaml` — composition, patterns, anti-patterns");
280
+ lines.push("- `.brand/messaging.yaml` — voice, vocabulary, perspective");
281
+ lines.push("- `.brand/tokens.json` — DTCG design tokens");
282
+ lines.push("- `.brand/visual-identity-manifest.md` — full visual spec (human-readable)");
283
+ lines.push("- `.brand/system-integration.md` — detailed integration guide");
284
+ lines.push("");
285
+ lines.push("## Hard rules:");
286
+ lines.push("- Use ONLY hex values from core-identity.yaml — no off-brand colors");
287
+ lines.push("- Embed the logo using inline SVG — never approximate with text");
288
+ lines.push("- Check anti-patterns in visual-identity.yaml — these are absolute constraints");
289
+ lines.push("```\n");
290
+ // Workflow
291
+ lines.push("## Workflow Reference\n");
292
+ lines.push("```");
293
+ lines.push("brand_status → check what's been extracted");
294
+ lines.push("brand_write → load brand context for content creation");
295
+ lines.push("brand_preflight → validate output against brand rules");
296
+ lines.push("brand_audit → run a full brand compliance audit");
297
+ lines.push("brand_export → generate shareable bundles (chat, code, team, email)");
298
+ lines.push("```\n");
299
+ return lines.join("\n").replace(/\n{4,}/g, "\n\n\n").trim() + "\n";
300
+ }
301
+ export function generateTeam(data, includeLogo) {
302
+ const { config, identity, visual, messaging } = data;
303
+ const lines = [];
304
+ lines.push(`# ${config.client_name} — Brand Guidelines`);
305
+ lines.push("");
306
+ if (config.industry) {
307
+ lines.push(`**Industry**: ${config.industry}`);
308
+ lines.push("");
309
+ }
310
+ lines.push("This document summarizes the brand identity system. Share it with designers, writers, and marketers to keep all creative work on-brand.");
311
+ lines.push("");
312
+ // Logo
313
+ if (includeLogo && identity.logo.length > 0) {
314
+ lines.push("## Logo\n");
315
+ for (const logo of identity.logo) {
316
+ lines.push(`**Type**: ${logo.type}\n`);
317
+ for (const v of logo.variants) {
318
+ if (v.inline_svg) {
319
+ lines.push(`### ${v.name} variant\n`);
320
+ lines.push("```svg");
321
+ lines.push(v.inline_svg.trim());
322
+ lines.push("```\n");
323
+ }
324
+ else {
325
+ lines.push(`- **${v.name}** variant available`);
326
+ }
327
+ }
328
+ }
329
+ lines.push("");
330
+ lines.push("*See the Logo section in the full brand system for all variants and usage rules.*\n");
331
+ }
332
+ // Colors
333
+ lines.push("## Color Palette\n");
334
+ if (identity.colors.length === 0) {
335
+ lines.push("*No colors extracted yet.*\n");
336
+ }
337
+ else {
338
+ for (const c of identity.colors) {
339
+ const roleLabel = c.role !== "unknown" ? ` (${c.role})` : "";
340
+ lines.push(`- **${cleanColorName(c)}**: \`${c.value}\`${roleLabel}`);
341
+ }
342
+ lines.push("");
343
+ }
344
+ // Typography
345
+ lines.push("## Typography\n");
346
+ lines.push(typographyList(identity.typography));
347
+ // Logo usage rules
348
+ if (identity.logo.length > 0) {
349
+ lines.push("## Logo Usage Rules\n");
350
+ lines.push("- Always use the provided logo files — never recreate the logo in a font");
351
+ lines.push("- Maintain clear space around the logo");
352
+ lines.push("- Do not stretch, rotate, or recolor the logo beyond approved variants");
353
+ lines.push("");
354
+ }
355
+ // Voice summary
356
+ if (messaging?.voice) {
357
+ lines.push("## Voice & Tone\n");
358
+ const v = messaging.voice;
359
+ lines.push(`**Tone**: ${v.tone.descriptors.join(", ")}`);
360
+ lines.push(`**Register**: ${v.tone.register}`);
361
+ lines.push(`**Never sounds like**: ${v.tone.never_sounds_like}\n`);
362
+ if (v.vocabulary.anchor.length > 0) {
363
+ lines.push("### Preferred Terms\n");
364
+ for (const a of v.vocabulary.anchor) {
365
+ lines.push(`- Say "${a.use}" instead of "${a.not}"`);
366
+ }
367
+ lines.push("");
368
+ }
369
+ if (v.vocabulary.never_say.length > 0) {
370
+ lines.push("### Words to Avoid\n");
371
+ for (const ns of v.vocabulary.never_say) {
372
+ lines.push(`- "${ns.word}" — ${ns.reason}`);
373
+ }
374
+ lines.push("");
375
+ }
376
+ }
377
+ // Composition philosophy
378
+ lines.push(compositionSection(visual));
379
+ // Anti-patterns in plain language
380
+ lines.push(antiPatternsSection(visual, "plain"));
381
+ // Brand story
382
+ if (messaging?.brand_story) {
383
+ const s = messaging.brand_story;
384
+ lines.push("## Brand Story\n");
385
+ if (s.tagline)
386
+ lines.push(`**Tagline**: ${s.tagline}\n`);
387
+ lines.push(s.origin);
388
+ lines.push("");
389
+ lines.push(`**Vision**: ${s.vision}`);
390
+ lines.push("");
391
+ }
392
+ lines.push("---\n");
393
+ lines.push("*Generated by [brandsystem.app](https://brandsystem.app)*\n");
394
+ return lines.join("\n").replace(/\n{4,}/g, "\n\n\n").trim() + "\n";
395
+ }
396
+ export function generateEmail(data) {
397
+ const { config, identity, visual, messaging } = data;
398
+ const lines = [];
399
+ lines.push(`# ${config.client_name} — Brand System Summary`);
400
+ lines.push("");
401
+ lines.push(`Here's the brand system for ${config.client_name}, built for AI tools and creative workflows. When generating content, visuals, or code for this brand, follow these guidelines to stay on-brand.`);
402
+ lines.push("");
403
+ // Brand positioning one-liner
404
+ if (messaging?.perspective?.one_liner) {
405
+ lines.push(`**Brand positioning:** ${messaging.perspective.one_liner}`);
406
+ lines.push("");
407
+ }
408
+ // All assigned colors with names
409
+ if (identity.colors.length > 0) {
410
+ lines.push("## Colors\n");
411
+ for (const c of identity.colors) {
412
+ const roleLabel = c.role !== "unknown" ? ` — ${c.role}` : "";
413
+ lines.push(`- **${cleanColorName(c)}**: \`${c.value}\`${roleLabel}`);
414
+ }
415
+ lines.push("");
416
+ lines.push("Use only these hex values. Do not introduce off-palette colors.\n");
417
+ }
418
+ // Typography with usage hints
419
+ if (identity.typography.length > 0) {
420
+ lines.push("## Typography\n");
421
+ for (const t of identity.typography) {
422
+ const parts = [`**${t.name}**: \`${t.family}\``];
423
+ if (t.weight)
424
+ parts.push(`weight ${t.weight}`);
425
+ if (t.size)
426
+ parts.push(`size ${t.size}`);
427
+ // Infer usage hint from name or weight
428
+ const nameLower = t.name.toLowerCase();
429
+ if (nameLower.includes("heading") || nameLower.includes("display") || (t.weight && t.weight >= 600)) {
430
+ parts.push("(headings)");
431
+ }
432
+ else if (nameLower.includes("body") || nameLower.includes("text")) {
433
+ parts.push("(body text)");
434
+ }
435
+ else if (nameLower.includes("code") || nameLower.includes("mono")) {
436
+ parts.push("(code / monospace)");
437
+ }
438
+ lines.push(`- ${parts.join(", ")}`);
439
+ }
440
+ lines.push("");
441
+ }
442
+ // Top 5 anti-patterns (hard severity first)
443
+ if (visual && visual.anti_patterns.length > 0) {
444
+ const sorted = [...visual.anti_patterns].sort((a, b) => a.severity === "hard" && b.severity !== "hard" ? -1 : b.severity === "hard" && a.severity !== "hard" ? 1 : 0);
445
+ const top5 = sorted.slice(0, 5);
446
+ lines.push("## Anti-Patterns (Do NOT Do These)\n");
447
+ for (const ap of top5) {
448
+ const severity = ap.severity === "hard" ? "NEVER" : "Avoid";
449
+ lines.push(`- **${severity}**: ${ap.rule}`);
450
+ }
451
+ lines.push("");
452
+ }
453
+ // Voice summary (2-3 sentences)
454
+ if (messaging?.voice) {
455
+ const v = messaging.voice;
456
+ const descriptors = v.tone.descriptors.join(", ");
457
+ lines.push("## Voice\n");
458
+ lines.push(`Our tone is ${descriptors}. Think of the register as: ${v.tone.register}. We never sound like ${v.tone.never_sounds_like}.`);
459
+ if (v.vocabulary.anchor.length > 0) {
460
+ const anchorExamples = v.vocabulary.anchor
461
+ .slice(0, 3)
462
+ .map((a) => `"${a.use}" (not "${a.not}")`)
463
+ .join(", ");
464
+ lines.push(`\nKey vocabulary: ${anchorExamples}.`);
465
+ }
466
+ lines.push("");
467
+ }
468
+ // Brand story tagline
469
+ if (messaging?.brand_story?.tagline) {
470
+ lines.push(`**Tagline:** ${messaging.brand_story.tagline}`);
471
+ lines.push("");
472
+ }
473
+ lines.push("---\n");
474
+ lines.push(`For the full guidelines — including logo files, composition rules, and detailed voice specs — visit [brandsystem.app](https://brandsystem.app).`);
475
+ lines.push("");
476
+ return lines.join("\n").replace(/\n{4,}/g, "\n\n\n").trim() + "\n";
477
+ }
478
+ // ---------------------------------------------------------------------------
479
+ // Claude Skill export — persistent brand identity across all Claude artifacts
480
+ // ---------------------------------------------------------------------------
481
+ export function generateClaudeSkill(data, includeLogo) {
482
+ const { config, identity, visual, messaging } = data;
483
+ const brandName = config.client_name;
484
+ const lines = [];
485
+ lines.push(`# ${brandName} Brand Identity Skill`);
486
+ lines.push("");
487
+ lines.push("## When to Use");
488
+ lines.push(`Use this skill whenever creating a visual artifact (HTML, React/JSX, SVG) for ${brandName}. This includes: dashboards, landing pages, social graphics, reports, interactive tools, presentations rendered as HTML, or any UI prototype.`);
489
+ lines.push("");
490
+ lines.push("Read this file BEFORE writing any artifact code.");
491
+ lines.push("");
492
+ // Logo section — the critical part
493
+ lines.push("## Logo Asset");
494
+ lines.push("");
495
+ if (identity.logo.length > 0 && includeLogo) {
496
+ for (const logo of identity.logo) {
497
+ for (const v of logo.variants) {
498
+ if (v.inline_svg) {
499
+ lines.push(`### Primary Logo (SVG — ${logo.type})`);
500
+ lines.push("");
501
+ lines.push("```svg");
502
+ lines.push(v.inline_svg.trim());
503
+ lines.push("```");
504
+ lines.push("");
505
+ }
506
+ if (v.data_uri) {
507
+ lines.push("### Base64 Data URI (for `<img>` tags)");
508
+ lines.push("");
509
+ lines.push("```");
510
+ lines.push(v.data_uri);
511
+ lines.push("```");
512
+ lines.push("");
513
+ }
514
+ }
515
+ }
516
+ }
517
+ else {
518
+ lines.push("*No logo extracted yet. Run `brand_set_logo` or `brand_extract_web` with a `logo_url` to add one.*");
519
+ lines.push("");
520
+ }
521
+ // Embedding rules
522
+ lines.push("## Embedding Rules");
523
+ lines.push("");
524
+ lines.push("1. **Always inline the logo** — never use an external URL `src`. Claude artifacts block external images via CSP.");
525
+ lines.push("2. **For SVG logos:** Paste the `<svg>` markup directly into the JSX/HTML. Wrap in a container with explicit width (e.g., `width: 120px`) and `height: auto`.");
526
+ lines.push("3. **For base64 logos:** Use `<img src=\"data:image/png;base64,...\" alt=\"" + brandName + " logo\" />`.");
527
+ lines.push("4. **Placement defaults** (override per request): top-left for dashboards, centered for landing pages.");
528
+ lines.push(`5. **Always include** \`alt="${brandName} logo"\` on all logo image tags.`);
529
+ lines.push("6. **Do not distort.** Always preserve aspect ratio. Use `object-fit: contain` or `preserveAspectRatio=\"xMidYMid meet\"`.");
530
+ lines.push("");
531
+ // Colors as CSS variables
532
+ lines.push("## Brand Colors");
533
+ lines.push("");
534
+ lines.push("Use ONLY these colors. Do not introduce off-palette colors.");
535
+ lines.push("");
536
+ lines.push("```css");
537
+ lines.push(":root {");
538
+ for (const c of identity.colors) {
539
+ const name = cleanColorName(c);
540
+ const role = c.role !== "unknown" ? c.role : name.toLowerCase().replace(/\s+/g, "-");
541
+ lines.push(` --brand-${role}: ${c.value}; /* ${name} */`);
542
+ }
543
+ lines.push("}");
544
+ lines.push("```");
545
+ lines.push("");
546
+ // Quick reference table
547
+ lines.push("| Role | Hex | Name |");
548
+ lines.push("|------|-----|------|");
549
+ for (const c of identity.colors) {
550
+ lines.push(`| ${c.role} | \`${c.value}\` | ${cleanColorName(c)} |`);
551
+ }
552
+ lines.push("");
553
+ // Typography
554
+ if (identity.typography.length > 0) {
555
+ lines.push("## Typography");
556
+ lines.push("");
557
+ for (const t of identity.typography) {
558
+ lines.push(`- **${t.family}**${t.weight ? ` (weight: ${t.weight})` : ""}`);
559
+ }
560
+ lines.push("");
561
+ lines.push("Use these font families in `font-family` declarations. If the font is not available in the artifact sandbox, fall back to `system-ui, -apple-system, sans-serif`.");
562
+ lines.push("");
563
+ }
564
+ // Anti-patterns as HARD RULES
565
+ if (visual?.anti_patterns && visual.anti_patterns.length > 0) {
566
+ lines.push("## Anti-Patterns (NEVER DO)");
567
+ lines.push("");
568
+ for (const ap of visual.anti_patterns) {
569
+ const rule = typeof ap === "string" ? ap : ap.rule;
570
+ lines.push(`- ❌ ${rule}`);
571
+ }
572
+ lines.push("");
573
+ }
574
+ // Voice quick reference
575
+ if (messaging?.voice?.tone?.descriptors) {
576
+ lines.push("## Voice (Quick Reference)");
577
+ lines.push("");
578
+ lines.push(`**Tone:** ${messaging.voice.tone.descriptors.join(", ")}`);
579
+ if (messaging.voice.tone.register) {
580
+ lines.push(`**Register:** ${messaging.voice.tone.register}`);
581
+ }
582
+ if (messaging.voice.vocabulary?.anchor?.length) {
583
+ lines.push("");
584
+ lines.push("**Vocabulary:**");
585
+ for (const a of messaging.voice.vocabulary.anchor) {
586
+ lines.push(`- Use "${a.use}" not "${a.not}"`);
587
+ }
588
+ }
589
+ lines.push("");
590
+ }
591
+ // Deployment instructions
592
+ lines.push("---");
593
+ lines.push("");
594
+ lines.push("## How to Deploy This Skill");
595
+ lines.push("");
596
+ lines.push("### Option 1: Personal Skill (Claude Pro, Team, or Enterprise)");
597
+ lines.push("");
598
+ lines.push(`1. Save this file as \`SKILL.md\` inside \`~/.claude/skills/${brandName.toLowerCase().replace(/\s+/g, "-")}-brand/\``);
599
+ lines.push("2. Claude will read it automatically before creating any visual artifact");
600
+ lines.push("3. The logo is embedded — no uploads needed, no external URLs, works every time");
601
+ lines.push("");
602
+ lines.push("### Option 2: Organization Skill (Claude Teams/Enterprise)");
603
+ lines.push("");
604
+ lines.push("1. Ask your admin to add this as an org-level skill");
605
+ lines.push("2. Every team member gets the brand applied automatically");
606
+ lines.push("");
607
+ lines.push("### Option 3: Project Instructions (Claude Pro+)");
608
+ lines.push("");
609
+ lines.push("1. Create a Claude Project for this brand");
610
+ lines.push("2. Paste this file into the Project Instructions");
611
+ lines.push("3. Works for all conversations in that project");
612
+ lines.push("");
613
+ lines.push("### Option 4: Per-Conversation (any plan)");
614
+ lines.push("");
615
+ lines.push("1. Upload this file at the start of a conversation");
616
+ lines.push(`2. Say: "Use this brand system for all visual artifacts this session"`);
617
+ lines.push("");
618
+ lines.push("**Why skills are best:** Skills re-read on every turn where triggered. Project knowledge loads once and can drift out of context in long conversations. For mission-critical brand consistency, skills > project knowledge > per-session upload.");
619
+ lines.push("");
620
+ lines.push(`---\n*Generated by [@brandsystem/mcp](https://brandsystem.app)*`);
621
+ return lines
622
+ .join("\n")
623
+ .replace(/\n{4,}/g, "\n\n\n")
624
+ .trim() + "\n";
625
+ }
626
+ // ---------------------------------------------------------------------------
627
+ // Filenames per target
628
+ // ---------------------------------------------------------------------------
629
+ const TARGET_FILES = {
630
+ chat: "exports/brand-system-chat.md",
631
+ code: "exports/brand-system-code.md",
632
+ team: "exports/brand-guidelines.md",
633
+ email: "exports/brand-summary.md",
634
+ "claude-skill": "exports/brand-skill.md",
635
+ };
636
+ // ---------------------------------------------------------------------------
637
+ // Main handler
638
+ // ---------------------------------------------------------------------------
639
+ async function handler(input) {
640
+ const brandDir = new BrandDir(process.cwd());
641
+ if (!(await brandDir.exists())) {
642
+ return buildResponse({
643
+ what_happened: "No .brand/ directory found",
644
+ next_steps: ["Run brand_start to create a brand system first"],
645
+ data: { error: ERROR_CODES.NOT_INITIALIZED },
646
+ });
647
+ }
648
+ let data;
649
+ try {
650
+ data = await loadBrandData(brandDir);
651
+ }
652
+ catch {
653
+ return buildResponse({
654
+ what_happened: "Could not read brand data",
655
+ next_steps: ["Run brand_extract_web to populate core identity first"],
656
+ data: { error: ERROR_CODES.NO_BRAND_DATA },
657
+ });
658
+ }
659
+ // Ensure exports/ directory exists
660
+ await mkdir(join(brandDir.brandPath, "exports"), { recursive: true });
661
+ const { target, include_logo: includeLogo } = input;
662
+ let content;
663
+ switch (target) {
664
+ case "chat":
665
+ content = generateChat(data, includeLogo);
666
+ break;
667
+ case "code":
668
+ content = generateCode(data);
669
+ break;
670
+ case "team":
671
+ content = generateTeam(data, includeLogo);
672
+ break;
673
+ case "email":
674
+ content = generateEmail(data);
675
+ break;
676
+ case "claude-skill":
677
+ content = generateClaudeSkill(data, includeLogo);
678
+ break;
679
+ }
680
+ const filename = TARGET_FILES[target];
681
+ await brandDir.writeMarkdown(filename, content);
682
+ const layers = ["core_identity"];
683
+ if (data.visual)
684
+ layers.push("visual_identity");
685
+ if (data.messaging)
686
+ layers.push("messaging");
687
+ return buildResponse({
688
+ what_happened: `Generated "${target}" export for "${data.config.client_name}" → .brand/${filename}`,
689
+ next_steps: [
690
+ target === "chat"
691
+ ? "Upload .brand/exports/brand-system-chat.md to any AI conversation"
692
+ : target === "code"
693
+ ? "Follow the setup instructions in .brand/exports/brand-system-code.md"
694
+ : target === "team"
695
+ ? "Share .brand/exports/brand-guidelines.md with your team"
696
+ : target === "claude-skill"
697
+ ? "Save .brand/exports/brand-skill.md as SKILL.md in your Claude skills folder. See deployment instructions at the bottom of the file."
698
+ : "Copy the summary from .brand/exports/brand-summary.md into an email or Slack",
699
+ ],
700
+ data: {
701
+ file: `.brand/${filename}`,
702
+ target,
703
+ brand_layers_included: layers,
704
+ file_size: `${Math.round(content.length / 1024)}KB`,
705
+ content,
706
+ },
707
+ });
708
+ }
709
+ // ---------------------------------------------------------------------------
710
+ // Tool registration
711
+ // ---------------------------------------------------------------------------
712
+ const paramsShape = {
713
+ target: z
714
+ .enum(["chat", "code", "team", "email", "claude-skill"])
715
+ .describe("Where this export will be used. 'chat': upload to AI conversation (Claude/ChatGPT/Gemini). 'code': paste into CLAUDE.md or .cursorrules. 'team': share with designers/writers. 'email': send via Slack or email. 'claude-skill': persistent Claude skill file with embedded logo + brand rules — the gold standard for automatic brand application in every artifact."),
716
+ include_logo: z
717
+ .boolean()
718
+ .default(true)
719
+ .describe("Embed logo SVG/data URI in the export. Set false to reduce file size. Default: true."),
720
+ };
721
+ const ParamsSchema = z.object(paramsShape);
722
+ export function register(server) {
723
+ server.tool("brand_export", "Generate portable brand files for any environment — Chat, Code, team sharing, or email. Target 'chat': self-contained markdown to upload to any AI conversation (Claude, ChatGPT, Gemini). Target 'code': MCP config + CLAUDE.md/.cursorrules snippet. Target 'team': clean brand guidelines for designers and writers. Target 'email': concise 500-word summary for Slack or email. Writes to .brand/exports/ and returns the full content. Use when the user wants to share their brand system or set up a new tool.", paramsShape, async (args) => {
724
+ const parsed = safeParseParams(ParamsSchema, args);
725
+ if (!parsed.success)
726
+ return parsed.response;
727
+ return handler(parsed.data);
728
+ });
729
+ }
730
+ //# sourceMappingURL=brand-export.js.map