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

@abconvert-tony/openclaw-linear-agent

v0.1.3

Published

Linear OAuth + AgentSessionEvent webhook bridge for OpenClaw, with registered agent tools that drive the Linear session lifecycle.

Readme

linear-agent

OpenClaw plugin that bridges a Linear OAuth app to a local OpenClaw agent. Receives AgentSessionEvent webhooks, dispatches the prompt to a configured OpenClaw agent, and exposes a small set of registerTool factories so the agent drives the Linear session lifecycle directly (final reply, error, elicitation, external URL attachment).

Routes

The plugin registers three HTTP routes under /linear-agent on your local gateway. Externally they sit behind whatever ingress you use (Tailscale Funnel, Cloudflare Tunnel, ngrok, raw reverse proxy, etc.); we'll call that base URL <public-base> below. If that ingress namespaces users with a path prefix (shared host, /<user>/*), include it as <prefix> everywhere — leave it empty if you have your own host.

| Method | Path | Purpose | |--------|-------------------------------------------------|-----------------------------------------------| | GET | <public-base>/<prefix>/linear-agent/connect | Kick off Linear OAuth (actor=app) | | GET | <public-base>/<prefix>/linear-agent/callback | Exchange code for tokens, persist to disk | | POST | <public-base>/<prefix>/linear-agent | AgentSessionEvent receiver (HMAC verified) |

The plugin itself only sees /linear-agent/... — the ingress is responsible for stripping <prefix> before forwarding. The same <prefix> (or its absence) must appear in all three places — the Linear app's Redirect URI, the Linear app's Webhook URL, and the linearRedirectUri plugin config — and must match byte-for-byte. A mismatch on the redirect URI surfaces as Linear's "Invalid redirect_uri parameter for the application" error.

The ingress must also forward both /.../linear-agent and /.../linear-agent/ (with and without trailing slash) to the gateway. Linear normalizes some delivery URLs with a trailing slash; if your reverse proxy only matches the no-slash form, webhooks return 404 from the proxy and never reach the plugin.

Agent-callable tools

Registered via api.registerTool. They auto-appear only on agent runs that originated from a Linear webhook (i.e. when a Linear session is bound to the active sessionKey).

| Tool | Maps to | Use | |------|---------|-----| | linear_post_thought | AgentActivityCreateInput { type: "thought" } | Non-terminal: mid-run narration. Set ephemeral: true for fleeting status that the next activity replaces | | linear_post_action | AgentActivityActionContent { action, parameter, result? } | Non-terminal: structured "action" card. Post once without result to announce work-in-progress, then again with the same action+parameter and a filled-in result when done | | linear_post_comment | CommentCreateInput { body, issueId, parentId? } | Non-terminal: post a real Linear comment (visible in the issue thread, not just the agent panel). Defaults to threading under the comment that triggered this turn; pass threadUnderSourceComment: false for a top-level issue comment | | linear_post_response | AgentActivityCreateInput { type: "response" } | Terminal: the agent's final reply for this turn | | linear_post_error | AgentActivityCreateInput { type: "error" } | Terminal: surface a failure to the requester | | linear_post_elicitation | AgentActivityCreateInput { type: "elicitation" } | Terminal: pause and ask the user for more info | | linear_update_issue | IssueUpdateInput (subset) | Update state, assignee/delegate, priority, labels, title/description, due date. stateType resolves to a stateId via the team's workflow states; issueId defaults to the bound issue | | linear_set_session_plan | AgentSessionUpdateInput.plan | Replace the agent's plan checklist for this session; each step has content and status: pending\|inProgress\|completed\|canceled | | linear_attach_external_url | AgentSessionUpdateInput.addedExternalUrls | Attach a PR/preview/dashboard URL; Linear renders it as a session button and tracks downstream updates |

Prerequisites

  • A running OpenClaw gateway (see the OpenClaw docs) with an agent in agents.list[] ready to handle Linear events.
  • A public ingress for that gateway (Tailscale Funnel, Cloudflare Tunnel, ngrok, reverse proxy, etc.) — Linear webhooks and the OAuth callback both need a reachable URL.

Setup

1. Register a Linear OAuth application

Linear → SettingsAPIOAuth applicationsNew application.

