DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

zcr-133 /

zcr-133/dsh-follow-edits

Verified

Cline-style follow-along for DeepSeek Harness: open the file the agent just changed in the right Sidebar, jump to the change, and highlight the diff.

★ 1 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@2f6bb66a

dsh-follow-edits

English · 中文

test

Watch the agent edit, live. A plugin for DeepSeek Harness (dsh) that opens the file the agent just changed in the right-hand Sidebar preview, jumps to the changed line, and highlights the diff — the Cline / VS Code follow-along experience, inside dsh's own web UI.

Status: v0.3, working. Open + jump + yellow/red highlighting, exact positioning for both edits and deletions. Host/client logic covered by unit tests and verified on a real desktop profile. Every API used here was checked against the packages bundled in dsh desktop 0.2.0-rc.2 — see docs/research.md for the evidence trail.

pnpm install && pnpm test    # host.test.mjs + client.test.mjs (mock ctx, synthetic events)

What dsh already does, and what this adds

Capability Where it comes from
Preview files in the Sidebar built in
Refresh the preview after an edit built in (workspace-files changes() stream)
Open a file at a given line built in (ctx.sidebarRight.openResource(address, { params: { line } }))
End-of-turn diff review (red/green) built in (workspace-changes + the deliverables review tab)
Open + jump at the moment the edit happens ✅ this plugin
Yellow = changed line, red = removed content, in the preview ✅ this plugin (custom documentPreviews renderer)
Files written by terminal / shell commands also open ⚠️ this plugin (degraded: opens the file, no jump, no highlight)

In one line: this is not "add a jump button" — it chains a set of primitives dsh already ships behind a live follow trigger, because dsh currently only tells you about edits at the end of a turn.

Two kinds of change, two paths:

  • Edit tools (str_replace_editor / write / edit) → precise follow: open + jump + yellow/red highlighting.
  • Terminal / shell (a command writing a file via pwsh / bash) → degraded follow: open the file only (the preview refreshes itself). Shell writes bypass ctx.fs, so there is no before/after content to diff.

How the line number is obtained:

  • create / write → line 1
  • str_replace_editor insert → insert_line + 1
  • str_replace / edit (both edits and deletions) → the host reads the old file in tools/pre-execute (i.e. before the edit) and locates the start line with indexOf(oldStr) — exact
    • Removed content no longer exists in the file afterwards, so it must be captured before the edit
    • On a cold read/replay that mapping is empty → the client falls back to searching for newStr (still exact for edits; deletions fall back to the top of the file)

Architecture (checked against the official packages)

Path A: edit tools (precise)
agent calls str_replace_editor / write / edit
   ▼ tool/call committed
   ▼ tools/pre-execute   (★ BEFORE the edit) → ctx.fs reads the old file → indexOf(oldStr) → line
   ▼ tool executes
   ▼ tool/result committed
host side: sessionProjections.register({ key: 'followEdits', ... })
   │  folds tool/call + tool/result → last = { path, line, oldStr, newStr, ... }
   │  wire.view pushes that state to the client automatically (keyed, no allow-list)
   ▼
client: openResource(fileAddress, { params: { line } }) → open + jump + yellow/red highlight

Path B: terminal / shell (degraded)
a shell command writes to disk from a child process (bypasses ctx.fs; no tool/call, no fs/observed)
   ▼ host: node:fs.watch(session.header.cwd, { recursive: true })
   ▼ text/code suffix hit → shellBySession.set(sessionId, { path, ts })
   ▼ the next session event recomputes the projection and merges it into last (command = 'shell')
   ▼
client: openResource(fileAddress) → opens the file only, no jump, no highlight
  • Host half (index.js): one sessionProjections.register unit + a tools/pre-execute listener (captures the pre-edit line) + a recursive workspace-root watcher attached on session/created; inject: ['sessionProjections', 'fs'].
  • Client half (client.js): subscribes to the sessions list + faceOf('followEdits'), calls openResource on change, and registers a priority: extension documentPreviews renderer that paints changed lines yellow and removed content red.
  • Key detail: a third-party projection's wire value shows up in the client's faceOf(key) by key — no compile-time allow-list from dsh-api-remotes is needed.

Repository layout

