Competitor analysis

01What is it?
Competitor research and intelligence skill. Takes a user's company (with optional seed competitor URLs), auto-discovers additional competitors via Browserbase Search API, deeply researches each using a 4-lane pattern. Its edge is a particular angle on competitive research, giving the agent tighter constraints than a plain competitor analysis request.
02Inputs
Context for competitive research: your goals, audience, constraints, and any source material the skill asks for.
03Output
A ready-to-use result for competitive research: the analysis, copy, or recommendations the agent produces.
Install-only

Install as a package

Installs this one skill package for your coding agent, including any supporting files that skill ships with — not every skill in the repository. Read the tutorial.

Terminal
$ npx skills add browserbase/skills --skill competitor-analysis

Skill instructions

The instruction file for this skill. The skill also includes other files you need to install to use it.

SKILL.md

Competitor Analysis

Analyze a user's competitors. Uses Browserbase Search API for discovery and a 4-lane Plan→Research→Synthesize pattern for enrichment — outputting an HTML report with overview, per-competitor deep dives, a side-by-side feature/pricing matrix, and a chronological mentions feed.

Required: BROWSERBASE_API_KEY env var and the browse CLI installed (npm install -g browse).

First-run setup: On the first run you'll be prompted to approve browse cloud fetch, browse cloud search, cat, mkdir, sed, etc. Select "Yes, and don't ask again for: browse cloud fetch:*" (or equivalent) for each. To permanently approve, add these to your ~/.claude/settings.json under permissions.allow:

"Bash(browse:*)", "Bash(bunx:*)", "Bash(bun:*)", "Bash(node:*)",
"Bash(cat:*)", "Bash(mkdir:*)", "Bash(sed:*)", "Bash(head:*)", "Bash(tr:*)", "Bash(rm:*)"

Path rules: Always use full literal paths in Bash — NOT ~ or $HOME. Resolve the home directory once and use it everywhere. When building subagent prompts, replace {SKILL_DIR} with the full literal path.

Output directory: All output goes to ~/Desktop/{company_slug}_competitors_{YYYY-MM-DD}/. This directory contains one .md file per competitor plus the generated HTML views and CSV.

CRITICAL — Tool restrictions (applies to main agent AND all subagents):

  • All web searches: use browse cloud search. NEVER WebSearch.
  • All page fetches: use browse cloud fetch --allow-redirects (returns markdown by default; add --format raw if you need the original HTML, then pipe through sed ... | tr -s ' \n' to extract text). NEVER WebFetch. 1 MB response limit — fall back to browse get markdown (after browse open <url> --remote) for JS-heavy pages.
  • All research output: subagents write one markdown file per competitor to {OUTPUT_DIR}/{competitor-slug}.md using bash heredoc. NEVER use the Write tool or python3 -c. See references/example-research.md for the file format.
  • Report compilation: use node {SKILL_DIR}/scripts/compile_report.mjs {OUTPUT_DIR} --user-company "{user_company}" --open — generates index.html, competitors/*.html, matrix.html, mentions.html, results.csv in one step and opens overview.
  • URL deduplication: node {SKILL_DIR}/scripts/list_urls.mjs /tmp --prefix competitor.
  • Subagents must use ONLY the Bash tool.
  • Main agent NEVER reads raw discovery JSON batch files.

CRITICAL — Minimize permission prompts:

  • Subagents MUST batch ALL file writes into a SINGLE Bash call using chained heredocs.
  • Batch ALL searches and ALL fetches into single Bash calls via && chaining.

Pipeline Overview

Follow these 8 steps in order. Do not skip or reorder.

  1. User Company Research — Deeply understand the user's company, produce precise_category + category_include_keywords + exclusion_list
  2. Depth Mode + Seed Input — Choose depth, accept optional seed competitor URLs
  3. Discovery (3 parallel waves) — Wave A (alternatives), Wave B (precise category), Wave C (comparison-page graph via "X vs Y" title parsing)
  4. Gatescripts/gate_candidates.mjs fetches each candidate's hero text (via browse cloud fetch) and drops wrong-category URLs
  5. Confirm enrichment set with the user — Present PASS / UNKNOWN / rejected-brand-matches via AskUserQuestion. User ticks the real ones, adds any the discovery missed. Skipping this step is wasteful because enrichment is expensive (25 subagents × depth budget) and the gate is imperfect (JS-heavy homepages, Cloudflare challenges, semantic-variant taglines)
  6. Deep Enrichment (5 subagents per competitor in deep/deeper modes) — Marketing, Discussion, Social, News, Technical — each lane a separate subagent writing to partials/; then merge_partials.mjs consolidates. In deep/deeper modes, Step 5d adds a 6th Battle Card synthesis lane AFTER Step 5c fact-check completes — produces per-competitor Landmines / Objection Handlers / Talk Tracks grounded in cited evidence.
  7. Screenshotscapture_screenshots.mjs via the browse CLI captures a 1280×800 homepage hero per competitor
  8. HTML Report — Overview + per-competitor (with embedded hero screenshot + Battle Card card) + matrix + mentions views

Step 0: Setup Output Directory

OUTPUT_DIR=~/Desktop/{company_slug}_competitors_{YYYY-MM-DD}
mkdir -p "$OUTPUT_DIR"

Replace {company_slug} with the user's company name (lowercase, hyphenated) and {YYYY-MM-DD} with today's date. Pass {OUTPUT_DIR} as a full literal path to every subagent.

Clean up discovery batch files from prior runs:

rm -f /tmp/competitor_discovery_batch_*.json

Re-runs must start from a clean $OUTPUT_DIR. compile_report.mjs ingests every {slug}.md in the directory, and merge_partials.mjs only overwrites the slugs in the current set — it never deletes ones dropped from a new enrichment set. Since the directory is keyed by date, a same-day re-run with a different competitor set would leave stale competitors in the overview, matrix, CSV, and screenshots. Either use a fresh directory or clear the prior per-competitor files first:

rm -f "$OUTPUT_DIR"/*.md && rm -rf "$OUTPUT_DIR"/partials "$OUTPUT_DIR"/screenshots

Step 1: User Company Research

This step sets the baseline for what "competitor" means AND produces the verified data the Step 5b matrix will use for the userCompany row.

Rule: The user's company gets the same 5-lane research depth as competitors. Do NOT fill userCompany in matrix.json from memory — it will ship false claims to the user's own team. On a search-API run (user company Exa, 2026-04-23), skipping this step produced a matrix that claimed Exa had a "published uptime SLA" (there is no numeric public SLA — only a status page) and marked its MIT-licensed Python SDK as open-source: false (the repo is github.com/exa-labs/exa-py, LICENSE confirmed MIT). Both errors would have surfaced in the "Where you're winning" card as fabricated moats.

Process:

  1. Ask the user for their company name or URL.

  2. Check for an existing profile at {SKILL_DIR}/profiles/{company-slug}.json. If it exists, load it and confirm with the user: "I have your profile from {researched_at}. Still accurate?" — if yes, skip to Step 2 BUT still run the partial-lane enrichment below so matrix synthesis has fresh feature evidence. The profile format is shared with company-research (same shape). If a user already has a profile saved under company-research/profiles/, you may copy it into this skill's profiles directory rather than re-researching.

  3. Run the full 5-lane enrichment on the user's company — identical to the competitor pattern in Step 5. For each lane, spawn a Bash-only subagent that writes to {OUTPUT_DIR}/partials/{user-slug}.{lane}.md:

    • marketing — tagline, positioning, pricing tiers, features, integrations, open-source components (SDK repos + licenses), regions offered, compliance (SOC 2 / HIPAA / trust portal URL)
    • technical — REST + streaming API support (with docs URLs), SDK languages, MCP server URL, neural vs keyword retrieval modes, reranking / highlights / live-crawl specifics, published uptime SLA (actual %, not status page), third-party retrieval-quality benchmarks
    • discussion, social, news — optional in quick mode, recommended in deep+ See references/research-patterns.md → "Self-Research" for sub-questions. Each finding MUST cite a URL.
  4. Run merge_partials.mjs on the user's partials too — produces {OUTPUT_DIR}/{user-slug}.md, the canonical source Step 5b reads from for userCompany flags.

  5. Synthesize into a profile: Company, Product, Existing Customers, Competitors (seed list), Use Cases, precise_category, category_include_keywords, exclusion_list. Do NOT include ICP — this skill doesn't need it.

    • precise_category: one sentence describing the category. e.g., "AI web search API for agents with neural + keyword retrieval". Avoid vague words like "tools" / "platform".
    • category_include_keywords: 8-15 phrases a direct competitor's marketing would likely contain (hero or title). Include semantic variants.
    • exclusion_list: phrases that indicate a different category — used by the gate to reject false positives (e.g. antidetect browser, scraping api, screenshot api, residential proxy). See references/research-patterns.md → "Synthesis Output" for the exact format and Exa as a worked example.
  6. Present the profile + the user-company .md to the user for confirmation. Do not proceed until confirmed.

  7. Save the confirmed profile to {SKILL_DIR}/profiles/{company-slug}.json.

Step 2: Depth Mode + Seed Input

Ask clarifying questions via AskUserQuestion with checkboxes:

  • Known competitors? Text area for URLs/names (optional — discovery will find more).
  • Depth mode?
    • quick — marketing surface only, many competitors, ~2-3 tool calls each
    • deep — + external signal (mentions, reviews, news), ~5-8 tool calls each
    • deeper — + public benchmarks + strategic diff vs user's company, ~10-15 tool calls each
  • Target count? Rough number of competitors to research (e.g., 10 / 20 / 50).

This is the ONLY user interaction. After this, execute silently until the report is ready.

ModeResearch per competitorBest for
quickLane 1 only (homepage + pricing)Scanning ~30-50 competitors fast
deepLanes 1+2~15-25 competitors with external signal
deeperAll 4 lanes (+ benchmarks + strategic diff)~5-15 competitors with full intel

Step 3: Discovery (3 parallel waves)

Formula: ceil(target_count / 20) queries per wave. Over-discover ~3x because the gate drops ~40-60%.

Evaluation on a search-API run shows all three waves are additive — skip any and you lose real competitors:

Wave A — Generic alternatives (broad; heavy aggregator noise, filtered out later)

  • "alternatives to {user_company}"
  • "{user_company} competitors"

Wave B — Precise category (uses precise_category from the profile)

  • "{precise_category}" verbatim
  • 2-3 queries composed from the most distinctive tokens (e.g. "web search api for ai agents", "retrieval API for LLMs")

Wave C — Comparison-page graph (highest precision)

  • "{user_company} vs"
  • "{seed1} vs", "{seed2} vs", "{seed3} vs" (seeds from the profile's competitors list)
  • After the searches, run scripts/extract_vs_names.mjs to parse "X vs Y" patterns from result titles — this uniquely surfaces competitors that don't appear as URL hits.

Process:

  1. Issue 3 parallel browse cloud search Bash calls (one per wave) in a SINGLE message — NOT subagents. Each Bash call chains its 2-4 queries with &&. See references/workflow.md → "Discovery — parallel Bash, not subagents" for the exact recipe. Subagents are too heavy for a workload of 6-12 browse cloud search calls.
  2. After all waves complete:
    node {SKILL_DIR}/scripts/list_urls.mjs /tmp --prefix competitor > /tmp/competitor_urls.txt
    node {SKILL_DIR}/scripts/extract_vs_names.mjs /tmp --prefix competitor \
      --seed "{user_company},{seed1},{seed2},{seed3}" \
      > /tmp/competitor_vs_names.jsonl
    
  3. Filter /tmp/competitor_urls.txt — remove blog posts, news, AI-tool directories (seektool.ai, respan.ai, agentsindex.ai, toolradar.com, aitoolsatlas.ai, vibecodedthis.com, etc.), review aggregators (g2.com, capterra.com), databases (crunchbase.com, tracxn.com), user's own domain. See references/workflow.md for the full noise-domain list.
  4. For vs_names entries that have a resolved domain, add them. For unresolved names, optionally run browse cloud search "{name}" --num-results 3 and pick the top root domain.
  5. Merge with user-provided seed URLs. Dedup by hostname → /tmp/competitor_candidates.txt.

Step 4: Gate (category-fit filter)

Drop candidates whose marketing identifies them as a different category before enrichment burns tool calls on them.

cat /tmp/competitor_candidates.txt \
  | node {SKILL_DIR}/scripts/gate_candidates.mjs \
      --include "{profile.category_include_keywords joined with commas}" \
      --exclude "{profile.exclusion_list joined with commas}" \
      --concurrency 6 \
  > /tmp/competitor_gated.jsonl

grep '"status":"PASS"' /tmp/competitor_gated.jsonl \
  | node -e 'require("fs").readFileSync(0,"utf-8").split("\n").filter(Boolean).forEach(l => { try { console.log(JSON.parse(l).url); } catch {} })' \
  > /tmp/competitor_passed.txt

The gate fetches each candidate's homepage via browse cloud fetch --allow-redirects --format raw, extracts the first 800 chars of visible text, and classifies position-aware: exclude in <title> → REJECT; include in <title> → PASS; hybrid title → hero200 tiebreak; otherwise fall through.

Evaluated on a search-API run with 12 mixed candidates: 7/7 real competitors passed, 4/4 wrong-category rejected, 1 known-hybrid edge case rejected.

Step 4.5: Confirm enrichment set with the user

This step is mandatory. Do NOT skip to enrichment just because the gate ran.

Enrichment is expensive: 5 competitors × 5 lane-subagents = 25 subagents, ~10-15 minutes of wall clock, ~300 browse cloud calls. Running it on the wrong set wastes all of that. The gate also has known blind spots:

  • JS-heavy homepages (e.g. Tavily, Firecrawl) — browse cloud fetch returns near-empty text, so keyword matching has nothing to match on → REJECT or UNKNOWN
  • Cloudflare challenge pages (e.g. Perplexity) — title becomes "Just a moment..." → no category signal
  • Semantic variants — "search foundation" / "retrieval backbone" don't lexically match a list centered on "search API"
  • Domain ambiguitybrave.com (the browser) vs api-dashboard.search.brave.com (the actual API product) can confuse classification

The user almost always has domain knowledge the skill lacks. Ask them.

Process — the main agent:

  1. Read /tmp/competitor_gated.jsonl and group rows:

    • PASS bucket: everything with status=PASS.
    • UNKNOWN bucket: status=UNKNOWN (fetch failed — always surface, these are the silent misses).
    • Rejected-brand bucket: top ~10 REJECT rows whose title mentions a well-known brand pattern (e.g. contains the token from a user-supplied seed list, or appears frequently in the Wave C "X vs Y" graph).
  2. Present the buckets to the user, one table per bucket, with URL + title + reason (for rejects).

  3. Use AskUserQuestion with a checkbox list of all candidates across the three buckets, plus a free-text "add more" field. The prompt should be explicit:

    "Here are the gate's picks plus a few it was unsure about. Tick the ones that are real competitors in your space, and paste any URLs I missed (comma-separated). Enrichment will run on ONLY the ticked set."

  4. Write the confirmed set to /tmp/competitor_enrichment_set.txt (one URL per line). This is the input for Step 5 — not /tmp/competitor_passed.txt.

If the user doesn't respond or explicitly says "just run it", fall back to /tmp/competitor_passed.txt as-is, but warn in chat that the run may waste budget on wrong-category hits.

Exa test, 2026-04-24: gate auto-passed 22 of 101 candidates but missed Tavily (generic title), Jina AI (semantic mismatch — "search foundation"), Firecrawl (JS-heavy fetch failure), and Perplexity (Cloudflare challenge). All four are real direct competitors. This step catches them.

Step 5: Deep Enrichment

Two modes. See references/workflow.md for prompt templates and wave management. See references/research-patterns.md for the lane-by-lane methodology.

Quick mode — single subagent per batch

  • Input: /tmp/competitor_enrichment_set.txt (user-confirmed set from Step 4.5), ~8 competitors per subagent.
  • One subagent runs Lane A only (marketing surface). 2-3 tool calls each.
  • Writes directly to {OUTPUT_DIR}/{slug}.md.

Deep / Deeper mode — 5 subagents PER competitor (parallel lane fan-out)

For each competitor, launch 5 parallel subagents, one per lane:

  • A. Marketing (marketing): pricing, features, positioning, integrations, customers, team, funding, HQ. Owns canonical frontmatter.
  • B. Discussion (discussion): Reddit, HN, forums, Dev.to, Hashnode. Broad queries beyond site: — also "{competitor}" review 2026, "{competitor}" issues OR problems, "{competitor}" discussion.
  • C. Social (social): LinkedIn posts, YouTube videos, Twitter/X. Snippets only — do NOT fetch.
  • D. News & Comparisons (news): TechCrunch, Verge, VentureBeat, Forbes, Businesswire, Substack, blog reviews. Every mention needs a date.
  • E. Technical & Benchmarks (technical): GitHub benchmark repos/PRs, performance posts. Writes Benchmarks + technical Findings.

Budget per lane: deep = 5-8 tool calls, deeper = 10-15. Launch ALL competitor × lane subagents in a SINGLE Agent tool message. For 10 competitors × 5 lanes = 50 parallel Agent calls in one message. Do NOT split into batches per competitor or per lane — wall clock collapses to the slowest single agent (~3-5 min). Splitting into 5 rounds of 10 cost 25 minutes of wall clock vs 5 minutes parallel on a real measured run; do not do it.

Each subagent writes a partial to {OUTPUT_DIR}/partials/{slug}.{lane}.md.

Critical: Pass the user's company name, product, and key features verbatim into every subagent prompt so the technical lane can do strategic diffing. Pass the full literal {OUTPUT_DIR} path to every subagent.

Merge partials → canonical per-competitor file

After all subagents for all competitors complete:

node {SKILL_DIR}/scripts/merge_partials.mjs {OUTPUT_DIR}

Unions the 5 partials per competitor into one {OUTPUT_DIR}/{slug}.md — dedup'd Mentions (sorted by date desc), dedup'd Benchmarks, merged Findings, canonical frontmatter from the marketing lane.

Synthesize the comparison matrix (write matrix.json)

Subagents write key_features and integrations as prose, not as pipe-separated atomic feature labels. So a naive |-split axis becomes one-blob-per-competitor with no overlap — the rendered matrix shows a useless diagonal.

The main agent fixes this by synthesizing a shared taxonomy across competitors and writing {OUTPUT_DIR}/matrix.json. compile_report.mjs auto-detects this file and renders the matrix from it instead of from the pipe split.

Process — main agent:

  1. Read ALL {slug}.md files, INCLUDING the user's company file {user-slug}.md produced in Step 1. The user is competitor #0 for matrix purposes — treat with identical rigor.

  2. Produce a canonical list of 12-20 atomic features — each must be a yes/no proposition a competitor either has or doesn't (e.g. "MCP server", "SOC 2", "Site crawler", "Reranker"). Avoid sentence-length features. Avoid features only one competitor has.

  3. Produce a canonical list of 10-20 integrations (frameworks, marketplaces, SDK languages).

  4. For each company INCLUDING THE USER, map each taxonomy entry to true / false based on the enrichment data in their .md file. Every flag must be traceable to a Research Findings bullet with a cited URL. If the user's file says "exa-py MIT-licensed (github.com/exa-labs/exa-py)", the Open-source feature is true with that URL as the source. If not mentioned, leave false.

  5. Write the result to {OUTPUT_DIR}/matrix.json in this shape:

    {
      "category": "AI search APIs",
      "features": [{ "name": "Web Search API", "description": "..." }, ...],
      "integrations": [{ "name": "LangChain" }, ...],
      "userCompany": {
        "name": "Exa",
        "winningSummary": "Exa's moats are its first-party neural index and the integrated Research API — no one else in the set ships a semantic/embeddings-native retrieval primitive alongside a multi-step agentic research endpoint. It's also the only provider with a crawler product bundled in, and ties with SerpAPI on breadth of SDK language coverage.",
        "losingSummary": "Exa trails competitors on operational transparency — SerpAPI, Serper, and Tavily all publish hourly throughput SLAs, and Exa lacks a dedicated news endpoint that SerpAPI, Serper, and You.com all ship. Image/visual search is also missing vs 4 of 5 competitors.",
        "features": { "Web Search API": true, "Site crawler": true, ... },
        "integrations": { "LangChain": true, ... }
      },
      "competitors": {
        "tavily": {
          "features": { "Web Search API": true, "Site crawler": true, ... },
          "integrations": { "LangChain": true, "Databricks Marketplace": true, ... }
        },
        "serpapi": { "features": {...}, "integrations": {...} }
      }
    }
    

    userCompany is required. The overview page renders two cards — "Where {user} is winning" and "Where {user} is losing". Populate userCompany.features and userCompany.integrations from the self-research profile (Step 1). Without this field those two cards don't render.

    Write order (two passes — this resolves the apparent ordering tension below). In this step (5b) write all features / integrations cells for userCompany and every competitor, plus a draft winningSummary / losingSummary. The drafts exist only to tell the Step 5c fact-checker which claims are high-stakes (it prioritizes cells named in the summaries). After Step 5c flips cells on verified evidence, rewrite the two summaries so the prose reflects only fact-checked cells. The JSON shape above shows the finalized post-fact-check object.

    userCompany.winningSummary / losingSummary are strongly preferred (analyst-style prose, 2-4 sentences each). When present, the cards render as paragraphs instead of bulleted lists — reads like a briefing, not a spreadsheet. If absent, the cards fall back to a bulleted list of winning/losing items with who-else-has-it.

If this step is skipped, the matrix view falls back to the raw pipe-split axis (useless for atomic comparison) and the strategic summary doesn't render. Do not skip.

Fact-check the matrix — spot-check the high-stakes cells (default)

Do not trust the taxonomy pass alone for high-stakes cells. It is LLM inference from prose and will hallucinate moats. Observed during a search-API run (2026-04-23): matrix.json claimed SOC 2 was unique to the user's company; verification showed three of the other competitors also have SOC 2 Type II.

But verifying every cell is the opposite mistake. A 7-company × 33-axis matrix has 231 cells. The Apr 2026 search-API run got stuck at 111+ tool calls in fact-check before interrupt — the subagent kept going on table-stakes cells (REST API, JSON responses, Python SDK) that are universal in the category.

Default = spot-check, not full sweep. Only verify cells that meaningfully change the strategic narrative.

Launch a single fact-check subagent (Bash-only) with a hard 25-call budget that targets ONLY these high-stakes axes:

  1. Every userCompany.features and userCompany.integrations cell (the user's own moats — these go straight into "Where you're winning" prose). Typical: 17 + 16 = 33 cells, but most are obvious (your own product). Focus on:

    • Anything claimed as a moat in winningSummary
    • Anything claimed as a gap in losingSummary
    • Compliance (SOC 2, HIPAA, ISO 27001, GDPR)
    • Open-source license claims (MIT / Apache 2.0 / AGPL — observed wrong on a competitor's SDK)
    • Published uptime SLA (status page ≠ SLA)
  2. Across competitors, only the cells that drive the win/loss summary:

    • For each "Winning" claim, verify the user has it AND verify the competitors don't.
    • For each "Losing" claim, verify the named competitors do have it.
    • Compliance + license + SLA across all competitors (high-trust, frequently wrong).
  3. Do NOT verify:

    • Universal table-stakes (REST API, JSON responses, Python SDK, API-key auth) — every search API has these.
    • false cells with no claim being made (no moat lost or won).
    • Integration cells unless they appear in the win/loss summary.
You are a matrix spot-check subagent. Budget: 25 browse cloud calls TOTAL across all cells.
Stop and return what you have when you hit the budget — partial fact-check is
better than blocking the rest of the pipeline.

TOOL RULES: Bash ONLY. browse cloud search + browse cloud fetch. Count your calls; stop at 25.

PRIORITY ORDER (highest-stakes first — work down until budget):
1. Every cell that appears in userCompany.winningSummary or losingSummary
2. Compliance cells (SOC 2, HIPAA, ISO 27001) for user + every competitor
3. Open-source / self-hostable + license cells across all competitors
4. Pricing tier numbers ($X/mo, /hr) for user + competitors named in summaries
5. Funding / employee_estimate fields (only if cited in summaries)

Skip:
- Universal cells (REST API, JSON responses, Python SDK, API-key auth, etc.)
- `false` cells where no claim is being made
- Integration matrix cells unless they appear in summaries

For each cell verified:
- If `true` — find one source URL (docs, trust portal, GitHub LICENSE, etc).
- If `false` — one targeted browse cloud search. Flip ONLY on first-party evidence.

Output: matrix.json with `sources: { "Feature": "https://..." }` on the
verified cells (other cells stay as-is). Cells-changed log to
{OUTPUT_DIR}/matrix_fact_check.md with each flip + URL + quoted evidence.
Report back: "spot-check: N cells verified, M flipped, B/25 budget used".

Full-sweep mode (opt-in, slower): if the user explicitly says "full fact check" or for a high-stakes deliverable (board deck, press release), set the budget to 80 calls and verify every non-universal cell. Default is spot-check.

After the subagent completes, re-read matrix.json, recompile, and surface matrix_fact_check.md delta to the user. The summary is much more trustworthy with spot-check than without — and ships in 3-5 minutes instead of stalling the pipeline.

Step 5d: Battle Card synthesis (deep/deeper only, after Step 5c)

Depends on fact-checked matrix.json from Step 5c. This is a sales-enablement lane. For each competitor, launch a Bash-only synthesis subagent (no new browse cloud calls) that reads all 5 existing partials + the user's merged .md + fact-checked matrix.json, and produces per-competitor Landmines / Objection Handlers / Talk Tracks grounded in cited evidence.

Prompt template: references/battle-card-subagent.md (substitute {COMPETITOR_SLUG} / {COMPETITOR_NAME} / {USER_COMPANY_NAME} / {USER_WINNING_SUMMARY} per competitor). Format spec: references/battle-card.md.

Output: {OUTPUT_DIR}/partials/{slug}.battle.md with a ## Battle Card section.

Re-run the merge after this lane completes. The Step 5 merge ran before the battle partials existed, so the consolidated {slug}.md files don't contain them yet. Re-run:

node {SKILL_DIR}/scripts/merge_partials.mjs {OUTPUT_DIR}

This unions each {slug}.battle.md into its consolidated {slug}.md (the battle lane is already handled by merge_partials.mjs). compile_report.mjs reads the ## Battle Card section from {slug}.md and renders it as a brand-accented card on the per-competitor HTML page. Skip this re-merge and the battle cards never appear in the report.

Why this lane is synthesis-only — battle cards must be grounded in facts that already survived Step 5c. Letting the subagent do fresh browse cloud searches would reintroduce the hallucinated-moat problem the fact-check step exists to prevent. The subagent's adversarial self-check explicitly rejects claims not traceable to an input partial bullet or a sources-backed matrix cell.

Parallelism: 1 subagent per competitor, all in one Agent-tool message (synthesis is fast, ~3-5 Bash calls per subagent). Skip this step in quick mode — there isn't enough research depth to ground the cards credibly.

Step 6: Screenshots

Capture a homepage hero screenshot per competitor:

node {SKILL_DIR}/scripts/capture_screenshots.mjs {OUTPUT_DIR} --mode remote

Uses the browse CLI (npm install -g browse). The --mode flag selects the browser session: remote (default) drives a Browserbase session — best for protected/bot-detecting homepages and the only option without local Chrome; local uses Chrome on your machine. The script passes the corresponding --remote / --local flag on each browse command, so there is no separate environment-config step to run. Writes one PNG per competitor to {OUTPUT_DIR}/screenshots/{slug}-hero.png. The compile step in Step 7 auto-embeds the hero on each per-competitor HTML page.

Cost: ~10-20s per competitor. ~60s for 5 competitors.

Step 7: HTML Report

  1. Generate all views + CSV (opens overview in browser):

    node {SKILL_DIR}/scripts/compile_report.mjs {OUTPUT_DIR} --user-company "{user_company}" --open
    

    Produces:

    • {OUTPUT_DIR}/index.html — overview: competitor table with tagline, pricing summary, key features, strategic diff
    • {OUTPUT_DIR}/competitors/{slug}.html — per-competitor deep dive (all sections)
    • {OUTPUT_DIR}/matrix.html — side-by-side feature/pricing matrix
    • {OUTPUT_DIR}/mentions.html — chronological feed with source-type pills + client-side filter
    • {OUTPUT_DIR}/results.csv — flat spreadsheet
  2. Present a chat summary:

## Competitor Analysis Complete

- **Competitors researched**: {count}
- **Depth mode**: {mode}
- **Mentions collected**: {total mentions} across {source types count} source types
- **Public benchmarks found**: {count}
- **Opened in browser**: ~/Desktop/{company_slug}_competitors_{date}/index.html
  1. Show the overview table in chat:
| Competitor | Positioning | Pricing | Key Features | Strategic Diff |
|------------|-------------|---------|--------------|----------------|
| Rival Co | AI-native web search API | $99/mo entry | semantic search, reranking, crawler | Similar retrieval; cheaper entry |
  1. Call out the top 3-5 most interesting findings — e.g., "3 competitors have public benchmarks; Rival Co is cheapest; Foo Inc launched a dedicated news-search endpoint 2 weeks ago." Offer to dig deeper into any specific competitor or re-run with different depth.

Supporting file: LICENSE.txt

MIT License

Copyright (c) 2026 Browserbase, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


Supporting file: references/battle-card-subagent.md

Battle Card subagent prompt

Contents

Main agent substitutes placeholders per competitor. Launch AFTER Step 5c fact-check completes — this lane depends on matrix.json cells having sources URLs.

Placeholders to substitute

  • {OUTPUT_DIR} → the absolute path to the output directory (the per-run directory from Step 0; no ~ or $HOME)
  • {COMPETITOR_SLUG} → e.g. rival-co
  • {COMPETITOR_NAME} → e.g. Rival Co
  • {USER_SLUG} → e.g. exa
  • {USER_COMPANY_NAME} → e.g. Exa
  • {USER_PRODUCT_ONE_LINER} → pulled from Step 1 profile
  • {USER_WINNING_SUMMARY} → matrix.json userCompany.winningSummary
  • {USER_LOSING_SUMMARY} → matrix.json userCompany.losingSummary

Prompt

You are the Battle Card synthesis subagent. Produce an evidence-grounded
battle card a real AE would use on a call.

TOOL RULES — CRITICAL, FOLLOW EXACTLY:
1. You may ONLY use the Bash tool. No exceptions.
2. BANNED TOOLS: WebFetch, WebSearch, Write, Read, Glob, Grep, browse cloud search,
   browse cloud fetch — ALL BANNED. This is a SYNTHESIS lane, not a research lane.
   You read files that already exist; you do not make new network calls.
3. Read ALL inputs in ONE Bash call via `cat`. Write output in ONE heredoc.
4. NEVER use ~ or $HOME — full literal paths only.

INPUTS (all already exist on disk — read in one Bash call):
- {OUTPUT_DIR}/partials/{COMPETITOR_SLUG}.marketing.md
- {OUTPUT_DIR}/partials/{COMPETITOR_SLUG}.discussion.md
- {OUTPUT_DIR}/partials/{COMPETITOR_SLUG}.social.md
- {OUTPUT_DIR}/partials/{COMPETITOR_SLUG}.news.md
- {OUTPUT_DIR}/partials/{COMPETITOR_SLUG}.technical.md
- {OUTPUT_DIR}/{USER_SLUG}.md              # user's own merged file
- {OUTPUT_DIR}/matrix.json                 # fact-checked matrix — cells
                                           # must have a `sources` URL to
                                           # be trustworthy; reject any
                                           # cell without one

CONTEXT:
- User's company: {USER_COMPANY_NAME}
- User's product: {USER_PRODUCT_ONE_LINER}
- User's verified moats (from matrix.json userCompany.winningSummary):
  {USER_WINNING_SUMMARY}
- User's verified gaps (from matrix.json userCompany.losingSummary):
  {USER_LOSING_SUMMARY}
- Competitor: {COMPETITOR_NAME}
- Competitor slug: {COMPETITOR_SLUG}

TASK — produce three sections, every claim traceable to an input bullet
or matrix.sources URL:

1. LANDMINES (3-5 items) — concrete verifiable facts that HURT
   {COMPETITOR_NAME} in a deal. Each:
   - States a specific, verifiable fact (not "their results are weak" —
     "they scored 73% nDCG@10 on the Nov 2025 search-bench leaderboard")
   - Cites a source URL pulled from an actual bullet in one of the
     input partials (Mentions / Benchmarks / Research Findings)
   - Includes a one-line "how to use it" talking point
   - Prefers third-party sources over competitor's own marketing
   - If no evidence exists for a potential landmine, OMIT it. 3 cited
     landmines > 5 half-invented ones.

2. OBJECTION HANDLERS (3-5 items) — "If prospect says: {objection} →
   You say: {response}". Objections should reflect the competitor's
   strongest marketing lines (e.g. if their homepage says "99.99%
   uptime", the objection is "we hear {user} has no uptime guarantee").
   Responses must reference a real user moat from winningSummary —
   never a hallucinated feature.

3. TALK TRACKS (2-3 items) — 1-2 sentence opening pitches. Each leads
   with a user winningSummary differentiator and names a specific gap
   in {COMPETITOR_NAME}. Confident, factual, no hyperbole.

ADVERSARIAL SELF-CHECK before writing:
- [ ] Every landmine cites a URL that appears in one of the input
      partials. No invented URLs.
- [ ] No claim contradicts a fact-checked cell in matrix.json.
- [ ] No talk track claims a user feature where matrix.json shows
      userCompany.features[X] = false.
- [ ] Objections are realistic (what a prospect would actually raise),
      not strawmen.

OUTPUT — write via a single heredoc to
  {OUTPUT_DIR}/partials/{COMPETITOR_SLUG}.battle.md

cat << 'BATTLE_MD' > {OUTPUT_DIR}/partials/{COMPETITOR_SLUG}.battle.md
---
competitor_name: {COMPETITOR_NAME}
lane: battle
generated_at: {YYYY-MM-DD}
---

## Battle Card

### Landmines

- **{one-line fact}** — {how to use it in the call}. (source: {url})

### Objection Handlers

- If they say: "{objection verbatim}"
  You say: {response citing user's moat} (evidence: {url})

### Talk Tracks

1. {1-2 sentence pitch}
BATTLE_MD

REPORT BACK only one line:
  "{COMPETITOR_SLUG} battle: {N} landmines, {M} objections, {K} tracks, all cited."

Do NOT return the card content.

Wave management

  • Launch 1 battle-card subagent per competitor. All can run in parallel (synthesis is fast and uses no shared state beyond already-written partials).
  • Depth: only run in deep or deeper modes. quick mode does not have the research depth to ground battle cards credibly.
  • Budget: ~3-5 Bash calls per subagent (1 big cat, 1 big heredoc, maybe 1-2 sanity checks).

Supporting file: references/battle-card.md

Battle Card — format spec

The Battle lane is the 6th subagent lane in deep/deeper mode. It runs AFTER Step 5c fact-check completes — it reads only existing partials + the fact-checked matrix.json, never makes new browse cloud calls. This is a pure synthesis lane.

Output file: {OUTPUT_DIR}/partials/{slug}.battle.md. merge_partials.mjs unions its ## Battle Card section into the consolidated {slug}.md. compile_report.mjs renders it as a brand-accented card on the per-competitor HTML page.

The three sections

Landmines (3-5 items)

Concrete, verifiable facts about the competitor that hurt them in a deal. Every item must cite a URL from an existing partial (Mentions, Benchmarks, or Research Findings). Prefer third-party evidence (benchmarks, reviews, news) over the competitor's own marketing — marketing claims are weak ammunition.

Format:

### Landmines

- **{one-line factual claim}** — {how an AE uses it in the call}. (source: {url})

Example:

- **Rival Co placed 4th of 7 on the Nov 2025 search-bench retrieval leaderboard (73% nDCG@10)** — use if prospect cares about relevance, but only after confirming their volume tier; Rival Co's reranking add-on is paywalled behind Scale ($499/mo). (source: https://github.com/example-org/search-bench)

Objection Handlers (3-5 items)

Format: "if prospect says X → you say Y, citing a real user moat from userCompany.winningSummary." Every response must reference a feature/integration the fact-checked matrix confirms the user has. Never respond with a claim that contradicts a fact-checked matrix cell.

Format:

### Objection Handlers

- If they say: "{objection verbatim}"
  You say: {response citing user's moat} (evidence: {url})

Example:

- If they say: "Rival Co is $99/mo cheaper than your Scale tier"
  You say: "Rival Co's reranking is a paid add-on you'll need for production relevance — once you add it the price gap closes. Our Scale tier includes neural reranking and a research endpoint; matrix.json confirms Rival Co's feature set doesn't cover the research API." (evidence: https://docs.rivalco.com/changelog)

Talk Tracks (2-3 items)

One-to-two sentence opening pitches an AE can memorize. Lead with a user winningSummary differentiator; name the specific gap in the competitor. No hyperbole, no claims not grounded in fact-checked matrix cells.

Format:

### Talk Tracks

1. {1-2 sentence pitch}

Example:

1. For production RAG, Exa is the only provider in the category with BOTH a first-party neural index AND a dedicated research/answer endpoint — Rival Co shipped neither, Serper shipped neither, and one competitor replaced its answer endpoint with a thin LLM wrapper last quarter.

Markdown file shape

---
competitor_name: Rival Co
lane: battle
generated_at: 2026-04-24
---

## Battle Card

### Landmines
- **Fact 1** — usage. (source: url)
- **Fact 2** — usage. (source: url)

### Objection Handlers
- If they say: "..."
  You say: ... (evidence: url)

### Talk Tracks
1. Pitch 1
2. Pitch 2

Quality gates — Adversarial self-check (subagent MUST run before writing)

  • Every landmine cites a URL that appears in one of the input partials (Mentions / Benchmarks / Research Findings). No invented URLs.
  • No claim contradicts a fact-checked cell in matrix.json (cells must have a sources URL to be trustworthy).
  • No talk track claims a user feature where matrix.json shows userCompany.features[X] = false.
  • Objections are realistic — they're what a prospect would actually raise based on the competitor's strongest marketing lines, not strawmen.
  • Third-party evidence preferred over competitor's own marketing (benchmarks, reviews, news > their docs/pricing).

If a potential landmine has no evidence in the partials, OMIT it. It is better to ship 3 cited landmines than 5 half-invented ones.


Supporting file: references/example-research.md

Example Competitor Research File

Contents

Each enrichment subagent writes one markdown file per competitor to {OUTPUT_DIR}/{competitor-slug}.md, where {OUTPUT_DIR} is the per-run Desktop directory set up by the main agent in Step 0 (e.g., ~/Desktop/acme_competitors_2026-04-23/). The YAML frontmatter contains structured fields for report/matrix compilation. The body contains per-section research plus aggregated mentions and benchmarks.

Template

---
competitor_name: Rival Co
website: https://rivalco.com
tagline: The fastest way to give your agents the web
positioning: Developer-first web search API
product_description: Web search & retrieval API for AI agents and RAG pipelines
target_customer: AI engineers, RAG/agent teams, SaaS companies
pricing_model: Usage-based + seat tiers
pricing_tiers: Free (1K searches) | Pro $99/mo | Scale $499/mo | Enterprise Contact
key_features: web search API | neural/semantic search | site crawler | reranking | live crawl
integrations: LangChain | LlamaIndex | Python SDK | TypeScript SDK
headquarters: San Francisco, CA
founded: 2023
employee_estimate: 11-50
funding_info: Seed, $5M (2024)
strategic_diff: Similar retrieval API; weaker neural relevance, but cheaper entry tier
---

## Product
Web search and retrieval API for AI agents. Exposes a REST search endpoint with both
keyword and semantic/neural modes, plus a site crawler and live-crawl fallback.
Positioned at AI engineers building RAG and agent pipelines.

## Pricing
- Free: 1K searches/month, 1 API key
- Pro ($99/mo): 100K searches, reranking, basic support
- Scale ($499/mo): 1M searches, neural search, live crawl, higher rate limits
- Enterprise: custom pricing, SSO, dedicated support

## Features
- Keyword + neural/semantic search modes
- Site crawler with scheduled recrawls
- Result reranking and content highlights
- Live-crawl fallback for fresh pages
- REST API with JSON responses
- Python and TypeScript SDKs

## Positioning
Marketing emphasizes "AI-native" and developer-first DX. Landing page hero:
"Give your agents the web." Targets solo devs through mid-market AI teams.

## Comparison vs {user_company}
- **Overlaps**: Web search API, neural search mode, crawler, LangChain integration
- **Gaps**: No dedicated research/answer endpoint, weaker neural relevance benchmarks, no news endpoint
- **Where they win**: Lower entry price ($99 vs $199), simpler pricing tiers
- **Where you win**: Stronger neural relevance (per public benchmarks), research API, larger integration ecosystem

## Mentions
- **[Benchmark]** retrieval-quality leaderboard — Rival Co 73% nDCG@10, 4th of 7 tested (source: https://github.com/example-org/search-bench/pull/92, 2026-03-14)
- **[Comparison]** Exa vs Rival Co — side-by-side review (source: https://example.com/exa-vs-rivalco, 2026-02-01)
- **[Reddit]** r/LangChain thread: "Moved from Rival Co to X after relevance issues" — 24 upvotes (source: https://reddit.com/r/LangChain/comments/abc123)
- **[HN]** "Show HN: Rival Co raises seed to build..." — 112 points, 48 comments (source: https://news.ycombinator.com/item?id=12345)
- **[LinkedIn]** CEO post on product launch — 412 reactions (source: https://linkedin.com/posts/rivalco-launch)
- **[YouTube]** "Rival Co vs Exa" review by Dev YouTuber — 8.2K views (source: https://youtube.com/watch?v=xyz)
- **[News]** TechCrunch coverage of seed round (source: https://techcrunch.com/2024/11/rival-co-seed)
- **[Review]** G2 4.3/5 (31 reviews), main complaint: stale results (source: https://g2.com/products/rival-co)

## Benchmarks
- **search-bench PR #92** — Rival Co 73% nDCG@10 on retrieval quality, 4th of 7 tested (https://github.com/example-org/search-bench/pull/92)
- **retrieval-latency blog** — Rival Co 480ms p50, 2nd fastest (https://example.com/search-latency-2026)

## Research Findings
- **[high]** Usage-based pricing starts at $99/mo for 100K searches (source: rivalco.com/pricing)
- **[high]** Series seed, $5M raised Nov 2024 (source: TechCrunch)
- **[medium]** CEO LinkedIn emphasizes AI-agent use cases (source: linkedin.com/in/rivalco-ceo)
- **[low]** Possibly a team under 20 based on careers page (source: rivalco.com/careers)

## Battle Card

### Landmines
- **Rival Co scores 73% nDCG@10 on the search-bench leaderboard (4th of 7 tested)** — use against relevance-sensitive prospects; they rank below Exa on the same test. (source: https://github.com/example-org/search-bench/pull/92)
- **G2 average 4.3/5 with "stale results" as top complaint across 31 reviews** — cite when prospect raises freshness concerns. (source: https://g2.com/products/rival-co)

### Objection Handlers
- If they say: "Rival Co is $99/mo — cheaper than your Pro tier"
  You say: "Cheaper upfront, but compare total cost of poor relevance — their 73% nDCG@10 means more irrelevant results your agent has to filter or re-query, and re-queries aren't free." (evidence: https://github.com/example-org/search-bench/pull/92)

### Talk Tracks
1. For RAG pipelines where relevance drives answer quality, Exa ships a neural index and a dedicated research/answer endpoint as table stakes; Rival Co has neither in their 2024 product set.

Field Rules

  • YAML frontmatter: All structured fields go here. Extracted for matrix + CSV compilation.
  • pricing_tiers: Pipe-separated (|) with tier name + short price. compile_report.mjs parses on | for the matrix view.
  • key_features, integrations: Pipe-separated lists.
  • strategic_diff: One-line summary (shown in overview table).
  • Body sections: ## Product, ## Pricing, ## Features, ## Positioning, ## Comparison vs {user_company}, ## Mentions, ## Benchmarks, ## Research Findings, ## Battle Card (deep/deeper modes only; synthesized by the Battle lane after fact-check).
  • Mentions format: - **[SourceType]** title | snippet (source: url, date)SourceType is one of Benchmark, Comparison, News, Reddit, HN, LinkedIn, YouTube, Review, Podcast, X.
  • Findings format: - **[confidence]** fact (source: url)confidence is high, medium, or low.
  • Filename: {OUTPUT_DIR}/{competitor-slug}.md where slug is lowercase, hyphenated.

Writing via Bash Heredoc

Subagents write these files using bash heredoc to avoid security prompts. Use the full literal {OUTPUT_DIR} path — no ~ or $HOME:

cat << 'COMPETITOR_MD' > {OUTPUT_DIR}/rival-co.md
---
competitor_name: Rival Co
website: https://rivalco.com
...
---

## Product
...

## Pricing
...

## Mentions
- **[Benchmark]** ...
COMPETITOR_MD

Use 'COMPETITOR_MD' (quoted) as the delimiter to prevent shell variable expansion.

IMPORTANT: Write ALL competitor files in a SINGLE Bash call using chained heredocs to minimize permission prompts.


Supporting file: references/research-patterns.md

Competitor Analysis — Research Patterns

Contents

Overview

Two research contexts:

  1. Self-Research (Step 1) — Deep research on the user's company so we know what "competitor" means for this run.
  2. Competitor Research (Step 4) — For each discovered/seeded competitor, run the 4-lane enrichment below.

Both use the Plan → Research → Synthesize pattern. Self-research is identical in shape to the one in company-research, so profiles can be reused across skills.

Self-Research (User's Company)

Sub-Questions

  • "What does {company} sell and what specific problem does it solve?"
  • "Who are {company}'s existing customers? What industries, company sizes, use cases?"
  • "Who are {company}'s known competitors? What category do they compete in?"
  • "What pricing model does {company} use?"
  • "What features, integrations, and differentiators does {company}'s marketing emphasize?"

Page Discovery

Dynamic via sitemap — do NOT hardcode /about or /pricing:

  1. browse cloud fetch --allow-redirects "{company website}/sitemap.xml" — primary source
  2. Scan for URLs with keywords: pricing, customer, compare, vs, about, features, integrations
  3. Optionally fetch /llms.txt for page descriptions
  4. Pick 3-5 most relevant URLs

External Research

  • browse cloud search "{company} alternatives competitors vs"
  • browse cloud search "{company} review comparison"
  • Fetch 1-2 most informative third-party pages

Synthesis Output

Produce a profile with:

  • Company, Product, Existing Customers, Competitors (seed list), Use Cases
  • precise_category — one clear sentence that describes what category this product competes in. Avoid fuzzy words like "tools" or "platform". Good: "AI web search API for agents with neural + keyword retrieval". Bad: "search tools". This becomes the anchor for discovery queries and the gate.
  • category_include_keywords — 8-15 phrases that a direct competitor's marketing would very likely contain (title or hero). Include semantic variants. e.g. for Exa: web search api, search api, neural search, semantic search, retrieval api, search for ai agents, search for llms, serp api, embeddings search, live crawling, answer api, research api.
  • exclusion_list — phrases that indicate a different category, used by the gate to reject false positives. e.g. vector database, enterprise search appliance, site search widget, observability, analytics platform, data warehouse, scraping platform (full ETL/scraping suites, not retrieval APIs), internal knowledge base.

The same profiles/{company-slug}.json shape used by company-research, extended with the three new fields. The competitors array becomes the seed list and the first inputs to the comparison-graph expansion in Step 3.


Competitor Research — 4 Research Lanes

For each competitor, run these four lanes (depth-gated):

Lane 1 — Marketing Surface (ALL depth modes)

Goal: extract what the competitor says about themselves from their own site.

Sub-questions:

  • "What does {competitor} sell, who is it for, and how is it positioned?"
  • "What are {competitor}'s pricing tiers and pricing model?"
  • "What key features, integrations, and platforms does {competitor} list?"

Pages to fetch (via sitemap discovery — do NOT hardcode):

  1. Homepage
  2. /pricing (or equivalent from sitemap)
  3. /features, /product, /platform, /solutions
  4. /integrations, /customers, /case-studies

Extract into frontmatter fields: tagline, positioning, product_description, target_customer, pricing_model, pricing_tiers, key_features, integrations.

Lane 2 — External Signal (deep + deeper)

Goal: what the rest of the internet says about them.

Sub-questions:

  • "What third-party comparison pages mention {competitor}?"
  • "What do users say on Reddit, HN, G2, Capterra?"
  • "What recent news, launches, or announcements?"
  • "Who is talking about them on LinkedIn or YouTube?"

Search queries:

"{competitor} vs"
"{competitor} alternatives"
"{competitor} review"
"{competitor} G2" / "{competitor} Capterra"
"site:reddit.com {competitor}"
"site:news.ycombinator.com {competitor}"
"site:linkedin.com/posts {competitor}"
"site:youtube.com {competitor}"
"{competitor} launch 2025 OR 2026"
"{competitor} funding announcement"

Extraction rule: From search results, harvest each hit as a Mentions entry. Classify source type from the URL:

  • reddit.comReddit
  • news.ycombinator.comHN
  • linkedin.comLinkedIn
  • youtube.com / youtu.beYouTube
  • g2.com / capterra.com / trustradius.comReview
  • *vs* in path or title → Comparison
  • news domains (techcrunch, theverge, venturebeat, forbes, businesswire, globenewswire) → News
  • twitter.com / x.comX
  • spotify.com/episode / transistor/simplecast → Podcast

For LinkedIn and YouTube, the snippet + URL from browse cloud search is enough. Do NOT try to deep-fetch individual LinkedIn posts (auth walls) — list them with title/snippet.

Lane 3 — Public Benchmarks (deeper only)

Goal: find third-party benchmarks that measured this competitor's product.

Sub-questions:

  • "Has {competitor} been included in any public benchmark?"
  • "Are there GitHub repos, PRs, or blog posts comparing {competitor} head-to-head on a measured axis (speed, accuracy, cost, pass rate)?"

Search queries:

"{competitor} benchmark"
"{competitor} performance test"
"site:github.com {competitor} benchmark"
"site:github.com {competitor} vs"
"{competitor} vs {seed_competitor} benchmark"   # pairwise, use another known competitor as the seed
"{category} benchmark {competitor}"             # e.g. "web search api benchmark {competitor}"

Extraction: Add each hit to Benchmarks section with: title, source, URL, key finding (one line). Also mirror into Mentions with type Benchmark.

Known benchmark repos to check directly (if domain is on-topic):

  • Public retrieval-quality leaderboards (e.g. BEIR / MTEB-style repos) when a vendor publishes scores
  • Category-specific benchmark repos discovered via the first search wave

Lane 4 — Strategic Diff vs User's Company (deeper only)

Goal: explicitly compare this competitor to the user's company.

Inputs: {user_company_profile} (from Step 1) — specifically product, use_cases, key_features if available.

Sub-questions:

  • "What features does {competitor} have that {user_company} does not?"
  • "What features does {user_company} have that {competitor} does not?"
  • "Who does {competitor} serve that {user_company} does not (and vice versa)?"
  • "Where does each one win on the marketing surface (price, feature depth, DX, ecosystem)?"

No new fetches required for this lane — it's a synthesis step over Lane 1 + 2 + 3 findings plus the user's profile. Write as:

## Comparison vs {user_company}
- **Overlaps**: ...
- **Gaps**: ...
- **Where they win**: ...
- **Where you win**: ...

Also populate the strategic_diff frontmatter field with a one-line summary for the overview table.


Depth Mode Behavior

Quick Mode (~lots of competitors, cheap)

  • Lanes: 1 only
  • Budget: 2-3 tool calls per competitor (homepage + pricing page)
  • Fields populated: tagline, product_description, pricing_tiers, key_features
  • Mentions / Benchmarks / Comparison: skipped

Deep Mode (balanced, default)

  • Lanes: 1 + 2
  • Budget: 5-8 tool calls per competitor
  • Everything in quick + 5-10 mentions across source types

Deeper Mode (full intel)

  • Lanes: 1 + 2 + 3 + 4
  • Budget: 10-15 tool calls per competitor
  • Everything in deep + benchmarks section + strategic diff section

Finding Format (per lane)

Every finding is a factual statement tied to a source:

{
  "lane": "marketing | external | benchmark | strategic",
  "fact": "Rival Co charges $99/mo for 10K search requests",
  "sourceUrl": "https://rivalco.com/pricing",
  "confidence": "high"
}

Confidence:

  • high: Directly stated on the competitor's own website or official press
  • medium: Inferred from third-party articles, reviews, or job posts
  • low: Speculative / outdated sources

Research Loop Rules

  1. Lane 1 first — always start with the competitor's own site
  2. Use sitemap, not hardcoded paths/pricing might be /plans or /pricing-plans
  3. Rephrase, don't retry — if a search returns generic junk, switch keywords
  4. Fetch selectively — pick the 1-2 most promising URLs per query
  5. For LinkedIn/YouTube: search only, don't fetch — snippet is enough, avoid auth walls
  6. Respect step budget per depth mode
  7. Deduplicate mentions — same URL should only appear once in ## Mentions

Synthesis Instructions

After the research loop completes for a competitor:

  1. Fill frontmatter fields from Lane 1 findings
  2. Write body sections: Product, Pricing, Features, Positioning (all from Lane 1)
  3. Append ## Mentions from Lane 2 classified hits
  4. Append ## Benchmarks from Lane 3 (deeper only)
  5. Append ## Comparison vs {user_company} from Lane 4 synthesis (deeper only)
  6. Append ## Research Findings as a raw-findings appendix with confidence tags

No ICP score. No threat score. Pure intel.

If a field has no supporting findings, leave it empty rather than guessing.


Supporting file: references/workflow.md

Competitor Analysis — Workflow Reference

Contents

Discovery Batch JSON Schema

File: /tmp/competitor_discovery_batch_{N}.json

browse cloud search --output writes a JSON object:

{
  "requestId": "abc123",
  "query": "alternatives to acme",
  "results": [
    { "id": "...", "url": "https://example.com", "title": "Example Corp", "image": null, "favicon": null }
  ]
}

The list_urls.mjs script (run with --prefix competitor) deduplicates across batches.

Competitor Research Markdown Format

File: {OUTPUT_DIR}/{competitor-slug}.md — see references/example-research.md for the full template.

YAML frontmatter fields (used by compile_report.mjs):

  • competitor_name (required)
  • website (required)
  • tagline
  • positioning
  • product_description
  • target_customer
  • pricing_model
  • pricing_tiers (pipe-separated: Free | Pro $99 | Enterprise Contact)
  • key_features (pipe-separated)
  • integrations (pipe-separated)
  • headquarters
  • founded
  • employee_estimate
  • funding_info
  • strategic_diff (one-line for overview table; deeper mode only)

Body sections (in this order — compile_report.mjs parses by heading):

  • ## Product
  • ## Pricing
  • ## Features
  • ## Positioning
  • ## Comparison vs {user_company} (deeper only)
  • ## Mentions
  • ## Benchmarks (deeper only)
  • ## Research Findings

Mentions line format (parsed into the mentions feed):

- **[SourceType]** Title | Snippet (source: URL, YYYY-MM-DD)

SourceTypeBenchmark | Comparison | News | Reddit | HN | LinkedIn | YouTube | Review | Podcast | X. Date is optional but preferred.

Extracting Page Text

browse cloud fetch --allow-redirects returns clean markdown by default — no HTML stripping needed. Just cap the length:

browse cloud fetch --allow-redirects "https://rivalco.com/pricing" | head -c 3000

If you need the original HTML (e.g. to read the <title> tag or parse markup), add --format raw and strip tags:

browse cloud fetch --allow-redirects --format raw "https://rivalco.com/pricing" | sed 's/<script[^>]*>.*<\/script>//g; s/<style[^>]*>.*<\/style>//g; s/<[^>]*>//g; s/&amp;/\&/g; s/&lt;/</g; s/&gt;/>/g; s/&nbsp;/ /g; s/&#[0-9]*;//g' | tr -s ' \n' | head -c 3000

Limit to ~3000 chars per page to keep subagent context manageable. For JS-heavy pages (client-rendered pricing tables) where the Fetch API returns thin content, open the page in a browser session and read it: browse open "{url}" --remote then browse get markdown.

Discovery — parallel Bash, not subagents

The main agent runs discovery as 3 parallel browse cloud search Bash calls (one per wave) in a SINGLE message. No subagent layer. Each wave chains its 2-4 queries with && and writes results to /tmp/competitor_discovery_batch_{wave}{N}.json.

Example — main agent issues these three Bash tool calls in parallel in one message:

# Wave A — alternatives
browse cloud search "alternatives to {user_company}" --num-results 12 --output /tmp/competitor_discovery_batch_A1.json && \
browse cloud search "{user_company} competitors" --num-results 12 --output /tmp/competitor_discovery_batch_A2.json && \
echo "A done"
# Wave B — precise category
browse cloud search "{precise_category}" --num-results 12 --output /tmp/competitor_discovery_batch_B1.json && \
browse cloud search "{compose 3 distinctive tokens}" --num-results 12 --output /tmp/competitor_discovery_batch_B2.json && \
browse cloud search "{primary_noun} for ai agents" --num-results 12 --output /tmp/competitor_discovery_batch_B3.json && \
echo "B done"
# Wave C — comparison-page graph
browse cloud search "{user_company} vs" --num-results 12 --output /tmp/competitor_discovery_batch_C1.json && \
browse cloud search "{seed1} vs" --num-results 12 --output /tmp/competitor_discovery_batch_C2.json && \
browse cloud search "{seed2} vs" --num-results 12 --output /tmp/competitor_discovery_batch_C3.json && \
echo "C done"

Why direct Bash and not subagents: each wave is 2-4 browse cloud search calls — agent cold-start + tool-reasoning overhead is bigger than the actual work. Using parallel Bash saves ~1-2 min per run with no quality loss.

Discovery query patterns

Discovery uses three parallel waves (evaluated — all three are additive):

Wave A — Generic alternatives (broad net, lots of noise):

  • "alternatives to {user_company}"
  • "{user_company} competitors"

Wave B — Precise category queries (uses precise_category from self-research):

  • "{precise_category}" verbatim
  • "{precise_category_2_3_keywords}" — pick the 3 most distinctive tokens
  • Compose with "API", "cloud", "for agents": "cloud {primary_noun} for ai agents", "{primary_noun} infrastructure API"

Wave C — Comparison-page graph (highest-precision single wave):

  • "{user_company} vs"
  • For each seed competitor from the user's profile, also run "{seed} vs"
  • After the searches, scripts/extract_vs_names.mjs parses "X vs Y" titles across all Wave C results to surface candidate names that don't appear as URLs.

Evaluation result (tested on a search-API run): Wave A returns ~10% real competitors (mostly AI-tool-listicle aggregators). Wave B returns ~35%. Wave C uniquely surfaces named brands via title parsing that neither A nor B finds. Use all three.

Enrichment fan-out — 5 subagents PER competitor (deep/deeper modes)

For each gated-PASS competitor, launch five parallel subagents, one per lane. Each subagent writes a partial to {OUTPUT_DIR}/partials/{slug}.{lane}.md. After all subagents complete, scripts/merge_partials.mjs unions the partials into one canonical {OUTPUT_DIR}/{slug}.md per competitor (dedup mentions by URL, sort by date desc).

The 5 lanes:

LaneSlugScope
A. MarketingmarketingOwns canonical frontmatter. Pricing, features, positioning, integrations, customers, target, team, funding, HQ. Homepage + sitemap-driven page discovery.
B. DiscussiondiscussionReddit, HN, forums, dev.to, hashnode. Broader queries beyond site: restrictions — also "{competitor}" discussion, "{competitor}" review 2026, "{competitor}" issues OR problems. Writes Mentions bullets with dates.
C. SocialsocialLinkedIn posts, YouTube videos, Twitter/X threads. Search snippets only — do NOT fetch (auth walls).
D. News & ComparisonsnewsComparison pages ("X vs Y"), TechCrunch / Verge / Forbes / VentureBeat / Businesswire, independent blog reviews, Substack. Every mention MUST include a date.
E. Technical & BenchmarkstechnicalGitHub benchmark repos/PRs, performance blog posts, independent tests. Writes Benchmarks bullets AND Findings on technical specifics (retrieval modes, latency, rate limits, SDKs).

Wave management — launch ALL subagents in ONE message: for N competitors × 5 lanes = 5N subagents, fit them all in a single Agent-tool message. Wall clock then equals the slowest single subagent (~3-5 min) instead of batches × slowest_per_batch. On a real 10-competitor run we measured 25 minutes wasted by self-throttling to 10-per-message — the Agent tool happily runs 50+ in parallel; do not split into batches for "politeness". The only cap is that each subagent still batches its own Bash operations into a single call.

Merge step (once all partials exist):

node {SKILL_DIR}/scripts/merge_partials.mjs {OUTPUT_DIR}

Produces one {OUTPUT_DIR}/{slug}.md per competitor with dedup'd Mentions (sorted date desc), Benchmarks, and Findings.

Legacy: Single-subagent template (quick mode only)

In quick mode, keep a single subagent per batch of competitors (no fan-out — Lane 1 only, budget 2-3 calls each).

You are a competitor enrichment subagent. For each competitor URL, run the 4-lane research
pattern and write a single markdown file per competitor.

CONTEXT:
- User's company: {user_company}
- User's product: {user_product}
- User's key features: {user_key_features}
- Depth mode: {depth_mode}   (quick | deep | deeper)
- Output directory: {OUTPUT_DIR}   ← write files HERE, as a full literal path

COMPETITOR URLS TO PROCESS:
{url_list}

TOOL RULES — CRITICAL, FOLLOW EXACTLY:
1. You may ONLY use the Bash tool. No exceptions.
2. All searches: Bash → browse cloud search "..." --num-results 10
3. All page fetches: Bash → browse cloud fetch --allow-redirects "..."
   browse cloud fetch returns clean markdown by default — just `| head -c 3000`, no HTML stripping.
   If you need the raw HTML, add --format raw and pipe through:
   sed 's/<script[^>]*>.*<\/script>//g; s/<style[^>]*>.*<\/style>//g; s/<[^>]*>//g' | tr -s ' \n' | head -c 3000
   If a page returns thin content or "enable JavaScript", use `browse open "{url}" --remote` then `browse get markdown`.
4. BATCH all file writes: Write ALL markdown files in a SINGLE Bash call using chained heredocs.
5. BANNED TOOLS: WebFetch, WebSearch, Write, Read, Glob, Grep — ALL BANNED.
6. NEVER use ~ or $HOME in paths — use full literal paths.

RESEARCH PATTERN (per competitor — lanes are depth-gated):

LANE 1 — Marketing Surface (always run):
  a. Fetch competitor homepage
  b. Discover via sitemap: /sitemap.xml — find /pricing, /features, /integrations, /customers
  c. Fetch 2-4 most relevant pages
  d. Extract: tagline, positioning, product_description, target_customer,
     pricing_model, pricing_tiers, key_features, integrations

LANE 2 — External Signal (deep + deeper):
  Run these searches:
    browse cloud search "{competitor} vs"
    browse cloud search "{competitor} alternatives review"
    browse cloud search "site:reddit.com {competitor}"
    browse cloud search "site:news.ycombinator.com {competitor}"
    browse cloud search "site:linkedin.com/posts {competitor}"
    browse cloud search "site:youtube.com {competitor}"
    browse cloud search "{competitor} G2 OR Capterra"
    browse cloud search "{competitor} launch OR funding 2025 OR 2026"

  For each search result, classify source type from URL:
    reddit.com → Reddit
    news.ycombinator.com → HN
    linkedin.com → LinkedIn
    youtube.com/youtu.be → YouTube
    twitter.com/x.com → X (or Twitter — either works)
    dev.to → DevTo
    hashnode.dev, hashnode.com → Hashnode
    *.substack.com → Substack
    spotify.com/episode, transistor.fm, simplecast.com → Podcast
    g2.com/capterra.com/trustradius.com → Review
    url or title contains "vs" → Comparison
    techcrunch/theverge/venturebeat/forbes/businesswire/wired/fortune → News
    other blog domain → Blog

  Record each as a Mentions line with title + one-line snippet + URL + **date**. Always include
  the date when available. If a `browse cloud search` result carries a date field, prefer it.
  If absent, parse the year from title/URL (e.g. "2026" or `/2025/11/` in a news URL).
  For LinkedIn and YouTube — use search snippet only, do NOT fetch the page.

LANE 3 — Public Benchmarks (deeper only):
  Run these searches:
    browse cloud search "{competitor} benchmark"
    browse cloud search "site:github.com {competitor} benchmark"
    browse cloud search "{category} benchmark {competitor}"

  Record each hit in ## Benchmarks with: title, source, URL, one-line key finding.
  Also append to ## Mentions with type Benchmark.

LANE 4 — Strategic Diff vs {user_company} (deeper only):
  Using Lane 1-3 findings + the user's company profile, write:
  ## Comparison vs {user_company}
  - Overlaps: ...
  - Gaps: ...
  - Where they win: ...
  - Where you win: ...
  Also fill the `strategic_diff` frontmatter field with a one-line summary.

HARD TOOL-CALL CAP — count your browse cloud calls and STOP at the cap. Partial output beats blocking the pipeline.
  quick mode:   3 browse cloud calls max per competitor
  deep mode:    8 browse cloud calls max per competitor
  deeper mode:  12 browse cloud calls max per competitor

ENFORCEMENT — at the start of every Bash call, prepend a comment like
  # browse call N/8 (deep mode)
After hitting the cap, write the output file with WHAT YOU HAVE — even if a section is thin.
NEVER do a 9th call in deep mode "to be thorough". The pipeline budgets time on this assumption.

Observed cost of overshoot (Apr 25 search-API run): two lanes hit 29-30 calls each, drove
wall-clock for the whole 30-agent fan-out from 5 min → 12 min. Don't do this.

OUTPUT — write ALL competitor files in a SINGLE Bash call using chained heredocs directly to {OUTPUT_DIR}:

cat << 'COMPETITOR_MD' > {OUTPUT_DIR}/{slug1}.md
---
competitor_name: {name}
website: {url}
tagline: {tagline}
positioning: {positioning}
product_description: {description}
target_customer: {audience}
pricing_model: {model}
pricing_tiers: {tier1} | {tier2} | {tier3}
key_features: {f1} | {f2} | {f3}
integrations: {i1} | {i2}
headquarters: {hq}
founded: {year}
employee_estimate: {estimate}
funding_info: {funding}
strategic_diff: {one line — deeper only}
---

## Product
{paragraph}

## Pricing
{bullets per tier}

## Features
{bullets}

## Positioning
{paragraph}

## Comparison vs {user_company}    ← deeper only
- Overlaps: ...
- Gaps: ...
- Where they win: ...
- Where you win: ...

## Mentions
- **[SourceType]** Title | Snippet (source: URL, YYYY-MM-DD)

## Benchmarks                       ← deeper only
- Title | Source | URL | Key finding

## Research Findings
- **[confidence]** Fact (source: URL)
COMPETITOR_MD
cat << 'COMPETITOR_MD' > {OUTPUT_DIR}/{slug2}.md
...
COMPETITOR_MD

Use 'COMPETITOR_MD' (quoted) as the heredoc delimiter to prevent shell variable expansion.

Report back ONLY: "Batch {batch_id}: {succeeded}/{total} competitors researched, {mentions_count} mentions, {benchmarks_count} benchmarks."
Do NOT return raw data to the main conversation.

Wave Management

Key Principle: Maximize Parallelism, Minimize Prompts

Launch ALL subagents needed for a phase in ONE message. No "up to 6 per message" cap — the Agent tool runs them in parallel, so wall clock = slowest single agent regardless of count. On a 10-competitor × 5-lane = 50-subagent enrichment, splitting into 5 batches of 10 cost an extra 20 minutes of wall clock vs one batch of 50 (measured Apr 2026). Each subagent still MUST batch its own Bash operations into a single call.

Discovery Phase

  • Run discovery as parallel browse cloud search Bash calls, not subagents. Subagent overhead (cold start + tool reasoning) is bigger than the work. Three Bash tool calls in one message — one per wave (A/B/C) — chain each wave's searches with &&.
  • Each wave's bash call writes its outputs as /tmp/competitor_discovery_batch_{wave}{N}.json
  • After all waves complete, run the following in sequence:
    # 1. Dedup URLs from all batches
    node {SKILL_DIR}/scripts/list_urls.mjs /tmp --prefix competitor > /tmp/competitor_urls.txt
    
    # 2. Extract candidate names from "X vs Y" titles (Wave C output)
    node {SKILL_DIR}/scripts/extract_vs_names.mjs /tmp --prefix competitor \
      --seed "{user_company},{seed1},{seed2},{seed3}" \
      > /tmp/competitor_vs_names.jsonl
    
  • Filter URLs: Remove blog posts, news articles, AI-tool directories (seektool.ai, respan.ai, agentsindex.ai, toolradar.com, aitoolsatlas.ai, aidirectory.com, vibecodedthis.com, aichief.com, openalternative.co, cbinsights.com, saasworthy.com, softwareworld.com), review aggregators (g2.com, capterra.com, trustradius.com), databases (crunchbase.com, tracxn.com), and the user's own domain. Keep only candidate company homepages.
  • For names from extract_vs_names.mjs that didn't resolve to a domain, optionally run browse cloud search "{name}" --num-results 3 to resolve the top domain; skip if ambiguous.
  • Merge: filtered-URL list ∪ resolved vs_names domains ∪ user-provided seed URLs. Dedup by hostname into /tmp/competitor_candidates.txt.

User-confirm phase (between gate and enrichment — mandatory)

After the gate writes /tmp/competitor_gated.jsonl, the main agent MUST ask the user to confirm the enrichment set before launching subagents. Enrichment is 25 subagents × depth budget per competitor — too expensive to run on guesses.

Present three buckets to the user:

  1. PASS — status=PASS rows with title
  2. UNKNOWN — status=UNKNOWN (fetch failed; always a silent miss risk — JS-heavy homepages, Cloudflare challenges)
  3. Rejected-brand matches — top ~10 REJECT rows whose title contains a seed token or that showed up repeatedly in the Wave C "X vs Y" graph

Then AskUserQuestion with a checkbox list + free-text "add more". Write the confirmed set to /tmp/competitor_enrichment_set.txt (one URL per line). That file — not /tmp/competitor_passed.txt — is the input to the enrichment subagents.

Known gate blind spots to surface aggressively:

  • JS-heavy landing pages return near-empty hero text → gate's keyword matcher has nothing to bite on
  • Cloudflare challenge titles ("Just a moment...") → obvious false negative
  • "Search foundation" / "retrieval backbone" / "agent runtime" — semantic variants of the category don't lexically match
  • Apex domain vs product subdomain (e.g. brave.com the browser vs api-dashboard.search.brave.com the actual API product)

Gate Phase (between discovery and enrichment)

Drop wrong-category candidates BEFORE enrichment burns tool calls on them.

cat /tmp/competitor_candidates.txt \
  | node {SKILL_DIR}/scripts/gate_candidates.mjs \
      --include "{category_include_keywords_csv}" \
      --exclude "{exclusion_list_csv}" \
      --concurrency 6 \
  > /tmp/competitor_gated.jsonl

# Extract PASS-only URLs for enrichment
grep '"status":"PASS"' /tmp/competitor_gated.jsonl \
  | node -e 'require("fs").readFileSync(0,"utf-8").split("\n").filter(Boolean).forEach(l => { try { console.log(JSON.parse(l).url); } catch {} })' \
  > /tmp/competitor_passed.txt

Keyword sources:

  • --include ← profile's category_include_keywords (comma-joined).
  • --exclude ← profile's exclusion_list.

Gate logic (position-aware): REJECT if exclude term in <title>; PASS if include term in <title>; for hybrid titles with both (e.g. "Browser Automation & Web Scraping API"), tiebreak by first 200 chars of hero text; otherwise fall through to hero-wide check. Conservative by default.

Review the output — the main agent SHOULD spot-check both lists and MAY manually re-include a REJECT if it recognizes a known direct competitor whose own marketing is category-ambiguous.

Evaluation on a search-API run (12 candidates): 7/7 real competitors PASSED; 4/4 wrong-category (vector database, scraping/ETL platform, analytics tool, internal-KB search) REJECTED. One split-identity edge (a search vendor that also sells a scraping suite) rejected — acceptable.

Enrichment Phase

Two modes:

  • quick mode — single subagent per batch of competitors. Lane A (marketing) only. ~8 competitors per subagent, 2-3 tool calls each. Writes directly to {OUTPUT_DIR}/{slug}.md.
  • deep / deeper modes — 5-subagent fan-out PER competitor. Each subagent owns ONE lane (marketing / discussion / social / news / technical). Writes to {OUTPUT_DIR}/partials/{slug}.{lane}.md. Budget: 5-8 calls per subagent (deep), 10-15 (deeper). After all lanes complete, run scripts/merge_partials.mjs to consolidate.
  • Launch ALL competitor × lane subagents in a SINGLE Agent tool message. For 10 competitors × 5 lanes = 50 parallel agents in one message. Do NOT split into batches — wall clock becomes the slowest single agent (~3-5 min) instead of batches-times-batch-max (~25 min on 10 competitors split into 5 rounds of 10).

Screenshots Phase (after merge, before compile)

Capture homepage hero screenshot for each competitor:

node {SKILL_DIR}/scripts/capture_screenshots.mjs {OUTPUT_DIR} --mode remote --concurrency 1

Requires the browse CLI (npm install -g browse). --mode remote drives a Browserbase session (the script passes --remote on each browse command); use --mode local for local Chrome. Writes one PNG per competitor to {OUTPUT_DIR}/screenshots/{slug}-hero.png. compile_report.mjs auto-embeds the hero in the per-competitor HTML page when present.

Cost: ~10-20s per competitor (serial). Total for 5 competitors ≈ 60s.

Sizing Formula

search_queries = ceil(requested_competitors / 20)   # discovery is narrower than lead gen
discovery_subagents = ceil(search_queries / 3)
expected_urls = search_queries * 15

quick:   research_subagents = ceil(expected_urls / 8)
deep:    research_subagents = ceil(expected_urls / 4)
deeper:  research_subagents = ceil(expected_urls / 2)

Error Handling

  • If a subagent fails, log and continue with remaining batches
  • If >50% of subagents fail in a wave, pause and inform the user
  • If browse cloud fetch --allow-redirects fails, try browse open "{url}" --remote + browse get markdown as fallback, or skip that page

Report Compilation

After all enrichment subagents complete, compile all HTML views in one command:

node {SKILL_DIR}/scripts/compile_report.mjs {OUTPUT_DIR} --user-company "{user_company}" --open

The script:

  • Reads all .md files in {OUTPUT_DIR}
  • Parses YAML frontmatter + body sections
  • Deduplicates by normalized competitor name
  • Generates {OUTPUT_DIR}/index.html — overview table (name, tagline, pricing, key features, strategic diff)
  • Generates {OUTPUT_DIR}/competitors/{slug}.html — per-competitor deep dive
  • Generates {OUTPUT_DIR}/matrix.html — side-by-side feature/pricing grid across competitors
  • Generates {OUTPUT_DIR}/mentions.html — chronological feed with source-type pills + client-side filter
  • Generates {OUTPUT_DIR}/results.csv — flat spreadsheet
  • Opens index.html in the default browser (--open flag)
  • Prints a JSON summary to stderr

Supporting file: scripts/package.json

{ "name": "competitor-analysis-scripts", "version": "0.1.0", "private": true, "type": "module" }

How do I install Competitor analysis in Cursor, Claude Code, or Codex?

Run npx skills add browserbase/skills --skill competitor-analysis in the project where you want it, then ask your agent for the skill by name. The --skill flag installs only Competitor analysis, not every skill in the repository.

Where does Competitor analysis come from and what license is it under?

Competitor analysis comes from the browserbase/skills repository on GitHub. That repository has 3.7K GitHub stars. No license was detected on the source repository, so check with the author before redistributing it.

Prefer plain text? Read the Competitor analysis guide as markdown.