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

@nlindstedt/zag-agent

v0.12.0

Published

TypeScript SDK for zag — a unified CLI for AI coding agents

Readme

Zag TypeScript Binding

TypeScript binding for zag — a unified CLI for AI coding agents.

Prerequisites

  • Node.js 18+
  • The zag CLI binary installed and on your PATH (or set via ZAG_BIN env var)

Installation

npm install @nlindstedt/zag-agent

Development setup

To work with the binding from source:

cd bindings/typescript
npm install
npm run build

Quick start

import { ZagBuilder } from "@nlindstedt/zag-agent";

// Non-interactive execution
const output = await new ZagBuilder()
  .provider("claude")
  .model("sonnet")
  .autoApprove()
  .exec("write a hello world program");

console.log(output.result);

Streaming

import { ZagBuilder } from "@nlindstedt/zag-agent";

// Stream events as they arrive (NDJSON)
for await (const event of new ZagBuilder().provider("claude").stream("analyze code")) {
  console.log(event.type, event);
}

Bidirectional streaming sessions

execStreaming() returns a StreamingSession with piped stdin for sending user messages mid-flight (Claude only). Call .close({ timeout }) when you are done to shut the session down gracefully — it closes stdin, waits for the child to exit, escalates to SIGTERM, and finally SIGKILL if the child refuses to stop:

const session = await new ZagBuilder()
  .provider("claude")
  .execStreaming("initial prompt");

session.sendUserMessage("follow-up question");

for await (const event of session.events()) {
  console.log(event.type);
}

// Graceful shutdown — replaces the closeInput/wait/terminate/kill dance.
await session.close({ timeout: "5s" });

Automatic orphan cleanup

Long-running Node servers (Next.js, Express) can leak zag/agent subprocesses if the parent process dies unexpectedly. Opt in to .autoCleanup() on the builder to install process-wide shutdown handlers (exit, SIGINT, SIGTERM, SIGHUP, uncaughtException) that SIGTERM every tracked live session:

const session = await new ZagBuilder()
  .provider("claude")
  .autoCleanup()
  .execStreaming("hello");

Off by default so the SDK imposes no global side effects on consumers that don't need them.

Builder methods

| Method | Description | |--------|-------------| | .provider(name) | Set provider: "claude", "codex", "gemini", "copilot", "ollama" | | .model(name) | Set model name or size alias ("small", "medium", "large") | | .systemPrompt(text) | Set a system prompt | | .root(path) | Set the working directory | | .autoApprove() | Skip permission prompts | | .addDir(path) | Add an additional directory (chainable) | | .file(path) | Attach a file to the prompt (chainable) | | .env(key, value) | Add an environment variable for the agent subprocess (chainable) | | .json() | Request JSON output | | .jsonSchema(schema) | Validate output against a JSON schema (implies .json()) | | .worktree(name?) | Run in an isolated git worktree | | .sandbox(name?) | Run in a Docker sandbox | | .sessionId(uuid) | Use a specific session ID | | .outputFormat(fmt) | Set output format ("text", "json", "json-pretty", "stream-json") | | .inputFormat(fmt) | Set input format ("text", "stream-json" — Claude only) | | .replayUserMessages() | Re-emit user messages on stdout (Claude only) | | .includePartialMessages() | Include partial message chunks (Claude only) | | .maxTurns(n) | Set the maximum number of agentic turns | | .timeout(duration) | Set a timeout duration (e.g., "30s", "5m", "1h"). Kills the agent if exceeded. | | .mcpConfig(config) | MCP server config: JSON string or file path (Claude only) | | .showUsage() | Show token usage statistics (JSON output mode) | | .size(size) | Set Ollama model parameter size (e.g., "2b", "9b", "35b") | | .autoCleanup(enabled?) | Install process-wide shutdown handlers that SIGTERM tracked StreamingSession children on parent exit (opt-in) | | .verbose() | Enable verbose output | | .quiet() | Suppress non-essential output | | .debug() | Enable debug logging | | .bin(path) | Override the zag binary path |

Provider support for streaming / MCP flags

Four builder methods that toggle streaming I/O details and per-invocation MCP configuration are only honored by the Claude provider. Passing them to any other provider is a no-op.

| Method | Claude | Codex | Gemini | Copilot | Ollama | |--------|--------|-------|--------|---------|--------| | .inputFormat() | Yes | No | No | No | No | | .replayUserMessages() | Yes | No | No | No | No | | .includePartialMessages() | Yes | No | No | No | No | | .mcpConfig() | Yes | No | No | No | No |

.execStreaming() is Claude-only and always sets -i stream-json, -o stream-json, and --replay-user-messages. By default it emits one assistant_message event per complete assistant turn — you get one event when the model finishes speaking, not a stream of token chunks. Call .includePartialMessages(true) to receive token-level partial assistant_message chunks instead. The default stays false so existing callers that render whole-turn bubbles are not broken.

At the end of every agent turn the session emits a turn_complete event carrying the provider's stop_reason (end_turn, tool_use, max_tokens, stop_sequence, or null), a zero-based monotonic turn_index, and the turn's usage. A per-turn result event fires immediately after. New code should key turn-boundary UI off turn_complete — it is the authoritative signal and carries richer metadata than result. result continues to fire per-turn for backward compatibility.

Terminal methods

| Method | Returns | Description | |--------|---------|-------------| | .exec(prompt) | Promise<AgentOutput> | Run non-interactively, return structured output | | .stream(prompt) | AsyncGenerator<Event> | Stream NDJSON events | | .execStreaming(prompt) | StreamingSession | Bidirectional streaming (Claude only). Emits one assistant_message event per complete turn; pair with .includePartialMessages(true) for token-level chunks. | | .run(prompt?) | Promise<void> | Start an interactive session (inherits stdio) | | .resume(sessionId) | Promise<void> | Resume a previous session by ID | | .continueLast() | Promise<void> | Resume the most recent session | | .execResume(sessionId, prompt) | Promise<AgentOutput> | Resume a session non-interactively with a follow-up prompt | | .execContinue(prompt) | Promise<AgentOutput> | Resume the most recent session non-interactively | | .streamResume(sessionId, prompt) | AsyncGenerator<Event> | Resume a session in streaming mode | | .streamContinue(prompt) | AsyncGenerator<Event> | Resume the most recent session in streaming mode |

Version checking

The SDK automatically checks the installed zag CLI version before running commands. If you use a builder method that requires a newer CLI version than what's installed, a clear error is thrown:

env() requires zag CLI >= 0.6.0, but the installed version is 0.5.0.
Please update the zag binary.

The version is detected once (by running zag --version) and cached for the lifetime of the process.

| Method | Minimum CLI version | |--------|-------------------| | .env() | 0.6.0 | | .mcpConfig() | 0.6.0 |

All other methods are available since the initial release (0.2.3).

Discovery

Standalone functions for discovering available providers, models, and capabilities:

import { listProviders, getCapability, getAllCapabilities, resolveModel } from "zag-agent";

const providers = await listProviders();
const cap = await getCapability("claude");
const all = await getAllCapabilities();
const resolved = await resolveModel("claude", "small"); // { input: "small", resolved: "haiku", is_alias: true }

| Function | Description | |----------|-------------| | listProviders(bin?) | List available provider names | | getCapability(provider, bin?) | Get capabilities for a provider | | getAllCapabilities(bin?) | Get capabilities for all providers | | resolveModel(provider, model, bin?) | Resolve a model alias |

How it works

The SDK spawns the zag CLI as a subprocess (zag exec -o json or -o stream-json) and parses the JSON/NDJSON output into typed models. Zero external runtime dependencies — only Node.js built-ins.

Testing

npm run build && npm test

See also

License

MIT