This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
What I Built
sloth-tidy is a small Python CLI that sorts, renames and archives a cluttered folder, like the Downloads folder I keep promising myself I'll clean up.
It reads each file's name, size and the first 300 characters of text files. A local open-weight model then picks a category (Documents, Images, Receipts, ...) and proposes a clean kebab-case name. Nothing moves until you pass --apply, and --undo puts everything back.
Anyone with a folder they dread opening gets to be a little lazier: point it at the folder, read the plan, press go.
Demo
Dry-run on a sample folder (extension-rules mode, --no-llm):
IMG_0042.png -> Tidied/Images/IMG_0042.png
backup.zip -> Tidied/Archives/backup.zip
installer.dmg -> Tidied/Installers/installer.dmg
invoice-oct.pdf -> Tidied/Documents/invoice-oct.pdf
notes.txt -> Tidied/Documents/notes.txt
5 file(s) planned (dry-run; add --apply).
Code
sloth-tidy
Sorts, renames and archives a messy folder using a local open-weight model (Gemma via Ollama). No cloud, no API key, stdlib-only Python.
ollama pull gemma3:4b
python3 sloth_tidy.py ~/Downloads # dry-run: prints the plan
python3 sloth_tidy.py ~/Downloads --apply # moves files, writes an undo log
python3 sloth_tidy.py ~/Downloads --undo # puts everything back
Safety: dry-run by default, never overwrites, skips hidden files and anything modified in the last 10 minutes, falls back to extension rules if the model is unreachable (--no-llm forces this). Only the first 300 characters of text files are sent to the model, and only to localhost.
Tests: python3 -m pytest
ollama pull gemma3:4b
python3 sloth_tidy.py ~/Downloads # dry-run: prints the plan
python3 sloth_tidy.py ~/Downloads --apply # moves files, writes an undo log
python3 sloth_tidy.py ~/Downloads --undo # puts everything back
How I Built It
-
Model: Gemma (default
gemma3:4b), served locally by Ollama. The--modelflag swaps in any other Ollama model. -
Integration: plain
POST /api/chattolocalhost:11434withformat: "json". No SDK and no third-party dependencies, so the whole tool is one Python file using only the standard library. - Batching: files go to the model 20 at a time, each with an id, so replies map back to files reliably.
-
Don't trust the model: every reply is validated. Unknown categories fall back to extension rules, suggested names are re-slugified, and name collisions get
-2,-3suffixes so nothing is overwritten. - Fails soft: if the server is down or returns bad JSON, that batch falls back to extension rules.
- Safety rails: dry-run by default, hidden files and files modified in the last 10 minutes are skipped, and every applied run writes an undo log.
Unit tests cover the fallback rules, apply/undo, model renames, collisions, invalid categories, recent-file skipping and model failure. They use a stubbed model, so they run without Ollama.
Honest note: I verified the model path against a stubbed response, not a live Gemma run, so I'm not claiming any naming-quality or speed numbers.
Why Does Open Innovation Matter?
-
Privacy: a Downloads folder is full of invoices, tax PDFs and screenshots. With a local model, file names and text previews only go to
localhost. A closed API would mean uploading that listing to a server I don't control. - Offline and free: it works without a connection and costs nothing per run, so it's fine to run on a schedule.
- Swappable: the model is one flag. A bigger model for better names, or a smaller one for speed, needs no code change.
- Small is enough: JSON-mode output plus validation means a small local model can do this job. Filing a PDF doesn't need a frontier model.
Top comments (0)