@jairodri/huckleberry-cli 1.0.2 → 1.1.0
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/README.md +85 -0
- package/agents/cos.json +2 -5
- package/agents/hermes/AGENTS.md +25 -49
- package/agents/hermes/fix-kimaki/SKILL.md +19 -92
- package/agents/hermes/hermes-fixer.md +44 -74
- package/agents/hermes.json +6 -18
- package/kanban/SKILL.md +18 -18
- package/kanban/package.json +1 -1
- package/kanban/src/dispatcher.mjs +1 -1
- package/kanban/templates/blog-social-campaign.yaml +6 -2
- package/kanban/templates/social-campaign.yaml +9 -4
- package/mcp/jev-calibration.mjs +335 -0
- package/mcp/jev-client.mjs +119 -0
- package/mcp/jev-thresholds.json +27 -0
- package/package.json +13 -9
- package/scripts/doctor-watchdog.sh +73 -0
- package/scripts/install-services.sh +34 -27
- package/scripts/self-improve-weekly.sh +34 -0
- package/scripts/sync-skills.mjs +121 -0
- package/skills/ab-test-setup/SKILL.md +266 -0
- package/skills/ab-test-setup/evals/evals.json +105 -0
- package/skills/ab-test-setup/references/sample-size-guide.md +263 -0
- package/skills/ab-test-setup/references/test-templates.md +277 -0
- package/skills/agent-reach/SKILL.md +132 -0
- package/skills/agent-reach/references/career.md +29 -0
- package/skills/agent-reach/references/dev.md +62 -0
- package/skills/agent-reach/references/search.md +33 -0
- package/skills/agent-reach/references/social.md +232 -0
- package/skills/agent-reach/references/video.md +130 -0
- package/skills/agent-reach/references/web.md +50 -0
- package/skills/ai-agent-seo/SKILL.md +450 -0
- package/skills/ai-cold-email-intelligence/SKILL.md +54 -0
- package/skills/ai-cold-email-intelligence/references/prompts.md +21 -0
- package/skills/ai-consensus-builder/SKILL.md +324 -0
- package/skills/ai-seo/SKILL.md +399 -0
- package/skills/ai-seo/evals/evals.json +90 -0
- package/skills/ai-seo/references/content-patterns.md +284 -0
- package/skills/ai-seo/references/platform-ranking-factors.md +152 -0
- package/skills/ai-visibility-audit/SKILL.md +322 -0
- package/skills/analytics-tracking/SKILL.md +309 -0
- package/skills/analytics-tracking/evals/evals.json +90 -0
- package/skills/analytics-tracking/references/event-library.md +260 -0
- package/skills/analytics-tracking/references/ga4-implementation.md +300 -0
- package/skills/analytics-tracking/references/gtm-implementation.md +390 -0
- package/skills/article-generator/SKILL.md +216 -0
- package/skills/auto-indexing/SKILL.md +179 -0
- package/skills/auto-indexing/scripts/auto-indexing.js +257 -0
- package/skills/auto-skill/SKILL.md +151 -0
- package/skills/auto-skill/correction-queue.js +102 -0
- package/skills/auto-skill/dedup.js +97 -0
- package/skills/auto-skill/skill-router.js +224 -0
- package/skills/autoresearch/SKILL.md +334 -0
- package/skills/autoresearch/autoresearch-autoresearch/SKILL.md.baseline +329 -0
- package/skills/autoresearch/autoresearch-autoresearch/changelog.md +34 -0
- package/skills/autoresearch/autoresearch-autoresearch/dashboard.html +116 -0
- package/skills/autoresearch/autoresearch-autoresearch/evals.md +39 -0
- package/skills/autoresearch/autoresearch-autoresearch/results.json +44 -0
- package/skills/autoresearch/autoresearch-autoresearch/results.tsv +3 -0
- package/skills/autoresearch/eval-guide.md +121 -0
- package/skills/b2b-cold-outreach/SKILL.md +322 -0
- package/skills/birdclaw/SKILL.md +82 -0
- package/skills/brainstorm/SKILL.md +168 -0
- package/skills/brand-kit-playground/SKILL.md +312 -0
- package/skills/brand-kit-playground/references/html-architecture.md +316 -0
- package/skills/brand-kit-playground/references/steal-patterns.md +88 -0
- package/skills/brand-voice/SKILL.md +223 -0
- package/skills/brand-voice/references/honesty-as-positioning.md +60 -0
- package/skills/brand-voice/references/voice-modes.md +1158 -0
- package/skills/business-advisory-council/README.md +136 -0
- package/skills/business-advisory-council/SKILL.md +94 -0
- package/skills/business-advisory-council/business-advisory-council.py +492 -0
- package/skills/business-advisory-council/discord-components-decision.json +8 -0
- package/skills/business-advisory-council/examples/steve-conley-partnership.json +6 -0
- package/skills/business-advisory-council/steve-conley-call-brief.json +6 -0
- package/skills/churn-prevention/SKILL.md +305 -0
- package/skills/churn-prevention/evals/evals.json +93 -0
- package/skills/churn-prevention/references/cancel-flow-patterns.md +316 -0
- package/skills/churn-prevention/references/dunning-playbook.md +408 -0
- package/skills/claw-compactor/LICENSE +21 -0
- package/skills/claw-compactor/README.md +459 -0
- package/skills/claw-compactor/SKILL.md +329 -0
- package/skills/claw-compactor/benchmark/RESULTS.md +104 -0
- package/skills/claw-compactor/benchmark/__init__.py +1 -0
- package/skills/claw-compactor/benchmark/compressors.py +520 -0
- package/skills/claw-compactor/benchmark/data/sample_01_devops.json +82 -0
- package/skills/claw-compactor/benchmark/data/sample_02_trading.json +54 -0
- package/skills/claw-compactor/benchmark/data/sample_03_ml_short.json +38 -0
- package/skills/claw-compactor/benchmark/data/sample_04_mixed_long.json +318 -0
- package/skills/claw-compactor/benchmark/data/sample_05_sysadmin.json +56 -0
- package/skills/claw-compactor/benchmark/evaluate.py +278 -0
- package/skills/claw-compactor/benchmark/report.py +241 -0
- package/skills/claw-compactor/benchmark/results/benchmark_results.json +405 -0
- package/skills/claw-compactor/benchmark/run_benchmark.py +238 -0
- package/skills/claw-compactor/engram.yaml +49 -0
- package/skills/claw-compactor/proxy/README.md +87 -0
- package/skills/claw-compactor/proxy/dashboard.html +804 -0
- package/skills/claw-compactor/proxy/event-log.mjs +213 -0
- package/skills/claw-compactor/proxy/fair-queue.mjs +288 -0
- package/skills/claw-compactor/proxy/metrics-store.mjs +425 -0
- package/skills/claw-compactor/proxy/package-lock.json +125 -0
- package/skills/claw-compactor/proxy/package.json +15 -0
- package/skills/claw-compactor/proxy/portal.html +360 -0
- package/skills/claw-compactor/proxy/process-registry.mjs +447 -0
- package/skills/claw-compactor/proxy/rate-limiter.mjs +117 -0
- package/skills/claw-compactor/proxy/redis-client.mjs +75 -0
- package/skills/claw-compactor/proxy/retry.mjs +182 -0
- package/skills/claw-compactor/proxy/server.mjs +2100 -0
- package/skills/claw-compactor/proxy/session-affinity.mjs +177 -0
- package/skills/claw-compactor/proxy/start.sh +23 -0
- package/skills/claw-compactor/proxy/test/event-log.test.mjs +237 -0
- package/skills/claw-compactor/proxy/test/helpers.mjs +103 -0
- package/skills/claw-compactor/proxy/test/integration.test.mjs +297 -0
- package/skills/claw-compactor/proxy/test/metrics-store.test.mjs +243 -0
- package/skills/claw-compactor/proxy/test/process-registry.test.mjs +390 -0
- package/skills/claw-compactor/proxy/test/rate-limiter.test.mjs +121 -0
- package/skills/claw-compactor/proxy/test/redis-client.test.mjs +62 -0
- package/skills/claw-compactor/proxy/test/token-tracker.test.mjs +200 -0
- package/skills/claw-compactor/proxy/token-tracker.mjs +482 -0
- package/skills/claw-compactor/pyproject.toml +26 -0
- package/skills/claw-compactor/references/README.md +44 -0
- package/skills/claw-compactor/references/architecture.md +197 -0
- package/skills/claw-compactor/references/benchmarks.md +94 -0
- package/skills/claw-compactor/references/compression-prompts.md +39 -0
- package/skills/claw-compactor/references/compression-techniques.md +219 -0
- package/skills/claw-compactor/references/testing.md +160 -0
- package/skills/claw-compactor/scripts/audit_memory.py +216 -0
- package/skills/claw-compactor/scripts/compress_memory.py +236 -0
- package/skills/claw-compactor/scripts/compressed_context.py +280 -0
- package/skills/claw-compactor/scripts/dedup_memory.py +147 -0
- package/skills/claw-compactor/scripts/dictionary_compress.py +170 -0
- package/skills/claw-compactor/scripts/engram-auto.sh +152 -0
- package/skills/claw-compactor/scripts/engram_auto.py +695 -0
- package/skills/claw-compactor/scripts/engram_cli.py +474 -0
- package/skills/claw-compactor/scripts/estimate_tokens.py +131 -0
- package/skills/claw-compactor/scripts/generate_summary_tiers.py +292 -0
- package/skills/claw-compactor/scripts/lib/__init__.py +34 -0
- package/skills/claw-compactor/scripts/lib/config.py +289 -0
- package/skills/claw-compactor/scripts/lib/dedup.py +119 -0
- package/skills/claw-compactor/scripts/lib/dictionary.py +281 -0
- package/skills/claw-compactor/scripts/lib/engram.py +793 -0
- package/skills/claw-compactor/scripts/lib/engram_prompts.py +175 -0
- package/skills/claw-compactor/scripts/lib/engram_storage.py +254 -0
- package/skills/claw-compactor/scripts/lib/exceptions.py +24 -0
- package/skills/claw-compactor/scripts/lib/markdown.py +312 -0
- package/skills/claw-compactor/scripts/lib/rle.py +165 -0
- package/skills/claw-compactor/scripts/lib/tokenizer_optimizer.py +188 -0
- package/skills/claw-compactor/scripts/lib/tokens.py +68 -0
- package/skills/claw-compactor/scripts/mem_compress.py +788 -0
- package/skills/claw-compactor/scripts/observation_compressor.py +346 -0
- package/skills/claw-compactor/tests/__init__.py +0 -0
- package/skills/claw-compactor/tests/conftest.py +94 -0
- package/skills/claw-compactor/tests/test_audit_comprehensive.py +123 -0
- package/skills/claw-compactor/tests/test_audit_memory.py +67 -0
- package/skills/claw-compactor/tests/test_benchmark.py +122 -0
- package/skills/claw-compactor/tests/test_cli_commands.py +228 -0
- package/skills/claw-compactor/tests/test_compress_memory.py +109 -0
- package/skills/claw-compactor/tests/test_compress_memory_comprehensive.py +191 -0
- package/skills/claw-compactor/tests/test_compressed_context.py +145 -0
- package/skills/claw-compactor/tests/test_config.py +54 -0
- package/skills/claw-compactor/tests/test_dedup_memory.py +124 -0
- package/skills/claw-compactor/tests/test_dictionary.py +327 -0
- package/skills/claw-compactor/tests/test_dictionary_comprehensive.py +241 -0
- package/skills/claw-compactor/tests/test_engram.py +1091 -0
- package/skills/claw-compactor/tests/test_engram_auto.py +529 -0
- package/skills/claw-compactor/tests/test_error_handling.py +194 -0
- package/skills/claw-compactor/tests/test_estimate_tokens.py +99 -0
- package/skills/claw-compactor/tests/test_generate_summary_tiers.py +105 -0
- package/skills/claw-compactor/tests/test_integration.py +249 -0
- package/skills/claw-compactor/tests/test_lib_dedup.py +135 -0
- package/skills/claw-compactor/tests/test_lib_markdown.py +388 -0
- package/skills/claw-compactor/tests/test_lib_tokens.py +82 -0
- package/skills/claw-compactor/tests/test_main_entry.py +176 -0
- package/skills/claw-compactor/tests/test_markdown_advanced.py +284 -0
- package/skills/claw-compactor/tests/test_new_features.py +77 -0
- package/skills/claw-compactor/tests/test_observation_comprehensive.py +261 -0
- package/skills/claw-compactor/tests/test_observation_compressor.py +128 -0
- package/skills/claw-compactor/tests/test_performance.py +172 -0
- package/skills/claw-compactor/tests/test_pipeline.py +109 -0
- package/skills/claw-compactor/tests/test_real_workspace.py +161 -0
- package/skills/claw-compactor/tests/test_rle.py +114 -0
- package/skills/claw-compactor/tests/test_rle_comprehensive.py +151 -0
- package/skills/claw-compactor/tests/test_roundtrip.py +253 -0
- package/skills/claw-compactor/tests/test_roundtrip_comprehensive.py +192 -0
- package/skills/claw-compactor/tests/test_tiers_comprehensive.py +171 -0
- package/skills/claw-compactor/tests/test_token_economics.py +237 -0
- package/skills/claw-compactor/tests/test_tokenizer_optimizer.py +175 -0
- package/skills/claw-compactor/tests/test_tokenizer_optimizer_comprehensive.py +241 -0
- package/skills/claw-compactor/tests/test_tokens.py +27 -0
- package/skills/cmo/CHANGELOG.md +61 -0
- package/skills/cmo/SKILL.md +500 -0
- package/skills/cmo/references/ai-slop-patterns.md +56 -0
- package/skills/cmo/references/analytics-guide.md +152 -0
- package/skills/cmo/references/ideas-library.md +197 -0
- package/skills/cmo/rules/brand-file-map.md +198 -0
- package/skills/cmo/rules/brand-memory.md +158 -0
- package/skills/cmo/rules/cli-runtime-index.md +95 -0
- package/skills/cmo/rules/command-reference.md +167 -0
- package/skills/cmo/rules/communication.md +15 -0
- package/skills/cmo/rules/context-switch.md +101 -0
- package/skills/cmo/rules/ecosystem.md +125 -0
- package/skills/cmo/rules/error-recovery.md +98 -0
- package/skills/cmo/rules/learning-loop.md +170 -0
- package/skills/cmo/rules/monorepo.md +78 -0
- package/skills/cmo/rules/output-format.md +124 -0
- package/skills/cmo/rules/persona.md +15 -0
- package/skills/cmo/rules/playbooks.md +327 -0
- package/skills/cmo/rules/progressive-enhancement.md +231 -0
- package/skills/cmo/rules/publish-index.md +106 -0
- package/skills/cmo/rules/quality-gate.md +90 -0
- package/skills/cmo/rules/recency-grounding.md +124 -0
- package/skills/cmo/rules/safety.md +80 -0
- package/skills/cmo/rules/studio-api-index.md +138 -0
- package/skills/cmo/rules/studio-integration.md +147 -0
- package/skills/cmo/rules/sub-agents.md +167 -0
- package/skills/cold-email/SKILL.md +240 -0
- package/skills/cold-email/evals/evals.json +94 -0
- package/skills/cold-email/references/benchmarks.md +83 -0
- package/skills/cold-email/references/follow-up-sequences.md +81 -0
- package/skills/cold-email/references/frameworks.md +90 -0
- package/skills/cold-email/references/personalization.md +79 -0
- package/skills/cold-email/references/subject-lines.md +53 -0
- package/skills/competitive-intel/SKILL.md +433 -0
- package/skills/competitive-intel/references/competitive-frameworks.md +145 -0
- package/skills/competitor-alternatives/SKILL.md +362 -0
- package/skills/competitor-alternatives/evals/evals.json +93 -0
- package/skills/competitor-alternatives/references/content-architecture.md +271 -0
- package/skills/competitor-alternatives/references/templates.md +223 -0
- package/skills/concreteness-fixer/SKILL.md +483 -0
- package/skills/concreteness-fixer/scripts/validation/concreteness-validator.js +221 -0
- package/skills/concreteness-fixer/utils/execFileNoThrow.js +16 -0
- package/skills/content-atomizer/SKILL.md +424 -0
- package/skills/content-atomizer/references/hook-strategies.md +56 -0
- package/skills/content-atomizer/references/platform-specs.md +118 -0
- package/skills/content-atomizer/references/platforms/bluesky.md +64 -0
- package/skills/content-atomizer/references/platforms/instagram.md +77 -0
- package/skills/content-atomizer/references/platforms/linkedin.md +120 -0
- package/skills/content-atomizer/references/platforms/reddit.md +73 -0
- package/skills/content-atomizer/references/platforms/threads.md +64 -0
- package/skills/content-atomizer/references/platforms/tiktok.md +55 -0
- package/skills/content-atomizer/references/platforms/twitter.md +123 -0
- package/skills/content-atomizer/references/platforms/youtube.md +56 -0
- package/skills/content-atomizer/references/tweet-patterns.md +40 -0
- package/skills/content-repost-manager/SKILL.md +245 -0
- package/skills/content-repost-manager/scripts/content-repost-scan.py +133 -0
- package/skills/context-recovery/.clawdhub/origin.json +7 -0
- package/skills/context-recovery/SKILL.md +198 -0
- package/skills/context-recovery/_meta.json +6 -0
- package/skills/conversion-flow-cro/SKILL.md +248 -0
- package/skills/conversion-flow-cro/references/flow-benchmarks.md +158 -0
- package/skills/copy-editing/SKILL.md +474 -0
- package/skills/copy-editing/evals/evals.json +89 -0
- package/skills/copy-editing/references/plain-english-alternatives.md +394 -0
- package/skills/copywriting/SKILL.md +349 -0
- package/skills/copywriting/evals/evals.json +111 -0
- package/skills/copywriting/references/awareness-level-guide.md +227 -0
- package/skills/copywriting/references/copy-frameworks.md +389 -0
- package/skills/copywriting/references/natural-transitions.md +272 -0
- package/skills/creative/SKILL.md +365 -0
- package/skills/creative/modes/ad-creative.md +1748 -0
- package/skills/creative/modes/product-photo.md +1520 -0
- package/skills/creative/modes/product-video.md +1657 -0
- package/skills/creative/modes/social-graphics.md +1511 -0
- package/skills/creative/modes/talking-head.md +2208 -0
- package/skills/creative/references/VISUAL_INTELLIGENCE.md +1233 -0
- package/skills/deep-serp-article/SKILL.md +677 -0
- package/skills/deep-serp-article/SKILL.md.bak +530 -0
- package/skills/earned-media/SKILL.md +260 -0
- package/skills/email-sequences/SKILL.md +446 -0
- package/skills/email-sequences/references/sequence-templates.md +1217 -0
- package/skills/firecrawl/.skill-meta.json +13 -0
- package/skills/firecrawl/SKILL.md +154 -0
- package/skills/firecrawl/rules/install.md +63 -0
- package/skills/firecrawl/rules/security.md +26 -0
- package/skills/form-cro/SKILL.md +429 -0
- package/skills/form-cro/evals/evals.json +90 -0
- package/skills/free-tool-strategy/SKILL.md +325 -0
- package/skills/free-tool-strategy/evals/evals.json +90 -0
- package/skills/free-tool-strategy/references/tool-types.md +253 -0
- package/skills/frontend-slides/SKILL.md +248 -0
- package/skills/frontend-slides/STYLE_PRESETS.md +347 -0
- package/skills/frontend-slides/animation-patterns.md +110 -0
- package/skills/frontend-slides/html-template.md +347 -0
- package/skills/frontend-slides/scripts/extract-pptx.py +96 -0
- package/skills/frontend-slides/viewport-base.css +153 -0
- package/skills/guardrail-framework/SKILL.md +319 -0
- package/skills/hook-generator/SKILL.md +438 -0
- package/skills/hook-generator/database/2026-PLATFORM-RESEARCH.md +211 -0
- package/skills/hook-generator/database/instagram-hooks.json +591 -0
- package/skills/hook-generator/database/linkedin-hooks.json +502 -0
- package/skills/hook-generator/database/medium-hooks.json +181 -0
- package/skills/hook-generator/database/tiktok-hooks.json +337 -0
- package/skills/hook-generator/database/twitter-hooks.json +567 -0
- package/skills/hook-generator/database/youtube-hooks.json +275 -0
- package/skills/hook-generator/package-lock.json +24 -0
- package/skills/hook-generator/package.json +5 -0
- package/skills/hook-generator/references/hook-generation-system.md +257 -0
- package/skills/hook-generator/references/hook-performance-analysis.json +153 -0
- package/skills/hook-generator/references/hooks-library.md +128 -0
- package/skills/hook-generator/references/platform-hook-frameworks.md +275 -0
- package/skills/hook-generator/scripts/generate-hooks-unified.js +722 -0
- package/skills/hook-generator/scripts/generate-hooks.js +225 -0
- package/skills/hook-generator/scripts/generate.sh +36 -0
- package/skills/hook-generator/scripts/ralph-hook-enhancer.js +864 -0
- package/skills/hook-generator/scripts/test-gemini-output.js +51 -0
- package/skills/humanizer/README.md +120 -0
- package/skills/humanizer/SKILL.md +699 -0
- package/skills/image-gen/SKILL.md +277 -0
- package/skills/image-gen/references/prompt-patterns.md +114 -0
- package/skills/image-gen/references/visual-metaphors.md +51 -0
- package/skills/impeccable/SKILL.md +86 -0
- package/skills/impeccable/reference/adapt.md +312 -0
- package/skills/impeccable/reference/adapt.native.md +58 -0
- package/skills/impeccable/reference/android.md +40 -0
- package/skills/impeccable/reference/animate.md +86 -0
- package/skills/impeccable/reference/audit.md +136 -0
- package/skills/impeccable/reference/audit.native.md +139 -0
- package/skills/impeccable/reference/bolder.md +31 -0
- package/skills/impeccable/reference/clarify.md +94 -0
- package/skills/impeccable/reference/colorize.md +86 -0
- package/skills/impeccable/reference/craft-floor.md +42 -0
- package/skills/impeccable/reference/craft.md +5 -0
- package/skills/impeccable/reference/critique.md +788 -0
- package/skills/impeccable/reference/degraded/asset-producer.md +90 -0
- package/skills/impeccable/reference/degraded/documenter.md +24 -0
- package/skills/impeccable/reference/degraded/finish-reviewer.md +37 -0
- package/skills/impeccable/reference/degraded/manual-edit-applier.md +92 -0
- package/skills/impeccable/reference/delight.md +70 -0
- package/skills/impeccable/reference/distill.md +111 -0
- package/skills/impeccable/reference/doctor.md +53 -0
- package/skills/impeccable/reference/document.md +416 -0
- package/skills/impeccable/reference/extract.md +69 -0
- package/skills/impeccable/reference/harden.md +336 -0
- package/skills/impeccable/reference/hooks.md +105 -0
- package/skills/impeccable/reference/init.md +125 -0
- package/skills/impeccable/reference/ios.md +45 -0
- package/skills/impeccable/reference/layout.md +84 -0
- package/skills/impeccable/reference/live-setup.md +102 -0
- package/skills/impeccable/reference/live.md +323 -0
- package/skills/impeccable/reference/new-work.md +110 -0
- package/skills/impeccable/reference/onboard.md +234 -0
- package/skills/impeccable/reference/operate.md +61 -0
- package/skills/impeccable/reference/optimize.md +258 -0
- package/skills/impeccable/reference/overdrive.md +127 -0
- package/skills/impeccable/reference/polish.md +97 -0
- package/skills/impeccable/reference/quieter.md +99 -0
- package/skills/impeccable/reference/routing.md +18 -0
- package/skills/impeccable/reference/shape.md +59 -0
- package/skills/impeccable/reference/typeset.md +80 -0
- package/skills/impeccable/reference/visualize.md +49 -0
- package/skills/impeccable/scripts/command-metadata.json +94 -0
- package/skills/impeccable/scripts/concept-seed.mjs +558 -0
- package/skills/impeccable/scripts/context-signals.mjs +334 -0
- package/skills/impeccable/scripts/context.mjs +1467 -0
- package/skills/impeccable/scripts/critique-storage.mjs +213 -0
- package/skills/impeccable/scripts/detect-csp.mjs +198 -0
- package/skills/impeccable/scripts/detect.mjs +21 -0
- package/skills/impeccable/scripts/detector/browser/injected/index.mjs +2023 -0
- package/skills/impeccable/scripts/detector/cli/main.mjs +438 -0
- package/skills/impeccable/scripts/detector/design-system.mjs +1046 -0
- package/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +8283 -0
- package/skills/impeccable/scripts/detector/detect-antipatterns.mjs +50 -0
- package/skills/impeccable/scripts/detector/engines/browser/detect-url.mjs +372 -0
- package/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +1168 -0
- package/skills/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +1186 -0
- package/skills/impeccable/scripts/detector/engines/static-html/detect-html.mjs +264 -0
- package/skills/impeccable/scripts/detector/engines/visual/screenshot-contrast.mjs +189 -0
- package/skills/impeccable/scripts/detector/findings.mjs +18 -0
- package/skills/impeccable/scripts/detector/node/file-system.mjs +203 -0
- package/skills/impeccable/scripts/detector/profile/profiler.mjs +166 -0
- package/skills/impeccable/scripts/detector/registry/antipatterns.mjs +617 -0
- package/skills/impeccable/scripts/detector/rules/checks.mjs +5580 -0
- package/skills/impeccable/scripts/detector/shared/color.mjs +124 -0
- package/skills/impeccable/scripts/detector/shared/constants.mjs +112 -0
- package/skills/impeccable/scripts/detector/shared/fonts.mjs +30 -0
- package/skills/impeccable/scripts/detector/shared/inline-ignores.mjs +148 -0
- package/skills/impeccable/scripts/detector/shared/page.mjs +7 -0
- package/skills/impeccable/scripts/doctor.mjs +336 -0
- package/skills/impeccable/scripts/embed-prompt.mjs +133 -0
- package/skills/impeccable/scripts/generate-image.mjs +240 -0
- package/skills/impeccable/scripts/hook-admin.mjs +801 -0
- package/skills/impeccable/scripts/hook-before-edit.mjs +508 -0
- package/skills/impeccable/scripts/hook-lib.mjs +2153 -0
- package/skills/impeccable/scripts/hook.mjs +78 -0
- package/skills/impeccable/scripts/lib/artifact-schema.mjs +93 -0
- package/skills/impeccable/scripts/lib/composition-catalog.mjs +200 -0
- package/skills/impeccable/scripts/lib/concept-catalog.mjs +384 -0
- package/skills/impeccable/scripts/lib/design-parser.mjs +925 -0
- package/skills/impeccable/scripts/lib/impeccable-config.mjs +658 -0
- package/skills/impeccable/scripts/lib/impeccable-paths.mjs +137 -0
- package/skills/impeccable/scripts/lib/is-generated.mjs +69 -0
- package/skills/impeccable/scripts/lib/provider.mjs +5 -0
- package/skills/impeccable/scripts/lib/roll-selection.mjs +362 -0
- package/skills/impeccable/scripts/lib/staleness-deep.mjs +478 -0
- package/skills/impeccable/scripts/lib/staleness-notice.mjs +169 -0
- package/skills/impeccable/scripts/lib/staleness.mjs +457 -0
- package/skills/impeccable/scripts/lib/surface-briefs.mjs +151 -0
- package/skills/impeccable/scripts/lib/target-args.mjs +42 -0
- package/skills/impeccable/scripts/lib/target-slug.mjs +33 -0
- package/skills/impeccable/scripts/lib/template-extensions.mjs +146 -0
- package/skills/impeccable/scripts/live/accept-css.mjs +617 -0
- package/skills/impeccable/scripts/live/accept-verify.mjs +60 -0
- package/skills/impeccable/scripts/live/browser-script-parts.mjs +55 -0
- package/skills/impeccable/scripts/live/completion.mjs +28 -0
- package/skills/impeccable/scripts/live/event-validation.mjs +199 -0
- package/skills/impeccable/scripts/live/frameworks/astro.mjs +47 -0
- package/skills/impeccable/scripts/live/frameworks/detect-utils.mjs +73 -0
- package/skills/impeccable/scripts/live/frameworks/index.mjs +143 -0
- package/skills/impeccable/scripts/live/frameworks/journal.mjs +197 -0
- package/skills/impeccable/scripts/live/frameworks/nextjs.mjs +49 -0
- package/skills/impeccable/scripts/live/frameworks/nuxt.mjs +161 -0
- package/skills/impeccable/scripts/live/frameworks/script-src.mjs +17 -0
- package/skills/impeccable/scripts/live/frameworks/static-html.mjs +26 -0
- package/skills/impeccable/scripts/live/frameworks/sveltekit.mjs +71 -0
- package/skills/impeccable/scripts/live/frameworks/tag-strategy.mjs +247 -0
- package/skills/impeccable/scripts/live/frameworks/tanstack-start.mjs +70 -0
- package/skills/impeccable/scripts/live/frameworks/vite-generic.mjs +42 -0
- package/skills/impeccable/scripts/live/generation-preflight.mjs +149 -0
- package/skills/impeccable/scripts/live/insert-ui.mjs +458 -0
- package/skills/impeccable/scripts/live/instructions.mjs +142 -0
- package/skills/impeccable/scripts/live/manual-apply.mjs +939 -0
- package/skills/impeccable/scripts/live/manual-edit-routes.mjs +357 -0
- package/skills/impeccable/scripts/live/manual-edits-buffer.mjs +152 -0
- package/skills/impeccable/scripts/live/poll-lanes.mjs +14 -0
- package/skills/impeccable/scripts/live/roots.mjs +508 -0
- package/skills/impeccable/scripts/live/session-store.mjs +563 -0
- package/skills/impeccable/scripts/live/source-lock.mjs +105 -0
- package/skills/impeccable/scripts/live/source-search.mjs +105 -0
- package/skills/impeccable/scripts/live/svelte-ast.mjs +961 -0
- package/skills/impeccable/scripts/live/svelte-component.mjs +1342 -0
- package/skills/impeccable/scripts/live/sveltekit-adapter.mjs +316 -0
- package/skills/impeccable/scripts/live/tanstack-adapter.mjs +280 -0
- package/skills/impeccable/scripts/live/ui-core.mjs +180 -0
- package/skills/impeccable/scripts/live/vocabulary.mjs +171 -0
- package/skills/impeccable/scripts/live-accept.mjs +954 -0
- package/skills/impeccable/scripts/live-browser-dom.js +146 -0
- package/skills/impeccable/scripts/live-browser-session.js +123 -0
- package/skills/impeccable/scripts/live-browser.js +12520 -0
- package/skills/impeccable/scripts/live-commit-manual-edits.mjs +1244 -0
- package/skills/impeccable/scripts/live-complete.mjs +107 -0
- package/skills/impeccable/scripts/live-copy-edit-agent.mjs +683 -0
- package/skills/impeccable/scripts/live-discard-manual-edits.mjs +51 -0
- package/skills/impeccable/scripts/live-inject.mjs +503 -0
- package/skills/impeccable/scripts/live-insert.mjs +292 -0
- package/skills/impeccable/scripts/live-manual-edit-evidence.mjs +368 -0
- package/skills/impeccable/scripts/live-poll.mjs +429 -0
- package/skills/impeccable/scripts/live-resume.mjs +123 -0
- package/skills/impeccable/scripts/live-server.mjs +1669 -0
- package/skills/impeccable/scripts/live-status.mjs +71 -0
- package/skills/impeccable/scripts/live-target.mjs +30 -0
- package/skills/impeccable/scripts/live-wrap.mjs +927 -0
- package/skills/impeccable/scripts/live.mjs +359 -0
- package/skills/impeccable/scripts/modern-screenshot.umd.js +14 -0
- package/skills/impeccable/scripts/palette.mjs +628 -0
- package/skills/impeccable/scripts/pin.mjs +221 -0
- package/skills/impeccable/scripts/serve-question.mjs +932 -0
- package/skills/impeccable/scripts/surface-brief.mjs +74 -0
- package/skills/inkstone-admin/SKILL.md +66 -0
- package/skills/jev-hook-gate/SKILL.md +111 -0
- package/skills/keyword-research/SKILL.md +503 -0
- package/skills/keyword-research/references/dr-cap-and-confidence.md +62 -0
- package/skills/keyword-research/references/keyword-examples.md +769 -0
- package/skills/landing-page-architect/SKILL.md +585 -0
- package/skills/landing-page-architect/references/architecture-reference.md +32 -0
- package/skills/launch-strategy/SKILL.md +384 -0
- package/skills/launch-strategy/evals/evals.json +91 -0
- package/skills/launch-strategy/references/launch-checklist.md +209 -0
- package/skills/launch-strategy/references/launch-timeline.md +159 -0
- package/skills/lead-email-craft/SKILL.md +522 -0
- package/skills/lead-magnets/SKILL.md +310 -0
- package/skills/lead-magnets/references/benchmarks.md +129 -0
- package/skills/lead-magnets/references/format-guide.md +196 -0
- package/skills/link-building-seo/SKILL.md +210 -0
- package/skills/lint-report.txt +1 -0
- package/skills/manifest.json +129 -0
- package/skills/marketing-demo/SKILL.md +249 -0
- package/skills/marketing-demo/references/remotion-examples.md +281 -0
- package/skills/marketing-ideas/SKILL.md +167 -0
- package/skills/marketing-ideas/evals/evals.json +90 -0
- package/skills/marketing-ideas/references/ideas-by-category.md +366 -0
- package/skills/marketing-psychology/SKILL.md +374 -0
- package/skills/marketing-psychology/evals/evals.json +88 -0
- package/skills/marketing-psychology/references/case-studies.md +148 -0
- package/skills/narrative-research-article/SKILL.md +469 -0
- package/skills/narrative-research-article/references/lanusse-patterns.md +166 -0
- package/skills/notebooklm/SKILL.md +674 -0
- package/skills/off-page-seo/SKILL.md +219 -0
- package/skills/onboarding-cro/SKILL.md +220 -0
- package/skills/onboarding-cro/evals/evals.json +92 -0
- package/skills/onboarding-cro/references/experiments.md +258 -0
- package/skills/original-research/SKILL.md +388 -0
- package/skills/page-cro/SKILL.md +269 -0
- package/skills/page-cro/references/cro-benchmarks.md +140 -0
- package/skills/paper-marketing/SKILL.md +448 -0
- package/skills/paper-marketing/references/agent-quality-rubric.md +68 -0
- package/skills/paper-marketing/references/brand-system-template.md +120 -0
- package/skills/paper-marketing/references/paper-mcp-rules.md +71 -0
- package/skills/paper-marketing/references/platform-conventions.md +43 -0
- package/skills/paywall-upgrade-cro/SKILL.md +227 -0
- package/skills/paywall-upgrade-cro/evals/evals.json +93 -0
- package/skills/paywall-upgrade-cro/references/experiments.md +164 -0
- package/skills/platform-formatting/SKILL.md +51 -0
- package/skills/popup-cro/SKILL.md +454 -0
- package/skills/popup-cro/evals/evals.json +94 -0
- package/skills/positioning-angles/SKILL.md +524 -0
- package/skills/positioning-angles/references/ad-matrix-and-examples.md +176 -0
- package/skills/positioning-angles/references/angle-frameworks.md +187 -0
- package/skills/positioning-angles/references/dunford-positioning.md +135 -0
- package/skills/positioning-angles/references/hormozi-offer.md +247 -0
- package/skills/positioning-angles/references/schwartz-sophistication.md +166 -0
- package/skills/positioning-angles/references/unique-mechanism.md +162 -0
- package/skills/pricing-strategy/SKILL.md +346 -0
- package/skills/pricing-strategy/evals/evals.json +90 -0
- package/skills/pricing-strategy/references/research-methods.md +152 -0
- package/skills/pricing-strategy/references/tier-structure.md +307 -0
- package/skills/programmatic-seo/SKILL.md +238 -0
- package/skills/programmatic-seo/evals/evals.json +94 -0
- package/skills/programmatic-seo/references/playbooks.md +308 -0
- package/skills/project-rag/SKILL.md +127 -0
- package/skills/quality-gate/SKILL.md +519 -0
- package/skills/quality-gate/judge-personas/buyer.md +47 -0
- package/skills/quality-gate/judge-personas/expert.md +47 -0
- package/skills/quality-gate/judge-personas/outsider.md +47 -0
- package/skills/quality-gate/judge-personas/scanner.md +41 -0
- package/skills/quality-gate/judge-personas/skeptic.md +40 -0
- package/skills/referral-program/SKILL.md +290 -0
- package/skills/referral-program/evals/evals.json +89 -0
- package/skills/referral-program/references/affiliate-programs.md +163 -0
- package/skills/referral-program/references/program-examples.md +143 -0
- package/skills/review-sites/SKILL.md +279 -0
- package/skills/revops/SKILL.md +343 -0
- package/skills/revops/evals/evals.json +91 -0
- package/skills/revops/references/automation-playbooks.md +290 -0
- package/skills/revops/references/lifecycle-definitions.md +278 -0
- package/skills/revops/references/routing-rules.md +203 -0
- package/skills/revops/references/scoring-models.md +247 -0
- package/skills/saas-product-seo/SKILL.md +324 -0
- package/skills/sales-enablement/SKILL.md +349 -0
- package/skills/sales-enablement/evals/evals.json +91 -0
- package/skills/sales-enablement/references/deck-frameworks.md +263 -0
- package/skills/sales-enablement/references/demo-scripts.md +355 -0
- package/skills/sales-enablement/references/objection-library.md +270 -0
- package/skills/sales-enablement/references/one-pager-templates.md +208 -0
- package/skills/schema-markup/SKILL.md +179 -0
- package/skills/schema-markup/evals/evals.json +87 -0
- package/skills/schema-markup/references/schema-examples.md +398 -0
- package/skills/self-improve/SKILL.md +166 -0
- package/skills/self-improving-agent/README.md +243 -0
- package/skills/self-improving-agent/SKILL.md +437 -0
- package/skills/self-improving-agent/scripts/__init__.py +16 -0
- package/skills/self-improving-agent/scripts/__pycache__/auto_detect.cpython-314.pyc +0 -0
- package/skills/self-improving-agent/scripts/__pycache__/nlm_curation.cpython-314.pyc +0 -0
- package/skills/self-improving-agent/scripts/__pycache__/self_improving_agent.cpython-314.pyc +0 -0
- package/skills/self-improving-agent/scripts/auto_detect.py +270 -0
- package/skills/self-improving-agent/scripts/init_expertise_registry.py +89 -0
- package/skills/self-improving-agent/scripts/nlm_curation.py +259 -0
- package/skills/self-improving-agent/scripts/self-improving-agent +113 -0
- package/skills/self-improving-agent/scripts/self_improving_agent.py +603 -0
- package/skills/self-improving-agent/scripts/test_curation_scorer.py +20 -0
- package/skills/self-improving-agent/scripts/test_self_improving.py +184 -0
- package/skills/send-email/SKILL.md +426 -0
- package/skills/send-email/references/batch-email-examples.md +807 -0
- package/skills/send-email/references/best-practices.md +453 -0
- package/skills/send-email/references/installation.md +123 -0
- package/skills/send-email/references/single-email-examples.md +470 -0
- package/skills/send-email/references/webhooks.md +221 -0
- package/skills/seo-audit/SKILL.md +362 -0
- package/skills/seo-audit/references/ai-writing-detection.md +200 -0
- package/skills/seo-audit/references/architecture-patterns.md +271 -0
- package/skills/seo-audit/references/schema-templates.md +427 -0
- package/skills/seo-content/SKILL.md +510 -0
- package/skills/seo-content/references/anti-ai-detection.md +184 -0
- package/skills/seo-content/references/content-structures.md +148 -0
- package/skills/seo-content/references/eeat-examples.md +235 -0
- package/skills/seo-content/references/on-page-seo.md +261 -0
- package/skills/seo-content/references/pseo-playbooks.md +226 -0
- package/skills/seo-content/references/schema-templates.md +104 -0
- package/skills/seo-content/references/serp-analysis-format.md +151 -0
- package/skills/seo-machine/SKILL.md +331 -0
- package/skills/seo-machine/assets/brand-template.md +73 -0
- package/skills/seo-machine/assets/link-inventory-template.md +74 -0
- package/skills/seo-machine/assets/roadmap-template.md +184 -0
- package/skills/seo-machine/references/api-stack-recipes.md +230 -0
- package/skills/seo-machine/references/exa-recipes.md +260 -0
- package/skills/seo-machine/references/manual-research.md +73 -0
- package/skills/seo-machine/references/methodology.md +87 -0
- package/skills/seo-machine/references/off-page.md +123 -0
- package/skills/seo-machine/references/patterns/alternatives.md +176 -0
- package/skills/seo-machine/references/patterns/compare.md +105 -0
- package/skills/seo-machine/references/patterns/playbooks.md +106 -0
- package/skills/seo-machine/references/patterns/use-case.md +109 -0
- package/skills/seo-machine/references/quality-bars.md +99 -0
- package/skills/seo-machine/references/stacks/astro.md +156 -0
- package/skills/seo-machine/references/stacks/detection.md +61 -0
- package/skills/seo-machine/references/stacks/markdown-fallback.md +181 -0
- package/skills/seo-machine/references/stacks/nextjs.md +108 -0
- package/skills/seo-machine/references/stacks/rails-inertia.md +99 -0
- package/skills/seo-machine/references/striking-distance.md +116 -0
- package/skills/seo-machine/references/technical-audit.md +153 -0
- package/skills/seo-machine/scripts/link_audit.py +265 -0
- package/skills/seo-machine/scripts/tech_audit.py +221 -0
- package/skills/seo-machine/scripts/word_count.py +154 -0
- package/skills/sharp-thesis-validator/SKILL.md +393 -0
- package/skills/sharp-thesis-validator/examples.md +186 -0
- package/skills/signup-flow-cro/SKILL.md +359 -0
- package/skills/signup-flow-cro/evals/evals.json +88 -0
- package/skills/site-architecture/SKILL.md +357 -0
- package/skills/site-architecture/evals/evals.json +88 -0
- package/skills/site-architecture/references/mermaid-templates.md +216 -0
- package/skills/site-architecture/references/navigation-patterns.md +305 -0
- package/skills/site-architecture/references/site-type-templates.md +293 -0
- package/skills/skill-self-improvement/SKILL.md +151 -0
- package/skills/slideshow-script/SKILL.md +274 -0
- package/skills/slideshow-script/references/content-spec-schema.md +77 -0
- package/skills/slideshow-script/references/frameworks.md +84 -0
- package/skills/slideshow-script/references/paips-framework.md +37 -0
- package/skills/social-campaign/SKILL.md +419 -0
- package/skills/social-campaign/references/paper-mcp-workflow.md +48 -0
- package/skills/social-content/SKILL.md +359 -0
- package/skills/social-content/evals/evals.json +92 -0
- package/skills/social-content/references/platforms.md +170 -0
- package/skills/social-content/references/post-templates.md +177 -0
- package/skills/social-content/references/reverse-engineering.md +195 -0
- package/skills/social-worker/SKILL.md +204 -0
- package/skills/startup-launcher/SKILL.md +252 -0
- package/skills/startup-launcher/references/file-router.md +64 -0
- package/skills/startup-launcher/references/platform-matrix.md +65 -0
- package/skills/startup-launcher/references/platforms/alternativeto.md +283 -0
- package/skills/startup-launcher/references/platforms/appsumo.md +495 -0
- package/skills/startup-launcher/references/platforms/auraplusplus.md +736 -0
- package/skills/startup-launcher/references/platforms/betalist.md +98 -0
- package/skills/startup-launcher/references/platforms/capterra.md +276 -0
- package/skills/startup-launcher/references/platforms/crunchbase.md +514 -0
- package/skills/startup-launcher/references/platforms/dealify.md +291 -0
- package/skills/startup-launcher/references/platforms/dealmirror.md +313 -0
- package/skills/startup-launcher/references/platforms/desifounder.md +490 -0
- package/skills/startup-launcher/references/platforms/devhunt.md +210 -0
- package/skills/startup-launcher/references/platforms/devpost.md +448 -0
- package/skills/startup-launcher/references/platforms/fazier.md +56 -0
- package/skills/startup-launcher/references/platforms/firsto.md +489 -0
- package/skills/startup-launcher/references/platforms/g2.md +226 -0
- package/skills/startup-launcher/references/platforms/hackernews.md +99 -0
- package/skills/startup-launcher/references/platforms/hackernoon.md +385 -0
- package/skills/startup-launcher/references/platforms/indiedeals.md +367 -0
- package/skills/startup-launcher/references/platforms/indiehackers.md +80 -0
- package/skills/startup-launcher/references/platforms/kenmoo.md +231 -0
- package/skills/startup-launcher/references/platforms/labstartups.md +618 -0
- package/skills/startup-launcher/references/platforms/launchboard.md +307 -0
- package/skills/startup-launcher/references/platforms/launchfast.md +560 -0
- package/skills/startup-launcher/references/platforms/launchigniter.md +56 -0
- package/skills/startup-launcher/references/platforms/launchingnext.md +674 -0
- package/skills/startup-launcher/references/platforms/launchit.md +574 -0
- package/skills/startup-launcher/references/platforms/launchyourapp.md +677 -0
- package/skills/startup-launcher/references/platforms/libhunt.md +220 -0
- package/skills/startup-launcher/references/platforms/ltdhunt.md +221 -0
- package/skills/startup-launcher/references/platforms/microlaunch.md +45 -0
- package/skills/startup-launcher/references/platforms/openalternative.md +195 -0
- package/skills/startup-launcher/references/platforms/peerlist.md +56 -0
- package/skills/startup-launcher/references/platforms/peerpush.md +64 -0
- package/skills/startup-launcher/references/platforms/primeclub.md +282 -0
- package/skills/startup-launcher/references/platforms/productcanyon.md +190 -0
- package/skills/startup-launcher/references/platforms/producthunt.md +98 -0
- package/skills/startup-launcher/references/platforms/rankinpublic.md +315 -0
- package/skills/startup-launcher/references/platforms/rockethub.md +374 -0
- package/skills/startup-launcher/references/platforms/saasgenius.md +175 -0
- package/skills/startup-launcher/references/platforms/saashub.md +205 -0
- package/skills/startup-launcher/references/platforms/saasmantra.md +221 -0
- package/skills/startup-launcher/references/platforms/saaspirate.md +287 -0
- package/skills/startup-launcher/references/platforms/saaswarrior.md +190 -0
- package/skills/startup-launcher/references/platforms/saaszilla.md +194 -0
- package/skills/startup-launcher/references/platforms/selected.md +925 -0
- package/skills/startup-launcher/references/platforms/sideprojectors.md +86 -0
- package/skills/startup-launcher/references/platforms/softonic.md +240 -0
- package/skills/startup-launcher/references/platforms/sourceforge.md +251 -0
- package/skills/startup-launcher/references/platforms/stacksocial.md +244 -0
- package/skills/startup-launcher/references/platforms/startupstash.md +326 -0
- package/skills/startup-launcher/references/platforms/theresanaiforthat.md +253 -0
- package/skills/startup-launcher/references/platforms/tinylaunch.md +70 -0
- package/skills/startup-launcher/references/platforms/tinystartups.md +72 -0
- package/skills/startup-launcher/references/platforms/toolfolio.md +291 -0
- package/skills/startup-launcher/references/platforms/trustmrr.md +194 -0
- package/skills/startup-launcher/references/platforms/trylaunch.md +531 -0
- package/skills/startup-launcher/references/platforms/uneed.md +57 -0
- package/skills/startup-launcher/references/templates/asset-checklist.md +218 -0
- package/skills/startup-launcher/references/templates/launch-tracker.md +195 -0
- package/skills/startup-launcher/references/templates/product-brief.md +98 -0
- package/skills/summarize/.skill-meta.json +13 -0
- package/skills/summarize/SKILL.md +170 -0
- package/skills/summarize/rules/install.md +157 -0
- package/skills/summarize/rules/usage.md +207 -0
- package/skills/topical-authority/SKILL.md +307 -0
- package/skills/topical-authority/references/jake-ward-framework.md +201 -0
- package/skills/topical-authority/references/serp-clustering-algorithm.md +325 -0
- package/skills/topical-authority/scripts/orchestrator.js +367 -0
- package/skills/topical-authority/scripts/seo/analyze-competitor-coverage.js +3 -0
- package/skills/topical-authority/scripts/seo/audit-internal-links.js +3 -0
- package/skills/topical-authority/scripts/seo/build-clusters-from-serp.js +3 -0
- package/skills/topical-authority/scripts/seo/extract-keywords-from-forums.js +3 -0
- package/skills/topical-authority/scripts/seo/extract-paa-questions.js +3 -0
- package/skills/topical-authority/scripts/seo/fetch-serp-urls.js +3 -0
- package/skills/topical-authority/scripts/seo/gemini-serp-clustering.js +3 -0
- package/skills/topical-authority/scripts/seo/identify-content-gaps.js +3 -0
- package/skills/topical-authority/scripts/seo/identify-research-gaps.js +3 -0
- package/skills/topical-authority/scripts/seo/scrape-quora-questions.js +3 -0
- package/skills/topical-authority/scripts/seo/scrape-reddit-pain-points.js +3 -0
- package/skills/topical-authority/scripts/seo/weave-internal-links.js +3 -0
- package/skills/visual-style/SKILL.md +199 -0
- package/skills/voice-brand-alignment/SKILL.md +70 -0
- package/skills/voice-brand-alignment/references/ai-slop-patterns.md +405 -0
- package/skills/voice-brand-alignment/scripts/validation/gemini-voice-validator.js +96 -0
- package/skills/voice-extraction/SKILL.md +234 -0
- package/skills/voice-extraction/references/extraction-prompt.md +155 -0
- package/skills/voice-extraction/references/voice-dna-template.md +168 -0
- package/skills/voice-extraction/references/voice-elements.md +259 -0
- package/skills/voice-extraction/scripts/build-corpus.js +120 -0
- package/skills/voice-extraction/scripts/collect-sources.sh +168 -0
- package/skills/warm-outbound/SKILL.md +299 -0
- package/skills/warm-outbound/templates/warm-message.md +39 -0
- package/skills/weekly-newsletter-digest/SKILL.md +78 -0
- package/skills/x-playwriter/SKILL.md +428 -0
- package/skills/x-playwriter/scripts/x-browser.js +185 -0
- package/skills/x-post-generator-openclaw/SKILL.md +138 -0
- package/skills/x-post-generator-openclaw/scripts/generate-x-post.js +537 -0
- package/skills/x-posting-playwriter/SKILL.md +203 -0
- package/skills/x-research/SKILL.md +129 -0
- package/skills/x-research/x-research.sh +85 -0
- package/skills/youtube-summarize/SKILL.md +90 -0
- package/skills/youtube-summarize/scripts/youtube-summarize.sh +143 -0
- package/src/cli.js +347 -172
- package/src/harness/deepseek-adapter.js +102 -0
- package/src/harness/deepseek.js +225 -0
- package/src/harness/opencode-adapter.js +174 -0
- package/src/harness/pi-adapter.js +172 -0
- package/src/harness/registry.js +121 -0
- package/src/harness/telemetry.js +102 -0
- package/src/harness/types.js +165 -0
- package/src/interview.js +36 -4
- package/src/postinstall.js +10 -12
- package/templates/AGENTS.md +17 -17
- package/templates/ORG.md +1 -1
- package/templates/TOOLS.md +7 -2
- package/templates/opencode.json +26 -29
- package/wiki/doctor.md +54 -0
- package/wiki/inkstone.md +4 -4
- package/wiki/opencode.md +3 -3
- package/skills/curated.txt +0 -68
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
# Resend Examples
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Idempotency Keys](#idempotency-keys)
|
|
6
|
+
- [Error Handling](#error-handling)
|
|
7
|
+
- [Retry Logic](#retry-logic)
|
|
8
|
+
- [Complete Examples](#complete-examples)
|
|
9
|
+
|
|
10
|
+
## Idempotency Keys
|
|
11
|
+
|
|
12
|
+
Use idempotency keys to prevent duplicate emails when retrying failed requests. Keys expire after 24 hours and have a max length of 256 characters.
|
|
13
|
+
|
|
14
|
+
**Key format:** Use `<event-type>/<entity-id>` pattern (e.g., `welcome-email/user-123`, `order-confirmation/order-456`).
|
|
15
|
+
|
|
16
|
+
**Behavior:** If same key is sent with a different payload, Resend returns a 409 error. If same key with same payload, returns the original response without resending.
|
|
17
|
+
|
|
18
|
+
### Node.js
|
|
19
|
+
|
|
20
|
+
The Node.js SDK has a dedicated `idempotencyKey` option:
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { Resend } from 'resend';
|
|
24
|
+
|
|
25
|
+
const resend = new Resend(process.env.RESEND_API_KEY);
|
|
26
|
+
|
|
27
|
+
const { data, error } = await resend.emails.send(
|
|
28
|
+
{
|
|
29
|
+
from: 'Acme <onboarding@resend.dev>',
|
|
30
|
+
to: ['delivered@resend.dev'],
|
|
31
|
+
subject: 'Order Confirmation',
|
|
32
|
+
html: '<p>Your order has been confirmed.</p>',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
idempotencyKey: `order-confirmation/${orderId}`,
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Python
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import resend
|
|
44
|
+
import os
|
|
45
|
+
|
|
46
|
+
resend.api_key = os.environ["RESEND_API_KEY"]
|
|
47
|
+
|
|
48
|
+
email = resend.Emails.send({
|
|
49
|
+
"from": "Acme <onboarding@resend.dev>",
|
|
50
|
+
"to": ["delivered@resend.dev"],
|
|
51
|
+
"subject": "Order Confirmation",
|
|
52
|
+
"html": "<p>Your order has been confirmed.</p>",
|
|
53
|
+
}, idempotency_key=f"order-confirmation/{order_id}")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Go
|
|
57
|
+
|
|
58
|
+
Other SDKs use the `Idempotency-Key` header:
|
|
59
|
+
|
|
60
|
+
```go
|
|
61
|
+
import "github.com/resend/resend-go/v3"
|
|
62
|
+
|
|
63
|
+
client := resend.NewClient(os.Getenv("RESEND_API_KEY"))
|
|
64
|
+
|
|
65
|
+
params := &resend.SendEmailRequest{
|
|
66
|
+
From: "Acme <onboarding@resend.dev>",
|
|
67
|
+
To: []string{"delivered@resend.dev"},
|
|
68
|
+
Subject: "Order Confirmation",
|
|
69
|
+
Html: "<p>Your order has been confirmed.</p>",
|
|
70
|
+
Headers: map[string]string{
|
|
71
|
+
"Idempotency-Key": fmt.Sprintf("order-confirmation/%s", orderID),
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
sent, err := client.Emails.Send(params)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### cURL
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
curl -X POST 'https://api.resend.com/emails' \
|
|
82
|
+
-H 'Authorization: Bearer re_xxxxxxxxx' \
|
|
83
|
+
-H 'Content-Type: application/json' \
|
|
84
|
+
-H 'Idempotency-Key: order-confirmation/12345' \
|
|
85
|
+
-d '{
|
|
86
|
+
"from": "Acme <onboarding@resend.dev>",
|
|
87
|
+
"to": ["delivered@resend.dev"],
|
|
88
|
+
"subject": "Order Confirmation",
|
|
89
|
+
"html": "<p>Your order has been confirmed.</p>"
|
|
90
|
+
}'
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Error Handling
|
|
94
|
+
|
|
95
|
+
### Common Error Codes
|
|
96
|
+
|
|
97
|
+
| Code | Description | Action |
|
|
98
|
+
|------|-------------|--------|
|
|
99
|
+
| 400 | Bad request (invalid params) | Fix request parameters |
|
|
100
|
+
| 400 | `invalid_idempotency_key` | Key must be 1-256 characters |
|
|
101
|
+
| 401 | Invalid API key | Check RESEND_API_KEY |
|
|
102
|
+
| 403 | Domain not verified | Verify domain at resend.com/domains |
|
|
103
|
+
| 409 | `invalid_idempotent_request` | Key already used with different payload |
|
|
104
|
+
| 409 | `concurrent_idempotent_requests` | Same key request in progress, retry later |
|
|
105
|
+
| 422 | Unprocessable entity | Check email format/content |
|
|
106
|
+
| 429 | Rate limited | Implement backoff and retry |
|
|
107
|
+
| 500 | Server error | Retry with backoff |
|
|
108
|
+
|
|
109
|
+
### Node.js
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { Resend } from 'resend';
|
|
113
|
+
|
|
114
|
+
const resend = new Resend(process.env.RESEND_API_KEY);
|
|
115
|
+
|
|
116
|
+
const { data, error } = await resend.emails.send({
|
|
117
|
+
from: 'Acme <onboarding@resend.dev>',
|
|
118
|
+
to: ['delivered@resend.dev'],
|
|
119
|
+
subject: 'Hello',
|
|
120
|
+
html: '<p>Hello world</p>',
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
if (error) {
|
|
124
|
+
console.error('Failed to send email:', error.message);
|
|
125
|
+
|
|
126
|
+
// Handle specific error types
|
|
127
|
+
if (error.name === 'validation_error') {
|
|
128
|
+
// Invalid parameters - don't retry
|
|
129
|
+
throw new Error(`Invalid email params: ${error.message}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (error.name === 'rate_limit_exceeded') {
|
|
133
|
+
// Rate limited - retry after delay
|
|
134
|
+
console.log('Rate limited, retrying...');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
console.log('Email sent:', data.id);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Python
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
import resend
|
|
147
|
+
import os
|
|
148
|
+
|
|
149
|
+
resend.api_key = os.environ["RESEND_API_KEY"]
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
email = resend.Emails.send({
|
|
153
|
+
"from": "Acme <onboarding@resend.dev>",
|
|
154
|
+
"to": ["delivered@resend.dev"],
|
|
155
|
+
"subject": "Hello",
|
|
156
|
+
"html": "<p>Hello world</p>",
|
|
157
|
+
})
|
|
158
|
+
print(f"Email sent: {email['id']}")
|
|
159
|
+
except resend.exceptions.ValidationError as e:
|
|
160
|
+
# Invalid parameters - don't retry
|
|
161
|
+
print(f"Validation error: {e}")
|
|
162
|
+
except resend.exceptions.RateLimitError as e:
|
|
163
|
+
# Rate limited - retry after delay
|
|
164
|
+
print(f"Rate limited: {e}")
|
|
165
|
+
except resend.exceptions.ResendError as e:
|
|
166
|
+
# Other API error
|
|
167
|
+
print(f"API error: {e}")
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Go
|
|
171
|
+
|
|
172
|
+
```go
|
|
173
|
+
import (
|
|
174
|
+
"fmt"
|
|
175
|
+
"github.com/resend/resend-go/v3"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
client := resend.NewClient(os.Getenv("RESEND_API_KEY"))
|
|
179
|
+
|
|
180
|
+
params := &resend.SendEmailRequest{
|
|
181
|
+
From: "Acme <onboarding@resend.dev>",
|
|
182
|
+
To: []string{"delivered@resend.dev"},
|
|
183
|
+
Subject: "Hello",
|
|
184
|
+
Html: "<p>Hello world</p>",
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
sent, err := client.Emails.Send(params)
|
|
188
|
+
if err != nil {
|
|
189
|
+
// Check error type and handle accordingly
|
|
190
|
+
fmt.Printf("Failed to send email: %v\n", err)
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
fmt.Printf("Email sent: %s\n", sent.Id)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Retry Logic
|
|
198
|
+
|
|
199
|
+
Implement exponential backoff for transient failures (rate limits, server errors). Don't retry 409 `invalid_idempotent_request` errors (different payload).
|
|
200
|
+
|
|
201
|
+
### Node.js
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import { Resend } from 'resend';
|
|
205
|
+
|
|
206
|
+
const resend = new Resend(process.env.RESEND_API_KEY);
|
|
207
|
+
|
|
208
|
+
async function sendEmailWithRetry(
|
|
209
|
+
params: Parameters<typeof resend.emails.send>[0],
|
|
210
|
+
options: { maxRetries?: number; idempotencyKey?: string } = {}
|
|
211
|
+
) {
|
|
212
|
+
const { maxRetries = 3, idempotencyKey } = options;
|
|
213
|
+
|
|
214
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
215
|
+
const { data, error } = await resend.emails.send(
|
|
216
|
+
params,
|
|
217
|
+
idempotencyKey ? { idempotencyKey } : undefined
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
if (!error) {
|
|
221
|
+
return data;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Don't retry validation errors or idempotency conflicts
|
|
225
|
+
if (error.name === 'validation_error' || error.name === 'invalid_idempotent_request') {
|
|
226
|
+
throw new Error(`${error.name}: ${error.message}`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Last attempt failed
|
|
230
|
+
if (attempt === maxRetries) {
|
|
231
|
+
throw new Error(`Failed after ${maxRetries + 1} attempts: ${error.message}`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Exponential backoff: 1s, 2s, 4s...
|
|
235
|
+
const delay = Math.pow(2, attempt) * 1000;
|
|
236
|
+
console.log(`Attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
|
|
237
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Usage
|
|
242
|
+
const result = await sendEmailWithRetry(
|
|
243
|
+
{
|
|
244
|
+
from: 'Acme <onboarding@resend.dev>',
|
|
245
|
+
to: ['delivered@resend.dev'],
|
|
246
|
+
subject: 'Order Confirmation',
|
|
247
|
+
html: '<p>Your order is confirmed.</p>',
|
|
248
|
+
},
|
|
249
|
+
{ idempotencyKey: `order-confirmation/${orderId}` }
|
|
250
|
+
);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Python
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
import resend
|
|
257
|
+
import os
|
|
258
|
+
import time
|
|
259
|
+
|
|
260
|
+
resend.api_key = os.environ["RESEND_API_KEY"]
|
|
261
|
+
|
|
262
|
+
def send_email_with_retry(params, max_retries=3, idempotency_key=None):
|
|
263
|
+
for attempt in range(max_retries + 1):
|
|
264
|
+
try:
|
|
265
|
+
return resend.Emails.send(params, idempotency_key=idempotency_key)
|
|
266
|
+
except resend.exceptions.ValidationError:
|
|
267
|
+
# Don't retry validation errors
|
|
268
|
+
raise
|
|
269
|
+
except resend.exceptions.ResendError as e:
|
|
270
|
+
if attempt == max_retries:
|
|
271
|
+
raise Exception(f"Failed after {max_retries + 1} attempts: {e}")
|
|
272
|
+
|
|
273
|
+
# Exponential backoff: 1s, 2s, 4s...
|
|
274
|
+
delay = (2 ** attempt)
|
|
275
|
+
print(f"Attempt {attempt + 1} failed, retrying in {delay}s...")
|
|
276
|
+
time.sleep(delay)
|
|
277
|
+
|
|
278
|
+
# Usage
|
|
279
|
+
result = send_email_with_retry(
|
|
280
|
+
{
|
|
281
|
+
"from": "Acme <onboarding@resend.dev>",
|
|
282
|
+
"to": ["delivered@resend.dev"],
|
|
283
|
+
"subject": "Order Confirmation",
|
|
284
|
+
"html": "<p>Your order is confirmed.</p>",
|
|
285
|
+
},
|
|
286
|
+
idempotency_key=f"order-confirmation/{order_id}"
|
|
287
|
+
)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Go
|
|
291
|
+
|
|
292
|
+
```go
|
|
293
|
+
import (
|
|
294
|
+
"fmt"
|
|
295
|
+
"time"
|
|
296
|
+
"github.com/resend/resend-go/v3"
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
func sendEmailWithRetry(client *resend.Client, params *resend.SendEmailRequest, maxRetries int) (*resend.SendEmailResponse, error) {
|
|
300
|
+
var lastErr error
|
|
301
|
+
|
|
302
|
+
for attempt := 0; attempt <= maxRetries; attempt++ {
|
|
303
|
+
sent, err := client.Emails.Send(params)
|
|
304
|
+
if err == nil {
|
|
305
|
+
return sent, nil
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
lastErr = err
|
|
309
|
+
|
|
310
|
+
if attempt == maxRetries {
|
|
311
|
+
break
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Exponential backoff: 1s, 2s, 4s...
|
|
315
|
+
delay := time.Duration(1<<attempt) * time.Second
|
|
316
|
+
fmt.Printf("Attempt %d failed, retrying in %v...\n", attempt+1, delay)
|
|
317
|
+
time.Sleep(delay)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return nil, fmt.Errorf("failed after %d attempts: %w", maxRetries+1, lastErr)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Usage
|
|
324
|
+
client := resend.NewClient(os.Getenv("RESEND_API_KEY"))
|
|
325
|
+
|
|
326
|
+
params := &resend.SendEmailRequest{
|
|
327
|
+
From: "Acme <onboarding@resend.dev>",
|
|
328
|
+
To: []string{"delivered@resend.dev"},
|
|
329
|
+
Subject: "Order Confirmation",
|
|
330
|
+
Html: "<p>Your order is confirmed.</p>",
|
|
331
|
+
Headers: map[string]string{
|
|
332
|
+
"Idempotency-Key": fmt.Sprintf("order-confirmation/%s", orderID),
|
|
333
|
+
},
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
sent, err := sendEmailWithRetry(client, params, 3)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## Complete Examples
|
|
340
|
+
|
|
341
|
+
### Node.js - Production-Ready Email Service
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import { Resend } from 'resend';
|
|
345
|
+
import { randomUUID } from 'crypto';
|
|
346
|
+
|
|
347
|
+
const resend = new Resend(process.env.RESEND_API_KEY);
|
|
348
|
+
|
|
349
|
+
interface SendEmailOptions {
|
|
350
|
+
to: string | string[];
|
|
351
|
+
subject: string;
|
|
352
|
+
html: string;
|
|
353
|
+
from?: string;
|
|
354
|
+
replyTo?: string;
|
|
355
|
+
idempotencyKey?: string;
|
|
356
|
+
maxRetries?: number;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async function sendEmail(options: SendEmailOptions) {
|
|
360
|
+
const {
|
|
361
|
+
to,
|
|
362
|
+
subject,
|
|
363
|
+
html,
|
|
364
|
+
from = 'Acme <noreply@acme.com>',
|
|
365
|
+
replyTo,
|
|
366
|
+
idempotencyKey = randomUUID(),
|
|
367
|
+
maxRetries = 3,
|
|
368
|
+
} = options;
|
|
369
|
+
|
|
370
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
371
|
+
const { data, error } = await resend.emails.send(
|
|
372
|
+
{
|
|
373
|
+
from,
|
|
374
|
+
to: Array.isArray(to) ? to : [to],
|
|
375
|
+
subject,
|
|
376
|
+
html,
|
|
377
|
+
...(replyTo && { replyTo }),
|
|
378
|
+
},
|
|
379
|
+
{ idempotencyKey }
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
if (!error) {
|
|
383
|
+
return { success: true, id: data.id };
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Don't retry client errors or idempotency conflicts
|
|
387
|
+
if (error.name === 'validation_error' || error.name === 'not_found' || error.name === 'invalid_idempotent_request') {
|
|
388
|
+
return { success: false, error: error.message, retryable: false };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (attempt < maxRetries) {
|
|
392
|
+
const delay = Math.pow(2, attempt) * 1000;
|
|
393
|
+
await new Promise(r => setTimeout(r, delay));
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return { success: false, error: 'Max retries exceeded', retryable: true };
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Usage
|
|
401
|
+
const result = await sendEmail({
|
|
402
|
+
to: 'delivered@resend.dev',
|
|
403
|
+
subject: 'Welcome!',
|
|
404
|
+
html: '<h1>Welcome to Acme</h1>',
|
|
405
|
+
idempotencyKey: `welcome-email/${userId}`,
|
|
406
|
+
});
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Python - Production-Ready Email Service
|
|
410
|
+
|
|
411
|
+
```python
|
|
412
|
+
import resend
|
|
413
|
+
import os
|
|
414
|
+
import time
|
|
415
|
+
import uuid
|
|
416
|
+
from dataclasses import dataclass
|
|
417
|
+
from typing import Optional, Union, List
|
|
418
|
+
|
|
419
|
+
resend.api_key = os.environ["RESEND_API_KEY"]
|
|
420
|
+
|
|
421
|
+
@dataclass
|
|
422
|
+
class EmailResult:
|
|
423
|
+
success: bool
|
|
424
|
+
id: Optional[str] = None
|
|
425
|
+
error: Optional[str] = None
|
|
426
|
+
retryable: bool = False
|
|
427
|
+
|
|
428
|
+
def send_email(
|
|
429
|
+
to: Union[str, List[str]],
|
|
430
|
+
subject: str,
|
|
431
|
+
html: str,
|
|
432
|
+
from_addr: str = "Acme <noreply@acme.com>",
|
|
433
|
+
reply_to: Optional[str] = None,
|
|
434
|
+
idempotency_key: Optional[str] = None,
|
|
435
|
+
max_retries: int = 3,
|
|
436
|
+
) -> EmailResult:
|
|
437
|
+
idempotency_key = idempotency_key or str(uuid.uuid4())
|
|
438
|
+
recipients = [to] if isinstance(to, str) else to
|
|
439
|
+
|
|
440
|
+
params = {
|
|
441
|
+
"from": from_addr,
|
|
442
|
+
"to": recipients,
|
|
443
|
+
"subject": subject,
|
|
444
|
+
"html": html,
|
|
445
|
+
}
|
|
446
|
+
if reply_to:
|
|
447
|
+
params["reply_to"] = reply_to
|
|
448
|
+
|
|
449
|
+
for attempt in range(max_retries + 1):
|
|
450
|
+
try:
|
|
451
|
+
result = resend.Emails.send(params, idempotency_key=idempotency_key)
|
|
452
|
+
return EmailResult(success=True, id=result["id"])
|
|
453
|
+
except resend.exceptions.ValidationError as e:
|
|
454
|
+
return EmailResult(success=False, error=str(e), retryable=False)
|
|
455
|
+
except resend.exceptions.ResendError as e:
|
|
456
|
+
if attempt < max_retries:
|
|
457
|
+
time.sleep(2 ** attempt)
|
|
458
|
+
else:
|
|
459
|
+
return EmailResult(success=False, error=str(e), retryable=True)
|
|
460
|
+
|
|
461
|
+
return EmailResult(success=False, error="Max retries exceeded", retryable=True)
|
|
462
|
+
|
|
463
|
+
# Usage
|
|
464
|
+
result = send_email(
|
|
465
|
+
to="delivered@resend.dev",
|
|
466
|
+
subject="Welcome!",
|
|
467
|
+
html="<h1>Welcome to Acme</h1>",
|
|
468
|
+
idempotency_key=f"welcome-email/{user_id}",
|
|
469
|
+
)
|
|
470
|
+
```
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Webhooks
|
|
2
|
+
|
|
3
|
+
Receive real-time notifications when email events occur (delivered, bounced, opened, etc.).
|
|
4
|
+
|
|
5
|
+
## When to Use Webhooks
|
|
6
|
+
|
|
7
|
+
- Track delivery status in your database
|
|
8
|
+
- Remove bounced addresses from mailing lists
|
|
9
|
+
- Trigger follow-up actions when emails are opened/clicked
|
|
10
|
+
- Create alerts for failures or complaints
|
|
11
|
+
- Build custom analytics dashboards
|
|
12
|
+
|
|
13
|
+
## Event Types
|
|
14
|
+
|
|
15
|
+
### Email Events
|
|
16
|
+
|
|
17
|
+
| Event | Trigger | Use Case |
|
|
18
|
+
|-------|---------|----------|
|
|
19
|
+
| `email.sent` | API request successful, delivery attempted | Confirm email accepted |
|
|
20
|
+
| `email.delivered` | Email reached recipient's mail server | Confirm successful delivery |
|
|
21
|
+
| `email.bounced` | Mail server permanently rejected (hard bounce) | Remove from list, alert user |
|
|
22
|
+
| `email.complained` | Recipient marked as spam | Unsubscribe, review content |
|
|
23
|
+
| `email.opened` | Recipient opened email | Track engagement |
|
|
24
|
+
| `email.clicked` | Recipient clicked a link | Track engagement |
|
|
25
|
+
| `email.delivery_delayed` | Temporary delivery issue (soft bounce) | Monitor, may resolve automatically |
|
|
26
|
+
| `email.failed` | Send error (invalid recipient, quota, etc.) | Debug, alert |
|
|
27
|
+
|
|
28
|
+
### Bounce Types
|
|
29
|
+
|
|
30
|
+
| Type | Event | Action |
|
|
31
|
+
|------|-------|--------|
|
|
32
|
+
| **Hard bounce (Permanent)** | `email.bounced` | Remove address immediately - never retry |
|
|
33
|
+
| **Soft bounce (Transient)** | `email.delivery_delayed` | Monitor - Resend retries automatically |
|
|
34
|
+
| **Undetermined** | `email.bounced` | Treat as hard bounce if repeated |
|
|
35
|
+
|
|
36
|
+
**Hard bounces** (`email.bounced`) are permanent failures. The address is invalid and will never accept mail. Continuing to send to hard-bounced addresses destroys your sender reputation.
|
|
37
|
+
|
|
38
|
+
| Subtype | Cause |
|
|
39
|
+
|---------|-------|
|
|
40
|
+
| General | Recipient's email provider sent a hard bounce |
|
|
41
|
+
| NoEmail | Address doesn't exist or couldn't be determined |
|
|
42
|
+
|
|
43
|
+
**Soft bounces** (`email.delivery_delayed`) are temporary issues. Resend automatically retries these. If delivery ultimately fails after retries, you'll receive an `email.bounced` event.
|
|
44
|
+
|
|
45
|
+
| Subtype | Cause | May Resolve If... |
|
|
46
|
+
|---------|-------|-------------------|
|
|
47
|
+
| General | Temporary rejection | Underlying issue clears |
|
|
48
|
+
| MailboxFull | Recipient's inbox at capacity | Recipient frees space |
|
|
49
|
+
| MessageTooLarge | Exceeds provider size limit | You reduce message size |
|
|
50
|
+
| ContentRejected | Contains disallowed content | You modify content |
|
|
51
|
+
| AttachmentRejected | Attachment type/size rejected | You modify attachment |
|
|
52
|
+
|
|
53
|
+
**Undetermined bounces** occur when the bounce message doesn't contain enough information for Resend to determine the reason. Treat repeated undetermined bounces as hard bounces.
|
|
54
|
+
|
|
55
|
+
### Other Events
|
|
56
|
+
|
|
57
|
+
| Event | Trigger |
|
|
58
|
+
|-------|---------|
|
|
59
|
+
| `domain.created` / `updated` / `deleted` | Domain configuration changes |
|
|
60
|
+
| `contact.created` / `updated` / `deleted` | Contact list changes (not from CSV imports) |
|
|
61
|
+
|
|
62
|
+
## Setup
|
|
63
|
+
|
|
64
|
+
1. **Create endpoint** - POST endpoint that returns HTTP 200
|
|
65
|
+
2. **Add webhook** - In Resend dashboard (resend.com/webhooks), add your URL and select events
|
|
66
|
+
3. **Verify signatures** - **REQUIRED** - See [Signature Verification](#signature-verification)
|
|
67
|
+
4. **Test locally** - Use ngrok or similar for local development
|
|
68
|
+
|
|
69
|
+
## Signature Verification
|
|
70
|
+
|
|
71
|
+
**You MUST verify webhook signatures.** Without verification, attackers can send fake webhooks to your endpoint.
|
|
72
|
+
|
|
73
|
+
### Why Verification Matters
|
|
74
|
+
|
|
75
|
+
- Webhooks are unauthenticated HTTP POST requests
|
|
76
|
+
- Anyone who knows your endpoint URL can send fake events
|
|
77
|
+
- Verification ensures the webhook genuinely came from Resend
|
|
78
|
+
- Unique signatures prevent replay attacks
|
|
79
|
+
|
|
80
|
+
### Required Headers
|
|
81
|
+
|
|
82
|
+
Every webhook includes these headers for verification:
|
|
83
|
+
|
|
84
|
+
| Header | Purpose |
|
|
85
|
+
|--------|---------|
|
|
86
|
+
| `svix-id` | Unique message identifier |
|
|
87
|
+
| `svix-timestamp` | Unix timestamp when sent |
|
|
88
|
+
| `svix-signature` | Cryptographic signature |
|
|
89
|
+
|
|
90
|
+
### Get Your Webhook Secret
|
|
91
|
+
|
|
92
|
+
Find your signing secret in the Resend dashboard:
|
|
93
|
+
1. Go to resend.com/webhooks
|
|
94
|
+
2. Click on your webhook
|
|
95
|
+
3. Copy the signing secret (starts with `whsec_`)
|
|
96
|
+
|
|
97
|
+
Store it securely as `RESEND_WEBHOOK_SECRET` environment variable.
|
|
98
|
+
|
|
99
|
+
### Using Resend SDK (Recommended)
|
|
100
|
+
|
|
101
|
+
Example using Next.js:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { Resend } from 'resend';
|
|
105
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
106
|
+
import { db } from '@/lib/db'; // Your database client (Prisma, Drizzle, etc.)
|
|
107
|
+
|
|
108
|
+
const resend = new Resend(process.env.RESEND_API_KEY);
|
|
109
|
+
|
|
110
|
+
export async function POST(req: NextRequest) {
|
|
111
|
+
try {
|
|
112
|
+
// CRITICAL: Use raw body, not parsed JSON
|
|
113
|
+
const payload = await req.text();
|
|
114
|
+
|
|
115
|
+
// Throws an error if the webhook is invalid
|
|
116
|
+
// Otherwise, returns the parsed payload object
|
|
117
|
+
const event = resend.webhooks.verify({
|
|
118
|
+
payload,
|
|
119
|
+
headers: {
|
|
120
|
+
'svix-id': req.headers.get('svix-id'),
|
|
121
|
+
'svix-timestamp': req.headers.get('svix-timestamp'),
|
|
122
|
+
'svix-signature': req.headers.get('svix-signature'),
|
|
123
|
+
},
|
|
124
|
+
secret: process.env.RESEND_WEBHOOK_SECRET,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Handle the verified event
|
|
128
|
+
switch (event.type) {
|
|
129
|
+
case 'email.delivered':
|
|
130
|
+
// update database with the email delivery status
|
|
131
|
+
break;
|
|
132
|
+
|
|
133
|
+
case 'email.bounced':
|
|
134
|
+
// Hard bounce - remove from mailing list immediately
|
|
135
|
+
break;
|
|
136
|
+
|
|
137
|
+
case 'email.complained':
|
|
138
|
+
// Spam complaint - unsubscribe and flag
|
|
139
|
+
break;
|
|
140
|
+
|
|
141
|
+
default:
|
|
142
|
+
// handle other events
|
|
143
|
+
return new Response('OK', { status: 200, body: 'Event skipped' });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return new NextResponse('OK', { status: 200 });
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('Webhook verification failed:', error);
|
|
149
|
+
return new NextResponse('Invalid signature', { status: 400 });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Common Mistakes
|
|
155
|
+
|
|
156
|
+
| Mistake | Fix |
|
|
157
|
+
|---------|-----|
|
|
158
|
+
| Not verifying signatures | **Always verify** - attackers can send fake webhooks |
|
|
159
|
+
| Using parsed JSON body | Use raw request body - JSON parsing breaks signature |
|
|
160
|
+
| Using `express.json()` middleware | Use `express.raw()` for webhook routes |
|
|
161
|
+
| Hardcoding webhook secret | Store in environment variable |
|
|
162
|
+
| Returning non-200 status for valid webhooks | Return 200 OK to acknowledge receipt |
|
|
163
|
+
|
|
164
|
+
## Retry Schedule
|
|
165
|
+
|
|
166
|
+
If your endpoint doesn't return HTTP 200, Resend retries with exponential backoff:
|
|
167
|
+
|
|
168
|
+
| Attempt | Delay After Failure |
|
|
169
|
+
|---------|---------------------|
|
|
170
|
+
| 1 | Immediate |
|
|
171
|
+
| 2 | 5 seconds |
|
|
172
|
+
| 3 | 5 minutes |
|
|
173
|
+
| 4 | 30 minutes |
|
|
174
|
+
| 5 | 2 hours |
|
|
175
|
+
| 6 | 5 hours |
|
|
176
|
+
| 7 | 10 hours |
|
|
177
|
+
|
|
178
|
+
Example: A webhook that fails 3 times before succeeding will be delivered ~35 minutes after the first attempt.
|
|
179
|
+
|
|
180
|
+
**Tip:** Always return 200 quickly, then process asynchronously if needed. You can manually replay failed webhooks from the dashboard.
|
|
181
|
+
|
|
182
|
+
## IP Allowlist
|
|
183
|
+
|
|
184
|
+
If your firewall requires allowlisting, webhooks come from:
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
44.228.126.217
|
|
188
|
+
50.112.21.217
|
|
189
|
+
52.24.126.164
|
|
190
|
+
54.148.139.208
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
IPv6: `2600:1f24:64:8000::/52`
|
|
194
|
+
|
|
195
|
+
## Local Development
|
|
196
|
+
|
|
197
|
+
Use tunneling tools to test webhooks locally:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# ngrok
|
|
201
|
+
ngrok http 3000
|
|
202
|
+
|
|
203
|
+
# use the port that your server is running on (e.g. 3000)
|
|
204
|
+
# Then use the ngrok URL in Resend dashboard
|
|
205
|
+
# https://abc123.ngrok.io/webhooks/resend
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Event Payload Example
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"type": "email.delivered",
|
|
213
|
+
"created_at": "2024-01-15T12:00:00.000Z",
|
|
214
|
+
"data": {
|
|
215
|
+
"email_id": "ae2014de-c168-4c61-8267-70d2662a1ce1",
|
|
216
|
+
"from": "Acme <noreply@acme.com>",
|
|
217
|
+
"to": ["delivered@resend.dev"],
|
|
218
|
+
"subject": "Welcome to Acme"
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|