DEV Community

Harshita Gupta
Harshita Gupta

Posted on AI-assisted

Slow and Steady: a lazy folder tidier that runs on a local open model

The problem

Sorting files by hand is boring, and it is the kind of job I keep putting off. So I built a small script that does the sorting for me.

What I built

tidy.py is a short Python script that asks a local open-weight model to sort the files in a folder into subfolders: documents, spreadsheets, images, videos, code, archives, installers and other.

  • The model is Qwen 2.5 3B, run locally through Ollama.
  • It uses only the Python standard library, so there is nothing else to install.
  • It does a dry run by default: it prints a plan and moves nothing until I add --apply.
  • It never deletes anything. It only moves files into subfolders, and it skips a file if one with the same name is already in the target folder.

Demo

I tested it on a dummy folder of 12 empty files with realistic names (holiday.jpg, budget.xlsx, sales_data.csv, setup.exe and so on), so nothing real was at risk.

Before: 12 loose files, and the plan the model proposed.

After running with --apply: the same files, sorted into folders.

Why open matters

  • The model runs on my own computer. The script only talks to localhost, so my file names are never sent to a server I don't control. Once the model is downloaded, nothing in the script needs the internet. (I did not test it with Wi-Fi switched off.)
  • It costs nothing to run. There is no API key and no per-request fee.
  • I can swap the model by changing one line. The model name is a single variable at the top of the script.
  • I can change how it behaves. When budget.xlsx landed in "documents", I added a "spreadsheets" category to the list, and the next run sorted it correctly. I could do that because I control the whole setup.

What went wrong

  • The first run seemed to hang. I stopped it and ran it again, and it worked. My guess is that the model was loading into memory for the first time, but I did not confirm that.
  • The model only sees file names, not contents. It can't tell what final_v2.pdf is about.
  • It is not always consistent. Even after I added the "spreadsheets" category, sales_data.csv still went to "documents". Giving the model a few examples, or checking the file extension in code before asking the model, would probably fix this.
  • My test was tiny. 12 dummy files is a sanity check, not a benchmark. I'd want to try it on a real, messy folder before trusting it.

How to run it

  1. Install Ollama and download the model:
ollama pull qwen2.5:3b
Enter fullscreen mode Exit fullscreen mode
  1. Save the script below as tidy.py.
  2. Make a dry run on a test folder:
python tidy.py path/to/folder
Enter fullscreen mode Exit fullscreen mode
  1. If the plan looks right, apply it:
python tidy.py path/to/folder --apply
Enter fullscreen mode Exit fullscreen mode
"""Folder tidier: a local open-weight model (via Ollama) sorts files into folders.
Dry run by default. Never deletes anything; it only moves files into subfolders."""
import json
import shutil
import sys
import urllib.error
import urllib.request
from pathlib import Path

MODEL = "qwen2.5:3b"
CATEGORIES = ["documents", "spreadsheets", "images", "videos", "code", "archives", "installers", "other"]


def ask(filename: str) -> str:
    prompt = (f"Classify this file name into exactly one of: {', '.join(CATEGORIES)}.\n"
              f"File name: {filename}\nAnswer with the single category word only.")
    body = json.dumps({"model": MODEL, "prompt": prompt, "stream": False,
                       "options": {"temperature": 0}}).encode()
    req = urllib.request.Request("http://localhost:11434/api/generate", data=body,
                                 headers={"Content-Type": "application/json"})
    try:
        with urllib.request.urlopen(req, timeout=120) as r:
            answer = json.load(r)["response"].strip().lower()
    except urllib.error.URLError:
        sys.exit("Could not reach Ollama. Is it running? Try: ollama list")
    for category in CATEGORIES:
        if category in answer:
            return category
    return "other"


def main():
    if len(sys.argv) < 2:
        sys.exit("Usage: python tidy.py <folder> [--apply]")
    folder = Path(sys.argv[1]).expanduser()
    apply = "--apply" in sys.argv
    plan = [(f, folder / ask(f.name)) for f in sorted(folder.iterdir()) if f.is_file()]
    for f, dest in plan:
        print(f"{f.name:30} ->  {dest.name}/")
    if apply:
        for f, dest in plan:
            dest.mkdir(exist_ok=True)
            target = dest / f.name
            if not target.exists():
                shutil.move(str(f), str(target))
        print("\nDone.")
    else:
        print("\nDry run only. Re-run with --apply to move the files.")


main()
Enter fullscreen mode Exit fullscreen mode

A note on AI use

I used an AI assistant (Claude) to help write the script and to draft and edit this post. I ran and tested the script myself, and the results and limits described above are from my own runs.

Top comments (0)