Hashline patch grammar

August 22, 2026 ยท View on GitHub

When edit_mode is "hashline", edit accepts exactly one argument:

{ "patch": "..." }

A patch contains one or more tagged file sections. An optional *** Begin Patch / *** End Patch envelope may wrap the sections.

[path/to/file#TAG]
OPERATION

TAG is exactly four hexadecimal digits. Obtain it from a current tagged read of that path. A tagged read retains only the rows it shows, so each line address must be within the read result. Gap addresses also require the adjacent rows, and REM and MV require a whole-file tagged read. Tags rotate after a mutation; read the file again before a follow-up patch. An edit response can show a tag for changed context, but it may not retain all rows needed by the next operation.

Addresses

All addresses use the coordinates from the tagged read, even when a section has multiple operations.

FormMeaning
0Beginning-of-file insertion point.
NOne line. With PUT, this replaces line N.
N.=MInclusive range. N..=M and N..M are also accepted.
<N / >NInsertion gap before / after line N. Use these to insert without replacing a line.
N*Language-aware block containing line N.
<N* / >N*Insertion gap before / after that block.
$ / $-KLast line / K lines before the last line. These forms also work in ranges, gaps, and block addresses.

Operations

PUT

Use a trailing colon for text:

PUT <address>:
+first new line
+
+third new line

Every text row starts with +; + alone adds an empty line. A final newline in the patch is allowed. Without the colon, PUT copies a register and cannot have body rows:

PUT >$ @saved
PUT >$

The latter uses the anonymous register from a bare CUT. A named register starts with @ and may contain only ASCII letters, digits, _, and -.

CUT

CUT <address> [@name]

CUT removes the addressed content and saves it in the named register, or in the anonymous register when the name is omitted.

REM

REM

REM removes the entire section file, accepts no address, and cannot be combined with another operation in that section.

MV

MV destination/path

MV moves the complete section file. The destination is one whitespace-free path; matching single or double quotes are optional. It may appear once per section and must come after any PUT or CUT operations. It also requires a whole-file tagged read of the source.

Example

*** Begin Patch
[src/example.rs#CAFE]
PUT >25:
+inserted after line 25
CUT 13.=14 @fragment
MV src/renamed.rs
*** End Patch

If an edit returns a stale-tag or eligibility error, follow its steering text and perform a new tagged read with the required rows or boundaries before retrying.