# atomwrite Cookbook
[Leia em Português](COOKBOOK.pt-BR.md)
> Practical recipes you can copy-paste into your agent workflows
## What's New in v0.1.36 (2026-07-31)
- Pin agents to `^0.1.36`. **45** subcommands (SSOT via `agent-surface`)
- Agent-native shape: `--select`/`--fields`, `--filter`, `--limit` (**no global `-n`**). Errors always emit
- Staging: `atomwrite --workspace "$WS" scratch init` then `--old-file`/`--new-file` under `.atomwrite/scratch/`
- Thin agent loops without jq: `atomwrite --select ok,error,code --filter 'ok=false' --limit 1 <cmd>`
- GAP-019…033 closed/mitigated (content-source jail + `ok:false` errors + honest fuzzy)
### New Recipes (v0.1.36)
- **How to Stage a Patch with scratch init** -- `--old-file`/`--new-file` inside the jail
- **How to Project NDJSON with --select/--filter/--limit** -- agent-native shape without jq
- **How to Pass a Literal `$VAR` with a Single-Quoted Heredoc** -- shell-safe content
- **How to Discover Host Readiness with doctor + commands** -- agent bootstrap
- **How to Intentional Shrink Rewrite with --allow-shrink** -- override shrink guard
- **How to Use Aggressive Replace Only After --dry-run** -- fail-closed mass edits
## 45 Subcommands (SSOT)
Recipes below cover common agent paths; the complete surface is **45** tools (`CANONICAL_SUBCOMMAND_COUNT` / `atomwrite agent-surface`). Discovery:
```bash
atomwrite agent-surface
atomwrite commands --include-globals
```
- `agent-surface` — clap-derived tool inventory (anti-MCP manifesto)
- `apply` — apply patches from stdin (unified, search/replace, full, markdown)
- `backup` — create timestamped backups with BLAKE3 checksums
- `batch` — NDJSON-driven multi-op batch (optional transaction)
- `calc` — math expressions and unit conversions
- `case` — rename identifiers across files (`--subvert` + `--to` style)
- `codemod` — multi-rule YAML codemod campaign (delegates to transform)
- `commands` — emit full clap command tree as JSON for agent discovery
- `completions` — generate shell completions (bash, zsh, fish, elvish, powershell)
- `copy` — copy files with checksum verification
- `count` — count lines, matches, or files by extension
- `del` — delete a key from a structured config file (TOML/JSON)
- `delete` — delete files with optional backup (`--plan` plan-only)
- `diff` — compare two files (unified, stat, or changes)
- `doctor` — diagnose environment and host readiness
- `edit` — surgically edit by line, marker, or exact match
- `edit-loop` — apply N `old`/`new` pairs from NDJSON stdin in one atomic write
- `extract` — extract fields from NDJSON via pipe (local `--field`; **never** local `--select`)
- `get` — read a value at a dotted path in TOML/JSON
- `hash` — calculate BLAKE3 checksums
- `list` — list files in a directory tree
- `locale` — show / set / clear UI locale preference (XDG)
- `move` — move or rename files atomically (EXDEV copy-fallback)
- `outline` — extract high-level AST structure (functions, classes, …)
- `prune-backups` — prune `.bak.*` backups by age or count
- `query` — walk a tree-sitter AST and emit nodes as NDJSON
- `read` — read files with metadata, checksum, optional content
- `recipe` — named multi-step recipes (`list` / `run`)
- `regex` — generate regex from examples
- `replace` — replace text across files with atomic writes
- `rollback` — restore a file from a previous backup
- `scope` — grammatical scoping (delete comments, case transforms, …)
- `scratch` — staging under `.atomwrite/scratch/`; never OS `/tmp` outside workspace (exit 126)
- `search` — search file contents in parallel (ripgrep engine)
- `semantic-merge` — three-way line-based merge (not AST/embedding)
- `semantic-search` — offline token Jaccard search (no network)
- `set` — write a value at a dotted path in TOML/JSON
- `sparse` — budgeted sparse list/read for agent context windows
- `stat` — metadata alias for `read --stat` (no body)
- `transform` — AST refactoring via ast-grep
- `verify` — verify file integrity by BLAKE3 checksum
- `wal-heal` — reap stale terminal WAL journals (safe, idempotent)
- `wal-stats` — WAL telemetry snapshot (counts, age, reclaim estimate)
- `watch` — watch filesystem events (feature `watch`)
- `write` — create or overwrite files atomically via stdin
## What's 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`); `watch` always emits `watch_summary`; semantic-merge conflict markers default ON; monólitos `include!` 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). `--confirm` is a legacy awareness flag; `--require-large-ack` is independent (no alias collision)
- **Delete:** use `--plan` or `--dry-run` for plan-only; `delete --confirm` and `delete --yes`/`-y` are **rejected** (fail-closed). Run again without those flags to delete
- **Watch:** final NDJSON `type:watch_summary` on 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`
- Historical pin `^0.1.35` (superseded by **`^0.1.36`**). Configuration is CLI + XDG / `.atomwrite.toml` only (no product `ATOMWRITE_*` runtime knobs; no product telemetry)
## What's New in v0.1.34
v0.1.34 (2026-07-19) docs-complete publish of the v0.1.33 one-shot runtime: fuzzy multi-apply is **one-pass** L→R (`apply_fuzzy_one_pass`); default max applies **1**; embeds force single apply; global `--timeout-secs` / `--timeout` **default 120** (`0` disables, deadline exit **124**). **Historical (at v0.1.34):** **41 subcommands**.
### New Recipes (v0.1.34)
- **How to Expand a Section with Fuzzy Replace Safely** -- NEW contains OLD; one-pass, no hang
- Global timeout default 120s — long monorepo jobs use `--timeout-secs 0` or a higher value
### New Recipes (v0.1.29 surface + v0.1.30 residual)
- **How to Fuzzy-Replace Across a Monorepo** -- `replace --fuzzy auto` with optional `--progress-every`
- **How to Run search-replace-verify** -- `recipe run --name search-replace-verify`
- **How to Budget a Sparse List** -- `sparse list --max-files` / `--max-bytes`
- **How to Three-Way Semantic Merge (line-based, not AST)** -- `semantic-merge --base --ours --theirs`
- **How to Choose write Durability** -- `--durability full|fast|auto`
- **How to Install Slim vs Full** -- `--no-default-features --features core` vs default
- **How to Recover from a Missed Match with best_candidate** -- inspect error envelope near-miss and retry
- **How to Inspect rename_method and durability** -- `platform.*` via `jaq` (Linux `renameat2`)
- **How to Read Honest Backup Method** -- `platform.backup_method` is `reflink_or_copy` (never hardlink of live file)
- **How to Inventory the Agent Surface (no MCP)** -- `agent-surface`
- **How to Stat Without Reading Body** -- `stat` (alias of `read --stat`)
- **How to Watch with Debounce** -- `watch` (feature `watch`)
- **How to Semantic-Search Offline (Jaccard)** -- `semantic-search` feature `semantic`, `--index-dir`
- **How to Run a Codemod Campaign** -- `codemod --dry-run`, `by_rule_id`
- **How to Observe Batch Progress** -- batch heartbeats + `--progress-every` on replace
## How to Expand a Section with Fuzzy Replace Safely
- Agent expand-section edits often produce NEW that **contains** OLD (the original block plus more lines)
- Before v0.1.33, re-scanning inserted text could hang for days; one-pass L→R never re-scans inserted text
- When replacement contains pattern (fixed-string), atomwrite forces a **single** apply even if `--max-replacements` is huge
- Default max fuzzy applies is **1** when `--max-replacements` is omitted (hard ceiling **10_000**)
- Global `--timeout-secs` defaults to **120** (exit **124** on deadline); pass `0` only if you intentionally want unlimited runtime
```bash
# NEW embeds OLD — safe, finishes in one pass (no hang)
atomwrite --workspace . replace --fuzzy auto \
$'## Setup\n\nInstall deps.' \
$'## Setup\n\nInstall deps.\n\n### Extra\nAlso run migrations.' \
docs/README.md
# Explicit multi-hit still one-pass on original content
atomwrite --workspace . replace --fuzzy auto --max-replacements 3 \
'TODO' 'DONE' src/
```
## How to Fuzzy-Replace Across a Monorepo
- Use fuzzy replace when indentation or whitespace diverges across packages
- Prefer `--fuzzy auto` (default cascade for agents); `--fuzzy off` = exact-only (G-010 CLOSED), not rejected
- Emit progress NDJSON on large trees with `--progress-every`
- Remember global timeout default is **120s** (exit 124); use `--timeout-secs 0` for unbounded monorepo sweeps
- Fuzzy max applies hard ceiling is **10_000**; default remains **1** when `--max-replacements` is omitted
```bash
# Preview first
atomwrite --workspace . replace --dry-run --fuzzy auto \
'legacy_api(' 'new_api(' packages/ --include '*.rs'
# Apply with progress every 50 files
atomwrite --workspace . replace --fuzzy auto --progress-every 50 \
'legacy_api(' 'new_api(' packages/ --include '*.rs'
```
## How to Run search-replace-verify
- Built-in recipe searches, replaces, then hashes for integrity
- Requires `--pattern` and `--replacement`
```bash
atomwrite --workspace . recipe list
atomwrite --workspace . recipe run --name search-replace-verify \
--path src --pattern OLD_TOKEN --replacement NEW_TOKEN --fuzzy auto
```
## How to Budget a Sparse List
- Cap file count and path-byte budget for agent context windows
```bash
atomwrite --workspace . sparse list --max-files 100 --max-bytes 1048576 src/
atomwrite --workspace . sparse list --max-files 20 --include '*.rs' .
# Feed paths into sparse read
atomwrite --workspace . sparse list --max-files 20 src/ \
| atomwrite extract path > /tmp/paths.txt
atomwrite --workspace . sparse read --paths-file /tmp/paths.txt --head 30 --max-files 20
```
## How to Three-Way Semantic Merge (line-based, not AST)
- Merge concurrent agent edits with a common base
- Add `--fail-on-conflict` to exit 65 on unresolved conflicts
```bash
atomwrite --workspace . semantic-merge \
--base /tmp/base.rs \
--ours /tmp/ours.rs \
--theirs /tmp/theirs.rs \
--output src/lib.rs
```
## How to Choose write Durability
- `full` for configs and durable state
- `fast` for ephemeral artifacts
- `auto` for default policy resolution
```bash
atomwrite --workspace . scratch init
echo scratch | atomwrite --workspace . write --durability fast .atomwrite/scratch/ephemeral-out.txt
# Or a workspace-relative staging dir:
mkdir -p ./tmp-agent
## How to Stage a Patch with scratch init
- Content-source paths for `--old-file`/`--new-file` are jail-checked (GAP-019)
- Never stage under OS `/tmp` — exit **126** `WORKSPACE_JAIL`
- Canonical staging: `{workspace}/.atomwrite/scratch/` via `scratch init`
```bash
atomwrite --workspace . scratch init
SCR=.atomwrite/scratch
printf %s "$OLD" >"$SCR/old.txt"
printf %s "$NEW" >"$SCR/new.txt"
atomwrite --workspace . edit --old-file "$SCR/old.txt" --new-file "$SCR/new.txt" src/f.rs
```
## How to Project NDJSON with --select/--filter/--limit
- Global shape flags apply to every subcommand (and error path)
- `--select`/`--fields` project top-level keys; `--filter` is top-level equality (repeatable AND)
- `--limit N` is **long-only** (no global `-n`); errors always emit even under filter
```bash
# Compact agent parse without jq
atomwrite --workspace . --select ok,error,code,match_count,suggestion \
edit --old missing --new x --fuzzy off src/f.rs
# First failure only
atomwrite --workspace . --filter ok=false --limit 1 \
edit --old a --new b src/f.rs
# Project success fields on search hits
atomwrite --workspace . --select path,line_number,text --limit 20 \
search 'TODO' src/
```
## How to Pass a Literal `$VAR` with a Single-Quoted Heredoc
- Double quotes and unquoted heredocs expand shell variables
- Single-quoted heredoc delimiter preserves `$VAR`, backticks, and `//` literally
- Prefer `--old-file`/`--new-file` for multi-line patches (still stage under workspace)
```bash
# Literal $VAR and shell metacharacters — no expansion
atomwrite --workspace . edit src/config.sh --old 'export PATH=$PATH' --new 'export PATH=$HOME/bin:$PATH'
# Multi-line literal body via single-quoted heredoc → staged file
atomwrite --workspace . scratch init
SCR=.atomwrite/scratch
cat >"$SCR/old.txt" <<'EOF'
# install
EOF
cat >"$SCR/new.txt" <<'EOF'
# install
# pinned
EOF
atomwrite --workspace . edit docs/install.md \
--old-file "$SCR/old.txt" --new-file "$SCR/new.txt"
```
## How to Discover Host Readiness with doctor + commands
- `doctor` reports host/workspace/feature readiness as one NDJSON object
- `commands` emits the full clap tree for agent discovery
- Pair with `agent-surface` for the tool manifesto (anti-MCP)
```bash
atomwrite --workspace . doctor --strict
atomwrite --workspace . commands --include-globals
atomwrite --workspace . agent-surface --format json
```
## How to Intentional Shrink Rewrite with --allow-shrink
- Shrink guard blocks writes that shrink >50% when `--expect-checksum` is set
- Pass `--allow-shrink` only when truncation is intentional
```bash
echo "tiny" | atomwrite --workspace . write --expect-checksum "$CS" target.rs
# Intentional shrink rewrite:
## How to Use Aggressive Replace Only After --dry-run
- `--fuzzy aggressive` is powerful and can over-match; never mass-apply blind
- Always preview with `--dry-run` first; inspect NDJSON, then apply
```bash
# Preview only
atomwrite --workspace . replace --dry-run --fuzzy aggressive \
'legacy_api(' 'new_api(' packages/ --include '*.rs'
# Apply only after dry-run envelope looks correct
atomwrite --workspace . replace --fuzzy aggressive --progress-every 50 \
'legacy_api(' 'new_api(' packages/ --include '*.rs'
```
## How to Install Slim vs Full
- Slim (~7.7 MiB, local size-gate ≤15 MiB): core-only, no AST stack
- Default/full (~52 MB): AST languages included
```bash
cargo install --path . --locked --force --no-default-features --features core
cargo install --path . --locked --force
```
## How to Recover from a Missed Match with best_candidate
- Match failures may emit `best_candidate` (line, similarity, strategy, text, diff_preview)
- Use the near-miss to correct `old` without re-reading the whole file
- `--fuzzy off` = exact-only (G-010); default remains `auto`. Prefer auto/aggressive for near-miss recovery via MATCH_AMBIGUOUS / best_candidate
```bash
# Capture the error envelope on a near-miss (exit 65 / MatchFailed)
atomwrite --workspace . scratch init
atomwrite --workspace . replace --fuzzy auto \
'fn main(){let x=1;}' 'fn main(){let x=2;}' src/main.rs \
| tee .atomwrite/scratch/miss.json \
| jaq '{exit: .exit, best: .best_candidate, ok: .ok, match_count: .match_count}'
# Retry with the candidate text (or a hand-fixed snippet from diff_preview)
atomwrite --workspace . replace --fuzzy auto \
"$(jaq -r '.best_candidate.text' < .atomwrite/scratch/miss.json)" \
'fn main() { let x = 2; }' src/main.rs
```
## How to Inspect rename_method and durability
- Write NDJSON reports `platform.durability`, `platform.rename_method`, `platform.backup_method`
- Linux prefers `renameat2` when available, else `rename`
- Parse platform fields with `jaq` after every durable write
```bash
# Fast path for ephemeral output (workspace-relative; jail retained)
atomwrite --workspace . scratch init
```
## How to Read Honest Backup Method
- Backup of the live file is NEVER a hardlink of the same inode
- Inspect `platform.backup_method` (`reflink_or_copy` or `copy`)
- Prefer this honesty for agent pipelines that would otherwise corrupt .bak via shared inode
```bash
# Force a backuped overwrite and read the method used
# Compare with a plain write (default transactional backup auto-removes on success)
```
## How to Inventory the Agent Surface (no MCP)
- `agent-surface` emits a clap-derived tool manifesto
- MCP is forbidden; wire Bash/argv from this inventory
- Use for host tool registration without hand-maintained flag lists
```bash
atomwrite --workspace . agent-surface
## How to Stat Without Reading Body
- `stat` is a thin alias of `read --stat`
- Returns metadata and checksum without body bytes
- Prefer for agent loops that only need size, mode, or BLAKE3
```bash
atomwrite --workspace . stat src/lib.rs
atomwrite --workspace . read --stat src/lib.rs
```
## How to Watch with Debounce
- Requires build with `--features watch` (or full default with watch enabled)
- Coalesces per-path events with `--debounce-ms`
- Cap lifetime with `--max-events`; optional `--checksum` for BLAKE3 on regular files
```bash
# Build with watch, then stream debounced events
cargo install --path . --locked --force --features watch
atomwrite --workspace . watch . --debounce-ms 200 --max-events 20
atomwrite --workspace . watch src/ --debounce-ms 100 --checksum --max-events 5
```
## How to Semantic-Search Offline (Jaccard)
- Offline token ranking (Jaccard); not OpenRouter embeddings
- Requires feature `semantic`
- Pass `--index-dir` for inverted-index backend under a local directory
```bash
atomwrite --workspace . semantic-search "atomic rename tempfile" src/ --k 10
atomwrite --workspace . semantic-search "fsync durability" . --k 20 --min-score 0.1 \
--index-dir .atomwrite/semantic-index
```
## How to Run a Codemod Campaign
- Multi-rule AST campaign over a YAML rules file
- Always dry-run first; summary includes `codemod_summary.by_rule_id`
- Apply only after the dry-run envelope looks correct
```bash
atomwrite --workspace . codemod --rules rules.yaml --dry-run src/
atomwrite --workspace . codemod --rules rules.yaml --dry-run src/ \
| jaq 'select(.type=="summary" or .codemod_summary != null) | .codemod_summary.by_rule_id // .'
# Apply
atomwrite --workspace . codemod --rules rules.yaml src/
```
## How to Observe Batch Progress
- `replace --progress-every N` emits NDJSON heartbeats (`type: progress`)
- Long batch/replace also checks cooperative cancel between units (SIGINT/SIGTERM → cancelled / exit 143)
- Batch emits automatic heartbeats about every max(1, total/20) ops (capped at 50)
- Suppress progress with global `-q` / `-qq` when only the final summary matters
```bash
# Bulk replace with progress every 50 files (default)
atomwrite --workspace . replace --progress-every 50 'old_api' 'new_api' src/ \
| jaq -c 'select(.type=="progress" or .type=="summary")'
# Batch transaction with built-in heartbeats
printf '%s\n' \
'{"op":"write","target":"a.txt","content":"one"}' \
'{"op":"write","target":"b.txt","content":"two"}' \
| atomwrite --workspace . batch --transaction \
| jaq -c 'select(.type=="progress" or .type=="summary")'
```
## What's New in v0.1.12
This section summarizes recipe-relevant changes in v0.1.12. The v0.1.12 release adds new recipes to the cookbook for the 6 new subcommands, the new flags, and the crash recovery workflow.
### New Recipes (Added in v0.1.12)
- **How to Edit a Config File Without Rewriting It** -- use `set`/`get`/`del` instead of `read`+`edit` for dotted paths in TOML/JSON
- **How to Rename an Identifier Across a Module** -- use `case --subvert OLD NEW --to <style>` for 5 case styles
- **How to Walk a Code AST** -- use `query --kinds` to list kinds, `query --query KIND` to filter
- **How to Extract a Code Map** -- use `outline --positions` for functions/structs/enums/traits
- **How to Detect Syntax Errors Before Commit** -- use `write --syntax-check` for tree-sitter validation
- **How to Recover from a Crashed Write** -- use `recover_orphan_journals` to inspect WAL sidecars
- **How to Compose read with sed/awk** -- use `read --format raw` for Unix composability
- **How to Limit Search to Small Files** -- use `search --max-filesize` and `--max-columns`
- **How to Replace Literal Strings** -- use `replace --literal` to disable regex
- **How to Apply Multi-Rule Refactor** -- use `transform --rules <file.yaml>` for cascading rules
- **How to Acquire Advisory File Lock** -- use `write --lock` and `--lock-timeout` for multi-agent safety
- **How to Backup with CoW** -- use `backup` and `copy` with default reflink on APFS/btrfs/XFS
### Updated Recipes (v0.1.12 changes reflected in existing recipes)
- **How to Write a File Atomically** -- now mentions `--syntax-check`, `--lock`, `--include-fifo`
- **How to Edit a File** -- now mentions `--after-line`, `--before-line`, `--range`, `--delete-range`, `--between`
- **How to Read a File** -- now mentions `--format raw`, `--head N`, `--tail N`, `--line N`, `--grep`
- **How to Search** -- now mentions `--max-filesize`, `--max-columns`
- **How to Replace Text in Bulk** -- now mentions `--literal`, `--fuzzy`
- **How to Transform Code** -- now mentions `--rules`, `--inline-rules`
- **How to Batch Operations** -- now mentions `--batch-size`, `--file`
- **How to Backup** -- now mentions `--no-reflink`, `--output-dir`
### New Subcommands Available
- `set` -- write a value at a dotted path in TOML/JSON
- `get` -- read a value at a dotted path
- `del` -- remove a key at a dotted path
- `case` -- rename identifiers in 5 case styles via `heck`
- `query` -- walk a tree-sitter AST
- `outline` -- extract top-level structure
### New Flags for Existing Commands
- `read --format raw` (G81)
- `write --syntax-check` (G72)
- `write --lock` and `--lock-timeout` (G54)
- `search --max-filesize`, `--max-columns` (G68)
- `replace --literal` (G66)
- `transform --rules`, `--inline-rules` (G44)
- `batch --batch-size` (G77)
- `backup/copy --no-reflink` (G64)
### Test Coverage
- 661+ tests passing (v0.1.28)
- 9 ADRs in `docs/decisions/` (0019-0027)
- 7 new JSON schemas in `docs/schemas/`
- See [docs/decisions/README.md](decisions/README.md) for architectural decisions
## Latency Note
- All operations execute locally with sub-millisecond overhead
- The atomic write sequence adds ~1ms for the fsync-rename-fsync cycle
- Search parallelism scales with available CPU cores
- Batch mode amortizes startup cost across N operations
## Default Values Reference
- `--threads` defaults to number of available CPU cores
- `--max-filesize` defaults to 1 GiB (1,073,741,824 bytes)
- `--color` defaults to `auto` (detect terminal)
- `--workspace` defaults to current working directory
- `diff --context` defaults to 3 lines
- `diff --algorithm` defaults to `patience`
- Global `--timeout-secs` / `--timeout` → **120** (`0` disables; deadline exit **124**)
- Fuzzy max applies when `--max-replacements` omitted → **1** (hard ceiling **10_000**); multi-apply is one-pass on original content; embeds force 1
## How to Write a File Atomically
- Pipe content from stdin to create or overwrite a file
- The write survives power failure and process crashes
```bash
echo "fn main() { println!(\"hello\"); }" | atomwrite write src/main.rs
```
- Create a backup before overwriting:
```bash
cat updated_config.toml | atomwrite write --backup config.toml
```
- Write with workspace restriction:
```bash
## How to Normalize Line Endings
- Force LF line endings when writing:
```bash
echo "line1\r\nline2\r\n" | atomwrite write --line-ending lf src/file.txt
```
- Force CRLF for Windows compatibility:
```bash
cat unix_file.txt | atomwrite write --line-ending crlf src/windows_file.txt
```
- Preserve original line endings (default):
```bash
- Normalize line endings during edit:
```bash
atomwrite edit --line-ending lf src/mixed.rs --old "old_text" --new "new_text"
```
## How to Search Across a Project
- Search for a pattern across all files in a directory:
```bash
atomwrite search 'TODO' src/
```
- Search with regex and context lines:
```bash
atomwrite search --regex 'fn\s+test_\w+' --context 2 src/
```
- Get just file paths with matches:
```bash
atomwrite search --files 'deprecated' src/
```
- Get match counts per file:
```bash
atomwrite search --count 'unwrap()' src/
```
- Combine with extract to get specific fields:
```bash
## How to Replace Text in Bulk
- Replace a string across all files in a directory:
```bash
atomwrite replace 'old_function' 'new_function' src/
```
- Preview replacements without modifying files:
```bash
atomwrite replace --dry-run 'before' 'after' src/
```
- Replace with regex:
```bash
atomwrite replace --regex 'v\d+\.\d+\.\d+' 'v2.0.0' src/
```
- Replace with workspace restriction:
```bash
atomwrite replace --workspace /home/user/project 'old' 'new' src/
```
## How to Scope Code by Grammar Category
- Delete all comments from a Rust file:
```bash
atomwrite scope --query comments --delete src/main.rs
```
- Uppercase all function names in Python:
```bash
atomwrite scope --query def --action upper src/app.py
```
- Squeeze whitespace in strings:
```bash
atomwrite scope --query strings --action squeeze src/lib.rs
```
- Replace comments with a standard header:
```bash
atomwrite scope --query comments --replace-with "// TODO: review" src/main.rs
```
- Use custom AST pattern for titlecase:
```bash
atomwrite scope --pattern 'fn $NAME($$$ARGS)' --action titlecase -l rust src/
```
## How to Create and Restore Backups
- Create a timestamped backup with BLAKE3 checksum:
```bash
atomwrite backup src/main.rs src/lib.rs
```
- Preview backup creation without writing:
```bash
atomwrite backup --dry-run src/main.rs
```
- Set backup retention to 30 days:
```bash
atomwrite backup --retention 30 src/config.toml
```
- Restore the most recent backup:
```bash
atomwrite rollback src/main.rs --latest
```
- Restore a specific timestamped backup:
```bash
atomwrite rollback src/main.rs --timestamp 2026-05-29T12-00-00
```
- Verify checksum before restoring:
```bash
atomwrite rollback --verify src/main.rs --latest
```
- Preview restore without applying:
```bash
atomwrite rollback --dry-run src/main.rs --latest
```
## How to Apply Patches From Stdin
- Apply a unified diff patch:
```bash
- Apply a markdown-fenced patch:
```bash
- Apply SEARCH/REPLACE blocks from an agent:
```bash
cat agent_output.txt | atomwrite apply --format search-replace src/main.rs
```
- Apply with automatic backup before patching:
```bash
- Preview patch application without modifying:
```bash
- Apply a full file replacement:
```bash
cat new_version.rs | atomwrite apply --format full src/main.rs
```
## How to Refactor With AST Patterns
- Rename a function across a Rust codebase:
```bash
atomwrite transform --pattern 'old_fn($$$ARGS)' --rewrite 'new_fn($$$ARGS)' -l rust src/
```
- Migrate from println to tracing:
```bash
atomwrite transform --pattern 'println!($$$ARGS)' --rewrite 'tracing::info!($$$ARGS)' -l rust src/
```
- Replace all unwrap calls with the `?` operator:
```bash
atomwrite transform --pattern '$EXPR.unwrap()' --rewrite '$EXPR?' -l rust src/
```
- Migrate JavaScript console.log:
```bash
atomwrite transform --pattern 'console.log($$$ARGS)' --rewrite 'logger.info($$$ARGS)' -l js src/
```
- Preview AST transform without applying:
```bash
atomwrite transform --dry-run --pattern 'old_api($$$ARGS)' --rewrite 'new_api($$$ARGS)' -l python src/
```
## How to Generate Regex From Examples
- Generate a date pattern regex:
```bash
atomwrite regex "2024-01-15" "2025-12-31" "2026-06-01"
```
- Generate with digit and word generalization:
```bash
atomwrite regex --digits --words "user_123" "admin_456" "guest_789"
```
- Use the generated regex in a search:
```bash
```
## How to Calculate Unit Conversions
- Convert time units:
```bash
atomwrite calc "2 hours + 30 minutes to seconds"
```
- Convert data sizes:
```bash
atomwrite calc "10 GiB to MB"
```
- Evaluate math expressions:
```bash
atomwrite calc "sqrt(144) + 3^2"
```
- Calculate percentages:
```bash
atomwrite calc "15% of 200"
```
## How to Batch Multiple Operations
- Batch supports 7 operations: write, replace, delete, edit, hash, move, copy
- Create an NDJSON manifest with multiple operations:
```bash
cat <<'EOF' > manifest.ndjson
{"op":"write","path":"src/a.txt","content":"hello"}
{"op":"write","path":"src/b.txt","content":"world"}
{"op":"delete","path":"src/old.txt"}
{"op":"edit","path":"src/a.txt","old":"hello","new":"hello world"}
{"op":"hash","path":"src/b.txt"}
{"op":"move","source":"src/a.txt","target":"src/renamed.txt"}
{"op":"copy","source":"src/b.txt","target":"src/b_copy.txt"}
EOF
- Preview the batch without executing:
```bash
- Execute as all-or-nothing transaction with automatic rollback on failure:
```bash
- Generate a manifest from search results:
```bash
while read -r p; do echo "{\"op\":\"delete\",\"path\":\"$p\"}"; done | \
atomwrite batch --dry-run
```
## How to Verify File Integrity
- Hash a file and store the checksum:
```bash
atomwrite hash src/main.rs
```
- Verify a file against a known checksum:
```bash
atomwrite hash --verify abc123def456 src/main.rs
```
- Hash from stdin:
```bash
- Compare two files for differences:
```bash
atomwrite diff --stat src/old.rs src/new.rs
```
## How to Use Optimistic Locking
- Since v0.1.15 the target is resolved against `--workspace` before verification (G118); on v0.1.14 and earlier, run with CWD = workspace or a relative target silently skips the checksum check
- Read a file and capture the checksum:
```bash
- Write with the expected checksum:
```bash
- Handle state drift (exit code 82):
```bash
echo "File changed by another process, re-reading..."
CHECKSUM=$(atomwrite read --stat src/config.toml | atomwrite extract checksum)
echo "updated content" | atomwrite write --expect-checksum "$CHECKSUM" src/config.toml
fi
```
## How to Edit and Trigger a Build Without Manual Touch
- Edit a source file in a Rust project and trigger cargo without manually running `touch`:
```bash
atomwrite edit src/main.rs --old "old_text" --new "new_text"
cargo build
```
- This works because `edit` updates the mtime by default, so cargo sees the source as newer than its dep-info file and recompiles.
- If you opt out of mtime updates with `--preserve-timestamps`, cargo may skip the rebuild silently (the famous `Finished in 0.29s` no-op):
```bash
atomwrite edit --preserve-timestamps src/main.rs --old "old_text" --new "new_text"
cargo build # may be a silent no-op, forcing you to touch the file manually
```
- Check whether mtime was preserved by reading the `mtime_preserved` field in the NDJSON response:
```bash
- Use `--preserve-timestamps` only for backup, snapshot, or reproducible-build scenarios. For interactive development, leave the default in place so build systems detect your changes.
## How to Create Backups With Retention
- Write a file with automatic backup:
```bash
- Delete a file with backup:
```bash
atomwrite delete --backup src/old_module.rs
```
- Set retention period for backups:
```bash
atomwrite delete --backup --retention 30 src/old_module.rs
```
- List backup files in a directory:
```bash
atomwrite list --long .atomwrite-backups/
```
## How to Extract Fields From NDJSON Pipeline
- Use extract to pull specific fields from atomwrite output
- Use field names for JSON keys or positional indices for text columns
```bash
- Extract just paths from search results:
```bash
- Extract checksums from write results:
```bash
- Extract text columns by index:
```bash
## How to List Project Structure
- List files with NDJSON output:
```bash
atomwrite list src/
```
- Long format with size, permissions and modification time:
```bash
atomwrite list --long src/
```
- Count files grouped by extension:
```bash
atomwrite list --count-by-ext src/
```
- Combine with extract for custom views:
```bash
## Scope Operations
### Delete All Comments from Rust Files
```bash
atomwrite --workspace . scope src/ --lang rust --query comments --delete
```
### Uppercase All Function Names (Preview)
```bash
atomwrite --workspace . scope src/ --lang rust --query fn --action upper --dry-run
```
### Remove Comments from Python Scripts
```bash
atomwrite --workspace . scope scripts/ --lang python --query comments --delete
```
## Backup and Rollback
### Create Backup Before Risky Edit
```bash
atomwrite --workspace . backup src/config.rs
### Restore from Latest Backup
```bash
atomwrite --workspace . rollback src/config.rs
```
### Restore from Specific Timestamp with Verification
```bash
atomwrite --workspace . rollback src/config.rs --timestamp 20260530_120000 --verify
```
## Apply Patches
### Apply Full File Replacement
```bash
### Apply Unified Diff from Git
```bash
### Apply SEARCH/REPLACE Blocks
```bash
old_function_name
==== REPLACE
new_function_name
>>>> END
EOF
```
## Agent-First Patterns (v0.1.2+)
### Bound a Long Search with Timeout
```bash
# Global default is 120s (exit 124 on deadline); 0 disables
# Override to 60s if search doesn't finish; emits NDJSON error with error_class=transient
atomwrite --workspace . --timeout-secs 60 search 'TODO' src/
```
### Read Only Lines Matching a Regex
```bash
# Useful for extracting logs from huge files without exhausting context
### Read First N Lines of a Huge File
```bash
# Avoids loading the entire file into context
atomwrite --workspace . read --head 20 huge.log
```
### Batch from File Instead of stdin
```bash
# Persisted manifest file (NDJSON, one op per line)
atomwrite --workspace . batch --file ops.ndjson
```
### Backup to a Centralized Directory
```bash
# Keep source directory clean; centralize backups
atomwrite --workspace . backup --output-dir /var/backups/atomwrite src/critical.rs
```
### Install Shell Completions on First Use
```bash
# Auto-installs to ~/.local/share/bash-completion/completions/atomwrite
atomwrite completions bash --install
```
### Always pass `--workspace` for agents
```bash
# Agents must pass --workspace explicitly (no product environment variables)
atomwrite --workspace /home/user/project read src/main.rs
```
## Agent-First Patterns (v0.1.3+)
### Edit and Trigger Cargo Build Without Manual Touch
```bash
# New default: edit updates the mtime, so cargo rebuilds automatically
atomwrite edit src/main.rs --old "old_text" --new "new_text"
cargo build # rebuilds without needing `touch` first
```
### Read mtime_preserved From Edit Response
```bash
# Parse the NDJSON response to verify whether the timestamp was kept
### Preserve Original mtime for Backup or Snapshot Workflows
```bash
# Opt back into the v0.1.2 behavior of preserving the original file mtime
atomwrite edit --preserve-timestamps src/snapshot.rs --old "old" --new "new"
atomwrite replace --preserve-timestamps 'old_api' 'new_api' src/
```
## How to Interpret Error Suggestions (v0.1.4)
- Every error envelope includes a `suggestion` field with actionable recovery guidance
- The `WorkspaceJail` suggestion adapts based on whether `--workspace` was provided
- Use the suggestion to drive agent retry logic instead of parsing the message text
```bash
# When workspace is NOT provided, the suggestion prompts for the flag
atomwrite read /etc/passwd 2>/dev/null
# Output: {"error":true,"code":"WORKSPACE_JAIL","exit":126,...,"suggestion":"set --workspace <root> (XDG/config only; no environment variables)",...}
# When workspace IS provided, the suggestion says "use a path inside"
atomwrite --workspace /home/user/project read /etc/passwd 2>/dev/null
# Output: {"error":true,"code":"WORKSPACE_JAIL","exit":126,...,"suggestion":"use a path inside the workspace (/home/user/project)",...}
```
## How to Install on Windows 10/11 (v0.1.4)
- v0.1.4 finally fixes `cargo install atomwrite` on Windows
- Install Visual Studio 2019+ Build Tools with the C++ workload
- Install Rust 1.88+ via rustup
- Run `cargo install atomwrite --locked`
- See [INSTALL.md](INSTALL.md) for the full Windows troubleshooting guide
```powershell
# PowerShell 7+ or Windows Terminal
rustup default stable
rustup target add x86_64-pc-windows-msvc
cargo install atomwrite --locked
atomwrite --version # NDJSON output
```
## How to Discover the JSON Schema at Runtime
- Use `--json-schema` to emit the JSON Schema for any subcommand's output
- No need to read a static schema file; the schema is part of the binary
```bash
# Get the schema for the read subcommand output
atomwrite --json-schema read
atomwrite --json-schema write
atomwrite --json-schema edit
atomwrite --json-schema search
atomwrite --json-schema replace
atomwrite --json-schema batch
atomwrite --json-schema error # shared by all subcommands
```
- Pipe the schema to `jaq` to validate live output:
```bash
# 1. Capture the schema
atomwrite --json-schema error > /tmp/error.schema.json
# 2. Run the actual command and validate each NDJSON line
atomwrite --workspace . read /missing 2>/dev/null \
| while IFS= read -r line; do
echo "$line" | jaq -r --validate --slurpfile s /tmp/error.schema.json '.' && echo "OK" || echo "FAIL"
done
```
## How to Read NDJSON in a Shell Pipeline with jaq
- All atomwrite output is one JSON object per line
- Pair with `jaq` for structured filtering, mapping, and aggregation
```bash
# Extract just the checksum from a read response
# Count search matches per file
# Sum bytes_written across a batch
# Filter error envelopes by error class
## How to Handle Persistent Errors with Retry Logic
- Combine `retryable: true` from error envelopes with `set -e` and a retry loop in shell
```bash
#!/usr/bin/env bash
# retry-on-transient.sh
attempt=1
max_attempts=5
delay=1
while [ $attempt -le $max_attempts ]; do
output=$(atomwrite --workspace . "$@" 2>/dev/null)
exit_code=$?
if [ $exit_code -eq 0 ]; then
echo "$output"
exit 0
fi
# Parse retryable flag from the error envelope
if [ "$retryable" = "true" ]; then
echo "Attempt $attempt: transient error, retrying in ${delay}s..." >&2
sleep $delay
delay=$((delay * 2))
attempt=$((attempt + 1))
else
echo "$output" >&2
exit $exit_code
fi
done
echo "Failed after $max_attempts attempts" >&2
exit 1
```
## How to Batch Multiple Edit Pairs Safely (v0.1.15, G117)
## WAL Journal Management (v0.1.15+)
The G119 effort introduces three new layers for managing sidecar WAL journals. These recipes show practical usage of the new subcommands and flags.
### How to Reap Stale Journals Before Commit
The `wal-heal` subcommand (G119 L3) safely removes terminal journals (Committed and Aborted). Wire it into a pre-commit hook to keep the working tree clean.
```bash
# .git/hooks/pre-commit
atomwrite --workspace . wal-heal --threshold-secs 0 \
| jaq -e '.removed_count >= 0' \
The `--threshold-secs 0` flag removes terminal journals of any age. `wal-heal` NEVER touches `Started` entries. For a more conservative sweep, raise the threshold to retain recent journals for forensic analysis.
### How to Configure WAL Policy per Workload
The `--wal-policy` flag (G119 L1) on `write` and `edit` controls when the sidecar journal is written. Three values are accepted:
- `auto` (default) -- the policy chosen by the build, optimized for general use
- `always` -- always write the sidecar journal (forensic-grade audit trail)
- `never` -- never write the sidecar journal (fastest path, no recovery metadata)
```bash
# ephemeral builds: skip journal overhead, sidecars have no consumer there
atomwrite --workspace . write --wal-policy never ephemeral-config.toml < config.toml
# Production deploys: audit trail matters, force the sidecar
atomwrite --workspace . write --wal-policy always /etc/myapp/config.toml < prod.toml
# General agent use: let the default decide
atomwrite --workspace . write src/lib.rs < new_lib.rs
```
### How to Inspect Journal Health
The `wal-stats` subcommand (G119 L5) emits read-only local diagnostics about the current WAL state. Pair it with `jaq` to gate local or post-build scripts.
```bash
# Full local WAL diagnostics as NDJSON
atomwrite --workspace . wal-stats
# Gate on zero reclaimable journals
# Extract just the stale threshold for diagnostics
- Repeated `--old`/`--new` pairs run the full 9-strategy fuzzy cascade per pair
- The default is all-or-nothing: a failed pair aborts the batch with exit 65, no write, and `failed_pair_index` in the error envelope
- `--partial` applies the matching pairs and reports the rest with `matched: false`
```bash
# All-or-nothing with per-pair ground truth
atomwrite --workspace . edit src/main.rs --old "foo" --new "bar" --old "baz" --new "qux" \
| jaq -e '.pair_results'
# Partial application: keep the valid work, list the misses
atomwrite --workspace . edit --partial src/main.rs --old "foo" --new "bar" --old "maybe" --new "x" \
| jaq -e '{edits, pairs_total, missing: [.pair_results[] | select(.matched | not) | .index]}'
# Anti-masking: a bare pipe hides exit 65 as {"edits": null} with pipeline exit 0
```
## v0.1.20 — What Is New
This release introduces a new safety layer called **intention guards** and renames the global `--lang` flag to `--locale` to disambiguate from the tree-sitter `--lang` selector used by `scope` and `transform`.
### Intention Guards (5 OPT-IN flags)
- `--require-backup <N>` — refuse the operation when fewer than `N` retained backups exist for the target
- `--confirm` — emit a confirmation prompt listing the planned mutation in NDJSON before executing
- `--auto-rotate <N>` — automatically rotate the backup ring down to `N` entries after a successful write
- `--risk-threshold <LOW|MEDIUM|HIGH>` — block operations whose classified risk meets or exceeds the threshold
- `--locale <en|pt-BR>` — renamed from `--lang` to disambiguate from the tree-sitter `--lang`
### 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`
### Statistics
- 542 tests passing in 47 integration suites, 0 failures
- 11 GAP-2026 closed
- 3 Windows cross-compile targets green
- 19 ADRs in `docs/decisions/` (0019-0037)
### Migration `--lang` to `--locale`
```bash
# Discover all files using --lang
rg -l -- '--lang\b' .
# Bulk replace while preserving other matches
fd -e sh -e md -e toml -e yml -e yaml -e json -x sd -- '--lang\b' '--locale' {}
# Or via ruplacer
ruplacer --subvert --lang --locale
```
## v0.1.21 — Recipes
### Backup Deleted After Success
- **Default change** — `write --backup` no longer leaves a `.bak.<timestamp>` sibling on disk after success. The backup is deleted.
- **Opt-in to preserve** — pass `--keep-backup` to any of `write`, `edit`, `replace`, `rollback`, `apply`, `batch`:
```bash
# Default v0.1.21: backup is created, write succeeds, backup is deleted
# Opt-in v0.1.21: backup is created, write succeeds, backup is preserved
### Sequential Edit Pattern with Checksum Re-capture
- **Pattern A — explicit re-capture** is the canonical way to chain N `edit` calls on the same file:
```bash
#!/usr/bin/env bash
set -euo pipefail
for pair in "foo:bar" "baz:qux" "alpha:beta"; do
OLD="${pair%:*}"
NEW="${pair#*:}"
done
```
- **Pattern B — `--allow-sequential-drift` opt-in** for one-shot scripts that prefer not to re-capture:
```bash
OLD="${pair%:*}"; NEW="${pair#*:}"
echo "$NEW" | atomwrite --workspace . edit --allow-sequential-drift --old "$OLD" --new "$NEW" --expect-checksum "$CS" src/foo.rs
done
```
## v0.1.22 — Recipes
### `edit-loop` for N Pairs in 1 Invocation
- **Use case**: apply a batch of textual transformations to one file (rename an identifier in 7 places, sweep obsolete type aliases) where today you invoke `edit` N times in a shell loop.
```bash
# Apply 3 pairs in 1 invocation
printf '%s\n' \
'{"old":"v0_1_20","new":"v0_1_22"}' \
'{"old":"foo","new":"bar"}' \
'{"old":"baz","new":"qux"}' \
| atomwrite --workspace . edit-loop src/version.rs
# With backup preserved for forensic timeline
printf '%s\n' '{"old":"foo","new":"bar"}' \
| atomwrite --workspace . edit-loop --backup --keep-backup src/foo.rs
# With --partial (best-effort: apply matched, report unmatched)
printf '%s\n' '{"old":"exists","new":"X"}' '{"old":"absent","new":"Y"}' \
| atomwrite --workspace . edit-loop --partial src/foo.rs
```
### `prune-backups` for Manual Cleanup of Legacy `.bak.*`
- **Use case**: operators who upgraded from v0.1.20 inherit `.bak.<timestamp>` siblings that v0.1.21 no longer creates (and therefore no longer cleans up automatically).
```bash
# Default --dry-run true: list what WOULD be removed
atomwrite --workspace . prune-backups --max-age-secs 86400 .
# Remove backups older than 24 hours
atomwrite --workspace . prune-backups --max-age-secs 86400 --dry-run false .
# Keep only the 3 most recent backups per directory
atomwrite --workspace . prune-backups --max-count 3 --dry-run false .
# Local gate: assert zero backup orphans after cleanup
atomwrite --workspace . prune-backups --max-age-secs 0 --dry-run false . \
## v0.1.25 — Recipes
### Verify File Checksum
```bash
# Dedicated verify subcommand (delegates to hash --verify)
atomwrite --workspace . verify src/main.rs --checksum abc123def456
# Exit 0 on match, exit 81 on mismatch
```
### Delete Old Files by Age
```bash
# Delete files older than 7 days (duration suffixes: s/m/h/d/w)
atomwrite --workspace . delete --older-than 7d --dry-run logs/
atomwrite --workspace . delete --older-than 7d logs/
# Preview deletion plan with --plan
atomwrite --workspace . delete --plan --older-than 1h tmp/
```
### Replace with Case Preservation
```bash
# Automatically adapt replacement case: Hello→Goodbye, HELLO→GOODBYE, hello→goodbye
atomwrite --workspace . replace --preserve-case 'hello' 'goodbye' src/
```
### Configure Fuzzy Threshold
```bash
# Strict: only accept very close matches (0.99)
atomwrite --workspace . edit src/main.rs --old "approximate_text" --new "new_text" --fuzzy-threshold 0.99
# Loose: accept distant matches (0.5)
atomwrite --workspace . edit src/main.rs --old "approximate_text" --new "new_text" --fuzzy-threshold 0.5 --fuzzy aggressive
```
### Scope Symbols and Normalize
```bash
# Convert operators to Unicode symbols: => → ⇒, -> → →, != → ≠
atomwrite --workspace . scope src/ --lang rust --action symbols --dry-run
# Apply NFC Unicode normalization
atomwrite --workspace . scope src/ --lang rust --action normalize --dry-run
```
### Use .atomwrite.toml Config
```bash
# Create a project config (optional, CLI flags take precedence)
cat > .atomwrite.toml << 'EOF'
[defaults]
backup = true
[fuzzy]
mode = "auto"
threshold = 0.85
EOF
# atomwrite auto-loads .atomwrite.toml from project root
atomwrite --workspace . edit src/main.rs --old "foo" --new "bar"
```
### Hash Output Uses checksum Field
```bash
# v0.1.25: field is "checksum" (was "value" in v0.1.24)
## v0.1.24 — Recipes
### Typed Error Handling in Agent Pipelines
All errors now emit structured JSON on stdout. Parse exit codes deterministically:
```bash
# Safe pipeline: check exit code BEFORE parsing stdout
output=$(atomwrite --workspace . get config.toml database.pool.max 2>/dev/null)
exit_code=$?
case $exit_code in
65) echo "$output" | jaq -r '.suggestion' ;;
*) echo "unexpected: exit $exit_code" ;;
esac
```
### Delete Recursive (Now Works)
```bash
# v0.1.24: delete --recursive actually traverses and removes
atomwrite --workspace . delete --recursive logs/
# Dry-run first to preview
atomwrite --workspace . delete --recursive --dry-run logs/
```
### Hash Recursive (Now Works)
```bash
# v0.1.24: hash --recursive walks directories
atomwrite --workspace . hash --recursive src/
```
### Search Multiline (Now Works)
```bash
# v0.1.24: multiline patterns actually match across lines
atomwrite --workspace . search --multiline 'fn main\(\).*\{' src/ --include '*.rs'
```
### Replace Rejects Empty Pattern
```bash
# v0.1.24: empty pattern is rejected (was silently destructive)
atomwrite --workspace . replace '' 'X' src/
# Exit 65: INVALID_INPUT — empty pattern would match every position
# Correct usage: explicit pattern
atomwrite --workspace . replace 'old_api' 'new_api' src/
```
### Backup Timestamp With Milliseconds
```bash
# v0.1.24: timestamps include milliseconds to prevent collision
atomwrite --workspace . backup config.toml
# Creates: config.toml.bak.20260621_143022_847
# Rollback accepts prefix match (backward-compatible)
atomwrite --workspace . rollback config.toml --timestamp 20260621_143022
```
### Get Values Without Double Quotes
```bash
# v0.1.24: get returns raw value, not JSON-serialized string
atomwrite --workspace . get Cargo.toml package.version
# Output: {"type":"result","value":"0.1.24",...}
# NOT: {"type":"result","value":"\"0.1.24\"",...} (old behavior)
```
## How to Overwrite a Large File Safely (v0.1.35)
- Problem: existing target above XDG `[write].confirm_large_bytes` (default 100 KiB)
- Solution: pass `--ack-overwrite` (one-shot; no Y/N)
```bash
## How to Preview Deletes Without Mutating (v0.1.35)
- Problem: need a plan without deleting
- Solution: `delete --plan` (not `--confirm` / `--yes`)
```bash
atomwrite --workspace . delete --plan --older-than 7d logs/
atomwrite --workspace . delete --older-than 7d logs/
```
## How to Consume Watch Idle Output (v0.1.35)
- Problem: agents assumed empty stdout when no FS events
- Solution: always parse final `type:watch_summary`
```bash
atomwrite --workspace . watch . --idle-exit-ms 500
```
## How to Merge With Conflict Markers (v0.1.35)
```bash
atomwrite --workspace . semantic-merge --base b --ours o --theirs t --output out.txt
atomwrite --workspace . semantic-merge --base b --ours o --theirs t --output out.txt --no-conflict-markers
```