Skip to content

feat: event-prospecting skill v0.1#78

Closed
jay-sahnan wants to merge 20 commits into
mainfrom
event-prospecting
Closed

feat: event-prospecting skill v0.1#78
jay-sahnan wants to merge 20 commits into
mainfrom
event-prospecting

Conversation

@jay-sahnan
Copy link
Copy Markdown
Contributor

@jay-sahnan jay-sahnan commented Apr 25, 2026

event-prospecting — design

Status: approved 2026-04-25
Sibling skills: company-research, cold-outbound, account-positioning, competitor-analysis, github-stargazers
Path: /Users/jay/skills/skills/event-prospecting/

Purpose

Take a conference / event speakers URL, extract the people, filter their companies against the user's ICP, then deep-research only the people at ICP-fit companies. Output a person-first HTML report where each card answers "why should the AE talk to this person?" with all their public links and a one-click DM opener.

Architectural shape: a thin front-end (event recon + extract) and back-end (person enrichment + HTML) wrapped around the company-research backend. Most of the cost lives in company-research; this skill is glue + a different report.

What problem this solves

GTM teams scrape conference speaker lists into spreadsheets manually or via Clay / Apify actors. The work that follows — figuring out which speakers actually matter for THIS sender, what the wedge is, and how to open the conversation — stays manual. This skill collapses that to one command per event.

Five gaps this targets (per find-skills research, 2026-04-25):

  1. JS-rendered / Next.js speaker pages — most existing tools degrade
  2. Sponsor → buying-committee mapping — tools dump company names but don't fan out
  3. ICP scoring tied to event context (track / sponsor tier / talk topic) — generic ICP isn't enough
  4. Multi-event recurrence — "this person spoke at 4 events I care about"
  5. Open agent-native skill that chains scrape → enrich → score → CSV in one install

v0.1 hits #1, #3, #5. #2 and #4 are v0.2 candidates.

Pipeline (10 steps)

Step What Reuses
0 Setup output dir ~/Desktop/{event_slug}_prospects_{YYYY-MM-DD-HHMM}/
1 Load user profile from profiles/{slug}.json. Fail loudly if missing — point at company-research to build one. company-research profile shape
2 Recon the event URL. Detect platform (Next.js __NEXT_DATA__, Sessionize public API, Lu.ma JSON-LD, Eventbrite JSON-LD, custom). Persist recon.json. NEW
3 Extract people using platform-specific shortcut. For Next.js: 4 browse commands extract structured speaker JSON. Output people.jsonl. NEW
4 Group by .company → unique seed company list (e.g. 240 people → 99 companies). NEW (~5 LOC)
5 ICP triage — for each unique company, fetch homepage + score against user ICP (one tool call per company). extract_page.mjs
6 Filter companies by icp_fit_score >= --icp-threshold (default 6). NEW (~10 LOC)
7 Deep research the ICP-fit companies — full Plan→Research→Synthesize pattern, one subagent per company. Writes companies/{slug}.md with frontmatter scores. company-research's references/research-patterns.md verbatim
8 Enrich speakers at ICP-fit companies only. Per person: LinkedIn snippet, recent activity (podcast / blog / talk / GitHub / X) for hook generation, harvest all public links. Writes people/{slug}.md. NEW
9 Compile HTML report — person-first index, plus per-person + per-company drill-downs. Open in browser. Write results.csv for cold-outbound. extends company-research's compile_report.mjs

Per-person card design

The primary deliverable. The index is a stack of these, ranked by ICP score.

┌────────────────────────────────────────────────┐
│  {Name}                              ICP {NN}  │
│  {Title} · {Company}                           │
│                                                │
│  🔗 {LinkedIn} {X} {GitHub} {Blog} {Podcast}   │  ← all public links found

---

## Phase E validation

Ran the full pipeline as a dry-run (recon → extract → manual ICP stubs → enrich → compile) against Stripe Sessions 2026 with the browserbase profile. Artifacts at `/tmp/sf_e2e_1777152213/`:
- `recon.json` → platform=next-data, 2 paths (correct)
- `people.jsonl` → 121 lines, `seed_companies.txt` → 98 (after host-org filter)
- `index.html` renders 3 person cards with correct ICP badges (high/high/mid)
- `people.html` and `companies.html` both written
- Copy DM opener + Research deeper buttons have populated `data-clipboard`

One bug found and fixed: Step 5's batch builder pulled `p.companyUrl`/`p.website` from `people.jsonl` but `extract_event.mjs` doesn't emit those fields. Replaced with a slugify-based homepage guess; the `Unknown — homepage content not accessible` fallback in `workflow.md` rule 3 absorbs misses.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Adds two new Claude skills with multiple scripts, large prompt/workflow docs, and HTML templates; primary risk is operational (tool-call limits, brittle recon/extraction heuristics, and report generation) rather than changes to existing runtime code.
> 
> **Overview**
> Introduces a new `event-prospecting` skill that performs event URL recon, extracts speaker lists (Next.js `__NEXT_DATA__` plus markdown fallback), filters out host/user-company employees, and defines a subagent-driven pipeline for ICP triage, deep company research, and person enrichment that compiles into person-first HTML/CSV outputs.
> 
> Adds the supporting skill package structure (`SKILL.md`, profiles, fixtures, and reference docs for platform detection, research patterns, and workflow/tool-call caps) plus a reusable report template.
> 
> Separately adds a new `audit-agent-experience` skill (MIT licensed) with a full methodology/rubric, subagent brief + prompt variants, and an HTML report template for benchmarking onboarding/documentation DX via parallel agent runs.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 2a8ed253b29d1c5daf182507c251ff59cae01938. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

