Skip to content

Repository files navigation

Moni CLI

GitHub release GitHub actions

| Installation | Usage | Flags | How It Works | Contributing |

moni renames generic files (like image.jpg or download (1).png) into a clean, chronological, fingerprint-based format (YYYYMMDD_HHMMSS_<hash>.ext) without overwriting existing files.

$ moni ~/Downloads

- downloads/image.jpg
+ downloads/20260127_150405_a1b2c3d4e5f6.jpg

- downloads/image (1).jpg
+ downloads/20260127_150405_a1b2c3d4e5f6_1.jpg

Apply these changes? [y/N]: y
Done! Renamed 2 file(s).

Installation

Pre-built Binaries (Recommended)

This is the fastest way — you don't even need to have Go installed on your system. Each release provides archives for Linux, macOS, and Windows on amd64 and arm64, plus a SHA-256 checksums.txt manifest.

  1. Go to the Releases page.
  2. Download the archive for your operating system and architecture.
  3. Extract the moni executable and move it to a directory in your system's PATH (e.g., /usr/local/bin on Linux/macOS).

Go Install

If you have Go 1.21+ installed:

go install github.com/kirinyoku/moni@latest

NOTE: Make sure $GOPATH/bin is in your system's PATH to run moni from any directory.

Build from Source

git clone https://github.com/kirinyoku/moni.git
cd moni
make build
# Binary created at bin/moni

Usage

Basic

# Rename a single file
moni path/to/image.png

# Rename all regular files in a directory
moni ~/Downloads/

# Rename multiple specific files and folders
moni file1.png folder/ file2.mp4

Filtering & Depth Control

# Only process specific file extensions
moni ~/Downloads/ --include="*.jpg,*.png,*.webp"

# Exclude temporary or unfinished download files
moni ~/Downloads/ --exclude="*.tmp,*.part,*.crdownload"

# Process only the top-level directory (do not enter subfolders)
moni ~/Downloads/ --max-depth=1

Automation & Rollback

# Skip interactive prompt and apply renames immediately
moni -y ~/Downloads/

# Quiet mode: suppress all output on success (ideal for cron and CI)
moni -q -y ~/Downloads/

# Undo the most recent rename operation
moni --rollback

# Undo silently
moni --rollback -q -y

Pipelines & Standard Input

# Pipe paths from find or other tools (pass -y to apply changes)
find ~/Downloads -type f -name "*.png" | moni -y

# Read null-delimited paths (-0 / --null) to safely handle spaces and special characters
find ~/Downloads -type f -print0 | moni -y -0

# Read paths from standard input explicitly using '-'
git status --porcelain | awk '{print $2}' | moni -y -

Machine-Readable Downstream Output

# Output only renamed paths (pipe into downstream tools like xargs or s3cmd)
moni -y --print ~/Downloads | xargs -I{} aws s3 cp {} s3://my-bucket/

# Output null-separated paths for safe xargs -0 processing
moni -y --print -0 ~/Downloads | xargs -0 -I{} process-file {}

# Output structured JSON mapping of old-to-new paths
moni -y --format=json ~/Downloads | jq '.[] | {from: .old_path, to: .new_path}'

# Output tab-separated mapping (TSV) for processing with awk or while read
moni -y --format=tsv ~/Downloads | while IFS=$'\t' read -r old new; do
  ln -s "$new" "$old"
done

Flags

Flag Description Default
-y Skip interactive prompt and apply changes immediately false
--print Output only resulting renamed paths to stdout (one per line, or \0 with -0) false
--format Output structured mapping to stdout ('json' or 'tsv') ""
-q, --quiet Suppress diffs, status headers, and completion summaries on success false
-0, --null Read input paths separated by NUL (\0) characters instead of newlines false
-v Enable verbose logging (shows skipped/excluded files on stderr when piping) false
--max-depth Maximum directory recursion depth (0 = unlimited) 0
--include Comma-separated glob patterns to include (e.g. '*.jpg,*.png') ""
--exclude Comma-separated glob patterns to exclude (e.g. '*.tmp,*.part') ""
--rollback Restore the latest batch of renamed files false
--cache-dir Print the rollback journal location and exit false
--version Print current moni version and exit false

How It Works

1. Filename Anatomy

20260127_150405_a1b2c3d4e5f6.jpg
└───┬─────────┘ └────┬─────┘ └─┬┘
    │                │         └─── Preserved original extension
    │                └───────────── 12-char BLAKE3 content fingerprint
    └────────────────────────────── UTC modification timestamp (YYYYMMDD_HHMMSS)

2. Adaptive Fingerprinting

  • Small files (≤ 128 KB): The entire file content is hashed to guarantee absolute precision.
  • Large files (> 128 KB): Computes a composite hash across the first 4 KB, the last 4 KB, and the 64-bit file size. This prevents heavy I/O spikes and enables instant processing of multi-gigabyte video files or raw images.

3. Duplicate Disambiguation & Idempotency

  • If two files in the same batch share identical content and timestamps (or if a destination name already exists on disk), moni automatically resolves the collision by appending sequential counter suffixes: ..._1.ext, ..._2.ext.
  • Running moni repeatedly on an already processed directory is completely idempotent: correctly formatted files are skipped automatically.

Contributing

Contributions, bug reports, and pull requests are welcome!

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Ensure tests and lint pass (make test && make lint)
  4. Commit your changes (git commit -m 'feat: add amazing feature')
  5. Push to the branch (git push origin feature/amazing-feature)
  6. Open a Pull Request

About

A simple CLI tool to organize and rename generic files into deterministic, collision-safe timestamped fingerprints.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages