@jack200714/mafw 4.5.1 → 4.5.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.
Files changed (320) hide show
  1. package/README.md +5 -2
  2. package/bin/mafw.js +16 -14
  3. package/package.json +1 -7
  4. package/.opencode/mafw/.backup-v61/parametric/base-skill-manifest.yaml +0 -2
  5. package/.opencode/mafw/STATUS.md +0 -6
  6. package/.opencode/mafw/ledger.md +0 -20
  7. package/.opencode/mafw/memory/.harmonic_index.json +0 -28
  8. package/.opencode/mafw/memory/.review_queue.json +0 -1
  9. package/.opencode/mafw/memory/tier3/parametric.json +0 -13
  10. package/.opencode/mafw/memory/tier3/test-goal.json +0 -19
  11. package/.opencode/mafw/memory-index.json +0 -6
  12. package/.opencode/mafw/parametric/base-skill-manifest.yaml +0 -2
  13. package/.opencode/mafw/waves.json +0 -4
  14. package/.opencode/opencode.json +0 -3
  15. package/.opencode/plugins/mafw-plugin.ts +0 -8
  16. package/.opencode/skills/banner-design/SKILL.md +0 -196
  17. package/.opencode/skills/banner-design/references/banner-sizes-and-styles.md +0 -118
  18. package/.opencode/skills/brand/SKILL.md +0 -97
  19. package/.opencode/skills/brand/references/approval-checklist.md +0 -169
  20. package/.opencode/skills/brand/references/asset-organization.md +0 -157
  21. package/.opencode/skills/brand/references/brand-guideline-template.md +0 -140
  22. package/.opencode/skills/brand/references/color-palette-management.md +0 -186
  23. package/.opencode/skills/brand/references/consistency-checklist.md +0 -94
  24. package/.opencode/skills/brand/references/logo-usage-rules.md +0 -185
  25. package/.opencode/skills/brand/references/messaging-framework.md +0 -85
  26. package/.opencode/skills/brand/references/typography-specifications.md +0 -214
  27. package/.opencode/skills/brand/references/update.md +0 -118
  28. package/.opencode/skills/brand/references/visual-identity.md +0 -96
  29. package/.opencode/skills/brand/references/voice-framework.md +0 -88
  30. package/.opencode/skills/brand/scripts/extract-colors.cjs +0 -341
  31. package/.opencode/skills/brand/scripts/inject-brand-context.cjs +0 -349
  32. package/.opencode/skills/brand/scripts/sync-brand-to-tokens.cjs +0 -248
  33. package/.opencode/skills/brand/scripts/tests/test_sync_brand_to_tokens.py +0 -52
  34. package/.opencode/skills/brand/scripts/validate-asset.cjs +0 -387
  35. package/.opencode/skills/brand/templates/brand-guidelines-starter.md +0 -275
  36. package/.opencode/skills/design/SKILL.md +0 -313
  37. package/.opencode/skills/design/data/cip/deliverables.csv +0 -51
  38. package/.opencode/skills/design/data/cip/industries.csv +0 -21
  39. package/.opencode/skills/design/data/cip/mockup-contexts.csv +0 -21
  40. package/.opencode/skills/design/data/cip/styles.csv +0 -21
  41. package/.opencode/skills/design/data/icon/styles.csv +0 -16
  42. package/.opencode/skills/design/data/logo/colors.csv +0 -56
  43. package/.opencode/skills/design/data/logo/industries.csv +0 -56
  44. package/.opencode/skills/design/data/logo/styles.csv +0 -56
  45. package/.opencode/skills/design/references/banner-sizes-and-styles.md +0 -118
  46. package/.opencode/skills/design/references/cip-deliverable-guide.md +0 -95
  47. package/.opencode/skills/design/references/cip-design.md +0 -121
  48. package/.opencode/skills/design/references/cip-prompt-engineering.md +0 -84
  49. package/.opencode/skills/design/references/cip-style-guide.md +0 -68
  50. package/.opencode/skills/design/references/design-routing.md +0 -207
  51. package/.opencode/skills/design/references/icon-design.md +0 -122
  52. package/.opencode/skills/design/references/logo-color-psychology.md +0 -101
  53. package/.opencode/skills/design/references/logo-design.md +0 -92
  54. package/.opencode/skills/design/references/logo-prompt-engineering.md +0 -158
  55. package/.opencode/skills/design/references/logo-style-guide.md +0 -109
  56. package/.opencode/skills/design/references/slides-copywriting-formulas.md +0 -84
  57. package/.opencode/skills/design/references/slides-create.md +0 -4
  58. package/.opencode/skills/design/references/slides-html-template.md +0 -295
  59. package/.opencode/skills/design/references/slides-layout-patterns.md +0 -137
  60. package/.opencode/skills/design/references/slides-strategies.md +0 -94
  61. package/.opencode/skills/design/references/slides.md +0 -42
  62. package/.opencode/skills/design/references/social-photos-design.md +0 -329
  63. package/.opencode/skills/design/scripts/cip/core.py +0 -215
  64. package/.opencode/skills/design/scripts/cip/generate.py +0 -484
  65. package/.opencode/skills/design/scripts/cip/render-html.py +0 -424
  66. package/.opencode/skills/design/scripts/cip/search.py +0 -127
  67. package/.opencode/skills/design/scripts/icon/generate.py +0 -487
  68. package/.opencode/skills/design/scripts/logo/core.py +0 -175
  69. package/.opencode/skills/design/scripts/logo/generate.py +0 -362
  70. package/.opencode/skills/design/scripts/logo/search.py +0 -114
  71. package/.opencode/skills/design-system/SKILL.md +0 -244
  72. package/.opencode/skills/design-system/data/slide-backgrounds.csv +0 -11
  73. package/.opencode/skills/design-system/data/slide-charts.csv +0 -26
  74. package/.opencode/skills/design-system/data/slide-color-logic.csv +0 -14
  75. package/.opencode/skills/design-system/data/slide-copy.csv +0 -26
  76. package/.opencode/skills/design-system/data/slide-layout-logic.csv +0 -16
  77. package/.opencode/skills/design-system/data/slide-layouts.csv +0 -26
  78. package/.opencode/skills/design-system/data/slide-strategies.csv +0 -16
  79. package/.opencode/skills/design-system/data/slide-typography.csv +0 -15
  80. package/.opencode/skills/design-system/references/component-specs.md +0 -236
  81. package/.opencode/skills/design-system/references/component-tokens.md +0 -214
  82. package/.opencode/skills/design-system/references/primitive-tokens.md +0 -203
  83. package/.opencode/skills/design-system/references/semantic-tokens.md +0 -215
  84. package/.opencode/skills/design-system/references/states-and-variants.md +0 -241
  85. package/.opencode/skills/design-system/references/tailwind-integration.md +0 -251
  86. package/.opencode/skills/design-system/references/token-architecture.md +0 -224
  87. package/.opencode/skills/design-system/scripts/embed-tokens.cjs +0 -99
  88. package/.opencode/skills/design-system/scripts/fetch-background.py +0 -317
  89. package/.opencode/skills/design-system/scripts/generate-slide.py +0 -770
  90. package/.opencode/skills/design-system/scripts/generate-tokens.cjs +0 -205
  91. package/.opencode/skills/design-system/scripts/html-token-validator.py +0 -327
  92. package/.opencode/skills/design-system/scripts/search-slides.py +0 -218
  93. package/.opencode/skills/design-system/scripts/slide-token-validator.py +0 -35
  94. package/.opencode/skills/design-system/scripts/slide_search_core.py +0 -453
  95. package/.opencode/skills/design-system/scripts/tests/test_validate_tokens.py +0 -48
  96. package/.opencode/skills/design-system/scripts/validate-tokens.cjs +0 -246
  97. package/.opencode/skills/design-system/templates/design-tokens-starter.json +0 -143
  98. package/.opencode/skills/frontend-design/LICENSE.txt +0 -21
  99. package/.opencode/skills/frontend-design/SKILL.md +0 -115
  100. package/.opencode/skills/mafw-automation-interview/SKILL.md +0 -43
  101. package/.opencode/skills/mafw-cli-scanner/SKILL.md +0 -37
  102. package/.opencode/skills/mafw-compression-verifier/SKILL.md +0 -52
  103. package/.opencode/skills/mafw-desktop-inspector/SKILL.md +0 -170
  104. package/.opencode/skills/mafw-execute/SKILL.md +0 -62
  105. package/.opencode/skills/mafw-gateway-restart/SKILL.md +0 -87
  106. package/.opencode/skills/mafw-github-scanner/SKILL.md +0 -38
  107. package/.opencode/skills/mafw-goal/SKILL.md +0 -76
  108. package/.opencode/skills/mafw-interview/SKILL.md +0 -68
  109. package/.opencode/skills/mafw-memory-extractor/SKILL.md +0 -65
  110. package/.opencode/skills/mafw-plan/SKILL.md +0 -58
  111. package/.opencode/skills/mafw-review/SKILL.md +0 -85
  112. package/.opencode/skills/runtime-plugin-authoring/SKILL.md +0 -791
  113. package/.opencode/skills/slides/SKILL.md +0 -40
  114. package/.opencode/skills/slides/references/copywriting-formulas.md +0 -84
  115. package/.opencode/skills/slides/references/create.md +0 -4
  116. package/.opencode/skills/slides/references/html-template.md +0 -295
  117. package/.opencode/skills/slides/references/layout-patterns.md +0 -137
  118. package/.opencode/skills/slides/references/slide-strategies.md +0 -94
  119. package/.opencode/skills/ui-styling/LICENSE.txt +0 -202
  120. package/.opencode/skills/ui-styling/SKILL.md +0 -324
  121. package/.opencode/skills/ui-styling/references/canvas-design-system.md +0 -320
  122. package/.opencode/skills/ui-styling/references/shadcn-accessibility.md +0 -471
  123. package/.opencode/skills/ui-styling/references/shadcn-components.md +0 -424
  124. package/.opencode/skills/ui-styling/references/shadcn-theming.md +0 -373
  125. package/.opencode/skills/ui-styling/references/tailwind-customization.md +0 -483
  126. package/.opencode/skills/ui-styling/references/tailwind-responsive.md +0 -382
  127. package/.opencode/skills/ui-styling/references/tailwind-utilities.md +0 -455
  128. package/.opencode/skills/ui-styling/scripts/requirements.txt +0 -17
  129. package/.opencode/skills/ui-styling/scripts/shadcn_add.py +0 -308
  130. package/.opencode/skills/ui-styling/scripts/tailwind_config_gen.py +0 -473
  131. package/.opencode/skills/ui-styling/scripts/tests/coverage-ui.json +0 -1
  132. package/.opencode/skills/ui-styling/scripts/tests/requirements.txt +0 -3
  133. package/.opencode/skills/ui-styling/scripts/tests/test_shadcn_add.py +0 -266
  134. package/.opencode/skills/ui-styling/scripts/tests/test_tailwind_config_gen.py +0 -394
  135. package/.opencode/skills/ui-ux-pro-max/SKILL.md +0 -388
  136. package/.opencode/skills/ui-ux-pro-max/data/app-interface.csv +0 -31
  137. package/.opencode/skills/ui-ux-pro-max/data/charts.csv +0 -26
  138. package/.opencode/skills/ui-ux-pro-max/data/colors.csv +0 -193
  139. package/.opencode/skills/ui-ux-pro-max/data/google-fonts.csv +0 -1924
  140. package/.opencode/skills/ui-ux-pro-max/data/icons.csv +0 -106
  141. package/.opencode/skills/ui-ux-pro-max/data/landing.csv +0 -35
  142. package/.opencode/skills/ui-ux-pro-max/data/motion.csv +0 -17
  143. package/.opencode/skills/ui-ux-pro-max/data/products.csv +0 -193
  144. package/.opencode/skills/ui-ux-pro-max/data/react-performance.csv +0 -45
  145. package/.opencode/skills/ui-ux-pro-max/data/stacks/angular.csv +0 -51
  146. package/.opencode/skills/ui-ux-pro-max/data/stacks/astro.csv +0 -54
  147. package/.opencode/skills/ui-ux-pro-max/data/stacks/avalonia.csv +0 -57
  148. package/.opencode/skills/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  149. package/.opencode/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  150. package/.opencode/skills/ui-ux-pro-max/data/stacks/javafx.csv +0 -76
  151. package/.opencode/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
  152. package/.opencode/skills/ui-ux-pro-max/data/stacks/laravel.csv +0 -51
  153. package/.opencode/skills/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  154. package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -71
  155. package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  156. package/.opencode/skills/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  157. package/.opencode/skills/ui-ux-pro-max/data/stacks/react.csv +0 -54
  158. package/.opencode/skills/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
  159. package/.opencode/skills/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  160. package/.opencode/skills/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  161. package/.opencode/skills/ui-ux-pro-max/data/stacks/threejs.csv +0 -54
  162. package/.opencode/skills/ui-ux-pro-max/data/stacks/uno.csv +0 -60
  163. package/.opencode/skills/ui-ux-pro-max/data/stacks/uwp.csv +0 -56
  164. package/.opencode/skills/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  165. package/.opencode/skills/ui-ux-pro-max/data/stacks/winui.csv +0 -60
  166. package/.opencode/skills/ui-ux-pro-max/data/stacks/wpf.csv +0 -57
  167. package/.opencode/skills/ui-ux-pro-max/data/styles.csv +0 -85
  168. package/.opencode/skills/ui-ux-pro-max/data/typography.csv +0 -75
  169. package/.opencode/skills/ui-ux-pro-max/data/ui-reasoning.csv +0 -162
  170. package/.opencode/skills/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  171. package/.opencode/skills/ui-ux-pro-max/scripts/core.py +0 -464
  172. package/.opencode/skills/ui-ux-pro-max/scripts/design_system.py +0 -1479
  173. package/.opencode/skills/ui-ux-pro-max/scripts/search.py +0 -162
  174. package/.opencode/skills/ui-ux-pro-max/scripts/tests/test_core.py +0 -134
  175. package/.opencode/skills/ui-ux-pro-max/scripts/tests/test_design_system_mode.py +0 -159
  176. package/.opencode/skills/ui-ux-pro-max/scripts/validate_data.py +0 -114
  177. package/dist/hooks/bash-python-guide.d.ts +0 -4
  178. package/dist/hooks/bash-python-guide.d.ts.map +0 -1
  179. package/dist/hooks/bash-python-guide.js +0 -67
  180. package/dist/hooks/bash-python-guide.js.map +0 -1
  181. package/dist/hooks/handoff.d.ts +0 -9
  182. package/dist/hooks/handoff.d.ts.map +0 -1
  183. package/dist/hooks/handoff.js +0 -8
  184. package/dist/hooks/handoff.js.map +0 -1
  185. package/dist/hooks/hook-manager.d.ts +0 -32
  186. package/dist/hooks/hook-manager.d.ts.map +0 -1
  187. package/dist/hooks/hook-manager.js +0 -91
  188. package/dist/hooks/hook-manager.js.map +0 -1
  189. package/dist/hooks/llm-after.d.ts +0 -8
  190. package/dist/hooks/llm-after.d.ts.map +0 -1
  191. package/dist/hooks/llm-after.js +0 -8
  192. package/dist/hooks/llm-after.js.map +0 -1
  193. package/dist/hooks/media-ingest.d.ts +0 -43
  194. package/dist/hooks/media-ingest.d.ts.map +0 -1
  195. package/dist/hooks/media-ingest.js +0 -269
  196. package/dist/hooks/media-ingest.js.map +0 -1
  197. package/dist/hooks/memory-guide.d.ts +0 -3
  198. package/dist/hooks/memory-guide.d.ts.map +0 -1
  199. package/dist/hooks/memory-guide.js +0 -47
  200. package/dist/hooks/memory-guide.js.map +0 -1
  201. package/dist/hooks/observation-capture.d.ts +0 -17
  202. package/dist/hooks/observation-capture.d.ts.map +0 -1
  203. package/dist/hooks/observation-capture.js +0 -20
  204. package/dist/hooks/observation-capture.js.map +0 -1
  205. package/dist/hooks/session-compacting.d.ts +0 -6
  206. package/dist/hooks/session-compacting.d.ts.map +0 -1
  207. package/dist/hooks/session-compacting.js +0 -6
  208. package/dist/hooks/session-compacting.js.map +0 -1
  209. package/dist/hooks/session-ending.d.ts +0 -20
  210. package/dist/hooks/session-ending.d.ts.map +0 -1
  211. package/dist/hooks/session-ending.js +0 -113
  212. package/dist/hooks/session-ending.js.map +0 -1
  213. package/dist/hooks/session-recall.d.ts +0 -2
  214. package/dist/hooks/session-recall.d.ts.map +0 -1
  215. package/dist/hooks/session-recall.js +0 -103
  216. package/dist/hooks/session-recall.js.map +0 -1
  217. package/dist/hooks/session-start.d.ts +0 -9
  218. package/dist/hooks/session-start.d.ts.map +0 -1
  219. package/dist/hooks/session-start.js +0 -42
  220. package/dist/hooks/session-start.js.map +0 -1
  221. package/dist/hooks/tool-before.d.ts +0 -10
  222. package/dist/hooks/tool-before.d.ts.map +0 -1
  223. package/dist/hooks/tool-before.js +0 -6
  224. package/dist/hooks/tool-before.js.map +0 -1
  225. package/dist/hooks/tool-executed.d.ts +0 -19
  226. package/dist/hooks/tool-executed.d.ts.map +0 -1
  227. package/dist/hooks/tool-executed.js +0 -12
  228. package/dist/hooks/tool-executed.js.map +0 -1
  229. package/dist/hooks/user-profile.d.ts +0 -2
  230. package/dist/hooks/user-profile.d.ts.map +0 -1
  231. package/dist/hooks/user-profile.js +0 -30
  232. package/dist/hooks/user-profile.js.map +0 -1
  233. package/dist/hooks/user-prompt.d.ts +0 -9
  234. package/dist/hooks/user-prompt.d.ts.map +0 -1
  235. package/dist/hooks/user-prompt.js +0 -8
  236. package/dist/hooks/user-prompt.js.map +0 -1
  237. package/dist/hooks/voice-guide.d.ts +0 -8
  238. package/dist/hooks/voice-guide.d.ts.map +0 -1
  239. package/dist/hooks/voice-guide.js +0 -101
  240. package/dist/hooks/voice-guide.js.map +0 -1
  241. package/dist/plugin.d.ts +0 -55
  242. package/dist/plugin.d.ts.map +0 -1
  243. package/dist/plugin.js +0 -342
  244. package/dist/plugin.js.map +0 -1
  245. package/dist/tools/add-memory.d.ts +0 -17
  246. package/dist/tools/add-memory.d.ts.map +0 -1
  247. package/dist/tools/add-memory.js +0 -60
  248. package/dist/tools/add-memory.js.map +0 -1
  249. package/dist/tools/media-ask.d.ts +0 -14
  250. package/dist/tools/media-ask.d.ts.map +0 -1
  251. package/dist/tools/media-ask.js +0 -224
  252. package/dist/tools/media-ask.js.map +0 -1
  253. package/dist/tools/media-speak.d.ts +0 -16
  254. package/dist/tools/media-speak.d.ts.map +0 -1
  255. package/dist/tools/media-speak.js +0 -78
  256. package/dist/tools/media-speak.js.map +0 -1
  257. package/dist/tools/media-upload.d.ts +0 -14
  258. package/dist/tools/media-upload.d.ts.map +0 -1
  259. package/dist/tools/media-upload.js +0 -155
  260. package/dist/tools/media-upload.js.map +0 -1
  261. package/dist/tools/python-exec.d.ts +0 -13
  262. package/dist/tools/python-exec.d.ts.map +0 -1
  263. package/dist/tools/python-exec.js +0 -99
  264. package/dist/tools/python-exec.js.map +0 -1
  265. package/dist/tools/python-restart.d.ts +0 -11
  266. package/dist/tools/python-restart.d.ts.map +0 -1
  267. package/dist/tools/python-restart.js +0 -33
  268. package/dist/tools/python-restart.js.map +0 -1
  269. package/dist/utils/circuit-breaker.d.ts +0 -23
  270. package/dist/utils/circuit-breaker.d.ts.map +0 -1
  271. package/dist/utils/circuit-breaker.js +0 -75
  272. package/dist/utils/circuit-breaker.js.map +0 -1
  273. package/dist/utils/config-loader.d.ts +0 -20
  274. package/dist/utils/config-loader.d.ts.map +0 -1
  275. package/dist/utils/config-loader.js +0 -194
  276. package/dist/utils/config-loader.js.map +0 -1
  277. package/dist/utils/fallback.d.ts +0 -7
  278. package/dist/utils/fallback.d.ts.map +0 -1
  279. package/dist/utils/fallback.js +0 -20
  280. package/dist/utils/fallback.js.map +0 -1
  281. package/dist/utils/git.d.ts +0 -24
  282. package/dist/utils/git.d.ts.map +0 -1
  283. package/dist/utils/git.js +0 -71
  284. package/dist/utils/git.js.map +0 -1
  285. package/dist/utils/github.d.ts +0 -37
  286. package/dist/utils/github.d.ts.map +0 -1
  287. package/dist/utils/github.js +0 -53
  288. package/dist/utils/github.js.map +0 -1
  289. package/dist/utils/global-path.d.ts +0 -2
  290. package/dist/utils/global-path.d.ts.map +0 -1
  291. package/dist/utils/global-path.js +0 -32
  292. package/dist/utils/global-path.js.map +0 -1
  293. package/dist/utils/logger.d.ts +0 -6
  294. package/dist/utils/logger.d.ts.map +0 -1
  295. package/dist/utils/logger.js +0 -69
  296. package/dist/utils/logger.js.map +0 -1
  297. package/dist/utils/obs-capture.d.ts +0 -7
  298. package/dist/utils/obs-capture.d.ts.map +0 -1
  299. package/dist/utils/obs-capture.js +0 -49
  300. package/dist/utils/obs-capture.js.map +0 -1
  301. package/dist/utils/retry.d.ts +0 -11
  302. package/dist/utils/retry.d.ts.map +0 -1
  303. package/dist/utils/retry.js +0 -45
  304. package/dist/utils/retry.js.map +0 -1
  305. package/dist/utils/self-wiring.d.ts +0 -13
  306. package/dist/utils/self-wiring.d.ts.map +0 -1
  307. package/dist/utils/self-wiring.js +0 -86
  308. package/dist/utils/self-wiring.js.map +0 -1
  309. package/dist/utils/state.d.ts +0 -106
  310. package/dist/utils/state.d.ts.map +0 -1
  311. package/dist/utils/state.js +0 -166
  312. package/dist/utils/state.js.map +0 -1
  313. package/dist/utils/status.d.ts +0 -47
  314. package/dist/utils/status.d.ts.map +0 -1
  315. package/dist/utils/status.js +0 -147
  316. package/dist/utils/status.js.map +0 -1
  317. package/dist/utils/ttl-map.d.ts +0 -17
  318. package/dist/utils/ttl-map.d.ts.map +0 -1
  319. package/dist/utils/ttl-map.js +0 -67
  320. package/dist/utils/ttl-map.js.map +0 -1
