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-papercuts

v0.4.0

Published

Agent complaint box for Pi/OMP: file friction into an append-only .papercuts.jsonl, then keep working.

Readme

pi-papercuts

npm license node pi-package

Agent files one-line friction notes into .papercuts.jsonl and keeps working. Review the backlog later.

Port of treygoff24/papercuts (MIT) for Pi and OMP. Pure Node · Node 22+.

pi install npm:pi-papercuts
omp install npm:pi-papercuts
# from a checkout:
pi install ./packages/pi-papercuts
omp install ./packages/pi-papercuts

Use

papercuts({ action: "add", text: "what broke + what would have prevented it", tags: ["tooling"], severity: "major" })
papercuts({ action: "list" })
papercuts({ action: "list", format: "md" })
papercuts({ action: "resolve", ids: ["pc_9f2c"], note: "fixed" })
papercuts({ action: "doctor" })
papercuts({ action: "schema" })
papercuts({ action: "prune" })

| Action | What it does | |--------|----------------| | add | File a cut (text required). Optional tags, severity. Evidence is free-note or cmd/exit/stderr, not both. Alias log → add. | | list | Open cuts by default. Filters: status, agent, tag, severity, limit, format (json | md). | | resolve | Mark ids resolved (pc_ + ≥4 hex). Append-only. | | prune | Archive resolved cuts; keep the working log lean. | | doctor | Validate the log. | | schema | Machine contract for agents. |

Severity: minor (default), major, blocker. New IDs hash a JSON tuple of timestamp, agent, text, severity and the sorted tag array. They retain the pc_ + 12-hex format; old logged IDs remain readable/resolvable but are not regenerated by the new encoding. Distinct tag boundaries and lone surrogates are preserved. Hash collisions are still possible with 48-bit IDs.

Dedupe applies to an identical ID in the working log, including the timestamp. Evidence is not part of identity. A later timestamp is a new cut, and pruning forgets working-log dedupe for archived cuts. Duplicate receipts return the first stored record, including its original evidence and tag order, never unsaved replacement fields. Unknown legacy severity values sort after the three known severity levels without dropping their records.

Interactive hosts show a compact themed call/result row -- expand for full text, tags, and log path. Pi 0.99 programmatic callers receive the same versioned envelope in structuredContent (validated by outputSchema); UI details and model-facing text stay intact. Rejected usage, busy and I/O operations set native isError, so nested execution does not report them as successful writes. The combined tool includes mutations and does not claim to be read-only. Markdown lists retain this programmatic envelope too. The required action and action-specific field types are checked before storage; known null placeholders are absent, but unknown keys are rejected even when null. TUI previews strip terminal controls and do not split valid surrogate pairs.


Layout

index.js binds the host signature and error boundary. lib/contract.js owns wire schemas/envelopes, lib/params.js narrows action-specific arguments, lib/actions.js implements validated actions, and lib/render.js owns compact/expanded TUI output. lib/worker-client.js manages lazy background execution and graceful shutdown; lib/worker.js runs the unchanged synchronous actions serially off the UI thread. lib/store.js remains the sole persistence boundary, including canonical-path locks, append durability, tear healing, and archive-before-rewrite pruning.

Rendering

Renderers reuse their own previous Text component when freshly formatted text is unchanged, preserving Pi's width/layout cache without caching theme colors. Pi's self-rendering API also preserves the surrounding Box cache: padding, pending/success/error backgrounds and click expansion match the default shell. Each transcript row retains at most two frames, one per expansion state, so collapsing and reopening a result preserves both layouts. If the call renderer fails, its detached frame cannot swallow the host-visible error result. Failed result rendering clears the old receipt before the host shows its fallback. Changed arguments, nested result values, expansion, theme invalidation and resize remain visible. Common-width resolve batches build one ordered prefix index per call; longer legacy IDs still participate in ambiguity checks. The temporary index retains only requested-prefix hits rather than unrelated cuts. Mixed-width requests retain prefix scanning.

There is no persistent log cache: cooperating writes keep the full snapshot-to- append lock, reads still observe current file contents, and acknowledgements still require fsync. Each extension registration owns one lazy worker, so independent sessions cannot shut down each other's execution. schema and input validation stay local. The first storage call pays worker startup latency without blocking on its I/O. Results are copied back to the host, so unbounded result payloads and cold layout of very large expanded lists can still block a frame.

The worker is unreferenced when idle and drains accepted work on session_shutdown. Already-aborted calls are rejected before dispatch; after dispatch, cancellation waits for the transaction's receipt rather than interrupting a durable write. Worker failures reject pending calls without replay: a write may have committed before a lost receipt, so inspect the log before retrying. Later calls can restart the worker. Per-request PAPERCUTS_FILE, PAPERCUTS_AGENT and PAPERCUTS_NOW overrides are snapshotted before dispatch, along with the execution directory.

Storage

  1. file param
  2. PAPERCUTS_FILE
  3. nearest .git → <root>/.papercuts.jsonl (exact basename only; foo.papercuts.jsonl is not the repo log)
  4. else ~/.papercuts/log.jsonl
echo .papercuts.jsonl >> .gitignore

Relative file and PAPERCUTS_FILE overrides resolve against the active execution directory, not the host process directory. Reads reject non-regular files without waiting for a FIFO writer. Missing files remain empty backlogs. Pruning retains the working log's basic permission bits even under a tighter umask; ownership, ACLs, and extended attributes are not preserved by that replacement.

Optional: PAPERCUTS_AGENT, PAPERCUTS_NOW (tests).

With pi-deferred-context-engine, pin papercuts if you want it always active.

Tests

npm test --prefix packages/pi-papercuts

Hermetic Node 22 tests under tests/. Local bench tests/performance.mjs is not shipped.

No-claim boundaries

  • Does not auto-detect failures -- the agent must call it (habit / AGENTS.md).
  • Not a secret store. Size caps only; resolve does not erase cut text.
  • Outside git, log goes under home unless PAPERCUTS_FILE is set.
  • Log paths must be normal files, without hard-link aliases. Existing symlink targets are resolved before locking; dangling log symlinks are refused.
  • All writers/pruners must run this version's locking protocol. Older versions or external appenders are not coordinated. Stop them before pruning with this version.
  • Canonical log/archive paths ending in .lock (case-insensitive) are reserved and refused, including symlinks into that namespace. This prevents event data from being deleted as lock cleanup.
  • Writes acquire exclusive <canonical-log>.lock files; add/resolve hold the lock from snapshot through append, and prune also locks its archive. Contention returns a retryable busy error without acknowledging a write. Locks contain a PID/time and are not stolen automatically: after a crash, confirm the writer has stopped before manually removing a stale lock.
  • Prune archives before replacing the working log. A crash can leave duplicate archive events or a temp file; duplicate events are harmless to fold. File contents are flushed, but directory durability after power loss and network filesystems are not guaranteed.
  • Appends separate an unterminated tail from the new event; malformed old lines remain on disk until explicit pruning.
  • Invalid UTF-8 (strict per-record decode, not replacement characters), invalid/missing IDs, malformed tag arrays, and wrong-typed ts/agent or cut text/severity fields are torn. These text fields must be strings when present; sparse legacy records, unknown severity strings, and opaque extra metadata remain supported. doctor flags torn records; list/resolve skip them without rewriting those lines. Explicit prune removes them.
  • Text and tag byte caps keep the original UTF-16 slice, including lone surrogates. A Buffer round-trip would replace those code units and change content-addressed IDs.
  • Open-cut list order is severity, then RFC3339 timestamp (not lexical), then remaining text. Truncated list leftovers are total - shown.length.

More: residual risks.

License

MIT · AdityaVG13/pi-stack