A fast, modern stream editor built in Rust. Like ripgrep is to grep, ripsed is to sed.
Designed for humans and machines — with first-class JSON support for AI coding agents.
- Sensible defaults. Recursive,
.gitignore-aware, UTF-8. No flags needed for the common case. - No escape hell. Standard Rust regex syntax. No sed-style delimiters.
- Agent-native. Structured JSON I/O as a first-class interface, not an afterthought.
- Safe by default. Dry-run previews, atomic writes, undo log, backup files.
- Fast. Parallel file discovery, memory-mapped I/O, same philosophy as ripgrep.
- Scriptable. Chain operations in
.ripscript files for multi-step refactors.
cargo install --path crates/ripsed-cliRequires Rust 1.85+.
# Find-and-replace across all files (recursive, respects .gitignore)
ripsed 'old_function' 'new_function'
# Regex with capture groups
ripsed -e 'fn\s+old_(\w+)' 'fn new_$1'
# Scope to specific files
ripsed 'TODO' 'DONE' --glob '*.rs'
# Delete lines matching a pattern
ripsed -d 'console\.log'
# Insert text after matching lines
ripsed 'use serde;' --after 'use serde_json;'
# Transform matched text (upper, lower, title, snake_case, camel_case)
ripsed --transform upper 'select|from|where' -e
# Surround matching lines with prefix/suffix
ripsed --surround '/* ' ' */' 'HACK'
# Indent/dedent matching lines
ripsed --indent 4 'nested_block'
ripsed --dedent 2 'over_indented'
# Run a multi-step refactor from a .rip script
ripsed --script refactor.rip
# Preview changes without applying
ripsed 'foo' 'bar' --dry-run
# Pipe mode (stdin/stdout, like traditional sed)
echo 'hello world' | ripsed 'hello' 'goodbye'USAGE:
ripsed [OPTIONS] <FIND> [REPLACE]
ARGS:
<FIND> Pattern to search for (literal by default, regex with -e)
[REPLACE] Replacement string
OPTIONS:
-e, --regex Treat FIND as a regex
-d, --delete Delete matching lines
--dry-run Preview changes without writing
--backup Create .ripsed.bak files before modifying
--glob <PATTERN> Only process files matching glob
--ignore <PATTERN> Skip files matching glob
--hidden Include hidden files
--no-gitignore Don't respect .gitignore
--case-insensitive Case-insensitive matching
--after <TEXT> Insert text after matching lines
--before <TEXT> Insert text before matching lines
--replace-line <TEXT> Replace entire matching line
-n, --line-range <N:M> Only operate on lines N through M
--max-depth <N> Maximum directory recursion depth
-c, --count Print count of matches only
-q, --quiet Suppress all non-error output
--confirm Interactive confirmation before each file
--undo [N] Undo the last N operations (default: 1)
--undo-list Show recent undo log entries
--follow Follow symbolic links during discovery
--config <PATH> Path to .ripsed.toml config file
--transform <MODE> Transform matched text (upper, lower, title, snake_case, camel_case)
--surround <P> <S> Surround matching lines with prefix and suffix
--indent <N> Indent matching lines by N spaces
--dedent <N> Remove up to N leading whitespace chars from matching lines
--script <PATH> Run operations from a .rip script file
-j, --json Enable agent/JSON mode
--jsonl Stream results as JSON Lines
--no-json Force human mode even if stdin looks like JSON
ripsed has a structured JSON interface designed for AI coding agents, editor plugins, and automation pipelines. In agent mode, dry_run defaults to true for safety.
ripsed --json << 'EOF'
{
"version": "1",
"operations": [
{
"op": "replace",
"find": "old_function",
"replace": "new_function",
"glob": "src/**/*.rs"
},
{
"op": "delete",
"find": "^\\s*//\\s*TODO:.*$",
"regex": true
}
],
"options": {
"dry_run": true,
"root": "./my-project"
}
}
EOF{
"version": "1",
"success": true,
"dry_run": true,
"summary": {
"files_matched": 12,
"files_modified": 0,
"total_replacements": 34
},
"results": [
{
"operation_index": 0,
"files": [
{
"path": "src/lib.rs",
"changes": [
{
"line": 42,
"before": " let result = old_function(x);",
"after": " let result = new_function(x);",
"context": {
"before": ["fn main() {", " let x = 5;"],
"after": [" println!(\"{}\", result);", "}"]
}
}
]
}
]
}
],
"errors": []
}| Operation | JSON op |
Human flag | Description |
|---|---|---|---|
| Replace | replace |
ripsed 'find' 'replace' |
Find and replace text |
| Delete | delete |
-d |
Remove lines matching pattern |
| Insert after | insert_after |
--after |
Insert text after matching lines |
| Insert before | insert_before |
--before |
Insert text before matching lines |
| Replace line | replace_line |
--replace-line |
Replace entire matching line |
| Transform | transform |
--transform MODE |
Change case of matched text |
| Surround | surround |
--surround P S |
Wrap matching lines with prefix/suffix |
| Indent | indent |
--indent N |
Add N spaces before matching lines |
| Dedent | dedent |
--dedent N |
Remove up to N leading whitespace chars from matching lines |
Every error includes a machine-readable code, human-readable message, and actionable hint:
| Code | Description |
|---|---|
no_matches |
Pattern matched nothing |
invalid_regex |
Regex failed to compile |
invalid_request |
Malformed JSON or missing fields |
file_not_found |
Target path doesn't exist |
permission_denied |
Can't read/write target files |
binary_file_skipped |
Binary file was skipped |
write_failed |
Could not write output file |
Chain multiple operations in a .rip file:
# refactor.rip — rename and clean up
replace "oldApi" "newApi" --glob "*.ts"
replace "OldApi" "NewApi" --glob "*.ts"
delete "// DEPRECATED" -e
transform "select|from|where|join" --mode upper -e --glob "*.sql"ripsed --script refactor.rip --dry-run # preview
ripsed --script refactor.rip # applyEach line is an operation with the same flags as the CLI. Comments start with #. Strings with spaces use quotes (single or double, with escape support).
Create a .ripsed.toml in your project root:
[defaults]
backup = true
max_depth = 10
[undo]
max_entries = 100ripsed discovers this file by walking up from the current directory, similar to .gitignore.
ripsed is organized as a Rust workspace with four crates:
| Crate | Description |
|---|---|
ripsed-core |
Pure logic: edit engine, matcher, operation IR, error taxonomy |
ripsed-fs |
File I/O: discovery, reading (with mmap), atomic writes, locking |
ripsed-json |
Agent interface: request/response schemas, auto-detection |
ripsed-cli |
Binary: CLI args, human output formatting, interactive confirm |
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contributions are welcome! Please open an issue or submit a pull request.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.