npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dsh-xhl/dsh-live-inspector

v1.0.0

Published

Git Tree and live diff inspector for the DeepSeek Harness web surface: watch every file the agent touches, review changes turn by turn, and undo a specific turn or a single hunk — with the filesystem reached through the plugin's own Host HTTP routes.

Downloads

121

Readme

@dsh-xhl/dsh-live-inspector

DeepSeek Harness (DSH) Web plugin that provides a lightweight, real-time Git Tree & Live Changes Inspector in the right sidebar (sidebar.right tab), with turn-scoped and hunk-level undo.

Features

  • Single Tab Architecture (Zero RAM Bloat): Instead of opening heavy editor/viewer tabs for every accessed file, it keeps everything inside a single, high-performance Git Tree tab (~10 KB memory footprint).
  • Live File Status Badges:
    • [M] (Amber): Modified files (edit, str_replace_editor).
    • [A] (Emerald): Created / added files (write, write_file).
    • [R] (Sky Blue): Read / inspected files (read).
    • [D] (Red): Deleted in this session by an undo.
  • Real-Time Active Indicator: Displays ⚡ Agent active on: <path> with a pulsing live status on the exact file currently being touched.
  • Interactive Controls:
    • Filter by All, Changes (M/A), or Reads (R).
    • Instant text filter search.
    • Optional View button to open a single file preview only when explicitly desired.
    • Clear button to reset the session file list.

Undo

Undo is scoped to a turn. There is no file-level or session-level undo: undoing turn N restores the state each file entered turn N with, so earlier turns always survive.

Only the newest turn is undoable

Undo controls appear only while the newest turn is selected. On any older turn they are absent — not greyed out — because an older turn has already been reviewed, and a visible dead button would imply an action the panel is going to refuse.

Today's turn is the default, so the panel opens ready to undo what the agent just did.

The selected turn scopes the whole panel:

  • the file list shows only the files that turn changed (All (n) counts that scope),
  • the diff inspector follows, re-selecting a file when the turn changes,
  • the ⟲ Undo Turn N (n) button undoes that turn across every file it changed.

All turns is available in the selector for reading history across the whole session; it is a view rather than an undo scope, so no undo controls appear while it is selected.

  • Each file row shows its turns (Turn 3, or Turn 1–4) and carries ↶ Undo Turn N.
  • A file's detail header carries ↶ Undo change, which reverts just the selected hunk.
  • Older turns' change counts are labelled reviewed, and hunks outside the selected turn are dimmed and not individually revertible.

The turn snapshot

A file's text is captured immediately before that turn's first mutation of it, so the snapshot is exactly what the file inherited — including every earlier turn's work. Writing it back is what makes a turn-scoped undo unable to reach past its own turn. A read before the first mutation counts as untouched too, which is what catches a write-after-read.

Line / diff-level undo

Reverts one contiguous change — one tool call's hunk — leaving the file's other changes in place.

  • Every hunk the diff view shows is listed as a #n chip above the diff, each with its own ↶ Undo change button.
  • A hunk is undone by replacing its recorded post-change text with its recorded original text, so unrelated edits in the same file survive.

Deleting and restoring

Deleting is the only destructive action, so it is the only button rendered as a solid red fill. Every restorative action — hunk undo, turn undo, ⤴ Restore — is amber, because it writes content back rather than removing it.

Two kinds of deletion are covered:

  • A file the agent created and you delete with 🗑 Delete file. The row becomes a strike-through [D] and ⤴ Restore writes it back from the recorded content.
  • A file deleted by a shell command (rm, del, …). The file tools have no delete verb, so the panel cannot see the operation directly. After any shell call it re-probes the paths it already knows about, and a path that vanished becomes a [D] row you can restore from the newest recorded content — or from the turn snapshot if the file was only read.

A deleted file with no recorded content cannot be restored, and its ⤴ Restore says so rather than failing on click.

Safety rules

Undo refuses rather than guesses:

  • Drift: if the file changed since the edit, the recorded text no longer matches and the undo is refused.
  • Ambiguity: if the recorded text now occurs more than once, the undo is refused.
  • No artifact: a change with no exact recorded before/after pair (replayed history, or a tool other than edit/write) is never offered as individually revertible. The panel says so instead of showing a button that would corrupt the file.
  • Older turn: undo is not offered at all outside the newest turn.
  • Mid-write: a file the agent is currently writing is not undoable until the step ends.
  • Oversized: files above 512 KB get no text snapshot. They are still recognized by digest, but a restore is refused.
  • Roots: paths that resolve to a filesystem root, a drive root, or a UNC share root are rejected outright.

Destructive actions need a second click (the button switches to a ⚠ Confirm … state).

Architecture

  • lib/index.js — Host half. Owns real filesystem access and publishes a Typert Remote namespace named inspectorFs with four methods: readText, writeText, deleteFile, and statPath (the last detects deletions performed outside the file tools). Paths resolve through ctx.fs.processPathFromHostPath when the backend provides a mapping, and by joining the session working directory otherwise.
  • lib/client.js — Browser half. Records file activity, diff artifacts, and per-turn snapshots from session events, then renders the panel and undo controls.

Why a Remote namespace, not host.call

host.call(method, args) is an API for dynamic packages — the ones an agent authors as a code string. There, host is injected as a closure parameter by the runner.

An installed bundle's Client module runs under window.__ModuleLoader__, which resolves only a fixed seed list (react, react-dom, @deepseek-ai/cordis, @deepseek-ai/dsh-client-store, the dsh-client-ui-* primitives) and traps require itself. host does not exist there, so host.call throws host is not defined. A bundle reaches its Host half through a Typert Remote namespace consumed as ctx.inject(['remote.<namespace>']).

This package has no build step, so lib/index.js writes the protocol's Remote-method descriptor onto the service prototype by hand instead of using the build-time @Remote decorator. The Gateway's source-mode (src-json) fallback accepts it: parameter names come from the function signature and arguments travel as plain JSON, so no generated schema is required.

Method failures reject across the boundary. The Browser half converts a rejection into the { ok: false, error } shape it reasons about, so a refused path surfaces as one toast rather than an unhandled throw.

Per-turn snapshots are keyed by file and turn. A snapshot is read immediately before that turn's first mutation of the file, so it captures exactly what the file inherited — including every earlier turn's work. That is what makes a turn-scoped undo unable to reach past its own turn.

The pre-edit baseline read is authorized by this plugin's own approval/request listener, which allows fs-read only; every other request is left untouched for the normal approval chain.

Installation

Published to npm as @dsh-xhl/dsh-live-inspector:

dsh plugin --profile web add @dsh-xhl/dsh-live-inspector

From a checkout, dsh plugin --profile web add <path-to-this-directory> installs it as a local link, which is what the test profile does.

Manifest notes

  • dsh.bundle.patch points at cordis.patch.yml, whose row inserts the plugin under the id dsh-live-inspector with name: '@dsh-xhl/dsh-live-inspector'.
  • dsh.client declares platform: web and the two sidebar Client packages this UI orders itself against.
  • lib/client.js registers into window.__ModuleLoader__ under an id that must equal the package name. The loader derives the expected module id from the manifest and reports loaded without registering "<id>" when the two disagree, so renaming the package without updating that id silently drops the panel.
  • files ships only lib/ plus the patch and README. The former root-level client.js and index.js copies were removed; exports has pointed at lib/ for some time, and keeping two near-identical copies on disk invited editing the wrong one.