Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,89 @@
# Changelog

## [1.57.4.0] - 2026-06-08

## **A failed Playwright Chromium install no longer aborts `./setup` mid-run. Skills, hooks, and migrations are already in place; you get a named warning and a retry hint.**

The Playwright Chromium install is the riskiest step in `./setup`. It needs
network, it talks to a Microsoft CDN, on Windows it bounces through Node.js
because of a Bun pipe bug, and on locked-down machines it may not even be
allowed to run. Until now, any failure in that step was fatal: `exit 1`, and
the run dies before skills get registered, before hooks land, before
migrations run. Re-running `./setup` would start over from the top.

That's the wrong shape. The user already has a working bun, a built browse
binary, and a tree of skill templates ready to go. The thing that failed —
Chromium availability — only affects browser-driven features (`/qa`,
`/design-review`, `make-pdf`, sidebar, `/pair-agent`). Everything else can
still work. So make the failure non-fatal.

This release replaces both `exit 1` paths in the Playwright install/verify
block with a `$_PW_FAIL_REASON` accumulator. On install failure the script
prints a named warning telling you which sub-step failed, names the
affected features, gives Windows users the specific known-issue link, and
tells you to re-run `./setup` to retry — at which point only the Playwright
step needs to succeed, because everything else is already done.

### The numbers that matter

Source: `bun test test/setup-playwright-warn-not-exit.test.ts` on this branch.

| Metric | Before | After | Δ |
|--------|--------|-------|---|
| Playwright failure aborts `./setup` | yes | no | warn instead of exit |
| Skills registered when Chromium download fails | sometimes | always | depends on which step failed first |
| Named failure reason in warning | no | yes | `chromium-install`, `windows-no-node`, `windows-node-modules`, `post-install-launch` |
| Retry guidance in failure output | no | yes | "Re-run ./setup to retry" |

The four named reasons matter. When a Playwright install fails it's usually
not obvious whether the download died, Chromium was downloaded but won't
launch, or Node.js isn't on PATH (Windows). The warning calls it by name
so users and bug reports can be specific.

### What this means for you

If your network blinks during `./setup`, or you're on a corporate machine
that won't let `bunx playwright install chromium` finish, you'll now get
gstack installed with everything except browser features, plus a clear
warning. Re-run `./setup` when the network is back to enable browser
features. If you're on Windows and hit the Bun-on-Windows pipe bug, the
warning tells you exactly which `node -e` check to verify.

### Itemized changes

#### Changed

- `setup`: Playwright Chromium install/verify no longer aborts on failure.
The two `exit 1` paths in the install block are replaced with a
`$_PW_FAIL_REASON` accumulator (values: `chromium-install`,
`windows-no-node`, `windows-node-modules`, `post-install-launch`). When
set, the script prints a named warning to stderr, lists the affected
browser-driven features, includes the Bun-on-Windows known-issue link
if applicable, and tells the user to re-run `./setup` to retry. All
other setup steps complete normally.

#### For contributors

- `test/setup-playwright-warn-not-exit.test.ts` — 5 tests:
- Two source-anchored checks confirm no `exit 1` is reachable from the
Playwright block and the `_PW_FAIL_REASON` accumulator pattern is in
place.
- Two functional tests execute the live block from `setup` with stubbed
`ensure_playwright_browser` and `bunx`, simulate install + post-install
failures, and assert the script exits 0 with the named warning.
- One side-by-side test inlines the OLD `exit 1` bug shape and confirms
it returned non-zero — proves the exit-code difference is real, not a
quoting illusion.

#### Coordination

- This PR is one of three carved out from #1883 per @jbetala7's review
request. It touches the same code block as #1838 (which pins the
Playwright install version), so whichever of #1838 or this PR lands
first will need a small rebase on the other. The two changes are
complementary — #1838 fixes which Chromium build gets installed; this
PR fixes what happens when the install fails.

## [1.57.3.0] - 2026-06-07

