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.
$ npx -y skills add duckbugio/flock --agent claude-code
Run the curl in your terminal, the rest in Claude Code.
What's inside
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 -d
That 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 |
/goal arms an independent evaluator that loops the team until your criterion actually holds; /schedule runs 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.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):
ENABLE_POST_VERIFY, default on) — after a run reports done, the bot itself re-runs the changed repos' own check gate (task/make/npm check/test/lint) and, on red, sends the team back with the real failure output — up to POST_VERIFY_MAX_FIXES consecutive rounds. The agent's "tests pass" is verified, not trusted./goal evaluator — /goal all list views paginate correctly arms 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 by GOAL_MAX_ATTEMPTS; EVALUATOR_MODEL can pick a cheaper judge; /goal off disarms./schedule cron 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.ENABLE_CI_WATCH) — polls CI state on the duck/* 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. With ENABLE_AUTO_MERGE=true a 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=90
For 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).
ENABLE_VOICE_MESSAGES=true, VOICE_PROVIDER=mistral|openai|local, plus MISTRAL_API_KEY (or OPENAI_API_KEY). Transcribed and run as commands.docker compose --profile dind up -d gives the team dockerized linters/tests (set DOCKER_HOST=tcp://dind:2375). Ansible deploys enable dind by default and inject DOCKER_HOST automatically./workspace/chat_<id> (1:1 → private; group → one shared workspace); chats are fully isolated and run in parallel, capped by MAX_CONCURRENT_CHAT_RUNS. In groups, set REQUIRE_GROUP_MENTION=true to respond only when @mentioned or replied to.adapters/telegram/deploy — copy inventories/example to your own inventories/<name>/ (gitignored), fill inventory/vars/vault, then ansible-playbook -i inventories/<name>/inventory.ini playbook.yml. The role pulls the prebuilt image; set bot_image to pin a tag.Found a vulnerability? Please disclose it privately — see SECURITY.md. Hardening notes for operators:
ALLOWED_USERS (Telegram) / VK_ALLOWED_USERS (VK) may use the bot — never leave it empty; it grants shell/edit access to your server..env (gitignored) or, for Ansible, in a real instance's vault.yml (gitignored, ansible-vault encryptable). Only inventories/example/ is tracked.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 binaries
MIT © DuckBug.
We welcome contributions! See CONTRIBUTING.md for the full guide — development setup, the task lint/test/build workflow, and conventions. In short:
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.
.claude/
settings.json
.dockerignore
.github/
dependabot.yml
ISSUE_TEMPLATE/
bug_report.md
config.yml
feature_request.md
PULL_REQUEST_TEMPLATE.md
workflows/
ci.yml
publish-telegram.yml
publish-vk.yml
.gitignore
.golangci.yml
adapters/
telegram/
.env.example
bot_reply_test.go
bot_rich_test.go
bot.go
capabilities_test.go
commands_test.go
commands.go
deploy/
ansible.cfg
inventories/
example/
group_vars/
all/
vars.yml
vault.yml
inventory.ini
playbook.yml
roles/
claude_tg_bot/
defaults/
main.yml
handlers/
main.yml
tasks/
main.yml
templates/
Caddyfile.j2
docker-compose.yml.j2
docker-prune.service.j2
docker-prune.timer.j2
env.j2
docker-compose.yml
Dockerfile
entrypoint.sh
html_test.go
html.go
nudge.go
richtransport_test.go
richtransport.go
richwire_test.go
richwire.go
upload_test.go
upload.go
voice_test.go
voice.go
vk/
.env.example
api_test.go
api.go
bot_test.go
bot.go
commands.go
Dockerfile
longpoll.go
mention_test.go
mention.go
payload_test.go
payload.go
receiver_test.go
receiver.go
upload_test.go
upload.go
voice.go
AGENTS.md
CLAUDE.md
cmd/
duck-vk/
main.go
flock-telegram/
autonomy.go
commands_test.go
main.go
media_routing_test.go
quoted_context_test.go
CODE_OF_CONDUCT.md
CONTRIBUTING.md
core/
agent/
types_test.go
types.go
agents/
arbiter.md
coder.md
planner.md
reviewer.md
tester.md
autobudget/
autobudget_test.go
autobudget.go
chat/
chunk_fenced_test.go
chunk_test.go
chunk.go
commands_test.go
commands.go
cost_record_test.go
followup.go
gate_test.go
gate.go
guards_test.go
guards.go
media_test.go
media.go
nudge_test.go
nudge.go
outbox_test.go
outbox.go
postrun_test.go
postrun.go
progress_test.go
progress.go
reserved_test.go
reserved.go
service_test.go
service.go
transport.go
ciwatch/
ciwatch_test.go
ciwatch.go
hosts_test.go
hosts.go
state.go
claude/
CLAUDE.workspace.md.tmpl
decode_test.go
decode.go
integration_test.go
mcp_test.go
mcp.go
runner_test.go
runner.go
tail.go
testdata/
error.jsonl
fake-claude.sh
forward_compat.jsonl
happy.jsonl
types.go
codex/
runner_test.go
runner.go
tail.go
cost/
cost_test.go
cost.go
dispatch/
dispatch_test.go
dispatch.go
followup/
followup_test.go
followup.go
ghstar/
ghstar_test.go
ghstar.go
gitsetup/
gitsetup_test.go
gitsetup.go
goal/
goal_test.go
goal.go
nudge/
nudge_test.go
nudge.go
openaicompat/
runner_test.go
runner.go
pending/
pending_test.go
pending.go
poller/
poller_test.go
poller.go
ratelimit/
ratelimit_test.go
ratelimit.go
README.md
schedule/
cron_test.go
cron.go
manager_test.go
manager.go
store_test.go
store.go
tzdata_test.go
session/
session_test.go
session.go
skills/
library-docs-lookup/
SKILL.md
verification-before-completion/
SKILL.md
teambranch/
teambranch_test.go
teambranch.go
verify/
verify_test.go
verify.go
voice/
errors.go
http.go
local.go
voice_test.go
voice.go
workspace/
workspace_test.go
workspace.go
docs/
ai-provider-architecture-plan.md
codex-integration-plan.md
go-rewrite-plan.md
multi-backend-plan.md
multi-transport-plan.md
rich-messages-plan.md
GEMINI.md
go.mod
go.sum
internal/
airunner/
backend_test.go
backend.go
atomicfile/
atomicfile_test.go
atomicfile.go
autonomy/
autonomy.go
config/
config_test.go
config.go
fsutil/
upload_test.go
upload.go
LICENSE
README.de.md
README.es.md
README.fr.md
README.ja.md
README.md
README.pt-BR.md
README.ru.md
README.zh.md
SECURITY.md
Taskfile.yml
tools/
Dockerfile
shotter/
package-lock.json
package.json
shot
shot.mjsFAQ
flock is a Claude Code plugin with 2 hand-picked skills for automation work, indexed on Flowy. Install it with the command on its page. It includes library-docs-lookup, verification-before-completion. Its skills do not fire on their own yet. Request auto-invocation to have Flowy route them as you prompt. Free and open source.