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-sub

v0.4.0

Published

PATH shim that routes claude -p invocations through interactive Claude when CLAUDE_USE_SUB is set

Readme

claude-sub

A PATH shim that routes claude -p calls through an interactive Claude session so they bill against your Claude subscription instead of API credits.

Disclaimer. Using claude-sub may violate the Anthropic terms of service governing your Claude subscription — review them and the Claude Code documentation before relying on it.

claude-sub installs ahead of the real claude binary on your PATH. When enabled, it intercepts -p / --print invocations, spawns interactive Claude under a PTY, sends the prompt as keystrokes, waits for the reply, extracts the clean text, and writes it to stdout. Every other invocation passes straight through. Toggle it on or off with csub on / csub off — no environment variables required.

Install

npx claude-sub setup

setup detects your shell (zsh / bash / fish), shows you the one-line PATH edit it plans to make, asks for confirmation, writes the line with a marker comment so uninstall can reverse it, and verifies that which claude resolves to the shim. The shim ships disabled — your existing claude calls keep working until you run csub on.

To update later:

npx claude-sub@latest setup

The @latest matters: plain npx claude-sub reuses npx's cached copy without checking the registry. setup is idempotent — when the marker line already points at the new install it makes no changes, and when it points at an old location (e.g. a previous npx cache dir) it shows a diff and repoints it in place.

To remove it later:

npx claude-sub uninstall

This strips the marker line from your rc file and uninstalls the global package.

Usage

csub on        # route `claude -p` through your subscription
csub off       # passthrough only; `claude -p` hits the API again
csub status    # show whether routing is on or off, and which `claude` PATH wins
csub --version # print the installed version (also: csub version, csub -v)

Once csub on is set, just use claude as you normally would:

claude -p "reply with the single word OK"
# OK

Exit codes: 0 on success, 1 for unsupported flags or when the session ends without a clean reply, 124 if the PTY session times out. Failures write a csub:-prefixed diagnostic to stderr — the shim never passes raw terminal output off as a reply.

Supported flags

When routing is enabled, the following flags are forwarded to the interactive session:

| Flag | Description | |------|-------------| | --model / -m | Model to use (e.g. claude-sonnet-4-5) | | --verbose / -v | Enable verbose output | | --append-system-prompt | Append to the system prompt | | --system-prompt | Override the system prompt | | --permission-mode | Set permission mode (e.g. acceptEdits) | | --dangerously-skip-permissions | Skip permission prompts | | --settings | Path to a settings file | | --agent / --agents | Agent configuration | | --strict-mcp-config | Strict MCP config validation | | --bare | Bare-mode output | | --add-dir | Additional working directories (variadic) | | --mcp-config | MCP config paths (variadic) | | --allowedTools / --allowed-tools | Tool allowlist (variadic) | | --disallowedTools / --disallowed-tools | Tool denylist (variadic) | | --tools | Tool list (variadic) | | --plugin-dir | Plugin directories (variadic) | | --output-format stream-json | Emit a Claude-compatible NDJSON event stream — consumed by the shim, not forwarded (see JSON output) | | --output-format json | Emit a single JSON result object on completion — consumed by the shim, not forwarded (see JSON output) |

Flag and prompt order don't matter: claude -p --output-format stream-json "question" and claude -p "question" --output-format stream-json both send "question" as the prompt. Any other flag causes the shim to exit non-zero with a csub:-prefixed message naming the unsupported flag and listing what's accepted.

JSON output

Both upstream JSON output formats are emulated, not rejected. With csub on the call routes through your subscription like any other -p invocation — no "will bill against API" warning. The PTY session is still plain text internally; the JSON shapes are synthesized around it. Both the space-separated (--output-format json) and equals (--output-format=json) forms work.

Single-object JSON (--output-format json)

The shim stays silent during the run and prints exactly one JSON object on completion:

claude -p "reply with the single word OK" --output-format json
# {"type":"result","result":"OK"}

claude -p "reply with the single word OK" --output-format json | jq -r .result
# OK

