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

agent-annotate

v0.1.2

Published

The missing component for your agentic engineering workflow: click any element on your local page, or drag to capture a region, and the note lands in the conversation you are already having.

Readme

agent-annotate

The missing component for your agentic engineering workflow while working with the Claude terminal app and developing HTML pages. (If you are building React apps, check out Agentation.)

Describing which thing you mean is the slow part of design iteration. The current meta is referring to it and taking screenshots to be specific. Codex and Claude desktop apps have a built-in annotator to help you debug, but for the terminal I don't see a similar solution — so I asked Claude to create one for me.

How this works

Install the plugin, reload, and call it with /agent-annotate:annotate in your Claude Code terminal app.

That starts a local server, hosts your HTML, and gives you the URL. It also leaves --wait running in the background, so the notes you send land in the conversation you are already having rather than in a log.

How to use

At the bottom right of the page you will find a small toolbar.

  1. With the annotate tool — click the annotate icon (or press A), then click any element on your page and say what should change. The note carries the element it points at, what kind of element it is, the page, the viewport and the theme.

  2. With the camera tool — click the camera icon (or press S) and drag over the part of the page you mean. You may need to grant screen-share permission the first time. Once captured, add whatever you want to say about it.

When you are done, press send to agent in the notes list — or the send icon on a single note — and Claude starts working on them.


Note: Text below this is written by Claude.

Install

One command, on a machine that has Claude Code:

npx agent-annotate install

That stages the plugin under ~/.agent-annotate/marketplace and registers it with the claude CLI, so your next session has the /agent-annotate:annotate skill — which starts the server, gives you the URL, and waits for your notes in the background. It stages rather than registering the package where npx put it, because npx unpacks into a temp cache that gets cleaned up; staging is also what makes the plugin and the npm package the same files. --dry-run shows what it would do, --root <dir> stages somewhere else, and --no-claude stages only and prints the two claude commands for you to run yourself.

Or just run the server, with nothing installed and no plugin involved:

npx agent-annotate --root .

To keep it around, install it globally or into the project:

npm install -g agent-annotate     # then: agent-annotate --root .
npm install -D agent-annotate     # then: npx agent-annotate --root .

Node 18 or newer. Zero dependencies.

It serves static files: point --root at a directory of HTML and the toolbar is injected into every page it serves. Using a framework instead — Astro, Eleventy, Next, Vite? See the FAQ.

Using it

In the page

| | | |---|---| | A | arm the picker — or, with text highlighted, annotate that text straight away | | S | capture a region — drag over the part you mean | | click | drop a pin on that element | | Cmd/Ctrl + Enter | save | | Esc | cancel | | chips | pick the change: tone: warmer, side by side, tap target too small | | breadcrumb | the click landed on the wrong node — walk up the tree | | list icon | every note on this page, with the count on the icon; the bin deletes a row | | send to agent | at the top of the list — hands the whole batch over | | click a pin | read that note back, then edit it or send it on its own | | grip | drag the dots at the right of the toolbar to move it out of your way |

Keys typed into the toolbar stay in the toolbar: a page that binds space or the arrow keys — a slide deck, an editor — never sees them, so you can type a note on top of one without driving it. Clicking anywhere outside the notes list closes the list.

On the command line

--root <dir>      directory to serve            (default: cwd)
--port <n>        port, 0 picks a free one      (default: 8765)
--host <addr>     interface to bind        (default: 127.0.0.1, loopback only)
--store <file>    annotations file       (default: <root>/.annotate/annotations.json)
--index <file>    directory index               (default: index.html)
--ignore <a,b>    runtime-only classes to keep out of selectors
--wait            block until the next batch, print it, exit (for an agent
                  already in a conversation with you)
--agent           send-to-agent starts a fresh headless Claude Code
--on-handoff <c>  run your own command instead of --agent
--quiet           no logging

--ignore is worth setting if your app adds classes at runtime. Given --ignore rules,faint, a pin records section.mo > h3 instead of section.mo > h3.rules.faint, which is both more readable and more stable.

Starting the agent

Notes are worth acting on once you say they are, so the list has a send to agent bar, and a note's own card has a send icon. Sending marks those notes "status": "ready" — a note you are still typing is never in that set.

