opengstack 0.13.10 → 0.14.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.
Files changed (189) hide show
  1. package/AGENTS.md +4 -4
  2. package/CLAUDE.md +127 -110
  3. package/README.md +10 -5
  4. package/SKILL.md +500 -70
  5. package/bin/opengstack.js +69 -69
  6. package/{skills/land-and-deploy/SKILL.md → commands/autoplan.md} +7 -25
  7. package/{skills/benchmark/SKILL.md → commands/benchmark.md} +84 -108
  8. package/{skills/browse/SKILL.md → commands/browse.md} +60 -81
  9. package/{skills/ship/SKILL.md → commands/canary.md} +7 -27
  10. package/{skills/careful/SKILL.md → commands/careful.md} +2 -22
  11. package/{skills/canary/SKILL.md → commands/codex.md} +7 -26
  12. package/{skills/connect-chrome/SKILL.md → commands/connect-chrome.md} +7 -24
  13. package/commands/cso.md +70 -0
  14. package/commands/design-consultation.md +70 -0
  15. package/commands/design-review.md +70 -0
  16. package/commands/design-shotgun.md +70 -0
  17. package/commands/document-release.md +70 -0
  18. package/{skills/freeze/SKILL.md → commands/freeze.md} +3 -29
  19. package/{skills/guard/SKILL.md → commands/guard.md} +4 -35
  20. package/commands/investigate.md +70 -0
  21. package/commands/land-and-deploy.md +70 -0
  22. package/commands/office-hours.md +70 -0
  23. package/{skills/gstack-upgrade/SKILL.md → commands/opengstack-upgrade.md} +64 -79
  24. package/commands/plan-ceo-review.md +70 -0
  25. package/commands/plan-design-review.md +70 -0
  26. package/commands/plan-eng-review.md +70 -0
  27. package/commands/qa-only.md +70 -0
  28. package/commands/qa.md +70 -0
  29. package/commands/retro.md +70 -0
  30. package/commands/review.md +70 -0
  31. package/{skills/setup-browser-cookies/SKILL.md → commands/setup-browser-cookies.md} +22 -40
  32. package/commands/setup-deploy.md +70 -0
  33. package/commands/ship.md +70 -0
  34. package/commands/unfreeze.md +25 -0
  35. package/docs/designs/CHROME_VS_CHROMIUM_EXPLORATION.md +9 -9
  36. package/docs/designs/CONDUCTOR_CHROME_SIDEBAR_INTEGRATION.md +2 -2
  37. package/docs/designs/CONDUCTOR_SESSION_API.md +16 -16
  38. package/docs/designs/DESIGN_SHOTGUN.md +74 -74
  39. package/docs/designs/DESIGN_TOOLS_V1.md +111 -111
  40. package/docs/skills.md +483 -202
  41. package/package.json +42 -43
  42. package/scripts/analytics.ts +188 -0
  43. package/scripts/dev-skill.ts +83 -0
  44. package/scripts/discover-skills.ts +39 -0
  45. package/scripts/eval-compare.ts +97 -0
  46. package/scripts/eval-list.ts +117 -0
  47. package/scripts/eval-select.ts +86 -0
  48. package/scripts/eval-summary.ts +188 -0
  49. package/scripts/eval-watch.ts +172 -0
  50. package/scripts/gen-skill-docs.ts +473 -0
  51. package/scripts/resolvers/browse.ts +129 -0
  52. package/scripts/resolvers/codex-helpers.ts +133 -0
  53. package/scripts/resolvers/composition.ts +48 -0
  54. package/scripts/resolvers/confidence.ts +37 -0
  55. package/scripts/resolvers/constants.ts +50 -0
  56. package/scripts/resolvers/design.ts +950 -0
  57. package/scripts/resolvers/index.ts +59 -0
  58. package/scripts/resolvers/learnings.ts +96 -0
  59. package/scripts/resolvers/preamble.ts +505 -0
  60. package/scripts/resolvers/review.ts +884 -0
  61. package/scripts/resolvers/testing.ts +573 -0
  62. package/scripts/resolvers/types.ts +45 -0
  63. package/scripts/resolvers/utility.ts +421 -0
  64. package/scripts/skill-check.ts +190 -0
  65. package/scripts/cleanup.py +0 -100
  66. package/scripts/filter-skills.sh +0 -114
  67. package/scripts/filter_skills.py +0 -164
  68. package/scripts/install-skills.js +0 -60
  69. package/skills/autoplan/SKILL.md +0 -96
  70. package/skills/autoplan/SKILL.md.tmpl +0 -694
  71. package/skills/benchmark/SKILL.md.tmpl +0 -222
  72. package/skills/browse/SKILL.md.tmpl +0 -131
  73. package/skills/browse/bin/find-browse +0 -21
  74. package/skills/browse/bin/remote-slug +0 -14
  75. package/skills/browse/scripts/build-node-server.sh +0 -48
  76. package/skills/browse/src/activity.ts +0 -208
  77. package/skills/browse/src/browser-manager.ts +0 -959
  78. package/skills/browse/src/buffers.ts +0 -137
  79. package/skills/browse/src/bun-polyfill.cjs +0 -109
  80. package/skills/browse/src/cli.ts +0 -678
  81. package/skills/browse/src/commands.ts +0 -128
  82. package/skills/browse/src/config.ts +0 -150
  83. package/skills/browse/src/cookie-import-browser.ts +0 -625
  84. package/skills/browse/src/cookie-picker-routes.ts +0 -230
  85. package/skills/browse/src/cookie-picker-ui.ts +0 -688
  86. package/skills/browse/src/find-browse.ts +0 -61
  87. package/skills/browse/src/meta-commands.ts +0 -550
  88. package/skills/browse/src/platform.ts +0 -17
  89. package/skills/browse/src/read-commands.ts +0 -358
  90. package/skills/browse/src/server.ts +0 -1192
  91. package/skills/browse/src/sidebar-agent.ts +0 -280
  92. package/skills/browse/src/sidebar-utils.ts +0 -21
  93. package/skills/browse/src/snapshot.ts +0 -407
  94. package/skills/browse/src/url-validation.ts +0 -95
  95. package/skills/browse/src/write-commands.ts +0 -364
  96. package/skills/browse/test/activity.test.ts +0 -120
  97. package/skills/browse/test/adversarial-security.test.ts +0 -32
  98. package/skills/browse/test/browser-manager-unit.test.ts +0 -17
  99. package/skills/browse/test/bun-polyfill.test.ts +0 -72
  100. package/skills/browse/test/commands.test.ts +0 -2075
  101. package/skills/browse/test/compare-board.test.ts +0 -342
  102. package/skills/browse/test/config.test.ts +0 -316
  103. package/skills/browse/test/cookie-import-browser.test.ts +0 -519
  104. package/skills/browse/test/cookie-picker-routes.test.ts +0 -260
  105. package/skills/browse/test/file-drop.test.ts +0 -271
  106. package/skills/browse/test/find-browse.test.ts +0 -50
  107. package/skills/browse/test/findport.test.ts +0 -191
  108. package/skills/browse/test/fixtures/basic.html +0 -33
  109. package/skills/browse/test/fixtures/cursor-interactive.html +0 -22
  110. package/skills/browse/test/fixtures/dialog.html +0 -15
  111. package/skills/browse/test/fixtures/empty.html +0 -2
  112. package/skills/browse/test/fixtures/forms.html +0 -55
  113. package/skills/browse/test/fixtures/iframe.html +0 -30
  114. package/skills/browse/test/fixtures/network-idle.html +0 -30
  115. package/skills/browse/test/fixtures/qa-eval-checkout.html +0 -108
  116. package/skills/browse/test/fixtures/qa-eval-spa.html +0 -98
  117. package/skills/browse/test/fixtures/qa-eval.html +0 -51
  118. package/skills/browse/test/fixtures/responsive.html +0 -49
  119. package/skills/browse/test/fixtures/snapshot.html +0 -55
  120. package/skills/browse/test/fixtures/spa.html +0 -24
  121. package/skills/browse/test/fixtures/states.html +0 -17
  122. package/skills/browse/test/fixtures/upload.html +0 -25
  123. package/skills/browse/test/gstack-config.test.ts +0 -138
  124. package/skills/browse/test/gstack-update-check.test.ts +0 -514
  125. package/skills/browse/test/handoff.test.ts +0 -235
  126. package/skills/browse/test/path-validation.test.ts +0 -91
  127. package/skills/browse/test/platform.test.ts +0 -37
  128. package/skills/browse/test/server-auth.test.ts +0 -65
  129. package/skills/browse/test/sidebar-agent-roundtrip.test.ts +0 -226
  130. package/skills/browse/test/sidebar-agent.test.ts +0 -199
  131. package/skills/browse/test/sidebar-integration.test.ts +0 -320
  132. package/skills/browse/test/sidebar-unit.test.ts +0 -96
  133. package/skills/browse/test/snapshot.test.ts +0 -467
  134. package/skills/browse/test/state-ttl.test.ts +0 -35
  135. package/skills/browse/test/test-server.ts +0 -57
  136. package/skills/browse/test/url-validation.test.ts +0 -72
  137. package/skills/browse/test/watch.test.ts +0 -129
  138. package/skills/canary/SKILL.md.tmpl +0 -212
  139. package/skills/careful/SKILL.md.tmpl +0 -56
  140. package/skills/careful/bin/check-careful.sh +0 -112
  141. package/skills/codex/SKILL.md +0 -90
  142. package/skills/codex/SKILL.md.tmpl +0 -417
  143. package/skills/connect-chrome/SKILL.md.tmpl +0 -195
  144. package/skills/cso/ACKNOWLEDGEMENTS.md +0 -14
  145. package/skills/cso/SKILL.md +0 -93
  146. package/skills/cso/SKILL.md.tmpl +0 -606
  147. package/skills/design-consultation/SKILL.md +0 -94
  148. package/skills/design-consultation/SKILL.md.tmpl +0 -415
  149. package/skills/design-review/SKILL.md +0 -94
  150. package/skills/design-review/SKILL.md.tmpl +0 -290
  151. package/skills/design-shotgun/SKILL.md +0 -91
  152. package/skills/design-shotgun/SKILL.md.tmpl +0 -285
  153. package/skills/document-release/SKILL.md +0 -91
  154. package/skills/document-release/SKILL.md.tmpl +0 -359
  155. package/skills/freeze/SKILL.md.tmpl +0 -77
  156. package/skills/freeze/bin/check-freeze.sh +0 -79
  157. package/skills/gstack-upgrade/SKILL.md.tmpl +0 -222
  158. package/skills/guard/SKILL.md.tmpl +0 -77
  159. package/skills/investigate/SKILL.md +0 -105
  160. package/skills/investigate/SKILL.md.tmpl +0 -194
  161. package/skills/land-and-deploy/SKILL.md.tmpl +0 -881
  162. package/skills/office-hours/SKILL.md +0 -96
  163. package/skills/office-hours/SKILL.md.tmpl +0 -645
  164. package/skills/plan-ceo-review/SKILL.md +0 -94
  165. package/skills/plan-ceo-review/SKILL.md.tmpl +0 -811
  166. package/skills/plan-design-review/SKILL.md +0 -92
  167. package/skills/plan-design-review/SKILL.md.tmpl +0 -446
  168. package/skills/plan-eng-review/SKILL.md +0 -93
  169. package/skills/plan-eng-review/SKILL.md.tmpl +0 -303
  170. package/skills/qa/SKILL.md +0 -95
  171. package/skills/qa/SKILL.md.tmpl +0 -316
  172. package/skills/qa/references/issue-taxonomy.md +0 -85
  173. package/skills/qa/templates/qa-report-template.md +0 -126
  174. package/skills/qa-only/SKILL.md +0 -89
  175. package/skills/qa-only/SKILL.md.tmpl +0 -101
  176. package/skills/retro/SKILL.md +0 -89
  177. package/skills/retro/SKILL.md.tmpl +0 -820
  178. package/skills/review/SKILL.md +0 -92
  179. package/skills/review/SKILL.md.tmpl +0 -281
  180. package/skills/review/TODOS-format.md +0 -62
  181. package/skills/review/checklist.md +0 -220
  182. package/skills/review/design-checklist.md +0 -132
  183. package/skills/review/greptile-triage.md +0 -220
  184. package/skills/setup-browser-cookies/SKILL.md.tmpl +0 -81
  185. package/skills/setup-deploy/SKILL.md +0 -92
  186. package/skills/setup-deploy/SKILL.md.tmpl +0 -215
  187. package/skills/ship/SKILL.md.tmpl +0 -636
  188. package/skills/unfreeze/SKILL.md +0 -37
  189. package/skills/unfreeze/SKILL.md.tmpl +0 -36
