@runchr/gstack-antigravity 0.1.0 → 0.1.2
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.
Potentially problematic release.
This version of @runchr/gstack-antigravity might be problematic. Click here for more details.
- package/.agents/skills/gstack/.agents/skills/gstack/SKILL.md +651 -0
- package/.agents/skills/gstack/.agents/skills/gstack-autoplan/SKILL.md +678 -0
- package/.agents/skills/gstack/.agents/skills/gstack-benchmark/SKILL.md +482 -0
- package/.agents/skills/gstack/.agents/skills/gstack-browse/SKILL.md +511 -0
- package/.agents/skills/gstack/.agents/skills/gstack-canary/SKILL.md +486 -0
- package/.agents/skills/gstack/.agents/skills/gstack-careful/SKILL.md +50 -0
- package/.agents/skills/gstack/.agents/skills/gstack-cso/SKILL.md +607 -0
- package/.agents/skills/gstack/.agents/skills/gstack-design-consultation/SKILL.md +615 -0
- package/.agents/skills/gstack/.agents/skills/gstack-design-review/SKILL.md +988 -0
- package/.agents/skills/gstack/.agents/skills/gstack-document-release/SKILL.md +604 -0
- package/.agents/skills/gstack/.agents/skills/gstack-freeze/SKILL.md +67 -0
- package/.agents/skills/gstack/.agents/skills/gstack-guard/SKILL.md +62 -0
- package/.agents/skills/gstack/.agents/skills/gstack-investigate/SKILL.md +415 -0
- package/.agents/skills/gstack/.agents/skills/gstack-land-and-deploy/SKILL.md +873 -0
- package/.agents/skills/gstack/.agents/skills/gstack-office-hours/SKILL.md +986 -0
- package/.agents/skills/gstack/.agents/skills/gstack-plan-ceo-review/SKILL.md +1268 -0
- package/.agents/skills/gstack/.agents/skills/gstack-plan-design-review/SKILL.md +668 -0
- package/.agents/skills/gstack/.agents/skills/gstack-plan-eng-review/SKILL.md +826 -0
- package/.agents/skills/gstack/.agents/skills/gstack-qa/SKILL.md +1006 -0
- package/.agents/skills/gstack/.agents/skills/gstack-qa-only/SKILL.md +626 -0
- package/.agents/skills/gstack/.agents/skills/gstack-retro/SKILL.md +1065 -0
- package/.agents/skills/gstack/.agents/skills/gstack-review/SKILL.md +704 -0
- package/.agents/skills/gstack/.agents/skills/gstack-setup-browser-cookies/SKILL.md +325 -0
- package/.agents/skills/gstack/.agents/skills/gstack-setup-deploy/SKILL.md +450 -0
- package/.agents/skills/gstack/.agents/skills/gstack-ship/SKILL.md +1312 -0
- package/.agents/skills/gstack/.agents/skills/gstack-unfreeze/SKILL.md +36 -0
- package/.agents/skills/gstack/.agents/skills/gstack-upgrade/SKILL.md +220 -0
- package/.agents/skills/gstack/.env.example +5 -0
- package/.agents/skills/gstack/.github/workflows/skill-docs.yml +17 -0
- package/.agents/skills/gstack/AGENTS.md +49 -0
- package/.agents/skills/gstack/ARCHITECTURE.md +359 -0
- package/.agents/skills/gstack/BROWSER.md +271 -0
- package/.agents/skills/gstack/CHANGELOG.md +800 -0
- package/.agents/skills/gstack/CLAUDE.md +284 -0
- package/.agents/skills/gstack/CONTRIBUTING.md +370 -0
- package/.agents/skills/gstack/ETHOS.md +129 -0
- package/.agents/skills/gstack/LICENSE +21 -0
- package/.agents/skills/gstack/README.md +228 -0
- package/.agents/skills/gstack/SKILL.md +657 -0
- package/.agents/skills/gstack/SKILL.md.tmpl +281 -0
- package/.agents/skills/gstack/TODOS.md +564 -0
- package/.agents/skills/gstack/VERSION +1 -0
- package/.agents/skills/gstack/autoplan/SKILL.md +689 -0
- package/.agents/skills/gstack/autoplan/SKILL.md.tmpl +416 -0
- package/.agents/skills/gstack/benchmark/SKILL.md +489 -0
- package/.agents/skills/gstack/benchmark/SKILL.md.tmpl +233 -0
- package/.agents/skills/gstack/bin/dev-setup +68 -0
- package/.agents/skills/gstack/bin/dev-teardown +56 -0
- package/.agents/skills/gstack/bin/gstack-analytics +191 -0
- package/.agents/skills/gstack/bin/gstack-community-dashboard +113 -0
- package/.agents/skills/gstack/bin/gstack-config +38 -0
- package/.agents/skills/gstack/bin/gstack-diff-scope +71 -0
- package/.agents/skills/gstack/bin/gstack-global-discover.ts +591 -0
- package/.agents/skills/gstack/bin/gstack-repo-mode +93 -0
- package/.agents/skills/gstack/bin/gstack-review-log +9 -0
- package/.agents/skills/gstack/bin/gstack-review-read +12 -0
- package/.agents/skills/gstack/bin/gstack-slug +15 -0
- package/.agents/skills/gstack/bin/gstack-telemetry-log +158 -0
- package/.agents/skills/gstack/bin/gstack-telemetry-sync +127 -0
- package/.agents/skills/gstack/bin/gstack-update-check +196 -0
- package/.agents/skills/gstack/browse/SKILL.md +517 -0
- package/.agents/skills/gstack/browse/SKILL.md.tmpl +141 -0
- package/.agents/skills/gstack/browse/bin/find-browse +21 -0
- package/.agents/skills/gstack/browse/bin/remote-slug +14 -0
- package/.agents/skills/gstack/browse/scripts/build-node-server.sh +48 -0
- package/.agents/skills/gstack/browse/src/browser-manager.ts +634 -0
- package/.agents/skills/gstack/browse/src/buffers.ts +137 -0
- package/.agents/skills/gstack/browse/src/bun-polyfill.cjs +109 -0
- package/.agents/skills/gstack/browse/src/cli.ts +420 -0
- package/.agents/skills/gstack/browse/src/commands.ts +111 -0
- package/.agents/skills/gstack/browse/src/config.ts +150 -0
- package/.agents/skills/gstack/browse/src/cookie-import-browser.ts +417 -0
- package/.agents/skills/gstack/browse/src/cookie-picker-routes.ts +207 -0
- package/.agents/skills/gstack/browse/src/cookie-picker-ui.ts +541 -0
- package/.agents/skills/gstack/browse/src/find-browse.ts +61 -0
- package/.agents/skills/gstack/browse/src/meta-commands.ts +269 -0
- package/.agents/skills/gstack/browse/src/platform.ts +17 -0
- package/.agents/skills/gstack/browse/src/read-commands.ts +335 -0
- package/.agents/skills/gstack/browse/src/server.ts +369 -0
- package/.agents/skills/gstack/browse/src/snapshot.ts +398 -0
- package/.agents/skills/gstack/browse/src/url-validation.ts +91 -0
- package/.agents/skills/gstack/browse/src/write-commands.ts +352 -0
- package/.agents/skills/gstack/browse/test/bun-polyfill.test.ts +72 -0
- package/.agents/skills/gstack/browse/test/commands.test.ts +1836 -0
- package/.agents/skills/gstack/browse/test/config.test.ts +250 -0
- package/.agents/skills/gstack/browse/test/cookie-import-browser.test.ts +397 -0
- package/.agents/skills/gstack/browse/test/cookie-picker-routes.test.ts +205 -0
- package/.agents/skills/gstack/browse/test/find-browse.test.ts +50 -0
- package/.agents/skills/gstack/browse/test/fixtures/basic.html +33 -0
- package/.agents/skills/gstack/browse/test/fixtures/cursor-interactive.html +22 -0
- package/.agents/skills/gstack/browse/test/fixtures/dialog.html +15 -0
- package/.agents/skills/gstack/browse/test/fixtures/empty.html +2 -0
- package/.agents/skills/gstack/browse/test/fixtures/forms.html +55 -0
- package/.agents/skills/gstack/browse/test/fixtures/qa-eval-checkout.html +108 -0
- package/.agents/skills/gstack/browse/test/fixtures/qa-eval-spa.html +98 -0
- package/.agents/skills/gstack/browse/test/fixtures/qa-eval.html +51 -0
- package/.agents/skills/gstack/browse/test/fixtures/responsive.html +49 -0
- package/.agents/skills/gstack/browse/test/fixtures/snapshot.html +55 -0
- package/.agents/skills/gstack/browse/test/fixtures/spa.html +24 -0
- package/.agents/skills/gstack/browse/test/fixtures/states.html +17 -0
- package/.agents/skills/gstack/browse/test/fixtures/upload.html +25 -0
- package/.agents/skills/gstack/browse/test/gstack-config.test.ts +125 -0
- package/.agents/skills/gstack/browse/test/gstack-update-check.test.ts +467 -0
- package/.agents/skills/gstack/browse/test/handoff.test.ts +235 -0
- package/.agents/skills/gstack/browse/test/path-validation.test.ts +63 -0
- package/.agents/skills/gstack/browse/test/platform.test.ts +37 -0
- package/.agents/skills/gstack/browse/test/snapshot.test.ts +467 -0
- package/.agents/skills/gstack/browse/test/test-server.ts +57 -0
- package/.agents/skills/gstack/browse/test/url-validation.test.ts +72 -0
- package/.agents/skills/gstack/canary/SKILL.md +493 -0
- package/.agents/skills/gstack/canary/SKILL.md.tmpl +220 -0
- package/.agents/skills/gstack/careful/SKILL.md +59 -0
- package/.agents/skills/gstack/careful/SKILL.md.tmpl +57 -0
- package/.agents/skills/gstack/careful/bin/check-careful.sh +112 -0
- package/.agents/skills/gstack/codex/SKILL.md +677 -0
- package/.agents/skills/gstack/codex/SKILL.md.tmpl +356 -0
- package/.agents/skills/gstack/conductor.json +6 -0
- package/.agents/skills/gstack/cso/SKILL.md +615 -0
- package/.agents/skills/gstack/cso/SKILL.md.tmpl +376 -0
- package/.agents/skills/gstack/design-consultation/SKILL.md +625 -0
- package/.agents/skills/gstack/design-consultation/SKILL.md.tmpl +369 -0
- package/.agents/skills/gstack/design-review/SKILL.md +998 -0
- package/.agents/skills/gstack/design-review/SKILL.md.tmpl +262 -0
- package/.agents/skills/gstack/docs/images/github-2013.png +0 -0
- package/.agents/skills/gstack/docs/images/github-2026.png +0 -0
- package/.agents/skills/gstack/docs/skills.md +877 -0
- package/.agents/skills/gstack/document-release/SKILL.md +613 -0
- package/.agents/skills/gstack/document-release/SKILL.md.tmpl +357 -0
- package/.agents/skills/gstack/freeze/SKILL.md +82 -0
- package/.agents/skills/gstack/freeze/SKILL.md.tmpl +80 -0
- package/.agents/skills/gstack/freeze/bin/check-freeze.sh +68 -0
- package/.agents/skills/gstack/gstack-upgrade/SKILL.md +226 -0
- package/.agents/skills/gstack/gstack-upgrade/SKILL.md.tmpl +224 -0
- package/.agents/skills/gstack/guard/SKILL.md +82 -0
- package/.agents/skills/gstack/guard/SKILL.md.tmpl +80 -0
- package/.agents/skills/gstack/investigate/SKILL.md +435 -0
- package/.agents/skills/gstack/investigate/SKILL.md.tmpl +196 -0
- package/.agents/skills/gstack/land-and-deploy/SKILL.md +880 -0
- package/.agents/skills/gstack/land-and-deploy/SKILL.md.tmpl +575 -0
- package/.agents/skills/gstack/office-hours/SKILL.md +996 -0
- package/.agents/skills/gstack/office-hours/SKILL.md.tmpl +624 -0
- package/.agents/skills/gstack/package.json +55 -0
- package/.agents/skills/gstack/plan-ceo-review/SKILL.md +1277 -0
- package/.agents/skills/gstack/plan-ceo-review/SKILL.md.tmpl +838 -0
- package/.agents/skills/gstack/plan-design-review/SKILL.md +676 -0
- package/.agents/skills/gstack/plan-design-review/SKILL.md.tmpl +314 -0
- package/.agents/skills/gstack/plan-eng-review/SKILL.md +836 -0
- package/.agents/skills/gstack/plan-eng-review/SKILL.md.tmpl +279 -0
- package/.agents/skills/gstack/qa/SKILL.md +1016 -0
- package/.agents/skills/gstack/qa/SKILL.md.tmpl +316 -0
- package/.agents/skills/gstack/qa/references/issue-taxonomy.md +85 -0
- package/.agents/skills/gstack/qa/templates/qa-report-template.md +126 -0
- package/.agents/skills/gstack/qa-only/SKILL.md +633 -0
- package/.agents/skills/gstack/qa-only/SKILL.md.tmpl +101 -0
- package/.agents/skills/gstack/retro/SKILL.md +1072 -0
- package/.agents/skills/gstack/retro/SKILL.md.tmpl +833 -0
- package/.agents/skills/gstack/review/SKILL.md +849 -0
- package/.agents/skills/gstack/review/SKILL.md.tmpl +259 -0
- package/.agents/skills/gstack/review/TODOS-format.md +62 -0
- package/.agents/skills/gstack/review/checklist.md +190 -0
- package/.agents/skills/gstack/review/design-checklist.md +132 -0
- package/.agents/skills/gstack/review/greptile-triage.md +220 -0
- package/.agents/skills/gstack/scripts/analytics.ts +190 -0
- package/.agents/skills/gstack/scripts/dev-skill.ts +82 -0
- package/.agents/skills/gstack/scripts/eval-compare.ts +96 -0
- package/.agents/skills/gstack/scripts/eval-list.ts +116 -0
- package/.agents/skills/gstack/scripts/eval-select.ts +86 -0
- package/.agents/skills/gstack/scripts/eval-summary.ts +187 -0
- package/.agents/skills/gstack/scripts/eval-watch.ts +172 -0
- package/.agents/skills/gstack/scripts/gen-skill-docs.ts +2414 -0
- package/.agents/skills/gstack/scripts/skill-check.ts +167 -0
- package/.agents/skills/gstack/setup +269 -0
- package/.agents/skills/gstack/setup-browser-cookies/SKILL.md +330 -0
- package/.agents/skills/gstack/setup-browser-cookies/SKILL.md.tmpl +74 -0
- package/.agents/skills/gstack/setup-deploy/SKILL.md +459 -0
- package/.agents/skills/gstack/setup-deploy/SKILL.md.tmpl +220 -0
- package/.agents/skills/gstack/ship/SKILL.md +1457 -0
- package/.agents/skills/gstack/ship/SKILL.md.tmpl +528 -0
- package/.agents/skills/gstack/supabase/config.sh +10 -0
- package/.agents/skills/gstack/supabase/functions/community-pulse/index.ts +59 -0
- package/.agents/skills/gstack/supabase/functions/telemetry-ingest/index.ts +135 -0
- package/.agents/skills/gstack/supabase/functions/update-check/index.ts +37 -0
- package/.agents/skills/gstack/supabase/migrations/001_telemetry.sql +89 -0
- package/.agents/skills/gstack/test/analytics.test.ts +277 -0
- package/.agents/skills/gstack/test/codex-e2e.test.ts +197 -0
- package/.agents/skills/gstack/test/fixtures/coverage-audit-fixture.ts +76 -0
- package/.agents/skills/gstack/test/fixtures/eval-baselines.json +7 -0
- package/.agents/skills/gstack/test/fixtures/qa-eval-checkout-ground-truth.json +43 -0
- package/.agents/skills/gstack/test/fixtures/qa-eval-ground-truth.json +43 -0
- package/.agents/skills/gstack/test/fixtures/qa-eval-spa-ground-truth.json +43 -0
- package/.agents/skills/gstack/test/fixtures/review-eval-design-slop.css +86 -0
- package/.agents/skills/gstack/test/fixtures/review-eval-design-slop.html +41 -0
- package/.agents/skills/gstack/test/fixtures/review-eval-enum-diff.rb +30 -0
- package/.agents/skills/gstack/test/fixtures/review-eval-enum.rb +27 -0
- package/.agents/skills/gstack/test/fixtures/review-eval-vuln.rb +14 -0
- package/.agents/skills/gstack/test/gemini-e2e.test.ts +173 -0
- package/.agents/skills/gstack/test/gen-skill-docs.test.ts +1049 -0
- package/.agents/skills/gstack/test/global-discover.test.ts +187 -0
- package/.agents/skills/gstack/test/helpers/codex-session-runner.ts +282 -0
- package/.agents/skills/gstack/test/helpers/e2e-helpers.ts +239 -0
- package/.agents/skills/gstack/test/helpers/eval-store.test.ts +548 -0
- package/.agents/skills/gstack/test/helpers/eval-store.ts +689 -0
- package/.agents/skills/gstack/test/helpers/gemini-session-runner.test.ts +104 -0
- package/.agents/skills/gstack/test/helpers/gemini-session-runner.ts +201 -0
- package/.agents/skills/gstack/test/helpers/llm-judge.ts +130 -0
- package/.agents/skills/gstack/test/helpers/observability.test.ts +283 -0
- package/.agents/skills/gstack/test/helpers/session-runner.test.ts +96 -0
- package/.agents/skills/gstack/test/helpers/session-runner.ts +357 -0
- package/.agents/skills/gstack/test/helpers/skill-parser.ts +206 -0
- package/.agents/skills/gstack/test/helpers/touchfiles.ts +260 -0
- package/.agents/skills/gstack/test/hook-scripts.test.ts +373 -0
- package/.agents/skills/gstack/test/skill-e2e-browse.test.ts +293 -0
- package/.agents/skills/gstack/test/skill-e2e-deploy.test.ts +279 -0
- package/.agents/skills/gstack/test/skill-e2e-design.test.ts +614 -0
- package/.agents/skills/gstack/test/skill-e2e-plan.test.ts +538 -0
- package/.agents/skills/gstack/test/skill-e2e-qa-bugs.test.ts +194 -0
- package/.agents/skills/gstack/test/skill-e2e-qa-workflow.test.ts +412 -0
- package/.agents/skills/gstack/test/skill-e2e-review.test.ts +535 -0
- package/.agents/skills/gstack/test/skill-e2e-workflow.test.ts +586 -0
- package/.agents/skills/gstack/test/skill-e2e.test.ts +3325 -0
- package/.agents/skills/gstack/test/skill-llm-eval.test.ts +787 -0
- package/.agents/skills/gstack/test/skill-parser.test.ts +179 -0
- package/.agents/skills/gstack/test/skill-routing-e2e.test.ts +605 -0
- package/.agents/skills/gstack/test/skill-validation.test.ts +1520 -0
- package/.agents/skills/gstack/test/telemetry.test.ts +278 -0
- package/.agents/skills/gstack/test/touchfiles.test.ts +262 -0
- package/.agents/skills/gstack/unfreeze/SKILL.md +40 -0
- package/.agents/skills/gstack/unfreeze/SKILL.md.tmpl +38 -0
- package/README.md +12 -7
- package/README_KO.md +12 -6
- package/package.json +3 -2
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# gstack-repo-mode — detect solo vs collaborative repo mode
|
|
3
|
+
# Usage: source <(gstack-repo-mode) → sets REPO_MODE variable
|
|
4
|
+
# Or: gstack-repo-mode → prints REPO_MODE=... line
|
|
5
|
+
#
|
|
6
|
+
# Detection heuristic (90-day window):
|
|
7
|
+
# Solo: top author >= 80% of commits
|
|
8
|
+
# Collaborative: top author < 80%
|
|
9
|
+
#
|
|
10
|
+
# Override: gstack-config set repo_mode solo|collaborative
|
|
11
|
+
# Cache: ~/.gstack/projects/$SLUG/repo-mode.json (7-day TTL)
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
15
|
+
# Compute SLUG directly (avoid eval of gstack-slug — branch names can contain shell metacharacters)
|
|
16
|
+
REMOTE_URL=$(git remote get-url origin 2>/dev/null || true)
|
|
17
|
+
if [ -z "$REMOTE_URL" ]; then
|
|
18
|
+
echo "REPO_MODE=unknown"
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
SLUG=$(echo "$REMOTE_URL" | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-')
|
|
22
|
+
[ -z "${SLUG:-}" ] && { echo "REPO_MODE=unknown"; exit 0; }
|
|
23
|
+
|
|
24
|
+
# Validate: only allow known values (prevent shell injection via source <(...))
|
|
25
|
+
validate_mode() {
|
|
26
|
+
case "$1" in solo|collaborative|unknown) echo "$1" ;; *) echo "unknown" ;; esac
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Config override takes precedence
|
|
30
|
+
OVERRIDE=$("$SCRIPT_DIR/gstack-config" get repo_mode 2>/dev/null || true)
|
|
31
|
+
if [ -n "$OVERRIDE" ] && [ "$OVERRIDE" != "null" ]; then
|
|
32
|
+
echo "REPO_MODE=$(validate_mode "$OVERRIDE")"
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Check cache (7-day TTL)
|
|
37
|
+
CACHE_DIR="$HOME/.gstack/projects/$SLUG"
|
|
38
|
+
CACHE_FILE="$CACHE_DIR/repo-mode.json"
|
|
39
|
+
if [ -f "$CACHE_FILE" ]; then
|
|
40
|
+
CACHE_AGE=$(( $(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0) ))
|
|
41
|
+
if [ "$CACHE_AGE" -lt 604800 ]; then # 7 days in seconds
|
|
42
|
+
MODE=$(grep -o '"mode":"[^"]*"' "$CACHE_FILE" | head -1 | cut -d'"' -f4)
|
|
43
|
+
[ -n "$MODE" ] && echo "REPO_MODE=$(validate_mode "$MODE")" && exit 0
|
|
44
|
+
fi
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Compute from git history (90-day window)
|
|
48
|
+
# Use default branch (not HEAD) to avoid feature-branch sampling bias
|
|
49
|
+
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/||' || true)
|
|
50
|
+
# Fallback: try origin/main, then origin/master, then HEAD
|
|
51
|
+
if [ -z "$DEFAULT_BRANCH" ]; then
|
|
52
|
+
if git rev-parse --verify origin/main &>/dev/null; then
|
|
53
|
+
DEFAULT_BRANCH="origin/main"
|
|
54
|
+
elif git rev-parse --verify origin/master &>/dev/null; then
|
|
55
|
+
DEFAULT_BRANCH="origin/master"
|
|
56
|
+
else
|
|
57
|
+
DEFAULT_BRANCH="HEAD"
|
|
58
|
+
fi
|
|
59
|
+
fi
|
|
60
|
+
SHORTLOG=$(git shortlog -sn --since="90 days ago" --no-merges "$DEFAULT_BRANCH" 2>/dev/null)
|
|
61
|
+
if [ -z "$SHORTLOG" ]; then
|
|
62
|
+
echo "REPO_MODE=unknown"
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# Compute TOTAL from ALL authors (not truncated) to avoid solo bias
|
|
67
|
+
TOTAL=$(echo "$SHORTLOG" | awk '{s+=$1} END {print s}')
|
|
68
|
+
TOP=$(echo "$SHORTLOG" | head -1 | awk '{print $1}')
|
|
69
|
+
AUTHORS=$(echo "$SHORTLOG" | wc -l | tr -d ' ')
|
|
70
|
+
|
|
71
|
+
# Minimum sample: need at least 5 commits to classify
|
|
72
|
+
if [ "$TOTAL" -lt 5 ]; then
|
|
73
|
+
echo "REPO_MODE=unknown"
|
|
74
|
+
exit 0
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
TOP_PCT=$(( TOP * 100 / TOTAL ))
|
|
78
|
+
|
|
79
|
+
# Solo: top author >= 80% of commits (occasional outside PRs don't change mode)
|
|
80
|
+
if [ "$TOP_PCT" -ge 80 ]; then
|
|
81
|
+
MODE=solo
|
|
82
|
+
else
|
|
83
|
+
MODE=collaborative
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Cache result atomically (fail silently if ~/.gstack is unwritable)
|
|
87
|
+
mkdir -p "$CACHE_DIR" 2>/dev/null || true
|
|
88
|
+
CACHE_TMP=$(mktemp "$CACHE_DIR/.repo-mode-XXXXXX" 2>/dev/null || true)
|
|
89
|
+
if [ -n "$CACHE_TMP" ]; then
|
|
90
|
+
echo "{\"mode\":\"$MODE\",\"top_pct\":$TOP_PCT,\"authors\":$AUTHORS,\"total\":$TOTAL,\"computed\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" > "$CACHE_TMP" 2>/dev/null && mv "$CACHE_TMP" "$CACHE_FILE" 2>/dev/null || rm -f "$CACHE_TMP" 2>/dev/null
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
echo "REPO_MODE=$MODE"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# gstack-review-log — atomically log a review result
|
|
3
|
+
# Usage: gstack-review-log '{"skill":"...","timestamp":"...","status":"..."}'
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
+
source <("$SCRIPT_DIR/gstack-slug" 2>/dev/null)
|
|
7
|
+
GSTACK_HOME="${GSTACK_HOME:-$HOME/.gstack}"
|
|
8
|
+
mkdir -p "$GSTACK_HOME/projects/$SLUG"
|
|
9
|
+
echo "$1" >> "$GSTACK_HOME/projects/$SLUG/$BRANCH-reviews.jsonl"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# gstack-review-read — read review log and config for dashboard
|
|
3
|
+
# Usage: gstack-review-read
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
+
source <("$SCRIPT_DIR/gstack-slug" 2>/dev/null)
|
|
7
|
+
GSTACK_HOME="${GSTACK_HOME:-$HOME/.gstack}"
|
|
8
|
+
cat "$GSTACK_HOME/projects/$SLUG/$BRANCH-reviews.jsonl" 2>/dev/null || echo "NO_REVIEWS"
|
|
9
|
+
echo "---CONFIG---"
|
|
10
|
+
"$SCRIPT_DIR/gstack-config" get skip_eng_review 2>/dev/null || echo "false"
|
|
11
|
+
echo "---HEAD---"
|
|
12
|
+
git rev-parse --short HEAD 2>/dev/null || echo "unknown"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# gstack-slug — output project slug and sanitized branch name
|
|
3
|
+
# Usage: source <(gstack-slug) → sets SLUG and BRANCH variables
|
|
4
|
+
# Or: gstack-slug → prints SLUG=... and BRANCH=... lines
|
|
5
|
+
#
|
|
6
|
+
# Security: output is sanitized to [a-zA-Z0-9._-] only, preventing
|
|
7
|
+
# shell injection when consumed via source or eval.
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
RAW_SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-')
|
|
10
|
+
RAW_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-')
|
|
11
|
+
# Strip any characters that aren't alphanumeric, dot, hyphen, or underscore
|
|
12
|
+
SLUG=$(printf '%s' "$RAW_SLUG" | tr -cd 'a-zA-Z0-9._-')
|
|
13
|
+
BRANCH=$(printf '%s' "$RAW_BRANCH" | tr -cd 'a-zA-Z0-9._-')
|
|
14
|
+
echo "SLUG=$SLUG"
|
|
15
|
+
echo "BRANCH=$BRANCH"
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# gstack-telemetry-log — append a telemetry event to local JSONL
|
|
3
|
+
#
|
|
4
|
+
# Data flow:
|
|
5
|
+
# preamble (start) ──▶ .pending marker
|
|
6
|
+
# preamble (epilogue) ──▶ gstack-telemetry-log ──▶ skill-usage.jsonl
|
|
7
|
+
# └──▶ gstack-telemetry-sync (bg)
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# gstack-telemetry-log --skill qa --duration 142 --outcome success \
|
|
11
|
+
# --used-browse true --session-id "12345-1710756600"
|
|
12
|
+
#
|
|
13
|
+
# Env overrides (for testing):
|
|
14
|
+
# GSTACK_STATE_DIR — override ~/.gstack state directory
|
|
15
|
+
# GSTACK_DIR — override auto-detected gstack root
|
|
16
|
+
#
|
|
17
|
+
# NOTE: Uses set -uo pipefail (no -e) — telemetry must never exit non-zero
|
|
18
|
+
set -uo pipefail
|
|
19
|
+
|
|
20
|
+
GSTACK_DIR="${GSTACK_DIR:-$(cd "$(dirname "$0")/.." && pwd)}"
|
|
21
|
+
STATE_DIR="${GSTACK_STATE_DIR:-$HOME/.gstack}"
|
|
22
|
+
ANALYTICS_DIR="$STATE_DIR/analytics"
|
|
23
|
+
JSONL_FILE="$ANALYTICS_DIR/skill-usage.jsonl"
|
|
24
|
+
PENDING_DIR="$ANALYTICS_DIR" # .pending-* files live here
|
|
25
|
+
CONFIG_CMD="$GSTACK_DIR/bin/gstack-config"
|
|
26
|
+
VERSION_FILE="$GSTACK_DIR/VERSION"
|
|
27
|
+
|
|
28
|
+
# ─── Parse flags ─────────────────────────────────────────────
|
|
29
|
+
SKILL=""
|
|
30
|
+
DURATION=""
|
|
31
|
+
OUTCOME="unknown"
|
|
32
|
+
USED_BROWSE="false"
|
|
33
|
+
SESSION_ID=""
|
|
34
|
+
ERROR_CLASS=""
|
|
35
|
+
EVENT_TYPE="skill_run"
|
|
36
|
+
|
|
37
|
+
while [ $# -gt 0 ]; do
|
|
38
|
+
case "$1" in
|
|
39
|
+
--skill) SKILL="$2"; shift 2 ;;
|
|
40
|
+
--duration) DURATION="$2"; shift 2 ;;
|
|
41
|
+
--outcome) OUTCOME="$2"; shift 2 ;;
|
|
42
|
+
--used-browse) USED_BROWSE="$2"; shift 2 ;;
|
|
43
|
+
--session-id) SESSION_ID="$2"; shift 2 ;;
|
|
44
|
+
--error-class) ERROR_CLASS="$2"; shift 2 ;;
|
|
45
|
+
--event-type) EVENT_TYPE="$2"; shift 2 ;;
|
|
46
|
+
*) shift ;;
|
|
47
|
+
esac
|
|
48
|
+
done
|
|
49
|
+
|
|
50
|
+
# ─── Read telemetry tier ─────────────────────────────────────
|
|
51
|
+
TIER="$("$CONFIG_CMD" get telemetry 2>/dev/null || true)"
|
|
52
|
+
TIER="${TIER:-off}"
|
|
53
|
+
|
|
54
|
+
# Validate tier
|
|
55
|
+
case "$TIER" in
|
|
56
|
+
off|anonymous|community) ;;
|
|
57
|
+
*) TIER="off" ;; # invalid value → default to off
|
|
58
|
+
esac
|
|
59
|
+
|
|
60
|
+
if [ "$TIER" = "off" ]; then
|
|
61
|
+
# Still clear pending markers for this session even if telemetry is off
|
|
62
|
+
[ -n "$SESSION_ID" ] && rm -f "$PENDING_DIR/.pending-$SESSION_ID" 2>/dev/null || true
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# ─── Finalize stale .pending markers ────────────────────────
|
|
67
|
+
# Each session gets its own .pending-$SESSION_ID file to avoid races
|
|
68
|
+
# between concurrent sessions. Finalize any that don't match our session.
|
|
69
|
+
for PFILE in "$PENDING_DIR"/.pending-*; do
|
|
70
|
+
[ -f "$PFILE" ] || continue
|
|
71
|
+
# Skip our own session's marker (it's still in-flight)
|
|
72
|
+
PFILE_BASE="$(basename "$PFILE")"
|
|
73
|
+
PFILE_SID="${PFILE_BASE#.pending-}"
|
|
74
|
+
[ "$PFILE_SID" = "$SESSION_ID" ] && continue
|
|
75
|
+
|
|
76
|
+
PENDING_DATA="$(cat "$PFILE" 2>/dev/null || true)"
|
|
77
|
+
rm -f "$PFILE" 2>/dev/null || true
|
|
78
|
+
if [ -n "$PENDING_DATA" ]; then
|
|
79
|
+
# Extract fields from pending marker using grep -o + awk
|
|
80
|
+
P_SKILL="$(echo "$PENDING_DATA" | grep -o '"skill":"[^"]*"' | head -1 | awk -F'"' '{print $4}')"
|
|
81
|
+
P_TS="$(echo "$PENDING_DATA" | grep -o '"ts":"[^"]*"' | head -1 | awk -F'"' '{print $4}')"
|
|
82
|
+
P_SID="$(echo "$PENDING_DATA" | grep -o '"session_id":"[^"]*"' | head -1 | awk -F'"' '{print $4}')"
|
|
83
|
+
P_VER="$(echo "$PENDING_DATA" | grep -o '"gstack_version":"[^"]*"' | head -1 | awk -F'"' '{print $4}')"
|
|
84
|
+
P_OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
85
|
+
P_ARCH="$(uname -m)"
|
|
86
|
+
|
|
87
|
+
# Write the stale event as outcome: unknown
|
|
88
|
+
mkdir -p "$ANALYTICS_DIR"
|
|
89
|
+
printf '{"v":1,"ts":"%s","event_type":"skill_run","skill":"%s","session_id":"%s","gstack_version":"%s","os":"%s","arch":"%s","duration_s":null,"outcome":"unknown","error_class":null,"used_browse":false,"sessions":1}\n' \
|
|
90
|
+
"$P_TS" "$P_SKILL" "$P_SID" "$P_VER" "$P_OS" "$P_ARCH" >> "$JSONL_FILE" 2>/dev/null || true
|
|
91
|
+
fi
|
|
92
|
+
done
|
|
93
|
+
|
|
94
|
+
# Clear our own session's pending marker (we're about to log the real event)
|
|
95
|
+
[ -n "$SESSION_ID" ] && rm -f "$PENDING_DIR/.pending-$SESSION_ID" 2>/dev/null || true
|
|
96
|
+
|
|
97
|
+
# ─── Collect metadata ────────────────────────────────────────
|
|
98
|
+
TS="$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u +%Y-%m-%dT%H:%M:%S 2>/dev/null || echo "")"
|
|
99
|
+
GSTACK_VERSION="$(cat "$VERSION_FILE" 2>/dev/null | tr -d '[:space:]' || echo "unknown")"
|
|
100
|
+
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
101
|
+
ARCH="$(uname -m)"
|
|
102
|
+
SESSIONS="1"
|
|
103
|
+
if [ -d "$STATE_DIR/sessions" ]; then
|
|
104
|
+
_SC="$(find "$STATE_DIR/sessions" -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' \n\r\t')"
|
|
105
|
+
[ -n "$_SC" ] && [ "$_SC" -gt 0 ] 2>/dev/null && SESSIONS="$_SC"
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Generate installation_id for community tier
|
|
109
|
+
INSTALL_ID=""
|
|
110
|
+
if [ "$TIER" = "community" ]; then
|
|
111
|
+
HOST="$(hostname 2>/dev/null || echo "unknown")"
|
|
112
|
+
USER="$(whoami 2>/dev/null || echo "unknown")"
|
|
113
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
114
|
+
INSTALL_ID="$(printf '%s-%s' "$HOST" "$USER" | shasum -a 256 | awk '{print $1}')"
|
|
115
|
+
elif command -v sha256sum >/dev/null 2>&1; then
|
|
116
|
+
INSTALL_ID="$(printf '%s-%s' "$HOST" "$USER" | sha256sum | awk '{print $1}')"
|
|
117
|
+
elif command -v openssl >/dev/null 2>&1; then
|
|
118
|
+
INSTALL_ID="$(printf '%s-%s' "$HOST" "$USER" | openssl dgst -sha256 | awk '{print $NF}')"
|
|
119
|
+
fi
|
|
120
|
+
# If no SHA-256 command available, install_id stays empty
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# Local-only fields (never sent remotely)
|
|
124
|
+
REPO_SLUG=""
|
|
125
|
+
BRANCH=""
|
|
126
|
+
if command -v git >/dev/null 2>&1; then
|
|
127
|
+
REPO_SLUG="$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-' 2>/dev/null || true)"
|
|
128
|
+
BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# ─── Construct and append JSON ───────────────────────────────
|
|
132
|
+
mkdir -p "$ANALYTICS_DIR"
|
|
133
|
+
|
|
134
|
+
# Escape null fields
|
|
135
|
+
ERR_FIELD="null"
|
|
136
|
+
[ -n "$ERROR_CLASS" ] && ERR_FIELD="\"$ERROR_CLASS\""
|
|
137
|
+
|
|
138
|
+
DUR_FIELD="null"
|
|
139
|
+
[ -n "$DURATION" ] && DUR_FIELD="$DURATION"
|
|
140
|
+
|
|
141
|
+
INSTALL_FIELD="null"
|
|
142
|
+
[ -n "$INSTALL_ID" ] && INSTALL_FIELD="\"$INSTALL_ID\""
|
|
143
|
+
|
|
144
|
+
BROWSE_BOOL="false"
|
|
145
|
+
[ "$USED_BROWSE" = "true" ] && BROWSE_BOOL="true"
|
|
146
|
+
|
|
147
|
+
printf '{"v":1,"ts":"%s","event_type":"%s","skill":"%s","session_id":"%s","gstack_version":"%s","os":"%s","arch":"%s","duration_s":%s,"outcome":"%s","error_class":%s,"used_browse":%s,"sessions":%s,"installation_id":%s,"_repo_slug":"%s","_branch":"%s"}\n' \
|
|
148
|
+
"$TS" "$EVENT_TYPE" "$SKILL" "$SESSION_ID" "$GSTACK_VERSION" "$OS" "$ARCH" \
|
|
149
|
+
"$DUR_FIELD" "$OUTCOME" "$ERR_FIELD" "$BROWSE_BOOL" "${SESSIONS:-1}" \
|
|
150
|
+
"$INSTALL_FIELD" "$REPO_SLUG" "$BRANCH" >> "$JSONL_FILE" 2>/dev/null || true
|
|
151
|
+
|
|
152
|
+
# ─── Trigger sync if tier is not off ─────────────────────────
|
|
153
|
+
SYNC_CMD="$GSTACK_DIR/bin/gstack-telemetry-sync"
|
|
154
|
+
if [ -x "$SYNC_CMD" ]; then
|
|
155
|
+
"$SYNC_CMD" 2>/dev/null &
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
exit 0
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# gstack-telemetry-sync — sync local JSONL events to Supabase
|
|
3
|
+
#
|
|
4
|
+
# Fire-and-forget, backgrounded, rate-limited to once per 5 minutes.
|
|
5
|
+
# Strips local-only fields before sending. Respects privacy tiers.
|
|
6
|
+
#
|
|
7
|
+
# Env overrides (for testing):
|
|
8
|
+
# GSTACK_STATE_DIR — override ~/.gstack state directory
|
|
9
|
+
# GSTACK_DIR — override auto-detected gstack root
|
|
10
|
+
# GSTACK_TELEMETRY_ENDPOINT — override Supabase endpoint URL
|
|
11
|
+
set -uo pipefail
|
|
12
|
+
|
|
13
|
+
GSTACK_DIR="${GSTACK_DIR:-$(cd "$(dirname "$0")/.." && pwd)}"
|
|
14
|
+
STATE_DIR="${GSTACK_STATE_DIR:-$HOME/.gstack}"
|
|
15
|
+
ANALYTICS_DIR="$STATE_DIR/analytics"
|
|
16
|
+
JSONL_FILE="$ANALYTICS_DIR/skill-usage.jsonl"
|
|
17
|
+
CURSOR_FILE="$ANALYTICS_DIR/.last-sync-line"
|
|
18
|
+
RATE_FILE="$ANALYTICS_DIR/.last-sync-time"
|
|
19
|
+
CONFIG_CMD="$GSTACK_DIR/bin/gstack-config"
|
|
20
|
+
|
|
21
|
+
# Source Supabase config if not overridden by env
|
|
22
|
+
if [ -z "${GSTACK_TELEMETRY_ENDPOINT:-}" ] && [ -f "$GSTACK_DIR/supabase/config.sh" ]; then
|
|
23
|
+
. "$GSTACK_DIR/supabase/config.sh"
|
|
24
|
+
fi
|
|
25
|
+
ENDPOINT="${GSTACK_TELEMETRY_ENDPOINT:-}"
|
|
26
|
+
ANON_KEY="${GSTACK_SUPABASE_ANON_KEY:-}"
|
|
27
|
+
|
|
28
|
+
# ─── Pre-checks ──────────────────────────────────────────────
|
|
29
|
+
# No endpoint configured yet → exit silently
|
|
30
|
+
[ -z "$ENDPOINT" ] && exit 0
|
|
31
|
+
|
|
32
|
+
# No JSONL file → nothing to sync
|
|
33
|
+
[ -f "$JSONL_FILE" ] || exit 0
|
|
34
|
+
|
|
35
|
+
# Rate limit: once per 5 minutes
|
|
36
|
+
if [ -f "$RATE_FILE" ]; then
|
|
37
|
+
STALE=$(find "$RATE_FILE" -mmin +5 2>/dev/null || true)
|
|
38
|
+
[ -z "$STALE" ] && exit 0
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# ─── Read tier ───────────────────────────────────────────────
|
|
42
|
+
TIER="$("$CONFIG_CMD" get telemetry 2>/dev/null || true)"
|
|
43
|
+
TIER="${TIER:-off}"
|
|
44
|
+
[ "$TIER" = "off" ] && exit 0
|
|
45
|
+
|
|
46
|
+
# ─── Read cursor ─────────────────────────────────────────────
|
|
47
|
+
CURSOR=0
|
|
48
|
+
if [ -f "$CURSOR_FILE" ]; then
|
|
49
|
+
CURSOR="$(cat "$CURSOR_FILE" 2>/dev/null | tr -d ' \n\r\t')"
|
|
50
|
+
# Validate: must be a non-negative integer
|
|
51
|
+
case "$CURSOR" in *[!0-9]*) CURSOR=0 ;; esac
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# Safety: if cursor exceeds file length, reset
|
|
55
|
+
TOTAL_LINES="$(wc -l < "$JSONL_FILE" | tr -d ' \n\r\t')"
|
|
56
|
+
if [ "$CURSOR" -gt "$TOTAL_LINES" ] 2>/dev/null; then
|
|
57
|
+
CURSOR=0
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Nothing new to sync
|
|
61
|
+
[ "$CURSOR" -ge "$TOTAL_LINES" ] 2>/dev/null && exit 0
|
|
62
|
+
|
|
63
|
+
# ─── Read unsent lines ───────────────────────────────────────
|
|
64
|
+
SKIP=$(( CURSOR + 1 ))
|
|
65
|
+
UNSENT="$(tail -n "+$SKIP" "$JSONL_FILE" 2>/dev/null || true)"
|
|
66
|
+
[ -z "$UNSENT" ] && exit 0
|
|
67
|
+
|
|
68
|
+
# ─── Strip local-only fields and build batch ─────────────────
|
|
69
|
+
BATCH="["
|
|
70
|
+
FIRST=true
|
|
71
|
+
COUNT=0
|
|
72
|
+
|
|
73
|
+
while IFS= read -r LINE; do
|
|
74
|
+
# Skip empty or malformed lines
|
|
75
|
+
[ -z "$LINE" ] && continue
|
|
76
|
+
echo "$LINE" | grep -q '^{' || continue
|
|
77
|
+
|
|
78
|
+
# Strip local-only fields + map JSONL field names to Postgres column names
|
|
79
|
+
CLEAN="$(echo "$LINE" | sed \
|
|
80
|
+
-e 's/,"_repo_slug":"[^"]*"//g' \
|
|
81
|
+
-e 's/,"_branch":"[^"]*"//g' \
|
|
82
|
+
-e 's/"v":/"schema_version":/g' \
|
|
83
|
+
-e 's/"ts":/"event_timestamp":/g' \
|
|
84
|
+
-e 's/"sessions":/"concurrent_sessions":/g' \
|
|
85
|
+
-e 's/,"repo":"[^"]*"//g')"
|
|
86
|
+
|
|
87
|
+
# If anonymous tier, strip installation_id
|
|
88
|
+
if [ "$TIER" = "anonymous" ]; then
|
|
89
|
+
CLEAN="$(echo "$CLEAN" | sed 's/,"installation_id":"[^"]*"//g; s/,"installation_id":null//g')"
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
if [ "$FIRST" = "true" ]; then
|
|
93
|
+
FIRST=false
|
|
94
|
+
else
|
|
95
|
+
BATCH="$BATCH,"
|
|
96
|
+
fi
|
|
97
|
+
BATCH="$BATCH$CLEAN"
|
|
98
|
+
COUNT=$(( COUNT + 1 ))
|
|
99
|
+
|
|
100
|
+
# Batch size limit
|
|
101
|
+
[ "$COUNT" -ge 100 ] && break
|
|
102
|
+
done <<< "$UNSENT"
|
|
103
|
+
|
|
104
|
+
BATCH="$BATCH]"
|
|
105
|
+
|
|
106
|
+
# Nothing to send after filtering
|
|
107
|
+
[ "$COUNT" -eq 0 ] && exit 0
|
|
108
|
+
|
|
109
|
+
# ─── POST to Supabase ────────────────────────────────────────
|
|
110
|
+
HTTP_CODE="$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 \
|
|
111
|
+
-X POST "${ENDPOINT}/telemetry_events" \
|
|
112
|
+
-H "Content-Type: application/json" \
|
|
113
|
+
-H "apikey: ${ANON_KEY}" \
|
|
114
|
+
-H "Authorization: Bearer ${ANON_KEY}" \
|
|
115
|
+
-H "Prefer: return=minimal" \
|
|
116
|
+
-d "$BATCH" 2>/dev/null || echo "000")"
|
|
117
|
+
|
|
118
|
+
# ─── Update cursor on success (2xx) ─────────────────────────
|
|
119
|
+
case "$HTTP_CODE" in
|
|
120
|
+
2*) NEW_CURSOR=$(( CURSOR + COUNT ))
|
|
121
|
+
echo "$NEW_CURSOR" > "$CURSOR_FILE" 2>/dev/null || true ;;
|
|
122
|
+
esac
|
|
123
|
+
|
|
124
|
+
# Update rate limit marker
|
|
125
|
+
touch "$RATE_FILE" 2>/dev/null || true
|
|
126
|
+
|
|
127
|
+
exit 0
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# gstack-update-check — periodic version check for all skills.
|
|
3
|
+
#
|
|
4
|
+
# Output (one line, or nothing):
|
|
5
|
+
# JUST_UPGRADED <old> <new> — marker found from recent upgrade
|
|
6
|
+
# UPGRADE_AVAILABLE <old> <new> — remote VERSION differs from local
|
|
7
|
+
# (nothing) — up to date, snoozed, disabled, or check skipped
|
|
8
|
+
#
|
|
9
|
+
# Env overrides (for testing):
|
|
10
|
+
# GSTACK_DIR — override auto-detected gstack root
|
|
11
|
+
# GSTACK_REMOTE_URL — override remote VERSION URL
|
|
12
|
+
# GSTACK_STATE_DIR — override ~/.gstack state directory
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
GSTACK_DIR="${GSTACK_DIR:-$(cd "$(dirname "$0")/.." && pwd)}"
|
|
16
|
+
STATE_DIR="${GSTACK_STATE_DIR:-$HOME/.gstack}"
|
|
17
|
+
CACHE_FILE="$STATE_DIR/last-update-check"
|
|
18
|
+
MARKER_FILE="$STATE_DIR/just-upgraded-from"
|
|
19
|
+
SNOOZE_FILE="$STATE_DIR/update-snoozed"
|
|
20
|
+
VERSION_FILE="$GSTACK_DIR/VERSION"
|
|
21
|
+
REMOTE_URL="${GSTACK_REMOTE_URL:-https://raw.githubusercontent.com/garrytan/gstack/main/VERSION}"
|
|
22
|
+
|
|
23
|
+
# ─── Force flag (busts cache for standalone /gstack-upgrade) ──
|
|
24
|
+
if [ "${1:-}" = "--force" ]; then
|
|
25
|
+
rm -f "$CACHE_FILE"
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# ─── Step 0: Check if updates are disabled ────────────────────
|
|
29
|
+
_UC=$("$GSTACK_DIR/bin/gstack-config" get update_check 2>/dev/null || true)
|
|
30
|
+
if [ "$_UC" = "false" ]; then
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# ─── Snooze helper ──────────────────────────────────────────
|
|
35
|
+
# check_snooze <remote_version>
|
|
36
|
+
# Returns 0 if snoozed (should stay quiet), 1 if not snoozed (should output).
|
|
37
|
+
#
|
|
38
|
+
# Snooze file format: <version> <level> <epoch>
|
|
39
|
+
# Level durations: 1=24h, 2=48h, 3+=7d
|
|
40
|
+
# New version (version mismatch) resets snooze.
|
|
41
|
+
check_snooze() {
|
|
42
|
+
local remote_ver="$1"
|
|
43
|
+
if [ ! -f "$SNOOZE_FILE" ]; then
|
|
44
|
+
return 1 # no snooze file → not snoozed
|
|
45
|
+
fi
|
|
46
|
+
local snoozed_ver snoozed_level snoozed_epoch
|
|
47
|
+
snoozed_ver="$(awk '{print $1}' "$SNOOZE_FILE" 2>/dev/null || true)"
|
|
48
|
+
snoozed_level="$(awk '{print $2}' "$SNOOZE_FILE" 2>/dev/null || true)"
|
|
49
|
+
snoozed_epoch="$(awk '{print $3}' "$SNOOZE_FILE" 2>/dev/null || true)"
|
|
50
|
+
|
|
51
|
+
# Validate: all three fields must be non-empty
|
|
52
|
+
if [ -z "$snoozed_ver" ] || [ -z "$snoozed_level" ] || [ -z "$snoozed_epoch" ]; then
|
|
53
|
+
return 1 # corrupt file → not snoozed
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Validate: level and epoch must be integers
|
|
57
|
+
case "$snoozed_level" in *[!0-9]*) return 1 ;; esac
|
|
58
|
+
case "$snoozed_epoch" in *[!0-9]*) return 1 ;; esac
|
|
59
|
+
|
|
60
|
+
# New version dropped? Ignore snooze.
|
|
61
|
+
if [ "$snoozed_ver" != "$remote_ver" ]; then
|
|
62
|
+
return 1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# Compute snooze duration based on level
|
|
66
|
+
local duration
|
|
67
|
+
case "$snoozed_level" in
|
|
68
|
+
1) duration=86400 ;; # 24 hours
|
|
69
|
+
2) duration=172800 ;; # 48 hours
|
|
70
|
+
*) duration=604800 ;; # 7 days (level 3+)
|
|
71
|
+
esac
|
|
72
|
+
|
|
73
|
+
local now
|
|
74
|
+
now="$(date +%s)"
|
|
75
|
+
local expires=$(( snoozed_epoch + duration ))
|
|
76
|
+
if [ "$now" -lt "$expires" ]; then
|
|
77
|
+
return 0 # still snoozed
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
return 1 # snooze expired
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# ─── Step 1: Read local version ──────────────────────────────
|
|
84
|
+
LOCAL=""
|
|
85
|
+
if [ -f "$VERSION_FILE" ]; then
|
|
86
|
+
LOCAL="$(cat "$VERSION_FILE" 2>/dev/null | tr -d '[:space:]')"
|
|
87
|
+
fi
|
|
88
|
+
if [ -z "$LOCAL" ]; then
|
|
89
|
+
exit 0 # No VERSION file → skip check
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# ─── Step 2: Check "just upgraded" marker ─────────────────────
|
|
93
|
+
if [ -f "$MARKER_FILE" ]; then
|
|
94
|
+
OLD="$(cat "$MARKER_FILE" 2>/dev/null | tr -d '[:space:]')"
|
|
95
|
+
rm -f "$MARKER_FILE"
|
|
96
|
+
rm -f "$SNOOZE_FILE"
|
|
97
|
+
mkdir -p "$STATE_DIR"
|
|
98
|
+
echo "UP_TO_DATE $LOCAL" > "$CACHE_FILE"
|
|
99
|
+
if [ -n "$OLD" ]; then
|
|
100
|
+
echo "JUST_UPGRADED $OLD $LOCAL"
|
|
101
|
+
fi
|
|
102
|
+
exit 0
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# ─── Step 3: Check cache freshness ──────────────────────────
|
|
106
|
+
# UP_TO_DATE: 60 min TTL (detect new releases quickly)
|
|
107
|
+
# UPGRADE_AVAILABLE: 720 min TTL (keep nagging)
|
|
108
|
+
if [ -f "$CACHE_FILE" ]; then
|
|
109
|
+
CACHED="$(cat "$CACHE_FILE" 2>/dev/null || true)"
|
|
110
|
+
case "$CACHED" in
|
|
111
|
+
UP_TO_DATE*) CACHE_TTL=60 ;;
|
|
112
|
+
UPGRADE_AVAILABLE*) CACHE_TTL=720 ;;
|
|
113
|
+
*) CACHE_TTL=0 ;; # corrupt → force re-fetch
|
|
114
|
+
esac
|
|
115
|
+
|
|
116
|
+
STALE=$(find "$CACHE_FILE" -mmin +$CACHE_TTL 2>/dev/null || true)
|
|
117
|
+
if [ -z "$STALE" ] && [ "$CACHE_TTL" -gt 0 ]; then
|
|
118
|
+
case "$CACHED" in
|
|
119
|
+
UP_TO_DATE*)
|
|
120
|
+
CACHED_VER="$(echo "$CACHED" | awk '{print $2}')"
|
|
121
|
+
if [ "$CACHED_VER" = "$LOCAL" ]; then
|
|
122
|
+
exit 0
|
|
123
|
+
fi
|
|
124
|
+
;;
|
|
125
|
+
UPGRADE_AVAILABLE*)
|
|
126
|
+
CACHED_OLD="$(echo "$CACHED" | awk '{print $2}')"
|
|
127
|
+
if [ "$CACHED_OLD" = "$LOCAL" ]; then
|
|
128
|
+
CACHED_NEW="$(echo "$CACHED" | awk '{print $3}')"
|
|
129
|
+
if check_snooze "$CACHED_NEW"; then
|
|
130
|
+
exit 0 # snoozed — stay quiet
|
|
131
|
+
fi
|
|
132
|
+
echo "$CACHED"
|
|
133
|
+
exit 0
|
|
134
|
+
fi
|
|
135
|
+
;;
|
|
136
|
+
esac
|
|
137
|
+
fi
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
# ─── Step 4: Slow path — fetch remote version ────────────────
|
|
141
|
+
mkdir -p "$STATE_DIR"
|
|
142
|
+
|
|
143
|
+
# Fire Supabase install ping in background (parallel, non-blocking)
|
|
144
|
+
# This logs an update check event for community health metrics.
|
|
145
|
+
# If the endpoint isn't configured or Supabase is down, this is a no-op.
|
|
146
|
+
# Source Supabase config for install ping
|
|
147
|
+
if [ -z "${GSTACK_TELEMETRY_ENDPOINT:-}" ] && [ -f "$GSTACK_DIR/supabase/config.sh" ]; then
|
|
148
|
+
. "$GSTACK_DIR/supabase/config.sh"
|
|
149
|
+
fi
|
|
150
|
+
_SUPA_ENDPOINT="${GSTACK_TELEMETRY_ENDPOINT:-}"
|
|
151
|
+
_SUPA_KEY="${GSTACK_SUPABASE_ANON_KEY:-}"
|
|
152
|
+
# Respect telemetry opt-out — don't ping Supabase if user set telemetry: off
|
|
153
|
+
_TEL_TIER="$("$GSTACK_DIR/bin/gstack-config" get telemetry 2>/dev/null || true)"
|
|
154
|
+
if [ -n "$_SUPA_ENDPOINT" ] && [ -n "$_SUPA_KEY" ] && [ "${_TEL_TIER:-off}" != "off" ]; then
|
|
155
|
+
_OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
156
|
+
curl -sf --max-time 5 \
|
|
157
|
+
-X POST "${_SUPA_ENDPOINT}/update_checks" \
|
|
158
|
+
-H "Content-Type: application/json" \
|
|
159
|
+
-H "apikey: ${_SUPA_KEY}" \
|
|
160
|
+
-H "Authorization: Bearer ${_SUPA_KEY}" \
|
|
161
|
+
-H "Prefer: return=minimal" \
|
|
162
|
+
-d "{\"gstack_version\":\"$LOCAL\",\"os\":\"$_OS\"}" \
|
|
163
|
+
>/dev/null 2>&1 &
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
# GitHub raw fetch (primary, always reliable)
|
|
167
|
+
REMOTE=""
|
|
168
|
+
REMOTE="$(curl -sf --max-time 5 "$REMOTE_URL" 2>/dev/null || true)"
|
|
169
|
+
REMOTE="$(echo "$REMOTE" | tr -d '[:space:]')"
|
|
170
|
+
|
|
171
|
+
# Validate: must look like a version number (reject HTML error pages)
|
|
172
|
+
if ! echo "$REMOTE" | grep -qE '^[0-9]+\.[0-9.]+$'; then
|
|
173
|
+
# Invalid or empty response — assume up to date
|
|
174
|
+
echo "UP_TO_DATE $LOCAL" > "$CACHE_FILE"
|
|
175
|
+
exit 0
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
if [ "$LOCAL" = "$REMOTE" ]; then
|
|
179
|
+
echo "UP_TO_DATE $LOCAL" > "$CACHE_FILE"
|
|
180
|
+
exit 0
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
# Versions differ — upgrade available
|
|
184
|
+
echo "UPGRADE_AVAILABLE $LOCAL $REMOTE" > "$CACHE_FILE"
|
|
185
|
+
if check_snooze "$REMOTE"; then
|
|
186
|
+
exit 0 # snoozed — stay quiet
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
# Log upgrade_prompted event (only on slow-path fetch, not cached replays)
|
|
190
|
+
TEL_CMD="$GSTACK_DIR/bin/gstack-telemetry-log"
|
|
191
|
+
if [ -x "$TEL_CMD" ]; then
|
|
192
|
+
"$TEL_CMD" --event-type upgrade_prompted --skill "" --duration 0 \
|
|
193
|
+
--outcome success --session-id "update-$$-$(date +%s)" 2>/dev/null &
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
echo "UPGRADE_AVAILABLE $LOCAL $REMOTE"
|