jan-cli 0.27.0

YAML-defined CLI trees with progressive help, optional exec aliases, merged extra specs, and SQLite audit logging keyed by git branch
Documentation
# Changelog

## 0.27.0 — 2026-09-13

- Release 0.27.0


## 0.26.0 — 2026-09-13

- Release 0.26.0


## 0.25.0 — 2026-09-13

- Release 0.25.0


## Unreleased

- `jan cron status` prints running/deferred/recent spawn detail (leaf, trigger, exit codes); `--compact` / `--json` flags
- `jan cron watch` polls status; `jan cron wakeups` lists event-addressable leaves
- Optional outer `unifier log` spans on cron spawn (`JAN_CRON_SPAN`)
- `jan cron start` no longer prints `(no managed JAN CRON block…)` on every start; legacy crontab eviction stays silent unless it actually removes a block
- YAML `system:` on command groups tags descendant leaves; `jan systems list|show|status` derives schedule/mail/execution/timeline observability without per-agent code


## 0.24.0 — 2026-09-09

- Release 0.24.0


## 0.23.0 — 2026-09-09

- Release 0.23.0


## 0.22.0 — 2026-09-09

- Release 0.22.0


## 0.21.0 — 2026-09-03

- Release 0.21.0


## 0.20.0 — 2026-09-03

- Release 0.20.0


## 0.19.0 — 2026-08-25

- Release 0.19.0


## 0.18.0 — 2026-08-22

- Release 0.18.0


## 0.17.0 — 2026-08-18

- Release 0.17.0


## 0.16.0 — 2026-08-16

- Release 0.16.0


## 0.15.0 — 2026-08-14

- Release 0.15.0


## 0.14.0 — 2026-08-13

- Release 0.14.0


## Unreleased

### Added

- `jan lab`: isolated preferred tree + cron daemon for trying agent systems
  without changing daily `jan use` or the user systemd unit
  (`JAN_CONFIG_DIR` + `XDG_RUNTIME_DIR`, optional `--with-unifier`).
  See `docs/cli/lab.md`.
- `jan cron start --no-systemd`: background-spawn only; also implied whenever
  `$JAN_CONFIG_DIR` is set so labs never hijack the daily user unit.
- `jan cron stop --no-systemd`: stop the daemon without removing the user
  unit (also implied when `$JAN_CONFIG_DIR` is set); `jan lab stop` uses this.

- `jan ps`: list jan processes (and descendants) with per-process and
  collective CPU usage (`--jan-only`, `--avg`). See `docs/cli/ps.md`.

- `jan cron install` / `uninstall`: managed user-crontab block that runs
  `jan --no-log cron` every minute (BEGIN/END JAN CRON markers; idempotent).
- `jan cron --list` prints each schedule plus the next fire in absolute local
  time and a relative span (`--at` sets the list base minute). One line per
  schedule as `chain|cron|next|status` for grep/cut; script-count summary on
  stderr.

- Optional `config:` on any command node and `jan config` with `emit`, `link`,
  `unlink`, `apply`, and `deps`. `emit` concatenates `config.shell` fragments
  (path or inline) for sourcing; `link` symlinks (or `--copy`) path sources or
  writes inline bodies into `$HOME` / `$XDG_CONFIG_HOME`; `unlink` removes only
  destinations that still match managed content; `apply` runs `config.apply`
  argv lists; `deps` reports missing `config.deps` host tools. Config-only
  nodes appear in `--help`. See `docs/cli/config.md`.
- Subcommand `--help` lists that node's `aliases:` and, when present, a
  **Host configuration (`jan config`)** section for that node's `config:`
  (shell path/inline, links, apply counts). Nodes with `run`, `aliases`, or
  `config` (including alias-/config-only children) appear in the parent
  subcommand list. Extra jan names show as "same as `jan … run`".
- Optional `aliases:` on any command node. `jan alias` emits extra names
  for that node's `run` / leaf exec (`aliases: [lb]`) and traditional
  shell aliases from a map (`gs: git status`). Empty map values are extra
  jan names. Jan command names win on collision; skipped shell aliases are
  noted as comments. Unsafe names fail at spec load.

### Changed

- Essential child-env allowlist includes `JAN_CONFIG_DIR`, `UNIFIER_HOME`, and
  `JAN_LAB_ID` so `env.pass` restricted scripts still honor `jan lab` isolation.


- `jan cron --list` is one pipe-separated line per schedule
  (`chain|cron|next|status`) instead of a multi-line block, so output works
  with `grep` / `cut -d'|'`. Disabled entries use status `disabled`; the
  script-count footer moves to stderr.

- `jan config link` skips destinations that already exist: it prints a warning
  and continues (use `--force` to replace). An existing symlink to the intended
  source is treated as already linked. `config.link` values may be inline file
  bodies (written to the destination) or relative paths (symlinked / `--copy`).
  Each `link` / `unlink` run prints a summary (created/already ok/skipped, or
  removed/absent/foreign).

- `packages.gradle` for JVM/Kotlin script deps: inline
  `group:artifact:version` lists or a `project:` directory with
  `build.gradle(.kts)` + `gradle.lockfile`. Envs are hash-keyed under
  `~/.cache/jan/packages/gradle/<hash>/`; Gradle resolves jars into `lib/`
  (Java Library plugin / `runtimeClasspath`) and jan injects `CLASSPATH` plus
  automatic `-cp` for `java` / `kotlin` / `kotlinc` argv.
  Warm hits skip spawning `gradle`.
- Per-manager language/runtime minima on package decls: `packages.uv.python`,
  `packages.pnpm.node`, and `packages.gradle.java` (alias `jdk`). Forms like
  `21` or `>=3.11` are checked against the host toolchain on ensure/run and
  included in the env hash. Map form uses `packages:` for the dependency list
  (short list form unchanged).
- `exec.kotlin` for `.kt` / `.kts` leaves (path or inline): compile-once into the
  package cache, wire gradle classpath, forward `exec.argv` to `main`.
- `exec.python` for `.py` leaves (path or inline via `python3 -c`) and `exec.node`
  for `.js` / `.mjs` / `.cjs` (path or inline via `node -e`). `exec.argv` is
  forwarded as script args; CLI string eval (`argv: [python3, -c, …]` /
  `[node, -e, …]`) still works alongside these forms.
- `exec.bash` / `exec.sh` / `exec.zsh` for shell leaves (path or inline).
  Inline Bash uses `bash -lc`, Zsh `zsh -c`, POSIX sh `sh -c`, with `$0`
  inserted so `$1` / `"$@"` match script semantics. `argv: [bash, -lc, …]`
  still works.
- Optional `tests:` on any command node: Given/When/Then POSIX `sh` cases
  (`given_…_when_…_then_…` names). `jan test <path>` runs that node and every
  nested descendant. `when:` is extra argv for the command the test is declared
  on (the script path is implied); omit it to invoke that command with no extra
  args. Captured into `JAN_STATUS` / `JAN_STDOUT` / `JAN_STDERR` for `then`.
  Result lines print the script path and the test name. `jan test` never writes
  the audit log (`--no-log` is implied, including nested `jan` invocations).
- `exec.text` (alias `exec.cat`) prints a literal YAML block with no
  subprocess. `commands.help` leaves using this form are inlined into
  `--help` (and omitted from the subcommand list), like `run` is the
  conventional exec child.
- Optional `type:` and `choices:` on `inputs:` entries. jan checks values
  (and non-empty defaults) before interpolating `${{ inputs.* }}` into
  `exec.argv` / `env`. Types include `url`, `https`, `int`, `uint`, `float`,
  `bool`, `path`, `file`, `dir`, `port`, `pid`, `email`, `hostname`, `ipv4`,
  `ipv6`, `ip`, `uuid`, `hex`, `duration`, `date`, `json`, and `enum`.
  Unknown types fail at spec load; bad CLI values fail before the script runs.
- `packages.pnpm` is implemented: inline `name@version` lists or a `project:`
  directory with `package.json` + `pnpm-lock.yaml`. Envs are hash-keyed under
  `~/.cache/jan/packages/pnpm/<hash>/` and installed there (never in your tree);
  on run jan prepends `node_modules/.bin` to PATH, sets `NODE_PATH`, and
  resolves argv[0] from `.bin`.
- `jan packages` built-in for inspecting cached package environments:
  `list` / `show` / `path` for uv, pnpm, and gradle (`--lib` / `--classpath`
  for gradle). `--json`, `--orphans`, and `--uv` / `--pnpm` / `--gradle`
  scoping. `prune` drops orphaned envs older than a threshold (default 30d),
  with optional `--min-size`, `--all`, and `--yes` (dry-run otherwise).
  Successful runs update `.jan-used` so prune keeps hot caches.

### Changed

- Warm `packages.uv` / `packages.pnpm` / `packages.gradle` hits skip spawning
  the manager `--version`; the tool is only required when an env must be built
  or rebuilt. Cold `packages.gradle` resolves stream Gradle's console output and
  print jan progress lines on stderr (coordinates, cache path, jar count).

- Package dependencies must pin an exact version so env hashes identify a single
  resolution. uv: inline entries need `==` / `===` (rejected at load),
  requirements files are checked line by line (no `-r` / `-e`), and `project:`
  requires a `uv.lock`. pnpm: inline entries need a full
  `name@major.minor.patch` (no ranges, dist-tags, aliases, or git/file specs),
  and `project:` requires a `pnpm-lock.yaml` installed with `--frozen-lockfile`.
  gradle: inline entries need `group:artifact:version` with an exact version
  (no `+`, Ivy ranges, or `latest.release`), and `project:` requires a
  `gradle.lockfile`. Locked uv projects now install from the exported lock plus
  `uv pip install --no-deps <project>` instead of re-resolving.

## 0.13.0 — 2026-08-07

- Release 0.13.0


## 0.12.0 — 2026-08-07

### Added

- Advanced include links: local includes stay unhashed by default; optional
  `sha256` pins verify on load and in `jan validate`.
- Script-file includes (non-`.yaml`/`.yml`) become exec leaves, with optional
  `argv` / `passthrough` on the include map; include identity is retained on
  the node for `jan show` / bundle closure.
- Local `exec.file` (optional `sha256`), mirroring remote `exec.url`.
- `jan bundle` packs linked script files alongside YAML.
- `packages.uv` for script package deps: inline list, `project:`, or
  `requirements:`; hash-keyed venvs under `~/.cache/jan/packages/uv/`.
  `packages.pnpm` is reserved (rejected until implemented).

- Release 0.12.0


## 0.11.0 — 2026-08-06

### Added

- Remote jan trees: `jan use https://…/bundle.zip --sha256 <hex>` downloads a
  bundle, verifies the zip and `manifest.json` member hashes, unpacks under
  `~/.cache/jan/trees/<sha256>/`, and prefers that directory.
- Remote YAML `include:` entries with required `url` + `sha256` (optional `ttl`).
- Remote script leaves via `exec.url` + `exec.sha256` (optional interpreter
  `argv` prefix and `ttl`), using the same HTTPS fetch / SHA256 / TTL cache as
  wgex-inspired object storage under `~/.cache/jan/objects/`.
- `JAN_ALLOW_HTTP` / `--allow-http` to override HTTPS-only defaults.

### Security

- Remote content still executes with the user's privileges once preferred or
  included; hashes provide integrity, not publisher authentication.
- `jan bundle` fails if the tree contains remote includes (bundles stay
  self-contained).

## 0.10.0 — 2026-08-06

- Release 0.10.0


## 0.9.0 — 2026-08-06

- Release 0.9.0


## 0.8.0 — 2026-08-06

- Release 0.8.0


Entries describe behavior at the time of each release and are not current usage
instructions. See [README.md](README.md) for the supported interface.

## Unreleased

### Added

- Built-ins `jan list`, `search`, `show`, `validate`, and `audit` for inspecting
  the preferred tree and SQLite invocation log (semantic replacement for the
  retired `scripts-cli`).

### Changed

- Personal utilities are no longer nested under `jan-cli/generated/scripts/`.
  Prefer a flat tree such as `dotfiles/jan`. The old
  `generate_scripts_jan_spec.py` helper is retired.
- Removed the incubator `scripts` project dependency (`scripts/source`).

### Breaking

- Spec selection now uses only `jan use <DIR>`. Removed direct spec flags,
  spec-selection environment variables, cwd discovery, and well-known-directory
  fallback behavior.
- `jan alias` now emits bare `jan` command chains without embedded spec flags.

### Security

- Confined YAML includes to the preferred tree; hardened alias generation and
  bundle extraction; resolved executables before applying spec PATH entries.
- Documented that preferred trees are unsandboxed executable code and bundle
  manifest hashes do not authenticate publishers.

## 0.7.0 — 2026-08-04

- Release 0.7.0


## 0.6.0 — 2026-08-04

- Release 0.6.0


## 0.5.0 — 2026-08-04

- Release 0.5.0


## 0.4.0 — 2026-08-04

- Release 0.4.0


## 0.3.0 — 2026-08-04

- Release 0.3.0


## 0.2.0 — 2026-06-03

### Breaking

- Removed the bundled embedded default spec (`default.spec.yaml` compiled into
  the binary). At the time of this release, `jan` loaded specs from explicit
  flags, cwd-local files, or the well-known install directory. Those resolution
  modes were removed after 0.7.0; current versions use `jan use`.

### Added

- Added, at that time, a well-known spec directory and related environment
  overrides. These interfaces are historical and no longer supported.
- Script `path`, `dependencies`, `requires`, and `env` fields for PATH prepending, transitive script deps, external tool checks, and environment variables.
- `jan bundle` and `jan alias` built-ins for portable spec export and shell alias generation.

### Changed

- Example default command tree moved to `examples/default.spec.yaml` (install manually or via `jan bundle` / `jan-install.sh`).
- Used the now-removed direct flags, environment variables, cwd lookup, and
  well-known-directory fallback for spec resolution.

## 0.1.0

- Initial release with embedded default spec, YAML command trees, SQLite audit log, and extra-spec merging.