This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
The folder I've been avoiding
My Downloads folder had about 140 loose files in it when I started this. When I sorted them oldest-first, the first sixty told the whole story: forty of them were installers. Four separate IntelliJ installers. Two PostgreSQLs, two HeidiSQLs, two Postmans, two Chromes, three identical copies of SupportAssistLauncher. Somewhere between them, the important stuff: my Aadhaar and PAN card scans, a tax certificate, a train booking, my resume, and a PDF called train (1).pdf that turned out to be that train booking.
Every few months I open this folder, look at it, and close it again. That's the whole relationship.
Sloths aren't really lazy. They're specialists in doing one thing at a time and never spending energy they don't need to. That felt like exactly the right temperament for this job. The work is boring, nobody needs it done fast, and it can happen while I'm asleep. So I built Slothbox.
What it does
Slothbox is a single Python script. It looks at every loose file in Downloads, one at a time. For each one, a local Gemma 3 model reads what's inside (the text, the first pages of a PDF, the words in a Word or PowerPoint file, the file list of a zip, or the pixels of a screenshot) and decides two things: which category it belongs in, and what a sensible human would have named it.
Then it moves the file. Recent files go to Sorted/<category>/. Anything older than 90 days goes to Archive/<year>/<category>/. At the end it writes a short morning note. Here's a slice of my first real run:
train (1).pdf → Archive\2025\personal-docs\train-booking-slip-2025-05.pdf (The file contains a train booking slip with passenger details and booking information.) npp.8.7.1.Installer.x64.exe → Archive\2024\installers\npp-8.7.1-installer.exe (The file is an executable installer for Notepad++.) Dropbox Integration.pdf → Archive\2025\work-docs\dropbox-integration-guide.pdf (This document describes how to integrate with the Dropbox API.) Why this had to be open, local AI
For this project, open models aren't a nice-to-have. They're the only reason the tool can exist.
My Downloads folder is the most sensitive folder on my computer. That's where ID scans, tax forms, bank statements and contracts land, precisely because I never got around to filing them anywhere. An AI tidier is only useful if it reads the contents; train (1).pdf tells you nothing until you open it. With a hosted model, "tidy my Downloads" really means "upload my Aadhaar card and my tax certificate to a server I don't control, every night, forever." I wasn't willing to do that.
With Gemma running through Ollama, none of it leaves the laptop. You can switch off Wi-Fi and it keeps working. That's not a privacy policy I have to trust. It's just where the bytes are.
It costs nothing to leave running. A tool you schedule every night for years is the worst possible fit for per-token pricing. The cost of my run was the electricity my laptop was already using.
It doesn't need to be fast, so a small model is enough. Gemma 3 4B averaged 14.8 seconds per file on my laptop (Intel Core Ultra 7 255H, 32 GB RAM): 55 files in about 14 minutes. In a chat window, fifteen seconds per answer would be painful. At 3 a.m. it doesn't matter at all. This is where "slow and steady" stops being a theme and becomes the design: once you stop needing the answer now, the model that fits on your own hardware is the right model.
I could reach inside the stack. Two open features did real work here. Ollama's structured outputs let me pass a JSON schema with a fixed list of 13 categories, so the decoder can't produce a category that isn't on the list. And because the model is a flag (--model), swapping Gemma sizes is a one-word change.
How it works
The whole thing is one file of standard-library Python, plus pypdf for reading PDFs.
for each top-level file, oldest first: skip hidden files, system files, half-finished downloads fingerprint = sha256(size + first 1 MB) if fingerprint is cached: reuse the decision else: read a snippet, or attach the image ask Gemma for {category, new_name, reason}, constrained by a JSON schema rest a few seconds clean_decision(): the code checks the model's work move into Sorted/ or Archive/, never overwriting, and journal the move write a morning note It runs at the lowest CPU priority, so it never competes with anything else I'm doing. It never deletes anything (Archive is just a folder), never overwrites (clashes become name-2.exe), shows every proposed move with Gemma's reason before touching anything (slothbox plan), and keeps a journal so slothbox undo can put the last run back exactly as it was.
What I learned: the model suggests, the code decides
The interesting part of this project wasn't the first run. It was the three runs after it.
Run 1: prompt only. Gemma recognized every installer, which was impressive, but the names were inconsistent. It kept the version number for VS Code and dropped it for Notepad++. It renamed HeidiSQL_12.8.0.6908_Setup.exe to heidisql-setup-2024-11-26.exe: the version vanished and a date appeared that wasn't anywhere in the file. It had taken the "last modified" date I'd shown it and decided that was the file's date. It also filed debug.log under installers.
I added rules to the prompt: keep version numbers with their dots, only use dates that appear inside the file, logs aren't installers. Then I found that half the version problem was my own code. My filename cleaner turned every dot into a dash, so mysql-9.1.0 could never survive no matter what Gemma said. Fixed that too.
Run 2: prompt rules. Versions were much better: npp-8.7.1, mysql-9.1.0, node-v22.17.1. But the second run surfaced the scariest bug of the project. One of my tax documents had my PAN number in its filename, and Gemma changed one letter of it. Imagine ABCDE1234F becoming ABCTE1234F. A 4B model isn't copying characters; it's predicting them, and an ID number looks a lot like noise to a language model.
It also tried to move desktop.ini, a Windows system file. It renamed a Zoom log to a 70-character string of random letters. And it still put a made-up date on the Slack installer, prompt rule or not.
That's when the design clicked for me. A prompt is a suggestion. Rules that must never break belong in code. So I wrote clean_decision(), which checks every answer before anything moves:
- If an ID-like token from the original name (8+ characters mixing letters and digits) shows up in the new name with a character or two changed, put the original back.
- If a date's year appears nowhere in the filename or the contents, remove it.
- If the new name is over 60 characters or contains a long random-looking token, fall back to the original name, cleaned up.
-
.exeand.msiare always installers,.logis always data. The model doesn't get a vote. -
desktop.iniand friends are never touched. Run 3: guardrails, and actually reading PDFs. I'd also discovered that every PDF reason said something like "the file name suggests…", becausepypdfwasn't installed and Gemma was guessing from names alone. Once it could read the pages, the difference was night and day:
| Before | After |
|---|---|
train-1.pdf | train-booking-slip-2025-05.pdf |
carelink-3.pdf | carelink-consent-form.pdf |
anshika-pandey-pdf.pdf | anshika-pandey-resume.pdf |
My favorite moment: Gemma described the Zoom log as "a Zoom installer executable file." Wrong. But the code filed it under data anyway. The model can be confidently mistaken and the file still ends up in the right place.
That run also crashed halfway, because a file disappeared from Downloads while Slothbox was working on it. A tool that's slow on purpose has to expect the world to change underneath it, so a vanished file is now just "skipped." And because every decision is cached the moment it's made, the rerun picked up exactly where it stopped. The 28 files it had already read didn't cost a second.
It's still not perfect, and I think that's honest to say. IBALauncher.exe became ibalancher.exe, the same dropped-character problem as the PAN number, just harmless this time. A JRE zip picked up "windows-installer" in its name, which it isn't. And my first morning note cheerfully mentioned "a few receipts lurking in there." There were zero receipts. It also said "overnight" at 7:46 p.m. Even when you hand a model the exact facts, it will happily add a few of its own.
Try it
ollama pull gemma3:4b pip install pypdf git clone https://github.com/Anshika-019/slothbox && cd slothbox python slothbox.py plan --limit 20 Read what it proposes, then apply when you trust it. To run it every night at 3 a.m. on Windows:
schtasks /create /sc daily /st 03:00 /tn slothbox /tr "python C:\path\to\slothbox\slothbox.py apply" (On macOS or Linux, the README has a cron line and a launchd file.)
Repo: https://github.com/Anshika-019/slothbox
Built with Gemma 3 (open-weight) and Ollama (open source). Submitting for Best Use of Gemma.
Sixty files down, seventy-eight to go. Slothbox will get to them tonight. 🦥

