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

claude-wrap

v0.2.1

Published

Client library for wrapping the Claude Code CLI window: spawn it headless or in a visible terminal, read screen/parsed state, subscribe to status events, send input, and control out-of-process instances over a pipe or loopback HTTP.

Downloads

145

Readme

claude-wrap

npm version npm downloads types node license

A client library for driving the Claude Code CLI from your own code. It has two clients:

  • PTY client (ClaudeManager.spawn / ClaudeInstance) — wraps the interactive claude terminal: spawn it headless or in a visible window, mirror its output into a virtual screen, read parsed state, subscribe to status events, send input. Use it to watch or take over a real session.
  • Print client (ClaudeManager.print / PrintSession) — drives the official headless JSON protocol (claude -p): clean, cheap, structured, scriptable. Returns normalized turn results (text, structured output, tool calls, usage, cost). On top of it sits an OpenAI-compatible chat gateway (ChatGateway + the claude-wrap-serve HTTP server).

Windows-first. The PTY uses ConPTY (windowed mode uses cmd.exe start); the print client spawns claude via cmd /c for a real stdin pipe. Other platforms work headlessly (openWindow falls back to headless).

Install

npm install claude-wrap

Requires Node ≥ 18 and the claude CLI on your PATH.

Native dependency: node-pty

This package depends on node-pty, a native (node-gyp) addon. Installation downloads a prebuilt binary when one matches your platform + Node ABI; otherwise it compiles from source and needs a C/C++ toolchain:

  • Windows — the "Desktop development with C++" workload (Visual Studio Build Tools) and Python 3.
  • macOS — Xcode Command Line Tools (xcode-select --install).
  • Linuxbuild-essential (gcc/g++/make) and Python 3.

If npm install fails building node-pty, install the toolchain above and retry.

Quickstart — PTY client ask()

import { ClaudeManager } from "claude-wrap";

const manager = new ClaudeManager();
const instance = manager.spawn({ cwd: process.cwd() });

// Send a prompt and wait until Claude goes idle. Returns the parsed state.
const state = await instance.ask("List the files in this repo.");

// `ask` may return with a pending permission prompt — handle it if present.
if (state.permissionPrompt) {
  instance.approve(); // or instance.deny()
}

// Read what's on screen (full scrollback, trailing blanks trimmed).
const snap = instance.snapshot({ clean: true });
console.log(snap.lines.join("\n"));

await instance.shutdown();

Subscribe to events

instance.on("status:busy", () => console.log("working…"));
instance.on("status:idle", () => console.log("done"));
instance.on("permission:prompt", ({ prompt }) => console.log("needs:", prompt.title));
instance.on("tool:start", ({ tool, args }) => console.log("tool:", tool, args));
instance.on("todo:changed", ({ todoList }) => console.log("todos:", todoList));

All event names are in ALL_SESSION_EVENTS; payload types are keyed in SessionEvents.

Stream the display as it updates

Two headless-only push APIs follow the terminal live instead of polling: onData(cb) delivers every raw PTY chunk (ANSI included) for a byte-for-byte mirror, while the screen:changed event signals a redraw so you can pull clean lines from snapshot().

const stop = instance.onData((chunk) => process.stdout.write(chunk)); // raw bytes
instance.on("screen:changed", () => render(instance.snapshot({ clean: true }).lines));

Open a visible window (Windows)

const win = manager.spawn({
  cwd: "C:\\my\\project",
  label: "my-project",
  openWindow: true,   // visible cmd.exe window the user can type into
  enablePipe: true,   // control channel for snapshots / input
  enableHttp: true,   // loopback HTTP bridge
});

The window registers itself in an on-disk instance registry, so a separate process can discover and drive it:

import { listInstances, snapshot, write } from "claude-wrap";

const [entry] = listInstances();
if (entry) {
  const snap = await snapshot(entry.pipe, { clean: true });
  await write(entry.pipe, "hello\r");
}

Forward events out-of-process — EventSink

When a wrapper runs in another process, attach an EventSink to forward its events over a transport. The built-in WebSocketEventSink ships a generic JSON wire format (hello / event / exit frames).

import { ClaudeManager, WebSocketEventSink } from "claude-wrap";

const manager = new ClaudeManager();
// `reportTo` builds the sink internally; or call inst.attachSink(...) explicitly.
manager.spawn({ cwd: process.cwd(), reportTo: "ws://127.0.0.1:8080" });

The wrapper binary reads the same URL from --report-to or the CLAUDE_WRAP_REPORT_URL environment variable.

Print client — structured claude -p

ClaudeManager.print() (or new PrintSession(...)) drives Claude through the headless JSON protocol and returns a normalized TurnResult per turn — no screen scraping.

import { ClaudeManager } from "claude-wrap";

const manager = new ClaudeManager();
const session = manager.print({ cwd: process.cwd(), isolate: true });

const r = await session.ask("Say hello in one word.");
console.log(r.text, r.usage, r.costUsd);

await session.shutdown();
  • Transports. persistent (default) keeps one process alive for fast multi-turn with a warm prompt cache; oneshot (transport: "oneshot") spawns a fresh process per turn. Memory carries across turns either way.
  • isolate: true runs the cheap clean profile (no host MCP servers, tools, or plugins) — ~13× cheaper for plain chat.
  • Structured output. Pass jsonSchema (or ask(text, { schema }) in oneshot) and read r.structuredOutput.
  • Resume. { resume: "<claude-session-uuid>" } continues a prior session (cwd-scoped); the id is on session.claudeSessionId / r.sessionId.
  • Permissions. Supply canUseTool(call) to approve/deny each tool live, or subscribe to the permission:request event. session.interrupt() cancels the in-flight turn.
  • In-process functions. Pass functions: [{ name, inputSchema, handler }] and Claude can call your JavaScript directly (hosted as an in-process MCP server over the control protocol).

OpenAI-compatible chat gateway

ChatGateway exposes an OpenAI-shaped client (isolated by default), and claude-wrap-serve puts an HTTP server in front of it — point any OpenAI SDK at http://127.0.0.1:<port>/v1.

import { ChatGateway } from "claude-wrap";

const chat = new ChatGateway();
const res = await chat.completions.create({
  model: "claude-sonnet-4-6",
  messages: [{ role: "user", content: "Describe rain in one line." }],
});
console.log(res.choices[0].message.content);
claude-wrap-serve            # listens on 127.0.0.1:4000 by default
# POST /v1/chat/completions (JSON or SSE), GET /v1/models, GET /health

Supports streaming (SSE), response_format (json schema / json object), max_tokens, client-side function calling (toolstool_calls), and three history strategies — replay (stateless, default), session (pooled warm session via X-Claude-Session-Id), and diff (auto-resume on an exact prefix match). Errors use the OpenAI envelope.

MCP server

claude-wrap-mcp exposes both clients to agents as MCP tools: claude_* drive PTY sessions, claudep_* drive print sessions (claudep_spawn / claudep_ask / claudep_resume / claudep_resolve_permission / …).

Bins

| Bin | Purpose | |---|---| | claude-wrap | Launch a wrapped claude in a new terminal window | | claude-wrap-run | The wrapper process (PTY + pipe + HTTP bridge) | | claude-wrap-inject | CLI to snapshot/parse/drive a running instance over its pipe | | claude-wrap-serve | OpenAI-compatible HTTP chat gateway (/v1/chat/completions) |

Logging

Diagnostic logs are written to claude-wrap.log in the OS temp directory. Override the path with the CLAUDE_WRAP_LOG environment variable.

License

MIT © Alex Kaffetzakis