Skip to content

gaberger/hex

Repository files navigation

hex — Hexagonal Architecture Harness for AI-Driven Development

Node >= 20 npm Bun MIT License Languages Swarm

Give AI coding agents mechanical architecture enforcement — not just prompt templates.
Typed port contracts  |  Static boundary analysis  |  Multi-agent swarm coordination  |  Token-efficient AST summaries



The Problem

When AI agents generate code autonomously, they produce spaghetti. Adapters import other adapters. Domain logic leaks into HTTP handlers. Database queries appear in UI components. No amount of prompt engineering prevents this at scale.

Traditional AI coding tools improve the conversation with AI. hex improves the output.


SPECKit vs BMAD vs hex — what AI agents actually receive


Quick Start

# Scaffold a new hexagonal project
npx @anthropic-hex/hex scaffold my-app --lang typescript

# Initialize hex in an existing project (works on 100K+ LOC codebases)
npx @anthropic-hex/hex init --lang ts

# Analyze architecture health
npx @anthropic-hex/hex analyze .

# Generate token-efficient summaries for AI context
npx @anthropic-hex/hex summarize src/ --level L1



Architecture

Hexagonal Architecture Layers

How the layers work
Layer May Import From Purpose
domain/ domain/ only Pure business logic, zero external deps
ports/ domain/ only Typed interfaces — contracts between layers
usecases/ domain/ + ports/ Application logic composing ports
adapters/primary/ ports/ only Driving: CLI, HTTP, MCP, Dashboard
adapters/secondary/ ports/ only Driven: FS, Git, LLM, TreeSitter, Ruflo, Secrets
composition-root.ts Everything The ONLY file that imports adapters

The golden rule: Adapters NEVER import other adapters. This is the most common mistake AI agents make, and hex analyze catches it every time.


Port Contracts — What AI Agents Actually Implement

Ports are typed interfaces. When an AI agent is told "implement this adapter against this port," it has clear input/output contracts — not prose descriptions:

// Port: the contract (what we need)
export interface IFileSystemPort {
  read(filePath: string): Promise<string>;
  write(filePath: string, content: string): Promise<void>;
  exists(filePath: string): Promise<boolean>;
  glob(pattern: string): Promise<string[]>;
  streamFiles(pattern: string, options?: StreamOptions): AsyncGenerator<string>;
}

// Adapter: the implementation (how we do it)
// AI generates this within its boundary — can't leak into other adapters
export class S3Adapter implements IFileSystemPort {
  async read(filePath: string): Promise<string> { /* S3 GetObject */ }
  async write(filePath: string, content: string): Promise<void> { /* S3 PutObject */ }
  async exists(filePath: string): Promise<boolean> { /* S3 HeadObject */ }
  async glob(pattern: string): Promise<string[]> { /* S3 ListObjectsV2 */ }
}

Architecture Validation

$ hex analyze .

Architecture Analysis
=====================
Dead exports:     0 found
Hex violations:   0 found
Circular deps:    0 found

✓ All hexagonal boundary rules pass

When an adapter imports another adapter:

- Hex violations:   1 found
-   ✗ src/adapters/secondary/cache-adapter.ts imports from
-     src/adapters/secondary/filesystem-adapter.ts
-     Rule: adapters must NEVER import other adapters



Specs-First Workflow

Specify → Build → Test → Validate → Ship


1. Specify

hex plan "JWT auth with rate limiting"

Decomposes into adapter-bounded steps:

steps:
  - adapter: secondary/auth
    port: IAuthPort
    task: "JWT generation + validation"
    tokenBudget: 4000

  - adapter: secondary/rate-limiter
    port: IRateLimitPort
    task: "Sliding window limiter"
    tokenBudget: 3000

2. Build

Each step generates code within its boundary.

The AI agent receives:

  • The port interface (typed contract)
  • L1 summaries of related code (token-efficient)
  • The behavioral spec (acceptance criteria)
hex generate \
  --adapter secondary/auth \
  --port IAuthPort \
  --lang typescript

3. Test

Three levels, integrated into the workflow:

# Unit tests (mock ports, test logic)
bun test

# Property tests (fuzz inputs)
bun test --property

# Smoke tests (can it start?)
hex validate .

4–5. Validate & Ship

Validation is a blocking gate:

  • Behavioral spec assertions pass
  • Property test invariants hold
  • Smoke scenarios succeed
  • hex analyze finds no violations

Only then:

bun run build && git commit

Feature Progress Display

During feature development, hex shows a persistent status view that eliminates agent console noise:

hex feature: webhook-notifications
────────────────────────────────────────────────────────────────────────
Phase 1/7: SPECS     ✓ Complete (5 specs, 1 negative)
Phase 2/7: PLAN      ✓ Complete (8 tasks, 3 tiers)
Phase 3/7: WORKTREES ✓ Created 8 worktrees
Phase 4/7: CODE      ⟳ In Progress (3/8 done, 5 running)

Workplan:
  Tier 0 (domain/ports)
    ✓ domain-changes       (feat/webhook-notifications/domain)
    ✓ port-changes         (feat/webhook-notifications/ports)

  Tier 1 (adapters - parallel)
    ✓ git-adapter          Q:95  [===========] test
    ⟳ webhook-adapter      Q:82  [========---] lint
    ⟳ cli-adapter          Q:78  [=======----] test
    ⏳ mcp-adapter                [           ] queued
    ⏳ fs-adapter                 [           ] queued

  Tier 2 (integration)
    ⏳ composition-root          [           ] queued
    ⏳ integration-tests         [           ] queued

Overall: 38% │ Tokens: 124k/500k │ Time: 3m42s │ Blockers: 0
────────────────────────────────────────────────────────────────────────
[Press 'd' for details | 'q' to abort | 'h' for help]

Agent logs are redirected to .hex/logs/agent-<name>.log to keep the console clean. Press 'd' to view detailed logs, 'q' to abort cleanly, or 'h' for help. This is powered by IFeatureProgressPort with structured event streaming via the event bus.




Multi-Agent Swarm Coordination

Multi-Agent Swarm with Worktree Isolation


hex coordinates multiple AI agents working in parallel via ruflo (@claude-flow/cli), with lock-based coordination to prevent duplicate work and file conflicts across instances (ADR-022).

Agent Roles

Role Responsibility
planner Decomposes requirements into tasks
coder Implements one adapter boundary
tester Writes unit + property tests
reviewer Checks hex boundary violations
integrator Merges worktrees, integration tests
monitor Tracks progress, reports status

Swarm Configuration

interface SwarmConfig {
  topology: 'hierarchical' | 'mesh'
            | 'hierarchical-mesh';
  maxAgents: number;       // default: 4
  strategy: 'specialized' | 'generalist'
            | 'adaptive';
  consensus: 'raft' | 'pbft';
  memoryNamespace: string;
}
Swarm Port Interface (full)
interface ISwarmPort {
  // Lifecycle
  init(config: SwarmConfig): Promise<SwarmStatus>;
  createTask(task: SwarmTask): Promise<SwarmTask>;
  completeTask(taskId: string, result: string, commitHash?: string): Promise<void>;
  spawnAgent(name: string, role: AgentRole, taskId?: string): Promise<SwarmAgent>;

  // Pattern learning — agents get smarter over time
  patternStore(pattern: AgentDBPattern): Promise<AgentDBPattern>;
  patternSearch(query: string, category?: string): Promise<AgentDBPattern[]>;
  patternFeedback(feedback: AgentDBFeedback): Promise<void>;

  // Persistent memory across sessions
  memoryStore(entry: SwarmMemoryEntry): Promise<void>;
  memoryRetrieve(key: string, namespace: string): Promise<string | null>;

  // Hierarchical memory (layer > namespace > key)
  hierarchicalStore(layer: string, namespace: string, key: string, value: string): Promise<void>;
  hierarchicalRecall(layer: string, namespace?: string): Promise<SwarmMemoryEntry[]>;

  // Intelligence
  consolidate(): Promise<{ merged: number; removed: number }>;
  contextSynthesize(query: string, sources?: string[]): Promise<string>;
  getProgressReport(): Promise<AgentDBProgressReport>;
}

Dashboard (hex-hub)

hex Dashboard — architecture health, token efficiency, dependency graph, swarm status

