sumulige-claude 1.0.9 → 1.1.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 (320) hide show
  1. package/.claude/.version +1 -0
  2. package/.claude/AGENTS.md +9 -9
  3. package/.claude/commands/commit-push-pr.md +23 -3
  4. package/.claude/commands/todos.md +41 -6
  5. package/.claude/hooks/session-restore.cjs +102 -0
  6. package/.claude/hooks/session-save.cjs +164 -0
  7. package/.claude/hooks/todo-manager.cjs +262 -141
  8. package/.claude/settings.local.json +25 -1
  9. package/.claude/skills/algorithmic-art/LICENSE.txt +202 -0
  10. package/.claude/skills/algorithmic-art/SKILL.md +405 -0
  11. package/.claude/skills/algorithmic-art/templates/generator_template.js +223 -0
  12. package/.claude/skills/algorithmic-art/templates/viewer.html +599 -0
  13. package/.claude/skills/api-tester/SKILL.md +52 -23
  14. package/.claude/skills/brand-guidelines/LICENSE.txt +202 -0
  15. package/.claude/skills/brand-guidelines/SKILL.md +73 -0
  16. package/.claude/skills/canvas-design/LICENSE.txt +202 -0
  17. package/.claude/skills/canvas-design/SKILL.md +130 -0
  18. package/.claude/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +93 -0
  19. package/.claude/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  20. package/.claude/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  21. package/.claude/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +93 -0
  22. package/.claude/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  23. package/.claude/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +93 -0
  24. package/.claude/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  25. package/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  26. package/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
  27. package/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  28. package/.claude/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  29. package/.claude/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  30. package/.claude/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +93 -0
  31. package/.claude/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  32. package/.claude/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +93 -0
  33. package/.claude/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  34. package/.claude/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +94 -0
  35. package/.claude/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  36. package/.claude/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  37. package/.claude/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +93 -0
  38. package/.claude/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  39. package/.claude/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +93 -0
  40. package/.claude/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  41. package/.claude/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  42. package/.claude/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
  43. package/.claude/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  44. package/.claude/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  45. package/.claude/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  46. package/.claude/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  47. package/.claude/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  48. package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  49. package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  50. package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  51. package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
  52. package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  53. package/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  54. package/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  55. package/.claude/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +93 -0
  56. package/.claude/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  57. package/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  58. package/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
  59. package/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  60. package/.claude/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  61. package/.claude/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  62. package/.claude/skills/canvas-design/canvas-fonts/Jura-OFL.txt +93 -0
  63. package/.claude/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
  64. package/.claude/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  65. package/.claude/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  66. package/.claude/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  67. package/.claude/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  68. package/.claude/skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
  69. package/.claude/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  70. package/.claude/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  71. package/.claude/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +93 -0
  72. package/.claude/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  73. package/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
  74. package/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  75. package/.claude/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  76. package/.claude/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
  77. package/.claude/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  78. package/.claude/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  79. package/.claude/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +93 -0
  80. package/.claude/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +93 -0
  81. package/.claude/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  82. package/.claude/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  83. package/.claude/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +93 -0
  84. package/.claude/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  85. package/.claude/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +93 -0
  86. package/.claude/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  87. package/.claude/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  88. package/.claude/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +93 -0
  89. package/.claude/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  90. package/.claude/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
  91. package/.claude/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  92. package/.claude/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  93. package/.claude/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  94. package/.claude/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  95. package/.claude/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +93 -0
  96. package/.claude/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  97. package/.claude/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
  98. package/.claude/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  99. package/.claude/skills/doc-coauthoring/SKILL.md +375 -0
  100. package/.claude/skills/docx/LICENSE.txt +30 -0
  101. package/.claude/skills/docx/SKILL.md +197 -0
  102. package/.claude/skills/docx/docx-js.md +350 -0
  103. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  104. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  105. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  106. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  107. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  108. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  109. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  110. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  111. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  112. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  113. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  114. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  115. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  116. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  117. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  118. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  119. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  120. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  121. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  122. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  123. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  124. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  125. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  126. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  127. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  128. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  129. package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  130. package/.claude/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  131. package/.claude/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  132. package/.claude/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  133. package/.claude/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  134. package/.claude/skills/docx/ooxml/schemas/mce/mc.xsd +75 -0
  135. package/.claude/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  136. package/.claude/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  137. package/.claude/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  138. package/.claude/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  139. package/.claude/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  140. package/.claude/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  141. package/.claude/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  142. package/.claude/skills/docx/ooxml/scripts/pack.py +159 -0
  143. package/.claude/skills/docx/ooxml/scripts/unpack.py +29 -0
  144. package/.claude/skills/docx/ooxml/scripts/validate.py +69 -0
  145. package/.claude/skills/docx/ooxml/scripts/validation/__init__.py +15 -0
  146. package/.claude/skills/docx/ooxml/scripts/validation/base.py +951 -0
  147. package/.claude/skills/docx/ooxml/scripts/validation/docx.py +274 -0
  148. package/.claude/skills/docx/ooxml/scripts/validation/pptx.py +315 -0
  149. package/.claude/skills/docx/ooxml/scripts/validation/redlining.py +279 -0
  150. package/.claude/skills/docx/ooxml.md +610 -0
  151. package/.claude/skills/docx/scripts/__init__.py +1 -0
  152. package/.claude/skills/docx/scripts/document.py +1276 -0
  153. package/.claude/skills/docx/scripts/templates/comments.xml +3 -0
  154. package/.claude/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  155. package/.claude/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  156. package/.claude/skills/docx/scripts/templates/commentsIds.xml +3 -0
  157. package/.claude/skills/docx/scripts/templates/people.xml +3 -0
  158. package/.claude/skills/docx/scripts/utilities.py +374 -0
  159. package/.claude/skills/frontend-design/LICENSE.txt +177 -0
  160. package/.claude/skills/frontend-design/SKILL.md +42 -0
  161. package/.claude/skills/internal-comms/LICENSE.txt +202 -0
  162. package/.claude/skills/internal-comms/SKILL.md +32 -0
  163. package/.claude/skills/internal-comms/examples/3p-updates.md +47 -0
  164. package/.claude/skills/internal-comms/examples/company-newsletter.md +65 -0
  165. package/.claude/skills/internal-comms/examples/faq-answers.md +30 -0
  166. package/.claude/skills/internal-comms/examples/general-comms.md +16 -0
  167. package/.claude/skills/mcp-builder/LICENSE.txt +202 -0
  168. package/.claude/skills/mcp-builder/SKILL.md +236 -0
  169. package/.claude/skills/mcp-builder/reference/evaluation.md +602 -0
  170. package/.claude/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
  171. package/.claude/skills/mcp-builder/reference/node_mcp_server.md +970 -0
  172. package/.claude/skills/mcp-builder/reference/python_mcp_server.md +719 -0
  173. package/.claude/skills/mcp-builder/scripts/connections.py +151 -0
  174. package/.claude/skills/mcp-builder/scripts/evaluation.py +373 -0
  175. package/.claude/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  176. package/.claude/skills/mcp-builder/scripts/requirements.txt +2 -0
  177. package/.claude/skills/pdf/LICENSE.txt +30 -0
  178. package/.claude/skills/pdf/SKILL.md +294 -0
  179. package/.claude/skills/pdf/forms.md +205 -0
  180. package/.claude/skills/pdf/reference.md +612 -0
  181. package/.claude/skills/pdf/scripts/check_bounding_boxes.py +70 -0
  182. package/.claude/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
  183. package/.claude/skills/pdf/scripts/check_fillable_fields.py +12 -0
  184. package/.claude/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
  185. package/.claude/skills/pdf/scripts/create_validation_image.py +41 -0
  186. package/.claude/skills/pdf/scripts/extract_form_field_info.py +152 -0
  187. package/.claude/skills/pdf/scripts/fill_fillable_fields.py +114 -0
  188. package/.claude/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
  189. package/.claude/skills/pptx/LICENSE.txt +30 -0
  190. package/.claude/skills/pptx/SKILL.md +484 -0
  191. package/.claude/skills/pptx/html2pptx.md +625 -0
  192. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  193. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  194. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  195. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  196. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  197. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  198. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  199. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  200. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  201. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  202. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  203. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  204. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  205. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  206. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  207. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  208. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  209. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  210. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  211. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  212. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  213. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  214. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  215. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  216. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  217. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  218. package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  219. package/.claude/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  220. package/.claude/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  221. package/.claude/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  222. package/.claude/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  223. package/.claude/skills/pptx/ooxml/schemas/mce/mc.xsd +75 -0
  224. package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  225. package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  226. package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  227. package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  228. package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  229. package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  230. package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  231. package/.claude/skills/pptx/ooxml/scripts/pack.py +159 -0
  232. package/.claude/skills/pptx/ooxml/scripts/unpack.py +29 -0
  233. package/.claude/skills/pptx/ooxml/scripts/validate.py +69 -0
  234. package/.claude/skills/pptx/ooxml/scripts/validation/__init__.py +15 -0
  235. package/.claude/skills/pptx/ooxml/scripts/validation/base.py +951 -0
  236. package/.claude/skills/pptx/ooxml/scripts/validation/docx.py +274 -0
  237. package/.claude/skills/pptx/ooxml/scripts/validation/pptx.py +315 -0
  238. package/.claude/skills/pptx/ooxml/scripts/validation/redlining.py +279 -0
  239. package/.claude/skills/pptx/ooxml.md +427 -0
  240. package/.claude/skills/pptx/scripts/html2pptx.js +979 -0
  241. package/.claude/skills/pptx/scripts/inventory.py +1020 -0
  242. package/.claude/skills/pptx/scripts/rearrange.py +231 -0
  243. package/.claude/skills/pptx/scripts/replace.py +385 -0
  244. package/.claude/skills/pptx/scripts/thumbnail.py +450 -0
  245. package/.claude/skills/skill-creator/LICENSE.txt +202 -0
  246. package/.claude/skills/skill-creator/SKILL.md +356 -0
  247. package/.claude/skills/skill-creator/references/output-patterns.md +82 -0
  248. package/.claude/skills/skill-creator/references/workflows.md +28 -0
  249. package/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
  250. package/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
  251. package/.claude/skills/skill-creator/scripts/quick_validate.py +95 -0
  252. package/.claude/skills/slack-gif-creator/LICENSE.txt +202 -0
  253. package/.claude/skills/slack-gif-creator/SKILL.md +254 -0
  254. package/.claude/skills/slack-gif-creator/core/easing.py +234 -0
  255. package/.claude/skills/slack-gif-creator/core/frame_composer.py +176 -0
  256. package/.claude/skills/slack-gif-creator/core/gif_builder.py +269 -0
  257. package/.claude/skills/slack-gif-creator/core/validators.py +136 -0
  258. package/.claude/skills/slack-gif-creator/requirements.txt +4 -0
  259. package/.claude/skills/template/SKILL.md +6 -0
  260. package/.claude/skills/test-workflow/SKILL.md +191 -0
  261. package/.claude/skills/theme-factory/LICENSE.txt +202 -0
  262. package/.claude/skills/theme-factory/SKILL.md +59 -0
  263. package/.claude/skills/theme-factory/theme-showcase.pdf +0 -0
  264. package/.claude/skills/theme-factory/themes/arctic-frost.md +19 -0
  265. package/.claude/skills/theme-factory/themes/botanical-garden.md +19 -0
  266. package/.claude/skills/theme-factory/themes/desert-rose.md +19 -0
  267. package/.claude/skills/theme-factory/themes/forest-canopy.md +19 -0
  268. package/.claude/skills/theme-factory/themes/golden-hour.md +19 -0
  269. package/.claude/skills/theme-factory/themes/midnight-galaxy.md +19 -0
  270. package/.claude/skills/theme-factory/themes/modern-minimalist.md +19 -0
  271. package/.claude/skills/theme-factory/themes/ocean-depths.md +19 -0
  272. package/.claude/skills/theme-factory/themes/sunset-boulevard.md +19 -0
  273. package/.claude/skills/theme-factory/themes/tech-innovation.md +19 -0
  274. package/.claude/skills/web-artifacts-builder/LICENSE.txt +202 -0
  275. package/.claude/skills/web-artifacts-builder/SKILL.md +74 -0
  276. package/.claude/skills/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
  277. package/.claude/skills/web-artifacts-builder/scripts/init-artifact.sh +322 -0
  278. package/.claude/skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  279. package/.claude/skills/webapp-testing/LICENSE.txt +202 -0
  280. package/.claude/skills/webapp-testing/SKILL.md +96 -0
  281. package/.claude/skills/webapp-testing/examples/console_logging.py +35 -0
  282. package/.claude/skills/webapp-testing/examples/element_discovery.py +40 -0
  283. package/.claude/skills/webapp-testing/examples/static_html_automation.py +33 -0
  284. package/.claude/skills/webapp-testing/scripts/with_server.py +106 -0
  285. package/.claude/skills/xlsx/LICENSE.txt +30 -0
  286. package/.claude/skills/xlsx/SKILL.md +289 -0
  287. package/.claude/skills/xlsx/recalc.py +178 -0
  288. package/.claude/templates/tasks/develop.md +69 -0
  289. package/.claude/templates/tasks/research.md +64 -0
  290. package/.claude/templates/tasks/test.md +96 -0
  291. package/.claude-plugin/marketplace.json +2 -2
  292. package/.versionrc +25 -0
  293. package/AGENTS.md +171 -86
  294. package/CHANGELOG.md +83 -4
  295. package/PROJECT_STRUCTURE.md +40 -3
  296. package/Q&A.md +184 -0
  297. package/README.md +74 -2
  298. package/cli.js +79 -5
  299. package/config/official-skills.json +183 -0
  300. package/development/todos/.state.json +4 -0
  301. package/development/todos/INDEX.md +67 -32
  302. package/docs/RELEASE.md +93 -0
  303. package/jest.config.js +61 -0
  304. package/lib/commands.js +1724 -39
  305. package/lib/migrations.js +154 -0
  306. package/lib/utils.js +102 -14
  307. package/lib/version-check.js +169 -0
  308. package/package.json +13 -3
  309. package/scripts/fix-hooks.mjs +97 -0
  310. package/template/.claude/commands/commit-push-pr.md +23 -3
  311. package/template/.claude/hooks/project-kickoff.cjs +190 -1
  312. package/template/.claude/hooks/session-restore.cjs +102 -0
  313. package/template/.claude/hooks/session-save.cjs +164 -0
  314. package/template/.claude/settings.json +114 -50
  315. package/tests/README.md +263 -0
  316. package/tests/commands.test.js +163 -0
  317. package/tests/config.test.js +100 -0
  318. package/tests/marketplace.test.js +304 -0
  319. package/tests/migrations.test.js +187 -0
  320. package/tests/utils.test.js +167 -0
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Frame Composer - Utilities for composing visual elements into frames.
4
+
5
+ Provides functions for drawing shapes, text, emojis, and compositing elements
6
+ together to create animation frames.
7
+ """
8
+
9
+ from typing import Optional
10
+
11
+ import numpy as np
12
+ from PIL import Image, ImageDraw, ImageFont
13
+
14
+
15
+ def create_blank_frame(
16
+ width: int, height: int, color: tuple[int, int, int] = (255, 255, 255)
17
+ ) -> Image.Image:
18
+ """
19
+ Create a blank frame with solid color background.
20
+
21
+ Args:
22
+ width: Frame width
23
+ height: Frame height
24
+ color: RGB color tuple (default: white)
25
+
26
+ Returns:
27
+ PIL Image
28
+ """
29
+ return Image.new("RGB", (width, height), color)
30
+
31
+
32
+ def draw_circle(
33
+ frame: Image.Image,
34
+ center: tuple[int, int],
35
+ radius: int,
36
+ fill_color: Optional[tuple[int, int, int]] = None,
37
+ outline_color: Optional[tuple[int, int, int]] = None,
38
+ outline_width: int = 1,
39
+ ) -> Image.Image:
40
+ """
41
+ Draw a circle on a frame.
42
+
43
+ Args:
44
+ frame: PIL Image to draw on
45
+ center: (x, y) center position
46
+ radius: Circle radius
47
+ fill_color: RGB fill color (None for no fill)
48
+ outline_color: RGB outline color (None for no outline)
49
+ outline_width: Outline width in pixels
50
+
51
+ Returns:
52
+ Modified frame
53
+ """
54
+ draw = ImageDraw.Draw(frame)
55
+ x, y = center
56
+ bbox = [x - radius, y - radius, x + radius, y + radius]
57
+ draw.ellipse(bbox, fill=fill_color, outline=outline_color, width=outline_width)
58
+ return frame
59
+
60
+
61
+ def draw_text(
62
+ frame: Image.Image,
63
+ text: str,
64
+ position: tuple[int, int],
65
+ color: tuple[int, int, int] = (0, 0, 0),
66
+ centered: bool = False,
67
+ ) -> Image.Image:
68
+ """
69
+ Draw text on a frame.
70
+
71
+ Args:
72
+ frame: PIL Image to draw on
73
+ text: Text to draw
74
+ position: (x, y) position (top-left unless centered=True)
75
+ color: RGB text color
76
+ centered: If True, center text at position
77
+
78
+ Returns:
79
+ Modified frame
80
+ """
81
+ draw = ImageDraw.Draw(frame)
82
+
83
+ # Uses Pillow's default font.
84
+ # If the font should be changed for the emoji, add additional logic here.
85
+ font = ImageFont.load_default()
86
+
87
+ if centered:
88
+ bbox = draw.textbbox((0, 0), text, font=font)
89
+ text_width = bbox[2] - bbox[0]
90
+ text_height = bbox[3] - bbox[1]
91
+ x = position[0] - text_width // 2
92
+ y = position[1] - text_height // 2
93
+ position = (x, y)
94
+
95
+ draw.text(position, text, fill=color, font=font)
96
+ return frame
97
+
98
+
99
+ def create_gradient_background(
100
+ width: int,
101
+ height: int,
102
+ top_color: tuple[int, int, int],
103
+ bottom_color: tuple[int, int, int],
104
+ ) -> Image.Image:
105
+ """
106
+ Create a vertical gradient background.
107
+
108
+ Args:
109
+ width: Frame width
110
+ height: Frame height
111
+ top_color: RGB color at top
112
+ bottom_color: RGB color at bottom
113
+
114
+ Returns:
115
+ PIL Image with gradient
116
+ """
117
+ frame = Image.new("RGB", (width, height))
118
+ draw = ImageDraw.Draw(frame)
119
+
120
+ # Calculate color step for each row
121
+ r1, g1, b1 = top_color
122
+ r2, g2, b2 = bottom_color
123
+
124
+ for y in range(height):
125
+ # Interpolate color
126
+ ratio = y / height
127
+ r = int(r1 * (1 - ratio) + r2 * ratio)
128
+ g = int(g1 * (1 - ratio) + g2 * ratio)
129
+ b = int(b1 * (1 - ratio) + b2 * ratio)
130
+
131
+ # Draw horizontal line
132
+ draw.line([(0, y), (width, y)], fill=(r, g, b))
133
+
134
+ return frame
135
+
136
+
137
+ def draw_star(
138
+ frame: Image.Image,
139
+ center: tuple[int, int],
140
+ size: int,
141
+ fill_color: tuple[int, int, int],
142
+ outline_color: Optional[tuple[int, int, int]] = None,
143
+ outline_width: int = 1,
144
+ ) -> Image.Image:
145
+ """
146
+ Draw a 5-pointed star.
147
+
148
+ Args:
149
+ frame: PIL Image to draw on
150
+ center: (x, y) center position
151
+ size: Star size (outer radius)
152
+ fill_color: RGB fill color
153
+ outline_color: RGB outline color (None for no outline)
154
+ outline_width: Outline width
155
+
156
+ Returns:
157
+ Modified frame
158
+ """
159
+ import math
160
+
161
+ draw = ImageDraw.Draw(frame)
162
+ x, y = center
163
+
164
+ # Calculate star points
165
+ points = []
166
+ for i in range(10):
167
+ angle = (i * 36 - 90) * math.pi / 180 # 36 degrees per point, start at top
168
+ radius = size if i % 2 == 0 else size * 0.4 # Alternate between outer and inner
169
+ px = x + radius * math.cos(angle)
170
+ py = y + radius * math.sin(angle)
171
+ points.append((px, py))
172
+
173
+ # Draw star
174
+ draw.polygon(points, fill=fill_color, outline=outline_color, width=outline_width)
175
+
176
+ return frame
@@ -0,0 +1,269 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ GIF Builder - Core module for assembling frames into GIFs optimized for Slack.
4
+
5
+ This module provides the main interface for creating GIFs from programmatically
6
+ generated frames, with automatic optimization for Slack's requirements.
7
+ """
8
+
9
+ from pathlib import Path
10
+ from typing import Optional
11
+
12
+ import imageio.v3 as imageio
13
+ import numpy as np
14
+ from PIL import Image
15
+
16
+
17
+ class GIFBuilder:
18
+ """Builder for creating optimized GIFs from frames."""
19
+
20
+ def __init__(self, width: int = 480, height: int = 480, fps: int = 15):
21
+ """
22
+ Initialize GIF builder.
23
+
24
+ Args:
25
+ width: Frame width in pixels
26
+ height: Frame height in pixels
27
+ fps: Frames per second
28
+ """
29
+ self.width = width
30
+ self.height = height
31
+ self.fps = fps
32
+ self.frames: list[np.ndarray] = []
33
+
34
+ def add_frame(self, frame: np.ndarray | Image.Image):
35
+ """
36
+ Add a frame to the GIF.
37
+
38
+ Args:
39
+ frame: Frame as numpy array or PIL Image (will be converted to RGB)
40
+ """
41
+ if isinstance(frame, Image.Image):
42
+ frame = np.array(frame.convert("RGB"))
43
+
44
+ # Ensure frame is correct size
45
+ if frame.shape[:2] != (self.height, self.width):
46
+ pil_frame = Image.fromarray(frame)
47
+ pil_frame = pil_frame.resize(
48
+ (self.width, self.height), Image.Resampling.LANCZOS
49
+ )
50
+ frame = np.array(pil_frame)
51
+
52
+ self.frames.append(frame)
53
+
54
+ def add_frames(self, frames: list[np.ndarray | Image.Image]):
55
+ """Add multiple frames at once."""
56
+ for frame in frames:
57
+ self.add_frame(frame)
58
+
59
+ def optimize_colors(
60
+ self, num_colors: int = 128, use_global_palette: bool = True
61
+ ) -> list[np.ndarray]:
62
+ """
63
+ Reduce colors in all frames using quantization.
64
+
65
+ Args:
66
+ num_colors: Target number of colors (8-256)
67
+ use_global_palette: Use a single palette for all frames (better compression)
68
+
69
+ Returns:
70
+ List of color-optimized frames
71
+ """
72
+ optimized = []
73
+
74
+ if use_global_palette and len(self.frames) > 1:
75
+ # Create a global palette from all frames
76
+ # Sample frames to build palette
77
+ sample_size = min(5, len(self.frames))
78
+ sample_indices = [
79
+ int(i * len(self.frames) / sample_size) for i in range(sample_size)
80
+ ]
81
+ sample_frames = [self.frames[i] for i in sample_indices]
82
+
83
+ # Combine sample frames into a single image for palette generation
84
+ # Flatten each frame to get all pixels, then stack them
85
+ all_pixels = np.vstack(
86
+ [f.reshape(-1, 3) for f in sample_frames]
87
+ ) # (total_pixels, 3)
88
+
89
+ # Create a properly-shaped RGB image from the pixel data
90
+ # We'll make a roughly square image from all the pixels
91
+ total_pixels = len(all_pixels)
92
+ width = min(512, int(np.sqrt(total_pixels))) # Reasonable width, max 512
93
+ height = (total_pixels + width - 1) // width # Ceiling division
94
+
95
+ # Pad if necessary to fill the rectangle
96
+ pixels_needed = width * height
97
+ if pixels_needed > total_pixels:
98
+ padding = np.zeros((pixels_needed - total_pixels, 3), dtype=np.uint8)
99
+ all_pixels = np.vstack([all_pixels, padding])
100
+
101
+ # Reshape to proper RGB image format (H, W, 3)
102
+ img_array = (
103
+ all_pixels[:pixels_needed].reshape(height, width, 3).astype(np.uint8)
104
+ )
105
+ combined_img = Image.fromarray(img_array, mode="RGB")
106
+
107
+ # Generate global palette
108
+ global_palette = combined_img.quantize(colors=num_colors, method=2)
109
+
110
+ # Apply global palette to all frames
111
+ for frame in self.frames:
112
+ pil_frame = Image.fromarray(frame)
113
+ quantized = pil_frame.quantize(palette=global_palette, dither=1)
114
+ optimized.append(np.array(quantized.convert("RGB")))
115
+ else:
116
+ # Use per-frame quantization
117
+ for frame in self.frames:
118
+ pil_frame = Image.fromarray(frame)
119
+ quantized = pil_frame.quantize(colors=num_colors, method=2, dither=1)
120
+ optimized.append(np.array(quantized.convert("RGB")))
121
+
122
+ return optimized
123
+
124
+ def deduplicate_frames(self, threshold: float = 0.9995) -> int:
125
+ """
126
+ Remove duplicate or near-duplicate consecutive frames.
127
+
128
+ Args:
129
+ threshold: Similarity threshold (0.0-1.0). Higher = more strict (0.9995 = nearly identical).
130
+ Use 0.9995+ to preserve subtle animations, 0.98 for aggressive removal.
131
+
132
+ Returns:
133
+ Number of frames removed
134
+ """
135
+ if len(self.frames) < 2:
136
+ return 0
137
+
138
+ deduplicated = [self.frames[0]]
139
+ removed_count = 0
140
+
141
+ for i in range(1, len(self.frames)):
142
+ # Compare with previous frame
143
+ prev_frame = np.array(deduplicated[-1], dtype=np.float32)
144
+ curr_frame = np.array(self.frames[i], dtype=np.float32)
145
+
146
+ # Calculate similarity (normalized)
147
+ diff = np.abs(prev_frame - curr_frame)
148
+ similarity = 1.0 - (np.mean(diff) / 255.0)
149
+
150
+ # Keep frame if sufficiently different
151
+ # High threshold (0.9995+) means only remove nearly identical frames
152
+ if similarity < threshold:
153
+ deduplicated.append(self.frames[i])
154
+ else:
155
+ removed_count += 1
156
+
157
+ self.frames = deduplicated
158
+ return removed_count
159
+
160
+ def save(
161
+ self,
162
+ output_path: str | Path,
163
+ num_colors: int = 128,
164
+ optimize_for_emoji: bool = False,
165
+ remove_duplicates: bool = False,
166
+ ) -> dict:
167
+ """
168
+ Save frames as optimized GIF for Slack.
169
+
170
+ Args:
171
+ output_path: Where to save the GIF
172
+ num_colors: Number of colors to use (fewer = smaller file)
173
+ optimize_for_emoji: If True, optimize for emoji size (128x128, fewer colors)
174
+ remove_duplicates: If True, remove duplicate consecutive frames (opt-in)
175
+
176
+ Returns:
177
+ Dictionary with file info (path, size, dimensions, frame_count)
178
+ """
179
+ if not self.frames:
180
+ raise ValueError("No frames to save. Add frames with add_frame() first.")
181
+
182
+ output_path = Path(output_path)
183
+
184
+ # Remove duplicate frames to reduce file size
185
+ if remove_duplicates:
186
+ removed = self.deduplicate_frames(threshold=0.9995)
187
+ if removed > 0:
188
+ print(
189
+ f" Removed {removed} nearly identical frames (preserved subtle animations)"
190
+ )
191
+
192
+ # Optimize for emoji if requested
193
+ if optimize_for_emoji:
194
+ if self.width > 128 or self.height > 128:
195
+ print(
196
+ f" Resizing from {self.width}x{self.height} to 128x128 for emoji"
197
+ )
198
+ self.width = 128
199
+ self.height = 128
200
+ # Resize all frames
201
+ resized_frames = []
202
+ for frame in self.frames:
203
+ pil_frame = Image.fromarray(frame)
204
+ pil_frame = pil_frame.resize((128, 128), Image.Resampling.LANCZOS)
205
+ resized_frames.append(np.array(pil_frame))
206
+ self.frames = resized_frames
207
+ num_colors = min(num_colors, 48) # More aggressive color limit for emoji
208
+
209
+ # More aggressive FPS reduction for emoji
210
+ if len(self.frames) > 12:
211
+ print(
212
+ f" Reducing frames from {len(self.frames)} to ~12 for emoji size"
213
+ )
214
+ # Keep every nth frame to get close to 12 frames
215
+ keep_every = max(1, len(self.frames) // 12)
216
+ self.frames = [
217
+ self.frames[i] for i in range(0, len(self.frames), keep_every)
218
+ ]
219
+
220
+ # Optimize colors with global palette
221
+ optimized_frames = self.optimize_colors(num_colors, use_global_palette=True)
222
+
223
+ # Calculate frame duration in milliseconds
224
+ frame_duration = 1000 / self.fps
225
+
226
+ # Save GIF
227
+ imageio.imwrite(
228
+ output_path,
229
+ optimized_frames,
230
+ duration=frame_duration,
231
+ loop=0, # Infinite loop
232
+ )
233
+
234
+ # Get file info
235
+ file_size_kb = output_path.stat().st_size / 1024
236
+ file_size_mb = file_size_kb / 1024
237
+
238
+ info = {
239
+ "path": str(output_path),
240
+ "size_kb": file_size_kb,
241
+ "size_mb": file_size_mb,
242
+ "dimensions": f"{self.width}x{self.height}",
243
+ "frame_count": len(optimized_frames),
244
+ "fps": self.fps,
245
+ "duration_seconds": len(optimized_frames) / self.fps,
246
+ "colors": num_colors,
247
+ }
248
+
249
+ # Print info
250
+ print(f"\n✓ GIF created successfully!")
251
+ print(f" Path: {output_path}")
252
+ print(f" Size: {file_size_kb:.1f} KB ({file_size_mb:.2f} MB)")
253
+ print(f" Dimensions: {self.width}x{self.height}")
254
+ print(f" Frames: {len(optimized_frames)} @ {self.fps} fps")
255
+ print(f" Duration: {info['duration_seconds']:.1f}s")
256
+ print(f" Colors: {num_colors}")
257
+
258
+ # Size info
259
+ if optimize_for_emoji:
260
+ print(f" Optimized for emoji (128x128, reduced colors)")
261
+ if file_size_mb > 1.0:
262
+ print(f"\n Note: Large file size ({file_size_kb:.1f} KB)")
263
+ print(" Consider: fewer frames, smaller dimensions, or fewer colors")
264
+
265
+ return info
266
+
267
+ def clear(self):
268
+ """Clear all frames (useful for creating multiple GIFs)."""
269
+ self.frames = []
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Validators - Check if GIFs meet Slack's requirements.
4
+
5
+ These validators help ensure your GIFs meet Slack's size and dimension constraints.
6
+ """
7
+
8
+ from pathlib import Path
9
+
10
+
11
+ def validate_gif(
12
+ gif_path: str | Path, is_emoji: bool = True, verbose: bool = True
13
+ ) -> tuple[bool, dict]:
14
+ """
15
+ Validate GIF for Slack (dimensions, size, frame count).
16
+
17
+ Args:
18
+ gif_path: Path to GIF file
19
+ is_emoji: True for emoji (128x128 recommended), False for message GIF
20
+ verbose: Print validation details
21
+
22
+ Returns:
23
+ Tuple of (passes: bool, results: dict with all details)
24
+ """
25
+ from PIL import Image
26
+
27
+ gif_path = Path(gif_path)
28
+
29
+ if not gif_path.exists():
30
+ return False, {"error": f"File not found: {gif_path}"}
31
+
32
+ # Get file size
33
+ size_bytes = gif_path.stat().st_size
34
+ size_kb = size_bytes / 1024
35
+ size_mb = size_kb / 1024
36
+
37
+ # Get dimensions and frame info
38
+ try:
39
+ with Image.open(gif_path) as img:
40
+ width, height = img.size
41
+
42
+ # Count frames
43
+ frame_count = 0
44
+ try:
45
+ while True:
46
+ img.seek(frame_count)
47
+ frame_count += 1
48
+ except EOFError:
49
+ pass
50
+
51
+ # Get duration
52
+ try:
53
+ duration_ms = img.info.get("duration", 100)
54
+ total_duration = (duration_ms * frame_count) / 1000
55
+ fps = frame_count / total_duration if total_duration > 0 else 0
56
+ except:
57
+ total_duration = None
58
+ fps = None
59
+
60
+ except Exception as e:
61
+ return False, {"error": f"Failed to read GIF: {e}"}
62
+
63
+ # Validate dimensions
64
+ if is_emoji:
65
+ optimal = width == height == 128
66
+ acceptable = width == height and 64 <= width <= 128
67
+ dim_pass = acceptable
68
+ else:
69
+ aspect_ratio = (
70
+ max(width, height) / min(width, height)
71
+ if min(width, height) > 0
72
+ else float("inf")
73
+ )
74
+ dim_pass = aspect_ratio <= 2.0 and 320 <= min(width, height) <= 640
75
+
76
+ results = {
77
+ "file": str(gif_path),
78
+ "passes": dim_pass,
79
+ "width": width,
80
+ "height": height,
81
+ "size_kb": size_kb,
82
+ "size_mb": size_mb,
83
+ "frame_count": frame_count,
84
+ "duration_seconds": total_duration,
85
+ "fps": fps,
86
+ "is_emoji": is_emoji,
87
+ "optimal": optimal if is_emoji else None,
88
+ }
89
+
90
+ # Print if verbose
91
+ if verbose:
92
+ print(f"\nValidating {gif_path.name}:")
93
+ print(
94
+ f" Dimensions: {width}x{height}"
95
+ + (
96
+ f" ({'optimal' if optimal else 'acceptable'})"
97
+ if is_emoji and acceptable
98
+ else ""
99
+ )
100
+ )
101
+ print(
102
+ f" Size: {size_kb:.1f} KB"
103
+ + (f" ({size_mb:.2f} MB)" if size_mb >= 1.0 else "")
104
+ )
105
+ print(
106
+ f" Frames: {frame_count}"
107
+ + (f" @ {fps:.1f} fps ({total_duration:.1f}s)" if fps else "")
108
+ )
109
+
110
+ if not dim_pass:
111
+ print(
112
+ f" Note: {'Emoji should be 128x128' if is_emoji else 'Unusual dimensions for Slack'}"
113
+ )
114
+
115
+ if size_mb > 5.0:
116
+ print(f" Note: Large file size - consider fewer frames/colors")
117
+
118
+ return dim_pass, results
119
+
120
+
121
+ def is_slack_ready(
122
+ gif_path: str | Path, is_emoji: bool = True, verbose: bool = True
123
+ ) -> bool:
124
+ """
125
+ Quick check if GIF is ready for Slack.
126
+
127
+ Args:
128
+ gif_path: Path to GIF file
129
+ is_emoji: True for emoji GIF, False for message GIF
130
+ verbose: Print feedback
131
+
132
+ Returns:
133
+ True if dimensions are acceptable
134
+ """
135
+ passes, _ = validate_gif(gif_path, is_emoji, verbose)
136
+ return passes
@@ -0,0 +1,4 @@
1
+ pillow>=10.0.0
2
+ imageio>=2.31.0
3
+ imageio-ffmpeg>=0.4.9
4
+ numpy>=1.24.0
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: template-skill
3
+ description: Replace with description of the skill and when Claude should use it.
4
+ ---
5
+
6
+ # Insert instructions below