watfile
Classify files with a decision-making AI model and sort them into category folders.
watfile sends each document's text (title/abstract-grade extract) to a
TypeSafe AI Jev (System One) Choice
question, gets back a typed answer with a selected category, per-category
probabilities and confidence, then moves the file into the matching folder.
A Classifier abstraction keeps the backend pluggable — local MLX (laya) and
other backends slot in later.
Install
Requires Python 3.12+ and uv.
From PyPI (once published)
# one-off run, no install
uvx watfile --help
# persistent CLI on your PATH
uv tool install watfile
watfile --help
From source
git clone <repo> && cd watfile
uv sync # create venv + install deps (typesafe-sdk, liteparse)
uv run watfile --help
# or install the local checkout as a tool
uv tool install --from . watfile
Configuration
watfile resolves its TypeSafe API key (create one at https://console.typesafe.ai/) with this precedence — first match wins:
TYPESAFE_API_KEYenvironment variable.envfile in the current directory (gitignored;TYPESAFE_API_KEY=...)~/.config/watfile/config.toml(api_key = "...", alsobase_url,model;$WATFILE_CONFIGor$XDG_CONFIG_HOMEcan relocate it)
export TYPESAFE_API_KEY=... # option 1
echo 'TYPESAFE_API_KEY=...' > .env # option 2
cat > ~/.config/watfile/config.toml <<'EOF' # option 3
api_key = "..."
EOF
Usage
Point watfile at files or folders, and either give a comma-separated category
list (-c) or a target folder whose subfolders are the categories (-d):
# explicit categories, files moved into ./sorted/<category>/
uv run watfile ~/Downloads/invoice.pdf -c invoice,donation,apartment
# folder input, recursive; categories = existing subfolders of -d
mkdir -p ~/docs/{invoice,donation,apartment}
uv run watfile ~/Downloads -r -d ~/docs
# preview without touching anything
uv run watfile ~/Downloads -r -d ~/docs -n
# actually move the files (default is symlinking into the category folders)
uv run watfile ~/Downloads -r -d ~/docs -m
# copy instead
uv run watfile ~/Downloads -r -d ~/docs --copy
# custom output root with -c
uv run watfile *.pdf -c computerscience,biology -o ~/sorted
Output per file:
bill.pdf: invoice (conf 0.94) -> symlink to ~/docs/invoice/bill.pdf
Files that can't be classified (unsupported extension, no extractable text) are
skipped with a warning; name collisions get a _1, _2… suffix.
Supported inputs
- Text formats (read directly):
.txt .md .markdown .rst .log .csv .json - PDF (via liteparse): only the first 2 pages are parsed, OCR disabled — enough for classification, ~1000x faster than a full parse. Scanned/image-only PDFs are skipped.
Options
usage: watfile [-h] [-r] (-c CATEGORIES | -d DIRECTORY) [-o OUTPUT]
[--backend {jev,laya}] [-n] [--copy]
inputs [inputs ...]
positional arguments:
inputs files and/or folders to process
options:
-h, --help show this help message and exit
-r, --recursive recurse into folder inputs
-c CATEGORIES, --categories CATEGORIES
comma-separated categories, e.g. invoice,donation,apartment
-d DIRECTORY, --directory
target folder whose existing subfolders are the categories
-o OUTPUT, --output output root for sorted files (default: same as -d, or ./sorted with -c)
--backend {jev,laya} classifier backend (default: jev)
-n, --dry-run print decisions without placing files
-m, --move move files into the category folder (default: symlink)
--copy copy files instead of symlinking
--symlink create symlinks in category folders (default)
Development
uv sync
uv run pytest # unit tests; live API tests skip without TYPESAFE_API_KEY
tests/fixture/ contains 4 real arXiv PDFs with ground-truth categories
(derived from their arXiv subject tags) used by the integration tests.
Roadmap
layalocal backend (MLX via OpenAI-compatible HTTP)- batching: classify 25/50/100 files in a single API call
Release files for watfile 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| watfile-0.1.0.tar.gz | 8.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| watfile-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 19.1 kB
Release files / watfile-0.1.0.tar.gz
| Download URL | watfile-0.1.0.tar.gz |
|---|---|
| Size | 8.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6a0b6474cf0869d3b081111417ea4478484174ff7c4be3af1aa2043b2f9049a8
|
|
BLAKE2b-256 checksum How to use checksums |
3dc4eb6ac9c4ca51d24d6083017b0e9b3093c998f61ec19eb8bdcfd4878d1bf3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.
Transparency logRelease files / watfile-0.1.0-py3-none-any.whl
| Download URL | watfile-0.1.0-py3-none-any.whl |
|---|---|
| Size | 11.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
54ee3fdf3fac891088ff4963276a80c70d6b066c6367af47d30ac64f4043cfe5
|
|
BLAKE2b-256 checksum How to use checksums |
847772bb1838af57c1c6c45a999a571e3fda62b4459fcf7a85fa6aa475a32b3f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.
Transparency log