Documentation
Everything you need to know about using Hazelnut to organize your files.
Installation
Homebrew (Recommended)
The easiest way to install Hazelnut on macOS or Linux:
brew install ricardodantas/tap/hazelnut Cargo
If you have Rust installed, you can use Cargo:
cargo install hazelnut From Source
git clone https://github.com/ricardodantas/hazelnut.git
cd hazelnut
cargo build --release Requirements
- Rust 1.93+ (only for Cargo/source installation)
- Linux or macOS
Quick Start
- Watch folders — which directories to monitor
- Rules — what to do with files in those folders
Get up and running in these steps:
Configure watches and rules
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" 💡 Use full paths — the ~ shortcut is not expanded.
Start the daemon
The daemon runs in the background, watching folders and applying rules:
hazelnutd start Launch the TUI
Open the terminal interface to manage rules and monitor activity:
hazelnut 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.
Config File
Hazelnut uses TOML for configuration. The default location is:
~/.config/hazelnut/config.toml Here's a complete example configuration:
[general]
log_level = "info"
log_file = "~/.local/share/hazelnut/hazelnut.log"
dry_run = false
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 |
dry_run | bool | false | Preview actions without executing |
debounce_seconds | int | 2 | Wait time before processing a file |
Watch Folders
Configure which directories Hazelnut monitors for changes:
[[watch]]
path = "/home/user/Downloads" # Use full paths
recursive = false
rules = [] # Empty = all rules apply | 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) |
Watch Editor Keybindings
| Key | Action |
|---|---|
| a / n | Add new watch folder |
| e | Edit selected watch |
| d | Delete selected watch |
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 pattern File 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 MB File Age
[rule.condition]
age_days_greater_than = 30 # Older than 30 days
age_days_less_than = 7 # Newer than 7 days Actions
Move
[rule.action]
type = "move"
destination = "~/Documents/PDFs"
create_destination = true # Create folder if missing
overwrite = false # Don't overwrite existing files 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{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
# Or permanently delete (⚠️ dangerous!)
type = "delete" Run Command
[rule.action]
type = "run"
command = "convert"
args = ["{path}", "-resize", "50%", "{dir}/{name}_small.{ext}"] Archive
[rule.action]
type = "archive"
destination = "~/Archives"
delete_original = false 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:
hazelnut
Keyboard Shortcuts
Navigation
| Key | Action |
|---|---|
| ? | Show help |
| q | Quit |
| 1-4 | Switch tabs (Dashboard, Rules, Watches, Log) |
| 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/disabled |
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 watch |
| d | Delete selected watch |
Rule Editor / Watch Editor
| Key | Action |
|---|---|
| Tab | Next field |
| Shift+Tab | Previous field |
| Enter | Save |
| Esc | Cancel |