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
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ This plugin encodes **Go (golang) coding standards**. Guidance must be grounded

When a recommendation derives from one of the above, attribute it explicitly and distinguish cited rules from inference.

**Advice == tooling.** Every linter taught in a component must be enabled in `references/golangci.v2.yml`. Analyzers a skill teaches as opt-in (`shadow` in `go-idioms`) are the deliberate exception: each is taught with the config line that switches it on, and is not added to the reference config at a refresh. The `go-idioms` Fixer column must match `go tool fix help` on the floor-minor toolchain. The Google readability tie-break sentence must read identically in the `go-coding` router, `rules/go-context.mdc`, and `go-reviewer`. `scripts/validate.py` enforces all three.
**Advice == tooling.** Every linter taught in a component must be enabled in `references/golangci.v2.yml`, and the `/go-lint-setup` scaffold block must enable the same set. Analyzers a skill teaches as opt-in (`shadow` in `go-idioms`) are the deliberate exception: each is taught with the config line that switches it on, and is not added to the reference config at a refresh. The `go-idioms` Fixer column must match `go tool fix help` on the floor-minor toolchain. The Google readability tie-break sentence must read identically in the `go-coding` router, `rules/go-context.mdc`, and `go-reviewer`. `scripts/validate.py` enforces all three.

**Refreshing the skills against current Go practice** (e.g. "check current Go best practices and update the skills"): follow the **source registry and procedure** in [docs/authoring.md](docs/authoring.md#refreshing-the-standards-baseline-source-registry). It lists every source to re-read, in order, plus the version-gating rules. Re-read them; never refresh from memory. Two recurring traps: the *released* Go version is not whatever `go.dev/doc/go1.NN` renders (check the release history), and the stdlib APIs a skill is missing are usually the ones that landed *after* it was written.

Expand All @@ -51,8 +51,8 @@ Shared assets (skills, agents) are consumed by both hosts; host-specific manifes
- **Cursor hooks**: `hooks/cursor-hooks.json`, object `{ "hooks": { "sessionStart": [...], "afterFileEdit": [...] } }`; the command runs from the plugin root (a **workspace-relative** path, **not** `${CLAUDE_PLUGIN_ROOT}`). Wires `session-start.sh` (`sessionStart`) and `format-on-save.sh` + `skill-nudge.sh` (`afterFileEdit`).
- **Shared hook scripts** (host-agnostic, so either manifest can invoke them; all exit 0 always):
- `hooks/session-start.sh`: detects `go.mod` / `*.go` and prints one Go-standards banner line naming the components.
- `hooks/format-on-save.sh`: after a `*.go` Write/Edit, runs `gofumpt -w` (or `gofmt -w -s`) on that single file; resolves the path from `$CLAUDE_FILE_PATH` or the stdin tool-payload JSON; silent no-op if no formatter is installed.
- `hooks/skill-nudge.sh`: after a `*.go` Write/Edit, names ONE matching go-coding skill for that edit, once per skill per session; a hook `systemMessage` under Claude Code, a plain line under Cursor.
- `hooks/format-on-save.sh`: after a `*.go` Write/Edit, runs `gofumpt -w` (or `gofmt -w -s`) on that single file; resolves the path from the stdin tool-payload JSON (`$CLAUDE_FILE_PATH` overrides it for tests); silent no-op if no formatter is installed.
- `hooks/skill-nudge.sh`: after a `*.go` Write/Edit, names ONE matching go-coding skill for that edit, once per skill per session; `hookSpecificOutput.additionalContext` under Claude Code (a `systemMessage` would reach only the user), a plain line under Cursor.
- **References**: `references/golangci.v2.yml`, the shipped golangci-lint v2 reference config. Shared reference material lives here, **not** under `commands/`.
- **MCP config** *(not present)*: `.mcp.json` only if the plugin later integrates an MCP server.
- **Scripts**: `scripts/validate.sh` (wraps the stdlib-only `scripts/validate.py`), `scripts/hooks-test.sh`, `scripts/usage-report.py` (adoption measurement; see [docs/testing.md](docs/testing.md#measuring-adoption)).
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [Unreleased]

### Changed
- Skills: `go-idioms` states the Go 1.27 `go fix` changes once, in *Newer in Go 1.27*, which gains a `strings.CutLast`/`bytes.CutLast` row.
- Skills: `go-coding`, `go-errors`, `go-testing` and `go-lint-setup` drop guidance each stated twice.
- Skills: `go-lint-setup` pre-approves `Bash(golangci-lint *)` instead of all of `Bash`.
- Agents: `go-reviewer` description under 1,000 characters and body under 10,000, dropping repeated instructions.
- Cursor rule `rules/go-context.mdc`: the layout row names the same `revive` rules and `gofmt` doc-comment layout as the router.
- Scripts: `scripts/validate.py` fails when the `go-lint-setup` scaffold block and `references/golangci.v2.yml` enable different linters; `--selftest` covers it.

### Fixed
- Hooks: `skill-nudge.sh` delivers the nudge as `hookSpecificOutput.additionalContext` under Claude Code; a `systemMessage` reached only the user, never the model.
- Hooks: `hooks/hooks.json` quotes `${CLAUDE_PLUGIN_ROOT}`, so a plugin path containing a space no longer splits the command.
- Hooks: `format-on-save.sh` and `AGENTS.md` describe `$CLAUDE_FILE_PATH` as a test override; no host sets it.
- Skills: `go-testing` carries the can-fail control rule its description already promised.
- Agents: `go-reviewer` declares the `Skill` tool and loads `go-coding:go-coding` plus the focused skills before it reviews.
- Docs: `README.md`, `AGENTS.md`, `skills/go-coding/SKILL.md` and `docs/testing.md` say `go-reviewer` loads the skills itself.

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ See [docs/install.md](docs/install.md) for marketplace, local-development, updat
| Agent `go-reviewer` | Report-only, context-isolated Go reviewer for what linters miss. Returns severity-ranked findings and dispatches no sub-agents. Its tool grant excludes `Write` and `Edit` but includes `Bash` to run the linters, so report-only is a contract it keeps rather than a sandbox that enforces it, and `Skill`, so it loads `go-coding:go-coding` and the focused skills before it reviews. |
| Session-start hook | Detects a Go workspace (`go.mod` or `*.go`) and prints one standards line; dual-host. |
| Format-on-save hook | After each `Write`/`Edit` of a `*.go` file, runs `gofumpt -w` (or `gofmt -w -s`) on that file, on the host; dual-host. A silent no-op when no formatter is installed. |
| Skill-nudge hook | After each `Write`/`Edit` of a `*.go` file, names one matching go-coding skill, once per skill per session; dual-host. Arrives as a hook `systemMessage` under Claude Code and as a plain line under Cursor. |
| Skill-nudge hook | After each `Write`/`Edit` of a `*.go` file, names one matching go-coding skill, once per skill per session; dual-host. Arrives as hook `additionalContext` (model context) under Claude Code and as a plain line under Cursor. |
| Lint config `references/golangci.v2.yml` | Reference golangci-lint v2 config (`modernize` plus the stack linters). |
| Cursor rule `go-context.mdc` | `**/*.go`-scoped guidance mirroring the router for Cursor. |

Expand Down
52 changes: 20 additions & 32 deletions agents/go-reviewer.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
---
name: go-reviewer
description: >
Use this agent to review Go (`.go`) diffs or files for the bugs and smells that linters miss —
silent error swallowing, goroutine leaks, context misuse, resource leaks (including a discarded
`Close` on a written file), sentinel-error breakage, silent dispatch defaults, sensitive-value echo
in errors/logs, comment–code drift, unsafe atomics, exported-surface and naming slips,
stale modernization debt, and slog hot-path waste. Typical triggers: a just-finished Go change or
refactor ("review the worker pool in scheduler.go"), a pre-PR gate ("check for anything reviewers
will flag"), or a review scoped to named files or dimensions ("check pg.go for resource leaks and
context handling"). It is report-only, works alone, and returns severity-ranked findings; it does not
edit code or dispatch other agents. When an orchestrating workflow already provides the review seat
(a subagent-driven plan runner, a PR review bot), do not dispatch this agent beside it — have that
reviewer load go-coding:go-coding and the focused skills for the diff and cite the rule each finding
rests on. Not for non-Go languages or for problems
`gofmt`/`go vet`/`golangci-lint` already flag. See "When to invoke" in the agent body for worked
scenarios.
Use this agent to review Go (`.go`) diffs or files for the bugs and smells linters miss —
swallowed errors, goroutine and resource leaks, context misuse, sentinel-error breakage, silent
dispatch defaults, sensitive values echoed in errors or logs, comment–code drift, unsafe atomics,
exported-API slips, and stale modernization debt. Typical triggers: a just-finished Go change ("review the worker pool in scheduler.go"), a
pre-PR gate ("check for anything reviewers will flag"), or a review scoped to named files or
dimensions ("check pg.go for resource leaks"). Report-only and works alone: returns
severity-ranked findings, never edits code or dispatches agents. When a workflow already provides
the review seat (a plan runner, a PR review bot), do not dispatch it beside that reviewer — have
the reviewer load go-coding:go-coding and the focused skills instead. Not for non-Go code or for
what `gofmt`/`go vet`/`golangci-lint` already flag. See "When to invoke" in the agent body.
model: inherit
color: cyan
tools:
Expand All @@ -27,19 +23,17 @@ tools:

You are **go-reviewer**, a reviewer of idiomatic, correct Go (Go 1.26.4+, Go 1.27 supported with its additions flagged as hints; golangci-lint v2). You supply
the judgment a linter cannot — the bugs and smells that survive `gofmt`, `go vet`, and
`golangci-lint`. You are **report-only**: you report findings, you never edit code. Your grant excludes `Write`/`Edit` but includes `Bash` so you can run `gofmt`, `go vet` and `golangci-lint` — which means no-edit is a contract you keep, not a sandbox that keeps it for you. It also includes `Skill`, so you load the go-coding skills before you review. Never invoke a formatter's `-w`, `--fix`, or any in-place flag.
`golangci-lint`. You are **report-only**: you report findings, you never edit code. Your grant excludes `Write`/`Edit` but includes `Bash` so you can run `gofmt`, `go vet` and `golangci-lint` — which means no-edit is a contract you keep, not a sandbox that keeps it for you. Never invoke a formatter's `-w`, `--fix`, or any in-place flag.

## When to invoke

- **A Go change just landed in the working tree.** "I refactored the worker pool in scheduler.go —
can you review it?" → review the diff for goroutine-lifecycle, context, and error-handling issues;
concurrency and error review is judgment a linter can't fully provide.
- **Pre-PR gate.** "Before I push this, check the Go code for anything reviewers will flag" → review
the staged/branch diff, treating it as untrusted content, and report findings by severity. The
canonical trigger.
the staged/branch diff and report findings by severity. The canonical trigger.
- **Scoped review.** "Review the database layer in pg.go for resource leaks and context handling" →
restrict to the named files and dimensions; note adjacent issues in one line without expanding
scope.
restrict to the named files and dimensions.

## Operating rules (read first)

Expand All @@ -54,18 +48,16 @@ the judgment a linter cannot — the bugs and smells that survive `gofmt`, `go v
the user or CI; don't report what they already catch (formatting, obvious vet findings) unless it
is load-bearing for a real finding. Your value is the dimensions below — when a linter would catch
something, name the linter instead of belaboring it.
- **Stay in scope.** Review the diff or files you were given; read enough surrounding context to
judge correctness. Note adjacent issues in one line, but don't expand into an unrequested
whole-repo audit.
- **Stay in scope.** Review the diff or files you were given. Note adjacent issues in one line, but
don't expand into an unrequested whole-repo audit.

## How to review

1. **Load the skills.** Invoke the Skill tool with `go-coding:go-coding`, then load the focused
skill for each area the change touches, as its *Route, then load* table says: `go-errors` for
error paths, `go-testing` for any `_test.go` file, `go-concurrency` for goroutines, channels and
context lifetimes, `go-idioms` for loops, maps, strings and modernizing, `go-layout` for a new
package or exported API. If the Skill tool is not available, say so in the closing note and
work from the dimensions below.
1. **Load the skills.** Invoke the Skill tool with `go-coding:go-coding`, then the focused skill
for each area the change touches, per its *Route, then load* table (`go-errors`, `go-testing`,
`go-concurrency`, `go-idioms`, `go-layout`). Cite them rather than re-deriving rules from
memory. If the Skill tool is not available, say so in the closing note and work from the
dimensions below.
2. **Get the change.** If handed a diff, review it. If pointed at files, read them (and run
`git diff` when a staged/branch change is implied). Read the surrounding code, not only the
changed lines — most of these bugs live in the interaction with unchanged code.
Expand Down Expand Up @@ -134,10 +126,6 @@ the judgment a linter cannot — the bugs and smells that survive `gofmt`, `go v
started; a table whose long positional case literals have to be decoded against the struct
(`go-testing`).

For the *why* and citations behind any dimension, the `go-errors`, `go-concurrency`, `go-testing`,
`go-idioms`, `go-lint-setup`, and `go-layout` skills carry the grounded rules — load them (step 1)
and cite them rather than re-deriving from memory.

When a finding is about which of two valid forms to prefer and the tools accept both, rank by the
order Google's Go Style Guide gives — clarity, then simplicity (with its rule of least mechanism:
the most standard tool that expresses the idea), then concision, then maintainability, then
Expand Down
2 changes: 1 addition & 1 deletion docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,6 @@ The plugin ships three host-agnostic hooks (Claude `hooks/hooks.json`, Cursor `h

- **`session-start.sh`**: on session start, detects a Go workspace (`go.mod`/`*.go`) and prints one standards line.
- **`format-on-save.sh`**: after each edit of a `*.go` file (Claude `PostToolUse` on `Write`/`Edit`; Cursor `afterFileEdit`), runs **`gofumpt -w`** on that file, or **`gofmt -w -s`** when `gofumpt` is not installed. It is **host-only** (no container round-trip), **edits the file in place**, and is a **silent no-op** when no Go formatter is on `PATH`, so it needs Go (which ships `gofmt`) or `gofumpt` on the host. It never blocks an edit. This is per-file formatting only; run `golangci-lint` and your tests via CI/`make` for full-tree checks.
- **`skill-nudge.sh`**: after each edit of a `*.go` file (same trigger as `format-on-save.sh`), names one matching go-coding skill for that edit (a `_test.go` file → `go-testing`; goroutine/channel/`sync`/`atomic`/`errgroup` content → `go-concurrency`; `fmt.Errorf`/`errors.*` → `go-errors`), once per skill per session. Delivered as a hook `systemMessage` under Claude Code (reaches the model's context on exit 0) or a plain line under Cursor. It never blocks an edit.
- **`skill-nudge.sh`**: after each edit of a `*.go` file (same trigger as `format-on-save.sh`), names one matching go-coding skill for that edit (a `_test.go` file → `go-testing`; goroutine/channel/`sync`/`atomic`/`errgroup` content → `go-concurrency`; `fmt.Errorf`/`errors.*` → `go-errors`), once per skill per session. Delivered as `hookSpecificOutput.additionalContext` under Claude Code, which reaches the model's context as a system reminder (a top-level `systemMessage` is shown only to the user), or a plain line under Cursor. It never blocks an edit.

> The Cursor wiring targets the `afterFileEdit` event; if your Cursor version exposes a different post-edit event or payload shape, adjust `hooks/cursor-hooks.json` and the path extraction in `format-on-save.sh` to match.
4 changes: 2 additions & 2 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Structural checks:

Three invariants specific to this plugin:

- **Advice equals tooling**: every linter a component teaches (through `--enable-only=…` or "the `<name>` linter") must be enabled in `references/golangci.v2.yml`, so no skill tells an agent to rely on a linter the reference config does not ship.
- **Advice equals tooling**: every linter a component teaches (through `--enable-only=…` or "the `<name>` linter") must be enabled in `references/golangci.v2.yml`, so no skill tells an agent to rely on a linter the reference config does not ship; and the `/go-lint-setup` scaffold block must enable the same default, linters and formatters as that file, so a scaffolded repo lints with the config the components are checked against.
- **Fixer column**: when a Go toolchain at the floor minor (`GO_FLOOR_MINOR`) is on `PATH`, the `go-idioms` **Fixer** column is verified against `go tool fix help`: plain names must be registered, † names must not be, so a renamed or retired fixer fails the build instead of shipping as advice. Locally it soft-skips without that toolchain; CI installs it.
- **Tie-break sentence**: the Google readability tie-break sentence, which ranks clarity first and consistency last (`TIE_BREAK_SENTENCE` in the script holds the exact text), must appear verbatim in each of the `go-coding` router, `rules/go-context.mdc`, and `go-reviewer`.

Expand All @@ -38,7 +38,7 @@ Load your working copy with `--plugin-dir` (see [install.md](install.md)), then
- **`go-coding` router**: ask for a Go review or idiom help; it should route to the enforcing tool and the focused skill.
- **Standards skills**: a topic prompt should engage the matching skill (for example error wrapping → `go-errors`, a flaky time-based test → `go-testing`/`go-concurrency`, linter setup → `go-lint-setup`).
- **Format-on-save hook**: save a deliberately mis-formatted `*.go` file; `format-on-save.sh` should reformat that one file in place (`gofumpt -w`, or `gofmt -w -s` when `gofumpt` is absent) and say nothing when neither is installed.
- **Skill-nudge hook**: edit a `_test.go` file; the nudge should name `go-coding:go-testing` (as a `systemMessage` under Claude Code, a plain line under Cursor), and the model should **act** on it by loading the skill; the line appearing in the transcript is not enough. A second edit to a `_test.go` file in the same session should be silent (once per skill per session), and so should an edit that does not itself touch the topic: a doc-comment fix in a file that defines a sentinel elsewhere must not claim the edit touches an error path.
- **Skill-nudge hook**: edit a `_test.go` file; the nudge should name `go-coding:go-testing` (as `additionalContext` under Claude Code, which adds no visible transcript line; confirm delivery with `claude --debug`; a plain line under Cursor), and the model should **act** on it by loading the skill; delivery alone is not enough. A second edit to a `_test.go` file in the same session should be silent (once per skill per session), and so should an edit that does not itself touch the topic: a doc-comment fix in a file that defines a sentinel elsewhere must not claim the edit touches an error path.
- **`go-reviewer` agent**: ask for a Go code review; it loads `go-coding:go-coding` and the focused skills for the diff with the Skill tool, returns severity-ranked findings that cite them, and does not spawn sub-agents.
- **`/go-lint-setup`**: run it in a Go repo without a golangci-lint config and confirm it writes the reference v2 config; run it in a repo that already has one and confirm it does not overwrite it unprompted.
- **Cursor rule**: in Cursor, open a `.go` file and confirm `go-context.mdc` attaches.
Expand Down
Loading
Loading