Skip to content

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.mod at repository root
  • Standard Go layout uses cmd/ and internal/ 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 -C flags
  • 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

  1. Package Rename: internal/cmdinternal/commands
  2. Avoids confusion with root cmd/ directory
  3. Go convention uses cmd/ for entry points only

  4. Import Paths: Simplified from nested structure

  5. Before: github.com/ar4mirez/samuel/packages/cli/internal/cmd
  6. After: github.com/ar4mirez/samuel/internal/commands

  7. Build Configuration: Makefile LDFLAGS updated to new paths

  8. History Preservation: Used git mv to 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.md file 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