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

@tb.p/kanban

v2.3.0

Published

Minimal npx-runnable kanban board backed by a local kanban.json file.

Readme

@tb.p/kanban

A minimal, zero-dependency kanban board you run with npx. State lives in a kanban.json file in the directory you run it from — no database, no server to host.

Usage

npx @tb.p/kanban
  • If kanban.json doesn't exist in the current directory, it's created with three default columns: Backlog, In Progress, Done.
  • If it already exists, it's loaded as-is.
  • The board opens automatically in your default browser.
  • Uploaded files (attachments, avatars, markdown file references) are saved under .kanban/uploads/ next to kanban.json.

Options

--user, -u <name>    Create/select a user and pre-select them as "current user" in the
                      browser tab this command auto-launches (does not affect other
                      clients connecting to the same server)
--port, -p <port>    Port to listen on (default: last used port, or 4321 the first time)
--no-open            Don't automatically open the browser
--ntfy-topic <t>     ntfy topic to notify on card create/move (saved in kanban.json;
                      omit to reuse the saved one)
--ntfy-server <url>  ntfy server to use (default: https://ntfy.sh, or the last saved value)
--project <name>     Set the board's display name — replaces "🗂️ Kanban" in the header
                      and the page/tab title (saved in kanban.json; omit to keep the
                      last saved value)
--help, -h           Show help

If --port isn't given, the server starts at whatever port it last successfully bound to (saved in kanban.json), auto-incrementing from there if that port is busy. Passing --port explicitly requires that exact port — if it's taken, the process exits with an error instead of silently picking another.

Identity and permissions

Users are cosmetic identity, not accounts — no passwords, just "who's currently at the keyboard." A browser with no user selected is view-only: no add/edit/delete/drag actions are available anywhere in the UI, and a banner says so. Picking or creating a user (top right) unlocks editing for that browser, remembered in localStorage.

--user <name> only pre-selects identity for the one browser tab the CLI auto-launches (via a one-time URL parameter) — it does not change what other clients see. Anyone else opening the URL still lands in view-only mode and picks their own identity.

Many users, one board

This is built for two overlapping setups:

  • Async, one instance each — everyone clones/pulls the repo, runs their own npx @tb.p/kanban locally, and kanban.json (plus .kanban/uploads/) is synced via git (commit/push/pull like any other file — being a dotdir doesn't exclude it from git, only from a plain ls). Commit the uploads folder too, or attachments/avatars will 404 for anyone who didn't generate them locally.
  • Synced, one shared instance — one person runs the server and others connect to its URL directly; all changes go through the same process.

Live updates

Every connected tab stays in sync automatically — no manual refresh needed. The server pushes a change notification over Server-Sent Events (GET /api/events) whenever the board is mutated, by anyone: another tab, a teammate's browser, or an API client like Claude Code creating tickets. The board view (columns, cards, users) updates itself within ~200ms of any change, and so does an open card or notes modal — e.g. a comment someone else adds shows up while you're looking at that same card. The one exception: if you're actively focused in a text field (title, description, comment box, notes), a live update won't overwrite your unsaved keystrokes; it catches up automatically as soon as you click away. If the card you have open gets deleted (directly, or via its whole column being deleted), the modal closes on its own.

Events are typed and granular — one per change, carrying just enough to act on it (ids and small fields, not full markdown bodies), so a listener never has to poll or re-fetch the whole board just to find out what happened:

| Event | Payload | |---|---| | column.created | { column } | | column.renamed | { columnId, name } | | column.deleted | { columnId, removedCardIds } | | columns.reordered | { order } | | card.created | { card } | | card.updated | { cardId, fields, title?, assigneeId?, color? }fields lists what changed; body changes are flagged but the text itself isn't sent | | card.deleted | { cardId } | | card.moved | { cardId, fromColumnId, toColumnId, toIndex } | | comment.added | { cardId, comment } | | comment.deleted | { cardId, commentId } | | attachment.added | { cardId, attachment } | | attachment.deleted | { cardId, attachmentId } | | user.created | { user } | | user.avatar_updated | { userId, avatar } | | notes.updated | {} — fetch GET /api/notes for content | | project.updated | { project } |

Lines starting with : (e.g. : ping) are keep-alive comments per the SSE spec — ignore them; EventSource already does.

This also means an AI agent can watch the board cheaply: run curl -N http://localhost:<port>/api/events in the background and react to specific event types instead of re-reading the whole board on every change.

Browser notifications

The 🔕/🔔 toggle in the header enables native OS notifications (via the browser's Notification API) for card.created, card.moved, and comment.added. First click prompts for permission; after that it just mutes/unmutes (stored per-browser). Notifications are skipped while the tab is actually focused — you're already looking at the board update, no need to also pop a toast — so in practice you'll only see them when the board changes while you're elsewhere. Works over localhost without HTTPS (browsers treat it as a secure context); if you ever expose this over a real network address instead, most browsers will require HTTPS for the Notification API to work at all.

Dark mode

Follows your OS light/dark setting automatically. Use the 🌙/☀️ toggle in the header to override it — the choice is remembered per-browser.

Instructions and notes

Two related but distinct fields:

  • instructions (GET /api/instructions, read-only) — fixed default guidance baked into every board: use the API instead of hand-editing kanban.json, fetch GET /api first to discover routes, and check notes for board-specific context. Not user-editable; it exists so a fresh AI agent or unfamiliar human has somewhere to start.
  • notes (📋 Notes button in the UI, GET/PATCH /api/notes) — a board-level markdown scratchpad for context that isn't tied to any one card: introductions, conventions, sprint goals, whatever the next person (human or AI) should know. Empty by default; whoever's working the board fills it in.

Notifications (ntfy)

ntfy is a free push-notification service: you pick a topic name, subscribe to it on your phone/desktop, and anything posted to that topic shows up as a push notification — no account required. This app posts to your topic on card.created, card.moved, and comment.added. Notifications are fire-and-forget — a down/unreachable ntfy server never affects the app.

To set it up:

  1. Pick a topic name. On the public ntfy.sh server this is effectively a shared secret (anyone who knows the name can subscribe), so use something unguessable — e.g. kanban-a1b2c3d4e5, not my-board.
  2. Subscribe to it before or after starting the server:
    • Phone/desktop: install the ntfy app, add your topic.
    • Browser: open https://ntfy.sh/<your-topic> and click "Subscribe" (or just leave that tab open — it shows pushes live via the same kind of stream this project uses internally).
    • Quick test without any app: curl -s ntfy.sh/<your-topic>/json and watch it while you trigger an event elsewhere.
  3. Set the topic, either way works, both save into kanban.json (meta.ntfy):
    • In the UI: ⚙️ Settings → enter the topic → Save. (Needs a user selected — it's board config, not exempt from the view-only gate.)
    • Or via CLI flag when starting the server: npx @tb.p/kanban --ntfy-topic kanban-a1b2c3d4e5
  4. Click Test connection in ⚙️ Settings (or POST /api/ntfy/test) — it sends a real push using whatever's currently in the form (even before you hit Save), so you can catch a typo'd topic or bad token before committing it. Reports success or the actual failure reason (unreachable server, 401 from a bad token, etc).

Running your own server instead of the public one: set it in ⚙️ Settings, or pass --ntfy-server https://your-ntfy-instance on the CLI — saved the same way either path.

Token (optional). Only needed if your ntfy server requires authentication — a self-hosted instance with auth enabled, or a reserved topic on ntfy.sh. Set it in ⚙️ Settings (the field is password-masked and never echoed back in plaintext after saving — reopening Settings shows a "leave blank to keep" placeholder, with a separate "Clear saved token" action to remove it) or via PATCH /api/ntfy {"token":...}. Sent as Authorization: Bearer <token>. The public ntfy.sh server doesn't need one for ordinary topics — the topic name itself is the only "secret."

API

The API is self-describing — GET /api returns a JSON list of every endpoint, method, and expected body. This is meant to be discoverable by an agent (e.g. Claude Code) creating/updating tickets without needing to read the source first.

Example:

npx @tb.p/kanban --user "Batman" --ntfy-topic "kanban-a1b2c3d4"

Features

  • Columns: add/remove from the board UI, drag to reorder, drag cards between and within columns.
  • Cards: emoji-friendly titles, markdown description (Write/Preview toggle, rendered as a preview snippet directly on the card face), assignee (any board user), a pastel background color picker. The card face shows the creator's avatar before the title and the assignee's avatar bottom-right; hovering any avatar shows the username as a tooltip.
  • Attachments: drag-and-drop or file-picker upload (copied into .kanban/uploads/, referenced by URL — not embedded as base64).
  • Avatars: upload and crop an image (pan/zoom) per user; stored as a file under .kanban/uploads/ and referenced by URL. Falls back to colored initials if unset.
  • Markdown file references: drop a file directly onto a card body or comment box (or use the 📎 Insert file button) to upload it and insert a [name](url) — or ![name](url) for images — reference at the cursor.
  • Comments: markdown, attributed to whichever user is "current" in the browser.
  • Everything persists to kanban.json after every change.

Data model

kanban.json shape:

{
  "version": 1,
  "columns": [{ "id": "...", "name": "Backlog" }],
  "cards": [
    {
      "id": "...",
      "columnId": "...",
      "title": "🔥 Fix the thing",
      "body": "markdown text",
      "color": "#FADDE1",
      "assigneeId": "...",
      "createdBy": "...",
      "attachments": [{ "id": "...", "url": "/kanban-uploads/...", "name": "..." }],
      "comments": [{ "id": "...", "userId": "...", "body": "...", "createdAt": "..." }],
      "createdAt": "...",
      "updatedAt": "..."
    }
  ],
  "users": [
    { "id": "...", "name": "Batman", "initials": "BA", "color": "hsl(...)", "avatar": "/kanban-uploads/..." }
  ],
  "notes": "markdown text shared across the whole board",
  "instructions": "default guidance for humans/AI on how to use this board (see below)",
  "project": "Acme Sprint Board",
  "meta": {
    "lastPort": 4321,
    "ntfy": { "server": "https://ntfy.sh", "topic": null }
  }
}

It's plain JSON, but don't hand-edit it — use the HTTP API (or the web UI) so uploads, notifications, and file references stay consistent. Check it into version control alongside your project if you want (see the "many users, one board" note above about also committing .kanban/uploads/); just make changes through the API, not a text editor.