@@ -1,349 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * inject-brand-context.cjs
4
- *
5
- * Extracts brand context from markdown brand guidelines
6
- * and outputs a formatted system prompt addition.
7
- *
8
- * Usage:
9
- * node inject-brand-context.cjs [path-to-guidelines]
10
- * node inject-brand-context.cjs --json [path-to-guidelines]
11
- *
12
- * Default path: docs/brand-guidelines.md
13
- */
14
-
15
- const fs = require("fs");
16
- const path = require("path");
17
-
18
- // Default brand guidelines path
19
- const DEFAULT_GUIDELINES_PATH = "docs/brand-guidelines.md";
20
-
21
- /**
22
- * Extract hex colors from text
23
- */
24
- function extractHexColors(text) {
25
- const hexPattern = /#[0-9A-Fa-f]{6}\b/g;
26
- return [...new Set(text.match(hexPattern) || [])];
27
- }
28
-
29
- /**
30
- * Extract color data from markdown table
31
- */
32
- function extractColorsFromTable(content) {
33
- const colors = {
34
- primary: [],
35
- secondary: [],
36
- neutral: [],
37
- semantic: [],
38
- };
39
-
40
- // Find color tables
41
- const primaryMatch = content.match(
42
- /### Primary Colors[\s\S]*?\|[\s\S]*?(?=###|$)/i
43
- );
44
- const secondaryMatch = content.match(
45
- /### Secondary Colors[\s\S]*?\|[\s\S]*?(?=###|$)/i
46
- );
47
- const neutralMatch = content.match(
48
- /### Neutral[\s\S]*?\|[\s\S]*?(?=###|$)/i
49
- );
50
- const semanticMatch = content.match(
51
- /### Semantic[\s\S]*?\|[\s\S]*?(?=###|$)/i
52
- );
53
-
54
- if (primaryMatch) colors.primary = extractHexColors(primaryMatch[0]);
55
- if (secondaryMatch) colors.secondary = extractHexColors(secondaryMatch[0]);
56
- if (neutralMatch) colors.neutral = extractHexColors(neutralMatch[0]);
57
- if (semanticMatch) colors.semantic = extractHexColors(semanticMatch[0]);
58
-
59
- return colors;
60
- }
61
-
62
- /**
63
- * Extract typography info
64
- */
65
- function extractTypography(content) {
66
- const typography = {
67
- heading: null,
68
- body: null,
69
- mono: null,
70
- };
71
-
72
- // Look for font definitions
73
- const headingMatch = content.match(/--font-heading:\s*['"]([^'"]+)['"]/);
74
- const bodyMatch = content.match(/--font-body:\s*['"]([^'"]+)['"]/);
75
- const monoMatch = content.match(/--font-mono:\s*['"]([^'"]+)['"]/);
76
-
77
- // Fallback: look in tables
78
- const fontStackMatch = content.match(/### Font Stack[\s\S]*?(?=###|##|$)/i);
79
- if (fontStackMatch) {
80
- const stackText = fontStackMatch[0];
81
- const headingAlt = stackText.match(/heading[^']*['"]([^'"]+)['"]/i);
82
- const bodyAlt = stackText.match(/body[^']*['"]([^'"]+)['"]/i);
83
-
84
- if (headingAlt) typography.heading = headingAlt[1];
85
- if (bodyAlt) typography.body = bodyAlt[1];
86
- }
87
-
88
- if (headingMatch) typography.heading = headingMatch[1];
89
- if (bodyMatch) typography.body = bodyMatch[1];
90
- if (monoMatch) typography.mono = monoMatch[1];
91
-
92
- return typography;
93
- }
94
-
95
- /**
96
- * Extract voice/tone information
97
- */
98
- function extractVoice(content) {
99
- const voice = {
100
- traits: [],
101
- prohibited: [],
102
- personality: "",
103
- };
104
-
105
- // Extract personality traits from table
106
- const personalityMatch = content.match(
107
- /### Brand Personality[\s\S]*?\|[\s\S]*?(?=###|##|$)/i
108
- );
109
- if (personalityMatch) {
110
- const traits = personalityMatch[0].match(
111
- /\*\*([^*]+)\*\*\s*\|\s*([^|]+)/g
112
- );
113
- if (traits) {
114
- voice.traits = traits.map((t) => {
115
- const match = t.match(/\*\*([^*]+)\*\*/);
116
- return match ? match[1].trim() : "";
117
- }).filter(Boolean);
118
- }
119
- }
120
-
121
- // Extract prohibited terms
122
- const prohibitedMatch = content.match(
123
- /### Prohibited[\s\S]*?(?=###|##|$)/i
124
- );
125
- if (prohibitedMatch) {
126
- const terms = prohibitedMatch[0].match(/\|\s*([^|]+)\s*\|/g);
127
- if (terms) {
128
- voice.prohibited = terms
129
- .map((t) => t.replace(/\|/g, "").trim())
130
- .filter((t) => t && !t.includes("Avoid") && !t.includes("---"));
131
- }
132
- }
133
-
134
- // Fallback: look for Forbidden Phrases
135
- const forbiddenMatch = content.match(
136
- /### Forbidden Phrases[\s\S]*?(?=###|##|$)/i
137
- );
138
- if (forbiddenMatch && voice.prohibited.length === 0) {
139
- const items = forbiddenMatch[0].match(/-\s*["']?([^"'\n(]+)/g);
140
- if (items) {
141
- voice.prohibited = items
142
- .map((item) => item.replace(/^-\s*["']?/, "").trim())
143
- .filter(Boolean);
144
- }
145
- }
146
-
147
- voice.personality = voice.traits.join(", ");
148
-
149
- return voice;
150
- }
151
-
152
- /**
153
- * Extract core attributes
154
- */
155
- function extractCoreAttributes(content) {
156
- const attributes = [];
157
-
158
- const attributesMatch = content.match(
159
- /### Core Attributes[\s\S]*?\|[\s\S]*?(?=###|##|$)/i
160
- );
161
- if (attributesMatch) {
162
- const rows = attributesMatch[0].match(
163
- /\|\s*\*\*([^*]+)\*\*\s*\|\s*([^|]+)\|/g
164
- );
165
- if (rows) {
166
- rows.forEach((row) => {
167
- const match = row.match(/\*\*([^*]+)\*\*\s*\|\s*([^|]+)/);
168
- if (match) {
169
- attributes.push({
170
- name: match[1].trim(),
171
- description: match[2].trim(),
172
- });
173
- }
174
- });
175
- }
176
- }
177
-
178
- return attributes;
179
- }
180
-
181
- /**
182
- * Extract AI image generation context
183
- */
184
- function extractImageStyle(content) {
185
- const imageStyle = {
186
- basePrompt: "",
187
- keywords: [],
188
- mood: [],
189
- donts: [],
190
- examplePrompts: [],
191
- };
192
-
193
- // Extract base prompt template (content between ``` blocks after "Base Prompt Template")
194
- const basePromptMatch = content.match(
195
- /### Base Prompt Template[\s\S]*?```\n?([\s\S]*?)```/i
196
- );
197
- if (basePromptMatch) {
198
- imageStyle.basePrompt = basePromptMatch[1].trim().replace(/\n/g, " ");
199
- }
200
-
201
- // Extract style keywords from table
202
- const keywordsMatch = content.match(
203
- /### Style Keywords[\s\S]*?\|[\s\S]*?(?=###|##|$)/i
204
- );
205
- if (keywordsMatch) {
206
- const keywordRows = keywordsMatch[0].match(/\|\s*\*\*[^*]+\*\*\s*\|\s*([^|]+)\|/g);
207
- if (keywordRows) {
208
- keywordRows.forEach((row) => {
209
- const match = row.match(/\|\s*\*\*[^*]+\*\*\s*\|\s*([^|]+)\|/);
210
- if (match) {
211
- const keywords = match[1].split(",").map((k) => k.trim()).filter(Boolean);
212
- imageStyle.keywords.push(...keywords);
213
- }
214
- });
215
- }
216
- }
217
-
218
- // Extract visual mood descriptors (bullet points)
219
- const moodMatch = content.match(
220
- /### Visual Mood Descriptors[\s\S]*?(?=###|##|$)/i
221
- );
222
- if (moodMatch) {
223
- const moodItems = moodMatch[0].match(/-\s*([^\n]+)/g);
224
- if (moodItems) {
225
- imageStyle.mood = moodItems.map((item) => item.replace(/^-\s*/, "").trim());
226
- }
227
- }
228
-
229
- // Extract visual don'ts from table
230
- const dontsMatch = content.match(
231
- /### Visual Don'ts[\s\S]*?\|[\s\S]*?(?=###|##|$)/i
232
- );
233
- if (dontsMatch) {
234
- const dontRows = dontsMatch[0].match(/\|\s*([^|]+)\s*\|\s*([^|]+)\s*\|/g);
235
- if (dontRows) {
236
- dontRows.forEach((row) => {
237
- const match = row.match(/\|\s*([^|]+)\s*\|\s*([^|]+)\s*\|/);
238
- if (match && !match[1].includes("Avoid") && !match[1].includes("---")) {
239
- imageStyle.donts.push(match[1].trim());
240
- }
241
- });
242
- }
243
- }
244
-
245
- // Extract example prompts (content between ``` blocks after specific headers)
246
- const exampleMatch = content.match(/### Example Prompts[\s\S]*?(?=##|$)/i);
247
- if (exampleMatch) {
248
- const prompts = exampleMatch[0].match(/\*\*([^*]+)\*\*:\s*```\n?([\s\S]*?)```/g);
249
- if (prompts) {
250
- prompts.forEach((p) => {
251
- const match = p.match(/\*\*([^*]+)\*\*:\s*```\n?([\s\S]*?)```/);
252
- if (match) {
253
- imageStyle.examplePrompts.push({
254
- type: match[1].trim(),
255
- prompt: match[2].trim().replace(/\n/g, " "),
256
- });
257
- }
258
- });
259
- }
260
- }
261
-
262
- return imageStyle;
263
- }
264
-
265
- /**
266
- * Generate system prompt addition
267
- */
268
- function generatePromptAddition(brandContext) {
269
- const { colors, typography, voice, attributes, imageStyle } = brandContext;
270
-
271
- let prompt = `
272
- BRAND CONTEXT:
273
- ==============
274
-
275
- VISUAL IDENTITY:
276
- - Primary Colors: ${colors.primary.join(", ") || "Not specified"}
277
- - Secondary Colors: ${colors.secondary.join(", ") || "Not specified"}
278
- - Typography: ${typography.heading || typography.body || "System fonts"}
279
-
280
- BRAND VOICE:
281
- - Personality: ${voice.personality || "Professional"}
282
- - Core Attributes: ${attributes.map((a) => a.name).join(", ") || "Not specified"}
283
-
284
- CONTENT RULES:
285
- - Prohibited Terms: ${voice.prohibited.join(", ") || "None specified"}
286
- `;
287
-
288
- // Add image style context if available
289
- if (imageStyle && imageStyle.basePrompt) {
290
- prompt += `
291
- IMAGE GENERATION:
292
- - Base Prompt: ${imageStyle.basePrompt}
293
- - Style Keywords: ${imageStyle.keywords.slice(0, 10).join(", ") || "Not specified"}
294
- - Visual Mood: ${imageStyle.mood.slice(0, 5).join("; ") || "Not specified"}
295
- - Avoid: ${imageStyle.donts.join(", ") || "None specified"}
296
- `;
297
- }
298
-
299
- prompt += `
300
- Apply these brand guidelines to all generated content.
301
- Maintain consistent voice, colors, and messaging.
302
- `;
303
-
304
- return prompt.trim();
305
- }
306
-
307
- /**
308
- * Main function
309
- */
310
- function main() {
311
- const args = process.argv.slice(2);
312
- const jsonOutput = args.includes("--json");
313
- const guidelinesPath = args.find((a) => !a.startsWith("--")) || DEFAULT_GUIDELINES_PATH;
314
-
315
- // Resolve path
316
- const resolvedPath = path.isAbsolute(guidelinesPath)
317
- ? guidelinesPath
318
- : path.join(process.cwd(), guidelinesPath);
319
-
320
- // Check if file exists
321
- if (!fs.existsSync(resolvedPath)) {
322
- console.error(`Error: Brand guidelines not found at ${resolvedPath}`);
323
- console.error(`Create brand guidelines at ${DEFAULT_GUIDELINES_PATH} or specify a path.`);
324
- process.exit(1);
325
- }
326
-
327
- // Read file
328
- const content = fs.readFileSync(resolvedPath, "utf-8");
329
-
330
- // Extract brand context
331
- const brandContext = {
332
- colors: extractColorsFromTable(content),
333
- typography: extractTypography(content),
334
- voice: extractVoice(content),
335
- attributes: extractCoreAttributes(content),
336
- imageStyle: extractImageStyle(content),
337
- source: resolvedPath,
338
- extractedAt: new Date().toISOString(),
339
- };
340
-
341
- // Output
342
- if (jsonOutput) {
343
- console.log(JSON.stringify(brandContext, null, 2));
344
- } else {
345
- console.log(generatePromptAddition(brandContext));
346
- }
347
- }
348
-
349
- main();
@@ -1,248 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * sync-brand-to-tokens.cjs
4
- *
5
- * Syncs brand-guidelines.md colors → design-tokens.json → design-tokens.css
6
- *
7
- * Usage:
8
- * node sync-brand-to-tokens.cjs
9
- * node sync-brand-to-tokens.cjs --dry-run
10
- */
11
-
12
- const fs = require('fs');
13
- const path = require('path');
14
- const { execFileSync } = require('child_process');
15
-
16
- // Paths
17
- const BRAND_GUIDELINES = 'docs/brand-guidelines.md';
18
- const DESIGN_TOKENS_JSON = 'assets/design-tokens.json';
19
- const DESIGN_TOKENS_CSS = 'assets/design-tokens.css';
20
- const GENERATE_TOKENS_SCRIPT = '.claude/skills/design-system/scripts/generate-tokens.cjs';
21
-
22
- /**
23
- * Extract color info from brand guidelines markdown
24
- */
25
- function extractColorsFromMarkdown(content) {
26
- const colors = {
27
- primary: { name: 'primary', shades: {} },
28
- secondary: { name: 'secondary', shades: {} },
29
- accent: { name: 'accent', shades: {} }
30
- };
31
-
32
- // Match a "| Label | #hex |" markdown table row. Bold around the label
33
- // (**Label**) is optional, so this handles both the bundled starter template
34
- // ("| Primary Blue | #2563EB |") and bolded variants.
35
- const rowRe = /\|\s*\*{0,2}([^*|]+?)\*{0,2}\s*\|\s*#([A-Fa-f0-9]{6})\b/g;
36
-
37
- // 1) Quick Reference table — hex only, no parenthesized name required.
38
- const quickRef = {
39
- primary: /Primary Color\s*\|\s*#([A-Fa-f0-9]{6})/i,
40
- secondary: /Secondary Color\s*\|\s*#([A-Fa-f0-9]{6})/i,
41
- accent: /Accent Color\s*\|\s*#([A-Fa-f0-9]{6})/i
42
- };
43
- for (const key of Object.keys(quickRef)) {
44
- const m = content.match(quickRef[key]);
45
- if (m) colors[key].base = `#${m[1]}`;
46
- }
47
-
48
- // 2) Dedicated "### <Role> Colors" tables — assign base/dark/light by the
49
- // row label keyword.
50
- const assignFromSection = (heading, target) => {
51
- const section = content.match(new RegExp(`### ${heading}[\\s\\S]*?(?=\\n###|$)`, 'i'));
52
- if (!section) return;
53
- for (const m of section[0].matchAll(rowRe)) {
54
- const label = m[1].trim().toLowerCase();
55
- const hex = `#${m[2]}`;
56
- if (label.includes('dark')) target.dark = hex;
57
- else if (label.includes('light')) target.light = hex;
58
- else if (!target.base) target.base = hex;
59
- }
60
- };
61
- assignFromSection('Primary Colors', colors.primary);
62
- assignFromSection('Secondary Colors', colors.secondary);
63
- assignFromSection('Accent Colors', colors.accent);
64
-
65
- // 3) Fallback: an accent swatch may live in another table (the starter
66
- // lists "Accent Green" under Secondary Colors).
67
- if (!colors.accent.base) {
68
- for (const m of content.matchAll(rowRe)) {
69
- if (m[1].trim().toLowerCase().includes('accent')) {
70
- colors.accent.base = `#${m[2]}`;
71
- break;
72
- }
73
- }
74
- }
75
-
76
- return colors;
77
- }
78
-
79
- /**
80
- * Generate color scale from base color (simple approach)
81
- */
82
- function generateColorScale(baseHex, darkHex, lightHex) {
83
- // Use provided shades or generate approximations
84
- return {
85
- "50": { "$value": lightHex || adjustBrightness(baseHex, 0.9), "$type": "color" },
86
- "100": { "$value": lightHex || adjustBrightness(baseHex, 0.8), "$type": "color" },
87
- "200": { "$value": adjustBrightness(baseHex, 0.6), "$type": "color" },
88
- "300": { "$value": adjustBrightness(baseHex, 0.4), "$type": "color" },
89
- "400": { "$value": adjustBrightness(baseHex, 0.2), "$type": "color" },
90
- "500": { "$value": baseHex, "$type": "color" },
91
- "600": { "$value": darkHex || adjustBrightness(baseHex, -0.15), "$type": "color" },
92
- "700": { "$value": adjustBrightness(baseHex, -0.3), "$type": "color" },
93
- "800": { "$value": adjustBrightness(baseHex, -0.45), "$type": "color" },
94
- "900": { "$value": adjustBrightness(baseHex, -0.6), "$type": "color" }
95
- };
96
- }
97
-
98
- /**
99
- * Adjust hex color brightness
100
- */
101
- function adjustBrightness(hex, percent) {
102
- if (typeof hex !== 'string') return '#000000';
103
- const num = parseInt(hex.replace('#', ''), 16);
104
- const r = Math.min(255, Math.max(0, (num >> 16) + Math.round(255 * percent)));
105
- const g = Math.min(255, Math.max(0, ((num >> 8) & 0x00FF) + Math.round(255 * percent)));
106
- const b = Math.min(255, Math.max(0, (num & 0x0000FF) + Math.round(255 * percent)));
107
- return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0').toUpperCase()}`;
108
- }
109
-
110
- /**
111
- * Update design tokens JSON
112
- */
113
- function updateDesignTokens(tokens, colors) {
114
- // Update brand name
115
- const brandName = `ClaudeKit Marketing - ${colors.primary.name.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')}`;
116
- tokens.brand = brandName;
117
-
118
- // Update primitive colors with new names
119
- tokens.primitive = tokens.primitive || {};
120
- const primitiveColors = tokens.primitive.color || {};
121
-
122
- // Remove old color keys, add new ones
123
- delete primitiveColors.coral;
124
- delete primitiveColors.purple;
125
- delete primitiveColors.mint;
126
-
127
- // Add new named colors. Skip any role with no base hex rather than crashing
128
- // on an unexpected guidelines format.
129
- for (const role of ['primary', 'secondary', 'accent']) {
130
- const c = colors[role];
131
- if (!c.base) {
132
- console.warn(`āš ļø No base hex found for ${role} color — skipping its token scale.`);
133
- continue;
134
- }
135
- primitiveColors[c.name] = generateColorScale(c.base, c.dark, c.light);
136
- }
137
-
138
- tokens.primitive.color = primitiveColors;
139
-
140
- // Update ALL semantic color references
141
- if (tokens.semantic?.color) {
142
- const sem = tokens.semantic.color;
143
- const p = colors.primary.name;
144
- const s = colors.secondary.name;
145
- const a = colors.accent.name;
146
-
147
- // Primary variants
148
- sem.primary = { "$value": `{primitive.color.${p}.500}`, "$type": "color" };
149
- sem['primary-hover'] = { "$value": `{primitive.color.${p}.600}`, "$type": "color" };
150
- sem['primary-active'] = { "$value": `{primitive.color.${p}.700}`, "$type": "color" };
151
- sem['primary-light'] = { "$value": `{primitive.color.${p}.400}`, "$type": "color" };
152
- sem['primary-lighter'] = { "$value": `{primitive.color.${p}.100}`, "$type": "color" };
153
- sem['primary-dark'] = { "$value": `{primitive.color.${p}.600}`, "$type": "color" };
154
-
155
- // Secondary variants
156
- sem.secondary = { "$value": `{primitive.color.${s}.500}`, "$type": "color" };
157
- sem['secondary-hover'] = { "$value": `{primitive.color.${s}.600}`, "$type": "color" };
158
- sem['secondary-light'] = { "$value": `{primitive.color.${s}.300}`, "$type": "color" };
159
- sem['secondary-dark'] = { "$value": `{primitive.color.${s}.600}`, "$type": "color" };
160
-
161
- // Accent variants
162
- sem.accent = { "$value": `{primitive.color.${a}.500}`, "$type": "color" };
163
- sem['accent-hover'] = { "$value": `{primitive.color.${a}.600}`, "$type": "color" };
164
- sem['accent-light'] = { "$value": `{primitive.color.${a}.300}`, "$type": "color" };
165
-
166
- // Status colors (use accent for success, primary for error/info)
167
- sem.success = { "$value": `{primitive.color.${a}.500}`, "$type": "color" };
168
- sem['success-light'] = { "$value": `{primitive.color.${a}.300}`, "$type": "color" };
169
- sem.error = { "$value": `{primitive.color.${p}.500}`, "$type": "color" };
170
- sem['error-light'] = { "$value": `{primitive.color.${p}.300}`, "$type": "color" };
171
- sem.info = { "$value": `{primitive.color.${s}.500}`, "$type": "color" };
172
- sem['info-light'] = { "$value": `{primitive.color.${s}.300}`, "$type": "color" };
173
- }
174
-
175
- // Update component references (button uses primary color with opacity)
176
- if (tokens.component?.button?.secondary && colors.primary.base) {
177
- const primaryBase = colors.primary.base;
178
- tokens.component.button.secondary['bg-hover'] = {
179
- "$value": `${primaryBase}1A`,
180
- "$type": "color"
181
- };
182
- }
183
-
184
- return tokens;
185
- }
186
-
187
- /**
188
- * Main
189
- */
190
- function main() {
191
- const dryRun = process.argv.includes('--dry-run');
192
-
193
- console.log('šŸ”„ Syncing brand guidelines → design tokens\n');
194
-
195
- // Read brand guidelines
196
- const guidelinesPath = path.resolve(process.cwd(), BRAND_GUIDELINES);
197
- if (!fs.existsSync(guidelinesPath)) {
198
- console.error(`āŒ Brand guidelines not found: ${guidelinesPath}`);
199
- process.exit(1);
200
- }
201
- const guidelinesContent = fs.readFileSync(guidelinesPath, 'utf-8');
202
-
203
- // Extract colors
204
- const colors = extractColorsFromMarkdown(guidelinesContent);
205
- console.log('šŸ“Š Extracted colors:');
206
- console.log(` Primary: ${colors.primary.name} (${colors.primary.base})`);
207
- console.log(` Secondary: ${colors.secondary.name} (${colors.secondary.base})`);
208
- console.log(` Accent: ${colors.accent.name} (${colors.accent.base})\n`);
209
-
210
- // Read existing tokens
211
- const tokensPath = path.resolve(process.cwd(), DESIGN_TOKENS_JSON);
212
- let tokens = {};
213
- if (fs.existsSync(tokensPath)) {
214
- tokens = JSON.parse(fs.readFileSync(tokensPath, 'utf-8'));
215
- }
216
-
217
- // Update tokens
218
- tokens = updateDesignTokens(tokens, colors);
219
-
220
- if (dryRun) {
221
- console.log('šŸ“‹ Would update design-tokens.json:');
222
- console.log(JSON.stringify(tokens.primitive.color, null, 2).slice(0, 500) + '...');
223
- console.log('\nā­ļø Dry run - no files changed');
224
- return;
225
- }
226
-
227
- // Write updated tokens
228
- fs.writeFileSync(tokensPath, JSON.stringify(tokens, null, 2));
229
- console.log(`āœ… Updated: ${DESIGN_TOKENS_JSON}`);
230
-
231
- // Regenerate CSS
232
- const generateScript = path.resolve(process.cwd(), GENERATE_TOKENS_SCRIPT);
233
- if (fs.existsSync(generateScript)) {
234
- try {
235
- execFileSync('node', [generateScript, '--config', DESIGN_TOKENS_JSON, '-o', DESIGN_TOKENS_CSS], {
236
- cwd: process.cwd(),
237
- stdio: 'inherit'
238
- });
239
- console.log(`āœ… Regenerated: ${DESIGN_TOKENS_CSS}`);
240
- } catch (e) {
241
- console.error('āš ļø Failed to regenerate CSS:', e.message);
242
- }
243
- }
244
-
245
- console.log('\n✨ Brand sync complete!');
246
- }
247
-
248
- main();
@@ -1,52 +0,0 @@
1
- """Regression test for sync-brand-to-tokens.cjs.
2
-
3
- The color parser required a parenthesized name in the Quick Reference row
4
- (`#2563EB (name)`) and a bolded label in the color tables (`**Primary Blue**`),
5
- neither of which the bundled starter template uses. As a result the base hex
6
- came back `undefined` and `adjustBrightness(undefined)` threw a TypeError —
7
- i.e. the script crashed on its own documented happy path. This test runs the
8
- sync against the bundled starter template and asserts it completes and writes
9
- the expected base colors. It is pytest-based so the existing pytest CI runs it.
10
- """
11
-
12
- import json
13
- import shutil
14
- import subprocess
15
- from pathlib import Path
16
-
17
- import pytest
18
-
19
- SCRIPTS = Path(__file__).resolve().parent.parent
20
- SCRIPT = SCRIPTS / "sync-brand-to-tokens.cjs"
21
- BRAND_STARTER = SCRIPTS.parent / "templates" / "brand-guidelines-starter.md"
22
- TOKENS_STARTER = (
23
- SCRIPTS.parent.parent / "design-system" / "templates" / "design-tokens-starter.json"
24
- )
25
-
26
-
27
- def test_sync_parses_bundled_starter_template(tmp_path):
28
- node = shutil.which("node")
29
- if not node:
30
- pytest.skip("node not available")
31
-
32
- (tmp_path / "docs").mkdir()
33
- (tmp_path / "assets").mkdir()
34
- shutil.copy(BRAND_STARTER, tmp_path / "docs" / "brand-guidelines.md")
35
- shutil.copy(TOKENS_STARTER, tmp_path / "assets" / "design-tokens.json")
36
-
37
- result = subprocess.run(
38
- [node, str(SCRIPT)],
39
- cwd=tmp_path,
40
- capture_output=True,
41
- text=True,
42
- )
43
-
44
- # Must not crash (the bug raised an unhandled TypeError).
45
- assert "TypeError" not in result.stderr, result.stderr
46
- assert result.returncode == 0, result.stderr + result.stdout
47
-
48
- tokens = json.loads((tmp_path / "assets" / "design-tokens.json").read_text())
49
- primitive = tokens["primitive"]["color"]
50
- assert primitive["primary"]["500"]["$value"] == "#2563EB"
51
- assert primitive["secondary"]["500"]["$value"] == "#8B5CF6"
52
- assert primitive["accent"]["500"]["$value"] == "#10B981"