Initial commit: omp-edit DSH plugin (oh-my-pi port) with docs and tests
This commit is contained in:
commit
71953ffb8f
19 files changed
+2412
No files matched your search
@@ -0,0 +1,155 @@
|
||||
# omp-edit
|
||||
|
||||
[oh-my-pi](https://github.com/can1357/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 `-x` is 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* @fn` in one section, `PUT <1 @fn` in another).
|
||||
- The whole call is validated before anything is written.
|
||||
- Tolerant parsing: an optional `*** Begin/End Patch` envelope and markdown fences are stripped, and `PUT 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:
|
||||
1. `modelVariants` (substring of `provider/model` → mode), first match wins.
|
||||
2. `mode`.
|
||||
3. With `hashline` and `strict: false`, model families oh-my-pi considers unreliable at hashline fall back to `fallbackMode` (default `replace`). The list is `fallbackModels`, default `kimi, 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:
|
||||
- **`edit`** is replaced outright. Nothing of DSH's `edit` is used.
|
||||
- **`read`** and **`grep`** delegate 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` (or `grep`) a file before editing it. This is `fs-observation-policy`.
|
||||
- **Guarded writes:** writes go through `ctx.fs` with 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
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```bash
|
||||
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`:
|
||||
|
||||
```yaml
|
||||
- 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
|
||||
|
||||
```bash
|
||||
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](https://git.rizzy.dev/DSH-Plugins/omp-lsp), which appends language-server diagnostics to every edit.
|
||||
Reference in new issue
Block a user