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,334 @@
|
|
|
1
|
+
"""Reusable local scoring signals for v3 pipeline stages."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
from . import dates, relevance, schema
|
|
8
|
+
|
|
9
|
+
# Editorial signal-to-noise scores. Grounding (Google Search) is 1.0 baseline;
|
|
10
|
+
# social platforms discounted for noise.
|
|
11
|
+
SOURCE_QUALITY = {
|
|
12
|
+
"xiaohongshu": 0.7,
|
|
13
|
+
"hackernews": 0.8,
|
|
14
|
+
"youtube": 0.85,
|
|
15
|
+
"digg": 0.85,
|
|
16
|
+
"arxiv": 0.9,
|
|
17
|
+
"techmeme": 0.85,
|
|
18
|
+
"trustpilot": 0.78,
|
|
19
|
+
"reddit": 0.6,
|
|
20
|
+
"x": 0.68,
|
|
21
|
+
"bluesky": 0.66,
|
|
22
|
+
"truthsocial": 0.6,
|
|
23
|
+
"polymarket": 0.5,
|
|
24
|
+
"instagram": 0.58,
|
|
25
|
+
"tiktok": 0.58,
|
|
26
|
+
"jobs": 0.72,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def source_quality(source: str) -> float:
|
|
31
|
+
return SOURCE_QUALITY.get(source, 0.6)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def local_relevance(
|
|
35
|
+
item: schema.SourceItem,
|
|
36
|
+
ranking_query: "str | relevance.PreparedQuery",
|
|
37
|
+
) -> float:
|
|
38
|
+
text = "\n".join(
|
|
39
|
+
part
|
|
40
|
+
for part in [item.title, item.body, item.snippet]
|
|
41
|
+
if part
|
|
42
|
+
)
|
|
43
|
+
hashtags = item.metadata.get("hashtags") if isinstance(item.metadata, dict) else None
|
|
44
|
+
score = relevance.token_overlap_relevance(ranking_query, text, hashtags=hashtags)
|
|
45
|
+
|
|
46
|
+
# High-engagement YouTube floor: official videos with millions of views
|
|
47
|
+
# often have titles that don't keyword-match the query (e.g., "YE - FATHER
|
|
48
|
+
# (feat. TRAVIS SCOTT)" doesn't match "kanye west"). The engagement signals
|
|
49
|
+
# say "this is important" even when text overlap is weak.
|
|
50
|
+
if item.source == "youtube" and item.engagement.get("views", 0) > 100_000:
|
|
51
|
+
score = max(score, 0.3)
|
|
52
|
+
|
|
53
|
+
# Project-mode GitHub floor: items fetched via --github-repo are explicitly
|
|
54
|
+
# requested by the user and relevant by construction. Without this floor,
|
|
55
|
+
# repos with low token diversity (e.g., "openclaw/openclaw" -> 1 unique token)
|
|
56
|
+
# get pruned despite being the primary search target.
|
|
57
|
+
labels = item.metadata.get("labels", []) if isinstance(item.metadata, dict) else []
|
|
58
|
+
if "project-mode" in labels:
|
|
59
|
+
score = max(score, 0.8)
|
|
60
|
+
|
|
61
|
+
return score
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def freshness(
|
|
65
|
+
item: schema.SourceItem,
|
|
66
|
+
freshness_mode: str = "balanced_recent",
|
|
67
|
+
*,
|
|
68
|
+
reference_date: str | None = None,
|
|
69
|
+
max_days: int = 30,
|
|
70
|
+
) -> int:
|
|
71
|
+
score = dates.recency_score(
|
|
72
|
+
item.published_at,
|
|
73
|
+
max_days=max_days,
|
|
74
|
+
reference_date=reference_date,
|
|
75
|
+
)
|
|
76
|
+
if freshness_mode == "strict_recent":
|
|
77
|
+
return int(score)
|
|
78
|
+
if freshness_mode == "evergreen_ok":
|
|
79
|
+
return int((score * 0.6) + 40)
|
|
80
|
+
return int((score * 0.8) + 10)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def log1p_safe(value: float | int | None) -> float:
|
|
84
|
+
if value is None:
|
|
85
|
+
return 0.0
|
|
86
|
+
try:
|
|
87
|
+
numeric = float(value)
|
|
88
|
+
except (TypeError, ValueError):
|
|
89
|
+
return 0.0
|
|
90
|
+
if numeric <= 0:
|
|
91
|
+
return 0.0
|
|
92
|
+
return math.log1p(numeric)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _top_comment_score(item: schema.SourceItem) -> float:
|
|
96
|
+
comments = item.metadata.get("top_comments") or []
|
|
97
|
+
if not comments or not isinstance(comments[0], dict):
|
|
98
|
+
return 0.0
|
|
99
|
+
return log1p_safe(comments[0].get("score"))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# Per-platform log-reference for normalizing a top comment's vote count into a
|
|
103
|
+
# [0,1] signal. Reddit upvotes run in the hundreds-to-thousands; YouTube/TikTok
|
|
104
|
+
# likes run 10-600x higher (and the top end is display-abbreviated: "39K" is
|
|
105
|
+
# stored as 39000). A raw or single-scale log compare would let YouTube/TikTok
|
|
106
|
+
# dominate purely by platform scale, not by being funnier. Each value is the
|
|
107
|
+
# log1p of a "very high" top-comment count for that platform, so dividing a
|
|
108
|
+
# comment's log1p(score) by it yields a comparable cross-platform strength.
|
|
109
|
+
_VOTE_LOG_REFERENCE: dict[str, float] = {
|
|
110
|
+
"reddit": 7.6, # ~log1p(2000)
|
|
111
|
+
"hackernews": 6.2, # ~log1p(500)
|
|
112
|
+
"youtube": 10.3, # ~log1p(30000)
|
|
113
|
+
"tiktok": 10.3, # ~log1p(30000)
|
|
114
|
+
"instagram": 9.2, # ~log1p(10000)
|
|
115
|
+
"x": 9.2, # ~log1p(10000)
|
|
116
|
+
"bluesky": 9.2, # ~log1p(10000); like X/IG, not the Reddit default
|
|
117
|
+
}
|
|
118
|
+
_VOTE_LOG_REFERENCE_DEFAULT = 7.6
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def normalized_comment_vote(source: str, score: "float | int | None") -> float:
|
|
122
|
+
"""Normalize a single comment's vote count to [0,1] within its platform.
|
|
123
|
+
|
|
124
|
+
Same per-platform reference as ``top_comment_vote_signal`` so a 22k-like
|
|
125
|
+
TikTok comment and a 600-upvote Reddit comment rank on a comparable scale.
|
|
126
|
+
Used to rank the cross-candidate Top Community Comments block.
|
|
127
|
+
"""
|
|
128
|
+
base = log1p_safe(score)
|
|
129
|
+
if base <= 0.0:
|
|
130
|
+
return 0.0
|
|
131
|
+
ref = _VOTE_LOG_REFERENCE.get(source, _VOTE_LOG_REFERENCE_DEFAULT)
|
|
132
|
+
return max(0.0, min(1.0, base / ref))
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def top_comment_vote_signal(candidate: schema.Candidate) -> float:
|
|
136
|
+
"""Strength of a candidate's most-upvoted top comment, as [0,1].
|
|
137
|
+
|
|
138
|
+
Normalized *within the candidate's platform* (see ``_VOTE_LOG_REFERENCE``)
|
|
139
|
+
so a 22k-like TikTok comment and a 600-upvote Reddit comment land on a
|
|
140
|
+
comparable scale rather than letting raw counts dominate. Returns 0.0 when
|
|
141
|
+
no top comment carries votes. Used by the fun judge to amplify (never
|
|
142
|
+
drive) crowd-certified comments.
|
|
143
|
+
"""
|
|
144
|
+
best_log = 0.0
|
|
145
|
+
for item in candidate.source_items:
|
|
146
|
+
comments = item.metadata.get("top_comments") or []
|
|
147
|
+
for comment in comments[:3]:
|
|
148
|
+
if isinstance(comment, dict):
|
|
149
|
+
best_log = max(best_log, log1p_safe(comment.get("score")))
|
|
150
|
+
if best_log <= 0.0:
|
|
151
|
+
return 0.0
|
|
152
|
+
ref = _VOTE_LOG_REFERENCE.get(candidate.source, _VOTE_LOG_REFERENCE_DEFAULT)
|
|
153
|
+
return max(0.0, min(1.0, best_log / ref))
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# Per-source engagement weights: list of (field_name, weight) tuples.
|
|
157
|
+
# Reddit, YouTube, and TikTok use custom functions because they include
|
|
158
|
+
# a dedicated 10% top-comment-score slot (see _reddit_engagement,
|
|
159
|
+
# _youtube_engagement, _tiktok_engagement).
|
|
160
|
+
ENGAGEMENT_WEIGHTS: dict[str, list[tuple[str, float]]] = {
|
|
161
|
+
"x": [("likes", 0.55), ("reposts", 0.25), ("replies", 0.15), ("quotes", 0.05)],
|
|
162
|
+
"instagram": [("views", 0.50), ("likes", 0.30), ("comments", 0.20)],
|
|
163
|
+
"hackernews": [("points", 0.55), ("comments", 0.45)],
|
|
164
|
+
"bluesky": [("likes", 0.40), ("reposts", 0.30), ("replies", 0.20), ("quotes", 0.10)],
|
|
165
|
+
"truthsocial": [("likes", 0.45), ("reposts", 0.30), ("replies", 0.25)],
|
|
166
|
+
"polymarket": [("volume", 0.60), ("liquidity", 0.40)],
|
|
167
|
+
"digg": [("postCount", 0.40), ("uniqueAuthors", 0.30), ("rank_score", 0.30)],
|
|
168
|
+
"trustpilot": [("reviews", 1.0)],
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _weighted_engagement(item: schema.SourceItem, weights: list[tuple[str, float]]) -> float | None:
|
|
173
|
+
values = [(log1p_safe(item.engagement.get(field)), weight) for field, weight in weights]
|
|
174
|
+
if not any(v for v, _ in values):
|
|
175
|
+
return None
|
|
176
|
+
return sum(v * w for v, w in values)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _reddit_engagement(item: schema.SourceItem) -> float | None:
|
|
180
|
+
score = log1p_safe(item.engagement.get("score"))
|
|
181
|
+
comments = log1p_safe(item.engagement.get("num_comments"))
|
|
182
|
+
ratio = float(item.engagement.get("upvote_ratio") or 0.0)
|
|
183
|
+
top_comment = _top_comment_score(item)
|
|
184
|
+
if not any([score, comments, ratio, top_comment]):
|
|
185
|
+
return None
|
|
186
|
+
return (0.50 * score) + (0.35 * comments) + (0.05 * (ratio * 10.0)) + (0.10 * top_comment)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _youtube_engagement(item: schema.SourceItem) -> float | None:
|
|
190
|
+
views = log1p_safe(item.engagement.get("views"))
|
|
191
|
+
likes = log1p_safe(item.engagement.get("likes"))
|
|
192
|
+
comments = log1p_safe(item.engagement.get("comments"))
|
|
193
|
+
top_comment = _top_comment_score(item)
|
|
194
|
+
if not any([views, likes, comments, top_comment]):
|
|
195
|
+
return None
|
|
196
|
+
# Mirrors Reddit: carve out 10% for top-comment signal, keep view-weight
|
|
197
|
+
# dominant. Without comments, the pre-change weights (0.50/0.35/0.15)
|
|
198
|
+
# still govern relative ordering.
|
|
199
|
+
return (0.45 * views) + (0.32 * likes) + (0.13 * comments) + (0.10 * top_comment)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _tiktok_engagement(item: schema.SourceItem) -> float | None:
|
|
203
|
+
views = log1p_safe(item.engagement.get("views"))
|
|
204
|
+
likes = log1p_safe(item.engagement.get("likes"))
|
|
205
|
+
comments = log1p_safe(item.engagement.get("comments"))
|
|
206
|
+
top_comment = _top_comment_score(item)
|
|
207
|
+
if not any([views, likes, comments, top_comment]):
|
|
208
|
+
return None
|
|
209
|
+
return (0.45 * views) + (0.27 * likes) + (0.18 * comments) + (0.10 * top_comment)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _generic_engagement(item: schema.SourceItem) -> float | None:
|
|
213
|
+
if not item.engagement:
|
|
214
|
+
return None
|
|
215
|
+
values = [logged for v in item.engagement.values() if (logged := log1p_safe(v)) > 0]
|
|
216
|
+
if not values:
|
|
217
|
+
return None
|
|
218
|
+
return sum(values) / len(values)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def engagement_raw(item: schema.SourceItem) -> float | None:
|
|
222
|
+
if item.source == "reddit":
|
|
223
|
+
return _reddit_engagement(item)
|
|
224
|
+
if item.source == "youtube":
|
|
225
|
+
return _youtube_engagement(item)
|
|
226
|
+
if item.source == "tiktok":
|
|
227
|
+
return _tiktok_engagement(item)
|
|
228
|
+
weights = ENGAGEMENT_WEIGHTS.get(item.source)
|
|
229
|
+
if weights:
|
|
230
|
+
return _weighted_engagement(item, weights)
|
|
231
|
+
return _generic_engagement(item)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def normalize(values: list[float | None]) -> list[int | None]:
|
|
235
|
+
valid = [value for value in values if value is not None]
|
|
236
|
+
if not valid:
|
|
237
|
+
return [None for _ in values]
|
|
238
|
+
low = min(valid)
|
|
239
|
+
high = max(valid)
|
|
240
|
+
if math.isclose(low, high):
|
|
241
|
+
return [50 if value is not None else None for value in values]
|
|
242
|
+
return [
|
|
243
|
+
None
|
|
244
|
+
if value is None
|
|
245
|
+
else int(((value - low) / (high - low)) * 100)
|
|
246
|
+
for value in values
|
|
247
|
+
]
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def annotate_stream(
|
|
251
|
+
items: list[schema.SourceItem],
|
|
252
|
+
ranking_query: "str | relevance.PreparedQuery",
|
|
253
|
+
freshness_mode: str,
|
|
254
|
+
reference_date: str | None = None,
|
|
255
|
+
max_days: int = 30,
|
|
256
|
+
) -> list[schema.SourceItem]:
|
|
257
|
+
"""Attach local scoring metadata and return items sorted by local_rank_score."""
|
|
258
|
+
prepared_query = ranking_query if isinstance(ranking_query, relevance.PreparedQuery) else relevance.PreparedQuery(ranking_query)
|
|
259
|
+
engagement_scores = normalize([engagement_raw(item) for item in items])
|
|
260
|
+
for item, eng_score in zip(items, engagement_scores, strict=True):
|
|
261
|
+
item.local_relevance = local_relevance(item, prepared_query)
|
|
262
|
+
item.freshness = freshness(
|
|
263
|
+
item,
|
|
264
|
+
freshness_mode,
|
|
265
|
+
reference_date=reference_date,
|
|
266
|
+
max_days=max_days,
|
|
267
|
+
)
|
|
268
|
+
item.engagement_score = eng_score
|
|
269
|
+
item.source_quality = source_quality(item.source)
|
|
270
|
+
item.local_rank_score = (
|
|
271
|
+
0.65 * item.local_relevance
|
|
272
|
+
+ 0.25 * (item.freshness / 100.0)
|
|
273
|
+
+ 0.10 * ((eng_score or 0) / 100.0)
|
|
274
|
+
)
|
|
275
|
+
return sorted(items, key=lambda item: item.local_rank_score or 0, reverse=True)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
_SOCIAL_SOURCES = {"reddit", "x", "tiktok", "instagram", "bluesky", "truthsocial"}
|
|
279
|
+
|
|
280
|
+
# Minimum view count for short-video platforms. Items below this floor
|
|
281
|
+
# are typically spam reposts or low-effort clips that add no unique signal.
|
|
282
|
+
_VIDEO_ENGAGEMENT_FLOOR_SOURCES = {"tiktok", "instagram"}
|
|
283
|
+
_VIDEO_ENGAGEMENT_FLOOR_VIEWS = 1000
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _passes_engagement_floor(item: schema.SourceItem, sole_source: bool) -> bool:
|
|
287
|
+
"""Check whether a TikTok/Instagram item meets the minimum view floor.
|
|
288
|
+
|
|
289
|
+
Items from sources not in _VIDEO_ENGAGEMENT_FLOOR_SOURCES always pass.
|
|
290
|
+
If the item's source is the *only* source represented in the batch
|
|
291
|
+
(sole_source=True), all items pass so we never return an empty result
|
|
292
|
+
for a whole source.
|
|
293
|
+
"""
|
|
294
|
+
if item.source not in _VIDEO_ENGAGEMENT_FLOOR_SOURCES:
|
|
295
|
+
return True
|
|
296
|
+
if sole_source:
|
|
297
|
+
return True
|
|
298
|
+
views = item.engagement.get("views", 0) if item.engagement else 0
|
|
299
|
+
return views >= _VIDEO_ENGAGEMENT_FLOOR_VIEWS
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def prune_low_relevance(
|
|
303
|
+
items: list[schema.SourceItem],
|
|
304
|
+
minimum: float = 0.15,
|
|
305
|
+
) -> list[schema.SourceItem]:
|
|
306
|
+
"""Drop weak lexical matches when stronger evidence exists.
|
|
307
|
+
|
|
308
|
+
Social-source items with zero engagement get a stricter threshold
|
|
309
|
+
because zero engagement on a social platform is a strong noise signal.
|
|
310
|
+
|
|
311
|
+
TikTok and Instagram items with fewer than 1000 views are pruned
|
|
312
|
+
(unless they are the only source represented in the batch).
|
|
313
|
+
"""
|
|
314
|
+
sources_present = {item.source for item in items}
|
|
315
|
+
|
|
316
|
+
def passes(item: schema.SourceItem) -> bool:
|
|
317
|
+
# YouTube items with successfully extracted transcripts should not
|
|
318
|
+
# be pruned by title-only relevance scoring — the transcript content
|
|
319
|
+
# already proves substantive topical coverage.
|
|
320
|
+
if item.source == "youtube" and item.snippet:
|
|
321
|
+
return True
|
|
322
|
+
rel = item.local_relevance if item.local_relevance is not None else 0.0
|
|
323
|
+
if rel < minimum:
|
|
324
|
+
return False
|
|
325
|
+
if item.source in _SOCIAL_SOURCES and (item.engagement_score is None or item.engagement_score == 0):
|
|
326
|
+
if rel < minimum * 1.5:
|
|
327
|
+
return False
|
|
328
|
+
sole_source = sources_present == {item.source}
|
|
329
|
+
if not _passes_engagement_floor(item, sole_source):
|
|
330
|
+
return False
|
|
331
|
+
return True
|
|
332
|
+
|
|
333
|
+
filtered = [item for item in items if passes(item)]
|
|
334
|
+
return filtered or items
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""SKILL.md metadata helpers — single source of truth for parsing skill frontmatter.
|
|
2
|
+
|
|
3
|
+
Centralizes the version regex that previously lived in render.py and was
|
|
4
|
+
duplicated in tests/test_plugin_contract.py and tests/test_version_consistency.py.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
# Matches `version: "x.y.z"`, `version: 'x.y.z'`, or `version: x.y.z` in YAML
|
|
11
|
+
# frontmatter. Multiline so the pattern can be applied to a full SKILL.md text.
|
|
12
|
+
# Three alternation groups — exactly one captures per successful match.
|
|
13
|
+
_VERSION_RE = re.compile(
|
|
14
|
+
r'''^version:\s*(?:"([^"]+)"|'([^']+)'|(\S+))\s*$''',
|
|
15
|
+
re.MULTILINE,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def read_skill_version(skill_md_path: Path) -> str | None:
|
|
20
|
+
"""Return the version string from a SKILL.md's frontmatter, or None.
|
|
21
|
+
|
|
22
|
+
Returns None if the file can't be read (missing, permission, decode error)
|
|
23
|
+
or if no `version:` line is found. Accepts double-quoted, single-quoted,
|
|
24
|
+
or unquoted YAML version scalars.
|
|
25
|
+
"""
|
|
26
|
+
try:
|
|
27
|
+
text = skill_md_path.read_text(encoding="utf-8")
|
|
28
|
+
except (OSError, UnicodeDecodeError):
|
|
29
|
+
return None
|
|
30
|
+
match = _VERSION_RE.search(text)
|
|
31
|
+
if not match:
|
|
32
|
+
return None
|
|
33
|
+
return match.group(1) or match.group(2) or match.group(3)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Best-window extraction for rerankable evidence snippets."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from . import relevance, schema
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _truncate_words(text: str, max_words: int) -> str:
|
|
9
|
+
words = text.split()
|
|
10
|
+
if len(words) <= max_words:
|
|
11
|
+
return text.strip()
|
|
12
|
+
return " ".join(words[:max_words]).strip() + "..."
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _windows(words: list[str], size: int, overlap: int) -> list[str]:
|
|
16
|
+
if not words:
|
|
17
|
+
return []
|
|
18
|
+
if len(words) <= size:
|
|
19
|
+
return [" ".join(words)]
|
|
20
|
+
step = max(1, size - overlap)
|
|
21
|
+
return [
|
|
22
|
+
" ".join(words[start:start + size])
|
|
23
|
+
for start in range(0, len(words), step)
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def extract_best_snippet(
|
|
28
|
+
item: schema.SourceItem,
|
|
29
|
+
ranking_query: "str | relevance.PreparedQuery",
|
|
30
|
+
max_words: int = 120,
|
|
31
|
+
) -> str:
|
|
32
|
+
"""Prefer existing snippets, else extract the best matching evidence window."""
|
|
33
|
+
preferred = item.snippet.strip()
|
|
34
|
+
if preferred:
|
|
35
|
+
return _truncate_words(preferred, max_words)
|
|
36
|
+
|
|
37
|
+
body = item.body.strip()
|
|
38
|
+
if not body:
|
|
39
|
+
return _truncate_words(item.title, max_words)
|
|
40
|
+
|
|
41
|
+
words = body.split()
|
|
42
|
+
candidates = _windows(words, size=min(max_words, 110), overlap=30)
|
|
43
|
+
if not candidates:
|
|
44
|
+
return _truncate_words(body, max_words)
|
|
45
|
+
|
|
46
|
+
prepared_query = ranking_query if isinstance(ranking_query, relevance.PreparedQuery) else relevance.PreparedQuery(ranking_query)
|
|
47
|
+
best = max(
|
|
48
|
+
candidates,
|
|
49
|
+
key=lambda candidate: relevance.token_overlap_relevance(prepared_query, candidate),
|
|
50
|
+
)
|
|
51
|
+
return _truncate_words(best, max_words)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Subprocess helpers: safe timeout + process-group cleanup.
|
|
2
|
+
|
|
3
|
+
Used by bird_x.py (Node.js Bird search) and youtube_yt.py (yt-dlp search
|
|
4
|
+
and transcript download). Both need the same os.setsid/killpg cleanup
|
|
5
|
+
dance on timeout to avoid orphaning child processes.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import signal
|
|
12
|
+
import subprocess
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from typing import Optional, Sequence
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SubprocTimeout(Exception):
|
|
18
|
+
"""Raised when a subprocess exceeds its timeout and is killed."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class SubprocResult:
|
|
23
|
+
"""Result of a subprocess run that captured stdout and stderr."""
|
|
24
|
+
|
|
25
|
+
returncode: int
|
|
26
|
+
stdout: str
|
|
27
|
+
stderr: str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def run_with_timeout(
|
|
31
|
+
cmd: Sequence[str],
|
|
32
|
+
*,
|
|
33
|
+
timeout: int,
|
|
34
|
+
env: Optional[dict] = None,
|
|
35
|
+
on_pid: Optional[callable] = None,
|
|
36
|
+
) -> SubprocResult:
|
|
37
|
+
"""Run a subprocess with process-group cleanup on timeout.
|
|
38
|
+
|
|
39
|
+
Spawns ``cmd`` inside its own process group via ``os.setsid`` where
|
|
40
|
+
available. If ``communicate(timeout=...)`` raises ``TimeoutExpired``,
|
|
41
|
+
signals ``SIGTERM`` to the entire group, falls back to ``proc.kill()``
|
|
42
|
+
if the signal fails, then waits up to 5 seconds for cleanup, and
|
|
43
|
+
raises ``SubprocTimeout``.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
cmd: Command and arguments to spawn.
|
|
47
|
+
timeout: Timeout in seconds passed to ``communicate()``.
|
|
48
|
+
env: Optional environment dict. If None, inherits parent env.
|
|
49
|
+
on_pid: Optional callable invoked with the child PID right after
|
|
50
|
+
spawn. Used by bird_x.py to register child PIDs for cleanup
|
|
51
|
+
tracking. Exceptions raised by the callback are suppressed.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
SubprocResult with returncode, stdout, and stderr as strings.
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
SubprocTimeout: If the process exceeded ``timeout``.
|
|
58
|
+
FileNotFoundError: If the executable is not found.
|
|
59
|
+
OSError: For other spawn failures.
|
|
60
|
+
"""
|
|
61
|
+
preexec = os.setsid if hasattr(os, "setsid") else None
|
|
62
|
+
|
|
63
|
+
proc = subprocess.Popen(
|
|
64
|
+
list(cmd),
|
|
65
|
+
stdout=subprocess.PIPE,
|
|
66
|
+
stderr=subprocess.PIPE,
|
|
67
|
+
text=True,
|
|
68
|
+
encoding="utf-8",
|
|
69
|
+
errors="replace",
|
|
70
|
+
preexec_fn=preexec,
|
|
71
|
+
env=env,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if on_pid is not None:
|
|
75
|
+
try:
|
|
76
|
+
on_pid(proc.pid)
|
|
77
|
+
except Exception:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
stdout, stderr = proc.communicate(timeout=timeout)
|
|
82
|
+
except subprocess.TimeoutExpired:
|
|
83
|
+
try:
|
|
84
|
+
if hasattr(os, "killpg") and hasattr(os, "getpgid"):
|
|
85
|
+
os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
|
|
86
|
+
else:
|
|
87
|
+
proc.kill()
|
|
88
|
+
except (ProcessLookupError, PermissionError, OSError, AttributeError):
|
|
89
|
+
proc.kill()
|
|
90
|
+
try:
|
|
91
|
+
proc.wait(timeout=5)
|
|
92
|
+
except subprocess.TimeoutExpired:
|
|
93
|
+
# Child ignored SIGTERM (or our killpg lost the race); escalate.
|
|
94
|
+
# Guard killpg/getpgid the same way the SIGTERM path above does:
|
|
95
|
+
# they are POSIX-only and raise AttributeError on Windows. The
|
|
96
|
+
# primary path was hardened in #552; this mirrors that guard on the
|
|
97
|
+
# escalation path (added later in #433) so the same crash can't
|
|
98
|
+
# re-surface here (#588).
|
|
99
|
+
try:
|
|
100
|
+
if hasattr(os, "killpg") and hasattr(os, "getpgid"):
|
|
101
|
+
os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
|
|
102
|
+
else:
|
|
103
|
+
proc.kill()
|
|
104
|
+
except (ProcessLookupError, PermissionError, OSError, AttributeError):
|
|
105
|
+
proc.kill()
|
|
106
|
+
try:
|
|
107
|
+
proc.wait(timeout=5)
|
|
108
|
+
except subprocess.TimeoutExpired:
|
|
109
|
+
pass # process unkillable (e.g. D-state); leave as zombie
|
|
110
|
+
raise SubprocTimeout(f"Command {cmd[0]} timed out after {timeout}s")
|
|
111
|
+
|
|
112
|
+
return SubprocResult(
|
|
113
|
+
returncode=proc.returncode,
|
|
114
|
+
stdout=stdout or "",
|
|
115
|
+
stderr=stderr or "",
|
|
116
|
+
)
|