Decide your <prefix> first. If your ingress namespaces users with a path (e.g. https://shared.example.com/jacklyn/* → her gateway), the prefix is /jacklyn and every URL below must include it. On the shared abconvert host (abconvert-spark.tailc2b7f2.ts.net) the prefix is your own first name (/tony, /jacklyn, …) — the segment that routes traffic to your gateway. If you have your own host, leave the prefix empty. The Redirect URI you register here, the Webhook URL, and the linearRedirectUri in step 2 must all match exactly.

  • Redirect URI: <public-base>/<prefix>/linear-agent/callback (e.g. https://abconvert-spark.tailc2b7f2.ts.net/jacklyn/linear-agent/callback)
  • Webhook URL: <public-base>/<prefix>/linear-agent (e.g. https://abconvert-spark.tailc2b7f2.ts.net/jacklyn/linear-agent)
  • Enable webhooks; subscribe to Agent session events
  • Allowed scopes: must be a superset of whatever you set in linearScopes later. The default linearScopes is read,write,app:assignable,app:mentionable; if the Linear app isn't configured with app:assignable and app:mentionable, OAuth silently grants fewer scopes and delegateOnCreate later fails.
  • Copy the client id, client secret, and webhook signing secret

Admin permissions on the workspace are required to install with actor=app.

Make sure your ingress proxies both /<prefix>/linear-agent and /<prefix>/linear-agent/ (trailing slash) to the gateway. Linear sometimes delivers webhooks to the trailing-slash form; if the proxy only matches one, those deliveries return 404 from the proxy and never reach the plugin. Quick check: curl -i -X POST <public-base>/<prefix>/linear-agent/ should return 401 Unauthorized from the plugin (good — handler reachable), not 404 from the proxy.

2. Configure the plugin

Edit ~/.openclaw/openclaw.json and set plugins.entries.linear-agent.config:

{
  "plugins": {
    "allow": ["linear-agent"],
    "entries": {
      "linear-agent": {
        "enabled": true,
        "config": {
          "agentId": "dev",
          "linearClientId": "${LINEAR_CLIENT_ID}",
          "linearClientSecret": "${LINEAR_CLIENT_SECRET}",
          "linearWebhookSecret": "${LINEAR_WEBHOOK_SECRET}",
          "linearRedirectUri": "<public-base>/<prefix>/linear-agent/callback"
        }
      }
    }
  }
}

${ENV_VAR} interpolation is supported — keep secrets in ~/.openclaw/.env rather than the JSON.

The agentId here must match the id of an entry in agents.list[] — that's the OpenClaw agent the plugin will dispatch Linear events to. Step 2b uses the same id.

Optional config keys: linearScopes (default read,write,app:assignable,app:mentionable), linearTokenStorePath (default ~/.openclaw/workspace/.pi/linear-agent-tokens.json), historyLimit (default 20), startOnCreate, delegateOnCreate, strictAddressing + mentionHandle (only run prompted events that explicitly @-mention the agent; useful when humans and the agent share an issue thread).

2b. Allow the plugin's tools on the bound agent

Plugin-registered tools are gated by the gateway's tool policy. The configured agent (agentId) must have group:plugins in its allowlist, otherwise the linear_* tools are filtered out before the model sees them and the run will fall back to the plain-text reply path.

The simplest setup — a single alsoAllow on the agent and no global tools.allow filter:

{
  "tools": { "profile": "full" },
  "agents": {
    "list": [
      {
        "id": "dev",
        "tools": { "alsoAllow": ["exec", "group:plugins"] }
      }
    ]
  }
}

Gotcha: a global tools.allow: ["exec"] (or any other narrow list) acts as an AND filter applied before the agent step, so it strips plugin tools even if the agent's alsoAllow includes group:plugins. Either drop the global tools.allow (as above) or extend it to ["exec", "group:plugins"].

3. Restart the gateway

Look for the load line:

linear-agent: routes registered under /linear-agent (connect, callback, webhook); tools: linear_post_{thought,action,response,error,elicitation}, linear_update_issue, linear_set_session_plan, linear_attach_external_url

4. Install the agent into your Linear workspace

Required final step — and one only the human can do. No webhook will fire and no tool call will succeed until OAuth has been completed in a browser. If you are an AI assistant helping with this setup, stop here and ask the user to open the URL below in their browser; you cannot complete the OAuth handshake on their behalf.

Have the user open this URL in a browser and approve the app in Linear:

<public-base>/<prefix>/linear-agent/connect

Concrete example for the shared abconvert host:

https://abconvert-spark.tailc2b7f2.ts.net/jacklyn/linear-agent/connect

(Substitute the user's own <prefix> — typically their first name. See §1.)

After they approve in Linear, tokens write to the configured store path (0600) and they'll see "Linear agent installed. You can close this tab." At that point the integration is live and webhooks will start being delivered.

5. Try it

Mention or delegate to the agent in a Linear issue. Expected sequence:

  1. Webhook POST arrives with linear-signature; HMAC-SHA256 verified against linearWebhookSecret. Stale or unsigned payloads are rejected.
  2. Plugin posts a quick thought activity (latency ack).
  3. The configured OpenClaw agent runs with the issue + prompt + recent session activities as its message.
  4. The agent calls one of the linear_post_* tools as its terminal step; that becomes the visible reply. If it produced a PR, it should also call linear_attach_external_url first.
  5. If the agent ends without calling a terminal tool, the plugin falls back to extracting reply text and posting it as a response, or posts a generic error if nothing is extractable.

Troubleshooting

| Symptom | Likely cause | |---------|--------------| | Webhook returns 401 | Wrong or missing linearWebhookSecret; the value must match the signing secret on the Linear app. | | Webhook returns 404 from the proxy (not the plugin) | Trailing-slash mismatch — the ingress only routes /<prefix>/linear-agent and not /<prefix>/linear-agent/ (or vice versa). Either fix the proxy to accept both, or edit the Linear webhook URL to the form your proxy serves. Confirm with curl -i -X POST <public-base>/<prefix>/linear-agent/401 means the handler is reachable, 404 from Server: Caddy/nginx means the proxy never forwarded. | | OAuth fails with "Invalid redirect_uri parameter" | The redirect_uri the plugin sends doesn't byte-match a Redirect URI registered on the Linear OAuth app. Most often a missing /<prefix> — check that linearRedirectUri includes it and the same string is in the Linear app's Redirect URI list. | | Webhook never fires | The Linear app isn't subscribed to Agent session events, or the public webhook URL doesn't reach the gateway. | | Agent runs but linear_* tools aren't callable | The bound agent's tool policy is filtering them out. See step 2b — group:plugins must be in the agent's allowlist, and any narrow global tools.allow will AND-filter plugin tools out before the agent step. | | delegateOnCreate / scope-related errors | The Linear app's allowed scopes don't include what you requested via linearScopes. Add app:assignable and app:mentionable to the app and re-install. | | OAuth refresh fails / tokens disappear | A PermissionChange or OAuthApp revoke/uninstall event arrived; the plugin clears the token store on those. Re-run <public-base>/linear-agent/connect. | | Replies arrive as plain text instead of structured activities | The agent didn't call a terminal linear_post_* tool, so the plugin fell back to extracting reply text. Usually means the tools weren't visible — see the policy row above. |

Install

openclaw plugins install npm:@abconvert-tony/openclaw-linear-agent
openclaw gateway restart

openclaw plugins install adds linear-agent to plugins.allow automatically. You still need to fill in plugins.entries.linear-agent.config (Setup → step 2), allow plugin tools on the bound agent (step 2b), and run the OAuth install (step 4) — those are per-deployment and don't ship with the package.

Don't skip step 4. Even after the gateway loads the plugin, no Linear webhook will be processed until a human opens <public-base>/<prefix>/linear-agent/connect in a browser and approves the app. AI assistants helping with installation: ask the user to do this — you can't do it for them.

Pin the resolved version so subsequent openclaw plugins update stays put:

openclaw plugins install npm:@abconvert-tony/openclaw-linear-agent --pin

Updating

openclaw plugins update @abconvert-tony/openclaw-linear-agent
openclaw gateway restart

Pulls the latest published version from npm and replaces the installed copy. Pinned installs hold their version until you re-pin to a new one.

Releasing (maintainers)

git commit -am "..."          # land your changes first
npm run release:patch         # bump version, build, publish
git push --follow-tags

release:minor and release:major exist for non-patch bumps. prepublishOnly rebuilds dist/ before each publish, so users always get fresh compiled output.

Local development

npm install
npm run build
openclaw plugins install -l .   # link this directory
# edit, then: npm run build && restart the gateway

Token store

OAuth tokens persist at ~/.openclaw/workspace/.pi/linear-agent-tokens.json (override via linearTokenStorePath). On PermissionChange/OAuthApp revoke or uninstall events the file is cleared automatically.