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

lark-for-claude

v1.7.1

Published

Feishu (Lark) channel plugin for Claude Code — route multiple groups to isolated Claude instances via a single bot

Readme

Feishu Channel for Claude Code

npm version license

A Feishu (Lark) channel plugin for Claude Code, built on the native Channel interface. Chat with Claude directly in Feishu — DMs, group chats, interactive cards.

English | 中文

Uses the MCP Channel protocol with WebSocket persistent connection — no public HTTPS endpoint needed.

npx lark-for-claude   # one-command install

Table of Contents


Architecture & Modes

The plugin operates in three modes, selected automatically based on runtime context:

┌─────────────────────────────────────────────────────────────────┐
│                      Mode Selection Flow                        │
│                                                                 │
│  claude-feishu launched                                         │
│       │                                                         │
│       ▼                                                         │
│  Is --channels feishu in ancestor process?                      │
│       │                                                         │
│    NO ──→ Passive Mode (no connection, tools only)              │
│    YES                                                          │
│       │                                                         │
│       ▼                                                         │
│  Can we start/connect to a Router?                              │
│       │                                                         │
│    YES ──→ Worker Mode (connects to Router via Unix socket)     │
│    NO  ──→ Channel Mode (direct Feishu WebSocket)               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Mode Comparison

| | Channel Mode | Worker Mode (via Router) | Passive Mode | |---|---|---|---| | Connection | Direct Feishu WebSocket | Unix socket → Router → Feishu WebSocket | None | | Use case | Single user / single project | Multi-user / multi-project | Non-channel Claude instances | | Message routing | All messages → this instance | chat_id → workdir → worker | N/A | | Auto-started | Fallback when Router fails | First claude-feishu auto-spawns Router | Always | | How many instances | 1 Claude per bot | N Claudes per bot | N/A |

Channel Mode (1:1)

┌─────────────┐
│  Feishu Bot  │──── WebSocket ────→ Claude Code Instance
└─────────────┘

Simplest setup. One bot, one Claude. All messages go to the single connected instance.

Worker Mode (1:N via Router)

┌─────────────┐                    ┌─ Claude Code (/path/to/project-a)
│  Feishu Bot  │──── WebSocket ──→ Router ──┼─ Claude Code (/path/to/project-b)
└─────────────┘                    └─ Claude Code (/path/to/project-c)
                                        ▲
                                   Unix socket

The Router holds the single Feishu WebSocket. Each Claude Code instance connects as a worker via Unix socket. Messages are routed by:

chat_id → groups[chat_id].workdir → registered worker (by cwd)

Key behaviors:

  • First claude-feishu auto-spawns the Router process
  • Subsequent instances auto-connect as workers
  • When all workers disconnect, Router auto-shuts down after 10s grace period
  • If Router fails to start, falls back to Channel Mode

Features

  • Multi-group routing — One Feishu bot serves multiple Claude Code instances, each in its own project
  • Auto-managed router — Router spawns on first launch, shuts down when all workers disconnect
  • Direct messages — Chat with Claude through Feishu DMs
  • Group chats — Add the bot to group chats with @mention support and custom trigger patterns
  • Access control — Allowlist-based user authorization and per-group policies
  • Permission cards — Interactive approve/deny cards for tool permission requests (✅ allow once / ✅✅ always allow / ❌ deny)
  • Confirm cards — Interactive confirmation cards for risky actions (✅ / ✅✅ / ❌ buttons + text reply)
  • Unanswered reminders — Auto-nudges at 30min / 60min / 120min if Claude hasn't replied
  • Reactions — Configurable emoji reactions on message receipt (default: 👍)
  • Message editing — Update previously sent messages (no push notification)
  • Smart connection — Only connects when launched as a Feishu channel
  • Graceful shutdown — Detects parent process exit via ppid polling
  • Worker auto-reconnect — Workers automatically reconnect to Router after disconnection
  • Log rotation — Automatic log rotation (5MB max, 3 backups) to prevent disk exhaustion

Prerequisites

  • Bun runtime
  • Claude Code CLI installed
  • A Feishu (or Lark) workspace with admin access to create apps

Quick Start

Step 1: Create a Feishu App

  1. Go to Feishu Open Platform (or Lark Open Platform)

  2. Create a Custom App (enterprise internal app)

  3. Note the App ID (cli_...) and App Secret

  4. Under Events & Callbacks, configure two separate tabs:

    Event Configuration tab:

    • Switch to Using persistent connection (WebSocket mode)
    • Add event: im.message.receive_v1

    Callback Configuration tab:

    • Switch to Using persistent connection (WebSocket mode)
    • Add callback: card.action.trigger (required for confirm/permission card buttons)
  5. Under Permissions & Scopes, add:

    | Permission | Purpose | |---|---| | im:message | Send messages | | im:message.receive_v1 | Receive messages | | im:message.p2p_msg:readonly | Read DM messages | | im:message.group_at_msg:readonly | Read group @mentions | | im:chat:readonly | Read chat metadata | | im:resource | Download and upload attachments |

  6. Publish the app version so permissions take effect