hex-hub is a Rust-native dashboard service that runs as a system-wide daemon on port 5555. It provides real-time visibility into architecture health, token efficiency across summary levels, interactive dependency graphs with violation detection, and swarm agent/task status — all streamed via WebSocket.

# Auto-starts on any hex command (if binary installed)
hex analyze .

# Manual control
hex daemon start        # Start daemon
hex daemon stop         # Stop daemon
hex hub status          # Show status + connected projects

# Open in browser
open http://localhost:5555

Dashboard features:

  • Multi-project tabs — switch between projects with live freshness indicators
  • Architecture health ring — real-time score with violation/dead-export breakdown
  • Token efficiency panel — L0–L3 compression bars per file
  • Swarm status — agent list, task progress, status badges with pulse animations
  • Dependency graph — interactive canvas with hexagonal ring layout, zoom/pan, click-to-trace transitive deps, violation highlighting
  • Command chat — send commands (hex analyze, hex claude <prompt>, spawn-agent) directly from the browser
  • Event log — filterable real-time stream (errors, decisions, milestones)
  • Decision modal — interactive prompts for agent decisions requiring human input

1.5MB binary, zero runtime dependencies. Modern dark theme optimized for extended developer use.

Persistence & coordination:

  • Swarm state (agents, tasks, events) is persisted in SQLite (hex_hub.db) — survives daemon restarts (ADR-015)
  • Hub binary embeds a compile-time build hash for version verification against the TypeScript CLI (ADR-016)
  • Multi-instance coordination uses ICoordinationPort with worktree locks, task claiming, and activity broadcasting to prevent duplicate work and file conflicts (ADR-011, ADR-022)
  • SwarmOrchestrator acquires locks before worktree creation; WorkplanExecutor claims tasks before agent spawn — conflicts throw typed WorktreeConflictError / TaskConflictError

See hex-hub/README.md for full API reference.




Multi-Channel Notifications

hex provides real-time feedback through four notification channels behind a single INotificationEmitPort interface:

Channel Adapter Purpose
Terminal TerminalNotifier Color-coded messages, persistent status bar, interactive decision prompts
File Log FileLogNotifier Structured JSONL audit trail in .hex/activity.log, rotated at 10 MB
Webhook WebhookNotifier External integration (Slack, CI); batched delivery with exponential-backoff retry
Event Bus EventBusNotifier In-memory pub/sub for agent-to-agent coordination; wildcard subscriptions

Decision Requests

When agents encounter ambiguous choices (e.g., two valid architectural approaches), they emit a DecisionRequest with:

  • Numbered options with risk ratings
  • Configurable deadline (default: 5 minutes)
  • Auto-select default option if no human response (prevents agent stalls)

Status Line Format

[execute] coder-1: generating tests | quality: 85 | 3/6 adapters | ████░░ 50%

Fields: [phase], active agent and step, quality score from QualityScore.score, adapter completion ratio, and a Unicode progress bar. All channels are independently testable via constructor-injected dependencies (no global state).




Token-Efficient Summaries

A 500-line adapter becomes a 30-line L1 summary. This is how AI agents understand your codebase without blowing their context window.

Level What's Included Tokens Use Case
L0 File list only ~2% Project overview, file discovery
L1 Exports + function signatures ~6% Ideal for AI context — the sweet spot
L2 L1 + function bodies ~40% Detailed understanding of logic
L3 Full source code 100% Complete file contents
# Generate L1 summaries for the whole project
hex summarize src/ --level L1

Powered by tree-sitter (WASM) for language-agnostic AST extraction.




hex vs SPECKit vs BMAD

Capability SPECKit BMAD hex
Architecture enforcement - Docs only static analysis
Boundary violation detection - - import-graph
Adapter isolation - - enforced
Multi-agent orchestration - Manual swarm
Token efficiency - Sharding tree-sitter
Testing pipeline Spec-only TEA add-on integrated
Parallel development Single branch Monolithic worktrees
Code gen scope Prose Lifecycle docs typed contracts
Dead code detection - - dead exports
Pattern learning - - AgentDB

Why architecture-first beats spec-first

SPECKit gives AI agents prose descriptions. The agent decides how to structure the code. Works for small features, produces spaghetti at scale. Known issues: duplicative documentation, incomplete implementations that "look done" in specs.

