Files
teabot/AGENTS.md
T
unkinben 748048be50
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Add fail-closed author allowlist gating job dispatch
Sessions run claude with --dangerously-skip-permissions and a prompt built
from issue/PR/comment text, so only trusted authors may supply that text.
teabot now dispatches a job only when the triggering event's author login is
on an allowlist; an empty allowlist dispatches nothing (fail-closed).

Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-27 00:33:09 +10:00

4.3 KiB

AGENTS.md

Project Overview

teabot is a Go daemon (systemd user service) that watches Gitea repositories and dispatches one-shot Claude Code sessions in Docker containers to work issues and review pull requests. It polls the Gitea API for new issues, pull requests, and comments; for each event it runs a throwaway container that clones the repo and runs claude --print with a task-specific prompt, acting as a configurable bot personality.

Structure

main.go                     # package main; wires version into internal/cli
internal/cli/               # cobra command tree: run, config init/show
internal/config/            # config.yaml + tea-config parsing, defaults, validation
internal/gitea/             # read-only Gitea REST client (issues/pulls/comments/diff)
internal/state/             # processed-event persistence (JSON, atomic writes)
internal/prompt/            # per-event prompt construction (issue/pull/follow-up)
internal/dispatch/          # poll loop, event filtering, loop prevention, job orchestration
internal/docker/            # containerised Claude execution behind a Runner interface
docs/                       # architecture + configuration + per-subcommand docs
packaging/nfpm.yaml         # RPM spec (binary + completions + systemd unit + example config)
scripts/build-rpm.sh        # generates completions + packages the RPM with nfpm
systemd/teabot.service      # systemd user unit
config.example.yaml         # example config (also embedded for `config init`)
.woodpecker/                # CI: build, test, pre-commit (PR) + release (tag)

Build / test

make build      # -> dist/teabot (CGO disabled, static)
make test       # go test -race ./...
make rpm        # build + package RPM (needs nfpm)

Requires Go 1.25+. Deps: github.com/spf13/cobra, gopkg.in/yaml.v3.

Design notes for contributors

  • Loop prevention is load-bearing. Any event authored by a personality's Gitea username is skipped (but recorded). Never remove this — it is what stops the bot reacting to its own PRs/comments forever.
  • Author allowlist is a security control. Jobs run --dangerously-skip-permissions with a prompt built from event text, so teabot only dispatches for authors in allowed_authors (optional per-repo override). Fail-closed: empty list dispatches nothing. Comment follow-ups gate on the new comment's author, not just the acted-on thread. Skipped events are marked processed-only (never acted-on).
  • Dedup before dispatch. Issues/PRs/comments are marked processed in the state store before their job starts, so a re-poll or mid-job restart cannot double-launch. State lives at ~/.local/state/teabot/state.json.
  • First-contact seeding. The first poll of a repo records existing open items as processed without dispatching, so a fresh install doesn't stampede.
  • Follow-ups only on acted threads. Comment follow-ups fire only when teabot previously opened a PR for / reviewed the parent issue/PR.
  • Docker is behind docker.Runner. All dispatch logic is tested with a fake runner; no Docker daemon is needed for go test. DockerRunner.buildArgs is pure given a job dir so the docker run argument list is unit-tested directly.
  • Personalities use tea configs. tea persists to $XDG_CONFIG_HOME/tea and has no --config flag, so each personality's config is mounted into the container and XDG_CONFIG_HOME is set there.
  • Image reuse. The default job_image is agent-dev, which already ships the claude CLI + dev toolchain; teabot does not build its own image.

Testing conventions

Every package has meaningful unit tests (no rubber-stamps): config precedence & validation & tea parsing, state persistence/dedup roundtrips, gitea client via httptest, prompt content assertions, docker arg/mount/env construction, and dispatch filtering/loop-prevention/seeding/follow-up routing via a fake client + fake runner. go build ./... and go test -race ./... must pass.

Conventions (house rules)

  • Branches benvin/<name>. PR bodies: short "why" paragraph + present-tense "how" bullets. HTTPS remotes for git.unkin.net (SSH blocked). Use tea, not gh. Do not merge PRs yourself.
  • Releases are Woodpecker pipelines triggered on v* tags; bump with make patch|minor|major. Every CI step sets k8s resource requests+limits.