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

@trycedar/pi-mdiff

v0.4.0

Published

Markdown-aware edit tools for pi coding agent — normalized SEARCH matching and block-level anchored editing for .md files

Readme

pi-mdiff

Markdown has a granularity problem that code doesn't. In code, a line is a unit of meaning. In markdown, a paragraph is — but it can span 1 line or 10 depending on who last ran the formatter. pi-mdiff fixes this for the pi coding agent.

npm version license pi-package

pi-mdiff demo

The problem

Pi's normal edit tool uses exact text matching. That is brittle for Markdown because prose is semantically paragraph-based, but physically line-wrapped by whatever formatter, editor, or human last touched the file.

A paragraph like this:

The system uses PostgreSQL for all storage.
The schema is defined in schema.sql.
All queries go through the repository layer.

…is identical in meaning to this:

The system uses PostgreSQL for all storage. The schema is defined in schema.sql. All queries go through the repository layer.

A formatter rewraps it. The content is equivalent, but an exact SEARCH block no longer matches the physical lines in the file. The agent sees a tool error even though it identified the right paragraph.

Error: cannot find matching context in docs/architecture.md
<<<<<<< SEARCH
The system uses PostgreSQL for all storage.
The schema is defined in schema.sql.
All queries go through the repository layer.

pi-mdiff fixes the agent workflow around Markdown: normal edit calls get soft-wrap-aware matching, and larger prose changes can use section/block anchors instead of fragile exact text.

Install

pi install npm:@trycedar/pi-mdiff

No config, no API keys.

Try this first

After installing, ask pi naturally:

Update the Architecture section of docs/README.md to mention that we moved to PostgreSQL.
Inspect docs/CONTRIBUTING.md and rewrite the Getting Started section.
Delete the "Legacy Notes" section from docs/api.md.
Add a new "Troubleshooting" section after Installation in README.md.

pi will use md_inspect to find the right section and md_edit to apply the change — no fragile text matching involved.

What this adds

Transparent fix for edit on .md files

Every edit call on a markdown file is intercepted. SEARCH blocks are normalized to paragraph granularity before matching — soft-wrapped lines joined, blank lines collapsed, bullets standardized. Fenced code and frontmatter are never touched.

Before pi-mdiff: formatter reflows paragraph → SEARCH fails → LLM retries → confusion.

After pi-mdiff: normalization runs silently → match found → edit applied → done.

If the built-in edit still cannot match, pi-mdiff attempts normalized soft-wrap recovery and returns success only when it can apply the intended edit.

md_inspect — see section structure before editing

md_inspect path="docs/architecture.md"
## Overview  (path: Overview, lines 1-6)
  [0] paragraph lines 3-4: "This project is a web application that helps…"
## Database Layer  (path: Database Layer, lines 7-13)
  [0] paragraph lines 9-10: "We use PostgreSQL for all persistent storage…"
  [1] paragraph lines 12-13: "Migrations are managed via Alembic…"
## API Layer  (path: API Layer, lines 14-18)
  [0] paragraph lines 16-18: "The REST API is built with Express…"

Shows every section heading, heading path, line range, and block inside it, with 0-based block indices. Call this before md_edit so you know exactly what to target. Heading paths disambiguate repeated section names such as API > Usage and CLI > Usage.

md_edit — section-anchored editing

md_edit path="docs/architecture.md"
        operation="replace"
        section="## Database Layer"
        block_index=0
        content="We use PostgreSQL for all persistent storage.
The schema is defined in schema.sql and managed via Alembic."

Anchors to a heading + block index. No text matching at all. Formatters can reflow the entire file — this still works.

| Operation | What it does | |---|---| | replace | Replace the block at block_index with new content | | insert_after | Insert a new block after block_index | | delete | Remove the block at block_index | | append | Add a new block at the end of the section | | rename_section | Rename the section heading itself | | delete_section | Remove the entire section including its heading | | add_section | Insert a brand-new section after another; content includes the heading line |

md_diff — review Markdown structurally

md_diff before_path="/tmp/architecture.before.md"
        after_path="docs/architecture.md"

Reports changes by heading path and block index instead of raw physical lines:

~ section Database Layer
  ~ [0] paragraph lines 9-10 → 9-11
    - "We use SQLite for local storage…"
    + "We use PostgreSQL for persistent storage…"

Use this when a normal line diff is noisy because prose was reflowed. It helps the agent verify what changed at the Markdown block level.

Common workflows

