Metadata-Version: 2.4
Name: watfile
Version: 0.1.0
Summary: Classify documents with the TypeSafe Jev decision model and sort them into folders
Keywords: classification,files,llm,typesafe,cli
Author: Michael Hunger
Author-email: Michael Hunger <github@jexp.de>
License-Expression: MIT
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Utilities
Requires-Dist: liteparse>=2.14.6
Requires-Dist: typesafe-sdk>=0.7.0
Requires-Python: >=3.12
Project-URL: Repository, https://github.com/jexp/watfile
Description-Content-Type: text/markdown

# 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](https://docs.typesafe.ai/primitives/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](https://docs.astral.sh/uv/).

### From PyPI (once published)

```sh
# one-off run, no install
uvx watfile --help

# persistent CLI on your PATH
uv tool install watfile
watfile --help
```

### From source

```sh
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:

1. `TYPESAFE_API_KEY` environment variable
2. `.env` file in the current directory (gitignored; `TYPESAFE_API_KEY=...`)
3. `~/.config/watfile/config.toml` (`api_key = "..."`, also `base_url`, `model`;
   `$WATFILE_CONFIG` or `$XDG_CONFIG_HOME` can relocate it)

```sh
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`):

```sh
# 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](https://github.com/run-llama/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

```sh
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

- `laya` local backend (MLX via OpenAI-compatible HTTP)
- batching: classify 25/50/100 files in a single API call
