agentsclimarketplace

Hashline

Skill codered/ai/skills/hashline

Hash-anchored file editing with hashline — read a file as line:hash anchors, then patch by anchor so an edit against drifted content is rejected instead of silently landing on the wrong line. Use whenever hashline is available, as a native MCP server or as a Prime Agent kernel skill, for reads, creates, patches, deletes, and renames.From its SKILL.md

Install
npx -y skills add codered/ai --skill hashline

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

4.8 KB, as published. Nobody here has run it

hashline

Every line carries an xxh32 content hash. You target lines by line:hash anchors instead of by line number or by matching a string. If the file drifted since you read it, the hash no longer matches and the patch is rejected — you never write to the wrong place, and you find out immediately rather than three edits later.

Requires the hashline binary (developed against 0.9.1) on PATH, or HASHLINE_BIN pointing at it.

Getting the tools

Any harness with native MCP support (Claude Code, Codex, …) — register the stdio server and call its tools directly:

{ "mcpServers": { "hashline": { "command": "hashline", "args": ["mcp"] } } }

Prime Agent — this skill ships a Python package that speaks the same stdio server from the IPython kernel. See references/prime-agent-setup.md for install and the AGENTS.md rules; once loaded:

import hashline
print(await hashline.read("src/auth.py"))

Every kernel call is async — always await. The rest of this skill applies to both: same server, same tools, same patch syntax.

Workflow

Always read → patch → verify. The read gives you the anchors; the patch result is the re-read file, so it doubles as the verification.

[src/auth.py#1a2b]
1:4f|def verify_token(token):
2:a3|    decoded = jwt.decode(token, SECRET)
3:9c|    return decoded

1a2b is the whole-file hash; 2:a3 is the anchor for line 2. Copy anchors verbatim from the read — never guess a hash.

await hashline.patch("src/auth.py", """SWAP 2:a3:
+    decoded = jwt.decode(token, SECRET, algorithms=["HS256"])""")

Operations

Each op is a line; content lines are prefixed with +. Ranges are N..M (N.=M also accepted). The :HH hash is optional but always include it — it is the whole point of the tool.

OpEffect
SWAP N:HH: + +linesReplace line N
SWAP N:HH..M:HH: + +linesReplace lines N through M
DEL N:HHDelete line N
DEL N..MDelete lines N through M
INS.PRE N:HH: + +linesInsert before line N
INS.POST N:HH: + +linesInsert after line N
INS.HEAD: + +linesInsert at start of file
INS.TAIL: + +linesInsert at end of file
SWAP.BLK N:HH: + +linesReplace the whole syntactic block around N
DEL.BLK N:HHDelete the syntactic block around N
INS.BLK.PRE / INS.BLK.POST N:HH: + +linesInsert before/after the block around N

Multiple ops in one patch, wrapped in envelope markers:

*** Begin Patch
SWAP 5:1a:
+    decoded = jwt.decode(token, SECRET, algorithms=["HS256"])
DEL 9:c3
INS.POST 12:d4:
+    log.info("verified %s", decoded["sub"])
*** End Patch

Ops within one patch address the ORIGINAL line numbers — do not re-number for earlier inserts or deletes in the same patch.

Use dry_run=True to validate anchors without writing.

The other tools

ToolArguments
readfile, json=False
patchfile, patch, dry_run=False
writefile, content, force=False (create; force to overwrite)
find_blockfile, anchor — the structural block around a line:hash
remove_filefile
rename_filesrc, dst

The parameter is file, not path. Relative paths resolve against the working directory. list_tools() discovers them at runtime if in doubt.

Rules

  • Read before you patch, every time. Anchors from an earlier turn may be stale.
  • Never invent an anchor. Copy line:hash from the most recent read output.
  • Failures come back as text, not exceptions. A rejected patch returns a string starting with Error: (e.g. line 2 content changed since last read (expected hash ff, got bf)) and the call still "succeeds". Read the returned text before assuming the edit landed.
  • A rejected patch means the file changed under you. Re-read and rebuild the patch — do not retry with the hash stripped off.
  • Prefer SWAP.BLK over a long SWAP N..M when replacing a whole function.
  • Use this instead of string-replace edit tools, open(...).write(), sed, or shell heredocs. Those bypass the drift check that makes hashline worth using.

Troubleshooting

  • RuntimeError: hashline binary not found — install hashline, or set HASHLINE_BIN to its absolute path.
  • unknown operation in the warnings — the op name or the line:hash is malformed. Re-read the file and rebuild the anchor from its output.

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.