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

@pitcher/cli-scripts

v2.16.0

Published

The Pitcher CLI for various tasks in its ecosystem.

Readme

@pitcher/cli-scripts

Pitcher CLI Tools provide a suite of command-line utilities to streamline your workflow. For the best experience, we recommend installing the tools globally using npm install -g @pitcher/cli-scripts.

Help

p --help
# or for specific command help:
p command --help

MCP server

Run p mcp to start a stdio MCP server that exposes this CLI's commands as tools to an MCP-capable host (Claude Desktop, Claude Code, etc). It wraps 121 tools, one per CLI run* function: every command above (including dsr create + dsr bind-landing and the full ld surface) except the five local scaffold subcommands (which just print/write a starter file and make no request). dev-sync is exposed as a one-shot sync only; the continuous watch mode is not available as a tool.

p mcp
# or without a global install
npx @pitcher/cli-scripts mcp

Environment variables

Auth and org scope are resolved once at boot from environment variables. They are never tool inputs, so the model cannot change org or credentials mid-session.

  • NG_API_KEY (required, secret): the API key the server authenticates as.
  • NG_ORG (default dev): organization subdomain, or set NG_URL directly for a full origin.
  • NG_ENV (prod or test): controls the destructive-hint guard on write tools (see below). Defaults to a guess based on NG_ORG when unset.

One config entry = one org, because the API key is org-specific. To work against multiple orgs, add multiple entries (e.g. pitcher-dev, pitcher-prod) with different env blocks; do not try to pass org per tool call.

Host config example (Claude Desktop / .mcp.json)

"pitcher": {
  "command": "npx",
  "args": ["-y", "@pitcher/cli-scripts", "mcp"],
  "env": { "NG_API_KEY": "<key>", "NG_ORG": "dev" }
}

Tool annotations

  • Read commands are marked readOnlyHint. dev-sync counts as a write (it mutates remote dev-override state).
  • publish-app is always marked destructiveHint (it creates a live, org-visible release). Other write tools get destructiveHint only when the resolved environment is production (NG_ENV=prod, or NG_ORG other than dev with NG_ENV unset). This steers hosts toward requiring extra confirmation before mutating a production org.

Desktop Extension (.mcpb)

For one-click install in Claude Desktop (name + icon, an install-time API-key/org form, no hand-edited config, no Node prerequisite), build a .mcpb bundle:

yarn mcpb:pack        # → pitcher.mcpb (validated + packed)

Install by double-clicking pitcher.mcpb (or Settings → Extensions → Install Extension). Claude Desktop prompts for the API key (stored in the OS keychain) and organization; both are editable afterward, so switching orgs needs no reinstall.

The bundle reuses the same server as p mcp but registers a content-tool subset (118) — it omits the developer-only publish-app and dev-sync/dev-sync-revert. The organization field accepts a subdomain (acme), a bare hostname (acme.my.pitcher.com), or a full https:// URL. Build details (esbuild single-file, version injection) live in mcpb/ and CLAUDE.md.

This section is a reference only. For app-development conventions (modules, settings, mount points, SFDC), see the pitcher-apps skill, not this README.

To run the publish-app command:

# Basic usage
p publish-app <folder> --url https://pitcher.my.pitcher.com --api-key myapikey

# Publish to current folder and default org (dev)
p publish-app --api-key myapikey

# Publish to a specific organization subdomain (e.g., pitcher.my.pitcher.com)
p publish-app --api-key myapikey --org pitcher

# Dry run with version increment
p publish-app --api-key myapikey --org pitcher --dry-run --increment-version

# Publish and install to specific instances
p publish-app --api-key myapikey --org pitcher --increment-version --install-to 01JB8KQRM3XYZ2P4N5W7T9VHFA,01JB8KR0P6QS4T8X1M9Y3C7ZBE

# Publish and install to all instances with a specific environment name (case-insensitive)
p publish-app --api-key myapikey --org pitcher --increment-version --install-to-env-name Demo

