Local lead prospector
Curated Claude Code agent skills — local business lead prospecting and cold-email outreach (bring your own API keys).
npx -y skills add AirDoogle/claude-skills --skill local-lead-prospectorAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 21 days oldThe repository was created 21 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Find high-value local business prospects for a website-design business (with automation, chatbot, booking and analytics add-ons). Scores leads by NEED + ABILITY-TO-PAY, writes them to the Notion Prospecting Clients table, and produces a call-then-email work list. Ireland-first, token-efficient, portable to other countries. Pairs with cold-email-drafter for drafting the emails.
The file declares its own license as private. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
16.1 KB, ~4.1k tokens by cl100k_base, as published. Nobody here has run it
DM 1 — Web Client Prospecting
Finds and scores local businesses that (a) NEED a website and (b) can AFFORD one, then writes decision-ready leads to Notion for cold calling and emailing. Core offer is a website; booking, chatbot, automation and analytics are add-ons flagged only from a free signal.
Goal: a short list worth calling, each with a phone number, a ready hook, and the gap already filled in. Not volume.
Inputs
- Location (required) — town or county (e.g. "Mallow", "Galway").
- Business type (optional) — one trade to focus on. If omitted, sweep the category list below.
- Country (optional, default Ireland) — see Portability.
Ideal client
Established local SMEs with real revenue but a weak or absent web presence. High NEED × high ABILITY-TO-PAY. A busy 200-review clinic on a Facebook page beats a brand-new sole trader every time.
High-value categories (priority order): plumbers, electricians, builders, roofers, painters, solicitors, accountants, estate agents, dentists, physios, salons, barbers, beauty clinics, spas, gyms, vets, cafés, restaurants.
Token budget — tick this before every fetch
1. Discovery fetches used: __ / 2 max
2. Enrichment fetches used: __ / 6 max, and __ / 1 per lead
3. Confirm queries used: __ / 3 max
4. Add-on fetches: MUST be 0
5. CRO / registry fetches: MUST be 0 unless the user explicitly asked
Once enrichment hits 6, stop enriching and write the rest on free signals. COOL and SKIP leads get ZERO enrichment fetches.
Search pipeline
1. Build URL list. From the location, build Golden Pages URLs goldenpages.ie/<category>/<town>-<county>/. One category if a type was given, else take enough from the priority list for ~20–40 candidates. FETCH: no.
- Slug quirks: a city in its own county doubles up — Cork city is
cork-cork-county(notcork). Categories are plural nouns (barbers,solicitors) but some don't exist (gyms404s — tryfitness-centres). If a URL 404s, adjust the slug or fall back to a DuckDuckGo"<category> <town>"search; don't spend more than one retry.
2. Discovery fetch. WebFetch ONE Golden Pages results page. Parse ~20 rows: name, phone, address+eircode, category, description, star rating, review count, and whether a Website link is present. Socket closes? Run permission-bypass-apple once, retry. Do NOT fetch google.com/search, Cylex, Yell-IE, CRO or social pages. FETCH: yes (1–2). STOP: after 1–2 pages.
3. Free filter. DISCARD rows with a modern custom-domain site (Modern-skip; log as deduped). KEEP: no link (None), only facebook/instagram (Facebook-only), only a listing/booking platform (Platform-only), http/stale/builder-subdomain (Outdated/Basic). FETCH: no.
4. Free score + tier. Score every kept row (see Scoring). Assign HOT/WARM/COOL/SKIP. FETCH: no. STOP: COOL and SKIP get no enrichment — write them to Notion now.
5. Confirm (WARM+ only). Enrich each WARM/HOT lead once:
- If Google Places is set up (a key in
GOOGLE_PLACES_API_KEYor~/.claude/google_places_api_key.txt), callplaces_lookup(name, area)(see SETUP.md). One call returns rating + review count + website + phone — set Review Count from it, confirm/flip Website Status, set Scoring Confidence = high, then recompute Priority Score and Tier. This is what lets a lead reach HOT. - If no key, run one
html.duckduckgo.com/html/?q=BusinessName Town irelandquery (no quotes) to confirm the no-website guess and grab an email only if a snippet shows one. Leads stay review-blank (cap WARM). If review data would materially change the ranking, print one line and offer to open the console: "No Google Places key — enable it at https://console.cloud.google.com/apis/library/places.googleapis.com and make a key at https://console.cloud.google.com/apis/credentials (full steps in SETUP.md). Want me to open those pages?"
FETCH: yes (1 per lead). CAP: 3 confirm/enrich lookups per run — if more than 3 qualify, do the top 3 by score and set the rest to Scoring Confidence = low.
6. Promote (HOT only). Fetch a homepage ONLY when the lead is HOT AND has a real custom-domain https site AND snippets are unclear on redesign-worthiness. Extract six things: viewport tag, http-vs-https, real copyright year, services+contact/booking nav, builder fingerprint, dated-vs-current. Set Fetched=yes, then recompute Priority Score and Tier before writing. Never fetch None/Facebook-only/Platform-only leads or any WARM/COOL/SKIP lead. FETCH: yes (counts as this lead's 1 enrichment). STOP: one homepage, six items, done.
7. Add-on flags. Map Sector → Add-on Flag from the table. FETCH: no — never query for add-ons.
8. Dedupe & write. Query the DB once for existing Business Name + Phone. Dedupe on phone, then eircode, then name+town. Write every non-duplicate kept lead (including COOL) with add_prospect; omit unknown fields. Print the summary. FETCH: no (one dedupe query).
Scoring
NEED (cap 15) — from Website Status
| Website Status | Points |
|---|---|
| None (no website) | +8 |
| Facebook/Instagram-only | +7 |
| Platform-only (Fresha/Treatwell/JustEat/Yelp) | +6 |
| Outdated (http, stale ©, builder subdomain) | +4 |
| Basic (custom domain, thin/dated) | +2 |
| Confirmed no mobile viewport (homepage fetch) | +4 |
| Confirmed no lead-capture form (homepage fetch) | +2 |
| Modern-skip | disqualifier |
ABILITY-TO-PAY (cap 15) — revenue proxies
| Signal | Points |
|---|---|
| Review count 200+ | +8 |
| Review count 80–199 | +5 |
| Review count 30–79 | +2 |
| Review count under 30 (confirmed) | +0 |
| Review count BLANK | +0, set Scoring Confidence = low, cap tier at WARM |
| Rating ≥4.3 AND ≥50 reviews | +2 |
| Premium sector (dentist, clinic, solicitor, accountant, jeweller, wedding venue) | +3 |
| Higher-ticket sub-sector (wedding venue, cosmetic clinic, private healthcare) | +2 |
| Established (since 19XX / over N years / family-run; 200+ reviews implies it) | +3 |
| Multiple locations (bank only once independence confirmed; else tag "confirm independence") | +3 |
| Wealthy catchment (affluent town/eircode) | +2 |
| Paid/enhanced Golden Pages listing | +3 |
| Commercial/B2B trade descriptor | +2 |
Priority Score = NEED + ABILITY (each capped at 15; total 0–30). This is the sort key and is never blank. If a need signal is not fetch-confirmed, use the table value as-is and set Scoring Confidence = low. No half-math, no other caps.
Tiers
HOT requires BOTH:
(a) Priority Score >= 15, AND
(b) at least ONE of: Review Count >= 80 / confirmed 2+ locations / (premium sector AND established)
If (b) is false, cap at WARM. (In practice HOT = a no-website business with 80+ reviews.)
WARM = score >= 11 (also the ceiling for blank-review leads)
COOL = score >= 6 (or any real business we can't confirm — still written)
SKIP = score < 6, or a disqualifier
Disqualifiers
- HARD SKIP (always): modern mobile https site with booking/lead-capture (offer ops upsell only); dormant/closed.
- SOFT (keep as WARM/COOL, tag "confirm independence"): looks like a chain/franchise but the name is ambiguous. Only a confirmed chain is a hard SKIP.
- Sole trader with no site, no reviews, no ability signal → COOL/SKIP, never HOT on need alone.
Missing data
Never drop a lead for missing data. Blank reviews → +0, confidence low, cap WARM. Missing sector → no fetch, leave Add-on Flags empty. Missing years/staff/catchment → +0, never a penalty. Phone AND email both missing → route to Needs Contact Info view. Only true duplicates and confirmed Modern-skip sites are excluded (park SKIPs, never delete).
€ Budget (ability-to-pay only — NOT call order)
A separate select column showing likely deal size. Priority Score stays the sort key. Deterministic from Review Count + Sector:
R = Review Count (blank = MISSING). premium = Sector in {Professional, Health/Fitness} (reliably high-ticket).
1. R >= 200 -> €€€€
2. R >= 80 -> €€€€ if premium else €€€
3. R 30–79 -> €€€ if premium else €€
4. premium, few/no reviews -> €€€ (a premium practice is high-ticket even with no review data)
5. non-premium, R MISSING -> €€
6. non-premium, R < 30 -> €
Then ONE optional +1 bump (max once, never past €€€€) if Notes contains any of:
"multi-location","2 locations","3 locations","branches","since 19","since 20","established","family-run".
Meaning: € starter/price-sensitive · €€ standard 4–6 page build · €€€ multi-page + add-ons, tailored proposal · €€€€ premium/high-ticket, top package. (Beauty/Wellness is NOT auto-premium here — a barber and an aesthetic clinic differ; reviews lift them.)
Golden Pages carries NO review counts (every listing shows "0 stars"). So Review Count is usually BLANK from discovery, ability leans on premium sector + established/paid-listing markers, and most Golden Pages leads cap at WARM. The confirm query (step 5, WARM+ only) is where a real Google review count/rating can be grabbed. To get reliable review data (and let leads reach HOT), plug in the Google Places API — one-time, optional, see
SETUP.md.
Website Status → pitch (short, sayable on a call)
| Status | Say |
|---|---|
| None | "You've no site yet, so one page turns your reviews into booked jobs." |
| Facebook-only | "You don't own your Facebook audience. Your own site keeps those customers with you." |
| Platform-only | "The platform owns your bookings and takes a cut. Your own site keeps the repeat business." |
| Outdated | "Your site is dated and losing you mobile visitors. A refresh sorts that." |
| Basic | "Your site isn't pulling its weight on mobile or booking. I can make it book jobs." |
| Modern-skip | Not a web lead. Automation/analytics upsell only. |
Add-on flags (Sector → flag, no fetch, zero points)
| Sector | Add-on |
|---|---|
| Beauty/Wellness, Health/Fitness, Hospitality/Food | Booking |
| Professional | Chatbot/Lead-Capture |
| Trades | Automation |
| Retail | Analytics |
Best time to call (by sector — caller guidance)
Trades: early (8–9am) or after 5pm. Beauty/Health: mid-morning Tue–Thu. Professional: 9–11am Tue–Thu. Retail: mid-morning/early afternoon. Hospitality: 2–4pm.
Hook
Write one sayable line from the pitch table, add the owner's first name if it's free, prefix with » . One line only.
Notion integration
Credentials: the Notion token + database ID are not stored here. prospect.py loads them from NOTION_TOKEN / NOTION_DB_ID env vars or ~/.claude/prospecting_config.json. First-time users run the one-time setup in SETUP.md. Never hardcode a token in this folder.
Node.js is EPERM-blocked in the Apple sandbox. Use Python that writes each payload to a temp file and runs
curl -d @file(also handles apostrophes). Never inline JSON in a bash string.
Auto columns: Business Name (title) · Priority Score (number) · Euro Budget (select €/€€/€€€/€€€€) · Tier (select) · Scoring Confidence (select) · Sector (select) · Area (select) · Phone · Email · Website · Website Status (select) · Add-on Flags (multi-select) · Hook · Source Link (url, mandatory) · Decision-maker Name · Review Count (number) · Reviews · Notes · Status (always "Not Contacted") · Fetched (checkbox).
Manual columns: Stitch Link (Google Stitch prototype URL, left blank by the script) · Last Contacted · Call Notes.
Filled by DM_2: Draft Email (a click-to-open Gmail compose link) — run DM_2's refresh_draft_links.py after a prospecting run to populate it for leads that have an email.
Stored helpers — import, don't re-type
All scoring, € budget, hooks, dedup, Places enrichment and the Notion write live in
prospect.py (in this skill folder). Import it — never paste this logic into a run:
import sys, os; sys.path.insert(0, os.path.expanduser("~/.claude/skills/local-lead-prospector"))
from prospect import add_prospect, existing_keys, places_lookup
names, phones = existing_keys() # dedup once per run
# For each business you observed, pass the FACTS — add_prospect scores, tiers,
# picks the € band, writes the hook, and creates the Notion page itself:
ok, meta = add_prospect(
name="Example Dental Practice", sector="Health/Fitness", area="Yourtown",
website_status="None", source_link="https://www.goldenpages.ie/dentists/<town>-<county>-county/",
phone="021 555 0100", review_count=None, rating=None,
established=False, multi_loc=False, commercial=False,
notes="No website — dental practice") # -> meta = {score, tier, euro, confidence}
add_prospect args: name, sector, area, website_status, source_link (required) then
optional phone, email, website, review_count, rating, reviews_text, dm_name, notes, established, multi_loc, commercial, addons, tier_override. Skip a category by matching
names/phones from existing_keys().
Enrich with real Google reviews (needs Places key — see SETUP.md)
After writing leads, pull live ratings/review counts so leads can reach HOT:
python3 ~/.claude/skills/local-lead-prospector/enrich.py # Places lookup + re-score
python3 ~/.claude/skills/local-lead-prospector/enrich.py --retier # re-score from stored data only (no API calls)
enrich.py guards against wrong-business matches (skips rows whose Places phone differs).
Dedupe first with one /v1/databases/{DB}/query (body {}), collect existing Business Name + Phone, skip case-insensitive matches. New Area towns auto-create as options on first write.
Views to set up in Notion (one-time, manual — the API cannot create views)
- Call List (daily driver) — filter
Status = Not Contacted AND Phone is not empty AND Tier ≠ SKIP; sortPriority Score ↓, Review Count ↓; group byArea; show Business Name, Phone, Euro Budget, Hook, Notes, Tier. - Needs Contact Info — filter
Phone is empty AND Email is empty AND Tier ≠ SKIP; sortPriority Score ↓. - Cold Email — filter
Email is not empty AND Phone is empty AND Tier ≠ SKIP; sortPriority Score ↓; show Business Name, Email, Euro Budget, Hook, Stitch Link. - Call Follow-up — filter
Status is Contacted OR Interested OR Meeting; sortLast Contacted ↑; show Business Name, Phone, Email, Last Contacted, Call Notes, Stitch Link. - Pipeline — board grouped by
Status; sortLast Contacted ↓, Priority Score ↓.
Output (after writing)
Print a table sorted by Priority Score: # · Business · Sector · Tier · € · Score · Website Status · Phone · Reviews · Hook. End with ✓ X leads added (H HOT / W WARM / C COOL), Y skipped (duplicates), Z parked (SKIP). Fetches: D discovery + E enrichment.
Next step
Pick a lead and run cold-email-drafter to draft the email and (optionally) create a Gmail draft.
Portability (non-Ireland)
Default is Ireland. For another country, swap the discovery source, keep everything else:
- UK: Yell.com category+town pages; Companies House for established-status.
- Anywhere: Google Maps/Places (name, phone, website-present flag, review count, rating in one view) — heavier per fetch, so lean harder on the free filter. Review-count bands and the affluent-area list are Ireland-tuned; adjust per market.
What not to do
- Don't chase volume — a short scored list beats a long dump.
- Don't exceed the fetch budget — write the rest on free signals.
- Don't deep-scan sites, hunt emails with dedicated queries, or run CRO by default.
- Don't discard a lead for missing data — park SKIPs, never delete.
Gives 0 of the 12 instructions most sales crm skills give in ~4.1k tokens
Counted across 361 of the 361 authors here whose files we hold, read 2026-08-06
- Read product marketing context before writing if it existsin 22 of 361, across 14 files
- keep the ask low-frictionin 16 of 361, across 7 files
- Call RUBE_SEARCH_TOOLS firstin 15 of 361, across 5 files
- personalize every outbound messagein 13 of 361, across 4 files
- confirm connection status is activein 13 of 361, across 4 files
- Keep forwardable blurbs under 100 wordsin 13 of 361, across 4 files
- State if personalization context is missingin 13 of 361, across 4 files
- Cut any sentence that does not drive a replyin 13 of 361, across 4 files
- Use proof instead of adjectivesin 12 of 361, across 3 files
- Use a single, low-friction call to actionin 12 of 361, across 4 files
- Calibrate tone to the specific audiencein 12 of 361, across 3 files
- Make each follow-up email add new valuein 12 of 361, across 6 files
Said here and by no other author read
- obtain a location before starting
- build golden pages url lists without fetching
- fetch one golden pages results page for discovery
- stop discovery fetches after two pages
- discard rows with modern custom-domain websites
- score every kept row and assign a tier
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.