If an agent is already in a conversation with you, it should block on this in the background — which is what /agent-annotate:annotate sets up for you:

npx agent-annotate --wait --root .

That serves nothing. It waits until you press the button, prints the batch, and exits, which is what puts the notes in front of the agent you are already talking to.

If nobody is watching, --agent starts a fresh headless Claude Code per batch instead. It gets the batch, the store and every measured fact in its prompt, edits the project it is serving, and resolves what it finished. The trade is that it starts cold every time.

Triage

A batch is rarely a straight list. Notes written in one pass tend to pile onto the same few elements, and doing them in the order they were typed causes rework — you restyle a container, then the next note moves it. So the agent groups the batch, works out what has to land first, and names any notes that genuinely conflict, before touching a file.

Then it says what it is going to do and does it, resolving the notes it finished. Where a note is ambiguous, where two notes conflict, or where it thinks the change is wrong, it does the unambiguous part and comes back with the question rather than guessing — a pin often lands on a wrapper when you meant something inside it.

Whatever it prints comes straight back to the page, in a panel that streams while it works, and is kept in .annotate/annotations-agent.log.

If you would rather run something else — your own script, a different agent, a webhook — --on-handoff <cmd> replaces --agent entirely. It runs with ANNOTATE_IDS (the batch) and ANNOTATE_STORE (the file) in its environment, and whatever it prints is shown on the page the same way. This is the only thing the server ever executes, which is why it has to be passed explicitly on the command line — there is no config file that can set it, and nothing on the page can change it.

What a note carries

{
  "text": "make these lines wobbly",
  "target": "section.mo > h3",
  "page": "index.html",
  "x": 278, "y": 1398,
  "viewport": "1446x703",
  "theme": "light",
  "context": {
    "kind": "heading", "label": "heading", "tag": "h3",
    "facts": ["412×38", "24px/1.2 600", "rgb(20, 22, 26)", "5 words"]
  },
  "intents": [{ "id": "tone", "label": "tone: punchier" }],
  "id": "amsyvcaio2ij",
  "created": "2026-08-18T16:20:07.104Z",
  "status": "open"
}

target and x/y are what make a note actionable — an agent can find the element rather than guess at your prose. context is that element read off the live page: its kind (one of heading, text, layout, action, field, list, table, image, icon, graphic, media, page, element) and facts measured at the moment you clicked — box size, computed display and gap, font size and colour, word count, a missing alt. intents are the chips you picked, in plain words.

When a note was written from highlighted text, it also carries "selection": { "text": "…" } — the exact words you had selected, kept whole beside what you typed, so the agent can find that string in the source rather than infer it. The quote is dropped into the note box too, for you to write around.

A capture is the other shape of note: "shot": "shots/<id>.png", whatever you typed, and nothing else. It is not about an element, so it carries no target or context — the agent reads the image and works out what it is about from there.

Working through notes

Read .annotate/annotations.json, make the changes, then mark them done:

curl -s -X POST localhost:8765/__annotations/resolve \
  -H 'Content-Type: application/json' -d '{"ids":["amsyvcaio2ij"]}'

Omit ids to resolve everything open. Resolved notes move to annotations-resolved.json with a timestamp, so the live queue stays short and you keep the history.

Nothing to add to your .gitignore. Everything this writes lives in one folder that ignores itself:

.annotate/
  .gitignore                  *
  annotations.json            the queue
  annotations-resolved.json   what has been dealt with
  annotations-agent.log       what the agent said, run by run
  shots/                      captured regions

Cleaning up is deleting that directory. A project that already has an annotations.json at its root — from an earlier version — keeps using it.

With Claude Code

This package doubles as a Claude Code plugin, and the repo is its own marketplace. npx agent-annotate install is the short way in; these are the same thing by hand, from a session:

/plugin marketplace add inosaint/agent-annotate
/plugin install agent-annotate@agent-annotate

or from a terminal:

claude plugin marketplace add inosaint/agent-annotate
claude plugin install agent-annotate@agent-annotate

The difference between these and the installer is where the plugin comes from: these track the GitHub repo, the installer uses the npm version you just pulled.

Working on it locally instead:

claude plugin marketplace add ./
claude plugin install agent-annotate@agent-annotate

