Skip to the content.

Embercore Development Guide

A comprehensive guide for contributors. For project-specific terminology, see the Glossary. For design rationale, see the Architecture Decision Records.


Prerequisites

Tool Version Purpose
Go 1.23+ Engine development
Node.js 20+ Web dashboard (see .nvmrc)
pnpm 9+ Monorepo package management
Anthropic API key Required for E2E tests (ANTHROPIC_API_KEY)
Git Version control

Repository Structure

embercore/
├── apps/
│   └── web/                    # Next.js 16 web dashboard
│       ├── src/
│       │   ├── app/            # App Router pages & API routes
│       │   │   ├── agents/     # Agent management UI
│       │   │   ├── plans/      # Plan list & detail views
│       │   │   ├── setup/      # Onboarding/setup flow
│       │   │   └── api/        # API routes (brief, run, checkpoint, artifact)
│       │   ├── components/     # Shared UI (app-shell, sidebar)
│       │   └── lib/            # Client utilities (herald, mock-data, workspace)
│       ├── package.json
│       └── next.config.ts
│
├── packages/
│   └── engine/                 # Go engine (MCP server + CLI)
│       ├── main.go             # Entry point (stdio MCP / HTTP server)
│       ├── Makefile             # build, dev, test, lint, clean, release
│       ├── go.mod              # Go module (modernc.org/sqlite, mcp-go, etc.)
│       ├── agents/             # AI agent implementations
│       │   ├── athena/         # Planner agent
│       │   │   ├── athena.go   # Agent struct, New(), Plan()
│       │   │   └── plan.go     # Plan generation types, YAML marshal
│       │   ├── hermes/         # Executor agent
│       │   │   ├── hermes.go   # Orchestration, checkpoint handling
│       │   │   ├── executor.go # Step execution logic
│       │   │   └── approval.go # Approval types
│       │   ├── apollo/         # Creative/copy agent
│       │   │   ├── apollo.go   # Content generation
│       │   │   └── types.go    # CopyRequest, CopyResult, Variant
│       │   ├── hephaestus/     # Builder/assembler agent
│       │   │   ├── hephaestus.go # Assembly modes (template, LLM, deterministic)
│       │   │   └── types.go    # BuildRequest, ArtifactRef, OutputType
│       │   └── hestia/         # Memory/storage agent
│       │       └── hestia.go   # SQLite wrapper (SavePlan, StartRun, etc.)
│       ├── plan/               # Shared plan types
│       │   ├── plan.go         # Spec, Step, Input, Parse(), Validate()
│       │   └── toposort.go     # Kahn's algorithm for execution layers
│       ├── cmd/
│       │   └── embercore/      # CLI binary
│       │       ├── main.go     # Cobra entry point
│       │       ├── root.go     # Root command, subcommands
│       │       ├── plan.go     # `embercore plan` command
│       │       ├── run.go      # `embercore run` command
│       │       ├── resume.go   # `embercore resume` command
│       │       └── status.go   # `embercore status` command
│       ├── tools/              # MCP tool handlers
│       │   ├── run_workflow.go # Full plan→execute workflow
│       │   ├── pm_plan.go      # Plan generation tool
│       │   ├── run_research.go # Research task tool
│       │   ├── run_brand.go    # Brand/positioning tool
│       │   ├── run_ux.go       # UX task tool
│       │   ├── run_gtm.go      # Go-to-market tool
│       │   ├── request_approval.go # Checkpoint approval tool
│       │   ├── assemble_plan.go    # Artifact assembly tool
│       │   └── prompts.go     # Prompt loader utility
│       ├── prompts/            # Embedded prompt templates (*.md)
│       │   └── prompts.go     # go:embed loader
│       ├── internal/           # Internal packages
│       │   ├── provider/       # LLM provider abstraction
│       │   │   ├── provider.go # Provider interface, Message, Options
│       │   │   ├── factory.go  # NewFromEnv() factory
│       │   │   ├── anthropic.go # Anthropic implementation
│       │   │   └── openai.go   # OpenAI implementation (+ base URL)
│       │   ├── store/          # SQLite persistence
│       │   │   ├── store.go    # DB init, WAL mode, connection
│       │   │   ├── migrations.go # Schema migrations
│       │   │   └── models.go   # Plan, Checkpoint, Run, Artifact types
│       │   ├── state/          # Filesystem state (atomic writes, lockfiles)
│       │   │   └── state.go
│       │   ├── claude/         # Herald — direct Anthropic client
│       │   │   └── client.go
│       │   ├── logger/         # stderr-only structured logging
│       │   │   └── logger.go
│       │   ├── ctxguard/       # Context window management
│       │   │   └── guard.go
│       │   └── search/         # DuckDuckGo fallback search
│       │       └── ddg.go
│       └── e2e/                # End-to-end tests
│
├── examples/                   # Example plans and workflows
├── docs/                       # Documentation
│   ├── decisions/              # Architecture Decision Records
│   ├── architecture.md         # System architecture overview
│   ├── agents.md               # Agent role descriptions
│   ├── quickstart.md           # Getting started guide
│   ├── glossary.md             # Terminology reference
│   └── DEVELOPMENT.md          # This file
│
├── pnpm-workspace.yaml         # Workspace: apps/*, packages/*, examples/*
├── package.json                # Root scripts, version 0.1.0
├── CONTRIBUTING.md             # Contribution guidelines
├── ROADMAP.md                  # Project roadmap
├── CHANGELOG.md                # Release changelog
└── README.md                   # Project overview

Package Dependency Graph

