---
name: company-research
description: |-
  The one company-research skill (replaces the old separate "company cheat sheet"). Understands any company: what they do, why now plus the angle to use, how they make money, size, and competitors, rendered as one clean cheat-sheet artefact. Use for "research [company]", "cheat sheet on [company]", "look into / tell me about [company]", "prep me on [company]", "I've a call with [company]", "full brief before my meeting", "what do [company] do", or 1-5 names/domains. Default is neutral research, free web only. If the user's business profile is loaded it ALSO scores fit (BANT) and gives the single best outreach angle; those layers stay OFF until then. It NEVER calls Apollo, spends credits, enriches or verifies contacts, or writes the email/DM (list-building lives in the list-builder, the email in the Outbound Room).
---

# Company Research

One job: **understand a company.** Whatever the reason for asking. Produce a clean,
sourced cheat sheet per company (1–5). Ported from proven GitHub research skills; output
packaged inside the real Bingley desk shell (interchangeable, Pattern A).

## Scope (in / out)
- **Default: neutral research only.** Facts, sourced, no judgement. Free web data, zero credits.
- **Optional (only with a business profile, only when asked): fit (BANT) + the single best
  outreach angle.** No profile means facts only, no fit. Don't score by default.
- **Always out: Apollo / any paid enrichment, verified-contact lookup, and copywriting (the
  email/DM).** Those live in the list-builder and the Outbound Room. This skill never spends a credit.

## Data access — the brain bridge (shared Sales OS profile)
This skill reads and writes the user's business profile through the shared **second brain** — the SAME
brain the cold-email builder uses — via one bundled generic script, `scripts/brain_bridge.py` (deps
`brainstore.py` / `schema.py` / `brain_maps.py` bundled beside it). The business profile is the shared
spine (`business.*` in `sales-os-profile.json`): whatever the user tells ANY Sales OS skill about what
they sell lands there, so a user who filled it in cold-email gets fit scoring on their FIRST research
run, and vice versa. One profile, every skill, one brain. **Two verbs**, always pass `--skill
company-research --base "<the user's working folder>/Claude HQ"` (the `Claude HQ` subfolder, where the
brain lives — NEVER the working-folder root); each prints JSON.
- **`get`** → `{"profile":{…},"lens":{…}}`, brain-first then schema default, creates nothing.
- **`save key=value …`** → writes to the brain (provenance-stamped). Capture flags (`seen`/`deepDone`/`deepSkips`) always apply; `deepSkips=+1` increments.
Install order is irrelevant: either skill (or both) extends the one brain; the libs are identical and writes are field-level + file-locked, so they never clobber each other.

## Fit (BANT) — progressive, return-gated (mirrors the cold-email builder)
**Zero friction on the first run — never ask for the business profile up front. The offer to switch on
fit appears only once the user comes BACK (returning is consent to be asked).** Fit is scored against
the USER's business (`business.product / audience / problem`), never the target company alone.

**At the very top of every run, before anything else, run `python3 scripts/brain_bridge.py get --skill
company-research --base "<wf>/Claude HQ"` and branch on the `profile` block** (`{seen, name, product,
audience, problem, proof, website, deepDone, deepSkips}`). "Profile present" = `product` (ideally also
`audience`) holds a real value.

- **Profile present (filled here OR in cold-email).** Score BANT for every company (Budget / Authority /
  Need / Timing 0-5 + tier, vs the stored business); set the render DATA `company_profile` and each
  company's `bant`. Print ONE guard line: *"Scoring fit as
  **[name]**. Different business? Say 'redo' and I'll re-tailor."* No questions. **Stale check:** the
  same `get` returns `"stale": [...]`; if it lists `product` or `audience`, extend the guard line once
  per session with *"…(you confirmed this a while back — still right?)"*. A "redo" save needs `--confirmed`.
- **Profile empty — RUN 1 (`seen` false).** Research + render with BANT left QUIET (the panel's muted
  state is fine; say nothing about it in chat). Then silently `save seen=true`. The barrier-free first
  result is what earns the return.
