moesniper
Escape-proof precision file editor for LLM agents. Hex-encoded content, line-range splicing, atomic writes, metabolic pacing.
Quick Start
# Replace line 5
# Undo
Features
| Feature | What It Does |
|---|---|
| Hex-encoded content | Zero shell corruption — all payloads are hex strings |
| Line-range splicing | Replace or delete any contiguous line range, 1-indexed |
| Atomic writes | Temp file + rename(2) — file is never in an inconsistent state |
| Multi-step undo | Each edit creates a backup; --undo pops the stack |
| Manifest operations | Batch JSON operations applied bottom-up (line numbers never shift) |
| Dry-run preview | --dry-run shows diff with +/-/~ markers before applying |
| Indentation safety | Validation blocks mis-indented edits; --auto-indent fixes them, --force-indent bypasses |
| Context verification | --context <hash> verifies SHA-256 of surrounding lines before applying |
| PID file locks | Per-file locks with stale PID detection — auto-recovery if previous process died |
| Metabolic pacing | llmosafe 0.6.2 ResourceGuard::auto(0.5) — adaptive back-pressure based on RSS, IO wait, and load |
| Path traversal protection | .. components rejected, SecurityPolicy guards all file access |
| Configurable limits | File size, backup retention, lock timeout — all via environment variables |
| JSON output | --json for machine-readable results |
| Backup retention | Count-based + age-based purge policies |
Usage
sniper <file> <start> <end> <hex> Replace lines with hex content
sniper <file> <start> <end> --delete Delete line range
sniper <file> <start> <end> --stdin Read content from stdin
sniper <file> <start> <end> <hex> --context <h> Replace with context verification
sniper <file> --manifest <path> Batch operations (JSON)
sniper <file> --undo Restore from backup
sniper encode [--stdin|--file <path>|<text>] Hex-encode content
sniper context <file> <start> <end> Compute context hash for a given line range
Flags
| Flag | Effect |
|---|---|
--dry-run |
Preview changes without writing |
--json |
Output machine-readable JSON |
--stdin |
Read content from stdin instead of hex arg |
--context <hash> |
Verify SHA-256 hash (first 16 hex chars) of lines before/after edit target |
--auto-indent |
Auto-detect and apply indentation from context |
--force-indent |
Bypass indentation validation (allow unindented content) |
Context Verification
The --context <hash> flag allows rejecting an edit if the file has changed around the edit site.
The hash is the first 16 hex characters of the SHA-256 of the concatenated raw bytes (including all newlines) of exactly 3 lines before the <start> line, and exactly 3 lines after the <end> line. Line numbers are not part of the hash.
You can generate the context hash using the command:
Encoding
Content must be hex-encoded to prevent shell corruption:
|
# 666e206d61696e2829207b7d
Manifest Format
Operations apply bottom-up (highest line first, so earlier edits don't shift later targets). Line numbers are 1-indexed.
Examples
# Replace a function
# Delete a block
# Safe workflow (preview first)
&&
# Batch edit via manifest
|
# Insert at end of file
# Context-verified edit (rejects if surrounding code changed)
Configuration
| Variable | Default | Description |
|---|---|---|
SNIPER_LOCK_TIMEOUT |
30 |
Lock acquisition timeout (seconds, min 1) |
SNIPER_MAX_FILE_SIZE |
100MB |
Maximum file size to edit (0 = unlimited) |
SNIPER_BACKUP_RETENTION_COUNT |
50 |
Number of backups to retain (0 = unlimited) |
SNIPER_BACKUP_MAX_AGE_DAYS |
30 |
Max backup age in days (0 = no limit) |
SNIPER_DISABLE_AUDIT |
(unset) | Set to any value to disable audit logging |
How It Works
- Path validation —
normalize_path()rejects..traversal and canonicalizes - File size check — rejects files exceeding
SNIPER_MAX_FILE_SIZE - Lock acquisition — PID file lock with configurable timeout; stale PID detection recovers from crashed processes
- Context verification — optional
--context <sha256>flag hashes 3 lines before and 3 lines after the edit target; if the context changed since the agent computed line numbers, the edit is rejected with a "context mismatch" error - Backup — file copied to
.sniper/with hash+timestamp name - Splice — lines loaded into memory, range replaced, result written atomically (temp file + rename)
- Metabolic pacing —
llmosafechecks RSS, IO wait, load average; sleeps if system is under pressure - Purge — old backups pruned by count and age per retention policy
JSON Output
The --json flag produces a CliResult object with these fields:
| Field | Type | Description |
|---|---|---|
status |
string | ok, dry_run, restored, encoded, or error |
file |
string | Target file path |
message |
string | Error message (on failure) or encoded hex (on encode) |
lines_removed |
number | Number of lines removed |
lines_inserted |
number | Number of lines inserted |
line_shift |
number | Net line change (lines_inserted - lines_removed); positive = lines moved down |
total_lines |
number | Total lines in file after edit |
operations |
number | Number of manifest operations applied |
backup |
string | Path to backup file created |
ai_hint |
string | Suggestion for the LLM agent |
diff_preview |
array | Dry-run diff with +/-/~ markers |
indent_warning |
string | Indentation mismatch warning |
indent_fixed |
boolean | Whether auto-indent was applied |
Contributing
See CONTRIBUTING.md.
License
MIT — see LICENSE.