@@ -1,222 +0,0 @@
1
- ---
2
- name: benchmark
3
- preamble-tier: 1
4
- version: 1.0.0
5
- description: |
6
- Performance regression detection using the browse daemon. Establishes
7
- baselines for page load times, Core Web Vitals, and resource sizes.
8
- Compares before/after on every PR. Tracks performance trends over time.
9
- Use when: "performance", "benchmark", "page speed", "lighthouse", "web vitals",
10
- "bundle size", "load time".
11
- allowed-tools:
12
- - Bash
13
- - Read
14
- - Write
15
- - Glob
16
- - AskUserQuestion
17
- ---
18
-
19
- {{PREAMBLE}}
20
-
21
- {{BROWSE_SETUP}}
22
-
23
- # /benchmark — Performance Regression Detection
24
-
25
- 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.
26
-
27
- 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.
28
-
29
- ## User-invocable
30
- When the user types `/benchmark`, run this skill.
31
-
32
- ## Arguments
33
- - `/benchmark <url>` — full performance audit with baseline comparison
34
- - `/benchmark <url> --baseline` — capture baseline (run before making changes)
35
- - `/benchmark <url> --quick` — single-pass timing check (no baseline needed)
36
- - `/benchmark <url> --pages /,/dashboard,/api/health` — specify pages
37
- - `/benchmark --diff` — benchmark only pages affected by current branch
38
- - `/benchmark --trend` — show performance trends from historical data
39
-
40
- ## Instructions
41
-
42
- ### Phase 1: Setup
43
-
44
- ```bash
45
- eval "$(~/.claude/skills/opengstack/bin/gstack-slug 2>/dev/null || echo "SLUG=unknown")"
46
- mkdir -p .gstack/benchmark-reports
47
- mkdir -p .gstack/benchmark-reports/baselines
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
- ### Phase 3: Performance Data Collection
58
-
59
- For each page, collect comprehensive performance metrics:
60
-
61
- ```bash
62
- $B goto <page-url>
63
- $B perf
64
-
65
- Then gather detailed metrics via JavaScript:
66
-
67
- ```bash
68
- $B eval "JSON.stringify(performance.getEntriesByType('navigation')[0])"
69
-
70
- Extract key metrics:
71
- - **TTFB** (Time to First Byte): `responseStart - requestStart`
72
- - **FCP** (First Contentful Paint): from PerformanceObserver or `paint` entries
73
- - **LCP** (Largest Contentful Paint): from PerformanceObserver
74
- - **DOM Interactive**: `domInteractive - navigationStart`
75
- - **DOM Complete**: `domComplete - navigationStart`
76
- - **Full Load**: `loadEventEnd - navigationStart`
77
-
78
- Resource analysis:
79
- ```bash
80
- $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))"
81
-
82
- Bundle size check:
83
- ```bash
84
- $B eval "JSON.stringify(performance.getEntriesByType('resource').filter(r => r.initiatorType === 'script').map(r => ({name: r.name.split('/').pop().split('?')[0], size: r.transferSize})))"
85
- $B eval "JSON.stringify(performance.getEntriesByType('resource').filter(r => r.initiatorType === 'css').map(r => ({name: r.name.split('/').pop().split('?')[0], size: r.transferSize})))"
86
-
87
- Network summary:
88
- ```bash
89
- $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])})})()"
90
-
91
- ### Phase 4: Baseline Capture (--baseline mode)
92
-
93
- Save metrics to baseline file:
94
-
95
- ```json
96
- {
97
- "url": "<url>",
98
- "timestamp": "<ISO>",
99
- "branch": "<branch>",
100
- "pages": {
101
- "/": {
102
- "ttfb_ms": 120,
103
- "fcp_ms": 450,
104
- "lcp_ms": 800,
105
- "dom_interactive_ms": 600,
106
- "dom_complete_ms": 1200,
107
- "full_load_ms": 1400,
108
- "total_requests": 42,
109
- "total_transfer_bytes": 1250000,
110
- "js_bundle_bytes": 450000,
111
- "css_bundle_bytes": 85000,
112
- "largest_resources": [
113
- {"name": "main.js", "size": 320000, "duration": 180},
114
- {"name": "vendor.js", "size": 130000, "duration": 90}
115
- ]
116
- }
117
- }
118
- }
119
-
120
- Write to `.gstack/benchmark-reports/baselines/baseline.json`.
121
-
122
- ### Phase 5: Comparison
123
-
124
- If baseline exists, compare current metrics against it:
125
-
126
-
127
- PERFORMANCE REPORT — [url]
128
- ══════════════════════════
129
- Branch: [current-branch] vs baseline ([baseline-branch])
130
-
131
- Page: /
132
- ─────────────────────────────────────────────────────
133
- Metric Baseline Current Delta Status
134
- ──────── ──────── ─────── ───── ──────
135
- TTFB 120ms 135ms +15ms OK
136
- FCP 450ms 480ms +30ms OK
137
- LCP 800ms 1600ms +800ms REGRESSION
138
- DOM Interactive 600ms 650ms +50ms OK
139
- DOM Complete 1200ms 1350ms +150ms WARNING
140
- Full Load 1400ms 2100ms +700ms REGRESSION
141
- Total Requests 42 58 +16 WARNING
142
- Transfer Size 1.2MB 1.8MB +0.6MB REGRESSION
143
- JS Bundle 450KB 720KB +270KB REGRESSION
144
- CSS Bundle 85KB 88KB +3KB OK
145
-
146
- REGRESSIONS DETECTED: 3
147
- [1] LCP doubled (800ms → 1600ms) — likely a large new image or blocking resource
148
- [2] Total transfer +50% (1.2MB → 1.8MB) — check new JS bundles
149
- [3] JS bundle +60% (450KB → 720KB) — new dependency or missing tree-shaking
150
-
151
- **Regression thresholds:**
152
- - Timing metrics: >50% increase OR >500ms absolute increase = REGRESSION
153
- - Timing metrics: >20% increase = WARNING
154
- - Bundle size: >25% increase = REGRESSION
155
- - Bundle size: >10% increase = WARNING
156
- - Request count: >30% increase = WARNING
157
-
158
- ### Phase 6: Slowest Resources
159
-
160
-
161
- TOP 10 SLOWEST RESOURCES
162
- ═════════════════════════
163
- # Resource Type Size Duration
164
- 1 vendor.chunk.js script 320KB 480ms
165
- 2 main.js script 250KB 320ms
166
- 3 hero-image.webp img 180KB 280ms
167
- 4 analytics.js script 45KB 250ms ← third-party
168
- 5 fonts/inter-var.woff2 font 95KB 180ms
169
- ...
170
-
171
- RECOMMENDATIONS:
172
- - vendor.chunk.js: Consider code-splitting — 320KB is large for initial load
173
- - analytics.js: Load async/defer — blocks rendering for 250ms
174
- - hero-image.webp: Add width/height to prevent CLS, consider lazy loading
175
-
176
- ### Phase 7: Performance Budget
177
-
178
- Check against industry budgets:
179
-
180
-
181
- PERFORMANCE BUDGET CHECK
182
- ════════════════════════
183
- Metric Budget Actual Status
184
- ──────── ────── ────── ──────
185
- FCP < 1.8s 0.48s PASS
186
- LCP < 2.5s 1.6s PASS
187
- Total JS < 500KB 720KB FAIL
188
- Total CSS < 100KB 88KB PASS
189
- Total Transfer < 2MB 1.8MB WARNING (90%)
190
- HTTP Requests < 50 58 FAIL
191
-
192
- Grade: B (4/6 passing)
193
-
194
- ### Phase 8: Trend Analysis (--trend mode)
195
-
196
- Load historical baseline files and show trends:
197
-
198
-
199
- PERFORMANCE TRENDS (last 5 benchmarks)
200
- ══════════════════════════════════════
201
- Date FCP LCP Bundle Requests Grade
202
- 2026-03-10 420ms 750ms 380KB 38 A
203
- 2026-03-12 440ms 780ms 410KB 40 A
204
- 2026-03-14 450ms 800ms 450KB 42 A
205
- 2026-03-16 460ms 850ms 520KB 48 B
206
- 2026-03-18 480ms 1600ms 720KB 58 B
207
-
208
- TREND: Performance degrading. LCP doubled in 8 days.
209
- JS bundle growing 50KB/week. Investigate.
210
-
211
- ### Phase 9: Save Report
212
-
213
- Write to `.gstack/benchmark-reports/{date}-benchmark.md` and `.gstack/benchmark-reports/{date}-benchmark.json`.
214
-
215
- ## Important Rules
216
-
217
- - **Measure, don't guess.** Use actual performance.getEntries() data, not estimates.
218
- - **Baseline is essential.** Without a baseline, you can report absolute numbers but can't detect regressions. Always encourage baseline capture.
219
- - **Relative thresholds, not absolute.** 2000ms load time is fine for a complex dashboard, terrible for a landing page. Compare against YOUR baseline.
220
- - **Third-party scripts are context.** Flag them, but the user can't fix Google Analytics being slow. Focus recommendations on first-party resources.
221
- - **Bundle size is the leading indicator.** Load time varies with network. Bundle size is deterministic. Track it religiously.
222
- - **Read-only.** Produce the report. Don't modify code unless explicitly asked.
@@ -1,131 +0,0 @@
1
- ---
2
- name: browse
3
- preamble-tier: 1
4
- version: 1.1.0
5
- description: |
6
- Fast headless browser for QA testing and site dogfooding. Navigate any URL, interact with
7
- elements, verify page state, diff before/after actions, take annotated screenshots, check
8
- responsive layouts, test forms and uploads, handle dialogs, and assert element states.
9
- ~100ms per command. Use when you need to test a feature, verify a deployment, dogfood a
10
- user flow, or file a bug with evidence. Use when asked to "open in browser", "test the
11
- site", "take a screenshot", or "dogfood this".
12
- allowed-tools:
13
- - Bash
14
- - Read
15
- - AskUserQuestion
16
-
17
- ---
18
-
19
- {{PREAMBLE}}
20
-
21
- # browse: QA Testing & Dogfooding
22
-
23
- Persistent headless Chromium. First call auto-starts (~3s), then ~100ms per command.
24
- State persists between calls (cookies, tabs, login sessions).
25
-
26
- {{BROWSE_SETUP}}
27
-
28
- ## Core QA Patterns
29
-
30
- ### 1. Verify a page loads correctly
31
- ```bash
32
- $B goto https://yourapp.com
33
- $B text # content loads?
34
- $B console # JS errors?
35
- $B network # failed requests?
36
- $B is visible ".main-content" # key elements present?
37
-
38
- ### 2. Test a user flow
39
- ```bash
40
- $B goto https://app.com/login
41
- $B snapshot -i # see all interactive elements
42
- $B fill @e3 "user@test.com"
43
- $B fill @e4 "password"
44
- $B click @e5 # submit
45
- $B snapshot -D # diff: what changed after submit?
46
- $B is visible ".dashboard" # success state present?
47
-
48
- ### 3. Verify an action worked
49
- ```bash
50
- $B snapshot # baseline
51
- $B click @e3 # do something
52
- $B snapshot -D # unified diff shows exactly what changed
53
-
54
- ### 4. Visual evidence for bug reports
55
- ```bash
56
- $B snapshot -i -a -o /tmp/annotated.png # labeled screenshot
57
- $B screenshot /tmp/bug.png # plain screenshot
58
- $B console # error log
59
-
60
- ### 5. Find all clickable elements (including non-ARIA)
61
- ```bash
62
- $B snapshot -C # finds divs with cursor:pointer, onclick, tabindex
63
- $B click @c1 # interact with them
64
-
65
- ### 6. Assert element states
66
- ```bash
67
- $B is visible ".modal"
68
- $B is enabled "#submit-btn"
69
- $B is disabled "#submit-btn"
70
- $B is checked "#agree-checkbox"
71
- $B is editable "#name-field"
72
- $B is focused "#search-input"
73
- $B js "document.body.textContent.includes('Success')"
74
-
75
- ### 7. Test responsive layouts
76
- ```bash
77
- $B responsive /tmp/layout # mobile + tablet + desktop screenshots
78
- $B viewport 375x812 # or set specific viewport
79
- $B screenshot /tmp/mobile.png
80
-
81
- ### 8. Test file uploads
82
- ```bash
83
- $B upload "#file-input" /path/to/file.pdf
84
- $B is visible ".upload-success"
85
-
86
- ### 9. Test dialogs
87
- ```bash
88
- $B dialog-accept "yes" # set up handler
89
- $B click "#delete-button" # trigger dialog
90
- $B dialog # see what appeared
91
- $B snapshot -D # verify deletion happened
92
-
93
- ### 10. Compare environments
94
- ```bash
95
- $B diff https://staging.app.com https://prod.app.com
96
-
97
- ### 11. Show screenshots to the user
98
- After `$B screenshot`, `$B snapshot -a -o`, or `$B responsive`, always use the Read tool on the output PNG(s) so the user can see them. Without this, screenshots are invisible.
99
-
100
- ## User Handoff
101
-
102
- When you hit something you can't handle in headless mode (CAPTCHA, complex auth, multi-factor
103
- login), hand off to the user:
104
-
105
- ```bash
106
- # 1. Open a visible Chrome at the current page
107
- $B handoff "Stuck on CAPTCHA at login page"
108
-
109
- # 2. Tell the user what happened (via AskUserQuestion)
110
- # "I've opened Chrome at the login page. Please solve the CAPTCHA
111
- # and let me know when you're done."
112
-
113
- # 3. When user says "done", re-snapshot and continue
114
- $B resume
115
-
116
- **When to use handoff:**
117
- - CAPTCHAs or bot detection
118
- - Multi-factor authentication (SMS, authenticator app)
119
- - OAuth flows that require user interaction
120
- - Complex interactions the AI can't handle after 3 attempts
121
-
122
- The browser preserves all state (cookies, localStorage, tabs) across the handoff.
123
- After `resume`, you get a fresh snapshot of wherever the user left off.
124
-
125
- ## Snapshot Flags
126
-
127
- {{SNAPSHOT_FLAGS}}
128
-
129
- ## Full Command List
130
-
131
- {{COMMAND_REFERENCE}}
@@ -1,21 +0,0 @@
1
- #!/bin/bash
2
- # Shim: delegates to compiled find-browse binary, falls back to basic discovery.
3
- # The compiled binary handles git root detection for workspace-local installs.
4
- DIR="$(cd "$(dirname "$0")/.." && pwd)/dist"
5
- if test -x "$DIR/find-browse"; then
6
- exec "$DIR/find-browse" "$@"
7
- fi
8
- # Fallback: basic discovery with priority chain
9
- ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
10
- for MARKER in .codex .agents .claude; do
11
- if [ -n "$ROOT" ] && test -x "$ROOT/$MARKER/skills/gstack/browse/dist/browse"; then
12
- echo "$ROOT/$MARKER/skills/gstack/browse/dist/browse"
13
- exit 0
14
- fi
15
- if test -x "$HOME/$MARKER/skills/gstack/browse/dist/browse"; then
16
- echo "$HOME/$MARKER/skills/gstack/browse/dist/browse"
17
- exit 0
18
- fi
19
- done
20
- echo "ERROR: browse binary not found. Run: cd <skill-dir> && ./setup" >&2
21
- exit 1
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Output the remote slug (owner-repo) for the current git repo.
3
- # Used by SKILL.md files to derive project-specific paths in ~/.gstack/projects/.
4
- set -e
5
- URL=$(git remote get-url origin 2>/dev/null || true)
6
- if [ -n "$URL" ]; then
7
- # Strip trailing .git if present, then extract owner/repo
8
- URL="${URL%.git}"
9
- # Handle both SSH (git@host:owner/repo) and HTTPS (https://host/owner/repo)
10
- OWNER_REPO=$(echo "$URL" | sed -E 's#.*[:/]([^/]+)/([^/]+)$#\1-\2#')
11
- echo "$OWNER_REPO"
12
- else
13
- basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
14
- fi
@@ -1,48 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Build a Node.js-compatible server bundle for Windows.
3
- #
4
- # On Windows, Bun can't launch or connect to Playwright's Chromium
5
- # (oven-sh/bun#4253, #9911). This script produces a server bundle
6
- # that runs under Node.js with Bun API polyfills.
7
-
8
- set -e
9
-
10
- GSTACK_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
11
- SRC_DIR="$GSTACK_DIR/browse/src"
12
- DIST_DIR="$GSTACK_DIR/browse/dist"
13
-
14
- echo "Building Node-compatible server bundle..."
15
-
16
- # Step 1: Transpile server.ts to a single .mjs bundle (externalize runtime deps)
17
- bun build "$SRC_DIR/server.ts" \
18
- --target=node \
19
- --outfile "$DIST_DIR/server-node.mjs" \
20
- --external playwright \
21
- --external playwright-core \
22
- --external diff \
23
- --external "bun:sqlite"
24
-
25
- # Step 2: Post-process
26
- # Replace import.meta.dir with a resolvable reference
27
- perl -pi -e 's/import\.meta\.dir/__browseNodeSrcDir/g' "$DIST_DIR/server-node.mjs"
28
- # Stub out bun:sqlite (macOS-only cookie import, not needed on Windows)
29
- perl -pi -e 's|import { Database } from "bun:sqlite";|const Database = null; // bun:sqlite stubbed on Node|g' "$DIST_DIR/server-node.mjs"
30
-
31
- # Step 3: Create the final file with polyfill header injected after the first line
32
- {
33
- head -1 "$DIST_DIR/server-node.mjs"
34
- echo '// ── Windows Node.js compatibility (auto-generated) ──'
35
- echo 'import { fileURLToPath as _ftp } from "node:url";'
36
- echo 'import { dirname as _dn } from "node:path";'
37
- echo 'const __browseNodeSrcDir = _dn(_dn(_ftp(import.meta.url))) + "/src";'
38
- echo '{ const _r = createRequire(import.meta.url); _r("./bun-polyfill.cjs"); }'
39
- echo '// ── end compatibility ──'
40
- tail -n +2 "$DIST_DIR/server-node.mjs"
41
- } > "$DIST_DIR/server-node.tmp.mjs"
42
-
43
- mv "$DIST_DIR/server-node.tmp.mjs" "$DIST_DIR/server-node.mjs"
44
-
45
- # Step 4: Copy polyfill to dist/
46
- cp "$SRC_DIR/bun-polyfill.cjs" "$DIST_DIR/bun-polyfill.cjs"
47
-
48
- echo "Node server bundle ready: $DIST_DIR/server-node.mjs"
@@ -1,208 +0,0 @@
1
- /**
2
- * Activity streaming — real-time feed of browse commands for the Chrome extension Side Panel
3
- *
4
- * Architecture:
5
- * handleCommand() ──► emitActivity(command_start)
6
- * ──► emitActivity(command_end)
7
- * wirePageEvents() ──► emitActivity(navigation)
8
- *
9
- * GET /activity/stream?after=ID ──► SSE via ReadableStream
10
- * GET /activity/history?limit=N ──► REST fallback
11
- *
12
- * Privacy: filterArgs() redacts passwords, auth tokens, and sensitive query params.
13
- * Backpressure: subscribers notified via queueMicrotask (never blocks command path).
14
- * Gap detection: client sends ?after=ID, server detects if ring buffer overflowed.
15
- */
16
-
17
- import { CircularBuffer } from './buffers';
18
-
19
- // ─── Types ──────────────────────────────────────────────────────
20
-
21
- export interface ActivityEntry {
22
- id: number;
23
- timestamp: number;
24
- type: 'command_start' | 'command_end' | 'navigation' | 'error';
25
- command?: string;
26
- args?: string[];
27
- url?: string;
28
- duration?: number;
29
- status?: 'ok' | 'error';
30
- error?: string;
31
- result?: string;
32
- tabs?: number;
33
- mode?: string;
34
- }
35
-
36
- // ─── Buffer & Subscribers ───────────────────────────────────────
37
-
38
- const BUFFER_CAPACITY = 1000;
39
- const activityBuffer = new CircularBuffer<ActivityEntry>(BUFFER_CAPACITY);
40
- let nextId = 1;
41
-
42
- type ActivitySubscriber = (entry: ActivityEntry) => void;
43
- const subscribers = new Set<ActivitySubscriber>();
44
-
45
- // ─── Privacy Filtering ─────────────────────────────────────────
46
-
47
- const SENSITIVE_COMMANDS = new Set(['fill', 'type', 'cookie', 'header']);
48
- const SENSITIVE_PARAM_PATTERN = /\b(password|token|secret|key|auth|bearer|api[_-]?key)\b/i;
49
-
50
- /**
51
- * Redact sensitive data from command args before streaming.
52
- */
53
- export function filterArgs(command: string, args: string[]): string[] {
54
- if (!args || args.length === 0) return args;
55
-
56
- // fill: redact the value (last arg) for password-type fields
57
- if (command === 'fill' && args.length >= 2) {
58
- const selector = args[0];
59
- // If the selector suggests a password field, redact the value
60
- if (/password|passwd|secret|token/i.test(selector)) {
61
- return [selector, '[REDACTED]'];
62
- }
63
- return args;
64
- }
65
-
66
- // header: redact Authorization and other sensitive headers
67
- if (command === 'header' && args.length >= 1) {
68
- const headerLine = args[0];
69
- if (/^(authorization|x-api-key|cookie|set-cookie)/i.test(headerLine)) {
70
- const colonIdx = headerLine.indexOf(':');
71
- if (colonIdx > 0) {
72
- return [headerLine.substring(0, colonIdx + 1) + '[REDACTED]'];
73
- }
74
- }
75
- return args;
76
- }
77
-
78
- // cookie: redact cookie values
79
- if (command === 'cookie' && args.length >= 1) {
80
- const cookieStr = args[0];
81
- const eqIdx = cookieStr.indexOf('=');
82
- if (eqIdx > 0) {
83
- return [cookieStr.substring(0, eqIdx + 1) + '[REDACTED]'];
84
- }
85
- return args;
86
- }
87
-
88
- // type: always redact (could be a password field)
89
- if (command === 'type') {
90
- return ['[REDACTED]'];
91
- }
92
-
93
- // URL args: redact sensitive query params
94
- return args.map(arg => {
95
- if (arg.startsWith('http://') || arg.startsWith('https://')) {
96
- try {
97
- const url = new URL(arg);
98
- let redacted = false;
99
- for (const key of url.searchParams.keys()) {
100
- if (SENSITIVE_PARAM_PATTERN.test(key)) {
101
- url.searchParams.set(key, '[REDACTED]');
102
- redacted = true;
103
- }
104
- }
105
- return redacted ? url.toString() : arg;
106
- } catch {
107
- return arg;
108
- }
109
- }
110
- return arg;
111
- });
112
- }
113
-
114
- /**
115
- * Truncate result text for streaming (max 200 chars).
116
- */
117
- function truncateResult(result: string | undefined): string | undefined {
118
- if (!result) return undefined;
119
- if (result.length <= 200) return result;
120
- return result.substring(0, 200) + '...';
121
- }
122
-
123
- // ─── Public API ─────────────────────────────────────────────────
124
-
125
- /**
126
- * Emit an activity event. Backpressure-safe: subscribers notified asynchronously.
127
- */
128
- export function emitActivity(entry: Omit<ActivityEntry, 'id' | 'timestamp'>): ActivityEntry {
129
- const full: ActivityEntry = {
130
- ...entry,
131
- id: nextId++,
132
- timestamp: Date.now(),
133
- args: entry.args ? filterArgs(entry.command || '', entry.args) : undefined,
134
- result: truncateResult(entry.result),
135
- };
136
- activityBuffer.push(full);
137
-
138
- // Notify subscribers asynchronously — never block the command path
139
- for (const notify of subscribers) {
140
- queueMicrotask(() => {
141
- try { notify(full); } catch { /* subscriber error — don't crash */ }
142
- });
143
- }
144
-
145
- return full;
146
- }
147
-
148
- /**
149
- * Subscribe to live activity events. Returns unsubscribe function.
150
- */
151
- export function subscribe(fn: ActivitySubscriber): () => void {
152
- subscribers.add(fn);
153
- return () => subscribers.delete(fn);
154
- }
155
-
156
- /**
157
- * Get recent activity entries after the given cursor ID.
158
- * Returns entries and gap info if the buffer has overflowed.
159
- */
160
- export function getActivityAfter(afterId: number): {
161
- entries: ActivityEntry[];
162
- gap: boolean;
163
- gapFrom?: number;
164
- availableFrom?: number;
165
- totalAdded: number;
166
- } {
167
- const total = activityBuffer.totalAdded;
168
- const allEntries = activityBuffer.toArray();
169
-
170
- if (afterId === 0) {
171
- return { entries: allEntries, gap: false, totalAdded: total };
172
- }
173
-
174
- // Check for gap: if afterId is too old and has been evicted
175
- const oldestId = allEntries.length > 0 ? allEntries[0].id : nextId;
176
- if (afterId < oldestId) {
177
- return {
178
- entries: allEntries,
179
- gap: true,
180
- gapFrom: afterId + 1,
181
- availableFrom: oldestId,
182
- totalAdded: total,
183
- };
184
- }
185
-
186
- // Filter to entries after the cursor
187
- const filtered = allEntries.filter(e => e.id > afterId);
188
- return { entries: filtered, gap: false, totalAdded: total };
189
- }
190
-
191
- /**
192
- * Get the N most recent activity entries.
193
- */
194
- export function getActivityHistory(limit: number = 50): {
195
- entries: ActivityEntry[];
196
- totalAdded: number;
197
- } {
198
- const allEntries = activityBuffer.toArray();
199
- const sliced = limit < allEntries.length ? allEntries.slice(-limit) : allEntries;
200
- return { entries: sliced, totalAdded: activityBuffer.totalAdded };
201
- }
202
-
203
- /**
204
- * Get subscriber count (for debugging/health).
205
- */
206
- export function getSubscriberCount(): number {
207
- return subscribers.size;
208
- }