rnmtool 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
rnmtool/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """
2
+ rnmtool — A flexible, zero-dependency file renaming utility.
3
+ """
4
+
5
+ __version__ = "1.0.0"
6
+ __author__ = "Your Name"
7
+ __license__ = "MIT"
rnmtool/__main__.py ADDED
@@ -0,0 +1,210 @@
1
+ """
2
+ rnmtool.__main__ - Entry point for the rnmtool CLI.
3
+
4
+ Features:
5
+ - Batch rename files in a directory
6
+ - Add prefix or suffix to filenames
7
+ - Find-and-replace text in filenames (with optional regex support)
8
+ - Change file extensions
9
+ - Convert filenames to lowercase, uppercase, or title case
10
+ - Dry-run mode to preview changes before applying them
11
+
12
+ Usage:
13
+ rnmtool --help
14
+ python -m rnmtool --help
15
+ """
16
+
17
+ import os
18
+ import re
19
+ import argparse
20
+ from pathlib import Path
21
+
22
+
23
+ # ── Helpers ────────────────────────────────────────────────────────────────────
24
+
25
+ def collect_files(directory: str, recursive: bool, pattern: str) -> list[Path]:
26
+ """Return a sorted list of files matching *pattern* inside *directory*."""
27
+ base = Path(directory)
28
+ glob = "**/" + pattern if recursive else pattern
29
+ return sorted(p for p in base.glob(glob) if p.is_file())
30
+
31
+
32
+ def apply_transformations(name: str, args: argparse.Namespace) -> str:
33
+ """Apply all requested transformations to a bare filename (no directory)."""
34
+ stem, suffix = os.path.splitext(name)
35
+
36
+ # 1. Find-and-replace (plain text or regex)
37
+ if args.find is not None:
38
+ if args.regex:
39
+ stem = re.sub(args.find, args.replace or "", stem)
40
+ else:
41
+ stem = stem.replace(args.find, args.replace or "")
42
+
43
+ # 2. Case conversion
44
+ if args.case == "lower":
45
+ stem = stem.lower()
46
+ elif args.case == "upper":
47
+ stem = stem.upper()
48
+ elif args.case == "title":
49
+ stem = stem.title()
50
+
51
+ # 3. Prefix / suffix
52
+ if args.prefix:
53
+ stem = args.prefix + stem
54
+ if args.suffix:
55
+ stem = stem + args.suffix
56
+
57
+ # 4. Extension replacement
58
+ if args.ext:
59
+ ext = args.ext if args.ext.startswith(".") else "." + args.ext
60
+ else:
61
+ ext = suffix
62
+
63
+ return stem + ext
64
+
65
+
66
+ def preview_table(pairs: list[tuple[Path, Path]]) -> None:
67
+ """Print a formatted before/after table."""
68
+ if not pairs:
69
+ print(" (no files matched)")
70
+ return
71
+
72
+ col = max(len(p[0].name) for p in pairs)
73
+ print(f"\n {'BEFORE':<{col}} AFTER")
74
+ print(" " + "─" * col + " " + "─" * 40)
75
+ for old, new in pairs:
76
+ changed = "✓" if old.name != new.name else " "
77
+ print(f" {old.name:<{col}} {new.name} {changed}")
78
+ print()
79
+
80
+
81
+ # ── Core logic ─────────────────────────────────────────────────────────────────
82
+
83
+ def run(args: argparse.Namespace) -> None:
84
+ files = collect_files(args.directory, args.recursive, args.pattern)
85
+
86
+ if not files:
87
+ print(f"[rnmtool] No files matching '{args.pattern}' found in '{args.directory}'.")
88
+ return
89
+
90
+ pairs: list[tuple[Path, Path]] = []
91
+ for old_path in files:
92
+ new_name = apply_transformations(old_path.name, args)
93
+ new_path = old_path.parent / new_name
94
+ pairs.append((old_path, new_path))
95
+
96
+ if args.dry_run:
97
+ print("[rnmtool] DRY RUN - no files will be changed.")
98
+ preview_table(pairs)
99
+ return
100
+
101
+ renamed, skipped, errors = 0, 0, 0
102
+ for old_path, new_path in pairs:
103
+ if old_path == new_path:
104
+ skipped += 1
105
+ continue
106
+ if new_path.exists() and not args.overwrite:
107
+ print(f" [SKIP] '{new_path.name}' already exists. Use --overwrite to replace.")
108
+ skipped += 1
109
+ continue
110
+ try:
111
+ old_path.rename(new_path)
112
+ print(f" [OK] '{old_path.name}' → '{new_path.name}'")
113
+ renamed += 1
114
+ except OSError as exc:
115
+ print(f" [ERR] '{old_path.name}': {exc}")
116
+ errors += 1
117
+
118
+ print(f"\n[rnmtool] Done. Renamed: {renamed}, Skipped: {skipped}, Errors: {errors}")
119
+
120
+
121
+ # ── CLI ────────────────────────────────────────────────────────────────────────
122
+
123
+ def build_parser() -> argparse.ArgumentParser:
124
+ p = argparse.ArgumentParser(
125
+ prog="rnmtool",
126
+ description="Flexible file renaming utility.",
127
+ formatter_class=argparse.RawDescriptionHelpFormatter,
128
+ epilog="""
129
+ Examples:
130
+ # Preview renaming all .txt files in the current directory
131
+ rnmtool --dry-run --pattern "*.txt"
132
+
133
+ # Add a prefix to every JPEG
134
+ rnmtool --pattern "*.jpg" --prefix "2025_"
135
+
136
+ # Replace spaces with underscores in all filenames
137
+ rnmtool --find " " --replace "_"
138
+
139
+ # Convert all .mp3 filenames to lowercase
140
+ rnmtool --pattern "*.mp3" --case lower
141
+
142
+ # Change extension from .jpeg to .jpg
143
+ rnmtool --pattern "*.jpeg" --ext .jpg
144
+
145
+ # Regex: remove leading digits from filenames
146
+ rnmtool --regex --find "^\\d+_?" --replace ""
147
+ """,
148
+ )
149
+
150
+ p.add_argument(
151
+ "directory",
152
+ nargs="?",
153
+ default=".",
154
+ help="Target directory (default: current directory).",
155
+ )
156
+ p.add_argument(
157
+ "--pattern", "-p",
158
+ default="*",
159
+ metavar="GLOB",
160
+ help="Glob pattern to filter files (default: '*').",
161
+ )
162
+ p.add_argument(
163
+ "--recursive", "-r",
164
+ action="store_true",
165
+ help="Search for files recursively.",
166
+ )
167
+ p.add_argument(
168
+ "--dry-run", "-n",
169
+ action="store_true",
170
+ help="Preview changes without renaming anything.",
171
+ )
172
+ p.add_argument(
173
+ "--overwrite",
174
+ action="store_true",
175
+ help="Allow overwriting existing files.",
176
+ )
177
+
178
+ # Transformation options
179
+ t = p.add_argument_group("transformations")
180
+ t.add_argument("--find", "-f", metavar="TEXT", help="Text (or regex) to search for in filenames.")
181
+ t.add_argument("--replace", "-s", metavar="TEXT", default="", help="Replacement text (default: remove match).")
182
+ t.add_argument("--regex", "-x", action="store_true", help="Treat --find as a regular expression.")
183
+ t.add_argument("--prefix", metavar="TEXT", help="Prepend text to each filename.")
184
+ t.add_argument("--suffix", metavar="TEXT", help="Append text to each filename (before extension).")
185
+ t.add_argument("--ext", metavar="EXT", help="Replace the file extension (e.g. '.jpg').")
186
+ t.add_argument(
187
+ "--case", "-c",
188
+ choices=["lower", "upper", "title"],
189
+ help="Convert filename case.",
190
+ )
191
+
192
+ return p
193
+
194
+
195
+ def main() -> None:
196
+ parser = build_parser()
197
+ args = parser.parse_args()
198
+
199
+ if not Path(args.directory).is_dir():
200
+ parser.error(f"Directory not found: '{args.directory}'")
201
+
202
+ if args.find is None and args.prefix is None and args.suffix is None \
203
+ and args.ext is None and args.case is None and not args.dry_run:
204
+ parser.error("No transformation specified. Use --help for usage or --dry-run to preview files.")
205
+
206
+ run(args)
207
+
208
+
209
+ if __name__ == "__main__":
210
+ main()
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aayush Goswami
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,468 @@
1
+ Metadata-Version: 2.2
2
+ Name: rnmtool
3
+ Version: 1.0.0
4
+ Summary: A powerful, zero-dependency cross-platform CLI file renaming utility
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/AayushGoswami/rnmtool
7
+ Project-URL: Bug Tracker, https://github.com/AayushGoswami/rnmtool/issues
8
+ Project-URL: Changelog, https://github.com/AayushGoswami/rnmtool/blob/main/CHANGELOG.md
9
+ Keywords: rename,cli,files,batch,utility,tool,terminal
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Environment :: Console
18
+ Classifier: Topic :: Utilities
19
+ Classifier: Topic :: System :: Filesystems
20
+ Classifier: Intended Audience :: Developers
21
+ Classifier: Intended Audience :: System Administrators
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+
26
+ # 🗂️ rnmtool - File Rename Utility
27
+
28
+ > A powerful, **zero-dependency**, cross-platform CLI for batch renaming files - with a safe dry-run preview.
29
+
30
+ ![PyPI version](https://img.shields.io/pypi/v/rnmtool)
31
+ ![Python](https://img.shields.io/pypi/pyversions/rnmtool)
32
+ ![License](https://img.shields.io/github/license/AayushGoswami/rnmtool)
33
+ ![CI](https://img.shields.io/github/actions/workflow/status/AayushGoswami/rnmtool/ci.yml?label=CI)
34
+ ![Stars](https://img.shields.io/github/stars/AayushGoswami/rnmtool?style=social)
35
+
36
+ ```bash
37
+ pipx install rnmtool
38
+ ```
39
+
40
+ > Works on **Windows, macOS, and Linux** - invoke from any folder, any terminal.
41
+
42
+ ---
43
+
44
+ ## ✨ Why rnmtool?
45
+
46
+ | Feature | rnmtool | Manual renaming | PowerShell scripts |
47
+ |---|---|---|---|
48
+ | Safe dry-run preview | ✅ | ❌ | ❌ |
49
+ | Regex support | ✅ | ❌ | ⚠️ complex |
50
+ | Zero dependencies | ✅ | - | ✅ |
51
+ | Works on Win / Mac / Linux | ✅ | ❌ | ❌ |
52
+ | One-word invocation | ✅ | ❌ | ❌ |
53
+ | Glob file filtering | ✅ | ❌ | ⚠️ complex |
54
+ | Recursive directory support | ✅ | ❌ | ⚠️ complex |
55
+
56
+ ---
57
+
58
+ ## 📋 Table of Contents
59
+
60
+ - [Installation](#installation)
61
+ - [Quick Start](#quick-start)
62
+ - [All Options](#all-options)
63
+ - [Tutorials](#tutorials)
64
+ - [1. Dry-Run Preview](#1-dry-run-preview)
65
+ - [2. Add a Prefix](#2-add-a-prefix)
66
+ - [3. Add a Suffix](#3-add-a-suffix)
67
+ - [4. Find & Replace Text](#4-find--replace-text)
68
+ - [5. Regex Find & Replace](#5-regex-find--replace)
69
+ - [6. Convert Case](#6-convert-case)
70
+ - [7. Change File Extension](#7-change-file-extension)
71
+ - [8. Filter by Glob Pattern](#8-filter-by-glob-pattern)
72
+ - [9. Recursive Renaming](#9-recursive-renaming)
73
+ - [10. Combine Multiple Transformations](#10-combine-multiple-transformations)
74
+ - [Test Files](#test-files)
75
+ - [Contributing](#contributing)
76
+ - [Safety Notes](#safety-notes)
77
+
78
+ ---
79
+
80
+ ## Installation
81
+
82
+ ### Recommended: `pipx` (cross-platform, isolated)
83
+
84
+ ```bash
85
+ pipx install rnmtool
86
+ ```
87
+
88
+ > Don't have pipx? Install it first: `pip install pipx`
89
+
90
+ ### Alternative: `pip`
91
+
92
+ ```bash
93
+ pip install rnmtool
94
+ ```
95
+
96
+ ### One-command installer scripts
97
+
98
+ **Windows (PowerShell):**
99
+ ```powershell
100
+ irm https://raw.githubusercontent.com/AayushGoswami/rnmtool/main/scripts/install.ps1 | iex
101
+ ```
102
+
103
+ **Linux / macOS (Bash):**
104
+ ```bash
105
+ curl -fsSL https://raw.githubusercontent.com/AayushGoswami/rnmtool/main/scripts/install.sh | bash
106
+ ```
107
+
108
+ ### Verify installation
109
+
110
+ ```bash
111
+ rnmtool --help
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Quick Start
117
+
118
+ ```bash
119
+ # Always preview first - no files are changed
120
+ rnmtool test_files --dry-run --find " " --replace "_"
121
+
122
+ # If the preview looks good, apply (remove --dry-run)
123
+ rnmtool test_files --find " " --replace "_"
124
+
125
+ # Run from inside the target folder (no directory argument needed)
126
+ cd /path/to/my/folder
127
+ rnmtool --dry-run --case lower
128
+ ```
129
+
130
+ ---
131
+
132
+ ## All Options
133
+
134
+ ```
135
+ usage: rnmtool [-h] [--pattern GLOB] [--recursive] [--dry-run] [--overwrite]
136
+ [--find TEXT] [--replace TEXT] [--regex] [--prefix TEXT]
137
+ [--suffix TEXT] [--ext EXT] [--case {lower,upper,title}]
138
+ [directory]
139
+ ```
140
+
141
+ ### Positional Arguments
142
+
143
+ | Argument | Description | Default |
144
+ |-------------|-------------------------------------------|----------------------|
145
+ | `directory` | The folder containing files to rename | `.` (current folder) |
146
+
147
+ ### General Options
148
+
149
+ | Flag | Short | Description |
150
+ |------------------|-------|---------------------------------------------------------|
151
+ | `--pattern GLOB` | `-p` | Only rename files matching this glob (e.g. `"*.txt"`) |
152
+ | `--recursive` | `-r` | Search subdirectories recursively |
153
+ | `--dry-run` | `-n` | Preview changes without modifying any file |
154
+ | `--overwrite` | | Allow overwriting a file if the new name already exists |
155
+ | `--help` | `-h` | Show help message and exit |
156
+
157
+ ### Transformation Options
158
+
159
+ | Flag | Short | Description |
160
+ |------------------|-------|----------------------------------------------------------|
161
+ | `--find TEXT` | `-f` | Text (or regex pattern) to search for in filenames |
162
+ | `--replace TEXT` | `-s` | Replacement text (default: empty - deletes the match) |
163
+ | `--regex` | `-x` | Treat `--find` as a regular expression |
164
+ | `--prefix TEXT` | | Text to prepend to every filename |
165
+ | `--suffix TEXT` | | Text to append to every filename (before the extension) |
166
+ | `--ext EXT` | | Replace the file extension (e.g. `.jpg` or `jpg`) |
167
+ | `--case` | `-c` | Convert case: `lower`, `upper`, or `title` |
168
+
169
+ > **Transformation order:** find/replace → case → prefix/suffix → extension
170
+
171
+ ---
172
+
173
+ ## Tutorials
174
+
175
+ All examples below use the included `test_files/` folder.
176
+ **Always run with `--dry-run` first to preview changes safely.**
177
+
178
+ ---
179
+
180
+ ### 1. Dry-Run Preview
181
+
182
+ Preview what *would* happen without touching any file.
183
+ A ✓ mark appears next to filenames that would be changed.
184
+
185
+ ```bash
186
+ rnmtool test_files --dry-run --case lower
187
+ ```
188
+
189
+ **Preview output:**
190
+ ```
191
+ BEFORE AFTER
192
+ ──────────────────────────── ────────────────────────
193
+ 04 helper SCRIPT.py 04 helper script.py ✓
194
+ 07 settings.XML 07 settings.xml ✓
195
+ 12 USER preferences.ini 12 user preferences.ini ✓
196
+ ```
197
+
198
+ No files are modified. Remove `--dry-run` to apply changes.
199
+
200
+ ---
201
+
202
+ ### 2. Add a Prefix
203
+
204
+ Prepend a label or date to every filename in a folder.
205
+
206
+ ```bash
207
+ # Dry-run first
208
+ rnmtool test_files --dry-run --prefix "2025_"
209
+
210
+ # Apply
211
+ rnmtool test_files --prefix "2025_"
212
+ ```
213
+
214
+ **Result:**
215
+ ```
216
+ 01 My Notes.txt → 2025_01 My Notes.txt
217
+ 02 Sales Data 2024.csv → 2025_02 Sales Data 2024.csv
218
+ 03 AppConfig.json → 2025_03 AppConfig.json
219
+ ```
220
+
221
+ **Tip:** Combine with `--pattern` to prefix only specific file types:
222
+ ```bash
223
+ rnmtool test_files --pattern "*.csv" --prefix "report_"
224
+ ```
225
+
226
+ ---
227
+
228
+ ### 3. Add a Suffix
229
+
230
+ Append a label to every filename (inserted *before* the extension).
231
+
232
+ ```bash
233
+ # Dry-run first
234
+ rnmtool test_files --dry-run --suffix "_backup"
235
+
236
+ # Apply
237
+ rnmtool test_files --suffix "_backup"
238
+ ```
239
+
240
+ **Result:**
241
+ ```
242
+ 01 My Notes.txt → 01 My Notes_backup.txt
243
+ 05 README draft.md → 05 README draft_backup.md
244
+ 11 RunBackup.bat → 11 RunBackup_backup.bat
245
+ ```
246
+
247
+ ---
248
+
249
+ ### 4. Find & Replace Text
250
+
251
+ Replace any text string inside filenames - spaces, words, anything.
252
+
253
+ #### Example A - Replace spaces with underscores
254
+
255
+ ```bash
256
+ rnmtool test_files --dry-run --find " " --replace "_"
257
+ rnmtool test_files --find " " --replace "_"
258
+ ```
259
+
260
+ **Result:**
261
+ ```
262
+ 01 My Notes.txt → 01_My_Notes.txt
263
+ 02 Sales Data 2024.csv → 02_Sales_Data_2024.csv
264
+ 08 app ERROR log.log → 08_app_ERROR_log.log
265
+ ```
266
+
267
+ #### Example B - Remove a specific word
268
+
269
+ ```bash
270
+ rnmtool test_files --find " draft" --replace ""
271
+ ```
272
+
273
+ **Result:**
274
+ ```
275
+ 05 README draft.md → 05 README.md
276
+ ```
277
+
278
+ #### Example C - Replace a word with another
279
+
280
+ ```bash
281
+ rnmtool test_files --find "ERROR" --replace "error"
282
+ ```
283
+
284
+ **Result:**
285
+ ```
286
+ 08 app ERROR log.log → 08 app error log.log
287
+ ```
288
+
289
+ ---
290
+
291
+ ### 5. Regex Find & Replace
292
+
293
+ Use regular expressions for advanced pattern-based renaming.
294
+ Enable with the `--regex` (or `-x`) flag alongside `--find`.
295
+
296
+ #### Example A - Strip leading numbers from filenames
297
+
298
+ ```bash
299
+ rnmtool test_files --dry-run --regex --find "^\d+\s"
300
+ rnmtool test_files --regex --find "^\d+\s"
301
+ ```
302
+
303
+ **Result:**
304
+ ```
305
+ 01 My Notes.txt → My Notes.txt
306
+ 02 Sales Data 2024.csv → Sales Data 2024.csv
307
+ 03 AppConfig.json → AppConfig.json
308
+ ```
309
+
310
+ #### Example B - Remove all digits from filenames
311
+
312
+ ```bash
313
+ rnmtool test_files --regex --find "\d+" --replace ""
314
+ ```
315
+
316
+ #### Example C - Replace multiple spaces with a single underscore
317
+
318
+ ```bash
319
+ rnmtool test_files --regex --find "\s+" --replace "_"
320
+ ```
321
+
322
+ ---
323
+
324
+ ### 6. Convert Case
325
+
326
+ ```bash
327
+ # Lowercase
328
+ rnmtool test_files --case lower
329
+
330
+ # Uppercase
331
+ rnmtool test_files --case upper
332
+
333
+ # Title Case
334
+ rnmtool test_files --case title
335
+ ```
336
+
337
+ **Title case result:**
338
+ ```
339
+ 08 app ERROR log.log → 08 App Error Log.log
340
+ 10 CREATE tables.sql → 10 Create Tables.sql
341
+ ```
342
+
343
+ ---
344
+
345
+ ### 7. Change File Extension
346
+
347
+ #### Normalize `.XML` to `.xml`
348
+
349
+ ```bash
350
+ rnmtool test_files --pattern "*.XML" --ext .xml
351
+ ```
352
+
353
+ #### Convert `.yaml` to `.yml`
354
+
355
+ ```bash
356
+ rnmtool test_files --pattern "*.yaml" --ext .yml
357
+ ```
358
+
359
+ #### Change `.txt` to `.md`
360
+
361
+ ```bash
362
+ rnmtool test_files --pattern "*.txt" --ext .md
363
+ ```
364
+
365
+ > The leading dot is optional: `--ext yml` and `--ext .yml` both work.
366
+
367
+ ---
368
+
369
+ ### 8. Filter by Glob Pattern
370
+
371
+ ```bash
372
+ # Only .log files
373
+ rnmtool test_files --pattern "*.log" --prefix "archived_"
374
+
375
+ # Files starting with "0"
376
+ rnmtool test_files --pattern "0*" --case lower
377
+
378
+ # Only .json files
379
+ rnmtool test_files --pattern "*.json" --suffix "_v2"
380
+ ```
381
+
382
+ ---
383
+
384
+ ### 9. Recursive Renaming
385
+
386
+ ```bash
387
+ # Rename all .txt files in test_files and any subfolders
388
+ rnmtool test_files --recursive --pattern "*.txt" --case lower
389
+
390
+ # Always dry-run first with recursive!
391
+ rnmtool test_files --recursive --dry-run --find " " --replace "_"
392
+ ```
393
+
394
+ ---
395
+
396
+ ### 10. Combine Multiple Transformations
397
+
398
+ Transformations are applied in order: **find/replace → case → prefix/suffix → extension**
399
+
400
+ #### Clean up all filenames at once
401
+
402
+ ```bash
403
+ rnmtool test_files --find " " --replace "_" --case lower --prefix "2025_"
404
+ ```
405
+
406
+ **Result:**
407
+ ```
408
+ 01 My Notes.txt → 2025_01_my_notes.txt
409
+ 08 app ERROR log.log → 2025_08_app_error_log.log
410
+ 12 USER preferences.ini → 2025_12_user_preferences.ini
411
+ ```
412
+
413
+ #### Strip numbers, title-case, add suffix
414
+
415
+ ```bash
416
+ rnmtool test_files --regex --find "^\d+\s" --case title --suffix "_final"
417
+ ```
418
+
419
+ #### Pattern-scoped multi-transform
420
+
421
+ ```bash
422
+ rnmtool test_files --pattern "*.py" --case lower --prefix "script_"
423
+ ```
424
+
425
+ ---
426
+
427
+ ## Test Files
428
+
429
+ A `test_files/` folder is included with 12 dummy files of varied types to safely experiment:
430
+
431
+ | File | Extension |
432
+ |------|-----------|
433
+ | `01 My Notes.txt` | `.txt` |
434
+ | `02 Sales Data 2024.csv` | `.csv` |
435
+ | `03 AppConfig.json` | `.json` |
436
+ | `04 helper SCRIPT.py` | `.py` |
437
+ | `05 README draft.md` | `.md` |
438
+ | `06 index PAGE.html` | `.html` |
439
+ | `07 settings.XML` | `.XML` |
440
+ | `08 app ERROR log.log` | `.log` |
441
+ | `09 docker-compose.yaml` | `.yaml` |
442
+ | `10 CREATE tables.sql` | `.sql` |
443
+ | `11 RunBackup.bat` | `.bat` |
444
+ | `12 USER preferences.ini` | `.ini` |
445
+
446
+ ---
447
+
448
+ ## Contributing
449
+
450
+ Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
451
+
452
+ - 🐛 [Report a bug](https://github.com/AayushGoswami/rnmtool/issues)
453
+ - 💡 [Request a feature](https://github.com/AayushGoswami/rnmtool/issues)
454
+ - 📖 [Read the changelog](CHANGELOG.md)
455
+
456
+ ---
457
+
458
+ ## Safety Notes
459
+
460
+ - ✅ **Always use `--dry-run` before applying any changes.**
461
+ - ✅ Files whose names are unchanged by a transformation are automatically skipped.
462
+ - ⚠️ If a rename would cause a **name collision**, the tool skips that file and warns you. Use `--overwrite` only if you are sure.
463
+ - ⚠️ **Recursive mode** (`--recursive`) can affect many files. Always dry-run first.
464
+ - ℹ️ The tool only renames files - it **never moves, deletes, or modifies file contents.**
465
+
466
+ ---
467
+
468
+ *Built with Python's standard library - zero dependencies. Works on Windows, macOS, and Linux.*
@@ -0,0 +1,8 @@
1
+ rnmtool/__init__.py,sha256=IBr45_PcWqztsKHyTXvBFooHGvyjWydaGtd2Dhs_2bw,139
2
+ rnmtool/__main__.py,sha256=5UnQb-iAs_kgzQxpxFF7YWaYtdkAClOuuFHlUy0R95I,6997
3
+ rnmtool-1.0.0.dist-info/LICENSE,sha256=aISDkcq2bEeVc-7jleiuUtB3vck5kIeZHFlSrOjUwmY,1092
4
+ rnmtool-1.0.0.dist-info/METADATA,sha256=oRItEK81dgrHpUHxS1AZA8GQ2_LzDjSKvboAG-v64os,13021
5
+ rnmtool-1.0.0.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
6
+ rnmtool-1.0.0.dist-info/entry_points.txt,sha256=7p5Mf2kJZInOB437eBpRlot1j-AR5-TfzTR8SZkmeUc,50
7
+ rnmtool-1.0.0.dist-info/top_level.txt,sha256=5CnrJZWSCuQLSweQHjMKU4dYk_dqWsYMSFrT0tQ85wk,8
8
+ rnmtool-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (76.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ rnmtool = rnmtool.__main__:main
@@ -0,0 +1 @@
1
+ rnmtool