English · Русский · 中文 · Español · Deutsch · Français · Português (BR) · 日本語
Run a Claude Code AI dev team on your server and drive it from chat. Describe a feature in Telegram or VK; the team plans it, builds it on a branch, tests it, reviews it, and opens a PR — each chat in its own isolated workspace.
It runs on your Claude Pro/Max subscription (no per-token billing) or an Anthropic API key, ships as prebuilt Docker images (no build step), and keeps every chat in its own sandboxed workspace.
git clone https://github.com/duckbugio/flock
cd flock/adapters/telegram
cp .env.example .env # fill in the REQUIRED block (4 values)
docker compose up -dThat pulls the prebuilt image ghcr.io/duckbugio/flock-telegram — no build, no Ansible — then message your bot. The minimum .env:
| Variable | What |
|---|---|
TELEGRAM_BOT_TOKEN |
from @BotFather |
TELEGRAM_BOT_USERNAME |
your bot's @username (no @) |
ALLOWED_USERS |
comma-separated Telegram user IDs allowed to use the bot |
CLAUDE_CODE_OAUTH_TOKEN |
claude setup-token (subscription) — or set ANTHROPIC_API_KEY |
Everything else in .env.example has sensible defaults. Update later with docker compose pull && docker compose up -d.
Region: host in an Anthropic-supported region (some countries, e.g. RU/CN, are geo-blocked) — otherwise Claude calls fail.
VK is the same pattern under adapters/vk/, built on the same core and published as ghcr.io/duckbugio/flock-vk. It ships only an env template (no compose file): cp .env.example .env, then docker run --env-file .env ghcr.io/duckbugio/flock-vk. Claude auth and core settings match Telegram; only the three transport vars change:
| Variable | What |
|---|---|
VK_BOT_TOKEN |
community access token (VK community → Manage → API usage → access token) |
VK_GROUP_ID |
your community's numeric id (long-poll server + mention parse) |
VK_ALLOWED_USERS |
comma-separated VK user IDs allowed to use the bot |
- The conversation is the task source — describe what you want in chat and review the PR that comes back; the agent's shell and editor are sandboxed inside the container.
- A real dev-team pipeline, not a single prompt — spec-first acceptance criteria, build/regression gates, and an arbiter that breaks loops.
- Autonomy loops — the bot verifies its own "done" by re-running the repo's check gate itself;
/goalarms an independent evaluator that loops the team until your criterion actually holds;/scheduleruns recurring jobs; an optional CI watch reacts to red builds (and can auto-merge green PRs) — all under a per-chat daily autonomy budget. See Autonomy loops. - Multi-transport — Telegram and VK today, both on the same core; a new platform is a thin adapter, not a fork.
- PR reactions without inbound webhooks — the bot polls your git host for new review comments and routes each back to the chat that opened the PR.
- Subscription-friendly — authenticate with a Claude Pro/Max token (no per-token cost) or an Anthropic API key.
You (in a chat): "implement X across the api + web services"
→ bot's Claude (Lead) → planner → confirm scope → coder ⇄ tester → PR per repo
→ reviewer (inline comments) ⇄ coder → arbiter
├ APPROVE → you merge
└ ESCALATE → asks you
The five subagents — planner → coder → tester → reviewer → arbiter — run as native Claude Code subagents in core/agents/. A plain question is just answered; a build request triggers the team. The arbiter is the risk-aware, cycle-limited loop-breaker so agents never spin forever. Branches are named duck/<chatid>/<slug> so PR-webhook/poll events route back to the right chat.
The team is built for a microservices workspace: a feature can span several services, and it coordinates branches and one cross-linked PR per repo. The full pipeline, guardrails, and role table live in core/README.md.
Four opt-in loops move you up the delegation ladder — from "the agent checks its own work" to "the agent runs without you" — each with a hard stop condition (env keys in .env.example):
- Post-run verification (
ENABLE_POST_VERIFY, default on) — after a run reports done, the bot itself re-runs the changed repos' own check gate (task/make/npmcheck/test/lint) and, on red, sends the team back with the real failure output — up toPOST_VERIFY_MAX_FIXESconsecutive rounds. The agent's "tests pass" is verified, not trusted. /goalevaluator —/goal all list views paginate correctlyarms a goal; after every completed run an independent, fresh-session judge (no shared context with the working session) inspects the workspace, re-runs checks, and returns a strict verdict. Not met → the unmet points are injected back as a fix-up; met → 🎯 and the goal disarms. Bounded byGOAL_MAX_ATTEMPTS;EVALUATOR_MODELcan pick a cheaper judge;/goal offdisarms./schedulecron jobs — durable per-chat recurring prompts with per-chat timezones (see the SCHEDULER block in.env.example); fired as normal team runs, gated by the creator's allow-list status and cost cap at fire time.- CI watch (
ENABLE_CI_WATCH) — polls CI state on theduck/*branches (GitHub check-runs or Gitea commit status). A red build injects "CI is red — fix and push" into the owning chat; a green build wakes the chat too, so "I'll report when CI finishes" actually happens — each once per commit. WithENABLE_AUTO_MERGE=truea green PR is merged automatically — the full hands-off mode; leave it false to keep the human merge.
Runs can also legally come back later: writing followup/<delay>.md (content = the prompt) schedules a durable one-shot return — and a promise nudge fires one corrective run whenever a final answer says "I'll report back" without scheduling anything that actually would, so the bot stops going silent on its own promises.
Two safety rails apply across all of it: AUTO_TASK_MAX_COST_PER_DAY caps what autonomy-originated runs may spend per chat per day (direct messages are unaffected), and AUTO_APPROVE_SCOPE controls which planner complexities may skip the "confirm scope & wait" step (off by default).
The platform-agnostic dev-team brain lives in core/; each platform is a thin adapter under adapters/<name>/ that shares it.
| Adapter | Path | Prebuilt image |
|---|---|---|
| Telegram | adapters/telegram/ |
ghcr.io/duckbugio/flock-telegram |
| VK | adapters/vk/ |
ghcr.io/duckbugio/flock-vk |
Future platforms reuse the same core — see docs/multi-transport-plan.md.
Set these in .env to let the team clone repos and open PRs (works with Gitea/GitHub/GitLab):
GIT_HOST=git.example.com
GIT_USER=...
GIT_TOKEN=... # write-scoped PAT
GIT_AUTHOR_NAME=AI Team
GIT_AUTHOR_EMAIL=ai@example.com
# Poll the host for new PR comments (reliable; no inbound webhook needed):
GITEA_API_URL=https://git.example.com/api/v1
GITEA_POLL_INTERVAL=90For github.com, also set GH_TOKEN (= your GIT_TOKEN) so the gh CLI can open PRs.
The poller is the recommended way to react to review comments — it reaches out, so it works even when your host can't reach the bot. It's active when ENABLE_PR_REVIEW=true and GITEA_API_URL is set. An inbound-webhook + Caddy TLS proxy alternative is available only through the Ansible deploy (set webhook_domain).
- Voice messages:
ENABLE_VOICE_MESSAGES=true,VOICE_PROVIDER=mistral|openai|local, plusMISTRAL_API_KEY(orOPENAI_API_KEY). Transcribed and run as commands. - dind sidecar:
docker compose --profile dind up -dgives the team dockerized linters/tests (setDOCKER_HOST=tcp://dind:2375). - Per-chat isolation: each chat gets
/workspace/chat_<id>(1:1 → private; group → one shared workspace); chats are fully isolated and run in parallel, capped byMAX_CONCURRENT_CHAT_RUNS. In groups, setREQUIRE_GROUP_MENTION=trueto respond only when @mentioned or replied to. - Ansible deploy (Telegram): one-command VPS provision from
adapters/telegram/deploy— copyinventories/exampleto your owninventories/<name>/(gitignored), fill inventory/vars/vault, thenansible-playbook -i inventories/<name>/inventory.ini playbook.yml. The role pulls the prebuilt image; setbot_imageto pin a tag.
Found a vulnerability? Please disclose it privately — see SECURITY.md. Hardening notes for operators:
- Whitelist: only
ALLOWED_USERS(Telegram) /VK_ALLOWED_USERS(VK) may use the bot — never leave it empty; it grants shell/edit access to your server. - Per-chat isolation: different chats get separate workspaces. The git token is shared across a deployment — scope it accordingly.
- Secrets: keep them in
.env(gitignored) or, for Ansible, in a real instance'svault.yml(gitignored,ansible-vaultencryptable). Onlyinventories/example/is tracked. - Sandbox: the agent runs as a non-root user; its Bash/Edit are confined to the container, not your host.
The repo uses Task as its CI runner — the same entrypoint CI uses:
task lint # format + vet + linters (in the dev-tools image)
task tests # Go test suite
task build # compile the binariesMIT © DuckBug.
We welcome contributions! See CONTRIBUTING.md for the full guide — development setup, the task lint/test/build workflow, and conventions. In short:
- Fork the repository: Create your own fork of the repository on GitHub.
- Create a new branch: Make a new branch for your feature or bugfix.
- Make your changes: Implement your feature or fix the bug.
- Write tests: Ensure that your changes are covered by tests.
- Submit a pull request: Push your changes to your fork and submit a pull request to the main repository.
This project follows the Contributor Covenant. Please be respectful and considerate in your interactions with others; harassment and discrimination of any kind will not be tolerated. Report concerns to conduct@duckbug.io.