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

opencode-auto-nudge

v1.0.1

Published

Keeps OpenCode agent sessions alive: auto-retries rate-limited, timed-out, overloaded or empty model turns with capped backoff, then gives up loudly instead of silently stalling. Plays distinct sounds for task completed, failure, and human-decision-needed

Readme

opencode-auto-nudge

An OpenCode plugin that keeps agent sessions alive: it retries failed or empty model turns automatically, gives up loudly instead of silently, and plays distinct sounds when your attention is actually needed — so you stop typing continue every few minutes.

The problem

During long agentic runs, OpenCode sessions can stall in ways that are easy to miss:

  • Provider hiccups (429 rate limits, 5xx, timeouts) exhaust OpenCode's built-in retries and the turn just ends.
  • Empty responses — the model returns a turn with zero content (only internal step markers), the loop exits, and nothing tells you.
  • Permission prompts sit unanswered while you're in another window.

opencode-auto-nudge watches session events and closes each of these gaps: transient failures get retried with backoff, empty turns get a "continue exactly where you left off" nudge, and every terminal outcome is surfaced via toast, log line, and sound.

What it does

| Situation | Behavior | |---|---| | Retryable provider error (429, 5xx, timeout, overloaded, network…) | Re-prompts the same session after exponential backoff (3s → 30s), up to MAX_ATTEMPTS | | Turn completed but produced no output | Watchdog detects it on session.idle and injects a continuation prompt within seconds | | Retries exhausted | Explicit "giving up" warn log + error toast + error sound. Never silently stops | | Permission / question waiting for you | Attention ping, repeated on an interval until you answer | | Task finished legitimately | Short ascending chime (long turns only) | | You aborted (esc) or typed a message | Nothing is auto-continued; counters reset |

Sound signals

Sounds are synthesized at startup into ~/.cache/opencode/sounds/ — no asset files needed and no dependency on any specific sound driver or device.

Playback is a fallback chain, not a single player: every detected player among paplay (PulseAudio), pw-play (PipeWire), ffplay, afplay (macOS), aplay (ALSA) is tried in order until one exits successfully. Player detection uses Bun's native which inside OpenCode (no shell quirks) with a portable fallback elsewhere. If no player exists at all, the plugin degrades silently to toasts/logs — or, with AUTO_SOUND_BEL=1, emits a terminal bell (\a) as a last resort (terminal-dependent; many terminals mute it by default).

Each turn-end writes a breadcrumb to ~/.cache/opencode/sounds/debug.json recording the classification decision (success, skip-short-turn, nudged, …) plus the last playback attempt result — so "why no sound?" is always answerable from one file.

| File | Pattern | Meaning | |---|---|---| | success.wav | two ascending tones (880→1318 Hz) | turn completed with real output | | error.wav | descending low buzz (330→196 Hz) | turn died in a non-abort error, or retries were exhausted | | attention.wav | urgent double high ping (1568 Hz) | permission/question needs your decision; re-pings every AUTO_SOUND_REMIND_MS up to AUTO_SOUND_REMIND_MAX times |

Deliberate silences so this doesn't get annoying:

  • Turns shorter than AUTO_SOUND_SHORT_TURN_MS (you're actively chatting anyway)
  • Aborts (esc) — you did that on purpose
  • Subagent (child) sessions — no false "done!" while the parent keeps working
  • Intermediate retry attempts — only outcomes beep
  • Headless environments without an audio player — falls back to toasts/logs only

Installation

Requires OpenCode. Restart OpenCode after any install method — plugins load once at startup.

Option A — npm package (recommended):

// ~/.config/opencode/opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["opencode-auto-nudge"]
}

No sound files to download or install — the three tones are synthesized programmatically on your machine at every startup (pure JS, ~47 KB of PCM). Nothing ships as binary assets, nothing depends on a particular sound driver.

Option B — global drop-in (no npm):

curl -o ~/.config/opencode/plugins/auto-nudge.ts \
  https://raw.githubusercontent.com/ay8-dev/opencode-auto-nudge/main/index.ts

Option C — per-project drop-in:

cp index.ts .opencode/plugins/auto-nudge.ts

Configuration

All knobs are environment variables (set them where you launch opencode). Defaults are tuned for unattended runs; everything works out of the box.

Retry ladder

| Variable | Default | Description | |---|---|---| | AUTO_NUDGE_MAX_ATTEMPTS | 5 | Max nudges per failure episode | | AUTO_NUDGE_EMPTY_DELAY_MS | 1500 | Delay before re-prompting an empty response | | AUTO_NUDGE_ERROR_BASE_MS | 3000 | Base delay for error backoff (doubles per attempt) | | AUTO_NUDGE_ERROR_MAX_MS | 30000 | Backoff ceiling | | AUTO_NUDGE_MIN_GAP_MS | 1200 | Collapse duplicate triggers for the same failure | | AUTO_NUDGE_BUSY_RECHECK_MS | 2000 | Wait before re-checking if the session was busy at fire time | | AUTO_NUDGE_BUSY_RECHECK_MAX | 10 | Max busy-rechecks before dropping a retry (logged loudly) | | AUTO_NUDGE_TTL_MS | 300000 | Failure counter expires after this much quiet time |

