DEV Community

Ali Shoaib
Ali Shoaib

Posted on

Lazy Tidier: I Taught an Open-Weight Model to Clean My Downloads Folder

Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady Submission 🦥

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.
  • --apply actually moves the files.
  • --undo reverses 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

Diagram of the Lazy Tidier flow: a Downloads folder goes through extension rules, then an open model for ambiguous files only, then a dry-run plan that can be applied or undone

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

  1. Scans a folder (non-recursive) for files.
  2. Rules first – images, videos, archives, and installers are sorted instantly by extension, no API call needed.
  3. LLM for ambiguous files – .pdf, .txt, .md, .docx are sent to a hosted open-weight model (default: Llama 3.1 8B on Groq) along with a short text snippet.
  4. Dry-run by default – prints a table of planned moves without touching anything.
  5. --apply – writes an undo log then moves files into <folder>/<Category>/ sub-folders.
  6. --undo – reverses the last --apply run.

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
…
Enter fullscreen mode Exit fullscreen mode

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:

  1. Rules first. Extensions like .jpg, .mp4, .zip and .exe are sorted locally with no API call.
  2. 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.
  3. 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.
  4. 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.
  5. Undo log. Every move is recorded in a JSON log before it happens, so --undo can 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 .env and 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.
  • --apply asks 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 --undo always restores the original names.
  • Hosted model catalogs change. If LLM_MODEL is 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 --apply reuses 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)