luckiest-co 1.0.39 → 1.0.41

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 (165) 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/ATTRIBUTION.md +3 -0
  108. package/skills/video-studio/CHANGELOG.md +25 -0
  109. package/skills/video-studio/LICENSE-motionmaxxing +202 -0
  110. package/skills/video-studio/SKILL.md +115 -25
  111. package/skills/video-studio/references/audio.md +15 -0
  112. package/skills/video-studio/references/interview.md +75 -0
  113. package/skills/video-studio/references/step-2-plan.md +39 -7
  114. package/skills/video-studio/references/storyboard.md +3 -1
  115. package/skills/video-studio/references/tones.md +5 -5
  116. package/skills/video-studio/scripts/pyproject.toml +4 -2
  117. package/skills/video-studio/scripts/qa.mjs +85 -1
  118. package/skills/video-studio/scripts/qa.test.mjs +37 -1
  119. package/skills/video-studio/scripts/render-scenes.mjs +1 -0
  120. package/skills/video-studio/scripts/storyboard.schema.json +6 -1
  121. package/skills/video-studio/scripts/uv.lock +8 -5
  122. package/skills/video-studio-art-director/ATTRIBUTION.md +3 -0
  123. package/skills/video-studio-art-director/CHANGELOG.md +7 -0
  124. package/skills/video-studio-art-director/LICENSE-motionmaxxing +202 -0
  125. package/skills/video-studio-art-director/SKILL.md +42 -7
  126. package/skills/video-studio-composition/ATTRIBUTION.md +3 -0
  127. package/skills/video-studio-composition/CHANGELOG.md +7 -0
  128. package/skills/video-studio-composition/LICENSE-motionmaxxing +202 -0
  129. package/skills/video-studio-composition/SKILL.md +17 -4
  130. package/skills/video-studio-director/ATTRIBUTION.md +3 -0
  131. package/skills/video-studio-director/CHANGELOG.md +15 -0
  132. package/skills/video-studio-director/LICENSE-motionmaxxing +202 -0
  133. package/skills/video-studio-director/SKILL.md +105 -10
  134. package/skills/video-studio-motion/ATTRIBUTION.md +3 -0
  135. package/skills/video-studio-motion/CHANGELOG.md +8 -0
  136. package/skills/video-studio-motion/LICENSE-motionmaxxing +202 -0
  137. package/skills/video-studio-motion/SKILL.md +98 -5
  138. package/skills/video-studio-motion/references/kinetic-type.md +89 -0
  139. package/skills/video-studio-motion/references/ui-demo.md +59 -0
  140. package/skills/video-studio-review/CHANGELOG.md +11 -0
  141. package/skills/video-studio-review/SKILL.md +31 -5
  142. package/skills/video-studio-review/editor/index.html +21 -3
  143. package/skills/video-studio-review/references/apply-feedback.md +19 -0
  144. package/skills/video-studio-review/scripts/build-reel.mjs +35 -10
  145. package/skills/video-studio-review/scripts/build-reel.test.mjs +15 -0
  146. package/skills/video-studio-script/ATTRIBUTION.md +6 -0
  147. package/skills/video-studio-script/CHANGELOG.md +12 -0
  148. package/skills/video-studio-script/LICENSE-motionmaxxing +202 -0
  149. package/skills/video-studio-script/SKILL.md +41 -10
  150. package/skills/video-studio-showreel/CHANGELOG.md +21 -0
  151. package/skills/video-studio-showreel/SKILL.md +231 -0
  152. package/skills/video-studio-showreel/references/motion-design-pack-notes.md +28 -0
  153. package/skills/video-studio-showreel/references/zingage-breakdown.md +47 -0
  154. package/skills/video-studio-showreel/scripts/build.py +10 -0
  155. package/skills/video-studio-showreel/templates/showreel.html +264 -0
  156. package/skills/video-watcher/CHANGELOG.md +6 -0
  157. package/skills/video-watcher/SKILL.md +3 -3
  158. package/skills/video-watcher/scripts/setup.py +9 -7
  159. package/skills/trends/agents/openai.yaml +0 -8
  160. package/skills/trends/scripts/build-skill.sh +0 -39
  161. package/skills/trends/scripts/compare.sh +0 -61
  162. package/skills/trends/scripts/evaluate_search_quality.py +0 -573
  163. package/skills/trends/scripts/test-v1-vs-v2.sh +0 -220
  164. package/skills/trends/scripts/test_device_auth.py +0 -128
  165. package/skills/trends/scripts/verify_v3.py +0 -195
