# inup — full documentation > Interactive dependency upgrader for npm, yarn, pnpm & bun. Zero-config, monorepo-ready. Upgrade-interactive for every package manager. ## Getting started inup is an interactive CLI for upgrading outdated npm dependencies. It auto-detects your package manager, works in monorepos and workspaces, and requires zero configuration. ## Quick start Run it in any project — nothing to install first: ```bash npx inup ``` Or install globally with your preferred package manager: ```bash npm install -g inup pnpm add -g inup yarn global add inup bun add -g inup ``` ## What happens when you run it 1. inup scans the directory for `package.json` files — including every workspace in a monorepo — and detects your package manager from the lockfile (npm, yarn, pnpm or bun). 2. It checks the npm registry for newer versions of every dependency: production, dev, peer and optional, all loaded automatically. 3. The interactive picker opens. Browse packages, run the vulnerability audit (`s`), read changelogs (`i`), search (`/`), and select the upgrades you want. 4. Press `Enter` — inup writes the new ranges to the right `package.json` (or `pnpm-workspace.yaml` for catalog entries) and runs your package manager's install. See [keyboard shortcuts](../keyboard-shortcuts/) for everything the picker can do. ## Requirements - Node.js `>=22.19.0` - One of: npm, yarn, pnpm or bun (detected automatically from the lockfile) ## Monorepos There is nothing to configure. inup discovers every workspace in one pass and shows all upgradable dependencies in a single picker. Dependencies declared as `catalog:` / `catalog:` are resolved from `pnpm-workspace.yaml` and upgraded right there — comments and formatting preserved. ## Private registries inup honors your `.npmrc` (project, user and global): scoped registries (`@scope:registry=…`) and credentials (`_authToken`, `username`/`_password`, `${ENV_VAR}` expansion) work exactly like npm's own resolution. ## Next steps - [CLI reference](../cli/) — every flag, exit codes, headless behavior - [Configuration](../configuration/) — `.inuprc` for ignores and scan rules - [CI & scripting](../ci/) — gate builds, JSON reports, auto-apply - [GitHub Action](../github-action/) — one rolling upgrade PR on a schedule --- ## CLI reference inup is a single command. Without flags it opens the interactive picker; with `--json`, `--check` or `--apply` (or when stdout isn't a TTY) it runs headless. ```bash inup [options] ``` ## Options | Flag | Description | | --- | --- | | `-d, --dir ` | Run in a specific directory (default: current directory) | | `-e, --exclude ` | Exclude paths matching regex patterns, comma-separated | | `-i, --ignore ` | Ignore packages — comma-separated, glob supported (`@babel/*`) | | `--max-depth ` | Maximum directory depth for `package.json` discovery (default: 10) | | `--package-manager ` | Force the package manager: `npm`, `yarn`, `pnpm` or `bun` | | `--json` | Print a machine-readable JSON report and exit (read-only) | | `-c, --check` | Exit non-zero if updates exist, without writing (for CI; read-only) | | `--apply` | Non-interactively write upgrades and install (for CI/automation) | | `--target ` | With `--apply`: `minor` (default, in-range), `patch`, or `latest` | | `--save-exact` | Write exact versions instead of preserving the range prefix (`^`/`~`) | | `--no-color` | Disable colored output (also respects `NO_COLOR` / `FORCE_COLOR`) | | `--debug` | Write a verbose debug log to `/tmp/inup-debug-YYYY-MM-DD.log` | ## Exit codes | Code | Meaning | | --- | --- | | `0` | Everything up to date | | `1` | Updates exist (with `--check`) | | `2` | Error | ## Headless by default in pipelines When stdout isn't a TTY or `$CI` is set, inup never opens the interactive UI — it prints a report instead, so it can't hang a pipeline. `inup | cat` gives a plain line-based report; `--json` gives the structured document. See [CI & scripting](../ci/) for the JSON schema and `--apply` semantics, and [Configuration](../configuration/) for the `.inuprc` file that all modes honor. --- ## Configuration inup needs no configuration to run. When you want persistent project rules, add a JSON config file. inup looks for the first of these, searching from the working directory upward to the filesystem root: 1. `.inuprc` 2. `.inuprc.json` 3. `inup.config.json` Every mode honors it — the interactive picker, `--json`, `--check` and `--apply`. A package the config excludes is never reported or written. ## Example ```json { "ignore": ["@babel/*", "eslint-*", "typescript"], "exclude": ["fixtures", "examples/.*"], "scanDirs": ["lib"], "showPeerDependencyVulnerabilities": false, "showOptionalDependencyVulnerabilities": false } ``` ## Fields ### `ignore` Packages to skip during upgrade checks. Supports exact names and glob patterns: - `"lodash"` — exact match - `"@babel/*"` — every package in a scope - `"eslint-*"` — wildcard, `*` matches any sequence, `?` matches one character The same syntax works ad hoc via `--ignore` on the command line. ### `exclude` Directory patterns to skip while discovering `package.json` files, as regular expressions. Equivalent to `--exclude`. ### `scanDirs` Directory names to scan even though they are on the default skip list (`node_modules`, `dist`, `build`, `coverage`, `out`, `lib`, `es`, `esm`, `cjs`). Use this when a real package lives under e.g. `lib/`. ### `showPeerDependencyVulnerabilities` Show vulnerability badges for `peerDependencies` in the package list. Defaults to `false` so peer-dependency risk stays hidden unless you opt in. ### `showOptionalDependencyVulnerabilities` Show vulnerability badges for `optionalDependencies`. Defaults to `false`. ## Environment variables - `CI` — when set, inup runs headless (report mode) instead of opening the UI - `NO_COLOR` / `FORCE_COLOR` — standard color controls, same as `--no-color` --- ## CI & scripting Run inup non-interactively to gate builds on outdated or vulnerable dependencies, generate a machine-readable dependency report, or auto-apply safe upgrades. inup runs headless automatically when stdout isn't a TTY or `$CI` is set, so it never hangs in a pipeline waiting on the interactive UI. Both `--json` and `--check` are **read-only** — they report, they never edit `package.json` or install. ```bash inup --check # exit 1 if anything is outdated → fails the build inup --json | jq # structured drift report for dashboards/bots inup | cat # plain line-based report when piped to a log inup --apply # write safe in-range bumps + install (non-interactive) inup --apply --target latest # include major bumps; --json to also emit the report ``` ## `--apply` Unlike `--json` and `--check`, **`--apply` writes**: it bumps `package.json` and runs your package manager's install to update the lockfile. - `--target minor` (default) applies only **in-range** updates and leaves majors for you to review - `--target patch` stays within the current `major.minor` line - `--target latest` includes majors It honors [`.inuprc`](../configuration/) exactly as the report does — a package the config excludes is never written. With `--apply --json`, the install output goes to stderr so stdout stays pure JSON. ## pnpm catalogs work in every mode Dependencies declared as `catalog:` / `catalog:` are resolved from `pnpm-workspace.yaml`; `--apply` writes the new range back into that file (comments and formatting preserved), and in `--json` output such entries carry a `"catalog"` field with their `packageJsonPath` pointing at `pnpm-workspace.yaml`. ## The JSON report Each reported package carries its health signals: - `deprecated` — the npm deprecation message, if any - `enginesNode` — the package's declared `engines.node` - `vulnerability` — known advisories on the currently-installed version, from one bulk `npm audit`-style request Every advisory is **cross-referenced against the upgrade targets**, so you know whether the upgrade actually fixes it: - `vulnerability.advisories[].fixedByRange` / `fixedByLatest` — does the in-range / latest target escape this advisory's affected range? - `vulnerability.fixedByRange` / `fixedByLatest` — does the target clear **every** advisory? The summary includes a `vulnerable` count, and the payload carries a `schemaVersion` so scripts and agents can pin to a known shape. ## Output hygiene With `--json`, stdout carries **only** the JSON document; all progress and warnings go to stderr. | Exit code | Meaning | | --- | --- | | `0` | Up to date | | `1` | Updates exist (`--check`) | | `2` | Error | For scheduled upgrades with a rolling pull request, use the [GitHub Action](../github-action/). --- ## JSON report schema `inup --json` prints one JSON document to stdout and exits — read-only, never writing `package.json` or the lockfile. This page is the field-by-field reference for that document; for when to use it and the surrounding `--check` / `--apply` flags, see [CI & scripting](../ci/). The payload carries a `schemaVersion` so scripts and agents can pin to a known shape. Only the fields documented here are part of the contract; treat anything else as internal. ## Top level ```ts { schemaVersion: number // bumped only on a breaking shape change (currently 1) summary: { total: number // packages scanned outdated: number // packages with an available update major: number // of the outdated, how many offer a major bump vulnerable: number // of the outdated, how many have ≥1 known advisory on the installed version } outdated: PackageEntry[] } ``` ## `PackageEntry` One entry per outdated package. | Field | Type | Notes | | --- | --- | --- | | `name` | `string` | Package name. | | `current` | `string` | Raw specifier from `package.json`, including the `^`/`~` prefix. | | `range` | `string` | Newest version that still satisfies `current`'s range (the in-range target). | | `latest` | `string` | Absolute latest published version. | | `type` | `string` | One of `dependencies`, `devDependencies`, `optionalDependencies`, `peerDependencies`. | | `packageJsonPath` | `string` | File the range is declared in — `pnpm-workspace.yaml` for catalog entries. | | `catalog` | `string?` | Present only for pnpm catalog entries: the catalog name (`default` or a named catalog). | | `hasMajorUpdate` | `boolean` | `true` when `latest` is a major bump beyond `range`. | | `deprecated` | `string?` | npm deprecation message for `latest`, if the package is deprecated. | | `enginesNode` | `string?` | Declared `engines.node` range for `latest`, if any. | | `vulnerability` | `Vulnerability?` | Present only when the installed version has ≥1 known advisory. | ## `Vulnerability` Advisories affecting the **currently-installed** version, each cross-referenced against the upgrade targets so you know whether the bump actually clears it. | Field | Type | Notes | | --- | --- | --- | | `count` | `number` | Number of advisories on the installed version. | | `highestSeverity` | `string` | Highest of `info`, `low`, `moderate`, `high`, `critical`. | | `fixedByRange` | `boolean` | `true` when the in-range target (`range`) clears **every** advisory. | | `fixedByLatest` | `boolean` | `true` when upgrading to `latest` clears **every** advisory. | | `advisories` | `Advisory[]` | The individual advisories. | ### `Advisory` | Field | Type | Notes | | --- | --- | --- | | `id` | `number` | npm advisory id. | | `title` | `string` | Advisory title. | | `severity` | `string` | `info` \| `low` \| `moderate` \| `high` \| `critical`. | | `url` | `string` | Link to the advisory. | | `vulnerableVersions` | `string` | The affected semver range, verbatim from npm. | | `fixedByRange` | `boolean` | The in-range target is no longer in the affected range. | | `fixedByLatest` | `boolean` | The latest target is no longer in the affected range. | ## Example ```json { "schemaVersion": 1, "summary": { "total": 42, "outdated": 3, "major": 1, "vulnerable": 1 }, "outdated": [ { "name": "undici", "current": "^7.10.0", "range": "7.11.0", "latest": "7.11.0", "type": "dependencies", "packageJsonPath": "package.json", "hasMajorUpdate": false, "vulnerability": { "count": 1, "highestSeverity": "moderate", "fixedByRange": true, "fixedByLatest": true, "advisories": [ { "id": 1234567, "title": "Denial of Service in undici", "severity": "moderate", "url": "https://github.com/advisories/GHSA-xxxx-xxxx-xxxx", "vulnerableVersions": "<7.10.1", "fixedByRange": true, "fixedByLatest": true } ] } }, { "name": "react", "current": "^18.2.0", "range": "18.3.1", "latest": "19.2.0", "type": "dependencies", "packageJsonPath": "pnpm-workspace.yaml", "catalog": "default", "hasMajorUpdate": true } ] } ``` ## Output hygiene & exit codes With `--json`, stdout carries **only** the JSON document — all progress, warnings, and (under `--apply`) install output go to stderr, so `inup --json | jq` is always safe. | Exit code | Meaning | | --- | --- | | `0` | Up to date | | `1` | Updates exist (with `--check`) | | `2` | Error | --- ## GitHub Action Automate dependency upgrades for your GitHub repo: run inup on a schedule and get **one rolling pull request** with safe upgrades applied. Re-runs update the same PR instead of opening new ones. Add this workflow to **your** repo: ```yaml # .github/workflows/inup.yml name: inup on: schedule: - cron: '17 5 * * *' # daily around 05:17 UTC workflow_dispatch: {} permissions: contents: write pull-requests: write jobs: upgrade: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: donfear/inup@v1 with: target: minor # minor (default) | patch | latest ``` That's it. Also enable Settings → Actions → General → **Workflow permissions** → **"Allow GitHub Actions to create and approve pull requests"** so the action can open the PR. > Scheduled workflows are not real-time timers: GitHub documents that `schedule` runs can be delayed during busy periods, and the start of each hour is especially busy. Schedule earlier than you need and avoid `:00` cron minutes. ## What the action creates When inup finds applicable upgrades, the workflow commits the changed manifest/lockfile and opens or updates one pull request. The PR body contains a summary line, the list of upgrades applied in the PR, and a full table of available updates — including whether a major is available and whether an upgrade fixes a known vulnerability. Catalog-sourced upgrades (pnpm `catalog:` deps) are applied to `pnpm-workspace.yaml` and marked `catalog:` in the PR body so reviewers know which file the diff touches. ## Commit as you, not the bot By default the upgrade commit is authored by `github-actions[bot]`. To attribute it to **you**, store your name/email as repo secrets and pass `committer`/`author`: ```yaml - uses: donfear/inup@v1 with: committer: ${{ secrets.GH_USERNAME }} <${{ secrets.GH_EMAIL }}> author: ${{ secrets.GH_USERNAME }} <${{ secrets.GH_EMAIL }}> ``` ## Inputs | Input | Default | Description | | --- | --- | --- | | `target` | `minor` | How far to bump: `minor` (in-range), `patch`, or `latest` (includes majors) | | `directory` | `.` | Directory to run in | | `package-manager` | _(auto)_ | Force `npm`/`yarn`/`pnpm`/`bun`; empty auto-detects from the lockfile | | `node-version` | `22` | Node.js version for the run (minimum `22.19`) | | `inup-version` | `latest` | inup version to run (pin for reproducible runs) | | `pr-branch` | `inup/dependency-upgrades` | Branch for the rolling PR | | `pr-title` | `chore(deps): dependency upgrades` | PR title | | `commit-message` | `chore(deps): upgrade dependencies via inup` | Commit message | | `base` | _(default branch)_ | Base branch the PR targets | | `labels` | `dependencies` | Labels to apply to the PR | | `token` | `${{ github.token }}` | Token to push + open the PR. Pass a PAT to also trigger CI on the PR | | `committer` | `github-actions[bot]` | Commit committer, as `Name ` | | `author` | _(triggering user)_ | Commit author, as `Name ` | ## Outputs `outdated`, `vulnerable`, `pull-request-number`. `@v1` floats to the latest `1.x` release; pin to a specific version or a SHA for reproducible runs. The action honors your [`.inuprc`](../configuration/) (`ignore`, `exclude`, `scanDirs`). --- ## FAQ & troubleshooting ## Does inup edit my files or run install? It depends on the mode: - **Interactive** (the default): nothing is written until you confirm. When you press `Enter` on your selection, inup bumps `package.json` (and `pnpm-workspace.yaml` for catalog entries) and runs your package manager's install to update the lockfile. - **`--json` and `--check`**: **read-only** — they report and exit, never touching `package.json` or the lockfile. - **`--apply`**: the only non-interactive write path — it applies the bumps and runs install. See [CI & scripting](../ci/). By default the range prefix is preserved (`^`/`~`); pass [`--save-exact`](../cli/) to write exact versions instead. ## Why is a package (or directory) being skipped? A few things cause inup to skip something: - **Ignored packages** — anything matching `ignore` in [`.inuprc`](../configuration/) or `-i, --ignore` (glob supported, e.g. `@babel/*`). - **Excluded directories** — anything matching `exclude` in `.inuprc` or `-e, --exclude` (regex). - **Default skip list** — inup does not descend into `node_modules`, `dist`, `build`, `coverage`, `out`, `lib`, `es`, `esm`, or `cjs`. If a real package lives under one of those (e.g. `lib/`), add it to `scanDirs` in `.inuprc`. - **Scan depth** — package discovery stops at `--max-depth` (default `10`) directories deep. ## How does inup know which package manager to use? It auto-detects from your lockfile (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `bun.lockb`/`bun.lock`). Force it with `--package-manager npm|yarn|pnpm|bun` when a repo has more than one lockfile. ## Do private registries and `.npmrc` work? Yes. inup resolves registries and credentials with npm's own model, reading your project, user, and global `.npmrc`: scoped registries (`@scope:registry=…`) and credentials (`_authToken`, `username`/`_password`, legacy `_auth`), with `${ENV_VAR}` values expanded. A token is only ever sent to the registry its config binds it to. No extra setup — if npm can install it, inup can read it. ## It printed JSON / a plain list instead of opening the UI That's the headless fallback, and it's intentional. inup runs headless — never opening the interactive UI — when **any** of these is true: - stdout isn't a TTY (e.g. piped: `inup | cat`), - the `CI` environment variable is set, - you passed `--json`, `--check`, or `--apply`. This keeps it from hanging a pipeline. To force the interactive picker, run it in a real terminal without those flags. ## How do I turn off colors? Pass `--no-color`, or set the standard `NO_COLOR` environment variable. `FORCE_COLOR` is also respected. Useful for log files and CI output. ## It warned about a dirty working tree In interactive mode, inup warns before changing anything if your git working tree has uncommitted changes, so an upgrade diff stays easy to review and revert. Commit or stash first, or continue past the prompt. Headless mode skips the prompt (it's read-only unless `--apply`). ## Does inup send any telemetry? No. There is no tracking, no telemetry, and no data collection. Package metadata comes straight from the npm registry, download counts from the npm downloads API, and changelogs/release notes from GitHub — nothing else leaves your machine. ## What Node version do I need? Node `22.19` or newer (`engines.node`). `npx inup` uses your local Node; the [GitHub Action](../github-action/) defaults to Node 22. ## Still stuck? Run with `--debug` to write a verbose log (`/tmp/inup-debug-YYYY-MM-DD.log`), then [open an issue](https://github.com/donfear/inup/issues) with it attached. --- ## Keyboard shortcuts | Key | Action | | --- | --- | | ↑ / k | Move up | | ↓ / j | Move down | | g | Jump to the first package | | G | Jump to the last package | | ← | Cycle selection left (none → range → latest) | | → | Cycle selection right (none → range → latest) | | Space | Toggle the current package on/off | | m | Select all minor/patch updates | | l | Select all latest updates (including major) | | u | Unselect all packages | | Enter | Confirm selection and upgrade | | / | Search packages by name | | d | Toggle devDependencies | | p | Toggle peerDependencies | | o | Toggle optionalDependencies | | s | Run the vulnerability audit | | v | Show only vulnerable packages | | Esc | Clear the active search filter | | i | View package details and changelog | | t | Change the color theme | | ? | Show this help | | ! | Show the performance/debug panel |