Preface

Running coding agents in DeepSeek Harness (DSH), file editing is often a major source of token consumption and errors. Common str_replace or line-number-based approaches require the model to verbatim reproduce the old code to be replaced in its output—this part counts as output tokens, which are typically billed at about 5–6 times the rate of input tokens. Inserting one line above shifts all subsequent line numbers, increasing the risk of editing the wrong line, and the tool side often lacks validation of whether “this range is exactly what the model just saw.”

Below is dsh-better-edit (maintainer Rianico). It provides content-hash-based (hashline) read, edit, and undo_last_edit tools for DSH: each line is assigned a 3-character hash as its address. Editing only sends the start/end hashes and replacement text without echoing the old content. The range is validated against the model’s read state before writing; expired or unseen lines are directly rejected, and new anchors are sent back. The project uses the MIT license, with an npm version of 0.4.0 and approximately 15 GitHub stars.

What It Is

dsh-better-edit is a client-side plugin for DSH, classified as “client-side.” It mounts onto the DSH runtime via cordis.patch.yml and registers hashline versions of read/edit to the agent scope during agent/session-start, replacing the built-in tools of the same name in the preset while retaining the built-in write.

Unlike line-number or full-text search-and-replace, hashline treats each line as content-addressed: the hash is derived from the line content, so editing above does not invalidate anchors for unchanged lines below. The plugin is discoverable in the SkillHub community directory (skillhub.cn/plugins/Rianico/dsh-better-edit); this directory is an independent community site with no official affiliation to DeepSeek or High-Flyer.

Core Features

Three Tools

Tool Function
read Returns the file in HASH│content format, supporting offset (starting from 1) and limit for pagination
edit Replaces content based on remove_from / remove_to hash ranges; supports up to 32 edits per atomic batch in the same file
undo_last_edit Undoes the last hashline edit for a specified path; remains available after restart

Lines shown by read, lines echoed in diffs, and lines rejected-and-served are all counted as “served.” edit validates each line before writing: unseen lines trigger [E_RANGE_UNSERVED], disk content mismatch with anchors triggers [E_RANGE_STALE], and failure of any item in a batch aborts the entire write ([E_BATCH_ABORT]).

Differences from str_replace

The comparison points from the README, all sourced from project documentation and reproducible benchmarks (not third-party cases), are as follows:

  • Edit calls do not echo replaced text; only two 3-character hashes and new content are sent.
  • Anchors are content-addressed; during consecutive edits, hashes for unchanged lines remain valid. Diff output includes new anchors, so read is often unnecessary after each edit.
  • The range is validated line-by-line against what the model has seen; incorrect anchors or expired content are hard-rejected before writing, and current lines with new hashes are sent back for retry.
  • Tolerates minor ASCII whitespace changes (e.g., after prettier or black processing, anchors remain parseable); whitespace inside string literals is excluded.

The project reports, in a benchmark with fixed 103 lines of corpus and 12 edits: compared to str_replace, hashline edit reduces output tokens by approximately 31% (29–47% for multi-line ranges); in an external drift refactoring task, tool calls are about 3 vs. 6 (compared to OMP wrapper, method explanation). Deterministic correctness battery score is 23/23. Locally, run npm run benchmark to reproduce the counting part.

Storage and Undo

Hash snapshots, served status, and undo history are stored by default in central mode:

$DSH_HOME/plugins/dsh-better-edit/runtime/<name>-<hash8>/hash-store.sqlite

The DB is a disposable cache; deleting it rebuilds hashes on the next read based on file content. Undo default TTL is 7 days (undo_ttl_s: 604800; -1 for permanent).

Installation and Enabling

Environment requirements: Node ^22.19.0 || >=24.0.0 (required by DSH; storage depends on node:sqlite); an existing dsh profile.

Choose one installation method:

npx @deepseek-ai/dsh plugin --profile web add github:Rianico/dsh-better-edit   # From GitHub
npx @deepseek-ai/dsh plugin --profile web add dsh-better-edit                   # From npm
npx @deepseek-ai/dsh plugin --profile web add /path/to/dsh-better-edit          # From local source

No additional configuration is needed. The next session for this profile will load the hashline tools. To verify the plugin layer is active:

dsh --profile <name> --dump-config   # Output should include "# == dsh-better-edit" layer

Optional configurations are in $DSH_HOME/plugins/dsh-better-edit/config.yaml, such as storage location, undo TTL, and central cleanup strategy. Environment variables DSH_BETTER_EDIT_STORE_DIR and DSH_BETTER_EDIT_AUTO_GITIGNORE can override the yaml. On first startup, if the file does not exist, the plugin generates a commented default configuration without overwriting existing files.

Typical Usage

Read: Lines with Hash Prefix

Each line returned by read has a hash prefix at the beginning, which serves as the line address:

ve7│function hello() {
szJ│  console.log("world");
kQm│}

Edit: Locate by Hash Range

The following example replaces the line with szJ with new content:

{
  "path": "src/main.ts",
  "edits": [["szJ", "szJ", "  console.log('hi');"]]
}

The tool returns a diff with new anchors for chaining edits:

- szJ │   console.log("world");
+ a3m │   console.log('hi');
  kQm │ }

Multiple edits can be submitted for the same file at once, with all-or-nothing writing:

{
  "path": "src/main.ts",
  "edits": [
    ["a1b", "a1b", "new line 1\n"],
    ["c3d", "c3d", "new line 2"]
  ]
}

Undo

Call undo_last_edit for the recently edited file, passing { "path": "src/main.ts" }. It only takes effect if the disk content matches the snapshot after the last edit; if externally modified in between, it returns [E_UNDO_STALE].

Use Cases and Considerations

Suitable for:

  • Structural changes in long sessions requiring consecutive edits without editing the wrong line.
  • Reducing edit-related output tokens and lowering the cost of str_replace-style repetition.
  • Files that may be formatted by tools or external processes between edits, requiring stale detection rather than silent overwrites.

Less suitable for:

  • Single-line tweaks (token savings are nearly break-even, as noted in the README).
  • Creating new files (continue using the built-in write; the plugin automatically runs read after write to establish anchors).

Before Installation:

  • The plugin reads and writes the workspace and storage under $DSH_HOME with the current dsh process permissions. Before installation, read the source code and MIT license.
  • DSH is still in developer preview; the plugin README notes it is currently wired for a specific dsh version. After upgrading DSH, recheck compatibility.
  • Benchmark numbers measure request load tokens and do not fully model real session costs like transcript failure retries; the correctness advantage should be evaluated in the context of specific workflows.

Links

If you have repeatedly encountered line drift or str_replace repetition errors in DSH, you can install the profile using the commands above, then run through the minimal loop of readedit → check the diff for new anchors before deciding whether to use it in daily agent sessions.