@jack200714/mafw 4.5.1 → 4.8.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 (330) hide show
  1. package/README.md +5 -2
  2. package/bin/mafw.js +16 -14
  3. package/gateway/dist/index.js +52 -38
  4. package/gateway/dist/media/media-plugin-loader.js +0 -12
  5. package/gateway/dist/plugins/hub.js +153 -19
  6. package/gateway/dist/routes/plugins.js +15 -5
  7. package/gateway/dist/runtime/event-broadcast.js +33 -0
  8. package/gateway/dist/runtime/loader.js +23 -13
  9. package/gateway/dist/usage/builtin-plugins/gateway.js +25 -3
  10. package/gateway/dist/usage/plugin-loader.js +0 -11
  11. package/gateway/package.json +2 -2
  12. package/package.json +1 -7
  13. package/packages/tui/dist/cli.js +8 -3
  14. package/.opencode/mafw/.backup-v61/parametric/base-skill-manifest.yaml +0 -2
  15. package/.opencode/mafw/STATUS.md +0 -6
  16. package/.opencode/mafw/ledger.md +0 -20
  17. package/.opencode/mafw/memory/.harmonic_index.json +0 -28
  18. package/.opencode/mafw/memory/.review_queue.json +0 -1
  19. package/.opencode/mafw/memory/tier3/parametric.json +0 -13
  20. package/.opencode/mafw/memory/tier3/test-goal.json +0 -19
  21. package/.opencode/mafw/memory-index.json +0 -6
  22. package/.opencode/mafw/parametric/base-skill-manifest.yaml +0 -2
  23. package/.opencode/mafw/waves.json +0 -4
  24. package/.opencode/opencode.json +0 -3
  25. package/.opencode/plugins/mafw-plugin.ts +0 -8
  26. package/.opencode/skills/banner-design/SKILL.md +0 -196
  27. package/.opencode/skills/banner-design/references/banner-sizes-and-styles.md +0 -118
  28. package/.opencode/skills/brand/SKILL.md +0 -97
  29. package/.opencode/skills/brand/references/approval-checklist.md +0 -169
  30. package/.opencode/skills/brand/references/asset-organization.md +0 -157
  31. package/.opencode/skills/brand/references/brand-guideline-template.md +0 -140
  32. package/.opencode/skills/brand/references/color-palette-management.md +0 -186
  33. package/.opencode/skills/brand/references/consistency-checklist.md +0 -94
  34. package/.opencode/skills/brand/references/logo-usage-rules.md +0 -185
  35. package/.opencode/skills/brand/references/messaging-framework.md +0 -85
  36. package/.opencode/skills/brand/references/typography-specifications.md +0 -214
  37. package/.opencode/skills/brand/references/update.md +0 -118
  38. package/.opencode/skills/brand/references/visual-identity.md +0 -96
  39. package/.opencode/skills/brand/references/voice-framework.md +0 -88
  40. package/.opencode/skills/brand/scripts/extract-colors.cjs +0 -341
  41. package/.opencode/skills/brand/scripts/inject-brand-context.cjs +0 -349
  42. package/.opencode/skills/brand/scripts/sync-brand-to-tokens.cjs +0 -248
  43. package/.opencode/skills/brand/scripts/tests/test_sync_brand_to_tokens.py +0 -52
  44. package/.opencode/skills/brand/scripts/validate-asset.cjs +0 -387
  45. package/.opencode/skills/brand/templates/brand-guidelines-starter.md +0 -275
  46. package/.opencode/skills/design/SKILL.md +0 -313
  47. package/.opencode/skills/design/data/cip/deliverables.csv +0 -51
  48. package/.opencode/skills/design/data/cip/industries.csv +0 -21
  49. package/.opencode/skills/design/data/cip/mockup-contexts.csv +0 -21
  50. package/.opencode/skills/design/data/cip/styles.csv +0 -21
  51. package/.opencode/skills/design/data/icon/styles.csv +0 -16
  52. package/.opencode/skills/design/data/logo/colors.csv +0 -56
  53. package/.opencode/skills/design/data/logo/industries.csv +0 -56
  54. package/.opencode/skills/design/data/logo/styles.csv +0 -56
  55. package/.opencode/skills/design/references/banner-sizes-and-styles.md +0 -118
  56. package/.opencode/skills/design/references/cip-deliverable-guide.md +0 -95
  57. package/.opencode/skills/design/references/cip-design.md +0 -121
  58. package/.opencode/skills/design/references/cip-prompt-engineering.md +0 -84
  59. package/.opencode/skills/design/references/cip-style-guide.md +0 -68
  60. package/.opencode/skills/design/references/design-routing.md +0 -207
  61. package/.opencode/skills/design/references/icon-design.md +0 -122
  62. package/.opencode/skills/design/references/logo-color-psychology.md +0 -101
  63. package/.opencode/skills/design/references/logo-design.md +0 -92
  64. package/.opencode/skills/design/references/logo-prompt-engineering.md +0 -158
  65. package/.opencode/skills/design/references/logo-style-guide.md +0 -109
  66. package/.opencode/skills/design/references/slides-copywriting-formulas.md +0 -84
  67. package/.opencode/skills/design/references/slides-create.md +0 -4
  68. package/.opencode/skills/design/references/slides-html-template.md +0 -295
  69. package/.opencode/skills/design/references/slides-layout-patterns.md +0 -137
  70. package/.opencode/skills/design/references/slides-strategies.md +0 -94
  71. package/.opencode/skills/design/references/slides.md +0 -42
  72. package/.opencode/skills/design/references/social-photos-design.md +0 -329
  73. package/.opencode/skills/design/scripts/cip/core.py +0 -215
  74. package/.opencode/skills/design/scripts/cip/generate.py +0 -484
  75. package/.opencode/skills/design/scripts/cip/render-html.py +0 -424
  76. package/.opencode/skills/design/scripts/cip/search.py +0 -127
  77. package/.opencode/skills/design/scripts/icon/generate.py +0 -487
  78. package/.opencode/skills/design/scripts/logo/core.py +0 -175
  79. package/.opencode/skills/design/scripts/logo/generate.py +0 -362
  80. package/.opencode/skills/design/scripts/logo/search.py +0 -114
  81. package/.opencode/skills/design-system/SKILL.md +0 -244
  82. package/.opencode/skills/design-system/data/slide-backgrounds.csv +0 -11
  83. package/.opencode/skills/design-system/data/slide-charts.csv +0 -26
  84. package/.opencode/skills/design-system/data/slide-color-logic.csv +0 -14
  85. package/.opencode/skills/design-system/data/slide-copy.csv +0 -26
  86. package/.opencode/skills/design-system/data/slide-layout-logic.csv +0 -16
  87. package/.opencode/skills/design-system/data/slide-layouts.csv +0 -26
  88. package/.opencode/skills/design-system/data/slide-strategies.csv +0 -16
  89. package/.opencode/skills/design-system/data/slide-typography.csv +0 -15
  90. package/.opencode/skills/design-system/references/component-specs.md +0 -236
  91. package/.opencode/skills/design-system/references/component-tokens.md +0 -214
  92. package/.opencode/skills/design-system/references/primitive-tokens.md +0 -203
  93. package/.opencode/skills/design-system/references/semantic-tokens.md +0 -215
  94. package/.opencode/skills/design-system/references/states-and-variants.md +0 -241
  95. package/.opencode/skills/design-system/references/tailwind-integration.md +0 -251
  96. package/.opencode/skills/design-system/references/token-architecture.md +0 -224
  97. package/.opencode/skills/design-system/scripts/embed-tokens.cjs +0 -99
  98. package/.opencode/skills/design-system/scripts/fetch-background.py +0 -317
  99. package/.opencode/skills/design-system/scripts/generate-slide.py +0 -770
  100. package/.opencode/skills/design-system/scripts/generate-tokens.cjs +0 -205
  101. package/.opencode/skills/design-system/scripts/html-token-validator.py +0 -327
  102. package/.opencode/skills/design-system/scripts/search-slides.py +0 -218
  103. package/.opencode/skills/design-system/scripts/slide-token-validator.py +0 -35
  104. package/.opencode/skills/design-system/scripts/slide_search_core.py +0 -453
  105. package/.opencode/skills/design-system/scripts/tests/test_validate_tokens.py +0 -48
  106. package/.opencode/skills/design-system/scripts/validate-tokens.cjs +0 -246
  107. package/.opencode/skills/design-system/templates/design-tokens-starter.json +0 -143
  108. package/.opencode/skills/frontend-design/LICENSE.txt +0 -21
  109. package/.opencode/skills/frontend-design/SKILL.md +0 -115
  110. package/.opencode/skills/mafw-automation-interview/SKILL.md +0 -43
  111. package/.opencode/skills/mafw-cli-scanner/SKILL.md +0 -37
  112. package/.opencode/skills/mafw-compression-verifier/SKILL.md +0 -52
  113. package/.opencode/skills/mafw-desktop-inspector/SKILL.md +0 -170
  114. package/.opencode/skills/mafw-execute/SKILL.md +0 -62
  115. package/.opencode/skills/mafw-gateway-restart/SKILL.md +0 -87
  116. package/.opencode/skills/mafw-github-scanner/SKILL.md +0 -38
  117. package/.opencode/skills/mafw-goal/SKILL.md +0 -76
  118. package/.opencode/skills/mafw-interview/SKILL.md +0 -68
  119. package/.opencode/skills/mafw-memory-extractor/SKILL.md +0 -65
  120. package/.opencode/skills/mafw-plan/SKILL.md +0 -58
  121. package/.opencode/skills/mafw-review/SKILL.md +0 -85
  122. package/.opencode/skills/runtime-plugin-authoring/SKILL.md +0 -791
  123. package/.opencode/skills/slides/SKILL.md +0 -40
  124. package/.opencode/skills/slides/references/copywriting-formulas.md +0 -84
  125. package/.opencode/skills/slides/references/create.md +0 -4
  126. package/.opencode/skills/slides/references/html-template.md +0 -295
  127. package/.opencode/skills/slides/references/layout-patterns.md +0 -137
  128. package/.opencode/skills/slides/references/slide-strategies.md +0 -94
  129. package/.opencode/skills/ui-styling/LICENSE.txt +0 -202
  130. package/.opencode/skills/ui-styling/SKILL.md +0 -324
  131. package/.opencode/skills/ui-styling/references/canvas-design-system.md +0 -320
  132. package/.opencode/skills/ui-styling/references/shadcn-accessibility.md +0 -471
  133. package/.opencode/skills/ui-styling/references/shadcn-components.md +0 -424
  134. package/.opencode/skills/ui-styling/references/shadcn-theming.md +0 -373
  135. package/.opencode/skills/ui-styling/references/tailwind-customization.md +0 -483
  136. package/.opencode/skills/ui-styling/references/tailwind-responsive.md +0 -382
  137. package/.opencode/skills/ui-styling/references/tailwind-utilities.md +0 -455
  138. package/.opencode/skills/ui-styling/scripts/requirements.txt +0 -17
  139. package/.opencode/skills/ui-styling/scripts/shadcn_add.py +0 -308
  140. package/.opencode/skills/ui-styling/scripts/tailwind_config_gen.py +0 -473
  141. package/.opencode/skills/ui-styling/scripts/tests/coverage-ui.json +0 -1
  142. package/.opencode/skills/ui-styling/scripts/tests/requirements.txt +0 -3
  143. package/.opencode/skills/ui-styling/scripts/tests/test_shadcn_add.py +0 -266
  144. package/.opencode/skills/ui-styling/scripts/tests/test_tailwind_config_gen.py +0 -394
  145. package/.opencode/skills/ui-ux-pro-max/SKILL.md +0 -388
  146. package/.opencode/skills/ui-ux-pro-max/data/app-interface.csv +0 -31
  147. package/.opencode/skills/ui-ux-pro-max/data/charts.csv +0 -26
  148. package/.opencode/skills/ui-ux-pro-max/data/colors.csv +0 -193
  149. package/.opencode/skills/ui-ux-pro-max/data/google-fonts.csv +0 -1924
  150. package/.opencode/skills/ui-ux-pro-max/data/icons.csv +0 -106
  151. package/.opencode/skills/ui-ux-pro-max/data/landing.csv +0 -35
  152. package/.opencode/skills/ui-ux-pro-max/data/motion.csv +0 -17
  153. package/.opencode/skills/ui-ux-pro-max/data/products.csv +0 -193
  154. package/.opencode/skills/ui-ux-pro-max/data/react-performance.csv +0 -45
  155. package/.opencode/skills/ui-ux-pro-max/data/stacks/angular.csv +0 -51
  156. package/.opencode/skills/ui-ux-pro-max/data/stacks/astro.csv +0 -54
  157. package/.opencode/skills/ui-ux-pro-max/data/stacks/avalonia.csv +0 -57
  158. package/.opencode/skills/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  159. package/.opencode/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  160. package/.opencode/skills/ui-ux-pro-max/data/stacks/javafx.csv +0 -76
  161. package/.opencode/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
  162. package/.opencode/skills/ui-ux-pro-max/data/stacks/laravel.csv +0 -51
  163. package/.opencode/skills/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  164. package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -71
  165. package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  166. package/.opencode/skills/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  167. package/.opencode/skills/ui-ux-pro-max/data/stacks/react.csv +0 -54
  168. package/.opencode/skills/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
  169. package/.opencode/skills/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  170. package/.opencode/skills/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  171. package/.opencode/skills/ui-ux-pro-max/data/stacks/threejs.csv +0 -54
  172. package/.opencode/skills/ui-ux-pro-max/data/stacks/uno.csv +0 -60
  173. package/.opencode/skills/ui-ux-pro-max/data/stacks/uwp.csv +0 -56
  174. package/.opencode/skills/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  175. package/.opencode/skills/ui-ux-pro-max/data/stacks/winui.csv +0 -60
  176. package/.opencode/skills/ui-ux-pro-max/data/stacks/wpf.csv +0 -57
  177. package/.opencode/skills/ui-ux-pro-max/data/styles.csv +0 -85
  178. package/.opencode/skills/ui-ux-pro-max/data/typography.csv +0 -75
  179. package/.opencode/skills/ui-ux-pro-max/data/ui-reasoning.csv +0 -162
  180. package/.opencode/skills/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  181. package/.opencode/skills/ui-ux-pro-max/scripts/core.py +0 -464
  182. package/.opencode/skills/ui-ux-pro-max/scripts/design_system.py +0 -1479
  183. package/.opencode/skills/ui-ux-pro-max/scripts/search.py +0 -162
  184. package/.opencode/skills/ui-ux-pro-max/scripts/tests/test_core.py +0 -134
  185. package/.opencode/skills/ui-ux-pro-max/scripts/tests/test_design_system_mode.py +0 -159
  186. package/.opencode/skills/ui-ux-pro-max/scripts/validate_data.py +0 -114
  187. package/dist/hooks/bash-python-guide.d.ts +0 -4
  188. package/dist/hooks/bash-python-guide.d.ts.map +0 -1
  189. package/dist/hooks/bash-python-guide.js +0 -67
  190. package/dist/hooks/bash-python-guide.js.map +0 -1
  191. package/dist/hooks/handoff.d.ts +0 -9
  192. package/dist/hooks/handoff.d.ts.map +0 -1
  193. package/dist/hooks/handoff.js +0 -8
  194. package/dist/hooks/handoff.js.map +0 -1
  195. package/dist/hooks/hook-manager.d.ts +0 -32
  196. package/dist/hooks/hook-manager.d.ts.map +0 -1
  197. package/dist/hooks/hook-manager.js +0 -91
  198. package/dist/hooks/hook-manager.js.map +0 -1
  199. package/dist/hooks/llm-after.d.ts +0 -8
  200. package/dist/hooks/llm-after.d.ts.map +0 -1
  201. package/dist/hooks/llm-after.js +0 -8
  202. package/dist/hooks/llm-after.js.map +0 -1
  203. package/dist/hooks/media-ingest.d.ts +0 -43
  204. package/dist/hooks/media-ingest.d.ts.map +0 -1
  205. package/dist/hooks/media-ingest.js +0 -269
  206. package/dist/hooks/media-ingest.js.map +0 -1
  207. package/dist/hooks/memory-guide.d.ts +0 -3
  208. package/dist/hooks/memory-guide.d.ts.map +0 -1
  209. package/dist/hooks/memory-guide.js +0 -47
  210. package/dist/hooks/memory-guide.js.map +0 -1
  211. package/dist/hooks/observation-capture.d.ts +0 -17
  212. package/dist/hooks/observation-capture.d.ts.map +0 -1
  213. package/dist/hooks/observation-capture.js +0 -20
  214. package/dist/hooks/observation-capture.js.map +0 -1
  215. package/dist/hooks/session-compacting.d.ts +0 -6
  216. package/dist/hooks/session-compacting.d.ts.map +0 -1
  217. package/dist/hooks/session-compacting.js +0 -6
  218. package/dist/hooks/session-compacting.js.map +0 -1
  219. package/dist/hooks/session-ending.d.ts +0 -20
  220. package/dist/hooks/session-ending.d.ts.map +0 -1
  221. package/dist/hooks/session-ending.js +0 -113
  222. package/dist/hooks/session-ending.js.map +0 -1
  223. package/dist/hooks/session-recall.d.ts +0 -2
  224. package/dist/hooks/session-recall.d.ts.map +0 -1
  225. package/dist/hooks/session-recall.js +0 -103
  226. package/dist/hooks/session-recall.js.map +0 -1
  227. package/dist/hooks/session-start.d.ts +0 -9
  228. package/dist/hooks/session-start.d.ts.map +0 -1
  229. package/dist/hooks/session-start.js +0 -42
  230. package/dist/hooks/session-start.js.map +0 -1
  231. package/dist/hooks/tool-before.d.ts +0 -10
  232. package/dist/hooks/tool-before.d.ts.map +0 -1
  233. package/dist/hooks/tool-before.js +0 -6
  234. package/dist/hooks/tool-before.js.map +0 -1
  235. package/dist/hooks/tool-executed.d.ts +0 -19
  236. package/dist/hooks/tool-executed.d.ts.map +0 -1
  237. package/dist/hooks/tool-executed.js +0 -12
  238. package/dist/hooks/tool-executed.js.map +0 -1
  239. package/dist/hooks/user-profile.d.ts +0 -2
  240. package/dist/hooks/user-profile.d.ts.map +0 -1
  241. package/dist/hooks/user-profile.js +0 -30
  242. package/dist/hooks/user-profile.js.map +0 -1
  243. package/dist/hooks/user-prompt.d.ts +0 -9
  244. package/dist/hooks/user-prompt.d.ts.map +0 -1
  245. package/dist/hooks/user-prompt.js +0 -8
  246. package/dist/hooks/user-prompt.js.map +0 -1
  247. package/dist/hooks/voice-guide.d.ts +0 -8
  248. package/dist/hooks/voice-guide.d.ts.map +0 -1
  249. package/dist/hooks/voice-guide.js +0 -101
  250. package/dist/hooks/voice-guide.js.map +0 -1
  251. package/dist/plugin.d.ts +0 -55
  252. package/dist/plugin.d.ts.map +0 -1
  253. package/dist/plugin.js +0 -342
  254. package/dist/plugin.js.map +0 -1
  255. package/dist/tools/add-memory.d.ts +0 -17
  256. package/dist/tools/add-memory.d.ts.map +0 -1
  257. package/dist/tools/add-memory.js +0 -60
  258. package/dist/tools/add-memory.js.map +0 -1
  259. package/dist/tools/media-ask.d.ts +0 -14
  260. package/dist/tools/media-ask.d.ts.map +0 -1
  261. package/dist/tools/media-ask.js +0 -224
  262. package/dist/tools/media-ask.js.map +0 -1
  263. package/dist/tools/media-speak.d.ts +0 -16
  264. package/dist/tools/media-speak.d.ts.map +0 -1
  265. package/dist/tools/media-speak.js +0 -78
  266. package/dist/tools/media-speak.js.map +0 -1
  267. package/dist/tools/media-upload.d.ts +0 -14
  268. package/dist/tools/media-upload.d.ts.map +0 -1
  269. package/dist/tools/media-upload.js +0 -155
  270. package/dist/tools/media-upload.js.map +0 -1
  271. package/dist/tools/python-exec.d.ts +0 -13
  272. package/dist/tools/python-exec.d.ts.map +0 -1
  273. package/dist/tools/python-exec.js +0 -99
  274. package/dist/tools/python-exec.js.map +0 -1
  275. package/dist/tools/python-restart.d.ts +0 -11
  276. package/dist/tools/python-restart.d.ts.map +0 -1
  277. package/dist/tools/python-restart.js +0 -33
  278. package/dist/tools/python-restart.js.map +0 -1
  279. package/dist/utils/circuit-breaker.d.ts +0 -23
  280. package/dist/utils/circuit-breaker.d.ts.map +0 -1
  281. package/dist/utils/circuit-breaker.js +0 -75
  282. package/dist/utils/circuit-breaker.js.map +0 -1
  283. package/dist/utils/config-loader.d.ts +0 -20
  284. package/dist/utils/config-loader.d.ts.map +0 -1
  285. package/dist/utils/config-loader.js +0 -194
  286. package/dist/utils/config-loader.js.map +0 -1
  287. package/dist/utils/fallback.d.ts +0 -7
  288. package/dist/utils/fallback.d.ts.map +0 -1
  289. package/dist/utils/fallback.js +0 -20
  290. package/dist/utils/fallback.js.map +0 -1
  291. package/dist/utils/git.d.ts +0 -24
  292. package/dist/utils/git.d.ts.map +0 -1
  293. package/dist/utils/git.js +0 -71
  294. package/dist/utils/git.js.map +0 -1
  295. package/dist/utils/github.d.ts +0 -37
  296. package/dist/utils/github.d.ts.map +0 -1
  297. package/dist/utils/github.js +0 -53
  298. package/dist/utils/github.js.map +0 -1
  299. package/dist/utils/global-path.d.ts +0 -2
  300. package/dist/utils/global-path.d.ts.map +0 -1
  301. package/dist/utils/global-path.js +0 -32
  302. package/dist/utils/global-path.js.map +0 -1
  303. package/dist/utils/logger.d.ts +0 -6
  304. package/dist/utils/logger.d.ts.map +0 -1
  305. package/dist/utils/logger.js +0 -69
  306. package/dist/utils/logger.js.map +0 -1
  307. package/dist/utils/obs-capture.d.ts +0 -7
  308. package/dist/utils/obs-capture.d.ts.map +0 -1
  309. package/dist/utils/obs-capture.js +0 -49
  310. package/dist/utils/obs-capture.js.map +0 -1
  311. package/dist/utils/retry.d.ts +0 -11
  312. package/dist/utils/retry.d.ts.map +0 -1
  313. package/dist/utils/retry.js +0 -45
  314. package/dist/utils/retry.js.map +0 -1
  315. package/dist/utils/self-wiring.d.ts +0 -13
  316. package/dist/utils/self-wiring.d.ts.map +0 -1
  317. package/dist/utils/self-wiring.js +0 -86
  318. package/dist/utils/self-wiring.js.map +0 -1
  319. package/dist/utils/state.d.ts +0 -106
  320. package/dist/utils/state.d.ts.map +0 -1
  321. package/dist/utils/state.js +0 -166
  322. package/dist/utils/state.js.map +0 -1
  323. package/dist/utils/status.d.ts +0 -47
  324. package/dist/utils/status.d.ts.map +0 -1
  325. package/dist/utils/status.js +0 -147
  326. package/dist/utils/status.js.map +0 -1
  327. package/dist/utils/ttl-map.d.ts +0 -17
  328. package/dist/utils/ttl-map.d.ts.map +0 -1
  329. package/dist/utils/ttl-map.js +0 -67
  330. package/dist/utils/ttl-map.js.map +0 -1
