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

product-doc

v0.13.2

Published

Visual documents to explain systems, plan work, and review changes

Readme

productdoc

Understand the system, decide what to build, and review what changed. Productdoc turns your coding agent's findings into visual documents, with diagrams, media, and source links behind the reasoning.

Use it to explain an existing workflow, plan future work, or review a change. Documents start with the reader's question and familiar UI or involved services—not automatically with code. Plans keep one small, independent or commit-sized change per page.

Try it

Release status: the renamed package is prepared in this checkout, not yet published. The commands below describe the release candidate. From this checkout, use npm run cli -- guide or npm run cli -- serve <directory> until publication; do not expect a public npm install to work yet.

Install the skill after the package is released:

npx skills add tanishqkancharla/diffmap --skill generate-product-doc

Then ask your agent to explain an existing system, plan work, or review changes:

Use generate-product-doc to explain how this app handles sign-in, including what happens when the session expires.

The skill is a short pointer: your agent loads the full instructions from npx product-doc guide, so they stay version-matched to the viewer. When the stub itself changes, update it with npx skills update generate-product-doc.

The skill keeps documents in .diffmap/specs/<name>/. Productdoc is open source and MIT licensed. Documents are served locally by default; sharing as an unlisted gist still uses diffmap.dev until productdoc.dev supports those links.

Migrating from Diffmap

Productdoc is the standalone successor to Diffmap, expanding beyond diff review. The npm package is product-doc, its executable is product-doc, and its website will be productdoc.dev. The GitHub repository keeps its current URL.

Both skills coexist temporarily. Existing published Diffmap packages, the legacy skill, and the live site are unchanged by these local edits; users will be invited to migrate later. Keep existing documents and paths rather than copying them. No legacy binary alias or automatic installation migration is added.

The original Diffmap launch video shows the review workflow this builds on.

See the main ideas

Your agent picks the best diagram for each idea, and each part of a diagram links to its code. diffmap.dev walks through each one on an example pull request.

Service diagrams

A service diagram shows each service and its state. Each step shows one event and the state that the event changes, so you do not have to remember the state. Press Play to step through it. See Animated service diagrams for the syntax.

A service diagram stepping through how a client reopens a saved agent's thread

Diagram and call stack diffs

A Mermaid or service diagram can show a change in one picture: red parts exist only today, and green parts are new. In a service diagram diff, each value shows today's state crossed out next to the new state. Call stacks show a change the same way, with + rows for new code and - rows for code the change removes. See Diagram diffs.

Mermaid flowchart, sequence, and service diagram diffs for moving sessions from an in-memory cache to Postgres

Phases and commits

A spec is a directory with a cover page and one page per phase. The agent writes the plan in phases, each small enough to be one commit. When a phase is done, the spec shows a card for its commit with the files to review, most important first. Click a file to open its diff. Every link in the phase then points at that commit, so it keeps showing the code from that commit after the file changes.

A finished phase's commit card, with a file opening in the Diff panel

Comments

Highlight text on the page and press Comment to leave a note beside it. In a diff, file excerpt, or the Diff panel, hover a line and press the + in the gutter, or drag it across several lines, to comment under those lines. Comments save in your browser (localStorage, per page) and survive reloads. Copy comments in the top right copies them all as a numbered Markdown list, each with its location and the quoted text or lines, ready to paste to an agent. ⌘/Ctrl-Enter saves a comment and Escape cancels it.

Leaving comments on a spec and its diff, then copying them for the agent

Run the viewer

npx product-doc serve .diffmap/specs/<name>/

Bare npx product-doc .diffmap/specs/<name>/ is the same as serve. A spec is always a directory; serve, validate, and share reject a single Markdown file and say where to move it.

Options:

  • --port <n> — pin a listen port (default: a free port)
  • --root <dir> — workspace root for file excerpts (default the directory you ran the command from)

Close server in the top right stops the server. Hosted gist and GitHub viewers have no local server, so they omit this control.

The server also stops after 24 hours without a page or source request. Loading or refreshing the page resets the timer; background health checks and the live-reload connection do not keep it alive.

Check a spec without starting a server or publishing anything:

npx product-doc validate .diffmap/specs/<name>/

validate uses the viewer's parser to check every page's Markdown, diagrams, and annotations, and the spec's page names. It exits 0 with valid: true and the pages in order on success, and 1 with the error, naming the page, on failure. Use --json for machine-readable output. It does not resolve source files or Git commits. Run it after edits, including when reusing an already-running viewer. After updating productdoc itself, restart existing viewers so they use the new parser.

List every running viewer, including viewers using custom ports:

npx product-doc list

Update a global install to the latest product-doc on npm. product-doc --update does the same thing:

product-doc update

If you are already on the latest version, it says so and exits. Otherwise it runs the global install for the package manager that installed productdoc (npm install --global, pnpm add --global, or bun add --global) and prints the old and new versions. Under npx, or when it cannot tell how productdoc was installed, it prints the command to run instead of installing anything.

Request a page with Accept: text/markdown to read its current source file instead of the rendered HTML. Use the URL printed by serve, with the page's slug for any page but the cover:

curl -H 'Accept: text/markdown' http://127.0.0.1:<port>/write-through

Spec layout

A spec is a directory of Markdown pages:

.diffmap/specs/sessions-in-postgres/
  README.md               the cover, at /
  01-sessions-table.md    at /sessions-table
  02-write-through.md     at /write-through
  media/                  images and videos the pages show
  • README.md is the cover. Its first # heading is the spec's title.
  • Every other .md or .mdx file needs a number prefix, which sets its place: 2- comes before 10-. The rest of the name, the slug, is the page's URL, so renumbering pages keeps their links and comments. Two pages cannot share a slug, and validate and serve reject a page without a number.
  • The viewer shows one page at a time, with the spec's pages and the current page's headings in the left gutter, and links to the previous and next page at the bottom. A page's first heading is its title in that list.
  • A relative link to a page file, such as [Phase 2](02-write-through.md#tests), opens that page, in productdoc and on GitHub.
  • Each page keeps its own comments and Diff panel, which opens the page's first changes card. Copy comments copies the comments from every page, under each page's title.
  • serve reloads a page when its file changes, and every page when you add, remove, or retitle one.

Run npx product-doc fixtures/sessions-in-postgres for an example.

Share a spec with a link (experimental)

Share an unlisted gist and print a diffmap.dev link (requires gh logged in). Does not start a local server:

npx product-doc share .diffmap/specs/<name>/

A spec's pages share one gist. Hosted viewer: https://diffmap.dev/g/<gistId> for the cover, and /<slug> after it for a page (#heading is the table of contents). Gists shared before specs were directories still open, one file at a time. The page fetches the gist in the browser from api.github.com. Gists are unlisted, not private.

A spec committed to a GitHub pull request also opens at https://diffmap.dev/<owner>/<repo>/pull/<n>/.diffmap/specs/<name>/, which pins to the pull request's head commit and opens the spec's cover. Its pages keep their file names in the URL, as in …/commit/<sha>/.diffmap/specs/<name>/02-write-through.md, since a pinned commit never renames them.

Library

import { startServer, parseFence, parseViewerDocument } from "product-doc";

parseViewerDocument turns markdown into the page document with md4x. startServer listens. The guide that product-doc guide prints owns document shape; the viewer does not.

Fences

| Fence info string | Viewer | | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | mermaid | Beautiful Mermaid | | services | Animated service diagram you can play or step through | | mermaid diff or services diff | The same diagram with added and removed parts marked | | callstack or diff containing └── / ├── | Interactive stack rows, no file header | | diff or diff:path with a file path | Pierre patch with Pierre’s file header | | diff with no path | Pierre patch, no file header | | changes with a commit, range, or empty body | Files changed card with a Review button; empty means uncommitted changes | | start:end:path | Pierre file excerpt with Pierre’s file header | | html | Trusted HTML from this file. productdoc does not sanitize it. Only use it for local files you wrote. | | other langs | Highlighted code with optional titles, highlights, and notes |

Fences keep whitespace. Use them for mermaid, call stacks, and diffs.

Highlight lines and add notes

In a code fence, {2,4-6} after the language highlights those lines. Comment lines at the bottom of the fence add notes that show on hover or keyboard focus:

```ts {2-3}
function requestHandler(record: RecordInput) {
  const error = validateInput(record);
  if (error) return error;
  return existingService(record);
}
// [!note L2 validateInput] Returns an error when `name` is blank, otherwise undefined.
// [!note L3] Early return, so storage never sees an invalid record.
```
  • [!note L3] or [!note L3-L5] notes a line range, shown as a blue snippet highlight.
  • [!note L3 save] or [!note L3-L5 save] notes the first whole-word occurrence of save on each specified line, shown with a dotted underline.
  • [!note save] applies fence-wide, matching the first whole-word occurrence on each line. A scoped note takes precedence over a fence-wide note for the same match. Matching is textual, not semantic symbol resolution.
  • Start note lines with //, #, or --. The viewer removes them, so line numbers count only the code above. Backticks in a note render as code.
  • A symbol that does not appear within its scope, overlapping line notes, or a line number outside the fence is a parse error. A note line above other code stays code.

Add [Title] after the language to show a title above the code, as in ```ts [src/request.ts] {2-3}. Call stacks and mermaid fences take a title the same way, as in ```mermaid diff [Request flow], or ::callstack{title="Request flow"} and ::mermaid{title="Request flow"} in MDC. A services fence uses its start caption instead.

Call stacks take the same highlights and notes, counting rows from the first. Write their notes with #. A symbol note matches the row text you see, not hidden references. Inside a linked row, symbol notes show on hover only; the row itself keeps keyboard focus.

Run npx product-doc fixtures/code-notes for an example.

MDC

md4x Comark components map to the same views:

::mermaid
flowchart TD
A --> B
::

::callstack
startServer
+└── createViteServer
::

::diff{path="src/cli.ts"}
--- a/src/cli.ts
+++ b/src/cli.ts
::

::file{path="src/cli.ts" start="1" end="20"}
::

::changes{rev="a1b2c3d"}
::

::html
<aside>Note.</aside>
::

::details{summary="How it works"}
Folded content: prose, lists, and any fence above.
::

::details folds its content under the summary label until the reader opens it. .md and .mdx are both markdown. Curly braces in prose are plain text.

Images, videos, and galleries

Show screenshots and recordings with Markdown image syntax. Paths are relative to the spec file.

![The save button after the fix](media/save.png)
![Saving a record](media/save.mp4)

A .mp4, .webm, .mov, .m4v, or .ogv file renders as a video player; anything else is an image. On its own line, it shows centered in a card, with its alt text as the title row. A title, as in ![alt](path "title"), shows on hover.

Group related images and videos in a gallery, with the layout after :::gallery:

:::gallery side-by-side
![Before](media/before.png)
![After](media/after.png)
:::
  • side-by-side puts every item in one row, to compare before and after or one page at several widths.
  • grid wraps items into rows, to scan states or variants.
  • carousel shows one item at a time with Previous and Next (← and →), to step through a flow. Add a title after the layout, as in :::gallery carousel [Saving a record]; it stays in the title row while the current item's label changes below it.

Each item's alt text is its visible label. A gallery holds only images and videos, and ::gallery{layout="grid"} works too. The syntax matches galleries in Amp threads.

serve loads media from beside the spec, and a spec on a GitHub pull request loads it from the same commit. share fails when a spec uses local media, because a gist holds only text files; link media by https URL to share it.

Run npx product-doc fixtures/media for an example.

Link call stacks to source changes

Append a reference to a call stack line to link it. Paths are relative to --root.

  • [[path/to/file.ts#symbolName]] links a TypeScript or JavaScript declaration, including qualified names such as [[src/store.ts#Store.save]]. Ambiguous names show the qualified names you can use instead.
  • [[path/to/file]] links a whole file and [[path/to/file#L12-L30]] a line range. These work with any language.
  • [[<sha>:path/to/file#L12-L30]] or [[<sha>:path/to/file]] links a change in a commit. <sha> is a 7–40 character lowercase commit SHA, or a range <sha>..<sha>.
 shareMarkdownFile [[src/share.ts#shareMarkdownFile]]
+└── gistViewerUrl # print the diffmap.dev link [[d14ae1a:src/share.ts#L55-L59]]

A reference without a commit opens the Diff panel on uncommitted changes (working tree against HEAD), focused on that file or range. If the file has no uncommitted changes, the panel shows the current file.

A commit reference opens the Diff panel on every file changed in that commit or range, with a sticky header per file, scrolled to the linked file and lines. Line numbers are the file's lines after the commit. The file must be changed in that commit and the range must fit the new file; serve and share run git and fail at startup otherwise. Deleted lines cannot be referenced, and a commit cannot be combined with #symbol.

Multiple references on one stack line are allowed. The references are hidden in the rendered stack, and linked rows support mouse clicks and keyboard activation.

A changes fence shows a commit's files as a card. Each row shows the file name and +N −M. Review opens the Diff panel on all files; a row scrolls to that file without expanding its unchanged lines. An empty body (or working) means uncommitted changes.

Lines after the rev list the files to review, in order. The card shows those first and folds the rest under a dimmed N other files row that expands and collapses. Review opens the Diff panel in the same order. With no list, the card shows every file.

List files a reviewer can skip after an ignore: line. They fold away with the rest; with nothing to review, the card says no files are marked as important. When a commit's fence lists files, serve prints a warning naming each changed file that is in neither list, so no file is left out by accident. serve and share fail if a listed file is not changed in that commit.

```changes
a1b2c3d
src/request.ts
src/validate.ts

ignore:
package-lock.json
```

References open in one shared panel beside the walkthrough. Selecting a stack line or diagram element highlights it and scrolls the panel to the linked code range. When a target has several references, buttons above the source viewer select among them. On narrow windows the source panel sits below the walkthrough. Documents without references keep the single-panel layout.

Malformed references produce a parse error.

On a GitHub-hosted spec, commit diffs come from the GitHub API. It returns patch hunks only, so only lines near changes are reachable, and the Diff button opens the pull request's full diff.

A shared gist is a snapshot taken when you run product-doc share. It saves every referenced commit and every changes card, including uncommitted ones, to diffmap-changes.json. It saves the current contents of every file that a file excerpt or a reference without a commit reads to diffmap-files.json. In the gist, those references open the saved file; symbols resolve to their first matching line, and Cmd-click definitions only work in a local viewer. Commit your work and point references at the commit before sharing, so the gist shows the diff instead of a bare file.

Cmd-click (or Ctrl-click) a TypeScript or JavaScript symbol in the source panel to open its definition. Use Back to retrace navigation and return to the reference. Resolution uses the workspace's TypeScript configuration and current files.

Run npx product-doc fixtures/annotations for an example with commit references, multiple files, and unchanged context.

Animated service diagrams

A services fence shows services, their state, and the events they send each other, then replays those events step by step. Readers press Play, step with the arrow buttons or ← and →, or click the progress bar to jump to a step.

```services
service Browser "login page"
  state cookie = none
service API "HTTP server" [[src/api.ts]]
  > Receives every request and routes it to a service.
  service Auth
    state attempts = 0
      > Failures since the last success. Five locks the account.

start "Alice logs in"
  > The API checks her password with Auth, then sets a session cookie.
step "The browser submits the form"
  Browser -> API: POST /login
step "The API asks Auth to verify"
  API -> Auth: verify(alice) [[src/api.ts#login]]
    > Sends the username and the password from the form.
  Auth.attempts = 1
    > Counted before the check, so a crash still counts as a failure.
step "The API sets the session cookie"
  API -> Browser: 200 Set-Cookie
  Browser.cookie = sid=s_81f
```
  • service <Name> "label" declares a service. Indent a service line under another to nest it. Names are unique within the diagram.
  • state <key> = <value> declares a value and its initial state. Indent it under its service.
  • start "caption" at column 0, before the first step, titles the whole diagram, such as start "Alice logs in". It stays in the title row while the reader steps. A > line under it gives an overview of the whole process, shown under the title until the first step. A services fence takes no [Title]; the start caption is its title.
  • step "caption" starts a step at column 0. The caption is required: a sentence that says what happens, shown under the title row above the controls. Indented lines under it are From -> To: label events and Service.key = value updates. A step's events travel one after another, in the order written, and the updates land when the last one arrives.
  • A > line adds text to the line above it. Indent it further than that line; consecutive > lines join.
    • Under a service or state, it is a description: the name gets a dotted underline and the text shows in a tooltip on hover or focus.
    • Under start or a step, it explains the caption and shows under it.
    • Under an event or update, it is a note for that step. Notes appear in a column beside the diagram once the step's events land, each joined by a line to its event label or state row.
    • Under a service name on its own line in a step, it is a note on that service for the step, joined by a line to the service box.
  • %% starts a comment line.
  • Append [[reference]] to a service line or an event to link it to source.

Services must be declared before the first step, and updates may only change declared state. ELK lays the services out left to right, or top to bottom when that fits the column better, and routes one connection between each pair of services that talk. A request and its reply share that connection. Indentation is significant, so services has no MDC form. See the runnable examples.

Diagram diffs

Add diff after mermaid or services to show a change in one diagram. Start every line with - for Today only, + for Proposed only, or a space for both, as in a call stack diff. Indentation starts after that column.

```mermaid diff
 flowchart LR
   Client --> Handler
-  Handler --> Service
+  Handler --> Validate
+  Validate --> Service
```

productdoc draws every line once, so both versions share one layout. Nodes, edges, sequence participants, and messages that only Today has are red and dashed; those only Proposed has are green. A node whose label differs between the versions is amber and shows its Proposed label. Each version must also be a valid diagram on its own. To relabel a node, declare it on its own line (- B[Old], + B[New]) so its edges are not drawn twice. In a diff, %% ref edge:<index> counts edges as drawn: within each run of changed lines, + lines come before - lines.

```services diff
 service API
-  service SessionCache
-    state active = 0
+  service Sessions
 service Postgres
-  state sessions = not used
+  state sessions = 0 rows

 start "Sessions move to Postgres"
-step "API caches the session"
   API -> SessionCache: create(alice)
   SessionCache.active = 1
+step "Sessions writes the session"
   API -> Sessions: create(alice)
   Sessions -> Postgres: INSERT
   Postgres.sessions = 1 row
```

In a services diff, lines inside a - or + service or step take its sign. Today replays the unmarked and - lines and Proposed the unmarked and + lines, so each state row shows Today's value struck through beside Proposed's when they differ. A - and a + declaration of the same state key give each version its own starting value. Added and removed steps color their segment of the progress bar, and their caption starts with Added or Removed. See the runnable examples.

Link Mermaid nodes and edges

Inside a Mermaid fence, add %% ref node:<id> [[reference]] to link a node or participant, or %% ref edge:<index> [[reference]] to link an edge or sequence message. Edge indices start at zero in declaration order. The same file, symbol, and commit references work here. Keep one directive per target; append multiple references to that directive when needed.

```mermaid
flowchart LR
  A[ViewerApp] -->|Local mode only| B[CloseServerButton]
  %% ref node:A [[src/viewer/ViewerApp.tsx#ViewerApp]]
  %% ref node:B [[dae6b98:src/viewer/CloseServerButton.tsx#L8-L16]]
  %% ref edge:0 [[dae6b98:src/viewer/ViewerApp.tsx#L219-L227]]
```

```mermaid
sequenceDiagram
  participant App
  participant Store
  App->>Store: Save record
  %% ref node:Store [[src/store.ts#Store]]
  %% ref edge:0 [[src/store.ts#Store.save]]
```

Click a linked shape, edge line, or edge label, or focus it with Tab and press Enter or Space. Directives are Mermaid comments, so they remain hidden and do not change the diagram when rendered by other Mermaid tools. Missing diagram targets display an error. See the runnable example.

Link from prose

Write any reference in a paragraph, list item, or table cell, such as See [[src/store.ts#Store.save]] for the write path. or Close server landed in [[dae6b98:src/viewer/CloseServerButton.tsx]]. It renders as clickable inline code labeled with the file name and fragment (store.ts#Store.save) and opens the same source panel. References inside code spans and link text stay literal, so `[[path]]` shows the syntax itself.

Telemetry

The CLI sends anonymous usage events to PostHog: which commands run, whether serve started, which coding agent ran it, and which features of the local viewer get used. Events never include file paths, spec contents, comments, or anything else from your repository. TELEMETRY.md lists every event and field.

Turn it off with DIFFMAP_TELEMETRY=0 (or false) or DO_NOT_TRACK=1. It is also off when CI is set.

Feedback

Found a bug, hit a confusing error, or want a feature? Open an issue with what you did, what you expected, what happened, and your productdoc version (product-doc --version). Agents using the generate-product-doc skill do the same through gh issue create.