This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
Shelf is a command-line tool that tidies a messy folder. Point it at your Downloads folder and it shows you where every top-level file would go. When you're happy with the plan, you run it again with --apply and the files move into folders like Documents, Images and Receipts.
Photos, PDFs and archives are sorted by their extension, so no model is involved. Plain-text files are different: a .txt or .md file tells you nothing about what it contains. For those, a small open-weight sentence embedding model reads the first 1,000 characters and compares them with short descriptions of each folder. If it can't clearly pick one, the file goes to Unsorted instead of a guessed folder.
It's for anyone who keeps putting off the clutter. You don't have to decide where each file goes, and you don't have to delete anything to make room.
There is no hosted version, because the tool runs on your own machine. Here is what a dry run prints on a folder of ten sample text files plus a few binaries:
01-shop.txt -> Receipts/01-shop.txt (model)
02-appt.txt -> Letters/02-appt.txt (model)
03-cake.txt -> Recipes/03-cake.txt (model)
04-parse.txt -> Unsorted/04-parse.txt (unsorted)
holiday.jpg -> Images/holiday.jpg (rule)
taxes.pdf -> Documents/taxes.pdf (rule)
Dry run: nothing was moved. Run again with --apply to move these files.
The output shows the reason for each decision, so you can check the plan before anything moves.
GitHub: https://github.com/Shishir2405/shelf (Apache-2.0)
Stack. TypeScript on Node.js 20, with no framework. The only runtime dependencies are @huggingface/transformers for the model and zod for validating the undo log.
Model. Xenova/all-MiniLM-L6-v2, an ONNX export of sentence-transformers/all-MiniLM-L6-v2, licensed Apache-2.0. I use the 8-bit weights, which are about 23 MB on disk. The model is downloaded once to ~/.cache/shelf/models. After that, sorting needs no network.
How a text file is decided. Each folder has two or three short prototype sentences, such as "a receipt listing items, subtotal, tax and total paid". The file's text is embedded once, and each folder is scored by its best-matching prototype. The top folder wins only if it beats the runner-up by at least 0.05 cosine similarity. Otherwise the file is Unsorted. Any answer outside the allowed folder list is discarded, so the model cannot create folders on its own.
What I tried first. My first plan used a chat model, Qwen2.5 in 0.5B and 1.5B sizes, to pick a folder. On ten labeled sample files it got 2 or 3 right, and it kept choosing the same folder regardless of content. Scoring folder names by log-probability did not help. The 1.5B model was about 1.6 GB and was no better. Switching to a sentence embedder with a much smaller model fixed the problem.
Safety. This was the part I cared most about, because a file tidier that loses files is worse than no tidier at all:
--apply. report (2).pdf..shelf-log.json. shelf undo <folder> reverses the last run, and anything it can't restore safely is reported and left alone.
Tests. 31 unit and integration tests run without down the model. They cover the rules, name collisions, the decision margin, the dry run, the undo, and a folder with a symlink and a hidden file. CI runs the same validation: typecheck, lint, format check, tests and build.
Measured, honestly. On ten synthetic text files that I wrote for this test, the real model got 7 right. Two went to Unsorted and one was wrong: a line of code containing the word "total" went to Receipts. The folder descriptions were edited while I looked at these files, so this number is optimistic. There is no held-out benchmark yet.
What the open approach made possible here:
What a closed API would have done better: a larger model would almost certainly be more accurate on messy text. I accepted lower accuracy to keep the files local.
No DevRelay session was saved for this project, so I'm not including one.
I haven't measured how much time it saves, so I'm not claiming a number. What it removes is the decision on every file: the dry run shows the whole plan, and the undo log means a wrong guess costs one command to reverse.