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-smart-timeout

v0.1.2

Published

Content-aware shell command timeouts for Pi: caps every bash/powershell call so a hung command cannot stall the agent, while long-running builds keep the room they need.

Readme

pi-smart-timeout

CI npm license

Content-aware shell command timeouts for Pi.

Pi's bash tool has no default timeouttimeout is an optional parameter the model has to remember to pass, and it usually doesn't. When a command hangs (a bare REPL, tail -f, a stalled network call, an interactive prompt), nothing kills it and the agent sits there until you interrupt by hand.

This extension caps every bash and powershell call so Pi's own process-tree kill fires and the tool returns Command timed out after N seconds. The model sees the error and recovers on its own.

Install

pi install npm:pi-smart-timeout

Or load it for a single run:

pi -e npm:pi-smart-timeout

Why "smart"

A single blunt number has an obvious failure mode: low enough to catch hangs and it kills your cargo build; high enough to let builds finish and it doesn't catch hangs. So the cap is chosen per command.

Decision order — first match wins:

| # | Condition | Applied cap | |---|-----------|-------------| | 1 | mode is off | nothing injected | | 2 | the model supplied timeout | used as-is, clamped to maxSeconds | | 3 | the command contains timeout N ... | min(N + graceSeconds, maxSeconds) | | 4 | the command looks long-running | longSeconds (default 1800) | | 5 | anything else | defaultSeconds (default 120) |

Hangs are caught, and a 20-minute build is not.

How this differs from the other timeout extensions

Three Pi timeout packages exist, and the difference is what they do with the command text. @jamiefutch/pi-timeout sorts commands into safe / run / unknown and never caps safe — but its safe set includes tail -f and npm install, which is exactly where hangs live:

| Command | Safe/never-capped classifier | pi-smart-timeout | |---------|------------------------------|------------------| | tail -f app.log | safe → never capped | 1800s → killed | | npm install | safe → never capped | 1800s | | git status | safe → never capped | 120s | | grep -rn 'make sure' src/ | — | 120s (heuristics ignore quoted text and English words) |

@rukaachan/pi-timeout guards a different failure mode: a model passing 60000 when it meant 60 seconds. It clamps that number but applies the same cap to every command. pi-extend-timeout changes the timeout of a command already running, which is a recovery tool rather than a preventive one.

This package keeps two buckets so a low cap catches hangs without killing builds, and treats tail -f-style blocking commands as the long-running case they are.

Case 3 in detail

If the command already guards itself, the outer cap gets out of the way so the inner guard fires first and you get a precise error instead of an ambiguous kill:

timeout 2m cargo build     ->  150   (120 + 30 grace)
timeout -s KILL 90 job     ->  120
/usr/bin/timeout 45 job    ->   75
timeout 1h backup.sh       -> 3600   (3630 clamped to maxSeconds)
echo "timeout 5"           ->  120   (quoted, not a guard)
sh -c 'echo "a"; timeout 7 x' -> 37   (quote state resets per segment)
timeout 0 npm install      -> 1800   (coreutils: `timeout 0` means no limit)
timeout 99999 cat          -> 3600   (huge inner guard clamped to maxSeconds)

Option values are tokenized properly rather than matched with one regex, so -s KILL and --kill-after=5s are not misread as the duration. The inner guard is clamped to maxSeconds like every other path: when N exceeds the ceiling the inner guard could not fire before it anyway, so clamping never races the guard.

It also tells the model

The policy is appended to the system prompt each turn, so instead of being silently killed the model knows to pass timeout whenever a command might outlast the default cap, and knows not to re-run a timed-out command unchanged. The long bucket is framed as what commands known to run long receive, not as a blanket allowance, so the model asks for more time explicitly rather than relying on a promoted default. This turns a hard kill into a recoverable error.

Configuration

Settings are merged over built-in defaults. Project settings only apply to trusted projects.

~/.pi/agent/settings.json (global) or <cwd>/.pi/settings.json (project):

{
  "smartTimeout": {
    "mode": "long",
    "defaultSeconds": 120,
    "longSeconds": 1800,
    "maxSeconds": 3600,
    "graceSeconds": 30,
    "longPatterns": ["\\bmy-slow-tool\\b", "scripts/nightly\\.sh"]
  }
}

| Key | Default | Meaning | |-----|---------|---------| | mode | "long" | "long", "short" (cap everything at defaultSeconds), or "off" (never inject) | | defaultSeconds | 120 | Cap for ordinary commands | | longSeconds | 1800 | Cap for long-running commands | | maxSeconds | 3600 | Hard ceiling applied even to model-supplied values. 0 disables it | | graceSeconds | 30 | Headroom added on top of a detected inner timeout N | | longPatterns | [] | Extra regexes (matched against the raw command) marking it long-running |

Environment variables work too and sit below settings files: PI_BASH_TIMEOUT_SEC, PI_BASH_TIMEOUT_LONG_SEC, PI_BASH_TIMEOUT_MAX_SEC, PI_BASH_TIMEOUT_MODE, PI_BASH_TIMEOUT_LOG (append one line per decision — useful for verifying what was applied).

Invalid values are rejected with a warning and fall back to defaults; a malformed settings.json never breaks startup.

Commands

/bash-timeout                   show current configuration
/bash-timeout off               disable capping for this session
/bash-timeout short             cap everything at defaultSeconds
/bash-timeout long              restore content-aware capping
/bash-timeout reload            re-read settings.json without restarting

What it does not do

  • It can't make Pi have a default timeout. Pi's bash schema is timeout?: number with no default; only injecting the field changes behavior.
  • It won't kill deliberately detached work. nohup x & leaves a process outside the killed tree. That is usually what you want.
  • It doesn't hang-proof interactive network tools. Bare ssh and ssh-keygen and curl/wget stay in the default bucket: they hang on prompts and unreachable hosts far more often than they legitimately run long, so a low cap catches the hang. Pass an explicit timeout when you really do need a long session or a big download.
  • It doesn't promote while true loops. A while true; do ...; done loop is a hang by definition and gets the default cap. Only bounded poll loops (until ping -c1 host; do sleep 10; done) get the long bucket.
  • It doesn't stop output flooding. It caps duration, not volume. Pi truncates output on its own (2000 lines / 50KB, full output saved to a temp file).
  • timeout 0 cmd is not treated as a guard, because coreutils reads it as "no timeout" — so the outer cap still applies.

Tests

npm install
npm test          # fast: decision table + config + notifications   (~2s)
npm run test:e2e  # slow: real kill path, spawns and kills processes (~2min)

| Suite | What it covers | |-------|----------------| | tests/logic.test.mts | ~75-case decision table: classification, inner-guard parsing, clamping, mode switching, system-prompt injection | | tests/config.test.mts | settings.json layering, project trust gating, validation, malformed input, /bash-timeout reload | | tests/ui.test.mts | notifications, hasUI: false, malformed tool input | | tests/e2e.test.mts | drives Pi's real createBashTool: verifies 15 blocking scenarios are actually killed, that no orphaned grandchildren survive, and that fast commands and 3s commands under a 6s cap are not killed |

The e2e suite asserts on real process state, not mocks: it reads taskkilled process tables via WMI to confirm the whole tree dies.

License

MIT