← Plugin catalog
Developer Tools

Nimble

Nimble v1.7.0

Publisher description

From the marketplace listing

Nimble gives agents governed, accurate access to the public web, at a lower token cost per answer. Search finds current pages with ranked results and snippets. Extract returns clean, structured content from any URL, including pages needing JavaScript or interaction. Extraction Templates give consistently shaped records from known sites. Web Search Agents handle open-ended work across many sources, returning findings with citations, for research, enrichment, and dataset building. Also included: ready-made workflows for competitor research, company profiles, meeting prep, market discovery, brand monitoring, SEO, and talent sourcing.

Language: English · Automatically detected from descriptions.

Files & skills

File archives

Plugin package121 files · 623 KBBrowse files →
Skill instructions
brand-mention-monitor25.2 KB

View saved version →

---
name: brand-mention-monitor
description: |
  Scans Reddit, X, LinkedIn, Instagram, TikTok, YouTube, blogs, news, and review platforms
  for brand mentions — scoring each across four dimensions (reach, velocity, sentiment, and
  risk-topic match) so teams can respond before one spirals. Sources adapt to the market: B2B
  brands weight LinkedIn, G2, and trade press; consumer brands weight TikTok, Instagram, and
  YouTube. Each is bucketed into Crisis / Watch / Engage / Log with a suggested owner and
  response window.

  Use when asked to "monitor brand mentions", "scan for brand mentions", "what are people
  saying about [brand]", "brand monitoring", "social listening", "run a brand sweep",
  "find high-risk mentions", or any variation of brand monitoring across web and social.

  Do NOT use for one-time company research or due diligence — use company-deep-dive instead.
  Do NOT use for competitor messaging/positioning analysis — use competitor-positioning instead.
  Do NOT use for funding/hiring/business signals on a competitor — use competitor-intel instead.
allowed-tools:
  - Bash(nimble:*)
  - Bash(date:*)
  - Bash(cat:*)
  - Bash(mkdir:*)
  - Bash(python3:*)
  - Bash(echo:*)
  - Bash(jq:*)
  - Bash(ls:*)
  - Read
  - Write
  - Agent
  - AskUserQuestion
metadata:
  author: Nimbleway
  version: 1.6.1
  category: marketing
---

# Brand Mention Monitor

Scans the web and social media for brand mentions, scores each one on reach, velocity, sentiment, and risk-topic match, and surfaces the ones that need attention — bucketed into Crisis / Watch / Engage / Log with a suggested owner.

---

## Onboarding message

When this skill is triggered for the first time in a session, send this message:

> 👋 **Brand Mention Monitor is ready.**
>
> This skill scans Reddit, X, LinkedIn, Instagram, TikTok, YouTube, blogs, news, and review platforms for mentions of your brand — scoring each one across reach, velocity, sentiment, and risk so you see what matters before it spirals, and telling you exactly who should respond and how fast.
>
> To start, just say:
> _"Monitor mentions of [brand name]"_
>
> Or try:
> - "What are people saying about [brand] this week?"
> - "Run a brand sweep for [brand] — last 30 days"
> - "Find high-risk mentions of [brand]"
> - "How does [brand] compare to [competitor] in the conversation?"
>
> Would you like me to save your preferences so I skip the questions next time?

---

## Preflight

Follow the transport selection and standard preflight from `references/nimble-playbook.md`: pick CLI vs MCP at session start, then run the parallel preflight calls (date, profile, memory index) simultaneously. Tag every Nimble CLI call: `nimble --client-source nimble-agent-skills <subcommand>`.

From the profile (`~/.nimble/business-profile.json`): load brand name, competitors, routing preferences, and `last_runs.brand-mention-monitor` for date windowing. Pre-populate setup questions so the user confirms rather than re-enters. If no profile exists, follow the first-run onboarding flow in `references/profile-and-onboarding.md` and create a stub after the first run. Check `~/.nimble/memory/index.md` to understand what mention data already exists before sweeping.

---

## How to start

**Before asking anything, do two quick research steps:**

**Step A — Resolve brand variants automatically:**
Search for the brand the user named to discover all alternate spellings, hashtags, product names, handles, and common misspellings. Do not ask the user for this. Use what you find to build a comprehensive search term list for the sweep.
- Search: `"[brand name]" official name OR handle OR "also known as" OR hashtag`
- Check the brand's main product names and any sub-brands that get mentioned independently
- For brands with common-word names, find the disambiguating terms (industry, founder, domain) so the sweep doesn't pull unrelated noise
- Add all confirmed variants to your search queries silently — the user never needs to see this step

**Step B — Profile the brand automatically:**
Search to establish the brand's industry, business model (B2B/B2C), geography, language, and audience before asking the user. This selects the market-specific source profile (see Step 0) and calibrates scoring — what counts as "high reach" differs for a niche B2B tool vs a consumer app with millions of users. Surface what you find and fold it into the confirmation question rather than asking blind.
- Search: `"[brand name]" company OR product industry OR category`
- Determine: B2B vs B2C, industry vertical, primary geography/language, rough audience size
- Use this to pick the source profile and the default risk-topic dictionary for the industry

**Then ask in a single message:**

> "Before I run — just confirming a few things:
> 1. **Brand:** I found [brand] — a [category] [B2B/B2C] company targeting [audience]. Is that right, and any competitors to track alongside it?
> 2. **Date range:** How far back should I look? (default: last 7 days — or give me a window like 'June 1–15' or 'since the launch')
> 3. **Depth:** Quick scan (faster) or deep sweep (more thorough, more sources)? (default: deep)
> 4. **Routing:** When I find a Crisis-tier mention, who should I flag it for? (default: marketing team — or name a PR lead, legal, founder, etc.)"

**Output is always the triage console rendered directly in Claude.** Do not ask about format or output options.

**Exceptions — skip asking entirely if:**
- The user provided all the above in their initial message
- The user has run this skill before in the session (use prior config)

**Defaults if user says "just run it":**
- Date range: last 7 days
- Depth: deep
- Risk topics: auto-detected for the industry
- Routing: flag to marketing team

**Disambiguation:** If the brand name is ambiguous after research, confirm before proceeding:
> "Just to confirm — by [brand], do you mean [Option A] or [Option B]?"

---

## Step 0 — Source profile (market-specific, not fixed list)

**This is the most important configuration step.** Source selection must match where the brand's audience actually talks. Do not scan all platforms equally — weight the channels that matter for this company type.

### B2B enterprise software / SaaS
**Primary (run every pass):** LinkedIn, G2, Capterra, Hacker News, r/sysadmin, r/devops, r/[category], trade press (InfoQ, TechCrunch, ZDNet, The Register), Glassdoor (employee signal)
**Secondary:** Reddit broad, X/Twitter (exec accounts, analysts), Medium/Substack
**Deprioritize:** TikTok, Instagram, Facebook (low-signal for B2B buyers)

### Consumer brand / e-commerce
**Primary (run every pass):** TikTok, Instagram, X/Twitter, Reddit, YouTube, Facebook, Trustpilot, Google Play / App Store
**Secondary:** News press, blogs, Pinterest
**Deprioritize:** HN, LinkedIn (low signal for consumer sentiment), trade press

### Regulated industry (finance, healthcare, pharma, insurance)
**Primary:** News press (Reuters, AP, Bloomberg, sector-specific), regulatory watchdog sites, journalist Twitter accounts, LinkedIn exec commentary, formal review platforms (BBB, Consumer Financial Protection Bureau)
**Secondary:** Reddit, X, forums
**Deprioritize:** TikTok, Instagram (reputational risk from user-gen content is lower priority than press/regulatory)

### Regional / non-English brand
**Primary:** Local-language news, regional forums and social platforms (e.g. Weibo for China, VK for Russia, Naver for Korea), local-language Twitter/Instagram
**Secondary:** English-language global platforms only if relevant
**Note:** Use Nimble `locale` and `country` parameters to surface local-language results

### Startup / developer tool
**Primary:** HN, Reddit (r/programming, r/webdev, r/[category]), GitHub discussions, Dev.to, X/Twitter (developer influencers), ProductHunt
**Secondary:** LinkedIn, Medium, TechCrunch

---

## Step 1 — Mention sweep

For deep sweeps, fan out across source tiers using parallel sub-agents (max 4 concurrent) via the `Agent` tool — one agent per source group (e.g., social, review platforms, news, community). Follow the parallel-gathering pattern in `references/nimble-playbook.md`. Always include a fallback: if a sub-agent fails, continue with remaining agents and note the gap in the output.

Run sources matching the brand's profile (Step 0). Use `--search-depth lite` for discovery; use `--search-depth deep` for full content on high-score candidates. Apply `--start-date` / `--end-date` from the user's date range on every search call. Tag every call: `nimble --client-source nimble-agent-skills search ...`

### Core queries (run for every brand type)
- `"[brand name]" site:reddit.com`
- `"[brand name]" site:x.com`
- `"[brand name]" news`
- `"[brand name]" review OR complaint OR "doesn't work"` — risk sweep
- `"[brand name]" love OR recommend OR "game changer"` — opportunity sweep
- Nimble `focus:"social"` query `"[brand name]"` — broad social

### Risk-specific queries (run every pass)
Build from the risk topic dictionary for this brand type plus any user-specified topics:
- `"[brand name]" [risk topic 1]`
- `"[brand name]" [risk topic 2]`
- `"[brand name]" lawsuit OR legal OR "class action"`
- `"[brand name]" outage OR "not working" OR down` (for SaaS/tech)
- `"[brand name]" recall OR safety OR "side effects"` (for consumer/pharma)
- `"[brand name]" scam OR fraud OR fake`

### Velocity check (run on high-score candidates — re-runs only)
For any mention that scored above 50 on a previous run, re-fetch the post to compare engagement counts. If this is a first-pass sweep with no prior baseline, skip hourly-rate velocity scoring — proxy signals only (see Step 2 velocity gating rules).

---

## Step 2 — Scoring each mention (four dimensions)

Score every mention 0–100 on each dimension, then compute composite.

### Reach / Visibility (0–100)
How many people can see this?
| Signal | Points |
|---|---|
| 500K+ followers / major publication | +35 |
| 100K–500K followers | +25 |
| 10K–100K followers | +15 |
| 1K–10K followers | +8 |
| Under 1K | +3 |
| Thread with 100+ replies/comments | +20 |
| Post going viral (100+ reposts in <1h) | +25 |
| High-authority domain (TechCrunch, Reuters, etc.) | +25 |
| Reddit front page / 1K+ upvotes | +25 |

### Velocity (0–100) — the differentiator
How fast is this gaining ground? This is what separates "viral forming" from "stale."

**Baseline requirement:** Hourly-rate rows (+40/+25/+10) require two data points — a prior engagement count and the current count — to compute a real rate. On a first-pass single sweep you do not have a baseline. Apply the following rules:
- **Re-run with prior data (baseline available):** use the full table below.
- **First-pass single sweep (no baseline):** skip hourly-rate rows; score only observable proxy signals (cross-platform pickup, press pickup, absolute engagement thresholds). Set velocity label to `~estimated` on the card so the user knows it is inferred, not measured.

| Signal | Points | Requires baseline? |
|---|---|---|
| Engagement climbing 50%+/hour vs. baseline | +40 | Yes |
| Engagement climbing 20–50%/hour | +25 | Yes |
| Engagement climbing 5–20%/hour | +10 | Yes |
| Flat engagement | +0 | Yes |
| Cross-platform pickup (mention appearing on 2+ platforms) | +20 | No |
| Press picking up a social post | +25 | No |
| 2.3K+ reposts in 40 minutes (crisis velocity) | +40 | No — observable in single sweep |

**Tier upgrade on rapid re-check:** if re-running within 2 hours of a previous run, re-fetch every mention that scored Watch or higher and compare engagement counts. If engagement has grown 20%+ since last check, upgrade the tier and flag it `↑ accelerating`. If flat or declining, note `→ stable` or `↓ declining`.

### Sentiment (0–100 risk score; 0–100 opportunity score)
| Negative signals (risk) | Points |
|---|---|
| Explicit negative sentiment | +20 |
| Complaint + product/service failure language | +20 |
| Sarcasm detected ("great job [brand]…") | +15 |
| All-caps, exclamation marks, profanity | +10 |
| Replies amplifying the negative tone | +15 |
| Positive signals (opportunity) | Points |
| Organic praise, unprompted | +20 |
| Purchase intent or recommendation | +20 |
| User-generated content shareable by brand | +15 |
| Journalist / analyst positive mention | +20 |

### Risk topic match (0–100)
Does this hit a flagged risk category?
| Topic category | Points |
|---|---|
| Legal / regulatory / lawsuit / class action | +40 |
| Safety / health / injury / recall | +40 |
| Executive misconduct or controversy | +35 |
| Product outage or critical failure | +30 |
| Pricing / billing complaint (if viral) | +20 |
| Competitor comparison framing brand negatively | +15 |
| False claim / misinformation about brand | +25 |

### Composite score
`composite = (reach × 0.30) + (velocity × 0.30) + (max(risk_sentiment, risk_topic) × 0.25) + (opportunity × 0.15)`

---

## Step 2.5 — Memory dedup (filter already-known mentions)

Before assigning tiers, run the dedup lifecycle from `references/memory-and-distribution.md` against prior brand-mention-monitor reports in `~/.nimble/memory/reports/`.

Skill-specific rules:
- **Fingerprint:** `{url, platform, published_date}` — normalize URLs (strip query params, trailing slashes).
- **Returning with score shift ≥10:** keep in feed, mark with `↩ returning · score changed` badge.
- **Already known, score unchanged:** move to Log tier; suppress from Crisis/Watch/Engage unless user requested full history.
- **Summary line:** show at top of triage console: `X net-new · Y returning (score changed) · Z suppressed (already logged)`.
- **Persist:** after the run, save mention fingerprints to `~/.nimble/memory/reports/brand-mention-monitor-{BRAND}-{YYYY-MM-DD}.md` and append a `log.md` entry per `references/memory-and-distribution.md`.

---

## Step 3 — Tier assignment and routing

Assign every mention to exactly one tier. Teams act on tiers, not numbers.

| Tier | Score | Color | Action | Suggested owner | Window |
|---|---|---|---|---|---|
| Crisis | 80–100 | 🔴 | Route immediately | PR + Legal + Leadership | Respond <2h |
| Watch | 50–79 | 🟠 | Assign owner, monitor velocity | Marketing / Comms | Respond <24h |
| Engage | Any score, positive high-reach | 🟢 | Amplify / thank / share | Marketing / Social team | Act within 48h |
| Log | <50, no risk signals | ⚪ | No action, searchable record | — | — |

**Crisis-tier mentions must surface immediately** — they should appear at the very top of the output with a full decision card showing: excerpt, reach, velocity, reason for flagging, suggested owner, response window, and suggested draft action.

---

## Output template (REQUIRED)

Claude MUST follow `references/template.html` exactly. Load the template, substitute real data, keep all CSS, JS, and interaction patterns identical.

### Response structure (wrap the widget)

The chat response must follow the standard contract: open with a **TL;DR** and close with a **What This Means** section, with the triage console in between.

1. **TL;DR** (first, 2–4 lines): total mentions, the tier breakdown (`X Crisis · Y Watch · Z Engage`), and the single most urgent item with its response window.
2. **The triage console** — the interactive widget, rendered inline per the rules below.
3. **What This Means** (final top-level section): what the sweep signals for the brand right now and the top 1–3 recommended actions, each tied to a tier and owner.

### Rendering — INLINE FIRST, ALWAYS (read this before producing output)

The triage console is an **interactive widget that must be rendered inline in the chat**. A downloadable file is a *secondary* artifact, never the primary deliverable. Follow this sequence exactly, every run:

1. **Render the triage console inline FIRST.** Write the fully-populated `brand-mention-monitor-{YYYY-MM-DD}.html` to `~/.nimble/` using the Write tool, then emit the HTML content inline in the conversation so the user sees the interactive widget immediately. This inline output is the main deliverable and must happen before anything else is offered.
2. **Then, and only then, offer downloads.** After the inline output is on screen, confirm that `~/.nimble/brand-mention-monitor-{YYYY-MM-DD}.html` and `~/.nimble/brand-mention-monitor-{YYYY-MM-DD}.md` have been saved and offer them to the user for download or sharing.

**Hard rules:**
- **Never** respond with only a file path. If the user sees a path but no inline triage console, the run has failed its primary job.
- **Do not ask** the user whether they want it inline or as a file, and do not ask about format — inline is always the default and the file always accompanies it.
- The inline output and the saved HTML are the **same artifact** — render the identical template, do not produce a stripped-down inline version.

**If the inline output genuinely cannot be produced:**
1. Say so explicitly in one short line — e.g. "I couldn't render the triage console inline this time, so here's the file instead."
2. Confirm the file has been saved to `~/.nimble/brand-mention-monitor-{YYYY-MM-DD}.html` as the fallback.

Never silently fall back to a file — if inline fails, name the failure so the user knows it was the environment, not the intended behavior.

### Output template spec (`brand-mention-monitor-{YYYY-MM-DD}.html`)

**Visual identity — distinct from all other skills:**
- Triage console aesthetic — dense, action-oriented, not a report
- Crisis-tier mentions get a full-width alert card at the top before the feed
- Four score pips per card: Reach / Velocity / Sentiment / Risk — not just one urgency signal
- Tier badge replaces composite number as the primary visual label
- Sources searched panel (collapsible) showing exactly what was queried this run
- Date range picker (custom From/To date inputs) that re-filters the feed client-side
- Platform filter + Tier filter stacked
- Velocity indicator on each card: `↑ accelerating` / `→ stable` / `↓ declining`

**Score pip colors:**
- Reach: `#185FA5` (blue)
- Velocity: `#854F0B` (amber — urgency signal)
- Sentiment: `#A32D2D` (red for risk) / `#3B6D11` (green for opportunity)
- Risk topic: `#A32D2D` (red)

**Tier colors:**
- Crisis 🔴: red left border + red tier badge
- Watch 🟠: amber left border + amber tier badge
- Engage 🟢: green left border + green tier badge
- Log ⚪: gray border

**Required sections (in this render order):**
1. Brand header bar — brand name · date range (from onboarding, read-only label e.g. "Window: Jun 11–18, 2026") · total mentions
2. Sources searched — collapsible panel showing every source queried this run with ✓ marks + mention-count badges; click a source to filter the feed by platform
3. Score summary row — four aggregate meters: avg Reach / top Velocity / top Sentiment risk / top Opportunity
4. Market visibility (share of voice) — donut chart (white center) + legend showing the brand's share of conversations vs competitors in this window. Hovering a segment or legend row highlights it and shows that brand's share in the donut center. CLICKING a segment or row opens a detail panel with critical intelligence for that brand: mention count, trend vs last window (color-coded red rising / green falling), estimated reach, a positive/neutral/negative sentiment split bar, and a one-line Signal takeaway explaining what's driving that brand's share. Use `mvBrands[]`: first entry is the user's brand; each entry is `{name, pct, fill, stroke, mentions, trend, reach, pos, neu, neg, signal}`. Percentages sum to ~100 (include an "Other" bucket); pos+neu+neg sum to 100.
5. Crisis alert cards — full-width, only shown if tier = Crisis; includes excerpt, all 4 scores, published date, reason, owner, window, suggested action
6. Mentions by platform — horizontal bars, clickable to filter feed (synced with sources panel)
7. Geographic breakdown — interactive world map with mention hotspots. Use `geoPoints[]`: each point `{name, lat, lng, tier, sources}` where tier is 'high'/'med'/'low' (drives dot color red/amber/blue, size, pulse ring on high-tier). Mention count is derived from the length of `sources`. Each entry in `sources` is `{head, plat, meta, url, tier}` — head = headline, plat = platform key, meta = followers/upvotes and date, tier = that mention's own tier, and url MUST be the EXACT Nimble result URL (article/post/thread), never a homepage. Hovering a point shows a summary tooltip; CLICKING pins a detail panel below the map listing every source from that location with an open link to the exact URL. Map geometry loads from world-atlas via D3 (jsDelivr/unpkg/cdnjs fallbacks; shows "Map unavailable" if all blocked). lat/lng = country centroid.
8. Mention feed (2-column grid) — sorted by composite score, each card with 4 score pips + tier badge + velocity arrow + platform tag + publish date + source link
9. Filter bar — Tier (All / Crisis / Watch / Engage / Log) + Score range, stacked with dismissable chips

**Interaction:**
- Click any card to expand: full quote, score breakdown explaining each score, routing suggestion, suggested action text
- Click "Sources searched" header to expand/collapse the sources panel; click a source row to filter the feed by platform
- Market visibility donut: hover a segment or legend row to highlight and preview share; click to open the detail panel with that brand's mentions, trend, reach, sentiment split, and signal
- Platform bars clickable to filter feed (synced with sources panel)
- Geographic map: hover a hotspot for the summary; click it to pin a panel listing the exact sources (with open links) from that location
- All filters stack with dismissable chips

**Visualization libraries:**
The template loads D3 (`d3.min.js`) and topojson (`topojson.min.js`) from cdnjs for the geographic map, plus world-atlas country geometry from jsDelivr/unpkg/cdnjs (with fallbacks). The market visibility donut uses a plain `<canvas>` with no dependency. Keep these script tags.

**Source URL rule:**
Every mention must include `<a href="[EXACT_NIMBLE_URL]" class="src-link">↗ source</a>` with the exact article/post URL from Nimble. Never use a homepage.
- CORRECT: `https://reddit.com/r/SaaS/comments/abc123/title`
- CORRECT: `https://x.com/username/status/1234567890`
- WRONG: `https://reddit.com`  WRONG: `https://x.com`

---

### Markdown output spec (`brand-mention-monitor-{YYYY-MM-DD}.md`)

```markdown
# Brand Mention Monitor — [Brand Name]
**Date range:** [DATE RANGE]
**Generated:** [TIMESTAMP]
**Total mentions:** [N]
**Sources searched:** [list]

## TL;DR
[2–4 lines: total mentions · tier breakdown (X Crisis · Y Watch · Z Engage) · the single most urgent item + its response window]

## Crisis tier (80–100) — respond <2h
- **[Tier] Score:[N]** | [Platform] | [Author] | R:[N] V:[N] S:[N] RT:[N]
  "[Excerpt]"
  Owner: [PR/Legal/Marketing] · Window: <2h
  Action: [Suggested action]
  Source: [EXACT_NIMBLE_URL]

## Watch tier (50–79) — respond <24h
...

## Engage tier — amplify within 48h
...

## Log (no action required)
...

## What This Means
[What the sweep signals for the brand right now + the top 1–3 recommended actions, each tied to a tier and owner]
```

---

## Source URL rule

Every mention must include the exact Nimble result URL.
**CORRECT:** `https://reddit.com/r/SaaS/comments/abc123/title`
**WRONG:** `https://reddit.com`

---

## Distribution

After output is rendered inline and files are saved, offer sharing following `references/memory-and-distribution.md` (connector detection, `AskUserQuestion` flow, Notion/Slack options).

Skill-specific routing: push `brand-mention-monitor-{YYYY-MM-DD}.md` to Notion (full report); post Crisis + Watch tiers only to Slack (not the full feed). Use destination from `integrations` in `~/.nimble/business-profile.json` if set; otherwise ask and save for next time.

---

## Saved preferences

After first run, ask:
> "Save preferences? I'll remember [brand], source profile, risk topics, and routing so future runs skip setup."

Say **"change settings"** to update anytime.

---

## Re-run behavior

For date window calculation, follow the Smart Date Windowing pattern in `references/nimble-playbook.md` — use `last_runs.brand-mention-monitor` from the profile.

> "Sweeping for new mentions since [last run]. Anything to add to the watch list?"

Net-new mentions only. Crisis and Watch items carry forward until marked handled.

---

## End-of-run next steps

After delivering the triage console and confirming distribution, suggest the most relevant follow-on action based on what the sweep surfaced:

- **If any Watch or Crisis mentions came from competitors framing your brand negatively:** → "Want to go deeper on what competitors are saying about you? Try the `competitor-positioning` skill — it maps competitor messaging, positioning gaps, and attack vectors in detail."
- **If the brand appeared in funding, hiring, or M&A context:** → "There are signals here that go beyond brand sentiment — the `competitor-intel` skill tracks business moves, hiring signals, and strategic shifts that could affect your market position."
- **If the sweep found mostly positive mentions worth amplifying:** → "Some of these are worth turning into content or outreach — the `competitor-positioning` skill can help you identify the messaging angles your audience is already responding to."
- **If this is the first run (no prior memory):** → "Run this again in 7 days to get velocity data and start seeing trends. I'll have a baseline for comparison on the next pass."

Present only the suggestions relevant to this run — do not list all four if only one applies.

Referenced files: 6

company-deep-dive13.4 KB

View saved version →

---
name: company-deep-dive
description: |
  Use this skill ANY TIME the user asks about a specific company. Triggers:
  "tell me about [company]", "research [company]", "what does [company] do",
  "who is [company]", "look up [company]", "company deep dive", "due diligence
  on [company]", "background on [company]", "dig into [company]", "analyze
  [company]", or evaluating a company for investment, partnership, or sales.
  MUST be used instead of answering from memory — fetches real-time web data
  (funding, leadership changes, product launches, news) your training data
  lacks. Use even for well-known companies.

  Produces a sourced 360° report covering funding, leadership, product/tech,
  market position, news, and strategic outlook with dates and URLs.

  Do NOT use for multi-company competitor monitoring (use competitor-intel)
  or meeting prep with attendees (use meeting-prep).
allowed-tools:
  - Bash(nimble:*)
  - Bash(date:*)
  - Bash(cat:*)
  - Bash(mkdir:*)
  - Bash(python3:*)
  - Bash(echo:*)
  - Bash(jq:*)
  - Bash(ls:*)
  - Read
  - Write
  - Edit
  - Glob
  - Grep
  - Agent
  - AskUserQuestion
metadata:
  author: Nimbleway
  version: 1.6.1
  category: business-research
---

# Company Deep Dive

360° company research powered by Nimble's web data APIs.

User request: $ARGUMENTS

**Before running any commands**, read `references/nimble-playbook.md` for Claude Code
constraints (no shell state, no `&`/`wait`, sub-agent permissions, communication style).

---

## Instructions

### Step 0: Preflight

Follow the transport selection + standard preflight from `references/nimble-playbook.md` — pick CLI or MCP at session start, then run the standard preflight calls (date calc, today, profile, memory index) in parallel.

From the results:
- CLI missing or API key unset → `references/profile-and-onboarding.md`, stop
- Tag all `nimble` CLI calls: `nimble --client-source nimble-agent-skills <subcommand>`. MCP requests are attributed at the transport level — see `references/nimble-playbook.md`.
- Profile exists → note it for context (company name helps frame the research). Read
  `~/.nimble/memory/companies/index.md` to check if the target company already has
  prior research. Follow `[[path/entity]]` cross-references to load related context.
  - **Prior research exists:** Load it. Run in **refresh mode** — focus on what's new
    since the last report date. Tell the user: "I have prior research on [Company]
    from [date]. Refreshing with latest data."
  - **No prior research:** Run in **full mode** — comprehensive across all dimensions.
- No profile → that's fine. Company deep dive doesn't require onboarding (unlike
  competitor-intel). Proceed directly to Step 1.

### Step 1: Identify Target Company

Parse the target company from `$ARGUMENTS` or the user's message.

**If clear** (e.g., "research Stripe", "tell me about Datadog"):
- Extract the company name
- Run two Bash calls simultaneously to confirm identity:
  - `nimble search --query "[Company] official site" --max-results 3 --search-depth lite`
  - `nimble search --query "[Company] company overview" --max-results 5 --search-depth lite`
- Confirm briefly: "Researching **[Company]** ([domain])..."

**If ambiguous** (e.g., "research Mercury" — could be bank, auto, or other):
- Ask one clarifying question with the top candidates

**If missing** — ask: "Which company would you like me to research?"

**Scope selection** — if the user hasn't specified depth, default to **full deep dive**.
If they say "quick overview", "brief", or "summary", run a **quick mode** that skips
the Deep Extraction step and produces a shorter report.

### Step 2: WSA Discovery

Discover available WSAs for the target company's domain. Run both searches
simultaneously:

```bash
nimble extract:templates list --limit 100  # then filter items for "{company-domain}"
```

```bash
nimble extract:templates list --limit 100  # then filter items for "{company-name}"
```

From the results, filter for WSAs with `entity_type` matching SERP or PDP, and
prefer `managed_by: "nimble"`. Validate each with
`nimble extract:templates get --extract-template-name {name}`, then cache discovered WSA names + params
for the run. Pass them to dimension agents in Step 3 for enrichment alongside
`nimble search`. If no WSAs found, continue with `nimble search` alone.

### Step 3: Parallel Research Across Dimensions (sub-agents)

Read `references/dimension-agent-prompt.md` for the full agent prompt template.
Follow the sub-agent spawning rules from `references/nimble-playbook.md`
(bypassPermissions, batch max 4, explicit Bash instruction, fallback on failure).

Spawn `nimble-researcher` agents (`agents/nimble-researcher.md`) with
`mode: "bypassPermissions"`. Each agent researches one dimension of the company.
Pass discovered WSA names from Step 2 to each agent so they can use them for
enrichment alongside `nimble search`.

**Important:** The Nimble API has a 10 req/sec rate limit per API key. With each agent
running 4-5 searches in parallel, limit concurrent agents to 2 per batch to stay under
the limit. Run overview searches in their own phase, not alongside agent batches.

**Call estimation & Scaled Execution:** Before launching agents, estimate total API
calls: 2 overview searches + ~5 searches per agent × 5 agents = ~27 calls. Each agent
should use `extract-batch` or `agent run-batch` for 11+ calls instead of individual
calls. See the Scaled Execution pattern in `references/nimble-playbook.md` for tier
selection.

**Phase A — Overview searches** (run directly, before agents):

- `nimble search --query "about" --include-domain '["[domain]"]' --max-results 3 --search-depth lite`
- `nimble search --query "[Company] Wikipedia OR Crunchbase OR Pitchbook" --max-results 5 --search-depth lite`

These give foundational context (founding date, HQ, employee count, mission) that
frames all dimensional findings.

**Phase B — Batch 1** (2 agents simultaneously):

| Agent | Dimension | Focus |
|-------|-----------|-------|
| 1 | **Funding & Financials** | Funding rounds, valuation, revenue signals, investors, financial health |
| 2 | **Product & Technology** | Products, tech stack, recent launches, engineering blog, open-source |

**Phase C — Batch 2** (2 agents simultaneously):

| Agent | Dimension | Focus |
|-------|-----------|-------|
| 3 | **Leadership & Team** | Founders, C-suite, key hires, departures, team size, culture signals |
| 4 | **Recent News & Events** | Press coverage, announcements, partnerships, awards, conferences |

**Phase D — Batch 3** (1 agent):

| Agent | Dimension | Focus |
|-------|-----------|-------|
| 5 | **Market Position** | Competitors, market share, positioning, analyst coverage, customer reviews |

**Refresh mode adjustment:** If prior research exists, pass the known facts to each
agent as context so they focus on what's new. Agents should use `--start-date` to
filter for recent data only.

**Fallback:** If any agent fails or returns empty, run those dimension searches
directly from the main context. Don't leave gaps in the report.

### Step 4: Deep Extraction

From all agents' results, identify the **top 5-8 most informative URLs** across
dimensions. Prioritize:
- Funding announcements with specific amounts
- Official product/feature pages
- Executive interviews, podcast appearances, or conference talks
- In-depth analyst or journalist profiles
- The company's own about/team page

Make one Bash call per URL, all simultaneously:

`nimble extract --url "https://..." --format markdown`

For extraction failures, follow the fallback in `references/nimble-playbook.md`.

**Quick mode:** Skip this step entirely. Report from search snippets only.

**WSA enrichment:** If WSAs were discovered in Step 2, use them here for richer
extraction on key URLs before falling back to `nimble extract`.

### Step 5: Synthesize Report

Structure the output as a **360° Company Report**:

```
# [Company Name] — Deep Dive
*As of [today's date]*

## Quick Assessment
[2-3 sentence verdict: what this company is, where they stand, and the one thing
that matters most right now. This is the "if you read nothing else" paragraph.]

## Company Overview
- Founded: [year] | HQ: [location] | Employees: [estimate]
- Domain: [domain] | Industry: [industry]
- Mission/focus: [one line]

## Funding & Financials
[Latest round, total raised, key investors, valuation signals, revenue indicators.
Every claim dated and sourced.]

## Leadership & Team
[Founders, C-suite, notable recent hires or departures. Executive perspectives
on company direction — direct quotes when available from interviews or talks.]

## Product & Technology
[Core products, recent launches, tech stack signals, engineering culture,
open-source contributions. What they're building and how.]

## Market Position
[Key competitors, differentiation, market share signals, analyst perspectives,
customer sentiment from reviews (G2/Capterra/Reddit).]

## Recent News & Events
[Chronological, most recent first. Each entry dated with source.]

## Strategic Outlook
[Synthesis across all dimensions: where the company is heading, key risks,
growth signals, and strategic bets. This is insight, not summary.]

## Sources
[Numbered list of all URLs cited in the report]
```

**Core rules:**
- Every factual claim must have a date and source URL.
- Lead with the Quick Assessment — most readers stop there.
- Say "no public data found" for a dimension rather than speculating.
- Distinguish between confirmed facts and inferred signals.
- Executive quotes add credibility — include direct quotes from interviews,
  earnings calls, or conference talks when found.
- In refresh mode: lead with "What's New Since [last date]" before the full sections.

### Step 6: Save to Memory

Make all Write calls simultaneously:

- Report → `~/.nimble/memory/reports/company-deep-dive-[date].md`
- Company profile → `~/.nimble/memory/companies/[company-name-slug].md`
  (use the format in `references/memory-and-distribution.md`). Add `[[path/entity]]`
  cross-references for key people discovered (e.g., `[[people/jane-smith]]`),
  competitors in the same space (e.g., `[[competitors/widgetco]]`), and any other
  related entities.
- If profile exists → update `last_runs.company-deep-dive` in
  `~/.nimble/business-profile.json`
- Follow the wiki update pattern from `references/memory-and-distribution.md`: update
  `index.md` rows for all affected entity files, append a `log.md` entry for this run.

The company profile in `companies/` should contain structured key facts (overview,
financials, leadership, products) that can be loaded by future runs of any skill
that needs context on this company.

### Step 7: Share & Distribute

**Always offer distribution — do not skip this step.** Follow
`references/memory-and-distribution.md` for connector detection, sharing flow, and
source links enforcement.

### Step 8: Follow-ups

- **Go deeper** on a dimension → focused searches on that topic
- **Compare with another company** → side-by-side analysis
- **"What about [specific topic]?"** → targeted search + extraction
- **"Looks good"** → done

**Sibling skill suggestions:**

> **Next steps:**
> - Run `competitor-intel` to track this company as a competitor over time
> - Run `meeting-prep` if you're meeting with someone at this company
> - Run `competitor-positioning` to analyze their messaging vs yours

---

## Agent Teams Mode (Dual-Mode)

Check at startup: `echo $CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS`

**Team mode** (flag set): Spawn 3 **teammates** instead of sub-agents. Each teammate
covers related dimensions and can message the others to cross-check findings.

| Teammate | Dimensions | Cross-checks with |
|----------|-----------|-------------------|
| **Financials & News** | Funding, revenue, recent news, events | Market (valuation vs positioning) |
| **Product & Leadership** | Products, tech stack, founders, key hires | Financials (pivots vs funding) |
| **Market** | Competitors, positioning, reviews, analysts | Product (differentiation claims) |

Lead (you): Create shared tasks, wait for all teammates to complete, then synthesize
the final report. When a teammate finds a claim that another should verify (e.g., a
funding amount that implies a valuation), it posts a task for the relevant teammate.

**Solo mode** (flag not set): Standard sub-agent flow from Step 3.

---

## What This Skill Is NOT

- **Not competitor monitoring.** For tracking multiple competitors over time, use
  `competitor-intel`. This skill goes deep on ONE company.
- **Not meeting prep.** For researching people you're meeting with, use `meeting-prep`.
  This skill researches companies, not individuals.
- **Not financial advice.** This is intelligence gathering from public sources, not
  investment analysis or due diligence certification.
- **Not real-time monitoring.** This produces a point-in-time report. For ongoing
  tracking, run it again later or use `competitor-intel` with the company added.

---

## Error Handling

See `references/nimble-playbook.md` for the standard error table (missing API key, 429,
401, empty results, extraction garbage). Skill-specific errors:

- **Search 500:** Retry once without `--focus` flag. If still failing, retry with a
  simplified query (shorter terms, no date filter). Log the failure but don't skip
  the dimension.
- **Search timeout:** Retry once, then skip that call and continue — consistent with
  the playbook's timeout policy.
- **Company not found:** Retry with domain, alternative names, or parent company
- **Empty results for a dimension:** Note "No public data found" — don't speculate

Referenced files: 4

competitor-intel14.7 KB

View saved version →

---
name: competitor-intel
description: |
  Searches the live web via Nimble APIs to monitor competitors and produce a
  structured intelligence briefing. Runs parallel searches for news, product
  launches, hiring signals, and funding — then compares against previous
  findings to highlight only what's new.

  Use this skill when the user asks about competitors, competitive intelligence,
  or what rival companies are doing. Common triggers: "what are my competitors
  doing", "competitor update", "competitor news", "competitive landscape",
  "market intel", "what's new with [company]", "track [company]", "competitor
  briefing", "who's making moves", "competitive analysis", "losing deals to
  [company]", "battlecard". Also use before board meetings or strategy sessions
  when the user wants competitive context.

  Requires the Nimble CLI (nimble search, nimble extract) for live web data.
  Do NOT use for single-company deep dives (use company-deep-dive), meeting
  prep with attendees (use meeting-prep), or non-business queries.
allowed-tools:
  - Bash(nimble:*)
  - Bash(date:*)
  - Bash(cat:*)
  - Bash(mkdir:*)
  - Bash(python3:*)
  - Bash(echo:*)
  - Bash(jq:*)
  - Bash(ls:*)
  - Read
  - Write
  - Edit
  - Glob
  - Grep
  - Agent
  - AskUserQuestion
metadata:
  author: Nimbleway
  version: 1.6.1
  category: business-research
---

# Competitor Intelligence

Real-time competitive intelligence powered by Nimble's web data APIs.

User request: $ARGUMENTS

**Before running any commands**, read `references/nimble-playbook.md` for Claude Code
constraints (no shell state, no `&`/`wait`, sub-agent permissions, communication style).

---

## Instructions

### Step 0: Preflight

Follow the transport selection + standard preflight from `references/nimble-playbook.md` — pick CLI or MCP at session start, then run the standard preflight calls (date calc, today, profile, memory index) in parallel.

From the results:
- CLI missing or API key unset → `references/profile-and-onboarding.md`, stop
- Tag all `nimble` CLI calls: `nimble --client-source nimble-agent-skills <subcommand>`. MCP requests are attributed at the transport level — see `references/nimble-playbook.md`.
- Profile exists → read `~/.nimble/memory/competitors/index.md` to identify which
  competitor files exist and their last-updated dates. If the index doesn't exist
  (first run or upgrade), fall back to reading all `~/.nimble/memory/competitors/*.md`
  directly — the index is an optimization, not a gate. Then load the relevant
  competitor files for known signals
  (used for dedup in Steps 3 + 5). Follow cross-references (`[[path/entity]]` links)
  to load related context. Determine mode using smart date windowing
  from `references/nimble-playbook.md`:
  - **Full mode:** first run OR last run > 14 days ago
  - **Quick refresh:** last run < 14 days ago
  - **Same-day repeat:** if `last_runs.competitor-intel` is today, check if a report
    already exists at `~/.nimble/memory/reports/competitor-intel-[today].md`. If so,
    ask: "Already ran today. Run again for fresh data?" Don't silently re-run.
  - Skip to Step 2
- No profile → Step 1

**Note:** Step 2 (WSA Discovery) runs after onboarding but before any research.

### Step 1: First-Run Onboarding (2 prompts max)

**Prompt 1** — ask in plain text (NOT AskUserQuestion with options):

> "What's your company's website domain? (e.g., acme.com)"

Verify — make two Bash calls simultaneously:

- `nimble search --query "[domain]" --include-domain '["[domain]"]' --max-results 3 --search-depth lite`
- `nimble search --query "[domain] company" --max-results 5 --search-depth lite`

**Prompt 2** — confirm company + choose competitor method (use AskUserQuestion):

> I found that **[Company]** ([domain]) is [brief description].
> Is this right? And how should I find your competitors?
> - **Yes — find competitors for me**
> - **Yes — I'll list them myself**
> - **Wrong company — let me clarify**

If "find competitors", make three Bash calls simultaneously:

- `nimble search --query "[Company] competitors" --max-results 10 --search-depth lite`
- `nimble search --query "[Company] vs" --max-results 10 --search-depth lite`
- `nimble search --query "[Company] alternatives" --max-results 5 --search-depth lite`

Propose the list. Once the user confirms, create the profile and start Steps 2+3.
When creating the profile, also ask for or infer each competitor's domain and the
user's industry keywords. See `references/profile-and-onboarding.md` for the full
profile schema (company, competitors with domains/categories, industry_keywords,
integrations, preferences).

### Step 2: WSA Discovery

For each competitor domain and the user's domain, discover available WSAs:

```bash
nimble extract:templates list --limit 100  # then filter items for "{domain}"
```

Run one search per domain simultaneously. From the results, filter for WSAs with
`entity_type` matching SERP or PDP, prefer `managed_by: "nimble"`, and validate
each with `nimble extract:templates get --extract-template-name {name}`. Cache discovered WSA names +
params for the run. Use discovered WSAs alongside `nimble search` in Steps 3-4
for richer data. If no WSAs found, continue with `nimble search` alone.

### Step 3: Research the User's Company

Use `--include-domain` to avoid noise from generic company names. Make two Bash calls:

- `nimble search --query "product updates OR changelog OR releases" --include-domain '["[company-domain]"]' --start-date "[start-date]" --max-results 5 --search-depth lite`
- `nimble search --query "[UserCompany] news" --focus news --start-date "[start-date]" --max-results 5 --search-depth lite`

**Fallback if < 3 results:** `nimble search --query "blog" --include-domain '["[company-domain]"]' --max-results 5 --search-depth lite`

### Step 4: Parallel Research Per Competitor (sub-agents)

Read `references/competitor-agent-prompt.md` for the full agent prompt template.
Follow the sub-agent spawning rules from `references/nimble-playbook.md`
(bypassPermissions, batch max 4, explicit Bash instruction, fallback on failure).

Spawn `nimble-researcher` agents (`agents/nimble-researcher.md`) with
`mode: "bypassPermissions"`. Customize the prompt template with each competitor's
name, domain, start-date, known signals from memory (loaded in Step 0), and any
discovered WSA names from Step 2 so agents can use them for enrichment.

**Call estimation & Scaled Execution:** Before launching agents, estimate total API
calls: ~6 searches per competitor × N competitors + ~2 industry searches + extractions.
For 2+ competitors (12+ calls), tell agents to use `extract-batch` for page extractions
instead of individual calls. See the Scaled Execution pattern in
`references/nimble-playbook.md` for tier selection.

Also run **industry searches** directly (not in sub-agents), using `industry_keywords`
from the business profile:

- `nimble search --query "[industry_keyword] AI agents OR automation" --focus news --start-date "[start-date]" --max-results 5 --search-depth lite`
- `nimble search --query "[industry_keyword] regulation OR compliance OR pricing" --focus news --start-date "[start-date]" --max-results 5 --search-depth lite`

### Step 5: Deep Extraction

Extract signals that need date verification OR richer detail. See
`references/nimble-playbook.md` → "Signal Date Validation" → "Verification Budget"
for the full rules.

**Must extract:**
- All P1 signals (funding, M&A, leadership) — need confirmed details AND date verification
- Any signal with `DATE_CONFIDENCE: LOW` — event date needs verification from page content
- Any signal where `SOURCE_TYPE: DERIVATIVE` — confirm the event date from the actual
  page content

**Extract if useful:**
- P2 signals where the snippet lacks a date or key detail

**Skip:** P3 signals with `DATE_CONFIDENCE: HIGH`.

Make one Bash call per URL, all simultaneously:

`nimble extract --url "https://..." --format markdown`

For extraction failures, follow the fallback in `references/nimble-playbook.md`.

When reading extracted content, determine the **actual event date** from the article body
(not just the page header date). Look for: explicit dates tied to the event, temporal
language ("last September", "in Q3"), and datelines.

### Step 5.5: Signal Validation

Before building the report, validate every signal's freshness. See
`references/nimble-playbook.md` → "Signal Date Validation" for the full pattern.

**For each signal from Step 3, classify it:**

| Check | Result | Action |
|---|---|---|
| EVENT_DATE within freshness window + not in memory | **NEW** | Include |
| EVENT_DATE within window + updates a known signal | **UPDATED** | Include as update |
| EVENT_DATE outside freshness window | **STALE** | Drop — old event, new article |
| DATE_CONFIDENCE: LOW + couldn't verify in Step 4 | **UNCERTAIN** | Drop with note |

**P1 corroboration (mandatory)** — any P1 signal with `NEEDS_CORROBORATION: true` MUST
be corroborated before it can enter the report. This is a hard gate, not a suggestion.

For each flagged P1, run:

`nimble search --query "[Company] [event summary]" --max-results 5 --search-depth lite`

Look for the **primary source** (company blog, press release, official filing). If the
primary source dates the event outside the freshness window, reclassify as STALE.
If no primary source is found, reclassify as UNCERTAIN and drop.

**Drop rules:**
- Event date is outside the freshness window → STALE
- Only sourced from derivative/aggregator sites with no corroborating primary or major
  outlet → UNCERTAIN, drop unless verified via extraction
- Content clearly describes a past event (temporal language like "last year", "back in Q3",
  "months ago") with event date outside the window → STALE

After validation, you should have a clean list of NEW and UPDATED signals only.

### Step 6: Analysis & Output

**Full mode** (first run or > 14 days since last) — structured briefing:

- **TL;DR** — 3-5 P1 signals, most recent first, every one dated with source
- **Per competitor** — "Recent" and "Older Context" subsections, "Where They Win
  vs. Where You Win" table, "What This Means" (1-2 sentences)
- **Industry Trends** — signals from industry searches
- **Your Company Update** — releases/news from Step 2
- **Cross-Competitor Patterns** — converging trends
- **What This Means for [Company]** — strategic implications + suggested actions

**Quick refresh mode** (last run < 14 days) — short format:

- **New Signals** — dated, with competitor name, priority, and clickable source URL
- **Nothing New** — list competitors with no new signals
- **Action Items** — only if something requires attention

**Core rules:**
- Every signal MUST have a verified **event date**. Only events that happened within the
  freshness window qualify as new signals — older events are background context.
- Only include signals classified as NEW or UPDATED in Step 5.5. STALE and UNCERTAIN
  signals have already been dropped.
- Deduplicate against `~/.nimble/memory/competitors/*.md` — only surface NEW findings.
- Say "nothing notable this period" rather than padding with fluff.
- P3 signals: mention briefly or omit if report is long.

### Step 7: Save & Update Memory

**Only persist signals that passed Step 5.5 validation** (classified as NEW or UPDATED).
Do not write STALE or UNCERTAIN signals to competitor memory files.

Make all Write calls simultaneously:

- Report → `~/.nimble/memory/reports/competitor-intel-[date].md` (save the **full
  briefing**, not a summary — this is the local source of truth)
- Per competitor → append validated signals to `~/.nimble/memory/competitors/[name].md`
  (use the format documented in `references/memory-and-distribution.md`). Add
  `[[path/entity]]` cross-references for relationships discovered during research
  (e.g., key people → `[[people/name]]`, related competitors → `[[competitors/name]]`).
- Profile → update `last_runs.competitor-intel` in `~/.nimble/business-profile.json`
- Follow the wiki update pattern from `references/memory-and-distribution.md`: update
  `index.md` rows for all affected entity files, append a `log.md` entry for this run.

### Step 7.5: Synthesis Page Generation

If 3+ competitors were researched in this run, OR the existing
`~/.nimble/memory/synthesis/competitive-landscape.md` has stale source timestamps
(source entity files were updated since generation), generate or refresh the synthesis
page.

Use the `nimble-analyst` agent (`agents/nimble-analyst.md`) with
`mode: "bypassPermissions"` to synthesize patterns across all competitor files. The
agent should read all `~/.nimble/memory/competitors/*.md` files and produce a
`competitive-landscape.md` following the format in
`references/memory-and-distribution.md` — market map, feature comparison, pricing
comparison, key patterns, and strategic implications. Cite source entity files with
`[[competitors/name]]` links.

Also append any unanswered questions to `~/.nimble/memory/backlog.md`
(e.g., competitors where key data like pricing or funding is missing).

After generating, update `index.md` with the synthesis page entry.

### Step 8: Share & Distribute

**Always offer distribution — do not skip this step.** Follow
`references/memory-and-distribution.md` for connector detection, sharing flow, and
source links enforcement.

### Step 9: Follow-ups

- **Go deeper** on a competitor → more focused searches
- **Skip a competitor** → update `preferences.skip_competitors`
- **Add a competitor** → update `competitors`, create memory stub
- **"Looks good"** → done

**Sibling skill suggestions:**

> **Next steps:**
> - Run `competitor-positioning` to analyze how competitors present themselves online
> - Run `company-deep-dive` for a full 360 profile on any competitor from this report
> - Run `meeting-prep` if you're meeting with someone at a competitor

---

## Agent Teams Mode (Dual-Mode)

Check at startup: `echo $CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS`

**Team mode** (flag set): Spawn full **teammates** instead of sub-agents:

- **Lead** (you): Assign competitors, synthesize the final briefing
- **One teammate per competitor**: Uses `references/competitor-agent-prompt.md` with discovered WSAs —
  teammates can message each other when they find overlapping signals
- **Devil's Advocate** (optional): Challenges findings, looks for blind spots
- Lead synthesizes a **cross-validated** briefing with higher confidence

**Solo mode** (flag not set): Standard sub-agent flow from Step 3.

---

## Error Handling

See `references/nimble-playbook.md` for the standard error table (missing API key, 429,
401, empty results, extraction garbage). Skill-specific errors:

- **Search 500:** Retry once without `--focus` flag. If still failing, retry with a
  simplified query (shorter terms, no date filter). Log the failure but don't skip
  the competitor.
- **Search timeout:** Retry once, then skip that call and continue — consistent with
  the playbook's timeout policy.

Referenced files: 4

competitor-positioning16.8 KB

View saved version →

---
name: competitor-positioning
description: |
  Tracks how competitors position themselves online — scrapes homepages,
  features, pricing, and blogs to extract messaging, value props, CTAs, and
  pricing models. Compares against previous snapshots to surface positioning
  shifts with before/after tracking. Produces messaging matrices, content gap
  analysis, white space maps, and battlecard inputs.

  Use when anyone asks about competitor messaging, positioning, website copy,
  content strategy, or how competitors present themselves. Triggers: "competitor
  positioning", "messaging comparison", "content gap", "what changed on their
  site", "competitor homepage", "landing page teardown", "marketing battlecard",
  "how do they describe their product", "share of voice", "counter-messaging".

  Do NOT use for business signals like funding/hiring (use competitor-intel),
  single-company deep dives (use company-deep-dive), or meeting prep (use
  meeting-prep).
allowed-tools:
  - Bash(nimble:*)
  - Bash(date:*)
  - Bash(cat:*)
  - Bash(mkdir:*)
  - Bash(python3:*)
  - Bash(echo:*)
  - Bash(jq:*)
  - Bash(ls:*)
  - Read
  - Write
  - Edit
  - Glob
  - Grep
  - Agent
  - AskUserQuestion
metadata:
  author: Nimbleway
  version: 1.6.1
  category: marketing
---

# Competitor Positioning

Marketing-focused competitive positioning analysis powered by Nimble's web data APIs.
Built for marketing teams who need to understand how competitors present themselves —
messaging, value props, content themes, pricing — and how that evolves over time.

The output is a **marketing briefing**, not a signal feed. Every insight should answer:
"what does this mean for our messaging and positioning?"

User request: $ARGUMENTS

**Argument parsing** — determine what to do before running anything:
- No arguments → run full workflow (scope confirmation in Step 2)
- Competitor names (e.g., "Exa, Tavily") → research only those, skip scope confirmation
- "battlecard [competitor]" → skip to Battlecard Generation (see below) using
  existing snapshots from memory
- "delta" / "what changed" → force delta mode regardless of timing

**Before running any commands**, read `references/nimble-playbook.md` for Claude Code
constraints (no shell state, no `&`/`wait`, sub-agent permissions, communication style).

---

## Instructions

### Step 0: Preflight

Follow the transport selection + standard preflight from `references/nimble-playbook.md` — pick CLI or MCP at session start, then run the standard preflight calls (date calc, today, profile, memory index) in parallel.

From the results:
- CLI missing or API key unset → `references/profile-and-onboarding.md`, stop
- Tag all `nimble` CLI calls: `nimble --client-source nimble-agent-skills <subcommand>`. MCP requests are attributed at the transport level — see `references/nimble-playbook.md`.
- Profile exists → load prior data from two sources:
  - `~/.nimble/memory/positioning/*.md` — prior positioning snapshots (used for
    delta detection in Steps 4 + 5)
  - `~/.nimble/memory/competitors/*.md` — business signals from competitor-intel
    runs (provides context for *why* positioning may have shifted, e.g., a funding
    round or leadership change that preceded a messaging pivot)
  Determine mode:
  - **Full snapshot:** first run OR no prior positioning data OR last run > 14 days ago
  - **Delta mode:** last run < 14 days ago — only surface what changed
  - **Same-day repeat:** if `last_runs.competitor-positioning` is today, check for
    existing report at `~/.nimble/memory/reports/competitor-positioning-[today].md`.
    If found, ask: "Already ran today. Run again for fresh data?" Don't silently re-run.
  - Skip to Step 2
- No profile → Step 1

### Step 1: First-Run Onboarding (2 prompts max)

This skill shares the competitor list from `competitor-intel`. If a profile already
exists with competitors, skip onboarding entirely.

If no profile exists, follow `references/profile-and-onboarding.md` for the full
onboarding flow. The profile and competitor list created here will be shared across
all business skills.

### Step 2: Confirm Scope

If `$ARGUMENTS` already specifies competitors, use those and skip this step.

Otherwise, check how many competitors are in the profile:
- **4 or fewer** → proceed with all, no confirmation needed
- **More than 4** → ask which to focus on (use AskUserQuestion):

  > You have [N] competitors tracked. Which ones should I analyze?
  > - **All [N]** (~[3-5 × N] Nimble API credits, ~[N × 2] min)
  > - **[Category A]**: [names] (grouped by `category` from profile)
  > - **[Category B]**: [names]
  > - **Let me pick** — I'll list them

  Accept natural language: "just the AI search ones" → resolve from profile categories.

This prevents wasted API credits and wall time on competitors the user doesn't care
about right now. Each competitor costs ~3-5 Nimble API credits (1 map + 3-4 extracts
+ 2-3 searches).

### Step 3: WSA Discovery

For each competitor domain and the user's domain, discover available WSAs:

```bash
nimble extract:templates list --limit 100  # then filter items for "{domain}"
```

Run one search per domain simultaneously. Filter for SERP/PDP WSAs, prefer
`managed_by: "nimble"`, validate with `nimble extract:templates get --extract-template-name {name}`.
Cache discovered names + params. Pass them to competitor agents in Step 5 for
richer extraction. If no WSAs found, continue with `nimble search/extract/map`.

### Step 4: Capture the User's Own Positioning (baseline)

Before analyzing competitors, capture the user's own positioning as a baseline for
the messaging matrix.

**First, discover the site structure** to find the right pages to extract:

`nimble --transform "links.#.url" map --url "https://[company-domain]" --sitemap only --limit 200`

From the returned URLs, identify the features/product page and pricing page (look
for paths containing `/features`, `/product`, `/platform`, `/pricing`, `/plans`).

**Then extract the key pages simultaneously** (homepage + whichever pages map found):

- `nimble extract --url "https://[company-domain]" --format markdown`
  → Homepage messaging, tagline, hero copy, CTAs
- `nimble extract --url "[features-page-url]" --format markdown`
  → Features page structure and emphasis (skip if map found no match)
- `nimble extract --url "[pricing-page-url]" --format markdown`
  → Pricing structure and tier naming (skip if map found no match)

If a page extraction returns garbage, note "page not accessible" and continue —
partial baseline is better than none.

### Step 5: Parallel Research Per Competitor (sub-agents)

Read `references/positioning-agent-prompt.md` for the full agent prompt template.
Follow the sub-agent spawning rules from `references/nimble-playbook.md`
(bypassPermissions, batch max 4, explicit Bash instruction, fallback on failure).

**Call estimation & Scaled Execution:** Before launching agents, estimate total API
calls: ~1 map + 3 extractions + 3 searches per competitor = ~7 × N calls (plus baseline
from Step 3). For 2+ competitors (14+ calls), tell agents to use `extract-batch` for
page extractions instead of individual calls. See the Scaled Execution pattern in
`references/nimble-playbook.md` for tier selection.

For each competitor in scope, spawn a **general-purpose** sub-agent with
`mode: "bypassPermissions"` and inline the prompt from
`references/positioning-agent-prompt.md`. Customize the prompt with each competitor's
name, domain, start-date, previous positioning snapshot from memory (loaded in
Step 0), and any discovered WSA names from Step 3 for richer data access.

Do NOT use `agents/nimble-researcher.md` — that agent is scoped for raw data gathering
and explicitly forbids analysis, but this skill requires interpretive work (identifying
audience signals, comparing positioning snapshots, assessing structure implications).

Each agent handles the complete research cycle for one competitor:
1. `nimble map` to discover the site's actual page structure
2. Extract homepage, features, and pricing pages (using discovered URLs)
3. Search for and extract recent blog posts (2-3 deep dives)
4. Analyze social proof (case studies, testimonials)
5. Compare against previous snapshot for changes

The agent returns a structured positioning snapshot — see the prompt template for the
full output format. No separate blog extraction step is needed; agents handle it.

**If an agent's blog extraction was thin** (< 2 posts extracted), optionally extract
additional posts from the main context using URLs from the agent's search results.

**Fallback:** If an agent fails entirely, run extractions directly from the main context
using the same prompt template steps.

### Step 6: Analysis & Output

Frame everything for a marketing team. Use terms they work with: messaging
hierarchy, share of voice, battlecard inputs, content calendar implications.

