Skip to content

Repository files navigation

Anvil

Artifact-driven multi-agent orchestration, in your terminal.

You assign work to AI agents the way you'd assign it to a teammate — they pick it up, report progress, raise blockers, and hand it back for review. No web UI, no lock-in, runs on your laptop or a 100-node cluster.

English · 简体中文

Go Version License: MIT CI Tests Coverage Go Report Card


         _/  __/  __/  __/  __/  __/  __/  __/  __/  __/  __/  __/  __/  __/
                  _/  __/                                                 __/
                   _/  __/    TERMINAL-NATIVE MULTI-AGENT ORCHESTRATION  __/
                    _/  __/                                                _/
                     _/  __/    artifact-driven · blackboard · C/S       _/
                      _/  __/                                             _/
                       _/  __/    v1.3.0 · MIT · github.com/zealot00/Anvil _/
                        _/  __/__________________________________________/


  $ af create --name "research-2025" --project "RD-01"
  $ af status  --id art_xxx           # INIT → ... → PENDING_APPROVAL
  $ af approve --id art_xxx           # → APPROVED
  $ af publish --id art_xxx --out out.md

Why Anvil?

You want But Anvil
Self-hostable artifact state machine LangChain / AutoGPT are LLM loops without durable artifacts Git-as-source-of-truth + Postgres metadata + explicit state machine with 13 states
Distributed workers (100-node scale) tmux + ssh + tail -f NATS JetStream KV leases, server-side Reaper, automatic republish on lease expiry
Plug any local Agent CLI Vendor-specific SDKs Model Context Protocol — works with opencode, claude, aider, hermes, openclaw, codex, …
Real HITL (human-in-the-loop) "vibe coding" without review First-class PENDING_APPROVAL state, Slack Interactive buttons, audit-logged transitions
Terminal-native workflow Web dashboards and desktop apps get in the way TUI + CLI + REPL + Connect-gRPC; everything scriptable

Features

  • Artifact is a first-class citizen. Every output of every agent is a versioned file (Markdown / JSON / code / image). go-git tracks history; Postgres stores metadata. Diff, revert, audit by default.
  • 13-state state machine with [human]-gated transitions (approve, reject, revision, publish, cancel) and a DAG fan-in for pipelines.
  • Blackboard dispatch — agents don't talk to each other; they coordinate through the artifact state machine + dependency gates.
  • C/S cluster. Nodes pair in with a one-time code, heartbeat every 5 s, get woken (or evicted) by the server. Two-level routing by node_tags and role.
  • Multi-Agent daemon. The Worker scans PATH for any CLI that speaks MCP (opencode, claude, aider, hermes, openclaw, codex, custom), spawns each as a stdio JSON-RPC server, and registers them. The server then routes tasks based on task.AgentProvider (server hint) → capability → any.
  • HITL Zen TUI (Bubble Tea). j/k navigation, $EDITOR integration, diff/approve/reject, ? help overlay, REPL mode with bash-style autocompletion.
  • Hook system. Three kinds: webhook (outbound HMAC + retry/dead-letter), command (sh -c, optionally routed to a node), inbound (HMAC-validated, maps to create_artifact / trigger_pipeline). Slack Interactive callback is a special case.
  • Daily pipelines. Cron-scheduled templates with depends_on fan-in; per-day runs view (af pipeline runs).
  • Material library. Cross-day knowledge assets, tagged with tags[] (GIN index) + pg_trgm fuzzy search, with ReferencedBy and UsedBy reverse links for full provenance.
  • Observability. /healthz, /readyz, /metrics (Prometheus text), audit-log table for every state change.

Quick Start

30-second local demo

# 1. PostgreSQL (Docker).
docker compose up -d postgres

# 2. Server (auto-migrates schema + registers demo agents).
go run ./cmd/server
# → 2025/01/01 12:00:00 [server] NATS mode disabled (using in-process blackboard)
# → 2025/01/01 12:00:00 [server] listening on :9090

# 3. CLI — create, status, approve, export.
go run ./cmd/af create --name "research-2025" --project "RD-01"
# → Created artifact ID: "art_xxx" [INIT]