# Combine both: install to specific instances AND all instances in an environment
p publish-app --api-key myapikey --org pitcher --increment-version --install-to 01JB8KS5F2WH9V6D8R3N7JXMQK --install-to-env-name Demo

# Alternative: Set API key via environment variable
export NG_API_KEY=myapikey
p publish-app --org pitcher --increment-version --install-to-env-name Production

Options

  • --api-key <apiKey>: API Key for authentication (can also use NG_API_KEY env variable)
  • --org <organization>: Organization subdomain (e.g., "pitcher" for pitcher.my.pitcher.com, default: "dev")
  • --increment-version: Increment version in app.json with timestamp
  • --dry-run: Perform a dry run without publishing
  • --publish-url <url>: Explicit publish URL
  • --install-to <instanceIds>: Comma-separated list of instance IDs to install the app to after publishing
  • --install-to-env-name <envName>: Environment name to filter instances and install the app to after publishing (case-insensitive)

To run the dev-sync command:

Sync a marketplace app directly to S3 for live development testing without publishing. This is useful for testing app changes in a pre-production environment.

# One-time sync from dist folder
p dev-sync sync --file-id <pitcher_file_id> --api-key myapikey

# Sync to a specific organization (default: dev)
p dev-sync sync --file-id <pitcher_file_id> --org pitcher

# Watch mode - auto-sync on file changes (reverts on Ctrl+C)
p dev-sync watch --file-id <pitcher_file_id> --api-key myapikey

# Watch with custom debounce (default: 2500ms)
p dev-sync watch --file-id <pitcher_file_id> --debounce 5000

# Manually revert dev override to restore original version
p dev-sync revert --file-id <pitcher_file_id> --api-key myapikey

# Alternative: Set API key via environment variable
export NG_API_KEY=myapikey
p dev-sync watch --file-id <pitcher_file_id> --org pitcher

Options

  • --file-id <id>: (Required) File ID of the installed app (pitcher_file.id) - copy this from the installed apps view
  • --api-key <apiKey>: API Key for authentication (can also use NG_API_KEY env variable)
  • --org <organization>: Organization subdomain (default: "dev")
  • --debounce <ms>: Debounce time in milliseconds for watch mode (default: 2500)

Notes

  • Run from your app's project root (with dist/ folder) or directly from the dist/ folder
  • The watch command automatically reverts changes on graceful shutdown (Ctrl+C)
  • Apps with dev override show a badge in the installed apps view

To run the files command:

Manage Pitcher content files — list within an instance, fetch by id, create (upload or link), or update.

# List files in an instance (paginated)
p files list --instance-id 01... --api-key myapikey

# Filter by tags or specific IDs
p files list --instance-id 01... --tags brochure,demo --api-key myapikey

# Get a single file by id
p files get 01FILEID... --api-key myapikey

# Create a file from a local upload
p files create --instance-id 01... --name "Q3 Deck" --file ./deck.pdf --api-key myapikey

# Create a link/WEB file from a source URL
p files create --instance-id 01... --name "Product Page" --source-url https://example.com --api-key myapikey

# Update a file's tags or metadata (metadata/app-metadata are deep-merged, not replaced)
p files update 01FILEID... --tags updated,reviewed --api-key myapikey

To run the folders command:

Manage Pitcher content folders — list, fetch (or the instance root), create, and update smart-folder rules.

# List folders in an instance (paginated)
p folders list --instance-id 01... --api-key myapikey

# Get a folder by id
p folders get 01FOLDERID... --api-key myapikey

# Get the instance root folder
p folders get --root --instance-id 01... --api-key myapikey

# Create a folder under a parent
p folders create --name "Marketing" --parent-folder-id 01PARENTID... --api-key myapikey

# Update a folder's name or access type
p folders update 01FOLDERID... --name "Sales Enablement" --access-type restricted --api-key myapikey

To run the search command:

# Ranked cross-type search (files, canvases, users, apps) within an instance
p search --instance-id 01... --query "quarterly deck" --api-key myapikey

To run the instances command:

Discover Pitcher instances — resolve names to instance IDs before running other commands.

# List instances (optionally filter by region/storage-region/environment)
p instances list --api-key myapikey

