<?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: Ernest Achuka $DROPEE</title>
    <description>The latest articles on DEV Community by Ernest Achuka $DROPEE (@ernestino).</description>
    <link>https://dev.to/ernestino</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%2F3579160%2F239a100c-31e3-4305-98a1-6e3079013ddd.png</url>
      <title>DEV Community: Ernest Achuka $DROPEE</title>
      <link>https://dev.to/ernestino</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ernestino"/>
    <language>en</language>
    <item>
      <title>Sloth Sort -- a local, open-weight file tidier that works while you nap</title>
      <dc:creator>Ernest Achuka $DROPEE</dc:creator>
      <pubDate>Sun, 25 Oct 2026 04:57:58 +0000</pubDate>
      <link>https://dev.to/ernestino/sloth-sort-dyy-aeu-a-local-open-weight-file-tidier-that-works-while-you-nap-1ofb</link>
      <guid>https://dev.to/ernestino/sloth-sort-dyy-aeu-a-local-open-weight-file-tidier-that-works-while-you-nap-1ofb</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;&lt;strong&gt;Don't sort your Downloads folder. Outsource it to a sloth.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My &lt;code&gt;todo list.txt&lt;/code&gt; had carried the item &lt;em&gt;"sort downloads folder (lol, never)"&lt;/em&gt; for years. So I hired an animal with the correct energy policy: &lt;strong&gt;a local file-tidying agent whose brain is a 397 MB open-weight model&lt;/strong&gt; (Qwen2.5-0.5B-Instruct, Apache-2.0) served by &lt;strong&gt;Ollama (MIT)&lt;/strong&gt; on my own machine.&lt;/p&gt;

&lt;p&gt;Point it at any messy folder. It reads each file  -- real text extraction for PDFs, DOCX, notes, CSVs, JSON; filename-only for photos and media  -- asks the local model where it belongs and what a decent name would be, then files everything into tidy per-topic folders. In small shifts: eight files, a siesta, eight more.&lt;/p&gt;

