@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,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: benchmark
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: |
|
|
5
|
+
Performance regression detection using the browse daemon. Establishes
|
|
6
|
+
baselines for page load times, Core Web Vitals, and resource sizes.
|
|
7
|
+
Compares before/after on every PR. Tracks performance trends over time.
|
|
8
|
+
Use when: "performance", "benchmark", "page speed", "lighthouse", "web vitals",
|
|
9
|
+
"bundle size", "load time".
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- Bash
|
|
12
|
+
- Read
|
|
13
|
+
- Write
|
|
14
|
+
- Glob
|
|
15
|
+
- AskUserQuestion
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
{{PREAMBLE}}
|
|
19
|
+
|
|
20
|
+
{{BROWSE_SETUP}}
|
|
21
|
+
|
|
22
|
+
# /benchmark — Performance Regression Detection
|
|
23
|
+
|
|
24
|
+
You are a **Performance Engineer** who has optimized apps serving millions of requests. You know that performance doesn't degrade in one big regression — it dies by a thousand paper cuts. Each PR adds 50ms here, 20KB there, and one day the app takes 8 seconds to load and nobody knows when it got slow.
|
|
25
|
+
|
|
26
|
+
Your job is to measure, baseline, compare, and alert. You use the browse daemon's `perf` command and JavaScript evaluation to gather real performance data from running pages.
|
|
27
|
+
|
|
28
|
+
## User-invocable
|
|
29
|
+
When the user types `/benchmark`, run this skill.
|
|
30
|
+
|
|
31
|
+
## Arguments
|
|
32
|
+
- `/benchmark <url>` — full performance audit with baseline comparison
|
|
33
|
+
- `/benchmark <url> --baseline` — capture baseline (run before making changes)
|
|
34
|
+
- `/benchmark <url> --quick` — single-pass timing check (no baseline needed)
|
|
35
|
+
- `/benchmark <url> --pages /,/dashboard,/api/health` — specify pages
|
|
36
|
+
- `/benchmark --diff` — benchmark only pages affected by current branch
|
|
37
|
+
- `/benchmark --trend` — show performance trends from historical data
|
|
38
|
+
|
|
39
|
+
## Instructions
|
|
40
|
+
|
|
41
|
+
### Phase 1: Setup
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
source <(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null || echo "SLUG=unknown")
|
|
45
|
+
mkdir -p .gstack/benchmark-reports
|
|
46
|
+
mkdir -p .gstack/benchmark-reports/baselines
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Phase 2: Page Discovery
|
|
50
|
+
|
|
51
|
+
Same as /canary — auto-discover from navigation or use `--pages`.
|
|
52
|
+
|
|
53
|
+
If `--diff` mode:
|
|
54
|
+
```bash
|
|
55
|
+
git diff $(gh pr view --json baseRefName -q .baseRefName 2>/dev/null || gh repo view --json defaultBranchRef -q .defaultBranchRef.name 2>/dev/null || echo main)...HEAD --name-only
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Phase 3: Performance Data Collection
|
|
59
|
+
|
|
60
|
+
For each page, collect comprehensive performance metrics:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
$B goto <page-url>
|
|
64
|
+
$B perf
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then gather detailed metrics via JavaScript:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
$B eval "JSON.stringify(performance.getEntriesByType('navigation')[0])"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Extract key metrics:
|
|
74
|
+
- **TTFB** (Time to First Byte): `responseStart - requestStart`
|
|
75
|
+
- **FCP** (First Contentful Paint): from PerformanceObserver or `paint` entries
|
|
76
|
+
- **LCP** (Largest Contentful Paint): from PerformanceObserver
|
|
77
|
+
- **DOM Interactive**: `domInteractive - navigationStart`
|
|
78
|
+
- **DOM Complete**: `domComplete - navigationStart`
|
|
79
|
+
- **Full Load**: `loadEventEnd - navigationStart`
|
|
80
|
+
|
|
81
|
+
Resource analysis:
|
|
82
|
+
```bash
|
|
83
|
+
$B eval "JSON.stringify(performance.getEntriesByType('resource').map(r => ({name: r.name.split('/').pop().split('?')[0], type: r.initiatorType, size: r.transferSize, duration: Math.round(r.duration)})).sort((a,b) => b.duration - a.duration).slice(0,15))"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Bundle size check:
|
|
87
|
+
```bash
|
|
88
|
+
$B eval "JSON.stringify(performance.getEntriesByType('resource').filter(r => r.initiatorType === 'script').map(r => ({name: r.name.split('/').pop().split('?')[0], size: r.transferSize})))"
|
|
89
|
+
$B eval "JSON.stringify(performance.getEntriesByType('resource').filter(r => r.initiatorType === 'css').map(r => ({name: r.name.split('/').pop().split('?')[0], size: r.transferSize})))"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Network summary:
|
|
93
|
+
```bash
|
|
94
|
+
$B eval "(() => { const r = performance.getEntriesByType('resource'); return JSON.stringify({total_requests: r.length, total_transfer: r.reduce((s,e) => s + (e.transferSize||0), 0), by_type: Object.entries(r.reduce((a,e) => { a[e.initiatorType] = (a[e.initiatorType]||0) + 1; return a; }, {})).sort((a,b) => b[1]-a[1])})})()"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Phase 4: Baseline Capture (--baseline mode)
|
|
98
|
+
|
|
99
|
+
Save metrics to baseline file:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"url": "<url>",
|
|
104
|
+
"timestamp": "<ISO>",
|
|
105
|
+
"branch": "<branch>",
|
|
106
|
+
"pages": {
|
|
107
|
+
"/": {
|
|
108
|
+
"ttfb_ms": 120,
|
|
109
|
+
"fcp_ms": 450,
|
|
110
|
+
"lcp_ms": 800,
|
|
111
|
+
"dom_interactive_ms": 600,
|
|
112
|
+
"dom_complete_ms": 1200,
|
|
113
|
+
"full_load_ms": 1400,
|
|
114
|
+
"total_requests": 42,
|
|
115
|
+
"total_transfer_bytes": 1250000,
|
|
116
|
+
"js_bundle_bytes": 450000,
|
|
117
|
+
"css_bundle_bytes": 85000,
|
|
118
|
+
"largest_resources": [
|
|
119
|
+
{"name": "main.js", "size": 320000, "duration": 180},
|
|
120
|
+
{"name": "vendor.js", "size": 130000, "duration": 90}
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Write to `.gstack/benchmark-reports/baselines/baseline.json`.
|
|
128
|
+
|
|
129
|
+
### Phase 5: Comparison
|
|
130
|
+
|
|
131
|
+
If baseline exists, compare current metrics against it:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
PERFORMANCE REPORT — [url]
|
|
135
|
+
══════════════════════════
|
|
136
|
+
Branch: [current-branch] vs baseline ([baseline-branch])
|
|
137
|
+
|
|
138
|
+
Page: /
|
|
139
|
+
─────────────────────────────────────────────────────
|
|
140
|
+
Metric Baseline Current Delta Status
|
|
141
|
+
──────── ──────── ─────── ───── ──────
|
|
142
|
+
TTFB 120ms 135ms +15ms OK
|
|
143
|
+
FCP 450ms 480ms +30ms OK
|
|
144
|
+
LCP 800ms 1600ms +800ms REGRESSION
|
|
145
|
+
DOM Interactive 600ms 650ms +50ms OK
|
|
146
|
+
DOM Complete 1200ms 1350ms +150ms WARNING
|
|
147
|
+
Full Load 1400ms 2100ms +700ms REGRESSION
|
|
148
|
+
Total Requests 42 58 +16 WARNING
|
|
149
|
+
Transfer Size 1.2MB 1.8MB +0.6MB REGRESSION
|
|
150
|
+
JS Bundle 450KB 720KB +270KB REGRESSION
|
|
151
|
+
CSS Bundle 85KB 88KB +3KB OK
|
|
152
|
+
|
|
153
|
+
REGRESSIONS DETECTED: 3
|
|
154
|
+
[1] LCP doubled (800ms → 1600ms) — likely a large new image or blocking resource
|
|
155
|
+
[2] Total transfer +50% (1.2MB → 1.8MB) — check new JS bundles
|
|
156
|
+
[3] JS bundle +60% (450KB → 720KB) — new dependency or missing tree-shaking
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Regression thresholds:**
|
|
160
|
+
- Timing metrics: >50% increase OR >500ms absolute increase = REGRESSION
|
|
161
|
+
- Timing metrics: >20% increase = WARNING
|
|
162
|
+
- Bundle size: >25% increase = REGRESSION
|
|
163
|
+
- Bundle size: >10% increase = WARNING
|
|
164
|
+
- Request count: >30% increase = WARNING
|
|
165
|
+
|
|
166
|
+
### Phase 6: Slowest Resources
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
TOP 10 SLOWEST RESOURCES
|
|
170
|
+
═════════════════════════
|
|
171
|
+
# Resource Type Size Duration
|
|
172
|
+
1 vendor.chunk.js script 320KB 480ms
|
|
173
|
+
2 main.js script 250KB 320ms
|
|
174
|
+
3 hero-image.webp img 180KB 280ms
|
|
175
|
+
4 analytics.js script 45KB 250ms ← third-party
|
|
176
|
+
5 fonts/inter-var.woff2 font 95KB 180ms
|
|
177
|
+
...
|
|
178
|
+
|
|
179
|
+
RECOMMENDATIONS:
|
|
180
|
+
- vendor.chunk.js: Consider code-splitting — 320KB is large for initial load
|
|
181
|
+
- analytics.js: Load async/defer — blocks rendering for 250ms
|
|
182
|
+
- hero-image.webp: Add width/height to prevent CLS, consider lazy loading
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Phase 7: Performance Budget
|
|
186
|
+
|
|
187
|
+
Check against industry budgets:
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
PERFORMANCE BUDGET CHECK
|
|
191
|
+
════════════════════════
|
|
192
|
+
Metric Budget Actual Status
|
|
193
|
+
──────── ────── ────── ──────
|
|
194
|
+
FCP < 1.8s 0.48s PASS
|
|
195
|
+
LCP < 2.5s 1.6s PASS
|
|
196
|
+
Total JS < 500KB 720KB FAIL
|
|
197
|
+
Total CSS < 100KB 88KB PASS
|
|
198
|
+
Total Transfer < 2MB 1.8MB WARNING (90%)
|
|
199
|
+
HTTP Requests < 50 58 FAIL
|
|
200
|
+
|
|
201
|
+
Grade: B (4/6 passing)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Phase 8: Trend Analysis (--trend mode)
|
|
205
|
+
|
|
206
|
+
Load historical baseline files and show trends:
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
PERFORMANCE TRENDS (last 5 benchmarks)
|
|
210
|
+
══════════════════════════════════════
|
|
211
|
+
Date FCP LCP Bundle Requests Grade
|
|
212
|
+
2026-03-10 420ms 750ms 380KB 38 A
|
|
213
|
+
2026-03-12 440ms 780ms 410KB 40 A
|
|
214
|
+
2026-03-14 450ms 800ms 450KB 42 A
|
|
215
|
+
2026-03-16 460ms 850ms 520KB 48 B
|
|
216
|
+
2026-03-18 480ms 1600ms 720KB 58 B
|
|
217
|
+
|
|
218
|
+
TREND: Performance degrading. LCP doubled in 8 days.
|
|
219
|
+
JS bundle growing 50KB/week. Investigate.
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Phase 9: Save Report
|
|
223
|
+
|
|
224
|
+
Write to `.gstack/benchmark-reports/{date}-benchmark.md` and `.gstack/benchmark-reports/{date}-benchmark.json`.
|
|
225
|
+
|
|
226
|
+
## Important Rules
|
|
227
|
+
|
|
228
|
+
- **Measure, don't guess.** Use actual performance.getEntries() data, not estimates.
|
|
229
|
+
- **Baseline is essential.** Without a baseline, you can report absolute numbers but can't detect regressions. Always encourage baseline capture.
|
|
230
|
+
- **Relative thresholds, not absolute.** 2000ms load time is fine for a complex dashboard, terrible for a landing page. Compare against YOUR baseline.
|
|
231
|
+
- **Third-party scripts are context.** Flag them, but the user can't fix Google Analytics being slow. Focus recommendations on first-party resources.
|
|
232
|
+
- **Bundle size is the leading indicator.** Load time varies with network. Bundle size is deterministic. Track it religiously.
|
|
233
|
+
- **Read-only.** Produce the report. Don't modify code unless explicitly asked.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Set up gstack for local development — test skills from within this repo.
|
|
3
|
+
#
|
|
4
|
+
# Creates .claude/skills/gstack → (symlink to repo root) so Claude Code
|
|
5
|
+
# discovers skills from your working tree. Changes take effect immediately.
|
|
6
|
+
#
|
|
7
|
+
# Also copies .env from the main worktree if this is a Conductor workspace
|
|
8
|
+
# or git worktree (so API keys carry over automatically).
|
|
9
|
+
#
|
|
10
|
+
# Usage: bin/dev-setup # set up
|
|
11
|
+
# bin/dev-teardown # clean up
|
|
12
|
+
set -e
|
|
13
|
+
|
|
14
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
15
|
+
|
|
16
|
+
# 1. Copy .env from main worktree (if we're a worktree and don't have one)
|
|
17
|
+
if [ ! -f "$REPO_ROOT/.env" ]; then
|
|
18
|
+
MAIN_WORKTREE="$(git -C "$REPO_ROOT" worktree list --porcelain 2>/dev/null | head -1 | sed 's/^worktree //')"
|
|
19
|
+
if [ -n "$MAIN_WORKTREE" ] && [ "$MAIN_WORKTREE" != "$REPO_ROOT" ] && [ -f "$MAIN_WORKTREE/.env" ]; then
|
|
20
|
+
cp "$MAIN_WORKTREE/.env" "$REPO_ROOT/.env"
|
|
21
|
+
echo "Copied .env from main worktree ($MAIN_WORKTREE)"
|
|
22
|
+
fi
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# 2. Install dependencies
|
|
26
|
+
if [ ! -d "$REPO_ROOT/node_modules" ]; then
|
|
27
|
+
echo "Installing dependencies..."
|
|
28
|
+
(cd "$REPO_ROOT" && bun install)
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# 3. Create .claude/skills/ inside the repo
|
|
32
|
+
mkdir -p "$REPO_ROOT/.claude/skills"
|
|
33
|
+
|
|
34
|
+
# 4. Symlink .claude/skills/gstack → repo root
|
|
35
|
+
# This makes setup think it's inside a real .claude/skills/ directory
|
|
36
|
+
GSTACK_LINK="$REPO_ROOT/.claude/skills/gstack"
|
|
37
|
+
if [ -L "$GSTACK_LINK" ]; then
|
|
38
|
+
echo "Updating existing symlink..."
|
|
39
|
+
rm "$GSTACK_LINK"
|
|
40
|
+
elif [ -d "$GSTACK_LINK" ]; then
|
|
41
|
+
echo "Error: .claude/skills/gstack is a real directory, not a symlink." >&2
|
|
42
|
+
echo "Remove it manually if you want to use dev mode." >&2
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
ln -s "$REPO_ROOT" "$GSTACK_LINK"
|
|
46
|
+
|
|
47
|
+
# 5. Create .agents/skills/gstack → repo root (for Codex/Gemini/Cursor)
|
|
48
|
+
mkdir -p "$REPO_ROOT/.agents/skills"
|
|
49
|
+
AGENTS_LINK="$REPO_ROOT/.agents/skills/gstack"
|
|
50
|
+
if [ -L "$AGENTS_LINK" ]; then
|
|
51
|
+
rm "$AGENTS_LINK"
|
|
52
|
+
elif [ -d "$AGENTS_LINK" ]; then
|
|
53
|
+
echo "Warning: .agents/skills/gstack is a real directory, skipping." >&2
|
|
54
|
+
fi
|
|
55
|
+
if [ ! -e "$AGENTS_LINK" ]; then
|
|
56
|
+
ln -s "$REPO_ROOT" "$AGENTS_LINK"
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# 6. Run setup via the symlink so it detects .claude/skills/ as its parent
|
|
60
|
+
"$GSTACK_LINK/setup"
|
|
61
|
+
|
|
62
|
+
echo ""
|
|
63
|
+
echo "Dev mode active. Skills resolve from this working tree."
|
|
64
|
+
echo " .claude/skills/gstack → $REPO_ROOT"
|
|
65
|
+
echo " .agents/skills/gstack → $REPO_ROOT"
|
|
66
|
+
echo "Edit any SKILL.md and test immediately — no copy/deploy needed."
|
|
67
|
+
echo ""
|
|
68
|
+
echo "To tear down: bin/dev-teardown"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Remove local dev skill symlinks. Restores global gstack as the active install.
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
6
|
+
|
|
7
|
+
removed=()
|
|
8
|
+
|
|
9
|
+
# ─── Clean up .claude/skills/ ─────────────────────────────────
|
|
10
|
+
CLAUDE_SKILLS="$REPO_ROOT/.claude/skills"
|
|
11
|
+
if [ -d "$CLAUDE_SKILLS" ]; then
|
|
12
|
+
for link in "$CLAUDE_SKILLS"/*/; do
|
|
13
|
+
name="$(basename "$link")"
|
|
14
|
+
[ "$name" = "gstack" ] && continue
|
|
15
|
+
if [ -L "${link%/}" ]; then
|
|
16
|
+
rm "${link%/}"
|
|
17
|
+
removed+=("claude/$name")
|
|
18
|
+
fi
|
|
19
|
+
done
|
|
20
|
+
|
|
21
|
+
if [ -L "$CLAUDE_SKILLS/gstack" ]; then
|
|
22
|
+
rm "$CLAUDE_SKILLS/gstack"
|
|
23
|
+
removed+=("claude/gstack")
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
rmdir "$CLAUDE_SKILLS" 2>/dev/null || true
|
|
27
|
+
rmdir "$REPO_ROOT/.claude" 2>/dev/null || true
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# ─── Clean up .agents/skills/ ────────────────────────────────
|
|
31
|
+
AGENTS_SKILLS="$REPO_ROOT/.agents/skills"
|
|
32
|
+
if [ -d "$AGENTS_SKILLS" ]; then
|
|
33
|
+
for link in "$AGENTS_SKILLS"/*/; do
|
|
34
|
+
name="$(basename "$link")"
|
|
35
|
+
[ "$name" = "gstack" ] && continue
|
|
36
|
+
if [ -L "${link%/}" ]; then
|
|
37
|
+
rm "${link%/}"
|
|
38
|
+
removed+=("agents/$name")
|
|
39
|
+
fi
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
if [ -L "$AGENTS_SKILLS/gstack" ]; then
|
|
43
|
+
rm "$AGENTS_SKILLS/gstack"
|
|
44
|
+
removed+=("agents/gstack")
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
rmdir "$AGENTS_SKILLS" 2>/dev/null || true
|
|
48
|
+
rmdir "$REPO_ROOT/.agents" 2>/dev/null || true
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
if [ ${#removed[@]} -gt 0 ]; then
|
|
52
|
+
echo "Removed: ${removed[*]}"
|
|
53
|
+
else
|
|
54
|
+
echo "No symlinks found."
|
|
55
|
+
fi
|
|
56
|
+
echo "Dev mode deactivated. Global gstack (~/.claude/skills/gstack) is now active."
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# gstack-analytics — personal usage dashboard from local JSONL
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# gstack-analytics # default: last 7 days
|
|
6
|
+
# gstack-analytics 7d # last 7 days
|
|
7
|
+
# gstack-analytics 30d # last 30 days
|
|
8
|
+
# gstack-analytics all # all time
|
|
9
|
+
#
|
|
10
|
+
# Env overrides (for testing):
|
|
11
|
+
# GSTACK_STATE_DIR — override ~/.gstack state directory
|
|
12
|
+
set -uo pipefail
|
|
13
|
+
|
|
14
|
+
STATE_DIR="${GSTACK_STATE_DIR:-$HOME/.gstack}"
|
|
15
|
+
JSONL_FILE="$STATE_DIR/analytics/skill-usage.jsonl"
|
|
16
|
+
|
|
17
|
+
# ─── Parse time window ───────────────────────────────────────
|
|
18
|
+
WINDOW="${1:-7d}"
|
|
19
|
+
case "$WINDOW" in
|
|
20
|
+
7d) DAYS=7; LABEL="last 7 days" ;;
|
|
21
|
+
30d) DAYS=30; LABEL="last 30 days" ;;
|
|
22
|
+
all) DAYS=0; LABEL="all time" ;;
|
|
23
|
+
*) DAYS=7; LABEL="last 7 days" ;;
|
|
24
|
+
esac
|
|
25
|
+
|
|
26
|
+
# ─── Check for data ──────────────────────────────────────────
|
|
27
|
+
if [ ! -f "$JSONL_FILE" ]; then
|
|
28
|
+
echo "gstack usage — no data yet"
|
|
29
|
+
echo ""
|
|
30
|
+
echo "Usage data will appear here after you use gstack skills"
|
|
31
|
+
echo "with telemetry enabled (gstack-config set telemetry anonymous)."
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
TOTAL_LINES="$(wc -l < "$JSONL_FILE" | tr -d ' ')"
|
|
36
|
+
if [ "$TOTAL_LINES" = "0" ]; then
|
|
37
|
+
echo "gstack usage — no data yet"
|
|
38
|
+
exit 0
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# ─── Filter by time window ───────────────────────────────────
|
|
42
|
+
if [ "$DAYS" -gt 0 ] 2>/dev/null; then
|
|
43
|
+
# Calculate cutoff date
|
|
44
|
+
if date -v-1d +%Y-%m-%d >/dev/null 2>&1; then
|
|
45
|
+
# macOS date
|
|
46
|
+
CUTOFF="$(date -v-${DAYS}d -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
47
|
+
else
|
|
48
|
+
# GNU date
|
|
49
|
+
CUTOFF="$(date -u -d "$DAYS days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "2000-01-01T00:00:00Z")"
|
|
50
|
+
fi
|
|
51
|
+
# Filter: skill_run events (new format) OR basic skill events (old format, no event_type)
|
|
52
|
+
# Old format: {"skill":"X","ts":"Y","repo":"Z"} (no event_type field)
|
|
53
|
+
# New format: {"event_type":"skill_run","skill":"X","ts":"Y",...}
|
|
54
|
+
FILTERED="$(awk -F'"' -v cutoff="$CUTOFF" '
|
|
55
|
+
/"ts":"/ {
|
|
56
|
+
# Skip hook_fire events
|
|
57
|
+
if (/"event":"hook_fire"/) next
|
|
58
|
+
# Skip non-skill_run new-format events
|
|
59
|
+
if (/"event_type":"/ && !/"event_type":"skill_run"/) next
|
|
60
|
+
for (i=1; i<=NF; i++) {
|
|
61
|
+
if ($i == "ts" && $(i+1) ~ /^:/) {
|
|
62
|
+
ts = $(i+2)
|
|
63
|
+
if (ts >= cutoff) { print; break }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
' "$JSONL_FILE")"
|
|
68
|
+
else
|
|
69
|
+
# All time: include skill_run events + old-format basic events, exclude hook_fire
|
|
70
|
+
FILTERED="$(awk '/"ts":"/ && !/"event":"hook_fire"/' "$JSONL_FILE" | grep -v '"event_type":"upgrade_' 2>/dev/null || true)"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
if [ -z "$FILTERED" ]; then
|
|
74
|
+
echo "gstack usage ($LABEL) — no skill runs found"
|
|
75
|
+
exit 0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# ─── Aggregate by skill ──────────────────────────────────────
|
|
79
|
+
# Extract skill names and count
|
|
80
|
+
SKILL_COUNTS="$(echo "$FILTERED" | awk -F'"' '
|
|
81
|
+
/"skill":"/ {
|
|
82
|
+
for (i=1; i<=NF; i++) {
|
|
83
|
+
if ($i == "skill" && $(i+1) ~ /^:/) {
|
|
84
|
+
skill = $(i+2)
|
|
85
|
+
counts[skill]++
|
|
86
|
+
break
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
END {
|
|
91
|
+
for (s in counts) print counts[s], s
|
|
92
|
+
}
|
|
93
|
+
' | sort -rn)"
|
|
94
|
+
|
|
95
|
+
# Count outcomes
|
|
96
|
+
TOTAL="$(echo "$FILTERED" | wc -l | tr -d ' ')"
|
|
97
|
+
SUCCESS="$(echo "$FILTERED" | grep -c '"outcome":"success"' || true)"
|
|
98
|
+
SUCCESS="${SUCCESS:-0}"; SUCCESS="$(echo "$SUCCESS" | tr -d ' \n\r\t')"
|
|
99
|
+
ERRORS="$(echo "$FILTERED" | grep -c '"outcome":"error"' || true)"
|
|
100
|
+
ERRORS="${ERRORS:-0}"; ERRORS="$(echo "$ERRORS" | tr -d ' \n\r\t')"
|
|
101
|
+
# Old format events have no outcome field — count them as successful
|
|
102
|
+
NO_OUTCOME="$(echo "$FILTERED" | grep -vc '"outcome":' || true)"
|
|
103
|
+
NO_OUTCOME="${NO_OUTCOME:-0}"; NO_OUTCOME="$(echo "$NO_OUTCOME" | tr -d ' \n\r\t')"
|
|
104
|
+
SUCCESS=$(( SUCCESS + NO_OUTCOME ))
|
|
105
|
+
|
|
106
|
+
# Calculate success rate
|
|
107
|
+
if [ "$TOTAL" -gt 0 ] 2>/dev/null; then
|
|
108
|
+
SUCCESS_RATE=$(( SUCCESS * 100 / TOTAL ))
|
|
109
|
+
else
|
|
110
|
+
SUCCESS_RATE=100
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# ─── Calculate total duration ────────────────────────────────
|
|
114
|
+
TOTAL_DURATION="$(echo "$FILTERED" | awk -F'[:,]' '
|
|
115
|
+
/"duration_s"/ {
|
|
116
|
+
for (i=1; i<=NF; i++) {
|
|
117
|
+
if ($i ~ /"duration_s"/) {
|
|
118
|
+
val = $(i+1)
|
|
119
|
+
gsub(/[^0-9.]/, "", val)
|
|
120
|
+
if (val+0 > 0) total += val
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
END { printf "%.0f", total }
|
|
125
|
+
')"
|
|
126
|
+
|
|
127
|
+
# Format duration
|
|
128
|
+
TOTAL_DURATION="${TOTAL_DURATION:-0}"
|
|
129
|
+
if [ "$TOTAL_DURATION" -ge 3600 ] 2>/dev/null; then
|
|
130
|
+
HOURS=$(( TOTAL_DURATION / 3600 ))
|
|
131
|
+
MINS=$(( (TOTAL_DURATION % 3600) / 60 ))
|
|
132
|
+
DUR_DISPLAY="${HOURS}h ${MINS}m"
|
|
133
|
+
elif [ "$TOTAL_DURATION" -ge 60 ] 2>/dev/null; then
|
|
134
|
+
MINS=$(( TOTAL_DURATION / 60 ))
|
|
135
|
+
DUR_DISPLAY="${MINS}m"
|
|
136
|
+
else
|
|
137
|
+
DUR_DISPLAY="${TOTAL_DURATION}s"
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
# ─── Render output ───────────────────────────────────────────
|
|
141
|
+
echo "gstack usage ($LABEL)"
|
|
142
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
143
|
+
|
|
144
|
+
# Find max count for bar scaling
|
|
145
|
+
MAX_COUNT="$(echo "$SKILL_COUNTS" | head -1 | awk '{print $1}')"
|
|
146
|
+
BAR_WIDTH=20
|
|
147
|
+
|
|
148
|
+
echo "$SKILL_COUNTS" | while read -r COUNT SKILL; do
|
|
149
|
+
# Scale bar
|
|
150
|
+
if [ "$MAX_COUNT" -gt 0 ] 2>/dev/null; then
|
|
151
|
+
BAR_LEN=$(( COUNT * BAR_WIDTH / MAX_COUNT ))
|
|
152
|
+
else
|
|
153
|
+
BAR_LEN=1
|
|
154
|
+
fi
|
|
155
|
+
[ "$BAR_LEN" -lt 1 ] && BAR_LEN=1
|
|
156
|
+
|
|
157
|
+
# Build bar
|
|
158
|
+
BAR=""
|
|
159
|
+
i=0
|
|
160
|
+
while [ "$i" -lt "$BAR_LEN" ]; do
|
|
161
|
+
BAR="${BAR}█"
|
|
162
|
+
i=$(( i + 1 ))
|
|
163
|
+
done
|
|
164
|
+
|
|
165
|
+
# Calculate avg duration for this skill
|
|
166
|
+
AVG_DUR="$(echo "$FILTERED" | awk -v skill="$SKILL" '
|
|
167
|
+
index($0, "\"skill\":\"" skill "\"") > 0 {
|
|
168
|
+
# Extract duration_s value using split on "duration_s":
|
|
169
|
+
n = split($0, parts, "\"duration_s\":")
|
|
170
|
+
if (n >= 2) {
|
|
171
|
+
# parts[2] starts with the value, e.g. "142,"
|
|
172
|
+
gsub(/[^0-9.].*/, "", parts[2])
|
|
173
|
+
if (parts[2]+0 > 0) { total += parts[2]; count++ }
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
END { if (count > 0) printf "%.0f", total/count; else print "0" }
|
|
177
|
+
')"
|
|
178
|
+
|
|
179
|
+
# Format avg duration
|
|
180
|
+
if [ "$AVG_DUR" -ge 60 ] 2>/dev/null; then
|
|
181
|
+
AVG_DISPLAY="$(( AVG_DUR / 60 ))m"
|
|
182
|
+
else
|
|
183
|
+
AVG_DISPLAY="${AVG_DUR}s"
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
printf " /%-20s %s %d runs (avg %s)\n" "$SKILL" "$BAR" "$COUNT" "$AVG_DISPLAY"
|
|
187
|
+
done
|
|
188
|
+
|
|
189
|
+
echo ""
|
|
190
|
+
echo "Success rate: ${SUCCESS_RATE}% | Errors: ${ERRORS} | Total time: ${DUR_DISPLAY}"
|
|
191
|
+
echo "Events: ${TOTAL} skill runs"
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# gstack-community-dashboard — community usage stats from Supabase
|
|
3
|
+
#
|
|
4
|
+
# Queries the Supabase REST API to show community-wide gstack usage:
|
|
5
|
+
# skill popularity, crash clusters, version distribution, retention.
|
|
6
|
+
#
|
|
7
|
+
# Env overrides (for testing):
|
|
8
|
+
# GSTACK_DIR — override auto-detected gstack root
|
|
9
|
+
# GSTACK_SUPABASE_URL — override Supabase project URL
|
|
10
|
+
# GSTACK_SUPABASE_ANON_KEY — override Supabase anon key
|
|
11
|
+
set -uo pipefail
|
|
12
|
+
|
|
13
|
+
GSTACK_DIR="${GSTACK_DIR:-$(cd "$(dirname "$0")/.." && pwd)}"
|
|
14
|
+
|
|
15
|
+
# Source Supabase config if not overridden by env
|
|
16
|
+
if [ -z "${GSTACK_SUPABASE_URL:-}" ] && [ -f "$GSTACK_DIR/supabase/config.sh" ]; then
|
|
17
|
+
. "$GSTACK_DIR/supabase/config.sh"
|
|
18
|
+
fi
|
|
19
|
+
SUPABASE_URL="${GSTACK_SUPABASE_URL:-}"
|
|
20
|
+
ANON_KEY="${GSTACK_SUPABASE_ANON_KEY:-}"
|
|
21
|
+
|
|
22
|
+
if [ -z "$SUPABASE_URL" ] || [ -z "$ANON_KEY" ]; then
|
|
23
|
+
echo "gstack community dashboard"
|
|
24
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
25
|
+
echo ""
|
|
26
|
+
echo "Supabase not configured yet. The community dashboard will be"
|
|
27
|
+
echo "available once the gstack Supabase project is set up."
|
|
28
|
+
echo ""
|
|
29
|
+
echo "For local analytics, run: gstack-analytics"
|
|
30
|
+
exit 0
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# ─── Helper: query Supabase REST API ─────────────────────────
|
|
34
|
+
query() {
|
|
35
|
+
local table="$1"
|
|
36
|
+
local params="${2:-}"
|
|
37
|
+
curl -sf --max-time 10 \
|
|
38
|
+
"${SUPABASE_URL}/rest/v1/${table}?${params}" \
|
|
39
|
+
-H "apikey: ${ANON_KEY}" \
|
|
40
|
+
-H "Authorization: Bearer ${ANON_KEY}" \
|
|
41
|
+
2>/dev/null || echo "[]"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
echo "gstack community dashboard"
|
|
45
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
46
|
+
echo ""
|
|
47
|
+
|
|
48
|
+
# ─── Weekly active installs ──────────────────────────────────
|
|
49
|
+
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 "")"
|
|
50
|
+
if [ -n "$WEEK_AGO" ]; then
|
|
51
|
+
PULSE="$(curl -sf --max-time 10 \
|
|
52
|
+
"${SUPABASE_URL}/functions/v1/community-pulse" \
|
|
53
|
+
-H "Authorization: Bearer ${ANON_KEY}" \
|
|
54
|
+
2>/dev/null || echo '{"weekly_active":0}')"
|
|
55
|
+
|
|
56
|
+
WEEKLY="$(echo "$PULSE" | grep -o '"weekly_active":[0-9]*' | grep -o '[0-9]*' || echo "0")"
|
|
57
|
+
CHANGE="$(echo "$PULSE" | grep -o '"change_pct":[0-9-]*' | grep -o '[0-9-]*' || echo "0")"
|
|
58
|
+
|
|
59
|
+
echo "Weekly active installs: ${WEEKLY}"
|
|
60
|
+
if [ "$CHANGE" -gt 0 ] 2>/dev/null; then
|
|
61
|
+
echo " Change: +${CHANGE}%"
|
|
62
|
+
elif [ "$CHANGE" -lt 0 ] 2>/dev/null; then
|
|
63
|
+
echo " Change: ${CHANGE}%"
|
|
64
|
+
fi
|
|
65
|
+
echo ""
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# ─── Skill popularity (top 10) ───────────────────────────────
|
|
69
|
+
echo "Top skills (last 7 days)"
|
|
70
|
+
echo "────────────────────────"
|
|
71
|
+
|
|
72
|
+
# Query telemetry_events, group by skill
|
|
73
|
+
EVENTS="$(query "telemetry_events" "select=skill,gstack_version&event_type=eq.skill_run&event_timestamp=gte.${WEEK_AGO}&limit=1000" 2>/dev/null || echo "[]")"
|
|
74
|
+
|
|
75
|
+
if [ "$EVENTS" != "[]" ] && [ -n "$EVENTS" ]; then
|
|
76
|
+
echo "$EVENTS" | grep -o '"skill":"[^"]*"' | awk -F'"' '{print $4}' | sort | uniq -c | sort -rn | head -10 | while read -r COUNT SKILL; do
|
|
77
|
+
printf " /%-20s %d runs\n" "$SKILL" "$COUNT"
|
|
78
|
+
done
|
|
79
|
+
else
|
|
80
|
+
echo " No data yet"
|
|
81
|
+
fi
|
|
82
|
+
echo ""
|
|
83
|
+
|
|
84
|
+
# ─── Crash clusters ──────────────────────────────────────────
|
|
85
|
+
echo "Top crash clusters"
|
|
86
|
+
echo "──────────────────"
|
|
87
|
+
|
|
88
|
+
CRASHES="$(query "crash_clusters" "select=error_class,gstack_version,total_occurrences,identified_users&limit=5" 2>/dev/null || echo "[]")"
|
|
89
|
+
|
|
90
|
+
if [ "$CRASHES" != "[]" ] && [ -n "$CRASHES" ]; then
|
|
91
|
+
echo "$CRASHES" | grep -o '"error_class":"[^"]*"' | awk -F'"' '{print $4}' | head -5 | while read -r ERR; do
|
|
92
|
+
C="$(echo "$CRASHES" | grep -o "\"error_class\":\"$ERR\"[^}]*\"total_occurrences\":[0-9]*" | grep -o '"total_occurrences":[0-9]*' | head -1 | grep -o '[0-9]*')"
|
|
93
|
+
printf " %-30s %s occurrences\n" "$ERR" "${C:-?}"
|
|
94
|
+
done
|
|
95
|
+
else
|
|
96
|
+
echo " No crashes reported"
|
|
97
|
+
fi
|
|
98
|
+
echo ""
|
|
99
|
+
|
|
100
|
+
# ─── Version distribution ────────────────────────────────────
|
|
101
|
+
echo "Version distribution (last 7 days)"
|
|
102
|
+
echo "───────────────────────────────────"
|
|
103
|
+
|
|
104
|
+
if [ "$EVENTS" != "[]" ] && [ -n "$EVENTS" ]; then
|
|
105
|
+
echo "$EVENTS" | grep -o '"gstack_version":"[^"]*"' | awk -F'"' '{print $4}' | sort | uniq -c | sort -rn | head -5 | while read -r COUNT VER; do
|
|
106
|
+
printf " v%-15s %d events\n" "$VER" "$COUNT"
|
|
107
|
+
done
|
|
108
|
+
else
|
|
109
|
+
echo " No data yet"
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
echo ""
|
|
113
|
+
echo "For local analytics: gstack-analytics"
|