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/monorepo

v0.3.0

Published

Headless terminal testing library — like Playwright, but for terminal apps. Cross-terminal conformance, composable selectors, 21+ Vitest matchers.

Readme

Termless

Headless terminal testing library. Like Playwright, but for terminal apps.

Terminal apps are hard to test because the terminal is a black box — you can see text on screen but can't programmatically inspect colors, cursor position, scrollback history, terminal modes, or cell attributes. Termless opens up the entire terminal buffer for structured testing, and runs the same tests against multiple terminal emulators to catch cross-terminal compatibility issues.

Built alongside silvery, a React TUI framework, but works with any terminal app.

  • Full terminal internals -- access scrollback, cursor state, cell colors, terminal modes, alt screen, resize behavior — everything that's invisible to string matching
  • Cross-terminal conformance -- run the same tests against xterm.js, Ghostty, Alacritty, WezTerm, vt100, and Peekaboo to find where terminals disagree
  • Composable region selectors -- term.screen, term.scrollback, term.cell(r, c), term.row(n) for precise assertions
  • 21+ Vitest matchers -- text, cell style, cursor, mode, scrollback, and snapshot matchers
  • SVG & PNG screenshots -- no Chromium, no native deps (PNG via optional @resvg/resvg-js)
  • PTY support -- spawn real processes, send keypresses, wait for output
  • Fast -- typically under 1ms per unit-style test (in-memory backend, no PTY). No Chromium, no subprocesses
  • CLI + MCP -- termless capture for scripts, termless mcp for AI agents

Quick Start

import { createTerminal } from "@termless/core"
import { createXtermBackend } from "@termless/xtermjs"

const GREEN = (s: string) => `\x1b[38;2;0;255;0m${s}\x1b[0m`

const term = createTerminal({ backend: createXtermBackend(), cols: 80, rows: 24 })
term.feed(GREEN("● API online"))

// String matching sees text. termless sees everything.
term.screen.getText() // "● API online"
term.cell(0, 0).fg // { r: 0, g: 255, b: 0 } — the color getText() can't see

await term.close()

Spawn a real process

const term = createTerminal({ backend: createXtermBackend(), cols: 120, rows: 40 })
await term.spawn(["ls", "-la"])
await term.waitFor("total")

// Region selectors — inspect specific parts of the terminal
console.log(term.screen.getText()) // visible area
console.log(term.scrollback.getText()) // history above screen
console.log(term.row(0).getText()) // first row
console.log(term.lastRow().getText()) // last row

const svg = term.screenshotSvg()
const png = await term.screenshotPng() // requires: bun add -d @resvg/resvg-js
await term.close()

Write tests

import { test, expect } from "vitest"
import { createTerminalFixture } from "@termless/test"

// ANSI helpers — real apps use silvery or chalk, these are just for test data
const BOLD = (s: string) => `\x1b[1m${s}\x1b[0m`
const GREEN = (s: string) => `\x1b[38;2;0;255;0m${s}\x1b[0m`

test("inspect what string matching can't see", () => {
  // Creates an xterm.js terminal by default. Ghostty, Alacritty, WezTerm, vt100,
  // and Peekaboo backends are also available — see Multi-Backend Testing below.
  const term = createTerminalFixture({ cols: 40, rows: 3 })

  // Simulate a build pipeline — 4 lines overflow a 3-row terminal
  term.feed("Step 1: install\r\n")
  term.feed(`Step 2: ${GREEN("build ok")}\r\n`)
  term.feed(`Step 3: ${BOLD("test")}\r\n`)
  term.feed("Step 4: deploy")

  // Region selectors — screen, scrollback, buffer
  expect(term.scrollback).toContainText("install") // scrolled off, still in history
  expect(term.screen).toContainText("deploy") // visible area
  expect(term.buffer).toContainText("install") // everything (scrollback + screen)
  expect(term.row(0)).toHaveText("Step 2: build ok") // specific row

  // Cell styles — colors that getText() can't see
  expect(term.cell(0, 8)).toHaveFg("#00ff00") // "build ok" is green
  expect(term.cell(1, 8)).toBeBold() // "test" is bold

  // Scroll up, then assert on viewport
  term.backend.scrollViewport(1)
  expect(term.viewport).toContainText("install")

  // Resize — verify content survives
  term.resize(20, 3)
  expect(term.screen).toContainText("deploy")

  // Terminal state — window title, cursor, modes
  term.feed("\x1b]2;Build Pipeline\x07") // OSC 2 — set window title
  expect(term).toHaveTitle("Build Pipeline")
  expect(term).toHaveCursorAt(14, 2) // after "Step 4: deploy"
  expect(term).toBeInMode("autoWrap") // default mode
  expect(term).not.toBeInMode("altScreen") // not in alternate screen
})