jay-sahnan and others added 20 commits April 25, 2026 09:07
Spawns parallel Claude subagents against a target docs/SDK/SKILL.md from a
one-sentence prompt, captures structured traces, and renders a graded HTML
report scoring Setup Friction, Speed, Efficiency, Error Recovery, and Doc
Quality. Includes narrative cross-agent review to surface convergent
hallucinations and silent workarounds the JSON self-report misses.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-event prospecting skill that wraps company-research:
- Recon event URL + extract people via platform-specific shortcuts
  (Next.js __NEXT_DATA__, Sessionize API, Lu.ma JSON-LD)
- Company-first ICP filter before person enrichment (~45% cheaper
  than enrich-everyone)
- Person-first HTML report with 3-bullet "why reach out" + DM
  opener + clipboard "research deeper" trigger

Validated on Stripe Sessions: 4 browse commands extracted 240
speakers / 99 companies in 6s.

Reuses extract_page.mjs, compile_report.mjs, profiles, and the
Plan→Research→Synthesize pattern from company-research.

Sibling event-follow-up skill (post-event artifacts → CRM-ready
notes) explicitly out of scope for v0.1.
5-phase TDD-style plan: scaffolding+recon → extract+group →
ICP triage delegated to company-research → person enrichment +
HTML report → end-to-end validation on Stripe Sessions.

Each phase produces a runnable artifact. Tool-call caps + format
normalization patterns inherited from competitor-analysis.
Three fixes per skill-creator principles:
- New Task A5 creates references/event-platforms.md (was named in
  the design doc but had no creation task)
- Task C2 now copies the Plan→Research→Synthesize block VERBATIM
  from company-research instead of cross-referencing its filesystem
  path at runtime — skills must be self-contained
- Tasks A5 and C2 both gain explicit "add TOC" steps for files
  that will exceed 100 lines (skill-creator requirement)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…time

Step 5's batch builder previously read p.companyUrl / p.website from
people.jsonl, but extract_event.mjs only emits {name,title,company,
linkedin,bio,slug} — no URL field. Result: every triage subagent got
empty URLs after the | separator and would either guess wildly or burn
a second tool call to discover the homepage, busting the 1-call
HARD CAP.

Fix: slugify the company name and guess https://{name-no-spaces}.com.
Most common names map correctly; the few that 404 hit the documented
'Unknown — homepage content not accessible' fallback in workflow.md
rule 3. Also add a third field (slug) to the batch line so subagents
write to the canonical filename without re-slugifying.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jay-sahnan jay-sahnan closed this Apr 25, 2026
@jay-sahnan jay-sahnan deleted the event-prospecting branch April 25, 2026 21:33
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2a8ed25. Configure here.

if (depth === 'deeper') {
const r3 = bbSearch(`"${name}" github`);
const r4 = bbSearch(`"${name}" site:x.com OR site:twitter.com`);
out.links = { ...out.links, ...harvestLinks([...(r3.results || []), ...(r4.results || [])]) };
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lane 3 spread overwrites previously harvested links with null

Medium Severity

In deeper mode, Lane 3 merges links via { ...out.links, ...harvestLinks([...]) }. The harvestLinks function always initializes a fresh object with linkedin: linkedinIn || null (and other fields as null). When linkedinIn is empty (the default), any LinkedIn URL discovered in Lane 1 gets overwritten with null by the spread from Lane 3's harvestLinks return, since the GitHub/X search results are unlikely to also contain a LinkedIn URL.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2a8ed25. Configure here.

for (const row of deduped) {
csvLines.push(cols.map(c => csvEscape(row[c] || '')).join(','));
}
writeFileSync(join(dir, 'results.csv'), csvLines.join('\n') + '\n');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSV export contains only company data, missing people

Medium Severity

The results.csv iterates over deduped (the companies array) and only writes company-level fields. The SKILL.md and design doc describe this CSV as "cold-outbound-ready," but cold outbound requires person-level data — name, title, company, LinkedIn URL, hook, and DM opener. As written, the CSV is a company scorecard, not an actionable prospect list.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2a8ed25. Configure here.

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Company Research — {{COMPANY_NAME}}</title>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Report template title and footer reference wrong skill

Low Severity

The report-template.html was copied from company-research without updating the <title> tag (still says "Company Research — {{COMPANY_NAME}}") or the footer (still credits company-research). The compile_report.mjs replaces {{COMPANY_NAME}} but can't fix the hardcoded "Company Research" prefix, so the browser tab title is wrong for every generated report.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2a8ed25. Configure here.

<div class="stat"><div class="label">Completed</div><div class="value ok">{{COMPLETED_COUNT}}</div></div>
<div class="stat"><div class="label">Stuck</div><div class="value warn">{{STUCK_COUNT}}</div></div>
<div class="stat"><div class="label">Errored</div><div class="value bad">{{ERRORED_COUNT}}</div></div>
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Audit template missing critical narrative review placeholder

Medium Severity

The SKILL.md Step 9 references {{NARRATIVE_REVIEW_SECTION}} and {{AGENT_TRACES_SECTION}} as placeholders to fill in the template, but neither exists in report-template.html. The narrative review is described as "CRITICAL" (Step 6.5) and should appear "right after the scorecard," yet there is no placeholder between the scorecard and the executive summary sections where it would be inserted.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2a8ed25. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant