A modular code transformation framework for applying code transformations to code in a set of source code files.
Organized as a Cargo workspace:
- reformat-core: Core transformation library
- reformat-cli: Command-line interface
- reformat-plugins: Plugin system (foundation)
- Run all common transformations in a single pass with
reformat <path> - Combines three operations efficiently:
- Rename files to lowercase
- Transform task emojis to text alternatives
- Remove trailing whitespace
- 3x faster than running individual commands separately
- Perfect for quick project cleanup:
reformat -r src/
- Convert between 6 case formats: camelCase, PascalCase, snake_case, SCREAMING_SNAKE_CASE, kebab-case, and SCREAMING-KEBAB-CASE
- Process single files or entire directories (with recursive option)
- Dry-run mode to preview changes
- Filter files by glob patterns
- Filter which words to convert using regex patterns
- Add prefix/suffix to converted identifiers
- Support for multiple file extensions (.c, .h, .py, .md, .js, .ts, .java, .cpp, .hpp)
- Remove trailing whitespace from files
- Preserve line endings and file structure
- Recursive directory processing
- Extension filtering with sensible defaults
- Dry-run mode to preview changes
- Automatically skips hidden files and build directories
- Replace task completion emojis with text alternatives (✅ → [x], ☐ → [ ], etc.)
- Replace status indicator emojis (🟡 → [yellow], 🟢 → [green], 🔴 → [red])
- Remove non-task emojis from code and documentation
- Smart replacements for common task tracking symbols
- Configurable behavior (replace task emojis, remove others, or both)
- Support for markdown, documentation, and source files
- Organize files by common prefix into subdirectories
- Automatically detect file prefixes based on separator character
- Optional prefix stripping from filenames after moving
- Suffix-based splitting (
--from-suffix): Split at the LAST separator for multi-part prefixes - Preview mode to see what groups would be created
- Configurable minimum file count for group creation
- Recursive processing for nested directories
- Broken reference detection: Automatically scan codebase for references to moved files
- Interactive fix workflow: Review and apply fixes for broken references
- Change tracking with
changes.jsonandfixes.jsonoutput files
- Define reusable transformation pipelines in a
reformat.jsonconfig file - Run named presets with
reformat -p <preset-name> <path> - Chain any combination of steps:
rename,emojis,clean,convert,group - Per-step configuration overrides (case transforms, file extensions, separators, etc.)
- Dry-run mode applies to all steps in the preset
- Step validation with clear error messages for unknown steps
- Multi-level verbosity control (
-v,-vv,-vvv) - Quiet mode for silent operation (
-q) - File logging for debugging (
--log-file) - Progress spinners with indicatif
- Automatic operation timing
- Color-coded console output
Install from crates.io:
cargo install reformatOr install from the workspace:
cargo install --path reformat-cliOr build from source:
cargo build --release -p reformatThe binary will be at ./target/release/reformat
Add to your Cargo.toml:
[dependencies]
reformat-core = "0.1.4"use reformat_core::{CaseConverter, CaseFormat};
let converter = CaseConverter::new(
CaseFormat::CamelCase, // from
CaseFormat::SnakeCase, // to
None, // file_extensions
false, // recursive
false, // dry_run
String::new(), // prefix
String::new(), // suffix
None, // strip_prefix
None, // strip_suffix
None, // replace_prefix_from
None, // replace_prefix_to
None, // replace_suffix_from
None, // replace_suffix_to
None, // glob_pattern
None, // word_filter
)?;
converter.process_directory(std::path::Path::new("src"))?;use reformat_core::{WhitespaceCleaner, WhitespaceOptions};
let mut options = WhitespaceOptions::default();
options.dry_run = false;
options.recursive = true;
let cleaner = WhitespaceCleaner::new(options);
let (files_cleaned, lines_cleaned) = cleaner.process(std::path::Path::new("src"))?;
println!("Cleaned {} lines in {} files", lines_cleaned, files_cleaned);use reformat_core::{CombinedProcessor, CombinedOptions};
let mut options = CombinedOptions::default();
options.recursive = true;
options.dry_run = false;
let processor = CombinedProcessor::new(options);
let stats = processor.process(std::path::Path::new("src"))?;
println!("Files renamed: {}", stats.files_renamed);
println!("Emojis transformed: {} files ({} changes)",
stats.files_emoji_transformed, stats.emoji_changes);
println!("Whitespace cleaned: {} files ({} lines)",
stats.files_whitespace_cleaned, stats.whitespace_lines_cleaned);use reformat_core::{FileGrouper, GroupOptions};
let mut options = GroupOptions::default();
options.strip_prefix = true; // Remove prefix from filenames
options.from_suffix = false; // Set true to split at LAST separator
options.min_count = 2; // Require at least 2 files to create a group
options.dry_run = false;
let grouper = FileGrouper::new(options);
let stats = grouper.process(std::path::Path::new("templates"))?;
println!("Directories created: {}", stats.dirs_created);
println!("Files moved: {}", stats.files_moved);
println!("Files renamed: {}", stats.files_renamed);The fastest way to clean up your code:
# Process directory (non-recursive)
reformat <path>
# Process recursively
reformat -r <path>
# Preview changes without modifying files
reformat -d <path>What it does:
- Renames files to lowercase
- Transforms task emojis: ✅ → [x], ☐ → [ ]
- Removes trailing whitespace
Example:
# Clean up an entire project directory
reformat -r src/
# Preview changes first
reformat -d -r docs/
# Process a single file
reformat README.mdOutput:
Renamed '/tmp/TestFile.txt' -> '/tmp/testfile.txt'
Transformed emojis in '/tmp/testfile.txt'
Cleaned 2 lines in '/tmp/testfile.txt'
Processed files:
- Renamed: 1 file(s)
- Emoji transformations: 1 file(s) (1 changes)
- Whitespace cleaned: 1 file(s) (2 lines)
Basic conversion (using subcommand):
reformat convert --from-camel --to-snake myfile.pyRecursive directory conversion:
reformat convert --from-snake --to-camel -r src/Dry run (preview changes):
reformat convert --from-camel --to-kebab --dry-run mydir/Add prefix to all converted identifiers:
reformat convert --from-camel --to-snake --prefix "old_" myfile.pyFilter files by pattern:
reformat convert --from-camel --to-snake -r --glob "*test*.py" src/Only convert specific identifiers:
reformat convert --from-camel --to-snake --word-filter "^get.*" src/Clean all default file types in current directory:
reformat clean .Clean with dry-run to preview changes:
reformat clean --dry-run src/Clean only specific file types:
reformat clean -e .py -e .rs src/Clean a single file:
reformat clean myfile.pyReplace task emojis with text in markdown files:
reformat emojis docs/Process with dry-run to preview changes:
reformat emojis --dry-run README.mdOnly replace task emojis, keep other emojis:
reformat emojis --replace-task --no-remove-other docs/Process specific file types:
reformat emojis -e .md -e .txt project/Organize files by common prefix into subdirectories:
# Preview what groups would be created
reformat group --preview templates/
# Dry run to see what would happen
reformat group --dry-run templates/
# Group files (keep original filenames)
reformat group templates/
# Group files and strip prefix from filenames
reformat group --strip-prefix templates/
# Group by suffix (split at LAST separator) - for multi-part prefixes
reformat group --from-suffix templates/
# Process subdirectories recursively
reformat group -r templates/
# Use custom separator (e.g., hyphen)
reformat group -s '-' templates/
# Require at least 3 files to create a group
reformat group -m 3 templates/Example transformation with --strip-prefix (splits at FIRST separator):
Before: After:
templates/ templates/
├── wbs_create.tmpl ├── wbs/
├── wbs_delete.tmpl │ ├── create.tmpl
├── wbs_list.tmpl │ ├── delete.tmpl
├── work_package_create.tmpl │ └── list.tmpl
├── work_package_delete.tmpl ├── work/
└── other.txt │ ├── package_create.tmpl
│ └── package_delete.tmpl
└── other.txt
Example transformation with --from-suffix (splits at LAST separator):
Before: After:
templates/ templates/
├── activity_relationships_list.tmpl ├── activity_relationships/
├── activity_relationships_create.tmpl │ ├── list.tmpl
├── activity_relationships_delete.tmpl │ ├── create.tmpl
├── user_profile_edit.tmpl │ └── delete.tmpl
├── user_profile_view.tmpl ├── user_profile/
└── other.txt │ ├── edit.tmpl
│ └── view.tmpl
└── other.txt
After grouping files, reformat can scan your codebase for broken references:
# Interactive mode (default) - prompts for scanning
reformat group --strip-prefix templates/
# Output:
# Grouping complete:
# - Directories created: 2
# - Files moved: 5
#
# Changes recorded to: changes.json
#
# Would you like to scan for broken references? [y/N]: y
# Enter directories to scan: src
#
# Found 3 broken reference(s).
# Proposed fixes written to: fixes.json
#
# Review fixes.json and apply changes? [y/N]: y
# Fixed 3 reference(s) in 2 file(s).# Non-interactive mode with automatic scanning
reformat group --strip-prefix --no-interactive --scope src templates/
# Skip reference scanning entirely
reformat group --strip-prefix --no-interactive templates/Generated files:
changes.json- Record of all file operations (for auditing)fixes.json- Proposed reference fixes (review before applying)
Define reusable transformation pipelines in a reformat.json file in your project root:
{
"code": {
"steps": ["rename", "emojis", "clean"],
"rename": {
"case_transform": "lowercase",
"space_replace": "hyphen"
},
"emojis": {
"replace_task_emojis": true,
"remove_other_emojis": false,
"file_extensions": [".md", ".txt"]
},
"clean": {
"remove_trailing": true,
"file_extensions": [".rs", ".py"]
}
},
"templates": {
"steps": ["group", "clean"],
"group": {
"separator": "_",
"min_count": 3,
"strip_prefix": true
}
}
}Run a preset:
reformat -p code src/
# Dry-run to preview changes
reformat -p code -d src/
# Run a different preset
reformat -p templates web/templates/Available step configuration options:
| Step | Options |
|---|---|
rename |
case_transform (lowercase/uppercase/capitalize), space_replace (underscore/hyphen), recursive, include_symlinks |
emojis |
replace_task_emojis, remove_other_emojis, file_extensions, recursive |
clean |
remove_trailing, file_extensions, recursive |
convert |
from_format, to_format, file_extensions, recursive, prefix, suffix, glob, word_filter |
group |
separator, min_count, strip_prefix, from_suffix, recursive |
Steps without explicit configuration use sensible defaults.
Control output verbosity:
# Info level output (-v)
reformat -v convert --from-camel --to-snake src/
# Debug level output (-vv)
reformat -vv clean src/
# Silent mode (errors only)
reformat -q convert --from-camel --to-snake src/
# Log to file
reformat --log-file debug.log -v convert --from-camel --to-snake src/Output example with -v:
2025-10-10T00:15:08.927Z [INFO] Converting from CamelCase to SnakeCase
2025-10-10T00:15:08.927Z [INFO] Target path: /tmp/test.py
2025-10-10T00:15:08.927Z [INFO] Recursive: false, Dry run: false
Converted '/tmp/test.py'
2025-10-10T00:15:08.931Z [INFO] Conversion completed successfully
2025-10-10T00:15:08.931Z [INFO] run_convert(), Elapsed=4.089125ms
--from-camel/--to-camel- camelCase (firstName, lastName)--from-pascal/--to-pascal- PascalCase (FirstName, LastName)--from-snake/--to-snake- snake_case (first_name, last_name)--from-screaming-snake/--to-screaming-snake- SCREAMING_SNAKE_CASE (FIRST_NAME, LAST_NAME)--from-kebab/--to-kebab- kebab-case (first-name, last-name)--from-screaming-kebab/--to-screaming-kebab- SCREAMING-KEBAB-CASE (FIRST-NAME, LAST-NAME)
Convert Python file from camelCase to snake_case:
reformat convert --from-camel --to-snake main.pyConvert C++ project from snake_case to PascalCase:
reformat convert --from-snake --to-pascal -r -e .cpp -e .hpp src/Preview converting JavaScript getters to snake_case:
reformat convert --from-camel --to-snake --word-filter "^get.*" -d src/Clean trailing whitespace from entire project:
reformat clean -r .Clean only Python files in src directory:
reformat clean -e .py src/Preview what would be cleaned without making changes:
reformat clean --dry-run .Transform task emojis in documentation:
reformat emojis -r docs/Example transformation:
Before:
- Task done ✅
- Task pending ☐
- Warning ⚠ issue
- 🟡 In progress
- 🟢 Complete
- 🔴 Blocked
After:
- Task done [x]
- Task pending [ ]
- Warning [!] issue
- [yellow] In progress
- [green] Complete
- [red] BlockedProcess only markdown files:
reformat emojis -e .md README.mdOrganize template files by prefix (split at first separator):
reformat group --strip-prefix web/templates/Organize files with multi-part prefixes (split at last separator):
# activity_relationships_list.tmpl -> activity_relationships/list.tmpl
reformat group --from-suffix web/templates/Preview groups without making changes:
reformat group --preview web/templates/Example output:
Found 2 potential group(s):
wbs (3 files):
- wbs_create.tmpl
- wbs_delete.tmpl
- wbs_list.tmpl
work (2 files):
- work_package_create.tmpl
- work_package_delete.tmpl
Group files with hyphen separator:
reformat group -s '-' --strip-prefix components/Recursively organize nested directories:
reformat group -r --strip-prefix src/Group files and automatically scan for broken references:
reformat group --strip-prefix --scope src templates/Example changes.json:
{
"operation": "group",
"timestamp": "2026-01-15T16:30:00+00:00",
"base_dir": "/project/templates",
"changes": [
{"type": "directory_created", "path": "wbs"},
{"type": "file_moved", "from": "wbs_create.tmpl", "to": "wbs/create.tmpl"}
]
}Example fixes.json:
{
"generated_from": "changes.json",
"fixes": [
{
"file": "src/handler.go",
"line": 15,
"context": "template.ParseFiles(\"wbs_create.tmpl\")",
"old_reference": "wbs_create.tmpl",
"new_reference": "wbs/create.tmpl"
}
]
}Run a multi-step cleanup preset:
# Define in reformat.json, then run:
reformat -p code src/
# Output:
# rename: 3 file(s) renamed
# emojis: 2 file(s), 5 change(s)
# clean: 4 file(s), 12 line(s) cleaned
# Preset 'code' complete.Preview preset changes without modifying files:
reformat -p code -d src/Case conversion preset:
{
"snake-to-camel": {
"steps": ["convert"],
"convert": {
"from_format": "snake",
"to_format": "camel",
"file_extensions": [".py"],
"recursive": true
}
}
}reformat -p snake-to-camel src/MIT License. See LICENSE for details.