3.7k browserbase

competitor-analysis Skill

竞品调研与情报技能。接收用户的公司(可选提供种子竞品 URL),通过 Browserbase Search API 自动发现更多竞品,用四轨模式(营销面、外部信号、公开基准、与用户公司的战略对比)对每个竞品深入研究,并将结果汇总成含四种视图的 HTML 报告:总览、单个竞品深度剖析、功能/价格并排矩阵,以及按时间排列的提及信息流(新闻、评价、社交媒体、对比页和公开基准)。当用户想要分析竞品、调研竞争格局时使用。

安装方式:把技能目录放入 ~/.claude/skills/(Claude Code)或在 claude.ai 设置中启用;也可复制右侧安装命令一键添加。

查看源码

技能指令原文(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 --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. Gate — scripts/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. Screenshots — capture_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.
  1. 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.

  1. 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.

  1. 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.
  1. 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.

  1. Present the profile + the user-company .md to the user for confirmation. Do not proceed until confirmed.
  1. 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.

| Mode | Research per competitor | Best for |
|------|--------------------------|----------|
| quick | Lane 1 only (homepage + pricing) | Scanning ~30-50 competitors fast |
| deep | Lanes 1+2 | ~15-25 competitors with external signal |
| deeper | All 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
  1. 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.
  2. 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.
  3. 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 </code> → REJECT; include in <code><title></code> → PASS; hybrid title → hero200 tiebreak; otherwise fall through. </p> <p> <strong>Evaluated on a search-API run</strong> with 12 mixed candidates: 7/7 real competitors passed, 4/4 wrong-category rejected, 1 known-hybrid edge case rejected. </p> <h2>Step 4.5: Confirm enrichment set with the user</h2> <p> <strong>This step is mandatory. Do NOT skip to enrichment just because the gate ran.</strong> </p> <p> Enrichment is expensive: 5 competitors × 5 lane-subagents = 25 subagents, ~10-15 minutes of wall clock, ~300 <code>browse cloud</code> calls. Running it on the wrong set wastes all of that. The gate also has known blind spots: </p> <ul> <li><strong>JS-heavy homepages</strong> (e.g. Tavily, Firecrawl) — <code>browse cloud fetch</code> returns near-empty text, so keyword matching has nothing to match on → REJECT or UNKNOWN</li> <li><strong>Cloudflare challenge pages</strong> (e.g. Perplexity) — title becomes "Just a moment..." → no category signal</li> <li><strong>Semantic variants</strong> — "search foundation" / "retrieval backbone" don't lexically match a list centered on "search API"</li> <li><strong>Domain ambiguity</strong> — <code>brave.com</code> (the browser) vs <code>api-dashboard.search.brave.com</code> (the actual API product) can confuse classification</li> </ul> <p> The user almost always has domain knowledge the skill lacks. Ask them. </p> <p> <strong>Process</strong> — the main agent: </p> <ol> <li>Read <code>/tmp/competitor_gated.jsonl</code> and group rows:</li> </ol> <ul> <li><strong>PASS bucket</strong>: everything with status=PASS.</li> <li><strong>UNKNOWN bucket</strong>: status=UNKNOWN (fetch failed — always surface, these are the silent misses).</li> <li><strong>Rejected-brand bucket</strong>: 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).</li> </ul> <ol> <li>Present the buckets to the user, one table per bucket, with URL + title + reason (for rejects).</li> </ol> <ol> <li>Use <code>AskUserQuestion</code> with a checkbox list of all candidates across the three buckets, plus a free-text "add more" field. The prompt should be explicit:</li> </ol> <blockquote>"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."</blockquote> <ol> <li>Write the confirmed set to <code>/tmp/competitor_enrichment_set.txt</code> (one URL per line). This is the input for Step 5 — not <code>/tmp/competitor_passed.txt</code>.</li> </ol> <p> <strong>If the user doesn't respond</strong> or explicitly says "just run it", fall back to <code>/tmp/competitor_passed.txt</code> as-is, but warn in chat that the run may waste budget on wrong-category hits. </p> <p> <strong>Exa test, 2026-04-24</strong>: 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. </p> <h2>Step 5: Deep Enrichment</h2> <p> Two modes. See <code>references/workflow.md</code> for prompt templates and wave management. See <code>references/research-patterns.md</code> for the lane-by-lane methodology. </p> <h3>Quick mode — single subagent per batch</h3> <ul> <li>Input: <code>/tmp/competitor_enrichment_set.txt</code> (user-confirmed set from Step 4.5), ~8 competitors per subagent.</li> <li>One subagent runs Lane A only (marketing surface). 2-3 tool calls each.</li> <li>Writes directly to <code>{OUTPUT_DIR}/{slug}.md</code>.</li> </ul> <h3>Deep / Deeper mode — 5 subagents PER competitor (parallel lane fan-out)</h3> <p> For each competitor, launch 5 parallel subagents, one per lane: </p> <ul> <li><strong>A. Marketing</strong> (<code>marketing</code>): pricing, features, positioning, integrations, customers, team, funding, HQ. Owns canonical frontmatter.</li> <li><strong>B. Discussion</strong> (<code>discussion</code>): Reddit, HN, forums, Dev.to, Hashnode. Broad queries beyond <code>site:</code> — also <code>"{competitor}" review 2026</code>, <code>"{competitor}" issues OR problems</code>, <code>"{competitor}" discussion</code>.</li> <li><strong>C. Social</strong> (<code>social</code>): LinkedIn posts, YouTube videos, Twitter/X. Snippets only — do NOT fetch.</li> <li><strong>D. News & Comparisons</strong> (<code>news</code>): TechCrunch, Verge, VentureBeat, Forbes, Businesswire, Substack, blog reviews. Every mention needs a date.</li> <li><strong>E. Technical & Benchmarks</strong> (<code>technical</code>): GitHub benchmark repos/PRs, performance posts. Writes Benchmarks + technical Findings.</li> </ul> <p> Budget per lane: deep = 5-8 tool calls, deeper = 10-15. <br /> <strong>Launch ALL competitor × lane subagents in a SINGLE Agent tool message.</strong> 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. </p> <p> Each subagent writes a partial to <code>{OUTPUT_DIR}/partials/{slug}.{lane}.md</code>. </p> <p> <strong>Critical</strong>: 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 <code>{OUTPUT_DIR}</code> path to every subagent. </p> <h3>Merge partials → canonical per-competitor file</h3> <p> After all subagents for all competitors complete: </p> <pre><code class="language-bash">node {SKILL_DIR}/scripts/merge_partials.mjs {OUTPUT_DIR}</code></pre> <p> Unions the 5 partials per competitor into one <code>{OUTPUT_DIR}/{slug}.md</code> — dedup'd Mentions (sorted by date desc), dedup'd Benchmarks, merged Findings, canonical frontmatter from the marketing lane. </p> <h3>Synthesize the comparison matrix (write <code>matrix.json</code>)</h3> <p> <strong>Subagents write <code>key_features</code> and <code>integrations</code> as prose</strong>, not as pipe-separated atomic feature labels. So a naive <code>|</code>-split axis becomes one-blob-per-competitor with no overlap — the rendered matrix shows a useless diagonal. </p> <p> The main agent fixes this by synthesizing a <strong>shared taxonomy</strong> across competitors and writing <code>{OUTPUT_DIR}/matrix.json</code>. <code>compile_report.mjs</code> auto-detects this file and renders the matrix from it instead of from the pipe split. </p> <p> <strong>Process</strong> — main agent: </p> <ol> <li>Read ALL <code>{slug}.md</code> files, INCLUDING the user's company file <code>{user-slug}.md</code> produced in Step 1. The user is competitor #0 for matrix purposes — treat with identical rigor.</li> <li>Produce a canonical list of 12-20 <em>atomic</em> 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.</li> <li>Produce a canonical list of 10-20 integrations (frameworks, marketplaces, SDK languages).</li> <li>For each company INCLUDING THE USER, map each taxonomy entry to <code>true</code> / <code>false</code> based on the enrichment data in their <code>.md</code> file. <strong>Every flag must be traceable to a Research Findings bullet with a cited URL.</strong> If the user's file says "exa-py MIT-licensed (github.com/exa-labs/exa-py)", the Open-source feature is <code>true</code> with that URL as the source. If not mentioned, leave <code>false</code>.</li> <li>Write the result to <code>{OUTPUT_DIR}/matrix.json</code> in this shape:</li> </ol> <pre><code class="language-json"> { "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": {...} } } }</code></pre> <p> <strong><code>userCompany</code> is required</strong>. The overview page renders two cards — "Where {user} is winning" and "Where {user} is losing". Populate <code>userCompany.features</code> and <code>userCompany.integrations</code> from the self-research profile (Step 1). Without this field those two cards don't render. </p> <p> <strong>Write order (two passes — this resolves the apparent ordering tension below).</strong> In this step (5b) write all <code>features</code> / <code>integrations</code> cells for <code>userCompany</code> and every competitor, plus a <strong>draft</strong> <code>winningSummary</code> / <code>losingSummary</code>. 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, <strong>rewrite</strong> the two summaries so the prose reflects only fact-checked cells. The JSON shape above shows the finalized post-fact-check object. </p> <p> <strong><code>userCompany.winningSummary</code> / <code>losingSummary</code> are strongly preferred</strong> (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. </p> <p> 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. </p> <h3>Fact-check the matrix — spot-check the high-stakes cells (default)</h3> <p> <strong>Do not trust the taxonomy pass alone for high-stakes cells.</strong> 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. </p> <p> 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. </p> <p> <strong>Default = spot-check, not full sweep.</strong> Only verify cells that meaningfully change the strategic narrative. </p> <p> Launch a single fact-check subagent (Bash-only) with <strong>a hard 25-call budget</strong> that targets ONLY these high-stakes axes: </p> <ol> <li><strong>Every <code>userCompany.features</code> and <code>userCompany.integrations</code> cell</strong> (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:</li> </ol> <ul> <li>Anything claimed as a <em>moat</em> in <code>winningSummary</code></li> <li>Anything claimed as a <em>gap</em> in <code>losingSummary</code></li> <li>Compliance (SOC 2, HIPAA, ISO 27001, GDPR)</li> <li>Open-source license claims (MIT / Apache 2.0 / AGPL — observed wrong on a competitor's SDK)</li> <li>Published uptime SLA (status page ≠ SLA)</li> </ul> <ol> <li><strong>Across competitors, only the cells that drive the win/loss summary</strong>:</li> </ol> <ul> <li>For each "Winning" claim, verify the user has it AND verify the competitors don't.</li> <li>For each "Losing" claim, verify the named competitors do have it.</li> <li>Compliance + license + SLA across all competitors (high-trust, frequently wrong).</li> </ul> <ol> <li><strong>Do NOT verify</strong>:</li> </ol> <ul> <li>Universal table-stakes (REST API, JSON responses, Python SDK, API-key auth) — every search API has these.</li> <li><code>false</code> cells with no claim being made (no moat lost or won).</li> <li>Integration cells unless they appear in the win/loss summary.</li> </ul> <pre><code class="language-text">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".</code></pre> <p> <strong>Full-sweep mode (opt-in, slower)</strong>: 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. </p> <p> After the subagent completes, re-read matrix.json, recompile, and surface <code>matrix_fact_check.md</code> 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. </p> <h3>Step 5d: Battle Card synthesis (deep/deeper only, after Step 5c)</h3> <p> <strong>Depends on fact-checked matrix.json from Step 5c.</strong> This is a sales-enablement lane. For each competitor, launch a Bash-only synthesis subagent (no new <code>browse cloud</code> calls) that reads all 5 existing partials + the user's merged <code>.md</code> + fact-checked <code>matrix.json</code>, and produces per-competitor Landmines / Objection Handlers / Talk Tracks grounded in cited evidence. </p> <p> Prompt template: <code>references/battle-card-subagent.md</code> (substitute <code>{COMPETITOR_SLUG}</code> / <code>{COMPETITOR_NAME}</code> / <code>{USER_COMPANY_NAME}</code> / <code>{USER_WINNING_SUMMARY}</code> per competitor). Format spec: <code>references/battle-card.md</code>. </p> <p> Output: <code>{OUTPUT_DIR}/partials/{slug}.battle.md</code> with a <code>## Battle Card</code> section. </p> <p> <strong>Re-run the merge after this lane completes.</strong> The Step 5 merge ran <em>before</em> the battle partials existed, so the consolidated <code>{slug}.md</code> files don't contain them yet. Re-run: </p> <pre><code class="language-bash">node {SKILL_DIR}/scripts/merge_partials.mjs {OUTPUT_DIR}</code></pre> <p> This unions each <code>{slug}.battle.md</code> into its consolidated <code>{slug}.md</code> (the <code>battle</code> lane is already handled by <code>merge_partials.mjs</code>). <code>compile_report.mjs</code> reads the <code>## Battle Card</code> section from <code>{slug}.md</code> and renders it as a brand-accented card on the per-competitor HTML page. <strong>Skip this re-merge and the battle cards never appear in the report.</strong> </p> <p> <strong>Why this lane is synthesis-only</strong> — battle cards must be grounded in facts that already survived Step 5c. Letting the subagent do fresh <code>browse cloud</code> 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 <code>sources</code>-backed matrix cell. </p> <p> Parallelism: 1 subagent per competitor, all in one Agent-tool message (synthesis is fast, ~3-5 Bash calls per subagent). Skip this step in <code>quick</code> mode — there isn't enough research depth to ground the cards credibly. </p> <h2>Step 6: Screenshots</h2> <p> Capture a homepage hero screenshot per competitor: </p> <pre><code class="language-bash">node {SKILL_DIR}/scripts/capture_screenshots.mjs {OUTPUT_DIR} --mode remote</code></pre> <p> Uses the <code>browse</code> CLI (<code>npm install -g browse</code>). The <code>--mode</code> flag selects the browser session: <code>remote</code> (default) drives a Browserbase session — best for protected/bot-detecting homepages and the only option without local Chrome; <code>local</code> uses Chrome on your machine. The script passes the corresponding <code>--remote</code> / <code>--local</code> flag on each <code>browse</code> command, so there is no separate environment-config step to run. Writes one PNG per competitor to <code>{OUTPUT_DIR}/screenshots/{slug}-hero.png</code>. The compile step in Step 7 auto-embeds the hero on each per-competitor HTML page. </p> <p> Cost: ~10-20s per competitor. ~60s for 5 competitors. </p> <h2>Step 7: HTML Report</h2> <ol> <li><strong>Generate all views + CSV</strong> (opens overview in browser):</li> </ol> <pre><code class="language-bash"> node {SKILL_DIR}/scripts/compile_report.mjs {OUTPUT_DIR} --user-company "{user_company}" --open</code></pre> <p> Produces: </p> <ul> <li><code>{OUTPUT_DIR}/index.html</code> — overview: competitor table with tagline, pricing summary, key features, strategic diff</li> <li><code>{OUTPUT_DIR}/competitors/{slug}.html</code> — per-competitor deep dive (all sections)</li> <li><code>{OUTPUT_DIR}/matrix.html</code> — side-by-side feature/pricing matrix</li> <li><code>{OUTPUT_DIR}/mentions.html</code> — chronological feed with source-type pills + client-side filter</li> <li><code>{OUTPUT_DIR}/results.csv</code> — flat spreadsheet</li> </ul> <ol> <li><strong>Present a chat summary</strong>:</li> </ol> <pre><code class="language-text">## 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</code></pre> <ol> <li>Show the <strong>overview table</strong> in chat:</li> </ol> <pre><code class="language-text">| 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 |</code></pre> <ol> <li>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.</li> </ol>