Community编程与开发github.com

keinsaasforever/gtm-pipeline-skills

B2B Sales Claude Skills

gtm-pipeline-skills 是什么?

gtm-pipeline-skills is a Claude Code agent skill that b2B Sales Claude Skills.

兼容平台Claude Code~Codex CLI~CursorGemini CLI
npx skills add keinsaasforever/gtm-pipeline-skills

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Demo

Generate a demo lead list of ~10 enriched contacts with personalized message examples, triggered by a webhook prompt.

Read ~/.claude/skills/gtm-pipeline/_shared/conventions.md before executing.


When to Use

  • Webhook trigger: user submits a free demo form describing their target audience
  • Goal: prove the AI agent writes authentic, non-generic outreach using real leads
  • Scope: ~10 contacts, enriched with LinkedIn + email, 2–4 message examples

Demo Restrictions

  • No phone enrichment — email only
  • ~10 contacts (request 10–15, expect enrichment drop-off)
  • Message generation is optional but recommended
  • Signal search is opt-in — off by default. Enable via --with-signals flag or explicit user request. See Step 5.5.
  • Cost gate: before the first paid provider call, state the expected spend (finder searches per People-Source Cadence are mostly free; ~10-15 email enrichment credits; signals <$0.50 if enabled). Interactive: confirm with the user first. Headless: log the estimate in run_log.md and proceed, never block.

Step 1 — Parse the Prompt & Establish the ICP

The webhook prompt describes the user's target audience. Extract:

Must have:

  • What do you sell / offer?
  • Who is your ideal customer? (industry, role, company size, location)
  • What's your value proposition?
  • What tone? (formal vs. casual, examples if possible)
  • Is this for recruiting OR selling to customers?

If not in the prompt, infer (do not stall):

  • Target job titles, location, company size/type

Auto-resolve the requester before interpreting the audience (fixes the recurring "what do they even sell / why do they want this audience" gap):

  1. Resolve the requester's own domain from their email (e.g. [email protected]acme.de), scrape/enrich it, and establish what they sell. Multi-offering companies: confirm which product line the demo is for — the prompt's stated product can mismatch the real one.
  2. Determine the relationship to the target audience — a target term (e.g. "call centers") is usually a segment they sell to, not what they are. Classify: sell-to / buy-from / acquire / partner / recruit. Persona keywords derive from this, not from a guess.

Interactive vs deployed (headless):

  • Interactive: if a must-have is genuinely ambiguous after auto-resolution, ask one concise clarifying question. Otherwise proceed.
  • Deployed / headless (invoked via claude -p from the webhook — see Deployment): never block on questions. Infer every field from the prompt + requester-domain research, record assumptions in context/icp.md under an "Assumptions" heading, and proceed end-to-end.

Do NOT proceed to search until the ICP is clear enough to build a meaningful filter.

Save ICP to: {client-slug}-gtm/context/icp.md (include the offering, the relationship classification, and any headless assumptions).


Step 2 — Create Working Directory

Create the {client-slug}-gtm/ directory structure as defined in conventions.md. Write the ICP definition to context/icp.md.


Step 3 — People Search (10 contacts)

Use the people-search skill to find ~10–15 contacts.

Provider selection for demo: follow the finder cadence in conventions.md → People-Source Cadence — FullEnrich Finder → BetterContact → Pipe0 → Amplemarket/Crustdata (last resort), max 2 attempts/source, FE-first for SME/owner-led/non-English segments. Both FE and BC return LinkedIn URLs directly (needed for email enrichment). If no company list (persona-based prompt), use Parallel FindAll or BC Search. For directory/scrape-sourced company lists, search by company name + location, never by exact domain (conventions #11).

Key fields to collect:

full_name, first_name, last_name,
job_title, company_name, company_domain,
linkedin_profile_url, location

Follow the people-search execution protocol: sandbox → test → review → run.


Step 4 — Contact Filter (ICP Ranking)

Run contact-filter on the 10–15 contacts found. Even small batches benefit from ICP ranking — it ensures the enrichment step focuses on the best-fit contacts.

  • Applies job tier, industry tier, location tier, and company size classification
  • Rejects hard non-ICP contacts
  • Ranks passed contacts by priority
  • Output: csv/intermediate/contacts_filtered.csv

For demos: use a relaxed hard-reject threshold (allow tiers 1–5 to pass), prioritize ranking over filtering.


Step 5 — People Enrichment (Email Only)

Run people-enrichment on the filtered contacts. Demo mode: email only, no phone.

Recommended flow:

  1. FullEnrich v2 (email) — all contacts
  2. Pipe0 waterfall — for FE misses only

Additional enrichment for message personalization (if available):

  • LinkedIn headline and summary (from LinkedIn scrape via PhantomBuster)
  • Recent LinkedIn posts (2–3 per contact) — significantly improves message quality

Minimum viable fields for message generation:

name, job_title, company_name, linkedin_profile_url,
headline (optional), summary (optional), recent_posts (optional)

Low yield: if fewer than ~8 contacts survive filtering + enrichment, run one additional finder pass (next source in the People-Source Cadence) before proceeding to messages.


Step 5.5 — Signal Search (Optional)

Default: OFF. Enable when:

  • The user explicitly asks for signal-anchored messages
  • The webhook prompt mentions buying triggers (funding, hiring, transformation, recent news)
  • The client's offering depends on timing signals to make sense (e.g. "we help post-Series-A companies scale ops")
  • A --with-signals flag is passed to the demo invocation

Cost note: adds ~$0.01–0.05 per unique company (web search + scoring). On a 10-contact demo, that's typically 5–10 unique companies, so <$0.50.

Required inputs for signal-search

Signal-search needs three context files:

  • context/icp.md — already collected in Step 1
  • context/offering.md — the value-prop block from Step 1 (write it now if not already saved)
  • context/signal_criteria.md — bulleted list of signal types relevant to the offering

If signal_criteria.md doesn't exist, ask the user: "What would a company be doing right now that suggests they need what you offer?" — capture 5–10 bullets and save.

Run

# Build company list from unique companies in the enriched contacts
python3 -c "
import csv, sys
seen = set()
with open('csv/output/contacts_enriched.csv') as f, open('csv/input/companies_raw.csv', 'w', newline='') as out:
    reader = csv.DictReader(f)
    writer = csv.DictWriter(out, fieldnames=['company_name', 'company_domain', 'company_website'])
    writer.writeheader()
    for row in reader:
        c = row.get('company_name') or row.get('company') or ''
        if c and c not in seen:
            seen.add(c)
            writer.writerow({
                'company_name': c,
                'company_domain': row.get('company_domain') or '',
                'company_website': row.get('company_website') or row.get('website') or '',
            })
"

# Run signal-search on the unique companies
source "$HOME/.claude/skills/gtm-pipeline/_shared/resolve_env.sh" && \
export $(grep -E '^(PARALLEL_API_KEY|OPENROUTER_API_KEY|FIRECRAWL_API_KEY|GEMINI_API_KEY)=' "$GTM_ENV_PATH" | xargs) && \
  python3 ~/.claude/skills/gtm-signal-search/signal_search.py \
    --client-dir {client-slug}-gtm

The resolve_env.sh source line ensures $GTM_ENV_PATH is set even in a fresh shell (see signal-search SKILL.md / conventions). For the demo, leave Firecrawl and Parallel enrichment OFF — web search + scoring is enough for a ~10-contact lead list. Enable Firecrawl only if on-site content (careers, blog) is the primary signal source; if this machine has Firecrawl only via MCP (no FIRECRAWL_API_KEY), use the --firecrawl-pages-dir route documented in the signal-search skill.

Merge signals back into contacts

import csv, json
signals = {}
with open('csv/intermediate/signals.csv') as f:
    for row in csv.DictReader(f):
        signals[row['company_name']] = {
            'overall_score': row.get('overallScore', ''),
            'scored_signals': row.get('scoredSignals', ''),
            'overall_summary': row.get('overallSummary', ''),
        }

rows_out = []
with open('csv/output/contacts_enriched.csv') as f:
    for row in csv.DictReader(f):
        s = signals.get(row.get('company_name') or row.get('company') or '', {})
        row['company_overall_score'] = s.get('overall_score', '')
        row['company_scored_signals'] = s.get('scored_signals', '')
        row['company_overall_summary'] = s.get('overall_summary', '')
        rows_out.append(row)

with open('csv/output/contacts_enriched.csv', 'w', newline='') as f:
    w = csv.DictWriter(f, fieldnames=list(rows_out[0].keys()))
    w.writeheader()
    w.writerows(rows_out)

The message-generation step in Step 6 will then have company_overall_summary and company_scored_signals per contact — use the highest-scored signal as the message hook.


Step 6 — Generate Message Examples

Generate 2–4 sample messages before committing to the full batch.

Message Structure

Every message must follow: Hook → Bridge → Offer → Soft CTA

PartPurposeLength
HookReference something specific to this person (post, career move, company signal)1 sentence
BridgeConnect their situation to your offer1 sentence
OfferWhat you provide, clearly stated1 sentence
CTASoft ask — not "let's schedule a call"1 sentence

Total: 320–450 characters. No blank line after greeting. Paragraphs separated by single line break.

Quality Rules

Must have:

  • Specific hook (post reference OR career insight OR scored buying signal — not generic)
  • Clear value proposition
  • Natural, conversational tone
  • Soft CTA

Must avoid:

  • Repeating profile info they already know ("You work as X at Y")
  • Generic observations ("impressive background", "I noticed you're in [industry]")
  • Corporate jargon or buzzwords
  • Pushy CTAs ("Let's schedule a call this week")

If Step 5.5 ran: for each contact, prefer the highest-scored signal from company_scored_signals as the hook over generic LinkedIn-post references. A score >= 70 signal anchored in real recent news is the strongest hook the demo can produce.

Generation Process

  1. Write a client-specific system prompt (save to prompts/message_prompt.md)
  2. Generate 2–4 samples — include contacts with and without LinkedIn posts
  3. Review against quality checklist above
  4. If issues found, refine the system prompt and regenerate
  5. Only batch generate once quality is approved

System Prompt Template (key sections)

- Client context: what they sell, who they target, their value prop, tone
- Forbidden rules: no profile repetition, no generic flattery
- Message structure: hook → bridge → offer → CTA
- Hook examples: with posts / without posts
- Character limit: 320–450

Step 7 — Sanitize & Output

Step 7a — Sanitize (mandatory, deterministic, no LLM). csv/intermediate/ keeps every field. Before building anything lead-facing, run the shared sanitizer so the recurring hand-scrubbing (provider labels, empty columns, bad emails, stale signals) happe

相关技能