tools/          ──→  agents/*  ──→  internal/provider/
  │                    │                  │
  │                    ▼                  │
  │               plan/                   │
  │                    │                  │
  │                    ▼                  │
  └──────────→  agents/hestia/  ──→  internal/store/
                                          │
                                          ▼
                                   internal/state/

Key relationships:


Getting Started

1. Clone and install dependencies

git clone <repo-url> && cd embercore
pnpm install          # Node.js dependencies (web + root)
cd packages/engine
go mod download       # Go dependencies

2. Configure environment

# Required
export ANTHROPIC_API_KEY="sk-ant-..."

# Optional
export EMBERCORE_PROVIDER="anthropic"    # or "openai"
export OPENAI_API_KEY="sk-..."
export EMBERCORE_MODEL="claude-sonnet-4-20250514"
export OPENAI_BASE_URL="http://localhost:11434/v1"  # Ollama

3. Build and run

# Engine
cd packages/engine
make build          # → embercore-engine binary
make dev            # Development mode

# Web dashboard
cd apps/web
pnpm dev            # → http://localhost:3000

# CLI
cd packages/engine
go run ./cmd/embercore plan "Launch product X"
go run ./cmd/embercore run <plan-id>
go run ./cmd/embercore status

4. Run tests

# Engine unit tests
cd packages/engine
make test

# Engine lint
make lint

# Web (from apps/web)
pnpm lint

Adding a New Agent

Follow this step-by-step template to add a new agent (e.g., “Artemis” for analytics).

Step 1: Create the package

packages/engine/agents/artemis/
├── artemis.go      # Agent struct, New(), core methods
└── types.go        # Request/response types (if needed)

Step 2: Define the agent struct with functional options

package artemis

import (
    "github.com/your-org/embercore/packages/engine/internal/provider"
    "github.com/your-org/embercore/packages/engine/agents/hestia"
)

type Artemis struct {
    provider provider.Provider
    hestia   *hestia.Hestia
    model    string
}

type Option func(*Artemis)

func WithProvider(p provider.Provider) Option {
    return func(a *Artemis) { a.provider = p }
}

func WithHestia(h *hestia.Hestia) Option {
    return func(a *Artemis) { a.hestia = h }
}

func WithModel(model string) Option {
    return func(a *Artemis) { a.model = model }
}

func New(opts ...Option) *Artemis {
    a := &Artemis{}
    for _, opt := range opts {
        opt(a)
    }
    return a
}

Step 3: Implement core methods

Define the agent’s primary action (e.g., Analyze, Generate, Research). Use the provider for LLM calls:

func (a *Artemis) Analyze(ctx context.Context, req AnalysisRequest) (*AnalysisResult, error) {
    messages := []provider.Message{
        {Role: "user", Content: buildPrompt(req)},
    }
    resp, err := a.provider.Complete(ctx, messages, provider.Options{})
    if err != nil {
        return nil, fmt.Errorf("artemis analyze: %w", err)
    }
    // Parse and return result
}

Step 4: Register the agent name

Add the agent name to Athena’s known agents list in agents/athena/athena.go so plans can reference it.

Step 5: Create an MCP tool handler (optional)

Add a tool handler in tools/ and register it in main.go’s buildServer():

// tools/run_analytics.go
func handleRunAnalytics(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    // Parse input, invoke Artemis, return result
}

Step 6: Add prompt templates (optional)

Create prompts/artemis_*.md files — they’ll be auto-embedded via //go:embed *.md.


Adding a New Provider

Step 1: Implement the interface

Create internal/provider/newprovider.go:

package provider

type NewProvider struct {
    apiKey string
    model  string
}

func (p *NewProvider) Complete(ctx context.Context, messages []Message, opts Options) (*Response, error) {
    // Call the provider's API
}

func (p *NewProvider) Name() string {
    return "newprovider"
}

Step 2: Update the factory

Add the provider to NewFromEnv() in internal/provider/factory.go:

case "newprovider":
    apiKey := os.Getenv("NEWPROVIDER_API_KEY")
    if apiKey == "" {
        return nil, fmt.Errorf("NEWPROVIDER_API_KEY required")
    }
    return &NewProvider{apiKey: apiKey, model: model}, nil

Step 3: Document environment variables

Update the Glossary and ADR-003 with the new provider’s configuration.


Testing Strategy

Layer Tool Location What it tests
Unit go test *_test.go alongside source Individual functions and types
Integration go test e2e/ Full workflow with real LLM calls
Lint make lint Engine-wide Code style and static analysis
Web lint pnpm lint apps/web/ ESLint for TypeScript/React

Running tests

cd packages/engine

# All unit tests
make test

# Specific package
go test ./agents/athena/...
go test ./plan/...
go test ./internal/store/...

# E2E (requires ANTHROPIC_API_KEY)
go test ./e2e/...

# With verbose output
go test -v ./...

Writing tests


Environment Variables Reference

Variable Required Default Purpose
ANTHROPIC_API_KEY Yes* Anthropic API authentication
OPENAI_API_KEY Yes* OpenAI API authentication
EMBERCORE_PROVIDER No anthropic Provider selection
EMBERCORE_MODEL No Provider default Model override
OPENAI_BASE_URL No OpenAI default Custom endpoint (Ollama, LM Studio)
HERALD_MODEL No Herald client model override
HERALD_MAX_TOKENS No Herald max token limit
HERALD_TIMEOUT_SECONDS No Herald request timeout
HERALD_ANTHROPIC_BASE_URL No Herald custom Anthropic endpoint

*At least one API key is required depending on the chosen provider.


Makefile Targets (Engine)

make build    # Build embercore-engine binary
make dev      # Run in development mode
make test     # Run all Go tests
make lint     # Run linters
make clean    # Remove build artifacts
make release  # Build release binaries

Further Reading