dsh-follow-edits/
├── package.json        # bundle manifest: dsh.bundle.patch + dsh.client
├── cordis.patch.yml    # patch layer: inserts dsh-follow-edits
├── index.js            # host half: projection + tools/pre-execute line capture
├── client.js           # client half: projection subscription → openResource; highlight renderer
├── host.test.mjs       # host unit tests (projection folding / line capture)
├── client.test.mjs     # client unit tests (follow wiring / address building / highlight tree)
├── scripts/verify-install.mjs  # loader-resolution self-check (stale names, missing targets)
├── .github/workflows/ci.yml    # pnpm test on Linux + Windows, Node 20/22
├── docs/research.md    # research notes — where every API was verified
└── LICENSE

Install

Through the official Plugin Manager (runs pnpm install and loads both faces):

plugin_manager  action: install_bundle  target: <absolute path to this directory>

Or with the dsh CLI first, to check it on the web build:

dsh web --patch ./cordis.patch.yml

Desktop: merge the insert entry from cordis.patch.yml into ~/.dsh/profiles/desktop/cordis.patch.yml and restart the app.

Uninstall / disable: the Plugin Manager's set_bundle / remove_bundle.


Verification

Unit level (reproducible in this repo): pnpm test

  • host.test.mjs — projection folding (create/insert/write/view/failure/unrelated tools/gating), tools/pre-execute line capture (deletion → line 2, edit → line 3, capture failure → fallback, mapping read-once-then-deleted), and the recursive shell watcher (real node:fs.watch: write → last.command === 'shell', line null, no before/after text, a second write follows the newest file).
  • client.test.mjs — follow wiring (subscribe → open + jump, ts dedupe, 90 s expiry), address building (relative / absolute / outside the workspace), highlight tree (yellow line + red ⊖ row, other files untouched).

Real machine (desktop profile, verified) — after installing into the profile and restarting:

  1. Have the agent change a line → the right Sidebar opens the file automatically, the new content is yellow, the old content shows as red ⊖;
  2. Have the agent delete a line → the removed content appears as red ⊖ where it used to be.

Evidence: screenshot + pixel analysis (the yellow blend lands on (103,96,36) and the red on #e5484d = (229,72,77), both exact) plus eyeball confirmation.

Install-time self-check (reproducible, no dsh needed): node scripts/verify-install.mjs --profile ~/.dsh/profiles/<name>

Walks the same resolution steps the dsh loader does — exports targets exist, the patch entry name and the client module id both match package.json, dependencies resolve, and the profile symlink points back at this directory. It exists because a rename that misses one of those references fails silently: nothing in the UI tells you, it just stops following after the next restart.

Clean-room load check (verified) — a brand-new $DSH_HOME with a brand-new profile whose only non-official entry is this bundle:

export DSH_HOME=/tmp/dsh-home-clean
# profiles/verify-web/package.json → dependencies: { "dsh-follow-edits": "link:<repo>" }
#                                   → dsh.profile.bundles: [dsh-base, dsh-web-app, dsh-follow-edits]
dsh --profile verify-web --dump-config | grep -A2 '== dsh-follow-edits'
# == dsh-follow-edits
- id: dsh-follow-edits
  name: dsh-follow-edits

Exit code 0, 1262-line composed tree, no errors — the bundle layers exactly like an official one.


Known limitations

  • The highlight renderer replaces the built-in code preview. To overlay diff colours it registers a priority: extension renderer, so you lose the built-in Shiki syntax highlighting by default (switch back to "Code" in the preview's renderer dropdown). Folding diff colours onto the built-in highlighter is future work.
  • Exact line numbers depend on a non-session map. The host stores callId → oldStr start line in an in-memory map during tools/pre-execute, and the projection reads it when folding tool/result. This is the "apply that reads torn non-session state" pattern called out in practices.md (review-tier, not mechanically detectable). The cost: on a cold read/replay the map is empty and the line falls back to a client-side search.
  • Shell following is degraded and depends on a recursive watcher. Shell changes come from node:fs.watch({recursive: true}) (Windows/macOS support it; silently unavailable on Linux); with no before/after content it can only open the file; it follows text/code suffixes only and skips node_modules / .git / dist and friends.
  • A shell change is delivered on the next session event. Filesystem events are async and the projection is only recomputed when a session event drives it, so a shell write usually arrives with the event right after it (often the shell's own tool/result), or one hop later.
  • Follows all sessions (including background ones). To restrict it to the session in view, filter with retainedBy.mainView in client.js.
  • dsh is a developer preview; APIs drift between rc releases.

License

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 2f6bb66a7589

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout