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

@termless/peekaboo

v0.10.2

Published

Peekaboo backend for termless — OS-level terminal automation and testing via real terminal apps

Readme

@termless/peekaboo

Peekaboo backend for Termless -- OS-level terminal control via real terminal apps.

Install from npm; a git install resolves the TypeScript source and runs only under Bun.

What is this?

A Termless backend that combines:

  1. Data layer: xterm.js headless backend for fast, accurate cell/text data
  2. Visual layer: Real terminal app (Ghostty, iTerm2, etc.) for screenshot-based visual verification

This enables tests to verify both programmatic output (via xterm.js) AND real terminal rendering (via screenshots of actual terminal apps).

Usage

import { createPeekabooBackend } from "@termless/peekaboo"
import { createTerminal } from "@termless/core"

// Data-only mode (no terminal app launched)
const backend = createPeekabooBackend()
const term = createTerminal({ backend })
await term.spawn(["bun", "my-tui-app"])
console.log(term.getText()) // from xterm.js

// Visual mode (launches a real terminal app)
const visualBackend = createPeekabooBackend({
  visual: true,
  app: "ghostty",
})
visualBackend.init({ cols: 80, rows: 24 })
await visualBackend.spawnCommand(["bun", "my-tui-app"])
const png = await visualBackend.takeScreenshot()

Supported terminal apps (macOS)

| App | ID | Notes | | ------------ | ---------- | ------------------------------ | | Ghostty | ghostty | Default. Uses --command flag | | iTerm2 | iterm2 | Uses AppleScript | | Terminal.app | terminal | Uses AppleScript | | WezTerm | wezterm | Uses wezterm start | | kitty | kitty | Uses open -a |

Architecture

PTY (Bun.spawn with terminal)
  |
  +--> xterm.js backend (headless) --> getText(), getCell(), getCursor(), ...
  |
  +--> Real terminal app (optional) --> takeScreenshot() --> PNG buffer

Known limitation: dual-process divergence

In visual mode, spawnCommand() starts two independent processes running the same command:

  1. A real terminal app process (for takeScreenshot())
  2. A separate PTY feeding xterm.js (for getText(), getCell(), etc.)

Because these are independent OS processes, their output can diverge at any time due to timing, randomness, or interleaving differences. This means:

  • getText() content may not match what appears in the screenshot
  • Use either data methods or screenshots for assertions, not both together
  • Visual mode is best for human-in-the-loop verification, not automated cross-referencing
  • Data-only mode (visual: false) uses a single PTY and is fully consistent

compat-screenshot — real desktop-terminal capture

The peekaboo backend's takeScreenshot() captures a window for a backend session. compatScreenshot() is the standalone orchestration tool for the common "what does my TUI look like in my actual terminal?" question — it owns the full launch → run → capture → cleanup cycle.

import { compatScreenshot } from "@termless/peekaboo"

const result = await compatScreenshot({
  cmd: "bun km view ~/Vault",
  terminal: "ghostty", // | "kitty" | "iterm" | "terminal" — auto-detected if omitted
  cols: 140,
  rows: 40,
  waitFor: "ms", // text to wait for before the screenshot
  outputPath: "/tmp/compat.png",
})
// → { path, mimeType: "image/png", terminal: { name, version, font, theme, resized } }

CLI form:

termless compat-screenshot --terminal ghostty --cols 140 --rows 40 \
  --wait-for ms -o /tmp/compat.png -- bun km view ~/Vault

MCP form: the compat-screenshot tool of termless mcp.

How it works (8 steps)

  1. Resolve the target terminal app — auto-detect order is ghostty > kitty > iterm > terminal.
  2. Generate a .command wrapper script that cds into the working dir and runs the command.
  3. Spawn the terminal app pointed at the script (open -na <App> …).
  4. Request the window size — kitty and Ghostty honor a cell-count override; iTerm and Terminal.app are resized best-effort via AppleScript afterward.
  5. Wait for first paint — poll the window's Accessibility text until waitFor appears (or any text).
  6. screencapture the window region.
  7. Close the spawned window (unless keep: true).
  8. Return the PNG path plus a metadata object naming the terminal version / font / theme that was captured.

The wrapper script ends with exec /bin/bash --login so the window stays open after the TUI command exits — without that keep-alive the window can close before the screenshot lands.

When to use this vs the canvas renderer

compat-screenshot is slow, macOS-only, and pops a real window. Use it only for terminal-specific compat questions ("does this look right in Ghostty 1.3 with my Tokyo Night theme?"). For routine visual iteration, regression snapshots, and any CI/headless context, use the canvas renderer (Terminal.screenshot()) — it is fast, cross-platform, and Chromium-free.

Failure modes

compatScreenshot() (and assertCompatCapable()) fail with a clear, actionable message when:

  • Not macOS — Linux/Windows have no screencapture; a sibling Linux path (grim / gnome-screenshot) is tracked separately.
  • No GUI session — e.g. SSH'd in without a console session; no window server to capture.
  • No Screen Recording permission — grant it under System Settings → Privacy & Security → Screen Recording.

Window cleanup

Every window peekaboo opens is registered with a process-wide tracker and a single SIGINT/SIGTERM/exit handler. When your script:

  • Calls backend.destroy() — the window closes immediately via an AppleScript id-targeted close (survives the user activating another window between launch and close).
  • Crashes or is Ctrl-C'd — the exit handler synchronously closes every still-tracked window.
  • Forgets to call destroy — same as above; the exit handler is the safety net.

For iTerm2 and Terminal.app, peekaboo captures the new window's AppleScript id at launch time, so it can target that specific window for close. For Ghostty / WezTerm / kitty (which spawn via open -a and don't expose a window id), peekaboo falls back to close front window — usually fine because there's typically only one window remaining at exit, but if you have multiple peekaboo windows from those apps open simultaneously, the wrong one could close. Use iTerm2 or Terminal.app if you need precise multi-window cleanup.

If you find leaked terminal windows after a peekaboo run, that's a bug — file it on the termless repo with the app, script that reproduced it, and what kind of exit (Ctrl-C / crash / clean shutdown).

Requirements

  • macOS (uses screencapture and AppleScript for window management)
  • Bun runtime (for PTY support)
  • A terminal app installed (if using visual mode)