wormclaude 1.0.74 → 1.0.76

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 (228) hide show
  1. package/dist/theme.js +4 -4
  2. package/package.json +2 -2
  3. package/skills/build-mcp-app/SKILL.md +393 -0
  4. package/skills/build-mcp-app/references/abuse-protection.md +60 -0
  5. package/skills/build-mcp-app/references/apps-sdk-messages.md +227 -0
  6. package/skills/build-mcp-app/references/directory-checklist.md +18 -0
  7. package/skills/build-mcp-app/references/iframe-sandbox.md +164 -0
  8. package/skills/build-mcp-app/references/payload-budgeting.md +54 -0
  9. package/skills/build-mcp-app/references/widget-templates.md +249 -0
  10. package/skills/build-mcp-server/SKILL.md +222 -0
  11. package/skills/build-mcp-server/references/auth.md +108 -0
  12. package/skills/build-mcp-server/references/deploy-cloudflare-workers.md +106 -0
  13. package/skills/build-mcp-server/references/elicitation.md +129 -0
  14. package/skills/build-mcp-server/references/remote-http-scaffold.md +211 -0
  15. package/skills/build-mcp-server/references/resources-and-prompts.md +122 -0
  16. package/skills/build-mcp-server/references/server-capabilities.md +164 -0
  17. package/skills/build-mcp-server/references/tool-design.md +189 -0
  18. package/skills/build-mcp-server/references/versions.md +25 -0
  19. package/skills/build-mcpb/SKILL.md +200 -0
  20. package/skills/build-mcpb/references/local-security.md +149 -0
  21. package/skills/build-mcpb/references/manifest-schema.md +156 -0
  22. package/skills/docx/script/__init__.py +1 -0
  23. package/skills/docx/script/accept_chages.py +135 -0
  24. package/skills/docx/script/comment.py +318 -0
  25. package/skills/docx/script/office/helpers/__init__.py +0 -0
  26. package/skills/docx/script/office/helpers/merge_runs.py +199 -0
  27. package/skills/docx/script/office/helpers/simplify_redlines.py +197 -0
  28. package/skills/docx/script/office/pack.py +159 -0
  29. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  30. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  31. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  32. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  33. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  34. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  35. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  36. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  37. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  38. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  39. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  40. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  41. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  42. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  43. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  44. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  45. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  46. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  47. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  48. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  49. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  50. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  51. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  52. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  53. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  54. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  55. package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  56. package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  57. package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  58. package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  59. package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  60. package/skills/docx/script/office/schemas/mce/mc.xsd +75 -0
  61. package/skills/docx/script/office/schemas/microsoft/wml-2010.xsd +560 -0
  62. package/skills/docx/script/office/schemas/microsoft/wml-2012.xsd +67 -0
  63. package/skills/docx/script/office/schemas/microsoft/wml-2018.xsd +14 -0
  64. package/skills/docx/script/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  65. package/skills/docx/script/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  66. package/skills/docx/script/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  67. package/skills/docx/script/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  68. package/skills/docx/script/office/soffice.py +183 -0
  69. package/skills/docx/script/office/unpack.py +132 -0
  70. package/skills/docx/script/office/validate.py +117 -0
  71. package/skills/docx/script/office/validators/__init__.py +15 -0
  72. package/skills/docx/script/office/validators/base.py +851 -0
  73. package/skills/docx/script/office/validators/docx.py +446 -0
  74. package/skills/docx/script/office/validators/pptx.py +275 -0
  75. package/skills/docx/script/office/validators/redlining.py +247 -0
  76. package/skills/docx/script/templates/comments.xml +3 -0
  77. package/skills/docx/script/templates/commentsExtended.xml +3 -0
  78. package/skills/docx/script/templates/commentsExtensible.xml +3 -0
  79. package/skills/docx/script/templates/commentsIds.xml +3 -0
  80. package/skills/docx/script/templates/people.xml +3 -0
  81. package/skills/docx/skill.md +593 -0
  82. package/skills/frontend-design/SKILL.md +42 -0
  83. package/skills/pdf/FORMS.md +294 -0
  84. package/skills/pdf/REFERENCE.md +612 -0
  85. package/skills/pdf/SKILL.md +314 -0
  86. package/skills/pdf/scripts/check_bounding_boxes.py +65 -0
  87. package/skills/pdf/scripts/check_fillable_fields.py +11 -0
  88. package/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
  89. package/skills/pdf/scripts/create_validation_image.py +37 -0
  90. package/skills/pdf/scripts/extract_form_field_info.py +122 -0
  91. package/skills/pdf/scripts/extract_form_structure.py +115 -0
  92. package/skills/pdf/scripts/fill_fillable_fields.py +98 -0
  93. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
  94. package/skills/playground/SKILL.md +77 -0
  95. package/skills/playground/templates/code-map.md +158 -0
  96. package/skills/playground/templates/concept-map.md +73 -0
  97. package/skills/playground/templates/data-explorer.md +67 -0
  98. package/skills/playground/templates/design-playground.md +67 -0
  99. package/skills/playground/templates/diff-review.md +179 -0
  100. package/skills/playground/templates/document-critique.md +171 -0
  101. package/skills/pptx/SKILL.md +230 -0
  102. package/skills/pptx/editing.md +205 -0
  103. package/skills/pptx/pptxgenjs.md +437 -0
  104. package/skills/pptx/scripts/__init__.py +0 -0
  105. package/skills/pptx/scripts/add_slide.py +195 -0
  106. package/skills/pptx/scripts/clean.py +286 -0
  107. package/skills/pptx/scripts/office/helpers/__init__.py +0 -0
  108. package/skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
  109. package/skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
  110. package/skills/pptx/scripts/office/pack.py +159 -0
  111. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  112. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  113. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  114. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  115. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  116. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  117. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  118. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  119. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  120. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  121. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  122. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  123. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  124. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  125. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  126. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  127. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  128. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  129. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  130. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  131. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  132. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  133. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  134. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  135. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  136. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  137. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  138. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  139. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  140. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  141. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  142. package/skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
  143. package/skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  144. package/skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  145. package/skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  146. package/skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  147. package/skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  148. package/skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  149. package/skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  150. package/skills/pptx/scripts/office/soffice.py +183 -0
  151. package/skills/pptx/scripts/office/unpack.py +132 -0
  152. package/skills/pptx/scripts/office/validate.py +117 -0
  153. package/skills/pptx/scripts/office/validators/__init__.py +15 -0
  154. package/skills/pptx/scripts/office/validators/base.py +851 -0
  155. package/skills/pptx/scripts/office/validators/docx.py +446 -0
  156. package/skills/pptx/scripts/office/validators/pptx.py +275 -0
  157. package/skills/pptx/scripts/office/validators/redlining.py +247 -0
  158. package/skills/pptx/scripts/thumbnail.py +289 -0
  159. package/skills/talent-creator/SKILL.md +486 -0
  160. package/skills/talent-creator/agents/analyzer.md +274 -0
  161. package/skills/talent-creator/agents/comparator.md +202 -0
  162. package/skills/talent-creator/agents/grader.md +223 -0
  163. package/skills/talent-creator/assets/eval_review.html +146 -0
  164. package/skills/talent-creator/eval-viewer/generate_review.py +471 -0
  165. package/skills/talent-creator/eval-viewer/viewer.html +1325 -0
  166. package/skills/talent-creator/references/schemas.md +430 -0
  167. package/skills/talent-creator/scripts/__init__.py +0 -0
  168. package/skills/talent-creator/scripts/aggregate_benchmark.py +401 -0
  169. package/skills/talent-creator/scripts/generate_report.py +326 -0
  170. package/skills/talent-creator/scripts/improve_description.py +247 -0
  171. package/skills/talent-creator/scripts/package_skill.py +136 -0
  172. package/skills/talent-creator/scripts/quick_validate.py +146 -0
  173. package/skills/talent-creator/scripts/run_eval.py +310 -0
  174. package/skills/talent-creator/scripts/run_loop.py +328 -0
  175. package/skills/talent-creator/scripts/utils.py +47 -0
  176. package/skills/xlsx/SKILL.md +300 -0
  177. package/skills/xlsx/scripts/office/helpers/__init__.py +0 -0
  178. package/skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
  179. package/skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
  180. package/skills/xlsx/scripts/office/pack.py +159 -0
  181. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  182. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  183. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  184. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  185. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  186. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  187. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  188. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  189. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  190. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  191. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  192. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  193. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  194. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  195. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  196. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  197. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  198. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  199. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  200. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  201. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  202. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  203. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  204. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  205. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  206. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  207. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  208. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  209. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  210. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  211. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  212. package/skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
  213. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  214. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  215. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  216. package/skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  217. package/skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  218. package/skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  219. package/skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  220. package/skills/xlsx/scripts/office/soffice.py +183 -0
  221. package/skills/xlsx/scripts/office/unpack.py +132 -0
  222. package/skills/xlsx/scripts/office/validate.py +117 -0
  223. package/skills/xlsx/scripts/office/validators/__init__.py +15 -0
  224. package/skills/xlsx/scripts/office/validators/base.py +851 -0
  225. package/skills/xlsx/scripts/office/validators/docx.py +446 -0
  226. package/skills/xlsx/scripts/office/validators/pptx.py +275 -0
  227. package/skills/xlsx/scripts/office/validators/redlining.py +247 -0
  228. package/skills/xlsx/scripts/recalc.py +184 -0
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Skill Packager - Creates a distributable .skill file of a skill folder
4
+
5
+ Usage:
6
+ python utils/package_skill.py <path/to/skill-folder> [output-directory]
7
+
8
+ Example:
9
+ python utils/package_skill.py skills/public/my-skill
10
+ python utils/package_skill.py skills/public/my-skill ./dist
11
+ """
12
+
13
+ import fnmatch
14
+ import sys
15
+ import zipfile
16
+ from pathlib import Path
17
+ from scripts.quick_validate import validate_skill
18
+
19
+ # Patterns to exclude when packaging skills.
20
+ EXCLUDE_DIRS = {"__pycache__", "node_modules"}
21
+ EXCLUDE_GLOBS = {"*.pyc"}
22
+ EXCLUDE_FILES = {".DS_Store"}
23
+ # Directories excluded only at the skill root (not when nested deeper).
24
+ ROOT_EXCLUDE_DIRS = {"evals"}
25
+
26
+
27
+ def should_exclude(rel_path: Path) -> bool:
28
+ """Check if a path should be excluded from packaging."""
29
+ parts = rel_path.parts
30
+ if any(part in EXCLUDE_DIRS for part in parts):
31
+ return True
32
+ # rel_path is relative to skill_path.parent, so parts[0] is the skill
33
+ # folder name and parts[1] (if present) is the first subdir.
34
+ if len(parts) > 1 and parts[1] in ROOT_EXCLUDE_DIRS:
35
+ return True
36
+ name = rel_path.name
37
+ if name in EXCLUDE_FILES:
38
+ return True
39
+ return any(fnmatch.fnmatch(name, pat) for pat in EXCLUDE_GLOBS)
40
+
41
+
42
+ def package_skill(skill_path, output_dir=None):
43
+ """
44
+ Package a skill folder into a .skill file.
45
+
46
+ Args:
47
+ skill_path: Path to the skill folder
48
+ output_dir: Optional output directory for the .skill file (defaults to current directory)
49
+
50
+ Returns:
51
+ Path to the created .skill file, or None if error
52
+ """
53
+ skill_path = Path(skill_path).resolve()
54
+
55
+ # Validate skill folder exists
56
+ if not skill_path.exists():
57
+ print(f"❌ Error: Skill folder not found: {skill_path}")
58
+ return None
59
+
60
+ if not skill_path.is_dir():
61
+ print(f"❌ Error: Path is not a directory: {skill_path}")
62
+ return None
63
+
64
+ # Validate SKILL.md exists
65
+ skill_md = skill_path / "SKILL.md"
66
+ if not skill_md.exists():
67
+ print(f"❌ Error: SKILL.md not found in {skill_path}")
68
+ return None
69
+
70
+ # Run validation before packaging
71
+ print("🔍 Validating skill...")
72
+ valid, message = validate_skill(skill_path)
73
+ if not valid:
74
+ print(f"❌ Validation failed: {message}")
75
+ print(" Please fix the validation errors before packaging.")
76
+ return None
77
+ print(f"✅ {message}\n")
78
+
79
+ # Determine output location
80
+ skill_name = skill_path.name
81
+ if output_dir:
82
+ output_path = Path(output_dir).resolve()
83
+ output_path.mkdir(parents=True, exist_ok=True)
84
+ else:
85
+ output_path = Path.cwd()
86
+
87
+ skill_filename = output_path / f"{skill_name}.skill"
88
+
89
+ # Create the .skill file (zip format)
90
+ try:
91
+ with zipfile.ZipFile(skill_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
92
+ # Walk through the skill directory, excluding build artifacts
93
+ for file_path in skill_path.rglob('*'):
94
+ if not file_path.is_file():
95
+ continue
96
+ arcname = file_path.relative_to(skill_path.parent)
97
+ if should_exclude(arcname):
98
+ print(f" Skipped: {arcname}")
99
+ continue
100
+ zipf.write(file_path, arcname)
101
+ print(f" Added: {arcname}")
102
+
103
+ print(f"\n✅ Successfully packaged skill to: {skill_filename}")
104
+ return skill_filename
105
+
106
+ except Exception as e:
107
+ print(f"❌ Error creating .skill file: {e}")
108
+ return None
109
+
110
+
111
+ def main():
112
+ if len(sys.argv) < 2:
113
+ print("Usage: python utils/package_skill.py <path/to/skill-folder> [output-directory]")
114
+ print("\nExample:")
115
+ print(" python utils/package_skill.py skills/public/my-skill")
116
+ print(" python utils/package_skill.py skills/public/my-skill ./dist")
117
+ sys.exit(1)
118
+
119
+ skill_path = sys.argv[1]
120
+ output_dir = sys.argv[2] if len(sys.argv) > 2 else None
121
+
122
+ print(f"📦 Packaging skill: {skill_path}")
123
+ if output_dir:
124
+ print(f" Output directory: {output_dir}")
125
+ print()
126
+
127
+ result = package_skill(skill_path, output_dir)
128
+
129
+ if result:
130
+ sys.exit(0)
131
+ else:
132
+ sys.exit(1)
133
+
134
+
135
+ if __name__ == "__main__":
136
+ main()
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Quick validation script for skills - minimal version
4
+ """
5
+
6
+ import sys
7
+ import re
8
+ import yaml
9
+ from pathlib import Path
10
+
11
+ # Directories whose contents are not packaged as part of the skill, so any
12
+ # SKILL.md inside them shouldn't count toward the single-SKILL.md check below.
13
+ # Mirrors package_skill.py: __pycache__ and node_modules are excluded at any
14
+ # depth, while evals is only excluded at the skill root.
15
+ EXCLUDED_DIR_PARTS = {'__pycache__', 'node_modules'}
16
+ ROOT_EXCLUDED_DIR_PARTS = {'evals'}
17
+
18
+
19
+ def _counts_as_skill_md(rel_path):
20
+ """True if a SKILL.md at rel_path (relative to the skill root) would be packaged."""
21
+ dir_parts = rel_path.parts[:-1]
22
+ if any(part in EXCLUDED_DIR_PARTS for part in dir_parts):
23
+ return False
24
+ if dir_parts and dir_parts[0] in ROOT_EXCLUDED_DIR_PARTS:
25
+ return False
26
+ return True
27
+
28
+
29
+ def validate_skill(skill_path):
30
+ """Basic validation of a skill"""
31
+ skill_path = Path(skill_path)
32
+
33
+ # Check SKILL.md exists
34
+ skill_md = skill_path / 'SKILL.md'
35
+ if not skill_md.exists():
36
+ return False, "SKILL.md not found"
37
+
38
+ # A skill must contain exactly one SKILL.md, at <folder>/SKILL.md. Extra
39
+ # (nested) SKILL.md files are rejected on upload: the Skills API and wormclaude.ai
40
+ # accept exactly one per skill — only wormclaude Code's filesystem loads nested
41
+ # ones. package_skill produces an upload-bound .skill, so block here rather
42
+ # than ship an artifact that's guaranteed to fail on upload.
43
+ skill_md_files = [
44
+ p for p in skill_path.rglob('SKILL.md')
45
+ if _counts_as_skill_md(p.relative_to(skill_path))
46
+ ]
47
+ if len(skill_md_files) > 1:
48
+ extras = sorted(
49
+ str(p.relative_to(skill_path)) for p in skill_md_files
50
+ if p.resolve() != skill_md.resolve()
51
+ )
52
+ return False, (
53
+ f"Found {len(skill_md_files)} SKILL.md files, but a skill must contain "
54
+ f"exactly one at <folder>/SKILL.md. The Skills API and wormclaude.ai reject "
55
+ f"multiple on upload (only wormclaude Code's filesystem loads nested skills). "
56
+ f"Extra: {', '.join(extras)}.\n"
57
+ " - Separate skills: package each on its own, or build a plugin "
58
+ "(skills/<name>/SKILL.md).\n"
59
+ " - Supporting docs: rename to non-SKILL.md files (e.g. references/<topic>.md).\n"
60
+ " - Swept in by mistake: package only the one skill directory."
61
+ )
62
+
63
+ # Read and validate frontmatter
64
+ content = skill_md.read_text()
65
+ if not content.startswith('---'):
66
+ return False, "No YAML frontmatter found"
67
+
68
+ # Extract frontmatter
69
+ match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
70
+ if not match:
71
+ return False, "Invalid frontmatter format"
72
+
73
+ frontmatter_text = match.group(1)
74
+
75
+ # Parse YAML frontmatter
76
+ try:
77
+ frontmatter = yaml.safe_load(frontmatter_text)
78
+ if not isinstance(frontmatter, dict):
79
+ return False, "Frontmatter must be a YAML dictionary"
80
+ except yaml.YAMLError as e:
81
+ return False, f"Invalid YAML in frontmatter: {e}"
82
+
83
+ # Define allowed properties
84
+ ALLOWED_PROPERTIES = {'name', 'description', 'license', 'allowed-tools', 'metadata', 'compatibility'}
85
+
86
+ # Check for unexpected properties (excluding nested keys under metadata)
87
+ unexpected_keys = set(frontmatter.keys()) - ALLOWED_PROPERTIES
88
+ if unexpected_keys:
89
+ return False, (
90
+ f"Unexpected key(s) in SKILL.md frontmatter: {', '.join(sorted(unexpected_keys))}. "
91
+ f"Allowed properties are: {', '.join(sorted(ALLOWED_PROPERTIES))}"
92
+ )
93
+
94
+ # Check required fields
95
+ if 'name' not in frontmatter:
96
+ return False, "Missing 'name' in frontmatter"
97
+ if 'description' not in frontmatter:
98
+ return False, "Missing 'description' in frontmatter"
99
+
100
+ # Extract name for validation
101
+ name = frontmatter.get('name', '')
102
+ if not isinstance(name, str):
103
+ return False, f"Name must be a string, got {type(name).__name__}"
104
+ name = name.strip()
105
+ if name:
106
+ # Check naming convention (kebab-case: lowercase with hyphens)
107
+ if not re.match(r'^[a-z0-9-]+$', name):
108
+ return False, f"Name '{name}' should be kebab-case (lowercase letters, digits, and hyphens only)"
109
+ if name.startswith('-') or name.endswith('-') or '--' in name:
110
+ return False, f"Name '{name}' cannot start/end with hyphen or contain consecutive hyphens"
111
+ # Check name length (max 64 characters per spec)
112
+ if len(name) > 64:
113
+ return False, f"Name is too long ({len(name)} characters). Maximum is 64 characters."
114
+
115
+ # Extract and validate description
116
+ description = frontmatter.get('description', '')
117
+ if not isinstance(description, str):
118
+ return False, f"Description must be a string, got {type(description).__name__}"
119
+ description = description.strip()
120
+ if description:
121
+ # Check for angle brackets
122
+ if '<' in description or '>' in description:
123
+ return False, "Description cannot contain angle brackets (< or >)"
124
+ # Check description length (max 1024 characters per spec)
125
+ if len(description) > 1024:
126
+ return False, f"Description is too long ({len(description)} characters). Maximum is 1024 characters."
127
+
128
+ # Validate compatibility field if present (optional)
129
+ compatibility = frontmatter.get('compatibility', '')
130
+ if compatibility:
131
+ if not isinstance(compatibility, str):
132
+ return False, f"Compatibility must be a string, got {type(compatibility).__name__}"
133
+ if len(compatibility) > 500:
134
+ return False, f"Compatibility is too long ({len(compatibility)} characters). Maximum is 500 characters."
135
+
136
+ return True, "Skill is valid!"
137
+
138
+
139
+ if __name__ == "__main__":
140
+ if len(sys.argv) != 2:
141
+ print("Usage: python quick_validate.py <skill_directory>")
142
+ sys.exit(1)
143
+
144
+ valid, message = validate_skill(sys.argv[1])
145
+ print(message)
146
+ sys.exit(0 if valid else 1)
@@ -0,0 +1,310 @@
1
+ #!/usr/bin/env python3
2
+ """Run trigger evaluation for a skill description.
3
+
4
+ Tests whether a skill's description causes wormclaude to trigger (read the skill)
5
+ for a set of queries. Outputs results as JSON.
6
+ """
7
+
8
+ import argparse
9
+ import json
10
+ import os
11
+ import select
12
+ import subprocess
13
+ import sys
14
+ import time
15
+ import uuid
16
+ from concurrent.futures import ProcessPoolExecutor, as_completed
17
+ from pathlib import Path
18
+
19
+ from scripts.utils import parse_skill_md
20
+
21
+
22
+ def find_project_root() -> Path:
23
+ """Find the project root by walking up from cwd looking for .wormclaude/.
24
+
25
+ Mimics how wormclaude Code discovers its project root, so the command file
26
+ we create ends up where wormclaude -p will look for it.
27
+ """
28
+ current = Path.cwd()
29
+ for parent in [current, *current.parents]:
30
+ if (parent / ".wormclaude").is_dir():
31
+ return parent
32
+ return current
33
+
34
+
35
+ def run_single_query(
36
+ query: str,
37
+ skill_name: str,
38
+ skill_description: str,
39
+ timeout: int,
40
+ project_root: str,
41
+ model: str | None = None,
42
+ ) -> bool:
43
+ """Run a single query and return whether the skill was triggered.
44
+
45
+ Creates a command file in .wormclaude/commands/ so it appears in wormclaude's
46
+ available_skills list, then runs `wormclaude -p` with the raw query.
47
+ Uses --include-partial-messages to detect triggering early from
48
+ stream events (content_block_start) rather than waiting for the
49
+ full assistant message, which only arrives after tool execution.
50
+ """
51
+ unique_id = uuid.uuid4().hex[:8]
52
+ clean_name = f"{skill_name}-skill-{unique_id}"
53
+ project_commands_dir = Path(project_root) / ".wormclaude" / "commands"
54
+ command_file = project_commands_dir / f"{clean_name}.md"
55
+
56
+ try:
57
+ project_commands_dir.mkdir(parents=True, exist_ok=True)
58
+ # Use YAML block scalar to avoid breaking on quotes in description
59
+ indented_desc = "\n ".join(skill_description.split("\n"))
60
+ command_content = (
61
+ f"---\n"
62
+ f"description: |\n"
63
+ f" {indented_desc}\n"
64
+ f"---\n\n"
65
+ f"# {skill_name}\n\n"
66
+ f"This skill handles: {skill_description}\n"
67
+ )
68
+ command_file.write_text(command_content)
69
+
70
+ cmd = [
71
+ "wormclaude",
72
+ "-p", query,
73
+ "--output-format", "stream-json",
74
+ "--verbose",
75
+ "--include-partial-messages",
76
+ ]
77
+ if model:
78
+ cmd.extend(["--model", model])
79
+
80
+ # Remove wormclaudeCODE env var to allow nesting wormclaude -p inside a
81
+ # wormclaude Code session. The guard is for interactive terminal conflicts;
82
+ # programmatic subprocess usage is safe.
83
+ env = {k: v for k, v in os.environ.items() if k != "wormclaudeCODE"}
84
+
85
+ process = subprocess.Popen(
86
+ cmd,
87
+ stdout=subprocess.PIPE,
88
+ stderr=subprocess.DEVNULL,
89
+ cwd=project_root,
90
+ env=env,
91
+ )
92
+
93
+ triggered = False
94
+ start_time = time.time()
95
+ buffer = ""
96
+ # Track state for stream event detection
97
+ pending_tool_name = None
98
+ accumulated_json = ""
99
+
100
+ try:
101
+ while time.time() - start_time < timeout:
102
+ if process.poll() is not None:
103
+ remaining = process.stdout.read()
104
+ if remaining:
105
+ buffer += remaining.decode("utf-8", errors="replace")
106
+ break
107
+
108
+ ready, _, _ = select.select([process.stdout], [], [], 1.0)
109
+ if not ready:
110
+ continue
111
+
112
+ chunk = os.read(process.stdout.fileno(), 8192)
113
+ if not chunk:
114
+ break
115
+ buffer += chunk.decode("utf-8", errors="replace")
116
+
117
+ while "\n" in buffer:
118
+ line, buffer = buffer.split("\n", 1)
119
+ line = line.strip()
120
+ if not line:
121
+ continue
122
+
123
+ try:
124
+ event = json.loads(line)
125
+ except json.JSONDecodeError:
126
+ continue
127
+
128
+ # Early detection via stream events
129
+ if event.get("type") == "stream_event":
130
+ se = event.get("event", {})
131
+ se_type = se.get("type", "")
132
+
133
+ if se_type == "content_block_start":
134
+ cb = se.get("content_block", {})
135
+ if cb.get("type") == "tool_use":
136
+ tool_name = cb.get("name", "")
137
+ if tool_name in ("Skill", "Read"):
138
+ pending_tool_name = tool_name
139
+ accumulated_json = ""
140
+ else:
141
+ return False
142
+
143
+ elif se_type == "content_block_delta" and pending_tool_name:
144
+ delta = se.get("delta", {})
145
+ if delta.get("type") == "input_json_delta":
146
+ accumulated_json += delta.get("partial_json", "")
147
+ if clean_name in accumulated_json:
148
+ return True
149
+
150
+ elif se_type in ("content_block_stop", "message_stop"):
151
+ if pending_tool_name:
152
+ return clean_name in accumulated_json
153
+ if se_type == "message_stop":
154
+ return False
155
+
156
+ # Fallback: full assistant message
157
+ elif event.get("type") == "assistant":
158
+ message = event.get("message", {})
159
+ for content_item in message.get("content", []):
160
+ if content_item.get("type") != "tool_use":
161
+ continue
162
+ tool_name = content_item.get("name", "")
163
+ tool_input = content_item.get("input", {})
164
+ if tool_name == "Skill" and clean_name in tool_input.get("skill", ""):
165
+ triggered = True
166
+ elif tool_name == "Read" and clean_name in tool_input.get("file_path", ""):
167
+ triggered = True
168
+ return triggered
169
+
170
+ elif event.get("type") == "result":
171
+ return triggered
172
+ finally:
173
+ # Clean up process on any exit path (return, exception, timeout)
174
+ if process.poll() is None:
175
+ process.kill()
176
+ process.wait()
177
+
178
+ return triggered
179
+ finally:
180
+ if command_file.exists():
181
+ command_file.unlink()
182
+
183
+
184
+ def run_eval(
185
+ eval_set: list[dict],
186
+ skill_name: str,
187
+ description: str,
188
+ num_workers: int,
189
+ timeout: int,
190
+ project_root: Path,
191
+ runs_per_query: int = 1,
192
+ trigger_threshold: float = 0.5,
193
+ model: str | None = None,
194
+ ) -> dict:
195
+ """Run the full eval set and return results."""
196
+ results = []
197
+
198
+ with ProcessPoolExecutor(max_workers=num_workers) as executor:
199
+ future_to_info = {}
200
+ for item in eval_set:
201
+ for run_idx in range(runs_per_query):
202
+ future = executor.submit(
203
+ run_single_query,
204
+ item["query"],
205
+ skill_name,
206
+ description,
207
+ timeout,
208
+ str(project_root),
209
+ model,
210
+ )
211
+ future_to_info[future] = (item, run_idx)
212
+
213
+ query_triggers: dict[str, list[bool]] = {}
214
+ query_items: dict[str, dict] = {}
215
+ for future in as_completed(future_to_info):
216
+ item, _ = future_to_info[future]
217
+ query = item["query"]
218
+ query_items[query] = item
219
+ if query not in query_triggers:
220
+ query_triggers[query] = []
221
+ try:
222
+ query_triggers[query].append(future.result())
223
+ except Exception as e:
224
+ print(f"Warning: query failed: {e}", file=sys.stderr)
225
+ query_triggers[query].append(False)
226
+
227
+ for query, triggers in query_triggers.items():
228
+ item = query_items[query]
229
+ trigger_rate = sum(triggers) / len(triggers)
230
+ should_trigger = item["should_trigger"]
231
+ if should_trigger:
232
+ did_pass = trigger_rate >= trigger_threshold
233
+ else:
234
+ did_pass = trigger_rate < trigger_threshold
235
+ results.append({
236
+ "query": query,
237
+ "should_trigger": should_trigger,
238
+ "trigger_rate": trigger_rate,
239
+ "triggers": sum(triggers),
240
+ "runs": len(triggers),
241
+ "pass": did_pass,
242
+ })
243
+
244
+ passed = sum(1 for r in results if r["pass"])
245
+ total = len(results)
246
+
247
+ return {
248
+ "skill_name": skill_name,
249
+ "description": description,
250
+ "results": results,
251
+ "summary": {
252
+ "total": total,
253
+ "passed": passed,
254
+ "failed": total - passed,
255
+ },
256
+ }
257
+
258
+
259
+ def main():
260
+ parser = argparse.ArgumentParser(description="Run trigger evaluation for a skill description")
261
+ parser.add_argument("--eval-set", required=True, help="Path to eval set JSON file")
262
+ parser.add_argument("--skill-path", required=True, help="Path to skill directory")
263
+ parser.add_argument("--description", default=None, help="Override description to test")
264
+ parser.add_argument("--num-workers", type=int, default=10, help="Number of parallel workers")
265
+ parser.add_argument("--timeout", type=int, default=30, help="Timeout per query in seconds")
266
+ parser.add_argument("--runs-per-query", type=int, default=3, help="Number of runs per query")
267
+ parser.add_argument("--trigger-threshold", type=float, default=0.5, help="Trigger rate threshold")
268
+ parser.add_argument("--model", default=None, help="Model to use for wormclaude -p (default: user's configured model)")
269
+ parser.add_argument("--verbose", action="store_true", help="Print progress to stderr")
270
+ args = parser.parse_args()
271
+
272
+ eval_set = json.loads(Path(args.eval_set).read_text())
273
+ skill_path = Path(args.skill_path)
274
+
275
+ if not (skill_path / "SKILL.md").exists():
276
+ print(f"Error: No SKILL.md found at {skill_path}", file=sys.stderr)
277
+ sys.exit(1)
278
+
279
+ name, original_description, content = parse_skill_md(skill_path)
280
+ description = args.description or original_description
281
+ project_root = find_project_root()
282
+
283
+ if args.verbose:
284
+ print(f"Evaluating: {description}", file=sys.stderr)
285
+
286
+ output = run_eval(
287
+ eval_set=eval_set,
288
+ skill_name=name,
289
+ description=description,
290
+ num_workers=args.num_workers,
291
+ timeout=args.timeout,
292
+ project_root=project_root,
293
+ runs_per_query=args.runs_per_query,
294
+ trigger_threshold=args.trigger_threshold,
295
+ model=args.model,
296
+ )
297
+
298
+ if args.verbose:
299
+ summary = output["summary"]
300
+ print(f"Results: {summary['passed']}/{summary['total']} passed", file=sys.stderr)
301
+ for r in output["results"]:
302
+ status = "PASS" if r["pass"] else "FAIL"
303
+ rate_str = f"{r['triggers']}/{r['runs']}"
304
+ print(f" [{status}] rate={rate_str} expected={r['should_trigger']}: {r['query'][:70]}", file=sys.stderr)
305
+
306
+ print(json.dumps(output, indent=2))
307
+
308
+
309
+ if __name__ == "__main__":
310
+ main()