lark-channel-bridge puts a local coding CLI in a Lark chat card, and its manifest still names the old repository
Bot that bridges Feishu/Lark messenger with a local Claude Code or Codex CLI. Streaming cards, per-chat sessions, multiple workspaces
At a glance
- What is it?
- lark-channel-bridge forwards Feishu or Lark messages to a local Claude Code or Codex CLI, renders the answer and tool calls on one streaming card, and keeps a separate session per chat, topic or document comment thread. It installs as one global npm package, registers an OS service per profile, warns that the daemon breaks if installed through npx, and still points its repository and issue URLs at a repository name it no longer has.
- Who is it for?
- lark-channel-bridge fits a team that wants a coding agent reachable from the chat tool it already lives in, with sessions kept per thread rather than per person.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 75 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- zarazhangrui/lark-coding-agent-bridge2,824 ★ · +271
| Repository | 2026-09-21 | 2026-11-14 |
|---|---|---|
| zarazhangrui/lark-coding-agent-bridge | 2.6k | 2.8k |
271 Stars in the last 54 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The package, the repository and the manifest use three names
The repository is called `lark-coding-agent-bridge`. The README's own title is `lark-channel-bridge`, and the npm package is `lark-channel-bridge` at version 0.7.1, published publicly to the npm registry with pnpm 10.33.0 as the declared package manager.
The manifest has not caught up with the rename. Its repository URL is a `git+https` link to `zarazhangrui/feishu-claude-code-bridge.git`, the bugs URL points at issues under that same name, and the homepage field is a readme anchor inside it. None of those match the repository the code lives in, so anything that reads package metadata to find the source, the issue tracker or the documentation is being sent to a name this project no longer uses.
The install instructions themselves are short, which is the one place the naming settles down because the command is the package name:
npm i -g lark-channel-bridge
# or
pnpm add -g lark-channel-bridgeBoth are documented, even though the manifest pins a package manager. There are no GitHub releases, so the version trail is the npm one, and a Chinese README sits next to the English one in both the tree and the published file list.
First run is a QR wizard that writes a single config file
Setup starts with a foreground command:
lark-channel-bridge runThat opens a wizard with five steps: a QR code renders in the terminal, you scan it with the Feishu or Lark app, you pick or create a PersonalAgent app, you choose which agent to initialise if prompted, and configuration is written to `~/.lark-channel/config.json`. The app creation happens inside that scan, so you do not have to build the app by hand in the developer console first.
The requirements are narrow. Node.js 20.12.0 or newer, at least one local agent already installed and logged in, either the `claude` CLI or the `codex` CLI, and a PersonalAgent app. That last requirement is a bridge to an agent running on your own machine, so the local agent has to be signed in before the bot is useful.
There is one deliberate convenience: you do not choose a project directory during setup. The bridge creates a profile-managed default working directory, and you switch to a real project afterwards by sending `/cd <path>` in the chat. If you already have an app, pass `--app-id` to skip creation and the command will prompt you for the App Secret instead:
lark-channel-bridge run --app-id cli_xxx
# or initialize and start the background service directly
lark-channel-bridge start --app-id cli_xxxGlobal Lark apps are the one flag difference: `--tenant lark`.
The daemon records the CLI path, so npx breaks it
The warning in the service section is the most operationally important sentence in the documentation. The daemon's launchd plist, its systemd unit and its Windows scheduled task all record the path to the bridge CLI. If that path came from an npm temporary cache because you ran the tool through npx, the daemon breaks as soon as the cache is cleaned. Running `run` through npx is fine, because that is a one-shot foreground process.
So the order matters. Start in the foreground, confirm the bot can send and receive, stop it with Ctrl-C, and only then move to a managed service. The service commands are:
lark-channel-bridge start
lark-channel-bridge status
lark-channel-bridge stopEach maps to a different native mechanism. macOS gets a launchd user agent named `ai.lark-channel-bridge.bot.<profile>`, Linux gets a systemd user unit named `lark-channel-bridge.bot.<profile>.service`, and Windows gets a Task Scheduler task named `LarkChannelBridge.Bot.<profile>` launched through a `.cmd` wrapper. Daemon logs land under `~/.lark-channel/profiles/<profile>/logs/daemon/`, so per-profile logs are the first place to look when a background instance is silent.
All five service commands take an optional `--profile`, including `restart` and `unregister`, so a second bot can be cycled without touching the first.
A profile holds credentials, sessions, directories and logs
Profiles are the unit of isolation, and each one keeps its own app credentials, sessions, working directories and logs. The documented reason to create more than one is narrow: you need to connect several PersonalAgent apps, or you want Claude and Codex to appear as two separate bots.
lark-channel-bridge profile create claude --agent claude
lark-channel-bridge profile create codex --agent codex
lark-channel-bridge profile list
lark-channel-bridge profile use <name>
lark-channel-bridge profile remove <name>
lark-channel-bridge profile remove <name> --purge --yes
lark-channel-bridge profile export <name> [--output ./profile.json] [--force]
lark-channel-bridge profile export <name> --include-secrets --yesThe removal semantics are the interesting part. `profile remove` archives local state by default rather than deleting it, including when the profile being removed is the active one. If other profiles remain, the bridge switches to the next; if it was the last profile, the root config is cleared so the same name can be created again. Permanent deletion needs both `--purge` and `--yes`, and export redacts app secrets unless you pass `--include-secrets` and `--yes` together.
One recovery case is called out explicitly. If a profile was created with the wrong agent kind, you have to stop or unregister the matching background service first, then remove the profile and recreate it with the intended `--agent`. That ordering exists because the service unit is what holds the old agent selection.
Sessions are per thread, and commands can cut into a running turn
The session model is finer than per user. Each chat, each topic and each document comment thread keeps its own session, so a conversation inside a document does not inherit the state of the room it is attached to.
Message handling has three behaviours worth naming. Messages sent in quick succession are batched together. Messages that arrive while a run is in progress are queued for the next turn rather than dropped. And a small set of commands can interrupt the current task instead of waiting: `/new`, `/cd`, `/ws use` and `/stop` are named as the interrupting ones.
That queue is paired with `/resume`, which resumes compatible history only when three things match: the same agent, the same working directory, and the same permission mode. So a resumed conversation is not a time machine, it is a continuation of a specific configuration.
The workspace commands make the working directory a first-class object rather than a launch parameter. `/cd <path>` switches directory and resets the session, `/ws save <name>` stores the current directory as a named workspace, `/ws use <name>` switches to one, `/ws remove <name>` deletes one, and `/ws list` lists them. Interactive cards are how the answers come back: text replies and tool calls update on a single Lark card in real time, and `/help`, `/ws list` and `/status` return cards with clickable buttons rather than plain text.
Three test suites that all pass when empty
The test scripts are split three ways and each one is allowed to find nothing:
test:unit "vitest run tests/unit --passWithNoTests"
test:integration "vitest run tests/integration --passWithNoTests"
test:process "vitest run tests/process --passWithNoTests"The same flag on all three means a green suite is not evidence that the tests exist. If a directory is empty or has been renamed, the run still succeeds, so a passing CI run tells you nothing about coverage of that area.
The two CI entry points differ in a way worth noting. `ci:local` runs a whitespace check on the diff before the tests, typecheck and build, while `ci:platform` runs tests, typecheck and build with that diff check removed. So the two are not interchangeable, and a platform run is not simply a local run on another machine.
There is a second build axis that explains the extra tooling. Alongside the CLI there is a `web/` directory built by vite, a tsup build for the package itself, and a Radix dialog component in the dev dependencies, which is UI code for that web surface. Type checking is `tsc --noEmit`, tests run under vitest, and both `prepare` and `prepublishOnly` run a build, with `prepublishOnly` also type checking. The `prepare` script invokes npm specifically, in a project whose manifest declares pnpm.
The slash command table is also the access control model
Most of the commands do navigation, but four of them decide who may use the bot at all, and they live in the same table as the rest.
`/invite user @name` allows a named user to use the bot in direct messages. `/invite admin @name` adds an access-control admin. `/invite group` allows the current group to use the bot. `/config` adjusts presentation preferences, access settings and what the tool calls its lark-cli identity. Since a direct message to the bot is forwarded straight to a shell on the machine running it, those four rows are the entire boundary between a chat participant and a command line.
Two more commands are diagnostic rather than navigational. `/status` reports the profile, the agent, the working directory, the session, the lark-cli identity and the run state, which is the first thing to ask for when a bot is not answering. `/invite` also appears with an `@name` pattern in the two rows above, so the table distinguishes between granting access to a person and granting it to a whole conversation.
The visible part of that table ends on an `/invite` row, so the set of documented invite variants stops there. The host CLI list above it is complete and separate: `run` with its profile, agent, workspace and config flags, `migrate` with profile and agent, `ps`, `kill <id|#>`, and `--help`.
Editorial conclusion
lark-channel-bridge fits a team that wants a coding agent reachable from the chat tool it already lives in, with sessions kept per thread rather than per person. Check four things first: install it globally before you register a service, because the recorded CLI path can come from an npx cache that later gets cleaned; the repository, issue and homepage URLs in the package manifest still carry an earlier repository name, so anything automated that resolves them will land in the wrong place; the three test suites all pass when empty, so a green run does not prove the tests exist; and there are no GitHub releases, so the published version to track is the npm one, currently 0.7.1. Anyone who needs an audited channel between chat and a shell should treat the LocalAgent app permissions and the slash command access model as the thing to review first.
Frequently asked questions
What does lark-channel-bridge do?
It bridges Feishu or Lark messenger with a local Claude Code or Codex CLI. You send a direct message or mention the bot in a group, and text replies plus tool calls update on one Lark card in real time.
What do I need before running lark-channel-bridge?
Node.js 20.12.0 or newer, at least one local agent already installed and logged in, either the claude CLI or the codex CLI, and a Feishu or Lark PersonalAgent app. The first-run QR wizard can create and bind that app for you.
How do I keep lark-channel-bridge running in the background?
Run it in the foreground first with `lark-channel-bridge run`, confirm messages work, then use `start` to register an OS service: a launchd user agent on macOS, a systemd user unit on Linux, or a Task Scheduler task through a .cmd wrapper on Windows. Daemon logs land under ~/.lark-channel/profiles/<profile>/logs/daemon/.
Why must lark-channel-bridge be installed globally before using service commands?
The daemon's launchd plist, systemd unit and Windows task all record the bridge CLI path. A path taken from an npm temp cache through npx breaks when the cache is cleaned, while `run` is fine through npx as a one-shot foreground process.
What does a lark-channel-bridge profile hold?
Each profile keeps its own app credentials, sessions, working directories and logs. `profile remove` archives local state by default, `--purge --yes` deletes it permanently, and `profile export` redacts app secrets unless `--include-secrets --yes` is passed.
Can Claude and Codex run as two separate Lark bots?
Yes. Create one profile per agent with `profile create <name> --agent claude|codex`, then start each with its own `--profile`. The service commands are all per-profile, so one bot can be restarted or inspected without touching the other.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/zarazhangrui-lark-coding-agent-bridge)Star history chart for your README
[](https://hysenlabs.com/projects/zarazhangrui-lark-coding-agent-bridge)Was this useful?