txed is a stream-oriented text processor designed for two audiences:
- Humans who want a safer, clearer CLI than
sedorawk - AI agents that require structured inputs, deterministic behavior, and strict JSON validation
It follows the Unix philosophy strictly.
txed does not walk directories, infer context, or guess intent. It consumes streams, applies explicit operations, and performs atomic, transactional edits.
- Atomic edits by default: Transactional writes across one file or many (
--transaction all|file). - Explicit inputs: Edits only the files you pass (args/stdin); no implicit directory traversal.
- Multiple input modes: Positional files, newline/NUL-delimited stdin paths, stdin text, or
rg --jsonspans. - Safe previews:
--dry-rundiffs,--no-write, and validation-only runs. - Structured automation: JSON event stream (
--format json) and JSON Schema (txed schema) for agent tooling. - Manifest apply mode: Multi-file pipelines via
txed apply --manifest β¦.
- Developer guide:
HACKING.md - Architecture/design notes:
DESIGN.md - JSON output contract:
docs/JSON_EVENTS.md - MCP integration example:
docs/MCP_INTEGRATION.md
Simple positional arguments. No regex soup, no flag archaeology.
# Replace 'lazy_static' with 'once_cell' in main.rs
txed "lazy_static" "once_cell" src/main.rsLet ripgrep (rg) or fd decide what to touch, and let txed decide how to edit.
# Find files containing unwrap()
# Replace it safely everywhere
rg -l "unwrap()" | txed "unwrap()" "expect(\"checked by safe-mode\")"No directory traversal. No implicit recursion. No surprises.
# Replace in explicit files
txed [OPTIONS] FIND REPLACE [FILES...]
# Replace in files listed on stdin
fd -e rs | txed [OPTIONS] FIND REPLACE
rg -l PATTERN | txed [OPTIONS] FIND REPLACE
# Targeted edits using rg JSON matches
rg --json PATTERN | txed --rg-json [OPTIONS] FIND REPLACE
# Agent workflows
txed schema
txed apply --manifest manifest.json [OPTIONS]txed FIND REPLACE [FILES...]
Default command. Edits provided files or reads file paths from stdin when no files are passed.
schema
Print the JSON Schema describing manifests, operations, and output events.
txed schema > tools_schema.jsonapply --manifest FILE
Apply a manifest (multi-file, multi-operation), with full validation and atomic commit.
txed apply --manifest manifest.jsontxed is explicit about what stdin represents.
If stdin is piped and no FILES... are provided, stdin is treated as a newline-delimited list of paths.
rg -l unwrap | txed unwrap expectForce stdin to be interpreted as newline-delimited paths.
Read NUL-delimited paths from stdin. Compatible with fd -0, find -print0, rg -l0.
fd -0 -e rs | txed --files0 foo barTreat stdin as content and write transformed content to stdout. No files are opened.
printf '%s\n' "hello foo" | txed --stdin-text foo barConsume rg --json output from stdin and apply edits only to reported match spans.
- No re-searching
- No heuristic matching
- Fails if input is not valid
rgJSON
rg --json "foo" | txed --rg-json foo barForce positional arguments to be treated as files even when stdin is present.
FIND is treated as a literal string.
Interpret FIND as a regular expression.
txed --regex 'foo\s+bar' baz file.txt--ignore-case--smart-caseCase-insensitive unlessFINDcontains uppercase
(Default behavior is case-sensitive matching.)
--limit N
Maximum replacements per file.
txed foo bar file.rs --limit 1--range START[:END]
Apply replacements only within a line range (1-based).
txed foo bar file.rs --range 10:200--glob-include GLOB
Apply edits only to files whose paths match the glob.
fd . | txed foo bar --glob-include '**/*.rs'--glob-exclude GLOB
Exclude matching paths.
--dry-run
Print unified diffs. Perform no writes.
--no-write
Guarantee zero filesystem writes even if output mode changes.
--require-match
Fail if zero matches are found across all inputs.
--expect N
Require exactly N total replacements or abort.
--fail-on-change
Exit non-zero if any change would occur. Useful for CI.
--transaction all|file
all(default): Commit only if every file succeedsfile: Commit each file independently (still atomic per file)
--symlinks follow|skip|error
follow(default)skiperror
--binary skip|error
skip(default)error
--permissions preserve|fixed
preserve(default)fixed
Default behavior
- TTY: unified diff + summary
- Pipe: structured JSON events
Flags
--json: Force JSON output. See JSON Event Schema.--quiet--format diff|summary|json|agent
Preview changes without modifying files.
# Preview replacing 3 digits with "NUM"
txed --dry-run --regex '\d{3}' 'NUM' data.txtUse rg to find specific matches (e.g. only in function bodies) and txed to replace them using the exact spans found by rg.
# Find "foo" only in lines starting with "fn" and replace with "bar"
rg --json '^fn.*foo' | txed --rg-json foo barApply complex multi-file edits transactionally.
manifest.json:
{
"files": ["src/main.rs", "src/lib.rs"],
"transaction": "all",
"operations": [
{
"type": "replace",
"find": "OldName",
"with": "NewName"
},
{
"type": "delete",
"find": "// TODO: remove me"
}
]
}txed apply --manifest manifest.jsonCheck if a replacement would change anything without actually doing it.
# Fail if no changes would be made (ensure your regex matches)
txed --require-match foo bar src/
# Fail if changes WOULD be made (verify cleanliness)
txed --fail-on-change --dry-run foo bar src/Check the examples/ directory for ready-to-use recipes:
examples/manifest_simple.json: Basic replacement.examples/manifest_advanced.json: Regex, deletes, and capture expansion.examples/mcp_server.py: Python script to runtxedas a Model Context Protocol server.
Agents submit a pipeline manifest describing multi-file atomic edits.
{
"files": ["src/lib.rs", "src/config.rs"],
"operations": [
{
"replace": {
"find": "fn process(data: String)",
"with": "fn process(data: &str)",
"limit": 1
}
}
],
"transaction": "all"
}txed apply --manifest manifest.jsonOptions:
--validate-only--dry-run--json
Workload Literal replacement on a large wordlist, streamed to stdout
-
txed (release, fixed string, stdin-text) ~1.36 s mean User: ~0.82 s System: ~0.53 s
-
sed (streaming replace) ~3.81 s mean User: ~3.67 s System: ~0.11 s
txedis ~2.8Γ faster thansedfor this workloadsedspends most time in user-space regex processingtxedbenefits from deterministic parsing and efficient buffered I/O
# From a local checkout
cargo install --path .
# Or, install directly from git (replace with your repo URL)
cargo install --git <REPO_URL>Requires Rust 1.86+.
Prebuilt binaries and crates.io publishing are not set up yet.
0success1operational failure2policy failure3partial or aborted transaction
MIT
