<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Harsh Chaturvedi</title>
    <description>The latest articles on DEV Community by Harsh Chaturvedi (@harsh_chaturvedi_a09bf925).</description>
    <link>https://dev.to/harsh_chaturvedi_a09bf925</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4189920%2F75aae27f-6dea-425e-a257-13932ca20738.jpg</url>
      <title>DEV Community: Harsh Chaturvedi</title>
      <link>https://dev.to/harsh_chaturvedi_a09bf925</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/harsh_chaturvedi_a09bf925"/>
    <language>en</language>
    <item>
      <title>slothsort 🦥 — Gemma reads my messy Downloads folder so I never have to</title>
      <dc:creator>Harsh Chaturvedi</dc:creator>
      <pubDate>Sun, 25 Oct 2026 16:55:55 +0000</pubDate>
      <link>https://dev.to/harsh_chaturvedi_a09bf925/slothsort-gemma-reads-my-messy-downloads-folder-so-i-never-have-to-22eo</link>
      <guid>https://dev.to/harsh_chaturvedi_a09bf925/slothsort-gemma-reads-my-messy-downloads-folder-so-i-never-have-to-22eo</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;My Downloads folder is where files go to be forgotten. &lt;code&gt;Invoice_88213 (1).pdf&lt;/code&gt;. &lt;code&gt;download.pdf&lt;/code&gt;. &lt;code&gt;image (3).png&lt;/code&gt;. &lt;code&gt;doc_final_v2_FINAL.pdf&lt;/code&gt;. Every few months I tell myself I'll sort it, open it, look at forty files with meaningless names, and close it again.&lt;/p&gt;

&lt;p&gt;The work is dull, but it isn't hard. You open each file, see what it is, give it a real name, and drop it in a folder. A small open-weight model can do exactly that. So I built &lt;strong&gt;slothsort&lt;/strong&gt;, a command-line folder tidier that hands the job to &lt;strong&gt;Gemma 3 4B running locally through Ollama&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For every loose file, slothsort shows the model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the name, size and date,&lt;/li&gt;
&lt;li&gt;the first part of the text, for &lt;code&gt;.txt&lt;/code&gt;, &lt;code&gt;.md&lt;/code&gt;, &lt;code&gt;.csv&lt;/code&gt;, &lt;code&gt;.json&lt;/code&gt;, code and simple PDFs,&lt;/li&gt;
&lt;li&gt;the file list inside a &lt;code&gt;.zip&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;the image itself&lt;/strong&gt;, for screenshots and photos. Gemma 3 is multimodal, so it actually &lt;em&gt;looks&lt;/em&gt; at your screenshots.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gemma answers with a folder (Finance, Screenshots, Code, Notes…), a readable new name, and a one-line reason. You see the full plan before anything moves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python slothsort.py plan  ~/Downloads   &lt;span class="c"&gt;# look, don't touch&lt;/span&gt;
python slothsort.py apply ~/Downloads   &lt;span class="c"&gt;# do it&lt;/span&gt;
python slothsort.py undo  ~/Downloads   &lt;span class="c"&gt;# never mind&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Who gets to be lazier? Anyone whose Downloads folder is a junk drawer. That's everyone.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Here's a real run on my Windows laptop: Gemma 3 4B in Ollama 0.34.2. The test folder is a fake "messy Downloads" I made with fictional bills, a payslip, screenshots, a bank CSV, code and an installer. All 14 files are in the repo, so you can run it yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data.json                       export_1729834.csv        New Text Document.txt
doc_final_v2_FINAL.pdf          files.zip                 Screenshot 2026-10-21 231407.png
download.pdf                    image (3).png             Screenshot 2026-10-23 104512.png
Invoice_88213 (1).pdf           main (2).py               untitled.md
VSCodeUserSetup-x64-1.105.1.exe WhatsApp Audio 2026-10-19 at 22.14.03.mp3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;What Gemma proposed&lt;/strong&gt; (trimmed from the real log):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Finance/
  download.pdf
    -&amp;gt; payslip-a-sharma-september-2026.pdf
       The contents clearly show a payslip with employee and pay period details.
  Invoice_88213 (1).pdf
    -&amp;gt; electricity-bill-september-2026-a-sharma.pdf
  doc_final_v2_FINAL.pdf
    -&amp;gt; rental-agreement-august-2026.pdf
  export_1729834.csv
    -&amp;gt; bank-statement-october-2026.csv

Code/
  Screenshot 2026-10-23 104512.png
    -&amp;gt; android-gradle-build-failure.png
       The screenshot shows a failed Gradle build output from an Android project.
  main (2).py
    -&amp;gt; python-results-checker.py
  untitled.md
    -&amp;gt; talk-ideas-college-tech-fest.md

Notes/
  New Text Document.txt
    -&amp;gt; groceries-sunday-plumber.txt
       The file contains a shopping list and a reminder about a plumbing issue.

14 files planned. Nothing has moved yet.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Look at &lt;code&gt;download.pdf&lt;/code&gt; → &lt;code&gt;payslip-a-sharma-september-2026.pdf&lt;/code&gt;. Nothing in that filename says "payslip". Gemma got it by reading the document. The Gradle screenshot is even better: there's no text to extract, so the model read the error off the image.&lt;/p&gt;