&lt;p&gt;The sloth design spec:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--pace 8&lt;/code&gt;&lt;/strong&gt;  -- never moves more than N files per shift&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--siesta 2&lt;/code&gt;&lt;/strong&gt;  -- rests between files, like a professional&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;watch --nap 6h&lt;/code&gt;, or cron&lt;/strong&gt;  -- short night shifts, forever&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Archive, never delete&lt;/strong&gt;  -- junk is parked in &lt;code&gt;_archive/&lt;/code&gt;; deleting is a decision the sloth refuses to make&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dry-run first, &lt;code&gt;undo&lt;/code&gt; always&lt;/strong&gt;  -- every run writes a manifest and can be fully reversed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Who gets to be lazier? Me, obviously  -- but also anyone whose "Downloads" is a geological record, searching receipts with Spotlight at 11pm. The whole thing costs them nothing, needs no internet, and never sends a single byte of their files anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live project page:&lt;/strong&gt; &lt;a href="https://sloth-sort-hackathon.onrender.com" rel="noopener noreferrer"&gt;sloth-sort-hackathon.onrender.com&lt;/a&gt; -- the landing site, deployed on Render straight from the repo's docs/ folder.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://sloth-sort-hackathon.onrender.com" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;sloth-sort-hackathon.onrender.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This is a real, unedited run. I built a synthetic-but-painfully-true mess (&lt;code&gt;make_mess.py&lt;/code&gt;, included): 24 files with real PDFs, a real DOCX, camera imports, temp junk  -- and, deliberately, two files you'd never want to leave your machine.&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;1password-backup-2019.txt invoice_INV-2204_final_FINAL (2).pdf
IMG_8102.PNG jollof recipe grandma.md
PXL_20250914_181204.jpg letter to landlord.docx
Screenshot 2025-10-02 .png rental_agreement_signed.pdf
api_keys_OLD_ROTATED.json tax_summary_2024.pdf
bank_statement_sept.csv tmp9F31.tmp
data_pipeline.py tsdump_error_20251009.log ...(24 total)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Dry-run (it's the default verb  -- you review the plan before anything moves):&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;$ python3 sloth_sort.py scan demos/messy_downloads --pace 24
  1password-backup-2019.txt -&amp;gt; _archive/1password-backup-2019.txt
 stale password export, archive safely
  api_keys_OLD_ROTATED.json -&amp;gt; _archive/api-keys-old-rotated.json
 old credentials export, archive safely
  rental_agreement_signed.pdf -&amp;gt; Documents/rental-agreement-signed.pdf
 rental agreement signed, not finance
  tax_summary_2024.pdf -&amp;gt; Finance/tax-summary-2024.pdf
 personal income tax summary
  tmp9F31.tmp -&amp;gt; _archive/tmp9f31.tmp
 temporary file
  ... (24/24 classified by the model, zero fallbacks)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;After &lt;code&gt;apply&lt;/code&gt;:&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;Finance/{ark-cafe-october-receipt, bank-statement-sept, invoice-..., tax-summary-2024}
Documents/{letter-to-landlord.docx, rental-agreement-signed.pdf, jollof-recipe-grandma.md}
Notes/{lecture-w4-notes, meeting-notes-monday, todo-list ...}
Photos/{img-8102.png, pxl-....jpg, cat-meme-final.jpg}
Screenshots/  Software/  Code/
_archive/{1password-backup-2019.txt, api-keys-old-rotated.json, tmp9f31.tmp}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Undo, verified live:&lt;/strong&gt; &lt;code&gt;Undid run 20261025-031510: 23 file(s) back home.&lt;/code&gt;  -- then I re-applied. Full, unedited logs are in the repo under &lt;code&gt;demo/*.log&lt;/code&gt;.&lt;/p&gt;



&lt;p&gt;Audio: &lt;a href="https://github.com/leephil1907-lab/sloth-sort-hackathon-/blob/main/audio/sloth-sort-audio.mp3" rel="noopener noreferrer"&gt;listen to the 75-second narrated walkthrough&lt;/a&gt; (GitHub plays mp3s right in the page).&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;The whole harness is one dependency-light file (&lt;code&gt;sloth_sort.py&lt;/code&gt;, ~450 LOC; the only optional pip import is &lt;code&gt;pypdf&lt;/code&gt; for PDF text). Two excerpts give the flavor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The brain conversation&lt;/strong&gt;  -- a 0.5B model is very coachable if you give it a closed category list, strict JSON, and few-shot examples:&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;PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are a meticulous file organizer. Classify ONE file ...
Categories: Finance, Documents, Notes, Photos, Screenshots, Code, Media, Software, Personal, Junk
Rules: answer with ONLY a single JSON object; filename keeps the original extension;
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;archive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: true only for junk; if content mentions passwords/credentials/API keys,
the category MUST be Personal; leases and landlord letters are Documents, never Finance.

Example:
file: tenancy_agreement_v2.pdf | content: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RESIDENTIAL TENANCY AGREEMENT ... Monthly rent: 200,000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&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="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Documents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filename&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tenancy-agreement.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;archive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:false,&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="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lease, not finance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}

Now classify this file.
file: {name} | type: {kind} | size: {size} | content: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{snippet}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
JSON answer:&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;The responsible-part-of-the-sloth loop&lt;/strong&gt;  -- tolerant parsing, closed-list guard rails, manifest logging, no overwrites:&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_answer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# never crash mid-pile
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&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="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;CATEGORIES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# closed list, enforced
&lt;/span&gt; &lt;span class="n"&gt;data&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="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&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="n"&gt;folder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_archive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;archive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;data&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="n"&gt;dst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;unique_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;folder&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filename&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="c1"&gt;# never overwrite
&lt;/span&gt;&lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# only after dry-run approval
&lt;/span&gt;&lt;span class="nf"&gt;log_manifest_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# makes `undo` possible
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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/leephil1907-lab" rel="noopener noreferrer"&gt;
        leephil1907-lab
      &lt;/a&gt; / &lt;a href="https://github.com/leephil1907-lab/sloth-sort-hackathon-" rel="noopener noreferrer"&gt;
        sloth-sort-hackathon-
      &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;Sloth Sort 🦥&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A slow, local, open-source file tidier.&lt;/strong&gt; Point it at a messy folder; an open-weight
model running on &lt;em&gt;your&lt;/em&gt; machine decides where each file belongs; the sloth moves a few
files, takes a nap, moves a few more. It never deletes, never overwrites, and every run
can be undone.&lt;/p&gt;
&lt;p&gt;Built for the &lt;em&gt;Slow and Steady&lt;/em&gt; challenge (International Sloth Day) — see
&lt;a href="https://github.com/leephil1907-lab/sloth-sort-hackathon-/./SUBMISSION.md" rel="noopener noreferrer"&gt;&lt;code&gt;SUBMISSION.md&lt;/code&gt;&lt;/a&gt; for the write-up.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why "sloth"?&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--pace N&lt;/code&gt; — never handles more than N files per shift (default 10)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--siesta SECONDS&lt;/code&gt; — rests between files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;watch --nap 6h&lt;/code&gt; — works short shifts, sleeps for hours, repeats forever&lt;/li&gt;
&lt;li&gt;junk is &lt;strong&gt;archived&lt;/strong&gt;, never deleted — sloths conserve, they don't destroy&lt;/li&gt;
&lt;li&gt;dry-run by default; &lt;code&gt;undo&lt;/code&gt; reverses the last run from the manifest&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Stack&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;License&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/ollama/ollama" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;local model server (&lt;code&gt;/api/generate&lt;/code&gt;) on 127.0.0.1&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;qwen2.5:0.5b-instruct&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the classifier brain (397 MB, CPU-only is fine)&lt;/td&gt;
&lt;td&gt;Apache-2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;sloth_sort.py&lt;/code&gt; (~450 LOC)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/leephil1907-lab/sloth-sort-hackathon-" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Everything else  -- mess generator, README, logs, hero image, narration  -- is in the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sloth-sort/  sloth_sort.py  make_mess.py  README.md  demo/*.log  images/  audio/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;The stack is open all the way down, and the openness is load-bearing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Thing&lt;/th&gt;
&lt;th&gt;License&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Brain&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;qwen2.5:0.5b-instruct&lt;/code&gt;, Q4_K_M, 397 MB&lt;/td&gt;
&lt;td&gt;Apache-2.0&lt;/td&gt;
&lt;td&gt;reads each file, returns strict JSON: category, clean filename, archive?, reason&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;Ollama 0.40 on &lt;code&gt;127.0.0.1:11434&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;serves the GGUF via &lt;code&gt;/api/generate&lt;/code&gt;; CPU-only, no GPU needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Harness&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sloth_sort.py&lt;/code&gt;, stdlib HTTP client&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;agent loop: sniff -&amp;gt; classify -&amp;gt; (dry-run&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Built and demoed entirely inside a 2-core cloud sandbox  -- deliberately weak hardware, because sloths don't need a gym:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;~&lt;strong&gt;2.5-3 s per file&lt;/strong&gt; end-to-end (~35 tokens/s generation on two threads)&lt;/li&gt;
&lt;li&gt;24-file folder sorted in &lt;strong&gt;~65 s&lt;/strong&gt;, including model load&lt;/li&gt;
&lt;li&gt;RAM stayed flat and boring; the model weighs less than the PDFs of some people's tax years&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Honest process notes (this took three prompt iterations, and it shows why local iteration matters): &lt;strong&gt;pass 1&lt;/strong&gt; leaked a legacy password export into "Documents"; fixed with a hard rule  -- &lt;em&gt;anything mentioning credentials is Personal&lt;/em&gt;. &lt;strong&gt;Pass 2&lt;/strong&gt; filed a signed lease under "Finance" and a crash log under "Finance"; fixed with category-clarifying rules and two more few-shots. Final pass: 20/24 placements exactly right, 4 defensible-but-imperfect (a &lt;code&gt;.mp4&lt;/code&gt; screen recording landed in Notes; grandma's jollof recipe reasoned &lt;em&gt;"recipe notes"&lt;/em&gt; then chose Documents). For a 0.5B model, I'll take it  -- and when I want better, &lt;code&gt;--model qwen2.5:1.5b-instruct&lt;/code&gt; is a one-flag upgrade; the harness doesn't change.&lt;/p&gt;

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

&lt;p&gt;Not decoratively. For this specific tool, the closed option simply fails the requirements list:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. My Downloads folder contains a tax summary, a signed lease, and a password export.&lt;/strong&gt;&lt;br&gt;
A cloud classifier means uploading exactly those to a server I don't control. With local inference, privacy isn't a settings toggle on a privacy policy  -- it's physics. Nothing leaves the machine because nothing can.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. It works with the internet off, forever.&lt;/strong&gt;&lt;br&gt;
After a one-time 397 MB pull, sorting runs on a plane, in a blackout, in a village with carrier pigeons. File hygiene shouldn't have network availability as a dependency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Marginal cost is a solid, constitutional $0.00.&lt;/strong&gt;&lt;br&gt;
A pass over 24 files is ~25k input tokens. Pennies per run on even a cheap API  -- but forever, nightly, on every machine in the house. Local costs nothing at any scale and burns watts you already own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The brain is swappable and tunable  -- no product roadmap required.&lt;/strong&gt;&lt;br&gt;
My remaining misfires have two open fixes: bump model size with one flag, or fine-tune the small weights on &lt;em&gt;my&lt;/em&gt; definition of tidy. Closed APIs deprecate models mid-project and cap customization behind enterprise tiers; Apache-2.0 weights just sit on disk being mine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Auditable behavior on sensitive files.&lt;/strong&gt;&lt;br&gt;
The full decision loop  -- prompt, categories, fallback rules  -- is ~450 lines that fit in a head. When software is moving a file named &lt;code&gt;1password-backup-2019.txt&lt;/code&gt;, "trust us" is not an architecture.&lt;/p&gt;

&lt;p&gt;Where open beat closed during the build, concretely: I re-ran the full 24-file classification about six times while tuning the prompt  -- zero financial friction, no tokens-in-a-tab counting. &lt;code&gt;temperature=0&lt;/code&gt; plus pinned local weights gave me bit-for-bit reproducible plans, which a silently-updated hosted model cannot. And "does it run on my potato?" stopped being a billing question and became a laptop question.&lt;/p&gt;
&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;p&gt;This project was built in a single AI-agent session (Arena Agent Mode): environment probes -&amp;gt; direct-download Ollama binary -&amp;gt; pull model -&amp;gt; three prompt iterations, with every intermediate misclassification and the exact fixes preserved in &lt;code&gt;demo/*.log&lt;/code&gt;. The session included a real failure recovery you can audit: when the model server briefly raced its own model load, the harness cleanly took the extension-rule fallback path  -- no crash, no wrong move; that fallback exists &lt;em&gt;because&lt;/em&gt; sloths should never panic.&lt;/p&gt;

&lt;p&gt;Full build log, prompts, and all transcripts: in the &lt;code&gt;demo/&lt;/code&gt; folder of the &lt;a href="https://github.com/leephil1907-lab/sloth-sort-hackathon-" rel="noopener noreferrer"&gt;repo&lt;/a&gt; above.&lt;/p&gt;




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

&lt;p&gt;Manual triage of a folder like this is ~15-25 minutes of pure tedium: open, skim, decide, drag, rename, second-guess. The sloth does it in ~65 seconds of &lt;em&gt;not my time&lt;/em&gt;  -- and in watch/cron mode, it does it at 2am every night, forever, for free:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 2 * * *  cd ~/sloth-sort &amp;amp;&amp;amp; python3 sloth_sort.py apply "$HOME/Downloads" --pace 8 --siesta 2 &amp;gt;&amp;gt; "$HOME/.slothsort-nightly.log" 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This week it sorted the 24-file demo mess end-to-end multiple times, survived a full undo/re-apply cycle, and did its final run on hardware slower than my phone. Tonight it's going on cron for the real Downloads folder  -- which means the &lt;code&gt;todo list.txt&lt;/code&gt; item &lt;em&gt;"sort downloads folder (lol, never)"&lt;/em&gt; can finally be deleted. By me. Some work a sloth shouldn't do.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Entire&lt;/strong&gt;  -- the full build session is shared in &lt;em&gt;My Agent Session&lt;/em&gt; above via DevRelay.&lt;/li&gt;
&lt;/ul&gt;

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