luckiest-co 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +16 -0
- package/.claude-plugin/plugin.json +1 -1
- package/LICENSE +24 -0
- package/bin/install.js +7 -5
- package/commands/{luckiest/finish.md → finish.md} +2 -0
- package/commands/go.md +53 -0
- package/commands/plan.md +66 -0
- package/package.json +16 -5
- package/skills/brainstorm/ATTRIBUTION.md +27 -0
- package/skills/brainstorm/SKILL.md +139 -0
- package/skills/luckiest-ab-testing/ATTRIBUTION.md +35 -0
- package/skills/luckiest-ab-testing/CHANGELOG.md +27 -0
- package/skills/luckiest-ab-testing/SKILL.md +379 -0
- package/skills/luckiest-ab-testing/evals/evals.json +118 -0
- package/skills/luckiest-ab-testing/references/sample-size-guide.md +290 -0
- package/skills/luckiest-ab-testing/references/test-templates.md +284 -0
- package/skills/luckiest-ad-creative/ATTRIBUTION.md +35 -0
- package/skills/luckiest-ad-creative/CHANGELOG.md +23 -0
- package/skills/luckiest-ad-creative/SKILL.md +393 -0
- package/skills/luckiest-ad-creative/evals/evals.json +90 -0
- package/skills/luckiest-ad-creative/references/generative-tools.md +637 -0
- package/skills/luckiest-ad-creative/references/platform-specs.md +213 -0
- package/skills/luckiest-ads/ATTRIBUTION.md +36 -0
- package/skills/luckiest-ads/CHANGELOG.md +24 -0
- package/skills/luckiest-ads/LICENSE +21 -0
- package/skills/luckiest-ads/SKILL.md +434 -0
- package/skills/luckiest-ads/evals/evals.json +90 -0
- package/skills/luckiest-ads/references/ad-copy-templates.md +207 -0
- package/skills/luckiest-ads/references/audience-targeting.md +243 -0
- package/skills/luckiest-ads/references/conversion-tracking.md +361 -0
- package/skills/luckiest-ads/references/platform-setup-checklists.md +277 -0
- package/skills/luckiest-advisors/ATTRIBUTION.md +16 -0
- package/skills/luckiest-advisors/CHANGELOG.md +99 -0
- package/skills/luckiest-advisors/SKILL.md +151 -0
- package/skills/luckiest-aeo-grader/ATTRIBUTION.md +19 -0
- package/skills/luckiest-aeo-grader/CHANGELOG.md +52 -0
- package/skills/luckiest-aeo-grader/SKILL.md +200 -0
- package/skills/luckiest-aeo-grader/evals/evals.json +19 -0
- package/skills/luckiest-aeo-grader/references/rubric.md +72 -0
- package/skills/luckiest-aeo-grader/scripts/scan.py +265 -0
- package/skills/luckiest-ai-seo/ATTRIBUTION.md +38 -0
- package/skills/luckiest-ai-seo/CHANGELOG.md +23 -0
- package/skills/luckiest-ai-seo/SKILL.md +510 -0
- package/skills/luckiest-ai-seo/evals/evals.json +90 -0
- package/skills/luckiest-ai-seo/references/content-patterns.md +285 -0
- package/skills/luckiest-ai-seo/references/content-types.md +71 -0
- package/skills/luckiest-ai-seo/references/okf.md +104 -0
- package/skills/luckiest-ai-seo/references/platform-ranking-factors.md +154 -0
- package/skills/luckiest-analytics/ATTRIBUTION.md +42 -0
- package/skills/luckiest-analytics/CHANGELOG.md +36 -0
- package/skills/luckiest-analytics/SKILL.md +380 -0
- package/skills/luckiest-analytics/evals/evals.json +90 -0
- package/skills/luckiest-analytics/references/event-library.md +260 -0
- package/skills/luckiest-analytics/references/ga4-implementation.md +300 -0
- package/skills/luckiest-analytics/references/gtm-implementation.md +390 -0
- package/skills/luckiest-aso/ATTRIBUTION.md +40 -0
- package/skills/luckiest-aso/CHANGELOG.md +33 -0
- package/skills/luckiest-aso/SKILL.md +327 -0
- package/skills/luckiest-aso/evals/evals.json +91 -0
- package/skills/luckiest-aso/references/apple-specs.md +107 -0
- package/skills/luckiest-aso/references/benchmarks.md +129 -0
- package/skills/luckiest-aso/references/google-play-specs.md +131 -0
- package/skills/luckiest-aso/references/report-template.md +213 -0
- package/skills/luckiest-aso/references/scoring-criteria.md +213 -0
- package/skills/luckiest-churn-prevention/ATTRIBUTION.md +41 -0
- package/skills/luckiest-churn-prevention/CHANGELOG.md +24 -0
- package/skills/luckiest-churn-prevention/SKILL.md +445 -0
- package/skills/luckiest-churn-prevention/evals/evals.json +93 -0
- package/skills/luckiest-churn-prevention/references/cancel-flow-patterns.md +316 -0
- package/skills/luckiest-churn-prevention/references/dunning-playbook.md +408 -0
- package/skills/luckiest-co-marketing/ATTRIBUTION.md +38 -0
- package/skills/luckiest-co-marketing/CHANGELOG.md +25 -0
- package/skills/luckiest-co-marketing/SKILL.md +331 -0
- package/skills/luckiest-co-marketing/evals/evals.json +84 -0
- package/skills/luckiest-coder/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder/CHANGELOG.md +30 -0
- package/skills/luckiest-coder/SKILL.md +77 -0
- package/skills/luckiest-coder/references/antigravity-tools.md +23 -0
- package/skills/luckiest-coder/references/codex-tools.md +39 -0
- package/skills/luckiest-coder/references/pi-tools.md +16 -0
- package/skills/luckiest-coder-brainstorming/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-brainstorming/CHANGELOG.md +31 -0
- package/skills/luckiest-coder-brainstorming/SKILL.md +173 -0
- package/skills/luckiest-coder-brainstorming/scripts/frame-template.html +213 -0
- package/skills/luckiest-coder-brainstorming/scripts/helper.js +168 -0
- package/skills/luckiest-coder-brainstorming/scripts/server.cjs +721 -0
- package/skills/luckiest-coder-brainstorming/scripts/start-server.sh +209 -0
- package/skills/luckiest-coder-brainstorming/scripts/stop-server.sh +120 -0
- package/skills/luckiest-coder-brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/skills/luckiest-coder-brainstorming/visual-companion.md +291 -0
- package/skills/luckiest-coder-consistency/ATTRIBUTION.md +30 -0
- package/skills/luckiest-coder-consistency/CHANGELOG.md +26 -0
- package/skills/luckiest-coder-consistency/SKILL.md +98 -0
- package/skills/luckiest-coder-constitution/ATTRIBUTION.md +28 -0
- package/skills/luckiest-coder-constitution/CHANGELOG.md +25 -0
- package/skills/luckiest-coder-constitution/SKILL.md +115 -0
- package/skills/luckiest-coder-debugging/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-debugging/CHANGELOG.md +22 -0
- package/skills/luckiest-coder-debugging/CREATION-LOG.md +119 -0
- package/skills/luckiest-coder-debugging/SKILL.md +309 -0
- package/skills/luckiest-coder-debugging/condition-based-waiting-example.ts +158 -0
- package/skills/luckiest-coder-debugging/condition-based-waiting.md +115 -0
- package/skills/luckiest-coder-debugging/defense-in-depth.md +122 -0
- package/skills/luckiest-coder-debugging/find-polluter.sh +63 -0
- package/skills/luckiest-coder-debugging/root-cause-tracing.md +169 -0
- package/skills/luckiest-coder-debugging/test-academic.md +14 -0
- package/skills/luckiest-coder-debugging/test-pressure-1.md +58 -0
- package/skills/luckiest-coder-debugging/test-pressure-2.md +68 -0
- package/skills/luckiest-coder-debugging/test-pressure-3.md +69 -0
- package/skills/luckiest-coder-dispatching-parallel-agents/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-dispatching-parallel-agents/CHANGELOG.md +23 -0
- package/skills/luckiest-coder-dispatching-parallel-agents/SKILL.md +198 -0
- package/skills/luckiest-coder-executing-plans/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-executing-plans/CHANGELOG.md +22 -0
- package/skills/luckiest-coder-executing-plans/SKILL.md +83 -0
- package/skills/luckiest-coder-finishing-a-branch/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-finishing-a-branch/CHANGELOG.md +22 -0
- package/skills/luckiest-coder-finishing-a-branch/SKILL.md +258 -0
- package/skills/luckiest-coder-git-worktrees/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-git-worktrees/CHANGELOG.md +22 -0
- package/skills/luckiest-coder-git-worktrees/SKILL.md +215 -0
- package/skills/luckiest-coder-guard/ATTRIBUTION.md +29 -0
- package/skills/luckiest-coder-guard/CHANGELOG.md +48 -0
- package/skills/luckiest-coder-guard/LICENSE +21 -0
- package/skills/luckiest-coder-guard/SKILL.md +126 -0
- package/skills/luckiest-coder-guard/bin/check-careful.sh +65 -0
- package/skills/luckiest-coder-guard/bin/check-freeze.sh +48 -0
- package/skills/luckiest-coder-qa/ATTRIBUTION.md +28 -0
- package/skills/luckiest-coder-qa/CHANGELOG.md +49 -0
- package/skills/luckiest-coder-qa/LICENSE +21 -0
- package/skills/luckiest-coder-qa/SKILL.md +160 -0
- package/skills/luckiest-coder-qa/references/issue-taxonomy.md +85 -0
- package/skills/luckiest-coder-qa/references/qa-report-template.md +126 -0
- package/skills/luckiest-coder-receiving-code-review/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-receiving-code-review/CHANGELOG.md +28 -0
- package/skills/luckiest-coder-receiving-code-review/SKILL.md +226 -0
- package/skills/luckiest-coder-requesting-code-review/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-requesting-code-review/CHANGELOG.md +31 -0
- package/skills/luckiest-coder-requesting-code-review/SKILL.md +118 -0
- package/skills/luckiest-coder-requesting-code-review/code-reviewer.md +172 -0
- package/skills/luckiest-coder-shipping/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-shipping/CHANGELOG.md +48 -0
- package/skills/luckiest-coder-shipping/LICENSE +21 -0
- package/skills/luckiest-coder-shipping/SKILL.md +148 -0
- package/skills/luckiest-coder-shipping/references/canary.md +44 -0
- package/skills/luckiest-coder-shipping/references/test-and-coverage.md +91 -0
- package/skills/luckiest-coder-subagent-driven-development/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-subagent-driven-development/CHANGELOG.md +28 -0
- package/skills/luckiest-coder-subagent-driven-development/SKILL.md +439 -0
- package/skills/luckiest-coder-subagent-driven-development/implementer-prompt.md +139 -0
- package/skills/luckiest-coder-subagent-driven-development/scripts/review-package +44 -0
- package/skills/luckiest-coder-subagent-driven-development/scripts/sdd-workspace +22 -0
- package/skills/luckiest-coder-subagent-driven-development/scripts/task-brief +40 -0
- package/skills/luckiest-coder-subagent-driven-development/task-reviewer-prompt.md +188 -0
- package/skills/luckiest-coder-tdd/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-tdd/CHANGELOG.md +26 -0
- package/skills/luckiest-coder-tdd/SKILL.md +384 -0
- package/skills/luckiest-coder-tdd/testing-anti-patterns.md +299 -0
- package/skills/luckiest-coder-verification/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-verification/CHANGELOG.md +22 -0
- package/skills/luckiest-coder-verification/SKILL.md +152 -0
- package/skills/luckiest-coder-writing-plans/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-writing-plans/CHANGELOG.md +22 -0
- package/skills/luckiest-coder-writing-plans/SKILL.md +191 -0
- package/skills/luckiest-coder-writing-plans/plan-document-reviewer-prompt.md +49 -0
- package/skills/luckiest-coder-writing-skills/ATTRIBUTION.md +26 -0
- package/skills/luckiest-coder-writing-skills/CHANGELOG.md +31 -0
- package/skills/luckiest-coder-writing-skills/SKILL.md +702 -0
- package/skills/luckiest-coder-writing-skills/anthropic-best-practices.md +1150 -0
- package/skills/luckiest-coder-writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/skills/luckiest-coder-writing-skills/graphviz-conventions.dot +172 -0
- package/skills/luckiest-coder-writing-skills/persuasion-principles.md +187 -0
- package/skills/luckiest-coder-writing-skills/render-graphs.js +168 -0
- package/skills/luckiest-coder-writing-skills/testing-skills-with-subagents.md +384 -0
- package/skills/luckiest-cold-email/ATTRIBUTION.md +36 -0
- package/skills/luckiest-cold-email/CHANGELOG.md +24 -0
- package/skills/luckiest-cold-email/SKILL.md +213 -0
- package/skills/luckiest-cold-email/evals/evals.json +94 -0
- package/skills/luckiest-cold-email/references/benchmarks.md +83 -0
- package/skills/luckiest-cold-email/references/follow-up-sequences.md +81 -0
- package/skills/luckiest-cold-email/references/frameworks.md +90 -0
- package/skills/luckiest-cold-email/references/personalization.md +79 -0
- package/skills/luckiest-cold-email/references/subject-lines.md +53 -0
- package/skills/luckiest-community-marketing/ATTRIBUTION.md +41 -0
- package/skills/luckiest-community-marketing/CHANGELOG.md +35 -0
- package/skills/luckiest-community-marketing/SKILL.md +200 -0
- package/skills/luckiest-community-marketing/evals/evals.json +89 -0
- package/skills/luckiest-competitor-profiling/ATTRIBUTION.md +36 -0
- package/skills/luckiest-competitor-profiling/CHANGELOG.md +24 -0
- package/skills/luckiest-competitor-profiling/SKILL.md +443 -0
- package/skills/luckiest-competitor-profiling/evals/evals.json +85 -0
- package/skills/luckiest-competitor-profiling/references/templates.md +167 -0
- package/skills/luckiest-competitor-profiling/references/tool-reference.md +179 -0
- package/skills/luckiest-competitors/ATTRIBUTION.md +36 -0
- package/skills/luckiest-competitors/CHANGELOG.md +25 -0
- package/skills/luckiest-competitors/SKILL.md +171 -0
- package/skills/luckiest-competitors/evals/evals.json +93 -0
- package/skills/luckiest-competitors/references/content-architecture.md +271 -0
- package/skills/luckiest-competitors/references/templates.md +223 -0
- package/skills/luckiest-content-strategy/ATTRIBUTION.md +39 -0
- package/skills/luckiest-content-strategy/CHANGELOG.md +23 -0
- package/skills/luckiest-content-strategy/SKILL.md +395 -0
- package/skills/luckiest-content-strategy/evals/evals.json +90 -0
- package/skills/luckiest-content-strategy/references/headless-cms.md +194 -0
- package/skills/luckiest-copy-editing/ATTRIBUTION.md +41 -0
- package/skills/luckiest-copy-editing/CHANGELOG.md +31 -0
- package/skills/luckiest-copy-editing/SKILL.md +491 -0
- package/skills/luckiest-copy-editing/evals/evals.json +89 -0
- package/skills/luckiest-copy-editing/references/checklist.md +66 -0
- package/skills/luckiest-copy-editing/references/content-refresh.md +38 -0
- package/skills/luckiest-copy-editing/references/plain-english-alternatives.md +394 -0
- package/skills/luckiest-copywriting/ATTRIBUTION.md +35 -0
- package/skills/luckiest-copywriting/CHANGELOG.md +23 -0
- package/skills/luckiest-copywriting/SKILL.md +296 -0
- package/skills/luckiest-copywriting/evals/evals.json +111 -0
- package/skills/luckiest-copywriting/references/copy-frameworks.md +344 -0
- package/skills/luckiest-copywriting/references/natural-transitions.md +272 -0
- package/skills/luckiest-cro/ATTRIBUTION.md +34 -0
- package/skills/luckiest-cro/CHANGELOG.md +23 -0
- package/skills/luckiest-cro/SKILL.md +215 -0
- package/skills/luckiest-cro/evals/evals.json +111 -0
- package/skills/luckiest-cro/references/experiments.md +248 -0
- package/skills/luckiest-cro/references/form.md +422 -0
- package/skills/luckiest-customer-research/ATTRIBUTION.md +40 -0
- package/skills/luckiest-customer-research/CHANGELOG.md +26 -0
- package/skills/luckiest-customer-research/SKILL.md +293 -0
- package/skills/luckiest-customer-research/evals/evals.json +162 -0
- package/skills/luckiest-customer-research/references/source-guides.md +401 -0
- package/skills/luckiest-directory-submissions/ATTRIBUTION.md +40 -0
- package/skills/luckiest-directory-submissions/CHANGELOG.md +33 -0
- package/skills/luckiest-directory-submissions/SKILL.md +419 -0
- package/skills/luckiest-directory-submissions/evals/evals.json +94 -0
- package/skills/luckiest-directory-submissions/references/directory-list.md +477 -0
- package/skills/luckiest-directory-submissions/references/positioning-variations.md +232 -0
- package/skills/luckiest-directory-submissions/references/submission-tracker-template.csv +266 -0
- package/skills/luckiest-emails/ATTRIBUTION.md +42 -0
- package/skills/luckiest-emails/CHANGELOG.md +33 -0
- package/skills/luckiest-emails/SKILL.md +220 -0
- package/skills/luckiest-emails/evals/evals.json +93 -0
- package/skills/luckiest-emails/references/copy-guidelines.md +120 -0
- package/skills/luckiest-emails/references/email-types.md +515 -0
- package/skills/luckiest-emails/references/sequence-templates.md +168 -0
- package/skills/luckiest-extract-design-system/ATTRIBUTION.md +8 -0
- package/skills/luckiest-extract-design-system/CHANGELOG.md +37 -0
- package/skills/luckiest-extract-design-system/SKILL.md +83 -0
- package/skills/luckiest-free-tools/ATTRIBUTION.md +43 -0
- package/skills/luckiest-free-tools/CHANGELOG.md +25 -0
- package/skills/luckiest-free-tools/SKILL.md +220 -0
- package/skills/luckiest-free-tools/evals/evals.json +90 -0
- package/skills/luckiest-free-tools/references/tool-types.md +217 -0
- package/skills/luckiest-image/ATTRIBUTION.md +43 -0
- package/skills/luckiest-image/CHANGELOG.md +23 -0
- package/skills/luckiest-image/SKILL.md +376 -0
- package/skills/luckiest-image/evals/evals.json +89 -0
- package/skills/luckiest-image/references/ai-image-prompting.md +229 -0
- package/skills/luckiest-launch/ATTRIBUTION.md +40 -0
- package/skills/luckiest-launch/CHANGELOG.md +43 -0
- package/skills/luckiest-launch/SKILL.md +385 -0
- package/skills/luckiest-launch/evals/evals.json +91 -0
- package/skills/luckiest-lead-magnets/ATTRIBUTION.md +35 -0
- package/skills/luckiest-lead-magnets/CHANGELOG.md +23 -0
- package/skills/luckiest-lead-magnets/SKILL.md +331 -0
- package/skills/luckiest-lead-magnets/evals/evals.json +87 -0
- package/skills/luckiest-lead-magnets/references/benchmarks.md +129 -0
- package/skills/luckiest-lead-magnets/references/format-guide.md +196 -0
- package/skills/luckiest-marketing-ideas/ATTRIBUTION.md +39 -0
- package/skills/luckiest-marketing-ideas/CHANGELOG.md +33 -0
- package/skills/luckiest-marketing-ideas/SKILL.md +191 -0
- package/skills/luckiest-marketing-ideas/evals/evals.json +90 -0
- package/skills/luckiest-marketing-ideas/references/ideas-by-category.md +366 -0
- package/skills/luckiest-marketing-plan/ATTRIBUTION.md +40 -0
- package/skills/luckiest-marketing-plan/CHANGELOG.md +23 -0
- package/skills/luckiest-marketing-plan/SKILL.md +285 -0
- package/skills/luckiest-marketing-plan/evals/evals.json +96 -0
- package/skills/luckiest-marketing-plan/references/aarrr-framework.md +180 -0
- package/skills/luckiest-marketing-plan/references/budget-planning.md +168 -0
- package/skills/luckiest-marketing-plan/references/client-types.md +373 -0
- package/skills/luckiest-marketing-plan/references/current-state-rubric.md +255 -0
- package/skills/luckiest-marketing-plan/references/example-quietude.md +972 -0
- package/skills/luckiest-marketing-plan/references/funding-stage-unlocks.md +230 -0
- package/skills/luckiest-marketing-plan/references/growth-patterns.md +148 -0
- package/skills/luckiest-marketing-plan/references/idea-cross-reference.md +265 -0
- package/skills/luckiest-marketing-plan/references/measurement-framework.md +213 -0
- package/skills/luckiest-marketing-plan/references/methodology.md +363 -0
- package/skills/luckiest-marketing-plan/references/ops-stack-mapping.md +197 -0
- package/skills/luckiest-marketing-plan/references/plan-template.md +494 -0
- package/skills/luckiest-marketing-plan/references/team-and-agency-model.md +278 -0
- package/skills/luckiest-marketing-psychology/ATTRIBUTION.md +31 -0
- package/skills/luckiest-marketing-psychology/CHANGELOG.md +32 -0
- package/skills/luckiest-marketing-psychology/SKILL.md +202 -0
- package/skills/luckiest-marketing-psychology/evals.json +88 -0
- package/skills/luckiest-model-router/ATTRIBUTION.md +11 -0
- package/skills/luckiest-model-router/CHANGELOG.md +45 -0
- package/skills/luckiest-model-router/SKILL.md +74 -0
- package/skills/luckiest-model-router/scripts/route.py +145 -0
- package/skills/luckiest-offers/ATTRIBUTION.md +39 -0
- package/skills/luckiest-offers/CHANGELOG.md +23 -0
- package/skills/luckiest-offers/SKILL.md +167 -0
- package/skills/luckiest-offers/references/bonus-stacking.md +150 -0
- package/skills/luckiest-offers/references/examples.md +215 -0
- package/skills/luckiest-offers/references/guarantee-design.md +172 -0
- package/skills/luckiest-offers/references/offer-anatomy.md +203 -0
- package/skills/luckiest-offers/references/offer-formats.md +240 -0
- package/skills/luckiest-offers/references/scarcity-urgency.md +175 -0
- package/skills/luckiest-offers/references/value-equation.md +134 -0
- package/skills/luckiest-onboarding/ATTRIBUTION.md +43 -0
- package/skills/luckiest-onboarding/CHANGELOG.md +31 -0
- package/skills/luckiest-onboarding/SKILL.md +243 -0
- package/skills/luckiest-onboarding/references/experiments.md +258 -0
- package/skills/luckiest-paywalls/ATTRIBUTION.md +39 -0
- package/skills/luckiest-paywalls/CHANGELOG.md +25 -0
- package/skills/luckiest-paywalls/SKILL.md +256 -0
- package/skills/luckiest-paywalls/evals/evals.json +93 -0
- package/skills/luckiest-paywalls/references/experiments.md +164 -0
- package/skills/luckiest-popups/ATTRIBUTION.md +35 -0
- package/skills/luckiest-popups/CHANGELOG.md +27 -0
- package/skills/luckiest-popups/SKILL.md +477 -0
- package/skills/luckiest-popups/evals/evals.json +107 -0
- package/skills/luckiest-pricing/ATTRIBUTION.md +35 -0
- package/skills/luckiest-pricing/CHANGELOG.md +31 -0
- package/skills/luckiest-pricing/SKILL.md +283 -0
- package/skills/luckiest-pricing/evals/evals.json +90 -0
- package/skills/luckiest-pricing/references/research-methods.md +152 -0
- package/skills/luckiest-pricing/references/tier-structure.md +232 -0
- package/skills/luckiest-product-marketing/ATTRIBUTION.md +38 -0
- package/skills/luckiest-product-marketing/CHANGELOG.md +23 -0
- package/skills/luckiest-product-marketing/SKILL.md +256 -0
- package/skills/luckiest-product-marketing/evals/evals.json +85 -0
- package/skills/luckiest-programmatic-seo/ATTRIBUTION.md +35 -0
- package/skills/luckiest-programmatic-seo/CHANGELOG.md +24 -0
- package/skills/luckiest-programmatic-seo/SKILL.md +286 -0
- package/skills/luckiest-programmatic-seo/evals/evals.json +95 -0
- package/skills/luckiest-programmatic-seo/references/playbooks.md +308 -0
- package/skills/luckiest-prompt-rewrite/ATTRIBUTION.md +39 -0
- package/skills/luckiest-prompt-rewrite/CHANGELOG.md +70 -0
- package/skills/luckiest-prompt-rewrite/LICENSE +21 -0
- package/skills/luckiest-prompt-rewrite/SKILL.md +494 -0
- package/skills/luckiest-prompt-rewrite/references/patterns.md +82 -0
- package/skills/luckiest-prompt-rewrite/references/templates.md +454 -0
- package/skills/luckiest-prospecting/ATTRIBUTION.md +29 -0
- package/skills/luckiest-prospecting/CHANGELOG.md +24 -0
- package/skills/luckiest-prospecting/SKILL.md +275 -0
- package/skills/luckiest-prospecting/evals/evals.json +107 -0
- package/skills/luckiest-prospecting/references/b2b-prospecting.md +106 -0
- package/skills/luckiest-prospecting/references/compliance.md +123 -0
- package/skills/luckiest-prospecting/references/data-sources.md +287 -0
- package/skills/luckiest-prospecting/references/local-prospecting.md +165 -0
- package/skills/luckiest-prospecting/references/saas-prospecting.md +123 -0
- package/skills/luckiest-public-relations/ATTRIBUTION.md +34 -0
- package/skills/luckiest-public-relations/CHANGELOG.md +23 -0
- package/skills/luckiest-public-relations/SKILL.md +154 -0
- package/skills/luckiest-public-relations/references/journalist-pitching.md +368 -0
- package/skills/luckiest-public-relations/references/media-outlets.md +207 -0
- package/skills/luckiest-public-relations/references/newsjacking.md +218 -0
- package/skills/luckiest-public-relations/references/press-platforms.md +188 -0
- package/skills/luckiest-referrals/ATTRIBUTION.md +41 -0
- package/skills/luckiest-referrals/CHANGELOG.md +23 -0
- package/skills/luckiest-referrals/SKILL.md +292 -0
- package/skills/luckiest-referrals/evals/evals.json +90 -0
- package/skills/luckiest-referrals/references/affiliate-programs.md +167 -0
- package/skills/luckiest-referrals/references/program-examples.md +143 -0
- package/skills/luckiest-revops/ATTRIBUTION.md +40 -0
- package/skills/luckiest-revops/CHANGELOG.md +24 -0
- package/skills/luckiest-revops/SKILL.md +362 -0
- package/skills/luckiest-revops/evals/evals.json +91 -0
- package/skills/luckiest-revops/references/automation-playbooks.md +290 -0
- package/skills/luckiest-revops/references/lifecycle-definitions.md +278 -0
- package/skills/luckiest-revops/references/routing-rules.md +203 -0
- package/skills/luckiest-revops/references/scoring-models.md +247 -0
- package/skills/luckiest-sales-enablement/ATTRIBUTION.md +39 -0
- package/skills/luckiest-sales-enablement/CHANGELOG.md +23 -0
- package/skills/luckiest-sales-enablement/SKILL.md +379 -0
- package/skills/luckiest-sales-enablement/evals/evals.json +91 -0
- package/skills/luckiest-sales-enablement/references/deck-frameworks.md +263 -0
- package/skills/luckiest-sales-enablement/references/demo-scripts.md +355 -0
- package/skills/luckiest-sales-enablement/references/objection-library.md +270 -0
- package/skills/luckiest-sales-enablement/references/one-pager-templates.md +208 -0
- package/skills/luckiest-schema/ATTRIBUTION.md +39 -0
- package/skills/luckiest-schema/CHANGELOG.md +25 -0
- package/skills/luckiest-schema/SKILL.md +209 -0
- package/skills/luckiest-schema/evals/evals.json +88 -0
- package/skills/luckiest-schema/references/schema-examples.md +398 -0
- package/skills/luckiest-seo-audit/ATTRIBUTION.md +39 -0
- package/skills/luckiest-seo-audit/CHANGELOG.md +23 -0
- package/skills/luckiest-seo-audit/SKILL.md +529 -0
- package/skills/luckiest-seo-audit/evals/evals.json +136 -0
- package/skills/luckiest-seo-audit/references/ai-writing-detection.md +200 -0
- package/skills/luckiest-seo-audit/references/international-seo.md +230 -0
- package/skills/luckiest-session-handoff/ATTRIBUTION.md +14 -0
- package/skills/luckiest-session-handoff/CHANGELOG.md +30 -0
- package/skills/luckiest-session-handoff/SKILL.md +96 -0
- package/skills/luckiest-signup/ATTRIBUTION.md +48 -0
- package/skills/luckiest-signup/CHANGELOG.md +24 -0
- package/skills/luckiest-signup/SKILL.md +382 -0
- package/skills/luckiest-signup/evals/evals.json +88 -0
- package/skills/luckiest-site-architecture/ATTRIBUTION.md +38 -0
- package/skills/luckiest-site-architecture/CHANGELOG.md +23 -0
- package/skills/luckiest-site-architecture/SKILL.md +383 -0
- package/skills/luckiest-site-architecture/evals/evals.json +88 -0
- package/skills/luckiest-site-architecture/references/mermaid-templates.md +216 -0
- package/skills/luckiest-site-architecture/references/navigation-patterns.md +305 -0
- package/skills/luckiest-site-architecture/references/site-type-templates.md +293 -0
- package/skills/luckiest-sms/ATTRIBUTION.md +27 -0
- package/skills/luckiest-sms/CHANGELOG.md +24 -0
- package/skills/luckiest-sms/SKILL.md +400 -0
- package/skills/luckiest-sms/evals/evals.json +100 -0
- package/skills/luckiest-sms/references/compliance.md +202 -0
- package/skills/luckiest-sms/references/platforms.md +318 -0
- package/skills/luckiest-sms/references/sequence-templates.md +282 -0
- package/skills/luckiest-social/ATTRIBUTION.md +27 -0
- package/skills/luckiest-social/CHANGELOG.md +24 -0
- package/skills/luckiest-social/SKILL.md +430 -0
- package/skills/luckiest-social/evals/evals.json +92 -0
- package/skills/luckiest-social/references/listening-sources-template.md +123 -0
- package/skills/luckiest-social/references/listening.md +283 -0
- package/skills/luckiest-social/references/platform-limits.md +110 -0
- package/skills/luckiest-social/references/platforms.md +170 -0
- package/skills/luckiest-social/references/post-templates.md +177 -0
- package/skills/luckiest-social/references/reverse-engineering.md +195 -0
- package/skills/luckiest-social/references/short-form-video.md +237 -0
- package/skills/luckiest-thinker/ATTRIBUTION.md +16 -0
- package/skills/luckiest-thinker/CHANGELOG.md +20 -0
- package/skills/luckiest-thinker/LICENSE +21 -0
- package/skills/luckiest-thinker/SKILL.md +92 -0
- package/skills/luckiest-thinker/references/distribution-calibration.md +106 -0
- package/skills/luckiest-thinker/references/research-foundations.md +214 -0
- package/skills/luckiest-thinker/references/thinking-patterns.md +138 -0
- package/skills/luckiest-trends/.skillignore +10 -0
- package/skills/luckiest-trends/ATTRIBUTION.md +43 -0
- package/skills/luckiest-trends/CHANGELOG.md +86 -0
- package/skills/luckiest-trends/LICENSE +21 -0
- package/skills/luckiest-trends/SKILL.md +194 -0
- package/skills/luckiest-trends/agents/openai.yaml +8 -0
- package/skills/luckiest-trends/references/engine-contract.md +2041 -0
- package/skills/luckiest-trends/references/save-html-brief.md +206 -0
- package/skills/luckiest-trends/scripts/briefing.py +272 -0
- package/skills/luckiest-trends/scripts/build-skill.sh +39 -0
- package/skills/luckiest-trends/scripts/compare.sh +61 -0
- package/skills/luckiest-trends/scripts/evaluate_search_quality.py +573 -0
- package/skills/luckiest-trends/scripts/last30days.py +1467 -0
- package/skills/luckiest-trends/scripts/lib/__init__.py +1 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/arxiv.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/bird_x.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/bluesky.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/categories.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/cjk.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/cluster.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/dates.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/dedupe.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/digg.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/entity_extract.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/env.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/fusion.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/github.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/grounding.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/hackernews.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/hiring_signals.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/html_render.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/http.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/instagram.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/jobs.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/linkedin.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/log.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/normalize.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/permission_preflight.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/perplexity.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/pinterest.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/pipeline.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/planner.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/polymarket.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/preflight.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/providers.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/quality_nudge.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/query.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/reddit.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_arctic.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_enrich.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_keyless.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_listing.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_public.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_rss.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_shreddit.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/relevance.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/render.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/rerank.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/resolve.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/schema.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/signals.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/skill_meta.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/snippet.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/subproc.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/techmeme.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/threads.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/tiktok.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/trustpilot.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/truthsocial.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/ui.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/web_search_keyless.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/xai_x.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/xiaohongshu_api.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/xquik.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/xurl_x.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/__pycache__/youtube_yt.cpython-313.pyc +0 -0
- package/skills/luckiest-trends/scripts/lib/arxiv.py +290 -0
- package/skills/luckiest-trends/scripts/lib/bird_x.py +639 -0
- package/skills/luckiest-trends/scripts/lib/bluesky.py +323 -0
- package/skills/luckiest-trends/scripts/lib/categories.py +283 -0
- package/skills/luckiest-trends/scripts/lib/chrome_cookies.py +414 -0
- package/skills/luckiest-trends/scripts/lib/cjk.py +107 -0
- package/skills/luckiest-trends/scripts/lib/cluster.py +271 -0
- package/skills/luckiest-trends/scripts/lib/competitors.py +198 -0
- package/skills/luckiest-trends/scripts/lib/cookie_extract.py +508 -0
- package/skills/luckiest-trends/scripts/lib/dates.py +169 -0
- package/skills/luckiest-trends/scripts/lib/dedupe.py +145 -0
- package/skills/luckiest-trends/scripts/lib/digg.py +440 -0
- package/skills/luckiest-trends/scripts/lib/entity_extract.py +127 -0
- package/skills/luckiest-trends/scripts/lib/env.py +1073 -0
- package/skills/luckiest-trends/scripts/lib/fanout.py +84 -0
- package/skills/luckiest-trends/scripts/lib/fusion.py +207 -0
- package/skills/luckiest-trends/scripts/lib/github.py +996 -0
- package/skills/luckiest-trends/scripts/lib/grounding.py +331 -0
- package/skills/luckiest-trends/scripts/lib/hackernews.py +357 -0
- package/skills/luckiest-trends/scripts/lib/health.py +92 -0
- package/skills/luckiest-trends/scripts/lib/hiring_signals.py +326 -0
- package/skills/luckiest-trends/scripts/lib/html_publish.py +73 -0
- package/skills/luckiest-trends/scripts/lib/html_render.py +738 -0
- package/skills/luckiest-trends/scripts/lib/http.py +372 -0
- package/skills/luckiest-trends/scripts/lib/instagram.py +521 -0
- package/skills/luckiest-trends/scripts/lib/jobs.py +708 -0
- package/skills/luckiest-trends/scripts/lib/linkedin.py +363 -0
- package/skills/luckiest-trends/scripts/lib/log.py +35 -0
- package/skills/luckiest-trends/scripts/lib/normalize.py +759 -0
- package/skills/luckiest-trends/scripts/lib/permission_preflight.py +242 -0
- package/skills/luckiest-trends/scripts/lib/perplexity.py +745 -0
- package/skills/luckiest-trends/scripts/lib/pinterest.py +154 -0
- package/skills/luckiest-trends/scripts/lib/pipeline.py +1720 -0
- package/skills/luckiest-trends/scripts/lib/planner.py +763 -0
- package/skills/luckiest-trends/scripts/lib/polymarket.py +794 -0
- package/skills/luckiest-trends/scripts/lib/preflight.py +119 -0
- package/skills/luckiest-trends/scripts/lib/providers.py +380 -0
- package/skills/luckiest-trends/scripts/lib/quality_nudge.py +383 -0
- package/skills/luckiest-trends/scripts/lib/query.py +202 -0
- package/skills/luckiest-trends/scripts/lib/reddit.py +731 -0
- package/skills/luckiest-trends/scripts/lib/reddit_arctic.py +92 -0
- package/skills/luckiest-trends/scripts/lib/reddit_enrich.py +322 -0
- package/skills/luckiest-trends/scripts/lib/reddit_keyless.py +329 -0
- package/skills/luckiest-trends/scripts/lib/reddit_listing.py +188 -0
- package/skills/luckiest-trends/scripts/lib/reddit_public.py +275 -0
- package/skills/luckiest-trends/scripts/lib/reddit_rss.py +224 -0
- package/skills/luckiest-trends/scripts/lib/reddit_shreddit.py +184 -0
- package/skills/luckiest-trends/scripts/lib/relevance.py +200 -0
- package/skills/luckiest-trends/scripts/lib/render.py +2130 -0
- package/skills/luckiest-trends/scripts/lib/rerank.py +708 -0
- package/skills/luckiest-trends/scripts/lib/resolve.py +345 -0
- package/skills/luckiest-trends/scripts/lib/safari_cookies.py +193 -0
- package/skills/luckiest-trends/scripts/lib/schema.py +319 -0
- package/skills/luckiest-trends/scripts/lib/setup_wizard.py +843 -0
- package/skills/luckiest-trends/scripts/lib/signals.py +334 -0
- package/skills/luckiest-trends/scripts/lib/skill_meta.py +33 -0
- package/skills/luckiest-trends/scripts/lib/snippet.py +51 -0
- package/skills/luckiest-trends/scripts/lib/subproc.py +116 -0
- package/skills/luckiest-trends/scripts/lib/techmeme.py +239 -0
- package/skills/luckiest-trends/scripts/lib/threads.py +207 -0
- package/skills/luckiest-trends/scripts/lib/tiktok.py +596 -0
- package/skills/luckiest-trends/scripts/lib/transcribe.py +235 -0
- package/skills/luckiest-trends/scripts/lib/trustpilot.py +277 -0
- package/skills/luckiest-trends/scripts/lib/truthsocial.py +162 -0
- package/skills/luckiest-trends/scripts/lib/ui.py +626 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/LICENSE +21 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/bird-search.mjs +147 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/cookies.js +220 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/features.json +17 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/paginate-cursor.js +37 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/query-ids.json +20 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/runtime-features.js +151 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/runtime-query-ids.js +264 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/twitter-client-base.js +129 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/twitter-client-constants.js +50 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/twitter-client-features.js +347 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/twitter-client-search.js +157 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/twitter-client-types.js +2 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/twitter-client-utils.js +511 -0
- package/skills/luckiest-trends/scripts/lib/vendor/bird-search/package.json +13 -0
- package/skills/luckiest-trends/scripts/lib/web_fetch_keyless.py +105 -0
- package/skills/luckiest-trends/scripts/lib/web_search_keyless.py +158 -0
- package/skills/luckiest-trends/scripts/lib/xai_x.py +236 -0
- package/skills/luckiest-trends/scripts/lib/xiaohongshu_api.py +162 -0
- package/skills/luckiest-trends/scripts/lib/xquik.py +404 -0
- package/skills/luckiest-trends/scripts/lib/xurl_x.py +171 -0
- package/skills/luckiest-trends/scripts/lib/youtube_yt.py +1365 -0
- package/skills/luckiest-trends/scripts/setup-keychain.sh +123 -0
- package/skills/luckiest-trends/scripts/setup-pass.sh +132 -0
- package/skills/luckiest-trends/scripts/store.py +1020 -0
- package/skills/luckiest-trends/scripts/test-v1-vs-v2.sh +220 -0
- package/skills/luckiest-trends/scripts/test_device_auth.py +128 -0
- package/skills/luckiest-trends/scripts/verify_v3.py +195 -0
- package/skills/luckiest-trends/scripts/watchlist.py +318 -0
- package/skills/luckiest-video/ATTRIBUTION.md +27 -0
- package/skills/luckiest-video/CHANGELOG.md +23 -0
- package/skills/luckiest-video/SKILL.md +371 -0
- package/skills/luckiest-video/evals/evals.json +88 -0
- package/skills/luckiest-video/references/ai-video-prompting.md +175 -0
- package/skills/remix/SKILL.md +32 -0
- package/commands/luckiest/go.md +0 -45
- package/commands/luckiest/plan.md +0 -55
- /package/commands/{luckiest/charms.md → charms.md} +0 -0
- /package/commands/{luckiest/helpers.md → helpers.md} +0 -0
- /package/commands/{luckiest/home.md → home.md} +0 -0
- /package/commands/{luckiest/leaderboard.md → leaderboard.md} +0 -0
- /package/commands/{luckiest/merge.md → merge.md} +0 -0
- /package/commands/{luckiest/overlaps.md → overlaps.md} +0 -0
- /package/commands/{luckiest/skills.md → skills.md} +0 -0
- /package/commands/{luckiest/start.md → start.md} +0 -0
- /package/commands/{luckiest/status.md → status.md} +0 -0
- /package/commands/{luckiest/updates.md → updates.md} +0 -0
- /package/commands/{luckiest/vouch.md → vouch.md} +0 -0
- /package/commands/{luckiest/wishes.md → wishes.md} +0 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"""Bluesky search via AT Protocol (requires app password).
|
|
2
|
+
|
|
3
|
+
Uses bsky.social for auth and api.bsky.app for post search (the canonical
|
|
4
|
+
authenticated AppView). The previous default `public.api.bsky.app` is the
|
|
5
|
+
unauthenticated public mirror, which BunnyCDN now blocks for searchPosts
|
|
6
|
+
regardless of auth header (verified 2026-05-04). Override the search host
|
|
7
|
+
via BSKY_SEARCH_HOST env var if Bluesky migrates infrastructure again.
|
|
8
|
+
|
|
9
|
+
Requires BSKY_HANDLE and BSKY_APP_PASSWORD env vars. App passwords are
|
|
10
|
+
19-char xxxx-xxxx-xxxx-xxxx; generate at bsky.app/settings/app-passwords.
|
|
11
|
+
The createSession endpoint accepts main-account passwords too, but they're
|
|
12
|
+
bad hygiene (no scope, can't revoke individually).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import math
|
|
16
|
+
import os
|
|
17
|
+
import re
|
|
18
|
+
import sys
|
|
19
|
+
import time
|
|
20
|
+
from datetime import datetime, timezone
|
|
21
|
+
from typing import Any, Dict, List, Optional
|
|
22
|
+
|
|
23
|
+
from . import http, log
|
|
24
|
+
|
|
25
|
+
BSKY_SESSION_URL = "https://bsky.social/xrpc/com.atproto.server.createSession"
|
|
26
|
+
_DEFAULT_BSKY_SEARCH_HOST = "api.bsky.app"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _resolve_search_url(config: Optional[Dict[str, Any]] = None) -> str:
|
|
30
|
+
"""Resolve the Bluesky search URL with BSKY_SEARCH_HOST override.
|
|
31
|
+
|
|
32
|
+
Default is api.bsky.app. Override via BSKY_SEARCH_HOST in shell env or
|
|
33
|
+
.env file. The project's env.py loads .env into config but not into
|
|
34
|
+
os.environ, so check both — same hybrid pattern as last30days.py for
|
|
35
|
+
LAST30DAYS_STORE.
|
|
36
|
+
|
|
37
|
+
Hardens user-supplied host values against three common mis-configurations:
|
|
38
|
+
whitespace (e.g. " api.bsky.app "), embedded path components (e.g.
|
|
39
|
+
"api.bsky.app/xrpc/proxy") that would double the /xrpc/ segment, and
|
|
40
|
+
embedded scheme prefixes (e.g. "https://api.bsky.app"). On any of these
|
|
41
|
+
we log a warning and fall back to the default rather than building an
|
|
42
|
+
invalid URL with an opaque downstream error.
|
|
43
|
+
"""
|
|
44
|
+
config = config or {}
|
|
45
|
+
raw = (
|
|
46
|
+
os.environ.get("BSKY_SEARCH_HOST")
|
|
47
|
+
or config.get("BSKY_SEARCH_HOST")
|
|
48
|
+
or _DEFAULT_BSKY_SEARCH_HOST
|
|
49
|
+
)
|
|
50
|
+
host = raw.strip().rstrip("/")
|
|
51
|
+
# Strip embedded scheme so users who paste full URLs do not break the f-string.
|
|
52
|
+
for prefix in ("https://", "http://"):
|
|
53
|
+
if host.lower().startswith(prefix):
|
|
54
|
+
host = host[len(prefix):]
|
|
55
|
+
break
|
|
56
|
+
if not host or "/" in host or " " in host:
|
|
57
|
+
# Embedded path or whitespace remains — don't trust it. Default + log.
|
|
58
|
+
if raw != _DEFAULT_BSKY_SEARCH_HOST:
|
|
59
|
+
_log(
|
|
60
|
+
f"BSKY_SEARCH_HOST={raw!r} is not a bare hostname; "
|
|
61
|
+
f"falling back to default {_DEFAULT_BSKY_SEARCH_HOST!r}"
|
|
62
|
+
)
|
|
63
|
+
host = _DEFAULT_BSKY_SEARCH_HOST
|
|
64
|
+
return f"https://{host}/xrpc/app.bsky.feed.searchPosts"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# App-password format: xxxx-xxxx-xxxx-xxxx (19 chars, lowercase alphanumeric
|
|
68
|
+
# with three hyphens at fixed positions).
|
|
69
|
+
_APP_PASSWORD_RE = re.compile(r"^[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}$")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _validate_app_password_format(value) -> bool:
|
|
73
|
+
"""Return True if value matches Bluesky's 19-char app-password format.
|
|
74
|
+
|
|
75
|
+
False for non-strings (None, int, list) so callers passing config dict
|
|
76
|
+
values directly don't crash. Detect-but-not-gate: the createSession
|
|
77
|
+
endpoint also accepts main-account passwords, so failing this check is
|
|
78
|
+
a hygiene smell, not a hard error.
|
|
79
|
+
"""
|
|
80
|
+
if not isinstance(value, str):
|
|
81
|
+
return False
|
|
82
|
+
return bool(_APP_PASSWORD_RE.fullmatch(value))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
DEPTH_CONFIG = {
|
|
86
|
+
"quick": 15,
|
|
87
|
+
"default": 30,
|
|
88
|
+
"deep": 60,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Module-level token cache (valid for the lifetime of a single research run)
|
|
92
|
+
_cached_token: Optional[str] = None
|
|
93
|
+
_token_created_at: float = 0.0
|
|
94
|
+
_session_error: Optional[str] = None
|
|
95
|
+
_TOKEN_MAX_AGE_SECONDS = 5400 # 90 minutes (conservative, tokens last ~2 hours)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _log(msg: str):
|
|
99
|
+
log.source_log("Bluesky", msg, tty_only=False)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _create_session(handle: str, app_password: str) -> Optional[str]:
|
|
103
|
+
"""Create an AT Protocol session and return the access token.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
handle: Bluesky handle (e.g. user.bsky.social)
|
|
107
|
+
app_password: App password from bsky.app/settings/app-passwords
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Access JWT string, or None on failure. Sets _session_error on failure.
|
|
111
|
+
"""
|
|
112
|
+
global _cached_token, _token_created_at, _session_error
|
|
113
|
+
if _cached_token and (time.monotonic() - _token_created_at < _TOKEN_MAX_AGE_SECONDS):
|
|
114
|
+
return _cached_token
|
|
115
|
+
if _cached_token:
|
|
116
|
+
_log("Session token expired, re-authenticating")
|
|
117
|
+
_cached_token = None
|
|
118
|
+
_token_created_at = 0.0
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
response = http.request(
|
|
122
|
+
"POST",
|
|
123
|
+
BSKY_SESSION_URL,
|
|
124
|
+
json_data={"identifier": handle, "password": app_password},
|
|
125
|
+
timeout=15,
|
|
126
|
+
)
|
|
127
|
+
token = response.get("accessJwt")
|
|
128
|
+
if token:
|
|
129
|
+
_cached_token = token
|
|
130
|
+
_token_created_at = time.monotonic()
|
|
131
|
+
_session_error = None
|
|
132
|
+
_log("Session created successfully")
|
|
133
|
+
return token
|
|
134
|
+
_log("No accessJwt in session response")
|
|
135
|
+
_session_error = "No accessJwt in session response"
|
|
136
|
+
return None
|
|
137
|
+
except http.HTTPError as e:
|
|
138
|
+
if e.status_code == 403 and e.body and "cloudflare" in e.body.lower():
|
|
139
|
+
_session_error = "Cloudflare blocked the request (403 Forbidden). This is a network-level block, not an auth issue. Try a different network or VPN."
|
|
140
|
+
elif e.status_code == 401:
|
|
141
|
+
_session_error = "Invalid credentials (401 Unauthorized). Check BSKY_HANDLE and BSKY_APP_PASSWORD."
|
|
142
|
+
else:
|
|
143
|
+
_session_error = f"Session request failed: {e}"
|
|
144
|
+
_log(f"Session creation failed: {_session_error}")
|
|
145
|
+
return None
|
|
146
|
+
except Exception as e:
|
|
147
|
+
_session_error = f"Session request failed: {type(e).__name__}: {e}"
|
|
148
|
+
_log(f"Session creation failed: {_session_error}")
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _reset_session_cache() -> None:
|
|
153
|
+
global _cached_token, _token_created_at, _session_error
|
|
154
|
+
_cached_token = None
|
|
155
|
+
_token_created_at = 0.0
|
|
156
|
+
_session_error = None
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _extract_core_subject(topic: str) -> str:
|
|
160
|
+
"""Extract core subject from verbose query for Bluesky search."""
|
|
161
|
+
from .query import SOCIAL_NOISE, extract_core_subject
|
|
162
|
+
return extract_core_subject(topic, noise=SOCIAL_NOISE)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _parse_date(item: Dict[str, Any]) -> Optional[str]:
|
|
166
|
+
"""Parse date from Bluesky post to YYYY-MM-DD.
|
|
167
|
+
|
|
168
|
+
AT Protocol uses ISO 8601 format in indexedAt and createdAt fields.
|
|
169
|
+
"""
|
|
170
|
+
for key in ("indexedAt", "createdAt"):
|
|
171
|
+
val = item.get(key)
|
|
172
|
+
if val and isinstance(val, str):
|
|
173
|
+
try:
|
|
174
|
+
dt = datetime.fromisoformat(val.replace("Z", "+00:00"))
|
|
175
|
+
return dt.strftime("%Y-%m-%d")
|
|
176
|
+
except (ValueError, TypeError):
|
|
177
|
+
pass
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def search_bluesky(
|
|
182
|
+
topic: str,
|
|
183
|
+
from_date: str,
|
|
184
|
+
to_date: str,
|
|
185
|
+
depth: str = "default",
|
|
186
|
+
config: Optional[Dict[str, Any]] = None,
|
|
187
|
+
) -> Dict[str, Any]:
|
|
188
|
+
"""Search Bluesky via AT Protocol API.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
topic: Search topic
|
|
192
|
+
from_date: Start date (YYYY-MM-DD)
|
|
193
|
+
to_date: End date (YYYY-MM-DD)
|
|
194
|
+
depth: 'quick', 'default', or 'deep'
|
|
195
|
+
config: Config dict with BSKY_HANDLE and BSKY_APP_PASSWORD
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Dict with 'posts' list from AT Protocol response.
|
|
199
|
+
"""
|
|
200
|
+
config = config or {}
|
|
201
|
+
handle = config.get("BSKY_HANDLE", "")
|
|
202
|
+
app_password = config.get("BSKY_APP_PASSWORD", "")
|
|
203
|
+
|
|
204
|
+
if not handle or not app_password:
|
|
205
|
+
return {"posts": [], "error": "Bluesky credentials not configured"}
|
|
206
|
+
|
|
207
|
+
# One-shot hygiene warning if BSKY_APP_PASSWORD is not in app-password
|
|
208
|
+
# form. createSession accepts main-account passwords too — but main
|
|
209
|
+
# passwords have no scope (full account access), can't be revoked
|
|
210
|
+
# individually, and rotating them breaks every service that holds them.
|
|
211
|
+
# We warn but do not gate, matching the project's detect-don't-block
|
|
212
|
+
# philosophy elsewhere.
|
|
213
|
+
if not _validate_app_password_format(app_password):
|
|
214
|
+
_log(
|
|
215
|
+
"BSKY_APP_PASSWORD does not look like an app password "
|
|
216
|
+
"(expected xxxx-xxxx-xxxx-xxxx, 19 chars). It may be a main "
|
|
217
|
+
"account password — those work but are bad hygiene. Generate "
|
|
218
|
+
"an app password at https://bsky.app/settings/app-passwords"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
count = DEPTH_CONFIG.get(depth, DEPTH_CONFIG["default"])
|
|
222
|
+
core_topic = _extract_core_subject(topic)
|
|
223
|
+
|
|
224
|
+
_log(f"Searching for '{core_topic}' (depth={depth}, limit={count})")
|
|
225
|
+
|
|
226
|
+
from urllib.parse import urlencode
|
|
227
|
+
params = {
|
|
228
|
+
"q": core_topic,
|
|
229
|
+
"limit": str(min(count, 100)),
|
|
230
|
+
"sort": "top",
|
|
231
|
+
}
|
|
232
|
+
url = f"{_resolve_search_url(config)}?{urlencode(params)}"
|
|
233
|
+
|
|
234
|
+
def _auth_and_search() -> tuple[Optional[Dict[str, Any]], Optional[str]]:
|
|
235
|
+
token = _create_session(handle, app_password)
|
|
236
|
+
if not token:
|
|
237
|
+
error_msg = _session_error or "Bluesky session creation failed (unknown error)"
|
|
238
|
+
return None, error_msg
|
|
239
|
+
try:
|
|
240
|
+
response = http.request(
|
|
241
|
+
"GET", url,
|
|
242
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
243
|
+
timeout=30,
|
|
244
|
+
)
|
|
245
|
+
return response, None
|
|
246
|
+
except http.HTTPError as e:
|
|
247
|
+
_log(f"Search failed: {e}")
|
|
248
|
+
if e.status_code == 401:
|
|
249
|
+
_reset_session_cache()
|
|
250
|
+
return None, "refresh"
|
|
251
|
+
if e.status_code == 403 and e.body and "cloudflare" in e.body.lower():
|
|
252
|
+
return None, "Bluesky search blocked by Cloudflare (403). This is a network-level block - try a different network or VPN."
|
|
253
|
+
return None, f"Bluesky search failed: {e}"
|
|
254
|
+
except Exception as e:
|
|
255
|
+
_log(f"Search failed: {e}")
|
|
256
|
+
return None, f"Bluesky search failed: {type(e).__name__}: {e}"
|
|
257
|
+
|
|
258
|
+
response, error_msg = _auth_and_search()
|
|
259
|
+
if error_msg == "refresh":
|
|
260
|
+
_log("Session expired; recreating token and retrying once")
|
|
261
|
+
response, error_msg = _auth_and_search()
|
|
262
|
+
if error_msg:
|
|
263
|
+
return {"posts": [], "error": error_msg}
|
|
264
|
+
if response is None:
|
|
265
|
+
return {"posts": [], "error": "Bluesky search failed (unknown error)"}
|
|
266
|
+
|
|
267
|
+
posts = response.get("posts", [])
|
|
268
|
+
_log(f"Found {len(posts)} posts")
|
|
269
|
+
return response
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def parse_bluesky_response(response: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
273
|
+
"""Parse AT Protocol response into normalized item dicts.
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
List of item dicts ready for normalization.
|
|
277
|
+
"""
|
|
278
|
+
posts = response.get("posts", [])
|
|
279
|
+
items = []
|
|
280
|
+
|
|
281
|
+
for i, post in enumerate(posts):
|
|
282
|
+
record = post.get("record") or {}
|
|
283
|
+
text = record.get("text") or ""
|
|
284
|
+
|
|
285
|
+
author = post.get("author") or {}
|
|
286
|
+
handle = author.get("handle") or ""
|
|
287
|
+
display_name = author.get("displayName") or handle
|
|
288
|
+
|
|
289
|
+
# Post URI -> URL
|
|
290
|
+
# URI format: at://did:plc:xxx/app.bsky.feed.post/rkey
|
|
291
|
+
uri = post.get("uri") or ""
|
|
292
|
+
rkey = uri.rsplit("/", 1)[-1] if uri else ""
|
|
293
|
+
url = f"https://bsky.app/profile/{handle}/post/{rkey}" if handle and rkey else ""
|
|
294
|
+
|
|
295
|
+
likes = post.get("likeCount") or 0
|
|
296
|
+
reposts = post.get("repostCount") or 0
|
|
297
|
+
replies = post.get("replyCount") or 0
|
|
298
|
+
quotes = post.get("quoteCount") or 0
|
|
299
|
+
|
|
300
|
+
date_str = _parse_date(post) or _parse_date(record)
|
|
301
|
+
|
|
302
|
+
# Relevance: position-based (AT Protocol sorts by relevance with sort=top)
|
|
303
|
+
rank_score = max(0.3, 1.0 - (i * 0.02))
|
|
304
|
+
engagement_boost = min(0.2, math.log1p(likes + reposts) / 40)
|
|
305
|
+
relevance = min(1.0, rank_score * 0.7 + engagement_boost + 0.1)
|
|
306
|
+
|
|
307
|
+
items.append({
|
|
308
|
+
"handle": handle,
|
|
309
|
+
"display_name": display_name,
|
|
310
|
+
"text": text,
|
|
311
|
+
"url": url,
|
|
312
|
+
"date": date_str,
|
|
313
|
+
"engagement": {
|
|
314
|
+
"likes": likes,
|
|
315
|
+
"reposts": reposts,
|
|
316
|
+
"replies": replies,
|
|
317
|
+
"quotes": quotes,
|
|
318
|
+
},
|
|
319
|
+
"relevance": round(relevance, 2),
|
|
320
|
+
"why_relevant": f"Bluesky: @{handle}: {text[:60]}" if text else f"Bluesky: {handle}",
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
return items
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""Category-peer subreddit map for Step 0.55 community resolution.
|
|
2
|
+
|
|
3
|
+
When a topic is a product in a known category (AI image generation, AI coding
|
|
4
|
+
agents, SaaS screen recording, etc.), brand-specific subreddits returned by
|
|
5
|
+
WebSearch are insufficient: cross-product technique discussion lives in
|
|
6
|
+
category-peer subs. This module classifies a topic into a category by matching
|
|
7
|
+
compound-term patterns against the lowercased topic string, then returns the
|
|
8
|
+
priority-ordered peer subreddit list for that category.
|
|
9
|
+
|
|
10
|
+
The map is intentionally small, curated, and code-reviewed. Adding a new
|
|
11
|
+
category is a code change; there is no user-editable override surface.
|
|
12
|
+
|
|
13
|
+
False-positive guard: every pattern is either a multi-word compound (e.g.
|
|
14
|
+
"image generation", "text to image") or a domain-specific single word
|
|
15
|
+
(e.g. "midjourney", "stablediffusion"). Bare common nouns like "image",
|
|
16
|
+
"ai", or "model" are never used as patterns.
|
|
17
|
+
|
|
18
|
+
First-match-wins: categories are evaluated in declared order. Entries are
|
|
19
|
+
sorted from most-specific to least-specific so narrower categories claim a
|
|
20
|
+
topic before broader ones. For example, `ai_image_generation` appears
|
|
21
|
+
before `ai_chat_model` so "gpt image 2" matches the image-gen category.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from typing import List, Optional, TypedDict
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class _CategoryEntry(TypedDict):
|
|
30
|
+
patterns: List[str]
|
|
31
|
+
peer_subs: List[str]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
CATEGORY_PEERS: dict[str, _CategoryEntry] = {
|
|
35
|
+
"ai_image_generation": {
|
|
36
|
+
"patterns": [
|
|
37
|
+
"image generation",
|
|
38
|
+
"image gen",
|
|
39
|
+
"text to image",
|
|
40
|
+
"text-to-image",
|
|
41
|
+
"gpt image",
|
|
42
|
+
"gpt-image",
|
|
43
|
+
"nano banana",
|
|
44
|
+
"midjourney",
|
|
45
|
+
"stable diffusion",
|
|
46
|
+
"stablediffusion",
|
|
47
|
+
"dall-e",
|
|
48
|
+
"dalle",
|
|
49
|
+
"flux.1",
|
|
50
|
+
"flux schnell",
|
|
51
|
+
"imagen",
|
|
52
|
+
"seedance",
|
|
53
|
+
"ideogram",
|
|
54
|
+
"recraft",
|
|
55
|
+
],
|
|
56
|
+
"peer_subs": [
|
|
57
|
+
"StableDiffusion",
|
|
58
|
+
"midjourney",
|
|
59
|
+
"dalle2",
|
|
60
|
+
"aiArt",
|
|
61
|
+
"PromptEngineering",
|
|
62
|
+
"MediaSynthesis",
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
"ai_video_generation": {
|
|
66
|
+
"patterns": [
|
|
67
|
+
"video generation",
|
|
68
|
+
"text to video",
|
|
69
|
+
"text-to-video",
|
|
70
|
+
"sora",
|
|
71
|
+
"veo 3",
|
|
72
|
+
"veo3",
|
|
73
|
+
"runway gen",
|
|
74
|
+
"kling",
|
|
75
|
+
"pika labs",
|
|
76
|
+
"luma dream machine",
|
|
77
|
+
"hailuo",
|
|
78
|
+
],
|
|
79
|
+
"peer_subs": [
|
|
80
|
+
"aivideo",
|
|
81
|
+
"StableDiffusion",
|
|
82
|
+
"runwayml",
|
|
83
|
+
"singularity",
|
|
84
|
+
"MediaSynthesis",
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
"ai_music_generation": {
|
|
88
|
+
"patterns": [
|
|
89
|
+
"music generation",
|
|
90
|
+
"ai music",
|
|
91
|
+
"suno",
|
|
92
|
+
"udio",
|
|
93
|
+
"riffusion",
|
|
94
|
+
"stable audio",
|
|
95
|
+
],
|
|
96
|
+
"peer_subs": [
|
|
97
|
+
"SunoAI",
|
|
98
|
+
"udiomusic",
|
|
99
|
+
"aimusic",
|
|
100
|
+
"artificial",
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
"ai_coding_agent": {
|
|
104
|
+
"patterns": [
|
|
105
|
+
"claude code",
|
|
106
|
+
"cursor ide",
|
|
107
|
+
"github copilot",
|
|
108
|
+
"windsurf",
|
|
109
|
+
"aider",
|
|
110
|
+
"cline",
|
|
111
|
+
"openclaw",
|
|
112
|
+
"hermes agent",
|
|
113
|
+
"continue.dev",
|
|
114
|
+
"codeium",
|
|
115
|
+
"sweep ai",
|
|
116
|
+
"devin ai",
|
|
117
|
+
"coding agent",
|
|
118
|
+
"coding assistant",
|
|
119
|
+
],
|
|
120
|
+
"peer_subs": [
|
|
121
|
+
"ChatGPTCoding",
|
|
122
|
+
"LocalLLaMA",
|
|
123
|
+
"singularity",
|
|
124
|
+
"PromptEngineering",
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
"ai_agent_framework": {
|
|
128
|
+
"patterns": [
|
|
129
|
+
"agent framework",
|
|
130
|
+
"agentic framework",
|
|
131
|
+
"langchain",
|
|
132
|
+
"langgraph",
|
|
133
|
+
"crewai",
|
|
134
|
+
"autogen",
|
|
135
|
+
"llamaindex",
|
|
136
|
+
"dspy",
|
|
137
|
+
"smolagents",
|
|
138
|
+
],
|
|
139
|
+
"peer_subs": [
|
|
140
|
+
"LangChain",
|
|
141
|
+
"LocalLLaMA",
|
|
142
|
+
"AI_Agents",
|
|
143
|
+
"MachineLearning",
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
"ai_chat_model": {
|
|
147
|
+
"patterns": [
|
|
148
|
+
"gpt-5",
|
|
149
|
+
"gpt-4",
|
|
150
|
+
"claude opus",
|
|
151
|
+
"claude sonnet",
|
|
152
|
+
"claude haiku",
|
|
153
|
+
"gemini pro",
|
|
154
|
+
"gemini flash",
|
|
155
|
+
"llama 3",
|
|
156
|
+
"llama 4",
|
|
157
|
+
"deepseek",
|
|
158
|
+
"qwen",
|
|
159
|
+
"mistral large",
|
|
160
|
+
"grok",
|
|
161
|
+
],
|
|
162
|
+
"peer_subs": [
|
|
163
|
+
"LocalLLaMA",
|
|
164
|
+
"ChatGPT",
|
|
165
|
+
"ClaudeAI",
|
|
166
|
+
"singularity",
|
|
167
|
+
"artificial",
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
"saas_screen_recording": {
|
|
171
|
+
"patterns": [
|
|
172
|
+
"screen recording",
|
|
173
|
+
"screen recorder",
|
|
174
|
+
"loom video",
|
|
175
|
+
"tella screen",
|
|
176
|
+
"vidyard",
|
|
177
|
+
"screen capture tool",
|
|
178
|
+
],
|
|
179
|
+
"peer_subs": [
|
|
180
|
+
"SaaS",
|
|
181
|
+
"screenrecording",
|
|
182
|
+
"productivity",
|
|
183
|
+
"Entrepreneur",
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
"saas_productivity": {
|
|
187
|
+
"patterns": [
|
|
188
|
+
"notion app",
|
|
189
|
+
"obsidian plugin",
|
|
190
|
+
"obsidian app",
|
|
191
|
+
"linear app",
|
|
192
|
+
"asana",
|
|
193
|
+
"clickup",
|
|
194
|
+
"productivity app",
|
|
195
|
+
],
|
|
196
|
+
"peer_subs": [
|
|
197
|
+
"productivity",
|
|
198
|
+
"SaaS",
|
|
199
|
+
"ObsidianMD",
|
|
200
|
+
"Notion",
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
"prediction_markets": {
|
|
204
|
+
"patterns": [
|
|
205
|
+
"polymarket",
|
|
206
|
+
"kalshi",
|
|
207
|
+
"prediction market",
|
|
208
|
+
"event contracts",
|
|
209
|
+
"manifold markets",
|
|
210
|
+
],
|
|
211
|
+
"peer_subs": [
|
|
212
|
+
"Polymarket",
|
|
213
|
+
"Kalshi",
|
|
214
|
+
"predictionmarkets",
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
"crypto_defi": {
|
|
218
|
+
"patterns": [
|
|
219
|
+
"defi protocol",
|
|
220
|
+
"yield farming",
|
|
221
|
+
"liquidity pool",
|
|
222
|
+
"stablecoin",
|
|
223
|
+
"ethereum layer",
|
|
224
|
+
"layer 2",
|
|
225
|
+
"l2 rollup",
|
|
226
|
+
],
|
|
227
|
+
"peer_subs": [
|
|
228
|
+
"defi",
|
|
229
|
+
"ethfinance",
|
|
230
|
+
"CryptoCurrency",
|
|
231
|
+
"ethereum",
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
"dev_tool_cli": {
|
|
235
|
+
"patterns": [
|
|
236
|
+
"cli tool",
|
|
237
|
+
"command line tool",
|
|
238
|
+
"terminal app",
|
|
239
|
+
"dev tool",
|
|
240
|
+
],
|
|
241
|
+
"peer_subs": [
|
|
242
|
+
"commandline",
|
|
243
|
+
"programming",
|
|
244
|
+
"webdev",
|
|
245
|
+
],
|
|
246
|
+
},
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def detect_category(topic: Optional[str]) -> Optional[str]:
|
|
251
|
+
"""Classify a topic into a known category by compound-term match.
|
|
252
|
+
|
|
253
|
+
Returns the category id (e.g. "ai_image_generation") or None if no
|
|
254
|
+
category's patterns match. Matching is case-insensitive substring over
|
|
255
|
+
the lowercased topic. Declaration order wins (first-match-wins), so the
|
|
256
|
+
map is ordered from most-specific to least-specific.
|
|
257
|
+
|
|
258
|
+
A None or empty topic returns None. Classification never raises on
|
|
259
|
+
normal string inputs; callers do not need to wrap in try/except for
|
|
260
|
+
typical paths, though defensive callers may.
|
|
261
|
+
"""
|
|
262
|
+
if not topic:
|
|
263
|
+
return None
|
|
264
|
+
lowered = topic.lower()
|
|
265
|
+
for category_id, entry in CATEGORY_PEERS.items():
|
|
266
|
+
for pattern in entry["patterns"]:
|
|
267
|
+
if pattern in lowered:
|
|
268
|
+
return category_id
|
|
269
|
+
return None
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def peer_subs_for(category_id: Optional[str]) -> List[str]:
|
|
273
|
+
"""Return the priority-ordered peer subreddit list for a category.
|
|
274
|
+
|
|
275
|
+
Returns an empty list for None or unknown category ids. The returned
|
|
276
|
+
list is a fresh copy; callers may safely mutate it.
|
|
277
|
+
"""
|
|
278
|
+
if not category_id:
|
|
279
|
+
return []
|
|
280
|
+
entry = CATEGORY_PEERS.get(category_id)
|
|
281
|
+
if not entry:
|
|
282
|
+
return []
|
|
283
|
+
return list(entry["peer_subs"])
|