Claude
Skills
Sign in
Back

network-scan

Included with Lifetime
$97 forever

Scan your LinkedIn contacts' companies for matching job openings

Generalscripts

What this skill does


# Network Scan Skill

> **Priority hierarchy**: See `shared/references/priority-hierarchy.md` for conflict resolution.

Proactively check whether companies where you know someone are hiring for roles that match you. First run builds a cache of company careers page URLs. Subsequent runs reuse the cache, making weekly checks fast.

## Quick Start

- `/proficiently:network-scan` - Scan companies from your 25 most recent contacts
- `/proficiently:network-scan 50` - Check the 50 most recent contacts
- `/proficiently:network-scan all` - Check all contacts (can be slow with 1500+)

## File Structure

```
scripts/
  resolve-careers.md     # Subagent for resolving a batch of company careers URLs
  evaluate-company.md    # Subagent for scanning a batch of companies' open roles
```

User data (stored at ~/.proficiently/):
```
~/.proficiently/
  resume/                # Your resume PDF/DOCX
  preferences.md         # Job matching rules
  profile.md             # Work history from interview
  linkedin-contacts.csv  # LinkedIn contacts export
  company-careers.json   # Cached company careers URLs
  network-scan-history.md # Running log of scan results
  jobs/                  # Per-job application folders
```

---

## Workflow

### Step 0: Check Prerequisites

Resolve the data directory, then check prerequisites per `shared/references/prerequisites.md`. Resume, preferences, and linkedin-contacts.csv are all required.

Load these files for use in later steps:
- `DATA_DIR/preferences.md` (target roles, must-haves, dealbreakers, nice-to-haves)
- `DATA_DIR/resume/*` (candidate profile)
- `DATA_DIR/profile.md` (work history, if it exists)

### Step 1: Select Contacts & Extract Companies

Parse `$ARGUMENTS`:
- If a number (e.g., `50`): use that as the contact limit
- If `all`: use all contacts (warn user this may be slow if > 200)
- If empty/missing: default to 25

Read `~/.proficiently/linkedin-contacts.csv`. Sort by "Connected On" descending (most recent first). Take the first N contacts based on the limit.

Extract unique company names from the selected contacts. Skip companies with empty or blank names.

Group contacts by company into a lookup:
```
{
  "Google": [{"name": "Jane Smith", "position": "PM Director", "url": "https://linkedin.com/in/janesmith"}, ...],
  "Stripe": [{"name": "John Doe", "position": "Eng Manager", "url": "https://linkedin.com/in/johndoe"}]
}
```

Report to user: "Found X unique companies from Y contacts. Checking careers pages..."

### Step 2: Resolve Careers Pages (Parallelized)

Load `~/.proficiently/company-careers.json` if it exists (the cache). If it doesn't exist, start with an empty object.

Split companies into three groups:
- **Cached (fresh)**: `last_checked` within last 7 days - use as-is, no work needed
- **Cached (stale)**: `last_checked` older than 7 days - needs re-verification
- **Uncached**: not in cache, or `type` is `"not_found"` and stale - needs full resolution

Report: "X companies from cache, Y need resolution..."

**Parallel resolution using subagents:**

Take all companies needing resolution (stale + uncached) and split them into batches of 10. Spawn one subagent per batch using the Task tool (`subagent_type: "general-purpose"`). Run all batches in parallel.

Each subagent receives:
- A batch of company names to resolve
- Instructions from `scripts/resolve-careers.md`

Each subagent uses `WebSearch` (NOT the browser) to find careers pages:
1. Search: `"[Company Name]" careers jobs site:[company domain if known]`
2. From the search results, identify the careers/jobs page URL
3. Classify the URL type:
   - `"direct"` - company's own careers page (e.g., careers.google.com)
   - `"greenhouse"` - Greenhouse ATS (boards.greenhouse.io/company or company.greenhouse.io)
   - `"lever"` - Lever ATS (jobs.lever.co/company)
   - `"workday"` - Workday ATS (company.wd5.myworkdayjobs.com)
   - `"other_ats"` - other ATS platforms (Ashby, BambooHR, etc.)
   - `"not_found"` - no careers page could be found (set `careers_url` to null)
