Contributing to Aether
Active Development
Aether is under rapid, active development. Codebase structure and patterns are evolving. Before starting a large contribution, open an issue to align on approach.
Development Setup
Prerequisites
| Tool | Version | Purpose |
|---|---|---|
| Go | 1.24+ | Backend development |
| Node.js | 22+ | Frontend development |
| Docker & Docker Compose | 24.0+ / 2.0+ | Running services |
| golang-migrate | latest | Database migrations |
| golangci-lint | latest | Go linting |
Quick Start
bash
# 1. Clone
git clone https://github.com/amansrivastava/hacky-automation.git
cd hacky-automation
# 2. Start infrastructure (DB, Redis, NATS, LiteLLM)
docker-compose up -d postgres redis nats litellm
# 3. Run backend
cd goway
cp .env.example .env
# Edit .env with your configuration
make run
# 4. Run frontend (separate terminal)
cd web
npm install
npm run devSee the Installation Guide for detailed setup instructions.
Development Workflow
1. Branch Naming
bash
git checkout -b feature/your-feature-namePrefixes:
feature/— New featuresfix/— Bug fixesdocs/— Documentation onlyrefactor/— Code restructuring without behavior changestest/— Test additions or changes
2. Backend Development Commands
bash
cd goway
make run # Run locally with auto-reload
make build # Build binary to bin/server
make test # Run all tests
make test-race # Run tests with race detector
make fmt # Format code (gofmt)
make lint # Run golangci-lint
make migrate-up # Apply pending migrations
make migrate-down # Rollback last migration3. Frontend Development Commands
bash
cd web
npm run dev # Dev server with HMR at :5173
npm run build # Production build
npm run lint # ESLint4. Write Tests (Mandatory)
All code contributions must include tests. This is a hard requirement.
bash
cd goway
make test # All tests
go test -v ./... # Verbose
go test -cover ./... # With coverage
go test -race ./... # Race detector
go test -run TestSpecific ./path/ # Specific testSee the Testing Guide for requirements and patterns.
5. Lint and Format
bash
cd goway && make fmt && make lint
cd web && npm run lint6. Commit Format
<type>: <subject>
<body (optional)>Types: feat, fix, docs, refactor, test, chore
7. Pull Request
- Clear title and description
- Reference related issues
- Screenshots for UI changes
- All tests passing
Code Patterns
Repository Pattern (Go)
All database access goes through interfaces defined in domain/port/repository/:
go
// Interface in domain/port/repository/agent.go
type AgentRepository interface {
GetByID(ctx context.Context, id string) (*entity.Agent, error)
List(ctx context.Context) ([]*entity.Agent, error)
Create(ctx context.Context, agent *entity.Agent) error
Update(ctx context.Context, agent *entity.Agent) error
Delete(ctx context.Context, id string) error
}
// Implementation in infrastructure/adapter/postgres/agent.go
type postgresAgentRepository struct {
db *pgxpool.Pool
}
func (r *postgresAgentRepository) GetByID(ctx context.Context, id string) (*entity.Agent, error) {
query := `SELECT id, name, role_id, prompt FROM agents WHERE id = $1`
var a entity.Agent
err := r.db.QueryRow(ctx, query, id).Scan(&a.ID, &a.Name, &a.RoleID, &a.Prompt)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrNotFound
}
return nil, fmt.Errorf("failed to get agent %s: %w", id, err)
}
return &a, nil
}Error Handling
go
// Use sentinel errors for specific cases
var (
ErrNotFound = errors.New("not found")
ErrInvalidInput = errors.New("invalid input")
)
// Wrap errors with context (never swallow)
if err != nil {
return fmt.Errorf("failed to resolve tasks for trigger %s: %w", trigger, err)
}Configuration
go
// Use env struct tags (caarlos0/env)
type Config struct {
DatabaseURL string `env:"DATABASE_URL,required"`
NATSUrl string `env:"NATS_URL,required"`
RedisURL string `env:"REDIS_URL,required"`
LLMBaseURL string `env:"LLM_BASE_URL,required"`
AgentGatewayEnabled bool `env:"AGENT_GATEWAY_ENABLED" envDefault:"false"`
AgentGatewayPort string `env:"AGENT_GATEWAY_PORT" envDefault:"9090"`
}React/TypeScript (Frontend)
typescript
// Type everything
interface Agent {
id: string;
name: string;
role_id: string;
prompt: string;
created_at: string;
}
// Functional components with typed props
export const AgentCard: React.FC<{ agent: Agent; onEdit: (id: string) => void }> = ({
agent,
onEdit,
}) => (
<div>
<h3>{agent.name}</h3>
<button onClick={() => onEdit(agent.id)}>Edit</button>
</div>
);Pull Request Checklist
- [ ] All existing tests pass (
make test) - [ ] New code has tests covering success and error cases
- [ ] Test coverage ≥80% for new code, ≥90% for critical paths
- [ ]
make fmtandmake lintpass - [ ] No merge conflicts with main
- [ ] Documentation updated if behavior changed
- [ ] Database migration included if schema changed (with
.up.sqland.down.sql)
Key Resources
- Architecture Overview — System design and component relationships
- Testing Guide — Testing requirements and patterns
- Database Guide — Migrations and schema
- Adding Integrations — How to add a new issue tracker
- Agent Runtime Protocol — External agent API
- REST API Reference — HTTP endpoints
