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

@matrice-technologies/served-tool

v0.2.1

Published

An app that serves its own CLI and skill to a Claude Code session, and mints the session's key through a device flow. The parts every such app shares.

Readme

served-tool

The parts every app shares that serves its own CLI and skill to a Claude Code session and mints the session's key through a device flow: the CLI core, the decisions of the device flow and the key store, and the build that stamps a skill and two counts into one served file. The design is written up as "the served tool" in truva-mapper-v2/docs/served-tool.md; what this package fixes is contract.md.

No runtime dependency. Node 20 or later.

Four entry points

  • @matrice-technologies/served-tool/cli: createTool({ identity, commands, options, version, skillFiles, help }) answers main(argv, deps). The core's login, whoami, version, upgrade, skill --install and help come with it; a project adds its own commands, each { usage, summary, run({ deps, values, positionals, identity, version }) }. api(deps, cfg, path, opts) and loadConfig(deps, identity) are what a command reaches the app with; runAsMain(import.meta.url, main) is the entry's last line.
  • @matrice-technologies/served-tool/server: openLogin, claimLogin, lookUpLogin, approveLogin, verifyKey, ownKeys, revokeOwnKey take a store and answer an outcome; a route turns the outcome into HTTP in its own envelope. memoryStore() is the interface implemented in memory. Beside them: the device arithmetic and the limiter, originOf, and the readers for the four served files.
  • @matrice-technologies/served-tool/build and the served-tool bin: served-tool build --entry cli/src/main.mjs --out cli/<tool>.mjs --skill cli/skill --ledger cli/version.json --repo-skill ../.claude/skills/<skill> bundles the entry through the project's esbuild and stamps the skill and the counts. served-tool embed stamps a script that already carries the spans.
  • @matrice-technologies/served-tool/guides: the skill's files as pages an app draws. parseMarkdown(source) answers blocks (headings, paragraphs, code, lists, tables, quotes, figures) with titleOf, bodyUnderTitle, headingsOf, plainText and markdownOf; skillGuides(files, { labels, order, base }) makes the served files guides, and guideTarget(target, pages, current, { href, base }) makes a link between them an address on the app, or null for a file the app does not serve. Pure, with types.

A project's entry, in outline

import { createTool, api, loadConfig, runAsMain, EXIT } from "@matrice-technologies/served-tool/cli";

const SKILL_FILES = __SERVED_TOOL_SKILL_FILES__;
const VERSION = __SERVED_TOOL_VERSION__;

const tool = createTool({
  identity: { tool: "my-tool", skill: "my-skill", app: "My App" },
  version: VERSION,
  skillFiles: SKILL_FILES,
  selfUrl: import.meta.url,
  commands: {
    things: {
      usage: "my-tool things [text]",
      summary: "the things in the app",
      run: async ({ deps, identity, positionals }) => {
        const cfg = loadConfig(deps, identity);
        const answer = await api(deps, cfg, `${identity.api}/things`);
        deps.stdout(`${JSON.stringify(answer, null, 2)}\n`);
        return EXIT.ok;
      },
    },
  },
});

export const { main } = tool;
export { SKILL_FILES, VERSION };
await runAsMain(import.meta.url, main);

runAsMain runs main when Node was started on the entry, directly or through a link, and ends by setting process.exitCode rather than calling process.exit. On macOS a write to a pipe is asynchronous, and process.exit(await main(...)) dropped what the pipe had not yet taken: a long --json answer piped to jq stopped at 64 KiB. A reader that stops early, as head does, ends the tool quietly.

Choices for a tool that existed before it was served

Each is off unless the identity or the command line asks for it, and a tool that asks for none behaves as 0.1 did.

Published to npm as well. Name the package, and upgrade leaves a copy npm installed to npm: it still takes the skill, and prints the command that takes the tool.

identity: { tool: "fixit", skill: "fixit", npmPackage: "@matrice-technologies/fixit-cli" }
$ fixit upgrade
updated the fixit skill (4 to 5)  /home/alex/.claude/skills/fixit
The skill changed. Read /home/alex/.claude/skills/fixit/SKILL.md again and follow that.
https://fixit.example serves tool 7 and this one is 6, installed by npm as @matrice-technologies/fixit-cli in /usr/local/lib/node_modules/@matrice-technologies/fixit-cli, so npm upgrades it.
Run: npm install -g @matrice-technologies/fixit-cli@latest

The names its users already have. Their variables win over <ENV>_URL and <ENV>_KEY, and their old config file is read while the new one does not exist. Neither is written.

identity: {
  tool: "fixit",
  skill: "fixit",
  envNames: { url: "FIXIT_HOST", key: "FIXIT_TOKEN" },
  legacyConfig: { path: ".fixit/config.json", url: "host", key: "token" },
}

One hosted app. With defaultUrl, login needs no address: it takes the one given, then the address variable, then the default. Every other command takes the address from the variable, then the config file, then the default, so a CI job that sets only the key reaches the hosted app.

identity: { tool: "fixit", skill: "fixit", defaultUrl: "https://fixit.matricetechnologies.com" }

A login in two commands. A session sees a command's output only when it ends, so it opens the login, shows the person the link and the code, and then waits for the approval. The pending login waits in ~/.config/<tool>/pending-login.json, as private as the config.

$ fixit login fixit.example --begin
  open   https://fixit.example/login/device
  code   ABCD-EFGH
  ...
$ fixit login --finish

The skill in one repository. skill --install --project [dir] writes it into <dir>/.claude/skills/<skill>/, the working directory by default, instead of ~/.claude/skills/<skill>/.

Tests

npm test runs the package's own: the device flow's properties against a clock, the CLI core against a fake platform and a fake disk, the embed against a temporary directory, the guides against skill files written in the test, and an entry in its own process with its stdout a pipe.

docs/consolidation.md says why 0.2 exists.