sumulige-claude 1.0.8 → 1.0.11
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.
- package/.claude/.version +1 -0
- package/.claude/AGENTS.md +9 -9
- package/.claude/commands/commit-push-pr.md +23 -3
- package/.claude/sessions/active-sessions.json +1 -359
- package/.claude/settings.local.json +31 -1
- package/.claude/skills/algorithmic-art/LICENSE.txt +202 -0
- package/.claude/skills/algorithmic-art/SKILL.md +405 -0
- package/.claude/skills/algorithmic-art/templates/generator_template.js +223 -0
- package/.claude/skills/algorithmic-art/templates/viewer.html +599 -0
- package/.claude/skills/brand-guidelines/LICENSE.txt +202 -0
- package/.claude/skills/brand-guidelines/SKILL.md +73 -0
- package/.claude/skills/canvas-design/LICENSE.txt +202 -0
- package/.claude/skills/canvas-design/SKILL.md +130 -0
- package/.claude/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +94 -0
- package/.claude/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Jura-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- package/.claude/skills/doc-coauthoring/SKILL.md +375 -0
- package/.claude/skills/docx/LICENSE.txt +30 -0
- package/.claude/skills/docx/SKILL.md +197 -0
- package/.claude/skills/docx/docx-js.md +350 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/.claude/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/.claude/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/.claude/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/.claude/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/.claude/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/.claude/skills/docx/ooxml/schemas/mce/mc.xsd +75 -0
- package/.claude/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/.claude/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/.claude/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/.claude/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/.claude/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/.claude/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/.claude/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/.claude/skills/docx/ooxml/scripts/pack.py +159 -0
- package/.claude/skills/docx/ooxml/scripts/unpack.py +29 -0
- package/.claude/skills/docx/ooxml/scripts/validate.py +69 -0
- package/.claude/skills/docx/ooxml/scripts/validation/__init__.py +15 -0
- package/.claude/skills/docx/ooxml/scripts/validation/base.py +951 -0
- package/.claude/skills/docx/ooxml/scripts/validation/docx.py +274 -0
- package/.claude/skills/docx/ooxml/scripts/validation/pptx.py +315 -0
- package/.claude/skills/docx/ooxml/scripts/validation/redlining.py +279 -0
- package/.claude/skills/docx/ooxml.md +610 -0
- package/.claude/skills/docx/scripts/__init__.py +1 -0
- package/.claude/skills/docx/scripts/document.py +1276 -0
- package/.claude/skills/docx/scripts/templates/comments.xml +3 -0
- package/.claude/skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/.claude/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/.claude/skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/.claude/skills/docx/scripts/templates/people.xml +3 -0
- package/.claude/skills/docx/scripts/utilities.py +374 -0
- package/.claude/skills/frontend-design/LICENSE.txt +177 -0
- package/.claude/skills/frontend-design/SKILL.md +42 -0
- package/.claude/skills/internal-comms/LICENSE.txt +202 -0
- package/.claude/skills/internal-comms/SKILL.md +32 -0
- package/.claude/skills/internal-comms/examples/3p-updates.md +47 -0
- package/.claude/skills/internal-comms/examples/company-newsletter.md +65 -0
- package/.claude/skills/internal-comms/examples/faq-answers.md +30 -0
- package/.claude/skills/internal-comms/examples/general-comms.md +16 -0
- package/.claude/skills/mcp-builder/LICENSE.txt +202 -0
- package/.claude/skills/mcp-builder/SKILL.md +236 -0
- package/.claude/skills/mcp-builder/reference/evaluation.md +602 -0
- package/.claude/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
- package/.claude/skills/mcp-builder/reference/node_mcp_server.md +970 -0
- package/.claude/skills/mcp-builder/reference/python_mcp_server.md +719 -0
- package/.claude/skills/mcp-builder/scripts/connections.py +151 -0
- package/.claude/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/.claude/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/.claude/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/.claude/skills/pdf/LICENSE.txt +30 -0
- package/.claude/skills/pdf/SKILL.md +294 -0
- package/.claude/skills/pdf/forms.md +205 -0
- package/.claude/skills/pdf/reference.md +612 -0
- package/.claude/skills/pdf/scripts/check_bounding_boxes.py +70 -0
- package/.claude/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
- package/.claude/skills/pdf/scripts/check_fillable_fields.py +12 -0
- package/.claude/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/.claude/skills/pdf/scripts/create_validation_image.py +41 -0
- package/.claude/skills/pdf/scripts/extract_form_field_info.py +152 -0
- package/.claude/skills/pdf/scripts/fill_fillable_fields.py +114 -0
- package/.claude/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/.claude/skills/pptx/LICENSE.txt +30 -0
- package/.claude/skills/pptx/SKILL.md +484 -0
- package/.claude/skills/pptx/html2pptx.md +625 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/.claude/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/.claude/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/.claude/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/.claude/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/.claude/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/.claude/skills/pptx/ooxml/schemas/mce/mc.xsd +75 -0
- package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/.claude/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/.claude/skills/pptx/ooxml/scripts/pack.py +159 -0
- package/.claude/skills/pptx/ooxml/scripts/unpack.py +29 -0
- package/.claude/skills/pptx/ooxml/scripts/validate.py +69 -0
- package/.claude/skills/pptx/ooxml/scripts/validation/__init__.py +15 -0
- package/.claude/skills/pptx/ooxml/scripts/validation/base.py +951 -0
- package/.claude/skills/pptx/ooxml/scripts/validation/docx.py +274 -0
- package/.claude/skills/pptx/ooxml/scripts/validation/pptx.py +315 -0
- package/.claude/skills/pptx/ooxml/scripts/validation/redlining.py +279 -0
- package/.claude/skills/pptx/ooxml.md +427 -0
- package/.claude/skills/pptx/scripts/html2pptx.js +979 -0
- package/.claude/skills/pptx/scripts/inventory.py +1020 -0
- package/.claude/skills/pptx/scripts/rearrange.py +231 -0
- package/.claude/skills/pptx/scripts/replace.py +385 -0
- package/.claude/skills/pptx/scripts/thumbnail.py +450 -0
- package/.claude/skills/skill-creator/LICENSE.txt +202 -0
- package/.claude/skills/skill-creator/SKILL.md +356 -0
- package/.claude/skills/skill-creator/references/output-patterns.md +82 -0
- package/.claude/skills/skill-creator/references/workflows.md +28 -0
- package/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.claude/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/.claude/skills/slack-gif-creator/LICENSE.txt +202 -0
- package/.claude/skills/slack-gif-creator/SKILL.md +254 -0
- package/.claude/skills/slack-gif-creator/core/easing.py +234 -0
- package/.claude/skills/slack-gif-creator/core/frame_composer.py +176 -0
- package/.claude/skills/slack-gif-creator/core/gif_builder.py +269 -0
- package/.claude/skills/slack-gif-creator/core/validators.py +136 -0
- package/.claude/skills/slack-gif-creator/requirements.txt +4 -0
- package/.claude/skills/template/SKILL.md +6 -0
- package/.claude/skills/theme-factory/LICENSE.txt +202 -0
- package/.claude/skills/theme-factory/SKILL.md +59 -0
- package/.claude/skills/theme-factory/theme-showcase.pdf +0 -0
- package/.claude/skills/theme-factory/themes/arctic-frost.md +19 -0
- package/.claude/skills/theme-factory/themes/botanical-garden.md +19 -0
- package/.claude/skills/theme-factory/themes/desert-rose.md +19 -0
- package/.claude/skills/theme-factory/themes/forest-canopy.md +19 -0
- package/.claude/skills/theme-factory/themes/golden-hour.md +19 -0
- package/.claude/skills/theme-factory/themes/midnight-galaxy.md +19 -0
- package/.claude/skills/theme-factory/themes/modern-minimalist.md +19 -0
- package/.claude/skills/theme-factory/themes/ocean-depths.md +19 -0
- package/.claude/skills/theme-factory/themes/sunset-boulevard.md +19 -0
- package/.claude/skills/theme-factory/themes/tech-innovation.md +19 -0
- package/.claude/skills/web-artifacts-builder/LICENSE.txt +202 -0
- package/.claude/skills/web-artifacts-builder/SKILL.md +74 -0
- package/.claude/skills/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
- package/.claude/skills/web-artifacts-builder/scripts/init-artifact.sh +322 -0
- package/.claude/skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/.claude/skills/webapp-testing/LICENSE.txt +202 -0
- package/.claude/skills/webapp-testing/SKILL.md +96 -0
- package/.claude/skills/webapp-testing/examples/console_logging.py +35 -0
- package/.claude/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/.claude/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/.claude/skills/webapp-testing/scripts/with_server.py +106 -0
- package/.claude/skills/xlsx/LICENSE.txt +30 -0
- package/.claude/skills/xlsx/SKILL.md +289 -0
- package/.claude/skills/xlsx/recalc.py +178 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +165 -86
- package/README.md +22 -0
- package/cli.js +7 -2
- package/development/todos/INDEX.md +10 -1
- package/jest.config.js +61 -0
- package/lib/commands.js +67 -0
- package/lib/migrations.js +154 -0
- package/package.json +7 -2
- package/scripts/fix-hooks.mjs +97 -0
- package/template/.claude/commands/commit-push-pr.md +23 -3
- package/template/.claude/hooks/conversation-logger.cjs +222 -0
- package/template/.claude/settings.json +114 -45
- package/tests/README.md +263 -0
- package/tests/commands.test.js +163 -0
- package/tests/config.test.js +100 -0
- package/tests/marketplace.test.js +304 -0
- package/tests/migrations.test.js +187 -0
- package/tests/utils.test.js +167 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketplace 模块单元测试
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const mockFs = require('mock-fs');
|
|
8
|
+
const sinon = require('sinon');
|
|
9
|
+
|
|
10
|
+
// 在 mock 之前加载模块
|
|
11
|
+
const marketplace = require('../lib/marketplace');
|
|
12
|
+
|
|
13
|
+
// 手动实现 parseSimpleYaml 用于测试(与模块中相同的实现)
|
|
14
|
+
function parseSimpleYaml(content) {
|
|
15
|
+
const result = { skills: [] };
|
|
16
|
+
let currentSection = null;
|
|
17
|
+
let currentSkill = null;
|
|
18
|
+
let currentKey = null;
|
|
19
|
+
|
|
20
|
+
content.split('\n').forEach(line => {
|
|
21
|
+
const trimmed = line.trim();
|
|
22
|
+
const indent = line.search(/\S/);
|
|
23
|
+
|
|
24
|
+
// Skip empty lines and comments
|
|
25
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Version
|
|
30
|
+
if (trimmed.startsWith('version:')) {
|
|
31
|
+
result.version = parseInt(trimmed.split(':')[1].trim());
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Skills array starts
|
|
36
|
+
if (trimmed === 'skills:') {
|
|
37
|
+
currentSection = 'skills';
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// New skill entry (starts with -)
|
|
42
|
+
if (trimmed.startsWith('- name:')) {
|
|
43
|
+
if (currentSkill) {
|
|
44
|
+
result.skills.push(currentSkill);
|
|
45
|
+
}
|
|
46
|
+
currentSkill = { name: trimmed.split(':')[1].trim() };
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Skill properties
|
|
51
|
+
if (currentSection === 'skills' && currentSkill) {
|
|
52
|
+
// Determine nesting level by indent
|
|
53
|
+
const isTopLevel = indent === 2;
|
|
54
|
+
|
|
55
|
+
if (isTopLevel) {
|
|
56
|
+
const match = trimmed.match(/^([\w-]+):\s*(.*)$/);
|
|
57
|
+
if (match) {
|
|
58
|
+
currentKey = match[1];
|
|
59
|
+
let value = match[2].trim();
|
|
60
|
+
|
|
61
|
+
// Handle arrays
|
|
62
|
+
if (value === '[]') {
|
|
63
|
+
value = [];
|
|
64
|
+
} else if (value === 'true') {
|
|
65
|
+
value = true;
|
|
66
|
+
} else if (value === 'false') {
|
|
67
|
+
value = false;
|
|
68
|
+
} else if (value.startsWith('"') || value.startsWith("'")) {
|
|
69
|
+
value = value.slice(1, -1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Initialize nested objects for special keys
|
|
73
|
+
if (['source', 'target', 'author', 'sync'].includes(currentKey)) {
|
|
74
|
+
currentSkill[currentKey] = {};
|
|
75
|
+
// Store the value for potential later use
|
|
76
|
+
currentSkill[currentKey]._value = value;
|
|
77
|
+
} else {
|
|
78
|
+
currentSkill[currentKey] = value;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} else if (currentKey) {
|
|
82
|
+
// Nested property
|
|
83
|
+
const match = trimmed.match(/^([\w-]+):\s*(.*)$/);
|
|
84
|
+
if (match) {
|
|
85
|
+
let value = match[2].trim();
|
|
86
|
+
if (value === 'true') value = true;
|
|
87
|
+
if (value === 'false') value = false;
|
|
88
|
+
if (value === '[]') value = [];
|
|
89
|
+
currentSkill[currentKey][match[1]] = value;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Push last skill
|
|
96
|
+
if (currentSkill) {
|
|
97
|
+
result.skills.push(currentSkill);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
describe('Marketplace Module', () => {
|
|
104
|
+
let consoleLogStub;
|
|
105
|
+
let execSyncStub;
|
|
106
|
+
|
|
107
|
+
beforeEach(() => {
|
|
108
|
+
jest.resetModules();
|
|
109
|
+
// Mock 文件系统,保留项目目录
|
|
110
|
+
mockFs({
|
|
111
|
+
'/project': {
|
|
112
|
+
'sources.yaml': `version: 1
|
|
113
|
+
skills:
|
|
114
|
+
- name: test-skill
|
|
115
|
+
description: "A test skill"
|
|
116
|
+
native: true
|
|
117
|
+
- name: external-skill
|
|
118
|
+
description: "External skill"
|
|
119
|
+
source:
|
|
120
|
+
repo: owner/repo`,
|
|
121
|
+
'.claude-plugin': {
|
|
122
|
+
'marketplace.json': JSON.stringify({
|
|
123
|
+
plugins: [],
|
|
124
|
+
metadata: {
|
|
125
|
+
skill_count: 2,
|
|
126
|
+
categories: {
|
|
127
|
+
tools: { name: 'CLI 工具', icon: '🔧' }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
},
|
|
132
|
+
'config': {
|
|
133
|
+
'skill-categories.json': JSON.stringify({
|
|
134
|
+
tools: { name: 'CLI 工具', icon: '🔧' },
|
|
135
|
+
workflow: { name: '工作流编排', icon: '🎼' }
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}, {
|
|
140
|
+
// 不 mock 项目的 lib 目录
|
|
141
|
+
[path.join(__dirname, '../lib')]: true
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Stub console.log to avoid actual output
|
|
145
|
+
consoleLogStub = sinon.stub(console, 'log');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
afterEach(() => {
|
|
149
|
+
mockFs.restore();
|
|
150
|
+
if (consoleLogStub) consoleLogStub.restore();
|
|
151
|
+
if (execSyncStub) execSyncStub.restore();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('parseSimpleYaml', () => {
|
|
155
|
+
it('should parse version number', () => {
|
|
156
|
+
const yaml = `version: 1
|
|
157
|
+
skills:
|
|
158
|
+
- name: test-skill`;
|
|
159
|
+
|
|
160
|
+
const result = parseSimpleYaml(yaml);
|
|
161
|
+
|
|
162
|
+
expect(result.version).toBe(1);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should parse skill names', () => {
|
|
166
|
+
const yaml = `skills:
|
|
167
|
+
- name: skill-one
|
|
168
|
+
- name: skill-two
|
|
169
|
+
- name: skill-three`;
|
|
170
|
+
|
|
171
|
+
const result = parseSimpleYaml(yaml);
|
|
172
|
+
|
|
173
|
+
expect(result.skills).toHaveLength(3);
|
|
174
|
+
expect(result.skills[0].name).toBe('skill-one');
|
|
175
|
+
expect(result.skills[1].name).toBe('skill-two');
|
|
176
|
+
expect(result.skills[2].name).toBe('skill-three');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should handle empty skills array', () => {
|
|
180
|
+
const yaml = `version: 1`;
|
|
181
|
+
|
|
182
|
+
const result = parseSimpleYaml(yaml);
|
|
183
|
+
|
|
184
|
+
expect(result.skills).toEqual([]);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should skip comments', () => {
|
|
188
|
+
const yaml = `# Comment
|
|
189
|
+
skills:
|
|
190
|
+
# Another comment
|
|
191
|
+
- name: test`;
|
|
192
|
+
|
|
193
|
+
const result = parseSimpleYaml(yaml);
|
|
194
|
+
|
|
195
|
+
expect(result.skills).toHaveLength(1);
|
|
196
|
+
expect(result.skills[0].name).toBe('test');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should handle empty lines', () => {
|
|
200
|
+
const yaml = `skills:
|
|
201
|
+
|
|
202
|
+
- name: test
|
|
203
|
+
|
|
204
|
+
- name: test2`;
|
|
205
|
+
|
|
206
|
+
const result = parseSimpleYaml(yaml);
|
|
207
|
+
|
|
208
|
+
expect(result.skills).toHaveLength(2);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe('marketplaceCommands', () => {
|
|
213
|
+
describe('marketplace:list', () => {
|
|
214
|
+
it('should be a function', () => {
|
|
215
|
+
expect(typeof marketplace.marketplaceCommands['marketplace:list']).toBe('function');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should output to console', () => {
|
|
219
|
+
marketplace.marketplaceCommands['marketplace:list']();
|
|
220
|
+
|
|
221
|
+
expect(consoleLogStub.called).toBe(true);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
describe('marketplace:install', () => {
|
|
226
|
+
it('should show usage when no skill name provided', () => {
|
|
227
|
+
marketplace.marketplaceCommands['marketplace:install']();
|
|
228
|
+
|
|
229
|
+
expect(consoleLogStub.called).toBe(true);
|
|
230
|
+
const output = consoleLogStub.args.flat().join(' ');
|
|
231
|
+
expect(output).toContain('Usage');
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
describe('marketplace:sync', () => {
|
|
236
|
+
it('should be a function', () => {
|
|
237
|
+
expect(typeof marketplace.marketplaceCommands['marketplace:sync']).toBe('function');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should output sync message', () => {
|
|
241
|
+
marketplace.marketplaceCommands['marketplace:sync']();
|
|
242
|
+
|
|
243
|
+
expect(consoleLogStub.called).toBe(true);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe('marketplace:add', () => {
|
|
248
|
+
it('should show usage when no repo provided', () => {
|
|
249
|
+
marketplace.marketplaceCommands['marketplace:add']();
|
|
250
|
+
|
|
251
|
+
expect(consoleLogStub.called).toBe(true);
|
|
252
|
+
const output = consoleLogStub.args.flat().join(' ');
|
|
253
|
+
expect(output).toContain('Usage');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('should validate repo format', () => {
|
|
257
|
+
marketplace.marketplaceCommands['marketplace:add']('invalid-repo-format');
|
|
258
|
+
|
|
259
|
+
const output = consoleLogStub.args.flat().join(' ');
|
|
260
|
+
expect(output).toContain('Invalid');
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
describe('marketplace:remove', () => {
|
|
265
|
+
it('should show usage when no skill name provided', () => {
|
|
266
|
+
marketplace.marketplaceCommands['marketplace:remove']();
|
|
267
|
+
|
|
268
|
+
expect(consoleLogStub.called).toBe(true);
|
|
269
|
+
const output = consoleLogStub.args.flat().join(' ');
|
|
270
|
+
expect(output).toContain('Usage');
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
describe('marketplace:status', () => {
|
|
275
|
+
it('should be a function', () => {
|
|
276
|
+
expect(typeof marketplace.marketplaceCommands['marketplace:status']).toBe('function');
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should output status information', () => {
|
|
280
|
+
marketplace.marketplaceCommands['marketplace:status']();
|
|
281
|
+
|
|
282
|
+
expect(consoleLogStub.called).toBe(true);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe('exports', () => {
|
|
288
|
+
it('should export marketplaceCommands object', () => {
|
|
289
|
+
expect(marketplace.marketplaceCommands).toBeDefined();
|
|
290
|
+
expect(typeof marketplace.marketplaceCommands).toBe('object');
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should export all marketplace commands', () => {
|
|
294
|
+
const commands = marketplace.marketplaceCommands;
|
|
295
|
+
|
|
296
|
+
expect(commands['marketplace:list']).toBeDefined();
|
|
297
|
+
expect(commands['marketplace:install']).toBeDefined();
|
|
298
|
+
expect(commands['marketplace:sync']).toBeDefined();
|
|
299
|
+
expect(commands['marketplace:add']).toBeDefined();
|
|
300
|
+
expect(commands['marketplace:remove']).toBeDefined();
|
|
301
|
+
expect(commands['marketplace:status']).toBeDefined();
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migrations 模块单元测试
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const mockFs = require('mock-fs');
|
|
8
|
+
|
|
9
|
+
// 在 mock 之前加载模块
|
|
10
|
+
const migrations = require('../lib/migrations');
|
|
11
|
+
|
|
12
|
+
describe('Migrations Module', () => {
|
|
13
|
+
let originalFs = { ...fs };
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
// Mock 文件系统,保留一些必要的路径
|
|
17
|
+
mockFs({
|
|
18
|
+
'/test-project': {
|
|
19
|
+
'.claude': {}
|
|
20
|
+
}
|
|
21
|
+
}, {
|
|
22
|
+
// 不 mock 项目的 lib 目录
|
|
23
|
+
[path.join(__dirname, '../lib')]: true
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
mockFs.restore();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('getProjectVersion', () => {
|
|
32
|
+
it('should return 1.0.0 when version file does not exist', () => {
|
|
33
|
+
const version = migrations.getProjectVersion('/test-project');
|
|
34
|
+
expect(version).toBe('1.0.0');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should read version from file', () => {
|
|
38
|
+
const versionFile = path.join('/test-project', '.claude', '.version');
|
|
39
|
+
fs.writeFileSync(versionFile, '1.0.5\n');
|
|
40
|
+
|
|
41
|
+
const version = migrations.getProjectVersion('/test-project');
|
|
42
|
+
expect(version).toBe('1.0.5');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should trim whitespace from version', () => {
|
|
46
|
+
const versionFile = path.join('/test-project', '.claude', '.version');
|
|
47
|
+
fs.writeFileSync(versionFile, ' 1.0.8 \n');
|
|
48
|
+
|
|
49
|
+
const version = migrations.getProjectVersion('/test-project');
|
|
50
|
+
expect(version).toBe('1.0.8');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should return 1.0.0 on read error', () => {
|
|
54
|
+
const version = migrations.getProjectVersion('/nonexistent');
|
|
55
|
+
expect(version).toBe('1.0.0');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('setProjectVersion', () => {
|
|
60
|
+
it('should write version to file', () => {
|
|
61
|
+
migrations.setProjectVersion('/test-project', '1.0.7');
|
|
62
|
+
|
|
63
|
+
const versionFile = path.join('/test-project', '.claude', '.version');
|
|
64
|
+
expect(fs.existsSync(versionFile)).toBe(true);
|
|
65
|
+
|
|
66
|
+
const content = fs.readFileSync(versionFile, 'utf-8');
|
|
67
|
+
expect(content.trim()).toBe('1.0.7');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should add newline after version', () => {
|
|
71
|
+
migrations.setProjectVersion('/test-project', '1.0.9');
|
|
72
|
+
|
|
73
|
+
const versionFile = path.join('/test-project', '.claude', '.version');
|
|
74
|
+
const content = fs.readFileSync(versionFile, 'utf-8');
|
|
75
|
+
expect(content).toBe('1.0.9\n');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should create .claude directory if not exists', () => {
|
|
79
|
+
// 注意:setProjectVersion 不会自动创建目录
|
|
80
|
+
// 需要先创建目录
|
|
81
|
+
fs.mkdirSync(path.join('/new-project', '.claude'), { recursive: true });
|
|
82
|
+
migrations.setProjectVersion('/new-project', '1.0.1');
|
|
83
|
+
|
|
84
|
+
const versionFile = path.join('/new-project', '.claude', '.version');
|
|
85
|
+
expect(fs.existsSync(versionFile)).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('compareVersions', () => {
|
|
90
|
+
it('should return -1 when v1 < v2', () => {
|
|
91
|
+
expect(migrations.compareVersions('1.0.0', '1.0.1')).toBe(-1);
|
|
92
|
+
expect(migrations.compareVersions('1.0.0', '1.1.0')).toBe(-1);
|
|
93
|
+
expect(migrations.compareVersions('1.0.0', '2.0.0')).toBe(-1);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should return 1 when v1 > v2', () => {
|
|
97
|
+
expect(migrations.compareVersions('1.0.1', '1.0.0')).toBe(1);
|
|
98
|
+
expect(migrations.compareVersions('1.1.0', '1.0.0')).toBe(1);
|
|
99
|
+
expect(migrations.compareVersions('2.0.0', '1.0.0')).toBe(1);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should return 0 when v1 === v2', () => {
|
|
103
|
+
expect(migrations.compareVersions('1.0.0', '1.0.0')).toBe(0);
|
|
104
|
+
expect(migrations.compareVersions('1.5.10', '1.5.10')).toBe(0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should handle version comparison correctly', () => {
|
|
108
|
+
// Patch version comparison
|
|
109
|
+
expect(migrations.compareVersions('1.0.5', '1.0.10')).toBe(-1);
|
|
110
|
+
expect(migrations.compareVersions('1.0.10', '1.0.5')).toBe(1);
|
|
111
|
+
|
|
112
|
+
// Minor version comparison
|
|
113
|
+
expect(migrations.compareVersions('1.5.0', '1.10.0')).toBe(-1);
|
|
114
|
+
|
|
115
|
+
// Major version comparison
|
|
116
|
+
expect(migrations.compareVersions('2.0.0', '10.0.0')).toBe(-1);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should handle version strings with different formats', () => {
|
|
120
|
+
expect(migrations.compareVersions('1', '1.0.0')).toBe(0);
|
|
121
|
+
expect(migrations.compareVersions('1.0', '1.0.0')).toBe(0);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('runMigrations', () => {
|
|
126
|
+
it('should return success when already at latest version', () => {
|
|
127
|
+
// 设置当前版本为最新
|
|
128
|
+
migrations.setProjectVersion('/test-project', migrations.TEMPLATE_VERSION);
|
|
129
|
+
|
|
130
|
+
const result = migrations.runMigrations('/test-project', true);
|
|
131
|
+
|
|
132
|
+
expect(result.success).toBe(true);
|
|
133
|
+
expect(result.migrations).toEqual([]);
|
|
134
|
+
expect(result.currentVersion).toBe(migrations.TEMPLATE_VERSION);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should return success when version is newer than template', () => {
|
|
138
|
+
// 设置一个未来版本
|
|
139
|
+
migrations.setProjectVersion('/test-project', '2.0.0');
|
|
140
|
+
|
|
141
|
+
const result = migrations.runMigrations('/test-project', true);
|
|
142
|
+
|
|
143
|
+
expect(result.success).toBe(true);
|
|
144
|
+
expect(result.migrations).toEqual([]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should return correct structure when pending migrations exist', () => {
|
|
148
|
+
// 设置旧版本
|
|
149
|
+
migrations.setProjectVersion('/test-project', '1.0.0');
|
|
150
|
+
|
|
151
|
+
// 创建需要迁移的 settings.json
|
|
152
|
+
const settingsFile = path.join('/test-project', '.claude', 'settings.json');
|
|
153
|
+
fs.writeFileSync(
|
|
154
|
+
settingsFile,
|
|
155
|
+
JSON.stringify({ matcher: {}, hooks: [] }, null, 2)
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const result = migrations.runMigrations('/test-project', true);
|
|
159
|
+
|
|
160
|
+
expect(result).toBeDefined();
|
|
161
|
+
expect(typeof result.success).toBe('boolean');
|
|
162
|
+
expect(result.currentVersion).toBeDefined();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should handle missing settings.json gracefully', () => {
|
|
166
|
+
migrations.setProjectVersion('/test-project', '1.0.0');
|
|
167
|
+
|
|
168
|
+
const result = migrations.runMigrations('/test-project', true);
|
|
169
|
+
|
|
170
|
+
expect(result.success).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('exports', () => {
|
|
175
|
+
it('should export TEMPLATE_VERSION constant', () => {
|
|
176
|
+
expect(migrations.TEMPLATE_VERSION).toBeDefined();
|
|
177
|
+
expect(typeof migrations.TEMPLATE_VERSION).toBe('string');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should export all functions', () => {
|
|
181
|
+
expect(typeof migrations.getProjectVersion).toBe('function');
|
|
182
|
+
expect(typeof migrations.setProjectVersion).toBe('function');
|
|
183
|
+
expect(typeof migrations.compareVersions).toBe('function');
|
|
184
|
+
expect(typeof migrations.runMigrations).toBe('function');
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
});
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utils 模块单元测试
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
|
|
9
|
+
const utils = require('../lib/utils');
|
|
10
|
+
|
|
11
|
+
describe('Utils Module', () => {
|
|
12
|
+
describe('copyRecursive', () => {
|
|
13
|
+
let tempSrc, tempDest;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
// 创建临时测试目录
|
|
17
|
+
tempSrc = path.join(os.tmpdir(), 'test-src-' + Date.now());
|
|
18
|
+
tempDest = path.join(os.tmpdir(), 'test-dest-' + Date.now());
|
|
19
|
+
fs.mkdirSync(tempSrc, { recursive: true });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
// 清理临时目录
|
|
24
|
+
if (fs.existsSync(tempSrc)) {
|
|
25
|
+
fs.rmSync(tempSrc, { recursive: true, force: true });
|
|
26
|
+
}
|
|
27
|
+
if (fs.existsSync(tempDest)) {
|
|
28
|
+
fs.rmSync(tempDest, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should return 0 when source does not exist', () => {
|
|
33
|
+
const nonExistent = path.join(os.tmpdir(), 'non-existent-' + Date.now());
|
|
34
|
+
const count = utils.copyRecursive(nonExistent, tempDest);
|
|
35
|
+
|
|
36
|
+
expect(count).toBe(0);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should copy files recursively', () => {
|
|
40
|
+
// 创建测试文件结构
|
|
41
|
+
fs.mkdirSync(path.join(tempSrc, 'subdir'));
|
|
42
|
+
fs.writeFileSync(path.join(tempSrc, 'file1.txt'), 'content1');
|
|
43
|
+
fs.writeFileSync(path.join(tempSrc, 'subdir', 'file2.txt'), 'content2');
|
|
44
|
+
|
|
45
|
+
const count = utils.copyRecursive(tempSrc, tempDest);
|
|
46
|
+
|
|
47
|
+
expect(count).toBe(2);
|
|
48
|
+
expect(fs.existsSync(path.join(tempDest, 'file1.txt'))).toBe(true);
|
|
49
|
+
expect(fs.existsSync(path.join(tempDest, 'subdir', 'file2.txt'))).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should set execute permission for script files', () => {
|
|
53
|
+
fs.writeFileSync(path.join(tempSrc, 'test.sh'), '#!/bin/bash\necho test');
|
|
54
|
+
fs.writeFileSync(path.join(tempSrc, 'test.cjs'), 'console.log("test");');
|
|
55
|
+
fs.writeFileSync(path.join(tempSrc, 'test.txt'), 'plain text');
|
|
56
|
+
|
|
57
|
+
utils.copyRecursive(tempSrc, tempDest);
|
|
58
|
+
|
|
59
|
+
// 检查 .sh 文件权限
|
|
60
|
+
const shStats = fs.statSync(path.join(tempDest, 'test.sh'));
|
|
61
|
+
const cjsStats = fs.statSync(path.join(tempDest, 'test.cjs'));
|
|
62
|
+
const txtStats = fs.statSync(path.join(tempDest, 'test.txt'));
|
|
63
|
+
|
|
64
|
+
// 检查执行权限 (0o755 的执行位)
|
|
65
|
+
expect(shStats.mode & 0o111).toBeTruthy();
|
|
66
|
+
expect(cjsStats.mode & 0o111).toBeTruthy();
|
|
67
|
+
// txt 文件不应该有执行权限
|
|
68
|
+
expect(txtStats.mode & 0o111).toBeFalsy();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should not overwrite when overwrite=false', () => {
|
|
72
|
+
fs.writeFileSync(path.join(tempSrc, 'file.txt'), 'new content');
|
|
73
|
+
fs.mkdirSync(tempDest, { recursive: true });
|
|
74
|
+
fs.writeFileSync(path.join(tempDest, 'file.txt'), 'old content');
|
|
75
|
+
|
|
76
|
+
const count = utils.copyRecursive(tempSrc, tempDest, false);
|
|
77
|
+
|
|
78
|
+
expect(count).toBe(0);
|
|
79
|
+
const content = fs.readFileSync(path.join(tempDest, 'file.txt'), 'utf-8');
|
|
80
|
+
expect(content).toBe('old content');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should overwrite when overwrite=true', () => {
|
|
84
|
+
fs.writeFileSync(path.join(tempSrc, 'file.txt'), 'new content');
|
|
85
|
+
fs.mkdirSync(tempDest, { recursive: true });
|
|
86
|
+
fs.writeFileSync(path.join(tempDest, 'file.txt'), 'old content');
|
|
87
|
+
|
|
88
|
+
const count = utils.copyRecursive(tempSrc, tempDest, true);
|
|
89
|
+
|
|
90
|
+
expect(count).toBe(1);
|
|
91
|
+
const content = fs.readFileSync(path.join(tempDest, 'file.txt'), 'utf-8');
|
|
92
|
+
expect(content).toBe('new content');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('ensureDir', () => {
|
|
97
|
+
it('should create directory if not exists', () => {
|
|
98
|
+
const tempDir = path.join(os.tmpdir(), 'test-ensure-' + Date.now());
|
|
99
|
+
|
|
100
|
+
utils.ensureDir(tempDir);
|
|
101
|
+
|
|
102
|
+
expect(fs.existsSync(tempDir)).toBe(true);
|
|
103
|
+
|
|
104
|
+
// 清理
|
|
105
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should not error if directory already exists', () => {
|
|
109
|
+
const tempDir = path.join(os.tmpdir(), 'test-ensure-' + Date.now());
|
|
110
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
111
|
+
|
|
112
|
+
expect(() => utils.ensureDir(tempDir)).not.toThrow();
|
|
113
|
+
|
|
114
|
+
// 清理
|
|
115
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should create nested directories', () => {
|
|
119
|
+
const tempDir = path.join(os.tmpdir(), 'test-nested-' + Date.now(), 'level1', 'level2', 'level3');
|
|
120
|
+
|
|
121
|
+
utils.ensureDir(tempDir);
|
|
122
|
+
|
|
123
|
+
expect(fs.existsSync(tempDir)).toBe(true);
|
|
124
|
+
|
|
125
|
+
// 清理
|
|
126
|
+
fs.rmSync(path.join(os.tmpdir(), 'test-nested-' + Date.now()), { recursive: true, force: true });
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('toTitleCase', () => {
|
|
131
|
+
it('should convert string to title case', () => {
|
|
132
|
+
expect(utils.toTitleCase('hello world')).toBe('Hello World');
|
|
133
|
+
expect(utils.toTitleCase('foo-bar')).toBe('Foo-Bar');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should handle single word', () => {
|
|
137
|
+
expect(utils.toTitleCase('hello')).toBe('Hello');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should handle empty string', () => {
|
|
141
|
+
expect(utils.toTitleCase('')).toBe('');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should handle already capitalized string', () => {
|
|
145
|
+
expect(utils.toTitleCase('Hello World')).toBe('Hello World');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should handle strings with multiple spaces', () => {
|
|
149
|
+
expect(utils.toTitleCase('hello world')).toBe('Hello World');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should preserve special characters', () => {
|
|
153
|
+
// 下划线 _ 是单词字符(\w),所以 _t 中的 t 前面没有单词边界
|
|
154
|
+
expect(utils.toTitleCase('hello-world_test')).toBe('Hello-World_test');
|
|
155
|
+
// 连字符 - 不是单词字符,所以后面的字母会被大写
|
|
156
|
+
expect(utils.toTitleCase('hello-world test')).toBe('Hello-World Test');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('exports', () => {
|
|
161
|
+
it('should export all functions', () => {
|
|
162
|
+
expect(typeof utils.copyRecursive).toBe('function');
|
|
163
|
+
expect(typeof utils.ensureDir).toBe('function');
|
|
164
|
+
expect(typeof utils.toTitleCase).toBe('function');
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
});
|