Imported from gqf2008/abb (
AGENTS.md). Install upstream withnpx skills add gqf2008/abb. Copyright stays with the author.
Repository Guidelines
Contributor guide for ABB (agent-bridge), a Rust + Slint menu-bar app that bridges Feishu/WeChat/DingTalk messages to local Claude/Codex agents. It runs as a tray controller (default) or a headless bridge daemon (--service).
Project Structure & Module Organization
src/— Rust source; one module per concern (agent,bridge,wechat,feishu,dingtalk,schedule,ws, …). Unit tests live inline at the bottom of each file.ui/app.slint— Slint UI definition, compiled at build time viabuild.rs.app-assets/— macOS bundle assets (Info.plist,AppIcon.icns, tray icons).scripts/— macOS helpers:build.sh(bundle + sign + install),sign.sh(re-sign),make-certs.sh(dev cert).reference/— protocol references (e.g.,feishu_ws_protocol.py).crates/buzz-agent/— self-maintained fork (the ACP agent execution layer). Independent package: own manifest + lock, not a member of the root package (rootCargo.tomlhas no[workspace]), so rootcargo clippy/fmt/testnever touches it — CI has a dedicatedfork-lintjob; run commands with--manifest-path crates/buzz-agent/Cargo.toml.third_party/i-slint-core/— vendoredi-slint-corewired via[patch.crates-io]. The only local patch is the Windows tray window (message-only → top-level hidden; seeCargo.tomlcomment). Its published tree does not ship everything upstream's repo has —benches/string.rsand a font its lib testsinclude_bytes!are missing, so standalone--all-targets/--lib --testsbuilds fail for reasons unrelated to the patch; compile coverage of the patch comes from root CI building it as a dependency on windows-latest.src/buzz/**— upstream-sync zone (ported buzz harness). Every change there must be logged in the ledgerdocs/buzz-port-sync.md(处置表); that file also records the fork's known-flaky tests and sync constraints.
Runtime data lives in ~/.agent-bridge/; per-bot workspaces under ~/.agent-bridge/workspaces/<bot_key>/.
Walgit Collaboration
- Canonical development remote:
originis the local walgit repository athttp://127.0.0.1:8081/gqf2008/abb.git;githubis a mirror/release remote only. - All work tracking lives in walgit: create/update issues, patches, reviews,
merge results and status transitions with
walgit collabentries underrefs/collab/*. Do not open new GitHub issues or PRs for normal development. - Entry contract:
issuestarts a thread;comment+status: in-progressrecords owner/worktree/branch;patchuses--base refs/heads/main --head refs/heads/<branch>;reviewusesdecision(approve/needs-changes),agent, andnote;needs-changesreturns tostatus: in-progress; approve keepsstatus: needs-reviewuntil merge; after pushing the local merge, appendmerge_resultwith the merged oid and then a secondmerge_resultwithmerged=true; finish withstatus: closed. - Board/CI declarations:
.walgit/board.tomland.walgit/ci.tomlare part of the tested tree. Move cards only by appending a signedstatusentry; never edit the board to represent a state change. - Build intermediates go to
/Volumes/DataExt/tmp(user policy, 2026-09-17): the 228 GiB boot volume must not hold build output. UseCARGO_TARGET_DIR=/Volumes/DataExt/tmp/abb-targetfor local gates andexport TMPDIR=/Volumes/DataExt/tmpfor anything that makes temp trees. Falling back to the defaulttarget/or$TMPDIRfills the boot volume and manifests as anerrno=28 (No space left on device)link failure — not as a code failure. - Walgit CI runner:
ci.tomlis only a declaration; the server does not execute it. Start/supervise the local runner withtools/run_ci_runner.shfrom the stable main checkout — it setsTMPDIR=/Volumes/DataExt/tmpandCARGO_TARGET_DIR=/Volumes/DataExt/tmp/abb-ci-target(required, see above). Run exactly one runner per host (two runners race to claim the same run's tasks). Check results withwalgit ci status --repo .; a missing runner or zero runs is not a pass. - Mirror discipline: push normal heads/tags to
originonly. The local walgit-to-GitHub mirror syncsrefs/heads/*andrefs/tags/*; GitHub Actions is used for mirror/release artifacts, not day-to-day collaboration.
Build, Test, and Development Commands
Every cargo command below must carry the build-intermediate env prefix (user policy: build output never lands on the 228 GiB boot volume):
export TMPDIR=/Volumes/DataExt/tmp
export CARGO_TARGET_DIR=/Volumes/DataExt/tmp/abb-target
cargo build— debug build.cargo run— run the tray app;cargo run -- --serviceruns the headless daemon.cargo test— run all unit tests (do not run this directly on a live machine: it writes the real~/.agent-bridge; usetools/check_test_isolation.shinstead).cargo clippy --all-targets -- -D warnings— lint.cargo fmt --check— verify formatting.tools/check_test_isolation.sh— the full-test gate (isolated HOME; never substitute a barecargo test).scripts/build.sh— build the release macOS bundle into~/Applications/ABB.app.scripts/sign.sh— re-sign with theagent-bridge-devcertificate so TCC privacy grants survive rebuilds.
With the two exports above in the shell, the four gate commands are verbatim:
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo build --locked
tools/check_test_isolation.sh
Coding Style & Naming Conventions
- Rust:
snake_caseidentifiers,CamelCasetypes/enums; followrustfmt(4-space indent) and keepcargo clippyclean. - Use
//!module docs and///doc comments; explain why in comments. Existing comments are often in Chinese — match the language of the file you edit. - String handling must be UTF-8/char aware (e.g.,
agent::truncatetruncates by chars, not bytes). - Keep
.slintchanges inui/app.slint, consistent with existing component naming.
Testing Guidelines
- Framework: built-in Rust unit tests in
#[cfg(test)]modules at the end of eachsrc/*.rs; the root package has no Rust integration tests —tests/only holds Python mock helper scripts (e.g.tests/mock_acp_agent.py). The forkcrates/buzz-agentdoes have Rust integration tests incrates/buzz-agent/tests/. - Name tests with
snake_case, behavior-focused names (e.g.,codex_single_message_no_progress,strip_user_mentions). - Add tests alongside the code you change and run
cargo testbefore pushing.
Commit & Pull Request Guidelines
- History is short; use imperative, concise subjects, optionally prefixed with the affected area (e.g.,
feishu: …). - Keep commits focused and explain why in the body.
- Walgit patch/PR entries: describe what and why, link the issue thread, and run
cargo fmt --check,cargo clippy --all-targets -- -D warnings,cargo build --locked, andtools/check_test_isolation.shwithTMPDIR=/Volumes/DataExt/tmp CARGO_TARGET_DIR=/Volumes/DataExt/tmp/abb-targetexported (see Build/Test above). The isolated test runner is the full-test gate; do not substitute a barecargo testbecause it can write the real~/.agent-bridge. Include before/after screenshots for UI changes. GitHub PRs are only for mirror/release maintenance.
Security & Configuration
- macOS entitlements 是分发的硬前提(#251):
app-assets/abb.entitlements必须随签名带上 (bundle 与内部可执行都要),否则 hardened runtime 下相机/麦克风/自动化会被静默拒绝 (连授权弹窗都没有)。scripts/被.gitignore排除、不在版本控制内,所以本机 本机公证统一走仓库内入口tools/notarize_app.sh <App.app>(tools/notarize_app.sh --self-test可自测参数转发):它强制把app-assets/abb.entitlements传给~/scripts/notarize.sh—— 后者用--deep --force重签,不传就把授权签没 —— 并在重签后跑tools/check_entitlements.sh断言最终产物。CI 的 release.yml 同样已接入该守卫。 config.json(contains App Secret) and*.secretare gitignored — never commit credentials.- Don't commit
logs/,target/, or generated.appbundles. - Before touching signing, read
scripts/sign.sh: usage-description entitlements on the bare binary can cause a startupSIGKILL.