Hazelnut
🌰 Terminal-based automated file organizer inspired by Hazel. Watch folders and organize files with rules.
Install / Use
/learn @ricardodantas/HazelnutREADME
📖 Table of Contents
- ✨ Features
- 🚀 Quick Start
- ⚙️ Configuration
- 📋 Rules
- ⌨️ Keybindings
- 🎨 Themes
- 🏗️ Architecture
- 🔧 Building from Source
- 🤝 Contributing
- 📄 License
✨ Features
<table> <tr> <td width="50%">📁 Smart File Watching
Watch any folder for new and changed files with configurable debouncing and recursive monitoring. Paths support ~, $VAR, and ${VAR} expansion.
🎯 Flexible Rules Engine
Define powerful rules with conditions based on name, extension, size, age, and more. Multiple rules can match the same file — all matching rules execute in order.
⚡ Powerful Actions
Move, copy, rename, delete, archive files or directories, send to trash, or run custom scripts — all automated. Cross-filesystem moves are handled transparently.
</td> <td width="50%">🖥️ Beautiful TUI
A gorgeous terminal interface for managing rules and monitoring activity in real-time.
🔧 Background Daemon
Set it and forget it — the daemon runs quietly and applies rules 24/7.
📝 Simple Configuration
Human-readable TOML config that's easy to write and maintain.
</td> </tr> </table> <br>Feature Highlights
| Feature | Description | |---------|-------------| | 🔍 Pattern Matching | Glob patterns and regex for precise file matching | | 📊 Size Conditions | Filter files by size (greater than, less than) | | 📅 Age Conditions | Match files by modification date | | 🏷️ Multiple Extensions | Match any of multiple file types | | 📂 Recursive Watching | Monitor subdirectories automatically | | 🎨 15 Built-in Themes | From Dracula to Cyberpunk | | 🔔 Desktop Notifications | Get alerted on errors (cross-platform) | | 📋 Activity Log | Full history of all file operations |
<br>🚀 Quick Start
Installation
macOS
# Homebrew (recommended - fast, pre-built binary)
brew install ricardodantas/tap/hazelnut
Linux
# Homebrew
brew install ricardodantas/tap/hazelnut
# Or via Cargo
cargo install hazelnut
Arch Linux (pacman)
pacman -S hazelnut
Windows
# Via Cargo (requires Rust toolchain)
cargo install hazelnut
# Or download pre-built binary from GitHub Releases:
# https://github.com/ricardodantas/hazelnut/releases
From Source
git clone https://github.com/ricardodantas/hazelnut
cd hazelnut
cargo install --path .
Note: The daemon (
hazelnutd) is only available on macOS and Linux. On Windows, only the TUI (hazelnut) is available.
First Run
Hazelnut needs two things to work:
- Watch folders — which directories to monitor
- Rules — what to do with files in those folders
⚠️ Important: Rules alone won't do anything! You must also configure at least one watch folder.
- Create a config file at
~/.config/hazelnut/config.toml:
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# STEP 1: Define which folders to watch
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[[watch]]
path = "/home/youruser/Downloads" # Full path, ~, $HOME all work
recursive = false # Set true to include subfolders
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# STEP 2: Define rules for what to do with files
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[[rule]]
name = "Organize PDFs"
[rule.condition]
extension = "pdf"
[rule.action]
type = "move"
destination = "/home/youruser/Documents/PDFs"
- Launch the TUI to manage and monitor:
hazelnut
- Or start the daemon to run in the background:
hazelnutd start
<p align="center">
<img src="screenshots/05-rules.png" alt="Rules View" width="600">
</p>
<br>
⚙️ Configuration
Hazelnut uses TOML for configuration. The config file is always located at:
~/.config/hazelnut/config.toml
Full Configuration Example
# ─────────────────────────────────────────────────────────────
# General Settings
# ─────────────────────────────────────────────────────────────
[general]
# Logging level: trace, debug, info, warn, error
log_level = "info"
# Optional log file path
log_file = "~/.local/share/hazelnut/hazelnut.log"
# Wait time (seconds) before processing a file after change detected
debounce_seconds = 2
# How often to check for file changes (seconds)
polling_interval_secs = 5
# Desktop notifications on errors (cross-platform)
notifications_enabled = true
# Theme for the TUI
theme = "catppuccin-mocha"
# ─────────────────────────────────────────────────────────────
# Watch Folders
# ─────────────────────────────────────────────────────────────
[[watch]]
path = "~/Downloads"
recursive = false
[[watch]]
path = "~/Desktop"
recursive = false
# Only apply specific rules to this folder
rules = ["screenshots", "temp-files"]
[[watch]]
path = "~/Documents/Inbox"
recursive = true # Watch subdirectories too
# ─────────────────────────────────────────────────────────────
# Rules
# ─────────────────────────────────────────────────────────────
[[rule]]
name = "Organize PDFs"
enabled = true
stop_processing = false # Continue checking other rules
[rule.condition]
extension = "pdf"
[rule.action]
type = "move"
destination = "~/Documents/PDFs"
create_destination = true
overwrite = false
[[rule]]
name = "Screenshots"
enabled = true
[rule.condition]
name_matches = "Screenshot*.png"
[rule.action]
type = "move"
destination = "~/Pictures/Screenshots"
[[rule]]
name = "Clean Old Downloads"
enabled = true
[rule.condition]
age_days_greater_than = 30
extensions = ["tmp", "log", "bak"]
[rule.action]
type = "trash"
See docs/configuration.md for the complete reference.
Watch Editor (TUI)
You can manage watch folders directly in the TUI:
- Add a new watch: Press
aornin the Watches view - Edit an existing watch: Select a watch and press
e - Delete a watch: Select a watch and press
d
The watch editor dialog allows you to configure:
- Path - Path to the folder to watch (supports
~,$VAR,${VAR}, or absolute paths) - Recursive - Whether to include subdirectories
Use Tab to move between fields, Enter to save, and Esc to cancel.
📋 Rules
Rules are the heart of Hazelnut. Each rule has a condition (what files to match) and an action (what to do with them).
Conditions
All conditions in a rule must match for the rule to apply.
File Name
[rule.condition]
# Glob pattern matching
name_matches = "Screenshot*.png"
# Regex pattern matching
name_regex = "^invoice_\\d{4}\\.pdf$"
File Extension
[rule.condition]
# Single extension
extension = "pdf"
# Multiple extensions (match any)
extensions = ["jpg", "jpeg", "png", "gif", "webp"]
File Size
[rule.condition]
# Size in bytes
size_greater_than = 10485760 # > 10 MB
size_less_than = 1048576 # < 1 MB
File Age
[rule.condition]
# Age in days (based on modification time)
age_days_greater_than = 30 # Older than 30 days
age_days_less_than = 7 # Newer than 7 days
File Type
[rule.condition]
is_directory = false # Match only files
is_hidden = true # Match hidden files (starting with .)
Actions
Move
[rule.action]
type = "move"
destination = "~/Documents/Archive"
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 variables:
| Variable | Description | Example |
|----------|-------------|---------|
| {name} | Filename without extension | document |
| {filename} | Full filename | document.pdf |
| {ext} | Extension | pdf (empty if none) |
| {path} | Full path | /home/user/document.pdf |
| {dir} | Parent directory | /home/user |
| {date} | Current date | 2024-01-15 |
| {datetime} | Current datetime | 2024-01-15_14-30-00 |
| {date:FORMAT} | Custom format | {date:%Y%m%d} → 20240115 |
Trash
[rule.action]
