omp-edit
oh-my-pi-style editing for the DeepSeek Harness (DSH). It replaces DSH's built-in edit tool with oh-my-pi's hashline patch language. It also wraps read and grep so their output carries the content-hash tags the patches anchor on.
The model points at line numbers of a tagged snapshot instead of retyping the old text. That avoids whitespace-mismatch "string not found" loops and saves output tokens. A stale snapshot is detected before anything is written, and is rebased automatically when the anchored lines survived.
What the agent sees
read output carries a [PATH#TAG] header and LINE:TEXT rows (the 4 hex characters are a hash of the whole file):
<path>[src/app.py#1A2B]</path>
1:def greet(name):
2: print("Hello, " + name)
grep groups matches under a tagged header per file. A grep hit counts as having read the file, so it can be edited straight away:
[src/app.py#1A2B]
2: print("Hello, " + name)
edit takes one input string with one or more file sections:
[src/app.py#1A2B]
PUT 2.=2:
+ print(f"Hi, {name}")
It returns the fresh tag plus a compact numbered preview of the changed region, so the agent can chain edits without re-reading. The Web UI shows the usual diff card.
Hashline operations
| Form | Effect |
|---|---|
PUT N.=M: + +rows |
Replace original lines N..M (N.=N for one line) |
PUT N*: + +rows |
Replace the syntactic block opening at line N (function/class/object, Markdown heading section, Python/YAML indented block; leading decorators included) |
PUT <N: / PUT >N: |
Insert before / after line N (<1 = file head, >$ = file tail) |
PUT >N*: |
Insert after the block opening at N |
CUT N.=M [@name] / CUT N* [@name] |
Delete and capture (anonymous register, or a named one) |
PUT <N [@name] / PUT >N [@name] |
Paste a register at a gap |
PUT N.=M @name / PUT N* @name |
Paste a named register over a range/block |
REM |
Delete the file |
MV DEST |
Move the file after the section's edits |
The rules match oh-my-pi:
- Body rows are
+TEXT; a lone+is a blank line; literal-xis written+-x. - All numbers refer to the tagged snapshot, never shifted by earlier hunks.
- Overlapping ranges are rejected.
- Named registers persist for the session, and cross-file moves work (
CUT 1* @fnin one section,PUT <1 @fnin another). - The whole call is validated before anything is written.
- Tolerant parsing: an optional
*** Begin/End Patchenvelope and markdown fences are stripped, andPUT 3-5:/PUT 3:are accepted with a warning.
Stale tags
Every tagged read/grep/edit records a snapshot. When the file changed since the tag (another tool, a formatter, the user):
- if every anchored line still exists unchanged, with a neighbour that moved by the same offset, the patch is remapped onto the current file and applied. A warning says so.
- otherwise the edit is rejected without writing, with a message to re-read.
A tag that was never issued for that file (e.g. copied from another file) is rejected with a hint.
Edit modes
mode picks the wire format of the replacement edit tool:
| Mode | Input | Notes |
|---|---|---|
hashline (default) |
input |
Described above |
replace |
file_path, old_string, new_string, replace_all, or edits: [{old_string,new_string,replace_all}] |
Fuzzy whitespace matching: exact → indentation/trailing-space-insensitive → whitespace-collapsed → smart quotes/dashes → ≥95% similarity. The file's indentation is carried over. Batched edits are atomic |
sloppy |
input |
*** Edit File: / *** Find / *** Replace · *** Insert Before · *** Insert After, … captures, all suffix |
apply_patch |
input |
Codex format: *** Add/Delete/Update File, *** Move to, @@ scoped hunks, *** End of File |
Per-model selection, like oh-my-pi:
modelVariants(substring ofprovider/model→ mode), first match wins.mode.- With
hashlineandstrict: false, model families oh-my-pi considers unreliable at hashline fall back tofallbackMode(defaultreplace). The list isfallbackModels, defaultkimi, mimo, minimax, deepseek, stepfun, codex-spark, glm-5.3-flash.
The tool schema follows the model the agent actually uses and switches mid-session when you change models.
How it replaces the built-ins
DSH registers edit/read/grep in each agent preset. Tools registered in an agent's own scope shadow those, so this plugin listens for agent/created and registers into every agent's scope:
editis replaced outright. Nothing of DSH'seditis used.readandgrepdelegate to DSH's implementations (sandbox, limits, ripgrep, spill files all unchanged) and only add tags to the output.
DSH's safety rules are kept:
- Read first: you still need to
read(orgrep) a file before editing it. This isfs-observation-policy. - Guarded writes: writes go through
ctx.fswith the version guard. - Sandbox: the session's sandbox policy applies to writes and
REM. - Line endings: CRLF files stay CRLF.
To use DSH's original tools again, disable this plugin (Settings → Plugins → OMP Edit) or set replaceEdit/replaceRead/replaceGrep: false.
Installation
dsh plugin --profile web add git+https://git.rizzy.dev/DSH-Plugins/omp-edit.git
Or in the Web UI, go to Settings → Plugins → Install and paste git+https://git.rizzy.dev/DSH-Plugins/omp-edit.git. You can also ask the agent to install it with plugin_manager → install_bundle.
For development:
git clone https://git.rizzy.dev/DSH-Plugins/omp-edit.git ~/dsh-plugins/omp-edit
dsh plugin --profile web add link:$HOME/dsh-plugins/omp-edit
Then restart DSH (systemctl --user restart dsh or re-run dsh web). New sessions get the new edit tool. Sessions that already exist pick it up once they're re-created, e.g. after the restart.
No dependencies: everything is plain Node.js (≥ 20).
Configuration
Set options in the config of the row in ~/.dsh/profiles/web/cordis.patch.yml:
- id: omp-edit
config:
mode: hashline # hashline | replace | sloppy | apply_patch
strict: false # true: never fall back from hashline
fallbackMode: replace
fallbackModels: [kimi, mimo, minimax, deepseek, stepfun, codex-spark, glm-5.3-flash]
modelVariants: # substring of "provider/model" -> mode
gpt-5-codex: apply_patch
replaceEdit: true
replaceRead: true
replaceGrep: true
fuzzy: true # replace mode fuzzy matching
Tests
npm test # node --test test/*.test.mjs — 32 tests (engine + integration on the real DSH fs-local backend)
Files
| File | Role |
|---|---|
index.js |
DSH plugin: per-agent tool registration, mode selection, fs policy, diff cards |
lib/hashline.js |
Hashline parser and applier |
lib/block.js |
N* block resolution (brackets, indentation, Markdown, decorators) |
lib/store.js |
Session snapshot store, Myers line diff, stale-tag rebase |
lib/replace.js |
Fuzzy replace mode |
lib/sloppy.js |
sloppy mode |
lib/apply_patch.js |
apply_patch mode |
lib/diff.js |
Hunk diffs for the UI diff card |
lib/prompts.js |
Model-facing tool descriptions (adapted from oh-my-pi's crates/pi-edit/prompts) |
Pairs well with omp-lsp, which appends language-server diagnostics to every edit.