go run ./cmd/af status --id art_xxx
# → Current Status: PENDING_APPROVAL       (demo agents auto-advance)

go run ./cmd/af approve --id art_xxx
# → Status transitioned to APPROVED

go run ./cmd/af export --ids art_xxx > report.md
# → # Research 2025 ...

# 4. TUI.
go run ./cmd/tui
# j/k navigate, Enter detail, a approve, r reject, e $EDITOR, / command, ? help

Distributed mode with multi-agent daemon

# 1. PG + NATS.
docker compose up -d postgres
docker run -d --name nats -p 4222:4222 nats:latest -js

# 2. Server (NATS mode).
ANVIL_NATS_URL=nats://localhost:4222 go run ./cmd/server

# 3. Edge Worker daemon (auto-discovers Agent CLIs in $PATH).
ANVIL_NATS_URL=nats://localhost:4222 \
  ANVIL_WORKER_ID=edge-1 ANVIL_NODE_ID=node-a \
  go run ./cmd/worker
# → [worker edge-1] capabilities=planner,writer,reviewer node=node-a ...
# → [agent-pool] registered provider=hermes bin=... tools=10
# → [agent-pool] registered provider=openclaw bin=... tools=9

No ANVIL_AGENT=mcp:... hard-coding required — the worker discovers what's installed and reports it to the server.

Inspecting registered agents

# What does the server think this worker can do?
curl -s http://server:9090/agents | jq

# Pick a specific worker for a given capability:
curl -s "http://server:9090/agents?choose=1&capability=writer" | jq

Architecture

Three layers — presentation, application, infrastructure — with the domain model kept pure (no external imports).

                         ┌────────────────────────────────────┐
   ┌──────────┐          │        Presentation Surface       │
   │   TUI   │ ──HTTP──►│  Connect-gRPC (HTTP/1.1 + SSE)    │
   │  (cmd/  │          │  + plain HTTP (/healthz/readyz/   │
   │   tui)  │          │    /metrics /agents /webhooks)    │
   └────┬─────┘          └────────────────┬───────────────┘
        │                                  │
   ┌────┴─────┐                            ▼
   │   CLI    │              ┌──────────────────────────┐
   │  (cmd/   │ ──HTTP─────►│  Use Cases (internal/app) │
   │    af)   │              │  · ArtifactService        │
   └──────────┘              │  · PipelineService        │
                             │  · NodeService            │
                             │  · MaterialService        │
                             │  · HookService            │
                             │  · NotificationService   │
                             └─────────────┬────────────┘
                                           │ ports
                             ┌─────────────▼────────────┐
                             │   Ports (internal/ports)│
                             │   Store · Blackboard    │
                             │   EventBus · Notifier   │
                             │   Versioner · HookDis-  │
                             │   patcher · LeaseReg-   │
                             │   istry · Searcher      │
                             └─────────────┬────────────┘
                                           │ adapters
       ┌───────────────────┬───────────────┼───────────────────┬──────────┐
       ▼                   ▼               ▼                   ▼          ▼
  ┌────────┐         ┌────────┐       ┌────────┐         ┌────────┐  ┌────────┐
  │ git    │         │postgres│       │  mem   │         │  nats  │  │ memstore│
  │ + CAS  │         │ store  │       │  black │         │ (KV +  │  │  (test) │
  └────────┘         └────────┘       │  board │         │ Jet-   │  └────────┘
                                      └────────┘         │ Stream)│
                                                         └────────┘
       ┌─────────────────────────┐
       │   Domain (pure Go)       │
       │   artifact · statemachine│
       │   events · node · material│
       │   pipeline · hook        │
       └─────────────────────────┘

The full architecture document — including the multi-agent daemon sequence diagram, state machine diagram, and DAG fan-in flow — lives at docs/architecture.md.


Core Concepts

1. Artifact

Everything an agent produces is an artifact — a Markdown report, a JSON diff, a generated image. Artifacts have:

  • An immutable version chain (Git hash per version).
  • A current status (see state machine below).
  • A list of dependencies on other artifacts (with minimum required status).
  • An owner (which agent or human created them).

