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-tmux-harness

v0.1.0

Published

Pi extension exposing tmux as native tools — drive other TUIs (pi, claude, copilot CLI, lazygit, etc.) for adversarial testing without fragile sleep+grep loops.

Downloads

21

Readme

pi-tmux-harness

npm version License

Tmux session/keystroke/capture exposed as native pi coding agent tools, so any agent in pi can drive other terminal programs (most importantly: another pi instance, claude code, copilot CLI, lazygit, htop, gh) for adversarial testing — without ad-hoc bash → tmux send-keys → sleep → grep chains.

Why

When testing pi extensions, you end up writing this kind of fragile shell:

tmux new-session -d -s pi-test 'pi'
sleep 8                                    # how long is enough?
tmux send-keys -t pi-test '/myextension' Enter
sleep 12                                   # guess again
tmux capture-pane -t pi-test -p | tail -25 | grep "expected output"

Every sleep is a guess. Wrong guesses produce flaky tests. The grep is eyeball-driven assertion.

This extension turns that into:

tmux_new({ command: "pi", cwd: "/path/to/repo" })
tmux_expect({ name, pattern: "\\[Extensions\\]", timeoutMs: 30000 })  // ready signal
tmux_send({ name, text: "/myextension foo" })
tmux_expect({ name, pattern: "expected output regex", timeoutMs: 15000 })
tmux_capture({ name })  // full snapshot for further assertions
tmux_kill({ name })

The killer feature is tmux_expect: poll capture-pane until a regex matches or timeout fires. Eliminates ~90% of the fragile sleep+grep we used to write.

Install

pi install npm:pi-tmux-harness

Then restart pi (or start a new session). The 7 tools below register automatically.

Tools

All sessions go to an isolated tmux socket (-L pi-harness) so they never appear in your normal tmux ls output, can't accidentally be attached to, and kill all is bounded.

| Tool | Purpose | |---|---| | tmux_new | Spawn a detached tmux session running any command (default: shell). Returns the session name. | | tmux_send | Send literal text. Optional enter: true (default) appends Enter. | | tmux_send_key | Send a named special key (Escape, Tab, Up/Down, C-c, C-d, BSpace, etc.) — uses tmux's native key names. | | tmux_capture | Snapshot the pane. Configurable scrollback lines + trim. | | tmux_expect | Poll capture until a regex matches or timeout. Returns {matched, match?, elapsedMs, lastSnapshot}. The fragile-sleep killer. | | tmux_kill | Kill a session. Pass name: "all" for every managed session. | | tmux_list | List sessions managed by this extension (with elapsed time + cwd). |

Hardening

This extension has been through ~10 rounds of multi-model code review. The defenses include:

  • Regex DoS protection in tmux_expect: AST-level rejection of obviously catastrophic patterns (nested quantifiers like (a+)+, (a{1,2})+, etc.) + 32KB input cap on the regex search window
  • Tmux flag injection blocked: strict allowlist /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/ on session names, validated at every tool call. Rejects names starting with - (which would be parsed as tmux flags) and reserved names like all
  • Tmux command-separator blocked: tmux_send_key rejects ;, {, }, and control bytes anywhere in key names (would otherwise escape the send-keys command context)
  • Exact-target syntax: every -t name invocation uses tmux's exact-match form (=name for session targets, =name: for pane targets) to defeat tmux's default prefix matching
  • AbortSignal honored at every poll boundary in tmux_expect
  • Bounded LRU on the managed-session map (hard cap 64; rejects new sessions when at cap)
  • Tracker bookkeeping for prune-on-list when sessions disappear

Per-agent reference (drive these via the harness)

The harness is agent-agnostic — it doesn't care what's in the tmux pane. Put the right binary in tmux_new's command and use the right ready-signal in tmux_expect.

pi (recursive testing)

tmux_new({ command: "pi", cwd: "/path/to/repo", width: 220, height: 50 })
tmux_expect({ name, pattern: "\\[Extensions\\]", timeoutMs: 30000 })  // pi finished startup
tmux_send({ name, text: "/t why does my parser leak memory" })
tmux_expect({ name, pattern: "\\[Triage\\]", timeoutMs: 15000 })
tmux_send_key({ name, keys: ["Escape"] })  // close any modal
tmux_send_key({ name, keys: ["C-d"] })     // quit pi

Claude Code

tmux_new({ command: "claude", cwd: "/path/to/repo" })
tmux_expect({ name, pattern: "\u2502\\s*>\\s*\u2502|Try .*claude", timeoutMs: 30000 })
tmux_send({ name, text: "explain the auth flow" })
tmux_send_key({ name, keys: ["Escape"] })  // interrupt running operation
tmux_send({ name, text: "/exit" })

GitHub Copilot CLI

tmux_new({ command: "copilot", cwd: "/path/to/repo" })
tmux_expect({ name, pattern: "GitHub Copilot|Welcome|Trust this folder|copilot>", timeoutMs: 30000 })
// If "Trust this folder?" appears, accept it:
tmux_send_key({ name, keys: ["Enter"] })
tmux_send({ name, text: "summarize this repo" })
tmux_send_key({ name, keys: ["Escape"] })  // cancel "Thinking..."
tmux_send({ name, text: "/exit" })

Generic shell (sanity check)

tmux_new({ command: "/bin/sh" })
tmux_send({ name, text: "echo READY_$(date +%s)" })
tmux_expect({ name, pattern: "READY_\\d+", timeoutMs: 5000 })

When to use harness vs -p print mode

| Need | Use | |---|---| | One-shot answer, parse stdout | claude -p / copilot -p / pi -p directly. No harness needed. | | Multi-step interactive flow with cancels / modals / slash commands | Harness. | | Drive a TUI you don't control (lazygit, htop, gh, etc.) | Harness. | | Stream structured events to a supervisor | claude --output-format stream-json directly. No harness needed. | | Test that a pi extension renders a specific message at a specific point | Harness with tmux_expect. |

Requirements

  • tmux (≥ 3.2 recommended for full key-modifier support)
  • Pi coding agent

License

MIT