## **Every PR `/ship` opens gets the version stamped into its title, fork and agent PRs included.**
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.57.3.0
1.57.4.0
144 changes: 72 additions & 72 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,74 +1,74 @@
{
"name": "gstack",
"version": "1.57.3.0",
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
"license": "MIT",
"type": "module",
"bin": {
"browse": "./browse/dist/browse",
"make-pdf": "./make-pdf/dist/pdf"
},
"scripts": {
"build": "bash scripts/build.sh",
"vendor:xterm": "mkdir -p extension/lib && cp node_modules/xterm/lib/xterm.js extension/lib/xterm.js && cp node_modules/xterm/css/xterm.css extension/lib/xterm.css && cp node_modules/xterm-addon-fit/lib/xterm-addon-fit.js extension/lib/xterm-addon-fit.js",
"dev:make-pdf": "bun run make-pdf/src/cli.ts",
"dev:design": "bun run design/src/cli.ts",
"gen:skill-docs": "bun run scripts/gen-skill-docs.ts",
"gen:skill-docs:user": "bun run scripts/gen-skill-docs.ts --respect-detection",
"dev": "bun run browse/src/cli.ts",
"server": "bun run browse/src/server.ts",
"test": "bun test browse/test/ test/ make-pdf/test/ --ignore 'test/skill-e2e-*.test.ts' --ignore test/skill-llm-eval.test.ts --ignore test/skill-routing-e2e.test.ts --ignore test/codex-e2e.test.ts --ignore test/gemini-e2e.test.ts && (bun run slop:diff 2>/dev/null || true)",
"test:free": "bun run scripts/test-free-shards.ts",
"test:windows": "bun run scripts/test-free-shards.ts --windows-only",
"test:evals": "EVALS=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:evals:all": "EVALS=1 EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:e2e": "EVALS=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:e2e:all": "EVALS=1 EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:gate": "EVALS=1 EVALS_TIER=gate bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:periodic": "EVALS=1 EVALS_TIER=periodic EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:codex": "EVALS=1 bun test test/codex-e2e.test.ts",
"test:codex:all": "EVALS=1 EVALS_ALL=1 bun test test/codex-e2e.test.ts",
"test:gemini": "EVALS=1 bun test test/gemini-e2e.test.ts",
"test:gemini:all": "EVALS=1 EVALS_ALL=1 bun test test/gemini-e2e.test.ts",
"skill:check": "bun run scripts/skill-check.ts",
"dev:skill": "bun run scripts/dev-skill.ts",
"start": "bun run browse/src/server.ts",
"eval:list": "bun run scripts/eval-list.ts",
"eval:compare": "bun run scripts/eval-compare.ts",
"eval:summary": "bun run scripts/eval-summary.ts",
"eval:watch": "bun run scripts/eval-watch.ts",
"eval:select": "bun run scripts/eval-select.ts",
"analytics": "bun run scripts/analytics.ts",
"test:audit": "bun test test/audit-compliance.test.ts",
"slop": "npx slop-scan scan . 2>/dev/null || echo 'slop-scan not available (install with: npm i -g slop-scan)'",
"slop:diff": "bun run scripts/slop-diff.ts"
},
"dependencies": {
"@huggingface/transformers": "^4.1.0",
"@ngrok/ngrok": "^1.7.0",
"diff": "^7.0.0",
"marked": "^18.0.2",
"playwright": "^1.58.2",
"puppeteer-core": "^24.40.0",
"socks": "^2.8.8"
},
"engines": {
"bun": ">=1.0.0"
},
"keywords": [
"browser",
"automation",
"playwright",
"headless",
"cli",
"claude",
"ai-agent",
"devtools"
],
"devDependencies": {
"@anthropic-ai/claude-agent-sdk": "0.2.117",
"@anthropic-ai/sdk": "^0.78.0",
"xterm": "5",
"xterm-addon-fit": "^0.8.0"
}
"name": "gstack",
"version": "1.57.4.0",
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
"license": "MIT",
"type": "module",
"bin": {
"browse": "./browse/dist/browse",
"make-pdf": "./make-pdf/dist/pdf"
},
"scripts": {
"build": "bash scripts/build.sh",
"vendor:xterm": "mkdir -p extension/lib && cp node_modules/xterm/lib/xterm.js extension/lib/xterm.js && cp node_modules/xterm/css/xterm.css extension/lib/xterm.css && cp node_modules/xterm-addon-fit/lib/xterm-addon-fit.js extension/lib/xterm-addon-fit.js",
"dev:make-pdf": "bun run make-pdf/src/cli.ts",
"dev:design": "bun run design/src/cli.ts",
"gen:skill-docs": "bun run scripts/gen-skill-docs.ts",
"gen:skill-docs:user": "bun run scripts/gen-skill-docs.ts --respect-detection",
"dev": "bun run browse/src/cli.ts",
"server": "bun run browse/src/server.ts",
"test": "bun test browse/test/ test/ make-pdf/test/ --ignore 'test/skill-e2e-*.test.ts' --ignore test/skill-llm-eval.test.ts --ignore test/skill-routing-e2e.test.ts --ignore test/codex-e2e.test.ts --ignore test/gemini-e2e.test.ts && (bun run slop:diff 2>/dev/null || true)",
"test:free": "bun run scripts/test-free-shards.ts",
"test:windows": "bun run scripts/test-free-shards.ts --windows-only",
"test:evals": "EVALS=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:evals:all": "EVALS=1 EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:e2e": "EVALS=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:e2e:all": "EVALS=1 EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:gate": "EVALS=1 EVALS_TIER=gate bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-llm-eval.test.ts test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:periodic": "EVALS=1 EVALS_TIER=periodic EVALS_ALL=1 bun test --retry 2 --concurrent --max-concurrency ${EVALS_CONCURRENCY:-15} test/skill-e2e-*.test.ts test/skill-routing-e2e.test.ts test/codex-e2e.test.ts test/gemini-e2e.test.ts",
"test:codex": "EVALS=1 bun test test/codex-e2e.test.ts",
"test:codex:all": "EVALS=1 EVALS_ALL=1 bun test test/codex-e2e.test.ts",
"test:gemini": "EVALS=1 bun test test/gemini-e2e.test.ts",
"test:gemini:all": "EVALS=1 EVALS_ALL=1 bun test test/gemini-e2e.test.ts",
"skill:check": "bun run scripts/skill-check.ts",
"dev:skill": "bun run scripts/dev-skill.ts",
"start": "bun run browse/src/server.ts",
"eval:list": "bun run scripts/eval-list.ts",
"eval:compare": "bun run scripts/eval-compare.ts",
"eval:summary": "bun run scripts/eval-summary.ts",
"eval:watch": "bun run scripts/eval-watch.ts",
"eval:select": "bun run scripts/eval-select.ts",
"analytics": "bun run scripts/analytics.ts",
"test:audit": "bun test test/audit-compliance.test.ts",
"slop": "npx slop-scan scan . 2>/dev/null || echo 'slop-scan not available (install with: npm i -g slop-scan)'",
"slop:diff": "bun run scripts/slop-diff.ts"
},
"dependencies": {
"@huggingface/transformers": "^4.1.0",
"@ngrok/ngrok": "^1.7.0",
"diff": "^7.0.0",
"marked": "^18.0.2",
"playwright": "^1.58.2",
"puppeteer-core": "^24.40.0",
"socks": "^2.8.8"
},
"engines": {
"bun": ">=1.0.0"
},
"keywords": [
"browser",
"automation",
"playwright",
"headless",
"cli",
"claude",
"ai-agent",
"devtools"
],
"devDependencies": {
"@anthropic-ai/claude-agent-sdk": "0.2.117",
"@anthropic-ai/sdk": "^0.78.0",
"xterm": "5",
"xterm-addon-fit": "^0.8.0"
}
}
65 changes: 39 additions & 26 deletions setup
Original file line number Diff line number Diff line change
Expand Up @@ -475,44 +475,57 @@ if [ "$INSTALL_OPENCODE" -eq 1 ] && [ "$NEEDS_BUILD" -eq 0 ]; then
)
fi