Step 2: Install the Plugin

npx lark-for-claude

This clones the repo, installs dependencies, registers the Claude Code plugin, and creates the claude-feishu shortcut.

git clone https://github.com/jbts6/lark-for-claude.git
cd lark-for-claude
bun install
claude plugin marketplace add .
claude plugin install feishu@feishu-local

Step 3: Start Claude Code with Feishu Channel

claude-feishu

On subsequent launches, claude-feishu automatically resumes the session named after the current directory. If no matching session is found, an interactive session picker opens.

Full command equivalent:

claude --dangerously-load-development-channels plugin:feishu@feishu-local

Step 4: Configure Credentials

In your Claude Code terminal:

claude-feishu auth cli_YOUR_APP_ID YOUR_APP_SECRET

Credentials are stored in ~/.claude/channels/feishu/.env (mode 600).

Step 5: Authorize Users

Add users to the allowlist by their Feishu open_id:

claude-feishu access allow ou_xxxxxxxxxxxxxxxxxxxx

To find a user's open_id, check the debug log after they send a message to the bot:

tail -5 ~/.claude/channels/feishu/debug.log

You can also set a default chat ID for outbound messages (optional):

claude-feishu auth chat-id oc_xxxxxxxxxxxxxxxxxxxx

You're ready — authorized users can now message the bot and Claude will respond.


Multi-Group Router Setup

1. Configure Group Workdirs

Add workdir to each group in ~/.claude/channels/feishu/access.json:

{
  "groups": {
    "oc_groupA": {
      "requireMention": true,
      "allowFrom": [],
      "workdir": "/path/to/project-a"
    },
    "oc_groupB": {
      "requireMention": true,
      "allowFrom": [],
      "workdir": "/path/to/project-b"
    }
  },
  "defaultWorkdir": "/path/to/default-project"  // DMs and unconfigured groups route here
}

2. Start Claude Code Instances

In separate terminals, start Claude in each project directory:

cd /path/to/project-a && claude-feishu   # first: spawns router + connects as worker
cd /path/to/project-b && claude-feishu   # subsequent: connects to existing router
cd /path/to/project-c && claude-feishu   # subsequent: connects to existing router

The first instance auto-spawns the Router. Subsequent instances connect as workers. The Router routes incoming messages by chat_id → workdir → connected worker.

3. Manual Router Start (Optional)

bun run router

4. Check Router Status

kill -USR1 $(pgrep -f 'bun router.ts')
cat ~/.claude/channels/feishu/router-debug.log | tail -10

Access Management

All access commands are run in your Claude Code terminal via claude-feishu access.

Check Status

claude-feishu access

DM Policies

| Policy | Behavior | |---|---| | allowlist (default) | Only users in allowFrom can send DMs; others are silently dropped | | disabled | All DMs dropped |

claude-feishu access policy allowlist

Manage Users

# Allow a user by open_id
claude-feishu access allow ou_xxxxxxxxxxxxxxxxxxxx

# Remove a user
claude-feishu access remove ou_xxxxxxxxxxxxxxxxxxxx

Group Chats

Groups are off by default. The bot must be added to the group by a group admin first.

# Enable a group (responds on @mention only)
claude-feishu access group add oc_xxxxxxxxxxxxxxxxxxxx

# Respond to all messages (no @mention needed)
claude-feishu access group add oc_xxxxxxxxxxxxxxxxxxxx --no-mention

# Restrict to specific users within the group
claude-feishu access group add oc_xxxxxxxxxxxxxxxxxxxx --allow ou_id1,ou_id2

# Remove a group
claude-feishu access group rm oc_xxxxxxxxxxxxxxxxxxxx

Delivery Settings

# React to received messages with an emoji (default: Get)
claude-feishu access set ackReaction Get

# Set max characters per message chunk
claude-feishu access set textChunkLimit 4096

# Custom mention patterns for group chats
claude-feishu access set mentionPatterns ["@claude","@assistant"]

File Layout

~/.claude/channels/feishu/
├── .env              # App credentials (FEISHU_APP_ID, FEISHU_APP_SECRET, FEISHU_APP_CHAT_ID)
├── access.json       # Access control state (auto-managed)
├── debug.log         # Server debug log (auto-rotated at 5MB, 3 backups)
├── router-debug.log  # Router debug log (auto-rotated at 5MB, 3 backups)
└── router.sock       # Unix socket for worker-router IPC

Multi-Device Sync

To use the same Feishu bot on multiple devices (e.g., office desktop + home laptop), you only need to copy two files:

