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

@ian-pascoe/pi-formatter

v0.5.1

Published

Configured automatic post-edit formatting for Pi

Readme

@ian-pascoe/pi-formatter

Configured automatic post-edit formatting for Pi.

Install

pi install npm:@ian-pascoe/pi-formatter
# or
pi install git:github.com/ian-pascoe/pi-extensions

For a local checkout, run pi -e ./packages/pi-formatter/src/index.ts. Install formatter executables separately.

Settings

Pi Formatter reads the formatter key from Pi's global ~/.pi/agent/settings.json and trusted project .pi/settings.json:

{
  "formatter": {
    "timeoutMs": 30000,
    "formatters": {
      "markdownlint-cli2": {
        "command": "markdownlint-cli2",
        "args": ["--fix", "$FILE"],
        "files": {
          "extensions": [".md", ".mdx"],
          "fileNames": ["README"]
        },
        "requireRootMarker": true,
        "rootMarkers": ["package.json", ".git"],
        "environment": {}
      }
    }
  }
}

Each formatter requires a non-empty command and at least one file extension or exact basename. Extensions include the leading period. Matching is case-sensitive. Arguments are passed directly to the executable without a shell. Every $FILE substring in an argument is replaced with the absolute changed-file path.

A formatter using $FILE runs once per matching changed file. A formatter without $FILE runs once per matching workspace root, allowing full-project formatting. rootMarkers are basename glob patterns; the nearest matching ancestor becomes the command working directory and Pi's working directory is the fallback. Set requireRootMarker to true to skip the formatter unless any root marker exists above the changed file; it defaults to false. A required empty rootMarkers list is invalid. Formatters run sequentially in configuration order.

When formatters change a file, a Formatted by line per changed file is appended to the original tool result so the agent knows its view of the file is stale, for example Formatted by ruff-fix, ruff-format: lines 3–13 changed. It names every File Formatter and Workspace Formatter that changed the file, in run order. Line numbers describe the formatted file: the span runs from the first line that differs from the content before any formatter ran to the last one, and it includes unchanged lines between them. When formatting only removed lines, the line says lines removed after line N, lines removed before line 1, or all lines removed. It starts with the file path, relative to Pi's working directory, when the mutation changed more than one file. Nothing is added when the final content equals the original. A formatter that exits non-zero or times out after writing changes is reported too; the Formatted by lines follow any warnings.

Each Formatted by line is followed by a compact unified diff of what the formatters changed, measured against the content before any formatter ran: - lines are the original text, + lines are what is on disk now, up to three unchanged lines ( ) surround each change, and \ No newline at end of file marks a line with no final newline. To edit the formatted file without reading it again, take the and + lines, drop their first character, and use them as oldText. The diffs of all files in one mutation result share a budget of 60 lines and 6,000 bytes, not counting the Formatted by lines. A file whose diff does not fit in what remains, or whose formatter changed more than 100 lines, keeps only its Formatted by line, so a large reformat stays bounded and the agent re-reads the file.

A timeout, spawn error, or non-zero exit appends a warning to the original tool result without changing that result's success state; later formatters still run. The warning ends with a pointer to the package's troubleshooting Skill, except when the formatter's stderr reports a syntax error in the changed file: it matches wording such as SyntaxError, parse error, or Unexpected token, names the file, and does not mention configuration outside file paths. That is an input outcome that Pi LSP's Post-edit Diagnostics report when Pi LSP is installed, not a formatter failure to diagnose. Bad-configuration errors and Workspace Formatter failures keep the pointer.

Declaring a syntax-error signal

That wording heuristic is a guess for formatters other than oxfmt, ruff, and stylua. A File Formatter can instead declare how it reports a syntax error with syntaxErrorPattern, a JavaScript regular expression source string tested against the formatter's stderr. The pattern is case-sensitive and has no flags, so ^ and $ anchor to the whole trimmed stderr, not to each line; match a line start with (?:^|\n). JSON strings need doubled backslashes.

{
  "formatter": {
    "formatters": {
      "oxfmt": {
        "command": "oxfmt",
        "args": ["$FILE"],
        "files": { "extensions": [".ts", ".tsx", ".js", ".json"] },
        "syntaxErrorPattern": "(?:^|\\n) *x "
      },
      "ruff-format": {
        "command": "ruff",
        "args": ["format", "$FILE"],
        "files": { "extensions": [".py", ".pyi"] },
        "syntaxErrorPattern": "(?:^|\\n)error: Failed to parse "
      }
    }
  }
}

oxfmt prints a syntax error as a x <message> diagnostic and reports a bad configuration file as Failed to load configuration file.. ruff prints a syntax error as error: Failed to parse <file>:<line>:<column>: …, possibly after warning: lines, while a bad configuration file appears on a later Cause: Failed to parse … line. Forced colour (FORCE_COLOR) changes stderr: ruff adds ANSI codes and oxfmt prints × instead of x, so these patterns then miss and the pointer stays.

When syntaxErrorPattern is set, it replaces the heuristic for that formatter: a non-zero exit whose stderr matches it is an input outcome without the pointer, and any other non-zero exit keeps the pointer. The stderr need not name the file, and it is not checked for configuration wording, so make the pattern specific enough not to match the formatter's configuration errors. Timeouts and spawn errors always keep the pointer. Without the setting, the heuristic applies unchanged. The setting needs $FILE in args, and an empty or invalid regular expression quarantines the definition with a startup warning. The pattern is never shown to the model.

Global and project timeoutMs values override by scope. A project formatter replaces the complete global definition with the same ID; set it to null to disable it. Invalid definitions and fields are quarantined individually and reported at session startup. An invalid project replacement still shadows the global formatter. Untrusted project settings are ignored.

Supported mutations

Formatting runs after successful native edit and write operations, Codex-style apply_patch results, and applied Pi LSP Workspace Edit Previews. Changed and created files plus rename destinations are formatted; deleted or vanished files are skipped.

Formatting holds Pi's file mutation queue for the mutation's files, the same queue native edit and write use. When a batch of parallel tool calls edits one file, each edit lands before or after formatting, never during it: the first formatting covers every edit that landed before it, and its diff shows only the formatters' changes. Later results of the batch usually find nothing left to format, so only one of them carries the Formatted by line. An edit that arrives while a formatter runs waits for it instead of being overwritten, and a file that a queued mutation deleted or renamed meanwhile is skipped. A formatter that times out or is aborted is killed, and the queue is held until its process tree closes its stderr, for at most two more seconds. Pi's read does not join the queue, so a parallel read can still see the file before it is formatted.

When the Git collection is installed, Pi Formatter loads before Pi LSP so Post-edit Diagnostics observe formatted content. Separately installed extensions depend on Pi's configured extension order.

Security

Trusted formatter settings execute arbitrary local programs with the Pi process's environment and permissions. Review project settings and formatter binaries before trusting a project.