Changelog¶
All notable changes to HexaGo will be documented here.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
v0.5.0 - 2026-08-05¶
Added¶
with_testonhexago_add_service— new MCP tool param to force test generation for servicesfrom_portonhexago_add_service— new MCP tool param for port-based signature inferencecliproject type in MCP init tool (was already CLI-flagged but missing from MCP tool)db_driveron init MCP tool — postgres|sqlite3 selectionfieldalignmenttarget in Makefile, added to preflighte2etarget in Makefile for full generator E2E tests
Changed¶
- MCP param rename:
worker_type→type,migration_type→type,from-port→from_port,test→with_test— aligns MCP tool params with Go struct field naming GOPATHin Makefile →$(shell go env GOPATH)for cross-platform portability
Fixed¶
- MCP tool args now correctly mapped to CLI flags —
cliFlag()helper auto-converts snake_case MCP params to hyphenated cobra flags (--project-type,--core_logic→--core-logic), preventing unknown-flag errors
Refactored¶
- Extracted
cliFlag()frominternal/mcp/register.go— single source of truth for MCP→CLI flag mapping
v0.4.1 - 2026-07-01¶
Added¶
- GitHub community templates: ISSUE_TEMPLATE, pull request template, and other community health files
- README badges: Go version, build status, coverage, and other project badges
Changed¶
- README cleanup: stripped noise, replaced inline documentation references with doc site links
- Reduced cyclomatic complexity (crap scores) for
AdapterGenerator,ProjectGenerator.addDependencies, andrunInit
Fixed¶
- Template rendering error when certain template conditions are met
- Linting issues; lowered crap threshold for stricter CI enforcement
v0.4.0 - 2026-06-29¶
Note on version jump (v0.1.3 → v0.4.0): Earlier releases used PATCH bumps for new features, which violates semver's rule that MINOR increments for features, PATCH for bug fixes only. We've skipped
v0.2.0andv0.3.0to align our tag history with actual release content. From here forward: MINOR for features/changes, PATCH for bug fixes only.
Added¶
Semantic Code Analysis via go/packages¶
- New package
internal/analyzer/provides Go semantic analysis without LSP dependencies loader.go— loads project packages usinggo/packagesinterfaces.go— discovers port interfaces ininternal/core/structs.go— discovers domain structs-
types.go— core types:PortInfo,MethodInfo,ParamInfo,DomainStruct,FieldInfo -
New CLI flags for semantic code generation:
-
--from-port <PortName>— infers method signatures from an existing port interface -
go-testgen integration for
add adapter(requiresgo-testgen≥ v0.1.0): - New
testing:block in.hexago.yamlwithenabledfield hexago init --with-tests— setstesting.enabled: truein the generated confighexago add adapter primary|secondary --with-test— enables test generation for this runhexago add adapter primary|secondary --no-test— disables test generation for this run (overrides config)- After adapter generation, HexaGo runs
go-testgen report --format jsonon the adapter package and executes the suggestedgo-testgen gencommand for each untested exported function - If
go-testgenis missing or outdated, a warning is printed and generation continues normally -
New package
internal/testgen/encapsulates allgo-testgenexec, version check, and JSON parsing -
Templates now generate code with actual signatures:
service/service.go.tmpl— generates methods from port interfaceadapter/external.go.tmpl— generates methods from port interface-
Includes compile-time interface verification:
var _ PortName = (*Adapter)(nil) -
Template helper function
zeroVal— returns correct zero values for Go types: string→""error→nilint,int64→0bool→falsefloat64→0
Changed¶
Adapter Template Directory Restructured into primary/ and secondary/¶
- Adapter templates reorganized to mirror the generated project's
adapters/primaryandadapters/secondarysplit:
| Old path | New path |
|---|---|
templates/adapter/http.go.tmpl |
templates/adapter/primary/http.go.tmpl |
templates/adapter/grpc.go.tmpl |
templates/adapter/primary/grpc.go.tmpl |
templates/adapter/database.go.tmpl |
templates/adapter/secondary/database.go.tmpl |
templates/adapter/external.go.tmpl |
templates/adapter/secondary/external.go.tmpl |
templates/adapter/cache.go.tmpl |
templates/adapter/secondary/cache.go.tmpl |
internal/generator/adapter.gorender calls updated to use the new paths
Domain Constructor Parameters Auto-Generated from Field Definitions¶
domain/entity.go.tmplanddomain/value_object.go.tmplupdated to use{{.ConstructorParams}}and{{.ConstructorInit}}— constructors now emit real parameter lists and struct initializers derived from--fieldsat generation time- Replaces the previous
/* TODO: Add constructor parameters */and// TODO: Initialize fieldsplaceholders; generated entities and value objects are immediately usable - Powered by two new helpers in
internal/generator/domain.go: constructorParams(fields []Field) string— comma-separatedparamName typelistconstructorInit(fields []Field) string— indentedFieldName: paramName,block
Fixed¶
-
Output directory paths now correctly use
OutputDirfrom project config -
Database adapter generation now auto-creates
internal/core/domain/errors.go(exportsErrNotFound) when the file does not exist, resolving import errors in generated repositories
Go Reserved Keyword Sanitisation in Constructor Parameters¶
- Field names that lower-case to a Go reserved keyword (e.g.
type,map,range) are now automatically renamed in constructor parameter lists by appendingVal(e.g.type string→typeVal string) — the struct field name is left unchanged - Uses
go/token.IsKeyword()from the standard library; covers all 25 Go keywords with zero maintenance overhead - Affected helpers:
constructorParams()andconstructorInit()ininternal/generator/domain.go - New utilities in
pkg/utils/case.go: SafeParamName(name string) string— lowercases first letter, appendsValon keyword collisionLcFirst(s string) string— lowercases the first rune of a stringZeroValueFor(typ string) string— returns correct Go zero-value literal for a type string
v0.1.3 - 2026-04-07¶
Added¶
Handler Plugin Pattern (Use(ServerHandler) Server)¶
Serverinterface inpkg/server/server.goextended withUse(ServerHandler) Server— a fluent registration method that accepts any type implementingServerHandlerServerHandlerinterface: a singleConfigure(Server)method — each handler package mounts its own routes when called, enabling self-contained, isolated handler units- Route handlers are now registered on the main server through
Use()instead of being wired directly inside the adapter constructor
pkg/httpserver — Exported Framework Server¶
- Framework-specific server implementations moved from
internal/adapters/{inbound}/http/topkg/httpserver/(package namehttpsrv) - Each framework server (
chi,echo,gin,fiber,stdlib) exposes its underlying router/engine as a public field so handlers can register routes without casting: - Chi →
Server.Router chi.Router - Echo →
Server.Echo *echo.Echo - Gin →
Server.Router *gin.Engine - Fiber →
Server.App *fiber.App - stdlib →
Server.Mux *http.ServeMux Use(handler srv.ServerHandler) srv.Serverimplemented on every framework serverServerConfig.Metricsfield removed — metrics are now registered as a regular handler
Observability Integrated into Main Server (No Separate Port)¶
- Health checks (
/health,/health/ready,/health/live) and Prometheus metrics (/metrics) are now registered asServerHandlerinstances on the main HTTP server viaUse() - Eliminated the separate observability server (
observability.Server) that previously ran on a dedicated port (:8081) --observability/--observability-addrCLI flags removed from the run command- Templates
observability/server.go.tmpldeleted
Isolated Route Handler Packages¶
- Each route group ships as its own sub-package inside
internal/adapters/{inbound}/http/: ping/— health ping at/pinghealth/— Kubernetes probes at/health,/health/ready,/health/live(with--with-observability)metrics/— Prometheus scrape endpoint at/metrics(with--with-observability)- New adapter wiring file
internal/adapters/{inbound}/http/http.gocreates the server and registers all handlers in one place, keepingcmd/run.gocompletely framework-agnostic - All five frameworks (
chi,echo,gin,fiber,stdlib) have a full set of handler templates
Idiomatic Route Groups with Middleware Examples in HTTP Adapter Templates¶
- All five HTTP adapter templates now include a commented
/api/v1route group with route-scoped middleware examples (request-id, logging, panic recovery, authorization): - chi —
router.Route("/api/v1", func(r chi.Router) { r.Use(...) })(idiomatic sub-router) - echo —
v1 := srv.Echo.Group("/api/v1"); v1.Use(...) - fiber —
v1 := srv.App.Group("/api/v1"); v1.Use(...) - gin —
v1 := srv.Router.Group("/api/v1"); v1.Use(...) - stdlib — nested
http.NewServeMux()mounted withhttp.StripPrefix("/api/v1", ...); per-group middlewares applied by wrapping the sub-mux before mounting
Changed¶
Template Directory Restructured to Mirror Generated Project¶
- Template paths now mirror the generated project structure for intuitive discovery:
| Template path | Generates |
|---|---|
templates/pkg/server/server_interface.go.tmpl |
pkg/server/server.go |
templates/pkg/httpserver/http_server_{fw}.go.tmpl |
pkg/httpserver/server.go |
templates/adapter/primary/http/{fw}/http_adapter.go.tmpl |
internal/adapters/{inbound}/http/http.go |
templates/adapter/primary/http/{fw}/http_ping.go.tmpl |
internal/adapters/{inbound}/http/ping/ping.go |
templates/adapter/primary/http/{fw}/http_health.go.tmpl |
internal/adapters/{inbound}/http/health/health.go |
templates/adapter/primary/http/{fw}/http_metrics.go.tmpl |
internal/adapters/{inbound}/http/metrics/metrics.go |
//go:embeddirective changed fromtemplates/**/*.tmplto//go:embed templatesto support deeply nested subdirectories (Go's**glob does not recurse beyond one level)
template_loader.go Cross-Platform Fix¶
- Embedded FS path lookups changed from
filepath.Jointopath.Join—embed.FSalways uses forward slashes;filepath.Joinproduces backslashes on Windows and would fail to find templates
Fixed¶
Template Code Style (interface{} → any)¶
- All generated code templates updated to use the
anytype alias (Go 1.18+) in place ofinterface{}— affects adapter, tool, worker, observability, and project templates - Matching documentation examples updated to
anyas well
v0.0.3 - 2026-03-04¶
Added¶
--working-directory global flag¶
-w/--working-directorypersistent flag on the root command — every subcommand can now target a project in a different directory withoutcd-ing into it firsthexago init --working-directory <dir>uses the supplied path asOutputDir, so the project is scaffolded relative to<dir>instead of the current working directory- All
add *andvalidatecommands pass the flag value toGetCurrentProjectConfig, which falls back toos.Getwd()when the flag is not supplied
--in-place flag for hexago init¶
- New
--in-placebool flag: generates project files directly intoworking_directoryinstead of creating a<name>subdirectory inside it - Useful when the target directory already exists and is the intended project root (e.g. a freshly cloned empty repo or the current working directory)
InPlace boolfield added toProjectConfigininternal/generator/types.goProjectGenerator.Generate()checksconfig.InPlace: when true it usesOutputDiras the project path directly and skips the "directory already exists" guard
Built-in MCP Server (hexago mcp)¶
cmd/mcp.go(new):hexago mcpstarts a stdio Model Context Protocol server usinggithub.com/mark3labs/mcp-go v0.44.0- Nine tools registered — each tool calls back into the running hexago binary with
--working-directory, so all generation logic is shared with the regular CLI:
| Tool | Equivalent CLI call |
|---|---|
hexago_init |
hexago [--wd W] init <name> [flags] |
hexago_add_service |
hexago [--wd W] add service <name> |
hexago_add_domain_entity |
hexago [--wd W] add domain entity <name> |
hexago_add_domain_valueobject |
hexago [--wd W] add domain valueobject <name> |
hexago_add_adapter |
hexago [--wd W] add adapter <direction> <type> <name> |
hexago_add_worker |
hexago [--wd W] add worker <name> |
hexago_add_migration |
hexago [--wd W] add migration <name> |
hexago_add_tool |
hexago [--wd W] add tool <type> <name> |
hexago_validate |
hexago [--wd W] validate |
- MCP server instructions (
server.WithInstructions) delivered on everyinitializehandshake — covers all tool parameters, valid enum values, defaults, field format, and a "do not run shell commands" directive that prevents AI agents from falling back to raw CLI calls - All MCP tool descriptions enriched with: generated file paths, architectural layer context, valid enum values for every string parameter, defaults for every optional parameter, and concrete call examples
github.com/mark3labs/mcp-go v0.44.0added as a direct dependency- MCP server version sourced from
version.CurrentVersion()instead of a hardcoded string
MCP client registration documentation¶
- New
## MCP Serversection inREADME.mdwith config snippets for six clients: Claude Code, Claude Desktop, VS Code, Cursor, Windsurf, Zed - Quick-reference table comparing config file paths, top-level JSON keys, and whether
"type": "stdio"is required per client
Changed¶
GetCurrentProjectConfig()signature changed toGetCurrentProjectConfig(dir string); empty string falls back toos.Getwd(). All call sites updated.cmd/init.goresolvesOutputDirfrom the--working-directoryflag value (withos.Getwd()fallback) and explicitly setsconfig.OutputDirbefore calling the generatorinternal/generator/project.goandinternal/generator/detector.gomigrated frompkg/fileutiltopkg/utilsfor file-system helpers (internal refactor, no behaviour change)
v0.0.2 - 2026-02-26¶
Added¶
Template Management Commands (hexago templates)¶
hexago templates list: lists all embedded templates grouped by directory; annotates overrides with← project-localor← user-globalhexago templates which <name>: shows the winning source (embedded, project-local, user-global, or binary-local) with its full pathhexago templates export <name> [--global]: copies a built-in template to.hexago/templates/<name>or~/.hexago/templates/<name>for customizationhexago templates export-all [--global] [--force]: bulk-exports every embedded template at once; skips templates that already have an override unless--forceis passedhexago templates validate <path>: parses a template file and reportstext/templatesyntax errors — prints✓on success,✗ <error>on failurehexago templates reset <name> [--global]: removes a custom override, reverting to the next-priority source; errors clearly when no override existsTemplateLoader.Validate(path string) errorandTemplateLoader.Reset(name string, global bool) erroradded tointernal/generator/template_loader.go
.hexago.yaml Project Configuration File¶
internal/generator/hexago_config.go(new): typed YAML structs (HexagoConfig,HexagoProjectConfig,HexagoStructureConfig,HexagoFeaturesConfig) plus four helpers:HexagoConfigFromProject(cfg)— mapsProjectConfig→ YAML struct(h) ToProjectConfig()— maps YAML struct →ProjectConfigLoadHexagoConfig(dir)— reads{dir}/.hexago.yamlwithgopkg.in/yaml.v3SaveHexagoConfig(dir, cfg)— writes{dir}/.hexago.yamlwith a comment headerhexago initwrites.hexago.yamlinto the generated project root after scaffolding, persisting all init-time settings (framework, adapter style, features, etc.) that could not be recovered from the filesystem alonehexago add *reads.hexago.yamlfirst:DetectConfig()indetector.gonow triesLoadHexagoConfigbefore falling back to filesystem heuristics — giving everyaddcommand access to the full original config (includingFramework,ProjectType,Author,GoVersion, feature flags)hexago inithonours.hexago.yamlas a defaults layer: priority isflags > .hexago.yaml > hardcoded defaults. Any flag not explicitly passed on the command line is filled from a.hexago.yamlfound in the current working directory, enabling a personal or team-wide preferences file without forcing every flag on every invocation. Uses Cobra'scmd.Flags().Changed()to distinguish user-supplied flags from default valuesgopkg.in/yaml.v3added as a direct dependency
HTTP Server Interface Pattern¶
- Shared
Serverinterface inpkg/server/server.go: a singleRun(errChan chan<- error)/Stop(ctx context.Context) errorcontract lives in a public, framework-agnostic package instead of being re-declared in every adapter http_server_interface.go.tmpl: new template that generatespkg/server/server.gofor everyhttp-serverproject- Compile-time interface guard in every framework adapter:
var _ srv.Server = (*server)(nil)catches implementation drift at build time, not at runtime
HTTP Server Adapter Refactoring¶
- Framework-specific
server.gofiles extracted fromcmd/run.gointointernal/adapters/{primary|driver}/http/server.gofor all five supported frameworks (Echo, Gin, Chi, Fiber, stdlib): - Framework instance creation, middleware wiring, and
http.Serverconfiguration are now encapsulated inside each adapter setupRoutespromoted from a package-level function to a method on*server, giving it direct access to the framework instance without parameter passing- Each adapter's
New()constructor returnssrv.Server(the shared interface), hiding all framework types behind the abstraction boundary - Thin
cmd/run.goorchestrator: the run command is now completely framework-agnostic — it only callshttpserver.New(),srv.Run(), andsrv.Stop(). No framework imports, no repeated signal/shutdown boilerplate per framework
Changed¶
cmd/run.go(generated) no longer containssetupRoutesor any web-framework importsinternal/adapters/{inbound}/http/server.go(generated) now owns all framework-specific lifecycle codepkg/server/server.go(generated, new) is the single source of truth for theServerinterface contract
Refactored (internal — no generated-code change)¶
Remove global template loader singleton¶
globalTemplateLoaderpackage-level variable andinit()removed frominternal/generator/templates.goTemplateLoaderis now a field (templateLoader *TemplateLoader) onProjectConfig, initialized inNewProjectConfig()- All generator methods that previously called
globalTemplateLoader.Render(...)now callg.config.templateLoader.Render(...)— scoping the loader to its owning config and making generators straightforward to test in isolation
New pkg/utils package¶
pkg/utils/case.goadded with two exported helpers:ToSnakeCase(s string) string— converts CamelCase identifiers to snake_case file namesToTitleCase(s string) string— uppercases the first letter of a string- Eliminates at least three identical local
toSnakeCasecopies that existed independently inservice.go,tool.go,worker.go,domain.go,adapter.go, andcmd/add_tool.go createTemplateFuncMap()intemplate_loader.gonow referencesutils.ToSnakeCaseandutils.ToTitleCasefor the"snake"and"title"template functions
Observability templates moved to dedicated directory¶
internal/generator/templates/misc/health.go.tmpl→observability/health.go.tmplinternal/generator/templates/misc/metrics.go.tmpl→observability/metrics.go.tmplinternal/generator/templates/misc/server.go.tmpl→observability/server.go.tmplgenerateObservability()intemplates_misc.goupdated to reference the newobservability/prefixmisc/now contains only pure project-support files (Makefile, README, Dockerfile, compose.yaml, .gitignore); observability templates have their own top-level group matching the generatedinternal/observability/package
Extended pkg/fileutil¶
HomeDir() stringandBinaryDir() stringmigrated frominternal/generator/template_loader.gointopkg/fileutil/fileutil.gotemplate_loader.gonow usesfileutil.HomeDir(),fileutil.BinaryDir(), andfileutil.FileExists— removing three private helper functions from the generator package
0.0.1 - 2026-02-17¶
Added - MVP Release¶
Core Features¶
- Project Type Support: Generate projects with different architectural patterns
http-server: HTTP API server with framework support (Echo, Gin, Chi, Fiber, stdlib)service: Long-running daemon/service with no web framework for main logic- Hexagonal Architecture: Strict separation of concerns with core/adapters structure
- Framework Support: Echo, Gin, Chi, Fiber, and Go stdlib for HTTP servers
- Graceful Shutdown: Context-based cancellation with signal handling for all project types
- Configuration Management: Viper-based config with YAML files and environment variable support
- Structured Logging: Logger package with configurable levels and formats
Observability (Available for All Project Types)¶
- Health Checks:
/health- Complete health report with component status/health/ready- Kubernetes readiness probe/health/live- Kubernetes liveness probe- Prometheus Metrics: Request counters, latency histograms, active operations gauge
- Separate Observability Server: Runs on independent port (default: 8080)
- Component Registration: Register custom health checks for databases, queues, etc.
Service Pattern (Long-Running Daemon)¶
- Processor Pattern: Main business logic in
Processor.Start(ctx)method - Context-Based Shutdown: Clean cancellation and resource cleanup
- Background Processing: Example implementations for queues, schedulers, file watchers
- Signal Handling: SIGINT, SIGTERM, SIGQUIT support
- Configurable Timeouts: Grace period for shutdown operations
Template System¶
- Externalized Templates: All code templates can be customized
- Multi-Source Loading:
- Binary-local:
templates/(next to executable) - Project-local:
.hexago/templates/(per-project customization) - User-global:
~/.hexago/templates/(user-wide customization) - Embedded: Fallback templates compiled into binary
- Company Branding: Easy to customize headers, comments, and code style
- Version Control: Share custom templates across teams
Code Generation¶
- Component Generators:
- Services/UseCases: Business logic layer
- Domain Entities: Core domain objects with fields
- Value Objects: Immutable domain values
- HTTP Adapters: Framework-specific handlers
- Database Adapters: Repository implementations
- External Service Adapters: API client wrappers
- Cache Adapters: Redis/memory cache implementations
- Queue Adapters: Message queue consumers
- Background Workers: Queue-based, periodic, and event-driven patterns
- Database Migrations: Sequential numbered migrations with golang-migrate support
- Infrastructure Tools: Loggers, validators, mappers, middleware
Project Flexibility¶
- Optional Features: All features opt-in via flags (default: false)
--with-docker: Docker files (Dockerfile, compose.yaml)--with-observability: Health checks and metrics--with-migrations: Database migration setup--with-workers: Background worker pattern--with-metrics: Prometheus metrics (deprecated, use --with-observability)--with-example: Example code--explicit-ports: Explicit ports/ directory structure- Naming Conventions:
- Adapter style:
primary-secondaryordriver-driven - Core logic:
servicesorusecases - Architecture Validation: Auto-detection of existing project conventions
Developer Experience¶
- Cobra CLI: Command structure with subcommands
- Auto-Detection: Respects existing project structure and conventions
- Smart Defaults: Sensible defaults with override options
- Helpful Messages: Clear error messages and configuration summaries
- Educational Comments: Generated code includes architecture guidance
Build & Release¶
- GoReleaser Integration: Automated multi-platform builds
- GitHub Actions: CI/CD workflow for releases
- Platform Support:
- Linux: x86_64, arm64
- macOS: x86_64 (Intel), arm64 (Apple Silicon)
- Static Binaries: CGO_ENABLED=0 for portability
- Homebrew Support: Ready for homebrew-tap publication
Documentation¶
- Comprehensive README with examples
- Quick start guide
- Architecture documentation
- Template customization guide
- Project type comparison
Project Types Use Cases¶
HTTP Server (http-server)¶
Perfect for: - REST APIs - GraphQL servers - Microservices with HTTP interfaces - Web applications with API backends
Service (service)¶
Perfect for: - MQTT/Kafka message consumers - File system watchers - Background job processors - Event stream processors - Periodic task schedulers - Data pipeline processors
Breaking Changes¶
None (initial release)
Security¶
- No external dependencies in core (stdlib only)
- Static binary compilation
- No code execution from templates (text/template, not html/template)
How to Update¶
Or download binaries from GitHub Releases