The envelope is minimal by design — no cost_usd, usage, or session_id fields, since the shim does not have upstream's metadata.

Streaming JSON (--output-format stream-json)

The shim emits a Claude-compatible NDJSON (newline-delimited JSON) event stream on stdout.

claude -p --output-format stream-json "reply with the single word OK"
# {"type":"heartbeat"}
# {"type":"assistant","message":{"content":[{"type":"text","text":"OK"}]}}
# {"type":"result","result":"OK"}

Events emitted:

  • heartbeat{"type":"heartbeat"}, written during the run at most once every ~10s, and only when the PTY produced new output since the previous interval. It is a genuine proof-of-life: when the interactive session stalls, the stream goes quiet, so a watcher can tell "still working" from "wedged." Nothing acts on the heartbeat automatically — it is purely observational.
  • assistant{"type":"assistant","message":{"content":[{"type":"text","text":"…"}]}}, written once at the end, carrying the full clean reply.
  • result{"type":"result","result":"…"}, the terminal event carrying the same clean, sentinel-stripped text the plain-text path produces.

These shapes satisfy the jq filters a stream-json consumer (e.g. ralph.sh) uses: .type=="assistant" | .message.content[] | select(.type=="text").text for live text and .type=="result" | .result for the outcome. This makes claude-sub a drop-in for tools that pipe --output-format stream-json through jq.

Only stream-json and json are supported. Other --output-format values (e.g. text) exit non-zero with a message; on failure (timeout, idle, or no clean reply) the shim writes a diagnostic to stderr and exits non-zero, as in plain mode.

Running under a sandbox (srt)

claude-sub is designed to run inside Anthropic's sandbox-runtime (srt) — e.g. when ralph.sh wraps each iteration's claude call. Because the shim drives interactive Claude through a pseudo-terminal, the sandbox must grant pty access. The default sandbox profile denies /dev/ptmx and pty slave devices, so node-pty fails with posix_spawnp failed / PTY error.

Add allowPty: true to your srt settings file (alongside filesystem and network):

{
  "allowPty": true,
  "filesystem": { "...": "..." },
  "network": { "allowedDomains": ["*.anthropic.com", "anthropic.com"], "...": "..." }
}

The sandbox also needs write access to wherever interactive Claude persists session state — typically ~/.claude and ~/.claude.json — plus the usual temp directories. With those granted and allowPty: true, the shim's pty session spawns under the sandbox and stream-json routes through your subscription (no API-bypass warning).

Known limitations

The following flags and features are not supported when routing is on:

  • --output-format text — only stream-json and json are emulated (see JSON output)
  • --resume — session resume requires API session IDs not available under a PTY
  • --json / --stream-json — these are not real upstream claude flags; rejected like any other unsupported flag, with the supported list (including both --output-format values) in the message
  • --no-markdown — not forwarded; output formatting follows interactive Claude defaults
  • Piped stdin — the PTY session cannot receive stdin piped from a shell pipe
  • Non-print invocations — only -p / --print routes through your subscription; everything else passes through to the real binary

Timeouts

| Variable | Default | Description | |----------|---------|-------------| | CLAUDE_USE_SUB_TIMEOUT_MS | 300000 (5 min) | Overall timeout for a routed invocation |

If the session times out, the shim exits with code 124 and writes a diagnostic to stderr containing the elapsed time and the last 4 KB of raw PTY output.

Troubleshooting

Run csub doctor first — it reports whether the shim is in front of the real claude binary and prints a remediation hint if it isn't.

which claude resolves to the real binary. Your global npm bin directory isn't ahead of the real claude on your PATH. npx claude-sub setup adds the right line for your shell; if you ran it before installing claude itself, run it again, or open a fresh shell so the rc-file change takes effect.

Manual install (without setup). If you'd rather wire the PATH yourself:

npm install -g claude-sub
# then, in your shell profile:
export PATH="$(npm bin -g):$PATH"

After that, which -a claude should list the shim first and the real claude second.

Install from a local tarball.

pnpm pack  # produces claude-sub-<version>.tgz
npm install -g ./claude-sub-<version>.tgz