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.
Files changed (618) hide show
  1. package/.claude-plugin/marketplace.json +16 -0
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/LICENSE +24 -0
  4. package/bin/install.js +7 -5
  5. package/commands/{luckiest/finish.md → finish.md} +2 -0
  6. package/commands/go.md +53 -0
  7. package/commands/plan.md +66 -0
  8. package/package.json +16 -5
  9. package/skills/brainstorm/ATTRIBUTION.md +27 -0
  10. package/skills/brainstorm/SKILL.md +139 -0
  11. package/skills/luckiest-ab-testing/ATTRIBUTION.md +35 -0
  12. package/skills/luckiest-ab-testing/CHANGELOG.md +27 -0
  13. package/skills/luckiest-ab-testing/SKILL.md +379 -0
  14. package/skills/luckiest-ab-testing/evals/evals.json +118 -0
  15. package/skills/luckiest-ab-testing/references/sample-size-guide.md +290 -0
  16. package/skills/luckiest-ab-testing/references/test-templates.md +284 -0
  17. package/skills/luckiest-ad-creative/ATTRIBUTION.md +35 -0
  18. package/skills/luckiest-ad-creative/CHANGELOG.md +23 -0
  19. package/skills/luckiest-ad-creative/SKILL.md +393 -0
  20. package/skills/luckiest-ad-creative/evals/evals.json +90 -0
  21. package/skills/luckiest-ad-creative/references/generative-tools.md +637 -0
  22. package/skills/luckiest-ad-creative/references/platform-specs.md +213 -0
  23. package/skills/luckiest-ads/ATTRIBUTION.md +36 -0
  24. package/skills/luckiest-ads/CHANGELOG.md +24 -0
  25. package/skills/luckiest-ads/LICENSE +21 -0
  26. package/skills/luckiest-ads/SKILL.md +434 -0
  27. package/skills/luckiest-ads/evals/evals.json +90 -0
  28. package/skills/luckiest-ads/references/ad-copy-templates.md +207 -0
  29. package/skills/luckiest-ads/references/audience-targeting.md +243 -0
  30. package/skills/luckiest-ads/references/conversion-tracking.md +361 -0
  31. package/skills/luckiest-ads/references/platform-setup-checklists.md +277 -0
  32. package/skills/luckiest-advisors/ATTRIBUTION.md +16 -0
  33. package/skills/luckiest-advisors/CHANGELOG.md +99 -0
  34. package/skills/luckiest-advisors/SKILL.md +151 -0
  35. package/skills/luckiest-aeo-grader/ATTRIBUTION.md +19 -0
  36. package/skills/luckiest-aeo-grader/CHANGELOG.md +52 -0
  37. package/skills/luckiest-aeo-grader/SKILL.md +200 -0
  38. package/skills/luckiest-aeo-grader/evals/evals.json +19 -0
  39. package/skills/luckiest-aeo-grader/references/rubric.md +72 -0
  40. package/skills/luckiest-aeo-grader/scripts/scan.py +265 -0
  41. package/skills/luckiest-ai-seo/ATTRIBUTION.md +38 -0
  42. package/skills/luckiest-ai-seo/CHANGELOG.md +23 -0
  43. package/skills/luckiest-ai-seo/SKILL.md +510 -0
  44. package/skills/luckiest-ai-seo/evals/evals.json +90 -0
  45. package/skills/luckiest-ai-seo/references/content-patterns.md +285 -0
  46. package/skills/luckiest-ai-seo/references/content-types.md +71 -0
  47. package/skills/luckiest-ai-seo/references/okf.md +104 -0
  48. package/skills/luckiest-ai-seo/references/platform-ranking-factors.md +154 -0
  49. package/skills/luckiest-analytics/ATTRIBUTION.md +42 -0
  50. package/skills/luckiest-analytics/CHANGELOG.md +36 -0
  51. package/skills/luckiest-analytics/SKILL.md +380 -0
  52. package/skills/luckiest-analytics/evals/evals.json +90 -0
  53. package/skills/luckiest-analytics/references/event-library.md +260 -0
  54. package/skills/luckiest-analytics/references/ga4-implementation.md +300 -0
  55. package/skills/luckiest-analytics/references/gtm-implementation.md +390 -0
  56. package/skills/luckiest-aso/ATTRIBUTION.md +40 -0
  57. package/skills/luckiest-aso/CHANGELOG.md +33 -0
  58. package/skills/luckiest-aso/SKILL.md +327 -0
  59. package/skills/luckiest-aso/evals/evals.json +91 -0
  60. package/skills/luckiest-aso/references/apple-specs.md +107 -0
  61. package/skills/luckiest-aso/references/benchmarks.md +129 -0
  62. package/skills/luckiest-aso/references/google-play-specs.md +131 -0
  63. package/skills/luckiest-aso/references/report-template.md +213 -0
  64. package/skills/luckiest-aso/references/scoring-criteria.md +213 -0
  65. package/skills/luckiest-churn-prevention/ATTRIBUTION.md +41 -0
  66. package/skills/luckiest-churn-prevention/CHANGELOG.md +24 -0
  67. package/skills/luckiest-churn-prevention/SKILL.md +445 -0
  68. package/skills/luckiest-churn-prevention/evals/evals.json +93 -0
  69. package/skills/luckiest-churn-prevention/references/cancel-flow-patterns.md +316 -0
  70. package/skills/luckiest-churn-prevention/references/dunning-playbook.md +408 -0
  71. package/skills/luckiest-co-marketing/ATTRIBUTION.md +38 -0
  72. package/skills/luckiest-co-marketing/CHANGELOG.md +25 -0
  73. package/skills/luckiest-co-marketing/SKILL.md +331 -0
  74. package/skills/luckiest-co-marketing/evals/evals.json +84 -0
  75. package/skills/luckiest-coder/ATTRIBUTION.md +26 -0
  76. package/skills/luckiest-coder/CHANGELOG.md +30 -0
  77. package/skills/luckiest-coder/SKILL.md +77 -0
  78. package/skills/luckiest-coder/references/antigravity-tools.md +23 -0
  79. package/skills/luckiest-coder/references/codex-tools.md +39 -0
  80. package/skills/luckiest-coder/references/pi-tools.md +16 -0
  81. package/skills/luckiest-coder-brainstorming/ATTRIBUTION.md +26 -0
  82. package/skills/luckiest-coder-brainstorming/CHANGELOG.md +31 -0
  83. package/skills/luckiest-coder-brainstorming/SKILL.md +173 -0
  84. package/skills/luckiest-coder-brainstorming/scripts/frame-template.html +213 -0
  85. package/skills/luckiest-coder-brainstorming/scripts/helper.js +168 -0
  86. package/skills/luckiest-coder-brainstorming/scripts/server.cjs +721 -0
  87. package/skills/luckiest-coder-brainstorming/scripts/start-server.sh +209 -0
  88. package/skills/luckiest-coder-brainstorming/scripts/stop-server.sh +120 -0
  89. package/skills/luckiest-coder-brainstorming/spec-document-reviewer-prompt.md +49 -0
  90. package/skills/luckiest-coder-brainstorming/visual-companion.md +291 -0
  91. package/skills/luckiest-coder-consistency/ATTRIBUTION.md +30 -0
  92. package/skills/luckiest-coder-consistency/CHANGELOG.md +26 -0
  93. package/skills/luckiest-coder-consistency/SKILL.md +98 -0
  94. package/skills/luckiest-coder-constitution/ATTRIBUTION.md +28 -0
  95. package/skills/luckiest-coder-constitution/CHANGELOG.md +25 -0
  96. package/skills/luckiest-coder-constitution/SKILL.md +115 -0
  97. package/skills/luckiest-coder-debugging/ATTRIBUTION.md +26 -0
  98. package/skills/luckiest-coder-debugging/CHANGELOG.md +22 -0
  99. package/skills/luckiest-coder-debugging/CREATION-LOG.md +119 -0
  100. package/skills/luckiest-coder-debugging/SKILL.md +309 -0
  101. package/skills/luckiest-coder-debugging/condition-based-waiting-example.ts +158 -0
  102. package/skills/luckiest-coder-debugging/condition-based-waiting.md +115 -0
  103. package/skills/luckiest-coder-debugging/defense-in-depth.md +122 -0
  104. package/skills/luckiest-coder-debugging/find-polluter.sh +63 -0
  105. package/skills/luckiest-coder-debugging/root-cause-tracing.md +169 -0
  106. package/skills/luckiest-coder-debugging/test-academic.md +14 -0
  107. package/skills/luckiest-coder-debugging/test-pressure-1.md +58 -0
  108. package/skills/luckiest-coder-debugging/test-pressure-2.md +68 -0
  109. package/skills/luckiest-coder-debugging/test-pressure-3.md +69 -0
  110. package/skills/luckiest-coder-dispatching-parallel-agents/ATTRIBUTION.md +26 -0
  111. package/skills/luckiest-coder-dispatching-parallel-agents/CHANGELOG.md +23 -0
  112. package/skills/luckiest-coder-dispatching-parallel-agents/SKILL.md +198 -0
  113. package/skills/luckiest-coder-executing-plans/ATTRIBUTION.md +26 -0
  114. package/skills/luckiest-coder-executing-plans/CHANGELOG.md +22 -0
  115. package/skills/luckiest-coder-executing-plans/SKILL.md +83 -0
  116. package/skills/luckiest-coder-finishing-a-branch/ATTRIBUTION.md +26 -0
  117. package/skills/luckiest-coder-finishing-a-branch/CHANGELOG.md +22 -0
  118. package/skills/luckiest-coder-finishing-a-branch/SKILL.md +258 -0
  119. package/skills/luckiest-coder-git-worktrees/ATTRIBUTION.md +26 -0
  120. package/skills/luckiest-coder-git-worktrees/CHANGELOG.md +22 -0
  121. package/skills/luckiest-coder-git-worktrees/SKILL.md +215 -0
  122. package/skills/luckiest-coder-guard/ATTRIBUTION.md +29 -0
  123. package/skills/luckiest-coder-guard/CHANGELOG.md +48 -0
  124. package/skills/luckiest-coder-guard/LICENSE +21 -0
  125. package/skills/luckiest-coder-guard/SKILL.md +126 -0
  126. package/skills/luckiest-coder-guard/bin/check-careful.sh +65 -0
  127. package/skills/luckiest-coder-guard/bin/check-freeze.sh +48 -0
  128. package/skills/luckiest-coder-qa/ATTRIBUTION.md +28 -0
  129. package/skills/luckiest-coder-qa/CHANGELOG.md +49 -0
  130. package/skills/luckiest-coder-qa/LICENSE +21 -0
  131. package/skills/luckiest-coder-qa/SKILL.md +160 -0
  132. package/skills/luckiest-coder-qa/references/issue-taxonomy.md +85 -0
  133. package/skills/luckiest-coder-qa/references/qa-report-template.md +126 -0
  134. package/skills/luckiest-coder-receiving-code-review/ATTRIBUTION.md +26 -0
  135. package/skills/luckiest-coder-receiving-code-review/CHANGELOG.md +28 -0
  136. package/skills/luckiest-coder-receiving-code-review/SKILL.md +226 -0
  137. package/skills/luckiest-coder-requesting-code-review/ATTRIBUTION.md +26 -0
  138. package/skills/luckiest-coder-requesting-code-review/CHANGELOG.md +31 -0
  139. package/skills/luckiest-coder-requesting-code-review/SKILL.md +118 -0
  140. package/skills/luckiest-coder-requesting-code-review/code-reviewer.md +172 -0
  141. package/skills/luckiest-coder-shipping/ATTRIBUTION.md +26 -0
  142. package/skills/luckiest-coder-shipping/CHANGELOG.md +48 -0
  143. package/skills/luckiest-coder-shipping/LICENSE +21 -0
  144. package/skills/luckiest-coder-shipping/SKILL.md +148 -0
  145. package/skills/luckiest-coder-shipping/references/canary.md +44 -0
  146. package/skills/luckiest-coder-shipping/references/test-and-coverage.md +91 -0
  147. package/skills/luckiest-coder-subagent-driven-development/ATTRIBUTION.md +26 -0
  148. package/skills/luckiest-coder-subagent-driven-development/CHANGELOG.md +28 -0
  149. package/skills/luckiest-coder-subagent-driven-development/SKILL.md +439 -0
  150. package/skills/luckiest-coder-subagent-driven-development/implementer-prompt.md +139 -0
  151. package/skills/luckiest-coder-subagent-driven-development/scripts/review-package +44 -0
  152. package/skills/luckiest-coder-subagent-driven-development/scripts/sdd-workspace +22 -0
  153. package/skills/luckiest-coder-subagent-driven-development/scripts/task-brief +40 -0
  154. package/skills/luckiest-coder-subagent-driven-development/task-reviewer-prompt.md +188 -0
  155. package/skills/luckiest-coder-tdd/ATTRIBUTION.md +26 -0
  156. package/skills/luckiest-coder-tdd/CHANGELOG.md +26 -0
  157. package/skills/luckiest-coder-tdd/SKILL.md +384 -0
  158. package/skills/luckiest-coder-tdd/testing-anti-patterns.md +299 -0
  159. package/skills/luckiest-coder-verification/ATTRIBUTION.md +26 -0
  160. package/skills/luckiest-coder-verification/CHANGELOG.md +22 -0
  161. package/skills/luckiest-coder-verification/SKILL.md +152 -0
  162. package/skills/luckiest-coder-writing-plans/ATTRIBUTION.md +26 -0
  163. package/skills/luckiest-coder-writing-plans/CHANGELOG.md +22 -0
  164. package/skills/luckiest-coder-writing-plans/SKILL.md +191 -0
  165. package/skills/luckiest-coder-writing-plans/plan-document-reviewer-prompt.md +49 -0
  166. package/skills/luckiest-coder-writing-skills/ATTRIBUTION.md +26 -0
  167. package/skills/luckiest-coder-writing-skills/CHANGELOG.md +31 -0
  168. package/skills/luckiest-coder-writing-skills/SKILL.md +702 -0
  169. package/skills/luckiest-coder-writing-skills/anthropic-best-practices.md +1150 -0
  170. package/skills/luckiest-coder-writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  171. package/skills/luckiest-coder-writing-skills/graphviz-conventions.dot +172 -0
  172. package/skills/luckiest-coder-writing-skills/persuasion-principles.md +187 -0
  173. package/skills/luckiest-coder-writing-skills/render-graphs.js +168 -0
  174. package/skills/luckiest-coder-writing-skills/testing-skills-with-subagents.md +384 -0
  175. package/skills/luckiest-cold-email/ATTRIBUTION.md +36 -0
  176. package/skills/luckiest-cold-email/CHANGELOG.md +24 -0
  177. package/skills/luckiest-cold-email/SKILL.md +213 -0
  178. package/skills/luckiest-cold-email/evals/evals.json +94 -0
  179. package/skills/luckiest-cold-email/references/benchmarks.md +83 -0
  180. package/skills/luckiest-cold-email/references/follow-up-sequences.md +81 -0
  181. package/skills/luckiest-cold-email/references/frameworks.md +90 -0
  182. package/skills/luckiest-cold-email/references/personalization.md +79 -0
  183. package/skills/luckiest-cold-email/references/subject-lines.md +53 -0
  184. package/skills/luckiest-community-marketing/ATTRIBUTION.md +41 -0
  185. package/skills/luckiest-community-marketing/CHANGELOG.md +35 -0
  186. package/skills/luckiest-community-marketing/SKILL.md +200 -0
  187. package/skills/luckiest-community-marketing/evals/evals.json +89 -0
  188. package/skills/luckiest-competitor-profiling/ATTRIBUTION.md +36 -0
  189. package/skills/luckiest-competitor-profiling/CHANGELOG.md +24 -0
  190. package/skills/luckiest-competitor-profiling/SKILL.md +443 -0
  191. package/skills/luckiest-competitor-profiling/evals/evals.json +85 -0
  192. package/skills/luckiest-competitor-profiling/references/templates.md +167 -0
  193. package/skills/luckiest-competitor-profiling/references/tool-reference.md +179 -0
  194. package/skills/luckiest-competitors/ATTRIBUTION.md +36 -0
  195. package/skills/luckiest-competitors/CHANGELOG.md +25 -0
  196. package/skills/luckiest-competitors/SKILL.md +171 -0
  197. package/skills/luckiest-competitors/evals/evals.json +93 -0
  198. package/skills/luckiest-competitors/references/content-architecture.md +271 -0
  199. package/skills/luckiest-competitors/references/templates.md +223 -0
  200. package/skills/luckiest-content-strategy/ATTRIBUTION.md +39 -0
  201. package/skills/luckiest-content-strategy/CHANGELOG.md +23 -0
  202. package/skills/luckiest-content-strategy/SKILL.md +395 -0
  203. package/skills/luckiest-content-strategy/evals/evals.json +90 -0
  204. package/skills/luckiest-content-strategy/references/headless-cms.md +194 -0
  205. package/skills/luckiest-copy-editing/ATTRIBUTION.md +41 -0
  206. package/skills/luckiest-copy-editing/CHANGELOG.md +31 -0
  207. package/skills/luckiest-copy-editing/SKILL.md +491 -0
  208. package/skills/luckiest-copy-editing/evals/evals.json +89 -0
  209. package/skills/luckiest-copy-editing/references/checklist.md +66 -0
  210. package/skills/luckiest-copy-editing/references/content-refresh.md +38 -0
  211. package/skills/luckiest-copy-editing/references/plain-english-alternatives.md +394 -0
  212. package/skills/luckiest-copywriting/ATTRIBUTION.md +35 -0
  213. package/skills/luckiest-copywriting/CHANGELOG.md +23 -0
  214. package/skills/luckiest-copywriting/SKILL.md +296 -0
  215. package/skills/luckiest-copywriting/evals/evals.json +111 -0
  216. package/skills/luckiest-copywriting/references/copy-frameworks.md +344 -0
  217. package/skills/luckiest-copywriting/references/natural-transitions.md +272 -0
  218. package/skills/luckiest-cro/ATTRIBUTION.md +34 -0
  219. package/skills/luckiest-cro/CHANGELOG.md +23 -0
  220. package/skills/luckiest-cro/SKILL.md +215 -0
  221. package/skills/luckiest-cro/evals/evals.json +111 -0
  222. package/skills/luckiest-cro/references/experiments.md +248 -0
  223. package/skills/luckiest-cro/references/form.md +422 -0
  224. package/skills/luckiest-customer-research/ATTRIBUTION.md +40 -0
  225. package/skills/luckiest-customer-research/CHANGELOG.md +26 -0
  226. package/skills/luckiest-customer-research/SKILL.md +293 -0
  227. package/skills/luckiest-customer-research/evals/evals.json +162 -0
  228. package/skills/luckiest-customer-research/references/source-guides.md +401 -0
  229. package/skills/luckiest-directory-submissions/ATTRIBUTION.md +40 -0
  230. package/skills/luckiest-directory-submissions/CHANGELOG.md +33 -0
  231. package/skills/luckiest-directory-submissions/SKILL.md +419 -0
  232. package/skills/luckiest-directory-submissions/evals/evals.json +94 -0
  233. package/skills/luckiest-directory-submissions/references/directory-list.md +477 -0
  234. package/skills/luckiest-directory-submissions/references/positioning-variations.md +232 -0
  235. package/skills/luckiest-directory-submissions/references/submission-tracker-template.csv +266 -0
  236. package/skills/luckiest-emails/ATTRIBUTION.md +42 -0
  237. package/skills/luckiest-emails/CHANGELOG.md +33 -0
  238. package/skills/luckiest-emails/SKILL.md +220 -0
  239. package/skills/luckiest-emails/evals/evals.json +93 -0
  240. package/skills/luckiest-emails/references/copy-guidelines.md +120 -0
  241. package/skills/luckiest-emails/references/email-types.md +515 -0
  242. package/skills/luckiest-emails/references/sequence-templates.md +168 -0
  243. package/skills/luckiest-extract-design-system/ATTRIBUTION.md +8 -0
  244. package/skills/luckiest-extract-design-system/CHANGELOG.md +37 -0
  245. package/skills/luckiest-extract-design-system/SKILL.md +83 -0
  246. package/skills/luckiest-free-tools/ATTRIBUTION.md +43 -0
  247. package/skills/luckiest-free-tools/CHANGELOG.md +25 -0
  248. package/skills/luckiest-free-tools/SKILL.md +220 -0
  249. package/skills/luckiest-free-tools/evals/evals.json +90 -0
  250. package/skills/luckiest-free-tools/references/tool-types.md +217 -0
  251. package/skills/luckiest-image/ATTRIBUTION.md +43 -0
  252. package/skills/luckiest-image/CHANGELOG.md +23 -0
  253. package/skills/luckiest-image/SKILL.md +376 -0
  254. package/skills/luckiest-image/evals/evals.json +89 -0
  255. package/skills/luckiest-image/references/ai-image-prompting.md +229 -0
  256. package/skills/luckiest-launch/ATTRIBUTION.md +40 -0
  257. package/skills/luckiest-launch/CHANGELOG.md +43 -0
  258. package/skills/luckiest-launch/SKILL.md +385 -0
  259. package/skills/luckiest-launch/evals/evals.json +91 -0
  260. package/skills/luckiest-lead-magnets/ATTRIBUTION.md +35 -0
  261. package/skills/luckiest-lead-magnets/CHANGELOG.md +23 -0
  262. package/skills/luckiest-lead-magnets/SKILL.md +331 -0
  263. package/skills/luckiest-lead-magnets/evals/evals.json +87 -0
  264. package/skills/luckiest-lead-magnets/references/benchmarks.md +129 -0
  265. package/skills/luckiest-lead-magnets/references/format-guide.md +196 -0
  266. package/skills/luckiest-marketing-ideas/ATTRIBUTION.md +39 -0
  267. package/skills/luckiest-marketing-ideas/CHANGELOG.md +33 -0
  268. package/skills/luckiest-marketing-ideas/SKILL.md +191 -0
  269. package/skills/luckiest-marketing-ideas/evals/evals.json +90 -0
  270. package/skills/luckiest-marketing-ideas/references/ideas-by-category.md +366 -0
  271. package/skills/luckiest-marketing-plan/ATTRIBUTION.md +40 -0
  272. package/skills/luckiest-marketing-plan/CHANGELOG.md +23 -0
  273. package/skills/luckiest-marketing-plan/SKILL.md +285 -0
  274. package/skills/luckiest-marketing-plan/evals/evals.json +96 -0
  275. package/skills/luckiest-marketing-plan/references/aarrr-framework.md +180 -0
  276. package/skills/luckiest-marketing-plan/references/budget-planning.md +168 -0
  277. package/skills/luckiest-marketing-plan/references/client-types.md +373 -0
  278. package/skills/luckiest-marketing-plan/references/current-state-rubric.md +255 -0
  279. package/skills/luckiest-marketing-plan/references/example-quietude.md +972 -0
  280. package/skills/luckiest-marketing-plan/references/funding-stage-unlocks.md +230 -0
  281. package/skills/luckiest-marketing-plan/references/growth-patterns.md +148 -0
  282. package/skills/luckiest-marketing-plan/references/idea-cross-reference.md +265 -0
  283. package/skills/luckiest-marketing-plan/references/measurement-framework.md +213 -0
  284. package/skills/luckiest-marketing-plan/references/methodology.md +363 -0
  285. package/skills/luckiest-marketing-plan/references/ops-stack-mapping.md +197 -0
  286. package/skills/luckiest-marketing-plan/references/plan-template.md +494 -0
  287. package/skills/luckiest-marketing-plan/references/team-and-agency-model.md +278 -0
  288. package/skills/luckiest-marketing-psychology/ATTRIBUTION.md +31 -0
  289. package/skills/luckiest-marketing-psychology/CHANGELOG.md +32 -0
  290. package/skills/luckiest-marketing-psychology/SKILL.md +202 -0
  291. package/skills/luckiest-marketing-psychology/evals.json +88 -0
  292. package/skills/luckiest-model-router/ATTRIBUTION.md +11 -0
  293. package/skills/luckiest-model-router/CHANGELOG.md +45 -0
  294. package/skills/luckiest-model-router/SKILL.md +74 -0
  295. package/skills/luckiest-model-router/scripts/route.py +145 -0
  296. package/skills/luckiest-offers/ATTRIBUTION.md +39 -0
  297. package/skills/luckiest-offers/CHANGELOG.md +23 -0
  298. package/skills/luckiest-offers/SKILL.md +167 -0
  299. package/skills/luckiest-offers/references/bonus-stacking.md +150 -0
  300. package/skills/luckiest-offers/references/examples.md +215 -0
  301. package/skills/luckiest-offers/references/guarantee-design.md +172 -0
  302. package/skills/luckiest-offers/references/offer-anatomy.md +203 -0
  303. package/skills/luckiest-offers/references/offer-formats.md +240 -0
  304. package/skills/luckiest-offers/references/scarcity-urgency.md +175 -0
  305. package/skills/luckiest-offers/references/value-equation.md +134 -0
  306. package/skills/luckiest-onboarding/ATTRIBUTION.md +43 -0
  307. package/skills/luckiest-onboarding/CHANGELOG.md +31 -0
  308. package/skills/luckiest-onboarding/SKILL.md +243 -0
  309. package/skills/luckiest-onboarding/references/experiments.md +258 -0
  310. package/skills/luckiest-paywalls/ATTRIBUTION.md +39 -0
  311. package/skills/luckiest-paywalls/CHANGELOG.md +25 -0
  312. package/skills/luckiest-paywalls/SKILL.md +256 -0
  313. package/skills/luckiest-paywalls/evals/evals.json +93 -0
  314. package/skills/luckiest-paywalls/references/experiments.md +164 -0
  315. package/skills/luckiest-popups/ATTRIBUTION.md +35 -0
  316. package/skills/luckiest-popups/CHANGELOG.md +27 -0
  317. package/skills/luckiest-popups/SKILL.md +477 -0
  318. package/skills/luckiest-popups/evals/evals.json +107 -0
  319. package/skills/luckiest-pricing/ATTRIBUTION.md +35 -0
  320. package/skills/luckiest-pricing/CHANGELOG.md +31 -0
  321. package/skills/luckiest-pricing/SKILL.md +283 -0
  322. package/skills/luckiest-pricing/evals/evals.json +90 -0
  323. package/skills/luckiest-pricing/references/research-methods.md +152 -0
  324. package/skills/luckiest-pricing/references/tier-structure.md +232 -0
  325. package/skills/luckiest-product-marketing/ATTRIBUTION.md +38 -0
  326. package/skills/luckiest-product-marketing/CHANGELOG.md +23 -0
  327. package/skills/luckiest-product-marketing/SKILL.md +256 -0
  328. package/skills/luckiest-product-marketing/evals/evals.json +85 -0
  329. package/skills/luckiest-programmatic-seo/ATTRIBUTION.md +35 -0
  330. package/skills/luckiest-programmatic-seo/CHANGELOG.md +24 -0
  331. package/skills/luckiest-programmatic-seo/SKILL.md +286 -0
  332. package/skills/luckiest-programmatic-seo/evals/evals.json +95 -0
  333. package/skills/luckiest-programmatic-seo/references/playbooks.md +308 -0
  334. package/skills/luckiest-prompt-rewrite/ATTRIBUTION.md +39 -0
  335. package/skills/luckiest-prompt-rewrite/CHANGELOG.md +70 -0
  336. package/skills/luckiest-prompt-rewrite/LICENSE +21 -0
  337. package/skills/luckiest-prompt-rewrite/SKILL.md +494 -0
  338. package/skills/luckiest-prompt-rewrite/references/patterns.md +82 -0
  339. package/skills/luckiest-prompt-rewrite/references/templates.md +454 -0
  340. package/skills/luckiest-prospecting/ATTRIBUTION.md +29 -0
  341. package/skills/luckiest-prospecting/CHANGELOG.md +24 -0
  342. package/skills/luckiest-prospecting/SKILL.md +275 -0
  343. package/skills/luckiest-prospecting/evals/evals.json +107 -0
  344. package/skills/luckiest-prospecting/references/b2b-prospecting.md +106 -0
  345. package/skills/luckiest-prospecting/references/compliance.md +123 -0
  346. package/skills/luckiest-prospecting/references/data-sources.md +287 -0
  347. package/skills/luckiest-prospecting/references/local-prospecting.md +165 -0
  348. package/skills/luckiest-prospecting/references/saas-prospecting.md +123 -0
  349. package/skills/luckiest-public-relations/ATTRIBUTION.md +34 -0
  350. package/skills/luckiest-public-relations/CHANGELOG.md +23 -0
  351. package/skills/luckiest-public-relations/SKILL.md +154 -0
  352. package/skills/luckiest-public-relations/references/journalist-pitching.md +368 -0
  353. package/skills/luckiest-public-relations/references/media-outlets.md +207 -0
  354. package/skills/luckiest-public-relations/references/newsjacking.md +218 -0
  355. package/skills/luckiest-public-relations/references/press-platforms.md +188 -0
  356. package/skills/luckiest-referrals/ATTRIBUTION.md +41 -0
  357. package/skills/luckiest-referrals/CHANGELOG.md +23 -0
  358. package/skills/luckiest-referrals/SKILL.md +292 -0
  359. package/skills/luckiest-referrals/evals/evals.json +90 -0
  360. package/skills/luckiest-referrals/references/affiliate-programs.md +167 -0
  361. package/skills/luckiest-referrals/references/program-examples.md +143 -0
  362. package/skills/luckiest-revops/ATTRIBUTION.md +40 -0
  363. package/skills/luckiest-revops/CHANGELOG.md +24 -0
  364. package/skills/luckiest-revops/SKILL.md +362 -0
  365. package/skills/luckiest-revops/evals/evals.json +91 -0
  366. package/skills/luckiest-revops/references/automation-playbooks.md +290 -0
  367. package/skills/luckiest-revops/references/lifecycle-definitions.md +278 -0
  368. package/skills/luckiest-revops/references/routing-rules.md +203 -0
  369. package/skills/luckiest-revops/references/scoring-models.md +247 -0
  370. package/skills/luckiest-sales-enablement/ATTRIBUTION.md +39 -0
  371. package/skills/luckiest-sales-enablement/CHANGELOG.md +23 -0
  372. package/skills/luckiest-sales-enablement/SKILL.md +379 -0
  373. package/skills/luckiest-sales-enablement/evals/evals.json +91 -0
  374. package/skills/luckiest-sales-enablement/references/deck-frameworks.md +263 -0
  375. package/skills/luckiest-sales-enablement/references/demo-scripts.md +355 -0
  376. package/skills/luckiest-sales-enablement/references/objection-library.md +270 -0
  377. package/skills/luckiest-sales-enablement/references/one-pager-templates.md +208 -0
  378. package/skills/luckiest-schema/ATTRIBUTION.md +39 -0
  379. package/skills/luckiest-schema/CHANGELOG.md +25 -0
  380. package/skills/luckiest-schema/SKILL.md +209 -0
  381. package/skills/luckiest-schema/evals/evals.json +88 -0
  382. package/skills/luckiest-schema/references/schema-examples.md +398 -0
  383. package/skills/luckiest-seo-audit/ATTRIBUTION.md +39 -0
  384. package/skills/luckiest-seo-audit/CHANGELOG.md +23 -0
  385. package/skills/luckiest-seo-audit/SKILL.md +529 -0
  386. package/skills/luckiest-seo-audit/evals/evals.json +136 -0
  387. package/skills/luckiest-seo-audit/references/ai-writing-detection.md +200 -0
  388. package/skills/luckiest-seo-audit/references/international-seo.md +230 -0
  389. package/skills/luckiest-session-handoff/ATTRIBUTION.md +14 -0
  390. package/skills/luckiest-session-handoff/CHANGELOG.md +30 -0
  391. package/skills/luckiest-session-handoff/SKILL.md +96 -0
  392. package/skills/luckiest-signup/ATTRIBUTION.md +48 -0
  393. package/skills/luckiest-signup/CHANGELOG.md +24 -0
  394. package/skills/luckiest-signup/SKILL.md +382 -0
  395. package/skills/luckiest-signup/evals/evals.json +88 -0
  396. package/skills/luckiest-site-architecture/ATTRIBUTION.md +38 -0
  397. package/skills/luckiest-site-architecture/CHANGELOG.md +23 -0
  398. package/skills/luckiest-site-architecture/SKILL.md +383 -0
  399. package/skills/luckiest-site-architecture/evals/evals.json +88 -0
  400. package/skills/luckiest-site-architecture/references/mermaid-templates.md +216 -0
  401. package/skills/luckiest-site-architecture/references/navigation-patterns.md +305 -0
  402. package/skills/luckiest-site-architecture/references/site-type-templates.md +293 -0
  403. package/skills/luckiest-sms/ATTRIBUTION.md +27 -0
  404. package/skills/luckiest-sms/CHANGELOG.md +24 -0
  405. package/skills/luckiest-sms/SKILL.md +400 -0
  406. package/skills/luckiest-sms/evals/evals.json +100 -0
  407. package/skills/luckiest-sms/references/compliance.md +202 -0
  408. package/skills/luckiest-sms/references/platforms.md +318 -0
  409. package/skills/luckiest-sms/references/sequence-templates.md +282 -0
  410. package/skills/luckiest-social/ATTRIBUTION.md +27 -0
  411. package/skills/luckiest-social/CHANGELOG.md +24 -0
  412. package/skills/luckiest-social/SKILL.md +430 -0
  413. package/skills/luckiest-social/evals/evals.json +92 -0
  414. package/skills/luckiest-social/references/listening-sources-template.md +123 -0
  415. package/skills/luckiest-social/references/listening.md +283 -0
  416. package/skills/luckiest-social/references/platform-limits.md +110 -0
  417. package/skills/luckiest-social/references/platforms.md +170 -0
  418. package/skills/luckiest-social/references/post-templates.md +177 -0
  419. package/skills/luckiest-social/references/reverse-engineering.md +195 -0
  420. package/skills/luckiest-social/references/short-form-video.md +237 -0
  421. package/skills/luckiest-thinker/ATTRIBUTION.md +16 -0
  422. package/skills/luckiest-thinker/CHANGELOG.md +20 -0
  423. package/skills/luckiest-thinker/LICENSE +21 -0
  424. package/skills/luckiest-thinker/SKILL.md +92 -0
  425. package/skills/luckiest-thinker/references/distribution-calibration.md +106 -0
  426. package/skills/luckiest-thinker/references/research-foundations.md +214 -0
  427. package/skills/luckiest-thinker/references/thinking-patterns.md +138 -0
  428. package/skills/luckiest-trends/.skillignore +10 -0
  429. package/skills/luckiest-trends/ATTRIBUTION.md +43 -0
  430. package/skills/luckiest-trends/CHANGELOG.md +86 -0
  431. package/skills/luckiest-trends/LICENSE +21 -0
  432. package/skills/luckiest-trends/SKILL.md +194 -0
  433. package/skills/luckiest-trends/agents/openai.yaml +8 -0
  434. package/skills/luckiest-trends/references/engine-contract.md +2041 -0
  435. package/skills/luckiest-trends/references/save-html-brief.md +206 -0
  436. package/skills/luckiest-trends/scripts/briefing.py +272 -0
  437. package/skills/luckiest-trends/scripts/build-skill.sh +39 -0
  438. package/skills/luckiest-trends/scripts/compare.sh +61 -0
  439. package/skills/luckiest-trends/scripts/evaluate_search_quality.py +573 -0
  440. package/skills/luckiest-trends/scripts/last30days.py +1467 -0
  441. package/skills/luckiest-trends/scripts/lib/__init__.py +1 -0
  442. package/skills/luckiest-trends/scripts/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  443. package/skills/luckiest-trends/scripts/lib/__pycache__/arxiv.cpython-313.pyc +0 -0
  444. package/skills/luckiest-trends/scripts/lib/__pycache__/bird_x.cpython-313.pyc +0 -0
  445. package/skills/luckiest-trends/scripts/lib/__pycache__/bluesky.cpython-313.pyc +0 -0
  446. package/skills/luckiest-trends/scripts/lib/__pycache__/categories.cpython-313.pyc +0 -0
  447. package/skills/luckiest-trends/scripts/lib/__pycache__/cjk.cpython-313.pyc +0 -0
  448. package/skills/luckiest-trends/scripts/lib/__pycache__/cluster.cpython-313.pyc +0 -0
  449. package/skills/luckiest-trends/scripts/lib/__pycache__/dates.cpython-313.pyc +0 -0
  450. package/skills/luckiest-trends/scripts/lib/__pycache__/dedupe.cpython-313.pyc +0 -0
  451. package/skills/luckiest-trends/scripts/lib/__pycache__/digg.cpython-313.pyc +0 -0
  452. package/skills/luckiest-trends/scripts/lib/__pycache__/entity_extract.cpython-313.pyc +0 -0
  453. package/skills/luckiest-trends/scripts/lib/__pycache__/env.cpython-313.pyc +0 -0
  454. package/skills/luckiest-trends/scripts/lib/__pycache__/fusion.cpython-313.pyc +0 -0
  455. package/skills/luckiest-trends/scripts/lib/__pycache__/github.cpython-313.pyc +0 -0
  456. package/skills/luckiest-trends/scripts/lib/__pycache__/grounding.cpython-313.pyc +0 -0
  457. package/skills/luckiest-trends/scripts/lib/__pycache__/hackernews.cpython-313.pyc +0 -0
  458. package/skills/luckiest-trends/scripts/lib/__pycache__/hiring_signals.cpython-313.pyc +0 -0
  459. package/skills/luckiest-trends/scripts/lib/__pycache__/html_render.cpython-313.pyc +0 -0
  460. package/skills/luckiest-trends/scripts/lib/__pycache__/http.cpython-313.pyc +0 -0
  461. package/skills/luckiest-trends/scripts/lib/__pycache__/instagram.cpython-313.pyc +0 -0
  462. package/skills/luckiest-trends/scripts/lib/__pycache__/jobs.cpython-313.pyc +0 -0
  463. package/skills/luckiest-trends/scripts/lib/__pycache__/linkedin.cpython-313.pyc +0 -0
  464. package/skills/luckiest-trends/scripts/lib/__pycache__/log.cpython-313.pyc +0 -0
  465. package/skills/luckiest-trends/scripts/lib/__pycache__/normalize.cpython-313.pyc +0 -0
  466. package/skills/luckiest-trends/scripts/lib/__pycache__/permission_preflight.cpython-313.pyc +0 -0
  467. package/skills/luckiest-trends/scripts/lib/__pycache__/perplexity.cpython-313.pyc +0 -0
  468. package/skills/luckiest-trends/scripts/lib/__pycache__/pinterest.cpython-313.pyc +0 -0
  469. package/skills/luckiest-trends/scripts/lib/__pycache__/pipeline.cpython-313.pyc +0 -0
  470. package/skills/luckiest-trends/scripts/lib/__pycache__/planner.cpython-313.pyc +0 -0
  471. package/skills/luckiest-trends/scripts/lib/__pycache__/polymarket.cpython-313.pyc +0 -0
  472. package/skills/luckiest-trends/scripts/lib/__pycache__/preflight.cpython-313.pyc +0 -0
  473. package/skills/luckiest-trends/scripts/lib/__pycache__/providers.cpython-313.pyc +0 -0
  474. package/skills/luckiest-trends/scripts/lib/__pycache__/quality_nudge.cpython-313.pyc +0 -0
  475. package/skills/luckiest-trends/scripts/lib/__pycache__/query.cpython-313.pyc +0 -0
  476. package/skills/luckiest-trends/scripts/lib/__pycache__/reddit.cpython-313.pyc +0 -0
  477. package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_arctic.cpython-313.pyc +0 -0
  478. package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_enrich.cpython-313.pyc +0 -0
  479. package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_keyless.cpython-313.pyc +0 -0
  480. package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_listing.cpython-313.pyc +0 -0
  481. package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_public.cpython-313.pyc +0 -0
  482. package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_rss.cpython-313.pyc +0 -0
  483. package/skills/luckiest-trends/scripts/lib/__pycache__/reddit_shreddit.cpython-313.pyc +0 -0
  484. package/skills/luckiest-trends/scripts/lib/__pycache__/relevance.cpython-313.pyc +0 -0
  485. package/skills/luckiest-trends/scripts/lib/__pycache__/render.cpython-313.pyc +0 -0
  486. package/skills/luckiest-trends/scripts/lib/__pycache__/rerank.cpython-313.pyc +0 -0
  487. package/skills/luckiest-trends/scripts/lib/__pycache__/resolve.cpython-313.pyc +0 -0
  488. package/skills/luckiest-trends/scripts/lib/__pycache__/schema.cpython-313.pyc +0 -0
  489. package/skills/luckiest-trends/scripts/lib/__pycache__/signals.cpython-313.pyc +0 -0
  490. package/skills/luckiest-trends/scripts/lib/__pycache__/skill_meta.cpython-313.pyc +0 -0
  491. package/skills/luckiest-trends/scripts/lib/__pycache__/snippet.cpython-313.pyc +0 -0
  492. package/skills/luckiest-trends/scripts/lib/__pycache__/subproc.cpython-313.pyc +0 -0
  493. package/skills/luckiest-trends/scripts/lib/__pycache__/techmeme.cpython-313.pyc +0 -0
  494. package/skills/luckiest-trends/scripts/lib/__pycache__/threads.cpython-313.pyc +0 -0
  495. package/skills/luckiest-trends/scripts/lib/__pycache__/tiktok.cpython-313.pyc +0 -0
  496. package/skills/luckiest-trends/scripts/lib/__pycache__/trustpilot.cpython-313.pyc +0 -0
  497. package/skills/luckiest-trends/scripts/lib/__pycache__/truthsocial.cpython-313.pyc +0 -0
  498. package/skills/luckiest-trends/scripts/lib/__pycache__/ui.cpython-313.pyc +0 -0
  499. package/skills/luckiest-trends/scripts/lib/__pycache__/web_search_keyless.cpython-313.pyc +0 -0
  500. package/skills/luckiest-trends/scripts/lib/__pycache__/xai_x.cpython-313.pyc +0 -0
  501. package/skills/luckiest-trends/scripts/lib/__pycache__/xiaohongshu_api.cpython-313.pyc +0 -0
  502. package/skills/luckiest-trends/scripts/lib/__pycache__/xquik.cpython-313.pyc +0 -0
  503. package/skills/luckiest-trends/scripts/lib/__pycache__/xurl_x.cpython-313.pyc +0 -0
  504. package/skills/luckiest-trends/scripts/lib/__pycache__/youtube_yt.cpython-313.pyc +0 -0
  505. package/skills/luckiest-trends/scripts/lib/arxiv.py +290 -0
  506. package/skills/luckiest-trends/scripts/lib/bird_x.py +639 -0
  507. package/skills/luckiest-trends/scripts/lib/bluesky.py +323 -0
  508. package/skills/luckiest-trends/scripts/lib/categories.py +283 -0
  509. package/skills/luckiest-trends/scripts/lib/chrome_cookies.py +414 -0
  510. package/skills/luckiest-trends/scripts/lib/cjk.py +107 -0
  511. package/skills/luckiest-trends/scripts/lib/cluster.py +271 -0
  512. package/skills/luckiest-trends/scripts/lib/competitors.py +198 -0
  513. package/skills/luckiest-trends/scripts/lib/cookie_extract.py +508 -0
  514. package/skills/luckiest-trends/scripts/lib/dates.py +169 -0
  515. package/skills/luckiest-trends/scripts/lib/dedupe.py +145 -0
  516. package/skills/luckiest-trends/scripts/lib/digg.py +440 -0
  517. package/skills/luckiest-trends/scripts/lib/entity_extract.py +127 -0
  518. package/skills/luckiest-trends/scripts/lib/env.py +1073 -0
  519. package/skills/luckiest-trends/scripts/lib/fanout.py +84 -0
  520. package/skills/luckiest-trends/scripts/lib/fusion.py +207 -0
  521. package/skills/luckiest-trends/scripts/lib/github.py +996 -0
  522. package/skills/luckiest-trends/scripts/lib/grounding.py +331 -0
  523. package/skills/luckiest-trends/scripts/lib/hackernews.py +357 -0
  524. package/skills/luckiest-trends/scripts/lib/health.py +92 -0
  525. package/skills/luckiest-trends/scripts/lib/hiring_signals.py +326 -0
  526. package/skills/luckiest-trends/scripts/lib/html_publish.py +73 -0
  527. package/skills/luckiest-trends/scripts/lib/html_render.py +738 -0
  528. package/skills/luckiest-trends/scripts/lib/http.py +372 -0
  529. package/skills/luckiest-trends/scripts/lib/instagram.py +521 -0
  530. package/skills/luckiest-trends/scripts/lib/jobs.py +708 -0
  531. package/skills/luckiest-trends/scripts/lib/linkedin.py +363 -0
  532. package/skills/luckiest-trends/scripts/lib/log.py +35 -0
  533. package/skills/luckiest-trends/scripts/lib/normalize.py +759 -0
  534. package/skills/luckiest-trends/scripts/lib/permission_preflight.py +242 -0
  535. package/skills/luckiest-trends/scripts/lib/perplexity.py +745 -0
  536. package/skills/luckiest-trends/scripts/lib/pinterest.py +154 -0
  537. package/skills/luckiest-trends/scripts/lib/pipeline.py +1720 -0
  538. package/skills/luckiest-trends/scripts/lib/planner.py +763 -0
  539. package/skills/luckiest-trends/scripts/lib/polymarket.py +794 -0
  540. package/skills/luckiest-trends/scripts/lib/preflight.py +119 -0
  541. package/skills/luckiest-trends/scripts/lib/providers.py +380 -0
  542. package/skills/luckiest-trends/scripts/lib/quality_nudge.py +383 -0
  543. package/skills/luckiest-trends/scripts/lib/query.py +202 -0
  544. package/skills/luckiest-trends/scripts/lib/reddit.py +731 -0
  545. package/skills/luckiest-trends/scripts/lib/reddit_arctic.py +92 -0
  546. package/skills/luckiest-trends/scripts/lib/reddit_enrich.py +322 -0
  547. package/skills/luckiest-trends/scripts/lib/reddit_keyless.py +329 -0
  548. package/skills/luckiest-trends/scripts/lib/reddit_listing.py +188 -0
  549. package/skills/luckiest-trends/scripts/lib/reddit_public.py +275 -0
  550. package/skills/luckiest-trends/scripts/lib/reddit_rss.py +224 -0
  551. package/skills/luckiest-trends/scripts/lib/reddit_shreddit.py +184 -0
  552. package/skills/luckiest-trends/scripts/lib/relevance.py +200 -0
  553. package/skills/luckiest-trends/scripts/lib/render.py +2130 -0
  554. package/skills/luckiest-trends/scripts/lib/rerank.py +708 -0
  555. package/skills/luckiest-trends/scripts/lib/resolve.py +345 -0
  556. package/skills/luckiest-trends/scripts/lib/safari_cookies.py +193 -0
  557. package/skills/luckiest-trends/scripts/lib/schema.py +319 -0
  558. package/skills/luckiest-trends/scripts/lib/setup_wizard.py +843 -0
  559. package/skills/luckiest-trends/scripts/lib/signals.py +334 -0
  560. package/skills/luckiest-trends/scripts/lib/skill_meta.py +33 -0
  561. package/skills/luckiest-trends/scripts/lib/snippet.py +51 -0
  562. package/skills/luckiest-trends/scripts/lib/subproc.py +116 -0
  563. package/skills/luckiest-trends/scripts/lib/techmeme.py +239 -0
  564. package/skills/luckiest-trends/scripts/lib/threads.py +207 -0
  565. package/skills/luckiest-trends/scripts/lib/tiktok.py +596 -0
  566. package/skills/luckiest-trends/scripts/lib/transcribe.py +235 -0
  567. package/skills/luckiest-trends/scripts/lib/trustpilot.py +277 -0
  568. package/skills/luckiest-trends/scripts/lib/truthsocial.py +162 -0
  569. package/skills/luckiest-trends/scripts/lib/ui.py +626 -0
  570. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/LICENSE +21 -0
  571. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/bird-search.mjs +147 -0
  572. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/cookies.js +220 -0
  573. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/features.json +17 -0
  574. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/paginate-cursor.js +37 -0
  575. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/query-ids.json +20 -0
  576. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/runtime-features.js +151 -0
  577. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/runtime-query-ids.js +264 -0
  578. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/twitter-client-base.js +129 -0
  579. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/twitter-client-constants.js +50 -0
  580. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/twitter-client-features.js +347 -0
  581. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/twitter-client-search.js +157 -0
  582. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/twitter-client-types.js +2 -0
  583. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/lib/twitter-client-utils.js +511 -0
  584. package/skills/luckiest-trends/scripts/lib/vendor/bird-search/package.json +13 -0
  585. package/skills/luckiest-trends/scripts/lib/web_fetch_keyless.py +105 -0
  586. package/skills/luckiest-trends/scripts/lib/web_search_keyless.py +158 -0
  587. package/skills/luckiest-trends/scripts/lib/xai_x.py +236 -0
  588. package/skills/luckiest-trends/scripts/lib/xiaohongshu_api.py +162 -0
  589. package/skills/luckiest-trends/scripts/lib/xquik.py +404 -0
  590. package/skills/luckiest-trends/scripts/lib/xurl_x.py +171 -0
  591. package/skills/luckiest-trends/scripts/lib/youtube_yt.py +1365 -0
  592. package/skills/luckiest-trends/scripts/setup-keychain.sh +123 -0
  593. package/skills/luckiest-trends/scripts/setup-pass.sh +132 -0
  594. package/skills/luckiest-trends/scripts/store.py +1020 -0
  595. package/skills/luckiest-trends/scripts/test-v1-vs-v2.sh +220 -0
  596. package/skills/luckiest-trends/scripts/test_device_auth.py +128 -0
  597. package/skills/luckiest-trends/scripts/verify_v3.py +195 -0
  598. package/skills/luckiest-trends/scripts/watchlist.py +318 -0
  599. package/skills/luckiest-video/ATTRIBUTION.md +27 -0
  600. package/skills/luckiest-video/CHANGELOG.md +23 -0
  601. package/skills/luckiest-video/SKILL.md +371 -0
  602. package/skills/luckiest-video/evals/evals.json +88 -0
  603. package/skills/luckiest-video/references/ai-video-prompting.md +175 -0
  604. package/skills/remix/SKILL.md +32 -0
  605. package/commands/luckiest/go.md +0 -45
  606. package/commands/luckiest/plan.md +0 -55
  607. /package/commands/{luckiest/charms.md → charms.md} +0 -0
  608. /package/commands/{luckiest/helpers.md → helpers.md} +0 -0
  609. /package/commands/{luckiest/home.md → home.md} +0 -0
  610. /package/commands/{luckiest/leaderboard.md → leaderboard.md} +0 -0
  611. /package/commands/{luckiest/merge.md → merge.md} +0 -0
  612. /package/commands/{luckiest/overlaps.md → overlaps.md} +0 -0
  613. /package/commands/{luckiest/skills.md → skills.md} +0 -0
  614. /package/commands/{luckiest/start.md → start.md} +0 -0
  615. /package/commands/{luckiest/status.md → status.md} +0 -0
  616. /package/commands/{luckiest/updates.md → updates.md} +0 -0
  617. /package/commands/{luckiest/vouch.md → vouch.md} +0 -0
  618. /package/commands/{luckiest/wishes.md → wishes.md} +0 -0
@@ -0,0 +1,596 @@
1
+ """TikTok search via ScrapeCreators API for /last30days.
2
+
3
+ Uses ScrapeCreators REST API to search TikTok by keyword, extract engagement
4
+ metrics (views, likes, comments, shares), and fetch video transcripts.
5
+
6
+ Requires SCRAPECREATORS_API_KEY in config. 100 free API calls, then PAYG.
7
+ API docs: https://scrapecreators.com/docs
8
+ """
9
+
10
+ import re
11
+ import sys
12
+ from typing import Any, Dict, List, Optional, Set
13
+
14
+ from . import dates, http, log
15
+
16
+ SCRAPECREATORS_BASE = "https://api.scrapecreators.com/v1/tiktok"
17
+
18
+ # Depth configurations: how many results to fetch / captions to extract
19
+ DEPTH_CONFIG = {
20
+ "quick": {"results_per_page": 10, "max_captions": 3},
21
+ "default": {"results_per_page": 20, "max_captions": 5},
22
+ "deep": {"results_per_page": 40, "max_captions": 8},
23
+ }
24
+
25
+ # Max words to keep from each caption
26
+ CAPTION_MAX_WORDS = 500
27
+
28
+ from .query import infer_query_intent
29
+ from .relevance import token_overlap_relevance as _compute_relevance
30
+
31
+
32
+ def _extract_core_subject(topic: str) -> str:
33
+ """Extract core subject from verbose query for TikTok search."""
34
+ from .query import VIRAL_NOISE, extract_core_subject
35
+ return extract_core_subject(topic, noise=VIRAL_NOISE)
36
+
37
+
38
+ def expand_tiktok_queries(topic: str, depth: str) -> List[str]:
39
+ """Generate multiple TikTok search queries from a topic.
40
+
41
+ Mirrors reddit.py's expand_reddit_queries() pattern:
42
+ 1. Extract core subject (strip noise words)
43
+ 2. Include original topic if different from core
44
+ 3. Add intent-specific OR-joined content-type variants
45
+ 4. Cap by depth: 1 for quick, 2 for default, 3 for deep
46
+
47
+ Returns 1-3 query strings depending on depth.
48
+ """
49
+ core = _extract_core_subject(topic)
50
+ queries = [core]
51
+
52
+ # Include cleaned original topic as variant if different from core
53
+ original_clean = topic.strip().rstrip('?!.')
54
+ if core.lower() != original_clean.lower() and len(original_clean.split()) <= 8:
55
+ queries.append(original_clean)
56
+
57
+ qtype = infer_query_intent(topic)
58
+
59
+ # Intent-specific TikTok content-type variants
60
+ if qtype in ("breaking_news", "opinion"):
61
+ queries.append(f"{core} edit OR reaction OR trend")
62
+ elif qtype == "product":
63
+ queries.append(f"{core} review OR haul OR unboxing")
64
+ elif qtype == "comparison":
65
+ queries.append(f"{core} vs OR compared OR which is better")
66
+ elif qtype == "how_to":
67
+ queries.append(f"{core} tutorial OR hack OR tip")
68
+ else:
69
+ queries.append(f"{core} edit OR reaction OR trend")
70
+
71
+ # Deep depth: add viral content variant
72
+ if depth == "deep":
73
+ queries.append(f"{core} viral OR fyp OR trending")
74
+
75
+ # Cap by depth budget
76
+ caps = {"quick": 1, "default": 2, "deep": 3}
77
+ cap = caps.get(depth, 2)
78
+ return queries[:cap]
79
+
80
+
81
+ def _log(msg: str):
82
+ log.source_log("TikTok", msg, tty_only=False)
83
+
84
+
85
+ def _parse_date(item: Dict[str, Any]) -> Optional[str]:
86
+ """Parse date from ScrapeCreators TikTok item to YYYY-MM-DD."""
87
+ ts = item.get("create_time")
88
+ if ts:
89
+ try:
90
+ return dates.timestamp_to_date(int(ts))
91
+ except (ValueError, TypeError):
92
+ pass
93
+ return None
94
+
95
+
96
+ def _clean_webvtt(text: str) -> str:
97
+ """Strip WebVTT timestamps and headers from transcript text."""
98
+ if not text:
99
+ return ""
100
+ lines = text.split('\n')
101
+ cleaned = []
102
+ for line in lines:
103
+ line = line.strip()
104
+ if not line:
105
+ continue
106
+ if line.startswith('WEBVTT'):
107
+ continue
108
+ if re.match(r'^\d{2}:\d{2}', line):
109
+ continue
110
+ if '-->' in line:
111
+ continue
112
+ cleaned.append(line)
113
+ return ' '.join(cleaned)
114
+
115
+
116
+ def _parse_items(raw_items: List[Dict[str, Any]], core_topic: str) -> List[Dict[str, Any]]:
117
+ """Parse raw TikTok items into normalized dicts."""
118
+ items = []
119
+ for raw in raw_items:
120
+ video_id = str(raw.get("aweme_id", ""))
121
+ text = raw.get("desc", "")
122
+
123
+ stats = raw.get("statistics") if isinstance(raw.get("statistics"), dict) else {}
124
+ play_count = stats.get("play_count") if stats.get("play_count") is not None else 0
125
+ digg_count = stats.get("digg_count") if stats.get("digg_count") is not None else 0
126
+ comment_count = stats.get("comment_count") if stats.get("comment_count") is not None else 0
127
+ share_count = stats.get("share_count") if stats.get("share_count") is not None else 0
128
+
129
+ author_raw = raw.get("author")
130
+ if isinstance(author_raw, dict):
131
+ author_name = author_raw.get("unique_id", "")
132
+ elif isinstance(author_raw, str):
133
+ author_name = author_raw
134
+ else:
135
+ author_name = ""
136
+
137
+ share_url = raw.get("share_url", "")
138
+ text_extra = raw.get("text_extra") or []
139
+ hashtag_names = [t.get("hashtag_name", "") for t in text_extra
140
+ if isinstance(t, dict) and t.get("hashtag_name")]
141
+
142
+ video_raw = raw.get("video")
143
+ duration = video_raw.get("duration") if isinstance(video_raw, dict) else None
144
+
145
+ date_str = _parse_date(raw)
146
+
147
+ # Compute relevance with hashtag boost
148
+ relevance = _compute_relevance(core_topic, text, hashtag_names)
149
+
150
+ # Build URL: prefer share_url, fallback to constructed URL
151
+ url = share_url.split("?")[0] if share_url else ""
152
+ if not url and author_name and video_id:
153
+ url = f"https://www.tiktok.com/@{author_name}/video/{video_id}"
154
+
155
+ items.append({
156
+ "video_id": video_id,
157
+ "text": text,
158
+ "url": url,
159
+ "author_name": author_name,
160
+ "date": date_str,
161
+ "engagement": {
162
+ "views": play_count,
163
+ "likes": digg_count,
164
+ "comments": comment_count,
165
+ "shares": share_count,
166
+ },
167
+ "hashtags": hashtag_names,
168
+ "duration": duration,
169
+ "relevance": relevance,
170
+ "why_relevant": f"TikTok: {text[:60]}" if text else f"TikTok: {core_topic}",
171
+ "caption_snippet": "", # populated by fetch_captions
172
+ })
173
+ return items
174
+
175
+
176
+ def _hashtag_search(
177
+ hashtag: str,
178
+ token: str,
179
+ ) -> List[Dict[str, Any]]:
180
+ """Search TikTok by hashtag via ScrapeCreators.
181
+
182
+ Args:
183
+ hashtag: Hashtag name (without #)
184
+ token: ScrapeCreators API key
185
+
186
+ Returns:
187
+ List of raw TikTok item dicts (aweme_info format).
188
+ """
189
+ _log(f"Hashtag search: #{hashtag}")
190
+ try:
191
+ data = http.get(
192
+ f"{SCRAPECREATORS_BASE}/search/hashtag",
193
+ params={"hashtag": hashtag},
194
+ headers=http.scrapecreators_headers(token),
195
+ timeout=30,
196
+ retries=2,
197
+ )
198
+ except Exception as e:
199
+ _log(f"Hashtag search error for #{hashtag}: {e}")
200
+ return []
201
+
202
+ raw_items = data.get("aweme_list") or data.get("data") or []
203
+ _log(f" -> {len(raw_items)} results for #{hashtag}")
204
+ return raw_items
205
+
206
+
207
+ def _profile_videos(
208
+ handle: str,
209
+ token: str,
210
+ count: int = 10,
211
+ ) -> List[Dict[str, Any]]:
212
+ """Fetch a TikTok creator's recent videos via ScrapeCreators.
213
+
214
+ Args:
215
+ handle: TikTok username (without @)
216
+ token: ScrapeCreators API key
217
+ count: Max videos to return
218
+
219
+ Returns:
220
+ List of raw TikTok item dicts (aweme_info format).
221
+ """
222
+ _log(f"Profile videos: @{handle}")
223
+ profile_url = "https://api.scrapecreators.com/v3/tiktok/profile/videos"
224
+ try:
225
+ data = http.get(
226
+ profile_url,
227
+ params={"handle": handle, "sort_by": "latest"},
228
+ headers=http.scrapecreators_headers(token),
229
+ timeout=30,
230
+ retries=2,
231
+ )
232
+ except Exception as e:
233
+ _log(f"Profile videos error for @{handle}: {e}")
234
+ return []
235
+
236
+ raw_items = data.get("aweme_list") or data.get("data") or []
237
+ _log(f" -> {len(raw_items)} videos from @{handle}")
238
+ return raw_items[:count]
239
+
240
+
241
+ def search_tiktok(
242
+ topic: str,
243
+ from_date: str,
244
+ to_date: str,
245
+ depth: str = "default",
246
+ token: str = None,
247
+ ) -> Dict[str, Any]:
248
+ """Search TikTok via ScrapeCreators API.
249
+
250
+ Args:
251
+ topic: Search topic
252
+ from_date: Start date (YYYY-MM-DD)
253
+ to_date: End date (YYYY-MM-DD)
254
+ depth: 'quick', 'default', or 'deep'
255
+ token: ScrapeCreators API key
256
+
257
+ Returns:
258
+ Dict with 'items' list and optional 'error'.
259
+ """
260
+ if not token:
261
+ return {"items": [], "error": "No SCRAPECREATORS_API_KEY configured"}
262
+
263
+ config = DEPTH_CONFIG.get(depth, DEPTH_CONFIG["default"])
264
+ core_topic = _extract_core_subject(topic)
265
+
266
+ _log(f"Searching TikTok for '{core_topic}' (depth={depth}, count={config['results_per_page']})")
267
+
268
+ try:
269
+ data = http.get(
270
+ f"{SCRAPECREATORS_BASE}/search/keyword",
271
+ params={"query": core_topic, "sort_by": "relevance"},
272
+ headers=http.scrapecreators_headers(token),
273
+ timeout=30,
274
+ retries=2,
275
+ )
276
+ except Exception as e:
277
+ _log(f"ScrapeCreators error: {e}")
278
+ return {"items": [], "error": f"{type(e).__name__}: {e}"}
279
+
280
+ # Items are nested under aweme_info
281
+ raw_entries = data.get("search_item_list") or data.get("data") or []
282
+ raw_items = []
283
+ for entry in raw_entries:
284
+ if isinstance(entry, dict):
285
+ info = entry.get("aweme_info", entry)
286
+ raw_items.append(info)
287
+
288
+ # Limit to configured count
289
+ raw_items = raw_items[:config["results_per_page"]]
290
+
291
+ # Parse items
292
+ items = _parse_items(raw_items, core_topic)
293
+
294
+ # Hard date filter
295
+ in_range = [i for i in items if i["date"] and from_date <= i["date"] <= to_date]
296
+ out_of_range = len(items) - len(in_range)
297
+ if in_range:
298
+ items = in_range
299
+ if out_of_range:
300
+ _log(f"Filtered {out_of_range} videos outside date range")
301
+ else:
302
+ _log(f"No videos within date range, keeping all {len(items)}")
303
+
304
+ # Sort by views descending
305
+ items.sort(key=lambda x: x["engagement"]["views"], reverse=True)
306
+
307
+ _log(f"Found {len(items)} TikTok videos")
308
+ return {"items": items}
309
+
310
+
311
+ def fetch_captions(
312
+ video_items: List[Dict[str, Any]],
313
+ token: str,
314
+ depth: str = "default",
315
+ ) -> Dict[str, str]:
316
+ """Fetch transcripts for top N TikTok videos via ScrapeCreators.
317
+
318
+ Strategy:
319
+ 1. Use the 'text' field (video description) as baseline caption
320
+ 2. For top N, call /video/transcript for spoken-word captions
321
+
322
+ Args:
323
+ video_items: Items from search_tiktok()
324
+ token: ScrapeCreators API key
325
+ depth: Depth level for caption limit
326
+
327
+ Returns:
328
+ Dict mapping video_id -> caption text (truncated to 500 words)
329
+ """
330
+ config = DEPTH_CONFIG.get(depth, DEPTH_CONFIG["default"])
331
+ max_captions = config["max_captions"]
332
+
333
+ if not video_items or not token:
334
+ return {}
335
+
336
+ top_items = video_items[:max_captions]
337
+ _log(f"Enriching captions for {len(top_items)} videos")
338
+
339
+ captions = {}
340
+
341
+ # First pass: use text field as caption (always available, free)
342
+ for item in top_items:
343
+ vid = item["video_id"]
344
+ text = item.get("text", "")
345
+ if text:
346
+ words = text.split()
347
+ if len(words) > CAPTION_MAX_WORDS:
348
+ text = ' '.join(words[:CAPTION_MAX_WORDS]) + '...'
349
+ captions[vid] = text
350
+
351
+ # Second pass: try to get spoken-word transcripts (1 credit each)
352
+ for item in top_items:
353
+ vid = item["video_id"]
354
+ url = item.get("url", "")
355
+ if not url:
356
+ continue
357
+ try:
358
+ data = http.get(
359
+ f"{SCRAPECREATORS_BASE}/video/transcript",
360
+ params={"url": url},
361
+ headers=http.scrapecreators_headers(token),
362
+ timeout=15,
363
+ retries=1,
364
+ )
365
+ transcript = data.get("transcript")
366
+ if transcript:
367
+ if isinstance(transcript, list):
368
+ transcript = " ".join(str(s) for s in transcript)
369
+ transcript = _clean_webvtt(transcript)
370
+ if transcript:
371
+ words = transcript.split()
372
+ if len(words) > CAPTION_MAX_WORDS:
373
+ transcript = ' '.join(words[:CAPTION_MAX_WORDS]) + '...'
374
+ captions[vid] = transcript
375
+ except Exception as e:
376
+ _log(f"Transcript fetch failed for {vid}: {e}")
377
+
378
+ got = sum(1 for v in captions.values() if v)
379
+ _log(f"Got captions for {got}/{len(top_items)} videos")
380
+ return captions
381
+
382
+
383
+ def search_and_enrich(
384
+ topic: str,
385
+ from_date: str,
386
+ to_date: str,
387
+ depth: str = "default",
388
+ token: str = None,
389
+ hashtags: List[str] | None = None,
390
+ creators: List[str] | None = None,
391
+ ) -> Dict[str, Any]:
392
+ """Full TikTok search: find videos, then fetch captions for top results.
393
+
394
+ Uses expand_tiktok_queries() to generate multiple search queries,
395
+ runs ScrapeCreators for each, and merges/deduplicates results by video ID.
396
+
397
+ Args:
398
+ topic: Search topic (raw topic, not planner's narrowed query)
399
+ from_date: Start date (YYYY-MM-DD)
400
+ to_date: End date (YYYY-MM-DD)
401
+ depth: 'quick', 'default', or 'deep'
402
+ token: ScrapeCreators API key
403
+ hashtags: Optional list of TikTok hashtags to search (without #)
404
+ creators: Optional list of TikTok creator handles to fetch videos from
405
+
406
+ Returns:
407
+ Dict with 'items' list. Each item has a 'caption_snippet' field.
408
+ """
409
+ core_topic = _extract_core_subject(topic)
410
+ seen_ids: Set[str] = set()
411
+ items: List[Dict[str, Any]] = []
412
+ last_error = None
413
+
414
+ # Step 0a: Hashtag search (high-signal, runs first)
415
+ if hashtags and token:
416
+ for hashtag in hashtags:
417
+ raw_items = _hashtag_search(hashtag, token)
418
+ parsed = _parse_items(raw_items, core_topic)
419
+ for item in parsed:
420
+ vid = item.get("video_id", "")
421
+ if vid and vid not in seen_ids:
422
+ seen_ids.add(vid)
423
+ items.append(item)
424
+
425
+ # Step 0b: Creator profile videos (high-signal)
426
+ if creators and token:
427
+ for creator in creators:
428
+ raw_items = _profile_videos(creator, token)
429
+ parsed = _parse_items(raw_items, core_topic)
430
+ for item in parsed:
431
+ vid = item.get("video_id", "")
432
+ if vid and vid not in seen_ids:
433
+ seen_ids.add(vid)
434
+ items.append(item)
435
+
436
+ # Step 1: Multi-query keyword search — run ScrapeCreators for each expanded query
437
+ queries = expand_tiktok_queries(topic, depth)
438
+ for q in queries:
439
+ search_result = search_tiktok(q, from_date, to_date, depth, token)
440
+ if search_result.get("error"):
441
+ last_error = search_result["error"]
442
+ for item in search_result.get("items", []):
443
+ vid = item.get("video_id", "")
444
+ if vid and vid not in seen_ids:
445
+ seen_ids.add(vid)
446
+ items.append(item)
447
+
448
+ # Sort merged results by views descending
449
+ items.sort(key=lambda x: x.get("engagement", {}).get("views", 0), reverse=True)
450
+
451
+ if not items:
452
+ return {"items": [], "error": last_error}
453
+
454
+ # Step 2: Fetch captions for top N
455
+ captions = fetch_captions(items, token, depth)
456
+
457
+ # Step 3: Attach captions to items
458
+ for item in items:
459
+ vid = item["video_id"]
460
+ caption = captions.get(vid)
461
+ if caption:
462
+ item["caption_snippet"] = caption
463
+
464
+ return {"items": items, "error": last_error}
465
+
466
+
467
+ def parse_tiktok_response(response: Dict[str, Any]) -> List[Dict[str, Any]]:
468
+ """Parse TikTok search response to normalized format.
469
+
470
+ Returns:
471
+ List of item dicts ready for normalization.
472
+ """
473
+ return response.get("items", [])
474
+
475
+
476
+ def _tiktok_total_engagement(item: Dict[str, Any]) -> int:
477
+ """Total engagement for ranking which posts deserve comment enrichment."""
478
+ eng = item.get("engagement", {})
479
+ return (eng.get("views", 0) or 0) + (eng.get("likes", 0) or 0) + (eng.get("comments", 0) or 0)
480
+
481
+
482
+ def enrich_with_comments(
483
+ items: List[Dict[str, Any]],
484
+ token: str,
485
+ max_posts: int = 3,
486
+ max_comments: int = 5,
487
+ ) -> List[Dict[str, Any]]:
488
+ """Enrich top TikTok posts with comment data from ScrapeCreators.
489
+
490
+ For the top N posts by engagement, fetches comments via the SC API
491
+ and attaches them as a ``top_comments`` field on each item. Mirrors
492
+ youtube_yt.enrich_with_comments.
493
+
494
+ Args:
495
+ items: TikTok items from search_tiktok()
496
+ token: ScrapeCreators API key
497
+ max_posts: How many posts to enrich with comments
498
+ max_comments: Max comments to keep per post
499
+
500
+ Returns:
501
+ Items list (mutated in place) with top_comments added to enriched items.
502
+ """
503
+ if not items or not token or max_posts <= 0:
504
+ return items
505
+
506
+ ranked = sorted(items, key=_tiktok_total_engagement, reverse=True)
507
+ top_items = ranked[:max_posts]
508
+ _log(f"Enriching comments for {len(top_items)} TikTok posts")
509
+
510
+ from concurrent.futures import ThreadPoolExecutor, as_completed
511
+
512
+ def _enrich_one(item: dict) -> bool:
513
+ post_url = item.get("url", "")
514
+ if not post_url:
515
+ return False
516
+ try:
517
+ comments = _fetch_post_comments(post_url, token, max_comments)
518
+ if comments:
519
+ item["top_comments"] = comments
520
+ return True
521
+ except Exception as exc:
522
+ _log(f"Comment enrichment failed for {post_url}: {exc}")
523
+ return False
524
+
525
+ enriched_count = 0
526
+ with ThreadPoolExecutor(max_workers=min(4, len(top_items))) as executor:
527
+ futures = {executor.submit(_enrich_one, item): item for item in top_items}
528
+ for future in as_completed(futures):
529
+ if future.result():
530
+ enriched_count += 1
531
+
532
+ _log(f"Enriched {enriched_count}/{len(top_items)} posts with comments")
533
+ return items
534
+
535
+
536
+ def _fetch_post_comments(
537
+ post_url: str,
538
+ token: str,
539
+ max_comments: int = 5,
540
+ ) -> List[Dict[str, Any]]:
541
+ """Fetch comments for a single TikTok post via ScrapeCreators.
542
+
543
+ SC endpoint: GET /v1/tiktok/video/comments?url=<video_url>
544
+ Response shape: { comments: [{text, user.nickname, digg_count, create_time, ...}], cursor, total }
545
+
546
+ Args:
547
+ post_url: Canonical TikTok post URL (share_url form works)
548
+ token: ScrapeCreators API key
549
+ max_comments: Maximum comments to return
550
+
551
+ Returns:
552
+ List of comment dicts with author, text, digg_count (likes), date.
553
+ Empty list on any error — comment failures never crash the pipeline.
554
+ """
555
+ try:
556
+ data = http.get(
557
+ f"{SCRAPECREATORS_BASE}/video/comments",
558
+ params={"url": post_url, "trim": "true"},
559
+ headers=http.scrapecreators_headers(token),
560
+ timeout=30,
561
+ retries=2,
562
+ )
563
+ except Exception as exc:
564
+ _log(f"Comment fetch error for {post_url}: {exc}")
565
+ return []
566
+
567
+ raw_comments = data.get("comments") or data.get("data") or []
568
+ # Sort by digg_count desc so normalize sees the highest-signal first.
569
+ raw_comments = sorted(
570
+ raw_comments,
571
+ key=lambda c: c.get("digg_count", 0) or 0,
572
+ reverse=True,
573
+ )
574
+ out: List[Dict[str, Any]] = []
575
+ for c in raw_comments[:max_comments]:
576
+ text = c.get("text") or ""
577
+ if not text:
578
+ continue
579
+ user = c.get("user") if isinstance(c.get("user"), dict) else {}
580
+ # Prefer unique_id (the @handle) over nickname (display name) so
581
+ # downstream render can cite @handle consistently across platforms.
582
+ author = user.get("unique_id") or user.get("nickname") or ""
583
+ create_time = c.get("create_time")
584
+ date_str = ""
585
+ if create_time:
586
+ try:
587
+ date_str = dates.timestamp_to_date(int(create_time)) or ""
588
+ except (ValueError, TypeError):
589
+ date_str = ""
590
+ out.append({
591
+ "author": author,
592
+ "text": text[:400],
593
+ "digg_count": c.get("digg_count", 0) or 0,
594
+ "date": date_str,
595
+ })
596
+ return out