None of this is possible with expect(output).toContain("text"). String matching can't see colors, can't inspect scrollback, can't verify cursor position, can't test resize behavior, and can't query terminal capabilities. Termless gives you the full terminal state machine.

Cross-terminal differences are real. Emoji width, color palette mapping, scroll region behavior, key encoding, Kitty keyboard protocol support, and hyperlink handling all differ between terminals. Run the same test against xterm.js and Ghostty and you'll find them. The cross-backend.test.ts suite runs 120+ conformance tests across all backends, catching differences automatically in CI.

Region Selectors

The composable API separates where to look from what to assert:

// Region selectors (getter properties — no parens)
term.screen // the rows x cols visible area
term.scrollback // history above screen
term.buffer // everything (scrollback + screen)
term.viewport // current scroll position view

// Region selectors (methods with args)
term.row(n) // screen row (negative from bottom)
term.cell(row, col) // single cell
term.range(r1, c1, r2, c2) // rectangular region
term.firstRow() // first screen row
term.lastRow() // last screen row

Then assert using the appropriate matchers for each view type:

// Text matchers work on RegionView (screen, scrollback, buffer, viewport, row, range)
expect(term.screen).toContainText("Hello")
expect(term.row(0)).toHaveText("Title")
expect(term.screen).toMatchLines(["Line 1", "Line 2"])

// Style matchers work on CellView
expect(term.cell(0, 0)).toBeBold()
expect(term.cell(0, 0)).toHaveFg("#ff0000")
expect(term.cell(2, 5)).toHaveUnderline("curly")

// Terminal matchers work on the terminal itself
expect(term).toHaveCursorAt(5, 0)
expect(term).toBeInMode("altScreen")
expect(term).toHaveTitle("My App")

Matchers Reference

Text Matchers (on RegionView / RowView)

| Matcher | Description | | ----------------------- | -------------------------------------------------------- | | toContainText(text) | Region contains text as substring | | toHaveText(text) | Region text matches exactly (trimmed) | | toMatchLines(lines[]) | Lines match expected array (trailing whitespace trimmed) |

Cell Style Matchers (on CellView)

| Matcher | Description | | ------------------------- | --------------------------------------------------------------------------------------------- | | toBeBold() | Cell is bold | | toBeItalic() | Cell is italic | | toBeFaint() | Cell is faint/dim | | toBeStrikethrough() | Cell has strikethrough | | toBeInverse() | Cell has inverse video | | toBeWide() | Cell is double-width (CJK, emoji) | | toHaveUnderline(style?) | Cell has underline; optional style: "single", "double", "curly", "dotted", "dashed" | | toHaveFg(color) | Foreground color ("#rrggbb" or { r, g, b }) | | toHaveBg(color) | Background color ("#rrggbb" or { r, g, b }) |

Terminal Matchers (on TerminalReadable)

| Matcher | Description | | ---------------------------- | ------------------------------------------------ | | toHaveCursorAt(x, y) | Cursor at position | | toHaveCursorVisible() | Cursor is visible | | toHaveCursorHidden() | Cursor is hidden | | toHaveCursorStyle(style) | Cursor style: "block", "underline", "beam" | | toBeInMode(mode) | Terminal mode is enabled | | toHaveTitle(title) | OSC 2 title matches | | toHaveScrollbackLines(n) | Scrollback has N total lines | | toBeAtBottomOfScrollback() | Viewport at bottom (no scroll offset) | | toMatchTerminalSnapshot() | Vitest snapshot of terminal state |

Installation

npm install -D @termless/test               # Vitest matchers + fixtures (includes xterm.js backend)
npm install -D @resvg/resvg-js              # Optional: PNG screenshot support

Which Package Do I Need?

| You want to... | Install | | ----------------------------------------------- | --------------------------------------------------------- | | Test a terminal UI in Vitest | @termless/test (includes xterm.js backend) | | Use the core Terminal API without test matchers | @termless/core + a backend (@termless/xtermjs, etc.) | | Test against Ghostty's VT parser | @termless/ghostty | | Test with a zero-dependency emulator | @termless/vt100 | | Take SVG/PNG screenshots | Built into @termless/core (PNG needs @resvg/resvg-js) | | Spawn and test real processes via PTY | Built into @termless/core (used via any backend) | | Automate a real terminal app (OS-level) | @termless/peekaboo | | Use the CLI or MCP server | @termless/cli |

