Install
$ agentstack add skill-moonlight-lupin-agent-skills-people-enrichment ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
People Enrichment & Search (People Data Labs)
One script, scripts/enrich.py, with five subcommands over PDL's Person and Company datasets. All share the same API-key handling, the boolean-PII contact logic, status flags, .xlsx styling, and a --dry-run preflight mode.
| Subcommand | Input | Output | Billing | | --- | --- | --- | --- | | person-enrich | list of named people | profile + work history | 1 credit / match | | person-identify | list of people (ambiguous) | several scored candidates each | per PDL identify terms | | person-search | criteria flags | a list of matching people | 1 credit / record returned | | company-enrich | list of companies | firmographics | 1 credit / match | | company-search | criteria flags | a list of matching companies | 1 credit / record returned |
The two search commands return data per result, so cost scales with --size. Always state the rough cost before a large search and keep --size modest unless the user asks for more (max 100 per request).
Scope and routing
Use this skill when the user wants PDL person/company enrichment or search for a legitimate, proportionate purpose. Do not use it to scrape LinkedIn directly, to compile an intrusive profile, or where a host environment mandates a different data provider.
Why People Data Labs, not a LinkedIn scraper
Scraping LinkedIn is fragile and legally risky. This skill uses PDL as a licensed aggregator rather than live LinkedIn scraping. If the user insists on live scraping, explain the trade-off rather than building a scraper.
The API key
scripts/enrich.py needs a PDL key, resolved at runtime in this order: PDL_API_KEY env var → .env in the cwd → .env next to the script. The key must never be written into this skill or any script. If absent, live commands exit with instructions. --dry-run and --self-test do not require a key.
Create the .env only locally:
echo 'PDL_API_KEY=their_key_here' > .env
Treat .env as a secret: don't print, commit, or upload it. --self-test runs offline (canned data, no key) and writes sample people + company sheets so the user can see the output format before spending credits.
Dry-run preflight
Before spending credits, especially on larger files or searches, run the same command with --dry-run:
python scripts/enrich.py person-enrich --input people.csv --output out.xlsx --dry-run
python scripts/enrich.py person-search --company "Northwind Capital" --title director --size 25 --dry-run
python scripts/enrich.py company-search --industry "real estate" --country singapore --size 50 --dry-run
Dry-run validates the input/search, prints the planned output, and estimates maximum credits without requiring PDL_API_KEY, calling PDL, or writing .xlsx output.
Contact fields and the free plan
On the free plan, PDL returns PII fields (emails, phone_numbers) as a boolean, not the value: true = a contact exists but is paywalled, false = none on file. The people sheets surface this in Email status / Phone status columns: included (real value present, Pro plans), exists - upgrade to view (paywalled but present), none on file, or unknown.
The five commands
person-enrich — one-to-one match named people.
python scripts/enrich.py person-enrich --input people.csv --output out.xlsx
Recognised input columns (case/space-insensitive): name/full name, first name, last name, company/employer, title, location, email, linkedin/profile. More context per row = higher match rate.
person-identify — when a single enrich is ambiguous, get the candidate set.
python scripts/enrich.py person-identify --input people.csv --max-candidates 5
Same input as enrich. Output has multiple rows per input person, each a scored candidate (Match score column), so the user can pick the right one.
person-search — find people by criteria, no name list needed.
python scripts/enrich.py person-search --company "Northwind Capital" --title director --size 25
Flags: --company --title --location --country --industry --name, plus --size (1–100), --dataset (default all), and --sql to pass a raw PDL SQL query for full control. Flags are combined with AND.
company-enrich — match a list of companies to firmographics.
python scripts/enrich.py company-enrich --input companies.csv --output firms.xlsx
Recognised input columns: name/company, website/domain, ticker, linkedin/profile, location/country/region/locality. Needs at least one of name/website/ticker/profile per row.
company-search — find companies by criteria.
python scripts/enrich.py company-search --industry "real estate" --country singapore --min-employees 50 --size 50
Flags: --name --industry --country --locality --tag --min-employees, plus --size and --sql.
Reviewing output with the user
People sheets have two tabs: People (one row per person, status colour-coded) and Employment history (one row per past role). Company sheets have a single Companies tab. The Status column is the first thing to check: matched (green), needs_review (amber), no_match (red), error (orange). Call out the amber/red rows explicitly so the user knows what to double-check.
Tuning matches
--min-likelihood N (1–10) is the confidence floor for the enrich/identify commands. Raise it (6–8) when names/companies are common and you'd rather miss than mis-match; lower it when you have rich context and want coverage. Company enrichment is most reliable with a website/domain; person enrichment with a company or email alongside the name.
Swapping providers later
The PDL-specific pieces are the *_params builders, pdl_request, and the parse_person / parse_company mappers. To support another provider, implement those against its API and keep the same record dict shape; input parsing, status logic, SQL/flag handling, dry-run summaries, and .xlsx writing stay as is.
Principles
- Drafts, not advice — output is a research aid for a person to review, not a determination.
- Never invent — surface only what PDL returns; mark
no_match/needs_reviewhonestly rather than guessing an identity. - Deterministic where it counts — input parsing, status logic, dry-run estimates and
.xlsxwriting are deterministic. - Honesty and calibration — flag low-confidence matches and show the rough credit cost up front.
- Workspace hygiene — write outputs where the user expects; never write the API key to disk in the skill.
Data handling
PDL is a third party and enrichment sends the real name/company to it — it cannot be tokenised, because the name is the lookup. Treat the .env / PDL_API_KEY as a secret: never print, commit or upload it. For people, confirm a legitimate and proportionate purpose and collect only fields relevant to that purpose.
Pitfalls
- Free plan hides contact values — emails/phones come back as booleans; read the status columns.
- Search cost scales with
--size— quote the rough credit cost and keep--sizemodest. - Common names mis-match — raise
--min-likelihoodor add context columns (company/email). - Never commit
.env— the key is a secret;--self-testand--dry-runneed no key. - Dry-run is an estimate — actual billing follows PDL's endpoint terms and returned records/matches.
Verification checklist
- [ ] Legitimate, proportionate purpose confirmed for any person enrichment/search.
- [ ]
--dry-runused for large files or searches to validate scope and estimate credits. - [ ]
PDL_API_KEYavailable via env or.envfor live runs (never written into the skill). - [ ] Rough credit cost stated before any large search.
- [ ] Amber/red status rows flagged to the user.
- [ ]
.envnot printed/committed.
Requirements
- Python 3.8+
pip install openpyxl(HTTP uses the stdliburllib— norequestsneeded)PDL_API_KEY(env var or.env) for live API calls; get one at https://www.peopledatalabs.com- Network access to the PDL API.
--self-testand--dry-runrun fully offline.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: moonlight-lupin
- Source: moonlight-lupin/agent-skills
- License: MIT
- Homepage: https://hermes-agent.nousresearch.com
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.