Turn your Feishu group chat into a fully-featured Claude Code workspace.
CodeLark connects Claude Code (via the Claude Agent SDK) to Feishu as a bot, giving your team access to Claude's coding capabilities — file editing, shell commands, project management, and deep Feishu document integration — directly from chat.
- Claude Code in your chat — Read, write, edit files, run shell commands, and manage git repos without leaving Feishu
- Real-time streaming — CardKit 2.0 streaming cards with live thinking display, tool execution status, and progressive content updates
- Feishu-native document tools — Create, read, update cloud documents; manage wiki spaces; search across docs — all through OAuth-authorized user context
- Multi-project workspaces — Create projects, clone repos, switch contexts; each group thread can bind to its own project
- Team-ready — User allowlists, per-project access control, admin roles, group chat support with thread isolation
- Claude Opus / Sonnet / Haiku — Switch models per session with
/model - Full Claude Code toolset — Read, Write, Edit, Bash, Glob, Grep
- Extended thinking — Reasoning displayed in collapsible panels, separated from the final answer
- Session management — Fork sessions to explore different directions, resume bot or CLI sessions, list/rename sessions
- MCP plugin support — Auto-loads Claude Code plugins from local cache
- Text, image, file, and rich-text (post) messages — Images sent to Claude as multimodal content
- Quote/reply context — Media and text from quoted messages are merged into the current request
- Group chat awareness — Recent chat history (20 messages / 30 min) provided as context
- Slash commands —
/help,/status,/cancel,/model,/project,/session,/file,/auth,/whoami
- Cloud documents — Create with Feishu-flavored Markdown (callouts, grids, tables, Mermaid, images), fetch as Markdown, update with 7 edit modes
- Wiki — List/create knowledge spaces and nodes, auto-convert wiki URLs
- Drive — List, copy, move, delete, upload (chunked for large files), download
- Search — Unified doc & wiki search with filters (creator, type, time range)
- Comments — List, create, and resolve document comments
- Media — Insert images/files into documents, download attachments
- Bitable — Full CRUD for multi-dimensional tables: create apps/tables/views, manage fields (22 field types), batch read/write/search records (up to 500 per call)
- CardKit 2.0 with streaming mode — real-time content updates
- Phased display — Thinking panel, tool execution status, main content as separate streams
- Graceful fallback — Degrades to standard IM cards if CardKit unavailable
- Typing indicator — Reaction on user's message during processing
- User & group allowlists — Optional filtering for authorized users and groups
- Per-project ACL —
access.jsonallowlists with creator-based auto-grant - Admin roles — Unrestricted access to all projects and operations
- Thread permissions — Only admins/creators can rename or reset sessions
- Path traversal prevention — Realpath resolution, sandboxed to project directory
- OAuth identity verification — Prevents cross-user authorization hijacking
- Tool permission confirmation — Dangerous operations require user approval via card buttons (60s timeout, auto-deny)
- Event deduplication — ID-based with 12h TTL, stale message filtering (>2 min)
- Per-chat message queue — Serialized processing prevents interleaved responses
- Rate limiting — Card update throttling (100ms CardKit / 1.5s IM fallback), configurable debounce
- OAuth token management — Auto-refresh with transient error retry, 60s expiration buffer
- Graceful shutdown — SIGINT/SIGTERM handling, abort all active tasks, wait for in-flight updates
CodeLark has three core concepts you need to understand:
Every conversation with Claude operates inside a directory on the server. Claude can read, write, and execute commands within this directory — just like using Claude Code locally.
- DM (no project selected) — You get a personal default directory (
users/<your_id>/), auto-created on first use - Group thread (no project bound) — The thread gets a group default directory (
groups/<chat_id>/), shared across all threads in that group - With a project selected — Claude works inside that project's directory
A project is a named working directory that persists across conversations and can be shared with other users.
/project create my-app # Create an empty project (git-initialized)
/project clone https://... # Clone a repo as a project
/project use my-app # Switch to a project (DM only)
/project list # List available projects
/project grant my-app ou_xxx # Grant access to another user
In DMs, you switch projects with /project use. Your selection persists — next time you message the bot, you're still in the same project.
In group threads, the project is bound to the thread. The first /project use in a thread locks it — this cannot be changed later, ensuring the thread's conversation history stays consistent with its working directory.
A session is the conversation context (chat history) that Claude remembers.
- Bot sessions — Created automatically. In DMs, one session per user per project. In groups, one session per thread. Use
/newto clear context and start fresh. - CLI sessions — If you also use Claude Code locally, you can resume a local CLI session in Feishu with
/session resume <id>. This lets you continue a task you started in your terminal. Use/session listto see available local sessions, and/session exitto return to normal mode. - Forking — Use
/session forkto branch off from the current conversation. This creates a new session with a copy of the chat history, letting you explore a different direction while keeping the original intact.
DM conversation:
You → bot Uses your selected project (or personal default)
One session per project, /new to reset
Group conversation:
Thread A → bound to my-app Each thread has its own session
Thread B → bound to api-server Each thread can bind to a different project
Thread C → no project bound Uses the group's default directory
- Node.js >= 20
- A Feishu custom app with bot capabilities (create one here)
- A Claude subscription or Anthropic API key
git clone https://github.com/anthropics/codelark.git
cd codelark
npm installcp .env.example .envEdit .env:
FEISHU_APP_ID=cli_xxxxx
FEISHU_APP_SECRET=xxxxx
WORKSPACE_DIR=~/workspaces
# Optional: API key auth (otherwise uses Claude subscription)
# ANTHROPIC_API_KEY=sk-ant-xxxxx
# Optional: restrict access
ALLOWED_USER_IDS=ou_xxx1,ou_xxx2
ALLOWED_GROUP_IDS=oc_xxx1
# Optional: enables accurate @mention detection in groups
# BOT_OPEN_ID=ou_xxxxxIn the Feishu Open Platform console:
- Bot — Enable bot capability
- Event subscriptions — Enable WebSocket mode (long connection), subscribe to:
im.message.receive_v1— Receive messagescard.action.trigger— Card button callbacks
- Permissions — Add the required scopes:
im:message/im:message:send_as_bot— Read and send messagesim:chat/im:chat:readonly— Access chat infoim:resource— Download media (images, files)contact:user.base:readonly— Resolve user names
# Development (with hot reload)
npm run dev
# Production
npm run build && npm startThe bot connects via WebSocket — no public server or domain needed.
- Send
/whoamito the bot in a DM to get youropen_id - Add your
open_idtoALLOWED_USER_IDSin.env(if using allowlists) - Send
/authto authorize Feishu document access (OAuth device flow) - Start chatting — or use
/helpto see all commands
| Command | Description |
|---|---|
/help |
Show available commands |
/status |
Current project and task status |
/whoami |
Get your open_id |
/cancel |
Cancel the running task |
/model [opus|sonnet|haiku] |
Switch model or show current |
/project list |
List available projects |
/project use <name> |
Switch to a project |
/project create <name> |
Create a new project |
/project clone <url> |
Clone a git repository |
/project grant <name> <user_id> |
Grant project access |
/project revoke <name> <user_id> |
Revoke project access |
/session list |
List bot and CLI sessions |
/session resume <id> |
Resume a bot or CLI session |
/session fork [title] |
Fork current session into a new branch |
/session rename <name> |
Rename current session |
/session new or /new |
Start a fresh session |
/file <path> |
Upload a file from the project |
/auth |
Authorize Feishu document access |
src/
├── index.ts # Entry point, startup & shutdown
├── config.ts # Environment variable loading
├── logger.ts # Structured logging (pino)
├── messaging/
│ ├── inbound/
│ │ ├── event-handlers.ts # Pipeline orchestration
│ │ ├── parse.ts # Message type parsing & extraction
│ │ ├── media.ts # Image/file download
│ │ ├── gate.ts # User/group allowlist checks
│ │ ├── dedup.ts # Event deduplication
│ │ ├── dispatch.ts # Route to command or Claude task
│ │ ├── card-actions.ts # Card button callbacks
│ │ └── user-name-cache.ts # User name resolution cache
│ ├── outbound/
│ │ └── send.ts # Feishu client & message sending
│ └── types.ts # Shared message types
├── claude/
│ ├── executor.ts # Claude Agent SDK integration
│ └── feishu-tools-guide.ts # System prompt guide for Feishu tools
├── card/
│ ├── streaming-card.ts # CardKit 2.0 streaming lifecycle
│ ├── builder.ts # Card content construction
│ ├── flush-controller.ts # Debounced card updates
│ └── markdown-style.ts # Markdown normalization
├── channel/
│ ├── chat-queue.ts # Per-chat message serialization
│ ├── chat-history.ts # Group chat context window
│ ├── active-registry.ts # Running task tracking & cancellation
│ └── model-config.ts # Per-session model selection
├── session/
│ ├── db.ts # SQLite database (users, sessions, tokens, threads)
│ ├── manager.ts # Session CRUD & thread binding
│ └── local-sessions.ts # Local CLI session discovery
├── project/
│ ├── manager.ts # Project CRUD, git clone, access control
│ └── config.ts # Project configuration types
├── auth/
│ ├── device-flow.ts # OAuth device authorization flow
│ ├── token-store.ts # Token storage & refresh
│ ├── oauth-card.ts # Authorization prompt cards
│ ├── access.ts # Project access control (access.json)
│ └── group-admin.ts # Group admin detection
├── tools/
│ ├── feishu-doc-server.ts # MCP server aggregating all Feishu tools
│ ├── feishu-oapi.ts # Document create/fetch/update
│ ├── feishu-wiki.ts # Wiki space & node operations
│ ├── feishu-drive.ts # Drive file operations
│ ├── feishu-search.ts # Doc & wiki search
│ ├── feishu-doc-media.ts # Document media (images/files)
│ ├── feishu-doc-comments.ts # Document comments
│ ├── feishu-bitable.ts # Bitable (multi-dimensional table) CRUD
│ └── feishu-mcp.ts # MCP tool type definitions
└── utils/
└── command.ts # Slash command parser
Every incoming message flows through a 6-stage pipeline:
Message → Dedup → Parse → Record (history) → Gate (ACL) → Enqueue → Dispatch
↓
Command handler
or
Claude task
↓
Streaming card ← CardKit 2.0
| Variable | Required | Default | Description |
|---|---|---|---|
FEISHU_APP_ID |
Yes | — | Feishu app ID |
FEISHU_APP_SECRET |
Yes | — | Feishu app secret |
WORKSPACE_DIR |
Yes | — | Root directory for projects and data |
ANTHROPIC_API_KEY |
No | — | API key (uses Claude subscription if unset) |
ALLOWED_USER_IDS |
No | — | Comma-separated open_ids for user allowlist |
ALLOWED_GROUP_IDS |
No | — | Comma-separated chat_ids for group allowlist |
BOT_OPEN_ID |
No | — | Bot's open_id for accurate @mention detection |
ADMIN_USER_IDS |
No | — | Comma-separated admin open_ids (full access) |
TASK_TIMEOUT_MS |
No | 300000 |
Max task duration (5 min) |
DEBOUNCE_MS |
No | 500 |
Message processing debounce |
SESSION_TITLED_ONLY |
No | false |
Only show titled sessions in list |
BOT_CLAUDE_HOME |
No | — | Isolated HOME for bot sessions (prevents bot sessions from appearing in CLI /insights) |
LOG_LEVEL |
No | info |
Log level (info or debug) |
- Runtime — Node.js + TypeScript (ES2022)
- AI — @anthropic-ai/claude-agent-sdk + @modelcontextprotocol/sdk
- Feishu — @larksuiteoapi/node-sdk
- Database — SQLite via better-sqlite3 (WAL mode)
- Logging — pino (structured JSON)
- Validation — zod
- Testing — vitest
# Run tests
npm test
# Watch mode
npm run test:watch
# Development server with hot reload
npm run devCodeLark's Feishu document integration (cloud docs, wiki, drive, search, comments) was heavily inspired by and referenced from openclaw-lark, the official Feishu channel plugin for OpenClaw by ByteDance. Thanks to the Lark Open Platform team for open-sourcing their work.
MIT