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

nudge-mode

v0.4.4

Published

Design mode for herdr. Select components on a local dev server, comment on them, and send them to the herdr agent working on that project.

Downloads

781

Readme

Nudge

Design mode for herdr. Select components on a local dev server, comment on them, and send them straight to the herdr agent session that is working on that project.

you select + comment in the browser
        │
        │   ┌─ which project is this?      lsof on the dev-server port
        ▼   ▼
  extension  ──HTTP + pairing token──▶  daemon (in a herdr pane)
                            │
                            ├─ which agent owns it?         herdr agent list
                            ├─ is that agent really in it?  scope check
                            └─ send the review              herdr agent prompt

Requirements

  • Node 23.6 or newer — it runs the daemon's TypeScript directly, no build step
  • herdr on your PATH, with the daemon started from inside a herdr pane
  • Chrome

Setup

Both halves ship in one npm package: the daemon runs from it, and the built extension rides along inside it.

1. Install it once:

npm install -g nudge-mode

A global install matters here: Chrome loads the extension from wherever the package sits on disk, and a global install stays put. npx nudge-mode works for a one-off try, but its folder lives in npm's cache, which can be cleared or replaced on the next version and would leave Chrome pointing at nothing.

2. Start the daemon, in a pane in the herdr session you want reviews sent to:

nudge

It draws a card with the pairing code and the extension folder, listens on 127.0.0.1:8791, and stays up for the session. Loopback only — it runs herdr commands and must never be reachable from the network. The code is generated once and kept at ~/.nudge/token (mode 0600), so it survives restarts.

3. Load the extension. It is not on the Chrome Web Store, so Chrome loads it from disk. Open chrome://extensions, turn on Developer mode, choose Load unpacked, and pick the folder from the card. The manifest pins a public key, so the extension keeps the same id across reinstalls.

Need the folder again? In another pane:

nudge extension     # prints the folder; add | pbcopy to copy it

Updates arrive the same way code does in development: npm update -g nudge-mode, and the running daemon tells the extension to reload itself from the same folder.

4. Pair them. Open the extension popup and paste the pairing code. Without it the daemon answers 401 — see Why pairing exists.

From a clone

npm install && npm run build       # builds dist/daemon.js and extension/dist
npx .                              # start the daemon from the repo root

Using it

  1. Run your dev server yourself, e.g. npm run dev on localhost:3000.
  2. Open the page in Chrome and click the extension icon, then Turn on. The popup also shows the keyboard shortcut Chrome assigned — ⌥⇧D on macOS.
  3. Hover to highlight, click to select, describe the change, press Enter. Use Shift+Enter for a new line. The sliders button opens a live style editor — anything you change there is applied to the page immediately and sent as an exact before and after. While you hover, the child components inside the highlighted one are outlined too, each in its own colour, so the shape of the tree is visible before you click. With a Vite inspector plugin in your app those outlines follow real component boundaries — the wrapper <div>s belonging to the hovered component are skipped, and only descendants declared in another file are drawn. Without one they fall back to direct DOM children. The arrow keys walk the tree while picking — up to the parent, down to the first child, left and right between siblings — and Enter selects. Elements inside open shadow roots and same-origin iframes can be picked too.
  4. Use the pen button to draw directly over the page. Draw one or more strokes, click the pen again, then comment on the marked region.
  5. Add an optional page note in the tray. It can accompany selections or be sent on its own when the feedback applies to the whole page.
  6. Select as many elements and drawings as you like — the tray lists them in a card above the bar with the exact crop the agent will get, where each one can be edited, pointed at a different element, tagged (bug, polish, question, and P1 to P3) or removed before sending. Closing the composer with Esc keeps its draft for the next time you click the same element. The session follows the tab across reloads and route changes, so one review can contain annotations and page notes from multiple pages.
  7. The terminal button in the tray turns on console capture: errors, unhandled rejections and console.error calls from then on ride along with the review.
  8. Check the agent named in the tray; its menu lists the others and can ask herdr again. Then hit Send.
  9. After sending, a row under the agent picker follows it: queued, working, blocked, done. When it is done, Reload and show reloads the page and outlines every element from that review, and Reply sends a follow-up that points at the same note.

Two states

A session is open whenever the tray is showing, and it holds every selection you have made. Annotating is the narrower mode where clicks are intercepted to pick elements.

They are separate so you can step out of annotating to scroll, open a menu, or type into the form you are reviewing, and come back with your selections intact.

| Action | Effect | |---|---| | Esc | Close the composer, or stop annotating — selections are kept | | ⌘. | Toggle annotating back on (Ctrl+. off macOS) | | Arrows / Enter | Walk to the parent, child or siblings of the highlighted element, and select it | | Bubble button in the tray | The same toggle, with the shortcut on hover | | End session in the popup | Ends the session and discards anything unsent | | The annotation pill in the tray | Opens the card of queued annotations, each removable | | Drag the bar by its edge | Moves the tray anywhere on screen; it is remembered | | Chevron in the bar's corner | Collapses the tray to a tab on the nearest screen edge; click the tab to bring it back |

While annotating is off the tray stays put, and the page behaves completely normally. Navigate in the same tab and the tray, queued annotations, and page-specific notes are restored on the next page.

What the agent receives

A short prompt pointing at a note on disk, so the review does not flood its context:

Browser review — 2 selections on http://localhost:3000/tools/wallet.
Read @/tmp/nudge-picks/2026-08-18-16-52-03-y55p/note.md and address each comment.

The note starts with an optional page-level comment and any captured console errors, then leads with the source location of each component or the position of each drawing. Every review item includes its tag, its comment, surrounding facts, accessibility context when available, the viewport it was seen in, the rules that apply on hover, focus and active, and a cropped screenshot. Live style edits list the value as written in the stylesheet next to the computed one, so var(--space-4) or a Tailwind class survives the trip:

## 1. [bug · P1] src/components/PriceCard.tsx:42

> padding here is inconsistent with the other two cards

- element: `<article>`
- selector: `main > div.grid > article:nth-child(2)`
- box: 320x186 at (410, 220)
- viewport: 1440x900 @2x
- styles: padding: 24px 16px; gap: 8px; border-radius: 12px
- :hover (`.card:hover` in app.css): box-shadow: var(--shadow-2)

![selection 1](./shot-1.png)

How a page finds its agent

A browser tab knows a port. herdr knows working directories. The process listening on that port bridges the two:

  1. lsof maps the port to a PID, and the PID to its working directory.
  2. That directory widens to its git repository root, so an agent sitting at the root still matches a dev server started from a package inside it.
  3. Agents whose working directory falls inside that repository are candidates.
  4. The focused pane wins; otherwise the highest state_change_seq does, which is herdr's session-wide counter of who acted most recently.

The winner is only a suggestion. The tray shows it by name and lets you pick a different agent before anything is sent. Targets can be refreshed in place. Blocked agents cannot receive a review, while a working agent requires a second confirmation before the review is queued.

Where source locations come from

Clicking an element gives a selector and a screenshot. Turning that into a file and line needs the framework to expose it, which only happens in development builds. Four sources are tried, most reliable first:

| Source | Covers | |---|---| | data-inspector-* / data-v-inspector attributes | Vite inspector plugins, React and Vue | | _debugSource on the React fiber | React 16 to 18 | | __source prop from the JSX source transform | React with the Babel transform | | __vueParentComponent.type.__file | Vue 3, file only — no line number |

React 19 removed _debugSource, so a React 19 app with no inspector attributes falls all the way through to the selector and screenshot.

When none of the sources apply the review still sends — the note simply says the source is unknown and leans on the selector, classes, and text instead.

Endpoints

| Route | Auth | Purpose | |---|---|---| | GET /health | open | Liveness check for the popup | | GET /build | open | Long poll the dev live-reload loop waits on | | GET /targets?url=<page-url> | paired | Which agents could act on this page | | POST /blob | paired | Store one PNG, return a blobId | | POST /send | paired | Write the note and prompt the chosen agent | | GET /pick?id=&since= | paired | Long poll the status of a sent review | | POST /pick/follow-up | paired | Append a reply to the note and prompt again |

The daemon resolves the project from the dev-server port with lsof; the response reports "source": "lsof" | "none".

curl -H 'Authorization: Bearer <pairing code>' \
     -H 'Origin: chrome-extension://<your extension id>' \
     'http://127.0.0.1:8791/targets?url=http://localhost:3000'

Why pairing exists

The daemon types text into a coding agent. Anything that can reach it can drive that agent. Loopback alone is not a boundary: every website you visit can fetch() 127.0.0.1, and a plain POST with a text/plain body does not even trigger a preflight the daemon could refuse.

So three things guard it:

  • A bearer token on /targets, /send, and /blob, compared in constant time. /health and /build stay open because they reveal nothing.
  • An origin allowlist. A request carrying any Origin that is not chrome-extension://… is refused with 403, which covers every web page. Requests with no Origin at all — curl, for example — are still allowed.
  • Scope re-validation on /send. The chosen pane must genuinely be working inside the repository behind the URL being reviewed, so a stale or forged paneId cannot reach an unrelated agent.

Reviews are written to /tmp/nudge-picks with 0700 directories and 0600 files, and anything older than seven days is deleted on the next boot.

Development

cd daemon     && npm run dev        # also watches extension/dist for rebuilds
cd extension  && npm run dev        # rebuilds on save

Changes reach the browser on their own. The daemon watches the extension's build output and holds a request open until it changes; the service worker is waiting on that request, so a rebuild restarts the extension, which then replaces the overlay on every open page. Save a file and the page is running the new code a moment later — no reloading the extension, no refreshing the page.

If the daemon is not running, the loop simply retries every few seconds and the extension behaves like any other unpacked one.

cd daemon    && npm test && npm run typecheck
cd extension && npm run typecheck && npm run build

Documentation