Feishu/Lark Claude Code Bridge
Skill by ara.so — Claude Code Skills collection.
What It Does
lark-channel-bridge connects Feishu (Lark) messenger with your local Claude Code CLI. Messages sent to the bot are forwarded to claude running in a working directory you control, with responses streaming back to Lark in real-time cards. Features:
- Streaming cards: Claude's output updates live in a single Lark card
- Per-chat sessions: each chat/topic maintains its own Claude session
- Multiple workspaces: switch between project directories with
/ws - Images & files: send media directly; Claude reads downloaded paths
- Preemption: new messages interrupt running requests
- Access control: optional allowlists for users, chats, and admin commands
Installation
Prerequisites
- Node.js >= 20
claudeCLI installed and authenticated: https://docs.anthropic.com/en/docs/claude-code/quickstart- Feishu/Lark PersonalAgent app (created via QR wizard on first run)
Install Globally
npm install -g lark-channel-bridge
# or
pnpm add -g lark-channel-bridge
Important: Always install globally before using daemon/service commands. npx paths are ephemeral and break background services.
First-Time Setup
lark-channel-bridge run
On first run without config:
- Terminal displays a QR code
- Scan with Feishu/Lark app
- Select or create a PersonalAgent app
- Credentials saved to
~/.lark-channel/config.json(mode 600)
CLI Commands
Process Management
# Foreground (interactive)
lark-channel-bridge run [-c <config>]
# List all running bridges
lark-channel-bridge ps
# Kill a specific bridge
lark-channel-bridge kill <id|#>
# Help
lark-channel-bridge --help
Daemon/Service Management
Background OS-managed service with auto-restart:
# Install and start daemon
lark-channel-bridge start
# Stop daemon
lark-channel-bridge stop
# Restart in place
lark-channel-bridge restart
# Check status (pid, logs, exit code)
lark-channel-bridge status
# Remove service definition
lark-channel-bridge unregister
Platform mapping:
- macOS: launchd user agent at
~/Library/LaunchAgents/ai.lark-channel-bridge.bot.plist - Linux: systemd user unit at
~/.config/systemd/user/lark-channel-bridge.bot.service(runloginctl enable-linger $USERfor persistence across logout) - Windows: Task Scheduler task
LarkChannelBridge.Botwith launcher at~/.lark-channel/daemon-launcher.cmd
Logs: ~/.lark-channel/logs/daemon-stdout.log and daemon-stderr.log
Migration (for pre-0.1.11 users)
lark-channel-bridge migrate
Moves data from old paths (~/.config/lark-channel-bridge/, ~/.cache/lark-channel-bridge/) to ~/.lark-channel/.
In-Chat Commands
Send these inside Feishu/Lark chats:
| Command | Description |
|---|---|
/new, /reset |
Clear current chat's Claude session |
/cd <path> |
Switch working directory (resets session) |
/ws list |
List named workspaces (interactive card) |
/ws save <name> |
Save current directory as workspace |
/ws use <name> |
Switch to named workspace |
/ws remove <name> |
Delete workspace |
/status |
Show current directory/session/agent |
/config |
Adjust preferences (reply style, access control, timeouts) |
/stop |
Terminate running Claude process |
/timeout [N|off|default] |
Set idle watchdog (minutes) for session |
/ps |
List all running bridge processes |
/exit <id|#> |
Stop a bridge process |
/reconnect |
Force WebSocket reconnect |
/doctor [description] |
Self-diagnose using recent logs |
/help |
Show help card |
Any other /xxx |
Forwarded to Claude verbatim |
Reply behavior:
- DM: bot replies to all messages
- Group: bot only replies when
@-mentioned (default since 0.1.22) - Change via
/config→ "Require @bot in groups"
Configuration
Config File Structure
~/.lark-channel/config.json:
{
"appId": "cli_xxxxxxxxxxxxxxxx",
"appSecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"preferences": {
"access": {
"allowedUsers": [],
"allowedChats": [],
"admins": []
},
"requireAtInGroups": true,
"defaultTimeoutMinutes": 0,
"replyStyle": "streaming",
"showToolCalls": true
}
}
Access Control
Configure via /config command in Feishu or edit JSON directly.
Scenario: Personal use only
{
"preferences": {
"access": {
"allowedUsers": ["ou_your_open_id_here"],
"allowedChats": [],
"admins": []
}
}
}
Scenario: Team with restricted admins
{
"preferences": {
"access": {
"allowedUsers": ["ou_user1", "ou_user2", "ou_user3"],
"allowedChats": [],
"admins": ["ou_admin_open_id"]
}
}
}
Scenario: Specific groups only
{
"preferences": {
"access": {
"allowedUsers": [],
"allowedChats": ["oc_group1_id", "oc_group2_id"],
"admins": ["ou_admin_open_id"]
}
}
}
Finding IDs: Have users message the bot, then:
grep '"event":"enter"' ~/.lark-channel/logs/$(date +%Y-%m-%d).log | tail -5
Each line contains chatId and senderId (user's open_id).
Notes:
- Empty field = unrestricted
- DMs exempt from chat allowlist
- Changes apply on next message (no restart)
- Admin commands restricted:
/account,/config,/exit,/reconnect,/doctor,/cd,/ws
Data Directories
| Path | Content |
|---|---|
~/.lark-channel/config.json |
App credentials (mode 600) |
~/.lark-channel/sessions.json |
Session IDs, working directories per chat |
~/.lark-channel/workspaces.json |
Named workspace map |
~/.lark-channel/processes.json |
Process registry for ps/stop |
~/.lark-channel/media/<chatId>/ |
Downloaded files (24h cleanup) |
~/.lark-channel/logs/YYYY-MM-DD.log |
Structured logs (7-day retention, configurable via LARK_CHANNEL_LOG_DAYS) |
Common Patterns
Running as Background Service
# Install globally first
npm install -g lark-channel-bridge
# Start daemon with auto-restart
lark-channel-bridge start
# Check status
lark-channel-bridge status
# View logs
tail -f ~/.lark-channel/logs/$(date +%Y-%m-%d).log
Managing Multiple Workspaces
In Feishu chat:
/cd /path/to/project-a
/ws save project-a
/cd /path/to/project-b
/ws save project-b
/ws list
# Click button or:
/ws use project-a
Session history is preserved per workspace.
Handling Timeouts
Global default (applies to new sessions):
/config
# Set "Default timeout (minutes)" to 10
Per-session override:
/timeout 5 # 5 minutes for this chat
/timeout off # disable for this chat
/timeout default # revert to global default
Watchdog kills Claude if no output for N minutes; card annotated with ⏱ N min no response, auto-terminated.
Sending Files to Claude
- Upload image/file directly in Feishu chat
- Bot downloads to
~/.lark-channel/media/<chatId>/ - File path passed to Claude CLI
- Files auto-deleted after 24h
Diagnosing Issues
Bot not responding:
/status
# Check if session/directory valid
/doctor The bot stopped replying after I changed directories
# Claude analyzes recent logs and suggests fixes
/reconnect
# Force WebSocket reconnect if network interrupted
Check logs:
tail -f ~/.lark-channel/logs/$(date +%Y-%m-%d).log | jq
List running processes:
lark-channel-bridge ps
# or in Feishu:
/ps
Troubleshooting
Bot Silent / No Responses
Cause: claude CLI not authenticated or session points to nonexistent directory.
Fix:
/status # inspect current state
/new # reset session
/cd /valid/path # switch to valid directory
Verify Claude CLI works:
cd /your/project
claude "test message"
Frozen Card / No Updates
Cause: Claude subprocess hung; idle watchdog may have triggered.
Fix:
/stop # terminate current run
/timeout 10 # enable 10-minute watchdog
Adjust global default via /config.
Image Not Recognized
Cause: Old version (pre-0.1.0) had filename dedup bug.
Fix:
npm update -g lark-channel-bridge
# or
pnpm update -g lark-channel-bridge
Daemon Not Starting After Reboot (Linux)
Cause: User services disabled after logout.
Fix:
loginctl enable-linger $USER
lark-channel-bridge restart
Multiple Instances Fighting for Events
Cause: Lark routes events randomly to multiple WebSocket connections for same app.
Fix:
lark-channel-bridge ps
lark-channel-bridge kill <id> # kill duplicates
# or in Feishu:
/ps
/exit <id>
run command prompts if existing process detected.
Logs Too Large
Default: 7-day retention, daily rotation.
Override:
export LARK_CHANNEL_LOG_DAYS=3
lark-channel-bridge restart
Or clean manually:
find ~/.lark-channel/logs/ -name "*.log" -mtime +3 -delete
Environment Variables
| Variable | Default | Description |
|---|---|---|
LARK_CHANNEL_LOG_DAYS |
7 |
Log retention days; older logs pruned at startup |
Integration Examples
Systemd Service (Linux)
Generated automatically by start, but can be customized at:
~/.config/systemd/user/lark-channel-bridge.bot.service
Example customization:
[Service]
Environment="LARK_CHANNEL_LOG_DAYS=3"
Environment="NODE_ENV=production"
Restart=always
RestartSec=10
After editing:
systemctl --user daemon-reload
systemctl --user restart lark-channel-bridge.bot
macOS launchd (auto-generated)
~/Library/LaunchAgents/ai.lark-channel-bridge.bot.plist
Customize environment variables:
<key>EnvironmentVariables</key>
<dict>
<key>LARK_CHANNEL_LOG_DAYS</key>
<string>3</string>
</dict>
Reload:
launchctl unload ~/Library/LaunchAgents/ai.lark-channel-bridge.bot.plist
launchctl load ~/Library/LaunchAgents/ai.lark-channel-bridge.bot.plist
Docker Deployment (community pattern)
Not officially supported but community users run:
FROM node:20-slim
RUN npm install -g lark-channel-bridge
VOLUME /root/.lark-channel
CMD ["lark-channel-bridge", "run"]
Mount config as volume:
docker run -d \
-v ~/.lark-channel:/root/.lark-channel \
--name feishu-bridge \
your-image
Best Practices
- Always install globally before using
start/daemon commands - Use named workspaces for frequently-switched projects
- Enable timeouts if working with large codebases (Claude may hang on complex operations)
- Lock down access in shared/team environments via
/config - Monitor logs during initial setup:
tail -f ~/.lark-channel/logs/$(date +%Y-%m-%d).log - Run as daemon for persistent bot availability:
lark-channel-bridge start - Check
/statusfirst when troubleshooting response issues - Use
/doctorfor self-diagnosis with automatic log analysis
License
MIT