# Package Manager

aux4 includes a built-in package manager (`pkger`) for installing, managing, and publishing packages from the [aux4 hub](https://hub.aux4.io). All package manager commands are accessed through `aux4 aux4 pkger`.

## Requirements

The package manager is included with aux4. If you need to install or update it separately, install the [aux4/pkger](https://hub.aux4.io/r/public/packages/aux4/pkger) package:

```bash
> aux4 aux4 pkger install aux4/pkger
```

## Installation Directory

Packages are installed to `~/.aux4.config/packages/`. The package manager maintains a registry file at `~/.aux4.config/packages/all.json` that tracks all installed packages, dependencies, and system dependencies.

After installation, packages are automatically available via the `aux4` command — their commands are merged into the global environment via `~/.aux4.config/global.aux4`.

## Install

Install a package from the aux4 hub:

```bash
> aux4 aux4 pkger install aux4/test
```

Install several packages at once:

```bash
> aux4 aux4 pkger install aux4/test aux4/config aux4/adapter
```

Install a specific version:

```bash
> aux4 aux4 pkger install aux4/test@0.2.18
```

Install a semver range — the highest published version that satisfies it is installed. Quote the reference: `^`, `>`, `<`, `*` and `|` are shell metacharacters.

```bash
> aux4 aux4 pkger install 'aux4/config@^0.2.0'
```

See [Version Ranges](#version-ranges) for the full grammar and how a range behaves when the package is already installed.

Install from a local file:

```bash
> aux4 aux4 pkger install --fromFile ./my-package_1.0.0.zip
```

Install from a private repository:

```bash
> aux4 aux4 pkger install --repository my-org aux4/internal-tool
```

Force a reinstall or downgrade with `--force`. This overrides the currently installed version regardless of dependencies, so you can reinstall a package or roll back to an older version without first uninstalling everything that depends on it:

```bash
> aux4 aux4 pkger install aux4/test@0.2.18 --force
```

The installer automatically:
- Resolves and installs dependencies
- Detects your platform (darwin/linux/windows, amd64/arm64) for multi-platform packages
- Installs system dependencies (e.g., Node.js via brew/apt/pkgx) if needed
- Signs executables on macOS with `codesign`

## Version Ranges

A package reference is `scope/name`, optionally followed by `@` and a version. That version can be an exact version, `latest`, or an **npm-style semver range**. Ranges work in **both** places a reference appears — on the install command line and in a package's `dependencies` — and always resolve to the **highest published version that satisfies the range**.

| Form | Example | Means |
|------|---------|-------|
| Caret | `^1.2.3` | `>=1.2.3 <2.0.0` — the left-most non-zero segment is held, so `^0.2.3` is `>=0.2.3 <0.3.0` and `^0.0.3` is `>=0.0.3 <0.0.4` |
| Tilde | `~1.2.3` | `>=1.2.3 <1.3.0`; `~1.2` is `>=1.2.0 <1.3.0`; `~1` is `>=1.0.0 <2.0.0` |
| Comparators | `>=1.2.0` `>1.2.0` `<=2.0.0` `<2.0.0` `=1.2.3` `!=1.2.3` | A partial version is allowed after a comparator: `>=1.2` is `>=1.2.0`, `>1.2` is `>=1.3.0`, `<=1.2` is `<1.3.0` |
| x-range | `1.x` `1.2.x` | `1.x` is `>=1.0.0 <2.0.0`; `1.2.x` is `>=1.2.0 <1.3.0` |
| Any | `*` | Any published version |
| Hyphen | `1.2.3 - 2.0.0` | `>=1.2.3 <=2.0.0`; an incomplete upper bound covers the whole missing segment, so `1.2.3 - 2.3` is `<2.4.0` |
| AND | `>=0.1.0 <0.2.0` | A space between terms means both must hold |
| OR | `^0.1.0 \|\| ^0.2.0` | `\|\|` separates alternatives |

Prereleases are excluded from a range unless the range itself mentions one, so a published `2.1.0-beta.1` is never picked up by `^2.0.0`.

**A bare version is never a range — including a partial one.** A reference is only treated as a range when it fails to parse as a complete semver version *and* carries range syntax (a leading `^ ~ > < =`, a `||`, a whitespace-delimited `-`, or an `x`/`X`/`*` segment). So `1.2.3` is exact, the prerelease `1.0.0-local` is exact, `latest` still means latest, and a bare partial such as `0.2` is **exact** too — it asks for the literal version `0.2`:

```bash
> aux4 aux4 pkger install aux4/config@0.2
```

```text
Package aux4/config@0.2 not found
```

This differs from npm, where `0.2` means `0.2.x`. aux4 keeps it exact on purpose: every version already written in an existing `.aux4` keeps meaning exactly what it meant before ranges existed. Write `0.2.x` (or `~0.2`) when you want the range.

Exact references and `latest` keep their fast path — they resolve without asking the hub for the version list, so only a real range costs an extra request.

## Dependency Versions and Automatic Upgrades

Only **one** version of a package is installed at a time, so a dependency declared with a version has to be reconciled against what is already installed. What a reference means for that decision:

| Reference | Constraint |
|-----------|-----------|
| `aux4/config` | **Unconstrained** — accepts any installed version and never blocks an upgrade |
| `aux4/config@latest` | Unconstrained, same as above |
| `aux4/config@0.2.10` | **Exact pin** — a constraint of exactly `0.2.10`, so it **caps the whole tree**: while that package is installed, nothing can move the dependency off `0.2.10` |
| `aux4/config@^0.2.0` | Range — any version it matches is acceptable |

When a newly installed package requires a version the installed dependency does not satisfy, the dependency is upgraded **only with unanimous consent**: to the highest published version that satisfies the new requirer *and* every package that already depends on it. The upgrade goes through the normal install path and is never silent:

```bash
> aux4 aux4 pkger install tester/upgrade-b
```

```text
Upgrading aux4/config 0.1.1 -> 0.2.10 (required by tester/upgrade-b@^0.2.0)
Unzipping package aux4 config 0.2.10
Loading package aux4 config 0.2.10
Unzipping package tester upgrade-b 1.0.0
Loading package tester upgrade-b 1.0.0
Installed packages:
 ✓ aux4/config 0.2.10
 ✓ tester/upgrade-b 1.0.0
```

If no published version satisfies everyone, the install fails and names the package that blocks it — usually an exact pin held by another dependent — along with the versions that are published:

```text
tester/upgrade-b requires aux4/config@^0.2.0, but aux4/config 0.1.1 is installed.
Cannot upgrade: tester/upgrade-a requires aux4/config@0.1.1.
Published versions: 0.2.10, 0.2.0, 0.1.1, 0.1.0
```

A dependency is **never downgraded automatically**. If the only version everyone agrees on is older than the installed one, the install fails rather than breaking the dependent that works today:

```text
tester/old-pin requires aux4/config@~0.1.0, but aux4/config 0.2.10 is installed.
Cannot upgrade: the highest version satisfying every dependent is 0.1.1, which is older than the installed 0.2.10. pkger never downgrades a dependency automatically.
```

`--force` bypasses range enforcement entirely: the package is installed and the dependency is left at whatever version is already there.

Two exemptions keep local development working: an installed **prerelease** — a local build such as `0.2.10-local` — is checked as its release version, so a range like `^0.2.0` accepts it and your local build is never silently upgraded over; and an installed version that is **not semver at all** is not comparable, so enforcement is skipped for it rather than guessed.

## Uninstall

Remove an installed package:

```bash
> aux4 aux4 pkger uninstall aux4/test
```

Remove several packages at once:

```bash
> aux4 aux4 pkger uninstall aux4/test aux4/config
```

If other packages depend on the package you're trying to uninstall, the command will fail with an error showing which packages still need it.

Use `--force` to remove it anyway. Force uninstall cascades: the package and every package that depends on it — directly or transitively — are removed together, so the package database is never left referencing a package that is gone:

```bash
> aux4 aux4 pkger uninstall aux4/test --force
```

## List

List all installed packages:

```bash
> aux4 aux4 pkger list
```

```text
 ✓ aux4/aux4 5.1.20
 ✓ aux4/test 0.2.18
 ✓ aux4/config 1.0.5
```

Show dependencies:

```bash
> aux4 aux4 pkger list --showDependencies true
```

```text
 ✓ aux4/test 0.2.18
   ↪ aux4/config 1.0.5
```

Filter packages by name:

```bash
> aux4 aux4 pkger list --filter config
```

## Search

Search for packages on the aux4 hub:

```bash
> aux4 aux4 pkger search database
```

Search in a specific repository:

```bash
> aux4 aux4 pkger search --repository my-org internal
```

## Discover Installed Commands

`aux4 aux4 pkger search` looks at the remote hub for packages you could install. `find` does the opposite: it searches what's **already installed** on your machine and resolves a query straight down to the command you need.

Search by what you want to do, not by package name — `list --filter` returns package names, and a package name is not a command.

```bash
> aux4 aux4 pkger find "get a configuration value"
```

```text
aux4 config set  (aux4/config)
If file is not provided it will look for a file named config.yaml, config.yml, or config.json in the current directory.

aux4 config get  (aux4/config)
If file is not provided it will look for a file named config.yaml, config.yml, or config.json in the current directory.
```

The first line of each result is the exact command to run, with its package beside it; the line below is a short description from its documentation.

Limit how many results come back:

```bash
> aux4 aux4 pkger find "send an email" --limit 3
```

It searches a local index built over every installed package's `README.md` and command man pages. You should never need to manage it: it is built the first time you search, and rebuilt automatically whenever your set of installed packages has changed. To force a rebuild:

```bash
> aux4 aux4 pkger reindex
```

> This used to be a separate `aux4/pkger-finder` package. It is now part of `aux4/pkger`, so no install step is needed. If you still have `aux4/pkger-finder` installed, you can uninstall it.

## Build

Build a package into a distributable `.zip` file:

```bash
> aux4 aux4 pkger build package
```

This creates a `.zip` file in the current directory, named `{scope}_{name}_{version}.zip`.

Build only for the current platform (useful for testing multi-platform packages locally):

```bash
> aux4 aux4 pkger build package --currentPlatform true
```

The build process:
- Validates that `.aux4`, `LICENSE`, and `README.md` exist
- Detects man pages in `man/` and test files in `test/` and sets `hasMan`/`hasExample` flags
- For multi-platform packages (with `dist/` directory), creates platform-specific zips plus a meta-package
- Excludes `.DS_Store`, `.git`, `.gitignore`, `.npmignore`, `.idea`, `.vscode`

## Publish

Publish a package to the aux4 hub (requires authentication):

```bash
> aux4 aux4 pkger publish aux4_mypackage_1.0.0.zip
```

Publish to a specific repository:

```bash
> aux4 aux4 pkger publish --repository my-org aux4_mypackage_1.0.0.zip
```

## Man

Display documentation for an installed package:

```bash
> aux4 aux4 pkger man aux4/test
```

Open in browser:

```bash
> aux4 aux4 pkger man aux4/test --browser true
```

Output raw markdown:

```bash
> aux4 aux4 pkger man aux4/test --markdown true
```

## Authentication

### Login

Authenticate with the aux4 hub. This starts a device authorization flow — you'll be given a URL and code to enter in your browser:

```bash
> aux4 aux4 login
```

```text
Go to https://sso.aux4.io/device_code/verify and enter the code: ABCD-1234
```

After authorization, your session is saved to `~/.aux4.config/credentials`.

For CI/CD environments, don't log in interactively. Create a durable **publishing token** (a client id/secret pair) once on [user.aux4.io](https://user.aux4.io) → **Scopes** → your scope → **Tokens** → **Create token** (**Publish to Hub**), store the **Client ID** and **Client Secret** as secrets, then mint a fresh, short-lived hub token per run and expose it as `AUX4_ACCESS_TOKEN`:

```bash
export AUX4_ACCESS_TOKEN=$(curl -sf -u "$AUX4_CLIENT_ID:$AUX4_CLIENT_SECRET" \
  -d grant_type=client_credentials https://sso.aux4.io/token | jq -r '.access_token')
```

A pasted access token expires and breaks CI; the client id/secret is durable, and you mint a new token each run. If you publish from GitHub Actions, [`aux4/action@v1`](/github-actions) does this for you — see [GitHub Actions](/github-actions).

### Logout

Revoke your session and remove credentials:

```bash
> aux4 aux4 logout
```

## System Dependencies

Packages can declare system-level dependencies that are automatically installed when the package is installed. System dependencies are defined in the `.aux4` metadata:

```json
{
  "system": [
    ["node --version", "brew:node", "apt:nodejs", "pkgx:node"]
  ]
}
```

Each entry is an array where:
- The first element with prefix `test:` (or without a prefix) is a command to check if the dependency is already installed
- Remaining elements specify the package name for each supported package manager

Supported system package managers:
- `brew` — Homebrew (macOS/Linux)
- `apt` — APT (Debian/Ubuntu)
- `dnf` — DNF (Fedora/RHEL)
- `yum` — YUM (CentOS/RHEL)
- `apk` — APK (Alpine Linux)
- `npm` — Node.js packages
- `pkgx` — pkgx universal package manager

## Package Metadata

The `.aux4` file in a package root contains metadata about the package:

```json
{
  "scope": "my-org",
  "name": "my-package",
  "version": "1.0.0",
  "description": "A useful tool",
  "license": "MIT",
  "git": "https://github.com/my-org/my-package",
  "tags": ["utility", "automation"],
  "dependencies": ["aux4/config@1.0.0"],
  "system": [],
  "platforms": ["darwin", "linux"],
  "profiles": [...]
}
```

Required fields: `scope`, `name`, `version`, `license`

Optional fields: `description`, `git`, `tags`, `dependencies`, `system`, `platforms`, `distribution`

---

## See Also

- [Variables](/variables)
- [Executors](/executors)
- [Functions](/functions)
- [Config](/config)
- [Commands](/commands)
- [Hooks](/hooks)
- [Render](/render)
- [Testing](/testing)
- [Packaging](/packaging)
- [Docker](/docker)