Most users only need @termless/test.

Multi-Backend Testing

Test your TUI against multiple terminal emulators with a single test suite. Write tests once, configure backends via vitest workspace:

// vitest.workspace.ts — add as many backends as you want
export default [
  { test: { name: "xterm", setupFiles: ["./test/setup-xterm.ts"] } },
  { test: { name: "ghostty", setupFiles: ["./test/setup-ghostty.ts"] } },
  { test: { name: "vt100", setupFiles: ["./test/setup-vt100.ts"] } },
  // Also available: alacritty, wezterm (require Rust build), peekaboo (OS-level)
]
// test/setup-xterm.ts                         // test/setup-ghostty.ts
import { createXtermBackend }                  import { createGhosttyBackend }
  from "@termless/xtermjs"                       from "@termless/ghostty"
globalThis.createBackend =                     globalThis.createBackend =
  () => createXtermBackend()                     () => createGhosttyBackend()

// test/setup-vt100.ts — pure TypeScript, zero native deps
import { createVt100Backend } from "@termless/vt100"
globalThis.createBackend = () => createVt100Backend()

Your tests use globalThis.createBackend() and run against every configured backend automatically. vitest runs the entire test suite once per workspace entry — same tests, different terminal emulators. See docs/guide/multi-backend.md.

Cross-Backend Conformance

All backends are tested for conformance via cross-backend.test.ts — text rendering, SGR styles, cursor positioning, modes, scrollback, capabilities, key encoding, unicode, and cross-backend output comparison. Run with:

bun vitest run tests/cross-backend.test.ts

Packages

| Package | Description | | ----------------------------------------- | ------------------------------------------------------------------- | | termless | Core: Terminal, PTY, SVG/PNG screenshots, key mapping, region views | | @termless/xtermjs | xterm.js backend (@xterm/headless) | | @termless/ghostty | Ghostty backend (ghostty-web WASM) | | @termless/vt100 | Pure TypeScript VT100 emulator (zero native deps) | | @termless/alacritty | Alacritty backend (alacritty_terminal via napi-rs) | | @termless/wezterm | WezTerm backend (wezterm-term via napi-rs) | | @termless/peekaboo | OS-level terminal automation (xterm.js + real app) | | @termless/test | Vitest matchers, fixtures, and snapshot serializer | | @termless/cli | CLI (termless capture) + MCP server (termless mcp) |

How Termless Compares

Termless is the only headless terminal testing library that supports multi-backend testing with composable matchers:

| Feature | Termless | Playwright + xterm.js | TUI Test | ttytest2 | pexpect | Textual | Ink | | ------------------------- | ---------------------------------------- | ----------------------- | ---------------- | ------------ | ------- | ------- | --- | | Terminal internals | ✅ scrollback, cursor, modes, cell attrs | ⚠️ xterm.js buffer only | ❌ | ❌ | ❌ | ⚠️ | ❌ | | Multi-backend | ✅ 6 backends | ❌ xterm.js only | ❌ xterm.js only | ❌ tmux only | ❌ | ❌ | ❌ | | Composable selectors | ✅ 8 types | ❌ | ❌ | ❌ | ❌ | ⚠️ | ❌ | | Visual matchers | ✅ 21+ | ❌ DIY | ⚠️ | ❌ | ❌ | ⚠️ | ❌ | | Protocol capabilities | ✅ Kitty, sixel, OSC 8, reflow | ❌ xterm.js subset | ❌ | ❌ | ❌ | ❌ | ❌ | | SVG & PNG screenshots | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | No browser/Chromium | ✅ | ❌ needs Chromium | ✅ | ✅ | ✅ | ✅ | ✅ | | Framework-agnostic | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | | TypeScript | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |

Documentation

Full documentation site

CLI & MCP Server

For scripting and AI agents, @termless/cli provides terminal capture and an MCP server:

# Capture terminal output as text or screenshot
termless capture --command "ls -la" --wait-for "total" --text
termless capture --command "vim file.txt" --keys "i,Hello,Escape,:,w,q,Enter" --screenshot /tmp/vim.svg

# MCP server for AI agents (Claude Code, etc.)
termless mcp

See the CLI & MCP docs for full options.

See Also

silvery -- if Termless is for testing terminal apps, silvery is for building them. A React TUI framework that fully leverages modern terminal features (truecolor, Kitty keyboard protocol, mouse events, images, scroll regions) and generates all the ANSI codes automatically. Write terminal UIs in familiar React/JSX — silvery handles the terminal complexity. Use @termless/test to verify your silvery app renders correctly across terminals.

License

MIT