merge: incorporate origin/main into community-mode branch

Resolved 10 conflicted files:
- VERSION/package.json: kept 0.12.0.0 (feature branch version)
- CHANGELOG.md: preserved both branch entry and main's new entries
- supabase/config.sh: kept GSTACK_WEB_URL, accepted TELEMETRY_ENDPOINT removal
- bin/gstack-{community-dashboard,telemetry-log,telemetry-sync,update-check}:
  took main's improved versions (edge function approach, safe cursor, UUID gen)
- supabase/functions/community-pulse: took main's count-based approach
- test/telemetry.test.ts: took main's structure with fingerprint field name

Post-merge fixes:
- Removed shadowed local RESOLVERS/functions in gen-skill-docs.ts (main's
  resolver imports now take precedence for tier-based preamble, coverage gates)
- Added 3 missing E2E_TIERS entries (ship-plan-*, review-plan-completion)
- Updated telemetry test to match current prompt text
- Regenerated all SKILL.md files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-03-24 21:01:19 -07:00
68 changed files with 5645 additions and 495 deletions

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# gstack-community-dashboard — community usage stats from Supabase
#
# Queries the Supabase REST API to show community-wide gstack usage:
# Calls the community-pulse edge function for aggregated stats:
# skill popularity, crash clusters, version distribution, retention.
#
# Env overrides (for testing):
@@ -30,46 +30,40 @@ if [ -z "$SUPABASE_URL" ] || [ -z "$ANON_KEY" ]; then
exit 0
fi
# ─── Helper: query Supabase REST API ─────────────────────────
query() {
local table="$1"
local params="${2:-}"
curl -sf --max-time 10 \
"${SUPABASE_URL}/rest/v1/${table}?${params}" \
-H "apikey: ${ANON_KEY}" \
-H "Authorization: Bearer ${ANON_KEY}" \
2>/dev/null || echo "[]"
}
# ─── Fetch aggregated stats from edge function ────────────────
DATA="$(curl -sf --max-time 15 \
"${SUPABASE_URL}/functions/v1/community-pulse" \
-H "apikey: ${ANON_KEY}" \
2>/dev/null || echo "{}")"
echo "gstack community dashboard"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# ─── Weekly active installs ──────────────────────────────────
WEEK_AGO="$(date -u -v-7d +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "")"
if [ -n "$WEEK_AGO" ]; then
# Direct REST query (replaces unreliable community-pulse edge function)
WEEKLY="$(curl -sf --max-time 10 \
"${SUPABASE_URL}/rest/v1/update_checks?select=install_fingerprint&checked_at=gte.${WEEK_AGO}&source=eq.live" \
-H "apikey: ${ANON_KEY}" \
-H "Authorization: Bearer ${ANON_KEY}" \
2>/dev/null | grep -o '"install_fingerprint":"[^"]*"' | sort -u | wc -l | tr -d ' ')"
WEEKLY="${WEEKLY:-0}"
WEEKLY="$(echo "$DATA" | grep -o '"weekly_active":[0-9]*' | grep -o '[0-9]*' || echo "0")"
CHANGE="$(echo "$DATA" | grep -o '"change_pct":[0-9-]*' | grep -o '[0-9-]*' || echo "0")"
echo "Weekly active installs: ${WEEKLY} unique"
echo ""
echo "Weekly active installs: ${WEEKLY}"
if [ "$CHANGE" -gt 0 ] 2>/dev/null; then
echo " Change: +${CHANGE}%"
elif [ "$CHANGE" -lt 0 ] 2>/dev/null; then
echo " Change: ${CHANGE}%"
fi
echo ""
# ─── Skill popularity (top 10) ───────────────────────────────
echo "Top skills (last 7 days)"
echo "────────────────────────"
# Query telemetry_events, group by skill
EVENTS="$(query "telemetry_events" "select=skill,gstack_version,session_id&event_type=eq.skill_run&event_timestamp=gte.${WEEK_AGO}&source=eq.live&limit=1000" 2>/dev/null || echo "[]")"
if [ "$EVENTS" != "[]" ] && [ -n "$EVENTS" ]; then
echo "$EVENTS" | grep -o '"skill":"[^"]*"' | awk -F'"' '{print $4}' | sort | uniq -c | sort -rn | head -10 | while read -r COUNT SKILL; do
printf " /%-20s %d runs\n" "$SKILL" "$COUNT"
# Parse top_skills array from JSON
SKILLS="$(echo "$DATA" | grep -o '"top_skills":\[[^]]*\]' || echo "")"
if [ -n "$SKILLS" ] && [ "$SKILLS" != '"top_skills":[]' ]; then
# Parse each object — handle any key order (JSONB doesn't preserve order)
echo "$SKILLS" | grep -o '{[^}]*}' | while read -r OBJ; do
SKILL="$(echo "$OBJ" | grep -o '"skill":"[^"]*"' | awk -F'"' '{print $4}')"
COUNT="$(echo "$OBJ" | grep -o '"count":[0-9]*' | grep -o '[0-9]*')"
[ -n "$SKILL" ] && [ -n "$COUNT" ] && printf " /%-20s %s runs\n" "$SKILL" "$COUNT"
done
else
echo " No data yet"
@@ -80,31 +74,13 @@ echo ""
echo "Top errors (last 7 days)"
echo "────────────────────────"
ERRORS="$(query "telemetry_events" "select=skill,error_class,error_message,failed_step,duration_s,session_id&outcome=eq.error&event_timestamp=gte.${WEEK_AGO}&source=eq.live&order=event_timestamp.desc&limit=200" 2>/dev/null || echo "[]")"
if [ "$ERRORS" != "[]" ] && [ -n "$ERRORS" ]; then
# Group by skill + error_class, show count and example message
echo "$ERRORS" | grep -o '"skill":"[^"]*"[^}]*"error_class":"[^"]*"' | \
sed 's/.*"skill":"//;s/".*"error_class":"/\t/' | sed 's/"$//' | \
sort | uniq -c | sort -rn | head -8 | while read -r COUNT COMBO; do
SKILL="$(echo "$COMBO" | cut -f1)"
ERR="$(echo "$COMBO" | cut -f2)"
# Find an example error_message for this combo
MSG="$(echo "$ERRORS" | grep -o "\"skill\":\"${SKILL}\"[^}]*\"error_message\":\"[^\"]*\"" | \
grep -o '"error_message":"[^"]*"' | head -1 | sed 's/"error_message":"//;s/"$//' || true)"
# Find an example failed_step
STEP="$(echo "$ERRORS" | grep -o "\"skill\":\"${SKILL}\"[^}]*\"failed_step\":\"[^\"]*\"" | \
grep -o '"failed_step":"[^"]*"' | head -1 | sed 's/"failed_step":"//;s/"$//' || true)"
printf " /%-12s %-18s %3d errors\n" "$SKILL" "${ERR:-unknown}" "$COUNT"
[ -n "$STEP" ] && printf " step: %s\n" "$STEP"
[ -n "$MSG" ] && printf " e.g.: %s\n" "$(echo "$MSG" | head -c 80)"
done
# Show how many unique sessions have errors
ERR_SESSIONS="$(echo "$ERRORS" | grep -o '"session_id":"[^"]*"' | sort -u | wc -l | tr -d ' ')"
echo ""
echo " ${ERR_SESSIONS} unique session(s) with errors"
CRASHES="$(echo "$DATA" | grep -o '"crashes":\[[^]]*\]' || echo "")"
if [ -n "$CRASHES" ] && [ "$CRASHES" != '"crashes":[]' ]; then
echo "$CRASHES" | grep -o '{[^}]*}' | head -5 | while read -r OBJ; do
ERR="$(echo "$OBJ" | grep -o '"error_class":"[^"]*"' | awk -F'"' '{print $4}')"
C="$(echo "$OBJ" | grep -o '"total_occurrences":[0-9]*' | grep -o '[0-9]*')"
[ -n "$ERR" ] && printf " %-30s %s occurrences\n" "$ERR" "${C:-?}"
done
else
echo " No errors reported"
fi
@@ -114,49 +90,17 @@ echo ""
echo "Version distribution (last 7 days)"
echo "───────────────────────────────────"
if [ "$EVENTS" != "[]" ] && [ -n "$EVENTS" ]; then
echo "$EVENTS" | grep -o '"gstack_version":"[^"]*"' | awk -F'"' '{print $4}' | sort | uniq -c | sort -rn | head -5 | while read -r COUNT VER; do
printf " v%-15s %d events\n" "$VER" "$COUNT"
VERSIONS="$(echo "$DATA" | grep -o '"versions":\[[^]]*\]' || echo "")"
if [ -n "$VERSIONS" ] && [ "$VERSIONS" != '"versions":[]' ]; then
echo "$VERSIONS" | grep -o '{[^}]*}' | head -5 | while read -r OBJ; do
VER="$(echo "$OBJ" | grep -o '"version":"[^"]*"' | awk -F'"' '{print $4}')"
COUNT="$(echo "$OBJ" | grep -o '"count":[0-9]*' | grep -o '[0-9]*')"
[ -n "$VER" ] && [ -n "$COUNT" ] && printf " v%-15s %s events\n" "$VER" "$COUNT"
done
else
echo " No data yet"
fi
# ─── Sessions (distinct session_id, works for all tiers) ────
echo "Sessions (last 7 days)"
echo "──────────────────────"
if [ "$EVENTS" != "[]" ] && [ -n "$EVENTS" ]; then
SESSION_COUNT="$(echo "$EVENTS" | grep -o '"session_id":"[^"]*"' | sort -u | wc -l | tr -d ' ')"
echo " ${SESSION_COUNT} unique sessions"
else
echo " No session data"
fi
echo ""
# ─── Skill recommendations ─────────────────────────────────
# Fetch top skills for recommendations
TOP_SKILLS="$(echo "$EVENTS" | grep -o '"skill":"[^"]*"' | awk -F'"' '{print $4}' | sort | uniq -c | sort -rn | head -3 | awk '{print $2}' | tr '\n' ',' | sed 's/,$//')"
if [ -n "$TOP_SKILLS" ]; then
RECS="$(curl -sf --max-time 10 \
"${SUPABASE_URL}/functions/v1/community-recommendations?skills=${TOP_SKILLS}" \
-H "Authorization: Bearer ${ANON_KEY}" \
2>/dev/null || echo '{"recommendations":[]}')"
REC_LIST="$(echo "$RECS" | grep -o '"skill":"[^"]*"' | awk -F'"' '{print $4}')"
REC_REASONS="$(echo "$RECS" | grep -o '"reason":"[^"]*"' | awk -F'"' '{print $4}')"
if [ -n "$REC_LIST" ]; then
echo "Skills you might like"
echo "─────────────────────"
paste <(echo "$REC_LIST") <(echo "$REC_REASONS") 2>/dev/null | while IFS=$'\t' read -r SKILL REASON; do
[ -z "$SKILL" ] && continue
printf " /%-20s %s\n" "$SKILL" "${REASON:-}"
done
echo ""
fi
fi
echo "For local analytics: gstack-analytics"
echo "For benchmarks: gstack-community-benchmarks"