Skip to main content
Extensions let you add new commands to the nuon CLI. Once installed, an extension called policies is available as nuon policies, just like a built-in command. Extensions are generally available and do not require preview flags. The extension system was heavily inspired by GitHub CLI extensions.

Available extensions

Compiled vs interpreted extensions

Extensions come in two flavors:
  • Compiled extensions ship precompiled binaries via GitHub Releases. The CLI downloads the correct binary for your platform during install. This is the standard distribution model for production extensions.
  • Interpreted extensions are cloned as source and run directly. The CLI supports two interpreted types:
    • Script: a single executable file (bash, Python, etc.) with a shebang line
    • Python: a uv-managed project executed via uv run
The extension type is auto-detected during installation, you don’t need to declare it. The CLI checks for GitHub Release assets first; if none match your platform, it clones the repo and detects the type from the directory contents.

Quick start

Managing extensions

All extension management commands live under nuon extensions (alias: nuon ext).

Browsing available extensions

This queries the nuonco GitHub organization for repositories matching the nuon-ext-* naming convention and shows which ones you already have installed. Extensions can be installed from any GitHub organization, but browse lists Nuon-authored extensions by default. Browse results are filtered to repositories with the nuon-extensions GitHub topic.

Installing an extension

You can provide the extension name in several formats: Shorthand names (without an org prefix) default to the nuonco GitHub organization. To install an extension from another org, use the full org/nuon-ext-name format.

How install works

The install behavior depends on the extension type:
  • Compiled extensions: The CLI checks the latest GitHub Release for a platform-specific binary asset (e.g. nuon-ext-policies-darwin-arm64). It also supports release archives like nuon-ext-policies-darwin-arm64.tar.gz and nuon-ext-policies-darwin-arm64.zip. If a match is found, it downloads and installs the binary directly. No clone is needed.
  • Interpreted extensions: If no release asset matches your platform, the CLI clones the repository and detects the extension type from the directory contents (pyproject.toml for python, executable nuon-ext-<name> for script).
When @ref is specified (e.g. demo@main, nuonco/nuon-ext-demo@v1.0), the CLI first checks for a release at that tag and installs a matching binary asset if one exists. If no matching release asset is found (for example when using a branch name or commit SHA), it clones the repo at that exact ref.

Local install

Local installs create a symlink from the extensions directory to your source directory. This enables a live development loop: rebuild the binary or edit a script and the changes take effect immediately. The directory must contain a nuon-ext.toml and the directory name must match nuon-ext-<name>. If the extension is already installed, the command fails. For local development installs, remove and re-install from source to refresh the symlinked install.

Listing installed extensions

Shows a table of installed extensions with their name, version, repository, and description. Use --json for machine-readable output.

Upgrading extensions

Upgrade checks for newer GitHub releases and re-downloads the binary if a new version is available. This currently only works for compiled extensions installed from a release. Interpreted extensions installed via clone should be re-installed to pick up changes.

Removing an extension

This deletes the extension and its local files. For locally installed extensions (symlinks), the symlink is removed but the source directory is left untouched.

Running an extension explicitly

The exec subcommand is an escape hatch for cases where an extension name conflicts with a built-in command. In most cases, you can run extensions directly as top-level commands (e.g. nuon policies).

How extensions work

Top-level commands

When the CLI starts, it scans the extensions directory and registers each installed extension as a top-level command. This means nuon policies works the same as nuon ext exec policies. When using top-level extension commands, Nuon root flags (for example -f / --config) are handled by the CLI, and only arguments after the extension command are forwarded to the extension process.

Environment variables

When running an extension, the CLI provides context through environment variables: Extensions can use these variables to interact with the Nuon API or read the local configuration. These values are populated from your active CLI config/context.

Authentication

Extensions declare their auth requirements in a nuon-ext.toml manifest. If an extension requires an API token or org context and one isn’t configured, the CLI prints a warning before running the extension. The extension still runs and is allowed to handle the missing credentials on its own.

Local storage

Extensions are stored under ~/.config/nuon/extensions/. Each extension gets its own directory:

Creating an extension

Every extension needs two things: a GitHub repository named nuon-ext-<name> and a nuon-ext.toml manifest at the root. Beyond that, the approach differs depending on whether you’re building a compiled or interpreted extension.

Repository name

The repository must use the nuon-ext- prefix. The rest becomes the command name:

Extension manifest

Every extension repository must include a nuon-ext.toml file at the root:
nuon-ext.toml

Compiled extensions (binary)

Compiled extensions ship precompiled binaries for each platform via GitHub Releases. This is the best choice when you want fast startup, no runtime dependencies, and a clean upgrade path via nuon ext upgrade. Create GitHub releases with assets following this naming scheme:
Archive variants are also supported with the same base name:
For example:
The CLI automatically selects the correct binary for the user’s platform during install. You can use any language and build system. GoReleaser works well for Go extensions.

Interpreted extensions (script and python)

Interpreted extensions are cloned as source and run directly. They’re simpler to set up and great for tooling that doesn’t need to be compiled. For example: documentation generators, linters, config validators, and similar utilities. The CLI supports two interpreted types:

Script extensions

Place a single executable file named nuon-ext-<name> at the repo root with a shebang line:
nuon-ext-hello
The script receives the same environment variables as any other extension. Make sure the file is marked executable (chmod +x).

Python extensions

Python extensions use a standard pyproject.toml with a console script entry point. The CLI runs them via uv run nuon-ext-<name>, so uv manages the virtualenv and dependencies automatically. A minimal project structure:
With a pyproject.toml entry point like:
We recommend click for building the CLI interface. Users install interpreted extensions with @ref to pin a branch: