Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "go-coding",
"version": "0.2.1",
"version": "0.3.0",
"description": "Idiomatic Go coding standards for AI assistants — formatting, errors, concurrency, testing, layout.",
"author": {
"name": "Cadasto B.V.",
Expand Down
2 changes: 1 addition & 1 deletion .cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "go-coding",
"version": "0.2.1",
"version": "0.3.0",
"description": "Idiomatic Go coding standards for AI assistants — formatting, errors, concurrency, testing, layout.",
"author": {
"name": "Cadasto B.V.",
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This file provides guidance to AI coding assistants (Claude Code, Cursor, and co

The **Go Coding Plugin** is an AI plugin by Cadasto B.V. that teaches AI coding assistants **idiomatic Go coding standards** — formatting, naming, error handling, concurrency, testing, and project layout — through skills, commands, agents, hooks, and Cursor rules. It targets **both Claude Code and Cursor** from a single shared component set.

> **Current status — v0.2.0.** A complete dual-host (Claude Code + Cursor) Go-standards set that validates clean (`./scripts/validate.sh` + `claude plugin validate .`): the auto-invoked `go-coding` **router** skill; the focused standards skills `go-errors`, `go-concurrency`, `go-testing`, `go-idioms`, `go-linting`, `go-layout`; the read-only `go-reviewer` agent; the user-invoked `/go-explain` and `/go-lint-setup` skills; a shipped `references/golangci.v2.yml`; the `rules/go-context.mdc` Cursor rule; and host-agnostic `session-start` + `format-on-save` hooks. Do not assume a file is present because it is documented here — check first.
> **Current status — v0.3.0.** A complete dual-host (Claude Code + Cursor) Go-standards set, baselined on **Go 1.26** (1.26.4+) with version-gated guidance that still holds for 1.25 modules, that validates clean (`./scripts/validate.sh` + `claude plugin validate .`): the auto-invoked `go-coding` **router** skill; the focused standards skills `go-errors`, `go-concurrency`, `go-testing`, `go-idioms`, `go-linting`, `go-layout`; the read-only `go-reviewer` agent; the user-invoked `/go-explain` and `/go-lint-setup` skills; a shipped `references/golangci.v2.yml`; the `rules/go-context.mdc` Cursor rule; and host-agnostic `session-start` + `format-on-save` hooks. Do not assume a file is present because it is documented here — check first.

## Domain Context

Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
- Keep a Changelog: https://keepachangelog.com/en/1.1.0/
- Semantic Versioning: https://semver.org/spec/v2.0.0.html

## [0.3.0] - 2026-07-01

Retargets the standards baseline to **Go 1.26** (1.26.4+) while keeping version-gated guidance valid for 1.25 modules.

### Added
- Skills: `go-idioms` — `new(expr)` row (Go 1.26 pointer/optional-field initialization) and a note on self-referencing generic type parameters.
- Skills: `go-testing` — `testing/cryptotest.SetGlobalRandom` for deterministic crypto tests (Go 1.26); note that the pre-1.25 `GOEXPERIMENT=synctest` (`synctest.Run`) API was removed in Go 1.26.
- Skills: `go-concurrency` — experimental `goroutineleak` profile in `runtime/pprof` (Go 1.26) as a toolchain-native complement to `goleak`.

### Changed
- Skills / agent / Cursor rule: standards baseline moved from Go 1.25 to **Go 1.26** in `go-coding`, `go-idioms`, `go-linting`, `go-explain`, the `go-reviewer` agent, and `rules/go-context.mdc`; all keep working against 1.25 modules via the `Since`/`go.mod` version-gating already in `go-idioms`.
- Skills: `go-idioms`, `go-linting`, `go-coding`, `rules/go-context.mdc` — frame `go fix ./...` as the canonical modernizer runner on Go 1.26 (rewritten atop the analysis framework), with `golangci-lint --enable-only=modernize` for CI reproducibility and older toolchains.
- Docs: `docs/install.md` and `README.md` — minimum host toolchain raised to **Go 1.26.x** (1.26.4+); tarball example updated to `go1.26.4`; `gopls` pin moved to `v0.22.x` (the line that adds Go 1.26 support).
- Manifests: `version` → `0.3.0` in both `.claude-plugin/plugin.json` and `.cursor-plugin/plugin.json`.

## [0.2.1] - 2026-06-18

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Or from a local working copy: `claude plugin add /path/to/go-coding-plugin`.

**Cursor**: add this repository as a plugin (Settings → Plugins). See [`docs/install.md`](docs/install.md) for both hosts.

**Prerequisites** — the plugin installs without a Go toolchain, but its hooks and enforcement guidance expect **Go 1.25.x** plus `gofmt`, `gofumpt`, `goimports`, and `gopls` on the host `PATH`. See [Host toolchain (minimal requirements)](docs/install.md#host-toolchain-minimal-requirements) for what each tool drives and copy-paste install commands.
**Prerequisites** — the plugin installs without a Go toolchain, but its hooks and enforcement guidance expect **Go 1.26.x** (minimum 1.26.4; still works against 1.25 modules) plus `gofmt`, `gofumpt`, `goimports`, and `gopls` on the host `PATH`. See [Host toolchain (minimal requirements)](docs/install.md#host-toolchain-minimal-requirements) for what each tool drives and copy-paste install commands.

## Component surface

Expand Down
7 changes: 4 additions & 3 deletions agents/go-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ tools:
- Bash
---

You are **go-reviewer**, a reviewer of idiomatic, correct Go (Go 1.25, golangci-lint v2). You supply
You are **go-reviewer**, a reviewer of idiomatic, correct Go (Go 1.26, works with 1.25+; golangci-lint v2). You supply
the judgment a linter cannot — the bugs and smells that survive `gofmt`, `go vet`, and
`golangci-lint`. You are **read-only**: you report findings, you never edit code.

Expand Down Expand Up @@ -94,8 +94,9 @@ the judgment a linter cannot — the bugs and smells that survive `gofmt`, `go v
non-atomic reads of those fields); `sync.Mutex`/`WaitGroup` copied by value; a map written
concurrently without a lock; check-then-act races.
- **Stale modernization debt** — code `modernize`/`go fix` would rewrite (range-int, `min`/`max`,
`slices`/`maps`, `strings.Cut`, `cmp.Or`, pre-1.22 loop-var copies). Low severity; point at
`golangci-lint run --enable-only=modernize`.
`slices`/`maps`, `strings.Cut`, `cmp.Or`, pre-1.22 loop-var copies; on Go 1.26 modules also
pointer-helper temps that `new(expr)` replaces). Low severity; point at `go fix ./...` (Go 1.26) or
`golangci-lint run --enable-only=modernize`. Gate suggestions on the module's `go.mod` version.
- **slog hot-path waste** — building a per-call logger instead of `logger.With(...)`; formatting or
allocating before a level check; key-value variadic on a hot path instead of `slog.LogAttrs`.

Expand Down
26 changes: 13 additions & 13 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,51 +41,51 @@ Add this repository as a plugin (Cursor **Settings → Plugins**, via Git URL or

## Host toolchain (minimal requirements)

Installing the plugin itself needs no Go toolchain — it is pure Markdown + JSON. But its **enforcement** layer only delivers value when the standard Go tools are on the host `PATH`: the `format-on-save` hook shells out to a formatter, the golangci-lint v2 reference config lists `gofumpt`/`goimports` as formatters, and the recommended official `gopls-lsp` plugin (`@claude-plugins-official`) drives `gopls`. The plugin targets **Go 1.25** + golangci-lint v2.
Installing the plugin itself needs no Go toolchain — it is pure Markdown + JSON. But its **enforcement** layer only delivers value when the standard Go tools are on the host `PATH`: the `format-on-save` hook shells out to a formatter, the golangci-lint v2 reference config lists `gofumpt`/`goimports` as formatters, and the recommended official `gopls-lsp` plugin (`@claude-plugins-official`) drives `gopls`. The plugin targets **Go 1.26** + golangci-lint v2, and its version-gated idiom guidance still works against 1.25 modules.

At minimum the host should provide:

| Tool | Provided by | Used for | If missing |
|------|-------------|----------|------------|
| **Go 1.25.x** | [go.dev/dl](https://go.dev/dl/) / package manager | everything; satisfies `go.mod` `go 1.25.x` | no toolchain at all |
| **Go 1.26.x** (min 1.26.4) | [go.dev/dl](https://go.dev/dl/) / package manager | everything; satisfies `go.mod` `go 1.26.x` (and 1.25 modules); `go fix ./...` runs the modernizers | no toolchain at all |
| **`gofmt`** | the Go distribution | `format-on-save.sh` fallback (`gofmt -w -s`) | n/a — always ships with Go |
| **`gofumpt`** | `go install` | `format-on-save.sh` primary (`gofumpt -w`), stricter gofmt superset | hook degrades to `gofmt` |
| **`goimports`** | `go install` | `goimports` formatter in the golangci-lint v2 config (import grouping/pruning) | import-group formatting skipped |
| **`gopls`** (v0.21.1) | `go install` | the `gopls-lsp` plugin (defs/refs/diagnostics/rename/vulncheck) | no code intelligence |
| **`gopls`** (v0.22.x) | `go install` | the `gopls-lsp` plugin (defs/refs/diagnostics/rename/vulncheck) | no code intelligence |

### Install / upgrade Go (official tarball, Linux)

Pick the latest **1.25.x** patch from <https://go.dev/dl/> and the build matching your platform (`linux-amd64` shown):
Pick the latest **1.26.x** patch (1.26.4 or newer) from <https://go.dev/dl/> and the build matching your platform (`linux-amd64` shown):

```bash
# replace the version with the current latest 1.25.x patch
curl -fLO https://go.dev/dl/go1.25.11.linux-amd64.tar.gz
# replace the version with the current latest 1.26.x patch (1.26.4+)
curl -fLO https://go.dev/dl/go1.26.4.linux-amd64.tar.gz
sudo rm -rf /usr/local/go # remove any prior install (don't overlay)
sudo tar -C /usr/local -xzf go1.25.11.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.26.4.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin # add to your shell profile if not already present
go version # → go version go1.25.11 linux/amd64
go version # → go version go1.26.4 linux/amd64
```

> macOS/Windows or a package manager (Homebrew `go`, `winget`, distro packages) work equally well — the only requirement is that `go version` reports **1.25.x**. `gofmt` is included in every Go distribution, so nothing extra is needed for the hook's fallback path.
> macOS/Windows or a package manager (Homebrew `go`, `winget`, distro packages) work equally well — the only requirement is that `go version` reports **1.26.x** (1.26.4+). `gofmt` is included in every Go distribution, so nothing extra is needed for the hook's fallback path.

### Install the supporting tools

`go install` drops binaries in `$(go env GOPATH)/bin` (default `~/go/bin`) — make sure that directory is on your `PATH`. Run these **after** Go is in place so they compile against your 1.25 toolchain:
`go install` drops binaries in `$(go env GOPATH)/bin` (default `~/go/bin`) — make sure that directory is on your `PATH`. Run these **after** Go is in place so they compile against your 1.26 toolchain:

```bash
go install mvdan.cc/gofumpt@latest # stricter gofmt superset (hook primary)
go install golang.org/x/tools/cmd/goimports@latest # import grouping / pruning
go install golang.org/x/tools/gopls@v0.21.1 # language server for the gopls-lsp plugin (pinned: v0.21.1, verified against Go 1.25.x)
go install golang.org/x/tools/gopls@v0.22.0 # language server for the gopls-lsp plugin (pinned: v0.22.x — the gopls line that adds Go 1.26 support; use @latest for the newest patch)
```

Verify:

```bash
go version # → 1.25.x
go version # → 1.26.x (1.26.4+)
command -v gofmt # ships with Go (in GOROOT/bin)
gofumpt --version
command -v goimports # goimports has no --version flag
gopls version # → golang.org/x/tools/gopls v0.21.1
gopls version # → golang.org/x/tools/gopls v0.22.x
```

These are **host-only** dev tools; the plugin still works without them (the format hook degrades to `gofmt`, then to a silent no-op). Full-tree `golangci-lint` runs separately — often in a pinned container — so it does not depend on these host binaries.
Expand Down
6 changes: 3 additions & 3 deletions rules/go-context.mdc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
description: Go coding standards — idiomatic Go (Go 1.25, golangci-lint v2) for .go files. Mirrors the go-coding router skill for Cursor.
description: Go coding standards — idiomatic Go (Go 1.26, works with 1.25+; golangci-lint v2) for .go files. Mirrors the go-coding router skill for Cursor.
globs: ["**/*.go"]
alwaysApply: false
---
Expand All @@ -17,10 +17,10 @@ This Cursor rule mirrors the `go-coding` router skill — apply it when editing
|---|---|---|
| Formatting | `gofmt`/`gofumpt` (+ `goimports`) — machine-enforced | — |
| Static analysis / bugs | `go vet ./...`, `golangci-lint run` | `go-linting` |
| Modern idioms | `golangci-lint run --enable-only=modernize`, `go fix ./...` | `go-idioms` |
| Modern idioms | `go fix ./...` (Go 1.26 runs the modernizers natively), or `golangci-lint run --enable-only=modernize` | `go-idioms` |
| Errors | `golangci-lint run --enable-only=errorlint` | `go-errors` |
| Concurrency | `go test -race ./...`, `go vet ./...` | `go-concurrency` |
| Testing | `go test -race ./...`; `testing/synctest` (1.25) for time/concurrency | `go-testing` |
| Testing | `go test -race ./...`; `testing/synctest` (since 1.25) for time/concurrency | `go-testing` |
| Layout | judgment | `go-layout` |

Ground every judgment call in a cited source — Effective Go, Go Code Review Comments, the Google
Expand Down
6 changes: 3 additions & 3 deletions skills/go-coding/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: go-coding
description: Go coding-standards router and entry point for idiomatic Go (Go 1.25, golangci-lint v2). This skill should be used when a Go task spans multiple areas, is unspecified, or the question is which tool or standard applies — it routes each topic to the deterministic tool (gofmt/gofumpt, go vet, golangci-lint v2 + modernize, go test -race), to the gopls-lsp plugin for code intelligence, and then to the focused go-* skill that owns it. For a single, already-identified topic prefer that skill directly (errors → go-errors, concurrency → go-concurrency, testing → go-testing, idioms/modernization → go-idioms, linter config → go-linting, layout → go-layout). Not for non-Go languages or domain/business rules.
description: Go coding-standards router and entry point for idiomatic Go (Go 1.26, works with 1.25+; golangci-lint v2). This skill should be used when a Go task spans multiple areas, is unspecified, or the question is which tool or standard applies — it routes each topic to the deterministic tool (gofmt/gofumpt, go vet, go fix / golangci-lint v2 modernize, go test -race), to the gopls-lsp plugin for code intelligence, and then to the focused go-* skill that owns it. For a single, already-identified topic prefer that skill directly (errors → go-errors, concurrency → go-concurrency, testing → go-testing, idioms/modernization → go-idioms, linter config → go-linting, layout → go-layout). Not for non-Go languages or domain/business rules.
---

# go-coding — Go standards router
Expand All @@ -19,10 +19,10 @@ Two principles from the project research drive it:
|---|---|---|
| Formatting | `gofmt -l` / `gofumpt -l` (+ `goimports`) — machine-enforced, non-negotiable | — |
| Static analysis / likely bugs | `go vet ./...`, `golangci-lint run` | `go-linting` |
| Modern idioms (range-int, `min`/`max`, `slices`/`maps`, `wg.Go`, `strings.Cut`) | `golangci-lint run --enable-only=modernize`, or `go fix ./...` | `go-idioms` |
| Modern idioms (range-int, `min`/`max`, `slices`/`maps`, `wg.Go`, `strings.Cut`, `new(expr)`) | `go fix ./...` (Go 1.26 applies the full modernizer suite natively), or `golangci-lint run --enable-only=modernize` on older toolchains | `go-idioms` |
| Errors (`%w`, `errors.Is`/`As`, `errors.Join`, sentinel/typed) | `golangci-lint run --enable-only=errorlint` | `go-errors` |
| Concurrency (goroutine leaks, ctx lifecycle, atomics) | `go test -race ./...`, `go vet ./...` | `go-concurrency` |
| Testing (table-driven, `t.Parallel`, `B.Loop`, `testing/synctest`) | `go test -race ./...`; use `testing/synctest` (stable in 1.25) for time/concurrency tests | `go-testing` |
| Testing (table-driven, `t.Parallel`, `B.Loop`, `testing/synctest`) | `go test -race ./...`; use `testing/synctest` (stable since 1.25) for time/concurrency tests | `go-testing` |
| Project layout (`internal/`, start-flat) | judgment — see sources below | `go-layout` |
| Code intelligence (defs/refs/diagnostics/rename/vulncheck) | install the **`gopls-lsp`** plugin | — |

Expand Down
8 changes: 6 additions & 2 deletions skills/go-concurrency/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ description: Idiomatic, leak-free Go concurrency. This skill should be used when

Deterministic backstop: `go test -race ./...`, `go vet ./...` (catches copylocks, lost cancel),
and `go.uber.org/goleak`. The race detector is the source of truth — run it before reasoning.
On **Go 1.26+** the runtime also ships an experimental `goroutineleak` profile in `runtime/pprof`
that reports leaked goroutines — a toolchain-native complement to `goleak` for leak hunts (enable it
with `GOEXPERIMENT=goroutineleakprofile` at build time).

## Rules

Expand All @@ -28,11 +31,12 @@ and `go.uber.org/goleak`. The race detector is the source of truth — run it be
a concurrent map write panics; `-race` catches it.
- **Channels:** close on the *send* side, never the receive side; a `nil` channel blocks forever
(useful for disabling a `select` arm, a bug everywhere else).
- **Testing time/concurrency:** use **`testing/synctest`** (stable in Go 1.25) — fake clock +
- **Testing time/concurrency:** use **`testing/synctest`** (stable since Go 1.25) — fake clock +
deterministic scheduling. See `go-testing`.

## Sources
- synctest — <https://go.dev/blog/synctest>; Go 1.25 release notes — <https://go.dev/doc/go1.25>
- synctest — <https://go.dev/blog/synctest>; Go 1.25/1.26 release notes — <https://go.dev/doc/go1.26>
- `goroutineleak` profile (Go 1.26, experimental) — <https://pkg.go.dev/runtime/pprof>
- Code Review Comments (Goroutine Lifetimes, Contexts) — <https://go.dev/wiki/CodeReviewComments>
- Uber Go Style Guide (Concurrency) — <https://github.com/uber-go/guide>

Expand Down
5 changes: 3 additions & 2 deletions skills/go-explain/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ Cover, in a few lines:
4. **Source** — cite one authoritative reference: Effective Go, Go Code Review Comments, the Google
or Uber Go style guide, a `go.dev/blog` post, or `pkg.go.dev`.

Tailor to the repo's Go version when a `go.mod` is present — don't recommend a Go 1.25 idiom for an
older module. For a fuller treatment, route to the matching skill: `go-errors`, `go-concurrency`,
Tailor to the repo's Go version when a `go.mod` is present — don't recommend a Go 1.26 idiom (e.g.
`new(expr)`) for a module pinned to 1.25 or older. For a fuller treatment, route to the matching
skill: `go-errors`, `go-concurrency`,
`go-testing`, `go-idioms`, `go-linting`, or `go-layout`.

Keep it tight — this is a lookup, not a lecture. If `$ARGUMENTS` is empty, ask what to explain.
Loading
Loading