RFD 0002: Idiomatic Go Project Layout for CLI Tools¶
Summary¶
This RFD documents the decision to restructure the Samuel CLI from a non-idiomatic packages/cli/ location (inherited from Node.js monorepo patterns) to a standard Go project layout with go.mod, cmd/, and internal/ at the repository root.
Problem Statement¶
The Samuel CLI was originally placed in packages/cli/ following a Node.js monorepo pattern. This was not idiomatic Go and caused several issues:
- Go developers expect
go.modat repository root - Standard Go layout uses
cmd/andinternal/at root - The
packages/pattern is from JavaScript/Node.js ecosystem - Import paths were unnecessarily long:
github.com/ar4mirez/samuel/packages/cli/internal/cmd - Standard Go tooling (
go build ./...,go test ./...) didn't work from root - GoReleaser configuration was more complex than necessary
Key Insight: A framework promoting best practices for AI-assisted development should itself follow language-idiomatic patterns. Using Node.js conventions for a Go project undermined credibility.
Background¶
Standard Go Project Layout¶
The Go community has established conventions for project structure:
project/
├── cmd/
│ └── appname/
│ └── main.go # Entry point
├── internal/ # Private packages
│ ├── feature1/
│ └── feature2/
├── pkg/ # Public packages (optional)
├── go.mod # At repository root
├── go.sum
└── Makefile
Original Samuel Structure¶
samuel/
├── packages/
│ └── cli/
│ ├── cmd/
│ │ └── samuel/
│ │ └── main.go
│ ├── internal/
│ │ └── cmd/ # Confusing name
│ ├── go.mod # Buried in subdirectory
│ └── go.sum
├── docs/
├── CLAUDE.md
└── (other non-Go files)
Options Considered¶
Option A: Keep packages/cli/ Structure¶
Maintain the existing structure to avoid migration effort.
Pros:
- No migration effort
- Existing code works
- No risk of breaking changes
Cons:
- Confusing for Go developers
- Non-idiomatic layout
- Longer import paths
- Standard tooling requires
-Cflags - Undermines framework credibility
Effort: None
Option B: Move Go Code to Repository Root (Chosen)¶
Restructure to standard Go project layout.
Pros:
- Idiomatic Go layout
- Cleaner import paths
- Standard tooling works from root
- Easier contribution from Go developers
- Framework dogfooding (using its own patterns)
Cons:
- Requires file moves
- Import path updates needed
- Potential git history impact
- Documentation updates required
Effort: Medium (one-time)
Proposal¶
Chosen: Option B - Standard Go Layout at Repository Root
New Structure¶
samuel/
├── cmd/
│ └── samuel/
│ └── main.go # Entry point
├── internal/
│ ├── commands/ # Renamed from internal/cmd
│ ├── core/
│ ├── github/
│ └── ui/
├── go.mod # At root
├── go.sum
├── Makefile
├── .goreleaser.yaml
├── docs/
├── CLAUDE.md
└── .claude/
Key Implementation Details¶
- Package Rename:
internal/cmd→internal/commands - Avoids confusion with root
cmd/directory -
Go convention uses
cmd/for entry points only -
Import Paths: Simplified from nested structure
- Before:
github.com/ar4mirez/samuel/packages/cli/internal/cmd -
After:
github.com/ar4mirez/samuel/internal/commands -
Build Configuration: Makefile LDFLAGS updated to new paths
-
History Preservation: Used
git mvto preserve file history
Migration Commands¶
# Key commands used
git mv packages/cli/go.mod go.mod
git mv packages/cli/go.sum go.sum
git mv packages/cli/cmd cmd
git mv packages/cli/internal internal
git mv packages/cli/Makefile Makefile
git mv packages/cli/.goreleaser.yaml .goreleaser.yaml
# Update all import paths in Go files
# Update package declarations
Implementation Considerations¶
Affected Files¶
- All Go source files (import path updates)
go.mod(module path stays same, location changes)Makefile(LDFLAGS path updates).goreleaser.yaml(build paths).claude/patterns.md(file path examples)- Documentation referencing old paths
Testing¶
After restructure, verify:
go build ./... # Should work from root
go test ./... # Should work from root
go vet ./... # Should work from root
make build # Should produce working binary
Security Considerations¶
No security implications. This is purely a structural change.
Compatibility¶
- Breaking changes: External references to old import paths will break
- Migration path: No migration needed for end users (CLI binary unchanged)
- Backwards compatibility: N/A (internal restructure)
Consequences¶
Positive¶
- Standard Go project structure that Go developers recognize
- Cleaner import paths
- Build tools work without specifying subdirectory
- Easier contribution from Go developers
- Framework credibility improved (dogfooding)
Negative¶
- Old
packages/cli/paths in documentation needed updating - Any external references to old paths are broken
patterns.mdfile paths needed updating
Technical Debt Created¶
- Some documentation may still reference old paths
- CI/CD workflows may need path updates if added later
Future Work Enabled¶
- Easier to add more Go packages at root level
- Standard tooling works naturally
- GoReleaser configuration is simplified
References¶
- PRD:
.claude/tasks/0002-prd-go-project-restructure.md - Task list:
.claude/tasks/tasks-0002-prd-go-project-restructure.md - Decision documented:
.claude/memory/2026-01-14-go-project-restructure.md - Go Project Layout: golang-standards/project-layout