BMAD simulates an agile team with 12+ markdown personas. No real multi-agent orchestration — users manually invoke each persona. Architecture decisions are in documents, not enforced in code. Complexity grows with every persona added.

hex gives AI agents typed port interfaces. The agent knows exactly what methods to implement, what types to accept, and what boundary it's working within. Architecture is enforced mechanically.

The difference compounds:

  • At 10 files, any approach works
  • At 100 files, only enforced boundaries prevent collapse
  • At 1000 files, hex's static analysis is the difference between a maintainable codebase and a rewrite



Installation

# Global install
npm install -g @anthropic-hex/hex

# Or use npx
npx @anthropic-hex/hex --help

Requirements: Node.js >= 20, Bun (for build/test)




CLI Reference

Command Description
hex build <requirements> Single entry point — auto-plans, orchestrates agents, generates code, analyzes, validates
hex scaffold <name> Create a new hex project with full structure
hex analyze <path> Architecture health check (dead code, violations, cycles)
hex summarize <path> --level <L0-L3> Token-efficient AST summaries via tree-sitter
hex generate Generate code within an adapter boundary
hex plan <requirements> Decompose requirements into workplan steps
hex validate <path> Post-build semantic validation (blocking gate)
hex orchestrate Execute workplan steps via swarm agents
hex status Swarm progress report
hex daemon [start|stop|status] Manage hex-hub daemon (Rust dashboard service, port 5555)
hex hub [start|stop|status] Alias for hex daemon
hex dashboard Legacy Node.js dashboard (fallback if hex-hub binary not installed)
hex mcp Start MCP stdio server for Claude Code / IDE integration
hex setup Install tree-sitter grammars + skills + agents + hex-hub binary
hex init [--fast|--minimal|--include|--large-project] Initialize project (supports large codebases via streaming scanner)
hex adr list List all ADRs with status
hex adr status Show ADR lifecycle summary
hex adr search <query> Search ADRs by keyword
hex adr abandoned Detect stale/abandoned ADRs
hex help Show all commands and usage
hex version Print current version

ADR Lifecycle

Architecture Decision Records follow a tracked lifecycle with automated staleness detection:

proposed → accepted → (deprecated | superseded | rejected)
    ↓           ↓
  stale     abandoned (no git activity in 90 days)

Commands:

  • hex adr list [--status accepted] — Filter by lifecycle status (proposed, accepted, deprecated, superseded, rejected)
  • hex adr status — Show status distribution (e.g., "15 accepted, 3 proposed, 2 deprecated")
  • hex adr search <query> — Full-text search across all ADR content and metadata
  • hex adr abandoned — Detect stale ADRs with no related commits in 90 days (indicates forgotten decisions)

ADRs are stored in docs/adrs/ and tracked via frontmatter (status, date, supersedes). The lifecycle system helps teams identify which architectural decisions are active vs. historical.




Claude Code Integration

Skills (Slash Commands)

Skill Description
/hex-feature-dev Full feature lifecycle with hex decomposition
/hex-scaffold Scaffold new hex project
/hex-generate Generate adapter code
/hex-summarize Token-efficient summaries
/hex-analyze-arch Architecture health check
/hex-analyze-deps Dependency + tech stack analysis
/hex-validate Post-build validation

MCP Tools

Available via hex mcp:

Tool Description
hex_build Single entry point — plans, orchestrates, analyzes, validates
hex_analyze Architecture health check
hex_analyze_json Analysis with JSON output
hex_summarize Summarize a single file
hex_summarize_project Summarize entire project
hex_validate_boundaries Validate hex boundary rules
hex_dead_exports Find unused exports
hex_scaffold Scaffold a new project
hex_generate Generate code from spec
hex_plan Create workplan
hex_orchestrate Run swarm orchestration
hex_status Query swarm progress
hex_dashboard_start Start dashboard server
hex_dashboard_register Register project
hex_dashboard_unregister Unregister project
hex_dashboard_list List registered projects
hex_dashboard_query Query dashboard data
hex_hub_command Send command to hub (analyze, build, validate, claude)
hex_hub_commands_list List pending/completed hub commands
hex_hub_command_status Check status of a hub command
hex_adr_list List all ADRs with status
hex_adr_search Search ADRs by keyword
hex_adr_status ADR lifecycle summary
hex_adr_abandoned Detect stale/abandoned ADRs
hex_secrets_has Check if a secret key exists
hex_secrets_resolve Resolve a secret value
hex_secrets_status Show secrets adapter chain status