&lt;p&gt;After &lt;code&gt;apply&lt;/code&gt;, the folder is a tidy tree. After &lt;code&gt;undo&lt;/code&gt;, all 14 files were back exactly where they started. The full, unedited log is in &lt;a href="https://github.com/Chaturvediharsh123/slothsort/blob/main/demo/run-log.txt" rel="noopener noreferrer"&gt;&lt;code&gt;demo/run-log.txt&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Chaturvediharsh123" rel="noopener noreferrer"&gt;
        Chaturvediharsh123
      &lt;/a&gt; / &lt;a href="https://github.com/Chaturvediharsh123/slothsort" rel="noopener noreferrer"&gt;
        slothsort
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🦥 slothsort&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A lazy, local folder tidier.&lt;/strong&gt; Point it at your messy Downloads folder. An open-weight model running on your own machine (Gemma 3 4B via &lt;a href="https://ollama.com" rel="nofollow noopener noreferrer"&gt;Ollama&lt;/a&gt;) looks at every loose file. It reads text files and PDFs, and actually &lt;em&gt;looks&lt;/em&gt; at images. Then it suggests a folder and a readable name for each one.&lt;/p&gt;

&lt;p&gt;You review the plan, apply it with one command, and undo it with another. Nothing leaves your computer.&lt;/p&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;download.pdf                      -&amp;gt; Finance/payslip-a-sharma-september-2026.pdf
Screenshot 2026-10-23 104512.png  -&amp;gt; Code/android-gradle-build-failure.png
New Text Document.txt             -&amp;gt; Notes/groceries-sunday-plumber.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;(Real output from Gemma 3 4B on a CPU-only Windows laptop. The full log is in &lt;a href="https://github.com/Chaturvediharsh123/slothsort/demo/run-log.txt" rel="noopener noreferrer"&gt;&lt;code&gt;demo/run-log.txt&lt;/code&gt;&lt;/a&gt;.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Built for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19" rel="nofollow"&gt;DEV Hacktoberfest Open-Source AI Challenge, Week 3: Slow and Steady&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why it's built like this&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local model, local files.&lt;/strong&gt; Downloads folders are full of payslips, bank exports, rental agreements and boarding passes. Sending their contents to a cloud API just to…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Chaturvediharsh123/slothsort" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;It's a single Python file (~500 lines) using &lt;strong&gt;only the standard library&lt;/strong&gt;. There are 13 tests, which run against a fake Ollama server, so CI doesn't need a model.&lt;/p&gt;
&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The model: Gemma 3 4B, via Ollama.&lt;/strong&gt; I wanted one small model that could handle text &lt;em&gt;and&lt;/em&gt; screenshots, since screenshots are half of what's in my Downloads. Gemma 3 4B is multimodal, runs on a laptop CPU, and was already pulled on my machine. I talk to Ollama's &lt;code&gt;/api/chat&lt;/code&gt; with plain &lt;code&gt;urllib&lt;/code&gt;. No SDK, no LangChain, nothing to install except Ollama.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured output keeps it honest.&lt;/strong&gt; Small models like to chat. Ollama lets you pass a JSON Schema in &lt;code&gt;format&lt;/code&gt;, so every answer is forced into this shape:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;SCHEMA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;enum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CATEGORIES&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reason&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reason&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The category &lt;em&gt;has&lt;/em&gt; to be one from my list, and with &lt;code&gt;temperature: 0&lt;/code&gt; the same file gets the same answer every time. I still don't trust the output blindly: names are slugified, so a creative &lt;code&gt;../../etc/passwd&lt;/code&gt; becomes &lt;code&gt;etc-passwd&lt;/code&gt;. If the model is offline or returns junk, the file falls back to an extension-based guess and is marked &lt;code&gt;(guess)&lt;/code&gt; in the plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built for a sloth, not a demo.&lt;/strong&gt; These are the details that make it safe to run on a real folder:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;plan&lt;/code&gt; touches nothing. It writes &lt;code&gt;.slothsort/plan.json&lt;/code&gt;, and &lt;code&gt;apply&lt;/code&gt; executes exactly that plan.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;apply&lt;/code&gt; never overwrites. Name clashes become &lt;code&gt;-2&lt;/code&gt;, &lt;code&gt;-3&lt;/code&gt;, and if a target appears between plan and apply, that move is skipped.&lt;/li&gt;
&lt;li&gt;Every apply is logged, and &lt;code&gt;undo&lt;/code&gt; reverses it and removes the folders it created.&lt;/li&gt;
&lt;li&gt;Half-finished downloads (&lt;code&gt;.crdownload&lt;/code&gt;, &lt;code&gt;.part&lt;/code&gt;), hidden files and &lt;code&gt;desktop.ini&lt;/code&gt; are ignored.&lt;/li&gt;
&lt;li&gt;Decisions are &lt;strong&gt;cached by file fingerprint&lt;/strong&gt;. My first run took 428s. Running &lt;code&gt;plan&lt;/code&gt; again on the same folder reported &lt;code&gt;Took 0s&lt;/code&gt;, because no file needed the model. A nightly run only pays for &lt;em&gt;new&lt;/em&gt; downloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is the whole "slow and steady" idea. On my laptop, Gemma spends ~16–23s on a text file and ~70s on a screenshot. Waiting for that by hand would be slow. But &lt;code&gt;slothsort tidy&lt;/code&gt; is built for a scheduler, and at 2am nobody cares how long it takes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;schtasks&lt;/span&gt; &lt;span class="na"&gt;/create /sc &lt;/span&gt;&lt;span class="kd"&gt;daily&lt;/span&gt; &lt;span class="na"&gt;/st &lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt;:00 &lt;span class="na"&gt;/tn &lt;/span&gt;&lt;span class="kd"&gt;slothsort&lt;/span&gt; &lt;span class="na"&gt;/tr &lt;/span&gt;&lt;span class="s2"&gt;"python C:\path\to\slothsort.py tidy &lt;/span&gt;&lt;span class="nv"&gt;%USERPROFILE%&lt;/span&gt;&lt;span class="s2"&gt;\Downloads"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;I iterated on real output.&lt;/strong&gt; In the first run, &lt;code&gt;files.zip&lt;/code&gt; came back as &lt;code&gt;zip-file.zip&lt;/code&gt;. Fair enough, since the model only saw the name. I added a step that lists the files inside the archive. On the next run it became &lt;code&gt;assignment3-report-code-main.zip&lt;/code&gt;. That loop of running it, reading the log and fixing what the model couldn't see is most of what building with a small local model looks like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Look at what's actually in that demo folder: a &lt;strong&gt;payslip&lt;/strong&gt;, a &lt;strong&gt;bank statement&lt;/strong&gt;, a &lt;strong&gt;rental agreement&lt;/strong&gt;, an &lt;strong&gt;electricity bill&lt;/strong&gt; and a &lt;strong&gt;train booking&lt;/strong&gt; with a PNR. That's a normal Downloads folder. To sort it, the model has to &lt;em&gt;read&lt;/em&gt; those files.&lt;/p&gt;

&lt;p&gt;With a closed API, tidying my folder would mean uploading my salary, my bank transactions and my address to someone else's server, just to pick a folder name. That's a terrible trade, and it's the reason I'd never run a cloud-based version of this on my real files.&lt;/p&gt;

&lt;p&gt;With an open-weight model running locally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;My files never leave my laptop.&lt;/strong&gt; The only network call in the whole program goes to &lt;code&gt;localhost:11434&lt;/code&gt;. Pull out the network cable and it still works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing to run forever.&lt;/strong&gt; My first run made 14 model calls, and a big Downloads folder can mean hundreds. On an API I'd hesitate before every nightly run. Locally it's free, so I can let it run every night.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I can swap the brain.&lt;/strong&gt; It's one flag: &lt;code&gt;--model qwen2.5vl&lt;/code&gt; or &lt;code&gt;--model llava&lt;/code&gt;, or a smaller text-only model on a weak machine. Nothing is tied to one vendor's SDK.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ollama's open tooling did the heavy lifting.&lt;/strong&gt; Structured outputs, image input and keep-alive model caching are all features of an open-source runtime I can read and rely on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The closed models would be &lt;em&gt;smarter&lt;/em&gt;. But for a file tidier, privacy and "free to run forever" matter more than the last few points of accuracy, and the misses are cheap to fix because of plan-and-undo.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lazy Payoff
&lt;/h2&gt;

&lt;p&gt;On the 14-file test folder, slothsort did in one command what would take me a good 10–15 minutes by hand: opening each PDF to see what it is, squinting at screenshots, and typing new names. More importantly, it's the kind of 15 minutes I &lt;em&gt;never actually spend&lt;/em&gt;, which is why my real Downloads folder looks the way it does.&lt;/p&gt;

&lt;p&gt;It's not perfect, and I'd rather show the misses than hide them. Of 14 files, Gemma got sensible names for all of them, but put a few in debatable folders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The train booking &lt;strong&gt;screenshot&lt;/strong&gt; went to &lt;code&gt;Finance/&lt;/code&gt; (because of the fare) instead of &lt;code&gt;Screenshots/&lt;/code&gt;, even though the prompt says screen captures go to Screenshots.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;bar chart&lt;/strong&gt; image I'd exported went to &lt;code&gt;Screenshots/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The Gradle error screenshot went to &lt;code&gt;Code/&lt;/code&gt;. Honestly, I like that one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is exactly why the tool plans first. You read the plan in ten seconds, and if you don't like it you don't apply it, or you &lt;code&gt;undo&lt;/code&gt;. Being lazy is safe when the machine shows its work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 3 4B runs locally through Ollama and is the core of the project. It reads text and PDFs, and uses its vision input to classify and name screenshots and images.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Built with help from an AI coding assistant (Claude), which wrote much of the code and helped draft this post. The model calls, test run and results above are all real.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>python</category>
    </item>
  </channel>
</rss>
