Dev.to AI 🤖 Ai 👁 0 📖 5 min read

Sloth Valet: a local AI that tidies my Downloads overnight (and can undo everything)

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady What I Built Sloth Valet is a small Linux tool that tidies a messy Downloads folder while I sleep. It waits until fi

Sloth Valet: a local AI that tidies my Downloads overnight (and can undo everything)

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady

What I Built

Sloth Valet is a small Linux tool that tidies a messy Downloads folder while I sleep. It waits until files have been left alone for a while, asks a small open-weight model running on my own machine what each file is, then renames it cleanly and moves it into a sorted folder tree. In the morning there's a short report waiting.

The effort it saves is the one I keep dodging: opening Downloads, seeing 200 files called final_v2 (3).pdf, and closing the window again. Anyone with a permanently messy folder gets to be a little lazier.

Because it runs unattended, I designed it around not breaking anything:

  • Dry-run is the default. Nothing moves until you pass --apply.
  • It waits. Files must sit untouched for a settle period, and partial downloads are skipped.
  • It never guesses. Low confidence, bad JSON, a timeout, or a missing model sends the file to _review/ or leaves it alone.
  • Everything is undoable. Every move is journaled in SQLite with a SHA-256 hash, and valet undo --last puts files back.
  • It never deletes or overwrites. Name collisions get a suffix.

Code

Sloth Valet

A slow, private file valet for a messy downloads folder.

Sloth Valet waits until files have settled, asks a small local open-weight model (through Ollama) for a category and a clean filename, and moves the files into a tidy destination tree. It runs offline, journals every move, writes a morning report, and prefers leaving a file alone over guessing.

Built for Hacktoberfest 2026: Slow and Steady — open-weight AI that rewards patience, privacy, and a recoverable filesystem.

Status

Version 0.1.0. The full pipeline is implemented: validated configuration, read-only scanning, bounded extraction, local classification, planning, journaled moves, review routing, crash reconciliation, undo, Markdown reports, optional desktop notifications a systemd user timer, and a model-comparison benchmark harness.

The default test suite uses mocked HTTP and fake classifiers. It does not need a network connection, a running Ollama, a real systemd session, or a notification daemon.

Why this shape

  • Offline…

It's about 4,700 lines of plain Python (no agent framework), MIT licensed, with mocked-HTTP tests so CI needs no Ollama. Setup is ./install.sh, then valet config init, then valet run for a dry run. scripts/demo.sh <model-name> runs the whole flow against generated fixtures in a temp folder, so you can try it without touching real files.

How I Built It

Open pieces

  • Model: open-weight models served locally through Ollama. The code never assumes a model name. You put one in the config, or pass several to valet bench --models.
  • Harness: no LangChain, no framework. The agent loop is plain code: scan, extract, classify, plan, apply, report.
  • Building it: I built it phase by phase with opencode, driven by an AGENTS.md of safety rules, with a branch and checks per phase.

Pipeline

  1. Scan watch folders. Protected paths, ignore globs, symlinks, size limits, and a settle check all run before any file is opened.
  2. Extract a bounded snippet of text (text, code, PDF, DOCX). Images and unknown binaries contribute metadata only.
  3. Classify through Ollama with a strict JSON schema. The category must come from an allowlist, and filenames are sanitized.
  4. Plan collision-free destinations. Obvious extensions like .deb or .iso skip the model entirely, which saves compute.
  5. Apply by journaling pending, revalidating the file, moving it, then marking done. A crash is reconciled on the next run.
  6. Report in Markdown, with an optional count-only desktop notification.

It runs from a systemd user timer (valet install-timer --time 03:00) at low CPU and I/O priority. The timer is persistent, so a run missed while the laptop was asleep happens after it wakes.

Treating files as hostile. A downloaded file can contain text like "ignore previous instructions". The model only picks from an allowlisted category and returns JSON, and nothing it outputs is executed or used raw in a path. There's a test for exactly that case.

Why Does Open Innovation Matter?

Privacy was the whole point. Downloads are full of marksheets, ID scans, offer letters, and invoices. With a local model, file text only ever goes to localhost:11434. There's no telemetry and no hosted API. The only HTTP module is ollama.py, and an architecture test enforces that boundary.

It costs nothing to run. No per-token bill means it can scan every night without me thinking about cost.

Swapping models was easy, and that mattered. Because the model is just a config value, I could benchmark it properly. I ran the same 50 labeled synthetic files through two tiny models on a CPU-only machine (8 cores, 7.6 GiB RAM; Ollama couldn't use my GTX 1050, so this is the no-GPU case):

Model Valid JSON Category accuracy Mean latency Peak RSS
qwen2.5:0.5b 100% 12% 936 ms 661 MiB
llama3.2:1b 100% 80% 1,490 ms 789 MiB

The surprise is that both models produced perfectly valid JSON, and one of them was still almost always wrong. A model that looks fine can quietly misfile your documents, which is why the review folder, the confidence threshold, and undo are core features, not extras. Being able to swap and measure models locally is what made that visible.

Where a closed model would have been better. I didn't benchmark a hosted model, so I can't give you a number. A large cloud model would probably be more accurate. For this job I'd rather accept some misfiling that I can see and undo than send my documents to a server I don't control. Those numbers are one run on synthetic files on one machine, not a general verdict.

The Lazy Payoff

Here's the honest accounting, using only what I measured. On my 50-file benchmark, llama3.2:1b averaged about 1.5 seconds per file on a CPU-only machine, so a full pass over 50 files costs roughly 75 seconds of machine time. That happens at 3 AM at low CPU and I/O priority, so none of it is my time.

The cost I'm really removing isn't the seconds spent moving a file. It's the decision cost: opening a folder full of final_v2 (3).pdf and deciding, file by file, where each one belongs. That's the part I never get around to.

I also ran it on a copy of my own Downloads: [N files scanned, X moved, Y sent to _review/, Z left alone because they were too new, in T minutes. Then valet undo --last restored all X.]

Prize Categories

Best Use of Gemma: Sloth Valet runs Gemma locally through Ollama as one of the models in its benchmark.

📰 Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.