# Packages
`packages:` declares **package-manager** dependencies. Distinct from `dependencies:`, which names other jan scripts.
Identical declarations share a cached environment under `~/.cache/jan/packages/<manager>/<hash>/` (or `$JAN_CACHE_DIR/…`). Successful runs update `.jan-used` so `jan packages prune` can keep hot caches.
Package dependencies must pin an **exact** version so the env hash identifies a single resolution. Warm hits skip spawning the manager `--version`; the tool is only required when an env must be built.
Inspect caches with [`jan packages`](../cli/packages.md).
## uv (Python)
Inline pins need `==` or `===`. Requirements files are checked line by line (no `-r` / `-e`). `project:` requires `pyproject.toml` plus `uv.lock`. Locked projects install from the exported lock plus `uv pip install --no-deps <project>`.
```yaml
# Inline pins
analyze:
packages:
uv: [rich==13.9.4, pathspec==0.12.1]
commands:
run:
exec:
python: |
import rich
print("ok")
# Requirements file under the jan use root
ingest:
packages:
uv:
requirements: deps/ingest.txt
exec:
argv: ["python3", "tools/ingest.py"]
# uv project directory
train:
packages:
uv:
project: deps/ml
python: ">=3.11"
exec:
python: -m train
```
Map form must set exactly one of `packages` (alias `deps`), `project`, or `requirements`. Optional `python:` is a minimum like `3.11` or `>=3.11`, checked against the host/venv toolchain and included in the env hash.
On run, jan requires `uv` on PATH (cold build), ensures the hashed venv, prepends its `bin/` to the child PATH, and resolves `python` / `python3` from that venv.
Worked example: [`examples/packages.spec.yaml`](../workflows/examples.md) — `jan --cwd examples summarize-sales run`.
## pnpm (Node)
Inline entries need a full `name@major.minor.patch` (no ranges, dist-tags, aliases, or git/file specs). `project:` requires `package.json` + `pnpm-lock.yaml`, installed with `--frozen-lockfile`. Envs are installed in the cache (never in your tree).
```yaml
pin-report:
packages:
pnpm:
- lodash@4.17.21
- cowsay@1.6.0
# or map form:
# packages: [lodash@4.17.21]
# node: ">=18"
commands:
run:
exec:
node: |
console.log(require("lodash").VERSION)
cow:
exec:
argv: ["cowsay", "pinned deps only"]
```
On run jan prepends `node_modules/.bin` to PATH, sets `NODE_PATH`, and resolves argv[0] from `.bin`.
## gradle (JVM / Kotlin)
Inline entries are `group:artifact:version` with an exact version (no `+`, Ivy ranges, or `latest.release`). `project:` needs `build.gradle` or `build.gradle.kts` plus `gradle.lockfile`.
```yaml
kotlin-greet:
packages:
gradle:
- org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.6
# java: 21 # alias: jdk
commands:
run:
exec:
kotlin: packages/kotlin-greet.kts
```
Gradle resolves jars into `lib/` (Java Library plugin / `runtimeClasspath`). jan injects `CLASSPATH` and automatic `-cp` for `java` / `kotlin` / `kotlinc` argv. Cold resolves stream Gradle's console output and print progress on stderr.
## Runtime minima
| uv | `python` | `3.11`, `>=3.11` |
| pnpm | `node` | `18`, `>=18.0.0` |
| gradle | `java` (alias `jdk`) | `21`, `>=21` |
Constraints are checked on ensure/run and included in the env hash.
## Chain merge
Deeper nodes **replace** a given manager's declaration (no list merge). A `help` leaf under a node that declares packages still inherits that declaration for `jan packages` listing.