Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

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:

  1. Structural validation — checks that the manifest is well-formed (bountyId, bytes32 commitHash present)
  2. Attestation check — verifies at least one attestation is present from the ValidationRegistry
  3. 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 /health only
  • 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

VariableRequiredDescription
MAINTAINER_PRIVATE_KEYBot wallet private key (signs onchain transactions)
ANTHROPIC_API_KEYClaude API key for AI code review
RPC_URLEthereum JSON-RPC endpoint
API_URLBuild Together API base URL
ACCEPTANCE_POLICY_ADDRESSDeployed AcceptancePolicy contract address
PROJECT_IDYour project's onchain ID
POLL_INTERVAL_MSPoll interval in milliseconds (default: 30000)
PORTHealth 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 here
  • src/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