This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
TL;DR: Lazy Tidier is a Python CLI that sorts a messy folder using simple rules first and an open-weight model only for ambiguous files. It previews every change before touching anything, and it can undo what it did.
What I Built
My Downloads folder is a mess of IMG_2031.pdf, final_final(1).txt and installers I forgot about. I kept putting off cleaning it, so I built a tool to do it for me.
Lazy Tidier sorts a folder into categories like Images, Videos, Archives, Installers, Assignments and Lectures, and gives text-based files cleaner names.
Most of the work needs no AI at all. Rules based on file extension sort the obvious files for free. Only the ambiguous ones (PDFs and text notes) go to an open-weight model.
It is also safe by default:
- A normal run is a dry-run: it only prints what it would do.
-
--applyactually moves the files. -
--undoreverses the last run, restoring the original names too.
It's for anyone who dreads cleaning up their files and wants it done with one command.
Demo
Three commands cover everything:
python main.py --folder test_downloads # dry-run: shows the plan
python main.py --folder test_downloads --apply # moves the files
python main.py --folder test_downloads --undo # puts everything back
Here is a real dry-run on a test folder of 19 files (some rows trimmed):
OLD NAME CATEGORY NEW NAME
a b (1).txt Other a_b_1.txt
Artificial_Intelligence_A_Modern_... Books ai_modern_approach_summary.md
assignment3_notes.txt Assignments assignment3_notes.txt
bank_statement_jan.pdf Other bank_statement_jan.pdf
cs106b_assignment2_recursion.pdf Assignments cs106b_assignment2.pdf
lecture5_pointers.txt Lectures lecture5_pointers.txt
photo_vacation.jpg Images photo_vacation.jpg
project_backup.zip Archives project_backup.zip
video_lecture.mp4 Videos video_lecture.mp4
bank_statement_jan.pdf stayed in "Other" on purpose: its name matched the privacy guard, so its text was never sent to the model.
To enable the AI step, copy .env.example to .env and add your API key. Without a key, the tool still sorts the obvious files and safely puts the rest in "Other".
Code
Lazy Tidier 🗂️
Organise a messy Downloads folder in seconds — using extension rules for obvious files and an open-weight LLM for the ambiguous ones.
What it does
- Scans a folder (non-recursive) for files.
- Rules first – images, videos, archives, and installers are sorted instantly by extension, no API call needed.
-
LLM for ambiguous files –
.pdf,.txt,.md,.docxare sent to a hosted open-weight model (default: Llama 3.1 8B on Groq) along with a short text snippet. - Dry-run by default – prints a table of planned moves without touching anything.
-
--apply– writes an undo log then moves files into<folder>/<Category>/sub-folders. -
--undo– reverses the last--applyrun.
Setup
# 1. Clone & enter the repo
git clone https://github.com/<you>/lazy-tidier.git
cd lazy-tidier
# 2. Create and activate a virtual environment
python -m venv venv
# Windows:
venv\Scripts\activate
…How I Built It
Stack: Python, the OpenAI-compatible client library, pypdf for reading PDF text, and pytest.
Open-weight model: openai/gpt-oss-20b, served through Groq's free tier. The endpoint, key and model name all come from a .env file, so the code isn't tied to any one provider or model. My first choice, Llama 3.1 8B, returned a "model not found" error on my account, and swapping models took one line.
How it works:
-
Rules first. Extensions like
.jpg,.mp4,.zipand.exeare sorted locally with no API call. - Model for the rest. For ambiguous documents (PDFs and text files), only the filename and up to ~500 extracted characters are sent. The model replies with a category and a suggested name in JSON.
- Never trust the output. The reply is validated: code fences are stripped, unknown categories become "Other", and if the API fails three times the file keeps its original name.
-
Safe file handling. A filename sanitizer blocks path tricks (like
../) and characters Windows doesn't allow. A unique-path function never overwrites an existing file. -
Undo log. Every move is recorded in a JSON log before it happens, so
--undocan reverse it.
Privacy guard: files whose names contain any of 14 sensitive whole words (such as bank, cv, resume, passport or password) are sorted locally and never sent to the model. The matching is whole-word only, so video_lecture.mp4 isn't wrongly blocked.
Testing: 47 pytest tests cover filename safety, collisions, privacy matching and JSON parsing. The API is mocked, so the tests run offline and need no key.
I used Antigravity as a coding assistant, then reviewed the output, ran the tests myself and tried to break the file-moving logic on a folder of copies. That caught real issues, like the tool crashing when no API key was set even though most files don't need one.
Why Does Open Innovation Matter?
-
No vendor lock-in. When my first model failed on my provider, I changed one line in
.envand kept going. The same code works with another host or a local Ollama model. - Privacy I can improve. Right now my model runs on a hosted API, so short snippets do leave my machine, and I want to be upfront about that. But because the model is open, I can move to local inference later and keep every file on my own computer without rewriting anything. A closed API wouldn't give me that option.
- Cost. The whole project runs on free tiers, and a local model would cost nothing per run.
- Fit for my hardware. I can choose a model size that suits my 8 GB laptop and swap in better open models as they appear.
Limitations:
- A file with a harmless name can still contain private text in its first 500 characters, and that text would be sent to the API.
- A small model can suggest weak or generic names. That's why the dry-run comes first and why undo exists.
-
--applyasks the model again instead of reusing the dry-run result, so a suggested name can differ slightly from the preview. The categories matter most, and--undoalways restores the original names. - Hosted model catalogs change. If
LLM_MODELis wrong, the tool prints a warning and sorts ambiguous files into "Other" instead of crashing. - Audio files go straight to "Other" for now, because there is no Audio category yet.
- Hosted free tiers have rate limits, so very large folders are best processed in batches.
- My test folder had 19 small files, so these results are a small sample, not a benchmark.
The Lazy Payoff
On my 19-file test folder, 11 files went to the model and the rest were sorted locally. The whole dry-run took about 8.6 seconds in one run. In a manual review of the 11 model-handled files, every category fit and no name contained details missing from the file.
What's Next
- Saving the dry-run plan so
--applyreuses it exactly - A watch mode that tidies new downloads automatically
- A fully local run with Ollama so nothing leaves the machine
- Smarter naming for lecture and assignment files

Top comments (0)