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

@corti/cli

v0.10.0

Published

Corti CLI — wire Corti as an OpenAI-compatible provider into terminal AI coding agents (OpenCode, Pi, Crush, ForgeCode, Codex CLI) and more.

Readme

Corti CLI

npm License: MIT

Wire Corti as an OpenAI-compatible provider into your terminal AI coding agent.

npx @corti/cli@latest init models

The command prompts you to paste an .env block from the Corti Console (Developer quickstart → Copy all as .env variables) containing CORTI_TENANT_NAME, CORTI_CLIENT_ID, and CORTI_CLIENT_SECRET. It constructs the Bearer token (base64 of <tenant>:client_credentials:<client_id>:<client_secret>) and writes a corti provider entry into each tool you select. Pass -i, --interactive to enter credentials field-by-field instead.

| Tool | File written | Format | Notes | | ------------- | --------------------------------------------------------- | ------ | ----- | | OpenCode | opencode.json (or ~/.config/opencode/opencode.json) | JSON | Full provider entry — CORTI_BEARER referenced via {env:…} | | Pi | .pi/agent/models.json (or ~/.pi/agent/models.json) | JSON | providers.corti entry, api: "openai-completions", apiKey: "$CORTI_BEARER" | | ZCode | ~/.zcode/v2/config.json (global only) | JSON | provider.corti entry with the bearer embedded (ZCode has no env indirection); restart ZCode after, see below | | Crush | .crush.json (or ~/.config/crush/crush.json) | JSON | Full provider entry — $CORTI_BEARER substitution | | ForgeCode | .forge.toml | TOML | [[providers]] entry with api_key_vars = "CORTI_BEARER" (selected by default) |

It also writes a managed # >>> corti-cli >>> block to .env with:

  • CORTI_CLIENT_ID
  • CORTI_CLIENT_SECRET
  • CORTI_TENANT
  • CORTI_BEARER (base64 of <tenant>:client_credentials:<client_id>:<client_secret>, used by the tools)
  • CORTI_BASE_URL

Each tool config references CORTI_BEARER rather than embedding the secret in the JSON/TOML file, so the credential stays out of version control. Two exceptions embed the bearer because the tool offers no env indirection: ForgeCode's .credentials.json sidecar and ZCode's ~/.zcode/v2/config.json (both global/home-dir files, never in your repo).

Under global scope it also offers to add a managed block to your shell rc (~/.zshrc, ~/.bashrc / ~/.bash_profile, or fish config, resolved from $SHELL) so new shells load the CORTI_* variables from ~/.env automatically. The block exports only CORTI_* lines — it never sources the whole file and never contains secret values. It shows up as its own row on the plan screen (press s there to skip it), and the file is backed up before its first edit. Project scope never touches your shell rc.

Usage

corti init models [options]

  --client-id <id>          Corti client ID     (else $CORTI_CLIENT_ID,     else prompted)
  --client-secret <secret>  Corti client secret (else $CORTI_CLIENT_SECRET, else prompted)
  --tenant-name <name>      Corti tenant name   (else $CORTI_TENANT,        else prompted; default base)
  --base-url <url>          Corti base URL      (else $CORTI_BASE_URL,      else prompted; default https://ai.eu.corti.app/v1)
  --region <region>         Corti region, e.g. eu (else $CORTI_ENVIRONMENT) — sets the base URL
  --model <id>              Default model       (else $CORTI_MODEL,  else picked from /models, else prompted)
  --tools <list>            Comma-separated subset: opencode,pi,zcode,crush,forgecode (else $CORTI_TOOLS)
  --scope <project|global>  Where to write configs (else $CORTI_SCOPE; default global)
  --shell-rc                Wire your shell rc to load CORTI_* into new shells (headless is opt-in; interactive default)
  --no-shell-rc             Never touch your shell rc file
  -y, --yes                 Non-interactive; fail if required values are missing
  -i, --interactive         Prompt for credentials field-by-field instead of pasting a .env block
  --dry-run                 Print file changes without writing them
  --no-verify               Skip the /models probe (--yes skips prompts, not verification)
  --json                    Emit exactly one JSON document on stdout; all text to stderr (else $CORTI_JSON)
  --no-color                Disable color output (NO_COLOR and TERM=dumb are also honored)
  --theme <mode>            Palette for light/dark terminals (else $CORTI_THEME; default auto — probes the background)

Resolution order per value: flag → env var → interactive prompt. With --yes, missing required values fail fast (exit 2), the /models probe still runs (--no-verify opts out), and Ink/React are never loaded.

When a newer version is published, the wizard shows a one-line update row at startup; headless runs print the notice on stderr after the command finishes (never in CI, --json runs, or piped output; set NO_UPDATE_NOTIFIER=1 to opt out). Prefer npx @corti/cli@latest — it always runs the newest release.

Exit codes

| Code | Meaning | | ---- | ------- | | 0 | Full success (including "everything already up to date" and clean --dry-run) | | 1 | Internal error, or plan declined | | 2 | Usage/validation error (bad flag, missing flag under --yes, no TTY without --yes) | | 3 | Auth failure (HTTP 401/403 from the probe) | | 4 | Network/API failure (DNS, timeout, TLS, connection refused, 5xx) | | 5 | Run failed with nothing modified (total write failure, backup gate, stale plan) | | 6 | Partial success (some tools configured, some failed; includes env-write failure) | | 130 | Interrupted (SIGINT/SIGTERM) |

Every failure carries a stable E_* code documented in docs/errors.md.

--json

--json puts exactly one versioned JSON document on stdout — on success and failure — with all human-readable progress on stderr. Combine with --yes (writes) or --dry-run (preview):

corti init models --yes --json --tools opencode --model corti-1 \
  --client-id … --client-secret …          # writes, then emits the result document
corti init models --json --dry-run --tools opencode --model corti-1 \
  --client-id … --client-secret …          # previews with would-* actions

The document carries ok, exitCode, resolved (each value with its provenance), probe, per-file results (with diffs — secret-bearing files are diffstat-only, never a diff body), env, backupDir, warnings, and error: {code, message} | null. Secrets never appear in it.

Backups

Before modifying any existing file, the CLI copies it to ~/.local/share/corti/backups/<timestamp>/ (mode 0700, manifest included) and verifies the file on disk still matches what was planned — a mismatch aborts the whole run with nothing written.

What it does

  1. Builds the Bearer token as base64 of <tenant>:client_credentials:<client_id>:<client_secret>.
  2. Probes GET {base-url}/models with that Bearer.
    • On success, lets you pick a model.
    • On failure (auth or network), surfaces the status in the side panel and falls back to a typed model id.
  3. Detects which tools are installed (best-effort) and pre-selects them.
  4. Asks before writing. Each writer merges into existing config rather than overwriting unrelated keys.
  5. Writes are atomic (temp file + rename) and idempotent — a second run is a no-op.

After init

New shells load CORTI_* automatically if you accepted the shell rc block (global scope). For the current shell — or if you skipped the block or used project scope — load .env manually so CORTI_BEARER is available when the coding tool starts:

set -a; . .env; set +a

Then run the tool:

opencode --model corti/<model>     # OpenCode
pi                                 # Pi           (select provider 'corti')
crush                              # Crush        (select provider 'corti')
forge                              # ForgeCode

ZCode is a desktop app: restart it after init (it loads providers at launch), then pick the Corti provider in chat.

Development

pnpm install
pnpm build
pnpm test
node ./dist/cli.js init models --dry-run \
  --client-id test-client-id \
  --client-secret test-client-secret \
  --base-url https://ai.eu.corti.app/v1 \
  --model corti-1 \
  --tools opencode,pi,zcode,crush,forgecode

Releasing

Releases are automated — merging to main runs semantic-release, which derives the version from the conventional commit message and publishes to npm via OIDC. Since PRs are squash-merged, that commit message is the PR title.

  • fix: → patch · feat: → minor · feat!: / BREAKING CHANGE: → major
  • Prerelease: push a suffixed tag — git tag v1.2.0-rc.1 && git push origin v1.2.0-rc.1 (publishes under the rc dist-tag, never latest)
  • Don't hand-push a bare vX.Y.Z tag — CI rejects it; stable goes through main

License

MIT