Before you run this skill
This skill is brand-neutral. It reads its brand, palette and endpoints from
brand.config.json at the repo root.
On first use, do this before anything else:
- Run
python3 brandkit.py. It prints the config source and any placeholder
that is still unset.
- If it says
configured: False, copy brand.config.example.json to
brand.config.json.
- Ask the operator for each value under
missing, then write them in. Do not
guess a brand name, a domain, or a colour.
- Anything the skill writes out should be passed through
brandkit.fill(text), which swaps every {{TOKEN}} for its configured value
and remaps the default palette to the operator's.
Text below uses {{TOKEN}} where a value is operator-specific. Treat an
unresolved {{TOKEN}} in your output as a bug, not as literal copy.
/copy-command — Email Copy → Google Sheet
Takes approved email copy from a Google Doc and places it into a Google Sheet,
structured the way the user asks, with clean personalization and zero
signature contamination. Prioritizes accuracy, clean personalization, correct
segmentation, and campaign readiness over speed.
Step 0 — Load prior learnings (MANDATORY, before anything else)
Read LEARNINGS.md in this skill's directory (~/.claude/skills/copy-command/LEARNINGS.md).
It is the accumulated record of corrections and confirmed behaviors from every
previous copy-management iteration in this project. Everything in it is
default behavior — apply it without the user having to repeat it.
At the END of every run: if the user corrected anything, or you established a
new convention (a new banned phrase, a new mapping rule, a client-specific
quirk), append it to LEARNINGS.md with the date and one line of context.
Step 1 — Collect inputs
Ask the user for all of these up front (one message / one AskUserQuestion batch,
not a drip). Skip any the user already provided.
- Sheet URL — the Google Sheet to add copy to.
- Tab name — the tab (or base name) the copy goes into.
- Tab structure — ask: "Do you want to create a new tab or multiple tabs?
Describe how you want the tabs structured."
- Subject line — ask: "Do you want to add a subject line? (y/N)" If yes,
get the subject line(s) or confirm they come from the copy doc.
- Copy Document URL — the Google Doc containing the approved email copy.
- Multiple-copy clarification — ask: "If the document contains multiple
copy variants, paste the copies here and explain which copy should be used
for which tab/segment." If the doc has one variant, this can be skipped.
Do not start writing to the sheet until the copy→tab mapping is unambiguous.
If the doc contains more variants than the user mapped, stop and ask.
Step 2 — Read the sources
- Copy doc: read via the Docs API through the OAuth helper (readonly scope
is already on the token). Extract each variant exactly as written: subject
(if present), email 1, and every follow-up in order.
- Sheet: read the source tab(s) via the Sheets API. Identify the lead
columns (first name, company, email, any segment/ICP columns) and note every
existing column — all of it must survive untouched.
Step 3 — Clean personalization variables (ALWAYS, via /copy-cleaner)
Before any copy lands in the sheet, clean the personalization inputs for
every lead by invoking the copy-cleaner skill
(~/.claude/skills/copy-cleaner/SKILL.md). It runs the LOCKED prompt v6
(operator-approved 2026-08-18, Prompt Builder task
copy-personalization-cleaner) and is authoritative for all three fields:
first_name_clean — the one name the person goes by (Muhammad Ali Kazimi → Ali, Dr Karim El Solh → Karim, Mariia Eroshin, JD, CFA →
Mariia).
company_clean — shortest form that still uniquely identifies the firm
(Transpose Platform → Transpose, Raffles Family Office → Raffles,
Partners Capital stays).
event_clean — the 1-3 word name attendees call the event
(SuperReturn Asia Venture Capital Summit 2026 → SuperReturn,
Markets Group Private Equity Boston Forum 2026 → PE Forum) — for
event-based campaigns (subject lines and opening lines per the 2026-08-18
event-copy learning).
Execution per that skill: dedupe unique values, fan out sub-agents that read
the locked prompt file cold and verbatim, merge by row id, honor its statuses:
unresolvable_name / unresolvable_company → null: blank the copy cell
or use the fallback branch, flag in Notes, report the count. Never render
"Hi NULL" or "your work at LLC".
needs_review → the value ships, but list those rows in the run summary.
Do NOT re-implement cleaning with ad-hoc heuristics or a paraphrased prompt —
drift was proven in tuning. Legacy references (CNCleanup,
scripts/push_to_drive/ib_clean_first_names.py) are superseded by
copy-cleaner for this skill's cleaning stage; a Python fast path is acceptable
ONLY if its outputs match copy-cleaner's golden examples table exactly
(spot-check before use).
Step 4 — Prepare the copy
- Preserve the original copy. Do not rewrite, reword, "improve", or shorten
the approved copy. The ONLY permitted changes: personalization substitution,
company-name cleaning, first-name cleaning, signature removal, "Sent from my
iPhone" removal, and variant→tab mapping.
- NEVER add a signature. No
Ramo, no Dash, no Best, [Name],
no Thanks, [Name], no sender sign-off of any kind. If the source doc ends
with one, strip it. The sending platform appends the signature dynamically.
- NEVER include "Sent from my iPhone" or any equivalent mobile signature.
If the source doc contains it, remove it before writing to the sheet.
- Multiple variants: identify each variant clearly, keep its follow-ups in
the correct number and order, and map it to exactly the tab/segment the user
specified. Never mix lines from one variant into another. If the user asked
for a randomized split, make it deterministic (sort-and-slice on a hash of a
stable lead key, per the linkedin-outreach-sheet convention) so re-runs land
the same leads in the same buckets.
Step 5 — Write to the sheet
Writes go ONLY via the Python OAuth helper ({{GOOGLE_ACCOUNT}}). Never use
the Drive MCP create/copy tools — they are bound to a different account
({{BRAND_NAME}}).
import sys
sys.path.insert(0, "{{WORKSPACE_DIR}} - Second Brain")
from scripts.push_to_drive.auth import sheets_service, docs_service
Run scripts with the project venv:
cd "{{WORKSPACE_DIR}} - Second Brain/scripts/push_to_drive"
./.venv/bin/python <your_script.py>
Sheet rules (unless the user explicitly instructs otherwise):
- Never modify the original/source tab. New copy goes into new tabs or new
columns per the user's requested structure.
- Preserve all existing lead data and columns. Never delete a column.
Columns not needed by the VA/campaign may be hidden, not removed.
- New tabs follow the house format: Geist font everywhere, bold frozen header
row, copy columns (
Email Body, Followup 1, Followup 2, LinkedIn DM)
at ~220px width, other columns auto-resized.
- Subject line goes in its own
Subject Line column when requested.
- Follow-ups each get their own column, in send order.
- Write per-lead rendered copy (personalization substituted), not raw
templates, unless the user asks for template + variable columns.
- If the token is expired, run
reauth.py in the same directory first.
Step 6 — Final quality check (MANDATORY before reporting done)
Verify, by re-reading what was actually written to the sheet:
Report the sheet URL, which tabs were created/updated, lead counts per tab, and
anything you cleaned or flagged (e.g. "14 first names fixed, 2 left blank and
flagged in a Notes column").
Step 7 — Capture learnings
If this run produced any correction from the user or any new convention,
append it to LEARNINGS.md (dated, one line of context). That is how this
skill gets better instead of repeating mistakes.
1---2name: copy-command3description: Add and manage email copy in Google Sheets for outbound campaigns: pull approved copy from a Google Doc, clean personalization variables (first names, company names, event names — via the locked copy-cleaner skill), map copy variants to the right tabs/segments, and write campaign-ready tabs via the {{GOOGLE_ACCOUNT}} OAuth helper — without ever touching the source tab or adding a signature. TRIGGER on: `/copy-command`, "add this copy to the sheet", "put the email copy in the sheet", "load the copy doc into the tabs", "map these copy variants to segments", or any request to place approved email copy into a Google Sheet for a campaign.4---5<!-- SETUP:BEGIN -->6## Before you run this skill78This skill is brand-neutral. It reads its brand, palette and endpoints from9`brand.config.json` at the repo root.1011**On first use, do this before anything else:**12131. Run `python3 brandkit.py`. It prints the config source and any placeholder14 that is still unset.152. If it says `configured: False`, copy `brand.config.example.json` to16 `brand.config.json`.173. Ask the operator for each value under `missing`, then write them in. Do not18 guess a brand name, a domain, or a colour.194. Anything the skill writes out should be passed through20 `brandkit.fill(text)`, which swaps every `{{TOKEN}}` for its configured value21 and remaps the default palette to the operator's.2223Text below uses `{{TOKEN}}` where a value is operator-specific. Treat an24unresolved `{{TOKEN}}` in your output as a bug, not as literal copy.2526<!-- SETUP:END -->2728# /copy-command — Email Copy → Google Sheet2930Takes approved email copy from a Google Doc and places it into a Google Sheet,31structured the way the user asks, with clean personalization and zero32signature contamination. Prioritizes **accuracy, clean personalization, correct33segmentation, and campaign readiness over speed**.3435## Step 0 — Load prior learnings (MANDATORY, before anything else)3637Read `LEARNINGS.md` in this skill's directory (`~/.claude/skills/copy-command/LEARNINGS.md`).38It is the accumulated record of corrections and confirmed behaviors from every39previous copy-management iteration in this project. Everything in it is40**default behavior** — apply it without the user having to repeat it.4142At the END of every run: if the user corrected anything, or you established a43new convention (a new banned phrase, a new mapping rule, a client-specific44quirk), append it to `LEARNINGS.md` with the date and one line of context.4546## Step 1 — Collect inputs4748Ask the user for all of these up front (one message / one AskUserQuestion batch,49not a drip). Skip any the user already provided.50511. **Sheet URL** — the Google Sheet to add copy to.522. **Tab name** — the tab (or base name) the copy goes into.533. **Tab structure** — ask: *"Do you want to create a new tab or multiple tabs?54 Describe how you want the tabs structured."*554. **Subject line** — ask: *"Do you want to add a subject line? (y/N)"* If yes,56 get the subject line(s) or confirm they come from the copy doc.575. **Copy Document URL** — the Google Doc containing the approved email copy.586. **Multiple-copy clarification** — ask: *"If the document contains multiple59 copy variants, paste the copies here and explain which copy should be used60 for which tab/segment."* If the doc has one variant, this can be skipped.6162Do not start writing to the sheet until the copy→tab mapping is unambiguous.63If the doc contains more variants than the user mapped, stop and ask.6465## Step 2 — Read the sources6667- **Copy doc:** read via the Docs API through the OAuth helper (readonly scope68 is already on the token). Extract each variant exactly as written: subject69 (if present), email 1, and every follow-up in order.70- **Sheet:** read the source tab(s) via the Sheets API. Identify the lead71 columns (first name, company, email, any segment/ICP columns) and note every72 existing column — all of it must survive untouched.7374## Step 3 — Clean personalization variables (ALWAYS, via /copy-cleaner)7576Before any copy lands in the sheet, clean the personalization inputs for77**every lead** by invoking the **`copy-cleaner` skill**78(`~/.claude/skills/copy-cleaner/SKILL.md`). It runs the LOCKED prompt v679(operator-approved 2026-08-18, Prompt Builder task80`copy-personalization-cleaner`) and is authoritative for all three fields:8182- **`first_name_clean`** — the one name the person goes by (`Muhammad Ali83 Kazimi` → `Ali`, `Dr Karim El Solh` → `Karim`, `Mariia Eroshin, JD, CFA` →84 `Mariia`).85- **`company_clean`** — shortest form that still uniquely identifies the firm86 (`Transpose Platform` → `Transpose`, `Raffles Family Office` → `Raffles`,87 `Partners Capital` stays).88- **`event_clean`** — the 1-3 word name attendees call the event89 (`SuperReturn Asia Venture Capital Summit 2026` → `SuperReturn`,90 `Markets Group Private Equity Boston Forum 2026` → `PE Forum`) — for91 event-based campaigns (subject lines and opening lines per the 2026-08-1892 event-copy learning).9394Execution per that skill: dedupe unique values, fan out sub-agents that read95the locked prompt file cold and verbatim, merge by row id, honor its statuses:9697- `unresolvable_name` / `unresolvable_company` → **null**: blank the copy cell98 or use the fallback branch, flag in Notes, report the count. Never render99 "Hi NULL" or "your work at LLC".100- `needs_review` → the value ships, but list those rows in the run summary.101102Do NOT re-implement cleaning with ad-hoc heuristics or a paraphrased prompt —103drift was proven in tuning. Legacy references (`CNCleanup`,104`scripts/push_to_drive/ib_clean_first_names.py`) are superseded by105copy-cleaner for this skill's cleaning stage; a Python fast path is acceptable106ONLY if its outputs match copy-cleaner's golden examples table exactly107(spot-check before use).108109## Step 4 — Prepare the copy110111- **Preserve the original copy.** Do not rewrite, reword, "improve", or shorten112 the approved copy. The ONLY permitted changes: personalization substitution,113 company-name cleaning, first-name cleaning, signature removal, "Sent from my114 iPhone" removal, and variant→tab mapping.115- **NEVER add a signature.** No `Ramo`, no `Dash`, no `Best, [Name]`,116 no `Thanks, [Name]`, no sender sign-off of any kind. If the source doc ends117 with one, strip it. The sending platform appends the signature dynamically.118- **NEVER include "Sent from my iPhone"** or any equivalent mobile signature.119 If the source doc contains it, remove it before writing to the sheet.120- **Multiple variants:** identify each variant clearly, keep its follow-ups in121 the correct number and order, and map it to exactly the tab/segment the user122 specified. Never mix lines from one variant into another. If the user asked123 for a randomized split, make it deterministic (sort-and-slice on a hash of a124 stable lead key, per the linkedin-outreach-sheet convention) so re-runs land125 the same leads in the same buckets.126127## Step 5 — Write to the sheet128129**Writes go ONLY via the Python OAuth helper ({{GOOGLE_ACCOUNT}}). Never use130the Drive MCP create/copy tools — they are bound to a different account131({{BRAND_NAME}}).**132133```python134import sys135sys.path.insert(0, "{{WORKSPACE_DIR}} - Second Brain")136from scripts.push_to_drive.auth import sheets_service, docs_service137```138139Run scripts with the project venv:140141```bash142cd "{{WORKSPACE_DIR}} - Second Brain/scripts/push_to_drive"143./.venv/bin/python <your_script.py>144```145146Sheet rules (unless the user explicitly instructs otherwise):147148- **Never modify the original/source tab.** New copy goes into new tabs or new149 columns per the user's requested structure.150- **Preserve all existing lead data and columns.** Never delete a column.151 Columns not needed by the VA/campaign may be hidden, not removed.152- New tabs follow the house format: Geist font everywhere, bold frozen header153 row, copy columns (`Email Body`, `Followup 1`, `Followup 2`, `LinkedIn DM`)154 at ~220px width, other columns auto-resized.155- Subject line goes in its own `Subject Line` column when requested.156- Follow-ups each get their own column, in send order.157- Write per-lead rendered copy (personalization substituted), not raw158 templates, unless the user asks for template + variable columns.159- If the token is expired, run `reauth.py` in the same directory first.160161## Step 6 — Final quality check (MANDATORY before reporting done)162163Verify, by re-reading what was actually written to the sheet:164165- [ ] Every intended lead has the correct copy variant.166- [ ] Personalization variables are populated for every row (no `[First name]`167 / `{{company}}` tokens left unrendered, no blanks mid-sentence).168- [ ] Company names are clean and natural (copy-cleaner output, spot-checked169 against its golden examples).170- [ ] First names are clean; event names (if used) are the copy-cleaner short171 form.172- [ ] No null/unresolvable lead got a rendered greeting or company mention;173 all flagged rows are counted in the report.174- [ ] Subject lines are present when requested, absent when not.175- [ ] Follow-ups are in the correct order, correct count per variant.176- [ ] Variants are correctly separated — spot-check rows in each tab against177 the mapping.178- [ ] No signature anywhere (`Ramo`, `Dash`, `Best,`, `Thanks,` …).179- [ ] No "Sent from my iPhone" or equivalent.180- [ ] No copy from one variant bleeding into another.181- [ ] Source tab untouched; no existing columns deleted.182- [ ] Tabs are campaign-ready as-is.183184Report the sheet URL, which tabs were created/updated, lead counts per tab, and185anything you cleaned or flagged (e.g. "14 first names fixed, 2 left blank and186flagged in a Notes column").187188## Step 7 — Capture learnings189190If this run produced any correction from the user or any new convention,191append it to `LEARNINGS.md` (dated, one line of context). That is how this192skill gets better instead of repeating mistakes.