@@ -1,464 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- UI/UX Pro Max Core - BM25 search engine for UI/UX style guides
5
- """
6
-
7
- import csv
8
- import re
9
- from pathlib import Path
10
- from math import log
11
- from collections import defaultdict
12
-
13
- # ============ CONFIGURATION ============
14
- DATA_DIR = Path(__file__).parent.parent / "data"
15
- MAX_RESULTS = 3
16
-
17
- CSV_CONFIG = {
18
- "style": {
19
- "file": "styles.csv",
20
- "search_cols": ["Style Category", "Keywords", "Best For", "Type", "AI Prompt Keywords"],
21
- "output_cols": ["Style Category", "Type", "Keywords", "Primary Colors", "Effects & Animation", "Best For", "Light Mode ✓", "Dark Mode ✓", "Performance", "Accessibility", "Framework Compatibility", "Complexity", "AI Prompt Keywords", "CSS/Technical Keywords", "Implementation Checklist", "Design System Variables"]
22
- },
23
- "color": {
24
- "file": "colors.csv",
25
- "search_cols": ["Product Type", "Notes"],
26
- "output_cols": ["Product Type", "Primary", "On Primary", "Secondary", "On Secondary", "Accent", "On Accent", "Background", "Foreground", "Card", "Card Foreground", "Muted", "Muted Foreground", "Border", "Destructive", "On Destructive", "Ring", "Notes"]
27
- },
28
- "chart": {
29
- "file": "charts.csv",
30
- "search_cols": ["Data Type", "Keywords", "Best Chart Type", "When to Use", "When NOT to Use", "Accessibility Notes"],
31
- "output_cols": ["Data Type", "Keywords", "Best Chart Type", "Secondary Options", "When to Use", "When NOT to Use", "Data Volume Threshold", "Color Guidance", "Accessibility Grade", "Accessibility Notes", "A11y Fallback", "Library Recommendation", "Interactive Level"]
32
- },
33
- "landing": {
34
- "file": "landing.csv",
35
- "search_cols": ["Pattern Name", "Keywords", "Conversion Optimization", "Section Order"],
36
- "output_cols": ["Pattern Name", "Keywords", "Section Order", "Primary CTA Placement", "Color Strategy", "Conversion Optimization"]
37
- },
38
- "product": {
39
- "file": "products.csv",
40
- "search_cols": ["Product Type", "Keywords", "Primary Style Recommendation", "Key Considerations"],
41
- "output_cols": ["Product Type", "Keywords", "Primary Style Recommendation", "Secondary Styles", "Landing Page Pattern", "Dashboard Style (if applicable)", "Color Palette Focus"]
42
- },
43
- "ux": {
44
- "file": "ux-guidelines.csv",
45
- "search_cols": ["Category", "Issue", "Description", "Platform"],
46
- "output_cols": ["Category", "Issue", "Platform", "Description", "Do", "Don't", "Code Example Good", "Code Example Bad", "Severity"]
47
- },
48
- "typography": {
49
- "file": "typography.csv",
50
- "search_cols": ["Font Pairing Name", "Category", "Mood/Style Keywords", "Best For", "Heading Font", "Body Font"],
51
- "output_cols": ["Font Pairing Name", "Category", "Heading Font", "Body Font", "Mood/Style Keywords", "Best For", "Google Fonts URL", "CSS Import", "Tailwind Config", "Notes"]
52
- },
53
- "icons": {
54
- "file": "icons.csv",
55
- "search_cols": ["Category", "Icon Name", "Keywords", "Best For"],
56
- "output_cols": ["Category", "Icon Name", "Keywords", "Library", "Import Code", "Usage", "Best For", "Style"]
57
- },
58
- "gsap": {
59
- "file": "motion.csv",
60
- "search_cols": ["Category", "Intensity Tier", "Keywords", "Trigger"],
61
- "output_cols": ["Category", "Intensity Tier", "Trigger", "Duration", "Easing", "GSAP Snippet", "Framework Notes", "Do", "Don't", "Performance Notes"]
62
- },
63
- "react": {
64
- "file": "react-performance.csv",
65
- "search_cols": ["Category", "Issue", "Keywords", "Description"],
66
- "output_cols": ["Category", "Issue", "Platform", "Description", "Do", "Don't", "Code Example Good", "Code Example Bad", "Severity"]
67
- },
68
- "web": {
69
- "file": "app-interface.csv",
70
- "search_cols": ["Category", "Issue", "Keywords", "Description"],
71
- "output_cols": ["Category", "Issue", "Platform", "Description", "Do", "Don't", "Code Example Good", "Code Example Bad", "Severity"]
72
- },
73
- "google-fonts": {
74
- "file": "google-fonts.csv",
75
- "search_cols": ["Family", "Category", "Stroke", "Classifications", "Keywords", "Subsets", "Designers"],
76
- "output_cols": ["Family", "Category", "Stroke", "Classifications", "Styles", "Variable Axes", "Subsets", "Designers", "Popularity Rank", "Google Fonts URL"]
77
- }
78
- }
79
-
80
- # Output columns whose content (code samples, checklists) must never be
81
- # hard-truncated for display -- truncating mid-snippet destroys the value.
82
- UNTRUNCATED_COLS = {
83
- "Code Example Good", "Code Example Bad", "Code Good", "Code Bad",
84
- "Implementation Checklist", "Design System Variables", "CSS Import",
85
- "Tailwind Config", "GSAP Snippet",
86
- }
87
-
88
- STACK_CONFIG = {
89
- "react": {"file": "stacks/react.csv"},
90
- "nextjs": {"file": "stacks/nextjs.csv"},
91
- "vue": {"file": "stacks/vue.csv"},
92
- "svelte": {"file": "stacks/svelte.csv"},
93
- "astro": {"file": "stacks/astro.csv"},
94
- "swiftui": {"file": "stacks/swiftui.csv"},
95
- "react-native": {"file": "stacks/react-native.csv"},
96
- "flutter": {"file": "stacks/flutter.csv"},
97
- "nuxtjs": {"file": "stacks/nuxtjs.csv"},
98
- "nuxt-ui": {"file": "stacks/nuxt-ui.csv"},
99
- "html-tailwind": {"file": "stacks/html-tailwind.csv"},
100
- "shadcn": {"file": "stacks/shadcn.csv"},
101
- "jetpack-compose": {"file": "stacks/jetpack-compose.csv"},
102
- "threejs": {"file": "stacks/threejs.csv"},
103
- "angular": {"file": "stacks/angular.csv"},
104
- "laravel": {"file": "stacks/laravel.csv"},
105
- "javafx": {"file": "stacks/javafx.csv"},
106
- "wpf": {"file": "stacks/wpf.csv"},
107
- "winui": {"file": "stacks/winui.csv"},
108
- "avalonia": {"file": "stacks/avalonia.csv"},
109
- "uno": {"file": "stacks/uno.csv"},
110
- "uwp": {"file": "stacks/uwp.csv"},
111
- }
112
-
113
- # Common columns for all stacks
114
- _STACK_COLS = {
115
- "search_cols": ["Category", "Guideline", "Description", "Do", "Don't"],
116
- "output_cols": ["Category", "Guideline", "Description", "Do", "Don't", "Code Good", "Code Bad", "Severity", "Docs URL"]
117
- }
118
-
119
- AVAILABLE_STACKS = list(STACK_CONFIG.keys())
120
-
121
-
122
- # ============ TOKENIZATION ============
123
- # Common two-letter/three-letter words that add noise without adding search
124
- # signal. Deliberately short -- domain-relevant short tokens (ui, ux, ai,
125
- # css, 3d, js, os, md, gsap) must stay searchable, which is why we don't
126
- # filter purely by length.
127
- _STOPWORDS = {
128
- "to", "in", "on", "at", "is", "of", "by", "or", "an", "if", "no", "so",
129
- "do", "be", "we", "it", "as", "the", "and", "for", "are", "was",
130
- }
131
-
132
- # Query/corpus normalization so common spelling variants match each other.
133
- # Keep this a plain dict (stdlib only, no fuzzy-matching dependency).
134
- _SYNONYMS = {
135
- "e-commerce": "ecommerce",
136
- "dark-mode": "dark",
137
- "darkmode": "dark",
138
- "light-mode": "light",
139
- "lightmode": "light",
140
- "a11y": "accessibility",
141
- "nav": "navigation",
142
- "sign-up": "signup",
143
- "log-in": "login",
144
- "colour": "color",
145
- "colours": "colors",
146
- "customisation": "customization",
147
- "organisation": "organization",
148
- "behaviour": "behavior",
149
- "ux/ui": "ux ui",
150
- }
151
-
152
-
153
- def _normalize(text):
154
- """Apply synonym substitution before tokenizing."""
155
- for variant, canonical in _SYNONYMS.items():
156
- text = text.replace(variant, canonical)
157
- return text
158
-
159
-
160
- # ============ BM25 IMPLEMENTATION ============
161
- class BM25:
162
- """BM25 ranking algorithm for text search"""
163
-
164
- def __init__(self, k1=1.5, b=0.75):
165
- self.k1 = k1
166
- self.b = b
167
- self.corpus = []
168
- self.doc_lengths = []
169
- self.avgdl = 0
170
- self.idf = {}
171
- self.doc_freqs = defaultdict(int)
172
- self.N = 0
173
- self._term_freqs = [] # precomputed per-doc term frequencies
174
-
175
- def tokenize(self, text):
176
- """Lowercase, normalize synonyms, split, remove punctuation, filter stopwords"""
177
- text = _normalize(str(text).lower())
178
- text = re.sub(r'[^\w\s]', ' ', text)
179
- return [w for w in text.split() if len(w) >= 2 and w not in _STOPWORDS]
180
-
181
- def fit(self, documents):
182
- """Build BM25 index from documents"""
183
- self.corpus = [self.tokenize(doc) for doc in documents]
184
- self.N = len(self.corpus)
185
- if self.N == 0:
186
- return
187
- self.doc_lengths = [len(doc) for doc in self.corpus]
188
- self.avgdl = sum(self.doc_lengths) / self.N
189
-
190
- self._term_freqs = []
191
- for doc in self.corpus:
192
- tf = defaultdict(int)
193
- for word in doc:
194
- tf[word] += 1
195
- self._term_freqs.append(tf)
196
- for word in tf:
197
- self.doc_freqs[word] += 1
198
-
199
- for word, freq in self.doc_freqs.items():
200
- self.idf[word] = log((self.N - freq + 0.5) / (freq + 0.5) + 1)
201
-
202
- def score(self, query):
203
- """Score all documents against query"""
204
- query_tokens = self.tokenize(query)
205
- scores = []
206
-
207
- for idx in range(self.N):
208
- score = 0
209
- doc_len = self.doc_lengths[idx]
210
- term_freqs = self._term_freqs[idx]
211
-
212
- for token in query_tokens:
213
- if token in self.idf:
214
- tf = term_freqs.get(token, 0)
215
- idf = self.idf[token]
216
- numerator = tf * (self.k1 + 1)
217
- denominator = tf + self.k1 * (1 - self.b + self.b * doc_len / self.avgdl)
218
- score += idf * numerator / denominator
219
-
220
- scores.append((idx, score))
221
-
222
- return sorted(scores, key=lambda x: x[1], reverse=True)
223
-
224
- def vocabulary(self):
225
- """All indexed terms, for suggestion/typo-recovery purposes."""
226
- return list(self.idf.keys())
227
-
228
-
229
- # ============ CSV / INDEX CACHE ============
230
- # Data files are small and reused across multiple domain searches within a
231
- # single --design-system run; avoid re-reading + re-indexing the same file
232
- # repeatedly in one process.
233
- _csv_cache = {} # filepath -> (mtime, rows)
234
- _bm25_cache = {} # (filepath, tuple(search_cols)) -> (mtime, BM25 instance)
235
-
236
-
237
- def _load_csv(filepath):
238
- """Load CSV and return list of dicts, with mtime-based caching."""
239
- mtime = filepath.stat().st_mtime
240
- cached = _csv_cache.get(filepath)
241
- if cached and cached[0] == mtime:
242
- return cached[1]
243
-
244
- with open(filepath, 'r', encoding='utf-8') as f:
245
- rows = list(csv.DictReader(f))
246
-
247
- _csv_cache[filepath] = (mtime, rows)
248
- return rows
249
-
250
-
251
- def _get_bm25(filepath, search_cols, data):
252
- """Fitted BM25 index for this file+columns, with mtime-based caching."""
253
- key = (filepath, tuple(search_cols))
254
- mtime = filepath.stat().st_mtime
255
- cached = _bm25_cache.get(key)
256
- if cached and cached[0] == mtime:
257
- return cached[1]
258
-
259
- documents = [" ".join(str(row.get(col, "")) for col in search_cols) for row in data]
260
- bm25 = BM25()
261
- bm25.fit(documents)
262
- _bm25_cache[key] = (mtime, bm25)
263
- return bm25
264
-
265
-
266
- # ============ SEARCH FUNCTIONS ============
267
- def _search_csv(filepath, search_cols, output_cols, query, max_results):
268
- """Core search function using BM25. Returns (results, bm25_or_none)."""
269
- if not filepath.exists():
270
- return [], None
271
-
272
- try:
273
- data = _load_csv(filepath)
274
- except (csv.Error, OSError, UnicodeDecodeError) as e:
275
- return [{"_error": f"Failed to read {filepath.name}: {e}"}], None
276
-
277
- if not data:
278
- return [], None
279
-
280
- bm25 = _get_bm25(filepath, search_cols, data)
281
- ranked = bm25.score(query)
282
-
283
- results = []
284
- for idx, score in ranked[:max_results]:
285
- if score > 0:
286
- row = data[idx]
287
- results.append({col: row.get(col, "") for col in output_cols if col in row})
288
-
289
- return results, bm25
290
-
291
-
292
- def _suggest_terms(bm25, query, limit=6):
293
- """Nearest known vocabulary terms for a query that returned 0 hits,
294
- so the caller can retry instead of silently reporting nothing."""
295
- if bm25 is None:
296
- return []
297
- query_tokens = set(bm25.tokenize(query))
298
- if not query_tokens:
299
- return []
300
-
301
- candidates = []
302
- for term in bm25.vocabulary():
303
- for qt in query_tokens:
304
- if term.startswith(qt[:3]) or qt.startswith(term[:3]):
305
- candidates.append(term)
306
- break
307
-
308
- # Stable de-dup, most frequent terms first (doc_freqs available via idf keys only,
309
- # so just de-dup preserving discovery order).
310
- seen = set()
311
- ordered = []
312
- for term in candidates:
313
- if term not in seen:
314
- seen.add(term)
315
- ordered.append(term)
316
- return ordered[:limit]
317
-
318
-
319
- # Load the product-domain keyword list from products.csv at import time so
320
- # it stays in sync with the data instead of needing manual updates to a
321
- # hardcoded list. Falls back to a small built-in seed if the file is
322
- # missing (e.g. package built without data/).
323
- def _load_product_keywords():
324
- seed = ["saas", "ecommerce", "e-commerce", "fintech", "healthcare", "gaming",
325
- "portfolio", "crypto", "dashboard", "fitness", "marketplace"]
326
- filepath = DATA_DIR / CSV_CONFIG["product"]["file"]
327
- if not filepath.exists():
328
- return seed
329
- try:
330
- rows = _load_csv(filepath)
331
- except (csv.Error, OSError, UnicodeDecodeError):
332
- return seed
333
-
334
- keywords = set(seed)
335
- for row in rows:
336
- raw = row.get("Keywords", "")
337
- for kw in re.split(r"[,;]", raw):
338
- kw = kw.strip().lower()
339
- if kw and len(kw) >= 3:
340
- keywords.add(kw)
341
- return sorted(keywords, key=len, reverse=True)
342
-
343
-
344
- _DOMAIN_KEYWORDS = None
345
-
346
-
347
- def _domain_keywords():
348
- global _DOMAIN_KEYWORDS
349
- if _DOMAIN_KEYWORDS is not None:
350
- return _DOMAIN_KEYWORDS
351
-
352
- _DOMAIN_KEYWORDS = {
353
- "color": ["color", "palette", "hex", "#", "rgb", "token", "semantic", "accent", "destructive", "muted", "foreground"],
354
- "chart": ["chart", "graph", "visualization", "trend", "bar", "pie", "scatter", "heatmap", "funnel"],
355
- "landing": ["landing", "page", "cta", "conversion", "hero", "testimonial", "pricing", "section"],
356
- "product": _load_product_keywords(),
357
- "style": ["style", "design", "ui", "minimalism", "glassmorphism", "neumorphism", "brutalism", "dark mode", "flat", "aurora", "prompt", "css", "implementation", "variable", "checklist", "tailwind"],
358
- "ux": ["ux", "usability", "accessibility", "wcag", "touch", "scroll", "animation", "keyboard", "navigation", "mobile"],
359
- "typography": ["font pairing", "typography pairing", "heading font", "body font"],
360
- "google-fonts": ["google font", "font family", "font weight", "font style", "variable font", "noto", "font for", "find font", "font subset", "font language", "monospace font", "serif font", "sans serif font", "display font", "handwriting font", "font", "typography", "serif", "sans"],
361
- "icons": ["icon", "icons", "lucide", "heroicons", "symbol", "glyph", "pictogram", "svg icon"],
362
- "gsap": ["gsap", "quickto", "scrolltrigger", "stagger", "magnetic cursor", "parallax", "page transition", "scroll reveal", "scroll-triggered", "scrollytelling", "flip plugin", "splittext", "shimmer", "skeleton loader"],
363
- "react": ["react", "next.js", "nextjs", "suspense", "memo", "usecallback", "useeffect", "rerender", "bundle", "waterfall", "barrel", "dynamic import", "rsc", "server component"],
364
- "web": ["aria", "focus", "outline", "semantic", "virtualize", "autocomplete", "form", "input type", "preconnect"]
365
- }
366
- return _DOMAIN_KEYWORDS
367
-
368
-
369
- # Domains checked in this fixed order when scores tie, so results are
370
- # deterministic instead of depending on dict/hash ordering.
371
- _DOMAIN_TIEBREAK_ORDER = [
372
- "ux", "product", "style", "color", "typography", "google-fonts",
373
- "chart", "landing", "icons", "gsap", "react", "web",
374
- ]
375
-
376
-
377
- def detect_domain(query, return_scores=False):
378
- """Auto-detect the most relevant domain from query.
379
-
380
- Matches are weighted by keyword length (multi-word/longer phrases are
381
- more specific and score higher than short generic words). Ties are
382
- broken by a fixed domain priority order, not dict/insertion order.
383
- """
384
- query_lower = query.lower()
385
- domain_keywords = _domain_keywords()
386
-
387
- scores = {}
388
- for domain, keywords in domain_keywords.items():
389
- total = 0.0
390
- for kw in keywords:
391
- if re.search(r'\b' + re.escape(kw) + r'\b', query_lower):
392
- # weight = 1 point per word in the keyword phrase
393
- total += max(1, len(kw.split()))
394
- scores[domain] = total
395
-
396
- ranked = sorted(
397
- scores.items(),
398
- key=lambda item: (item[1], -_DOMAIN_TIEBREAK_ORDER.index(item[0])
399
- if item[0] in _DOMAIN_TIEBREAK_ORDER else -999),
400
- reverse=True,
401
- )
402
- best_domain, best_score = ranked[0]
403
- result = best_domain if best_score > 0 else "style"
404
-
405
- if return_scores:
406
- runner_up = ranked[1][0] if len(ranked) > 1 and ranked[1][1] > 0 else None
407
- return result, runner_up
408
- return result
409
-
410
-
411
- def search(query, domain=None, max_results=MAX_RESULTS):
412
- """Main search function with auto-domain detection"""
413
- auto_detected = domain is None
414
- runner_up = None
415
- if domain is None:
416
- domain, runner_up = detect_domain(query, return_scores=True)
417
-
418
- config = CSV_CONFIG.get(domain, CSV_CONFIG["style"])
419
- filepath = DATA_DIR / config["file"]
420
-
421
- if not filepath.exists():
422
- return {"error": f"File not found: {filepath}", "domain": domain}
423
-
424
- results, bm25 = _search_csv(filepath, config["search_cols"], config["output_cols"], query, max_results)
425
-
426
- out = {
427
- "domain": domain,
428
- "query": query,
429
- "file": config["file"],
430
- "count": len(results),
431
- "results": results,
432
- }
433
- if auto_detected:
434
- out["auto_detected"] = True
435
- if runner_up:
436
- out["runner_up_domain"] = runner_up
437
- if not results:
438
- out["suggestions"] = _suggest_terms(bm25, query)
439
- return out
440
-
441
-
442
- def search_stack(query, stack, max_results=MAX_RESULTS):
443
- """Search stack-specific guidelines"""
444
- if stack not in STACK_CONFIG:
445
- return {"error": f"Unknown stack: {stack}. Available: {', '.join(AVAILABLE_STACKS)}"}
446
-
447
- filepath = DATA_DIR / STACK_CONFIG[stack]["file"]
448
-
449
- if not filepath.exists():
450
- return {"error": f"Stack file not found: {filepath}", "stack": stack}
451
-
452
- results, bm25 = _search_csv(filepath, _STACK_COLS["search_cols"], _STACK_COLS["output_cols"], query, max_results)
453
-
454
- out = {
455
- "domain": "stack",
456
- "stack": stack,
457
- "query": query,
458
- "file": STACK_CONFIG[stack]["file"],
459
- "count": len(results),
460
- "results": results,
461
- }
462
- if not results:
463
- out["suggestions"] = _suggest_terms(bm25, query)
464
- return out