cokit-cli 1.0.1 → 1.0.3
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/AGENTS.md +103 -0
- package/README.FLOW.md +237 -0
- package/README.md +153 -94
- package/agents/brainstormer.agent.md +71 -0
- package/agents/code-reviewer.agent.md +128 -0
- package/agents/database-admin.agent.md +91 -0
- package/agents/debugger.agent.md +128 -0
- package/agents/docs-manager.agent.md +121 -0
- package/agents/fullstack-developer.agent.md +96 -0
- package/agents/git-manager.agent.md +392 -0
- package/agents/mcp-manager.agent.md +93 -0
- package/agents/planner.agent.md +94 -0
- package/agents/project-manager.agent.md +124 -0
- package/agents/researcher.agent.md +32 -0
- package/agents/scout-external.agent.md +141 -0
- package/agents/scout.agent.md +107 -0
- package/agents/tester.agent.md +106 -0
- package/agents/ui-ux-designer.agent.md +225 -0
- package/collections/ck-core.collection.yml +30 -0
- package/collections/ck-development-rules.collection.yml +18 -0
- package/collections/ck-documentation.collection.yml +18 -0
- package/collections/ck-git-workflow.collection.yml +18 -0
- package/collections/ck-orchestration.collection.yml +22 -0
- package/collections/ck-ui-design.collection.yml +18 -0
- package/docs/README.md +85 -0
- package/docs/copilot-processing-flow.md +128 -0
- package/instructions/ck-backend.instructions.md +48 -0
- package/instructions/ck-development.instructions.md +40 -0
- package/instructions/ck-frontend-design-pro.instructions.md +58 -0
- package/instructions/ck-frontend.instructions.md +44 -0
- package/instructions/ck-google-adk-python.instructions.md +242 -0
- package/instructions/ck-research.instructions.md +167 -0
- package/instructions/ck-testing.instructions.md +36 -0
- package/package.json +9 -2
- package/plans/260108-0924-rebuild-cokit-awesome-copilot-format/phase-01-restructure-folders.md +183 -0
- package/plans/260108-0924-rebuild-cokit-awesome-copilot-format/phase-02-convert-agents.md +206 -0
- package/plans/260108-0924-rebuild-cokit-awesome-copilot-format/phase-03-convert-commands-to-prompts.md +284 -0
- package/plans/260108-0924-rebuild-cokit-awesome-copilot-format/phase-04-convert-skills-to-instructions.md +349 -0
- package/plans/260108-0924-rebuild-cokit-awesome-copilot-format/phase-05-create-collections.md +320 -0
- package/plans/260108-0924-rebuild-cokit-awesome-copilot-format/phase-06-update-cli-build-scripts.md +450 -0
- package/plans/260108-0924-rebuild-cokit-awesome-copilot-format/phase-07-update-documentation.md +407 -0
- package/plans/260108-0924-rebuild-cokit-awesome-copilot-format/plan.md +136 -0
- package/prompts/ck-ask.prompt.md +57 -0
- package/prompts/ck-bootstrap-auto-fast.prompt.md +108 -0
- package/prompts/ck-bootstrap-auto-parallel.prompt.md +66 -0
- package/prompts/ck-bootstrap-auto.prompt.md +114 -0
- package/prompts/ck-bootstrap.prompt.md +136 -0
- package/prompts/ck-brainstorm.prompt.md +68 -0
- package/prompts/ck-ck-help.prompt.md +114 -0
- package/prompts/ck-code-auto.prompt.md +171 -0
- package/prompts/ck-code-no-test.prompt.md +159 -0
- package/prompts/ck-code-parallel.prompt.md +56 -0
- package/prompts/ck-code.prompt.md +177 -0
- package/prompts/ck-coding-level.prompt.md +54 -0
- package/prompts/ck-cook-auto-fast.prompt.md +26 -0
- package/prompts/ck-cook-auto-parallel.prompt.md +49 -0
- package/prompts/ck-cook-auto.prompt.md +15 -0
- package/prompts/ck-cook.prompt.md +96 -0
- package/prompts/ck-debug.prompt.md +14 -0
- package/prompts/ck-design-3d.prompt.md +84 -0
- package/prompts/ck-design-describe.prompt.md +24 -0
- package/prompts/ck-design-fast.prompt.md +32 -0
- package/prompts/ck-design-good.prompt.md +36 -0
- package/prompts/ck-design-screenshot.prompt.md +35 -0
- package/prompts/ck-design-video.prompt.md +35 -0
- package/prompts/ck-docs-init.prompt.md +27 -0
- package/prompts/ck-docs-summarize.prompt.md +23 -0
- package/prompts/ck-docs-update.prompt.md +36 -0
- package/prompts/ck-fix-ci.prompt.md +18 -0
- package/prompts/ck-fix-fast.prompt.md +18 -0
- package/prompts/ck-fix-hard.prompt.md +36 -0
- package/prompts/ck-fix-logs.prompt.md +27 -0
- package/prompts/ck-fix-parallel.prompt.md +54 -0
- package/prompts/ck-fix-test.prompt.md +19 -0
- package/prompts/ck-fix-types.prompt.md +11 -0
- package/prompts/ck-fix-ui.prompt.md +49 -0
- package/prompts/ck-fix.prompt.md +44 -0
- package/prompts/ck-git-cm.prompt.md +8 -0
- package/prompts/ck-git-cp.prompt.md +7 -0
- package/prompts/ck-git-merge.prompt.md +41 -0
- package/prompts/ck-git-pr.prompt.md +49 -0
- package/prompts/ck-kanban.prompt.md +77 -0
- package/prompts/ck-plan-archive.prompt.md +58 -0
- package/prompts/ck-plan-ci.prompt.md +34 -0
- package/prompts/ck-plan-cro.prompt.md +66 -0
- package/prompts/ck-plan-fast.prompt.md +67 -0
- package/prompts/ck-plan-hard.prompt.md +89 -0
- package/prompts/ck-plan-parallel.prompt.md +126 -0
- package/prompts/ck-plan-two.prompt.md +43 -0
- package/prompts/ck-plan-validate.prompt.md +118 -0
- package/prompts/ck-plan.prompt.md +29 -1
- package/prompts/ck-preview.prompt.md +65 -0
- package/prompts/ck-review-codebase.prompt.md +44 -0
- package/prompts/ck-scout-ext.prompt.md +35 -0
- package/prompts/ck-scout.prompt.md +25 -0
- package/prompts/ck-skill-add.prompt.md +36 -0
- package/prompts/ck-skill-create.prompt.md +29 -0
- package/prompts/ck-skill-fix-logs.prompt.md +22 -0
- package/prompts/ck-skill-optimize-auto.prompt.md +25 -0
- package/prompts/ck-skill-optimize.prompt.md +34 -0
- package/prompts/ck-skill-plan.prompt.md +46 -0
- package/prompts/ck-test-ui.prompt.md +92 -0
- package/prompts/ck-test.prompt.md +10 -0
- package/prompts/ck-use-mcp.prompt.md +27 -0
- package/prompts/ck-watzup.prompt.md +11 -0
- package/prompts/ck-worktree.prompt.md +127 -0
- package/rules/README.agents.md +55 -0
- package/rules/README.collections.md +78 -0
- package/rules/README.copilot-instructions.md +66 -0
- package/rules/README.instructions.md +73 -0
- package/rules/README.md +39 -0
- package/rules/README.prompts.md +76 -0
- package/rules/README.skills.md +71 -0
- package/skills/ck-ai-artist/SKILL.md +73 -0
- package/skills/ck-ai-artist/references/advanced-techniques.md +184 -0
- package/skills/ck-ai-artist/references/domain-code.md +66 -0
- package/skills/ck-ai-artist/references/domain-data.md +72 -0
- package/skills/ck-ai-artist/references/domain-marketing.md +66 -0
- package/skills/ck-ai-artist/references/domain-patterns.md +33 -0
- package/skills/ck-ai-artist/references/domain-writing.md +68 -0
- package/skills/ck-ai-artist/references/image-prompting.md +141 -0
- package/skills/ck-ai-artist/references/llm-prompting.md +165 -0
- package/skills/ck-ai-artist/references/nano-banana.md +59 -0
- package/skills/ck-ai-artist/references/reasoning-techniques.md +201 -0
- package/skills/ck-backend-development/SKILL.md +93 -0
- package/skills/ck-backend-development/references/backend-api-design.md +495 -0
- package/skills/ck-backend-development/references/backend-architecture.md +454 -0
- package/skills/ck-backend-development/references/backend-authentication.md +338 -0
- package/skills/ck-backend-development/references/backend-code-quality.md +659 -0
- package/skills/ck-backend-development/references/backend-debugging.md +904 -0
- package/skills/ck-backend-development/references/backend-devops.md +494 -0
- package/skills/ck-backend-development/references/backend-mindset.md +387 -0
- package/skills/ck-backend-development/references/backend-performance.md +397 -0
- package/skills/ck-backend-development/references/backend-security.md +290 -0
- package/skills/ck-backend-development/references/backend-technologies.md +256 -0
- package/skills/ck-backend-development/references/backend-testing.md +429 -0
- package/skills/ck-better-auth/SKILL.md +202 -0
- package/skills/ck-better-auth/references/advanced-features.md +553 -0
- package/skills/ck-better-auth/references/database-integration.md +577 -0
- package/skills/ck-better-auth/references/email-password-auth.md +416 -0
- package/skills/ck-better-auth/references/oauth-providers.md +430 -0
- package/skills/ck-better-auth/scripts/.coverage +0 -0
- package/skills/ck-better-auth/scripts/better_auth_init.py +521 -0
- package/skills/ck-better-auth/scripts/requirements.txt +15 -0
- package/skills/ck-better-auth/scripts/tests/.coverage +0 -0
- package/skills/ck-better-auth/scripts/tests/test_better_auth_init.py +421 -0
- package/skills/ck-chrome-devtools/SKILL.md +470 -0
- package/skills/ck-chrome-devtools/references/cdp-domains.md +694 -0
- package/skills/ck-chrome-devtools/references/performance-guide.md +940 -0
- package/skills/ck-chrome-devtools/references/puppeteer-reference.md +953 -0
- package/skills/ck-chrome-devtools/scripts/README.md +272 -0
- package/skills/ck-chrome-devtools/scripts/__tests__/selector.test.js +210 -0
- package/skills/ck-chrome-devtools/scripts/aria-snapshot.js +362 -0
- package/skills/ck-chrome-devtools/scripts/click.js +83 -0
- package/skills/ck-chrome-devtools/scripts/console.js +79 -0
- package/skills/ck-chrome-devtools/scripts/evaluate.js +53 -0
- package/skills/ck-chrome-devtools/scripts/fill.js +76 -0
- package/skills/ck-chrome-devtools/scripts/inject-auth.js +229 -0
- package/skills/ck-chrome-devtools/scripts/install-deps.sh +181 -0
- package/skills/ck-chrome-devtools/scripts/install.sh +83 -0
- package/skills/ck-chrome-devtools/scripts/lib/browser.js +318 -0
- package/skills/ck-chrome-devtools/scripts/lib/selector.js +178 -0
- package/skills/ck-chrome-devtools/scripts/navigate.js +54 -0
- package/skills/ck-chrome-devtools/scripts/network.js +106 -0
- package/skills/ck-chrome-devtools/scripts/package-lock.json +1589 -0
- package/skills/ck-chrome-devtools/scripts/package.json +16 -0
- package/skills/ck-chrome-devtools/scripts/performance.js +149 -0
- package/skills/ck-chrome-devtools/scripts/screenshot.js +198 -0
- package/skills/ck-chrome-devtools/scripts/select-ref.js +131 -0
- package/skills/ck-chrome-devtools/scripts/snapshot.js +135 -0
- package/skills/ck-code-review/SKILL.md +110 -53
- package/skills/ck-code-review/references/code-review-reception.md +182 -49
- package/skills/ck-code-review/references/requesting-code-review.md +105 -0
- package/skills/ck-code-review/references/verification-before-completion.md +111 -58
- package/skills/ck-databases/SKILL.md +231 -0
- package/skills/ck-databases/references/mongodb-aggregation.md +447 -0
- package/skills/ck-databases/references/mongodb-atlas.md +465 -0
- package/skills/ck-databases/references/mongodb-crud.md +408 -0
- package/skills/ck-databases/references/mongodb-indexing.md +442 -0
- package/skills/ck-databases/references/postgresql-administration.md +594 -0
- package/skills/ck-databases/references/postgresql-performance.md +527 -0
- package/skills/ck-databases/references/postgresql-psql-cli.md +467 -0
- package/skills/ck-databases/references/postgresql-queries.md +475 -0
- package/skills/ck-databases/scripts/.coverage +0 -0
- package/skills/ck-databases/scripts/db_backup.py +502 -0
- package/skills/ck-databases/scripts/db_migrate.py +425 -0
- package/skills/ck-databases/scripts/db_performance_check.py +456 -0
- package/skills/ck-databases/scripts/requirements.txt +20 -0
- package/skills/ck-databases/scripts/tests/coverage-db.json +1 -0
- package/skills/ck-databases/scripts/tests/requirements.txt +4 -0
- package/skills/ck-databases/scripts/tests/test_db_backup.py +340 -0
- package/skills/ck-databases/scripts/tests/test_db_migrate.py +277 -0
- package/skills/ck-databases/scripts/tests/test_db_performance_check.py +370 -0
- package/skills/ck-debugging/SKILL.md +55 -43
- package/skills/ck-debugging/references/defense-in-depth.md +124 -0
- package/skills/ck-debugging/references/root-cause-tracing.md +100 -43
- package/skills/ck-debugging/references/systematic-debugging.md +80 -52
- package/skills/ck-debugging/references/verification.md +101 -52
- package/skills/ck-debugging/scripts/find-polluter.sh +63 -0
- package/skills/ck-debugging/scripts/find-polluter.test.md +102 -0
- package/skills/ck-devops/.env.example +76 -0
- package/skills/ck-devops/SKILL.md +283 -0
- package/skills/ck-devops/references/browser-rendering.md +305 -0
- package/skills/ck-devops/references/cloudflare-d1-kv.md +123 -0
- package/skills/ck-devops/references/cloudflare-platform.md +271 -0
- package/skills/ck-devops/references/cloudflare-r2-storage.md +280 -0
- package/skills/ck-devops/references/cloudflare-workers-advanced.md +312 -0
- package/skills/ck-devops/references/cloudflare-workers-apis.md +309 -0
- package/skills/ck-devops/references/cloudflare-workers-basics.md +418 -0
- package/skills/ck-devops/references/docker-basics.md +297 -0
- package/skills/ck-devops/references/docker-compose.md +292 -0
- package/skills/ck-devops/references/gcloud-platform.md +297 -0
- package/skills/ck-devops/references/gcloud-services.md +304 -0
- package/skills/ck-devops/scripts/cloudflare_deploy.py +269 -0
- package/skills/ck-devops/scripts/docker_optimize.py +331 -0
- package/skills/ck-devops/scripts/requirements.txt +20 -0
- package/skills/ck-devops/scripts/tests/requirements.txt +3 -0
- package/skills/ck-devops/scripts/tests/test_cloudflare_deploy.py +285 -0
- package/skills/ck-devops/scripts/tests/test_docker_optimize.py +436 -0
- package/skills/ck-docs-seeker/.env.example +15 -0
- package/skills/ck-docs-seeker/SKILL.md +70 -65
- package/skills/ck-docs-seeker/package.json +25 -0
- package/skills/ck-docs-seeker/references/advanced.md +79 -0
- package/skills/ck-docs-seeker/references/context7-patterns.md +68 -0
- package/skills/ck-docs-seeker/references/errors.md +68 -0
- package/skills/ck-docs-seeker/scripts/analyze-llms-txt.js +211 -0
- package/skills/ck-docs-seeker/scripts/detect-topic.js +172 -0
- package/skills/ck-docs-seeker/scripts/fetch-docs.js +213 -0
- package/skills/ck-docs-seeker/scripts/tests/run-tests.js +72 -0
- package/skills/ck-docs-seeker/scripts/tests/test-analyze-llms.js +119 -0
- package/skills/ck-docs-seeker/scripts/tests/test-detect-topic.js +112 -0
- package/skills/ck-docs-seeker/scripts/tests/test-fetch-docs.js +84 -0
- package/skills/ck-docs-seeker/scripts/utils/env-loader.js +94 -0
- package/skills/ck-docs-seeker/workflows/library-search.md +87 -0
- package/skills/ck-docs-seeker/workflows/repo-analysis.md +91 -0
- package/skills/ck-docs-seeker/workflows/topic-search.md +77 -0
- package/skills/ck-frontend-design/SKILL.md +79 -0
- package/skills/ck-frontend-design/references/analysis-best-practices.md +80 -0
- package/skills/ck-frontend-design/references/analysis-prompts.md +141 -0
- package/skills/ck-frontend-design/references/analysis-techniques.md +118 -0
- package/skills/ck-frontend-design/references/animejs.md +396 -0
- package/skills/ck-frontend-design/references/design-extraction-overview.md +71 -0
- package/skills/ck-frontend-design/references/extraction-best-practices.md +141 -0
- package/skills/ck-frontend-design/references/extraction-output-templates.md +162 -0
- package/skills/ck-frontend-design/references/extraction-prompts.md +127 -0
- package/skills/ck-frontend-design/references/technical-accessibility.md +119 -0
- package/skills/ck-frontend-design/references/technical-best-practices.md +97 -0
- package/skills/ck-frontend-design/references/technical-optimization.md +44 -0
- package/skills/ck-frontend-design/references/technical-overview.md +90 -0
- package/skills/ck-frontend-design/references/technical-workflows.md +150 -0
- package/skills/ck-frontend-design/references/visual-analysis-overview.md +95 -0
- package/skills/ck-frontend-development/SKILL.md +399 -0
- package/skills/ck-frontend-development/resources/common-patterns.md +331 -0
- package/skills/ck-frontend-development/resources/complete-examples.md +872 -0
- package/skills/ck-frontend-development/resources/component-patterns.md +502 -0
- package/skills/ck-frontend-development/resources/data-fetching.md +767 -0
- package/skills/ck-frontend-development/resources/file-organization.md +502 -0
- package/skills/ck-frontend-development/resources/loading-and-error-states.md +501 -0
- package/skills/ck-frontend-development/resources/performance.md +406 -0
- package/skills/ck-frontend-development/resources/routing-guide.md +364 -0
- package/skills/ck-frontend-development/resources/styling-guide.md +428 -0
- package/skills/ck-frontend-development/resources/typescript-standards.md +418 -0
- package/skills/ck-markdown-novel-viewer/SKILL.md +190 -0
- package/skills/ck-markdown-novel-viewer/assets/directory-browser.css +215 -0
- package/skills/ck-markdown-novel-viewer/assets/favicon.png +0 -0
- package/skills/ck-markdown-novel-viewer/assets/novel-theme.css +818 -0
- package/skills/ck-markdown-novel-viewer/assets/reader.js +262 -0
- package/skills/ck-markdown-novel-viewer/assets/template.html +80 -0
- package/skills/ck-markdown-novel-viewer/package.json +15 -0
- package/skills/ck-markdown-novel-viewer/scripts/lib/http-server.cjs +434 -0
- package/skills/ck-markdown-novel-viewer/scripts/lib/markdown-renderer.cjs +272 -0
- package/skills/ck-markdown-novel-viewer/scripts/lib/plan-navigator.cjs +509 -0
- package/skills/ck-markdown-novel-viewer/scripts/lib/port-finder.cjs +48 -0
- package/skills/ck-markdown-novel-viewer/scripts/lib/process-mgr.cjs +150 -0
- package/skills/ck-markdown-novel-viewer/scripts/server.cjs +411 -0
- package/skills/ck-markdown-novel-viewer/scripts/tests/server.test.cjs +283 -0
- package/skills/ck-markdown-novel-viewer/tests/dashboard-assets.test.cjs +340 -0
- package/skills/ck-markdown-novel-viewer/tests/dashboard-renderer.test.cjs +404 -0
- package/skills/ck-markdown-novel-viewer/tests/http-server.test.cjs +271 -0
- package/skills/ck-markdown-novel-viewer/tests/run-tests.cjs +51 -0
- package/skills/ck-markdown-novel-viewer/tests/test-framework.cjs +154 -0
- package/skills/ck-markdown-novel-viewer/tests/verify-xss.cjs +90 -0
- package/skills/ck-mcp-builder/LICENSE.txt +202 -0
- package/skills/ck-mcp-builder/SKILL.md +327 -0
- package/skills/ck-mcp-builder/reference/evaluation.md +602 -0
- package/skills/ck-mcp-builder/reference/mcp_best_practices.md +915 -0
- package/skills/ck-mcp-builder/reference/node_mcp_server.md +916 -0
- package/skills/ck-mcp-builder/reference/python_mcp_server.md +752 -0
- package/skills/ck-mcp-builder/scripts/connections.py +151 -0
- package/skills/ck-mcp-builder/scripts/evaluation.py +373 -0
- package/skills/ck-mcp-builder/scripts/example_evaluation.xml +22 -0
- package/skills/ck-mcp-builder/scripts/requirements.txt +2 -0
- package/skills/ck-mcp-management/README.md +219 -0
- package/skills/ck-mcp-management/SKILL.md +209 -0
- package/skills/ck-mcp-management/assets/tools.json +3146 -0
- package/skills/ck-mcp-management/references/configuration.md +114 -0
- package/skills/ck-mcp-management/references/gemini-cli-integration.md +215 -0
- package/skills/ck-mcp-management/references/mcp-protocol.md +116 -0
- package/skills/ck-mcp-management/scripts/.env.example +10 -0
- package/skills/ck-mcp-management/scripts/cli.ts +195 -0
- package/skills/ck-mcp-management/scripts/dist/analyze-tools.js +70 -0
- package/skills/ck-mcp-management/scripts/dist/cli.js +160 -0
- package/skills/ck-mcp-management/scripts/dist/mcp-client.js +183 -0
- package/skills/ck-mcp-management/scripts/mcp-client.ts +230 -0
- package/skills/ck-mcp-management/scripts/package.json +20 -0
- package/skills/ck-mcp-management/scripts/tsconfig.json +15 -0
- package/skills/ck-media-processing/SKILL.md +90 -0
- package/skills/ck-media-processing/references/common-workflows.md +132 -0
- package/skills/ck-media-processing/references/ffmpeg-encoding.md +358 -0
- package/skills/ck-media-processing/references/ffmpeg-filters.md +503 -0
- package/skills/ck-media-processing/references/ffmpeg-streaming.md +403 -0
- package/skills/ck-media-processing/references/format-compatibility.md +375 -0
- package/skills/ck-media-processing/references/imagemagick-batch.md +612 -0
- package/skills/ck-media-processing/references/imagemagick-editing.md +623 -0
- package/skills/ck-media-processing/references/rmbg-background-removal.md +66 -0
- package/skills/ck-media-processing/references/troubleshooting.md +109 -0
- package/skills/ck-media-processing/scripts/README.md +111 -0
- package/skills/ck-media-processing/scripts/batch-remove-background.sh +124 -0
- package/skills/ck-media-processing/scripts/batch_resize.py +342 -0
- package/skills/ck-media-processing/scripts/media_convert.py +311 -0
- package/skills/ck-media-processing/scripts/remove-background.sh +96 -0
- package/skills/ck-media-processing/scripts/remove-bg-node.js +158 -0
- package/skills/ck-media-processing/scripts/requirements.txt +24 -0
- package/skills/ck-media-processing/scripts/tests/.coverage +0 -0
- package/skills/ck-media-processing/scripts/tests/requirements.txt +2 -0
- package/skills/ck-media-processing/scripts/tests/test_batch_resize.py +372 -0
- package/skills/ck-media-processing/scripts/tests/test_media_convert.py +259 -0
- package/skills/ck-media-processing/scripts/tests/test_video_optimize.py +397 -0
- package/skills/ck-media-processing/scripts/video_optimize.py +414 -0
- package/skills/ck-mobile-development/SKILL.md +210 -0
- package/skills/ck-mobile-development/references/mobile-android.md +604 -0
- package/skills/ck-mobile-development/references/mobile-best-practices.md +545 -0
- package/skills/ck-mobile-development/references/mobile-debugging.md +1089 -0
- package/skills/ck-mobile-development/references/mobile-frameworks.md +465 -0
- package/skills/ck-mobile-development/references/mobile-ios.md +496 -0
- package/skills/ck-mobile-development/references/mobile-mindset.md +544 -0
- package/skills/ck-payment-integration/README.md +185 -0
- package/skills/ck-payment-integration/SKILL.md +116 -0
- package/skills/ck-payment-integration/references/polar/benefits.md +396 -0
- package/skills/ck-payment-integration/references/polar/best-practices.md +482 -0
- package/skills/ck-payment-integration/references/polar/checkouts.md +266 -0
- package/skills/ck-payment-integration/references/polar/overview.md +184 -0
- package/skills/ck-payment-integration/references/polar/products.md +244 -0
- package/skills/ck-payment-integration/references/polar/sdk.md +436 -0
- package/skills/ck-payment-integration/references/polar/subscriptions.md +340 -0
- package/skills/ck-payment-integration/references/polar/webhooks.md +405 -0
- package/skills/ck-payment-integration/references/sepay/api.md +140 -0
- package/skills/ck-payment-integration/references/sepay/best-practices.md +337 -0
- package/skills/ck-payment-integration/references/sepay/overview.md +138 -0
- package/skills/ck-payment-integration/references/sepay/qr-codes.md +228 -0
- package/skills/ck-payment-integration/references/sepay/sdk.md +213 -0
- package/skills/ck-payment-integration/references/sepay/webhooks.md +208 -0
- package/skills/ck-payment-integration/scripts/.env.example +20 -0
- package/skills/ck-payment-integration/scripts/checkout-helper.js +244 -0
- package/skills/ck-payment-integration/scripts/package.json +17 -0
- package/skills/ck-payment-integration/scripts/polar-webhook-verify.js +202 -0
- package/skills/ck-payment-integration/scripts/sepay-webhook-verify.js +193 -0
- package/skills/ck-payment-integration/scripts/test-scripts.js +237 -0
- package/skills/ck-planning/SKILL.md +79 -49
- package/skills/ck-planning/references/codebase-understanding.md +62 -0
- package/skills/ck-planning/references/output-standards.md +127 -0
- package/skills/ck-planning/references/plan-organization.md +122 -60
- package/skills/ck-planning/references/research-phase.md +49 -56
- package/skills/ck-planning/references/solution-design.md +61 -63
- package/skills/ck-problem-solving/SKILL.md +95 -0
- package/skills/ck-problem-solving/references/attribution.md +69 -0
- package/skills/ck-problem-solving/references/collision-zone-thinking.md +79 -0
- package/skills/ck-problem-solving/references/inversion-exercise.md +91 -0
- package/skills/ck-problem-solving/references/meta-pattern-recognition.md +87 -0
- package/skills/ck-problem-solving/references/scale-game.md +95 -0
- package/skills/ck-problem-solving/references/simplification-cascades.md +80 -0
- package/skills/ck-problem-solving/references/when-stuck.md +72 -0
- package/skills/ck-repomix/SKILL.md +247 -0
- package/skills/ck-repomix/references/configuration.md +211 -0
- package/skills/ck-repomix/references/usage-patterns.md +232 -0
- package/skills/ck-repomix/scripts/.coverage +0 -0
- package/skills/ck-repomix/scripts/README.md +179 -0
- package/skills/ck-repomix/scripts/repomix_batch.py +455 -0
- package/skills/ck-repomix/scripts/repos.example.json +15 -0
- package/skills/ck-repomix/scripts/requirements.txt +15 -0
- package/skills/ck-repomix/scripts/tests/test_repomix_batch.py +531 -0
- package/skills/ck-sequential-thinking/.env.example +8 -0
- package/skills/ck-sequential-thinking/README.md +183 -0
- package/skills/ck-sequential-thinking/SKILL.md +14 -2
- package/skills/ck-sequential-thinking/package.json +31 -0
- package/skills/ck-sequential-thinking/references/advanced-strategies.md +79 -0
- package/skills/ck-sequential-thinking/references/advanced-techniques.md +2 -14
- package/skills/ck-sequential-thinking/references/core-patterns.md +8 -0
- package/skills/ck-sequential-thinking/references/examples-api.md +88 -0
- package/skills/ck-sequential-thinking/references/examples-architecture.md +94 -0
- package/skills/ck-sequential-thinking/references/examples-debug.md +90 -0
- package/skills/ck-sequential-thinking/scripts/format-thought.js +159 -0
- package/skills/ck-sequential-thinking/scripts/process-thought.js +236 -0
- package/skills/ck-sequential-thinking/tests/format-thought.test.js +133 -0
- package/skills/ck-sequential-thinking/tests/process-thought.test.js +215 -0
- package/skills/ck-shopify/README.md +66 -0
- package/skills/ck-shopify/SKILL.md +319 -0
- package/skills/ck-shopify/references/app-development.md +470 -0
- package/skills/ck-shopify/references/extensions.md +493 -0
- package/skills/ck-shopify/references/themes.md +498 -0
- package/skills/ck-shopify/scripts/.coverage +0 -0
- package/skills/ck-shopify/scripts/requirements.txt +19 -0
- package/skills/ck-shopify/scripts/shopify_init.py +423 -0
- package/skills/ck-shopify/scripts/tests/.coverage +0 -0
- package/skills/ck-shopify/scripts/tests/test_shopify_init.py +385 -0
- package/skills/ck-skill-creator/LICENSE.txt +202 -0
- package/skills/ck-skill-creator/SKILL.md +265 -0
- package/skills/ck-skill-creator/scripts/init_skill.py +303 -0
- package/skills/ck-skill-creator/scripts/package_skill.py +110 -0
- package/skills/ck-skill-creator/scripts/quick_validate.py +65 -0
- package/skills/ck-threejs/SKILL.md +87 -0
- package/skills/ck-threejs/references/01-getting-started.md +177 -0
- package/skills/ck-threejs/references/02-loaders.md +169 -0
- package/skills/ck-threejs/references/03-textures.md +170 -0
- package/skills/ck-threejs/references/04-cameras.md +195 -0
- package/skills/ck-threejs/references/05-lights.md +183 -0
- package/skills/ck-threejs/references/06-animations.md +214 -0
- package/skills/ck-threejs/references/07-math.md +260 -0
- package/skills/ck-threejs/references/08-interaction.md +267 -0
- package/skills/ck-threejs/references/09-postprocessing.md +240 -0
- package/skills/ck-threejs/references/10-controls.md +259 -0
- package/skills/ck-threejs/references/11-materials-advanced.md +270 -0
- package/skills/ck-threejs/references/12-performance.md +269 -0
- package/skills/ck-threejs/references/13-node-materials.md +298 -0
- package/skills/ck-threejs/references/14-physics-vr.md +304 -0
- package/skills/ck-threejs/references/15-specialized-loaders.md +333 -0
- package/skills/ck-threejs/references/16-webgpu.md +302 -0
- package/skills/ck-ui-styling/LICENSE.txt +202 -0
- package/skills/ck-ui-styling/SKILL.md +319 -0
- package/skills/ck-ui-styling/canvas-fonts/ArsenalSC-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/BigShoulders-Bold.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/BigShoulders-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/BigShoulders-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Boldonse-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/Boldonse-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/CrimsonPro-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/DMMono-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/DMMono-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/EricaOne-OFL.txt +94 -0
- package/skills/ck-ui-styling/canvas-fonts/EricaOne-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/GeistMono-Bold.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/GeistMono-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/GeistMono-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Gloock-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/Gloock-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/InstrumentSans-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Italiana-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/Italiana-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Jura-Light.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Jura-Medium.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Jura-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Lora-Bold.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Lora-Italic.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Lora-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/Lora-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/NationalPark-Bold.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/NationalPark-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/NationalPark-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Outfit-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/PixelifySans-Medium.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/PixelifySans-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/PoiretOne-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/PoiretOne-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/RedHatMono-Bold.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/RedHatMono-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/RedHatMono-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Silkscreen-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/Silkscreen-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/SmoochSans-Medium.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/SmoochSans-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/Tektur-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/WorkSans-Bold.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/WorkSans-Italic.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/WorkSans-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/WorkSans-Regular.ttf +0 -0
- package/skills/ck-ui-styling/canvas-fonts/YoungSerif-OFL.txt +93 -0
- package/skills/ck-ui-styling/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- package/skills/ck-ui-styling/references/canvas-design-system.md +320 -0
- package/skills/ck-ui-styling/references/shadcn-accessibility.md +471 -0
- package/skills/ck-ui-styling/references/shadcn-components.md +424 -0
- package/skills/ck-ui-styling/references/shadcn-theming.md +373 -0
- package/skills/ck-ui-styling/references/tailwind-customization.md +483 -0
- package/skills/ck-ui-styling/references/tailwind-responsive.md +382 -0
- package/skills/ck-ui-styling/references/tailwind-utilities.md +455 -0
- package/skills/ck-ui-styling/scripts/.coverage +0 -0
- package/skills/ck-ui-styling/scripts/requirements.txt +17 -0
- package/skills/ck-ui-styling/scripts/shadcn_add.py +292 -0
- package/skills/ck-ui-styling/scripts/tailwind_config_gen.py +456 -0
- package/skills/ck-ui-styling/scripts/tests/coverage-ui.json +1 -0
- package/skills/ck-ui-styling/scripts/tests/requirements.txt +3 -0
- package/skills/ck-ui-styling/scripts/tests/test_shadcn_add.py +266 -0
- package/skills/ck-ui-styling/scripts/tests/test_tailwind_config_gen.py +336 -0
- package/skills/ck-ui-ux-pro-max/SKILL.md +227 -0
- package/skills/ck-ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ck-ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ck-ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ck-ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ck-ui-ux-pro-max/data/prompts.csv +24 -0
- package/skills/ck-ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ck-ui-ux-pro-max/data/stacks/html-tailwind.csv +51 -0
- package/skills/ck-ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ck-ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ck-ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ck-ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ck-ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ck-ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ck-ui-ux-pro-max/data/styles.csv +59 -0
- package/skills/ck-ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ck-ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ck-ui-ux-pro-max/scripts/core.py +236 -0
- package/skills/ck-ui-ux-pro-max/scripts/search.py +76 -0
- package/skills/ck-web-frameworks/SKILL.md +322 -0
- package/skills/ck-web-frameworks/references/nextjs-app-router.md +465 -0
- package/skills/ck-web-frameworks/references/nextjs-data-fetching.md +459 -0
- package/skills/ck-web-frameworks/references/nextjs-optimization.md +511 -0
- package/skills/ck-web-frameworks/references/nextjs-server-components.md +495 -0
- package/skills/ck-web-frameworks/references/remix-icon-integration.md +603 -0
- package/skills/ck-web-frameworks/references/turborepo-caching.md +551 -0
- package/skills/ck-web-frameworks/references/turborepo-pipelines.md +517 -0
- package/skills/ck-web-frameworks/references/turborepo-setup.md +542 -0
- package/skills/ck-web-frameworks/scripts/.coverage +0 -0
- package/skills/ck-web-frameworks/scripts/__init__.py +0 -0
- package/skills/ck-web-frameworks/scripts/nextjs_init.py +547 -0
- package/skills/ck-web-frameworks/scripts/requirements.txt +16 -0
- package/skills/ck-web-frameworks/scripts/tests/coverage-web.json +1 -0
- package/skills/ck-web-frameworks/scripts/tests/requirements.txt +3 -0
- package/skills/ck-web-frameworks/scripts/tests/test_nextjs_init.py +319 -0
- package/skills/ck-web-frameworks/scripts/tests/test_turborepo_migrate.py +374 -0
- package/skills/ck-web-frameworks/scripts/turborepo_migrate.py +394 -0
- package/src/commands/init.js +61 -30
- package/src/utils/paths.js +35 -4
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Markdown Novel Viewer Server
|
|
5
|
+
* Background HTTP server rendering markdown files with calm, book-like UI
|
|
6
|
+
*
|
|
7
|
+
* Universal viewer - pass ANY path and view it:
|
|
8
|
+
* - Markdown files → novel-reader UI
|
|
9
|
+
* - Directories → file listing browser
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* node server.cjs --file ./plan.md [--port 3456] [--no-open] [--stop] [--host 0.0.0.0]
|
|
13
|
+
* node server.cjs --dir ./plans [--port 3456] # Browse directory
|
|
14
|
+
*
|
|
15
|
+
* Options:
|
|
16
|
+
* --file <path> Path to markdown file
|
|
17
|
+
* --dir <path> Path to directory (browse mode)
|
|
18
|
+
* --port <number> Server port (default: 3456, auto-increment if busy)
|
|
19
|
+
* --host <addr> Host to bind (default: localhost, use 0.0.0.0 for all interfaces)
|
|
20
|
+
* --no-open Disable auto-open browser (opens by default)
|
|
21
|
+
* --stop Stop all running servers
|
|
22
|
+
* --background Run in background (detached) - legacy mode
|
|
23
|
+
* --foreground Run in foreground (for CC background tasks)
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const fs = require('fs');
|
|
27
|
+
const path = require('path');
|
|
28
|
+
const os = require('os');
|
|
29
|
+
const { spawn, execSync } = require('child_process');
|
|
30
|
+
|
|
31
|
+
const { findAvailablePort, DEFAULT_PORT } = require('./lib/port-finder.cjs');
|
|
32
|
+
const { writePidFile, stopAllServers, setupShutdownHandlers, findRunningInstances } = require('./lib/process-mgr.cjs');
|
|
33
|
+
const { createHttpServer } = require('./lib/http-server.cjs');
|
|
34
|
+
const { renderMarkdownFile, renderTOCHtml } = require('./lib/markdown-renderer.cjs');
|
|
35
|
+
const { generateNavSidebar, generateNavFooter, detectPlan, getNavigationContext } = require('./lib/plan-navigator.cjs');
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Parse command line arguments
|
|
39
|
+
*/
|
|
40
|
+
function parseArgs(argv) {
|
|
41
|
+
const args = {
|
|
42
|
+
file: null,
|
|
43
|
+
dir: null,
|
|
44
|
+
port: DEFAULT_PORT,
|
|
45
|
+
host: 'localhost',
|
|
46
|
+
open: true, // Auto-open browser by default
|
|
47
|
+
stop: false,
|
|
48
|
+
background: false,
|
|
49
|
+
foreground: false,
|
|
50
|
+
isChild: false
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
for (let i = 2; i < argv.length; i++) {
|
|
54
|
+
const arg = argv[i];
|
|
55
|
+
if (arg === '--file' && argv[i + 1]) {
|
|
56
|
+
args.file = argv[++i];
|
|
57
|
+
} else if (arg === '--dir' && argv[i + 1]) {
|
|
58
|
+
args.dir = argv[++i];
|
|
59
|
+
} else if (arg === '--port' && argv[i + 1]) {
|
|
60
|
+
args.port = parseInt(argv[++i], 10);
|
|
61
|
+
} else if (arg === '--host' && argv[i + 1]) {
|
|
62
|
+
args.host = argv[++i];
|
|
63
|
+
} else if (arg === '--open') {
|
|
64
|
+
args.open = true;
|
|
65
|
+
} else if (arg === '--no-open') {
|
|
66
|
+
args.open = false;
|
|
67
|
+
} else if (arg === '--stop') {
|
|
68
|
+
args.stop = true;
|
|
69
|
+
} else if (arg === '--background') {
|
|
70
|
+
args.background = true;
|
|
71
|
+
} else if (arg === '--foreground') {
|
|
72
|
+
args.foreground = true;
|
|
73
|
+
} else if (arg === '--child') {
|
|
74
|
+
args.isChild = true;
|
|
75
|
+
} else if (!arg.startsWith('--') && !args.file && !args.dir) {
|
|
76
|
+
// Positional argument - could be file or directory
|
|
77
|
+
args.file = arg;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return args;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Resolve input path - simple logic, no smart detection
|
|
86
|
+
* @param {string} input - Input path
|
|
87
|
+
* @param {string} cwd - Current working directory
|
|
88
|
+
* @returns {{type: 'file'|'directory'|null, path: string|null}}
|
|
89
|
+
*/
|
|
90
|
+
function resolveInput(input, cwd) {
|
|
91
|
+
if (!input) return { type: null, path: null };
|
|
92
|
+
|
|
93
|
+
// Resolve relative to CWD
|
|
94
|
+
const resolved = path.isAbsolute(input) ? input : path.resolve(cwd, input);
|
|
95
|
+
|
|
96
|
+
if (!fs.existsSync(resolved)) {
|
|
97
|
+
return { type: null, path: null };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const stats = fs.statSync(resolved);
|
|
101
|
+
|
|
102
|
+
// File mode
|
|
103
|
+
if (stats.isFile()) {
|
|
104
|
+
return { type: 'file', path: resolved };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Directory mode - browse, no auto-detection of plan.md
|
|
108
|
+
if (stats.isDirectory()) {
|
|
109
|
+
return { type: 'directory', path: resolved };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return { type: null, path: null };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Open browser with URL
|
|
117
|
+
*/
|
|
118
|
+
function openBrowser(url) {
|
|
119
|
+
const platform = process.platform;
|
|
120
|
+
let cmd;
|
|
121
|
+
|
|
122
|
+
if (platform === 'darwin') {
|
|
123
|
+
cmd = `open "${url}"`;
|
|
124
|
+
} else if (platform === 'win32') {
|
|
125
|
+
// On Windows, start command treats first quoted arg as window title
|
|
126
|
+
// Use empty title "" before the URL to prevent this
|
|
127
|
+
cmd = `start "" "${url}"`;
|
|
128
|
+
} else {
|
|
129
|
+
cmd = `xdg-open "${url}"`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
execSync(cmd, { stdio: 'ignore' });
|
|
134
|
+
} catch {
|
|
135
|
+
// Ignore browser open errors
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generate full HTML page from markdown
|
|
141
|
+
*/
|
|
142
|
+
function generateFullPage(filePath, assetsDir) {
|
|
143
|
+
const { html, toc, frontmatter, title } = renderMarkdownFile(filePath);
|
|
144
|
+
const tocHtml = renderTOCHtml(toc);
|
|
145
|
+
const navSidebar = generateNavSidebar(filePath);
|
|
146
|
+
const navFooter = generateNavFooter(filePath);
|
|
147
|
+
const planInfo = detectPlan(filePath);
|
|
148
|
+
const navContext = getNavigationContext(filePath);
|
|
149
|
+
|
|
150
|
+
// Read template
|
|
151
|
+
const templatePath = path.join(assetsDir, 'template.html');
|
|
152
|
+
let template = fs.readFileSync(templatePath, 'utf8');
|
|
153
|
+
|
|
154
|
+
// Generate back button (links to parent directory browser)
|
|
155
|
+
const parentDir = path.dirname(filePath);
|
|
156
|
+
const backButton = `
|
|
157
|
+
<a href="/browse?dir=${encodeURIComponent(parentDir)}" class="icon-btn back-btn" title="Back to folder">
|
|
158
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
159
|
+
<path d="M19 12H5M12 19l-7-7 7-7"/>
|
|
160
|
+
</svg>
|
|
161
|
+
</a>`;
|
|
162
|
+
|
|
163
|
+
// Generate header nav (prev/next) for plan files
|
|
164
|
+
let headerNav = '';
|
|
165
|
+
if (navContext.prev || navContext.next) {
|
|
166
|
+
const prevBtn = navContext.prev && fs.existsSync(navContext.prev.file)
|
|
167
|
+
? `<a href="/view?file=${encodeURIComponent(navContext.prev.file)}" class="header-nav-btn prev" title="${navContext.prev.name}">
|
|
168
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>
|
|
169
|
+
<span>Prev</span>
|
|
170
|
+
</a>`
|
|
171
|
+
: '';
|
|
172
|
+
const nextBtn = navContext.next && fs.existsSync(navContext.next.file)
|
|
173
|
+
? `<a href="/view?file=${encodeURIComponent(navContext.next.file)}" class="header-nav-btn next" title="${navContext.next.name}">
|
|
174
|
+
<span>Next</span>
|
|
175
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
|
|
176
|
+
</a>`
|
|
177
|
+
: '';
|
|
178
|
+
headerNav = `<div class="header-nav">${prevBtn}${nextBtn}</div>`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Replace placeholders
|
|
182
|
+
template = template
|
|
183
|
+
.replace(/\{\{title\}\}/g, title)
|
|
184
|
+
.replace('{{toc}}', tocHtml)
|
|
185
|
+
.replace('{{nav-sidebar}}', navSidebar)
|
|
186
|
+
.replace('{{nav-footer}}', navFooter)
|
|
187
|
+
.replace('{{content}}', html)
|
|
188
|
+
.replace('{{has-plan}}', planInfo.isPlan ? 'has-plan' : '')
|
|
189
|
+
.replace('{{frontmatter}}', JSON.stringify(frontmatter || {}))
|
|
190
|
+
.replace('{{back-button}}', backButton)
|
|
191
|
+
.replace('{{header-nav}}', headerNav);
|
|
192
|
+
|
|
193
|
+
return template;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Get local network IP address for remote access
|
|
198
|
+
* @returns {string|null} - Local IP or null if not found
|
|
199
|
+
*/
|
|
200
|
+
function getLocalIP() {
|
|
201
|
+
const interfaces = os.networkInterfaces();
|
|
202
|
+
for (const name of Object.keys(interfaces)) {
|
|
203
|
+
for (const iface of interfaces[name]) {
|
|
204
|
+
// Skip internal (loopback) and non-IPv4 addresses
|
|
205
|
+
if (iface.family === 'IPv4' && !iface.internal) {
|
|
206
|
+
return iface.address;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Build URL with query parameters (fixes path conflicts)
|
|
215
|
+
* @returns {{url: string, networkUrl: string|null}} - Local and network URLs
|
|
216
|
+
*/
|
|
217
|
+
function buildUrl(host, port, type, filePath) {
|
|
218
|
+
const displayHost = host === '0.0.0.0' ? 'localhost' : host;
|
|
219
|
+
const baseUrl = `http://${displayHost}:${port}`;
|
|
220
|
+
|
|
221
|
+
let urlPath = '';
|
|
222
|
+
if (type === 'file') {
|
|
223
|
+
urlPath = `/view?file=${encodeURIComponent(filePath)}`;
|
|
224
|
+
} else if (type === 'directory') {
|
|
225
|
+
urlPath = `/browse?dir=${encodeURIComponent(filePath)}`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const url = baseUrl + urlPath;
|
|
229
|
+
|
|
230
|
+
// If binding to all interfaces, provide network URL for remote access
|
|
231
|
+
let networkUrl = null;
|
|
232
|
+
if (host === '0.0.0.0') {
|
|
233
|
+
const localIP = getLocalIP();
|
|
234
|
+
if (localIP) {
|
|
235
|
+
networkUrl = `http://${localIP}:${port}${urlPath}`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return { url, networkUrl };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Main function
|
|
244
|
+
*/
|
|
245
|
+
async function main() {
|
|
246
|
+
const args = parseArgs(process.argv);
|
|
247
|
+
const cwd = process.cwd();
|
|
248
|
+
const assetsDir = path.join(__dirname, '..', 'assets');
|
|
249
|
+
|
|
250
|
+
// Handle --stop
|
|
251
|
+
if (args.stop) {
|
|
252
|
+
const instances = findRunningInstances();
|
|
253
|
+
if (instances.length === 0) {
|
|
254
|
+
console.log('No server running to stop');
|
|
255
|
+
process.exit(0);
|
|
256
|
+
}
|
|
257
|
+
const stopped = stopAllServers();
|
|
258
|
+
console.log(`Stopped ${stopped} server(s)`);
|
|
259
|
+
process.exit(0);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Determine input
|
|
263
|
+
const input = args.dir || args.file;
|
|
264
|
+
|
|
265
|
+
// Validate input
|
|
266
|
+
if (!input) {
|
|
267
|
+
console.error('Error: --file or --dir argument required');
|
|
268
|
+
console.error('Usage:');
|
|
269
|
+
console.error(' node server.cjs --file <path.md> [--port 3456] [--open]');
|
|
270
|
+
console.error(' node server.cjs --dir <path> [--port 3456] [--open] # Browse directory');
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Resolve input path - simple logic
|
|
275
|
+
let resolved = resolveInput(input, cwd);
|
|
276
|
+
|
|
277
|
+
// If --dir was explicitly used, force directory mode
|
|
278
|
+
if (args.dir && resolved.type === null) {
|
|
279
|
+
const dirPath = path.isAbsolute(args.dir) ? args.dir : path.resolve(cwd, args.dir);
|
|
280
|
+
if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
|
|
281
|
+
resolved = { type: 'directory', path: dirPath };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (resolved.type === null) {
|
|
286
|
+
console.error(`Error: Invalid path: ${input}`);
|
|
287
|
+
console.error('Path must be a file or directory.');
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Background mode - spawn child and exit (legacy mode for manual runs)
|
|
292
|
+
// Skip if --foreground is set (for Claude Code background tasks)
|
|
293
|
+
if (args.background && !args.foreground && !args.isChild) {
|
|
294
|
+
const childArgs = ['--port', String(args.port), '--host', args.host, '--child'];
|
|
295
|
+
if (resolved.type === 'file') {
|
|
296
|
+
childArgs.unshift('--file', resolved.path);
|
|
297
|
+
} else {
|
|
298
|
+
childArgs.unshift('--dir', resolved.path);
|
|
299
|
+
}
|
|
300
|
+
if (args.open) childArgs.push('--open');
|
|
301
|
+
|
|
302
|
+
const child = spawn(process.execPath, [__filename, ...childArgs], {
|
|
303
|
+
detached: true,
|
|
304
|
+
stdio: 'ignore',
|
|
305
|
+
cwd: cwd
|
|
306
|
+
});
|
|
307
|
+
child.unref();
|
|
308
|
+
|
|
309
|
+
// Wait briefly for child to start
|
|
310
|
+
await new Promise(r => setTimeout(r, 500));
|
|
311
|
+
|
|
312
|
+
// Find the port the child is using
|
|
313
|
+
const instances = findRunningInstances();
|
|
314
|
+
const instance = instances.find(i => i.port >= args.port);
|
|
315
|
+
const port = instance ? instance.port : args.port;
|
|
316
|
+
|
|
317
|
+
const { url, networkUrl } = buildUrl(args.host, port, resolved.type, resolved.path);
|
|
318
|
+
|
|
319
|
+
const result = {
|
|
320
|
+
success: true,
|
|
321
|
+
url,
|
|
322
|
+
path: resolved.path,
|
|
323
|
+
port,
|
|
324
|
+
host: args.host,
|
|
325
|
+
mode: resolved.type
|
|
326
|
+
};
|
|
327
|
+
if (networkUrl) result.networkUrl = networkUrl;
|
|
328
|
+
|
|
329
|
+
console.log(JSON.stringify(result));
|
|
330
|
+
|
|
331
|
+
process.exit(0);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Find available port
|
|
335
|
+
const port = await findAvailablePort(args.port);
|
|
336
|
+
if (port !== args.port) {
|
|
337
|
+
console.error(`Port ${args.port} in use, using ${port}`);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Determine allowed directories for security
|
|
341
|
+
const allowedDirs = [assetsDir, cwd];
|
|
342
|
+
if (resolved.path) {
|
|
343
|
+
const targetDir = resolved.type === 'file' ? path.dirname(resolved.path) : resolved.path;
|
|
344
|
+
if (!allowedDirs.includes(targetDir)) {
|
|
345
|
+
allowedDirs.push(targetDir);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Create server
|
|
350
|
+
const server = createHttpServer({
|
|
351
|
+
assetsDir,
|
|
352
|
+
renderMarkdown: (fp) => generateFullPage(fp, assetsDir),
|
|
353
|
+
allowedDirs
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// Start server
|
|
357
|
+
server.listen(port, args.host, () => {
|
|
358
|
+
const { url, networkUrl } = buildUrl(args.host, port, resolved.type, resolved.path);
|
|
359
|
+
|
|
360
|
+
// Write PID file
|
|
361
|
+
writePidFile(port, process.pid);
|
|
362
|
+
|
|
363
|
+
// Setup shutdown handlers
|
|
364
|
+
setupShutdownHandlers(port, () => {
|
|
365
|
+
server.close();
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// Output for CLI/command integration
|
|
369
|
+
// In foreground mode (CC background task), always output JSON
|
|
370
|
+
if (args.foreground || args.isChild || process.env.CLAUDE_COMMAND) {
|
|
371
|
+
const result = {
|
|
372
|
+
success: true,
|
|
373
|
+
url,
|
|
374
|
+
path: resolved.path,
|
|
375
|
+
port,
|
|
376
|
+
host: args.host,
|
|
377
|
+
mode: resolved.type
|
|
378
|
+
};
|
|
379
|
+
if (networkUrl) result.networkUrl = networkUrl;
|
|
380
|
+
console.log(JSON.stringify(result));
|
|
381
|
+
} else {
|
|
382
|
+
console.log(`\nMarkdown Novel Viewer`);
|
|
383
|
+
console.log(`${'─'.repeat(40)}`);
|
|
384
|
+
console.log(`URL: ${url}`);
|
|
385
|
+
if (networkUrl) {
|
|
386
|
+
console.log(`Network: ${networkUrl}`);
|
|
387
|
+
}
|
|
388
|
+
console.log(`Path: ${resolved.path}`);
|
|
389
|
+
console.log(`Port: ${port}`);
|
|
390
|
+
console.log(`Host: ${args.host}`);
|
|
391
|
+
console.log(`Mode: ${resolved.type === 'file' ? 'File Viewer' : 'Directory Browser'}`);
|
|
392
|
+
console.log(`\nPress Ctrl+C to stop\n`);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Open browser
|
|
396
|
+
if (args.open) {
|
|
397
|
+
openBrowser(url);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
server.on('error', (err) => {
|
|
402
|
+
console.error(`Server error: ${err.message}`);
|
|
403
|
+
process.exit(1);
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Run
|
|
408
|
+
main().catch(err => {
|
|
409
|
+
console.error(`Error: ${err.message}`);
|
|
410
|
+
process.exit(1);
|
|
411
|
+
});
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tests for markdown-novel-viewer
|
|
5
|
+
* Run: node scripts/tests/server.test.cjs
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const http = require('http');
|
|
11
|
+
|
|
12
|
+
const { isPortAvailable, findAvailablePort, DEFAULT_PORT } = require('../lib/port-finder.cjs');
|
|
13
|
+
const { writePidFile, readPidFile, removePidFile, findRunningInstances } = require('../lib/process-mgr.cjs');
|
|
14
|
+
const { getMimeType, MIME_TYPES, isPathSafe, sanitizeErrorMessage } = require('../lib/http-server.cjs');
|
|
15
|
+
const { resolveImages, addHeadingIds, generateTOC, renderTOCHtml } = require('../lib/markdown-renderer.cjs');
|
|
16
|
+
const { detectPlan, parsePlanTable, getNavigationContext, generateNavSidebar } = require('../lib/plan-navigator.cjs');
|
|
17
|
+
|
|
18
|
+
// Test utilities
|
|
19
|
+
let passed = 0;
|
|
20
|
+
let failed = 0;
|
|
21
|
+
|
|
22
|
+
function test(name, fn) {
|
|
23
|
+
try {
|
|
24
|
+
fn();
|
|
25
|
+
passed++;
|
|
26
|
+
console.log(` ✓ ${name}`);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
failed++;
|
|
29
|
+
console.log(` ✗ ${name}`);
|
|
30
|
+
console.log(` Error: ${err.message}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function assertEqual(actual, expected, message) {
|
|
35
|
+
if (actual !== expected) {
|
|
36
|
+
throw new Error(`${message}: expected "${expected}", got "${actual}"`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function assertTrue(value, message) {
|
|
41
|
+
if (!value) {
|
|
42
|
+
throw new Error(`${message}: expected truthy value`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function assertFalse(value, message) {
|
|
47
|
+
if (value) {
|
|
48
|
+
throw new Error(`${message}: expected falsy value`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function assertIncludes(str, substr, message) {
|
|
53
|
+
if (!str.includes(substr)) {
|
|
54
|
+
throw new Error(`${message}: expected to include "${substr}"`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Test suites
|
|
59
|
+
console.log('\n--- Port Finder Tests ---');
|
|
60
|
+
|
|
61
|
+
test('DEFAULT_PORT is 3456', () => {
|
|
62
|
+
assertEqual(DEFAULT_PORT, 3456, 'Default port');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('isPortAvailable returns boolean', () => {
|
|
66
|
+
// Sync test - function exists
|
|
67
|
+
assertTrue(typeof isPortAvailable === 'function', 'Should be function');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('findAvailablePort returns number', () => {
|
|
71
|
+
// Sync test - actual async behavior tested in integration
|
|
72
|
+
assertTrue(typeof findAvailablePort === 'function', 'Should be function');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
console.log('\n--- Process Manager Tests ---');
|
|
76
|
+
|
|
77
|
+
test('writePidFile and readPidFile work correctly', () => {
|
|
78
|
+
const testPort = 9876;
|
|
79
|
+
const testPid = 12345;
|
|
80
|
+
|
|
81
|
+
writePidFile(testPort, testPid);
|
|
82
|
+
const readPid = readPidFile(testPort);
|
|
83
|
+
assertEqual(readPid, testPid, 'PID should match');
|
|
84
|
+
|
|
85
|
+
removePidFile(testPort);
|
|
86
|
+
const afterRemove = readPidFile(testPort);
|
|
87
|
+
assertEqual(afterRemove, null, 'Should be null after remove');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('findRunningInstances returns array', () => {
|
|
91
|
+
const instances = findRunningInstances();
|
|
92
|
+
assertTrue(Array.isArray(instances), 'Should return array');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
console.log('\n--- HTTP Server Tests ---');
|
|
96
|
+
|
|
97
|
+
test('getMimeType returns correct types', () => {
|
|
98
|
+
assertEqual(getMimeType('test.html'), 'text/html', 'HTML type');
|
|
99
|
+
assertEqual(getMimeType('test.css'), 'text/css', 'CSS type');
|
|
100
|
+
assertEqual(getMimeType('test.js'), 'application/javascript', 'JS type');
|
|
101
|
+
assertEqual(getMimeType('test.png'), 'image/png', 'PNG type');
|
|
102
|
+
assertEqual(getMimeType('test.jpg'), 'image/jpeg', 'JPG type');
|
|
103
|
+
assertEqual(getMimeType('test.unknown'), 'application/octet-stream', 'Unknown type');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('MIME_TYPES has common extensions', () => {
|
|
107
|
+
assertTrue(MIME_TYPES['.html'], 'Has .html');
|
|
108
|
+
assertTrue(MIME_TYPES['.css'], 'Has .css');
|
|
109
|
+
assertTrue(MIME_TYPES['.js'], 'Has .js');
|
|
110
|
+
assertTrue(MIME_TYPES['.png'], 'Has .png');
|
|
111
|
+
assertTrue(MIME_TYPES['.md'], 'Has .md');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
console.log('\n--- Security Tests ---');
|
|
115
|
+
|
|
116
|
+
test('isPathSafe blocks path traversal', () => {
|
|
117
|
+
assertFalse(isPathSafe('/etc/../etc/passwd', ['/home']), 'Should block .. traversal');
|
|
118
|
+
assertFalse(isPathSafe('/path\0/file', ['/path']), 'Should block null bytes');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('isPathSafe allows valid paths', () => {
|
|
122
|
+
assertTrue(isPathSafe('/tmp/test.md', ['/tmp']), 'Should allow path in allowed dir');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('sanitizeErrorMessage removes paths', () => {
|
|
126
|
+
const sanitized = sanitizeErrorMessage('Error: /etc/passwd not found');
|
|
127
|
+
assertFalse(sanitized.includes('/etc/passwd'), 'Should not contain path');
|
|
128
|
+
assertIncludes(sanitized, '[path]', 'Should replace with placeholder');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
console.log('\n--- Markdown Renderer Tests ---');
|
|
132
|
+
|
|
133
|
+
test('resolveImages converts relative paths', () => {
|
|
134
|
+
const md = '';
|
|
135
|
+
const resolved = resolveImages(md, '/base/path');
|
|
136
|
+
assertIncludes(resolved, '/file/', 'Should include /file/ route');
|
|
137
|
+
assertIncludes(resolved, '/base/path', 'Should include base path');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('resolveImages preserves absolute URLs', () => {
|
|
141
|
+
const md = '';
|
|
142
|
+
const resolved = resolveImages(md, '/base/path');
|
|
143
|
+
assertEqual(resolved, md, 'Should preserve absolute URL');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('resolveImages handles reference-style definitions', () => {
|
|
147
|
+
const md = '![Step 1 Initial]\n\n[Step 1 Initial]: ./screenshots/step1.png';
|
|
148
|
+
const resolved = resolveImages(md, '/base/path');
|
|
149
|
+
assertIncludes(resolved, '/file/', 'Should include /file/ route in ref definition');
|
|
150
|
+
assertIncludes(resolved, '/base/path/screenshots/step1.png', 'Should resolve relative path');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('resolveImages handles reference-style with titles', () => {
|
|
154
|
+
const md = '[logo]: ./images/logo.png "Company Logo"';
|
|
155
|
+
const resolved = resolveImages(md, '/project');
|
|
156
|
+
assertIncludes(resolved, '/file/project/images/logo.png', 'Should resolve path with title');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('resolveImages handles inline images with titles', () => {
|
|
160
|
+
const md = '';
|
|
161
|
+
const resolved = resolveImages(md, '/base');
|
|
162
|
+
assertIncludes(resolved, '/file/base/image.png', 'Should resolve inline with title');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test('addHeadingIds adds id attributes', () => {
|
|
166
|
+
const html = '<h1>Test Heading</h1><h2>Another</h2>';
|
|
167
|
+
const withIds = addHeadingIds(html);
|
|
168
|
+
assertIncludes(withIds, 'id="test-heading"', 'Should add id to h1');
|
|
169
|
+
assertIncludes(withIds, 'id="another"', 'Should add id to h2');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('addHeadingIds handles duplicates', () => {
|
|
173
|
+
const html = '<h1>Test</h1><h2>Test</h2>';
|
|
174
|
+
const withIds = addHeadingIds(html);
|
|
175
|
+
assertIncludes(withIds, 'id="test"', 'Should have first id');
|
|
176
|
+
assertIncludes(withIds, 'id="test-1"', 'Should have unique second id');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('generateTOC extracts headings', () => {
|
|
180
|
+
const html = '<h1 id="one">One</h1><h2 id="two">Two</h2><h3 id="three">Three</h3>';
|
|
181
|
+
const toc = generateTOC(html);
|
|
182
|
+
assertEqual(toc.length, 3, 'Should find 3 headings');
|
|
183
|
+
assertEqual(toc[0].level, 1, 'First should be h1');
|
|
184
|
+
assertEqual(toc[0].id, 'one', 'First id should be "one"');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('renderTOCHtml generates list', () => {
|
|
188
|
+
const toc = [{ level: 1, id: 'test', text: 'Test' }];
|
|
189
|
+
const html = renderTOCHtml(toc);
|
|
190
|
+
assertIncludes(html, '<ul', 'Should have ul');
|
|
191
|
+
assertIncludes(html, 'href="#test"', 'Should have anchor');
|
|
192
|
+
assertIncludes(html, 'Test', 'Should have text');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('renderTOCHtml handles empty array', () => {
|
|
196
|
+
const html = renderTOCHtml([]);
|
|
197
|
+
assertEqual(html, '', 'Should return empty string');
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
console.log('\n--- Plan Navigator Tests ---');
|
|
201
|
+
|
|
202
|
+
// Create temp plan structure for testing
|
|
203
|
+
const testPlanDir = '/tmp/test-novel-viewer-plan';
|
|
204
|
+
const testPlanFile = path.join(testPlanDir, 'plan.md');
|
|
205
|
+
const testPhaseFile = path.join(testPlanDir, 'phase-01-test.md');
|
|
206
|
+
|
|
207
|
+
function setupTestPlan() {
|
|
208
|
+
if (!fs.existsSync(testPlanDir)) {
|
|
209
|
+
fs.mkdirSync(testPlanDir, { recursive: true });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
fs.writeFileSync(testPlanFile, `# Test Plan
|
|
213
|
+
|
|
214
|
+
| Phase | Name | Status | Link |
|
|
215
|
+
|-------|------|--------|------|
|
|
216
|
+
| 1 | Test Phase | Pending | [phase-01-test.md](./phase-01-test.md) |
|
|
217
|
+
`);
|
|
218
|
+
|
|
219
|
+
fs.writeFileSync(testPhaseFile, `# Phase 1: Test Phase
|
|
220
|
+
|
|
221
|
+
Content here.
|
|
222
|
+
`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function cleanupTestPlan() {
|
|
226
|
+
if (fs.existsSync(testPlanDir)) {
|
|
227
|
+
fs.rmSync(testPlanDir, { recursive: true });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
setupTestPlan();
|
|
232
|
+
|
|
233
|
+
test('detectPlan identifies plan directory', () => {
|
|
234
|
+
const result = detectPlan(testPlanFile);
|
|
235
|
+
assertTrue(result.isPlan, 'Should detect as plan');
|
|
236
|
+
assertEqual(result.planDir, testPlanDir, 'Should have correct dir');
|
|
237
|
+
assertTrue(result.phases.length >= 1, 'Should find phases');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('detectPlan returns false for non-plan', () => {
|
|
241
|
+
const result = detectPlan('/tmp/random-file.md');
|
|
242
|
+
assertFalse(result.isPlan, 'Should not be plan');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test('parsePlanTable extracts phases', () => {
|
|
246
|
+
const phases = parsePlanTable(testPlanFile);
|
|
247
|
+
assertTrue(phases.length >= 1, 'Should find phases');
|
|
248
|
+
assertEqual(phases[0].phase, 1, 'First phase number');
|
|
249
|
+
assertEqual(phases[0].name, 'Test Phase', 'Phase name');
|
|
250
|
+
assertEqual(phases[0].status, 'pending', 'Status should be lowercase');
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test('getNavigationContext returns correct structure', () => {
|
|
254
|
+
const ctx = getNavigationContext(testPlanFile);
|
|
255
|
+
assertTrue(ctx.planInfo.isPlan, 'Should be plan');
|
|
256
|
+
assertTrue(ctx.allPhases.length >= 1, 'Should have phases');
|
|
257
|
+
assertEqual(ctx.currentIndex, 0, 'Plan.md should be index 0');
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test('generateNavSidebar returns HTML', () => {
|
|
261
|
+
const html = generateNavSidebar(testPlanFile);
|
|
262
|
+
assertIncludes(html, '<nav', 'Should have nav element');
|
|
263
|
+
assertIncludes(html, 'phase-list', 'Should have phase list');
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test('generateNavSidebar returns empty for non-plan', () => {
|
|
267
|
+
const html = generateNavSidebar('/tmp/random.md');
|
|
268
|
+
assertEqual(html, '', 'Should return empty string');
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
cleanupTestPlan();
|
|
272
|
+
|
|
273
|
+
// Summary
|
|
274
|
+
console.log('\n--- Test Results ---');
|
|
275
|
+
console.log(`Passed: ${passed}`);
|
|
276
|
+
console.log(`Failed: ${failed}`);
|
|
277
|
+
console.log(`Total: ${passed + failed}`);
|
|
278
|
+
|
|
279
|
+
if (failed > 0) {
|
|
280
|
+
process.exit(1);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
console.log('\nAll tests passed!');
|