Sounds

| Variable | Default | Description | |---|---|---| | AUTO_SOUND_DISABLE | unset (1 disables) | Turn all sounds off; toasts/logs remain | | AUTO_SOUND_DIR | ~/.cache/opencode/sounds | Where generated wavs live | | AUTO_SOUND_SHORT_TURN_MS | 4000 | Turns faster than this don't play success (interactive chat) | | AUTO_SOUND_REMIND_MS | 20000 | Interval for permission reminders | | AUTO_SOUND_REMIND_MAX | 2 | Extra reminder pings per permission ask | | AUTO_SOUND_GAIN | 100 | Playback gain in percent (20–400) | | AUTO_SOUND_BEL | 0 | Emit terminal bell as last-resort fallback when no player works |

Safety design

This plugin is built to never make things worse:

  • Hard caps everywhere — attempt caps, TTL expiry, min-gap deduplication, and bounded rescheduling mean no runaway loops, even if a provider returns instant failures forever.
  • Aborts are sacred — turns ended by user abort are never auto-continued.
  • User intervention wins — typing any message resets the failure counters.
  • Failed prompt calls refund their attempt — a rejected nudge doesn't burn budget.
  • Errors attached to a turn (auth failures etc.) are not retried — nudging can't fix a bad API key.
  • Every action is logged under the [auto-nudge] tag with service name auto-nudge.

Observability

grep "\[auto-nudge\]" ~/.local/share/opencode/log/opencode.log

Expect lines like:

[auto-nudge] session ses_xxx retry 2/5 in 3000ms (empty response)
[auto-nudge] session ses_xxx recovered after 2 nudge(s)
[auto-nudge] giving up on session ses_xxx after 5 attempts (empty response)
[auto-nudge] permission ask detected (per_xxx)
[auto-nudge] sounds on via pw-play

Toasts appear in the TUI: warning variants for retries, error variant on give-up.

Known limitations

  • Same-model retries only. If a provider is down for good, the ladder burns its attempts and reports loudly. It intentionally does not switch models — pair it with a model-failover plugin if you want that layer (mind the overlap: both react to the same events).
  • Heuristic empty detection. A turn counts as empty when the last assistant message has no text/tool/reasoning parts. Exotic part types could be misclassified.
  • Coupled to OpenCode event shapes — session.error, session.idle, session.status, message.updated, permission.asked/replied, question.asked/replied/rejected — verified against OpenCode source and live logs (Aug 2026). Both legacy bus names and internal .v2. schema names are handled for permissions and questions. Note: the plugin-facing event bus delivers the non-prefixed names; permission.v2.* / question.v2.* exist only in core internals. Verified against OpenCode 1.18.x; future releases may change shapes and require plugin updates. The init.json marker written into AUTO_SOUND_DIR at startup ({"player": "...", "at": "..."}) doubles as a load-health check: no file = plugin didn't initialize, "player": null = no audio player found.
  • Permission reminder depends on permission.asked/permission.replied events matching expected id fields; if those change, pings may not repeat (the initial ping comes from the same handler).
  • Subagent sessions are nudged but silent. A parent that already given up on a child can see up to MAX_ATTEMPTS late child-resurrections — usually desirable, occasionally surprising.
  • Tested on Linux (Wayland/PipeWire) with Node/Bun. macOS/Windows player probing includes afplay/ffplay paths but is not battle-tested.

Troubleshooting

| Symptom | Check | |---|---| | No sounds at all | Read ~/.cache/opencode/sounds/init.json: "players": [] means no player binary was found on PATH. Test manually: pw-play ~/.cache/opencode/sounds/success.wav | | Chime missing on some turns | Read debug.json: the last turn's action field explains the decision (skip-short-turn = under threshold, skip-child = subagent, etc.) | | No sound on a permission prompt but logs show permission ask detected | Audio server issue, not the plugin | | Nudges seem absent | Is there any [auto-nudge] line at all? None = plugin didn't load — restart OpenCode | | Gives up too early/late | Tune AUTO_NUDGE_MAX_ATTEMPTS and backoff vars | | Want silence | AUTO_SOUND_DISABLE=1 |

Development

npm install
npm test        # builds dist/ then runs the offline test suite (no OpenCode instance needed)

The test suite mocks the OpenCode SDK client and shell, so it verifies decision logic end-to-end — retry chains, dedup, caps, re-arm, sound routing — without touching a running instance.

License

MIT