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

politty

v0.1.2

Published

A lightweight CLI framework inspired by citty with zod v4 registry integration for type-safe metadata management

Readme

politty

politty is a lightweight, type-safe CLI framework for Node.js built on Zod v4.

From simple scripts to complex CLI tools with subcommands, validation, and auto-generated help, you can build them all with a developer-friendly API.

Features

  • Zod Native: Use Zod schemas directly for argument definition and validation
  • Type Safety: Full TypeScript support with automatic type inference for parsed arguments
  • Flexible Argument Definition: Support for positional arguments, flags, aliases, arrays, and environment variable fallbacks
  • Subcommands: Build Git-style nested subcommands (with lazy loading support)
  • Lifecycle Management: Guaranteed setupruncleanup execution order
  • Signal Handling: Proper SIGINT/SIGTERM handling with guaranteed cleanup execution
  • Auto Help Generation: Automatically generate help text from definitions
  • Discriminated Union: Support for mutually exclusive argument sets

Requirements

  • Node.js >= 18
  • Zod >= 4.2.1

Installation

npm install politty zod
# or
pnpm add politty zod
# or
yarn add politty zod

Quick Start

import { z } from "zod";
import { defineCommand, runMain, arg } from "politty";

const command = defineCommand({
  name: "greet",
  description: "A CLI tool that displays greetings",
  args: z.object({
    name: arg(z.string(), {
      positional: true,
      description: "Name of the person to greet",
    }),
    greeting: arg(z.string().default("Hello"), {
      alias: "g",
      description: "Greeting phrase",
    }),
    loud: arg(z.boolean().default(false), {
      alias: "l",
      description: "Output in uppercase",
    }),
  }),
  run: (args) => {
    let message = `${args.greeting}, ${args.name}!`;
    if (args.loud) {
      message = message.toUpperCase();
    }
    console.log(message);
  },
});

runMain(command);

Example usage:

$ my-cli World
Hello, World!

$ my-cli World -g "Hi" -l
HI, WORLD!

$ my-cli --help
Usage: greet <name> [options]

A CLI tool that displays greetings

Arguments:
  name    Name of the person to greet

Options:
  -g, --greeting <value>  Greeting phrase (default: "Hello")
  -l, --loud              Output in uppercase
  -h, --help              Show help

Basic Usage

Defining Arguments

Use the arg() function to define argument metadata:

import { z } from "zod";
import { arg, defineCommand } from "politty";

const command = defineCommand({
  name: "example",
  args: z.object({
    // Positional argument (required)
    input: arg(z.string(), {
      positional: true,
      description: "Input file",
    }),

    // Optional positional argument
    output: arg(z.string().optional(), {
      positional: true,
      description: "Output file",
    }),

    // Flag (with alias)
    verbose: arg(z.boolean().default(false), {
      alias: "v",
      description: "Verbose output",
    }),

    // Environment variable fallback
    apiKey: arg(z.string().optional(), {
      env: "API_KEY",
      description: "API key",
    }),

    // Array argument (--file a.txt --file b.txt)
    files: arg(z.array(z.string()).default([]), {
      alias: "f",
      description: "Files to process",
    }),
  }),
  run: (args) => {
    console.log(args);
  },
});

Subcommands

Define Git-style subcommands:

import { z } from "zod";
import { arg, defineCommand, runMain } from "politty";

const initCommand = defineCommand({
  name: "init",
  description: "Initialize a project",
  args: z.object({
    template: arg(z.string().default("default"), {
      alias: "t",
      description: "Template name",
    }),
  }),
  run: (args) => {
    console.log(`Initializing with template: ${args.template}`);
  },
});

const buildCommand = defineCommand({
  name: "build",
  description: "Build the project",
  args: z.object({
    output: arg(z.string().default("dist"), {
      alias: "o",
      description: "Output directory",
    }),
    minify: arg(z.boolean().default(false), {
      alias: "m",
      description: "Minify output",
    }),
  }),
  run: (args) => {
    console.log(`Building to: ${args.output}`);
  },
});

const cli = defineCommand({
  name: "my-cli",
  description: "Example CLI with subcommands",
  subCommands: {
    init: initCommand,
    build: buildCommand,
  },
});

runMain(cli, { version: "1.0.0" });

Example usage:

$ my-cli init -t react
$ my-cli build -o out -m
$ my-cli --help

Lifecycle Hooks

Execute hooks in setupruncleanup order. The cleanup hook is always executed, even if an error occurs:

const command = defineCommand({
  name: "db-query",
  description: "Execute database queries",
  args: z.object({
    database: arg(z.string(), {
      alias: "d",
      description: "Database connection string",
    }),
    query: arg(z.string(), {
      alias: "q",
      description: "SQL query",
    }),
  }),
  setup: async ({ args }) => {
    console.log("[setup] Connecting to database...");
    // Establish DB connection
  },
  run: async (args) => {
    console.log("[run] Executing query...");
    // Execute query
    return { rowCount: 42 };
  },
  cleanup: async ({ args, error }) => {
    console.log("[cleanup] Closing connection...");
    if (error) {
      console.error(`Error occurred: ${error.message}`);
    }
    // Close connection
  },
});

API

defineCommand(options)

Define a command.

| Option | Type | Description | | ------------- | ----------------------------- | ------------------- | | name | string | Command name | | description | string? | Command description | | args | ZodSchema | Argument schema | | subCommands | Record<string, Command>? | Subcommands | | setup | (context) => Promise<void>? | Setup hook | | run | (args) => T? | Run function | | cleanup | (context) => Promise<void>? | Cleanup hook |

runMain(command, options?)

CLI entry point. Handles signals and calls process.exit().

runMain(command, {
  version: "1.0.0",    // Displayed with --version flag
  argv: process.argv,  // Custom argv
});

runCommand(command, argv, options?)

Programmatic/testing entry point. Does not call process.exit() and returns a result object.

const result = await runCommand(command, ["arg1", "--flag"]);
if (result.success) {
  console.log(result.result);
} else {
  console.error(result.error);
}

arg(schema, meta)

Attach metadata to an argument.

| Metadata | Type | Description | | ------------- | ---------- | ------------------------------------ | | positional | boolean? | Treat as positional argument | | alias | string? | Short alias (e.g., -v) | | description | string? | Argument description | | placeholder | string? | Placeholder shown in help | | env | string? | Environment variable name (fallback) |

Documentation

For detailed documentation, see the docs/ directory:

Examples

The playground/ directory contains many examples:

  • 01-hello-world - Minimal command configuration
  • 02-greet - Positional arguments and flags
  • 03-array-args - Array arguments
  • 05-lifecycle-hooks - Lifecycle hooks
  • 10-subcommands - Subcommands
  • 12-discriminated-union - Discriminated Union
  • 21-lazy-subcommands - Lazy loading

License

MIT