# 2. Ensure Playwright's Chromium is available
# 2. Ensure Playwright's Chromium is available (best-effort: warn on failure).
#
# When Chromium download or launch fails (network issue, missing sudo,
# offline mirror, Bun-on-Windows pipe bug), browser-driven features
# (/qa, /design-review, make-pdf, sidebar, /pair-agent) are unavailable
# until the user retries. All other gstack functionality (skill
# registration, hooks, migrations) is already installed by earlier steps.
# Previously this step was fatal (exit 1) and would abort the whole run
# mid-setup, leaving skills unregistered. Now it sets $_PW_FAIL_REASON,
# prints a named warning, and returns control. Re-running ./setup retries.
_PW_FAIL_REASON=""

if ! ensure_playwright_browser; then
echo "Installing Playwright Chromium..."
(
cd "$SOURCE_GSTACK_DIR"
bunx playwright install chromium
)
if ! ( cd "$SOURCE_GSTACK_DIR" && bunx playwright install chromium ); then
_PW_FAIL_REASON="chromium-install"
fi

if [ "$IS_WINDOWS" -eq 1 ]; then
if [ -z "$_PW_FAIL_REASON" ] && [ "$IS_WINDOWS" -eq 1 ]; then
# On Windows, Node.js launches Chromium (not Bun — see oven-sh/bun#4253).
# Ensure playwright is importable by Node from the gstack directory.
if ! command -v node >/dev/null 2>&1; then
echo "gstack setup failed: Node.js is required on Windows (Bun cannot launch Chromium due to a pipe bug)" >&2
echo " Install Node.js: https://nodejs.org/" >&2
exit 1
_PW_FAIL_REASON="windows-no-node"
else
echo "Windows detected — verifying Node.js can load Playwright..."
if ! ( cd "$SOURCE_GSTACK_DIR" && \
# Bun's node_modules already has playwright; verify Node can require it
{ node -e "require('playwright')" 2>/dev/null || npm install --no-save playwright; } && \
# @ngrok/ngrok is externalized in server-node.mjs and resolved at runtime.
# Verify the platform-specific native binary is installed so /pair-agent
# tunnels don't fail later with a cryptic module-not-found error.
{ node -e "require('@ngrok/ngrok')" 2>/dev/null || npm install --no-save @ngrok/ngrok; } ); then
_PW_FAIL_REASON="windows-node-modules"
fi
fi
echo "Windows detected — verifying Node.js can load Playwright..."
(
cd "$SOURCE_GSTACK_DIR"
# Bun's node_modules already has playwright; verify Node can require it
node -e "require('playwright')" 2>/dev/null || npm install --no-save playwright
# @ngrok/ngrok is externalized in server-node.mjs and resolved at runtime.
# Verify the platform-specific native binary is installed so /pair-agent
# tunnels don't fail later with a cryptic module-not-found error.
node -e "require('@ngrok/ngrok')" 2>/dev/null || npm install --no-save @ngrok/ngrok
)
fi
fi