- **Profile empty — RUN 2 (`seen` true, `deepDone` not true, `deepSkips` < 2).** Make the one-time
  offer, verbatim, nothing added:
  > Want these ranked by how well they fit YOUR business? Tell me what you sell or drop your website, and every company gets a GO / maybe / skip fit. Or just give me more companies.

  If they give a website/sentence (or paste the BANT card's snippet) → the deep round. If they decline
  (more companies, "no", ignore it) → render as-is and `save deepSkips=+1`. When `deepSkips >= 2`,
  suppress the offer and just render (never nag).
- **Profile empty — RUN 3+ (`deepDone` true).** Never offer; score silently + the guard line.

**The deep round (run 2 only — the SHARED capture).** Same business-profile capture the cold-email
builder runs, so doing it here fills the one brain for both skills. (1) **Infer hard first** from the
website/sentence — short `name`, `product`, `audience`, likely `problem`, any `proof`; never ask for
what you can see. (2) **Read it back and confirm** in one line. (3) If gaps remain, ask them as ONE
multiple-choice elicitation form (reuse the cold-email builder's locked problem/proof/ask form, options
re-clothed from the scrape — never open prose). (4) **Save:** `python3 scripts/brain_bridge.py save
--skill company-research --confirmed --base "<wf>/Claude HQ" name="…" product="…" audience="…" problem="…"
proof="…" deepDone=true`, then re-render with fit scored. On Skip: save nothing, `save deepSkips=+1`.
Profile is complete the moment the round is done even if a field was skipped; never re-ask.
**`--confirmed` is required when the user has explicitly confirmed or corrected a business fact**;
without it the brain refuses to replace confirmed identity and lists the refused fields in the save
result's `"rejected": [...]` — if that's non-empty after a user-requested change, re-run with `--confirmed`.

## Procedure
0. **Count gate — FIRST, before research, the brain read, or any render.** The tabbed view fits about
   **8 companies** before tabs scroll off-screen (name-length dependent: ~10 with short names, ~6–7 with
   long ones). So: **1–8 → proceed** (tabbed cheat sheets). **9 or more → STOP: do not research, do not
   render**, and reply with this short note. Do NOT assume the user has any other skill, and do NOT offer
   to "run" one:
   > That's [N] companies, more than the cheat-sheet tabs can show. There's a separate skill built for whole lists, **Full Accounts Scan**, which ranks them all and lets you open any one in full. If you don't already have it, you'll need to download it first.

   You may also offer to do up to 8 here as cheat sheets. Never silently render 9+ tabs (they scroll
   off-screen) and never run 9+ deep researches.
1. **Research the company** with `references/research-framework.md`. WebSearch + WebFetch.
   **Decide the fundamentals — don't collect everything.** Capture: **what they do** (one
   line) → **why now + the angle to use** (triggers + the play, the payoff) → **how big &
   growing/shrinking** (employees + a growth trend ▲/▼) → **money & scale** (revenue; funding
   only if it exists) → **competitors** (3-5 + neutral win/lose angle) → **what they likely
   run** (tools, especially anything near the user's product) → **location**. Source +
   confidence flag on every material fact. ⛔ **No people/leadership section** (this is about
   the company, not names) and **no fabricated contacts**.
   **Last 30 days (neutral intel).** Also gather what's moved in the last ~30 days from FREE
   sources (Reddit/HN/news/app store/Trustpilot, scored by engagement; WebSearch quality is fine,
   no API keys) and write `last30days`: `momentum` = 2-3 STANDING facts (ARR / subs / profitability —
   the old Momentum signals); `signals` = 3-5 DATED events, each carrying **source + date**. Keep it
   **neutral** — what's happening, not a pitch (the cold-email wedge lives in the outreach room).
   If a fact is already in the momentum strip (e.g. ARR), don't repeat it in a signal.
2. **Fit (BANT) — OPTIONAL, scored against the USER's business, never the company alone.**
   Governed by the brain bridge + the return-gated flow above (read the shared `business.*` at the
   top of the run): profile present → score Budget / Authority / Need / Timing (0-5) + tier and set
   `company_profile`; profile empty → BANT stays the muted panel and the offer to switch it on is
   made only on a RETURN visit, never up front. Plain research with no profile = facts only, no fit.
3. **Contacts & enrichment = a separate later layer, NOT this skill.** This skill is **free
   web data only** — it never calls Apollo or spends credits. Verified-contact enrichment and
   fit/qualification are a future layer that builds on this brief.
4. **Render the artefact** (below).
5. **In chat:** a short, plain summary per company + "opened your cheat sheet →". Keep the
   long detail in the artefact.

## Render the artefact (real Bingley desk shell, Pattern A)
The artefact renders **inside the genuine Bingley desk shell** — `assets/cheatsheet-template.html`
is the desk chrome (cloned byte-for-byte from the bundled Bingley desk shell) + research room, with an
empty `room-data` block and a `<!--RESEARCH_TEMPLATE_VERSION:N-->` marker. Shell, data and
instance stay separate, so the shell is interchangeable (swap it, rebuild, content untouched).
⛔ **Never hand-write the HTML.** The only way to build the instance is the render script:

1. Build the `DATA` object (schema below), save to e.g. `/tmp/research-data.json`.
2. Run:
   ```bash
   python3 "<skill dir>/scripts/render_instance.py" \
     --template "<skill dir>/assets/cheatsheet-template.html" \
     --data /tmp/research-data.json \
     --out /tmp/research-instance.html
   ```
   It injects your data into the `room-data` block and copies the desk shell verbatim, printing
   `OK ... shell intact`. If it errors, fix the data — never fall back to hand-built HTML.
   (`<skill dir>` is this skill's folder; resolve it from where SKILL.md lives.)
3. **Publish (fresh view):** `mcp__cowork__list_artifacts`; id `company-research` exists →
   `update_artifact`, else `create_artifact`, with `/tmp/research-instance.html`. Same id every run.
   The on-screen artefact always shows the CURRENT run (fresh), not an accumulating list.
4. **Save / archive the run.** Create `Claude HQ/Company Research/<slug>-<YYYY-MM-DD>/` in the workspace
   (slug = company name kebab-cased; `-batch-` for multi-company) and write `data.json` (the DATA)
   + `cheatsheet.html` (the instance). Research persists on disk though the on-screen view is fresh.
5. **PDF = a one-page "meeting brief" per company (Bingley-styled).** The page has a **Save PDF**
   button (browser print). A print-only `#pbwrap` (built by JS from the same `room-data`, scoped under
   `#pbwrap` so it can't touch the screen card) renders ONE A4 brief per company; print hides the desk
   shell and shows the briefs. **Fixed structure every run:** header (monogram, name, domain, summary,
   Website/LinkedIn pills; a green/amber/grey **fit card** ONLY if the company carries `scoring`) →
   vitals strip → two-column body (Why now + Angle / Business model / Likely running | Last 30 days /
   Competitors / Fit signals BANT — BANT shown only if `bant` present) → a **Walk in with** band
   (relabelled **"Why it's a pass"** when `scoring.decision == "NO-GO"`) → pinned Sources footer. One
   colour per section (blue Why/Competitors/BANT, violet Last 30, amber band, green/grey grade; neutral
   research shows no fit card and no BANT). **Robustness guarantee:** every field is hard-capped and
   each brief is a fixed-height `overflow:hidden` box, so ANY company input yields exactly one page,
   never a broken/2-page doc (less info over broken). Save PDF exports the whole batch, one page each.
   Logos are monogram-only (no network needed). Brief source: `_build/brief_renderer.py`, applied inline by `build_template.py`'s PB_JS at build time (never hand-edit `#pbwrap`).
6. **Hand to the user:** the live artefact is open on screen; mention the Save PDF button.
7. Versioning: copy the live instance → `versions/research-vN.html` + a line in `versions/VERSIONS.md`.

Rebuild the template only via `_build/build_template.py` (clones the Bingley desk shell, re-injects
the research room) — never hand-edit the chrome. After a shell upgrade, just re-run it.

## DATA schema (injected into the `room-data` block)
```js
// contents of <script type="application/json" id="room-data">
{
  generated: "2026-06-20",
  run: {},                               // reserved for a future layer; not shown. This skill spends nothing.
  company_profile: null,                 // the USER's business (what you sell / ICP). Present → BANT scored; null → BANT shows the onboarding CTA
  companies: [{                          // 1-5 companies → folder TABS; ONE per tab, two-column panel (left fits one page; right scrolls)
    name, domain,                        // domain drives the logo (Clearbit → favicon → monogram)
    logo,                                // OPTIONAL data: URI; overrides domain fetch so the logo shows in the network-blocked Cowork panel
    summary: "1-2 plain sentences: what they do",                  // → hero (3-line clamp)
    links: { website, linkedin, maps, x, instagram, tiktok },     // → "Links" brand-icon row INSIDE the hero (real SVG glyphs; rendered only where present; Maps = a google-maps search URL)
    highlights: [ { value:"7M", label:"users" }, ... ],           // → signature-numbers row (2-3 distinctive OPS stats; NOT revenue/employees — those live in Vitals). Keep value + label SHORT (label ≤ ~16 chars / ~2 words); the template WRAPS, it never truncates, so reword or shorten rather than relying on a cut-off.
    angle: "the play: why-now turned into a reason to reach out",  // → Angle-to-use box (alias: why_it_matters)
    triggers: [ { trigger, date, source, confidence } ],          // WHY NOW feature
    snapshot: { founded, hq, employees, stage, revenue_estimate, total_funding, latest_round },  // → LEFT Vitals (employees+growth / revenue / funding / type[=stage]); terse: head=value, "(…)"=caption; funding cell only if present; stage is labelled "Type"
    growth: { dir:"up|down|flat", label:"e.g. ARR +51%" },         // ▲/▼ coloured caption under Employees (CSS triangle)
    ratings: { employer:{score,count,source:"Glassdoor"}, customer:{score,count,source:"Trustpilot|G2"}, google:{score,count,source:"Google|Google Play"} },  // → Reviews bars; any subset; ALWAYS look for a Google rating too
    hooks: [ "smart question to ask in the meeting", ... ],        // → "Walk in with" list (3 best)
    scale_signals: [ "headcount/hiring/growth signal [confidence]", ... ],  // optional/legacy; standing facts now live in last30days.momentum
    last30days: {                                                  // → "Last 30 Days" (right). Collapsed TEASER card (headline + one-liner + momentum strip); clicking opens a bottom-RIGHT overlay (dims the back, sized to content, mirrors "More detail") holding the dated signals. No inline scroll. Prints inline as one block.
      updated: "Jun 2026",                                         // small "updated …" stamp
      headline: "one strong, neutral line — what's most notable right now",
      oneLine:  "the verdict in one sentence (neutral, not a pitch)",
      momentum: [ { value:"$300M+", label:"ARR · >2× YoY" }, ... ],// 2-3 STANDING facts (the old Momentum signals)
      signals:  [ { type:"News|Strategy|Product|Community|Risk", date:"2026", text:"…", source:"Sacra" }, ... ],  // 3-5 DATED last-30-days events
      take:     { quote:"…", by:"App Store review" },              // OPTIONAL pull-quote
      fullBriefUrl: ""                                             // OPTIONAL "Open full brief →" link
    },
    competitors: [ { name, note } ],                               // → compact non-interactive list (name + one-line note); no click action; max 3 shown
    bant: { budget, authority, need, timing, tier:"Strong|Fair|Weak" },  // OPTIONAL; small panel, scored vs company_profile. Omit → onboarding CTA
    scoring: { score, decision:"GO|maybe|NO-GO", grade, tier, reach, buyer_found },  // OPTIONAL; drives the green/amber/grey FIT CARD and the PDF grade (brief_renderer/build_template read it). Omit → no fit card. Set it whenever company_profile is present and you scored fit.
    tech_stack: [ "tool / stack item [confidence]", ... ],         // inside the "More detail" overlay
    dimensions: [ { title, confidence, points: ["sourced point", ...] }, ... ],  // inside the "More detail" overlay
    sources:  [ { label, url } ]                                   // inside the "More detail" overlay
    // NO leadership/people. NO Apollo — free web data only. contacts[] come ONLY from the later enrichment/qualification layer.
  }]
}
```
Layout (v6.4 — meeting prep) mirrors the Bingley **Outbound Room** (blue #3D6FD0, green
#16ad68, amber #d6920f). Folder tabs (each with the **company logo**, monogram fallback;
active tab = white + soft shadow + a company-colour top accent, no blue underline). One
company per tab, a **two-column panel** (left fits one page, right scrolls; overflow-y:auto valve). **LEFT:**
hero (logo + name + domain + summary + a **"Links"** row of brand icons: Website · LinkedIn ·
Maps · X · Instagram · TikTok), a **signature-numbers row** (2-3 ops stats; signed values
tinted ▲green/▼red), **Vitals** (employees+growth / revenue / funding / type), **Reviews**
(Glassdoor employer / Trustpilot-G2 customer / **Google**), a **small BANT** fit (one-line plain-English explainer under the header), and a
**"More detail"** button pinned at the bottom. **RIGHT (scrolls):** why-now+angle, **"Walk in
with"** hooks, **compact competitors** (plain list, no click, max 3), a **"Last 30 Days" teaser**
card (headline + momentum strip) that opens a **bottom-right overlay** (dims the back, sized to
content, mirrors More detail) holding the dated signals. **"More detail"** opens the matching
**bottom-left in-place overlay** (floats over the card, no page scroll; X / backdrop / Esc) holding
tech stack + deeper detail + **Sources**; print reveals both inline. No people/leadership, no
location map, **no Apollo** (free web only). BANT scored only against `company_profile`;
without it the panel is the onboarding hook. Real logos load in a browser; the network-blocked
Cowork panel shows the monogram fallback.

**Never truncate (on screen).** Every label and value WRAPS rather than clipping anywhere in the
on-screen artefact, so author short labels (reword or shorten); the layout never cuts a word off
mid-text. **Exception by design:** the print BRIEF deliberately hard-caps fields (with ellipsis) to
guarantee a one-page brief. Wrap on screen, cap in the brief.

## Graceful degradation
No Apollo → web only, emails flagged best-guess. Thin data → lower confidence, never fake.
Always produce a brief with whatever exists.
