π° Hazelnut
CLI file autosorter inspired by macOS Hazel. Watches directories and automatically moves, renames, or processes files based on configurable rules (name patterns, extensions, dates, etc.).
Architecture
Hazelnut consists of two main components:
hazelnut- The TUI (Terminal User Interface) for managing rules, viewing logs, and monitoring file organization activity.hazelnutd- The background daemon that watches folders and applies rules in real-time. Communicates with the TUI via Unix socket.
Quick Start
Install & Configure
Edit ~/.config/hazelnut/config.toml:
# Watch your Downloads folder
[[watch]]
path = "/home/youruser/Downloads"
# Move PDFs to Documents
[[rule]]
name = "Organize PDFs"
[rule.condition]
extension = "pdf"
[rule.action]
type = "move"
destination = "/home/youruser/Documents/PDFs"Run the Daemon
The daemon runs in the background, watching folders and applying rules:
hazelnutd startUse the TUI
Open the terminal interface to manage rules and monitor activity:
hazelnutStatus Output
Check the daemon status with:
$ hazelnutd status
π° Hazelnut daemon is running
PID: 12345
PID file: ~/.local/state/hazelnut/hazelnutd.pid
Log file: ~/.local/state/hazelnut/hazelnutd.log
Uptime: 2h 15m 30sConfiguration
Full Example Configuration
[general]
log_level = "info"
log_file = "~/.local/share/hazelnut/hazelnut.log"
debounce_seconds = 2
# Watch folders
[[watch]]
path = "~/Downloads"
recursive = false
[[watch]]
path = "~/Desktop"
recursive = false
# Rules
[[rule]]
name = "Move PDFs"
enabled = true
[rule.condition]
extension = "pdf"
[rule.action]
type = "move"
destination = "~/Documents/PDFs"General Settings
| Setting | Type | Default | Description |
|------|------|--------|--------| ------ | -------- | ----------------------------------------------- |
| log_level | string | "info" | Logging level (trace, debug, info, warn, error) |
| log_file | string | none | Path to log file |
| debounce_seconds | int | 2 | Wait time before processing a file |
| polling_interval_secs | int | 5 | How often to check for file changes |
| log_retention | int | 500 | Maximum activity log entries to keep |
| start_daemon_on_launch | bool | false | Auto-start daemon when TUI opens |
| notifications_enabled | bool | false | Desktop notifications on errors |
π Desktop Notifications: Enable
notifications_enabledto get alerted when something goes wrong (watch errors, rule failures, command errors). Works on Linux, macOS, and Windows.
Watch Folders
Configure which directories Hazelnut monitors for changes:
[[watch]]
path = "/home/user/Downloads" # Use full paths
recursive = false
rules = [] # Empty = all rules applyπ‘ TUI Tip: You can manage watches directly in the terminal interface! Press
ato add,eto edit,dto delete.
Watch Fields
| Field | Type | Default | Description |
| ----------- | ------ | -------- |------|------|--------|-----------------------------------------------|
| path | string | required | Directory to watch (use full paths, ~ not expanded) |
| recursive | bool | false | Watch subdirectories |
| rules | array | [] | Rule names to apply (empty = all rules apply) |
Watch Editor Keybindings
| Key | Action |
|---|---|
a / n | Add new watch |
e | Edit selected |
d | Delete selected |
Conditions
All conditions must match for a rule to apply. Omit conditions you donβt need.
File Name
[rule.condition]
name_matches = "Screenshot*.png" # Glob pattern
name_regex = "^invoice_\\d+\\.pdf$" # Regex patternFile Type
[rule.condition]
extension = "pdf" # Single extension
extensions = ["jpg", "jpeg", "png", "gif"] # Multiple
is_directory = false
is_hidden = true # Files starting with .File Size
[rule.condition]
size_greater_than = 10485760 # > 10 MB (in bytes)
size_less_than = 1048576 # < 1 MBFile Age
[rule.condition]
age_days_greater_than = 30 # Older than 30 days
age_days_less_than = 7 # Newer than 7 daysActions
Move
[rule.action]
type = "move"
destination = "~/Documents/PDFs"
create_destination = true # Create folder if missing
overwrite = false # Don't overwrite existing filesWorks across filesystems β automatically falls back to copy + delete when needed. Supports both files and directories.
Copy
[rule.action]
type = "copy"
destination = "~/Backup"
create_destination = true
overwrite = false
Rename
[rule.action]
type = "rename"
pattern = "{date}_{name}.{ext}"Available pattern variables:
{name}- Filename without extension{filename}- Full filename{ext}- Extension (empty string if none){path}- Full path{dir}- Parent directory{date}- Current date (YYYY-MM-DD){datetime}- Current datetime{date:FORMAT}- Custom date format
Trash / Delete
[rule.action]
type = "trash" # Safe, recoverable (uses native OS trash)# Or permanently delete (β οΈ dangerous!)
[rule.action]
type = "delete"Run Command
[rule.action]
type = "run"
command = "convert"
args = ["{path}", "-resize", "50%", "{dir}/{name}_small.{ext}"]Pattern variables are automatically shell-escaped for security. Commands have a 60-second timeout.
Archive
[rule.action]
type = "archive"
destination = "~/Archives"
delete_original = falseSupports both files and directories (directories are recursively archived).
Examples
Organize Screenshots
[[rule]]
name = "Screenshots"
[rule.condition]
name_matches = "Screenshot*.png"
[rule.action]
type = "move"
destination = "~/Pictures/Screenshots"Clean Old Downloads
[[rule]]
name = "Clean old downloads"
[rule.condition]
age_days_greater_than = 30
extensions = ["tmp", "log", "bak"]
[rule.action]
type = "trash"Sort Media Files
[[rule]]
name = "Sort Images"
[rule.condition]
extensions = ["jpg", "jpeg", "png", "gif", "webp"]
[rule.action]
type = "move"
destination = "~/Pictures/Unsorted"
[[rule]]
name = "Sort Videos"
[rule.condition]
extensions = ["mp4", "mov", "avi", "mkv"]
[rule.action]
type = "move"
destination = "~/Videos/Unsorted"Date-Prefix Invoices
[[rule]]
name = "Date prefix invoices"
[rule.condition]
name_matches = "invoice*.pdf"
[rule.action]
type = "rename"
pattern = "{date:YYYY-MM-DD}_{name}.{ext}"TUI Overview
The TUI provides a beautiful interface for managing Hazelnut. Launch it with:
hazelnutViews
- Dashboard - Overview of daemon status and recent activity
- Rules - Manage your file organization rules
- Log - View recent file operations
- Watches - Manage watched directories
Keyboard Shortcuts
Navigation
| Key | Action |
|β|----| --------------------- |
| ? | Show help |
| q | Quit |
| 1-4 | Switch tabs |
| Tab | Next tab |
| j/k or β/β | Navigate list |
| g / G | Go to first/last item |
Rules View
| Key | Action |
|---|---|
n | Create new rule |
e | Edit selected rule |
d | Delete selected rule |
Enter / Space | Toggle rule enabled |
General
| Key | Action |
|---|---|
t | Open theme picker |
s | Open settings |
A | About dialog |
Watches View
| Key | Action |
|---|---|
a / n | Add new watch |
e | Edit selected |
d | Delete selected |
Rule Editor / Watch Editor
| Key | Action |
|---|---|
Tab | Next field |
Shift+Tab | Previous |
Enter | Save |
Esc | Cancel |