npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hasna/experts

v0.0.8

Published

Crawl expert marketplaces (intro.co and more) into a local store, then query them via CLI or a remote HTTP API.

Readme

@hasna/experts

Crawl expert marketplaces into a local store, then query them from the command line or over HTTP. intro.co ships built-in; the architecture is source-agnostic, so other marketplaces are added as small adapters.

$ experts list --topic "Career & Business" --limit 3
   $350  ★5.00  Raad Mobrem ✓        CEO of Intro. 2x Entrepreneur
   $125  ★5.00  David Greenfeld TOP ✓  Founder & CEO of Dream Pops
   $275  ★5.00  Jason Feifer TOP ✓     Editor in Chief of Entrepreneur Magazine

Why

Expert/advisor marketplaces hide their full roster behind infinite-scroll UIs. experts pulls the whole catalog once (politely — throttled, with backoff), stores it in SQLite, and gives you fast local search, filtering and stats so you can actually understand who is on a platform, what they charge, and how they rank — without clicking through hundreds of profiles.

Install

bun install -g @hasna/experts

Requires Bun ≥ 1.0. Two binaries are installed:

  • experts — the CLI
  • experts-serve — a read-only HTTP API over the same store

Quick start

experts crawl intro       # fetch the full intro.co catalog (~2,300 experts)
experts stats             # summary: counts, price range, avg rating
experts list --top        # featured experts, highest-rated first
experts search "growth"   # full-text search over name, title and bio
experts show AlexisOhanian

Commands

| Command | What it does | | --- | --- | | crawl [source] | Fetch experts from a source into the store (default: intro) | | enrich [source] | Enrich via X/Twitter — profile, recent tweets, avatar | | enrich-youtube [source] | Fetch recent YouTube videos for experts with a YT handle | | enrich-linkedin [source] | LinkedIn headline/company/about (needs linkedin connector auth) | | enrich-sites [source] | Fetch experts' personal sites → text summary | | avatars [source] | Download + properly name profile pictures (no API) | | find-contacts [source] | Discover email + phone via Exa.ai websets | | verify-contacts [source] | Validate contacts → working/not status | | embed [source] | Build the semantic search index | | ask <query…> | Natural-language semantic search | | brief <text…> | Ranked, de-duplicated shortlist for a problem statement | | find <needs…> | Knowledge-graph search — experts who cover specific needs | | related <id\|slug> | Experts who share the most expertise with this one | | list / search <q…> | Filtered listing / full-text search | | show <id\|slug> | Full profile (X, tweets, videos, contacts, LinkedIn, site) | | tweets <id\|slug> | Recent tweets (--live to fetch fresh) | | videos <id\|slug> | Recent YouTube videos | | contacts <id\|slug> | Discovered emails/phones with working/not status | | graph / persons / changes / stale | Graph, identity, change-log, staleness overviews | | topics / tags / stats | Categories, tags, aggregate statistics | | sync-contacts <ids…> | Export experts + contacts to the contacts system | | reindex | Rebuild graph + authority + persons (no network) | | export / sources | Dump store (JSON/CSV) / list marketplace adapters |

Global options

  • --db <path> — use a specific database file (default ~/.hasna/experts/experts.db, or $OPEN_EXPERTS_DB)
  • --json — machine-readable output for list, search, show, topics, tags, stats

crawl

experts crawl intro                 # full catalog + topic membership + tags
experts crawl intro --no-topics     # catalog only (fewer requests)
experts crawl intro --max 100       # cap for a quick run
experts crawl intro --delay 1000    # 1s between requests (default 500ms)

The crawler throttles every request and backs off on 429/5xx, so it stays a polite guest on the upstream API.

list

experts list --topic "Wellness" --verified --sort price --asc -n 20
experts list --min-price 100 --max-price 500 --min-rating 4.9

Filters: --source, --topic, --verified, --top, --min-price, --max-price, --min-rating. Sort with --sort rating|price|name|reviews (--asc to reverse), limit with -n.

Enrichment — X/Twitter profiles, tweets, avatars

enrich augments each expert that has a Twitter handle with live data the marketplace doesn't expose: their current X bio ("what they work on"), follower counts, their last-30-day tweets, and a properly-named profile picture downloaded to ~/.hasna/experts/avatars/<source>/<name>.jpg.

experts enrich intro                 # all experts with a handle (resumable)
experts enrich --max 50              # process 50 (resume later to continue)
experts enrich --refresh             # re-enrich already-done experts
experts enrich --since-days 14       # tweet window
experts enrich --no-avatars          # skip image downloads
experts tweets AlexisOhanian         # show stored tweets

It runs through the @hasna/connectors x connector, throttles requests, and stops gracefully when rate-limited so you can re-run the same command to pick up where it left off. Enriched data shows up in show and feeds the knowledge graph (X bios sharpen tag inference).

Knowledge graph — search by what you need

Every crawl builds a small graph in SQLite: expert, topic and tag nodes joined by IN_TOPIC and HAS_TAG edges. Per-expert expertise tags are inferred by matching the marketplace's tag vocabulary against each expert's title and bio, so you can search by capability — not just keywords.

experts find Fundraising Startups      # experts who cover BOTH (ranked)
experts find SEO Growth --any          # experts who cover EITHER
experts find Wellness Yoga --limit 10  # topics and tags are interchangeable needs
experts related AlexisOhanian          # peers who share the most expertise
experts graph                          # node/edge counts + most-covered expertise
experts reindex                        # rebuild the graph without re-crawling

find prints how many of your needs each expert matched ([2/2]). The graph is rebuilt automatically at the end of every crawl; reindex regenerates it from the stored data when you tweak the vocabulary.

export

experts export --format csv -o experts.csv
experts export --format json --source intro > experts.json

HTTP API

Run the server to query the store remotely:

experts-serve              # listens on :7077 (override with PORT)

| Endpoint | Description | | --- | --- | | GET /health | Service status, expert count, last crawl time | | GET /sources | Available adapters and how many experts are stored | | GET /experts | List/filter (source, topic, verified, top, min_price, max_price, min_rating, sort, asc, limit) | | GET /experts?q=… | Full-text search | | GET /experts?needs=a,b&match=all | Need-based graph search | | GET /experts/:source/:idOrSlug | One expert | | GET /find?needs=a,b&match=all | Need-based graph search (with match counts) | | GET /related/:source/:idOrSlug | Experts sharing the most expertise | | GET /graph | Knowledge-graph overview | | GET /topics | Topics with counts | | GET /tags | Expertise tags | | GET /stats | Aggregate statistics | | POST /crawl/:source | Trigger a crawl (disabled unless EXPERTS_ALLOW_CRAWL=1) |

curl "http://localhost:7077/experts?topic=Wellness&min_rating=4.9&limit=5"
curl "http://localhost:7077/experts?q=reddit"

Config via env: PORT, OPEN_EXPERTS_DB, EXPERTS_ALLOW_CRAWL.

SDK & SaaS

A typed HTTP client ships at @hasna/experts/sdk for consuming a running experts-serve instance from another service:

import { ExpertsClient } from "@hasna/experts/sdk";

const experts = new ExpertsClient({ baseUrl: "http://localhost:7077" });
const matches = await experts.find(["Fundraising", "Startups"], { limit: 10 });
const profile = await experts.expert.get("intro", "AlexisOhanian");

The core is built to be wrapped by a separate SaaS repo (platform-experts, mirroring platform-todos / @hasna/todos). See docs/PLATFORM.md for the open-core ↔ platform architecture and the three integration surfaces (library, HTTP API, SDK).

What gets stored

Each expert is normalized to a common, source-agnostic shape:

{
  "source": "intro",
  "sourceId": "177804",
  "slug": "AlexisOhanian",
  "url": "https://intro.co/AlexisOhanian",
  "fullName": "Alexis Ohanian",
  "title": "Tech Entrepreneur + Investor",
  "headline": "Founder of Reddit, Initialized & 776",
  "bio": "Co-founder of Reddit…",
  "price": 2000,
  "priceCurrency": "USD",
  "priceUnit": "15 min video call",
  "rating": 4.98,
  "ratingCount": 50,
  "verified": true,
  "featured": true,
  "topics": ["Career & Business", "Top Experts"],
  "tags": [],
  "socials": { "instagram": "…", "twitter": "…", "tiktok": "…" },
  "extra": { "charityName": "776 Foundation", "charityPercent": 100, "exampleQuestions": ["…"] }
}

Anything marketplace-specific (tiered pricing, charity split, example questions, …) lives under extra, keeping the common schema clean across sources.

Adding a new source

Implement the Source interface and register it — that's the whole extension surface:

import { registerSource, type Source } from "@hasna/experts";

class MyMarketplace implements Source {
  name = "mysite";
  description = "My expert marketplace";
  website = "https://mysite.example";
  async crawl(opts) {
    // fetch + map to the common Expert shape
    return { experts, topics, tags, total };
  }
}

registerSource(new MyMarketplace());

The CLI (experts crawl mysite) and HTTP API pick it up automatically.

Development

bun install
bun test          # 102 tests
bun run typecheck
bun run build     # → dist/
bun run dev list  # run the CLI from source

License

Apache-2.0 © Hasna