Hash-anchored file editing for Gemini CLI. Replaces the built-in read_file, replace, and grep_search tools with versions that use content hashes instead of exact string matching.
Gemini CLI's replace tool requires byte-exact string matching. Models frequently get whitespace wrong, causing "0 occurrences found" loops. This is the most common editing failure.
Read The Harness Problem for the full context.
Every line gets a 4-character content hash. The model references lines by hash instead of reproducing content.
1#ZTVK:import os
2#HBPH:import sys
3#RKSY:
4#XMXP:def hello():
5#ZSTX: return "world"
Editing targets the hash, not the text:
{"op": "replace", "anchor": "5#ZSTX", "new_text": " return \"hello world\""}If the file changed since the read, hashes won't match and the edit fails safely with corrected anchors. The model re-reads and retries.
Based on the hashline concept by Can Bölük (oh-my-pi).
gemini extensions install https://github.com/cupofcat/hashline-gemini-cliPrerequisites: Node.js 18+. For best hashline_grep performance, install ripgrep (rg). Falls back to grep if rg is not available.
To uninstall:
gemini extensions uninstall hashlineThree new tools alongside the built-ins (nothing is disabled by default):
| Tool | Use when | Built-in equivalent |
|---|---|---|
hashline_read |
You plan to edit the file | read_file (still available for plain reads) |
hashline_edit |
Surgical edits with anchors | replace (still available as fallback) |
hashline_grep |
Search then edit the matches | grep_search (still available for plain search) |
The model learns to use hashline tools for editing and built-ins for reading via the bundled GEMINI.md context.
Want to go all-in? To disable the built-in tools entirely, add excludeTools to gemini-extension.json in the installed extension directory:
{
"excludeTools": ["read_file", "replace", "grep_search"]
}Read a file. Every line is tagged LINE#HASH:content.
| Parameter | Required | Description |
|---|---|---|
file_path |
yes | Absolute path |
offset |
no | Start line (1-indexed) |
limit |
no | Max lines |
Edit a file using anchors from hashline_read.
| Parameter | Required | Description |
|---|---|---|
file_path |
yes | Absolute path |
edits |
yes | Array of operations |
Operations:
Rules: Always hashline_read first. Copy anchors exactly. new_text is plain content (no hashes). Batch edits into one call. Don't anchor on blank lines or closing delimiters.
Search files. Results include anchors usable with hashline_edit.
| Parameter | Required | Description |
|---|---|---|
pattern |
yes | Regex pattern |
path |
no | File or directory |
glob |
no | File filter (e.g., "*.py") |
case_insensitive |
no | Case-insensitive |
context |
no | Context lines (0-10) |
Uses ripgrep. Falls back to grep.
The hash is xxHash32 of each line's whitespace-stripped content, truncated to 2 bytes (65,536 possible values), encoded as 4 characters from the alphabet ZPMQVRWSNKTXJBYH. Punctuation-only lines (like }) mix the line number into the hash seed to avoid collisions.
Pure JavaScript — no WASM, no native modules.
Safety features:
- Stale anchor detection with ±20 line closest-match relocation
- Overlapping edit detection
- CRLF and BOM preservation
- Atomic file writes with permission preservation
- Binary file rejection
- Auto-stripping of hash prefixes models copy into replacements
- Boundary autocorrection (trailing/leading line dedup)
- Escaped tab autocorrection
- Noop detection
git clone https://github.com/cupofcat/hashline-gemini-cli
cd hashline-gemini-cli
npm install
npm run build
npm test # 150 unit + E2E tests
gemini extensions link .
# Gemini integration tests (slow, needs API access)
bash test/gemini-integration.sh # basic CUJ tests
bash test/gemini-stress.sh # complex editing scenariosApache 2.0. See LICENSE and NOTICE.
Based on oh-my-pi by Can Bölük (MIT). xxHash32 from the public spec (BSD-2).
hashline, hash-anchored editing, content-addressable line references, gemini cli extension, MCP file editing, coding agent tools, replace_in_file alternative, 0 occurrences found fix, AI coding agent file editing, whitespace-invariant editing, model context protocol