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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/skills/ad-creative/CHANGELOG.md +7 -0
- package/skills/ad-creative/SKILL.md +23 -4
- package/skills/ad-creative/references/generative-tools.md +6 -30
- package/skills/ads/CHANGELOG.md +9 -0
- package/skills/ads/SKILL.md +19 -9
- package/skills/ads/references/reading-google-ads-data.md +121 -0
- package/skills/ai-seo/CHANGELOG.md +11 -0
- package/skills/ai-seo/SKILL.md +21 -22
- package/skills/ai-seo/references/content-types.md +11 -0
- package/skills/ai-seo/references/linkedin-ai-citations.md +122 -0
- package/skills/ai-seo/references/platform-ranking-factors.md +32 -19
- package/skills/ai-seo/references/positioning-and-consensus.md +102 -0
- package/skills/coder/CHANGELOG.md +23 -0
- package/skills/coder/SKILL.md +16 -4
- package/skills/coder-brainstorming/CHANGELOG.md +9 -0
- package/skills/coder-brainstorming/SKILL.md +5 -3
- package/skills/coder-debugging/CHANGELOG.md +11 -0
- package/skills/coder-debugging/SKILL.md +15 -3
- package/skills/coder-executing-plans/CHANGELOG.md +9 -0
- package/skills/coder-executing-plans/SKILL.md +5 -3
- package/skills/coder-finishing-a-branch/CHANGELOG.md +9 -0
- package/skills/coder-finishing-a-branch/SKILL.md +7 -3
- package/skills/coder-guard/CHANGELOG.md +7 -0
- package/skills/coder-guard/SKILL.md +6 -6
- package/skills/coder-receiving-code-review/CHANGELOG.md +9 -0
- package/skills/coder-receiving-code-review/SKILL.md +5 -3
- package/skills/coder-shipping/CHANGELOG.md +9 -0
- package/skills/coder-shipping/SKILL.md +4 -4
- package/skills/coder-subagent-driven-development/CHANGELOG.md +9 -0
- package/skills/coder-subagent-driven-development/SKILL.md +5 -3
- package/skills/coder-writing-plans/CHANGELOG.md +9 -0
- package/skills/coder-writing-plans/SKILL.md +5 -3
- package/skills/cold-email/CHANGELOG.md +7 -0
- package/skills/cold-email/SKILL.md +15 -8
- package/skills/cold-email/references/follow-up-sequences.md +4 -4
- package/skills/cold-email/references/frameworks.md +5 -5
- package/skills/cold-email/references/personalization.md +10 -10
- package/skills/competitor-profiling/CHANGELOG.md +8 -0
- package/skills/competitor-profiling/SKILL.md +34 -9
- package/skills/competitor-profiling/references/tool-reference.md +141 -5
- package/skills/competitors/CHANGELOG.md +8 -0
- package/skills/competitors/SKILL.md +32 -5
- package/skills/competitors/references/content-architecture.md +6 -0
- package/skills/competitors/references/templates.md +3 -0
- package/skills/copy-editing/CHANGELOG.md +10 -0
- package/skills/copy-editing/SKILL.md +41 -9
- package/skills/copy-editing/references/checklist.md +11 -0
- package/skills/copywriting/CHANGELOG.md +11 -0
- package/skills/copywriting/SKILL.md +32 -4
- package/skills/copywriting/references/copy-frameworks.md +2 -3
- package/skills/copywriting/references/natural-transitions.md +4 -20
- package/skills/customer-research/CHANGELOG.md +8 -0
- package/skills/customer-research/SKILL.md +6 -3
- package/skills/directory-submissions/CHANGELOG.md +9 -0
- package/skills/directory-submissions/SKILL.md +21 -10
- package/skills/emails/CHANGELOG.md +8 -0
- package/skills/emails/SKILL.md +22 -3
- package/skills/emails/references/sequence-templates.md +3 -3
- package/skills/image/CHANGELOG.md +8 -0
- package/skills/image/SKILL.md +4 -4
- package/skills/image/references/ai-image-prompting.md +1 -1
- package/skills/marketing-plan/CHANGELOG.md +8 -0
- package/skills/marketing-plan/SKILL.md +8 -4
- package/skills/marketing-plan/references/market-sizing.md +166 -0
- package/skills/marketing-plan/references/methodology.md +2 -0
- package/skills/model-router/CHANGELOG.md +9 -0
- package/skills/model-router/SKILL.md +3 -3
- package/skills/model-router/references/weave-routing-notes.md +1 -0
- package/skills/model-router/scripts/route.py +3 -1
- package/skills/prospecting/CHANGELOG.md +7 -0
- package/skills/prospecting/SKILL.md +3 -3
- package/skills/prospecting/references/saas-prospecting.md +1 -1
- package/skills/public-relations/CHANGELOG.md +10 -0
- package/skills/public-relations/SKILL.md +16 -4
- package/skills/public-relations/references/crisis-communications.md +111 -0
- package/skills/public-relations/references/newsjacking.md +2 -0
- package/skills/public-relations/references/statement-templates.md +143 -0
- package/skills/sales-enablement/CHANGELOG.md +8 -0
- package/skills/sales-enablement/SKILL.md +72 -4
- package/skills/sales-enablement/references/win-loss-analysis.md +72 -0
- package/skills/skill-audit/ATTRIBUTION.md +5 -0
- package/skills/skill-audit/CHANGELOG.md +19 -0
- package/skills/skill-audit/SKILL.md +121 -0
- package/skills/skill-audit-security/ATTRIBUTION.md +14 -0
- package/skills/skill-audit-security/CHANGELOG.md +14 -0
- package/skills/skill-audit-security/LICENSE-APACHE-2.0 +190 -0
- package/skills/skill-audit-security/SKILL.md +99 -0
- package/skills/skill-audit-security/references/rule-ids.md +28 -0
- package/skills/skill-audit-security/references/source-review.md +58 -0
- package/skills/sms/CHANGELOG.md +8 -0
- package/skills/sms/SKILL.md +18 -4
- package/skills/sms/references/whatsapp.md +258 -0
- package/skills/social/CHANGELOG.md +11 -0
- package/skills/social/SKILL.md +31 -7
- package/skills/social/references/listening.md +2 -0
- package/skills/social/references/platforms.md +15 -9
- package/skills/social/references/post-templates.md +9 -9
- package/skills/social/references/short-form-video.md +5 -5
- package/skills/social/references/x-algorithm.md +69 -0
- package/skills/trends/CHANGELOG.md +3 -0
- package/skills/trends/SKILL.md +14 -4
- package/skills/video/CHANGELOG.md +8 -0
- package/skills/video/SKILL.md +11 -25
- package/skills/video-studio/ATTRIBUTION.md +3 -0
- package/skills/video-studio/CHANGELOG.md +25 -0
- package/skills/video-studio/LICENSE-motionmaxxing +202 -0
- package/skills/video-studio/SKILL.md +115 -25
- package/skills/video-studio/references/audio.md +15 -0
- package/skills/video-studio/references/interview.md +75 -0
- package/skills/video-studio/references/step-2-plan.md +39 -7
- package/skills/video-studio/references/storyboard.md +3 -1
- package/skills/video-studio/references/tones.md +5 -5
- package/skills/video-studio/scripts/pyproject.toml +4 -2
- package/skills/video-studio/scripts/qa.mjs +85 -1
- package/skills/video-studio/scripts/qa.test.mjs +37 -1
- package/skills/video-studio/scripts/render-scenes.mjs +1 -0
- package/skills/video-studio/scripts/storyboard.schema.json +6 -1
- package/skills/video-studio/scripts/uv.lock +8 -5
- package/skills/video-studio-art-director/ATTRIBUTION.md +3 -0
- package/skills/video-studio-art-director/CHANGELOG.md +7 -0
- package/skills/video-studio-art-director/LICENSE-motionmaxxing +202 -0
- package/skills/video-studio-art-director/SKILL.md +42 -7
- package/skills/video-studio-composition/ATTRIBUTION.md +3 -0
- package/skills/video-studio-composition/CHANGELOG.md +7 -0
- package/skills/video-studio-composition/LICENSE-motionmaxxing +202 -0
- package/skills/video-studio-composition/SKILL.md +17 -4
- package/skills/video-studio-director/ATTRIBUTION.md +3 -0
- package/skills/video-studio-director/CHANGELOG.md +15 -0
- package/skills/video-studio-director/LICENSE-motionmaxxing +202 -0
- package/skills/video-studio-director/SKILL.md +105 -10
- package/skills/video-studio-motion/ATTRIBUTION.md +3 -0
- package/skills/video-studio-motion/CHANGELOG.md +8 -0
- package/skills/video-studio-motion/LICENSE-motionmaxxing +202 -0
- package/skills/video-studio-motion/SKILL.md +98 -5
- package/skills/video-studio-motion/references/kinetic-type.md +89 -0
- package/skills/video-studio-motion/references/ui-demo.md +59 -0
- package/skills/video-studio-review/CHANGELOG.md +11 -0
- package/skills/video-studio-review/SKILL.md +31 -5
- package/skills/video-studio-review/editor/index.html +21 -3
- package/skills/video-studio-review/references/apply-feedback.md +19 -0
- package/skills/video-studio-review/scripts/build-reel.mjs +35 -10
- package/skills/video-studio-review/scripts/build-reel.test.mjs +15 -0
- package/skills/video-studio-script/ATTRIBUTION.md +6 -0
- package/skills/video-studio-script/CHANGELOG.md +12 -0
- package/skills/video-studio-script/LICENSE-motionmaxxing +202 -0
- package/skills/video-studio-script/SKILL.md +41 -10
- package/skills/video-studio-showreel/CHANGELOG.md +21 -0
- package/skills/video-studio-showreel/SKILL.md +231 -0
- package/skills/video-studio-showreel/references/motion-design-pack-notes.md +28 -0
- package/skills/video-studio-showreel/references/zingage-breakdown.md +47 -0
- package/skills/video-studio-showreel/scripts/build.py +10 -0
- package/skills/video-studio-showreel/templates/showreel.html +264 -0
- package/skills/video-watcher/CHANGELOG.md +6 -0
- package/skills/video-watcher/SKILL.md +3 -3
- package/skills/video-watcher/scripts/setup.py +9 -7
- package/skills/trends/agents/openai.yaml +0 -8
- package/skills/trends/scripts/build-skill.sh +0 -39
- package/skills/trends/scripts/compare.sh +0 -61
- package/skills/trends/scripts/evaluate_search_quality.py +0 -573
- package/skills/trends/scripts/test-v1-vs-v2.sh +0 -220
- package/skills/trends/scripts/test_device_auth.py +0 -128
- 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())
|