What to Copy

| File | Contains | Must Sync? | |---|---|---| | .env | App credentials (FEISHU_APP_ID, FEISHU_APP_SECRET, FEISHU_APP_CHAT_ID) | ✅ Yes — without this, the bot can't connect | | access.json | Access control (allowFrom, groups, policies) | ✅ Yes — without this, all users appear unauthorized | | debug.log | Debug log | ❌ No — auto-created and auto-rotated | | router-debug.log | Router debug log | ❌ No — auto-created and auto-rotated | | router.sock | Unix socket | ❌ No — auto-created at runtime |

How to Sync

Option 1: Manual copy

# On the source device
scp ~/.claude/channels/feishu/.env ~/.claude/channels/feishu/access.json target-device:~

# On the target device
mkdir -p ~/.claude/channels/feishu
mv ~/.env ~/.claude/channels/feishu/.env
mv ~/access.json ~/.claude/channels/feishu/access.json
chmod 600 ~/.claude/channels/feishu/.env

Option 2: Symlink to a synced folder (e.g., Dropbox, iCloud, Syncthing)

# Create a synced config folder
mkdir -p ~/Sync/feishu-config

# Copy existing config into it
cp ~/.claude/channels/feishu/.env ~/.claude/channels/feishu/access.json ~/Sync/feishu-config/

# Replace original files with symlinks
mv ~/.claude/channels/feishu/.env ~/.claude/channels/feishu/.env.bak
mv ~/.claude/channels/feishu/access.json ~/.claude/channels/feishu/access.json.bak
ln -s ~/Sync/feishu-config/.env ~/.claude/channels/feishu/.env
ln -s ~/Sync/feishu-config/access.json ~/.claude/channels/feishu/access.json

Option 3: Use FEISHU_STATE_DIR to point to a synced location

# Add to your shell profile (~/.bashrc, ~/.zshrc, etc.)
export FEISHU_STATE_DIR="$HOME/Sync/feishu-config"

This makes the entire state directory live in your synced folder — no symlinks needed.

Important Notes

  • Only one device should run the bot at a time in Channel mode. Two simultaneous WebSocket connections from the same app may cause message loss or duplication.
  • Router mode is safe for multi-device: each device runs its own Worker, and the Router handles deduplication. But only one device should run the Router.
  • access.json changes are not auto-synced: if you add a user on device A, device B won't see it until the file syncs. The 2-second access cache means changes take effect quickly after sync.
  • workdir paths in access.json are absolute: /home/user/project-a on one device may not exist on another. Use consistent paths or adjust per device.

Environment Variables

| Variable | Required | Description | |---|---|---| | FEISHU_APP_ID | Yes | Feishu app ID (cli_...) | | FEISHU_APP_SECRET | Yes | Feishu app secret | | FEISHU_ENCRYPT_KEY | No | Event payload encryption key | | FEISHU_APP_CHAT_ID | No | Default chat ID for outbound messages (fallback when no chat_id is specified) | | FEISHU_ACCESS_MODE | No | Set to static to use allowlist-only mode (no runtime access.json writes) | | FEISHU_STATE_DIR | No | Override state directory path (default: ~/.claude/channels/feishu/) |


How It Works

Smart Connection Detection

The plugin detects whether it's running under a Feishu channel Claude instance by walking up the process tree and checking for --dangerously-load-development-channels with feishu in the ancestor's command line. Non-channel Claude instances (e.g., regular claude or claude --channels plugin:discord@...) skip the Feishu WebSocket connection entirely, keeping the MCP tools available without unnecessary remote connections.

Orphan Protection

When the parent Claude process exits, the plugin detects the ppid change within 2 seconds and shuts down gracefully. This prevents orphaned bun server.ts processes from consuming 100% CPU — a workaround for Bun not reliably firing stdin end/close events on broken Unix domain sockets.

Worker Auto-Reconnect

When a worker's Unix socket connection to the Router breaks, it automatically attempts to reconnect after 3 seconds. This handles temporary Router restarts and network glitches without requiring manual intervention.

Access Caching

The access.json file is cached with a 2-second TTL based on file modification time. This avoids redundant readFileSync calls on every message while still reflecting configuration changes within seconds.

Log Rotation

Debug logs (debug.log, router-debug.log) are automatically rotated when they exceed 5MB. Up to 3 backup files are kept (debug.log.1, debug.log.2, debug.log.3). Rotation is checked lazily every 100 writes to minimize overhead.


Testing & Development

bun test              # Run tests (65 tests)
bun run lint          # Check code style with Biome
bun run lint:fix      # Auto-fix code style issues
bun run format        # Format code with Biome
bun run typecheck     # TypeScript type checking
bun run check         # Full check: typecheck + lint + test

Tests cover: access control (gate logic), text chunking, mention detection, permission reply parsing (including yy/yesyes for always-allow), confirm code generation, chat authorization, chat ID resolution and fallback, message parsing, attachment info, timestamp formatting, log rotation, router workdir resolution, and access caching.


Security

  • Credentials stored with chmod 600 — only the owner can read them
  • State directory uses chmod 700
  • Router Unix socket uses chmod 600
  • Confirm codes use 8-byte cryptographic randomness (crypto.randomBytes)
  • Access mutations can only be made from the Claude Code terminal — never from channel messages (prompt injection protection)
  • Chat allowlist prevents unauthorized message delivery
  • Log rotation prevents disk exhaustion from unbounded log growth
  • Log output redacts sensitive IDs (open_id, chat_id)
  • PID validation prevents misidentifying ancestor processes
  • Regex escaping prevents injection in CLI pattern handling
  • Pending permissions and confirms auto-expire after 1 hour

AI Automated Deployment Guide

This section provides a condensed, step-by-step guide for AI agents to deploy and configure the Feishu Channel plugin automatically.

Prerequisites Check

# Verify Bun is installed
bun --version || (echo "INSTALL bun: curl -fsSL https://bun.sh/install | bash" && exit 1)

# Verify Claude Code is installed
claude --version || (echo "INSTALL claude-code first" && exit 1)

One-Command Deploy

npx lark-for-claude

This handles: clone → install → plugin register → claude-feishu shortcut.

Credential Configuration

# Write credentials to state directory
mkdir -p ~/.claude/channels/feishu
cat > ~/.claude/channels/feishu/.env << 'EOF'
FEISHU_APP_ID=cli_YOUR_APP_ID
FEISHU_APP_SECRET=YOUR_APP_SECRET
FEISHU_APP_CHAT_ID=oc_YOUR_CHAT_ID
EOF
chmod 600 ~/.claude/channels/feishu/.env

Or use the CLI command inside a Claude Code session:

claude-feishu auth cli_YOUR_APP_ID YOUR_APP_SECRET
claude-feishu auth chat-id oc_YOUR_CHAT_ID

Multi-Group Router Configuration

Write ~/.claude/channels/feishu/access.json:

{
  "dmPolicy": "allowlist",
  "allowFrom": [],
  "p2pChats": {},
  "groups": {
    "oc_GROUP_ID_A": {
      "requireMention": true,
      "allowFrom": [],
      "workdir": "/absolute/path/to/project-a"
    },
    "oc_GROUP_ID_B": {
      "requireMention": false,
      "allowFrom": [],
      "workdir": "/absolute/path/to/project-b"
    }
  },
  "ackReaction": "Get",
  "defaultWorkdir": "/absolute/path/to/default-project"
}

Key fields:

  • groups[chat_id].workdir — maps a Feishu group to a project directory
  • defaultWorkdir — fallback for DMs and groups without explicit workdir
  • dmPolicyallowlist (default) or disabled
  • ackReaction — emoji code for read receipts (default: Get)

Launch Commands

# Single project (Channel mode or auto-Router)
cd /path/to/project && claude-feishu

# Multi-project (Router mode)
cd /path/to/project-a && claude-feishu &  # first: spawns Router
cd /path/to/project-b && claude-feishu &  # connects as worker

User Authorization

Pre-authorize users by open_id:

claude-feishu access allow ou_xxxxxxxxxxxxxxxxxxxx

To find a user's open_id, check the debug log after they send a message:

tail -5 ~/.claude/channels/feishu/debug.log

Verification Checklist

# 1. Plugin installed
claude plugin list | grep feishu

# 2. Credentials configured
test -f ~/.claude/channels/feishu/.env && echo "OK" || echo "MISSING"

# 3. Tests pass
cd $(dirname $(which claude-feishu))/.. && bun test

# 4. Router running (multi-group mode)
test -S ~/.claude/channels/feishu/router.sock && echo "Router active" || echo "No router"

# 5. Debug logs
tail -5 ~/.claude/channels/feishu/debug.log
tail -5 ~/.claude/channels/feishu/router-debug.log

Troubleshooting

| Symptom | Check | Fix | |---|---|---| | Bot not responding | debug.log for errors | Verify credentials in .env | | Router not starting | router-debug.log | Check if port/socket is in use | | Worker not connecting | debug.log for "worker" entries | Verify Router socket exists | | DMs silently dropped | dmPolicy in access.json | Must be allowlist, not disabled; add user to allowFrom | | Group messages ignored | Group in access.json? | claude-feishu access group add <chat_id> | | Card buttons not working | Callback configured? | Add card.action.trigger in Feishu app | | No default chat for outbound | FEISHU_APP_CHAT_ID set? | claude-feishu auth chat-id <chat_id> |


License

MIT