@baselane/packs 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aliases.js +1 -0
- package/package.json +2 -2
- package/packs/database-review/pack.json +75 -9
- package/packs/database-review/rendered/.claude/agents/database-reviewer.md +75 -2
- package/packs/database-review/rendered/.claude/commands/migration-review.md +6 -0
- package/packs/database-review/rendered/.claude/settings.json +11 -0
- package/packs/database-review/rendered/.claude/skills/database-migrations/SKILL.md +431 -0
- package/packs/database-review/rendered/.claude/skills/mysql-patterns/SKILL.md +414 -0
- package/packs/database-review/rendered/.claude/skills/postgres-patterns/SKILL.md +149 -0
- package/packs/database-review/rendered/.github/copilot-instructions.md +1096 -14
- package/packs/database-review/rendered/AGENTS.md +45 -16
- package/packs/database-review/rendered/CLAUDE.md +4 -0
- package/packs/database-review/rendered/GEMINI.md +987 -0
- package/packs/go-rules/pack.json +65 -9
- package/packs/go-rules/rendered/.claude/agents/go-build-resolver.md +90 -0
- package/packs/go-rules/rendered/.claude/agents/go-reviewer.md +72 -2
- package/packs/go-rules/rendered/.claude/commands/go-build.md +6 -0
- package/packs/go-rules/rendered/.claude/commands/go-review.md +1 -1
- package/packs/go-rules/rendered/.claude/commands/go-test.md +6 -0
- package/packs/go-rules/rendered/.claude/settings.json +9 -0
- package/packs/go-rules/rendered/.claude/skills/golang-patterns/SKILL.md +673 -0
- package/packs/go-rules/rendered/.claude/skills/golang-testing/SKILL.md +719 -0
- package/packs/go-rules/rendered/.github/copilot-instructions.md +1783 -7
- package/packs/go-rules/rendered/AGENTS.md +244 -8
- package/packs/go-rules/rendered/CLAUDE.md +5 -0
- package/packs/go-rules/rendered/GEMINI.md +1394 -0
- package/packs/python-rules/pack.json +87 -10
- package/packs/python-rules/rendered/.claude/agents/django-reviewer.md +159 -0
- package/packs/python-rules/rendered/.claude/agents/fastapi-reviewer.md +70 -0
- package/packs/python-rules/rendered/.claude/agents/python-reviewer.md +92 -2
- package/packs/python-rules/rendered/.claude/commands/django-review.md +6 -0
- package/packs/python-rules/rendered/.claude/commands/fastapi-review.md +6 -0
- package/packs/python-rules/rendered/.claude/commands/python-review.md +1 -1
- package/packs/python-rules/rendered/.claude/settings.json +1 -1
- package/packs/python-rules/rendered/.claude/skills/django-security/SKILL.md +642 -0
- package/packs/python-rules/rendered/.claude/skills/django-tdd/SKILL.md +728 -0
- package/packs/python-rules/rendered/.claude/skills/fastapi-patterns/SKILL.md +512 -0
- package/packs/python-rules/rendered/.claude/skills/python-patterns/SKILL.md +749 -0
- package/packs/python-rules/rendered/.claude/skills/python-testing/SKILL.md +815 -0
- package/packs/python-rules/rendered/.github/copilot-instructions.md +3815 -12
- package/packs/python-rules/rendered/AGENTS.md +73 -13
- package/packs/python-rules/rendered/CLAUDE.md +9 -0
- package/packs/python-rules/rendered/GEMINI.md +3448 -0
- package/packs/security-review/pack.json +51 -7
- package/packs/security-review/rendered/.claude/agents/security-reviewer.md +29 -2
- package/packs/security-review/rendered/.claude/settings.json +11 -0
- package/packs/security-review/rendered/.claude/skills/security-review/SKILL.md +302 -0
- package/packs/security-review/rendered/.claude/skills/security-review/cloud-infrastructure-security.md +171 -0
- package/packs/security-review/rendered/.claude/skills/security-scan-config/SKILL.md +56 -0
- package/packs/security-review/rendered/.github/copilot-instructions.md +658 -15
- package/packs/security-review/rendered/AGENTS.md +283 -17
- package/packs/security-review/rendered/CLAUDE.md +2 -0
- package/packs/security-review/rendered/GEMINI.md +352 -0
- package/packs/software-engineer-harness/pack.json +249 -13
- package/packs/software-engineer-harness/rendered/.claude/agents/code-explorer.md +67 -0
- package/packs/software-engineer-harness/rendered/.claude/agents/performance-optimizer.md +444 -0
- package/packs/software-engineer-harness/rendered/.claude/agents/pr-test-analyzer.md +43 -0
- package/packs/software-engineer-harness/rendered/.claude/commands/feature-dev.md +6 -0
- package/packs/software-engineer-harness/rendered/.claude/settings.json +29 -0
- package/packs/software-engineer-harness/rendered/.claude/skills/api-design/SKILL.md +522 -0
- package/packs/software-engineer-harness/rendered/.claude/skills/architecture-decision-records/SKILL.md +178 -0
- package/packs/software-engineer-harness/rendered/.claude/skills/backend-patterns/SKILL.md +560 -0
- package/packs/software-engineer-harness/rendered/.claude/skills/codebase-onboarding/SKILL.md +232 -0
- package/packs/software-engineer-harness/rendered/.claude/skills/deployment-patterns/SKILL.md +426 -0
- package/packs/software-engineer-harness/rendered/.claude/skills/docker-patterns/SKILL.md +363 -0
- package/packs/software-engineer-harness/rendered/.claude/skills/e2e-testing/SKILL.md +325 -0
- package/packs/software-engineer-harness/rendered/.claude/skills/tdd-workflow/SKILL.md +580 -0
- package/packs/software-engineer-harness/rendered/.github/copilot-instructions.md +3948 -2
- package/packs/software-engineer-harness/rendered/AGENTS.md +194 -4
- package/packs/software-engineer-harness/rendered/CLAUDE.md +20 -0
- package/packs/software-engineer-harness/rendered/GEMINI.md +3208 -0
- package/packs/typescript-rules/pack.json +68 -5
- package/packs/typescript-rules/rendered/.claude/agents/react-reviewer.md +165 -0
- package/packs/typescript-rules/rendered/.claude/agents/type-design-analyzer.md +48 -0
- package/packs/typescript-rules/rendered/.claude/commands/react-review.md +6 -0
- package/packs/typescript-rules/rendered/.claude/settings.json +9 -0
- package/packs/typescript-rules/rendered/.claude/skills/react-patterns/SKILL.md +340 -0
- package/packs/typescript-rules/rendered/.claude/skills/react-performance/SKILL.md +573 -0
- package/packs/typescript-rules/rendered/.claude/skills/react-testing/SKILL.md +422 -0
- package/packs/typescript-rules/rendered/.github/copilot-instructions.md +2459 -53
- package/packs/typescript-rules/rendered/AGENTS.md +893 -17
- package/packs/typescript-rules/rendered/CLAUDE.md +6 -0
- package/packs/typescript-rules/rendered/GEMINI.md +1335 -0
- package/packs/ecc-essentials/pack.json +0 -93
- package/packs/ecc-essentials/rendered/.claude/settings.json +0 -37
- package/packs/ecc-essentials/rendered/.github/copilot-instructions.md +0 -110
- package/packs/ecc-essentials/rendered/AGENTS.md +0 -74
- package/packs/ecc-essentials/rendered/CLAUDE.md +0 -14
- package/packs/ecc-essentials/rendered/GEMINI.md +0 -25
- /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/agents/build-error-resolver.md +0 -0
- /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/agents/refactor-cleaner.md +0 -0
- /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/agents/silent-failure-hunter.md +0 -0
- /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/agents/tdd-guide.md +0 -0
- /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/commands/fix-build.md +0 -0
- /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/commands/hunt-silent-failures.md +0 -0
- /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/commands/tdd.md +0 -0
- /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/skills/tdd-red-green-refactor/SKILL.md +0 -0
package/packs/go-rules/pack.json
CHANGED
|
@@ -1,21 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://baselane.dev/schema/workflow-pack-v1.json",
|
|
3
3
|
"id": "go-rules",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.0",
|
|
5
5
|
"title": "Go rules",
|
|
6
|
-
"summary": "Go conventions
|
|
6
|
+
"summary": "Comprehensive Go conventions, reviewer, build-error resolver, and skills covering API shape, errors, concurrency, interfaces, package layout, security, and testing \u2014 mechanical checks (gofmt/vet/race) stay in CI.",
|
|
7
7
|
"scope": "repo",
|
|
8
|
-
"attribution": {
|
|
8
|
+
"attribution": {
|
|
9
|
+
"source": "affaan-m/ECC",
|
|
10
|
+
"url": "https://github.com/affaan-m/ECC",
|
|
11
|
+
"license": "MIT"
|
|
12
|
+
},
|
|
9
13
|
"context": {
|
|
10
|
-
"markdown": "## Go conventions (beyond what the linter enforces)\n\n`gofmt`, `goimports`, and `go vet` already catch formatting and the obvious mistakes — this pack is the judgment they can't encode. Keep the mechanical checks in CI (`gofmt -l .`, `go vet ./...`, `go test -race ./...`, `gosec ./...`); the reviewer covers the rest.\n\n### API shape\n\n- Accept interfaces, return structs. Define an interface where it is consumed (1-3 methods), never next to the implementation.\n- Use functional options (`type Option func(*Server)`) for constructors with many optional parameters, not a giant config struct or a long positional list.\n- Inject dependencies through constructors (`NewUserService(repo, logger)`), never package-level globals.\n\n### Errors\n\n- Wrap every propagated error with context and `%w` (`fmt.Errorf(\"create user: %w\", err)`). A bare returned `err` with no origin is a defect, not a shortcut.\n\n### Concurrency\n\n- Thread `context.Context` through call chains for cancellation and timeout (`ctx, cancel := context.WithTimeout(ctx, 5*time.Second)`; `defer cancel()`).\n- Every piece of shared state has an explicit owner — a mutex or a channel — or a comment proving why it is safe. \"Probably fine\" is a race.\n\n### Boundaries\n\n- Validate external input at the boundary before it reaches business logic; fail fast with a clear message.\n- Read required secrets from the environment and `log.Fatal` on an empty value at startup — never a silent zero value.\n\n### Flag in review (a linter won't)\n\n- A propagated error with no `%w` and no context string.\n- An interface defined beside its implementation instead of its consumer.\n- Shared state touched by multiple goroutines with no mutex/channel and no safety comment.\n- A required env var read without an empty-check before use.\n"
|
|
14
|
+
"markdown": "## Go conventions (beyond what the linter enforces)\n\n`gofmt`, `goimports`, and `go vet` already catch formatting and the obvious mistakes \u2014 this is the judgment they can't encode: API shape, error handling, concurrency safety, interface design, package organization, and the anti-patterns a linter waves through. Keep the mechanical checks in CI (`gofmt -l .`, `go vet ./...`, `go test -race ./...`, `gosec ./...`); the reviewer and skills below cover the rest.\n\n### Formatting & tooling\n\n- `gofmt` and `goimports` are mandatory \u2014 no style debates.\n- Recommended `.golangci.yml`:\n\n```yaml\nlinters:\n enable:\n - errcheck\n - gosimple\n - govet\n - ineffassign\n - staticcheck\n - unused\n - gofmt\n - goimports\n - misspell\n - unconvert\n - unparam\n\nlinters-settings:\n errcheck:\n check-type-assertions: true\n govet:\n enable:\n - shadow\n\nissues:\n exclude-use-default: false\n```\n\n### API shape\n\n- Accept interfaces, return structs. Define an interface where it is consumed (1-3 methods), never next to the implementation.\n- Use functional options (`type Option func(*Server)`) for constructors with many optional parameters, not a giant config struct or a long positional list.\n- Inject dependencies through constructors (`NewUserService(repo, logger)`), never package-level globals.\n- Make the zero value useful \u2014 design types so their zero value is immediately usable without initialization (e.g. `bytes.Buffer`, a mutex-guarded counter).\n- Context is always the first parameter, never a struct field: `func ProcessRequest(ctx context.Context, id string) error`.\n\n```go\n// Functional options\ntype Option func(*Server)\n\nfunc WithTimeout(d time.Duration) Option {\n return func(s *Server) { s.timeout = d }\n}\n\nfunc NewServer(addr string, opts ...Option) *Server {\n s := &Server{addr: addr, timeout: 30 * time.Second}\n for _, opt := range opts {\n opt(s)\n }\n return s\n}\n```\n\n```go\n// Consumer-defined interface, not provider-defined\npackage service\n\ntype UserStore interface {\n GetUser(id string) (*User, error)\n SaveUser(user *User) error\n}\n\ntype Service struct{ store UserStore }\n```\n\n### Errors\n\n- Wrap every propagated error with context and `%w` (`fmt.Errorf(\"create user: %w\", err)`). A bare returned `err` with no origin is a defect, not a shortcut.\n- Never discard an error with `_` unless it is truly best-effort and the reason is commented (`_ = writer.Close() // best-effort cleanup, error logged elsewhere`).\n- Use `errors.Is`/`errors.As` to check sentinel/typed errors, not `==` or type assertions.\n- Define sentinel errors (`var ErrNotFound = errors.New(...)`) or custom error types for domain-specific failures the caller needs to branch on.\n- Never `panic` for a recoverable error \u2014 return it.\n\n```go\nfunc LoadConfig(path string) (*Config, error) {\n data, err := os.ReadFile(path)\n if err != nil {\n return nil, fmt.Errorf(\"load config %s: %w\", path, err)\n }\n var cfg Config\n if err := json.Unmarshal(data, &cfg); err != nil {\n return nil, fmt.Errorf(\"parse config %s: %w\", path, err)\n }\n return &cfg, nil\n}\n\nfunc HandleError(err error) {\n if errors.Is(err, sql.ErrNoRows) {\n log.Println(\"no records found\")\n return\n }\n var validationErr *ValidationError\n if errors.As(err, &validationErr) {\n log.Printf(\"validation error on %s: %s\", validationErr.Field, validationErr.Message)\n return\n }\n log.Printf(\"unexpected error: %v\", err)\n}\n```\n\n### Concurrency\n\n- Thread `context.Context` through call chains for cancellation and timeout (`ctx, cancel := context.WithTimeout(ctx, 5*time.Second)`; `defer cancel()`).\n- Every piece of shared state has an explicit owner \u2014 a mutex or a channel \u2014 or a comment proving why it is safe. \"Probably fine\" is a race.\n- Always `defer mu.Unlock()` immediately after `mu.Lock()`; never leave an unlock path implicit.\n- Coordinate goroutines with `sync.WaitGroup` or `golang.org/x/sync/errgroup`; a bare `go func(){...}()` with no join point is a leak waiting to happen.\n- A goroutine that sends on an unbuffered channel with no guaranteed receiver blocks forever if the reader gives up \u2014 buffer the channel or `select` on `ctx.Done()`.\n\n```go\nfunc WorkerPool(jobs <-chan Job, results chan<- Result, numWorkers int) {\n var wg sync.WaitGroup\n for i := 0; i < numWorkers; i++ {\n wg.Add(1)\n go func() {\n defer wg.Done()\n for job := range jobs {\n results <- process(job)\n }\n }()\n }\n wg.Wait()\n close(results)\n}\n\n// Good: handles cancellation instead of leaking\nfunc safeFetch(ctx context.Context, url string) <-chan []byte {\n ch := make(chan []byte, 1)\n go func() {\n data, err := fetch(url)\n if err != nil {\n return\n }\n select {\n case ch <- data:\n case <-ctx.Done():\n }\n }()\n return ch\n}\n```\n\n```go\nimport \"golang.org/x/sync/errgroup\"\n\nfunc FetchAll(ctx context.Context, urls []string) ([][]byte, error) {\n g, ctx := errgroup.WithContext(ctx)\n results := make([][]byte, len(urls))\n for i, url := range urls {\n i, url := i, url // capture loop variables\n g.Go(func() error {\n data, err := FetchWithTimeout(ctx, url)\n if err != nil {\n return err\n }\n results[i] = data\n return nil\n })\n }\n if err := g.Wait(); err != nil {\n return nil, err\n }\n return results, nil\n}\n```\n\n### Interface design\n\n- Keep interfaces small (1-3 methods) and compose them (`ReadWriteCloser` from `Reader`+`Writer`+`Closer`), rather than one large interface.\n- Define the interface in the consumer's package, not the provider's \u2014 the implementation doesn't need to know the interface exists.\n- Optional behavior can be probed with a type assertion (`if f, ok := w.(Flusher); ok { ... }`) instead of a bloated required interface.\n\n### Package organization\n\n- Standard layout: `cmd/<app>/main.go` (entry point), `internal/` (handler/service/repository/config), `pkg/` (public client), `api/` (proto/OpenAPI), `testdata/`.\n- Package names: short, lowercase, no underscores, no redundant suffix (`user`, not `userService`).\n- Avoid package-level mutable state (`var db *sql.DB` + `init()`) \u2014 inject dependencies through a constructor instead.\n- Prefer embedding for composition (`type Server struct { *Logger; addr string }`) over duplicating methods.\n\n### Memory & performance\n\n- Preallocate slices when the size is known: `make([]T, 0, len(items))` instead of an unbounded `append` loop.\n- Use `strings.Builder` (or `strings.Join`) instead of `+=` string concatenation in a loop.\n- Use `sync.Pool` for frequently allocated short-lived buffers in hot paths.\n- Avoid unnecessary allocations in hot paths; N+1 query patterns and per-item deferred calls in a loop are red flags too.\n\n### Boundaries & secrets\n\n- Validate external input at the boundary before it reaches business logic; fail fast with a clear message.\n- Read required secrets from the environment and `log.Fatal` on an empty value at startup \u2014 never a silent zero value.\n\n```go\napiKey := os.Getenv(\"OPENAI_API_KEY\")\nif apiKey == \"\" {\n log.Fatal(\"OPENAI_API_KEY not configured\")\n}\n```\n\n### Security (flag in review \u2014 a linter won't catch these)\n\n- SQL injection: string concatenation in `database/sql` queries instead of parameterized placeholders.\n- Command injection: unvalidated input passed into `os/exec`.\n- Path traversal: user-controlled file paths used without `filepath.Clean` + a prefix check.\n- `unsafe` package usage without justification.\n- Hardcoded secrets (API keys, passwords) in source.\n- Insecure TLS (`InsecureSkipVerify: true`).\n- Run `gosec ./...` for static security analysis in CI.\n\n### Anti-patterns to flag in review (a linter won't)\n\n- A propagated error with no `%w` and no context string, or an error discarded with `_`.\n- `panic` used for a recoverable error instead of an error return.\n- An interface defined beside its implementation instead of its consumer.\n- Shared state touched by multiple goroutines with no mutex/channel and no safety comment.\n- A required env var read without an empty-check before use.\n- Naked returns in long functions, mixed value/pointer receivers on the same type, `context.Context` stored in a struct field instead of passed as the first parameter.\n- Functions over ~50 lines or nested more than 4 levels deep \u2014 prefer early return over `if/else` chains.\n\n### Testing & tooling\n\n- Standard `go test` with **table-driven tests**; always run `go test -race ./...` and check coverage with `go test -cover ./...`. See skill `golang-testing` for the full pattern set (TDD workflow, subtests, benchmarks, fuzzing, golden files, HTTP handler tests).\n\n```bash\ngo build ./...\ngo vet ./...\nstaticcheck ./...\ngolangci-lint run\ngo test -race ./...\ngo test -cover ./...\ngofmt -l .\ngoimports -l .\ngo mod tidy\ngo mod verify\n```\n\n### Quick reference: Go idioms\n\n| Idiom | Description |\n|-------|-------------|\n| Accept interfaces, return structs | Functions accept interface params, return concrete types |\n| Errors are values | Treat errors as first-class values, not exceptions |\n| Don't communicate by sharing memory | Use channels for coordination between goroutines |\n| Make the zero value useful | Types should work without explicit initialization |\n| A little copying is better than a little dependency | Avoid unnecessary external dependencies |\n| Clear is better than clever | Prioritize readability over cleverness |\n| gofmt is no one's favorite but everyone's friend | Always format with gofmt/goimports |\n| Return early | Handle errors first, keep happy path unindented |\n\nSee skills `golang-patterns` (comprehensive idioms, concurrency, package organization) and `golang-testing` (TDD workflow, table-driven tests, benchmarks, fuzzing) for full worked examples.\n"
|
|
11
15
|
},
|
|
12
16
|
"agents": [
|
|
13
17
|
{
|
|
14
18
|
"name": "go-reviewer",
|
|
15
|
-
"description": "
|
|
16
|
-
"tools": [
|
|
19
|
+
"description": "Expert Go code reviewer specializing in idiomatic Go, concurrency patterns, error handling, and security. Reports file:line + severity across CRITICAL/HIGH/MEDIUM categories. Use for all Go code changes.",
|
|
20
|
+
"tools": [
|
|
21
|
+
"Read",
|
|
22
|
+
"Grep",
|
|
23
|
+
"Glob",
|
|
24
|
+
"Bash"
|
|
25
|
+
],
|
|
26
|
+
"model": "sonnet",
|
|
27
|
+
"prompt": "You are a senior Go code reviewer ensuring high standards of idiomatic Go and best practices.\n\nWhen invoked:\n1. Run `git diff -- '*.go'` to see recent Go file changes.\n2. Run `go vet ./...` and `staticcheck ./...` if available.\n3. Focus on modified `.go` files.\n4. Begin review immediately.\n\n## Review priorities\n\n### CRITICAL \u2014 Security\n- SQL injection: string concatenation in `database/sql` queries.\n- Command injection: unvalidated input in `os/exec`.\n- Path traversal: user-controlled file paths without `filepath.Clean` + prefix check.\n- Race conditions: shared state without synchronization.\n- `unsafe` package use without justification.\n- Hardcoded secrets: API keys, passwords in source.\n- Insecure TLS: `InsecureSkipVerify: true`.\n\n### CRITICAL \u2014 Error handling\n- Ignored errors: using `_` to discard errors.\n- Missing error wrapping: `return err` without `fmt.Errorf(\"context: %w\", err)`.\n- Panic for recoverable errors: use error returns instead.\n- Missing `errors.Is`/`errors.As`: use `errors.Is(err, target)` not `err == target`.\n\n### HIGH \u2014 Concurrency\n- Goroutine leaks: no cancellation mechanism (use `context.Context`).\n- Unbuffered channel deadlock: sending without a guaranteed receiver.\n- Missing `sync.WaitGroup`/errgroup: goroutines without coordination.\n- Mutex misuse: not using `defer mu.Unlock()`.\n\n### HIGH \u2014 Code quality\n- Large functions: over 50 lines.\n- Deep nesting: more than 4 levels.\n- Non-idiomatic: `if/else` instead of early return.\n- Package-level variables: mutable global state.\n- Interface pollution: defining unused abstractions.\n\n### MEDIUM \u2014 Performance\n- String concatenation in loops: use `strings.Builder`.\n- Missing slice pre-allocation: `make([]T, 0, cap)`.\n- N+1 queries: database queries in loops.\n- Unnecessary allocations: objects in hot paths.\n\n### MEDIUM \u2014 Best practices\n- Context first: `ctx context.Context` should be the first parameter.\n- Table-driven tests: tests should use the table-driven pattern.\n- Error messages: lowercase, no punctuation.\n- Package naming: short, lowercase, no underscores.\n- Deferred call in a loop: resource accumulation risk.\n\n## Diagnostic commands\n\n```bash\ngo vet ./...\nstaticcheck ./...\ngolangci-lint run\ngo build -race ./...\ngo test -race ./...\ngovulncheck ./...\n```\n\n## Approval criteria\n\n- Approve: no CRITICAL or HIGH issues.\n- Warning: MEDIUM issues only.\n- Block: CRITICAL or HIGH issues found.\n\nCite the exact file:line for each finding, name the concrete failure mode, and give severity. Formatting itself (gofmt) is the linter/CI's job \u2014 don't re-litigate it. If the change is sound, say so plainly rather than inventing findings.\n\nFor detailed Go code examples and anti-patterns, see skill `golang-patterns`."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "go-build-resolver",
|
|
31
|
+
"description": "Go build, vet, and compilation error resolution specialist. Fixes build errors, go vet issues, and linter warnings with minimal, surgical changes. Use when Go builds fail.",
|
|
32
|
+
"tools": [
|
|
33
|
+
"Read",
|
|
34
|
+
"Write",
|
|
35
|
+
"Edit",
|
|
36
|
+
"Bash",
|
|
37
|
+
"Grep",
|
|
38
|
+
"Glob"
|
|
39
|
+
],
|
|
17
40
|
"model": "sonnet",
|
|
18
|
-
"prompt": "You are
|
|
41
|
+
"prompt": "You are an expert Go build error resolution specialist. Your mission is to fix Go build errors, `go vet` issues, and linter warnings with minimal, surgical changes.\n\n## Core responsibilities\n\n1. Diagnose Go compilation errors.\n2. Fix `go vet` warnings.\n3. Resolve `staticcheck`/`golangci-lint` issues.\n4. Handle module dependency problems.\n5. Fix type errors and interface mismatches.\n\n## Diagnostic commands (run in order)\n\n```bash\ngo build ./...\ngo vet ./...\nstaticcheck ./... 2>/dev/null || echo \"staticcheck not installed\"\ngolangci-lint run 2>/dev/null || echo \"golangci-lint not installed\"\ngo mod verify\ngo mod tidy -v\n```\n\n## Resolution workflow\n\n```text\n1. go build ./... -> Parse error message\n2. Read affected file -> Understand context\n3. Apply minimal fix -> Only what's needed\n4. go build ./... -> Verify fix\n5. go vet ./... -> Check for warnings\n6. go test ./... -> Ensure nothing broke\n```\n\n## Common fix patterns\n\n| Error | Cause | Fix |\n|-------|-------|-----|\n| `undefined: X` | Missing import, typo, unexported | Add import or fix casing |\n| `cannot use X as type Y` | Type mismatch, pointer/value | Type conversion or dereference |\n| `X does not implement Y` | Missing method | Implement method with correct receiver |\n| `import cycle not allowed` | Circular dependency | Extract shared types to new package |\n| `cannot find package` | Missing dependency | `go get pkg@version` or `go mod tidy` |\n| `missing return` | Incomplete control flow | Add return statement |\n| `declared but not used` | Unused var/import | Remove or use blank identifier |\n| `multiple-value in single-value context` | Unhandled return | `result, err := func()` |\n| `cannot assign to struct field in map` | Map value mutation | Use pointer map or copy-modify-reassign |\n| `invalid type assertion` | Assert on non-interface | Only assert from `interface{}` |\n\n## Module troubleshooting\n\n```bash\ngrep \"replace\" go.mod # Check local replaces\ngo mod why -m package # Why a version is selected\ngo get package@v1.2.3 # Pin specific version\ngo clean -modcache && go mod download # Fix checksum issues\n```\n\n## Key principles\n\n- Surgical fixes only \u2014 don't refactor, just fix the error.\n- Never add `//nolint` without explicit approval.\n- Never change function signatures unless necessary.\n- Always run `go mod tidy` after adding/removing imports.\n- Fix root cause over suppressing symptoms.\n\n## Stop conditions\n\nStop and report if:\n- The same error persists after 3 fix attempts.\n- A fix introduces more errors than it resolves.\n- The error requires architectural changes beyond scope.\n\n## Output format\n\n```text\n[FIXED] internal/handler/user.go:42\nError: undefined: UserService\nFix: Added import \"project/internal/service\"\nRemaining errors: 3\n```\n\nFinal line: `Build Status: SUCCESS/FAILED | Errors Fixed: N | Files Modified: list`\n\nFor detailed Go error patterns and code examples, see skill `golang-patterns`."
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"skills": [
|
|
45
|
+
{
|
|
46
|
+
"name": "golang-patterns",
|
|
47
|
+
"description": "Idiomatic Go patterns, best practices, and conventions for building robust, efficient, and maintainable Go applications \u2014 API shape, error handling, concurrency, interface design, package organization, and performance. Use when writing, reviewing, or refactoring Go code, or designing Go packages/modules.",
|
|
48
|
+
"body": "# Go Development Patterns\n\nIdiomatic Go patterns and best practices for building robust, efficient, and maintainable applications.\n\n## When to use\n\n- Writing new Go code.\n- Reviewing Go code.\n- Refactoring existing Go code.\n- Designing Go packages/modules.\n\n## Core principles\n\n### 1. Simplicity and clarity\n\nGo favors simplicity over cleverness. Code should be obvious and easy to read.\n\n\\`\\`\\`go\n// Good: clear and direct\nfunc GetUser(id string) (*User, error) {\n user, err := db.FindUser(id)\n if err != nil {\n return nil, fmt.Errorf(\"get user %s: %w\", id, err)\n }\n return user, nil\n}\n\n// Bad: overly clever\nfunc GetUser(id string) (*User, error) {\n return func() (*User, error) {\n if u, e := db.FindUser(id); e == nil {\n return u, nil\n } else {\n return nil, e\n }\n }()\n}\n\\`\\`\\`\n\n### 2. Make the zero value useful\n\nDesign types so their zero value is immediately usable without initialization.\n\n\\`\\`\\`go\n// Good: zero value is useful\ntype Counter struct {\n mu sync.Mutex\n count int // zero value is 0, ready to use\n}\n\nfunc (c *Counter) Inc() {\n c.mu.Lock()\n c.count++\n c.mu.Unlock()\n}\n\n// Good: bytes.Buffer works with zero value\nvar buf bytes.Buffer\nbuf.WriteString(\"hello\")\n\n// Bad: requires initialization\ntype BadCounter struct {\n counts map[string]int // nil map will panic\n}\n\\`\\`\\`\n\n### 3. Accept interfaces, return structs\n\nFunctions should accept interface parameters and return concrete types.\n\n\\`\\`\\`go\n// Good: accepts interface, returns concrete type\nfunc ProcessData(r io.Reader) (*Result, error) {\n data, err := io.ReadAll(r)\n if err != nil {\n return nil, err\n }\n return &Result{Data: data}, nil\n}\n\n// Bad: returns interface (hides implementation details unnecessarily)\nfunc ProcessData(r io.Reader) (io.Reader, error) {\n // ...\n}\n\\`\\`\\`\n\n## Error handling patterns\n\n### Error wrapping with context\n\n\\`\\`\\`go\nfunc LoadConfig(path string) (*Config, error) {\n data, err := os.ReadFile(path)\n if err != nil {\n return nil, fmt.Errorf(\"load config %s: %w\", path, err)\n }\n\n var cfg Config\n if err := json.Unmarshal(data, &cfg); err != nil {\n return nil, fmt.Errorf(\"parse config %s: %w\", path, err)\n }\n\n return &cfg, nil\n}\n\\`\\`\\`\n\n### Custom error types\n\n\\`\\`\\`go\n// Define domain-specific errors\ntype ValidationError struct {\n Field string\n Message string\n}\n\nfunc (e *ValidationError) Error() string {\n return fmt.Sprintf(\"validation failed on %s: %s\", e.Field, e.Message)\n}\n\n// Sentinel errors for common cases\nvar (\n ErrNotFound = errors.New(\"resource not found\")\n ErrUnauthorized = errors.New(\"unauthorized\")\n ErrInvalidInput = errors.New(\"invalid input\")\n)\n\\`\\`\\`\n\n### Error checking with errors.Is and errors.As\n\n\\`\\`\\`go\nfunc HandleError(err error) {\n // Check for specific error\n if errors.Is(err, sql.ErrNoRows) {\n log.Println(\"No records found\")\n return\n }\n\n // Check for error type\n var validationErr *ValidationError\n if errors.As(err, &validationErr) {\n log.Printf(\"Validation error on field %s: %s\",\n validationErr.Field, validationErr.Message)\n return\n }\n\n // Unknown error\n log.Printf(\"Unexpected error: %v\", err)\n}\n\\`\\`\\`\n\n### Never ignore errors\n\n\\`\\`\\`go\n// Bad: ignoring error with blank identifier\nresult, _ := doSomething()\n\n// Good: handle or explicitly document why it's safe to ignore\nresult, err := doSomething()\nif err != nil {\n return err\n}\n\n// Acceptable: when error truly doesn't matter (rare)\n_ = writer.Close() // Best-effort cleanup, error logged elsewhere\n\\`\\`\\`\n\n## Concurrency patterns\n\n### Worker pool\n\n\\`\\`\\`go\nfunc WorkerPool(jobs <-chan Job, results chan<- Result, numWorkers int) {\n var wg sync.WaitGroup\n\n for i := 0; i < numWorkers; i++ {\n wg.Add(1)\n go func() {\n defer wg.Done()\n for job := range jobs {\n results <- process(job)\n }\n }()\n }\n\n wg.Wait()\n close(results)\n}\n\\`\\`\\`\n\n### Context for cancellation and timeouts\n\n\\`\\`\\`go\nfunc FetchWithTimeout(ctx context.Context, url string) ([]byte, error) {\n ctx, cancel := context.WithTimeout(ctx, 5*time.Second)\n defer cancel()\n\n req, err := http.NewRequestWithContext(ctx, \"GET\", url, nil)\n if err != nil {\n return nil, fmt.Errorf(\"create request: %w\", err)\n }\n\n resp, err := http.DefaultClient.Do(req)\n if err != nil {\n return nil, fmt.Errorf(\"fetch %s: %w\", url, err)\n }\n defer resp.Body.Close()\n\n return io.ReadAll(resp.Body)\n}\n\\`\\`\\`\n\n### Graceful shutdown\n\n\\`\\`\\`go\nfunc GracefulShutdown(server *http.Server) {\n quit := make(chan os.Signal, 1)\n signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)\n\n <-quit\n log.Println(\"Shutting down server...\")\n\n ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n defer cancel()\n\n if err := server.Shutdown(ctx); err != nil {\n log.Fatalf(\"Server forced to shutdown: %v\", err)\n }\n\n log.Println(\"Server exited\")\n}\n\\`\\`\\`\n\n### errgroup for coordinated goroutines\n\n\\`\\`\\`go\nimport \"golang.org/x/sync/errgroup\"\n\nfunc FetchAll(ctx context.Context, urls []string) ([][]byte, error) {\n g, ctx := errgroup.WithContext(ctx)\n results := make([][]byte, len(urls))\n\n for i, url := range urls {\n i, url := i, url // Capture loop variables\n g.Go(func() error {\n data, err := FetchWithTimeout(ctx, url)\n if err != nil {\n return err\n }\n results[i] = data\n return nil\n })\n }\n\n if err := g.Wait(); err != nil {\n return nil, err\n }\n return results, nil\n}\n\\`\\`\\`\n\n### Avoiding goroutine leaks\n\n\\`\\`\\`go\n// Bad: goroutine leak if context is cancelled\nfunc leakyFetch(ctx context.Context, url string) <-chan []byte {\n ch := make(chan []byte)\n go func() {\n data, _ := fetch(url)\n ch <- data // Blocks forever if no receiver\n }()\n return ch\n}\n\n// Good: properly handles cancellation\nfunc safeFetch(ctx context.Context, url string) <-chan []byte {\n ch := make(chan []byte, 1) // Buffered channel\n go func() {\n data, err := fetch(url)\n if err != nil {\n return\n }\n select {\n case ch <- data:\n case <-ctx.Done():\n }\n }()\n return ch\n}\n\\`\\`\\`\n\n## Interface design\n\n### Small, focused interfaces\n\n\\`\\`\\`go\n// Good: single-method interfaces\ntype Reader interface {\n Read(p []byte) (n int, err error)\n}\n\ntype Writer interface {\n Write(p []byte) (n int, err error)\n}\n\ntype Closer interface {\n Close() error\n}\n\n// Compose interfaces as needed\ntype ReadWriteCloser interface {\n Reader\n Writer\n Closer\n}\n\\`\\`\\`\n\n### Define interfaces where they're used\n\n\\`\\`\\`go\n// In the consumer package, not the provider\npackage service\n\n// UserStore defines what this service needs\ntype UserStore interface {\n GetUser(id string) (*User, error)\n SaveUser(user *User) error\n}\n\ntype Service struct {\n store UserStore\n}\n\n// Concrete implementation can be in another package\n// It doesn't need to know about this interface\n\\`\\`\\`\n\n### Optional behavior with type assertions\n\n\\`\\`\\`go\ntype Flusher interface {\n Flush() error\n}\n\nfunc WriteAndFlush(w io.Writer, data []byte) error {\n if _, err := w.Write(data); err != nil {\n return err\n }\n\n // Flush if supported\n if f, ok := w.(Flusher); ok {\n return f.Flush()\n }\n return nil\n}\n\\`\\`\\`\n\n## Package organization\n\n### Standard project layout\n\n\\`\\`\\`text\nmyproject/\n\u251c\u2500\u2500 cmd/\n\u2502 \u2514\u2500\u2500 myapp/\n\u2502 \u2514\u2500\u2500 main.go # Entry point\n\u251c\u2500\u2500 internal/\n\u2502 \u251c\u2500\u2500 handler/ # HTTP handlers\n\u2502 \u251c\u2500\u2500 service/ # Business logic\n\u2502 \u251c\u2500\u2500 repository/ # Data access\n\u2502 \u2514\u2500\u2500 config/ # Configuration\n\u251c\u2500\u2500 pkg/\n\u2502 \u2514\u2500\u2500 client/ # Public API client\n\u251c\u2500\u2500 api/\n\u2502 \u2514\u2500\u2500 v1/ # API definitions (proto, OpenAPI)\n\u251c\u2500\u2500 testdata/ # Test fixtures\n\u251c\u2500\u2500 go.mod\n\u251c\u2500\u2500 go.sum\n\u2514\u2500\u2500 Makefile\n\\`\\`\\`\n\n### Package naming\n\n\\`\\`\\`go\n// Good: short, lowercase, no underscores\npackage http\npackage json\npackage user\n\n// Bad: verbose, mixed case, or redundant\npackage httpHandler\npackage json_parser\npackage userService // Redundant 'Service' suffix\n\\`\\`\\`\n\n### Avoid package-level state\n\n\\`\\`\\`go\n// Bad: global mutable state\nvar db *sql.DB\n\nfunc init() {\n db, _ = sql.Open(\"postgres\", os.Getenv(\"DATABASE_URL\"))\n}\n\n// Good: dependency injection\ntype Server struct {\n db *sql.DB\n}\n\nfunc NewServer(db *sql.DB) *Server {\n return &Server{db: db}\n}\n\\`\\`\\`\n\n## Struct design\n\n### Functional options pattern\n\n\\`\\`\\`go\ntype Server struct {\n addr string\n timeout time.Duration\n logger *log.Logger\n}\n\ntype Option func(*Server)\n\nfunc WithTimeout(d time.Duration) Option {\n return func(s *Server) {\n s.timeout = d\n }\n}\n\nfunc WithLogger(l *log.Logger) Option {\n return func(s *Server) {\n s.logger = l\n }\n}\n\nfunc NewServer(addr string, opts ...Option) *Server {\n s := &Server{\n addr: addr,\n timeout: 30 * time.Second, // default\n logger: log.Default(), // default\n }\n for _, opt := range opts {\n opt(s)\n }\n return s\n}\n\n// Usage\nserver := NewServer(\":8080\",\n WithTimeout(60*time.Second),\n WithLogger(customLogger),\n)\n\\`\\`\\`\n\n### Embedding for composition\n\n\\`\\`\\`go\ntype Logger struct {\n prefix string\n}\n\nfunc (l *Logger) Log(msg string) {\n fmt.Printf(\"[%s] %s\\n\", l.prefix, msg)\n}\n\ntype Server struct {\n *Logger // Embedding - Server gets Log method\n addr string\n}\n\nfunc NewServer(addr string) *Server {\n return &Server{\n Logger: &Logger{prefix: \"SERVER\"},\n addr: addr,\n }\n}\n\n// Usage\ns := NewServer(\":8080\")\ns.Log(\"Starting...\") // Calls embedded Logger.Log\n\\`\\`\\`\n\n## Memory and performance\n\n### Preallocate slices when size is known\n\n\\`\\`\\`go\n// Bad: grows slice multiple times\nfunc processItems(items []Item) []Result {\n var results []Result\n for _, item := range items {\n results = append(results, process(item))\n }\n return results\n}\n\n// Good: single allocation\nfunc processItems(items []Item) []Result {\n results := make([]Result, 0, len(items))\n for _, item := range items {\n results = append(results, process(item))\n }\n return results\n}\n\\`\\`\\`\n\n### Use sync.Pool for frequent allocations\n\n\\`\\`\\`go\nvar bufferPool = sync.Pool{\n New: func() interface{} {\n return new(bytes.Buffer)\n },\n}\n\nfunc ProcessRequest(data []byte) []byte {\n buf := bufferPool.Get().(*bytes.Buffer)\n defer func() {\n buf.Reset()\n bufferPool.Put(buf)\n }()\n\n buf.Write(data)\n // Process...\n return buf.Bytes()\n}\n\\`\\`\\`\n\n### Avoid string concatenation in loops\n\n\\`\\`\\`go\n// Bad: creates many string allocations\nfunc join(parts []string) string {\n var result string\n for _, p := range parts {\n result += p + \",\"\n }\n return result\n}\n\n// Good: single allocation with strings.Builder\nfunc join(parts []string) string {\n var sb strings.Builder\n for i, p := range parts {\n if i > 0 {\n sb.WriteString(\",\")\n }\n sb.WriteString(p)\n }\n return sb.String()\n}\n\n// Best: use standard library\nfunc join(parts []string) string {\n return strings.Join(parts, \",\")\n}\n\\`\\`\\`\n\n## Go tooling integration\n\n### Essential commands\n\n\\`\\`\\`bash\n# Build and run\ngo build ./...\ngo run ./cmd/myapp\n\n# Testing\ngo test ./...\ngo test -race ./...\ngo test -cover ./...\n\n# Static analysis\ngo vet ./...\nstaticcheck ./...\ngolangci-lint run\n\n# Module management\ngo mod tidy\ngo mod verify\n\n# Formatting\ngofmt -w .\ngoimports -w .\n\\`\\`\\`\n\n### Recommended linter configuration (.golangci.yml)\n\n\\`\\`\\`yaml\nlinters:\n enable:\n - errcheck\n - gosimple\n - govet\n - ineffassign\n - staticcheck\n - unused\n - gofmt\n - goimports\n - misspell\n - unconvert\n - unparam\n\nlinters-settings:\n errcheck:\n check-type-assertions: true\n govet:\n enable:\n - shadow\n\nissues:\n exclude-use-default: false\n\\`\\`\\`\n\n## Quick reference: Go idioms\n\n| Idiom | Description |\n|-------|-------------|\n| Accept interfaces, return structs | Functions accept interface params, return concrete types |\n| Errors are values | Treat errors as first-class values, not exceptions |\n| Don't communicate by sharing memory | Use channels for coordination between goroutines |\n| Make the zero value useful | Types should work without explicit initialization |\n| A little copying is better than a little dependency | Avoid unnecessary external dependencies |\n| Clear is better than clever | Prioritize readability over cleverness |\n| gofmt is no one's favorite but everyone's friend | Always format with gofmt/goimports |\n| Return early | Handle errors first, keep happy path unindented |\n\n## Anti-patterns to avoid\n\n\\`\\`\\`go\n// Bad: naked returns in long functions\nfunc process() (result int, err error) {\n // ... 50 lines ...\n return // What is being returned?\n}\n\n// Bad: using panic for control flow\nfunc GetUser(id string) *User {\n user, err := db.Find(id)\n if err != nil {\n panic(err) // Don't do this\n }\n return user\n}\n\n// Bad: passing context in struct\ntype Request struct {\n ctx context.Context // Context should be first param\n ID string\n}\n\n// Good: context as first parameter\nfunc ProcessRequest(ctx context.Context, id string) error {\n // ...\n}\n\n// Bad: mixing value and pointer receivers\ntype Counter struct{ n int }\nfunc (c Counter) Value() int { return c.n } // Value receiver\nfunc (c *Counter) Increment() { c.n++ } // Pointer receiver\n// Pick one style and be consistent\n\\`\\`\\`\n\n**Remember**: Go code should be boring in the best way \u2014 predictable, consistent, and easy to understand. When in doubt, keep it simple.\n",
|
|
49
|
+
"category": "language"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "golang-testing",
|
|
53
|
+
"description": "Go testing patterns including TDD workflow, table-driven tests, subtests, benchmarks, fuzzing, golden files, HTTP handler testing, mocking with interfaces, and coverage targets. Use when writing new Go functions, adding test coverage, creating benchmarks, or following TDD in a Go project.",
|
|
54
|
+
"body": "# Go Testing Patterns\n\nComprehensive Go testing patterns for writing reliable, maintainable tests following TDD methodology.\n\n## When to use\n\n- Writing new Go functions or methods.\n- Adding test coverage to existing code.\n- Creating benchmarks for performance-critical code.\n- Implementing fuzz tests for input validation.\n- Following a TDD workflow in Go projects.\n\n## TDD workflow for Go\n\n### The RED-GREEN-REFACTOR cycle\n\n\\`\\`\\`\nRED \u2192 Write a failing test first\nGREEN \u2192 Write minimal code to pass the test\nREFACTOR \u2192 Improve code while keeping tests green\nREPEAT \u2192 Continue with next requirement\n\\`\\`\\`\n\n### Step-by-step TDD in Go\n\n\\`\\`\\`go\n// Step 1: Define the interface/signature\n// calculator.go\npackage calculator\n\nfunc Add(a, b int) int {\n panic(\"not implemented\") // Placeholder\n}\n\n// Step 2: Write failing test (RED)\n// calculator_test.go\npackage calculator\n\nimport \"testing\"\n\nfunc TestAdd(t *testing.T) {\n got := Add(2, 3)\n want := 5\n if got != want {\n t.Errorf(\"Add(2, 3) = %d; want %d\", got, want)\n }\n}\n\n// Step 3: Run test - verify FAIL\n// $ go test\n// --- FAIL: TestAdd (0.00s)\n// panic: not implemented\n\n// Step 4: Implement minimal code (GREEN)\nfunc Add(a, b int) int {\n return a + b\n}\n\n// Step 5: Run test - verify PASS\n// $ go test\n// PASS\n\n// Step 6: Refactor if needed, verify tests still pass\n\\`\\`\\`\n\n## Table-driven tests\n\nThe standard pattern for Go tests. Enables comprehensive coverage with minimal code.\n\n\\`\\`\\`go\nfunc TestAdd(t *testing.T) {\n tests := []struct {\n name string\n a, b int\n expected int\n }{\n {\"positive numbers\", 2, 3, 5},\n {\"negative numbers\", -1, -2, -3},\n {\"zero values\", 0, 0, 0},\n {\"mixed signs\", -1, 1, 0},\n {\"large numbers\", 1000000, 2000000, 3000000},\n }\n\n for _, tt := range tests {\n t.Run(tt.name, func(t *testing.T) {\n got := Add(tt.a, tt.b)\n if got != tt.expected {\n t.Errorf(\"Add(%d, %d) = %d; want %d\",\n tt.a, tt.b, got, tt.expected)\n }\n })\n }\n}\n\\`\\`\\`\n\n### Table-driven tests with error cases\n\n\\`\\`\\`go\nfunc TestParseConfig(t *testing.T) {\n tests := []struct {\n name string\n input string\n want *Config\n wantErr bool\n }{\n {\n name: \"valid config\",\n input: \\`{\"host\": \"localhost\", \"port\": 8080}\\`,\n want: &Config{Host: \"localhost\", Port: 8080},\n },\n {\n name: \"invalid JSON\",\n input: \\`{invalid}\\`,\n wantErr: true,\n },\n {\n name: \"empty input\",\n input: \"\",\n wantErr: true,\n },\n {\n name: \"minimal config\",\n input: \\`{}\\`,\n want: &Config{}, // Zero value config\n },\n }\n\n for _, tt := range tests {\n t.Run(tt.name, func(t *testing.T) {\n got, err := ParseConfig(tt.input)\n\n if tt.wantErr {\n if err == nil {\n t.Error(\"expected error, got nil\")\n }\n return\n }\n\n if err != nil {\n t.Fatalf(\"unexpected error: %v\", err)\n }\n\n if !reflect.DeepEqual(got, tt.want) {\n t.Errorf(\"got %+v; want %+v\", got, tt.want)\n }\n })\n }\n}\n\\`\\`\\`\n\n## Subtests and sub-benchmarks\n\n### Organizing related tests\n\n\\`\\`\\`go\nfunc TestUser(t *testing.T) {\n // Setup shared by all subtests\n db := setupTestDB(t)\n\n t.Run(\"Create\", func(t *testing.T) {\n user := &User{Name: \"Alice\"}\n err := db.CreateUser(user)\n if err != nil {\n t.Fatalf(\"CreateUser failed: %v\", err)\n }\n if user.ID == \"\" {\n t.Error(\"expected user ID to be set\")\n }\n })\n\n t.Run(\"Get\", func(t *testing.T) {\n user, err := db.GetUser(\"alice-id\")\n if err != nil {\n t.Fatalf(\"GetUser failed: %v\", err)\n }\n if user.Name != \"Alice\" {\n t.Errorf(\"got name %q; want %q\", user.Name, \"Alice\")\n }\n })\n\n t.Run(\"Update\", func(t *testing.T) {\n // ...\n })\n\n t.Run(\"Delete\", func(t *testing.T) {\n // ...\n })\n}\n\\`\\`\\`\n\n### Parallel subtests\n\n\\`\\`\\`go\nfunc TestParallel(t *testing.T) {\n tests := []struct {\n name string\n input string\n }{\n {\"case1\", \"input1\"},\n {\"case2\", \"input2\"},\n {\"case3\", \"input3\"},\n }\n\n for _, tt := range tests {\n tt := tt // Capture range variable\n t.Run(tt.name, func(t *testing.T) {\n t.Parallel() // Run subtests in parallel\n result := Process(tt.input)\n // assertions...\n _ = result\n })\n }\n}\n\\`\\`\\`\n\n## Test helpers\n\n### Helper functions\n\n\\`\\`\\`go\nfunc setupTestDB(t *testing.T) *sql.DB {\n t.Helper() // Marks this as a helper function\n\n db, err := sql.Open(\"sqlite3\", \":memory:\")\n if err != nil {\n t.Fatalf(\"failed to open database: %v\", err)\n }\n\n // Cleanup when test finishes\n t.Cleanup(func() {\n db.Close()\n })\n\n // Run migrations\n if _, err := db.Exec(schema); err != nil {\n t.Fatalf(\"failed to create schema: %v\", err)\n }\n\n return db\n}\n\nfunc assertNoError(t *testing.T, err error) {\n t.Helper()\n if err != nil {\n t.Fatalf(\"unexpected error: %v\", err)\n }\n}\n\nfunc assertEqual[T comparable](t *testing.T, got, want T) {\n t.Helper()\n if got != want {\n t.Errorf(\"got %v; want %v\", got, want)\n }\n}\n\\`\\`\\`\n\n### Temporary files and directories\n\n\\`\\`\\`go\nfunc TestFileProcessing(t *testing.T) {\n // Create temp directory - automatically cleaned up\n tmpDir := t.TempDir()\n\n // Create test file\n testFile := filepath.Join(tmpDir, \"test.txt\")\n err := os.WriteFile(testFile, []byte(\"test content\"), 0644)\n if err != nil {\n t.Fatalf(\"failed to create test file: %v\", err)\n }\n\n // Run test\n result, err := ProcessFile(testFile)\n if err != nil {\n t.Fatalf(\"ProcessFile failed: %v\", err)\n }\n\n // Assert...\n _ = result\n}\n\\`\\`\\`\n\n## Golden files\n\nTesting against expected output files stored in \\`testdata/\\`.\n\n\\`\\`\\`go\nvar update = flag.Bool(\"update\", false, \"update golden files\")\n\nfunc TestRender(t *testing.T) {\n tests := []struct {\n name string\n input Template\n }{\n {\"simple\", Template{Name: \"test\"}},\n {\"complex\", Template{Name: \"test\", Items: []string{\"a\", \"b\"}}},\n }\n\n for _, tt := range tests {\n t.Run(tt.name, func(t *testing.T) {\n got := Render(tt.input)\n\n golden := filepath.Join(\"testdata\", tt.name+\".golden\")\n\n if *update {\n // Update golden file: go test -update\n err := os.WriteFile(golden, got, 0644)\n if err != nil {\n t.Fatalf(\"failed to update golden file: %v\", err)\n }\n }\n\n want, err := os.ReadFile(golden)\n if err != nil {\n t.Fatalf(\"failed to read golden file: %v\", err)\n }\n\n if !bytes.Equal(got, want) {\n t.Errorf(\"output mismatch:\\ngot:\\n%s\\nwant:\\n%s\", got, want)\n }\n })\n }\n}\n\\`\\`\\`\n\n## Mocking with interfaces\n\n### Interface-based mocking\n\n\\`\\`\\`go\n// Define interface for dependencies\ntype UserRepository interface {\n GetUser(id string) (*User, error)\n SaveUser(user *User) error\n}\n\n// Production implementation\ntype PostgresUserRepository struct {\n db *sql.DB\n}\n\nfunc (r *PostgresUserRepository) GetUser(id string) (*User, error) {\n // Real database query\n}\n\n// Mock implementation for tests\ntype MockUserRepository struct {\n GetUserFunc func(id string) (*User, error)\n SaveUserFunc func(user *User) error\n}\n\nfunc (m *MockUserRepository) GetUser(id string) (*User, error) {\n return m.GetUserFunc(id)\n}\n\nfunc (m *MockUserRepository) SaveUser(user *User) error {\n return m.SaveUserFunc(user)\n}\n\n// Test using mock\nfunc TestUserService(t *testing.T) {\n mock := &MockUserRepository{\n GetUserFunc: func(id string) (*User, error) {\n if id == \"123\" {\n return &User{ID: \"123\", Name: \"Alice\"}, nil\n }\n return nil, ErrNotFound\n },\n }\n\n service := NewUserService(mock)\n\n user, err := service.GetUserProfile(\"123\")\n if err != nil {\n t.Fatalf(\"unexpected error: %v\", err)\n }\n if user.Name != \"Alice\" {\n t.Errorf(\"got name %q; want %q\", user.Name, \"Alice\")\n }\n}\n\\`\\`\\`\n\n## Benchmarks\n\n### Basic benchmarks\n\n\\`\\`\\`go\nfunc BenchmarkProcess(b *testing.B) {\n data := generateTestData(1000)\n b.ResetTimer() // Don't count setup time\n\n for i := 0; i < b.N; i++ {\n Process(data)\n }\n}\n\n// Run: go test -bench=BenchmarkProcess -benchmem\n// Output: BenchmarkProcess-8 10000 105234 ns/op 4096 B/op 10 allocs/op\n\\`\\`\\`\n\n### Benchmark with different sizes\n\n\\`\\`\\`go\nfunc BenchmarkSort(b *testing.B) {\n sizes := []int{100, 1000, 10000, 100000}\n\n for _, size := range sizes {\n b.Run(fmt.Sprintf(\"size=%d\", size), func(b *testing.B) {\n data := generateRandomSlice(size)\n b.ResetTimer()\n\n for i := 0; i < b.N; i++ {\n // Make a copy to avoid sorting already sorted data\n tmp := make([]int, len(data))\n copy(tmp, data)\n sort.Ints(tmp)\n }\n })\n }\n}\n\\`\\`\\`\n\n### Memory allocation benchmarks\n\n\\`\\`\\`go\nfunc BenchmarkStringConcat(b *testing.B) {\n parts := []string{\"hello\", \"world\", \"foo\", \"bar\", \"baz\"}\n\n b.Run(\"plus\", func(b *testing.B) {\n for i := 0; i < b.N; i++ {\n var s string\n for _, p := range parts {\n s += p\n }\n _ = s\n }\n })\n\n b.Run(\"builder\", func(b *testing.B) {\n for i := 0; i < b.N; i++ {\n var sb strings.Builder\n for _, p := range parts {\n sb.WriteString(p)\n }\n _ = sb.String()\n }\n })\n\n b.Run(\"join\", func(b *testing.B) {\n for i := 0; i < b.N; i++ {\n _ = strings.Join(parts, \"\")\n }\n })\n}\n\\`\\`\\`\n\n## Fuzzing (Go 1.18+)\n\n### Basic fuzz test\n\n\\`\\`\\`go\nfunc FuzzParseJSON(f *testing.F) {\n // Add seed corpus\n f.Add(\\`{\"name\": \"test\"}\\`)\n f.Add(\\`{\"count\": 123}\\`)\n f.Add(\\`[]\\`)\n f.Add(\\`\"\"\\`)\n\n f.Fuzz(func(t *testing.T, input string) {\n var result map[string]interface{}\n err := json.Unmarshal([]byte(input), &result)\n\n if err != nil {\n // Invalid JSON is expected for random input\n return\n }\n\n // If parsing succeeded, re-encoding should work\n _, err = json.Marshal(result)\n if err != nil {\n t.Errorf(\"Marshal failed after successful Unmarshal: %v\", err)\n }\n })\n}\n\n// Run: go test -fuzz=FuzzParseJSON -fuzztime=30s\n\\`\\`\\`\n\n### Fuzz test with multiple inputs\n\n\\`\\`\\`go\nfunc FuzzCompare(f *testing.F) {\n f.Add(\"hello\", \"world\")\n f.Add(\"\", \"\")\n f.Add(\"abc\", \"abc\")\n\n f.Fuzz(func(t *testing.T, a, b string) {\n result := Compare(a, b)\n\n // Property: Compare(a, a) should always equal 0\n if a == b && result != 0 {\n t.Errorf(\"Compare(%q, %q) = %d; want 0\", a, b, result)\n }\n\n // Property: Compare(a, b) and Compare(b, a) should have opposite signs\n reverse := Compare(b, a)\n if (result > 0 && reverse >= 0) || (result < 0 && reverse <= 0) {\n if result != 0 || reverse != 0 {\n t.Errorf(\"Compare(%q, %q) = %d, Compare(%q, %q) = %d; inconsistent\",\n a, b, result, b, a, reverse)\n }\n }\n })\n}\n\\`\\`\\`\n\n## Test coverage\n\n### Running coverage\n\n\\`\\`\\`bash\n# Basic coverage\ngo test -cover ./...\n\n# Generate coverage profile\ngo test -coverprofile=coverage.out ./...\n\n# View coverage in browser\ngo tool cover -html=coverage.out\n\n# View coverage by function\ngo tool cover -func=coverage.out\n\n# Coverage with race detection\ngo test -race -coverprofile=coverage.out ./...\n\\`\\`\\`\n\n### Coverage targets\n\n| Code Type | Target |\n|-----------|--------|\n| Critical business logic | 100% |\n| Public APIs | 90%+ |\n| General code | 80%+ |\n| Generated code | Exclude |\n\n### Excluding generated code from coverage\n\n\\`\\`\\`go\n//go:generate mockgen -source=interface.go -destination=mock_interface.go\n\n// In coverage profile, exclude with build tags:\n// go test -cover -tags=!generate ./...\n\\`\\`\\`\n\n## HTTP handler testing\n\n\\`\\`\\`go\nfunc TestHealthHandler(t *testing.T) {\n // Create request\n req := httptest.NewRequest(http.MethodGet, \"/health\", nil)\n w := httptest.NewRecorder()\n\n // Call handler\n HealthHandler(w, req)\n\n // Check response\n resp := w.Result()\n defer resp.Body.Close()\n\n if resp.StatusCode != http.StatusOK {\n t.Errorf(\"got status %d; want %d\", resp.StatusCode, http.StatusOK)\n }\n\n body, _ := io.ReadAll(resp.Body)\n if string(body) != \"OK\" {\n t.Errorf(\"got body %q; want %q\", body, \"OK\")\n }\n}\n\nfunc TestAPIHandler(t *testing.T) {\n tests := []struct {\n name string\n method string\n path string\n body string\n wantStatus int\n wantBody string\n }{\n {\n name: \"get user\",\n method: http.MethodGet,\n path: \"/users/123\",\n wantStatus: http.StatusOK,\n wantBody: \\`{\"id\":\"123\",\"name\":\"Alice\"}\\`,\n },\n {\n name: \"not found\",\n method: http.MethodGet,\n path: \"/users/999\",\n wantStatus: http.StatusNotFound,\n },\n {\n name: \"create user\",\n method: http.MethodPost,\n path: \"/users\",\n body: \\`{\"name\":\"Bob\"}\\`,\n wantStatus: http.StatusCreated,\n },\n }\n\n handler := NewAPIHandler()\n\n for _, tt := range tests {\n t.Run(tt.name, func(t *testing.T) {\n var body io.Reader\n if tt.body != \"\" {\n body = strings.NewReader(tt.body)\n }\n\n req := httptest.NewRequest(tt.method, tt.path, body)\n req.Header.Set(\"Content-Type\", \"application/json\")\n w := httptest.NewRecorder()\n\n handler.ServeHTTP(w, req)\n\n if w.Code != tt.wantStatus {\n t.Errorf(\"got status %d; want %d\", w.Code, tt.wantStatus)\n }\n\n if tt.wantBody != \"\" && w.Body.String() != tt.wantBody {\n t.Errorf(\"got body %q; want %q\", w.Body.String(), tt.wantBody)\n }\n })\n }\n}\n\\`\\`\\`\n\n## Testing commands\n\n\\`\\`\\`bash\n# Run all tests\ngo test ./...\n\n# Run tests with verbose output\ngo test -v ./...\n\n# Run specific test\ngo test -run TestAdd ./...\n\n# Run tests matching pattern\ngo test -run \"TestUser/Create\" ./...\n\n# Run tests with race detector\ngo test -race ./...\n\n# Run tests with coverage\ngo test -cover -coverprofile=coverage.out ./...\n\n# Run short tests only\ngo test -short ./...\n\n# Run tests with timeout\ngo test -timeout 30s ./...\n\n# Run benchmarks\ngo test -bench=. -benchmem ./...\n\n# Run fuzzing\ngo test -fuzz=FuzzParse -fuzztime=30s ./...\n\n# Count test runs (for flaky test detection)\ngo test -count=10 ./...\n\\`\\`\\`\n\n## Best practices\n\n**DO:**\n- Write tests FIRST (TDD).\n- Use table-driven tests for comprehensive coverage.\n- Test behavior, not implementation.\n- Use \\`t.Helper()\\` in helper functions.\n- Use \\`t.Parallel()\\` for independent tests.\n- Clean up resources with \\`t.Cleanup()\\`.\n- Use meaningful test names that describe the scenario.\n\n**DON'T:**\n- Test private functions directly (test through the public API).\n- Use \\`time.Sleep()\\` in tests (use channels or conditions).\n- Ignore flaky tests (fix or remove them).\n- Mock everything (prefer integration tests when possible).\n- Skip error path testing.\n\n## Integration with CI/CD\n\n\\`\\`\\`yaml\n# GitHub Actions example\ntest:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-go@v5\n with:\n go-version: '1.22'\n\n - name: Run tests\n run: go test -race -coverprofile=coverage.out ./...\n\n - name: Check coverage\n run: |\n go tool cover -func=coverage.out | grep total | awk '{print $3}' | \\\n awk -F'%' '{if ($1 < 80) exit 1}'\n\\`\\`\\`\n\n**Remember**: Tests are documentation. They show how your code is meant to be used. Write them clearly and keep them up to date.\n",
|
|
55
|
+
"category": "testing"
|
|
19
56
|
}
|
|
20
57
|
],
|
|
21
58
|
"commands": [
|
|
@@ -23,7 +60,19 @@
|
|
|
23
60
|
"name": "go-review",
|
|
24
61
|
"description": "Run the Go reviewer over the current change against this pack's Go standards.",
|
|
25
62
|
"argument_hint": "[base ref]",
|
|
26
|
-
"prompt": "Invoke the go-reviewer subagent on the changed Go files (the diff since $ARGUMENTS, or the working tree if no ref is given). It checks this pack's conventions
|
|
63
|
+
"prompt": "Invoke the go-reviewer subagent on the changed Go files (the diff since $ARGUMENTS, or the working tree if no ref is given). It checks this pack's conventions -- consumer-defined interfaces, errors wrapped with %w and context, functional options, context threading, explicitly owned shared state, boundary validation, and fail-fast env-var secrets -- plus the security/concurrency/quality priorities in its CRITICAL/HIGH/MEDIUM rubric, and reports file:line + severity. Formatting itself (gofmt) is CI's job; don't re-litigate it. Fix any Critical or High findings before merging."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"name": "go-build",
|
|
67
|
+
"description": "Fix Go build errors, go vet warnings, and linter issues incrementally with minimal, surgical changes.",
|
|
68
|
+
"argument_hint": "(no arguments)",
|
|
69
|
+
"prompt": "Invoke the go-build-resolver subagent to incrementally fix Go build errors, go vet warnings, and linter issues with minimal, surgical changes. It runs go build/go vet/staticcheck/golangci-lint/go mod verify in order, fixes one error at a time, and re-verifies after each change. It will not refactor beyond the fix or change function signatures unless necessary, and stops to report if the same error persists after 3 attempts or the fix requires an architectural change. Run /go-review after the build is green."
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "go-test",
|
|
73
|
+
"description": "Enforce TDD workflow for Go: write table-driven tests first, then implement. Verify 80%+ coverage with go test -cover.",
|
|
74
|
+
"argument_hint": "<what to build>",
|
|
75
|
+
"prompt": "Invoke the go-reviewer subagent to drive a TDD session for $ARGUMENTS: scaffold the function/interface signature first, write comprehensive table-driven tests (RED), verify they fail for the right reason, implement the minimal code to pass (GREEN), then refactor while keeping tests green (REFACTOR). Use go test -race ./... and go test -cover ./... to verify; target 80%+ coverage on general code and 100% on critical business logic. Use /go-build first if the package doesn't compile yet."
|
|
27
76
|
}
|
|
28
77
|
],
|
|
29
78
|
"hooks": [
|
|
@@ -32,7 +81,14 @@
|
|
|
32
81
|
"matcher": "Edit|Write",
|
|
33
82
|
"description": "After an edit, check Go formatting with gofmt when it is installed.",
|
|
34
83
|
"action": "run-command",
|
|
35
|
-
"command": "if command -v gofmt >/dev/null 2>&1; then gofmt -l .; else echo \"gofmt not found
|
|
84
|
+
"command": "if command -v gofmt >/dev/null 2>&1; then gofmt -l .; else echo \"gofmt not found \u2014 install the Go toolchain to check formatting (go-rules)\"; fi"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"event": "PostToolUse",
|
|
88
|
+
"matcher": "Edit|Write",
|
|
89
|
+
"description": "After an edit in a Go module, run gofmt and go vet and flag any issues.",
|
|
90
|
+
"action": "run-command",
|
|
91
|
+
"command": "if [ -f go.mod ] && command -v go >/dev/null 2>&1; then gofmt -l . && go vet ./... || echo \"gofmt/vet issues (go-rules)\"; fi"
|
|
36
92
|
}
|
|
37
93
|
]
|
|
38
94
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: go-build-resolver
|
|
3
|
+
description: Go build, vet, and compilation error resolution specialist. Fixes build errors, go vet issues, and linter warnings with minimal, surgical changes. Use when Go builds fail.
|
|
4
|
+
tools: Read, Write, Edit, Bash, Grep, Glob
|
|
5
|
+
model: sonnet
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are an expert Go build error resolution specialist. Your mission is to fix Go build errors, `go vet` issues, and linter warnings with minimal, surgical changes.
|
|
9
|
+
|
|
10
|
+
## Core responsibilities
|
|
11
|
+
|
|
12
|
+
1. Diagnose Go compilation errors.
|
|
13
|
+
2. Fix `go vet` warnings.
|
|
14
|
+
3. Resolve `staticcheck`/`golangci-lint` issues.
|
|
15
|
+
4. Handle module dependency problems.
|
|
16
|
+
5. Fix type errors and interface mismatches.
|
|
17
|
+
|
|
18
|
+
## Diagnostic commands (run in order)
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
go build ./...
|
|
22
|
+
go vet ./...
|
|
23
|
+
staticcheck ./... 2>/dev/null || echo "staticcheck not installed"
|
|
24
|
+
golangci-lint run 2>/dev/null || echo "golangci-lint not installed"
|
|
25
|
+
go mod verify
|
|
26
|
+
go mod tidy -v
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Resolution workflow
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
1. go build ./... -> Parse error message
|
|
33
|
+
2. Read affected file -> Understand context
|
|
34
|
+
3. Apply minimal fix -> Only what's needed
|
|
35
|
+
4. go build ./... -> Verify fix
|
|
36
|
+
5. go vet ./... -> Check for warnings
|
|
37
|
+
6. go test ./... -> Ensure nothing broke
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Common fix patterns
|
|
41
|
+
|
|
42
|
+
| Error | Cause | Fix |
|
|
43
|
+
|-------|-------|-----|
|
|
44
|
+
| `undefined: X` | Missing import, typo, unexported | Add import or fix casing |
|
|
45
|
+
| `cannot use X as type Y` | Type mismatch, pointer/value | Type conversion or dereference |
|
|
46
|
+
| `X does not implement Y` | Missing method | Implement method with correct receiver |
|
|
47
|
+
| `import cycle not allowed` | Circular dependency | Extract shared types to new package |
|
|
48
|
+
| `cannot find package` | Missing dependency | `go get pkg@version` or `go mod tidy` |
|
|
49
|
+
| `missing return` | Incomplete control flow | Add return statement |
|
|
50
|
+
| `declared but not used` | Unused var/import | Remove or use blank identifier |
|
|
51
|
+
| `multiple-value in single-value context` | Unhandled return | `result, err := func()` |
|
|
52
|
+
| `cannot assign to struct field in map` | Map value mutation | Use pointer map or copy-modify-reassign |
|
|
53
|
+
| `invalid type assertion` | Assert on non-interface | Only assert from `interface{}` |
|
|
54
|
+
|
|
55
|
+
## Module troubleshooting
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
grep "replace" go.mod # Check local replaces
|
|
59
|
+
go mod why -m package # Why a version is selected
|
|
60
|
+
go get package@v1.2.3 # Pin specific version
|
|
61
|
+
go clean -modcache && go mod download # Fix checksum issues
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Key principles
|
|
65
|
+
|
|
66
|
+
- Surgical fixes only — don't refactor, just fix the error.
|
|
67
|
+
- Never add `//nolint` without explicit approval.
|
|
68
|
+
- Never change function signatures unless necessary.
|
|
69
|
+
- Always run `go mod tidy` after adding/removing imports.
|
|
70
|
+
- Fix root cause over suppressing symptoms.
|
|
71
|
+
|
|
72
|
+
## Stop conditions
|
|
73
|
+
|
|
74
|
+
Stop and report if:
|
|
75
|
+
- The same error persists after 3 fix attempts.
|
|
76
|
+
- A fix introduces more errors than it resolves.
|
|
77
|
+
- The error requires architectural changes beyond scope.
|
|
78
|
+
|
|
79
|
+
## Output format
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
[FIXED] internal/handler/user.go:42
|
|
83
|
+
Error: undefined: UserService
|
|
84
|
+
Fix: Added import "project/internal/service"
|
|
85
|
+
Remaining errors: 3
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Final line: `Build Status: SUCCESS/FAILED | Errors Fixed: N | Files Modified: list`
|
|
89
|
+
|
|
90
|
+
For detailed Go error patterns and code examples, see skill `golang-patterns`.
|
|
@@ -1,8 +1,78 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: go-reviewer
|
|
3
|
-
description:
|
|
3
|
+
description: Expert Go code reviewer specializing in idiomatic Go, concurrency patterns, error handling, and security. Reports file:line + severity across CRITICAL/HIGH/MEDIUM categories. Use for all Go code changes.
|
|
4
4
|
tools: Read, Grep, Glob, Bash
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
You are a
|
|
8
|
+
You are a senior Go code reviewer ensuring high standards of idiomatic Go and best practices.
|
|
9
|
+
|
|
10
|
+
When invoked:
|
|
11
|
+
1. Run `git diff -- '*.go'` to see recent Go file changes.
|
|
12
|
+
2. Run `go vet ./...` and `staticcheck ./...` if available.
|
|
13
|
+
3. Focus on modified `.go` files.
|
|
14
|
+
4. Begin review immediately.
|
|
15
|
+
|
|
16
|
+
## Review priorities
|
|
17
|
+
|
|
18
|
+
### CRITICAL — Security
|
|
19
|
+
- SQL injection: string concatenation in `database/sql` queries.
|
|
20
|
+
- Command injection: unvalidated input in `os/exec`.
|
|
21
|
+
- Path traversal: user-controlled file paths without `filepath.Clean` + prefix check.
|
|
22
|
+
- Race conditions: shared state without synchronization.
|
|
23
|
+
- `unsafe` package use without justification.
|
|
24
|
+
- Hardcoded secrets: API keys, passwords in source.
|
|
25
|
+
- Insecure TLS: `InsecureSkipVerify: true`.
|
|
26
|
+
|
|
27
|
+
### CRITICAL — Error handling
|
|
28
|
+
- Ignored errors: using `_` to discard errors.
|
|
29
|
+
- Missing error wrapping: `return err` without `fmt.Errorf("context: %w", err)`.
|
|
30
|
+
- Panic for recoverable errors: use error returns instead.
|
|
31
|
+
- Missing `errors.Is`/`errors.As`: use `errors.Is(err, target)` not `err == target`.
|
|
32
|
+
|
|
33
|
+
### HIGH — Concurrency
|
|
34
|
+
- Goroutine leaks: no cancellation mechanism (use `context.Context`).
|
|
35
|
+
- Unbuffered channel deadlock: sending without a guaranteed receiver.
|
|
36
|
+
- Missing `sync.WaitGroup`/errgroup: goroutines without coordination.
|
|
37
|
+
- Mutex misuse: not using `defer mu.Unlock()`.
|
|
38
|
+
|
|
39
|
+
### HIGH — Code quality
|
|
40
|
+
- Large functions: over 50 lines.
|
|
41
|
+
- Deep nesting: more than 4 levels.
|
|
42
|
+
- Non-idiomatic: `if/else` instead of early return.
|
|
43
|
+
- Package-level variables: mutable global state.
|
|
44
|
+
- Interface pollution: defining unused abstractions.
|
|
45
|
+
|
|
46
|
+
### MEDIUM — Performance
|
|
47
|
+
- String concatenation in loops: use `strings.Builder`.
|
|
48
|
+
- Missing slice pre-allocation: `make([]T, 0, cap)`.
|
|
49
|
+
- N+1 queries: database queries in loops.
|
|
50
|
+
- Unnecessary allocations: objects in hot paths.
|
|
51
|
+
|
|
52
|
+
### MEDIUM — Best practices
|
|
53
|
+
- Context first: `ctx context.Context` should be the first parameter.
|
|
54
|
+
- Table-driven tests: tests should use the table-driven pattern.
|
|
55
|
+
- Error messages: lowercase, no punctuation.
|
|
56
|
+
- Package naming: short, lowercase, no underscores.
|
|
57
|
+
- Deferred call in a loop: resource accumulation risk.
|
|
58
|
+
|
|
59
|
+
## Diagnostic commands
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
go vet ./...
|
|
63
|
+
staticcheck ./...
|
|
64
|
+
golangci-lint run
|
|
65
|
+
go build -race ./...
|
|
66
|
+
go test -race ./...
|
|
67
|
+
govulncheck ./...
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Approval criteria
|
|
71
|
+
|
|
72
|
+
- Approve: no CRITICAL or HIGH issues.
|
|
73
|
+
- Warning: MEDIUM issues only.
|
|
74
|
+
- Block: CRITICAL or HIGH issues found.
|
|
75
|
+
|
|
76
|
+
Cite the exact file:line for each finding, name the concrete failure mode, and give severity. Formatting itself (gofmt) is the linter/CI's job — don't re-litigate it. If the change is sound, say so plainly rather than inventing findings.
|
|
77
|
+
|
|
78
|
+
For detailed Go code examples and anti-patterns, see skill `golang-patterns`.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Fix Go build errors, go vet warnings, and linter issues incrementally with minimal, surgical changes.
|
|
3
|
+
argument-hint: (no arguments)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Invoke the go-build-resolver subagent to incrementally fix Go build errors, go vet warnings, and linter issues with minimal, surgical changes. It runs go build/go vet/staticcheck/golangci-lint/go mod verify in order, fixes one error at a time, and re-verifies after each change. It will not refactor beyond the fix or change function signatures unless necessary, and stops to report if the same error persists after 3 attempts or the fix requires an architectural change. Run /go-review after the build is green.
|
|
@@ -3,4 +3,4 @@ description: Run the Go reviewer over the current change against this pack's Go
|
|
|
3
3
|
argument-hint: [base ref]
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Invoke the go-reviewer subagent on the changed Go files (the diff since $ARGUMENTS, or the working tree if no ref is given). It checks this pack's conventions
|
|
6
|
+
Invoke the go-reviewer subagent on the changed Go files (the diff since $ARGUMENTS, or the working tree if no ref is given). It checks this pack's conventions -- consumer-defined interfaces, errors wrapped with %w and context, functional options, context threading, explicitly owned shared state, boundary validation, and fail-fast env-var secrets -- plus the security/concurrency/quality priorities in its CRITICAL/HIGH/MEDIUM rubric, and reports file:line + severity. Formatting itself (gofmt) is CI's job; don't re-litigate it. Fix any Critical or High findings before merging.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Enforce TDD workflow for Go: write table-driven tests first, then implement. Verify 80%+ coverage with go test -cover.
|
|
3
|
+
argument-hint: <what to build>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Invoke the go-reviewer subagent to drive a TDD session for $ARGUMENTS: scaffold the function/interface signature first, write comprehensive table-driven tests (RED), verify they fail for the right reason, implement the minimal code to pass (GREEN), then refactor while keeping tests green (REFACTOR). Use go test -race ./... and go test -cover ./... to verify; target 80%+ coverage on general code and 100% on critical business logic. Use /go-build first if the package doesn't compile yet.
|
|
@@ -9,6 +9,15 @@
|
|
|
9
9
|
"command": "if command -v gofmt >/dev/null 2>&1; then gofmt -l .; else echo \"gofmt not found — install the Go toolchain to check formatting (go-rules)\"; fi"
|
|
10
10
|
}
|
|
11
11
|
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"matcher": "Edit|Write",
|
|
15
|
+
"hooks": [
|
|
16
|
+
{
|
|
17
|
+
"type": "command",
|
|
18
|
+
"command": "if [ -f go.mod ] && command -v go >/dev/null 2>&1; then gofmt -l . && go vet ./... || echo \"gofmt/vet issues (go-rules)\"; fi"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
12
21
|
}
|
|
13
22
|
]
|
|
14
23
|
}
|