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

@mattstack/rt-client

v0.16.0

Published

Typed client for the rt daemon: repos, worktrees, ports, tokens, and the event relay

Readme

@mattstack/rt-client

Typed client for the rt daemon.

rt keeps the per-repo state a dev environment needs: worktrees, assigned ports, tokens, dev servers, and a live event relay. This package is how other programs read and drive that state without shelling out to the CLI and parsing text.

bun add @mattstack/rt-client
import { rtCommand, readProjectMRs } from '@mattstack/rt-client';

const repos = await rtCommand(['repos', 'list']);
const mrs = await readProjectMRs('group/repo');

Requires a running rt daemon. Install rt from the latest GitHub Release (./rt --post-install), then rt verify.

Bun-only: the settings exec path (src/settings/exec.ts) shells out via Bun.spawn, so this package does not run under Node.

@mattstack/glance is a peer dependency: rt-client returns glance's forge types so merge request shapes stay identical across rt, gitq, and mr-board.

Repo identity

Every per-repo key in the rt estate is a stable serialized identity, not a repo name. Whatever you store per-repo, send to the daemon, or put in a REST path is keyed by the wire form this package emits. The one exception: settings-store sections (repos.<identity>) key on the RAW host/path form (RepoIdentity.id for a remote-kind identity) — the settings resolver never sees the wire form, and a serialized key there misses silently.

remote:gitlab.com%2Facme%2Facme-dev     path:%2FUsers%2Fdev%2Fscratch
└─┬──┘ └──────────┬─────────────┘
 kind    the id, encodeURIComponent'd — slash-free, fits one URL segment

kind is remote (the repo has an origin: id is normalized host/path) or path (no usable remote: id is the main worktree's realpath). The : is a literal delimiter.

import {
  deriveRepoIdentity,   // (repoPath: string) => Promise<RepoIdentity> — never null
  serializeIdentity,    // (id: RepoIdentity) => string — the wire form above
  parseIdentity,        // (wire: string) => RepoIdentity | null — THE validity check
  identityFromRemote,   // (remoteUrl: string) => RepoIdentity | null — sync
  type RepoIdentity,    // { kind: "remote" | "path"; id: string }
} from '@mattstack/rt-client';

const identity = serializeIdentity(await deriveRepoIdentity(repoPath));

await rtCommand(['worktree', 'list', '--repo', identity]);        // daemon key
const url = `/api/runs/${encodeURIComponent(identity)}/${runId}`; // URL segment

| Do | Don't | |---|---| | Get identities from these functions, once, at the boundary | Re-derive with your own git calls (git remote get-url diverges under insteadOf) | | Key stores and daemon payloads on the serialized form | Key anything on a folder basename or a remote's last segment | | Key settings sections (repos.<identity>) on the raw host/path id | Put the serialized form in a settings lookup, or the raw form in a daemon payload | | encodeURIComponent(identity) in URL path segments | Ship the wire form raw in a URL — its % signs decode into slashes | | Decode for display: parseIdentity(wire), then the id's last path segment (remote) or basename (path) — the returned id is already decoded | decodeURIComponent the id again, show the wire form to a human, or build a chat handle from it | | Treat the repo field from runs:list as an opaque key, passed back verbatim | Validate or re-derive runs:* repo keys — pre-cutover runs keep their original keys |

Repo-keyed daemon verbs accept serialized identities only; a bare repo name doesn't error, it resolves empty. parseIdentity is strict — only strings serializeIdentity emitted parse — so validate payloads with it, and never hand-assemble or string-split a wire.

Chat

The rt chat surface the chat viewer (~/Documents/GitHub/chat) is built on. Every call degrades to { ok: false, error } instead of throwing, and a daemon that is down and a daemon that refused look the same to the caller; branch on ok, not on exceptions.

import { chatRooms, chatMessages, chatPost, createRelay, daemonHealth } from '@mattstack/rt-client';

const rooms = await chatRooms({ handle: 'matt' });            // { ok, data: { rooms } }
const page = await chatMessages({ room: 'build', limit: 50 }); // newest page; `before: <id>` pages older
await chatPost({ handle: 'matt', room: 'build', body: 'on it' });

const health = await daemonHealth();                           // { reachable, error? }, never throws
const stop = createRelay({                                     // one daemon subscription, republished
  match: t => t.startsWith('chat/'),                            // `chat/<room>/msg`
  topic: 'chat',
  publish: (topic, data) => server.publish(topic, data),
});

| Function | Daemon verb | | --- | --- | | chatSignIn / chatSignOut / chatAway / chatBack | presence: the buddy-list row and its session heartbeat | | chatBuddies / chatWho / chatRooms | the roster, one room's members, a handle's rooms | | chatJoin / chatLeave / chatArchive | membership (wakeOn: mention \| all \| none); archive parks a room for everyone until a post revives it | | chatPost / chatDm / chatDmOpen / chatRead / chatMessages / chatMark | messages: post, DM, open a DM room without posting, read-and-advance, page, advance the cursor | | paneList / panePeek / paneSpawn / paneAccounts / paneDirectories | herdr panes: list with presence joined, peek a screen, start claude in a tab, cswap accounts, directory suggestions | | chatInvite | type /chat:join <room> into a pane; accepted, queued or refused | | createRelay / subscribe | the event stream; daemonHealth the reachability probe |

Pass { sockPath } as the trailing options to reach a non-default daemon socket. The verbs and their payloads are specified in repo-tools docs/superpowers/specs/2026-08-28-rt-chat-delivery-v2-design.md; the agent-facing rules are skills/rt-chat/SKILL.md.

Runs

RunStageRow.status is one of running | done | failed | redirected; rt runs stage-redirect writes the fourth when the work engine leaves a stage for another one, so a reader that maps statuses to icons or filters must handle all four.

License

MIT