lark-im
Skill1 starsChanged 6 months agoMITView on GitHub
Bridge IM platforms (Feishu/Telegram/QQ/DingTalk/WeCom/WeChat) to AI coding assistants (Claude/Codex/CodeBuddy) with model switching, privacy isolation, and tool-level access control
- Reads credentials
- Deletes or force-pushes
- Installs packages
- Commits and pushes
10 other projects use a version of this.
Adopted by
Most recently taken up by
- leishifu666/skillstheir own version, first seen Oct 9, 2026
- loongclaw-ai/loongclawtheir own version, first seen Oct 8, 2026
- IWanalq/doubao-office-skillstheir own version, first seen Oct 8, 2026
- HanksChen0110/skillstheir own version, first seen Oct 3, 2026
- larksuite/clitheir own version, first seen Sep 10, 2026
- anyhui/hermes-skillstheir own version, first seen Jul 29, 2026
Used with
Projects with it also use lark-slides, lark-approval and 3 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
7emotionsSkilllark-slides
飞书幻灯片:以 XML 格式读取和管理 PPT 页面。创建演示文稿优先用 `+create`;XML API 主要用于读取 PPT 全文信息、创建和删除幻灯片页面。当用户需要创建 PPT、读取 PPT 内容、管理幻灯片页面时使用。
Also in 4 projects that use this
loongclaw-aiSkilllark-approval
飞书审批 API:审批实例、审批任务管理。
Also in 5 projects that use this
7emotionsSkilllark-attendance
飞书考勤打卡:查询自己的考勤打卡记录
Also in 4 projects that use this
IWanalqSkilllark-markdown
飞书 Markdown:查看、创建、上传、编辑和比较 Markdown 文件。当用户需要创建或编辑 Markdown 文件、读取、修改、局部 patch 或比较差异时使用。
Also in 3 projects that use this
- +1 more from 7emotions
Other versions10 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 10
- larksuite/cli★ 18ksame name
- loongclaw-ai/loongclaw★ 640same name
- liangdabiao/lark-workflow-feishu-cli★ 66same name
- IWanalq/doubao-office-skills★ 14same name
- SpaceZephyr/allSkills★ 14same name
- 7emotions/lark-skills★ 2same name
- dqtx760/skills-manage★ 2same name
- HanksChen0110/skills★ 2same name
- leishifu666/skills★ 1same name
- anyhui/hermes-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository10 other files it gives its agents.
- claude-code-statuslineSkill
- macos-accessibilitySkill
- remote-guiSkill
- cf-auto-checkinSkill
- cf-pages-deploySkill
- cloudflare-tunnelSkill
- proxy-multi-platformSkill
- ssh-persistSkill
- tailscale-meshSkill
- vps-singboxSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
21 rules. 0 are common, 8 are distinctive, and 13 are very specific to this project.
0
Common
Most of the 10 edited versions we read keep these.
8
Distinctive
This author's own choices. Worth a look.
13
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 113
Create an app, enable Bot capability
- Distinctive: line 118
Create config file (store OUTSIDE the project directory for security):
- Very specific: line 182
Create patches/@wu529778790+open-im+1.8.0.patch (see Privacy Patch section below for the full patch).
- Distinctive: line 264
After changing, re-apply the patch:
- Distinctive: line 273
Add model selection to your startup script:
- Very specific: line 443
Create .claude/settings.json as a second layer of path restrictions:
- Very specific: line 515
After manually editing the files in node_modules/@wu529778790/open-im/dist/:
- Very specific: line 527
Create .mcp.json in the project root to define available MCP servers:
- Distinctive: line 563
All MCP data paths should point within the project directory to maintain isolation.
- Distinctive: line 569
Create a CLAUDE.md in the project root to give the bot its identity and constraints:
- Very specific: line 682
Fix: Never set allowedTools in sessionOptions.
- Very specific: line 700
Fix: Use permissionMode: 'acceptEdits'.
- Very specific: line 708
Fix: Always include updatedPermissions in the return:
- Very specific: line 718
Root cause: When npx @wu529778790/open-im start forks a daemon, the child process may lose parent environment variables.
- Distinctive: line 753
Fix: After npm update:
- Very specific: line 754
Check if patch applies: npm install
- Very specific: line 755
If it fails, manually re-edit the files in node_modules/
- Distinctive: line 757
Commit the updated patch file
- Very specific: line 765
Fix: Add @mention filter in event-handler.js patch:
- Very specific: line 842
Fix: Never use bypassPermissions.
- Very specific: line 850
Fix: Use @wu529778790/open-im (npm published, actively maintained).
“Common” is measured: kept by at least 6 in 10 of the 10 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Lark-IM: Multi-Platform IM ↔ AI Coding Assistant Bridge
Bridge IM platforms (Feishu/Telegram/QQ/DingTalk/WeCom/WeChat) to AI coding assistants (Claude Code/Codex/CodeBuddy), turning any chat into an AI-powered coding assistant with full tool access and multi-layer privacy protection.
When to use
- Want to chat with an AI coding assistant through IM (Feishu, Telegram, QQ, etc.) instead of terminal
- Need to expose AI tools (file read/write, bash, web search) to a bot safely
- Need fine-grained privacy control: restrict which files the bot can access/modify
- Want to switch the underlying AI model or provider without redeploying
Supported Platforms
IM Platforms
| Platform | Connection | Status |
| Feishu/Lark | WebSocket | Fully supported |
| Telegram | Bot API | Fully supported |
| Bot API | Supported | |
| DingTalk | WebSocket | Supported |
| WeCom | WebSocket | Supported |
| Bridge | Supported |
AI Backends
| Backend | Provider | Models | Auth Method |
| Claude (native) | Anthropic | Opus 4.6, Opus 4.5, Sonnet 4.6, Haiku 4.5 | OAuth token / API key |
| Claude (custom endpoint) | Any OpenAI-compatible API | Qwen, GLM-4, DeepSeek, etc. | ANTHROPIC_BASE_URL + ANTHROPIC_AUTH_TOKEN + ANTHROPIC_MODEL |
| Codex | OpenAI | GPT-4o, o3, etc. via Codex CLI | OPENAI_API_KEY / interactive login |
| CodeBuddy | Tencent | Tencent AI models via CLI | CODEBUDDY_API_KEY / interactive login |
Custom endpoint support means you can use any model that provides an OpenAI-compatible API (Qwen, GLM, DeepSeek, Llama, Mistral, etc.) by setting ANTHROPIC_BASE_URL and ANTHROPIC_MODEL.
Each IM platform can be routed to a different AI backend (see Per-Platform Routing).
Architecture
IM Users (Feishu / Telegram / QQ / DingTalk / WeCom / WeChat)
│
↓ WebSocket / Bot API
│
┌───────────────────────────────┐
│ @wu529778790/open-im │ Multi-IM ↔ Multi-AI bridge
│ + patch-package patch │ Injects canUseTool callback
└───────────┬───────────────────┘
│
┌───────┼────────┐
↓ ↓ ↓
┌────────┐┌────────┐┌──────────┐
│ Claude ││ Codex ││CodeBuddy │ AI Adapters (selectable)
│ SDK ││ CLI ││ CLI │
└───┬────┘└───┬────┘└────┬─────┘
│ │ │
↓ ↓ ↓
MCP Servers + Tools (filtered by canUseTool)Setup Guide
Prerequisites
- Node.js >= 20
- A Feishu app with Bot capability (App ID + App Secret)
- Claude Code OAuth token (subscription or API key)
- Windows / macOS / Linux
Step 1: Create project
mkdir my-im-bot && cd my-im-bot
npm init -yEdit package.json:
{
"name": "my-im-bot",
"type": "module",
"scripts": {
"start": "node src/index.js",
"postinstall": "patch-package"
},
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.83",
"@wu529778790/open-im": "^1.8.0",
"ws": "^8.20.0"
},
"devDependencies": {
"patch-package": "^8.0.1"
}
}npm installStep 2: Configure Feishu app
- Go to Feishu Open Platform
- Create an app, enable Bot capability
- Subscribe to events:
im.message.receive_v1,card.action.trigger - Connection mode: WebSocket (long-polling)
- Save App ID and App Secret
Create config file (store OUTSIDE the project directory for security):
# Linux/macOS
mkdir -p ~/.open-im
cat > ~/.open-im/config.json << 'EOF'
{
"appId": "cli_your_app_id",
"appSecret": "your_app_secret"
}
EOF
# Windows (PowerShell)
New-Item -ItemType Directory -Path "$env:USERPROFILE\.open-im" -Force
# Then create config.json in that directoryStep 3: Store OAuth token securely
# The token must be stored OUTSIDE the project directory
# so the bot cannot read it at runtime
# Linux/macOS
echo "your-oauth-token" > ~/.open-im/token
# Windows (PowerShell)
"your-oauth-token" | Set-Content "$env:USERPROFILE\.open-im\token"Step 4: Create startup script
Windows (start.ps1):
$tokenFile = "$env:USERPROFILE\.open-im\token"
if (-not (Test-Path $tokenFile)) {
Write-Host "Error: token file not found at $tokenFile" -ForegroundColor Red
exit 1
}
$env:CLAUDE_CODE_OAUTH_TOKEN = (Get-Content $tokenFile -Raw).Trim()
$env:CLAUDE_CODE_GIT_BASH_PATH = "D:\Program Files\Git\bin\bash.exe"
Write-Host "Starting Feishu-Claude bridge..." -ForegroundColor Green
npx @wu529778790/open-im startLinux/macOS (start.sh):
#!/bin/bash
TOKEN_FILE="$HOME/.open-im/token"
if [ ! -f "$TOKEN_FILE" ]; then
echo "Error: token file not found at $TOKEN_FILE"
exit 1
fi
export CLAUDE_CODE_OAUTH_TOKEN=$(cat "$TOKEN_FILE" | tr -d '\n')
echo "Starting Feishu-Claude bridge..."
npx @wu529778790/open-im startStep 5: Apply the privacy patch
Create patches/@wu529778790+open-im+1.8.0.patch (see Privacy Patch section below for the full patch).
Then run:
npx patch-package # auto-applied on npm install via postinstall hookSwitching the Underlying AI Backend & Model
Backend Selection via config.json
The ~/.open-im/config.json controls which AI backend to use:
{
"appId": "your_feishu_app_id",
"appSecret": "your_feishu_app_secret",
"aiCommand": "claude",
"tools": {
"claude": {
"env": {
"ANTHROPIC_MODEL": "claude-opus-4-5"
}
},
"codex": {
"cliPath": "codex",
"timeoutMs": 600000
},
"codebuddy": {
"cliPath": "codebuddy",
"timeoutMs": 600000
}
}
}Change "aiCommand" to switch the entire backend:
"claude"— Anthropic Claude (default)"codex"— OpenAI Codex"codebuddy"— Tencent CodeBuddy
Per-Platform AI Routing
Route different IM platforms to different AI backends:
{
"aiCommand": "claude",
"platforms": {
"telegram": { "aiCommand": "codex" },
"feishu": { "aiCommand": "claude" },
"qq": { "aiCommand": "codebuddy" }
}
}Claude Model Switching
Available Claude models
| Model | ID | Best for |
| Opus 4.6 | claude-opus-4-6 | Deepest reasoning, complex tasks |
| Opus 4.5 | claude-opus-4-5 | Strong reasoning |
| Sonnet 4.6 | claude-sonnet-4-6 | Best coding model, cost-effective |
| Haiku 4.5 | claude-haiku-4-5-20251001 | Fast, lightweight tasks |
Approach A: Change the default in patch (permanent)
In the patch file, modify this line:
// Before
model: model || 'claude-opus-4-5',
// After (e.g., switch to Sonnet for cost savings)
model: model || 'claude-sonnet-4-6',After changing, re-apply the patch:
npx patch-package
# Then restart the botApproach B: Environment variable override (runtime)
Add model selection to your startup script:
# start.ps1 — add before the npx line:
$env:ANTHROPIC_MODEL = "claude-sonnet-4-6"Or in config.json:
{
"tools": {
"claude": {
"env": { "ANTHROPIC_MODEL": "claude-sonnet-4-6" }
}
}
}Approach C: Per-message model routing (advanced)
Users type a prefix in chat to select a model:
// In event-handler.js, before creating session:
let selectedModel = 'claude-opus-4-5'; // default
if (content.startsWith('/fast ')) {
selectedModel = 'claude-haiku-4-5-20251001';
content = content.replace('/fast ', '');
} else if (content.startsWith('/think ')) {
selectedModel = 'claude-opus-4-6';
content = content.replace('/think ', '');
}Users can then type /fast what is 2+2 in Feishu to use Haiku.
Using Custom/Third-Party Models (Qwen, GLM, DeepSeek, etc.)
The Claude adapter supports any OpenAI-compatible API endpoint via custom environment variables:
{
"tools": {
"claude": {
"env": {
"ANTHROPIC_BASE_URL": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"ANTHROPIC_AUTH_TOKEN": "sk-your-qwen-api-key",
"ANTHROPIC_MODEL": "qwen3-coder"
}
}
}
}Tested compatible providers:
| Provider | Base URL | Model ID Example |
| Qwen (DashScope) | https://dashscope.aliyuncs.com/compatible-mode/v1 | qwen3-coder, qwen-plus |
| GLM (Zhipu AI) | https://open.bigmodel.cn/api/paas/v4 | glm-4, glm-4-plus |
| DeepSeek | https://api.deepseek.com/v1 | deepseek-chat, deepseek-coder |
| Moonshot (Kimi) | https://api.moonshot.cn/v1 | moonshot-v1-128k |
| Any OpenAI-compatible | Your endpoint URL | Your model ID |
Codex (OpenAI) Setup
# Install Codex CLI
npm install -g @openai/codex
# Auth (one of these):
export OPENAI_API_KEY="sk-..."
# Or: codex login
# Configure in config.json:
{
"aiCommand": "codex",
"tools": {
"codex": {
"cliPath": "codex",
"timeoutMs": 600000,
"proxy": "http://127.0.0.1:7890"
}
}
}CodeBuddy (Tencent) Setup
# Install CodeBuddy CLI
npm install -g @tencent-ai/codebuddy-code
# Auth (one of these):
export CODEBUDDY_API_KEY="your-key"
# Or: codebuddy login
# Configure in config.json:
{
"aiCommand": "codebuddy",
"tools": {
"codebuddy": {
"cliPath": "codebuddy",
"timeoutMs": 600000
}
}
}Privacy & Security (4-Layer Protection)
Layer 1: Token Physical Isolation
OAuth token and Feishu app secret are stored outside the project directory (on a different drive on Windows, or in ~/.open-im/ on Linux/macOS). The bot process cannot read these files at runtime.
Token flow:
~/.open-im/token → start.ps1 reads → sets env var → SDK uses env var
(human runs) (in memory) (never logged)Layer 2: canUseTool Callback (Code-Level Enforcement)
The patch injects a canUseTool function that intercepts every tool call before execution:
async function canUseTool(toolName, input, options) {
const rawPath = (input.file_path || input.path || '').replace(/\\/g, '/').toLowerCase();
// Write/Edit — only within allowed subdirectory
if (['Write', 'Edit', 'NotebookEdit'].includes(toolName)) {
if (!isInAllowedWriteDir(rawPath)) {
return { behavior: 'deny', message: 'Write access denied' };
}
}
// Read/Glob/Grep — only within project directory
if (['Read', 'Glob', 'Grep'].includes(toolName)) {
if (!isInProjectDir(rawPath)) {
return { behavior: 'deny', message: 'Read access denied' };
}
}
// Bash — block dangerous commands
if (toolName === 'Bash') {
const cmd = (input.command || '').toLowerCase();
const blocked = [
'printenv', 'env ', // env var leaks
'$claude_code_oauth', '$anthropic', // token leaks
'process.env', 'os.environ', // programmatic env access
'git push', 'git remote', // no upstream pushes
'powershell', 'pwsh', 'cmd /c', // shell escape
'node -e', 'python -c', // inline code execution
];
for (const b of blocked) {
if (cmd.includes(b)) {
return { behavior: 'deny', message: `Blocked: "${b}"` };
}
}
}
return { behavior: 'allow' };
}Layer 3: settings.json Declarative Rules (Defense in Depth)
Create .claude/settings.json as a second layer of path restrictions:
{
"permissions": {
"allow": [
"Read(<project-dir>/**)",
"Write(<project-dir>/<writable-subdir>/**)",
"Edit(<project-dir>/<writable-subdir>/**)",
"Glob(<project-dir>/**)",
"Grep(<project-dir>/**)",
"Bash(ls *)", "Bash(cat *)", "Bash(git status)", "Bash(git log*)"
],
"deny": [
"Read(C:/**)", "Write(C:/**)", "Edit(C:/**)",
"Bash(printenv*)", "Bash(env *)", "Bash(echo $*)",
"Bash(* $CLAUDE_CODE_OAUTH*)", "Bash(* $ANTHROPIC*)",
"Bash(powershell*)", "Bash(cmd *)",
"Bash(git push*)", "Bash(git remote*)", "Bash(rm -rf*)"
]
}
}Layer 4: Group Message Filtering
The patch also ensures the bot only responds to @mentions in group chats, preventing accidental triggers:
if (chatType === 'group') {
const mentions = message.mentions;
const hasBotMention = Array.isArray(mentions) && mentions.length > 0;
if (!hasBotMention) {
return; // Skip non-@mention group messages
}
}Security Checklist
Before deploying, verify:
- Token file is outside the project directory
canUseToolblocks env var access (printenv,$CLAUDE_CODE_OAUTH, etc.)canUseToolrestricts Write/Edit to the intended subdirectory onlycanUseToolblocks shell escape (powershell,cmd /c,node -e,python -c)settings.jsondeny list covers all sensitive paths- No secrets in CLAUDE.md, package.json, or any committed file
.gitignoreexcludes token files,.open-im/,.mcp-data/- Bot cannot
git pushorgit remote add
Privacy Patch (canUseTool)
The full patch lives in patches/@wu529778790+open-im+<version>.patch. It modifies two files:
1. claude-sdk-adapter.js — adds canUseTool callback
Key changes:
- Injects
canUseToolfunction with path isolation logic - Sets
permissionMode: 'acceptEdits'(auto-approve edits that pass canUseTool) - Passes
env: { ...process.env }so the SDK inherits the OAuth token
2. event-handler.js — adds group @mention filter
Key changes:
- Reads
chat_typefrom message - Skips group messages without @mention
Generating the patch
After manually editing the files in node_modules/@wu529778790/open-im/dist/:
npx patch-package @wu529778790/open-im
# Creates patches/@wu529778790+open-im+1.8.0.patch
# Committed to git, auto-applied on npm installMCP Server Configuration
Create .mcp.json in the project root to define available MCP servers:
{
"mcpServers": {
"context7": {
"command": "npx",
"args": ["-y", "@upstash/context7-mcp@latest"]
},
"fetch": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-fetch"]
},
"sequential-thinking": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sequential-thinking"]
},
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"],
"env": {
"MEMORY_FILE_PATH": "<project-dir>/.mcp-data/memory.json"
}
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "<project-dir>"]
},
"sqlite": {
"command": "uvx",
"args": ["mcp-server-sqlite", "--db-path", "<project-dir>/.mcp-data/data.db"]
}
}
}All MCP data paths should point within the project directory to maintain isolation.
CLAUDE.md (Bot Instructions)
Create a CLAUDE.md in the project root to give the bot its identity and constraints:
# My Feishu Bot
## Identity
You are an AI assistant accessed through Feishu, powered by Claude.
## Constraints
- Working directory: <project-dir>/ only
- No push to GitHub, no adding git remotes
- Reply in the same language the user uses
## What you can do
- Read/search/analyze files in the project
- Write/edit files in <writable-subdir>/ only
- Run safe bash commands (ls, cat, grep, git status/log/diff)
- Use MCP tools (web fetch, memory, sequential thinking)Operations: Start / Stop / Status
Start the bot
Windows (PowerShell):
# In the project directory
.\start.ps1start.ps1 does three things:
- Reads OAuth token from
~\.open-im\token - Sets
CLAUDE_CODE_OAUTH_TOKENandCLAUDE_CODE_GIT_BASH_PATHenv vars - Runs
npx @wu529778790/open-im start(WebSocket daemon)
Linux/macOS:
chmod +x start.sh
./start.sh
# Or run in background:
nohup ./start.sh > bot.log 2>&1 &Stop the bot
Windows (PowerShell):
.\stop.ps1
# Internally runs: npx @wu529778790/open-im stopLinux/macOS:
npx @wu529778790/open-im stop
# Or if running in background, find and kill:
ps aux | grep open-im | grep -v grep | awk '{print $2}' | xargs killCheck status
Windows (PowerShell) — status.ps1:
# Checks if the bot's WebSocket ports are occupied
Get-NetTCPConnection -LocalPort 39281,39282 -ErrorAction SilentlyContinue | ForEach-Object {
$proc = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
Write-Host "Port $($_.LocalPort) in use — PID: $($_.OwningProcess) Process: $($proc.Name)" -ForegroundColor Cyan
}
if (-not (Get-NetTCPConnection -LocalPort 39281 -ErrorAction SilentlyContinue)) {
Write-Host "Service not running" -ForegroundColor Red
}Linux/macOS:
# Check if open-im process is running
pgrep -f "open-im" && echo "Running" || echo "Not running"
# Check port usage
ss -tlnp | grep -E '39281|39282'Restart
# Windows
.\stop.ps1; Start-Sleep -Seconds 2; .\start.ps1
# Linux/macOS
npx @wu529778790/open-im stop && sleep 2 && ./start.shKnown Issues & Solutions (Lessons Learned)
These are real problems encountered during development. The skill documents them so Claude can auto-diagnose and fix them.
Issue 1: canUseTool bypassed when allowedTools is set
Symptom: Bot writes to files outside the allowed directory despite canUseTool checks.
Root cause: The Claude Agent SDK pre-approves tools listed in allowedTools, completely skipping the canUseTool callback.
Fix: Never set allowedTools in sessionOptions. Let all tool invocations go through canUseTool:
const sessionOptions = {
model: model || 'claude-opus-4-5',
permissionMode: 'acceptEdits',
canUseTool,
// Do NOT set allowedTools — it bypasses canUseTool
env: { ...process.env },
};Issue 2: Non-interactive write fails with permissionMode 'default'
Symptom: Bot silently fails when trying to write files. No error, no output.
Root cause: permissionMode: 'default' tries to show interactive permission prompts, which fail in a daemon/bot context with no stdin.
Fix: Use permissionMode: 'acceptEdits'. The actual path isolation is enforced by canUseTool + settings.json deny rules.
Issue 3: SDK error "suggested permissions not found in response"
Symptom: Writing to allowed directories throws an internal SDK error.
Root cause: canUseTool callback returns { behavior: 'allow' } without the updatedPermissions field. The SDK expects this field even for allow responses.
Fix: Always include updatedPermissions in the return:
return { behavior: 'allow', updatedPermissions: options.suggestions || [] };Issue 4: OAuth token lost in daemon mode
Symptom: Bot starts but returns authentication errors. Token was set in the parent shell.
Root cause: When npx @wu529778790/open-im start forks a daemon, the child process may lose parent environment variables.
Fix: Explicitly pass env: { ...process.env } in sessionOptions so the SDK inherits the OAuth token from the daemon process.
Issue 5: V2 Session does not support settingSources
Symptom: Bot doesn't auto-load CLAUDE.md, skills, or system prompts at session start.
Root cause: Claude Agent SDK V2 intentionally removed support for settingSources, cwd, and systemPrompt parameters.
Workaround: Instruct the bot in CLAUDE.md to manually read context files on first message. This is a V2 limitation with no fix.
Issue 6: Relative paths bypass canUseTool on Windows
Symptom: Bot writes to project root files (e.g., CLAUDE.md) despite canUseTool restricting writes to a subdirectory.
Root cause: Relative paths like ./CLAUDE.md don't get normalized properly. Combined with acceptEdits, the operation is approved before path checks can distinguish directories.
Fix: Two layers:
- In canUseTool, normalize all paths:
.replace(/\\/g, '/').toLowerCase() - In settings.json, use explicit deny rules for the broad directory, with specific allow rules for the writable subdirectory:
{
"deny": ["Write(D:/**)", "Write(/d/**)"],
"allow": ["Write(D:/mybot/my-project/**)"]
}Issue 7: patch-package fails after npm update
Symptom: npm install shows "PATCH FAILED" errors after updating @wu529778790/open-im.
Root cause: The patch contains exact line numbers. Any change in the target file (even whitespace) causes offset mismatches.
Fix: After npm update:
- Check if patch applies:
npm install - If it fails, manually re-edit the files in
node_modules/ - Regenerate:
npx patch-package @wu529778790/open-im - Commit the updated patch file
Issue 8: Bot responds to all group messages (spam)
Symptom: Bot replies to every message in a Feishu group, not just @mentions.
Root cause: Event handler processes all im.message.receive_v1 events without checking the mentions array.
Fix: Add @mention filter in event-handler.js patch:
const chatType = message.chat_type ?? 'p2p';
if (chatType === 'group') {
const hasBotMention = Array.isArray(message.mentions) && message.mentions.length > 0;
if (!hasBotMention) return; // Skip non-@mention messages
}Issue 9: Bash commands exit with code 1 on Windows (no error output)
Symptom: Bash tool calls silently fail on Windows.
Root cause: Claude Code defaults to cmd.exe on Windows. Commands like grep, find, Unix-style paths don't work in cmd.
Fix: Set CLAUDE_CODE_GIT_BASH_PATH in the startup script:
$env:CLAUDE_CODE_GIT_BASH_PATH = "D:\Program Files\Git\bin\bash.exe"Issue 10: settings.json path matching unreliable on Windows
Symptom: Allow/deny rules don't match. Write(my-project/**) doesn't catch my-project\file.txt.
Root cause: Windows backslash paths vs Unix forward slash patterns.
Fix: Include both formats in rules:
"allow": [
"Write(my-project/**)",
"Write(D:/mybot/my-project/**)",
"Write(/d/mybot/my-project/**)"
]Issue 11: New Feishu group doesn't trigger events
Symptom: Bot added to a new group but doesn't respond to any messages.
Root cause: Feishu event subscriptions are tied to the published app version. Adding new groups or changing permissions requires a new version publication.
Fix: Go to Feishu Open Platform → App → Version Management → Publish a new version. Wait for approval.
Issue 12: OAuth token expires after 1 year
Symptom: Bot stops responding. Logs show authentication errors.
Root cause: Anthropic OAuth tokens have a 1-year TTL.
Fix: Regenerate before expiration:
# Generate new token
claude setup-token
# Update stored token
Set-Content $env:USERPROFILE\.open-im\token "new-token-here"
# Restart bot
.\stop.ps1; .\start.ps1Issue 13: "No API credentials" warning in logs (false alarm)
Symptom: Logs show "No API credentials found" but bot works fine.
Root cause: The open-im library tries to auto-detect credentials and warns if not found. The actual token is passed via env: { ...process.env }, which the auto-detection doesn't check.
Fix: Safe to ignore. This is a cosmetic warning, not a functional problem.
Issue 14: bypassPermissions disables ALL security checks
Symptom: Setting permissionMode: 'bypassPermissions' makes canUseTool and settings.json rules completely non-functional.
Root cause: By design, bypassPermissions skips all permission layers including canUseTool.
Fix: Never use bypassPermissions. Always use acceptEdits (for bot/daemon) or default (for interactive CLI).
Issue 15: @wu529778790/open-im vs claude-to-im confusion
Symptom: Searching for Feishu-Claude bridge libraries returns op7418/claude-to-im, which is not published to npm.
Root cause: Two different projects with similar goals. claude-to-im (op7418) is a private/unreleased package.
Fix: Use @wu529778790/open-im (npm published, actively maintained). This is the correct library.
Critical Rules (Quick Reference)
| Rule | Why |
Never set allowedTools | It bypasses canUseTool completely |
Always use acceptEdits, not default | default requires interactive stdin |
Always return updatedPermissions in canUseTool | SDK crashes without it |
Always pass env: { ...process.env } | OAuth token gets lost in daemon fork |
| Always normalize paths in canUseTool | Windows backslashes break matching |
Include both / and \ path formats in settings.json | Windows path inconsistency |
Never use bypassPermissions | Disables ALL security layers |
| Re-patch after npm update | Line offsets may shift |
| Republish Feishu app after permission changes | Events are version-tied |
| Renew OAuth token before 1-year expiry | Token silently expires |
Key Dependencies
| Package | Version | Role |
@wu529778790/open-im | ^1.8.0 | Feishu WebSocket ↔ Claude bridge |
@anthropic-ai/claude-agent-sdk | ^0.2.83 | Claude V2 Session API |
ws | ^8.20.0 | WebSocket client |
patch-package | ^8.0.1 | Persist canUseTool patch across npm install |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

