| name | email-finder-batch |
| description | Find email addresses using OSINT techniques with a subagent-driven architecture optimized for context window management. Use this skill whenever the user asks to find, look up, discover, or locate someone's email address, contact info, or how to reach someone — especially for batch requests with multiple people. Trigger on phrases like 'get me their email', 'find contact details for', 'how do I email X', 'what's their email', 'find emails for these people', 'find emails for this list', or any variation of finding a person's email from publicly available information. This skill dispatches parallel subagents for web search, website crawling, GitHub inspection, social profile scanning, and email pattern guessing — each in isolated context windows — then synthesizes results. Preferred over email-finder for multi-person lookups or when context budget is limited. |
| user-invocable | true |
| argument-hint | [person-name or list of names] |
Email Finder Skill
Find email addresses by systematically searching publicly available sources. This skill uses a subagent-driven architecture — the main agent acts as a thin orchestrator while specialized subagents do the heavy searching, crawling, and verification in their own isolated context windows.
Why Subagents?
Email finding is context-expensive. Each web fetch, search result, and page crawl can consume thousands of tokens of raw HTML and snippets. Running this in the main context window degrades quality fast — especially for batch requests with multiple people. Subagents solve this:
- Each subagent works in its own fresh 200K context window
- Only structured summaries return to the main agent (~200 tokens per subagent vs ~10,000+ tokens of raw results)
- Multiple people and multiple sources can be searched in parallel
- If one source blocks or errors, it doesn't pollute the main context
Architecture Overview
Main Agent (Orchestrator)
│
├─ Phase 1: Context Gathering (inline, ~100 tokens)
│ └─ Clarify names, orgs, known handles
│
├─ Phase 2: Per-Person Dispatch
│ │
│ │ ┌─ Person A ─────────────────────────────┐
│ ├──│ Recon Scout → Target List │
│ │ │ ├─ Website Crawler (parallel) │
│ │ │ ├─ GitHub Inspector (parallel) │
│ │ │ ├─ Social Scanner (parallel) │
│ │ │ └─ Academic/Conf Scanner (if relevant) │
│ │ │ Gap Filler (sequential, if needed) │
│ │ │ Pattern Guesser (if no confirmed hit) │
│ │ └─────────────────────────────────────────┘
│ │
│ │ ┌─ Person B (same structure, parallel) ───┐
│ ├──│ ... │
│ │ └─────────────────────────────────────────┘
│ │
│ └─ ... (one pipeline per person)
│
├─ Phase 3: Verification (optional subagent)
│ └─ Cross-reference found emails against web
│
└─ Phase 4: Synthesis (inline)
└─ Deduplicate, rank, format output
Important Notes
- This skill comprehensively finds all email addresses from all sources.
- Always inform the user of the sources where emails were found.
- If no email is found after exhausting all steps, say so clearly and suggest alternatives.
- For batch requests (multiple people), dispatch a separate per-person pipeline for each. They run in parallel.
Phase 1: Context Gathering (Main Agent, Inline)
Before dispatching any subagents, collect what you know. This is lightweight and stays in the main context.
For each person, gather:
- Full name (first, last, variations)
- Organization/company (current or recent)
- Known handles (Twitter, GitHub, LinkedIn URL)
- Domain (personal website, company domain)
- Role/title (helps disambiguate common names)
If the user hasn't provided enough, ask. At minimum you need a name.
Batch detection: If the user provides multiple names (a list, comma-separated, pasted from a spreadsheet, conference speaker list, etc.), parse each person into a separate entry. Each person gets their own independent pipeline in Phase 2.
Phase 2: Per-Person Pipeline
For each person, run the following pipeline. If there are multiple people, dispatch their pipelines in parallel — each person's pipeline is fully independent.
Step 2a: Recon Scout Subagent
Dispatch a single subagent to run broad search queries and return a structured target list.
Subagent prompt: Read ./prompts/recon-scout.md and pass it:
- The person's name, org, role, and any known handles/domains
- Instructions to use all available search tools in parallel (web_search, exa, brave-search)
Expected return format: A structured JSON summary (see prompt file for schema). This should be ~200-400 tokens, not raw search results.
Tool access: web_search, exa (if available), brave-search (if available), web_fetch (for snippets only)
Step 2b: Parallel Deep Crawl Subagents
Based on the recon scout's target list, dispatch up to 4 subagents in parallel, each scoped to one source type:
| Subagent | Trigger | Prompt File | Tools |
|---|
| Website Crawler | Personal site URL found | ./prompts/website-crawler.md | web_fetch, Playwright/Chrome DevTools (if available) |
| GitHub Inspector | GitHub username found | ./prompts/github-inspector.md | web_fetch, Bash (for API calls) |
| Social Scanner | Twitter handle or LinkedIn URL found | ./prompts/social-scanner.md | web_fetch |
| Academic Scanner | Person appears to be researcher/academic | ./prompts/academic-scanner.md | web_fetch |
Only dispatch subagents for sources that exist in the recon results. If there's no GitHub profile, don't dispatch the GitHub Inspector.
Each subagent returns a structured JSON summary — emails found, sources checked, confidence level, and any new URLs discovered that weren't in the original recon.
Step 2c: Gap Filler (Sequential, Conditional)
After all parallel subagents return, review their summaries. If any subagent discovered new URLs (e.g., a Linktree from a Twitter bio, a personal site linked from GitHub), dispatch a follow-up Website Crawler subagent for those specific URLs.
This step is sequential because it depends on Phase 2b results. Skip if no new URLs were discovered.
Step 2d: Pattern Guesser (Conditional)
If no confirmed email was found but a company domain is known, dispatch a pattern-guessing subagent.
Subagent prompt: Read ./prompts/pattern-guesser.md and pass it:
- Person's first name, last name
- Company domain
- Any other emails found at the same company (from recon or crawl results)
Tool access: web_search (to check if guessed patterns appear anywhere online)
Skip if a confirmed email was already found.
Phase 3: Verification Subagent (Optional)
If you found emails and want higher confidence, dispatch a verification subagent that:
- Searches each found email address in quotes to see if it appears on other pages
- Checks if the email domain's MX records exist (via
dig or nslookup in Bash)
- Cross-references against other known emails at the same company for pattern consistency
Subagent prompt: Read ./prompts/verifier.md
Tool access: web_search, Bash
This step is optional. Use it when:
- The email was found in only one source
- The email came from an old/archived page
- The user specifically asked for verified results
Phase 4: Synthesis (Main Agent, Inline)
At this point, the main agent has received structured JSON summaries from all subagents — typically 200-500 tokens per person. The main context is clean.
For each person:
- Deduplicate — merge emails found by multiple subagents
- Rank by confidence — confirmed (found on live page) > snippet (found in search snippet) > pattern guess (unverified)
- Note staleness — flag emails found only on archived/old pages
- Format output using the template below
Output Format
For a single person:
## Email Finder Results: {Person's Name}
**Found emails:**
- ✅ john@example.com — Found on personal website (example.com/contact)
- ✅ john.smith@company.com — Found in GitHub commit history
**Possible emails (unverified):**
- 🔄 j.smith@company.com — Pattern guess based on company domain
**Sources checked:**
- ✅ Personal website: example.com (homepage, /contact, /about)
- ✅ GitHub: github.com/johnsmith (email in commits)
- ✅ Twitter: @johnsmith (no email, links to personal site)
- ✅ LinkedIn: linkedin.com/in/johnsmith (blocked, used search snippets)
- ❌ No academic/conference pages found
**Alternative contact methods:**
- Contact form: example.com/contact
- Twitter DM: @johnsmith
- LinkedIn message
For batch requests (multiple people), present results per person in the same format, with a summary table at the top:
## Batch Email Finder Results
| Person | Email Found | Confidence | Source |
|---|---|---|---|
| John Smith | john@example.com | ✅ Confirmed | Personal website |
| Jane Doe | j.doe@company.com | 🔄 Pattern guess | Company domain |
| Bob Lee | — | ❌ Not found | — |
---
[Detailed results per person below]
Subagent Dispatch Guidelines
When dispatching subagents, follow these principles:
-
Structured returns only. Every subagent prompt ends with an explicit instruction to return JSON, not raw content. The prompt files enforce this.
-
Minimal tool access. Crawlers get web_fetch. GitHub inspector gets web_fetch + Bash. No subagent needs Write or Edit tools.
-
Fail gracefully. If a subagent encounters a 403, timeout, or block, it should note the failure in its return JSON and move on. The main agent treats missing data as "source checked, nothing found" — not as an error.
-
No nesting. Subagents do not dispatch further subagents. If a crawler finds a new URL, it returns that URL in its summary and the main agent handles it in Phase 2c.
-
Context isolation. Never pass the full conversation history to a subagent. Pass only the specific inputs it needs (name, URL, handle).
-
Parallelism budget. For a single person, dispatch up to 4 subagents in parallel. For batch requests, dispatch up to 3-4 person pipelines in parallel (each with their own internal parallelism). This prevents overwhelming rate limits.
Tips for Difficult Cases
- Common names: Add company, location, or role to disambiguate in recon queries
- Obfuscated emails: Crawler subagent checks for
[at]/[dot] patterns, HTML entities, and JS-rendered content
- Multiple emails found: Present all with source attribution, note which seems most current
- Dead links: Recon scout tries Wayback Machine (
web.archive.org) for archived versions
- Non-English sites: Email regex works universally, but recon should include local-language queries
Error Handling
- If a URL times out or returns 403 → subagent notes it in return JSON, main agent marks source as "checked, blocked"
- If LinkedIn blocks access → social scanner falls back to search engine snippets
- If no personal website exists → skip website crawler, proceed with social + GitHub
- If all sources exhausted with no result → report clearly, suggest alternatives
- If a subagent hangs or returns malformed output → main agent skips it and proceeds with other results