When set, the browse CLI refuses to start a new server and exits with
a clear error: "Server not available, run /open-gstack-browser to restart."
The sidebar agent sets this so users never get an invisible headless
browser when the headed one is closed.
Ensures the child claude process connects to the existing headed
browse server (port 34567) instead of spawning a new headless one.
Without this, sidebar chat commands run in an invisible browser.
Two issues making the sidebar look broken when it's actually working:
1. "Timed out after 300s" error displayed after agent_done — this is a
cleanup timer, not a real error. Now suppressed when no active session.
2. "(session ended)" text appended on every idle poll — removed entirely.
The thinking spinner is cleaned up silently instead.
Root cause: sidepanel asked background "are you connected?" but background's
health check hadn't succeeded yet (1-10s gap). Sidepanel waited forever.
Fix: when background says not connected, sidepanel hits /health directly
with fetch(). Gets the token from the response. Bypasses background
entirely for initial connection. Shows step-by-step debug info:
"Checking server directly... port: 34567 / Trying GET /health..."
Replace useless "Connecting..." with real-time debug info:
- "Looking for browse server... (attempt N)"
- Shows port, server responding status, token status
- Shows chrome.runtime errors if extension messaging fails
- Tells user to run /open-gstack-browser if server not found
Root cause: extension service worker starts before Bun.serve() is
listening. First checkHealth() fails, next attempt is 10 seconds
later. User stares at "Connecting..." for 10 seconds.
Fix: retry every 1s for up to 15 attempts on startup, then switch
to 10s polling once connected (or after 15s gives up). Sidebar
should connect within 1-2 seconds of server becoming available.
3 new tests verify the fast-retry → slow-poll transition.
- Show attempt count in loading screen ("Connecting... attempt 3")
- After 5 failed attempts, show debug details (port, connected, token)
so stuck users can see exactly what's failing
- Add 4 tests: getPort includes token, tryConnect uses token,
dead state exists with MAX_RECONNECT_ATTEMPTS, reconnectAttempts visible
The sidebar called tryConnect() → getPort → got {port, connected} but
NO token. All subsequent requests (SSE, chat poll) failed with 401.
The token only arrived later via the health broadcast, but by then
the SSE connection was already broken.
Fix: include authToken in the getPort response so the sidebar has
the token from its very first connection attempt.
Replace invisible text fallback with visible amber bubble + animated
right arrow (→) pointing toward where the sidebar opens. Always correct
regardless of window size (unlike the old up arrow at toolbar chrome).
Adds a "reload" button next to "debug" and "clear" in the sidebar
footer. Calls location.reload() to fully refresh the side panel,
re-run connection logic, and clear stale state.
- Replace single-attempt sidePanel.open() with autoOpenSidePanel() that
retries up to 5 times with 500ms-5000ms backoff
- Fire on both onInstalled AND every service worker startup
- Remove misaligned arrow from welcome page, replace with text fallback
- Add 12 tests: welcome page structure, /welcome endpoint, headed launch
navigation timing, sidebar auto-open retry logic, extension-ready event
- Add top-level setTimeout in background.js that fires on every service
worker startup (onInstalled only fires on install/update)
- Remove misaligned arrow from welcome page, replace with text fallback
that hides when extension content script fires gstack-extension-ready
- Add /welcome endpoint to server.ts, serves welcome.html
- Navigate to /welcome after server starts (not during launchHeaded,
which runs before the server is listening)
- welcome.html bundled in browse/src/ for portability
* fix: top-level skill dirs so Claude discovers unprefixed names
Replace directory symlinks (gstack/qa → qa) with real directories
containing a SKILL.md symlink. Claude Code auto-prefixes skills nested
under a parent dir symlink, so /plan-ceo-review became "Unknown skill"
even with skill_prefix=false. Real dirs fix this.
Also syncs package.json version to match VERSION file and updates
test assertions to match the new mkdir + ln approach.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: update symlink references to new top-level directory pattern
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: regression tests for top-level skill directory structure
Verifies the invariant that setup/relink creates real directories (not
symlinks) at the top level, with SKILL.md symlinks inside. This prevents
Claude Code from auto-prefixing skills with gstack- when using --no-prefix.
Tests added:
- unprefixed skills must be real dirs with SKILL.md symlinks
- prefixed skills must also be real dirs with SKILL.md symlinks
- old directory symlinks get upgraded to real directories
- cleanup functions handle both old symlinks and new dir pattern
- link function removes old directory symlinks before mkdir
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: namespace isolation tests for first install + mode switching
Verifies the core invariant: when you pick a prefix mode, ONLY that
mode's entries exist. Zero pollution from the other mode.
- first install --no-prefix: only flat names, zero gstack-* leaks
- first install --prefix: only gstack-* names, zero flat leaks
- non-TTY defaults to flat names
- switching prefix→no-prefix removes ALL gstack-* entries
- switching no-prefix→prefix removes ALL flat entries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: upgrade migration system — versioned fix scripts for broken state
Adds gstack-upgrade/migrations/ directory with version-keyed bash scripts
that run automatically during /gstack-upgrade (Step 4.75, after ./setup).
Each script is idempotent and handles state fixes that setup alone can't
cover. First migration: v0.15.2.0.sh runs gstack-relink to fix stale
directory symlinks from pre-v0.15.2.0 installs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: migration script validation + v0.15.2.0 end-to-end fix test
Tests that migration scripts are executable, parse without syntax errors,
follow the v{VERSION}.sh naming convention, and that v0.15.2.0 actually
fixes stale directory symlinks by converting them to real directories.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: upgrade migration guide in CONTRIBUTING.md + CLAUDE.md pointer
CONTRIBUTING.md: new "Upgrade migrations" section documenting when and
how to add migration scripts for broken on-disk state.
CLAUDE.md: added note under vendored symlink awareness pointing to
CONTRIBUTING.md's migration section when worried about broken installs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: /design-html works from any starting point — not just design-shotgun
Three routing modes: approved mockup (Case A), CEO plan or design variants
without formal approval (Case B), or clean slate with just a description
(Case C). Each mode asks the right questions via AskUserQuestion instead of
blocking with "no approved design found."
* chore: bump version and changelog (v0.15.1.0)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: session timeline binaries (gstack-timeline-log + gstack-timeline-read)
New binaries for the Session Intelligence Layer. gstack-timeline-log appends
JSONL events to ~/.gstack/projects/$SLUG/timeline.jsonl. gstack-timeline-read
reads, filters, and formats timeline data for /retro consumption.
Timeline is local-only project intelligence, never sent anywhere. Always-on
regardless of telemetry setting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: preamble context recovery + timeline events + predictive suggestions
Layers 1-3 of the Session Intelligence Layer:
- Timeline start/complete events injected into every skill via preamble
- Context recovery (tier 2+): lists recent CEO plans, checkpoints, reviews
- Cross-session injection: LAST_SESSION and LATEST_CHECKPOINT for branch
- Predictive skill suggestion from recent timeline patterns
- Welcome back message synthesis
- Routing rules for /checkpoint and /health
Timeline writes are NOT gated by telemetry (local project intelligence).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: /checkpoint + /health skills (Layers 4-5)
/checkpoint: save/resume/list working state snapshots. Supports cross-branch
listing for Conductor workspace handoff. Session duration tracking.
/health: code quality scorekeeper. Wraps project tools (tsc, biome, knip,
shellcheck, tests), computes composite 0-10 score, tracks trends over time.
Auto-detects tools or reads from CLAUDE.md ## Health Stack.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: regenerate SKILL.md files + add timeline tests
9 timeline tests (all passing) mirroring learnings.test.ts pattern.
All 34 SKILL.md files regenerated with new preamble (context recovery,
timeline events, routing rules for /checkpoint and /health).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: bump version and changelog (v0.15.0.0)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update self-learning roadmap post-Session Intelligence
R1-R3 marked shipped with actual versions. R4 becomes Adaptive Ceremony
(trust as separate policy engine, scope-aware, gradual degradation). R5
becomes /autoship (resumable state machine, not linear chain). R6-R7
unbundled from old R5. Added State Systems reference, Risk Register
(Codex-reviewed), and validation metrics for R4.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: E2E tests for Session Intelligence (timeline, recovery, checkpoint)
3 gate-tier E2E tests:
- timeline-event-flow: binary data flow round-trip (no LLM)
- context-recovery-artifacts: seeded artifacts appear in preamble
- checkpoint-save-resume: checkpoint file created with YAML frontmatter
Also fixes package.json version sync (0.14.6.0 → 0.15.0.0).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: remove dead contributor mode, replace with operational self-improvement slot
Contributor mode never fired in 18 days of heavy use (required manual opt-in
via gstack-config, gated behind _CONTRIB=true, wrote disconnected markdown).
Removes: generateContributorMode(), _CONTRIB bash var, 2 E2E tests, touchfile
entry, doc references. Cleans up skip-lists in plan-ceo-review, autoplan,
review resolver, and document-release templates.
The operational self-improvement system (next commit) replaces this slot with
automatic learning capture that requires no opt-in.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: operational self-improvement — every skill learns from failures
Adds universal operational learning capture to the preamble completion protocol.
At the end of every skill session, the agent reflects on CLI failures, wrong
approaches, and project quirks, logging them as type "operational" to the
learnings JSONL. Future sessions surface these automatically.
- generateCompletionStatus(ctx) now includes operational capture section
- Preamble bash shows top 3 learnings inline when count > 5
- New "operational" type in generateLearningsLog alongside pattern/pitfall/etc
- Updated unit tests + operational seed entry in learnings E2E
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: wire learnings into all insight-producing skills
Adds LEARNINGS_SEARCH and/or LEARNINGS_LOG to 10 skill templates that
produce reusable insights but were previously disconnected from the
learning system:
- office-hours, plan-ceo-review, plan-eng-review: add LOG (had SEARCH)
- plan-design-review: add both SEARCH + LOG (had neither)
- design-review, design-consultation, cso, qa, qa-only: add both
- retro: add SEARCH (had LOG)
13 skills now fully participate in the learning loop (read + write).
Every review, QA, investigation, and design session both consults prior
learnings and contributes new ones.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add operational-learning E2E test (gate-tier)
Validates the write path: agent encounters a CLI failure, logs an
operational learning to JSONL via gstack-learnings-log. Replaces the
removed contributor-mode E2E test.
Setup: temp git repo, copy bin scripts, set GSTACK_HOME.
Prompt: simulated npm test failure needing --experimental-vm-modules.
Assert: learnings.jsonl exists with type=operational entry.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: learnings-show E2E slug mismatch — seed at computed slug, not hardcoded
The test seeded learnings at projects/test-project/ but gstack-slug computes
the slug from basename(workDir) when no git remote exists. The agent's search
looked at the wrong path and found nothing.
Fix: compute slug the same way gstack-slug does (basename + sanitize) and
seed the learnings there.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: bump version and changelog (v0.13.8.0)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: Session Intelligence Layer design doc
Frames gstack as the persistent brain that survives Claude's ephemeral
context window. Architecture diagram, 5-layer feature breakdown, and
research sources from claude-mem, Anthropic engineering blog, CodeScene,
and Claude Code Agent Teams.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add context intelligence, health, swarm, and refactoring to roadmap
9 new TODOS across 4 sections based on Claude Code ecosystem research:
- Context Intelligence (P1): preamble artifact recovery, session timeline,
cross-session injection, /checkpoint skill, vision doc
- Health (P1): /health dashboard with CodeScene MCP integration option,
/health as /ship quality gate
- Swarm (P2): extract Review Army into reusable multi-agent primitive
- Refactoring (P2): /refactor-prep for pre-refactor token hygiene
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Left-align all sidebar text (chat welcome, loading, empty states,
notifications, inspector empty, session placeholder)
- Dispatch 'gstack-extension-ready' CustomEvent from content.js so
the welcome page can detect when the sidebar is active
- Generated 1024px icon: dark terminal window with amber prompt cursor
- Converted to .icns with all macOS sizes (16-1024px, 1x and 2x)
- build-app.sh copies icon into both the outer .app and bundled Chromium's
Resources (Chromium's process owns the Dock icon, not the launcher)
- browser-manager.ts patches Chromium's icon at runtime for dev mode too
- Both the Dock and Cmd+Tab now show the GStack icon
Main shipped v0.14.5.0 (Ship Idempotency + Skill Prefix Fix). Our branch's
GStack Browser entry bumps to v0.14.6.0. Both entries preserved in order.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add idempotency guards to /ship Steps 4, 7, 8 (#649)
If git push succeeds but gh pr create fails, re-running /ship would
double-bump VERSION and duplicate CHANGELOG entries. Now:
- Step 4: check if VERSION already differs from base branch
- Step 7: fetch only the specific branch, skip push if already up to date
- Step 8: if PR exists, update body via gh pr edit instead of creating duplicate
No CHANGELOG guard needed — Step 5 is already idempotent by design
("replace existing entries with one unified entry").
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: patch name: in SKILL.md frontmatter for prefix mode (#620, #578)
./setup --prefix creates gstack-* symlinks but SKILL.md still says
name: qa, so Claude Code ignores the prefix. Now:
- New bin/gstack-patch-names shared helper patches name: field via sed
- setup calls it after link_claude_skill_dirs
- gstack-relink calls it after symlink loop
- gen-skill-docs.ts prints warning when skill_prefix is true
Edge cases: gstack-upgrade not double-prefixed, root gstack skill
never prefixed, prefix removal restores original names, SKILL.md
without frontmatter is a safe no-op.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add name patching + ship idempotency tests (#620, #649)
- 4 unit tests for name: patching in relink.test.ts (prefix on/off,
gstack-upgrade not double-prefixed, no-frontmatter no-op)
- 2 tests for gen-skill-docs prefix warning
- 1 E2E test for ship idempotency (periodic tier)
- Updated setupMockInstall to write SKILL.md with proper frontmatter
- Added ship-idempotency touchfiles + tier classification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: bump version and changelog (v0.14.3.0)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: PR idempotency checks open state, dedupe touchfiles, sync package.json
- Step 8 PR guard now checks state==OPEN so closed PRs don't prevent
new PR creation (adversarial review finding)
- Remove duplicate ship-idempotency entry in E2E_TOUCHFILES
- Sync package.json version to 0.14.3.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: patch name: before creating symlinks to fix --no-prefix ordering bug
gstack-patch-names must run BEFORE link_claude_skill_dirs so symlink
names reflect the correct (patched) name: values. Previously, switching
from --prefix to --no-prefix would read stale gstack-* names from
SKILL.md and create wrong symlinks. (Codex adversarial finding)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add GSTACK_CHROMIUM_PATH env var for custom Chromium binary
- Add BROWSE_EXTENSIONS_DIR env var for extension path override
- Move auth token to /health endpoint (fixes read-only .app bundles)
- Anti-bot stealth: disable navigator.webdriver, fake plugins, languages
- Custom user agent: Chrome/<version> GStackBrowser (auto-detects version)
- Rebrand Chromium plist to "GStack Browser" at launch time
- Update security test to match new token-via-health approach
* feat: CDP inspector module — persistent sessions, CSS cascade, style modification
New browse/src/cdp-inspector.ts with full CDP inspection engine:
- inspectElement() via CSS.getMatchedStylesForNode + DOM.getBoxModel
- modifyStyle() via CSS.setStyleTexts with headless page.evaluate fallback
- Persistent CDP session lifecycle (create, reuse, detach on nav, re-create)
- Specificity sorting, overridden property detection, UA rule filtering
- Modification history with undo support
- formatInspectorResult() for CLI output
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: browse server inspector endpoints + inspect/style/cleanup/prettyscreenshot CLI
Server endpoints: POST /inspector/pick, GET /inspector, POST /inspector/apply,
POST /inspector/reset, GET /inspector/history, GET /inspector/events (SSE).
CLI commands: inspect (CDP cascade), style (live CSS mod), cleanup (page clutter
removal), prettyscreenshot (clean screenshot pipeline).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: sidebar CSS inspector — element picker, box model, rule cascade, quick edit
Extension changes for the visual CSS inspector:
- inspector.js: element picker with hover highlight, CSS selector generation,
basic mode fallback (getComputedStyle + CSSOM), page alteration handlers
- inspector.css: picker overlay styles (blue highlight + tooltip)
- background.js: inspector message routing (picker <-> server <-> sidepanel)
- sidepanel: Inspector tab with box model viz (gstack palette), matched rules
with specificity badges, computed styles, click-to-edit quick edit,
Send to Agent/Code button, empty/loading/error states
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: document inspect, style, cleanup, prettyscreenshot browse commands
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: auto-track user-created tabs and handle tab close
browser-manager.ts changes:
- context.on('page') listener: automatically tracks tabs opened by the user
(Cmd+T, right-click open in new tab, window.open). Previously only
programmatic newTab() was tracked, so user tabs were invisible.
- page.on('close') handler in wirePageEvents: removes closed tabs from the
pages map and switches activeTabId to the last remaining tab.
- syncActiveTabByUrl: match Chrome extension's active tab URL to the correct
Playwright page for accurate tab identity.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: per-tab agent isolation via BROWSE_TAB environment variable
Prevents parallel sidebar agents from interfering with each other's tab context.
Three-layer fix:
- sidebar-agent.ts: passes BROWSE_TAB=<tabId> env var to each claude process,
per-tab processing set allows concurrent agents across tabs
- cli.ts: reads process.env.BROWSE_TAB and includes tabId in command request body
- server.ts: handleCommand() temporarily switches activeTabId when tabId is present,
restores after command completes (safe: Bun event loop is single-threaded)
Also: per-tab agent state (TabAgentState map), per-tab message queuing,
per-tab chat buffers, verbose streaming narration, stop button endpoint.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: sidebar per-tab chat context, tab bar sync, stop button, UX polish
Extension changes:
- sidepanel.js: per-tab chat history (tabChatHistories map), switchChatTab()
swaps entire chat view, browserTabActivated handler for instant tab sync,
stop button wired to /sidebar-agent/stop, pollTabs renders tab bar
- sidepanel.html: updated banner text ("Browser co-pilot"), stop button markup,
input placeholder "Ask about this page..."
- sidepanel.css: tab bar styles, stop button styles, loading state fixes
- background.js: chrome.tabs.onActivated sends browserTabActivated to sidepanel
with tab URL for instant tab switch detection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: per-tab isolation, BROWSE_TAB pinning, tab tracking, sidebar UX
sidebar-agent.test.ts (new tests):
- BROWSE_TAB env var passed to claude process
- CLI reads BROWSE_TAB and sends tabId in body
- handleCommand accepts tabId, saves/restores activeTabId
- Tab pinning only activates when tabId provided
- Per-tab agent state, queue, concurrency
- processingTabs set for parallel agents
sidebar-ux.test.ts (new tests):
- context.on('page') tracks user-created tabs
- page.on('close') removes tabs from pages map
- Tab isolation uses BROWSE_TAB not system prompt hack
- Per-tab chat context in sidepanel
- Tab bar rendering, stop button, banner text
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: resolve merge conflicts — keep security defenses + per-tab isolation
Merged main's security improvements (XML escaping, prompt injection defense,
allowed commands whitelist, --model opus, Write tool, stderr capture) with
our branch's per-tab isolation (BROWSE_TAB env var, processingTabs set,
no --resume). Updated test expectations for expanded system prompt.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: bump version and changelog (v0.13.9.0)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add inspector message types to background.js allowlist
Pre-existing bug found by Codex: ALLOWED_TYPES in background.js was missing
all inspector message types (startInspector, stopInspector, elementPicked,
pickerCancelled, applyStyle, toggleClass, injectCSS, resetAll, inspectResult).
Messages were silently rejected, making the inspector broken on ALL pages.
Also: separate executeScript and insertCSS into individual try blocks in
injectInspector(), store inspectorMode for routing, and add content.js
fallback when script injection fails (CSP, chrome:// pages).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: basic element picker in content.js for CSP-restricted pages
When inspector.js can't be injected (CSP, chrome:// pages), content.js
provides a basic picker using getComputedStyle + CSSOM:
- startBasicPicker/stopBasicPicker message handlers
- captureBasicData() with ~30 key CSS properties, box model, matched rules
- Hover highlight with outline save/restore (never leaves artifacts)
- Click uses e.target directly (no re-querying by selector)
- Sends inspectResult with mode:'basic' for sidebar rendering
- Escape key cancels picker and restores outlines
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: cleanup + screenshot buttons in sidebar inspector toolbar
Two action buttons in the inspector toolbar:
- Cleanup (🧹): POSTs cleanup --all to server, shows spinner, chat
notification on success, resets inspector state (element may be removed)
- Screenshot (📸): POSTs screenshot to server, shows spinner, chat
notification with saved file path
Shared infrastructure:
- .inspector-action-btn CSS with loading spinner via ::after pseudo-element
- chat-notification type in addChatEntry() for system messages
- package.json version bump to 0.13.9.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: inspector allowlist, CSP fallback, cleanup/screenshot buttons
16 new tests in sidebar-ux.test.ts:
- Inspector message allowlist includes all inspector types
- content.js basic picker (startBasicPicker, captureBasicData, CSSOM,
outline save/restore, inspectResult with mode basic, Escape cleanup)
- background.js CSP fallback (separate try blocks, inspectorMode, fallback)
- Cleanup button (POST /command, inspector reset after success)
- Screenshot button (POST /command, notification rendering)
- Chat notification type and CSS styles
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update project documentation for v0.13.9.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: cleanup + screenshot buttons in chat toolbar (not just inspector)
Quick actions toolbar (🧹 Cleanup, 📸 Screenshot) now appears above the chat
input, always visible. Both inspector and chat buttons share runCleanup() and
runScreenshot() helper functions. Clicking either set shows loading state on
both simultaneously.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: chat toolbar buttons, shared helpers, quick-action-btn styles
Tests that chat toolbar exists (chat-cleanup-btn, chat-screenshot-btn,
quick-actions container), CSS styles (.quick-action-btn, .quick-action-btn.loading),
shared runCleanup/runScreenshot helper functions, and cleanup inspector reset.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: aggressive cleanup heuristics — overlays, scroll unlock, blur removal
Massively expanded CLEANUP_SELECTORS with patterns from uBlock Origin and
Readability.js research:
- ads: 30+ selectors (Google, Amazon, Outbrain, Taboola, Criteo, etc.)
- cookies: OneTrust, Cookiebot, TrustArc, Quantcast + generic patterns
- overlays (NEW): paywalls, newsletter popups, interstitials, push prompts,
app download banners, survey modals
- social: follow prompts, share tools
- Cleanup now defaults to --all when no args (sidebar button fix)
- Uses !important on all display:none (overrides inline styles)
- Unlocks body/html scroll (overflow:hidden from modal lockout)
- Removes blur/filter effects (paywall content blur)
- Removes max-height truncation (article teaser truncation)
- Collapses empty ad placeholder whitespace (empty divs after ad removal)
- Skips gstack-ctrl indicator in sticky removal
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: disable action buttons when disconnected, no error spam
- setActionButtonsEnabled() toggles .disabled class on all cleanup/screenshot
buttons (both chat toolbar and inspector toolbar)
- Called with false in updateConnection when server URL is null
- Called with true when connection established
- runCleanup/runScreenshot silently return when disconnected instead of
showing 'Not connected' error notifications
- CSS .disabled style: pointer-events:none, opacity:0.3, cursor:not-allowed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: cleanup heuristics, button disabled state, overlay selectors
17 new tests:
- cleanup defaults to --all on empty args
- CLEANUP_SELECTORS overlays category (paywall, newsletter, interstitial)
- Major ad networks in selectors (doubleclick, taboola, criteo, etc.)
- Major consent frameworks (OneTrust, Cookiebot, TrustArc, Quantcast)
- !important override for inline styles
- Scroll unlock (body overflow:hidden)
- Blur removal (paywall content blur)
- Article truncation removal (max-height)
- Empty placeholder collapse
- gstack-ctrl indicator skip in sticky cleanup
- setActionButtonsEnabled function
- Buttons disabled when disconnected
- No error spam from cleanup/screenshot when disconnected
- CSS disabled styles for action buttons
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: LLM-based page cleanup — agent analyzes page semantically
Instead of brittle CSS selectors, the cleanup button now sends a prompt to
the sidebar agent (which IS an LLM). The agent:
1. Runs deterministic $B cleanup --all as a quick first pass
2. Takes a snapshot to see what's left
3. Analyzes the page semantically to identify remaining clutter
4. Removes elements intelligently, preserving site branding
This means cleanup works correctly on any site without site-specific selectors.
The LLM understands that "Your Daily Puzzles" is clutter, "ADVERTISEMENT" is
junk, but the SF Chronicle masthead should stay.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: aggressive cleanup heuristics + preserve top nav bar
Deterministic cleanup improvements (used as first pass before LLM analysis):
- New 'clutter' category: audio players, podcast widgets, sidebar puzzles/games,
recirculation widgets (taboola, outbrain, nativo), cross-promotion banners
- Text-content detection: removes "ADVERTISEMENT", "Article continues below",
"Sponsored", "Paid content" labels and their parent wrappers
- Sticky fix: preserves the topmost full-width element near viewport top (site
nav bar) instead of hiding all sticky/fixed elements. Sorts by vertical
position, preserves the first one that spans >80% viewport width.
Tests: clutter category, ad label removal, nav bar preservation logic.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: LLM-based cleanup architecture, deterministic heuristics, sticky nav
22 new tests covering:
- Cleanup button uses /sidebar-command (agent) not /command (deterministic)
- Cleanup prompt includes deterministic first pass + agent snapshot analysis
- Cleanup prompt lists specific clutter categories for agent guidance
- Cleanup prompt preserves site identity (masthead, headline, body, byline)
- Cleanup prompt instructs scroll unlock and $B eval removal
- Loading state management (async agent, setTimeout)
- Deterministic clutter: audio/podcast, games/puzzles, recirculation
- Ad label text patterns (ADVERTISEMENT, Sponsored, Article continues)
- Ad label parent wrapper hiding for small containers
- Sticky nav preservation (sort by position, first full-width near top)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: prevent repeat chat message rendering on reconnect/replay
Root cause: server persists chat to disk (chat.jsonl) and replays on restart.
Client had no dedup, so every reconnect re-rendered the entire history.
Messages from an old HN session would repeat endlessly on the SF Chronicle tab.
Fix: renderedEntryIds Set tracks which entry IDs have been rendered. addChatEntry
skips entries already in the set. Entries without an id (local notifications)
bypass the check. Clear chat resets the set.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: agent stops when done, no focus stealing, opus for prompt injection safety
Three fixes for sidebar agent UX:
- System prompt: "Be CONCISE. STOP as soon as the task is done. Do NOT keep
exploring or doing bonus work." Prevents agent from endlessly taking
screenshots and highlighting elements after answering the question.
- switchTab(id, opts): new bringToFront option. Internal tab pinning
(BROWSE_TAB) uses bringToFront: false so agent commands never steal
window focus from the user's active app.
- Keep opus model (not sonnet) for prompt injection resistance on untrusted
web pages. Remove Write from allowedTools (agent only needs Bash for $B).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: agent conciseness, focus stealing, opus model, switchTab opts
Tests for the three UX fixes:
- System prompt contains STOP/CONCISE/Do NOT keep exploring
- sidebar agent uses opus (not sonnet) for prompt injection resistance
- switchTab has bringToFront option, defaults to true (opt-out)
- handleCommand tab pinning uses bringToFront: false (no focus steal)
- Updated stale tests: switchTab signature, allowedTools excludes Write,
narration -> conciseness, tab pinning restore calls
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: sidebar CSS interaction E2E — HN comment highlight round-trip
New E2E test (periodic tier, ~$2/run) that exercises the full sidebar
agent pipeline with CSS interaction:
1. Agent navigates to Hacker News
2. Clicks into the top story's comments
3. Reads comments and identifies the most insightful one
4. Highlights it with a 4px solid orange outline via style injection
Tests: navigation, snapshot, text reading, LLM judgment, CSS modification.
Requires real browser + real Claude (ANTHROPIC_API_KEY).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: sidebar CSS E2E test — correct idle timeout (ms not s), pipe stdio
Root cause of test failure: BROWSE_IDLE_TIMEOUT is in milliseconds, not
seconds. '600' = 0.6 seconds, server died immediately after health check.
Fixed to '600000' (10 minutes).
Also: use 'pipe' stdio instead of file descriptors (closing fds kills child
on macOS/bun), catch ConnectionRefused on poll retry, 4 min poll timeout
for the multi-step opus task.
Test passes: agent navigates to HN, reads comments, identifies most
insightful one, highlights it with orange CSS, stops. 114s, $0.00.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: force comparison board as default variant chooser
The comparison board ($D compare --serve) was being skipped in favor of
showing variants inline + AskUserQuestion "which do you prefer?" — a
degraded experience missing rating controls, comments, and remix buttons.
Changes:
- Replace "show inline" instruction with "do NOT show inline, proceed to
comparison board" in plan-design-review/SKILL.md.tmpl
- Add CRITICAL RULE: never use AskUserQuestion as the variant chooser
- Change DESIGN_SHOTGUN_LOOP resolver to AskUserQuestion-first wait with
polling fallback (affects all 3 consumer skills)
- Fix board URL from /design-board.html (404) to / (correct)
- Improve serve-failure fallback to show variants inline via Read tool
* chore: bump version and changelog (v0.14.1.0)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: /design-html skill — Pretext-native HTML from approved mockups
New skill that takes approved design-shotgun mockups and generates
production-quality HTML with Pretext for computed text layout. Text
reflows on resize, heights adjust to content, zero hardcoded CSS.
Includes vendored Pretext bundle (30KB), smart API routing per design
type, AskUserQuestion refinement loop, framework detection, and
3-viewport verification screenshots.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: integrate /design-html into design skill pipeline
- design-shotgun: Step 6 option B now chains to /design-html
- design-consultation: suggests /design-html after shipping DESIGN.md
(conditional on screen-level output, not tokens-only)
- plan-design-review: expanded chaining to include /design-shotgun
and /design-html alongside review skills
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: update plan-design-review chaining test for design skills
plan-design-review now chains to /design-shotgun and /design-html
in addition to review skills. Update the assertion to match.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add gstack keyword to design-html description for validation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: bump version and changelog (v0.14.0.0)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Deterministic cleanup improvements (used as first pass before LLM analysis):
- New 'clutter' category: audio players, podcast widgets, sidebar puzzles/games,
recirculation widgets (taboola, outbrain, nativo), cross-promotion banners
- Text-content detection: removes "ADVERTISEMENT", "Article continues below",
"Sponsored", "Paid content" labels and their parent wrappers
- Sticky fix: preserves the topmost full-width element near viewport top (site
nav bar) instead of hiding all sticky/fixed elements. Sorts by vertical
position, preserves the first one that spans >80% viewport width.
Tests: clutter category, ad label removal, nav bar preservation logic.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of brittle CSS selectors, the cleanup button now sends a prompt to
the sidebar agent (which IS an LLM). The agent:
1. Runs deterministic $B cleanup --all as a quick first pass
2. Takes a snapshot to see what's left
3. Analyzes the page semantically to identify remaining clutter
4. Removes elements intelligently, preserving site branding
This means cleanup works correctly on any site without site-specific selectors.
The LLM understands that "Your Daily Puzzles" is clutter, "ADVERTISEMENT" is
junk, but the SF Chronicle masthead should stay.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- setActionButtonsEnabled() toggles .disabled class on all cleanup/screenshot
buttons (both chat toolbar and inspector toolbar)
- Called with false in updateConnection when server URL is null
- Called with true when connection established
- runCleanup/runScreenshot silently return when disconnected instead of
showing 'Not connected' error notifications
- CSS .disabled style: pointer-events:none, opacity:0.3, cursor:not-allowed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: rotating founder resources in /office-hours closing
Add Beat 3.5 with 34 curated resources (5 Garry Tan videos, 2 YC Backstory,
9 Lightcone Podcast, 8 Startup School, 10 PG essays) that rotate contextually
each session. Includes dedup log to avoid repeats, analytics logging, and
browser-open offers. Also adds chmod +x safety net to build script.
* chore: bump version and changelog (v0.13.10.0)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>