Files
linkedin_skills/onboard/SKILL.md
T
2026-04-02 15:30:23 +08:00

7.9 KiB
Raw Blame History

name, version, description, allowed-tools
name version description allowed-tools
onboard 1.0.0 First-run onboarding. Analyzes the user's landing page to suggest an ICP, asks for confirmation and any additions, then collects timezone + cadence. Saves everything to state/linkedin-settings.json and marks firstRunCompleted: true. Run this once before using any other skill.
Read
Write
AskUserQuestion
Bash

Voice

Clear and helpful. You're doing the heavy lifting — the user just confirms. Never dump a blank form. Always propose first, then ask for corrections.

Completion Status Protocol

  • DONE — Settings saved, firstRunCompleted set to true.
  • BLOCKED — Cannot proceed. State what failed and what was tried.
  • NEEDS_CONTEXT — Missing required input. State exactly what you need.

Step 0: Check if already set up

Read state/linkedin-settings.json.

If firstRunCompleted is true, say:

Setup already complete. Your ICP is loaded in state/linkedin-settings.json.
To re-run setup, set "firstRunCompleted": false in that file and run /onboard again.

Then stop.

If the file doesn't exist, copy defaults from config/default-settings.json and continue.


Step 1: Get the landing page URL

Ask:

Context: First-time setup for LinkedIn outreach.
What is your product or service landing page URL?
(e.g. https://yourcompany.com — I'll analyze it to suggest your ICP)

Step 2: Analyze the landing page

Call the proxy with bash:

curl -s -X POST https://landing-page-analysis-proxy.vercel.app/api/crawl \
  -H "Content-Type: application/json" \
  -d "{\"url\": \"<URL_FROM_STEP_1>\"}"

Parse the returned markdown content.

From the content, extract and infer:

  • industries — what industries does this product serve?
  • seniority — what job levels are likely buyers? (look for "for teams", "for founders", "for managers", etc.)
  • companySizes — any signals about company size? (e.g. "enterprise", "startups", "SMBs")
  • geographies — any regional focus mentioned?
  • alwaysExcludeRoles — any roles clearly not the target?
  • offer summary — one sentence: what does this product do and who is it for?
  • disqualifyCategories — always include competitors and non_decision_makers. Then derive 15 additional categories from the product analysis. Each should be a short snake_case label with a plain-English description. Only add categories that genuinely follow from what the product does and who it serves — do not pad. Examples:
    • A B2B SaaS tool → b2c_focused (companies selling to consumers, not businesses)
    • An outbound sales tool → no_outbound_intent (companies with no sales motion)
    • A hiring tool → no_hiring_activity (companies not actively hiring)
    • A compliance tool → non_regulated_industries (industries without compliance requirements)
    • A developer tool → non_technical_teams (teams with no engineering function)
    • A PLG product → no_self_serve_motion (companies that only buy through procurement)

Also infer the competitor profile — who would be disqualified from outreach because they are competitors or would never buy:

  • competitorDescription — one sentence: what kind of company is a competitor? (e.g. "Companies that offer similar outreach automation or lead generation tools")
  • competitorCompanyKeywords — 510 lowercase keywords that would appear in a competitor's company name (e.g. ["lead gen", "sales automation", "crm", "sequencer"])
  • competitorIndustryKeywords — 36 lowercase keywords for competitor industries (e.g. ["sales enablement", "marketing automation"])
  • competitorServiceKeywords — 815 lowercase phrases describing the specific services a competitor would offer, grounded in what this product does (e.g. if the product automates LinkedIn outreach, then: ["linkedin outreach", "automated prospecting", "cold outreach", "outbound sequences", "sales cadence", "prospect automation"]). These will be matched against a lead's company description or bio, so phrases should reflect how competing services describe themselves.

If the proxy call fails, say:

BLOCKED: Could not fetch the landing page.
Error: [error message]
Please check the URL and try again, or provide your ICP manually.

Then ask the user to provide ICP fields manually and skip to Step 4.


Step 3: Present suggested ICP for confirmation

Show the extracted ICP and competitor profile in this format:

Based on your landing page, here's your suggested ICP:

Offer:          [one-sentence summary]
Industries:     [list]
Seniority:      [list]
Company size:   [list or "not specified"]
Geographies:    [list or "not specified"]
Exclude roles:  [list]

Disqualify — leads in these buckets will be filtered out:
  [for each category: "- category_label: plain English description"]

And here's the inferred competitor profile (used to auto-filter leads):

Competitors:    [competitorDescription]
Co. keywords:   [competitorCompanyKeywords joined with ", "]
Industries:     [competitorIndustryKeywords joined with ", "]
Services:       [competitorServiceKeywords joined with ", "]

Does this look right?
A) Yes, looks good
B) Some changes needed — tell me what to adjust
C) Ignore this, I'll fill in everything myself

If B: apply the user's corrections to both the ICP and the competitor profile before continuing. If C: ask for each ICP field individually, then ask for competitor description and keywords.


Step 4: Collect conversion goal

The user already provided a landing page URL in Step 1. Use it as the default. Ask:

What do you want an interested prospect to do?

A) Visit your landing page — [URL from Step 1]
B) Book a call — paste a Calendly or booking link
C) Fill out a form — paste the form URL
D) Receive a lead magnet — paste the link
E) Subscribe to a newsletter — paste the link
F) Other — describe it

Wait for the user to reply before continuing. Do not assume A.

If user confirms A: set outreach.conversionGoal.type to landing_page and outreach.conversionGoal.link to the URL from Step 1. Otherwise: save their choice as type and their link as the URL. If no link given, leave link as null. Do not block progress.


Step 5: Save settings

Write the confirmed ICP + preferences into state/linkedin-settings.json:

  • targetMarket.industries
  • targetMarket.seniority
  • targetMarket.companySizes
  • targetMarket.geographies
  • targetMarket.alwaysExcludeRoles
  • qualification.disqualifyCategories — array of snake_case labels derived in Step 2 (e.g. ["competitors", "non_decision_makers", "b2c_focused"])
  • outreach.conversionGoal.type
  • outreach.conversionGoal.link (may be null)
  • firstRunCompletedtrue

Also write the competitor profile to state/competitor-profile.json:

{
  "generatedAt": "<ISO timestamp>",
  "sourceUrl": "<landing page URL>",
  "description": "<competitorDescription>",
  "companyKeywords": ["<keyword1>", "<keyword2>", "..."],
  "industryKeywords": ["<keyword1>", "<keyword2>", "..."],
  "serviceKeywords": ["<phrase1>", "<phrase2>", "..."]
}

Step 6: Confirm and hand off

Output:

SETUP COMPLETE
══════════════════════════════════════
Offer:        [summary]
Industries:   [list]
Seniority:    [list]
Company size: [list]
Geographies:  [list]
Goal:         [conversionGoal.type] — [conversionGoal.link or "no link set"]

Settings saved to: state/linkedin-settings.json
Competitor profile: state/competitor-profile.json

What's next:
  Step 1 → /calibrate-voice   Build your voice profile so every message sounds like you, not a bot.
  Step 2 → /linkedin-lead-filter   Filter your CSV leads against your ICP.
  Step 3 → /draft-outreach   Draft personalized first-touch messages for qualified leads.
══════════════════════════════════════