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

@schlessera/brain-module-jobs

v0.6.2

Published

brain-kit module: job-search pipeline — board scraping, scoring, dedup, triage

Readme

@schlessera/brain-module-jobs

A brain-kit module that runs a personal job-search pipeline: it scrapes remote-job boards, deduplicates and full-text-indexes the postings in its own SQLite database, scores each one against criteria you define, and gives you a CLI (brain jobs …) to triage the results and scaffold opportunity notes into your brain.

Install / enable

Add it to brain.config.ts:

export default defineConfig({
  modules: {
    "@schlessera/brain-module-jobs": {
      criteria: "career/opportunities/search-criteria.md",
      opportunitiesDir: "career/opportunities",
      boards: ["remoteok", "remotive", "weworkremotely"],
      // queries: ["staff engineer", "platform engineer"],  // for query-driven boards
      // dbPath: "jobs.db",                                   // default: <root>/jobs.db
    },
  },
});

Contributed taxonomy type

| Type | Default dir | Index anchor | | ------------- | ---------------------- | ------------ | | opportunity | career/opportunities | status.md |

brain jobs scaffold <id> creates career/opportunities/<company-slug>/status.md for a job you're interested in, and marks the job interested in the jobs DB.

Relocating the dir. Set opportunitiesDir in the module's config block — the manifest's setup() derives the opportunity taxonomy dir from it, so the CLI's write target and path→type inference move together. No separate taxonomy override needed.

Index-sync rule

The module contributes the dir anchor status.md, so a wiki-link to an opportunity directory ([[career/opportunities/acme]]) resolves to that dir's status.md.

Cron

Advisory schedule (consumed by container entrypoints / brain doctor): scrape daily at 06:00 — jobs scrape --all --browser, i.e. one unified run covering both the API boards and the headless-Chrome ones. The Chrome pass is skipped cleanly when no browser is reachable, so a host without Chrome loses the browser boards rather than the whole scrape.

The scoring criteria file

Scoring is not hard-coded. It is driven by the YAML frontmatter of the markdown file named by criteria (which stays brain content you own and tune). Copy docs/criteria-template.md to that path and edit the scoring: block. Score breakdowns are keyed by your group names.

scoring:
  groups:                       # each contributes up to `weight` points
    - name: distributed-systems # becomes a key in the score breakdown
      weight: 25
      match: all                # "all" (title+desc+tags+location, default) | "title"
      titleBoost: 1.2           # optional: multiply a tier's points on a title match
      tiers:                    # graded lists, strongest first; best match wins
        - points: 25
          keywords: [distributed systems, consensus]
        - points: 15
          keywords: [scalability, microservices]
    - name: seniority
      weight: 15
      match: title
      keywords: [staff, principal, lead]   # flat list => full weight on any match
  location:                     # word-boundary matched ("uk"/"eu" stay safe)
    weight: 20
    preferred: [remote, europe, worldwide] # any match => full weight
    excluded:  [us only, united states only] # any match => location scores 0
  excludeTitles: [sales, recruiter]        # title match => whole job scores 0
  compensationBenchmark: 15000000          # optional; EUR minor units (cents), annual
  compensationWeight: 10
  queueThreshold: 60            # score >= => queued for review
  dismissThreshold: 35          # score <  => auto-dismissed

Scoring algorithm: for each group, the best matching tier's points (clamped to weight) are added; the location dimension adds its weight for a preferred marker (or 0 if an excluded marker matches); the compensation dimension compares salary_max (normalized to EUR cents on ingest) to the benchmark. An excludeTitles hit zeroes the whole job. total is the sum of all dimensions.

Config schema

| Key | Type | Default | Meaning | | ------------------ | ---------- | ----------------------------------------------------------- | -------------------------------------------- | | criteria | string | (required) | Path to the scoring criteria markdown file. | | opportunitiesDir | string | career/opportunities | Where scaffold writes opportunity dirs. | | boards | string[] | ["remoteok"] | Boards scraped when no source is given. | | queries | string[] | ["software engineer", "backend engineer", "platform …"] | Search terms for query-driven boards. | | dbPath | string? | <root>/jobs.db | Jobs database location (gitignore it). |

CLI

brain jobs scrape [sources...]   # configured boards (--all for every API board,
                                 #   --browser to append the Chrome pass)
brain jobs score                 # score unscored jobs + classify (--rescore for all)
brain jobs triage                # interactive one-at-a-time review (TTY)
brain jobs review                # list the review queue
brain jobs stats                 # database + adapter-health stats
brain jobs scaffold <id>         # create an opportunity dir from a job
brain jobs show|open|decide|search|gc …

API and browser passes

Some boards need a real browser. scrape therefore has two passes, and the flags compose:

| Invocation | Runs | | --------------------------------- | ------------------------------------------ | | brain jobs scrape | the configured API boards | | brain jobs scrape --all | every API board | | brain jobs scrape --browser | the API pass, then the browser pass | | brain jobs scrape --browser-only| only the browser pass |

The browser path speaks raw Chrome DevTools Protocol — no Playwright or Puppeteer dependency — and covers builtin, nodesk, and dice. Start Chrome with --remote-debugging-port=9222 --headless=new first, or point CHROME_CDP_URL at an existing instance. When nothing is listening there, the browser pass reports that it was skipped and the API results still land.

Boards & sources

Full-feed / category boards (no browser, enabled by default): remoteok, remotive, weworkremotely, workingnomads, remotelyde, remoteineurope. Query-driven or JS-heavy boards (may need --proxy or --browser): simplyhired, jobgether, builtin, nodesk, dice.

⚠️ Scraping & Terms of Service

This module fetches public job listings for personal research. Scraping may violate a site's Terms of Service, and sites can rate-limit or block requests. Before scraping a board:

  • Review its Terms of Service and robots.txt and respect them.
  • Keep request volume low; the built-in per-domain rate limiter is a courtesy, not a substitute for reading the ToS.
  • Prefer official APIs/feeds where a board offers them.

You are responsible for how you use these scrapers. They are provided as-is, with no warranty, for individual use.