soap — a reference manager (CLI + TUI)
Project description
🧼 soap
A terminal reference manager for papers, books, and PDFs — add a source, fetch its metadata, review what's uncertain, and browse it all from a keyboard-driven TUI.
Point soap at a local file, DOI, arXiv ID, ISBN, directory, or URL. It resolves the metadata (and, for links, best-effort downloads the PDF), queues anything it is unsure about for a quick review, and keeps everything in a plain, readable, version-controllable library on disk. From there you can browse, search, tag, and open it — all without leaving the terminal.
Overview
Keeping a reference library usually means running a heavyweight desktop
application or maintaining a folder of PDFs with names like
paper (3) final_v2.pdf. soap is neither. It is a fast, keyboard-first TUI
backed by a library in which every record is a plain info.yaml file on
disk — so you can read it, diff it, and check it into git.
soap:
- accepts any source — a local file, a whole directory, a DOI, a bare arXiv ID, an ISBN, or a URL;
- fetches metadata automatically from Crossref, arXiv, or Open Library, and best-effort downloads the PDF for arXiv and direct-PDF links (and open-access DOIs);
- queues uncertain records so you can accept, correct, or skip them in a quick review pass rather than trust a bad guess;
- and lets you browse, search, tag, and open the whole library from a TUI — or drive the same library from the CLI.
The on-disk record is the source of truth; the SQLite index is only a fast, rebuildable view. soap never parses the contents of your PDFs.
Installation
Homebrew (recommended — no Python required):
brew install GhifariArsa/soap/soap-tui
This installs a self-contained binary (embedded CPython 3.14 via PyApp), so no
Python or pip is needed. Coverage is Apple-Silicon macOS and Linux
(arm64 / x86_64); there is no Intel-macOS binary, so brew install on an Intel
mac fails fast with a clear message. Upgrade with brew upgrade soap-tui. The
tap and its Intel-mac note live at
GhifariArsa/homebrew-soap.
Standalone binary (macOS arm64, Linux arm64 / x86_64) — the installer
verifies the download before placing soap in ~/.local/bin:
curl -fsSL https://raw.githubusercontent.com/GhifariArsa/soap/main/install.sh | sh
Set SOAP_VERSION=v0.1.0 to pin a release or SOAP_INSTALL_DIR to change the
install directory. The installer requires a published release.
From PyPI (requires uv and Python 3.14+):
uv tool install soap-tui
From a checkout — the simplest way to try it:
git clone https://github.com/GhifariArsa/soap.git
cd soap
uv run soap init
The distribution is named soap-tui (plain soap was taken on PyPI),
but the installed command is always soap.
Then set up your library once:
soap init
init creates the library, its SQLite index, and a shell export for SOAP_DIR.
The default library is ~/.soap; SOAP_DIR changes the default, and `--path
| Option | Description |
|---|---|
--path <dir> |
Initialize a different library. |
--shell auto|zsh|bash|fish |
Choose the shell config to update. |
--force |
Reinitialize an existing library; destructive, but backs up the old database. |
Usage
The core workflow is straightforward: add a source, review it, then run
soap to browse.
From a checkout, prefix commands with uv run; an installed copy uses soap
directly.
# An arXiv ID resolves metadata and best-effort downloads its PDF.
soap add 1706.03762
# A fresh `soap init` routes adds through the review queue.
soap inbox review
# Then browse the library.
soap
For a local PDF, supply an identifier or the metadata yourself:
soap add ~/papers/paper.pdf --doi 10.1145/3292500.3330701
# Or work completely offline:
soap add ~/papers/paper.pdf --no-fetch \
--title "Attention Is All You Need" \
--author "Vaswani, Ashish" --year 2017
SOURCE can be a local file, directory, URL, DOI, or bare arXiv ID; ISBN
metadata comes from --isbn, and identifiers can be passed explicitly with
--doi or --arxiv. The most commonly used options:
| Option | Description |
|---|---|
--title, --author, --year, --type |
Override metadata. --author is repeatable. |
--tag, --collection |
Add repeatable tags or collections. |
--no-fetch |
Skip network metadata lookups. |
--recursive |
Include files below a directory source. |
--confirm |
Correct the core fields inline before saving. |
--edit, -e |
Edit the generated info.yaml in $EDITOR. |
--dry-run |
Preview the add without writing anything. |
--force |
Add even when a duplicate is detected. |
--path <dir> |
Use a library other than $SOAP_DIR or ~/.soap. |
Run soap add --help and soap inbox review --help to list every option.
Reviewing the inbox
soap inbox review presents one needs_review record at a time:
a— accept it as-isc— correct title, authors, year, type, or venue; Enter keeps a valuee— open the completeinfo.yamlin$EDITORs— skip it for laterd— delete it and its attached files, after confirmationq— quit the walk
The TUI review screen shares the same review core: enter/a files, c
corrects, e opens $EDITOR, s skips, and q/esc finishes. soap add --confirm provides the same guided field correction during an add.
Keybindings
Run soap with no subcommand to open the TUI. Press ? at any time for the
in-app reference; the compact map for the main screen is below.
j / k · g / G move · jump to top / bottom
Ctrl-D / Ctrl-U half-page down / up
Tab / Shift-Tab cycle panes h / l focus left / right
enter / o open the selected file or URL
/ search title, author, tag, or DOI (Enter/Tab → list)
E edit the core fields (title/authors/year/type/venue) in an in-app form
e edit the complete `info.yaml` in $EDITOR (full power option)
d delete the selected document and its files (asks to confirm)
t edit tags (Enter/comma adds · Tab completes · Ctrl-S saves · Esc cancels)
m cycle read status: unread → reading → read
r review the inbox
Ctrl-R refresh from disk
? / Ctrl-P keyboard reference / command palette
Ctrl-T cycle themes
q quit
How the workflow fits together
- Initialize once.
soap initcreates the library, its SQLite index, and a shell export forSOAP_DIR. - Add sources.
soap addtakes a file, directory, DOI, arXiv ID, ISBN, or URL. Repeat--author,--tag, or--collectionas needed; use--recursivefor a directory. - Review.
soap inbox review, or the TUI'sraction, lets you accept, correct, edit, skip, or delete eachneeds_reviewrecord.--confirmfolds the same guided correction intoadd. - Browse. Run
soapwith no subcommand. The sidebar filters all documents, the review inbox, read status, tags, and collections;/searches. - Open and mark.
enter/oopens the first attached file (or the recorded URL) with the OS default handler.mcycles unread → reading → read.
Metadata lookups use Crossref, arXiv, or Open Library as appropriate. arXiv and direct-PDF URLs download a PDF on a best-effort basis, and an open-access DOI may too; a paywall or failed download still saves the metadata. soap does not parse PDF contents.
Configuration and data
The library path resolves in the following order:
--path <dir>where the option is available (init,add,inbox review)$SOAP_DIR~/.soap
Its important files are laid out like this:
$SOAP_DIR/
├── config.yaml
├── soap.db # rebuildable SQLite index
├── inbox/ # library directory created by init
└── documents/
└── <citekey>/
├── info.yaml # authoritative document record
└── paper.pdf # attached file(s), if any
info.yaml is the source of truth. Every change writes the document file
first, then synchronizes the SQLite index — the index is only a fast,
denormalized view of the files and metadata. The TUI and CLI therefore read and
mutate the same library, and the on-disk record stays readable and
version-controllable without the index.
The review inbox is a needs_review status, not a second copy of the
document: records and their attachments remain under documents/<citekey>/ until
they are filed, skipped, or deleted. A new citekey names both the document folder
and its info.yaml; correcting a record during review keeps that citekey, and
only a new add derives a fresh key.
Themes
Tags are edited from the selected document with t and double as sidebar
filters. The TUI ships with the aqua-slate (default), one-dark, and
catppuccin-mocha themes — Ctrl-T cycles them and the choice is saved in
config.yaml. User themes live in $SOAP_DIR/themes/.
See the theme format and the example theme to build your own.
Contributing
soap is a standard Python project managed with uv. Run the tests with:
uv run pytest
Issues and pull requests are welcome. The distribution is named soap-tui; the
installed command is soap.
License
MIT.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file soap_tui-0.2.41.tar.gz.
File metadata
- Download URL: soap_tui-0.2.41.tar.gz
- Upload date:
- Size: 156.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7c1ebda7226c24c0cc6794340f644dfdd84061148bc8f5d5ef03f541f30c1cb7
|
|
| MD5 |
c7d69642d879427f51debc51584cbc84
|
|
| BLAKE2b-256 |
796a237f7f59ca98d83e31c8878fd1e0221e21fa0be449c4402677d872245b3a
|
Provenance
The following attestation bundles were made for soap_tui-0.2.41.tar.gz:
Publisher:
release.yml on GhifariArsa/soap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
soap_tui-0.2.41.tar.gz -
Subject digest:
7c1ebda7226c24c0cc6794340f644dfdd84061148bc8f5d5ef03f541f30c1cb7 - Sigstore transparency entry: 2310757078
- Sigstore integration time:
-
Permalink:
GhifariArsa/soap@b6614457fb12911be11e86b7bfa6cf28bc71eef2 -
Branch / Tag:
refs/tags/v0.2.41 - Owner: https://github.com/GhifariArsa
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b6614457fb12911be11e86b7bfa6cf28bc71eef2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file soap_tui-0.2.41-py3-none-any.whl.
File metadata
- Download URL: soap_tui-0.2.41-py3-none-any.whl
- Upload date:
- Size: 93.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a78f11d4cc61f16d3c2637c55aa5f708fba5de2ceb6b9dedd94a6b4c77c142d
|
|
| MD5 |
02e1575f54f969618c79d1338c2660bb
|
|
| BLAKE2b-256 |
bcbb491badccba83956fb024cba13ba8c0a03f05680d087b2f55e17546a88c7c
|
Provenance
The following attestation bundles were made for soap_tui-0.2.41-py3-none-any.whl:
Publisher:
release.yml on GhifariArsa/soap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
soap_tui-0.2.41-py3-none-any.whl -
Subject digest:
7a78f11d4cc61f16d3c2637c55aa5f708fba5de2ceb6b9dedd94a6b4c77c142d - Sigstore transparency entry: 2310757090
- Sigstore integration time:
-
Permalink:
GhifariArsa/soap@b6614457fb12911be11e86b7bfa6cf28bc71eef2 -
Branch / Tag:
refs/tags/v0.2.41 - Owner: https://github.com/GhifariArsa
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b6614457fb12911be11e86b7bfa6cf28bc71eef2 -
Trigger Event:
push
-
Statement type: