@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,487 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Icon Generation Script using Gemini 3.1 Pro Preview API
5
- Generates SVG icons via text generation (SVG is XML text format)
6
-
7
- Model: gemini-3.1-pro-preview - best thinking, token efficiency, factual consistency
8
-
9
- Usage:
10
- python generate.py --prompt "settings gear icon" --style outlined
11
- python generate.py --prompt "shopping cart" --style filled --color "#6366F1"
12
- python generate.py --name "dashboard" --category navigation --style duotone
13
- python generate.py --prompt "cloud upload" --batch 4 --output-dir ./icons
14
- python generate.py --prompt "user profile" --sizes "16,24,32,48"
15
- """
16
-
17
- import argparse
18
- import json
19
- import os
20
- import re
21
- import sys
22
- import time
23
- from pathlib import Path
24
- from datetime import datetime
25
-
26
-
27
- def load_env():
28
- """Load .env files in priority order"""
29
- env_paths = [
30
- Path(__file__).parent.parent.parent / ".env",
31
- Path.home() / ".claude" / "skills" / ".env",
32
- Path.home() / ".claude" / ".env"
33
- ]
34
- for env_path in env_paths:
35
- if env_path.exists():
36
- with open(env_path) as f:
37
- for line in f:
38
- line = line.strip()
39
- if line and not line.startswith('#') and '=' in line:
40
- key, value = line.split('=', 1)
41
- if key not in os.environ:
42
- os.environ[key] = value.strip('"\'')
43
-
44
- load_env()
45
-
46
- try:
47
- from google import genai
48
- from google.genai import types
49
- except ImportError:
50
- print("Error: google-genai package not installed.")
51
- print("Install with: pip install google-genai")
52
- sys.exit(1)
53
-
54
-
55
- # ============ CONFIGURATION ============
56
- GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
57
- MODEL = "gemini-3.1-pro-preview"
58
-
59
- # Icon styles with SVG-specific instructions
60
- ICON_STYLES = {
61
- "outlined": "outlined stroke icons, 2px stroke width, no fill, clean open paths",
62
- "filled": "solid filled icons, no stroke, flat color fills, bold shapes",
63
- "duotone": "duotone style with primary color at full opacity and secondary color at 30% opacity, layered shapes",
64
- "thin": "thin line icons, 1px or 1.5px stroke width, delicate minimalist lines",
65
- "bold": "bold thick line icons, 3px stroke width, heavy weight, impactful",
66
- "rounded": "rounded icons with round line caps and joins, soft corners, friendly feel",
67
- "sharp": "sharp angular icons, square line caps and mitered joins, precise edges",
68
- "flat": "flat design icons, solid fills, no gradients or shadows, geometric simplicity",
69
- "gradient": "linear or radial gradient fills, modern vibrant color transitions",
70
- "glassmorphism": "glassmorphism style with semi-transparent fills, blur backdrop effect simulation, frosted glass",
71
- "pixel": "pixel art style icons on a grid, retro 8-bit aesthetic, crisp edges",
72
- "hand-drawn": "hand-drawn sketch style, slightly irregular strokes, organic feel, imperfect lines",
73
- "isometric": "isometric 3D projection, 30-degree angles, dimensional depth",
74
- "glyph": "simple glyph style, single solid shape, minimal detail, pictogram",
75
- "animated-ready": "animated-ready SVG with named groups and IDs for CSS/JS animation targets",
76
- }
77
-
78
- ICON_CATEGORIES = {
79
- "navigation": "arrows, menus, hamburger, chevrons, home, back, forward, breadcrumb",
80
- "action": "edit, delete, save, download, upload, share, copy, paste, print, search",
81
- "communication": "email, chat, phone, video call, notification, bell, message bubble",
82
- "media": "play, pause, stop, skip, volume, microphone, camera, image, gallery",
83
- "file": "document, folder, archive, attachment, cloud, database, storage",
84
- "user": "person, group, avatar, profile, settings, lock, key, shield",
85
- "commerce": "cart, bag, wallet, credit card, receipt, tag, gift, store",
86
- "data": "chart, graph, analytics, dashboard, table, filter, sort, calendar",
87
- "development": "code, terminal, bug, git, API, server, database, deploy",
88
- "social": "heart, star, thumbs up, bookmark, flag, trophy, badge, crown",
89
- "weather": "sun, moon, cloud, rain, snow, wind, thunder, temperature",
90
- "map": "pin, location, compass, globe, route, directions, map marker",
91
- }
92
-
93
- # SVG generation prompt template
94
- SVG_PROMPT_TEMPLATE = """Generate a clean, production-ready SVG icon.
95
-
96
- Requirements:
97
- - Output ONLY valid SVG code, nothing else
98
- - ViewBox: "0 0 {viewbox} {viewbox}"
99
- - Use currentColor for strokes/fills (inherits CSS color)
100
- - No embedded fonts or text elements unless specifically requested
101
- - No raster images or external references
102
- - Optimized paths with minimal nodes
103
- - Accessible: include <title> element with icon description
104
- {style_instructions}
105
- {color_instructions}
106
- {size_instructions}
107
-
108
- Icon to generate: {prompt}
109
-
110
- Output the SVG code only, wrapped in ```svg``` code block."""
111
-
112
- SVG_BATCH_PROMPT_TEMPLATE = """Generate {count} distinct SVG icon variations for: {prompt}
113
-
114
- Requirements for EACH icon:
115
- - Output ONLY valid SVG code
116
- - ViewBox: "0 0 {viewbox} {viewbox}"
117
- - Use currentColor for strokes/fills (inherits CSS color)
118
- - No embedded fonts, raster images, or external references
119
- - Optimized paths with minimal nodes
120
- - Include <title> element with icon description
121
- {style_instructions}
122
- {color_instructions}
123
-
124
- Generate {count} different visual interpretations. Output each SVG in a separate ```svg``` code block.
125
- Label each variation (e.g., "Variation 1: [brief description]")."""
126
-
127
-
128
- def extract_svgs(text):
129
- """Extract SVG code blocks from model response"""
130
- svgs = []
131
-
132
- # Try ```svg code blocks first
133
- pattern = r'```svg\s*\n(.*?)```'
134
- matches = re.findall(pattern, text, re.DOTALL)
135
- if matches:
136
- svgs.extend(matches)
137
-
138
- # Fallback: try ```xml code blocks
139
- if not svgs:
140
- pattern = r'```xml\s*\n(.*?)```'
141
- matches = re.findall(pattern, text, re.DOTALL)
142
- svgs.extend(matches)
143
-
144
- # Fallback: try bare <svg> tags
145
- if not svgs:
146
- pattern = r'(<svg[^>]*>.*?</svg>)'
147
- matches = re.findall(pattern, text, re.DOTALL)
148
- svgs.extend(matches)
149
-
150
- # Clean up extracted SVGs
151
- cleaned = []
152
- for svg in svgs:
153
- svg = svg.strip()
154
- if not svg.startswith('<svg'):
155
- # Try to find <svg> within the extracted text
156
- match = re.search(r'(<svg[^>]*>.*?</svg>)', svg, re.DOTALL)
157
- if match:
158
- svg = match.group(1)
159
- else:
160
- continue
161
- cleaned.append(svg)
162
-
163
- return cleaned
164
-
165
-
166
- def apply_color(svg_code, color):
167
- """Replace currentColor with specific color if provided"""
168
- if color:
169
- # Replace currentColor with the specified color
170
- svg_code = svg_code.replace('currentColor', color)
171
- # If no currentColor was present, add fill/stroke color
172
- if color not in svg_code:
173
- svg_code = svg_code.replace('<svg', f'<svg color="{color}"', 1)
174
- return svg_code
175
-
176
-
177
- def apply_viewbox_size(svg_code, size):
178
- """Adjust SVG viewBox to target size"""
179
- if size:
180
- # Update width/height attributes if present
181
- svg_code = re.sub(r'width="[^"]*"', f'width="{size}"', svg_code)
182
- svg_code = re.sub(r'height="[^"]*"', f'height="{size}"', svg_code)
183
- # Add width/height if not present
184
- if 'width=' not in svg_code:
185
- svg_code = svg_code.replace('<svg', f'<svg width="{size}" height="{size}"', 1)
186
- return svg_code
187
-
188
-
189
- def generate_icon(prompt, style=None, category=None, name=None,
190
- color=None, size=24, output_path=None, viewbox=24):
191
- """Generate a single SVG icon using Gemini 3.1 Pro Preview"""
192
-
193
- if not GEMINI_API_KEY:
194
- print("Error: GEMINI_API_KEY not set")
195
- print("Set it with: export GEMINI_API_KEY='your-key'")
196
- return None
197
-
198
- client = genai.Client(api_key=GEMINI_API_KEY)
199
-
200
- # Build style instructions
201
- style_instructions = ""
202
- if style and style in ICON_STYLES:
203
- style_instructions = f"- Style: {ICON_STYLES[style]}"
204
-
205
- # Build color instructions
206
- color_instructions = "- Use currentColor for all strokes and fills"
207
- if color:
208
- color_instructions = f"- Use color: {color} for primary elements, currentColor for secondary"
209
-
210
- # Build size instructions
211
- size_instructions = f"- Design for {size}px display size, optimize detail level accordingly"
212
-
213
- # Build final prompt
214
- icon_prompt = prompt
215
- if category and category in ICON_CATEGORIES:
216
- icon_prompt = f"{prompt} (category: {ICON_CATEGORIES[category]})"
217
- if name:
218
- icon_prompt = f"'{name}' icon: {icon_prompt}"
219
-
220
- full_prompt = SVG_PROMPT_TEMPLATE.format(
221
- prompt=icon_prompt,
222
- viewbox=viewbox,
223
- style_instructions=style_instructions,
224
- color_instructions=color_instructions,
225
- size_instructions=size_instructions
226
- )
227
-
228
- print(f"Generating icon with {MODEL}...")
229
- print(f"Prompt: {prompt}")
230
- if style:
231
- print(f"Style: {style}")
232
- print()
233
-
234
- try:
235
- response = client.models.generate_content(
236
- model=MODEL,
237
- contents=full_prompt,
238
- config=types.GenerateContentConfig(
239
- temperature=0.7,
240
- max_output_tokens=4096,
241
- )
242
- )
243
-
244
- # Extract SVG from response
245
- response_text = response.text if hasattr(response, 'text') else ""
246
- if not response_text:
247
- for part in response.candidates[0].content.parts:
248
- if hasattr(part, 'text') and part.text:
249
- response_text += part.text
250
-
251
- svgs = extract_svgs(response_text)
252
-
253
- if not svgs:
254
- print("No valid SVG generated. Model response:")
255
- print(response_text[:500])
256
- return None
257
-
258
- svg_code = svgs[0]
259
-
260
- # Apply color if specified
261
- svg_code = apply_color(svg_code, color)
262
-
263
- # Apply size
264
- svg_code = apply_viewbox_size(svg_code, size)
265
-
266
- # Determine output path
267
- if output_path is None:
268
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
269
- slug = name or prompt.split()[0] if prompt else "icon"
270
- slug = re.sub(r'[^a-zA-Z0-9_-]', '_', slug.lower())
271
- style_suffix = f"_{style}" if style else ""
272
- output_path = f"{slug}{style_suffix}_{timestamp}.svg"
273
-
274
- # Save SVG
275
- with open(output_path, "w", encoding="utf-8") as f:
276
- f.write(svg_code)
277
-
278
- print(f"Icon saved to: {output_path}")
279
- return output_path
280
-
281
- except Exception as e:
282
- print(f"Error generating icon: {e}")
283
- return None
284
-
285
-
286
- def generate_batch(prompt, count, output_dir, style=None, color=None,
287
- viewbox=24, name=None):
288
- """Generate multiple icon variations"""
289
-
290
- if not GEMINI_API_KEY:
291
- print("Error: GEMINI_API_KEY not set")
292
- return []
293
-
294
- client = genai.Client(api_key=GEMINI_API_KEY)
295
- os.makedirs(output_dir, exist_ok=True)
296
-
297
- # Build instructions
298
- style_instructions = ""
299
- if style and style in ICON_STYLES:
300
- style_instructions = f"- Style: {ICON_STYLES[style]}"
301
-
302
- color_instructions = "- Use currentColor for all strokes and fills"
303
- if color:
304
- color_instructions = f"- Use color: {color} for primary elements"
305
-
306
- full_prompt = SVG_BATCH_PROMPT_TEMPLATE.format(
307
- prompt=prompt,
308
- count=count,
309
- viewbox=viewbox,
310
- style_instructions=style_instructions,
311
- color_instructions=color_instructions
312
- )
313
-
314
- print(f"\n{'='*60}")
315
- print(f" BATCH ICON GENERATION")
316
- print(f" Model: {MODEL}")
317
- print(f" Prompt: {prompt}")
318
- print(f" Variants: {count}")
319
- print(f" Output: {output_dir}")
320
- print(f"{'='*60}\n")
321
-
322
- try:
323
- response = client.models.generate_content(
324
- model=MODEL,
325
- contents=full_prompt,
326
- config=types.GenerateContentConfig(
327
- temperature=0.9,
328
- max_output_tokens=16384,
329
- )
330
- )
331
-
332
- response_text = response.text if hasattr(response, 'text') else ""
333
- if not response_text:
334
- for part in response.candidates[0].content.parts:
335
- if hasattr(part, 'text') and part.text:
336
- response_text += part.text
337
-
338
- svgs = extract_svgs(response_text)
339
-
340
- if not svgs:
341
- print("No valid SVGs generated.")
342
- print(response_text[:500])
343
- return []
344
-
345
- results = []
346
- slug = name or re.sub(r'[^a-zA-Z0-9_-]', '_', prompt.split()[0].lower())
347
- style_suffix = f"_{style}" if style else ""
348
-
349
- for i, svg_code in enumerate(svgs[:count]):
350
- svg_code = apply_color(svg_code, color)
351
- filename = f"{slug}{style_suffix}_{i+1:02d}.svg"
352
- filepath = os.path.join(output_dir, filename)
353
-
354
- with open(filepath, "w", encoding="utf-8") as f:
355
- f.write(svg_code)
356
-
357
- results.append(filepath)
358
- print(f" [{i+1}/{len(svgs[:count])}] Saved: {filename}")
359
-
360
- print(f"\n{'='*60}")
361
- print(f" BATCH COMPLETE: {len(results)}/{count} icons generated")
362
- print(f"{'='*60}\n")
363
-
364
- return results
365
-
366
- except Exception as e:
367
- print(f"Error generating icons: {e}")
368
- return []
369
-
370
-
371
- def generate_sizes(prompt, sizes, style=None, color=None, output_dir=None, name=None):
372
- """Generate same icon at multiple sizes"""
373
- if output_dir is None:
374
- output_dir = "."
375
- os.makedirs(output_dir, exist_ok=True)
376
-
377
- results = []
378
- slug = name or re.sub(r'[^a-zA-Z0-9_-]', '_', prompt.split()[0].lower())
379
- style_suffix = f"_{style}" if style else ""
380
-
381
- for size in sizes:
382
- print(f"Generating {size}px variant...")
383
- filename = f"{slug}{style_suffix}_{size}px.svg"
384
- filepath = os.path.join(output_dir, filename)
385
-
386
- result = generate_icon(
387
- prompt=prompt,
388
- style=style,
389
- color=color,
390
- size=size,
391
- output_path=filepath,
392
- viewbox=size
393
- )
394
-
395
- if result:
396
- results.append(result)
397
-
398
- time.sleep(1)
399
-
400
- return results
401
-
402
-
403
- def main():
404
- parser = argparse.ArgumentParser(
405
- description="Generate SVG icons using Gemini 3.1 Pro Preview"
406
- )
407
- parser.add_argument("--prompt", "-p", type=str, help="Icon description")
408
- parser.add_argument("--name", "-n", type=str, help="Icon name (for filename)")
409
- parser.add_argument("--style", "-s", choices=list(ICON_STYLES.keys()),
410
- help="Icon style")
411
- parser.add_argument("--category", "-c", choices=list(ICON_CATEGORIES.keys()),
412
- help="Icon category for context")
413
- parser.add_argument("--color", type=str,
414
- help="Primary color (hex, e.g. #6366F1). Default: currentColor")
415
- parser.add_argument("--size", type=int, default=24,
416
- help="Icon size in px (default: 24)")
417
- parser.add_argument("--viewbox", type=int, default=24,
418
- help="SVG viewBox size (default: 24)")
419
- parser.add_argument("--output", "-o", type=str, help="Output file path")
420
- parser.add_argument("--output-dir", type=str, help="Output directory for batch")
421
- parser.add_argument("--batch", type=int,
422
- help="Number of icon variants to generate")
423
- parser.add_argument("--sizes", type=str,
424
- help="Comma-separated sizes (e.g. '16,24,32,48')")
425
- parser.add_argument("--list-styles", action="store_true",
426
- help="List available icon styles")
427
- parser.add_argument("--list-categories", action="store_true",
428
- help="List available icon categories")
429
-
430
- args = parser.parse_args()
431
-
432
- if args.list_styles:
433
- print("Available icon styles:")
434
- for style, desc in ICON_STYLES.items():
435
- print(f" {style}: {desc[:70]}...")
436
- return
437
-
438
- if args.list_categories:
439
- print("Available icon categories:")
440
- for cat, desc in ICON_CATEGORIES.items():
441
- print(f" {cat}: {desc}")
442
- return
443
-
444
- if not args.prompt and not args.name:
445
- parser.error("Either --prompt or --name is required")
446
-
447
- prompt = args.prompt or args.name
448
-
449
- # Multi-size mode
450
- if args.sizes:
451
- sizes = [int(s.strip()) for s in args.sizes.split(",")]
452
- generate_sizes(
453
- prompt=prompt,
454
- sizes=sizes,
455
- style=args.style,
456
- color=args.color,
457
- output_dir=args.output_dir or "./icons",
458
- name=args.name
459
- )
460
- # Batch mode
461
- elif args.batch:
462
- output_dir = args.output_dir or "./icons"
463
- generate_batch(
464
- prompt=prompt,
465
- count=args.batch,
466
- output_dir=output_dir,
467
- style=args.style,
468
- color=args.color,
469
- viewbox=args.viewbox,
470
- name=args.name
471
- )
472
- # Single icon
473
- else:
474
- generate_icon(
475
- prompt=prompt,
476
- style=args.style,
477
- category=args.category,
478
- name=args.name,
479
- color=args.color,
480
- size=args.size,
481
- output_path=args.output,
482
- viewbox=args.viewbox
483
- )
484
-
485
-
486
- if __name__ == "__main__":
487
- main()
@@ -1,175 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Logo Design Core - BM25 search engine for logo design guidelines
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.parent / "data" / "logo"
15
- MAX_RESULTS = 3
16
-
17
- CSV_CONFIG = {
18
- "style": {
19
- "file": "styles.csv",
20
- "search_cols": ["Style Name", "Category", "Keywords", "Best For"],
21
- "output_cols": ["Style Name", "Category", "Keywords", "Primary Colors", "Secondary Colors", "Typography", "Effects", "Best For", "Avoid For", "Complexity", "Era"]
22
- },
23
- "color": {
24
- "file": "colors.csv",
25
- "search_cols": ["Palette Name", "Category", "Keywords", "Psychology", "Best For"],
26
- "output_cols": ["Palette Name", "Category", "Keywords", "Primary Hex", "Secondary Hex", "Accent Hex", "Background Hex", "Text Hex", "Psychology", "Best For", "Avoid For"]
27
- },
28
- "industry": {
29
- "file": "industries.csv",
30
- "search_cols": ["Industry", "Keywords", "Recommended Styles", "Mood"],
31
- "output_cols": ["Industry", "Keywords", "Recommended Styles", "Primary Colors", "Typography", "Common Symbols", "Mood", "Best Practices", "Avoid"]
32
- }
33
- }
34
-
35
-
36
- # ============ BM25 IMPLEMENTATION ============
37
- class BM25:
38
- """BM25 ranking algorithm for text search"""
39
-
40
- def __init__(self, k1=1.5, b=0.75):
41
- self.k1 = k1
42
- self.b = b
43
- self.corpus = []
44
- self.doc_lengths = []
45
- self.avgdl = 0
46
- self.idf = {}
47
- self.doc_freqs = defaultdict(int)
48
- self.N = 0
49
-
50
- def tokenize(self, text):
51
- """Lowercase, split, remove punctuation, filter short words"""
52
- text = re.sub(r'[^\w\s]', ' ', str(text).lower())
53
- return [w for w in text.split() if len(w) > 2]
54
-
55
- def fit(self, documents):
56
- """Build BM25 index from documents"""
57
- self.corpus = [self.tokenize(doc) for doc in documents]
58
- self.N = len(self.corpus)
59
- if self.N == 0:
60
- return
61
- self.doc_lengths = [len(doc) for doc in self.corpus]
62
- self.avgdl = sum(self.doc_lengths) / self.N
63
-
64
- for doc in self.corpus:
65
- seen = set()
66
- for word in doc:
67
- if word not in seen:
68
- self.doc_freqs[word] += 1
69
- seen.add(word)
70
-
71
- for word, freq in self.doc_freqs.items():
72
- self.idf[word] = log((self.N - freq + 0.5) / (freq + 0.5) + 1)
73
-
74
- def score(self, query):
75
- """Score all documents against query"""
76
- query_tokens = self.tokenize(query)
77
- scores = []
78
-
79
- for idx, doc in enumerate(self.corpus):
80
- score = 0
81
- doc_len = self.doc_lengths[idx]
82
- term_freqs = defaultdict(int)
83
- for word in doc:
84
- term_freqs[word] += 1
85
-
86
- for token in query_tokens:
87
- if token in self.idf:
88
- tf = term_freqs[token]
89
- idf = self.idf[token]
90
- numerator = tf * (self.k1 + 1)
91
- denominator = tf + self.k1 * (1 - self.b + self.b * doc_len / self.avgdl)
92
- score += idf * numerator / denominator
93
-
94
- scores.append((idx, score))
95
-
96
- return sorted(scores, key=lambda x: x[1], reverse=True)
97
-
98
-
99
- # ============ SEARCH FUNCTIONS ============
100
- def _load_csv(filepath):
101
- """Load CSV and return list of dicts"""
102
- with open(filepath, 'r', encoding='utf-8') as f:
103
- return list(csv.DictReader(f))
104
-
105
-
106
- def _search_csv(filepath, search_cols, output_cols, query, max_results):
107
- """Core search function using BM25"""
108
- if not filepath.exists():
109
- return []
110
-
111
- data = _load_csv(filepath)
112
-
113
- # Build documents from search columns
114
- documents = [" ".join(str(row.get(col, "")) for col in search_cols) for row in data]
115
-
116
- # BM25 search
117
- bm25 = BM25()
118
- bm25.fit(documents)
119
- ranked = bm25.score(query)
120
-
121
- # Get top results with score > 0
122
- results = []
123
- for idx, score in ranked[:max_results]:
124
- if score > 0:
125
- row = data[idx]
126
- results.append({col: row.get(col, "") for col in output_cols if col in row})
127
-
128
- return results
129
-
130
-
131
- def detect_domain(query):
132
- """Auto-detect the most relevant domain from query"""
133
- query_lower = query.lower()
134
-
135
- domain_keywords = {
136
- "style": ["style", "minimalist", "vintage", "modern", "retro", "geometric", "abstract", "emblem", "badge", "wordmark", "mascot", "luxury", "playful", "corporate"],
137
- "color": ["color", "palette", "hex", "#", "rgb", "blue", "red", "green", "gold", "warm", "cool", "vibrant", "pastel"],
138
- "industry": ["tech", "healthcare", "finance", "legal", "restaurant", "food", "fashion", "beauty", "education", "sports", "fitness", "real estate", "crypto", "gaming"]
139
- }
140
-
141
- scores = {domain: sum(1 for kw in keywords if kw in query_lower) for domain, keywords in domain_keywords.items()}
142
- best = max(scores, key=scores.get)
143
- return best if scores[best] > 0 else "style"
144
-
145
-
146
- def search(query, domain=None, max_results=MAX_RESULTS):
147
- """Main search function with auto-domain detection"""
148
- if domain is None:
149
- domain = detect_domain(query)
150
-
151
- config = CSV_CONFIG.get(domain, CSV_CONFIG["style"])
152
- filepath = DATA_DIR / config["file"]
153
-
154
- if not filepath.exists():
155
- return {"error": f"File not found: {filepath}", "domain": domain}
156
-
157
- results = _search_csv(filepath, config["search_cols"], config["output_cols"], query, max_results)
158
-
159
- return {
160
- "domain": domain,
161
- "query": query,
162
- "file": config["file"],
163
- "count": len(results),
164
- "results": results
165
- }
166
-
167
-
168
- def search_all(query, max_results=2):
169
- """Search across all domains and combine results"""
170
- all_results = {}
171
- for domain in CSV_CONFIG.keys():
172
- result = search(query, domain, max_results)
173
- if result.get("results"):
174
- all_results[domain] = result["results"]
175
- return all_results