Installed, /agent-annotate:annotate starts the server, tells you the URL, and leaves --wait running in the background, so the notes you send land in the conversation you are already having rather than in a log. The plugin carries the server with it — the skill runs $CLAUDE_PLUGIN_ROOT/bin/agent-annotate.js, so there is nothing to install from npm.

The skill lives in skills/annotate/, and is invoked as /agent-annotate:annotate. It is what teaches an agent to triage a batch, act on it, and resolve only what it actually finished.

FAQ

Does it work with Astro (or Next, Vite, Eleventy, Hugo)?

Yes, against the build output. This is a static file server, not a proxy, so it cannot sit in front of astro dev — that server produces the HTML, and only the server that produces the HTML can inject the toolbar into it. Build, then serve what was built:

npx astro build                       # writes dist/
npx agent-annotate --root dist        # open the URL it prints, press A

Or as one loop you can re-run after each round of changes:

{
  "scripts": {
    "annotate": "astro build && npx agent-annotate --root dist --ignore 'astro-.*'"
  }
}

Notes then point at the built HTML — a pin records main > section.hero > h1, which is the <h1> in your .astro component. So make the change in src/, run npm run annotate again, and carry on. Two things worth knowing:

  • Use --wait, not --agent. --agent starts a fresh Claude Code with its working directory set to the root it is serving — for a build that is dist/, where every edit is thrown away by the next build. --wait hands the batch to the agent you are already talking to, which is in your project root. The plugin's /agent-annotate:annotate does this for you.
  • --ignore earns its keep here. Frameworks hang generated classes off elements (astro-j7pv25f6, hashed CSS-module names), and those change on every build. Each entry is matched against the whole class name as a regular expression, so a prefix needs writing as one — --ignore 'astro-.*', not --ignore astro-. That keeps them out of the recorded selector, so a note written before a rebuild still points at the same element after it.

The same recipe works for anything with a static output directory: next build && next export (out/), vite build (dist/), eleventy (_site/), hugo (public/).

Can I just add a <script> tag to the page my own dev server serves?

No, and deliberately. The toolbar talks to /__annotations on its own origin, and the server refuses any request carrying a foreign Origin — otherwise any page you had open could read your notes or POST a handoff that starts an agent with write access to your project. Loading the script from port 8765 into a page on port 4321 fails that check.

Will it show up on my deployed site?

It cannot. The toolbar is only ever injected by this dev server, which you run yourself, and even then the client returns immediately unless the page's hostname is localhost or 127.0.0.1. Nothing about it is in your source, so there is nothing to strip before you ship.

Which browsers?

Chromium is what the design was tuned against. Safari and Firefox work, but the panels fall back to plain frosted blur — the glass depends on backdrop-filter: url(#…), which only Chromium honours.

What it exposes

It is a dev server, so it is worth being precise about what it will and will not do:

  • Loopback only. It binds 127.0.0.1, so nothing on your network can reach it. --host 0.0.0.0 opts out and prints a warning; only do that on a network you trust.
  • No CORS. The toolbar is served by this server and talks to it same-origin. A request carrying another origin is refused — otherwise any page you happened to have open could read your notes, or POST a handoff that starts an agent with edit rights on your project.
  • Requests arriving under another hostname are refused, which is what stops a hostile name resolved to 127.0.0.1 from talking to it.
  • Dotted paths are not served.git, .env, .npmrc and the like stay private, even though the server is pointed at your project root. Its own .annotate/ folder is the exception, so captures can be shown back to you.
  • It runs nothing unless you ask. --agent and --on-handoff are the only things that execute anything, both are command-line flags, and neither can be set by the page or by a note.

API

const { createServer } = require('agent-annotate');
const server = createServer({ root: './site', ignoreClasses: ['rules'] });
server.listen(8765);

| Route | | |---|---| | GET /__annotations | every note in the queue | | POST /__annotations | add one (text required) | | PATCH /__annotations?id= | edit one's text | | DELETE /__annotations?id= | drop one | | POST /__annotations/shot?id= | PNG body → save it beside the note | | POST /__annotations/handoff | {ids:[…]} → mark ready, start the agent | | GET /__annotations/handoff | is a run in flight, and how the last one went | | POST /__annotations/resolve | {ids:[…]}, or all when omitted | | GET /__annotate/client.js | the toolbar |

MIT