| Task | How to ask | |---|---| | Update a specific section | "Rewrite the Deployment section of docs/README.md to mention Docker." | | Add content to a section | "Add a note about rate limiting at the end of the Authentication section." | | Add a new section | "Add a Troubleshooting section after Installation in README.md." | | Rename a section | "Rename the 'Legacy API' section to 'Deprecated API' in docs/api.md." | | Delete stale content | "Remove the 'Legacy API' section from docs/api.md." | | Bulk doc update | "Update all references to 'SQLite' to 'PostgreSQL' in docs/architecture.md." | | Inspect before editing | "Show me the structure of CONTRIBUTING.md before we edit it." |

What the normalizer preserves

Only prose lines are joined. Everything structurally meaningful is left exactly as-is:

| Element | Example | Touched? | |---|---|---| | Fenced code blocks | `````` | Never | | YAML frontmatter | ---\ntitle: …\n--- | Never | | Table rows | \| Col A \| Col B \| | Never | | Headings | ## Section Name | Never | | List items | - item, - nested | Never | | Blockquotes | > quoted text | Never | | Horizontal rules | ---, *** | Never | | Explicit line breaks | line ending with | Never | | Inline code | `code` | Never |

Tools

md_inspect

| Parameter | Description | |---|---| | path | Path to the markdown file (.md or .markdown) |

Returns a formatted section map with heading paths, section line ranges, block line ranges, block type, and preview text. Use before md_edit to find the right section and block_index.

md_edit

| Parameter | Description | |---|---| | path | Path to the markdown file (.md or .markdown) | | operation | See table above | | section | Heading text to anchor to — case-insensitive, ## prefix optional. For add_section: the section to insert after (use (end) to append at end of file) | | block_index | 0-based index of the target block (not needed for append, rename_section, delete_section, add_section) | | content | New content — required for replace, insert_after, append, add_section; new heading text for rename_section |

md_diff

| Parameter | Description | |---|---| | before_path | Path to the before/original markdown file (.md or .markdown) | | after_path | Path to the after/modified markdown file (.md or .markdown) |

Returns a Markdown-aware structural diff grouped by heading path and block index. Use it when a normal line diff is dominated by prose reflow.

Note: md_edit, md_inspect, and md_diff support .md and .markdown files only. For .mdx files, use the built-in edit tool (normalization still applies automatically).

How it works

Normalization path (fixes edit calls transparently):

LLM calls edit() on .md file
  → pi-mdiff intercepts tool_call
  → normalizes SEARCH block to paragraph granularity
  → built-in edit runs with normalized text
  → if still fails: normalized findInMarkdown(), writes file, returns success

Block-anchor path (md_edit, most robust):

LLM calls md_edit()
  → parse file into mdast AST
  → find section by heading (case-insensitive)
  → locate nth block node
  → splice at exact character offsets
  → write file

Structural review path (md_diff):

LLM calls md_diff(before, after)
  → parse both files into section/block maps
  → compare heading paths and block indices
  → report section/block changes with line ranges

Eval coverage

npm run eval   # 79 cases, 100% pass rate

| Category | Cases | Covers | |---|---|---| | normalize | 20 | Tables, frontmatter, fences, lists, blockquotes, headings, setext, unicode | | find | 10 | 2-line/3-line reflow, reverse reflow, flowmark vs 80-char, multi-paragraph, no-match | | md_edit | 30 | All 7 operations, blast-radius, frontmatter, section ops, error cases | | reflow | 9 | 3×3 format matrix — all 6 off-diagonal mismatches recover correctly | | edge | 10 | Empty files, preamble, duplicate headings, long paragraphs, inline code |

Next steps

pi-mdiff currently focuses on reliable prose edits and structural review for Markdown. Broader agent/documentation workflows could still benefit from:

  • Formatter-aware writing: detect the file's wrapping style and reflow inserted prose to match it.
  • Structural table and list operations: update a table cell, insert a list item, or move list items without exact text matching.
  • MDX-aware block editing: parse MDX safely and treat JSX nodes as protected structural blocks.
  • Intent-level search: add md_find/md_query for prompts like "find sections that mention auth tokens".
  • Markdown validation: check links, duplicate anchors, malformed tables, and frontmatter after edits.

Development

git clone https://github.com/trycedar0x/pi-mdiff
cd pi-mdiff && npm install
npm test          # 67 unit tests
npm run eval      # 79 scenario evals
npm run typecheck # strict TypeScript check
pi -e ./src/index.ts  # load in pi for manual testing