@runchr/gstack-antigravity 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @runchr/gstack-antigravity might be problematic. Click here for more details.

Files changed (297) hide show
  1. package/.agents/rules/ETHOS.md +129 -0
  2. package/.agents/rules/global-gstack.md +117 -0
  3. package/.agents/rules/persona-gstack-autoplan.md +14 -0
  4. package/.agents/rules/persona-gstack-benchmark.md +14 -0
  5. package/.agents/rules/persona-gstack-browse.md +14 -0
  6. package/.agents/rules/persona-gstack-canary.md +14 -0
  7. package/.agents/rules/persona-gstack-careful.md +14 -0
  8. package/.agents/rules/persona-gstack-codex.md +14 -0
  9. package/.agents/rules/persona-gstack-cso.md +14 -0
  10. package/.agents/rules/persona-gstack-design-consultation.md +14 -0
  11. package/.agents/rules/persona-gstack-design-review.md +14 -0
  12. package/.agents/rules/persona-gstack-document-release.md +14 -0
  13. package/.agents/rules/persona-gstack-freeze.md +14 -0
  14. package/.agents/rules/persona-gstack-gstack-upgrade.md +14 -0
  15. package/.agents/rules/persona-gstack-guard.md +14 -0
  16. package/.agents/rules/persona-gstack-investigate.md +14 -0
  17. package/.agents/rules/persona-gstack-land-and-deploy.md +14 -0
  18. package/.agents/rules/persona-gstack-office-hours.md +14 -0
  19. package/.agents/rules/persona-gstack-plan-ceo-review.md +14 -0
  20. package/.agents/rules/persona-gstack-plan-design-review.md +14 -0
  21. package/.agents/rules/persona-gstack-plan-eng-review.md +14 -0
  22. package/.agents/rules/persona-gstack-qa-only.md +14 -0
  23. package/.agents/rules/persona-gstack-qa.md +14 -0
  24. package/.agents/rules/persona-gstack-retro.md +14 -0
  25. package/.agents/rules/persona-gstack-review.md +14 -0
  26. package/.agents/rules/persona-gstack-setup-browser-cookies.md +14 -0
  27. package/.agents/rules/persona-gstack-setup-deploy.md +14 -0
  28. package/.agents/rules/persona-gstack-ship.md +14 -0
  29. package/.agents/rules/persona-gstack-unfreeze.md +14 -0
  30. package/.agents/rules/persona-gstack.md +40 -0
  31. package/.agents/rules/recursive-identities.md +22 -0
  32. package/.agents/workflows/autoplan.md +30 -0
  33. package/.agents/workflows/benchmark.md +31 -0
  34. package/.agents/workflows/browse.md +26 -0
  35. package/.agents/workflows/canary.md +33 -0
  36. package/.agents/workflows/careful.md +22 -0
  37. package/.agents/workflows/codex.md +36 -0
  38. package/.agents/workflows/cso.md +29 -0
  39. package/.agents/workflows/design-consultation.md +28 -0
  40. package/.agents/workflows/design-review.md +28 -0
  41. package/.agents/workflows/document-release.md +32 -0
  42. package/.agents/workflows/freeze.md +17 -0
  43. package/.agents/workflows/gstack-upgrade.md +54 -0
  44. package/.agents/workflows/gstack.md +56 -0
  45. package/.agents/workflows/guard.md +18 -0
  46. package/.agents/workflows/investigate.md +37 -0
  47. package/.agents/workflows/land-and-deploy.md +35 -0
  48. package/.agents/workflows/office-hours.md +27 -0
  49. package/.agents/workflows/plan-ceo-review.md +34 -0
  50. package/.agents/workflows/plan-design-review.md +31 -0
  51. package/.agents/workflows/plan-eng-review.md +28 -0
  52. package/.agents/workflows/qa-only.md +28 -0
  53. package/.agents/workflows/qa.md +73 -0
  54. package/.agents/workflows/retro.md +34 -0
  55. package/.agents/workflows/review.md +30 -0
  56. package/.agents/workflows/setup-browser-cookies.md +15 -0
  57. package/.agents/workflows/setup-cookies.md +8 -0
  58. package/.agents/workflows/setup-deploy.md +21 -0
  59. package/.agents/workflows/ship.md +93 -0
  60. package/.agents/workflows/unfreeze.md +12 -0
  61. package/LICENSE +22 -0
  62. package/README.md +189 -0
  63. package/README_KO.md +191 -0
  64. package/bin/install.js +105 -0
  65. package/gstack-origin/.agents/skills/gstack/SKILL.md +651 -0
  66. package/gstack-origin/.agents/skills/gstack-autoplan/SKILL.md +678 -0
  67. package/gstack-origin/.agents/skills/gstack-benchmark/SKILL.md +482 -0
  68. package/gstack-origin/.agents/skills/gstack-browse/SKILL.md +511 -0
  69. package/gstack-origin/.agents/skills/gstack-canary/SKILL.md +486 -0
  70. package/gstack-origin/.agents/skills/gstack-careful/SKILL.md +50 -0
  71. package/gstack-origin/.agents/skills/gstack-cso/SKILL.md +607 -0
  72. package/gstack-origin/.agents/skills/gstack-design-consultation/SKILL.md +615 -0
  73. package/gstack-origin/.agents/skills/gstack-design-review/SKILL.md +988 -0
  74. package/gstack-origin/.agents/skills/gstack-document-release/SKILL.md +604 -0
  75. package/gstack-origin/.agents/skills/gstack-freeze/SKILL.md +67 -0
  76. package/gstack-origin/.agents/skills/gstack-guard/SKILL.md +62 -0
  77. package/gstack-origin/.agents/skills/gstack-investigate/SKILL.md +415 -0
  78. package/gstack-origin/.agents/skills/gstack-land-and-deploy/SKILL.md +873 -0
  79. package/gstack-origin/.agents/skills/gstack-office-hours/SKILL.md +986 -0
  80. package/gstack-origin/.agents/skills/gstack-plan-ceo-review/SKILL.md +1268 -0
  81. package/gstack-origin/.agents/skills/gstack-plan-design-review/SKILL.md +668 -0
  82. package/gstack-origin/.agents/skills/gstack-plan-eng-review/SKILL.md +826 -0
  83. package/gstack-origin/.agents/skills/gstack-qa/SKILL.md +1006 -0
  84. package/gstack-origin/.agents/skills/gstack-qa-only/SKILL.md +626 -0
  85. package/gstack-origin/.agents/skills/gstack-retro/SKILL.md +1065 -0
  86. package/gstack-origin/.agents/skills/gstack-review/SKILL.md +704 -0
  87. package/gstack-origin/.agents/skills/gstack-setup-browser-cookies/SKILL.md +325 -0
  88. package/gstack-origin/.agents/skills/gstack-setup-deploy/SKILL.md +450 -0
  89. package/gstack-origin/.agents/skills/gstack-ship/SKILL.md +1312 -0
  90. package/gstack-origin/.agents/skills/gstack-unfreeze/SKILL.md +36 -0
  91. package/gstack-origin/.agents/skills/gstack-upgrade/SKILL.md +220 -0
  92. package/gstack-origin/.env.example +5 -0
  93. package/gstack-origin/.github/workflows/skill-docs.yml +17 -0
  94. package/gstack-origin/AGENTS.md +49 -0
  95. package/gstack-origin/ARCHITECTURE.md +359 -0
  96. package/gstack-origin/BROWSER.md +271 -0
  97. package/gstack-origin/CHANGELOG.md +800 -0
  98. package/gstack-origin/CLAUDE.md +284 -0
  99. package/gstack-origin/CONTRIBUTING.md +370 -0
  100. package/gstack-origin/ETHOS.md +129 -0
  101. package/gstack-origin/LICENSE +21 -0
  102. package/gstack-origin/README.md +228 -0
  103. package/gstack-origin/SKILL.md +657 -0
  104. package/gstack-origin/SKILL.md.tmpl +281 -0
  105. package/gstack-origin/TODOS.md +564 -0
  106. package/gstack-origin/VERSION +1 -0
  107. package/gstack-origin/autoplan/SKILL.md +689 -0
  108. package/gstack-origin/autoplan/SKILL.md.tmpl +416 -0
  109. package/gstack-origin/benchmark/SKILL.md +489 -0
  110. package/gstack-origin/benchmark/SKILL.md.tmpl +233 -0
  111. package/gstack-origin/bin/dev-setup +68 -0
  112. package/gstack-origin/bin/dev-teardown +56 -0
  113. package/gstack-origin/bin/gstack-analytics +191 -0
  114. package/gstack-origin/bin/gstack-community-dashboard +113 -0
  115. package/gstack-origin/bin/gstack-config +38 -0
  116. package/gstack-origin/bin/gstack-diff-scope +71 -0
  117. package/gstack-origin/bin/gstack-global-discover.ts +591 -0
  118. package/gstack-origin/bin/gstack-repo-mode +93 -0
  119. package/gstack-origin/bin/gstack-review-log +9 -0
  120. package/gstack-origin/bin/gstack-review-read +12 -0
  121. package/gstack-origin/bin/gstack-slug +15 -0
  122. package/gstack-origin/bin/gstack-telemetry-log +158 -0
  123. package/gstack-origin/bin/gstack-telemetry-sync +127 -0
  124. package/gstack-origin/bin/gstack-update-check +196 -0
  125. package/gstack-origin/browse/SKILL.md +517 -0
  126. package/gstack-origin/browse/SKILL.md.tmpl +141 -0
  127. package/gstack-origin/browse/bin/find-browse +21 -0
  128. package/gstack-origin/browse/bin/remote-slug +14 -0
  129. package/gstack-origin/browse/scripts/build-node-server.sh +48 -0
  130. package/gstack-origin/browse/src/browser-manager.ts +634 -0
  131. package/gstack-origin/browse/src/buffers.ts +137 -0
  132. package/gstack-origin/browse/src/bun-polyfill.cjs +109 -0
  133. package/gstack-origin/browse/src/cli.ts +420 -0
  134. package/gstack-origin/browse/src/commands.ts +111 -0
  135. package/gstack-origin/browse/src/config.ts +150 -0
  136. package/gstack-origin/browse/src/cookie-import-browser.ts +417 -0
  137. package/gstack-origin/browse/src/cookie-picker-routes.ts +207 -0
  138. package/gstack-origin/browse/src/cookie-picker-ui.ts +541 -0
  139. package/gstack-origin/browse/src/find-browse.ts +61 -0
  140. package/gstack-origin/browse/src/meta-commands.ts +269 -0
  141. package/gstack-origin/browse/src/platform.ts +17 -0
  142. package/gstack-origin/browse/src/read-commands.ts +335 -0
  143. package/gstack-origin/browse/src/server.ts +369 -0
  144. package/gstack-origin/browse/src/snapshot.ts +398 -0
  145. package/gstack-origin/browse/src/url-validation.ts +91 -0
  146. package/gstack-origin/browse/src/write-commands.ts +352 -0
  147. package/gstack-origin/browse/test/bun-polyfill.test.ts +72 -0
  148. package/gstack-origin/browse/test/commands.test.ts +1836 -0
  149. package/gstack-origin/browse/test/config.test.ts +250 -0
  150. package/gstack-origin/browse/test/cookie-import-browser.test.ts +397 -0
  151. package/gstack-origin/browse/test/cookie-picker-routes.test.ts +205 -0
  152. package/gstack-origin/browse/test/find-browse.test.ts +50 -0
  153. package/gstack-origin/browse/test/fixtures/basic.html +33 -0
  154. package/gstack-origin/browse/test/fixtures/cursor-interactive.html +22 -0
  155. package/gstack-origin/browse/test/fixtures/dialog.html +15 -0
  156. package/gstack-origin/browse/test/fixtures/empty.html +2 -0
  157. package/gstack-origin/browse/test/fixtures/forms.html +55 -0
  158. package/gstack-origin/browse/test/fixtures/qa-eval-checkout.html +108 -0
  159. package/gstack-origin/browse/test/fixtures/qa-eval-spa.html +98 -0
  160. package/gstack-origin/browse/test/fixtures/qa-eval.html +51 -0
  161. package/gstack-origin/browse/test/fixtures/responsive.html +49 -0
  162. package/gstack-origin/browse/test/fixtures/snapshot.html +55 -0
  163. package/gstack-origin/browse/test/fixtures/spa.html +24 -0
  164. package/gstack-origin/browse/test/fixtures/states.html +17 -0
  165. package/gstack-origin/browse/test/fixtures/upload.html +25 -0
  166. package/gstack-origin/browse/test/gstack-config.test.ts +125 -0
  167. package/gstack-origin/browse/test/gstack-update-check.test.ts +467 -0
  168. package/gstack-origin/browse/test/handoff.test.ts +235 -0
  169. package/gstack-origin/browse/test/path-validation.test.ts +63 -0
  170. package/gstack-origin/browse/test/platform.test.ts +37 -0
  171. package/gstack-origin/browse/test/snapshot.test.ts +467 -0
  172. package/gstack-origin/browse/test/test-server.ts +57 -0
  173. package/gstack-origin/browse/test/url-validation.test.ts +72 -0
  174. package/gstack-origin/canary/SKILL.md +493 -0
  175. package/gstack-origin/canary/SKILL.md.tmpl +220 -0
  176. package/gstack-origin/careful/SKILL.md +59 -0
  177. package/gstack-origin/careful/SKILL.md.tmpl +57 -0
  178. package/gstack-origin/careful/bin/check-careful.sh +112 -0
  179. package/gstack-origin/codex/SKILL.md +677 -0
  180. package/gstack-origin/codex/SKILL.md.tmpl +356 -0
  181. package/gstack-origin/conductor.json +6 -0
  182. package/gstack-origin/cso/SKILL.md +615 -0
  183. package/gstack-origin/cso/SKILL.md.tmpl +376 -0
  184. package/gstack-origin/design-consultation/SKILL.md +625 -0
  185. package/gstack-origin/design-consultation/SKILL.md.tmpl +369 -0
  186. package/gstack-origin/design-review/SKILL.md +998 -0
  187. package/gstack-origin/design-review/SKILL.md.tmpl +262 -0
  188. package/gstack-origin/docs/images/github-2013.png +0 -0
  189. package/gstack-origin/docs/images/github-2026.png +0 -0
  190. package/gstack-origin/docs/skills.md +877 -0
  191. package/gstack-origin/document-release/SKILL.md +613 -0
  192. package/gstack-origin/document-release/SKILL.md.tmpl +357 -0
  193. package/gstack-origin/freeze/SKILL.md +82 -0
  194. package/gstack-origin/freeze/SKILL.md.tmpl +80 -0
  195. package/gstack-origin/freeze/bin/check-freeze.sh +68 -0
  196. package/gstack-origin/gstack-upgrade/SKILL.md +226 -0
  197. package/gstack-origin/gstack-upgrade/SKILL.md.tmpl +224 -0
  198. package/gstack-origin/guard/SKILL.md +82 -0
  199. package/gstack-origin/guard/SKILL.md.tmpl +80 -0
  200. package/gstack-origin/investigate/SKILL.md +435 -0
  201. package/gstack-origin/investigate/SKILL.md.tmpl +196 -0
  202. package/gstack-origin/land-and-deploy/SKILL.md +880 -0
  203. package/gstack-origin/land-and-deploy/SKILL.md.tmpl +575 -0
  204. package/gstack-origin/office-hours/SKILL.md +996 -0
  205. package/gstack-origin/office-hours/SKILL.md.tmpl +624 -0
  206. package/gstack-origin/package.json +55 -0
  207. package/gstack-origin/plan-ceo-review/SKILL.md +1277 -0
  208. package/gstack-origin/plan-ceo-review/SKILL.md.tmpl +838 -0
  209. package/gstack-origin/plan-design-review/SKILL.md +676 -0
  210. package/gstack-origin/plan-design-review/SKILL.md.tmpl +314 -0
  211. package/gstack-origin/plan-eng-review/SKILL.md +836 -0
  212. package/gstack-origin/plan-eng-review/SKILL.md.tmpl +279 -0
  213. package/gstack-origin/qa/SKILL.md +1016 -0
  214. package/gstack-origin/qa/SKILL.md.tmpl +316 -0
  215. package/gstack-origin/qa/references/issue-taxonomy.md +85 -0
  216. package/gstack-origin/qa/templates/qa-report-template.md +126 -0
  217. package/gstack-origin/qa-only/SKILL.md +633 -0
  218. package/gstack-origin/qa-only/SKILL.md.tmpl +101 -0
  219. package/gstack-origin/retro/SKILL.md +1072 -0
  220. package/gstack-origin/retro/SKILL.md.tmpl +833 -0
  221. package/gstack-origin/review/SKILL.md +849 -0
  222. package/gstack-origin/review/SKILL.md.tmpl +259 -0
  223. package/gstack-origin/review/TODOS-format.md +62 -0
  224. package/gstack-origin/review/checklist.md +190 -0
  225. package/gstack-origin/review/design-checklist.md +132 -0
  226. package/gstack-origin/review/greptile-triage.md +220 -0
  227. package/gstack-origin/scripts/analytics.ts +190 -0
  228. package/gstack-origin/scripts/dev-skill.ts +82 -0
  229. package/gstack-origin/scripts/eval-compare.ts +96 -0
  230. package/gstack-origin/scripts/eval-list.ts +116 -0
  231. package/gstack-origin/scripts/eval-select.ts +86 -0
  232. package/gstack-origin/scripts/eval-summary.ts +187 -0
  233. package/gstack-origin/scripts/eval-watch.ts +172 -0
  234. package/gstack-origin/scripts/gen-skill-docs.ts +2414 -0
  235. package/gstack-origin/scripts/skill-check.ts +167 -0
  236. package/gstack-origin/setup +269 -0
  237. package/gstack-origin/setup-browser-cookies/SKILL.md +330 -0
  238. package/gstack-origin/setup-browser-cookies/SKILL.md.tmpl +74 -0
  239. package/gstack-origin/setup-deploy/SKILL.md +459 -0
  240. package/gstack-origin/setup-deploy/SKILL.md.tmpl +220 -0
  241. package/gstack-origin/ship/SKILL.md +1457 -0
  242. package/gstack-origin/ship/SKILL.md.tmpl +528 -0
  243. package/gstack-origin/supabase/config.sh +10 -0
  244. package/gstack-origin/supabase/functions/community-pulse/index.ts +59 -0
  245. package/gstack-origin/supabase/functions/telemetry-ingest/index.ts +135 -0
  246. package/gstack-origin/supabase/functions/update-check/index.ts +37 -0
  247. package/gstack-origin/supabase/migrations/001_telemetry.sql +89 -0
  248. package/gstack-origin/test/analytics.test.ts +277 -0
  249. package/gstack-origin/test/codex-e2e.test.ts +197 -0
  250. package/gstack-origin/test/fixtures/coverage-audit-fixture.ts +76 -0
  251. package/gstack-origin/test/fixtures/eval-baselines.json +7 -0
  252. package/gstack-origin/test/fixtures/qa-eval-checkout-ground-truth.json +43 -0
  253. package/gstack-origin/test/fixtures/qa-eval-ground-truth.json +43 -0
  254. package/gstack-origin/test/fixtures/qa-eval-spa-ground-truth.json +43 -0
  255. package/gstack-origin/test/fixtures/review-eval-design-slop.css +86 -0
  256. package/gstack-origin/test/fixtures/review-eval-design-slop.html +41 -0
  257. package/gstack-origin/test/fixtures/review-eval-enum-diff.rb +30 -0
  258. package/gstack-origin/test/fixtures/review-eval-enum.rb +27 -0
  259. package/gstack-origin/test/fixtures/review-eval-vuln.rb +14 -0
  260. package/gstack-origin/test/gemini-e2e.test.ts +173 -0
  261. package/gstack-origin/test/gen-skill-docs.test.ts +1049 -0
  262. package/gstack-origin/test/global-discover.test.ts +187 -0
  263. package/gstack-origin/test/helpers/codex-session-runner.ts +282 -0
  264. package/gstack-origin/test/helpers/e2e-helpers.ts +239 -0
  265. package/gstack-origin/test/helpers/eval-store.test.ts +548 -0
  266. package/gstack-origin/test/helpers/eval-store.ts +689 -0
  267. package/gstack-origin/test/helpers/gemini-session-runner.test.ts +104 -0
  268. package/gstack-origin/test/helpers/gemini-session-runner.ts +201 -0
  269. package/gstack-origin/test/helpers/llm-judge.ts +130 -0
  270. package/gstack-origin/test/helpers/observability.test.ts +283 -0
  271. package/gstack-origin/test/helpers/session-runner.test.ts +96 -0
  272. package/gstack-origin/test/helpers/session-runner.ts +357 -0
  273. package/gstack-origin/test/helpers/skill-parser.ts +206 -0
  274. package/gstack-origin/test/helpers/touchfiles.ts +260 -0
  275. package/gstack-origin/test/hook-scripts.test.ts +373 -0
  276. package/gstack-origin/test/skill-e2e-browse.test.ts +293 -0
  277. package/gstack-origin/test/skill-e2e-deploy.test.ts +279 -0
  278. package/gstack-origin/test/skill-e2e-design.test.ts +614 -0
  279. package/gstack-origin/test/skill-e2e-plan.test.ts +538 -0
  280. package/gstack-origin/test/skill-e2e-qa-bugs.test.ts +194 -0
  281. package/gstack-origin/test/skill-e2e-qa-workflow.test.ts +412 -0
  282. package/gstack-origin/test/skill-e2e-review.test.ts +535 -0
  283. package/gstack-origin/test/skill-e2e-workflow.test.ts +586 -0
  284. package/gstack-origin/test/skill-e2e.test.ts +3325 -0
  285. package/gstack-origin/test/skill-llm-eval.test.ts +787 -0
  286. package/gstack-origin/test/skill-parser.test.ts +179 -0
  287. package/gstack-origin/test/skill-routing-e2e.test.ts +605 -0
  288. package/gstack-origin/test/skill-validation.test.ts +1520 -0
  289. package/gstack-origin/test/telemetry.test.ts +278 -0
  290. package/gstack-origin/test/touchfiles.test.ts +262 -0
  291. package/gstack-origin/unfreeze/SKILL.md +40 -0
  292. package/gstack-origin/unfreeze/SKILL.md.tmpl +38 -0
  293. package/package.json +38 -0
  294. package/scripts/install-antigravity-skill.ps1 +33 -0
  295. package/scripts/install-antigravity-skill.sh +41 -0
  296. package/scripts/sync-gstack-origin.ps1 +37 -0
  297. package/scripts/sync-gstack-origin.sh +35 -0
@@ -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"