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:
tools/handlers invoke agents and referenceplan/types- All agents depend on
internal/provider/for LLM access (viaWithProvider) - Athena, Hermes, Apollo, Hephaestus optionally depend on Hestia for persistence
- Hestia wraps
internal/store/(SQLite) andinternal/state/(filesystem) plan/is a leaf package with no internal dependencies — imported by agents and toolsinternal/packages are not importable outside the engine module
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
- Place test files next to source:
athena.go→athena_test.go - Use table-driven tests for validation logic
- Mock the
Providerinterface for unit tests (avoid real LLM calls) - Use Hestia with an in-memory SQLite database (
:memory:) for store 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
- Architecture Overview — System design diagrams
- Agent Roles — Detailed agent descriptions
- Quickstart Guide — First-run tutorial
- Glossary — Terminology reference
- ADR Index — Architecture Decision Records
- Contributing Guidelines — PR process and conventions
- Roadmap — Planned features and milestones