Agent Definitions

Pre-built YAML agents for swarm orchestration:

planner hex-coder integrator swarm-coordinator dependency-analyst
dead-code-analyzer validation-judge behavioral-spec-writer scaffold-validator status-monitor
dev-tracker



Multi-Language Support

Powered by tree-sitter WASM for language-agnostic AST extraction:

Capability TypeScript Go Rust
AST Summarize (L0–L3) Full Full Full
Export extraction export keyword Capitalized names pub visibility
Import extraction import statements import declarations use declarations
Boundary validation Full Full Full
Code generation Full (TS rules) Full (Go rules) Full (Rust rules)
Path resolution .js.ts Module paths crate:: paths
Scaffold package.json + tsconfig.json go.mod Cargo.toml
Example project 4 apps 1 (weather) 1 (rust-api)

Build Enforcement

hex enforces compile/lint/test checks for all languages via IBuildPort:

Method TypeScript Go Rust
compile() tsc --noEmit go build ./... cargo check
lint() eslint --format json golangci-lint run --out-format json cargo clippy -- -D warnings
test() bun test go test ./... -json cargo test

The pre-commit hook automatically detects staged file languages and runs the corresponding toolchain checks. CI includes a rust-check job for hex-hub validation (compiles, lints, tests, and verifies --build-hash works).

Example: Go Backend (Weather API)

The examples/weather/ directory shows hex applied to a Go project:

examples/weather/backend/src/
  core/
    domain/               # Weather types, F1 race data
    ports/                # IWeatherPort, ICachePort
    usecases/             # F1Service (composes ports)
  adapters/
    primary/
      http_adapter.go     # HTTP handlers + HTML templates
    secondary/
      jolpica_adapter.go  # External F1 API client
      cache_adapter.go    # In-memory cache with TTL
  composition-root.go     # Wires adapters to ports

Same hexagonal rules, different language. The architecture transfers.




Project Structure

src/
  core/
    domain/              # Value objects, entities, domain events
    ports/               # Typed interfaces (input + output)
    usecases/            # Application logic
  adapters/
    primary/             # CLI, MCP, Dashboard, Notifications
    secondary/           # FS, Git, TreeSitter, LLM, Ruflo, Build, Registry, Secrets
  infrastructure/        # Tree-sitter query definitions
  composition-root.ts    # Single DI wiring point
  cli.ts                 # CLI entry point
  index.ts               # Library public API
tests/
  unit/                  # London-school mock-first tests
  integration/           # Real adapter tests
examples/                # Reference apps (weather, rust-api, flappy-bird, todo-app, test-app, summaries)
hex-hub/                 # Rust dashboard daemon (axum, system-wide on port 5555)
agents/                  # Agent definitions (YAML)
skills/                  # Skill definitions (Markdown)
config/                  # Language configs, tree-sitter settings
docs/
  adrs/                  # Architecture Decision Records
  analysis/              # Adversarial review reports



Status Line

hex includes a status line script that shows real-time swarm and project health directly in your Claude Code terminal:

⬡ hex │ my-app │ ⎇main │ ●swarm 2⚡ [3/5] │ ●db │ ◉localhost:3456 │ ◉mcp │ 87/100

Indicators:

  • Swarm green (agents active) / yellow (available, idle) / dim (not configured)
  • Agent counts2⚡ active, 1💤 idle, [3/5] tasks completed
  • AgentDB — pattern store connectivity
  • Dashboard — clickable URL when running (auto-starts on project load)
  • MCP — hex MCP server status
  • Score — last architecture health score

Three-tier detection: .hex/status.json (written by hooks) → ~/.claude-flow/metrics (daemon) → ruflo MCP config (fallback). Auto-configured during hex init.




Build & Test

bun run build        # Bundle CLI + library to dist/
bun test             # Run all tests (unit + property + smoke)
bun run check        # TypeScript type check (no emit)
hex analyze .   # Architecture validation
hex setup       # Install grammars + skills + agents + hex-hub binary
cargo build --release -p hex-hub  # Build dashboard binary manually



