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

@pencilink/gateway

v0.2.3

Published

Pencil Gateway — local daemon that drives Claude Code for tasks assigned in Pencil.

Downloads

754

Readme

@pencilink/gateway

Local daemon that runs Pencil-assigned chats and tasks through your own Claude Code installation. When your workspace's Agent Runtime is set to Run on my computer, Pencil queues work for this daemon; the daemon claims that work, invokes claude -p locally, and sends status/replies back to Pencil. Pencil never sees your Anthropic credentials and never relays Anthropic API calls.

Install

Double-click installer (recommended)

Pencil Gateway ships user-scoped installers for macOS and Linux. Neither installer needs sudo; each installs the gateway under ~/.pencil/service, walks through pairing when needed, starts the service immediately, and configures it to start again whenever you sign in.

  • macOS: extract pencil-gateway-installer-macos.zip, then double-click Install Pencil Gateway.command. macOS may ask you to confirm opening a downloaded script the first time.
  • Linux (systemd desktop): extract pencil-gateway-installer-linux.tar.gz, then double-click Install Pencil Gateway.desktop. Some file managers require Allow Launching on the first run.

The installer asks whether to install future stable releases automatically. Choose No to get a daily desktop notification instead. Re-running the installer is safe and updates the existing installation in place.

Both installers require Node ≥ 20, npm, and a signed-in claude CLI on the interactive user's PATH.

To build the two distributable archives from this repository:

pnpm installers

The repository's Test Pencil Gateway CI job also publishes both archives as the pencil-gateway-installers workflow artifact.

npm / command-line install

npm install -g @pencilink/gateway

Requires Node ≥ 20 and a working claude CLI on PATH.

Quick Start

  1. In Pencil, open Settings → Agent runtime and choose Run on my computer.

  2. Click Generate pair code. The code is valid for 15 minutes and can be redeemed once.

  3. Pair this machine:

    pencil-gateway pair

    Paste the code when prompted. The gateway exchanges it for a long-lived device token stored at ~/.pencil/gateway.json with mode 0600. The plaintext token is shown to the daemon once; Pencil stores only its hash.

  4. Start the daemon:

    pencil-gateway start

    In an interactive terminal, startup now opens with a multicolor outlined Pencil wordmark, a slim horizontal colored-pencil drawing, and a compact summary of the active account, Pencil URL, machine, Claude authentication, version, and local concurrency. Redirected output and launchd/systemd services keep the existing clean structured logs instead.

    Disable the interactive banner when needed:

    pencil-gateway start --no-banner

    The daemon heartbeats every 30s and claims dispatches when it has local capacity. When you assign yourself a task in Pencil, it spawns claude -p in ~/.pencil/work/<orgId>/<taskId>/. It runs up to 3 independent dispatches at a time by default while keeping the same task or chat thread serialized.

    To tune local parallelism:

    pencil-gateway start --max-concurrency 4
  5. Leave the process running. New eligible chats/tasks will be picked up automatically. Stop it with Ctrl+C.

Each dispatch attaches the paired Pencil MCP server directly to Claude Code. Agents can read Pencil objects and post reviewable proposal cards with native tools even when local Bash or network commands are not approved.

Self-Hosted Pencil

Use --mc-url when pairing against a self-hosted Pencil instance:

pencil-gateway pair --mc-url https://pencil.example.com

The paired config stores that URL, so later start, status, doctor, and mcp commands use the same host automatically.

Multiple Pencil accounts

The default pairing still uses ~/.pencil/gateway.json. To connect two Pencil accounts at the same time, pair each one into a named local profile and run one daemon per profile:

pencil-gateway pair --account work
pencil-gateway pair --account personal

pencil-gateway start --account work
pencil-gateway start --account personal

Named profiles are stored as ~/.pencil/gateway.<account>.json. Gateway requests for named profiles also include ?account=<account> so server logs and future routing can distinguish concurrent daemons; the device token remains the authority for auth and workspace scope.