p instances list --environment Demo --api-key myapikey

# Get a single instance by id
p instances info 01INSTANCEID... --api-key myapikey

To run the canvases command:

Read Pitcher canvases — presentations, sections, and blocks.

# List canvases in an instance (paginated)
p canvases list --instance-id 01... --api-key myapikey

# Only templates or only sections
p canvases list --instance-id 01... --is-template --api-key myapikey
p canvases list --instance-id 01... --is-section --api-key myapikey

# Only blocks (blocks are hidden unless --is-block is passed)
p canvases list --instance-id 01... --is-block --api-key myapikey

# Get a single canvas by id
p canvases get 01CANVASID... --api-key myapikey

To run the shared-links command:

Create and read share links for canvases or files (returns the short_url).

# Create a share link for a canvas
p shared-links create --canvas-id 01CANVASID... --api-key myapikey

# Create a share link for a file
p shared-links create --file-id 01FILEID... --api-key myapikey

# Fetch the existing share link for a canvas
p shared-links get --canvas-id 01CANVASID... --api-key myapikey

To run the tags command:

List tags in an instance (the server requires a search term of at least 3 characters).

# List tags matching a search term
p tags list --instance-id 01... --search "demo" --api-key myapikey

# Filter by type
p tags list --instance-id 01... --search "demo" --type canvases --api-key myapikey

To run the email-template command:

Create/list/get HTML email templates for the emails app. create, list, and get require --instance-id (scaffold does not).

# Create a template from an HTML file (inline CSS)
p email-template create --instance-id 01... --name "Welcome" --body-file ./welcome.html --api-key myapikey

# ...or pass the HTML inline instead of a file (use one, not both)
p email-template create --instance-id 01... --name "Welcome" --body-html "<p>Hi {{recipient.firstName}}</p>" --api-key myapikey

# List templates (optionally filter by name/status/category/target)
p email-template list --instance-id 01... --api-key myapikey

# Get a template by id
p email-template get --instance-id 01... --id 01TEMPLATEID... --api-key myapikey

# Generate a starter HTML file with tokens in context
p email-template scaffold --out ./welcome.html --api-key myapikey

Run p email-template create --help for the full token/marker reference.

scaffold returns the starter HTML in the envelope data (never printed raw to stdout); pass --out <path> to write it to a file instead.

To run the dsr-landing command:

Create/list DSR (Digital Sales Room) landing-page templates, and set one as the instance-scope default. create, list, and set-default require --instance-id (scaffold does not).

# Create a landing template from an HTML file (must contain the email-gate marker)
p dsr-landing create --instance-id 01... --name "Default" --html-file ./landing.html --api-key myapikey

# ...or pass the HTML (and optionally CSS) inline instead of a file (use one, not both)
p dsr-landing create --instance-id 01... --name "Default" --html "<div data-pitcher-email-gate></div>" --api-key myapikey

# Create and set as default in one call
p dsr-landing create --instance-id 01... --name "Default" --html-file ./landing.html --default --api-key myapikey

# Set an existing template as the default
p dsr-landing set-default --instance-id 01... --id 01TEMPLATEID... --api-key myapikey

# List templates in an instance
p dsr-landing list --instance-id 01... --api-key myapikey

# Generate a starter HTML file with the required marker and tokens in context
p dsr-landing scaffold --out ./landing.html --api-key myapikey

The landing HTML (whether via --html-file or --html) must contain the data-pitcher-email-gate marker verbatim (the platform injects the email-gate form there) — create fails client-side before any request if it's missing. Provide exactly one of --html / --html-file (and at most one of --css / --css-file).

Run p dsr-landing create --help for the full token/marker reference (editable-region attributes, recipient/customer/org/rep tokens).

scaffold returns the starter HTML in the envelope data; pass --out <path> to write it to a file instead.

To run the announcements command:

Create/list announcement records for the wa-announcements app. create and list require --instance-id (scaffold does not).

# Create from a JSON data file
p announcements create --instance-id 01... --from-file ./announcement.json --api-key myapikey

# Create from convenience flags only
p announcements create --instance-id 01... --title "New feature" --body "<p>Check it out</p>" --api-key myapikey

# List announcements
p announcements list --instance-id 01... --api-key myapikey

# Generate a fully populated starter data.json
p announcements scaffold --out ./announcement.json --api-key myapikey

create requires either --from-file or both --title and --body. --title/--body/--priority override matching keys from --from-file when both are given. Run p announcements create --help for the full data field catalog (media, links, scheduling, target groups, popup overrides) — note start_at/expires_at are naive local dates (YYYY-MM-DD[THH:mm]), not ISO/UTC.

scaffold returns the starter JSON in the envelope data; pass --out <path> to write it to a file instead.

To run the themes command:

Read canvas/branding themes (colors via settings, fonts via typography, plus assets/logo) to reference when authoring email/DSR/canvas content. Read-only.

# List all themes in the org (returns full theme objects)
p themes list --api-key myapikey

# Narrow to one instance; add per-theme canvas usage and canvas ids
p themes list --instance-id 01... --with-usage --with-canvas-ids --api-key myapikey

# Get one theme by id
p themes get --id 01THEMEID... --api-key myapikey

list is server-capped (newest-first) with no --limit/--page; when the cap is hit the envelope carries a truncation warning. --instance-id narrows to themes bound to that instance and excludes org-level (default) themes, so omit it to see everything usable in the org.

To run the org command:

Read the organization the API key belongs to.

p org get --api-key myapikey

To run the analytics command:

Read rollup analytics totals (org/instance), KPI metrics, and content/user engagement analytics. org/instance are Django rollups; kpi and the 7 verbs below hit the clickhouse-api service (clickhouse.pitcher.com) via the same env resolution as everything else — --org/--url pick the environment (routed via x-original-domain). Access follows the key user's real role: popularity, users, files, and canvases need an org-admin key; engagement, viewers, and sessions work with a member key. kpi and sessions return the raw payload; the other row-heavy verbs return a shaped envelope (summary, rows, total_count, returned, has_more, and sometimes truncated/warnings) capped by --limit (default 20, max 100). Date-range verbs default to a 30-day window when --start-date/--end-date are omitted. viewers and sessions require --canvas-id; sessions also requires --user.

# Organization-wide rollups (org-admin key required)
p analytics org --api-key myapikey

# Instance rollups (instance-admin or org-admin key required)
p analytics instance --instance-id 01... --api-key myapikey

# KPI metrics from the clickhouse analytics service (PIT-7647).
# Access follows the key user's real role on the instance; --org/--url pick the
# env exactly as for other commands (they route via x-original-domain).
# Activity metrics need a date window; inventory counts (total-members, ...) do not.
p analytics kpi --instance-id 01... --type total-members --api-key myapikey
p analytics kpi --instance-id 01... --type total-active-users \
  --start-date 2026-01-01 --end-date 2026-01-31 --api-key myapikey

# DSR/content engagement rows (member key)
p analytics engagement --instance-id 01... --engagement-level high --limit 50 --api-key myapikey

# Most-viewed canvases / view-analysis (org-admin key)
p analytics popularity --instance-id 01... --sort-by total_views --sort-order DESC --api-key myapikey

# User roster (org-admin key)
p analytics users --instance-id 01... --api-key myapikey

# Active-user statistics instead of the roster (org-admin key)
p analytics users --instance-id 01... --stats --start-date 2026-01-01 --end-date 2026-01-31 --api-key myapikey

# File usage analytics (org-admin key)
p analytics files --instance-id 01... --file-type pdf --sort-by views --sort-order desc --api-key myapikey

# Per-section canvas engagement list (org-admin key)
p analytics canvases --instance-id 01... --group-ids 01GROUPID... --api-key myapikey

# DSR canvas viewers — who viewed a given canvas (member key)
p analytics viewers --instance-id 01... --canvas-id 01CANVASID... --viewer-type external --api-key myapikey

# Per-viewer session event timeline for a DSR canvas (member key)
p analytics sessions --instance-id 01... --canvas-id 01CANVASID... --user [email protected] --api-key myapikey

MCP: all ten verbs are exposed as read-only pitcher_analytics_* tools, alongside a pitcher://analytics/catalog resource (which verb answers which question — returns, time model, key tier) and four query prompts — top_content, content_engagement_summary, who_viewed_canvas, adoption_snapshot.

To run the users command:

Read users in the org (read-only). Org scope comes from the API key via the proxy x-org-id header — there is no --org-id flag.

# List users in the org (paginated)
p users list --api-key myapikey

# Filter by instance membership, roles, active status, or content-distribution group
p users list --instance-id 01... --api-key myapikey
p users list --instance-id 01... --instance-role editor --api-key myapikey
p users list --role admin --api-key myapikey
p users list --is-active --api-key myapikey
p users list --cdg 01GROUPID... --api-key myapikey

# Get a single user by id (user ids are integers, not ULIDs)
p users get --id 2446 --api-key myapikey

List filters: --instance-id, --instance-role (server-side requires --instance-id), --role, --is-active, --cdg, plus --page/--page-size (max 500). ID shapes: user ids are integers; instance and group ids are ULIDs.

To run the invitations command:

Read pending user invitations (read-only).

# List pending invitations (paginated)
p invitations list --api-key myapikey

# Get a single invitation by id (invitation ids are 'uinv_...' strings, not ULIDs)
p invitations get --id uinv_0yKQ41LLqOI2kn4E --api-key myapikey

To run the memberships command:

Read instance memberships, add members, and change their role.

# List memberships (filter by instance or user; user ids are integers)
p memberships list --instance-id 01... --api-key myapikey
p memberships list --user-id 2446 --api-key myapikey

# Add a user to an instance (always created as 'member')
p memberships add --instance-id 01... --user-id 2446 --api-key myapikey

# Change a membership's role (org-admin key required; membership ids are integers)
p memberships set-role --membership-id 3537 --role editor --api-key myapikey

add always creates the membership as member; use set-role to change it. set-role needs an org-admin key (an instance-admin key can add but not set-role). --membership-id is the integer membership row id from memberships list (not a user id). --role is the short role name (e.g. editor) discovered via p roles list — the prefixed key (instance:editor) is rejected. --scope defaults to instance; --scope organization targets the organization membership row instead — note its row id is NOT discoverable via this CLI today (memberships list returns instance memberships only), so use it only when you already have the org-membership id from another source.

To run the groups command:

Read/create content-distribution groups and manage their members.

# List groups (filter by instance)
p groups list --instance-id 01... --api-key myapikey

# Get a group by id
p groups get --id 01GROUPID... --api-key myapikey

# Create a group (name min 3 chars)
p groups create --instance-id 01... --name "West Region" --api-key myapikey

# Add a user to a group (user ids are integers)
p groups add-user --group-id 01GROUPID... --user-id 2446 --api-key myapikey

# List a group's members
p groups list-users --group-id 01GROUPID... --api-key myapikey

add-user is rejected on auto-login-managed groups (their membership is derived, not manually editable).

To run the roles command:

Read the org's role set (6 system roles + any custom roles) — the discovery aid for p memberships set-role.

# List the org's roles
p roles list --api-key myapikey

# Filter by scope
p roles list --scope instance --api-key myapikey

list returns a { items: [...] } object; each item's role_key is prefixed (org:admin, instance:editor). Pass the short name (e.g. editor) to set-role, not the prefixed key. Like set-role, roles list currently needs an org-admin key: while the granular-permissions rollout is off (the production default), the endpoint 403s non-org-admin callers.

To run the metadata-template command:

Read and additively build an instance's metadata-field schema. list and upsert require --instance-id (get only needs --id; scaffold makes no request).

# Read the schema for an instance (0 or 1 template — one per instance)
p metadata-template list --instance-id 01... --api-key myapikey

# Get one template by id (ids are ULIDs)
p metadata-template get --id 01TEMPLATEID... --api-key myapikey

# Print a starter schema to fill in (optionally to a file)
p metadata-template scaffold --out ./schema.json

