thanh-kit 2.5.0 → 2.5.2
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/dist/index.js +20 -61
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/.ck.json +60 -0
- package/templates/.ckignore +27 -0
- package/templates/.mcp.json.example +23 -0
- package/templates/agents/brainstormer.md +18 -2
- package/templates/agents/code-reviewer.md +122 -142
- package/templates/agents/code-simplifier.md +50 -100
- package/templates/agents/debugger.md +27 -11
- package/templates/agents/docs-manager.md +100 -38
- package/templates/agents/fullstack-developer.md +15 -3
- package/templates/agents/git-manager.md +11 -386
- package/templates/agents/journal-writer.md +13 -8
- package/templates/agents/mcp-manager.md +21 -6
- package/templates/agents/planner.md +24 -8
- package/templates/agents/project-manager.md +17 -121
- package/templates/agents/researcher.md +22 -7
- package/templates/agents/tester.md +23 -7
- package/templates/agents/ui-ux-designer.md +23 -14
- package/templates/{commands → command-archive}/ask.md +5 -5
- package/templates/{commands → command-archive}/ck-help.md +18 -2
- package/templates/command-archive/docs/init.md +38 -0
- package/templates/command-archive/docs/summarize.md +22 -0
- package/templates/command-archive/docs/update.md +76 -0
- package/templates/command-archive/journal.md +18 -0
- package/templates/{commands → command-archive}/kanban.md +5 -7
- package/templates/{commands → command-archive}/plan/archive.md +2 -2
- package/templates/command-archive/plan/red-team.md +200 -0
- package/templates/command-archive/plan/validate.md +188 -0
- package/templates/command-archive/preview.md +283 -0
- package/templates/command-archive/review/codebase/parallel.md +122 -0
- package/templates/{commands → command-archive}/test/ui.md +3 -3
- package/templates/{commands → command-archive}/use-mcp.md +6 -2
- package/templates/command-archive/worktree.md +109 -0
- package/templates/hooks/__tests__/ck-config-utils.test.cjs +557 -0
- package/templates/hooks/__tests__/descriptive-name.test.cjs +292 -0
- package/templates/hooks/__tests__/dev-rules-reminder.test.cjs +336 -0
- package/templates/hooks/__tests__/integration/path-resolution.test.cjs +319 -0
- package/templates/hooks/__tests__/privacy-block.test.cjs +273 -0
- package/templates/hooks/__tests__/session-init.test.cjs +308 -0
- package/templates/hooks/__tests__/skill-dedup.test.cjs +527 -0
- package/templates/hooks/__tests__/subagent-init.test.cjs +622 -0
- package/templates/hooks/__tests__/task-completed-handler.test.cjs +246 -0
- package/templates/hooks/__tests__/team-context-inject.test.cjs +804 -0
- package/templates/hooks/__tests__/teammate-idle-handler.test.cjs +254 -0
- package/templates/hooks/cook-after-plan-reminder.cjs +72 -0
- package/templates/hooks/descriptive-name.cjs +47 -0
- package/templates/hooks/dev-rules-reminder.cjs +37 -214
- package/templates/hooks/lib/__tests__/README.md +240 -0
- package/templates/hooks/lib/__tests__/ck-config-utils.test.cjs +613 -1
- package/templates/hooks/lib/__tests__/context-builder.test.cjs +468 -0
- package/templates/hooks/lib/__tests__/project-detector.test.cjs +754 -0
- package/templates/hooks/lib/__tests__/statusline-integration.test.cjs +678 -0
- package/templates/hooks/lib/__tests__/statusline.test.cjs +689 -0
- package/templates/hooks/lib/ck-config-utils.cjs +146 -21
- package/templates/hooks/lib/colors.cjs +95 -0
- package/templates/hooks/lib/config-counter.cjs +103 -0
- package/templates/hooks/lib/context-builder.cjs +616 -0
- package/templates/hooks/lib/git-info-cache.cjs +143 -0
- package/templates/hooks/lib/hook-logger.cjs +92 -0
- package/templates/hooks/lib/privacy-checker.cjs +297 -0
- package/templates/hooks/lib/project-detector.cjs +474 -0
- package/templates/hooks/lib/scout-checker.cjs +263 -0
- package/templates/hooks/lib/transcript-parser.cjs +181 -0
- package/templates/hooks/notifications/discord_notify.sh +17 -4
- package/templates/hooks/notifications/docs/discord-hook-setup.md +26 -10
- package/templates/hooks/notifications/docs/telegram-hook-setup.md +24 -6
- package/templates/hooks/notifications/notify.cjs +0 -0
- package/templates/hooks/notifications/send-discord.sh +0 -0
- package/templates/hooks/notifications/telegram_notify.sh +17 -4
- package/templates/hooks/post-edit-simplify-reminder.cjs +156 -0
- package/templates/hooks/privacy-block.cjs +97 -188
- package/templates/hooks/scout-block/broad-pattern-detector.cjs +4 -6
- package/templates/hooks/scout-block/error-formatter.cjs +0 -0
- package/templates/hooks/scout-block/path-extractor.cjs +102 -13
- package/templates/hooks/scout-block/pattern-matcher.cjs +16 -1
- package/templates/hooks/scout-block/tests/{test-broad-pattern-detector.js → test-broad-pattern-detector.cjs} +1 -61
- package/templates/hooks/scout-block/tests/{test-build-command-allowlist.js → test-build-command-allowlist.cjs} +1 -1
- package/templates/hooks/scout-block/tests/{test-error-formatter.js → test-error-formatter.cjs} +1 -1
- package/templates/hooks/scout-block/tests/{test-full-flow-edge-cases.js → test-full-flow-edge-cases.cjs} +1 -1
- package/templates/hooks/scout-block/tests/{test-monorepo-scenarios.js → test-monorepo-scenarios.cjs} +1 -1
- package/templates/hooks/scout-block/tests/{test-path-extractor.js → test-path-extractor.cjs} +1 -1
- package/templates/hooks/scout-block/tests/{test-pattern-matcher.js → test-pattern-matcher.cjs} +1 -1
- package/templates/hooks/scout-block.cjs +100 -87
- package/templates/hooks/session-init.cjs +197 -330
- package/templates/hooks/skill-dedup.cjs +268 -0
- package/templates/hooks/subagent-init.cjs +75 -22
- package/templates/hooks/task-completed-handler.cjs +118 -0
- package/templates/hooks/team-context-inject.cjs +176 -0
- package/templates/hooks/teammate-idle-handler.cjs +121 -0
- package/templates/hooks/tests/scout-block/broad-pattern-detector.test.cjs +231 -0
- package/templates/hooks/tests/scout-block/fixtures/ckignore-custom.txt +6 -0
- package/templates/hooks/tests/scout-block/fixtures/ckignore-default.txt +13 -0
- package/templates/hooks/tests/scout-block/fixtures/ckignore-negation.txt +8 -0
- package/templates/hooks/tests/scout-block/path-extractor.test.cjs +527 -0
- package/templates/hooks/tests/scout-block/pattern-matcher.test.cjs +293 -0
- package/templates/hooks/tests/scout-block/scout-checker.test.cjs +741 -0
- package/templates/hooks/tests/{test-ckignore.js → test-ckignore.cjs} +0 -0
- package/templates/hooks/tests/{test-modularization-hook.js → test-modularization-hook.cjs} +0 -0
- package/templates/hooks/tests/{test-privacy-block.js → test-privacy-block.cjs} +1 -1
- package/templates/hooks/tests/test-scout-block.cjs +315 -0
- package/templates/hooks/usage-context-awareness.cjs +179 -0
- package/templates/metadata.json +104 -0
- package/templates/{workflows → rules}/development-rules.md +12 -53
- package/templates/rules/orchestration-protocol.md +43 -0
- package/templates/{workflows → rules}/primary-workflow.md +16 -4
- package/templates/rules/team-coordination-rules.md +90 -0
- package/templates/schemas/ck-config.schema.json +381 -0
- package/templates/scripts/README.md +94 -198
- package/templates/scripts/ck-help.py +19 -855
- package/templates/scripts/commands_data.yaml +3 -621
- package/templates/scripts/fix-shebang-permissions.sh +50 -0
- package/templates/scripts/generate_catalogs.py +37 -8
- package/templates/scripts/resolve_env.py +0 -0
- package/templates/scripts/scan_commands.py +14 -96
- package/templates/scripts/scan_skills.py +59 -19
- package/templates/scripts/set-active-plan.cjs +8 -3
- package/templates/scripts/skills_data.yaml +2 -596
- package/templates/scripts/test-ck-help.py +15 -0
- package/templates/scripts/test_ck_help.py +139 -0
- package/templates/scripts/test_ck_help_integration.py +72 -0
- package/templates/scripts/validate-docs.cjs +342 -0
- package/templates/scripts/win_compat.py +0 -0
- package/templates/scripts/worktree.cjs +4 -652
- package/templates/scripts/worktree.test.cjs +5 -330
- package/templates/settings.json +104 -239
- package/templates/skills/README.md +95 -255
- package/templates/skills/THIRD_PARTY_NOTICES.md +405 -0
- package/templates/skills/agent-browser/SKILL.md +294 -0
- package/templates/skills/agent-browser/references/.gitkeep +0 -0
- package/templates/skills/agent-browser/references/agent-browser-vs-chrome-devtools.md +112 -0
- package/templates/skills/agent-browser/references/browserbase-cloud-setup.md +161 -0
- package/templates/skills/ai-artist/SKILL.md +103 -56
- package/templates/skills/ai-artist/data/awesome-prompts.csv +3592 -0
- package/templates/skills/ai-artist/data/lighting.csv +19 -0
- package/templates/skills/ai-artist/data/nano-banana-templates.csv +17 -0
- package/templates/skills/ai-artist/data/platforms.csv +11 -0
- package/templates/skills/ai-artist/data/styles.csv +26 -0
- package/templates/skills/ai-artist/data/techniques.csv +19 -0
- package/templates/skills/ai-artist/data/use-cases.csv +16 -0
- package/templates/skills/ai-artist/references/awesome-nano-banana-pro-prompts.md +8575 -0
- package/templates/skills/ai-artist/references/nano-banana.md +78 -1
- package/templates/skills/ai-artist/references/validation-workflow.md +117 -0
- package/templates/skills/ai-artist/scripts/core.py +197 -0
- package/templates/skills/ai-artist/scripts/extract_prompts.py +102 -0
- package/templates/skills/ai-artist/scripts/generate.py +370 -0
- package/templates/skills/ai-artist/scripts/search.py +147 -0
- package/templates/skills/ai-multimodal/SKILL.md +4 -3
- package/templates/skills/ai-multimodal/scripts/check_setup.py +12 -2
- package/templates/skills/ai-multimodal/scripts/document_converter.py +0 -0
- package/templates/skills/ai-multimodal/scripts/gemini_batch_process.py +3 -2
- package/templates/skills/ai-multimodal/scripts/media_optimizer.py +0 -0
- package/templates/skills/ask/SKILL.md +58 -0
- package/templates/skills/backend-development/SKILL.md +3 -2
- package/templates/skills/better-auth/SKILL.md +3 -2
- package/templates/skills/better-auth/scripts/better_auth_init.py +3 -3
- package/templates/skills/bootstrap/SKILL.md +101 -0
- package/templates/skills/bootstrap/references/shared-phases.md +59 -0
- package/templates/skills/bootstrap/references/workflow-auto.md +52 -0
- package/templates/skills/bootstrap/references/workflow-fast.md +50 -0
- package/templates/skills/bootstrap/references/workflow-full.md +60 -0
- package/templates/skills/bootstrap/references/workflow-parallel.md +59 -0
- package/templates/{commands/brainstorm.md → skills/brainstorm/SKILL.md} +21 -18
- package/templates/skills/chrome-devtools/SKILL.md +221 -68
- package/templates/skills/chrome-devtools/scripts/README.md +18 -0
- package/templates/skills/chrome-devtools/scripts/__tests__/error-handling.test.js +102 -0
- package/templates/skills/chrome-devtools/scripts/aria-snapshot.js +2 -1
- package/templates/skills/chrome-devtools/scripts/click.js +2 -1
- package/templates/skills/chrome-devtools/scripts/connect-chrome.js +146 -0
- package/templates/skills/chrome-devtools/scripts/console.js +3 -1
- package/templates/skills/chrome-devtools/scripts/evaluate.js +6 -3
- package/templates/skills/chrome-devtools/scripts/fill.js +2 -1
- package/templates/skills/chrome-devtools/scripts/import-cookies.js +205 -0
- package/templates/skills/chrome-devtools/scripts/inject-auth.js +2 -1
- package/templates/skills/chrome-devtools/scripts/install-deps.sh +0 -0
- package/templates/skills/chrome-devtools/scripts/install.sh +0 -0
- package/templates/skills/chrome-devtools/scripts/lib/browser.js +60 -4
- package/templates/skills/chrome-devtools/scripts/navigate.js +86 -2
- package/templates/skills/chrome-devtools/scripts/network.js +3 -1
- package/templates/skills/chrome-devtools/scripts/performance.js +3 -1
- package/templates/skills/chrome-devtools/scripts/screenshot.js +2 -1
- package/templates/skills/chrome-devtools/scripts/select-ref.js +2 -1
- package/templates/skills/chrome-devtools/scripts/snapshot.js +2 -1
- package/templates/skills/chrome-devtools/scripts/ws-debug.js +44 -0
- package/templates/skills/chrome-devtools/scripts/ws-full-debug.js +107 -0
- package/templates/skills/ck-help/SKILL.md +102 -0
- package/templates/skills/ck-help/scripts/ck-help.py +1321 -0
- package/templates/skills/ck-help/scripts/commands_data.yaml +3 -0
- package/templates/skills/ck-help/scripts/skills_data.yaml +593 -0
- package/templates/skills/code-review/SKILL.md +97 -93
- package/templates/skills/code-review/references/code-review-reception.md +113 -209
- package/templates/skills/code-review/references/codebase-scan-workflow.md +29 -0
- package/templates/skills/code-review/references/edge-case-scouting.md +119 -0
- package/templates/skills/code-review/references/parallel-review-workflow.md +69 -0
- package/templates/skills/code-review/references/requesting-code-review.md +115 -104
- package/templates/skills/code-review/references/task-management-reviews.md +140 -0
- package/templates/skills/code-review/references/verification-before-completion.md +138 -138
- package/templates/skills/coding-level/SKILL.md +56 -0
- package/templates/skills/common/README.md +120 -0
- package/templates/skills/common/api_key_helper.py +411 -0
- package/templates/skills/common/api_key_rotator.py +248 -0
- package/templates/skills/context-engineering/SKILL.md +108 -0
- package/templates/skills/context-engineering/references/context-compression.md +84 -0
- package/templates/skills/context-engineering/references/context-degradation.md +93 -0
- package/templates/skills/context-engineering/references/context-fundamentals.md +75 -0
- package/templates/skills/context-engineering/references/context-optimization.md +82 -0
- package/templates/skills/context-engineering/references/evaluation.md +89 -0
- package/templates/skills/context-engineering/references/memory-systems.md +88 -0
- package/templates/skills/context-engineering/references/multi-agent-patterns.md +90 -0
- package/templates/skills/context-engineering/references/project-development.md +97 -0
- package/templates/skills/context-engineering/references/runtime-awareness.md +202 -0
- package/templates/skills/context-engineering/references/tool-design.md +86 -0
- package/templates/skills/context-engineering/scripts/compression_evaluator.py +349 -0
- package/templates/skills/context-engineering/scripts/context_analyzer.py +317 -0
- package/templates/skills/context-engineering/scripts/tests/test_edge_cases.py +246 -0
- package/templates/skills/cook/README.md +86 -0
- package/templates/skills/cook/SKILL.md +113 -0
- package/templates/skills/cook/references/intent-detection.md +101 -0
- package/templates/skills/cook/references/review-cycle.md +75 -0
- package/templates/skills/cook/references/subagent-patterns.md +75 -0
- package/templates/skills/cook/references/workflow-steps.md +172 -0
- package/templates/skills/copywriting/SKILL.md +94 -0
- package/templates/skills/copywriting/references/copy-formulas.md +150 -0
- package/templates/skills/copywriting/references/cta-patterns.md +168 -0
- package/templates/skills/copywriting/references/email-copy.md +193 -0
- package/templates/skills/copywriting/references/headline-templates.md +140 -0
- package/templates/skills/copywriting/references/landing-page-copy.md +175 -0
- package/templates/skills/copywriting/references/power-words.md +189 -0
- package/templates/skills/copywriting/references/social-media-copy.md +222 -0
- package/templates/skills/copywriting/references/workflow-cro.md +83 -0
- package/templates/skills/copywriting/references/workflow-enhance.md +32 -0
- package/templates/skills/copywriting/references/workflow-fast.md +29 -0
- package/templates/skills/copywriting/references/workflow-good.md +39 -0
- package/templates/skills/copywriting/references/writing-styles.md +247 -0
- package/templates/skills/copywriting/scripts/extract-writing-styles.py +308 -0
- package/templates/skills/copywriting/templates/copy-brief.md +49 -0
- package/templates/skills/databases/SKILL.md +7 -155
- package/templates/skills/databases/analytics.md +198 -0
- package/templates/skills/databases/db-design.md +188 -0
- package/templates/skills/databases/incremental-etl.md +213 -0
- package/templates/skills/databases/scripts/db_backup.py +0 -0
- package/templates/skills/databases/scripts/db_migrate.py +3 -2
- package/templates/skills/databases/scripts/db_performance_check.py +3 -2
- package/templates/skills/databases/stacks/bigquery.md +231 -0
- package/templates/skills/databases/stacks/d1_cloudflare.md +137 -0
- package/templates/skills/databases/stacks/mysql.md +216 -0
- package/templates/skills/databases/stacks/postgres.md +235 -0
- package/templates/skills/databases/stacks/sqlite.md +244 -0
- package/templates/skills/databases/transactional.md +176 -0
- package/templates/skills/debug/SKILL.md +121 -0
- package/templates/skills/debug/references/frontend-verification.md +103 -0
- package/templates/skills/debug/references/investigation-methodology.md +101 -0
- package/templates/skills/debug/references/log-and-ci-analysis.md +97 -0
- package/templates/skills/debug/references/performance-diagnostics.md +113 -0
- package/templates/skills/debug/references/reporting-standards.md +122 -0
- package/templates/skills/debug/references/task-management-debugging.md +155 -0
- package/templates/skills/devops/SKILL.md +65 -253
- package/templates/skills/devops/references/kubernetes-basics.md +99 -0
- package/templates/skills/devops/references/kubernetes-helm-advanced.md +75 -0
- package/templates/skills/devops/references/kubernetes-helm.md +81 -0
- package/templates/skills/devops/references/kubernetes-kubectl.md +74 -0
- package/templates/skills/devops/references/kubernetes-security-advanced.md +98 -0
- package/templates/skills/devops/references/kubernetes-security.md +95 -0
- package/templates/skills/devops/references/kubernetes-troubleshooting-advanced.md +74 -0
- package/templates/skills/devops/references/kubernetes-troubleshooting.md +49 -0
- package/templates/skills/devops/references/kubernetes-workflows-advanced.md +75 -0
- package/templates/skills/devops/references/kubernetes-workflows.md +78 -0
- package/templates/skills/devops/scripts/cloudflare_deploy.py +0 -0
- package/templates/skills/devops/scripts/docker_optimize.py +3 -2
- package/templates/skills/docs/SKILL.md +55 -0
- package/templates/skills/docs/references/init-workflow.md +32 -0
- package/templates/skills/docs/references/summarize-workflow.md +18 -0
- package/templates/skills/docs/references/update-workflow.md +59 -0
- package/templates/skills/docs-seeker/SKILL.md +3 -2
- package/templates/skills/docs-seeker/scripts/analyze-llms-txt.js +0 -0
- package/templates/skills/docs-seeker/scripts/detect-topic.js +0 -0
- package/templates/skills/docs-seeker/scripts/fetch-docs.js +0 -0
- package/templates/skills/docs-seeker/scripts/tests/run-tests.js +0 -0
- package/templates/skills/docs-seeker/scripts/tests/test-analyze-llms.js +0 -0
- package/templates/skills/docs-seeker/scripts/tests/test-detect-topic.js +0 -0
- package/templates/skills/docs-seeker/scripts/tests/test-fetch-docs.js +0 -0
- package/templates/skills/docs-seeker/scripts/utils/env-loader.js +0 -0
- package/templates/skills/document-skills/docx/SKILL.md +2 -2
- package/templates/skills/document-skills/docx/ooxml/scripts/pack.py +0 -0
- package/templates/skills/document-skills/docx/ooxml/scripts/unpack.py +0 -0
- package/templates/skills/document-skills/docx/ooxml/scripts/validate.py +0 -0
- package/templates/skills/document-skills/docx/scripts/document.py +0 -0
- package/templates/skills/document-skills/docx/scripts/utilities.py +0 -0
- package/templates/skills/document-skills/pdf/SKILL.md +2 -2
- package/templates/skills/document-skills/pptx/SKILL.md +2 -2
- package/templates/skills/document-skills/pptx/ooxml/scripts/pack.py +0 -0
- package/templates/skills/document-skills/pptx/ooxml/scripts/unpack.py +0 -0
- package/templates/skills/document-skills/pptx/ooxml/scripts/validate.py +0 -0
- package/templates/skills/document-skills/pptx/scripts/inventory.py +0 -0
- package/templates/skills/document-skills/pptx/scripts/rearrange.py +0 -0
- package/templates/skills/document-skills/pptx/scripts/replace.py +0 -0
- package/templates/skills/document-skills/pptx/scripts/thumbnail.py +0 -0
- package/templates/skills/document-skills/xlsx/SKILL.md +2 -2
- package/templates/skills/document-skills/xlsx/recalc.py +3 -2
- package/templates/skills/find-skills/SKILL.md +134 -0
- package/templates/skills/fix/SKILL.md +111 -0
- package/templates/skills/fix/references/complexity-assessment.md +72 -0
- package/templates/skills/fix/references/mode-selection.md +46 -0
- package/templates/skills/fix/references/parallel-exploration.md +100 -0
- package/templates/skills/fix/references/review-cycle.md +77 -0
- package/templates/skills/fix/references/skill-activation-matrix.md +78 -0
- package/templates/skills/fix/references/task-orchestration.md +103 -0
- package/templates/skills/fix/references/workflow-ci.md +28 -0
- package/templates/skills/fix/references/workflow-deep.md +122 -0
- package/templates/skills/fix/references/workflow-logs.md +72 -0
- package/templates/skills/fix/references/workflow-quick.md +59 -0
- package/templates/skills/fix/references/workflow-standard.md +111 -0
- package/templates/skills/fix/references/workflow-test.md +75 -0
- package/templates/skills/fix/references/workflow-types.md +33 -0
- package/templates/skills/fix/references/workflow-ui.md +75 -0
- package/templates/skills/frontend-design/SKILL.md +78 -91
- package/templates/skills/frontend-design/references/ai-multimodal-overview.md +6 -6
- package/templates/skills/frontend-design/references/animejs.md +395 -395
- package/templates/skills/frontend-design/references/asset-generation.md +4 -4
- package/templates/skills/frontend-design/references/visual-analysis-overview.md +1 -1
- package/templates/skills/frontend-design/references/workflow-3d.md +102 -0
- package/templates/skills/frontend-design/references/workflow-describe.md +87 -0
- package/templates/skills/frontend-design/references/workflow-immersive.md +87 -0
- package/templates/skills/frontend-design/references/workflow-quick.md +57 -0
- package/templates/skills/frontend-design/references/workflow-screenshot.md +63 -0
- package/templates/skills/frontend-design/references/workflow-video.md +74 -0
- package/templates/skills/frontend-development/SKILL.md +4 -3
- package/templates/skills/git/SKILL.md +114 -0
- package/templates/skills/git/references/branch-management.md +88 -0
- package/templates/skills/git/references/commit-standards.md +46 -0
- package/templates/skills/git/references/gh-cli-guide.md +109 -0
- package/templates/skills/git/references/safety-protocols.md +69 -0
- package/templates/skills/git/references/workflow-commit.md +58 -0
- package/templates/skills/git/references/workflow-merge.md +48 -0
- package/templates/skills/git/references/workflow-pr.md +58 -0
- package/templates/skills/git/references/workflow-push.md +52 -0
- package/templates/skills/gkg/SKILL.md +91 -0
- package/templates/skills/gkg/references/cli-commands.md +106 -0
- package/templates/skills/gkg/references/http-api.md +102 -0
- package/templates/skills/gkg/references/language-support.md +57 -0
- package/templates/skills/gkg/references/mcp-tools.md +99 -0
- package/templates/skills/google-adk-python/SKILL.md +91 -195
- package/templates/skills/google-adk-python/references/agent-types-and-architecture.md +128 -0
- package/templates/skills/google-adk-python/references/callbacks-plugins-observability.md +117 -0
- package/templates/skills/google-adk-python/references/deployment-cloud-run-vertex-gke.md +138 -0
- package/templates/skills/google-adk-python/references/evaluation-testing-cli.md +112 -0
- package/templates/skills/google-adk-python/references/multi-agent-and-a2a-protocol.md +145 -0
- package/templates/skills/google-adk-python/references/sessions-state-memory-artifacts.md +131 -0
- package/templates/skills/google-adk-python/references/tools-and-mcp-integration.md +146 -0
- package/templates/skills/install.ps1 +130 -26
- package/templates/skills/install.sh +383 -63
- package/templates/{commands/journal.md → skills/journal/SKILL.md} +5 -1
- package/templates/skills/kanban/SKILL.md +99 -0
- package/templates/skills/markdown-novel-viewer/SKILL.md +314 -0
- package/templates/skills/markdown-novel-viewer/assets/directory-browser.css +215 -0
- package/templates/skills/markdown-novel-viewer/assets/favicon.png +0 -0
- package/templates/skills/markdown-novel-viewer/assets/novel-theme.css +16 -0
- package/templates/skills/markdown-novel-viewer/assets/reader.js +838 -0
- package/templates/skills/markdown-novel-viewer/assets/styles/novel-theme-base.css +54 -0
- package/templates/skills/markdown-novel-viewer/assets/styles/novel-theme-components.css +180 -0
- package/templates/skills/markdown-novel-viewer/assets/styles/novel-theme-content.css +176 -0
- package/templates/skills/markdown-novel-viewer/assets/styles/novel-theme-header.css +217 -0
- package/templates/skills/markdown-novel-viewer/assets/styles/novel-theme-mermaid.css +153 -0
- package/templates/skills/markdown-novel-viewer/assets/styles/novel-theme-overlays.css +202 -0
- package/templates/skills/markdown-novel-viewer/assets/styles/novel-theme-responsive.css +285 -0
- package/templates/skills/markdown-novel-viewer/assets/styles/novel-theme-sidebar.css +359 -0
- package/templates/skills/markdown-novel-viewer/assets/styles/novel-theme-variables.css +56 -0
- package/templates/skills/markdown-novel-viewer/assets/template.html +149 -0
- package/templates/skills/markdown-novel-viewer/bun.lock +38 -0
- package/templates/skills/markdown-novel-viewer/package.json +15 -0
- package/templates/skills/markdown-novel-viewer/scripts/lib/http-server.cjs +434 -0
- package/templates/skills/markdown-novel-viewer/scripts/lib/markdown-renderer.cjs +335 -0
- package/templates/skills/markdown-novel-viewer/scripts/lib/plan-navigator.cjs +571 -0
- package/templates/skills/markdown-novel-viewer/scripts/lib/port-finder.cjs +48 -0
- package/templates/skills/markdown-novel-viewer/scripts/lib/process-mgr.cjs +150 -0
- package/templates/skills/markdown-novel-viewer/scripts/server.cjs +411 -0
- package/templates/skills/markdown-novel-viewer/scripts/tests/server.test.cjs +283 -0
- package/templates/skills/markdown-novel-viewer/tests/dashboard-assets.test.cjs +340 -0
- package/templates/skills/markdown-novel-viewer/tests/dashboard-renderer.test.cjs +404 -0
- package/templates/skills/markdown-novel-viewer/tests/http-server.test.cjs +271 -0
- package/templates/skills/markdown-novel-viewer/tests/run-tests.cjs +51 -0
- package/templates/skills/markdown-novel-viewer/tests/test-framework.cjs +154 -0
- package/templates/skills/markdown-novel-viewer/tests/verify-xss.cjs +90 -0
- package/templates/skills/mcp-builder/SKILL.md +3 -2
- package/templates/skills/mcp-builder/scripts/evaluation.py +9 -1
- package/templates/skills/mcp-management/SKILL.md +8 -7
- package/templates/skills/mcp-management/references/gemini-cli-integration.md +16 -10
- package/templates/skills/mcp-management/scripts/cli.ts +0 -0
- package/templates/skills/mcp-management/scripts/dist/analyze-tools.js +0 -0
- package/templates/skills/mcp-management/scripts/dist/cli.js +0 -0
- package/templates/skills/mcp-management/scripts/dist/mcp-client.js +0 -0
- package/templates/skills/mcp-management/scripts/mcp-client.ts +0 -0
- package/templates/skills/media-processing/SKILL.md +3 -2
- package/templates/skills/media-processing/scripts/batch-remove-background.sh +0 -0
- package/templates/skills/media-processing/scripts/batch_resize.py +0 -0
- package/templates/skills/media-processing/scripts/media_convert.py +0 -0
- package/templates/skills/media-processing/scripts/remove-background.sh +0 -0
- package/templates/skills/media-processing/scripts/remove-bg-node.js +0 -0
- package/templates/skills/media-processing/scripts/tests/test_batch_resize.py +0 -0
- package/templates/skills/media-processing/scripts/tests/test_media_convert.py +0 -0
- package/templates/skills/media-processing/scripts/tests/test_video_optimize.py +0 -0
- package/templates/skills/media-processing/scripts/video_optimize.py +0 -0
- package/templates/skills/mermaidjs-v11/SKILL.md +116 -0
- package/templates/skills/mermaidjs-v11/references/cli-usage.md +228 -0
- package/templates/skills/mermaidjs-v11/references/configuration.md +232 -0
- package/templates/skills/mermaidjs-v11/references/diagram-types.md +315 -0
- package/templates/skills/mermaidjs-v11/references/examples.md +344 -0
- package/templates/skills/mermaidjs-v11/references/integration.md +310 -0
- package/templates/skills/mintlify/SKILL.md +121 -0
- package/templates/skills/mintlify/references/ai-features-and-integrations-reference.md +756 -0
- package/templates/skills/mintlify/references/api-documentation-components-reference.md +873 -0
- package/templates/skills/mintlify/references/deployment-and-continuous-integration-reference.md +674 -0
- package/templates/skills/mintlify/references/docs-json-configuration-reference.md +724 -0
- package/templates/skills/mintlify/references/mdx-components-reference.md +551 -0
- package/templates/skills/mintlify/references/navigation-structure-and-organization-reference.md +775 -0
- package/templates/skills/mobile-development/SKILL.md +3 -2
- package/templates/skills/payment-integration/README.md +44 -12
- package/templates/skills/payment-integration/SKILL.md +82 -97
- package/templates/skills/payment-integration/references/creem/api.md +139 -0
- package/templates/skills/payment-integration/references/creem/checkouts.md +99 -0
- package/templates/skills/payment-integration/references/creem/licensing.md +136 -0
- package/templates/skills/payment-integration/references/creem/overview.md +65 -0
- package/templates/skills/payment-integration/references/creem/sdk.md +161 -0
- package/templates/skills/payment-integration/references/creem/subscriptions.md +129 -0
- package/templates/skills/payment-integration/references/creem/webhooks.md +120 -0
- package/templates/skills/payment-integration/references/implementation-workflows.md +43 -0
- package/templates/skills/payment-integration/references/multi-provider-order-management-patterns.md +821 -0
- package/templates/skills/payment-integration/references/paddle/api.md +116 -0
- package/templates/skills/payment-integration/references/paddle/best-practices.md +130 -0
- package/templates/skills/payment-integration/references/paddle/overview.md +57 -0
- package/templates/skills/payment-integration/references/paddle/paddle-js.md +106 -0
- package/templates/skills/payment-integration/references/paddle/sdk.md +131 -0
- package/templates/skills/payment-integration/references/paddle/subscriptions.md +118 -0
- package/templates/skills/payment-integration/references/paddle/webhooks.md +112 -0
- package/templates/skills/payment-integration/references/polar/best-practices.md +781 -361
- package/templates/skills/payment-integration/references/sepay/best-practices.md +870 -268
- package/templates/skills/payment-integration/references/stripe/stripe-best-practices.md +32 -0
- package/templates/skills/payment-integration/references/stripe/stripe-cli.md +148 -0
- package/templates/skills/payment-integration/references/stripe/stripe-js.md +116 -0
- package/templates/skills/payment-integration/references/stripe/stripe-sdks.md +84 -0
- package/templates/skills/payment-integration/references/stripe/stripe-upgrade.md +175 -0
- package/templates/skills/payment-integration/scripts/checkout-helper.js +0 -0
- package/templates/skills/payment-integration/scripts/polar-webhook-verify.js +0 -0
- package/templates/skills/payment-integration/scripts/sepay-webhook-verify.js +0 -0
- package/templates/skills/payment-integration/scripts/test-scripts.js +0 -0
- package/templates/skills/plan/SKILL.md +137 -0
- package/templates/skills/plan/references/archive-workflow.md +53 -0
- package/templates/skills/{planning → plan}/references/codebase-understanding.md +1 -1
- package/templates/skills/{planning → plan}/references/output-standards.md +15 -1
- package/templates/skills/{planning → plan}/references/plan-organization.md +12 -19
- package/templates/skills/plan/references/red-team-personas.md +69 -0
- package/templates/skills/plan/references/red-team-workflow.md +77 -0
- package/templates/skills/{planning → plan}/references/research-phase.md +2 -2
- package/templates/skills/plan/references/task-management.md +132 -0
- package/templates/skills/plan/references/validate-question-framework.md +80 -0
- package/templates/skills/plan/references/validate-workflow.md +65 -0
- package/templates/skills/plan/references/workflow-modes.md +145 -0
- package/templates/skills/plans-kanban/SKILL.md +167 -0
- package/templates/skills/plans-kanban/assets/dashboard-template.html +119 -0
- package/templates/skills/plans-kanban/assets/dashboard.css +1594 -0
- package/templates/skills/plans-kanban/assets/dashboard.js +596 -0
- package/templates/skills/plans-kanban/assets/favicon.png +0 -0
- package/templates/skills/plans-kanban/package.json +13 -0
- package/templates/skills/plans-kanban/scripts/lib/dashboard-renderer.cjs +884 -0
- package/templates/skills/plans-kanban/scripts/lib/http-server.cjs +310 -0
- package/templates/skills/plans-kanban/scripts/lib/plan-metadata-extractor.cjs +489 -0
- package/templates/skills/plans-kanban/scripts/lib/plan-parser.cjs +175 -0
- package/templates/skills/plans-kanban/scripts/lib/plan-scanner.cjs +272 -0
- package/templates/skills/plans-kanban/scripts/lib/port-finder.cjs +48 -0
- package/templates/skills/plans-kanban/scripts/lib/process-mgr.cjs +128 -0
- package/templates/skills/plans-kanban/scripts/server.cjs +260 -0
- package/templates/skills/preview/SKILL.md +75 -0
- package/templates/skills/preview/references/generation-modes.md +95 -0
- package/templates/skills/preview/references/view-mode.md +42 -0
- package/templates/skills/problem-solving/SKILL.md +3 -2
- package/templates/skills/project-management/SKILL.md +122 -0
- package/templates/skills/project-management/references/documentation-triggers.md +60 -0
- package/templates/skills/project-management/references/hydration-workflow.md +85 -0
- package/templates/skills/project-management/references/progress-tracking.md +96 -0
- package/templates/skills/project-management/references/reporting-patterns.md +94 -0
- package/templates/skills/project-management/references/task-operations.md +85 -0
- package/templates/skills/react-best-practices/AGENTS.md +2249 -0
- package/templates/skills/react-best-practices/README.md +123 -0
- package/templates/skills/react-best-practices/SKILL.md +122 -0
- package/templates/skills/react-best-practices/metadata.json +15 -0
- package/templates/skills/react-best-practices/rules/_sections.md +46 -0
- package/templates/skills/react-best-practices/rules/_template.md +28 -0
- package/templates/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/templates/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/templates/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/templates/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/templates/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/templates/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/templates/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/templates/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/templates/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/templates/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/templates/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/templates/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/templates/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/templates/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/templates/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/templates/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/templates/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/templates/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/templates/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/templates/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/templates/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/templates/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/templates/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/templates/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/templates/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/templates/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/templates/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/templates/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/templates/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/templates/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/templates/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/templates/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/templates/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/templates/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/templates/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/templates/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/templates/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/templates/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/templates/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/templates/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/templates/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/templates/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/templates/skills/react-best-practices/rules/server-cache-react.md +26 -0
- package/templates/skills/react-best-practices/rules/server-parallel-fetching.md +79 -0
- package/templates/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/templates/skills/remotion/SKILL.md +44 -0
- package/templates/skills/remotion/rules/3d.md +86 -0
- package/templates/skills/remotion/rules/animations.md +29 -0
- package/templates/skills/remotion/rules/assets/charts-bar-chart.tsx +173 -0
- package/templates/skills/remotion/rules/assets/text-animations-typewriter.tsx +100 -0
- package/templates/skills/remotion/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/templates/skills/remotion/rules/assets.md +78 -0
- package/templates/skills/remotion/rules/audio.md +172 -0
- package/templates/skills/remotion/rules/calculate-metadata.md +104 -0
- package/templates/skills/remotion/rules/can-decode.md +75 -0
- package/templates/skills/remotion/rules/charts.md +58 -0
- package/templates/skills/remotion/rules/compositions.md +146 -0
- package/templates/skills/remotion/rules/display-captions.md +126 -0
- package/templates/skills/remotion/rules/extract-frames.md +229 -0
- package/templates/skills/remotion/rules/fonts.md +152 -0
- package/templates/skills/remotion/rules/get-audio-duration.md +58 -0
- package/templates/skills/remotion/rules/get-video-dimensions.md +68 -0
- package/templates/skills/remotion/rules/get-video-duration.md +58 -0
- package/templates/skills/remotion/rules/gifs.md +138 -0
- package/templates/skills/remotion/rules/images.md +130 -0
- package/templates/skills/remotion/rules/import-srt-captions.md +67 -0
- package/templates/skills/remotion/rules/lottie.md +68 -0
- package/templates/skills/remotion/rules/measuring-dom-nodes.md +35 -0
- package/templates/skills/remotion/rules/measuring-text.md +143 -0
- package/templates/skills/remotion/rules/sequencing.md +106 -0
- package/templates/skills/remotion/rules/tailwind.md +11 -0
- package/templates/skills/remotion/rules/text-animations.md +20 -0
- package/templates/skills/remotion/rules/timing.md +179 -0
- package/templates/skills/remotion/rules/transcribe-captions.md +19 -0
- package/templates/skills/remotion/rules/transitions.md +122 -0
- package/templates/skills/remotion/rules/trimming.md +53 -0
- package/templates/skills/remotion/rules/videos.md +171 -0
- package/templates/skills/repomix/SKILL.md +3 -2
- package/templates/skills/repomix/scripts/repomix_batch.py +0 -0
- package/templates/skills/research/SKILL.md +9 -6
- package/templates/skills/scout/SKILL.md +89 -0
- package/templates/skills/scout/references/external-scouting.md +140 -0
- package/templates/skills/scout/references/internal-scouting.md +119 -0
- package/templates/skills/scout/references/task-management-scouting.md +125 -0
- package/templates/skills/sequential-thinking/SKILL.md +3 -2
- package/templates/skills/sequential-thinking/scripts/format-thought.js +0 -0
- package/templates/skills/sequential-thinking/scripts/process-thought.js +0 -0
- package/templates/skills/shader/SKILL.md +113 -0
- package/templates/skills/shader/references/glsl-cellular-voronoi-worley-noise-patterns.md +142 -0
- package/templates/skills/shader/references/glsl-colors-rgb-hsb-gradients-mixing-color-spaces.md +143 -0
- package/templates/skills/shader/references/glsl-fbm-fractional-brownian-motion-turbulence-octaves.md +146 -0
- package/templates/skills/shader/references/glsl-fundamentals-data-types-vectors-precision-coordinates.md +104 -0
- package/templates/skills/shader/references/glsl-noise-random-perlin-simplex-cellular-voronoi.md +115 -0
- package/templates/skills/shader/references/glsl-pattern-symmetry-truchet-domain-warping.md +134 -0
- package/templates/skills/shader/references/glsl-patterns-tiling-fract-matrices-transformations.md +133 -0
- package/templates/skills/shader/references/glsl-procedural-textures-clouds-marble-wood-terrain.md +144 -0
- package/templates/skills/shader/references/glsl-shader-builtin-functions-complete-api-reference.md +112 -0
- package/templates/skills/shader/references/glsl-shapes-polygon-star-polar-sdf-combinations.md +124 -0
- package/templates/skills/shader/references/glsl-shapes-sdf-circles-rectangles-polar-distance-fields.md +106 -0
- package/templates/skills/shader/references/glsl-shaping-functions-step-smoothstep-curves-interpolation.md +141 -0
- package/templates/skills/shopify/SKILL.md +3 -2
- package/templates/skills/shopify/scripts/shopify_init.py +5 -5
- package/templates/skills/skill-creator/SKILL.md +91 -238
- package/templates/skills/skill-creator/references/benchmark-optimization-guide.md +86 -0
- package/templates/skills/skill-creator/references/distribution-guide.md +79 -0
- package/templates/skills/skill-creator/references/mcp-skills-integration.md +71 -0
- package/templates/skills/skill-creator/references/metadata-quality-criteria.md +76 -0
- package/templates/skills/skill-creator/references/plugin-marketplace-hosting.md +104 -0
- package/templates/skills/skill-creator/references/plugin-marketplace-overview.md +89 -0
- package/templates/skills/skill-creator/references/plugin-marketplace-schema.md +93 -0
- package/templates/skills/skill-creator/references/plugin-marketplace-sources.md +103 -0
- package/templates/skills/skill-creator/references/plugin-marketplace-troubleshooting.md +76 -0
- package/templates/skills/skill-creator/references/script-quality-criteria.md +106 -0
- package/templates/skills/skill-creator/references/skill-anatomy-and-requirements.md +76 -0
- package/templates/skills/skill-creator/references/skill-creation-workflow.md +95 -0
- package/templates/skills/skill-creator/references/skill-design-patterns.md +75 -0
- package/templates/skills/skill-creator/references/skillmark-benchmark-criteria.md +102 -0
- package/templates/skills/skill-creator/references/structure-organization-criteria.md +114 -0
- package/templates/skills/skill-creator/references/testing-and-iteration.md +78 -0
- package/templates/skills/skill-creator/references/token-efficiency-criteria.md +74 -0
- package/templates/skills/skill-creator/references/troubleshooting-guide.md +80 -0
- package/templates/skills/skill-creator/references/validation-checklist.md +83 -0
- package/templates/skills/skill-creator/references/writing-effective-instructions.md +88 -0
- package/templates/skills/skill-creator/references/yaml-frontmatter-reference.md +91 -0
- package/templates/skills/skill-creator/scripts/debug.zip +0 -0
- package/templates/skills/skill-creator/scripts/encoding_utils.py +36 -0
- package/templates/skills/skill-creator/scripts/init_skill.py +9 -4
- package/templates/skills/skill-creator/scripts/package_skill.py +5 -0
- package/templates/skills/skill-creator/scripts/quick_validate.py +6 -2
- package/templates/skills/tanstack/SKILL.md +141 -0
- package/templates/skills/tanstack/references/tanstack-ai.md +97 -0
- package/templates/skills/tanstack/references/tanstack-form.md +125 -0
- package/templates/skills/tanstack/references/tanstack-start.md +100 -0
- package/templates/skills/team/SKILL.md +285 -0
- package/templates/skills/team/references/agent-teams-controls-and-modes.md +107 -0
- package/templates/skills/team/references/agent-teams-examples-and-best-practices.md +182 -0
- package/templates/skills/team/references/agent-teams-official-docs.md +175 -0
- package/templates/skills/template-skill/SKILL.md +1 -1
- package/templates/skills/test/SKILL.md +109 -0
- package/templates/skills/test/references/report-format.md +58 -0
- package/templates/skills/test/references/test-execution-workflow.md +103 -0
- package/templates/skills/test/references/ui-testing-workflow.md +65 -0
- package/templates/skills/threejs/SKILL.md +106 -53
- package/templates/skills/threejs/data/api-reference.csv +61 -0
- package/templates/skills/threejs/data/categories.csv +14 -0
- package/templates/skills/threejs/data/examples-all.csv +557 -0
- package/templates/skills/threejs/data/use-cases.csv +21 -0
- package/templates/skills/threejs/references/00-fundamentals.md +487 -0
- package/templates/skills/threejs/references/11-materials-advanced.md +1 -1
- package/templates/skills/threejs/references/11-materials.md +519 -0
- package/templates/skills/threejs/references/17-shader.md +641 -0
- package/templates/skills/threejs/references/18-geometry.md +547 -0
- package/templates/skills/threejs/scripts/core.py +236 -0
- package/templates/skills/threejs/scripts/extract_examples.py +688 -0
- package/templates/skills/threejs/scripts/generate_csv_from_json.py +135 -0
- package/templates/skills/threejs/scripts/search.py +77 -0
- package/templates/skills/ui-styling/SKILL.md +3 -2
- package/templates/skills/ui-styling/scripts/shadcn_add.py +0 -0
- package/templates/skills/ui-styling/scripts/tailwind_config_gen.py +1 -1
- package/templates/skills/ui-ux-pro-max/SKILL.md +69 -32
- package/templates/skills/ui-ux-pro-max/data/charts.csv +25 -25
- package/templates/skills/ui-ux-pro-max/data/colors.csv +96 -96
- package/templates/skills/ui-ux-pro-max/data/landing.csv +30 -30
- package/templates/skills/ui-ux-pro-max/data/products.csv +96 -96
- package/templates/skills/ui-ux-pro-max/data/prompts.csv +20 -20
- package/templates/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -53
- package/templates/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -56
- package/templates/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -53
- package/templates/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -52
- package/templates/skills/ui-ux-pro-max/data/stacks/react.csv +54 -54
- package/templates/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -54
- package/templates/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -51
- package/templates/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -50
- package/templates/skills/ui-ux-pro-max/data/styles.csv +58 -58
- package/templates/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/templates/skills/ui-ux-pro-max/data/ux-guidelines.csv +99 -99
- package/templates/skills/ui-ux-pro-max/scripts/design_system.py +494 -0
- package/templates/skills/ui-ux-pro-max/scripts/search.py +84 -61
- package/templates/skills/use-mcp/SKILL.md +42 -0
- package/templates/skills/watzup/SKILL.md +12 -0
- package/templates/skills/web-design-guidelines/SKILL.md +36 -0
- package/templates/skills/web-frameworks/SKILL.md +4 -3
- package/templates/skills/web-frameworks/scripts/nextjs_init.py +13 -13
- package/templates/skills/web-frameworks/scripts/turborepo_migrate.py +0 -0
- package/templates/skills/web-testing/SKILL.md +97 -0
- package/templates/skills/web-testing/references/accessibility-testing.md +84 -0
- package/templates/skills/web-testing/references/api-testing.md +78 -0
- package/templates/skills/web-testing/references/ci-cd-testing-workflows.md +121 -0
- package/templates/skills/web-testing/references/component-testing.md +94 -0
- package/templates/skills/web-testing/references/contract-testing.md +146 -0
- package/templates/skills/web-testing/references/cross-browser-checklist.md +72 -0
- package/templates/skills/web-testing/references/database-testing.md +139 -0
- package/templates/skills/web-testing/references/e2e-testing-playwright.md +119 -0
- package/templates/skills/web-testing/references/functional-testing-checklist.md +88 -0
- package/templates/skills/web-testing/references/interactive-testing-patterns.md +89 -0
- package/templates/skills/web-testing/references/load-testing-k6.md +93 -0
- package/templates/skills/web-testing/references/mobile-gesture-testing.md +85 -0
- package/templates/skills/web-testing/references/performance-core-web-vitals.md +124 -0
- package/templates/skills/web-testing/references/playwright-component-testing.md +115 -0
- package/templates/skills/web-testing/references/pre-release-checklist.md +75 -0
- package/templates/skills/web-testing/references/security-checklists.md +81 -0
- package/templates/skills/web-testing/references/security-testing-overview.md +92 -0
- package/templates/skills/web-testing/references/shadow-dom-testing.md +70 -0
- package/templates/skills/web-testing/references/test-data-management.md +131 -0
- package/templates/skills/web-testing/references/test-flakiness-mitigation.md +86 -0
- package/templates/skills/web-testing/references/testing-pyramid-strategy.md +76 -0
- package/templates/skills/web-testing/references/unit-integration-testing.md +138 -0
- package/templates/skills/web-testing/references/visual-regression.md +92 -0
- package/templates/skills/web-testing/references/vulnerability-payloads.md +93 -0
- package/templates/skills/web-testing/scripts/analyze-test-results.js +280 -0
- package/templates/skills/web-testing/scripts/init-playwright.js +233 -0
- package/templates/skills/worktree/SKILL.md +96 -0
- package/templates/skills/worktree/scripts/worktree.cjs +916 -0
- package/templates/skills/worktree/scripts/worktree.test.cjs +792 -0
- package/templates/statusline.cjs +477 -244
- package/templates/statusline.ps1 +0 -1
- package/templates/statusline.sh +0 -1
- package/templates/agents/README.md +0 -172
- package/templates/agents/copywriter.md +0 -113
- package/templates/agents/database-admin.md +0 -97
- package/templates/agents/scout-external.md +0 -146
- package/templates/agents/scout.md +0 -260
- package/templates/commands/README.md +0 -251
- package/templates/commands/bootstrap/auto/fast.md +0 -111
- package/templates/commands/bootstrap/auto/parallel.md +0 -66
- package/templates/commands/bootstrap/auto.md +0 -115
- package/templates/commands/bootstrap.md +0 -137
- package/templates/commands/build.md +0 -39
- package/templates/commands/checkpoint.md +0 -156
- package/templates/commands/code/auto.md +0 -170
- package/templates/commands/code/no-test.md +0 -158
- package/templates/commands/code/parallel.md +0 -55
- package/templates/commands/code-simplifier.md +0 -71
- package/templates/commands/code.md +0 -176
- package/templates/commands/compact.md +0 -57
- package/templates/commands/content/cro.md +0 -43
- package/templates/commands/content/enhance.md +0 -14
- package/templates/commands/content/fast.md +0 -13
- package/templates/commands/content/good.md +0 -16
- package/templates/commands/context.md +0 -48
- package/templates/commands/cook/auto/fast.md +0 -26
- package/templates/commands/cook/auto/parallel.md +0 -49
- package/templates/commands/cook/auto.md +0 -15
- package/templates/commands/cook/fast.md +0 -47
- package/templates/commands/cook/hard.md +0 -80
- package/templates/commands/cook/parallel.md +0 -90
- package/templates/commands/cook.md +0 -105
- package/templates/commands/create-feature.md +0 -48
- package/templates/commands/db-migrate.md +0 -52
- package/templates/commands/debug.md +0 -13
- package/templates/commands/design/3d.md +0 -83
- package/templates/commands/design/describe.md +0 -23
- package/templates/commands/design/fast.md +0 -31
- package/templates/commands/design/good.md +0 -35
- package/templates/commands/design/screenshot.md +0 -34
- package/templates/commands/design/video.md +0 -34
- package/templates/commands/docs/init.md +0 -39
- package/templates/commands/docs/summarize.md +0 -31
- package/templates/commands/docs/update.md +0 -57
- package/templates/commands/feature.md +0 -62
- package/templates/commands/fix/ci.md +0 -17
- package/templates/commands/fix/fast.md +0 -19
- package/templates/commands/fix/hard.md +0 -39
- package/templates/commands/fix/logs.md +0 -26
- package/templates/commands/fix/parallel.md +0 -54
- package/templates/commands/fix/test.md +0 -20
- package/templates/commands/fix/types.md +0 -9
- package/templates/commands/fix/ui.md +0 -48
- package/templates/commands/fix-issue.md +0 -177
- package/templates/commands/fix.md +0 -43
- package/templates/commands/generate-dto.md +0 -67
- package/templates/commands/git/cm.md +0 -5
- package/templates/commands/git/cp.md +0 -4
- package/templates/commands/git/merge.md +0 -40
- package/templates/commands/git/pr.md +0 -48
- package/templates/commands/integrate/polar.md +0 -28
- package/templates/commands/integrate/sepay.md +0 -28
- package/templates/commands/investigate.md +0 -324
- package/templates/commands/lint.md +0 -47
- package/templates/commands/migration.md +0 -111
- package/templates/commands/performance.md +0 -110
- package/templates/commands/plan/ci.md +0 -33
- package/templates/commands/plan/cro.md +0 -69
- package/templates/commands/plan/fast.md +0 -86
- package/templates/commands/plan/hard.md +0 -103
- package/templates/commands/plan/parallel.md +0 -152
- package/templates/commands/plan/preview.md +0 -40
- package/templates/commands/plan/two.md +0 -52
- package/templates/commands/plan/validate.md +0 -132
- package/templates/commands/plan.md +0 -36
- package/templates/commands/pr.md +0 -49
- package/templates/commands/preview.md +0 -87
- package/templates/commands/release-notes.md +0 -144
- package/templates/commands/review/post-task.md +0 -157
- package/templates/commands/review-changes.md +0 -46
- package/templates/commands/review.md +0 -56
- package/templates/commands/scout/ext.md +0 -35
- package/templates/commands/scout.md +0 -283
- package/templates/commands/security.md +0 -119
- package/templates/commands/skill/add.md +0 -36
- package/templates/commands/skill/create.md +0 -29
- package/templates/commands/skill/fix-logs.md +0 -22
- package/templates/commands/skill/optimize/auto.md +0 -25
- package/templates/commands/skill/optimize.md +0 -34
- package/templates/commands/skill/plan.md +0 -45
- package/templates/commands/worktree.md +0 -126
- package/templates/hooks/.python-cache.json +0 -1
- package/templates/hooks/README.md +0 -246
- package/templates/hooks/backend-csharp-context.cjs +0 -223
- package/templates/hooks/design-system-context.cjs +0 -185
- package/templates/hooks/frontend-typescript-context.cjs +0 -233
- package/templates/hooks/lib/ck-paths.cjs +0 -110
- package/templates/hooks/lib/context-tracker.cjs +0 -335
- package/templates/hooks/notify-waiting.js +0 -117
- package/templates/hooks/post-edit-prettier.cjs +0 -189
- package/templates/hooks/post-task-review.cjs +0 -142
- package/templates/hooks/scss-styling-context.cjs +0 -213
- package/templates/hooks/session-end.cjs +0 -35
- package/templates/hooks/tests/test-context-tracker.cjs +0 -454
- package/templates/hooks/tests/test-scout-block.js +0 -163
- package/templates/hooks/workflow-router.cjs +0 -326
- package/templates/hooks/write-compact-marker.cjs +0 -159
- package/templates/memory/session-log.md +0 -186
- package/templates/router/README.md +0 -294
- package/templates/router/agents-guide.md +0 -38
- package/templates/router/commands-guide.md +0 -122
- package/templates/router/decision-flow.md +0 -92
- package/templates/router/skills-guide.md +0 -127
- package/templates/router/workflows-guide.md +0 -68
- package/templates/scripts/__pycache__/win_compat.cpython-312.pyc +0 -0
- package/templates/scripts/plan-preview.cjs +0 -921
- package/templates/skills/arch-cross-service-integration/SKILL.md +0 -48
- package/templates/skills/arch-performance-optimization/SKILL.md +0 -306
- package/templates/skills/arch-security-review/SKILL.md +0 -344
- package/templates/skills/branch-comparison/SKILL.md +0 -150
- package/templates/skills/bug-diagnosis/SKILL.md +0 -309
- package/templates/skills/claude-code/references/advanced-features.md +0 -399
- package/templates/skills/claude-code/references/agent-skills.md +0 -399
- package/templates/skills/claude-code/references/api-reference.md +0 -498
- package/templates/skills/claude-code/references/best-practices.md +0 -447
- package/templates/skills/claude-code/references/cicd-integration.md +0 -428
- package/templates/skills/claude-code/references/common-workflows.md +0 -119
- package/templates/skills/claude-code/references/configuration.md +0 -480
- package/templates/skills/claude-code/references/enterprise-features.md +0 -472
- package/templates/skills/claude-code/references/getting-started.md +0 -252
- package/templates/skills/claude-code/references/hooks-and-plugins.md +0 -444
- package/templates/skills/claude-code/references/hooks-comprehensive.md +0 -622
- package/templates/skills/claude-code/references/ide-integration.md +0 -316
- package/templates/skills/claude-code/references/mcp-integration.md +0 -386
- package/templates/skills/claude-code/references/slash-commands.md +0 -489
- package/templates/skills/claude-code/references/troubleshooting.md +0 -456
- package/templates/skills/claude-code/skill.md +0 -60
- package/templates/skills/debugging/SKILL.md +0 -84
- package/templates/skills/developer-growth-analysis/SKILL.md +0 -322
- package/templates/skills/documentation/SKILL.md +0 -134
- package/templates/skills/domain-name-brainstormer/SKILL.md +0 -212
- package/templates/skills/dual-pass-review/SKILL.md +0 -249
- package/templates/skills/feature-docs/SKILL.md +0 -294
- package/templates/skills/feature-implementation/SKILL.md +0 -262
- package/templates/skills/feature-investigation/SKILL.md +0 -346
- package/templates/skills/frontend-design-pro/SKILL.md +0 -58
- package/templates/skills/package-upgrade/SKILL.md +0 -189
- package/templates/skills/plan-analysis/SKILL.md +0 -191
- package/templates/skills/planning/SKILL.md +0 -115
- package/templates/skills/planning-with-files/SKILL.md +0 -160
- package/templates/skills/planning-with-files/examples.md +0 -202
- package/templates/skills/planning-with-files/reference.md +0 -110
- package/templates/skills/project-index/SKILL.md +0 -97
- package/templates/skills/project-index/scripts/scan-structure.js +0 -417
- package/templates/skills/project-index/scripts/scan_structure.py +0 -450
- package/templates/skills/readme-improvement/SKILL.md +0 -177
- package/templates/skills/skill-share/SKILL.md +0 -80
- package/templates/skills/tasks-code-review/SKILL.md +0 -298
- package/templates/skills/tasks-documentation/SKILL.md +0 -328
- package/templates/skills/tasks-spec-update/SKILL.md +0 -318
- package/templates/skills/tasks-test-generation/SKILL.md +0 -433
- package/templates/skills/test-generation/SKILL.md +0 -203
- package/templates/skills/webapp-testing/LICENSE.txt +0 -202
- package/templates/skills/webapp-testing/SKILL.md +0 -96
- package/templates/skills/webapp-testing/examples/console_logging.py +0 -35
- package/templates/skills/webapp-testing/examples/element_discovery.py +0 -40
- package/templates/skills/webapp-testing/examples/static_html_automation.py +0 -33
- package/templates/skills/webapp-testing/scripts/with_server.py +0 -106
- package/templates/workflows/README.md +0 -241
- package/templates/workflows/orchestration-protocol.md +0 -16
- /package/templates/{commands → command-archive}/coding-level.md +0 -0
- /package/templates/{commands → command-archive}/review/codebase.md +0 -0
- /package/templates/{commands → command-archive}/test.md +0 -0
- /package/templates/{commands → command-archive}/watzup.md +0 -0
- /package/templates/hooks/scout-block/vendor/{ignore.js → ignore.cjs} +0 -0
- /package/templates/{workflows → rules}/documentation-management.md +0 -0
- /package/templates/skills/{debugging → debug}/references/defense-in-depth.md +0 -0
- /package/templates/skills/{debugging → debug}/references/root-cause-tracing.md +0 -0
- /package/templates/skills/{debugging → debug}/references/systematic-debugging.md +0 -0
- /package/templates/skills/{debugging → debug}/references/verification.md +0 -0
- /package/templates/skills/{debugging → debug}/scripts/find-polluter.sh +0 -0
- /package/templates/skills/{debugging → debug}/scripts/find-polluter.test.md +0 -0
- /package/templates/skills/{planning → plan}/references/solution-design.md +0 -0
|
@@ -1,482 +1,902 @@
|
|
|
1
1
|
# Polar Best Practices
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Production-proven patterns from real SaaS implementations covering SDK initialization, checkout flows, webhooks, discounts, fee calculations, and error handling.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Environment Configuration
|
|
6
6
|
|
|
7
|
-
###
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
### Required Environment Variables
|
|
8
|
+
```bash
|
|
9
|
+
# Core API
|
|
10
|
+
POLAR_API_KEY=polar_at_xxx # Access token from Polar Dashboard
|
|
11
|
+
POLAR_ORGANIZATION_ID=org_xxx # Your organization ID
|
|
12
|
+
POLAR_WEBHOOK_SECRET=whsec_xxx # Webhook signature verification
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
# Product IDs (one per product)
|
|
15
|
+
POLAR_PRODUCT_ENGINEER_ID=prod_xxx
|
|
16
|
+
POLAR_PRODUCT_MARKETING_ID=prod_xxx
|
|
17
|
+
POLAR_PRODUCT_COMBO_ID=prod_xxx
|
|
18
|
+
|
|
19
|
+
# Environment (optional, defaults to production)
|
|
20
|
+
POLAR_ENV=production # 'production' or 'sandbox'
|
|
18
21
|
```
|
|
19
22
|
|
|
20
|
-
###
|
|
23
|
+
### Lazy Initialization Pattern
|
|
21
24
|
```typescript
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
// lib/polar.ts - Defer validation until first access
|
|
26
|
+
import { Polar } from '@polar-sh/sdk';
|
|
27
|
+
import { z } from 'zod';
|
|
28
|
+
|
|
29
|
+
const polarEnvSchema = z.object({
|
|
30
|
+
POLAR_API_KEY: z.string().min(1),
|
|
31
|
+
POLAR_ORGANIZATION_ID: z.string().min(1),
|
|
32
|
+
POLAR_WEBHOOK_SECRET: z.string().min(1),
|
|
30
33
|
});
|
|
31
|
-
```
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
let _polar: Polar | null = null;
|
|
36
|
+
let _env: z.infer<typeof polarEnvSchema> | null = null;
|
|
37
|
+
|
|
38
|
+
export function getPolarEnv() {
|
|
39
|
+
if (!_env) {
|
|
40
|
+
_env = polarEnvSchema.parse({
|
|
41
|
+
POLAR_API_KEY: process.env.POLAR_API_KEY,
|
|
42
|
+
POLAR_ORGANIZATION_ID: process.env.POLAR_ORGANIZATION_ID,
|
|
43
|
+
POLAR_WEBHOOK_SECRET: process.env.POLAR_WEBHOOK_SECRET,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return _env;
|
|
40
47
|
}
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
export function getPolar() {
|
|
50
|
+
if (!_polar) {
|
|
51
|
+
const env = getPolarEnv();
|
|
52
|
+
const polarEnv = process.env.POLAR_ENV || 'production';
|
|
53
|
+
_polar = new Polar({
|
|
54
|
+
accessToken: env.POLAR_API_KEY,
|
|
55
|
+
server: polarEnv as 'production' | 'sandbox',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return _polar;
|
|
59
|
+
}
|
|
44
60
|
```
|
|
45
61
|
|
|
46
|
-
|
|
62
|
+
**Key Benefit:** Module imports succeed at build time; validation deferred until runtime when env vars are available.
|
|
63
|
+
|
|
64
|
+
## Checkout Flow Implementation
|
|
47
65
|
|
|
48
|
-
###
|
|
66
|
+
### Standard Checkout API
|
|
49
67
|
```typescript
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
// app/api/checkout/polar/route.ts
|
|
69
|
+
import { NextResponse } from 'next/server';
|
|
70
|
+
import { z } from 'zod';
|
|
71
|
+
import { getPolar, getPolarEnv } from '@/lib/polar';
|
|
72
|
+
|
|
73
|
+
const checkoutSchema = z.object({
|
|
74
|
+
email: z.string().email(),
|
|
75
|
+
name: z.string().optional(),
|
|
76
|
+
productType: z.enum(['engineer_kit', 'marketing_kit', 'combo']),
|
|
77
|
+
githubUsername: z.string().min(1),
|
|
78
|
+
referralCode: z.string().regex(/^[A-Z0-9]{8}$/).optional(),
|
|
79
|
+
couponCode: z.string().optional(),
|
|
80
|
+
});
|
|
62
81
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
82
|
+
// Pricing in cents
|
|
83
|
+
const PRODUCT_PRICES = {
|
|
84
|
+
engineer_kit: 9900, // $99
|
|
85
|
+
marketing_kit: 9900, // $99
|
|
86
|
+
combo: 14900, // $149
|
|
87
|
+
} as const;
|
|
69
88
|
|
|
70
|
-
|
|
71
|
-
|
|
89
|
+
export async function POST(request: Request) {
|
|
90
|
+
try {
|
|
91
|
+
const body = await request.json();
|
|
92
|
+
const data = checkoutSchema.parse(body);
|
|
93
|
+
const polar = getPolar();
|
|
94
|
+
const env = getPolarEnv();
|
|
95
|
+
|
|
96
|
+
// 1. Normalize email
|
|
97
|
+
const normalizedEmail = data.email.toLowerCase().trim();
|
|
98
|
+
|
|
99
|
+
// 2. Validate GitHub username against GitHub API
|
|
100
|
+
const githubValid = await validateGitHubUsername(data.githubUsername);
|
|
101
|
+
if (!githubValid) {
|
|
102
|
+
return NextResponse.json(
|
|
103
|
+
{ error: 'Invalid GitHub username' },
|
|
104
|
+
{ status: 400 }
|
|
105
|
+
);
|
|
106
|
+
}
|
|
72
107
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
108
|
+
// 3. Get product ID and base price
|
|
109
|
+
const productId = getProductId(data.productType);
|
|
110
|
+
const originalAmount = PRODUCT_PRICES[data.productType];
|
|
111
|
+
|
|
112
|
+
// 4. Apply discount hierarchy (order matters!)
|
|
113
|
+
let finalAmount = originalAmount;
|
|
114
|
+
let polarDiscountId: string | undefined;
|
|
115
|
+
let discountMetadata: Record<string, any> = {};
|
|
116
|
+
|
|
117
|
+
// Step A: Apply coupon FIRST (if provided)
|
|
118
|
+
if (data.couponCode) {
|
|
119
|
+
const couponResult = await validateAndApplyCoupon(
|
|
120
|
+
data.couponCode,
|
|
121
|
+
productId,
|
|
122
|
+
originalAmount
|
|
123
|
+
);
|
|
124
|
+
if (couponResult.valid) {
|
|
125
|
+
finalAmount = originalAmount - couponResult.discountAmount;
|
|
126
|
+
discountMetadata.couponCode = data.couponCode;
|
|
127
|
+
discountMetadata.couponDiscountAmount = couponResult.discountAmount;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
78
130
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
131
|
+
// Step B: Apply referral discount SECOND (on post-coupon price)
|
|
132
|
+
if (data.referralCode) {
|
|
133
|
+
const referralResult = await calculateReferralDiscount(
|
|
134
|
+
data.referralCode,
|
|
135
|
+
finalAmount, // Applied to post-coupon amount
|
|
136
|
+
normalizedEmail
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
if (referralResult.valid && referralResult.discountAmount > 0) {
|
|
140
|
+
// Validate discount calculation
|
|
141
|
+
if (referralResult.discountAmount <= 0) {
|
|
142
|
+
return NextResponse.json(
|
|
143
|
+
{ error: 'Invalid discount calculation - contact support' },
|
|
144
|
+
{ status: 400 }
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
finalAmount -= referralResult.discountAmount;
|
|
149
|
+
discountMetadata.referralCode = data.referralCode;
|
|
150
|
+
discountMetadata.referralDiscountAmount = referralResult.discountAmount;
|
|
151
|
+
discountMetadata.referrerId = referralResult.referrerId;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
82
154
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
155
|
+
// 5. Create order record BEFORE Polar checkout
|
|
156
|
+
const order = await db.insert(orders).values({
|
|
157
|
+
id: crypto.randomUUID(),
|
|
158
|
+
email: normalizedEmail,
|
|
159
|
+
productType: data.productType,
|
|
160
|
+
amount: finalAmount,
|
|
161
|
+
originalAmount,
|
|
162
|
+
currency: 'USD',
|
|
163
|
+
status: 'pending',
|
|
164
|
+
paymentProvider: 'polar',
|
|
165
|
+
referredBy: discountMetadata.referrerId,
|
|
166
|
+
discountAmount: originalAmount - finalAmount,
|
|
167
|
+
metadata: JSON.stringify({
|
|
168
|
+
...discountMetadata,
|
|
169
|
+
githubUsername: data.githubUsername,
|
|
170
|
+
}),
|
|
171
|
+
}).returning();
|
|
172
|
+
|
|
173
|
+
// 6. Create dynamic Polar discount (if referral applied)
|
|
174
|
+
if (discountMetadata.referrerId && discountMetadata.referralDiscountAmount > 0) {
|
|
175
|
+
try {
|
|
176
|
+
const discount = await polar.discounts.create({
|
|
177
|
+
type: 'fixed',
|
|
178
|
+
name: `referral-${order[0].id.slice(0, 8)}`,
|
|
179
|
+
amount: discountMetadata.referralDiscountAmount,
|
|
180
|
+
currency: 'usd',
|
|
181
|
+
duration: 'once',
|
|
182
|
+
maxRedemptions: 1,
|
|
183
|
+
products: [productId],
|
|
184
|
+
metadata: {
|
|
185
|
+
orderId: order[0].id,
|
|
186
|
+
type: 'referral',
|
|
187
|
+
referrerId: discountMetadata.referrerId,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
polarDiscountId = discount.id;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
// FAIL-OPEN: Proceed with full price, flag for manual refund
|
|
193
|
+
console.error('⚠️ Failed to create Polar discount:', error);
|
|
194
|
+
}
|
|
86
195
|
}
|
|
87
|
-
}
|
|
88
196
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
197
|
+
// 7. Create Polar checkout session
|
|
198
|
+
const checkout = await polar.checkouts.create({
|
|
199
|
+
productPriceId: productId,
|
|
200
|
+
customerEmail: normalizedEmail,
|
|
201
|
+
successUrl: `${process.env.NEXT_PUBLIC_URL}/checkout/success?orderId=${order[0].id}`,
|
|
202
|
+
discountId: polarDiscountId,
|
|
203
|
+
allowDiscountCodes: !polarDiscountId, // Prevent stacking
|
|
204
|
+
metadata: {
|
|
205
|
+
orderId: order[0].id,
|
|
206
|
+
githubUsername: data.githubUsername,
|
|
207
|
+
referredBy: discountMetadata.referrerId,
|
|
208
|
+
},
|
|
100
209
|
});
|
|
101
210
|
|
|
102
|
-
|
|
103
|
-
|
|
211
|
+
return NextResponse.json({
|
|
212
|
+
checkoutUrl: checkout.url,
|
|
213
|
+
orderId: order[0].id,
|
|
214
|
+
});
|
|
104
215
|
|
|
105
|
-
|
|
106
|
-
|
|
216
|
+
} catch (error) {
|
|
217
|
+
if (error instanceof z.ZodError) {
|
|
218
|
+
return NextResponse.json({ error: error.errors }, { status: 400 });
|
|
219
|
+
}
|
|
220
|
+
console.error('Checkout error:', error);
|
|
221
|
+
return NextResponse.json(
|
|
222
|
+
{ error: 'Failed to create checkout' },
|
|
223
|
+
{ status: 500 }
|
|
224
|
+
);
|
|
107
225
|
}
|
|
108
226
|
}
|
|
109
227
|
```
|
|
110
228
|
|
|
111
|
-
###
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
});
|
|
229
|
+
### Discount Application Order (Critical)
|
|
230
|
+
```
|
|
231
|
+
1. Original price (e.g., $99)
|
|
232
|
+
2. Apply coupon discount FIRST → post-coupon price (e.g., $79)
|
|
233
|
+
3. Apply referral discount SECOND → final price (e.g., $63.20)
|
|
117
234
|
|
|
118
|
-
|
|
119
|
-
});
|
|
235
|
+
Never apply referral to original price if coupon was used!
|
|
120
236
|
```
|
|
121
237
|
|
|
122
|
-
|
|
238
|
+
## Webhook Handling
|
|
239
|
+
|
|
240
|
+
### Signature Verification
|
|
123
241
|
```typescript
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
await db.usage.insert({
|
|
128
|
-
user_id: userId,
|
|
129
|
-
event_name: eventName,
|
|
130
|
-
properties: properties,
|
|
131
|
-
timestamp: new Date()
|
|
132
|
-
});
|
|
242
|
+
// app/api/webhooks/polar/route.ts
|
|
243
|
+
import { validateEvent } from '@polar-sh/sdk/webhooks';
|
|
244
|
+
import { NextResponse } from 'next/server';
|
|
133
245
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
246
|
+
export async function POST(request: Request) {
|
|
247
|
+
const payload = await request.text();
|
|
248
|
+
const headers = Object.fromEntries(request.headers);
|
|
249
|
+
const secret = process.env.POLAR_WEBHOOK_SECRET!;
|
|
250
|
+
|
|
251
|
+
let webhookEvent;
|
|
252
|
+
try {
|
|
253
|
+
webhookEvent = validateEvent(payload, headers, secret);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
console.error('Invalid webhook signature:', error);
|
|
256
|
+
return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
|
|
140
257
|
}
|
|
141
258
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
meter_id: meterId
|
|
146
|
-
});
|
|
259
|
+
// Extract event ID for idempotency
|
|
260
|
+
const parsedPayload = JSON.parse(payload);
|
|
261
|
+
const eventId = parsedPayload.id || `${parsedPayload.type}-${Date.now()}`;
|
|
147
262
|
|
|
148
|
-
|
|
263
|
+
// Check for duplicate processing
|
|
264
|
+
const existingEvent = await db.select()
|
|
265
|
+
.from(webhookEvents)
|
|
266
|
+
.where(eq(webhookEvents.eventId, eventId))
|
|
267
|
+
.limit(1);
|
|
268
|
+
|
|
269
|
+
if (existingEvent.length > 0) {
|
|
270
|
+
console.log(`Duplicate webhook ignored: ${eventId}`);
|
|
271
|
+
return NextResponse.json({ received: true });
|
|
149
272
|
}
|
|
150
273
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
274
|
+
// Record event BEFORE processing (idempotency)
|
|
275
|
+
await db.insert(webhookEvents).values({
|
|
276
|
+
id: crypto.randomUUID(),
|
|
277
|
+
provider: 'polar',
|
|
278
|
+
eventType: webhookEvent.type,
|
|
279
|
+
eventId,
|
|
280
|
+
payload,
|
|
281
|
+
processed: false,
|
|
282
|
+
});
|
|
157
283
|
|
|
158
|
-
|
|
284
|
+
try {
|
|
285
|
+
await handleWebhookEvent(webhookEvent);
|
|
286
|
+
|
|
287
|
+
// Mark as processed
|
|
288
|
+
await db.update(webhookEvents)
|
|
289
|
+
.set({ processed: true, processedAt: new Date() })
|
|
290
|
+
.where(eq(webhookEvents.eventId, eventId));
|
|
159
291
|
|
|
160
|
-
|
|
292
|
+
} catch (error) {
|
|
293
|
+
// Log error but don't fail the webhook
|
|
294
|
+
await db.update(webhookEvents)
|
|
295
|
+
.set({
|
|
296
|
+
processed: true,
|
|
297
|
+
processedAt: new Date(),
|
|
298
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
299
|
+
})
|
|
300
|
+
.where(eq(webhookEvents.eventId, eventId));
|
|
161
301
|
}
|
|
302
|
+
|
|
303
|
+
return NextResponse.json({ received: true });
|
|
162
304
|
}
|
|
163
305
|
```
|
|
164
306
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
### External ID Strategy
|
|
307
|
+
### Event Handlers
|
|
168
308
|
```typescript
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
});
|
|
309
|
+
async function handleWebhookEvent(event: WebhookEvent) {
|
|
310
|
+
switch (event.type) {
|
|
311
|
+
case 'checkout.created':
|
|
312
|
+
// Order already exists from API - just log
|
|
313
|
+
console.log(`Checkout created: ${event.data.id}`);
|
|
314
|
+
break;
|
|
315
|
+
|
|
316
|
+
case 'checkout.updated':
|
|
317
|
+
await handleCheckoutUpdated(event.data);
|
|
318
|
+
break;
|
|
319
|
+
|
|
320
|
+
case 'order.created':
|
|
321
|
+
await handleOrderCreated(event.data);
|
|
322
|
+
break;
|
|
323
|
+
|
|
324
|
+
case 'order.refunded':
|
|
325
|
+
await handleOrderRefunded(event.data);
|
|
326
|
+
break;
|
|
327
|
+
|
|
328
|
+
default:
|
|
329
|
+
console.log(`Unhandled event type: ${event.type}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
174
332
|
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
333
|
+
async function handleOrderCreated(order: PolarOrder) {
|
|
334
|
+
const orderId = order.metadata?.orderId;
|
|
335
|
+
if (!orderId) {
|
|
336
|
+
console.error('Order missing orderId in metadata');
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
179
339
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
340
|
+
const dbOrder = await db.select()
|
|
341
|
+
.from(orders)
|
|
342
|
+
.where(eq(orders.id, orderId))
|
|
343
|
+
.limit(1);
|
|
184
344
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
345
|
+
if (!dbOrder[0]) {
|
|
346
|
+
console.error(`Order not found: ${orderId}`);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// 1. Update order status
|
|
351
|
+
await db.update(orders)
|
|
352
|
+
.set({
|
|
353
|
+
status: 'completed',
|
|
354
|
+
paymentId: order.id,
|
|
355
|
+
updatedAt: new Date(),
|
|
356
|
+
})
|
|
357
|
+
.where(eq(orders.id, orderId));
|
|
358
|
+
|
|
359
|
+
// 2. Create license (non-blocking)
|
|
360
|
+
try {
|
|
361
|
+
await createLicense(dbOrder[0]);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
console.error('Failed to create license:', error);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// 3. Send confirmation email (non-blocking)
|
|
367
|
+
try {
|
|
368
|
+
await sendOrderConfirmation(dbOrder[0], order);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
console.error('Failed to send confirmation:', error);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// 4. Create referral commission (non-blocking)
|
|
374
|
+
if (dbOrder[0].referredBy) {
|
|
375
|
+
try {
|
|
376
|
+
await createCommission(dbOrder[0]);
|
|
377
|
+
} catch (error) {
|
|
378
|
+
console.error('Failed to create commission:', error);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// 5. Grant GitHub access (non-blocking)
|
|
383
|
+
try {
|
|
384
|
+
const metadata = JSON.parse(dbOrder[0].metadata || '{}');
|
|
385
|
+
await inviteToGitHub(metadata.githubUsername, dbOrder[0].productType);
|
|
386
|
+
} catch (error) {
|
|
387
|
+
console.error('Failed to invite to GitHub:', error);
|
|
194
388
|
}
|
|
195
|
-
}
|
|
196
389
|
|
|
197
|
-
//
|
|
198
|
-
|
|
390
|
+
// 6. Send Discord notification (non-blocking)
|
|
391
|
+
try {
|
|
392
|
+
await sendSalesNotification(dbOrder[0]);
|
|
393
|
+
} catch (error) {
|
|
394
|
+
console.error('Failed to send Discord notification:', error);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
199
397
|
```
|
|
200
398
|
|
|
201
|
-
###
|
|
399
|
+
### Status Mapping
|
|
202
400
|
```typescript
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
});
|
|
401
|
+
function mapPolarStatusToAppStatus(polarStatus: string): string | null {
|
|
402
|
+
switch (polarStatus) {
|
|
403
|
+
case 'succeeded':
|
|
404
|
+
return 'completed';
|
|
405
|
+
case 'failed':
|
|
406
|
+
case 'expired':
|
|
407
|
+
return 'failed';
|
|
408
|
+
case 'open':
|
|
409
|
+
case 'confirmed':
|
|
410
|
+
return null; // Don't update - still pending
|
|
411
|
+
default:
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
218
414
|
}
|
|
219
415
|
```
|
|
220
416
|
|
|
221
|
-
##
|
|
417
|
+
## Fee Calculation
|
|
222
418
|
|
|
223
|
-
###
|
|
419
|
+
### Platform Fee Structure (Dec 2025)
|
|
224
420
|
```typescript
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
421
|
+
// lib/polar-fees.ts
|
|
422
|
+
interface PolarFeeConfig {
|
|
423
|
+
basePercentage: number; // 4%
|
|
424
|
+
baseFlatCents: number; // $0.40 per transaction
|
|
425
|
+
internationalSurcharge: number; // +1.5% for non-US cards
|
|
426
|
+
subscriptionSurcharge: number; // +0.5% (not for one-time)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const POLAR_FEES: PolarFeeConfig = {
|
|
430
|
+
basePercentage: 0.04,
|
|
431
|
+
baseFlatCents: 40,
|
|
432
|
+
internationalSurcharge: 0.015,
|
|
433
|
+
subscriptionSurcharge: 0.005,
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
export function calculatePolarFees(
|
|
437
|
+
amountCents: number,
|
|
438
|
+
isInternational: boolean = true, // Conservative default
|
|
439
|
+
isSubscription: boolean = false
|
|
440
|
+
): {
|
|
441
|
+
baseFee: number;
|
|
442
|
+
internationalFee: number;
|
|
443
|
+
subscriptionFee: number;
|
|
444
|
+
totalFee: number;
|
|
445
|
+
netRevenue: number;
|
|
446
|
+
} {
|
|
447
|
+
// Handle zero/negative
|
|
448
|
+
if (amountCents <= 0) {
|
|
449
|
+
return { baseFee: 0, internationalFee: 0, subscriptionFee: 0, totalFee: 0, netRevenue: 0 };
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const baseFee = Math.round(amountCents * POLAR_FEES.basePercentage + POLAR_FEES.baseFlatCents);
|
|
453
|
+
const internationalFee = isInternational
|
|
454
|
+
? Math.round(amountCents * POLAR_FEES.internationalSurcharge)
|
|
455
|
+
: 0;
|
|
456
|
+
const subscriptionFee = isSubscription
|
|
457
|
+
? Math.round(amountCents * POLAR_FEES.subscriptionSurcharge)
|
|
458
|
+
: 0;
|
|
459
|
+
|
|
460
|
+
const totalFee = baseFee + internationalFee + subscriptionFee;
|
|
461
|
+
const netRevenue = amountCents - totalFee;
|
|
462
|
+
|
|
463
|
+
return { baseFee, internationalFee, subscriptionFee, totalFee, netRevenue };
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Aggregate fees preserve per-transaction flat fees
|
|
467
|
+
export function calculateAggregatePolarFees(transactionAmounts: number[]): {
|
|
468
|
+
totalFees: number;
|
|
469
|
+
totalNetRevenue: number;
|
|
470
|
+
} {
|
|
471
|
+
let totalFees = 0;
|
|
472
|
+
let totalNetRevenue = 0;
|
|
473
|
+
|
|
474
|
+
for (const amount of transactionAmounts) {
|
|
475
|
+
const { totalFee, netRevenue } = calculatePolarFees(amount);
|
|
476
|
+
totalFees += totalFee;
|
|
477
|
+
totalNetRevenue += netRevenue;
|
|
236
478
|
}
|
|
237
479
|
|
|
238
|
-
return
|
|
480
|
+
return { totalFees, totalNetRevenue };
|
|
239
481
|
}
|
|
240
482
|
```
|
|
241
483
|
|
|
242
|
-
|
|
484
|
+
## Discount Management
|
|
485
|
+
|
|
486
|
+
### Discount Validation with Timeout
|
|
243
487
|
```typescript
|
|
244
|
-
//
|
|
245
|
-
const
|
|
488
|
+
// lib/polar-discounts.ts
|
|
489
|
+
const VALIDATION_TIMEOUT_MS = 15000;
|
|
490
|
+
|
|
491
|
+
export async function validateDiscount(
|
|
492
|
+
code: string,
|
|
493
|
+
productId: string
|
|
494
|
+
): Promise<{ valid: boolean; discount?: PolarDiscount; reason?: string }> {
|
|
495
|
+
const sanitizedCode = code.trim().toUpperCase();
|
|
496
|
+
if (!sanitizedCode) {
|
|
497
|
+
return { valid: false, reason: 'Code cannot be empty' };
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const polar = getPolar();
|
|
501
|
+
const env = getPolarEnv();
|
|
502
|
+
|
|
503
|
+
try {
|
|
504
|
+
// Race against timeout
|
|
505
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
506
|
+
setTimeout(() => reject(new Error('Validation timeout')), VALIDATION_TIMEOUT_MS);
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
const searchPromise = polar.discounts.list({
|
|
510
|
+
organizationId: env.POLAR_ORGANIZATION_ID,
|
|
511
|
+
query: sanitizedCode,
|
|
512
|
+
limit: 100,
|
|
513
|
+
});
|
|
246
514
|
|
|
247
|
-
|
|
248
|
-
eventQueue.push({ external_customer_id: userId, ...event });
|
|
515
|
+
const result = await Promise.race([searchPromise, timeoutPromise]);
|
|
249
516
|
|
|
250
|
-
|
|
251
|
-
|
|
517
|
+
// Find exact match
|
|
518
|
+
const discount = result.items.find(d =>
|
|
519
|
+
d.code?.toUpperCase() === sanitizedCode
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
if (!discount) {
|
|
523
|
+
return { valid: false, reason: 'Code not found' };
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Check eligibility
|
|
527
|
+
const now = new Date();
|
|
528
|
+
if (discount.startsAt && now < new Date(discount.startsAt)) {
|
|
529
|
+
return { valid: false, reason: `Code starts on ${discount.startsAt}` };
|
|
530
|
+
}
|
|
531
|
+
if (discount.endsAt && now > new Date(discount.endsAt)) {
|
|
532
|
+
return { valid: false, reason: 'Code has expired' };
|
|
533
|
+
}
|
|
534
|
+
if (discount.maxRedemptions && discount.redemptionsCount >= discount.maxRedemptions) {
|
|
535
|
+
return { valid: false, reason: 'Code redemption limit reached' };
|
|
536
|
+
}
|
|
537
|
+
if (!discount.products?.some(p => p.id === productId)) {
|
|
538
|
+
return { valid: false, reason: 'Code not valid for this product' };
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return { valid: true, discount };
|
|
542
|
+
|
|
543
|
+
} catch (error) {
|
|
544
|
+
console.error('Discount validation error:', error);
|
|
545
|
+
return { valid: false, reason: 'Validation failed - please try again' };
|
|
252
546
|
}
|
|
253
547
|
}
|
|
548
|
+
```
|
|
254
549
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
550
|
+
### VND Conversion for Discounts
|
|
551
|
+
```typescript
|
|
552
|
+
const VND_TO_USD_RATE = 25000; // 1 USD = 25,000 VND
|
|
553
|
+
|
|
554
|
+
export function convertDiscountToVND(discount: PolarDiscount, amountVND: number): number {
|
|
555
|
+
if (discount.type === 'percentage') {
|
|
556
|
+
// Basis points: 1000 = 10%, 10000 = 100%
|
|
557
|
+
const percentage = discount.basisPoints / 10000;
|
|
558
|
+
return Math.round(amountVND * percentage);
|
|
559
|
+
} else {
|
|
560
|
+
// Fixed amount in USD cents → VND
|
|
561
|
+
const amountUSD = discount.amount / 100;
|
|
562
|
+
return Math.round(amountUSD * VND_TO_USD_RATE);
|
|
563
|
+
}
|
|
260
564
|
}
|
|
261
565
|
```
|
|
262
566
|
|
|
263
|
-
###
|
|
567
|
+
### Syncing SePay Redemptions to Polar
|
|
264
568
|
```typescript
|
|
265
|
-
|
|
266
|
-
|
|
569
|
+
// lib/polar-discount-sync.ts
|
|
570
|
+
// When SePay payment completes, decrement Polar discount redemptions
|
|
571
|
+
|
|
572
|
+
export async function syncPolarDiscountRedemption(
|
|
573
|
+
orderId: string,
|
|
574
|
+
discountId: string,
|
|
575
|
+
discountCode: string
|
|
576
|
+
): Promise<{ success: boolean; action: string }> {
|
|
577
|
+
const order = await db.select().from(orders).where(eq(orders.id, orderId)).limit(1);
|
|
578
|
+
if (!order[0]) {
|
|
579
|
+
return { success: false, action: 'order_not_found' };
|
|
580
|
+
}
|
|
267
581
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
582
|
+
// Idempotency check
|
|
583
|
+
const metadata = order[0].metadata ? JSON.parse(order[0].metadata) : {};
|
|
584
|
+
if (metadata.polarDiscountSynced) {
|
|
585
|
+
return { success: true, action: 'already_synced' };
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const polar = getPolar();
|
|
589
|
+
|
|
590
|
+
try {
|
|
591
|
+
const discount = await polar.discounts.get({ id: discountId });
|
|
592
|
+
|
|
593
|
+
if (discount.maxRedemptions === null || discount.maxRedemptions === undefined) {
|
|
594
|
+
return { success: true, action: 'skipped_unlimited' };
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
const currentMax = discount.maxRedemptions;
|
|
598
|
+
|
|
599
|
+
if (currentMax <= 1) {
|
|
600
|
+
await polar.discounts.delete({ id: discountId });
|
|
601
|
+
await markOrderSynced(orderId, 'deleted');
|
|
602
|
+
} else {
|
|
603
|
+
await polar.discounts.update({
|
|
604
|
+
id: discountId,
|
|
605
|
+
discountUpdate: { maxRedemptions: currentMax - 1 },
|
|
606
|
+
});
|
|
607
|
+
await markOrderSynced(orderId, 'decremented');
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return { success: true, action: currentMax <= 1 ? 'deleted' : 'decremented' };
|
|
611
|
+
|
|
612
|
+
} catch (error: any) {
|
|
613
|
+
if (error.statusCode === 404) {
|
|
614
|
+
// Already deleted - treat as success
|
|
615
|
+
await markOrderSynced(orderId, 'already_deleted');
|
|
616
|
+
return { success: true, action: 'already_deleted' };
|
|
279
617
|
}
|
|
618
|
+
throw error;
|
|
280
619
|
}
|
|
281
620
|
}
|
|
282
|
-
```
|
|
283
621
|
|
|
284
|
-
|
|
622
|
+
async function markOrderSynced(orderId: string, action: string) {
|
|
623
|
+
const order = await db.select().from(orders).where(eq(orders.id, orderId)).limit(1);
|
|
624
|
+
const metadata = order[0].metadata ? JSON.parse(order[0].metadata) : {};
|
|
285
625
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
checkouts_created: counter('polar_checkouts_created_total'),
|
|
290
|
-
orders_completed: counter('polar_orders_completed_total'),
|
|
291
|
-
subscriptions_active: gauge('polar_subscriptions_active'),
|
|
292
|
-
revenue: counter('polar_revenue_total'),
|
|
293
|
-
webhook_processing_time: histogram('polar_webhook_duration_seconds')
|
|
294
|
-
};
|
|
626
|
+
metadata.polarDiscountSynced = true;
|
|
627
|
+
metadata.polarDiscountSyncAction = action;
|
|
628
|
+
metadata.polarDiscountSyncedAt = new Date().toISOString();
|
|
295
629
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
630
|
+
await db.update(orders)
|
|
631
|
+
.set({ metadata: JSON.stringify(metadata) })
|
|
632
|
+
.where(eq(orders.id, orderId));
|
|
633
|
+
}
|
|
300
634
|
```
|
|
301
635
|
|
|
302
|
-
|
|
636
|
+
## Revenue Tracking with Caching
|
|
637
|
+
|
|
303
638
|
```typescript
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
639
|
+
// lib/polar.ts
|
|
640
|
+
const REVENUE_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
641
|
+
|
|
642
|
+
let revenueCache: {
|
|
643
|
+
data: { totalRevenueCents: number; orderCount: number } | null;
|
|
644
|
+
timestamp: number;
|
|
645
|
+
} = { data: null, timestamp: 0 };
|
|
646
|
+
|
|
647
|
+
export async function getPolarApiRevenue(): Promise<{
|
|
648
|
+
totalRevenueCents: number;
|
|
649
|
+
orderCount: number;
|
|
650
|
+
fromCache: boolean;
|
|
651
|
+
}> {
|
|
652
|
+
const now = Date.now();
|
|
653
|
+
|
|
654
|
+
// Return cache if valid
|
|
655
|
+
if (revenueCache.data && now - revenueCache.timestamp < REVENUE_CACHE_TTL_MS) {
|
|
656
|
+
return { ...revenueCache.data, fromCache: true };
|
|
657
|
+
}
|
|
310
658
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
user_id: order.customer.external_id,
|
|
314
|
-
amount: order.amount,
|
|
315
|
-
billing_reason: order.billing_reason
|
|
316
|
-
});
|
|
659
|
+
const polar = getPolar();
|
|
660
|
+
const env = getPolarEnv();
|
|
317
661
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
662
|
+
try {
|
|
663
|
+
let totalRevenueCents = 0;
|
|
664
|
+
let orderCount = 0;
|
|
665
|
+
let page = 1;
|
|
666
|
+
const maxPages = 100; // Safety limit
|
|
667
|
+
|
|
668
|
+
while (page <= maxPages) {
|
|
669
|
+
const response = await polar.orders.list({
|
|
670
|
+
organizationId: env.POLAR_ORGANIZATION_ID,
|
|
671
|
+
page,
|
|
672
|
+
limit: 100,
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
for (const order of response.items) {
|
|
676
|
+
if (order.status === 'succeeded') {
|
|
677
|
+
totalRevenueCents += order.netAmount; // After discounts, before tax
|
|
678
|
+
orderCount++;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
324
681
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (webhookFailures > threshold) {
|
|
329
|
-
alert.send({
|
|
330
|
-
severity: 'high',
|
|
331
|
-
message: 'Polar webhook failures exceed threshold',
|
|
332
|
-
details: { failures: webhookFailures, threshold }
|
|
333
|
-
});
|
|
334
|
-
}
|
|
682
|
+
if (!response.pagination.hasMore) break;
|
|
683
|
+
page++;
|
|
684
|
+
}
|
|
335
685
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
686
|
+
revenueCache = { data: { totalRevenueCents, orderCount }, timestamp: now };
|
|
687
|
+
return { totalRevenueCents, orderCount, fromCache: false };
|
|
688
|
+
|
|
689
|
+
} catch (error) {
|
|
690
|
+
// Return stale cache on error
|
|
691
|
+
if (revenueCache.data) {
|
|
692
|
+
console.warn('Using stale revenue cache due to API error');
|
|
693
|
+
return { ...revenueCache.data, fromCache: true };
|
|
694
|
+
}
|
|
695
|
+
throw error;
|
|
696
|
+
}
|
|
343
697
|
}
|
|
344
698
|
```
|
|
345
699
|
|
|
346
|
-
##
|
|
700
|
+
## Error Handling Patterns
|
|
347
701
|
|
|
348
|
-
###
|
|
702
|
+
### Fail-Open for Non-Critical Operations
|
|
349
703
|
```typescript
|
|
350
|
-
//
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
})
|
|
355
|
-
|
|
356
|
-
//
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
// 3. Subscription creation
|
|
360
|
-
// 4. Subscription upgrade/downgrade
|
|
361
|
-
// 5. Subscription cancellation
|
|
362
|
-
// 6. Refund processing
|
|
363
|
-
// 7. Webhook delivery
|
|
364
|
-
// 8. Benefit granting/revoking
|
|
704
|
+
// Discount creation fails → proceed with full price
|
|
705
|
+
try {
|
|
706
|
+
const discount = await createReferralDiscount(productId, amount, referralCode);
|
|
707
|
+
polarDiscountId = discount.id;
|
|
708
|
+
} catch (error) {
|
|
709
|
+
console.error('⚠️ Discount creation failed - proceeding with full price:', error);
|
|
710
|
+
// Flag for manual refund investigation
|
|
711
|
+
await flagOrderForReview(orderId, 'discount_creation_failed');
|
|
712
|
+
}
|
|
365
713
|
```
|
|
366
714
|
|
|
367
|
-
###
|
|
715
|
+
### Graceful Degradation in Webhooks
|
|
368
716
|
```typescript
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
717
|
+
// Non-critical operations don't block order completion
|
|
718
|
+
const operations = [
|
|
719
|
+
{ name: 'GitHub invite', fn: () => inviteToGitHub(username, productType) },
|
|
720
|
+
{ name: 'Welcome email', fn: () => sendWelcomeEmail(order) },
|
|
721
|
+
{ name: 'Discord notification', fn: () => sendSalesNotification(order) },
|
|
722
|
+
{ name: 'Tier update', fn: () => updateReferrerTier(referrerId, revenueUsd) },
|
|
723
|
+
];
|
|
724
|
+
|
|
725
|
+
for (const op of operations) {
|
|
726
|
+
try {
|
|
727
|
+
await op.fn();
|
|
728
|
+
} catch (error) {
|
|
729
|
+
console.error(`❌ ${op.name} failed:`, error);
|
|
730
|
+
// Continue processing - don't block order
|
|
731
|
+
}
|
|
732
|
+
}
|
|
374
733
|
```
|
|
375
734
|
|
|
376
|
-
###
|
|
735
|
+
### Rate Limit Handling with Exponential Backoff
|
|
377
736
|
```typescript
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
737
|
+
async function callWithRetry<T>(
|
|
738
|
+
fn: () => Promise<T>,
|
|
739
|
+
maxRetries: number = 3
|
|
740
|
+
): Promise<T> {
|
|
741
|
+
let attempt = 0;
|
|
382
742
|
|
|
383
|
-
|
|
384
|
-
|
|
743
|
+
while (attempt < maxRetries) {
|
|
744
|
+
try {
|
|
745
|
+
return await fn();
|
|
746
|
+
} catch (error: any) {
|
|
747
|
+
if (error.statusCode === 429) {
|
|
748
|
+
const retryAfter = parseInt(error.headers?.['retry-after'] || '1', 10);
|
|
749
|
+
const delay = retryAfter * 1000 * Math.pow(2, attempt);
|
|
750
|
+
console.log(`Rate limited, retrying in ${delay}ms...`);
|
|
751
|
+
await sleep(delay);
|
|
752
|
+
attempt++;
|
|
753
|
+
} else {
|
|
754
|
+
throw error;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
385
758
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
expect(order.status).toBe('paid');
|
|
389
|
-
});
|
|
390
|
-
});
|
|
759
|
+
throw new Error('Max retries exceeded');
|
|
760
|
+
}
|
|
391
761
|
```
|
|
392
762
|
|
|
393
|
-
##
|
|
394
|
-
|
|
395
|
-
- [ ] Environment variables configured
|
|
396
|
-
- [ ] Sandbox testing completed
|
|
397
|
-
- [ ] Production tokens obtained
|
|
398
|
-
- [ ] Webhook endpoint configured and tested
|
|
399
|
-
- [ ] Webhook signature verification implemented
|
|
400
|
-
- [ ] Error monitoring enabled
|
|
401
|
-
- [ ] Logging infrastructure ready
|
|
402
|
-
- [ ] Metrics collection configured
|
|
403
|
-
- [ ] Alerting rules set up
|
|
404
|
-
- [ ] Database indexes created
|
|
405
|
-
- [ ] Rate limiting handled
|
|
406
|
-
- [ ] Customer portal linked
|
|
407
|
-
- [ ] Email notifications configured
|
|
408
|
-
- [ ] Refund policy documented
|
|
409
|
-
- [ ] Support process established
|
|
410
|
-
- [ ] Team trained on platform
|
|
411
|
-
- [ ] Documentation updated
|
|
412
|
-
- [ ] Compliance requirements met (if applicable)
|
|
413
|
-
- [ ] Acceptable use policy reviewed
|
|
763
|
+
## Database Schema
|
|
414
764
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
### 1. Not Verifying Webhooks
|
|
765
|
+
### Orders Table
|
|
418
766
|
```typescript
|
|
419
|
-
//
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
767
|
+
// db/schema/orders.ts
|
|
768
|
+
export const orders = pgTable('orders', {
|
|
769
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
770
|
+
userId: uuid('user_id').references(() => users.id),
|
|
771
|
+
email: text('email').notNull(),
|
|
772
|
+
productType: text('product_type').notNull(),
|
|
773
|
+
amount: integer('amount').notNull(), // Final amount in cents
|
|
774
|
+
originalAmount: integer('original_amount'), // Before discounts
|
|
775
|
+
currency: text('currency').default('USD'),
|
|
776
|
+
status: text('status').default('pending'), // pending, completed, failed, refunded
|
|
777
|
+
paymentProvider: text('payment_provider').notNull(), // 'polar' or 'sepay'
|
|
778
|
+
paymentId: text('payment_id'), // External payment ID
|
|
779
|
+
referredBy: uuid('referred_by').references(() => users.id),
|
|
780
|
+
discountAmount: integer('discount_amount').default(0),
|
|
781
|
+
discountRate: numeric('discount_rate', { precision: 5, scale: 2 }),
|
|
782
|
+
metadata: text('metadata'), // JSON with audit trail
|
|
783
|
+
createdAt: timestamp('created_at').defaultNow(),
|
|
784
|
+
updatedAt: timestamp('updated_at').defaultNow(),
|
|
428
785
|
});
|
|
429
786
|
```
|
|
430
787
|
|
|
431
|
-
###
|
|
788
|
+
### Webhook Events Table (Idempotency)
|
|
432
789
|
```typescript
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
}
|
|
444
|
-
// Still wait for webhook for fulfillment
|
|
790
|
+
export const webhookEvents = pgTable('webhook_events', {
|
|
791
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
792
|
+
provider: text('provider').notNull(), // 'polar' or 'sepay'
|
|
793
|
+
eventType: text('event_type').notNull(),
|
|
794
|
+
eventId: text('event_id').notNull().unique(), // Idempotency key
|
|
795
|
+
payload: text('payload').notNull(),
|
|
796
|
+
processed: boolean('processed').default(false),
|
|
797
|
+
processedAt: timestamp('processed_at'),
|
|
798
|
+
error: text('error'),
|
|
799
|
+
createdAt: timestamp('created_at').defaultNow(),
|
|
445
800
|
});
|
|
446
801
|
```
|
|
447
802
|
|
|
448
|
-
|
|
449
|
-
```typescript
|
|
450
|
-
// ✓ Good: Idempotent webhook handling
|
|
451
|
-
async function handleOrderPaid(order) {
|
|
452
|
-
const existing = await db.orders.findOne({ polar_id: order.id });
|
|
453
|
-
if (existing) {
|
|
454
|
-
console.log('Order already processed');
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
803
|
+
## Metadata Best Practices
|
|
457
804
|
|
|
458
|
-
|
|
459
|
-
|
|
805
|
+
### Comprehensive Audit Trail
|
|
806
|
+
```typescript
|
|
807
|
+
// Store everything needed for debugging and reconciliation
|
|
808
|
+
metadata: JSON.stringify({
|
|
809
|
+
// Pricing history
|
|
810
|
+
originalAmount: 9900,
|
|
811
|
+
|
|
812
|
+
// Coupon tracking
|
|
813
|
+
couponCode: 'LAUNCH20',
|
|
814
|
+
couponDiscountAmount: 1980,
|
|
815
|
+
|
|
816
|
+
// Referral tracking
|
|
817
|
+
referralCode: 'ABC12345',
|
|
818
|
+
referralDiscountAmount: 1584,
|
|
819
|
+
referrerId: 'user-uuid',
|
|
820
|
+
|
|
821
|
+
// Customer context
|
|
822
|
+
githubUsername: 'customer',
|
|
823
|
+
|
|
824
|
+
// Polar integration
|
|
825
|
+
polarDiscountId: 'disc_xxx',
|
|
826
|
+
polarDiscountSynced: true,
|
|
827
|
+
polarDiscountSyncAction: 'decremented',
|
|
828
|
+
polarDiscountSyncedAt: '2025-01-15T10:30:00Z',
|
|
829
|
+
|
|
830
|
+
// Team context (if applicable)
|
|
831
|
+
isTeamPurchase: false,
|
|
832
|
+
teamId: null,
|
|
833
|
+
quantity: 1,
|
|
834
|
+
})
|
|
460
835
|
```
|
|
461
836
|
|
|
462
|
-
|
|
837
|
+
## Testing
|
|
838
|
+
|
|
839
|
+
### Unit Tests for Fee Calculation
|
|
463
840
|
```typescript
|
|
464
|
-
//
|
|
465
|
-
|
|
841
|
+
// __tests__/lib/polar-fees.test.ts
|
|
842
|
+
describe('calculatePolarFees', () => {
|
|
843
|
+
it('handles zero amount', () => {
|
|
844
|
+
const result = calculatePolarFees(0);
|
|
845
|
+
expect(result.totalFee).toBe(0);
|
|
846
|
+
expect(result.netRevenue).toBe(0);
|
|
847
|
+
});
|
|
466
848
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
849
|
+
it('calculates international one-time correctly', () => {
|
|
850
|
+
// $100 transaction
|
|
851
|
+
const result = calculatePolarFees(10000, true, false);
|
|
852
|
+
expect(result.baseFee).toBe(440); // 4% + $0.40
|
|
853
|
+
expect(result.internationalFee).toBe(150); // 1.5%
|
|
854
|
+
expect(result.totalFee).toBe(590);
|
|
855
|
+
expect(result.netRevenue).toBe(9410); // $94.10
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
it('preserves per-transaction flat fees in aggregate', () => {
|
|
859
|
+
// Two $100 transactions should each have $0.40 flat fee
|
|
860
|
+
const aggregate = calculateAggregatePolarFees([10000, 10000]);
|
|
861
|
+
const single = calculatePolarFees(20000);
|
|
862
|
+
|
|
863
|
+
expect(aggregate.totalFees).toBeGreaterThan(single.totalFee);
|
|
864
|
+
// Difference should be one extra flat fee ($0.40)
|
|
865
|
+
expect(aggregate.totalFees - single.totalFee).toBe(40);
|
|
866
|
+
});
|
|
471
867
|
});
|
|
472
868
|
```
|
|
473
869
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
870
|
+
## Production Checklist
|
|
871
|
+
|
|
872
|
+
- [ ] Environment variables configured in all environments
|
|
873
|
+
- [ ] Sandbox testing completed for all checkout flows
|
|
874
|
+
- [ ] Production API key obtained and secured
|
|
875
|
+
- [ ] Webhook endpoint deployed and reachable
|
|
876
|
+
- [ ] Webhook signature verification implemented
|
|
877
|
+
- [ ] Idempotency handling tested with duplicate webhooks
|
|
878
|
+
- [ ] Fee calculations verified against Polar dashboard
|
|
879
|
+
- [ ] Discount validation timeout configured
|
|
880
|
+
- [ ] Error monitoring enabled (Sentry, etc.)
|
|
881
|
+
- [ ] Structured logging in place
|
|
882
|
+
- [ ] Database indexes on orders.status, orders.paymentProvider
|
|
883
|
+
- [ ] Revenue caching configured
|
|
884
|
+
- [ ] Rate limit handling implemented
|
|
885
|
+
- [ ] Fail-open patterns for non-critical operations
|
|
886
|
+
- [ ] Customer email notifications working
|
|
887
|
+
- [ ] Refund flow tested end-to-end
|
|
888
|
+
- [ ] GitHub access grant/revoke tested
|
|
889
|
+
- [ ] Discord sales notifications configured
|
|
890
|
+
|
|
891
|
+
## Common Pitfalls
|
|
892
|
+
|
|
893
|
+
1. **Applying discounts in wrong order** - Always coupon first, then referral
|
|
894
|
+
2. **Trusting success redirect without verification** - Always verify via API or webhook
|
|
895
|
+
3. **Not handling duplicate webhooks** - Use eventId for idempotency
|
|
896
|
+
4. **Blocking webhook on non-critical failures** - Wrap in try-catch, log, continue
|
|
897
|
+
5. **Hardcoding Polar customer IDs** - Use external_id (your user ID) for lookups
|
|
898
|
+
6. **Not setting timeout on discount validation** - API can be slow
|
|
899
|
+
7. **Calculating aggregate fees as single transaction** - Each transaction has flat fee
|
|
900
|
+
8. **Exposing API keys client-side** - Always server-side
|
|
901
|
+
9. **Not preserving original amount in metadata** - Need for audit/debugging
|
|
902
|
+
10. **Syncing discount redemptions synchronously** - Can fail; use retry with backoff
|