To see which profiles you've already paired (and the exact command to start each one), run:

pencil-gateway list

Example output:

Paired account profiles (2):

  work
    MC URL:    https://app.pencil.ink
    Org/User:  org_abc / user_123
    Paired at: 2026-07-17T13:00:00.000Z
    Start:     pencil-gateway start --account work

  personal
    MC URL:    https://app.pencil.ink
    Org/User:  org_def / user_456
    Paired at: 2026-07-17T13:05:00.000Z
    Start:     pencil-gateway start --account personal

Use the same account flag with diagnostics and MCP:

pencil-gateway status --account work
pencil-gateway doctor --account work
pencil-gateway mcp --account work

MCP server

The same pairing can expose Pencil's agent/operator tools to any MCP client over stdio:

pencil-gateway mcp

This is a local MCP server. It runs on your machine and communicates with Pencil through the same paired device token used by pencil-gateway start. Hosted remote MCP is also available from Pencil at SITE_URL/api/mcp for connector-style clients; use OAuth there, or an API key created in Settings with MCP access enabled if your client supports static bearer headers.

If you only need stdio MCP and do not want to pair the local task runtime, authenticate the gateway through Pencil OAuth instead:

pencil-gateway mcp --oauth

The first run opens Pencil in your browser for sign-in, workspace selection, and consent. The gateway dynamically registers a public PKCE client and stores its short-lived access token, rotating refresh token, and client registration in ~/.pencil/mcp-oauth.json with mode 0600. Later runs refresh the access token automatically. Named OAuth profiles use ~/.pencil/mcp-oauth.<account>.json:

pencil-gateway mcp --oauth --account work

OAuth mode proxies the hosted /api/mcp tool surface, so the selected workspace and mcp:read / mcp:write scopes are enforced server-side. Hosted remote MCP intentionally excludes dangerous tools; --oauth cannot be combined with --include-dangerous.

Example client config:

{
  "mcpServers": {
    "pencil": {
      "command": "pencil-gateway",
      "args": ["mcp"]
    }
  }
}

For a named profile:

{
  "mcpServers": {
    "pencil-work": {
      "command": "pencil-gateway",
      "args": ["mcp", "--account", "work"]
    }
  }
}

On startup the MCP server reads the selected gateway config (~/.pencil/gateway.json by default, or ~/.pencil/gateway.<account>.json with --account), fetches the current tool catalog from Pencil via GET /api/mc/tools, and forwards tool calls to POST /api/mc/actions with the paired device token. Pencil keeps enforcing workspace and task-assignment scope server-side.

For task work, clients should use this compact workflow:

  1. Call list_tasks with human/agent assignee, roadmap, and status filters to locate the work. It returns resolved assignee summaries, canonical task URLs, and an opaque cursor; done tasks are excluded unless requested. Use list_members or resolve_member for explicit human-directory lookup.
  2. Call get_task_context once before acting. It returns the task, plan gate, checklist, relationships, recent comments, files, repositories, and completion readiness in one bounded response.
  3. Use post_comment for durable discussion and save_output for artifacts.
  4. Move completed implementation to review with update_task_status; use done only when the user explicitly wants final completion.

Pencil injects workspace and caller identity for MCP writes. Do not add workspaceId, orgId, or agentId to MCP calls. Reuse idempotencyKey when retrying post_comment, save_output, agent_update, or ask_question so network retries do not duplicate records.

The v1 MCP surface exposes tools only. MCP resources and prompts are planned, but are not available from pencil-gateway mcp yet.

Dangerous tools are hidden by default. To request them, the Pencil server must set PENCIL_MCP_ENABLE_DANGEROUS_TOOLS=1 and the local command must opt in:

pencil-gateway mcp --include-dangerous

For self-hosted Pencil, pair with --mc-url or set PENCIL_MC_URL before pairing:

PENCIL_MC_URL=https://your-pencil.example.com pencil-gateway pair

OAuth mode can target the same self-hosted instance without pairing:

pencil-gateway mcp --oauth --mc-url https://your-pencil.example.com

If port 8787 is already in use during first-time consent, choose another loopback port with --oauth-callback-port <port>; the gateway will dynamically register that exact callback URL.

Commands

| Command | What it does | |---|---| | pencil-gateway pair | Redeem a one-time pair code → device token | | pencil-gateway start | Long-running daemon: heartbeat, claim dispatches, run Claude Code | | pencil-gateway list | List paired account profiles + a ready-to-run start command for each (alias ls) | | pencil-gateway mcp | Local MCP stdio server for Pencil tools; add --oauth to use hosted OAuth instead of pairing | | pencil-gateway status | Show current pairing + a one-shot ping | | pencil-gateway doctor | Sanity-check claude/server/token and the core MCP task workflow | | pencil-gateway service install | Install and start a user-level launchd/systemd service | | pencil-gateway service status | Show whether the startup service is installed and running | | pencil-gateway service update | Check for and install a newer managed release | | pencil-gateway service uninstall | Stop/remove startup units; preserve pairing and work files | | pencil-gateway unpair | Remove the startup service and local pairing config |

All commands accept --account <name> to select a named local profile.

Running as a service

The double-click installer configures the service automatically. For a command-line npm installation, pair first and then run:

pencil-gateway service install
pencil-gateway service status

On macOS this creates per-user LaunchAgents under ~/Library/LaunchAgents. On Linux this creates systemd user units under ~/.config/systemd/user. The gateway starts at sign-in, restarts after unexpected failures, and does not run as root.

Named profiles get independent units:

pencil-gateway service install --account work
pencil-gateway service status --account work

Updates

Every service installation includes a daily update check against the stable latest release of @pencilink/gateway.

  • A normal CLI service install sends a desktop notification when a newer release exists. Update a global npm installation first, then reinstall the service so its exact executable path stays current.
  • The double-click installer can enable managed automatic updates. It updates the rootless package under ~/.pencil/service and restarts the gateway only after npm succeeds.

Useful commands:

pencil-gateway service update --check
pencil-gateway service update
pencil-gateway service uninstall

service uninstall removes only the startup definitions and service metadata. It deliberately preserves ~/.pencil/gateway*.json, work directories, logs, and the installed npm package.

Logs:

  • macOS: ~/.pencil/logs/gateway*.log
  • Linux: journalctl --user -u pencil-gateway.service

Configuration

| Env var | Default | Notes | |---|---|---| | PENCIL_MC_URL | https://app.pencil.ink | Override for self-hosted Pencil. | | PENCIL_GATEWAY_MAX_CONCURRENCY | 3 | Concurrent local Claude Code dispatches, clamped to 1..8. CLI flag wins. | | PENCIL_GATEWAY_DEBUG| unset | 1 → emit debug log lines. | | PENCIL_GATEWAY_NO_BANNER | unset | 1 disables the interactive startup banner. NO_COLOR disables ANSI color while preserving the banner. | | PENCIL_GATEWAY_AUTO_UPDATE | unset | Double-click installer only: 1 chooses automatic updates; 0 chooses notifications without prompting. | | PENCIL_GATEWAY_INSTALL_ROOT | ~/.pencil/service | Double-click installer only: override the rootless npm prefix. |