if ! ensure_playwright_browser; then
if [ -z "$_PW_FAIL_REASON" ] && ! ensure_playwright_browser; then
_PW_FAIL_REASON="post-install-launch"
fi

if [ -n "$_PW_FAIL_REASON" ]; then
echo "" >&2
echo " warning: Playwright Chromium is unavailable ($_PW_FAIL_REASON)." >&2
echo " Browser features (/qa, /design-review, make-pdf, sidebar, /pair-agent) are unavailable." >&2
if [ "$IS_WINDOWS" -eq 1 ]; then
echo "gstack setup failed: Playwright Chromium could not be launched via Node.js" >&2
echo " This is a known issue with Bun on Windows (oven-sh/bun#4253)." >&2
echo " Ensure Node.js is installed and 'node -e \"require('playwright')\"' works." >&2
else
echo "gstack setup failed: Playwright Chromium could not be launched" >&2
echo " Known Bun-on-Windows issue (oven-sh/bun#4253). Ensure Node.js is installed," >&2
echo " then verify: node -e \"require('playwright')\"" >&2
fi
exit 1
echo " Re-run ./setup to retry. Skills, hooks, and migrations are already installed." >&2
fi

# 2b. Ensure a color-emoji font is installed so make-pdf emoji render (Linux).
Expand Down
Loading
Loading