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

@willowai/cli

v0.0.3

Published

Willow CLI — the developer entrypoint to the Willow AI control platform

Readme

willow

The developer CLI for the Willow AI control platform — the identity, governance, and capability layer for AI agents inside the enterprise.

willow is the single entrypoint for humans and AI agents to talk to the platform: log in, switch between organizational contexts, browse the catalog of MCP tools you have access to, and call them from a shell.

Features

  • Hierarchical, noun-verb commandswillow auth login, willow tools call, willow context use ...
  • Multi-context — same org, but switch between any number of mcp / toolkit targets
  • OAuth-first auth — PKCE flow with token cache; static bearer tokens still supported for CI
  • Agent-optimizedwillow auth status and willow auth token are designed to be scripted
  • Connection pooling — lazy-spawn daemon keeps MCP connections warm between invocations
  • Self-diagnosingwillow doctor checks env, config, auth, daemon, MCP reachability
  • Self-updatingwillow update pulls the latest @willow/cli from npm
  • Actionable errors — every error ships with a Suggestion: line tuned for both humans and LLMs

Quick start

Requires Node.js ≥ 18.

npm install -g @willow/cli

willow init                    # interactive setup
willow auth login              # OAuth, opens browser
willow doctor                  # sanity-check the environment
willow tools list -d           # browse the catalog
willow tools call read_file '{"path":"./README.md"}'

Command surface

willow auth              login | logout | whoami | status | token
willow context           list | use | add | remove
willow tools             list | info | grep | call | servers | refresh
willow config            get  | set | show | edit
willow doctor                                     # diagnose env, auth, daemon, MCP
willow init                                       # create ~/.config/willow/config.json
willow update                                     # self-update via npm
willow uninstall-skill                            # remove the willow SKILL.md from every detected AI agent

Global flags

| Flag | Description | | ------------------------- | ---------------------------------------------------------- | | -c, --config <path> | Override the config file path | | --context <name> | Use a specific context for this invocation only | | -d, --with-descriptions | Include tool descriptions in tools list / tools grep | | -h, --help | Show help (works at every level: willow tools call --help) | | -v, --version | Show version |


Auth (willow auth)

willow auth login        # OAuth login (opens browser)
willow auth logout       # Remove stored OAuth tokens
willow auth whoami       # Identity, org, current context
willow auth status       # Offline check; exit 0=ok, 4=needs login
willow auth token        # Print the current bearer token to stdout

willow auth status is designed to be the cheap preflight check at the top of scripts and AI-agent sessions:

willow auth status && willow tools call my_tool '{}'

willow auth token prints only the token, suitable for piping:

curl -H "Authorization: Bearer $(willow auth token)" https://example.com/api

Contexts (willow context)

A context is a named (mcp, toolkit) pair scoped to your current org. All contexts share the same org / baseUrl / userAccessKey — only the target on the gateway changes.

willow context list                                            # show all (active is starred)
willow context add slack-eng --mcp slack --toolkit engineering
willow context add github-platform --mcp github --toolkit platform --use
willow context use slack-eng
willow context remove old-context

Every command that needs a server uses the active context. You can override the active context per-invocation with the global --context <name> flag — handy for one-off calls without flipping state.


Tools (willow tools)

willow tools list                                  # tool names
willow tools list -d                               # names + descriptions
willow tools info <tool>                           # JSON schema for one tool
willow tools grep <pattern>                        # substring search across names
willow tools grep <pattern> -d                     # ... include descriptions
willow tools servers                               # list integrations on the gateway
willow tools servers -d                            # ... include descriptions
willow tools call <tool> '<json>'                  # call with inline JSON
echo '<json>' | willow tools call <tool>           # call with JSON from stdin
willow tools call <tool> --force-auth '<json>'     # ensure logged in first
willow tools refresh                               # clear the cached tool catalog

Cache invalidation

The daemon caches tools list results in process memory for settings.cacheTtl seconds. The CLI invalidates this cache automatically when the underlying state changes:

  • willow context use <name> — stops the daemon so the next call respawns with the new context's headers.
  • willow auth login — stops the daemon so the next call uses the fresh token instead of the stale one held in the existing connection.

If you need to force a refresh manually (e.g. you added or removed a tool on the platform side and don't want to wait for the TTL), run:

willow tools refresh

This stops the daemon. The next willow tools list or willow tools call respawns it and re-fetches a fresh catalog.

willow tools servers is the lightweight discovery rung: when you don't yet know which tool to grep for, it returns the catalog of available integrations (slug, name, description). Use the slug as a keyword in a follow-up willow tools grep <slug> to narrow down.

Examples:

willow tools list -d
willow tools info read_file
willow tools grep file
willow tools servers -d
willow tools call search_issues '{"query": "open bugs", "limit": 5}'

# Pipe JSON through jq before calling
jq -n '{path: "./src/index.ts"}' | willow tools call read_file

willow tools call extracts text content from MCP responses and writes it straight to stdout (no jq needed for the common case). When the response has no text parts, it falls back to pretty-printed JSON.


Config (willow config)

willow config show                       # entire config as JSON
willow config get <key>                  # single value (dot-notation)
willow config set <key> <value>          # set a value (dot-notation, type-coerced)
willow config edit                       # open in $VISUAL / $EDITOR; validates on save

Examples:

willow config set settings.timeout 30
willow config set settings.daemon false
willow config set contexts.default.mcp slack
willow config get settings.timeout

Values written through set are best-effort coerced (true/false/null, numerics). For anything more structured, use willow config edit.


Doctor (willow doctor)

Runs a checklist over the local Willow environment:

  • Node version
  • Config file present and valid JSON
  • At least one context defined; active context exists
  • Auth state (OAuth token present and not expired)
  • Daemon process health
  • MCP server reachability (HTTP HEAD / stdio --version)
  • Skill install status (per detected AI agent)

Exits non-zero if any item fails, so it's safe to use in CI.

willow doctor

Update (willow update)

willow update             # install the latest @willow/cli globally via npm
willow update --check     # only print whether an update is available

Uninstalling the agent skill (willow uninstall-skill)

willow init installs a small SKILL.md into every detected AI agent (Cursor, Codex, Claude Code, Windsurf, GitHub Copilot, Cline, Roo Code, Continue) so the agent can discover the CLI on its own. If you ever want the agent to stop opportunistically reaching for willow, remove it with:

willow uninstall-skill

This deletes the per-agent skill directories and the canonical ~/.agents/skills/willow/ directory. It does not uninstall the willow binary itself, change your config, or revoke any tokens — only the AI-facing skill file is removed. Reinstall any time by running willow init again. willow doctor always shows the current install status under "Skill".


Config file format

The config lives at ~/.config/willow/config.json (override via -c / WILLOW_CONFIG_PATH).

{
  "enabled": true,
  "org": "acme",
  "currentContext": "default",
  "contexts": {
    "default":   { "mcp": null,    "toolkit": null },
    "slack-eng": { "mcp": "slack", "toolkit": "engineering" }
  },
  "settings": {
    "timeout": 60,
    "daemon": true
  }
}

For static bearer-token auth (rarely needed; OAuth via willow auth login is the default):

{
  "baseUrl": "https://acme.mcp-s.com/mcp",
  "token": "${MY_BEARER}",
  "currentContext": "default",
  "contexts": { "default": {} }
}

For stdio mode (runs MCP locally via npx -y @mcp-s/mcp):

{
  "org": "acme",
  "userAccessKey": "<key>",
  "currentContext": "default",
  "contexts": { "default": { "mcp": "slack", "toolkit": "engineering" } }
}

Top-level fields

| Field | Description | | ---------------- | ----------------------------------------------------------------- | | enabled | Master kill-switch. When false, all commands refuse to run. | | org | Org name — derives URL https://<org>.mcp-s.com/mcp | | baseUrl | Custom server URL (alternative to org) | | userAccessKey | User access key — triggers stdio mode via npx @mcp-s/mcp | | token | Static Bearer token; usually replaced by OAuth (auth login) | | currentContext | Name of the active context | | contexts | Map of name → { mcp, toolkit, allowedTools, disabledTools } | | settings | Per-installation behavioral knobs (see below) |

Environment variable substitution is supported anywhere in the file: "token": "${MY_TOKEN}".

settings block

| Field | Description | Default | | --------------- | ---------------------------------------------------- | ------- | | timeout | Request timeout (seconds) | 1800 | | maxRetries | Retry attempts for transient errors (0 = disable) | 3 | | retryDelay | Base retry delay (milliseconds) | 1000 | | daemon | Enable connection caching (daemon mode) | true | | daemonTimeout | Idle timeout for cached connections (seconds) | 300 | | cacheTtl | Tool-list cache TTL in seconds (0 = disable) | 300 | | history | Append each invocation to history.jsonl | false |

Per-context fields

Each entry under contexts.<name>:

| Field | Description | | --------------- | -------------------------------------------- | | mcp | MCP identifier (sent as x-mcp header) | | toolkit | Toolkit name (sent as x-toolkit header) | | allowedTools | Glob patterns of tool names to allow | | disabledTools | Glob patterns of tool names to exclude |


Working with complex JSON arguments

When arguments contain quotes, special characters, or multi-line content, use stdin to avoid shell-escaping pain:

# Heredoc
willow tools call create_ticket <<EOF
{"title": "It's broken", "description": "User said \"it doesn't work\""}
EOF

# From a file
cat args.json | willow tools call some_tool

# Built with jq
jq -n '{query: "open bugs", assignee: "me"}' | willow tools call search_issues

Chaining and scripting

# Find issues and extract URLs
willow tools call search_issues '{"query": "priority: high"}' \
  | jq -r '.issues[].url'

# Conditional: only act if a precondition holds
willow tools call list_channels '{}' \
  | jq -e '.channels[] | select(.name == "engineering")' >/dev/null \
  && willow tools call post_message '{"channel": "engineering", "text": "Deploy complete"}'

# Skip everything if not authenticated
willow auth status || { echo "Run 'willow auth login' first" >&2; exit 1; }

Tips:

  • jq -r for raw output (no surrounding quotes)
  • jq -e to make jq set the exit code from the filter result
  • 2>/dev/null to silence diagnostics when probing existence
  • Pipe willow auth token straight into curl -H "Authorization: Bearer ..."

Environment variables

| Variable | Description | | ---------------------- | ------------------------------------------------------ | | WILLOW_CONFIG_PATH | Override the config file path | | WILLOW_DEBUG | Enable verbose debug output to stderr | | WILLOW_STRICT_ENV | false to warn instead of erroring on missing ${VAR}| | WILLOW_NO_OAUTH | Internal: disable OAuth in spawned subprocesses |


Output streams

| Stream | Content | | ---------- | ---------------------------------------- | | stdout | Tool results, JSON, scriptable output | | stderr | Errors, diagnostics, progress messages |


Exit codes

| Code | Meaning | | ---- | ---------------------------------------------- | | 0 | Success | | 1 | Client error (bad args, missing config, etc.) | | 2 | Server error (tool execution failed) | | 3 | Network error (connection failed) | | 4 | Auth error (willow auth login required) |


Using with AI agents

willow is designed to give AI coding agents direct shell access to the platform without burning thousands of tokens loading every tool schema into the prompt. The agent discovers and inspects tools on demand.

A ready-made SKILL.md ships with the package and is installed automatically by willow init for any detected agent (Claude Code, Cursor, Gemini CLI, ...). You can also drop the snippet below into your agent's system prompt:

## Willow

You have access to enterprise tools through the Willow platform via the `willow` CLI.

Preflight (always run first):
  willow auth status               # exit 0=ready, 4=run `willow auth login`

Discovery → Inspect → Execute:
  willow tools grep <query>        # find candidate tool names
  willow tools info <tool>         # get JSON schema
  willow tools call <tool> '<json>' # execute (or pipe JSON via stdin)

Examples:
  willow tools call search_issues '{"query":"open bugs"}'
  echo '{"query":"urgent"}' | willow tools call search_issues

Architecture

Connection pooling (daemon)

Daemon mode is on by default. The first invocation lazy-spawns a willow-<uid> worker that keeps the MCP connection open for settings.daemonTimeout seconds (default: 300s) after the last request, avoiding repeated startup latency.

willow tools call some_tool '{}'      # uses cached connection (default)
WILLOW_DEBUG=1 willow tools list      # see connection debug output
willow doctor                         # check daemon health

To disable, run willow config set settings.daemon false. With the daemon off, each call opens a fresh connection and tears it down on exit.

Retries

Transient failures are retried automatically with exponential backoff.

  • Retried: network errors (ECONNREFUSED, ETIMEDOUT, ECONNRESET), HTTP 429, 502, 503, 504
  • Failed immediately: config errors, auth errors (401, 403), tool-not-found, invalid JSON

Error messages

Every error has the same shape, optimized for both humans and LLMs:

Error [TOOL_NOT_FOUND]: Tool "search" not found in server "server"
  Details: Available tools: search_issues, create_ticket, list_channels (+5 more)
  Suggestion: Run `willow tools list` to see all available tools

Contributing

git clone https://github.com/willow-ai/cli.git willow-cli
cd willow-cli
npm install
npx simple-git-hooks   # register the pre-commit lint hook (once)

Development workflow:

npm run dev        # run from source
npm run typecheck  # tsc --noEmit
npm run lint       # biome
npm test           # vitest
npm run build      # tsup → dist/

The pre-commit hook runs npm run lint:fix automatically and re-stages any auto-fixed files.

Releasing

  1. Bump the version in package.json.
  2. Commit and push to main. CI runs lint + typecheck + tests.
  3. On a green build with a version bump, the release workflow tags the commit and publishes @willow/cli to npm.

License

MIT — see LICENSE.