Metadata-Version: 2.3
Name: zavis
Version: 0.1.0
Summary: Local multiproject dependency manager for Python, Node, Rust, Go and C#
Author: Bobby Warren
Author-email: Bobby Warren <blwarren@gmail.com>
Requires-Dist: click>=8.5.0
Requires-Dist: filelock>=4.0.4
Requires-Dist: packaging>=26.3
Requires-Dist: platformdirs>=4.12.0
Requires-Dist: requests>=2.34.2
Requires-Dist: rich>=15.0.0
Requires-Dist: semantic-version>=2.10.0
Requires-Dist: univers>=32.0.1
Requires-Python: >=3.14
Description-Content-Type: text/markdown

# Zavis

Zavis is a local Python CLI for inspecting dependency releases and accepting native
package-manager updates only after their publication-age policy passes. The default
cooling-off period is **seven full days (168 hours)**.

Zavis is not a resolver or package manager. It preserves dependency declarations,
uses native resolution, verifies changed direct **and transitive** selections, and
applies only dependency files. It does not synchronize environments or create commits.

## Install and run from this checkout

Python 3.14+ and uv are required. Only the toolchain for the ecosystem being updated
needs to be installed.

```powershell
uv sync --locked
uv run zavis --help
uv run zavis check --project D:/Projects/example
uv run zavis why requests --ecosystem python --project D:/Projects/example
uv run zavis pending --project D:/Projects/example --json
uv run zavis update --project D:/Projects/example --dry-run
uv run zavis update requests --ecosystem python --project D:/Projects/example
uv run zavis verify --project D:/Projects/example
```

Options follow the subcommand. `python -m zavis` is also supported. The distribution,
import package, executable and configuration namespace are all named `zavis`; the
existing checkout directory can retain its original name.

## Commands

| Command | Contract |
| --- | --- |
| `check` | Current, latest, and policy-eligible releases; direct dependencies by default |
| `check --include-transitive` | Include all discovered dependency instances |
| `pending` | Newer releases blocked specifically by age, with eligibility timestamps |
| `why PACKAGE` | Evidence, release decisions, declarations and effective policy |
| `verify` | Verify the entire available resolved graph |
| `update [PACKAGE]` | Stage native direct upgrades and required transitive changes |
| `update --dry-run` | Resolve and verify in staging without applying files |
| `update --run-checks` | Explicitly execute configured checks in staging before apply |
| `audit` | Send proven-public package names and versions to OSV for advisory lookup |
| `config show` | Effective settings and provenance |
| `recover` | Restore an interrupted file application when fingerprints still match |

Shared options: `--project PATH`, repeatable `--ecosystem`, `--config FILE`, `--json`,
`--offline`, `--refresh`, `--verbose`, and `--no-color`. Ecosystem names are `python`,
`rust`, `javascript`, `dotnet`, and `go`. Select a pip environment with
`--tool pip --python PATH`; without an explicit interpreter, a project `.venv` is required.

Targeted updates require one selected ecosystem and an existing lockfile. Unqualified
updates target direct dependencies, not every transitive package independently.
An eligible release may still be incompatible with the project's native constraints.
Exact pins may produce no update. No declaration-rewriting option is implemented.

## Configuration

Zavis reads user configuration from the platformdirs user configuration directory,
under `zavis/config.toml` (Windows uses Local AppData; Linux follows XDG; macOS uses
Application Support). It prefers existing project configuration namespaces:

- Python: `[tool.zavis]` in `pyproject.toml`.
- Cargo: `[package.metadata.zavis]` in `Cargo.toml`.
- npm: `"zavis"` in `package.json`.
- Otherwise: a project `zavis.toml`.

A standalone file looks like this:

```toml
schema_version = 1

[policy]
minimum_release_age = "7d"
allow_prereleases = false
allow_yanked = false

[cache]
metadata_ttl = "1h"

[[checks]]
name = "tests"
argv = ["uv", "run", "--locked", "pytest"]
timeout = "10m"
cwd = "."
```

Precedence is defaults, user file, embedded project configuration, then `zavis.toml`.
`--config FILE` replaces project discovery, retaining user defaults. Tables merge by
key; arrays replace. Unknown keys and invalid types fail. If several embedded configs
exist, use a standalone file: it becomes the sole project configuration rather than
choosing an ecosystem arbitrarily. Durations use integer `s`, `m`, `h`, `d`, or `w` units
(up to 3650 days). Zero explicitly disables only the age delay, not evidence requirements.

Checks never run just because they appear in configuration. `--run-checks` authorizes
their argument arrays, in order, in the staged project. Check environments are not
automatically installed. A failed check rejects the update.

Per-ecosystem/package/direct/transitive overrides, security exceptions and private
registry authentication are deferred. Future options are not silently accepted today.

## Capabilities and evidence

| Ecosystem | Supported state and operations | Limitations |
| --- | --- | --- |
| Python/uv | Universal `uv.lock` version 1, artifact URLs/hashes, groups and conditional edges; `uv lock --no-build` updates | Public PyPI only; workspaces and source builds not enabled |
| Python/pip | Selected interpreter's structured installed inventory | Original registry/artifact provenance generally unknown; inspection only |
| npm | Lock versions 2/3, installation locations, aliases, peers and optional edges; lock-only updates | Public npm only; competing locks, workspaces, bundled/unproven sources fail closed |
| Cargo | Lock versions 3/4, renamed dependencies, duplicate versions and checksums; native precise updates | Public crates.io only; workspace updates deferred; at most 20 direct candidates |
| NuGet | One SDK-style PackageReference project, lock version 1, target frameworks and content hashes; floating reevaluation | No targeted updates, solutions, central/dynamic/imported versions; strict source provenance |
| Go | Native module inventory respecting privacy settings | Publication timestamps cannot be established; updates blocked |

The initial tested update-tool floors are uv 0.12.22, npm 12.0.1, Cargo 1.99.0 and
dotnet SDK 10.0.401. The installed tool is checked before updates. These conservative
floors describe tested capabilities, not when upstream features were first introduced.
See [native compatibility evidence](docs/compatibility.md).

PyPI upload times are per artifact: a newly uploaded wheel cannot borrow an older
wheel's age. Every referenced locked artifact must match registry URL and hash.
Crates.io uses version publication timestamps; npm uses `time[version]`; NuGet uses
registration metadata. Unlisted NuGet year-1900 sentinels are not valid old dates.
Go proxy commit times and index observation times are never treated as publication.

NuGet public-source verification requires matching lock content hashes, assets metadata,
and cached `.nupkg.metadata` origin information. A public source setting alone does not
prove where a cached package came from. Missing evidence is incomplete coverage.

Verification covers the available lockfile/environment inventory, including inactive
platform entries represented there. It does not claim to inventory arbitrary dynamic
build-backend requirements or dependencies outside that native state. Unsupported or
unresolved graph edges are reported, not silently treated as complete.

## Update safety

Zavis snapshots the current working tree into a disposable staging directory, including
uncommitted and untracked project files. It excludes Git metadata, dependency environments
and known build/cache directories. Links/junctions are rejected rather than followed.
External project references and unsupported workspace layouts are blocked.

Native tools change staged locks. Zavis verifies every added/changed version, source or
artifact; unchanged policy violations remain visible without blocking otherwise valid
changes. A first lock verifies all selections. Young or unverifiable transitive dependencies
reject the transaction. Zavis does not search combinations of transitive pins.

Before applying, all captured live inputs must still match. Only allowlisted lockfiles
are copied back. Each replacement is atomic; a durable journal supports rollback across
multiple files. Recovery refuses to overwrite subsequent user edits. Run `zavis recover`
after an interrupted apply. Recovery journals live in the user state directory, separate
from disposable metadata caches.

This is not an operating-system sandbox. Native tools can evaluate trusted project tooling
(notably MSBuild), and explicitly requested checks can have external effects. File rollback
cannot undo those effects, downloads, caches or arbitrary concurrent external writers.
Lifecycle scripts are disabled for npm; uv builds are disabled; Cargo dependencies are
not built. Never use Zavis as a safe executor for an untrusted repository.

## Cache, privacy and output

Registry responses use SQLite caching, one-hour discovery freshness and HTTP validators.
Updates refresh evidence for acceptance. No policy verdict is cached. Offline commands
require fresh cached evidence; offline updates are disabled. Go offline inventory disables
proxy and checksum-network access. Unknown/private package sources are never sent to public
registry or OSV clients. HTTP redirects, implicit netrc credentials and environment proxy
discovery are disabled in this initial public-registry client.

`--json` returns a schema-versioned document with results, diagnostics, coverage, UTC
evaluation time and transaction state. Native output does not contaminate stdout. Human
output escapes terminal controls and redacts common credential forms.

| Exit | Meaning |
| --- | --- |
| 0 | Completed; a fully assessed no-op is successful |
| 1 | Policy violations or advisory findings |
| 2 | Invalid arguments/configuration |
| 3 | Incomplete evidence or unsupported capability |
| 4 | Native/network/metadata failure |
| 5 | Configured check failure |
| 6 | Concurrent modification or recovery required |
| 130 | Interrupted |

`check` does not fail simply because young newer releases exist. `verify` fails for
violations or unknown age. OSV lookup is explicit and does not waive cooling-off policy.
No matching advisories is not a guarantee of safety.

## Development and verification

```powershell
just check
uv run pytest -m integration
uv build
```

The default pytest suite uses local fixtures and requires no public registry access or
non-Python toolchains. Opt-in integrations run real uv/npm/Cargo/dotnet commands against
temporary local registries or feeds. Windows execution is verified; Linux/macOS native
execution remains a release-validation limitation. No remote CI is configured.

The repository persists `tool.uv.exclude-newer = "1 week"`, including development dependency
selection. The build backend is pinned to an independently age-verified release. Preserve
the lock with `uv sync --locked`; use the configured age gate for future dependency changes.
