chmln/sd
Intuitive find & replace CLI (sed alternative)
Healthy across the board
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 2mo ago
- ✓18 active contributors
- ✓Distributed ownership (top contributor 36% of recent commits)
Show all 6 evidence items →Show less
- ✓MIT licensed
- ✓CI configured
- ✓Tests present
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/chmln/sd)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/chmln/sd on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: chmln/sd
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/chmln/sd shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 2mo ago
- 18 active contributors
- Distributed ownership (top contributor 36% of recent commits)
- MIT licensed
- CI configured
- Tests present
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live chmln/sd
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/chmln/sd.
What it runs against: a local clone of chmln/sd — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in chmln/sd | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 101 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of chmln/sd. If you don't
# have one yet, run these first:
#
# git clone https://github.com/chmln/sd.git
# cd sd
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of chmln/sd and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "chmln/sd(\\.git)?\\b" \\
&& ok "origin remote is chmln/sd" \\
|| miss "origin remote is not chmln/sd (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "sd/src/lib.rs" \\
&& ok "sd/src/lib.rs" \\
|| miss "missing critical file: sd/src/lib.rs"
test -f "sd/src/replacer/mod.rs" \\
&& ok "sd/src/replacer/mod.rs" \\
|| miss "missing critical file: sd/src/replacer/mod.rs"
test -f "sd-cli/src/main.rs" \\
&& ok "sd-cli/src/main.rs" \\
|| miss "missing critical file: sd-cli/src/main.rs"
test -f "sd/src/input.rs" \\
&& ok "sd/src/input.rs" \\
|| miss "missing critical file: sd/src/input.rs"
test -f "sd/src/output.rs" \\
&& ok "sd/src/output.rs" \\
|| miss "missing critical file: sd/src/output.rs"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 101 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~71d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/chmln/sd"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
sd is a command-line find & replace tool written in Rust that replaces sed with a more intuitive interface. It supports regex (using JavaScript/Python syntax), literal string replacements, and multi-line patterns across file content, delivering 2-12x performance gains over sed on typical workloads. Workspace monorepo split into three crates: sd/ contains the core replacer engine (lib.rs with input, output, replacer modules), sd-cli/ wraps it as a CLI binary (using clap for arg parsing), and xtask/ handles code generation for shell completions. Core logic lives in sd/src/replacer/mod.rs (replacement logic) and sd/src/unescape.rs (escape sequence handling).
👥Who it's for
Unix/Linux power users and DevOps engineers who regularly need to perform find-and-replace operations on text files and streams, but are frustrated by sed's cryptic syntax and escaping rules. Developers who want a faster, more readable alternative to sed for build scripts and text processing pipelines.
🌱Maturity & risk
Production-ready and actively maintained. The project has reached v1.0.0, includes comprehensive test coverage (proptest-based regression tests in proptest-regressions/), full CI/CD setup via GitHub Actions (test.yml and publish.yml workflows), and active dependency management (Dependabot configured). The 2021 creation date and ongoing updates indicate mature, stable development.
Low risk. The codebase is relatively simple (focused on one task), dependencies are minimal and well-vetted (clap, tempfile, regex), and the Rust 1.86.0 requirement is current. The main risk is single-maintainer dependency (appears primary authored by Gregory), but the project's scope is narrow enough to mitigate this. No obvious breaking changes in the CHANGELOG pattern.
Active areas of work
Active maintenance with version 1.0.0 released. Recent work appears focused on stability and cross-platform support (PowerShell and Elvish completion generation visible in gen/completions/). The release.toml and RELEASE.md presence suggests structured release processes are in place. Dependabot is actively managing dependencies.
🚀Get running
git clone https://github.com/chmln/sd.git
cd sd
cargo build --release
./target/release/sd --help
Daily commands:
For CLI: cargo run --release -- 'pattern' 'replacement' [file]. For tests: cargo test (runs all workspace tests including sd-cli snapshot tests). Dev iteration: cargo build && ./target/debug/sd 'test' 'replacement' file.txt.
🗺️Map of the codebase
sd/src/lib.rs— Core library entry point exposing the main replace API and orchestrating the entire find-and-replace workflow.sd/src/replacer/mod.rs— Heart of the regex replacement engine; implements pattern matching, capturing groups, and substitution logic.sd-cli/src/main.rs— CLI entry point that parses arguments, reads files, and coordinates the replace operation with I/O.sd/src/input.rs— Handles file reading and input normalization; critical for handling various file encodings and modes.sd/src/output.rs— Manages atomic file writes and output formatting; ensures data integrity during replacements.sd-cli/src/cli.rs— Clap-based CLI argument parser; defines all user-facing flags, options, and validation.sd/src/unescape.rs— String literal unescaping logic; essential for non-regex mode and escape sequence handling.
🧩Components & responsibilities
- sd-cli (binary) (Rust, Clap, std::fs, std::io) — User-facing entry point; parses CLI args, reads input, orchestrates the library, and writes output.
🛠️How to make changes
Add a new CLI flag or option
- Define the new flag in the Clap derive struct with #[arg(...)] attributes (
sd-cli/src/cli.rs) - Handle the flag in main() and pass to the core replace function or update input/output behavior (
sd-cli/src/main.rs) - Add integration tests verifying the flag behavior with snapshots (
sd-cli/tests/cli.rs) - Re-generate completions by running
cargo xtask(xtask/src/main.rs)
Add a new replacement mode or pattern type
- Extend the replacement logic in replacer/mod.rs to handle the new mode (e.g., glob patterns, literal strings with new escape rules) (
sd/src/replacer/mod.rs) - Add corresponding validation rules in replacer/validate.rs if pattern syntax is new (
sd/src/replacer/validate.rs) - Update unescape.rs if the new mode requires custom escape sequence handling (
sd/src/unescape.rs) - Write comprehensive property-based and unit tests in replacer/tests.rs (
sd/src/replacer/tests.rs) - Expose the new mode via lib.rs public API and wire it through CLI flags (
sd/src/lib.rs)
Fix a file I/O or encoding issue
- Diagnose the issue in input.rs (file reading, stdin, encoding detection) or output.rs (atomic writes) (
sd/src/input.rs) - Update the relevant read/write function and add error handling via error.rs (
sd/src/error.rs) - Add or update integration tests that reproduce the original issue (
sd-cli/tests/cli.rs) - Verify snapshot tests pass and no regressions in replacer tests (
sd/src/replacer/tests.rs)
🔧Why these technologies
- Rust + Cargo workspace — Memory safety and performance; three-member workspace (sd library, sd-cli binary, xtask) separates concerns cleanly.
- Regex crate — Fast, battle-tested regex engine matching JavaScript/Python syntax; core to the value proposition over sed.
- Clap derive macros — Ergonomic CLI argument parsing with built-in help, validation, and automatic shell completions.
- Tempfile crate — Ensures atomic file writes; prevents data loss if the process crashes mid-write.
- Insta snapshots — Regression detection for CLI output and snapshot testing without brittle string assertions.
⚖️Trade-offs already made
-
Regex-first design (JavaScript/Python syntax) instead of extended POSIX or GNU sed syntax
- Why: Users already know JS/Python regex; lowers barrier to entry and reduces cognitive load.
- Consequence: Not fully backward-compatible with sed scripts; customers must migrate patterns, but sd is marketed as a sed alternative, not drop-in replacement.
-
Atomic file writes using rename() instead of in-place edits
- Why: Prevents data corruption if the process crashes or is interrupted.
- Consequence: Slightly slower on systems with cross-filesystem moves; negligible for typical workflows.
-
Single-threaded find-and-replace within a file; no parallelism across files
- Why: Simplicity; typical use is single-file replacement. Regex engine is fast enough.
- Consequence: Batch operations on many files slower than a hypothetical parallel version; users can parallelize via xargs/GNU Parallel externally.
-
String-literal mode (no regex) as an opt-in flag, not default
- Why: Maintains alignment with sed semantics (regex is default); users explicitly request simplicity.
- Consequence: Users must remember -P (literal) flag; mitigated by clear help text and README examples.
🚫Non-goals (don't propose these)
- Multi-line pattern matching across entire files without -A flag
- Complex scripting or conditional replacements (e.g., sed's branching, loops)
- In-place binary file editing
- Support for sed macros, labels, or command chaining
- Real-time streaming processing (sd loads entire file into memory)
🪤Traps & gotchas
Atomic file swap only on Unix: sd/tests/snapshots/ contains a unix_only test for atomic file creation failures—Windows uses different mechanisms. Escape sequence context: unescape.rs behavior differs between string-literal mode vs. regex mode; both are context-sensitive. Capture group indices: Replacement strings use $1, $2 syntax (not \1, \2) matching JavaScript/Python, not sed—easy to trip up. Newline handling: -A (across mode) required to match \n across lines; default line-by-line mode won't match newlines even with explicit \n in pattern.
🏗️Architecture
💡Concepts to learn
- Atomic file-swap pattern — sd guarantees that in-place file modification (-i flag) either fully succeeds or fails completely, preventing corruption if the process crashes mid-write. This is implemented in
sd/src/output.rsand is critical for production use. - Regex capture groups with JavaScript/Python syntax — sd deviates from sed's \1 syntax to use $1/$2 (or ${name} for named groups), reducing cognitive load since most developers already know this from JavaScript/Python contexts. This design choice is baked throughout
sd/src/replacer/mod.rs. - Across-mode (multiline pattern matching) — The -A flag in sd loads the entire file into memory for pattern matching across newlines, enabling patterns like 'replace \n with ,' that sed requires complex workarounds for. Understanding when to use -A vs. default line-by-line is key to sd's usability.
- String-literal mode vs. regex mode — sd's -F/--fixed-strings flag treats the pattern as a literal string with no regex metacharacters, avoiding the need to escape special characters like . * ( ) [ ]. This dual mode (visible in
sd-cli/src/cli.rs) is sd's answer to sed's usability problem. - Escape sequence normalization — sd converts user input containing \n, \t, \r into actual newlines/tabs before regex matching, handled by
sd/src/unescape.rs. This cross-platform normalization is transparent to users but essential for Windows compatibility. - Property-based testing with proptest — sd uses proptest for regression testing (see
proptest-regressions/replacer/) to generate thousands of random test cases rather than hand-coded examples, catching edge cases that manual testing would miss in regex and replacement logic. - Snapshot testing for CLI behavior — The
sd-cli/tests/snapshots/directory contains insta-based snapshot tests that capture exact CLI output and error messages, making it easy to detect unintended changes to user-facing behavior.
🔗Related repos
BurntSushi/ripgrep— Industry-standard fast text search tool in Rust; sd is to find-replace as ripgrep is to find—both optimize for speed and modern regex syntax over GNU toolssharkdp/bat— Rust CLI tool that replaces cat with syntax highlighting; shares similar philosophy of improving user experience over classic Unix toolsstedolan/jq— Indispensable JSON query/transform tool often paired with find-replace in data processing pipelines; sd users commonly use both togethermawk/mawk— Modern AWK alternative; competes in the same text-processing space as sd for line-by-line transformations, though with different scopegit/git— Uses sed extensively for post-processing and scripting; sd serves as a drop-in faster replacement in Git-based workflows and git hooks
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive integration tests for atomic file operations across platforms
The repo has a snapshot test cli__unix_only__reports_errors_on_atomic_file_swap_creation_failure.snap but lacks corresponding tests for successful atomic writes and Windows compatibility. The sd-cli/src/output.rs likely handles atomic file swaps, but test coverage appears limited to error cases. Adding platform-specific integration tests would ensure reliability across Linux, macOS, and Windows.
- [ ] Examine
sd-cli/src/output.rsto understand atomic file swap implementation - [ ] Create
sd-cli/tests/atomic_operations.rswith tests for successful atomic writes - [ ] Add platform-specific test cases using
#[cfg(windows)]and#[cfg(unix)]for atomic behavior validation - [ ] Test edge cases: concurrent access, disk space exhaustion, permission issues
- [ ] Update
sd-cli/tests/snapshots/with new test outputs
Implement property-based testing for regex pattern validation and replacement edge cases
The repo already uses proptest (evidenced by proptest-regressions/ directory) but the regression tests in proptest-regressions/replacer/ suggest incomplete coverage. The sd/src/replacer/validate.rs needs more robust property-based tests to catch regex edge cases, escaping issues, and malformed patterns before they reach users.
- [ ] Review existing proptest regressions in
proptest-regressions/replacer/tests.txtandvalidate.txt - [ ] Expand
sd/src/replacer/tests.rswith property-based tests for: regex pattern generation, capture group handling, special character escaping - [ ] Add strategies for generating valid/invalid regex patterns and verify
sd/src/replacer/validate.rscatches invalid cases - [ ] Add property tests for
sd/src/unescape.rsto ensure all escape sequences are correctly handled - [ ] Run tests to generate new regressions and commit them to
proptest-regressions/
Add shell completion tests and automate completion file verification in CI
The repo generates shell completions in gen/completions/ (bash, fish, zsh, PowerShell, elvish) but there's no CI workflow or tests verifying they're valid or up-to-date. The xtask/src/generate.rs generates these, but no test validates that generated completions parse correctly and reflect current CLI flags from sd-cli/src/cli.rs.
- [ ] Create
xtask/tests/completion_generation.rsto verify completion files are valid syntax for each shell - [ ] Add tests that parse
sd-cli/src/cli.rsclap definitions and ensure all flags/options appear in completion files - [ ] Create GitHub Actions workflow step in
.github/workflows/test.ymlto runcargo xtask generateand verify no uncommitted changes (ensuring completions stay in sync) - [ ] Add validation scripts that test fish/bash/zsh/PowerShell/elvish completion parsing using shell-specific tools
- [ ] Document the completion update process in CONTRIBUTING.md (if it exists) or README.md
🌿Good first issues
- Add dedicated tests for
sd/src/input.rsfile reading logic. Currentlysd/src/replacer/tests.rscovers core logic, butinput.rscharacter encoding edge cases and file handle management lack direct tests. Create test fixtures insd-cli/tests/for various encodings (UTF-8, Latin-1, BOM handling).: Medium: Increases confidence in cross-platform file reading, especially on Windows - Document escape sequence behavior in README with a comparison table. Currently README lacks explicit examples of \n, \t, \r handling in string-literal vs. regex mode. Add a table comparing
sd '\n' ','vssd -P '\n' ','(regex vs literal).: Easy: Reduces user confusion about the most common 'why doesn't \n work?' question - Extend
sd/src/replacer/validate.rsto detect invalid capture group references like $0, $99, or unclosed ${...} syntax. Currently validation is minimal. Add proptest-based fuzzing (similar toproptest-regressions/replacer/validate.txt) to catch edge cases.: Medium: Prevents cryptic runtime errors when users typo capture group numbers
⭐Top contributors
Click to expand
Top contributors
- @CosmicHorrorDev — 36 commits
- @dependabot[bot] — 17 commits
- @chmln — 15 commits
- @oriongonza — 9 commits
- @yhakbar — 6 commits
📝Recent commits
Click to expand
Recent commits
44febdf— Merge pull request #330 from chmln/arm64-targets (oriongonza)97b4822— avoid running cross-target tests in CI (oriongonza)c864c58— add linux aarch64 gnu target to CI and releases (oriongonza)90bc67d— add Windows ARM64 GNU/GNUllvm release targets (oriongonza)4a7b216— feat: add line-by-line mode as default, stream without loading files into memory (#328) (oriongonza)87d1ba5— Upgrade assert_cmd (chmln)fee9c58— Merge pull request #327 from chmln/retain-ownership (chmln)84c7e6e— Fix clippy warnings (chmln)553663c— Retain file ownership on atomic writes (#326) (chmln)bffe271— xtask - clean up (chmln)
🔒Security observations
The codebase has a good security posture overall. The main concern is the use of an unstable Rust edition (2024) which poses compatibility and stability risks. A very recent MSRV (1.86.0) may limit accessibility. The absence of security.md and automated dependency auditing in CI are minor governance issues. The actual code components (regex-based find/replace) are well-scoped and less prone to common injection vulnerabilities. No hardcoded secrets, exposed configurations, or infrastructure issues were detected in the visible files.
- Medium · Rust Edition 2024 Not Yet Stabilized —
Cargo.toml (workspace.package.edition). The Cargo.toml specifies edition = '2024', which is not a stable Rust edition. The latest stable edition is 2021. This may cause compatibility issues, build failures, or undefined behavior when the crate is used as a dependency or built in different environments. Fix: Change edition to '2021' (the current stable edition) or '2015' for broader compatibility. Update to edition '2024' only when it is officially stabilized and released. - Low · Missing MSRV Documentation —
Cargo.toml (workspace.package.rust-version). While rust-version = '1.86.0' is specified, this version is very recent and potentially bleeding-edge. Users relying on older stable Rust versions may face compatibility issues. There is no documented migration path or compatibility notes. Fix: Document the MSRV policy clearly in README.md. Consider using a slightly older stable version (e.g., 1.70.0) to improve accessibility unless there are specific technical reasons for requiring 1.86.0. - Low · No Security Policy Defined —
Repository root / .github/. There is no SECURITY.md or security policy in .github/ directory. Users and researchers cannot find clear guidelines for responsible disclosure of security vulnerabilities. Fix: Create a SECURITY.md file at the repository root that outlines the vulnerability disclosure process, supported versions, and how to report issues privately. - Low · Dependency Audit Not Visible in CI —
.github/workflows/test.yml and publish.yml. The GitHub workflows (test.yml, publish.yml) do not appear to includecargo auditor dependency security scanning. This means vulnerable dependencies may not be detected before release. Fix: Addcargo auditstep to the test workflow to check for known vulnerabilities in dependencies. Example:- run: cargo audit
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.