aux4/pkger

The official aux4 package manager.

aux4/pkger is the core package management system for the aux4 ecosystem. It enables you to discover, install, manage, and publish aux4 packages from the aux4 hub. Whether you're using existing packages to extend your CLI capabilities or creating and sharing your own packages, aux4/pkger provides all the tools you need.

The package manager also handles system-level dependencies, automatically installing required tools from package managers like brew, apt, pkgx, or npm when packages need them.

Installation

aux4 aux4 pkger install aux4/pkger

Quick Start

List all installed packages:

aux4 aux4 pkger list

Managing Packages

Installing Packages

Install a package from the aux4 hub:

aux4 aux4 pkger install scope/package-name

Install several packages at once:

aux4 aux4 pkger install aux4/2table aux4/adapter aux4/config

Install from a specific repository:

aux4 aux4 pkger install scope/package-name --repository private

Install from a local file:

aux4 aux4 pkger install --fromFile ./my-package.tar.gz

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

aux4 aux4 pkger install scope/package-name@1.2.0 --force

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:

aux4 aux4 pkger install 'aux4/config@^0.2.0'

Quote the reference in your shell: ^, >, <, * and | are shell metacharacters.

| Form | Example | Resolves against | |------|---------|------------------| | Caret | ^1.2.3 | >=1.2.3 <2.0.0 — the left-most non-zero segment is held: ^0.2.3 is >=0.2.3 <0.3.0, ^0.0.3 is >=0.0.3 <0.0.4, ^0 is >=0.0.0 <1.0.0 | | 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 after a comparator is allowed: >=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 | 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 unless a range names a prerelease of that same version, so a published 2.1.0-beta.1 is never picked up by ^2.0.0 — only by something that asks for it, such as >=2.1.0-0. The - of a hyphen range is a separator, not a prerelease marker: 2.0.0 - 2.1.0 resolves to the highest released version in that window and skips 2.1.0-beta.1.

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 2.0 also stays exact — it asks for the literal version 2.0:

aux4 aux4 pkger install aux4/config@0.2
Package aux4/config@0.2 not found

Note: this differs from npm, where 2.0 means 2.0.x. pkger 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 also 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 there. What a reference means for that decision:

| Reference in dependencies | 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 — equivalent to 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, pkger upgrades it only with unanimous consent: it takes 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:

aux4 aux4 pkger install tester/upgrade-b
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:

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

pkger never downgrades a dependency 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:

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.

aux4 aux4 pkger install tester/upgrade-b --force

Two deliberate exemptions keep local development working:

  • An installed prerelease — a local build such as 0.2.10-local from aux4 aux4 releaser install —

is checked as its release version (0.2.10), so a range like ^0.2.0 accepts it and your local build is never silently upgraded over.

  • An installed version that is not semver at all is not comparable, so range enforcement is

skipped for it rather than guessed.

Listing Installed Packages

View all installed packages:

aux4 aux4 pkger list

Filter packages by name:

aux4 aux4 pkger list --filter aws

Show packages with their dependencies:

aux4 aux4 pkger list --showDependencies

Finding Commands

list and search answer "which packages exist"; find answers "which command do I run". It searches the documentation of everything installed — each package README and every command man page — and returns runnable command lines. On a terminal the results are shown as a readable list:

aux4 aux4 pkger find "get a configuration value"
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.

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.

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. Search by what you want to do rather than by package name — pkger list --filter returns package names, which are not commands.

Limit the number of results:

aux4 aux4 pkger find "send an email" --limit 3

JSON output

