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

contextpruner

v0.4.0

Published

Lint your AI-context config files (AGENTS.md, CLAUDE.md, GEMINI.md, Cursor rules) against your repo — find missing, dead, drifting, and conflicting rules.

Downloads

28

Readme

contextpruner

Lint your AI-context config files against your repo. contextpruner lint checks your AGENTS.md / CLAUDE.md / GEMINI.md / Copilot / Cursor rules — plus the enforced ignore artifacts (.cursorignore / .geminiignore / .codeiumignore and the Claude Code deny rules in .claude/settings.json) — against the files actually in your tree and reports:

  • Missing — junk the config doesn't ignore yet (with what it's costing you),
  • Dead — rules that match nothing,
  • Drift — rules in one config but not another,
  • Conflict — rules that contradict each other, including an enforced rule that hard-blocks a file you pinned Keep or Skim, or a path you declared keep: in .contextpruner.

It exits non-zero when it finds issues, so it drops straight into CI or a pre-commit hook.

This CLI is the check side of a larger loop. At contextpruner.app you can generate all nine config files free in your browser — four of them enforced by the agent's own harness as a best-effort block — and the $9/month automation regenerates all nine on every push, with your account's usage dashboard showing what the last sync cut. Lint is how you prove, locally or in CI, that the configs still match your tree.

Usage

export CONTEXTPRUNER_API_KEY=cp_live_...   # from https://contextpruner.app/account
npx contextpruner lint

One subscription, one key: the same cp_live_ key authorizes both this CLI and the ContextPruner config automation (the GitHub Action / pre-commit hook), so reuse the one you already have.

contextpruner lint [--config <path>]... [--fix] [--json]
  • --config <path> — a config to lint (repeatable). Defaults to auto-detecting the standard files at the repo root: the advisory markdown configs plus the four enforced artifacts. An explicit path's format is inferred from its basename (settings.json → Claude settings, the *ignore files → gitignore syntax, anything else → markdown).
  • --fix — rewrite each config's managed block in place to fix the issues. Your own content outside the block (and the "Exceptions" section) is left alone. Enforced ignore files regenerate only the # contextpruner:begin/end block; .claude/settings.json is merged via its sentinel-delimited deny span and is skipped — never clobbered — when it has no recognized span or can't be merged safely.
  • --json — emit the full report as JSON.

If the repo root has a committed .contextpruner file, its keep: <path-or-glob> lines (enforcement exceptions — paths that must stay readable by agents, even untracked ones like docs inside node_modules/) drop any enforced rule covering them from the expected rule set, exactly as the generate automation does — so lint, --fix, and generation always agree. A bare prefer-tools line (optionally prefer-tools: true|on|yes) is also recognized — it turns on the runtime filter's "Prefer these tools" section (written by serve, see below). Unrecognized lines are reported as a warning and ignored.

Exit codes: 0 clean · 1 issues found · 2 usage/auth error.

Filter search junk (serve)

export CONTEXTPRUNER_API_KEY=cp_live_...   # same key as lint
contextpruner serve

serve (paid, same cp_live_ key) installs shell shims for the common search tools — rg, grep, find, ls — into ~/.contextpruner/bin. Each shim runs the real tool and pipes its output through ContextPruner, dropping junk paths from broad fan-out results and printing a footer showing what it hid. When a search actually pruned something, a savings note is also printed to stderr — contextpruner: this search — ~120 → ~30 tokens (~90 of junk pruned) — so the filtered results on stdout stay clean while you still see the tokens-per-turn saving. The estimate is computed locally and never sent. cat gets a passthrough shim (reading a named file is a pointed read, never filtered). It verifies your key (no key or an invalid one exits 2), writes the shims, and prints the line to prepend to your PATH:

export PATH="$HOME/.contextpruner/bin:$PATH"

Once it's on the front of your PATH, broad rg/grep/find/ls searches drop junk paths automatically. Set CONTEXTPRUNER_ALL=1 to run a search unfiltered. The shims are POSIX sh (macOS/Linux); Windows is not yet supported.

After the shims, serve also writes (or merges into) each supported agent's MCP config so an MCP-speaking agent — whose bundled search bypasses the PATH shims — spawns ContextPruner's filtered-search server (contextpruner __mcp) and gets the same junk-pruning through the contextpruner_search / contextpruner_glob / contextpruner_list tools (pass all: true to see everything). Claude Code's project .mcp.json is written unconditionally (the proven target, and the generic project MCP config). The other three are detection-gated — written only when that agent is in use here, so serve never litters a config for an agent you don't have:

  • Cursor.cursor/mcp.json (project), when .cursor/ exists.
  • Gemini CLI.gemini/settings.json (project), when .gemini/ exists.
  • Windsurf~/.codeium/mcp_config.json (home; Windsurf has no project-local MCP config), when ~/.codeium/ exists.

An undetected agent prints a one-line "not detected — re-run serve once it's set up" hint. A fresh config file is created; an existing one is left untouched except for splicing in the contextpruner server, preserving every other server and key — and never overwritten if it can't be parsed safely (the note tells you to add the server by hand). Restart the agent afterward, then prefer those tools for broad searches.

Finally, serve marks the repo's .contextpruner file with a prefer-tools directive (creating the file with a header if it doesn't exist, appending the line if it does, or leaving it untouched if it's already there). Commit it: the committed directive is what tells the config automation to add the "Prefer these tools" section to your generated AGENTS.md / CLAUDE.md / etc., steering agents toward the filtered tools. It's a committed marker — not local detection — so the automation reads the same value locally and in CI and the section never flip-flops.

Optional aggregate stats

The first time you run serve in an interactive terminal, it asks once whether you'd share two aggregate numbers — how many files were assessed and how many bytes were pruned — so we can show running totals on the website. Only those two integers are ever sent — never a path, never file contents. A leading y/yes opts in; anything else opts out, and a non-interactive or CI run is never prompted and never sends. The answer is saved in ~/.contextpruner/config.json, so you're only asked once. If you opt in, each filtered search adds to a local tally in ~/.contextpruner/stats.json, and serve flushes it best-effort — at most once a day — to /api/stats/contribute with your cp_live_ key. Opting out (or never being prompted) writes and sends nothing.

In CI

Fail the build when a config drifts. Save this as .github/workflows/contextpruner-lint.yml and add your key as the repo secret CONTEXTPRUNER_API_KEY:

name: ContextPruner Lint
on: [push, pull_request]
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npx --yes contextpruner lint
        env:
          CONTEXTPRUNER_API_KEY: ${{ secrets.CONTEXTPRUNER_API_KEY }}

Privacy

The pruning engine runs entirely on your machine — your file tree never leaves it. lint makes a single network call, to /api/license/verify, which sends just your API key to confirm an active subscription. The only other thing that can ever leave your machine is the opt-in aggregate stats above: if — and only if — you say yes at serve, two integers (files assessed, bytes pruned) are POSTed to /api/stats/contribute. Never a path, never file contents.

Development

The CLI bundles the app's pure engine at build time:

npm install   # in packages/cli
npm run build # → dist/index.js

The published package name is contextpruner; the repository's root package.json is a separate private app that happens to share the name.