A terminal user interface (TUI) for managing PHP Composer dependencies, inspired by lazygit.
Beta — This project is under active development. Expect rough edges and breaking changes.
Built with ratatui + crossterm.
- Packages — Browse all
requireandrequire-devdependencies with version details - Outdated — See which packages have newer versions available, with color-coded status (safe update / major / abandoned)
- Audit — View security advisories and abandoned packages
- Require — Add new packages interactively
- Remove — Remove packages with confirmation
- Update — Update individual packages or all at once
- Search — Fuzzy-filter the package list
- Live output — Streaming command output displayed in real time
- Rust (stable)
- Composer installed and available in your
PATH(or setCOMPOSER_BIN, see below)
Pre-built binaries are available on the releases page.
Download the archive matching your platform, extract it, and move the binary to a directory in your PATH:
# Example for Linux x86_64 — adjust the version and asset name as needed
curl -L https://github.com/gilles-g/lazycomposer/releases/latest/download/lazycomposer-linux-x86_64.tar.gz \
| tar -xz
mv lazycomposer ~/.local/bin/git clone https://github.com/gilles-g/lazycomposer.git
cd lazycomposer
cargo build --releaseThe binary will be at ./target/release/lazycomposer.
To make it available globally, add it to your PATH:
cp ./target/release/lazycomposer ~/.local/bin/Make sure
~/.local/binis in yourPATH. If not, add this to your shell config (~/.bashrc,~/.zshrc, etc.):export PATH="$HOME/.local/bin:$PATH"
# In a directory containing a composer.json
lazycomposer
# Or specify the path explicitly
lazycomposer /path/to/my/projectBy default, LazyComposer looks for composer in your PATH. To use a different binary, set the COMPOSER_BIN environment variable:
# Use a specific path
COMPOSER_BIN=/usr/local/bin/composer2 lazycomposer
# Use composer via Docker
COMPOSER_BIN="docker compose exec php composer" lazycomposer| Key | Action |
|---|---|
1 2 3 |
Switch tab (Packages / Outdated / Audit) |
tab / shift+tab |
Next / previous tab |
j / k or arrows |
Navigate up / down |
shift+tab |
Toggle require / require-dev (Packages tab) |
/ |
Search / filter packages |
r |
Require a new package |
d |
Remove selected package |
u |
Update selected package |
U |
Update all packages (Outdated tab) |
esc |
Close overlay / cancel |
q / ctrl+c |
Quit |
cargo build # Debug build
cargo build --release # Release build
cargo test # Run all tests
cargo clippy # LintDebug logs are written to ~/.local/state/lazycomposer/debug.log.
src/
├── composer/ # Domain layer (no UI dependency)
│ ├── types.rs # Package, StringOrBool, AuditResult, Advisory...
│ ├── exec.rs # Executor trait, RealExecutor, streaming
│ ├── parser.rs # Parse composer.json + composer.lock
│ └── runner.rs # Typed wrapper over composer CLI
├── config/ # Config resolution, binary validation
├── security/ # Package name validation, log sanitization
├── logger/ # File logger (~/.local/state/lazycomposer/debug.log)
└── ui/
├── app.rs # Event loop, async loading, key routing
├── style/ # Color theme + style functions
├── components/ # TabBar, StatusBar, ConfirmDialog, InputBox, Spinner
└── panels/ # Packages, Outdated, Audit, Output
Data loading is asynchronous (background threads + mpsc channels) so the UI stays responsive.
MIT


