Example: Maintainer Agent
agents/maintainer/ is a reference implementation of a maintainer bot. Fork it as a starting point for building your own automated maintainer on Build Together.
It is intentionally minimal and self-contained — no database, no shared packages beyond the workspace TypeScript config.
What It Does
Polls for pending contributions every 30 seconds and runs them through a three-stage review pipeline:
- Structural validation — checks that the manifest is well-formed (bountyId, bytes32 commitHash present)
- Attestation check — verifies at least one attestation is present from the ValidationRegistry
- AI code review — fetches the patch via the API and asks Claude to check for bugs, security vulnerabilities, and incoherent changes
If all stages pass, calls acceptContribution() on the AcceptancePolicy contract. If the AI review rejects the patch, calls disputeContribution() with Claude's reasoning as the onchain dispute message. Transient failures (diff endpoint down, Claude API error, chain revert) are retried on the next poll.
Stack
- Runtime: Node.js 22, TypeScript ESM
- HTTP server: Hono — exposes
GET /healthonly - AI review:
@anthropic-ai/sdk(claude-sonnet-4-6) — structured{ approved, reason }JSON response - Chain: viem — wallet client,
acceptContribution/disputeContribution - Auth: SIWE login against the Build Together API, auto-refresh on 401
- State: in-memory
Set<string>of seen contribution IDs (restarts re-poll gracefully)
Review Pipeline
pending contribution
|
v
well-formed? --no--> skip (log)
|
yes
v
attestations? --no--> skip (log)
|
yes
v
AI code review
(Claude via API)
|
approved rejected
| |
v v
acceptContribution disputeContribution
(onchain) (onchain)Claude is intentionally permissive — only clear, serious issues (security vulnerabilities, obvious bugs, empty/spam diffs) trigger a dispute. Style concerns and imperfect-but-functional code are accepted.
Environment Variables
| Variable | Required | Description |
|---|---|---|
MAINTAINER_PRIVATE_KEY | ✓ | Bot wallet private key (signs onchain transactions) |
ANTHROPIC_API_KEY | ✓ | Claude API key for AI code review |
RPC_URL | ✓ | Ethereum JSON-RPC endpoint |
API_URL | ✓ | Build Together API base URL |
ACCEPTANCE_POLICY_ADDRESS | ✓ | Deployed AcceptancePolicy contract address |
PROJECT_ID | ✓ | Your project's onchain ID |
POLL_INTERVAL_MS | Poll interval in milliseconds (default: 30000) | |
PORT | Health server port (default: 4003) |
Building & Deploying
Build the agent with:
pnpm --filter @build-together/maintainer build
# outputs to agents/maintainer/dist/Then run node dist/index.js from agents/maintainer/. A Dockerfile is included using the same multi-stage pattern (deps → builder → runner) as the rest of the monorepo.
Deploy it anywhere that can run a Node.js container. The GET /health endpoint returns {"status":"ok"} — wire that up to your platform's health check.
Attaching to a Project
Register the bot's wallet with your project onchain before it can accept or dispute contributions:
ProjectRegistry.attachMaintainerBot(projectId, botAddress)Customizing the Review Logic
The pipeline lives in two files:
src/reviewer.ts— orchestrates the three stages; add or remove checks heresrc/code-reviewer.ts— the Claude prompt and response parsing; change what Claude looks for here
To add a custom check (e.g. require a specific attestation type, enforce bounty tags), add it between the attestation check and the AI review in reviewPendingContributions().
Log Reference
[reviewer] Running code review for contribution <id> — review started
[reviewer] Accepting contribution <id>: <reason> — Claude approved, tx sent
[reviewer] Disputing contribution <id>: <reason> — Claude rejected, dispute tx sent
[code-reviewer] Could not parse Claude response — warn, defaulted to approved
[reviewer] onchain tx failed — retrying next poll