Development Guide
Prerequisites
# Go 1.25+
go version
# Complexity checking tools
go install github.com/uudashr/gocognit/cmd/gocognit@latest
go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
Project Layout
goa/
├── cmd/goa/main.go # Entry point
├── doc.go # Module documentation
├── config/ # Configuration cascade
├── core/ # Core engine
│ ├── command.go # Command interface + registry
│ ├── router.go # Command routing
│ ├── agentmanager.go # Agent lifecycle
│ ├── execution.go # Execution modes
│ ├── loopdetector.go # Loop detection
│ ├── sessionstore.go # Session persistence
│ ├── docengine.go # Documentation engine
│ ├── context.go # Command context
│ └── commands/ # Command implementations
├── internal/ # Shared types, errors, git worktree,
│ └── agentic/ # Agent SDK
├── tui/ # ANSI TUI (inspired by pi/OpenCode/kimi-code)
├── tools/ # Tool implementations
├── memory/ # Memory store
├── prompts/ # System prompts (mode, skill, orchestrate, plan, goal)
├── provider/ # Provider management
├── skills/ # Skill registry + runner
├── multiagent/ # Multi-agent orchestrators
├── plugins/ # JS plugin system
├── profile/ # Built-in profile definitions
├── scripts/ # Build and development scripts
├── docs/ # Documentation
├── web/ # Website (GitHub Pages)
├── Makefile # Build automation
├── TODO.md # Active development tasks
└── bugs.md # Known issues and roadmap
Common Tasks
Build
make build # Build binary
make install # Install to GOPATH/bin
make clean # Remove artifacts
Test
make test # Full test suite with race detection + coverage
make test-short # Fast tests (no race)
make test-race # Race detection only
make test-cover # HTML coverage report → coverage.html
Quality
make vet # go vet
make fmt # go fmt
make lint # gocognit + gocyclo complexity checks
Run
make run # Build and run
go run . # Run without building
go run . --model gpt-4o --debug # With flags
Code Style
Formatting
Goa follows standard Go formatting (gofmt). Run make fmt before committing.
Complexity Budget
| Scope | Cognitive Limit | Cyclomatic Limit |
|---|---|---|
| Config parsing | 20 | — |
| TUI rendering | 18 | — |
| All other logic | 15 | 12 |
Run complexity checks with make lint or:
gocognit -over 15 ./...
gocyclo -over 12 ./...
Any function exceeding thresholds must be refactored.
Error Format
All tool errors follow the standard format:
[tool_name error: error_type]
Detail message
Hint: Actionable suggestion for recovery
Defined in internal/errors.go:
type ToolError struct {
Tool string // tool name
Type string // error category (file_not_found, permission_denied, etc.)
Detail string // human-readable detail
HintText string // actionable recovery hint
}
Testing Standards
Coverage Targets
| Package | Target |
|---|---|
internal/ |
≥90% |
config/ |
≥85% |
core/ |
≥80% |
tools/ |
≥80% |
memory/, prompts/, provider/ |
≥75% |
tui/ |
≥70% (state logic) |
Test Patterns
- Table-driven tests for validation logic
t.TempDir()for filesystem tests- Independent tests — no shared mutable state
- Explicit error cases — test missing params, invalid input, permission errors
- Naming:
Test<FunctionName>_<Scenario>
Testing TUI behavior (no real terminal)
Never debug a TUI/UI bug by running goa against a live model, and never assert
on ANSI escape bytes. The TUI is testable as data via a protocol-free
screen model and a Filmstrip recorder.
- Driving an event sequence: use the
uiScenarioharness (internal/app/ui_scenario_test.go) to feedagentic.OutputEvents through the realApp.handleAgentOutputEventand record atui.Filmstripper step. - Component-only behavior (input/navigation/overlays): drive
tui.TUIdirectly on a fake terminal and readengine.AgentFrame().
Load the tui-test skill (.agents/skills/tui-test/SKILL.md) for the
full workflow, event/state cheat sheet, and anti-patterns. See also the
"Agent-testable UI (Filmstrip)" section in docs/TUI.md.
Running Tests
# All tests
go test -count=1 -race -cover ./...
# Single package
go test -count=1 -race -cover ./config/...
# Single test
go test -count=1 -race -run TestLoader_Load ./config/...
# All packages must pass before commit
make test
Working with the Agent SDK
The Agent SDK lives in internal/agentic/. Its key types are used throughout Goa:
agentic.Agent— Created byAgentManageragentic.Tool— Implemented by all tools intools/agentic.LLMProvider— Created byprovider.Manageragentic.OutputObserver— Consumed by TUI panesagentic.AgentBus— Multi-agent messagingagentic/skillrunner.Runner— Skill execution
Adding a New Tool
- Create a file in
tools/implementingagentic.Tool - Optionally implement
tools.Documentablefor self-documentation - Register in
main.go'sregisterTools()function - Write tests in
tools/*_test.go - Run complexity checks
Adding a New Command
- Create a file in
core/commands/implementingcore.Command - Self-register via
init():core.RegisterCommand(&MyCommand{}) - Add command routing if needed in
core/router.go - Write tests in
core/commands/*_test.go
Adding a New Profile
- Add a built-in in
profiles/defaults.go - Or define custom in
~/.goa/config.yamlwithextends
Commit Guidelines
- Atomic commits — one logical change per commit
- Test first — write tests before implementation
- All tests pass —
make testbefore commit - Complexity check —
make lintpasses - Descriptive messages — explain what and why
Continuous Integration
(CI to be set up — currently local builds only.)