autotidy
Automatically organize files using declarative rules
About
autotidy is a cross-platform file organization daemon. Define rules in YAML, and it watches your folders for changes, moving, renaming, or organizing files as they appear.
- Automatic - Runs in the background, watching directories. Triggers your rules when contents change.
- Declarative - Define your rules in YAML. No code required.
- Filters - Match files by name, extension, size, date, MIME type, file type.
- Actions - Move, copy, rename, delete, and trash files that pass your filters.
- Standalone - Standalone compiled binary. No runtime dependencies.
- Dry-run - Preview what your config would do before running it with
autotidy run. - Cross-platform - Linux, macOS, Windows (experimental)
- Open source - MIT licensed
Quick Start
1. Install
See Installation
2. Verify installation
Verify autotidy is installed and running.
❯ autotidy status
status 🟢 running
config ~/.config/autotidy/config.yaml
watching none
rules
⚠ none
3. Create your first rule
Edit ~/.config/autotidy/config.yaml:
rules:
- name: Organize PDFs
locations: ~/Downloads
filters:
- extension: pdf
actions:
- move: ~/Documents/PDFs
4. Dry run your rule
Use autotidy run to preview what your rules would do without making changes:
❯ autotidy run
Dry-run mode enabled (pass --dry-run=false to perform a one-off run of all rules)
━━━ Rule: Clean up Desktop images ━━━
~/Downloads/document.pdf
├── filters:
│ └── extension: ✓
└── actions:
└── move: ✓ → ~/Documents/PDFs
5. Reload your rules
Tell the daemon to pick up your config changes:
❯ autotidy reload
Reloaded ~/.config/autotidy/config.yaml
Then verify your rule is active:
❯ autotidy status
status 🟢 running
config ~/.config/autotidy/config.yaml
watching 1 directories
rules
🟢 Organize PDFs
last run: 1 hour ago (2ms, 1 files)
Your rule is now running. Any PDFs added to ~/Downloads will be moved to ~/Documents/PDFs.
Next steps
- Configuration - Full configuration reference
- Filters - All available filters
- Actions - All available actions
Installation
Choose your platform:
Linux
Install
curl -fsSL https://raw.githubusercontent.com/prettymuchbryce/autotidy/master/install/linux/install.sh | sh
This downloads the latest release, installs it to ~/.local/bin, sets up a systemd user service, and starts the daemon.
Verify
autotidy status
Note: If
autotidyis not recognized, restart your shell or run:export PATH="$HOME/.local/bin:$PATH"
Service management
# Check status
systemctl --user status autotidy
# View logs
journalctl --user -u autotidy -f
# Restart
systemctl --user restart autotidy
# Stop
systemctl --user stop autotidy
Uninstall
curl -fsSL https://raw.githubusercontent.com/prettymuchbryce/autotidy/master/install/linux/uninstall.sh | sh
Alternative: deb/rpm packages
You can also install via .deb or .rpm packages from GitHub Releases:
# Debian/Ubuntu
sudo dpkg -i autotidy_*.deb
systemctl --user enable --now autotidy
# Fedora/RHEL
sudo rpm -i autotidy-*.rpm
systemctl --user enable --now autotidy
macOS
Homebrew
brew install prettymuchbryce/tap/autotidy
brew services start autotidy
Verify
autotidy status
Service management
# Stop
brew services stop autotidy
# Restart
brew services restart autotidy
# View logs
tail -f /tmp/autotidy.out.log
tail -f /tmp/autotidy.err.log
Uninstall
brew services stop autotidy
brew uninstall autotidy
Windows
Note: Windows support is experimental. Please open an issue if you run into problems.
Install
irm https://raw.githubusercontent.com/prettymuchbryce/autotidy/master/install/windows/install.ps1 | iex
This downloads the latest release, installs it to %LOCALAPPDATA%\autotidy, registers it to start at login, and starts the daemon.
Verify
& "$env:LOCALAPPDATA\autotidy\autotidy.exe" status
Service management
# Check if running
Get-Process -Name "autotidy" -ErrorAction SilentlyContinue
# Stop
Stop-Process -Name "autotidy" -Force
# Start
Start-Process -FilePath "$env:LOCALAPPDATA\autotidy\autotidy.exe" -ArgumentList "daemon" -WindowStyle Hidden
Uninstall
irm https://raw.githubusercontent.com/prettymuchbryce/autotidy/master/install/windows/uninstall.ps1 | iex
Nix
autotidy provides a Nix flake with modules for NixOS, nix-darwin, and home-manager.
home-manager
Works on both Linux and macOS:
{
inputs.autotidy.url = "github:prettymuchbryce/autotidy";
# Add to your home-manager modules:
imports = [ inputs.autotidy.homeModules.default ];
services.autotidy.enable = true;
}
NixOS
{
inputs.autotidy.url = "github:prettymuchbryce/autotidy";
# Add to your NixOS modules:
imports = [ inputs.autotidy.nixosModules.default ];
services.autotidy.enable = true;
}
nix-darwin
{
inputs.autotidy.url = "github:prettymuchbryce/autotidy";
# Add to your darwin modules:
imports = [ inputs.autotidy.darwinModules.default ];
services.autotidy.enable = true;
}
Verify installation
autotidy status
Configuration
Configuration for autotidy can be found at the following location:
| platform | path |
|---|---|
| Linux | ~/.config/autotidy/config.yaml |
| macOS | ~/.config/autotidy/config.yaml |
| Windows | %APPDATA%\autotidy\config.yaml |
Example
# Moves PDFs and Word docs from Downloads/Desktop to ~/Documents
# organized by extension
rules:
- name: Organize Downloads
locations:
- ~/Downloads
- ~/Desktop
filters:
- extension: [pdf, doc, docx]
actions:
- move: ~/Documents/${ext}
Reference
- Rules - Rule properties and structure
- Filters - Filter types and boolean operators
- Actions - Available actions
- Templates - Variables like
${name},${ext},${date} - Additional Options - Daemon and logging settings
Hot Reload
The autotidy daemon supports hot-reloading of configuration:
autotidy reload
Rules
Each rule defines directories (locations) to be watched. When the contents of those locations change, the containing files which match the filters will have the actions applied against them sequentially.
By default, rules evaluate both files and directories. Use the file_type filter to limit to one or the other.
# Trashes files (not directories) in ~/Downloads that haven't been modified in 30 days
rules:
- name: Clean Old Downloads
locations: ~/Downloads
filters:
- file_type: file
- date_modified:
before:
days_ago: 30
actions:
- trash
Properties
| property | type | default | description |
|---|---|---|---|
name | string | required | Rule identifier (shown in logs and status) |
enabled | bool | true | Whether the rule is active |
recursive | bool | false | Process subdirectories |
traversal | string | depth-first | depth-first or breadth-first |
locations | string/list | required | Directories to watch |
filters | list | - | Filter expressions |
actions | list | required | Actions to execute |
Locations
Locations can be a single path or a list:
# Single location
locations: ~/Downloads
# Multiple locations
locations:
- ~/Downloads
- ~/Desktop
Filters
Filters determine which files are processed. Filters are AND’d together by default.
# Matches PDFs that haven't been modified in 30 days
filters:
- extension: pdf
- date_modified:
before:
days_ago: 30
Use any: for OR logic and not: for negation:
# Matches PDFs or Word documents, excluding any with "_backup" in the name
filters:
- any:
- extension: pdf
- extension: [doc, docx]
- not:
- name: "*_backup*"
| operator | behavior |
|---|---|
| (default) | All filters must match (AND) |
any: | At least one child must match (OR) |
not: | None of the children must match |
See Filters for all available filter types.
Actions
Actions are executed in order for each matching file:
# Logs the file, renames it with a "_backup" suffix, then moves it to ~/Archive
actions:
- log: "Processing ${name}"
- rename: "${name}_backup${ext}"
- move: ~/Archive
See Actions for all available action types.
Recursive
By default, rules only process files directly in the specified locations. Set recursive: true to also process files in subdirectories.
# Moves all PDFs from ~/Downloads and its subdirectories to ~/Documents/PDFs
rules:
- name: Organize All Downloads
locations: ~/Downloads
recursive: true
filters:
- extension: pdf
actions:
- move: ~/Documents/PDFs
Note: Recursion is depth-first by default. This means children are processed before their parents.
Traversal
When recursive: true, the traversal option controls the order files are processed:
depth-first(default) - processes deepest files first, then works upwardbreadth-first- processes files at each level before going deeper
# Deletes empty directories in ~/Downloads, processing deepest folders first
rules:
- name: Clean Empty Folders
locations: ~/Downloads
recursive: true
traversal: depth-first
filters:
- file_type: directory
actions:
- delete
Note: Using
breadth-firstwhen moving, renaming, or copying a directory will result in subsequent actions being performed on the moved/renamed/copied contents.
Filters
Filters determine which files or directories are processed by a rule. They are specified in the filters section of a rule.
Available filters
| filter | description |
|---|---|
| name | Match by filename (glob or regex) |
| extension | Match by file extension |
| file_size | Match by file size |
| file_type | Match by type (file, directory, symlink) |
| date_modified | Match by modification time |
| date_accessed | Match by access time |
| date_created | Match by creation time |
| date_changed | Match by metadata change time |
| mime_type | Match by MIME type |
Filter logic
Filters are AND’d together by default. Use any: for OR logic and not: for negation.
# All filters must match (AND)
filters:
- extension: pdf
- date_modified:
before:
days_ago: 30
# At least one must match (OR)
filters:
- any:
- file_size: "> 100mb"
- date_modified:
before:
days_ago: 30
# None must match (NOT)
filters:
- not:
- name: "*.tmp"
| operator | behavior |
|---|---|
| (default) | All filters must match (AND) |
any: | At least one child must match (OR) |
not: | None of the children must match |
Both any: and not: can be nested for complex boolean expressions.
Examples
Match all files (not directories)
filters:
- file_type: file
Match specific file types
filters:
- extension: [jpg, png, gif]
Match files by name pattern
filters:
- name: "Screenshot*"
Match large files
filters:
- file_size: "> 100mb"
Match old files
filters:
- date_modified:
before:
days_ago: 30
Match old or large files (OR)
filters:
- any:
- file_size: "> 100mb"
- date_modified:
before:
days_ago: 30
Match old PDFs (AND)
filters:
- extension: pdf
- date_modified:
before:
days_ago: 30
Exclude temp files (NOT)
filters:
- not:
- extension: [tmp, temp, bak]
Complex: (old OR large) AND documents AND NOT backup
filters:
- any:
- file_size: "> 100mb"
- date_modified:
before:
days_ago: 90
- extension: [pdf, doc, docx]
- not:
- name: "*_backup*"
name
Matches files by filename using glob patterns or regular expressions.
Syntax
# Glob pattern (shorthand)
- name: "*.txt"
# Glob pattern (explicit)
- name:
glob: "report_*.pdf"
# Regular expression
- name:
regex: "^file_\d{4}\.txt$"
Options
| option | type | description |
|---|---|---|
glob | string | Glob pattern to match |
regex | string | Regular expression pattern |
Only one of glob or regex can be specified.
Glob patterns
The glob pattern is matched against the base filename only (not the full path).
| pattern | matches |
|---|---|
* | Any sequence of characters |
? | Any single character |
[abc] | Any character in the set |
[a-z] | Any character in the range |
** | Any path (in glob context) |
Examples
Match all text files
- name: "*.txt"
Match files starting with “report”
- name: "report*"
Match files with numbers in name
- name:
regex: ".*\d+.*"
Match screenshot files
- name: "Screenshot*"
Exclude hidden files (macOS)
filters:
- not:
- any:
- name: ".*"
- name: .DS_Store
extension
Matches files by their file extension.
Syntax
# Single extension
- extension: pdf
# With dot (also works)
- extension: .pdf
# Multiple extensions
- extension: [pdf, doc, docx]
# Explicit form
- extension:
extensions: [pdf, doc]
Options
| option | type | description |
|---|---|---|
extensions | string/list | One or more extensions to match |
Glob support
Extensions support glob patterns:
# Match doc and docx
- extension: "doc*"
# Match any single-character extension
- extension: "?"
Examples
Match PDF files
- extension: pdf
Match image files
- extension: [jpg, jpeg, png, gif, webp]
Match document files
- extension: [pdf, doc, docx, xls, xlsx, ppt, pptx]
Match video files
- extension: [mp4, mkv, avi, mov, wmv]
Match all Microsoft Office formats
- extension: ["doc*", "xls*", "ppt*"]
file_size
Matches files by their size. Directories are ignored (filter returns false for directories).
Syntax
Shorthand
- file_size: "> 10mb"
- file_size: "<= 500kb"
- file_size: ">= 1gb"
Explicit
- file_size:
greater_than:
mb: 10
- file_size:
between:
min:
mb: 1
max:
gb: 1
Operators
| shorthand | explicit | description |
|---|---|---|
> | greater_than | Strictly greater than |
>= | at_least | Greater than or equal |
< | less_than | Strictly less than |
<= | at_most | Less than or equal |
| - | between | Within a range (inclusive) |
Size units
| unit | bytes |
|---|---|
b | 1 |
kb | 1,024 |
mb | 1,048,576 |
gb | 1,073,741,824 |
tb | 1,099,511,627,776 |
Units are case-insensitive (MB, mb, Mb all work).
Examples
Match large files (> 100MB)
- file_size: "> 100mb"
Match small files (< 1KB)
- file_size: "< 1kb"
Match files in a range
- file_size:
between:
min:
mb: 10
max:
mb: 100
Match empty files
- file_size: "< 1b"
Match files at least 1GB
- file_size: ">= 1gb"
file_type
Matches by file type: regular file, directory, or symlink.
Syntax
# Single type
- file_type: file
# Multiple types
- file_type: [file, directory]
# Explicit form
- file_type:
types: [file, symlink]
Types
| type | aliases | description |
|---|---|---|
file | - | Regular files |
directory | dir, folder | Directories |
symlink | - | Symbolic links |
Examples
Match only files (not directories)
filters:
- file_type: file
Match directories only
filters:
- file_type: directory
Match files and symlinks
filters:
- file_type: [file, symlink]
Exclude directories
filters:
- not:
- file_type: directory
Notes
- Symlinks are checked without following them (uses
lstat) - This filter is useful when processing directories recursively to skip subdirectories
date_modified
Matches files by their last modification time.
Syntax
# Files modified before a relative time
- date_modified:
before:
days_ago: 30
# Files modified after a relative time
- date_modified:
after:
hours_ago: 24
# Files modified before an absolute date
- date_modified:
before:
date: "2024-01-01"
Operators
| operator | description |
|---|---|
before | File was modified before this time |
after | File was modified after this time |
Time specifications
Relative time
| option | description |
|---|---|
seconds_ago | Seconds in the past |
minutes_ago | Minutes in the past |
hours_ago | Hours in the past |
days_ago | Days in the past |
weeks_ago | Weeks in the past |
months_ago | Months in the past (~30.44 days) |
years_ago | Years in the past (~365.25 days) |
Absolute time
| option | format | example |
|---|---|---|
date | YYYY-MM-DD | 2024-01-15 |
date | YYYY-MM-DDTHH:MM:SS | 2024-01-15T14:30:00 |
unix | Unix timestamp | 1704067200 |
Examples
Files older than 30 days
- date_modified:
before:
days_ago: 30
Files modified in the last hour
- date_modified:
after:
hours_ago: 1
Files modified before 2024
- date_modified:
before:
date: "2024-01-01"
Files modified in the last week
- date_modified:
after:
weeks_ago: 1
Example: clean old downloads
rules:
- name: Archive Old Downloads
locations: ~/Downloads
filters:
- date_modified:
before:
days_ago: 90
actions:
- move: ~/Archive/Downloads
date_accessed
Matches files by their last access time.
Syntax
# Files not accessed in 30 days
- date_accessed:
before:
days_ago: 30
# Files accessed recently
- date_accessed:
after:
hours_ago: 24
Operators
| operator | description |
|---|---|
before | File was accessed before this time |
after | File was accessed after this time |
Time specifications
See date_modified for all available time specification options:
- Relative:
seconds_ago,minutes_ago,hours_ago,days_ago,weeks_ago,months_ago,years_ago - Absolute:
date(YYYY-MM-DD),unix(timestamp)
Examples
Files not accessed in 90 days
- date_accessed:
before:
days_ago: 90
Files accessed today
- date_accessed:
after:
hours_ago: 24
Notes
- Access time tracking may be disabled on some filesystems for performance
- On Linux, the
noatimemount option disables access time updates - macOS may not update access times in all cases
date_created
Matches files by their creation time (birth time).
Syntax
# Files created more than 30 days ago
- date_created:
before:
days_ago: 30
# Files created recently
- date_created:
after:
hours_ago: 24
Operators
| operator | description |
|---|---|
before | File was created before this time |
after | File was created after this time |
Time specifications
See date_modified for all available time specification options:
- Relative:
seconds_ago,minutes_ago,hours_ago,days_ago,weeks_ago,months_ago,years_ago - Absolute:
date(YYYY-MM-DD),unix(timestamp)
Examples
Files created in the last week
- date_created:
after:
weeks_ago: 1
Files created before 2024
- date_created:
before:
date: "2024-01-01"
Platform support
| platform | support |
|---|---|
| macOS | Full support |
| Windows | Full support |
| Linux | Depends on filesystem (ext4 4.11+, btrfs) |
On filesystems that don’t track creation time, this filter may not work as expected.
date_changed
Matches files by their metadata change time (ctime).
Syntax
# Files with metadata changed more than 30 days ago
- date_changed:
before:
days_ago: 30
# Files with recent metadata changes
- date_changed:
after:
hours_ago: 24
Operators
| operator | description |
|---|---|
before | Metadata was changed before this time |
after | Metadata was changed after this time |
Time specifications
See date_modified for all available time specification options:
- Relative:
seconds_ago,minutes_ago,hours_ago,days_ago,weeks_ago,months_ago,years_ago - Absolute:
date(YYYY-MM-DD),unix(timestamp)
What is ctime?
The “change time” (ctime) is updated when file metadata changes, including:
- Permission changes (
chmod) - Ownership changes (
chown) - Link count changes (hard links created/removed)
- File content modifications (also updates mtime)
This is different from modification time (mtime), which only updates when file content changes.
Examples
Files with old metadata
- date_changed:
before:
days_ago: 90
Recently modified files (including metadata)
- date_changed:
after:
hours_ago: 1
Notes
- On Unix systems, ctime cannot be set manually
- Copying a file typically resets its ctime to the current time
- On Windows, this typically maps to the file’s change time
mime_type
Matches files by their MIME type, detected from file content (not extension).
Syntax
# Single MIME type
- mime_type: "image/png"
# Wildcard pattern
- mime_type: "image/*"
# Multiple types
- mime_type: ["image/*", "video/*"]
# Explicit form
- mime_type:
mime_types: ["application/pdf", "application/msword"]
Glob patterns
MIME type patterns support glob matching:
| pattern | matches |
|---|---|
image/* | All image types |
video/* | All video types |
text/* | All text types |
application/* | All application types |
Common MIME types
Images
image/jpeg- JPEG imagesimage/png- PNG imagesimage/gif- GIF imagesimage/webp- WebP imagesimage/svg+xml- SVG images
Documents
application/pdf- PDF documentsapplication/msword- Word documents (.doc)application/vnd.openxmlformats-officedocument.wordprocessingml.document- Word (.docx)
Video
video/mp4- MP4 videovideo/x-matroska- MKV videovideo/quicktime- QuickTime video
Audio
audio/mpeg- MP3 audioaudio/wav- WAV audioaudio/flac- FLAC audio
Archives
application/zip- ZIP archivesapplication/x-tar- TAR archivesapplication/gzip- Gzip compressed
Examples
Match all images
- mime_type: "image/*"
Match videos and images
- mime_type: ["image/*", "video/*"]
Match PDF files
- mime_type: "application/pdf"
Match text files (including code)
- mime_type: "text/*"
Notes
- MIME type is detected from file content, not the file extension
- This is more accurate than extension-based filtering, but slower as the file needs to be read
- Directories always return
false(no MIME type) - Detection uses the first few bytes of the file (magic numbers)
Actions
Actions define what happens to files that match your filters. Each rule can have one or more actions that execute in order.
Available actions
| action | description |
|---|---|
| move | Move files to a new location |
| copy | Copy files with a new name in the same directory |
| rename | Rename files in place |
| delete | Permanently delete files |
| trash | Move files to system trash |
| log | Log a message (for debugging/testing) |
Action syntax
Actions are defined as a list under the actions key:
rules:
- name: Example Rule
locations: ~/Downloads
actions:
- move: ~/Documents
- log: "Moved ${name}"
Multiple actions
Actions execute in order. This allows chaining operations:
actions:
- copy: "${name}_backup${ext}" # First, create a backup copy
- move: ~/Documents # Then, move the original
- log: "Processed ${name}"
Template variables
Most actions support template variables in paths and messages:
| variable | description |
|---|---|
${name} | Filename without extension |
${ext} | File extension (with dot) |
%Y | Current year (4 digits) |
%m | Current month (01-12) |
%d | Current day (01-31) |
%H | Current hour (00-23) |
%M | Current minute (00-59) |
%S | Current second (00-59) |
See Templates for full details.
Conflict handling
Actions that create files (move, copy, rename) support conflict handling:
actions:
- move:
dest: ~/Documents
on_conflict: skip # Don't move if destination exists
| mode | behavior |
|---|---|
rename_with_suffix | Add numeric suffix (file_2.txt, file_3.txt, etc.) |
skip | Don’t move/copy if destination exists |
overwrite | Replace existing file |
Default is rename_with_suffix.
Dry run mode
Use --dry-run to preview actions without executing them:
autotidy run --dry-run
move
Moves files to a specified destination directory.
Syntax
# Simple form - just the destination
- move: ~/Documents
# Explicit form with options
- move:
dest: ~/Documents
on_conflict: skip
Options
| option | type | required | default | description |
|---|---|---|---|---|
dest | string | Yes | - | Destination directory path |
on_conflict | string | No | rename_with_suffix | How to handle existing files |
Conflict handling
| mode | behavior |
|---|---|
rename_with_suffix | Add numeric suffix (file_2.txt, file_3.txt, etc.) |
skip | Don’t move if destination file exists |
overwrite | Replace existing destination file |
Examples
Move downloads to documents
- move: ~/Documents
Move, skipping files on conflict
- move:
dest: ~/Documents
on_conflict: skip
Organize by date
- move: ~/Photos/%Y/%m
Move to categorized folders
rules:
- name: Organize Images
locations: ~/Downloads
filters:
- extension: [jpg, png, gif]
actions:
- move: ~/Pictures/Downloads
- name: Organize Documents
locations: ~/Downloads
filters:
- extension: [pdf, doc, docx]
actions:
- move: ~/Documents/Downloads
Template variables
The destination directory supports template variables:
- move: ~/Archive/%Y/%m
See Templates for all available variables.
Notes
- The destination must be a directory, not a file path
- The original filename is preserved (use rename to change filenames)
- Creates destination directories if they don’t exist
- Default conflict handling adds a numeric suffix (file_2.txt)
copy
Copies a file to a new name in the same directory.
Syntax
# Simple form - just the new filename
- copy: backup.txt
# With template
- copy: "${name}_backup${ext}"
# With conflict handling
- copy:
new_name: "${name}_backup${ext}"
on_conflict: overwrite
Options
| option | type | required | default | description |
|---|---|---|---|---|
new_name | string | Yes | - | New filename (supports templates) |
on_conflict | string | No | rename_with_suffix | How to handle existing files |
Conflict handling
| mode | behavior |
|---|---|
rename_with_suffix | Add numeric suffix (file_2.txt, file_3.txt, etc.). This is the default |
skip | Don’t copy if destination file exists |
overwrite | Replace existing destination file |
Examples
Simple copy
- copy: backup.txt
Copy with original name preserved
- copy: "${name}_copy${ext}"
Copy with timestamp
- copy: "${name}_%Y%m%d${ext}"
Backup with overwrite
- copy:
new_name: "${name}_backup${ext}"
on_conflict: overwrite
Template variables
The new_name field supports template variables:
- copy: "${name}_%H%M${ext}"
See Templates for all available variables.
Notes
- Copies to the same directory as the source file
- The
new_namemust not contain path separators - Original file remains unchanged
- To copy to a different directory, first
copythe file and then use the move action
rename
Renames files in place.
Syntax
# Simple form - new name with template
- rename: "${name}_archived${ext}"
# Explicit form with options
- rename:
new_name: "${name}_%Y%m%d${ext}"
on_conflict: rename_with_suffix
Options
| option | type | required | default | description |
|---|---|---|---|---|
new_name | string | Yes | - | New filename (supports templates) |
on_conflict | string | No | rename_with_suffix | How to handle existing files |
Conflict handling
| mode | behavior |
|---|---|
rename_with_suffix | Add numeric suffix (file_2.txt, file_3.txt, etc.) |
skip | Don’t rename if a file with the new name exists |
overwrite | Replace existing file with the new name |
Examples
Add date suffix
- rename: "${name}_%Y%m%d${ext}"
Preserve original name
- rename: "${name}${ext}"
Add prefix
- rename: "archived_${name}${ext}"
Rename with skip on conflict
- rename:
new_name: "processed_${name}${ext}"
on_conflict: skip
Standardize filenames
rules:
- name: Rename Screenshots
locations: ~/Desktop
filters:
- name: "Screen Shot*"
actions:
- rename: "screenshot_%Y%m%d_%H%M%S${ext}"
Template variables
The new_name field supports template variables:
| variable | description | example |
|---|---|---|
${name} | Filename without extension | document |
${ext} | File extension (with dot) | .pdf |
%Y | Year (4 digits) | 2024 |
%m | Month (01-12) | 03 |
%d | Day (01-31) | 15 |
%H | Hour (00-23) | 14 |
%M | Minute (00-59) | 30 |
%S | Second (00-59) | 45 |
See Templates for full details.
Notes
- File stays in the same directory
- Only the filename changes, not the location
- Default conflict handling adds a numeric suffix (file_2.txt)
- Use rename and move in succession to rename a file and move it to another directory
- Template variables are evaluated at the time of action execution
delete
Permanently deletes files and directories. For safer deletes consider trash instead.
Syntax
- delete
Options
This action has no options.
Examples
Delete old temp files
rules:
- name: Clean Temp Files
locations: ~/tmp
filters:
- date_modified:
before:
days_ago: 7
actions:
- delete
Delete by extension
rules:
- name: Remove Log Files
locations: ~/Projects
subfolders: true
filters:
- extension: log
- date_modified:
before:
days_ago: 30
actions:
- delete
Delete with size filter
rules:
- name: Remove Large Temp Files
locations: /tmp
filters:
- size:
gt: 100MB
- extension: [tmp, temp, cache]
actions:
- delete
Safety recommendations
Always use dry run first
autotidy run --dry-run
Combine with specific filters
Don’t use delete without filters:
# DANGEROUS - deletes everything!
rules:
- name: Bad Rule
locations: ~/Documents
actions:
- delete
# SAFE - specific filters
rules:
- name: Good Rule
locations: ~/Documents
filters:
- extension: tmp
- date_modified:
before:
days_ago: 30
actions:
- delete
Consider using trash instead
For most cases, trash is safer:
# Recoverable
- trash
# Permanent
- delete
Notes
- Permanent: Files cannot be recovered after deletion
- No confirmation: Executes immediately when rules match
Trash
Moves files to the system trash/recycle bin. This is a safer alternative to delete since files can be recovered.
Syntax
- trash
Options
This action has no options.
Examples
Clean old downloads
rules:
- name: Trash Old Downloads
locations: ~/Downloads
filters:
- date_modified:
before:
days_ago: 30
actions:
- trash
Trash old backups
rules:
- name: Trash Old Backups
locations: ~/Backup
filters:
- name: "*_backup_*"
- date_modified:
before:
weeks_ago: 4
actions:
- trash
Trash temporary files
rules:
- name: Clean Temp Files
locations: ~/Projects
subfolders: true
filters:
- extension: [tmp, temp, bak, swp]
actions:
- trash
Platform behavior
| platform | trash location |
|---|---|
| macOS | ~/.Trash |
| Linux | ~/.local/share/Trash (freedesktop.org spec) |
| Windows | Recycle Bin |
Comparison with delete
| aspect | trash | delete |
|---|---|---|
| Recoverable | Yes | No |
| Disk space | Still used until emptied | Freed immediately |
| Speed | Slightly slower | Faster |
| Safety | Safe | Dangerous |
Notes
- Safer than
deletefor automated cleanup rules - Uses the system’s native trash mechanism
- On Linux, follows the freedesktop.org trash specification
log
Logs a message when a file matches. Useful for debugging rules and testing configurations.
Syntax
# Simple form - just the message
- log: "Found file: ${name}${ext}"
# Explicit form with level
- log:
msg: "Processing ${name}"
level: info
Options
| option | type | required | default | description |
|---|---|---|---|---|
msg | string | Yes | - | Message to log (supports templates) |
level | string | No | info | Log level |
Log levels
| level | description |
|---|---|
debug | Detailed debugging information |
info | General information (default) |
warn | Warning messages |
error | Error messages |
Examples
Simple logging
- log: "Matched: ${name}${ext}"
Debug logging
- log:
msg: "File details: ${name}, ext: ${ext}"
level: debug
Log before action
actions:
- log: "Moving ${name}${ext} to archive"
- move: ~/Archive
Testing rules
rules:
- name: Test Image Filter
locations: ~/Downloads
filters:
- extension: [jpg, png, gif]
actions:
- log: "Would process image: ${name}${ext}"
# Comment out actual action while testing
# - move: ~/Pictures
Conditional logging
rules:
- name: Large File Warning
locations: ~/Downloads
filters:
- size:
gt: 1GB
actions:
- log:
msg: "Large file detected: ${name}${ext}"
level: warn
Template variables
The msg field supports all template variables:
- log: "File: ${name}${ext}, Year: %Y, Month: %m"
See Templates for all available variables.
Use cases
Audit trail
Log actions for review:
rules:
- name: Archive with Logging
locations: ~/Downloads
filters:
- extension: pdf
actions:
- log:
msg: "Archiving PDF: ${name}"
level: info
- move: ~/Archive/PDFs
Notes
- Does not modify files in any way
- Output goes to autotidy’s log output
- Useful during rule development and debugging
- Can be combined with other actions in the same rule
Templates
Templates allow you to use dynamic values in action parameters like destination paths and new filenames. Variables are replaced with actual values when the action executes.
Syntax
Template variables use the ${variable} syntax:
- move: ~/Archive/%Y/%m
- rename: "${name}_backup${ext}"
File variables
| variable | description | example |
|---|---|---|
${name} | Filename without extension | document |
${ext} | File extension (with dot) | .pdf |
Examples
For a file named report.pdf:
| template | result |
|---|---|
${name} | report |
${ext} | .pdf |
${name}${ext} | report.pdf |
${name}_copy${ext} | report_copy.pdf |
backup_${name}${ext} | backup_report.pdf |
Time variables
Time variables use strftime format tokens:
| token | description | example |
|---|---|---|
%Y | Year (4 digits) | 2024 |
%m | Month (01-12) | 03 |
%d | Day (01-31) | 15 |
%H | Hour (00-23) | 14 |
%M | Minute (00-59) | 30 |
%S | Second (00-59) | 45 |
Common Patterns
| pattern | example output |
|---|---|
%Y-%m-%d | 2024-03-15 |
%Y%m%d | 20240315 |
%Y/%m/%d | 2024/03/15 |
%H:%M:%S | 14:30:45 |
%Y%m%d_%H%M%S | 20240315_143045 |
Additional Time Tokens
| token | description | example |
|---|---|---|
%y | Year (2 digits) | 24 |
%B | Month name (full) | March |
%b | Month name (abbr) | Mar |
%A | Weekday name (full) | Friday |
%a | Weekday name (abbr) | Fri |
%j | Day of year (001-366) | 074 |
%U | Week number (00-53) | 11 |
%W | Week number (Monday start) | 10 |
Examples
Organize by date
rules:
- name: Organize Downloads
locations: ~/Downloads
actions:
- move: ~/Archive/%Y/%m/%d
Files are organized into folders like ~/Archive/2024/03/15/.
Add timestamp to filename
rules:
- name: Timestamp Files
locations: ~/Documents
filters:
- extension: pdf
actions:
- rename: "${name}_%Y%m%d${ext}"
report.pdf becomes report_20240315.pdf.
Create dated copies
rules:
- name: Daily Backup
locations: ~/Documents
actions:
- copy: "${name}_%Y%m%d${ext}"
Creates a timestamped copy of each file in the same directory.
Organize photos by date
rules:
- name: Organize Photos
locations: ~/Downloads
filters:
- extension: [jpg, jpeg, png, heic]
actions:
- move: ~/Pictures/%Y/%B
Photos organized into folders like ~/Pictures/2024/March/.
Archive old files
rules:
- name: Archive Old Downloads
locations: ~/Downloads
filters:
- date_modified:
before:
days_ago: 30
actions:
- move: ~/Archive/Downloads/%Y-%m
Unique filenames with timestamp
rules:
- name: Rename Duplicates
locations: ~/Downloads
actions:
- rename: "${name}_%Y%m%d_%H%M%S${ext}"
Combining variables
Mix file and time variables:
- move: ~/Archive/%Y/${name}/%m
- rename: "${name}_%Y%m%d_%H%M%S${ext}"
- copy: "${name}_backup_%Y%m%d${ext}"
Where templates work
Templates are supported in:
| action | fields |
|---|---|
move | dest |
copy | new_name |
rename | new_name |
log | msg |
Notes
- Time values are evaluated at action execution time
- File variables (
${name},${ext}) come from the matched file - Unknown variables are left unchanged in the output
- Paths are created automatically if they don’t exist
Additional options
Daemon
daemon:
debounce: 500ms
| property | type | default | description |
|---|---|---|---|
debounce | duration | 500ms | Wait for filesystem activity to settle before executing rules |
The debounce prevents rapid re-execution when files are being written or modified in quick succession. Decreasing it will make rule invocations more responsive, but may reduce performance.
Logging
logging:
level: warn
| property | type | default | description |
|---|---|---|---|
level | string | warn | Log level: debug, info, warn, error |