# Additively upsert the schema (merges by section label)
p metadata-template upsert --instance-id 01... --sections-file ./schema.json --api-key myapikey

upsert merges the incoming sections by section label — a section whose label matches an existing one is replaced, other sections are preserved. It cannot remove a section or field (that's a UI operation). options for select/multi-select fields are { label, value } objects (bare strings are rejected). upsert requires an instance- or org-admin key; a non-admin member key is rejected (400 → VALIDATION).

To run the dsr command:

Create and share a Digital Sales Room (a canvas share link) and get a link back in one command. Three subcommands: create (build + share, with optional landing-page binding), bind-landing (bind/unbind a landing template on an existing share), and scaffold (print a starter {source,security,landing} spec for --config-file).

# simplest — email-gated link (server default auth_strategy=any_email)
p dsr create --instance-id 01... --canvas-id 01... --api-key myapikey
# open link
p dsr create --instance-id 01... --canvas-id 01... --auth-strategy none
# ShareBox + security + branded landing
p dsr create --instance-id 01... --sharebox-file-ids 01A,01B --account-id 01... \
  --auth-strategy specified_domains --allowed-domains acme.com --valid-until 2026-09-01T00:00:00Z \
  --landing-template-id 01... --landing-context '{"rep":"Sam"}'
# bind/unbind a landing template on an existing share
p dsr bind-landing --id 01... --landing-template-id 01...
p dsr bind-landing --id 01... --landing-template-id none
# scaffold a spec for --config-file
p dsr scaffold --out share.json

Creating needs a share-capable key; binding a landing page currently needs an editor key (PIT-7525 will open it to reps for their own DSRs) — a denied bind returns the link with a warning. Discover landing template IDs via p dsr-landing list.

To run the ld command:

Author L&D (MicroLearn) content — courses, modules, certificates, and assessments — against the standalone L&D backend. The general form is p ld <resource> <verb>, plus the top-level p ld create-canvas recipe (create → set-canvas → publish in one step). --instance-id is required on every subcommand.

Unlike the other commands, L&D targets a separate backend (not <org>.my.pitcher.com). The base URL is resolved automatically from the instance's ld-app config; override with --ld-url or the NG_LD_API_URL env var (e.g. for a region whose ld-app config is unavailable). Auth is the same org x-api-key. The surface is authoring-only — enrollment, attempts, progress, groups, and analytics are not exposed (the org key cannot reach them). set-canvas likewise has no --require-recompletion: the backend rejects that flag for API-key callers (the bulk rep-completion reset is human-admin-only), so it could only ever 403.

# headline: author a designed canvas course in one step (create → set-canvas → publish)
p ld create-canvas --instance-id 01... --title "Onboarding 101" --canvas '{"content":{"data":[]}}' --publish

# courses
p ld course list --instance-id 01... --status draft
p ld course create --instance-id 01... --title "Intro" --description "A short intro course." --level Beginner --duration 10 --sections-count 1
p ld course set-canvas --instance-id 01... --id 01... --canvas-file ./canvas.json
p ld course publish --instance-id 01... --id 01...
p ld course add-to-modules --instance-id 01... --id 01... --module-ids m1,m2

# modules / certificates / assessments
p ld module create --instance-id 01... --title "Basics" --subtitle "Getting started" --description "Foundational module."
p ld module add-courses --instance-id 01... --id m1 --course-ids c1,c2
p ld cert create --instance-id 01... --title "Certified Rep"
p ld assessment create --instance-id 01... --title "Quiz 1"
p ld assessment add-question --instance-id 01... --id a1 --question '{"type":"true_false","questionText":"...","config":{},"points":1}'

# override the L&D base URL (else resolved from ld-app config)
NG_LD_API_URL=https://<lambda-host>/dev/api p ld course list --instance-id 01...

MCP: the same surface is exposed as pitcher_ld_* tools, plus a create_ld_canvas_course recipe prompt and a pitcher://design/ld-canvas-course resource. NG_LD_API_URL is the only way to override the L&D base URL under MCP (no per-call override, by design).