@@ -1,220 +0,0 @@
1
- #!/bin/bash
2
- set -euo pipefail
3
-
4
- # === V1 vs V2 Skill Test Harness ===
5
- # Runs all 17 test queries through both v1 and v2 SKILL.md
6
- # using `claude --print` to capture real end-to-end output.
7
-
8
- SKILL_DIR="$HOME/.claude/skills/last30days"
9
- REPO_DIR="${REPO_DIR:-$(cd "$(dirname "$0")/.." && pwd)}"
10
- CLAUDE="${CLAUDE:-$(command -v claude || echo claude)}"
11
-
12
- # Safety: always restore V2 SKILL.md on exit/crash
13
- cleanup() {
14
- if [ -f "$SKILL_DIR/SKILL.md.v2.bak" ]; then
15
- echo ""
16
- echo "⚠️ Restoring V2 SKILL.md from backup (script interrupted)..."
17
- cp "$SKILL_DIR/SKILL.md.v2.bak" "$SKILL_DIR/SKILL.md"
18
- rm -f "$SKILL_DIR/SKILL.md.v2.bak"
19
- echo " ✅ V2 restored"
20
- fi
21
- }
22
- trap cleanup EXIT
23
- TIMESTAMP=$(date +%Y%m%d-%H%M%S)
24
- OUT_DIR="$REPO_DIR/docs/test-results/v1-vs-v2-${TIMESTAMP}"
25
- V1_DIR="$OUT_DIR/v1"
26
- V2_DIR="$OUT_DIR/v2"
27
-
28
- mkdir -p "$V1_DIR" "$V2_DIR"
29
-
30
- echo "📁 Output directory: $OUT_DIR"
31
- echo ""
32
-
33
- # All 17 test queries
34
- QUERIES=(
35
- "prompting techniques for chatgpt for legal questions"
36
- "best clawdbot use cases"
37
- "how to best setup clawdbot"
38
- "prompting tips for nano banana pro for ios designs"
39
- "top claude code skills"
40
- "using ChatGPT to make images of dogs"
41
- "research best practices for beautiful remotion animation videos in claude code"
42
- "photorealistic people in nano banana pro"
43
- "What are the best rap songs lately"
44
- "what are people saying about DeepSeek R1"
45
- "best practices for cursor rules files for Cursor"
46
- "prompt advice for using suno to make killer songs in simple mode"
47
- "how do I use Codex with Claude Code on same app to make it better"
48
- "kanye west"
49
- "howie.ai"
50
- "open claw"
51
- "nano banana pro prompting"
52
- )
53
-
54
- TYPES=(
55
- "PROMPTING+TOOL"
56
- "RECOMMENDATIONS"
57
- "HOW-TO"
58
- "PROMPTING+TOOL"
59
- "RECOMMENDATIONS"
60
- "GENERAL"
61
- "PROMPTING"
62
- "PROMPTING"
63
- "RECOMMENDATIONS"
64
- "NEWS"
65
- "PROMPTING"
66
- "PROMPTING"
67
- "HOW-TO"
68
- "NEWS"
69
- "GENERAL"
70
- "GENERAL"
71
- "PROMPTING"
72
- )
73
-
74
- slugify() {
75
- echo "$1" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | cut -c1-50
76
- }
77
-
78
- run_version() {
79
- local version="$1"
80
- local outdir="$2"
81
- local total=${#QUERIES[@]}
82
-
83
- echo ""
84
- echo "=========================================="
85
- echo " Running $version — $total queries"
86
- echo "=========================================="
87
- echo ""
88
-
89
- for i in "${!QUERIES[@]}"; do
90
- local query="${QUERIES[$i]}"
91
- local type="${TYPES[$i]}"
92
- local slug
93
- slug=$(slugify "$query")
94
- local num=$((i + 1))
95
- local outfile="$outdir/${num}-${slug}.txt"
96
- local errfile="$outdir/${num}-${slug}.stderr.txt"
97
-
98
- echo "[$version] ($num/$total) $query [$type]"
99
-
100
- local start_time
101
- start_time=$(date +%s)
102
-
103
- # Run claude --print with the skill invocation
104
- # No timeout — claude --print exits on its own; kill manually if stuck
105
- if "$CLAUDE" --print \
106
- "/last30days $query" \
107
- > "$outfile" 2>"$errfile"; then
108
- local end_time
109
- end_time=$(date +%s)
110
- local duration=$((end_time - start_time))
111
- local lines
112
- lines=$(wc -l < "$outfile")
113
- echo " ✅ Done — ${lines} lines, ${duration}s"
114
- else
115
- local exit_code=$?
116
- echo " ❌ Failed (exit $exit_code)" | tee -a "$outfile"
117
- fi
118
-
119
- # Brief pause between queries to avoid rate limits
120
- sleep 3
121
- done
122
- }
123
-
124
- # === Phase 1: Test V1 ===
125
- echo "📦 Backing up current V2 SKILL.md..."
126
- cp "$SKILL_DIR/SKILL.md" "$SKILL_DIR/SKILL.md.v2.bak"
127
-
128
- echo "📥 Installing V1 SKILL.md from upstream..."
129
- cd "$REPO_DIR"
130
- git show upstream/main:SKILL.md | sed '/^context: fork$/d; /^agent: Explore$/d; /^disable-model-invocation: true$/d' > "$SKILL_DIR/SKILL.md"
131
- cp "$SKILL_DIR/SKILL.md" "$OUT_DIR/v1-SKILL.md"
132
- echo " ✅ V1 installed (stripped: context:fork, agent:Explore, disable-model-invocation)"
133
-
134
- run_version "V1" "$V1_DIR"
135
-
136
- # === Phase 2: Test V2 ===
137
- echo ""
138
- echo "📥 Restoring V2 SKILL.md..."
139
- cp "$SKILL_DIR/SKILL.md.v2.bak" "$SKILL_DIR/SKILL.md"
140
- cp "$SKILL_DIR/SKILL.md" "$OUT_DIR/v2-SKILL.md"
141
- echo " ✅ V2 restored"
142
-
143
- run_version "V2" "$V2_DIR"
144
-
145
- # === Phase 3: Generate summary ===
146
- echo ""
147
- echo "=========================================="
148
- echo " Generating comparison summary"
149
- echo "=========================================="
150
-
151
- SUMMARY="$OUT_DIR/comparison-summary.md"
152
-
153
- cat > "$SUMMARY" << EOF
154
- # V1 vs V2 Comparison Results
155
-
156
- Generated: $(date)
157
- Output directory: $OUT_DIR
158
-
159
- ## Output Files
160
-
161
- | # | Query | Type | V1 Lines | V2 Lines | V1 Time | V2 Time |
162
- |---|-------|------|----------|----------|---------|---------|
163
- EOF
164
-
165
- for i in "${!QUERIES[@]}"; do
166
- query="${QUERIES[$i]}"
167
- type="${TYPES[$i]}"
168
- slug=$(slugify "$query")
169
- num=$((i + 1))
170
-
171
- v1file="$V1_DIR/${num}-${slug}.txt"
172
- v2file="$V2_DIR/${num}-${slug}.txt"
173
-
174
- v1lines=$(wc -l < "$v1file" 2>/dev/null || echo "ERR")
175
- v2lines=$(wc -l < "$v2file" 2>/dev/null || echo "ERR")
176
-
177
- echo "| $num | \`$query\` | $type | $v1lines | $v2lines | — | — |" >> "$SUMMARY"
178
- done
179
-
180
- cat >> "$SUMMARY" << 'EOF'
181
-
182
- ## Quick Check: Key Features
183
-
184
- For each query, check these v2 improvements:
185
-
186
- - [ ] Query parsing display (`🔍 **{TOPIC}** · {QUERY_TYPE}`)
187
- - [ ] Sparse citations (not every sentence)
188
- - [ ] Bold topic headers in summary
189
- - [ ] Emoji stats tree (`├─ 🟠 Reddit:`)
190
- - [ ] Quality checklist applied to prompts
191
- - [ ] Self-check (research grounding, not generic)
192
-
193
- ## Scoring Guide
194
-
195
- Use the full scoring rubric from:
196
- `docs/plans/2026-02-06-test-v1-vs-v2-comparison-plan.md`
197
-
198
- ## Next Step
199
-
200
- Have Claude read all 34 output files and generate scored comparison:
201
- ```
202
- Read all files in docs/test-results/v1-vs-v2-*/v1/ and v2/
203
- Score each on the 7 dimensions from the test plan
204
- Write the final analysis to docs/test-results/v1-vs-v2-*/analysis.md
205
- ```
206
- EOF
207
-
208
- # Cleanup backup
209
- rm -f "$SKILL_DIR/SKILL.md.v2.bak"
210
-
211
- echo ""
212
- echo "✅ All done!"
213
- echo ""
214
- echo "📁 Results: $OUT_DIR"
215
- echo "📊 Summary: $SUMMARY"
216
- echo "📄 V1 files: $V1_DIR/"
217
- echo "📄 V2 files: $V2_DIR/"
218
- echo ""
219
- echo "To review:"
220
- echo " open $OUT_DIR"
@@ -1,128 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Test ScrapeCreators GitHub device auth flow from the CLI.
3
-
4
- Usage:
5
- python3 scripts/test_device_auth.py
6
-
7
- Flow:
8
- 1. Starts device code request
9
- 2. Shows user code + opens GitHub auth URL in browser
10
- 3. Polls for token until you complete auth
11
- 4. Fetches your profile and prints your API key
12
- """
13
-
14
- import json
15
- import sys
16
- import time
17
- import webbrowser
18
- from urllib.request import Request, urlopen
19
- from urllib.error import HTTPError, URLError
20
-
21
- BASE = "https://api.scrapecreators.com/v1/github/device"
22
-
23
-
24
- def _post(url, data=None):
25
- body = json.dumps(data).encode() if data else None
26
- req = Request(url, data=body, method="POST")
27
- req.add_header("Content-Type", "application/json")
28
- with urlopen(req, timeout=15) as resp:
29
- return json.loads(resp.read())
30
-
31
-
32
- def _get(url, token):
33
- req = Request(url)
34
- req.add_header("Authorization", f"Bearer {token}")
35
- with urlopen(req, timeout=15) as resp:
36
- return json.loads(resp.read())
37
-
38
-
39
- def main():
40
- # Step 1: Start device flow
41
- print("Starting ScrapeCreators GitHub device auth...\n")
42
- try:
43
- code_resp = _post(f"{BASE}/code")
44
- except (HTTPError, URLError) as e:
45
- print(f"Failed to start device flow: {e}")
46
- sys.exit(1)
47
-
48
- device_code = code_resp.get("device_code")
49
- user_code = code_resp.get("user_code")
50
- verification_uri = code_resp.get("verification_uri")
51
- interval = code_resp.get("interval", 5)
52
- expires_in = code_resp.get("expires_in", 900)
53
-
54
- if not device_code or not user_code:
55
- print(f"Unexpected response: {json.dumps(code_resp, indent=2)}")
56
- sys.exit(1)
57
-
58
- print(f"Your code: {user_code}")
59
- print(f"Open: {verification_uri}")
60
- print(f"Expires in: {expires_in}s\n")
61
-
62
- # Open browser
63
- if verification_uri:
64
- webbrowser.open(verification_uri)
65
- print("Opened browser. Enter the code above, then authorize.\n")
66
-
67
- # Step 2: Poll for token
68
- print("Waiting for authorization", end="", flush=True)
69
- deadline = time.time() + expires_in
70
- access_token = None
71
-
72
- while time.time() < deadline:
73
- time.sleep(interval)
74
- print(".", end="", flush=True)
75
- try:
76
- token_resp = _post(f"{BASE}/token", {"device_code": device_code})
77
- except HTTPError as e:
78
- # Some APIs return 4xx while pending
79
- if e.code in (400, 403, 428):
80
- continue
81
- print(f"\nPoll error: {e}")
82
- sys.exit(1)
83
- except URLError:
84
- continue
85
-
86
- if token_resp.get("access_token"):
87
- access_token = token_resp["access_token"]
88
- break
89
-
90
- # Check for explicit error states
91
- error = token_resp.get("error")
92
- if error == "authorization_pending" or error == "slow_down":
93
- if error == "slow_down":
94
- interval = min(interval + 2, 30)
95
- continue
96
- if error in ("expired_token", "access_denied"):
97
- print(f"\n\nAuth failed: {error}")
98
- sys.exit(1)
99
-
100
- if not access_token:
101
- print("\n\nTimed out waiting for authorization.")
102
- sys.exit(1)
103
-
104
- print(f"\n\nAuthorized! Access token: {access_token[:12]}...\n")
105
-
106
- # Step 3: Fetch profile
107
- print("Fetching profile...")
108
- try:
109
- profile = _get(f"{BASE}/profile", access_token)
110
- except (HTTPError, URLError) as e:
111
- print(f"Failed to fetch profile: {e}")
112
- print(f"(access_token was: {access_token})")
113
- sys.exit(1)
114
-
115
- print(f"\nProfile response:\n{json.dumps(profile, indent=2)}\n")
116
-
117
- api_key = profile.get("api_key")
118
- if api_key:
119
- print("=" * 50)
120
- print(f"Your ScrapeCreators API key: {api_key}")
121
- print("=" * 50)
122
- print(f"\nTo use it: echo 'SCRAPECREATORS_API_KEY={api_key}' >> ~/.config/last30days/.env")
123
- else:
124
- print("No api_key in profile response. Full response printed above.")
125
-
126
-
127
- if __name__ == "__main__":
128
- main()
@@ -1,195 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Run the v3 verification bundle for last30days."""
3
-
4
- from __future__ import annotations
5
-
6
- import argparse
7
- import json
8
- import os
9
- import statistics
10
- import subprocess
11
- import sys
12
- import time
13
- from pathlib import Path
14
-
15
-
16
- SKILL_ROOT = Path(__file__).resolve().parents[1]
17
- REPO_ROOT = Path(__file__).resolve().parents[3]
18
- PYTHON = sys.executable
19
- ENGINE = SKILL_ROOT / "scripts" / "last30days.py"
20
- EVALUATOR = SKILL_ROOT / "scripts" / "evaluate_search_quality.py"
21
-
22
- SMOKE_TOPIC = "openclaw skills"
23
- SMOKE_CASES = [
24
- ("gemini", ["--quick", "--search=grounding,hackernews"]),
25
- ("openai", ["--quick", "--search=reddit,hackernews"]),
26
- ("xai", ["--quick", "--search=reddit,hackernews"]),
27
- ("auto", ["--quick", "--search=reddit,grounding,hackernews"]),
28
- ]
29
-
30
- LATENCY_TOPICS = [
31
- "openclaw skills",
32
- "codex vs claude code",
33
- "anthropic odds",
34
- ]
35
- LATENCY_PROFILES = [
36
- ("quick", ["--quick", "--search=grounding,hackernews"]),
37
- ("default", ["--search=grounding,hackernews"]),
38
- ("deep", ["--deep", "--search=grounding,hackernews"]),
39
- ]
40
-
41
-
42
- def run_command(cmd: list[str], *, env: dict[str, str] | None = None, timeout: int = 600) -> subprocess.CompletedProcess[str]:
43
- return subprocess.run(
44
- cmd,
45
- cwd=REPO_ROOT,
46
- env=env,
47
- text=True,
48
- capture_output=True,
49
- timeout=timeout,
50
- check=True,
51
- )
52
-
53
-
54
- def verify_unit() -> dict[str, str]:
55
- run_command([PYTHON, "-m", "unittest", "discover", "-s", "tests", "-p", "test_*.py"], timeout=600)
56
- run_command(
57
- [
58
- PYTHON,
59
- "-m",
60
- "py_compile",
61
- *subprocess.run(
62
- [
63
- "rg",
64
- "--files",
65
- "skills/last30days/scripts",
66
- "tests",
67
- "-g",
68
- "*.py",
69
- "-g",
70
- "!skills/last30days/scripts/lib/vendor/**",
71
- ],
72
- cwd=REPO_ROOT,
73
- text=True,
74
- capture_output=True,
75
- check=True,
76
- ).stdout.split(),
77
- ],
78
- timeout=600,
79
- )
80
- return {"status": "ok"}
81
-
82
-
83
- def verify_diagnose() -> dict[str, object]:
84
- result = run_command([PYTHON, str(ENGINE), "--diagnose"], timeout=120)
85
- return json.loads(result.stdout)
86
-
87
-
88
- def verify_smoke() -> list[dict[str, object]]:
89
- rows: list[dict[str, object]] = []
90
- for provider, extra in SMOKE_CASES:
91
- env = os.environ.copy()
92
- env["LAST30DAYS_REASONING_PROVIDER"] = provider
93
- start = time.time()
94
- result = run_command(
95
- [PYTHON, str(ENGINE), SMOKE_TOPIC, "--emit=json", *extra],
96
- env=env,
97
- timeout=240,
98
- )
99
- duration = round(time.time() - start, 2)
100
- report = json.loads(result.stdout)
101
- rows.append(
102
- {
103
- "provider": provider,
104
- "duration_seconds": duration,
105
- "reasoning_provider": (report.get("provider_runtime") or {}).get("reasoning_provider"),
106
- "cluster_count": len(report.get("clusters") or []),
107
- "candidate_count": len(report.get("ranked_candidates") or []),
108
- "error_sources": sorted((report.get("errors_by_source") or {}).keys()),
109
- }
110
- )
111
- return rows
112
-
113
-
114
- def verify_latency() -> dict[str, dict[str, object]]:
115
- results: dict[str, dict[str, object]] = {}
116
- for profile, extra in LATENCY_PROFILES:
117
- timings = []
118
- for topic in LATENCY_TOPICS:
119
- start = time.time()
120
- run_command(
121
- [PYTHON, str(ENGINE), topic, "--emit=json", *extra],
122
- timeout=300,
123
- )
124
- timings.append(time.time() - start)
125
- results[profile] = {
126
- "times": [round(value, 2) for value in timings],
127
- "median_seconds": round(statistics.median(timings), 2),
128
- "max_seconds": round(max(timings), 2),
129
- }
130
- return results
131
-
132
-
133
- def verify_eval(
134
- *,
135
- baseline: str,
136
- candidate: str,
137
- output_dir: str,
138
- quick: bool,
139
- limit: int,
140
- timeout: int,
141
- ) -> dict[str, object]:
142
- cmd = [
143
- PYTHON,
144
- str(EVALUATOR),
145
- f"--baseline={baseline}",
146
- f"--candidate={candidate}",
147
- f"--output-dir={output_dir}",
148
- f"--limit={limit}",
149
- f"--timeout={timeout}",
150
- ]
151
- if quick:
152
- cmd.append("--quick")
153
- run_command(cmd, timeout=max(timeout * 8, 600))
154
- output = Path(output_dir)
155
- metrics = json.loads((output / "metrics.json").read_text())
156
- summary = (output / "summary.md").read_text()
157
- return {"metrics": metrics, "summary": summary}
158
-
159
-
160
- def build_parser() -> argparse.ArgumentParser:
161
- parser = argparse.ArgumentParser(description="Run the v3 verification bundle")
162
- parser.add_argument("--skip-eval", action="store_true", help="Skip the judged evaluator")
163
- parser.add_argument("--skip-latency", action="store_true", help="Skip live latency sampling")
164
- parser.add_argument("--baseline", default="HEAD~1")
165
- parser.add_argument("--candidate", default="WORKTREE")
166
- parser.add_argument("--output-dir", default="/tmp/last30days-v3-verify")
167
- parser.add_argument("--quick-eval", action="store_true", help="Use evaluator quick mode")
168
- parser.add_argument("--eval-limit", type=int, default=20)
169
- parser.add_argument("--eval-timeout", type=int, default=240)
170
- return parser
171
-
172
-
173
- def main() -> int:
174
- args = build_parser().parse_args()
175
- summary: dict[str, object] = {}
176
- summary["unit"] = verify_unit()
177
- summary["diagnose"] = verify_diagnose()
178
- summary["smoke"] = verify_smoke()
179
- if not args.skip_latency:
180
- summary["latency"] = verify_latency()
181
- if not args.skip_eval:
182
- summary["eval"] = verify_eval(
183
- baseline=args.baseline,
184
- candidate=args.candidate,
185
- output_dir=args.output_dir,
186
- quick=args.quick_eval,
187
- limit=args.eval_limit,
188
- timeout=args.eval_timeout,
189
- )
190
- print(json.dumps(summary, indent=2, sort_keys=True))
191
- return 0
192
-
193
-
194
- if __name__ == "__main__":
195
- raise SystemExit(main())