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

@nowly/cli

v1.3.0

Published

CLI to create, build, and validate [Nowly](https://nowly.me) presences.

Readme

@nowly/cli

CLI to create, build, and validate Nowly presences.

Install

npm install -g @nowly/cli

Requires Node.js 22+.

Quick start

nowly init "YouTube"
cd src/Y/YouTube
# Edit presence.ts, then:
nowly build YouTube

nowly build outputs to dist/presences/{slug}/ with bundle.js, metadata.json, and optional settings and language packs.

Commands

nowly init [name]

Scaffold a new presence.

| Option | Type | Default | Description | |---|---|---|---| | --category | string | — | streaming, music, video, social, gaming, tools, ai, learning, creator, other | | --color | hex | #555555 | Accent color for the presence | | --urls | string | — | Comma-separated list of URLs the presence runs on | | --author | string | Nowly | Author name | | --github | string | — | Author GitHub handle | | --description | string | — | Short description (en-US) |

Omitting an option starts an interactive prompt.

nowly init "Netflix" --category streaming --color "#E50914" --urls "netflix.com" --author "Steellgold"

Creates:

src/N/Netflix/
├── presence.ts
├── metadata.json
└── assets/

nowly build [slug]

Build one or all presences.

nowly build              # Build every presence in src/
nowly build youtube      # Build only the "youtube" presence

Each build produces:

  • dist/presences/{slug}/bundle.js — Minified IIFE (esbuild, es2022 target)
  • dist/presences/{slug}/metadata.json — Presence metadata
  • dist/presences/{slug}/settings.json — Extracted user settings (if any)
  • dist/presences/{slug}/assets/ — Copied assets

A dist/presences/registry.json is generated listing all built presences. When a presence contains a locales directory, its validated JSON files are also copied to dist/presences/{slug}/locales/ and included in generated metadata.

Optional language packs

A presence can keep using inline strings without adding language files. To localize its Discord text, add all three supported dictionaries:

src/Y/YouTube/locales/
├── en-US.json
├── fr-FR.json
└── es-ES.json

Each file must be a flat JSON object containing the same keys and string values. en-US.json is the reference dictionary and runtime fallback. Invalid JSON, unsupported files, missing locales, non-string values, and mismatched keys fail validation and builds.

Use the English dictionary to get typed autocomplete without generating types:

import type enUS from "./locales/en-US.json"

const locale = await presence.getStrings<typeof enUS>()

nowly list (alias: ls)

List all presences in src/.

nowly list

  Name       Category      Slug
 ─────────────────────────────────
  YouTube    video         youtube
  Netflix    streaming     netflix
  GitHub     tools         github
  ...

nowly validate [slug]

Validate presence metadata and structure.

nowly validate           # Validate all presences
nowly validate youtube   # Validate only "youtube"

Checks for required fields: name, color, category, description.en-US, assets.logo, assets.icon, assets.thumbnail, and that presence.ts exists.

Interactive mode

Run nowly with no arguments to open a menu:

┌──────────────────────────────────┐
│  Nowly CLI v1.2.2                │
│                                  │
│  ○ Create a new presence         │
│  ○ Build presences               │
│  ○ List all presences            │
│  ○ Validate presences            │
│  ○ Exit                          │
└──────────────────────────────────┘

After each action you can choose to continue or exit.

Project structure

├── src/
│   ├── A/
│   │   └── Amazon/
│   │       ├── presence.ts
│   │       └── metadata.json
│   ├── G/
│   │   └── GitHub/
│   │       ├── presence.ts
│   │       └── metadata.json
│   └── ...
├── dist/
│   └── presences/
│       ├── amazon/
│       │   ├── bundle.js
│       │   ├── metadata.json
│       │   └── settings.json
│       ├── github/
│       │   └── ...
│       └── registry.json
└── package.json

Presence script API

See @nowly/sdk for the full presence API documentation.

import { Presence, PresenceType } from "@nowly/presence";

const presence = new Presence({
  Settings({
    "show-button": {
      title: "Show button",
      description: "Display a button on Discord",
      type: "boolean",
      value: true,
    },
  }),
});

presence.on("UpdateData", async () => {
  presence.setActivity({
    details: "Browsing",
    state: "Some page",
    largeImageKey: "logo",
    type: PresenceType.Watching,
  });
});

License

MIT