# Changelog — cleanlib-cli
All notable changes to the `cleanlib-cli` tool will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this crate adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- **CLEANLIB-196 (Client-3.1) — SARIF v2.1.0 output format for `cleanlib
verdict` and `cleanlib scan`.** New `--output sarif` variant emits the
OASIS Static Analysis Results Interchange Format v2.1.0 log so verdict
findings can be ingested by GitHub Code Scanning, GitLab MR Security
Dashboard, SonarQube, and any third-party CI security dashboard that
supports SARIF. Wire mapping: cleanlib 3-tier decision → SARIF
`level` (`ALLOW → note`, `WARN → warning`, `DENY → error`);
verdict-label → `ruleId`; package coordinate →
`logicalLocations[0].fullyQualifiedName` (purl-lite
`pkg:<eco>/<name>@<version>`); severity + composite_score + verdict_id
surfaced under `properties` for filter-friendly consumption. Golden-file
test in `cleanlib-cli/tests/integration/sarif_golden.rs` locks the wire
shape byte-for-byte; regenerate via `UPDATE_SARIF_GOLDENS=1 cargo test
-p cleanlib-cli --test integration sarif_golden`.
### Fixed
- **CLEANLIB-166 — `--output <invalid-value>` now rejected at parse-time with a
clear error listing valid options (previously silent text-fallback).**
Prior to this fix every `--output`-carrying subcommand (`scan`, `audit`,
`policy preview`, `fix`) did `match output.as_str() { "json" => …, _ => text
}` — so `cleanlib scan --output josn` (a typo) silently rendered the text
envelope and any downstream JSON-parsing CI script would fail without seeing
a cleanlib-level error. The `--output` flag now derives clap `ValueEnum`
(`OutputFormat { Text, Json }`) shared across `verdict` / `scan` / `audit` /
`policy preview` / `fix`; an invalid value short-circuits at argv parse with
clap's canonical `error: invalid value 'X' for '--output <FORMAT>' [possible
values: text, json]` and exit code 2. `--help` now enumerates the valid
values per verb. Sister of CLEANLIB-132 (fail-loud on missing subcommand) —
same "never silent fail-open" philosophy applied to invalid flag values.
### Planned
- Pre-built cross-platform binaries (Linux, macOS, Windows) so customers without a Rust toolchain can install via `curl | sh` without `cargo install`
- Homebrew tap for `brew install` on macOS
## [0.1.4] — 2026-07-02
### Added
- **App-4 (CLEANLIB-211..214) — `cleanlib fix` autonomous remediation.**
Reads a customer lockfile (`package-lock.json`, `Cargo.lock`, `poetry.lock`,
`go.sum`), resolves verdicts per node via `cleanlib-client`, and emits an
upgrade plan.
- **CLEANLIB-211** — local upgrade: DENY-tier packages with a
`recommended_version=X` marker in `suggested_actions` become fix entries;
`--apply` rewrites the lockfile in-place (JSON edit for npm; line-oriented
edit for the TOML `[[package]]` shape used by Cargo/poetry; go.sum row
rewrite). Existing lockfile is backed up (`.cleanlib-backup-<unix>`).
- **CLEANLIB-212** — `--auto-pr` emits a host-agnostic PR bundle to stdout;
downstream drivers open the actual PR via github/gitlab/bitbucket REST.
`--apply` is a no-op when `--auto-pr` is set.
- **CLEANLIB-213** — risk-acceptance integration: `--risk-accept <file>`
loads the customer's `risk_accepted:` YAML (same shape `cleanlib
risk-accept` emits); matched packages are skipped; `expires_at` honored.
- **CLEANLIB-214** — safety guards: dry-run is DEFAULT (`--apply` required
to write; exit 4 when pending upgrades so pre-commit hooks can gate).
`--allow-list <file>` restricts which packages fix can touch.
Blast-radius block names every `file:line` the fix would rewrite.
Lockfile parsers ported from cleanlib-vscode-extension's
`manifestParsers/lockfiles.ts` (CLEANLIB-129 transitive scanning) so the
extension and CLI see the same graph.
## [0.1.2] — 2026-06-12
### Fixed
- **CLEANLIB-155 — `cleanlib scan` no longer silently passes block-equivalent
verdicts.** `scan_exit_code` matched only canonical decision strings (a bare
`_ => {}` fallthrough → exit 0), so a `VECTOR_VERDICT` / `DM_THRESHOLD_BLOCK`
decision let a customer's `cleanlib scan || exit 1` CI gate pass silently —
the scan-path sister-gap of the verdict-path fix (CLEANLIB-31b). Both paths
now share `normalize_decision`: block-labels → DENY → exit 1; unknown /
`INSUFFICIENT_DATA` → WARN → exit 2 (fail-loud, never silent-ALLOW). The two
paths can no longer drift apart.
- **CLEANLIB-156 — `cleanlib logout` now clears the persistent verdict cache**
(Path-2 defense-in-depth) so a logged-out session is not served previously
cached verdicts. Adds `PersistentCache::clear()`; idempotent if no cache exists.
## [0.1.1] — 2026-06-05
### Fixed
- **`cleanlib audit` now reaches the right endpoint.** The transport layer
hit `/v1/customer/audit` (404 on live App) instead of `/v1/audit` (200 —
the App's `verbs_router` mount). Verified live against
cleanapp.clnstrt.dev 2026-06-05; the command was completely broken on
every prior release. Fix lives in `cleanlib_client::transport::Client::audit`.
### Added
- **Leading `decision:` line** in `cleanlib verdict` text output. The
at-a-glance signal (ALLOW / WARN / DENY) now appears as the FIRST line,
styled via the existing `style_decision` palette (green / yellow / red
on TTY). The detail-level `verdict:` label (VECTOR_VERDICT / ...,
customer-facing-masked via `mask_engine_tag`) stays as the second line.
Customers reading from a terminal can scan a wall of `cleanlib verdict`
output and pick out DENY rows immediately.
- **Multi-line `suggested:` rendering.** Single-action verdicts still
print inline (`suggested: <one string>`); multi-action verdicts
now render as a bulleted list under a bare `suggested:` header. The
prior semicolon-joined run-on string was unreadable for the
CLEANLIB-72 populator output (3–4 advice strings per ALLOW_BY_ABSENCE
/ policy-DENY case).
- **`cleanlib scan --packages` accepts JSON-array input.** Auto-detected
via leading `[` character. Each element is
`{"name": "...", "version": "..."}`. The text-only `name@version`
per-line format remains the default. Common customer-facing producer
paths (`npm ls --json | jq ...`, package-list outputs from CI tools)
now work without a translation step.
### Notes
- No breaking changes to existing flag surface, exit codes, or env vars.
- `--output json` machine path unchanged; the text-mode `decision:` /
bulleted `suggested:` adjustments are TTY presentation only.
## [0.1.0] — 2026-06-03
### Added
- Inaugural crates.io release: `cargo install cleanlib-cli`
- Ten commands: `status`, `login`, `logout`, `verdict`, `scan`, `policy preview`, `risk-accept`, `audit`, `fetch`, `config init`
- Four ecosystem wrappers: `cleanlib pip`, `cleanlib npm`, `cleanlib cargo`, `cleanlib go`
- CI-gating exit-code semantics: `rc=0` on `ALLOW`, `rc=1` on `DENY`, `rc=2` on `WARN` with `--strict-warn`
- Cached verdicts with configurable TTL (default 300 seconds)
- Per-ecosystem proxy configuration emission (`.npmrc`, `pip.conf`, Go env)
- Empty-bearer fail-loud at `login` and `config init` (rejects silent-misconfig modes)
- Engine-tag masking at render: AI-derived signal sources surface as `AI-derived` to customers
- Transactional `config init` output (no partial writes on invalid input)
[0.1.0]: https://crates.io/crates/cleanlib-cli/0.1.0