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

@hardlydifficult/pr-analyzer

v1.0.148

Published

Opinionated pull request analysis for the way we actually use it.

Readme

@hardlydifficult/pr-analyzer

Opinionated pull request analysis for the way we actually use it.

Configure it once, then work with PR numbers, PR refs, or "my open PRs" directly. Results already include status buckets and available actions, so client code stays small and expressive.

Installation

npm install @hardlydifficult/pr-analyzer

Quick Start

import { GitHubClient } from "@hardlydifficult/github";
import { createPRAnalyzer } from "@hardlydifficult/pr-analyzer";

const client = new GitHubClient({ token: process.env.GITHUB_TOKEN });

const prs = createPRAnalyzer({
  client,
  repo: "owner/repo",
});

const pr = await prs.analyze(123);
pr.status; // "ready_to_merge"
pr.actions; // [{ type: "merge", label: "Merge", ... }]

const inbox = await prs.mine();
inbox.readyForHuman.map((item) => item.pr.number);

Why This API

  • repo is configured once, so normal usage can just say analyze(123).
  • bot defaults to @cursor, because that is the common case.
  • analyze() always returns an ActionablePR, so UI code does not need a second getAvailableActions() pass.
  • inbox() and mine() return already-bucketed results for human review, bot bumps, in-progress work, and blocked PRs.
  • analyzeMany() runs in parallel and skips failures instead of failing the whole batch.

Main API

createPRAnalyzer(config)

const prs = createPRAnalyzer({
  client,
  repo: "owner/repo",
  bot: "@cursor",
});

Config:

  • client: GitHub client with repo(...).pr(...).snapshot() support.
  • repo: optional default repo. Required only if you want to analyze bare PR numbers.
  • bot: optional bot mention. Defaults to @cursor.
  • hooks: optional status override hook.
  • classify: optional custom status buckets.
  • actions: optional extra actions.
  • actionContext: optional flags passed to extra actions.
  • logger: optional logger used when bulk analysis skips failures.

Methods:

  • analyze(ref): analyze one PR and return an ActionablePR.
  • analyzeMany(refs): analyze many PRs in parallel.
  • inbox(refs): analyze and bucket a set of PRs.
  • mine(): fetch and bucket your open PRs, optionally filtered to the configured repo.
  • classify(prs): bucket already-scanned PRs using this analyzer's defaults.
  • actionsFor(pr): compute actions for one scanned PR.

Accepted refs:

  • 123 when repo is configured
  • "owner/repo#123"
  • GitHub PR URL
  • raw PullRequest
  • DiscoveredPR

Custom Statuses And Actions

import { createPRAnalyzer } from "@hardlydifficult/pr-analyzer";

const prs = createPRAnalyzer({
  client,
  repo: "owner/repo",
  hooks: {
    resolveStatus: (coreStatus, details) => {
      if (
        coreStatus === "ci_failed" &&
        details.checks.some((check) => check.name === "Cursor")
      ) {
        return "ai_processing";
      }
      return undefined;
    },
  },
  classify: {
    inProgress: ["ai_processing"],
  },
  actions: [
    {
      type: "fix_ci",
      label: "Fix CI",
      description: "Ask @cursor to repair CI",
      when: (pr, flags) => pr.status === "ci_failed" && flags.work === true,
    },
  ],
  actionContext: {
    work: true,
  },
});

Result Shapes

ActionablePR extends ScannedPR with:

{
  actions: readonly PRActionDescriptor[];
}

PRInbox contains:

  • all
  • readyForHuman
  • needsBotBump
  • inProgress
  • blocked

Each bucket contains ActionablePR items.

Low-Level Helpers

The package still exports the smaller building blocks when you want them:

  • analyzePR(client, owner, repo, prOrNumber, botMention, hooks?)
  • analyzeAll(prs, client, botMention, logger?, hooks?)
  • scanSinglePR(client, botMention, owner, repo, prNumber, hooks?)
  • classifyPRs(prs, config?)
  • getAvailableActions(pr, extraActions?, actionContext?)

Use these when you need to plug the analyzer into an existing flow. Use createPRAnalyzer() when you want the cleanest client code.