Security model

  • One user → one daemon → that user's tasks only. The server refuses any /api/mc/actions call from this daemon that references a task assigned to a different user.
  • The device token is a long-lived bearer; treat ~/.pencil/gateway.json like an SSH private key.
  • The local MCP server never stores org-scoped connector credentials such as Notion, Slack, or GitHub OAuth tokens. Pencil keeps those credentials server-side and exposes narrow tools.
  • Local Claude dispatches use a strict per-run MCP config containing only the paired Pencil server. Agent allowlists are applied to the corresponding mcp__pencil__* tools instead of requiring broad Bash approval.
  • Hosted remote MCP uses OAuth or explicitly MCP-enabled API keys, binds OAuth tokens to SITE_URL/api/mcp, and filters out local-gateway-only, credential-bearing, destructive, dangerous, and sandbox tools.
  • OAuth-backed local stdio MCP stores its public-client registration and rotating tokens in ~/.pencil/mcp-oauth*.json with mode 0600; it never exposes those credentials to the MCP client process.
  • Revoke from Pencil → Settings → Gateway. After revoke the daemon's next heartbeat or dispatch claim returns 401 and the daemon exits.

MCP troubleshooting

| Symptom | Check | |---|---| | No paired gateway | Run pencil-gateway status; if using a named profile, include --account <name>. | | Tasks or chats do not start | Confirm the daemon is running, the workspace runtime is Run on my computer, and pencil-gateway doctor passes. | | Dispatch exits immediately with Invalid API key | The local gateway runs your local claude CLI; Pencil's saved BYO key applies to the direct server runtime and is not copied to the daemon. Fix or unset ANTHROPIC_API_KEY in the shell/service that starts the gateway, run /login inside claude if using a Claude subscription, then restart the gateway. | | Dispatch fails because the OAuth session expired and could not be refreshed | Run claude auth login as the same OS user that runs Pencil Gateway, then restart the gateway and select Resend. If a service definition sets CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_AUTH_TOKEN, refresh or unset that stale value first. pencil-gateway doctor now reports whether Claude Code is signed in. | | Agent work routes to the wrong machine | Check the agent operator user in Pencil and which paired user/account is running locally. | | Too much local load | Lower concurrency with pencil-gateway start --max-concurrency 1 or PENCIL_GATEWAY_MAX_CONCURRENCY=1. | | No tools appear | Run pencil-gateway doctor, confirm pairing, and restart the MCP client. | | Calls fail with 401 | Re-pair or revoke the stale device in Pencil settings and pair again. | | Service is installed but not running | Run pencil-gateway service status, then inspect the platform log above. Confirm the captured Node and Claude Code installation still exist. Re-run the installer after changing Node managers. | | Linux installer will not launch | Extract both files together, mark Install Pencil Gateway.desktop as trusted/Allow Launching, then double-click again. You can also run bash install-pencil-gateway.sh in a terminal. | | Update notification appears but update fails | Re-run the double-click installer. Global npm service installs intentionally require npm install -g @pencilink/gateway@latest followed by pencil-gateway service install. | | Tools call the wrong host | Re-pair with --mc-url <url> or PENCIL_MC_URL=<url>; commands use the URL saved in the selected config. | | Dangerous tools are missing | Enable PENCIL_MCP_ENABLE_DANGEROUS_TOOLS=1 on the Pencil server and start with --include-dangerous. | | A client asks for a remote MCP URL | Use SITE_URL/api/mcp with OAuth, or with an MCP-enabled API key if the client supports static bearer headers. |

Building from source

pnpm install builds dist/ automatically (via the prepare script), so after cloning you can go straight to the convenience scripts:

pnpm install    # installs deps + builds dist/

pnpm doctor     # sanity-check claude/server/token
pnpm pair       # pair against https://app.pencil.ink
pnpm start      # run the daemon

For a self-hosted Pencil running locally on port 3030, use the :local variants (they set PENCIL_MC_URL=http://localhost:3030 for you):

pnpm pair:local
pnpm start:local

Every command accepts extra flags appended directly, e.g.:

pnpm start --max-concurrency 4 --account work
pnpm pair --mc-url https://pencil.example.com

Available scripts: pnpm start, pnpm start:local, pnpm accounts (lists paired profiles), pnpm pair, pnpm pair:local, pnpm status, pnpm doctor, pnpm mcp, pnpm unpair. Rebuild manually anytime with pnpm build.