find always produces a JSON array. The readable list above is only how a terminal displays it (via the command's render.tty renderer). When the output is piped or captured — for example | cat, or a tool such as the aux4/mcp meta-toolset invoking find as a subprocess — the raw JSON comes through unchanged. There is no flag to choose the format: it is selected automatically from whether stdout is a terminal.

aux4 aux4 pkger find "get a configuration value" --limit 2 | cat
[
  {
    "command": "aux4 config get",
    "package": "aux4/config",
    "scope": "aux4",
    "name": "config",
    "type": "command",
    "description": "If file is not provided it will look for a file named config.yaml, config.yml, or config.json in the current directory.",
    "score": 12.34
  },
  {
    "command": "aux4 config set",
    "package": "aux4/config",
    "scope": "aux4",
    "name": "config",
    "type": "command",
    "description": "If file is not provided it will look for a file named config.yaml, config.yml, or config.json in the current directory.",
    "score": 11.02
  }
]

Each element has command (the indexed invocation; for a package/README result this is its install command), package (<scope>/<name>), scope, name, type (command or readme, so consumers can prefer runnable commands over package hits), description, and score. When nothing matches — including an empty query — the array is [] (exit 0).

The index lives at ~/.aux4.config/.pkg-index/index.json. You should never need to manage it: it is built lazily the first time you search, and rebuilt automatically whenever your set of installed packages has changed, by comparing the index against the package ledger. Installing and uninstalling also rebuild it ahead of time, so the cost rarely lands on a search. To force a rebuild:

aux4 aux4 pkger reindex

Viewing Package Documentation

Display the manual for a package:

aux4 aux4 pkger man aux4/aws

Open documentation in your browser:

aux4 aux4 pkger man aux4/aws --browser

Output raw markdown:

aux4 aux4 pkger man aux4/aws --markdown

Uninstalling Packages

Remove an installed package:

aux4 aux4 pkger uninstall scope/package-name

Remove several packages at once:

aux4 aux4 pkger uninstall aux4/2table aux4/adapter

Force the removal of a package even when other packages still depend on it. --force cascades: the package and every package that depends on it (directly or transitively) are removed together, so the package database is never left with a dangling reference:

aux4 aux4 pkger uninstall scope/package-name --force

Verifying Installed Packages

verify checks installed packages against the hub's authoritative per-file integrity manifest for the exact version that is installed. At publish time the hub records a SHA-256 of every file shipped in a package (the .aux4, the dist/ binaries, and lib/*.mjs), keyed by platform. verify fetches that manifest over HTTPS for the pinned version recorded in the lockfile (all.json) — never latest — re-hashes the files on disk, and compares them to the hub's copy. This detects post-install local tampering by anchoring the check to a trusted remote reference. Packages are fetched and hashed in parallel:

aux4 aux4 pkger verify
Verifying installed packages against the hub...
 ✓ aux4/aux4 5.1.29 (matches hub)
 ✓ aux4/config 1.0.0 (matches hub)
Rebuilding global.aux4 from verified sources...
 ✓ global.aux4 rebuilt
Summary: 2 verified, 0 unverifiable, 0 FAILED

Each package is reported as ✓ OK (matches hub), ! unverifiable (no hub reference for the pinned version — the package/version is not on the hub, or the hub returned no manifest), or x CHANGED (a changed, missing, or extra file, a missing directory, or a version that disagrees with the lockfile). When every package verifies cleanly, verify re-derives global.aux4 from the verified sources; if verification fails, it refuses to rebuild from unverified sources and exits non-zero.

Strict vs --soft: by default verify is strict and fail-closed — a mismatch fails, and an unverifiable package also fails. With --soft, a genuine mismatch still fails (a real tamper is never hidden), but an unverifiable package is tolerated as a warning and its files are compared against the local install-time manifest to report drift (local drift: none or the drifted files):

aux4 aux4 pkger verify --soft

Note: until the hub ships per-file manifests and the existing catalog is backfilled, most packages have no hub manifest yet and are reported as unverifiable. During that window, use --soft to tolerate unverifiable packages while still catching genuine tampering.

Note: on macOS the installer ad-hoc codesigns dist/darwin/** binaries after extraction, which mutates them, so their on-disk hash cannot match the hub manifest. verify does not hard-fail those files; it reconciles them against the local install-time manifest and labels them codesigned locally, hub re-hash n/a. Every other file stays hub-anchored.

Pass a filter to verify only a subset of packages. This is a diagnostic mode: it checks the matching packages but intentionally skips the global.aux4 rebuild, since only part of the tree was verified:

aux4 aux4 pkger verify aux4/config

Building and Publishing Packages

Building a Package

Build a package including specific files:

aux4 aux4 pkger build .aux4 LICENSE README.md

Build a package including all files in the current directory:

aux4 aux4 pkger build .

Specify an output directory:

aux4 aux4 pkger build . --out ./dist

The build command generates a .tar.gz package file ready for publishing.

Native Binaries

A package zip holds files exactly as they are on disk. If the build finds native binaries among them — *.node addons, or macOS (Mach-O) and Linux (ELF) executables and libraries — it prints a warning listing them, because the archive will only work on the platform it was built on:

Warning: found 1 native binary in the package. The archive is platform-specific to the build OS (darwin/arm64) and may not work on other platforms:
  - lib/node_modules/fsevents/fsevents.node (macOS/Mach-O)
Build on the target platform, or exclude optional native dependencies (for npm: npm install --omit=optional).

The build still succeeds. Binaries under dist/<os>/<arch>/ are per-platform by design and are not reported.

Package Structure

Your package must include a .aux4 configuration file:

{
  "scope": "your-scope",
  "name": "package-name",
  "version": "1.0.0",
  "description": "Your package description",
  "license": "MIT",
  "dependencies": [
    "aux4/aux4"
  ],
  "profiles": [
    ...
  ]
}

Include a LICENSE file and a README.md for documentation.

Publishing a Package

First, login to the aux4 hub:

aux4 aux4 login

This starts the interactive device flow: it prints a verification URL and code to open in your browser and stores the resulting session in ~/.aux4.config/credentials (owner-only). You can create an aux4 account if you don't have one.

Headless login (CI / scope tokens)

For CI and other headless environments, log in with a self-service scope token (a client_credentials OIDC client — a client id like st-<uuid> plus a secret) instead of the browser device flow. Provide both a client id and secret and the CLI performs the client_credentials grant and stores a scoped machine session:

aux4 aux4 login --client-id st-1a2b3c4d --client-secret <secret>

The credentials can also be supplied through the environment, which keeps the secret off the command line — the recommended form for CI:

export AUX4_CLIENT_ID=st-1a2b3c4d
export AUX4_CLIENT_SECRET=<secret>
aux4 aux4 login

A scope-token login is authorized only for the token's scopes (for example publishing to a specific scope), not a full user identity. The client_credentials grant issues no refresh token, so the CLI stores the client id and secret and automatically re-mints a fresh access token from them when the current one expires. The secret is stored owner-only (0600) and is never printed or logged. When no client id/secret is provided, login falls back to the interactive device flow.

Then publish your package:

aux4 aux4 pkger publish my-package-1.0.0.tar.gz

Publish to a specific repository:

aux4 aux4 pkger publish my-package-1.0.0.tar.gz --repository private

Logout when finished:

aux4 aux4 logout

System Dependencies

aux4/pkger can automatically manage system-level dependencies for packages. When a package requires external tools, the system installer handles installation automatically.

Declaring System Dependencies

In your .aux4 file, specify system dependencies:

{
  "scope": "your-scope",
  "name": "package-name",
  "version": "1.0.0",
  "system": [
    ["test:aws --version", "pkgx:aws", "brew:awscli"],
    ["test:jq --version", "pkgx:jq", "brew:jq"]
  ],
  "profiles": [
    ...
  ]
}

Each dependency is an array containing:

  • A test command to check if the package is already installed
  • One or more installation options for different package managers

Available System Installers

Users need one of these system installers to install packages with system dependencies:

  • brew - Homebrew for macOS/Linux
  • apt - APT for Debian/Ubuntu
  • pkgx - pkgx universal package manager
  • npm - Node.js packages

Managing System Packages Manually

Install a system package:

aux4 aux4 pkger system brew install jq

Uninstall a system package:

aux4 aux4 pkger system brew uninstall jq

Package Documentation

Command Documentation

Add documentation for specific commands by creating markdown files in a man/ directory:

man/<profile_name>__<command_name>.md

Replace colons in profile names with underscores. For example, documentation for the build command in the aux4:pkger profile would be:

man/aux4_pkger__build.md

Command Examples

Include usage examples by creating test files in a test/ directory:

test/<profile_name>__<command_name>.test.md

License

This package is licensed under a Commercial License.

See LICENSE for details.