Read in Portuguese / Leia em Portugues
atomwrite
Atomic file operations for LLM agents -- one CLI, zero corruption
What Is It
- A single Rust binary that handles every file operation an LLM agent needs
- 45 subcommands (SSOT: clap
Commands/agent-surface): read, write, edit, search, replace, hash, verify, delete, count, diff, move, copy, list, extract, calc, regex, transform, scope, batch, backup, rollback, apply, set, get, del, case, query, outline, wal-heal, wal-stats, completions, edit-loop, prune-backups, semantic-merge, sparse, recipe, stat, agent-surface, watch, codemod, semantic-search, doctor, locale, commands, scratch - Every write is atomic: tempfile, fsync, rename, fsync directory
- Every response is NDJSON: one JSON object per line, machine-readable by default
- Every file gets a BLAKE3 checksum: detect drift, verify integrity, enable optimistic locking
What Is New In v0.1.36 (2026-07-31)
- Agent staging:
scratch init+.atomwrite/scratch/for--old-file/--new-file(jail retained; no/tmpallowlist) - Error NDJSON:
ok: false+match_count; fuzzysimilarity: nullon normalized strategies - Global
--select/--filter/--limitfor agent-native NDJSON projection (no jq required) - Pin agents to
^0.1.36. 45 subcommands (SSOT). CLI + XDG only (no productATOMWRITE_*knobs; no product telemetry). Agent-native stdout:--select/--fields,--filter,--limit(no global-n). Error envelopes always emit
What Is New In v0.1.35 (2026-07-19)
- Residual A-* gaps closed (gaps.md §20): suite green without
delete --yes; large overwrite default-deny (--ack-overwrite);watchalways emitswatch_summary; semantic-merge conflict markers default ON; monólitosinclude!SRP splits - Write large files: existing targets above XDG
[write].confirm_large_bytes(default 100 KiB) require--ack-overwrite(agent one-shot; no interactive Y/N).--confirmis a legacy awareness flag;--require-large-ackis independent (no alias collision) - Delete: use
--planor--dry-runfor plan-only;delete --confirmanddelete --yes/-yare rejected (fail-closed). Run again without those flags to delete - Watch: final NDJSON
type:watch_summaryon every exit path; idle default 500 ms (--idle-exit-ms/ XDG[watch].idle_exit_ms); debounce via CLI or XDG[watch].debounce_ms - Semantic-merge: conflict markers written by default; opt-out
--no-conflict-markers - Local DoD (no product GitHub Actions):
cargo test --lib --tests,cargo clippy --all-targets -- -D warnings,cargo check --target x86_64-pc-windows-gnu,cargo install --path . --force - Contract suite:
cargo test --test cli_e2e_v0135 - Pin agents to
^0.1.36(was 0.1.35). Configuration is CLI + XDG /.atomwrite.tomlonly (no productATOMWRITE_*runtime knobs; no product telemetry)
What Is New In v0.1.34 (2026-07-19)
- CRITICAL one-shot fix:
replace --fuzzyno longer hangs whenreplacementembedspattern(agent “expand section” footgun — NEW may contain OLD) - Fuzzy multi-apply is one-pass on the original content (
apply_fuzzy_one_pass); never re-scans text just inserted - Default max fuzzy applies = 1 when
--max-replacementsis omitted; hard ceiling 10_000 - If
replacementcontainspattern(fixed-string), forces a single apply even with a huge--max-replacements - Global
--timeout-secsdefault is 120 (was 0); pass0to disable; exit 124 on deadline (GNUtimeoutconvention) - Fuzzy cascade polls cooperative cancel mid-file so the timeout works; resource caps: pattern 64 KiB, lev 8192 chars, windows 4096, growth max(4×, +16 MiB)
- Docs fully aligned with the runtime contract; ADR-0054; pin agents to
^0.1.34 - Regression:
tests/cli_v0133_oneshot_fuzzy.rs(hang < 2s). Historical (at v0.1.33): 41 subcommands unchanged; no telemetry
What Was New In v0.1.30 (2026-07-13)
- Fuzzy mandatory: only
autoandaggressive;--fuzzy offrejected (exit 65) with migration note. Superseded in v0.1.35: Off = exact-only (G-010). edit --replace-allwith uniqueness: multi-match without it failsMATCH_AMBIGUOUS; success NDJSON includesmatch_count- Fuzzy success NDJSON includes
indent_adjustedwhen indent delta realigns the replacement - Escape-drift guard blocks spurious escaped quotes; unicode_normalized strategy preserves typography
- Match failures emit ranked
best_candidate,candidates[], anddiff_preview search --target content|files|bothwith--offset/--limit; path typos getsimilar_pathsreplace --progress-everyreportstotal,rate_per_s,eta_msrecipedefault exclude*.bak.*(including hash step);sparse outlineemits real ASToutline_itemunder budgetplatform.backup_methodis honestreflink_or_copy(never hardlink of live file).atomwrite.toml [fuzzy]is applied to edit/replace/loop/batch;mode = "off"rejected at parsesemantic-mergehelp admits line-based three-way merge (not AST)- Contract suite:
tests/cli_v0130_agent_contract.rs
What Was New In v0.1.29 (2026-07-13)
- Historical (at v0.1.29): 41 subcommands (was 33): added
semantic-merge,sparse,recipe,stat,agent-surface,watch,codemod,semantic-search - Shared fuzzy module: 9-strategy cascade for
edit,replace,batch,edit-loop—replace --fuzzy auto|aggressiveand--fuzzy-threshold best_candidatestructured diagnostics on match failure (near-miss scores no longer discarded)- Cargo features:
core,ast,lang-*,watch,semantic,full— slim core build without AST crates (stubs exit 78) - Cooperative cancel during stdin read and chunked atomic write
semantic-mergethree-way merge with shutdown checks (line-based; not AST)sparse list|readbudgeted monorepo surfacereplace --progress-everyand batch progress heartbeatsrecipe list|runwith real dispatch search→replace→hash (no longer plan-only stub); versioned recipes underrecipes/*.yamlwrite --durability full|fast|autowith NDJSONplatform.durability; Linuxrenameat2inatomic_renamewith ENOSYS fallback- Backup uses reflink_or_copy (never hardlink of the live file)
statalias forread --stat;agent-surfaceclap-derived inventory (anti-MCP, no MCP server)watchdebounce coalesce + optional--checksum+ gitignore (featurewatch)semantic-search --index-diroffline Jaccard inverted index (featuresemantic)codemodcampaigncodemod_summary.by_rule_id- Local size-gate (manual): slim core release ≤ 15 MiB; measured slim ~7.7 MiB; default/full AST ~52 MB — PRD 5–8 MB applies to core only
- Default fixed-string
replaceuses fuzzy=auto after exact multi-match misses (breaking vs 0.1.28 exact-only exit 1)
What Was New In v0.1.28 (2026-07-06)
- BREAKING:
deletenow creates a backup by default (was opt-in) — pass--no-backupto disable;--keep-backupondeleteis redundant (deletion backups are always preserved) and now surfaces awarningsfield in the NDJSON envelope - BREAKING:
move/copyoverwriting an existing destination now require--forceOR an explicit--backup - GAP-CLI-SURFACE-DRIFT closed — single
BackupOptsstruct (--backup,--no-backup,--keep-backup,--retention <N>) via#[command(flatten)]unifies the backup contract across 15 mutating subcommands:write,edit,edit-loop,replace,transform,scope,apply,set,del,case,batch,delete,move,copy,rollback.replace --retentionanddelete --no-backupnow work (previously exit 2) --backupand--no-backupare now mutually exclusive (exit 2) instead of the last flag silently winningrollbackkeeps its pre-rollback safety snapshot opt-in via explicit--backup— the one documented exception to the unified default-true contract- GAP-CONFIG-DEFAULTS-DEAD closed —
.atomwrite.toml[defaults]backup/retentionkeys are now effective; precedence isATOMWRITE_BACKUPenv > CLI flags >[defaults]config > built-in default (true/5) edit's stdin-consuming modes (--after-line,--before-line,--range,--after-match,--before-match,--between,--multi) now fail fast with exit 65 and an actionablesuggestionwhen stdin is a terminal, instead of hanging indefinitelybatch --retention <N>andbatch --backupwere accepted but silently ignored — both are now effective end-to-end, including the batch transactional pre-backup and thedeleteoperation- 661 tests passing, zero clippy warnings, zero fmt diffs
What Was New In v0.1.27 (2026-06-24)
- 10 bugs fixed across 3 rounds of security and scope audit
- CRITICAL security fix: symlink-directory escape from workspace jail (BUG-SEC-001) — all write/read paths now resolve symlinks via
canonicalize_existing_prefixbefore jail check - CRITICAL scope fix:
scope --query comments --deleteno longer destroys code on lines with inline comments (BUG-SCOPE-004) scopeRust queries now matchpubitems for all query types (fn, struct, enum, trait, const, static, type-alias, mod, use)- Go
scope --query varnow handles type-inferred declarations (var x = 0) edit-loophardened: empty stdin and invalid JSON now return proper NDJSON error envelopesedit-looppair_resultsnow includesoldandnewfields for traceability- 3 known limitations documented: GAP-01 (test-fn), GAP-02 (doc-comment), GAP-03 (JS export)
- 631+ tests passing, zero clippy warnings, zero fmt diffs
What Was New In v0.1.25 (2026-06-22)
- 49 bugs fixed across 6 rounds of e2e audit (GAP-071 through GAP-134)
.atomwrite.tomlconfiguration file with hierarchy: CLI > env > local > XDG global > defaults- New
verifysubcommand for BLAKE3 checksum verification - Jaro-Winkler fuzzy matching + configurable
--fuzzy-thresholdfor edit copy --preservenow copies permissions AND timestamps from source- All
--backupflows now retain.bakfiles on disk (copy, replace, write --require-backup) scope --query commentscaptures block comments in addition to line comments- 3 critical fixes: phantom backup_path, TOML scalar misroute, case silent no-op
- I/O errors via anyhow now emit proper NDJSON envelopes (7 subcommands fixed)
- 631 tests passing, ~505 e2e scenarios across 6 audit rounds
What Was New In v0.1.24 (2026-06-21)
- 52 bugs fixed in comprehensive end-to-end audit (GAP-019 through GAP-070)
- ALL user-facing errors now emit typed JSON on stdout (20 bail! converted to AtomwriteError)
delete --recursive,hash --recursive,search --multilinenow actually workreplacerejects empty pattern (was a silent data-destruction bug)- Backup timestamp resolution improved to milliseconds (prevents collision)
scope/count/transform/diffnow resolve paths against--workspaceget/set/delvalues no longer doubly-quotedbatch --transactionproperly revertsmove/copyon rollback- 3 new ADRs (0045-0047)
- 621 tests passing, 12 new for v0.1.24
What Was New In v0.1.23 (2026-06-19)
- GAP-2026-015 closed —
allow_hyphen_valuesadded to 15 CLI text-accepting fields across 8 structs; values starting with-(Markdown bullets, negative numbers, YAML, diffs) no longer cause exit 2 - GAP-2026-016 closed — backup enabled by default in 9 content-mutating structs (
write,edit,edit-loop,replace,transform,apply,set,del,case); use--no-backuporATOMWRITE_BACKUP=0to opt out - GAP-2026-017 closed — shrink guard blocks writes that reduce file size by >50% when
--expect-checksumis active; pass--allow-shrinkto override - GAP-2026-018 closed —
edit --old-file <PATH> --new-file <PATH>reads match/replacement content from files inside the atomwrite process, bypassing shell expansion and kernel ARG_MAX (~131 KB limit); cross-mixing validation rejects--old+--new-file(exit 65); trailing newline stripping ensures argv parity - 631+ tests passing (see v0.1.25 for latest count)
- 4 new ADRs: 0041 (allow-hyphen-values), 0042 (backup-by-default), 0043 (shrink-guard), 0044 (edit-old-file-new-file)
What Was New In v0.1.22 (2026-06-17)
- 2 new subcommands:
edit-loopandprune-backups edit-loop <PATH>— apply N{old, new}substitution pairs via NDJSON on stdin in 1 invocation (reduces 5x subprocess overhead for batch edits). ADR-0039prune-backups [PATHS]...— manual cleanup of legacy.bak.YYYYMMDD_HHMMSSsiblings with--max-age,--max-count,--dry-run(default true for safety). ADR-0040- 2 new NDJSON schemas:
edit-loop-output.schema.json,prune-backups-output.schema.json - 575+ tests passing in 56+ integration suites, 0 failures, 0 clippy warnings
What Was New In v0.1.21 (2026-06-17)
- 3 GAP-2026 closed in v0.1.21
--allow-sequential-driftonedit— opt-in flag for sequential edit pipelines. ADR-0038--backupparity 4/4 —editandrollbacknow accept--backup(were hardcodedfalse)--keep-backupon 6 subcommands (write,edit,replace,rollback,apply,batch) — opt-in flag to preserve the backup after success- Default behavior change — backups are DELETED after successful
--backupoperations; add--keep-backupto preserve the previous behavior - 555+ tests passing, 1 new ADR (0038), 3 Windows cross-compile targets green
What Was New In v0.1.20 (2026-06-15)
- 542 tests passing in 47 test suites (502 in v0.1.18 + 21 v0.1.19 + 19 v0.1.20), 0 failures, 0 clippy warnings
- 11 GAP-2026 closed in v0.1.20
- Intention guards — a new OPT-IN safety layer of 5 flags:
--require-backup N,--confirm,--auto-rotate N,--risk-threshold LOW|MEDIUM|HIGH, and--locale en|pt-BR. They intercept destructive mutations before they touch disk. ADR-0032 --localerename from--langto disambiguate from the tree-sitter--langused byscopeandtransform. The old--langis kept as a hidden alias for one minor version. ADR-0034count --by-size— list the largest files in the tree with sizes and line counts. ADR-0035read --mode raw|envelope— select between byte-stream output and structured NDJSON envelope. ADR-0036search --no-begin-end— disable the implicit^and$anchor decoration in regex output. ADR-0037scope --lang rust— explicit alias accepted for ergonomic symmetry withtransform --lang. ADR-0033write --preserve-timestamps— keep the source file mtime when overwriting- 19 ADRs in
docs/decisions/(0019-0037). 3 Windows cross-compile targets green (x86_64-gnu, i686-gnu, x86_64-msvc)
What Was New In v0.1.19 (2026-06-14)
- G121 path resolution helper that unifies jail resolution across all write subcommands. Single source of truth for the workspace boundary check
- Real S-expression
querymode viatree-sitter— agents can now run structured AST queries against source files - 7 exit code documentation drifts consolidated to match the canonical list in
docs/exit-codes.md(ADR-0031) - 21 new tests across 3 integration suites. Cross-compile targets remain green
What Was New In v0.1.17 (2026-06-13)
- L3 startup auto-heal:
atomwriteruns an autonomouswal-healpass on startup with a 3600s threshold and 100ms budget. The pass is opt-out via--skip-startup-wal-heal(seesrc/cli.rs). The explicitwal-healsubcommand landed in v0.1.15; v0.1.17 wires the same logic intolib.rs::runbefore any subcommand dispatch
What Was New In v0.1.16 (2026-06-13)
- L1 WalPolicy:
enum WalPolicy { Auto, Always, Never }insrc/wal.rs, exposed via--wal-policyflag onwriteandedit. DefaultAutoskips the WAL sidecar for trivial writes (size under 1 MiB, not Edit/Replace, directory under Git, write under 4 KiB) - L4 HeuristicsEngine:
crate::wal::heuristicssubmodule with 5 composable functions (h1_ttl,h2_lru_within_cap,h3_rate_limit,h4_sentinel,h5_archive). Env vars:ATOMWRITE_WAL_KEEP_SECS,ATOMWRITE_WAL_MAX_COUNT,ATOMWRITE_WAL_RATE_LIMIT,ATOMWRITE_WAL_ARCHIVE_DAYS. Telemetry fieldwal_policyonWriteOutputNDJSON
What Was New In v0.1.15 (2026-06-11)
- G117 fixed — multi-pair
editreaches fuzzy parity with the single-pair path: repeated--old/--newpairs now run the full 9-strategy fuzzy cascade per pair instead of exact-only matching - Per-pair reporting: success envelopes gain
pairs_totalandpair_results(1-basedindex,matched,strategy,similarity); error envelopes gainfailed_pair_index,pairs_total, andpair_results— no more manual bisection of failed batches - New
--partialflag (opt-in): applies the matching pairs and reports the rest withmatched: false; zero applied pairs exits 1 (NO_MATCHES) without writing; the default stays all-or-nothing - Anti-masking recipe:
edit ... | jaq '.edits'hides exit 65 as{"edits": null}— always usejaq -e '.edits'or check${PIPESTATUS[0]} - G118 fixed —
writeresolves the target against the workspace before every pre-step: with a CWD outside the workspace,--append/--prependtruncated the file,--expect-checksumwas silently skipped, and--line-ending autolost detection (CWE-367 double path identity); checksum drift now exits 82 (STATE_DRIFT) and out-of-jail targets fail early with exit 126 (ADR-0027) - GAP 18 fixed — Windows snapshot tests are platform-stable again: the write snapshot now redacts
dir_fsyncas[platform_dir_fsync](Windows reportsbest_effort, Unixsync_all) - MSRV aligned: local checks now use the documented MSRV 1.88 (the pin was previously to 1.85)
- 461 tests passing (445 in v0.1.12 + 2 in v0.1.14 + 8 G117 + 6 G118). 43 test suites, 0 failures
What Was New In v0.1.12 (2026-06-07)
- 6 new subcommands (v14 Tier 3):
set,get,del,case,query,outlinefor structured config editing and AST analysis - G72 REAL syntax check --
atomwrite write --syntax-checkinvokes the realtree-sitterparser (24 languages covered) instead of the bracket-balance heuristic. Exit code 88 on the first syntax error - G114 WAL sidecar for crash recovery --
atomic_writewrites.atomwrite.journal.<target>.atomwrite.journal.jsonwithStartedandCommittedentries.recover_orphan_journals(dir)is consultative: reports orphans without auto-replay - 5 new error variants:
LockTimeout(83),SyntaxError(88),ExdevFallbackDisabled(91),CopyBackBlake3Failed(92),OrphanJournal(93). All bilingual EN/PT-BR with actionableErrorContextsuggestions - Dependency added:
tree-sitter-language-pack = "1.8"withdownload+dynamic-loadingfeatures. Parsers download on first use, install footprint stays around 5-10 MB instead of 1+ GB - 445 tests passing (was 320 baseline in v0.1.10, +125 new across v0.1.11 and v0.1.12). 43 integration test suites, 0 failures
- 7 ADRs in
docs/decisions/documenting all architectural decisions for v0.1.12 (tree-sitter-language-pack choice, WAL sidecar design, query/outline kind-name only, etc.) - 7 new JSON Schemas in
docs/schemas/(set, get, del, case, query, outline, wal-recovery)
See docs/HOW_TO_USE.md for the full v0.1.12 quickstart and docs/MIGRATION.md for the v0.1.11 → v0.1.12 upgrade path. v0.1.11 is the previous release.
What Was New In v0.1.11 (2026-06-05)
signal_test::shutdown_message_on_stderrno longer fails on Windows when stderr is a pipe —libc::write(STDERR_FILENO, ...)was moved fromsrc/main.rstosrc/signal.rsand gated by#[cfg(unix)]. The Windowsctrlcpath was kept as-is. Also added anEAGAINandEINTRretry loop to make the write robust against interrupted syscalls and tight pipe buffer limits under redirected stderr- Race-free readiness detection in
signal_test— The test now setsATOMWRITE_READY_FILEto a path under the tempdir, and atomwrite writes its PID there as soon asinstall_handlers_earlyreturns. The test polls the file with a 10 s deadline before sending SIGINT, eliminating the microsecond window where SIGINT could race withposix_spawnand arrive before the kernel'ssigactionis configured - Idempotent signal-handler installation —
install_handlers_earlyandinstall_handlersnow share a singleArc<ShutdownSignal>viaOnceCell. Previously each function created its own instance, and only the first instance was flipped by the signal-hook chain, so the main thread'sis_shutdown()check stayedfalseand the banner was never written
What Was New In v0.1.10 (2026-06-05)
- GAP 20 follow-up:
signal_test::shutdown_message_on_stderrflushes the shutdown message viaio::stderr().lock(). The v0.1.8 fix movedeprintln!from the signal handler to the main thread but usedwriteln!(io::stderr(), ...)which is fully buffered when stderr is redirected to a pipe. The fix uses theStderrLockguard that flushes on Drop
What Was New In v0.1.8 (2026-06-05)
signal_test::shutdown_message_on_stderrno longer fails when stderr is redirected on Linux —eprintln!removed from SIGINT/SIGTERM signal handlers per POSIX.1-2017signal-safety(7). Message now emitted by main thread insrc/main.rsafteratomwrite::runreturnsatomic::tests::create_backup_and_retentionno longer fails on Windows under restricted fsync —platform::fsync_file_best_effortlogs a warning and continues onERROR_ACCESS_DENIED
What Was New In v0.1.7 (2026-06-05)
- MSRV bumped to 1.88 — Required to allow
time0.3.47 which resolves RUSTSEC-2026-0009 - Cross-compile Windows validated locally on macOS — Both
x86_64-pc-windows-gnuandi686-pc-windows-gnupasscargo check,cargo build,cargo clippy --all-targets --all-features -- -D warnings, andcargo test --no-run
What Was New In v0.1.4
cargo install atomwriteworks on Windows 10/11 — Three compilation errors in#[cfg(windows)]blocks are fixed (E0433, E0507, E0308)- Context-aware error suggestions —
WorkspaceJailsuggestion adapts when--workspaceis already provided - Cross-compile gate —
tests/cross_compile_check.rsfails on anyE0433,E0308, orE0507regression incfg(windows)blocks
What Was New In v0.1.3
--preserve-timestampsflag oneditandreplaceto control file mtime (default: mtime is updated to reflect the change)mtime_preservedfield inEditOutputandReplaceResultNDJSON responses- BREAKING: atomic write no longer preserves the original file mtime by default. Fixes a silent no-op in
cargo build/make/cmake/gradle
Why
- LLM agents juggle dozens of shell commands to manipulate files
- A single power failure or crash mid-write corrupts the file
- Parsing unstructured CLI output wastes tokens and causes hallucinations
- Agents need checksums to detect concurrent edits but rarely compute them
- atomwrite solves all four problems with one
cargo install
Superpowers
Atomic Writes
- Uses tempfile + fsync + rename + directory fsync on every write
- Guarantees all-or-nothing: the file is never left half-written
- Survives power loss, OOM kills, and SIGKILL
- Optional G72 REAL syntax check via tree-sitter (
--syntax-checkflag onwrite) - Optional G114 WAL sidecar for crash recovery (
.atomwrite.journal.<target>.atomwrite.journal.json)
NDJSON Output
- stdout is ALWAYS structured JSON, one object per line
- Every object carries a
"type"discriminator field - Agents parse output without regex or brittle text scraping
- Errors also emit JSON with
error: trueon stdout
BLAKE3 Checksums
- Every
readandwriteresponse includes a BLAKE3 hash - Use
--expect-checksumfor optimistic locking on concurrent edits - Detect state drift before applying changes
Parallel Search
- Built on the ripgrep engine for file content search
- Respects
.gitignoreautomatically - Returns structured matches with file, line, column, and context
AST-Aware Transforms
- Structural search and rewrite powered by ast-grep
- Covers 306 programming languages
- Refactor code by syntax tree, not fragile regex
- New
queryandoutlinesubcommands walk tree-sitter ASTs (305 languages) viatree-sitter-language-pack
Grammatical Scoping
- Select AST categories like comments, functions, classes, and strings
- Apply actions: delete, uppercase, lowercase, titlecase, squeeze, or replace
- Covers Rust, Python, JavaScript, TypeScript, and Go with prepared queries
- Use
--patternfor custom AST patterns beyond the built-in queries
Batch Operations
- Execute write, replace, delete, edit, hash, move, and copy operations from an NDJSON manifest
- Use
--transactionfor all-or-nothing execution with automatic rollback - All operations in a batch share the same atomic guarantees
Structured Config Editing (v0.1.12)
setwrites a value at a dotted path in TOML or JSON files (preserves comments viatoml_edit)getreads a value at a dotted path with auto-detected formatdelremoves a key (with--force-missingto treat missing keys as no-op success)caserenames identifiers across multiple files viaheck(snake, camel, pascal, kebab, screaming-snake)
Quick Start
# Write a file atomically from stdin
|
# Read it back with checksum
# Search across a directory
# Replace text with atomic writes
# Evaluate math and unit conversions
# v0.1.12: set a value in a TOML file (preserves comments)
# v0.1.12: walk the AST of a Rust file
# v0.1.12: extract the outline of a Python file
# v0.1.12: REAL tree-sitter syntax check before committing
|
# v0.1.15: report stale sidecars and estimate reclaim
# v0.1.15: reap journals older than the threshold
Install (slim vs full)
- Features:
core,ast,lang-*,watch,semantic,full(default includes AST) - Slim (core only, ~7.7 MiB, local size-gate ≤ 15 MiB):
cargo install --path . --locked --force --no-default-features --features core - Full/default (AST languages, ~52 MB):
cargo install --path . --locked --force - PRD 5–8 MB target applies to the core profile, not the default AST build
- Durability:
write --durability full|fast|auto(default auto) - Recipe:
atomwrite recipe run --name search-replace-verify --pattern OLD --replacement NEW --path src/
Installation
From crates.io
From source
Shell Completions
# Bash
# Zsh
# Fish
Usage
- All output goes to stdout as NDJSON
- All logs go to stderr (only with
--verbose) - Always pass
--workspace <DIR>to restrict operations to a project root - Use
--dry-runbefore destructive operations - Use
--expect-checksum <HASH>for optimistic locking - Use
--locale <TAG>oratomwrite locale --set/locale --clearfor UI locale (en,pt-BR); NDJSON codes stay English - Configuration is CLI flags + XDG /
.atomwrite.tomlonly — no productATOMWRITE_*runtime knobs (v0.1.35+) - Pipe stdin for
writeandbatchcommands - Agent-native stdout globals (all subcommands):
--select/--fields(CSV top-level keys),--filter KEY=VAL(repeatable AND),--limit N(long-only — no global-n;replaceowns short-n) - Error NDJSON always has
ok: falseand usuallymatch_count— parseexit/codefirst; success shaping via--filter/--limitnever drops error envelopes
Commands (45 total — SSOT)
Core I/O
read— read files with metadata, checksum, optional contentwrite— create or overwrite files atomically via stdin (large existing targets need--ack-overwrite)edit— surgically edit by line number, text marker, or exact matchedit-loop— apply Nold/newpairs from NDJSON stdin in one atomic writedelete— delete files with optional backup (--planplan-only;--confirm/--yesrejected)copy— copy files with checksum verificationmove— move or rename files atomically (EXDEV copy-fallback)apply— apply patches from stdin (unified diff, search/replace, full, markdown)scratch— create agent staging dir under workspace (never OS/tmp)verify— verify file integrity by comparing BLAKE3 checksum
Search and replace
search— search file contents in parallel (ripgrep engine)replace— replace text across files with atomic writestransform— AST refactoring via ast-grep (306 languages)codemod— multi-rule codemod campaign (YAML rules, delegates to transform)semantic-search— offline token Jaccard search (no network, no embeddings API)semantic-merge— three-way line-based merge (not AST/embedding) for multi-agent writes
Inspection
hash— calculate BLAKE3 checksumsstat— metadata alias forread --stat(size, mode, checksum; no body)count— count lines, files by extensiondiff— compare two files (unified, stat, or changes)list— list files in a directory treesparse— budgeted sparse list/read (cap files and bytes for agent context)extract— extract fields from NDJSON input via pipe (positional fields or local--field; never localextract --select— global--selectonly projects output keys)scope— grammatical scoping (delete all comments, etc.)regex— generate regex from examplescalc— math and unit conversionsdoctor— diagnose environment and dependencieslocale— show resolved UI locale / persist preference (XDG)commands— emit full command tree as JSON for agent discoveryagent-surface— agent tool surface manifesto without MCPwatch— watch filesystem events (featurewatch)completions— generate shell completions (bash, zsh, fish, elvish, powershell)
Backup and recovery
backup— create timestamped backups with BLAKE3 checksumsrollback— restore from a previous backupprune-backups— prune.bak.YYYYMMDD_HHMMSSbackups by age or countbatch— NDJSON-driven batch operations (transactional)recipe— named multi-step recipes (list/run)
Structured config editors (v0.1.12, v14 Tier 3)
set <PATH> <KEY_PATH> <VALUE>— write a value at a dotted path in a TOML or JSON file. Preserves comments and key order viatoml_edit. Auto-coerces int/bool/float/string.get <PATH> <KEY_PATH>— read a value at a dotted path. NDJSON:{"type":"get","key_path","value","found","format"}.del <PATH> <KEY_PATH>— remove a key.--force-missingflag treats missing keys as a no-op success.case <PATHS...> --subvert OLD NEW --to <style>— rename identifiers across multiple files viaheck. Styles:snake,camel,pascal,kebab,screaming-snake.
AST tools (v0.1.12, v14 Tier 3 + G72, via tree-sitter-language-pack)
query <PATH> [--kinds|--query <KIND>|-Q <KIND>|--tree] [--positions]— walk a tree-sitter AST and emit nodes as NDJSON. 305 languages supported.outline <PATH> [--kind <KIND>] [--positions]— extract high-level structure (functions, classes, structs, enums, traits, modules) as NDJSON.write --syntax-check— G72 REAL syntax check via tree-sitter. 24 languages covered. Exit 88 with first error line/column/kind/message.
WAL cleanup (v0.1.15, G119)
wal-stats— telemetry snapshot: total journals, by-state breakdown, oldest age, total size, by-directory breakdown, auto-heal recommendation, estimated reclaim byteswal-heal [--threshold-secs N] [--budget-ms N]— reap stale terminal journals older than the threshold. Safe and idempotent
Common patterns for each subcommand
# read
# write
|
# edit
# edit-loop (N old/new pairs, one atomic write)
|
# scratch — stage under .atomwrite/scratch/; never OS /tmp outside --workspace for --old-file/--new-file
# verify
# search
# replace
# hash
# delete
# count
# diff
# move
# copy
# list
# extract — positional fields or local --field; NEVER local extract --select
|
|
# thin agent shape (global --select projects output keys only):
# calc
# regex
# transform
# scope
# batch
|
# backup
# rollback
# prune-backups
# apply
|
# set (v0.1.12)
# get (v0.1.12)
# del (v0.1.12)
# case (v0.1.12)
# query (v0.1.12)
# outline (v0.1.12)
# wal-stats (v0.1.15)
# wal-heal (v0.1.15)
# stat
# sparse
# semantic-merge (line-based, not AST)
# recipe
# agent-surface
# watch (requires feature watch)
# codemod
# semantic-search
# doctor
# locale
# commands (full clap tree for agent discovery)
# completions
Environment Variables
- v0.1.35 product policy: atomwrite does not use product environment variables for runtime knobs (workspace, backup, locale, log filter, home override, WAL, etc.).
- Configure via CLI flags and XDG storage /
.atomwrite.toml(and optional--config). doctormay observe host environment variables (for example CI indicators) for diagnostics only — that is not a configuration API.- Shell-level variables used by your agent harness (PATH, etc.) are outside atomwrite product config.
Exit Codes
- Error envelopes on stdout are always NDJSON with
ok: false(and usuallymatch_count); readexit/codebefore treating a line as success 0: success1: no matches found (search, not an error)4: file not found13: permission denied28: disk full (no space left on device)30: quota exceeded65: invalid input (bad arguments or malformed data, including empty stdin without--allow-empty-stdinsince v0.1.16)73: cross-device rename (filesystem boundary)74: I/O error78: configuration invalid81: checksum verify failed82: state drift (checksum mismatch, optimistic lock failed)83: lock timeout (v0.1.12+)85: FIFO detected (named pipe cannot be atomically written)86: device file detected (block or character device)88: syntax error detected (v0.1.12+,--syntax-checkfailed)91: EXDEV fallback disabled (v0.1.12+,--strict-atomicforbids cross-device copy-fallback)92: copy-back BLAKE3 verification failed (v0.1.12+)93: orphan journal detected (v0.1.12+, G114 consultive recovery)126: workspace jail violated (path escapes workspace)127: symlink blocked (symlink target outside workspace)128: file immutable (cannot modify)124: global--timeout-secsdeadline exceeded (default 120; pass0to disable)130: interrupted by SIGINT141: broken pipe (SIGPIPE)143: terminated by SIGTERM255: internal error
Signal Handling
- Unix: SIGINT (Ctrl+C) and SIGTERM intercepted for graceful shutdown
- Unix: SIGPIPE reset to SIG_DFL for standard pipe behavior (exit 141)
- Windows: Ctrl+C intercepted via console handler
- First signal: sets shutdown flag, prints "shutting down..." to stderr
- Second signal: immediate process termination via
_exit(Unix) orexit(Windows) - Walker threads (search, replace, transform, scope) stop between files
- Batch operations stop between operations
Error Handling
- All errors emit a JSON object on stdout with
error: true - Error fields:
code,exit,message,path,error_class,retryable,suggestion,workspace - Error classes:
permanent,transient,conflict,precondition_failed - 25 error variants total (20 baseline from v0.1.4 + 5 added in v0.1.12)
- Transient and conflict errors set
retryable: true - The
suggestionfield provides actionable recovery guidance for agents - See
docs/schemas/error-output.schema.jsonfor the full contract
Performance
- Single static binary with zero runtime dependencies
- Release builds use LTO, single codegen unit, and symbol stripping
- Memory-mapped file reads via
memmap2for large files - Parallel multi-file work is the default modus operandi:
ignore::WalkParallel/collect_files_parallel(search content +--target files, replace, count, transform, scope, recursive discovery) +rayon(hash, backup, multi-path + recursive delete/copy, case multi-path, semantic-search scoring, non-txn batch, prune multi-target, sparse read, diff dual-read) - Worker bound:
--threads/-j/--max-concurrency(default = all logical CPUs, RAM-capped; seesrc/concurrency.rs) - Typical file operation latency: under 5 ms for small files
Troubleshooting FAQ
atomwrite write hangs with no output
- Ensure you are piping content to stdin
writereads from stdin and waits for EOF- Example:
echo "content" | atomwrite --workspace . write file.txt
search returns exit code 1
- Exit code 1 means zero matches were found
- This is expected behavior, not an error
- Check the pattern and target path
cross-device rename fails with exit 73
- The source and destination are on different filesystems
- atomwrite falls back to copy+delete for
moveacross devices - Use
copyfollowed bydeleteas an alternative
checksum mismatch with exit 82
- Another process modified the file between read and write
- Re-read the file to get the current checksum
- Retry the operation with the updated
--expect-checksum
workspace jail violated with exit 126
- The target path resolves outside the
--workspaceboundary - Verify the path does not contain
..traversals or symlinks escaping the workspace
syntax check failed with exit 88 (v0.1.12+)
- The G72 REAL tree-sitter syntax check found a syntax error in the file
- Inspect the first error line/column/kind/message in the JSON error envelope
- Fix the syntax and retry, or remove
--syntax-checkto bypass
empty stdin rejected with exit 65 (v0.1.16+)
- The G120 L1 guard rejected 0 bytes from stdin as a likely upstream-pipeline failure
- Pass
--allow-empty-stdinto confirm the empty input is intentional - Or pipe real content:
echo "x" | atomwrite --workspace . write file
edit hangs (or fails with exit 65) on --after-line/--after-match/--between/--multi (v0.1.28+)
- These modes read replacement content from stdin, same as
write - Since v0.1.28 a terminal stdin fails FAST with exit 65
INVALID_INPUTinstead of blocking forever - Pipe content:
echo "new text" | atomwrite --workspace . edit file.rs --after-line 10 - Or use
--new-file <PATH>where the mode accepts it --after-match/--before-match/--between/--after-line/--before-line/--range/--delete-rangereject--old/--new/--old-file/--new-fileat parse time (exit 2) — these modes only take content from stdin
Architecture
- See ARCHITECTURE.md for module map, data flow, and design decisions
- See docs/decisions/ for 12 ADRs covering v0.1.12 onward architecture (G72, G114, v14 Tier 3, G117, G118, G119, G120, v0.1.18 trio)
- See docs/schemas/ for 22 stable JSON Schema contracts for all NDJSON output
Contributing
- See CONTRIBUTING.md for development setup, local DoD, PR process, and release notes
- See CONTRIBUTING.pt-BR.md for the Portuguese guide
- Follow the Code of Conduct / Código de Conduta
Security
- See SECURITY.md for vulnerability reporting
- See SECURITY.md section "Known Security Advisories" for resolved and active advisories
Changelog
- See CHANGELOG.md for English release history
- See CHANGELOG.pt-BR.md for Portuguese release history
License
- Licensed under MIT OR Apache-2.0
- See LICENSE-MIT and LICENSE-APACHE for details