# sjaur Fast, fuzzy **TUI search for the [Arch User Repository](https://aur.archlinux.org) and the official Arch Linux repositories** — with real package installation, built on the official AUR metadata dump and your local pacman sync databases. ``` ┌ Search: firfox ────────────────────────────────────────────────────────┐ ├──────────────────────────────────────┬─────────────────────────────────┤ │ firefox 130.0-1 +3500 │ Name firefox │ │ firefox-nightly 131.0b1 +120 │ Version 130.0-1 │ │ firefox-wayland 129.0 +90 │ Maintainer jonas │ │ … │ Votes 3500 │ │ │ Popularity 214.53 │ │ │ … │ ├──────────────────────────────────────┴─────────────────────────────────┤ │ 9 results · 24 ms · cache 2.1 hours old │ └────────────────────────────────────────────────────────────────────────┘ ``` Typo-tolerant ranking across name, description, maintainer, keywords, provides, and dependency fields — all served from a local snapshot, so searching never touches the network. ## Install ```bash cd sjaur python -m venv .venv .venv/bin/pip install . ``` Then link the executable onto your `PATH` (or copy it): ```bash sudo ln -s "$(pwd)/.venv/bin/sjaur" /usr/local/bin/sjaur # or, without root: mkdir -p ~/.local/bin && ln -s "$(pwd)/.venv/bin/sjaur" ~/.local/bin/sjaur ``` Requirements: Python ≥ 3.10 and Linux (deps: `rapidfuzz`, `textual`). ## Usage ```bash sjaur # launch the TUI sjaur --update # force a metadata refresh, then exit sjaur --version sjaur --help ``` Optional: - `--cache-dir ` overrides the cache location (default `~/.cache/sjaur`) - `--no-repos` searches the AUR only (skip official repository packages) - `--repos-dir ` overrides the pacman sync database directory (default `/var/lib/pacman/sync`) ## Official repository packages By default, sjaur merges the packages of your **local pacman sync databases** (`core`, `extra`, …) into the search index. They appear with a repository badge (`firefox [extra]`), can be installed directly via `sudo pacman -S ` (`i` in the TUI — no build step, pacman asks for confirmation), and work with all search features including `@maintainer` (the packager email is used, e.g. `@heftig`). Votes, popularity, and out-of-date status are AUR-only concepts and are hidden for repo packages. The databases are read-only and stay where pacman manages them; run `pacman -Sy` as usual to refresh them. On systems without sync databases, sjaur falls back to AUR-only search with a warning. ## Search syntax | Query | Meaning | | --------------------------- | ---------------------------------------------------------------- | | `firefox` | fuzzy search across all package fields | | `@max_musterman` | fuzzy-search the **maintainer** field | | `@max_musterman firefox` | maintainer search combined with a normal search | | `@john python editor` | maintainer query is `john`; `python editor` is the normal query | Rules: - Only the **first whitespace-delimited word** after `@` is the maintainer query; everything after it is the normal search. - Matching stays fuzzy: `@max_musterman` also finds `max_mustermann`, `max-musterman` or `max_musterman123` — no exact match required. - With `@`, the result score is `maintainer_similarity × 0.65 + package_score × 0.35`, so maintainer matches dominate while the text query still orders the results. - The parser is table-driven, so future operators (e.g. `#keyword`, `depends:python`) can be added without touching the search engine. ### Live search Results update **while you type** — no Enter needed: 1. On every keystroke the current list is instantly narrowed client-side (packages that no longer match disappear immediately). 2. After a short pause (0.18 s), the full fuzzy search over all fields runs in the background and refines the list. Editing or retyping a recent query restores its result set instantly from an in-memory cache; the status line shows `live` while you are typing and the measured search time once the background search has finished. ## Keys | Key | Action | | ---------------------- | ----------------------------------------- | | typing | **live search** — results update on every keystroke | | `↑` / `↓` | navigate results | | `enter` | open package details (`esc` returns) | | `i` / `ctrl+i` | install the selected package | | `tab` / `shift+tab` | switch between search field and results | | `page up` / `page down`, `home` / `end` | scroll / jump | | `ctrl+r` | reload AUR metadata now | | `f1` | help overlay | | `esc` | clear search / leave current view | | `ctrl+c` | quit | ## Installing packages With a package highlighted, press `i` (or `ctrl+i`) and confirm with `y`. sjaur then runs the classic AUR workflow with plain system tools: 1. **Clone** the package repository from `https://aur.archlinux.org/.git` into `~/.cache/sjaur/build/` (an existing clone is **reused**; confirm with `u` instead of `y` to hard-reset and update it first). 2. **Review** the PKGBUILD — it is printed to the terminal, and you can open it in `$EDITOR` before deciding. **Always read it**: the AUR ships user-produced build scripts. 3. **Build & install** via `makepkg --syncdeps --install`. pacman's confirmation, the sudo password prompt, and the whole build output appear directly in your terminal; the TUI suspends and resumes afterwards. The result is reported in the terminal; press Enter to return to sjaur. If anything fails (clone error, dependency problem, aborted build), the message explains what happened and the TUI keeps running. > **Security note:** AUR packages are user-produced content. sjaur shows the > PKGBUILD (and any bundled install scripts) before building — never install > packages whose build steps you do not understand. ## Package details Selecting a package shows everything the local snapshot knows — name, version, description, maintainer, votes, popularity, last modified, out-of-date status, upstream URL, AUR page, licenses, groups, keywords, depends, makedepends, checkdepends, optdepends, provides, conflicts, replaces — without any additional network request. ## Cache behavior `sjaur` keeps a decompressed copy of the official extended metadata dump (`https://aur.archlinux.org/packages-meta-ext-v1.json.gz`) in: ``` ~/.cache/sjaur/packages-meta-ext-v1.json ``` - At startup the cache's **modification time** is checked; if it is younger than **24 hours** it is used as-is, otherwise a fresh dump is downloaded. - The replacement is **atomic**: the dump is downloaded to a temporary file, decompressed, validated as JSON, and only then swapped in with `os.replace`. A failed download never destroys a working cache. - If the download fails but a (stale) cache exists, the old cache is used and a warning is shown. If there is no usable cache at all, `sjaur` exits with a clear error. - `SJAUR_CACHE_DIR` overrides the cache directory (XDG `XDG_CACHE_HOME` is respected). ## Development ```bash python -m venv .venv .venv/bin/pip install -e '.[dev]' .venv/bin/pytest # run the test suite .venv/bin/mypy src # type-check ``` Project layout: ``` sjaur/ ├── src/sjaur/ │ ├── cli.py argument parsing and startup flow │ ├── cache.py download / decompress / validate / atomic replace │ ├── metadata.py typed Package model, tolerant dump parsing │ ├── repodb.py local pacman sync database reader │ ├── query.py extensible query parser (@maintainer, …) │ ├── search.py fuzzy scoring and ranking engine │ ├── installer.py clone / review / makepkg / pacman install workflow │ ├── config.py paths and constants │ ├── errors.py user-facing error types │ └── tui/ Textual application, detail view └── tests/ ``` ## License MIT — see [LICENSE](LICENSE).