Lead Harvester
SkillDev tools"Harvest hand-raisers from a comment-keyword campaign. Trigger on 'who
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Lead Harvester skill
What this skill tells your AI
The instructions your AI receives, as published by legioncodeinc/vibe-coding-tools in src/quarantine/skills/keep/lead-harvester/SKILL.md and read by ahel’s review.
Purpose
You run a "comment KEYWORD to get it" campaign. Dozens or hundreds of people raise a hand. The platform shows them to you once, as a notification, and then they are gone. Some commented. Some skipped the comment and just DM'd you. Some sent a friend request. Some only reacted. A week later you have a vague memory of nine names and a folder of unanswered messages, one of which says "I DMd you and you haven't responded yet".
This skill reconstructs that list from what Littlebird already captured, ranks it by how hard each person waved, and writes the first message for every one of them.
It drafts. It does not send, and it does not automate platform actions. That is a
design decision, not a limitation, and references/platform-rules.md has the evidence.
It is the post-mortem. comment-to-crm-piper is the daily drip. Run this one after a
launch to reconstruct the whole roster with scoring and segmentation. Run the sibling every
day so the roster never has to be reconstructed. They share the extraction and matching
logic, and the sibling points at this skill's guides rather than duplicating them.
Littlebird MCP calls used
Real tool names, verified against references/littlebird-mcp-reference.md. LIST the tools
available in this session before calling anything and use the names you actually find. Do
not assume a tool exists because it is named here.
| Tool | Used for |
|---|---|
search_user_context | Every retrieval pass in step 3. search_queries carries the snapshot-side queries: notification pages, expanded comment threads, reaction lists, friend and connection request screens, and the campaign post itself. search_queries_messages carries the direct message and message request queries. date_range is bounded to the campaign window extended 2 days past the end date. filters sets data_source to snapshots or messages and app to chrome or the platform's native client. |
LB_INTERNAL_LIST_ROUTINES | Checking whether the campaign watcher already exists before offering to create one. |
LB_INTERNAL_GET_ROUTINE_REPORTS | The handoff. Reading the watcher's own past reports before step 3, for the day-by-day timeline and the running gap count. |
LB_INTERNAL_GET_ROUTINE_CONFIG | Reading the current routine before any update, because prompt and schedule each REPLACE the whole field. |
LB_INTERNAL_CREATE_ROUTINE | Creating the campaign watcher, from an interactive session only. |
LB_INTERNAL_UPDATE_ROUTINE | Rescheduling or pausing it later, from an interactive session only. |
LB_INTERNAL_GET_SUBSCRIPTION_STATUS | Explaining a plan gate or a routine-count limit. |
Not used, and deliberately. The meeting tools LB_INTERNAL_LIST_MEETINGS,
LB_INTERNAL_SEARCH_MEETINGS, LB_INTERNAL_GET_MEETING and
LB_INTERNAL_GET_MEETING_TRANSCRIPT are real, but a comment-keyword campaign lives on
social surfaces rather than in calls, so this skill does not touch them. There is also no
Littlebird tool that searches past Littlebird chat conversations and no calendar tool.
Anything that looks like search_chats or get_calendar is not real; use
search_user_context and say so.
CRM and platform tools are NOT Littlebird tools. GoHighLevel, HubSpot, Gmail and the rest are separate MCP servers that may or may not be connected in this session. List the tools first, and degrade to a file or a copy-paste block when a connector is absent.
Trigger
Fires on: who commented my keyword, harvest my campaign leads, who DMd me about the launch, build my outreach list, comment to get it campaign, I missed a bunch of DMs, rank my inbound leads, who raised their hand.
Also fires when the user opens Cowork after a notification from the campaign watcher routine.
Do NOT fire this for the everyday "anyone new since yesterday" question. That is
comment-to-crm-piper.
Routine cadence
On demand is primary. This skill is a campaign post-mortem and most runs are one-off.
The optional watcher runs DAILY during an active campaign window, default
{"frequency": "daily", "time": "17:00"} in the user's local timezone, so the nudge lands
while the window is still open. Offer it only when the user says they run these campaigns
constantly or has a window open right now, and offer to pause or delete it when the campaign
closes.
Offer to create it rather than telling the user to go set it up by hand.
LB_INTERNAL_CREATE_ROUTINE works from an interactive session and is only blocked from
inside a running routine. Show the exact prompt text and the schedule, get approval with
AskUserQuestion, then call it.
The routine OBSERVES and the Cowork session ACTS
(references/littlebird-mcp-reference.md, the Routines-observe Cowork-acts pattern). The
scoring, the ambiguous-merge questions, the drafting and the approval gate all happen in the
interactive session, because a routine cannot run an approval gate. Exact prompt text is in
the routine wiring section below.
Capability gate
This skill requires the Littlebird MCP on a Power or Pro plan.
- LIST the tools actually available in this session. Do not assume tool names. Use the real names you find.
- If no Littlebird tools are present, stop and tell the user the skill needs the Littlebird MCP connected, with a link to https://support.littlebird.ai/docs/mcp/. Do not proceed on capture-free guesswork.
- If the tools are present but return plan errors, call the subscription status tool and report the plan gate to the user.
Tool surface, parameters, and return shapes are documented in
references/littlebird-mcp-reference.md. Read it before writing any query.
Process
1. Frame the campaign. Do not guess.
Never infer which post was the campaign. Ask, with AskUserQuestion, in one batch:
- The keyword. The exact word or phrase you asked people to comment.
- The campaign window. Start and end dates. Offer sensible options: the last 7 days, the last 14 days, the last 30 days, or a custom range.
- The platform. Facebook, Instagram, LinkedIn, X, or several.
- The post. Enough to identify it: a URL if they have one, otherwise the date and the first line of the post text.
- The offer. What they promised to send. The first touch has to deliver it.
Record all five. They drive every query in step 3 and every draft in step 6.
2. Pre-flight. Offer the capture protocol.
This is the skill's power move, and it happens BEFORE the harvest.
Littlebird records what is on screen. Social notification UIs collapse rosters into "X, Y and 4 others commented" and "12 people reacted", so a roster built from ambient notification capture alone is partial by construction. Thirty to sixty seconds of the user slowly scrolling the expanded comment thread converts a partial roster into a near-complete one, because capture reads the names that render.
Ask the user whether they have scrolled the full thread since the campaign started.
- If no: give them the platform-specific instructions from
references/capture-protocol.md, tell them it takes under a minute, and offer to wait. If they would rather see partial results now, run the harvest and flag the gap loudly. - If yes: note when, and set the retrieval window to include it.
Never run a harvest and hand back a thin roster without telling the user that a 60-second action would have made it much thicker.
3. Retrieval brief
Run MULTIPLE NARROW queries, not one broad one. A broad query returns oversized results
that get dumped to a file, and it scores worse
(references/littlebird-mcp-reference.md, retrieval patterns). Every query below is
bounded by the campaign window from step 1, extended 2 days past the end date to catch
late arrivals.
Substitute the real keyword, platform, and dates. Run these as parallel calls where the harness allows it.
A. Comment and notification surfaces. search_user_context, data_source: snapshots,
app: chrome (add the platform's own app where the user works in a native client):
- "notifications page showing who commented on my post"
- "comment thread on my post with commenter names and comment text"
- "KEYWORD comment on post" using the actual keyword
- "post notification saying and others commented on your post"
- "list of people who reacted to my post"
B. Direct message surfaces. search_user_context, search_queries_messages
populated, data_source: messages:
- "message asking about the KEYWORD post or the free resource"
- "new message request from someone I do not have a thread with"
- "message saying I commented on your post" and "message saying I DMd you"
- "someone asking when they will receive the thing I promised"
C. Request surfaces. data_source: snapshots:
- "friend requests screen with pending request names"
- "LinkedIn invitations pending with names"
- "new follower notification"
D. Carry-forward. Same queries as A and B, with a date window covering the PREVIOUS campaign, to find people who raised a hand and never got a reply.
E. Campaign copy check. One query for the post itself, to read the wording. Meta
prohibits gating promised material behind required engagement, so if the post says the
material is only available by commenting, flag it. See references/platform-rules.md.
Read the relevance scores. Items scored 3 are maybes and do not carry a claim alone
(references/littlebird-mcp-reference.md).
4. Extract and dedupe
Follow references/signal-extraction-and-dedupe.md exactly. In short:
- Every extracted signal is a ROW carrying display name, signal type, event time, receipt, surface, verbatim text, and confidence.
- Deduplicate identical OCR lines before counting anything.
- Sort by event time, not by retrieval relevance.
- Merge rows into people using the matching ladder. Tiers 1 and 2 merge automatically. Tiers 3 through 6 require corroboration or a question.
- Surface ambiguous merges with
AskUserQuestion. Never merge silently. Batch them into one question. A bad merge sends a message to the wrong person and is invisible in the output; two unmerged rows are a duplicate the user fixes in a second. - Record every "and N others" and "N people reacted" string as a countable member of the unnamed gap, with its own receipt.
- Do not attribute the user's own replies or a comment bot's public replies to a
hand-raiser (
references/evidence-standards.md, rule 4).
5. Score and segment
Follow references/scoring-and-segmentation.md. Base points by signal (DM 5, keyword
comment 4, on-topic comment 4, friend or connection request 3, thread reply 3, reaction 1),
plus a combination bonus for multiple distinct signal TYPES, times a recency multiplier,
plus content adjustments. Segments: Hot, Warm, Light, Ambient, Carry-forward, Excluded.
Two things to state in the deliverable:
- This ranks priority. It does not qualify buyers. A hand-raiser is a marketing-qualified lead at best, and BANT-style qualification happens in the conversation that follows.
- The weights are a defensible starting point constructed from published recency and frequency scoring practice, not a validated instrument. The user should tune them. Show the arithmetic so they can.
6. Draft the first touches
Follow references/first-touch-drafting.md.
Short and casual, under 60 words for Hot and Warm. Four parts: the specific receipt of what they did, the thing they asked for, one question or none, and no pitch. Hot segment gets individually written messages. Warm gets a segment template with a mandatory personalization slot filled from that person's own verbatim. Ambient defaults to no individual DM at all, with a public thank-you offered instead.
If a personal voice skill is installed in this workspace, list the available skills, find
it, and draft through it. If none is installed, write plainly and say so in the
deliverable. Do not build a voice model from screen capture: capture shows what the user
was viewing, not what they wrote (references/evidence-standards.md, rule 4).
Draft at most the opener plus two follow-ups. The published lift concentrates on the SECOND follow-up, and the third onward is marginal.
7. Confirmation gates
Two gates, both AskUserQuestion (references/evidence-standards.md, rule 6).
Before encoding. Ambiguous merges, uncertain names, and anything about to be written down as durable fact about a person gets confirmed first.
Before sending. Present the actual draft TEXT, not a summary of it, and get explicit approval per batch. Approval attaches to text, never to a plan or a segment. Where a draft rests on a Low-confidence row, say so at the point of approval: a Low-rated claim never drives an irreversible action, and messaging a misidentified person is irreversible.
Then hand the approved drafts back for the user to send by hand. The skill does not send them.
Output
Write one file: campaign-harvest-YYYY-MM-DD.md, dated to the campaign end date, in
the user's working directory unless they name another location.
Sections, in this order:
- Campaign frame. Keyword, window, platform, post pointer, offer. As confirmed in step 1.
- Coverage report. Mandatory, and it comes second so nobody misses it. Named count, estimated total, coverage percentage, then the unnamed gap broken down by surface with receipts, then the pointer to the exact post and the instruction to run the capture protocol to close it. State that overlap between reaction and comment counts makes the estimated total a ceiling rather than a headcount.
- Ranked roster. Every person, by segment then score. Each row: rank, name, segment, score, every signal with its event time and receipt, the verbatim that matters, and a confidence rating.
- Ambiguous merges. Pairs the skill did not resolve, with both receipts, and how the user resolved them if they did.
- Excluded. Hostile rows, organization pages, unresolvable rows. Named with reasons. Never silently dropped.
- Carry-forward. People from the previous campaign who never got a reply.
- Drafted first touches. Grouped by segment, in full text, marked approved or pending.
- Scoring model used. The weights, so the user can audit and tune them.
- Compliance notes. Anything flagged: engagement-gating in the campaign copy, an expired 7-day Meta reply window, a channel change to email that triggers CAN-SPAM.
- Method and gaps. Which queries ran, what came back empty, and what the skill could not determine.
Raw retrieved capture does not go in this file. Process it in temp space and let it go
(references/evidence-standards.md, rule 7).
Guardrail
This skill builds a list of real people from a partial record and then writes messages to them. Four things follow from that, and none of them are optional.
- Only engagement on the user's OWN posts is harvestable. The campaign frame in step 1 pins the post, and step 3E reads the post itself to confirm it. Someone who commented on a competitor's post, a group thread, or a shared article did not raise a hand at the user. Harvesting them is scraping a stranger's audience, and it produces a first message nobody asked for.
- Never present a partial roster as complete. The coverage report is mandatory and it
is section 2 of the artifact for that reason. Social UIs collapse rosters into "and 4
others" and "12 people reacted", so the named set is a floor, never a total
(
references/evidence-standards.md, rule 5). Report the named count, the unnamed gap broken down by surface with receipts, and the capture protocol that closes it. Say that overlap between reaction and comment counts makes the estimated total a ceiling rather than a headcount. A roster with no coverage report is not finished. - Never automate a platform action and never auto-send. No bulk friend requests, no
scripted connection invites, no automated DM blast, no queued sequence, not even where a
connector in the session would technically allow it. The prohibitions, the rate limits
and the account-risk evidence are in
references/platform-rules.md. Every draft is handed back for the user to send by hand. - A private message's contents never enter a shared export. A direct message counts as
a signal: sender name, send time, receipt, and the fact that a message arrived. Pull the
verbatim only where it is needed to personalize that same person's first touch, and keep
it out of anything that leaves the user's own working file
(
references/evidence-standards.md, rule 7). Nothing derived from another person's private messages ends up in a shared artifact or a third-party system.
The draft-never-send law. Nothing is sent, posted, or written into a third-party system
without the user approving the actual final text or payload through AskUserQuestion. This
holds even when the user has approved the plan, the segment, or the scoring, because
approving a plan is not approving the words. Messaging a misidentified person is
irreversible, so a draft resting on a Low-confidence row says so at the point of approval.
Two more that bite in practice, both surfaced in the compliance notes of the artifact: engagement-gating in the campaign copy, which Meta prohibits, and a channel change to email, which brings CAN-SPAM into play. Surface the jurisdiction; do not give legal advice.
Empty retrieval
If the searches return nothing for the campaign window, stop and report the gap. Do not fabricate a roster.
Say exactly which queries ran, over which window, with which filters, and that they came back empty. Then give the user the two things that could fix it:
- Run the capture protocol in
references/capture-protocol.mdand re-run the harvest. The most common cause of an empty result is simply that the operator never had the comment thread open while Littlebird was watching. - Check the campaign window. A window off by a few days returns nothing.
A partial result is reported as partial. An empty result ends the run
(references/evidence-standards.md, rule 9).
Routine wiring: the active campaign watcher
Optional. Offer it only when the user says they run these campaigns constantly or has an
active window open. The routine OBSERVES, the Cowork session ACTS
(references/littlebird-mcp-reference.md, the Routines-observe Cowork-acts pattern).
Offer it. Do not tell the user to go set it up by hand. LB_INTERNAL_CREATE_ROUTINE works
from an interactive session and is only blocked from inside a running routine
(references/littlebird-mcp-reference.md, routine tools). Check
LB_INTERNAL_LIST_ROUTINES first for an existing watcher, show the user the exact prompt
text and the schedule, get approval with AskUserQuestion, then create it. Creating one
immediately generates a first report. There is a plan-based limit on routine count; check it
with LB_INTERNAL_GET_SUBSCRIPTION_STATUS if creation fails.
Schedule: {"frequency": "daily", "time": "17:00"} during the campaign window, in the
user's local timezone. Pausing, rescheduling or deleting it later happens the same way, from
an interactive session, with LB_INTERNAL_GET_ROUTINE_CONFIG read first because
LB_INTERNAL_UPDATE_ROUTINE REPLACES the whole prompt and the whole schedule.
Routine prompt text to pass, with the bracketed values substituted from step 1:
Read your own previous reports first before writing anything. If an item you already flagged is still unresolved, escalate it rather than restating it: say how many days running it has appeared and raise its priority in this report.
Search my captured context for the last 24 hours for activity on my [PLATFORM] campaign using the keyword [KEYWORD], posted on [DATE]. Run several narrow searches rather than one broad one. Cover: notification pages naming people who commented on my post, expanded comment threads, reaction lists, new direct messages and message requests mentioning the post or the offer, and pending friend or connection requests.
Write a short report with four parts.
First, the new hand-raisers you can NAME since your last report, each with the signal they gave, the time of the signal, and the receipt in the form [Day, Month DD, YYYY HH:MM TZ | app].
Second, the UNNAMED gap: every "and N others" or "N people reacted" string you saw, with the number, the surface it came from, and its receipt. Never present the named list as if it were complete.
Third, anyone who has now given a SECOND or THIRD distinct signal, for example someone who commented earlier and has now also sent a direct message or a friend request. Call these out at the top as the hottest leads.
Fourth, the action line. If the unnamed gap is larger than the named set, tell me to open the post, expand the full comment thread, and scroll it slowly for sixty seconds so capture can read the names. Then tell me to open Cowork and run the lead-harvester skill to build the ranked roster and draft the outreach.
If you find no new activity in the last 24 hours, say exactly that and stop. Do not invent names and do not repeat yesterday's list as if it were new.
Set notifications on so the user gets the nudge while the window is still open.
Handoff. When the user opens Cowork and runs this skill, read the routine's own past reports with the routine reports tool before running step 3. They give you the day-by-day timeline, the names captured while the thread was fresh, and the running gap count, which is strictly better than reconstructing it all from one retrieval at the end.
Evidence standards
Every claim in the deliverable follows references/evidence-standards.md. The rules that
bite hardest here:
- Rule 5, partial rosters. This skill's central obligation. Named set with receipts, count of unnamed entries and where they came from, and what the user can do to close the gap. A roster with no coverage report is not finished.
- Rule 1, receipts. Every signal carries one. For messages, the send time and the collection time are different values and both appear.
- Rule 2, observed and inferred. "Dani commented SYSTEM" is observed. "Dani is your hottest lead" is inferred from the signals it rests on. Mark which is which.
- Rule 4, attribution. Capture shows what the user was viewing. The operator's own replies and a comment bot's public replies are not hand-raiser signals.
- Rule 6, confirmation. Ambiguous merges before encoding. Actual draft text before sending.
- Rule 8, timelines. Sort by event time. Retrieval returns relevance order.
- Rule 10, reporting on people. Purpose-bound to the campaign. Provenance on every line. Sensitive categories stay out even when the capture contains them.
Related skills
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 83
- Forks
- 37
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
lead-harvester- Source
- github.com/legioncodeinc/vibe-coding-tools