When analyzing blog content from agent results, look for:
- **Recurring narratives** — what story is this company telling repeatedly?
- **Audience targeting** — are posts aimed at developers, executives, practitioners?
- **Competitive mentions** — do they name competitors or position against categories?
- **SEO patterns** — what keywords do titles and headings target?
- **Content maturity** — original research, thought leadership, or generic how-tos?

**Full snapshot mode** (first run or > 14 days since last):

- **TL;DR for Marketing** — 3-5 key positioning insights the marketing team should
  act on, each with a specific implication (e.g., "Competitor X shifted tagline from
  developer-focused to enterprise — consider whether our messaging still differentiates")

- **Messaging Matrix** — build a comparison table with rows for Tagline, Primary CTA,
  Value Props, Target Audience, and Pricing Model across all competitors including
  your company. Use verbatim quotes for taglines and CTAs.

- **Per Competitor — Positioning Profile**:
  - Site structure signals (what pages exist/don't exist, subdomains)
  - Homepage messaging breakdown (tagline, hero, CTAs, value props)
  - Features page analysis (what they emphasize, differentiation claims)
  - Pricing positioning (model, tier strategy, enterprise signals)
  - Content strategy (blog themes, cadence, audience, content types)
  - Social proof strategy (who they showcase, what outcomes they highlight)

- **Content Gap Analysis** — what competitors are publishing that you're not:
  - Topics they cover that you don't
  - Content formats they use (case studies, benchmarks, ROI calculators)
  - Audience segments they address in content

- **Positioning White Space** — messaging angles no competitor has claimed strongly:
  - Unclaimed value props
  - Underserved audience segments
  - Narrative gaps

- **Recommended Actions** — specific, actionable next steps for the marketing team
  (e.g., "Draft counter-messaging for Competitor X's new enterprise positioning",
  "Prioritize case studies targeting [segment] — 3 competitors already own this space")

**Delta mode** (last run < 14 days) — changes only:

- **What Changed** — per competitor, before/after for each shift:
  - "Tagline: '[old]' → '[new]'"
  - "New feature category added: [name]"
  - "Pricing model shifted from [old] to [new]"
  - "New blog theme emerging: [topic] (3 posts in last 2 weeks)"

- **Nothing Changed** — list competitors with no positioning shifts

- **Marketing Implications** — what the changes mean for your team's priorities

**Core rules:**
- Every claim must link to the source page.
- Deduplicate against `~/.nimble/memory/positioning/*.md` — in delta mode, only
  surface genuinely new changes.
- Say "no positioning changes detected" rather than padding with fluff.
- Use verbatim quotes for taglines, CTAs, and value props — don't paraphrase.

**WSA enrichment:** If WSAs were discovered in Step 3, agents should use them
alongside `nimble map`/`nimble extract` for richer page data.

### Step 7: Save & Update Memory

Make all Write calls simultaneously:

- Report → `~/.nimble/memory/reports/competitor-positioning-[date].md`
  (save the **full briefing**, not a summary — this is the local source of truth)

- Per competitor → save positioning snapshot to
  `~/.nimble/memory/positioning/[name].md` using the format in
  `references/positioning-snapshot-format.md`. Append a dated entry to the History
  section so future runs can detect what changed and when. Add `[[competitors/name]]`
  cross-references to link positioning snapshots to competitor intel files.

- Profile → update `last_runs.competitor-positioning` in
  `~/.nimble/business-profile.json`

- Follow the wiki update pattern from `references/memory-and-distribution.md`: update
  `index.md` rows for all affected entity files, append a `log.md` entry for this run.

### Step 8: Share & Distribute

**Always offer distribution — do not skip this step.** Follow
`references/memory-and-distribution.md` for connector detection, sharing flow, and
source links enforcement. Marketing teams especially benefit from shared Notion pages
they can reference in positioning workshops and content planning sessions.

### Battlecard Generation

Triggered by `"battlecard [competitor]"` argument or as a follow-up request.

**Inputs:** Read the competitor's positioning snapshot from
`~/.nimble/memory/positioning/[name].md` and the user's own baseline from the most
recent report. If no snapshot exists (or it's stale > 14 days), run Steps 4-5 for
that competitor first, then return here.

**Output format:**

```
# Battlecard: [Your Company] vs [Competitor]
## As of [date]

### Competitor Overview
- Tagline: [verbatim]
- Primary CTA: [verbatim]
- Target audience: [who their messaging speaks to]
- Pricing model: [type and entry price if known]

### Their Key Claims
[List each value prop / differentiator they emphasize, verbatim with source URL]

### Our Counter-Positioning
[For each claim above: our response, proof points, and messaging angle]

### Feature Comparison
| Capability | Us | Them | Advantage |
|---|---|---|---|

### Where They Win (acknowledge honestly)
[Areas where their positioning is stronger or they have genuine advantages]

### Where We Win
[Our unique advantages, with evidence]

### Objection Handling
| Prospect Says | Respond With |
|---|---|

### Recommended Talking Points
[3-5 concise talking points for sales/marketing conversations]
```

---

### Step 9: Follow-ups

- **Generate battlecard** for a competitor → runs Battlecard Generation above
- **Draft counter-messaging** for a specific competitor claim → suggest alternative
  angles and proof points
- **Content calendar comparison** → map your publishing against competitors' cadence
- **Go deeper** on a competitor → extract additional pages (about, careers, partners,
  case studies)
- **Track a new competitor** → update `competitors`, create positioning snapshot
- **Skip a competitor** → update `preferences.skip_competitors`
- **"Looks good"** → done

**Sibling skill suggestions:**

> **Next steps:**
> - Run `competitor-intel` for business signals (funding, hiring, product launches)
> - Run `company-deep-dive` for a full 360 profile on any competitor
> - Run `meeting-prep` if you're meeting with someone at a competitor

---

## Agent Teams Mode (Dual-Mode)

Check at startup: `echo $CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS`

**Team mode** (flag set):
- Use the `Agent` tool with `name:` parameter so teammates are addressable
- Teammates can use `SendMessage` to share cross-competitor patterns they discover
  (e.g., "two competitors both shifted to usage-based pricing this month")
- After all competitor teammates return, spawn a **Marketing Analyst** teammate
  with all findings as input, focused solely on content gap analysis and positioning
  white space — this separates data collection from strategic analysis
- Lead synthesizes the final cross-validated marketing briefing

**Solo mode** (flag not set):
- Standard fire-and-forget sub-agents (no `SendMessage`, no `name:`)
- All analysis happens in the main context after agents return

---

## Error Handling

See `references/nimble-playbook.md` for the standard error table (missing API key, 429,
401, empty results, extraction garbage). Skill-specific errors:

- **Search 500:** Retry once without `--focus` flag. If still failing, retry with a
  simplified query (shorter terms, no date filter). Log the failure but don't skip
  the competitor.
- **Search timeout:** Retry once, then skip that call and continue — consistent with
  the playbook's timeout policy.
- **Page extraction fails (404/garbage):** The map step should prevent most 404s by
  discovering actual URLs first. If extraction still fails, note "page not accessible"
  and continue — partial data is better than no data.
- **Map returns empty:** Some sites block sitemap access. Fall back to extracting
  the homepage directly and guessing common paths (/features, /pricing, /blog).
- **Empty blog results:** Some companies don't blog. Note "no active blog detected"
  and focus on page-based positioning instead.

Referenced files: 5

healthcare-providers-enrich15 KB

View saved version →

---
name: healthcare-providers-enrich
description: |
  Fills gaps in existing healthcare practitioner lists — adds missing phone numbers,
  credentials, specialties, contact info, education, reviews, and regulatory data.

  Triggers: "enrich my provider list", "fill in missing data", "add phone numbers
  to these doctors", "complete this practitioner database", "enrich CRM export",
  "fill gaps in my provider data", "supplement this healthcare list".

  Accepts CSV, Google Sheet URL, or pasted data. Searches for each provider's
  practice website, extracts missing fields, and enriches with reviews, clinical
  trials, and accreditation via WSAs.

  Do NOT use for extracting providers from practice URLs — use healthcare-providers-extract instead.
  Do NOT use for validating credentials — use healthcare-providers-verify instead.
  Do NOT use for discovering practices — use market-finder or local-places instead.
  Do NOT use for general extraction — use nimble-web-expert instead.
allowed-tools:
  - Bash(nimble:*)
  - Bash(date:*)
  - Bash(cat:*)
  - Bash(mkdir:*)
  - Bash(python3:*)
  - Bash(echo:*)
  - Bash(jq:*)
  - Bash(ls:*)
  - Bash(wc:*)
  - Read
  - Write
  - Edit
  - Glob
  - Grep
  - Agent
  - AskUserQuestion
metadata:
  author: Nimbleway
  version: 1.6.1
  category: healthcare
---

# Healthcare Providers Enrich

Fill gaps in existing practitioner lists with verified web data, powered by Nimble's
web data APIs.

User request: $ARGUMENTS

**Before running any commands**, read `references/nimble-playbook.md` for Claude Code
constraints (no shell state, no `&`/`wait`, sub-agent permissions, communication style).

---

## Instructions

### Step 0: Preflight + WSA Discovery

**Sibling handoff check:** Before running full preflight, check if
`healthcare-providers-extract` ran earlier in this session by following the Sibling
Handoff pattern from `references/nimble-playbook.md`. If same-day extract output
exists, skip CLI check and profile load, and reuse WSA Layer 1/3 inventory. Only
re-run Layer 2 if the specialty changed.

**Otherwise, run full preflight** from `references/nimble-playbook.md` (5 simultaneous
Bash calls: date calc, today, CLI check, profile load, index.md load).

**Also simultaneously** — run WSA discovery and setup:
- `mkdir -p ~/.nimble/memory/{reports,healthcare-providers-enrich/checkpoints}`
- `ls ~/.nimble/memory/healthcare-providers-enrich/checkpoints/ 2>/dev/null`
- Run Layer 1 (vertical) and Layer 3 (general tools) WSA discovery from
  `references/wsa-reference.md`. Layer 2 (session-specific) runs after Step 1 when
  you know the user's specialty.

Classify discovered agents into phases and validate with `nimble extract:templates get` per
`references/wsa-reference.md`.

From the preflight results:
- CLI missing or API key unset -> `references/profile-and-onboarding.md`, stop
- Tag all `nimble` CLI calls: `nimble --client-source nimble-agent-skills <subcommand>`. MCP requests are attributed at the transport level — see `references/nimble-playbook.md`.
- Profile exists -> note it for context. Determine mode using smart date windowing
  from `references/nimble-playbook.md`:
  - **Full mode:** first run OR last run > 14 days ago
  - **Quick refresh:** last run < 14 days ago (re-enrich only records with gaps)
  - **Same-day repeat:** if `last_runs.healthcare-providers-enrich` is today, check
    for existing report at `~/.nimble/memory/reports/healthcare-providers-enrich-*[today].md`.
    If found, ask: "Already ran today. Run again for fresh data?"
- No profile -> that's fine. This skill doesn't require onboarding. Proceed to Step 1.

### Step 1: Parse Input + Starting Questions

**Chained-from-extract shortcut:** Check for a same-day extract report:
```bash
ls ~/.nimble/memory/reports/healthcare-providers-extract-*$(date +%Y-%m-%d).md 2>/dev/null
```
If a same-day report exists, parse the `{slug}` from the filename and load
`~/.nimble/memory/healthcare-providers-extract/{slug}/providers.json`. The practice
domains and page URL patterns are already known — construct individual bio page URLs
from the site's URL convention and skip Step 3 entirely. This avoids N unnecessary
web searches. If no same-day report exists, do not reuse old `providers.json` files.

Parse `$ARGUMENTS` for input type using the Input Parsing Pattern from
`references/nimble-playbook.md`. Key routing:
- **Extract output detected** (providers.json) -> proceed to Step 2, mark Step 3 skip
- **CSV/Sheet/pasted data detected** -> proceed to Step 2
- **Unclear** -> ask (counts as 1 of max 2 prompts)

**If input is clear**, confirm and ask one shaping question (plain text, not
AskUserQuestion):

> "Found **N providers** in your list. Quick questions:
> 1. Which fields need filling? (contact info, credentials, specialty, reviews, regulatory — or all gaps)
> 2. Healthcare vertical? (ophthalmology, dental, dermatology, general, or other)"

**If input is ambiguous**, use AskUserQuestion (counts as 1 of max 2 prompts):

> **What provider list should I enrich?**
> - Paste provider data directly (name + any known info, one per line)
> - Provide a CSV file path or Google Sheet URL
> - Or describe what you have (e.g., "a list of 50 ophthalmologists with just names and states")

Skip questions the user already answered in their initial message.

### Step 2: Analyze Existing Data

Parse the input into structured records. For each provider, identify:
- **Known fields** — what the user already has (name, state, specialty, etc.)
- **Missing fields** — gaps against the 5 core fields from
  `references/provider-extraction-patterns.md` (name, credentials, specialty,
  contact, education)
- **Enrichment targets** — additional fields the user requested (reviews, regulatory,
  accreditation)

**Early exit — no gaps:** If all providers are already High confidence (5/5 fields),
skip to Step 5 (WSA enrichment) or report: "All providers already have complete
profiles. Want me to add supplementary data (reviews, clinical trials, accreditation)
instead?"

Build a gap analysis summary:

> "Analyzing **N providers**:
> - Names: N/N present
> - Credentials: N/N present (N missing)
> - Specialty: N/N present (N missing)
> - Contact info: N/N present (N missing)
> - Education: N/N present (N missing)
>
> Starting enrichment for **N providers with gaps**..."

Run Layer 2 WSA discovery now that you know the specialty:
```bash
nimble extract:templates list --limit 50  # filter items for "[specialty]"
nimble extract:templates list --limit 50  # filter items for "[directory-user-mentioned]"
```

See `references/wsa-reference.md` for session-specific discovery.

### Step 3: Web Search for Provider Identity

For each provider with gaps, find their practice website and bio page:

```bash
nimble search --query "[provider name] [credentials] [location] [specialty]" --max-results 5 --search-depth lite
```

**Search strategy:**
- Include all known fields in the query to disambiguate common names
- Prioritize results from practice websites over directory listings
- If the provider has a known practice name, add it to the query
- For providers with only name + state, broaden: `"[name] [state] doctor"`

**Result selection:** Pick the most relevant result — practice bio page > healthcare
directory profile > LinkedIn. Save the selected URL for extraction.

For 10+ providers, use sub-agents (see Sub-Agent Strategy below).

**Checkpoint (mandatory):** You MUST write the checkpoint file before proceeding.
Interrupted runs with 20+ providers waste significant API credits without resume.
```bash
echo '{...}' > ~/.nimble/memory/healthcare-providers-enrich/checkpoints/{slug}/search.json
```

### Step 4: Extract Missing Fields

Choose extraction strategy based on provider count. Follow the Scaled Execution
pattern from `references/nimble-playbook.md` — it covers individual calls (1-10),
`extract-batch` (11-100), and the confirmation gate for larger jobs. Use the Page
Extraction with Retry pattern from the same reference for garbage detection and
retry logic.

Parse extracted content for missing fields using the detection patterns from
`references/provider-extraction-patterns.md` (credential regex, specialty keywords,
contact patterns, education mentions).

**Merge rules:**
- Only fill fields that are actually missing — never overwrite existing data
- Track which fields were added and their source URL
- If extracted data conflicts with existing data, keep the existing value and flag
  the conflict for user review

**Checkpoint (mandatory):** You MUST write the checkpoint file before proceeding.
```bash
echo '{...}' > ~/.nimble/memory/healthcare-providers-enrich/checkpoints/{slug}/extraction.json
```

### Step 5: WSA Enrichment (Optional)

If the user requested reviews, regulatory data, or accreditation — or if the gap
analysis shows most core fields are already filled and enrichment adds more value:

**Run enrichment-phase WSAs** discovered in Step 0. See `references/wsa-reference.md`
for the enrichment phase mapping, agent evaluation, and fallback chains.

For each practice or provider, run relevant enrichment agents simultaneously.
Follow the Scaled Execution pattern from `references/nimble-playbook.md` for
batching.

**Merge enrichment data** into provider records:
- Reviews/ratings -> add as supplementary fields (not part of core 5)
- Clinical trial activity -> add as supplementary field
- Accreditation status -> add as supplementary field

### Step 6: Deduplication & Confidence Scoring

Follow the Entity Deduplication and Entity Confidence Scoring patterns from
`references/nimble-playbook.md`. Skill-specific dedup rules and the 5-field
confidence criteria are in `references/provider-extraction-patterns.md`.

**Enrichment-specific confidence:** Score only the **newly added** fields:
- **High** — field found and confirmed by 2+ sources
- **Medium** — field found from 1 source
- **Low** — field inferred or partially matched

### Step 7: Output

Present results as an enrichment diff — showing what was added to each provider.
Group by practice, sort by confidence within each group, and include a "What This
Means" section at the end with actionable next steps.

```markdown
# Provider Enrichment: [N] Providers Updated
*[Date] | [A] fields added across [P] providers | [H] High, [M] Medium, [L] Low confidence*

## TL;DR
Enriched [P] of [T] providers. Added [A] total fields: [breakdown by field type].
[Key finding: e.g., "Found contact info for 18 of 20 providers, 3 have clinical trials"].

## Enrichment Results

| # | Name | Added Fields | Confidence | Source |
|---|------|-------------|------------|--------|
| 1 | Dr. Jane Smith | +credentials (MD, FACS), +contact ((555) 123-4567) | High | [source](url) |
| 2 | Dr. John Doe | +specialty (General Ophthalmology), +education (Wills Eye) | Medium | [source](url) |
| 3 | Dr. Alex Chen | +contact ((555) 987-6543) | Low | [source](url) |

## Detailed Records

### Dr. Jane Smith
**Existing:** Name, State (TX)
**Added:**
- Credentials: MD, FACS — [source](url)
- Contact: (555) 123-4567 — [source](url)
- Education: Fellowship, Bascom Palmer Eye Institute — [source](url)
**Confidence:** High (3 fields added, 2 sources)

[Repeat per provider with additions]

## Providers Not Enriched
[List providers where no additional data was found, with attempted searches]

## Data Quality Summary
- **Fully enriched (5/5 fields):** [N] providers
- **Partially enriched:** [N] providers — common gaps: [list]
- **No new data found:** [N] providers

## Sources
[Clickable URL for every page used, grouped by provider]

## What This Means
[Actionable interpretation: which providers are ready to contact, which need more
data, what the enrichment coverage tells you about this list's quality]
```

**Source links are mandatory.** Every added field must trace back to a source URL.

### Step 8: Save to Memory

Make all Write calls simultaneously:

- Report -> `~/.nimble/memory/reports/healthcare-providers-enrich-{slug}-{date}.md`
- Enriched data -> `~/.nimble/memory/healthcare-providers-enrich/{slug}/enriched.json`
- Profile -> update `last_runs.healthcare-providers-enrich` in
  `~/.nimble/business-profile.json` (only if profile exists)
- Follow the wiki update pattern from `references/memory-and-distribution.md`: update
  `index.md` rows for all affected entity files, append a `log.md` entry for this run.
- Clean up checkpoint (complete run) or keep (partial run)

### Step 9: Share & Distribute

**Always offer distribution — do not skip.** Follow
`references/memory-and-distribution.md` for connector detection and sharing flow.

Notion: full enrichment report as a dated subpage.
Slack: TL;DR with enrichment summary and field counts only.

### Step 10: Follow-ups

- **"Tell me more about Dr. X"** -> show full enriched profile
- **"Export as CSV"** -> generate CSV with original + enriched fields
- **"Enrich more fields"** -> re-run with expanded field targets
- **"Which providers still have gaps?"** -> filter to incomplete records

**Sibling skill suggestions:**

> **Next steps:**
> - Run `healthcare-providers-verify` to validate the enriched credentials and
>   license status
> - Run `healthcare-providers-extract` to discover more providers from practice
>   websites
> - Run `market-finder` to find additional practices in this area

---

## Sub-Agent Strategy

For batch enrichment (10+ providers), use `nimble-researcher` agents
(`agents/nimble-researcher.md`) to parallelize search and extraction.

Follow the sub-agent spawning rules from `references/nimble-playbook.md`
(bypassPermissions, batch max 4, explicit Bash instruction, fallback on failure).

**Spawn pattern:** One agent per batch of 5 providers. Each agent runs Steps 3-4
for its assigned providers and returns enriched records. Tell each agent to use
`nimble extract-batch` for its assigned URLs rather than individual `nimble extract`
calls — one batch call per agent is faster and more reliable than sequential calls.

**Small batch optimization:** If fewer than 10 providers, run directly from the
main context instead of spawning agents.

**Fallback:** If any agent fails, run those enrichments directly from the main
context. Never leave gaps in the output.

---

## Error Handling

See `references/nimble-playbook.md` for the standard error table (missing API key,
429, 401, empty results, extraction garbage). Skill-specific errors:

- **No search results for provider:** "Couldn't find a web presence for [name] in
  [state]. The name may be too common or the provider may not have an online
  presence. Want me to try with additional context (practice name, specialty)?"
- **Ambiguous provider match:** "Found multiple providers named [name] in [state].
  Can you confirm which one? [list top 3 with practice names]"
- **All extractions returned garbage:** "The provider websites appear to be heavily
  JavaScript-rendered. Retrying with browser rendering..." (auto-retry with
  `--render` per the shared pattern)
- **CSV/Sheet parse error:** "Couldn't parse the input file. Expected columns with
  provider names and at least one identifier (state, specialty, or practice).
  Can you paste the data directly instead?"
- **No gaps detected:** Handled in Step 2 (early exit to WSA enrichment or report).

Referenced files: 5

healthcare-providers-extract13.6 KB

View saved version →

---
name: healthcare-providers-extract
description: |
  Extracts structured practitioner data from healthcare practice websites.
  Returns names, credentials, specialties, contact info, and education for
  every provider on a practice's site.

  Use when user asks to extract, pull, or list doctors, providers, or staff
  from practice websites. Triggers: "extract doctors from", "pull providers
  from", "who are the providers at", "build a provider database", "list all
  doctors at", "scrape the team page", "get practitioner data from".

  Accepts practice URLs (pasted, CSV, Google Sheet) or discovers practices
  via Google Maps when given specialty + location. Single sites or 100+ URLs.

  Do NOT use for filling data gaps — use healthcare-providers-enrich instead.
  Do NOT use for credential validation — use healthcare-providers-verify instead.
  Do NOT use for discovering practices — use market-finder or local-places instead.
  Do NOT use for general extraction — use nimble-web-expert instead.
allowed-tools:
  - Bash(nimble:*)
  - Bash(date:*)
  - Bash(cat:*)
  - Bash(mkdir:*)
  - Bash(python3:*)
  - Bash(echo:*)
  - Bash(jq:*)
  - Bash(ls:*)
  - Bash(wc:*)
  - Read
  - Write
  - Edit
  - Glob
  - Grep
  - Agent
  - AskUserQuestion
metadata:
  author: Nimbleway
  version: 1.6.1
  category: healthcare
---

# Healthcare Providers Extract

Structured practitioner extraction from healthcare practice websites, powered by
Nimble's web data APIs.

User request: $ARGUMENTS

**Before running any commands**, read `references/nimble-playbook.md` for Claude Code
constraints (no shell state, no `&`/`wait`, sub-agent permissions, communication style).

---

## Instructions

### Step 0: Preflight + WSA Discovery

Follow the transport selection + standard preflight from `references/nimble-playbook.md` — pick CLI or MCP at session start, then run the standard preflight calls (date calc, today, profile, memory index) in parallel.

**Also simultaneously** — run WSA discovery and setup:
- `mkdir -p ~/.nimble/memory/{reports,healthcare-providers-extract/checkpoints}`
- `ls ~/.nimble/memory/healthcare-providers-extract/checkpoints/ 2>/dev/null`
- Run Layer 1 (vertical) and Layer 3 (general tools) WSA discovery from
  `references/wsa-reference.md`. Layer 2 (session-specific) runs after Step 1 when
  you know the user's specialty.

Classify discovered agents into phases and validate with `nimble extract:templates get` per
`references/wsa-reference.md`.

From the preflight results:
- CLI missing or API key unset -> `references/profile-and-onboarding.md`, stop
- Tag all `nimble` CLI calls: `nimble --client-source nimble-agent-skills <subcommand>`. MCP requests are attributed at the transport level — see `references/nimble-playbook.md`.
- Profile exists -> note it for context. Determine mode using smart date windowing
  from `references/nimble-playbook.md`:
  - **Full mode:** first run OR last run > 14 days ago
  - **Quick refresh:** last run < 14 days ago (re-extract only new/changed pages)
  - **Same-day repeat:** if `last_runs.healthcare-providers-extract` is today, check
    for existing report at `~/.nimble/memory/reports/healthcare-providers-extract-*[today].md`.
    If found, ask: "Already ran today. Run again for fresh data?"
- No profile -> that's fine. This skill doesn't require onboarding. Proceed to Step 1.

### Step 1: Parse Input & Starting Questions

Parse `$ARGUMENTS` for input type using the Input Parsing Pattern from
`references/nimble-playbook.md`. Key routing:
- **URLs detected** -> proceed to Step 3
- **Specialty + location** (no URLs) -> proceed to Step 2 (practice discovery)
- **Unclear** -> ask (counts as 1 of max 2 prompts)

**If input is clear**, confirm and ask one shaping question (plain text, not
AskUserQuestion):

> "Extracting providers from **N practice sites**. Quick questions:
> 1. Healthcare vertical? (ophthalmology, dental, dermatology, general, or other)
> 2. Quick scan (names + credentials only) or full extraction (all 5 fields)?"

**If input is ambiguous**, use AskUserQuestion (counts as 1 of max 2 prompts):

> **What practice sites should I extract providers from?**
> - Paste URLs directly (one per line)
> - Provide a CSV file path or Google Sheet URL with practice URLs
> - Or describe what you're looking for (e.g., "ophthalmologists in Austin, TX")
>   and I'll find practices first

Skip questions the user already answered in their initial message.

### Step 2: Practice Discovery (Optional)

Only if the user provided a specialty + location instead of URLs.

**Two input paths into discovery:**

**Path A — Fresh discovery.** User gave specialty + location. Run Layer 2 WSA
discovery for session-specific agents:

```bash
nimble extract:templates list --limit 50  # filter items for "[specialty]"
nimble extract:templates list --limit 50  # filter items for "[directory-user-mentioned]"
```

See `references/wsa-reference.md` for the full discovery strategy, agent evaluation
criteria, and healthcare discovery prioritization.

Run all discovery-phase agents simultaneously. Validate params with
`nimble extract:templates get` first.

**Path B — Market-finder handoff.** User ran `market-finder` first and wants to
extract providers from those results. Read the market-finder output:

```bash
cat ~/.nimble/memory/market-finder/{slug}/entities.json 2>/dev/null
```

Extract practice records. Note: Google Maps results contain `place_url` (a Maps
link) but not the practice's actual website URL. Proceed to Step 2b to resolve
real website URLs before site mapping.

**After either path:** Deduplicate by domain. Present discovered practices:

> "Found **N practices** for [specialty] in [location] across [M] data sources.
> Proceeding to extract providers from these sites..."

**Fallback** — if no discovery WSAs were found, or results are sparse (< 3):
```bash
nimble search --query "[specialty] in [location]" --max-results 20 --search-depth lite
```

### Step 2b: Resolve Practice Website URLs

Discovery sources (Google Maps, Yelp, BBB) return listing URLs, not practice
website URLs. Before site mapping, resolve the actual website for each practice:

1. **Check structured data first** — Google Maps results often include a `website`
   field in the structured output. Use it if present.
2. **Extract from listing page** — if no `website` field, extract the Maps listing
   to find the practice website link:
   ```bash
   nimble extract --url "[maps-listing-url]" --format markdown
   ```
3. **Search fallback** — if extraction fails:
   ```bash
   nimble search --query "[practice-name] [city] official website" --max-results 3 --search-depth lite
   ```

Skip practices where no website URL can be resolved — note them in the "Data
Quality Summary" output section.

### Step 3: Site Mapping

Follow the Site Mapping Pattern from `references/nimble-playbook.md` for each
practice URL. Skill-specific settings:
- **Keyword weight table:** `references/provider-extraction-patterns.md`
- **Page cap:** 15 per site
- **Fallback query:** `site:[domain] doctors OR providers OR team`

For 6+ practices, use sub-agents (see Sub-Agent Strategy below).

Save checkpoint: `~/.nimble/memory/healthcare-providers-extract/checkpoints/{slug}/mapping.json`

### Step 4: Page Extraction

**WSA shortcuts first:** If WSA discovery found agents that extract provider data
from healthcare directories, use those for matching practices — structured WSA
output is higher quality than parsed markdown.

For all other practices, follow the Page Extraction with Retry pattern from
`references/nimble-playbook.md`. Scale using the Scaled Execution pattern from
the same reference.

Save checkpoint: `~/.nimble/memory/healthcare-providers-extract/checkpoints/{slug}/extraction.json`

### Step 5: Structured Parsing

Parse extracted markdown to identify providers and their fields. Read
`references/provider-extraction-patterns.md` for the 5 core fields, credential
regex patterns, and specialty keywords.

**For each extracted page:**
1. Scan for provider name patterns (Dr. prefix, heading patterns, bold text near
   credential suffixes)
2. Match credentials using the regex patterns from
   `references/provider-extraction-patterns.md`
3. Match specialty using keywords for the detected healthcare vertical
4. Extract contact info (phone regex, appointment URLs, email)
5. Extract education/training mentions

**Build structured records:**
```json
{
  "name": "Dr. Jane Smith",
  "credentials": "MD, FACS",
  "specialty": "Retinal Surgery",
  "contact": {"phone": "(555) 123-4567", "scheduling_url": "..."},
  "education": "Fellowship: Bascom Palmer Eye Institute",
  "source_url": "https://practice.com/our-doctors",
  "practice_name": "Shore Center for Eye Care",
  "practice_url": "https://practice.com",
  "confidence": "High"
}
```

### Step 6: Deduplication & Confidence Scoring

Follow the Entity Deduplication and Entity Confidence Scoring patterns from
`references/nimble-playbook.md`. Skill-specific dedup rules and the 5-field
confidence criteria are in `references/provider-extraction-patterns.md`.

### Step 7: Output

Present results grouped by practice, sorted by confidence within each practice.

```markdown
# Provider Extraction: [N] Providers from [M] Practices
*[Date] | [H] High, [M] Medium, [L] Low confidence*

## TL;DR
Extracted [N] providers from [M] practice websites. [H] with complete profiles,
[L] with partial data. [Key finding: e.g., "12 of 15 providers are board-certified"].

## [Practice Name] ([domain])

| # | Name | Credentials | Specialty | Contact | Education | Confidence |
|---|------|------------|-----------|---------|-----------|------------|
| 1 | Dr. Jane Smith | MD, FACS | Retinal Surgery | (555) 123-4567 | Fellowship: Bascom Palmer | High |
| 2 | Dr. John Doe | OD | General Ophthalmology | [Book](url) | Residency: Wills Eye | Medium |

[Repeat per practice]

## Data Quality Summary
- **Complete profiles (High):** [N] providers
- **Partial profiles (Medium):** [N] providers — missing: [list common gaps]
- **Minimal profiles (Low):** [N] providers — missing: [list common gaps]

## Sources
[Clickable URL for every page extracted, grouped by practice]
```

**Source links are mandatory.** Every provider record must trace back to a source URL.

### Step 8: Save to Memory

Make all Write calls simultaneously:

- Report -> `~/.nimble/memory/reports/healthcare-providers-extract-{slug}-{date}.md`
- Provider data -> `~/.nimble/memory/healthcare-providers-extract/{slug}/providers.json`
- Profile -> update `last_runs.healthcare-providers-extract` in
  `~/.nimble/business-profile.json` (only if profile exists)
- Follow the wiki update pattern from `references/memory-and-distribution.md`: update
  `index.md` rows for all affected entity files, append a `log.md` entry for this run.
- Clean up checkpoint (complete run) or keep (partial run)

### Step 9: Share & Distribute

**Always offer distribution -- do not skip.** Follow
`references/memory-and-distribution.md` for connector detection and sharing flow.

Notion: full provider table as a dated subpage.
Slack: TL;DR with provider count and confidence breakdown only.

### Step 10: Follow-ups

- **"Tell me more about Dr. X"** -> show full extracted profile
- **"Export as CSV"** -> generate CSV from providers.json
- **"Run on more sites"** -> append new practice URLs, extract and merge
- **"What's missing?"** -> detail the data gaps per provider

**Enrichment from discovered WSAs:** If Step 0 found enrichment-phase agents
(reviews, regulatory, practice details), offer them as immediate follow-ups:

> "I also found [N] WSAs that could enrich this data: [brief list]. Want me to
> run reputation checks or regulatory lookups on these providers/practices?"

See `references/wsa-reference.md` for enrichment phase mapping and fallback chains.

**Sibling skill suggestions:**

> **Next steps:**
> - Run `healthcare-providers-enrich` to fill data gaps (NPI lookup, board
>   certification verification, additional contact info)
> - Run `healthcare-providers-verify` to validate credentials and license status
> - Run `market-finder` to discover more practice URLs in this area

---

## Sub-Agent Strategy

For batch extraction (6+ practices), use `nimble-researcher` agents
(`agents/nimble-researcher.md`) to parallelize site mapping and extraction.

Follow the sub-agent spawning rules from `references/nimble-playbook.md`
(bypassPermissions, batch max 4, explicit Bash instruction, fallback on failure).

**Spawn pattern:** One agent per practice (or per batch of 3 practices for large
jobs). Each agent runs Steps 3-5 for its assigned practices and returns structured
provider records.

**Single-practice optimization:** If only 1-2 practices, run directly from the
main context instead of spawning agents.

**Fallback:** If any agent fails, run those extractions directly from the main
context. Never leave gaps in the output.

---

## Error Handling

See `references/nimble-playbook.md` for the standard error table (missing API key,
429, 401, empty results, extraction garbage). Skill-specific errors:

- **No provider pages found:** "Couldn't find provider/team pages on [domain].
  The site may list staff differently. Want me to try extracting from the homepage
  or search for this practice on healthcare directories?"
- **All extractions returned garbage:** "The practice sites appear to be heavily
  JavaScript-rendered. Retrying with browser rendering..." (auto-retry with
  `--render` per the shared pattern)
- **Ambiguous practice name:** If a URL fails and the user provided a name instead,
  search for the practice: `nimble search --query "[practice name] [location] doctors" --max-results 5 --search-depth lite`
- **CSV/Sheet parse error:** "Couldn't parse the input file. Expected a column with
  practice URLs. Can you paste the URLs directly instead?"

Referenced files: 5

healthcare-providers-verify17.6 KB

View saved version →

---
name: healthcare-providers-verify
description: |
  Validates practitioner credentials and license status against the NPI registry.
  Cross-references specialties, credentials, and practice addresses against
  official records. Returns Verified / Partially Verified / Unverified / Flagged
  per practitioner with mismatch details and source URLs.

  Triggers: "verify these doctors", "check provider credentials", "validate
  licenses", "verify NPI numbers", "cross-check credentials against NPI",
  "compliance audit on providers", "are these practitioners still licensed",
  "validate my provider list". Accepts CSV, Google Sheet URL, or pasted data.

  Do NOT use for extracting providers from practice URLs — use healthcare-providers-extract instead.
  Do NOT use for filling data gaps — use healthcare-providers-enrich instead.
  Do NOT use for discovering practices — use market-finder or local-places instead.
  Do NOT use for general extraction — use nimble-web-expert instead.
allowed-tools:
  - Bash(nimble:*)
  - Bash(date:*)
  - Bash(cat:*)
  - Bash(mkdir:*)
  - Bash(python3:*)
  - Bash(echo:*)
  - Bash(jq:*)
  - Bash(ls:*)
  - Bash(wc:*)
  - Read
  - Write
  - Edit
  - Glob
  - Grep
  - Agent
  - AskUserQuestion
metadata:
  author: Nimbleway
  version: 1.6.1
  category: healthcare
---

# Healthcare Providers Verify

Validate practitioner credentials against the NPI registry and authoritative
sources, powered by Nimble's web data APIs.

User request: $ARGUMENTS

**Before running any commands**, read `references/nimble-playbook.md` for Claude Code
constraints (no shell state, no `&`/`wait`, sub-agent permissions, communication style).

---

## Instructions

### Step 0: Preflight + WSA Discovery

**Sibling handoff check:** Before running full preflight, check if
`healthcare-providers-extract` or `healthcare-providers-enrich` ran earlier in this
session by following the Sibling Handoff pattern from `references/nimble-playbook.md`.
If same-day output exists, skip CLI check and profile load, and reuse WSA Layer 1/3
inventory. Only re-run Layer 2 if the verification focus changed.

**Otherwise, run full preflight** from `references/nimble-playbook.md` (5 simultaneous
Bash calls: date calc, today, CLI check, profile load, index.md load).

**Also simultaneously** — run WSA discovery and setup:
- `mkdir -p ~/.nimble/memory/{reports,healthcare-providers-verify/checkpoints}`
- `ls ~/.nimble/memory/healthcare-providers-verify/checkpoints/ 2>/dev/null`
- Run Layer 1 (vertical) and Layer 3 (general tools) WSA discovery from
  `references/wsa-reference.md`. Layer 2 (session-specific) runs after Step 1 when
  you know the user's specialty and verification focus.

Classify discovered agents into verification categories and validate with
`nimble extract:templates get` per `references/wsa-reference.md`.

From the preflight results:
- CLI missing or API key unset -> `references/profile-and-onboarding.md`, stop
- Tag all `nimble` CLI calls: `nimble --client-source nimble-agent-skills <subcommand>`. MCP requests are attributed at the transport level — see `references/nimble-playbook.md`.
- Profile exists -> note it for context. Determine mode using smart date windowing
  from `references/nimble-playbook.md`:
  - **Full mode:** first run OR last run > 14 days ago
  - **Quick refresh:** last run < 14 days ago (re-verify only previously
    Unverified/Flagged practitioners)
  - **Same-day repeat:** if `last_runs.healthcare-providers-verify` is today, check
    for existing report at `~/.nimble/memory/reports/healthcare-providers-verify-*[today].md`.
    If found, ask: "Already ran today. Run again for fresh data?"
- No profile -> that's fine. This skill doesn't require onboarding. Proceed to Step 1.

### Step 1: Parse Input + Starting Questions

**Chained-from-sibling shortcut:** Check for same-day extract or enrich output:
```bash
ls ~/.nimble/memory/reports/healthcare-providers-extract-*$(date +%Y-%m-%d).md 2>/dev/null
ls ~/.nimble/memory/reports/healthcare-providers-enrich-*$(date +%Y-%m-%d).md 2>/dev/null
```
If a same-day report exists, parse the `{slug}` and load the provider data
(`providers.json` or `enriched.json`). This gives you names, credentials, specialties,
and locations — skip parsing and go directly to Step 2.

Parse `$ARGUMENTS` for input type using the Input Parsing Pattern from
`references/nimble-playbook.md`. Key routing:
- **Sibling output detected** (providers.json/enriched.json) -> proceed to Step 2
- **CSV/Sheet/pasted data detected** -> proceed to Step 2
- **Unclear** -> ask (counts as 1 of max 2 prompts)

**If input is clear**, confirm and ask one shaping question (plain text, not
AskUserQuestion):

> "Found **N practitioners** to verify. Quick questions:
> 1. What should I verify? (credentials, specialty, active status, practice address — or all)
> 2. Healthcare vertical? (ophthalmology, dental, dermatology, general, or other)"

**If input is ambiguous**, use AskUserQuestion (counts as 1 of max 2 prompts):

> **What practitioner data should I verify?**
> - Paste provider data directly (name + credentials + location, one per line)
> - Provide a CSV file path or Google Sheet URL
> - Or describe what you have (e.g., "a list of 50 ophthalmologists I need to
>   verify against the NPI registry")

Skip questions the user already answered in their initial message.

### Step 2: Analyze Input Data

Parse the input into structured records. For each practitioner, identify:
- **Claimed fields** — name, credentials, specialty, state/city, practice name
- **Verification targets** — which claims to check based on user's focus

**Minimum required fields:** Name + at least one of (credentials, state, specialty).
If a practitioner has only a name with no other identifiers, flag it:
"Cannot verify [name] — need at least a state, credential, or specialty to search."

Build a verification plan summary:

> "Analyzing **N practitioners** for verification:
> - Names: N/N present
> - Credentials claimed: N/N
> - Specialty claimed: N/N
> - State/location: N/N
>
> Starting NPI verification..."

Run Layer 2 WSA discovery now that you know the specialty:
```bash
nimble extract:templates list --limit 50  # filter items for "[specialty]"
nimble extract:templates list --limit 50  # filter items for "[registry-user-mentioned]"
```

See `references/wsa-reference.md` for session-specific discovery.

### Step 3: NPI Registry Lookup

**Prefer the NPPES API** — it returns structured JSON in one call instead of
search + extract (two calls). Build the query URL from the practitioner's fields:

```bash
nimble extract --url "https://npiregistry.cms.hhs.gov/api/?version=2.1&first_name=[First]&last_name=[Last]&state=[ST]&limit=5" --format markdown
```

Add `&taxonomy_description=[Specialty]` if the specialty is known and specific
enough. The API returns NPI number, status, credentials, taxonomy codes, addresses,
and enumeration dates — everything needed for verification in a single call.

**Fallback to web search** if the NPPES API returns zero results or errors:

```bash
nimble search --query "[Name] [Credential] [State] NPI registry" --max-results 5 --search-depth lite
```

Then extract the top result from an NPI source (see source priority below).

**Source priority (enforce in sub-agent prompts):**
1. NPPES API (`npiregistry.cms.hhs.gov/api/`) — preferred, structured JSON
2. `npidb.org/doctors/` — clean structured data, good fallback
3. `nppes.cms.hhs.gov` (provider-view pages) — official CMS source
4. Skip all others (healthline, hmedata, vitals, etc.) — inconsistent formatting

Tell sub-agents: "Only extract from NPPES API, npidb.org, or nppes.cms.hhs.gov.
Ignore other NPI aggregator sites."

**Search budget per provider:** Max 3 search queries + 1 extraction per
practitioner. If no NPI match after 3 attempts, mark as Unverified and move on.
Tell sub-agents: "Do not run more than 4 nimble commands per provider. Mark as
Unverified if no match by then."

For 10+ practitioners, use sub-agents (see Sub-Agent Strategy below).

**Key fields from NPI records** — see `references/npi-verification-patterns.md`
for the full list: NPI number, status, credentials, taxonomy/specialty,
enumeration date, last updated, practice address.

**Checkpoint enforcement:** After each sub-agent returns its batch results, the
main context MUST write the checkpoint before spawning the next step or presenting
results:
1. Receive sub-agent results
2. Write checkpoint: `echo '[results]' > ~/.nimble/memory/healthcare-providers-verify/checkpoints/{slug}/batch-{n}.json`
3. Continue to next step

Do NOT skip this — if the run fails between steps, the user loses all progress.

### Step 4: Cross-Reference and Verify

For each practitioner, compare claimed data against extracted NPI data. Follow the
verification logic in `references/npi-verification-patterns.md`:

1. **Name matching** — normalize both names and determine match level (Strong,
   Likely, Weak, No Match) per the name matching rules in the reference
2. **Credential matching** — compare claimed credentials against NPI record
3. **Specialty matching** — compare claimed specialty against NPI taxonomy using
   the taxonomy matching strategy in the reference
4. **Address matching** — compare claimed state/city against NPI practice address
5. **Status check** — verify NPI status is Active

**Assign verification status** per practitioner based on the totality of evidence:
- **Verified** — all claims match NPI record
- **Partially Verified** — NPI found, minor discrepancies
- **Unverified** — no NPI match found or unable to disambiguate
- **Flagged** — active mismatches requiring human review

See `references/npi-verification-patterns.md` for the detailed criteria for each
status and the mismatch severity levels (Critical vs Warning).

### Step 5: WSA Supplementary Verification (Optional)

If the user requested regulatory verification beyond NPI lookup, or if Step 5
left practitioners as Unverified that might benefit from additional sources:

Run verification-phase WSAs discovered in Step 0. See `references/wsa-reference.md`
for the verification phase mapping, agent evaluation, and fallback chains.

**Practice confirmation:** For Unverified practitioners, try confirming their
practice exists via practice-level WSAs or web search:
```bash
nimble search --query "[practice-name] [city] [state]" --max-results 5 --search-depth lite
```

**Regulatory verification:** For practitioners the user wants regulatory checks on:
```bash
nimble search --query "[name] [credentials] clinical trials OR FDA OR board certification" --max-results 5 --search-depth lite
```

### Step 6: Deduplication & Confidence Scoring

Follow the Entity Deduplication pattern from `references/nimble-playbook.md`.
Skill-specific dedup rules are in `references/provider-extraction-patterns.md`.

**NPI dedup check:** After all sub-agents return, scan for duplicate NPI numbers
across batches. If two different providers mapped to the same NPI, flag both as
"Flagged — possible NPI collision, requires human review." This catches data entry
errors and name confusion in the source provider list.

**Verification-specific scoring:** The verification status (Verified / Partially
Verified / Unverified / Flagged) replaces confidence scoring for this skill.
Each status includes a confidence qualifier:
- **High confidence** — 2+ NPI sources corroborate, strong name match
- **Medium confidence** — single NPI source, likely name match
- **Low confidence** — weak name match, partial field matches

### Step 7: Output

Present results as a verification report — showing status per practitioner with
specific mismatch details. Group by verification status, include a "What This
Means" section at the end.

```markdown
# Provider Verification: [N] Practitioners Checked
*[Date] | [V] Verified, [PV] Partially Verified, [U] Unverified, [F] Flagged*

## TL;DR
Verified [V] of [T] practitioners against the NPI registry. [F] flagged for
review: [brief description of critical issues]. [U] could not be verified —
[common reason].

## Verification Results

| # | Name | Claimed | NPI Status | Verification | Issues | Source |
|---|------|---------|------------|-------------|--------|--------|
| 1 | Dr. Jane Smith | MD, Retinal Surgery, TX | Active (NPI 1234567890) | Verified | — | [NPI](url) |
| 2 | Dr. John Doe | OD, Ophthalmology, CA | Active (NPI 0987654321) | Partially Verified | Subspecialty differs | [NPI](url) |
| 3 | Dr. Alex Chen | MD, Dentistry, NY | Not Found | Unverified | No NPI match | [NPPES query](api-url) |
| 4 | Dr. Pat Lee | DO, Cardiology, FL | Deactivated | Flagged | NPI deactivated 2024-01 | [NPI](url) |

## Flagged Practitioners (Requires Human Review)

### Dr. Pat Lee
**Claimed:** DO, Cardiology, FL
**NPI Record:** NPI 1122334455 — **Deactivated** (01/15/2024)
**Issues:**
- CRITICAL: NPI status is Deactivated since January 2024
- Credential matches (DO confirmed)
- Specialty matches (Cardiovascular Disease taxonomy)
**Source:** [NPI Record](url)
**Action needed:** Confirm if provider has re-registered or if this is a
different individual.

[Repeat per flagged practitioner]

## Unverified Practitioners

[List practitioners where no NPI match was found, with search queries attempted]

## Verification Summary
- **Verified:** [V] practitioners — all claims confirmed
- **Partially Verified:** [PV] — minor discrepancies noted
- **Unverified:** [U] — no NPI match (common names, missing identifiers)
- **Flagged:** [F] — critical issues requiring review

## Sources
[Clickable URL for every NPI lookup page used, grouped by practitioner]

## What This Means
[Actionable interpretation: which practitioners are safe to include in your
directory, which need follow-up, what the verification rate tells you about
your data quality. Suggest next steps for unverified/flagged records.]
```

**Source links are mandatory.** Every verification finding must trace back to a
source URL.

### Step 8: Save to Memory

Make all Write calls simultaneously:

- Report -> `~/.nimble/memory/reports/healthcare-providers-verify-{slug}-{date}.md`
- Verification data -> `~/.nimble/memory/healthcare-providers-verify/{slug}/verified.json`
- Profile -> update `last_runs.healthcare-providers-verify` in
  `~/.nimble/business-profile.json` (only if profile exists)
- Follow the wiki update pattern from `references/memory-and-distribution.md`: update
  `index.md` rows for all affected entity files, append a `log.md` entry for this run.
- Clean up checkpoint (complete run) or keep (partial run)

**Update sibling artifacts:** If `providers.json` or `enriched.json` exists for this
slug under `~/.nimble/memory/`, merge NPI numbers and verification status into those
files. Generate a verified CSV export at
`~/.nimble/memory/healthcare-providers-verify/{slug}/verified-{date}.csv` with all
verification columns (NPI, NPI Status, NPI Taxonomy, Verification Status). Offer
this export path in Step 9 so the user can copy it where needed.

### Step 9: Share, Distribute & Follow-ups

**Always offer distribution — do not skip.** Follow
`references/memory-and-distribution.md` for connector detection and sharing flow.

Notion: full verification report as a dated subpage.
Slack: TL;DR with verification counts and flagged items only.

**Follow-ups:**

- **"Tell me more about Dr. X"** -> show full verification detail
- **"Export as CSV"** -> generate CSV with verification statuses
- **"Re-verify flagged only"** -> re-run NPI search for Flagged/Unverified only
- **"What should I do about the flagged ones?"** -> actionable next steps per issue

**Sibling skill suggestions:**

> **Next steps:**
> - Run `healthcare-providers-extract` on unverified providers' practice URLs
>   to get fresh data
> - Run `healthcare-providers-enrich` to fill gaps in verified providers' records
> - Run `market-finder` to find additional practices in this area

---

## Sub-Agent Strategy

For batch verification (10+ practitioners), use `nimble-researcher` agents
(`agents/nimble-researcher.md`) to parallelize NPI lookups and extraction.

Follow the sub-agent spawning rules from `references/nimble-playbook.md`
(bypassPermissions, batch max 4, explicit Bash instruction, fallback on failure).

**Spawn pattern:** One agent per batch of 5 practitioners. Each agent runs Steps 3-4
for its assigned practitioners and returns verification records. Tell each agent to
use `nimble extract-batch` for its NPI result URLs where possible — one batch call
per agent is faster than sequential calls.

**Small batch optimization:** If fewer than 10 practitioners, run directly from the
main context instead of spawning agents.

**Fallback:** If any agent fails, run those verifications directly from the main
context. Never leave gaps in the output.

---

## Error Handling

See `references/nimble-playbook.md` for the standard error table (missing API key,
429, 401, empty results, extraction garbage). Skill-specific errors:

- **No NPI results for practitioner:** "Couldn't find an NPI record for [name] in
  [state]. The name may be too common, or the provider may practice under a
  different name. Want me to try with additional context (practice name, NPI number)?"
- **Multiple NPI matches:** "Found multiple NPI records for [name] in [state].
  Can you confirm which one? [list top 3 with NPI numbers and specialties]"
- **NPI page extraction returned garbage:** "The NPI lookup page appears to be
  JavaScript-rendered. Retrying with browser rendering..." (auto-retry with
  `--render` per the shared pattern)
- **CSV/Sheet parse error:** "Couldn't parse the input file. Expected columns with
  practitioner names and at least one identifier (state, specialty, or credentials).
  Can you paste the data directly instead?"
- **Insufficient data for verification:** "Cannot verify [N] practitioners — they
  have only a name with no state, credential, or specialty. Add identifiers or
  remove them from the list."

Referenced files: 6

launch-monitor24.8 KB

View saved version →

---
name: launch-monitor
description: |
  Monitors press, social, developer communities, and competitor channels from any point around
  a product launch — tracking sentiment, flagging mischaracterizations, surfacing competitor
  responses, and recommending actions in real time. Covers press, Reddit, LinkedIn,
  JavaScript-heavy pages, and live community forums, triaging every signal by urgency.
  Delivers a Response War Room dashboard with a live signal feed, mischaracterization tracker,
  competitor response panel, and sentiment velocity chart.

  Use when asked to "monitor this launch", "track the launch", "what's being said about the
  launch", "flag any mischaracterizations", "competitor response to the launch", "post-launch
  coverage", "check press coverage for the launch", or "what's the reaction to the
  announcement".

  Do NOT use for ongoing brand monitoring unrelated to a launch — use brand-mention-monitor
  instead. Do NOT use for ongoing competitor intelligence unrelated to a specific launch
  window — use competitor-intel instead.
allowed-tools:
  - Bash(nimble:*)
  - Bash(claude:*)
  - Bash(grep:*)
  - Bash(date:*)
  - Bash(cat:*)
  - Bash(mkdir:*)
  - Bash(python3:*)
  - Bash(echo:*)
  - Bash(jq:*)
  - Bash(ls:*)
  - Read
  - Write
  - Agent
  - AskUserQuestion
metadata:
  author: Nimbleway
  version: 1.7.0
  category: marketing
---

# Launch Monitor

Monitors press, social, and community forums from launch day — tracking sentiment, flagging mischaracterizations, surfacing competitor responses, and recommending actions so you can respond fast.

---

## Onboarding message

When this skill is triggered for the first time in a session, send this message. Send it exactly once per session, before asking any setup questions. Do not skip it even when the user's opening message already names a product — the onboarding sets expectations for what the skill collects and how it works.

> 👋 **Launch Monitor is ready.**
>
> This skill monitors press, social media, developer communities, and competitor channels around a product launch — tracking sentiment, flagging mischaracterizations, surfacing competitor responses, and telling you exactly what to respond to and how.
>
> To start, just say:
> _"Monitor the launch of [product name]"_
>
> Or try:
> - "Track coverage since we announced [product] yesterday"
> - "What's the reaction to our [product] launch?"
> - "Flag any mischaracterizations in our [product] press coverage"
> - "How are competitors responding to our [product] launch?"
>
> Would you like me to save your preferences so I skip the questions next time?

---

## Preflight

Follow the transport selection and standard preflight from `references/nimble-playbook.md`: pick CLI vs MCP at session start, then run the parallel preflight calls (date, profile, memory index) simultaneously. Tag every Nimble CLI call: `nimble --client-source nimble-agent-skills <subcommand>`.

From the profile (`~/.nimble/business-profile.json`): load product/brand context and `last_runs.launch-monitor` for date windowing. Pre-populate setup questions so the user confirms rather than re-enters. If no profile exists, follow the first-run onboarding flow in `references/profile-and-onboarding.md` and create a stub after the first run.

---

## How to start

**Before asking anything, do two quick research steps:**

**Step A — Resolve alternate names automatically:**
Search for the product the user named to discover all alternate names, codenames, version names, and related search terms. Do not ask the user for this. Use what you find to build a comprehensive search term list for Step 1.
- Search: `"[product name]" official name OR "also known as" OR codename OR version`
- For Apple products, always check for internal codename, model number, and marketing name variants
- For software: check GitHub repo names, API names, and SDK names
- Add all confirmed variants to your search queries silently — the user never needs to see this step

**Step B — Confirm launch date automatically:**
Search for when the product launched before asking the user. Surface what you find and ask the user to confirm or correct it.
- Search: `"[product name]" launch date OR announced OR "available now" OR "shipping today"`
- If you find a clear date, present it to the user for confirmation
- If you find multiple conflicting dates (announcement vs GA), surface both and ask which window to use

**Then ask in a single message:**

> "Before I run — just confirming a couple of things:
> 1. **Launch date:** I found that [product] launched on [DATE YOU FOUND] — is that the right date to monitor from, or a different one?
> 2. **Window:** How far back should I look beyond that? (default: from launch date to now)
> 3. **Depth:** Quick scan (faster) or deep sweep (more thorough, more sources)? (default: deep)
> 4. **Competitors:** I'll monitor competitor responses by default — any specific competitors to prioritize or exclude?"

**Output is always the Response War Room rendered directly in Claude.** Do not ask about format or output options.

**Exceptions — skip asking entirely if:**
- The user provided all the above in their initial message
- The user has run this skill before in the session (use prior config)

**Defaults if user says "just run it":**
- Window: launch date to now
- Depth: deep
- Competitors: on (identify automatically from context profiling)

**Disambiguation:** If the product name is ambiguous after research, confirm before proceeding:
> "Just to confirm — by [product], do you mean [Option A] or [Option B]?"

---

## Step 0 — Context profiling (run before any searches)

Before searching, profile the product to adapt all queries:

1. What category is this? (developer tool, consumer app, enterprise SaaS, hardware, API, etc.)
2. What ecosystem does it live in? (e.g. Claude/Anthropic, AWS, Salesforce, open source)
3. Who is the target audience? (developers, PMs, enterprise buyers, consumers)
4. What are the key claims made at launch? (extract from the user's description or a quick search of the announcement)
5. What would a mischaracterization look like? (wrong category, wrong pricing, wrong capability, wrong comparison)

Use this profile to:
- Target the right communities and press outlets
- Build precise search queries that find real coverage vs noise
- Know what "correct" positioning looks like so mischaracterizations can be flagged accurately
- Understand what competitor responses would look like

---

## Step 1 — Signal sweep

Run all of the following in parallel. Use `--search-depth lite` for the discovery pass. Switch to `--search-depth deep` only when extracting full article or thread content. Tag every call: `nimble --client-source nimble-agent-skills search ...`

### Press & editorial
- `"[product name]" site:techcrunch.com`
- `"[product name]" site:theverge.com`
- `"[product name]" site:wired.com`
- `"[product name]" site:arstechnica.com`
- `"[product name]" site:venturebeat.com`
- `"[product name]" site:siliconangle.com`
- `"[product name]" site:theregister.com`
- `"[product name]" site:zdnet.com`
- `"[product name]" "[company name]" announcement OR launch OR release`
- Category-specific press (e.g. for dev tools: InfoQ, SDTimes; for AI: The Information, Import AI)

### Social & community
Run ALL of the following — social moves faster than press. Use `focus:"social"` on Nimble for broader platform reach. See `references/sources.md` Tier 2b for full query patterns per platform.

**Reddit** (run multiple subreddit-specific queries, not just the broad one):
- `"[product name]" site:reddit.com` — broad
- `"[product name]" site:reddit.com/r/[category]` — category subreddit
- `"[product name]" site:reddit.com/r/[company]` — brand subreddit
- `"[product name]" site:reddit.com/r/technology` and other relevant subs
- Nimble social focus: `focus:"social"` query `"[product name]" reddit`

**X / Twitter:**
- `"[product name]" site:x.com`
- `"[product name]" wrong OR broken OR disappointed site:x.com` — complaint hunt
- `"[product name]" "actually" OR correcting site:x.com` — correction chains
- Check quote-tweets of the official launch tweet

**LinkedIn:**
- `"[product name]" site:linkedin.com`
- `"[product name]" launched OR "my take" site:linkedin.com`

**Instagram:**
- Nimble social focus: `focus:"social"` query `"[product name]" instagram`
- Signal: influencer posts, brand account engagement, consumer reactions

**TikTok:**
- Nimble social focus: `focus:"social"` query `"[product name]" tiktok review OR reaction`
- Signal: viral reaction videos within 48h; comment sentiment

**YouTube:**
- `"[product name]" review OR reaction OR "first impressions" site:youtube.com`
- `"[product name]" problems OR issues site:youtube.com`

**Facebook / Threads:**
- Nimble social focus: `focus:"social"` query `"[product name]" facebook OR threads`

**Hacker News:**
- `"[product name]" site:news.ycombinator.com`
- Also search: `hn.algolia.com/?q=[product+name]&dateRange=last24h`

**Dev communities:**
- `"[product name]" site:dev.to`
- `"[product name]" site:medium.com`

### Developer communities (if applicable)
- `"[product name]" site:stackoverflow.com`
- `"[product name]" site:github.com` — issues, discussions, reactions
- `"[product name]" site:discord.com OR discord community`
- `"[product name]" site:hashnode.com`

### Competitor monitoring (if enabled)
- `[competitor A] "[product name]" OR "[category]"` — how are they reacting?
- `[competitor A] announcement OR response OR "compared to"` — any counter-announcements?
- Check competitor social accounts and blogs for positioning moves
- Search for "[competitor] vs [product name]" content published since launch date

### Mischaracterization hunting
Run targeted queries designed to surface wrong information:
- `"[product name]" "[wrong claim to watch for]"`
- `"[product name]" pricing OR price` — check if pricing is being reported accurately
- `"[product name]" vs "[wrong comparison]"` — is it being compared to the wrong thing?
- `"[product name]" "[capability it doesn't have]"` — check for capability inflation or deflation

---

## Step 2 — Signal triage

For every signal found, assign:

**Urgency level:**
- 🔴 **Act now** — mischaracterization going viral, high-reach negative coverage, competitor counter-announcement, crisis signal
- 🟡 **Monitor** — emerging negative theme, mid-reach inaccurate coverage, competitor positioning content
- 🟢 **Good signal** — accurate positive coverage, organic enthusiasm, developer adoption signals
- ⬜ **Noise** — irrelevant mentions, unrelated products with similar names, spam

**Action badge:**
- `RESPOND` — requires a direct public response (tweet, comment, press outreach)
- `CORRECT` — requires a correction or clarification (DM, comment, press note)
- `AMPLIFY` — worth sharing, retweeting, or building on
- `ESCALATE` — needs to go to comms, legal, or leadership
- `WATCH` — no action yet but track for escalation
- `IGNORE` — filtered noise

**Signal type:**
- Press coverage · Community discussion · Social mention · Competitor move · Mischaracterization · Churn signal · Influencer take · Developer reaction · Analyst comment

**Source URL — required for every signal:**
Every signal must include the **exact URL** returned by Nimble for that article, thread, or post. This URL powers the clickable **↗ source** link on each card. A homepage URL is useless to the user.

**CORRECT — exact article/thread/post URLs:**
- `https://techcrunch.com/2026/06/11/nimble-mcp-connector-launch`
- `https://news.ycombinator.com/item?id=12345678`
- `https://reddit.com/r/MachineLearning/comments/abc123/is_nimble_just_another_scraper`
- `https://x.com/[username]/status/[POST_ID]`

**WRONG — never use these:**
- `https://techcrunch.com`
- `https://news.ycombinator.com`
- `https://reddit.com`
- `https://x.com`

Use the URL exactly as Nimble returns it in the search result. Do not fabricate a URL.

---

## Step 3 — Mischaracterization analysis

For every piece of coverage that gets something wrong, extract:
1. **The claim** — exactly what was said
2. **The source** — outlet, author, reach estimate, date
3. **What's wrong** — specific inaccuracy
4. **The correct version** — what the accurate statement is
5. **Spread risk** — is this being picked up by others? (search for secondary coverage citing the wrong claim)
6. **Suggested response** — one-sentence correction Claude recommends

---

## Step 4 — Sentiment velocity

Track how sentiment is trending over time since launch:

- Break the window into intervals (e.g. hourly for first 24h, daily after that)
- For each interval: count positive, negative, neutral signals
- Identify the inflection point — when did sentiment peak? When did it shift?
- Flag any velocity spike — sudden surge in mentions (positive or negative)

---

## Memory dedup (filter already-known signals)

Before generating output, run the dedup lifecycle from `references/memory-and-distribution.md` against prior launch-monitor reports in `~/.nimble/memory/reports/`.

Skill-specific rules:
- **Fingerprint:** `{url, signal_type, published_date}` — normalize URLs (strip query params, trailing slashes).
- **Returning signal, urgency changed:** keep in feed, mark with `↩ returning · urgency changed` badge.
- **Already known, no change:** suppress from Act Now / Monitor sections; include in appendix only if user requested full history.
- **Summary line:** show at top of war room: `X net-new · Y returning (urgency changed) · Z suppressed`.
- **Persist:** after the run, save signal fingerprints to `~/.nimble/memory/reports/launch-monitor-{YYYY-MM-DD}.md` and append a `log.md` entry per `references/memory-and-distribution.md`.

---

## Output template (REQUIRED)

Claude MUST follow `references/template.html` exactly when generating the HTML output. Load the template, substitute real researched data into placeholders, keep all CSS, JS, and interaction patterns identical.

### Required response structure

Every launch-monitor response must follow this structure, in order:

1. **`## TL;DR`** — first section, always. Two to three sentences: overall sentiment read (positive / mixed / negative / trending), count of act-now items, and the single most urgent signal or mischaracterization. Example: "Sentiment is mixed-to-negative in the first 48 hours, with 5 act-now items. The dominant risk is the 'Gemini reskin' framing spreading across HN and press. Three mischaracterizations are active, two spreading."
2. **The inline Response War Room widget** — rendered immediately after TL;DR per the sequence below.
3. **`## What This Means`** — final section, always. Two to three sentences synthesizing what the signal pattern implies for launch trajectory and the single most important action for the team to take right now.

### Rendering — INLINE FIRST, ALWAYS (read this before producing output)

The Response War Room is an **interactive widget that must be rendered inline in the chat**. A downloadable file is a *secondary* artifact, never the primary deliverable. Follow this sequence exactly, every run:

1. **Render the war room inline FIRST.** Write the fully-populated `launch-monitor-{YYYY-MM-DD}.html` to `~/.nimble/` using the Write tool, then emit the HTML content inline in the conversation so the user sees the interactive widget immediately. This inline output is the main deliverable and must happen before anything else is offered.
2. **Then, and only then, offer downloads.** After the inline widget is on screen, confirm that `~/.nimble/launch-monitor-{YYYY-MM-DD}.html` and `~/.nimble/launch-monitor-{YYYY-MM-DD}.md` have been saved and offer them to the user for download or sharing.

**Hard rules:**
- **Never** respond with only a download link or only a file. If the user sees a file but no inline war room, the run has failed its primary job.
- **Do not ask** the user whether they want it inline or as a file, and do not ask about format — inline is always the default and the file always accompanies it.
- The inline widget and the downloadable HTML are the **same artifact** — render the identical template, do not produce a stripped-down inline version.

**If the inline output genuinely cannot be produced:**
1. Say so explicitly in one short line — e.g. "I couldn't render the war room inline this time, so here's the file instead."
2. Confirm the file has been saved to `~/.nimble/launch-monitor-{YYYY-MM-DD}.html` as the fallback.

Never silently fall back to a download — if inline fails, name the failure so the user knows it was the environment, not the intended behavior.

### Output template spec (`launch-monitor-{YYYY-MM-DD}.html`)

The Response War Room has a distinct visual identity from all other skills:
- **Light background** using CSS variables — inherits host theme, works in both light and dark mode
- **Monospace accents** for signal metadata — feels operational, not reporty
- **Color system:** Red `#A32D2D` / Amber `#854F0B` / Green `#3B6D11` for urgency — matches the CSS variable palette
- **Tight, dense layout** — war room feel, not a dashboard feel
- **Source links:** `<a href="[EXACT_NIMBLE_URL]" class="src-link">↗ source</a>` — color `#185FA5`, no border/background, click listener on `sc-top` (not `sc`) so links pass through

**Required sections in order:**

**0. Launch header bar**
Full-width bar showing: product name · launch date · time since launch · total signals found · last updated timestamp

**1. Sentiment velocity chart**
A small line chart (not bars) showing signal volume over time since launch, split into positive (green line) and negative (red line). X-axis = time intervals. Y-axis = signal count. Hoverable data points showing count + top signal at that moment. Click a point to filter the signal feed to that time window.

**2. Signal feed — the war room**
The main panel. Signal cards displayed in a **responsive grid that packs 2–3 cards across** by available width (it auto-fills columns rather than forcing a fixed count, so the feed stays compact and never collapses to a single long column). Cards are sorted by urgency (🔴 first, then 🟡, then 🟢). Each card shows:
- Top row: urgency dot + action badge + type tag + chevron (all on one line in `.sc-header`)
- Headline (font-size 12px, font-weight 500)
- One-line context (font-size 11px, muted)
- Bottom row: metadata + ↗ source link — pinned to card bottom with `margin-top:auto` and a top border so all cards in a row align visually
- Suggested action shown on expand — click card to expand (click listener on `.sc-top`, not `.sc`, so source link clicks pass through)

**Grid layout CSS:** `.feed { display:grid; grid-template-columns:repeat(auto-fill,minmax(215px,1fr)); gap:8px }` — auto-fill packs as many ~215px columns as fit (2–3 across at typical render widths), keeping the feed short. Do not change this back to a fixed `repeat(2,...)`, which can collapse to one column at narrow widths.
**`.no-sigs` spans both columns:** `grid-column:1/-1`

**Filter bar above the feed:**
- Primary: View tabs — All / 🔴 Act now / 🟡 Monitor / 🟢 Good / Mischar. — synced with clickable stat cards
- Secondary: Collapsed "Refine" button → expands panel with action type + signal type pills
- Active filters shown as dismissable chips with "Clear all"

All filters stack. Signal count updates live. "No signals match" empty state spans full width.

**3. Mischaracterization tracker**
A dedicated panel — only shown if mischaracterizations were found. Two-column layout:

Left column: **The claim** (what was said, source, reach)
Right column: **The correction** (accurate version + suggested response text)

Each row has a `COPIED` button that copies the suggested correction to clipboard. Status badge: `SPREADING` (if being picked up) / `CONTAINED` (single source) / `CORRECTED` (if already addressed).

**4. Competitor response panel**
Only shown if competitor monitoring is enabled. One card per competitor showing:
- Competitor name
- What they did (published content, tweet, counter-announcement, silence)
- Urgency assessment
- Suggested counter-move

**5. Coverage summary**
Compact stats row: Total signals · Press mentions · Community threads · Social mentions · Mischaracterizations · Competitor moves · Avg sentiment score

**Interaction requirements (JS click handlers — never CSS hover):**
- Signal cards: click to expand/collapse suggested action text
- Velocity chart: click data point to filter feed to that time window
- Filter buttons: all three filter rows stack independently
- Mischaracterization copy button: copies correction text to clipboard
- All filters show live count of matching signals

**Styling:**
- Background: `var(--color-background-primary)` — transparent outer, inherits host
- Card background: `var(--color-background-primary)` with `0.5px solid var(--color-border-tertiary)` border
- Section backgrounds: `var(--color-background-secondary)`
- Text: `var(--color-text-primary)` / `var(--color-text-secondary)` — never hardcoded
- Monospace: `'SF Mono', 'Fira Code', monospace` for metadata fields
- Urgency red: `#A32D2D` · amber: `#854F0B` · green: `#3B6D11`
- Action badge colors: RESPOND/ESCALATE use red tint · CORRECT amber tint · AMPLIFY green tint · WATCH gray tint
- Source links: `color: #185FA5` — inline `<a href>` tag, no border or background
- Do NOT use hardcoded dark backgrounds anywhere

---

### Markdown output spec (`launch-monitor-{YYYY-MM-DD}.md`)

```markdown
# Launch Monitor — [Product Name]
**Launch date:** [DATE]
**Monitored window:** [DATE RANGE]
**Generated:** [TIMESTAMP]
**Total signals:** [N]

## TL;DR
[2-3 sentences: overall sentiment read, count of act-now items, single most urgent signal or mischaracterization]

## Summary
- Act now: [N] | Monitor: [N] | Good signals: [N] | Noise: [N]
- Mischaracterizations found: [N]
- Competitor moves: [N]
- Overall sentiment: [Positive / Mixed / Negative / Trending negative]

## Signal Feed

### 🔴 Act Now
- **[RESPOND/CORRECT/ESCALATE]** | [Signal type] | [Headline]
  Context: [One sentence]
  Source: [outlet] · Reach: [estimate] · [Time since launch]
  Action: [Suggested action]

### 🟡 Monitor
...

### 🟢 Good Signals
...

## Mischaracterizations
| Claim | Source | Reach | Correct version | Status |
|---|---|---|---|---|
| [What was said] | [Source] | [Reach] | [Accurate version] | SPREADING/CONTAINED |

## Competitor Moves
- **[Competitor]:** [What they did] — [Suggested counter-move]

## Source Index
- [URL] | [Source] | [Date] | [Urgency] | [Action]

## What This Means
[2-3 sentences: what the signal pattern implies for launch trajectory; the single most important action for the team to take right now]
```

---

## Saved preferences

After onboarding, ask once:
> "Would you like me to save your preferences so I skip the questions next time? You can always say **'change settings'** to update anything."

Store: product name(s), key competitors, launch window preference, depth setting.

---

## Distribution

After output is rendered inline and files are saved, offer sharing following `references/memory-and-distribution.md` (connector detection, `AskUserQuestion` flow, Notion/Slack options).

Skill-specific routing: push `launch-monitor-{YYYY-MM-DD}.md` to Notion (full report); post Act Now signals only to Slack. Use destination from `integrations` in `~/.nimble/business-profile.json` if set; otherwise ask and save for next time.

---

## Re-run behavior

For date window calculation, follow the Smart Date Windowing pattern in `references/nimble-playbook.md` — use `last_runs.launch-monitor` from the profile.

If the user asks to re-run or refresh monitoring:
> "I'll sweep for new signals since [last run timestamp] and update the war room. Anything new to add to the watch list?"

Only surface net-new signals since last run. Carry forward unresolved mischaracterizations and open action items.

---

## Related skills — next steps

After delivering the war room, suggest relevant next steps in the `## What This Means` section:

- **Ongoing brand monitoring:** Once the active launch window closes (typically 2–4 weeks post-launch), suggest handing off to `brand-mention-monitor` for continuous brand health tracking — it is optimized for steady-state monitoring rather than launch spikes.
- **Competitor follow-up:** If competitors made significant moves during the launch window, suggest `competitor-intel` for deeper, ongoing competitive intelligence — it tracks messaging, positioning shifts, and counter-narrative development over time.
- **Consumer sentiment:** If the product is consumer-facing and early usage data is accumulating, suggest `consumer-sentiment-monitor` to track how real users are experiencing the product beyond the launch press cycle.

---

## Materiality threshold

Skip signals that are:
- Clearly about a different product with the same name (filter via context profiling)
- Automated bots or spam accounts
- Duplicate coverage with no new information (syndicated wire copy)
- Single-digit reach with no amplification potential

Flag but don't prioritize:
- Neutral coverage that accurately describes the product
- Positive signals that need no action

Referenced files: 5

local-places16.5 KB

View saved version →

---
name: local-places
description: |
  Discovers, enriches, and scores local businesses in any neighborhood using
  Nimble Web Search Agents (WSAs) and web data. Returns a structured, ranked
  list with confidence scores, reviews, social presence, and an interactive map.

  Use this skill when the user asks about local businesses, places, or
  neighborhood discovery. Common triggers: "find all coffee shops in",
  "map every bar in", "local businesses in", "discover gyms near",
  "what restaurants are in", "neighborhood guide for", "local places in",
  "find places near", "list all [business type] in [area]", "best [type]
  near [location]", "build a neighborhood guide", "local place search".

  Requires the Nimble CLI (nimble extract:templates run, nimble search, nimble extract)
  for live web data via WSAs and fallback search.
  Do NOT use for competitor analysis or monitoring (use competitor-intel),
  company research or deep dives (use company-deep-dive), general web search
  or extraction (use nimble-web-expert).
allowed-tools:
  - Bash(nimble:*)
  - Bash(date:*)
  - Bash(cat:*)
  - Bash(mkdir:*)
  - Bash(python3:*)
  - Bash(echo:*)
  - Bash(jq:*)
  - Bash(ls:*)
  - Bash(open:*)
  - Read
  - Write
  - Edit
  - Glob
  - Grep
  - Agent
  - AskUserQuestion
metadata:
  author: Nimbleway
  version: 1.6.1
  category: productivity
---

# Local Places

Location intelligence powered by Nimble Web Search Agents and web data APIs.

User request: $ARGUMENTS

**Before running any commands**, read `references/nimble-playbook.md` for Claude Code
constraints (no shell state, no `&`/`wait`, sub-agent permissions, communication style).

---

## Instructions

### Step 0: Preflight

Follow the transport selection + standard preflight from `references/nimble-playbook.md` — pick CLI or MCP at session start, then run the standard preflight calls (date calc, today, profile, memory index) in parallel.

Also simultaneously:
- `mkdir -p ~/.nimble/memory/{reports,local-places/checkpoints}`
- Check for existing checkpoints: `ls ~/.nimble/memory/local-places/checkpoints/ 2>/dev/null`

From the results:
- CLI missing or API key unset -> `references/profile-and-onboarding.md`, stop
- Tag all `nimble` CLI calls: `nimble --client-source nimble-agent-skills <subcommand>`. MCP requests are attributed at the transport level — see `references/nimble-playbook.md`.
- Profile exists -> note the user's location preferences if any. Determine mode
  using smart date windowing from `references/nimble-playbook.md`:
  - **Full mode:** first run OR last run > 14 days ago
  - **Quick refresh:** last run < 14 days ago (skip social enrichment, reviews only
    for new places)
  - **Same-day repeat:** if `last_runs.local-places` is today, check if a report
    already exists at `~/.nimble/memory/reports/local-places-*[today].md`. If so,
    ask: "Already ran today for this area. Run again for fresh data?" Don't silently
    re-run.
  - Skip to Step 1
- No profile -> that's fine. Local places doesn't require onboarding. Proceed to Step 1.

### Step 1: Parse Request & Starting Questions

Parse `$ARGUMENTS` for place type and location. Extract:

| Field | Required | Source |
|-------|----------|--------|
| Place type | Yes | User input ("coffee shops", "gyms", "restaurants") |
| Location | Yes | User input ("Williamsburg", "downtown Austin", "Park Slope") |
| Filters | Optional | User input ("with good reviews", "open late", "cheap") |
| Output preference | Optional | User input ("map", "list", "guide") |

**If both place type and location are clear** from `$ARGUMENTS`, confirm briefly and
proceed: "Searching for **coffee shops** in **Williamsburg, Brooklyn**..."

**If partial or ambiguous**, ask one combined question in plain text:

> "What type of places are you looking for, and where? (e.g., 'coffee shops in
> Williamsburg' or 'gyms near downtown Austin')"

**If the user provided both but you want to scope further**, use AskUserQuestion
(counts as 1 of max 2 prompts):

> **How thorough should this search be?**
> - **Quick scan** -- top results from Google Maps + Yelp (~20 places)
> - **Comprehensive** -- full discovery + social enrichment + reviews (~50+ places)
> - **Deep dive with map** -- everything above + interactive neighborhood map

### Step 2: Location Disambiguation

Before any API calls, resolve the location to avoid wasted searches.

**Disambiguation triggers:**
- Location name exists in multiple cities/states (e.g., "Williamsburg" = Brooklyn NY
  vs. Williamsburg VA)
- Location is a broad area (e.g., "downtown Austin" = multiple neighborhoods)
- Location is informal (e.g., "Soho" = NYC vs. London)

**If ambiguous**, ask the user (counts toward 2-prompt max):

> "There are a few places called **Williamsburg**. Which one?"
> - **Williamsburg, Brooklyn, NY**
> - **Williamsburg, VA**
> - **Other -- I'll specify**

**If unambiguous**, infer the full location (city + state/country) and confirm inline:
"Searching **Williamsburg, Brooklyn, NY**..."

After disambiguation, derive the `slug` for checkpointing and file paths:
lowercase, hyphenated, includes city + state/country (e.g., `williamsburg-brooklyn-ny`).

### Step 3: Check for Existing Checkpoint

Follow the Checkpointing & Resume pattern from `references/memory-and-distribution.md`.

Check: `cat ~/.nimble/memory/local-places/checkpoints/{slug}/discovery.json 2>/dev/null`

- **Checkpoint found** -> offer: "Found previous run ({N} places from {date}).
  Resume and fill gaps, or start fresh?"
- **No checkpoint** -> proceed to Step 4

### Step 4: WSA Discovery

Discover available WSAs for all phases before execution. Run these searches
simultaneously:

```bash
nimble extract:templates list --limit 100  # then filter items for "maps"
```

```bash
nimble extract:templates list --limit 100  # then filter items for "reviews"
```

```bash
nimble extract:templates list --limit 100  # then filter items for "social"
```

```bash
nimble extract:templates list --limit 100  # then filter items for "{place-type}"
```

From the combined results:
1. Filter by `entity_type`: SERP for discovery, PDP/Profile for enrichment/detail
2. Prefer `managed_by: "nimble"` over `managed_by: "community"`
3. Classify into phases -- see `references/wsa-pipeline.md` for classification strategy
4. Validate each with `nimble extract:templates get --extract-template-name {name}` to confirm params
5. Cache all discovered WSA names + validated params for the rest of the run

If no WSAs found for a phase, that phase falls back to `nimble search`. Log
which phases had WSA coverage and which are using fallback.

### Step 5: Primary Search (Phase 1)

Read `references/wsa-pipeline.md` for category detection logic.

Run discovered maps/location WSAs simultaneously, using the validated params from
Step 4:

```bash
nimble extract:templates run --template {discovered_maps_wsa} --params '{...validated params...}'
```

```bash
nimble extract:templates run --template {discovered_review_site_wsa} --params '{...validated params...}'
```

**Tertiary (conditional):** Run discovered credibility WSAs only if primary +
secondary return < 10 combined unique results, or if the user asked for
credibility/trust data.

If any WSA fails or returns empty, fall back to:
`nimble search --query "[place-type] in [location]" --max-results 20 --search-depth lite`

**After discovery:**
1. Parse all results into a unified entity list
2. Deduplicate following the Entity Deduplication pattern from
   `references/nimble-playbook.md`: place_id exact match -> domain normalization ->
   fuzzy name + city
3. Save checkpoint:
   `~/.nimble/memory/local-places/checkpoints/{slug}/discovery.json`

### Step 6: Social Enrichment (Phase 2)

For each discovered place that has a Facebook page or Instagram handle, run the
social WSAs discovered in Step 4. Batch max **4 concurrent Bash calls**.

```bash
nimble extract:templates run --template {discovered_social_wsa} --params '{...validated params...}'
```

Run each discovered social WSA for places with matching handles. Skip social
platforms for which no WSA was discovered. If no social WSAs were found in Step 4,
skip this phase entirely.

Save checkpoint: `~/.nimble/memory/local-places/checkpoints/{slug}/social.json`

### Step 7: Reviews (Phase 3)

For the top places (by source count and data completeness), run the review WSAs
discovered in Step 4:

```bash
nimble extract:templates run --template {discovered_reviews_wsa} --params '{...validated params...}'
```

Batch max 4 concurrent calls. Focus on places that have a `place_id` or equivalent
identifier from Phase 1 discovery. If no review WSAs were found in Step 4, fall
back to: `nimble search --query "[place-name] reviews" --max-results 5 --search-depth lite`

Save checkpoint:
`~/.nimble/memory/local-places/checkpoints/{slug}/reviews.json`

### Step 8: Food/Drink Bonus (Phase 4)

**Auto-trigger** when the place type category matches food/drink keywords.
See `references/wsa-pipeline.md` for the category detection logic.

If triggered, run the delivery/food WSAs discovered in Step 4. Discovery first,
then detail:

```bash
nimble extract:templates run --template {discovered_delivery_serp_wsa} --params '{...validated params...}'
```

For places found on delivery platforms, fetch full details using discovered
detail WSAs:

```bash
nimble extract:templates run --template {discovered_delivery_detail_wsa} --params '{...validated params...}'
```

If no delivery WSAs were found in Step 4, fall back to:
`nimble search --query "[place-name] [location] delivery" --max-results 3 --search-depth lite`

Only run for food/drink categories. Skip if category doesn't match.

### Step 9: Deduplication & Confidence Scoring

**Deduplication:** Run a final dedup pass across all phases following the Entity
Deduplication pattern from `references/nimble-playbook.md`. Merge fields from
multiple sources into a single enriched record per place.

**Confidence scoring:** Follow the Entity Confidence Scoring pattern from
`references/nimble-playbook.md`. Skill-specific target fields (N=8):

| Field | Description |
|-------|-------------|
| name | Business name |
| address | Full street address |
| phone | Phone number |
| website | Website URL |
| rating | Average rating |
| review_count | Number of reviews |
| social | At least one social profile |
| hours | Operating hours |

Scoring criteria:
- **High** (8/8 fields + 2+ sources + 10+ reviews) -> display as `*** High`
- **Medium** (5-7/8 fields OR 2+ sources with partial data) -> `** Medium`
- **Low** (<=4/8 fields, single source, few/no reviews) -> `* Low`

### Step 10: Output

Present results as a numbered table sorted by confidence (High first), then by
rating within each tier.

```
# Local Places: [Place Type] in [Location]
*Found [N] places | [Date] | Confidence: [H] High, [M] Medium, [L] Low*

## Results

| # | Name | Rating | Reviews | Confidence | Address | Sources |
|---|------|--------|---------|------------|---------|---------|
| 1 | Place A | 4.8 (312) | *** High | 123 Main St | [Maps][Yelp] |
| 2 | Place B | 4.6 (89)  | ** Medium | 456 Oak Ave | [Maps] |
...

## Top Picks (High Confidence)

### 1. Place A
- **Address:** 123 Main St, Williamsburg, Brooklyn, NY
- **Phone:** (555) 123-4567 | **Website:** [placea.com](https://placea.com)
- **Rating:** 4.8/5 (312 reviews on Google Maps, 289 on Yelp)
- **Social:** Instagram @placea (2.1K followers) | Facebook (1.8K likes)
- **Hours:** Mon-Fri 7am-7pm, Sat-Sun 8am-6pm
- **Delivery:** Available on DoorDash, Uber Eats
- **Why it stands out:** [1-2 sentences from review highlights]
- **Sources:** [Google Maps](link) | [Yelp](link) | [Facebook](link)

[Repeat for each High confidence place]

## Other Results (Medium + Low Confidence)
[Briefer format -- name, rating, address, missing data noted]

## What's Missing
[Note any data gaps: "3 places had no website or social presence",
 "Reviews unavailable for BBB-only listings"]
```

**Source links are mandatory.** Every place must have at least one clickable source
URL (Google Maps link, Yelp listing, website, or social profile). Places without
any source link should be noted in "What's Missing" but still included if they have
sufficient data from WSA results.

**Drill-down:** After presenting, tell the user:
> "Want details on any place? Say 'tell me more about #3' or ask for the
> interactive map."

### Step 11: Interactive Map (on request or "Deep dive" mode)

Generate an HTML file with Leaflet.js + OpenStreetMap tiles. See
`references/wsa-pipeline.md` for the full map generation pattern and color scheme.

Save to: `~/.nimble/memory/local-places/{slug}-map-{date}.html`

Open in browser: `open ~/.nimble/memory/local-places/{slug}-map-{date}.html`

Only generate automatically if the user chose "Deep dive with map" in Step 1.
For map generation details, see `references/wsa-pipeline.md`.
Otherwise, offer it as a follow-up action.

### Step 12: Save to Memory

Make all Write calls simultaneously:

- Report -> `~/.nimble/memory/reports/local-places-{slug}-{date}.md`
- Per-place data -> `~/.nimble/memory/local-places/{slug}/places.json`
  (structured JSON with all enriched records)
- Profile -> update `last_runs.local-places` in `~/.nimble/business-profile.json`
  (only if profile exists)
- Follow the wiki update pattern from `references/memory-and-distribution.md`: update
  `index.md` rows for all affected entity files, append a `log.md` entry for this run.
- Clean up checkpoint (complete run) or keep (partial run)

### Step 13: Share & Distribute

**Always offer distribution -- do not skip this step.** Follow
`references/memory-and-distribution.md` for connector detection, sharing flow, and
source links enforcement.

Notion: full results table as a dated subpage.
Slack: TL;DR with top 5 places only.

### Step 14: Follow-ups

- **"Tell me more about #N"** -> show full detail for that place
- **"Show the map"** -> generate interactive map (Step 11)
- **"Add filters"** -> re-search with additional constraints
- **"Search nearby area"** -> expand to adjacent neighborhoods
- **"Export as CSV"** -> generate CSV from places.json
- **"Looks good"** -> done

**Sibling skill suggestions:**

> **Next steps:**
> - Run `company-deep-dive` for a full 360 profile on any business from this list
> - Run `meeting-prep` if you're meeting with someone at one of these businesses
> - Run `competitor-positioning` to compare businesses in this area

---

## Sub-Agent Strategy

For comprehensive searches (50+ places), use `nimble-researcher` agents
(`agents/nimble-researcher.md`) to parallelize enrichment.

Follow the sub-agent spawning rules from `references/nimble-playbook.md`
(bypassPermissions, batch max 4, explicit Bash instruction, fallback on failure).
For WSA calls at scale (11+ entities), tell agents to use `agent run-batch` instead
of individual calls. See the Scaled Execution pattern in
`references/nimble-playbook.md` for tier selection. Pass the discovered WSA names
from Step 4 to each agent so they use the same cached names.

**Spawn pattern:** One agent per batch of 10 places for social enrichment.
Each agent runs the Phase 2 WSAs for its batch and returns structured results.

**Single-batch optimization:** If <= 10 places, run enrichment directly from the
main context instead of spawning agents -- saves overhead.

**Fallback:** If any agent fails, run those WSA calls directly from the main context.

---

## Agent Teams Mode (Dual-Mode)

Check at startup: `echo $CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS`

**Team mode** (flag set): Spawn **teammates** for parallel phases:

- **Discovery teammate**: Runs all Phase 1 WSAs, deduplicates, returns unified list
- **Enrichment teammate**: Runs Phases 2-4 for each place batch
- **Lead** (you): Coordinates, scores, generates output and map

**Solo mode** (flag not set): Standard sequential flow from Steps 4-7.

---

## Error Handling

See `references/nimble-playbook.md` for the standard error table (missing API key, 429,
401, empty results, extraction garbage). Skill-specific errors:

- **WSA/Search 500:** Retry once with the same params. If still failing, fall back
  to `nimble search` for that place/query. Log the failure but don't skip the place.
- **WSA/Search timeout:** Retry once, then skip that call and continue — consistent
  with the playbook's timeout policy.
- **WSA not found:** If no WSAs are discovered for a phase, skip that phase's WSA
  calls and fall back to `nimble search`. Log which phases had no WSA coverage.
- **Location not found:** "Couldn't find results for [location]. Could you be more specific?
  Try including city and state (e.g., 'Williamsburg, Brooklyn, NY')."
- **No results for place type:** "No [place type] found in [location]. Want to try a
  broader category or nearby area?"
- **Ambiguous place type:** "Did you mean [option A] or [option B]?" (e.g., "bar" could be
  cocktail bar, sports bar, wine bar)

Referenced files: 4

market-finder18.6 KB

View saved version →

---
name: market-finder
description: |
  Discovers all businesses of a given type in any geography using Nimble
  WSAs. Two modes: Discovery finds businesses from scratch; Audit compares
  a user's existing list (Google Sheet, CSV, inline) against fresh
  discovery, categorizing entries as matched, discovered-only, or
  reference-only. Vertical presets (Healthcare, SaaS, Restaurants, Legal,
  Auto/Home) auto-select WSA routing.

  Triggers: "find all X in Y", "build a list of", "market sizing",
  "account universe", "how many X in Y", "TAM for", "discover all",
  "audit my list", "compare against", "what am I missing", "gap analysis",
  "verify my business list", "prospect list".

  Do NOT use for competitor monitoring — use competitor-intel instead.
  Do NOT use for company deep dives — use company-deep-dive instead.
  Do NOT use for neighborhood-level exploration with social enrichment
  — use local-places instead.
allowed-tools:
  - Bash(nimble:*)
  - Bash(date:*)
  - Bash(cat:*)
  - Bash(mkdir:*)
  - Bash(python3:*)
  - Bash(echo:*)
  - Bash(jq:*)
  - Bash(ls:*)
  - Read
  - Write
  - Edit
  - Glob
  - Grep
  - Agent
  - AskUserQuestion
metadata:
  author: Nimbleway
  version: 1.6.1
  category: business-research
---

# Market Finder

Market intelligence powered by Nimble Web Search Agents.

User request: $ARGUMENTS

**Before running any commands**, read `references/nimble-playbook.md` for Claude Code
constraints (no shell state, no `&`/`wait`, sub-agent permissions, communication style).

---

## Instructions

### Step 0: Preflight

Follow the transport selection + standard preflight from `references/nimble-playbook.md` — pick CLI or MCP at session start, then run the standard preflight calls (date calc, today, profile, memory index) in parallel.

Also simultaneously:
- `mkdir -p ~/.nimble/memory/{reports,market-finder/checkpoints}`
- Check for existing checkpoints: `ls ~/.nimble/memory/market-finder/checkpoints/ 2>/dev/null`

From the results:
- CLI missing or API key unset -> `references/profile-and-onboarding.md`, stop
- Tag all `nimble` CLI calls: `nimble --client-source nimble-agent-skills <subcommand>`. MCP requests are attributed at the transport level — see `references/nimble-playbook.md`.
- Profile exists -> note industry keywords if any. Apply smart date windowing from
  `references/nimble-playbook.md`. Market-finder tweak: in quick refresh mode,
  skip enrichment and only discover new metros.
- No profile -> fine. Market-finder doesn't require onboarding. Proceed to Step 1.

### Step 1: Parse Request & Detect Mode

Parse `$ARGUMENTS` for business type, geography, qualifiers, and **mode detection**.

#### Mode detection

Check `$ARGUMENTS` for a reference list. Read `references/audit-mode.md` for the
full detection signals and parsing rules.

| Signal | Mode |
|--------|------|
| Google Sheet URL, CSV path, or inline list of 3+ businesses | **Audit** |
| Explicit audit language ("audit my list", "compare against", "gap analysis") | **Audit** |
| No reference list provided | **Discovery** (default) |

If a reference list is present but intent is ambiguous, ask: "Want me to **audit**
your list against fresh discovery, or use it as a starting point?"

If audit language is detected but no reference list is provided, ask: "You mentioned
auditing — please provide your list (Google Sheet URL, CSV file path, or paste inline)."
Do not proceed with Audit mode until a reference list is received.

#### Extract fields

| Field | Required | Source |
|-------|----------|--------|
| Business type / vertical | Yes | User input ("dentists", "SaaS CRM tools") |
| Geography | Yes (except SaaS) | User input ("Florida", "Austin TX", "nationwide") |
| Reference list | Audit mode only | Google Sheet URL, CSV path, or inline |
| Qualification criteria | Optional | User input ("must have website", "10+ reviews") |
| Output preference | Optional | User input ("quick summary", "full dataset") |

**If both type and geography are clear** from `$ARGUMENTS`, confirm briefly and
proceed: "Finding **dentists** in **Florida**..." (or "Auditing your list against
**dentists** in **Florida**..." in Audit mode)

**If partial or ambiguous**, ask one combined question (counts as 1 of max 2
AskUserQuestion prompts):

Use AskUserQuestion with up to 3 questions:
1. **Vertical** -- "What type of business?" with options: Healthcare, SaaS/Software,
   Restaurants/Food, Legal/Financial, Auto/Home Services, Other
2. **Geography** -- "What geography?" (free text or: City, State, Region, Nationwide)
3. **Depth** -- "Quick scan or comprehensive discovery?"

Skip questions already answered by `$ARGUMENTS`.

**Depth modes** (determines how much work each step does):

| Depth | Discovery | Enrichment | Verification | Distribution |
|-------|-----------|------------|-------------|--------------|
| **Quick scan** | All sources, 1 pass | Skip (or top 5 only) | Top 5 entities | Offer |
| **Comprehensive** | All sources + fallback retries | Full | All entities | Offer |

### Step 2: Vertical Detection & Preset Loading

Read `references/vertical-presets.md` and match the user's business type against
preset trigger keywords.

| Match | Action |
|-------|--------|
| Clear match | Load that preset's WSA routing and query pattern |
| Partial match | Confirm: "This looks like **Healthcare**. Use healthcare presets?" |
| No match | Use Custom preset with user's keywords |
| SaaS match | Switch to non-geographic pipeline (no geo-tiling) |

Note which discovery WSAs and enrichment WSAs the preset specifies.

### Step 3: Geographic Scoping

**Skip this step for SaaS vertical** (no geography needed).

| Geography level | Tiling strategy |
|----------------|-----------------|
| City | Single query, no tiling |
| Metro area | Single query per WSA |
| State | Tile by top 5-10 metros in the state |
| Region | Tile by states, then top metros per state |
| Nationwide | Tile by all states, then top metros per state |

**Estimate API calls:** `metros * discovery_wsas * (1 + enrichment_ratio)` where
`enrichment_ratio` is ~0.3. Follow the Scaled Execution pattern from
`references/nimble-playbook.md` to choose execution tier (individual / batch /
multi-batch / confirmation gate):

```
Estimated API calls: ~1,560 (50 states x 8 metros x 3 WSAs + enrichment)
This is a nationwide search. Proceed? [Y/n]
```

Derive a `slug` for checkpointing: lowercase, hyphenated, includes vertical + geo
(e.g., `dentists-florida`, `saas-crm-tools`, `hvac-nationwide`).

### Step 4: Check for Existing Checkpoint

Follow the Checkpointing & Resume pattern from `references/memory-and-distribution.md`.

Check: `cat ~/.nimble/memory/market-finder/checkpoints/{slug}/discovery.json 2>/dev/null`

- **Checkpoint found** -> offer: "Found previous run ({N} entities from {date}).
  Resume and fill gaps, or start fresh?"
- **No checkpoint** -> proceed to Step 5

### Step 5: WSA Discovery & Execution

#### 5a: Discover available WSAs

For each target domain in the selected vertical preset, discover current WSAs:

```bash
nimble extract:templates list --limit 100  # then filter items for "{domain}"
```

Run these searches simultaneously (one per target domain). From the results:
1. Filter by entity_type (SERP for discovery, PDP/Profile for enrichment)
2. Prefer `managed_by: "nimble"` over `managed_by: "community"`
3. If no WSA found for a domain, mark it for `nimble search` fallback
4. If no WSAs found for ANY domain, fall back entirely to `nimble search` for all metros

Then validate each discovered WSA's input params:
```bash
nimble extract:templates get --extract-template-name {discovered_name}
```

Cache the discovered WSA names + params for the rest of the run.

#### 5b: Geographic discovery (all except SaaS)

For each metro in the tiling plan, run the discovered WSAs simultaneously:

```bash
nimble extract:templates run --template {maps_wsa} --params '{...validated params...}'
```
```bash
nimble extract:templates run --template {yelp_wsa} --params '{...validated params...}'
```

Run tertiary domain WSAs only if the preset includes them AND primary + secondary
return < 10 combined unique results for that metro.

Choose execution tier per the Scaled Execution pattern in
`references/nimble-playbook.md` (based on total estimated calls from Step 3).

#### 5c: SaaS discovery (non-geographic)

SaaS skips WSA discovery. Run the two-pass search queries defined in the SaaS
preset from `references/vertical-presets.md`:
- **Pass 1 -- Product discovery:** G2, Capterra, general, ProductHunt, GitHub
- **Pass 2 -- Financial discovery:** Crunchbase, funding news, market landscape

Both passes run simultaneously. Pass 2 is critical -- without it, funding and
traction data will be missing or wrong.

#### 5d: Fallback

If no WSA was found for a target domain, or if a WSA fails for any metro:
```bash
nimble search --query "[type] in [metro]" --max-results 20 --search-depth lite
```

**After discovery:**
1. Parse all results into a unified entity list
2. Deduplicate following the Entity Deduplication pattern from
   `references/nimble-playbook.md`: place_id -> domain -> fuzzy name + city
3. Track `source_count` per entity (how many WSAs/sources found it)
4. Save checkpoint: `~/.nimble/memory/market-finder/checkpoints/{slug}/discovery.json`

### Step 6: Enrichment

Run enrichment using the WSAs discovered in Step 5a for the preset's enrichment
target domains. Prioritize entities with the highest source count first. Choose
execution tier per Scaled Execution in `references/nimble-playbook.md`.

```bash
nimble extract:templates run --template {enrichment_wsa} --params '{...validated params...}'
```

Only run enrichment WSAs that apply to the current vertical's enrichment targets
(see `references/vertical-presets.md`). Skip entities without the required ID/URL
for the enrichment WSA.

Save checkpoint: `~/.nimble/memory/market-finder/checkpoints/{slug}/enrichment.json`

### Step 6b: Financial Verification (SaaS vertical)

For SaaS entities, verify funding claims before reporting. Never label a company's
funding stage without a source.

For each entity in the top results (top 5 in quick scan, all in comprehensive):
```bash
nimble search --query "{company name} funding raised series" --max-results 5 --search-depth lite
```

- **Source found:** Use the sourced amount and date
- **No source found:** Display "Undisclosed" -- never guess "Early stage" or "Bootstrapped"

This step prevents publishing unverified financial claims. It's fast (one search
per entity, lite depth) and catches recent funding rounds that directory sites miss.

### Step 7: Deduplication & Scoring

**Final deduplication:** Run a final dedup pass across all phases following the
Entity Deduplication pattern from `references/nimble-playbook.md`. Merge fields
from multiple sources into a single record per entity.

**Discovery strength scoring** (skill-specific, varies by vertical):

Geographic verticals (Healthcare, Restaurants, Legal, Auto/Home, Custom):

| Level | Criteria |
|-------|----------|
| **High** | 3+ sources OR 2+ sources with reviews > 50 |
| **Medium** | 2 sources OR 1 source with reviews > 10 |
| **Low** | 1 source only, few/no reviews |

SaaS vertical (funding + directory presence matter more than review count):

| Level | Criteria |
|-------|----------|
| **High** | Verified funding > $10M OR 3+ directory sources OR 1000+ G2 reviews |
| **Medium** | Verified funding < $10M OR 2 sources OR 100+ G2 reviews |
| **Low** | 1 source only, no verified funding, few reviews |

Display as: `*** High`, `** Medium`, `* Low`

### Step 7b: Audit Comparison (Audit mode only)

**Skip this step in Discovery mode.**

Read `references/audit-mode.md` for the full matching algorithm, normalization rules,
and output template.

1. **Parse reference list** — detect format (Google Sheet / CSV / inline), extract
   records, normalize to `{name, domain, city, state, phone}` per the parsing rules
   in `references/audit-mode.md`
2. **Run three matching layers** in order (domain → name+city → phone). Once an entity
   matches at any layer, stop. Track which layer produced the match.
3. **Categorize** every entity:
   - `matched` — in both reference list and discovery results
   - `discovered_only` — found by discovery, not in reference list
   - `reference_only` — in reference list, not found by discovery
4. **Calculate coverage score** — `matched / reference_count × 100`

Proceed to Step 8 with the categorized results.

### Step 8: Output

```
# Market Finder: [Business Type] in [Geography]
*Found [N] businesses | [Date] | Strength: [H] High, [M] Medium, [L] Low*

## Summary
- **Total discovered:** [N] unique businesses across [M] metros
- **Geographic breakdown:** [top 5 metros by count]
- **Source coverage:** [list each source used with entity counts]

## Top Results (High Strength)

| # | Name | Location | Rating | Reviews | Strength | Sources |
|---|------|----------|--------|---------|----------|---------|
| 1 | Acme Dental | Miami, FL | 4.8 | 312 | *** High | Maps, Yelp, BBB |
| 2 | WidgetCo Health | Orlando, FL | 4.6 | 89 | *** High | Maps, Yelp |
...

## All Results by Geography

### Miami, FL ([n] businesses)
[Table of businesses in this metro]

### Orlando, FL ([n] businesses)
[Table of businesses in this metro]
...

## What's Missing
[Data gaps: metros with low coverage, entities without websites, etc.]
```

**SaaS output variant** (when vertical is SaaS, replace "All Results by Geography"
with tier-based grouping):

```
## Players by Tier

### Pure-Play (dedicated to this vertical)
| # | Name | Domain | Funding | Key Metric | Strength | Sources |
...

### Adjacent (feature overlap from larger platforms)
| # | Name | Domain | Funding | Key Metric | Strength | Sources |
...

### Open Source
| # | Name | Repo | Stars | Key Metric | Strength | Sources |
...
```

**Source links are mandatory.** Every entity must have at least one clickable source
URL (Google Maps link, Yelp listing, website, BBB profile, G2 page, or GitHub repo).

**Audit output variant** (when in Audit mode, replace the Discovery output above):
Use the audit output template from `references/audit-mode.md`. Key sections: Summary
with coverage score, Matched table, Discovered Only table (expansion candidates),
Reference Only table (coverage gaps), and "What This Means" interpretation.

### Step 9: Save to Memory

Make all Write calls simultaneously:

**Discovery mode:**
- Report -> `~/.nimble/memory/reports/market-finder-{slug}-{date}.md`
- Entity data -> `~/.nimble/memory/market-finder/{slug}/entities.json`

**Audit mode:**
- Report -> `~/.nimble/memory/reports/market-finder-audit-{slug}-{date}.md`
- Structured data -> `~/.nimble/memory/market-finder/{slug}/audit-{date}.json`
  (all three categories with match metadata)

**Both modes:**
- Profile -> update `last_runs.market-finder` in `~/.nimble/business-profile.json`
  (only if profile exists)
- Follow the wiki update pattern from `references/memory-and-distribution.md`: update
  `index.md` rows for all affected entity files, append a `log.md` entry for this run.
- Clean up checkpoint (complete run) or keep (partial run)

### Step 10: Share & Distribute

**Always offer distribution -- do not skip this step.** Follow
`references/memory-and-distribution.md` for connector detection, sharing flow, and
source links enforcement.

Notion: full results table as a dated subpage.
Slack: TL;DR with total count + top 10 entities only.

### Step 11: Follow-ups

**Discovery mode follow-ups:**
- **"Tell me more about #N"** -> show full detail for that entity
- **"Filter by [criteria]"** -> re-filter existing results
- **"Expand to [new geography]"** -> add metros and re-run discovery
- **"Export as CSV"** -> generate CSV from entities.json
- **"Run enrichment on all"** -> extend enrichment beyond top entities
- **"Audit against my existing list"** -> switch to Audit mode with this run's results
- **"Looks good"** -> done

**Audit mode follow-ups:**
- **"Export discovered-only as CSV for outreach?"** -> CSV of expansion candidates
- **"Investigate reference-only gaps?"** -> targeted searches for reference_only entries
- **"Run company-deep-dive on new discoveries?"** -> deep research on discovered_only
- **"Re-run with a different geography?"** -> audit the same list against a new area
- **"Looks good"** -> done

**Sibling skill suggestions:**

> **Next steps:**
> - Run `company-deep-dive` for a full 360 profile on any business from this list
> - Run `competitor-positioning` to compare top players in this market
> - Run `local-places` for neighborhood-level discovery with social enrichment and maps

---

## Sub-Agent Strategy

For large jobs, `nimble extract:templates batch` handles WSA parallelism server-side (see
Scaled Execution in `references/nimble-playbook.md`). Sub-agents are useful for
**preparing** batch inputs and **processing** results, not for running individual
WSA calls.

Use `nimble-researcher` agents (`agents/nimble-researcher.md`) when:
- Building metro query lists for large geographies (one agent per state)
- Processing and deduplicating batch results in parallel

Follow the sub-agent spawning rules from `references/nimble-playbook.md`
(bypassPermissions, batch max 4, fallback on failure).

---

## Agent Teams Mode (Dual-Mode)

Check at startup: `echo $CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS`

**Team mode** (flag set): Spawn **teammates** for parallel phases:

- **Discovery teammate(s):** Run all discovery WSAs across metro batches
- **Enrichment teammate:** Run enrichment WSAs for top entities
- **Lead** (you): Coordinate, scope, deduplicate, score, generate output

**Solo mode** (flag not set): Standard sequential flow from Steps 5-8.

---

## Error Handling

See `references/nimble-playbook.md` for the standard error table (missing API key,
429, 401, empty results, extraction garbage). Skill-specific errors:

- **WSA not found:** Skip silently and rely on other discovery sources. Log which
  WSAs were unavailable.
- **Search 500/timeout:** Retry once without `--focus` flag. If still failing,
  retry with a simplified query. Log the failure but don't skip the entire search
  category -- partial data is better than none.
- **No results for metro:** "No [type] found in [metro]. Skipping to next metro."
  Don't abort the entire job for one empty metro.
- **Ambiguous business type:** "Did you mean [option A] or [option B]?"
  (e.g., "practice" could be medical, dental, legal)
- **SaaS with geography:** If the selected preset has no geo-tiling but the user
  specified a geography, offer it as a search qualifier instead.
- **Reference list parse failure:** If Google Sheet extraction returns garbage or
  CSV is malformed, ask the user to paste the data inline instead.
- **Empty reference list:** If parsed list has 0 valid records, warn and offer to
  switch to Discovery mode.
- **No matches found:** If all three matching layers produce zero matches, report
  it — a 0% coverage score is valid and informative.

Referenced files: 5

meeting-prep23.8 KB

View saved version →

---
name: meeting-prep
description: |
  Researches meeting attendees and their companies before any meeting using
  real-time web data. Surfaces roles, recent activity, company context, and
  talking points — then maps cross-attendee relationships.

  Use this skill when the user asks to prepare for a meeting, research someone
  they're meeting, or wants context on attendees. Common triggers: "prepare me
  for my meeting", "who am I meeting with", "research this person", "meeting
  prep", "brief me on [person]", "I have a meeting with [person/company]",
  "get me ready for my call", "what should I know about [person]",
  "background on [person] before our meeting", "attendee research".

  Requires the Nimble CLI (nimble search, nimble extract) for live web data.
  Do NOT use for multi-company competitor monitoring (use competitor-intel)
  or single-company deep dives without attendees (use company-deep-dive).
allowed-tools:
  - Bash(nimble:*)
  - Bash(date:*)
  - Bash(cat:*)
  - Bash(mkdir:*)
  - Bash(python3:*)
  - Bash(echo:*)
  - Bash(jq:*)
  - Bash(ls:*)
  - Read
  - Write
  - Edit
  - Glob
  - Grep
  - Agent
  - AskUserQuestion
metadata:
  author: Nimbleway
  version: 1.6.1
  category: productivity
---

# Meeting Prep

Research-powered meeting preparation with attendee intelligence and company context.

User request: $ARGUMENTS

**Before running any commands**, read `references/nimble-playbook.md` for Claude Code
constraints (no shell state, no `&`/`wait`, sub-agent permissions, communication style).

---

## Instructions

### Step 0: Preflight

Follow the transport selection + standard preflight from `references/nimble-playbook.md` — pick CLI or MCP at session start, then run the standard preflight calls (date calc, today, profile, memory index) in parallel.

From the results:
- CLI missing or API key unset → `references/profile-and-onboarding.md`, stop
- Tag all `nimble` CLI calls: `nimble --client-source nimble-agent-skills <subcommand>`. MCP requests are attributed at the transport level — see `references/nimble-playbook.md`.
- Profile exists → read `~/.nimble/memory/people/index.md` to identify existing
  person profiles. Load relevant `~/.nimble/memory/people/` files for attendees
  before — skip redundant searches, surface prior meeting notes. Follow
  `[[path/entity]]` cross-references in person files: if an attendee's file links
  to `[[competitors/widgetco]]`, load that competitor file for richer context (e.g.,
  recent intel from competitor-intel runs). Also check `~/.nimble/memory/companies/`
  for cached company research.
  **No same-day report check** — meeting-prep is per-meeting, not per-day. Users
  may prep for multiple meetings in one day. Instead, check entity freshness:
  if a person/company profile was updated within the last 24 hours, offer to reuse
  it: "I have a recent profile for **[Name]** from earlier today. Use it, or refresh?"
- No profile → that's fine. Meeting prep doesn't require onboarding. Proceed to Step 1.

### Step 1: Gather Meeting Context

Parse the meeting details from `$ARGUMENTS` or ask the user.

**Calendar shortcut:** If the user didn't specify attendees and a calendar connector
is available — either a calendar MCP tool (look for `list_events` in the tool list)
or the `gws` CLI (`gws calendar +agenda --today`) — offer to pull today's meetings
so they can pick one. If neither is available, skip this silently.

**If clear** (e.g., "prep me for my meeting with Alex Kim at WidgetCo tomorrow"):
- Extract: attendee name(s), company, meeting date/time (if given)
- Confirm briefly: "Preparing briefing for your meeting with **Alex Kim** at **WidgetCo**..."

**If partial** (e.g., "prep me for my meeting tomorrow"):
- Ask one clarifying question in plain text:
  > "Who are you meeting with? (names, titles, and company if you have them)"

**If just a person** (e.g., "research John Smith"):
- Proceed with the person. Try to infer their company from search results.

**Extract these fields:**

| Field | Required | Source |
|-------|----------|--------|
| Attendee name(s) | Yes | User input or calendar event |
| Company | Preferred | User input or inferred from search |
| Attendee title(s) | Optional | User input or discovered in Step 2 |
| Meeting type | Required | User input, inferred, or asked (discovery, demo, check-in, interview, partnership, internal) |
| Meeting date/time | Optional | User input |
| Additional context | Optional | User notes ("they're evaluating our product", "board member intro") |

**Meeting type detection** — if the user doesn't specify, infer from context clues:

| Signal | Inferred type |
|--------|---------------|
| "prospect", "demo", "sales call" | Sales / discovery |
| "interview", "candidate" | Interview |
| "board", "investor" | Board / investor |
| "partner", "integration" | Partnership |
| "check-in", "sync", "1:1" with colleague | Internal |
| No signal | Ask (see below) |

**If no signal** — don't guess "general external." The meeting type gates whether the
Value Positioning section is generated, so it's worth one question. Use AskUserQuestion:

> **What's the goal of this meeting?**
> - **Sales / discovery** — pitching, demo, exploring fit
> - **Partnership** — integration, co-selling, joint venture
> - **Board / investor** — board meeting, investor update, fundraising
> - **Interview** — evaluating a candidate
> - **General / other** — networking, catch-up, or not sure

Map the answer to the meeting type. If the user picks "General / other", treat as
general external (no value positioning section).

The meeting type shapes the briefing focus — specifically, it determines whether the
Value Positioning section (Step 4.5 + Step 6) is generated. Value positioning activates
for: **sales/discovery, partnership, board/investor**. It is skipped for: **interview,
internal, general external**.

### Step 2: WSA Discovery

Discover available WSAs for each attendee's company domain:

```bash
nimble extract:templates list --limit 100  # then filter items for "{company-domain}"
```

Run one search per unique company simultaneously. Filter for SERP/PDP WSAs,
prefer `managed_by: "nimble"`, validate with `nimble extract:templates get --extract-template-name {name}`.
Cache discovered names + params. Pass them to attendee agents in Step 3 for richer
data. If no WSAs found, continue with `nimble search` alone.

### Step 3: Per-Attendee Research (sub-agents)

Read `references/attendee-agent-prompt.md` for the full agent prompt template.
Follow the sub-agent spawning rules from `references/nimble-playbook.md`
(bypassPermissions, batch max 4, explicit Bash instruction, fallback on failure).

**Check memory first.** For each attendee, check `~/.nimble/memory/people/[name-slug].md`.
If a profile exists and is < 30 days old, load it as known context and pass it to the
agent so it focuses on what's new. If > 30 days old, run a full refresh.

Spawn `nimble-researcher` agents (`agents/nimble-researcher.md`) with
`mode: "bypassPermissions"`. One agent per attendee. Pass discovered WSA names
from Step 2 to each agent for enrichment.

**Important:** The Nimble API has a 10 req/sec rate limit per API key. With each agent
running 4-5 searches, limit concurrent agents to 2 per batch. For 3+ attendees, batch
in groups of 2.

**Call estimation & Scaled Execution:** Before launching agents, estimate total API
calls: ~5 searches per attendee + ~4 company searches + 3-5 extractions = ~(5 × N) + 9
calls. For 3+ attendees (15+ calls), tell agents to use `extract-batch` for page
extractions instead of individual calls. See the Scaled Execution pattern in
`references/nimble-playbook.md` for tier selection.

**Batch 1** (2 agents simultaneously):
- Attendee 1 research
- Attendee 2 research

**Batch 2** (if needed):
- Attendee 3 research
- Attendee 4 research

**Single attendee optimization:** If only one person, run the searches directly from
the main context instead of spawning an agent — saves overhead.

**Fallback:** If any agent fails or returns empty, run those searches directly from
the main context. Don't leave gaps in the briefing.

### Step 3.5: Gap Check

Before proceeding, verify every attendee has at least a title and company confirmed.

**For any attendee with < 3 meaningful results or "Role Unknown":**
1. Run a `--focus social` fallback search directly (this searches social platform
   people indices and is the most reliable way to find someone):
   `nimble search --query "[Name] [Company]" --focus social --max-results 5 --search-depth lite`
2. If `--focus social` is unavailable, fall back to:
   `nimble search --query "[Name]" --include-domain '["linkedin.com"]' --max-results 5 --search-depth lite`
3. Try name variations: "[First] [Last]", "[Full Name] [Company] [Title if known]"

Do NOT present a briefing with "Role Unknown" — exhaust social search first. If still
nothing after fallbacks, note it honestly: "Limited public presence — could not confirm
role. Consider asking for their LinkedIn URL."

Also collect **LinkedIn profile URLs** for each attendee during this step if not already
found. These are high-value for the final briefing output and Notion distribution.

### Step 4: Company Research

Research the attendees' company for meeting-relevant context. This is a lighter version
of company-deep-dive — focused on what's useful for the conversation, not a full 360°.

**Company name quoting:** If the company name contains common words that cause noisy
results (e.g., "Acme Supply", "Nova Dynamics", "Global Industries"), wrap it in escaped
quotes: `"\"Acme Supply\" news"`. Use `--include-domain '["[domain]"]'` as an alternative anchor.

Make these Bash calls simultaneously:

- `nimble search --query "\"[Company]\" news" --focus news --start-date "[14-days-ago]" --max-results 8 --search-depth lite`
- `nimble search --query "\"[Company]\" product launch OR announcement" --focus news --start-date "[14-days-ago]" --max-results 5 --search-depth lite`
- `nimble search --query "about" --include-domain '["[domain]"]' --max-results 3 --search-depth lite`
- `nimble search --query "\"[Company]\" funding OR raised OR investors" --max-results 5 --search-depth lite`

If your user's company profile exists, also run:
- `nimble search --query "[Company] [UserCompany] OR [user-domain]" --max-results 5 --search-depth lite`

This catches any existing relationship between the two companies — prior partnerships,
mentions, shared investors, or competitive overlap.

**If < 3 results** from the news searches, retry without `--start-date`.

**Date validation:** When including company news in the briefing, verify that the
**event date** (when something actually happened) is recent, not just the article date.
See `references/nimble-playbook.md` → "Signal Date Validation" for details. If a snippet
uses past-tense language like "last year" or "back in Q3", treat it as background context
rather than recent news.

**If the company was already researched** (exists in `~/.nimble/memory/companies/`),
load the existing profile and only run the news search for fresh updates.

### Step 4.5: Value Positioning Research

**Skip this step** if the meeting type is interview, internal, or general external.

This step cross-references what you learned about the attendee's company (Step 4) with
the user's own business profile to find concrete positioning angles. It works best when
`business-profile.json` exists with at least `company.name` and `company.domain`.

**If no profile exists**, skip searches that reference the user's company or competitors
(searches 2, 4, 5) and rely on generic research (searches 1, 3) for positioning insights.
Use any WSAs discovered in Step 2 for richer attendee company data.
The Value Positioning section will be thinner but still useful — pain-to-solution mapping
and tech stack discovery work without a profile.

**Load the user's sales context** from `~/.nimble/business-profile.json`:
- `sales_context.key_differentiators` — what makes the user's product unique
- `sales_context.integration_partners` — tools the user's product connects with
- `sales_context.case_studies` — similar customers and outcomes
- `sales_context.common_objections` — pre-built objection responses
- `competitors` — tracked competitors (check if the attendee's company uses any)

If `sales_context` doesn't exist in the profile, the skill still works — the value
positioning section will rely on web research alone rather than profile-enriched data.
Mention at the end: "Tip: Add sales context to your profile for richer positioning
next time."

**Make these Bash calls simultaneously** (3-5 searches depending on available data):

1. `nimble search --query "\"[AttendeeCompany]\" tech stack OR tools OR platform OR uses" --max-results 5 --search-depth lite`
   → Discover what tools/platforms they use — match against `integration_partners`

2. `nimble search --query "\"[AttendeeCompany]\" [UserCompany] OR [user-domain]" --max-results 5 --search-depth lite`
   → Any existing relationship, mentions, or competitive overlap (skip if already run in Step 3)

3. `nimble search --query "\"[AttendeeCompany]\" challenges OR pain points OR struggling OR migrating" --max-results 5 --search-depth lite`
   → Pain signals to map against user's value props

4. (If `competitors` list exists) `nimble search --query "\"[AttendeeCompany]\" [CompetitorName1] OR [CompetitorName2]" --max-results 5 --search-depth lite`
   → Check if they use a competitor — critical for displacement positioning

5. (If `case_studies` exist with matching industry) `nimble search --query "[UserCompany] [attendee-industry] case study OR customer story" --max-results 5 --search-depth lite`
   → Find published case studies in the attendee's industry to reference

**From the results, extract:**
- Tools/platforms they use (for integration hooks)
- Pain signals or challenges (for value mapping)
- Competitor usage (for displacement angles)
- Industry match to existing case studies (for social proof)

This data feeds directly into the Value Positioning section in Step 5.

### Step 5: Deep Extraction

From Steps 3-4.5, identify the **top 3-5 most informative URLs** across all results.
Prioritize:
- Attendee's own LinkedIn posts, articles, or talks
- Recent company announcements directly relevant to the meeting
- Interviews or profiles of the attendee
- The company's about/team page (if attendee title wasn't found)
- (If value positioning active) Pages revealing their tech stack or tool usage
- (If value positioning active) Articles about their challenges or migration plans

Make one Bash call per URL, all simultaneously:

`nimble extract --url "https://..." --format markdown`

For extraction failures, follow the fallback in `references/nimble-playbook.md`.

**Single attendee + known company:** Skip company extraction, focus on person URLs.
**Multiple attendees:** Prioritize person-specific URLs over company-level ones.

### Step 6: Synthesize Briefing

Structure the output as a meeting prep briefing. Adapt focus based on meeting type.

```
# Meeting Prep: [Company Name]
*[Meeting date/time if known] | Prepared [today's date]*

## Quick Take
[2-3 sentences: who you're meeting, why it matters, and the one thing to know
going in. This is the "read nothing else" paragraph.]

## Attendees

### [Name] — [Title]
**Background:** [Current role, time in position, career trajectory highlights]
**Recent Activity:** [What they've been posting, speaking about, or working on.
  Direct quotes from posts/talks when available.]
**Conversation hooks:** [2-3 specific things to reference — shared connections,
  their recent project, a post they wrote, a talk they gave]
**Notes from prior meetings:** [If exists in memory — what was discussed, their
  preferences, open items. "No prior meetings on file" if none.]

[Repeat for each attendee]

## Relationship Map
[Cross-attendee connections — shared employers, mutual connections, overlapping
  interests, organizational dynamics between attendees. Skip if single attendee.]

## Company Context
- **What they do:** [One line]
- **Size / Stage:** [Employees, funding stage, HQ]
- **Recent news:** [Top 2-3 items, dated with source]
- **Relevant to your meeting:** [How their company context connects to your
  discussion — e.g., recent product launch you might discuss, funding that
  signals growth, leadership change affecting priorities]

## Value Positioning
*[Only for sales/discovery, partnership, and board/investor meetings. Omit entirely
  for interview, internal, and general external meetings.]*

### Value Mapping
[Match their specific needs/pain points to your capabilities. Every mapping must
  be grounded in research from Step 4.5, not generic claims.
  Format: "They [specific finding with source] → Your product [specific capability]"]

### Integration Hooks
[Tools/platforms they use that your product integrates with. Only include
  integrations confirmed from research (their tech stack) AND your profile
  (integration_partners). If no overlap found, say so honestly.]

### Recommended Positioning
[2-3 sentences on how to frame your pitch for THIS specific company and person.
  Consider: their company stage, recent news, the attendee's role and priorities,
  and any competitive displacement opportunity. This is the "elevator pitch
  calibrated to this meeting" paragraph.]

### Reference Customers
[Similar companies from your case_studies that match their industry, size, or
  use case. Include the outcome/metric if available. If no matching case studies,
  omit this subsection rather than forcing a weak match.]

## Talking Points
[3-5 specific, actionable conversation starters grounded in the research.
  Not generic "ask about their priorities" — specific: "Ask about their
  migration from [old tool] to [new tool] that they announced last month."
  When value positioning is active, weave 1-2 positioning angles into the
  talking points naturally — don't make every talking point a sales pitch.]

## Watch Out For
[1-3 things to be aware of — sensitive topics (recent layoffs, bad press),
  potential awkward overlaps, information gaps you couldn't fill.]

## Sources
[Numbered list of key URLs cited in the briefing]
```

**Meeting type adaptations:**

| Type | Emphasis | Add to briefing | Value Positioning |
|------|----------|-----------------|-------------------|
| Sales / discovery | Buyer authority, pain signals, competitive stack | "Qualification signals" section | **Yes** — full section |
| Partnership | Mutual benefit signals, integration opportunities | "Alignment opportunities" section | **Yes** — focus on integration hooks |
| Board / investor | Financial context, market position, portfolio overlap | "Key metrics to reference" section | **Yes** — focus on recommended positioning |
| Interview | Candidate's work history depth, cultural signals | "Assessment angles" section | No |
| Internal | Skip company research, focus on person's recent work | Lighter format, no company section | No |
| General external | Balanced across all dimensions | Standard format above | No |

**Core rules:**
- Every factual claim about an external company or person must have a source URL.
  Data drawn from the user's own business profile (differentiators, integrations,
  case studies) should be attributed to the profile rather than requiring an
  external source.
- Lead with the Quick Take — most readers stop there.
- Talking points must be specific to THIS meeting, grounded in research findings.
  Never generate generic conversation starters.
- Say "no public information found" for a person rather than speculating about their
  role or background.
- If memory has prior meeting notes, surface open items and continuity points
  prominently — this is the highest-value content.
- Value Positioning claims must be grounded in research from Step 4.5. Never
  generate generic positioning advice like "highlight your product's strengths."
  Every value mapping must reference a specific finding about the attendee's
  company paired with a specific capability from the user's profile or research.
- If `sales_context` is missing from the profile, note it once at the end of the
  Value Positioning section: "Tip: Edit your profile at
  `~/.nimble/business-profile.json` to add sales context (differentiators,
  integrations, case studies) for richer positioning next time."

### Step 7: Save to Memory

Make all Write calls simultaneously:

- Report → `~/.nimble/memory/reports/meeting-prep-[company-slug]-[date].md`
- Per attendee → `~/.nimble/memory/people/[name-slug].md`
  (use the format in `references/memory-and-distribution.md`). Add `[[path/entity]]`
  cross-references for the attendee's employer (e.g., `[[competitors/widgetco]]` or
  `[[companies/widgetco]]`) and any other discovered relationships.
- Company profile → update `~/.nimble/memory/companies/[company-slug].md` if new
  company data was found. Add reverse cross-references to the people researched
  (e.g., `[[people/alex-kim]]`).
- Profile → update `last_runs.meeting-prep` in `~/.nimble/business-profile.json`
  (only if profile exists)
- Follow the wiki update pattern from `references/memory-and-distribution.md`: update
  `index.md` rows for all affected entity files, append a `log.md` entry for this run.

The person profile in `people/` should contain structured key facts (role, background,
interests, communication style) that can be loaded by future meeting prep runs.

### Step 8: Share & Distribute

**Always offer distribution — do not skip this step.** Follow
`references/memory-and-distribution.md` for connector detection, sharing flow, and
source links enforcement.

### Step 9: Follow-ups

- **Go deeper** on an attendee → more focused person research
- **Add attendees** → research additional people joining the meeting
- **"What about [topic]?"** → targeted search on specific dimension
- **"Looks good"** → done
- **Sibling skills:** `company-deep-dive` for a full 360 on the company,
  `competitor-intel` to track them as a competitor, `competitor-positioning`
  to compare messaging before a sales meeting

---

## Agent Teams Mode (Dual-Mode)

Check at startup: `echo $CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS`

**Team mode** (flag set): Spawn **teammates** instead of sub-agents. Each teammate
researches one attendee and can message the others when finding cross-connections.

| Teammate | Focus | Cross-checks with |
|----------|-------|-------------------|
| **Attendee 1 researcher** | Full person + company research for attendee 1 | All other teammates (shared employers, connections) |
| **Attendee 2 researcher** | Full person + company research for attendee 2 | All other teammates |
| **[Additional per attendee]** | ... | ... |

How cross-attendee discovery works:
1. Each teammate researches their assigned attendee independently
2. When a teammate discovers a workplace, school, or connection that overlaps with
   another attendee, they send a message to that teammate: "My attendee [Name] worked
   at [Company] from 2019-2022 — did yours overlap?"
3. The receiving teammate checks and responds
4. Lead (you) collects all cross-references and builds the Relationship Map section

This produces higher-quality relationship maps than solo mode because teammates
actively search for connections rather than just comparing results post-hoc.

**Solo mode** (flag not set): Standard sub-agent flow from Step 3.

---

## What This Skill Is NOT

- **Not competitor monitoring** — use `competitor-intel` for tracking competitors
- **Not a company deep dive** — use `company-deep-dive` for research without attendees
- **Not a CRM** — gathers web intelligence, doesn't manage contacts or pipelines
- **Not a calendar app** — reads events for context but doesn't manage them

---

## Error Handling

See `references/nimble-playbook.md` for the standard error table. Skill-specific errors:

- **Person not found:** Try name variations (full, first+last, with company). If still
  nothing: "Couldn't find public info on [Name]. Can you share their title or LinkedIn?"
- **Ambiguous name:** Present top candidates with company/title context and ask.
- **Empty company results:** Note it and focus on attendee-level findings.

Referenced files: 4

nimble-databricks-data-products10.2 KB

View saved version →

---
name: nimble-databricks-data-products
description: |
  Builds Databricks data products from live web data, end to end: discovers the right Nimble
  web-data agents, scrapes into Delta tables, and produces an AI/BI dashboard and/or a deployed
  Databricks App — a table → dashboard → app workflow, for production data products or quick demos.
  Use whenever a request pairs live or scraped web data WITH a Databricks destination — e.g. "scrape
  Amazon/Walmart prices into a Delta table and build a dashboard", "load Zillow/Instagram/Maps/search
  results into Databricks and build a dashboard or app", "showcase Nimble + Databricks to a prospect".
  Prefer it over nimble-web-expert or competitor-intel when the data lands in Databricks. Do NOT use
  for one-off web fetches or CSV exports with no Databricks destination — use nimble-web-expert
  instead. Do NOT use for competitor or company research briefings — use competitor-intel or
  company-deep-dive instead. Do NOT use for generic Databricks work with no Nimble/web-data angle —
  use the official databricks-* skills instead.
allowed-tools:
  - Bash(databricks:*)
  - Bash(python3:*)
  - Bash(bash:*)
  - Bash(jq:*)
  - Bash(npm:*)
  - Read
  - Write
  - Edit
  - AskUserQuestion
  - Skill
metadata:
  author: Nimbleway
  version: 1.6.1
  category: data-platforms
---

# Nimble on Databricks — data products builder

Turn a natural-language brief like
`pricing analysis on dog products from walmart and amazon` into working Databricks data products:
**discover agents → ingest live web search data into Delta → build dashboard and/or app → deliver links.**
Equally at home for a quick demo or a real, reusable data product.

You are the orchestrator. Databricks mechanics are delegated to the official `databricks-*`
skills (see `references/databricks-skills.md`); this skill owns the **Nimble glue and the gaps**
(agent discovery, ingestion-from-agents, the AI/BI dashboard JSON, branding).

## Golden rules

- **Discover, don't assume.** Read agent names via `nimble_agent_list()`, input params via
  `nimble_agent_describe('<agent>')`, and output fields by probing one call (`to_json(parsing[0])`) —
  never hardcode from memory (Amazon search takes `keyword`, not `query`).
- **Probe before fanning out.** Run one call per source first to learn its localization flag, field
  names, and value formats — sources differ (some return numeric prices, others currency strings).
- **One statement per Statements API call.** Multiple `;`-separated statements in one call are a parse error.
- **Each Bash call is a fresh shell.** Env vars and `cd` don't persist — set them inline. See `references/preflight.md`.
- **Fail fast, then confirm.** Run Phase 0 preflight first; recommend a warehouse + writable schema, then confirm before writing.
- **Always ask the deliverable.** Table / +dashboard / +app is a per-run choice.
- **Branding is always on, neutral.** "Powered by Nimble" + light theme + yellow accent. See `references/branding.md`.
- **Leave artifacts in place.** No teardown.
- **Show your work and the headline.** End with URLs and the one-sentence insight (e.g. the price gap).

## Workflow

Track these as todos so nothing is skipped.

### Phase 0 — Preflight (read-only, fail fast)
Lean on the **`databricks-core`** skill for the generic checks.
1. `databricks current-user me` → confirm auth; capture the username (for the default schema).
2. Find a **RUNNING** SQL warehouse: `databricks warehouses list`. Prefer one already RUNNING; if none, offer to start one.
3. **Integration gate** — confirm these exist:
   `nimble_integration.tools.{nimble_search, nimble_extract, nimble_agent_run, nimble_agent_list, nimble_agent_describe}`.
   Quick check: `databricks functions list nimble_integration tools`.
   **If missing → STOP** and walk the user through `references/install-nimble-integration.md`
   (Nimble cookbook). Do not try to auto-install.
4. **Recommend + confirm** the target: a warehouse and a writable `catalog.schema`
   (default `users.<username>`). Verify writability — some shared catalogs deny `CREATE TABLE`.
   Present the recommendation and let the user confirm or override before writing.

Details + exact commands: `references/preflight.md`.

### Phase 1 — Interpret the brief + clarify (AskUserQuestion)
Parse the brief into: **domain/entity · search terms · sources · analysis goal**.
Then ask (batch into one AskUserQuestion call):
- **Deliverable** — always ask: table / table + dashboard / table + dashboard + app.
- **Sources** — confirm the agents you matched (e.g. Amazon + Walmart SERP).
- **Volume** — default ~8–10 search terms, ~100+ rows/source.

Keep the brief's intent (the "analysis goal") — it picks the Phase 4 template and the headline.

### Phase 2 — Discover agents + map a unified schema
See `references/nimble-agents.md`.
1. `nimble_agent_list()` via SQL, filter by the source/domain keywords.
2. For each chosen agent: `nimble_agent_describe('<name>')` → read its input params (required ones,
   exact names, localization/pagination flags). Output fields come from the §2.5 probe, not here.
3. Design **one unified table** with a `source` column + a normalized core
   (`product_name, price, currency, rating, review_count, brand, url, …`), keeping only fields the
   chosen agents actually emit. Multi-source comparison hinges on the shared columns.

### Phase 3 — Ingest (control table + one set-based call)
See `references/nimble-agents.md` for the full SQL. Drive ingestion from a **control table**, not
per-keyword files — it's reproducible and expandable (add a row, re-run).
0. **Probe ONE call per source first (fail fast).** Before fanning out, run a single
   `nimble_agent_run` per source and check: status, the real field names, the localization flag, and
   whether a price casts cleanly. This catches the Walmart-class surprises (localization, currency-
   string prices, `product_price` vs `price`) in ~40s instead of after a wasted full round. Highest-
   leverage step — see `nimble-agents.md` §2.5.
1. Create a **control (queries) table** `<schema>.<table>_queries` (source, agent, keyword,
   params_json, localization, enabled) and seed one row per (source × term). `params_json` uses each
   agent's **real** param name (from `input_properties`); set **localization per agent** (e.g.
   `amazon_serp` true, `walmart_serp` false).
2. Create the **unified results table** (`source` column + normalized core + `raw VARIANT`).
3. Run **one INSERT** that calls `nimble_agent_run(q.agent, q.params_json, q.localization)` via a
   correlated `LATERAL` join over the control table, with a `/*+ REPARTITION(N) */` hint (N ≈ enabled
   rows, kept modest — high parallelism can trip API rate limits) so the agent calls run in parallel.
   It's one long statement → run it async with `bash scripts/ingest.sh <WH> ingest.sql`.
4. **Reconcile against the control table** (LEFT JOIN): a term that lands no items returns an empty
   result, and a correlated LATERAL drops empty rows — so reconcile to confirm every source is
   covered. If a source shows 0, re-check its localization flag (per-agent) and casts before
   building; see `nimble-agents.md` §6 for the diagnostic order.

### Phase 4 — Build the deliverable(s)
Choose a **template** from the matched agents' `vertical`/`entity_type`:

| Vertical | Dashboard/app shape |
|----------|---------------------|
| Ecommerce (SERP/PDP/CLP) | KPIs; listings & avg price by source/keyword; sponsored share; price-vs-rating scatter; product table with Open links; multi-source → comparison bars + best-effort item-level price gap |
| Social | volume/engagement by account/post; top-content table; like/follower distributions |
| Real Estate | price & price/sqft; listings by location; beds/baths breakdowns |
| Maps / Local | avg rating; review counts; places table |
| LLM / AEO | source/answer presence; share-of-voice; citation table |
| _fallback_ | KPIs + 2 categorical bars + the raw table (works off any `output_schema`) |

**Comparison depth (hybrid):** always build the aggregate/category comparison; *additionally* try
best-effort item-level matching across sources (normalize brand + key tokens). If confident matches
exist, add a "same-product price gap" view; otherwise keep the aggregate comparison and note that
item-level matching wasn't confident.

- **Dashboard** → use `scripts/build_dashboard.py` (compact spec → valid `serialized_dashboard`,
  create + publish). It bakes in every Lakeview gotcha. Read `references/dashboard-cookbook.md` for
  the spec format and recipes.
- **App** → follow `references/app-cookbook.md` (delegates scaffold/deploy to `databricks-apps`;
  adds the Nimble-specific SQL, branding, and the numeric-string / light-mode gotchas).
- **Branding** → `references/branding.md` (always applied).

### Phase 5 — Verify, deliver & share
- Publish the dashboard / confirm the app is `RUNNING`; collect URLs.
- Summarize what was built and the **headline insight** (the comparison takeaway).
- **Offer to share** the dashboard/app link — if a Slack or Notion connector is available, offer to
  post it there (Slack = the link + headline; Notion = a short dated page). Mention once; don't nag.
- **Suggest next steps** with sibling skills, e.g. `competitor-intel` / `company-deep-dive` for
  business signals on the brands surfaced, or `nimble-web-expert` for a one-off deeper pull.
- Offer iterations (more charts, item-level matching, theming, a scheduled refresh job).

## Reference map
- `references/databricks-skills.md` — which official `databricks-*` skill to use per phase.
- `references/install-nimble-integration.md` — setup when the integration gate fails.
- `references/preflight.md` — auth, warehouse, writable-schema discovery (exact commands).
- `references/nimble-agents.md` — discovery, schema mapping, ingestion SQL + gotchas.
- `references/dashboard-cookbook.md` — Lakeview JSON recipes + every gotcha (authoritative).
- `references/app-cookbook.md` — AppKit demo app glue + gotchas.
- `references/branding.md` — "Powered by Nimble", logo, colors.
- `scripts/ingest.sh` — async statement fan-out + poll.
- `scripts/build_dashboard.py` — compact spec → create + publish a dashboard.
- `assets/nimble-logo.png` — the Nimble mark for app branding.

Referenced files: 10

nimble-web-expert23.7 KB

View saved version →

---
name: nimble-web-expert
description: |
  Get web data now — fast, incremental, immediately responsive to what the user needs.
  The only way Claude can access live websites.

  USE FOR:
  - Fetching any URL or reading any webpage
  - Scraping prices, listings, reviews, jobs, stats, docs from any site
  - Running Extraction Templates — reusable, site-specific structured scrapers
  - Running Web Search Agents — open-ended research, enrichment, and dataset building with citations
  - Discovering URLs on a site before bulk extraction
  - Calling public REST/XHR API endpoints
  - Web search and research (8 focus modes)
  - Bulk crawling website sections

  Must be pre-installed and authenticated. Run `nimble --version` to verify (>= 1.2.0).
allowed-tools:
  - Bash(nimble:*)
  - Bash(claude:*)
  - Bash(mkdir:*)
  - Bash(cat:*)
  - Bash(head:*)
  - Bash(ls:*)
  - Bash(grep:*)
  - Bash(echo:*)
  - Bash(python3:*)
  - Bash(uv:*)
  - Bash(npm:*)
  - Bash(open:*)
  - Bash(export:*)
  - Bash(wait:*)
  # MCP fallback (used when shell isn't available — Cowork, IDE-only hosts):
  - mcp__plugin_nimble_nimble__nimble_search
  - mcp__plugin_nimble_nimble__nimble_extract
  - mcp__plugin_nimble_nimble__nimble_extract_async
  - mcp__plugin_nimble_nimble__nimble_map
  - mcp__plugin_nimble_nimble__nimble_crawl_run
  - mcp__plugin_nimble_nimble__nimble_crawl_status
  - mcp__plugin_nimble_nimble__nimble_crawl_list
  - mcp__plugin_nimble_nimble__nimble_crawl_terminate
  - mcp__plugin_nimble_nimble__nimble_task_results
  - mcp__plugin_nimble_nimble__nimble_extract_templates_list
  - mcp__plugin_nimble_nimble__nimble_extract_templates_get
  - mcp__plugin_nimble_nimble__nimble_extract_templates_run
  - mcp__plugin_nimble_nimble__nimble_extract_templates_run_async
  - mcp__plugin_nimble_nimble__nimble_agents_list
  - mcp__plugin_nimble_nimble__nimble_agents_get
  - mcp__plugin_nimble_nimble__nimble_agent_templates_list
  - mcp__plugin_nimble_nimble__nimble_agent_templates_get
  - mcp__plugin_nimble_nimble__nimble_agents_run
  - mcp__plugin_nimble_nimble__nimble_agents_run_status
  - mcp__plugin_nimble_nimble__nimble_agents_run_result
  - mcp__plugin_nimble_nimble__nimble_agents_runs_list
  - Read
  - Write
  - Edit
  - Glob
  - Grep
  - Task
  - AskUserQuestion
license: MIT
metadata:
  version: 1.7.0
  author: Nimbleway
  repository: https://github.com/Nimbleway/agent-skills
  category: web-search-tools
---

# Nimble Web Expert

Web extraction, search, and URL discovery using the Nimble CLI. Returns clean structured data from any website.

User request: $ARGUMENTS

## Core principles

- **Route by intent first** (see [Analyze & Route](#analyze--route) for the full decision model). Named site with a matching Extraction Template + a direct item to look up → run the template. Site with no template, or a need that requires discovery/reasoning across pages → a Web Search Agent. One-off single URL → `nimble extract`. Raw results to work from ("find pages/articles about…") → `nimble search`; a synthesized deliverable (report, brief, comparison, recommendation) → a Web Search Agent. Discover/crawl URLs → `nimble map` or `nimble crawl`.
- **Web Search Agent runs: pick a run mode before building the command.** Default to named create-or-reuse — `nimble agents run --agent-name <stable-name>` — so a repeat session lands on the same agent. `agents:runs create` is the explicit-agent-ID route only and requires `--agent-id`. `references/nimble-agents/reference.md` has the mode table, `use_case` locking, and the one-time `skill` override.
- **One command → present results → done.** Run once, show the data immediately as a table. Do NOT experiment, loop, or write Python to parse output.
- **Multiple inputs → always parallel.** 2+ URLs/keywords/ASINs → `&`+`wait`. 6–20 → `xargs -P`. 20+ → Python asyncio script. See `references/batch-patterns.md`.
- **Escalate render tiers silently on empty or truncated content.** Tier 1 → 2 → 3 → … without asking. Surface a decision only when all tiers fail and investigation tools are needed. An access barrier is a different outcome, not a tier to climb — see Guardrails.
- **Never answer from training data.** Live prices, current news, today's listings → always fetch via Nimble. If unavailable, say so.
- **AskUserQuestion at every meaningful choice.** Header ≤12 chars, 2–4 options, label 1–5 words, recommended option first. Never present choices as numbered prose.
- **Save all outputs to `.nimble/`.** Never leave extraction results in memory only.
- **Verify the connection BEFORE working — don't fire a data call and react to the error.** With bash, `nimble --version` + `NIMBLE_API_KEY` confirms the CLI path; otherwise run one read-only `mcp__plugin_nimble_nimble__nimble_agents_list` probe. Success = connected; an auth/not-connected error or a response containing an OAuth authorization URL = not connected.
- **No working CLI and no connected MCP → stop.** Do not fall back to WebFetch, WebSearch, curl, or `dangerouslyDisableSandbox`. If the plugin is installed but the connector isn't connected (typical Cowork / claude.ai), surface the verbatim connect steps from `rules/setup.md` and stop; if no plugin at all, follow the install flow in `rules/setup.md`.
- **If a tool hands back an OAuth "Authorize" link instead of data, present it exactly as given and stop.** Never invent a "paste the URL back" / "I'll complete the connection" step — none exists — and never claim tools "will activate" then call them in the same turn. Wait for the user to authorize, then retry or re-probe.

## Capabilities

One skill, one taxonomy. Use Nimble's own product names precisely — never paraphrase them.

| Capability             | What it is                                                                        | Command family              |
| ---------------------- | --------------------------------------------------------------------------------- | --------------------------- |
| **Search**             | Real-time web search — raw results (pages, snippets), 8 focus modes               | `nimble search`             |
| **Extract**            | Fetch + parse a single known URL (the one-off primitive)                          | `nimble extract`            |
| **Extraction Template**| Reusable, site-specific structured scraper for a known item (by URL or identifier)| `nimble extract:templates`  |
| **Web Search Agent**   | Open-ended research / enrichment / dataset building — discovers sources, synthesizes, cites | `nimble agents` / `agents:runs` |
| **Map**                | Discover the URLs that exist on a site                                            | `nimble map`                |
| **Crawl**              | Bulk-fetch many pages across a site (one-time, at scale)                          | `nimble crawl`              |

Extraction Templates and Web Search Agents are distinct — an Extraction Template is a fixed, site-specific parser; a Web Search Agent reasons across sources. Never call an Extraction Template a "WSA" or a "legacy WSA," and never route a template use to `agents` (or vice-versa) by name alone. Building new templates/agents is out of scope here — use **existing** ones (point users to the Nimble app to build new).

## Interactive UX

- Use `AskUserQuestion` at every meaningful choice — never guess, never ask in prose.
- **Ambiguous request** (no URL, vague topic): ask before running — "What would you like to do?" → Research & report / Search / Fetch URL / Discover URLs
- **Gate B landed on a Web Search Agent at `high`+ effort**: offer the cost/latency fork — Researched report / Quick scan (see [Analyze & Route](#analyze--route))
- **Before running a search** (if task maps to a specific focus mode): offer focus mode — General / News / Coding / Shopping / Academic / Social
- **After all tiers fail**: check investigation tools (`which browser-use`, `python3 -c "from playwright.sync_api..."`) and ask whether to investigate with browser-use, Playwright, or skip.
- After presenting results, always close with: "Were these results what you needed?" → `Looks great!` / `Mostly good` / `Not quite` / `Skip feedback`

## Prerequisites

Pick CLI or MCP at session start — same skill, two transports. Once a transport is selected, stick with it for the session and don't re-probe on every command.

```bash
nimble --version && echo "${NIMBLE_API_KEY:+API key: set}"        # CLI path
# OR (fallback when shell isn't available)
claude mcp list 2>/dev/null | grep -q "nimble" && echo "MCP: ok"  # plugin MCP
```

- **CLI ready** (version + API key both print) → proceed to [Step 0](#analyze--route), use `nimble ...` commands.
- **MCP connected** (no CLI, but plugin is installed) → proceed to [Step 0](#analyze--route), use `mcp__plugin_nimble_nimble__*` tools instead.
- **Neither** → load `rules/setup.md` for the environment-aware install flow. Any Claude product (Code, Cowork, claude.ai) → `/plugin install nimble`. Codex or other terminal-only agents → `npm i -g @nimble-way/nimble-cli`. Cursor / VS Code / generic MCP clients → paste the `mcp.json` snippet.

**If bash is denied:** you're in a Cowork-like / MCP-only host. Use `mcp__plugin_nimble_nimble__*` tools, but verify the connection first with one read-only `nimble_agents_list` probe. If the probe fails with an auth/not-connected error or returns an OAuth authorization URL, the connector isn't connected — surface the connection steps from [Core principles](#core-principles) and stop (and never invent an auth-completion flow). **Never substitute WebFetch, WebSearch, curl, or any other tool for Nimble operations.**

---

## Analyze & Route

Two gates, in order. **Gate A** asks where the data lives; **Gate B** asks what the user wants back. Most mis-routes come from skipping Gate B — a request with no location signal is not automatically a search.

### Gate A — do I know where the data lives?

| User signal                                   | Route                                                                               |
| --------------------------------------------- | ------------------------------------------------------------------------------------ |
| Direct single URL to fetch                    | `nimble extract`                                                                     |
| Named site + a direct item to look up (URL/ID)| **Step 0** — check for an Extraction Template first                                  |
| "Find URLs / sitemap / all pages"             | `nimble map`                                                                         |
| "Crawl / archive a whole section"             | `nimble crawl`                                                                       |
| **No location signal at all**                 | Fall through to **Gate B**                                                           |
| Named site with **no** template               | Fall through to **Gate B**, carrying the site as a source constraint                 |

**The most common overlap — a site with no Extraction Template.** It looks like a choice between a raw `extract` (which dumps parsing work on the user) or building a template (out of scope). Neither is right: fall through to Gate B, which will land on a **Web Search Agent** — it configures fresh for any site and reasons about structure without a maintained template. This isn't a question to put to the user; when no template fits, the answer is the same every time.

### Step 0 — Extraction Template check (when a site + direct item is named)

Templates return clean structured data with zero selector work. Always check first.

**Always verbalize — never silently:**

1. **Announce:** _"Let me check if there's a Nimble Extraction Template for [site]..."_
2. **Report:** _"Found `<template_name>` — using it now."_ or _"No template for [site] — using a Web Search Agent instead."_

**Lookup order:**

1. `~/.claude/skills/nimble-web-expert/learned/examples.json` → learned templates
2. `nimble extract:templates list --limit 100` → filter by site/domain client-side; confirm the match
3. Inspect the schema before running: `nimble extract:templates get --extract-template-name <name>`
4. No match → route to a Web Search Agent (per the overlap rule above)

```bash
nimble extract:templates run --template <name> --params '{"key": "value"}'
```

`--params` is a JSON/YAML mapping matching the template's `input_schema`. The response is the records defined by the template's `output_schema` (array for list/SERP-style, object for detail/PDP-style) — read the schema from `get` to know the shape. See `references/nimble-extract-templates/reference.md`.

⚠️ For finding information, use `nimble search`, not a SERP-analysis template. SERP templates are for rank/SEO analysis, not general retrieval.

### Gate B — what does the user want back?

`nimble search` returns **raw material to skim**. A Web Search Agent returns a **finished, cited answer**. The prompt's deliverable noun decides it — route on that, not on how open-ended the topic sounds.

| → **Web Search Agent**                                                | → **`nimble search`**                       |
| ---------------------------------------------------------------------- | --------------------------------------------- |
| report, brief, analysis, landscape, teardown, deep dive                | find, search for, look up                     |
| compare, "best X", "which should I", "state of", recommend             | "pages/articles about", "links to"            |
| enrich, build a list, dataset, "…with their pricing/headcount"         | latest news, recent posts, what's trending    |

Structured rows about many entities → Web Search Agent with `enrichment` or `dataset_building`. See `references/nimble-agents/reference.md`.

### Offer the fork when the answer is the expensive one

A Web Search Agent at `high` effort takes minutes and costs more; a search takes seconds. That's a real trade-off, so surface it — **but only when Gate B lands on a Web Search Agent AND the recommended effort is `high` or above.** One `AskUserQuestion`, recommended option first:

- **Researched report** — Web Search Agent, a few minutes, every claim cited
- **Quick scan** — `nimble search`, seconds, raw links you skim yourself

Below `high`, don't ask — just run the Web Search Agent. Never ask when Gate A already resolved the route.

**Dataset requests always clear the threshold.** "Build a list of…" → `dataset_building`, which runs at `high` or above by definition, so the fork always applies. Enrichment has no such floor — judge "enrich these rows" on the normal effort rule and skip the prompt when a small, well-specified fill-in lands below `high`.

**Before starting any Web Search Agent run, say how long it will take**, then narrate at phase transitions. On MCP, progress comes from bounded status polling rather than a live stream — poll and report each step, because an un-narrated multi-minute run reads as a hang.

---

## Workflow

| Situation                        | Command                                        | Reference                                            |
| -------------------------------- | ---------------------------------------------- | ---------------------------------------------------- |
| Site + item → template first     | `extract:templates list` → `extract:templates run` | `references/nimble-extract-templates/reference.md`   |
| Research / enrichment / dataset  | pick a run mode → `get` → `result`             | `references/nimble-agents/reference.md`                  |
| Direct URL                       | `nimble extract`                               | `references/nimble-extract/reference.md`                 |
| Search the live web              | `nimble search`                                | `references/nimble-search/reference.md`                  |
| Discover URLs on a site          | `nimble map`                                   | `references/nimble-map/reference.md`                     |
| Bulk crawl a section             | `nimble crawl run`                             | `references/nimble-crawl/reference.md`                   |
| Batch templates (up to 1,000)    | `nimble extract:templates batch`               | `references/nimble-extract-templates/reference.md`       |
| Batch extract (up to 1,000)      | `nimble extract-batch`                         | `references/nimble-extract/reference.md`                 |
| Poll tasks / batches / results   | `nimble tasks` / `nimble batches`              | `references/nimble-tasks/reference.md`                   |
| Unknown selectors or XHR path    | browser-use or Playwright investigation        | `references/nimble-extract/browser-investigation.md` |
| Proven site patterns             | copy a recipe                                  | `references/recipes.md`                              |
| 2+ inputs                        | parallel bash `&`+`wait` or generated script   | `references/batch-patterns.md`                       |

For the full extract waterfall (tiers, flags, browser actions, network capture), see `references/nimble-extract/reference.md`.

---

## Response shapes

| Command                     | Output                                                                                  |
| --------------------------- | --------------------------------------------------------------------------------------- |
| `nimble extract:templates`  | Records per the template's `output_schema` — array (list/SERP) or object (detail/PDP)   |
| `nimble agents:runs result` | `output` (`type:"text"` prose or `type:"json"` structured) + `trust` per-claim citations |
| `nimble extract`            | HTML, Markdown, or parsed JSON — depends on `--format` and `--parse`                     |
| `nimble search`             | Structured results array (title, URL, description)                                      |
| `nimble map`                | URL list + metadata                                                                     |
| `nimble crawl`              | Async job — poll with `nimble crawl status <job_id>`                                     |

**Read the template's `output_schema` (from `extract:templates get`) before parsing** — a list/SERP-style template returns an array, a detail/PDP-style template returns an object. Web Search Agent runs are async: poll `agents:runs get` to a terminal state, then fetch `result`.

## Output & Organization

```bash
mkdir -p .nimble   # save all outputs here
```

Naming: `.nimble/<site>-<task>.md` (e.g. `.nimble/amazon-airpods.md`, `.nimble/yelp-sf-italian.json`)

Working with saved files:

```bash
wc -l .nimble/page.md && head -100 .nimble/page.md
grep -n "price\|rating" .nimble/page.md | head -30
```

End every response with: `Source: [URL] — fetched live via Nimble CLI`

---

## Self-Improvement

The skill maintains `~/.claude/skills/nimble-web-expert/learned/examples.json`.

- **At task start:** read the file, scan `good[]` for `url_pattern` matches → use documented `command`/`tier` as starting point. Scan `bad[]` → avoid documented pitfalls.
- **After presenting results:** ask "Were these results what you needed?" → on positive feedback, append to `good[]` with `url_pattern`, `task`, `command`, `tier`, `notes`. On negative feedback, ask "What went wrong?" and append to `bad[]` with `url_pattern`, `task`, `issue`, `avoid`, `better`.
- Keep entries concise — 5–10 per site. Only write on real feedback, never speculatively.

---

## Guardrails

- **NEVER answer from training data** for live prices, current news, or real-time data. If Nimble is unavailable, say so.
- **NEVER skip Step 0 silently.** Even if certain there's no template, announce the check before falling back to a Web Search Agent or extract/search.
- **NEVER answer a synthesis deliverable with raw search results.** "Report", "brief", "compare", "best X", "which should I" → Gate B routes to a Web Search Agent. Handing back a list of links and calling it a report is the most common mis-route.
- **Distinguish Extraction Templates from Web Search Agents.** Never call a template a "WSA"/"legacy WSA," and never route a template use to `agents` by name alone (or the reverse). Building new templates/agents is out of scope — use existing ones.
- **When a run comes back empty, partial, or clearly wrong, say so plainly** — a domain that returned nothing, a template that matched poorly, a search with no relevant hits are real outcomes, not something to present as success. Suggest an obvious next step (broader source, a different capability) where one exists.
- **NEVER retry the same render tier.** If a tier returns empty or truncated content, escalate — do not re-run.
- **NEVER escalate at an access barrier.** A CAPTCHA, a human-verification page, or a sign-in wall in place of the target is a real outcome — report it plainly and stop. Where a supported alternative exists, take it: `--focus social` search for social profiles, public search results for gated articles.
- **NEVER substitute WebFetch, WebSearch, curl, or wget for nimble operations.** They're not in `allowed-tools` — if a Nimble transport isn't available, stop and follow the guidance in the no-transport branch of Core principles. Don't try to work around it.
- **NEVER load reference files speculatively.** Only read a reference when the current task explicitly needs it.
- **Task agents MUST use `run_in_background=False`.**
- **Hard retry limit.** On error (not empty content): retry at most 2 times with different flags. After 2 errors, report and stop.
- **Hard 429 rule.** On rate-limit error: stop immediately. Do not retry or switch tiers.

---

## Reference files

Load only when needed:

| File                                                 | Load when                                                                     |
| ---------------------------------------------------- | ----------------------------------------------------------------------------- |
| `references/recipes.md`                              | Need a proven command for a common site (Amazon, Yelp, LinkedIn…)             |
| `references/nimble-extract-templates/reference.md`       | Step 0 — discover/inspect/run Extraction Templates for a known site           |
| `references/nimble-agents/reference.md`                  | Web Search Agents — discovery, run lifecycle, authoring, trust/citations      |
| `references/nimble-extract/reference.md`                 | Extract flags, render tiers, browser actions, network capture, parser schemas |
| `references/nimble-search/reference.md`                  | Search flags, all 8 focus modes                                               |
| `references/nimble-map/reference.md`                     | Map flags, response structure                                                 |
| `references/nimble-crawl/reference.md`                   | Full async crawl workflow                                                     |
| `references/nimble-tasks/reference.md`                   | Poll tasks/batches, fetch results — for async, batch, and crawl operations    |
| `references/nimble-extract/browser-investigation.md` | Tier 6 — CSS selector/XHR discovery with browser-use or Playwright            |
| `references/nimble-extract/parsing-schema.md`        | Parser types, selectors, extractors, post-processors                          |
| `references/nimble-extract/browser-actions.md`       | Full browser action types and parameters                                      |
| `references/nimble-extract/network-capture.md`       | Filter syntax, XHR mode, capture+parse patterns                               |
| `references/nimble-search/search-focus-modes.md`     | Decision tree, mode details, combination strategies                           |
| `references/batch-patterns.md`                       | Parallel bash patterns for 2–5, 6–20, and 20+ inputs                          |
| `references/error-handling.md`                       | Error codes, known site issues, troubleshooting                               |

Referenced files: 19

seo-intel5.16 KB

View saved version →

---
name: seo-intel
description: |
  SEO intelligence toolkit covering the full lifecycle via live web data:
  keyword research, rank tracking, site audits, content gap analysis,
  competitor keyword reverse-engineering, AI visibility across five platforms
  (ChatGPT, Perplexity, Google AI, Gemini, Grok), and GitHub repo SEO.
  Crawls real sites and SERPs via Nimble CLI — no fabricated metrics.

  Triggers: "SEO", "keywords", "rank tracker", "site audit", "content gap",
  "competitor keywords", "AI visibility", "GitHub SEO", "SERP analysis",
  "keyword research", "technical SEO", "keyword difficulty", "topic
  clusters", "ranking delta", "on-page SEO", "AI citation audit".

  Do NOT use for competitor business signals — use `competitor-intel`
  instead. Do NOT use for competitor messaging — use
  `competitor-positioning` instead. Do NOT use for general web scraping
  — use `nimble-web-expert` instead.
allowed-tools:
  - Bash(nimble:*)
  - Bash(date:*)
  - Bash(cat:*)
  - Bash(mkdir:*)
  - Bash(python3:*)
  - Bash(echo:*)
  - Bash(jq:*)
  - Bash(ls:*)
  - Bash(gh:*)
  - Read
  - Write
  - Edit
  - Glob
  - Grep
  - Agent
  - AskUserQuestion
metadata:
  author: Nimbleway
  version: 1.6.1
  category: seo
---

# SEO Intelligence Toolkit

All-in-one SEO intelligence: keyword discovery, rank tracking, technical audits,
content gaps, competitor analysis, AI visibility, and GitHub SEO.

User request: $ARGUMENTS

**Before running any commands**, read `references/nimble-playbook.md` for Claude Code
constraints (no shell state, no `&`/`wait`, sub-agent permissions, communication style).
Tag all `nimble` CLI calls: `nimble --client-source nimble-agent-skills <subcommand>`. MCP requests are attributed at the transport level — see `references/nimble-playbook.md`.

---

## Workflow Router

Detect the user's SEO intent from `$ARGUMENTS` and route to the appropriate
workflow. If the intent is ambiguous or generic ("help me with SEO"), ask which
workflow to run.

### Available Workflows

| Workflow | Triggers | Reference |
|----------|----------|-----------|
| **Keyword Research** | keyword opportunities, topic clusters, difficulty, what to rank for | `references/wf-keyword-research.md` |
| **Rank Tracker** | track rankings, keyword positions, ranking delta, position check | `references/wf-rank-tracker.md` |
| **Site Audit** | SEO audit, technical SEO, meta tags, schema, crawl for issues, CWV | `references/wf-site-audit.md` |
| **Content Gap** | content gap, keyword gap, compare coverage, missing topics | `references/wf-content-gap.md` |
| **Competitor Keywords** | competitor keywords, reverse-engineer SEO, competitor title tags | `references/wf-competitor-keywords.md` |
| **AI Visibility** | AI visibility, ChatGPT presence, Perplexity, AI Overview, GEO | `references/wf-ai-visibility.md` |
| **GitHub SEO** | github seo, repo discoverability, optimize readme, repo audit | `references/wf-github-seo.md` |

### Intent Detection

Map the request to one workflow:

- **Keywords / topics / difficulty / clusters / "what to rank for"** → Keyword Research
- **Rankings / positions / tracking / delta / monitoring** → Rank Tracker
- **Audit / crawl / technical / meta / schema / links / CWV** → Site Audit
- **Gap / missing topics / coverage comparison / "what should I write"** → Content Gap
- **Competitor site crawl / reverse-engineer / on-page at scale** → Competitor Keywords
- **AI visibility / ChatGPT / Perplexity / AI Overview / GEO** → AI Visibility
- **GitHub / repo / README / discoverability** → GitHub SEO

If unclear, present the options with AskUserQuestion:

> Which SEO workflow should I run?
> - **Keyword Research** — discover keyword opportunities and topic clusters
> - **Rank Tracker** — check and track keyword positions over time
> - **Site Audit** — full technical SEO crawl with JS rendering
> - **Content Gap** — compare content coverage against competitors
> - **Competitor Keywords** — reverse-engineer competitor on-page SEO at scale
> - **AI Visibility** — measure brand presence across 5 AI platforms
> - **GitHub SEO** — audit repository discoverability and README quality

### Execution

Once a workflow is identified:

1. **Read** the corresponding `references/wf-{name}.md`
2. **Follow** its instructions from Step 0 (Preflight) through to the output format
3. All `references/` paths inside workflow files are relative to this skill's directory

### Workflow Chaining

After completing a workflow, suggest natural next steps using sibling workflows.
Common chains:

- Site Audit → Keyword Research → Content Gap → Rank Tracker
- Keyword Research → Competitor Keywords → Content Gap
- AI Visibility → Content Gap → Keyword Research

When chaining, read the next workflow file and continue. Context from the
previous run (discovered domains, keyword lists, profile data) carries forward
— do not re-run preflight or re-ask onboarding questions.

### Shared Configuration

All workflows use:
- **Business profile + onboarding:** `references/profile-and-onboarding.md`
- **Data persistence:** `references/memory-and-distribution.md`
- **CLI patterns + constraints:** `references/nimble-playbook.md`
- **AI platform agent discovery:** `references/ai-platform-profiles.md`

Referenced files: 19

talent-sourcing10.7 KB

View saved version →

---
name: talent-sourcing
description: |
  Finds qualified candidates for a role by searching LinkedIn, Indeed, GitHub,
  and other professional platforms using Nimble Web Search Agents. Accepts a
  job description, role title, or freeform request and returns a ranked
  candidate list with profiles, skills, and contact signals.

  Use this skill when the user wants to find, source, or recruit candidates for
  a role. Common triggers: "find candidates for", "source engineers in",
  "who can I hire for", "find me a [role]", "recruiting for", "talent search",
  "find a [role] in [city]", "build a candidate list", "sourcing for [role]",
  "who's available for", "find potential hires". Also triggers on a pasted job
  description followed by a sourcing request.

  Do NOT use for job market research or salary benchmarking — use
  market-finder instead. Do NOT use for researching a single known person
  — use company-deep-dive or meeting-prep instead.
allowed-tools:
  - Bash(nimble:*)
  - Bash(date:*)
  - Bash(cat:*)
  - Bash(mkdir:*)
  - Bash(python3:*)
  - Bash(echo:*)
  - Bash(jq:*)
  - Bash(ls:*)
  - Read
  - Write
  - Edit
  - Glob
  - Grep
  - Agent
  - AskUserQuestion
metadata:
  author: Nimbleway
  version: 1.6.1
  category: human-resources
---

# Talent Sourcing

Candidate discovery powered by Nimble Web Search Agents.

User request: $ARGUMENTS

**Before running any commands**, read `references/nimble-playbook.md` for Claude Code
constraints (no shell state, no `&`/`wait`, sub-agent permissions, communication style).

---

## Instructions

### Step 0: Preflight

Follow the transport selection + standard preflight from `references/nimble-playbook.md` — pick CLI or MCP at session start, then run the standard preflight calls (date calc, today, profile, memory index) in parallel.

Also simultaneously:
- `mkdir -p ~/.nimble/memory/{reports,talent-sourcing}`

From the results:
- CLI missing or API key unset → read `references/profile-and-onboarding.md`, stop
- Tag all `nimble` CLI calls: `nimble --client-source nimble-agent-skills <subcommand>`. MCP requests are attributed at the transport level — see `references/nimble-playbook.md`.
- Profile exists → note industry keywords if any; proceed to Step 1
- No profile → fine, talent-sourcing doesn't require onboarding; proceed to Step 1

### Step 1: Parse Request & Confirm Search Parameters

Parse `$ARGUMENTS` for:
- **Role** — job title or function (e.g. "Senior React Engineer", "Head of Sales")
- **Location** — city, metro, region, or remote (e.g. "New York City", "remote US")
- **Skills / requirements** — specific technologies, years of experience, domain expertise
- **Seniority** — junior, mid, senior, staff, director, VP, C-level
- **Source preference** — specific platforms (LinkedIn, GitHub, Indeed, etc.) or "all"

If a full job description was pasted, extract the above fields from it.

If **role** is missing or ambiguous, ask with `AskUserQuestion`:

> "What role are you hiring for, and where? (e.g. 'Senior ML Engineer, remote US'
> or paste a job description)"

Once parameters are clear, confirm with the user using `AskUserQuestion`:

> "Searching for: **[Role]** | Location: **[Location]** | Key skills: **[Skills]**
> | Seniority: **[Seniority]**
>
> Platforms to search: LinkedIn, Indeed, GitHub (for technical roles), AngelList /
> Wellfound, and professional communities.
>
> - **Start search**
> - **Adjust parameters first**"

### Step 2: WSA Discovery

Discover available Web Search Agents for candidate-sourcing platforms. Run
simultaneously:

```bash
nimble extract:templates list --limit 100  # then filter items for "linkedin people"
nimble extract:templates list --limit 100  # then filter items for "indeed resume"
nimble extract:templates list --limit 100  # then filter items for "github profile"
nimble extract:templates list --limit 100  # then filter items for "wellfound talent"
```

Filter results for `entity_type: SERP` or `entity_type: PDP`. Prefer
`managed_by: "nimble"`. Validate promising agents with:

```bash
nimble extract:templates get --extract-template-name {name}
```

Cache discovered WSA names and required params. If no WSAs found for a platform,
fall back to `nimble search` for that platform.

### Step 3: Parallel Candidate Search (Sub-Agents)

Spawn `nimble-researcher` agents (`agents/nimble-researcher.md`) with
`mode: "bypassPermissions"`, max 4 concurrent. Assign one agent per platform:

**Agent 1 — LinkedIn**

Search for people matching the role criteria. Use Boolean-style query construction:

```bash
nimble search --query "site:linkedin.com/in [Role] [Location] [Key Skills]" \
  --max-results 15 --search-depth fast
nimble search --query "[Role] [Location] linkedin profile [Skill1] [Skill2]" \
  --max-results 10 --search-depth fast
```

If a LinkedIn WSA was discovered in Step 2, use it instead with the role title,
location, and skill keywords as inputs.

**Agent 2 — Indeed / Resumes**

```bash
nimble search --query "site:indeed.com resume [Role] [Location] [Key Skills]" \
  --max-results 10 --search-depth fast
nimble search --query "[Role] resume [Location] [Key Skills]" \
  --max-results 10 --search-depth fast
```

**Agent 3 — GitHub (technical roles only)**

Skip this agent for non-technical roles (e.g. Sales, Marketing, Operations).

```bash
nimble search --query "site:github.com [Role] [Location] [Key Skills]" \
  --max-results 10 --search-depth fast
nimble search --query "github [Key Skills] developer [Location] open to work" \
  --max-results 10 --search-depth fast
```

**Agent 4 — AngelList / Wellfound + Communities**

```bash
nimble search --query "site:wellfound.com [Role] [Location] [Key Skills]" \
  --max-results 10 --search-depth fast
nimble search --query "[Role] [Location] open to work OR seeking opportunities \
  [Key Skills]" --max-results 10 --search-depth fast
```

Each agent returns: candidate name (if available), profile URL, current title,
location snippet, inferred skills, availability signals ("open to work", "seeking",
"available") with event date (if available) and source URL.

### Step 4: Deep Profile Extraction

For the top candidates identified in Step 3 (aim for 10–20 unique profiles across
all platforms), extract full profile details. Run all extractions simultaneously:

```bash
nimble extract --url "[profile-url]" --format markdown
```

From each extracted profile, pull:
- **Full name**
- **Current role & company**
- **Location**
- **Skills / tech stack**
- **Experience summary** (years, notable employers)
- **Education**
- **Availability signals** (open to work, recent job change, posting activity)
- **Contact signals** (email, personal site, GitHub handle)

For extraction failures, follow the fallback pattern in
`references/nimble-playbook.md`. If a profile is behind a login wall and extraction
fails, keep the search-snippet summary instead — do not skip the candidate.

**Extraction budget:** extract up to 15 profiles. If more than 15 candidates were
found in Step 3, prioritize by relevance score (seniority match + skill overlap +
location match) before extracting.

### Step 5: Score & Rank Candidates

Score each candidate (1–10) using these weighted signals:

| Signal | Weight |
|--------|--------|
| Role / title match | 30% |
| Skill overlap with requirements | 30% |
| Location match | 20% |
| Seniority match | 10% |
| Availability signals | 10% |

Group candidates into tiers:
- **Tier 1 (Strong match, 7–10):** All required signals present
- **Tier 2 (Partial match, 4–6):** Most signals present, 1–2 gaps
- **Tier 3 (Stretch, 1–3):** Worth reviewing if Tier 1/2 list is thin

### Step 6: Output

Before presenting results, check `~/.nimble/memory/talent-sourcing/[role-slug].md` —
if a candidate was surfaced in a prior run, mark them `(previously surfaced)` rather
than re-presenting them as new.

Present a structured candidate report:

```
## Candidate Report: [Role] in [Location]
Searched: LinkedIn, Indeed, GitHub, Wellfound
Found: [N] candidates | Tier 1: [N] | Tier 2: [N] | Tier 3: [N]

**TL;DR:** [2-3 sentence summary of the strongest candidates and any notable patterns]

---

### Tier 1 — Strong Match

#### 1. [Name] — [Score]/10
- **Current role:** [Title] at [Company]
- **Location:** [Location]
- **Skills:** [Skill1], [Skill2], [Skill3]
- **Experience:** [X years, notable employers]
- **Availability:** [signal] — [event date or "date unknown"] — [source URL]
- **Profile:** [URL]
- **Contact signals:** [email / personal site / GitHub]

...

---

### What This Means
[1-2 sentences on hiring outlook: supply/demand signal, speed recommendation, any
standout sourcing channel]
```

Omit fields where data is unavailable. Do not fabricate details — use "unknown"
for missing fields. Add a one-sentence **"Why this candidate"** note for each
Tier 1 result.

### Step 7: Save to Memory

Make all Write calls simultaneously:

- Report → `~/.nimble/memory/reports/talent-sourcing-{YYYY-MM-DD}.md` (full candidate report with all tiers)
- Per-role → `~/.nimble/memory/talent-sourcing/[role-slug].md` (candidate list; write or update)
- Profile → update `last_runs.talent-sourcing` in `~/.nimble/business-profile.json` using the python3 snippet in `references/profile-and-onboarding.md`. Skip if the file does not exist.

Update `~/.nimble/memory/talent-sourcing/index.md` with a row for this search.
Follow the wiki update pattern from `references/memory-and-distribution.md`.

### Step 8: Share & Distribute

**Always offer distribution — do not skip this step.** Follow
`references/memory-and-distribution.md` for connector detection, sharing flow, and
source links enforcement.

### Step 9: Follow-ups

Offer next steps using `AskUserQuestion`:

> **What's next?**
> - **Go deeper on a candidate** — extract full profile + find contact info
> - **Expand search** — broaden location, relax seniority, try more platforms
> - **Narrow search** — add a required skill or tighten location
> - **Export list** — save as CSV or formatted doc
> - **Done**

**Sibling skill suggestions:**

> - Run `company-deep-dive` on a candidate's current employer for deal context
> - Run `meeting-prep` before reaching out to a Tier 1 candidate

---

## Error Handling

See `references/nimble-playbook.md` for the standard error table. Skill-specific
handling:

- **Profile behind login wall:** Keep search-snippet summary; note "full profile
  unavailable — LinkedIn/Indeed login required" in the candidate entry.
- **< 5 total candidates found:** Notify the user, suggest broadening location to
  remote or relaxing seniority, then ask whether to re-run with adjusted params.
- **Search 500 on a platform:** Retry once with a simplified query; if still failing,
  skip that platform and note it in the report header.
- **GitHub agent skipped for non-technical role:** Note "GitHub not searched for
  this role type" in the report header.

Referenced files: 3

Package details

Publisher declarations from the archived package. These are separate from our research and the live service's terms.

Package author
Nimble

Package observed Sep 30, 2026.

Technical details
First seen
Sep 30, 2026 · 22:02 UTC
Last seen
Oct 1, 2026 · 18:00 UTC
Collection status
Collected

plugin_asdk_app_6a79de2b746881918127ddf42127ddc4

Download plugin data (JSON)