feat: initial Claude Code configuration scaffold

Comprehensive Claude Code guidance system with:

- 5 agents: tdd-guardian, code-reviewer, security-scanner, refactor-scan, dependency-audit
- 18 skills covering languages (Python, TypeScript, Rust, Go, Java, C#),
  infrastructure (AWS, Azure, GCP, Terraform, Ansible, Docker/K8s, Database, CI/CD),
  testing (TDD, UI, Browser), and patterns (Monorepo, API Design, Observability)
- 3 hooks: secret detection, auto-formatting, TDD git pre-commit
- Strict TDD enforcement with 80%+ coverage requirements
- Multi-model strategy: Opus for planning, Sonnet for execution (opusplan)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-20 15:47:34 -05:00
commit befb8fbaeb
34 changed files with 12233 additions and 0 deletions

View File

@@ -0,0 +1,686 @@
---
name: go-development
description: Go development patterns with testing, error handling, and idiomatic practices. Use when writing Go code.
---
# Go Development Skill
## Project Structure
```
project/
├── cmd/
│ └── server/
│ └── main.go # Application entry point
├── internal/
│ ├── domain/ # Business logic
│ │ ├── user.go
│ │ └── user_test.go
│ ├── repository/ # Data access
│ │ ├── user_repo.go
│ │ └── user_repo_test.go
│ ├── service/ # Application services
│ │ ├── user_service.go
│ │ └── user_service_test.go
│ └── handler/ # HTTP handlers
│ ├── user_handler.go
│ └── user_handler_test.go
├── pkg/ # Public packages
│ └── validation/
├── go.mod
├── go.sum
└── Makefile
```
## Testing Patterns
### Table-Driven Tests
```go
package user
import (
"testing"
)
func TestValidateEmail(t *testing.T) {
tests := []struct {
name string
email string
wantErr bool
}{
{
name: "valid email",
email: "user@example.com",
wantErr: false,
},
{
name: "missing @ symbol",
email: "userexample.com",
wantErr: true,
},
{
name: "empty email",
email: "",
wantErr: true,
},
{
name: "missing domain",
email: "user@",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateEmail(tt.email)
if (err != nil) != tt.wantErr {
t.Errorf("ValidateEmail(%q) error = %v, wantErr %v",
tt.email, err, tt.wantErr)
}
})
}
}
```
### Test Fixtures with Helper Functions
```go
package service_test
import (
"testing"
"myapp/internal/domain"
)
// Test helper - creates valid user with optional overrides
func newTestUser(t *testing.T, opts ...func(*domain.User)) *domain.User {
t.Helper()
user := &domain.User{
ID: "user-123",
Email: "test@example.com",
Name: "Test User",
Role: domain.RoleUser,
}
for _, opt := range opts {
opt(user)
}
return user
}
// Option functions for customization
func withEmail(email string) func(*domain.User) {
return func(u *domain.User) {
u.Email = email
}
}
func withRole(role domain.Role) func(*domain.User) {
return func(u *domain.User) {
u.Role = role
}
}
func TestUserService_CreateUser(t *testing.T) {
t.Run("creates user with valid data", func(t *testing.T) {
user := newTestUser(t)
svc := NewUserService(mockRepo)
result, err := svc.CreateUser(user)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.ID == "" {
t.Error("expected user to have an ID")
}
})
t.Run("rejects user with invalid email", func(t *testing.T) {
user := newTestUser(t, withEmail("invalid-email"))
svc := NewUserService(mockRepo)
_, err := svc.CreateUser(user)
if err == nil {
t.Error("expected error for invalid email")
}
})
}
```
### Mocking with Interfaces
```go
package service
import (
"context"
"myapp/internal/domain"
)
// Repository interface for dependency injection
type UserRepository interface {
GetByID(ctx context.Context, id string) (*domain.User, error)
Create(ctx context.Context, user *domain.User) error
Update(ctx context.Context, user *domain.User) error
}
// Service with injected dependencies
type UserService struct {
repo UserRepository
}
func NewUserService(repo UserRepository) *UserService {
return &UserService{repo: repo}
}
// Test file
package service_test
import (
"context"
"errors"
"testing"
"myapp/internal/domain"
"myapp/internal/service"
)
// Mock implementation
type mockUserRepo struct {
users map[string]*domain.User
err error
}
func newMockUserRepo() *mockUserRepo {
return &mockUserRepo{
users: make(map[string]*domain.User),
}
}
func (m *mockUserRepo) GetByID(ctx context.Context, id string) (*domain.User, error) {
if m.err != nil {
return nil, m.err
}
user, ok := m.users[id]
if !ok {
return nil, errors.New("user not found")
}
return user, nil
}
func (m *mockUserRepo) Create(ctx context.Context, user *domain.User) error {
if m.err != nil {
return m.err
}
m.users[user.ID] = user
return nil
}
func (m *mockUserRepo) Update(ctx context.Context, user *domain.User) error {
if m.err != nil {
return m.err
}
m.users[user.ID] = user
return nil
}
```
## Error Handling
### Custom Error Types
```go
package domain
import (
"errors"
"fmt"
)
// Sentinel errors for comparison
var (
ErrNotFound = errors.New("not found")
ErrUnauthorized = errors.New("unauthorized")
ErrInvalidInput = errors.New("invalid input")
)
// Structured error with context
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)
}
// Error wrapping
func GetUser(ctx context.Context, id string) (*User, error) {
user, err := repo.GetByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("getting user %s: %w", id, err)
}
return user, nil
}
// Error checking
func handleError(err error) {
if errors.Is(err, ErrNotFound) {
// Handle not found
}
var validationErr *ValidationError
if errors.As(err, &validationErr) {
// Handle validation error
fmt.Printf("Field %s: %s\n", validationErr.Field, validationErr.Message)
}
}
```
### Result Pattern (Optional)
```go
package result
// Result type for explicit error handling
type Result[T any] struct {
value T
err error
}
func Ok[T any](value T) Result[T] {
return Result[T]{value: value}
}
func Err[T any](err error) Result[T] {
return Result[T]{err: err}
}
func (r Result[T]) IsOk() bool {
return r.err == nil
}
func (r Result[T]) IsErr() bool {
return r.err != nil
}
func (r Result[T]) Unwrap() (T, error) {
return r.value, r.err
}
func (r Result[T]) UnwrapOr(defaultValue T) T {
if r.err != nil {
return defaultValue
}
return r.value
}
// Usage
func ProcessPayment(amount float64) Result[*Receipt] {
if amount <= 0 {
return Err[*Receipt](ErrInvalidInput)
}
receipt := &Receipt{Amount: amount}
return Ok(receipt)
}
```
## HTTP Handlers
### Chi Router Pattern
```go
package handler
import (
"encoding/json"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"myapp/internal/service"
)
type UserHandler struct {
userService *service.UserService
}
func NewUserHandler(userService *service.UserService) *UserHandler {
return &UserHandler{userService: userService}
}
func (h *UserHandler) Routes() chi.Router {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Get("/", h.ListUsers)
r.Post("/", h.CreateUser)
r.Get("/{userID}", h.GetUser)
r.Put("/{userID}", h.UpdateUser)
r.Delete("/{userID}", h.DeleteUser)
return r
}
func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {
userID := chi.URLParam(r, "userID")
user, err := h.userService.GetUser(r.Context(), userID)
if err != nil {
if errors.Is(err, domain.ErrNotFound) {
http.Error(w, "User not found", http.StatusNotFound)
return
}
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
respondJSON(w, http.StatusOK, user)
}
func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := req.Validate(); err != nil {
respondJSON(w, http.StatusBadRequest, map[string]string{
"error": err.Error(),
})
return
}
user, err := h.userService.CreateUser(r.Context(), req.ToUser())
if err != nil {
http.Error(w, "Failed to create user", http.StatusInternalServerError)
return
}
respondJSON(w, http.StatusCreated, user)
}
func respondJSON(w http.ResponseWriter, status int, data any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(data)
}
```
### Request Validation
```go
package handler
import (
"regexp"
)
var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
type CreateUserRequest struct {
Email string `json:"email"`
Name string `json:"name"`
}
func (r *CreateUserRequest) Validate() error {
if r.Email == "" {
return &domain.ValidationError{
Field: "email",
Message: "email is required",
}
}
if !emailRegex.MatchString(r.Email) {
return &domain.ValidationError{
Field: "email",
Message: "invalid email format",
}
}
if r.Name == "" {
return &domain.ValidationError{
Field: "name",
Message: "name is required",
}
}
if len(r.Name) > 100 {
return &domain.ValidationError{
Field: "name",
Message: "name must be 100 characters or less",
}
}
return nil
}
func (r *CreateUserRequest) ToUser() *domain.User {
return &domain.User{
Email: r.Email,
Name: r.Name,
}
}
```
## Context Usage
```go
package service
import (
"context"
"time"
)
// Context with timeout
func (s *UserService) ProcessOrder(ctx context.Context, orderID string) error {
// Create a timeout context
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
// Check context before expensive operations
select {
case <-ctx.Done():
return ctx.Err()
default:
}
// Process order...
return s.repo.UpdateOrder(ctx, orderID)
}
// Context values for request-scoped data
type contextKey string
const (
userIDKey contextKey = "userID"
requestIDKey contextKey = "requestID"
)
func WithUserID(ctx context.Context, userID string) context.Context {
return context.WithValue(ctx, userIDKey, userID)
}
func UserIDFromContext(ctx context.Context) (string, bool) {
userID, ok := ctx.Value(userIDKey).(string)
return userID, ok
}
```
## Concurrency Patterns
### Worker Pool
```go
package worker
import (
"context"
"sync"
)
type Job func(ctx context.Context) error
type Pool struct {
workers int
jobs chan Job
results chan error
wg sync.WaitGroup
}
func NewPool(workers int) *Pool {
return &Pool{
workers: workers,
jobs: make(chan Job, workers*2),
results: make(chan error, workers*2),
}
}
func (p *Pool) Start(ctx context.Context) {
for i := 0; i < p.workers; i++ {
p.wg.Add(1)
go p.worker(ctx)
}
}
func (p *Pool) worker(ctx context.Context) {
defer p.wg.Done()
for {
select {
case <-ctx.Done():
return
case job, ok := <-p.jobs:
if !ok {
return
}
if err := job(ctx); err != nil {
p.results <- err
}
}
}
}
func (p *Pool) Submit(job Job) {
p.jobs <- job
}
func (p *Pool) Close() {
close(p.jobs)
p.wg.Wait()
close(p.results)
}
```
### Error Group
```go
package service
import (
"context"
"golang.org/x/sync/errgroup"
)
func (s *OrderService) ProcessBatch(ctx context.Context, orderIDs []string) error {
g, ctx := errgroup.WithContext(ctx)
// Limit concurrency
g.SetLimit(10)
for _, orderID := range orderIDs {
orderID := orderID // Capture for goroutine
g.Go(func() error {
return s.ProcessOrder(ctx, orderID)
})
}
return g.Wait()
}
```
## Commands
```bash
# Testing
go test ./... # Run all tests
go test -v ./... # Verbose output
go test -race ./... # Race detection
go test -cover ./... # Coverage summary
go test -coverprofile=coverage.out ./... # Coverage file
go tool cover -html=coverage.out # HTML coverage report
# Linting
go vet ./... # Built-in checks
golangci-lint run # Comprehensive linting
# Building
go build -o bin/server ./cmd/server # Build binary
go build -ldflags="-s -w" -o bin/server ./cmd/server # Smaller binary
# Dependencies
go mod tidy # Clean up dependencies
go mod verify # Verify dependencies
# Code generation
go generate ./... # Run generate directives
```
## Anti-Patterns to Avoid
```go
// ❌ BAD - Returning nil error with nil value
func GetUser(id string) (*User, error) {
user := db.Find(id)
return user, nil // user might be nil!
}
// ✅ GOOD - Explicit error for not found
func GetUser(id string) (*User, error) {
user := db.Find(id)
if user == nil {
return nil, ErrNotFound
}
return user, nil
}
// ❌ BAD - Ignoring errors
result, _ := SomeFunction()
// ✅ GOOD - Handle or propagate errors
result, err := SomeFunction()
if err != nil {
return fmt.Errorf("calling SomeFunction: %w", err)
}
// ❌ BAD - Naked goroutine without error handling
go processItem(item)
// ✅ GOOD - Error handling with channels or error groups
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error {
return processItem(ctx, item)
})
if err := g.Wait(); err != nil {
return err
}
// ❌ BAD - Mutex embedded in struct
type Service struct {
sync.Mutex
data map[string]string
}
// ✅ GOOD - Private mutex
type Service struct {
mu sync.Mutex
data map[string]string
}
```