Skill 安装
多 Agent 安装npx skills add https://github.com/browserbase/skills --skill what-antibot
使用 npx skills 快速安装,支持将 Skill 添加到多个兼容的 AI Agent。
Skill 调研报告
browserbase/skills#what-antibot
Skill 更新日志
版本列表
无版本号
[codex] Add source-integrated WebMCP skill (#155)
* [codex] Add source-integrated WebMCP skill
* fix: address review comments (autofix loop #1)
Addresses feedback from Cursor Bugbot and Socket Security
* feat: add adversarial verification step and default local runs to headed
Step 5 proves a tool is discoverable and that its executor ran, not that it
did what it claimed. Add step 6: probe schema closure, required fields, types
and constraints, error honesty, annotation honesty, clean rejection, and
consequential gating against the live page, then verify the effect in the
application rather than trusting the return value.
Also:
- Default --local validation to headed so the browser is visible; add
--headless for CI and unattended runs.
- Record that document.modelContext is the live accessor while
navigator.modelContext is undefined, so the fallback is load-bearing.
- Record that registerTool is idempotent by name with no unregister handle,
which is what makes remount and hot reload safe.
- Require handlers to read results back from application state instead of
echoing the request, including polling asynchronous stores until they settle.
* fix: run the scanner when invoked through a symlinked skill path
The self-invocation guard compared path.resolve(process.argv[1]) against
fileURLToPath(import.meta.url). path.resolve normalizes . and .. but does not
resolve symlinks, so when the skill is installed as a link the two paths never
match, main() never runs, and the scanner prints nothing and exits 0.
That is the default install shape for a Claude Code user, whose skill directory
is a symlink to the real one, and step 1 points them at exactly that path.
Resolve symlinks on both sides before comparing.
Also tell the skill to report what each step found as it goes, rather than
saving everything for the final report.
* docs: validate against localhost rather than 127.0.0.1
Dev servers commonly bind localhost only, so validating against
http://127.0.0.1:PORT discovers zero tools while http://localhost:PORT works.
The examples recommended the address that fails, and the symptom is
indistinguishable from tools never registering.
Point the examples at localhost and say what zero discovered tools usually
means.
无版本号
[codex] Add Optimize Agent Prompt skill (#146)
* Add Agent API Autobrowse skill
* Rename skill to optimize agent prompt
* Fix optimize prompt run artifact handling
无版本号
Add `browse upload` to browser skill docs (#62)
Document the new `browse upload ` command for
uploading files to elements. Works with CSS
selectors, XPath, and ref-based selectors from snapshot. Supports
single and multiple files, local and remote Browserbase sessions.
Co-authored-by: Claude Opus 4.6 (1M context)
无版本号
Add `browse get markdown` to browser skill docs (#61)
Document the new `browse get markdown [selector]` command that converts
page HTML to clean markdown. Defaults to body content, supports optional
selector for specific elements.
Co-authored-by: Claude Opus 4.6 (1M context)
无版本号
Resolve CDP tailing docs conflicts (#60)
无版本号
chore: remove deprecated browserbase-cli skill (#142)
The browserbase-cli skill describes the deprecated `bb` CLI
(@browserbasehq/cli), which has 0 tracked installs. The canonical
Browserbase CLI is now `browse`. Remove it from the marketplace catalog
so it no longer ships in the plugin marketplace.
- Delete skills/browserbase-cli/ (SKILL.md, REFERENCE.md, LICENSE.txt)
- Remove the browserbase-cli plugin entry from
.claude-plugin/marketplace.json
- Remove the browserbase-cli row from the README skills table
Note: the stale `@browserbasehq/browse-cli` install string called out in
the task is already absent on main (fixed in #111); skills/browser/SKILL.md
already uses `npm install -g browse`. No install-command change was needed.
Co-authored-by: Claude Opus 4.8 (1M context)
无版本号
harden(autobrowse): restrict trace artifacts to owner-only permissions (#139)
Autobrowse trace artifacts (trace.json, messages.json, summary.md, and
screenshots) can contain cookies, auth headers, bearer tokens, passwords
from form POSTs, private URLs, and screenshots of authenticated pages.
Previously these were written with fs.writeFileSync / fs.mkdirSync using
default permissions, which inherit the process umask (typically 0644 files
/ 0755 dirs). On a shared host -- CI runners, shared dev boxes, or
multi-tenant containers -- that leaves the trace tree readable by other
local users and processes.
This restricts trace artifacts to the owner:
- run dir + screenshots/ created with mode 0700; the run dir is also
chmod'd explicitly since mkdirSync's mode only applies to dirs it creates
(a pre-existing parent could otherwise stay traversable).
- trace.json / messages.json / summary.md written with mode 0600.
- lockDownTrace() sweeps the run dir at the end to cover files written by
subprocesses (screenshots from the browse CLI, .o11y artifacts) whose
creation mode we don't control. Symlinks (the `latest` link) are skipped.
0700/0600 are unaffected by the process umask, so the result is consistent
regardless of host configuration. The owner -- the same user the
self-improvement loop runs as -- retains full read/write, so the loop is
unaffected; only cross-user access is removed.
Co-authored-by: Claude Opus 4.8 (1M context)
无版本号
Add browser-use-to-stagehand skill: migrate browser-use to Stagehand on Browserbase (#130)
* Add bu-to-bb skill: migrate browser-use to Stagehand on Browserbase
Converts browser-use (Python) automation to Stagehand v3 (TypeScript) on
Browserbase, choosing the right level of determinism per step rather than a
one-to-one agentic copy.
- SKILL.md: detect the browser-use variant, decompose the task across the
determinism spectrum, emit Stagehand v3 + a migration summary
- references/: full API mapping, determinism decision framework, and an
optional trace-assisted path (pairs with the browser-trace skill)
- GUIDE.md: human migration guide (philosophy, feature mapping, determinism)
- PROMPT.md: tool-agnostic docs prompt (works in any AI assistant)
- EXAMPLES.md: before/after pairs
- README.md (skill): index; root README.md: add to the skills table
Targets Stagehand v3 (verified against live docs); validated by running the
skill on fresh scripts a clean agent had never seen.
Co-Authored-By: Claude Opus 4.8
* Remove Playwright references; full browser-use → Stagehand conversion
Express determinism purely through Stagehand primitives — cached observe()→act()
plus selfHeal/cacheDir/serverCache — instead of falling back to Playwright
selectors. Drops all "Playwright" references and page.locator()/page.fill()
calls across SKILL.md, GUIDE.md, PROMPT.md, README.md, and the determinism /
trace-assisted references. page.goto/page.url stay (Stagehand's own page methods).
Co-Authored-By: Claude Opus 4.8
* Address review (PR #130): add LICENSE.txt, fold docs into references/
Per @shrey150's standards pass:
- add LICENSE.txt (MIT, verbatim from skills/browser)
- move GUIDE.md -> references/guide.md and PROMPT.md -> references/prompt.md,
both linked from SKILL.md so the content isn't stranded
- drop the skill-local README.md
- fix relative links in references/guide.md and EXAMPLES.md
Skill dir now follows the convention: SKILL.md + EXAMPLES.md + references/ + LICENSE.txt.
Co-Authored-By: Claude Opus 4.8
* Fix runtime API bugs + add doc-resilience (validated via live eval)
Validated by converting 9 real browser-use examples through the skill and
running the generated Stagehand on live Browserbase. Fixes 3 runtime failures
that typecheck clean but broke at runtime:
- Page settling: networkidle -> domcontentloaded. networkidle never fires on
Google/analytics/SPA pages and throws a 15s timeout (broke 2 of 9 cases).
- Agent output schema: agent().execute({ output }) throws
ExperimentalNotConfiguredError unless experimental:true, and output must be a
zod object (not a top-level array). Document both, and recommend the
agent-then-extract pattern to stay on the managed API path (broke 1 case).
- setViewportSize takes positional (width, height), not Playwright's
{ width, height } object (would not compile).
- Variant table: ChatBrowserUse is browser-use's hosted-model class and appears
in stable 0.13.x code -- it is NOT a Rust-beta tell. Only a literal
browser_use.beta import is. Prevents variant mis-detection.
- Add an "iterate an extracted list / resolve relative URLs" pattern
(new URL(href, page.url())) to avoid "Cannot navigate to invalid URL".
Doc-resilience (keep the API tables, demote them): add a version-provenance
header, state that the live docs supersede this snapshot on any conflict, and
make "verify signatures against the installed package" an explicit workflow
step -- so the skill fails safe as Stagehand / browser-use drift.
Validated against @browserbasehq/stagehand 3.6.0 / browser-use 0.13.1.
Eval: 9/9 compile; live Browserbase 3 PASS/3 PARTIAL/3 FAIL -> 6 PASS/2 PARTIAL/0 FAIL.
Co-Authored-By: Claude Opus 4.8 (1M context)
* Rename skill bu-to-bb -> browser-use-to-stagehand
Cryptic abbreviation -> descriptive, discoverable name:
- "bu-to-bb" was opaque, and "bb" collided with the (now-deprecated) bb CLI.
- "bu" is also a loaded token in browser-use land -- it's the prefix for their
hosted models (bu-2-0, bu-30b), not the library -- so "bu-to-*" misreads.
- The name is loaded into the system prompt as discovery metadata, so a name
carrying "browser-use" + "stagehand" aids triggering, not just human scanning.
- Matches the repo's descriptive naming convention (competitor-analysis,
browser-trace, cookie-sync).
Renames the directory, frontmatter name, SKILL.md heading, README row, and all
internal /bu-to-bb references. Validator passes (node scripts/validate-skills.mjs).
Co-Authored-By: Claude Opus 4.8 (1M context)
* Add agent-tools + MCP mappings; real-world coverage (validated via tier-2 eval)
Patches the holes a tier-2 eval found by converting real-world browser-use code
(custom agent-tools, MCP client/server, and BU embedded in real app code:
LangManus, BLAST) through the skill and type-checking against installed 3.6.0.
Correctness:
- Agent custom `tools` require `experimental: true` (same gate as agent `output`
and MCP `integrations`) -- the prior tools example omitted it and would throw
ExperimentalNotConfiguredError at runtime. Consolidated callout added.
- `ai` must be pinned to v5: Stagehand 3.6.x types `tools` as the v5 ToolSet
(schema field `inputSchema`); the v4 `tool()` helper emits `parameters` and
fails to type-check. Template now pins `^5.0.0` and documents the plain-object
`{ description, inputSchema, execute }` alternative.
New mappings:
- §3.7 MCP: browser-use MCPClient (stdio + remote) -> Stagehand
`agent({ integrations })` via `connectToMCPServer(...)` / URL string, with the
experimental requirement and the tool_filter/prefix + server-direction gaps.
- §3.8 Real-world patterns: embedded/wrapped code (convert the browser-use
surface, keep app glue), sync-over-async -> async, long-lived stateful
executors -> stateless `messages`, vision intent -> `mode: hybrid/cua`, legacy
`result.final_result` attribute form.
- Custom-action gaps: injected special params (browser_session etc.) -> closures,
`domains=` -> in-tool host check, `terminates_sequence` -> no equivalent.
SKILL.md: added a "when NOT to migrate" scope gate (MCP-server / non-Agent /
embedded) so the workflow doesn't assume every input is a convertible script.
Tier-2: 5/5 handled correctly (4 convert+compile clean; MCP-server correctly
flagged out-of-scope without fabricating). Validated against stagehand 3.6.0 /
browser-use 0.13.1.
Co-Authored-By: Claude Opus 4.8 (1M context)
* Note Stagehand 3.6.0 stdio-MCP runtime bug in §3.7
Live e2e smoke of the converted MCP-client script surfaced a Stagehand bug:
passing a Client instance (local/stdio server via connectToMCPServer) into
agent({ integrations }) throws "Converting circular structure to JSON" before
the agent runs -- agent() does JSON.stringify(options.integrations) (v3.ts:1992)
and the Client object is circular. Reproduced with two different MCP servers
(filesystem, everything). URL-string integrations are unaffected. The skill's
mapping is correct (it connects to the server); this is a framework bug, so §3.7
now flags it and recommends remote/URL MCP until fixed upstream.
Co-Authored-By: Claude Opus 4.8 (1M context)
* Address Bugbot: experimental flag in prompt/determinism, complete login example
- prompt.md + determinism.md recommended agent().execute({ output }) (and prompt.md
the agent `tools` path) without the experimental:true requirement that api-mapping
already documents -> would throw ExperimentalNotConfiguredError. Added the
experimental gate (output/tools/integrations) + prefer agent-then-extract, and
pinned ai@^5 for the tools row.
- EXAMPLES.md login: the browser-use task is "log in then open the dashboard" but
the Stagehand after-script stopped at the allow-list check. Added the dashboard
step so the migration completes the same task.
Co-Authored-By: Claude Opus 4.8 (1M context)
* Address Bugbot: align prompt.md determinism levels with canonical scale
prompt.md numbered the spectrum 1=Navigation..5=Autonomous, inverted vs
determinism.md/guide.md/workflow (1=Autonomous..5=Navigation), so "Level N"
citations could contradict across docs. Flipped prompt.md's table + the
"decomposition (levels 2-5)" rule + the example's page.goto (L5) cite to match
the canonical scale.
Co-Authored-By: Claude Opus 4.8 (1M context)
* Address Bugbot: §3.5 saveUrl tool reads URL from page, not the model
The agent-tool example took `url` from the model via inputSchema, but the
original browser-use action reads the current URL from browser_session. That
let the model pass a guessed URL and contradicted the section's own "close over
page/stagehand in execute" guidance. The tool now takes no model args and reads
page.url() directly.
Co-Authored-By: Claude Opus 4.8 (1M context)
* Address Bugbot: enforce allow-list after every navigation in login example
The host check ran once after sign-in, but the added dashboard step navigated
past it — and browser-use's allowed_domains enforces across the whole run.
Extracted the check into assertAllowedHost() and call it after each navigation,
with a comment that even this is best-effort (real continuous enforcement =
Browserbase proxy domain rules, api-mapping §5).
Co-Authored-By: Claude Opus 4.8 (1M context)
---------
Co-authored-by: Chris Browder
Co-authored-by: Claude Opus 4.8
Co-authored-by: Shrey Pandya
无版本号
Competitor analysis skill (#76)
* feat: add competitor-analysis skill
* fix(competitor-analysis): harden merge_partials against subagent format drift
* feat(competitor-analysis): add mandatory Step 4.5 — confirm enrichment set with user
The gate has known blind spots that silently drop real competitors:
- JS-heavy homepages (Tavily, Firecrawl) — bb fetch returns near-empty
hero text, keyword matcher has nothing to match on
- Cloudflare challenge pages (Perplexity) — title becomes "Just a
moment...", no category signal
- Semantic variants — "search foundation" (Jina AI), "retrieval
backbone", etc. don't lexically match a list centered on "search API"
- Apex-vs-product domain confusion — brave.com (browser) vs
api-dashboard.search.brave.com (actual API)
Auto-promoting the PASS list to enrichment is the wrong default because
enrichment is expensive (5 competitors × 5 lane-subagents = 25 subagents,
~10-15 min wall time, ~300 bb calls). Running that on a partly wrong set
wastes all of it.
Insert a mandatory Step 4.5 between Gate and Deep Enrichment:
1. Main agent groups /tmp/competitor_gated.jsonl into three buckets —
PASS, UNKNOWN (fetch failed — surfaced separately, these are the
silent misses), and rejected-brand-matches (top ~10 REJECTs whose
title contains a seed token or shows up in Wave C "X vs Y" graph).
2. AskUserQuestion with a checkbox list + free-text "add more" field.
3. Write the confirmed set to /tmp/competitor_enrichment_set.txt — this
is the input for Step 5, not /tmp/competitor_passed.txt.
Surfaced while testing the skill on Exa (exa.ai): the gate passed
22/101 candidates but silently rejected Tavily, Jina AI, Firecrawl, and
Perplexity — all real direct competitors. Step 4.5 catches them.
SKILL.md pipeline overview is now 8 steps (was 7). Step 5 input path is
updated. workflow.md gets a User-confirm phase section with the three
buckets and the list of known gate blind spots.
* fix(competitor-analysis): normalize mention-bullet format during merge
Lane subagents don't consistently emit the canonical Mentions bullet
format specified in workflow.md — they drift into variants per lane:
- Discussion lane: `- **HN** — [Title](url) — snippet`
- News lane: `- **2025-08-06** — [News] Outlet — "title" — url`
- Technical lane: `- **[Benchmark]** ...` (canonical)
compile_report.mjs' parseMentions only matches the canonical
`- **[SourceType]** Title | Snippet (source: URL, YYYY-MM-DD)` shape,
so non-canonical variants silently dropped from the mentions feed.
On the Exa end-to-end test, merge reported 404 total mentions across
5 competitors but the rendered feed showed 0.
Rather than fight prompt drift across 25 subagents, normalize at merge
time. New normalizeMentionBullet() rewrites the three observed variants
into canonical form before dedup, so downstream (CSV, per-competitor
pages, mentions.html feed) stays clean.
After fix on the Exa run: 294 mentions render, 81% with dates,
distribution: 42 LinkedIn / 34 HN / 25 Blog / 24 YouTube / 20 Reddit /
12 Comparison / 11 DevTo / 8 News / 4 Substack.
* refactor(competitor-analysis): drop pricing screenshot — hero only
Pricing-page screenshots were adding ~300KB per competitor (Browsaur's
was 580KB) and doubling the per-run browse-CLI cost, but the per-tier
text already lives in the frontmatter (pricing_tiers, pricing_model)
and renders in the Pricing section of the per-competitor page. The
visual didn't add signal over the structured data — it was redundant.
Homepage hero stays. That one is worth keeping: the tagline, visual
brand identity, and positioning screenshot-vs-text diff surface things
the fields can't (logo treatment, animation cues, hero copy voice).
Changes:
- capture_screenshots.mjs: drop pricingCandidates() + pricing capture loop,
simplify result shape to {slug, hero, errors}, halve per-competitor
wall time (~10-20s vs ~15-20s, no pricing fallback chain).
- compile_report.mjs: remove 2-column .shots grid + .shot-pricing CSS,
render single .shot-hero card per page.
- SKILL.md Step 6 + references/workflow.md: doc sync. Also clarify that
`browse` is a separate package from `bb` (@browserbasehq/browse-cli
vs @browserbasehq/cli) — came up as a user question during test runs.
Existing runs re-rendered without pricing shots; ~1.5MB of PNGs removed
from the two test output dirs on Desktop.
* feat(competitor-analysis): render matrix from curated matrix.json taxonomy
The old matrix view was broken on real runs. Subagents write key_features
and integrations as prose (comma-separated or full sentences), not as
pipe-separated atomic labels the matrix expected. Pipe-splitting gave one
unique blob per competitor, so the matrix trivially rendered a diagonal —
zero actual comparison across competitors.
Fix is to synthesize a shared taxonomy after enrichment and render the
matrix from that. New flow:
- After merge, the main agent reads all per-competitor .md files, distills
a canonical list of 12-20 atomic features and 10-20 integrations that
apply across the category, and writes {OUTPUT_DIR}/matrix.json with a
per-competitor yes/no mapping.
- compile_report.mjs auto-detects matrix.json and renders the Features +
Integrations axes from it. Falls back to the old pipe-split behavior
when matrix.json is missing.
Verified on the Exa test: before fix, Features axis was 5 one-off blobs
with a diagonal of ●s. After: 19 atomic feature rows × 5 competitors
with 36 ● cells showing real overlap (Web Search API, MCP server, Free
tier, Structured JSON are universal; only Jina has Reranker+Embeddings;
only Tavily has Site crawler; SerpAPI alone has CAPTCHA solving and
hourly throughput SLA; etc.).
SKILL.md Step 5 gets a new "Synthesize the comparison matrix" substep
with the matrix.json schema and the rule "do not skip — without this the
matrix view is trivially diagonal".
* fix(competitor-analysis): realign matrix column headers
Rotated competitor-name headers were misaligned with their data columns
and the rightmost labels (Tavily, You.com in the Exa test) got cut off.
Root cause: `transform-origin: left top` combined with a fixed
`width: 160px` on the label made each label's horizontal extent run
~131px to the right of the column, so labels visually floated several
columns to the right of their target and the last N labels overflowed
off-screen.
Fix: anchor the rotated label to the BOTTOM-RIGHT of its column cell
(position:absolute right:4px bottom:8px, transform-origin:right bottom)
and steepen rotation from -35° to -55° so horizontal extent is
reduced. Drop the fixed width — label is now only as wide as its text,
which shrinks short names (Jina AI, Serper) and tightens layout.
Cell width 44→52px and header height 130→150px give rotated labels
room to live inside the cell rather than overflowing.
Result on the Exa run: all 5 competitor names visible, each label's
bottom-right sits at the top-right of its column, leaning up-left
toward the column — the shingled "hanging label" pattern.
* fix(competitor-analysis): horizontal matrix column headers
Rotated/diagonal competitor names (35° then 55°) kept producing awkward
alignment: the rotation anchor vs the column's visual center never quite
matched, and long names (You.com, LlamaIndex) overflowed off the right.
Simpler fix: just make the headers horizontal. With 5 competitors × 110px
each = 550px, plus the 240px feature column, the table is 790px wide —
fits inside the 1200px container without scrolling. For >10 competitors
the .mx-scroll wrapper already provides horizontal scroll.
Drops the .mx-comp-h-inner rotation wrapper, bumps cell width from 52→110px
and data font from 0.9→0.95rem for readability. Feature column grows
220→240px to fit longer taxonomy labels like "Hourly throughput SLA".
* feat(competitor-analysis): add "Where you're winning / losing" on overview
The overview page showed a list of competitors but no explicit view of
the user's own strategic position. Hard to answer at a glance: what do
I uniquely have, what are the table-stakes features I'm missing?
Extend matrix.json with a `userCompany` entry (same shape as each
competitor — features + integrations yes/no flags), and compute two
buckets on the overview page:
- Winning: features the user has where 0–1 competitors also have
them. Ordered by rarity (unique features first).
- Losing: features the user lacks where 3+ competitors have them.
Ordered by gap size (most common features first).
Each item shows who else has it ("only you" / "Tavily, SerpAPI" /
"4 competitors"), so users can assess the strategic weight at a glance.
Rendered as two cards (green-bordered "win", brand-red-bordered "loss")
between the summary stats and the results table on index.html. Cards
gracefully degrade to nothing if matrix.json lacks userCompany — a
skill run that skipped Step 5b's matrix synthesis gets an overview
without the strategic summary rather than an error.
On the Exa test: 4 wins (Site crawler · Embeddings · 3+ SDK languages
· CrewAI integration) and 3 losses (Image/visual search · Dedicated
news endpoint · Hourly throughput SLA). Clear strategic picture in
one screen.
SKILL.md Step 5b "Synthesize the comparison matrix" now documents
userCompany as a required field with the explicit note that without it
the strategic summary doesn't render.
* docs(competitor-analysis): require userCompany in matrix.json schema
Missed in the strategic-summary commit (652a9a4) — the SKILL.md block
that defines the matrix.json schema was on an older revision of the
file that didn't get re-staged. Re-add the userCompany field and flag
it as required, with the explicit note that skipping it means the
"Where you're winning / losing" cards don't render.
* feat(competitor-analysis): mandate fact-check subagent for matrix.json
Step 5b (matrix synthesis) produces LLM inference from heterogeneous
subagent prose. On the Browserbase run 2026-04-23 that inference
confidently marked SOC 2 as a Browserbase moat — except Hyperbrowser,
Kernel, AND Anchor Browser all have SOC 2 Type II (verified via their
own trust portals and compliance blog posts). Shipping that to a GTM
team would have made the whole report un-trustable.
Add Step 5c: a mandatory fact-check subagent that runs after the
taxonomy synthesis and before compile. For every true/false cell in
matrix.json, it:
- If true: finds a concrete source URL (docs, trust portal, changelog,
GitHub license) or flips to false.
- If false: runs one targeted bb search to guard against misses.
- Outputs a verified matrix.json with a per-cell `sources` field plus
a matrix_fact_check.md delta log of every flip.
The "Where you're winning / losing" cards are strategic claims. Without
verification they hallucinate moats. The SKILL now labels this step
MANDATORY with the Browserbase-SOC 2 example as proof of what skipping
it costs.
* feat(competitor-analysis): prose summaries for win/loss cards
Bulleted lists of winning/losing features read like a spreadsheet, not
the analyst-briefing the overview page is supposed to be. Extend
matrix.json's userCompany with optional winningSummary / losingSummary
prose fields (2-4 sentences each) and render them as paragraphs when
present. Falls back to the existing bulleted list when absent so a
partial run still shows the boolean comparison.
SKILL.md flags these as strongly preferred and tells the main agent to
write them AFTER the fact-check step so the prose is grounded in
verified cells — otherwise the paragraph will state fluent but false
moats. Updated the Exa example in the schema block to include the two
summary fields.
On the Browserbase run: two paragraphs replace the previous 12 bullets.
Winning reads as enterprise moats (SLA, Stagehand, EU/APAC, Selenium,
OpenAI Agents + n8n integrations). Losing reads as transparency +
openness gaps — concrete competitor names cited (Anchor's Halluminate
win, Steel's leaderboard, Browsaur MIT + Kernel + Steel AGPL).
* fix(competitor-analysis): mandate user-company research parity with competitors
Step 1 was doing light self-research on the user's company while Step 5
did deep 5-lane enrichment on every competitor. That asymmetry meant the
userCompany row in matrix.json was filled from the main agent's memory
rather than from verified partials, and the strategic summary printed
fabricated moats about the user's OWN product.
Concrete examples from the Browserbase run 2026-04-23, caught only when
the user pushed back:
- Claimed a "published uptime SLA" — no numeric SLA exists on
browserbase.com, only a status page.
- Marked open-source as false — Stagehand is MIT-licensed at
github.com/browserbase/stagehand, plus Browserbase ships 10+ other OSS
repos (sdk-node, sdk-python, create-browser-app, Arena, open-operator,
mcp-server-browserbase, etc). The correct framing is "OSS at the SDK
layer, cloud-only at the infra layer" — a split the skill wasn't
capturing.
Systemic fix:
- Step 1 now mandates the same 5-lane partial enrichment on the user's
company that Step 5 runs on competitors. Partials go to
partials/{user-slug}.{lane}.md. merge_partials.mjs consolidates to
{OUTPUT_DIR}/{user-slug}.md.
- Step 5b (matrix synthesis) now explicitly reads {user-slug}.md as
the source for userCompany flags. Every flag must be traceable to
a Research Findings bullet with a cited URL — the rule applies
identically to the user's company and every competitor.
- Added the Browserbase-SLA + Browserbase-Stagehand errors to SKILL.md
as the cautionary tale for why this parity matters.
* feat(competitor-analysis): battle card lane (6th synthesis subagent)
Adds sales-enablement output grounded in the fact-checked matrix.
Closes the single biggest gap surfaced by the v0.2 framework research:
the skill was a Competitor Profiling Matrix but not a Battle Card tool
— Klue/Crayon's most-requested artifact had no equivalent in our pipe.
Design: synthesis-only lane (no new bb calls), runs AFTER Step 5c
fact-check so battle cards are grounded in verified cells, not fresh
inference. Eliminates the failure mode where the skill's sales output
would contradict the matrix it publishes.
Changes:
- scripts/merge_partials.mjs: add 'battle' to LANES; union the
`## Battle Card` section into the merged {slug}.md between the
Comparison and Mentions sections.
- scripts/compile_report.mjs: parse the Battle Card section from
c.sections, render as a brand-accented `.research.battle` card on
the per-competitor HTML page (left border in brand orange,
uppercase small-caps subheadings for Landmines / Objection Handlers
/ Talk Tracks).
- references/battle-card.md (new): format spec — three sections,
citation rules, adversarial self-check checklist.
- references/battle-card-subagent.md (new): standalone prompt template
with placeholder list. Main agent substitutes per competitor and
launches one subagent per competitor in parallel.
- references/example-research.md: add a worked Battle Card section to
the Rival Co example.
- SKILL.md: new Step 5d (Battle synthesis) with explicit dependency
on Step 5c fact-check; Pipeline Overview updated to mention the
6th lane in deep/deeper modes.
Scope deliberately tight — this is Phase E of the approved v0.2 plan
(/Users/jay/.claude/plans/you-can-figure-out-jaunty-pelican.md).
Phases A/B/C/D/F/G deferred. Existing Browserbase + Exa compile runs
verified unchanged (no battle partials → battle card card omitted).
* fix(competitor-analysis): accept battle-lane format drift at merge time
First end-to-end run of the battle lane on Browserbase data: 4 of 5
subagents emitted their Battle Card content with format drift that
parseSections() couldn't resolve — some led with `# Battle Card: X vs Y`
(h1, not h2 and so invisible to the `## `-only section splitter), some
skipped the wrapper heading entirely and led with `## 1. Landmines`.
Only 1 of 5 battle cards made it into the merged {slug}.md.
Same root cause as the earlier mention-bullet-format fix (commit 953f078):
subagents will drift from any prompt-level format spec.
Treat the entire battle partial body as the Battle Card content
regardless of heading style. Strip any leading `# Battle Card …` h1 or
`## Battle Card` h2 wrapper so we don't double-wrap, then emit the rest
under our canonical `## Battle Card` heading in the merged file.
After the fix: 5 of 5 battle cards rendered in per-competitor HTML on the
Browserbase run, each with 5-6 cited landmines, 5 objection handlers with
source links, and 2-3 talk tracks. Content quality spot-checked on Anchor
(counters the Halluminate stealth benchmark loss with the Advanced Stealth
update link) and Steel (flags US-only regions + self-benchmark bias).
* fix(competitor-analysis): harden merge + capture scripts on fresh run
Second end-to-end run on Browserbase (2026-04-24-1955) exposed two
small-but-real bugs not caught on the 2026-04-23 run:
1) merge_partials.mjs — the Battle Card heading-stripper's regex
required the first line to be exactly `## Battle Card\s*\n` or
`# Battle Card[^\n]*\n`, so an h2-with-suffix line like
`## Battle Card — Hyperbrowser` slipped through. The merged
hyperbrowser.md got a duplicate `## Battle Card` heading and the
HTML rendered the section twice.
Generalize to strip any leading heading line (h1-h3) mentioning
"Battle Card" with any suffix. One regex handles all observed
drift patterns from the 5 subagents.
2) capture_screenshots.mjs — the --help template literal contained
unescaped backticks around `website`, breaking the enclosing
`\`...\`` literal and yielding a SyntaxError at load time. Never
caught before because prior runs skipped --help. Replaced the
inner backticks with double quotes.
Verified on the fresh Browserbase run: all 5 battle cards merge with
exactly one `## Battle Card` header each; 5/5 hero screenshots captured
(anchor / browserbase / hyperbrowser / kernel / steel).
* fix(competitor-analysis): tighten overview table — exclude user, truncate cells
Three concrete bugs on the refreshed Browserbase run:
1) The user's own company leaked into the competitor table as the
first row with '—' pricing. Filter it out by matching
competitor_name AND slug (case-insensitive) against
matrix.json userCompany.name (falling back to --user-company).
Also rebuild metaLine + {{TOTAL}} + mentions-header count off the
filtered list so "N competitors" is accurate.
2) Overview table cells rendered full 650-char pricing_tiers strings
when subagents drifted into prose instead of pipe-separated tiers.
Add truncate() helper (~140-160ch with word-boundary ellipsis) on
tagline, pricing, and strategic_diff cells.
3) featurePills dropped all pills when key_features had no pipes —
because splitPipes returned a single giant blob. Fall back to
splitting on semicolons/commas, and cap each pill to 40 chars
with a word-boundary ellipsis. Prevents wall-of-text pills.
Also lifted the curatedMatrix load above the first use site to avoid
a temporal dead zone (the filter needs userCompany.name; the matrix
was previously loaded farther down for the renderer functions).
After fix: 5-row table instead of 6, pricing/tagline/diff cells fit
the intended max-widths, feature pills show as short capsules.
* fix(competitor-analysis): mentions feed — alias frontmatter + normalize pills
Two bugs on the fresh Browserbase run that both traced to subagent
format drift:
1) Browsaur missing entirely from mentions feed chips
Root cause: browsaur.marketing.md wrote `competitor: Browsaur`
instead of canonical `competitor_name: Browsaur`. merge_partials'
CANONICAL_FIELDS whitelist dropped the field silently, leaving
Browsaur's merged .md with an empty competitor_name. The overview
table still rendered (by slug) but the mentions feed keys on
competitor name for the chip label — blank chips filtered out.
Fix: FIELD_ALIASES map in merge_partials — `competitor` and
`name` and `company` all map to `competitor_name`; `homepage` and
`url` to `website`; `price_tiers` and `pricing` to `pricing_tiers`.
canonicalValue(fm, key) walks the alias table when the canonical
key is absent. Silent fallback: subagents can drift on field names
without us losing data.
2) Unstyled mention pills with invented source types
Subagents emitted `[VendorBlog]`, `[HackerNews]`, `[GitHubIssue]`,
`[CompetitorBlog]` — none matching the CSS classes. Rendered as
unstyled spans.
Fix: normalizeSourceType() in parseMentions. Canonical set
(Benchmark/Comparison/News/Reddit/HN/LinkedIn/YouTube/Review/
Podcast/X/DevTo/Hashnode/Substack/Blog) stays. Aliases map
HackerNews→HN, Twitter→X, VendorBlog/CompetitorBlog/GitHubIssue/
Medium/Docs→Blog. Unknown types keyword-scan for a canonical
token; else fall back to Blog. Guarantees every pill gets styled.
Also filter competitorRows (not deduped) when building allMentions, so
the user's own company doesn't leak into the feed even if it has
mentions. Fallback chip label is c.slug if competitor_name is blank.
After fix on the Browserbase run: 5 competitor chips (Anchor 21,
Browsaur 12, Hyperbrowser 22, Kernel 28, Steel 23), all source pills
mapped to canonical palette.
* perf(competitor-analysis): fix 25-min Step 5 wall-clock + skill-creator nits
A real 10-competitor run on Browserbase clocked 40 minutes and never
reached fact-check or screenshots before interrupt. Trace attribution:
Step 5 enrichment alone burned 25 minutes by self-throttling to 10
agents per message (5 sequential rounds of 10), when the Agent tool
happily runs 50+ in parallel. Wall clock collapses to the slowest
single agent (~5 min) once we stop batching.
Three classes of fix in this commit:
1. Parallelism guidance — workflow.md + SKILL.md
- Drop the "up to ~6 per message" cap. Replaced with the explicit
rule: launch ALL subagents needed for a phase in ONE Agent
message. For 10 × 5 lanes = 50 parallel agents in one message.
- Document the measured cost: splitting cost 20 minutes vs
unsplit on the Apr 2026 Browserbase run.
- Update Step 5 + Wave Management + the lane-fan-out section to
match. No remaining contradictions in the docs.
2. Discovery is parallel Bash, not subagents
Discovery is 6-12 `bb search` calls. Wrapping each wave in an
Agent subagent costs more in cold-start + tool-reasoning overhead
than the work itself (~1-2 min wasted). New "Discovery — parallel
Bash, not subagents" section in workflow.md gives the exact
3-Bash-call recipe (Wave A/B/C). SKILL.md Step 3 points at it.
3. Skill-creator audit nits (rules from the skill-creator skill)
- Add Tables of Contents to all 4 reference docs >100 lines
(workflow.md, research-patterns.md, example-research.md,
battle-card-subagent.md). battle-card.md is 91 lines so
skipped per the rule.
- Bump version "0.1.0" → "0.2.0". The skill picked up battle
cards (df62374), fact-check (8502f71), prose summaries
(9fd482f), user-company parity (845422d), matrix taxonomy
(c74d229), Step 4.5 user-confirm (ae58982), and 7 more fixes
since 0.1.0 — well past a minor bump.
- Kept `allowed-tools` frontmatter field. Not in skill-creator's
spec but harness-consumed in some Claude Code setups; harmless
if ignored, useful if respected.
Estimated next-run impact: 40 min → ~12-15 min through compile,
dominated by per-subagent ceiling (3-5 min) + matrix synthesis
(4 min) + fact-check (5-10 min if you want it).
* perf(competitor-analysis): spot-check fact-check by default, 25-call budget
The Apr 25 Browserbase run got stuck at 111+ bb tool calls in
fact-check before user interrupt. Root cause: the previous Step 5c
mandated verifying EVERY cell of matrix.json — for a 7-company ×
33-axis matrix that's 231 cells. Most of those cells are universal
table-stakes (Playwright, Puppeteer, CDP, Python SDK) where any cloud
browser has them; verifying all of those is redundant work that blocks
the pipeline from reaching battle cards / screenshots / compile.
The original problem fact-check was solving (the SOC 2 hallucination
on the Apr 23 run) was about a HANDFUL of high-stakes cells: the ones
that drive the "Where you're winning" summary, plus compliance + license
+ pricing. Rest doesn't need verification.
Switch the default to spot-check with a hard 25-call budget. Priority
order is explicit and ranked:
1. Every cell that appears in userCompany.winningSummary/losingSummary
2. Compliance cells (SOC 2, HIPAA, ISO 27001) across all competitors
3. Open-source license cells (Steel was wrong as AGPL — actually Apache 2.0)
4. Pricing tiers + funding numbers cited in summaries
Explicit skip list:
- Universal cells (Playwright, Puppeteer, CDP, Python SDK, etc.)
- `false` cells with no claim
- Integration cells unless cited in summaries
The subagent counts its own bb calls and STOPS at 25 — partial
fact-check beats blocking the pipeline. Full-sweep mode (~80 calls,
verifies every non-universal cell) is opt-in for board-deck-level
deliverables.
Estimated impact on next run: fact-check phase 15+ min → 3-5 min,
no more pipeline stalls before battle cards. The summary stays
trustworthy because we verify the cells that actually feed it.
* perf(competitor-analysis): hard-cap research lane tool calls + halve discovery results
User reported research phase still takes ~25min before fact-check even after
prior parallelism fixes. Trace showed 2 lanes hitting 29-30 bb calls each
against an 8-call advisory budget, dragging the 30-agent fan-out from 5→12min.
- references/workflow.md: replace soft "BUDGETS (respect strictly)" with
HARD CAP + per-call self-counter ("# bb call N/8") and explicit "stop and
write what you have" instruction. Cite Apr 25 incident.
- references/workflow.md: drop discovery searches from 25→12 results per
query. Gate already filters most noise; 25 just inflated the candidate
list and downstream gate calls.
- profiles/example.json: drop redundant template (browserbase.json is the
reference profile).
* fix(competitor-analysis): four Cursor Bugbot findings
1. matrix.html leaked user company as a column with all-false features.
Move competitorRows definition above aggregates and replace `deduped`
with `competitorRows` in matrix headers/cells, axis counts, pricing
table, strategic-summary inner loop, per-competitor page generation,
and CSV. Now a single filter applies consistently across all views.
2. report-template.html referenced undefined --high / --low CSS vars on
strategic win/loss card border-lefts (and the loss badge text color),
so the colored borders silently didn't render. Define both in :root
(high=#5a8a1a green, low=#F03603 brand) so they match the existing
palette tokens.
3. gate_candidates.mjs used spawnSync inside async gateOne, blocking the
event loop and reducing the documented --concurrency 6 to N=1 in
practice. Switch to promisified execFile so the worker pool actually
parallelizes.
4. extract_vs_names.mjs used bidirectional startsWith for domain resolution,
which mapped "steel" -> steelhead.com and "browse" -> browserbase.com.
Restrict prefix matches to known branding suffixes (browser/ai/io/app/
labs/etc.), break ties by shortest suffix, and exclude seeds from the
host map so the user's own domain can't shadow shorter extracted names.
5. capture_screenshots.mjs (also flagged): the underlying `browse` CLI
shares a single session, so true async parallelism would race on the
same tab. Clamp --concurrency to 1 with a stderr note rather than
silently corrupting output.
* bugbot fixes
* fix: bugbot complaints
* small fix
* fix(competitor-analysis): address PR review — remove PII, neutral example industry, browse CLI migration
- Remove PII: replace literal /Users/jay desktop path in battle-card-subagent
with a generic "absolute path to the output directory" description.
- Reframe all worked examples from Browserbase's own competitive set (Hyperbrowser,
Stagehand, Anchor, Kernel, Steel, Browserless) to a neutral AI-search-API industry
(Exa/Tavily/SerpAPI/Serper, fictional Rival Co). Kept legitimate Browserbase
branding/attribution and the Search API tool references.
- Migrate bb -> unified browse CLI: bb search -> browse cloud search,
bb fetch -> browse cloud fetch (--format raw where HTML is needed since the new
default is markdown), bb browse -> browse open --remote + browse get markdown,
package npm install -g browse, permission Bash(browse:*).
- Screenshots: drop programmatic `browse env`; select mode per-command via
--remote/--local and document it in SKILL.md Step 6. Use an isolated named session
(and stop it on exit) so capture never collides with the user's existing browse
session. Update screenshot flags to the unified surface (--path, --animations disabled).
Verified end-to-end against the live browse CLI: gate (Exa PASS / Tavily REJECT),
discovery dedup + vs-name extraction, and screenshot capture (valid PNG written).
Co-Authored-By: Claude Opus 4.8 (1M context)
* fix(competitor-analysis): address review + skills CI compliance
- Add LICENSE.txt (MIT, Browserbase, Inc.) and a README table row; trim the
SKILL.md description to <=1024 chars so validate-skills passes
- Remove stray skill-creator symlink (leaked local ../.agents path, broken
for everyone else)
- SKILL.md: re-run merge_partials after the Step 5d battle-card lane so the
cards actually land in the consolidated files / report; resolve the
winning/losing summary write-order contradiction (draft in 5b, finalize
after 5c fact-check)
- capture_screenshots.mjs: bail on a failed `browse open` (the reused session
was saving the previous competitor's page under the new slug); pass mode
flags to `browse stop` so remote Browserbase sessions actually tear down
- merge_partials.mjs: map a bare `pricing` key to pricing_model, not
pricing_tiers (prose/model strings were corrupting the structured tiers)
Co-Authored-By: Claude Opus 4.8 (1M context)
* fix(competitor-analysis): address second-pass Bugbot findings
- md_utils.mjs: tolerate CRLF in parseFrontmatter/parseBody/parseSections
(the `---` anchors assumed LF, so CRLF files were skipped or read bodyless)
- compile_report.mjs: normalize the user-company exclusion (strip trailing
corporate suffix + punctuation) so legal/DBA drift ("Exa, Inc." vs slug
`exa`) still filters out the user's own file; still exact normalized-equality,
so a real competitor is only dropped if its normalized name equals the user's
- SKILL.md: Step 0 note that re-runs must start from a clean $OUTPUT_DIR, since
compile_report ingests every {slug}.md and merge_partials never deletes slugs
dropped from a new enrichment set (stale competitors would leak into the report)
Co-Authored-By: Claude Opus 4.8 (1M context)
* fix(competitor-analysis): harden gate concurrency + discovery dedup
- gate_candidates.mjs: floor --concurrency at 1, so `--concurrency 0` or a
non-numeric value can't spawn zero workers and silently exit having gated
nothing (discovery would look empty with no error)
- list_urls.mjs: when deduping by hostname, prefer the shallowest path (site
root) over a deep link — the first search hit for a domain is often a
blog/doc/comparison path, and gating + enrichment want the homepage
Co-Authored-By: Claude Opus 4.8 (1M context)
* fix(competitor-analysis): floor screenshot concurrency at 1
`--concurrency 0` spawned zero workers (no screenshots captured, script still
exited successfully) and a non-numeric value threw on Array(NaN). Normalize to
at least 1 before the existing >1 clamp. Same bug class as the gate_candidates
concurrency fix.
Co-Authored-By: Claude Opus 4.8 (1M context)
* fix(competitor-analysis): hide strategic badge count when prose is shown
The "Where X is winning/losing" card badge always showed the boolean-heuristic
count (allWins/allLosses length), even when the card body rendered analyst prose
from matrix.json — the count can be 0 or unrelated to the prose, misstating the
card. Only render the badge when the boolean list is what's actually shown.
Co-Authored-By: Claude Opus 4.8 (1M context)
* fix(competitor-analysis): correct screenshot open-guard + stop teardown
Live e2e against browse v0.8.5 showed my two earlier capture_screenshots fixes
didn't hold:
- Failed-open guard: `browse open` exits 0 even on nav failure (it lands on
chrome-error://chromewebdata/), so the exit-status check never fired and a
junk error-page PNG was still saved. Now parse the open result's `url` and
bail on chrome-error:// / about:blank / empty. Verified: a .invalid URL is
now skipped (no PNG) while a good URL still captures.
- Teardown: `browse stop --remote` is rejected ("Nonexistent flag: --remote")
in v0.8.5 — my flag-passing change broke teardown and leaked the session.
Reverted to `browse stop -s `, which does stop a remote session.
Co-Authored-By: Claude Opus 4.8 (1M context)
---------
Co-authored-by: Claude Opus 4.8 (1M context)
Co-authored-by: Shrey Pandya
无版本号
Add webmcp gen skill (#133)
* wip
* wip
* Conform webmcp-gen frontmatter to house skill standards
- allowed-tools: space-separated list (matches ui-test style)
- compatibility: include install commands (pnpm install, npm install -g browse)
Co-Authored-By: Claude Fable 5
* fix(webmcp-gen): make validate.mjs fail on empty tools and empty errorText
Address Cursor Bugbot review on validate.mjs:
- Empty manifest tools array no longer reports "passed" (vacuous every()
+ 0===0 length check); now pushes an explicit error and requires >0 tools.
- A tool returning { success: false } with an empty errorText is now stored
as an error and fails validation (|| fallback instead of ?? on "").
Co-Authored-By: Claude Opus 4.8 (1M context)
* fix(webmcp-gen): reject path-traversal in scaffold slug segments
Address Cursor Bugbot: parseSlug accepted a ".." segment (e.g. "../task"),
and artifactDirFor resolves with path.resolve, so scaffold could write
outside the artifacts/ tree. Now each slug segment must match
^[A-Za-z0-9][A-Za-z0-9._-]*$, rejecting ".." and leading-dot segments.
Co-Authored-By: Claude Opus 4.8 (1M context)
* fix(webmcp-gen): guard validate.mjs against non-array manifest tools
Address Cursor Bugbot: a manifest with missing/null `tools` made
`manifest.tools.length` throw after the try/finally, crashing before
eval.json/eval-report.md were written. Now validate early-returns a
written "failed" report when `tools` is not an array.
Co-Authored-By: Claude Opus 4.8 (1M context)
* fix(webmcp-gen): cap scaffolded tool name to 80 chars, widen validate tool-list timeout
Address Cursor Bugbot:
- scaffold.mjs toolNameFor now slices to compile.mjs's 1-80 char limit so a
long slug can't scaffold a manifest that then fails compile.
- validate.mjs listWebMCPTools timeout 1000ms -> 5000ms so slow page loads
don't false-report tools as unregistered (example uses the default wait).
Co-Authored-By: Claude Opus 4.8 (1M context)
* feat(webmcp-gen): emit stagehand-example as .ts run via tsx
Match the create-browser-app / browserbase/templates convention (TS examples
run with tsx). The generator now writes stagehand-example.ts (no shebang);
package.json adds tsx/typescript/@types/node devDeps; SKILL.md documents
`npx tsx stagehand-example.ts` and points to `npx create-browser-app` for
turning the demo into a standalone project.
Co-Authored-By: Claude Opus 4.8 (1M context)
---------
Co-authored-by: Sean McGuire
Co-authored-by: Claude Fable 5
无版本号
[STG-2253] ci: validate skills against agent-skills standards (#134)
* ci: validate skills against agent-skills standards
Adds scripts/validate-skills.mjs (zero-dependency Node 20+ validator),
a GitHub Actions workflow that runs it on PRs and pushes to main, and
CONTRIBUTING.md documenting the skill standards it enforces.
Co-Authored-By: Claude Fable 5
* ci: bump checkout/setup-node to v5 (Node 20 action runtime deprecated)
Co-Authored-By: Claude Fable 5
* ci: disable setup-node pnpm auto-cache in validate workflow
setup-node@v5 defaults package-manager-cache to true, reads the
packageManager (pnpm) field from package.json, and tries to prime a
pnpm cache. pnpm isn't installed in the job, so it fails with
"Unable to locate executable file: pnpm" before the validator runs.
validate-skills.mjs uses only Node built-ins (no deps to install),
so disable the auto-cache rather than adding a pnpm setup step.
Co-Authored-By: Claude Opus 4.8 (1M context)
---------
Co-authored-by: Claude Fable 5
无版本号
[STG-2254] chore: bring merged skills up to agent-skills standards (#135)
* chore: bring merged skills up to agent-skills standards
- Add MIT LICENSE.txt to browser-to-api, company-research, event-prospecting
- Normalize autobrowse LICENSE year to 2026 and license frontmatter to MIT
- Add license/compatibility/allowed-tools frontmatter where missing
(agent-experience, cookie-sync, fetch, functions, safe-browser, search)
- README: drop stale site-debugger/bb-usage rows, add agent-experience,
autobrowse, browser-to-api, company-research, event-prospecting rows
- Remove banned .env.example from autobrowse (update README setup snippet)
Co-Authored-By: Claude Fable 5
* fix: drop BROWSERBASE_PROJECT_ID from autobrowse setup line
Addresses review feedback from shrey150
---------
Co-authored-by: Claude Fable 5
无版本号
feat(autobrowse): scripts/codegen.mjs — multi-framework runnable-script generator (#125)
* feat(autobrowse): scripts/codegen.mjs — multi-framework runnable-script generator
Migrates the script-generation apparatus from browse.sh's sandbox prompt
into autobrowse itself, so any caller (browse.sh, a developer using
autobrowse standalone, future apps) can convert a converged trace into a
runnable script with one CLI invocation.
What this PR adds:
scripts/codegen.mjs orchestrator (~340 lines)
codegen/prompts/playwright.md framework-specific system prompt
codegen/prompts/stagehand.md framework-specific system prompt
codegen/scaffolds/{fw}/package.json pinned deps, self-contained
codegen/scaffolds/{fw}/tsconfig.json standard ESM/strict
codegen/runners/{fw}.mjs spawn npx tsx + parse JSON result
references/playwright-cdp-bridge.md canonical CDP-attach patterns
(moved from browse.sh)
Pipeline per framework (see scripts/codegen.mjs header):
1. Build context from task.md + unified-events.jsonl + descriptors.ndjson
+ strategy.md + the framework's prompt template + the cdp-bridge doc.
2. Cache key over sha256(framework + prompt-version + task + trace +
descriptors). Hit short-circuits with $0 cost.
3. Single Anthropic completion → emit . to the output dir.
4. Drop scaffold files (package.json, tsconfig) per framework.
5. If --verify: invoke the framework's runner against a fresh BB
session. On failure, feed the error back into a rewrite call up to
--max-retries times.
6. Emit one JSON line per framework on stdout. Non-zero exit if any
selected framework failed.
CLI surface:
--task required
--workspace default ./autobrowse
--run default: newest passing run-NNN
--frameworks default: playwright; builtins: playwright,stagehand
--verify | --no-verify default: --verify
--max-retries N default 2
--cache-dir / --out / --force / --dry-run / --cache-only / --model
--prompt-template custom template for --frameworks=custom
No new top-level deps (uses the @anthropic-ai/sdk that evaluate.mjs already
imports). Smoke-tested with --help, --dry-run, --cache-only error paths.
Companion change in browse.sh (separate PR): collapse the §4b
`for target in playwright stagehand` bash loop and §5 SCRIPT_FLAGS
construction into a single `codegen.mjs --frameworks $SKILL_FRAMEWORKS` call;
delete prompts/playwright-cdp-bridge.md and src/lib/agent-runtime/
script-references.ts (the doc + bundling logic both live in autobrowse now);
drop the playwright/stagehand/tsx/zod install block and the
playwright-best-practices-skill clone from src/lib/dispatch.ts.
Co-Authored-By: Claude Opus 4.7 (1M context)
* fix(codegen): framework-tagged filenames in --out mode
Without this, --out with multiple frameworks selected would have
both writing to /.ts and overwriting each other. Fix:
- Default (no --out): per-framework subdir, file named after the task
(e.g. tasks/acme/playwright/acme.ts) — feels like a standalone project.
- --out : flat, framework-named files (/playwright.ts +
/stagehand.ts) — for callers like browse.sh that flatten everything
into one upload-staging dir.
Runners updated to take --script instead of --task (the basename
is what codegen.mjs decides per the rule above).
No interface change to codegen.mjs's CLI — same flags, same JSON output.
Co-Authored-By: Claude Opus 4.7 (1M context)
* fix(codegen): teach playwright prompt to use APIRequestContext for HTTP/RPC workflows
Empirically across 13 non-browser-method skills tested, the agent's
emit behavior is inconsistent:
CLI (7/7) skipped codegen entirely — correct (CLI setup guides don't
wrap into a script).
MCP (5) 2 emitted, 3 skipped. Of the 2 that emitted:
- packrift: used @playwright/test + APIRequestContext to
drive the MCP JSON-RPC server (no browser). Genuinely
valuable conformance test.
- hiveround: emitted chromium.connectOverCDP + DOM scrape,
ignoring the MCP method label entirely.
fetch (8) 6 emitted; all 6 used the browser path even when fetch
+ cheerio could have done the job in 30 lines vs ~400.
api (2) chess.com used Playwright-as-fetch (goto + JSON.parse(body)
via residential proxy — correct for Cloudflare bypass).
sfgov used a similar pattern. Both kept the browser session
because they needed the proxy.
The packrift case proves the agent CAN write API-RPC scripts via
APIRequestContext — it just doesn't always pick that path. Refine
playwright.md with an explicit "approach selection" section at the top
that branches between API path and browser path based on:
- recommended_method in task.md
- whether the trace shows real DOM interaction vs HTTP-only calls
- whether the data lives in client-rendered DOM vs HTTP response body
API path uses `import { request, type APIRequestContext } from "playwright"`
(playwright already in the scaffold; @playwright/test is NOT required —
plain throw + Zod handles assertions/validation). Browser path is
unchanged.
Bump PROMPT_TEMPLATE_VERSION to "2" so the cache key invalidates and
existing cached outputs don't shadow the refined prompt.
stagehand.md untouched in this commit — Stagehand fundamentally needs a
browser, so for HTTP-only workflows the right answer is "don't emit a
Stagehand script at all", which needs a separate codegen.mjs change
(allow runners to signal "not applicable"). Follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context)
* Revert "fix(codegen): teach playwright prompt to use APIRequestContext for HTTP/RPC workflows"
This reverts commit aac0acc1733f5a2a7cc7b0104a6cf2e1c7474b88.
* fix(codegen): address 6 PR #125 review threads
Six findings from cursor + shubh24 on the codegen PR:
1. (HIGH / blocker) Rewrite verify uses wrong script name
`verify(framework, outDir, TASK)` on the rewrite path passes the bare
task slug; `verify()` then resolves it as a missing file basename
(TASK lacks the .ts extension that scriptBasename carries). This
silently disabled the verify→rewrite loop. Fix: pass scriptBasename
on both the initial and retry call sites, matching the variable used
to write the file at L375.
2. (MEDIUM) Cache hit allowed one extra rewrite
On cache hit, `attempts` stayed at 0 through the first failed
verify, so the loop condition `attempts < MAX_RETRIES + 1` allowed
MAX_RETRIES+1 rewrites for cached runs vs MAX_RETRIES for uncached.
--max-retries was therefore inconsistent. Initialize attempts to 1
on both paths so the bound matches.
Update the result-object's `cached` flag accordingly: was
`!!cached && attempts === 0` (now never true with the new init), now
`!!cached && cost === 0` — same semantic ("no LLM call was made for
this output"), now expressed via cost which is the load-bearing
signal anyway.
3. (MEDIUM) Forced --run path unchecked
`pickRun` returned any --run value without checking that
`traces//` exists. Codegen would then proceed with empty
trace/events/descriptors while logs reported the forced run id as
if it were a real input. Add an fs.existsSync gate on FORCED_RUN
and a distinct error message when the named dir is missing.
4. (LOW) `ranner_missing` typo
Producer (L301) and consumer (L385) shared the misspelling, so
the break-on-missing-runner path worked, but it was fragile — fix
one without the other and the retry loop would silently stop
stopping. Renamed both to `runner_missing`.
5. (DRY) Stagehand runner was 95% identical to Playwright runner
Factored the shared spawn-tsx-and-parse-JSON logic into
codegen/runners/lib/tsx-runner.mjs (runTsxTarget({label, extraEnv,
installEnv, preflight})). The per-framework runners now hold only
their genuine differences:
- playwright.mjs: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 for install+run
- stagehand.mjs: ANTHROPIC_API_KEY preflight check
Net: ~120 lines collapsed to ~25 each + ~110 in the shared lib.
6. (REGRESSION fix) Scaffold versions had no env override
browse.sh's old dispatch.ts used resolveVersion('PLAYWRIGHT_VERSION',
…) / 'STAGEHAND_VERSION' / 'TSX_VERSION' / 'ZOD_VERSION' so a
deployment could canary a new framework version without a code
change. Moving the pins into autobrowse's scaffold/package.json as
literals dropped that. Restore the knob: codegen.mjs now reads
{PLAYWRIGHT,STAGEHAND,TSX,ZOD,DOTENV}_VERSION env vars (with a
semver guard) and interpolates them into the scaffold via the
existing template-interpolation pass. Defaults match the previous
literals.
Bump PROMPT_TEMPLATE_VERSION to "2" so any cached outputs from the buggy
verify-loop path get re-emitted.
Co-Authored-By: Claude Opus 4.7 (1M context)
* fix(codegen): make scaffold `npm start` use generated script name
When --out is set, emitted scripts are named .ts but scaffolded
package.json hardcoded `tsx {{TASK}}.ts`, so standalone `npm start` in the
out dir targeted a nonexistent file. Thread {{SCRIPT}} through dropScaffold
and update both scaffolds to `tsx {{SCRIPT}}`.
Co-Authored-By: Claude Opus 4.7 (1M context)
* fix(codegen): merge package.json deps across frameworks in shared --out dir
When --out is set and multiple frameworks share one outDir (browse.sh
passes --frameworks playwright,stagehand --out ), framework #1
wrote package.json with its deps and verify ran npm install. Framework
#2's dropScaffold hit `if exists(dst) continue` and silently skipped
its package.json, so @browserbasehq/stagehand was never declared or
installed. The runner's `skip install if node_modules exists` gate
then skipped re-install, and verify died at
`Cannot find package '@browserbasehq/stagehand'` before any browser
work — burning all MAX_RETRIES on an unfixable error.
Fix:
- dropScaffold: when package.json already exists, deep-merge the
scaffold's dependencies/devDependencies in instead of skipping.
- tsx-runner: replace `!exists(node_modules)` gate with a sha256 stamp
of package.json written under node_modules/.codegen-pkg-hash, so
framework #2's merged deps trigger a re-install.
Co-Authored-By: Claude Opus 4.7 (1M context)
* fix(codegen): correct Stagehand prompt for the v3 API
The Stagehand codegen prompt described the old connectOverCDP/attach
pattern, so generated scripts failed verify against @browserbasehq/stagehand
3.4.0:
- passed the Anthropic key as the top-level `apiKey` (which is the Browserbase
key in v3), so session lookup 404'd with "Session not found"
- bare `modelName: "claude-opus-4-5"` — v3 wants `model: { modelName:
"anthropic/", apiKey }` (provider-prefixed)
- `stagehand.page` / `page.extract()` — v3 uses `stagehand.context.pages()[0]`
and `stagehand.extract(instruction, schema)` on the instance
- REQUEST_RELEASE teardown — v3 owns the session, so use `stagehand.close()`
Rewrite the prompt to teach the v3 own-session pattern. BROWSERBASE_API_KEY
alone is sufficient (the project is inferred from the key; no projectId).
Verified end-to-end: `codegen --frameworks playwright,stagehand --out
--verify` now passes both frameworks on the first attempt against live
Browserbase sessions (google-news task).
Co-Authored-By: Claude Opus 4.8 (1M context)
* fix(codegen): --cache-only must also short-circuit the rewrite loop
Previously --cache-only only blocked generation on a cache miss; a
cached script that failed verify would still enter the rewrite loop
and call the Anthropic API, contradicting the documented "no LLM call"
CI semantics. Bail out of the rewrite loop when CACHE_ONLY is set.
Co-Authored-By: Claude Opus 4.7 (1M context)
* fix(codegen): raise verify() timeout above runner worst case
verify() spawned the runner with a 5min cap, but tsx-runner internally
allows up to 3min for npm install + 5min for the tsx run (8min worst
case). The parent could SIGTERM a healthy runner mid-script, leaving
verify with no {passed:boolean} JSON line — codegen then treated the
script as broken and burned a retry. Bump verify's cap to 10min so the
parent always exceeds the child's bounded internals.
Co-Authored-By: Claude Opus 4.7 (1M context)
* fix(codegen): always set PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD on install
In shared --out mode, framework #2 (e.g. stagehand) inherits playwright
in its merged package.json via dropScaffold, so stagehand's install
would trigger playwright's postinstall and try to fetch ~hundreds of MB
of chromium — almost always blowing the 3min install budget and
failing verify on a healthy script.
Set PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 unconditionally in the install
env (we always connectOverCDP, never need local browsers). Per-runner
installEnv still wins via the spread order.
Co-Authored-By: Claude Opus 4.7 (1M context)
---------
Co-authored-by: Claude Opus 4.7 (1M context)
Co-authored-by: shubh24
无版本号
autobrowse: add opt-in --browser-trace integration (#121)
* autobrowse: add opt-in --browser-trace integration
Wire the sibling browser-trace skill into the autobrowse loop behind a
default-off --browser-trace flag. When set, each iteration pre-creates a
Browserbase --keep-alive session, attaches bb-capture as a passive CDP
observer, and runs evaluate.mjs with --connect-url so every inner browse
call is rewritten to attach via --cdp $connectUrl --session
autobrowse-main. browse stop is suppressed under trace mode so the named
daemon survives the iteration.
The --cdp attach mode is the key: --remote routes Network.*/Console.*
events only to the driving client, so the trace observer sees nothing
useful. --cdp gives the observer the full per-page firehose, matching
the canonical pattern documented in browser-trace's SKILL.md.
Default path is unchanged; --browser-trace implies --env remote.
Co-Authored-By: Claude Opus 4.7 (1M context)
* autobrowse: preflight browser-trace + teach cross-trace hypothesis
- Add a fail-fast preflight in the traced-path block: if
${CLAUDE_SKILL_DIR}/../browser-trace/scripts/bb-capture.mjs is missing,
bail with an install hint pointing at github.com/browserbase/skills.
Prevents the harness from blowing up halfway through with "no such
file or directory" on bb-capture.mjs.
- Make explicit in "Read the trace" + "Form one hypothesis" that under
--browser-trace there are now TWO complementary traces: trace.json
(what the agent did, per turn) and cdp/summary.json (what the browser
did, per page). Document the cross-reference pattern: find the failing
page in cdp/summary.json, then jump back to trace.json for the turn
whose command produced it. Hypotheses should cite both — "the click
went through but /api/checkout returned 403" beats "the click failed."
Co-Authored-By: Claude Opus 4.7 (1M context)
* autobrowse: thread --browser-trace through multi-task sub-agents
Closes the gap flagged in PR #121 review: /autobrowse --tasks a,b
--browser-trace would silently fall back to the untraced path inside
each spawned sub-agent because the sub-agent prompt template said only
"Use --env " without mentioning --browser-trace.
The sub-agent prompt now explicitly requires the traced-path loop block
(pre-create session, bb-capture, --connect-url to evaluate.mjs, stop+
bisect, release) when the parent invocation passed --browser-trace.
Co-Authored-By: Claude Opus 4.7 (1M context)
* autobrowse: unified event log as primary --browser-trace artifact
Per design feedback (Derek): the two-trace cross-reference UX from the
prior commits puts friction on the outer agent. Replace it with a single
time-ordered NDJSON stream that joins the agent's turn log
(trace.json) and the browser-trace CDP firehose (cdp/raw.ndjson) into
one source-tagged file the outer agent reads first each iteration.
New: skills/autobrowse/scripts/unify-trace.mjs
- Joins trace.json + cdp/raw.ndjson by wall-clock timestamp
- Reuses bisect-cdp.mjs's monotonic→wall-clock conversion logic
(anchor on first monotonic CDP timestamp + manifest.started_at)
- Filters out noisy/redundant CDP methods (dataReceived, ExtraInfo,
frame Started/Stopped Loading, executionContextCreated, ...) so
the unified log stays scannable
- Inline summary fields per browser-event family (url+status+mime
for Network.*, level+text for Console.*, name for Page.lifecycle,
etc.); full payloads remain in the cdp/ drill-down files
- Annotates each browser row with page_id, derived by walking
top-level Page.frameNavigated events in the same order bisect-cdp
uses
SKILL.md changes:
- Traced-path block now invokes unify-trace.mjs after bisect-cdp
- "Read the trace" section rewritten: unified-events.jsonl is the
primary path; structured files (cdp/summary.json, trace.json,
cdp/network/failed.jsonl, etc.) are documented as agent-consumable
drill-downs for queries the stream doesn't naturally answer
- "Form one hypothesis" updated: hypothesis must cite a specific
line/timestamp in unified-events.jsonl or name the drill-down file
Verified end-to-end on news.ycombinator.com → first story's article:
6 turns, $0.31, 126-event unified log (113 browser + 13 agent),
strictly time-ordered, no leaked sessions.
No changes to evaluate.mjs or browser-trace. Default path
(no --browser-trace) is unchanged — no unify step runs.
Co-Authored-By: Claude Opus 4.7 (1M context)
* autobrowse: unique daemon name per session + MAX_TURNS env override
Two small fixes surfaced by v3.1 verification:
1. Static bug: --browser-trace hardcoded the local browse-daemon name as
"autobrowse-main". Two parallel evaluate.mjs invocations (SKILL.md
Step-3 multi-task sub-agents) would collide on that socket. Derive a
per-process daemon name from a sha1 hash of the connectUrl
(autobrowse-) so each Browserbase session gets its own
local daemon. Deterministic, no deps beyond node:crypto.
2. QOL: MAX_TURNS is now overridable via the MAX_TURNS env var (default
30). Useful for testing failure-first recovery without patching
source. No effect on default invocations.
Verification (v3.1, /tmp/autobrowse-eval-v2):
- Part 1: joiner's page_id walk matches bisect-cdp exactly (no drift).
- Part 2: SKIP_METHODS audit — Network.dataReceived + *ExtraInfo etc.
are filtered correctly; ExtraInfo headers remain accessible via
drill-down (cdp/raw.ndjson, query.mjs). Known limitation: header-based
hypotheses need drill-down today.
- Part 3: parallel-daemon contention fixed (this commit). Unit-level
verified: different connectUrls → different daemon names.
- Part 4: failure-first single-arm on httpbin-form-incomplete
- iter 1 (MAX_TURNS=15, --browser-trace): max_turns failure at $0.38;
agent burned 4 turns on verification checks (turns 11-14) before
submit clicked at turn 15. POST went through (200 OK in unified log)
but agent ran out of turns before reading response.
- Manual outer-agent step: read unified-events.jsonl, cited specific
events (POST line + 4 verification turns), wrote evidence-grounded
strategy.md update telling iter 2 to skip the verification loop.
- iter 2: 12 turns, $0.27, status=completed, echoed_custname matches.
- Net: unified-log evidence drove a real recovery on a failure-first
task. ~$0.65 total v3.1 spend.
Co-Authored-By: Claude Opus 4.7 (1M context)
* autobrowse: export O11Y_RUN_ID so browse CLI can emit descriptors
Pair to the descriptor-capture change landing in the browse CLI: every
page-driving browse call (click/fill/select/goto/wait/etc.) now
side-channels a rich node descriptor to
$O11Y_ROOT/$O11Y_RUN_ID/cdp/descriptors.ndjson when both env vars are
set. The CLI was already gated on O11Y_ROOT — adding O11Y_RUN_ID lets
it know which run subdirectory to write into without inferring from
the most-recently-modified dir.
Two lines change in the traced-path loop block:
- export O11Y_RUN_ID="$RUN_ID" alongside O11Y_ROOT
- documentation note on what descriptors.ndjson is and that the
outer agent should skip it when reading the trace (it feeds
downstream codegen, not hypothesis formation)
No effect when --browser-trace is unset or when the CLI doesn't yet
have the descriptor-capture utility — the new env var is harmless.
Co-Authored-By: Claude Opus 4.7 (1M context)
---------
Co-authored-by: Claude Opus 4.7 (1M context)
Co-authored-by: ziruihao
无版本号
Rename skill: audit-agent-experience → agent-experience (#120)
* Rename skill: audit-agent-experience -> agent-experience
Rename the skill folder and scrub all slug references in SKILL.md
(name field, report output path) and the HTML report template
(header label, footer link).
* Point report footer link to browserbase/skills repo
Address review: footer linked to github.com/jay/... — repoint to the
skill's location in browserbase/skills.
Co-Authored-By: Claude Opus 4.8 (1M context)
---------
Co-authored-by: Claude Opus 4.8 (1M context)
无版本号
Audit Agent Experience Skill (#79)
* Add audit-agent-experience skill — multi-agent evaluation of agent DX
Spawns parallel Claude subagents against a target docs/SDK/SKILL.md from a
one-sentence prompt, captures structured traces, and renders a graded HTML
report scoring Setup Friction, Speed, Efficiency, Error Recovery, and Doc
Quality. Includes narrative cross-agent review to surface convergent
hallucinations and silent workarounds the JSON self-report misses.
Co-Authored-By: Claude Opus 4.7 (1M context)
* fix: updated license.txt
* bugbot fixes
* bugbot fixes
* bugbot fixes
* bugbot fixes
* fix: address review comments on audit-agent-experience SKILL.md
- Set author to GH username jay-sahnan
- Remove BROWSERBASE_PROJECT_ID from orchestrator-retained credentials example
Co-Authored-By: Claude Opus 4.8 (1M context)
---------
Co-authored-by: Claude Opus 4.7 (1M context)
无版本号
Update skills to use verified Browserbase sessions (#106)
无版本号
docs: rename bb/browse-cli references to unified browse CLI (#111)
* docs: rename bb/browse-cli references to unified browse CLI
The `@browserbasehq/cli` (bb) and `@browserbasehq/browse-cli` packages
were merged into a single `browse` package (`npm install -g browse`).
Update all skill docs, scripts, and examples to use the new command
surface:
- `bb fetch|search|sessions|projects|contexts|extensions` → `browse cloud ...`
- `bb functions ...` → `browse functions ...`
- `bb templates ...` → `browse templates ...`
- `bb skills install` → `browse skills install`
- `bb browse ` → top-level `browse ` (open, get, click, …)
- `pnpm bb dev|publish` → `browse functions dev|publish`
- `# bb call N/TOTAL` enforcement comments → `# browse call N/TOTAL`
- `bbFetch`/`bbBrowseMarkdown` helpers in extract_page.mjs renamed and
reworked to call the new `browse cloud fetch` / `browse get markdown`
surface.
- bb-capture.mjs / bb-finalize.mjs shell out to `browse cloud sessions ...`
SDK variable names (`const bb = new Browserbase()`) and the
`x-bb-api-key` HTTP header are untouched.
Co-Authored-By: Claude Opus 4.7 (1M context)
* fix: align browse command examples with 0.6
* docs: note deprecated context upload refresh
* docs: clarify cdp flag usage in trace example
* docs: rely on cdp daemon persistence in trace example
* docs(browser-trace): drop redundant target flags on follow-up commands
The browse daemon caches the target after the first command, so commands
that follow `browse open --cdp 9222` (or `--remote --session "$SID"`)
don't need to repeat the target flag. Addresses PR #111 review comments
about `--cdp` repetition in EXAMPLES.md.
Co-Authored-By: Claude Opus 4.7 (1M context)
* docs: clarify cdp daemon behavior
* fix(extract_page): drop redundant --local --headless on `browse get`
The browse daemon caches the target after `browse open --local --headless`,
so the follow-up `browse get markdown` call shouldn't repeat the mode flags.
Matches the convention from skills/browser/SKILL.md ("put --local, --remote,
--auto-connect, or --cdp on the first browser command") and the same fix Shrey
applied to browser-trace in 5468e0e.
Addresses the Cursor Bugbot finding on PR #111.
Co-Authored-By: Claude Opus 4.7 (1M context)
* docs: align browse lifecycle examples
* docs: remove stale fill flag reference
* docs: tighten browse skill migration wording
* docs: clarify browse stop mode switching
* docs: verify browse command caveats
---------
Co-authored-by: Claude Opus 4.7 (1M context)
Co-authored-by: Shrey Pandya
无版本号
Add browser-reverse skill — OpenAPI 3.1 from browser-trace captures (#88)
* Add browser-reverse skill — discover OpenAPI 3.1 from browser-trace captures
Consumes a browser-trace run (.o11y//), pairs CDP request/response
events, templatizes paths, infers JSON schemas from samples, and emits an
OpenAPI 3.1 document with a coverage report and confidence metadata.
Pipeline: load → filter → normalize → infer → emit. Each stage is a
discrete script writing to intermediate/ for debuggability. Optional
--bodies flag joins a `browse network on` capture by CDP requestId
so response bodies feed into schema inference.
E2E tested against Hacker News, jsonplaceholder, derekmeegan.com,
browserbase.com, browser-use.com, reddit.com.
Co-Authored-By: Claude Opus 4.7 (1M context)
* Address PR #88 review: rename to browser-to-api, drop lift doc, fix bugs
Renaming and doc cleanup (per shrey150):
- Rename skill from `browser-reverse` to `browser-to-api`. Updates SKILL.md
frontmatter + heading, package.json, REFERENCE.md heading, the OpenAPI
doc's `info.description`, and the report.md heading.
- Fix the stale `discover-api-spec` reference in SKILL.md's composition diagram
(left over from an earlier rename).
- Drop `BODY-CAPTURE-LIFT.md` from the PR; it's a separate proposal.
- Remove the `exec.sendFile()` reference in SKILL.md (browserbase-internal,
not a generic skill primitive).
- REFERENCE.md restructured to lead with the script/CLI/file-format reference
rather than an architecture intro. Pipeline diagram dropped.
Bug fixes (per Cursor Bugbot):
- `filter.mjs`: rework precedence so `--include` actually rescues URLs that
would be hit by a default exclude, matching the documented contract. User
`--exclude` still wins. Added a unit-style test path.
- `infer.mjs`: skip response-body samples whose CDP status is null. Previously
they were keyed under `"0"` but `emit.mjs` only iterates `ep.statusCodes`
(which excludes nulls), silently discarding the body.
- `load.mjs`: fix the comment in `urlQuery()` — code is first-value-wins, not
last-value-wins.
Co-Authored-By: Claude Opus 4.7 (1M context)
* Add Swagger UI preview for browser-to-api
* Noise classification, GraphQL decomposition, and client SDK generation
normalize.mjs:
- Auto-classify endpoints as api/noise/page and drop non-API traffic
(tracking, analytics, bot defense, session plumbing, HTML page renders)
- Detect multiplexed endpoints (GraphQL operationName, JSON-RPC method,
query param dispatch) and decompose into separate logical operations
- Typically drops 60-80% of captured traffic as noise
emit.mjs:
- Generate client.mjs — zero-dependency ES module wrapping each discovered
operation as an async function with JSDoc param types
- For GraphQL/APQ endpoints, embeds persisted query hashes and wires up
the full request shape so callers just pass variables
- Extract required headers from trace (CSRF tokens, custom headers) and
include them in client defaults
- Task-oriented report.md with quick-start import, curl examples,
variables tables, and response samples per operation
On OpenTable trace: 27 raw endpoints → 9 named operations, zero noise.
Generated client with autocomplete(), restaurantsAvailability(), etc.
Co-Authored-By: Claude Opus 4.6 (1M context)
* Add self-contained HTML report replacing Swagger UI
Generates index.html with:
- Summary stats (operations, endpoint, protocol, sample count)
- Expandable cards per operation with variables table, client usage,
request body, and response example
- Full generated client.mjs embedded at the bottom
The Swagger UI was a poor fit — 10 identical green POST bars for a
single GraphQL endpoint with bracket-syntax paths that aren't even
valid OpenAPI. The HTML report shows what actually matters.
Co-Authored-By: Claude Opus 4.6 (1M context)
* Replace Swagger UI with self-contained HTML report
emit.mjs already generates index.html as the primary visual output —
update SKILL.md to match and remove the dead open-swagger-ui.mjs script.
Co-Authored-By: Claude Opus 4.6 (1M context)
---------
Co-authored-by: Claude Opus 4.7 (1M context)
Co-authored-by: Shrey Pandya
无版本号
Remove what-antibot skill (#104)
Co-authored-by: Claude Opus 4.7 (1M context)
无版本号
chore: clean up what-antibot skill (#99)
无版本号
add skill to check sites for antibots (#98)
* add skill to check sites for antibots
* fix jank footer
* remove jawn
* whatantibot instead + url parsing and remove html
* fix no csv
无版本号
feat: add safe browser skill (#89)
无版本号
Add browser-trace skill (#83)
* Add browser-observability skill
Capture the full CDP firehose plus periodic screenshots and DOM dumps
alongside any browser automation (browse, Stagehand, Playwright, Puppeteer
— anything that speaks CDP), then bisect the stream into per-page
searchable buckets keyed by top-level navigations.
The skill is purely observational: it adds a second CDP client that
enables read-only domains (Network, Console, Runtime, Log, Page) and
never sends action commands, so it can attach to an in-flight automation
without disrupting it.
Layout produced under .o11y//:
manifest.json, index.jsonl
cdp/raw.ndjson full firehose, NDJSON
cdp/summary.json {sessionId, duration, totalEvents, pages[]}
cdp//... session-wide buckets
cdp/pages// per-page slices, indexed by Page.frameNavigated
summary.json, raw.jsonl, network/, console/, page/, ...
screenshots/.png, dom/.html
browserbase/ added by bb-finalize.sh on remote runs
Scripts (pure bash + jq, no Python/Node deps beyond the existing browse CLI):
start-capture.sh / stop-capture.sh / snapshot-loop.sh
bisect-cdp.sh — splits raw.ndjson into session-wide and per-page buckets,
writes the structured summary.json
query.sh — drill-down helper: list, page [bucket], errors,
hosts, host , timeline, summary
bb-capture.sh / bb-finalize.sh — Browserbase wrappers; bb-capture handles
--keep-alive + connectUrl resolution + manifest stamping with
the session id, region, and live debugger URL; bb-finalize
writes browserbase/{session.json, logs.json, downloads.zip}
and optionally releases the session
The CDP firehose is sourced via `browse cdp `, which streams
DevTools-protocol events as NDJSON. That command requires
@browserbasehq/browse-cli alpha or a release that includes
browserbase/stagehand#1905; lifecycle events specifically require
browserbase/stagehand#2056.
Co-Authored-By: Claude Opus 4.7 (1M context)
* Convert browser-observability scripts from bash to .mjs
Mirrors the cookie-sync skill's pattern: each script becomes a Node ESM
module, with a shared scripts/lib.mjs for common helpers and a minimal
package.json (no dependencies — pure Node stdlib, so no `npm install`
step is needed).
scripts/lib.mjs shared helpers, BUCKETS table, isTopNav
scripts/start-capture.mjs spawn detached `browse cdp` + sampler
scripts/snapshot-loop.mjs screenshot+DOM+url poll loop (internal)
scripts/stop-capture.mjs SIGTERM+grace+SIGKILL, sweep .partials
scripts/bisect-cdp.mjs page-aware bisect + structured summary.json
scripts/query.mjs drill-down: list/page/errors/hosts/host/timeline
scripts/bb-capture.mjs Browserbase wrapper (--new or attach by id)
scripts/bb-finalize.mjs pull session.json/logs.json/downloads.zip
Output layout, per-page bisection logic, summary.json shape, and the
query.mjs subcommands are identical to the bash versions. SKILL.md,
REFERENCE.md, and EXAMPLES.md updated to invoke `node scripts/foo.mjs`
instead of `bash scripts/foo.sh`.
Dogfooded end-to-end against a Browserbase session and against the
saved /Users/d/Desktop/browserbase/o11y-demo-1 run — page bisection,
durations, error attribution, host counts all match the bash output.
Co-Authored-By: Claude Opus 4.7 (1M context)
* Rename browser-observability → browser-trace
Shorter, more action-oriented name that reads cleanly as a slash command
and pairs naturally with the autobrowse skill (which can use the trace
output as the structured "what just happened" data for its loop).
Renames:
- skills/browser-observability/ → skills/browser-trace/
- SKILL.md frontmatter `name`, doc H1s, package.json name
- marketplace.json plugin entry (name, skills path, description, keywords)
- README.md skills table row
- "observer" → "tracer" throughout the prose for consistency with the new name
No behaviour change — all script interfaces, output layout, summary.json
shape, and query.mjs subcommands are unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context)
* Address Cursor Bugbot review on browser-trace
Fix three issues flagged on PR #83:
- query.mjs cmdTimeline: was printing every navigation followed by every
lifecycle event, so all `[NAV …]` lines bunched before any
`[init]`/`[load]` lines. Now reads cdp/raw.ndjson directly and emits
top-level frameNavigated and lifecycle events in actual stream order.
- query.mjs cmdHost: was using `url.startsWith('https://' + hostname)`,
which would match `https://api.example.com.evil.tld/...` when the user
asked for `api.example.com`. Switched to exact `new URL(url).hostname`
comparison via a small `hostMatches` helper.
- query.mjs: removed the unused `emitJsonl` helper (defined but never
called — same class of dead-code issue Bugbot raised against the bash
version's `cat_bucket_across`).
Bugbot also flagged the bash bb-capture.sh's `| grep ... || true` that
silently swallowed start-capture failures. The .mjs version already
handles this correctly via `if (start.status !== 0) process.exit(...)`,
so no change needed there.
Co-Authored-By: Claude Opus 4.7 (1M context)
* Fix Console domain bucket + align hosts/host port handling
Two more issues from Cursor Bugbot on PR #83:
1. Console errors and warnings were being silently dropped from the
per-page summary. computePageSummary classified each event by
`method.split('.')[0]`, which puts Runtime.consoleAPICalled events
into a `Runtime` bucket — but errors/warnings from those events were
being recorded under the key `Console`. The output loop iterates
`counts.entries()` and looks up errors/warnings by that domain key,
so the Console errors/warnings never made it into the rollup.
Added a `domainFor(method)` helper that returns 'Console' for
`Runtime.consoleAPICalled` and the standard `method.split('.')[0]`
otherwise. Now matches the schema documented in SKILL.md.
2. cmdHosts grouped by `URL.host` (port included) but cmdHost compared
against `URL.hostname` (port stripped), so `hosts` output couldn't
be piped into `host` when a port was present. Switched
`hostMatches` to `URL.host` for consistency. Impostor protection
(rejecting `example.com.evil.tld` for `example.com` queries) is
preserved by the exact-equality check.
Verified against /Users/d/Desktop/browserbase/o11y-demo-1: every page
now shows Console + Runtime as distinct domain blocks; page 4
(httpbin/418) correctly attributes the 418 status to a Log error.
Co-Authored-By: Claude Opus 4.7 (1M context)
---------
Co-authored-by: Claude Opus 4.7 (1M context)
无版本号
feat: event-prospecting skill (#80)
* feat: event-prospecting skill v0.1
Pre-event prospecting skill that wraps company-research's ICP backend.
Takes a conference / event speakers URL, extracts the people, filters
their companies against the user's ICP, then deep-researches only the
speakers at ICP-fit companies. Outputs a person-first HTML report with
3-bullet "why reach out" rationale, all public links surfaced, and a
one-click DM opener / "research deeper" clipboard hook per card.
Validated end-to-end on Stripe Sessions 2026: 4 browse commands
extract 240 speakers / 99 companies in ~6s via the Next.js
__NEXT_DATA__ shortcut.
Reuses extract_page.mjs + list_urls.mjs verbatim from company-research,
extends compile_report.mjs with person-card + people/companies views,
and copies the Plan→Research→Synthesize pattern (skills must be
self-contained per skill-creator).
* addressed bug bot comments
* fix: bugbot comments
* fix: more bug bot comments
* fix: bugbot comment
* more bug bot fixes
* more bug bot fixes
无版本号
Add company-research skill — deep ICP research with HTML report (#75)
* Add company-research skill — deep ICP research with HTML report
Co-Authored-By: Claude Opus 4.7 (1M context)
* removed profiles
* reduce hallucinations
* bugbot fix
---------
Co-authored-by: Claude Opus 4.7 (1M context)
无版本号
docs(autobrowse): replace slusa-payment with amazon-add-to-cart in examples (#74)
Co-authored-by: Claude Opus 4.7 (1M context)
无版本号
feat: autobrowse skill — self-improving browser automation (#71)
* feat: add autobrowse skill — self-improving browser automation
Auto-research loop for building reliable browser navigation skills.
Inner agent browses the site, outer agent reads the trace and improves
strategy.md. Repeat until it passes consistently.
- SKILL.md: /autobrowse skill (single + parallel task modes via sub-agents)
- scripts/evaluate.ts: inner agent harness (Anthropic API + browse CLI)
- references/: example-task.md and example-skill.md templates
- README, EXAMPLES, REFERENCE docs
Co-Authored-By: Claude Sonnet 4.6 (1M context)
* docs: replace example-skill.md with real graduated SF 311 skill
17 iterations, 3/3 consecutive passes, remote env.
Shows real site-specific gotchas: named Browserbase sessions,
ESRI map location, Verint form patterns, XPath vs ref tradeoffs.
Co-Authored-By: Claude Sonnet 4.6 (1M context)
* feat: sync SKILL.md with latest improvements
- structured skill.md graduation template (not just copying strategy.md)
- graduate at max iterations too, not just on pass rate
- persistent session reports in reports/ with per-iteration cost table
- richer sub-agent prompt with key learnings output
- cost column in multi-task report table
Co-Authored-By: Claude Sonnet 4.6 (1M context)
* docs: sync example-skill.md with latest sf-311 skill
- remove --session flags (skills must be portable, not session-coupled)
- add Known Failure Point section (turn budget exhaustion pattern)
- correct wait syntax docs (browse wait load / browse wait timeout N)
- 22 iterations, updated gotchas order
Co-Authored-By: Claude Sonnet 4.6 (1M context)
* fix: address all 5 bugbot findings
- [high] block shell metacharacters (;|&|`$()) to prevent injection bypass
- [medium] fix npm evaluate script path: evaluate.ts → scripts/evaluate.ts
- [low] fix tsconfig include: *.ts → scripts/**/*.ts
- [low] lastAssistantText += (accumulate, not overwrite)
- [low] remove unused deps: @browserbasehq/stagehand, zod
Co-Authored-By: Claude Sonnet 4.6 (1M context)
* fix autobrowse bugbot findings
* fix autobrowse pricing and symlink handling
* feat: add demo-video skill — Remotion launch video learnings
Captures full pipeline from storytelling to render:
- IDEA ENGINE narrative structure
- Remotion gotchas (extrapolateLeft, staticFile, durationInFrames)
- Browserbase brand colors and typography
- UI mocking patterns with real reference video analysis
- Audio production (background, SFX, ffmpeg generation)
- Content authenticity (real failures vs invented ones)
- Render quality settings (CRF 8 for Twitter masters)
Co-Authored-By: Claude Sonnet 4.6 (1M context)
* Revert "feat: add demo-video skill — Remotion launch video learnings"
This reverts commit 07e7794e3252aec4ee9ce1ac16de81e2e373c2f6.
* refactor(autobrowse): convert evaluate.ts to .mjs, follow agentskills.io conventions
- Converted scripts/evaluate.ts → scripts/evaluate.mjs (plain ESM JavaScript)
- Removed tsx, typescript, @types/node devDependencies
- Removed tsconfig.json (no longer needed)
- Added --help flag with full usage documentation
- Moved diagnostics to stderr, structured JSON result to stdout
- Added license field to SKILL.md frontmatter
- Updated all references from tsx/evaluate.ts to node/evaluate.mjs
Co-Authored-By: Claude Opus 4.6 (1M context)
* feat(autobrowse): graduate skills to ~/.claude/skills/ instead of git
Graduated skills now install as Claude Code slash commands at
~/.claude/skills//SKILL.md instead of committing a local
skill.md file. Removed all git commit references from the loop —
the working directory (tasks/, traces/, strategy.md) doesn't need
to be a git repo.
Co-Authored-By: Claude Opus 4.6 (1M context)
* fix(autobrowse): correct ref format, add workspace flag, fail-fast on missing key
Three fixes surfaced while running the google-flights task from an empty
strategy baseline:
1. Ref format: the inner agent was told to click by @ref (e.g. @0-5), but
browse snapshot emits refs as [X-Y] (e.g. [0-5], [2-147]). The agent
hallucinated @-refs which the CLI silently accepted as no-ops, burning
30 turns without useful interaction. Updated both the TOOLS description
and the system prompt to document [X-Y] format with examples, and added
a "never invent refs" rule.
2. Ref counter: the live stderr log and summary writer counted @ chars in
snapshot output, which always returned 0 for real snapshots. Switched
to /\[\d+-\d+\]/g so outer agents can see the real ref count.
3. Workspace + key UX: added --workspace flag (default ./autobrowse)
so tasks/, traces/, and reports/ land in CWD instead of under
~/.claude/skills/autobrowse/. This avoids the permission friction of
writing inside Claude's home dir during training. Added ensureApiKey()
that fails fast with a clear setup message when ANTHROPIC_API_KEY is
missing — no more outer-agent key-hunting paths.
SKILL.md updated to document natural-language invocation (URL drops and
free-form goals, not just --task ), the new CWD-based workspace
flow, and the new ref format / wait semantics.
Verified on google-flights: empty-strategy baseline now sees 1157 real
refs and drives the form correctly (previously 0 refs, total blindness).
With a 9-line strategy, completes in 5 turns / \$0.16.
Co-Authored-By: Claude Opus 4.7 (1M context)
* fix(autobrowse): address bugbot findings on evaluate.mjs and SKILL.md
- Guard lastAssistantText overwrite: only update when the turn produced
text, so a final tool_use-only response doesn't wipe prior output
(bugbot 3061376570).
- Drop redundant reasoningText; the two variables held identical values
(bugbot 3061376573).
- Reorder tool-result branches: check error before isSnapshot/isScreenshot
in both live log and summary writer, so a failed snapshot reports as
error, not as "0 refs" (bugbot 3061606988).
- Track completion via explicit flag set on end_turn / no-tool-use, not
via `turn < MAX_TURNS`: a clean finish on turn 30 was misreported as
max_turns (bugbot 3065604864).
- Replace personal home path with generic placeholder in the sub-agent
prompt template (bugbot 3118916022).
Co-Authored-By: Claude Opus 4.7 (1M context)
* fix(autobrowse): align wait guidance with browse cli
* fix(autobrowse): mark truncated runs incomplete
---------
Co-authored-by: Claude Sonnet 4.6 (1M context)
Co-authored-by: Shrey Pandya
无版本号
Update CLI command reference for bb v0.5.4 (#72)
- Add `sessions create` convenience flags (proxies, stealth, captchas,
region, viewport, etc.) that replace manual --body JSON
- Add new `templates` command (list, clone) for scaffolding starter projects
- Update SKILL.md to include templates in command selection and workflows
Co-authored-by: Claude Opus 4.6 (1M context)
无版本号
Add 5 new skills to README (#67)
暂无评论...