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

vocalcode

v0.1.1

Published

OpenCode plugin that speaks assistant responses aloud using Piper TTS

Readme

vocalcode

OpenCode plugin that speaks assistant responses aloud using Piper TTS.

What It Does

  • Narrates assistant replies sentence-by-sentence as they stream (markdown and code filtered out) — on by default, toggleable at runtime and persisted across sessions
  • Subagent threads are never narrated; their synthetic prompts also don't interrupt main-thread narration
  • User input takes over audio: sending a message, running /stop, or turning narration off interrupts current playback and drops the queued sentences
  • Runs one shared Piper HTTP server for all opencode sessions: the first session spawns it, the last session to exit kills it (ownership lock + heartbeat refcount, kills verified against the spawned child PID only), and a session that resumes after suspend respawns it automatically if it died
  • Caches synthesized audio (sha256 of voice + text + settings) so repeated lines replay instantly without re-synthesis; oldest clips are evicted over the size cap, and the shared cache + replay index are wiped only when the first active opencode session starts (TTS_CLEAR_CACHE)
  • /speak command and a speak tool let the model talk on demand (interrupts current narration)
  • /voice command plus voice_list / voice_set tools: pick a voice; missing voices are only downloaded after the user explicitly confirms
  • /tts command plus tts_toggle / tts_status / tts_download tools: toggle narration, check status, install voices
  • PIPER_MANAGED=false disables spawning — connect to an already-running server only

Commands & Tools

| Command / Tool | Purpose | |---|---| | /tts on\|off\|status\|setup | Toggle auto-narration, report status, or run the voice setup flow | | tts_toggle | Enable/disable/toggle narration (persists in state.json); disabling also stops current playback | | tts_status | Narration state, voice, server health, installed voices | | tts_download | Download a voice (~60 MB) — requests OpenCode permission before running the download command; a stalled download is killed after 10 minutes | | /speak, speak | Speak text on demand; still works when auto-narration is off | | /stop, stop_speaking | Stop audio playback and drop queued sentences; the rest of the current reply is not narrated | | /replay, replay_speech | Replay recent narrated sentences from cache (default: last sentence; interrupts current playback) | | /voice, voice_list, voice_set | Inspect and switch voices |

Voice Setup

If the configured voice is missing at startup, Vocalcode does not download anything on its own: it shows a toast telling you to run /tts setup. That flow asks you to confirm the download (one-time, ~60 MB) before installing the voice and starting the server.

Prerequisites

  • Bun runtime
  • Piper TTS installed (pip install piper-tts[http])

Voices are downloaded into the shared voices dir only after OpenCode permission is granted; the default (en_US-ljspeech-medium) is not fetched automatically.

Voice Licensing

Piper voice licenses vary per model — some are trained on non-commercial or research-only datasets. Vocalcode never redistributes voice models; each model's terms live in its MODEL_CARD on rhasspy/piper-voices. See docs/VOICES.md for the list of English voices with clean licensing (public domain / CC BY / CC0 / Apache-2.0 datasets and derivation chains), which includes the default.

Installation

Add to your opencode.json:

{
  "plugin": ["vocalcode"]
}

Configuration

All settings are environment variables. Values are validated at startup — malformed settings (bad URL protocol, non-positive numbers, unsupported voice names, non-http URL with PIPER_MANAGED=true) fail with a ConfigError instead of being silently ignored:

