<p align="center">
<img src="logo.svg" alt="Cabalist" width="600"/>
</p>
<p align="center">
<a href="https://github.com/joshburgess/cabalist/actions/workflows/ci.yml"><img src="https://github.com/joshburgess/cabalist/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<a href="LICENSE-MIT"><img src="https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg" alt="License: MIT OR Apache-2.0"></a>
</p>
**Cabalist** is a strongly opinionated toolkit for managing Haskell `.cabal` files. It provides an interactive TUI, a scriptable CLI, and an LSP server for editor integration, all backed by a parser with byte-identical round-trip fidelity.
The guiding principle: **make pure-Cabal Haskell development as approachable as Stack**, by eliminating the friction of `.cabal` file management while keeping the `.cabal` file as the source of truth.
## What Cabalist Does
- **Manages dependencies**: search Hackage, add packages with correct PVP bounds, detect outdated versions, visualize the dependency tree
- **Manages GHC extensions**: browse 119 extensions with descriptions and safety notes, toggle them on/off
- **Lints your .cabal file**: 16 opinionated, individually configurable checks that catch missing bounds, bad practices, and structural issues
- **Formats your .cabal file**: round-trip safe formatting with optional alphabetical sorting
- **Builds your project**: run `cabal build`, `cabal test`, and `cabal clean` with streaming output and error navigation
- **Initializes new projects**: guided wizard with templates (library, application, library+exe, full)
- **Edits metadata**: change name, version, license, synopsis, and all other top-level fields
- **Manages cabal.project**: view and edit project-level configuration (compiler, index-state, constraints)
- **Integrates with editors**: full LSP server with diagnostics, completions, hover, code actions, formatting, semantic tokens, inlay hints, goto definition, and rename
All of this while **preserving your comments, formatting, and field ordering**. Cabalist never rewrites what you didn't ask it to change.
## Three Tools, One Codebase
| **`cabalist`** | Interactive TUI for day-to-day development |
| **`cabalist-cli`** | Scriptable CLI for automation, CI, and quick edits from the terminal |
| **`cabalist-lsp`** | LSP server for VS Code, Neovim, Emacs, and any LSP-compatible editor |
## Installation
### Pre-built Binaries
Download from the [Releases page](https://github.com/joshburgess/cabalist/releases).
### Homebrew (macOS / Linux)
```sh
brew install joshburgess/tap/cabalist
```
### Nix
```sh
nix run github:joshburgess/cabalist
```
### From Source
Requires Rust 1.88+:
```sh
cargo install --path crates/cabalist-tui # Interactive TUI
cargo install --path crates/cabalist-cli # CLI tool
cargo install --path crates/cabalist-lsp # LSP server
```
## Quick Start
### TUI
```sh
cd my-haskell-project
cabalist # Launch the interactive TUI
```
Use `d` for dependencies, `e` for extensions, `b` for build, `m` for metadata, `p` for cabal.project. Press `?` for help anywhere.
### CLI
| `cabalist-cli check` | Lint your `.cabal` file |
| `cabalist-cli add aeson` | Add a dependency with PVP bounds |
| `cabalist-cli add text --version "^>=2.0"` | Add with specific version constraint |
| `cabalist-cli remove old-package` | Remove a dependency |
| `cabalist-cli extensions --toggle DerivingStrategies` | Toggle an extension |
| `cabalist-cli set synopsis "My cool library"` | Set a metadata field |
| `cabalist-cli fmt` | Format the `.cabal` file |
| `cabalist-cli deps --outdated` | Check for outdated dependencies |
| `cabalist-cli deps --tree` | Show dependency tree |
| `cabalist-cli modules --scan` | Find `.hs` files not listed in `.cabal` |
| `cabalist-cli build` | Run `cabal build` |
| `cabalist-cli test` | Run `cabal test` |
| `cabalist-cli info` | Show project summary |
| `cabalist-cli init --type full` | Create a new project |
| `cabalist-cli update-index` | Download/refresh Hackage index |
### Editor (LSP)
Install `cabalist-lsp` and configure your editor:
- **VS Code**: See `editors/vscode/` for the extension
- **Neovim**: See `editors/neovim/init.lua`
- **Emacs**: See `editors/emacs/cabalist-lsp.el`
The LSP provides: diagnostics (16 lints), completions (packages, extensions, fields), hover documentation, code actions (quick fixes), document symbols (outline), formatting, semantic tokens, inlay hints (latest versions), goto definition (`import:` to `common` stanza), and rename (common stanza refactoring).
## Documentation
| [Getting Started](docs/getting-started.md) | End-to-end walkthroughs for common scenarios |
| [TUI Guide](docs/tui-guide.md) | Every view, keybinding, and workflow in the interactive TUI |
| [CLI Reference](docs/cli-reference.md) | Every command, flag, and example for the CLI |
| [Editor Setup](docs/editor-setup.md) | VS Code, Neovim, and Emacs configuration |
| [Opinions & Lints](docs/opinions.md) | All 16 lints with rationale and configuration |
| [Configuration](docs/configuration.md) | `cabalist.toml` reference with all options |
| [Keybindings](docs/keybindings.md) | Complete TUI keyboard reference |
| [Cabal Spec Compliance](docs/cabal-spec-compliance.md) | Parser syntax support and test coverage |
## Architecture
Cabalist is a Rust workspace of 9 focused crates:
| `cabalist-parser` | CST/AST parser with byte-identical round-trip fidelity |
| `cabalist-project` | Parser for `cabal.project` files |
| `cabalist-ghc` | GHC extensions (200+), warnings, and version knowledge base |
| `cabalist-hackage` | Hackage index, package search, PVP version bounds |
| `cabalist-opinions` | 16 lints, defaults, templates, and configuration |
| `cabalist-cabal` | Async subprocess interface to `cabal` CLI |
| `cabalist-tui` | Interactive TUI (ratatui + crossterm) |
| `cabalist-cli` | Non-interactive CLI (clap) |
| `cabalist-lsp` | LSP server (tower-lsp) |
### Design Principles
1. **The `.cabal` file is the source of truth**: always.
2. **Preserve what the user wrote**: comments, formatting, and field ordering are never lost.
3. **Shell out to `cabal` for what it does well**: solving, building, testing.
4. **Be strongly opinionated with transparent escape hatches**: every lint and default is individually configurable.
### Why Rust?
Cabalist is a tool for Haskell developers, written in Rust so it can ship as a fast, single-binary developer tool without requiring a Haskell toolchain to install. The longer rationale is in [WHY_RUST.md](WHY_RUST.md).
## Contributing
```sh
cargo build --workspace # Build everything
cargo test --workspace # Run all 690+ tests
cargo clippy --workspace # Check for common issues
```
## License
Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT).