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

pi-hledit

v1.1.7

Published

Pi wrapper for hledit hash-anchored file edits. Rejects stale writes before they happen for AI coding agents.

Readme

pi-hledit

npm version License: MIT Pi package

Hashline edit support for Pi: hledit hash-anchored file editing tools for AI coding agents.

pi-hledit is a thin wrapper — it registers a single pi tool that shells out to the hledit CLI. All edit-safety behavior described below lives in hledit itself.

Demo

pi-hledit in action

Animated stale-edit demo (underlying hledit CLI):

hledit stale-edit demo

The GIF shows hledit read producing LN#ANCHOR references, a stale edit rejected with {"ok":false,"error":"stale"}, then a successful edit after re-reading a fresh anchor. Source: hledit's README.

Related packages

  • hledit — the standalone Go CLI this extension wraps. Usable directly without Pi.

Install

Requirements

  • Go toolchain and the hledit CLI — install it first:
go install github.com/dabito/hledit@latest

Compatibility notes:

  • Line delta summaries (Lines: +N -M) require hledit >= 1.2.4. Older hledit versions still work; they just omit the line delta summary.

Make sure hledit is on PATH for pi, or set HLEDIT_BIN:

export HLEDIT_BIN="$HOME/go/bin/hledit"

Then install the pi extension:

pi install npm:pi-hledit

Then reload or restart pi:

/reload

Alternative: install from git

pi install git:github.com/dabito/pi-hledit

Verify

/hledit-status

By default the extension runs hledit from PATH. If pi cannot find it, set HLEDIT_BIN before starting pi. /hledit-status also shows the active diff rendering config.

What it does

Registers a single hledit tool for pi agents:

  • read — read a file with LN#HASH anchors for stale-safe editing
  • edit — replace, insert, delete, or replace a range by anchor
  • batch — apply multiple edits atomically in one call
  • grep — filter lines by substring to reduce token usage Successful edit and batch calls render a compact UI-only diff through Pi's native renderDiff UI. Model-facing tool output stays metadata-only.

Diff rendering config

Diff rendering is UI-only and can be tuned with environment variables before starting pi:

| Env var | Default | Description | |---|---:|---| | PI_HLEDIT_DIFF_MAX_LINES | 80 | Max rendered diff lines, including the omission marker. Minimum accepted value: 3. | | PI_HLEDIT_DIFF_CONTEXT | 2 | Context lines around changed ranges. Minimum accepted value: 0. | | PI_HLEDIT_DIFF_MAX_CELLS | 40000 | Max LCS comparison cells before diff body is omitted. Minimum accepted value: 1. |

Invalid values fall back to defaults.

Run /hledit-status inside pi to see the effective diff config after environment defaults/overrides are applied.

Tool parameters

| Param | Ops | Description | |---|---|---| | op | all | read, edit, or batch | | path | all | File path, resolved from pi cwd | | offset | read | 1-indexed start line for ranged reads; default 1 when ranged | | limit | read | Max lines for ranged reads; default 2000 when ranged | | grep | read | Substring filter for read output; still line-capped, not byte-capped | | context | read | Surrounding lines around each grep match; defaults to 2 when grep is set, use 0 for match-only output | | action | edit | replace, insert, delete, or replace-range; default replace unless end_anchor/legacy after imply otherwise | | anchor | edit/batch | Start LN#HASH anchor from latest read | | end_anchor | edit/batch | End LN#HASH anchor for range replace/delete | | content | edit | Replacement/inserted content; delete uses empty stdin | | after | edit | With action:'insert', insert after anchor; omitted means insert before | | edits | batch | JSON array of batch edits using op, anchor, optional end_anchor, and lines |

hledit CLI has a --context option for contextual grep. This wrapper exposes it as context; omit it for the small default (2) or set 0 for match-only output.

Examples

Read anchors:

{ "op": "read", "path": "src/file.ts", "offset": 1, "limit": 80 }

Contextual grep with small default window:

{ "op": "read", "path": "src/file.ts", "grep": "validateToken" }

Match-only grep:

{ "op": "read", "path": "src/file.ts", "grep": "validateToken", "context": 0 }

Replace one line:

{ "op": "edit", "path": "src/file.ts", "action": "replace", "anchor": "12#NKA", "content": "const ok = true;" }

Insert before or after an anchor:

{ "op": "edit", "path": "src/file.ts", "action": "insert", "anchor": "12#NKA", "content": "const added = true;" }
{ "op": "edit", "path": "src/file.ts", "action": "insert", "anchor": "12#NKA", "after": true, "content": "const added = true;" }

Delete a line:

{ "op": "edit", "path": "src/file.ts", "action": "delete", "anchor": "12#NKA" }

Replace a range:

{ "op": "edit", "path": "src/file.ts", "action": "replace-range", "anchor": "12#NKA", "end_anchor": "18#VRC", "content": "new block" }

Batch edits use wrapper-friendly fields. pi-hledit translates them to the CLI-native {"edits":[{"pos":"..."}]} request before spawning hledit batch. Prefer a structured edits array; the legacy JSON string form remains supported during transition.

{
  "op": "batch",
  "path": "src/file.ts",
  "edits": [
    { "op": "replace", "anchor": "12#NKA", "lines": ["const ok = true;"] },
    { "op": "delete", "anchor": "20#ABC", "end_anchor": "22#CDE", "lines": [] },
    { "op": "insert", "anchor": "30#EFG", "lines": ["const added = true;"] }
  ]
}

Legacy string form:

{
  "op": "batch",
  "path": "src/file.ts",
  "edits": "[{\"op\":\"replace\",\"anchor\":\"12#NKA\",\"lines\":[\"const ok = true;\"]}]"
}

Why hash-anchored editing?

Traditional text-matching edits fail silently when the file changes between read and write. Hash anchors detect stale context before any write, and reject stale writes before they happen — the agent gets an error and can re-read, instead of silently patching the wrong line.

Behavior notes

  • Plain {op:'read'} (no offset/limit/grep) is bounded: it defaults to offset:1, limit:2000, same as an explicit ranged read.
  • grep filters which lines are returned but the result is still line-capped by limit, not byte-capped — a match set larger than limit is truncated with a pagination hint from hledit, not silently dropped.
  • action:'delete' sends empty content (empty stdin) to hledit; there is no separate delete-specific field.
  • Batch insert is insert-before only — the current hledit batch CLI has no insert-after field.

Failure modes

  • Stale anchor — the target line changed since the anchor's read. The edit is rejected with an error instead of writing to the wrong line; re-read and retry with a fresh anchor.
  • Malformed batch JSON — the edits string fails to parse; the tool returns an actionable error naming the expected shape rather than spawning hledit.
  • hledit not found — if HLEDIT_BIN/PATH don't resolve to the CLI, the tool returns the install hint (go install github.com/dabito/hledit@latest).

Limitations

  • This wrapper exposes hledit's --context flag as context for grep reads; omit it for default 2, or set 0 for match-only output.
  • Batch edits are applied by a single hledit batch invocation (validate-all-then-write), not by this wrapper independently — atomicity guarantees come from the CLI, not from pi-hledit's own code.
  • No sandboxing beyond what hledit itself does: paths are resolved relative to pi's cwd and passed through to the CLI as-is.

Development

npm test   # typecheck, contract tests, lint

Contract tests live in test/contract.test.ts and cover read-arg building, edit action resolution, batch translation, and the registered tool's rendering.