atomwrite 0.1.26

Atomic file operations CLI for LLM agents — read, write, edit, search, replace with NDJSON output
Documentation
# atomwrite

> Atomic file operations for LLM agents — one CLI, zero corruption

atomwrite is a Rust CLI that provides 33 subcommands for file manipulation with atomic write guarantees. Every write uses tempfile + fsync + rename + directory fsync. Every response is NDJSON with BLAKE3 checksums. Built for LLM agents that need reliable, structured file operations without corruption risk.

**Latest release: v0.1.26** (2026-06-23) — 9 bug fixes (GAP-135 to GAP-147): exit code consistency for replace/transform/scope zero-matches (exit 1), search --sort path deterministic ordering, read --verify-checksum exit 81, get missing key exit 4, scope dual AST patterns for Rust generics/return types, verify positional args, edit-loop JSON array input. 4 documentation clarifications (BY-DESIGN). 631+ tests, 33 subcommands. See `CHANGELOG.md` for full notes.

**Previous release: v0.1.25** (2026-06-22) — .atomwrite.toml config file, verify subcommand, Jaro-Winkler fuzzy matching, 49 bug fixes, 631 tests. See `CHANGELOG.md`.

## Installation

```bash
cargo install atomwrite
```

Parsers for 305 languages are NOT bundled; they are downloaded on first use by `tree-sitter-language-pack` and cached in `~/.cache/tree-sitter-language-pack/parsers/`. Install footprint stays small (~5-10 MB vs 1+ GB if all 305 parsers were bundled).

## 33 Subcommands

### Core I/O
- `read` -- read files with metadata, checksum, optional content. `--mode raw|envelope` selects byte-stream vs NDJSON envelope (v0.1.20)
- `write` -- create or overwrite files atomically via stdin. `--preserve-timestamps` keeps source mtime (v0.1.20)
- `edit` -- surgically edit by line number, text marker, or exact/fuzzy match; multi-pair `--old`/`--new` has per-pair fuzzy + `pair_results` + `--partial` (v0.1.15)
- `delete` -- delete files with optional backup
- `copy` -- copy files with checksum verification (reflink on APFS/btrfs/XFS)
- `move` -- move or rename files atomically (EXDEV copy-fallback)
- `apply` -- apply patches from stdin (unified diff, search/replace, full, markdown)

### Search and replace
- `search` -- search file contents in parallel (ripgrep engine). `--no-begin-end` disables anchor emission (v0.1.20)
- `replace` -- replace text across files with atomic writes
- `transform` -- AST refactoring via ast-grep (306 languages)

### Inspection
- `hash` -- calculate BLAKE3 checksums
- `count` -- count lines, files by extension. `--by-size` lists the largest files (v0.1.20)
- `diff` -- compare two files (unified, stat, or changes)
- `list` -- list files in a directory tree
- `extract` -- extract fields from NDJSON input via pipe
- `scope` -- grammatical scoping (delete all comments, etc.). `--lang rust` alias accepted (v0.1.20)
- `regex` -- generate regex from examples
- `calc` -- math and unit conversions
- `completions` -- generate shell completions (bash, zsh, fish, elvish, powershell)

### Backup and recovery
- `backup` -- create timestamped backups with BLAKE3 checksums
- `rollback` -- restore from a previous backup
- `batch` -- NDJSON-driven batch operations (transactional)

### Batch helpers (v0.1.22)
- `edit-loop` -- N edits in 1 invocation via JSON array or NDJSON stdin
- `prune-backups` -- manual cleanup of legacy `.bak.*` files with `--max-age-secs`, `--max-count`

### WAL diagnostics (v0.1.15)
- `wal-stats` -- snapshot of WAL journal state (counts, ages, sizes)
- `wal-heal` -- remove orphan terminal journals older than threshold

### Verification (v0.1.25)
- `verify <PATH> <HASH>` -- verify BLAKE3 checksum of a file (delegates to `hash --verify`)

### Structured config editors (v14 Tier 3, v0.1.12)
- `set <PATH> <KEY_PATH> <VALUE>` -- write a value at a dotted path in a TOML or JSON file. Preserves comments and key order via `toml_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-missing` flag treats missing keys as a no-op success.
- `case <PATHS...> --subvert OLD NEW --to <style>` -- rename identifiers across multiple files via `heck`. Styles: `snake`, `camel`, `pascal`, `kebab`, `screaming-snake`.

### AST tools (v14 Tier 3 + G72, v0.1.12, 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.

## v0.1.20 — Intention Guards (New Safety Layer)

Intention guards intercept destructive mutations before they touch the disk. All five flags are OPT-IN. Default behavior is unchanged.

### `--require-backup <N>`
Refuse the operation when fewer than `N` retained backups exist for the target. Forces the agent to take a backup ring snapshot before mutating. Exit code is non-zero and the NDJSON `error.code` is `IntentionGuardRefused`.

### `--confirm`
Emit a confirmation prompt listing the planned mutation in NDJSON before executing. The prompt includes target, byte delta, expected checksum, and risk classification. The agent must answer `y` on stdin to proceed.

### `--auto-rotate <N>`
Automatically rotate the backup ring down to `N` entries after a successful write. Pairs naturally with `--backup --retention N` so the backup ring is bounded.

### `--risk-threshold <LOW|MEDIUM|HIGH>`
Block operations whose classified risk meets or exceeds the threshold. Risk is computed from path depth, byte delta, target extension, and history. Exit code is non-zero and `error.class` is `IntentionGuardRisk`.

### `--locale <en|pt-BR>`
Renamed from `--lang` to disambiguate from the tree-sitter `--lang` used by `scope` and `transform`. The old `--lang` is still accepted as a hidden alias for one minor version to ease migration. See `docs/MIGRATION.md` for the `ruplacer` recipe.

## v0.1.20 — Other Additions

- `count --by-size` -- list the largest files in the tree with sizes and line counts
- `read --mode raw|envelope` -- select between byte-stream output and structured NDJSON envelope
- `search --no-begin-end` -- disable the implicit `^` and `$` anchor decoration in regex output
- `write --preserve-timestamps` -- keep the source file mtime when overwriting
- `scope --lang rust` -- explicit alias accepted for ergonomic symmetry with `transform --lang`

## v0.1.21 — Sequential Edits and Backup Deletion
### `--keep-backup` on 6 subcommands
- `--keep-backup` is the OPT-IN flag that preserves the backup file after a successful `--backup` write
- Accepted by `write`, `edit`, `replace`, `rollback`, `apply`, and `batch`
- DEFAULT behavior (no `--keep-backup`) is that the backup is DELETED after a successful write
- Backups are ALWAYS preserved when the write FAILS, regardless of `--keep-backup`
- Pre-v0.1.21 behavior (backup lives forever after success) is REMOVED

### `--allow-sequential-drift` on `edit`
- `--allow-sequential-drift` is the OPT-IN flag that suppresses `STATE_DRIFT` (exit 82) on subsequent `edit` calls in a sequential pipeline
- The flag emits `tracing::warn!` naming the drift and proceeds with the edit (exit 0 on success)
- Use Pattern A (re-capture `checksum_after` after each `edit`) for ZERO drift risk; use Pattern B (pass `--allow-sequential-drift` on every call) for fewer CLI invocations
- DO NOT use `--allow-sequential-drift` in a TRUE parallel scenario; concurrent agents should use Pattern A

### `--backup` parity 4/4
- `--backup` is now accepted by `write`, `edit`, `replace`, and `rollback` (was 2/4: only `write` and `replace` in v0.1.20)
- `rollback` gains `--backup` for symmetry with `write` and `edit`; pass-through behavior documented in `docs/HOW_TO_USE.md`

## Output Contract

Every command outputs **NDJSON** (one JSON object per line) to stdout. The last line is always a `*_summary` event. Schema files are in `docs/schemas/`. The contract is stable: if you write a parser for v0.1.20 output, it will keep working in v0.2.x with only minor additive changes.

### Critical NDJSON fields
- `type` -- operation name (`set`, `get`, `del`, `case`, `query_match`, `outline_item`, `write`, `edit`, ...)
- `checksum` (BLAKE3) -- the new file's content hash
- `checksum_before` (BLAKE3) -- the file's hash before the operation
- `mtime_preserved` (v0.1.3+) -- true if mtime was kept, false if updated
- `bytes_written` -- total bytes written to the file
- `elapsed_ms` -- wall-clock time for the operation

## Key Flags

- `--workspace <DIR>` -- workspace root for path jail validation (REQUIRED for file ops)
- `--expect-checksum <HASH>` -- optimistic locking for concurrent edits
- `--require-backup <N>` (v0.1.20) -- intention guard: refuse if fewer than N backups exist
- `--confirm` (v0.1.20) -- intention guard: prompt before mutating
- `--auto-rotate <N>` (v0.1.20) -- intention guard: rotate backup ring to N after write
- `--risk-threshold <LOW|MEDIUM|HIGH>` (v0.1.20) -- intention guard: block above threshold
- `--locale <en|pt-BR>` (v0.1.20, was `--lang`) -- display locale override
- `--verbose` / `-v` -- enable tracing logs on stderr
- `--include <PATTERN>` / `-g <PATTERN>` -- filter files by glob pattern
- `--stat` -- skip file content in read responses (metadata only)
- `--mode raw|envelope` (read, v0.1.20) -- select output mode
- `--by-size` (count, v0.1.20) -- list largest files
- `--no-begin-end` (search, v0.1.20) -- disable anchor emission
- `--preserve-timestamps` (edit, replace, write) -- keep the original file mtime
- `--syntax-check` (write, v0.1.12+) -- G72 tree-sitter REAL syntax check before commit
- `--positions` (query, outline) -- include start_line/start_column/byte_offset in AST output
- `--line-ending lf|crlf|cr|auto` -- normalize line endings
- `--timeout <SECONDS>` (global) -- global operation timeout (0 = no timeout)
- `--grep <REGEX>` (read) -- filter returned lines to those matching a regex
- `--head N` / `--tail N` (read) -- read first/last N lines
- `--lines START:END` (read) -- partial read with 1-based inclusive range
- `--file <PATH>` (batch) -- read NDJSON manifest from file
- `--output-dir <DIR>` (backup) -- place backups in custom directory
- `--install` (completions) -- install to XDG data directory
- `--lock` (global, v0.1.12+) -- advisory flock(2) on `.<target>.atomwrite.lock` sidecar
- `--max-filesize <BYTES>` (global) -- reject files larger than N bytes (default 1 GiB)
- `--threads <N>` (global) -- number of parallel workers (0 = all cores)
- `--json-schema` (global) -- print JSON Schema for the subcommand output and exit

## Exit Codes

- 0: success
- 1: no matches (search, replace, transform, scope) / zero results
- 4: file not found
- 13: permission denied
- 28: disk full
- 30: quota exceeded
- 65: invalid input
- 73: cross-device rename
- 74: I/O error
- 78: config invalid
- 81: checksum verify failed
- 82: state drift (optimistic locking failed)
- 83: lock timeout (v0.1.12+)
- 85: FIFO detected
- 86: device file detected
- 88: syntax error detected (v0.1.12+)
- 91: EXDEV fallback disabled (v0.1.12+)
- 92: copy-back BLAKE3 failed (v0.1.12+)
- 93: orphan journal detected (v0.1.12+)
- 126: workspace jail violated
- 127: symlink blocked
- 128: file immutable
- 130: SIGINT
- 141: SIGPIPE
- 143: SIGTERM
- 255: internal error

## Environment Variables

- `NO_COLOR` -- disable colored output
- `RUST_LOG` -- control log verbosity
- `ATOMWRITE_LOCALE` -- override display locale (en, pt-BR). Renamed from `ATOMWRITE_LANG` in v0.1.20
- `ATOMWRITE_WORKSPACE` -- set the workspace root for path jail validation
- `RAYON_NUM_THREADS` -- override `--threads` value

## Usage Examples

```bash
# Write atomically with checksum
echo "content" | atomwrite write --workspace . src/file.rs

# Write with mtime preservation
echo "content" | atomwrite write --workspace . --preserve-timestamps src/file.rs

# Read with checksum
atomwrite --workspace . read src/file.rs

# Read raw bytes (no envelope)
atomwrite --workspace . read --mode raw src/file.rs

# Optimistic locking: only write if the file hasn't changed
CS=$(atomwrite --workspace . read src/config.toml | jaq -r .checksum)
echo "new_value" | atomwrite --workspace . write --expect-checksum "$CS" src/config.toml

# Intention guard: refuse without 3 prior backups
atomwrite --workspace . write --require-backup 3 src/config.toml < new.toml

# Intention guard: confirm before mutating
atomwrite --workspace . edit --confirm src/config.toml --old foo --new bar

# Search in parallel
atomwrite --workspace . search 'TODO' src/ --include '*.rs'

# Count largest files
atomwrite --workspace . count --by-size --top 10 src/

# Replace across files
atomwrite --workspace . replace 'old_name' 'new_name' src/

# AST transform via ast-grep
atomwrite --workspace . transform -p 'println!($$$ARGS)' -r 'tracing::info!($$$ARGS)' -l rust src/

# Batch operations (transactional)
cat manifest.ndjson | atomwrite --workspace . batch --transaction

# Math and units
atomwrite calc "2 GiB to bytes"

# Generate regex
atomwrite regex "2024-01-15" "2025-12-31"

# v14 Tier 3: set a value in Cargo.toml
atomwrite --workspace . set Cargo.toml package.version 0.2.0

# v14 Tier 3: read a value
atomwrite --workspace . get Cargo.toml package.version

# v14 Tier 3: rename identifiers across multiple files
atomwrite --workspace . case src/ --subvert user_id UserId --to pascal

# v14 Tier 3: walk the AST of a Rust file
atomwrite --workspace . query src/main.rs --kinds

# v14 Tier 3: extract the outline of a Python file
atomwrite --workspace . outline src/app.py --kind function_definition

# G72 REAL syntax check before committing
atomwrite --workspace . write --syntax-check src/parser.rs < parser.rs

# Backup a file before risky edits
atomwrite --workspace . backup src/config.rs

# Rollback to previous backup
atomwrite --workspace . rollback src/config.rs

# Apply a unified diff from stdin
cat patch.diff | atomwrite --workspace . apply src/file.rs
```

## Primary Documentation

- [README](https://github.com/daniloaguiarbr/atomwrite/blob/main/README.md): Full documentation with all 28 commands and examples
- [HOW_TO_USE](https://github.com/daniloaguiarbr/atomwrite/blob/main/docs/HOW_TO_USE.md): Quickstart guide
- [COOKBOOK](https://github.com/daniloaguiarbr/atomwrite/blob/main/docs/COOKBOOK.md): Recipes for common tasks
- [AGENTS.md](https://github.com/daniloaguiarbr/atomwrite/blob/main/docs/AGENTS.md): Agent discovery file with output contract and token budget
- [TESTING](https://github.com/daniloaguiarbr/atomwrite/blob/main/docs/TESTING.md): 542 tests across 47 test suites
- [MIGRATION](https://github.com/daniloaguiarbr/atomwrite/blob/main/docs/MIGRATION.md): Migration guide between versions
- [INSTALL](https://github.com/daniloaguiarbr/atomwrite/blob/main/docs/INSTALL.md): Platform-specific install instructions
- [CROSS_PLATFORM](https://github.com/daniloaguiarbr/atomwrite/blob/main/docs/CROSS_PLATFORM.md): macOS, Linux, Windows quirks
- [CHANGELOG](https://github.com/daniloaguiarbr/atomwrite/blob/main/CHANGELOG.md): Release history
- [CONTRIBUTING](https://github.com/daniloaguiarbr/atomwrite/blob/main/CONTRIBUTING.md): Development setup and guidelines
- [SECURITY](https://github.com/daniloaguiarbr/atomwrite/blob/main/SECURITY.md): Vulnerability reporting
- [docs/decisions/](https://github.com/daniloaguiarbr/atomwrite/blob/main/docs/decisions/): 19 ADRs (v0.1.12 + v0.1.19 + v0.1.20 architectural decisions)
- [docs/schemas/](https://github.com/daniloaguiarbr/atomwrite/blob/main/docs/schemas/): 22 stable JSON Schema contracts
- [Crates.io](https://crates.io/crates/atomwrite): Package registry
- [Repository](https://github.com/daniloaguiarbr/atomwrite): Source code