Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 63 additions & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ These rules are **mandatory** for every Claude instance working on this repo.
6. **Atomic commits** — 1 commit = 1 concept. Better 3 small focused commits than 1 giant commit. Each commit should be self-contained and pass CI on its own.
7. **Commit message format** — `type: clear title` where type is `feat`, `fix`, `chore`, `refactor`, or `docs`. Add bullet points in the body for details when needed.
8. **Documentation** — After every significant change, update `CLAUDE.md` first (implementation state, module structure, test count), then `README.md` if user-facing features changed.
9. **GitHub metadata** — When changes affect the project scope, features, or tech stack, update the GitHub repository description and topics to stay in sync. See the "GitHub Metadata" section below for current values and the `gh` commands to update them.

## Project Overview

Expand All @@ -29,7 +30,7 @@ These rules are **mandatory** for every Claude instance working on this repo.
cargo build # Debug build
cargo build --release # Release build
cargo run -- <subcommand> # Run (e.g., cargo run -- install firefox)
cargo test # Run all tests (195 tests: 103 bin + 92 lib)
cargo test # Run all tests (209 tests: 92 bin + 117 lib)
cargo test <name> # Run a single test by name
cargo test -- --nocapture # Run tests with stdout visible
cargo clippy # Lint
Expand All @@ -47,17 +48,31 @@ There are no integration tests — all tests are unit tests inside `#[cfg(test)]

1. **Detect system** — `SystemProfile::detect()` auto-detects arch, dynamic linker, libc, lib dirs, filesystem layout
2. **Resolve source** — if `--from` omitted, queries ALL plugins in parallel (including AUR `-bin` variants); user picks from full list via interactive `dialoguer::Select` (`pick_source()` in `cli/install.rs`)
3. **Resolve deps** — recursive dependency resolution with cycle detection (`cli/deps.rs`)
3. **Resolve deps** — recursive dependency resolution with cycle detection + **cross-source fallback** (queries all plugins if dep not found in primary source, lets user choose) (`cli/deps.rs`)
4. **Check conflicts** — 5 types: file ownership, binary name, library soname, declared conflicts, version constraints (`core/conflicts.rs`)
5. **Download** in parallel (4 threads via `thread::scope`) with progress bars and retry
6. **Verify** — SHA256 checksum + GPG signature (best-effort)
5. **Download** in parallel (4 threads via `thread::scope`) with progress bars and retry — **step [1/4] indicator**
6. **Verify** — SHA256 checksum + GPG signature (best-effort) — **step [2/4]**
7. **Extract** and analyze ELF binaries with `goblin`
8. **Arch check** — verify ELF `e_machine` matches host architecture before patching (warning on mismatch)
9. **Patch** ELF binaries with `elb` (set interpreter, RUNPATH) using detected page size
9. **Patch** ELF binaries with `elb` (set interpreter, RUNPATH) — **parallel patching** via `thread::scope` for packages with multiple ELFs — **step [3/4]**
10. **Remap** FHS paths to ZL-managed directories (`core/path/`)
11. **Install** atomically — `Transaction` tracks all changes; rollback on any failure
12. **Post-install checks** — warn about missing shared libraries not found in ZL DB or system lib dirs
13. **Track** in redb database + dependency graph
14. **Record history** — install event stored in HISTORY table for `zl history`/`zl rollback`
15. **Summary** — colored output with step [4/4] completion indicator

### New commands (v0.2)

- **`zl run <package>`** — download, extract, patch, execute a package without installing. Temp dir auto-cleaned on exit.
- **`zl history list`** — show install/remove/upgrade history with timestamps
- **`zl history rollback [N]`** — undo the last N operations (installs can be rolled back; removes show reinstall hint)
- **`zl why <package>`** — trace dependency chain explaining why a package is installed
- **`zl doctor`** — full system diagnostics: DB integrity, broken symlinks, missing libs, orphans, disk usage, system profile
- **`zl size [package]`** — disk usage per package with file breakdown, dep costs. `--sort` for largest first.
- **`zl diff <package>`** — show version/dep/size changes before updating
- **`zl audit [package]`** — check installed packages for known CVEs via OSV.dev API
- **`zl cache dedup`** — deduplicate identical shared libraries across packages using hardlinks

### Search flow (`zl search`)

Expand All @@ -66,13 +81,15 @@ There are no integration tests — all tests are unit tests inside `#[cfg(test)]
3. **AUR binary discovery** — if the query doesn't end with `-bin`, automatically also fetches `-bin`, `-appimage`, `-prebuilt` variants and tags them `[binary]`
4. **Sorted output** — results sorted by relevance (default), name, or version via `--sort`
5. **Filtering** — `--exact` shows only exact name matches; `--from` limits to a single source; `--limit` controls results per source
6. **Colored output** — exact matches highlighted green+bold, versions in yellow, source headers in cyan

### Removal flow (`zl remove --cascade`)

1. **Preview before action** — `--cascade` always shows what will and won't be removed before prompting
2. **Orphan detection** — only removes packages that are: (a) tracked in ZL's DB, (b) marked as implicit (`explicit: false`), (c) not depended on by any remaining package (checked via both dependency table and shared lib needs)
3. **Shared dep protection** — dependencies used by other packages are listed as "Keeping (needed by X)" and never removed
4. **Dry-run support** — `--dry-run` with `--cascade` shows the full removal plan without touching anything
5. **History recording** — removal events stored for `zl history`/`zl rollback`

### Startup flow (`main.rs`)

Expand All @@ -91,9 +108,11 @@ There are no integration tests — all tests are unit tests inside `#[cfg(test)]
- **`SourcePlugin` trait** (`plugin/mod.rs`): Interface every package source implements — `name()`, `search()`, `resolve()`, `download()`, `extract()`, `sync()`. Plugins are compile-time modules with trait objects, not dynamic libraries.
- **`Transaction`** (`core/transaction.rs`): Atomic install — tracks files/dirs/symlinks/DB entries created during install, rolls back everything on failure.
- **`DepGraph`** (`core/graph/model.rs`): petgraph-based dependency graph with topological sort, cycle detection, orphan detection.
- **`ZlDatabase`** (`core/db/ops.rs`): redb-based persistent store. Tables: PACKAGES, FILE_OWNERS, LIB_INDEX, DEPENDENCIES, PINNED, PLUGIN_METADATA.
- **`ZlDatabase`** (`core/db/ops.rs`): redb-based persistent store. Tables: PACKAGES, FILE_OWNERS, LIB_INDEX, DEPENDENCIES, PINNED, PLUGIN_METADATA, HISTORY.
- **`PathMapping`** (`core/path/mod.rs`): Dynamic FHS-to-ZL path translation using SystemProfile.
- **`PackageCandidate` / `ExtractedPackage`** (`plugin/mod.rs`): Common types shared across all plugins for package metadata and extracted content.
- **`PluginInfo`** (`plugin/mod.rs`): Plugin metadata for the remote plugin registry.
- **`HistoryEntry`** (`core/db/ops.rs`): Tracks install/remove/upgrade/rollback events with timestamps.

### Plugin system

Expand All @@ -104,10 +123,14 @@ All plugins implement `SourcePlugin` and are registered in `main.rs`. To add a n

Current plugins: `pacman` (Arch repos), `aur` (AUR RPC v5 + makepkg, with `-bin` variant discovery), `apt` (Packages.gz + .deb), `github` (Releases API).

Remote plugin registry: `fetch_remote_registry()` fetches `PluginInfo` from a URL for future plugin marketplace.

### Command dispatch pattern

Each CLI command lives in `src/cli/<command>.rs` with a `pub fn handle(...)` function. Most `handle` functions receive the parsed args struct plus an `AppContext` reference (defined in `cli/mod.rs`), which bundles shared state: `ZlPaths`, `ZlDatabase`, `PluginRegistry`, `SystemProfile`, and flags (`auto_yes`, `dry_run`, `skip_verify`). Commands are dispatched via a `match` in `main.rs`.

**Full command list**: `install`, `remove`, `search`, `update`, `upgrade`, `list`, `info`, `cache` (list/clean/dedup), `completions`, `pin`, `unpin`, `export`, `import`, `switch`, `self-update`, `env` (shell/list/delete), `run`, `history` (list/rollback), `why`, `doctor`, `size`, `diff`, `audit`.

### Error handling

- `ZlError` enum in `error.rs` (thiserror, boxed where needed to keep size small) for domain errors with `.suggestion()` hints
Expand All @@ -122,6 +145,9 @@ Each CLI command lives in `src/cli/<command>.rs` with a `pub fn handle(...)` fun
- **Dynamic detection over hardcoded paths**: interpreter from /bin/sh's PT_INTERP, lib dirs from ldconfig + ld.so.conf
- **RUNPATH over RPATH**: modern standard, respects LD_LIBRARY_PATH
- **Atomic transactions**: every install is wrapped; failure = full rollback
- **Colored output**: uses `console` crate throughout (search, list, doctor, diff, audit, size, install steps)
- **Parallel ELF patching**: packages with >1 ELF are patched concurrently via `thread::scope` with 4-way chunking
- **Cross-source dep resolution**: when a dependency is not found in the primary source, all other sources are queried and the user chooses

### ZL directory layout (runtime)

Expand All @@ -134,7 +160,7 @@ Each CLI command lives in `src/cli/<command>.rs` with a `pub fn handle(...)` fun
packages/ # Per-package directories (name-version/)
cache/ # Download cache
envs/ # Ephemeral/named environment roots
zl.redb # Package database
zl.redb # Package database (includes HISTORY table)
```