2. State Machine

13 states, 16 legal transitions, with HumanOnly guards for HITL gating:

stateDiagram-v2
    INIT --> OUTLINE_DONE : planner
    INIT --> FAILED : error
    OUTLINE_DONE --> DRAFT_CREATED : writer
    OUTLINE_DONE --> FAILED : error
    DRAFT_CREATED --> PENDING_APPROVAL : writer/submit
    DRAFT_CREATED --> FAILED : error
    PENDING_APPROVAL --> APPROVED : [human] approve
    PENDING_APPROVAL --> REVISION_REQUESTED : [human] revision
    PENDING_APPROVAL --> REJECTED : [human] reject
    REVISION_REQUESTED --> DRAFT_CREATED : writer-revise
    REVISION_REQUESTED --> FAILED : error
    APPROVED --> READY_TO_PUBLISH : pack
    APPROVED --> FAILED : error
    READY_TO_PUBLISH --> PUBLISHED : [human] publish
    FAILED --> INIT : retry
    REJECTED --> CANCELLED : [human] cancel

Run af diagram --format mermaid|dot to render. The canonical table is in internal/domain/statemachine/statemachine.go.

3. Multi-Agent

Every agent carries a three-tuple identity:

Field Meaning Example
Name Human-readable label writer-1
Provider Which CLI / vendor opencode, claude, hermes, openclaw, aider, mock
Runtime Where it runs local, node:edge-1, remote:192.168.1.10

The Worker daemon (see internal/workersdk/) scans PATH for known CLIs at boot, spawns each as a stdio MCP server, and registers the agent in an AgentPool. Tasks arrive via NATS with an optional task.AgentProvider hint from the server. pool.Pick(hint, status) resolves:

  1. Explicit provider → that exact agent.
  2. Capability (status → planner | writer | reviewer) → first available agent.
  3. Any available agent.
  4. Fall back to an inline Handler (used in CI / when no CLI is installed).

The server tracks these via internal/server/agent_registry.go, which subscribes to NATS heartbeats and rebuilds the worker ↔ agents map.

4. Pipeline DAG (D13)

Templates can declare DependsOn: [other_template_id]. TriggerRun blocks until every dependency's latest Run.Status == COMPLETED. Templates form a DAG, fan-in enforced.

5. Material Library (D14)

Cross-day knowledge assets. Two reverse-link fields track provenance:

  • ReferencedBy — artifacts that cite this material (others → me).
  • UsedBy — artifacts that embed this material (me → others).

Queryable via af material search --tag ... with GIN + pg_trgm fuzzy match.


CLI Reference (af)

$ af --help
Available Commands:
  agent         view / pick a Worker's local Agent
  approve       HITL approve
  completion    generate shell completion (bash/zsh/fish/powershell)
  create        create an artifact and enter the blackboard
  diagram       render state machine (mermaid|dot)
  diff          show version diff
  export        package export (Markdown)
  help          help about any command
  hook          manage event hooks (webhook / command / inbound)
  material      material library
  node          node lifecycle (pair / approve-pair / evict)
  notifications query notification history
  pipeline      pipeline templates & runs
  publish       mark APPROVED as PUBLISHED, export to file (D15)
  reject        HITL reject
  revert        revert to an older version (creates a new version pointing at it)
  search        full-text search across artifacts
  shell         enter REPL mode with bash-style completion
  status        query artifact status

Example session:

af create   --name "research-2025"   --project "RD-01"
af status   --id art_xxx                         # INIT → PENDING_APPROVAL
af approve  --id art_xxx                         # → APPROVED
af export   --ids art_xxx > report.md            # → 131 bytes
af publish  --id art_xxx --out published.md      # → READY_TO_PUBLISH → PUBLISHED

af diagram  --format mermaid                    # 16 lines + [human] guards
af agent    list                                 # workers + their (provider, capabilities)
af agent    choose --capability writer           # server picks a worker
af search   --query "research"
af material search --tag sic
af pipeline create-template --name daily --project demo --cron "0 8 * * *"
af pipeline runs --project demo --days 7
af node     pair --name edge-1 --fingerprint fp-1
af notifications list --limit 20
af shell                                       # REPL mode

