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,626 @@
|
|
|
1
|
+
"""Terminal UI utilities for last30days skill."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import time
|
|
5
|
+
import threading
|
|
6
|
+
import random
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from .render import _skill_version
|
|
10
|
+
|
|
11
|
+
# Check if we're in a real terminal (not captured by Claude Code)
|
|
12
|
+
IS_TTY = sys.stderr.isatty()
|
|
13
|
+
|
|
14
|
+
# ANSI color codes
|
|
15
|
+
class Colors:
|
|
16
|
+
PURPLE = '\033[95m'
|
|
17
|
+
BLUE = '\033[94m'
|
|
18
|
+
CYAN = '\033[96m'
|
|
19
|
+
GREEN = '\033[92m'
|
|
20
|
+
YELLOW = '\033[93m'
|
|
21
|
+
RED = '\033[91m'
|
|
22
|
+
BOLD = '\033[1m'
|
|
23
|
+
DIM = '\033[2m'
|
|
24
|
+
RESET = '\033[0m'
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
BANNER = f"""{Colors.PURPLE}{Colors.BOLD}
|
|
28
|
+
██╗ █████╗ ███████╗████████╗██████╗ ██████╗ ██████╗ █████╗ ██╗ ██╗███████╗
|
|
29
|
+
██║ ██╔══██╗██╔════╝╚══██╔══╝╚════██╗██╔═████╗██╔══██╗██╔══██╗╚██╗ ██╔╝██╔════╝
|
|
30
|
+
██║ ███████║███████╗ ██║ █████╔╝██║██╔██║██║ ██║███████║ ╚████╔╝ ███████╗
|
|
31
|
+
██║ ██╔══██║╚════██║ ██║ ╚═══██╗████╔╝██║██║ ██║██╔══██║ ╚██╔╝ ╚════██║
|
|
32
|
+
███████╗██║ ██║███████║ ██║ ██████╔╝╚██████╔╝██████╔╝██║ ██║ ██║ ███████║
|
|
33
|
+
╚══════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝
|
|
34
|
+
{Colors.RESET}{Colors.DIM} 30 days of research. 30 seconds of work.{Colors.RESET}
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
MINI_BANNER = f"""{Colors.PURPLE}{Colors.BOLD}/last30days{Colors.RESET} {Colors.DIM}· researching...{Colors.RESET}"""
|
|
38
|
+
|
|
39
|
+
# Fun status messages for each phase
|
|
40
|
+
REDDIT_MESSAGES = [
|
|
41
|
+
"Diving into Reddit threads...",
|
|
42
|
+
"Scanning subreddits for gold...",
|
|
43
|
+
"Reading what Redditors are saying...",
|
|
44
|
+
"Exploring the front page of the internet...",
|
|
45
|
+
"Finding the good discussions...",
|
|
46
|
+
"Upvoting mentally...",
|
|
47
|
+
"Scrolling through comments...",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
X_MESSAGES = [
|
|
51
|
+
"Checking what X is buzzing about...",
|
|
52
|
+
"Reading the timeline...",
|
|
53
|
+
"Finding the hot takes...",
|
|
54
|
+
"Scanning tweets and threads...",
|
|
55
|
+
"Discovering trending insights...",
|
|
56
|
+
"Following the conversation...",
|
|
57
|
+
"Reading between the posts...",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
ENRICHING_MESSAGES = [
|
|
61
|
+
"Getting the juicy details...",
|
|
62
|
+
"Fetching engagement metrics...",
|
|
63
|
+
"Reading top comments...",
|
|
64
|
+
"Extracting insights...",
|
|
65
|
+
"Analyzing discussions...",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
YOUTUBE_MESSAGES = [
|
|
69
|
+
"Searching YouTube for videos...",
|
|
70
|
+
"Finding relevant video content...",
|
|
71
|
+
"Scanning YouTube channels...",
|
|
72
|
+
"Discovering video discussions...",
|
|
73
|
+
"Fetching transcripts...",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
TIKTOK_MESSAGES = [
|
|
77
|
+
"Searching TikTok for trending videos...",
|
|
78
|
+
"Finding what's viral on TikTok...",
|
|
79
|
+
"Scanning TikTok for relevant content...",
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
INSTAGRAM_MESSAGES = [
|
|
83
|
+
"Searching Instagram Reels...",
|
|
84
|
+
"Finding what's trending on Instagram...",
|
|
85
|
+
"Scanning Instagram for relevant reels...",
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
HN_MESSAGES = [
|
|
89
|
+
"Searching Hacker News...",
|
|
90
|
+
"Scanning HN front page stories...",
|
|
91
|
+
"Finding technical discussions...",
|
|
92
|
+
"Discovering developer conversations...",
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
POLYMARKET_MESSAGES = [
|
|
96
|
+
"Checking prediction markets...",
|
|
97
|
+
"Finding what people are betting on...",
|
|
98
|
+
"Scanning Polymarket for odds...",
|
|
99
|
+
"Discovering prediction markets...",
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
PROCESSING_MESSAGES = [
|
|
103
|
+
"Crunching the data...",
|
|
104
|
+
"Scoring and ranking...",
|
|
105
|
+
"Finding patterns...",
|
|
106
|
+
"Removing duplicates...",
|
|
107
|
+
"Organizing findings...",
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
WEB_ONLY_MESSAGES = [
|
|
111
|
+
"Searching the web...",
|
|
112
|
+
"Finding blogs and docs...",
|
|
113
|
+
"Crawling news sites...",
|
|
114
|
+
"Discovering tutorials...",
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
SOURCE_COMPLETION_ORDER = [
|
|
118
|
+
"reddit",
|
|
119
|
+
"x",
|
|
120
|
+
"youtube",
|
|
121
|
+
"tiktok",
|
|
122
|
+
"instagram",
|
|
123
|
+
"hackernews",
|
|
124
|
+
"bluesky",
|
|
125
|
+
"truthsocial",
|
|
126
|
+
"polymarket",
|
|
127
|
+
"grounding",
|
|
128
|
+
"xiaohongshu",
|
|
129
|
+
"digg",
|
|
130
|
+
"arxiv",
|
|
131
|
+
"techmeme",
|
|
132
|
+
"trustpilot",
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
SOURCE_COMPLETION_META = {
|
|
136
|
+
"reddit": ("Reddit", "thread", "threads", Colors.YELLOW),
|
|
137
|
+
"x": ("X", "post", "posts", Colors.CYAN),
|
|
138
|
+
"youtube": ("YouTube", "video", "videos", Colors.RED),
|
|
139
|
+
"tiktok": ("TikTok", "video", "videos", Colors.PURPLE),
|
|
140
|
+
"instagram": ("Instagram", "reel", "reels", Colors.PURPLE),
|
|
141
|
+
"hackernews": ("HN", "story", "stories", Colors.YELLOW),
|
|
142
|
+
"bluesky": ("Bluesky", "post", "posts", Colors.BLUE),
|
|
143
|
+
"truthsocial": ("Truth Social", "post", "posts", Colors.CYAN),
|
|
144
|
+
"polymarket": ("Polymarket", "market", "markets", Colors.GREEN),
|
|
145
|
+
"grounding": ("Web", "result", "results", Colors.GREEN),
|
|
146
|
+
"xiaohongshu": ("Xiaohongshu", "post", "posts", Colors.RED),
|
|
147
|
+
"digg": ("Digg", "cluster", "clusters", Colors.YELLOW),
|
|
148
|
+
"arxiv": ("arXiv", "paper", "papers", Colors.RED),
|
|
149
|
+
"techmeme": ("Techmeme", "headline", "headlines", Colors.CYAN),
|
|
150
|
+
"trustpilot": ("Trustpilot", "review", "reviews", Colors.GREEN),
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _completion_sources(source_counts: dict[str, int], display_sources: list[str] | None) -> list[str]:
|
|
155
|
+
requested = list(dict.fromkeys(display_sources or []))
|
|
156
|
+
if not requested:
|
|
157
|
+
requested = [source for source, count in source_counts.items() if count]
|
|
158
|
+
if not requested and source_counts:
|
|
159
|
+
requested = list(source_counts)
|
|
160
|
+
|
|
161
|
+
candidate_set = set(requested) | set(source_counts)
|
|
162
|
+
ordered = [source for source in SOURCE_COMPLETION_ORDER if source in candidate_set]
|
|
163
|
+
for source in requested + list(source_counts):
|
|
164
|
+
if source in candidate_set and source not in ordered:
|
|
165
|
+
ordered.append(source)
|
|
166
|
+
return ordered
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _format_completion_part(source: str, count: int, tty: bool) -> str:
|
|
170
|
+
label, singular, plural, color = SOURCE_COMPLETION_META.get(
|
|
171
|
+
source,
|
|
172
|
+
(source.replace("_", " ").title(), "result", "results", Colors.RESET),
|
|
173
|
+
)
|
|
174
|
+
unit = singular if count == 1 else plural
|
|
175
|
+
if tty:
|
|
176
|
+
return f"{color}{label}:{Colors.RESET} {count} {unit}"
|
|
177
|
+
return f"{label}: {count} {unit}"
|
|
178
|
+
|
|
179
|
+
def _build_nux_message(diag: dict = None) -> str:
|
|
180
|
+
"""Build conversational NUX message with dynamic source status."""
|
|
181
|
+
available = set((diag or {}).get("available_sources", []))
|
|
182
|
+
if diag:
|
|
183
|
+
reddit = "✓" if "reddit" in available else "✗"
|
|
184
|
+
x = "✓" if "x" in available else "✗"
|
|
185
|
+
youtube = "✓" if "youtube" in available else "✗"
|
|
186
|
+
web = "✓" if "grounding" in available else "✗"
|
|
187
|
+
status_line = f"Reddit {reddit}, X {x}, YouTube {youtube}, Web {web}"
|
|
188
|
+
else:
|
|
189
|
+
status_line = "YouTube ✓, Web ✓, Reddit ✗, X ✗"
|
|
190
|
+
|
|
191
|
+
return f"""
|
|
192
|
+
I just researched that for you. Here's what I've got right now:
|
|
193
|
+
|
|
194
|
+
{status_line}
|
|
195
|
+
|
|
196
|
+
More sources means better research, but it works fine as-is. You can unlock more for free - log into x.com in your browser for X, and run `brew install yt-dlp` for YouTube transcripts. That gives you Reddit (with comments), X, YouTube, HN, and Polymarket - all free.
|
|
197
|
+
|
|
198
|
+
Some examples of what you can do:
|
|
199
|
+
- "last30 what are people saying about Figma"
|
|
200
|
+
- "last30 watch my biggest competitor every week"
|
|
201
|
+
- "last30 watch AI video tools monthly"
|
|
202
|
+
- "last30 what have you found about AI video?"
|
|
203
|
+
|
|
204
|
+
Just start with "last30" and talk to me like normal.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
# Shorter promo for single missing key
|
|
208
|
+
PROMO_SINGLE_KEY = {
|
|
209
|
+
"reddit": "\n💡 Unlock TikTok and Instagram with SCRAPECREATORS_API_KEY - 10,000 free calls, no CC - scrapecreators.com\n",
|
|
210
|
+
"x": "\n💡 Unlock X: log into x.com in your browser, then re-run. "
|
|
211
|
+
"Firefox works on all platforms. Safari works on macOS (detected automatically). "
|
|
212
|
+
"Chrome, Brave, Edge, Arc, Vivaldi, Opera, or Chromium on macOS require "
|
|
213
|
+
"FROM_BROWSER=auto in .env (Keychain dialog). On Windows only Firefox is supported. "
|
|
214
|
+
"Or add AUTH_TOKEN/CT0 or XAI_API_KEY.\n",
|
|
215
|
+
"web": "\n💡 You can unlock native grounded web search with BRAVE_API_KEY or SERPER_API_KEY.\n",
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
# Bird auth help (for local users with vendored Bird CLI)
|
|
219
|
+
BIRD_AUTH_HELP = f"""
|
|
220
|
+
{Colors.YELLOW}Bird authentication failed.{Colors.RESET}
|
|
221
|
+
|
|
222
|
+
To fix this:
|
|
223
|
+
1. Add AUTH_TOKEN and CT0 to ~/.config/last30days/.env, or to trusted .claude/last30days.env with LAST30DAYS_TRUST_PROJECT_CONFIG=1
|
|
224
|
+
2. Or set XAI_API_KEY for the xAI fallback backend
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
BIRD_AUTH_HELP_PLAIN = """
|
|
228
|
+
Bird authentication failed.
|
|
229
|
+
|
|
230
|
+
To fix this:
|
|
231
|
+
1. Add AUTH_TOKEN and CT0 to ~/.config/last30days/.env, or to trusted .claude/last30days.env with LAST30DAYS_TRUST_PROJECT_CONFIG=1
|
|
232
|
+
2. Or set XAI_API_KEY for the xAI fallback backend
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
# Spinner frames
|
|
236
|
+
SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
|
237
|
+
DOTS_FRAMES = [' ', '. ', '.. ', '...']
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class Spinner:
|
|
241
|
+
"""Animated spinner for long-running operations."""
|
|
242
|
+
|
|
243
|
+
def __init__(self, message: str = "Working", color: str = Colors.CYAN, quiet: bool = False):
|
|
244
|
+
self.message = message
|
|
245
|
+
self.color = color
|
|
246
|
+
self.running = False
|
|
247
|
+
self.thread: Optional[threading.Thread] = None
|
|
248
|
+
self.frame_idx = 0
|
|
249
|
+
self.shown_static = False
|
|
250
|
+
self.quiet = quiet # Suppress non-TTY start message (still shows ✓ completion)
|
|
251
|
+
|
|
252
|
+
def _spin(self):
|
|
253
|
+
while self.running:
|
|
254
|
+
frame = SPINNER_FRAMES[self.frame_idx % len(SPINNER_FRAMES)]
|
|
255
|
+
sys.stderr.write(f"\r{self.color}{frame}{Colors.RESET} {self.message} ")
|
|
256
|
+
sys.stderr.flush()
|
|
257
|
+
self.frame_idx += 1
|
|
258
|
+
time.sleep(0.08)
|
|
259
|
+
|
|
260
|
+
def start(self):
|
|
261
|
+
self.running = True
|
|
262
|
+
if IS_TTY:
|
|
263
|
+
# Real terminal - animate
|
|
264
|
+
self.thread = threading.Thread(target=self._spin, daemon=True)
|
|
265
|
+
self.thread.start()
|
|
266
|
+
else:
|
|
267
|
+
# Not a TTY (Claude Code) - just print once
|
|
268
|
+
if not self.shown_static and not self.quiet:
|
|
269
|
+
sys.stderr.write(f"⏳ {self.message}\n")
|
|
270
|
+
sys.stderr.flush()
|
|
271
|
+
self.shown_static = True
|
|
272
|
+
|
|
273
|
+
def update(self, message: str):
|
|
274
|
+
self.message = message
|
|
275
|
+
if not IS_TTY and not self.shown_static:
|
|
276
|
+
# Print update in non-TTY mode
|
|
277
|
+
sys.stderr.write(f"⏳ {message}\n")
|
|
278
|
+
sys.stderr.flush()
|
|
279
|
+
|
|
280
|
+
def stop(self, final_message: str = ""):
|
|
281
|
+
self.running = False
|
|
282
|
+
if self.thread:
|
|
283
|
+
self.thread.join(timeout=0.2)
|
|
284
|
+
if IS_TTY:
|
|
285
|
+
# Clear the line in real terminal
|
|
286
|
+
sys.stderr.write("\r" + " " * 80 + "\r")
|
|
287
|
+
if final_message:
|
|
288
|
+
sys.stderr.write(f"✓ {final_message}\n")
|
|
289
|
+
sys.stderr.flush()
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class ProgressDisplay:
|
|
293
|
+
"""Progress display for research phases."""
|
|
294
|
+
|
|
295
|
+
def __init__(self, topic: str, show_banner: bool = True):
|
|
296
|
+
self.topic = topic
|
|
297
|
+
self.spinner: Optional[Spinner] = None
|
|
298
|
+
self.start_time = time.time()
|
|
299
|
+
|
|
300
|
+
if show_banner:
|
|
301
|
+
self._show_banner()
|
|
302
|
+
|
|
303
|
+
def _show_banner(self):
|
|
304
|
+
if IS_TTY:
|
|
305
|
+
sys.stderr.write(MINI_BANNER + "\n")
|
|
306
|
+
sys.stderr.write(f"{Colors.DIM}Topic: {Colors.RESET}{Colors.BOLD}{self.topic}{Colors.RESET}\n\n")
|
|
307
|
+
else:
|
|
308
|
+
# Simple text for non-TTY
|
|
309
|
+
sys.stderr.write(f"/last30days · researching: {self.topic}\n")
|
|
310
|
+
sys.stderr.flush()
|
|
311
|
+
|
|
312
|
+
def start_reddit(self):
|
|
313
|
+
msg = random.choice(REDDIT_MESSAGES)
|
|
314
|
+
self.spinner = Spinner(f"{Colors.YELLOW}Reddit{Colors.RESET} {msg}", Colors.YELLOW)
|
|
315
|
+
self.spinner.start()
|
|
316
|
+
|
|
317
|
+
def end_reddit(self, count: int):
|
|
318
|
+
if self.spinner:
|
|
319
|
+
self.spinner.stop(f"{Colors.YELLOW}Reddit{Colors.RESET} Found {count} threads")
|
|
320
|
+
|
|
321
|
+
def start_reddit_enrich(self, current: int, total: int):
|
|
322
|
+
if self.spinner:
|
|
323
|
+
self.spinner.stop()
|
|
324
|
+
msg = random.choice(ENRICHING_MESSAGES)
|
|
325
|
+
self.spinner = Spinner(f"{Colors.YELLOW}Reddit{Colors.RESET} [{current}/{total}] {msg}", Colors.YELLOW)
|
|
326
|
+
self.spinner.start()
|
|
327
|
+
|
|
328
|
+
def update_reddit_enrich(self, current: int, total: int):
|
|
329
|
+
if self.spinner:
|
|
330
|
+
msg = random.choice(ENRICHING_MESSAGES)
|
|
331
|
+
self.spinner.update(f"{Colors.YELLOW}Reddit{Colors.RESET} [{current}/{total}] {msg}")
|
|
332
|
+
|
|
333
|
+
def end_reddit_enrich(self):
|
|
334
|
+
if self.spinner:
|
|
335
|
+
self.spinner.stop(f"{Colors.YELLOW}Reddit{Colors.RESET} Enriched with engagement data")
|
|
336
|
+
|
|
337
|
+
def start_x(self):
|
|
338
|
+
msg = random.choice(X_MESSAGES)
|
|
339
|
+
self.spinner = Spinner(f"{Colors.CYAN}X{Colors.RESET} {msg}", Colors.CYAN)
|
|
340
|
+
self.spinner.start()
|
|
341
|
+
|
|
342
|
+
def end_x(self, count: int):
|
|
343
|
+
if self.spinner:
|
|
344
|
+
self.spinner.stop(f"{Colors.CYAN}X{Colors.RESET} Found {count} posts")
|
|
345
|
+
|
|
346
|
+
def start_youtube(self):
|
|
347
|
+
msg = random.choice(YOUTUBE_MESSAGES)
|
|
348
|
+
self.spinner = Spinner(f"{Colors.RED}YouTube{Colors.RESET} {msg}", Colors.RED)
|
|
349
|
+
self.spinner.start()
|
|
350
|
+
|
|
351
|
+
def end_youtube(self, count: int):
|
|
352
|
+
if self.spinner:
|
|
353
|
+
self.spinner.stop(f"{Colors.RED}YouTube{Colors.RESET} Found {count} videos")
|
|
354
|
+
|
|
355
|
+
def start_tiktok(self):
|
|
356
|
+
msg = random.choice(TIKTOK_MESSAGES)
|
|
357
|
+
self.spinner = Spinner(f"{Colors.PURPLE}TikTok{Colors.RESET} {msg}", Colors.PURPLE)
|
|
358
|
+
self.spinner.start()
|
|
359
|
+
|
|
360
|
+
def end_tiktok(self, count: int):
|
|
361
|
+
if self.spinner:
|
|
362
|
+
self.spinner.stop(f"{Colors.PURPLE}TikTok{Colors.RESET} Found {count} videos")
|
|
363
|
+
|
|
364
|
+
def start_instagram(self):
|
|
365
|
+
msg = random.choice(INSTAGRAM_MESSAGES)
|
|
366
|
+
self.spinner = Spinner(f"{Colors.PURPLE}Instagram{Colors.RESET} {msg}", Colors.PURPLE)
|
|
367
|
+
self.spinner.start()
|
|
368
|
+
|
|
369
|
+
def end_instagram(self, count: int):
|
|
370
|
+
if self.spinner:
|
|
371
|
+
self.spinner.stop(f"{Colors.PURPLE}Instagram{Colors.RESET} Found {count} reels")
|
|
372
|
+
|
|
373
|
+
def start_hackernews(self):
|
|
374
|
+
msg = random.choice(HN_MESSAGES)
|
|
375
|
+
self.spinner = Spinner(f"{Colors.YELLOW}HN{Colors.RESET} {msg}", Colors.YELLOW, quiet=True)
|
|
376
|
+
self.spinner.start()
|
|
377
|
+
|
|
378
|
+
def end_hackernews(self, count: int):
|
|
379
|
+
if self.spinner:
|
|
380
|
+
self.spinner.stop(f"{Colors.YELLOW}HN{Colors.RESET} Found {count} stories")
|
|
381
|
+
|
|
382
|
+
def start_polymarket(self):
|
|
383
|
+
msg = random.choice(POLYMARKET_MESSAGES)
|
|
384
|
+
self.spinner = Spinner(f"{Colors.GREEN}Polymarket{Colors.RESET} {msg}", Colors.GREEN, quiet=True)
|
|
385
|
+
self.spinner.start()
|
|
386
|
+
|
|
387
|
+
def end_polymarket(self, count: int):
|
|
388
|
+
if self.spinner:
|
|
389
|
+
self.spinner.stop(f"{Colors.GREEN}Polymarket{Colors.RESET} Found {count} markets")
|
|
390
|
+
|
|
391
|
+
def start_processing(self):
|
|
392
|
+
msg = random.choice(PROCESSING_MESSAGES)
|
|
393
|
+
self.spinner = Spinner(f"{Colors.PURPLE}Processing{Colors.RESET} {msg}", Colors.PURPLE)
|
|
394
|
+
self.spinner.start()
|
|
395
|
+
|
|
396
|
+
def end_processing(self):
|
|
397
|
+
if self.spinner:
|
|
398
|
+
self.spinner.stop()
|
|
399
|
+
|
|
400
|
+
def show_complete(
|
|
401
|
+
self,
|
|
402
|
+
reddit_count: int = 0,
|
|
403
|
+
x_count: int = 0,
|
|
404
|
+
youtube_count: int = 0,
|
|
405
|
+
hn_count: int = 0,
|
|
406
|
+
pm_count: int = 0,
|
|
407
|
+
tiktok_count: int = 0,
|
|
408
|
+
ig_count: int = 0,
|
|
409
|
+
*,
|
|
410
|
+
source_counts: dict[str, int] | None = None,
|
|
411
|
+
display_sources: list[str] | None = None,
|
|
412
|
+
):
|
|
413
|
+
elapsed = time.time() - self.start_time
|
|
414
|
+
if source_counts is None:
|
|
415
|
+
source_counts = {
|
|
416
|
+
"reddit": reddit_count,
|
|
417
|
+
"x": x_count,
|
|
418
|
+
"youtube": youtube_count,
|
|
419
|
+
"tiktok": tiktok_count,
|
|
420
|
+
"instagram": ig_count,
|
|
421
|
+
"hackernews": hn_count,
|
|
422
|
+
"polymarket": pm_count,
|
|
423
|
+
}
|
|
424
|
+
if display_sources is None:
|
|
425
|
+
display_sources = [source for source, count in source_counts.items() if count]
|
|
426
|
+
if not display_sources:
|
|
427
|
+
display_sources = ["reddit", "x"]
|
|
428
|
+
|
|
429
|
+
ordered_sources = _completion_sources(source_counts, display_sources)
|
|
430
|
+
parts = [
|
|
431
|
+
_format_completion_part(source, source_counts.get(source, 0), tty=IS_TTY)
|
|
432
|
+
for source in ordered_sources
|
|
433
|
+
]
|
|
434
|
+
if IS_TTY:
|
|
435
|
+
sys.stderr.write(f"\n{Colors.GREEN}{Colors.BOLD}✓ Research complete{Colors.RESET} ")
|
|
436
|
+
sys.stderr.write(f"{Colors.DIM}({elapsed:.1f}s){Colors.RESET}\n")
|
|
437
|
+
sys.stderr.write(" " + " ".join(parts))
|
|
438
|
+
sys.stderr.write("\n\n")
|
|
439
|
+
else:
|
|
440
|
+
sys.stderr.write(f"✓ Research complete ({elapsed:.1f}s) - {', '.join(parts)}\n")
|
|
441
|
+
sys.stderr.flush()
|
|
442
|
+
|
|
443
|
+
def show_cached(self, age_hours: float = None):
|
|
444
|
+
if age_hours is not None:
|
|
445
|
+
age_str = f" ({age_hours:.1f}h old)"
|
|
446
|
+
else:
|
|
447
|
+
age_str = ""
|
|
448
|
+
sys.stderr.write(f"{Colors.GREEN}⚡{Colors.RESET} {Colors.DIM}Using cached results{age_str} - use --refresh for fresh data{Colors.RESET}\n\n")
|
|
449
|
+
sys.stderr.flush()
|
|
450
|
+
|
|
451
|
+
def show_error(self, message: str):
|
|
452
|
+
sys.stderr.write(f"{Colors.RED}✗ Error:{Colors.RESET} {message}\n")
|
|
453
|
+
sys.stderr.flush()
|
|
454
|
+
|
|
455
|
+
def start_web_only(self):
|
|
456
|
+
"""Show web-only mode indicator."""
|
|
457
|
+
msg = random.choice(WEB_ONLY_MESSAGES)
|
|
458
|
+
self.spinner = Spinner(f"{Colors.GREEN}Web{Colors.RESET} {msg}", Colors.GREEN)
|
|
459
|
+
self.spinner.start()
|
|
460
|
+
|
|
461
|
+
def end_web_only(self):
|
|
462
|
+
"""End web-only spinner."""
|
|
463
|
+
if self.spinner:
|
|
464
|
+
self.spinner.stop(f"{Colors.GREEN}Web{Colors.RESET} assistant will search the web")
|
|
465
|
+
|
|
466
|
+
def show_web_only_complete(self):
|
|
467
|
+
"""Show completion for web-only mode."""
|
|
468
|
+
elapsed = time.time() - self.start_time
|
|
469
|
+
if IS_TTY:
|
|
470
|
+
sys.stderr.write(f"\n{Colors.GREEN}{Colors.BOLD}✓ Ready for web search{Colors.RESET} ")
|
|
471
|
+
sys.stderr.write(f"{Colors.DIM}({elapsed:.1f}s){Colors.RESET}\n")
|
|
472
|
+
sys.stderr.write(f" {Colors.GREEN}Web:{Colors.RESET} assistant will search blogs, docs & news\n\n")
|
|
473
|
+
else:
|
|
474
|
+
sys.stderr.write(f"✓ Ready for web search ({elapsed:.1f}s)\n")
|
|
475
|
+
sys.stderr.flush()
|
|
476
|
+
|
|
477
|
+
def show_promo(self, missing: str = "both", diag: dict = None):
|
|
478
|
+
"""Show NUX / promotional message for missing API keys.
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
missing: 'both', 'all', 'reddit', or 'x' - which keys are missing
|
|
482
|
+
diag: Optional diagnostics dict for dynamic source status
|
|
483
|
+
"""
|
|
484
|
+
if missing in ("both", "all"):
|
|
485
|
+
sys.stderr.write(_build_nux_message(diag))
|
|
486
|
+
elif missing in PROMO_SINGLE_KEY:
|
|
487
|
+
sys.stderr.write(PROMO_SINGLE_KEY[missing])
|
|
488
|
+
sys.stderr.flush()
|
|
489
|
+
|
|
490
|
+
def show_bird_auth_help(self):
|
|
491
|
+
"""Show Bird authentication help."""
|
|
492
|
+
if IS_TTY:
|
|
493
|
+
sys.stderr.write(BIRD_AUTH_HELP)
|
|
494
|
+
else:
|
|
495
|
+
sys.stderr.write(BIRD_AUTH_HELP_PLAIN)
|
|
496
|
+
sys.stderr.flush()
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def show_diagnostic_banner(diag: dict):
|
|
500
|
+
"""Show pre-flight source status banner when sources are missing.
|
|
501
|
+
|
|
502
|
+
Args:
|
|
503
|
+
diag: Dict from pipeline.diagnose() with available_sources, x_backend,
|
|
504
|
+
bird status, provider availability, and native web backend info.
|
|
505
|
+
"""
|
|
506
|
+
available_sources = set(diag.get("available_sources") or [])
|
|
507
|
+
has_reddit = "reddit" in available_sources
|
|
508
|
+
has_scrapecreators = diag.get("has_scrapecreators", False)
|
|
509
|
+
has_x = "x" in available_sources
|
|
510
|
+
has_youtube = "youtube" in available_sources
|
|
511
|
+
has_web = "grounding" in available_sources
|
|
512
|
+
has_xiaohongshu = "xiaohongshu" in available_sources
|
|
513
|
+
x_backend = diag.get("x_backend")
|
|
514
|
+
native_web_backend = diag.get("native_web_backend")
|
|
515
|
+
|
|
516
|
+
# If everything is available, no banner needed
|
|
517
|
+
if has_reddit and has_x and has_youtube and has_web:
|
|
518
|
+
return
|
|
519
|
+
|
|
520
|
+
lines = []
|
|
521
|
+
|
|
522
|
+
if IS_TTY:
|
|
523
|
+
lines.append(f"{Colors.DIM}┌─────────────────────────────────────────────────────┐{Colors.RESET}")
|
|
524
|
+
_header = f"/last30days v{_skill_version()} - Source Status"
|
|
525
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.BOLD}{_header}{Colors.RESET}{' ' * (52 - len(_header))}{Colors.DIM}│{Colors.RESET}")
|
|
526
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.DIM}│{Colors.RESET}")
|
|
527
|
+
|
|
528
|
+
# Reddit
|
|
529
|
+
if has_reddit and has_scrapecreators:
|
|
530
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.GREEN}✅ Reddit{Colors.RESET} — full threads with comments {Colors.DIM}│{Colors.RESET}")
|
|
531
|
+
elif has_reddit:
|
|
532
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.GREEN}✅ Reddit{Colors.RESET} — public threads (titles + scores) {Colors.DIM}│{Colors.RESET}")
|
|
533
|
+
else:
|
|
534
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.RED}❌ Reddit{Colors.RESET} — unavailable {Colors.DIM}│{Colors.RESET}")
|
|
535
|
+
|
|
536
|
+
# X/Twitter
|
|
537
|
+
if has_x:
|
|
538
|
+
username = diag.get("bird_username", "")
|
|
539
|
+
label = f"Bird ({username})" if x_backend == "bird" and username else str(x_backend or "xai").upper()
|
|
540
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.GREEN}✅ X/Twitter{Colors.RESET} — {label} {Colors.DIM}│{Colors.RESET}")
|
|
541
|
+
else:
|
|
542
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.RED}❌ X/Twitter{Colors.RESET} — No X auth or fallback key {Colors.DIM}│{Colors.RESET}")
|
|
543
|
+
if diag.get("bird_installed"):
|
|
544
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} └─ Add AUTH_TOKEN/CT0 or XAI_API_KEY {Colors.DIM}│{Colors.RESET}")
|
|
545
|
+
else:
|
|
546
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} └─ Needs Node.js 22+ (Bird is bundled) {Colors.DIM}│{Colors.RESET}")
|
|
547
|
+
|
|
548
|
+
# YouTube
|
|
549
|
+
if has_youtube:
|
|
550
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.GREEN}✅ YouTube{Colors.RESET} — yt-dlp found {Colors.DIM}│{Colors.RESET}")
|
|
551
|
+
else:
|
|
552
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.RED}❌ YouTube{Colors.RESET} — yt-dlp not installed {Colors.DIM}│{Colors.RESET}")
|
|
553
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} └─ Fix: brew install yt-dlp (free) {Colors.DIM}│{Colors.RESET}")
|
|
554
|
+
|
|
555
|
+
# Xiaohongshu (only show when configured)
|
|
556
|
+
if has_xiaohongshu:
|
|
557
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.GREEN}✅ Xiaohongshu{Colors.RESET} — API connected + logged in {Colors.DIM}│{Colors.RESET}")
|
|
558
|
+
|
|
559
|
+
# Web
|
|
560
|
+
if has_web:
|
|
561
|
+
backend = native_web_backend or "native"
|
|
562
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.GREEN}✅ Web{Colors.RESET} — {backend} API {Colors.DIM}│{Colors.RESET}")
|
|
563
|
+
else:
|
|
564
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.YELLOW}⚡ Web{Colors.RESET} — Add BRAVE_API_KEY or SERPER_API_KEY {Colors.DIM}│{Colors.RESET}")
|
|
565
|
+
|
|
566
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} {Colors.DIM}│{Colors.RESET}")
|
|
567
|
+
lines.append(f"{Colors.DIM}│{Colors.RESET} Config: {Colors.BOLD}~/.config/last30days/.env{Colors.RESET} {Colors.DIM}│{Colors.RESET}")
|
|
568
|
+
lines.append(f"{Colors.DIM}└─────────────────────────────────────────────────────┘{Colors.RESET}")
|
|
569
|
+
else:
|
|
570
|
+
# Plain text for non-TTY (Claude Code / Codex)
|
|
571
|
+
lines.append("┌─────────────────────────────────────────────────────┐")
|
|
572
|
+
_header_plain = f"/last30days v{_skill_version()} - Source Status"
|
|
573
|
+
lines.append(f"│ {_header_plain}{' ' * (52 - len(_header_plain))}│")
|
|
574
|
+
lines.append("│ │")
|
|
575
|
+
|
|
576
|
+
if has_reddit and has_scrapecreators:
|
|
577
|
+
lines.append("│ ✅ Reddit — full threads with comments │")
|
|
578
|
+
elif has_reddit:
|
|
579
|
+
lines.append("│ ✅ Reddit — public threads (titles + scores) │")
|
|
580
|
+
else:
|
|
581
|
+
lines.append("│ ❌ Reddit — unavailable │")
|
|
582
|
+
|
|
583
|
+
if has_x:
|
|
584
|
+
lines.append("│ ✅ X/Twitter — available │")
|
|
585
|
+
else:
|
|
586
|
+
lines.append("│ ❌ X/Twitter — No X auth or fallback key │")
|
|
587
|
+
if diag.get("bird_installed"):
|
|
588
|
+
lines.append("│ └─ Add AUTH_TOKEN/CT0 or XAI_API_KEY │")
|
|
589
|
+
else:
|
|
590
|
+
lines.append("│ └─ Needs Node.js 22+ (Bird is bundled) │")
|
|
591
|
+
|
|
592
|
+
if has_youtube:
|
|
593
|
+
lines.append("│ ✅ YouTube — yt-dlp found │")
|
|
594
|
+
else:
|
|
595
|
+
lines.append("│ ❌ YouTube — yt-dlp not installed │")
|
|
596
|
+
lines.append("│ └─ Fix: brew install yt-dlp (free) │")
|
|
597
|
+
|
|
598
|
+
if has_xiaohongshu:
|
|
599
|
+
lines.append("│ ✅ Xiaohongshu — API connected + logged in │")
|
|
600
|
+
|
|
601
|
+
if has_web:
|
|
602
|
+
backend = native_web_backend or "native"
|
|
603
|
+
lines.append(f"│ ✅ Web — {backend} API available{' ' * max(0, 13 - len(backend))}│")
|
|
604
|
+
else:
|
|
605
|
+
lines.append("│ ⚡ Web — Add BRAVE_API_KEY or SERPER_API_KEY │")
|
|
606
|
+
|
|
607
|
+
lines.append("│ │")
|
|
608
|
+
lines.append("│ Config: ~/.config/last30days/.env │")
|
|
609
|
+
lines.append("└─────────────────────────────────────────────────────┘")
|
|
610
|
+
|
|
611
|
+
sys.stderr.write("\n".join(lines) + "\n\n")
|
|
612
|
+
sys.stderr.flush()
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
def print_phase(phase: str, message: str):
|
|
616
|
+
"""Print a phase message."""
|
|
617
|
+
colors = {
|
|
618
|
+
"reddit": Colors.YELLOW,
|
|
619
|
+
"x": Colors.CYAN,
|
|
620
|
+
"process": Colors.PURPLE,
|
|
621
|
+
"done": Colors.GREEN,
|
|
622
|
+
"error": Colors.RED,
|
|
623
|
+
}
|
|
624
|
+
color = colors.get(phase, Colors.RESET)
|
|
625
|
+
sys.stderr.write(f"{color}▸{Colors.RESET} {message}\n")
|
|
626
|
+
sys.stderr.flush()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Peter Steinberger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|