CLI to create, build, and validate Nowly presences.
npm install -g @nowly/cliRequires Node.js 22+.
nowly init "YouTube"
cd src/Y/YouTube
# Edit presence.ts, then:
nowly build YouTubenowly build outputs to dist/presences/{slug}/ with bundle.js, metadata.json, and optional settings and language packs.
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) |
--discord-native |
boolean | prompt | Write discordNative: true when Discord already supports the platform via account linking |
Omitting an option starts an interactive prompt. Without --discord-native, init asks this as a yes/no (default no) and only writes the flag when the answer is yes.
nowly init "Netflix" --category streaming --color "#E50914" --urls "netflix.com" --author "Steellgold"
nowly init "Spotify" --discord-nativeCreates:
src/N/Netflix/
├── presence.ts
├── metadata.json
└── assets/
Build one or all presences.
nowly build # Build every presence in src/
nowly build youtube # Build only the "youtube" presenceEach build produces:
dist/presences/{slug}/bundle.js- Minified IIFE (esbuild,es2022target)dist/presences/{slug}/metadata.json- Presence metadatadist/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.
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>()Build a presence and zip it for drop-install in the extension Debug panel.
nowly pack youtubeWrites dist/packs/{slug}.zip (metadata.json + bundle.js, plus assets and locales when present). Unsigned zips install only on unpacked builds or with developer mode enabled.
Download (or copy) a Chrome dev extension and bake one or more built presences into it.
nowly extension youtube
nowly extension youtube github --from ../nowly/apps/extension/dist/chromeWrites dist/extension-dev with dev-presences.json. Load that folder unpacked at chrome://extensions.
List all presences in src/.
nowly list
Name Category Slug
─────────────────────────────────
YouTube video youtube
Netflix streaming netflix
GitHub tools github
...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, url, presence.ts, locales/en-US.json, and assets/. If discordNative is present, it must be a boolean.
Run nowly with no arguments to open a menu:
┌──────────────────────────────────┐
│ Nowly CLI v1.2.2 │
│ │
│ ○ Create a new presence │
│ ○ Build presences │
│ ○ Pack a presence zip │
│ ○ List all presences │
│ ○ Validate presences │
│ ○ Exit │
└──────────────────────────────────┘
After each action you can choose to continue or exit.
├── 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
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,
});
});