Design Decisions

Why these choices?
Decision Rationale
Tree-sitter over regex WASM-based AST extraction works across languages; regex breaks on edge cases
Hybrid TS+Rust via NAPI Tree-sitter hot path in native Rust (5-10x faster than WASM); falls back to WASM if binary unavailable (ADR-010)
Ruflo as required dep Swarm coordination is not optional; even solo workflows benefit from task tracking
Single composition root Only one file imports adapters; adapter swaps are one-line changes
L0-L3 summary levels AI agents need different detail at different phases; L1 is the sweet spot
Worktree isolation Each agent gets a git worktree, not just a branch; prevents merge conflicts
safePath() protection FileSystemAdapter prevents path traversal outside project root
execFile not exec RufloAdapter prevents shell injection from untrusted inputs
London-school testing Mock ports, test logic; hexagonal architecture makes this natural
No mock.module() Tests use dependency injection (Deps pattern), never mock.module() — prevents mock/prod divergence (ADR-014)
Streaming filesystem scanner streamFiles() uses an async generator with BFS queue and inode tracking — bounded memory regardless of project size (ADR-021)
.hexignore defaults Smart exclusions (target/, node_modules/, build/) prevent OOM on large projects; falls back to .gitignore
Lock-before-worktree SwarmOrchestrator acquires coordination lock before creating worktree; prevents parallel agents from stomping each other (ADR-022)
Claim-before-spawn WorkplanExecutor claims tasks via hub before spawning agents; prevents duplicate work across instances
hex_build single entry point Users describe what to build; hex handles plan → orchestrate → analyze → validate internally
Pluggable secrets chain ISecretsPort adapters stack: Infisical → LocalVault → env-var; composition root selects
Dashboard auto-start Dashboard HTTP server launches on project load; port conflicts and stale locks self-heal
Rust hub over Node hub 1.5MB binary vs Node.js process; zero runtime deps; system-wide daemon serves all projects
WebSocket over SSE Unified bidirectional channel for events, commands, and chat; 25s keepalive pings detect dead connections
run-claude hub command Invoke Claude CLI from dashboard chat; enables browser-based agent interaction without terminal

Secrets Management

hex includes ISecretsPort with a pluggable adapter chain for secret resolution:

Adapter When It's Used
InfisicalAdapter Production — fetches from Infisical vault
LocalVaultAdapter Development — encrypted local file (~/.hex/vault.json)
EnvSecretsAdapter Fallback — reads from environment variables
CachingSecretsAdapter Wraps any adapter with TTL-based in-memory cache

The composition root selects the adapter chain based on environment. Secrets never leak into domain or adapter code — only the composition root calls ISecretsPort.

Security

Protection Implementation
Path traversal FileSystemAdapter.safePath() blocks ../ escapes
Shell injection RufloAdapter uses execFile (not exec)
Secret management ISecretsPort — Infisical / LocalVault / env-var adapter chain
XSS prevention Primary adapters must not use innerHTML with external data
Credential safety .env files are gitignored; .env.example provided
Dashboard auth Bearer token authentication for HTTP endpoints
Pre-commit gate Security audit hook blocks commits with violations



Credits & References

hex builds on the Hexagonal Architecture pattern (also known as Ports and Adapters), originally conceived by Alistair Cockburn in 2005.

"Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases." — Alistair Cockburn

Foundational Work

Key Technologies

  • tree-sitter — Max Brunsfeld et al. Language-agnostic parsing framework powering hex's L0-L3 AST summaries
  • ruflo / claude-flow — Reuven Cohen (@ruvnet). Multi-agent swarm coordination framework
  • Infisical — Open-source secrets management platform integrated via ISecretsPort

Authors

Contributor Role
Gary (@gaberger) Creator, architect, primary developer
Claude (Anthropic) AI pair programmer — code generation, testing, documentation

License

MIT — Use it, fork it, build on it.




Hexagonal Architecture   Swarm Powered   Tree-sitter

Built for AI agents that write code, not just chat about it.

Quick Start  •  Architecture  •  Workflow  •  Swarm  •  CLI  •  Claude Code

About

Hex HarnassEngineering for AI based development

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors