Lead Magnets
sickn33/agentic-awesome-skills
Plan and optimize lead magnets for email capture and lead generation.
Agent skill
by growthenginenowoslawski in growthenginenowoslawski/coldoutboundskills
Turns the raw first-name field on a lead row into the name a person would actually be greeted by, so it can open an email.
$ npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-first-name-cleaning -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install growthenginenowoslawski/coldoutboundskills playbook-first-name-cleaning --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/growthenginenowoslawski/coldoutboundskills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/playbooks/playbook-first-name-cleaning .claude/skills/playbook-first-name-cleaning && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "playbook-first-name-cleaning" agent skill from https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-first-name-cleaning into .claude/skills/playbook-first-name-cleaning/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playbook-first-name-cleaning", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-first-name-cleaningType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-first-name-cleaning -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install growthenginenowoslawski/coldoutboundskills playbook-first-name-cleaning --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/growthenginenowoslawski/coldoutboundskills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/playbooks/playbook-first-name-cleaning .agents/skills/playbook-first-name-cleaning && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "playbook-first-name-cleaning" agent skill from https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-first-name-cleaning into .agents/skills/playbook-first-name-cleaning/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playbook-first-name-cleaning", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-first-name-cleaning -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install growthenginenowoslawski/coldoutboundskills playbook-first-name-cleaning --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/growthenginenowoslawski/coldoutboundskills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/playbooks/playbook-first-name-cleaning .cursor/skills/playbook-first-name-cleaning && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "playbook-first-name-cleaning" agent skill from https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-first-name-cleaning into .cursor/skills/playbook-first-name-cleaning/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playbook-first-name-cleaning", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/growthenginenowoslawski/coldoutboundskills.git --path skills/playbooks/playbook-first-name-cleaning--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-first-name-cleaning -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install growthenginenowoslawski/coldoutboundskills playbook-first-name-cleaning --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/growthenginenowoslawski/coldoutboundskills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/playbooks/playbook-first-name-cleaning .gemini/skills/playbook-first-name-cleaning && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "playbook-first-name-cleaning" agent skill from https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-first-name-cleaning into .gemini/skills/playbook-first-name-cleaning/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playbook-first-name-cleaning", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install growthenginenowoslawski/coldoutboundskills playbook-first-name-cleaningInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-first-name-cleaning -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/growthenginenowoslawski/coldoutboundskills.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/playbooks/playbook-first-name-cleaning .github/skills/playbook-first-name-cleaning && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "playbook-first-name-cleaning" agent skill from https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-first-name-cleaning into .github/skills/playbook-first-name-cleaning/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playbook-first-name-cleaning", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-first-name-cleaning -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install growthenginenowoslawski/coldoutboundskills playbook-first-name-cleaning --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/growthenginenowoslawski/coldoutboundskills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/playbooks/playbook-first-name-cleaning .opencode/skills/playbook-first-name-cleaning && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "playbook-first-name-cleaning" agent skill from https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-first-name-cleaning into .opencode/skills/playbook-first-name-cleaning/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playbook-first-name-cleaning", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
playbook-first-name-cleaningTurns the raw first-name field on a lead row into the name a person would actually be greeted by, so it can open an email.
Playbook First Name Cleaning is an agent skill from growthenginenowoslawski/coldoutboundskills. Turns the raw first-name field on a lead row into the name a person would actually be greeted by, so it can open an email. Triggers on "clean these first names", "first name variable", "the greeting says Hi DR MATTHEW", "strip the titles off the names", "some rows have the whole name in the first-name column", "normalize first names for the campaign". Outputs firstnameclean, one short string per row.
Its SKILL.md is about 7.1k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files (for example `clay-table.md` and `clay-workflow.md`).
The repository describes itself as: Open-source Claude Code skills for cold email and outbound sales. Grade campaigns, export Prospeo searches, scrape Google Maps — all from Claude Code. The licence is MIT.
7 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 25c5d85. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md.
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Playbook First Name Cleaning loads about 7.1k tokens when it runs. Until then it costs about 109 tokens; SKILL.md has 2,844 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from growthenginenowoslawski/coldoutboundskills at commit 25c5d85, republished under its MIT licence (© growthenginenowoslawski). 2,844 words, ~7,065 tokens.
.claude/skills/playbook-first-name-cleaning/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.All rules here are best practice, not law. Override any of them when the campaign calls for it; note the best practice once and move on.
Use when: any campaign whose email opens with the prospect's first name, which is every campaign anyone runs. Run it next to the company-name clean, before any other custom variable, because variables often interpolate the cleaned name.
Do not use when: you need the company name cleaned — that is
playbook-company-name-cleaning, its twin, built to the same shape on purpose. Also not for
finding a missing name. This playbook never looks anything up; a blank stays blank.
One-line output: first_name_clean = "Ruba" from the raw string "Dr Ruba", so the email opens
Hi Ruba, instead of Hi Dr Ruba,.
The first name is the first word of the email and the single most visible tell that a message was mail-merged. Lead databases store it the way a scraper found it, which is not the way anyone wants to be greeted. Real strings from a live contacts table:
Dr Matthew · PAUL · javonne · 👋 James · ★ Marc · Kathryn (Katie) · Robert wilkie
(last name Wilkie) · Araceli'S · Dr Sean Li We Are Actively Hiring At Antai Global · AAA (at
company AAA Upholstery) · Admin · O. · Gowinder with a trailing space · and rows where the
field is simply NULL.
Every one of those, pasted into Hi {{first_name}},, either looks broken or is not a person.
This playbook takes that string, plus the row's last name and company for context, and returns the short spoken form. It strips honorifics, credential suffixes, emoji and decoration, appended job titles and hiring notices, and possessive artifacts. It fixes shouting and all-lowercase. It picks the nickname when someone wrote one in parentheses. It refuses to mangle hyphenated and apostrophe names.
It explicitly does not: look the person up, translate or transliterate, expand an initial into a guessed name, split a run-together name into two words, or invent a letter not already in the input.
Why the last name and company are inputs. They are free, and they carry the only information
that settles the hardest case. ATC alone is undecidable. ATC with last name Systems at company
ATC is obviously not a person.
| Field | Type | Required? |
|---|---|---|
first_name_raw | string | yes |
last_name_raw | string | no, but free — it catches the duplicated-full-name and company-in-person-column cases |
company_name_raw | string | no, but free — the only reliable discriminator for short ALL-CAPS tokens |
Pass the RAW strings, not the cleaned company name. The model needs to see the mess.
| Field | Type | Example | Max | Null? |
|---|---|---|---|---|
first_name_clean | string | Ruba | 20 target, 40 hard | no, "" instead |
changed | boolean | true | n/a | no |
confidence | enum | high / low | n/a | no |
needs_review | boolean, computed by the guards, not the model | true | n/a | no |
Abstain value: "". Never N/A, never null as text, never there, never friend, never a
guess. This is not negotiable: N/A renders into a live email as Hi N/A,.
Two numbers, because the benchmark is deliberately adversarial and the production rate is not the same thing.
Hi {{first_name_clean}},, Hey {{first_name_clean}} -, and mid-sentence use.DeAndrea, McCurry) and non-Latin scripts,
which stay exactly as written.Exclude and review. A generic greeting is never the fallback.
| Condition | What happens |
|---|---|
first_name_clean is empty | EXCLUDE the row, route to review |
needs_review is true (any guard fired) | EXCLUDE the row, route to review |
the name is written only in a non-Latin script (珊, Дарья, عبدالله, 준식) | EXCLUDE from an English-language campaign, route to review |
| any of the above, and someone suggests a generic greeting | No. Never there, friend, team, folks, or the company name. Not by substitution, not by spintax |
The non-Latin-script rule is an exclusion, not an abstain and not a transliteration. The pipeline
keeps the name exactly as written and sets needs_review. Transliterating would invent letters;
blanking would destroy a real person's real name. About 6 of the 100 benchmark rows land here.
Routing them to a native-language campaign is a legitimate operator move, and the preserved name
is what makes it possible.
Why exclusion rather than a generic greeting: an empty or unusable first name usually means the row is not a person at all, which makes the title and the email suspect too. A generic greeting does not rescue that row, it just sends a worse email to a worse address.
The "source" is the string you already have. No vendor to call.
| # | Source | Cost | Hit rate | Stop rule |
|---|---|---|---|---|
| 1 | Deterministic strip (regex) | FREE | ungraded — do not quote a number for it | always advance. Step 1 cannot judge whether a string is a person, which is the case that actually ships a broken email |
| 2 | The locked prompt in §6, plus the six deterministic guards | CHEAP | 96/100 model-only, 0 silent failures with guards | the recommended stopping point |
| 3 | Recover the name from the email local part, then re-run step 2 | FREE | untested | opt-in only. info@, sales@ and jsmith@ all produce garbage, and a wrong first name is worse than no email |
Steps 1 and 2 are alternatives, not a waterfall. At $0.21 per 1,000 rows there is no reason to gate step 2 behind step 1.
Ruba is a real given name; you need the string that goes after Hi.TVK, KSM),
which a bigger model cannot solve either. They need a flag and a human.VERDICT: PASS 96/100 | gpt-4o-mini, locked prompt v2, JSON response format,
max_completion_tokens=200 | p50 0.95s/row | ~$0.21/1k.
Four open gaps, read before you quote the number:
clay-table.md has not
been executed.Measured variants on the identical 100 rows:
| Path | Usable | Silent failures | p50 | Cost / 1k warm |
|---|---|---|---|---|
gpt-4o-mini, prompt v2 | 96/100 (69/73 held out) | 0 | 0.95s | $0.21 |
| nano-class, default reasoning, v2 | 97/100 | 0 | 4.80s | $0.28 (~$0.14 flex) |
gpt-4o-mini, prompt v1 | 96/100 | 3 | 1.05s | $0.21 |
gpt-4o-mini + guards G1-G6 | 76 ship / 24 withheld | 0 | 0.95s | $0.21 |
| Abstain probe, both models | 10/10 | 0 | n/a | n/a |
Note row 3: v1 and v2 score identically on accuracy and differ only in silent failures. The whole value of v2 is that its errors announce themselves.
clay-table.md — the column build with the guards as a formula.clay-workflow.md — the CLI-buildable version.Model: gpt-4o-mini inside Clay ($0.21/1k vs $0.28 for a nano-class model at 96/100 vs 97/100).
Note the margin is thinner than it looks: on a cold cache the two are within 1.2%, so a column
running tiny batches that never warm the cache should redo the arithmetic. A nano-class model
outside Clay, flex tier for batch (~$0.14/1k).
Params: max_completion_tokens=200 for mini, 2000 for nano, never temperature,
response_format={"type":"json_object"}.
Cache structure: everything below is the static prefix, first, byte-identical. Measured 2,436 prompt tokens, of which 2,212 came back cached on mini after the first call.
This prompt looks too long. It was measured rather than argued about. A 13-line candidate (7 rules, no few-shot pairs) ran head to head against the prompt below on 30 fresh real rows, same model, same params:
| prompt v2 (below) | short candidate | |
|---|---|---|
| Prompt tokens | 2,434 | 435 |
| Cached tokens once warm | 1,920 | 0 (under the 1,024 cache floor) |
| Cost per 1,000 rows, warm | $0.047 | $0.0325 |
| Ship-ready rows | 29/30 | 27/30 |
| Broken sends | 0 | 3 (10%) |
The whole saving is $0.0145 per 1,000 rows, or $1.45 per 100,000. The three broken sends it
buys are Hi ktitor, (a lowercase handle that is actually the company name), Hi Kathryn Katy,
and Hi Ye Cynthia Xi Cpa,.
Two things make this conclusive rather than a close call:
ktitor is not in the blocklist;
Kathryn Katy and Ye Cynthia Xi Cpa both pass G6 because every letter really is in the raw
field. G6 catches invention, not failure to strip. The failures a short prompt produces are
plausible strings, and plausible strings are the one thing a deterministic guard cannot see.The two prompts agreed exactly on all 12 ordinary rows, all 4 honorifics, both lowercase rows, all 4 non-Latin rows and both multi-word rows. A short prompt is fine on everything that is easy, which is why this looks safe until you measure it.
You clean the first-name field on a sales lead so it can be dropped straight into the greeting line of a cold email.
You will be given the raw first-name string from a CRM, plus that row's last-name string and company name for context. Return the single name this person would be greeted by in a friendly business email.
Return JSON only, exactly these keys:
{"first_name_clean": "...", "changed": true, "confidence": "high"}
Rules:
1. Drop honorifics and titles at the front: Dr, Dr., Mr, Mrs, Ms, Miss, Prof, Professor, Rev, Fr, Capt, Sir, Dame, Lord, Sr, Sra, Hr, Ing, Eng, Adv.
2. Drop credential and qualification suffixes wherever they appear: MD, DO, DDS, DMD, RN, NP, PA-C, PhD, Ph.D, EdD, JD, Esq, Esquire, CPA, CFA, CFP, MBA, MSc, MA, BSc, PE, PMP, CISSP, CSM, MCIPS, FCA, ACCA, and the punctuation attached to them.
3. Drop emoji, stars, arrows, bullets, check marks, crowns, and any other decoration: the leading and trailing ornaments people add to a LinkedIn name. Keep only the letters of the name.
4. Fix shouting and whispering, and do it LAST, after you have picked the name out of the field. An ALL-CAPS ordinary name becomes Title Case, so PAUL becomes Paul and SUSHMA becomes Sushma. An all-lowercase ordinary name becomes Title Case, so javonne becomes Javonne and alan becomes Alan. The name you return is always Title Case unless rule 5 or rule 12 says otherwise.
5a. A trailing 'S or 's on the first-name field is a possessive artifact from a business listing, not part of the name. Drop it. Rosa'S becomes Rosa. This applies only at the very end of the field, never to an apostrophe inside the name.
5b. Keep deliberate internal capitals and punctuation that belong to the name: DeAndrea, McCurry, O'Brien, D'Anza, T'Kia, Jean-Paul, Yi-Hsuan, Anne-Maud. Never remove a hyphen and never remove an apostrophe from inside a name, and never split a hyphenated name into one half.
6. When a nickname or short form follows the name in parentheses or quotes, return the nickname, because that is what the person goes by. Kathryn (Katie) becomes Katie. Lazaro (Laz) becomes Laz.
7. When the parenthetical is not a short form of the outer name, keep the outer name and drop the parenthetical. Disa(Xiaobing) becomes Disa.
8. When two names are separated by a slash, return the second one if it is the everyday English short form, otherwise the first. Mihir/Mike becomes Mike.
9. When the first-name field holds the whole name and its last word repeats the last-name field, drop that repeated word. Robert wilkie with last name Wilkie becomes Robert.
10. Keep a genuine two-part given name intact: Jose Ramon, Guðmundur Ragnar, Yong Shuan, Marie-Laure. Do not shorten a name that is simply long.
11. Drop appended job titles, taglines, hiring notices, and company text that someone typed into the name field. Keep only the given name.
12. Keep names written in a non-Latin script exactly as they are. Never transliterate, never translate, never romanize. If the field mixes a native-script name with a Latin-script name, return the Latin-script one.
13. Never invent, expand, translate, or guess a name. Every letter you output must already appear in the first-name field. If you would have to add a letter, do not add it.
14. Never add a trailing period, comma, or quotation marks. Never return a leading or trailing space.
15. No em dashes anywhere in the output.
15b. When one ALL-CAPS token looks like two names run together, do NOT split it, because splitting invents a word boundary. Title Case it as one word and set confidence to "low" so a human checks the row.
16. Return "" for first_name_clean when the field is empty, when it is a placeholder or a mailbox role such as Admin, Info, Sales, Support, Team, Owner, Manager, HR, Office, Contact, N/A, None, Unknown, Test, TBD, when it holds a company name instead of a person, when the first-name field and the last-name field read together as the company name in the company field, or when it is a single letter or a single initial that cannot be greeted.
17. Multi-letter initials that a person actually goes by are fine and stay as written: J.D., K.C., J.C. A single initial such as O. or H. or C is not greetable, so return "".
18. "changed" is true when first_name_clean differs from the raw first-name field, and false when it is identical.
19. "confidence" is "low" when you had to judge whether the string was a person at all, or which part was the given name, and "high" otherwise.
The output must read correctly inside this greeting, with no edits: "Hi FIRST_NAME_CLEAN,"
Work fast. This is a formatting job, not a research job. Do not look anything up and do not reason at length. Do not output your reasoning, only the JSON.
Examples:
Input: first="Dr Ruba" last="Maatouk" company="Metropolitan Dental Care"
Output: {"first_name_clean": "Ruba", "changed": true, "confidence": "high"}
Input: first="Capt. Jehan" last="Alam" company="Fletcher International Exports Pty"
Output: {"first_name_clean": "Jehan", "changed": true, "confidence": "high"}
Input: first="Philip" last="Pickard, MBA" company="Dow"
Output: {"first_name_clean": "Philip", "changed": false, "confidence": "high"}
Input: first="Dr. Marie Y." last="Lemelle, MBA, PhD" company="Platinum Star Public Relations"
Output: {"first_name_clean": "Marie", "changed": true, "confidence": "high"}
Input: first="PAUL" last="Harlin" company=""
Output: {"first_name_clean": "Paul", "changed": true, "confidence": "high"}
Input: first="javonne" last="morgan" company="All Seasons"
Output: {"first_name_clean": "Javonne", "changed": true, "confidence": "high"}
Input: first="DeAndrea (Dee)" last="Davis" company="LyondellBasell"
Output: {"first_name_clean": "Dee", "changed": true, "confidence": "high"}
Input: first="Kathryn (Katie)" last="Connors" company="BrightFarms"
Output: {"first_name_clean": "Katie", "changed": true, "confidence": "high"}
Input: first="Disa(Xiaobing)" last="WU" company="Cordis"
Output: {"first_name_clean": "Disa", "changed": true, "confidence": "high"}
Input: first="Jean-Paul" last="Beleshay" company="Strata Clean Energy"
Output: {"first_name_clean": "Jean-Paul", "changed": false, "confidence": "high"}
Input: first="Anne-Maud" last="Boyard" company="CLARTEIS"
Output: {"first_name_clean": "Anne-Maud", "changed": false, "confidence": "high"}
Input: first="D'Anza" last="Alexander" company="NCTC"
Output: {"first_name_clean": "D'Anza", "changed": false, "confidence": "high"}
Input: first="Rosa'S" last="Delgado" company="Riverside Health"
Output: {"first_name_clean": "Rosa", "changed": true, "confidence": "low"}
Input: first="MARYELLEN" last="Boyd" company=""
Output: {"first_name_clean": "Maryellen", "changed": true, "confidence": "low"}
Input: first="Blue Ridge" last="Roofing" company="Blue Ridge Roofing"
Output: {"first_name_clean": "", "changed": true, "confidence": "high"}
Input: first="👋 James" last="Sansbury" company="Tugboat"
Output: {"first_name_clean": "James", "changed": true, "confidence": "high"}
Input: first="★ Marc" last="Deinum ★" company="MetroStation.nl"
Output: {"first_name_clean": "Marc", "changed": true, "confidence": "high"}
Input: first="Robert wilkie" last="Wilkie" company="RJ's Burgers & Ice Cream Co."
Output: {"first_name_clean": "Robert", "changed": true, "confidence": "high"}
Input: first="Jose Ramon" last="Carrasco" company="RC Innovations"
Output: {"first_name_clean": "Jose Ramon", "changed": false, "confidence": "high"}
Input: first="Guðmundur Ragnar" last="Guðmundsson" company="Prentmet Oddi"
Output: {"first_name_clean": "Guðmundur Ragnar", "changed": false, "confidence": "high"}
Input: first="J.D." last="Dougherty" company="Jeff's Bagel Run"
Output: {"first_name_clean": "J.D.", "changed": false, "confidence": "high"}
Input: first="O." last="Murdock" company="Murdock Chevrolet"
Output: {"first_name_clean": "", "changed": true, "confidence": "high"}
Input: first="珊" last="苏" company="Axine Water Technologies"
Output: {"first_name_clean": "珊", "changed": false, "confidence": "high"}
Input: first="王小明ken" last="Wang" company="Sunrise Optics"
Output: {"first_name_clean": "Ken", "changed": true, "confidence": "low"}
Input: first="Mihir/Mike" last="Parikh" company="FreshLime"
Output: {"first_name_clean": "Mike", "changed": true, "confidence": "high"}
Input: first="Dr Sean Li We Are Actively Hiring At Antai Global" last="Inc" company="Antai Global"
Output: {"first_name_clean": "Sean", "changed": true, "confidence": "low"}
Input: first="AAA" last="Upholstery" company="AAA Upholstery"
Output: {"first_name_clean": "", "changed": true, "confidence": "high"}
Input: first="Admin" last="E-Gree" company="e-gree"
Output: {"first_name_clean": "", "changed": true, "confidence": "high"}
Input: first="" last="Awhaitey" company="Healthy Kingdom"
Output: {"first_name_clean": "", "changed": false, "confidence": "high"}
Input: first="Gowinder " last="Singh" company="Mainfreight"
Output: {"first_name_clean": "Gowinder", "changed": true, "confidence": "high"}
Name to clean:Per-row user message, appended last:
first="<raw first name>" last="<raw last name>" company="<raw company name>"Measured: 2,436 prompt tokens per call (2,097 to 2,212 cached after the first), 20 completion tokens on mini, 627 on nano (~600 reasoning).
This playbook makes no claim about the world — the answer is a substring of the input. That gives you a deterministic guard that costs nothing:
normalize(output) must be a substring of normalize(raw first name)
where normalize = strip accents, casefold, drop everything that is not a letter or digitAccent stripping matters here in a way it did not for company names, because the model is allowed to Title Case a shouted name and you must not flag that as an invention. Zero rows tripped it in the 100-row test.
The model alone is 96/100 and every one of its four errors is the same class: a short or shouted token that could be initials or could be a business. There is no information in the string that settles it, so the fix is a flag and a human, not a better prompt.
| Guard | Catches | Action | Note |
|---|---|---|---|
| G1 placeholder | mailbox roles and junk (Admin, Info, Team, N/A, blank) | ABSTAIN | whole normalized string only, never a substring — Adminson and Teamer are real surnames |
| G2 company overlap | first + last read as the company name | FLAG ONLY | never an auto-abstain: a real row is Jana Meerman at company Jana Meerman |
| G3 caps acronym | 2 to 4 char ALL-CAPS with no vowel (TVK, KSM) | FLAG | the vowel test is what keeps PAUL and PHAM out of the flag |
| G4 run-together shout | one ALL-CAPS token of 9+ chars (KIRKDELANEY) | FLAG | splitting it would invent a word boundary |
| G5 non-Latin script | the cleaned value is not writable in Latin script | FLAG, never an abstain | keep the name exactly as written and let the §2 gate exclude it from an English campaign. Do not transliterate (invents letters), do not blank (destroys a real name) |
| G6 invented letters | normalize(output) is not a substring of normalize(input) | QUARANTINE | a trip here means the model made something up |
⚠️ A bug worth knowing about, found while building these guards. The first version of G1 tested
emptiness as not normalize(v) — and because normalize drops everything outside [0-9a-z],
normalize("珊") is the empty string. That version silently abstained on every Chinese, Cyrillic,
Arabic and Korean name in the benchmark, 4 of 100 rows, and reported them as ordinary placeholder
abstains, so the model score never moved. Any edit to normalize() must keep a Unicode-aware
has_letters() test alongside it.
Truncation guard: finish_reason=length means a retry with a larger cap, never an abstain.
Running a nano-class model at mini's 200-token cap returns empty content on essentially every row,
which is the single most common way to "measure" a 0% hit rate on a working prompt.
| Symptom | Cause | Fix |
|---|---|---|
Greeting reads Hi Dr Matthew, | The campaign is using the sequencer's built-in first_name field instead of the cleaned variable | Map copy to {{first_name_clean}}. Keep the raw value in first_name so a human can always see the source string |
Greeting reads Hi alan, or Hi PAUL, | Casing applied while extracting the name instead of after | Rule 4 fixes this. A lowercase or SHOUTING greeting is the most recognizable mail-merge tell in cold email |
Greeting reads Hi Araceli's, | The source was a business listing with a possessive | Rule 5a, scoped to the END of the field only. Broaden it and you destroy D'Anza, T'Kia, O'Brien, Qurratu'Aini |
| A hyphenated name comes back as one half | An over-eager "take the first token" rule | Rule 5b forbids it; all 6 hyphenated benchmark rows passed. If you see this, the prompt has been edited |
Greeting reads Hi Kirkdelaney, | A run-together ALL-CAPS name. The model correctly refuses to split it | G4 plus confidence: low routes it to review. A withheld row, not a send |
Greeting reads Hi Tvk, or Hi KSM, | Short ALL-CAPS that is either initials or a company acronym. Nothing in the string decides it | G3. Residual class, ~2% of an adversarial sample, far less on a real list. A bigger model does not fix it |
| A company name ships as a person | The person column holds the business | G2, flag only |
| Every Chinese, Arabic, Cyrillic or Korean name silently abstains | A normalize() that strips to [0-9a-z] reduces those names to the empty string, and the emptiness test reads them as blank | The Unicode-aware has_letters() test. 4 of 100 rows, invisible in the model score |
| A real person dropped because their company is named after them | G2 turned into an auto-abstain by a well-meaning edit | G2 is FLAG ONLY |
PAUL and PHAM flagged alongside TVK | G3 written without the vowel test | Add the vowel test |
finish_reason=length on every row | A nano-class model kept at mini's 200-token cap | 2000 for nano, 200 for mini |
| Every row empty and the AI column shows as never run | The run condition binds a column that does not exist. A gate that cannot evaluate true is indistinguishable from "no rows qualified" | Bind to real ids. Smoke-test on 10 rows you know have valid emails |
| Zero cache hits, cost nearly double | Static prefix under the 1,024-token floor | Keep the full example block |
| Someone shortens the prompt "because it looks too long", quality drops, cost barely moves | Cutting the example block drops the prefix under the cache floor: 82% fewer tokens buys only 31% less cost, and the model loses the examples carrying the hard cases | Do not shorten it. See the audit in §6 |
| A weaker prompt is proposed on the grounds that "the guards will catch it" | The guards catch invention and known junk, not failure to strip | Verified: all six guards returned false on all five rows where the short prompt diverged. The guards are a second net, never a substitute for the prompt |
| Clay column returns prose instead of JSON | response_format not set | Set it |
| Different answer on a rerun | No temperature passed, default is not 0 | Cache the output |
| The name is right but the person left the company | Stale row. Cleaning cannot detect it | Out of scope — a list-freshness problem |
| The first name and the company describe different people | A known defect in shared contact data, roughly 7.8% of rows | Flag and resolve upstream |
N/A, never there or friend. N/A renders into
a live email.© growthenginenowoslawski, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 2 other files in skills/playbooks/playbook-first-name-cleaning of growthenginenowoslawski/coldoutboundskills.
Open the folder on GitHubat commit 25c5d85
Playbook First Name Cleaning next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Playbook First Name Cleaning this skillgrowthenginenowoslawski/coldoutboundskills | 753 | — | ~7.1k | Automated safety check: Pass | MIT | |
| Lead Magnetssickn33/agentic-awesome-skills | 47k | 2 repos | ~2.8k | Automated safety check: Pass | MIT | |
| Lead Enrichmenttech-leads-club/agent-skills | 7k | — | ~5.3k | Automated safety check: Pass | Custom licence | |
| Field Playbooksgtmagents/gtm-agents | 414 | 1 repos | ~314 | Automated safety check: Pass | Apache-2.0 | |
| Lead Magnetscoreyhaines31/marketingskills | 54k | 5 repos | ~2.8k | Automated safety check: Pass | MIT | |
| Apify Lead Generationsickn33/agentic-awesome-skills | 47k | 2 repos | ~1.2k | Automated safety check: Notes | MIT |
sickn33/agentic-awesome-skills
Plan and optimize lead magnets for email capture and lead generation.
tech-leads-club/agent-skills
When the user wants to build data enrichment workflows, score leads against ICP, set up Clay waterfalls, or improve contact data quality.
gtmagents/gtm-agents
A skill your agent uses when operationalizing regional/field events with consistent playbooks and follow-up motions.
coreyhaines31/marketingskills
When the user wants to create, plan, or optimize a lead magnet for email capture or lead generation.
sickn33/agentic-awesome-skills
Scrape leads from multiple platforms using Apify Actors. An agent skill from sickn33/agentic-awesome-skills.
affaan-m/ECC
Applies Clean Architecture to Android and Kotlin Multiplatform projects: module layout, dependency rules, UseCases, Repositories and data layer patterns.
growthenginenowoslawski/coldoutboundskills
Diagnostic audit for a running cold email program. An agent skill from growthenginenowoslawski/coldoutboundskills.
growthenginenowoslawski/coldoutboundskills
Conversational intake for cold email campaigns. An agent skill from growthenginenowoslawski/coldoutboundskills.
growthenginenowoslawski/coldoutboundskills
META skill — build the largest possible qualified lead list for any request, end to end.
growthenginenowoslawski/coldoutboundskills
Autonomous cold email campaign launcher. An agent skill from growthenginenowoslawski/coldoutboundskills.
growthenginenowoslawski/coldoutboundskills
Use the Blitz API to find decision-makers at specific companies when you already have a list of company domains.
growthenginenowoslawski/coldoutboundskills
Compare reply rates, bounce rates, and positive reply rates broken down by inbox type (SMTP / Gmail / Outlook) for a Smartlead account.
Turns the raw first-name field on a lead row into the name a person would actually be greeted by, so it can open an email. Playbook First Name Cleaning is an agent skill from growthenginenowoslawski/coldoutboundskills. Turns the raw first-name field on a lead row into the name a person would actually be greeted by, so it can open an email.
Playbook First Name Cleaning fits situations like: clean these first names; first name variable; the greeting says Hi DR MATTHEW; strip the titles off the names.
Run `npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-first-name-cleaning -a claude-code`. Or copy the skill folder (skills/playbooks/playbook-first-name-cleaning in growthenginenowoslawski/coldoutboundskills) into .claude/skills/playbook-first-name-cleaning in your project. Claude Code loads it when a task matches its description.
Run `npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-first-name-cleaning -a codex`. Or copy the skill folder (skills/playbooks/playbook-first-name-cleaning in growthenginenowoslawski/coldoutboundskills) into .agents/skills/playbook-first-name-cleaning in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-first-name-cleaning -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/playbook-first-name-cleaning, .gemini/skills/playbook-first-name-cleaning, .github/skills/playbook-first-name-cleaning and .opencode/skills/playbook-first-name-cleaning in your project.
SKILL.md names no scripts, command-line tools or credentials: Playbook First Name Cleaning is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Playbook First Name Cleaning is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 7.1k tokens (SKILL.md is roughly 28k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Playbook First Name Cleaning: Lead Magnets (sickn33/agentic-awesome-skills, 47k stars), Lead Enrichment (tech-leads-club/agent-skills, 7k stars), Field Playbooks (gtmagents/gtm-agents, 414 stars) and Lead Magnets (coreyhaines31/marketingskills, 54k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
growthenginenowoslawski (a GitHub user) maintains it in growthenginenowoslawski/coldoutboundskills, which has 753 GitHub stars. The repository holds 49 skills in this directory. The repository was last updated on October 5, 2026.
Source: growthenginenowoslawski/coldoutboundskills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.