cue-ai 0.4.1 → 0.5.0
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/README.md +225 -8
- package/package.json +3 -2
- package/profiles/affiliate/profile.yaml +67 -0
- package/profiles/go-api/profile.yaml +8 -0
- package/profiles/marketing/profile.yaml +4 -1
- package/profiles/nextjs/profile.yaml +8 -0
- package/profiles/python-api/profile.yaml +8 -0
- package/profiles/rust/profile.yaml +8 -0
- package/profiles/video/profile.yaml +10 -0
- package/resources/mcps/configs/claude_runtime.sanitized.json +15 -1
- package/resources/skills/skills/design/headless-gif-demo/SKILL.md +57 -12
- package/resources/skills/skills/meta/awesome-list-submit/SKILL.md +463 -0
- package/src/commands/_index.ts +44 -0
- package/src/commands/ai-score.e2e.test.ts +113 -0
- package/src/commands/ai.ts +179 -0
- package/src/commands/benchmark.ts +258 -0
- package/src/commands/clean.ts +109 -0
- package/src/commands/completions.ts +4 -0
- package/src/commands/cost.ts +77 -3
- package/src/commands/diff.ts +105 -0
- package/src/commands/import-profile.ts +28 -5
- package/src/commands/launch.e2e.test.ts +119 -0
- package/src/commands/launch.ts +19 -0
- package/src/commands/lock.ts +21 -1
- package/src/commands/marketplace.ts +88 -3
- package/src/commands/migrate.ts +100 -0
- package/src/commands/optimizer.ts +2 -2
- package/src/commands/replay-whatif.ts +142 -0
- package/src/commands/replay.ts +6 -0
- package/src/commands/score.ts +304 -0
- package/src/commands/security.ts +47 -7
- package/src/commands/shell.ts +17 -0
- package/src/commands/skills.ts +2 -2
- package/src/commands/status.ts +14 -0
- package/src/commands/suggest.ts +170 -0
- package/src/commands/upgrade.ts +154 -0
- package/src/index.ts +24 -1
- package/src/lib/analytics.ts +73 -1
- package/src/lib/auto-detect.ts +38 -5
- package/src/lib/cache.ts +47 -6
- package/src/lib/runtime-materializer.test.ts +22 -11
- package/src/lib/runtime-materializer.ts +58 -15
- package/src/lib/star-prompt.ts +10 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: awesome-list-submit
|
|
3
|
+
description: >-
|
|
4
|
+
When user says "submit to awesome lists", "add to awesome repos",
|
|
5
|
+
"promote on GitHub lists", or "find lists for this project" — auto-detect
|
|
6
|
+
project metadata, find relevant awesome-* repos, check for duplicates,
|
|
7
|
+
match entry format, draft PRs, and track submissions.
|
|
8
|
+
tags: [meta, marketing, github, promotion, outreach]
|
|
9
|
+
category: meta
|
|
10
|
+
version: 2.0.0
|
|
11
|
+
requires_mcps: []
|
|
12
|
+
allowed-tools: Bash(gh:*), Bash(curl:*), Bash(git:*), Bash(jq:*), WebSearch, Read(*), Write(*)
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Submit to Awesome Lists (v2)
|
|
16
|
+
|
|
17
|
+
Auto-detect project info, find relevant awesome-* repos, and submit PRs — with dedup, format matching, and submission tracking.
|
|
18
|
+
|
|
19
|
+
## When to activate
|
|
20
|
+
|
|
21
|
+
- User says "submit to awesome lists" or "add to awesome repos"
|
|
22
|
+
- User says "promote on GitHub" or "get listed"
|
|
23
|
+
- User says "find lists for this project"
|
|
24
|
+
|
|
25
|
+
## Step 1: Auto-detect project metadata
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Read from package.json, Cargo.toml, pyproject.toml, or README
|
|
29
|
+
PROJECT_NAME=$(jq -r '.name // empty' package.json 2>/dev/null || basename "$PWD")
|
|
30
|
+
DESCRIPTION=$(jq -r '.description // empty' package.json 2>/dev/null)
|
|
31
|
+
HOMEPAGE=$(jq -r '.homepage // empty' package.json 2>/dev/null)
|
|
32
|
+
REPO_URL=$(gh repo view --json url -q '.url' 2>/dev/null)
|
|
33
|
+
STARS=$(gh repo view --json stargazerCount -q '.stargazerCount' 2>/dev/null)
|
|
34
|
+
LICENSE=$(jq -r '.license // empty' package.json 2>/dev/null)
|
|
35
|
+
TOPICS=$(gh repo view --json repositoryTopics -q '.repositoryTopics[].name' 2>/dev/null | tr '\n' ',')
|
|
36
|
+
|
|
37
|
+
echo "Project: $PROJECT_NAME"
|
|
38
|
+
echo "Description: $DESCRIPTION"
|
|
39
|
+
echo "URL: $REPO_URL"
|
|
40
|
+
echo "Stars: $STARS"
|
|
41
|
+
echo "License: $LICENSE"
|
|
42
|
+
echo "Topics: $TOPICS"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Step 2: Find relevant awesome lists
|
|
46
|
+
|
|
47
|
+
Search using the project's topics and keywords:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Search by each topic
|
|
51
|
+
for topic in $(echo "$TOPICS" | tr ',' '\n'); do
|
|
52
|
+
gh search repos "awesome-$topic" --sort stars --limit 3 --json fullName,stargazersCount,description
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
# Also search by project domain keywords from description
|
|
56
|
+
KEYWORDS=$(echo "$DESCRIPTION" | tr ' ' '\n' | grep -E '^[a-z]{4,}$' | head -5)
|
|
57
|
+
for kw in $KEYWORDS; do
|
|
58
|
+
gh search repos "awesome $kw" --sort stars --limit 3 --json fullName,stargazersCount,description
|
|
59
|
+
done
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Filter: only lists with >100 stars, not archived, updated in last 6 months.
|
|
63
|
+
|
|
64
|
+
## Step 3: Check for duplicates
|
|
65
|
+
|
|
66
|
+
Before submitting, verify the project isn't already listed:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
check_already_listed() {
|
|
70
|
+
local repo="$1"
|
|
71
|
+
# Check README for our repo URL
|
|
72
|
+
gh api "repos/$repo/readme" --jq '.content' | base64 -d | grep -qi "$PROJECT_NAME\|$REPO_URL"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# Check open PRs too
|
|
76
|
+
check_pending_pr() {
|
|
77
|
+
local repo="$1"
|
|
78
|
+
gh pr list --repo "$repo" --search "$PROJECT_NAME" --state open --json number | jq 'length'
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Step 4: Match entry format
|
|
83
|
+
|
|
84
|
+
Parse existing entries to match the list's style:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
detect_format() {
|
|
88
|
+
local readme="$1"
|
|
89
|
+
if echo "$readme" | grep -q "^|.*|.*|"; then
|
|
90
|
+
echo "table" # Markdown table format
|
|
91
|
+
elif echo "$readme" | grep -q "^- \["; then
|
|
92
|
+
echo "bullet-link" # - [name](url) — description
|
|
93
|
+
elif echo "$readme" | grep -q "^- \*\*\["; then
|
|
94
|
+
echo "bullet-bold" # - **[name](url)** - description
|
|
95
|
+
fi
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Format templates:
|
|
100
|
+
- **table**: `| [name](url) | description |`
|
|
101
|
+
- **bullet-link**: `- [name](url) — description`
|
|
102
|
+
- **bullet-bold**: `- **[name](url)** - description`
|
|
103
|
+
|
|
104
|
+
## Step 5: Submit PRs
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
submit_to_list() {
|
|
108
|
+
local target_repo="$1"
|
|
109
|
+
local section="$2"
|
|
110
|
+
local entry="$3"
|
|
111
|
+
|
|
112
|
+
# Fork
|
|
113
|
+
gh repo fork "$target_repo" --clone --remote 2>/dev/null
|
|
114
|
+
local dir=$(basename "$target_repo")
|
|
115
|
+
cd "/tmp/$dir"
|
|
116
|
+
|
|
117
|
+
# Branch
|
|
118
|
+
git checkout -b "add-$PROJECT_NAME"
|
|
119
|
+
|
|
120
|
+
# Find insertion point (alphabetical within section)
|
|
121
|
+
# Insert the entry at the right position
|
|
122
|
+
# ... (agent edits README.md)
|
|
123
|
+
|
|
124
|
+
git add README.md
|
|
125
|
+
git commit -m "Add $PROJECT_NAME — $DESCRIPTION"
|
|
126
|
+
git push -u origin "add-$PROJECT_NAME"
|
|
127
|
+
|
|
128
|
+
# Create PR
|
|
129
|
+
gh pr create \
|
|
130
|
+
--title "Add $PROJECT_NAME" \
|
|
131
|
+
--body "**[$PROJECT_NAME]($REPO_URL)** — $DESCRIPTION
|
|
132
|
+
|
|
133
|
+
⭐ $STARS stars | 📜 $LICENSE license | 🔧 Install: \`npm i -g $PROJECT_NAME\`
|
|
134
|
+
|
|
135
|
+
$REPO_URL" \
|
|
136
|
+
--repo "$target_repo"
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Step 6: Track submissions
|
|
141
|
+
|
|
142
|
+
Save submission history to avoid re-submitting:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
HISTORY_FILE="$HOME/.config/cue/awesome-submissions.json"
|
|
146
|
+
|
|
147
|
+
record_submission() {
|
|
148
|
+
local target="$1" pr_url="$2" status="$3"
|
|
149
|
+
jq --arg t "$target" --arg p "$pr_url" --arg s "$status" --arg d "$(date -I)" \
|
|
150
|
+
'. += [{"list": $t, "pr": $p, "status": $s, "date": $d}]' \
|
|
151
|
+
"$HISTORY_FILE" > "$HISTORY_FILE.tmp" && mv "$HISTORY_FILE.tmp" "$HISTORY_FILE"
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# Check before submitting
|
|
155
|
+
already_submitted() {
|
|
156
|
+
local target="$1"
|
|
157
|
+
jq -e --arg t "$target" '.[] | select(.list == $t)' "$HISTORY_FILE" >/dev/null 2>&1
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Step 7: Report results
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
📋 Awesome List Submissions for "cue":
|
|
165
|
+
|
|
166
|
+
✅ PR #442 opened: rohitg00/awesome-claude-code-toolkit
|
|
167
|
+
https://github.com/rohitg00/awesome-claude-code-toolkit/pull/442
|
|
168
|
+
|
|
169
|
+
✅ PR #767 opened: travisvn/awesome-claude-skills
|
|
170
|
+
https://github.com/travisvn/awesome-claude-skills/pull/767
|
|
171
|
+
|
|
172
|
+
⏭️ Skipped: hesreallyhim/awesome-claude-code (repo restructuring)
|
|
173
|
+
⏭️ Skipped: punkpeye/awesome-mcp-servers (already listed)
|
|
174
|
+
|
|
175
|
+
📊 Summary: 2 PRs opened, 2 skipped, 0 failed
|
|
176
|
+
📁 History saved to ~/.config/cue/awesome-submissions.json
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Rules
|
|
180
|
+
|
|
181
|
+
- Auto-detect everything — don't ask user for metadata that's in package.json
|
|
182
|
+
- Always check for duplicates before submitting (README + open PRs)
|
|
183
|
+
- Match the existing format exactly (table vs bullet, alphabetical order)
|
|
184
|
+
- Only submit to lists with >100 stars and recent activity
|
|
185
|
+
- Track all submissions to prevent re-submitting
|
|
186
|
+
- Max 5 submissions per session
|
|
187
|
+
- Show the PR URLs so user can track acceptance
|
|
188
|
+
- If a list is archived or restructuring, skip with explanation
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Advanced: Competitor Analysis
|
|
193
|
+
|
|
194
|
+
Find where competing tools are listed and submit to those same lists:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
COMPETITORS="claude-code-switcher skillport agent-skills-cli agent-skill-manager skillshub"
|
|
198
|
+
|
|
199
|
+
find_gaps() {
|
|
200
|
+
local our_listings=$(gh search code "$PROJECT_NAME" --filename README.md --limit 50 --json repository | jq -r '.[].repository.fullName')
|
|
201
|
+
for comp in $COMPETITORS; do
|
|
202
|
+
gh search code "$comp" --filename README.md --limit 20 --json repository \
|
|
203
|
+
| jq -r '.[].repository.fullName' \
|
|
204
|
+
| grep -i "awesome\|list\|collection" \
|
|
205
|
+
| while read repo; do
|
|
206
|
+
echo "$our_listings" | grep -q "$repo" || echo "GAP: $repo (has $comp, missing us)"
|
|
207
|
+
done
|
|
208
|
+
done
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Present as:
|
|
213
|
+
```
|
|
214
|
+
🔍 Competitor Analysis — found 3 gaps where competitors are listed but we aren't.
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Advanced: Custom PR Body Templates
|
|
220
|
+
|
|
221
|
+
Auto-detect list audience and use the right pitch:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
detect_list_type() {
|
|
225
|
+
local name="$1" desc="$2"
|
|
226
|
+
if echo "$name $desc" | grep -qi "mcp\|model.context.protocol"; then echo "mcp"
|
|
227
|
+
elif echo "$name $desc" | grep -qi "skill\|agent"; then echo "ai-agents"
|
|
228
|
+
elif echo "$name $desc" | grep -qi "cli\|command.line"; then echo "cli"
|
|
229
|
+
else echo "dev-tools"
|
|
230
|
+
fi
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Templates per audience:
|
|
235
|
+
- **ai-agents**: Lead with the problem (context overload), show 10-agent support
|
|
236
|
+
- **cli**: Lead with Unix philosophy, sub-5ms overhead, no daemon
|
|
237
|
+
- **mcp**: Lead with per-project MCP scoping, inheritance
|
|
238
|
+
- **skills**: Lead with 110+ bundled skills, npx skill packs
|
|
239
|
+
- **dev-tools**: Lead with developer experience, one-command install
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Advanced: Screenshot/GIF in PR Body
|
|
244
|
+
|
|
245
|
+
Auto-find and embed visual assets:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
find_demo_assets() {
|
|
249
|
+
for f in docs/assets/demo.gif assets/demo.gif \
|
|
250
|
+
docs/assets/terminal-optimizer.svg docs/assets/hero.svg; do
|
|
251
|
+
[ -f "$f" ] && echo "/main/$f)" && return
|
|
252
|
+
done
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Inject into every PR body — maintainers see what the tool does without clicking through. Visual PRs get merged 2-3x faster.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Advanced: Cross-reference After Merge
|
|
261
|
+
|
|
262
|
+
When one PR is merged, comment on pending PRs with social proof:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
# After a merge is detected:
|
|
266
|
+
gh pr comment $PENDING_PR --repo "$list" \
|
|
267
|
+
--body "👋 Friendly bump — recently accepted into $MERGED_LIST. Happy to adjust format if needed!"
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Advanced: Timing
|
|
273
|
+
|
|
274
|
+
Submit Tue-Thu during business hours for fastest maintainer response. Skip weekends and Mondays.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Advanced: Automated Weekly Status Check
|
|
279
|
+
|
|
280
|
+
Set up a cron/scheduled check for PR status updates:
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
# Run weekly: cue awesome-submit --check-status
|
|
284
|
+
check_all_submissions() {
|
|
285
|
+
jq -c '.[] | select(.status == "pending")' "$HISTORY_FILE" | while read entry; do
|
|
286
|
+
local list=$(echo "$entry" | jq -r '.list')
|
|
287
|
+
local pr=$(echo "$entry" | jq -r '.pr')
|
|
288
|
+
local pr_num=$(basename "$pr")
|
|
289
|
+
|
|
290
|
+
local state=$(gh pr view "$pr_num" --repo "$list" --json state -q '.state' 2>/dev/null)
|
|
291
|
+
case "$state" in
|
|
292
|
+
MERGED)
|
|
293
|
+
echo "🎉 MERGED: $list (#$pr_num)"
|
|
294
|
+
update_status "$list" "merged"
|
|
295
|
+
;;
|
|
296
|
+
CLOSED)
|
|
297
|
+
echo "❌ CLOSED: $list (#$pr_num)"
|
|
298
|
+
local reason=$(gh pr view "$pr_num" --repo "$list" --json comments -q '.comments[-1].body' 2>/dev/null)
|
|
299
|
+
echo " Reason: $reason"
|
|
300
|
+
update_status "$list" "closed"
|
|
301
|
+
;;
|
|
302
|
+
OPEN)
|
|
303
|
+
local age=$(( ($(date +%s) - $(date -d "$(echo "$entry" | jq -r '.date')" +%s)) / 86400 ))
|
|
304
|
+
if [ "$age" -gt 14 ]; then
|
|
305
|
+
echo "⏰ STALE ($age days): $list (#$pr_num) — consider bumping"
|
|
306
|
+
fi
|
|
307
|
+
;;
|
|
308
|
+
esac
|
|
309
|
+
done
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Advanced: Release-triggered Discovery
|
|
316
|
+
|
|
317
|
+
GitHub Action that runs on every release tag:
|
|
318
|
+
|
|
319
|
+
```yaml
|
|
320
|
+
# .github/workflows/awesome-submit.yml
|
|
321
|
+
name: Find new awesome lists
|
|
322
|
+
on:
|
|
323
|
+
release:
|
|
324
|
+
types: [published]
|
|
325
|
+
jobs:
|
|
326
|
+
discover:
|
|
327
|
+
runs-on: ubuntu-latest
|
|
328
|
+
steps:
|
|
329
|
+
- uses: actions/checkout@v4
|
|
330
|
+
- run: |
|
|
331
|
+
# Find lists created since last release that match our topics
|
|
332
|
+
SINCE=$(gh release list --limit 2 --json publishedAt -q '.[1].publishedAt')
|
|
333
|
+
gh search repos "awesome claude-code" --sort updated --json fullName,createdAt \
|
|
334
|
+
| jq --arg since "$SINCE" '[.[] | select(.createdAt > $since)]'
|
|
335
|
+
# Output as issue for manual review
|
|
336
|
+
- run: |
|
|
337
|
+
gh issue create --title "New awesome lists to submit to" \
|
|
338
|
+
--body "$(cat /tmp/new-lists.md)" --label "marketing"
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Advanced: Maintainer Relationship DB
|
|
344
|
+
|
|
345
|
+
Track maintainer behavior for smarter targeting:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
MAINTAINER_DB="$HOME/.config/cue/awesome-maintainers.json"
|
|
349
|
+
|
|
350
|
+
# After each PR resolution, record maintainer response
|
|
351
|
+
record_maintainer() {
|
|
352
|
+
local repo="$1" outcome="$2" days_to_respond="$3"
|
|
353
|
+
jq --arg r "$repo" --arg o "$outcome" --arg d "$days_to_respond" \
|
|
354
|
+
'.[$r] = (.[$r] // {}) | .[$r].history += [{"outcome": $o, "days": ($d|tonumber)}] | .[$r].avg_days = ([.[$r].history[].days] | add / length)' \
|
|
355
|
+
"$MAINTAINER_DB" > "$MAINTAINER_DB.tmp" && mv "$MAINTAINER_DB.tmp" "$MAINTAINER_DB"
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
# Prioritize responsive maintainers
|
|
359
|
+
rank_targets() {
|
|
360
|
+
jq -r 'to_entries | sort_by(.value.avg_days) | .[] | "\(.value.avg_days)d avg — \(.key)"' "$MAINTAINER_DB"
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Advanced: A/B Test PR Titles
|
|
367
|
+
|
|
368
|
+
Rotate title formats and track which gets merged:
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
PR_TITLE_VARIANTS=(
|
|
372
|
+
"Add $PROJECT_NAME"
|
|
373
|
+
"Add $PROJECT_NAME — $SHORT_DESC"
|
|
374
|
+
"Add $PROJECT_NAME ($STARS+ stars)"
|
|
375
|
+
"Add $PROJECT_NAME: $ONE_LINE_VALUE_PROP"
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
select_title() {
|
|
379
|
+
# Pick based on what's worked before
|
|
380
|
+
local best=$(jq -r '[.[] | select(.status=="merged")] | group_by(.title_style) | sort_by(-length) | .[0][0].title_style // empty' "$HISTORY_FILE")
|
|
381
|
+
if [ -n "$best" ]; then
|
|
382
|
+
echo "${PR_TITLE_VARIANTS[$best]}"
|
|
383
|
+
else
|
|
384
|
+
# Rotate through variants
|
|
385
|
+
local idx=$(( $(jq 'length' "$HISTORY_FILE") % ${#PR_TITLE_VARIANTS[@]} ))
|
|
386
|
+
echo "${PR_TITLE_VARIANTS[$idx]}"
|
|
387
|
+
fi
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## Advanced: Auto-generate Comparison Table Rows
|
|
394
|
+
|
|
395
|
+
Some lists have feature comparison tables. Auto-fill cue's row:
|
|
396
|
+
|
|
397
|
+
```bash
|
|
398
|
+
detect_comparison_table() {
|
|
399
|
+
local readme="$1"
|
|
400
|
+
# Find tables with competitor names
|
|
401
|
+
if echo "$readme" | grep -q "claude-code-switcher\|skillport\|agent-skill"; then
|
|
402
|
+
echo "comparison-table"
|
|
403
|
+
fi
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
generate_comparison_row() {
|
|
407
|
+
# cue's features for common comparison dimensions
|
|
408
|
+
cat <<'EOF'
|
|
409
|
+
| **cue** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
410
|
+
EOF
|
|
411
|
+
# Columns: skills | MCPs | plugins | profiles | per-dir | isolation | inherit
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
## Advanced: Backlink Monitoring
|
|
418
|
+
|
|
419
|
+
Verify links stay live after merge:
|
|
420
|
+
|
|
421
|
+
```bash
|
|
422
|
+
# Monthly check: are our links still in the READMEs?
|
|
423
|
+
monitor_backlinks() {
|
|
424
|
+
jq -c '.[] | select(.status == "merged")' "$HISTORY_FILE" | while read entry; do
|
|
425
|
+
local list=$(echo "$entry" | jq -r '.list')
|
|
426
|
+
local still_listed=$(gh api "repos/$list/readme" --jq '.content' | base64 -d | grep -c "$REPO_URL")
|
|
427
|
+
if [ "$still_listed" -eq 0 ]; then
|
|
428
|
+
echo "⚠️ REMOVED from $list — re-submit or investigate"
|
|
429
|
+
fi
|
|
430
|
+
done
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Advanced: Multi-language Submissions
|
|
437
|
+
|
|
438
|
+
Submit to non-English awesome lists with translated descriptions:
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
TRANSLATIONS=(
|
|
442
|
+
"zh:代理配置管理器 — 按目录隔离技能、MCP和插件,支持继承和缓存"
|
|
443
|
+
"ja:エージェントプロファイルマネージャー — ディレクトリごとにスキル・MCP・プラグインを分離"
|
|
444
|
+
"ko:에이전트 프로필 관리자 — 디렉토리별 스킬/MCP/플러그인 격리"
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
# Known non-English lists
|
|
448
|
+
NON_EN_LISTS=(
|
|
449
|
+
"yzfly/Awesome-MCP-ZH" # Chinese MCP list (7k stars)
|
|
450
|
+
"punkpeye/awesome-mcp-servers" # Has i18n section
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
get_translated_description() {
|
|
454
|
+
local lang="$1"
|
|
455
|
+
for t in "${TRANSLATIONS[@]}"; do
|
|
456
|
+
if [[ "$t" == "$lang:"* ]]; then
|
|
457
|
+
echo "${t#*:}"
|
|
458
|
+
return
|
|
459
|
+
fi
|
|
460
|
+
done
|
|
461
|
+
echo "$DESCRIPTION" # fallback to English
|
|
462
|
+
}
|
|
463
|
+
```
|
package/src/commands/_index.ts
CHANGED
|
@@ -33,6 +33,10 @@ export const COMMANDS = {
|
|
|
33
33
|
summary: "Print a tree of installed skills/plugins grouped by domain (A10/A11)",
|
|
34
34
|
load: () => import("./scan"),
|
|
35
35
|
},
|
|
36
|
+
score: {
|
|
37
|
+
summary: "Profile efficiency score (A+ to F) with SVG badge",
|
|
38
|
+
load: () => import("./score"),
|
|
39
|
+
},
|
|
36
40
|
doctor: {
|
|
37
41
|
summary: "Diff declared profile vs actual disk state; --fix repairs (A15)",
|
|
38
42
|
load: () => import("./doctor"),
|
|
@@ -89,6 +93,10 @@ export const COMMANDS = {
|
|
|
89
93
|
summary: "Show what a skill does — description, summary, size",
|
|
90
94
|
load: () => import("./ask"),
|
|
91
95
|
},
|
|
96
|
+
ai: {
|
|
97
|
+
summary: "Create a profile from natural language description",
|
|
98
|
+
load: () => import("./ai"),
|
|
99
|
+
},
|
|
92
100
|
builtin: {
|
|
93
101
|
summary: "Manage built-in skills shared across all profiles",
|
|
94
102
|
load: () => import("./builtin"),
|
|
@@ -209,14 +217,34 @@ export const COMMANDS = {
|
|
|
209
217
|
summary: "Self-update: git pull + bun install + sync",
|
|
210
218
|
load: () => import("./update"),
|
|
211
219
|
},
|
|
220
|
+
upgrade: {
|
|
221
|
+
summary: "Pull new skills/profiles from the registry",
|
|
222
|
+
load: () => import("./upgrade"),
|
|
223
|
+
},
|
|
212
224
|
completions: {
|
|
213
225
|
summary: "Output shell completion script (bash/zsh)",
|
|
214
226
|
load: () => import("./completions"),
|
|
215
227
|
},
|
|
228
|
+
clean: {
|
|
229
|
+
summary: "Prune stale runtimes, old cache, reclaim disk space",
|
|
230
|
+
load: () => import("./clean"),
|
|
231
|
+
},
|
|
232
|
+
migrate: {
|
|
233
|
+
summary: "Auto-migrate profiles to latest schema version",
|
|
234
|
+
load: () => import("./migrate"),
|
|
235
|
+
},
|
|
236
|
+
suggest: {
|
|
237
|
+
summary: "Profile recommendations based on usage patterns",
|
|
238
|
+
load: () => import("./suggest"),
|
|
239
|
+
},
|
|
216
240
|
watch: {
|
|
217
241
|
summary: "Auto-switch profile notification on cd (shell hook)",
|
|
218
242
|
load: () => import("./watch"),
|
|
219
243
|
},
|
|
244
|
+
benchmark: {
|
|
245
|
+
summary: "Measure profile efficiency: tokens, skill usage, cost",
|
|
246
|
+
load: () => import("./benchmark"),
|
|
247
|
+
},
|
|
220
248
|
tree: {
|
|
221
249
|
summary: "Visualize profile inheritance tree with resources",
|
|
222
250
|
load: () => import("./tree"),
|
|
@@ -225,6 +253,22 @@ export const COMMANDS = {
|
|
|
225
253
|
summary: "Show GitHub repos that provide skills for a profile",
|
|
226
254
|
load: () => import("./sources"),
|
|
227
255
|
},
|
|
256
|
+
sponsor: {
|
|
257
|
+
summary: "Star the repo / show support links",
|
|
258
|
+
load: async () => ({
|
|
259
|
+
run: async () => {
|
|
260
|
+
const { maybePromptStar } = await import("../lib/star-prompt");
|
|
261
|
+
// Force the prompt regardless of session count
|
|
262
|
+
const { existsSync, unlinkSync } = await import("node:fs");
|
|
263
|
+
const { join } = await import("node:path");
|
|
264
|
+
const { homedir } = await import("node:os");
|
|
265
|
+
const flag = join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "cue", ".star-prompted");
|
|
266
|
+
if (existsSync(flag)) unlinkSync(flag);
|
|
267
|
+
await maybePromptStar();
|
|
268
|
+
return 0;
|
|
269
|
+
},
|
|
270
|
+
}),
|
|
271
|
+
},
|
|
228
272
|
"migrate-symlinks": {
|
|
229
273
|
summary: "Rewrite ~/.codex and ~/.claude-accounts symlinks from soul/ to cue/",
|
|
230
274
|
load: () => import("./migrate-symlinks"),
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2e tests for `cue ai` and `cue score`.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
const CUE_BIN = join(import.meta.dir, "../index.ts");
|
|
10
|
+
|
|
11
|
+
function cue(args: string[]): { status: number; stdout: string; stderr: string } {
|
|
12
|
+
const res = spawnSync("bun", ["run", CUE_BIN, ...args], {
|
|
13
|
+
encoding: "utf8",
|
|
14
|
+
timeout: 15000,
|
|
15
|
+
});
|
|
16
|
+
return { status: res.status ?? 1, stdout: res.stdout ?? "", stderr: res.stderr ?? "" };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("cue ai", () => {
|
|
20
|
+
test("matches python-api for python/fastapi description", () => {
|
|
21
|
+
const res = cue(["ai", "python fastapi sqlalchemy"]);
|
|
22
|
+
expect(res.status).toBe(0);
|
|
23
|
+
expect(res.stdout).toContain("python-api");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("matches rust for rust/cargo description", () => {
|
|
27
|
+
const res = cue(["ai", "rust cargo async cli tool"]);
|
|
28
|
+
expect(res.status).toBe(0);
|
|
29
|
+
expect(res.stdout).toContain("rust");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("matches frontend for react/tailwind description", () => {
|
|
33
|
+
const res = cue(["ai", "react tailwind vite frontend"]);
|
|
34
|
+
expect(res.status).toBe(0);
|
|
35
|
+
expect(res.stdout).toContain("frontend");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("matches nextjs for next.js description", () => {
|
|
39
|
+
const res = cue(["ai", "next.js app router server components"]);
|
|
40
|
+
expect(res.status).toBe(0);
|
|
41
|
+
expect(res.stdout).toContain("nextjs");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("matches go-api for golang description", () => {
|
|
45
|
+
const res = cue(["ai", "golang gin api"]);
|
|
46
|
+
expect(res.status).toBe(0);
|
|
47
|
+
expect(res.stdout).toContain("go-api");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("matches video for ffmpeg/video description", () => {
|
|
51
|
+
const res = cue(["ai", "video ffmpeg frames"]);
|
|
52
|
+
expect(res.status).toBe(0);
|
|
53
|
+
expect(res.stdout).toContain("video");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("shows help with no args", () => {
|
|
57
|
+
const res = cue(["ai"]);
|
|
58
|
+
expect(res.status).toBe(0);
|
|
59
|
+
expect(res.stdout).toContain("Usage");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("handles no-match gracefully", () => {
|
|
63
|
+
const res = cue(["ai", "xyznonexistent"]);
|
|
64
|
+
expect(res.status).toBe(0);
|
|
65
|
+
expect(res.stdout).toContain("No matching");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("cue score", () => {
|
|
70
|
+
test("scores a specific profile", () => {
|
|
71
|
+
const res = cue(["score", "--profile", "core"]);
|
|
72
|
+
expect(res.status).toBe(0);
|
|
73
|
+
expect(res.stdout).toContain("core");
|
|
74
|
+
expect(res.stdout).toContain("/100");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("--all shows all profiles ranked", () => {
|
|
78
|
+
const res = cue(["score", "--all"]);
|
|
79
|
+
expect(res.status).toBe(0);
|
|
80
|
+
expect(res.stdout).toContain("Profile Scores");
|
|
81
|
+
expect(res.stdout).toContain("core");
|
|
82
|
+
expect(res.stdout).toContain("backend");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("--json returns valid JSON", () => {
|
|
86
|
+
const res = cue(["score", "--profile", "rust", "--json"]);
|
|
87
|
+
expect(res.status).toBe(0);
|
|
88
|
+
const data = JSON.parse(res.stdout);
|
|
89
|
+
expect(data.profile).toBe("rust");
|
|
90
|
+
expect(data.grade).toMatch(/^[A-F][+-]?$/);
|
|
91
|
+
expect(data.score).toBeGreaterThanOrEqual(0);
|
|
92
|
+
expect(data.score).toBeLessThanOrEqual(100);
|
|
93
|
+
expect(data.tokens).toBeGreaterThan(0);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("--markdown outputs shields.io badge", () => {
|
|
97
|
+
const res = cue(["score", "--profile", "backend", "--markdown"]);
|
|
98
|
+
expect(res.status).toBe(0);
|
|
99
|
+
expect(res.stdout).toContain("img.shields.io/badge/cue_score");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("--badge generates SVG file", () => {
|
|
103
|
+
const res = cue(["score", "--profile", "core", "--badge", "/tmp/cue-test-badge.svg"]);
|
|
104
|
+
expect(res.status).toBe(0);
|
|
105
|
+
expect(res.stdout).toContain("Badge saved");
|
|
106
|
+
|
|
107
|
+
const { readFileSync, unlinkSync } = require("node:fs");
|
|
108
|
+
const svg = readFileSync("/tmp/cue-test-badge.svg", "utf8");
|
|
109
|
+
expect(svg).toContain("<svg");
|
|
110
|
+
expect(svg).toContain("core");
|
|
111
|
+
unlinkSync("/tmp/cue-test-badge.svg");
|
|
112
|
+
});
|
|
113
|
+
});
|