| Variable | Default | Description | |---|---|---| | OPENCODE_TTS_ENABLED | true | Enable/disable TTS | | PIPER_URL | http://127.0.0.1:5000 | Piper HTTP server URL | | PIPER_VOICE | en_US-ljspeech-medium | Voice model to use | | PIPER_MANAGED | true | Spawn a managed server when none is reachable | | PIPER_COMMAND | python -m piper.http_server | Server spawn command | | PIPER_DOWNLOAD_COMMAND | python -m piper.download_voices | Voice download command | | PIPER_DATA_DIR | <data>/voices | Voice model directory (--data-dir) | | PIPER_LENGTH_SCALE | 1 | Speech rate (higher = slower) | | PIPER_NOISE_SCALE | 0.667 | Phoneme noise | | PIPER_NOISE_W_SCALE | 0.8 | Phoneme width noise | | PIPER_TIMEOUT_MS | 30000 | Request timeout in ms | | PIPER_READY_TIMEOUT_MS | 30000 | Server startup wait in ms | | TTS_MAX_CHUNK_LENGTH | 500 | Max characters per synthesis | | TTS_MAX_QUEUE_SIZE | 20 | Max queued sentences | | TTS_CACHE_MAX_BYTES | 209715200 | Audio cache size cap (200 MB) | | TTS_CLEAR_CACHE | true | Clear shared cached audio + replay index when the first active opencode session starts | | VOCALCODE_DATA | %LOCALAPPDATA%\vocalcode | Shared data dir (lock, heartbeats, cache, voices, state) | | TTS_LOG_LEVEL | info | Log level: debug, info, warn, error. Set debug to see per-clip playback/cache diagnostics and streaming dedup decisions (e.g. why audio is silent or re-narrated) |

The chosen voice and the auto-narration toggle persist across sessions in <data>/state.json.

How It Works

Assistant streaming response
  -> message.part.delta / message.part.updated events
  -> Extract only newly generated text (no duplicates)
  -> Filter Markdown / code blocks (stateful across chunk boundaries)
  -> Buffer into sentences
  -> Queue (Effect fiber + bounded dropping queue; interrupt = shutdown)
  -> Cache lookup (sha256 key) -> Piper HTTP /synthesize on miss (one delayed retry)
  -> Play WAV (ffplay / afplay / aplay / PowerShell SoundPlayer)

Architecture

Built on Effect TS: tagged errors (Data.TaggedError), a bounded dropping Queue with a single worker fiber, Schedule-driven readiness polling and synthesis retries, semaphores for operation serialization, and daemon fibers for the server heartbeat.

| File | Responsibility | |---|---| | src/index.ts | OpenCode plugin entry: events, tools, /speak + /stop + /replay + /voice + /tts commands | | src/piper.ts | Piper HTTP client (health, voices, synthesize) | | src/server.ts | Singleton server supervisor: ownership lock, child PID, heartbeats, health-gated last-out kill | | src/cache.ts | WAV cache, newest-wins eviction, replay index | | src/speech.ts | Sentence pipeline: queue, synthesis, playback, interrupt, replay | | src/audio.ts | Cross-platform WAV playback | | src/text.ts | Markdown filtering, sentence segmentation | | src/config.ts | Environment config + persisted voice state | | src/command.ts | Quote-aware command-line parser for spawn/download commands | | src/errors.ts | Tagged error types |

Observability

The narration pipeline and server supervisor emit Effect spans and incremental counters, ready for any OpenTelemetry backend:

  • Spans: server.ensure, speech.synthesize, speech.play
  • Counters: vocalcode_server_spawns, vocalcode_clips_synthesized, vocalcode_clips_played

Development

# Typecheck
bunx tsc --noEmit

# Run tests
bun test

The test suite covers command parsing, markdown/stream filtering, sentence segmentation, config validation, the Piper HTTP protocol, cache behavior (eviction, corruption, atomic writes), the speech pipeline (ordering, interrupts, per-session cancellation, retries), and server supervision (lock ownership, child-PID teardown, readiness-timeout recovery with verified child kill).

Platform Support

  • Windows: ffplay if installed, else System.Media.SoundPlayer via PowerShell
  • macOS: afplay
  • Linux: ffplay, aplay, or paplay (auto-detected)

License

Vocalcode is MIT-licensed. It does not vendor or redistribute Piper (GPL-3.0): Piper is installed separately (pip install piper-tts[http]) and runs as its own process, which the plugin spawns and talks to over HTTP. Voice models are likewise downloaded at runtime, never redistributed — each carries its own terms (see Voice Licensing).