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,92 @@
|
|
|
1
|
+
"""Typed source health: classify a source/tool outcome honestly.
|
|
2
|
+
|
|
3
|
+
The pipeline historically collapsed every failure into "returned nothing" or a
|
|
4
|
+
flat ``errors_by_source`` entry, which hides the difference between a tool that
|
|
5
|
+
is *absent*, one that is *present but broken* (the classic stale-venv-shim after
|
|
6
|
+
a Python upgrade), one that *timed out*, and one that merely *degraded* (fewer
|
|
7
|
+
results than expected). This module gives callers a small typed vocabulary so
|
|
8
|
+
warnings can say what actually happened and prescribe the right fix.
|
|
9
|
+
|
|
10
|
+
It complements ``preflight.py`` (which gates doomed *queries*); this gates
|
|
11
|
+
doomed *sources/tools*.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import shutil
|
|
17
|
+
import subprocess
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
|
|
20
|
+
# Health states, best to worst.
|
|
21
|
+
OK = "ok"
|
|
22
|
+
DEGRADED = "degraded" # ran, but returned less than expected
|
|
23
|
+
MISSING = "missing" # tool/binary/credential absent
|
|
24
|
+
BROKEN = "broken" # present but won't execute (stale shim, bad perms)
|
|
25
|
+
TIMEOUT = "timeout" # exceeded the probe deadline
|
|
26
|
+
ERROR = "error" # ran and failed for another reason
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class SourceHealth:
|
|
31
|
+
"""Typed outcome for a source or the tool backing it.
|
|
32
|
+
|
|
33
|
+
``state`` is one of the module-level constants. ``reason`` is a short,
|
|
34
|
+
human-readable explanation suitable for a run warning.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
name: str
|
|
38
|
+
state: str
|
|
39
|
+
reason: str = ""
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def ok(self) -> bool:
|
|
43
|
+
return self.state == OK
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def usable(self) -> bool:
|
|
47
|
+
"""True when the source produced something worth keeping (ok/degraded)."""
|
|
48
|
+
return self.state in (OK, DEGRADED)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def probe_command(
|
|
52
|
+
command: list[str],
|
|
53
|
+
timeout: float = 5.0,
|
|
54
|
+
) -> SourceHealth:
|
|
55
|
+
"""Probe an external command, distinguishing missing/broken/timeout/ok.
|
|
56
|
+
|
|
57
|
+
Separating these is what lets the caller emit a correct repair prescription
|
|
58
|
+
instead of a generic "failed":
|
|
59
|
+
- ``missing``: the executable is not on PATH.
|
|
60
|
+
- ``broken``: on PATH but won't run — FileNotFoundError/OSError on exec, or
|
|
61
|
+
shell exit 126/127 (not-executable / not-found-after-resolution), the
|
|
62
|
+
signature of a stale interpreter shim after an upgrade.
|
|
63
|
+
- ``timeout``: exceeded ``timeout`` seconds.
|
|
64
|
+
- ``ok``: exited 0.
|
|
65
|
+
- ``error``: ran but exited non-zero for another reason.
|
|
66
|
+
|
|
67
|
+
The command should be side-effect-free (e.g. ``["gh", "auth", "status"]``);
|
|
68
|
+
callers pass a status/version subcommand, not a mutating one.
|
|
69
|
+
"""
|
|
70
|
+
name = command[0] if command else ""
|
|
71
|
+
if not name or shutil.which(name) is None:
|
|
72
|
+
return SourceHealth(name=name, state=MISSING, reason=f"{name or 'command'} not found on PATH")
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
proc = subprocess.run(
|
|
76
|
+
command,
|
|
77
|
+
capture_output=True,
|
|
78
|
+
text=True,
|
|
79
|
+
timeout=timeout,
|
|
80
|
+
)
|
|
81
|
+
except (FileNotFoundError, OSError) as exc:
|
|
82
|
+
return SourceHealth(name=name, state=BROKEN, reason=f"{name} present but won't execute: {exc}")
|
|
83
|
+
except subprocess.TimeoutExpired:
|
|
84
|
+
return SourceHealth(name=name, state=TIMEOUT, reason=f"{name} timed out after {timeout:g}s")
|
|
85
|
+
|
|
86
|
+
if proc.returncode == 0:
|
|
87
|
+
return SourceHealth(name=name, state=OK)
|
|
88
|
+
if proc.returncode in (126, 127):
|
|
89
|
+
return SourceHealth(name=name, state=BROKEN, reason=f"{name} not executable (exit {proc.returncode})")
|
|
90
|
+
detail = (proc.stderr or proc.stdout or "").strip().splitlines()
|
|
91
|
+
first = detail[0] if detail else f"exit {proc.returncode}"
|
|
92
|
+
return SourceHealth(name=name, state=ERROR, reason=f"{name}: {first}")
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"""Hiring Signals analysis from normalized jobs SourceItems."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from collections import Counter, defaultdict
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from . import schema
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
THEME_KEYWORDS: dict[str, tuple[str, ...]] = {
|
|
13
|
+
"enterprise readiness": (
|
|
14
|
+
"enterprise", "soc 2", "sso", "security", "compliance", "procurement",
|
|
15
|
+
"admin", "governance", "audit",
|
|
16
|
+
),
|
|
17
|
+
"go-to-market": (
|
|
18
|
+
"sales", "account executive", "customer success", "solutions", "partnership",
|
|
19
|
+
"revenue", "demand generation", "marketing",
|
|
20
|
+
),
|
|
21
|
+
"ai and machine learning": (
|
|
22
|
+
"machine learning", "ml", "ai", "llm", "model", "research scientist",
|
|
23
|
+
"applied scientist", "data scientist",
|
|
24
|
+
),
|
|
25
|
+
"infrastructure and reliability": (
|
|
26
|
+
"infrastructure", "platform", "devops", "sre", "reliability", "cloud",
|
|
27
|
+
"distributed systems", "backend",
|
|
28
|
+
),
|
|
29
|
+
"product expansion": (
|
|
30
|
+
"product manager", "product designer", "growth", "activation", "mobile",
|
|
31
|
+
"frontend", "design",
|
|
32
|
+
),
|
|
33
|
+
"data and analytics": (
|
|
34
|
+
"data", "analytics", "business intelligence", "warehouse", "etl",
|
|
35
|
+
"insights",
|
|
36
|
+
),
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
SENIORITY_TERMS = ("head of", "director", "vp", "principal", "staff", "lead", "founding")
|
|
40
|
+
|
|
41
|
+
# Leadership markers that establish/own a function (a first-of-function hire).
|
|
42
|
+
LEADERSHIP_TERMS = ("head of", "chief", "global head", "svp", "vp of", "vp,", "director of")
|
|
43
|
+
|
|
44
|
+
# Title qualifiers that are level/logistics noise, not a specialized capability.
|
|
45
|
+
_GENERIC_QUALIFIERS = {
|
|
46
|
+
"senior", "staff", "principal", "lead", "junior", "mid", "sr", "jr",
|
|
47
|
+
"i", "ii", "iii", "iv", "remote", "hybrid", "onsite", "on-site",
|
|
48
|
+
"contract", "intern", "full-time", "part-time", "us", "uk", "emea",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def analyze(
|
|
53
|
+
items: list[schema.SourceItem],
|
|
54
|
+
*,
|
|
55
|
+
explicit: bool,
|
|
56
|
+
topic: str = "",
|
|
57
|
+
) -> dict[str, Any]:
|
|
58
|
+
"""Return a structured Hiring Signals summary for report artifacts."""
|
|
59
|
+
if not items:
|
|
60
|
+
return {
|
|
61
|
+
"mode": "explicit" if explicit else "standard",
|
|
62
|
+
"company_size_tier": "unknown",
|
|
63
|
+
"include": False,
|
|
64
|
+
"signals": [],
|
|
65
|
+
"strategic_candidates": [],
|
|
66
|
+
"omitted_reason": "no current public jobs evidence found",
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
size_tier = infer_company_size(items, topic=topic)
|
|
70
|
+
themes = _theme_items(items)
|
|
71
|
+
signals = [_build_signal(theme, theme_items, size_tier) for theme, theme_items in themes.items()]
|
|
72
|
+
signals = [signal for signal in signals if signal["evidence_count"] > 0]
|
|
73
|
+
signals.sort(key=lambda s: (s["confidence_score"], s["evidence_count"]), reverse=True)
|
|
74
|
+
|
|
75
|
+
# Strategic single-role signals are NOT count-gated: a founding or
|
|
76
|
+
# first-of-function role can outweigh a department's worth of headcount.
|
|
77
|
+
# The engine only FLAGS these; the reasoning model judges true novelty
|
|
78
|
+
# (e.g. whether "Human Simulation" is a new bet for this company).
|
|
79
|
+
strategic_candidates = _strategic_candidates(items)
|
|
80
|
+
|
|
81
|
+
include = (
|
|
82
|
+
bool(signals) or bool(strategic_candidates)
|
|
83
|
+
) if explicit else any(_passes_standard_threshold(signal, size_tier) for signal in signals)
|
|
84
|
+
if not explicit:
|
|
85
|
+
signals = [signal for signal in signals if _passes_standard_threshold(signal, size_tier)]
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
"mode": "explicit" if explicit else "standard",
|
|
89
|
+
"company_size_tier": size_tier,
|
|
90
|
+
"include": include,
|
|
91
|
+
"signals": signals,
|
|
92
|
+
"strategic_candidates": strategic_candidates,
|
|
93
|
+
"omitted_reason": "" if include else _omitted_reason(items, size_tier, signals),
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def infer_company_size(items: list[schema.SourceItem], *, topic: str = "") -> str:
|
|
98
|
+
"""Infer a coarse company-size tier from jobs evidence."""
|
|
99
|
+
topic_lower = topic.lower()
|
|
100
|
+
firmographic_text = " ".join(
|
|
101
|
+
" ".join([
|
|
102
|
+
str(item.metadata.get("company_size") or ""),
|
|
103
|
+
topic,
|
|
104
|
+
])
|
|
105
|
+
for item in items
|
|
106
|
+
).lower()
|
|
107
|
+
text = " ".join(
|
|
108
|
+
" ".join([
|
|
109
|
+
item.title,
|
|
110
|
+
item.body[:1000],
|
|
111
|
+
str(item.metadata.get("company_size") or ""),
|
|
112
|
+
str(item.metadata.get("source_domain") or ""),
|
|
113
|
+
topic,
|
|
114
|
+
])
|
|
115
|
+
for item in items
|
|
116
|
+
).lower()
|
|
117
|
+
count = len(items)
|
|
118
|
+
# Brand-name shortcut must match the COMPANY being researched (the topic),
|
|
119
|
+
# never the job-description body - JDs list enterprise customers (e.g.
|
|
120
|
+
# "trusted by Microsoft, Google"), which would misclassify a startup as
|
|
121
|
+
# mega-cap and suppress its real signals.
|
|
122
|
+
if re.search(r"\b(apple|uber|google|microsoft|amazon|meta|netflix)\b", topic_lower):
|
|
123
|
+
return "mega-cap"
|
|
124
|
+
if count >= 200 or re.search(r"\b(fortune 500|thousands of employees)\b", firmographic_text):
|
|
125
|
+
return "large-enterprise"
|
|
126
|
+
if count >= 35 or re.search(r"\b(series [cd]|public company)\b", text):
|
|
127
|
+
return "growth"
|
|
128
|
+
if count <= 12 or re.search(r"\b(founding|seed|series a|early[- ]stage|startup)\b", text):
|
|
129
|
+
return "startup"
|
|
130
|
+
return "mid-market"
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _theme_items(items: list[schema.SourceItem]) -> dict[str, list[schema.SourceItem]]:
|
|
134
|
+
themed: dict[str, list[schema.SourceItem]] = defaultdict(list)
|
|
135
|
+
for item in items:
|
|
136
|
+
text = f"{item.title} {item.body}".lower()
|
|
137
|
+
matched = False
|
|
138
|
+
for theme, keywords in THEME_KEYWORDS.items():
|
|
139
|
+
if any(keyword in text for keyword in keywords):
|
|
140
|
+
themed[theme].append(item)
|
|
141
|
+
matched = True
|
|
142
|
+
if not matched:
|
|
143
|
+
dept = str(item.metadata.get("department") or item.container or "").strip().lower()
|
|
144
|
+
fallback = dept or "general hiring"
|
|
145
|
+
themed[fallback].append(item)
|
|
146
|
+
return dict(themed)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _build_signal(theme: str, items: list[schema.SourceItem], size_tier: str) -> dict[str, Any]:
|
|
150
|
+
titles = [item.title for item in items if item.title]
|
|
151
|
+
departments = [
|
|
152
|
+
str(item.metadata.get("department") or item.container or "").strip()
|
|
153
|
+
for item in items
|
|
154
|
+
if str(item.metadata.get("department") or item.container or "").strip()
|
|
155
|
+
]
|
|
156
|
+
senior_roles = [
|
|
157
|
+
title for title in titles
|
|
158
|
+
if any(term in title.lower() for term in SENIORITY_TERMS)
|
|
159
|
+
]
|
|
160
|
+
strategic_count = sum(1 for title in titles if _is_strategic_title(title))
|
|
161
|
+
score = _confidence_score(
|
|
162
|
+
len(items), len(set(departments)), len(senior_roles), size_tier,
|
|
163
|
+
strategic_count=strategic_count,
|
|
164
|
+
)
|
|
165
|
+
evidence = [
|
|
166
|
+
{
|
|
167
|
+
"title": item.title,
|
|
168
|
+
"url": item.url,
|
|
169
|
+
"department": item.metadata.get("department") or item.container or "",
|
|
170
|
+
"published_at": item.published_at,
|
|
171
|
+
}
|
|
172
|
+
for item in items[:5]
|
|
173
|
+
]
|
|
174
|
+
return {
|
|
175
|
+
"theme": theme,
|
|
176
|
+
"interpretation": _interpretation(theme),
|
|
177
|
+
"confidence": _confidence_label(score),
|
|
178
|
+
"confidence_score": score,
|
|
179
|
+
"evidence_count": len(items),
|
|
180
|
+
"departments": [name for name, _count in Counter(departments).most_common(3)],
|
|
181
|
+
"senior_roles": senior_roles[:3],
|
|
182
|
+
"evidence": evidence,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _confidence_score(
|
|
187
|
+
count: int,
|
|
188
|
+
department_count: int,
|
|
189
|
+
senior_count: int,
|
|
190
|
+
size_tier: str,
|
|
191
|
+
strategic_count: int = 0,
|
|
192
|
+
) -> int:
|
|
193
|
+
# Count no longer dominates: founding/first-of-function and seniority can
|
|
194
|
+
# let a small cluster outrank a large generic one (a "new bet" beating
|
|
195
|
+
# "doubling down"). The reasoning model still makes the final novelty call.
|
|
196
|
+
score = count * 12 + min(department_count, 3) * 6 + senior_count * 10 + strategic_count * 14
|
|
197
|
+
if size_tier == "startup":
|
|
198
|
+
score += 20
|
|
199
|
+
elif size_tier == "mid-market":
|
|
200
|
+
score += 10
|
|
201
|
+
elif size_tier == "growth":
|
|
202
|
+
score -= 5
|
|
203
|
+
elif size_tier == "large-enterprise":
|
|
204
|
+
score -= 25
|
|
205
|
+
elif size_tier == "mega-cap":
|
|
206
|
+
score -= 40
|
|
207
|
+
return max(0, min(100, score))
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _passes_standard_threshold(signal: dict[str, Any], size_tier: str) -> bool:
|
|
211
|
+
thresholds = {
|
|
212
|
+
"startup": (2, 50),
|
|
213
|
+
"mid-market": (3, 58),
|
|
214
|
+
"growth": (4, 65),
|
|
215
|
+
"large-enterprise": (6, 78),
|
|
216
|
+
"mega-cap": (8, 86),
|
|
217
|
+
"unknown": (3, 62),
|
|
218
|
+
}
|
|
219
|
+
min_count, min_score = thresholds.get(size_tier, thresholds["unknown"])
|
|
220
|
+
return signal["evidence_count"] >= min_count and signal["confidence_score"] >= min_score
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _confidence_label(score: int) -> str:
|
|
224
|
+
if score >= 75:
|
|
225
|
+
return "high"
|
|
226
|
+
if score >= 50:
|
|
227
|
+
return "medium"
|
|
228
|
+
return "low"
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _interpretation(theme: str) -> str:
|
|
232
|
+
if theme == "general hiring":
|
|
233
|
+
return "hiring activity is visible, but the priority signal is diffuse"
|
|
234
|
+
return f"appears to be increasing focus on {theme}"
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _strategic_candidates(items: list[schema.SourceItem]) -> list[dict[str, Any]]:
|
|
238
|
+
"""Flag individual roles worth surfacing regardless of how many share a theme.
|
|
239
|
+
|
|
240
|
+
Pure structural detection (founding, first-of-function, specialized
|
|
241
|
+
qualifier, geographic novelty) - no semantic novelty judgment, which is
|
|
242
|
+
left to the reasoning model. Guarantees these roles reach synthesis instead
|
|
243
|
+
of being averaged away by count-weighting.
|
|
244
|
+
"""
|
|
245
|
+
item_locations = [(item, _norm_location(item)) for item in items]
|
|
246
|
+
location_counts = Counter(loc for _item, loc in item_locations if loc)
|
|
247
|
+
dominant = max(location_counts.values()) if location_counts else 0
|
|
248
|
+
|
|
249
|
+
scored: list[tuple[int, dict[str, Any]]] = []
|
|
250
|
+
for item, location in item_locations:
|
|
251
|
+
flags = _title_flags(item.title or "")
|
|
252
|
+
if location and location_counts.get(location, 0) == 1 and dominant >= 3:
|
|
253
|
+
flags.append("new-geo")
|
|
254
|
+
if not flags:
|
|
255
|
+
continue
|
|
256
|
+
priority = (
|
|
257
|
+
("founding" in flags) * 4
|
|
258
|
+
+ ("new-geo" in flags) * 3
|
|
259
|
+
+ ("leadership" in flags) * 2
|
|
260
|
+
+ ("specialized" in flags) * 1
|
|
261
|
+
)
|
|
262
|
+
scored.append((priority, {
|
|
263
|
+
"title": item.title,
|
|
264
|
+
"url": item.url,
|
|
265
|
+
"department": str(item.metadata.get("department") or item.container or "").strip(),
|
|
266
|
+
"location": location,
|
|
267
|
+
"published_at": item.published_at,
|
|
268
|
+
"flags": flags,
|
|
269
|
+
}))
|
|
270
|
+
scored.sort(key=lambda pair: pair[0], reverse=True)
|
|
271
|
+
return [candidate for _priority, candidate in scored[:10]]
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _title_flags(title: str) -> list[str]:
|
|
275
|
+
"""Structural strategic flags derivable from a title alone (no geo)."""
|
|
276
|
+
lowered = title.lower()
|
|
277
|
+
flags: list[str] = []
|
|
278
|
+
if "founding" in lowered or re.search(r"\bfirst\b", lowered):
|
|
279
|
+
flags.append("founding")
|
|
280
|
+
if any(term in lowered for term in LEADERSHIP_TERMS):
|
|
281
|
+
flags.append("leadership")
|
|
282
|
+
if _specialization(title):
|
|
283
|
+
flags.append("specialized")
|
|
284
|
+
return flags
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _is_strategic_title(title: str) -> bool:
|
|
288
|
+
return bool(_title_flags(title))
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _specialization(title: str) -> str:
|
|
292
|
+
"""Return a specialized sub-domain qualifier from a title, or ''.
|
|
293
|
+
|
|
294
|
+
"Research Scientist, Human Simulation" -> "Human Simulation".
|
|
295
|
+
"Engineer (Forward Deployed)" -> "Forward Deployed".
|
|
296
|
+
"Engineer, Senior" -> "" (generic level word, not a capability).
|
|
297
|
+
"""
|
|
298
|
+
tail = ""
|
|
299
|
+
paren = re.search(r"\(([^)]+)\)", title)
|
|
300
|
+
if paren:
|
|
301
|
+
tail = paren.group(1).strip()
|
|
302
|
+
elif "," in title:
|
|
303
|
+
tail = title.rsplit(",", 1)[1].strip()
|
|
304
|
+
if not tail or len(tail) < 4:
|
|
305
|
+
return ""
|
|
306
|
+
if tail.lower() in _GENERIC_QUALIFIERS:
|
|
307
|
+
return ""
|
|
308
|
+
return tail
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _norm_location(item: schema.SourceItem) -> str:
|
|
312
|
+
return str(item.metadata.get("location") or "").strip().lower()
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _omitted_reason(
|
|
316
|
+
items: list[schema.SourceItem],
|
|
317
|
+
size_tier: str,
|
|
318
|
+
signals: list[dict[str, Any]],
|
|
319
|
+
) -> str:
|
|
320
|
+
if not items:
|
|
321
|
+
return "no current public jobs evidence found"
|
|
322
|
+
if size_tier in {"large-enterprise", "mega-cap"}:
|
|
323
|
+
return "jobs evidence is too diffuse for the inferred company size"
|
|
324
|
+
if not signals:
|
|
325
|
+
return "jobs evidence did not cluster into a clear signal"
|
|
326
|
+
return "jobs evidence is too thin for standard-report inclusion"
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Optional hosted publishing for rendered HTML artifacts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any, Callable
|
|
7
|
+
from urllib.error import HTTPError, URLError
|
|
8
|
+
from urllib.request import Request, urlopen
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
DEFAULT_ENDPOINT = "https://api.ht-ml.app/v1/sites"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HtmlPublishError(RuntimeError):
|
|
15
|
+
"""Raised when the hosted HTML publish endpoint rejects the artifact."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def publish_html(
|
|
19
|
+
html_content: str,
|
|
20
|
+
*,
|
|
21
|
+
password: str | None = None,
|
|
22
|
+
endpoint: str = DEFAULT_ENDPOINT,
|
|
23
|
+
opener: Callable[..., Any] | None = None,
|
|
24
|
+
timeout: int = 30,
|
|
25
|
+
) -> dict[str, Any]:
|
|
26
|
+
"""Publish a single HTML document and return the provider response."""
|
|
27
|
+
if not html_content.strip():
|
|
28
|
+
raise HtmlPublishError("HTML content is empty")
|
|
29
|
+
|
|
30
|
+
payload: dict[str, str] = {"html_content": html_content}
|
|
31
|
+
if password is not None:
|
|
32
|
+
payload["password"] = password
|
|
33
|
+
|
|
34
|
+
request = Request(
|
|
35
|
+
endpoint,
|
|
36
|
+
data=json.dumps(payload).encode("utf-8"),
|
|
37
|
+
headers={"Content-Type": "application/json", "Accept": "application/json"},
|
|
38
|
+
method="POST",
|
|
39
|
+
)
|
|
40
|
+
open_fn = opener or urlopen
|
|
41
|
+
try:
|
|
42
|
+
with open_fn(request, timeout=timeout) as response:
|
|
43
|
+
body = response.read().decode("utf-8")
|
|
44
|
+
except HTTPError as exc:
|
|
45
|
+
detail = exc.read().decode("utf-8", errors="replace")
|
|
46
|
+
raise HtmlPublishError(_error_message(exc.code, detail)) from exc
|
|
47
|
+
except URLError as exc:
|
|
48
|
+
raise HtmlPublishError(str(exc.reason)) from exc
|
|
49
|
+
except OSError as exc:
|
|
50
|
+
raise HtmlPublishError(str(exc)) from exc
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
result = json.loads(body)
|
|
54
|
+
except json.JSONDecodeError as exc:
|
|
55
|
+
raise HtmlPublishError("publish endpoint returned non-JSON response") from exc
|
|
56
|
+
if not isinstance(result, dict):
|
|
57
|
+
raise HtmlPublishError("publish endpoint returned unexpected JSON response")
|
|
58
|
+
|
|
59
|
+
url = result.get("url")
|
|
60
|
+
if not isinstance(url, str) or not url.startswith("https://"):
|
|
61
|
+
raise HtmlPublishError("publish endpoint response did not include a valid url")
|
|
62
|
+
return result
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _error_message(status: int, detail: str) -> str:
|
|
66
|
+
try:
|
|
67
|
+
payload = json.loads(detail)
|
|
68
|
+
except json.JSONDecodeError:
|
|
69
|
+
payload = {}
|
|
70
|
+
message = payload.get("message") if isinstance(payload, dict) else None
|
|
71
|
+
if message:
|
|
72
|
+
return f"{status}: {message}"
|
|
73
|
+
return f"{status}: {detail.strip() or 'publish failed'}"
|