@thanhvn14/csvibe 0.1.4 → 0.1.5
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/.github/agents/schemas/base-output.schema.json +88 -0
- package/.github/agents/schemas/brainstorm-output.schema.json +88 -0
- package/.github/agents/schemas/scout-output.schema.json +60 -0
- package/.github/agents/scripts/fetch-copilot-tools.js +245 -0
- package/.github/agents/scripts/lib/parse-agent-file.js +275 -0
- package/.github/agents/scripts/package-lock.json +78 -0
- package/.github/agents/scripts/package.json +22 -0
- package/.github/agents/scripts/schemas/agent-frontmatter.schema.json +83 -0
- package/.github/agents/scripts/validate-agent-all.js +157 -0
- package/.github/agents/scripts/validate-agent-frontmatter.js +96 -0
- package/.github/agents/scripts/validate-agent-handoffs.js +169 -0
- package/.github/agents/scripts/validate-agent-output.js +157 -0
- package/.github/agents/scripts/validate-agent-tools.js +278 -0
- package/.github/skills/.env.example +100 -0
- package/.github/skills/.install-state.json +23 -0
- package/.github/skills/README.md +149 -0
- package/.github/skills/ai-multimodal/.env.example +204 -0
- package/.github/skills/ai-multimodal/scripts/.coverage +0 -0
- package/.github/skills/ai-multimodal/scripts/check_setup.py +305 -0
- package/.github/skills/ai-multimodal/scripts/document_converter.py +395 -0
- package/.github/skills/ai-multimodal/scripts/gemini_batch_process.py +1184 -0
- package/.github/skills/ai-multimodal/scripts/media_optimizer.py +506 -0
- package/.github/skills/ai-multimodal/scripts/requirements.txt +26 -0
- package/.github/skills/better-auth/scripts/.coverage +0 -0
- package/.github/skills/better-auth/scripts/better_auth_init.py +521 -0
- package/.github/skills/better-auth/scripts/requirements.txt +15 -0
- package/.github/skills/chrome-devtools/scripts/README.md +272 -0
- package/.github/skills/chrome-devtools/scripts/__tests__/selector.test.js +210 -0
- package/.github/skills/chrome-devtools/scripts/aria-snapshot.js +362 -0
- package/.github/skills/chrome-devtools/scripts/click.js +83 -0
- package/.github/skills/chrome-devtools/scripts/console.js +79 -0
- package/.github/skills/chrome-devtools/scripts/evaluate.js +53 -0
- package/.github/skills/chrome-devtools/scripts/fill.js +76 -0
- package/.github/skills/chrome-devtools/scripts/inject-auth.js +229 -0
- package/.github/skills/chrome-devtools/scripts/install-deps.sh +181 -0
- package/.github/skills/chrome-devtools/scripts/install.sh +83 -0
- package/.github/skills/chrome-devtools/scripts/lib/browser.js +318 -0
- package/.github/skills/chrome-devtools/scripts/lib/selector.js +178 -0
- package/.github/skills/chrome-devtools/scripts/navigate.js +54 -0
- package/.github/skills/chrome-devtools/scripts/network.js +106 -0
- package/.github/skills/chrome-devtools/scripts/package-lock.json +1589 -0
- package/.github/skills/chrome-devtools/scripts/package.json +16 -0
- package/.github/skills/chrome-devtools/scripts/performance.js +149 -0
- package/.github/skills/chrome-devtools/scripts/screenshot.js +198 -0
- package/.github/skills/chrome-devtools/scripts/select-ref.js +131 -0
- package/.github/skills/chrome-devtools/scripts/snapshot.js +135 -0
- package/.github/skills/common/README.md +120 -0
- package/.github/skills/common/api_key_helper.py +411 -0
- package/.github/skills/common/api_key_rotator.py +248 -0
- package/.github/skills/databases/scripts/.coverage +0 -0
- package/.github/skills/databases/scripts/db_backup.py +502 -0
- package/.github/skills/databases/scripts/db_migrate.py +425 -0
- package/.github/skills/databases/scripts/db_performance_check.py +456 -0
- package/.github/skills/databases/scripts/requirements.txt +20 -0
- package/.github/skills/debugging/scripts/find-polluter.sh +63 -0
- package/.github/skills/devops/.env.example +76 -0
- package/.github/skills/devops/scripts/cloudflare_deploy.py +269 -0
- package/.github/skills/devops/scripts/docker_optimize.py +331 -0
- package/.github/skills/devops/scripts/requirements.txt +20 -0
- package/.github/skills/docs-seeker/.env.example +15 -0
- package/.github/skills/docs-seeker/package.json +25 -0
- package/.github/skills/docs-seeker/scripts/analyze-llms-txt.js +211 -0
- package/.github/skills/docs-seeker/scripts/detect-topic.js +172 -0
- package/.github/skills/docs-seeker/scripts/fetch-docs.js +213 -0
- package/.github/skills/docs-seeker/scripts/utils/env-loader.js +94 -0
- package/.github/skills/document-skills/docx/LICENSE.txt +30 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/mce/mc.xsd +75 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/.github/skills/document-skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/.github/skills/document-skills/docx/ooxml/scripts/pack.py +159 -0
- package/.github/skills/document-skills/docx/ooxml/scripts/unpack.py +29 -0
- package/.github/skills/document-skills/docx/ooxml/scripts/validate.py +69 -0
- package/.github/skills/document-skills/docx/ooxml/scripts/validation/__init__.py +15 -0
- package/.github/skills/document-skills/docx/ooxml/scripts/validation/base.py +951 -0
- package/.github/skills/document-skills/docx/ooxml/scripts/validation/docx.py +274 -0
- package/.github/skills/document-skills/docx/ooxml/scripts/validation/pptx.py +315 -0
- package/.github/skills/document-skills/docx/ooxml/scripts/validation/redlining.py +279 -0
- package/.github/skills/document-skills/docx/scripts/__init__.py +1 -0
- package/.github/skills/document-skills/docx/scripts/document.py +1276 -0
- package/.github/skills/document-skills/docx/scripts/templates/comments.xml +3 -0
- package/.github/skills/document-skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/.github/skills/document-skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/.github/skills/document-skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/.github/skills/document-skills/docx/scripts/templates/people.xml +3 -0
- package/.github/skills/document-skills/docx/scripts/utilities.py +374 -0
- package/.github/skills/document-skills/pdf/LICENSE.txt +30 -0
- package/.github/skills/document-skills/pdf/scripts/check_bounding_boxes.py +70 -0
- package/.github/skills/document-skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
- package/.github/skills/document-skills/pdf/scripts/check_fillable_fields.py +12 -0
- package/.github/skills/document-skills/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/.github/skills/document-skills/pdf/scripts/create_validation_image.py +41 -0
- package/.github/skills/document-skills/pdf/scripts/extract_form_field_info.py +152 -0
- package/.github/skills/document-skills/pdf/scripts/fill_fillable_fields.py +114 -0
- package/.github/skills/document-skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/.github/skills/document-skills/pptx/LICENSE.txt +30 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/mce/mc.xsd +75 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/.github/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/.github/skills/document-skills/pptx/ooxml/scripts/pack.py +159 -0
- package/.github/skills/document-skills/pptx/ooxml/scripts/unpack.py +29 -0
- package/.github/skills/document-skills/pptx/ooxml/scripts/validate.py +69 -0
- package/.github/skills/document-skills/pptx/ooxml/scripts/validation/__init__.py +15 -0
- package/.github/skills/document-skills/pptx/ooxml/scripts/validation/base.py +951 -0
- package/.github/skills/document-skills/pptx/ooxml/scripts/validation/docx.py +274 -0
- package/.github/skills/document-skills/pptx/ooxml/scripts/validation/pptx.py +315 -0
- package/.github/skills/document-skills/pptx/ooxml/scripts/validation/redlining.py +279 -0
- package/.github/skills/document-skills/pptx/scripts/html2pptx.js +979 -0
- package/.github/skills/document-skills/pptx/scripts/inventory.py +1020 -0
- package/.github/skills/document-skills/pptx/scripts/rearrange.py +231 -0
- package/.github/skills/document-skills/pptx/scripts/replace.py +385 -0
- package/.github/skills/document-skills/pptx/scripts/thumbnail.py +450 -0
- package/.github/skills/document-skills/xlsx/LICENSE.txt +30 -0
- package/.github/skills/document-skills/xlsx/recalc.py +190 -0
- package/.github/skills/install.ps1 +1220 -0
- package/.github/skills/install.sh +1032 -0
- package/.github/skills/markdown-novel-viewer/assets/directory-browser.css +215 -0
- package/.github/skills/markdown-novel-viewer/assets/favicon.png +0 -0
- package/.github/skills/markdown-novel-viewer/assets/novel-theme.css +818 -0
- package/.github/skills/markdown-novel-viewer/assets/reader.js +262 -0
- package/.github/skills/markdown-novel-viewer/assets/template.html +80 -0
- package/.github/skills/markdown-novel-viewer/package-lock.json +146 -0
- package/.github/skills/markdown-novel-viewer/package.json +15 -0
- package/.github/skills/markdown-novel-viewer/scripts/lib/http-server.cjs +434 -0
- package/.github/skills/markdown-novel-viewer/scripts/lib/markdown-renderer.cjs +272 -0
- package/.github/skills/markdown-novel-viewer/scripts/lib/plan-navigator.cjs +509 -0
- package/.github/skills/markdown-novel-viewer/scripts/lib/port-finder.cjs +48 -0
- package/.github/skills/markdown-novel-viewer/scripts/lib/process-mgr.cjs +150 -0
- package/.github/skills/markdown-novel-viewer/scripts/server.cjs +411 -0
- package/.github/skills/mcp-builder/LICENSE.txt +202 -0
- package/.github/skills/mcp-builder/scripts/connections.py +151 -0
- package/.github/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/.github/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/.github/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/.github/skills/mcp-management/README.md +219 -0
- package/.github/skills/mcp-management/assets/tools.json +3146 -0
- package/.github/skills/mcp-management/package-lock.json +6 -0
- package/.github/skills/mcp-management/scripts/.env.example +10 -0
- package/.github/skills/mcp-management/scripts/cli.ts +195 -0
- package/.github/skills/mcp-management/scripts/dist/analyze-tools.js +70 -0
- package/.github/skills/mcp-management/scripts/dist/cli.js +160 -0
- package/.github/skills/mcp-management/scripts/dist/mcp-client.js +183 -0
- package/.github/skills/mcp-management/scripts/mcp-client.ts +230 -0
- package/.github/skills/mcp-management/scripts/package.json +20 -0
- package/.github/skills/media-processing/scripts/README.md +111 -0
- package/.github/skills/media-processing/scripts/batch-remove-background.sh +124 -0
- package/.github/skills/media-processing/scripts/batch_resize.py +342 -0
- package/.github/skills/media-processing/scripts/media_convert.py +311 -0
- package/.github/skills/media-processing/scripts/remove-background.sh +96 -0
- package/.github/skills/media-processing/scripts/remove-bg-node.js +158 -0
- package/.github/skills/media-processing/scripts/requirements.txt +24 -0
- package/.github/skills/media-processing/scripts/video_optimize.py +414 -0
- package/.github/skills/payment-integration/README.md +185 -0
- package/.github/skills/payment-integration/scripts/.env.example +20 -0
- package/.github/skills/payment-integration/scripts/checkout-helper.js +244 -0
- package/.github/skills/payment-integration/scripts/package.json +17 -0
- package/.github/skills/payment-integration/scripts/polar-webhook-verify.js +202 -0
- package/.github/skills/payment-integration/scripts/sepay-webhook-verify.js +193 -0
- package/.github/skills/payment-integration/scripts/test-scripts.js +237 -0
- package/.github/skills/plans-kanban/assets/dashboard-template.html +119 -0
- package/.github/skills/plans-kanban/assets/dashboard.css +1594 -0
- package/.github/skills/plans-kanban/assets/dashboard.js +596 -0
- package/.github/skills/plans-kanban/assets/favicon.png +0 -0
- package/.github/skills/plans-kanban/package-lock.json +123 -0
- package/.github/skills/plans-kanban/package.json +13 -0
- package/.github/skills/plans-kanban/scripts/lib/dashboard-renderer.cjs +884 -0
- package/.github/skills/plans-kanban/scripts/lib/http-server.cjs +310 -0
- package/.github/skills/plans-kanban/scripts/lib/plan-metadata-extractor.cjs +489 -0
- package/.github/skills/plans-kanban/scripts/lib/plan-parser.cjs +175 -0
- package/.github/skills/plans-kanban/scripts/lib/plan-scanner.cjs +272 -0
- package/.github/skills/plans-kanban/scripts/lib/port-finder.cjs +48 -0
- package/.github/skills/plans-kanban/scripts/lib/process-mgr.cjs +128 -0
- package/.github/skills/plans-kanban/scripts/server.cjs +260 -0
- package/.github/skills/repomix/scripts/.coverage +0 -0
- package/.github/skills/repomix/scripts/README.md +179 -0
- package/.github/skills/repomix/scripts/repomix_batch.py +455 -0
- package/.github/skills/repomix/scripts/repos.example.json +15 -0
- package/.github/skills/repomix/scripts/requirements.txt +15 -0
- package/.github/skills/scout-validation/scripts/lib/broad-pattern-detector.cjs +124 -0
- package/.github/skills/scout-validation/scripts/lib/path-checker.cjs +66 -0
- package/.github/skills/scout-validation/scripts/lib/schema-validator.cjs +45 -0
- package/.github/skills/scout-validation/scripts/package.json +11 -0
- package/.github/skills/scout-validation/scripts/validate-scout-output.cjs +219 -0
- package/.github/skills/scout-validation/test/broad-pattern-output.json +18 -0
- package/.github/skills/scout-validation/test/invalid-path-output.json +18 -0
- package/.github/skills/scout-validation/test/valid-scout-output.json +26 -0
- package/.github/skills/sequential-thinking/.env.example +8 -0
- package/.github/skills/sequential-thinking/README.md +183 -0
- package/.github/skills/sequential-thinking/package.json +31 -0
- package/.github/skills/sequential-thinking/scripts/format-thought.js +159 -0
- package/.github/skills/sequential-thinking/scripts/process-thought.js +236 -0
- package/.github/skills/shopify/README.md +66 -0
- package/.github/skills/shopify/scripts/.coverage +0 -0
- package/.github/skills/shopify/scripts/requirements.txt +19 -0
- package/.github/skills/shopify/scripts/shopify_init.py +423 -0
- package/.github/skills/skill-creator/LICENSE.txt +202 -0
- package/.github/skills/skill-creator/scripts/init_skill.py +303 -0
- package/.github/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.github/skills/skill-creator/scripts/quick_validate.py +65 -0
- package/.github/skills/ui-styling/LICENSE.txt +202 -0
- package/.github/skills/ui-styling/canvas-fonts/ArsenalSC-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/BigShoulders-Bold.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/BigShoulders-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/BigShoulders-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Boldonse-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/Boldonse-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/CrimsonPro-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/DMMono-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/DMMono-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/EricaOne-OFL.txt +94 -0
- package/.github/skills/ui-styling/canvas-fonts/EricaOne-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/GeistMono-Bold.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/GeistMono-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/GeistMono-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Gloock-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/Gloock-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/InstrumentSans-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Italiana-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/Italiana-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Jura-Light.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Jura-Medium.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Jura-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Lora-Bold.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Lora-Italic.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Lora-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/Lora-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/NationalPark-Bold.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/NationalPark-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/NationalPark-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Outfit-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/PixelifySans-Medium.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/PixelifySans-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/PoiretOne-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/PoiretOne-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/RedHatMono-Bold.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/RedHatMono-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/RedHatMono-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Silkscreen-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/Silkscreen-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/SmoochSans-Medium.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/SmoochSans-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/Tektur-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/WorkSans-Bold.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/WorkSans-Italic.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/WorkSans-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/WorkSans-Regular.ttf +0 -0
- package/.github/skills/ui-styling/canvas-fonts/YoungSerif-OFL.txt +93 -0
- package/.github/skills/ui-styling/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- package/.github/skills/ui-styling/scripts/.coverage +0 -0
- package/.github/skills/ui-styling/scripts/requirements.txt +17 -0
- package/.github/skills/ui-styling/scripts/shadcn_add.py +292 -0
- package/.github/skills/ui-styling/scripts/tailwind_config_gen.py +456 -0
- package/.github/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.github/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.github/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.github/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.github/skills/ui-ux-pro-max/data/prompts.csv +24 -0
- package/.github/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.github/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +51 -0
- package/.github/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.github/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.github/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.github/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.github/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.github/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.github/skills/ui-ux-pro-max/data/styles.csv +59 -0
- package/.github/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.github/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.github/skills/ui-ux-pro-max/scripts/core.py +236 -0
- package/.github/skills/ui-ux-pro-max/scripts/search.py +76 -0
- package/.github/skills/web-frameworks/scripts/.coverage +0 -0
- package/.github/skills/web-frameworks/scripts/__init__.py +0 -0
- package/.github/skills/web-frameworks/scripts/nextjs_init.py +547 -0
- package/.github/skills/web-frameworks/scripts/requirements.txt +16 -0
- package/.github/skills/web-frameworks/scripts/turborepo_migrate.py +394 -0
- package/dist/config/constants.d.ts +2 -0
- package/dist/config/constants.d.ts.map +1 -1
- package/dist/config/constants.js +4 -1
- package/dist/config/constants.js.map +1 -1
- package/dist/domains/github/github-client.d.ts +5 -0
- package/dist/domains/github/github-client.d.ts.map +1 -1
- package/dist/domains/github/github-client.js +44 -0
- package/dist/domains/github/github-client.js.map +1 -1
- package/dist/utils/downloader.d.ts +3 -1
- package/dist/utils/downloader.d.ts.map +1 -1
- package/dist/utils/downloader.js +48 -11
- package/dist/utils/downloader.js.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core HTTP server for markdown-novel-viewer
|
|
3
|
+
* Handles routing for markdown viewer and directory browser
|
|
4
|
+
*
|
|
5
|
+
* Routes:
|
|
6
|
+
* - /view?file=<path> - Markdown file viewer
|
|
7
|
+
* - /browse?dir=<path> - Directory browser
|
|
8
|
+
* - /assets/* - Static assets
|
|
9
|
+
* - /file/* - Local files (images, etc.)
|
|
10
|
+
*
|
|
11
|
+
* Security: Paths are validated to prevent directory traversal attacks
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const http = require('http');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const url = require('url');
|
|
18
|
+
|
|
19
|
+
// Allowed base directories for file access (set at runtime)
|
|
20
|
+
let allowedBaseDirs = [];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Set allowed directories for file serving
|
|
24
|
+
* @param {string[]} dirs - Array of allowed directory paths
|
|
25
|
+
*/
|
|
26
|
+
function setAllowedDirs(dirs) {
|
|
27
|
+
allowedBaseDirs = dirs.map(d => path.resolve(d));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Validate path is within allowed directories (prevents path traversal)
|
|
32
|
+
* @param {string} filePath - Path to validate
|
|
33
|
+
* @param {string[]} allowedDirs - Allowed base directories
|
|
34
|
+
* @returns {boolean} - True if path is safe
|
|
35
|
+
*/
|
|
36
|
+
function isPathSafe(filePath, allowedDirs = allowedBaseDirs) {
|
|
37
|
+
const resolved = path.resolve(filePath);
|
|
38
|
+
|
|
39
|
+
// Check for path traversal attempts
|
|
40
|
+
if (resolved.includes('..') || filePath.includes('\0')) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// If no allowed dirs set, allow only project paths
|
|
45
|
+
if (allowedDirs.length === 0) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Must be within one of the allowed directories
|
|
50
|
+
return allowedDirs.some(dir => resolved.startsWith(dir));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Sanitize error message to prevent path disclosure
|
|
55
|
+
*/
|
|
56
|
+
function sanitizeErrorMessage(message) {
|
|
57
|
+
return message.replace(/\/[^\s'"<>]+/g, '[path]');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// MIME type mapping
|
|
61
|
+
const MIME_TYPES = {
|
|
62
|
+
'.html': 'text/html',
|
|
63
|
+
'.css': 'text/css',
|
|
64
|
+
'.js': 'application/javascript',
|
|
65
|
+
'.json': 'application/json',
|
|
66
|
+
'.png': 'image/png',
|
|
67
|
+
'.jpg': 'image/jpeg',
|
|
68
|
+
'.jpeg': 'image/jpeg',
|
|
69
|
+
'.gif': 'image/gif',
|
|
70
|
+
'.svg': 'image/svg+xml',
|
|
71
|
+
'.webp': 'image/webp',
|
|
72
|
+
'.ico': 'image/x-icon',
|
|
73
|
+
'.md': 'text/markdown',
|
|
74
|
+
'.txt': 'text/plain',
|
|
75
|
+
'.pdf': 'application/pdf'
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get MIME type for file extension
|
|
80
|
+
*/
|
|
81
|
+
function getMimeType(filePath) {
|
|
82
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
83
|
+
return MIME_TYPES[ext] || 'application/octet-stream';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Send response with content
|
|
88
|
+
*/
|
|
89
|
+
function sendResponse(res, statusCode, contentType, content) {
|
|
90
|
+
res.writeHead(statusCode, { 'Content-Type': contentType });
|
|
91
|
+
res.end(content);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Send error response (sanitized)
|
|
96
|
+
*/
|
|
97
|
+
function sendError(res, statusCode, message) {
|
|
98
|
+
const safeMessage = sanitizeErrorMessage(message);
|
|
99
|
+
sendResponse(res, statusCode, 'text/html', `
|
|
100
|
+
<!DOCTYPE html>
|
|
101
|
+
<html>
|
|
102
|
+
<head><title>Error ${statusCode}</title></head>
|
|
103
|
+
<body style="font-family: system-ui; padding: 2rem;">
|
|
104
|
+
<h1>Error ${statusCode}</h1>
|
|
105
|
+
<p>${safeMessage}</p>
|
|
106
|
+
</body>
|
|
107
|
+
</html>
|
|
108
|
+
`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Serve static file with path validation
|
|
113
|
+
*/
|
|
114
|
+
function serveFile(res, filePath, skipValidation = false) {
|
|
115
|
+
if (!skipValidation && !isPathSafe(filePath)) {
|
|
116
|
+
sendError(res, 403, 'Access denied');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!fs.existsSync(filePath)) {
|
|
121
|
+
sendError(res, 404, 'File not found');
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const content = fs.readFileSync(filePath);
|
|
126
|
+
const mimeType = getMimeType(filePath);
|
|
127
|
+
sendResponse(res, 200, mimeType, content);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get file icon based on extension
|
|
132
|
+
*/
|
|
133
|
+
function getFileIcon(filename) {
|
|
134
|
+
const ext = path.extname(filename).toLowerCase();
|
|
135
|
+
const iconMap = {
|
|
136
|
+
'.md': '📄',
|
|
137
|
+
'.txt': '📝',
|
|
138
|
+
'.json': '📋',
|
|
139
|
+
'.js': '📜',
|
|
140
|
+
'.cjs': '📜',
|
|
141
|
+
'.mjs': '📜',
|
|
142
|
+
'.ts': '📘',
|
|
143
|
+
'.css': '🎨',
|
|
144
|
+
'.html': '🌐',
|
|
145
|
+
'.png': '🖼️',
|
|
146
|
+
'.jpg': '🖼️',
|
|
147
|
+
'.jpeg': '🖼️',
|
|
148
|
+
'.gif': '🖼️',
|
|
149
|
+
'.svg': '🖼️',
|
|
150
|
+
'.pdf': '📕',
|
|
151
|
+
'.yaml': '⚙️',
|
|
152
|
+
'.yml': '⚙️',
|
|
153
|
+
'.toml': '⚙️',
|
|
154
|
+
'.env': '🔐',
|
|
155
|
+
'.sh': '💻',
|
|
156
|
+
'.bash': '💻'
|
|
157
|
+
};
|
|
158
|
+
return iconMap[ext] || '📄';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Render directory browser HTML
|
|
163
|
+
*/
|
|
164
|
+
function renderDirectoryBrowser(dirPath, assetsDir) {
|
|
165
|
+
const items = fs.readdirSync(dirPath);
|
|
166
|
+
const displayPath = dirPath.length > 50 ? '...' + dirPath.slice(-47) : dirPath;
|
|
167
|
+
|
|
168
|
+
// Separate directories and files, sort alphabetically
|
|
169
|
+
const dirs = [];
|
|
170
|
+
const files = [];
|
|
171
|
+
|
|
172
|
+
for (const item of items) {
|
|
173
|
+
// Skip hidden files and deprecated folders
|
|
174
|
+
if (item.startsWith('.') || item === 'deprecated') continue;
|
|
175
|
+
|
|
176
|
+
const itemPath = path.join(dirPath, item);
|
|
177
|
+
try {
|
|
178
|
+
const stats = fs.statSync(itemPath);
|
|
179
|
+
if (stats.isDirectory()) {
|
|
180
|
+
dirs.push(item);
|
|
181
|
+
} else {
|
|
182
|
+
files.push(item);
|
|
183
|
+
}
|
|
184
|
+
} catch {
|
|
185
|
+
// Skip items we can't stat
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
dirs.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
190
|
+
files.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
191
|
+
|
|
192
|
+
// Build file list HTML
|
|
193
|
+
let listHtml = '';
|
|
194
|
+
|
|
195
|
+
// Parent directory link (if not root)
|
|
196
|
+
const parentDir = path.dirname(dirPath);
|
|
197
|
+
if (parentDir !== dirPath) {
|
|
198
|
+
listHtml += `<li class="dir-item parent">
|
|
199
|
+
<a href="/browse?dir=${encodeURIComponent(parentDir)}">
|
|
200
|
+
<span class="icon">📁</span>
|
|
201
|
+
<span class="name">..</span>
|
|
202
|
+
</a>
|
|
203
|
+
</li>`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Directories
|
|
207
|
+
for (const dir of dirs) {
|
|
208
|
+
const fullPath = path.join(dirPath, dir);
|
|
209
|
+
listHtml += `<li class="dir-item folder">
|
|
210
|
+
<a href="/browse?dir=${encodeURIComponent(fullPath)}">
|
|
211
|
+
<span class="icon">📁</span>
|
|
212
|
+
<span class="name">${dir}/</span>
|
|
213
|
+
</a>
|
|
214
|
+
</li>`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Files
|
|
218
|
+
for (const file of files) {
|
|
219
|
+
const fullPath = path.join(dirPath, file);
|
|
220
|
+
const icon = getFileIcon(file);
|
|
221
|
+
const isMarkdown = file.endsWith('.md');
|
|
222
|
+
|
|
223
|
+
if (isMarkdown) {
|
|
224
|
+
listHtml += `<li class="dir-item file markdown">
|
|
225
|
+
<a href="/view?file=${encodeURIComponent(fullPath)}">
|
|
226
|
+
<span class="icon">${icon}</span>
|
|
227
|
+
<span class="name">${file}</span>
|
|
228
|
+
</a>
|
|
229
|
+
</li>`;
|
|
230
|
+
} else {
|
|
231
|
+
listHtml += `<li class="dir-item file">
|
|
232
|
+
<a href="/file${fullPath}" target="_blank">
|
|
233
|
+
<span class="icon">${icon}</span>
|
|
234
|
+
<span class="name">${file}</span>
|
|
235
|
+
</a>
|
|
236
|
+
</li>`;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Empty directory message
|
|
241
|
+
if (dirs.length === 0 && files.length === 0) {
|
|
242
|
+
listHtml = '<li class="empty">This directory is empty</li>';
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Read CSS
|
|
246
|
+
let css = '';
|
|
247
|
+
const cssPath = path.join(assetsDir, 'directory-browser.css');
|
|
248
|
+
if (fs.existsSync(cssPath)) {
|
|
249
|
+
css = fs.readFileSync(cssPath, 'utf8');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return `<!DOCTYPE html>
|
|
253
|
+
<html lang="en">
|
|
254
|
+
<head>
|
|
255
|
+
<meta charset="UTF-8">
|
|
256
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
257
|
+
<title>📁 ${path.basename(dirPath)}</title>
|
|
258
|
+
<style>
|
|
259
|
+
${css}
|
|
260
|
+
</style>
|
|
261
|
+
</head>
|
|
262
|
+
<body>
|
|
263
|
+
<div class="container">
|
|
264
|
+
<header>
|
|
265
|
+
<h1>📁 ${path.basename(dirPath)}</h1>
|
|
266
|
+
<p class="path">${displayPath}</p>
|
|
267
|
+
</header>
|
|
268
|
+
<ul class="file-list">
|
|
269
|
+
${listHtml}
|
|
270
|
+
</ul>
|
|
271
|
+
<footer>
|
|
272
|
+
<p>${dirs.length} folder${dirs.length !== 1 ? 's' : ''}, ${files.length} file${files.length !== 1 ? 's' : ''}</p>
|
|
273
|
+
</footer>
|
|
274
|
+
</div>
|
|
275
|
+
</body>
|
|
276
|
+
</html>`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Create HTTP server with routing
|
|
281
|
+
* @param {Object} options - Server options
|
|
282
|
+
* @param {string} options.assetsDir - Static assets directory
|
|
283
|
+
* @param {Function} options.renderMarkdown - Markdown render function (filePath) => html
|
|
284
|
+
* @param {string[]} options.allowedDirs - Allowed directories for file access
|
|
285
|
+
* @returns {http.Server} - HTTP server instance
|
|
286
|
+
*/
|
|
287
|
+
function createHttpServer(options) {
|
|
288
|
+
const { assetsDir, renderMarkdown, allowedDirs = [] } = options;
|
|
289
|
+
|
|
290
|
+
// Set allowed directories for path validation
|
|
291
|
+
if (allowedDirs.length > 0) {
|
|
292
|
+
setAllowedDirs(allowedDirs);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const server = http.createServer((req, res) => {
|
|
296
|
+
const parsedUrl = url.parse(req.url, true);
|
|
297
|
+
const pathname = decodeURIComponent(parsedUrl.pathname);
|
|
298
|
+
|
|
299
|
+
// Route: /assets/* - serve static files from assets directory
|
|
300
|
+
if (pathname.startsWith('/assets/')) {
|
|
301
|
+
const relativePath = pathname.replace('/assets/', '');
|
|
302
|
+
if (relativePath.includes('..')) {
|
|
303
|
+
sendError(res, 403, 'Access denied');
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const assetPath = path.join(assetsDir, relativePath);
|
|
307
|
+
serveFile(res, assetPath, true);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Route: /file/* - serve local files (images, etc.)
|
|
312
|
+
if (pathname.startsWith('/file/')) {
|
|
313
|
+
// Extract path after '/file/' prefix (slice(6) removes '/file/')
|
|
314
|
+
// Path is already URL-decoded by decodeURIComponent above
|
|
315
|
+
const filePath = pathname.slice(6);
|
|
316
|
+
|
|
317
|
+
if (!isPathSafe(filePath)) {
|
|
318
|
+
sendError(res, 403, 'Access denied');
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
serveFile(res, filePath);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Route: /view?file=<path> - render markdown (query param)
|
|
327
|
+
if (pathname === '/view') {
|
|
328
|
+
const filePath = parsedUrl.query?.file;
|
|
329
|
+
|
|
330
|
+
if (!filePath) {
|
|
331
|
+
sendError(res, 400, 'Missing ?file= parameter. Use /view?file=/path/to/file.md');
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (!isPathSafe(filePath)) {
|
|
336
|
+
sendError(res, 403, 'Access denied');
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (!fs.existsSync(filePath)) {
|
|
341
|
+
sendError(res, 404, 'File not found');
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
try {
|
|
346
|
+
const html = renderMarkdown(filePath);
|
|
347
|
+
sendResponse(res, 200, 'text/html', html);
|
|
348
|
+
} catch (err) {
|
|
349
|
+
console.error('[http-server] Render error:', err.message);
|
|
350
|
+
sendError(res, 500, 'Error rendering markdown');
|
|
351
|
+
}
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Route: /browse?dir=<path> - directory browser (query param)
|
|
356
|
+
if (pathname === '/browse') {
|
|
357
|
+
const dirPath = parsedUrl.query?.dir;
|
|
358
|
+
|
|
359
|
+
if (!dirPath) {
|
|
360
|
+
sendError(res, 400, 'Missing ?dir= parameter. Use /browse?dir=/path/to/directory');
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (!isPathSafe(dirPath)) {
|
|
365
|
+
sendError(res, 403, 'Access denied');
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) {
|
|
370
|
+
sendError(res, 404, 'Directory not found');
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
const html = renderDirectoryBrowser(dirPath, assetsDir);
|
|
376
|
+
sendResponse(res, 200, 'text/html', html);
|
|
377
|
+
} catch (err) {
|
|
378
|
+
console.error('[http-server] Browse error:', err.message);
|
|
379
|
+
sendError(res, 500, 'Error listing directory');
|
|
380
|
+
}
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Route: / - show welcome/usage page
|
|
385
|
+
if (pathname === '/') {
|
|
386
|
+
sendResponse(res, 200, 'text/html', `
|
|
387
|
+
<!DOCTYPE html>
|
|
388
|
+
<html>
|
|
389
|
+
<head>
|
|
390
|
+
<title>Markdown Novel Viewer</title>
|
|
391
|
+
<style>
|
|
392
|
+
body { font-family: system-ui; max-width: 600px; margin: 2rem auto; padding: 1rem; }
|
|
393
|
+
h1 { color: #8b4513; }
|
|
394
|
+
code { background: #f5f5f5; padding: 0.2rem 0.4rem; border-radius: 3px; }
|
|
395
|
+
.routes { background: #faf8f3; padding: 1rem; border-radius: 8px; margin: 1rem 0; }
|
|
396
|
+
</style>
|
|
397
|
+
</head>
|
|
398
|
+
<body>
|
|
399
|
+
<h1>📖 Markdown Novel Viewer</h1>
|
|
400
|
+
<p>A calm, book-like viewer for markdown files.</p>
|
|
401
|
+
<div class="routes">
|
|
402
|
+
<h3>Routes</h3>
|
|
403
|
+
<ul>
|
|
404
|
+
<li><code>/view?file=/path/to/file.md</code> - View markdown</li>
|
|
405
|
+
<li><code>/browse?dir=/path/to/dir</code> - Browse directory</li>
|
|
406
|
+
</ul>
|
|
407
|
+
</div>
|
|
408
|
+
<p>Use the <code>/preview</code> command to start viewing files.</p>
|
|
409
|
+
</body>
|
|
410
|
+
</html>
|
|
411
|
+
`);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Default: 404
|
|
416
|
+
sendError(res, 404, 'Not found');
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
return server;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
module.exports = {
|
|
423
|
+
createHttpServer,
|
|
424
|
+
getMimeType,
|
|
425
|
+
sendResponse,
|
|
426
|
+
sendError,
|
|
427
|
+
serveFile,
|
|
428
|
+
isPathSafe,
|
|
429
|
+
setAllowedDirs,
|
|
430
|
+
sanitizeErrorMessage,
|
|
431
|
+
MIME_TYPES,
|
|
432
|
+
renderDirectoryBrowser,
|
|
433
|
+
getFileIcon
|
|
434
|
+
};
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown rendering engine with syntax highlighting and image resolution
|
|
3
|
+
* Converts markdown to styled HTML for novel-reader UI
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
// Lazy load dependencies
|
|
10
|
+
let marked = null;
|
|
11
|
+
let hljs = null;
|
|
12
|
+
let matter = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Initialize markdown dependencies
|
|
16
|
+
*/
|
|
17
|
+
function initDependencies() {
|
|
18
|
+
if (!marked) {
|
|
19
|
+
const { Marked } = require('marked');
|
|
20
|
+
hljs = require('highlight.js');
|
|
21
|
+
|
|
22
|
+
marked = new Marked({
|
|
23
|
+
gfm: true,
|
|
24
|
+
breaks: true
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Configure highlight.js renderer
|
|
28
|
+
marked.setOptions({
|
|
29
|
+
highlight: (code, lang) => {
|
|
30
|
+
if (lang && hljs.getLanguage(lang)) {
|
|
31
|
+
try {
|
|
32
|
+
return hljs.highlight(code, { language: lang }).value;
|
|
33
|
+
} catch {
|
|
34
|
+
return code;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return hljs.highlightAuto(code).value;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
matter = require('gray-matter');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a single image source path to /file/ route
|
|
47
|
+
* @param {string} src - Image source path
|
|
48
|
+
* @param {string} basePath - Base directory path
|
|
49
|
+
* @returns {string} - Resolved path or original if absolute URL
|
|
50
|
+
*/
|
|
51
|
+
function resolveImageSrc(src, basePath) {
|
|
52
|
+
// Skip absolute URLs
|
|
53
|
+
if (src.startsWith('http://') || src.startsWith('https://') || src.startsWith('/file')) {
|
|
54
|
+
return src;
|
|
55
|
+
}
|
|
56
|
+
// Resolve relative path to absolute /file/ route
|
|
57
|
+
// Use URL encoding to handle special chars and Windows paths (D:\...)
|
|
58
|
+
const absolutePath = path.resolve(basePath, src);
|
|
59
|
+
return `/file/${encodeURIComponent(absolutePath)}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Resolve relative image paths to /file/ routes
|
|
64
|
+
* Supports both inline and reference-style markdown images
|
|
65
|
+
* @param {string} markdown - Markdown content
|
|
66
|
+
* @param {string} basePath - Base directory path
|
|
67
|
+
* @returns {string} - Markdown with resolved image paths
|
|
68
|
+
*/
|
|
69
|
+
function resolveImages(markdown, basePath) {
|
|
70
|
+
let result = markdown;
|
|
71
|
+
|
|
72
|
+
// 1. Handle inline images:  or 
|
|
73
|
+
const inlineImgRegex = /!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
|
|
74
|
+
result = result.replace(inlineImgRegex, (match, alt, src) => {
|
|
75
|
+
const resolvedSrc = resolveImageSrc(src, basePath);
|
|
76
|
+
return ``;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// 2. Handle reference-style image definitions: [label]: src or [label]: src "title"
|
|
80
|
+
// These appear at the end of the document like: [Step 1 Initial]: ./screenshots/step1.png
|
|
81
|
+
const refDefRegex = /^\[([^\]]+)\]:\s*(\S+)(?:\s+"[^"]*")?$/gm;
|
|
82
|
+
result = result.replace(refDefRegex, (match, label, src) => {
|
|
83
|
+
const resolvedSrc = resolveImageSrc(src, basePath);
|
|
84
|
+
return `[${label}]: ${resolvedSrc}`;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Generate table of contents from headings
|
|
92
|
+
* @param {string} html - Rendered HTML
|
|
93
|
+
* @returns {Array<{level: number, id: string, text: string}>} - TOC items
|
|
94
|
+
*/
|
|
95
|
+
function generateTOC(html) {
|
|
96
|
+
const headings = [];
|
|
97
|
+
// Match h1-h3 with id attribute
|
|
98
|
+
const regex = /<h([1-3])[^>]*id="([^"]+)"[^>]*>([^<]+)<\/h\1>/gi;
|
|
99
|
+
|
|
100
|
+
let match;
|
|
101
|
+
while ((match = regex.exec(html)) !== null) {
|
|
102
|
+
headings.push({
|
|
103
|
+
level: parseInt(match[1], 10),
|
|
104
|
+
id: match[2],
|
|
105
|
+
text: match[3].trim()
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return headings;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Generate a slug from text for use as anchor ID (matches plan-navigator.cjs)
|
|
114
|
+
* @param {string} text - Text to slugify
|
|
115
|
+
* @returns {string} - URL-safe slug
|
|
116
|
+
*/
|
|
117
|
+
function slugify(text) {
|
|
118
|
+
return text
|
|
119
|
+
.toLowerCase()
|
|
120
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
121
|
+
.replace(/^-|-$/g, '');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Add IDs to headings for anchor links
|
|
126
|
+
* Also adds phase-specific IDs for inline phases in plan.md
|
|
127
|
+
* @param {string} html - Rendered HTML
|
|
128
|
+
* @returns {string} - HTML with heading IDs
|
|
129
|
+
*/
|
|
130
|
+
function addHeadingIds(html) {
|
|
131
|
+
const usedIds = new Set();
|
|
132
|
+
|
|
133
|
+
return html.replace(/<h([1-6])>([^<]+)<\/h\1>/gi, (match, level, text) => {
|
|
134
|
+
// Check if this is a phase heading (e.g., "Phase 01: Name" or contains phase table row content)
|
|
135
|
+
const phaseMatch = text.match(/^Phase\s*(\d+)[:\s]+(.+)/i);
|
|
136
|
+
|
|
137
|
+
let id;
|
|
138
|
+
if (phaseMatch) {
|
|
139
|
+
// Generate phase-specific anchor ID that matches plan-navigator.cjs format
|
|
140
|
+
const phaseNum = parseInt(phaseMatch[1], 10);
|
|
141
|
+
const phaseName = phaseMatch[2].trim();
|
|
142
|
+
id = `phase-${String(phaseNum).padStart(2, '0')}-${slugify(phaseName)}`;
|
|
143
|
+
} else {
|
|
144
|
+
// Standard heading ID generation
|
|
145
|
+
id = slugify(text);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Handle duplicate IDs
|
|
149
|
+
let uniqueId = id;
|
|
150
|
+
let counter = 1;
|
|
151
|
+
while (usedIds.has(uniqueId)) {
|
|
152
|
+
uniqueId = `${id}-${counter}`;
|
|
153
|
+
counter++;
|
|
154
|
+
}
|
|
155
|
+
usedIds.add(uniqueId);
|
|
156
|
+
|
|
157
|
+
return `<h${level} id="${uniqueId}">${text}</h${level}>`;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Add anchor IDs to phase table rows
|
|
163
|
+
* Matches table rows with phase numbers: | 01 | Description | Status |
|
|
164
|
+
* @param {string} html - Rendered HTML
|
|
165
|
+
* @returns {string} - HTML with phase anchor IDs in table rows
|
|
166
|
+
*/
|
|
167
|
+
function addPhaseTableAnchors(html) {
|
|
168
|
+
const usedIds = new Set();
|
|
169
|
+
|
|
170
|
+
// Match table rows with phase pattern: <tr><td>01</td><td>Description</td>...
|
|
171
|
+
// This handles the "Phase Summary" table format
|
|
172
|
+
return html.replace(/<tr>\s*<td>(\d{2})<\/td>\s*<td>([^<]+)<\/td>/gi, (match, phaseNum, description) => {
|
|
173
|
+
const num = parseInt(phaseNum, 10);
|
|
174
|
+
const slug = slugify(description.trim());
|
|
175
|
+
const id = `phase-${String(num).padStart(2, '0')}-${slug}`;
|
|
176
|
+
|
|
177
|
+
// Handle duplicates
|
|
178
|
+
let uniqueId = id;
|
|
179
|
+
let counter = 1;
|
|
180
|
+
while (usedIds.has(uniqueId)) {
|
|
181
|
+
uniqueId = `${id}-${counter}`;
|
|
182
|
+
counter++;
|
|
183
|
+
}
|
|
184
|
+
usedIds.add(uniqueId);
|
|
185
|
+
|
|
186
|
+
// Add anchor span at the start of the row
|
|
187
|
+
return `<tr id="${uniqueId}"><td>${phaseNum}</td><td>${description}</td>`;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Parse frontmatter from markdown
|
|
193
|
+
* @param {string} content - Raw markdown content
|
|
194
|
+
* @returns {{data: Object, content: string}} - Parsed frontmatter and content
|
|
195
|
+
*/
|
|
196
|
+
function parseFrontmatter(content) {
|
|
197
|
+
initDependencies();
|
|
198
|
+
return matter(content);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Render markdown file to HTML
|
|
203
|
+
* @param {string} filePath - Path to markdown file
|
|
204
|
+
* @param {Object} options - Render options
|
|
205
|
+
* @returns {{html: string, toc: Array, frontmatter: Object, title: string}}
|
|
206
|
+
*/
|
|
207
|
+
function renderMarkdownFile(filePath, options = {}) {
|
|
208
|
+
initDependencies();
|
|
209
|
+
|
|
210
|
+
const rawContent = fs.readFileSync(filePath, 'utf8');
|
|
211
|
+
const basePath = path.dirname(filePath);
|
|
212
|
+
|
|
213
|
+
// Parse frontmatter
|
|
214
|
+
const { data: frontmatter, content } = parseFrontmatter(rawContent);
|
|
215
|
+
|
|
216
|
+
// Resolve image paths
|
|
217
|
+
const resolvedContent = resolveImages(content, basePath);
|
|
218
|
+
|
|
219
|
+
// Render markdown to HTML
|
|
220
|
+
let html = marked.parse(resolvedContent);
|
|
221
|
+
|
|
222
|
+
// Add IDs to headings
|
|
223
|
+
html = addHeadingIds(html);
|
|
224
|
+
|
|
225
|
+
// Add anchor IDs to phase table rows (for inline phases in plan.md)
|
|
226
|
+
html = addPhaseTableAnchors(html);
|
|
227
|
+
|
|
228
|
+
// Generate TOC
|
|
229
|
+
const toc = generateTOC(html);
|
|
230
|
+
|
|
231
|
+
// Extract title from frontmatter or first h1
|
|
232
|
+
let title = frontmatter.title;
|
|
233
|
+
if (!title) {
|
|
234
|
+
const h1Match = html.match(/<h1[^>]*>([^<]+)<\/h1>/i);
|
|
235
|
+
title = h1Match ? h1Match[1] : path.basename(filePath, '.md');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
html,
|
|
240
|
+
toc,
|
|
241
|
+
frontmatter,
|
|
242
|
+
title
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Render TOC as HTML sidebar
|
|
248
|
+
* @param {Array} toc - TOC items
|
|
249
|
+
* @returns {string} - HTML string
|
|
250
|
+
*/
|
|
251
|
+
function renderTOCHtml(toc) {
|
|
252
|
+
if (!toc.length) return '';
|
|
253
|
+
|
|
254
|
+
const items = toc.map(({ level, id, text }) => {
|
|
255
|
+
const indent = (level - 1) * 12;
|
|
256
|
+
return `<li style="padding-left: ${indent}px"><a href="#${id}">${text}</a></li>`;
|
|
257
|
+
}).join('\n');
|
|
258
|
+
|
|
259
|
+
return `<ul class="toc-list">${items}</ul>`;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
module.exports = {
|
|
263
|
+
renderMarkdownFile,
|
|
264
|
+
resolveImages,
|
|
265
|
+
resolveImageSrc,
|
|
266
|
+
generateTOC,
|
|
267
|
+
addHeadingIds,
|
|
268
|
+
addPhaseTableAnchors,
|
|
269
|
+
parseFrontmatter,
|
|
270
|
+
renderTOCHtml,
|
|
271
|
+
initDependencies
|
|
272
|
+
};
|