go-clean-architecture
Use when scaffolding or refactoring a Go service into a framework-agnostic clean (hexagonal) architecture: Domain, Usecase,…
Use when setting up linting for a Go project, configuring golangci-lint, picking a linter set, suppressing findings with //nolint, or wiring lint checks into CI. Apply proactively whenever a project lacks .golangci.yml, when lint output is unclear, or when a new package needs
Claude Code
$ npx -y skills add muratmirgun/gophers --skill go-linting --agent claude-code
Codex
$ npx skills add muratmirgun/gophers --skill go-linting -a codex -y
Cursor
$ npx skills add muratmirgun/gophers --skill go-linting -a cursor -y
Gemini CLI
$ npx skills add muratmirgun/gophers --skill go-linting -a gemini-cli -y
GitHub Copilot
$ npx skills add muratmirgun/gophers --skill go-linting -a github-copilot -y
OpenCode
$ npx skills add muratmirgun/gophers --skill go-linting -a opencode -y
Windsurf
$ npx skills add muratmirgun/gophers --skill go-linting -a windsurf -y
name: go-linting description: "Use when setting up linting for a Go project, configuring golangci-lint, picking a linter set, suppressing findings with //nolint, or wiring lint checks into CI. Apply proactively whenever a project lacks .golangci.yml, when lint output is unclear, or when a new package needs the project's quality bar. Does not cover code review process (see go-code-review)." license: MIT compatibility: "Designed for Claude Code or similar AI coding agents. Targets golangci-lint v2.x and Go 1.21+. Some commands (golangci-lint fmt) require v2." allowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)
The single most important property of a linting setup is **consistency**: every contributor and every CI run uses the same rules. `golangci-lint` is the tool; a checked-in `.golangci.yml` is the contract.
1. **Every Go project has a `.golangci.yml`** at the repository root. It is the source of truth for which linters run. 2. **Lint runs in CI on every PR.** A green build means lint is green. 3. **Lint runs locally before commit.** A pre-commit hook or `make lint` keeps the feedback loop fast. 4. **Suppress with reasons.** `//nolint:linter // why` — never bare `//nolint`. 5. **Fix the cause first.** A suppression should be the last resort, not the default reaction. 6. **Never silence security linters** (`gosec`, `bodyclose`, `sqlclosecheck`) without a strong, documented reason.
1. Install: `go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest` (or `brew install golangci-lint`). 2. Drop a baseline [`.golangci.yml`](assets/.golangci.yml) at the repo root. 3. Run `golangci-lint run ./...`. 4. Fix the findings in order — formatting first, `govet` next, style last. 5. Re-run until clean. Commit `.golangci.yml` and any source fixes together. 6. Add the CI workflow (see [references/ci-integration.md](references/ci-integration.md)).
These five catch the most common issues and have the lowest noise rate. Start here:
| Linter | Catches | |---|---| | `errcheck` | Unchecked error returns | | `govet` | Mistakes that compile but are wrong (printf args, shifts, etc.) | | `staticcheck` | Bug-prone patterns, dead code, simplifications | | `ineffassign` | Assignments whose value is never read | | `revive` | Style issues (modern replacement for `golint`) |
Add formatting on top: `gofmt` / `goimports` (or `gofumpt` for stricter rules). With golangci-lint v2 these run via `golangci-lint fmt`.
Enable these once the baseline is clean:
| Linter | When to enable | |---|---| | `gosec` | Any service that handles untrusted input | | `bodyclose` | Any code that calls `http.Client.Do` | | `sqlclosecheck` | Any code using `database/sql` | | `nilerr` | Any code that does `if err != nil { return nil }` style returns | | `misspell` | Always — comments and strings | | `unconvert` | Always — flags useless type conversions | | `nolintlint` | Always — enforces the `//nolint` rules below | | `paralleltest` | If most tests can use `t.Parallel()` | | `thelper` | If you write test helpers (enforces `t.Helper()`) | | `testifylint` | If the project uses `testify` | | `gocyclo` / `gocognit` | When you want a complexity ceiling | | `exhaustive` | When you use `iota`-based enums and want full `switch` coverage |
> Read [references/linter-catalog.md](references/linter-catalog.md) when picking from the long tail of correctness, style, security, and complexity linters, or when deciding which ones to enable on legacy code.
lint: golangci-lint run ./... lint-fix: golangci-lint run --fix ./... fmt: golangci-lint fmt ./... ci-lint: golangci-lint run --new-from-rev=origin/main ./...
| Task | Command | |---|---| | Run all enabled linters | `golangci-lint run ./...` | | Auto-fix everything fixable | `golangci-lint run --fix ./...` | | Format the tree (v2+) | `golangci-lint fmt ./...` | | Lint only changed code | `golangci-lint run --new-from-rev=origin/main ./...` | | Run one linter | `golangci-lint run --enable-only=govet ./...` | | Show which linters exist | `golangci-lint linters` |
`--new-from-rev` is what makes incremental adoption work: legacy code stays untouched, new and changed code must meet the bar.
> Read [references/ci-integration.md](references/ci-integration.md) when wiring GitHub Actions, pre-commit hooks, or selective linting on PRs.
// Good: specific linter + a reason //nolint:errcheck // fire-and-forget; Sync error is not actionable on shutdown _ = logger.Sync()
// Bad: blanket, no reason — nolintlint will flag this //nolint _ = logger.Sync()
Rules (enforced by `nolintlint`):
> Read [references/nolint-directives.md](references/nolint-directives.md) when deciding between inline, block, and file-scope suppressions, or when reviewing existing `//nolint` for stale rationale.
Each finding looks like:
path/to/file.go:42:10: message describing the issue (linter-name)
The linter name in parentheses is the key — look it up in the catalog to see what it actually checks, then either fix the code or suppress with a reason that names the same linter.
| Anti-pattern | Why it hurts | Do this instead | |---|---|---| | No `.golangci.yml` in the repo | Each contributor lints differently or not at all | Commit a baseline config; CI enforces it | | `//nolint` with no linter name | Disables every check on that line, silently | `//nolint:errcheck // reason` | | `//nolint:all` at the top of a file | Whole file escapes review | Suppress per construct with a reason | | Lint
26 production-grade Go skills for Claude Code, Gemini CLI, and opencode. Battle-tested patterns from the Go community — codified as triggerable AI skills.
Use when scaffolding or refactoring a Go service into a framework-agnostic clean (hexagonal) architecture: Domain, Usecase,…
Invoke this skill to systematically review a Go change against community style standards before merging. Walks the diff topic by…
Use when writing or reviewing Go code for clarity, formatting, control flow, variable declarations, switch usage, and function…
Use when writing or reviewing concurrent Go code — goroutines, channels, select, mutexes, atomics, errgroup, singleflight, worker…
Use when designing, propagating, or debugging context.Context flow in Go — first-parameter placement, deadlines and cancellation,…
Use when writing conditionals, loops, switches, type switches, or blank-identifier patterns in Go. Covers if-with-initialization,…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.