# Development guide
Build, test, and run `ai-usagebar` from a checkout. For the pre-PR gate and
provider-addition rules see [CONTRIBUTING.md](CONTRIBUTING.md). For release
invariants see [CLAUDE.md](CLAUDE.md).
- [Prerequisites](#prerequisites)
- [Build](#build)
- [Run locally](#run-locally)
- [Tests](#tests)
- [Lint and format](#lint-and-format)
- [Configuration while developing](#configuration-while-developing)
- [Ollama Cloud](#ollama-cloud)
- [Windows](#windows)
- [Layout](#layout)
- [See also](#see-also)
## Prerequisites
| Rust / Cargo | **1.90+** (MSRV) | [rustup](https://rustup.rs/) |
| Node.js | 18+ | GNOME / KDE / Omarchy / Windows popover contract tests |
| Git | any | |
On Windows you also need the **MSVC Build Tools** (linker) and **NASM**
(`ring` uses it). See [docs/windows-build.md](docs/windows-build.md).
Optional:
- `cargo-machete` — unused-dependency check (`cargo install cargo-machete`)
- Live API keys in the environment for `make smoke`
## Build
```bash
git clone https://github.com/akitaonrails/ai-usagebar.git
cd ai-usagebar
cargo build --release
```
Binaries land at:
| Widget / CLI | `target/release/ai-usagebar` |
| TUI | `target/release/ai-usagebar-tui` |
| Windows tray | `target/release/ai-usagebar-tray` (Windows only) |
Debug builds (`cargo build`) are fine for iterating; the first release build
is slow because of `reqwest` / `ring`.
Install to `$PREFIX` (default `/usr/local`):
```bash
make install # /usr/local/bin + share
make install PREFIX=$HOME/.local
```
KDE plasmoid install is separate (`make install install-plasmoid`) and is
documented in [kde-plasmoid/README.md](kde-plasmoid/README.md).
## Run locally
From the checkout, without installing:
```bash
./target/release/ai-usagebar --json
./target/release/ai-usagebar --vendor openrouter --format '{or_balance}'
./target/release/ai-usagebar --watch 5 # refresh every 5s while iterating on --format
./target/release/ai-usagebar-tui
```
`--config PATH` points both binaries at an alternate TOML. The file **must
already exist**; loads and the Settings overlay then read and write that path
for the whole process, so a scratch config never touches the real one:
```bash
./target/release/ai-usagebar --config ./config.test.toml --vendor kimi --watch 5
./target/release/ai-usagebar-tui --config ./config.test.toml
```
Default config locations:
| Linux | `~/.config/ai-usagebar/config.toml` |
| macOS | `~/Library/Application Support/ai-usagebar/config.toml` (legacy `~/.config/…` still wins if present) |
| Windows | `%APPDATA%\ai-usagebar\config\config.toml` |
## Tests
The gate contributors run is the same one CI runs:
```bash
make test # cargo test + desktop JS suites
cargo clippy --all-targets -- -D warnings
cargo fmt --all -- --check
cargo machete # no unused dependencies
```
`make test` rather than `cargo test` alone: the GNOME, KDE, Omarchy, and
Windows-popover frontends have Node contract tests, and a report-shape change
can break them without touching Rust.
| `make test` | `cargo test` + `desktop-test` + `plugin-test` |
| `make desktop-test` | GNOME, KDE, Windows popover `.test.mjs` |
| `make plugin-test` | Omarchy `model.test.mjs` |
| `make smoke` | `cargo test --test live -- --ignored --nocapture` |
| `make clippy` | `cargo clippy --all-targets -- -D warnings` |
| `make fmt` | `cargo fmt` |
Live smoke tests need credentials in the environment (`OLLAMA_API_KEY`,
`ZAI_API_KEY`, …). Tests marked `#[ignore]` never run in `make test`. A
`#[test]` must not read a real `$HOME` / `$XDG` path — the AUR package runs
`cargo test` during install.
```bash
# One live vendor
OLLAMA_API_KEY=… cargo test --test live ollama_live -- --ignored --nocapture
```
## Lint and format
```bash
cargo fmt --all
cargo clippy --all-targets --locked -- -D warnings
```
CI denies warnings (`-D warnings`) and checks formatting. rustfmt is the
style; do not hand-format around it.
## Configuration while developing
Copy [config.example.toml](config.example.toml) and enable only the vendors
you have credentials for. Opt-in vendors (DeepSeek, Ollama Cloud, Kimi, …)
default to `enabled = false` and never fetch until flipped on.
```toml
[ollama]
enabled = true
api_key_env = "OLLAMA_API_KEY"
plan = "pro"
```
Putting an inline `api_key` in any section requires `chmod 600` on the file.
Environment variables are the safer default.
See [docs/configuration.md](docs/configuration.md) for the full reference.
## Ollama Cloud
Native provider — **not** a `[[custom]]` table. Bearer token from
https://ollama.com/settings/keys against `GET https://ollama.com/api/usage`.
Copy the `[ollama]` section from [config.example.toml](config.example.toml)
into your config and set `enabled = true`:
```toml
[ollama]
enabled = true
api_key_env = "OLLAMA_API_KEY"
plan = "pro"
```
```bash
export OLLAMA_API_KEY="…" # minted at ollama.com/settings/keys
./target/release/ai-usagebar --vendor ollama
./target/release/ai-usagebar-tui
```
The local daemon at `127.0.0.1:11434` has **no** quota route. The Ed25519
CLI key in `~/.ollama/id_ed25519` is a registry credential and is refused by
`/api/usage` with 401.
Full walkthrough: [docs/ollama-setup.md](docs/ollama-setup.md).
## Windows
MSVC + NASM are required to compile. After `cargo build --release`:
```powershell
.\target\release\ai-usagebar.exe --json
.\target\release\ai-usagebar-tui.exe
```
There is no `make` on a stock PowerShell. Run the cargo commands directly.
Details, PATH, and tray install: [docs/windows-build.md](docs/windows-build.md).
## Layout
| `src/` | Library + vendor modules (`src/ollama/`, `src/zai/`, …) |
| `src/bin/` | `ai-usagebar`, `ai-usagebar-tui`, `ai-usagebar-tray` |
| `src/tui/` | TUI app, panels, Settings overlay |
| `src/widget/` | Waybar / CLI renderer |
| `tests/` | Integration, live smoke, fixtures |
| `gnome-extension/`, `kde-plasmoid/`, `omarchy/`, `macos/`, `windows/` | Native frontends |
| `docs/` | Configuration, placeholders, vendor endpoints |
| `.github/workflows/` | `ci.yml` (fmt, clippy, tests, MSRV 1.90, Nix) and `release.yml` |
Adding a vendor is an exhaustive-match exercise: `VendorId`, `VendorSnapshot`,
`VendorId::all()`, config section, catalog, detect, widget CLI, TUI fetch,
Settings `KEY_VENDORS` (if API-key), placeholders, changelog. Missing
`VendorId::all()` is how a provider shows up in Settings and nowhere else.
## See also
- [CONTRIBUTING.md](CONTRIBUTING.md) — pre-PR gate and how to add a provider
- [CLAUDE.md](CLAUDE.md) — release checklist and invariants
- [docs/configuration.md](docs/configuration.md) — config reference
- [docs/vendor-endpoints.md](docs/vendor-endpoints.md) — endpoint matrix
- [docs/windows-build.md](docs/windows-build.md) — Windows toolchain
- [docs/ollama-setup.md](docs/ollama-setup.md) — Ollama Cloud