Disclaimer: This was 100% done using AI because I needed something quick. Caveat emptor.
Watches a Dropbox folder for scanned PDFs, OCRs them with Apple Vision, summarizes them with Apple's on-device Foundation Models, and creates an Apple Note with title, summary, keywords, and the original PDF attached.
100% local. No API keys. No cloud AI.
- macOS 26 (Tahoe) or later
- Apple Silicon Mac
- Apple Intelligence enabled (System Settings → Apple Intelligence & Siri)
- Xcode Command Line Tools:
xcode-select --install - Dropbox desktop app installed and syncing
One-liner (recommended):
curl -fsSL https://raw.githubusercontent.com/zacs/folder_to_notes/main/bootstrap.sh | bashThis clones the repo into ~/.folder-to-notes and runs the installer. Re-running
the same command later updates to the latest version.
Or manually:
git clone https://github.com/zacs/folder_to_notes.git ~/.folder-to-notes
cd ~/.folder-to-notes
bash install.shThe installer will:
- Ask for your Dropbox scanner folder path and target Notes folder name
(saved to
state/config, whichscripts/run.shreads) - Compile the Swift binary
- Install and load the launchd agent
Then grant the permissions below — without them the agent runs but can't see your scans.
launchd runs scripts/run.sh with a bare /bin/bash — no Terminal window
involved — so macOS privacy controls apply to /bin/bash itself. On current
macOS, Dropbox lives under ~/Library/CloudStorage (managed by macOS's File
Provider), and a background bash can't list it until you allow it. When
that's the problem, the log says:
FATAL: Cannot list /Users/you/Dropbox/Scanner: ... Operation not permitted ...
To fix it, open System Settings → Privacy & Security → Full Disk Access, click
+, press ⌘⇧G, enter /bin/bash, and turn it on. Note that Full Disk Access
applies to every script launchd runs with /bin/bash, not just this one.
If notes stop appearing, check the log for the FATAL line above.
The first note created also asks to let bash control Notes (Privacy &
Security → Automation).
Dropbox folder (local sync)
│ launchd WatchPaths fires on any change
▼
scripts/run.sh
│ finds new .pdf files at the top of the watched folder
│ (anything in ImportComplete/ has already been handled)
▼
bin/process_scan (Swift CLI)
│ Vision framework — OCR each page at 2x scale
│ Foundation Models — generate title, summary, keywords
│ outputs JSON to stdout
▼
scripts/create_note.py
│ AppleScript — creates Note in target folder
│ attaches original PDF
▼
Apple Notes / iCloud
Then: PDF is moved into Dropbox/Scanner/ImportComplete/
so it won't be reprocessed (state lives in Dropbox — survives
moving to a new machine).
~/.folder-to-notes/
├── install.sh ← run once to set up
├── Sources/
│ └── main.swift ← Swift OCR + AI tool
├── scripts/
│ ├── run.sh ← shell orchestration (called by launchd)
│ └── create_note.py ← AppleScript wrapper with proper escaping
├── launchd/
│ └── com.user.folder-to-notes.plist ← launchd template
├── bin/
│ └── process_scan ← compiled Swift binary (created by install.sh)
├── state/
│ └── config ← Dropbox + Notes folder (written by install.sh)
└── logs/
├── scanner.log
└── scanner-error.log
Processed PDFs are moved into <your-Dropbox-scanner-folder>/ImportComplete/,
which is the durable record of what's already been handled. Because that folder
lives inside Dropbox, the state syncs across machines — no risk of
reprocessing every PDF if you set this up on a second Mac.
Tail the log:
tail -f ~/.folder-to-notes/logs/scanner.logManually trigger a run (as launchd runs it, so permission problems show up):
launchctl kickstart gui/$(id -u)/com.user.folder-to-notesTest the Swift tool standalone:
~/.folder-to-notes/bin/process_scan ~/path/to/test.pdfReprocess a file (if something went wrong):
# Move it back from the ImportComplete folder to the watched folder
mv ~/Dropbox/Scanner/ImportComplete/filename.pdf ~/Dropbox/Scanner/
# Then drop another file in the folder, or run manuallyChange the Dropbox folder or Notes folder:
Re-run the installer (or the bootstrap one-liner). It saves the new values to
state/config and updates the launchd watch path:
bash ~/.folder-to-notes/install.shlaunchctl unload ~/Library/LaunchAgents/com.user.folder-to-notes.plist
rm ~/Library/LaunchAgents/com.user.folder-to-notes.plist
rm -rf ~/.folder-to-notes