Scan it. Drop it. Done.
ScanSort is a set-and-forget Windows desktop app that watches your scanner's output folder, automatically classifies each document using Google Gemini AI, and files it into the right place in your existing Documents folder β correctly named, right-side up, and searchable.
flowchart LR
A["π Scan a document"] --> B["π Appears in Drop Folder"]
B --> C["π€ AI reads & classifies"]
C --> D["βοΈ Renamed & tagged"]
D --> E["ποΈ Filed to correct folder"]
You scan a document. ScanSort picks it up, waits for the scanner to finish writing, sends it to Gemini AI for classification, renames it with a clean date-stamped title, embeds searchable metadata, fixes the page orientation if needed, and moves it to the deepest matching subfolder in your Documents directory. If it can't confidently classify something, it places it in _Review_Needed for you to handle β it never invents folders or loses files.
- π Reads your documents β Gemini AI examines the full content of each scan (text, logos, layouts) and picks the best folder from your existing directory structure.
- π³ Uses your folder structure β ScanSort discovers your real Documents hierarchy and matches scans to the deepest, most specific subfolder. No setup or folder configuration required.
βοΈ Event & trip awareness β Folders like2026 Sydney MarathonorTokyo Trip 2025are recognised automatically. ScanSort cross-references document dates and locations against event timing to route related receipts, tickets, and invoices correctly.- π‘ Keyword hints β Optionally provide a
folder_hints.jsonfile to help the AI with ambiguous folder names (see Folder Hints below). - π§ Self-improving memory (RAG) β Every filing teaches ScanSort: past filings are retrieved as few-shot examples for the next classification, so repeat bills file correctly after a single correction. Fully local (no new cloud calls, nothing leaves your machine); disable anytime with
scansort config --rag-memory disableor the Settings dialog toggle. - π― Confidence gating β Documents below 70% classification confidence go to
_Review_Neededinstead of being misfiled.
ScanSort remembers your past filings on this computer and uses them as examples for the next classification. Nothing leaves your machine β no new cloud calls, no uploads.
scansort memory # overview: status, examples learned, calibration
scansort memory --list -n 20 # newest learned examples
scansort memory --forget-folder "Finances/Utilities" # forget one folder
scansort memory --rebuild # re-index filing history
scansort memory --clear # forget everything (re-learns from history unless memory is off)Manage it from the tray (Filing Memory...) or Settings β Filing Memory (live count + Clear Memory...). Clearing only deletes the learned index β ScanSort re-learns from filing history on the next scan unless memory is turned off.
- π Triage unfiled scans β Documents placed in
_Review_Needed(low confidence or missing folder) can be reviewed and filed interactively through a visual desktop dialog or in your terminal (scansort review). - π‘ AI routing suggestions β ScanSort preserves what Gemini understood from the document (document type, summary, and suggested folder) so you can accept suggestions with a single click.
- π§ Self-learning feedback loop β When you choose or refine a destination folder, you can optionally teach ScanSort a new keyword hint. It automatically updates
folder_hints.json, ensuring future scans match that folder with high confidence. - β©οΈ Corrections stick β Undoing a filing removes its bad example from memory, and folders with a history of corrections require higher confidence before auto-filing again.
- π§ Honest memory feedback β Filing a reviewed document re-indexes the corrected folder; the CLI and tray confirm only when it was actually learned, and tell you when no keyword hint was saved instead of implying otherwise.
- π±οΈ System tray counter β The system tray menu dynamically indicates pending reviews (e.g.
Review Needed (3)...), allowing 1-click access to triage scans.
- π² System tray app β Runs quietly in your notification area. Pause/resume monitoring, undo moves, browse your folder taxonomy, open settings, and check for updates β all from the tray icon.
- π±οΈ Windows Explorer context menu β Right-click any PDF or image in Windows Explorer
$\rightarrow$ "File with ScanSort" to file digital downloads (Amazon invoices, flight tickets, emailed statements) directly without moving them into a scanner folder. - β‘ Direct CLI filing β Run
scansort file <path>to process documents in-place or preserve originals with--copy. - π― Drop Zone & Quick Filer β Access a minimalist desktop Drop Zone from the system tray or settings window to drag, paste, or select documents for instant filing.
- βοΈ Settings dialog β A visual settings window to configure folders, pick your Gemini model, manage your API key securely, toggle auto-start and Explorer context menu, and explore your folder tree with a built-in folder picker. Changes apply instantly to the running watcher.
- π Windows notifications β Native toast notifications tell you when a document is filed (click to open the folder), when something fails (with a "View Logs" button), or when an update is available.
- π Auto-start on login β Optionally launches at boot via Windows Registry so your scans are always filed, even if you forget to open the app.
- π‘οΈ Never loses files β All moves are atomic with automatic collision resolution. If
260901_Electricity_Bill.pdfalready exists, ScanSort creates260901_Electricity_Bill_1.pdf. - π Duplicate detection β SHA-256 hashing catches re-scans before they hit the AI, saving API quota and avoiding duplicates.
- β©οΈ Undo support β Misplaced a document? Undo from the tray menu or command line. Run it multiple times to roll back successive filings.
- β³ Scan stability β Waits for your scanner to finish writing before processing, so multi-page and slow scans are never partially filed.
- π₯ Catches up on startup β Files that arrived while the app was closed are automatically processed when monitoring starts.
- π Secure API key storage β Your Gemini key is stored in the OS credential vault (Windows Credential Manager), never in a config file.
- π Standardised filenames β Every document becomes
YYMMDD_Description.pdf(e.g.260901_Origin_Energy_Electricity_Bill.pdf). - π Windows Search indexing β Embeds title, summary, and keywords as PDF metadata so documents appear in Windows Start Menu and Explorer searches.
- π€ Searchable OCR text layer β Optionally embed an invisible text layer (
--ocr enable) so every page is Ctrl+F-able, not just metadata-tagged. The standalone Windows build bundles Tesseract, so no extra installation is needed. - ποΈ OCR backfill for existing archives β Retrofit text layers into already-filed PDFs in place with
scansort ocr-backfill, preserving DocInfo and XMP metadata. Right-click any PDF in Explorer and choose "Make searchable with ScanSort". - π Auto page orientation β Corrects sideways and upside-down pages automatically.
- πΌοΈ Image support β JPEGs, PNGs, and multi-page TIFFs are converted to searchable PDFs before filing.
flowchart TD
A["New file in Drop Folder"] --> B{"Supported format?"}
B -- No --> C["Ignored"]
B -- Yes --> D["Wait for write stability"]
D --> E["Compute SHA-256 hash"]
E --> F{"Duplicate?"}
F -- Yes --> G["Route to _Review_Needed/Duplicates"]
F -- No --> H{"Image file?"}
H -- Yes --> I["Convert to PDF"]
H -- No --> J["Use original PDF"]
I --> K["Stage in app temp dir"]
J --> K
K --> K2{"OCR enabled?"}
K2 -- Yes --> K3["Embed invisible OCR text layer"]
K2 -- No --> L
K3 --> L["Send to Gemini AI"]
L --> M["Fix orientation Β· Embed metadata"]
M --> N["Move to destination folder"]
N --> O["Log to audit history Β· Notify"]
- A Google Gemini API key β get one free at Google AI Studio
For the standalone Windows build (ScanSort.exe), that's all you need β no Python required.
For running from source:
- Python 3.14+
- Astral
uv - (Optional) Tesseract OCR β only needed for OCR text layers when running from source; the standalone Windows build bundles it.
git clone https://github.com/stephenwong/scansort.git
cd scansort
uv syncOption A β GUI (recommended):
uv run scansort watchThis starts ScanSort with the system tray icon. Right-click it and choose Settings... to set your folders and API key.
Option B β Command line:
# Store your API key securely
uv run scansort config --set-key AIzaSyYourActualKeyHere
# Set your scanner drop folder and documents root
uv run scansort config --watch-folder "C:\Scans\Inbox"
uv run scansort config --documents-folder "D:\My Documents"
# Start monitoring
uv run scansort watchAll settings can be changed via the Settings dialog (tray β Settings...) or via the CLI. Configuration is stored in %APPDATA%\ScanSort\config.json.
| Setting | CLI Flag | Description | Default |
|---|---|---|---|
| Watch folder | --watch-folder |
Scanner output directory to monitor | %USERPROFILE%\Scans\Inbox |
| Documents folder | --documents-folder |
Root of your filing destination | %USERPROFILE%\Documents |
| Gemini model | --gemini-model |
AI model for classification | gemini-3.1-flash-lite |
| API key | --set-key |
Stored in OS credential vault | β |
| Auto-start | --autostart enable/disable |
Launch on login | Disabled |
| Context menu | --context-menu enable/disable |
Windows Explorer right-click integration | Disabled |
| OCR text layer | --ocr enable/disable |
Embed a searchable text layer in new scans | Disabled |
| Filing memory | --rag-memory enable/disable |
Retrieve past filings as classification examples (Settings β Filing Memory) | Enabled |
| OCR context menu | --ocr-menu enable/disable |
Explorer right-click "Make searchable with ScanSort" (PDF only) | Disabled |
| Dry-run | --dry-run enable/disable |
Preview without moving files | Disabled |
| Auto-update | --auto-update enable/disable |
Check for updates on launch | Enabled |
| Max folder depth | --max-depth |
How deep to scan taxonomy (1β10) | 10 |
| Fallback folder | --fallback-folder |
Where unclassified docs go | _Review_Needed |
| Mirror CSV | --mirror-csv enable/disable |
Copy audit CSV to Documents | Disabled |
View current settings: uv run scansort config --show
| Command | What it does |
|---|---|
scansort watch |
Start monitoring with system tray |
scansort watch --dry-run |
Preview classifications without moving files |
scansort watch --minimized |
Start without banner output |
scansort file <path...> |
File one or more digital documents directly |
scansort file <path...> --copy |
File documents while preserving original source files |
scansort config --show |
View current configuration |
scansort config --set-key <KEY> |
Store API key securely |
scansort config --context-menu enable |
Enable Explorer right-click "File with ScanSort" |
scansort config --ocr enable |
Embed searchable OCR text layers in new scans |
scansort config --ocr-menu enable |
Enable Explorer right-click "Make searchable with ScanSort" |
scansort ocr-backfill |
Retrofit text layers into all filed PDFs (in place) |
scansort ocr-backfill --dry-run |
List PDFs that would be made searchable |
scansort ocr-backfill --limit 25 |
Backfill at most 25 PDFs this run |
scansort ocr-backfill path\to\file.pdf |
Make a single PDF searchable (used by the right-click menu) |
scansort review |
Interactively review & file documents in _Review_Needed |
scansort review --gui |
Open the graphical review dialog |
scansort review --cli |
Review unfiled scans in the terminal |
scansort review --limit 10 |
Limit review session to first 10 documents |
scansort undo |
Reverse the last filing (repeatable) |
scansort --dry-run undo |
Preview the reversal without moving files |
scansort rescan |
Refresh & display folder taxonomy |
scansort rescan --doctor |
Analyze folder health (near-duplicates, ghosts, catch-alls, review themes) |
scansort rescan --doctor --json |
Machine-readable Taxonomy Doctor report |
scansort history |
View recent filing history |
scansort history -q "electricity" |
Search filing history |
scansort stats |
View filing totals & API cost summary |
scansort memory |
Inspect and control filing memory (--list, --forget-folder, --clear, --rebuild) |
scansort logs |
View recent log entries |
scansort logs -f |
Stream logs in real-time |
scansort check-update |
Check for new versions |
scansort help <command> |
Help for any subcommand |
scansort --verbose watch |
Enable debug-level logging |
Notes:
scansort review --limit Napplies to the--clisession; the GUI always shows the full queue.scansort historyandscansort statsexit non-zero when the audit log cannot be read.scansort config --setmust be used alone (it is rejected when combined with other mutation flags), and unrecognisedconfig.jsonkeys are logged and ignored. Manually filing a document from the review queue re-checks its SHA-256 against history and asks you to confirm before filing content that has already been filed; declining moves it to_Review_Needed/Duplicates/. Filter reviewed filings withscansort history --status REVIEWED.scansort rescan --doctoris read-only: it reports near-duplicate folders, ghost folders (no recent filings), over-broad catch-alls, and recurring review themes. Apply suggestions from Settings β Taxonomy Doctor Findings (or the tray's "Taxonomy Doctor..." item) with 1-click Approve; nothing is created, merged, or deleted without your confirmation, and every change stays strictly inside your Documents root. Approving a proposed new folder also saves its detected keywords as classification hints for future scans.
Tip: The packaged
ScanSort.exeworks the same way β just replacescansortwithScanSort.exein the commands above. When launched from a terminal, CLI output appears there; when launched by double-click or auto-start, it runs silently in the tray.
If some of your folder names are ambiguous, you can help the AI with a folder_hints.json file in %APPDATA%\ScanSort\ (or ~/.config/scansort/ on Linux):
{
"Finances/Utilities/Electricity": ["Origin Energy", "AGL", "power bill", "kWh"],
"Medical/Dental": ["Bupa Dental", "cleaning", "orthodontics"],
"Taxes/2026": ["ATO", "group certificate", "PAYG", "tax return"]
}These keywords are injected into the AI classification prompt to improve accuracy. You can edit this file manually, or let ScanSort learn them automatically: whenever you file a document using the Review Dialog (scansort review), you can teach ScanSort a new keyword hint in one step.
Event and trip folders (conferences, vacations, marathons) generally don't need hints β ScanSort recognises them automatically from dates and context.
All documents are renamed to a clean, consistent format:
YYMMDD_Description.pdf
- Date β extracted from the document content (statement date, invoice date, etc.). Falls back to today's date if none is found.
- Description β English summary in
Title_Case_With_Underscores, max 60 characters. - Collisions β if the name already exists, a counter is appended:
_1,_2, etc.
Example: 260901_Origin_Energy_Electricity_Bill.pdf
Standalone Windows builds check for updates from GitHub Releases on each launch. When a new version is found, it downloads, verifies, and installs automatically with a rollback-safe swap β no admin rights needed. A toast notification lets you know when an update is applied.
Disable with scansort config --auto-update disable, or check manually with scansort check-update.
ScanSort enforces β₯95% test coverage:
# Lint
uv run ruff check .
# Format check
uv run ruff format --check .
# Test suite with coverage
uv run pytestscansort/
βββ scansort/ # Core Python package
β βββ classification/ # Gemini AI client, taxonomy scanner, prompt hints
β βββ cli/ # CLI subcommands and argument parser
β βββ core/ # Configuration, constants, filesystem utilities
β βββ document/ # PDF conversion, metadata embedding, orientation
β βββ logging/ # Audit logs, cost tracking, diagnostics
β βββ pipeline/ # Watcher, stabiliser, hasher, review queue, dispatcher, worker
β βββ platform/ # OS integrations (autorun, credentials, toasts)
β βββ ui/ # System tray, review dialog, settings dialog, icon generator
β βββ updater/ # GitHub Releases self-update engine
βββ tests/ # Pytest test suite (mirrors package structure)
βββ working-docs/ # PRD and working documentation
βββ pyproject.toml # Project config (uv, ruff, pytest-cov)
βββ scansort.spec # PyInstaller build spec
| Library | Role |
|---|---|
| google-genai | Multimodal Gemini AI client for document classification |
| watchfiles | Rust-powered filesystem watcher (wraps notify crate) |
| pypdf | PDF page rotation and metadata embedding |
| img2pdf / Pillow | Lossless image-to-PDF conversion |
| pystray | Cross-platform system tray integration |
| pydantic | Configuration validation and structured AI response models |
| keyring | OS credential vault for API key storage |
| windows-toasts | Native Windows toast notifications (optional) |
- Test-first β write failing tests before production code. Every test must validate real behaviour, not inflate coverage.
- β₯95% coverage enforced β
pytestfails the build below this threshold. - Ruff for linting and formatting β
ruff check .andruff format --check .must pass. - Secrets never in plaintext β API keys go through
keyring, are masked in output (mask_api_key()), and redacted from logs (redact_secrets_from_text()). - Atomic file operations β all moves use advisory locks and collision resolution. No partial writes to the drop folder.
See AGENTS.md for detailed architectural invariants, development rules, and contribution guidelines.
