Open ~/Downloads and count the PDFs. On most Macs it runs to hundreds: an electricity invoice, the receipt for a keyboard, a signed lease, two papers you meant to read, last month's card statement. Sorting the Downloads folder by extension puts every one of them in the same pile, which is the pile you already have.
Sorting by name does not help either, because names lie: document.pdf, download (3).pdf, scan_0042.pdf. The receipt you need for an expense claim is named like the forty files around it, so finding it means opening them one by one.
How the flow sorts each new download
Each new file in Downloads gets read on your Mac: the first page of a PDF, or the words in an image. The file name and that text go to Jev, TypeSafe AI's System One model, with one question: which of these eight kinds is this? When Jev is confident, Move File puts the file in ~/Documents/Sorted/<kind>/. When it is not, the file stays exactly where it landed.
Why Jev and not a chat model
Jev does not write text. You give it a state and a typed question, and it returns a probability for every option, summing to 1, plus a confidence that says how peaked that distribution is. TypeSafe calibrates those numbers, and their guidance is printed under the node's slider: below 0.5 the model is genuinely unsure, above 0.9 you can act automatically.
A threshold is only worth setting if the number behind it means the same thing every time. You can ask an AI Prompt node to rate its own confidence, but that number is more text it wrote, and nothing ties it to how often it is right. Jev: Choose routes on its measured confidence for you: Confident and Unsure are outlets, not a Condition you build.
The flow, node by node
Downloads is a Directory Changed trigger on ~/Downloads with Watch Subdirectories off and Event Types set to Created.
Finished download? is a Condition with one rule: fileExtension matches the regex ^(crdownload|part|download)$, with NOT on. Chrome writes a download as .crdownload and Firefox as .part, and without this rule the flow would move a half-written file out from under the browser. When the browser renames the finished file, the trigger sees the new name as a created file and the flow runs again on the real thing.
PDF? sends PDFs to Extract Text (PDF) with Page Range 1. Image? sends files whose fileType is Images to Extract Text (OCR). A .dmg or a .zip goes straight to Jev with only its name. Both readers run on your Mac, and a scanned PDF with no text layer gets OCR automatically.
The three paths join at Jev: Choose. A node with several inputs waits until each path into it has run or been skipped, so Jev runs once per file whichever way the file came.
Jev: Choose has four fields that matter:
- State is
File name: {{fileName}},Kind: {{fileType}}, then{{text}}{{extractedText}}. Only one of those two keys exists for a given file, and a missing key renders as nothing. An empty State would send the whole payload, and accuracy falls as unrelated context grows. - Question is "What is this downloaded file?"
- Options are eight
key = descriptionpairs: Invoices, Receipts, Statements, Contracts, Papers, Installers, Screenshots, Other. The descriptions carry what tells two kinds apart. Invoices is "a bill that asks for payment and has not been paid yet", Receipts is "proof that a payment already happened". Two descriptions that overlap split the probability between them and the node lands on Unsure. - Minimum confidence is 0.8.
The keys double as folder names. Worth filing? checks that {{jev.choice}} is not Other, and Move File sends the file to ~/Documents/Sorted/{{jev.choice}}/. Create Directories makes Receipts the first time a receipt arrives, and with Overwrite Existing off a second invoice.pdf lands as invoice-1.pdf.
The Unsure outlet is not wired. A file Jev is unsure about is left alone, which is the right default for a flow that moves your files.
What it needs and what it costs
Jev is a cloud model: the file name and extracted text leave your Mac, the file does not. The node's Account field picks who pays: a Jev account connected in Settings, Integrations (key from console.typesafe.ai), an OpenRouter provider, or Watchflows Sparks. OpenRouter hosts the same model, so thresholds mean the same on every route.
The pricing catalog Watchflows ships lists Jev at $0.042 per million input tokens, output free. A first page plus the question and options is a few thousand tokens at most, so a thousand files costs around a dime. The spend shows in the cost chip like any AI node's.
Edge cases that bite
- Page Range is not clamped.
1-2on a one-page receipt is out of range, not a short read: the node emits emptytextwithsuccessfalse and anerror, so Jev sees only the file name.1is safe on every PDF. - A failed Jev call fails the run. TypeSafe down, the rate limit still hit after retries, Sparks credit gone: with Failure unwired, the run fails and the file stays put. Wire Failure to a Log node if you want the run to finish green.
- Pin the model once you trust your threshold.
jev-latestmoves when TypeSafe ships a release. Set Model to a dated release such asjev-1.13.0and 0.8 keeps meaning what it meant when you chose it. - No loop. Files move to
~/Documents, outside the watched folder.
Sort the 1,400 files already in Downloads
Directory Changed only reacts to new files. Switch the flow on while Downloads is full and Folder Sweep asks what to do with the ones already there.
Pick Preview. Every file runs through the flow and Jev really answers, so Preview costs what the flow costs, but nothing moves. The review lists each file before and after, grouped by destination, with Unsure files under Left alone. Uncheck what you disagree with, then Commit: exactly the checked moves happen, and Jev is not asked again. The sweep's row in the run history carries Undo sweep, which puts the moved files back.
Variations
- Hear about the unsure ones. Wire Unsure to a Notification that says
{{fileName}}and{{jev.confidence}}. A Notification cannot be previewed, so it turns Preview off; disable the node while you sweep and turn it back on after. - Be stricter about one kind. Add a Condition that lets a
Contractsanswer through only when{{jev.probabilities.Contracts}}is greater than 0.95, and leave the rest at 0.8. - Ask a yes or no question instead. Jev: Yes or No with "Is this a tax document for 2026?" sends one kind of file somewhere specific and leaves the rest of the folder alone.
Paste this into the Flow Builder to get the same flow:
Watch ~/Downloads for new files. Skip anything ending in .crdownload, .part or .download. Read the first page of PDFs and OCR images, then ask Jev to choose what the file is: Invoices, Receipts, Statements, Contracts, Papers, Installers, Screenshots or Other, with a minimum confidence of 0.8. When it is confident and the answer is not Other, move the file into ~/Documents/Sorted/ in a folder named after the answer.