This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
What I Built
Everyone has a folder they keep meaning to clean up. sloth-tidy is the lazy fix: a single Python file that sorts, renames and archives a messy folder using a tiny local model, gemma3:1b, through Ollama.
You point it at a folder and it prints a plan. Nothing moves until you add --apply, and every run can be undone.
-
Sorts files into
Sorted/<Category>/by type: Images, Documents, Installers, Archives, Code and so on. -
Labels documents and
.emlemails by reading a snippet locally: Invoices, Receipts, Certificates, Resumes, Study, Work, Legal, Finance, Travel, Personal, Newsletters. -
Renames only junk names, like
scan0001.pdforScreenshot (12).png. Descriptive names are left alone. -
Archives files that haven't been touched in a long time into
Sorted/Archive/<year>/.
The people who get lazier are the ones whose files are too private for a cloud tool: invoices, certificates, personal email.
Demo
This is a real run on a scratch folder, using gemma3:1b on my own Windows machine:
PS> python tidy.py scratch --apply
[1/4] reading notes.txt
[2/4] reading scan0001.txt
notes.txt
-> Sorted\Documents\Personal\notes.txt
scan0001.txt
-> Sorted\Documents\Invoices\2026-10-21 Electricity bill October 2024.txt
Screenshot (12).png
-> Sorted\Images\Screenshot 2026-10-21 1304.png
setup.exe
-> Sorted\Installers\setup.exe
Moved 4 files.
PS> python tidy.py scratch --undo
Restored 4 files from the run at 2026-10-24T13:09:00.
scan0001.txt contained only a line about an electricity bill. The model read it, picked a label and wrote a title, and the date prefix comes from the file's modified time. After the undo, all four files were back under their original names.
Code
Rx-Metallica
/
sloth-tidy
A lazy dev's file and inbox tidier. Sorts, renames and archives messy folders with a local gemma3:1b model via Ollama. No cloud, no API keys.
sloth-tidy
A lazy dev's file and inbox tidier. It sorts, renames and archives a messy folder using a
local model (gemma3:1b through Ollama). Your files never leave your machine.
Single Python file, standard library only.
What it does
-
Sorts files into
Sorted/<Category>/by type: Images, Documents, Installers, Archives, Code, ... -
Labels documents and
.emlemails by reading a snippet locally: Invoices, Receipts, Certificates Resumes, Study, Work, Legal, Finance, Travel, Personal, Newsletters. A keyword fallback covers the cases where a 1B model answers "Misc". -
Renames only messy names (
scan0001.pdf,Screenshot (12).png), e.g. to2026-10-21 Electricity bill October 2024.pdf. Descriptive names are left alone. -
Archives files older than N days into
Sorted/Archive/<year>/. - Stays safe: dry run by default, never deletes or overwrites, skips files modified in the last 10 minutes (likely still downloading), and every run can be undone.
Setup
ollama pull gemma3:1b
pip installā¦It's one file with no dependencies beyond the standard library. pypdf is optional, for reading PDFs.
How I Built It
Stack: Python standard library, Ollama's local HTTP API, gemma3:1b. There's no SDK and no API key. The script posts JSON to localhost:11434.
The pipeline: scan the top level of the folder, skip anything hidden, still downloading, or modified in the last 10 minutes, categorise by extension, pull a short text snippet from documents (.txt, .md, .docx via zipfile, .eml via email, PDFs via optional pypdf), ask the model for a label and a title, build a plan, print it, and only then move files and write an undo log.
A 1B model needs guardrails. Here is what I did about that:
- Constrain the output. Ollama's JSON mode, one few-shot example, a fixed list of allowed labels, temperature 0.1 and a snippet capped at about 1,200 characters.
- Never trust it. If the model returns a label outside the list, it's ignored. If it says "Misc", a small keyword fallback gets a second chance:
KEYWORDS = {
"Invoices": ["invoice", "bill", "amount due", "total due"],
"Certificates": ["certificate", "completion", "awarded"],
"Newsletters": ["unsubscribe", "newsletter", "view in browser"],
# ...
}
In my first real run, the model left an obvious electricity bill unlabeled. With the fallback it landed in Invoices.
-
Make mistakes cheap. Dry run by default, no overwriting or deleting, collision-safe names, Windows-safe filenames, and a JSON log that
--undoreplays in reverse.
A bug I found in my own tool: on a folder of certificate PDFs, pypdf wasn't installed, and the tool silently fell back to labeling PDFs by filename. The plan looked plausible, but the model had never read the files. The tool now prints a warning when PDFs can't be read.
Limitations:
- Subfolders aren't scanned.
-
gemma3:1bis text-only, so images are sorted by type and never read. - Scanned PDFs with no text layer can't be labeled.
- A 1B model will sometimes be wrong, which is why the dry run exists.
Why Does Open Innovation Matter?
Look at what this tool reads: invoices, certificates, emails. That's the last data I'd upload to a hosted API just to get a folder sorted.
An open-weight model changes the trade-off:
- Privacy: file contents never leave the machine.
- Cost and limits: it's free per file, with no rate limits, so it can scan thousands of files without a bill.
- Offline: it works with no connection once the model is pulled.
-
Swappable: changing models is one environment variable (
SLOTH_MODEL=gemma3:4b). I haven't benchmarked the larger model yet.
The honest trade-off is that a bigger closed model would probably label better. But for this job a small local model plus validation, a keyword fallback and an undo log is good enough, and it keeps my files on my own disk.
The Lazy Payoff
You set it up once, and you can then run it weekly with Windows Task Scheduler:
schtasks /Create /SC WEEKLY /D SUN /ST 20:00 /TN "SlothTidy" /TR "python C:\path\to\tidy.py C:\Users\you\Downloads --apply"
Prize Categories
Best Use of Gemma: gemma3:1b runs locally through Ollama and does the labeling and titling for every document and email.
Top comments (1)
Brilliant work hope continue to progress š