luckiest-co 1.0.39 → 1.0.40

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 (141) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/package.json +1 -1
  4. package/skills/ad-creative/CHANGELOG.md +7 -0
  5. package/skills/ad-creative/SKILL.md +23 -4
  6. package/skills/ad-creative/references/generative-tools.md +6 -30
  7. package/skills/ads/CHANGELOG.md +9 -0
  8. package/skills/ads/SKILL.md +19 -9
  9. package/skills/ads/references/reading-google-ads-data.md +121 -0
  10. package/skills/ai-seo/CHANGELOG.md +11 -0
  11. package/skills/ai-seo/SKILL.md +21 -22
  12. package/skills/ai-seo/references/content-types.md +11 -0
  13. package/skills/ai-seo/references/linkedin-ai-citations.md +122 -0
  14. package/skills/ai-seo/references/platform-ranking-factors.md +32 -19
  15. package/skills/ai-seo/references/positioning-and-consensus.md +102 -0
  16. package/skills/coder/CHANGELOG.md +23 -0
  17. package/skills/coder/SKILL.md +16 -4
  18. package/skills/coder-brainstorming/CHANGELOG.md +9 -0
  19. package/skills/coder-brainstorming/SKILL.md +5 -3
  20. package/skills/coder-debugging/CHANGELOG.md +11 -0
  21. package/skills/coder-debugging/SKILL.md +15 -3
  22. package/skills/coder-executing-plans/CHANGELOG.md +9 -0
  23. package/skills/coder-executing-plans/SKILL.md +5 -3
  24. package/skills/coder-finishing-a-branch/CHANGELOG.md +9 -0
  25. package/skills/coder-finishing-a-branch/SKILL.md +7 -3
  26. package/skills/coder-guard/CHANGELOG.md +7 -0
  27. package/skills/coder-guard/SKILL.md +6 -6
  28. package/skills/coder-receiving-code-review/CHANGELOG.md +9 -0
  29. package/skills/coder-receiving-code-review/SKILL.md +5 -3
  30. package/skills/coder-shipping/CHANGELOG.md +9 -0
  31. package/skills/coder-shipping/SKILL.md +4 -4
  32. package/skills/coder-subagent-driven-development/CHANGELOG.md +9 -0
  33. package/skills/coder-subagent-driven-development/SKILL.md +5 -3
  34. package/skills/coder-writing-plans/CHANGELOG.md +9 -0
  35. package/skills/coder-writing-plans/SKILL.md +5 -3
  36. package/skills/cold-email/CHANGELOG.md +7 -0
  37. package/skills/cold-email/SKILL.md +15 -8
  38. package/skills/cold-email/references/follow-up-sequences.md +4 -4
  39. package/skills/cold-email/references/frameworks.md +5 -5
  40. package/skills/cold-email/references/personalization.md +10 -10
  41. package/skills/competitor-profiling/CHANGELOG.md +8 -0
  42. package/skills/competitor-profiling/SKILL.md +34 -9
  43. package/skills/competitor-profiling/references/tool-reference.md +141 -5
  44. package/skills/competitors/CHANGELOG.md +8 -0
  45. package/skills/competitors/SKILL.md +32 -5
  46. package/skills/competitors/references/content-architecture.md +6 -0
  47. package/skills/competitors/references/templates.md +3 -0
  48. package/skills/copy-editing/CHANGELOG.md +10 -0
  49. package/skills/copy-editing/SKILL.md +41 -9
  50. package/skills/copy-editing/references/checklist.md +11 -0
  51. package/skills/copywriting/CHANGELOG.md +11 -0
  52. package/skills/copywriting/SKILL.md +32 -4
  53. package/skills/copywriting/references/copy-frameworks.md +2 -3
  54. package/skills/copywriting/references/natural-transitions.md +4 -20
  55. package/skills/customer-research/CHANGELOG.md +8 -0
  56. package/skills/customer-research/SKILL.md +6 -3
  57. package/skills/directory-submissions/CHANGELOG.md +9 -0
  58. package/skills/directory-submissions/SKILL.md +21 -10
  59. package/skills/emails/CHANGELOG.md +8 -0
  60. package/skills/emails/SKILL.md +22 -3
  61. package/skills/emails/references/sequence-templates.md +3 -3
  62. package/skills/image/CHANGELOG.md +8 -0
  63. package/skills/image/SKILL.md +4 -4
  64. package/skills/image/references/ai-image-prompting.md +1 -1
  65. package/skills/marketing-plan/CHANGELOG.md +8 -0
  66. package/skills/marketing-plan/SKILL.md +8 -4
  67. package/skills/marketing-plan/references/market-sizing.md +166 -0
  68. package/skills/marketing-plan/references/methodology.md +2 -0
  69. package/skills/model-router/CHANGELOG.md +9 -0
  70. package/skills/model-router/SKILL.md +3 -3
  71. package/skills/model-router/references/weave-routing-notes.md +1 -0
  72. package/skills/model-router/scripts/route.py +3 -1
  73. package/skills/prospecting/CHANGELOG.md +7 -0
  74. package/skills/prospecting/SKILL.md +3 -3
  75. package/skills/prospecting/references/saas-prospecting.md +1 -1
  76. package/skills/public-relations/CHANGELOG.md +10 -0
  77. package/skills/public-relations/SKILL.md +16 -4
  78. package/skills/public-relations/references/crisis-communications.md +111 -0
  79. package/skills/public-relations/references/newsjacking.md +2 -0
  80. package/skills/public-relations/references/statement-templates.md +143 -0
  81. package/skills/sales-enablement/CHANGELOG.md +8 -0
  82. package/skills/sales-enablement/SKILL.md +72 -4
  83. package/skills/sales-enablement/references/win-loss-analysis.md +72 -0
  84. package/skills/skill-audit/ATTRIBUTION.md +5 -0
  85. package/skills/skill-audit/CHANGELOG.md +19 -0
  86. package/skills/skill-audit/SKILL.md +121 -0
  87. package/skills/skill-audit-security/ATTRIBUTION.md +14 -0
  88. package/skills/skill-audit-security/CHANGELOG.md +14 -0
  89. package/skills/skill-audit-security/LICENSE-APACHE-2.0 +190 -0
  90. package/skills/skill-audit-security/SKILL.md +99 -0
  91. package/skills/skill-audit-security/references/rule-ids.md +28 -0
  92. package/skills/skill-audit-security/references/source-review.md +58 -0
  93. package/skills/sms/CHANGELOG.md +8 -0
  94. package/skills/sms/SKILL.md +18 -4
  95. package/skills/sms/references/whatsapp.md +258 -0
  96. package/skills/social/CHANGELOG.md +11 -0
  97. package/skills/social/SKILL.md +31 -7
  98. package/skills/social/references/listening.md +2 -0
  99. package/skills/social/references/platforms.md +15 -9
  100. package/skills/social/references/post-templates.md +9 -9
  101. package/skills/social/references/short-form-video.md +5 -5
  102. package/skills/social/references/x-algorithm.md +69 -0
  103. package/skills/trends/CHANGELOG.md +3 -0
  104. package/skills/trends/SKILL.md +14 -4
  105. package/skills/video/CHANGELOG.md +8 -0
  106. package/skills/video/SKILL.md +11 -25
  107. package/skills/video-studio/CHANGELOG.md +16 -0
  108. package/skills/video-studio/SKILL.md +86 -22
  109. package/skills/video-studio/references/interview.md +75 -0
  110. package/skills/video-studio/references/step-2-plan.md +25 -1
  111. package/skills/video-studio/references/storyboard.md +3 -1
  112. package/skills/video-studio/scripts/pyproject.toml +4 -2
  113. package/skills/video-studio/scripts/render-scenes.mjs +1 -0
  114. package/skills/video-studio/scripts/storyboard.schema.json +3 -1
  115. package/skills/video-studio/scripts/uv.lock +8 -5
  116. package/skills/video-studio-director/CHANGELOG.md +7 -0
  117. package/skills/video-studio-director/SKILL.md +19 -6
  118. package/skills/video-studio-review/CHANGELOG.md +11 -0
  119. package/skills/video-studio-review/SKILL.md +31 -5
  120. package/skills/video-studio-review/editor/index.html +21 -3
  121. package/skills/video-studio-review/references/apply-feedback.md +19 -0
  122. package/skills/video-studio-review/scripts/build-reel.mjs +35 -10
  123. package/skills/video-studio-review/scripts/build-reel.test.mjs +15 -0
  124. package/skills/video-studio-script/CHANGELOG.md +5 -0
  125. package/skills/video-studio-script/SKILL.md +16 -7
  126. package/skills/video-studio-showreel/CHANGELOG.md +21 -0
  127. package/skills/video-studio-showreel/SKILL.md +231 -0
  128. package/skills/video-studio-showreel/references/motion-design-pack-notes.md +28 -0
  129. package/skills/video-studio-showreel/references/zingage-breakdown.md +47 -0
  130. package/skills/video-studio-showreel/scripts/build.py +10 -0
  131. package/skills/video-studio-showreel/templates/showreel.html +264 -0
  132. package/skills/video-watcher/CHANGELOG.md +6 -0
  133. package/skills/video-watcher/SKILL.md +3 -3
  134. package/skills/video-watcher/scripts/setup.py +9 -7
  135. package/skills/trends/agents/openai.yaml +0 -8
  136. package/skills/trends/scripts/build-skill.sh +0 -39
  137. package/skills/trends/scripts/compare.sh +0 -61
  138. package/skills/trends/scripts/evaluate_search_quality.py +0 -573
  139. package/skills/trends/scripts/test-v1-vs-v2.sh +0 -220
  140. package/skills/trends/scripts/test_device_auth.py +0 -128
  141. package/skills/trends/scripts/verify_v3.py +0 -195
@@ -0,0 +1,28 @@
1
+ # Paper-cut motion design pack: what showreel takes
2
+
3
+ Source: an open-source "Motion Design, Paper-Cut (Prompt Mode)" prompt pack
4
+ (10 files, no license file), studied 2026-10-10. It writes three prompts in a
5
+ row (paper-cut still, 9-panel assembly sheet, image-to-video animation) for the
6
+ user to run in their own image and video models. Showreel builds HTML, so it
7
+ takes the method, not the prompts. Nothing here is copied word for word.
8
+
9
+ | # | Pack item | Verdict | Lands in showreel | Why |
10
+ |---|---|---|---|---|
11
+ | 1 | Depth-sorted assembly: sort every element back to front, enter in that order (backdrop, ground, set pieces, crowd, props, hero last) | Adopt | Grammar rule 11, Mark born and Exploded UI | Gives every build a readable order; the hero arriving last is the payoff |
12
+ | 2 | Hero built part by part, base up, each part with a named entry direction | Adopt | Grammar rule 11, slab and wordmark builds | Turns "it appears" into a sequence the eye can follow; direction per part avoids everything flying from one side |
13
+ | 3 | Details, numbers, and logos enter as their own small pieces after their parent | Adopt | Exploded UI (counters, chips) | Matches our slab rule; the number lands after the card, not with it |
14
+ | 4 | Material verb list: only verbs the material can do (slide, drop, tumble, tip, swing, snap); banned verbs (morph, fold, bend, smooth glide) | Adopt | Grammar rule 12, STORYBOARD.md | One shared list stops mixed physics; for the default look the verbs are camera and slab moves |
15
+ | 5 | One cause and effect final beat, ending in a small scatter payoff (kick, ball, net, flecks) | Adopt | Bookend | Our bookend lands the motif; a short chain into it gives the last hit a reason |
16
+ | 6 | No-action default: everything settles and locks in with tiny staggered adjustments | Adopt | Bookend fallback | Safe ending when there is no product action worth showing |
17
+ | 7 | Locked style, negative, and sound blocks repeated unchanged in every prompt | Adopt | Step 2, STORYBOARD.md header | One locked look line and one "never" line keep chapters consistent across edits and sessions |
18
+ | 8 | Paper-only foley: a sound per arrival (slide, tap, drop, shuffle), no extra sound | Adopt (adapted) | Step 2 beat sheet, SFX anchors | Our reels have music; take the rule "every arrival owns a short foley hit" on top of the bed |
19
+ | 9 | 9-panel assembly sheet before animating: panel 1 near empty, last panel the finished frame, no text in panels | Adopt | Step 2, as a still contact sheet from the build | Cheap check of the order before a full render; we render 9 stills from the composition instead of generating them |
20
+ | 10 | Paper-cut material: torn edges, fibre grain, hard shadow under each piece, thin white slivers between pieces, muted color | Adopt as optional look | Template `data-look="papercut"` | A second look for brands that want handmade over glossy; reuses the cards paper-collage pack |
21
+ | 11 | Stop-motion timing: tiny misalignments, staggered starts | Adopt as optional look | Template: time stepped at 12 fps, seeded jitter | Reads handmade; stepping time keeps every frame a pure function of time |
22
+ | 12 | Never name real public figures or famous artworks; describe them generically | Adopt | Guards | Same rule we need for any prompt sent to an image model |
23
+ | 13 | Start silent, one question at a time, each prompt in its own copyable code block | Adopt (light) | Step 1 brief | Fits our coordinator flow; only for the image prompts we hand the user |
24
+ | 14 | Source quality: finished, poster-like source images animate cleanest | Adopt | Step 1 brief | Same holds for UI captures: clean, uncluttered hero screens |
25
+ | 15 | Fixed model picks per step (GPT Image 2.0, Seedance 2.0), 10 s at 720p | Skip | none | Showreel renders HTML locally; no generation step |
26
+ | 16 | Drop-folder flow (`reference/` dir, `ls -t` to find the newest file) | Skip | none | The coordinator already owns run-dir assets |
27
+ | 17 | Bundled style reference image | Skip | none | We use the brand's own tokens, not a fixed style image |
28
+ | 18 | Emoji-labelled hand-off briefs | Skip | none | Against our copy rules |
@@ -0,0 +1,47 @@
1
+ # Zingage showreel 1.0: breakdown
2
+
3
+ Source: whatships.com/videos/zingage (Blissful 2026 showreel series), 84.4 s,
4
+ 1920x1080, 30 fps. Studied from 2 fps contact sheets on 2026-09-30. Structure
5
+ and motion only; none of its footage, logo, or copy is reused.
6
+
7
+ **Structure:** mark born, wordmark, dive into the site, scroll tour, orange field
8
+ with exploded UI cards, devices and cursor, behind-the-scenes, headline,
9
+ culture board, people masked into the mark, kinetic line, bookend. 2 hard cuts
10
+ (14.7 s, 50.0 s); everything else is a continuous move.
11
+
12
+ | Time | Field | What happens | Transition out |
13
+ |---|---|---|---|
14
+ | 0-1.5 | white | Glossy orange orb with the mark, soft glow, dot-grid halo | Orb bursts into radial petals |
15
+ | 1.5-2.5 | orange | Petals become a tile mosaic, mark in a white center tile | Tiles slide into vertical bands |
16
+ | 2.5-3.5 | white/orange | Bands part, mark alone | Mark becomes dashed outline on construction guides |
17
+ | 3.5-5 | cream | Wordmark drawn as dashed outline, fills left to right in orange | Guides fade out under the fill |
18
+ | 5-7 | cream | Flat wordmark holds | Wordmark extrudes to 3D |
19
+ | 7-9 | cream | 3D wordmark tilts, orange site header rises under it | Camera dives into the nav |
20
+ | 9-12 | cream | Tilted nav, shallow focus: "AI Agents", "Platform", banner | Dolly right |
21
+ | 12-15 | cream | Log In and Book a Demo as thick keycaps, then the Get AI Call button | Cut (14.7) to the headline, still tilted |
22
+ | 15-19 | white | Headline plane levels out, phone mockup rises, rack focus | Pull back to the flat full page |
23
+ | 19-30 | white | Slow scroll of the real site: phone, stat callouts, logo strip, agent cards | Page tilts away |
24
+ | 30-33 | white | Testimonial and stats | Orange slab sweeps in from the bottom |
25
+ | 33-36 | orange | A risk-level card lifts off the page as a thick slab, bars fill | Slab returns into the page card |
26
+ | 36-42 | orange | Page card floats; compliance stats card lifts out, counters tick | Card sinks back |
27
+ | 42-46 | orange | "AI Agent / Case Active" pill slab, progress fills, becomes the phone UI | Phone rotates in 3D |
28
+ | 46-49 | orange | Phone with stacked notification slabs cascading | Cut (50.0) |
29
+ | 49-54 | cream | Desktop monitor dashboard; name-tagged cursor clicks | Push in |
30
+ | 54-63 | dark | After Effects UI: the edit itself, keyframes visible | Pull out to monitor |
31
+ | 63-66 | cream | Monitor dashboards | Monitor slides away |
32
+ | 66-68 | white | "Keep Grandma out of the Hospital." with an orange period, CTAs | Type pushes off |
33
+ | 68-72 | white | Cork board: photos, stickers, post-its pin on in sequence | Board shrinks |
34
+ | 72-75 | cream | Photos stack in a column, explode into a 3x2 grid | Grid collapses into the mark |
35
+ | 75-80 | white | Diagonal orange slab with a photo masked inside; "Build a smarter system for *home care*", "Focus on what truly drives success: *your people*" | Slab becomes the mark |
36
+ | 80-84 | orange | Photo-masked mark in a white tile, mark goes solid, mosaic, orb | Matches frame 0 |
37
+
38
+ **Pacing:** dense at both ends (a transform every 0.5-1 s), slow in the middle
39
+ (UI tour holds 10 s), second burst in the orange chapter.
40
+
41
+ **Signature moves to keep:** orb to mosaic burst; blueprint dashed draw-on;
42
+ 3D-extruded UI with depth of field; close to wide reveal; slabs lifting off a
43
+ page; photos masked inside the mark; italic accent word in brand color; loop
44
+ bookend.
45
+
46
+ **Do not copy:** the footage, the Zingage mark and wordmark, its copy, its
47
+ photos, its music.
@@ -0,0 +1,10 @@
1
+ # Inject the real Luckiest SVG paths into the showreel source. Run: python3 build.py
2
+ import re
3
+ wm=open('assets/logos/luckiest-wordmark.svg').read()
4
+ paths=''.join(re.findall(r'<path class="st[23]"[^>]*/>',wm)) # letters only; the coral i-dot is #idot
5
+ mark=open('assets/logos/luckiest-mark.svg').read()
6
+ glyph=''.join(re.findall(r'<path class="st[13]" d="[^"]*"/>',mark))
7
+ svg=lambda fill: f'<svg viewBox="6.5 4.5 12 13.5">{glyph.replace("class=\"st1\"","").replace("class=\"st3\"","")}</svg>'.replace('<path ',f'<path fill="{fill}" ')
8
+ src=open('../showreel/showreel.src.html').read() # run from <run-dir>/composition
9
+ out=src.replace('%%WM_PATHS%%',re.sub(r' class="st[23]"','',paths)).replace('%%MARK_WHITE%%',svg('#fff')).replace('%%MARK_INK%%',svg('#0A0A0A'))
10
+ open('index.html','w').write(out); print('ok', len(paths), len(glyph))
@@ -0,0 +1,264 @@
1
+ <!doctype html>
2
+ <html lang="en" data-resolution="landscape" data-look="default"><!-- data-look="papercut": handmade torn-paper stop-motion look -->
3
+ <head>
4
+ <!-- showreel recipe (luckiest-video-studio-showreel), Luckiest edition.
5
+ One motif: the coral dot (the i-dot of the wordmark, the period of every headline).
6
+ Chain: orb > mosaic center tile > i-dot > hero period > coral field > photo circle > i-dot.
7
+ Every time is B(n) beats at 120 BPM. Built by build.py from this source (injects the wordmark SVG). -->
8
+ <meta charset="UTF-8" />
9
+ <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
10
+ <style>
11
+ @font-face{font-family:'DM Serif Display';font-style:normal;font-weight:400;font-display:block;src:url(assets/fonts/DMSerifDisplay-normal-400.woff2) format('woff2')}
12
+ @font-face{font-family:'DM Serif Display';font-style:italic;font-weight:400;font-display:block;src:url(assets/fonts/DMSerifDisplay-italic-400.woff2) format('woff2')}
13
+ @font-face{font-family:'Inter';font-style:normal;font-weight:400 600;font-display:block;src:url(assets/fonts/Inter-normal-500.woff2) format('woff2')}
14
+ :root{--paper:#FFFFFF;--ink:#0A0A0A;--muted:#737373;--c1:#FF532C;--c2:#FF3D59;--edge:#E5E5E5;
15
+ --coral:linear-gradient(90deg,#FF532C,#FF3D59)}
16
+ *{margin:0;padding:0;box-sizing:border-box}
17
+ html,body{width:1920px;height:1080px;overflow:hidden;background:var(--paper)}
18
+ #root{position:relative;width:100%;height:100%;overflow:hidden;background:var(--paper);font-family:Inter,system-ui,sans-serif;color:var(--ink)}
19
+ .layer{position:absolute;inset:0}
20
+ .serif{font-family:'DM Serif Display',serif;font-weight:400}
21
+ .caps{font:600 18px/1 Inter,sans-serif;letter-spacing:.22em;text-transform:uppercase;color:var(--muted)}
22
+
23
+ /* A: orb */
24
+ #orb{position:absolute;left:850px;top:430px;width:220px;height:220px;border-radius:50%;
25
+ background:radial-gradient(circle at 34% 28%,#fff 0 5%,#FFB0A0 11%,#FF6A4A 28%,#FF3D59 62%,#C9203D 100%);
26
+ box-shadow:0 0 90px 10px rgba(255,61,89,.35),inset -14px -18px 40px rgba(120,0,30,.35)}
27
+ #orb svg{position:absolute;left:55px;top:55px;width:110px;height:110px}
28
+ #halo{position:absolute;left:660px;top:240px;width:600px;height:600px;border-radius:50%;
29
+ background:radial-gradient(circle,rgba(255,61,89,.16) 1.5px,transparent 2px) 0 0/18px 18px;
30
+ -webkit-mask:radial-gradient(circle,#000 30%,transparent 70%);mask:radial-gradient(circle,#000 30%,transparent 70%)}
31
+
32
+ /* B: mosaic, guides, wordmark */
33
+ #mosaic i{position:absolute;width:120px;height:120px}
34
+ #ctile{position:absolute;left:840px;top:420px;width:240px;height:240px;background:var(--paper);display:grid;place-items:center}
35
+ #ctile svg{width:130px;height:130px}
36
+ .guide{position:absolute;background:repeating-linear-gradient(90deg,var(--c2) 0 10px,transparent 10px 20px);height:2px;opacity:.55;transform-origin:left center}
37
+ .guide.v{width:2px;height:auto;background:repeating-linear-gradient(180deg,var(--c2) 0 10px,transparent 10px 20px);transform-origin:center top}
38
+ .node{position:absolute;width:12px;height:12px;margin:-5px 0 0 -5px;background:var(--c2);opacity:.8}
39
+ #wm{position:absolute;left:410px;top:445px;width:1100px;height:190px;transform-origin:50% 50%}
40
+ #wm svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
41
+ #wmOutline path{fill:none;stroke:var(--c2);stroke-width:.35;stroke-dasharray:1.1 .9}
42
+ #wmFill path{fill:var(--ink)}
43
+ #idot{position:absolute;width:44px;height:44px;margin:-22px 0 0 -22px;border-radius:50%;background:var(--coral)}
44
+
45
+ /* C: dive */
46
+ #stage{position:absolute;inset:0;perspective:1900px;perspective-origin:50% 40%}
47
+ #plane{position:absolute;left:160px;top:80px;width:1600px;height:920px;transform-style:preserve-3d;transform-origin:50% 50%;
48
+ background:#fff;box-shadow:0 60px 120px rgba(10,10,10,.18),0 0 0 1px rgba(10,10,10,.06)}
49
+ #plane img{position:absolute;inset:0;width:100%;height:100%}
50
+ #plane .thick{position:absolute;left:0;right:0;bottom:-18px;height:18px;background:linear-gradient(#ececec,#d6d6d6);transform-origin:top;transform:rotateX(-90deg)}
51
+ #fog{position:absolute;left:0;right:0;top:0;height:420px;background:linear-gradient(var(--paper) 10%,rgba(255,255,255,0))}
52
+ #burst{position:absolute;left:1098.6px;top:724px;width:20px;height:20px;margin:-10px 0 0 -10px;border-radius:50%;background:var(--coral)}
53
+
54
+ /* D: coral field and slabs */
55
+ #field{background:linear-gradient(120deg,#FF532C,#FF3D59)}
56
+ .slab{position:absolute;transform-style:preserve-3d}
57
+ .slab .face{position:absolute;inset:0;background:#fff;border-radius:3px;padding:34px 40px;display:flex;flex-direction:column;justify-content:space-between;
58
+ box-shadow:inset 0 0 0 1px rgba(10,10,10,.05)}
59
+ .slab .edge{position:absolute;inset:0;border-radius:3px;background:#C9C9C9;transform:translateZ(-16px)}
60
+ .slab .shade{position:absolute;left:6%;right:6%;bottom:-40px;height:50px;border-radius:50%;background:rgba(90,0,20,.35);filter:blur(22px);transform:translateZ(-40px)}
61
+ .stat .num{font-size:150px;line-height:.9;letter-spacing:-.02em}
62
+ .chip .face{justify-content:center;align-items:center;padding:0}
63
+ .chip .num{font-size:66px;line-height:1}
64
+ .slab .caps{font-size:22px}
65
+ #dropLabel{font-size:26px}
66
+ #url{font-size:24px;letter-spacing:.32em}
67
+
68
+ /* E: people + line, F: lockup */
69
+ #pc{position:absolute;left:260px;top:240px;width:600px;height:600px;border-radius:50%;overflow:hidden;background:var(--coral)}
70
+ #pc img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0}
71
+ #line{position:absolute;left:980px;top:270px;font-size:150px;line-height:1.02;letter-spacing:-.015em}
72
+ #line .row{overflow:hidden;padding-bottom:14px}
73
+ #line .w{display:inline-block}
74
+ #line em{font-style:italic}
75
+ #line .dot{display:inline-block;width:.2em;height:.2em;border-radius:50%;background:var(--coral);margin-left:.03em}
76
+ #url{position:absolute;left:0;right:0;top:690px;text-align:center}
77
+
78
+ /* Paper-cut look: torn edges, fibre grain, hard shadows, white slivers, warm paper.
79
+ Same technique as the cards pack 01-paper-collage. Fixed seeds keep renders identical. */
80
+ #grain{display:none;position:absolute;inset:0;pointer-events:none;z-index:50;mix-blend-mode:multiply;opacity:.35;
81
+ background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='7' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}
82
+ [data-look=papercut]{--paper:#efe9dc;--ink:#17130e}
83
+ [data-look=papercut] #grain{display:block}
84
+ [data-look=papercut] #orb{background:var(--c2);box-shadow:none}
85
+ [data-look=papercut] :is(#orb,#ctile,#mosaic i,.slab .face,#plane img,#pc,#idot,#ldot){filter:url(#torn) drop-shadow(5px 7px 0 rgba(23,19,14,.3))}
86
+ [data-look=papercut] #mosaic i{width:112px;height:112px;margin:4px}
87
+ [data-look=papercut] .slab .shade{display:none}
88
+ [data-look=papercut] #plane{box-shadow:10px 14px 0 rgba(23,19,14,.25)}
89
+ </style></head>
90
+ <body>
91
+ <svg width="0" height="0" style="position:absolute"><filter id="torn" x="-5%" y="-5%" width="110%" height="110%">
92
+ <feTurbulence type="fractalNoise" baseFrequency="0.06" numOctaves="3" seed="11"/>
93
+ <feDisplacementMap in="SourceGraphic" scale="7" xChannelSelector="R" yChannelSelector="G"/></filter></svg>
94
+ <div id="root" data-composition-id="main" data-start="0" data-duration="15" data-width="1920" data-height="1080">
95
+ <div id="sA" class="layer clip" data-start="0" data-duration="15" data-track-index="1"><div id="halo"></div><div id="orb">%%MARK_WHITE%%</div></div>
96
+ <div id="sB" class="layer clip" data-start="0" data-duration="15" data-track-index="2">
97
+ <div id="mosaic" class="layer"></div>
98
+ <div id="guides" class="layer">
99
+ <i class="guide" style="left:300px;top:445px;width:1320px"></i><i class="guide" style="left:300px;top:635px;width:1320px"></i>
100
+ <i class="guide v" style="left:410px;top:330px;height:420px"></i><i class="guide v" style="left:1510px;top:330px;height:420px"></i>
101
+ <i class="node" style="left:410px;top:445px"></i><i class="node" style="left:1510px;top:445px"></i>
102
+ <i class="node" style="left:410px;top:635px"></i><i class="node" style="left:1510px;top:635px"></i>
103
+ </div>
104
+ <div id="ctile">%%MARK_INK%%</div>
105
+ <div id="wm"><svg id="wmOutline" viewBox="0 0 191.5 33">%%WM_PATHS%%</svg><svg id="wmFill" viewBox="0 0 191.5 33">%%WM_PATHS%%</svg></div>
106
+ </div>
107
+ <div id="sC" class="layer clip" data-start="0" data-duration="15" data-track-index="3">
108
+ <div id="stage"><div id="plane"><img src="assets/ui/hero.png" alt=""><i class="thick"></i></div></div>
109
+ <div id="fog"></div>
110
+ </div>
111
+ <div id="sD" class="layer clip" data-start="0" data-duration="15" data-track-index="4">
112
+ <div id="field" class="layer"></div>
113
+ <div class="layer" style="perspective:1700px">
114
+ <div id="s1" class="slab stat" style="left:250px;top:230px;width:640px;height:330px"><i class="shade"></i><i class="edge"></i>
115
+ <div class="face"><div class="caps">Saved by startups</div><div class="num serif">$<span id="n1">0</span>k</div></div></div>
116
+ <div id="s2" class="slab stat" style="left:1010px;top:500px;width:640px;height:330px"><i class="shade"></i><i class="edge"></i>
117
+ <div class="face"><div class="caps">Trademark-screened</div><div class="num serif"><span id="n2">0</span>+</div></div></div>
118
+ <div id="chips"></div>
119
+ </div>
120
+ <div class="caps" id="dropLabel" style="position:absolute;left:0;right:0;top:150px;text-align:center;color:#fff;opacity:.9">This week's drop</div>
121
+ </div>
122
+ <div id="sE" class="layer clip" data-start="0" data-duration="15" data-track-index="5">
123
+ <div id="pc"><img src="assets/broll/b1.jpg"><img src="assets/broll/b2.jpg"><img src="assets/broll/b3.jpg"><img src="assets/broll/b4.jpg"><img src="assets/broll/b5.jpg"><img src="assets/broll/b6.jpg"></div>
124
+ <div id="line" class="serif">
125
+ <div class="row"><span class="w">Rent it.</span></div>
126
+ <div class="row"><span class="w">Build it.</span></div>
127
+ <div class="row"><span class="w"><em>Launch it</em><i class="dot" id="ldot"></i></span></div>
128
+ </div>
129
+ </div>
130
+ <div id="sF" class="layer clip" data-start="0" data-duration="15" data-track-index="6">
131
+ <div id="url" class="caps" style="color:var(--ink)">luckiest.co</div>
132
+ </div>
133
+ <div id="sG" class="layer clip" data-start="0" data-duration="15" data-track-index="7"><i id="idot"></i><i id="burst"></i></div>
134
+ <i id="grain"></i>
135
+ <audio id="master" data-start="0" data-duration="15" data-track-index="9" data-volume="1" src="assets/reel-master.wav"></audio>
136
+ </div>
137
+ <script>
138
+ const DUR=15, PERIOD=0.5, B=(n)=>n*PERIOD;
139
+ const h=(n)=>{const x=Math.sin(n*127.1+311.7)*43758.5453;return x-Math.floor(x)}; // seeded, never Math.random()
140
+ const $=(s)=>document.querySelector(s), $$=(s)=>[...document.querySelectorAll(s)];
141
+ const tl=gsap.timeline({paused:true});
142
+
143
+ // Layout, built once.
144
+ const mosaic=$("#mosaic"), tiles=[];
145
+ for(let y=0;y<9;y++)for(let x=0;x<16;x++){
146
+ const i=document.createElement("i"); i.style.left=x*120+"px"; i.style.top=y*120+"px";
147
+ const d=Math.hypot(x-7.5,y-4), k=h(x*31+y*7);
148
+ i.style.background=(x+y)%2?"#FF3D59":(k>.5?"#FF532C":"#FF7C8E");
149
+ mosaic.appendChild(i); tiles.push({i,d});
150
+ }
151
+ const chips=$("#chips"), DOMAINS=["Quota.io","Puffy.net","Tryasap.com","Playbook.cc"];
152
+ DOMAINS.forEach((n,k)=>{const c=document.createElement("div");c.className="slab chip";c.id="c"+k;
153
+ Object.assign(c.style,{left:(110+k*435)+"px",top:"460px",width:"410px",height:"160px"});
154
+ c.innerHTML=`<i class="shade"></i><i class="edge"></i><div class="face"><div class="num serif">${n}</div></div>`;chips.appendChild(c)});
155
+ // i-dot of the wordmark on screen: viewBox (120.8,4.0) r3.8 at scale 1100/191.5.
156
+ const S=1100/191.5, IDOT={x:410+120.8*S, y:445+4.0*S, d:3.8*2*S};
157
+
158
+ // Initial state.
159
+ gsap.set(["#sB","#sC","#sD","#sE","#sF"],{opacity:0});
160
+ gsap.set("#orb",{scale:.8}); gsap.set("#halo",{scale:.6,opacity:0});
161
+ gsap.set(tiles.map(t=>t.i),{scale:0}); gsap.set("#ctile",{scale:0}); gsap.set("#guides",{opacity:0});
162
+ gsap.set(".guide",{scaleX:0}); gsap.set(".guide.v",{scaleX:1,scaleY:0}); gsap.set(".node",{scale:0});
163
+ gsap.set("#wmOutline",{clipPath:"inset(0 100% 0 0)"}); gsap.set("#wmFill",{clipPath:"inset(0 100% 0 0)"});
164
+ gsap.set("#idot",{left:960,top:540,scale:0}); gsap.set("#burst",{scale:0});
165
+
166
+ // A. Mark born: the orb breathes on each build beat, squashes, bursts on the drop (beat 4 = 2.0s).
167
+ tl.to("#halo",{opacity:1,scale:1,duration:B(3),ease:"power2.out"},0);
168
+ for(let k=0;k<4;k++){
169
+ tl.to("#orb",{scale:.88+k*.1,duration:.08,ease:"power2.out"},B(k));
170
+ tl.to("#orb",{scale:.84+k*.1,duration:B(1)-.1,ease:"sine.inOut"},B(k)+.08);
171
+ }
172
+ tl.to("#orb",{scaleX:1.22,scaleY:.82,duration:B(.5),ease:"power2.in"},B(3.5));
173
+ // beat-locked: 2.00s drop. Orb becomes the center tile, the mosaic bursts outward.
174
+ tl.set("#sB",{opacity:1},B(4));
175
+ tl.to("#orb",{scale:0,duration:.12,ease:"power2.in"},B(4));
176
+ tl.to("#halo",{scale:2.4,opacity:0,duration:B(1),ease:"expo.out"},B(4));
177
+ tl.fromTo("#ctile",{scale:.9},{scale:1,duration:B(.5),ease:"back.out(2)"},B(4));
178
+ tiles.forEach(t=>tl.to(t.i,{scale:1,duration:.32,ease:"expo.out"},B(4)+t.d*.022));
179
+ // B. Tiles flip away from the edges in; the center tile flies to the i-dot and becomes it.
180
+ tiles.forEach(t=>tl.to(t.i,{scale:0,rotate:90,duration:.3,ease:"power3.in"},B(5.5)+(9-t.d)*.06));
181
+ tl.set("#guides",{opacity:1},B(6));
182
+ tl.to(".guide",{scaleX:1,duration:B(1),ease:"expo.out",stagger:.05},B(6));
183
+ tl.to(".guide.v",{scaleY:1,duration:B(1),ease:"expo.out",stagger:.05},B(6.1));
184
+ tl.to(".node",{scale:1,duration:.25,ease:"back.out(3)",stagger:.04},B(6.5));
185
+ tl.to("#ctile",{scale:.18,x:IDOT.x-960,y:IDOT.y-540,borderRadius:"50%",backgroundColor:"#FF3D59",duration:B(1),ease:"power3.inOut"},B(6));
186
+ tl.to("#ctile svg",{opacity:0,duration:.15},B(6));
187
+ tl.set("#ctile",{opacity:0},B(7)); tl.set("#idot",{left:IDOT.x,top:IDOT.y,scale:IDOT.d/44},B(7));
188
+ tl.to("#wmOutline",{clipPath:"inset(0 0% 0 0)",duration:B(1.2),ease:"power2.inOut"},B(6.5));
189
+ tl.to("#wmFill",{clipPath:"inset(0 0% 0 0)",duration:B(1),ease:"power3.inOut"},B(7.25));
190
+ tl.to(["#guides","#wmOutline"],{opacity:0,duration:B(1),ease:"power1.out"},B(8));
191
+ tl.to("#idot",{scaleX:IDOT.d/44*1.35,scaleY:IDOT.d/44*.7,duration:.07,yoyo:true,repeat:1,ease:"none"},B(8)); // beat-locked 4.0s
192
+
193
+ // C. Dive: wordmark tips back into the page, the real hero rises under it, rack focus, push to the period, pull back flat.
194
+ gsap.set("#plane",{rotateX:62,rotateZ:-20,z:-300,x:420,y:900,filter:"blur(10px)"});
195
+ tl.set("#sC",{opacity:1},B(8.5));
196
+ tl.to("#wm",{transformPerspective:1600,rotateX:62,rotateZ:-20,y:-330,scale:.55,duration:B(2),ease:"power3.in"},B(8.5));
197
+ tl.to("#idot",{x:-80,y:-360,scale:.4,duration:B(2),ease:"power3.in"},B(8.5));
198
+ tl.set(["#sB","#idot"],{opacity:0},B(10.5));
199
+ tl.to("#plane",{y:120,x:520,z:-120,duration:B(2.5),ease:"power3.out"},B(8.5));
200
+ tl.to("#plane",{filter:"blur(0px)",duration:B(1.5),ease:"power2.out"},B(9.5)); // rack focus
201
+ tl.to("#plane",{x:-90,y:20,z:180,rotateX:48,rotateZ:-14,duration:B(3),ease:"power2.inOut"},B(11));
202
+ tl.to("#plane",{rotateX:0,rotateZ:0,x:0,y:0,z:0,duration:B(2),ease:"expo.inOut"},B(13.75)); // close to wide
203
+ tl.to("#fog",{opacity:0,duration:B(2),ease:"power2.inOut"},B(13.75));
204
+ // beat-locked 8.00s: the headline period swells into the coral field (motif wipe).
205
+ tl.set("#burst",{opacity:1,scale:1},B(15.5));
206
+ tl.to("#burst",{scale:260,duration:B(.75),ease:"expo.in"},B(15.5));
207
+ tl.set("#sD",{opacity:1},B(16)); tl.set(["#sC","#burst"],{opacity:0},B(16));
208
+
209
+ // D. Exploded data on the coral field: real numbers from luckiest.co (captured 2026-09-30).
210
+ const slabIn=(id,b,rot)=>tl.fromTo(id,{y:700,rotateX:55,rotateY:rot[0],rotateZ:rot[1]},{y:0,rotateX:16,rotateY:rot[0]*.5,rotateZ:rot[1]*.5,duration:B(1.5),ease:"expo.out"},B(b));
211
+ slabIn("#s1",16.25,[-26,6]); slabIn("#s2",17,[22,-5]);
212
+ const cnt={a:0,b:0};
213
+ tl.to(cnt,{a:450,duration:B(2),ease:"power3.out",onUpdate:()=>{$("#n1").textContent=Math.round(cnt.a)}},B(16.5)); // beat-grid
214
+ tl.to(cnt,{b:400,duration:B(2),ease:"power3.out",onUpdate:()=>{$("#n2").textContent=Math.round(cnt.b)}},B(17.25));
215
+ tl.to(["#s1","#s2"],{y:-1100,rotateX:-30,duration:B(1.5),ease:"power3.in",stagger:.08},B(19));
216
+ gsap.set("#dropLabel",{y:30,opacity:0});
217
+ tl.to("#dropLabel",{y:0,opacity:.9,duration:B(1),ease:"expo.out"},B(19.5));
218
+ DOMAINS.forEach((_,k)=>tl.fromTo("#c"+k,{y:800,rotateX:70,rotateZ:-8},{y:0,rotateX:22,rotateZ:-3,duration:B(1.25),ease:"expo.out"},B(19.5+k*.5))); // beat-grid 9.75..11.25
219
+ // E. The field closes into a circle: people inside the motif.
220
+ tl.to(["#chips","#dropLabel"],{y:-160,duration:B(1.5),ease:"power2.in"},B(22));
221
+ tl.fromTo("#sD",{clipPath:"circle(2100px at 560px 540px)"},{clipPath:"circle(300px at 560px 540px)",duration:B(1),ease:"expo.inOut"},B(22.5)-0.067);
222
+ tl.set("#sE",{opacity:1},B(23.5)); tl.set("#sD",{opacity:0},B(23.5));
223
+ const imgs=$$("#pc img");
224
+ imgs.forEach((im,k)=>{tl.set(im,{opacity:1},B(23.5+k*.5)); tl.fromTo(im,{scale:1.18},{scale:1,duration:B(1),ease:"power2.out"},B(23.5+k*.5))});
225
+ gsap.set("#line .w",{yPercent:110});
226
+ $$("#line .w").forEach((w,k)=>tl.to(w,{yPercent:0,duration:B(1),ease:"expo.out"},B(23.5+k))); // beat-grid 11.75/12.25/12.75
227
+ gsap.set("#ldot",{y:-300,opacity:0});
228
+ tl.to("#ldot",{y:0,opacity:1,duration:B(.5),ease:"power2.in"},B(25.75));
229
+ tl.to("#ldot",{scaleX:1.4,scaleY:.7,duration:.07,yoyo:true,repeat:1,ease:"none",transformOrigin:"50% 100%"},B(26.25));
230
+
231
+ // F. Bookend: the line exits, the people circle shrinks into the wordmark's i-dot on the final hit (14.0s).
232
+ tl.to("#line",{x:1100,duration:B(1),ease:"power3.in"},B(26.5)); tl.set("#line",{opacity:0},B(27.5));
233
+ tl.set("#sB",{opacity:1},B(27)); tl.set(["#tiles"],{},B(27));
234
+ tl.set("#wm",{rotateX:0,rotateZ:0,y:0,scale:1},B(27)); tl.set("#wmOutline",{opacity:0},B(27));
235
+ tl.fromTo("#wmFill",{clipPath:"inset(0 100% 0 0)"},{clipPath:"inset(0 0% 0 0)",duration:B(1),ease:"power3.out"},B(27));
236
+ tl.to("#pc",{x:IDOT.x-560,y:IDOT.y-540,scale:IDOT.d/600,duration:B(1),ease:"power3.in"},B(27));
237
+ tl.to(imgs,{opacity:0,duration:.12},B(27.75));
238
+ tl.set("#pc",{opacity:0},B(28)); tl.set("#idot",{opacity:1,x:0,y:0,left:IDOT.x,top:IDOT.y,scale:IDOT.d/44},B(28));
239
+ tl.to("#idot",{scaleX:IDOT.d/44*1.4,scaleY:IDOT.d/44*.68,duration:.08,yoyo:true,repeat:1,ease:"none"},B(28)); // beat-locked 14.0s final hit
240
+ tl.set("#sF",{opacity:1},B(28.25));
241
+ tl.fromTo("#url",{y:24,opacity:0},{y:0,opacity:1,duration:B(1),ease:"expo.out"},B(28.25));
242
+
243
+ tl.to({},{duration:DUR},0);
244
+ // Paper-cut look: shoot on twos. A stepped tween drives the master's time, so every frame
245
+ // holds for 1/12 s, and each piece sits a seeded fraction of a degree off level.
246
+ let out=tl;
247
+ if(document.documentElement.dataset.look==="papercut"){
248
+ $$("#mosaic i,.slab,#ctile,#pc,#line .w").forEach((el,k)=>{el.style.rotate=((h(k+50)-.5)*2.4).toFixed(2)+"deg";el.style.translate=`${((h(k+90)-.5)*6).toFixed(1)}px ${((h(k+130)-.5)*6).toFixed(1)}px`});
249
+ out=gsap.timeline({paused:true}).to(tl,{time:DUR,duration:DUR,ease:`steps(${DUR*12})`},0);
250
+ }
251
+ window.__timelines=window.__timelines||{}; window.__timelines["main"]=out; out.seek(0);
252
+ // Browser preview: in any window that is not exactly 1920x1080, scale the frame to fit, letterboxed
253
+ // and centered. At the render size this is a no-op, so renders are untouched.
254
+ // Add #play to the URL to also loop playback (the renderer never sets the hash).
255
+ const root=$("#root"), fit=()=>{
256
+ if(innerWidth===1920&&innerHeight===1080){root.style.transform="";return}
257
+ const k=Math.min(innerWidth/1920,innerHeight/1080);
258
+ for(const el of [document.documentElement,document.body]) Object.assign(el.style,{width:"100vw",height:"100vh",background:"#000"});
259
+ Object.assign(root.style,{position:"absolute",width:"1920px",height:"1080px",transformOrigin:"0 0",
260
+ transform:`translate(${(innerWidth-1920*k)/2}px,${(innerHeight-1080*k)/2}px) scale(${k})`})};
261
+ fit(); addEventListener("resize",fit);
262
+ if(location.hash==="#play"){out.eventCallback("onComplete",()=>out.restart()); out.play(0)}
263
+ </script>
264
+ </body></html>
@@ -1,5 +1,11 @@
1
1
  # Changelog: luckiest-video-watcher
2
2
 
3
+ ## 1.1.1 (2026-10-10)
4
+
5
+ - Security: the WhisperX setup's uv installer is pinned to 0.11.16 and saved to a
6
+ file before it runs on Windows and Linux. It no longer pipes the moving
7
+ `install.ps1` URL straight into PowerShell (SkillSpector SC2).
8
+
3
9
  ## 1.1.0 (2026-10-08)
4
10
 
5
11
  Skill-updater pass, plus the marketplace listing.
@@ -19,7 +19,7 @@ argument-hint: "<video-url | path/to/video.mp4> [question] [--study]"
19
19
  license: MIT. See ATTRIBUTION.md
20
20
  allowed-tools: Bash, Read, Write, AskUserQuestion
21
21
  metadata:
22
- version: "1.1.0"
22
+ version: "1.1.1"
23
23
  listing_id: luckiest-video-watcher
24
24
  author: luckiest
25
25
  ---
@@ -39,11 +39,11 @@ run of a session, when setup fails, or when a run needs a detail not covered her
39
39
  ## Staying current
40
40
 
41
41
  On activation, call the Luckiest MCP `check_updates` tool with
42
- `{ listingId: "luckiest-video-watcher", installedSemver: "1.1.0" }`. If
42
+ `{ listingId: "luckiest-video-watcher", installedSemver: "1.1.1" }`. If
43
43
  `upToDate: false`, surface the `notice` once and continue. Never block on it.
44
44
 
45
45
  When done, call `report_usage` once with
46
- `{ listing_id: "luckiest-video-watcher", skill_version: "1.1.0", matched: true, success: <true|false> }`.
46
+ `{ listing_id: "luckiest-video-watcher", skill_version: "1.1.1", matched: true, success: <true|false> }`.
47
47
  Metadata only. Skip silently if unavailable.
48
48
 
49
49
  ## Standing rules
@@ -18,6 +18,7 @@ from runtime import configure_stdio, diagnostic, run_text
18
18
 
19
19
  REQUIRED_BINARIES = ['ffmpeg', 'ffprobe', 'yt-dlp']
20
20
  WHISPERX_VERSION = '3.8.6'
21
+ UV_VERSION = '0.11.16' # pinned installer; bump deliberately
21
22
  _PERM_WARNED: set[str] = set()
22
23
  ENV_TEMPLATE = '''# /watch configuration. No shell interpolation; last assignment wins.
23
24
  # Native captions always come first. Optional fallback: auto|whisperx|groq|openai|none.
@@ -247,14 +248,15 @@ def _ensure_uv():
247
248
  if not _which('brew'):
248
249
  raise SystemExit('WhisperX setup needs uv. Install Homebrew from https://brew.sh, then rerun setup.')
249
250
  _install_step(['brew', 'install', 'uv'], 'Install uv')
250
- elif system == 'Windows':
251
- _install_step(['powershell', '-NoProfile', '-ExecutionPolicy', 'ByPass', '-c', 'irm https://astral.sh/uv/install.ps1 | iex'], 'Install uv')
252
- elif system == 'Linux':
253
- # Fetch first: a failed curl must not be hidden by a successful empty sh.
251
+ elif system in ('Windows', 'Linux'):
252
+ # Pinned version, fetched to a file first: never pipe a moving URL into a shell.
253
+ windows = system == 'Windows'
254
+ url = f'https://astral.sh/uv/{UV_VERSION}/install.{"ps1" if windows else "sh"}'
254
255
  with tempfile.TemporaryDirectory(prefix='watch-uv-') as temporary:
255
- script = Path(temporary) / 'install.sh'
256
- _install_step(['curl', '-LsSf', 'https://astral.sh/uv/install.sh', '-o', script], 'Download uv installer')
257
- _install_step(['sh', script], 'Install uv')
256
+ script = Path(temporary) / ('install.ps1' if windows else 'install.sh')
257
+ _install_step(['curl', '-LsSf', url, '-o', script], f'Download uv {UV_VERSION} installer')
258
+ run = ['powershell', '-NoProfile', '-ExecutionPolicy', 'ByPass', '-File', script] if windows else ['sh', script]
259
+ _install_step(run, f'Install uv {UV_VERSION}')
258
260
  else:
259
261
  raise SystemExit('Install uv from https://docs.astral.sh/uv/ before retrying WhisperX setup.')
260
262
  installed = _which('uv')
@@ -1,8 +0,0 @@
1
- interface:
2
- display_name: "Luckiest Trends"
3
- short_description: "Research any topic across Reddit, X, YouTube, TikTok, Hacker News, Polymarket, GitHub, and the web over a time window you choose. Returns synthesized community-voice answers with vote-ranked citations."
4
- default_prompt: "Research this topic over the last 30 days across Reddit, X, YouTube, TikTok, and the web. Synthesize what people are actually saying, upvoting, and sharing right now."
5
- brand_color: "#FF6B35"
6
-
7
- policy:
8
- allow_implicit_invocation: true
@@ -1,39 +0,0 @@
1
- #!/usr/bin/env bash
2
- # build-skill.sh - package this repo as a claude.ai-upload-ready .skill file
3
- # Usage: bash skills/last30days/scripts/build-skill.sh (run from repo root)
4
- #
5
- # Produces dist/last30days.skill, a zip with a single top-level `last30days/`
6
- # directory containing SKILL.md and the scripts/ runtime from skills/last30days.
7
- # See
8
- # docs/plans/2026-04-14-001-fix-skill-upload-200-file-limit-plan.md.
9
- set -euo pipefail
10
-
11
- REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
12
- cd "$REPO_ROOT"
13
-
14
- if ! git diff --quiet || ! git diff --cached --quiet; then
15
- echo "error: working tree is dirty; commit or stash before building" >&2
16
- exit 1
17
- fi
18
-
19
- mkdir -p dist
20
- OUT="dist/last30days.skill"
21
- git archive --format=zip --prefix=last30days/ --output="$OUT" HEAD:skills/last30days
22
-
23
- COUNT=$(unzip -l "$OUT" | tail -1 | awk '{print $2}')
24
- SIZE=$(du -h "$OUT" | cut -f1)
25
-
26
- if [ "$COUNT" -gt 200 ]; then
27
- echo "error: $COUNT files in zip, claude.ai's cap is 200" >&2
28
- echo " check .gitattributes export-ignore entries and this script's zip -d excludes" >&2
29
- exit 1
30
- fi
31
-
32
- SKILL_MD_COUNT=$(unzip -l "$OUT" | grep -c "SKILL.md" || true)
33
- if [ "$SKILL_MD_COUNT" -ne 1 ]; then
34
- echo "error: expected exactly one SKILL.md, found $SKILL_MD_COUNT" >&2
35
- exit 1
36
- fi
37
-
38
- echo "built $OUT ($COUNT files, $SIZE)"
39
- echo "upload via the claude.ai skill UI"
@@ -1,61 +0,0 @@
1
- #!/bin/bash
2
- # A/B test runner: public release vs private beta
3
- # Usage: bash skills/last30days/scripts/compare.sh "Kanye West"
4
- #
5
- # Runs /last30days (public release) and /last30days-beta (private beta)
6
- # sequentially with a 30s gap, saves raw results with distinct suffixes,
7
- # prints file paths for comparison.
8
-
9
- set -e
10
-
11
- if [ $# -eq 0 ]; then
12
- echo "Usage: bash skills/last30days/scripts/compare.sh <topic>"
13
- echo " Example: bash skills/last30days/scripts/compare.sh Kevin Rose"
14
- exit 1
15
- fi
16
- TOPIC="$*"
17
- SLUG=$(echo "$TOPIC" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//' | sed 's/-$//')
18
- LAST30DAYS_MEMORY_DIR="${LAST30DAYS_MEMORY_DIR:-$HOME/Documents/Last30Days}"
19
- DIR="$LAST30DAYS_MEMORY_DIR"
20
- DATE=$(date +%Y-%m-%d)
21
-
22
- echo "=============================================="
23
- echo " A/B Test: $TOPIC"
24
- echo " Date: $DATE"
25
- echo "=============================================="
26
- echo ""
27
-
28
- # Run 1: public release
29
- echo "[1/2] Running /last30days (public release)..."
30
- echo " This takes 2-4 minutes..."
31
- claude -p "/last30days $TOPIC" > /dev/null 2>&1 || true
32
- RELEASE_FILE="$DIR/${SLUG}-raw.md"
33
- [ -f "$RELEASE_FILE" ] && echo " Done: $RELEASE_FILE" || echo " FAILED: no output file"
34
- echo ""
35
-
36
- echo " Waiting 30s for API rate limits..."
37
- sleep 30
38
-
39
- # Run 2: private beta
40
- echo "[2/2] Running /last30days-beta (private beta)..."
41
- echo " This takes 2-4 minutes..."
42
- claude -p "/last30days-beta $TOPIC" > /dev/null 2>&1 || true
43
- BETA_FILE="$DIR/${SLUG}-raw-beta.md"
44
- [ -f "$BETA_FILE" ] && echo " Done: $BETA_FILE" || echo " FAILED: no output file"
45
- echo ""
46
-
47
- echo "=============================================="
48
- echo " Both complete. Raw files:"
49
- echo "=============================================="
50
- echo ""
51
- ls -la "$DIR/${SLUG}-raw"*.md 2>/dev/null || echo " (no files found - check if skills saved correctly)"
52
- echo ""
53
- echo "To compare, run in Claude Code:"
54
- echo " Read and compare these raw research files, produce a detailed report:"
55
- echo " $RELEASE_FILE"
56
- echo " $BETA_FILE"
57
- echo ""
58
- echo "Beta output should start with a line like:"
59
- echo " 🧪 last30days-beta · branch <name> · synced $DATE"
60
- echo "If that line is missing, the beta badge regressed. See docs/plans/2026-04-17-005-*-plan.md."
61
- echo ""