Skip to content
stephenwongPublic

About

Intelligent automated desktop document filer powered by Gemini

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

ScanSort

CI GitHub Release Python Platform Powered by Gemini

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.


⚑ How It Works

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"]
Loading

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.


Features

πŸ€– Smart AI Classification

  • πŸ“– 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 like 2026 Sydney Marathon or Tokyo Trip 2025 are 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.json file 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 disable or the Settings dialog toggle.
  • 🎯 Confidence gating β€” Documents below 70% classification confidence go to _Review_Needed instead of being misfiled.

🧠 Filing memory

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.


πŸ—‚οΈ Interactive Review & Self-Learning

  • πŸ“‹ 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.

πŸ–₯️ Desktop Integration

  • πŸ”² 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.

System tray menu


πŸ”’ Safety & Reliability

  • πŸ›‘οΈ Never loses files β€” All moves are atomic with automatic collision resolution. If 260901_Electricity_Bill.pdf already exists, ScanSort creates 260901_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.

πŸ” Search & Organisation

  • πŸ“ 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.

πŸ”§ What Happens Under the Hood

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"]
Loading

πŸš€ Getting Started

Prerequisites

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.

Install from Source

git clone https://github.com/stephenwong/scansort.git
cd scansort
uv sync

First Run

Option A β€” GUI (recommended):

uv run scansort watch

This 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 watch

βš™οΈ Configuration

All 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


πŸ’» CLI Quick Reference

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 N applies to the --cli session; the GUI always shows the full queue. scansort history and scansort stats exit non-zero when the audit log cannot be read. scansort config --set must be used alone (it is rejected when combined with other mutation flags), and unrecognised config.json keys 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 with scansort history --status REVIEWED. scansort rescan --doctor is 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.exe works the same way β€” just replace scansort with ScanSort.exe in 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.


πŸ’‘ Folder Hints

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.


πŸ“ File Naming

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


πŸ”„ Automatic Updates

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.


πŸ› οΈ For Developers

Running Tests

ScanSort enforces β‰₯95% test coverage:

# Lint
uv run ruff check .

# Format check
uv run ruff format --check .

# Test suite with coverage
uv run pytest

Project Structure

scansort/
β”œβ”€β”€ 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

Tech Stack

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)

Key Conventions

  • Test-first β€” write failing tests before production code. Every test must validate real behaviour, not inflate coverage.
  • β‰₯95% coverage enforced β€” pytest fails the build below this threshold.
  • Ruff for linting and formatting β€” ruff check . and ruff 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.

About

Intelligent automated desktop document filer powered by Gemini

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages