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

toll-free-harness

v0.1.2

Published

Interactive PTY harness for local coding agents.

Readme

toll-free-harness

Full-featured user interaction simulator for terminal-based coding agents.

toll-free-harness models the complete user interaction surface of a coding agent CLI — prompting, answering questions, reviewing plans — as typed APIs. It spawns the agent in a local PTY, observes events through read-only hooks, and responds through the same keystroke channel a real user would.

Currently supports Claude Code. The framework is designed to add support for other coding agents whose headless modes do not fully expose the interactive user experience.

The name is a joke about toll booths around developer workflows.

What this project is not

Not an API proxy, credential-sharing service, or billing workaround. Users run their own local tools with their own accounts and must comply with those tools' terms.

Migrating from claude -p

Copy the migration guide and paste it to your coding agent:

# macOS
curl -s https://raw.githubusercontent.com/WeZZard/toll-free-harness/main/MIGRATION.md | pbcopy

# Linux
curl -s https://raw.githubusercontent.com/WeZZard/toll-free-harness/main/MIGRATION.md | xclip -selection clipboard

Ask your agent to convert your claude -p scripts to use toll-free-harness.

Quick start — CLI

No install needed. Replace claude -p with:

npx toll-free-harness claude -- -p "fix the failing tests"
npx toll-free-harness claude -- -p "explain this error" --allowedTools "Read"
cat build.log | npx toll-free-harness claude -- -p "what went wrong?"

When -p is detected, the tool routes the session through an interactive PTY harness. Without -p, it passes through to claude directly.

Quick start — Library API

pnpm add toll-free-harness
import { ClaudeCodeSession } from "toll-free-harness";

const session = new ClaudeCodeSession({
  args: ["--model", "opus"],
  cwd: "/path/to/project",
  prompt: "Fix the failing tests",
});

// Handle the agent's questions — return which option to select
session.onAskUserQuestion(async (event) => {
  console.log(event.text);
  console.log(event.questions[0]?.options.map((o, i) => `${i}: ${o.label}`));
  return { selectedIndex: 0 };
});

// Handle plan review — approve or reject with feedback
session.onExitPlanMode(async (event) => {
  console.log(event.planText.slice(0, 200));
  return { decision: "approve" };
});

const result = await session.run();

// Send a follow-up prompt (with optional images)
session.sendPrompt("Now add tests for the fix", {
  images: ["/tmp/screenshot.png"],
});

User interactions

The framework models three user interactions as dedicated typed APIs:

| Interaction | API | You provide | Library does | |---|---|---|---| | Prompting | sendPrompt(text, options?) | Text + optional image paths | Injects keystrokes into PTY | | Answering questions | onAskUserQuestion(handler) | { selectedIndex } | Navigates and selects the option | | Reviewing plans | onExitPlanMode(handler) | { decision: "approve" } or { decision: "reject", feedback } | Approves or rejects via keystrokes |

There is no raw write() — the library translates your typed decisions into the correct keystrokes internally.

Hook listeners (read-only)

Observe agent events without sending data back:

session.onPreToolUse("Bash", (event) => {
  console.log(`Running: ${event.toolInput?.command}`);
});

session.onPostToolUse("*", (event) => {
  console.log(`Tool ${event.toolName} completed`);
});

session.onStop(() => {
  console.log("Session ended");
});

Available: onPreToolUse, onPostToolUse, onPermissionRequest, onStop, onUserPromptSubmit. All are read-only — hooks return {} internally and never send data back to the agent.

Event guardrail

Wait for specific events with timeouts for deterministic test flows:

const event = await session.guardrail.expect(
  { kind: "pre_tool_use", toolName: "Bash" },
  10_000,
);

Timing model

PreToolUse and Stop hooks are blocking — the agent waits for them. Keystrokes injected during a blocking hook callback buffer in PTY stdin and are consumed by the UI after the hook returns. PermissionRequest is non-blocking — the dialog renders in parallel.

How it works

  1. run() generates a temporary plugin in /tmp/toll-free-plugin-<uuid>/ containing a manifest, hook definitions, and a bundled hook client
  2. Starts an HTTP server on a Unix domain socket (/tmp/toll-free-<uuid>.sock)
  3. Spawns the agent in a PTY with --plugin-dir /tmp/toll-free-plugin-<uuid>/ plus your args and prompt
  4. The agent loads the plugin and fires hooks as it runs. The hook client posts events to the socket.
  5. Your interaction handlers and listeners receive events; responses go through PTY keystrokes
  6. On exit, the socket and plugin directory are cleaned up

No user-scope settings are modified. The plugin is self-contained and session-scoped.

Cross-platform

Uses Node.js http.request({ socketPath }) for IPC — works on macOS, Linux, and Windows 10 1803+.

License

Apache-2.0