Deployment

docker-compose.yml (included)

services:
  postgres:
    image: postgres:16-alpine
    environment: { POSTGRES_USER: anvil, POSTGRES_PASSWORD: anvil, POSTGRES_DB: anvil }
    ports: ["5432:5432"]
    volumes: [anvil-pg:/var/lib/postgresql/data]

Add anvil-server and anvil-worker services in production. See docs/architecture.md#containerized for a full example.

Environment variables

See .env.example. Highlights:

Variable Required Purpose
ANVIL_HTTP_ADDR server listen address (default :9090)
ANVIL_PG_DSN server Postgres DSN
ANVIL_DATA_DIR server git working tree root
ANVIL_NATS_URL optional if set, distributed mode
ANVIL_JWT_SECRET production node JWT HS256 secret
ANVIL_SLACK_TOKEN / _SIGNING_SECRET optional Slack notifications + interactive approval
ANVIL_WORKER_ID worker stable identifier
ANVIL_NODE_ID worker parent node grouping
ANVIL_DISABLE_AUTODISCOVER worker if 1, don't scan PATH
ANVIL_FALLBACK_HANDLER worker if 1, use deterministic handler when pool is empty

Testing

# All unit tests (Postgres / NATS tests skip automatically if unavailable).
go test ./... -count=1

# Coverage report (excludes `gen/` proto-generated code).
go test ./... -coverprofile=cover.out -count=1 -short
grep -vE '^anvil/gen/' cover.out > cover2.out
go tool cover -func=cover2.out | tail -1

# Full-stack E2E (downloads embedded postgres + NATS, runs real binaries).
ANVIL_E2E_FULL=1 go test -timeout 180s ./internal/e2e/...

# Detailed endpoint dump (captures every /agents / /healthz / /metrics /
# CLI / JWT / webhook HMAC call as evidence).
ANVIL_E2E_DETAIL=1 go test -timeout 300s -run TestFullStackDetail ./internal/e2e/...

Domain-layer coverage: 93.5%–100%. Adapter layer: 55–80%. See .github/workflows/ci.yml for the CI matrix.


Inspiration

Anvil draws on ideas from the broader open-source multi-agent ecosystem and from the Model Context Protocol community. Where relevant, those influences are cited inline in the architecture doc; we try not to repeat them here.

If you are building a similar system and want to share patterns, please open a Discussion — the design notes in docs/PLAN.md record every decision D1–D15 in a way that should make cross-comparison easy.


Roadmap

Phase Status Highlights
P0 Foundation in-process blackboard, PG, git, TUI/CLI, hooks, cron, materials
P1 Distributed cluster NATS JetStream + KV leases + Reaper + node commands
P2 External surface inbound webhook + pipeline runs + TUI SSE + Slack Interactive
P3 Ecosystem nkey/JWT + multi-project + WOL + terminal image + notification history
P4 v1.0 polish /healthz /readyz + PG outbox → NATS + /metrics + completion
P5 v1.1 UX REPL + diagram + rate limit + full-text search + TUI help
P6 v1.2 Agent bridge MCP protocol + Identity triple + daemon auto-discovery
P7 v1.3 completeness D2 CAS + D13 DAG + D14 UsedBy + D11 dedup + D15 af publish
P8 v1.4 (planned) distributed cron leader election; workspace-level multi-tenant isolation; Lark / DingTalk / WeCom adapters; OpenTelemetry tracing

See CHANGELOG.md for what shipped in each release.


Contributing

Contributions of all sizes are welcome. See CONTRIBUTING.md for the dev setup, branch / PR flow, and how to add a new Agent CLI provider or state transition.

All participants are expected to follow the CODE_OF_CONDUCT.md.


Security

See SECURITY.md for the threat model, supported versions, and how to report a vulnerability privately.


License

MIT — © 2025 Anvil Contributors.


Acknowledgments

About

Terminal-native multi-agent orchestration (artifact-driven · blackboard · C/S cluster). MCP-bridged AI agents (opencode/claude/aider/hermes/openclaw). Inspired by multica.ai.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages