launch-linkedin-campaign
Set up a LinkedIn outreach campaign on a list of leads (connect → DM1 → DM2 sequence) by chaining `campaign:create` and `campaign:create-sequence`. Enforces the outbound hypothesis gate (refuses to launch without a hypothesis recorded for outreach-campaign-builder). Use when the user says 'launch a LinkedIn campaign for these leads', 'send a LinkedIn outreach to this list', 'start the outbound to the qualified leads', 'run the LinkedIn sequence on this result set', or 'fire the connect-then-DM flow'. Side-effecting — writes to SQLite and stages the sequence on Unipile.
适合你,如果你需要批量向领英联系人发送个性化消息序列。
npx oh-my-skill add othmane-khadri/yalc-the-gtm-operating-system/launch-linkedin-campaigncurl -fsSL https://oh-my-skill.com/install.sh | bash -s -- othmane-khadri/yalc-the-gtm-operating-system/launch-linkedin-campaignnpx oh-my-skill verify othmane-khadri/yalc-the-gtm-operating-system/launch-linkedin-campaign怎么用
商店整理自技能原文 · 版本 ffc6e37 · 表述以原文为准Claude 会引导你创建 LinkedIn 外展活动,包括创建活动、拉取合格潜客、生成连接→私信→私信的序列,并在发送前请你确认。
当你说出“为这些潜客启动 LinkedIn 活动”、“向这个列表发送 LinkedIn 外展”等关键词,或要求执行 LinkedIn 外展序列时触发。
技能原文 SKILL.md
Launch LinkedIn Campaign
I'll wrap two CLI commands — campaign:create (creates the campaign + pulls qualified leads from the holding pool) and campaign:create-sequence (drafts the connect→DM1→DM2 sequence). Both side-effecting → both shell-out per the 0.13.0 architecture.
When This Skill Applies
Use this skill when the user says:
- "launch a LinkedIn campaign for these leads"
- "send a LinkedIn outreach to this list"
- "start the outbound to the qualified leads"
- "run the LinkedIn sequence on this result set"
- "fire the connect-then-DM flow"
NOT this skill (use qualify-leads instead):
- "score these leads first" / "qualify the engagers" — qualification runs the 7-gate pipeline. Run that BEFORE this skill.
NOT this skill (use personalize-message instead):
- "personalize a single DM" — that's per-lead copy. This skill handles bulk sequence generation.
NOT this skill (use scrape-post-engagers instead):
- "pull who liked this post" — that produces a result set. This skill consumes a holding pool of qualified leads.
What This Skill Does
- Hypothesis gate. Before doing anything, checks that the outbound hypothesis is recorded —
~/.gtm-os/frameworks/installed/outreach-campaign-builder.hypothesis.jsonmust exist. If missing: refuses to launch and routes the user toframework:set-hypothesis(or setup Step 10). - Asks for campaign title + optional leads filter + sequence YAML + source CSV/JSON path.
- Shells out to
campaign:createto write the campaign row + pull qualified leads from the holding pool. - Shells out to
campaign:create-sequenceto draft the connect→DM1→DM2 sequence. - Renders both results + asks "ready to send?" — does not auto-trigger sending.
- Suggests enabling
campaign:trackcron (or running it manually) for monitoring.
Pre-flight (before Step 1)
Onboarding interruption guard
test -f ~/.gtm-os/.in-flight-setup && echo "BLOCKED" || echo "OK"
If BLOCKED, stop. Tell the user to finish yalc-gtm start first.
Hypothesis gate (THE critical check)
test -f ~/.gtm-os/frameworks/installed/outreach-campaign-builder.hypothesis.json && echo "OK" || echo "MISSING"
If MISSING, refuse to proceed:
"Can't launch a LinkedIn campaign without a recorded outbound hypothesis. Either: (a) finish setup Step 10 to record one (yalc-gtm start --review-in-chatand re-run setup), or (b) record one directly: ``yalc-gtm framework:set-hypothesis outreach-campaign-builder \ --icp-segment '<segment>' \ --message-angle '<angle>' \ --signal-trigger '<signal>' \ --expected-reply-rate 0.05`` Then re-invoke me."
This guard is enforced by the skill, not the CLI (campaign:create does NOT check the sidecar today).
Workflow
Step 0 — Ask for inputs
One question at a time:
- Campaign title? (e.g., "VP Marketing Q2 outbound — segment-fit hire signal")
- Lead source? Two paths:
- Use the holding pool of all qualified leads (default — pass no
--leads-filter). - Filter to a subset via
--leads-filter '<json-shape>'(e.g.,'{"score":{"$gte":80}}'). - Sequence YAML path? (
--sequence <path>— required bycampaign:create-sequence). Usuallyconfigs/sequences/connect-dm1-dm2.yamlor a custom path. - Source CSV/JSON? (
--source <path>— required bycampaign:create-sequence). The leads file the sequence will personalize against. - Optional toggles:
--linkedin-account <id>(multi-account routing)--timezone <tz>(default Pacific or whatever the framework default is)--start-at <date>(default = now)--send-window '<HH:MM-HH:MM>'--active-days <mon,tue,wed,thu,fri>--delay-mode <natural|fast>--dry-run(preview without writing to Unipile)
Step 1 — Read the hypothesis sidecar for the --hypothesis arg
HYPOTHESIS=$(jq -r '.message_angle' ~/.gtm-os/frameworks/installed/outreach-campaign-builder.hypothesis.json)
The CLI's --hypothesis flag accepts a string. Pass the message_angle so campaign-intelligence can score the actual reply rate against the declared expected rate later.
Step 2 — Shell out to campaign:create
cd ~/Desktop/gtm-os && set -a && source .env.local && set +a && \
npx tsx src/cli/index.ts campaign:create \
--title "<title>" \
--hypothesis "$HYPOTHESIS"
(Add --leads-filter '<json>', --auto-copy, --segment-id, --timezone, --start-at, --send-window, --active-days, --delay-mode, or --dry-run if the user opted in.)
Per the 0.13.0 benchmark: side-effecting commands always shell out. The CLI's withDiagnostics() wrapper handles env loading + tenant resolution.
Step 3 — Parse the campaign result
The CLI prints the campaign id + initial status + the count of qualified leads pulled into the campaign. Capture all three.
On non-zero exit, surface stderr verbatim.
Step 4 — Shell out to campaign:create-sequence
cd ~/Desktop/gtm-os && set -a && source .env.local && set +a && \
npx tsx src/cli/index.ts campaign:create-sequence \
--sequence "<sequence-yaml-path>" \
--source "<leads-csv-or-json-path>"
(Add --linkedin-account <id> or --dry-run if the user opted in.)
Note: campaign:create-sequence does NOT take --campaign-id or --variants flags. It reads the sequence shape from the YAML and personalizes against the source CSV/JSON. Variants are declared inside the YAML, not via CLI flag.
Step 5 — Parse the sequence result
The CLI emits per-lead message previews + the staged sequence ids. Capture them.
Step 6 — Render summary + ask before sending
"Campaign<id>created with<n>qualified leads. Sequence drafted with<m>per-lead messages staged. Ready to send? Hitting yes will start the campaign tracker so messages go out on schedule. (Or runyalc-gtm campaign:trackmanually anytime.)"
Do not run campaign:track yourself. That's the track-campaigns skill's job (Wave 4). The user makes the call.
Step 7 — Fallback path
If either CLI exits non-zero with a non-credential error (e.g., campaign:create fails because no qualified leads exist in the holding pool), surface the error verbatim and suggest:
- "Run
qualify-leadsfirst to populate the holding pool, then re-invoke me."
Failure surfacing — verbatim
When either CLI exits non-zero (Anthropic 429, Unipile DSN expired, sequence YAML invalid, missing leads), paste the stderr unchanged.
Notes
- The CLI surfaces
campaign:createflags this way (verified):--leads-filter <json>,--title <title>,--hypothesis <hypothesis>,--auto-copy,--segment-id <id>,--timezone <tz>,--start-at <date>,--send-window <range>,--active-days <days>,--delay-mode <mode>,--dry-run. No--result-setflag. - The CLI surfaces
campaign:create-sequenceflags this way (verified):--sequence <path>(required),--source <path>(required),--linkedin-account <id>,--dry-run. No--campaign-idor--variantsflag — variants are declared in the sequence YAML. - The hypothesis gate is intentional: prevents the misroute Step 10 of setup fixed in 0.10.0 (where "launch outbound" silently became "draft a content hook").
- 30 connects/day cap is enforced by Unipile and the campaign tracker — not by this skill.
- Dual flow:
campaign:createwrites the campaign row;campaign:create-sequencepopulates the per-lead messages. They're decoupled deliberately so you can re-stage messages without re-creating the campaign.