4. Return results for the batch

Collect results from all subagents and merge into the cache. Save `~/.proficiently/company-careers.json`. Format:
```json
{
  "Company Name": {
    "careers_url": "https://careers.example.com",
    "type": "direct",
    "last_checked": "YYYY-MM-DD",
    "last_found_roles": 0
  }
}
```

Report progress: "Resolved X new careers pages, Y from cache, Z not found."

### Step 3: Scan for Matching Jobs (Parallelized)

Take all companies with a valid `careers_url` (skip `not_found` and `ignored` entries). Split them into batches of 5 companies each.

**Spawn parallel subagents** using the Task tool (`subagent_type: "general-purpose"`). Run all batches in parallel (up to 5 concurrent subagents to avoid overwhelming the browser).

Each subagent receives:
- A batch of companies (name, careers_url, ATS type, network contacts)
- Candidate profile summary (from resume)
- Preferences (target roles, must-haves, dealbreakers, nice-to-haves)
- Instructions from `scripts/evaluate-company.md`

Each subagent:
1. Creates its own browser tab (`tabs_context_mcp` then `tabs_create_mcp`)
2. For each company in its batch:
   a. Navigate to the careers page
   b. Search/browse for roles matching target roles and keywords
   c. For ATS pages, use platform search/filter functionality:
      - **Greenhouse**: search box or department filters
      - **Lever**: search bar or team filter
      - **Workday**: keyword search field
      - **Direct/other**: browse the page, use any search, scan listed roles
   d. Extract listings: title, location, URL
   e. Score each listing (High/Medium/Low/Skip per fit criteria)
   f. Return only High and Medium fits
3. Returns results for its entire batch

**Fit scoring criteria:** See `shared/references/fit-scoring.md` for the canonical definitions.

Collect results from all subagents. Update `last_found_roles` count in the cache for each company scanned.

If a subagent fails or times out, log the companies it was processing and move on. Do not retry - the user can re-run with those companies next time.

### Step 4: Save Results

**Update company-careers.json:**
Update `last_checked` and `last_found_roles` for every company that was scanned.

**Append to `~/.proficiently/network-scan-history.md`:**

If the file doesn't exist, create it with:
```markdown
# Network Scan History

This file tracks all network scans run by the `/network-scan` skill.

---
```

Then append:
```markdown
## YYYY-MM-DD - Network Scan (N contacts, M companies)

| Company | Contact | Role Found | Fit | URL |
|---------|---------|------------|-----|-----|
| Google | Jane Smith (PM Director) | Sr. Product Manager | High | https://... |
| Stripe | John Doe (Eng Manager) | No matching roles | - | - |
```

Include all companies scanned (both matches and non-matches) in the table.

**Save full postings for High-fit matches:**
For each High-fit match, navigate to the job posting URL and save the full posting to `~/.proficiently/jobs/[company-slug]-[YYYY-MM-DD]/posting.md` using the standard format:

```markdown
# [Job Title] - [Company Name]

**Company**: [Company]
**Location**: [Location]
**Salary**: [Salary or N/A]
**Type**: [Type]
**Source**: network-scan
**Date Found**: YYYY-MM-DD
**Network Contact**: [Contact Name] ([Position]) - [LinkedIn URL]

## About the Role
[Description]

## Key Requirements
- [requirement]

## Direct Careers Page
- [URL]

## Fit Assessment
**Rating**: [High/Medium]
**Why**: [explanation]
```

### Step 5: Present Results

Show matches grouped by fit, with contact info for warm introductions:

```markdown
## Network Scan Results - YYYY-MM-DD
Scanned N companies from M contacts.

### Matches Found

#### 1. Senior Product Manager at Google
- **Fit**: High
- **Your contact**: Jane Smith (PM Director) - [LinkedIn](url)
- **Location**: Mountain View, CA
- **Apply**: https://careers.google.com/jobs/...
- **Why**: [brief match reason]

#### 2. Strategy Lead at Stripe
- **Fit**: Mediu

Related in General