### Key crates
Expand All @@ -150,20 +176,49 @@ Each CLI command lives in `src/cli/<command>.rs` with a `pub fn handle(...)` fun
| `tar` + `zstd` + `flate2` + `xz2` + `bzip2` + `ar` + `zip` | Archive formats |
| `sha2` | SHA256 checksums |
| `indicatif` + `dialoguer` | Progress bars and interactive prompts |
| `console` | Colored terminal output |

### Code quality

- **Zero clippy warnings**: `cargo clippy -- -D warnings` passes clean
- **Zero `cargo fmt` diff**: all code is formatted
- **195 tests**: comprehensive coverage of core modules (conflicts, ELF, path mapping, DB, graph, transaction, verify, plugins, search scoring, system detection)
- **209 tests**: comprehensive coverage of core modules (conflicts, ELF, path mapping, DB, graph, transaction, verify, plugins, search scoring, system detection, cache dedup, run, doctor, size, history, why)

### Naming conventions

- `SystemLayout` variants use PascalCase: `Fhs`, `MergedUsr`, `NixOS`, `Guix`, `Termux`, `GoboLinux`, `Custom`
- `Conflict` variants avoid repeating the enum name: `Declared` (not `DeclaredConflict`), `Version` (not `VersionConflict`)
- `Arch::parse()` and `SystemLayout::parse()` instead of `from_str()` (avoids confusion with `std::str::FromStr` trait)
- `SortOrder` enum (in `cli/mod.rs`) uses `ValueEnum` derive for clap: `Relevance`, `Name`, `Version`
- `HistoryAction` enum: `Install`, `Remove`, `Upgrade`, `Rollback`
- Structs with simple `new()` constructors also implement `Default` (via `#[derive(Default)]` or manual impl): `PluginRegistry`, `PacmanPlugin`, `AptPlugin`, `AurPlugin`, `GithubPlugin`, `DepGraph`, `Transaction`
- The `core/build/` module uses `#![allow(dead_code)]` since it is scaffolding for future source-build support
- `DepGraph`, `DependencyEdge`, `DepType` have `#[allow(dead_code)]` — they are part of the graph model used for future features
- `ArchMismatch` error variant has `#[allow(dead_code)]` — available for strict arch enforcement in future
- `PluginInfo`, `fetch_remote_registry`, `list_info` have `#[allow(dead_code)]` — scaffolding for remote plugin marketplace

## GitHub Metadata

Keep the repository description and topics in sync with the project state. Update them whenever features, scope, or tech stack change significantly.

### Current description

```
Universal Linux package manager with native binary translation. Install packages from any source (pacman, apt, AUR, GitHub releases) on any Linux system — no containers, no VMs, zero runtime overhead. Written in Rust.
```

### Current topics

```
linux, package-manager, rust, elf, binary-translation, cli, apt, pacman, aur, cross-distribution, dependency-management
```

### Commands to update

```bash
# Set description
gh repo edit --description "Universal Linux package manager with native binary translation. Install packages from any source (pacman, apt, AUR, GitHub releases) on any Linux system — no containers, no VMs, zero runtime overhead. Written in Rust."

# Set topics (replaces all topics)
gh repo edit --add-topic linux --add-topic package-manager --add-topic rust --add-topic elf --add-topic binary-translation --add-topic cli --add-topic apt --add-topic pacman --add-topic aur --add-topic cross-distribution --add-topic dependency-management
```
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Progress bars and user interaction
indicatif = "0.17"
dialoguer = "0.11"
console = "0.15"

# File hashing
sha2 = "0.10"
Expand Down
39 changes: 37 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,11 +305,41 @@ zl pin <package> # Pin a package (prevent updates)
zl unpin <package> # Unpin a package (allow updates)
```

### Run Without Installing

```bash
zl run <package> # Download, patch, execute, then cleanup
zl run <package> --from github # Run from a specific source
zl run <package> -- --help # Pass args to the binary
```

### Diagnostics & Analysis

```bash
zl doctor # Full system health check (DB, symlinks, libs, orphans)
zl why <package> # Show why a package is installed (dependency chain)
zl size # Disk usage per package
zl size <package> # Detailed breakdown with file sizes and dep costs
zl size --sort # Sort by size (largest first)
zl diff <package> # Show what would change if updated
zl audit # Check all packages for known CVEs (via OSV.dev)
zl audit <package> # Check a specific package
```

### History & Rollback

```bash
zl history list # Show install/remove/upgrade history
zl history rollback # Undo the last operation
zl history rollback 3 # Undo the last 3 operations
```

### Cache Management

```bash
zl cache list # Show cached downloads and sizes
zl cache clean # Remove all cached files
zl cache dedup # Deduplicate shared libraries (hardlinks)
```

### Lockfile Export/Import
Expand Down Expand Up @@ -530,10 +560,15 @@ ZL can build packages from source when precompiled binaries aren't available. It
- **5-way conflict detection** — prevents broken installs before they happen
- **Multi-version packages** — install multiple versions side-by-side, switch between them
- **Ephemeral environments** — isolated shells where packages disappear on exit
- **ELF patching with `elb`** — pure-Rust patchelf alternative, sets interpreter and RUNPATH
- **ELF patching with `elb`** — pure-Rust patchelf alternative, sets interpreter and RUNPATH, parallel patching for multi-ELF packages
- **RUNPATH over RPATH** — modern standard, respects `LD_LIBRARY_PATH`
- **`redb` database** — pure-Rust embedded key-value store (ACID, no SQLite/C dependency)
- **`petgraph` dependency graph** — topological sort, cycle detection, orphan detection
- **Cross-source dependency resolution** — when a dep is not found in the primary source, queries all other sources and lets the user choose
- **Colored output** — uses `console` crate for colored output throughout (search, list, doctor, audit, diff, size)
- **CVE auditing** — checks installed packages against the OSV.dev vulnerability database
- **History & rollback** — all install/remove events are recorded; undo recent operations
- **Cache deduplication** — identical shared libraries are hardlinked to save disk space

## Configuration

Expand Down Expand Up @@ -562,7 +597,7 @@ repos = ["core", "extra"]

```bash
cargo build # Build
cargo test # Run all tests (195 tests: 103 bin + 92 lib)
cargo test # Run all tests (209 tests: 92 bin + 117 lib)
cargo test <name> # Run a single test
cargo clippy # Lint
cargo fmt # Format
Expand Down
Loading